mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
New Moose Structure + Created New Update Mission Method
This commit is contained in:
54
Moose Development/Dcs/DCSAirbase.lua
Normal file
54
Moose Development/Dcs/DCSAirbase.lua
Normal file
@@ -0,0 +1,54 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSAirbase
|
||||
|
||||
|
||||
--- Represents airbases: airdromes, helipads and ships with flying decks or landing pads.
|
||||
-- @type Airbase
|
||||
-- @extends DCSCoalitionObject#CoalitionObject
|
||||
-- @field #Airbase.ID ID Identifier of an airbase. It assigned to an airbase by the Mission Editor automatically. This identifier is used in AI tasks to refer an airbase that exists (spawned and not dead) or not.
|
||||
-- @field #Airbase.Category Category enum contains identifiers of airbase categories.
|
||||
-- @field #Airbase.Desc Desc Airbase descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type.
|
||||
|
||||
--- Enum contains identifiers of airbase categories.
|
||||
-- @type Airbase.Category
|
||||
-- @field AIRDROME
|
||||
-- @field HELIPAD
|
||||
-- @field SHIP
|
||||
|
||||
--- Airbase descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type.
|
||||
-- @type Airbase.Desc
|
||||
-- @extends #Desc
|
||||
-- @field #Airbase.Category category Category of the airbase type.
|
||||
|
||||
--- Returns airbase by its name. If no airbase found the function will return nil.
|
||||
-- @function [parent=#Airbase] getByName
|
||||
-- @param #string name
|
||||
-- @return #Airbase
|
||||
|
||||
--- Returns airbase descriptor by type name. If no descriptor is found the function will return nil.
|
||||
-- @function [parent=#Airbase] getDescByName
|
||||
-- @param #TypeName typeName Airbase type name.
|
||||
-- @return #Airbase.Desc
|
||||
|
||||
--- Returns Unit that is corresponded to the airbase. Works only for ships.
|
||||
-- @function [parent=#Airbase] getUnit
|
||||
-- @param self
|
||||
-- @return Unit#Unit
|
||||
|
||||
--- Returns identifier of the airbase.
|
||||
-- @function [parent=#Airbase] getID
|
||||
-- @param self
|
||||
-- @return #Airbase.ID
|
||||
|
||||
--- Returns the airbase's callsign - the localized string.
|
||||
-- @function [parent=#Airbase] getCallsign
|
||||
-- @param self
|
||||
-- @return #string
|
||||
|
||||
--- Returns descriptor of the airbase.
|
||||
-- @function [parent=#Airbase] getDesc
|
||||
-- @param self
|
||||
-- @return #Airbase.Desc
|
||||
|
||||
|
||||
Airbase = {} --#Airbase
|
||||
28
Moose Development/Dcs/DCSCoalitionObject.lua
Normal file
28
Moose Development/Dcs/DCSCoalitionObject.lua
Normal file
@@ -0,0 +1,28 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSCoalitionObject
|
||||
|
||||
--- @type CoalitionObject
|
||||
-- @extends DCSObject#Object
|
||||
|
||||
--- @type coalition
|
||||
-- @field #coalition.side side
|
||||
|
||||
--- @type coalition.side
|
||||
-- @field NEUTRAL
|
||||
-- @field RED
|
||||
-- @field BLUE
|
||||
|
||||
coalition = {} --#coalition
|
||||
|
||||
--- Returns coalition of the object.
|
||||
-- @function [parent=#CoalitionObject] getCoalition
|
||||
-- @param #CoalitionObject self
|
||||
-- @return DCSTypes#coalition.side
|
||||
|
||||
--- Returns object country.
|
||||
-- @function [parent=#CoalitionObject] getCountry
|
||||
-- @param #CoalitionObject self
|
||||
-- @return #country.id
|
||||
|
||||
|
||||
CoalitionObject = {} --#CoalitionObject
|
||||
10
Moose Development/Dcs/DCSCommand.lua
Normal file
10
Moose Development/Dcs/DCSCommand.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
--- @module DCSCommand
|
||||
|
||||
|
||||
--- @type Command
|
||||
-- @field #string id
|
||||
-- @field #Command.params params
|
||||
|
||||
--- @type Command.params
|
||||
|
||||
env.info( "Command defined" )
|
||||
115
Moose Development/Dcs/DCSController.lua
Normal file
115
Moose Development/Dcs/DCSController.lua
Normal file
@@ -0,0 +1,115 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSController
|
||||
|
||||
--- Controller is an object that performs A.I.-routines. Other words controller is an instance of A.I.. Controller stores current main task, active enroute tasks and behavior options. Controller performs commands. Please, read DCS A-10C GUI Manual EN.pdf chapter "Task Planning for Unit Groups", page 91 to understand A.I. system of DCS:A-10C.
|
||||
--
|
||||
-- This class has 2 types of functions:
|
||||
--
|
||||
-- * Tasks
|
||||
-- * Commands: Commands are instant actions those required zero time to perform. Commands may be used both for control unit/group behavior and control game mechanics.
|
||||
-- @type Controller
|
||||
-- @field #Controller.Detection Detection Enum contains identifiers of surface types.
|
||||
|
||||
--- Enables and disables the controller.
|
||||
-- Note: Now it works only for ground / naval groups!
|
||||
-- @function [parent=#Controller] setOnOff
|
||||
-- @param self
|
||||
-- @param #boolean value Enable / Disable.
|
||||
|
||||
-- Tasks
|
||||
|
||||
--- Resets current task and then sets the task to the controller. Task is a table that contains task identifier and task parameters.
|
||||
-- @function [parent=#Controller] setTask
|
||||
-- @param self
|
||||
-- @param #Task task
|
||||
|
||||
--- Resets current task of the controller.
|
||||
-- @function [parent=#Controller] resetTask
|
||||
-- @param self
|
||||
|
||||
--- Pushes the task to the front of the queue and makes the task active. Further call of function Controller.setTask() function will stop current task, clear the queue and set the new task active. If the task queue is empty the function will work like function Controller.setTask() function.
|
||||
-- @function [parent=#Controller] pushTask
|
||||
-- @param self
|
||||
-- @param #Task task
|
||||
|
||||
--- Pops current (front) task from the queue and makes active next task in the queue (if exists). If no more tasks in the queue the function works like function Controller.resetTask() function. Does nothing if the queue is empty.
|
||||
-- @function [parent=#Controller] popTask
|
||||
-- @param self
|
||||
|
||||
--- Returns true if the controller has a task.
|
||||
-- @function [parent=#Controller] hasTask
|
||||
-- @param self
|
||||
-- @return #boolean
|
||||
|
||||
-- Commands
|
||||
|
||||
--TODO: describe #Command structure
|
||||
--- Sets the command to perform by controller.
|
||||
-- @function [parent=#Controller] setCommand
|
||||
-- @param self
|
||||
-- @param #Command command Table that contains command identifier and command parameters.
|
||||
|
||||
|
||||
-- Behaviours
|
||||
|
||||
--- Sets the option to the controller.
|
||||
-- Option is a pair of identifier and value. Behavior options are global parameters those affect controller behavior in all tasks it performs.
|
||||
-- Option identifiers and values are stored in table AI.Option in subtables Air, Ground and Naval.
|
||||
--
|
||||
-- OptionId = @{#AI.Option.Air.id} or @{#AI.Option.Ground.id} or @{#AI.Option.Naval.id}
|
||||
-- OptionValue = AI.Option.Air.val[optionName] or AI.Option.Ground.val[optionName] or AI.Option.Naval.val[optionName]
|
||||
--
|
||||
-- @function [parent=#Controller] setOption
|
||||
-- @param self
|
||||
-- @param #OptionId optionId Option identifier.
|
||||
-- @param #OptionValue optionValue Value of the option.
|
||||
|
||||
|
||||
-- Detection
|
||||
|
||||
--- Enum contains identifiers of surface types.
|
||||
-- @type Controller.Detection
|
||||
-- @field VISUAL
|
||||
-- @field OPTIC
|
||||
-- @field RADAR
|
||||
-- @field IRST
|
||||
-- @field RWR
|
||||
-- @field DLINK
|
||||
|
||||
--- Detected target.
|
||||
-- @type DetectedTarget
|
||||
-- @field Object#Object object The target
|
||||
-- @field #boolean visible The target is visible
|
||||
-- @field #boolean type The target type is known
|
||||
-- @field #boolean distance Distance to the target is known
|
||||
|
||||
|
||||
--- Checks if the target is detected or not. If one or more detection method is specified the function will return true if the target is detected by at least one of these methods. If no detection methods are specified the function will return true if the target is detected by any method.
|
||||
-- @function [parent=#Controller] isTargetDetected
|
||||
-- @param self
|
||||
-- @param Object#Object target Target to check
|
||||
-- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN
|
||||
-- @return #boolean detected True if the target is detected.
|
||||
-- @return #boolean visible Has effect only if detected is true. True if the target is visible now.
|
||||
-- @return #ModelTime lastTime Has effect only if visible is false. Last time when target was seen.
|
||||
-- @return #boolean type Has effect only if detected is true. True if the target type is known.
|
||||
-- @return #boolean distance Has effect only if detected is true. True if the distance to the target is known.
|
||||
-- @return #Vec3 lastPos Has effect only if visible is false. Last position of the target when it was seen.
|
||||
-- @return #Vec3 lastVel Has effect only if visible is false. Last velocity of the target when it was seen.
|
||||
|
||||
|
||||
--- Returns list of detected targets. If one or more detection method is specified the function will return targets which were detected by at least one of these methods. If no detection methods are specified the function will return targets which were detected by any method.
|
||||
-- @function [parent=#Controller] getDetectedTargets
|
||||
-- @param self
|
||||
-- @param #Controller.Detection detection Controller.Detection detection1, Controller.Detection detection2, ... Controller.Detection detectionN
|
||||
-- @return #list<#DetectedTarget> array of DetectedTarget
|
||||
|
||||
--- Know a target.
|
||||
-- @function [parent=#Controller] knowTarget
|
||||
-- @param self
|
||||
-- @param Object#Object object The target.
|
||||
-- @param #boolean type Target type is known.
|
||||
-- @param #boolean distance Distance to target is known.
|
||||
|
||||
|
||||
Controller = {} --#Controller
|
||||
83
Moose Development/Dcs/DCSGroup.lua
Normal file
83
Moose Development/Dcs/DCSGroup.lua
Normal file
@@ -0,0 +1,83 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSGroup
|
||||
|
||||
--- Represents group of Units.
|
||||
-- @type Group
|
||||
-- @field #ID ID Identifier of a group. It is assigned to a group by Mission Editor automatically.
|
||||
-- @field #Group.Category Category Enum contains identifiers of group types.
|
||||
|
||||
--- Enum contains identifiers of group types.
|
||||
-- @type Group.Category
|
||||
-- @field AIRPLANE
|
||||
-- @field HELICOPTER
|
||||
-- @field GROUND
|
||||
-- @field SHIP
|
||||
|
||||
-- Static Functions
|
||||
|
||||
--- Returns group by the name assigned to the group in Mission Editor.
|
||||
-- @function [parent=#Group] getByName
|
||||
-- @param #string name
|
||||
-- @return #Group
|
||||
|
||||
-- Member Functions
|
||||
|
||||
--- returns true if the group exist or false otherwise.
|
||||
-- @function [parent=#Group] isExist
|
||||
-- @param #Group self
|
||||
-- @return #boolean
|
||||
|
||||
--- Destroys the group and all of its units.
|
||||
-- @function [parent=#Group] destroy
|
||||
-- @param #Group self
|
||||
|
||||
--- Returns category of the group.
|
||||
-- @function [parent=#Group] getCategory
|
||||
-- @param #Group self
|
||||
-- @return #Group.Category
|
||||
|
||||
--TODO check coalition.side
|
||||
|
||||
--- Returns the coalition of the group.
|
||||
-- @function [parent=#Group] getCoalition
|
||||
-- @param #Group self
|
||||
-- @return DCSCoalitionObject#coalition.side
|
||||
|
||||
--- Returns the group's name. This is the same name assigned to the group in Mission Editor.
|
||||
-- @function [parent=#Group] getName
|
||||
-- @param #Group self
|
||||
-- @return #string
|
||||
|
||||
--- Returns the group identifier.
|
||||
-- @function [parent=#Group] getID
|
||||
-- @param #Group self
|
||||
-- @return #ID
|
||||
|
||||
--- Returns the unit with number unitNumber. If the unit is not exists the function will return nil.
|
||||
-- @function [parent=#Group] getUnit
|
||||
-- @param #Group self
|
||||
-- @param #number unitNumber
|
||||
-- @return DCSUnit#Unit
|
||||
|
||||
--- Returns current size of the group. If some of the units will be destroyed, As units are destroyed the size of the group will be changed.
|
||||
-- @function [parent=#Group] getSize
|
||||
-- @param #Group self
|
||||
-- @return #number
|
||||
|
||||
--- Returns initial size of the group. If some of the units will be destroyed, initial size of the group will not be changed. Initial size limits the unitNumber parameter for Group.getUnit() function.
|
||||
-- @function [parent=#Group] getInitialSize
|
||||
-- @param #Group self
|
||||
-- @return #number
|
||||
|
||||
--- Returns array of the units present in the group now. Destroyed units will not be enlisted at all.
|
||||
-- @function [parent=#Group] getUnits
|
||||
-- @param #Group self
|
||||
-- @return #list<DCSUnit#Unit> array of Units
|
||||
|
||||
--- Returns controller of the group.
|
||||
-- @function [parent=#Group] getController
|
||||
-- @param #Group self
|
||||
-- @return Controller#Controller
|
||||
|
||||
Group = {} --#Group
|
||||
|
||||
73
Moose Development/Dcs/DCSObject.lua
Normal file
73
Moose Development/Dcs/DCSObject.lua
Normal file
@@ -0,0 +1,73 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSObject
|
||||
|
||||
--- @type Object
|
||||
-- @field #Object.Category Category
|
||||
-- @field #Object.Desc Desc
|
||||
|
||||
--- @type Object.Category
|
||||
-- @field UNIT
|
||||
-- @field WEAPON
|
||||
-- @field STATIC
|
||||
-- @field SCENERY
|
||||
-- @field BASE
|
||||
|
||||
--- @type Object.Desc
|
||||
-- @extends #Desc
|
||||
-- @field #number life initial life level
|
||||
-- @field #Box3 box bounding box of collision geometry
|
||||
|
||||
--- @function [parent=#Object] isExist
|
||||
-- @param #Object self
|
||||
-- @return #boolean
|
||||
|
||||
--- @function [parent=#Object] destroy
|
||||
-- @param #Object self
|
||||
|
||||
--- @function [parent=#Object] getCategory
|
||||
-- @param #Object self
|
||||
-- @return #Object.Category
|
||||
|
||||
--- Returns type name of the Object.
|
||||
-- @function [parent=#Object] getTypeName
|
||||
-- @param #Object self
|
||||
-- @return #string
|
||||
|
||||
--- Returns object descriptor.
|
||||
-- @function [parent=#Object] getDesc
|
||||
-- @param #Object self
|
||||
-- @return #Object.Desc
|
||||
|
||||
--- Returns true if the object belongs to the category.
|
||||
-- @function [parent=#Object] hasAttribute
|
||||
-- @param #Object self
|
||||
-- @param #AttributeName attributeName Attribute name to check.
|
||||
-- @return #boolean
|
||||
|
||||
--- Returns name of the object. This is the name that is assigned to the object in the Mission Editor.
|
||||
-- @function [parent=#Object] getName
|
||||
-- @param #Object self
|
||||
-- @return #string
|
||||
|
||||
--- Returns object coordinates for current time.
|
||||
-- @function [parent=#Object] getPoint
|
||||
-- @param #Object self
|
||||
-- @return #Vec3
|
||||
|
||||
--- Returns object position for current time.
|
||||
-- @function [parent=#Object] getPosition
|
||||
-- @param #Object self
|
||||
-- @return #Position3
|
||||
|
||||
--- Returns the unit's velocity vector.
|
||||
-- @function [parent=#Object] getVelocity
|
||||
-- @param #Object self
|
||||
-- @return #Vec3
|
||||
|
||||
--- Returns true if the unit is in air.
|
||||
-- @function [parent=#Object] inAir
|
||||
-- @param #Object self
|
||||
-- @return #boolean
|
||||
|
||||
Object = {} --#Object
|
||||
|
||||
34
Moose Development/Dcs/DCSStaticObject.lua
Normal file
34
Moose Development/Dcs/DCSStaticObject.lua
Normal file
@@ -0,0 +1,34 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSStaticObject
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module StaticObject
|
||||
-- @extends CoalitionObject#CoalitionObject
|
||||
|
||||
--- Represents static object added in the Mission Editor.
|
||||
-- @type StaticObject
|
||||
-- @field #StaticObject.ID ID Identifier of a StaticObject. It assigned to an StaticObject by the Mission Editor automatically.
|
||||
-- @field #StaticObject.Desc Desc Descriptor of StaticObject and Unit are equal. StaticObject is just a passive variant of Unit.
|
||||
|
||||
--- StaticObject descriptor. Airdromes are unique and their types are unique, but helipads and ships are not always unique and may have the same type.
|
||||
-- @type StaticObject.Desc
|
||||
-- @extends Unit#Unit.Desc
|
||||
|
||||
--- Returns static object by its name. If no static object found nil will be returned.
|
||||
-- @function [parent=#StaticObject] getByName
|
||||
-- @param #string name Name of static object to find.
|
||||
-- @return #StaticObject
|
||||
|
||||
--- returns identifier of the static object.
|
||||
-- @function [parent=#StaticObject] getID
|
||||
-- @param #StaticObject self
|
||||
-- @return #StaticObject.ID
|
||||
|
||||
--- Returns descriptor of the StaticObject.
|
||||
-- @function [parent=#StaticObject] getDesc
|
||||
-- @param #StaticObject self
|
||||
-- @return #StaticObject.Desc
|
||||
|
||||
|
||||
StaticObject = {} --#StaticObject
|
||||
10
Moose Development/Dcs/DCSTask.lua
Normal file
10
Moose Development/Dcs/DCSTask.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
--- @module DCSTask
|
||||
|
||||
|
||||
--- @type Task
|
||||
-- @field #string id
|
||||
-- @field #Task.param param
|
||||
|
||||
--- @type Task.param
|
||||
|
||||
env.info( "Task defined" )
|
||||
8
Moose Development/Dcs/DCSTime.lua
Normal file
8
Moose Development/Dcs/DCSTime.lua
Normal file
@@ -0,0 +1,8 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSTime
|
||||
|
||||
--- @type ModelTime
|
||||
-- @extends #number
|
||||
|
||||
--- @type Time
|
||||
-- @extends #number
|
||||
236
Moose Development/Dcs/DCSTypes.lua
Normal file
236
Moose Development/Dcs/DCSTypes.lua
Normal file
@@ -0,0 +1,236 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSTypes
|
||||
|
||||
|
||||
|
||||
--- Time is given in seconds.
|
||||
-- @type Time
|
||||
-- @extends #number
|
||||
|
||||
--- Model time is the time that drives the simulation. Model time may be stopped, accelerated and decelerated relative real time.
|
||||
-- @type ModelTime
|
||||
-- @extends #number
|
||||
|
||||
--- Mission time is a model time plus time of the mission start.
|
||||
-- @type MissionTime
|
||||
-- @extends #number
|
||||
|
||||
|
||||
--- Distance is given in meters.
|
||||
-- @type Distance
|
||||
-- @extends #number
|
||||
|
||||
--- Angle is given in radians.
|
||||
-- @type Angle
|
||||
-- @extends #number
|
||||
|
||||
--- Azimuth is an angle of rotation around world axis y counter-clockwise.
|
||||
-- @type Azimuth
|
||||
-- @extends #number
|
||||
|
||||
--- Mass is given in kilograms.
|
||||
-- @type Mass
|
||||
-- @extends #number
|
||||
|
||||
--- Vec3 type is a 3D-vector.
|
||||
-- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain.
|
||||
-- @type Vec3
|
||||
-- @field #Distance x is directed to the north
|
||||
-- @field #Distance z is directed to the east
|
||||
-- @field #Distance y is directed up
|
||||
|
||||
--- Vec2 is a 2D-vector for the ground plane as a reference plane.
|
||||
-- @type Vec2
|
||||
-- @field #Distance x Vec2.x = Vec3.x
|
||||
-- @field #Distance y Vec2.y = Vec3.z
|
||||
|
||||
--- Position is a composite structure. It consists of both coordinate vector and orientation matrix. Position3 (also known as "Pos3" for short) is a table that has following format:
|
||||
-- @type Position3
|
||||
-- @field #Vec3 p
|
||||
-- @field #Vec3 x
|
||||
-- @field #Vec3 y
|
||||
-- @field #Vec3 z
|
||||
|
||||
--- 3-dimensional box.
|
||||
-- @type Box3
|
||||
-- @field #Vec3 min
|
||||
-- @field #Vec3 max
|
||||
|
||||
--- Each object belongs to a type. Object type is a named couple of properties those independent of mission and common for all units of the same type. Name of unit type is a string. Samples of unit type: "Su-27", "KAMAZ" and "M2 Bradley".
|
||||
-- @type TypeName
|
||||
-- @extends #string
|
||||
|
||||
|
||||
--- @type AI
|
||||
-- @field #AI.Skill Skill
|
||||
-- @field #AI.Task Task
|
||||
-- @field #AI.Option Option
|
||||
|
||||
--- @type AI.Skill
|
||||
-- @field AVERAGE
|
||||
-- @field GOOD
|
||||
-- @field HIGH
|
||||
-- @field EXCELLENT
|
||||
-- @field PLAYER
|
||||
-- @field CLIENT
|
||||
|
||||
--- @type AI.Task
|
||||
-- @field #AI.Task.WeaponExpend WeaponExpend
|
||||
-- @field #AI.Task.OrbitPattern OrbitPattern
|
||||
-- @field #AI.Task.Designation Designation
|
||||
-- @field #AI.Task.WaypointType WaypointType
|
||||
-- @field #AI.Task.TurnMethod TurnMethod
|
||||
-- @field #AI.Task.AltitudeType AltitudeType
|
||||
-- @field #AI.Task.VehicleFormation VehicleFormation
|
||||
|
||||
--- @type AI.Task.WeaponExpend
|
||||
-- @field ONE
|
||||
-- @field TWO
|
||||
-- @field FOUR
|
||||
-- @field QUARTER
|
||||
-- @field HALF
|
||||
-- @field ALL
|
||||
|
||||
--- @type AI.Task.OrbitPattern
|
||||
-- @field CIRCLE
|
||||
-- @field RACE_TRACK
|
||||
|
||||
--- @type AI.Task.Designation
|
||||
-- @field NO
|
||||
-- @field AUTO
|
||||
-- @field WP
|
||||
-- @field IR_POINTER
|
||||
-- @field LASER
|
||||
|
||||
--- @type AI.Task.WaypointType
|
||||
-- @field TAKEOFF
|
||||
-- @field TAKEOFF_PARKING
|
||||
-- @field TURNING_POINT
|
||||
-- @field LAND
|
||||
|
||||
--- @type AI.Task.TurnMethod
|
||||
-- @field FLY_OVER_POINT
|
||||
-- @field FIN_POINT
|
||||
|
||||
--- @type AI.Task.AltitudeType
|
||||
-- @field BARO
|
||||
-- @field RADIO
|
||||
|
||||
--- @type AI.Task.VehicleFormation
|
||||
-- @field OFF_ROAD
|
||||
-- @field ON_ROAD
|
||||
-- @field RANK
|
||||
-- @field CONE
|
||||
-- @field DIAMOND
|
||||
-- @field VEE
|
||||
-- @field ECHELON_LEFT
|
||||
-- @field ECHELON_RIGHT
|
||||
|
||||
--- @type AI.Option
|
||||
-- @field #AI.Option.Air Air
|
||||
-- @field #AI.Option.Ground Ground
|
||||
-- @field #AI.Option.Naval Naval
|
||||
|
||||
--- @type AI.Option.Air
|
||||
-- @field #AI.Option.Air.id id
|
||||
-- @field #AI.Option.Air.val val
|
||||
|
||||
--- @type AI.Option.Ground
|
||||
-- @field #AI.Option.Ground.id id
|
||||
-- @field #AI.Option.Ground.val val
|
||||
|
||||
--- @type AI.Option.Naval
|
||||
-- @field #AI.Option.Naval.id id
|
||||
-- @field #AI.Option.Naval.val val
|
||||
|
||||
--TODO: work on formation
|
||||
--- @type AI.Option.Air.id
|
||||
-- @field NO_OPTION
|
||||
-- @field ROE
|
||||
-- @field REACTION_ON_THREAT
|
||||
-- @field RADAR_USING
|
||||
-- @field FLARE_USING
|
||||
-- @field FORMATION
|
||||
-- @field RTB_ON_BINGO
|
||||
-- @field SILENCE
|
||||
|
||||
--- @type AI.Option.Air.val
|
||||
-- @field #AI.Option.Air.val.ROE ROE
|
||||
-- @field #AI.Option.Air.val.REACTION_ON_THREAT REACTION_ON_THREAT
|
||||
-- @field #AI.Option.Air.val.RADAR_USING RADAR_USING
|
||||
-- @field #AI.Option.Air.val.FLARE_USING FLARE_USING
|
||||
|
||||
--- @type AI.Option.Air.val.ROE
|
||||
-- @field WEAPON_FREE
|
||||
-- @field OPEN_FIRE_WEAPON_FREE
|
||||
-- @field OPEN_FIRE
|
||||
-- @field RETURN_FIRE
|
||||
-- @field WEAPON_HOLD
|
||||
|
||||
--- @type AI.Option.Air.val.REACTION_ON_THREAT
|
||||
-- @field NO_REACTION
|
||||
-- @field PASSIVE_DEFENCE
|
||||
-- @field EVADE_FIRE
|
||||
-- @field BYPASS_AND_ESCAPE
|
||||
-- @field ALLOW_ABORT_MISSION
|
||||
|
||||
--- @type AI.Option.Air.val.RADAR_USING
|
||||
-- @field NEVER
|
||||
-- @field FOR_ATTACK_ONLY
|
||||
-- @field FOR_SEARCH_IF_REQUIRED
|
||||
-- @field FOR_CONTINUOUS_SEARCH
|
||||
|
||||
--- @type AI.Option.Air.val.FLARE_USING
|
||||
-- @field NEVER
|
||||
-- @field AGAINST_FIRED_MISSILE
|
||||
-- @field WHEN_FLYING_IN_SAM_WEZ
|
||||
-- @field WHEN_FLYING_NEAR_ENEMIES
|
||||
|
||||
--- @type AI.Option.Ground.id
|
||||
-- @field NO_OPTION
|
||||
-- @field ROE @{#AI.Option.Ground.val.ROE}
|
||||
-- @field DISPERSE_ON_ATTACK true or false
|
||||
-- @field ALARM_STATE @{#AI.Option.Ground.val.ALARM_STATE}
|
||||
|
||||
--- @type AI.Option.Ground.val
|
||||
-- @field #AI.Option.Ground.val.ROE ROE
|
||||
-- @field #AI.Option.Ground.val.ALARM_STATE ALARM_STATE
|
||||
|
||||
--- @type AI.Option.Ground.val.ROE
|
||||
-- @field OPEN_FIRE
|
||||
-- @field RETURN_FIRE
|
||||
-- @field WEAPON_HOLD
|
||||
|
||||
--- @type AI.Option.Ground.val.ALARM_STATE
|
||||
-- @field AUTO
|
||||
-- @field GREEN
|
||||
-- @field RED
|
||||
|
||||
--- @type AI.Option.Naval.id
|
||||
-- @field NO_OPTION
|
||||
-- @field ROE
|
||||
|
||||
--- @type AI.Option.Naval.val
|
||||
-- @field #AI.Option.Naval.val.ROE ROE
|
||||
|
||||
--- @type AI.Option.Naval.val.ROE
|
||||
-- @field OPEN_FIRE
|
||||
-- @field RETURN_FIRE
|
||||
-- @field WEAPON_HOLD
|
||||
|
||||
AI = {} --#AI
|
||||
|
||||
|
||||
--- @type Desc
|
||||
-- @field #TypeName typeName type name
|
||||
-- @field #string displayName localized display name
|
||||
-- @field #table attributes object type attributes
|
||||
|
||||
--- A distance type
|
||||
-- @type Distance
|
||||
|
||||
--- An angle type
|
||||
-- @type Angle
|
||||
|
||||
env.info( 'AI types created' )
|
||||
|
||||
241
Moose Development/Dcs/DCSUnit.lua
Normal file
241
Moose Development/Dcs/DCSUnit.lua
Normal file
@@ -0,0 +1,241 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSUnit
|
||||
|
||||
--- @type Unit
|
||||
-- @extends DCSCoalitionObject#CoalitionObject
|
||||
-- @field ID Identifier of an unit. It assigned to an unit by the Mission Editor automatically.
|
||||
-- @field #Unit.Category Category
|
||||
-- @field #Unit.RefuelingSystem RefuelingSystem
|
||||
-- @field #Unit.SensorType SensorType
|
||||
-- @field #Unit.OpticType OpticType
|
||||
-- @field #Unit.RadarType RadarType
|
||||
-- @field #Unit.Desc Desc
|
||||
-- @field #Unit.DescAircraft DescAircraft
|
||||
-- @field #Unit.DescAirplane DescAirplane
|
||||
-- @field #Unit.DescHelicopter DescHelicopter
|
||||
-- @field #Unit.DescVehicle DescVehicle
|
||||
-- @field #Unit.DescShip DescShip
|
||||
-- @field #Unit.AmmoItem AmmoItem
|
||||
-- @field #list<#Unit.AmmoItem> Ammo
|
||||
-- @field #Unit.Sensor Sensor
|
||||
-- @field #Unit.Optic Optic
|
||||
-- @field #Unit.Radar Radar
|
||||
-- @field #Unit.IRST IRST
|
||||
|
||||
|
||||
--- Enum that stores unit categories.
|
||||
-- @type Unit.Category
|
||||
-- @field AIRPLANE
|
||||
-- @field HELICOPTER
|
||||
-- @field GROUND_UNIT
|
||||
-- @field SHIP
|
||||
-- @field STRUCTURE
|
||||
|
||||
--- Enum that stores aircraft refueling system types.
|
||||
-- @type Unit.RefuelingSystem
|
||||
-- @field BOOM_AND_RECEPTACLE
|
||||
-- @field PROBE_AND_DROGUE
|
||||
|
||||
--- Enum that stores sensor types.
|
||||
-- @type Unit.SensorType
|
||||
-- @field OPTIC
|
||||
-- @field RADAR
|
||||
-- @field IRST
|
||||
-- @field RWR
|
||||
|
||||
--- Enum that stores types of optic sensors.
|
||||
-- @type Unit.OpticType
|
||||
-- @field TV TV-sensor
|
||||
-- @field LLTV Low-level TV-sensor
|
||||
-- @field IR Infra-Red optic sensor
|
||||
|
||||
--- Enum that stores radar types.
|
||||
-- @type Unit.RadarType
|
||||
-- @field AS air search radar
|
||||
-- @field SS surface/land search radar
|
||||
|
||||
|
||||
--- A unit descriptor.
|
||||
-- @type Unit.Desc
|
||||
-- @extends Object#Object.Desc
|
||||
-- @field #Unit.Category category Unit Category
|
||||
-- @field #Mass massEmpty mass of empty unit
|
||||
-- @field #number speedMax istance / Time, --maximal velocity
|
||||
|
||||
--- An aircraft descriptor.
|
||||
-- @type Unit.DescAircraft
|
||||
-- @extends Unit#Unit.Desc
|
||||
-- @field #Mass fuelMassMax maximal inner fuel mass
|
||||
-- @field #Distance range Operational range
|
||||
-- @field #Distance Hmax Ceiling
|
||||
-- @field #number VyMax #Distance / #Time, --maximal climb rate
|
||||
-- @field #number NyMin minimal safe acceleration
|
||||
-- @field #number NyMax maximal safe acceleration
|
||||
-- @field #Unit.RefuelingSystem tankerType refueling system type
|
||||
|
||||
--- An airplane descriptor.
|
||||
-- @type Unit.DescAirplane
|
||||
-- @extends Unit#Unit.DescAircraft
|
||||
-- @field #number speedMax0 Distance / Time maximal TAS at ground level
|
||||
-- @field #number speedMax10K Distance / Time maximal TAS at altitude of 10 km
|
||||
|
||||
--- A helicopter descriptor.
|
||||
-- @type Unit.DescHelicopter
|
||||
-- @extends Unit#Unit.DescAircraft
|
||||
-- @field #Distance HmaxStat static ceiling
|
||||
|
||||
--- A vehicle descriptor.
|
||||
-- @type Unit.DescVehicle
|
||||
-- @extends Unit#Unit.Desc
|
||||
-- @field #Angle maxSlopeAngle maximal slope angle
|
||||
-- @field #boolean riverCrossing can the vehicle cross a rivers
|
||||
|
||||
--- A ship descriptor.
|
||||
-- @type Unit.DescShip
|
||||
-- @extends #Unit.Desc
|
||||
|
||||
--- ammunition item: "type-count" pair.
|
||||
-- @type Unit.AmmoItem
|
||||
-- @field #Weapon.Desc desc ammunition descriptor
|
||||
-- @field #number count ammunition count
|
||||
|
||||
--- A unit sensor.
|
||||
-- @type Unit.Sensor
|
||||
-- @field #TypeName typeName
|
||||
-- @field #Unit.SensorType type
|
||||
|
||||
--- An optic sensor.
|
||||
-- @type Unit.Optic
|
||||
-- @extends Unit#Unit.Sensor
|
||||
-- @field #Unit.OpticType opticType
|
||||
|
||||
--- A radar.
|
||||
-- @type Unit.Radar
|
||||
-- @extends Unit#Unit.Sensor
|
||||
-- @field #Distance detectionDistanceRBM detection distance for RCS=1m^2 in real-beam mapping mode, nil if radar doesn't support surface/land search
|
||||
-- @field #Distance detectionDistanceHRM detection distance for RCS=1m^2 in high-resolution mapping mode, nil if radar has no HRM
|
||||
-- @field #Unit.Radar.detectionDistanceAir detectionDistanceAir detection distance for RCS=1m^2 airborne target, nil if radar doesn't support air search
|
||||
|
||||
--- @type Unit.Radar.detectionDistanceAir
|
||||
-- @field #Unit.Radar.detectionDistanceAir.upperHemisphere upperHemisphere
|
||||
-- @field #Unit.Radar.detectionDistanceAir.lowerHemisphere lowerHemisphere
|
||||
|
||||
--- @type Unit.Radar.detectionDistanceAir.upperHemisphere
|
||||
-- @field #Distance headOn
|
||||
-- @field #Distance tailOn
|
||||
|
||||
--- @type Unit.Radar.detectionDistanceAir.lowerHemisphere
|
||||
-- @field #Distance headOn
|
||||
-- @field #Distance tailOn
|
||||
|
||||
--- An IRST.
|
||||
-- @type Unit#Unit.IRST
|
||||
-- @extends Unit.Sensor
|
||||
-- @field #Distance detectionDistanceIdle detection of tail-on target with heat signature = 1 in upper hemisphere, engines are in idle
|
||||
-- @field #Distance detectionDistanceMaximal ..., engines are in maximal mode
|
||||
-- @field #Distance detectionDistanceAfterburner ..., engines are in afterburner mode
|
||||
|
||||
--- An RWR.
|
||||
-- @type Unit.RWR
|
||||
-- @extends Unit#Unit.Sensor
|
||||
|
||||
--- table that stores all unit sensors.
|
||||
-- TODO @type Sensors
|
||||
--
|
||||
|
||||
|
||||
--- Returns unit object by the name assigned to the unit in Mission Editor. If there is unit with such name or the unit is destroyed the function will return nil. The function provides access to non-activated units too.
|
||||
-- @function [parent=#Unit] getByName
|
||||
-- @param #string name
|
||||
-- @return #Unit
|
||||
|
||||
--- Returns if the unit is activated.
|
||||
-- @function [parent=#Unit] isActive
|
||||
-- @param #Unit self
|
||||
-- @return #boolean
|
||||
|
||||
--- Returns name of the player that control the unit or nil if the unit is controlled by A.I.
|
||||
-- @function [parent=#Unit] getPlayerName
|
||||
-- @param #Unit self
|
||||
-- @return #string
|
||||
|
||||
--- returns the unit's unique identifier.
|
||||
-- @function [parent=#Unit] getID
|
||||
-- @param #Unit self
|
||||
-- @return #Unit.ID
|
||||
|
||||
|
||||
--- Returns the unit's number in the group. The number is the same number the unit has in ME. It may not be changed during the mission. If any unit in the group is destroyed, the numbers of another units will not be changed.
|
||||
-- @function [parent=#Unit] getNumber
|
||||
-- @param #Unit self
|
||||
-- @return #number
|
||||
|
||||
--- Returns controller of the unit if it exist and nil otherwise
|
||||
-- @function [parent=#Unit] getController
|
||||
-- @param #Unit self
|
||||
-- @return #Controller
|
||||
|
||||
--- Returns the unit's group if it exist and nil otherwise
|
||||
-- @function [parent=#Unit] getGroup
|
||||
-- @param #Unit self
|
||||
-- @return DCSGroup#Group
|
||||
|
||||
--- Returns the unit's callsign - the localized string.
|
||||
-- @function [parent=#Unit] getCallsign
|
||||
-- @param #Unit self
|
||||
-- @return #string
|
||||
|
||||
--- Returns the unit's health. Dead units has health <= 1.0
|
||||
-- @function [parent=#Unit] getLife
|
||||
-- @param #Unit self
|
||||
-- @return #number
|
||||
|
||||
--- returns the unit's initial health.
|
||||
-- @function [parent=#Unit] getLife0
|
||||
-- @param #Unit self
|
||||
-- @return #number
|
||||
|
||||
--- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
|
||||
-- @function [parent=#Unit] getFuel
|
||||
-- @param #Unit self
|
||||
-- @return #number
|
||||
|
||||
--- Returns the unit ammunition.
|
||||
-- @function [parent=#Unit] getAmmo
|
||||
-- @param #Unit self
|
||||
-- @return #Unit.Ammo
|
||||
|
||||
--- Returns the unit sensors.
|
||||
-- @function [parent=#Unit] getSensors
|
||||
-- @param #Unit self
|
||||
-- @return #Unit.Sensors
|
||||
|
||||
--- Returns true if the unit has specified types of sensors. This function is more preferable than Unit.getSensors() if you don't want to get information about all the unit's sensors, and just want to check if the unit has specified types of sensors.
|
||||
-- @function [parent=#Unit] hasSensors
|
||||
-- @param #Unit self
|
||||
-- @param #Unit.SensorType sensorType (= nil) Sensor type.
|
||||
-- @param ... Additional parameters.
|
||||
-- @return #boolean
|
||||
-- @usage
|
||||
-- If sensorType is Unit.SensorType.OPTIC, additional parameters are optic sensor types. Following example checks if the unit has LLTV or IR optics:
|
||||
-- unit:hasSensors(Unit.SensorType.OPTIC, Unit.OpticType.LLTV, Unit.OpticType.IR)
|
||||
-- If sensorType is Unit.SensorType.RADAR, additional parameters are radar types. Following example checks if the unit has air search radars:
|
||||
-- unit:hasSensors(Unit.SensorType.RADAR, Unit.RadarType.AS)
|
||||
-- If no additional parameters are specified the function returns true if the unit has at least one sensor of specified type.
|
||||
-- If sensor type is not specified the function returns true if the unit has at least one sensor of any type.
|
||||
--
|
||||
|
||||
--- returns two values:
|
||||
-- First value indicates if at least one of the unit's radar(s) is on.
|
||||
-- Second value is the object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target.
|
||||
-- @function [parent=#Unit] getRadar
|
||||
-- @param #Unit self
|
||||
-- @return #boolean, Object#Object
|
||||
|
||||
--- Returns unit descriptor. Descriptor type depends on unit category.
|
||||
-- @function [parent=#Unit] getDesc
|
||||
-- @param #Unit self
|
||||
-- @return #Unit.Desc
|
||||
|
||||
|
||||
Unit = {} --#Unit
|
||||
26
Moose Development/Dcs/DCScountry.lua
Normal file
26
Moose Development/Dcs/DCScountry.lua
Normal file
@@ -0,0 +1,26 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCScountry
|
||||
|
||||
--- @type country
|
||||
-- @field #country.id id
|
||||
|
||||
--- @type country.id
|
||||
-- @field RUSSIA
|
||||
-- @field UKRAINE
|
||||
-- @field USA
|
||||
-- @field TURKEY
|
||||
-- @field UK
|
||||
-- @field FRANCE
|
||||
-- @field GERMANY
|
||||
-- @field CANADA
|
||||
-- @field SPAIN
|
||||
-- @field THE_NETHERLANDS
|
||||
-- @field BELGIUM
|
||||
-- @field NORWAY
|
||||
-- @field DENMARK
|
||||
-- @field ISRAEL
|
||||
-- @field GEORGIA
|
||||
-- @field INSURGENTS
|
||||
-- @field ABKHAZIA
|
||||
-- @field SOUTH_OSETIA
|
||||
-- @field ITALY
|
||||
27
Moose Development/Dcs/DCSenv.lua
Normal file
27
Moose Development/Dcs/DCSenv.lua
Normal file
@@ -0,0 +1,27 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module env
|
||||
|
||||
--- @type env
|
||||
|
||||
--- Add message to simulator log with caption "INFO". Message box is optional.
|
||||
-- @function [parent=#env] info
|
||||
-- @field #string message message string to add to log.
|
||||
-- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional.
|
||||
|
||||
--- Add message to simulator log with caption "WARNING". Message box is optional.
|
||||
-- @function [parent=#env] warning
|
||||
-- @field #string message message string to add to log.
|
||||
-- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional.
|
||||
|
||||
--- Add message to simulator log with caption "ERROR". Message box is optional.
|
||||
-- @function [parent=#env] error
|
||||
-- @field #string message message string to add to log.
|
||||
-- @field #boolean showMessageBox If the parameter is true Message Box will appear. Optional.
|
||||
|
||||
--- Enables/disables appearance of message box each time lua error occurs.
|
||||
-- @function [parent=#env] setErrorMessageBoxEnabled
|
||||
-- @field #boolean on if true message box appearance is enabled.
|
||||
|
||||
|
||||
|
||||
env = {} --#env
|
||||
20
Moose Development/Dcs/DCSland.lua
Normal file
20
Moose Development/Dcs/DCSland.lua
Normal file
@@ -0,0 +1,20 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module land
|
||||
|
||||
--- @type land
|
||||
-- @field #land.SurfaceType SurfaceType
|
||||
|
||||
|
||||
--- @type land.SurfaceType
|
||||
-- @field LAND
|
||||
-- @field SHALLOW_WATER
|
||||
-- @field WATER
|
||||
-- @field ROAD
|
||||
-- @field RUNWAY
|
||||
|
||||
--- Returns altitude MSL of the point.
|
||||
-- @function [parent=#land] getHeight
|
||||
-- @param #Vec2 point point on the ground.
|
||||
-- @return DCSTypes#Distance
|
||||
|
||||
land = {} --#land
|
||||
45
Moose Development/Dcs/DCStimer.lua
Normal file
45
Moose Development/Dcs/DCStimer.lua
Normal file
@@ -0,0 +1,45 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCStimer
|
||||
|
||||
--- @type timer
|
||||
|
||||
|
||||
--- Returns model time in seconds.
|
||||
-- @function [parent=#timer] getTime
|
||||
-- @return #Time
|
||||
|
||||
--- Returns mission time in seconds.
|
||||
-- @function [parent=#timer] getAbsTime
|
||||
-- @return #Time
|
||||
|
||||
--- Returns mission start time in seconds.
|
||||
-- @function [parent=#timer] getTime0
|
||||
-- @return #Time
|
||||
|
||||
--- Schedules function to call at desired model time.
|
||||
-- Time function FunctionToCall(any argument, Time time)
|
||||
--
|
||||
-- ...
|
||||
--
|
||||
-- return ...
|
||||
--
|
||||
-- end
|
||||
--
|
||||
-- Must return model time of next call or nil. Note that the DCS scheduler calls the function in protected mode and any Lua errors in the called function will be trapped and not reported. If the function triggers a Lua error then it will be terminated and not scheduled to run again.
|
||||
-- @function [parent=#timer] scheduleFunction
|
||||
-- @param #FunctionToCall functionToCall Lua-function to call. Must have prototype of FunctionToCall.
|
||||
-- @param functionArgument Function argument of any type to pass to functionToCall.
|
||||
-- @param #Time time Model time of the function call.
|
||||
-- @return functionId
|
||||
|
||||
--- Re-schedules function to call at another model time.
|
||||
-- @function [parent=#timer] setFunctionTime
|
||||
-- @param functionId Lua-function to call. Must have prototype of FunctionToCall.
|
||||
-- @param #Time time Model time of the function call.
|
||||
|
||||
|
||||
--- Removes the function from schedule.
|
||||
-- @function [parent=#timer] removeFunction
|
||||
-- @param functionId Function identifier to remove from schedule
|
||||
|
||||
timer = {} --#timer
|
||||
35
Moose Development/Dcs/DCSworld.lua
Normal file
35
Moose Development/Dcs/DCSworld.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
-------------------------------------------------------------------------------
|
||||
-- @module DCSWorld
|
||||
|
||||
--- @type world
|
||||
-- @field #world.event event
|
||||
|
||||
|
||||
--- @type world.event
|
||||
-- @field S_EVENT_INVALID
|
||||
-- @field S_EVENT_SHOT
|
||||
-- @field S_EVENT_HIT
|
||||
-- @field S_EVENT_TAKEOFF
|
||||
-- @field S_EVENT_LAND
|
||||
-- @field S_EVENT_CRASH
|
||||
-- @field S_EVENT_EJECTION
|
||||
-- @field S_EVENT_REFUELING
|
||||
-- @field S_EVENT_DEAD
|
||||
-- @field S_EVENT_PILOT_DEAD
|
||||
-- @field S_EVENT_BASE_CAPTURED
|
||||
-- @field S_EVENT_MISSION_START
|
||||
-- @field S_EVENT_MISSION_END
|
||||
-- @field S_EVENT_TOOK_CONTROL
|
||||
-- @field S_EVENT_REFUELING_STOP
|
||||
-- @field S_EVENT_BIRTH
|
||||
-- @field S_EVENT_HUMAN_FAILURE
|
||||
-- @field S_EVENT_ENGINE_STARTUP
|
||||
-- @field S_EVENT_ENGINE_SHUTDOWN
|
||||
-- @field S_EVENT_PLAYER_ENTER_UNIT
|
||||
-- @field S_EVENT_PLAYER_LEAVE_UNIT
|
||||
-- @field S_EVENT_PLAYER_COMMENT
|
||||
-- @field S_EVENT_SHOOTING_START
|
||||
-- @field S_EVENT_SHOOTING_END
|
||||
-- @field S_EVENT_MAX
|
||||
|
||||
world = {} --#world
|
||||
8
Moose Development/Dcs/Moose_Test_WRAPPER.lua
Normal file
8
Moose Development/Dcs/Moose_Test_WRAPPER.lua
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
Include.File( "Group" )
|
||||
Include.File( "Unit" )
|
||||
|
||||
local UnitAirPlaneAI = _DATABASE:FindUnit( "Airplane AI" )
|
||||
|
||||
UnitAirPlaneAI:FlareRed()
|
||||
|
||||
BIN
Moose Development/Maths/Bearing calculation.ggb
Normal file
BIN
Moose Development/Maths/Bearing calculation.ggb
Normal file
Binary file not shown.
Binary file not shown.
497
Moose Development/Moose/Base.lua
Normal file
497
Moose Development/Moose/Base.lua
Normal file
@@ -0,0 +1,497 @@
|
||||
--- BASE classes.
|
||||
--
|
||||
-- @{#BASE} class
|
||||
-- ==============
|
||||
-- The @{#BASE} class is the super class for most of the classes defined within MOOSE.
|
||||
--
|
||||
-- It handles:
|
||||
--
|
||||
-- * The construction and inheritance of child classes.
|
||||
-- * The tracing of objects during mission execution within the DCS.log file (under saved games folder).
|
||||
--
|
||||
-- Note: Normally you would not use the BASE class unless you are extending the MOOSE framework with new classes.
|
||||
--
|
||||
-- BASE Trace functionality
|
||||
-- ========================
|
||||
-- The BASE class contains trace methods to trace progress within a mission execution of a certain object.
|
||||
-- Note that these trace methods are inherited by each MOOSE class interiting BASE.
|
||||
-- As such, each object created from derived class from BASE can use the tracing functions to trace its execution.
|
||||
--
|
||||
-- Trace a function call
|
||||
-- ---------------------
|
||||
-- There are basically 3 types of tracing methods available within BASE:
|
||||
--
|
||||
-- * @{#BASE.F}: Trace the beginning of a function and its given parameters.
|
||||
-- * @{#BASE.T}: Trace further logic within a function giving optional variables or parameters.
|
||||
-- * @{#BASE.E}: Trace an execption within a function giving optional variables or parameters. An exception will always be traced.
|
||||
--
|
||||
-- Tracing levels
|
||||
-- --------------
|
||||
-- There are 3 tracing levels within MOOSE.
|
||||
-- These tracing levels were defined to avoid bulks of tracing to be generated by lots of objects.
|
||||
--
|
||||
-- As such, the F and T methods have additional variants to trace level 2 and 3 respectively:
|
||||
--
|
||||
-- * @{#BASE.F2}: Trace the beginning of a function and its given parameters with tracing level 2.
|
||||
-- * @{#BASE.F3}: Trace the beginning of a function and its given parameters with tracing level 3.
|
||||
-- * @{#BASE.T2}: Trace further logic within a function giving optional variables or parameters with tracing level 2.
|
||||
-- * @{#BASE.T3}: Trace further logic within a function giving optional variables or parameters with tracing level 3.
|
||||
--
|
||||
-- BASE Inheritance support
|
||||
-- ========================
|
||||
-- The following methods are available to support inheritance:
|
||||
--
|
||||
-- * @{#BASE.Inherit}: Inherits from a class.
|
||||
-- * @{#BASE.Inherited}: Returns the parent class from the class.
|
||||
--
|
||||
-- Future
|
||||
-- ======
|
||||
-- Further methods may be added to BASE whenever there is a need to make "overall" functions available within MOOSE.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module Base
|
||||
-- @author FlightControl
|
||||
|
||||
Include.File( "Routines" )
|
||||
|
||||
local _TraceOn = true
|
||||
local _TraceLevel = 1
|
||||
local _TraceClass = {
|
||||
--DATABASE = true,
|
||||
--SEAD = true,
|
||||
--DESTROYBASETASK = true,
|
||||
--MOVEMENT = true,
|
||||
--SPAWN = true,
|
||||
--STAGE = true,
|
||||
--ZONE = true,
|
||||
--GROUP = true,
|
||||
--UNIT = true,
|
||||
--CLIENT = true,
|
||||
--CARGO = true,
|
||||
--CARGO_GROUP = true,
|
||||
--CARGO_PACKAGE = true,
|
||||
--CARGO_SLINGLOAD = true,
|
||||
--CARGO_ZONE = true,
|
||||
--CLEANUP = true,
|
||||
--MENU_CLIENT = true,
|
||||
--MENU_CLIENT_COMMAND = true,
|
||||
--ESCORT = true,
|
||||
}
|
||||
local _TraceClassMethod = {}
|
||||
|
||||
--- The BASE Class
|
||||
-- @type BASE
|
||||
-- @field ClassName The name of the class.
|
||||
-- @field ClassID The ID number of the class.
|
||||
BASE = {
|
||||
ClassName = "BASE",
|
||||
ClassID = 0,
|
||||
Events = {}
|
||||
}
|
||||
|
||||
--- The Formation Class
|
||||
-- @type FORMATION
|
||||
-- @field Cone A cone formation.
|
||||
FORMATION = {
|
||||
Cone = "Cone"
|
||||
}
|
||||
|
||||
|
||||
|
||||
--- The base constructor. This is the top top class of all classed defined within the MOOSE.
|
||||
-- Any new class needs to be derived from this class for proper inheritance.
|
||||
-- @param #BASE self
|
||||
-- @return #BASE The new instance of the BASE class.
|
||||
-- @usage
|
||||
-- function TASK:New()
|
||||
--
|
||||
-- local self = BASE:Inherit( self, BASE:New() )
|
||||
--
|
||||
-- -- assign Task default values during construction
|
||||
-- self.TaskBriefing = "Task: No Task."
|
||||
-- self.Time = timer.getTime()
|
||||
-- self.ExecuteStage = _TransportExecuteStage.NONE
|
||||
--
|
||||
-- return self
|
||||
-- end
|
||||
-- @todo need to investigate if the deepCopy is really needed... Don't think so.
|
||||
|
||||
function BASE:New()
|
||||
local Child = routines.utils.deepCopy( self )
|
||||
local Parent = {}
|
||||
setmetatable( Child, Parent )
|
||||
Child.__index = Child
|
||||
self.ClassID = self.ClassID + 1
|
||||
Child.ClassID = self.ClassID
|
||||
--Child.AddEvent( Child, S_EVENT_BIRTH, Child.EventBirth )
|
||||
return Child
|
||||
end
|
||||
|
||||
--- This is the worker method to inherit from a parent class.
|
||||
-- @param #BASE self
|
||||
-- @param Child is the Child class that inherits.
|
||||
-- @param #BASE Parent is the Parent class that the Child inherits from.
|
||||
-- @return #BASE Child
|
||||
function BASE:Inherit( Child, Parent )
|
||||
local Child = routines.utils.deepCopy( Child )
|
||||
local Parent = routines.utils.deepCopy( Parent )
|
||||
if Child ~= nil then
|
||||
setmetatable( Child, Parent )
|
||||
Child.__index = Child
|
||||
end
|
||||
--Child.ClassName = Child.ClassName .. '.' .. Child.ClassID
|
||||
self:T( 'Inherited from ' .. Parent.ClassName )
|
||||
return Child
|
||||
end
|
||||
|
||||
--- This is the worker method to retrieve the Parent class.
|
||||
-- @param #BASE self
|
||||
-- @param #BASE Child is the Child class from which the Parent class needs to be retrieved.
|
||||
-- @return #BASE
|
||||
function BASE:Inherited( Child )
|
||||
local Parent = getmetatable( Child )
|
||||
-- env.info('Inherited class of ' .. Child.ClassName .. ' is ' .. Parent.ClassName )
|
||||
return Parent
|
||||
end
|
||||
|
||||
--- Get the ClassName + ClassID of the class instance.
|
||||
-- The ClassName + ClassID is formatted as '%s#%09d'.
|
||||
-- @param #BASE self
|
||||
-- @return #string The ClassName + ClassID of the class instance.
|
||||
function BASE:GetClassNameAndID()
|
||||
return string.format( '%s#%09d', self:GetClassName(), self:GetClassID() )
|
||||
end
|
||||
|
||||
--- Get the ClassName of the class instance.
|
||||
-- @param #BASE self
|
||||
-- @return #string The ClassName of the class instance.
|
||||
function BASE:GetClassName()
|
||||
return self.ClassName
|
||||
end
|
||||
|
||||
--- Get the ClassID of the class instance.
|
||||
-- @param #BASE self
|
||||
-- @return #string The ClassID of the class instance.
|
||||
function BASE:GetClassID()
|
||||
return self.ClassID
|
||||
end
|
||||
|
||||
--- Set a new listener for the class.
|
||||
-- @param self
|
||||
-- @param DCSTypes#Event Event
|
||||
-- @param #function EventFunction
|
||||
-- @return #BASE
|
||||
function BASE:AddEvent( Event, EventFunction )
|
||||
self:F( Event )
|
||||
|
||||
self.Events[#self.Events+1] = {}
|
||||
self.Events[#self.Events].Event = Event
|
||||
self.Events[#self.Events].EventFunction = EventFunction
|
||||
self.Events[#self.Events].EventEnabled = false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Returns the event dispatcher
|
||||
-- @param #BASE self
|
||||
-- @return Event#EVENT
|
||||
function BASE:Event()
|
||||
|
||||
return _EVENTDISPATCHER
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Enable the event listeners for the class.
|
||||
-- @param #BASE self
|
||||
-- @return #BASE
|
||||
function BASE:EnableEvents()
|
||||
self:F( #self.Events )
|
||||
|
||||
for EventID, Event in pairs( self.Events ) do
|
||||
Event.Self = self
|
||||
Event.EventEnabled = true
|
||||
end
|
||||
self.Events.Handler = world.addEventHandler( self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Disable the event listeners for the class.
|
||||
-- @param #BASE self
|
||||
-- @return #BASE
|
||||
function BASE:DisableEvents()
|
||||
self:F()
|
||||
|
||||
world.removeEventHandler( self )
|
||||
for EventID, Event in pairs( self.Events ) do
|
||||
Event.Self = nil
|
||||
Event.EventEnabled = false
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
local BaseEventCodes = {
|
||||
"S_EVENT_SHOT",
|
||||
"S_EVENT_HIT",
|
||||
"S_EVENT_TAKEOFF",
|
||||
"S_EVENT_LAND",
|
||||
"S_EVENT_CRASH",
|
||||
"S_EVENT_EJECTION",
|
||||
"S_EVENT_REFUELING",
|
||||
"S_EVENT_DEAD",
|
||||
"S_EVENT_PILOT_DEAD",
|
||||
"S_EVENT_BASE_CAPTURED",
|
||||
"S_EVENT_MISSION_START",
|
||||
"S_EVENT_MISSION_END",
|
||||
"S_EVENT_TOOK_CONTROL",
|
||||
"S_EVENT_REFUELING_STOP",
|
||||
"S_EVENT_BIRTH",
|
||||
"S_EVENT_HUMAN_FAILURE",
|
||||
"S_EVENT_ENGINE_STARTUP",
|
||||
"S_EVENT_ENGINE_SHUTDOWN",
|
||||
"S_EVENT_PLAYER_ENTER_UNIT",
|
||||
"S_EVENT_PLAYER_LEAVE_UNIT",
|
||||
"S_EVENT_PLAYER_COMMENT",
|
||||
"S_EVENT_SHOOTING_START",
|
||||
"S_EVENT_SHOOTING_END",
|
||||
"S_EVENT_MAX",
|
||||
}
|
||||
|
||||
--onEvent( {[1]="S_EVENT_BIRTH",[2]={["subPlace"]=5,["time"]=0,["initiator"]={["id_"]=16884480,},["place"]={["id_"]=5000040,},["id"]=15,["IniUnitName"]="US F-15C@RAMP-Air Support Mountains#001-01",},}
|
||||
-- Event = {
|
||||
-- id = enum world.event,
|
||||
-- time = Time,
|
||||
-- initiator = Unit,
|
||||
-- target = Unit,
|
||||
-- place = Unit,
|
||||
-- subPlace = enum world.BirthPlace,
|
||||
-- weapon = Weapon
|
||||
-- }
|
||||
|
||||
--- Creation of a Birth Event.
|
||||
-- @param #BASE self
|
||||
-- @param DCSTypes#Time EventTime The time stamp of the event.
|
||||
-- @param DCSObject#Object Initiator The initiating object of the event.
|
||||
-- @param #string IniUnitName The initiating unit name.
|
||||
-- @param place
|
||||
-- @param subplace
|
||||
function BASE:CreateEventBirth( EventTime, Initiator, IniUnitName, place, subplace )
|
||||
self:F( { EventTime, Initiator, IniUnitName, place, subplace } )
|
||||
|
||||
local Event = {
|
||||
id = world.event.S_EVENT_BIRTH,
|
||||
time = EventTime,
|
||||
initiator = Initiator,
|
||||
IniUnitName = IniUnitName,
|
||||
place = place,
|
||||
subplace = subplace
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a Crash Event.
|
||||
-- @param #BASE self
|
||||
-- @param DCSTypes#Time EventTime The time stamp of the event.
|
||||
-- @param DCSObject#Object Initiator The initiating object of the event.
|
||||
function BASE:CreateEventCrash( EventTime, Initiator )
|
||||
self:F( { EventTime, Initiator } )
|
||||
|
||||
local Event = {
|
||||
id = world.event.S_EVENT_CRASH,
|
||||
time = EventTime,
|
||||
initiator = Initiator,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
-- TODO: Complete DCSTypes#Event structure.
|
||||
--- The main event handling function... This function captures all events generated for the class.
|
||||
-- @param #BASE self
|
||||
-- @param DCSTypes#Event event
|
||||
function BASE:onEvent(event)
|
||||
--self:F( { BaseEventCodes[event.id], event } )
|
||||
|
||||
if self then
|
||||
for EventID, EventObject in pairs( self.Events ) do
|
||||
if EventObject.EventEnabled then
|
||||
--env.info( 'onEvent Table EventObject.Self = ' .. tostring(EventObject.Self) )
|
||||
--env.info( 'onEvent event.id = ' .. tostring(event.id) )
|
||||
--env.info( 'onEvent EventObject.Event = ' .. tostring(EventObject.Event) )
|
||||
if event.id == EventObject.Event then
|
||||
if self == EventObject.Self then
|
||||
if event.initiator and event.initiator:isExist() then
|
||||
event.IniUnitName = event.initiator:getName()
|
||||
end
|
||||
if event.target and event.target:isExist() then
|
||||
event.TgtUnitName = event.target:getName()
|
||||
end
|
||||
--self:T( { BaseEventCodes[event.id], event } )
|
||||
--EventObject.EventFunction( self, event )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Trace section
|
||||
|
||||
-- Log a trace (only shown when trace is on)
|
||||
-- TODO: Make trace function using variable parameters.
|
||||
|
||||
--- Set trace level
|
||||
-- @param #BASE self
|
||||
-- @param #number Level
|
||||
function BASE:TraceLevel( Level )
|
||||
_TraceLevel = Level
|
||||
self:E( "Tracing level " .. Level )
|
||||
end
|
||||
|
||||
--- Set tracing for a class
|
||||
-- @param #BASE self
|
||||
-- @param #string Class
|
||||
function BASE:TraceClass( Class )
|
||||
_TraceClass[Class] = true
|
||||
_TraceClassMethod[Class] = {}
|
||||
self:E( "Tracing class " .. Class )
|
||||
end
|
||||
|
||||
--- Set tracing for a specific method of class
|
||||
-- @param #BASE self
|
||||
-- @param #string Class
|
||||
-- @param #string Method
|
||||
function BASE:TraceClassMethod( Class, Method )
|
||||
if not _TraceClassMethod[Class] then
|
||||
_TraceClassMethod[Class] = {}
|
||||
_TraceClassMethod[Class].Method = {}
|
||||
end
|
||||
_TraceClassMethod[Class].Method[Method] = true
|
||||
self:E( "Tracing method " .. Method .. " of class " .. Class )
|
||||
end
|
||||
|
||||
--- Trace a function call. Must be at the beginning of the function logic.
|
||||
-- @param #BASE self
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F( Arguments )
|
||||
|
||||
if _TraceOn and ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
||||
|
||||
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = debug.getinfo( 3, "l" )
|
||||
|
||||
local Function = "function"
|
||||
if DebugInfoCurrent.name then
|
||||
Function = DebugInfoCurrent.name
|
||||
end
|
||||
|
||||
if _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName].Method[Function] then
|
||||
local LineCurrent = DebugInfoCurrent.currentline
|
||||
local LineFrom = 0
|
||||
if DebugInfoFrom then
|
||||
LineFrom = DebugInfoFrom.currentline
|
||||
end
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Trace a function call level 2. Must be at the beginning of the function logic.
|
||||
-- @param #BASE self
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F2( Arguments )
|
||||
|
||||
if _TraceLevel >= 2 then
|
||||
self:F( Arguments )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Trace a function call level 3. Must be at the beginning of the function logic.
|
||||
-- @param #BASE self
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:F3( Arguments )
|
||||
|
||||
if _TraceLevel >= 3 then
|
||||
self:F( Arguments )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Trace a function logic. Can be anywhere within the function logic.
|
||||
-- @param #BASE self
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T( Arguments )
|
||||
|
||||
if _TraceOn and ( _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName] ) then
|
||||
|
||||
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = debug.getinfo( 3, "l" )
|
||||
|
||||
local Function = "function"
|
||||
if DebugInfoCurrent.name then
|
||||
Function = DebugInfoCurrent.name
|
||||
end
|
||||
|
||||
if _TraceClass[self.ClassName] or _TraceClassMethod[self.ClassName].Method[Function] then
|
||||
local LineCurrent = DebugInfoCurrent.currentline
|
||||
local LineFrom = 0
|
||||
if DebugInfoFrom then
|
||||
LineFrom = DebugInfoFrom.currentline
|
||||
end
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s" , LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Trace a function logic level 2. Can be anywhere within the function logic.
|
||||
-- @param #BASE self
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T2( Arguments )
|
||||
|
||||
if _TraceLevel >= 2 then
|
||||
self:T( Arguments )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Trace a function logic level 3. Can be anywhere within the function logic.
|
||||
-- @param #BASE self
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:T3( Arguments )
|
||||
|
||||
if _TraceLevel >= 3 then
|
||||
self:T( Arguments )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Log an exception which will be traced always. Can be anywhere within the function logic.
|
||||
-- @param #BASE self
|
||||
-- @param Arguments A #table or any field.
|
||||
function BASE:E( Arguments )
|
||||
|
||||
local DebugInfoCurrent = debug.getinfo( 2, "nl" )
|
||||
local DebugInfoFrom = debug.getinfo( 3, "l" )
|
||||
|
||||
local Function = "function"
|
||||
if DebugInfoCurrent.name then
|
||||
Function = DebugInfoCurrent.name
|
||||
end
|
||||
|
||||
local LineCurrent = DebugInfoCurrent.currentline
|
||||
local LineFrom = DebugInfoFrom.currentline
|
||||
|
||||
env.info( string.format( "%6d(%6d)/%1s:%20s%05d.%s(%s)" , LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, routines.utils.oneLineSerialize( Arguments ) ) )
|
||||
end
|
||||
|
||||
|
||||
|
||||
1076
Moose Development/Moose/Cargo.lua
Normal file
1076
Moose Development/Moose/Cargo.lua
Normal file
File diff suppressed because it is too large
Load Diff
323
Moose Development/Moose/CleanUp.lua
Normal file
323
Moose Development/Moose/CleanUp.lua
Normal file
@@ -0,0 +1,323 @@
|
||||
--- The CLEANUP class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area.
|
||||
-- @module CleanUp
|
||||
-- @author Flightcontrol
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Mission" )
|
||||
Include.File( "Client" )
|
||||
Include.File( "Task" )
|
||||
|
||||
--- The CLEANUP class.
|
||||
-- @type CLEANUP
|
||||
-- @extends Base#BASE
|
||||
CLEANUP = {
|
||||
ClassName = "CLEANUP",
|
||||
ZoneNames = {},
|
||||
TimeInterval = 300,
|
||||
CleanUpList = {},
|
||||
}
|
||||
|
||||
--- Creates the main object which is handling the cleaning of the debris within the given Zone Names.
|
||||
-- @param #CLEANUP self
|
||||
-- @param #table ZoneNames Is a table of zone names where the debris should be cleaned. Also a single string can be passed with one zone name.
|
||||
-- @param #number TimeInterval The interval in seconds when the clean activity takes place. The default is 300 seconds, thus every 5 minutes.
|
||||
-- @return #CLEANUP
|
||||
-- @usage
|
||||
-- -- Clean these Zones.
|
||||
-- CleanUpAirports = CLEANUP:New( { 'CLEAN Tbilisi', 'CLEAN Kutaisi' }, 150 )
|
||||
-- or
|
||||
-- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 )
|
||||
-- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 )
|
||||
function CLEANUP:New( ZoneNames, TimeInterval ) local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { ZoneNames, TimeInterval } )
|
||||
|
||||
if type( ZoneNames ) == 'table' then
|
||||
self.ZoneNames = ZoneNames
|
||||
else
|
||||
self.ZoneNames = { ZoneNames }
|
||||
end
|
||||
if TimeInterval then
|
||||
self.TimeInterval = TimeInterval
|
||||
end
|
||||
|
||||
_EVENTDISPATCHER:OnBirth( self._OnEventBirth, self )
|
||||
|
||||
self.CleanUpScheduler = routines.scheduleFunction( self._CleanUpScheduler, { self }, timer.getTime() + 1, TimeInterval )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Destroys a group from the simulator, but checks first if it is still existing!
|
||||
-- @param #CLEANUP self
|
||||
-- @param DCSGroup#Group GroupObject The object to be destroyed.
|
||||
-- @param #string CleanUpGroupName The groupname...
|
||||
function CLEANUP:_DestroyGroup( GroupObject, CleanUpGroupName )
|
||||
self:F( { GroupObject, CleanUpGroupName } )
|
||||
|
||||
if GroupObject then -- and GroupObject:isExist() then
|
||||
--MESSAGE:New( "Destroy Group " .. CleanUpGroupName, CleanUpGroupName, 1, CleanUpGroupName ):ToAll()
|
||||
trigger.action.deactivateGroup(GroupObject)
|
||||
self:T( { "GroupObject Destroyed", GroupObject } )
|
||||
end
|
||||
end
|
||||
|
||||
--- Destroys a @{DCSUnit#Unit} from the simulator, but checks first if it is still existing!
|
||||
-- @param #CLEANUP self
|
||||
-- @param DCSUnit#Unit CleanUpUnit The object to be destroyed.
|
||||
-- @param #string CleanUpUnitName The Unit name ...
|
||||
function CLEANUP:_DestroyUnit( CleanUpUnit, CleanUpUnitName )
|
||||
self:F( { CleanUpUnit, CleanUpUnitName } )
|
||||
|
||||
if CleanUpUnit then
|
||||
--MESSAGE:New( "Destroy " .. CleanUpUnitName, CleanUpUnitName, 1, CleanUpUnitName ):ToAll()
|
||||
local CleanUpGroup = Unit.getGroup(CleanUpUnit)
|
||||
-- TODO Client bug in 1.5.3
|
||||
if CleanUpGroup and CleanUpGroup:isExist() then
|
||||
local CleanUpGroupUnits = CleanUpGroup:getUnits()
|
||||
if #CleanUpGroupUnits == 1 then
|
||||
local CleanUpGroupName = CleanUpGroup:getName()
|
||||
--self:CreateEventCrash( timer.getTime(), CleanUpUnit )
|
||||
CleanUpGroup:destroy()
|
||||
self:T( { "Destroyed Group:", CleanUpGroupName } )
|
||||
else
|
||||
CleanUpUnit:destroy()
|
||||
self:T( { "Destroyed Unit:", CleanUpUnitName } )
|
||||
end
|
||||
self.CleanUpList[CleanUpUnitName] = nil -- Cleaning from the list
|
||||
CleanUpUnit = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO check DCSTypes#Weapon
|
||||
--- Destroys a missile from the simulator, but checks first if it is still existing!
|
||||
-- @param #CLEANUP self
|
||||
-- @param DCSTypes#Weapon MissileObject
|
||||
function CLEANUP:_DestroyMissile( MissileObject )
|
||||
self:F( { MissileObject } )
|
||||
|
||||
if MissileObject and MissileObject:isExist() then
|
||||
MissileObject:destroy()
|
||||
self:T( "MissileObject Destroyed")
|
||||
end
|
||||
end
|
||||
|
||||
function CLEANUP:_OnEventBirth( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
self.CleanUpList[Event.IniDCSUnitName] = {}
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
|
||||
|
||||
_EVENTDISPATCHER:OnEngineShutDownForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self )
|
||||
_EVENTDISPATCHER:OnEngineStartUpForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self )
|
||||
_EVENTDISPATCHER:OnHitForUnit( Event.IniDCSUnitName, self._EventAddForCleanUp, self )
|
||||
_EVENTDISPATCHER:OnPilotDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self )
|
||||
_EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self._EventCrash, self )
|
||||
_EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self._EventCrash, self )
|
||||
_EVENTDISPATCHER:OnShotForUnit( Event.IniDCSUnitName, self._EventShot, self )
|
||||
|
||||
--self:AddEvent( world.event.S_EVENT_ENGINE_SHUTDOWN, self._EventAddForCleanUp )
|
||||
--self:AddEvent( world.event.S_EVENT_ENGINE_STARTUP, self._EventAddForCleanUp )
|
||||
-- self:AddEvent( world.event.S_EVENT_HIT, self._EventAddForCleanUp ) -- , self._EventHitCleanUp )
|
||||
-- self:AddEvent( world.event.S_EVENT_CRASH, self._EventCrash ) -- , self._EventHitCleanUp )
|
||||
-- --self:AddEvent( world.event.S_EVENT_DEAD, self._EventCrash )
|
||||
-- self:AddEvent( world.event.S_EVENT_SHOT, self._EventShot )
|
||||
--
|
||||
-- self:EnableEvents()
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Detects if a crash event occurs.
|
||||
-- Crashed units go into a CleanUpList for removal.
|
||||
-- @param #CLEANUP self
|
||||
-- @param DCSTypes#Event event
|
||||
function CLEANUP:_EventCrash( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
--TODO: This stuff is not working due to a DCS bug. Burning units cannot be destroyed.
|
||||
--MESSAGE:New( "Crash ", "Crash", 10, "Crash" ):ToAll()
|
||||
-- self:T("before getGroup")
|
||||
-- local _grp = Unit.getGroup(event.initiator)-- Identify the group that fired
|
||||
-- self:T("after getGroup")
|
||||
-- _grp:destroy()
|
||||
-- self:T("after deactivateGroup")
|
||||
-- event.initiator:destroy()
|
||||
|
||||
self.CleanUpList[Event.IniDCSUnitName] = {}
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
|
||||
|
||||
end
|
||||
|
||||
--- Detects if a unit shoots a missile.
|
||||
-- If this occurs within one of the zones, then the weapon used must be destroyed.
|
||||
-- @param #CLEANUP self
|
||||
-- @param DCSTypes#Event event
|
||||
function CLEANUP:_EventShot( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
-- Test if the missile was fired within one of the CLEANUP.ZoneNames.
|
||||
local CurrentLandingZoneID = 0
|
||||
CurrentLandingZoneID = routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames )
|
||||
if ( CurrentLandingZoneID ) then
|
||||
-- Okay, the missile was fired within the CLEANUP.ZoneNames, destroy the fired weapon.
|
||||
--_SEADmissile:destroy()
|
||||
routines.scheduleFunction( CLEANUP._DestroyMissile, { self, Event.Weapon }, timer.getTime() + 0.1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Detects if the Unit has an S_EVENT_HIT within the given ZoneNames. If this is the case, destroy the unit.
|
||||
-- @param #CLEANUP self
|
||||
-- @param DCSTypes#Event event
|
||||
function CLEANUP:_EventHitCleanUp( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then
|
||||
self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniDCSUnit:getLife(), "/", Event.IniDCSUnit:getLife0() } )
|
||||
if Event.IniDCSUnit:getLife() < Event.IniDCSUnit:getLife0() then
|
||||
self:T( "CleanUp: Destroy: " .. Event.IniDCSUnitName )
|
||||
routines.scheduleFunction( CLEANUP._DestroyUnit, { self, Event.IniDCSUnit }, timer.getTime() + 0.1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Event.TgtDCSUnit then
|
||||
if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then
|
||||
self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtDCSUnit:getLife(), "/", Event.TgtDCSUnit:getLife0() } )
|
||||
if Event.TgtDCSUnit:getLife() < Event.TgtDCSUnit:getLife0() then
|
||||
self:T( "CleanUp: Destroy: " .. Event.TgtDCSUnitName )
|
||||
routines.scheduleFunction( CLEANUP._DestroyUnit, { self, Event.TgtDCSUnit }, timer.getTime() + 0.1 )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Add the @{DCSUnit#Unit} to the CleanUpList for CleanUp.
|
||||
function CLEANUP:_AddForCleanUp( CleanUpUnit, CleanUpUnitName )
|
||||
self:F( { CleanUpUnit, CleanUpUnitName } )
|
||||
|
||||
self.CleanUpList[CleanUpUnitName] = {}
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpGroup = Unit.getGroup(CleanUpUnit)
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpGroupName = Unit.getGroup(CleanUpUnit):getName()
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpTime = timer.getTime()
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpMoved = false
|
||||
|
||||
self:T( { "CleanUp: Add to CleanUpList: ", Unit.getGroup(CleanUpUnit):getName(), CleanUpUnitName } )
|
||||
|
||||
end
|
||||
|
||||
--- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given ZoneNames. If this is the case, add the Group to the CLEANUP List.
|
||||
-- @param #CLEANUP self
|
||||
-- @param DCSTypes#Event event
|
||||
function CLEANUP:_EventAddForCleanUp( Event )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if self.CleanUpList[Event.IniDCSUnitName] == nil then
|
||||
if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then
|
||||
self:_AddForCleanUp( Event.IniDCSUnit, Event.IniDCSUnitName )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Event.TgtDCSUnit then
|
||||
if self.CleanUpList[Event.TgtDCSUnitName] == nil then
|
||||
if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then
|
||||
self:_AddForCleanUp( Event.TgtDCSUnit, Event.TgtDCSUnitName )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local CleanUpSurfaceTypeText = {
|
||||
"LAND",
|
||||
"SHALLOW_WATER",
|
||||
"WATER",
|
||||
"ROAD",
|
||||
"RUNWAY"
|
||||
}
|
||||
|
||||
--- At the defined time interval, CleanUp the Groups within the CleanUpList.
|
||||
-- @param #CLEANUP self
|
||||
function CLEANUP:_CleanUpScheduler()
|
||||
self:F( { "CleanUp Scheduler" } )
|
||||
|
||||
local CleanUpCount = 0
|
||||
for CleanUpUnitName, UnitData in pairs( self.CleanUpList ) do
|
||||
CleanUpCount = CleanUpCount + 1
|
||||
|
||||
self:T( { CleanUpUnitName, UnitData } )
|
||||
local CleanUpUnit = Unit.getByName(UnitData.CleanUpUnitName)
|
||||
local CleanUpGroupName = UnitData.CleanUpGroupName
|
||||
local CleanUpUnitName = UnitData.CleanUpUnitName
|
||||
if CleanUpUnit then
|
||||
self:T( { "CleanUp Scheduler", "Checking:", CleanUpUnitName } )
|
||||
if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then
|
||||
local CleanUpUnitVec3 = CleanUpUnit:getPoint()
|
||||
--self:T( CleanUpUnitVec3 )
|
||||
local CleanUpUnitVec2 = {}
|
||||
CleanUpUnitVec2.x = CleanUpUnitVec3.x
|
||||
CleanUpUnitVec2.y = CleanUpUnitVec3.z
|
||||
--self:T( CleanUpUnitVec2 )
|
||||
local CleanUpSurfaceType = land.getSurfaceType(CleanUpUnitVec2)
|
||||
--self:T( CleanUpSurfaceType )
|
||||
--MESSAGE:New( "Surface " .. CleanUpUnitName .. " = " .. CleanUpSurfaceTypeText[CleanUpSurfaceType], CleanUpUnitName, 10, CleanUpUnitName ):ToAll()
|
||||
|
||||
if CleanUpUnit and CleanUpUnit:getLife() <= CleanUpUnit:getLife0() * 0.95 then
|
||||
if CleanUpSurfaceType == land.SurfaceType.RUNWAY then
|
||||
if CleanUpUnit:inAir() then
|
||||
local CleanUpLandHeight = land.getHeight(CleanUpUnitVec2)
|
||||
local CleanUpUnitHeight = CleanUpUnitVec3.y - CleanUpLandHeight
|
||||
self:T( { "CleanUp Scheduler", "Height = " .. CleanUpUnitHeight } )
|
||||
if CleanUpUnitHeight < 30 then
|
||||
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because below safe height and damaged." } )
|
||||
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
|
||||
end
|
||||
else
|
||||
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because on runway and damaged." } )
|
||||
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Clean Units which are waiting for a very long time in the CleanUpZone.
|
||||
if CleanUpUnit then
|
||||
local CleanUpUnitVelocity = CleanUpUnit:getVelocity()
|
||||
local CleanUpUnitVelocityTotal = math.abs(CleanUpUnitVelocity.x) + math.abs(CleanUpUnitVelocity.y) + math.abs(CleanUpUnitVelocity.z)
|
||||
if CleanUpUnitVelocityTotal < 1 then
|
||||
if UnitData.CleanUpMoved then
|
||||
if UnitData.CleanUpTime + 180 <= timer.getTime() then
|
||||
self:T( { "CleanUp Scheduler", "Destroy due to not moving anymore " .. CleanUpUnitName } )
|
||||
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
|
||||
end
|
||||
end
|
||||
else
|
||||
UnitData.CleanUpTime = timer.getTime()
|
||||
UnitData.CleanUpMoved = true
|
||||
--MESSAGE:New( "Moved " .. CleanUpUnitName, CleanUpUnitName, 10, CleanUpUnitName ):ToAll()
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
-- Do nothing ...
|
||||
self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE
|
||||
end
|
||||
else
|
||||
self:T( "CleanUp: Group " .. CleanUpUnitName .. " cannot be found in DCS RTE, removing ..." )
|
||||
self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE
|
||||
end
|
||||
end
|
||||
self:T(CleanUpCount)
|
||||
end
|
||||
|
||||
466
Moose Development/Moose/Client.lua
Normal file
466
Moose Development/Moose/Client.lua
Normal file
@@ -0,0 +1,466 @@
|
||||
--- The CLIENT models client units in multi player missions.
|
||||
--
|
||||
-- @{#CLIENT} class
|
||||
-- ================
|
||||
-- Clients are those **Units** defined within the Mission Editor that have the skillset defined as __Client__ or __Player__.
|
||||
-- Note that clients are NOT the same as Units, they are NOT necessarily alive.
|
||||
-- The @{CLIENT} class is a wrapper class to handle the DCS Unit objects that have the skillset defined as __Client__ or __Player__:
|
||||
--
|
||||
-- * Wraps the DCS Unit objects with skill level set to Player or Client.
|
||||
-- * Support all DCS Unit APIs.
|
||||
-- * Enhance with Unit specific APIs not in the DCS Group API set.
|
||||
-- * When player joins Unit, execute alive init logic.
|
||||
-- * Handles messages to players.
|
||||
-- * Manage the "state" of the DCS Unit.
|
||||
--
|
||||
-- Clients are being used by the @{MISSION} class to follow players and register their successes.
|
||||
--
|
||||
-- CLIENT reference methods
|
||||
-- =======================
|
||||
-- For each DCS Unit having skill level Player or Client, a CLIENT wrapper object (instance) will be created within the _@{DATABASE} object.
|
||||
-- This is done at the beginning of the mission (when the mission starts).
|
||||
--
|
||||
-- The CLIENT class does not contain a :New() method, rather it provides :Find() methods to retrieve the object reference
|
||||
-- using the DCS Unit or the DCS UnitName.
|
||||
--
|
||||
-- Another thing to know is that CLIENT objects do not "contain" the DCS Unit object.
|
||||
-- The CLIENT methods will reference the DCS Unit object by name when it is needed during API execution.
|
||||
-- If the DCS Unit object does not exist or is nil, the CLIENT methods will return nil and log an exception in the DCS.log file.
|
||||
--
|
||||
-- The CLIENT class provides the following functions to retrieve quickly the relevant CLIENT instance:
|
||||
--
|
||||
-- * @{#CLIENT.Find}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit object.
|
||||
-- * @{#CLIENT.FindByName}(): Find a CLIENT instance from the _DATABASE object using a DCS Unit name.
|
||||
--
|
||||
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these CLIENT OBJECT REFERENCES! (make the CLIENT object references nil).
|
||||
--
|
||||
-- @module Client
|
||||
-- @author FlightControl
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Cargo" )
|
||||
Include.File( "Message" )
|
||||
|
||||
|
||||
--- The CLIENT class
|
||||
-- @type CLIENT
|
||||
-- @extends Unit#UNIT
|
||||
CLIENT = {
|
||||
ONBOARDSIDE = {
|
||||
NONE = 0,
|
||||
LEFT = 1,
|
||||
RIGHT = 2,
|
||||
BACK = 3,
|
||||
FRONT = 4
|
||||
},
|
||||
ClassName = "CLIENT",
|
||||
ClientName = nil,
|
||||
ClientAlive = false,
|
||||
ClientTransport = false,
|
||||
ClientBriefingShown = false,
|
||||
_Menus = {},
|
||||
_Tasks = {},
|
||||
Messages = {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
--- Finds a CLIENT from the _DATABASE using the relevant DCS Unit.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor.
|
||||
-- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client.
|
||||
-- @return #CLIENT
|
||||
-- @usage
|
||||
-- -- Create new Clients.
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' )
|
||||
-- Mission:AddGoal( DeploySA6TroopsGoal )
|
||||
--
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
|
||||
function CLIENT:Find( DCSUnit )
|
||||
local ClientName = DCSUnit:getName()
|
||||
local ClientFound = _DATABASE:FindClient( ClientName )
|
||||
|
||||
if ClientFound then
|
||||
ClientFound:F( ClientName )
|
||||
return ClientFound
|
||||
end
|
||||
|
||||
error( "CLIENT not found for: " .. ClientName )
|
||||
end
|
||||
|
||||
|
||||
--- Finds a CLIENT from the _DATABASE using the relevant Client Unit Name.
|
||||
-- As an optional parameter, a briefing text can be given also.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string ClientName Name of the DCS **Unit** as defined within the Mission Editor.
|
||||
-- @param #string ClientBriefing Text that describes the briefing of the mission when a Player logs into the Client.
|
||||
-- @return #CLIENT
|
||||
-- @usage
|
||||
-- -- Create new Clients.
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' )
|
||||
-- Mission:AddGoal( DeploySA6TroopsGoal )
|
||||
--
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 1' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 3' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*HOT-Deploy Troops 2' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:FindByName( 'RU MI-8MTV2*RAMP-Deploy Troops 4' ):Transport() )
|
||||
function CLIENT:FindByName( ClientName, ClientBriefing )
|
||||
local ClientFound = _DATABASE:FindClient( ClientName )
|
||||
|
||||
if ClientFound then
|
||||
ClientFound:F( { ClientName, ClientBriefing } )
|
||||
ClientFound:AddBriefing( ClientBriefing )
|
||||
ClientFound.MessageSwitch = true
|
||||
|
||||
return ClientFound
|
||||
end
|
||||
|
||||
error( "CLIENT not found for: " .. ClientName )
|
||||
end
|
||||
|
||||
function CLIENT:Register( ClientName )
|
||||
local self = BASE:Inherit( self, UNIT:Register( ClientName ) )
|
||||
|
||||
self:F( ClientName )
|
||||
self.ClientName = ClientName
|
||||
self.MessageSwitch = true
|
||||
self.ClientAlive2 = false
|
||||
|
||||
self.AliveCheckScheduler = routines.scheduleFunction( self._AliveCheckScheduler, { self }, timer.getTime() + 1, 5 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Transport defines that the Client is a Transport. Transports show cargo.
|
||||
-- @param #CLIENT self
|
||||
-- @return #CLIENT
|
||||
function CLIENT:Transport()
|
||||
self:F()
|
||||
|
||||
self.ClientTransport = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- AddBriefing adds a briefing to a CLIENT when a player joins a mission.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string ClientBriefing is the text defining the Mission briefing.
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:AddBriefing( ClientBriefing )
|
||||
self:F( ClientBriefing )
|
||||
self.ClientBriefing = ClientBriefing
|
||||
self.ClientBriefingShown = false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Show the briefing of the MISSION to the CLIENT.
|
||||
-- @param #CLIENT self
|
||||
-- @return #CLIENT self
|
||||
function CLIENT:ShowBriefing()
|
||||
self:F( { self.ClientName, self.ClientBriefingShown } )
|
||||
|
||||
if not self.ClientBriefingShown then
|
||||
self.ClientBriefingShown = true
|
||||
local Briefing = ""
|
||||
if self.MissionBriefing then
|
||||
Briefing = Briefing .. self.MissionBriefing
|
||||
end
|
||||
if self.ClientBriefing then
|
||||
Briefing = Briefing .. "\n" .. self.ClientBriefing
|
||||
end
|
||||
Briefing = Briefing .. "\nPress [LEFT ALT]+[B] to view the complete mission briefing."
|
||||
self:Message( Briefing, 30, self.ClientName .. '/MissionBriefing', "Briefing" )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Resets a CLIENT.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string ClientName Name of the Group as defined within the Mission Editor. The Group must have a Unit with the type Client.
|
||||
function CLIENT:Reset( ClientName )
|
||||
self:F()
|
||||
self._Menus = {}
|
||||
end
|
||||
|
||||
-- Is Functions
|
||||
|
||||
--- Checks if the CLIENT is a multi-seated UNIT.
|
||||
-- @param #CLIENT self
|
||||
-- @return #boolean true if multi-seated.
|
||||
function CLIENT:IsMultiSeated()
|
||||
self:F( self.ClientName )
|
||||
|
||||
local ClientMultiSeatedTypes = {
|
||||
["Mi-8MT"] = "Mi-8MT",
|
||||
["UH-1H"] = "UH-1H",
|
||||
["P-51B"] = "P-51B"
|
||||
}
|
||||
|
||||
if self:IsAlive() then
|
||||
local ClientTypeName = self:GetClientGroupUnit():GetTypeName()
|
||||
if ClientMultiSeatedTypes[ClientTypeName] then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Checks for a client alive event and calls a function on a continuous basis.
|
||||
-- @param #CLIENT self
|
||||
-- @param #function CallBack Function.
|
||||
-- @return #CLIENT
|
||||
function CLIENT:Alive( CallBack, ... )
|
||||
self:F()
|
||||
|
||||
self.ClientCallBack = CallBack
|
||||
self.ClientParameters = arg
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #CLIENT self
|
||||
function CLIENT:_AliveCheckScheduler()
|
||||
self:F( { self.ClientName, self.ClientAlive2, self.ClientBriefingShown } )
|
||||
|
||||
if self:IsAlive() then -- Polymorphic call of UNIT
|
||||
if self.ClientAlive2 == false then
|
||||
self:ShowBriefing()
|
||||
if self.ClientCallBack then
|
||||
self:T("Calling Callback function")
|
||||
self.ClientCallBack( self, unpack( self.ClientParameters ) )
|
||||
end
|
||||
self.ClientAlive2 = true
|
||||
end
|
||||
else
|
||||
if self.ClientAlive2 == true then
|
||||
self.ClientAlive2 = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Return the DCSGroup of a Client.
|
||||
-- This function is modified to deal with a couple of bugs in DCS 1.5.3
|
||||
-- @param #CLIENT self
|
||||
-- @return DCSGroup#Group
|
||||
function CLIENT:GetDCSGroup()
|
||||
self:F3()
|
||||
|
||||
-- local ClientData = Group.getByName( self.ClientName )
|
||||
-- if ClientData and ClientData:isExist() then
|
||||
-- self:T( self.ClientName .. " : group found!" )
|
||||
-- return ClientData
|
||||
-- else
|
||||
-- return nil
|
||||
-- end
|
||||
|
||||
local ClientUnit = Unit.getByName( self.ClientName )
|
||||
|
||||
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
self:T3( { "CoalitionData:", CoalitionData } )
|
||||
for UnitId, UnitData in pairs( CoalitionData ) do
|
||||
self:T3( { "UnitData:", UnitData } )
|
||||
if UnitData and UnitData:isExist() then
|
||||
|
||||
--self:E(self.ClientName)
|
||||
if ClientUnit then
|
||||
local ClientGroup = ClientUnit:getGroup()
|
||||
if ClientGroup then
|
||||
self:T3( "ClientGroup = " .. self.ClientName )
|
||||
if ClientGroup:isExist() and UnitData:getGroup():isExist() then
|
||||
if ClientGroup:getID() == UnitData:getGroup():getID() then
|
||||
self:T3( "Normal logic" )
|
||||
self:T3( self.ClientName .. " : group found!" )
|
||||
self.ClientGroupID = ClientGroup:getID()
|
||||
self.ClientGroupName = ClientGroup:getName()
|
||||
return ClientGroup
|
||||
end
|
||||
else
|
||||
-- Now we need to resolve the bugs in DCS 1.5 ...
|
||||
-- Consult the database for the units of the Client Group. (ClientGroup:getUnits() returns nil)
|
||||
self:T3( "Bug 1.5 logic" )
|
||||
local ClientGroupTemplate = _DATABASE.Templates.Units[self.ClientName].GroupTemplate
|
||||
self.ClientGroupID = ClientGroupTemplate.groupId
|
||||
self.ClientGroupName = _DATABASE.Templates.Units[self.ClientName].GroupName
|
||||
self:T3( self.ClientName .. " : group found in bug 1.5 resolvement logic!" )
|
||||
return ClientGroup
|
||||
end
|
||||
-- else
|
||||
-- error( "Client " .. self.ClientName .. " not found!" )
|
||||
end
|
||||
else
|
||||
--self:E( { "Client not found!", self.ClientName } )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- For non player clients
|
||||
if ClientUnit then
|
||||
local ClientGroup = ClientUnit:getGroup()
|
||||
if ClientGroup then
|
||||
self:T3( "ClientGroup = " .. self.ClientName )
|
||||
if ClientGroup:isExist() then
|
||||
self:T3( "Normal logic" )
|
||||
self:T3( self.ClientName .. " : group found!" )
|
||||
return ClientGroup
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self.ClientGroupID = nil
|
||||
self.ClientGroupUnit = nil
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
-- TODO: Check DCSTypes#Group.ID
|
||||
--- Get the group ID of the client.
|
||||
-- @param #CLIENT self
|
||||
-- @return DCSTypes#Group.ID
|
||||
function CLIENT:GetClientGroupID()
|
||||
|
||||
local ClientGroup = self:GetDCSGroup()
|
||||
|
||||
--self:E( self.ClientGroupID ) -- Determined in GetDCSGroup()
|
||||
return self.ClientGroupID
|
||||
end
|
||||
|
||||
|
||||
--- Get the name of the group of the client.
|
||||
-- @param #CLIENT self
|
||||
-- @return #string
|
||||
function CLIENT:GetClientGroupName()
|
||||
|
||||
local ClientGroup = self:GetDCSGroup()
|
||||
|
||||
self:T( self.ClientGroupName ) -- Determined in GetDCSGroup()
|
||||
return self.ClientGroupName
|
||||
end
|
||||
|
||||
--- Returns the UNIT of the CLIENT.
|
||||
-- @param #CLIENT self
|
||||
-- @return Unit#UNIT
|
||||
function CLIENT:GetClientGroupUnit()
|
||||
self:F2()
|
||||
|
||||
local ClientDCSUnit = Unit.getByName( self.ClientName )
|
||||
|
||||
self:T( self.ClientDCSUnit )
|
||||
if ClientDCSUnit and ClientDCSUnit:isExist() then
|
||||
local ClientUnit = _DATABASE:FindUnit( self.ClientName )
|
||||
self:T2( ClientUnit )
|
||||
return ClientUnit
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns the DCSUnit of the CLIENT.
|
||||
-- @param #CLIENT self
|
||||
-- @return DCSTypes#Unit
|
||||
function CLIENT:GetClientGroupDCSUnit()
|
||||
self:F2()
|
||||
|
||||
local ClientDCSUnit = Unit.getByName( self.ClientName )
|
||||
|
||||
if ClientDCSUnit and ClientDCSUnit:isExist() then
|
||||
self:T2( ClientDCSUnit )
|
||||
return ClientDCSUnit
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Evaluates if the CLIENT is a transport.
|
||||
-- @param #CLIENT self
|
||||
-- @return #boolean true is a transport.
|
||||
function CLIENT:IsTransport()
|
||||
self:F()
|
||||
return self.ClientTransport
|
||||
end
|
||||
|
||||
--- Shows the @{Cargo#CARGO} contained within the CLIENT to the player as a message.
|
||||
-- The @{Cargo#CARGO} is shown using the @{Message#MESSAGE} distribution system.
|
||||
-- @param #CLIENT self
|
||||
function CLIENT:ShowCargo()
|
||||
self:F()
|
||||
|
||||
local CargoMsg = ""
|
||||
|
||||
for CargoName, Cargo in pairs( CARGOS ) do
|
||||
if self == Cargo:IsLoadedInClient() then
|
||||
CargoMsg = CargoMsg .. Cargo.CargoName .. " Type:" .. Cargo.CargoType .. " Weight: " .. Cargo.CargoWeight .. "\n"
|
||||
end
|
||||
end
|
||||
|
||||
if CargoMsg == "" then
|
||||
CargoMsg = "empty"
|
||||
end
|
||||
|
||||
self:Message( CargoMsg, 15, self.ClientName .. "/Cargo", "Co-Pilot: Cargo Status", 30 )
|
||||
|
||||
end
|
||||
|
||||
-- TODO (1) I urgently need to revise this.
|
||||
--- A local function called by the DCS World Menu system to switch off messages.
|
||||
function CLIENT.SwitchMessages( PrmTable )
|
||||
PrmTable[1].MessageSwitch = PrmTable[2]
|
||||
end
|
||||
|
||||
--- The main message driver for the CLIENT.
|
||||
-- This function displays various messages to the Player logged into the CLIENT through the DCS World Messaging system.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string Message is the text describing the message.
|
||||
-- @param #number MessageDuration is the duration in seconds that the Message should be displayed.
|
||||
-- @param #string MessageId is a text identifying the Message in the MessageQueue. The Message system overwrites Messages with the same MessageId
|
||||
-- @param #string MessageCategory is the category of the message (the title).
|
||||
-- @param #number MessageInterval is the interval in seconds between the display of the @{Message#MESSAGE} when the CLIENT is in the air.
|
||||
function CLIENT:Message( Message, MessageDuration, MessageId, MessageCategory, MessageInterval )
|
||||
self:F( { Message, MessageDuration, MessageId, MessageCategory, MessageInterval } )
|
||||
|
||||
if not self.MenuMessages then
|
||||
if self:GetClientGroupID() then
|
||||
self.MenuMessages = MENU_CLIENT:New( self, 'Messages' )
|
||||
self.MenuRouteMessageOn = MENU_CLIENT_COMMAND:New( self, 'Messages On', self.MenuMessages, CLIENT.SwitchMessages, { self, true } )
|
||||
self.MenuRouteMessageOff = MENU_CLIENT_COMMAND:New( self,'Messages Off', self.MenuMessages, CLIENT.SwitchMessages, { self, false } )
|
||||
end
|
||||
end
|
||||
|
||||
if self.MessageSwitch == true then
|
||||
if MessageCategory == nil then
|
||||
MessageCategory = "Messages"
|
||||
end
|
||||
if self.Messages[MessageId] == nil then
|
||||
self.Messages[MessageId] = {}
|
||||
self.Messages[MessageId].MessageId = MessageId
|
||||
self.Messages[MessageId].MessageTime = timer.getTime()
|
||||
self.Messages[MessageId].MessageDuration = MessageDuration
|
||||
if MessageInterval == nil then
|
||||
self.Messages[MessageId].MessageInterval = 600
|
||||
else
|
||||
self.Messages[MessageId].MessageInterval = MessageInterval
|
||||
end
|
||||
MESSAGE:New( Message, MessageCategory, MessageDuration, MessageId ):ToClient( self )
|
||||
else
|
||||
if self:GetClientGroupDCSUnit() and not self:GetClientGroupDCSUnit():inAir() then
|
||||
if timer.getTime() - self.Messages[MessageId].MessageTime >= self.Messages[MessageId].MessageDuration + 10 then
|
||||
MESSAGE:New( Message, MessageCategory, MessageDuration, MessageId ):ToClient( self )
|
||||
self.Messages[MessageId].MessageTime = timer.getTime()
|
||||
end
|
||||
else
|
||||
if timer.getTime() - self.Messages[MessageId].MessageTime >= self.Messages[MessageId].MessageDuration + self.Messages[MessageId].MessageInterval then
|
||||
MESSAGE:New( Message, MessageCategory, MessageDuration, MessageId ):ToClient( self )
|
||||
self.Messages[MessageId].MessageTime = timer.getTime()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
705
Moose Development/Moose/Database.lua
Normal file
705
Moose Development/Moose/Database.lua
Normal file
@@ -0,0 +1,705 @@
|
||||
--- Manage sets of units and groups.
|
||||
--
|
||||
-- @{#Database} class
|
||||
-- ==================
|
||||
-- Mission designers can use the DATABASE class to build sets of units belonging to certain:
|
||||
--
|
||||
-- * Coalitions
|
||||
-- * Categories
|
||||
-- * Countries
|
||||
-- * Unit types
|
||||
-- * Starting with certain prefix strings.
|
||||
--
|
||||
-- This list will grow over time. Planned developments are to include filters and iterators.
|
||||
-- Additional filters will be added around @{Zone#ZONEs}, Radiuses, Active players, ...
|
||||
-- More iterators will be implemented in the near future ...
|
||||
--
|
||||
-- Administers the Initial Sets of the Mission Templates as defined within the Mission Editor.
|
||||
--
|
||||
-- DATABASE construction methods:
|
||||
-- =================================
|
||||
-- Create a new DATABASE object with the @{#DATABASE.New} method:
|
||||
--
|
||||
-- * @{#DATABASE.New}: Creates a new DATABASE object.
|
||||
--
|
||||
--
|
||||
-- DATABASE filter criteria:
|
||||
-- =========================
|
||||
-- You can set filter criteria to define the set of units within the database.
|
||||
-- Filter criteria are defined by:
|
||||
--
|
||||
-- * @{#DATABASE.FilterCoalitions}: Builds the DATABASE with the units belonging to the coalition(s).
|
||||
-- * @{#DATABASE.FilterCategories}: Builds the DATABASE with the units belonging to the category(ies).
|
||||
-- * @{#DATABASE.FilterTypes}: Builds the DATABASE with the units belonging to the unit type(s).
|
||||
-- * @{#DATABASE.FilterCountries}: Builds the DATABASE with the units belonging to the country(ies).
|
||||
-- * @{#DATABASE.FilterUnitPrefixes}: Builds the DATABASE with the units starting with the same prefix string(s).
|
||||
--
|
||||
-- Once the filter criteria have been set for the DATABASE, you can start filtering using:
|
||||
--
|
||||
-- * @{#DATABASE.FilterStart}: Starts the filtering of the units within the database.
|
||||
--
|
||||
-- Planned filter criteria within development are (so these are not yet available):
|
||||
--
|
||||
-- * @{#DATABASE.FilterGroupPrefixes}: Builds the DATABASE with the groups of the units starting with the same prefix string(s).
|
||||
-- * @{#DATABASE.FilterZones}: Builds the DATABASE with the units within a @{Zone#ZONE}.
|
||||
--
|
||||
--
|
||||
-- DATABASE iterators:
|
||||
-- ===================
|
||||
-- Once the filters have been defined and the DATABASE has been built, you can iterate the database with the available iterator methods.
|
||||
-- The iterator methods will walk the DATABASE set, and call for each element within the set a function that you provide.
|
||||
-- The following iterator methods are currently available within the DATABASE:
|
||||
--
|
||||
-- * @{#DATABASE.ForEachAliveUnit}: Calls a function for each alive unit it finds within the DATABASE.
|
||||
--
|
||||
-- Planned iterators methods in development are (so these are not yet available):
|
||||
--
|
||||
-- * @{#DATABASE.ForEachUnit}: Calls a function for each unit contained within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachGroup}: Calls a function for each group contained within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachUnitInZone}: Calls a function for each unit within a certain zone contained within the DATABASE.
|
||||
--
|
||||
-- ====
|
||||
-- @module Database
|
||||
-- @author FlightControl
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Menu" )
|
||||
Include.File( "Group" )
|
||||
Include.File( "Unit" )
|
||||
Include.File( "Event" )
|
||||
Include.File( "Client" )
|
||||
|
||||
--- DATABASE class
|
||||
-- @type DATABASE
|
||||
-- @extends Base#BASE
|
||||
DATABASE = {
|
||||
ClassName = "DATABASE",
|
||||
Templates = {
|
||||
Units = {},
|
||||
Groups = {},
|
||||
ClientsByName = {},
|
||||
ClientsByID = {},
|
||||
},
|
||||
DCSUnits = {},
|
||||
DCSGroups = {},
|
||||
UNITS = {},
|
||||
GROUPS = {},
|
||||
NavPoints = {},
|
||||
Statics = {},
|
||||
Players = {},
|
||||
PlayersAlive = {},
|
||||
CLIENTS = {},
|
||||
ClientsAlive = {},
|
||||
Filter = {
|
||||
Coalitions = nil,
|
||||
Categories = nil,
|
||||
Types = nil,
|
||||
Countries = nil,
|
||||
UnitPrefixes = nil,
|
||||
GroupPrefixes = nil,
|
||||
},
|
||||
FilterMeta = {
|
||||
Coalitions = {
|
||||
red = coalition.side.RED,
|
||||
blue = coalition.side.BLUE,
|
||||
neutral = coalition.side.NEUTRAL,
|
||||
},
|
||||
Categories = {
|
||||
plane = Unit.Category.AIRPLANE,
|
||||
helicopter = Unit.Category.HELICOPTER,
|
||||
ground = Unit.Category.GROUND_UNIT,
|
||||
ship = Unit.Category.SHIP,
|
||||
structure = Unit.Category.STRUCTURE,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local _DATABASECoalition =
|
||||
{
|
||||
[1] = "Red",
|
||||
[2] = "Blue",
|
||||
}
|
||||
|
||||
local _DATABASECategory =
|
||||
{
|
||||
[Unit.Category.AIRPLANE] = "Plane",
|
||||
[Unit.Category.HELICOPTER] = "Helicopter",
|
||||
[Unit.Category.GROUND_UNIT] = "Vehicle",
|
||||
[Unit.Category.SHIP] = "Ship",
|
||||
[Unit.Category.STRUCTURE] = "Structure",
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new DATABASE object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.
|
||||
-- @param #DATABASE self
|
||||
-- @return #DATABASE
|
||||
-- @usage
|
||||
-- -- Define a new DATABASE Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE.
|
||||
-- DBObject = DATABASE:New()
|
||||
function DATABASE:New()
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
|
||||
_EVENTDISPATCHER:OnBirth( self._EventOnBirth, self )
|
||||
_EVENTDISPATCHER:OnDead( self._EventOnDeadOrCrash, self )
|
||||
_EVENTDISPATCHER:OnCrash( self._EventOnDeadOrCrash, self )
|
||||
|
||||
|
||||
-- Add database with registered clients and already alive players
|
||||
|
||||
-- Follow alive players and clients
|
||||
_EVENTDISPATCHER:OnPlayerEnterUnit( self._EventOnPlayerEnterUnit, self )
|
||||
_EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventOnPlayerLeaveUnit, self )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Finds a Unit based on the Unit Name.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string UnitName
|
||||
-- @return Unit#UNIT The found Unit.
|
||||
function DATABASE:FindUnit( UnitName )
|
||||
|
||||
local UnitFound = self.UNITS[UnitName]
|
||||
return UnitFound
|
||||
end
|
||||
|
||||
--- Adds a Unit based on the Unit Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:AddUnit( DCSUnit, DCSUnitName )
|
||||
|
||||
self.DCSUnits[DCSUnitName] = DCSUnit
|
||||
self.UNITS[DCSUnitName] = UNIT:Register( DCSUnitName )
|
||||
end
|
||||
|
||||
--- Deletes a Unit from the DATABASE based on the Unit Name.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:DeleteUnit( DCSUnitName )
|
||||
|
||||
self.DCSUnits[DCSUnitName] = nil
|
||||
end
|
||||
|
||||
--- Finds a CLIENT based on the ClientName.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string ClientName
|
||||
-- @return Client#CLIENT The found CLIENT.
|
||||
function DATABASE:FindClient( ClientName )
|
||||
|
||||
local ClientFound = self.CLIENTS[ClientName]
|
||||
return ClientFound
|
||||
end
|
||||
|
||||
--- Adds a CLIENT based on the ClientName in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:AddClient( ClientName )
|
||||
|
||||
self.CLIENTS[ClientName] = CLIENT:Register( ClientName )
|
||||
self:E( self.CLIENTS[ClientName]:GetClassNameAndID() )
|
||||
end
|
||||
|
||||
--- Finds a GROUP based on the GroupName.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string GroupName
|
||||
-- @return Group#GROUP The found GROUP.
|
||||
function DATABASE:FindGroup( GroupName )
|
||||
|
||||
local GroupFound = self.GROUPS[GroupName]
|
||||
return GroupFound
|
||||
end
|
||||
|
||||
--- Adds a GROUP based on the GroupName in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
function DATABASE:AddGroup( DCSGroup, GroupName )
|
||||
|
||||
self.DCSGroups[GroupName] = DCSGroup
|
||||
self.GROUPS[GroupName] = GROUP:Register( GroupName )
|
||||
end
|
||||
|
||||
--- Instantiate new Groups within the DCSRTE.
|
||||
-- This method expects EXACTLY the same structure as a structure within the ME, and needs 2 additional fields defined:
|
||||
-- SpawnCountryID, SpawnCategoryID
|
||||
-- This method is used by the SPAWN class.
|
||||
-- @param #DATABASE self
|
||||
-- @param #table SpawnTemplate
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:Spawn( SpawnTemplate )
|
||||
self:F( SpawnTemplate.name )
|
||||
|
||||
self:T( { SpawnTemplate.SpawnCountryID, SpawnTemplate.SpawnCategoryID } )
|
||||
|
||||
-- Copy the spawn variables of the template in temporary storage, nullify, and restore the spawn variables.
|
||||
local SpawnCoalitionID = SpawnTemplate.SpawnCoalitionID
|
||||
local SpawnCountryID = SpawnTemplate.SpawnCountryID
|
||||
local SpawnCategoryID = SpawnTemplate.SpawnCategoryID
|
||||
|
||||
-- Nullify
|
||||
SpawnTemplate.SpawnCoalitionID = nil
|
||||
SpawnTemplate.SpawnCountryID = nil
|
||||
SpawnTemplate.SpawnCategoryID = nil
|
||||
|
||||
self:_RegisterGroup( SpawnTemplate )
|
||||
coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate )
|
||||
|
||||
-- Restore
|
||||
SpawnTemplate.SpawnCoalitionID = SpawnCoalitionID
|
||||
SpawnTemplate.SpawnCountryID = SpawnCountryID
|
||||
SpawnTemplate.SpawnCategoryID = SpawnCategoryID
|
||||
|
||||
|
||||
local SpawnGroup = GROUP:Register( SpawnTemplate.name )
|
||||
return SpawnGroup
|
||||
end
|
||||
|
||||
|
||||
--- Set a status to a Group within the Database, this to check crossing events for example.
|
||||
function DATABASE:SetStatusGroup( GroupName, Status )
|
||||
self:F( Status )
|
||||
|
||||
self.Templates.Groups[GroupName].Status = Status
|
||||
end
|
||||
|
||||
|
||||
--- Get a status to a Group within the Database, this to check crossing events for example.
|
||||
function DATABASE:GetStatusGroup( GroupName )
|
||||
self:F( Status )
|
||||
|
||||
if self.Templates.Groups[GroupName] then
|
||||
return self.Templates.Groups[GroupName].Status
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
--- Private method that registers new Group Templates within the DATABASE Object.
|
||||
-- @param #DATABASE self
|
||||
-- @param #table GroupTemplate
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterGroup( GroupTemplate )
|
||||
|
||||
local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name)
|
||||
|
||||
if not self.Templates.Groups[GroupTemplateName] then
|
||||
self.Templates.Groups[GroupTemplateName] = {}
|
||||
self.Templates.Groups[GroupTemplateName].Status = nil
|
||||
end
|
||||
|
||||
-- Delete the spans from the route, it is not needed and takes memory.
|
||||
if GroupTemplate.route and GroupTemplate.route.spans then
|
||||
GroupTemplate.route.spans = nil
|
||||
end
|
||||
|
||||
self.Templates.Groups[GroupTemplateName].GroupName = GroupTemplateName
|
||||
self.Templates.Groups[GroupTemplateName].Template = GroupTemplate
|
||||
self.Templates.Groups[GroupTemplateName].groupId = GroupTemplate.groupId
|
||||
self.Templates.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units
|
||||
self.Templates.Groups[GroupTemplateName].Units = GroupTemplate.units
|
||||
|
||||
self:T( { "Group", self.Templates.Groups[GroupTemplateName].GroupName, self.Templates.Groups[GroupTemplateName].UnitCount } )
|
||||
|
||||
for unit_num, UnitTemplate in pairs( GroupTemplate.units ) do
|
||||
|
||||
local UnitTemplateName = env.getValueDictByKey(UnitTemplate.name)
|
||||
self.Templates.Units[UnitTemplateName] = {}
|
||||
self.Templates.Units[UnitTemplateName].UnitName = UnitTemplateName
|
||||
self.Templates.Units[UnitTemplateName].Template = UnitTemplate
|
||||
self.Templates.Units[UnitTemplateName].GroupName = GroupTemplateName
|
||||
self.Templates.Units[UnitTemplateName].GroupTemplate = GroupTemplate
|
||||
self.Templates.Units[UnitTemplateName].GroupId = GroupTemplate.groupId
|
||||
self:E( {"skill",UnitTemplate.skill})
|
||||
if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then
|
||||
self.Templates.ClientsByName[UnitTemplateName] = UnitTemplate
|
||||
self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate
|
||||
end
|
||||
self:E( { "Unit", self.Templates.Units[UnitTemplateName].UnitName } )
|
||||
end
|
||||
end
|
||||
|
||||
--- Private method that registers all alive players in the mission.
|
||||
-- @param #DATABASE self
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterPlayers()
|
||||
|
||||
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
for UnitId, UnitData in pairs( CoalitionData ) do
|
||||
self:T3( { "UnitData:", UnitData } )
|
||||
if UnitData and UnitData:isExist() then
|
||||
local UnitName = UnitData:getName()
|
||||
if not self.PlayersAlive[UnitName] then
|
||||
self:E( { "Add player for unit:", UnitName, UnitData:getPlayerName() } )
|
||||
self.PlayersAlive[UnitName] = UnitData:getPlayerName()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Private method that registers all datapoints within in the mission.
|
||||
-- @param #DATABASE self
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterDatabase()
|
||||
|
||||
local CoalitionsData = { GroupsRed = coalition.getGroups( coalition.side.RED ), GroupsBlue = coalition.getGroups( coalition.side.BLUE ) }
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
for DCSGroupId, DCSGroup in pairs( CoalitionData ) do
|
||||
|
||||
if DCSGroup:isExist() then
|
||||
local DCSGroupName = DCSGroup:getName()
|
||||
|
||||
self:E( { "Register Group:", DCSGroup, DCSGroupName } )
|
||||
self:AddGroup( DCSGroup, DCSGroupName )
|
||||
|
||||
for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do
|
||||
|
||||
local DCSUnitName = DCSUnit:getName()
|
||||
self:E( { "Register Unit:", DCSUnit, DCSUnitName } )
|
||||
self:AddUnit( DCSUnit, DCSUnitName )
|
||||
end
|
||||
else
|
||||
self:E( { "Group does not exist: ", DCSGroup } )
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do
|
||||
self:E( { "Adding Client:", ClientName } )
|
||||
self:AddClient( ClientName )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Events
|
||||
|
||||
--- Handles the OnBirth event for the alive units set.
|
||||
-- @param #DATABASE self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnBirth( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if self:_IsIncludeDCSUnit( Event.IniDCSUnit ) then
|
||||
self:AddUnit( Event.IniDCSUnit, Event.IniDCSUnitName )
|
||||
self:AddGroup( Event.IniDCSGroup, Event.IniDCSGroupName )
|
||||
self:_EventOnPlayerEnterUnit( Event )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnDead or OnCrash event for alive units set.
|
||||
-- @param #DATABASE self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnDeadOrCrash( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if self.DCSUnits[Event.IniDCSUnitName] then
|
||||
self:DeleteUnit( Event.IniDCSUnitName )
|
||||
-- add logic to correctly remove a group once all units are destroyed...
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied).
|
||||
-- @param #DATABASE self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnPlayerEnterUnit( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if self:_IsIncludeDCSUnit( Event.IniDCSUnit ) then
|
||||
if not self.PlayersAlive[Event.IniDCSUnitName] then
|
||||
self:E( { "Add player for unit:", Event.IniDCSUnitName, Event.IniDCSUnit:getPlayerName() } )
|
||||
self.PlayersAlive[Event.IniDCSUnitName] = Event.IniDCSUnit:getPlayerName()
|
||||
self.ClientsAlive[Event.IniDCSUnitName] = self.CLIENTS[ Event.IniDCSUnitName ]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnPlayerLeaveUnit event to clean the active players table.
|
||||
-- @param #DATABASE self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnPlayerLeaveUnit( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if self:_IsIncludeDCSUnit( Event.IniDCSUnit ) then
|
||||
if self.PlayersAlive[Event.IniDCSUnitName] then
|
||||
self:E( { "Cleaning player for unit:", Event.IniDCSUnitName, Event.IniDCSUnit:getPlayerName() } )
|
||||
self.PlayersAlive[Event.IniDCSUnitName] = nil
|
||||
self.ClientsAlive[Event.IniDCSUnitName] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Iterators
|
||||
|
||||
--- Interate the DATABASE and call an interator function for the given set, providing the Object for each element within the set and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive player in the database.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEach( IteratorFunction, arg, Set )
|
||||
self:F( arg )
|
||||
|
||||
local function CoRoutine()
|
||||
local Count = 0
|
||||
for ObjectID, Object in pairs( Set ) do
|
||||
self:T2( Object )
|
||||
IteratorFunction( Object, unpack( arg ) )
|
||||
Count = Count + 1
|
||||
if Count % 10 == 0 then
|
||||
coroutine.yield( false )
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local co = coroutine.create( CoRoutine )
|
||||
|
||||
local function Schedule()
|
||||
|
||||
local status, res = coroutine.resume( co )
|
||||
self:T( { status, res } )
|
||||
|
||||
if status == false then
|
||||
error( res )
|
||||
end
|
||||
if res == false then
|
||||
return true -- resume next time the loop
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local Scheduler = SCHEDULER:New( self, Schedule, {}, 0.001, 0.001, 0 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Interate the DATABASE and call an interator function for each **alive** unit, providing the Unit and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive unit in the database. The function needs to accept a UNIT parameter.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachDCSUnit( IteratorFunction, ... )
|
||||
self:F( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self.DCSUnits )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Interate the DATABASE and call an interator function for each **alive** player, providing the Unit of the player and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive player in the database. The function needs to accept a UNIT parameter.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachPlayer( IteratorFunction, ... )
|
||||
self:F( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self.PlayersAlive )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Interate the DATABASE and call an interator function for each client, providing the Client to the function and optional parameters.
|
||||
-- @param #DATABASE self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive player in the database. The function needs to accept a CLIENT parameter.
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:ForEachClient( IteratorFunction, ... )
|
||||
self:F( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self.CLIENTS )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function DATABASE:ScanEnvironment()
|
||||
self:F()
|
||||
|
||||
self.Navpoints = {}
|
||||
self.UNITS = {}
|
||||
--Build routines.db.units and self.Navpoints
|
||||
for coa_name, coa_data in pairs(env.mission.coalition) do
|
||||
|
||||
if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then
|
||||
--self.Units[coa_name] = {}
|
||||
|
||||
----------------------------------------------
|
||||
-- build nav points DB
|
||||
self.Navpoints[coa_name] = {}
|
||||
if coa_data.nav_points then --navpoints
|
||||
for nav_ind, nav_data in pairs(coa_data.nav_points) do
|
||||
|
||||
if type(nav_data) == 'table' then
|
||||
self.Navpoints[coa_name][nav_ind] = routines.utils.deepCopy(nav_data)
|
||||
|
||||
self.Navpoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory.
|
||||
self.Navpoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it.
|
||||
self.Navpoints[coa_name][nav_ind]['point']['x'] = nav_data.x
|
||||
self.Navpoints[coa_name][nav_ind]['point']['y'] = 0
|
||||
self.Navpoints[coa_name][nav_ind]['point']['z'] = nav_data.y
|
||||
end
|
||||
end
|
||||
end
|
||||
-------------------------------------------------
|
||||
if coa_data.country then --there is a country table
|
||||
for cntry_id, cntry_data in pairs(coa_data.country) do
|
||||
|
||||
local countryName = string.lower(cntry_data.name)
|
||||
--self.Units[coa_name][countryName] = {}
|
||||
--self.Units[coa_name][countryName]["countryId"] = cntry_data.id
|
||||
|
||||
if type(cntry_data) == 'table' then --just making sure
|
||||
|
||||
for obj_type_name, obj_type_data in pairs(cntry_data) do
|
||||
|
||||
if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then --should be an unncessary check
|
||||
|
||||
local category = obj_type_name
|
||||
|
||||
if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group!
|
||||
|
||||
--self.Units[coa_name][countryName][category] = {}
|
||||
|
||||
for group_num, GroupTemplate in pairs(obj_type_data.group) do
|
||||
|
||||
if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group
|
||||
self:_RegisterGroup( GroupTemplate )
|
||||
end --if GroupTemplate and GroupTemplate.units then
|
||||
end --for group_num, GroupTemplate in pairs(obj_type_data.group) do
|
||||
end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then
|
||||
end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then
|
||||
end --for obj_type_name, obj_type_data in pairs(cntry_data) do
|
||||
end --if type(cntry_data) == 'table' then
|
||||
end --for cntry_id, cntry_data in pairs(coa_data.country) do
|
||||
end --if coa_data.country then --there is a country table
|
||||
end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then
|
||||
end --for coa_name, coa_data in pairs(mission.coalition) do
|
||||
|
||||
self:_RegisterDatabase()
|
||||
self:_RegisterPlayers()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #DATABASE self
|
||||
-- @param DCSUnit#Unit DCSUnit
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_IsIncludeDCSUnit( DCSUnit )
|
||||
self:F( DCSUnit )
|
||||
local DCSUnitInclude = true
|
||||
|
||||
if self.Filter.Coalitions then
|
||||
local DCSUnitCoalition = false
|
||||
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
||||
self:T( { "Coalition:", DCSUnit:getCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
||||
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == DCSUnit:getCoalition() then
|
||||
DCSUnitCoalition = true
|
||||
end
|
||||
end
|
||||
DCSUnitInclude = DCSUnitInclude and DCSUnitCoalition
|
||||
end
|
||||
|
||||
if self.Filter.Categories then
|
||||
local DCSUnitCategory = false
|
||||
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
|
||||
self:T( { "Category:", DCSUnit:getDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } )
|
||||
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == DCSUnit:getDesc().category then
|
||||
DCSUnitCategory = true
|
||||
end
|
||||
end
|
||||
DCSUnitInclude = DCSUnitInclude and DCSUnitCategory
|
||||
end
|
||||
|
||||
if self.Filter.Types then
|
||||
local DCSUnitType = false
|
||||
for TypeID, TypeName in pairs( self.Filter.Types ) do
|
||||
self:T( { "Type:", DCSUnit:getTypeName(), TypeName } )
|
||||
if TypeName == DCSUnit:getTypeName() then
|
||||
DCSUnitType = true
|
||||
end
|
||||
end
|
||||
DCSUnitInclude = DCSUnitInclude and DCSUnitType
|
||||
end
|
||||
|
||||
if self.Filter.Countries then
|
||||
local DCSUnitCountry = false
|
||||
for CountryID, CountryName in pairs( self.Filter.Countries ) do
|
||||
self:T( { "Country:", DCSUnit:getCountry(), CountryName } )
|
||||
if country.id[CountryName] == DCSUnit:getCountry() then
|
||||
DCSUnitCountry = true
|
||||
end
|
||||
end
|
||||
DCSUnitInclude = DCSUnitInclude and DCSUnitCountry
|
||||
end
|
||||
|
||||
if self.Filter.UnitPrefixes then
|
||||
local DCSUnitPrefix = false
|
||||
for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do
|
||||
self:T( { "Unit Prefix:", string.find( DCSUnit:getName(), UnitPrefix, 1 ), UnitPrefix } )
|
||||
if string.find( DCSUnit:getName(), UnitPrefix, 1 ) then
|
||||
DCSUnitPrefix = true
|
||||
end
|
||||
end
|
||||
DCSUnitInclude = DCSUnitInclude and DCSUnitPrefix
|
||||
end
|
||||
|
||||
self:T( DCSUnitInclude )
|
||||
return DCSUnitInclude
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #DATABASE self
|
||||
-- @param DCSUnit#Unit DCSUnit
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_IsAliveDCSUnit( DCSUnit )
|
||||
self:F( DCSUnit )
|
||||
local DCSUnitAlive = false
|
||||
if DCSUnit and DCSUnit:isExist() and DCSUnit:isActive() then
|
||||
if self.DCSUnits[DCSUnit:getName()] then
|
||||
DCSUnitAlive = true
|
||||
end
|
||||
end
|
||||
self:T( DCSUnitAlive )
|
||||
return DCSUnitAlive
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #DATABASE self
|
||||
-- @param DCSGroup#Group DCSGroup
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_IsAliveDCSGroup( DCSGroup )
|
||||
self:F( DCSGroup )
|
||||
local DCSGroupAlive = false
|
||||
if DCSGroup and DCSGroup:isExist() then
|
||||
if self.DCSGroups[DCSGroup:getName()] then
|
||||
DCSGroupAlive = true
|
||||
end
|
||||
end
|
||||
self:T( DCSGroupAlive )
|
||||
return DCSGroupAlive
|
||||
end
|
||||
|
||||
|
||||
--- Traces the current database contents in the log ... (for debug reasons).
|
||||
-- @param #DATABASE self
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:TraceDatabase()
|
||||
self:F()
|
||||
|
||||
self:T( { "DCSUnits:", self.DCSUnits } )
|
||||
end
|
||||
|
||||
|
||||
158
Moose Development/Moose/DeployTask.lua
Normal file
158
Moose Development/Moose/DeployTask.lua
Normal file
@@ -0,0 +1,158 @@
|
||||
--- A DEPLOYTASK orchestrates the deployment of CARGO within a specific landing zone.
|
||||
-- @module DEPLOYTASK
|
||||
|
||||
Include.File( "Task" )
|
||||
|
||||
--- A DeployTask
|
||||
-- @type DEPLOYTASK
|
||||
DEPLOYTASK = {
|
||||
ClassName = "DEPLOYTASK",
|
||||
TEXT = { "Deploy", "deployed", "unloaded" },
|
||||
GoalVerb = "Deployment"
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new DEPLOYTASK object, which models the sequence of STAGEs to unload a cargo.
|
||||
-- @function [parent=#DEPLOYTASK] New
|
||||
-- @param #string CargoType Type of the Cargo.
|
||||
-- @return #DEPLOYTASK The created DeployTask
|
||||
function DEPLOYTASK:New( CargoType )
|
||||
local self = BASE:Inherit( self, TASK:New() )
|
||||
self:F()
|
||||
|
||||
local Valid = true
|
||||
|
||||
if Valid then
|
||||
self.Name = 'Deploy Cargo'
|
||||
self.TaskBriefing = "Fly to one of the indicated landing zones and deploy " .. CargoType .. ". Your co-pilot will provide you with the directions (required flight angle in degrees) and the distance (in km) to the deployment zone."
|
||||
self.CargoType = CargoType
|
||||
self.GoalVerb = CargoType .. " " .. self.GoalVerb
|
||||
self.Stages = { STAGE_CARGO_INIT:New(), STAGE_CARGO_LOAD:New(), STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGELANDING:New(), STAGELANDED:New(), STAGEUNLOAD:New(), STAGEDONE:New() }
|
||||
self.SetStage( self, 1 )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function DEPLOYTASK:ToZone( LandingZone )
|
||||
self:F()
|
||||
|
||||
self.LandingZones.LandingZoneNames[LandingZone.CargoZoneName] = LandingZone.CargoZoneName
|
||||
self.LandingZones.LandingZones[LandingZone.CargoZoneName] = LandingZone
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function DEPLOYTASK:InitCargo( InitCargos )
|
||||
self:F( { InitCargos } )
|
||||
|
||||
if type( InitCargos ) == "table" then
|
||||
self.Cargos.InitCargos = InitCargos
|
||||
else
|
||||
self.Cargos.InitCargos = { InitCargos }
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function DEPLOYTASK:LoadCargo( LoadCargos )
|
||||
self:F( { LoadCargos } )
|
||||
|
||||
if type( LoadCargos ) == "table" then
|
||||
self.Cargos.LoadCargos = LoadCargos
|
||||
else
|
||||
self.Cargos.LoadCargos = { LoadCargos }
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- When the cargo is unloaded, it will move to the target zone name.
|
||||
-- @param string TargetZoneName Name of the Zone to where the Cargo should move after unloading.
|
||||
function DEPLOYTASK:SetCargoTargetZoneName( TargetZoneName )
|
||||
self:F()
|
||||
|
||||
local Valid = true
|
||||
|
||||
Valid = routines.ValidateString( TargetZoneName, "TargetZoneName", Valid )
|
||||
|
||||
if Valid then
|
||||
self.TargetZoneName = TargetZoneName
|
||||
end
|
||||
|
||||
return Valid
|
||||
|
||||
end
|
||||
|
||||
function DEPLOYTASK:AddCargoMenus( Client, Cargos, TransportRadius )
|
||||
self:F()
|
||||
|
||||
local ClientGroupID = Client:GetClientGroupID()
|
||||
|
||||
self:T( ClientGroupID )
|
||||
|
||||
for CargoID, Cargo in pairs( Cargos ) do
|
||||
|
||||
self:T( { Cargo.ClassName, Cargo.CargoName, Cargo.CargoType, Cargo.CargoWeight } )
|
||||
|
||||
if Cargo:IsStatusLoaded() and Client == Cargo:IsLoadedInClient() then
|
||||
|
||||
if Client._Menus[Cargo.CargoType] == nil then
|
||||
Client._Menus[Cargo.CargoType] = {}
|
||||
end
|
||||
|
||||
if not Client._Menus[Cargo.CargoType].DeployMenu then
|
||||
Client._Menus[Cargo.CargoType].DeployMenu = missionCommands.addSubMenuForGroup(
|
||||
ClientGroupID,
|
||||
self.TEXT[1] .. " " .. Cargo.CargoType,
|
||||
nil
|
||||
)
|
||||
self:T( 'Added DeployMenu ' .. self.TEXT[1] )
|
||||
end
|
||||
|
||||
if Client._Menus[Cargo.CargoType].DeploySubMenus == nil then
|
||||
Client._Menus[Cargo.CargoType].DeploySubMenus = {}
|
||||
end
|
||||
|
||||
if Client._Menus[Cargo.CargoType].DeployMenu == nil then
|
||||
self:T( 'deploymenu is nil' )
|
||||
end
|
||||
|
||||
Client._Menus[Cargo.CargoType].DeploySubMenus[ #Client._Menus[Cargo.CargoType].DeploySubMenus + 1 ] = missionCommands.addCommandForGroup(
|
||||
ClientGroupID,
|
||||
Cargo.CargoName .. " ( " .. Cargo.CargoWeight .. "kg )",
|
||||
Client._Menus[Cargo.CargoType].DeployMenu,
|
||||
self.MenuAction,
|
||||
{ ReferenceTask = self, CargoTask = Cargo }
|
||||
)
|
||||
self:T( 'Added DeploySubMenu ' .. Cargo.CargoType .. ":" .. Cargo.CargoName .. " ( " .. Cargo.CargoWeight .. "kg )" )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function DEPLOYTASK:RemoveCargoMenus( Client )
|
||||
self:F()
|
||||
|
||||
local ClientGroupID = Client:GetClientGroupID()
|
||||
self:T( ClientGroupID )
|
||||
|
||||
for MenuID, MenuData in pairs( Client._Menus ) do
|
||||
if MenuData.DeploySubMenus ~= nil then
|
||||
for SubMenuID, SubMenuData in pairs( MenuData.DeploySubMenus ) do
|
||||
missionCommands.removeItemForGroup( ClientGroupID, SubMenuData )
|
||||
self:T( "Removed DeploySubMenu " )
|
||||
SubMenuData = nil
|
||||
end
|
||||
end
|
||||
if MenuData.DeployMenu then
|
||||
missionCommands.removeItemForGroup( ClientGroupID, MenuData.DeployMenu )
|
||||
self:T( "Removed DeployMenu " )
|
||||
MenuData.DeployMenu = nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
81
Moose Development/Moose/DestroyBaseTask.lua
Normal file
81
Moose Development/Moose/DestroyBaseTask.lua
Normal file
@@ -0,0 +1,81 @@
|
||||
--- A DESTROYBASETASK will monitor the destruction of Groups and Units. This is a BASE class, other classes are derived from this class.
|
||||
-- @module DESTROYBASETASK
|
||||
-- @see DESTROYGROUPSTASK
|
||||
-- @see DESTROYUNITTYPESTASK
|
||||
-- @see DESTROY_RADARS_TASK
|
||||
|
||||
Include.File("Task")
|
||||
|
||||
--- The DESTROYBASETASK class
|
||||
-- @type DESTROYBASETASK
|
||||
DESTROYBASETASK = {
|
||||
ClassName = "DESTROYBASETASK",
|
||||
Destroyed = 0,
|
||||
GoalVerb = "Destroy",
|
||||
DestroyPercentage = 100,
|
||||
}
|
||||
|
||||
--- Creates a new DESTROYBASETASK.
|
||||
-- @param #DESTROYBASETASK self
|
||||
-- @param #string DestroyGroupType Text describing the group to be destroyed. f.e. "Radar Installations", "Ships", "Vehicles", "Command Centers".
|
||||
-- @param #string DestroyUnitType Text describing the unit types to be destroyed. f.e. "SA-6", "Row Boats", "Tanks", "Tents".
|
||||
-- @param #list<#string> DestroyGroupPrefixes Table of Prefixes of the Groups to be destroyed before task is completed.
|
||||
-- @param #number DestroyPercentage defines the %-tage that needs to be destroyed to achieve mission success. eg. If in the Group there are 10 units, then a value of 75 would require 8 units to be destroyed from the Group to complete the @{TASK}.
|
||||
-- @return DESTROYBASETASK
|
||||
function DESTROYBASETASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupPrefixes, DestroyPercentage )
|
||||
local self = BASE:Inherit( self, TASK:New() )
|
||||
self:F()
|
||||
|
||||
self.Name = 'Destroy'
|
||||
self.Destroyed = 0
|
||||
self.DestroyGroupPrefixes = DestroyGroupPrefixes
|
||||
self.DestroyGroupType = DestroyGroupType
|
||||
self.DestroyUnitType = DestroyUnitType
|
||||
if DestroyPercentage then
|
||||
self.DestroyPercentage = DestroyPercentage
|
||||
end
|
||||
self.TaskBriefing = "Task: Destroy " .. DestroyGroupType .. "."
|
||||
self.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEGROUPSDESTROYED:New(), STAGEDONE:New() }
|
||||
self.SetStage( self, 1 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Handle the S_EVENT_DEAD events to validate the destruction of units for the task monitoring.
|
||||
-- @param #DESTROYBASETASK self
|
||||
-- @param Event#EVENTDATA Event structure of MOOSE.
|
||||
function DESTROYBASETASK:EventDead( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
local DestroyUnit = Event.IniDCSUnit
|
||||
local DestroyUnitName = Event.IniDCSUnitName
|
||||
local DestroyGroup = Event.IniDCSGroup
|
||||
local DestroyGroupName = Event.IniDCSGroupName
|
||||
|
||||
--TODO: I need to fix here if 2 groups in the mission have a similar name with GroupPrefix equal, then i should differentiate for which group the goal was reached!
|
||||
--I may need to test if for the goalverb that group goal was reached or something. Need to think about it a bit more ...
|
||||
local UnitsDestroyed = 0
|
||||
for DestroyGroupPrefixID, DestroyGroupPrefix in pairs( self.DestroyGroupPrefixes ) do
|
||||
self:T( DestroyGroupPrefix )
|
||||
if string.find( DestroyGroupName, DestroyGroupPrefix, 1, true ) then
|
||||
self:T( BASE:Inherited(self).ClassName )
|
||||
UnitsDestroyed = self:ReportGoalProgress( DestroyGroup, DestroyUnit )
|
||||
self:T( UnitsDestroyed )
|
||||
end
|
||||
end
|
||||
|
||||
self:T( { UnitsDestroyed } )
|
||||
self:IncreaseGoalCount( UnitsDestroyed, self.GoalVerb )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Validate task completeness of DESTROYBASETASK.
|
||||
-- @param DestroyGroup Group structure describing the group to be evaluated.
|
||||
-- @param DestroyUnit Unit structure describing the Unit to be evaluated.
|
||||
function DESTROYBASETASK:ReportGoalProgress( DestroyGroup, DestroyUnit )
|
||||
self:F()
|
||||
|
||||
return 0
|
||||
end
|
||||
57
Moose Development/Moose/DestroyGroupsTask.lua
Normal file
57
Moose Development/Moose/DestroyGroupsTask.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
--- DESTROYGROUPSTASK
|
||||
-- @module DESTROYGROUPSTASK
|
||||
|
||||
Include.File("DestroyBaseTask")
|
||||
|
||||
--- The DESTROYGROUPSTASK class
|
||||
-- @type
|
||||
DESTROYGROUPSTASK = {
|
||||
ClassName = "DESTROYGROUPSTASK",
|
||||
GoalVerb = "Destroy Groups",
|
||||
}
|
||||
|
||||
--- Creates a new DESTROYGROUPSTASK.
|
||||
-- @param #DESTROYGROUPSTASK self
|
||||
-- @param #string DestroyGroupType String describing the group to be destroyed.
|
||||
-- @param #string DestroyUnitType String describing the unit to be destroyed.
|
||||
-- @param #list<#string> DestroyGroupNames Table of string containing the name of the groups to be destroyed before task is completed.
|
||||
-- @param #number DestroyPercentage defines the %-tage that needs to be destroyed to achieve mission success. eg. If in the Group there are 10 units, then a value of 75 would require 8 units to be destroyed from the Group to complete the @{TASK}.
|
||||
---@return DESTROYGROUPSTASK
|
||||
function DESTROYGROUPSTASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupNames, DestroyPercentage )
|
||||
local self = BASE:Inherit( self, DESTROYBASETASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupNames, DestroyPercentage ) )
|
||||
self:F()
|
||||
|
||||
self.Name = 'Destroy Groups'
|
||||
self.GoalVerb = "Destroy " .. DestroyGroupType
|
||||
|
||||
_EVENTDISPATCHER:OnDead( self.EventDead , self )
|
||||
_EVENTDISPATCHER:OnCrash( self.EventDead , self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Report Goal Progress.
|
||||
-- @param #DESTROYGROUPSTASK self
|
||||
-- @param DCSGroup#Group DestroyGroup Group structure describing the group to be evaluated.
|
||||
-- @param DCSUnit#Unit DestroyUnit Unit structure describing the Unit to be evaluated.
|
||||
-- @return #number The DestroyCount reflecting the amount of units destroyed within the group.
|
||||
function DESTROYGROUPSTASK:ReportGoalProgress( DestroyGroup, DestroyUnit )
|
||||
self:F( { DestroyGroup, DestroyUnit, self.DestroyPercentage } )
|
||||
|
||||
local DestroyGroupSize = DestroyGroup:getSize() - 1 -- When a DEAD event occurs, the getSize is still one larger than the destroyed unit.
|
||||
local DestroyGroupInitialSize = DestroyGroup:getInitialSize()
|
||||
self:T( { DestroyGroupSize, DestroyGroupInitialSize - ( DestroyGroupInitialSize * self.DestroyPercentage / 100 ) } )
|
||||
|
||||
local DestroyCount = 0
|
||||
if DestroyGroup then
|
||||
if DestroyGroupSize <= DestroyGroupInitialSize - ( DestroyGroupInitialSize * self.DestroyPercentage / 100 ) then
|
||||
DestroyCount = 1
|
||||
end
|
||||
else
|
||||
DestroyCount = 1
|
||||
end
|
||||
|
||||
self:T( DestroyCount )
|
||||
|
||||
return DestroyCount
|
||||
end
|
||||
41
Moose Development/Moose/DestroyRadarsTask.lua
Normal file
41
Moose Development/Moose/DestroyRadarsTask.lua
Normal file
@@ -0,0 +1,41 @@
|
||||
--- Task class to destroy radar installations.
|
||||
-- @module DESTROYRADARSTASK
|
||||
|
||||
Include.File("DestroyBaseTask")
|
||||
|
||||
--- The DESTROYRADARS class
|
||||
-- @type
|
||||
DESTROYRADARSTASK = {
|
||||
ClassName = "DESTROYRADARSTASK",
|
||||
GoalVerb = "Destroy Radars"
|
||||
}
|
||||
|
||||
--- Creates a new DESTROYRADARSTASK.
|
||||
-- @param table{string,...} DestroyGroupNames Table of string containing the group names of which the radars are be destroyed.
|
||||
-- @return DESTROYRADARSTASK
|
||||
function DESTROYRADARSTASK:New( DestroyGroupNames )
|
||||
local self = BASE:Inherit( self, DESTROYGROUPSTASK:New( 'radar installations', 'radars', DestroyGroupNames ) )
|
||||
self:F()
|
||||
|
||||
self.Name = 'Destroy Radars'
|
||||
|
||||
_EVENTDISPATCHER:OnDead( self.EventDead , self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Report Goal Progress.
|
||||
-- @param Group DestroyGroup Group structure describing the group to be evaluated.
|
||||
-- @param Unit DestroyUnit Unit structure describing the Unit to be evaluated.
|
||||
function DESTROYRADARSTASK:ReportGoalProgress( DestroyGroup, DestroyUnit )
|
||||
self:F( { DestroyGroup, DestroyUnit } )
|
||||
|
||||
local DestroyCount = 0
|
||||
if DestroyUnit and DestroyUnit:hasSensors( Unit.SensorType.RADAR, Unit.RadarType.AS ) then
|
||||
if DestroyUnit and DestroyUnit:getLife() <= 1.0 then
|
||||
self:T( 'Destroyed a radar' )
|
||||
DestroyCount = 1
|
||||
end
|
||||
end
|
||||
return DestroyCount
|
||||
end
|
||||
52
Moose Development/Moose/DestroyUnitTypesTask.lua
Normal file
52
Moose Development/Moose/DestroyUnitTypesTask.lua
Normal file
@@ -0,0 +1,52 @@
|
||||
--- Set TASK to destroy certain unit types.
|
||||
-- @module DESTROYUNITTYPESTASK
|
||||
|
||||
Include.File("DestroyBaseTask")
|
||||
|
||||
--- The DESTROYUNITTYPESTASK class
|
||||
-- @type
|
||||
DESTROYUNITTYPESTASK = {
|
||||
ClassName = "DESTROYUNITTYPESTASK",
|
||||
GoalVerb = "Destroy",
|
||||
}
|
||||
|
||||
--- Creates a new DESTROYUNITTYPESTASK.
|
||||
-- @param string DestroyGroupType String describing the group to be destroyed. f.e. "Radar Installations", "Fleet", "Batallion", "Command Centers".
|
||||
-- @param string DestroyUnitType String describing the unit to be destroyed. f.e. "radars", "ships", "tanks", "centers".
|
||||
-- @param table{string,...} DestroyGroupNames Table of string containing the group names of which the radars are be destroyed.
|
||||
-- @param string DestroyUnitTypes Table of string containing the type names of the units to achieve mission success.
|
||||
-- @return DESTROYUNITTYPESTASK
|
||||
function DESTROYUNITTYPESTASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupNames, DestroyUnitTypes )
|
||||
local self = BASE:Inherit( self, DESTROYBASETASK:New( DestroyGroupType, DestroyUnitType, DestroyGroupNames ) )
|
||||
self:F( { DestroyGroupType, DestroyUnitType, DestroyGroupNames, DestroyUnitTypes } )
|
||||
|
||||
if type(DestroyUnitTypes) == 'table' then
|
||||
self.DestroyUnitTypes = DestroyUnitTypes
|
||||
else
|
||||
self.DestroyUnitTypes = { DestroyUnitTypes }
|
||||
end
|
||||
|
||||
self.Name = 'Destroy Unit Types'
|
||||
self.GoalVerb = "Destroy " .. DestroyGroupType
|
||||
|
||||
_EVENTDISPATCHER:OnDead( self.EventDead , self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Report Goal Progress.
|
||||
-- @param Group DestroyGroup Group structure describing the group to be evaluated.
|
||||
-- @param Unit DestroyUnit Unit structure describing the Unit to be evaluated.
|
||||
function DESTROYUNITTYPESTASK:ReportGoalProgress( DestroyGroup, DestroyUnit )
|
||||
self:F( { DestroyGroup, DestroyUnit } )
|
||||
|
||||
local DestroyCount = 0
|
||||
for UnitTypeID, UnitType in pairs( self.DestroyUnitTypes ) do
|
||||
if DestroyUnit and DestroyUnit:getTypeName() == UnitType then
|
||||
if DestroyUnit and DestroyUnit:getLife() <= 1.0 then
|
||||
DestroyCount = DestroyCount + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return DestroyCount
|
||||
end
|
||||
1247
Moose Development/Moose/Escort.lua
Normal file
1247
Moose Development/Moose/Escort.lua
Normal file
File diff suppressed because it is too large
Load Diff
511
Moose Development/Moose/Event.lua
Normal file
511
Moose Development/Moose/Event.lua
Normal file
@@ -0,0 +1,511 @@
|
||||
--- The EVENT class models an efficient event handling process between other classes and its units, weapons.
|
||||
-- @module Event
|
||||
-- @author FlightControl
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
|
||||
--- The EVENT structure
|
||||
-- @type EVENT
|
||||
-- @field #EVENT.Events Events
|
||||
EVENT = {
|
||||
ClassName = "EVENT",
|
||||
ClassID = 0,
|
||||
}
|
||||
|
||||
local _EVENTCODES = {
|
||||
"S_EVENT_SHOT",
|
||||
"S_EVENT_HIT",
|
||||
"S_EVENT_TAKEOFF",
|
||||
"S_EVENT_LAND",
|
||||
"S_EVENT_CRASH",
|
||||
"S_EVENT_EJECTION",
|
||||
"S_EVENT_REFUELING",
|
||||
"S_EVENT_DEAD",
|
||||
"S_EVENT_PILOT_DEAD",
|
||||
"S_EVENT_BASE_CAPTURED",
|
||||
"S_EVENT_MISSION_START",
|
||||
"S_EVENT_MISSION_END",
|
||||
"S_EVENT_TOOK_CONTROL",
|
||||
"S_EVENT_REFUELING_STOP",
|
||||
"S_EVENT_BIRTH",
|
||||
"S_EVENT_HUMAN_FAILURE",
|
||||
"S_EVENT_ENGINE_STARTUP",
|
||||
"S_EVENT_ENGINE_SHUTDOWN",
|
||||
"S_EVENT_PLAYER_ENTER_UNIT",
|
||||
"S_EVENT_PLAYER_LEAVE_UNIT",
|
||||
"S_EVENT_PLAYER_COMMENT",
|
||||
"S_EVENT_SHOOTING_START",
|
||||
"S_EVENT_SHOOTING_END",
|
||||
"S_EVENT_MAX",
|
||||
}
|
||||
|
||||
--- The Event structure
|
||||
-- @type EVENTDATA
|
||||
-- @field id
|
||||
-- @field initiator
|
||||
-- @field target
|
||||
-- @field weapon
|
||||
-- @field IniDCSUnit
|
||||
-- @field IniDCSUnitName
|
||||
-- @field IniDCSGroup
|
||||
-- @field IniDCSGroupName
|
||||
-- @field TgtDCSUnit
|
||||
-- @field TgtDCSUnitName
|
||||
-- @field TgtDCSGroup
|
||||
-- @field TgtDCSGroupName
|
||||
-- @field Weapon
|
||||
-- @field WeaponName
|
||||
-- @field WeaponTgtDCSUnit
|
||||
|
||||
--- The Events structure
|
||||
-- @type EVENT.Events
|
||||
-- @field #number IniUnit
|
||||
|
||||
function EVENT:New()
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F()
|
||||
self.EventHandler = world.addEventHandler( self )
|
||||
return self
|
||||
end
|
||||
|
||||
function EVENT:EventText( EventID )
|
||||
|
||||
local EventText = _EVENTCODES[EventID]
|
||||
|
||||
return EventText
|
||||
end
|
||||
|
||||
|
||||
--- Initializes the Events structure for the event
|
||||
-- @param #EVENT self
|
||||
-- @param DCSWorld#world.event EventID
|
||||
-- @param #string EventClass
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:Init( EventID, EventClass )
|
||||
self:F3( { _EVENTCODES[EventID], EventClass } )
|
||||
if not self.Events[EventID] then
|
||||
self.Events[EventID] = {}
|
||||
end
|
||||
if not self.Events[EventID][EventClass] then
|
||||
self.Events[EventID][EventClass] = {}
|
||||
end
|
||||
return self.Events[EventID][EventClass]
|
||||
end
|
||||
|
||||
|
||||
--- Create an OnDead event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param #table EventTemplate
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventSelf The self instance of the class for which the event is.
|
||||
-- @param #function OnEventFunction
|
||||
-- @return #EVENT
|
||||
function EVENT:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, OnEventFunction )
|
||||
self:F2( EventTemplate.name )
|
||||
|
||||
for EventUnitID, EventUnit in pairs( EventTemplate.units ) do
|
||||
OnEventFunction( self, EventUnit.name, EventFunction, EventSelf )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_X event independent from a unit or a weapon.
|
||||
-- @param #EVENT self
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @param EventID
|
||||
-- @return #EVENT
|
||||
function EVENT:OnEventGeneric( EventFunction, EventSelf, EventID )
|
||||
self:F2( { EventID } )
|
||||
|
||||
local Event = self:Init( EventID, EventSelf:GetClassNameAndID() )
|
||||
Event.EventFunction = EventFunction
|
||||
Event.EventSelf = EventSelf
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set a new listener for an S_EVENT_X event
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @param EventID
|
||||
-- @return #EVENT
|
||||
function EVENT:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, EventID )
|
||||
self:F2( EventDCSUnitName )
|
||||
|
||||
local Event = self:Init( EventID, EventSelf:GetClassNameAndID() )
|
||||
if not Event.IniUnit then
|
||||
Event.IniUnit = {}
|
||||
end
|
||||
Event.IniUnit[EventDCSUnitName] = {}
|
||||
Event.IniUnit[EventDCSUnitName].EventFunction = EventFunction
|
||||
Event.IniUnit[EventDCSUnitName].EventSelf = EventSelf
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Create an OnBirth event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param Group#GROUP EventGroup
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventSelf )
|
||||
self:F( EventTemplate.name )
|
||||
|
||||
self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnBirthForUnit )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_BIRTH event, and registers the unit born.
|
||||
-- @param #EVENT self
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf
|
||||
-- @return #EVENT
|
||||
function EVENT:OnBirth( EventFunction, EventSelf )
|
||||
self:F()
|
||||
|
||||
self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_BIRTH )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_BIRTH event.
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName The id of the unit for the event to be handled.
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf
|
||||
-- @return #EVENT
|
||||
function EVENT:OnBirthForUnit( EventDCSUnitName, EventFunction, EventSelf )
|
||||
self:F( EventDCSUnitName )
|
||||
|
||||
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_BIRTH )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create an OnCrash event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param Group#GROUP EventGroup
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventSelf )
|
||||
self:F( EventTemplate.name )
|
||||
|
||||
self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnCrashForUnit )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_CRASH event.
|
||||
-- @param #EVENT self
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf
|
||||
-- @return #EVENT
|
||||
function EVENT:OnCrash( EventFunction, EventSelf )
|
||||
self:F()
|
||||
|
||||
self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_CRASH )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_CRASH event.
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnCrashForUnit( EventDCSUnitName, EventFunction, EventSelf )
|
||||
self:F( EventDCSUnitName )
|
||||
|
||||
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_CRASH )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create an OnDead event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param Group#GROUP EventGroup
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventSelf )
|
||||
self:F( EventTemplate.name )
|
||||
|
||||
self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnDeadForUnit )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_DEAD event.
|
||||
-- @param #EVENT self
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf
|
||||
-- @return #EVENT
|
||||
function EVENT:OnDead( EventFunction, EventSelf )
|
||||
self:F()
|
||||
|
||||
self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_DEAD )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set a new listener for an S_EVENT_DEAD event.
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnDeadForUnit( EventDCSUnitName, EventFunction, EventSelf )
|
||||
self:F( EventDCSUnitName )
|
||||
|
||||
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_DEAD )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_PILOT_DEAD event.
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnPilotDeadForUnit( EventDCSUnitName, EventFunction, EventSelf )
|
||||
self:F( EventDCSUnitName )
|
||||
|
||||
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_PILOT_DEAD )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create an OnDead event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param #table EventTemplate
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventSelf )
|
||||
self:F( EventTemplate.name )
|
||||
|
||||
self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnLandForUnit )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_LAND event.
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnLandForUnit( EventDCSUnitName, EventFunction, EventSelf )
|
||||
self:F( EventDCSUnitName )
|
||||
|
||||
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_LAND )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create an OnDead event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param #table EventTemplate
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventSelf )
|
||||
self:F( EventTemplate.name )
|
||||
|
||||
self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnTakeOffForUnit )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_TAKEOFF event.
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnTakeOffForUnit( EventDCSUnitName, EventFunction, EventSelf )
|
||||
self:F( EventDCSUnitName )
|
||||
|
||||
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_TAKEOFF )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create an OnDead event handler for a group
|
||||
-- @param #EVENT self
|
||||
-- @param #table EventTemplate
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventSelf )
|
||||
self:F( EventTemplate.name )
|
||||
|
||||
self:OnEventForTemplate( EventTemplate, EventFunction, EventSelf, self.OnEngineShutDownForUnit )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_ENGINE_SHUTDOWN event.
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnEngineShutDownForUnit( EventDCSUnitName, EventFunction, EventSelf )
|
||||
self:F( EventDCSUnitName )
|
||||
|
||||
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_ENGINE_SHUTDOWN )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_ENGINE_STARTUP event.
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnEngineStartUpForUnit( EventDCSUnitName, EventFunction, EventSelf )
|
||||
self:F( EventDCSUnitName )
|
||||
|
||||
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_ENGINE_STARTUP )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_SHOT event.
|
||||
-- @param #EVENT self
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnShot( EventFunction, EventSelf )
|
||||
self:F()
|
||||
|
||||
self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_SHOT )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_SHOT event for a unit.
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnShotForUnit( EventDCSUnitName, EventFunction, EventSelf )
|
||||
self:F( EventDCSUnitName )
|
||||
|
||||
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_SHOT )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_HIT event.
|
||||
-- @param #EVENT self
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnHit( EventFunction, EventSelf )
|
||||
self:F()
|
||||
|
||||
self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_HIT )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_HIT event.
|
||||
-- @param #EVENT self
|
||||
-- @param #string EventDCSUnitName
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnHitForUnit( EventDCSUnitName, EventFunction, EventSelf )
|
||||
self:F( EventDCSUnitName )
|
||||
|
||||
self:OnEventForUnit( EventDCSUnitName, EventFunction, EventSelf, world.event.S_EVENT_HIT )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_PLAYER_ENTER_UNIT event.
|
||||
-- @param #EVENT self
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnPlayerEnterUnit( EventFunction, EventSelf )
|
||||
self:F()
|
||||
|
||||
self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_PLAYER_ENTER_UNIT )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a new listener for an S_EVENT_PLAYER_LEAVE_UNIT event.
|
||||
-- @param #EVENT self
|
||||
-- @param #function EventFunction The function to be called when the event occurs for the unit.
|
||||
-- @param Base#BASE EventSelf The self instance of the class for which the event is.
|
||||
-- @return #EVENT
|
||||
function EVENT:OnPlayerLeaveUnit( EventFunction, EventSelf )
|
||||
self:F()
|
||||
|
||||
self:OnEventGeneric( EventFunction, EventSelf, world.event.S_EVENT_PLAYER_LEAVE_UNIT )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
function EVENT:onEvent( Event )
|
||||
self:F( { _EVENTCODES[Event.id], Event } )
|
||||
|
||||
if self and self.Events and self.Events[Event.id] then
|
||||
if Event.initiator and Event.initiator:getCategory() == Object.Category.UNIT then
|
||||
Event.IniDCSUnit = Event.initiator
|
||||
Event.IniDCSGroup = Event.IniDCSUnit:getGroup()
|
||||
Event.IniDCSUnitName = Event.IniDCSUnit:getName()
|
||||
Event.IniDCSGroupName = ""
|
||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||
Event.IniDCSGroupName = Event.IniDCSGroup:getName()
|
||||
end
|
||||
end
|
||||
if Event.target then
|
||||
if Event.target and Event.target:getCategory() == Object.Category.UNIT then
|
||||
Event.TgtDCSUnit = Event.target
|
||||
Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup()
|
||||
Event.TgtDCSUnitName = Event.TgtDCSUnit:getName()
|
||||
Event.TgtDCSGroupName = ""
|
||||
if Event.TgtDCSGroup and Event.TgtDCSGroup:isExist() then
|
||||
Event.TgtDCSGroupName = Event.TgtDCSGroup:getName()
|
||||
end
|
||||
end
|
||||
end
|
||||
if Event.weapon then
|
||||
Event.Weapon = Event.weapon
|
||||
Event.WeaponName = Event.Weapon:getTypeName()
|
||||
--Event.WeaponTgtDCSUnit = Event.Weapon:getTarget()
|
||||
end
|
||||
self:E( { _EVENTCODES[Event.id], Event } )
|
||||
for ClassName, EventData in pairs( self.Events[Event.id] ) do
|
||||
if Event.IniDCSUnitName and EventData.IniUnit and EventData.IniUnit[Event.IniDCSUnitName] then
|
||||
self:T2( { "Calling event function for class ", ClassName, " unit ", Event.IniDCSUnitName } )
|
||||
EventData.IniUnit[Event.IniDCSUnitName].EventFunction( EventData.IniUnit[Event.IniDCSUnitName].EventSelf, Event )
|
||||
else
|
||||
if Event.IniDCSUnit and not EventData.IniUnit then
|
||||
self:T2( { "Calling event function for class ", ClassName } )
|
||||
EventData.EventFunction( EventData.EventSelf, Event )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
35
Moose Development/Moose/GoHomeTask.lua
Normal file
35
Moose Development/Moose/GoHomeTask.lua
Normal file
@@ -0,0 +1,35 @@
|
||||
--- A GOHOMETASK orchestrates the travel back to the home base, which is a specific zone defined within the ME.
|
||||
-- @module GOHOMETASK
|
||||
|
||||
Include.File("Task")
|
||||
|
||||
--- The GOHOMETASK class
|
||||
-- @type
|
||||
GOHOMETASK = {
|
||||
ClassName = "GOHOMETASK",
|
||||
}
|
||||
|
||||
--- Creates a new GOHOMETASK.
|
||||
-- @param table{string,...}|string LandingZones Table of Landing Zone names where Home(s) are located.
|
||||
-- @return GOHOMETASK
|
||||
function GOHOMETASK:New( LandingZones )
|
||||
local self = BASE:Inherit( self, TASK:New() )
|
||||
self:F( { LandingZones } )
|
||||
local Valid = true
|
||||
|
||||
Valid = routines.ValidateZone( LandingZones, "LandingZones", Valid )
|
||||
|
||||
if Valid then
|
||||
self.Name = 'Fly Home'
|
||||
self.TaskBriefing = "Task: Fly back to your home base. Your co-pilot will provide you with the directions (required flight angle in degrees) and the distance (in km) to your home base."
|
||||
if type( LandingZones ) == "table" then
|
||||
self.LandingZones = LandingZones
|
||||
else
|
||||
self.LandingZones = { LandingZones }
|
||||
end
|
||||
self.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGEARRIVE:New(), STAGEDONE:New() }
|
||||
self.SetStage( self, 1 )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
1872
Moose Development/Moose/Group.lua
Normal file
1872
Moose Development/Moose/Group.lua
Normal file
File diff suppressed because it is too large
Load Diff
371
Moose Development/Moose/Menu.lua
Normal file
371
Moose Development/Moose/Menu.lua
Normal file
@@ -0,0 +1,371 @@
|
||||
--- Encapsulation of DCS World Menu system in a set of MENU classes.
|
||||
-- @module Menu
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
|
||||
--- The MENU class
|
||||
-- @type MENU
|
||||
-- @extends Base#BASE
|
||||
MENU = {
|
||||
ClassName = "MENU",
|
||||
MenuPath = nil,
|
||||
MenuText = "",
|
||||
MenuParentPath = nil
|
||||
}
|
||||
|
||||
---
|
||||
function MENU:New( MenuText, MenuParentPath )
|
||||
|
||||
-- Arrange meta tables
|
||||
local Child = BASE:Inherit( self, BASE:New() )
|
||||
|
||||
Child.MenuPath = nil
|
||||
Child.MenuText = MenuText
|
||||
Child.MenuParentPath = MenuParentPath
|
||||
return Child
|
||||
end
|
||||
|
||||
--- The COMMANDMENU class
|
||||
-- @type COMMANDMENU
|
||||
-- @extends Menu#MENU
|
||||
COMMANDMENU = {
|
||||
ClassName = "COMMANDMENU",
|
||||
CommandMenuFunction = nil,
|
||||
CommandMenuArgument = nil
|
||||
}
|
||||
|
||||
function COMMANDMENU:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArgument )
|
||||
|
||||
-- Arrange meta tables
|
||||
|
||||
local MenuParentPath = nil
|
||||
if ParentMenu ~= nil then
|
||||
MenuParentPath = ParentMenu.MenuPath
|
||||
end
|
||||
|
||||
local Child = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) )
|
||||
|
||||
Child.MenuPath = missionCommands.addCommand( MenuText, MenuParentPath, CommandMenuFunction, CommandMenuArgument )
|
||||
Child.CommandMenuFunction = CommandMenuFunction
|
||||
Child.CommandMenuArgument = CommandMenuArgument
|
||||
return Child
|
||||
end
|
||||
|
||||
--- The SUBMENU class
|
||||
-- @type SUBMENU
|
||||
-- @extends Menu#MENU
|
||||
SUBMENU = {
|
||||
ClassName = "SUBMENU"
|
||||
}
|
||||
|
||||
function SUBMENU:New( MenuText, ParentMenu )
|
||||
|
||||
-- Arrange meta tables
|
||||
local MenuParentPath = nil
|
||||
if ParentMenu ~= nil then
|
||||
MenuParentPath = ParentMenu.MenuPath
|
||||
end
|
||||
|
||||
local Child = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) )
|
||||
|
||||
Child.MenuPath = missionCommands.addSubMenu( MenuText, MenuParentPath )
|
||||
return Child
|
||||
end
|
||||
|
||||
-- This local variable is used to cache the menus registered under clients.
|
||||
-- Menus don't dissapear when clients are destroyed and restarted.
|
||||
-- So every menu for a client created must be tracked so that program logic accidentally does not create
|
||||
-- the same menus twice during initialization logic.
|
||||
-- These menu classes are handling this logic with this variable.
|
||||
local _MENUCLIENTS = {}
|
||||
|
||||
--- The MENU_CLIENT class
|
||||
-- @type MENU_CLIENT
|
||||
-- @extends Menu#MENU
|
||||
MENU_CLIENT = {
|
||||
ClassName = "MENU_CLIENT"
|
||||
}
|
||||
|
||||
--- Creates a new menu item for a group
|
||||
-- @param self
|
||||
-- @param Client#CLIENT MenuClient The Client owning the menu.
|
||||
-- @param #string MenuText The text for the menu.
|
||||
-- @param #table ParentMenu The parent menu.
|
||||
-- @return #MENU_CLIENT self
|
||||
function MENU_CLIENT:New( MenuClient, MenuText, ParentMenu )
|
||||
|
||||
-- Arrange meta tables
|
||||
local MenuParentPath = {}
|
||||
if ParentMenu ~= nil then
|
||||
MenuParentPath = ParentMenu.MenuPath
|
||||
end
|
||||
|
||||
local self = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) )
|
||||
self:F( { MenuClient, MenuText, ParentMenu } )
|
||||
|
||||
self.MenuClient = MenuClient
|
||||
self.MenuClientGroupID = MenuClient:GetClientGroupID()
|
||||
self.MenuParentPath = MenuParentPath
|
||||
self.MenuText = MenuText
|
||||
self.ParentMenu = ParentMenu
|
||||
|
||||
self.Menus = {}
|
||||
|
||||
if not _MENUCLIENTS[self.MenuClientGroupID] then
|
||||
_MENUCLIENTS[self.MenuClientGroupID] = {}
|
||||
end
|
||||
|
||||
local MenuPath = _MENUCLIENTS[self.MenuClientGroupID]
|
||||
|
||||
self:T( { MenuClient:GetClientGroupName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText } )
|
||||
|
||||
local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText
|
||||
if MenuPath[MenuPathID] then
|
||||
missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] )
|
||||
end
|
||||
|
||||
self.MenuPath = missionCommands.addSubMenuForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath )
|
||||
MenuPath[MenuPathID] = self.MenuPath
|
||||
|
||||
self:T( { MenuClient:GetClientGroupName(), self.MenuPath } )
|
||||
|
||||
if ParentMenu and ParentMenu.Menus then
|
||||
ParentMenu.Menus[self.MenuPath] = self
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Removes the sub menus recursively of this MENU_CLIENT.
|
||||
-- @param #MENU_CLIENT self
|
||||
-- @return #MENU_CLIENT self
|
||||
function MENU_CLIENT:RemoveSubMenus()
|
||||
self:F( self.MenuPath )
|
||||
|
||||
for MenuID, Menu in pairs( self.Menus ) do
|
||||
Menu:Remove()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Removes the sub menus recursively of this MENU_CLIENT.
|
||||
-- @param #MENU_CLIENT self
|
||||
-- @return #MENU_CLIENT self
|
||||
function MENU_CLIENT:Remove()
|
||||
self:F( self.MenuPath )
|
||||
|
||||
self:RemoveSubMenus()
|
||||
|
||||
if not _MENUCLIENTS[self.MenuClientGroupID] then
|
||||
_MENUCLIENTS[self.MenuClientGroupID] = {}
|
||||
end
|
||||
|
||||
local MenuPath = _MENUCLIENTS[self.MenuClientGroupID]
|
||||
|
||||
if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then
|
||||
MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil
|
||||
end
|
||||
|
||||
missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), self.MenuPath )
|
||||
self.ParentMenu.Menus[self.MenuPath] = nil
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- The MENU_CLIENT_COMMAND class
|
||||
-- @type MENU_CLIENT_COMMAND
|
||||
-- @extends Menu#MENU
|
||||
MENU_CLIENT_COMMAND = {
|
||||
ClassName = "MENU_CLIENT_COMMAND"
|
||||
}
|
||||
|
||||
--- Creates a new radio command item for a group
|
||||
-- @param self
|
||||
-- @param Client#CLIENT MenuClient The Client owning the menu.
|
||||
-- @param MenuText The text for the menu.
|
||||
-- @param ParentMenu The parent menu.
|
||||
-- @param CommandMenuFunction A function that is called when the menu key is pressed.
|
||||
-- @param CommandMenuArgument An argument for the function.
|
||||
-- @return Menu#MENU_CLIENT_COMMAND self
|
||||
function MENU_CLIENT_COMMAND:New( MenuClient, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArgument )
|
||||
|
||||
-- Arrange meta tables
|
||||
|
||||
local MenuParentPath = {}
|
||||
if ParentMenu ~= nil then
|
||||
MenuParentPath = ParentMenu.MenuPath
|
||||
end
|
||||
|
||||
local self = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) )
|
||||
|
||||
self.MenuClient = MenuClient
|
||||
self.MenuClientGroupID = MenuClient:GetClientGroupID()
|
||||
self.MenuParentPath = MenuParentPath
|
||||
self.MenuText = MenuText
|
||||
self.ParentMenu = ParentMenu
|
||||
|
||||
if not _MENUCLIENTS[self.MenuClientGroupID] then
|
||||
_MENUCLIENTS[self.MenuClientGroupID] = {}
|
||||
end
|
||||
|
||||
local MenuPath = _MENUCLIENTS[self.MenuClientGroupID]
|
||||
|
||||
self:T( { MenuClient:GetClientGroupName(), MenuPath[table.concat(MenuParentPath)], MenuParentPath, MenuText, CommandMenuFunction, CommandMenuArgument } )
|
||||
|
||||
local MenuPathID = table.concat(MenuParentPath) .. "/" .. MenuText
|
||||
if MenuPath[MenuPathID] then
|
||||
missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] )
|
||||
end
|
||||
|
||||
self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, CommandMenuFunction, CommandMenuArgument )
|
||||
MenuPath[MenuPathID] = self.MenuPath
|
||||
|
||||
self.CommandMenuFunction = CommandMenuFunction
|
||||
self.CommandMenuArgument = CommandMenuArgument
|
||||
|
||||
ParentMenu.Menus[self.MenuPath] = self
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function MENU_CLIENT_COMMAND:Remove()
|
||||
self:F( self.MenuPath )
|
||||
|
||||
if not _MENUCLIENTS[self.MenuClientGroupID] then
|
||||
_MENUCLIENTS[self.MenuClientGroupID] = {}
|
||||
end
|
||||
|
||||
local MenuPath = _MENUCLIENTS[self.MenuClientGroupID]
|
||||
|
||||
if MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] then
|
||||
MenuPath[table.concat(self.MenuParentPath) .. "/" .. self.MenuText] = nil
|
||||
end
|
||||
|
||||
missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), self.MenuPath )
|
||||
self.ParentMenu.Menus[self.MenuPath] = nil
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- The MENU_COALITION class
|
||||
-- @type MENU_COALITION
|
||||
-- @extends Menu#MENU
|
||||
MENU_COALITION = {
|
||||
ClassName = "MENU_COALITION"
|
||||
}
|
||||
|
||||
--- Creates a new coalition menu item
|
||||
-- @param #MENU_COALITION self
|
||||
-- @param DCSCoalition#coalition.side MenuCoalition The coalition owning the menu.
|
||||
-- @param #string MenuText The text for the menu.
|
||||
-- @param #table ParentMenu The parent menu.
|
||||
-- @return #MENU_COALITION self
|
||||
function MENU_COALITION:New( MenuCoalition, MenuText, ParentMenu )
|
||||
|
||||
-- Arrange meta tables
|
||||
local MenuParentPath = {}
|
||||
if ParentMenu ~= nil then
|
||||
MenuParentPath = ParentMenu.MenuPath
|
||||
end
|
||||
|
||||
local self = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) )
|
||||
self:F( { MenuCoalition, MenuText, ParentMenu } )
|
||||
|
||||
self.MenuCoalition = MenuCoalition
|
||||
self.MenuParentPath = MenuParentPath
|
||||
self.MenuText = MenuText
|
||||
self.ParentMenu = ParentMenu
|
||||
|
||||
self.Menus = {}
|
||||
|
||||
self:T( { MenuParentPath, MenuText } )
|
||||
|
||||
self.MenuPath = missionCommands.addSubMenuForCoalition( self.MenuCoalition, MenuText, MenuParentPath )
|
||||
|
||||
self:T( { self.MenuPath } )
|
||||
|
||||
if ParentMenu and ParentMenu.Menus then
|
||||
ParentMenu.Menus[self.MenuPath] = self
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Removes the sub menus recursively of this MENU_COALITION.
|
||||
-- @param #MENU_COALITION self
|
||||
-- @return #MENU_COALITION self
|
||||
function MENU_COALITION:RemoveSubMenus()
|
||||
self:F( self.MenuPath )
|
||||
|
||||
for MenuID, Menu in pairs( self.Menus ) do
|
||||
Menu:Remove()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Removes the sub menus recursively of this MENU_COALITION.
|
||||
-- @param #MENU_COALITION self
|
||||
-- @return #MENU_COALITION self
|
||||
function MENU_COALITION:Remove()
|
||||
self:F( self.MenuPath )
|
||||
|
||||
self:RemoveSubMenus()
|
||||
missionCommands.removeItemForCoalition( self.MenuCoalition, self.MenuPath )
|
||||
self.ParentMenu.Menus[self.MenuPath] = nil
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- The MENU_COALITION_COMMAND class
|
||||
-- @type MENU_COALITION_COMMAND
|
||||
-- @extends Menu#MENU
|
||||
MENU_COALITION_COMMAND = {
|
||||
ClassName = "MENU_COALITION_COMMAND"
|
||||
}
|
||||
|
||||
--- Creates a new radio command item for a group
|
||||
-- @param #MENU_COALITION_COMMAND self
|
||||
-- @param DCSCoalition#coalition.side MenuCoalition The coalition owning the menu.
|
||||
-- @param MenuText The text for the menu.
|
||||
-- @param ParentMenu The parent menu.
|
||||
-- @param CommandMenuFunction A function that is called when the menu key is pressed.
|
||||
-- @param CommandMenuArgument An argument for the function.
|
||||
-- @return #MENU_COALITION_COMMAND self
|
||||
function MENU_COALITION_COMMAND:New( MenuCoalition, MenuText, ParentMenu, CommandMenuFunction, CommandMenuArgument )
|
||||
|
||||
-- Arrange meta tables
|
||||
|
||||
local MenuParentPath = {}
|
||||
if ParentMenu ~= nil then
|
||||
MenuParentPath = ParentMenu.MenuPath
|
||||
end
|
||||
|
||||
local self = BASE:Inherit( self, MENU:New( MenuText, MenuParentPath ) )
|
||||
|
||||
self.MenuCoalition = MenuCoalition
|
||||
self.MenuParentPath = MenuParentPath
|
||||
self.MenuText = MenuText
|
||||
self.ParentMenu = ParentMenu
|
||||
|
||||
self:T( { MenuParentPath, MenuText, CommandMenuFunction, CommandMenuArgument } )
|
||||
|
||||
self.MenuPath = missionCommands.addCommandForCoalition( self.MenuCoalition, MenuText, MenuParentPath, CommandMenuFunction, CommandMenuArgument )
|
||||
|
||||
self.CommandMenuFunction = CommandMenuFunction
|
||||
self.CommandMenuArgument = CommandMenuArgument
|
||||
|
||||
ParentMenu.Menus[self.MenuPath] = self
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Removes a radio command item for a coalition
|
||||
-- @param #MENU_COALITION_COMMAND self
|
||||
-- @return #MENU_COALITION_COMMAND self
|
||||
function MENU_COALITION_COMMAND:Remove()
|
||||
self:F( self.MenuPath )
|
||||
|
||||
missionCommands.removeItemForCoalition( self.MenuCoalition, self.MenuPath )
|
||||
self.ParentMenu.Menus[self.MenuPath] = nil
|
||||
return nil
|
||||
end
|
||||
247
Moose Development/Moose/Message.lua
Normal file
247
Moose Development/Moose/Message.lua
Normal file
@@ -0,0 +1,247 @@
|
||||
--- Message System to display Messages for Clients and Coalitions or All.
|
||||
-- Messages are grouped on the display panel per Category to improve readability for the players.
|
||||
-- Messages are shown on the display panel for an amount of seconds, and will then disappear.
|
||||
-- Messages are identified by an ID. The messages with the same ID belonging to the same category will be overwritten if they were still being displayed on the display panel.
|
||||
-- Messages are created with MESSAGE:@{New}().
|
||||
-- Messages are sent to Clients with MESSAGE:@{ToClient}().
|
||||
-- Messages are sent to Coalitions with MESSAGE:@{ToCoalition}().
|
||||
-- Messages are sent to All Players with MESSAGE:@{ToAll}().
|
||||
-- @module Message
|
||||
|
||||
Include.File( "Base" )
|
||||
|
||||
--- The MESSAGE class
|
||||
-- @type MESSAGE
|
||||
MESSAGE = {
|
||||
ClassName = "MESSAGE",
|
||||
MessageCategory = 0,
|
||||
MessageID = 0,
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
||||
-- @param self
|
||||
-- @param #string MessageText is the text of the Message.
|
||||
-- @param #string MessageCategory is a string expressing the Category of the Message. Messages are grouped on the display panel per Category to improve readability.
|
||||
-- @param #number MessageDuration is a number in seconds of how long the MESSAGE should be shown on the display panel.
|
||||
-- @param #string MessageID is a string expressing the ID of the Message.
|
||||
-- @return #MESSAGE
|
||||
-- @usage
|
||||
-- -- Create a series of new Messages.
|
||||
-- -- MessageAll is meant to be sent to all players, for 25 seconds, and is classified as "Score".
|
||||
-- -- MessageRED is meant to be sent to the RED players only, for 10 seconds, and is classified as "End of Mission", with ID "Win".
|
||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||
-- -- MessageClient1 is meant to be sent to a Client, for 25 seconds, and is classified as "Score", with ID "Score".
|
||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" )
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" )
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
|
||||
function MESSAGE:New( MessageText, MessageCategory, MessageDuration, MessageID )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MessageText, MessageCategory, MessageDuration, MessageID } )
|
||||
|
||||
-- When no messagecategory is given, we don't show it as a title...
|
||||
if MessageCategory and MessageCategory ~= "" then
|
||||
self.MessageCategory = MessageCategory .. ": "
|
||||
else
|
||||
self.MessageCategory = ""
|
||||
end
|
||||
|
||||
self.MessageDuration = MessageDuration
|
||||
self.MessageID = MessageID
|
||||
self.MessageTime = timer.getTime()
|
||||
self.MessageText = MessageText
|
||||
|
||||
self.MessageSent = false
|
||||
self.MessageGroup = false
|
||||
self.MessageCoalition = false
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
|
||||
-- @param #MESSAGE self
|
||||
-- @param Client#CLIENT Client is the Group of the Client.
|
||||
-- @return #MESSAGE
|
||||
-- @usage
|
||||
-- -- Send the 2 messages created with the @{New} method to the Client Group.
|
||||
-- -- Note that the Message of MessageClient2 is overwriting the Message of MessageClient1.
|
||||
-- ClientGroup = Group.getByName( "ClientGroup" )
|
||||
--
|
||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||
-- or
|
||||
-- MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||
-- MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" ):ToClient( ClientGroup )
|
||||
-- or
|
||||
-- MessageClient1 = MESSAGE:New( "Congratulations, you've just hit a target", "Score", 25, "Score" )
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
|
||||
-- MessageClient1:ToClient( ClientGroup )
|
||||
-- MessageClient2:ToClient( ClientGroup )
|
||||
function MESSAGE:ToClient( Client )
|
||||
self:F( Client )
|
||||
|
||||
if Client and Client:GetClientGroupID() then
|
||||
|
||||
local ClientGroupID = Client:GetClientGroupID()
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sends a MESSAGE to the Blue coalition.
|
||||
-- @param #MESSAGE self
|
||||
-- @return #MESSAGE
|
||||
-- @usage
|
||||
-- -- Send a message created with the @{New} method to the BLUE coalition.
|
||||
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
|
||||
-- or
|
||||
-- MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToBlue()
|
||||
-- or
|
||||
-- MessageBLUE = MESSAGE:New( "To the BLUE Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||
-- MessageBLUE:ToBlue()
|
||||
function MESSAGE:ToBlue()
|
||||
self:F()
|
||||
|
||||
self:ToCoalition( coalition.side.BLUE )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sends a MESSAGE to the Red Coalition.
|
||||
-- @param #MESSAGE self
|
||||
-- @return #MESSAGE
|
||||
-- @usage
|
||||
-- -- Send a message created with the @{New} method to the RED coalition.
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
|
||||
-- or
|
||||
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToRed()
|
||||
-- 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:ToRed()
|
||||
function MESSAGE:ToRed( )
|
||||
self:F()
|
||||
|
||||
self:ToCoalition( coalition.side.RED )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sends a MESSAGE to a Coalition.
|
||||
-- @param #MESSAGE self
|
||||
-- @param CoalitionSide needs to be filled out by the defined structure of the standard scripting engine @{coalition.side}.
|
||||
-- @return #MESSAGE
|
||||
-- @usage
|
||||
-- -- Send a message created with the @{New} method to the RED coalition.
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
|
||||
-- or
|
||||
-- MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" ):ToCoalition( coalition.side.RED )
|
||||
-- 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 )
|
||||
function MESSAGE:ToCoalition( CoalitionSide )
|
||||
self:F( CoalitionSide )
|
||||
|
||||
if CoalitionSide then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sends a MESSAGE to all players.
|
||||
-- @param #MESSAGE self
|
||||
-- @return #MESSAGE
|
||||
-- @usage
|
||||
-- -- Send a message created to all players.
|
||||
-- MessageAll = MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
||||
-- or
|
||||
-- MESSAGE:New( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", "End of Mission", 25, "Win" ):ToAll()
|
||||
-- or
|
||||
-- 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:ToCoalition( coalition.side.RED )
|
||||
self:ToCoalition( coalition.side.BLUE )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- The MESSAGEQUEUE class
|
||||
-- @type MESSAGEQUEUE
|
||||
MESSAGEQUEUE = {
|
||||
ClientGroups = {},
|
||||
CoalitionSides = {}
|
||||
}
|
||||
|
||||
function MESSAGEQUEUE:New( RefreshInterval )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { RefreshInterval } )
|
||||
|
||||
self.RefreshInterval = RefreshInterval
|
||||
|
||||
self.DisplayFunction = routines.scheduleFunction( self._DisplayMessages, { self }, 0, RefreshInterval )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- This function is called automatically by the MESSAGEQUEUE scheduler.
|
||||
function MESSAGEQUEUE:_DisplayMessages()
|
||||
|
||||
-- First we display all messages that a coalition needs to receive... Also those who are not in a client (CA module clients...).
|
||||
for CoalitionSideID, CoalitionSideData in pairs( self.CoalitionSides ) do
|
||||
for MessageID, MessageData in pairs( CoalitionSideData.Messages ) do
|
||||
if MessageData.MessageSent == false then
|
||||
--trigger.action.outTextForCoalition( CoalitionSideID, MessageData.MessageCategory .. '\n' .. MessageData.MessageText:gsub("\n$",""):gsub("\n$",""), MessageData.MessageDuration )
|
||||
MessageData.MessageSent = true
|
||||
end
|
||||
local MessageTimeLeft = ( MessageData.MessageTime + MessageData.MessageDuration ) - timer.getTime()
|
||||
if MessageTimeLeft <= 0 then
|
||||
MessageData = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Then we send the messages for each individual client, but also to be included are those Coalition messages for the Clients who belong to a coalition.
|
||||
-- Because the Client messages will overwrite the Coalition messages (for that Client).
|
||||
for ClientGroupName, ClientGroupData in pairs( self.ClientGroups ) do
|
||||
for MessageID, MessageData in pairs( ClientGroupData.Messages ) do
|
||||
if MessageData.MessageGroup == false then
|
||||
trigger.action.outTextForGroup( Group.getByName(ClientGroupName):getID(), MessageData.MessageCategory .. '\n' .. MessageData.MessageText:gsub("\n$",""):gsub("\n$",""), MessageData.MessageDuration )
|
||||
MessageData.MessageGroup = true
|
||||
end
|
||||
local MessageTimeLeft = ( MessageData.MessageTime + MessageData.MessageDuration ) - timer.getTime()
|
||||
if MessageTimeLeft <= 0 then
|
||||
MessageData = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Now check if the Client also has messages that belong to the Coalition of the Client...
|
||||
for CoalitionSideID, CoalitionSideData in pairs( self.CoalitionSides ) do
|
||||
for MessageID, MessageData in pairs( CoalitionSideData.Messages ) do
|
||||
local CoalitionGroup = Group.getByName( ClientGroupName )
|
||||
if CoalitionGroup and CoalitionGroup:getCoalition() == CoalitionSideID then
|
||||
if MessageData.MessageCoalition == false then
|
||||
trigger.action.outTextForGroup( Group.getByName(ClientGroupName):getID(), MessageData.MessageCategory .. '\n' .. MessageData.MessageText:gsub("\n$",""):gsub("\n$",""), MessageData.MessageDuration )
|
||||
MessageData.MessageCoalition = true
|
||||
end
|
||||
end
|
||||
local MessageTimeLeft = ( MessageData.MessageTime + MessageData.MessageDuration ) - timer.getTime()
|
||||
if MessageTimeLeft <= 0 then
|
||||
MessageData = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- The _MessageQueue object is created when the MESSAGE class module is loaded.
|
||||
--_MessageQueue = MESSAGEQUEUE:New( 0.5 )
|
||||
|
||||
680
Moose Development/Moose/MissileTrainer.lua
Normal file
680
Moose Development/Moose/MissileTrainer.lua
Normal file
@@ -0,0 +1,680 @@
|
||||
--- Provides missile training functions.
|
||||
--
|
||||
-- @{#MISSILETRAINER} class
|
||||
-- ========================
|
||||
-- The @{#MISSILETRAINER} class uses the DCS world messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft,
|
||||
-- the class will destroy the missile within a certain range, to avoid damage to your aircraft.
|
||||
-- It suports the following functionality:
|
||||
--
|
||||
-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes.
|
||||
-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range …
|
||||
-- * Provide alerts when a missile would have killed your aircraft.
|
||||
-- * Provide alerts when the missile self destructs.
|
||||
-- * Enable / Disable and Configure the Missile Trainer using the various menu options.
|
||||
--
|
||||
-- When running a mission where MISSILETRAINER is used, the following radio menu structure ( 'Radio Menu' -> 'Other (F10)' -> 'MissileTrainer' ) options are available for the players:
|
||||
--
|
||||
-- * **Messages**: Menu to configure all messages.
|
||||
-- * **Messages On**: Show all messages.
|
||||
-- * **Messages Off**: Disable all messages.
|
||||
-- * **Tracking**: Menu to configure missile tracking messages.
|
||||
-- * **To All**: Shows missile tracking messages to all players.
|
||||
-- * **To Target**: Shows missile tracking messages only to the player where the missile is targetted at.
|
||||
-- * **Tracking On**: Show missile tracking messages.
|
||||
-- * **Tracking Off**: Disable missile tracking messages.
|
||||
-- * **Frequency Increase**: Increases the missile tracking message frequency with one second.
|
||||
-- * **Frequency Decrease**: Decreases the missile tracking message frequency with one second.
|
||||
-- * **Alerts**: Menu to configure alert messages.
|
||||
-- * **To All**: Shows alert messages to all players.
|
||||
-- * **To Target**: Shows alert messages only to the player where the missile is (was) targetted at.
|
||||
-- * **Hits On**: Show missile hit alert messages.
|
||||
-- * **Hits Off**: Disable missile hit alert messages.
|
||||
-- * **Launches On**: Show missile launch messages.
|
||||
-- * **Launches Off**: Disable missile launch messages.
|
||||
-- * **Details**: Menu to configure message details.
|
||||
-- * **Range On**: Shows range information when a missile is fired to a target.
|
||||
-- * **Range Off**: Disable range information when a missile is fired to a target.
|
||||
-- * **Bearing On**: Shows bearing information when a missile is fired to a target.
|
||||
-- * **Bearing Off**: Disable bearing information when a missile is fired to a target.
|
||||
-- * **Distance**: Menu to configure the distance when a missile needs to be destroyed when near to a player, during tracking. This will improve/influence hit calculation accuracy, but has the risk of damaging the aircraft when the missile reaches the aircraft before the distance is measured.
|
||||
-- * **50 meter**: Destroys the missile when the distance to the aircraft is below or equal to 50 meter.
|
||||
-- * **100 meter**: Destroys the missile when the distance to the aircraft is below or equal to 100 meter.
|
||||
-- * **150 meter**: Destroys the missile when the distance to the aircraft is below or equal to 150 meter.
|
||||
-- * **200 meter**: Destroys the missile when the distance to the aircraft is below or equal to 200 meter.
|
||||
--
|
||||
--
|
||||
-- MISSILETRAINER construction methods:
|
||||
-- ====================================
|
||||
-- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method:
|
||||
--
|
||||
-- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed.
|
||||
--
|
||||
-- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those.
|
||||
--
|
||||
-- MISSILETRAINER initialization methods:
|
||||
-- ======================================
|
||||
-- A MISSILETRAINER object will behave differently based on the usage of initialization methods:
|
||||
--
|
||||
-- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF.
|
||||
-- * @{#MISSILETRAINER.InitTrackingToAll}: Sets by default the missile tracking report for all players or only for those missiles targetted to you.
|
||||
-- * @{#MISSILETRAINER.InitTrackingOnOff}: Sets by default the display of missile tracking report to be ON or OFF.
|
||||
-- * @{#MISSILETRAINER.InitTrackingFrequency}: Increases, decreases the missile tracking message display frequency with the provided time interval in seconds.
|
||||
-- * @{#MISSILETRAINER.InitAlertsToAll}: Sets by default the display of alerts to be shown to all players or only to you.
|
||||
-- * @{#MISSILETRAINER.InitAlertsHitsOnOff}: Sets by default the display of hit alerts ON or OFF.
|
||||
-- * @{#MISSILETRAINER.InitAlertsLaunchesOnOff}: Sets by default the display of launch alerts ON or OFF.
|
||||
-- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF.
|
||||
-- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF.
|
||||
-- * @{#MISSILETRAINER.InitMenusOnOff}: Allows to configure the options through the radio menu.
|
||||
--
|
||||
-- @module MissileTrainer
|
||||
-- @author FlightControl
|
||||
|
||||
|
||||
Include.File( "Client" )
|
||||
Include.File( "Scheduler" )
|
||||
|
||||
--- The MISSILETRAINER class
|
||||
-- @type MISSILETRAINER
|
||||
-- @extends Base#BASE
|
||||
MISSILETRAINER = {
|
||||
ClassName = "MISSILETRAINER",
|
||||
}
|
||||
|
||||
--- Creates the main object which is handling missile tracking.
|
||||
-- When a missile is fired a SCHEDULER is set off that follows the missile. When near a certain a client player, the missile will be destroyed.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #number Distance The distance in meters when a tracked missile needs to be destroyed when close to a player.
|
||||
-- @param #string Briefing (Optional) Will show a text to the players when starting their mission. Can be used for briefing purposes.
|
||||
-- @return #MISSILETRAINER
|
||||
function MISSILETRAINER:New( Distance, Briefing )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( Distance )
|
||||
|
||||
if Briefing then
|
||||
self.Briefing = Briefing
|
||||
end
|
||||
|
||||
self.Schedulers = {}
|
||||
self.SchedulerID = 0
|
||||
|
||||
self.MessageInterval = 2
|
||||
self.MessageLastTime = timer.getTime()
|
||||
|
||||
self.Distance = Distance / 1000
|
||||
|
||||
_EVENTDISPATCHER:OnShot( self._EventShot, self )
|
||||
|
||||
self.DB = DATABASE:New():FilterStart()
|
||||
self.DBClients = self.DB.Clients
|
||||
self.DBUnits = self.DB.Units
|
||||
|
||||
for ClientID, Client in pairs( self.DBClients ) do
|
||||
|
||||
local function _Alive( Client )
|
||||
|
||||
if self.Briefing then
|
||||
Client:Message( self.Briefing, 15, "HELLO WORLD", "Trainer" )
|
||||
end
|
||||
|
||||
if self.MenusOnOff == true then
|
||||
Client:Message( "Use the 'Radio Menu' -> 'Other (F10)' -> 'Missile Trainer' menu options to change the Missile Trainer settings (for all players).", 15, "MENU", "Trainer" )
|
||||
|
||||
Client.MainMenu = MENU_CLIENT:New( Client, "Missile Trainer", nil ) -- Menu#MENU_CLIENT
|
||||
|
||||
Client.MenuMessages = MENU_CLIENT:New( Client, "Messages", Client.MainMenu )
|
||||
Client.MenuOn = MENU_CLIENT_COMMAND:New( Client, "Messages On", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = true } )
|
||||
Client.MenuOff = MENU_CLIENT_COMMAND:New( Client, "Messages Off", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = false } )
|
||||
|
||||
Client.MenuTracking = MENU_CLIENT:New( Client, "Tracking", Client.MainMenu )
|
||||
Client.MenuTrackingToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = true } )
|
||||
Client.MenuTrackingToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = false } )
|
||||
Client.MenuTrackOn = MENU_CLIENT_COMMAND:New( Client, "Tracking On", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = true } )
|
||||
Client.MenuTrackOff = MENU_CLIENT_COMMAND:New( Client, "Tracking Off", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = false } )
|
||||
Client.MenuTrackIncrease = MENU_CLIENT_COMMAND:New( Client, "Frequency Increase", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingFrequency = -1 } )
|
||||
Client.MenuTrackDecrease = MENU_CLIENT_COMMAND:New( Client, "Frequency Decrease", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingFrequency = 1 } )
|
||||
|
||||
Client.MenuAlerts = MENU_CLIENT:New( Client, "Alerts", Client.MainMenu )
|
||||
Client.MenuAlertsToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = true } )
|
||||
Client.MenuAlertsToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = false } )
|
||||
Client.MenuHitsOn = MENU_CLIENT_COMMAND:New( Client, "Hits On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = true } )
|
||||
Client.MenuHitsOff = MENU_CLIENT_COMMAND:New( Client, "Hits Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = false } )
|
||||
Client.MenuLaunchesOn = MENU_CLIENT_COMMAND:New( Client, "Launches On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = true } )
|
||||
Client.MenuLaunchesOff = MENU_CLIENT_COMMAND:New( Client, "Launches Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = false } )
|
||||
|
||||
Client.MenuDetails = MENU_CLIENT:New( Client, "Details", Client.MainMenu )
|
||||
Client.MenuDetailsDistanceOn = MENU_CLIENT_COMMAND:New( Client, "Range On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = true } )
|
||||
Client.MenuDetailsDistanceOff = MENU_CLIENT_COMMAND:New( Client, "Range Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = false } )
|
||||
Client.MenuDetailsBearingOn = MENU_CLIENT_COMMAND:New( Client, "Bearing On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = true } )
|
||||
Client.MenuDetailsBearingOff = MENU_CLIENT_COMMAND:New( Client, "Bearing Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = false } )
|
||||
|
||||
Client.MenuDistance = MENU_CLIENT:New( Client, "Set distance to plane", Client.MainMenu )
|
||||
Client.MenuDistance50 = MENU_CLIENT_COMMAND:New( Client, "50 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 50 / 1000 } )
|
||||
Client.MenuDistance100 = MENU_CLIENT_COMMAND:New( Client, "100 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 100 / 1000 } )
|
||||
Client.MenuDistance150 = MENU_CLIENT_COMMAND:New( Client, "150 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 150 / 1000 } )
|
||||
Client.MenuDistance200 = MENU_CLIENT_COMMAND:New( Client, "200 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 200 / 1000 } )
|
||||
else
|
||||
if Client.MainMenu then
|
||||
Client.MainMenu:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local ClientID = Client:GetID()
|
||||
self:T( ClientID )
|
||||
if not self.TrackingMissiles[ClientID] then
|
||||
self.TrackingMissiles[ClientID] = {}
|
||||
end
|
||||
self.TrackingMissiles[ClientID].Client = Client
|
||||
if not self.TrackingMissiles[ClientID].MissileData then
|
||||
self.TrackingMissiles[ClientID].MissileData = {}
|
||||
end
|
||||
end
|
||||
|
||||
Client:Alive( _Alive )
|
||||
|
||||
end
|
||||
|
||||
-- self.DB:ForEachClient(
|
||||
-- --- @param Client#CLIENT Client
|
||||
-- function( Client )
|
||||
--
|
||||
-- ... actions ...
|
||||
--
|
||||
-- end
|
||||
-- )
|
||||
|
||||
self.MessagesOnOff = true
|
||||
|
||||
self.TrackingToAll = false
|
||||
self.TrackingOnOff = true
|
||||
self.TrackingFrequency = 3
|
||||
|
||||
self.AlertsToAll = true
|
||||
self.AlertsHitsOnOff = true
|
||||
self.AlertsLaunchesOnOff = true
|
||||
|
||||
self.DetailsRangeOnOff = true
|
||||
self.DetailsBearingOnOff = true
|
||||
|
||||
self.MenusOnOff = true
|
||||
|
||||
self.TrackingMissiles = {}
|
||||
|
||||
self.TrackingScheduler = SCHEDULER:New( self, self._TrackMissiles, {}, 0.5, 0.05, 0 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-- Initialization methods.
|
||||
|
||||
|
||||
--- Sets by default the display of any message to be ON or OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean MessagesOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
function MISSILETRAINER:InitMessagesOnOff( MessagesOnOff )
|
||||
self:F( MessagesOnOff )
|
||||
|
||||
self.MessagesOnOff = MessagesOnOff
|
||||
if self.MessagesOnOff == true then
|
||||
MESSAGE:New( "Messages ON", "Menu", 15, "ID" ):ToAll()
|
||||
else
|
||||
MESSAGE:New( "Messages OFF", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets by default the missile tracking report for all players or only for those missiles targetted to you.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean TrackingToAll true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
function MISSILETRAINER:InitTrackingToAll( TrackingToAll )
|
||||
self:F( TrackingToAll )
|
||||
|
||||
self.TrackingToAll = TrackingToAll
|
||||
if self.TrackingToAll == true then
|
||||
MESSAGE:New( "Missile tracking to all players ON", "Menu", 15, "ID" ):ToAll()
|
||||
else
|
||||
MESSAGE:New( "Missile tracking to all players OFF", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets by default the display of missile tracking report to be ON or OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean TrackingOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
function MISSILETRAINER:InitTrackingOnOff( TrackingOnOff )
|
||||
self:F( TrackingOnOff )
|
||||
|
||||
self.TrackingOnOff = TrackingOnOff
|
||||
if self.TrackingOnOff == true then
|
||||
MESSAGE:New( "Missile tracking ON", "Menu", 15, "ID" ):ToAll()
|
||||
else
|
||||
MESSAGE:New( "Missile tracking OFF", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Increases, decreases the missile tracking message display frequency with the provided time interval in seconds.
|
||||
-- The default frequency is a 3 second interval, so the Tracking Frequency parameter specifies the increase or decrease from the default 3 seconds or the last frequency update.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #number TrackingFrequency Provide a negative or positive value in seconds to incraese or decrease the display frequency.
|
||||
-- @return #MISSILETRAINER self
|
||||
function MISSILETRAINER:InitTrackingFrequency( TrackingFrequency )
|
||||
self:F( TrackingFrequency )
|
||||
|
||||
self.TrackingFrequency = self.TrackingFrequency + TrackingFrequency
|
||||
if self.TrackingFrequency < 0.5 then
|
||||
self.TrackingFrequency = 0.5
|
||||
end
|
||||
if self.TrackingFrequency then
|
||||
MESSAGE:New( "Missile tracking frequency is " .. self.TrackingFrequency .. " seconds.", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets by default the display of alerts to be shown to all players or only to you.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean AlertsToAll true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
function MISSILETRAINER:InitAlertsToAll( AlertsToAll )
|
||||
self:F( AlertsToAll )
|
||||
|
||||
self.AlertsToAll = AlertsToAll
|
||||
if self.AlertsToAll == true then
|
||||
MESSAGE:New( "Alerts to all players ON", "Menu", 15, "ID" ):ToAll()
|
||||
else
|
||||
MESSAGE:New( "Alerts to all players OFF", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets by default the display of hit alerts ON or OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean AlertsHitsOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
function MISSILETRAINER:InitAlertsHitsOnOff( AlertsHitsOnOff )
|
||||
self:F( AlertsHitsOnOff )
|
||||
|
||||
self.AlertsHitsOnOff = AlertsHitsOnOff
|
||||
if self.AlertsHitsOnOff == true then
|
||||
MESSAGE:New( "Alerts Hits ON", "Menu", 15, "ID" ):ToAll()
|
||||
else
|
||||
MESSAGE:New( "Alerts Hits OFF", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets by default the display of launch alerts ON or OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean AlertsLaunchesOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
function MISSILETRAINER:InitAlertsLaunchesOnOff( AlertsLaunchesOnOff )
|
||||
self:F( AlertsLaunchesOnOff )
|
||||
|
||||
self.AlertsLaunchesOnOff = AlertsLaunchesOnOff
|
||||
if self.AlertsLaunchesOnOff == true then
|
||||
MESSAGE:New( "Alerts Launches ON", "Menu", 15, "ID" ):ToAll()
|
||||
else
|
||||
MESSAGE:New( "Alerts Launches OFF", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets by default the display of range information of missiles ON of OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean DetailsRangeOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
function MISSILETRAINER:InitRangeOnOff( DetailsRangeOnOff )
|
||||
self:F( DetailsRangeOnOff )
|
||||
|
||||
self.DetailsRangeOnOff = DetailsRangeOnOff
|
||||
if self.DetailsRangeOnOff == true then
|
||||
MESSAGE:New( "Range display ON", "Menu", 15, "ID" ):ToAll()
|
||||
else
|
||||
MESSAGE:New( "Range display OFF", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets by default the display of bearing information of missiles ON of OFF.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean DetailsBearingOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
function MISSILETRAINER:InitBearingOnOff( DetailsBearingOnOff )
|
||||
self:F( DetailsBearingOnOff )
|
||||
|
||||
self.DetailsBearingOnOff = DetailsBearingOnOff
|
||||
if self.DetailsBearingOnOff == true then
|
||||
MESSAGE:New( "Bearing display OFF", "Menu", 15, "ID" ):ToAll()
|
||||
else
|
||||
MESSAGE:New( "Bearing display OFF", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enables / Disables the menus.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param #boolean MenusOnOff true or false
|
||||
-- @return #MISSILETRAINER self
|
||||
function MISSILETRAINER:InitMenusOnOff( MenusOnOff )
|
||||
self:F( MenusOnOff )
|
||||
|
||||
self.MenusOnOff = MenusOnOff
|
||||
if self.MenusOnOff == true then
|
||||
MESSAGE:New( "Menus are ENABLED (only when a player rejoins a slot)", "Menu", 15, "ID" ):ToAll()
|
||||
else
|
||||
MESSAGE:New( "Menus are DISABLED", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
-- Menu functions
|
||||
|
||||
function MISSILETRAINER._MenuMessages( MenuParameters )
|
||||
|
||||
local self = MenuParameters.MenuSelf
|
||||
|
||||
if MenuParameters.MessagesOnOff ~= nil then
|
||||
self:InitMessagesOnOff( MenuParameters.MessagesOnOff )
|
||||
end
|
||||
|
||||
if MenuParameters.TrackingToAll ~= nil then
|
||||
self:InitTrackingToAll( MenuParameters.TrackingToAll )
|
||||
end
|
||||
|
||||
if MenuParameters.TrackingOnOff ~= nil then
|
||||
self:InitTrackingOnOff( MenuParameters.TrackingOnOff )
|
||||
end
|
||||
|
||||
if MenuParameters.TrackingFrequency ~= nil then
|
||||
self:InitTrackingFrequency( MenuParameters.TrackingFrequency )
|
||||
end
|
||||
|
||||
if MenuParameters.AlertsToAll ~= nil then
|
||||
self:InitAlertsToAll( MenuParameters.AlertsToAll )
|
||||
end
|
||||
|
||||
if MenuParameters.AlertsHitsOnOff ~= nil then
|
||||
self:InitAlertsHitsOnOff( MenuParameters.AlertsHitsOnOff )
|
||||
end
|
||||
|
||||
if MenuParameters.AlertsLaunchesOnOff ~= nil then
|
||||
self:InitAlertsLaunchesOnOff( MenuParameters.AlertsLaunchesOnOff )
|
||||
end
|
||||
|
||||
if MenuParameters.DetailsRangeOnOff ~= nil then
|
||||
self:InitRangeOnOff( MenuParameters.DetailsRangeOnOff )
|
||||
end
|
||||
|
||||
if MenuParameters.DetailsBearingOnOff ~= nil then
|
||||
self:InitBearingOnOff( MenuParameters.DetailsBearingOnOff )
|
||||
end
|
||||
|
||||
if MenuParameters.Distance ~= nil then
|
||||
self.Distance = MenuParameters.Distance
|
||||
MESSAGE:New( "Hit detection distance set to " .. self.Distance .. " meters", "Menu", 15, "ID" ):ToAll()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
|
||||
-- @param #MISSILETRAINER self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function MISSILETRAINER:_EventShot( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
local TrainerSourceDCSUnit = Event.IniDCSUnit
|
||||
local TrainerSourceDCSUnitName = Event.IniDCSUnitName
|
||||
local TrainerWeapon = Event.Weapon -- Identify the weapon fired
|
||||
local TrainerWeaponName = Event.WeaponName -- return weapon type
|
||||
|
||||
self:T( "Missile Launched = " .. TrainerWeaponName )
|
||||
|
||||
local TrainerTargetDCSUnit = TrainerWeapon:getTarget() -- Identify target
|
||||
local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit )
|
||||
local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill
|
||||
|
||||
self:T(TrainerTargetDCSUnitName )
|
||||
|
||||
local Client = self.DBClients[TrainerTargetDCSUnitName]
|
||||
if Client then
|
||||
|
||||
local TrainerSourceUnit = UNIT:Find( TrainerSourceDCSUnit )
|
||||
local TrainerTargetUnit = UNIT:Find( TrainerTargetDCSUnit )
|
||||
|
||||
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
|
||||
|
||||
local Message = MESSAGE:New(
|
||||
string.format( "%s launched a %s",
|
||||
TrainerSourceUnit:GetTypeName(),
|
||||
TrainerWeaponName
|
||||
) .. self:_AddRange( Client, TrainerWeapon ) .. self:_AddBearing( Client, TrainerWeapon ),"Launch Alert", 5, "ID" )
|
||||
|
||||
if self.AlertsToAll then
|
||||
Message:ToAll()
|
||||
else
|
||||
Message:ToClient( Client )
|
||||
end
|
||||
end
|
||||
|
||||
local ClientID = Client:GetID()
|
||||
local MissileData = {}
|
||||
MissileData.TrainerSourceUnit = TrainerSourceUnit
|
||||
MissileData.TrainerWeapon = TrainerWeapon
|
||||
MissileData.TrainerTargetUnit = TrainerTargetUnit
|
||||
MissileData.TrainerWeaponTypeName = TrainerWeapon:getTypeName()
|
||||
MissileData.TrainerWeaponLaunched = true
|
||||
table.insert( self.TrackingMissiles[ClientID].MissileData, MissileData )
|
||||
--self:T( self.TrackingMissiles )
|
||||
end
|
||||
end
|
||||
|
||||
function MISSILETRAINER:_AddRange( Client, TrainerWeapon )
|
||||
|
||||
local RangeText = ""
|
||||
|
||||
if self.DetailsRangeOnOff then
|
||||
|
||||
local PositionMissile = TrainerWeapon:getPoint()
|
||||
local PositionTarget = Client:GetPointVec3()
|
||||
|
||||
local Range = ( ( PositionMissile.x - PositionTarget.x )^2 +
|
||||
( PositionMissile.y - PositionTarget.y )^2 +
|
||||
( PositionMissile.z - PositionTarget.z )^2
|
||||
) ^ 0.5 / 1000
|
||||
|
||||
RangeText = string.format( ", at %4.2fkm", Range )
|
||||
end
|
||||
|
||||
return RangeText
|
||||
end
|
||||
|
||||
function MISSILETRAINER:_AddBearing( Client, TrainerWeapon )
|
||||
|
||||
local BearingText = ""
|
||||
|
||||
if self.DetailsBearingOnOff then
|
||||
|
||||
local PositionMissile = TrainerWeapon:getPoint()
|
||||
local PositionTarget = Client:GetPointVec3()
|
||||
|
||||
self:T2( { PositionTarget, PositionMissile })
|
||||
|
||||
local DirectionVector = { x = PositionMissile.x - PositionTarget.x, y = PositionMissile.y - PositionTarget.y, z = PositionMissile.z - PositionTarget.z }
|
||||
local DirectionRadians = math.atan2( DirectionVector.z, DirectionVector.x )
|
||||
--DirectionRadians = DirectionRadians + routines.getNorthCorrection( PositionTarget )
|
||||
if DirectionRadians < 0 then
|
||||
DirectionRadians = DirectionRadians + 2 * math.pi
|
||||
end
|
||||
local DirectionDegrees = DirectionRadians * 180 / math.pi
|
||||
|
||||
BearingText = string.format( ", %d degrees", DirectionDegrees )
|
||||
end
|
||||
|
||||
return BearingText
|
||||
end
|
||||
|
||||
|
||||
function MISSILETRAINER:_TrackMissiles()
|
||||
self:F2()
|
||||
|
||||
|
||||
local ShowMessages = false
|
||||
if self.MessagesOnOff and self.MessageLastTime + self.TrackingFrequency <= timer.getTime() then
|
||||
self.MessageLastTime = timer.getTime()
|
||||
ShowMessages = true
|
||||
end
|
||||
|
||||
-- ALERTS PART
|
||||
|
||||
-- Loop for all Player Clients to check the alerts and deletion of missiles.
|
||||
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
local Client = ClientData.Client
|
||||
self:T2( { Client:GetName() } )
|
||||
|
||||
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
|
||||
self:T3( MissileDataID )
|
||||
|
||||
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
||||
local TrainerWeapon = MissileData.TrainerWeapon
|
||||
local TrainerTargetUnit = MissileData.TrainerTargetUnit
|
||||
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
|
||||
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
|
||||
|
||||
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
|
||||
local PositionMissile = TrainerWeapon:getPosition().p
|
||||
local PositionTarget = Client:GetPointVec3()
|
||||
|
||||
local Distance = ( ( PositionMissile.x - PositionTarget.x )^2 +
|
||||
( PositionMissile.y - PositionTarget.y )^2 +
|
||||
( PositionMissile.z - PositionTarget.z )^2
|
||||
) ^ 0.5 / 1000
|
||||
|
||||
if Distance <= self.Distance then
|
||||
-- Hit alert
|
||||
TrainerWeapon:destroy()
|
||||
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
|
||||
|
||||
self:T( "killed" )
|
||||
|
||||
local Message = MESSAGE:New(
|
||||
string.format( "%s launched by %s killed %s",
|
||||
TrainerWeapon:getTypeName(),
|
||||
TrainerSourceUnit:GetTypeName(),
|
||||
TrainerTargetUnit:GetPlayerName()
|
||||
),"Hit Alert", 15, "ID" )
|
||||
|
||||
if self.AlertsToAll == true then
|
||||
Message:ToAll()
|
||||
else
|
||||
Message:ToClient( Client )
|
||||
end
|
||||
|
||||
MissileData = nil
|
||||
table.remove( ClientData.MissileData, MissileDataID )
|
||||
self:T(ClientData.MissileData)
|
||||
end
|
||||
end
|
||||
else
|
||||
if not ( TrainerWeapon and TrainerWeapon:isExist() ) then
|
||||
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
|
||||
-- Weapon does not exist anymore. Delete from Table
|
||||
local Message = MESSAGE:New(
|
||||
string.format( "%s launched by %s self destructed!",
|
||||
TrainerWeaponTypeName,
|
||||
TrainerSourceUnit:GetTypeName()
|
||||
),"Tracking", 5, "ID" )
|
||||
|
||||
if self.AlertsToAll == true then
|
||||
Message:ToAll()
|
||||
else
|
||||
Message:ToClient( Client )
|
||||
end
|
||||
end
|
||||
MissileData = nil
|
||||
table.remove( ClientData.MissileData, MissileDataID )
|
||||
self:T( ClientData.MissileData )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if ShowMessages == true and self.MessagesOnOff == true and self.TrackingOnOff == true then -- Only do this when tracking information needs to be displayed.
|
||||
|
||||
-- TRACKING PART
|
||||
|
||||
-- For the current client, the missile range and bearing details are displayed To the Player Client.
|
||||
-- For the other clients, the missile range and bearing details are displayed To the other Player Clients.
|
||||
-- To achieve this, a cross loop is done for each Player Client <-> Other Player Client missile information.
|
||||
|
||||
-- Main Player Client loop
|
||||
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
local Client = ClientData.Client
|
||||
self:T2( { Client:GetName() } )
|
||||
|
||||
|
||||
ClientData.MessageToClient = ""
|
||||
ClientData.MessageToAll = ""
|
||||
|
||||
-- Other Players Client loop
|
||||
for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do
|
||||
self:T3( MissileDataID )
|
||||
|
||||
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
||||
local TrainerWeapon = MissileData.TrainerWeapon
|
||||
local TrainerTargetUnit = MissileData.TrainerTargetUnit
|
||||
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
|
||||
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
|
||||
|
||||
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
|
||||
|
||||
if ShowMessages == true then
|
||||
local TrackingTo
|
||||
TrackingTo = string.format( " -> %s",
|
||||
TrainerWeaponTypeName
|
||||
)
|
||||
|
||||
if ClientDataID == TrackingDataID then
|
||||
if ClientData.MessageToClient == "" then
|
||||
ClientData.MessageToClient = "Missiles to You:\n"
|
||||
end
|
||||
ClientData.MessageToClient = ClientData.MessageToClient .. TrackingTo .. self:_AddRange( ClientData.Client, TrainerWeapon ) .. self:_AddBearing( ClientData.Client, TrainerWeapon ) .. "\n"
|
||||
else
|
||||
if self.TrackingToAll == true then
|
||||
if ClientData.MessageToAll == "" then
|
||||
ClientData.MessageToAll = "Missiles to other Players:\n"
|
||||
end
|
||||
ClientData.MessageToAll = ClientData.MessageToAll .. TrackingTo .. self:_AddRange( ClientData.Client, TrainerWeapon ) .. self:_AddBearing( ClientData.Client, TrainerWeapon ) .. " ( " .. TrainerTargetUnit:GetPlayerName() .. " )\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Once the Player Client and the Other Player Client tracking messages are prepared, show them.
|
||||
if ClientData.MessageToClient ~= "" or ClientData.MessageToAll ~= "" then
|
||||
local Message = MESSAGE:New( ClientData.MessageToClient .. ClientData.MessageToAll, "Tracking", 1, "ID" ):ToClient( Client )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
654
Moose Development/Moose/Mission.lua
Normal file
654
Moose Development/Moose/Mission.lua
Normal file
@@ -0,0 +1,654 @@
|
||||
--- A MISSION is the main owner of a Mission orchestration within MOOSE . The Mission framework orchestrates @{CLIENT}s, @{TASK}s, @{STAGE}s etc.
|
||||
-- A @{CLIENT} needs to be registered within the @{MISSION} through the function @{AddClient}. A @{TASK} needs to be registered within the @{MISSION} through the function @{AddTask}.
|
||||
-- @module MISSION
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Client" )
|
||||
Include.File( "Task" )
|
||||
|
||||
--- The MISSION class
|
||||
-- @type
|
||||
MISSION = {
|
||||
ClassName = "MISSION",
|
||||
Name = "",
|
||||
MissionStatus = "PENDING",
|
||||
_Clients = {},
|
||||
_Tasks = {},
|
||||
_ActiveTasks = {},
|
||||
GoalFunction = nil,
|
||||
MissionReportTrigger = 0,
|
||||
MissionProgressTrigger = 0,
|
||||
MissionReportShow = false,
|
||||
MissionReportFlash = false,
|
||||
MissionTimeInterval = 0,
|
||||
MissionCoalition = "",
|
||||
SUCCESS = 1,
|
||||
FAILED = 2,
|
||||
REPEAT = 3,
|
||||
_GoalTasks = {}
|
||||
}
|
||||
|
||||
|
||||
function MISSION:Meta()
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc.
|
||||
-- @param string MissionName is the name of the mission. This name will be used to reference the status of each mission by the players.
|
||||
-- @param string MissionPriority is a string indicating the "priority" of the Mission. f.e. "Primary", "Secondary" or "First", "Second". It is free format and up to the Mission designer to choose. There are no rules behind this field.
|
||||
-- @param string MissionBriefing is a string indicating the mission briefing to be shown when a player joins a @{CLIENT}.
|
||||
-- @param string MissionCoalition is a string indicating the coalition or party to which this mission belongs to. It is free format and can be chosen freely by the mission designer. Note that this field is not to be confused with the coalition concept of the ME. Examples of a Mission Coalition could be "NATO", "CCCP", "Intruders", "Terrorists"...
|
||||
-- @return MISSION
|
||||
-- @usage
|
||||
-- -- Declare a few missions.
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'Russia Transport Troops SA-6', 'Operational', 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.', 'Russia' )
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'Patriots', 'Primary', 'Our intelligence reports that 3 Patriot SAM defense batteries are located near Ruisi, Kvarhiti and Gori.', 'Russia' )
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'Package Delivery', 'Operational', 'In order to be in full control of the situation, we need you to deliver a very important package at a secret location. Fly undetected through the NATO defenses and deliver the secret package. The secret agent is located at waypoint 4.', 'Russia' )
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'Rescue General', 'Tactical', 'Our intelligence has received a remote signal behind Gori. We believe it is a very important Russian General that was captured by Georgia. Go out there and rescue him! Ensure you stay out of the battle zone, keep south. Waypoint 4 is the location of our Russian General.', 'Russia' )
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'NATO Transport Troops', 'Operational', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.', 'NATO' )
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'SA-6 SAMs', 'Primary', 'Our intelligence reports that 3 SA-6 SAM defense batteries are located near Didmukha, Khetagurov and Berula. Eliminate the Russian SAMs.', 'NATO' )
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'NATO Sling Load', 'Operational', 'Fly to the cargo pickup zone at Dzegvi or Kaspi, and sling the cargo to Soganlug airbase.', 'NATO' )
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'Rescue secret agent', 'Tactical', 'In order to be in full control of the situation, we need you to rescue a secret agent from the woods behind enemy lines. Avoid the Russian defenses and rescue the agent. Keep south until Khasuri, and keep your eyes open for any SAM presence. The agent is located at waypoint 4 on your kneeboard.', 'NATO' )
|
||||
function MISSION:New( MissionName, MissionPriority, MissionBriefing, MissionCoalition )
|
||||
|
||||
self = MISSION:Meta()
|
||||
self:T({ MissionName, MissionPriority, MissionBriefing, MissionCoalition })
|
||||
|
||||
local Valid = true
|
||||
|
||||
Valid = routines.ValidateString( MissionName, "MissionName", Valid )
|
||||
Valid = routines.ValidateString( MissionPriority, "MissionPriority", Valid )
|
||||
Valid = routines.ValidateString( MissionBriefing, "MissionBriefing", Valid )
|
||||
Valid = routines.ValidateString( MissionCoalition, "MissionCoalition", Valid )
|
||||
|
||||
if Valid then
|
||||
self.Name = MissionName
|
||||
self.MissionPriority = MissionPriority
|
||||
self.MissionBriefing = MissionBriefing
|
||||
self.MissionCoalition = MissionCoalition
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Returns if a Mission has completed.
|
||||
-- @return bool
|
||||
function MISSION:IsCompleted()
|
||||
self:F()
|
||||
return self.MissionStatus == "ACCOMPLISHED"
|
||||
end
|
||||
|
||||
--- Set a Mission to completed.
|
||||
function MISSION:Completed()
|
||||
self:F()
|
||||
self.MissionStatus = "ACCOMPLISHED"
|
||||
self:StatusToClients()
|
||||
end
|
||||
|
||||
--- Returns if a Mission is ongoing.
|
||||
-- treturn bool
|
||||
function MISSION:IsOngoing()
|
||||
self:F()
|
||||
return self.MissionStatus == "ONGOING"
|
||||
end
|
||||
|
||||
--- Set a Mission to ongoing.
|
||||
function MISSION:Ongoing()
|
||||
self:F()
|
||||
self.MissionStatus = "ONGOING"
|
||||
--self:StatusToClients()
|
||||
end
|
||||
|
||||
--- Returns if a Mission is pending.
|
||||
-- treturn bool
|
||||
function MISSION:IsPending()
|
||||
self:F()
|
||||
return self.MissionStatus == "PENDING"
|
||||
end
|
||||
|
||||
--- Set a Mission to pending.
|
||||
function MISSION:Pending()
|
||||
self:F()
|
||||
self.MissionStatus = "PENDING"
|
||||
self:StatusToClients()
|
||||
end
|
||||
|
||||
--- Returns if a Mission has failed.
|
||||
-- treturn bool
|
||||
function MISSION:IsFailed()
|
||||
self:F()
|
||||
return self.MissionStatus == "FAILED"
|
||||
end
|
||||
|
||||
--- Set a Mission to failed.
|
||||
function MISSION:Failed()
|
||||
self:F()
|
||||
self.MissionStatus = "FAILED"
|
||||
self:StatusToClients()
|
||||
end
|
||||
|
||||
--- Send the status of the MISSION to all Clients.
|
||||
function MISSION:StatusToClients()
|
||||
self:F()
|
||||
if self.MissionReportFlash then
|
||||
for ClientID, Client in pairs( self._Clients ) do
|
||||
Client:Message( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. '! ( ' .. self.MissionPriority .. ' mission ) ', 10, self.Name .. '/Status', "Mission Command: Mission Status")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the reporting. After certain time intervals, a MISSION report MESSAGE will be shown to All Players.
|
||||
function MISSION:ReportTrigger()
|
||||
self:F()
|
||||
|
||||
if self.MissionReportShow == true then
|
||||
self.MissionReportShow = false
|
||||
return true
|
||||
else
|
||||
if self.MissionReportFlash == true then
|
||||
if timer.getTime() >= self.MissionReportTrigger then
|
||||
self.MissionReportTrigger = timer.getTime() + self.MissionTimeInterval
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Report the status of all MISSIONs to all active Clients.
|
||||
function MISSION:ReportToAll()
|
||||
self:F()
|
||||
|
||||
local AlivePlayers = ''
|
||||
for ClientID, Client in pairs( self._Clients ) do
|
||||
if Client:GetDCSGroup() then
|
||||
if Client:GetClientGroupDCSUnit() then
|
||||
if Client:GetClientGroupDCSUnit():getLife() > 0.0 then
|
||||
if AlivePlayers == '' then
|
||||
AlivePlayers = ' Players: ' .. Client:GetClientGroupDCSUnit():getPlayerName()
|
||||
else
|
||||
AlivePlayers = AlivePlayers .. ' / ' .. Client:GetClientGroupDCSUnit():getPlayerName()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local Tasks = self:GetTasks()
|
||||
local TaskText = ""
|
||||
for TaskID, TaskData in pairs( Tasks ) do
|
||||
TaskText = TaskText .. " - Task " .. TaskID .. ": " .. TaskData.Name .. ": " .. TaskData:GetGoalProgress() .. "\n"
|
||||
end
|
||||
MESSAGE:New( self.MissionCoalition .. ' "' .. self.Name .. '": ' .. self.MissionStatus .. ' ( ' .. self.MissionPriority .. ' mission )' .. AlivePlayers .. "\n" .. TaskText:gsub("\n$",""), "Mission Command: Mission Report", 10, self.Name .. '/Status'):ToAll()
|
||||
end
|
||||
|
||||
|
||||
--- Add a goal function to a MISSION. Goal functions are called when a @{TASK} within a mission has been completed.
|
||||
-- @param function GoalFunction is the function defined by the mission designer to evaluate whether a certain goal has been reached after a @{TASK} finishes within the @{MISSION}. A GoalFunction must accept 2 parameters: Mission, Client, which contains the current MISSION object and the current CLIENT object respectively.
|
||||
-- @usage
|
||||
-- PatriotActivation = {
|
||||
-- { "US SAM Patriot Zerti", false },
|
||||
-- { "US SAM Patriot Zegduleti", false },
|
||||
-- { "US SAM Patriot Gvleti", false }
|
||||
-- }
|
||||
--
|
||||
-- function DeployPatriotTroopsGoal( Mission, Client )
|
||||
--
|
||||
--
|
||||
-- -- Check if the cargo is all deployed for mission success.
|
||||
-- for CargoID, CargoData in pairs( Mission._Cargos ) do
|
||||
-- if Group.getByName( CargoData.CargoGroupName ) then
|
||||
-- CargoGroup = Group.getByName( CargoData.CargoGroupName )
|
||||
-- if CargoGroup then
|
||||
-- -- Check if the cargo is ready to activate
|
||||
-- CurrentLandingZoneID = routines.IsUnitInZones( CargoGroup:getUnits()[1], Mission:GetTask( 2 ).LandingZones ) -- The second task is the Deploytask to measure mission success upon
|
||||
-- if CurrentLandingZoneID then
|
||||
-- if PatriotActivation[CurrentLandingZoneID][2] == false then
|
||||
-- -- Now check if this is a new Mission Task to be completed...
|
||||
-- trigger.action.setGroupAIOn( Group.getByName( PatriotActivation[CurrentLandingZoneID][1] ) )
|
||||
-- PatriotActivation[CurrentLandingZoneID][2] = true
|
||||
-- MessageToBlue( "Mission Command: Message to all airborne units! The " .. PatriotActivation[CurrentLandingZoneID][1] .. " is armed. Our air defenses are now stronger.", 60, "BLUE/PatriotDefense" )
|
||||
-- MessageToRed( "Mission Command: Our satellite systems are detecting additional NATO air defenses. To all airborne units: Take care!!!", 60, "RED/PatriotDefense" )
|
||||
-- Mission:GetTask( 2 ):AddGoalCompletion( "Patriots activated", PatriotActivation[CurrentLandingZoneID][1], 1 ) -- Register Patriot activation as part of mission goal.
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- local Mission = MISSIONSCHEDULER.AddMission( 'NATO Transport Troops', 'Operational', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.', 'NATO' )
|
||||
-- Mission:AddGoalFunction( DeployPatriotTroopsGoal )
|
||||
function MISSION:AddGoalFunction( GoalFunction )
|
||||
self:F()
|
||||
self.GoalFunction = GoalFunction
|
||||
end
|
||||
|
||||
--- Register a new @{CLIENT} to participate within the mission.
|
||||
-- @param CLIENT Client is the @{CLIENT} object. The object must have been instantiated with @{CLIENT:New}.
|
||||
-- @return CLIENT
|
||||
-- @usage
|
||||
-- Add a number of Client objects to the Mission.
|
||||
-- Mission:AddClient( CLIENT:New( 'US UH-1H*HOT-Deploy Troops 1', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:New( 'US UH-1H*RAMP-Deploy Troops 3', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:New( 'US UH-1H*HOT-Deploy Troops 2', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
|
||||
-- Mission:AddClient( CLIENT:New( 'US UH-1H*RAMP-Deploy Troops 4', 'Transport 3 groups of air defense engineers from our barracks "Gold" and "Titan" to each patriot battery control center to activate our air defenses.' ):Transport() )
|
||||
function MISSION:AddClient( Client )
|
||||
self:F( { Client } )
|
||||
|
||||
local Valid = true
|
||||
|
||||
if Valid then
|
||||
self._Clients[Client.ClientName] = Client
|
||||
end
|
||||
|
||||
return Client
|
||||
end
|
||||
|
||||
--- Find a @{CLIENT} object within the @{MISSION} by its ClientName.
|
||||
-- @param CLIENT ClientName is a string defining the Client Group as defined within the ME.
|
||||
-- @return CLIENT
|
||||
-- @usage
|
||||
-- -- Seach for Client "Bomber" within the Mission.
|
||||
-- local BomberClient = Mission:FindClient( "Bomber" )
|
||||
function MISSION:FindClient( ClientName )
|
||||
self:F( { self._Clients[ClientName] } )
|
||||
return self._Clients[ClientName]
|
||||
end
|
||||
|
||||
|
||||
--- Register a @{TASK} to be completed within the @{MISSION}. Note that there can be multiple @{TASK}s registered to be completed. Each TASK can be set a certain Goal. The MISSION will not be completed until all Goals are reached.
|
||||
-- @param TASK Task is the @{TASK} object. The object must have been instantiated with @{TASK:New} or any of its inherited @{TASK}s.
|
||||
-- @param number TaskNumber is the sequence number of the TASK within the MISSION. This number does have to be chronological.
|
||||
-- @return TASK
|
||||
-- @usage
|
||||
-- -- Define a few tasks for the Mission.
|
||||
-- PickupZones = { "NATO Gold Pickup Zone", "NATO Titan Pickup Zone" }
|
||||
-- PickupSignalUnits = { "NATO Gold Coordination Center", "NATO Titan Coordination Center" }
|
||||
--
|
||||
-- -- Assign the Pickup Task
|
||||
-- local PickupTask = PICKUPTASK:New( PickupZones, CARGO_TYPE.ENGINEERS, CLIENT.ONBOARDSIDE.LEFT )
|
||||
-- PickupTask:AddSmokeBlue( PickupSignalUnits )
|
||||
-- PickupTask:SetGoalTotal( 3 )
|
||||
-- Mission:AddTask( PickupTask, 1 )
|
||||
--
|
||||
-- -- Assign the Deploy Task
|
||||
-- local PatriotActivationZones = { "US Patriot Battery 1 Activation", "US Patriot Battery 2 Activation", "US Patriot Battery 3 Activation" }
|
||||
-- local PatriotActivationZonesSmokeUnits = { "US SAM Patriot - Battery 1 Control", "US SAM Patriot - Battery 2 Control", "US SAM Patriot - Battery 3 Control" }
|
||||
-- local DeployTask = DEPLOYTASK:New( PatriotActivationZones, CARGO_TYPE.ENGINEERS )
|
||||
-- --DeployTask:SetCargoTargetZoneName( 'US Troops Attack ' .. math.random(2) )
|
||||
-- DeployTask:AddSmokeBlue( PatriotActivationZonesSmokeUnits )
|
||||
-- DeployTask:SetGoalTotal( 3 )
|
||||
-- DeployTask:SetGoalTotal( 3, "Patriots activated" )
|
||||
-- Mission:AddTask( DeployTask, 2 )
|
||||
|
||||
function MISSION:AddTask( Task, TaskNumber )
|
||||
self:F()
|
||||
|
||||
self._Tasks[TaskNumber] = Task
|
||||
self._Tasks[TaskNumber]:EnableEvents()
|
||||
self._Tasks[TaskNumber].ID = TaskNumber
|
||||
|
||||
return Task
|
||||
end
|
||||
|
||||
--- Get the TASK idenified by the TaskNumber from the Mission. This function is useful in GoalFunctions.
|
||||
-- @param number TaskNumber is the number of the @{TASK} within the @{MISSION}.
|
||||
-- @return TASK
|
||||
-- @usage
|
||||
-- -- Get Task 2 from the Mission.
|
||||
-- Task2 = Mission:GetTask( 2 )
|
||||
|
||||
function MISSION:GetTask( TaskNumber )
|
||||
self:F()
|
||||
|
||||
local Valid = true
|
||||
|
||||
local Task = nil
|
||||
|
||||
if type(TaskNumber) ~= "number" then
|
||||
Valid = false
|
||||
end
|
||||
|
||||
if Valid then
|
||||
Task = self._Tasks[TaskNumber]
|
||||
end
|
||||
|
||||
return Task
|
||||
end
|
||||
|
||||
--- Get all the TASKs from the Mission. This function is useful in GoalFunctions.
|
||||
-- @return {TASK,...} Structure of TASKS with the @{TASK} number as the key.
|
||||
-- @usage
|
||||
-- -- Get Tasks from the Mission.
|
||||
-- Tasks = Mission:GetTasks()
|
||||
-- env.info( "Task 2 Completion = " .. Tasks[2]:GetGoalPercentage() .. "%" )
|
||||
function MISSION:GetTasks()
|
||||
self:F()
|
||||
|
||||
return self._Tasks
|
||||
end
|
||||
|
||||
|
||||
--[[
|
||||
_TransportExecuteStage: Defines the different stages of Transport unload/load execution. This table is internal and is used to control the validity of Transport load/unload timing.
|
||||
|
||||
- _TransportExecuteStage.EXECUTING
|
||||
- _TransportExecuteStage.SUCCESS
|
||||
- _TransportExecuteStage.FAILED
|
||||
|
||||
--]]
|
||||
_TransportExecuteStage = {
|
||||
NONE = 0,
|
||||
EXECUTING = 1,
|
||||
SUCCESS = 2,
|
||||
FAILED = 3
|
||||
}
|
||||
|
||||
|
||||
--- The MISSIONSCHEDULER is an OBJECT and is the main scheduler of ALL active MISSIONs registered within this scheduler. It's workings are considered internal and is automatically created when the Mission.lua file is included.
|
||||
-- @type MISSIONSCHEDULER
|
||||
MISSIONSCHEDULER = {
|
||||
Missions = {},
|
||||
MissionCount = 0,
|
||||
TimeIntervalCount = 0,
|
||||
TimeIntervalShow = 150,
|
||||
TimeSeconds = 14400,
|
||||
TimeShow = 5
|
||||
}
|
||||
|
||||
--- This is the main MISSIONSCHEDULER Scheduler function. It is considered internal and is automatically created when the Mission.lua file is included.
|
||||
function MISSIONSCHEDULER.Scheduler()
|
||||
|
||||
-- loop through the missions in the TransportTasks
|
||||
for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do
|
||||
|
||||
if not Mission:IsCompleted() then
|
||||
|
||||
-- This flag will monitor if for this mission, there are clients alive. If this flag is still false at the end of the loop, the mission status will be set to Pending (if not Failed or Completed).
|
||||
local ClientsAlive = false
|
||||
|
||||
for ClientID, Client in pairs( Mission._Clients ) do
|
||||
|
||||
if Client:GetDCSGroup() then
|
||||
|
||||
-- There is at least one Client that is alive... So the Mission status is set to Ongoing.
|
||||
ClientsAlive = true
|
||||
|
||||
-- If this Client was not registered as Alive before:
|
||||
-- 1. We register the Client as Alive.
|
||||
-- 2. We initialize the Client Tasks and make a link to the original Mission Task.
|
||||
-- 3. We initialize the Cargos.
|
||||
-- 4. We flag the Mission as Ongoing.
|
||||
if not Client.ClientAlive then
|
||||
Client.ClientAlive = true
|
||||
Client.ClientBriefingShown = false
|
||||
for TaskNumber, Task in pairs( Mission._Tasks ) do
|
||||
-- Note that this a deepCopy. Each client must have their own Tasks with own Stages!!!
|
||||
Client._Tasks[TaskNumber] = routines.utils.deepCopy( Mission._Tasks[TaskNumber] )
|
||||
-- Each MissionTask must point to the original Mission.
|
||||
Client._Tasks[TaskNumber].MissionTask = Mission._Tasks[TaskNumber]
|
||||
Client._Tasks[TaskNumber].Cargos = Mission._Tasks[TaskNumber].Cargos
|
||||
Client._Tasks[TaskNumber].LandingZones = Mission._Tasks[TaskNumber].LandingZones
|
||||
end
|
||||
|
||||
Mission:Ongoing()
|
||||
end
|
||||
|
||||
|
||||
-- For each Client, check for each Task the state and evolve the mission.
|
||||
-- This flag will indicate if the Task of the Client is Complete.
|
||||
TaskComplete = false
|
||||
|
||||
for TaskNumber, Task in pairs( Client._Tasks ) do
|
||||
|
||||
if not Task.Stage then
|
||||
Task:SetStage( 1 )
|
||||
end
|
||||
|
||||
|
||||
local TransportTime = timer.getTime()
|
||||
|
||||
if not Task:IsDone() then
|
||||
|
||||
if Task:Goal() then
|
||||
Task:ShowGoalProgress( Mission, Client )
|
||||
end
|
||||
|
||||
--env.info( 'Scheduler: Mission = ' .. Mission.Name .. ' / Client = ' .. Client.ClientName .. ' / Task = ' .. Task.Name .. ' / Stage = ' .. Task.ActiveStage .. ' - ' .. Task.Stage.Name .. ' - ' .. Task.Stage.StageType )
|
||||
|
||||
-- Action
|
||||
if Task:StageExecute() then
|
||||
Task.Stage:Execute( Mission, Client, Task )
|
||||
end
|
||||
|
||||
-- Wait until execution is finished
|
||||
if Task.ExecuteStage == _TransportExecuteStage.EXECUTING then
|
||||
Task.Stage:Executing( Mission, Client, Task )
|
||||
end
|
||||
|
||||
-- Validate completion or reverse to earlier stage
|
||||
if Task.Time + Task.Stage.WaitTime <= TransportTime then
|
||||
Task:SetStage( Task.Stage:Validate( Mission, Client, Task ) )
|
||||
end
|
||||
|
||||
if Task:IsDone() then
|
||||
--env.info( 'Scheduler: Mission '.. Mission.Name .. ' Task ' .. Task.Name .. ' Stage ' .. Task.Stage.Name .. ' done. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) )
|
||||
TaskComplete = true -- when a task is not yet completed, a mission cannot be completed
|
||||
|
||||
else
|
||||
-- break only if this task is not yet done, so that future task are not yet activated.
|
||||
TaskComplete = false -- when a task is not yet completed, a mission cannot be completed
|
||||
--env.info( 'Scheduler: Mission "'.. Mission.Name .. '" Task "' .. Task.Name .. '" Stage "' .. Task.Stage.Name .. '" break. TaskComplete = ' .. string.format ( "%s", TaskComplete and "true" or "false" ) )
|
||||
break
|
||||
end
|
||||
|
||||
if TaskComplete then
|
||||
|
||||
if Mission.GoalFunction ~= nil then
|
||||
Mission.GoalFunction( Mission, Client )
|
||||
end
|
||||
if MISSIONSCHEDULER.Scoring then
|
||||
MISSIONSCHEDULER.Scoring:_AddMissionTaskScore( Client:GetClientGroupDCSUnit(), Mission.Name, 25 )
|
||||
end
|
||||
|
||||
-- if not Mission:IsCompleted() then
|
||||
-- end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local MissionComplete = true
|
||||
for TaskNumber, Task in pairs( Mission._Tasks ) do
|
||||
if Task:Goal() then
|
||||
-- Task:ShowGoalProgress( Mission, Client )
|
||||
if Task:IsGoalReached() then
|
||||
else
|
||||
MissionComplete = false
|
||||
end
|
||||
else
|
||||
MissionComplete = false -- If there is no goal, the mission should never be ended. The goal status will be set somewhere else.
|
||||
end
|
||||
end
|
||||
|
||||
if MissionComplete then
|
||||
Mission:Completed()
|
||||
if MISSIONSCHEDULER.Scoring then
|
||||
MISSIONSCHEDULER.Scoring:_AddMissionScore( Mission.Name, 100 )
|
||||
end
|
||||
else
|
||||
if TaskComplete then
|
||||
-- Reset for new tasking of active client
|
||||
Client.ClientAlive = false -- Reset the client tasks.
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
else
|
||||
if Client.ClientAlive then
|
||||
env.info( 'Scheduler: Client "' .. Client.ClientName .. '" is inactive.' )
|
||||
Client.ClientAlive = false
|
||||
|
||||
-- This is tricky. If we sanitize Client._Tasks before sanitizing Client._Tasks[TaskNumber].MissionTask, then the original MissionTask will be sanitized, and will be lost within the garbage collector.
|
||||
-- So first sanitize Client._Tasks[TaskNumber].MissionTask, after that, sanitize only the whole _Tasks structure...
|
||||
--Client._Tasks[TaskNumber].MissionTask = nil
|
||||
--Client._Tasks = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- If all Clients of this Mission are not activated, then the Mission status needs to be put back into Pending status.
|
||||
-- But only if the Mission was Ongoing. In case the Mission is Completed or Failed, the Mission status may not be changed. In these cases, this will be the last run of this Mission in the Scheduler.
|
||||
if ClientsAlive == false then
|
||||
if Mission:IsOngoing() then
|
||||
-- Mission status back to pending...
|
||||
Mission:Pending()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Mission:StatusToClients()
|
||||
|
||||
if Mission:ReportTrigger() then
|
||||
Mission:ReportToAll()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Start the MISSIONSCHEDULER.
|
||||
function MISSIONSCHEDULER.Start()
|
||||
if MISSIONSCHEDULER ~= nil then
|
||||
MISSIONSCHEDULER.SchedulerId = routines.scheduleFunction( MISSIONSCHEDULER.Scheduler, { }, 0, 2 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Stop the MISSIONSCHEDULER.
|
||||
function MISSIONSCHEDULER.Stop()
|
||||
if MISSIONSCHEDULER.SchedulerId then
|
||||
routines.removeFunction(MISSIONSCHEDULER.SchedulerId)
|
||||
MISSIONSCHEDULER.SchedulerId = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- This is the main MISSION declaration method. Each Mission is like the master or a Mission orchestration between, Clients, Tasks, Stages etc.
|
||||
-- @param Mission is the MISSION object instantiated by @{MISSION:New}.
|
||||
-- @return MISSION
|
||||
-- @usage
|
||||
-- -- Declare a mission.
|
||||
-- Mission = MISSION:New( 'Russia Transport Troops SA-6',
|
||||
-- 'Operational',
|
||||
-- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.',
|
||||
-- 'Russia' )
|
||||
-- MISSIONSCHEDULER:AddMission( Mission )
|
||||
function MISSIONSCHEDULER.AddMission( Mission )
|
||||
MISSIONSCHEDULER.Missions[Mission.Name] = Mission
|
||||
MISSIONSCHEDULER.MissionCount = MISSIONSCHEDULER.MissionCount + 1
|
||||
-- Add an overall AI Client for the AI tasks... This AI Client will facilitate the Events in the background for each Task.
|
||||
--MissionAdd:AddClient( CLIENT:New( 'AI' ) )
|
||||
|
||||
return Mission
|
||||
end
|
||||
|
||||
--- Remove a MISSION from the MISSIONSCHEDULER.
|
||||
-- @param MissionName is the name of the MISSION given at declaration using @{AddMission}.
|
||||
-- @usage
|
||||
-- -- Declare a mission.
|
||||
-- Mission = MISSION:New( 'Russia Transport Troops SA-6',
|
||||
-- 'Operational',
|
||||
-- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.',
|
||||
-- 'Russia' )
|
||||
-- MISSIONSCHEDULER:AddMission( Mission )
|
||||
--
|
||||
-- -- Now remove the Mission.
|
||||
-- MISSIONSCHEDULER:RemoveMission( 'Russia Transport Troops SA-6' )
|
||||
function MISSIONSCHEDULER.RemoveMission( MissionName )
|
||||
MISSIONSCHEDULER.Missions[MissionName] = nil
|
||||
MISSIONSCHEDULER.MissionCount = MISSIONSCHEDULER.MissionCount - 1
|
||||
end
|
||||
|
||||
--- Find a MISSION within the MISSIONSCHEDULER.
|
||||
-- @param MissionName is the name of the MISSION given at declaration using @{AddMission}.
|
||||
-- @return MISSION
|
||||
-- @usage
|
||||
-- -- Declare a mission.
|
||||
-- Mission = MISSION:New( 'Russia Transport Troops SA-6',
|
||||
-- 'Operational',
|
||||
-- 'Transport troops from the control center to one of the SA-6 SAM sites to activate their operation.',
|
||||
-- 'Russia' )
|
||||
-- MISSIONSCHEDULER:AddMission( Mission )
|
||||
--
|
||||
-- -- Now find the Mission.
|
||||
-- MissionFind = MISSIONSCHEDULER:FindMission( 'Russia Transport Troops SA-6' )
|
||||
function MISSIONSCHEDULER.FindMission( MissionName )
|
||||
return MISSIONSCHEDULER.Missions[MissionName]
|
||||
end
|
||||
|
||||
-- Internal function used by the MISSIONSCHEDULER menu.
|
||||
function MISSIONSCHEDULER.ReportMissionsShow( )
|
||||
for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do
|
||||
Mission.MissionReportShow = true
|
||||
Mission.MissionReportFlash = false
|
||||
end
|
||||
end
|
||||
|
||||
-- Internal function used by the MISSIONSCHEDULER menu.
|
||||
function MISSIONSCHEDULER.ReportMissionsFlash( TimeInterval )
|
||||
local Count = 0
|
||||
for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do
|
||||
Mission.MissionReportShow = false
|
||||
Mission.MissionReportFlash = true
|
||||
Mission.MissionReportTrigger = timer.getTime() + Count * TimeInterval
|
||||
Mission.MissionTimeInterval = MISSIONSCHEDULER.MissionCount * TimeInterval
|
||||
env.info( "TimeInterval = " .. Mission.MissionTimeInterval )
|
||||
Count = Count + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Internal function used by the MISSIONSCHEDULER menu.
|
||||
function MISSIONSCHEDULER.ReportMissionsHide( Prm )
|
||||
for MissionName, Mission in pairs( MISSIONSCHEDULER.Missions ) do
|
||||
Mission.MissionReportShow = false
|
||||
Mission.MissionReportFlash = false
|
||||
end
|
||||
end
|
||||
|
||||
--- Enables a MENU option in the communications menu under F10 to control the status of the active missions.
|
||||
-- This function should be called only once when starting the MISSIONSCHEDULER.
|
||||
function MISSIONSCHEDULER.ReportMenu()
|
||||
local ReportMenu = SUBMENU:New( 'Status' )
|
||||
local ReportMenuShow = COMMANDMENU:New( 'Show Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsShow, 0 )
|
||||
local ReportMenuFlash = COMMANDMENU:New('Flash Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsFlash, 120 )
|
||||
local ReportMenuHide = COMMANDMENU:New( 'Hide Report Missions', ReportMenu, MISSIONSCHEDULER.ReportMissionsHide, 0 )
|
||||
end
|
||||
|
||||
--- Show the remaining mission time.
|
||||
function MISSIONSCHEDULER:TimeShow()
|
||||
self.TimeIntervalCount = self.TimeIntervalCount + 1
|
||||
if self.TimeIntervalCount >= self.TimeTriggerShow then
|
||||
local TimeMsg = string.format("%00d", ( self.TimeSeconds / 60 ) - ( timer.getTime() / 60 )) .. ' minutes left until mission reload.'
|
||||
MESSAGE:New( TimeMsg, "Mission time", self.TimeShow, '/TimeMsg' ):ToAll()
|
||||
self.TimeIntervalCount = 0
|
||||
end
|
||||
end
|
||||
|
||||
function MISSIONSCHEDULER:Time( TimeSeconds, TimeIntervalShow, TimeShow )
|
||||
|
||||
self.TimeIntervalCount = 0
|
||||
self.TimeSeconds = TimeSeconds
|
||||
self.TimeIntervalShow = TimeIntervalShow
|
||||
self.TimeShow = TimeShow
|
||||
end
|
||||
|
||||
--- Adds a mission scoring to the game.
|
||||
function MISSIONSCHEDULER:Scoring( Scoring )
|
||||
|
||||
self.Scoring = Scoring
|
||||
end
|
||||
|
||||
15
Moose Development/Moose/Moose.lua
Normal file
15
Moose Development/Moose/Moose.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
--- The main include file for the MOOSE system.
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Database" )
|
||||
Include.File( "Event" )
|
||||
|
||||
-- The order of the declarations is important here. Don't touch it.
|
||||
|
||||
--- Declare the event dispatcher based on the EVENT class
|
||||
_EVENTDISPATCHER = EVENT:New() -- #EVENT
|
||||
|
||||
--- Declare the main database object, which is used internally by the MOOSE classes.
|
||||
_DATABASE = DATABASE:New():ScanEnvironment() -- Database#DATABASE
|
||||
|
||||
128
Moose Development/Moose/Movement.lua
Normal file
128
Moose Development/Moose/Movement.lua
Normal file
@@ -0,0 +1,128 @@
|
||||
--- Limit the simultaneous movement of Groups within a running Mission.
|
||||
-- This module is defined to improve the performance in missions, and to bring additional realism for GROUND vehicles.
|
||||
-- Performance: If in a DCSRTE there are a lot of moving GROUND units, then in a multi player mission, this WILL create lag if
|
||||
-- the main DCS execution core of your CPU is fully utilized. So, this class will limit the amount of simultaneous moving GROUND units
|
||||
-- on defined intervals (currently every minute).
|
||||
-- @module MOVEMENT
|
||||
|
||||
Include.File( "Routines" )
|
||||
|
||||
--- the MOVEMENT class
|
||||
-- @type
|
||||
MOVEMENT = {
|
||||
ClassName = "MOVEMENT",
|
||||
}
|
||||
|
||||
--- Creates the main object which is handling the GROUND forces movement.
|
||||
-- @param table{string,...}|string MovePrefixes is a table of the Prefixes (names) of the GROUND Groups that need to be controlled by the MOVEMENT Object.
|
||||
-- @param number MoveMaximum is a number that defines the maximum amount of GROUND Units to be moving during one minute.
|
||||
-- @return MOVEMENT
|
||||
-- @usage
|
||||
-- -- Limit the amount of simultaneous moving units on the ground to prevent lag.
|
||||
-- Movement_US_Platoons = MOVEMENT:New( { 'US Tank Platoon Left', 'US Tank Platoon Middle', 'US Tank Platoon Right', 'US CH-47D Troops' }, 15 )
|
||||
|
||||
function MOVEMENT:New( MovePrefixes, MoveMaximum )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MovePrefixes, MoveMaximum } )
|
||||
|
||||
if type( MovePrefixes ) == 'table' then
|
||||
self.MovePrefixes = MovePrefixes
|
||||
else
|
||||
self.MovePrefixes = { MovePrefixes }
|
||||
end
|
||||
self.MoveCount = 0 -- The internal counter of the amount of Moveing the has happened since MoveStart.
|
||||
self.MoveMaximum = MoveMaximum -- Contains the Maximum amount of units that are allowed to move...
|
||||
self.AliveUnits = 0 -- Contains the counter how many units are currently alive
|
||||
self.MoveUnits = {} -- Reflects if the Moving for this MovePrefixes is going to be scheduled or not.
|
||||
|
||||
_EVENTDISPATCHER:OnBirth( self.OnBirth, self )
|
||||
|
||||
-- self:AddEvent( world.event.S_EVENT_BIRTH, self.OnBirth )
|
||||
--
|
||||
-- self:EnableEvents()
|
||||
|
||||
self:ScheduleStart()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Call this function to start the MOVEMENT scheduling.
|
||||
function MOVEMENT:ScheduleStart()
|
||||
self:F()
|
||||
self.MoveFunction = routines.scheduleFunction( self._Scheduler, { self }, timer.getTime() + 1, 120 )
|
||||
end
|
||||
|
||||
--- Call this function to stop the MOVEMENT scheduling.
|
||||
-- @todo need to implement it ... Forgot.
|
||||
function MOVEMENT:ScheduleStop()
|
||||
self:F()
|
||||
|
||||
end
|
||||
|
||||
--- Captures the birth events when new Units were spawned.
|
||||
-- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration.
|
||||
function MOVEMENT:OnBirth( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if timer.getTime0() < timer.getAbsTime() then -- dont need to add units spawned in at the start of the mission if mist is loaded in init line
|
||||
if Event.IniDCSUnit then
|
||||
self:T( "Birth object : " .. Event.IniDCSUnitName )
|
||||
if Event.IniDCSGroup and Event.IniDCSGroup:isExist() then
|
||||
for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do
|
||||
if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then
|
||||
self.AliveUnits = self.AliveUnits + 1
|
||||
self.MoveUnits[Event.IniDCSUnitName] = Event.IniDCSGroupName
|
||||
self:T( self.AliveUnits )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
_EVENTDISPATCHER:OnCrashForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self )
|
||||
_EVENTDISPATCHER:OnDeadForUnit( Event.IniDCSUnitName, self.OnDeadOrCrash, self )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Captures the Dead or Crash events when Units crash or are destroyed.
|
||||
-- @todo This method should become obsolete. The new @{DATABASE} class will handle the collection administration.
|
||||
function MOVEMENT:OnDeadOrCrash( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
self:T( "Dead object : " .. Event.IniDCSUnitName )
|
||||
for MovePrefixID, MovePrefix in pairs( self.MovePrefixes ) do
|
||||
if string.find( Event.IniDCSUnitName, MovePrefix, 1, true ) then
|
||||
self.AliveUnits = self.AliveUnits - 1
|
||||
self.MoveUnits[Event.IniDCSUnitName] = nil
|
||||
self:T( self.AliveUnits )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- This function is called automatically by the MOVEMENT scheduler. A new function is scheduled when MoveScheduled is true.
|
||||
function MOVEMENT:_Scheduler()
|
||||
self:F( { self.MovePrefixes, self.MoveMaximum, self.AliveUnits, self.MovementGroups } )
|
||||
|
||||
if self.AliveUnits > 0 then
|
||||
local MoveProbability = ( self.MoveMaximum * 100 ) / self.AliveUnits
|
||||
self:T( 'Move Probability = ' .. MoveProbability )
|
||||
|
||||
for MovementUnitName, MovementGroupName in pairs( self.MoveUnits ) do
|
||||
local MovementGroup = Group.getByName( MovementGroupName )
|
||||
if MovementGroup and MovementGroup:isExist() then
|
||||
local MoveOrStop = math.random( 1, 100 )
|
||||
self:T( 'MoveOrStop = ' .. MoveOrStop )
|
||||
if MoveOrStop <= MoveProbability then
|
||||
self:T( 'Group continues moving = ' .. MovementGroupName )
|
||||
trigger.action.groupContinueMoving( MovementGroup )
|
||||
else
|
||||
self:T( 'Group stops moving = ' .. MovementGroupName )
|
||||
trigger.action.groupStopMoving( MovementGroup )
|
||||
end
|
||||
else
|
||||
self.MoveUnits[MovementUnitName] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
27
Moose Development/Moose/NoTask.lua
Normal file
27
Moose Development/Moose/NoTask.lua
Normal file
@@ -0,0 +1,27 @@
|
||||
--- A NOTASK is a dummy activity... But it will show a Mission Briefing...
|
||||
-- @module NOTASK
|
||||
|
||||
Include.File("Task")
|
||||
|
||||
--- The NOTASK class
|
||||
-- @type
|
||||
NOTASK = {
|
||||
ClassName = "NOTASK",
|
||||
}
|
||||
|
||||
--- Creates a new NOTASK.
|
||||
function NOTASK:New()
|
||||
local self = BASE:Inherit( self, TASK:New() )
|
||||
self:F()
|
||||
|
||||
local Valid = true
|
||||
|
||||
if Valid then
|
||||
self.Name = 'Nothing'
|
||||
self.TaskBriefing = "Task: Execute your mission."
|
||||
self.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEDONE:New() }
|
||||
self.SetStage( self, 1 )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
156
Moose Development/Moose/PickupTask.lua
Normal file
156
Moose Development/Moose/PickupTask.lua
Normal file
@@ -0,0 +1,156 @@
|
||||
--- A PICKUPTASK orchestrates the loading of CARGO at a specific landing zone.
|
||||
-- @module PICKUPTASK
|
||||
-- @parent TASK
|
||||
|
||||
Include.File("Task")
|
||||
Include.File("Cargo")
|
||||
|
||||
--- The PICKUPTASK class
|
||||
-- @type
|
||||
PICKUPTASK = {
|
||||
ClassName = "PICKUPTASK",
|
||||
TEXT = { "Pick-Up", "picked-up", "loaded" },
|
||||
GoalVerb = "Pick-Up"
|
||||
}
|
||||
|
||||
--- Creates a new PICKUPTASK.
|
||||
-- @param table{string,...}|string LandingZones Table of Zone names where Cargo is to be loaded.
|
||||
-- @param CARGO_TYPE CargoType Type of the Cargo. The type must be of the following Enumeration:..
|
||||
-- @param number OnBoardSide Reflects from which side the cargo Group will be on-boarded on the Carrier.
|
||||
function PICKUPTASK:New( CargoType, OnBoardSide )
|
||||
local self = BASE:Inherit( self, TASK:New() )
|
||||
self:F()
|
||||
|
||||
-- self holds the inherited instance of the PICKUPTASK Class to the BASE class.
|
||||
|
||||
local Valid = true
|
||||
|
||||
if Valid then
|
||||
self.Name = 'Pickup Cargo'
|
||||
self.TaskBriefing = "Task: Fly to the indicated landing zones and pickup " .. CargoType .. ". Your co-pilot will provide you with the directions (required flight angle in degrees) and the distance (in km) to the pickup zone."
|
||||
self.CargoType = CargoType
|
||||
self.GoalVerb = CargoType .. " " .. self.GoalVerb
|
||||
self.OnBoardSide = OnBoardSide
|
||||
self.IsLandingRequired = true -- required to decide whether the client needs to land or not
|
||||
self.IsSlingLoad = false -- Indicates whether the cargo is a sling load cargo
|
||||
self.Stages = { STAGE_CARGO_INIT:New(), STAGE_CARGO_LOAD:New(), STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGELANDING:New(), STAGELANDED:New(), STAGELOAD:New(), STAGEDONE:New() }
|
||||
self.SetStage( self, 1 )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function PICKUPTASK:FromZone( LandingZone )
|
||||
self:F()
|
||||
|
||||
self.LandingZones.LandingZoneNames[LandingZone.CargoZoneName] = LandingZone.CargoZoneName
|
||||
self.LandingZones.LandingZones[LandingZone.CargoZoneName] = LandingZone
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function PICKUPTASK:InitCargo( InitCargos )
|
||||
self:F( { InitCargos } )
|
||||
|
||||
if type( InitCargos ) == "table" then
|
||||
self.Cargos.InitCargos = InitCargos
|
||||
else
|
||||
self.Cargos.InitCargos = { InitCargos }
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function PICKUPTASK:LoadCargo( LoadCargos )
|
||||
self:F( { LoadCargos } )
|
||||
|
||||
if type( LoadCargos ) == "table" then
|
||||
self.Cargos.LoadCargos = LoadCargos
|
||||
else
|
||||
self.Cargos.LoadCargos = { LoadCargos }
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function PICKUPTASK:AddCargoMenus( Client, Cargos, TransportRadius )
|
||||
self:F()
|
||||
|
||||
for CargoID, Cargo in pairs( Cargos ) do
|
||||
|
||||
self:T( { Cargo.ClassName, Cargo.CargoName, Cargo.CargoType, Cargo:IsStatusNone(), Cargo:IsStatusLoaded(), Cargo:IsStatusLoading(), Cargo:IsStatusUnLoaded() } )
|
||||
|
||||
-- If the Cargo has no status, allow the menu option.
|
||||
if Cargo:IsStatusNone() or ( Cargo:IsStatusLoading() and Client == Cargo:IsLoadingToClient() ) then
|
||||
|
||||
local MenuAdd = false
|
||||
if Cargo:IsNear( Client, self.CurrentCargoZone ) then
|
||||
MenuAdd = true
|
||||
end
|
||||
|
||||
if MenuAdd then
|
||||
if Client._Menus[Cargo.CargoType] == nil then
|
||||
Client._Menus[Cargo.CargoType] = {}
|
||||
end
|
||||
|
||||
if not Client._Menus[Cargo.CargoType].PickupMenu then
|
||||
Client._Menus[Cargo.CargoType].PickupMenu = missionCommands.addSubMenuForGroup(
|
||||
Client:GetClientGroupID(),
|
||||
self.TEXT[1] .. " " .. Cargo.CargoType,
|
||||
nil
|
||||
)
|
||||
self:T( 'Added PickupMenu: ' .. self.TEXT[1] .. " " .. Cargo.CargoType )
|
||||
end
|
||||
|
||||
if Client._Menus[Cargo.CargoType].PickupSubMenus == nil then
|
||||
Client._Menus[Cargo.CargoType].PickupSubMenus = {}
|
||||
end
|
||||
|
||||
Client._Menus[Cargo.CargoType].PickupSubMenus[ #Client._Menus[Cargo.CargoType].PickupSubMenus + 1 ] = missionCommands.addCommandForGroup(
|
||||
Client:GetClientGroupID(),
|
||||
Cargo.CargoName .. " ( " .. Cargo.CargoWeight .. "kg )",
|
||||
Client._Menus[Cargo.CargoType].PickupMenu,
|
||||
self.MenuAction,
|
||||
{ ReferenceTask = self, CargoTask = Cargo }
|
||||
)
|
||||
self:T( 'Added PickupSubMenu' .. Cargo.CargoType .. ":" .. Cargo.CargoName .. " ( " .. Cargo.CargoWeight .. "kg )" )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function PICKUPTASK:RemoveCargoMenus( Client )
|
||||
self:F()
|
||||
|
||||
for MenuID, MenuData in pairs( Client._Menus ) do
|
||||
for SubMenuID, SubMenuData in pairs( MenuData.PickupSubMenus ) do
|
||||
missionCommands.removeItemForGroup( Client:GetClientGroupID(), SubMenuData )
|
||||
self:T( "Removed PickupSubMenu " )
|
||||
SubMenuData = nil
|
||||
end
|
||||
if MenuData.PickupMenu then
|
||||
missionCommands.removeItemForGroup( Client:GetClientGroupID(), MenuData.PickupMenu )
|
||||
self:T( "Removed PickupMenu " )
|
||||
MenuData.PickupMenu = nil
|
||||
end
|
||||
end
|
||||
|
||||
for CargoID, Cargo in pairs( CARGOS ) do
|
||||
self:T( { Cargo.ClassName, Cargo.CargoName, Cargo.CargoType, Cargo:IsStatusNone(), Cargo:IsStatusLoaded(), Cargo:IsStatusLoading(), Cargo:IsStatusUnLoaded() } )
|
||||
if Cargo:IsStatusLoading() and Client == Cargo:IsLoadingToClient() then
|
||||
Cargo:StatusNone()
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
function PICKUPTASK:HasFailed( ClientDead )
|
||||
self:F()
|
||||
|
||||
local TaskHasFailed = self.TaskFailed
|
||||
return TaskHasFailed
|
||||
end
|
||||
|
||||
41
Moose Development/Moose/RouteTask.lua
Normal file
41
Moose Development/Moose/RouteTask.lua
Normal file
@@ -0,0 +1,41 @@
|
||||
--- A ROUTETASK orchestrates the travel to a specific zone defined within the ME.
|
||||
-- @module ROUTETASK
|
||||
|
||||
--- The ROUTETASK class
|
||||
-- @type
|
||||
ROUTETASK = {
|
||||
ClassName = "ROUTETASK",
|
||||
GoalVerb = "Route",
|
||||
}
|
||||
|
||||
--- Creates a new ROUTETASK.
|
||||
-- @param table{sring,...}|string LandingZones Table of Zone Names where the target is located.
|
||||
-- @param string TaskBriefing (optional) Defines a text describing the briefing of the task.
|
||||
-- @return ROUTETASK
|
||||
function ROUTETASK:New( LandingZones, TaskBriefing )
|
||||
local self = BASE:Inherit( self, TASK:New() )
|
||||
self:F( { LandingZones, TaskBriefing } )
|
||||
|
||||
local Valid = true
|
||||
|
||||
Valid = routines.ValidateZone( LandingZones, "LandingZones", Valid )
|
||||
|
||||
if Valid then
|
||||
self.Name = 'Route To Zone'
|
||||
if TaskBriefing then
|
||||
self.TaskBriefing = TaskBriefing .. " Your co-pilot will provide you with the directions (required flight angle in degrees) and the distance (in km) to the target objective."
|
||||
else
|
||||
self.TaskBriefing = "Task: Fly to specified zone(s). Your co-pilot will provide you with the directions (required flight angle in degrees) and the distance (in km) to the target objective."
|
||||
end
|
||||
if type( LandingZones ) == "table" then
|
||||
self.LandingZones = LandingZones
|
||||
else
|
||||
self.LandingZones = { LandingZones }
|
||||
end
|
||||
self.Stages = { STAGEBRIEF:New(), STAGESTART:New(), STAGEROUTE:New(), STAGEARRIVE:New(), STAGEDONE:New() }
|
||||
self.SetStage( self, 1 )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
2620
Moose Development/Moose/Routines.lua
Normal file
2620
Moose Development/Moose/Routines.lua
Normal file
File diff suppressed because it is too large
Load Diff
114
Moose Development/Moose/Scheduler.lua
Normal file
114
Moose Development/Moose/Scheduler.lua
Normal file
@@ -0,0 +1,114 @@
|
||||
--- Models time events calling event handing functions.
|
||||
-- @module Scheduler
|
||||
-- @author FlightControl
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Cargo" )
|
||||
Include.File( "Message" )
|
||||
|
||||
|
||||
--- The SCHEDULER class
|
||||
-- @type SCHEDULER
|
||||
-- @extends Base#BASE
|
||||
SCHEDULER = {
|
||||
ClassName = "SCHEDULER",
|
||||
}
|
||||
|
||||
|
||||
--- SCHEDULER constructor.
|
||||
-- @param #SCHEDULER self
|
||||
-- @param #table TimeEventObject
|
||||
-- @param #function TimeEventFunction
|
||||
-- @param #table TimeEventFunctionArguments
|
||||
-- @param #number StartSeconds
|
||||
-- @param #number RepeatSecondsInterval
|
||||
-- @param #number RandomizationFactor
|
||||
-- @param #number StopSeconds
|
||||
-- @return #SCHEDULER
|
||||
function SCHEDULER:New( TimeEventObject, TimeEventFunction, TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { TimeEventObject, TimeEventFunction, TimeEventFunctionArguments, StartSeconds, RepeatSecondsInterval, RandomizationFactor, StopSeconds } )
|
||||
|
||||
self.TimeEventObject = TimeEventObject
|
||||
self.TimeEventFunction = TimeEventFunction
|
||||
self.TimeEventFunctionArguments = TimeEventFunctionArguments
|
||||
self.StartSeconds = StartSeconds
|
||||
|
||||
if RepeatSecondsInterval then
|
||||
self.RepeatSecondsInterval = RepeatSecondsInterval
|
||||
else
|
||||
self.RepeatSecondsInterval = 0
|
||||
end
|
||||
|
||||
if RandomizationFactor then
|
||||
self.RandomizationFactor = RandomizationFactor
|
||||
else
|
||||
self.RandomizationFactor = 0
|
||||
end
|
||||
|
||||
if StopSeconds then
|
||||
self.StopSeconds = StopSeconds
|
||||
end
|
||||
|
||||
self.Repeat = false
|
||||
|
||||
self.StartTime = timer.getTime()
|
||||
|
||||
self:Start()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function SCHEDULER:Scheduler()
|
||||
self:F( self.TimeEventFunctionArguments )
|
||||
|
||||
local ErrorHandler = function( errmsg )
|
||||
|
||||
env.info( "Error in SCHEDULER function:" .. errmsg )
|
||||
env.info( debug.traceback() )
|
||||
|
||||
return errmsg
|
||||
end
|
||||
|
||||
local Status, Result
|
||||
if self.TimeEventObject then
|
||||
Status, Result = xpcall( function() return self.TimeEventFunction( self.TimeEventObject, unpack( self.TimeEventFunctionArguments ) ) end, ErrorHandler )
|
||||
else
|
||||
Status, Result = xpcall( function() return self.TimeEventFunction( unpack( self.TimeEventFunctionArguments ) ) end, ErrorHandler )
|
||||
end
|
||||
|
||||
self:T( { Status, Result } )
|
||||
|
||||
if Status and Status == true and Result and Result == true then
|
||||
if self.Repeat and ( not self.StopSeconds or ( self.StopSeconds and timer.getTime() <= self.StartTime + self.StopSeconds ) ) then
|
||||
timer.scheduleFunction(
|
||||
self.Scheduler,
|
||||
self,
|
||||
timer.getTime() + self.RepeatSecondsInterval + math.random( - ( self.RandomizationFactor * self.RepeatSecondsInterval / 2 ), ( self.RandomizationFactor * self.RepeatSecondsInterval / 2 ) ) + 0.01
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function SCHEDULER:Start()
|
||||
self:F( self.TimeEventObject )
|
||||
|
||||
self.Repeat = true
|
||||
timer.scheduleFunction( self.Scheduler, self, timer.getTime() + self.StartSeconds + .01 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function SCHEDULER:Stop()
|
||||
self:F( self.TimeEventObject )
|
||||
|
||||
self.Repeat = false
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
815
Moose Development/Moose/Scoring.lua
Normal file
815
Moose Development/Moose/Scoring.lua
Normal file
@@ -0,0 +1,815 @@
|
||||
--- 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
|
||||
-- @author FlightControl
|
||||
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Menu" )
|
||||
Include.File( "Group" )
|
||||
Include.File( "Event" )
|
||||
|
||||
|
||||
--- The Scoring class
|
||||
-- @type SCORING
|
||||
-- @field Players A collection of the current players that have joined the game.
|
||||
-- @extends Base#BASE
|
||||
SCORING = {
|
||||
ClassName = "SCORING",
|
||||
ClassID = 0,
|
||||
Players = {},
|
||||
}
|
||||
|
||||
local _SCORINGCoalition =
|
||||
{
|
||||
[1] = "Red",
|
||||
[2] = "Blue",
|
||||
}
|
||||
|
||||
local _SCORINGCategory =
|
||||
{
|
||||
[Unit.Category.AIRPLANE] = "Plane",
|
||||
[Unit.Category.HELICOPTER] = "Helicopter",
|
||||
[Unit.Category.GROUND_UNIT] = "Vehicle",
|
||||
[Unit.Category.SHIP] = "Ship",
|
||||
[Unit.Category.STRUCTURE] = "Structure",
|
||||
}
|
||||
|
||||
--- Creates a new SCORING object to administer the scoring achieved by players.
|
||||
-- @param #SCORING self
|
||||
-- @param #string GameName The name of the game. This name is also logged in the CSV score file.
|
||||
-- @return #SCORING self
|
||||
-- @usage
|
||||
-- -- Define a new scoring object for the mission Gori Valley.
|
||||
-- ScoringObject = SCORING:New( "Gori Valley" )
|
||||
function SCORING:New( GameName )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
|
||||
if GameName then
|
||||
self.GameName = GameName
|
||||
else
|
||||
error( "A game name must be given to register the scoring results" )
|
||||
end
|
||||
|
||||
|
||||
_EVENTDISPATCHER:OnDead( self._EventOnDeadOrCrash, self )
|
||||
_EVENTDISPATCHER:OnCrash( self._EventOnDeadOrCrash, self )
|
||||
_EVENTDISPATCHER:OnHit( self._EventOnHit, self )
|
||||
|
||||
self.SchedulerId = routines.scheduleFunction( SCORING._FollowPlayersScheduled, { self }, 0, 5 )
|
||||
|
||||
self:ScoreMenu()
|
||||
|
||||
return self
|
||||
|
||||
end
|
||||
|
||||
--- Creates a score radio menu. Can be accessed using Radio -> F10.
|
||||
-- @param #SCORING self
|
||||
-- @return #SCORING self
|
||||
function SCORING:ScoreMenu()
|
||||
self.Menu = SUBMENU:New( 'Scoring' )
|
||||
self.AllScoresMenu = COMMANDMENU:New( 'Score All Active Players', self.Menu, SCORING.ReportScoreAll, self )
|
||||
--- = COMMANDMENU:New('Your Current Score', ReportScore, SCORING.ReportScorePlayer, self )
|
||||
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" )
|
||||
|
||||
local ClientUnit = 0
|
||||
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers(coalition.side.RED), AlivePlayersBlue = coalition.getPlayers(coalition.side.BLUE) }
|
||||
local unitId
|
||||
local unitData
|
||||
local AlivePlayerUnits = {}
|
||||
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
self:T3( { "_FollowPlayersScheduled", CoalitionData } )
|
||||
for UnitId, UnitData in pairs( CoalitionData ) do
|
||||
self:_AddPlayerFromUnit( UnitData )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Track DEAD or CRASH events for the scoring.
|
||||
-- @param #SCORING self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function SCORING:_EventOnDeadOrCrash( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
local TargetUnit = nil
|
||||
local TargetGroup = nil
|
||||
local TargetUnitName = ""
|
||||
local TargetGroupName = ""
|
||||
local TargetPlayerName = ""
|
||||
local TargetCoalition = nil
|
||||
local TargetCategory = nil
|
||||
local TargetType = nil
|
||||
local TargetUnitCoalition = nil
|
||||
local TargetUnitCategory = nil
|
||||
local TargetUnitType = nil
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
|
||||
TargetUnit = Event.IniDCSUnit
|
||||
TargetUnitName = Event.IniDCSUnitName
|
||||
TargetGroup = Event.IniDCSGroup
|
||||
TargetGroupName = Event.IniDCSGroupName
|
||||
TargetPlayerName = TargetUnit:getPlayerName()
|
||||
|
||||
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, "/PENALTY" .. PlayerName .. "/" .. InitUnitName ):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, "/SCORE" .. PlayerName .. "/" .. InitUnitName ):ToAll()
|
||||
self:ScoreCSV( PlayerName, "KILL_SCORE", 1, 10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Add a new player entering a Unit.
|
||||
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()
|
||||
local UnitCategory = UnitDesc.category
|
||||
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].Mission = {}
|
||||
|
||||
-- for CategoryID, CategoryName in pairs( SCORINGCategory ) do
|
||||
-- self.Players[PlayerName].Hit[CategoryID] = {}
|
||||
-- self.Players[PlayerName].Kill[CategoryID] = {}
|
||||
-- end
|
||||
self.Players[PlayerName].HitPlayers = {}
|
||||
self.Players[PlayerName].HitUnits = {}
|
||||
self.Players[PlayerName].Score = 0
|
||||
self.Players[PlayerName].Penalty = 0
|
||||
self.Players[PlayerName].PenaltyCoalition = 0
|
||||
self.Players[PlayerName].PenaltyWarning = 0
|
||||
end
|
||||
|
||||
if not self.Players[PlayerName].UnitCoalition then
|
||||
self.Players[PlayerName].UnitCoalition = UnitCoalition
|
||||
else
|
||||
if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then
|
||||
self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50
|
||||
self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1
|
||||
MESSAGE:New( "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] ..
|
||||
"(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.",
|
||||
"",
|
||||
2,
|
||||
"/PENALTYCOALITION" .. PlayerName
|
||||
):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() )
|
||||
end
|
||||
end
|
||||
self.Players[PlayerName].UnitName = UnitName
|
||||
self.Players[PlayerName].UnitCoalition = UnitCoalition
|
||||
self.Players[PlayerName].UnitCategory = UnitCategory
|
||||
self.Players[PlayerName].UnitType = UnitTypeName
|
||||
|
||||
if self.Players[PlayerName].Penalty > 100 then
|
||||
if self.Players[PlayerName].PenaltyWarning < 1 then
|
||||
MESSAGE:New( "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than 150, you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty,
|
||||
"",
|
||||
30,
|
||||
"/PENALTYCOALITION" .. PlayerName
|
||||
):ToAll()
|
||||
self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1
|
||||
end
|
||||
end
|
||||
|
||||
if self.Players[PlayerName].Penalty > 150 then
|
||||
ClientGroup = GROUP:NewFromDCSUnit( UnitData )
|
||||
ClientGroup:Destroy()
|
||||
MESSAGE:New( "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
|
||||
"",
|
||||
10,
|
||||
"/PENALTYCOALITION" .. PlayerName
|
||||
):ToAll()
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Registers Scores the players completing a Mission Task.
|
||||
function SCORING:_AddMissionTaskScore( PlayerUnit, MissionName, Score )
|
||||
self:F( { PlayerUnit, MissionName, Score } )
|
||||
|
||||
local PlayerName = PlayerUnit:getPlayerName()
|
||||
|
||||
if not self.Players[PlayerName].Mission[MissionName] then
|
||||
self.Players[PlayerName].Mission[MissionName] = {}
|
||||
self.Players[PlayerName].Mission[MissionName].ScoreTask = 0
|
||||
self.Players[PlayerName].Mission[MissionName].ScoreMission = 0
|
||||
end
|
||||
|
||||
self:T( PlayerName )
|
||||
self:T( self.Players[PlayerName].Mission[MissionName] )
|
||||
|
||||
self.Players[PlayerName].Score = self.Players[PlayerName].Score + Score
|
||||
self.Players[PlayerName].Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score
|
||||
|
||||
MESSAGE:New( "Player '" .. PlayerName .. "' has finished another Task in Mission '" .. MissionName .. "'. " ..
|
||||
Score .. " Score points added.",
|
||||
"", 20, "/SCORETASK" .. PlayerName ):ToAll()
|
||||
|
||||
self:ScoreCSV( PlayerName, "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:getName() )
|
||||
end
|
||||
|
||||
|
||||
--- Registers Mission Scores for possible multiple players that contributed in the Mission.
|
||||
function SCORING:_AddMissionScore( MissionName, Score )
|
||||
self:F( { MissionName, Score } )
|
||||
|
||||
for PlayerName, PlayerData in pairs( self.Players ) do
|
||||
|
||||
if PlayerData.Mission[MissionName] then
|
||||
PlayerData.Score = PlayerData.Score + Score
|
||||
PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score
|
||||
MESSAGE:New( "Player '" .. PlayerName .. "' has finished Mission '" .. MissionName .. "'. " ..
|
||||
Score .. " Score points added.",
|
||||
"", 20, "/SCOREMISSION" .. PlayerName ):ToAll()
|
||||
self:ScoreCSV( PlayerName, "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnHit event for the scoring.
|
||||
-- @param #SCORING self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function SCORING:_EventOnHit( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
local InitUnit = nil
|
||||
local InitUnitName = ""
|
||||
local InitGroup = nil
|
||||
local InitGroupName = ""
|
||||
local InitPlayerName = nil
|
||||
|
||||
local InitCoalition = nil
|
||||
local InitCategory = nil
|
||||
local InitType = nil
|
||||
local InitUnitCoalition = nil
|
||||
local InitUnitCategory = nil
|
||||
local InitUnitType = nil
|
||||
|
||||
local TargetUnit = nil
|
||||
local TargetUnitName = ""
|
||||
local TargetGroup = nil
|
||||
local TargetGroupName = ""
|
||||
local TargetPlayerName = ""
|
||||
|
||||
local TargetCoalition = nil
|
||||
local TargetCategory = nil
|
||||
local TargetType = nil
|
||||
local TargetUnitCoalition = nil
|
||||
local TargetUnitCategory = nil
|
||||
local TargetUnitType = nil
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
|
||||
InitUnit = Event.IniDCSUnit
|
||||
InitUnitName = Event.IniDCSUnitName
|
||||
InitGroup = Event.IniDCSGroup
|
||||
InitGroupName = Event.IniDCSGroupName
|
||||
InitPlayerName = InitUnit:getPlayerName()
|
||||
|
||||
InitCoalition = InitUnit:getCoalition()
|
||||
--TODO: Workaround Client DCS Bug
|
||||
--InitCategory = InitUnit:getCategory()
|
||||
InitCategory = InitUnit:getDesc().category
|
||||
InitType = InitUnit:getTypeName()
|
||||
|
||||
InitUnitCoalition = _SCORINGCoalition[InitCoalition]
|
||||
InitUnitCategory = _SCORINGCategory[InitCategory]
|
||||
InitUnitType = InitType
|
||||
|
||||
self:T( { InitUnitName, InitGroupName, InitPlayerName, InitCoalition, InitCategory, InitType , InitUnitCoalition, InitUnitCategory, InitUnitType } )
|
||||
end
|
||||
|
||||
|
||||
if Event.TgtDCSUnit then
|
||||
|
||||
TargetUnit = Event.TgtDCSUnit
|
||||
TargetUnitName = Event.TgtDCSUnitName
|
||||
TargetGroup = Event.TgtDCSGroup
|
||||
TargetGroupName = Event.TgtDCSGroupName
|
||||
TargetPlayerName = TargetUnit:getPlayerName()
|
||||
|
||||
TargetCoalition = TargetUnit:getCoalition()
|
||||
--TODO: Workaround Client DCS Bug
|
||||
--TargetCategory = TargetUnit:getCategory()
|
||||
TargetCategory = TargetUnit:getDesc().category
|
||||
TargetType = TargetUnit:getTypeName()
|
||||
|
||||
TargetUnitCoalition = _SCORINGCoalition[TargetCoalition]
|
||||
TargetUnitCategory = _SCORINGCategory[TargetCategory]
|
||||
TargetUnitType = TargetType
|
||||
|
||||
self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType, TargetUnitCoalition, TargetUnitCategory, TargetUnitType } )
|
||||
end
|
||||
|
||||
if InitPlayerName ~= nil then -- It is a player that is hitting something
|
||||
self:_AddPlayerFromUnit( InitUnit )
|
||||
if self.Players[InitPlayerName] then -- This should normally not happen, but i'll test it anyway.
|
||||
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
||||
self:_AddPlayerFromUnit( TargetUnit )
|
||||
self.Players[InitPlayerName].HitPlayers = self.Players[InitPlayerName].HitPlayers + 1
|
||||
end
|
||||
|
||||
self:T( "Hitting Something" )
|
||||
-- What is he hitting?
|
||||
if TargetCategory then
|
||||
if not self.Players[InitPlayerName].Hit[TargetCategory] then
|
||||
self.Players[InitPlayerName].Hit[TargetCategory] = {}
|
||||
end
|
||||
if not self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] then
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName] = {}
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = 0
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = 0
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = 0
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = 0
|
||||
end
|
||||
local Score = 0
|
||||
if InitCoalition == TargetCoalition then
|
||||
self.Players[InitPlayerName].Penalty = self.Players[InitPlayerName].Penalty + 10
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty + 10
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit + 1
|
||||
MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a friendly " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].PenaltyHit .. " times. Penalty: -" .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Penalty ..
|
||||
". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty,
|
||||
"",
|
||||
2,
|
||||
"/PENALTY" .. InitPlayerName .. "/" .. InitUnitName
|
||||
):ToAll()
|
||||
self:ScoreCSV( InitPlayerName, "HIT_PENALTY", 1, -25, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||
else
|
||||
self.Players[InitPlayerName].Score = self.Players[InitPlayerName].Score + 10
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score + 1
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit = self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit + 1
|
||||
MESSAGE:New( "Player '" .. InitPlayerName .. "' hit a target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||
self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].ScoreHit .. " times. Score: " .. self.Players[InitPlayerName].Hit[TargetCategory][TargetUnitName].Score ..
|
||||
". Score Total:" .. self.Players[InitPlayerName].Score - self.Players[InitPlayerName].Penalty,
|
||||
"",
|
||||
2,
|
||||
"/SCORE" .. InitPlayerName .. "/" .. InitUnitName
|
||||
):ToAll()
|
||||
self: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
|
||||
|
||||
|
||||
function SCORING:ReportScoreAll()
|
||||
|
||||
env.info( "Hello World " )
|
||||
|
||||
local ScoreMessage = ""
|
||||
local PlayerMessage = ""
|
||||
|
||||
self:T( "Score Report" )
|
||||
|
||||
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
|
||||
|
||||
local PlayerScore = 0
|
||||
local PlayerPenalty = 0
|
||||
|
||||
ScoreMessage = ":\n"
|
||||
|
||||
local ScoreMessageHits = ""
|
||||
|
||||
for CategoryID, CategoryName in pairs( _SCORINGCategory ) do
|
||||
self:T( CategoryName )
|
||||
if PlayerData.Hit[CategoryID] then
|
||||
local Score = 0
|
||||
local ScoreHit = 0
|
||||
local Penalty = 0
|
||||
local PenaltyHit = 0
|
||||
self:T( "Hit scores exist for player " .. PlayerName )
|
||||
for UnitName, UnitData in pairs( PlayerData.Hit[CategoryID] ) do
|
||||
Score = Score + UnitData.Score
|
||||
ScoreHit = ScoreHit + UnitData.ScoreHit
|
||||
Penalty = Penalty + UnitData.Penalty
|
||||
PenaltyHit = UnitData.PenaltyHit
|
||||
end
|
||||
local ScoreMessageHit = string.format( "%s:%d ", CategoryName, Score - Penalty )
|
||||
self:T( ScoreMessageHit )
|
||||
ScoreMessageHits = ScoreMessageHits .. ScoreMessageHit
|
||||
PlayerScore = PlayerScore + Score
|
||||
PlayerPenalty = PlayerPenalty + Penalty
|
||||
else
|
||||
--ScoreMessageHits = ScoreMessageHits .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 )
|
||||
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, "Player Scores", 30, "AllPlayerScores"):ToAll()
|
||||
end
|
||||
|
||||
|
||||
function SCORING:ReportScorePlayer()
|
||||
|
||||
env.info( "Hello World " )
|
||||
|
||||
local ScoreMessage = ""
|
||||
local PlayerMessage = ""
|
||||
|
||||
self:T( "Score Report" )
|
||||
|
||||
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
|
||||
|
||||
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 )
|
||||
end
|
||||
end
|
||||
if ScoreMessageHits ~= "" then
|
||||
ScoreMessage = ScoreMessage .. "\n Hits: " .. ScoreMessageHits .. " "
|
||||
end
|
||||
|
||||
local ScoreMessageKills = ""
|
||||
for CategoryID, CategoryName in pairs( _SCORINGCategory ) do
|
||||
self:T( "Kill scores exist for player " .. PlayerName )
|
||||
if PlayerData.Kill[CategoryID] then
|
||||
local Score = 0
|
||||
local ScoreKill = 0
|
||||
local Penalty = 0
|
||||
local PenaltyKill = 0
|
||||
|
||||
for UnitName, UnitData in pairs( PlayerData.Kill[CategoryID] ) do
|
||||
Score = Score + UnitData.Score
|
||||
ScoreKill = ScoreKill + UnitData.ScoreKill
|
||||
Penalty = Penalty + UnitData.Penalty
|
||||
PenaltyKill = PenaltyKill + UnitData.PenaltyKill
|
||||
end
|
||||
|
||||
local ScoreMessageKill = string.format( "\n %s = %d score(%d;-%d) hits(#%d;#-%d)", CategoryName, Score - Penalty, Score, Penalty, ScoreKill, PenaltyKill )
|
||||
self:T( ScoreMessageKill )
|
||||
ScoreMessageKills = ScoreMessageKills .. ScoreMessageKill
|
||||
|
||||
PlayerScore = PlayerScore + Score
|
||||
PlayerPenalty = PlayerPenalty + Penalty
|
||||
else
|
||||
--ScoreMessageKills = ScoreMessageKills .. string.format( "%s:%d ", string.format(CategoryName, 1, 1), 0 )
|
||||
end
|
||||
end
|
||||
if ScoreMessageKills ~= "" then
|
||||
ScoreMessage = ScoreMessage .. "\n Kills: " .. ScoreMessageKills .. " "
|
||||
end
|
||||
|
||||
local ScoreMessageCoalitionChangePenalties = ""
|
||||
if PlayerData.PenaltyCoalition ~= 0 then
|
||||
ScoreMessageCoalitionChangePenalties = ScoreMessageCoalitionChangePenalties .. string.format( " -%d (%d changed)", PlayerData.Penalty, PlayerData.PenaltyCoalition )
|
||||
PlayerPenalty = PlayerPenalty + PlayerData.Penalty
|
||||
end
|
||||
if ScoreMessageCoalitionChangePenalties ~= "" then
|
||||
ScoreMessage = ScoreMessage .. "\n Coalition: " .. ScoreMessageCoalitionChangePenalties .. " "
|
||||
end
|
||||
|
||||
local ScoreMessageMission = ""
|
||||
local ScoreMission = 0
|
||||
local ScoreTask = 0
|
||||
for MissionName, MissionData in pairs( PlayerData.Mission ) do
|
||||
ScoreMission = ScoreMission + MissionData.ScoreMission
|
||||
ScoreTask = ScoreTask + MissionData.ScoreTask
|
||||
ScoreMessageMission = ScoreMessageMission .. "'" .. MissionName .. "'; "
|
||||
end
|
||||
PlayerScore = PlayerScore + ScoreMission + ScoreTask
|
||||
|
||||
if ScoreMessageMission ~= "" then
|
||||
ScoreMessage = ScoreMessage .. "\n Tasks: " .. ScoreTask .. " Mission: " .. ScoreMission .. " ( " .. ScoreMessageMission .. ") "
|
||||
end
|
||||
|
||||
PlayerMessage = PlayerMessage .. string.format( "Player '%s' Score = %d ( %d Score, -%d Penalties ):%s", PlayerName, PlayerScore - PlayerPenalty, PlayerScore, PlayerPenalty, ScoreMessage )
|
||||
end
|
||||
end
|
||||
MESSAGE:New( PlayerMessage, "Player Scores", 30, "AllPlayerScores"):ToAll()
|
||||
|
||||
end
|
||||
|
||||
|
||||
function SCORING:SecondsToClock(sSeconds)
|
||||
local nSeconds = sSeconds
|
||||
if nSeconds == 0 then
|
||||
--return nil;
|
||||
return "00:00:00";
|
||||
else
|
||||
nHours = string.format("%02.f", math.floor(nSeconds/3600));
|
||||
nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60)));
|
||||
nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60));
|
||||
return nHours..":"..nMins..":"..nSecs
|
||||
end
|
||||
end
|
||||
|
||||
--- Opens a score CSV file to log the scores.
|
||||
-- @param #SCORING self
|
||||
-- @param #string ScoringCSV
|
||||
-- @return #SCORING self
|
||||
-- @usage
|
||||
-- -- Open a new CSV file to log the scores of the game Gori Valley. Let the name of the CSV file begin with "Player Scores".
|
||||
-- ScoringObject = SCORING:New( "Gori Valley" )
|
||||
-- ScoringObject:OpenCSV( "Player Scores" )
|
||||
function SCORING:OpenCSV( ScoringCSV )
|
||||
self:F( ScoringCSV )
|
||||
|
||||
if lfs and io and os then
|
||||
if ScoringCSV then
|
||||
self.ScoringCSV = ScoringCSV
|
||||
local fdir = lfs.writedir() .. [[Logs\]] .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv"
|
||||
|
||||
self.CSVFile, self.err = io.open( fdir, "w+" )
|
||||
if not self.CSVFile then
|
||||
error( "Error: Cannot open CSV file in " .. lfs.writedir() )
|
||||
end
|
||||
|
||||
self.CSVFile:write( '"GameName","RunTime","Time","PlayerName","ScoreType","PlayerUnitCoaltion","PlayerUnitCategory","PlayerUnitType","PlayerUnitName","TargetUnitCoalition","TargetUnitCategory","TargetUnitType","TargetUnitName","Times","Score"\n' )
|
||||
|
||||
self.RunTime = os.date("%y-%m-%d_%H-%M-%S")
|
||||
else
|
||||
error( "A string containing the CSV file name must be given." )
|
||||
end
|
||||
else
|
||||
self:E( "The MissionScripting.lua file has not been changed to allow lfs, io and os modules to be used..." )
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Registers a score for a player.
|
||||
-- @param #SCORING self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @param #string ScoreType The type of the score.
|
||||
-- @param #string ScoreTimes The amount of scores achieved.
|
||||
-- @param #string ScoreAmount The score given.
|
||||
-- @param #string PlayerUnitName The unit name of the player.
|
||||
-- @param #string PlayerUnitCoalition The coalition of the player unit.
|
||||
-- @param #string PlayerUnitCategory The category of the player unit.
|
||||
-- @param #string PlayerUnitType The type of the player unit.
|
||||
-- @param #string TargetUnitName The name of the target unit.
|
||||
-- @param #string TargetUnitCoalition The coalition of the target unit.
|
||||
-- @param #string TargetUnitCategory The category of the target unit.
|
||||
-- @param #string TargetUnitType The type of the target unit.
|
||||
-- @return #SCORING self
|
||||
function SCORING:ScoreCSV( PlayerName, ScoreType, ScoreTimes, ScoreAmount, PlayerUnitName, PlayerUnitCoalition, PlayerUnitCategory, PlayerUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||
--write statistic information to file
|
||||
local ScoreTime = self:SecondsToClock( timer.getTime() )
|
||||
PlayerName = PlayerName:gsub( '"', '_' )
|
||||
|
||||
if PlayerUnitName and PlayerUnitName ~= '' then
|
||||
local PlayerUnit = Unit.getByName( PlayerUnitName )
|
||||
|
||||
if PlayerUnit then
|
||||
if not PlayerUnitCategory then
|
||||
--PlayerUnitCategory = SCORINGCategory[PlayerUnit:getCategory()]
|
||||
PlayerUnitCategory = _SCORINGCategory[PlayerUnit:getDesc().category]
|
||||
end
|
||||
|
||||
if not PlayerUnitCoalition then
|
||||
PlayerUnitCoalition = _SCORINGCoalition[PlayerUnit:getCoalition()]
|
||||
end
|
||||
|
||||
if not PlayerUnitType then
|
||||
PlayerUnitType = PlayerUnit:getTypeName()
|
||||
end
|
||||
else
|
||||
PlayerUnitName = ''
|
||||
PlayerUnitCategory = ''
|
||||
PlayerUnitCoalition = ''
|
||||
PlayerUnitType = ''
|
||||
end
|
||||
else
|
||||
PlayerUnitName = ''
|
||||
PlayerUnitCategory = ''
|
||||
PlayerUnitCoalition = ''
|
||||
PlayerUnitType = ''
|
||||
end
|
||||
|
||||
if not TargetUnitCoalition then
|
||||
TargetUnitCoalition = ''
|
||||
end
|
||||
|
||||
if not TargetUnitCategory then
|
||||
TargetUnitCategory = ''
|
||||
end
|
||||
|
||||
if not TargetUnitType then
|
||||
TargetUnitType = ''
|
||||
end
|
||||
|
||||
if not TargetUnitName then
|
||||
TargetUnitName = ''
|
||||
end
|
||||
|
||||
if lfs and io and os then
|
||||
self.CSVFile:write(
|
||||
'"' .. self.GameName .. '"' .. ',' ..
|
||||
'"' .. self.RunTime .. '"' .. ',' ..
|
||||
'' .. ScoreTime .. '' .. ',' ..
|
||||
'"' .. PlayerName .. '"' .. ',' ..
|
||||
'"' .. ScoreType .. '"' .. ',' ..
|
||||
'"' .. PlayerUnitCoalition .. '"' .. ',' ..
|
||||
'"' .. PlayerUnitCategory .. '"' .. ',' ..
|
||||
'"' .. PlayerUnitType .. '"' .. ',' ..
|
||||
'"' .. PlayerUnitName .. '"' .. ',' ..
|
||||
'"' .. TargetUnitCoalition .. '"' .. ',' ..
|
||||
'"' .. TargetUnitCategory .. '"' .. ',' ..
|
||||
'"' .. TargetUnitType .. '"' .. ',' ..
|
||||
'"' .. TargetUnitName .. '"' .. ',' ..
|
||||
'' .. ScoreTimes .. '' .. ',' ..
|
||||
'' .. ScoreAmount
|
||||
)
|
||||
|
||||
self.CSVFile:write( "\n" )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function SCORING:CloseCSV()
|
||||
if lfs and io and os then
|
||||
self.CSVFile:close()
|
||||
end
|
||||
end
|
||||
|
||||
137
Moose Development/Moose/Sead.lua
Normal file
137
Moose Development/Moose/Sead.lua
Normal file
@@ -0,0 +1,137 @@
|
||||
--- Provides defensive behaviour to a set of SAM sites within a running Mission.
|
||||
-- @module Sead
|
||||
-- @author to be searched on the forum
|
||||
-- @author (co) Flightcontrol (Modified and enriched with functionality)
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Event" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Mission" )
|
||||
Include.File( "Client" )
|
||||
Include.File( "Task" )
|
||||
|
||||
--- The SEAD class
|
||||
-- @type SEAD
|
||||
-- @extends Base#BASE
|
||||
SEAD = {
|
||||
ClassName = "SEAD",
|
||||
TargetSkill = {
|
||||
Average = { Evade = 50, DelayOff = { 10, 25 }, DelayOn = { 10, 30 } } ,
|
||||
Good = { Evade = 30, DelayOff = { 8, 20 }, DelayOn = { 20, 40 } } ,
|
||||
High = { Evade = 15, DelayOff = { 5, 17 }, DelayOn = { 30, 50 } } ,
|
||||
Excellent = { Evade = 10, DelayOff = { 3, 10 }, DelayOn = { 30, 60 } }
|
||||
},
|
||||
SEADGroupPrefixes = {}
|
||||
}
|
||||
|
||||
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
||||
-- When an anti radiation missile is fired (KH-58, KH-31P, KH-31A, KH-25MPU, HARM missiles), the SA will shut down their radars and will take evasive actions...
|
||||
-- Chances are big that the missile will miss.
|
||||
-- @param table{string,...}|string SEADGroupPrefixes which is a table of Prefixes of the SA Groups in the DCSRTE on which evasive actions need to be taken.
|
||||
-- @return SEAD
|
||||
-- @usage
|
||||
-- -- CCCP SEAD Defenses
|
||||
-- -- Defends the Russian SA installations from SEAD attacks.
|
||||
-- SEAD_RU_SAM_Defenses = SEAD:New( { 'RU SA-6 Kub', 'RU SA-6 Defenses', 'RU MI-26 Troops', 'RU Attack Gori' } )
|
||||
function SEAD:New( SEADGroupPrefixes )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( SEADGroupPrefixes )
|
||||
if type( SEADGroupPrefixes ) == 'table' then
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( SEADGroupPrefixes ) do
|
||||
self.SEADGroupPrefixes[SEADGroupPrefix] = SEADGroupPrefix
|
||||
end
|
||||
else
|
||||
self.SEADGroupNames[SEADGroupPrefixes] = SEADGroupPrefixes
|
||||
end
|
||||
_EVENTDISPATCHER:OnShot( self.EventShot, self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME.
|
||||
-- @see SEAD
|
||||
function SEAD:EventShot( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
local SEADUnit = Event.IniDCSUnit
|
||||
local SEADUnitName = Event.IniDCSUnitName
|
||||
local SEADWeapon = Event.Weapon -- Identify the weapon fired
|
||||
local SEADWeaponName = Event.WeaponName -- return weapon type
|
||||
--trigger.action.outText( string.format("Alerte, depart missile " ..string.format(SEADWeaponName)), 20) --debug message
|
||||
-- Start of the 2nd loop
|
||||
self:T( "Missile Launched = " .. SEADWeaponName )
|
||||
if SEADWeaponName == "KH-58" or SEADWeaponName == "KH-25MPU" or SEADWeaponName == "AGM-88" or SEADWeaponName == "KH-31A" or SEADWeaponName == "KH-31P" then -- Check if the missile is a SEAD
|
||||
local _evade = math.random (1,100) -- random number for chance of evading action
|
||||
local _targetMim = Event.Weapon:getTarget() -- Identify target
|
||||
local _targetMimname = Unit.getName(_targetMim)
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||
local _targetMimgroupName = _targetMimgroup:getName()
|
||||
local _targetMimcont= _targetMimgroup:getController()
|
||||
local _targetskill = _DATABASE.Templates.Units[_targetMimname].Template.skill
|
||||
self:T( self.SEADGroupPrefixes )
|
||||
self:T( _targetMimgroupName )
|
||||
local SEADGroupFound = false
|
||||
for SEADGroupPrefixID, SEADGroupPrefix in pairs( self.SEADGroupPrefixes ) do
|
||||
if string.find( _targetMimgroupName, SEADGroupPrefix, 1, true ) then
|
||||
SEADGroupFound = true
|
||||
self:T( 'Group Found' )
|
||||
break
|
||||
end
|
||||
end
|
||||
if SEADGroupFound == true then
|
||||
if _targetskill == "Random" then -- when skill is random, choose a skill
|
||||
local Skills = { "Average", "Good", "High", "Excellent" }
|
||||
_targetskill = Skills[ math.random(1,4) ]
|
||||
end
|
||||
self:T( _targetskill ) -- debug message for skill check
|
||||
if self.TargetSkill[_targetskill] then
|
||||
if (_evade > self.TargetSkill[_targetskill].Evade) then
|
||||
self:T( string.format("Evading, target skill " ..string.format(_targetskill)) ) --debug message
|
||||
local _targetMim = Weapon.getTarget(SEADWeapon)
|
||||
local _targetMimname = Unit.getName(_targetMim)
|
||||
local _targetMimgroup = Unit.getGroup(Weapon.getTarget(SEADWeapon))
|
||||
local _targetMimcont= _targetMimgroup:getController()
|
||||
routines.groupRandomDistSelf(_targetMimgroup,300,'Diamond',250,20) -- move randomly
|
||||
local SuppressedGroups1 = {} -- unit suppressed radar off for a random time
|
||||
local function SuppressionEnd1(id)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
SuppressedGroups1[id.groupName] = nil
|
||||
end
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
local delay1 = math.random(self.TargetSkill[_targetskill].DelayOff[1], self.TargetSkill[_targetskill].DelayOff[2])
|
||||
if SuppressedGroups1[id.groupName] == nil then
|
||||
SuppressedGroups1[id.groupName] = {
|
||||
SuppressionEndTime1 = timer.getTime() + delay1,
|
||||
SuppressionEndN1 = SuppressionEndCounter1 --Store instance of SuppressionEnd() scheduled function
|
||||
}
|
||||
Controller.setOption(_targetMimcont, AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
timer.scheduleFunction(SuppressionEnd1, id, SuppressedGroups1[id.groupName].SuppressionEndTime1) --Schedule the SuppressionEnd() function
|
||||
--trigger.action.outText( string.format("Radar Off " ..string.format(delay1)), 20)
|
||||
end
|
||||
|
||||
local SuppressedGroups = {}
|
||||
local function SuppressionEnd(id)
|
||||
id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE,AI.Option.Ground.val.ALARM_STATE.RED)
|
||||
SuppressedGroups[id.groupName] = nil
|
||||
end
|
||||
local id = {
|
||||
groupName = _targetMimgroup,
|
||||
ctrl = _targetMimcont
|
||||
}
|
||||
local delay = math.random(self.TargetSkill[_targetskill].DelayOn[1], self.TargetSkill[_targetskill].DelayOn[2])
|
||||
if SuppressedGroups[id.groupName] == nil then
|
||||
SuppressedGroups[id.groupName] = {
|
||||
SuppressionEndTime = timer.getTime() + delay,
|
||||
SuppressionEndN = SuppressionEndCounter --Store instance of SuppressionEnd() scheduled function
|
||||
}
|
||||
timer.scheduleFunction(SuppressionEnd, id, SuppressedGroups[id.groupName].SuppressionEndTime) --Schedule the SuppressionEnd() function
|
||||
--trigger.action.outText( string.format("Radar On " ..string.format(delay)), 20)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
758
Moose Development/Moose/Set.lua
Normal file
758
Moose Development/Moose/Set.lua
Normal file
@@ -0,0 +1,758 @@
|
||||
--- Manage sets of units and groups.
|
||||
--
|
||||
-- @{#Set} class
|
||||
-- ==================
|
||||
-- Mission designers can use the SET class to build sets of units belonging to certain:
|
||||
--
|
||||
-- * Coalitions
|
||||
-- * Categories
|
||||
-- * Countries
|
||||
-- * Unit types
|
||||
-- * Starting with certain prefix strings.
|
||||
--
|
||||
-- This list will grow over time. Planned developments are to include filters and iterators.
|
||||
-- Additional filters will be added around @{Zone#ZONEs}, Radiuses, Active players, ...
|
||||
-- More iterators will be implemented in the near future ...
|
||||
--
|
||||
-- Administers the Initial Sets of the Mission Templates as defined within the Mission Editor.
|
||||
--
|
||||
-- SET construction methods:
|
||||
-- =================================
|
||||
-- Create a new SET object with the @{#SET.New} method:
|
||||
--
|
||||
-- * @{#SET.New}: Creates a new SET object.
|
||||
--
|
||||
--
|
||||
-- SET filter criteria:
|
||||
-- =========================
|
||||
-- You can set filter criteria to define the set of units within the SET.
|
||||
-- Filter criteria are defined by:
|
||||
--
|
||||
-- * @{#SET.FilterCoalitions}: Builds the SET with the units belonging to the coalition(s).
|
||||
-- * @{#SET.FilterCategories}: Builds the SET with the units belonging to the category(ies).
|
||||
-- * @{#SET.FilterTypes}: Builds the SET with the units belonging to the unit type(s).
|
||||
-- * @{#SET.FilterCountries}: Builds the SET with the units belonging to the country(ies).
|
||||
-- * @{#SET.FilterUnitPrefixes}: Builds the SET with the units starting with the same prefix string(s).
|
||||
--
|
||||
-- Once the filter criteria have been set for the SET, you can start filtering using:
|
||||
--
|
||||
-- * @{#SET.FilterStart}: Starts the filtering of the units within the SET.
|
||||
--
|
||||
-- Planned filter criteria within development are (so these are not yet available):
|
||||
--
|
||||
-- * @{#SET.FilterGroupPrefixes}: Builds the SET with the groups of the units starting with the same prefix string(s).
|
||||
-- * @{#SET.FilterZones}: Builds the SET with the units within a @{Zone#ZONE}.
|
||||
--
|
||||
--
|
||||
-- SET iterators:
|
||||
-- ===================
|
||||
-- Once the filters have been defined and the SET has been built, you can iterate the SET with the available iterator methods.
|
||||
-- The iterator methods will walk the SET set, and call for each element within the set a function that you provide.
|
||||
-- The following iterator methods are currently available within the SET:
|
||||
--
|
||||
-- * @{#SET.ForEachAliveUnit}: Calls a function for each alive unit it finds within the SET.
|
||||
--
|
||||
-- Planned iterators methods in development are (so these are not yet available):
|
||||
--
|
||||
-- * @{#SET.ForEachUnit}: Calls a function for each unit contained within the SET.
|
||||
-- * @{#SET.ForEachGroup}: Calls a function for each group contained within the SET.
|
||||
-- * @{#SET.ForEachUnitInZone}: Calls a function for each unit within a certain zone contained within the SET.
|
||||
--
|
||||
-- ====
|
||||
-- @module Set
|
||||
-- @author FlightControl
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Menu" )
|
||||
Include.File( "Group" )
|
||||
Include.File( "Unit" )
|
||||
Include.File( "Event" )
|
||||
Include.File( "Client" )
|
||||
|
||||
--- SET class
|
||||
-- @type SET
|
||||
-- @extends Base#BASE
|
||||
SET = {
|
||||
ClassName = "SET",
|
||||
Templates = {
|
||||
Units = {},
|
||||
Groups = {},
|
||||
ClientsByName = {},
|
||||
ClientsByID = {},
|
||||
},
|
||||
DCSUnits = {},
|
||||
DCSUnitsAlive = {},
|
||||
DCSGroups = {},
|
||||
DCSGroupsAlive = {},
|
||||
Units = {},
|
||||
UnitsAlive = {},
|
||||
Groups = {},
|
||||
GroupsAlive = {},
|
||||
NavPoints = {},
|
||||
Statics = {},
|
||||
Players = {},
|
||||
PlayersAlive = {},
|
||||
Clients = {},
|
||||
ClientsAlive = {},
|
||||
Filter = {
|
||||
Coalitions = nil,
|
||||
Categories = nil,
|
||||
Types = nil,
|
||||
Countries = nil,
|
||||
UnitPrefixes = nil,
|
||||
GroupPrefixes = nil,
|
||||
},
|
||||
FilterMeta = {
|
||||
Coalitions = {
|
||||
red = coalition.side.RED,
|
||||
blue = coalition.side.BLUE,
|
||||
neutral = coalition.side.NEUTRAL,
|
||||
},
|
||||
Categories = {
|
||||
plane = Unit.Category.AIRPLANE,
|
||||
helicopter = Unit.Category.HELICOPTER,
|
||||
ground = Unit.Category.GROUND_UNIT,
|
||||
ship = Unit.Category.SHIP,
|
||||
structure = Unit.Category.STRUCTURE,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
local _DATABASECoalition =
|
||||
{
|
||||
[1] = "Red",
|
||||
[2] = "Blue",
|
||||
}
|
||||
|
||||
local _DATABASECategory =
|
||||
{
|
||||
[Unit.Category.AIRPLANE] = "Plane",
|
||||
[Unit.Category.HELICOPTER] = "Helicopter",
|
||||
[Unit.Category.GROUND_UNIT] = "Vehicle",
|
||||
[Unit.Category.SHIP] = "Ship",
|
||||
[Unit.Category.STRUCTURE] = "Structure",
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new SET object, building a set of units belonging to a coalitions, categories, countries, types or with defined prefix names.
|
||||
-- @param #SET self
|
||||
-- @return #SET
|
||||
-- @usage
|
||||
-- -- Define a new SET Object. This DBObject will contain a reference to all Group and Unit Templates defined within the ME and the DCSRTE.
|
||||
-- DBObject = SET:New()
|
||||
function SET:New()
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
|
||||
_EVENTDISPATCHER:OnBirth( self._EventOnBirth, self )
|
||||
_EVENTDISPATCHER:OnDead( self._EventOnDeadOrCrash, self )
|
||||
_EVENTDISPATCHER:OnCrash( self._EventOnDeadOrCrash, self )
|
||||
|
||||
|
||||
-- Add SET with registered clients and already alive players
|
||||
|
||||
-- Follow alive players and clients
|
||||
_EVENTDISPATCHER:OnPlayerEnterUnit( self._EventOnPlayerEnterUnit, self )
|
||||
_EVENTDISPATCHER:OnPlayerLeaveUnit( self._EventOnPlayerLeaveUnit, self )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Finds a Unit based on the Unit Name.
|
||||
-- @param #SET self
|
||||
-- @param #string UnitName
|
||||
-- @return Unit#UNIT The found Unit.
|
||||
function SET:FindUnit( UnitName )
|
||||
|
||||
local UnitFound = self.Units[UnitName]
|
||||
return UnitFound
|
||||
end
|
||||
|
||||
--- Finds a Unit based on the Unit Name.
|
||||
-- @param #SET self
|
||||
-- @param Unit#UNIT UnitToAdd
|
||||
-- @return Unit#UNIT The added Unit.
|
||||
function SET:AddUnit( UnitToAdd )
|
||||
|
||||
self.Units[UnitToAdd.UnitName] = UnitToAdd
|
||||
return self.Units[UnitToAdd.UnitName]
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Builds a set of units of coalitons.
|
||||
-- Possible current coalitions are red, blue and neutral.
|
||||
-- @param #SET self
|
||||
-- @param #string Coalitions Can take the following values: "red", "blue", "neutral".
|
||||
-- @return #SET self
|
||||
function SET:FilterCoalitions( Coalitions )
|
||||
if not self.Filter.Coalitions then
|
||||
self.Filter.Coalitions = {}
|
||||
end
|
||||
if type( Coalitions ) ~= "table" then
|
||||
Coalitions = { Coalitions }
|
||||
end
|
||||
for CoalitionID, Coalition in pairs( Coalitions ) do
|
||||
self.Filter.Coalitions[Coalition] = Coalition
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of units out of categories.
|
||||
-- Possible current categories are plane, helicopter, ground, ship.
|
||||
-- @param #SET self
|
||||
-- @param #string Categories Can take the following values: "plane", "helicopter", "ground", "ship".
|
||||
-- @return #SET self
|
||||
function SET:FilterCategories( Categories )
|
||||
if not self.Filter.Categories then
|
||||
self.Filter.Categories = {}
|
||||
end
|
||||
if type( Categories ) ~= "table" then
|
||||
Categories = { Categories }
|
||||
end
|
||||
for CategoryID, Category in pairs( Categories ) do
|
||||
self.Filter.Categories[Category] = Category
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of units of defined unit types.
|
||||
-- Possible current types are those types known within DCS world.
|
||||
-- @param #SET self
|
||||
-- @param #string Types Can take those type strings known within DCS world.
|
||||
-- @return #SET self
|
||||
function SET:FilterTypes( Types )
|
||||
if not self.Filter.Types then
|
||||
self.Filter.Types = {}
|
||||
end
|
||||
if type( Types ) ~= "table" then
|
||||
Types = { Types }
|
||||
end
|
||||
for TypeID, Type in pairs( Types ) do
|
||||
self.Filter.Types[Type] = Type
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of units of defined countries.
|
||||
-- Possible current countries are those known within DCS world.
|
||||
-- @param #SET self
|
||||
-- @param #string Countries Can take those country strings known within DCS world.
|
||||
-- @return #SET self
|
||||
function SET:FilterCountries( Countries )
|
||||
if not self.Filter.Countries then
|
||||
self.Filter.Countries = {}
|
||||
end
|
||||
if type( Countries ) ~= "table" then
|
||||
Countries = { Countries }
|
||||
end
|
||||
for CountryID, Country in pairs( Countries ) do
|
||||
self.Filter.Countries[Country] = Country
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of units of defined unit prefixes.
|
||||
-- All the units starting with the given prefixes will be included within the set.
|
||||
-- @param #SET self
|
||||
-- @param #string Prefixes The prefix of which the unit name starts with.
|
||||
-- @return #SET self
|
||||
function SET:FilterUnitPrefixes( Prefixes )
|
||||
if not self.Filter.UnitPrefixes then
|
||||
self.Filter.UnitPrefixes = {}
|
||||
end
|
||||
if type( Prefixes ) ~= "table" then
|
||||
Prefixes = { Prefixes }
|
||||
end
|
||||
for PrefixID, Prefix in pairs( Prefixes ) do
|
||||
self.Filter.UnitPrefixes[Prefix] = Prefix
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Builds a set of units of defined group prefixes.
|
||||
-- All the units starting with the given group prefixes will be included within the set.
|
||||
-- @param #SET self
|
||||
-- @param #string Prefixes The prefix of which the group name where the unit belongs to starts with.
|
||||
-- @return #SET self
|
||||
function SET:FilterGroupPrefixes( Prefixes )
|
||||
if not self.Filter.GroupPrefixes then
|
||||
self.Filter.GroupPrefixes = {}
|
||||
end
|
||||
if type( Prefixes ) ~= "table" then
|
||||
Prefixes = { Prefixes }
|
||||
end
|
||||
for PrefixID, Prefix in pairs( Prefixes ) do
|
||||
self.Filter.GroupPrefixes[Prefix] = Prefix
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Starts the filtering.
|
||||
-- @param #SET self
|
||||
-- @return #SET self
|
||||
function SET:FilterStart()
|
||||
|
||||
if _DATABASE then
|
||||
-- OK, we have a _DATABASE
|
||||
-- Now use the different filters to build the set.
|
||||
-- We first take ALL of the Units of the _DATABASE.
|
||||
|
||||
self:E( { "Adding Set Datapoints with filters" } )
|
||||
for DCSUnitName, DCSUnit in pairs( _DATABASE.DCSUnits ) do
|
||||
|
||||
if self:_IsIncludeDCSUnit( DCSUnit ) then
|
||||
|
||||
self:E( { "Adding Unit:", DCSUnitName } )
|
||||
self.DCSUnits[DCSUnitName] = _DATABASE.DCSUnits[DCSUnitName]
|
||||
self.Units[DCSUnitName] = _DATABASE:FindUnit( DCSUnitName )
|
||||
|
||||
if _DATABASE.DCSUnitsAlive[DCSUnitName] then
|
||||
self.DCSUnitsAlive[DCSUnitName] = _DATABASE.DCSUnitsAlive[DCSUnitName]
|
||||
self.UnitsAlive[DCSUnitName] = _DATABASE.UnitsAlive[DCSUnitName]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
for DCSGroupName, DCSGroup in pairs( _DATABASE.DCSGroups ) do
|
||||
|
||||
--if self:_IsIncludeDCSGroup( DCSGroup ) then
|
||||
self:E( { "Adding Group:", DCSGroupName } )
|
||||
self.DCSGroups[DCSGroupName] = _DATABASE.DCSGroups[DCSGroupName]
|
||||
self.Groups[DCSGroupName] = _DATABASE:FindGroups( DCSGroupName )
|
||||
--end
|
||||
|
||||
if _DATABASE.DCSGroupsAlive[DCSGroupName] then
|
||||
self.DCSGroupsAlive[DCSGroupName] = _DATABASE.DCSGroupsAlive[DCSGroupName]
|
||||
self.GroupsAlive[DCSGroupName] = _DATABASE.GroupsAlive[DCSGroupName]
|
||||
end
|
||||
end
|
||||
|
||||
for DCSUnitName, Client in pairs( _DATABASE.CLIENTS ) do
|
||||
self:E( { "Adding Client for Unit:", DCSUnitName } )
|
||||
self.Clients[DCSUnitName] = _DATABASE.Clients[DCSUnitName]
|
||||
end
|
||||
|
||||
else
|
||||
self:E( "There is a structural error in MOOSE. No _DATABASE has been defined! Cannot build this custom SET." )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Private method that registers all alive players in the mission.
|
||||
-- @param #SET self
|
||||
-- @return #SET self
|
||||
function SET:_RegisterPlayers()
|
||||
|
||||
local CoalitionsData = { AlivePlayersRed = coalition.getPlayers( coalition.side.RED ), AlivePlayersBlue = coalition.getPlayers( coalition.side.BLUE ) }
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
for UnitId, UnitData in pairs( CoalitionData ) do
|
||||
self:T3( { "UnitData:", UnitData } )
|
||||
if UnitData and UnitData:isExist() then
|
||||
local UnitName = UnitData:getName()
|
||||
if not self.PlayersAlive[UnitName] then
|
||||
self:E( { "Add player for unit:", UnitName, UnitData:getPlayerName() } )
|
||||
self.PlayersAlive[UnitName] = UnitData:getPlayerName()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Private method that registers all datapoints within in the mission.
|
||||
-- @param #SET self
|
||||
-- @return #SET self
|
||||
function SET:_RegisterDatabase()
|
||||
|
||||
local CoalitionsData = { AlivePlayersRed = coalition.getGroups( coalition.side.RED ), AlivePlayersBlue = coalition.getGroups( coalition.side.BLUE ) }
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
for DCSGroupId, DCSGroup in pairs( CoalitionData ) do
|
||||
|
||||
if DCSGroup:isExist() then
|
||||
local DCSGroupName = DCSGroup:getName()
|
||||
|
||||
self:E( { "Register Group:", DCSGroup, DCSGroupName } )
|
||||
self.DCSGroups[DCSGroupName] = DCSGroup
|
||||
self.Groups[DCSGroupName] = GROUP:New( DCSGroup )
|
||||
|
||||
if self:_IsAliveDCSGroup(DCSGroup) then
|
||||
self:E( { "Register Alive Group:", DCSGroup, DCSGroupName } )
|
||||
self.DCSGroupsAlive[DCSGroupName] = DCSGroup
|
||||
self.GroupsAlive[DCSGroupName] = self.Groups[DCSGroupName]
|
||||
end
|
||||
|
||||
for DCSUnitId, DCSUnit in pairs( DCSGroup:getUnits() ) do
|
||||
|
||||
local DCSUnitName = DCSUnit:getName()
|
||||
self:E( { "Register Unit:", DCSUnit, DCSUnitName } )
|
||||
|
||||
self.DCSUnits[DCSUnitName] = DCSUnit
|
||||
self:AddUnit( UNIT:Find( DCSUnit ) )
|
||||
--self.Units[DCSUnitName] = UNIT:Register( DCSUnit )
|
||||
|
||||
if self:_IsAliveDCSUnit(DCSUnit) then
|
||||
self:E( { "Register Alive Unit:", DCSUnit, DCSUnitName } )
|
||||
self.DCSUnitsAlive[DCSUnitName] = DCSUnit
|
||||
self.UnitsAlive[DCSUnitName] = self.Units[DCSUnitName]
|
||||
end
|
||||
end
|
||||
else
|
||||
self:E( "Group does not exist: " .. DCSGroup )
|
||||
end
|
||||
|
||||
for ClientName, ClientTemplate in pairs( self.Templates.ClientsByName ) do
|
||||
self.Clients[ClientName] = CLIENT:Find( ClientName )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Events
|
||||
|
||||
--- Handles the OnBirth event for the alive units set.
|
||||
-- @param #SET self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function SET:_EventOnBirth( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if self:_IsIncludeDCSUnit( Event.IniDCSUnit ) then
|
||||
self.DCSUnits[Event.IniDCSUnitName] = Event.IniDCSUnit
|
||||
self.DCSUnitsAlive[Event.IniDCSUnitName] = Event.IniDCSUnit
|
||||
self:AddUnit( UNIT:Register( Event.IniDCSUnit ) )
|
||||
--self.Units[Event.IniDCSUnitName] = UNIT:Register( Event.IniDCSUnit )
|
||||
|
||||
--if not self.DCSGroups[Event.IniDCSGroupName] then
|
||||
-- self.DCSGroups[Event.IniDCSGroupName] = Event.IniDCSGroupName
|
||||
-- self.DCSGroupsAlive[Event.IniDCSGroupName] = Event.IniDCSGroupName
|
||||
-- self.Groups[Event.IniDCSGroupName] = GROUP:New( Event.IniDCSGroup )
|
||||
--end
|
||||
self:_EventOnPlayerEnterUnit( Event )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnDead or OnCrash event for alive units set.
|
||||
-- @param #SET self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function SET:_EventOnDeadOrCrash( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if self.DCSUnitsAlive[Event.IniDCSUnitName] then
|
||||
self.DCSUnits[Event.IniDCSUnitName] = nil
|
||||
self.DCSUnitsAlive[Event.IniDCSUnitName] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied).
|
||||
-- @param #SET self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function SET:_EventOnPlayerEnterUnit( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if self:_IsIncludeDCSUnit( Event.IniDCSUnit ) then
|
||||
if not self.PlayersAlive[Event.IniDCSUnitName] then
|
||||
self:E( { "Add player for unit:", Event.IniDCSUnitName, Event.IniDCSUnit:getPlayerName() } )
|
||||
self.PlayersAlive[Event.IniDCSUnitName] = Event.IniDCSUnit:getPlayerName()
|
||||
self.ClientsAlive[Event.IniDCSUnitName] = _DATABASE.Clients[ Event.IniDCSUnitName ]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Handles the OnPlayerLeaveUnit event to clean the active players table.
|
||||
-- @param #SET self
|
||||
-- @param Event#EVENTDATA Event
|
||||
function SET:_EventOnPlayerLeaveUnit( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if self:_IsIncludeDCSUnit( Event.IniDCSUnit ) then
|
||||
if self.PlayersAlive[Event.IniDCSUnitName] then
|
||||
self:E( { "Cleaning player for unit:", Event.IniDCSUnitName, Event.IniDCSUnit:getPlayerName() } )
|
||||
self.PlayersAlive[Event.IniDCSUnitName] = nil
|
||||
self.ClientsAlive[Event.IniDCSUnitName] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Iterators
|
||||
|
||||
--- Interate the SET and call an interator function for the given set, providing the Object for each element within the set and optional parameters.
|
||||
-- @param #SET self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive player in the SET.
|
||||
-- @return #SET self
|
||||
function SET:ForEach( IteratorFunction, arg, Set )
|
||||
self:F( arg )
|
||||
|
||||
local function CoRoutine()
|
||||
local Count = 0
|
||||
for ObjectID, Object in pairs( Set ) do
|
||||
self:T2( Object )
|
||||
IteratorFunction( Object, unpack( arg ) )
|
||||
Count = Count + 1
|
||||
if Count % 10 == 0 then
|
||||
coroutine.yield( false )
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local co = coroutine.create( CoRoutine )
|
||||
|
||||
local function Schedule()
|
||||
|
||||
local status, res = coroutine.resume( co )
|
||||
self:T( { status, res } )
|
||||
|
||||
if status == false then
|
||||
error( res )
|
||||
end
|
||||
if res == false then
|
||||
return true -- resume next time the loop
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local Scheduler = SCHEDULER:New( self, Schedule, {}, 0.001, 0.001, 0 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Interate the SET and call an interator function for each **alive** unit, providing the Unit and optional parameters.
|
||||
-- @param #SET self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive unit in the SET. The function needs to accept a UNIT parameter.
|
||||
-- @return #SET self
|
||||
function SET:ForEachDCSUnitAlive( IteratorFunction, ... )
|
||||
self:F( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self.DCSUnitsAlive )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Interate the SET and call an interator function for each **alive** player, providing the Unit of the player and optional parameters.
|
||||
-- @param #SET self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive player in the SET. The function needs to accept a UNIT parameter.
|
||||
-- @return #SET self
|
||||
function SET:ForEachPlayer( IteratorFunction, ... )
|
||||
self:F( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self.PlayersAlive )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Interate the SET and call an interator function for each client, providing the Client to the function and optional parameters.
|
||||
-- @param #SET self
|
||||
-- @param #function IteratorFunction The function that will be called when there is an alive player in the SET. The function needs to accept a CLIENT parameter.
|
||||
-- @return #SET self
|
||||
function SET:ForEachClient( IteratorFunction, ... )
|
||||
self:F( arg )
|
||||
|
||||
self:ForEach( IteratorFunction, arg, self.Clients )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function SET:ScanEnvironment()
|
||||
self:F()
|
||||
|
||||
self.Navpoints = {}
|
||||
self.Units = {}
|
||||
--Build routines.db.units and self.Navpoints
|
||||
for coa_name, coa_data in pairs(env.mission.coalition) do
|
||||
|
||||
if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then
|
||||
--self.Units[coa_name] = {}
|
||||
|
||||
----------------------------------------------
|
||||
-- build nav points DB
|
||||
self.Navpoints[coa_name] = {}
|
||||
if coa_data.nav_points then --navpoints
|
||||
for nav_ind, nav_data in pairs(coa_data.nav_points) do
|
||||
|
||||
if type(nav_data) == 'table' then
|
||||
self.Navpoints[coa_name][nav_ind] = routines.utils.deepCopy(nav_data)
|
||||
|
||||
self.Navpoints[coa_name][nav_ind]['name'] = nav_data.callsignStr -- name is a little bit more self-explanatory.
|
||||
self.Navpoints[coa_name][nav_ind]['point'] = {} -- point is used by SSE, support it.
|
||||
self.Navpoints[coa_name][nav_ind]['point']['x'] = nav_data.x
|
||||
self.Navpoints[coa_name][nav_ind]['point']['y'] = 0
|
||||
self.Navpoints[coa_name][nav_ind]['point']['z'] = nav_data.y
|
||||
end
|
||||
end
|
||||
end
|
||||
-------------------------------------------------
|
||||
if coa_data.country then --there is a country table
|
||||
for cntry_id, cntry_data in pairs(coa_data.country) do
|
||||
|
||||
local countryName = string.lower(cntry_data.name)
|
||||
--self.Units[coa_name][countryName] = {}
|
||||
--self.Units[coa_name][countryName]["countryId"] = cntry_data.id
|
||||
|
||||
if type(cntry_data) == 'table' then --just making sure
|
||||
|
||||
for obj_type_name, obj_type_data in pairs(cntry_data) do
|
||||
|
||||
if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then --should be an unncessary check
|
||||
|
||||
local category = obj_type_name
|
||||
|
||||
if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group!
|
||||
|
||||
--self.Units[coa_name][countryName][category] = {}
|
||||
|
||||
for group_num, GroupTemplate in pairs(obj_type_data.group) do
|
||||
|
||||
if GroupTemplate and GroupTemplate.units and type(GroupTemplate.units) == 'table' then --making sure again- this is a valid group
|
||||
self:_RegisterGroup( GroupTemplate )
|
||||
end --if GroupTemplate and GroupTemplate.units then
|
||||
end --for group_num, GroupTemplate in pairs(obj_type_data.group) do
|
||||
end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then
|
||||
end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then
|
||||
end --for obj_type_name, obj_type_data in pairs(cntry_data) do
|
||||
end --if type(cntry_data) == 'table' then
|
||||
end --for cntry_id, cntry_data in pairs(coa_data.country) do
|
||||
end --if coa_data.country then --there is a country table
|
||||
end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then
|
||||
end --for coa_name, coa_data in pairs(mission.coalition) do
|
||||
|
||||
self:_RegisterDatabase()
|
||||
self:_RegisterPlayers()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #SET self
|
||||
-- @param DCSUnit#Unit DCSUnit
|
||||
-- @return #SET self
|
||||
function SET:_IsIncludeDCSUnit( DCSUnit )
|
||||
self:F( DCSUnit )
|
||||
local DCSUnitInclude = true
|
||||
|
||||
if self.Filter.Coalitions then
|
||||
local DCSUnitCoalition = false
|
||||
for CoalitionID, CoalitionName in pairs( self.Filter.Coalitions ) do
|
||||
self:T( { "Coalition:", DCSUnit:getCoalition(), self.FilterMeta.Coalitions[CoalitionName], CoalitionName } )
|
||||
if self.FilterMeta.Coalitions[CoalitionName] and self.FilterMeta.Coalitions[CoalitionName] == DCSUnit:getCoalition() then
|
||||
DCSUnitCoalition = true
|
||||
end
|
||||
end
|
||||
DCSUnitInclude = DCSUnitInclude and DCSUnitCoalition
|
||||
end
|
||||
|
||||
if self.Filter.Categories then
|
||||
local DCSUnitCategory = false
|
||||
for CategoryID, CategoryName in pairs( self.Filter.Categories ) do
|
||||
self:T( { "Category:", DCSUnit:getDesc().category, self.FilterMeta.Categories[CategoryName], CategoryName } )
|
||||
if self.FilterMeta.Categories[CategoryName] and self.FilterMeta.Categories[CategoryName] == DCSUnit:getDesc().category then
|
||||
DCSUnitCategory = true
|
||||
end
|
||||
end
|
||||
DCSUnitInclude = DCSUnitInclude and DCSUnitCategory
|
||||
end
|
||||
|
||||
if self.Filter.Types then
|
||||
local DCSUnitType = false
|
||||
for TypeID, TypeName in pairs( self.Filter.Types ) do
|
||||
self:T( { "Type:", DCSUnit:getTypeName(), TypeName } )
|
||||
if TypeName == DCSUnit:getTypeName() then
|
||||
DCSUnitType = true
|
||||
end
|
||||
end
|
||||
DCSUnitInclude = DCSUnitInclude and DCSUnitType
|
||||
end
|
||||
|
||||
if self.Filter.Countries then
|
||||
local DCSUnitCountry = false
|
||||
for CountryID, CountryName in pairs( self.Filter.Countries ) do
|
||||
self:T( { "Country:", DCSUnit:getCountry(), CountryName } )
|
||||
if country.id[CountryName] == DCSUnit:getCountry() then
|
||||
DCSUnitCountry = true
|
||||
end
|
||||
end
|
||||
DCSUnitInclude = DCSUnitInclude and DCSUnitCountry
|
||||
end
|
||||
|
||||
if self.Filter.UnitPrefixes then
|
||||
local DCSUnitPrefix = false
|
||||
for UnitPrefixId, UnitPrefix in pairs( self.Filter.UnitPrefixes ) do
|
||||
self:T( { "Unit Prefix:", string.find( DCSUnit:getName(), UnitPrefix, 1 ), UnitPrefix } )
|
||||
if string.find( DCSUnit:getName(), UnitPrefix, 1 ) then
|
||||
DCSUnitPrefix = true
|
||||
end
|
||||
end
|
||||
DCSUnitInclude = DCSUnitInclude and DCSUnitPrefix
|
||||
end
|
||||
|
||||
self:T( DCSUnitInclude )
|
||||
return DCSUnitInclude
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #SET self
|
||||
-- @param DCSUnit#Unit DCSUnit
|
||||
-- @return #SET self
|
||||
function SET:_IsAliveDCSUnit( DCSUnit )
|
||||
self:F( DCSUnit )
|
||||
local DCSUnitAlive = false
|
||||
if DCSUnit and DCSUnit:isExist() and DCSUnit:isActive() then
|
||||
if self.DCSUnits[DCSUnit:getName()] then
|
||||
DCSUnitAlive = true
|
||||
end
|
||||
end
|
||||
self:T( DCSUnitAlive )
|
||||
return DCSUnitAlive
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #SET self
|
||||
-- @param DCSGroup#Group DCSGroup
|
||||
-- @return #SET self
|
||||
function SET:_IsAliveDCSGroup( DCSGroup )
|
||||
self:F( DCSGroup )
|
||||
local DCSGroupAlive = false
|
||||
if DCSGroup and DCSGroup:isExist() then
|
||||
if self.DCSGroups[DCSGroup:getName()] then
|
||||
DCSGroupAlive = true
|
||||
end
|
||||
end
|
||||
self:T( DCSGroupAlive )
|
||||
return DCSGroupAlive
|
||||
end
|
||||
|
||||
|
||||
--- Traces the current SET contents in the log ... (for debug reasons).
|
||||
-- @param #SET self
|
||||
-- @return #SET self
|
||||
function SET:TraceDatabase()
|
||||
self:F()
|
||||
|
||||
self:T( { "DCSUnits:", self.DCSUnits } )
|
||||
self:T( { "DCSUnitsAlive:", self.DCSUnitsAlive } )
|
||||
end
|
||||
|
||||
|
||||
1263
Moose Development/Moose/Spawn.lua
Normal file
1263
Moose Development/Moose/Spawn.lua
Normal file
File diff suppressed because it is too large
Load Diff
961
Moose Development/Moose/Stage.lua
Normal file
961
Moose Development/Moose/Stage.lua
Normal file
@@ -0,0 +1,961 @@
|
||||
--- Stages within a @{TASK} within a @{MISSION}. All of the STAGE functionality is considered internally administered and not to be used by any Mission designer.
|
||||
-- @module STAGE
|
||||
-- @author Flightcontrol
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Mission" )
|
||||
Include.File( "Client" )
|
||||
Include.File( "Task" )
|
||||
|
||||
--- The STAGE class
|
||||
-- @type
|
||||
STAGE = {
|
||||
ClassName = "STAGE",
|
||||
MSG = { ID = "None", TIME = 10 },
|
||||
FREQUENCY = { NONE = 0, ONCE = 1, REPEAT = -1 },
|
||||
|
||||
Name = "NoStage",
|
||||
StageType = '',
|
||||
WaitTime = 1,
|
||||
Frequency = 1,
|
||||
MessageCount = 0,
|
||||
MessageInterval = 15,
|
||||
MessageShown = {},
|
||||
MessageShow = false,
|
||||
MessageFlash = false
|
||||
}
|
||||
|
||||
|
||||
function STAGE:New()
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F()
|
||||
return self
|
||||
end
|
||||
|
||||
function STAGE:Execute( Mission, Client, Task )
|
||||
|
||||
local Valid = true
|
||||
|
||||
return Valid
|
||||
end
|
||||
|
||||
function STAGE:Executing( Mission, Client, Task )
|
||||
|
||||
end
|
||||
|
||||
function STAGE:Validate( Mission, Client, Task )
|
||||
local Valid = true
|
||||
|
||||
return Valid
|
||||
end
|
||||
|
||||
|
||||
STAGEBRIEF = {
|
||||
ClassName = "BRIEF",
|
||||
MSG = { ID = "Brief", TIME = 1 },
|
||||
Name = "Brief",
|
||||
StageBriefingTime = 0,
|
||||
StageBriefingDuration = 1
|
||||
}
|
||||
|
||||
function STAGEBRIEF:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'CLIENT'
|
||||
return self
|
||||
end
|
||||
|
||||
function STAGEBRIEF:Execute( Mission, Client, Task )
|
||||
local Valid = BASE:Inherited(self):Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
Client:ShowBriefing()
|
||||
self.StageBriefingTime = timer.getTime()
|
||||
return Valid
|
||||
end
|
||||
|
||||
function STAGEBRIEF:Validate( Mission, Client, Task )
|
||||
local Valid = STAGE:Validate( Mission, Client, Task )
|
||||
self:T()
|
||||
|
||||
if timer.getTime() - self.StageBriefingTime <= self.StageBriefingDuration then
|
||||
return 0
|
||||
else
|
||||
self.StageBriefingTime = timer.getTime()
|
||||
return 1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
STAGESTART = {
|
||||
ClassName = "START",
|
||||
MSG = { ID = "Start", TIME = 1 },
|
||||
Name = "Start",
|
||||
StageStartTime = 0,
|
||||
StageStartDuration = 1
|
||||
}
|
||||
|
||||
function STAGESTART:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'CLIENT'
|
||||
return self
|
||||
end
|
||||
|
||||
function STAGESTART:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
local Valid = BASE:Inherited(self):Execute( Mission, Client, Task )
|
||||
if Task.TaskBriefing then
|
||||
Client:Message( Task.TaskBriefing, 30, Mission.Name .. "/Stage", "Command" )
|
||||
else
|
||||
Client:Message( 'Task ' .. Task.TaskNumber .. '.', 30, Mission.Name .. "/Stage", "Command" )
|
||||
end
|
||||
self.StageStartTime = timer.getTime()
|
||||
return Valid
|
||||
end
|
||||
|
||||
function STAGESTART:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
local Valid = STAGE:Validate( Mission, Client, Task )
|
||||
|
||||
if timer.getTime() - self.StageStartTime <= self.StageStartDuration then
|
||||
return 0
|
||||
else
|
||||
self.StageStartTime = timer.getTime()
|
||||
return 1
|
||||
end
|
||||
|
||||
return 1
|
||||
|
||||
end
|
||||
|
||||
STAGE_CARGO_LOAD = {
|
||||
ClassName = "STAGE_CARGO_LOAD"
|
||||
}
|
||||
|
||||
function STAGE_CARGO_LOAD:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'CLIENT'
|
||||
return self
|
||||
end
|
||||
|
||||
function STAGE_CARGO_LOAD:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
local Valid = BASE:Inherited(self):Execute( Mission, Client, Task )
|
||||
|
||||
for LoadCargoID, LoadCargo in pairs( Task.Cargos.LoadCargos ) do
|
||||
LoadCargo:Load( Client )
|
||||
end
|
||||
|
||||
if Mission.MissionReportFlash and Client:IsTransport() then
|
||||
Client:ShowCargo()
|
||||
end
|
||||
|
||||
return Valid
|
||||
end
|
||||
|
||||
function STAGE_CARGO_LOAD:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
local Valid = STAGE:Validate( Mission, Client, Task )
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
|
||||
STAGE_CARGO_INIT = {
|
||||
ClassName = "STAGE_CARGO_INIT"
|
||||
}
|
||||
|
||||
function STAGE_CARGO_INIT:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'CLIENT'
|
||||
return self
|
||||
end
|
||||
|
||||
function STAGE_CARGO_INIT:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
local Valid = BASE:Inherited(self):Execute( Mission, Client, Task )
|
||||
|
||||
for InitLandingZoneID, InitLandingZone in pairs( Task.LandingZones.LandingZones ) do
|
||||
self:T( InitLandingZone )
|
||||
InitLandingZone:Spawn()
|
||||
end
|
||||
|
||||
|
||||
self:T( Task.Cargos.InitCargos )
|
||||
for InitCargoID, InitCargoData in pairs( Task.Cargos.InitCargos ) do
|
||||
self:T( { InitCargoData } )
|
||||
InitCargoData:Spawn( Client )
|
||||
end
|
||||
|
||||
return Valid
|
||||
end
|
||||
|
||||
|
||||
function STAGE_CARGO_INIT:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
local Valid = STAGE:Validate( Mission, Client, Task )
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
|
||||
|
||||
STAGEROUTE = {
|
||||
ClassName = "STAGEROUTE",
|
||||
MSG = { ID = "Route", TIME = 5 },
|
||||
Frequency = STAGE.FREQUENCY.REPEAT,
|
||||
Name = "Route"
|
||||
}
|
||||
|
||||
function STAGEROUTE:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'CLIENT'
|
||||
self.MessageSwitch = true
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Execute the routing.
|
||||
-- @param #STAGEROUTE self
|
||||
-- @param Mission#MISSION Mission
|
||||
-- @param Client#CLIENT Client
|
||||
-- @param Task#TASK Task
|
||||
function STAGEROUTE:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
local Valid = BASE:Inherited(self):Execute( Mission, Client, Task )
|
||||
|
||||
local RouteMessage = "Fly to: "
|
||||
self:T( Task.LandingZones )
|
||||
for LandingZoneID, LandingZoneName in pairs( Task.LandingZones.LandingZoneNames ) do
|
||||
RouteMessage = RouteMessage .. "\n " .. LandingZoneName .. ' at ' .. routines.getBRStringZone( { zone = LandingZoneName, ref = Client:GetClientGroupDCSUnit():getPoint(), true, true } ) .. ' km.'
|
||||
end
|
||||
|
||||
if Client:IsMultiSeated() then
|
||||
Client:Message( RouteMessage, self.MSG.TIME, Mission.Name .. "/StageRoute", "Co-Pilot", 20 )
|
||||
else
|
||||
Client:Message( RouteMessage, self.MSG.TIME, Mission.Name .. "/StageRoute", "Command", 20 )
|
||||
end
|
||||
|
||||
|
||||
if Mission.MissionReportFlash and Client:IsTransport() then
|
||||
Client:ShowCargo()
|
||||
end
|
||||
|
||||
return Valid
|
||||
end
|
||||
|
||||
function STAGEROUTE:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
local Valid = STAGE:Validate( Mission, Client, Task )
|
||||
|
||||
-- check if the Client is in the landing zone
|
||||
self:T( Task.LandingZones.LandingZoneNames )
|
||||
Task.CurrentLandingZoneName = routines.IsUnitNearZonesRadius( Client:GetClientGroupDCSUnit(), Task.LandingZones.LandingZoneNames, 500 )
|
||||
|
||||
if Task.CurrentLandingZoneName then
|
||||
|
||||
Task.CurrentLandingZone = Task.LandingZones.LandingZones[Task.CurrentLandingZoneName].CargoZone
|
||||
Task.CurrentCargoZone = Task.LandingZones.LandingZones[Task.CurrentLandingZoneName]
|
||||
|
||||
if Task.CurrentCargoZone then
|
||||
if not Task.Signalled then
|
||||
Task.Signalled = Task.CurrentCargoZone:Signal()
|
||||
end
|
||||
end
|
||||
|
||||
self:T( 1 )
|
||||
return 1
|
||||
end
|
||||
|
||||
self:T( 0 )
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
STAGELANDING = {
|
||||
ClassName = "STAGELANDING",
|
||||
MSG = { ID = "Landing", TIME = 10 },
|
||||
Name = "Landing",
|
||||
Signalled = false
|
||||
}
|
||||
|
||||
function STAGELANDING:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'CLIENT'
|
||||
return self
|
||||
end
|
||||
|
||||
--- Execute the landing coordination.
|
||||
-- @param #STAGELANDING self
|
||||
-- @param Mission#MISSION Mission
|
||||
-- @param Client#CLIENT Client
|
||||
-- @param Task#TASK Task
|
||||
function STAGELANDING:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
if Client:IsMultiSeated() then
|
||||
Client:Message( "We have arrived at the landing zone.", self.MSG.TIME, Mission.Name .. "/StageArrived", "Co-Pilot", 10 )
|
||||
else
|
||||
Client:Message( "You have arrived at the landing zone.", self.MSG.TIME, Mission.Name .. "/StageArrived", "Command", 10 )
|
||||
end
|
||||
|
||||
Task.HostUnit = Task.CurrentCargoZone:GetHostUnit()
|
||||
|
||||
self:T( { Task.HostUnit } )
|
||||
|
||||
if Task.HostUnit then
|
||||
|
||||
Task.HostUnitName = Task.HostUnit:GetPrefix()
|
||||
Task.HostUnitTypeName = Task.HostUnit:GetTypeName()
|
||||
|
||||
local HostMessage = ""
|
||||
Task.CargoNames = ""
|
||||
|
||||
local IsFirst = true
|
||||
|
||||
for CargoID, Cargo in pairs( CARGOS ) do
|
||||
if Cargo.CargoType == Task.CargoType then
|
||||
|
||||
if Cargo:IsLandingRequired() then
|
||||
self:T( "Task for cargo " .. Cargo.CargoType .. " requires landing.")
|
||||
Task.IsLandingRequired = true
|
||||
end
|
||||
|
||||
if Cargo:IsSlingLoad() then
|
||||
self:T( "Task for cargo " .. Cargo.CargoType .. " is a slingload.")
|
||||
Task.IsSlingLoad = true
|
||||
end
|
||||
|
||||
if IsFirst then
|
||||
IsFirst = false
|
||||
Task.CargoNames = Task.CargoNames .. Cargo.CargoName .. "( " .. Cargo.CargoWeight .. " )"
|
||||
else
|
||||
Task.CargoNames = Task.CargoNames .. "; " .. Cargo.CargoName .. "( " .. Cargo.CargoWeight .. " )"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Task.IsLandingRequired then
|
||||
HostMessage = "Land the helicopter to " .. Task.TEXT[1] .. " " .. Task.CargoNames .. "."
|
||||
else
|
||||
HostMessage = "Use the Radio menu and F6 to find the cargo, then fly or land near the cargo and " .. Task.TEXT[1] .. " " .. Task.CargoNames .. "."
|
||||
end
|
||||
|
||||
local Host = "Command"
|
||||
if Task.HostUnitName then
|
||||
Host = Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")"
|
||||
else
|
||||
if Client:IsMultiSeated() then
|
||||
Host = "Co-Pilot"
|
||||
end
|
||||
end
|
||||
|
||||
Client:Message( HostMessage, self.MSG.TIME, Mission.Name .. "/STAGELANDING.EXEC." .. Host, Host, 10 )
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function STAGELANDING:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
Task.CurrentLandingZoneName = routines.IsUnitNearZonesRadius( Client:GetClientGroupDCSUnit(), Task.LandingZones.LandingZoneNames, 500 )
|
||||
if Task.CurrentLandingZoneName then
|
||||
|
||||
-- Client is in de landing zone.
|
||||
self:T( Task.CurrentLandingZoneName )
|
||||
|
||||
Task.CurrentLandingZone = Task.LandingZones.LandingZones[Task.CurrentLandingZoneName].CargoZone
|
||||
Task.CurrentCargoZone = Task.LandingZones.LandingZones[Task.CurrentLandingZoneName]
|
||||
|
||||
if Task.CurrentCargoZone then
|
||||
if not Task.Signalled then
|
||||
Task.Signalled = Task.CurrentCargoZone:Signal()
|
||||
end
|
||||
end
|
||||
else
|
||||
if Task.CurrentLandingZone then
|
||||
Task.CurrentLandingZone = nil
|
||||
end
|
||||
if Task.CurrentCargoZone then
|
||||
Task.CurrentCargoZone = nil
|
||||
end
|
||||
Task.Signalled = false
|
||||
Task:RemoveCargoMenus( Client )
|
||||
self:T( -1 )
|
||||
return -1
|
||||
end
|
||||
|
||||
|
||||
local DCSUnitVelocityVec3 = Client:GetClientGroupDCSUnit():getVelocity()
|
||||
local DCSUnitVelocity = ( DCSUnitVelocityVec3.x ^2 + DCSUnitVelocityVec3.y ^2 + DCSUnitVelocityVec3.z ^2 ) ^ 0.5
|
||||
|
||||
local DCSUnitPointVec3 = Client:GetClientGroupDCSUnit():getPoint()
|
||||
local LandHeight = land.getHeight( { x = DCSUnitPointVec3.x, y = DCSUnitPointVec3.z } )
|
||||
local DCSUnitHeight = DCSUnitPointVec3.y - LandHeight
|
||||
|
||||
self:T( { Task.IsLandingRequired, Client:GetClientGroupDCSUnit():inAir() } )
|
||||
if Task.IsLandingRequired and not Client:GetClientGroupDCSUnit():inAir() then
|
||||
self:T( 1 )
|
||||
Task.IsInAirTestRequired = true
|
||||
return 1
|
||||
end
|
||||
|
||||
self:T( { DCSUnitVelocity, DCSUnitHeight, LandHeight, Task.CurrentCargoZone.SignalHeight } )
|
||||
if Task.IsLandingRequired and DCSUnitVelocity <= 0.05 and DCSUnitHeight <= Task.CurrentCargoZone.SignalHeight then
|
||||
self:T( 1 )
|
||||
Task.IsInAirTestRequired = false
|
||||
return 1
|
||||
end
|
||||
|
||||
self:T( 0 )
|
||||
return 0
|
||||
end
|
||||
|
||||
STAGELANDED = {
|
||||
ClassName = "STAGELANDED",
|
||||
MSG = { ID = "Land", TIME = 10 },
|
||||
Name = "Landed",
|
||||
MenusAdded = false
|
||||
}
|
||||
|
||||
function STAGELANDED:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'CLIENT'
|
||||
return self
|
||||
end
|
||||
|
||||
function STAGELANDED:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
if Task.IsLandingRequired then
|
||||
|
||||
local Host = "Command"
|
||||
if Task.HostUnitName then
|
||||
Host = Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")"
|
||||
else
|
||||
if Client:IsMultiSeated() then
|
||||
Host = "Co-Pilot"
|
||||
end
|
||||
end
|
||||
|
||||
Client:Message( 'You have landed within the landing zone. Use the radio menu (F10) to ' .. Task.TEXT[1] .. ' the ' .. Task.CargoType .. '.',
|
||||
self.MSG.TIME, Mission.Name .. "/STAGELANDED.EXEC" .. Host, Host )
|
||||
|
||||
if not self.MenusAdded then
|
||||
Task.Cargo = nil
|
||||
Task:RemoveCargoMenus( Client )
|
||||
Task:AddCargoMenus( Client, CARGOS, 250 )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
function STAGELANDED:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
if not routines.IsUnitNearZonesRadius( Client:GetClientGroupDCSUnit(), Task.CurrentLandingZoneName, 500 ) then
|
||||
self:T( "Client is not anymore in the landing zone, go back to stage Route, and remove cargo menus." )
|
||||
Task.Signalled = false
|
||||
Task:RemoveCargoMenus( Client )
|
||||
self:T( -2 )
|
||||
return -2
|
||||
end
|
||||
|
||||
local DCSUnitVelocityVec3 = Client:GetClientGroupDCSUnit():getVelocity()
|
||||
local DCSUnitVelocity = ( DCSUnitVelocityVec3.x ^2 + DCSUnitVelocityVec3.y ^2 + DCSUnitVelocityVec3.z ^2 ) ^ 0.5
|
||||
|
||||
local DCSUnitPointVec3 = Client:GetClientGroupDCSUnit():getPoint()
|
||||
local LandHeight = land.getHeight( { x = DCSUnitPointVec3.x, y = DCSUnitPointVec3.z } )
|
||||
local DCSUnitHeight = DCSUnitPointVec3.y - LandHeight
|
||||
|
||||
self:T( { Task.IsLandingRequired, Client:GetClientGroupDCSUnit():inAir() } )
|
||||
if Task.IsLandingRequired and Task.IsInAirTestRequired == true and Client:GetClientGroupDCSUnit():inAir() then
|
||||
self:T( "Client went back in the air. Go back to stage Landing." )
|
||||
self:T( -1 )
|
||||
return -1
|
||||
end
|
||||
|
||||
self:T( { DCSUnitVelocity, DCSUnitHeight, LandHeight, Task.CurrentCargoZone.SignalHeight } )
|
||||
if Task.IsLandingRequired and Task.IsInAirTestRequired == false and DCSUnitVelocity >= 2 and DCSUnitHeight >= Task.CurrentCargoZone.SignalHeight then
|
||||
self:T( "It seems the Client went back in the air and over the boundary limits. Go back to stage Landing." )
|
||||
self:T( -1 )
|
||||
return -1
|
||||
end
|
||||
|
||||
-- Wait until cargo is selected from the menu.
|
||||
if Task.IsLandingRequired then
|
||||
if not Task.Cargo then
|
||||
self:T( 0 )
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
self:T( 1 )
|
||||
return 1
|
||||
end
|
||||
|
||||
STAGEUNLOAD = {
|
||||
ClassName = "STAGEUNLOAD",
|
||||
MSG = { ID = "Unload", TIME = 10 },
|
||||
Name = "Unload"
|
||||
}
|
||||
|
||||
function STAGEUNLOAD:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'CLIENT'
|
||||
return self
|
||||
end
|
||||
|
||||
--- Coordinate UnLoading
|
||||
-- @param #STAGEUNLOAD self
|
||||
-- @param Mission#MISSION Mission
|
||||
-- @param Client#CLIENT Client
|
||||
-- @param Task#TASK Task
|
||||
function STAGEUNLOAD:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
if Client:IsMultiSeated() then
|
||||
Client:Message( 'The ' .. Task.CargoType .. ' are being ' .. Task.TEXT[2] .. ' within the landing zone. Wait until the helicopter is ' .. Task.TEXT[3] .. '.',
|
||||
self.MSG.TIME, Mission.Name .. "/StageUnLoad", "Co-Pilot" )
|
||||
else
|
||||
Client:Message( 'You are unloading the ' .. Task.CargoType .. ' ' .. Task.TEXT[2] .. ' within the landing zone. Wait until the helicopter is ' .. Task.TEXT[3] .. '.',
|
||||
self.MSG.TIME, Mission.Name .. "/StageUnLoad", "Command" )
|
||||
end
|
||||
Task:RemoveCargoMenus( Client )
|
||||
end
|
||||
|
||||
function STAGEUNLOAD:Executing( Mission, Client, Task )
|
||||
self:F()
|
||||
env.info( 'STAGEUNLOAD:Executing() Task.Cargo.CargoName = ' .. Task.Cargo.CargoName )
|
||||
|
||||
local TargetZoneName
|
||||
|
||||
if Task.TargetZoneName then
|
||||
TargetZoneName = Task.TargetZoneName
|
||||
else
|
||||
TargetZoneName = Task.CurrentLandingZoneName
|
||||
end
|
||||
|
||||
if Task.Cargo:UnLoad( Client, TargetZoneName ) then
|
||||
Task.ExecuteStage = _TransportExecuteStage.SUCCESS
|
||||
if Mission.MissionReportFlash then
|
||||
Client:ShowCargo()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Validate UnLoading
|
||||
-- @param #STAGEUNLOAD self
|
||||
-- @param Mission#MISSION Mission
|
||||
-- @param Client#CLIENT Client
|
||||
-- @param Task#TASK Task
|
||||
function STAGEUNLOAD:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
env.info( 'STAGEUNLOAD:Validate()' )
|
||||
|
||||
if routines.IsUnitNearZonesRadius( Client:GetClientGroupDCSUnit(), Task.CurrentLandingZoneName, 500 ) then
|
||||
else
|
||||
Task.ExecuteStage = _TransportExecuteStage.FAILED
|
||||
Task:RemoveCargoMenus( Client )
|
||||
if Client:IsMultiSeated() then
|
||||
Client:Message( 'The ' .. Task.CargoType .. " haven't been successfully " .. Task.TEXT[3] .. ' within the landing zone. Task and mission has failed.',
|
||||
_TransportStageMsgTime.DONE, Mission.Name .. "/StageFailure", "Co-Pilot" )
|
||||
else
|
||||
Client:Message( 'The ' .. Task.CargoType .. " haven't been successfully " .. Task.TEXT[3] .. ' within the landing zone. Task and mission has failed.',
|
||||
_TransportStageMsgTime.DONE, Mission.Name .. "/StageFailure", "Command" )
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
if not Client:GetClientGroupDCSUnit():inAir() then
|
||||
else
|
||||
Task.ExecuteStage = _TransportExecuteStage.FAILED
|
||||
Task:RemoveCargoMenus( Client )
|
||||
if Client:IsMultiSeated() then
|
||||
Client:Message( 'The ' .. Task.CargoType .. " haven't been successfully " .. Task.TEXT[3] .. ' within the landing zone. Task and mission has failed.',
|
||||
_TransportStageMsgTime.DONE, Mission.Name .. "/StageFailure", "Co-Pilot" )
|
||||
else
|
||||
Client:Message( 'The ' .. Task.CargoType .. " haven't been successfully " .. Task.TEXT[3] .. ' within the landing zone. Task and mission has failed.',
|
||||
_TransportStageMsgTime.DONE, Mission.Name .. "/StageFailure", "Command" )
|
||||
end
|
||||
return 1
|
||||
end
|
||||
|
||||
if Task.ExecuteStage == _TransportExecuteStage.SUCCESS then
|
||||
if Client:IsMultiSeated() then
|
||||
Client:Message( 'The ' .. Task.CargoType .. ' have been sucessfully ' .. Task.TEXT[3] .. ' within the landing zone.', _TransportStageMsgTime.DONE, Mission.Name .. "/Stage", "Co-Pilot" )
|
||||
else
|
||||
Client:Message( 'The ' .. Task.CargoType .. ' have been sucessfully ' .. Task.TEXT[3] .. ' within the landing zone.', _TransportStageMsgTime.DONE, Mission.Name .. "/Stage", "Command" )
|
||||
end
|
||||
Task:RemoveCargoMenus( Client )
|
||||
Task.MissionTask:AddGoalCompletion( Task.MissionTask.GoalVerb, Task.CargoName, 1 ) -- We set the cargo as one more goal completed in the mission.
|
||||
return 1
|
||||
end
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
STAGELOAD = {
|
||||
ClassName = "STAGELOAD",
|
||||
MSG = { ID = "Load", TIME = 10 },
|
||||
Name = "Load"
|
||||
}
|
||||
|
||||
function STAGELOAD:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'CLIENT'
|
||||
return self
|
||||
end
|
||||
|
||||
function STAGELOAD:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
if not Task.IsSlingLoad then
|
||||
|
||||
local Host = "Command"
|
||||
if Task.HostUnitName then
|
||||
Host = Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")"
|
||||
else
|
||||
if Client:IsMultiSeated() then
|
||||
Host = "Co-Pilot"
|
||||
end
|
||||
end
|
||||
|
||||
Client:Message( 'The ' .. Task.CargoType .. ' are being ' .. Task.TEXT[2] .. ' within the landing zone. Wait until the helicopter is ' .. Task.TEXT[3] .. '.',
|
||||
_TransportStageMsgTime.EXECUTING, Mission.Name .. "/STAGELOAD.EXEC." .. Host, Host )
|
||||
|
||||
-- Route the cargo to the Carrier
|
||||
|
||||
Task.Cargo:OnBoard( Client, Task.CurrentCargoZone, Task.OnBoardSide )
|
||||
Task.ExecuteStage = _TransportExecuteStage.EXECUTING
|
||||
else
|
||||
Task.ExecuteStage = _TransportExecuteStage.EXECUTING
|
||||
end
|
||||
end
|
||||
|
||||
function STAGELOAD:Executing( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
-- If the Cargo is ready to be loaded, load it into the Client.
|
||||
|
||||
local Host = "Command"
|
||||
if Task.HostUnitName then
|
||||
Host = Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")"
|
||||
else
|
||||
if Client:IsMultiSeated() then
|
||||
Host = "Co-Pilot"
|
||||
end
|
||||
end
|
||||
|
||||
if not Task.IsSlingLoad then
|
||||
self:T( Task.Cargo.CargoName)
|
||||
|
||||
if Task.Cargo:OnBoarded( Client, Task.CurrentCargoZone ) then
|
||||
|
||||
-- Load the Cargo onto the Client
|
||||
Task.Cargo:Load( Client )
|
||||
|
||||
-- Message to the pilot that cargo has been loaded.
|
||||
Client:Message( "The cargo " .. Task.Cargo.CargoName .. " has been loaded in our helicopter.",
|
||||
20, Mission.Name .. "/STAGELANDING.LOADING1." .. Host, Host )
|
||||
Task.ExecuteStage = _TransportExecuteStage.SUCCESS
|
||||
|
||||
Client:ShowCargo()
|
||||
end
|
||||
else
|
||||
Client:Message( "Hook the " .. Task.CargoNames .. " onto the helicopter " .. Task.TEXT[3] .. " within the landing zone.",
|
||||
_TransportStageMsgTime.EXECUTING, Mission.Name .. "/STAGELOAD.LOADING.1." .. Host, Host , 10 )
|
||||
for CargoID, Cargo in pairs( CARGOS ) do
|
||||
self:T( "Cargo.CargoName = " .. Cargo.CargoName )
|
||||
|
||||
if Cargo:IsSlingLoad() then
|
||||
local CargoStatic = StaticObject.getByName( Cargo.CargoStaticName )
|
||||
if CargoStatic then
|
||||
self:T( "Cargo is found in the DCS simulator.")
|
||||
local CargoStaticPosition = CargoStatic:getPosition().p
|
||||
self:T( "Cargo Position x = " .. CargoStaticPosition.x .. ", y = " .. CargoStaticPosition.y .. ", z = " .. CargoStaticPosition.z )
|
||||
local CargoStaticHeight = routines.GetUnitHeight( CargoStatic )
|
||||
if CargoStaticHeight > 5 then
|
||||
self:T( "Cargo is airborne.")
|
||||
Cargo:StatusLoaded()
|
||||
Task.Cargo = Cargo
|
||||
Client:Message( 'The Cargo has been successfully hooked onto the helicopter and is now being sling loaded. Fly outside the landing zone.',
|
||||
self.MSG.TIME, Mission.Name .. "/STAGELANDING.LOADING.2." .. Host, Host )
|
||||
Task.ExecuteStage = _TransportExecuteStage.SUCCESS
|
||||
break
|
||||
end
|
||||
else
|
||||
self:T( "Cargo not found in the DCS simulator." )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function STAGELOAD:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
self:T( "Task.CurrentLandingZoneName = " .. Task.CurrentLandingZoneName )
|
||||
|
||||
local Host = "Command"
|
||||
if Task.HostUnitName then
|
||||
Host = Task.HostUnitName .. " (" .. Task.HostUnitTypeName .. ")"
|
||||
else
|
||||
if Client:IsMultiSeated() then
|
||||
Host = "Co-Pilot"
|
||||
end
|
||||
end
|
||||
|
||||
if not Task.IsSlingLoad then
|
||||
if not routines.IsUnitNearZonesRadius( Client:GetClientGroupDCSUnit(), Task.CurrentLandingZoneName, 500 ) then
|
||||
Task:RemoveCargoMenus( Client )
|
||||
Task.ExecuteStage = _TransportExecuteStage.FAILED
|
||||
Task.CargoName = nil
|
||||
Client:Message( "The " .. Task.CargoType .. " loading has been aborted. You flew outside the pick-up zone while loading. ",
|
||||
self.MSG.TIME, Mission.Name .. "/STAGELANDING.VALIDATE.1." .. Host, Host )
|
||||
self:T( -1 )
|
||||
return -1
|
||||
end
|
||||
|
||||
local DCSUnitVelocityVec3 = Client:GetClientGroupDCSUnit():getVelocity()
|
||||
local DCSUnitVelocity = ( DCSUnitVelocityVec3.x ^2 + DCSUnitVelocityVec3.y ^2 + DCSUnitVelocityVec3.z ^2 ) ^ 0.5
|
||||
|
||||
local DCSUnitPointVec3 = Client:GetClientGroupDCSUnit():getPoint()
|
||||
local LandHeight = land.getHeight( { x = DCSUnitPointVec3.x, y = DCSUnitPointVec3.z } )
|
||||
local DCSUnitHeight = DCSUnitPointVec3.y - LandHeight
|
||||
|
||||
self:T( { Task.IsLandingRequired, Client:GetClientGroupDCSUnit():inAir() } )
|
||||
if Task.IsLandingRequired and Task.IsInAirTestRequired == true and Client:GetClientGroupDCSUnit():inAir() then
|
||||
Task:RemoveCargoMenus( Client )
|
||||
Task.ExecuteStage = _TransportExecuteStage.FAILED
|
||||
Task.CargoName = nil
|
||||
Client:Message( "The " .. Task.CargoType .. " loading has been aborted. Re-start the " .. Task.TEXT[3] .. " process. Don't fly outside the pick-up zone.",
|
||||
self.MSG.TIME, Mission.Name .. "/STAGELANDING.VALIDATE.1." .. Host, Host )
|
||||
self:T( -1 )
|
||||
return -1
|
||||
end
|
||||
|
||||
self:T( { DCSUnitVelocity, DCSUnitHeight, LandHeight, Task.CurrentCargoZone.SignalHeight } )
|
||||
if Task.IsLandingRequired and Task.IsInAirTestRequired == false and DCSUnitVelocity >= 2 and DCSUnitHeight >= Task.CurrentCargoZone.SignalHeight then
|
||||
Task:RemoveCargoMenus( Client )
|
||||
Task.ExecuteStage = _TransportExecuteStage.FAILED
|
||||
Task.CargoName = nil
|
||||
Client:Message( "The " .. Task.CargoType .. " loading has been aborted. Re-start the " .. Task.TEXT[3] .. " process. Don't fly outside the pick-up zone.",
|
||||
self.MSG.TIME, Mission.Name .. "/STAGELANDING.VALIDATE.1." .. Host, Host )
|
||||
self:T( -1 )
|
||||
return -1
|
||||
end
|
||||
|
||||
if Task.ExecuteStage == _TransportExecuteStage.SUCCESS then
|
||||
Task:RemoveCargoMenus( Client )
|
||||
Client:Message( "Good Job. The " .. Task.CargoType .. " has been sucessfully " .. Task.TEXT[3] .. " within the landing zone.",
|
||||
self.MSG.TIME, Mission.Name .. "/STAGELANDING.VALIDATE.3." .. Host, Host )
|
||||
Task.MissionTask:AddGoalCompletion( Task.MissionTask.GoalVerb, Task.CargoName, 1 )
|
||||
self:T( 1 )
|
||||
return 1
|
||||
end
|
||||
|
||||
else
|
||||
if Task.ExecuteStage == _TransportExecuteStage.SUCCESS then
|
||||
CargoStatic = StaticObject.getByName( Task.Cargo.CargoStaticName )
|
||||
if CargoStatic and not routines.IsStaticInZones( CargoStatic, Task.CurrentLandingZoneName ) then
|
||||
Client:Message( "Good Job. The " .. Task.CargoType .. " has been sucessfully " .. Task.TEXT[3] .. " and flown outside of the landing zone.",
|
||||
self.MSG.TIME, Mission.Name .. "/STAGELANDING.VALIDATE.4." .. Host, Host )
|
||||
Task.MissionTask:AddGoalCompletion( Task.MissionTask.GoalVerb, Task.Cargo.CargoName, 1 )
|
||||
self:T( 1 )
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
self:T( 0 )
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
STAGEDONE = {
|
||||
ClassName = "STAGEDONE",
|
||||
MSG = { ID = "Done", TIME = 10 },
|
||||
Name = "Done"
|
||||
}
|
||||
|
||||
function STAGEDONE:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'AI'
|
||||
return self
|
||||
end
|
||||
|
||||
function STAGEDONE:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
end
|
||||
|
||||
function STAGEDONE:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
Task:Done()
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
STAGEARRIVE = {
|
||||
ClassName = "STAGEARRIVE",
|
||||
MSG = { ID = "Arrive", TIME = 10 },
|
||||
Name = "Arrive"
|
||||
}
|
||||
|
||||
function STAGEARRIVE:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'CLIENT'
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Execute Arrival
|
||||
-- @param #STAGEARRIVE self
|
||||
-- @param Mission#MISSION Mission
|
||||
-- @param Client#CLIENT Client
|
||||
-- @param Task#TASK Task
|
||||
function STAGEARRIVE:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
if Client:IsMultiSeated() then
|
||||
Client:Message( 'We have arrived at ' .. Task.CurrentLandingZoneName .. ".", self.MSG.TIME, Mission.Name .. "/Stage", "Co-Pilot" )
|
||||
else
|
||||
Client:Message( 'We have arrived at ' .. Task.CurrentLandingZoneName .. ".", self.MSG.TIME, Mission.Name .. "/Stage", "Command" )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function STAGEARRIVE:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
Task.CurrentLandingZoneID = routines.IsUnitInZones( Client:GetClientGroupDCSUnit(), Task.LandingZones )
|
||||
if ( Task.CurrentLandingZoneID ) then
|
||||
else
|
||||
return -1
|
||||
end
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
STAGEGROUPSDESTROYED = {
|
||||
ClassName = "STAGEGROUPSDESTROYED",
|
||||
DestroyGroupSize = -1,
|
||||
Frequency = STAGE.FREQUENCY.REPEAT,
|
||||
MSG = { ID = "DestroyGroup", TIME = 10 },
|
||||
Name = "GroupsDestroyed"
|
||||
}
|
||||
|
||||
function STAGEGROUPSDESTROYED:New()
|
||||
local self = BASE:Inherit( self, STAGE:New() )
|
||||
self:F()
|
||||
self.StageType = 'AI'
|
||||
return self
|
||||
end
|
||||
|
||||
--function STAGEGROUPSDESTROYED:Execute( Mission, Client, Task )
|
||||
--
|
||||
-- Client:Message( 'Task: Still ' .. DestroyGroupSize .. " of " .. Task.DestroyGroupCount .. " " .. Task.DestroyGroupType .. " to be destroyed!", self.MSG.TIME, Mission.Name .. "/Stage" )
|
||||
--
|
||||
--end
|
||||
|
||||
function STAGEGROUPSDESTROYED:Validate( Mission, Client, Task )
|
||||
self:F()
|
||||
|
||||
if Task.MissionTask:IsGoalReached() then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function STAGEGROUPSDESTROYED:Execute( Mission, Client, Task )
|
||||
self:F()
|
||||
self:T( { Task.ClassName, Task.Destroyed } )
|
||||
--env.info( 'Event Table Task = ' .. tostring(Task) )
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--[[
|
||||
_TransportStage: Defines the different stages of which of transport missions can be in. This table is internal and is used to control the sequence of messages, actions and flow.
|
||||
|
||||
- _TransportStage.START
|
||||
- _TransportStage.ROUTE
|
||||
- _TransportStage.LAND
|
||||
- _TransportStage.EXECUTE
|
||||
- _TransportStage.DONE
|
||||
- _TransportStage.REMOVE
|
||||
--]]
|
||||
_TransportStage = {
|
||||
HOLD = "HOLD",
|
||||
START = "START",
|
||||
ROUTE = "ROUTE",
|
||||
LANDING = "LANDING",
|
||||
LANDED = "LANDED",
|
||||
EXECUTING = "EXECUTING",
|
||||
LOAD = "LOAD",
|
||||
UNLOAD = "UNLOAD",
|
||||
DONE = "DONE",
|
||||
NEXT = "NEXT"
|
||||
}
|
||||
|
||||
_TransportStageMsgTime = {
|
||||
HOLD = 10,
|
||||
START = 60,
|
||||
ROUTE = 5,
|
||||
LANDING = 10,
|
||||
LANDED = 30,
|
||||
EXECUTING = 30,
|
||||
LOAD = 30,
|
||||
UNLOAD = 30,
|
||||
DONE = 30,
|
||||
NEXT = 0
|
||||
}
|
||||
|
||||
_TransportStageTime = {
|
||||
HOLD = 10,
|
||||
START = 5,
|
||||
ROUTE = 5,
|
||||
LANDING = 1,
|
||||
LANDED = 1,
|
||||
EXECUTING = 5,
|
||||
LOAD = 5,
|
||||
UNLOAD = 5,
|
||||
DONE = 1,
|
||||
NEXT = 0
|
||||
}
|
||||
|
||||
_TransportStageAction = {
|
||||
REPEAT = -1,
|
||||
NONE = 0,
|
||||
ONCE = 1
|
||||
}
|
||||
246
Moose Development/Moose/StatHandler.lua
Normal file
246
Moose Development/Moose/StatHandler.lua
Normal file
@@ -0,0 +1,246 @@
|
||||
--- Provides a logging of statistics in a running DCS Mission.
|
||||
-- @script eStatHandler
|
||||
|
||||
Include.File( "Routines" )
|
||||
|
||||
|
||||
--Handler table
|
||||
local eStatHandler = {}
|
||||
local _StatRunID
|
||||
|
||||
--Neccessary tables for string instead of integers
|
||||
SETCoalition =
|
||||
{
|
||||
[1] = "red",
|
||||
[2] = "blue",
|
||||
}
|
||||
|
||||
SETGroupCat =
|
||||
{
|
||||
[1] = "AIRPLANE",
|
||||
[2] = "HELICOPTER",
|
||||
[3] = "GROUND",
|
||||
[4] = "SHIP",
|
||||
[5] = "STRUCTURE",
|
||||
}
|
||||
|
||||
SETWeaponCatName =
|
||||
{
|
||||
[0] = "SHELL",
|
||||
[1] = "MISSILE",
|
||||
[2] = "ROCKET",
|
||||
[3] = "BOMB",
|
||||
}
|
||||
|
||||
wEvent = {
|
||||
"S_EVENT_SHOT",
|
||||
"S_EVENT_HIT",
|
||||
"S_EVENT_TAKEOFF",
|
||||
"S_EVENT_LAND",
|
||||
"S_EVENT_CRASH",
|
||||
"S_EVENT_EJECTION",
|
||||
"S_EVENT_REFUELING",
|
||||
"S_EVENT_DEAD",
|
||||
"S_EVENT_PILOT_DEAD",
|
||||
"S_EVENT_BASE_CAPTURED",
|
||||
"S_EVENT_MISSION_START",
|
||||
"S_EVENT_MISSION_END",
|
||||
"S_EVENT_TOOK_CONTROL",
|
||||
"S_EVENT_REFUELING_STOP",
|
||||
"S_EVENT_BIRTH",
|
||||
"S_EVENT_HUMAN_FAILURE",
|
||||
"S_EVENT_ENGINE_STARTUP",
|
||||
"S_EVENT_ENGINE_SHUTDOWN",
|
||||
"S_EVENT_PLAYER_ENTER_UNIT",
|
||||
"S_EVENT_PLAYER_LEAVE_UNIT",
|
||||
"S_EVENT_PLAYER_COMMENT",
|
||||
"S_EVENT_SHOOTING_START",
|
||||
"S_EVENT_SHOOTING_END",
|
||||
"S_EVENT_MAX",
|
||||
}
|
||||
|
||||
statEventsTable = {}
|
||||
|
||||
|
||||
function SecondsToClock(sSeconds)
|
||||
local nSeconds = sSeconds
|
||||
if nSeconds == 0 then
|
||||
--return nil;
|
||||
return "00:00:00";
|
||||
else
|
||||
nHours = string.format("%02.f", math.floor(nSeconds/3600));
|
||||
nMins = string.format("%02.f", math.floor(nSeconds/60 - (nHours*60)));
|
||||
nSecs = string.format("%02.f", math.floor(nSeconds - nHours*3600 - nMins *60));
|
||||
return nHours..":"..nMins..":"..nSecs
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function eStatHandler:onEvent(e)
|
||||
local InitID_ = ""
|
||||
local InitName = ""
|
||||
local WorldEvent = wEvent[e.id]
|
||||
local InitCoa = ""
|
||||
local InitGroupCat = ""
|
||||
local InitType = ""
|
||||
local InitPlayer = ""
|
||||
local eWeaponCat = ""
|
||||
local eWeaponName = ""
|
||||
local TargID_ = ""
|
||||
local TargName = ""
|
||||
local TargType = ""
|
||||
local TargPlayer = ""
|
||||
local TargCoa = ""
|
||||
local TargGroupCat = ""
|
||||
|
||||
if e.initiator and Object.getCategory(e.initiator) == Object.Category.UNIT then
|
||||
--Initiator variables
|
||||
local InitGroup = e.initiator:getGroup()
|
||||
InitID_ = e.initiator.id_
|
||||
if e.initiator:getName() then
|
||||
InitName = e.initiator:getName()
|
||||
end
|
||||
if InitGroup:getCoalition() then
|
||||
InitCoa = SETCoalition[InitGroup:getCoalition()]
|
||||
end
|
||||
if InitGroup:getCategory() then
|
||||
InitGroupCat = SETGroupCat[InitGroup:getCategory() + 1]
|
||||
end
|
||||
InitType = e.initiator:getTypeName()
|
||||
|
||||
--Get initiator player name or AI if NIL
|
||||
if e.initiator:getPlayerName() == nil then
|
||||
InitPlayer = "AI"
|
||||
else
|
||||
InitPlayer = e.initiator:getPlayerName()
|
||||
end
|
||||
else
|
||||
if e.initiator then
|
||||
local InitGroup = e.initiator:getGroup()
|
||||
InitID_ = e.initiator.id_
|
||||
if e.initiator:getName() then
|
||||
InitName = e.initiator:getName()
|
||||
end
|
||||
InitCoa = SETCoalition[InitGroup:getCoalition()]
|
||||
InitGroupCat = SETGroupCat[InitGroup:getCategory() + 1]
|
||||
InitType = e.initiator:getTypeName()
|
||||
|
||||
--Get initiator player name or AI if NIL
|
||||
if e.initiator:getPlayerName() == nil then
|
||||
InitPlayer = "AI"
|
||||
else
|
||||
InitPlayer = e.initiator:getPlayerName()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--Weapon variables
|
||||
if e.weapon == nil then
|
||||
eWeaponCat = ""
|
||||
eWeaponName = ""
|
||||
else
|
||||
local eWeaponDesc = e.weapon:getDesc()
|
||||
eWeaponCat = SETWeaponCatName[eWeaponDesc.category]
|
||||
eWeaponName = eWeaponDesc.displayName
|
||||
end
|
||||
|
||||
--Target variables
|
||||
if e.target == nil then
|
||||
TargID_ = ""
|
||||
TargName = ""
|
||||
TargType = ""
|
||||
TargPlayer = ""
|
||||
TargCoa = ""
|
||||
TargGroupCat = ""
|
||||
elseif Object.getCategory(e.target) == Object.Category.UNIT then
|
||||
local TargGroup = e.target:getGroup()
|
||||
TargID_ = e.target.id_
|
||||
if e.target:getName() then
|
||||
TargName = e.target:getName()
|
||||
end
|
||||
TargType = e.target:getTypeName()
|
||||
TargCoa = SETCoalition[TargGroup:getCoalition()]
|
||||
TargGroupCat = SETGroupCat[TargGroup:getCategory() + 1]
|
||||
|
||||
--Get target player name or AI if NIL
|
||||
if not e.target:getPlayerName() then
|
||||
TargPlayer = "AI"
|
||||
else
|
||||
TargPlayer = e.target:getPlayerName()
|
||||
end
|
||||
else
|
||||
TargType = e.target:getTypeName()
|
||||
TargID_ = ""
|
||||
TargName = ""
|
||||
TargPlayer = ""
|
||||
TargCoa = ""
|
||||
TargGroupCat = ""
|
||||
end
|
||||
|
||||
--write events to table
|
||||
statEventsTable[#statEventsTable + 1] =
|
||||
{
|
||||
[1] = _StatRunID,
|
||||
[2] = SecondsToClock(timer.getTime()),
|
||||
[3] = WorldEvent,
|
||||
[4] = InitID_,
|
||||
[5] = InitName,
|
||||
[6] = InitCoa,
|
||||
[7] = InitGroupCat,
|
||||
[8] = InitType,
|
||||
[9] = InitPlayer,
|
||||
[10] = eWeaponCat,
|
||||
[11] = eWeaponName,
|
||||
[12] = TargID_,
|
||||
[13] = TargName,
|
||||
[14] = TargCoa,
|
||||
[15] = TargGroupCat,
|
||||
[16] = TargType,
|
||||
[17] = TargPlayer,
|
||||
}
|
||||
env.info( 'Event: ' .. _StatRunID .. '~ ' .. SecondsToClock(timer.getTime()) .. '~ ' .. WorldEvent .. '~ ' .. InitID_ .. '~ ' .. InitName .. '~ ' .. InitCoa .. '~ ' .. InitGroupCat .. '~ ' .. InitType .. '~ ' .. InitPlayer ..
|
||||
'~ ' .. eWeaponCat .. '~ ' .. eWeaponName .. '~ ' .. TargID_ .. '~ ' .. TargName .. '~ ' .. TargCoa .. '~ ' .. TargGroupCat .. '~ ' .. TargType .. '~ ' .. TargPlayer )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
do
|
||||
|
||||
local StatFile,err
|
||||
|
||||
|
||||
function StatOpen()
|
||||
local fdir = lfs.writedir() .. [[Logs\]] .. "Events_" .. os.date( "%Y-%m-%d_%H-%M-%S" ) .. ".csv"
|
||||
StatFile,err = io.open(fdir,"w+")
|
||||
if not StatFile then
|
||||
local errmsg = 'Error: No Logs folder found in the User\\Saved Games\\DCS\\Logs directory...' .. 'Save_stat . sample: C:\\Users\\youname\\Saved Games\\DCS\\Logs'
|
||||
trigger.action.outText(errmsg, 10)
|
||||
return print(err)
|
||||
end
|
||||
StatFile:write("RunID~Time~Event~Initiator ID~Initiator Name~Initiator Coalition~Initiator Group Category~Initiator Type~Initiator Player~Weapon Category~Weapon Name~Target ID~Target Name~Target Coalition~Target Group Category~Target Type~Target Player\n")
|
||||
|
||||
_StatRunID = os.date("%y-%m-%d_%H-%M-%S")
|
||||
routines.scheduleFunction( StatSave, { }, timer.getTime() + 1, 1)
|
||||
end
|
||||
|
||||
function StatSave()
|
||||
--write statistic information to file
|
||||
for Index, eDetails in ipairs(statEventsTable) do
|
||||
for eInfoName, eInfoData in ipairs(eDetails) do
|
||||
StatFile:write(eInfoData.."~")
|
||||
end
|
||||
StatFile:write("\n")
|
||||
end
|
||||
statEventsTable = {}
|
||||
end
|
||||
|
||||
function StatClose()
|
||||
StatFile:close()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
world.addEventHandler(eStatHandler)
|
||||
StatOpen()
|
||||
|
||||
465
Moose Development/Moose/Task.lua
Normal file
465
Moose Development/Moose/Task.lua
Normal file
@@ -0,0 +1,465 @@
|
||||
--- The TASK Classes define major end-to-end activities within a MISSION. The TASK Class is the Master Class to orchestrate these activities. From this class, many concrete TASK classes are inherited.
|
||||
-- @module TASK
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Mission" )
|
||||
Include.File( "Client" )
|
||||
Include.File( "Stage" )
|
||||
|
||||
--- The TASK class
|
||||
-- @type TASK
|
||||
-- @extends Base#BASE
|
||||
TASK = {
|
||||
|
||||
-- Defines the different signal types with a Task.
|
||||
SIGNAL = {
|
||||
COLOR = {
|
||||
RED = { ID = 1, COLOR = trigger.smokeColor.Red, TEXT = "A red" },
|
||||
GREEN = { ID = 2, COLOR = trigger.smokeColor.Green, TEXT = "A green" },
|
||||
BLUE = { ID = 3, COLOR = trigger.smokeColor.Blue, TEXT = "A blue" },
|
||||
WHITE = { ID = 4, COLOR = trigger.smokeColor.White, TEXT = "A white" },
|
||||
ORANGE = { ID = 5, COLOR = trigger.smokeColor.Orange, TEXT = "An orange" }
|
||||
},
|
||||
TYPE = {
|
||||
SMOKE = { ID = 1, TEXT = "smoke" },
|
||||
FLARE = { ID = 2, TEXT = "flare" }
|
||||
}
|
||||
},
|
||||
ClassName = "TASK",
|
||||
Mission = {}, -- Owning mission of the Task
|
||||
Name = '',
|
||||
Stages = {},
|
||||
Stage = {},
|
||||
Cargos = {
|
||||
InitCargos = {},
|
||||
LoadCargos = {}
|
||||
},
|
||||
LandingZones = {
|
||||
LandingZoneNames = {},
|
||||
LandingZones = {}
|
||||
},
|
||||
ActiveStage = 0,
|
||||
TaskDone = false,
|
||||
TaskFailed = false,
|
||||
GoalTasks = {}
|
||||
}
|
||||
|
||||
--- Instantiates a new TASK Base. Should never be used. Interface Class.
|
||||
-- @return TASK
|
||||
function TASK:New()
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F()
|
||||
|
||||
-- assign Task default values during construction
|
||||
self.TaskBriefing = "Task: No Task."
|
||||
self.Time = timer.getTime()
|
||||
self.ExecuteStage = _TransportExecuteStage.NONE
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function TASK:SetStage( StageSequenceIncrement )
|
||||
self:F( { StageSequenceIncrement } )
|
||||
|
||||
local Valid = false
|
||||
if StageSequenceIncrement ~= 0 then
|
||||
self.ActiveStage = self.ActiveStage + StageSequenceIncrement
|
||||
if 1 <= self.ActiveStage and self.ActiveStage <= #self.Stages then
|
||||
self.Stage = self.Stages[self.ActiveStage]
|
||||
self:T( { self.Stage.Name } )
|
||||
self.Frequency = self.Stage.Frequency
|
||||
Valid = true
|
||||
else
|
||||
Valid = false
|
||||
env.info( "TASK:SetStage() self.ActiveStage is smaller or larger than self.Stages array. self.ActiveStage = " .. self.ActiveStage )
|
||||
end
|
||||
end
|
||||
self.Time = timer.getTime()
|
||||
return Valid
|
||||
end
|
||||
|
||||
function TASK:Init()
|
||||
self:F()
|
||||
self.ActiveStage = 0
|
||||
self:SetStage(1)
|
||||
self.TaskDone = false
|
||||
self.TaskFailed = false
|
||||
end
|
||||
|
||||
|
||||
--- Get progress of a TASK.
|
||||
-- @return string GoalsText
|
||||
function TASK:GetGoalProgress()
|
||||
self:F2()
|
||||
|
||||
local GoalsText = ""
|
||||
for GoalVerb, GoalVerbData in pairs( self.GoalTasks ) do
|
||||
local Goals = self:GetGoalCompletion( GoalVerb )
|
||||
if Goals and Goals ~= "" then
|
||||
Goals = '(' .. Goals .. ')'
|
||||
else
|
||||
Goals = '( - )'
|
||||
end
|
||||
GoalsText = GoalsText .. GoalVerb .. ': ' .. self:GetGoalCount(GoalVerb) .. ' goals ' .. Goals .. ' of ' .. self:GetGoalTotal(GoalVerb) .. ' goals completed (' .. self:GetGoalPercentage(GoalVerb) .. '%); '
|
||||
end
|
||||
|
||||
if GoalsText == "" then
|
||||
GoalsText = "( - )"
|
||||
end
|
||||
|
||||
return GoalsText
|
||||
end
|
||||
|
||||
--- Show progress of a TASK.
|
||||
-- @param MISSION Mission Group structure describing the Mission.
|
||||
-- @param CLIENT Client Group structure describing the Client.
|
||||
function TASK:ShowGoalProgress( Mission, Client )
|
||||
self:F2()
|
||||
|
||||
local GoalsText = ""
|
||||
for GoalVerb, GoalVerbData in pairs( self.GoalTasks ) do
|
||||
if Mission:IsCompleted() then
|
||||
else
|
||||
local Goals = self:GetGoalCompletion( GoalVerb )
|
||||
if Goals and Goals ~= "" then
|
||||
else
|
||||
Goals = "-"
|
||||
end
|
||||
GoalsText = GoalsText .. self:GetGoalProgress()
|
||||
end
|
||||
end
|
||||
|
||||
if Mission.MissionReportFlash or Mission.MissionReportShow then
|
||||
Client:Message( GoalsText, 10, "/TASKPROGRESS" .. self.ClassName, "Mission Command: Task Status", 30 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Sets a TASK to status Done.
|
||||
function TASK:Done()
|
||||
self:F2()
|
||||
self.TaskDone = true
|
||||
end
|
||||
|
||||
--- Returns if a TASK is done.
|
||||
-- @return bool
|
||||
function TASK:IsDone()
|
||||
self:F2( self.TaskDone )
|
||||
return self.TaskDone
|
||||
end
|
||||
|
||||
--- Sets a TASK to status failed.
|
||||
function TASK:Failed()
|
||||
self:F()
|
||||
self.TaskFailed = true
|
||||
end
|
||||
|
||||
--- Returns if a TASk has failed.
|
||||
-- @return bool
|
||||
function TASK:IsFailed()
|
||||
self:F2( self.TaskFailed )
|
||||
return self.TaskFailed
|
||||
end
|
||||
|
||||
function TASK:Reset( Mission, Client )
|
||||
self:F2()
|
||||
self.ExecuteStage = _TransportExecuteStage.NONE
|
||||
end
|
||||
|
||||
--- Returns the Goals of a TASK
|
||||
-- @return @table Goals
|
||||
function TASK:GetGoals()
|
||||
return self.GoalTasks
|
||||
end
|
||||
|
||||
--- Returns if a TASK has Goal(s).
|
||||
-- @param #TASK self
|
||||
-- @param #string GoalVerb is the name of the Goal of the TASK.
|
||||
-- @return bool
|
||||
function TASK:Goal( GoalVerb )
|
||||
self:F2( { GoalVerb } )
|
||||
if not GoalVerb then
|
||||
GoalVerb = self.GoalVerb
|
||||
end
|
||||
self:T2( {self.GoalTasks[GoalVerb] } )
|
||||
if self.GoalTasks[GoalVerb] and self.GoalTasks[GoalVerb].GoalTotal > 0 then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
--- Sets the total Goals to be achieved of the Goal Name
|
||||
-- @param number GoalTotal is the number of times the GoalVerb needs to be achieved.
|
||||
-- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used.
|
||||
function TASK:SetGoalTotal( GoalTotal, GoalVerb )
|
||||
self:F2( { GoalTotal, GoalVerb } )
|
||||
|
||||
if not GoalVerb then
|
||||
GoalVerb = self.GoalVerb
|
||||
end
|
||||
self.GoalTasks[GoalVerb] = {}
|
||||
self.GoalTasks[GoalVerb].Goals = {}
|
||||
self.GoalTasks[GoalVerb].GoalTotal = GoalTotal
|
||||
self.GoalTasks[GoalVerb].GoalCount = 0
|
||||
return self
|
||||
end
|
||||
|
||||
--- Gets the total of Goals to be achieved within the TASK of the GoalVerb.
|
||||
-- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used.
|
||||
function TASK:GetGoalTotal( GoalVerb )
|
||||
self:F2( { GoalVerb } )
|
||||
if not GoalVerb then
|
||||
GoalVerb = self.GoalVerb
|
||||
end
|
||||
if self:Goal( GoalVerb ) then
|
||||
return self.GoalTasks[GoalVerb].GoalTotal
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
--- Sets the total of Goals currently achieved within the TASK of the GoalVerb.
|
||||
-- @param number GoalCount is the total number of Goals achieved within the TASK.
|
||||
-- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used.
|
||||
-- @return TASK
|
||||
function TASK:SetGoalCount( GoalCount, GoalVerb )
|
||||
self:F2()
|
||||
if not GoalVerb then
|
||||
GoalVerb = self.GoalVerb
|
||||
end
|
||||
if self:Goal( GoalVerb) then
|
||||
self.GoalTasks[GoalVerb].GoalCount = GoalCount
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Increments the total of Goals currently achieved within the TASK of the GoalVerb, with the given GoalCountIncrease.
|
||||
-- @param number GoalCountIncrease is the number of new Goals achieved within the TASK.
|
||||
-- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used.
|
||||
-- @return TASK
|
||||
function TASK:IncreaseGoalCount( GoalCountIncrease, GoalVerb )
|
||||
self:F2( { GoalCountIncrease, GoalVerb } )
|
||||
if not GoalVerb then
|
||||
GoalVerb = self.GoalVerb
|
||||
end
|
||||
if self:Goal( GoalVerb) then
|
||||
self.GoalTasks[GoalVerb].GoalCount = self.GoalTasks[GoalVerb].GoalCount + GoalCountIncrease
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Gets the total of Goals currently achieved within the TASK of the GoalVerb.
|
||||
-- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used.
|
||||
-- @return TASK
|
||||
function TASK:GetGoalCount( GoalVerb )
|
||||
self:F2()
|
||||
if not GoalVerb then
|
||||
GoalVerb = self.GoalVerb
|
||||
end
|
||||
if self:Goal( GoalVerb ) then
|
||||
return self.GoalTasks[GoalVerb].GoalCount
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets the percentage of Goals currently achieved within the TASK of the GoalVerb.
|
||||
-- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used.
|
||||
-- @return TASK
|
||||
function TASK:GetGoalPercentage( GoalVerb )
|
||||
self:F2()
|
||||
if not GoalVerb then
|
||||
GoalVerb = self.GoalVerb
|
||||
end
|
||||
if self:Goal( GoalVerb ) then
|
||||
return math.floor( self:GetGoalCount( GoalVerb ) / self:GetGoalTotal( GoalVerb ) * 100 + .5 )
|
||||
else
|
||||
return 100
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns if all the Goals of the TASK were achieved.
|
||||
-- @return bool
|
||||
function TASK:IsGoalReached()
|
||||
self:F2()
|
||||
|
||||
local GoalReached = true
|
||||
|
||||
for GoalVerb, Goals in pairs( self.GoalTasks ) do
|
||||
self:T2( { "GoalVerb", GoalVerb } )
|
||||
if self:Goal( GoalVerb ) then
|
||||
local GoalToDo = self:GetGoalTotal( GoalVerb ) - self:GetGoalCount( GoalVerb )
|
||||
self:T2( "GoalToDo = " .. GoalToDo )
|
||||
if GoalToDo <= 0 then
|
||||
else
|
||||
GoalReached = false
|
||||
break
|
||||
end
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
self:T( { GoalReached, self.GoalTasks } )
|
||||
return GoalReached
|
||||
end
|
||||
|
||||
--- Adds an Additional Goal for the TASK to be achieved.
|
||||
-- @param string GoalVerb is the name of the Goal of the TASK.
|
||||
-- @param string GoalTask is a text describing the Goal of the TASK to be achieved.
|
||||
-- @param number GoalIncrease is a number by which the Goal achievement is increasing.
|
||||
function TASK:AddGoalCompletion( GoalVerb, GoalTask, GoalIncrease )
|
||||
self:F2( { GoalVerb, GoalTask, GoalIncrease } )
|
||||
|
||||
if self:Goal( GoalVerb ) then
|
||||
self.GoalTasks[GoalVerb].Goals[#self.GoalTasks[GoalVerb].Goals+1] = GoalTask
|
||||
self.GoalTasks[GoalVerb].GoalCount = self.GoalTasks[GoalVerb].GoalCount + GoalIncrease
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Returns if the additional Goal for the TASK was completed.
|
||||
-- @param ?string GoalVerb is the name of the Goal of the TASK. If the GoalVerb is not given, then the default TASK Goals will be used.
|
||||
-- @return string Goals
|
||||
function TASK:GetGoalCompletion( GoalVerb )
|
||||
self:F2( { GoalVerb } )
|
||||
|
||||
if self:Goal( GoalVerb ) then
|
||||
local Goals = ""
|
||||
for GoalID, GoalName in pairs( self.GoalTasks[GoalVerb].Goals ) do Goals = Goals .. GoalName .. " + " end
|
||||
return Goals:gsub(" + $", ""), self.GoalTasks[GoalVerb].GoalCount
|
||||
end
|
||||
end
|
||||
|
||||
function TASK.MenuAction( Parameter )
|
||||
Parameter.ReferenceTask.ExecuteStage = _TransportExecuteStage.EXECUTING
|
||||
Parameter.ReferenceTask.Cargo = Parameter.CargoTask
|
||||
end
|
||||
|
||||
function TASK:StageExecute()
|
||||
self:F()
|
||||
|
||||
local Execute = false
|
||||
|
||||
if self.Frequency == STAGE.FREQUENCY.REPEAT then
|
||||
Execute = true
|
||||
elseif self.Frequency == STAGE.FREQUENCY.NONE then
|
||||
Execute = false
|
||||
elseif self.Frequency >= 0 then
|
||||
Execute = true
|
||||
self.Frequency = self.Frequency - 1
|
||||
end
|
||||
|
||||
return Execute
|
||||
|
||||
end
|
||||
|
||||
--- Work function to set signal events within a TASK.
|
||||
function TASK:AddSignal( SignalUnitNames, SignalType, SignalColor, SignalHeight )
|
||||
self:F()
|
||||
|
||||
local Valid = true
|
||||
|
||||
if Valid then
|
||||
if type( SignalUnitNames ) == "table" then
|
||||
self.LandingZoneSignalUnitNames = SignalUnitNames
|
||||
else
|
||||
self.LandingZoneSignalUnitNames = { SignalUnitNames }
|
||||
end
|
||||
self.LandingZoneSignalType = SignalType
|
||||
self.LandingZoneSignalColor = SignalColor
|
||||
self.Signalled = false
|
||||
if SignalHeight ~= nil then
|
||||
self.LandingZoneSignalHeight = SignalHeight
|
||||
else
|
||||
self.LandingZoneSignalHeight = 0
|
||||
end
|
||||
|
||||
if self.TaskBriefing then
|
||||
self.TaskBriefing = self.TaskBriefing .. " " .. SignalColor.TEXT .. " " .. SignalType.TEXT .. " will be fired when entering the landing zone."
|
||||
end
|
||||
end
|
||||
|
||||
return Valid
|
||||
end
|
||||
|
||||
--- When the CLIENT is approaching the landing zone, a RED SMOKE will be fired by an optional SignalUnitNames.
|
||||
-- @param table|string SignalUnitNames Name of the Group that will fire the signal. If this parameter is NIL, the signal will be fired from the center of the landing zone.
|
||||
-- @param number SignalHeight Altitude that the Signal should be fired...
|
||||
function TASK:AddSmokeRed( SignalUnitNames, SignalHeight )
|
||||
self:F()
|
||||
self:AddSignal( SignalUnitNames, TASK.SIGNAL.TYPE.SMOKE, TASK.SIGNAL.COLOR.RED, SignalHeight )
|
||||
end
|
||||
|
||||
--- When the CLIENT is approaching the landing zone, a GREEN SMOKE will be fired by an optional SignalUnitNames.
|
||||
-- @param table|string SignalUnitNames Name of the Group that will fire the signal. If this parameter is NIL, the signal will be fired from the center of the landing zone.
|
||||
-- @param number SignalHeight Altitude that the Signal should be fired...
|
||||
function TASK:AddSmokeGreen( SignalUnitNames, SignalHeight )
|
||||
self:F()
|
||||
self:AddSignal( SignalUnitNames, TASK.SIGNAL.TYPE.SMOKE, TASK.SIGNAL.COLOR.GREEN, SignalHeight )
|
||||
end
|
||||
|
||||
--- When the CLIENT is approaching the landing zone, a BLUE SMOKE will be fired by an optional SignalUnitNames.
|
||||
-- @param table|string SignalUnitNames Name of the Group that will fire the signal. If this parameter is NIL, the signal will be fired from the center of the landing zone.
|
||||
-- @param number SignalHeight Altitude that the Signal should be fired...
|
||||
function TASK:AddSmokeBlue( SignalUnitNames, SignalHeight )
|
||||
self:F()
|
||||
self:AddSignal( SignalUnitNames, TASK.SIGNAL.TYPE.SMOKE, TASK.SIGNAL.COLOR.BLUE, SignalHeight )
|
||||
end
|
||||
|
||||
--- When the CLIENT is approaching the landing zone, a WHITE SMOKE will be fired by an optional SignalUnitNames.
|
||||
-- @param table|string SignalUnitNames Name of the Group that will fire the signal. If this parameter is NIL, the signal will be fired from the center of the landing zone.
|
||||
-- @param number SignalHeight Altitude that the Signal should be fired...
|
||||
function TASK:AddSmokeWhite( SignalUnitNames, SignalHeight )
|
||||
self:F()
|
||||
self:AddSignal( SignalUnitNames, TASK.SIGNAL.TYPE.SMOKE, TASK.SIGNAL.COLOR.WHITE, SignalHeight )
|
||||
end
|
||||
|
||||
--- When the CLIENT is approaching the landing zone, an ORANGE SMOKE will be fired by an optional SignalUnitNames.
|
||||
-- @param table|string SignalUnitNames Name of the Group that will fire the signal. If this parameter is NIL, the signal will be fired from the center of the landing zone.
|
||||
-- @param number SignalHeight Altitude that the Signal should be fired...
|
||||
function TASK:AddSmokeOrange( SignalUnitNames, SignalHeight )
|
||||
self:F()
|
||||
self:AddSignal( SignalUnitNames, TASK.SIGNAL.TYPE.SMOKE, TASK.SIGNAL.COLOR.ORANGE, SignalHeight )
|
||||
end
|
||||
|
||||
--- When the CLIENT is approaching the landing zone, a RED FLARE will be fired by an optional SignalUnitNames.
|
||||
-- @param table|string SignalUnitNames Name of the Group that will fire the signal. If this parameter is NIL, the signal will be fired from the center of the landing zone.
|
||||
-- @param number SignalHeight Altitude that the Signal should be fired...
|
||||
function TASK:AddFlareRed( SignalUnitNames, SignalHeight )
|
||||
self:F()
|
||||
self:AddSignal( SignalUnitNames, TASK.SIGNAL.TYPE.FLARE, TASK.SIGNAL.COLOR.RED, SignalHeight )
|
||||
end
|
||||
|
||||
--- When the CLIENT is approaching the landing zone, a GREEN FLARE will be fired by an optional SignalUnitNames.
|
||||
-- @param table|string SignalUnitNames Name of the Group that will fire the signal. If this parameter is NIL, the signal will be fired from the center of the landing zone.
|
||||
-- @param number SignalHeight Altitude that the Signal should be fired...
|
||||
function TASK:AddFlareGreen( SignalUnitNames, SignalHeight )
|
||||
self:F()
|
||||
self:AddSignal( SignalUnitNames, TASK.SIGNAL.TYPE.FLARE, TASK.SIGNAL.COLOR.GREEN, SignalHeight )
|
||||
end
|
||||
|
||||
--- When the CLIENT is approaching the landing zone, a BLUE FLARE will be fired by an optional SignalUnitNames.
|
||||
-- @param table|string SignalUnitNames Name of the Group that will fire the signal. If this parameter is NIL, the signal will be fired from the center of the landing zone.
|
||||
-- @param number SignalHeight Altitude that the Signal should be fired...
|
||||
function TASK:AddFlareBlue( SignalUnitNames, SignalHeight )
|
||||
self:F()
|
||||
self:AddSignal( SignalUnitNames, TASK.SIGNAL.TYPE.FLARE, TASK.SIGNAL.COLOR.BLUE, SignalHeight )
|
||||
end
|
||||
|
||||
--- When the CLIENT is approaching the landing zone, a WHITE FLARE will be fired by an optional SignalUnitNames.
|
||||
-- @param table|string SignalUnitNames Name of the Group that will fire the signal. If this parameter is NIL, the signal will be fired from the center of the landing zone.
|
||||
-- @param number SignalHeight Altitude that the Signal should be fired...
|
||||
function TASK:AddFlareWhite( SignalUnitNames, SignalHeight )
|
||||
self:F()
|
||||
self:AddSignal( SignalUnitNames, TASK.SIGNAL.TYPE.FLARE, TASK.SIGNAL.COLOR.WHITE, SignalHeight )
|
||||
end
|
||||
|
||||
--- When the CLIENT is approaching the landing zone, an ORANGE FLARE will be fired by an optional SignalUnitNames.
|
||||
-- @param table|string SignalUnitNames Name of the Group that will fire the signal. If this parameter is NIL, the signal will be fired from the center of the landing zone.
|
||||
-- @param number SignalHeight Altitude that the Signal should be fired...
|
||||
function TASK:AddFlareOrange( SignalUnitNames, SignalHeight )
|
||||
self:F()
|
||||
self:AddSignal( SignalUnitNames, TASK.SIGNAL.TYPE.FLARE, TASK.SIGNAL.COLOR.ORANGE, SignalHeight )
|
||||
end
|
||||
775
Moose Development/Moose/Unit.lua
Normal file
775
Moose Development/Moose/Unit.lua
Normal file
@@ -0,0 +1,775 @@
|
||||
--- UNIT Class
|
||||
--
|
||||
-- @{UNIT} class
|
||||
-- ==============
|
||||
-- The @{UNIT} class is a wrapper class to handle the DCS Unit objects:
|
||||
--
|
||||
-- * Support all DCS Unit APIs.
|
||||
-- * Enhance with Unit specific APIs not in the DCS Unit API set.
|
||||
-- * Handle local Unit Controller.
|
||||
-- * Manage the "state" of the DCS Unit.
|
||||
--
|
||||
--
|
||||
-- UNIT reference methods
|
||||
-- ======================
|
||||
-- For each DCS Unit object alive within a running mission, a UNIT wrapper object (instance) will be created within the _@{DATABASE} object.
|
||||
-- This is done at the beginning of the mission (when the mission starts), and dynamically when new DCS Unit objects are spawned (using the @{SPAWN} class).
|
||||
--
|
||||
-- The UNIT class **does not contain a :New()** method, rather it provides **:Find()** methods to retrieve the object reference
|
||||
-- using the DCS Unit or the DCS UnitName.
|
||||
--
|
||||
-- Another thing to know is that UNIT objects do not "contain" the DCS Unit object.
|
||||
-- The UNIT methods will reference the DCS Unit object by name when it is needed during API execution.
|
||||
-- If the DCS Unit object does not exist or is nil, the UNIT methods will return nil and log an exception in the DCS.log file.
|
||||
--
|
||||
-- The UNIT class provides the following functions to retrieve quickly the relevant UNIT instance:
|
||||
--
|
||||
-- * @{#UNIT.Find}(): Find a UNIT instance from the _DATABASE object using a DCS Unit object.
|
||||
-- * @{#UNIT.FindByName}(): Find a UNIT instance from the _DATABASE object using a DCS Unit name.
|
||||
--
|
||||
-- IMPORTANT: ONE SHOULD NEVER SANATIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil).
|
||||
--
|
||||
-- DCS UNIT APIs
|
||||
-- =============
|
||||
-- The DCS Unit APIs are used extensively within MOOSE. The UNIT class has for each DCS Unit API a corresponding method.
|
||||
-- To be able to distinguish easily in your code the difference between a UNIT API call and a DCS Unit API call,
|
||||
-- the first letter of the method is also capitalized. So, by example, the DCS Unit method @{DCSUnit#Unit.getName}()
|
||||
-- is implemented in the UNIT class as @{#UNIT.GetName}().
|
||||
--
|
||||
-- Additional UNIT APIs
|
||||
-- ====================
|
||||
-- The UNIT class comes with additional methods. Find below a summary.
|
||||
--
|
||||
-- Smoke, Flare Units
|
||||
-- ------------------
|
||||
-- The UNIT class provides methods to smoke or flare units easily.
|
||||
-- The @{#UNIT.SmokeBlue}(), @{#UNIT.SmokeGreen}(),@{#UNIT.SmokeOrange}(), @{#UNIT.SmokeRed}(), @{#UNIT.SmokeRed}() methods
|
||||
-- will smoke the unit in the corresponding color. Note that smoking a unit is done at the current position of the DCS Unit.
|
||||
-- When the DCS Unit moves for whatever reason, the smoking will still continue!
|
||||
-- The @{#UNIT.FlareGreen}(), @{#UNIT.FlareRed}(), @{#UNIT.FlareWhite}(), @{#UNIT.FlareYellow}()
|
||||
-- methods will fire off a flare in the air with the corresponding color. Note that a flare is a one-off shot and its effect is of very short duration.
|
||||
--
|
||||
-- Position, Point
|
||||
-- ---------------
|
||||
-- The UNIT class provides methods to obtain the current point or position of the DCS Unit.
|
||||
-- The @{#UNIT.GetPointVec2}(), @{#UNIT.GetPointVec3}() will obtain the current location of the DCS Unit in a Vec2 (2D) or a Vec3 (3D) vector respectively.
|
||||
-- If you want to obtain the complete 3D position including oriëntation and direction vectors, consult the @{#UNIT.GetPositionVec3}() method respectively.
|
||||
--
|
||||
-- Alive
|
||||
-- -----
|
||||
-- The @{#UNIT.IsAlive}(), @{#UNIT.IsActive}() methods determines if the DCS Unit is alive, meaning, it is existing and active.
|
||||
--
|
||||
-- Test for other units in radius
|
||||
-- ------------------------------
|
||||
-- One can test if another DCS Unit is within a given radius of the current DCS Unit, by using the @{#UNIT.OtherUnitInRadius}() method.
|
||||
--
|
||||
-- More functions will be added
|
||||
-- ----------------------------
|
||||
-- During the MOOSE development, more functions will be added. A complete list of the current functions is below.
|
||||
--
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @module Unit
|
||||
-- @author FlightControl
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Message" )
|
||||
|
||||
--- The UNIT class
|
||||
-- @type UNIT
|
||||
-- @extends Base#BASE
|
||||
-- @field #UNIT.FlareColor FlareColor
|
||||
-- @field #UNIT.SmokeColor SmokeColor
|
||||
UNIT = {
|
||||
ClassName="UNIT",
|
||||
CategoryName = {
|
||||
[Unit.Category.AIRPLANE] = "Airplane",
|
||||
[Unit.Category.HELICOPTER] = "Helicoper",
|
||||
[Unit.Category.GROUND_UNIT] = "Ground Unit",
|
||||
[Unit.Category.SHIP] = "Ship",
|
||||
[Unit.Category.STRUCTURE] = "Structure",
|
||||
},
|
||||
FlareColor = {
|
||||
Green = trigger.flareColor.Green,
|
||||
Red = trigger.flareColor.Red,
|
||||
White = trigger.flareColor.White,
|
||||
Yellow = trigger.flareColor.Yellow
|
||||
},
|
||||
SmokeColor = {
|
||||
Green = trigger.smokeColor.Green,
|
||||
Red = trigger.smokeColor.Red,
|
||||
White = trigger.smokeColor.White,
|
||||
Orange = trigger.smokeColor.Orange,
|
||||
Blue = trigger.smokeColor.Blue
|
||||
},
|
||||
}
|
||||
|
||||
--- FlareColor
|
||||
-- @type UNIT.FlareColor
|
||||
-- @field Green
|
||||
-- @field Red
|
||||
-- @field White
|
||||
-- @field Yellow
|
||||
|
||||
--- SmokeColor
|
||||
-- @type UNIT.SmokeColor
|
||||
-- @field Green
|
||||
-- @field Red
|
||||
-- @field White
|
||||
-- @field Orange
|
||||
-- @field Blue
|
||||
|
||||
-- Registration.
|
||||
|
||||
--- Create a new UNIT from DCSUnit.
|
||||
-- @param #UNIT self
|
||||
-- @param DCSUnit#Unit DCSUnit
|
||||
-- @param Database#DATABASE Database
|
||||
-- @return Unit#UNIT
|
||||
function UNIT:Register( UnitName )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F2( UnitName )
|
||||
self.UnitName = UnitName
|
||||
return self
|
||||
end
|
||||
|
||||
-- Reference methods.
|
||||
|
||||
--- Finds a UNIT from the _DATABASE using a DCSUnit object.
|
||||
-- @param #UNIT self
|
||||
-- @param DCSUnit#Unit DCSUnit An existing DCS Unit object reference.
|
||||
-- @return Unit#UNIT self
|
||||
function UNIT:Find( DCSUnit )
|
||||
|
||||
local UnitName = DCSUnit:getName()
|
||||
local UnitFound = _DATABASE:FindUnit( UnitName )
|
||||
return UnitFound
|
||||
end
|
||||
|
||||
--- Find a UNIT in the _DATABASE using the name of an existing DCS Unit.
|
||||
-- @param #UNIT self
|
||||
-- @param #string UnitName The Unit Name.
|
||||
-- @return Unit#UNIT self
|
||||
function UNIT:FindByName( UnitName )
|
||||
|
||||
local UnitFound = _DATABASE:FindUnit( UnitName )
|
||||
return UnitFound
|
||||
end
|
||||
|
||||
function UNIT:GetDCSUnit()
|
||||
local DCSUnit = Unit.getByName( self.UnitName )
|
||||
|
||||
if DCSUnit then
|
||||
return DCSUnit
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns coalition of the Unit.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCSCoalitionObject#coalition.side The side of the coalition.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetCoalition()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitCoalition = DCSUnit:getCoalition()
|
||||
self:T3( UnitCoalition )
|
||||
return UnitCoalition
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns country of the Unit.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCScountry#country.id The country identifier.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetCountry()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitCountry = DCSUnit:getCountry()
|
||||
self:T3( UnitCountry )
|
||||
return UnitCountry
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns DCS Unit object name.
|
||||
-- The function provides access to non-activated units too.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #string The name of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetName()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitName = self.UnitName
|
||||
return UnitName
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns if the unit is alive.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #boolean true if Unit is alive.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:IsAlive()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitIsAlive = DCSUnit:isExist()
|
||||
return UnitIsAlive
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Returns if the unit is activated.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #boolean true if Unit is activated.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:IsActive()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
|
||||
local UnitIsActive = DCSUnit:isActive()
|
||||
return UnitIsActive
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns name of the player that control the unit or nil if the unit is controlled by A.I.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #string Player Name
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetPlayerName()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
|
||||
local PlayerName = DCSUnit:getPlayerName()
|
||||
if PlayerName == nil then
|
||||
PlayerName = ""
|
||||
end
|
||||
return PlayerName
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the unit's unique identifier.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCSUnit#Unit.ID Unit ID
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetID()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitID = DCSUnit:getID()
|
||||
return UnitID
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the unit's number in the group.
|
||||
-- The number is the same number the unit has in ME.
|
||||
-- It may not be changed during the mission.
|
||||
-- If any unit in the group is destroyed, the numbers of another units will not be changed.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #number The Unit number.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetNumber()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitNumber = DCSUnit:getNumber()
|
||||
return UnitNumber
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the unit's group if it exist and nil otherwise.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return Group#GROUP The Group of the Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetGroup()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitGroup = DCSUnit:getGroup()
|
||||
return UnitGroup
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns the unit's callsign - the localized string.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #string The Callsign of the Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetCallSign()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitCallSign = DCSUnit:getCallsign()
|
||||
return UnitCallSign
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the unit's health. Dead units has health <= 1.0.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #number The Unit's health value.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetLife()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitLife = DCSUnit:getLife()
|
||||
return UnitLife
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the Unit's initial health.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #number The Unit's initial health value.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetLife0()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitLife0 = DCSUnit:getLife0()
|
||||
return UnitLife0
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #number The relative amount of fuel (from 0.0 to 1.0).
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetFuel()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitFuel = DCSUnit:getFuel()
|
||||
return UnitFuel
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the Unit's ammunition.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCSUnit#Unit.Ammo
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetAmmo()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitAmmo = DCSUnit:getAmmo()
|
||||
return UnitAmmo
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the unit sensors.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCSUnit#Unit.Sensors
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetSensors()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitSensors = DCSUnit:getSensors()
|
||||
return UnitSensors
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Need to add here a function per sensortype
|
||||
-- unit:hasSensors(Unit.SensorType.RADAR, Unit.RadarType.AS)
|
||||
|
||||
--- Returns two values:
|
||||
--
|
||||
-- * First value indicates if at least one of the unit's radar(s) is on.
|
||||
-- * Second value is the object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #boolean Indicates if at least one of the unit's radar(s) is on.
|
||||
-- @return DCSObject#Object The object of the radar's interest. Not nil only if at least one radar of the unit is tracking a target.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetRadar()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitRadarOn, UnitRadarObject = DCSUnit:getRadar()
|
||||
return UnitRadarOn, UnitRadarObject
|
||||
end
|
||||
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-- Need to add here functions to check if radar is on and which object etc.
|
||||
|
||||
--- Returns unit descriptor. Descriptor type depends on unit category.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCSUnit#Unit.Desc The Unit descriptor.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetDesc()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitDesc = DCSUnit:getDesc()
|
||||
return UnitDesc
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns the type name of the DCS Unit.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #string The type name of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetTypeName()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitTypeName = DCSUnit:getTypeName()
|
||||
self:T3( UnitTypeName )
|
||||
return UnitTypeName
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns the prefix name of the DCS Unit. A prefix name is a part of the name before a '#'-sign.
|
||||
-- DCS Units spawned with the @{SPAWN} class contain a '#'-sign to indicate the end of the (base) DCS Unit name.
|
||||
-- The spawn sequence number and unit number are contained within the name after the '#' sign.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #string The name of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetPrefix()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitPrefix = string.match( self.UnitName, ".*#" ):sub( 1, -2 )
|
||||
self:T3( UnitPrefix )
|
||||
return UnitPrefix
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns the @{DCSTypes#Vec2} vector indicating the point in 2D of the DCS Unit within the mission.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCSTypes#Vec2 The 2D point vector of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetPointVec2()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitPointVec3 = DCSUnit:getPosition().p
|
||||
|
||||
local UnitPointVec2 = {}
|
||||
UnitPointVec2.x = UnitPointVec3.x
|
||||
UnitPointVec2.y = UnitPointVec3.z
|
||||
|
||||
self:T3( UnitPointVec2 )
|
||||
return UnitPointVec2
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns the @{DCSTypes#Vec3} vector indicating the point in 3D of the DCS Unit within the mission.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCSTypes#Vec3 The 3D point vector of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetPointVec3()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitPointVec3 = DCSUnit:getPosition().p
|
||||
self:T3( UnitPointVec3 )
|
||||
return UnitPointVec3
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the @{DCSTypes#Position3} position vectors indicating the point and direction vectors in 3D of the DCS Unit within the mission.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCSTypes#Position The 3D position vectors of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetPositionVec3()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitPosition = DCSUnit:getPosition()
|
||||
self:T3( UnitPosition )
|
||||
return UnitPosition
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the DCS Unit velocity vector.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCSTypes#Vec3 The velocity vector
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetVelocity()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitVelocityVec3 = DCSUnit:getVelocity()
|
||||
self:T3( UnitVelocityVec3 )
|
||||
return UnitVelocityVec3
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns true if the DCS Unit is in the air.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #boolean true if in the air.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:InAir()
|
||||
self:F2( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitInAir = DCSUnit:inAir()
|
||||
self:T3( UnitInAir )
|
||||
return UnitInAir
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the altitude of the DCS Unit.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return DCSTypes#Distance The altitude of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetAltitude()
|
||||
self:F2()
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitPointVec3 = DCSUnit:getPoint() --DCSTypes#Vec3
|
||||
return UnitPointVec3.y
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns true if there is an **other** DCS Unit within a radius of the current 2D point of the DCS Unit.
|
||||
-- @param Unit#UNIT self
|
||||
-- @param Unit#UNIT AwaitUnit The other UNIT wrapper object.
|
||||
-- @param Radius The radius in meters with the DCS Unit in the centre.
|
||||
-- @return true If the other DCS Unit is within the radius of the 2D point of the DCS Unit.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:OtherUnitInRadius( AwaitUnit, Radius )
|
||||
self:F2( { self.UnitName, AwaitUnit.UnitName, Radius } )
|
||||
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitPos = self:GetPointVec3()
|
||||
local AwaitUnitPos = AwaitUnit:GetPointVec3()
|
||||
|
||||
if (((UnitPos.x - AwaitUnitPos.x)^2 + (UnitPos.z - AwaitUnitPos.z)^2)^0.5 <= Radius) then
|
||||
self:T3( "true" )
|
||||
return true
|
||||
else
|
||||
self:T3( "false" )
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the DCS Unit category name as defined within the DCS Unit Descriptor.
|
||||
-- @param Unit#UNIT self
|
||||
-- @return #string The DCS Unit Category Name
|
||||
function UNIT:GetCategoryName()
|
||||
local DCSUnit = self:GetDCSUnit()
|
||||
|
||||
if DCSUnit then
|
||||
local UnitCategoryName = self.CategoryName[ self:GetDesc().category ]
|
||||
return UnitCategoryName
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Signal a flare at the position of the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:Flare( FlareColor )
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetPointVec3(), FlareColor , 0 )
|
||||
end
|
||||
|
||||
--- Signal a white flare at the position of the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:FlareWhite()
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.White , 0 )
|
||||
end
|
||||
|
||||
--- Signal a yellow flare at the position of the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:FlareYellow()
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.Yellow , 0 )
|
||||
end
|
||||
|
||||
--- Signal a green flare at the position of the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:FlareGreen()
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.Green , 0 )
|
||||
end
|
||||
|
||||
--- Signal a red flare at the position of the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:FlareRed()
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetPointVec3(), trigger.flareColor.Red, 0 )
|
||||
end
|
||||
|
||||
--- Smoke the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:Smoke( SmokeColor )
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetPointVec3(), SmokeColor )
|
||||
end
|
||||
|
||||
--- Smoke the UNIT Green.
|
||||
-- @param #UNIT self
|
||||
function UNIT:SmokeGreen()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Green )
|
||||
end
|
||||
|
||||
--- Smoke the UNIT Red.
|
||||
-- @param #UNIT self
|
||||
function UNIT:SmokeRed()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Red )
|
||||
end
|
||||
|
||||
--- Smoke the UNIT White.
|
||||
-- @param #UNIT self
|
||||
function UNIT:SmokeWhite()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.White )
|
||||
end
|
||||
|
||||
--- Smoke the UNIT Orange.
|
||||
-- @param #UNIT self
|
||||
function UNIT:SmokeOrange()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Orange )
|
||||
end
|
||||
|
||||
--- Smoke the UNIT Blue.
|
||||
-- @param #UNIT self
|
||||
function UNIT:SmokeBlue()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetPointVec3(), trigger.smokeColor.Blue )
|
||||
end
|
||||
|
||||
-- Is methods
|
||||
|
||||
--- Returns if the unit is of an air category.
|
||||
-- If the unit is a helicopter or a plane, then this method will return true, otherwise false.
|
||||
-- @param #UNIT self
|
||||
-- @return #boolean Air category evaluation result.
|
||||
function UNIT:IsAir()
|
||||
self:F2()
|
||||
|
||||
local UnitDescriptor = self.DCSUnit:getDesc()
|
||||
self:T3( { UnitDescriptor.category, Unit.Category.AIRPLANE, Unit.Category.HELICOPTER } )
|
||||
|
||||
local IsAirResult = ( UnitDescriptor.category == Unit.Category.AIRPLANE ) or ( UnitDescriptor.category == Unit.Category.HELICOPTER )
|
||||
|
||||
self:T3( IsAirResult )
|
||||
return IsAirResult
|
||||
end
|
||||
|
||||
79
Moose Development/Moose/Zone.lua
Normal file
79
Moose Development/Moose/Zone.lua
Normal file
@@ -0,0 +1,79 @@
|
||||
--- ZONE Classes
|
||||
-- @module Zone
|
||||
|
||||
Include.File( "Routines" )
|
||||
Include.File( "Base" )
|
||||
Include.File( "Message" )
|
||||
|
||||
--- The ZONE class
|
||||
-- @type ZONE
|
||||
-- @Extends Base#BASE
|
||||
ZONE = {
|
||||
ClassName="ZONE",
|
||||
}
|
||||
|
||||
function ZONE:New( ZoneName )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( ZoneName )
|
||||
|
||||
local Zone = trigger.misc.getZone( ZoneName )
|
||||
|
||||
if not Zone then
|
||||
error( "Zone " .. ZoneName .. " does not exist." )
|
||||
return nil
|
||||
end
|
||||
|
||||
self.Zone = Zone
|
||||
self.ZoneName = ZoneName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function ZONE:GetPointVec2()
|
||||
self:F( self.ZoneName )
|
||||
|
||||
local Zone = trigger.misc.getZone( self.ZoneName )
|
||||
local Point = { x = Zone.point.x, y = Zone.point.z }
|
||||
|
||||
self:T( { Zone, Point } )
|
||||
|
||||
return Point
|
||||
end
|
||||
|
||||
function ZONE:GetPointVec3( Height )
|
||||
self:F( self.ZoneName )
|
||||
|
||||
local Zone = trigger.misc.getZone( self.ZoneName )
|
||||
local Point = { x = Zone.point.x, y = land.getHeight( self:GetPointVec2() ) + Height, z = Zone.point.z }
|
||||
|
||||
self:T( { Zone, Point } )
|
||||
|
||||
return Point
|
||||
end
|
||||
|
||||
function ZONE:GetRandomPointVec2()
|
||||
self:F( self.ZoneName )
|
||||
|
||||
local Point = {}
|
||||
|
||||
local Zone = trigger.misc.getZone( self.ZoneName )
|
||||
|
||||
local angle = math.random() * math.pi*2;
|
||||
Point.x = Zone.point.x + math.cos( angle ) * math.random() * Zone.radius;
|
||||
Point.y = Zone.point.z + math.sin( angle ) * math.random() * Zone.radius;
|
||||
|
||||
self:T( { Zone, Point } )
|
||||
|
||||
return Point
|
||||
end
|
||||
|
||||
function ZONE:GetRadius()
|
||||
self:F( self.ZoneName )
|
||||
|
||||
local Zone = trigger.misc.getZone( self.ZoneName )
|
||||
|
||||
self:T( { Zone } )
|
||||
|
||||
return Zone.radius
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user