mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-08-15 10:47:21 +00:00
Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63b3807cf6 | ||
|
|
f3b6b27521 | ||
|
|
ff656182e8 | ||
|
|
a85ef6e769 | ||
|
|
0bb8c56ea9 | ||
|
|
5f108aec23 | ||
|
|
bf6683f993 | ||
|
|
b7a5e8dd85 | ||
|
|
57294aeaf5 | ||
|
|
20bfb8b08f | ||
|
|
5585a8992c | ||
|
|
d92a20050c | ||
|
|
c9a91d0683 | ||
|
|
ae6716ac01 | ||
|
|
34285b26ae | ||
|
|
5341e5faee | ||
|
|
a6224b484e | ||
|
|
e95c1ad3aa | ||
|
|
9cfed56847 | ||
|
|
6c1abddb1e | ||
|
|
7dc239f506 | ||
|
|
3e8413c6b7 | ||
|
|
ff86bfb91d | ||
|
|
66494b7b5a | ||
|
|
973127aa8c | ||
|
|
83866c3dd3 | ||
|
|
4797abc287 | ||
|
|
50f73f1be2 | ||
|
|
2ebbc8f466 | ||
|
|
1a96b30a45 | ||
|
|
3b1b57a33e | ||
|
|
f85c0320ec | ||
|
|
bae7edb914 | ||
|
|
35322399e9 | ||
|
|
5d4c0f3c87 | ||
|
|
92c5c32e8c | ||
|
|
c8780b2d5f | ||
|
|
f11dbfa367 | ||
|
|
54fb9deb3e | ||
|
|
d3c34ef04d | ||
|
|
3d5470eb22 | ||
|
|
2a3d69d0d5 | ||
|
|
9862c32378 | ||
|
|
9f02232589 | ||
|
|
ffc86bf046 | ||
|
|
e4c8ca34ed | ||
|
|
f02b7036a9 | ||
|
|
6adef47340 | ||
|
|
5b044cc036 | ||
|
|
03ba7524b2 | ||
|
|
772921c6b8 | ||
|
|
d4d305d53b | ||
|
|
c58918e002 | ||
|
|
dec7854476 | ||
|
|
8ca102f584 | ||
|
|
4748c66d9c | ||
|
|
bf486b9f44 | ||
|
|
6ff433ed7a | ||
|
|
f9aa392c6d | ||
|
|
819912cfd3 | ||
|
|
50ee1ae922 | ||
|
|
c9ccc28251 | ||
|
|
2157f8e8cd |
@@ -88,6 +88,7 @@ DATABASE = {
|
|||||||
WAREHOUSES = {},
|
WAREHOUSES = {},
|
||||||
FLIGHTGROUPS = {},
|
FLIGHTGROUPS = {},
|
||||||
FLIGHTCONTROLS = {},
|
FLIGHTCONTROLS = {},
|
||||||
|
PATHLINES = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
local _DATABASECoalition =
|
local _DATABASECoalition =
|
||||||
@@ -244,7 +245,7 @@ function DATABASE:FindAirbase( AirbaseName )
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
do -- Zones
|
do -- Zones and Pathlines
|
||||||
|
|
||||||
--- Finds a @{Core.Zone} based on the zone name.
|
--- Finds a @{Core.Zone} based on the zone name.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
@@ -267,7 +268,6 @@ do -- Zones
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Deletes a @{Core.Zone} from the DATABASE based on the zone name.
|
--- Deletes a @{Core.Zone} from the DATABASE based on the zone name.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @param #string ZoneName The name of the zone.
|
-- @param #string ZoneName The name of the zone.
|
||||||
@@ -277,6 +277,39 @@ do -- Zones
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Adds a @{Core.Pathline} based on its name in the DATABASE.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string PathlineName The name of the pathline
|
||||||
|
-- @param Core.Pathline#PATHLINE Pathline The pathline.
|
||||||
|
function DATABASE:AddPathline( PathlineName, Pathline )
|
||||||
|
|
||||||
|
if not self.PATHLINES[PathlineName] then
|
||||||
|
self.PATHLINES[PathlineName]=Pathline
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Finds a @{Core.Pathline} by its name.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string PathlineName The name of the Pathline.
|
||||||
|
-- @return Core.Pathline#PATHLINE The found PATHLINE.
|
||||||
|
function DATABASE:FindPathline( PathlineName )
|
||||||
|
|
||||||
|
local pathline = self.PATHLINES[PathlineName]
|
||||||
|
|
||||||
|
return pathline
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Deletes a @{Core.Pathline} from the DATABASE based on its name.
|
||||||
|
-- @param #DATABASE self
|
||||||
|
-- @param #string PathlineName The name of the PATHLINE.
|
||||||
|
function DATABASE:DeletePathline( PathlineName )
|
||||||
|
|
||||||
|
self.PATHLINES[PathlineName]=nil
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Private method that registers new ZONE_BASE derived objects within the DATABASE Object.
|
--- Private method that registers new ZONE_BASE derived objects within the DATABASE Object.
|
||||||
-- @param #DATABASE self
|
-- @param #DATABASE self
|
||||||
-- @return #DATABASE self
|
-- @return #DATABASE self
|
||||||
@@ -370,6 +403,7 @@ do -- Zones
|
|||||||
-- Add zone to DB.
|
-- Add zone to DB.
|
||||||
self:AddZone( ZoneName, Zone_Polygon )
|
self:AddZone( ZoneName, Zone_Polygon )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Drawings as zones
|
-- Drawings as zones
|
||||||
@@ -382,14 +416,23 @@ do -- Zones
|
|||||||
for objectID, objectData in pairs(layerData.objects or {}) do
|
for objectID, objectData in pairs(layerData.objects or {}) do
|
||||||
|
|
||||||
-- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice)
|
-- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice)
|
||||||
if objectData.polygonMode=="free" and objectData.points and #objectData.points>=4 then
|
if objectData.polygonMode and (objectData.polygonMode=="free") and objectData.points and #objectData.points>=4 then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Drawing: Polygon free
|
||||||
|
---
|
||||||
|
|
||||||
-- Name of the zone.
|
-- Name of the zone.
|
||||||
local ZoneName=objectData.name or "Unknown Drawing Zone"
|
local ZoneName=objectData.name or "Unknown free Polygon Drawing"
|
||||||
|
|
||||||
-- Reference point. All other points need to be translated by this.
|
-- Reference point. All other points need to be translated by this.
|
||||||
local vec2={x=objectData.mapX, y=objectData.mapY}
|
local vec2={x=objectData.mapX, y=objectData.mapY}
|
||||||
|
|
||||||
|
-- Debug stuff.
|
||||||
|
--local vec3={x=objectData.mapX, y=0, z=objectData.mapY}
|
||||||
|
--local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY")
|
||||||
|
--trigger.action.markToAll(id, "mapXY", vec3)
|
||||||
|
|
||||||
-- Copy points array.
|
-- Copy points array.
|
||||||
local points=UTILS.DeepCopy(objectData.points)
|
local points=UTILS.DeepCopy(objectData.points)
|
||||||
|
|
||||||
@@ -403,7 +446,7 @@ do -- Zones
|
|||||||
table.remove(points, #points)
|
table.remove(points, #points)
|
||||||
|
|
||||||
-- Debug output
|
-- Debug output
|
||||||
self:I(string.format("Register ZONE: %s (Polygon drawing with %d verticies)", ZoneName, #points))
|
self:I(string.format("Register ZONE: %s (Polygon (free) drawing with %d vertices)", ZoneName, #points))
|
||||||
|
|
||||||
-- Create new polygon zone.
|
-- Create new polygon zone.
|
||||||
local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points)
|
local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points)
|
||||||
@@ -417,13 +460,91 @@ do -- Zones
|
|||||||
-- Add zone.
|
-- Add zone.
|
||||||
self:AddZone(ZoneName, Zone)
|
self:AddZone(ZoneName, Zone)
|
||||||
|
|
||||||
|
-- Check for polygon which has at least 4 points (we would need 3 but the origin seems to be there twice)
|
||||||
|
elseif objectData.polygonMode and objectData.polygonMode=="rect" then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Drawing: Polygon rect
|
||||||
|
---
|
||||||
|
|
||||||
|
-- Name of the zone.
|
||||||
|
local ZoneName=objectData.name or "Unknown rect Polygon Drawing"
|
||||||
|
|
||||||
|
-- Reference point (center of the rectangle).
|
||||||
|
local vec2={x=objectData.mapX, y=objectData.mapY}
|
||||||
|
|
||||||
|
-- For a rectangular polygon drawing, we have the width (y) and height (x).
|
||||||
|
local w=objectData.width
|
||||||
|
local h=objectData.height
|
||||||
|
|
||||||
|
-- Create points from center using with and height (width for y and height for x is a bit confusing, but this is how ED implemented it).
|
||||||
|
local points={}
|
||||||
|
points[1]={x=vec2.x-h/2, y=vec2.y+w/2} --Upper left
|
||||||
|
points[2]={x=vec2.x+h/2, y=vec2.y+w/2} --Upper right
|
||||||
|
points[3]={x=vec2.x+h/2, y=vec2.y-w/2} --Lower right
|
||||||
|
points[4]={x=vec2.x-h/2, y=vec2.y-w/2} --Lower left
|
||||||
|
|
||||||
|
--local coord=COORDINATE:NewFromVec2(vec2):MarkToAll("MapX, MapY")
|
||||||
|
|
||||||
|
-- Debug output
|
||||||
|
self:I(string.format("Register ZONE: %s (Polygon (rect) drawing with %d vertices)", ZoneName, #points))
|
||||||
|
|
||||||
|
-- Create new polygon zone.
|
||||||
|
local Zone=ZONE_POLYGON:NewFromPointsArray(ZoneName, points)
|
||||||
|
|
||||||
|
-- Set color.
|
||||||
|
Zone:SetColor({1, 0, 0}, 0.15)
|
||||||
|
|
||||||
|
-- Store in DB.
|
||||||
|
self.ZONENAMES[ZoneName] = ZoneName
|
||||||
|
|
||||||
|
-- Add zone.
|
||||||
|
self:AddZone(ZoneName, Zone)
|
||||||
|
|
||||||
|
elseif objectData.lineMode and (objectData.lineMode=="segments" or objectData.lineMode=="segment" or objectData.lineMode=="free") and objectData.points and #objectData.points>=2 then
|
||||||
|
|
||||||
|
---
|
||||||
|
-- Drawing: Line (segments, segment or free)
|
||||||
|
---
|
||||||
|
|
||||||
|
-- Name of the zone.
|
||||||
|
local Name=objectData.name or "Unknown Line Drawing"
|
||||||
|
|
||||||
|
-- Reference point. All other points need to be translated by this.
|
||||||
|
local vec2={x=objectData.mapX, y=objectData.mapY}
|
||||||
|
|
||||||
|
-- Copy points array.
|
||||||
|
local points=UTILS.DeepCopy(objectData.points)
|
||||||
|
|
||||||
|
-- Translate points.
|
||||||
|
for i,_point in pairs(points) do
|
||||||
|
local point=_point --DCS#Vec2
|
||||||
|
points[i]=UTILS.Vec2Add(point, vec2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Debug output
|
||||||
|
self:I(string.format("Register PATHLINE: %s (Line drawing with %d points)", Name, #points))
|
||||||
|
|
||||||
|
-- Create new polygon zone.
|
||||||
|
local Pathline=PATHLINE:NewFromVec2Array(Name, points)
|
||||||
|
|
||||||
|
-- Set color.
|
||||||
|
--Zone:SetColor({1, 0, 0}, 0.15)
|
||||||
|
|
||||||
|
-- Add zone.
|
||||||
|
self:AddPathline(Name,Pathline)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end -- zone
|
end -- zone
|
||||||
|
|
||||||
do -- Zone_Goal
|
do -- Zone_Goal
|
||||||
|
|||||||
@@ -305,8 +305,8 @@ EVENTS = {
|
|||||||
-- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object.
|
-- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object.
|
||||||
-- @field #string PlaceName The name of the airbase.
|
-- @field #string PlaceName The name of the airbase.
|
||||||
--
|
--
|
||||||
-- @field #table weapon The weapon used during the event.
|
-- @field DCS#Weapon weapon The weapon used during the event.
|
||||||
-- @field #table Weapon
|
-- @field DCS#Weapon Weapon The weapon used during the event.
|
||||||
-- @field #string WeaponName Name of the weapon.
|
-- @field #string WeaponName Name of the weapon.
|
||||||
-- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon.
|
-- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon.
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
-- ### Author: **Applevangelist**
|
-- ### Author: **Applevangelist**
|
||||||
--
|
--
|
||||||
-- Date: 5 May 2021
|
-- Date: 5 May 2021
|
||||||
-- Last Update: Sep 2022
|
-- Last Update: Feb 2023
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
---
|
---
|
||||||
@@ -50,7 +50,7 @@ MARKEROPS_BASE = {
|
|||||||
ClassName = "MARKEROPS",
|
ClassName = "MARKEROPS",
|
||||||
Tag = "mytag",
|
Tag = "mytag",
|
||||||
Keywords = {},
|
Keywords = {},
|
||||||
version = "0.1.0",
|
version = "0.1.1",
|
||||||
debug = false,
|
debug = false,
|
||||||
Casesensitive = true,
|
Casesensitive = true,
|
||||||
}
|
}
|
||||||
@@ -124,6 +124,7 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
|
|||||||
-- @param #string Text The text on the marker
|
-- @param #string Text The text on the marker
|
||||||
-- @param #table Keywords Table of matching keywords found in the Event text
|
-- @param #table Keywords Table of matching keywords found in the Event text
|
||||||
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
|
||||||
|
-- @param #number idx DCS Marker ID
|
||||||
|
|
||||||
--- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map.
|
--- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map.
|
||||||
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted
|
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted
|
||||||
@@ -172,7 +173,7 @@ function MARKEROPS_BASE:OnEventMark(Event)
|
|||||||
if Eventtext~=nil then
|
if Eventtext~=nil then
|
||||||
if self:_MatchTag(Eventtext) then
|
if self:_MatchTag(Eventtext) then
|
||||||
local matchtable = self:_MatchKeywords(Eventtext)
|
local matchtable = self:_MatchKeywords(Eventtext)
|
||||||
self:MarkChanged(Eventtext,matchtable,coord)
|
self:MarkChanged(Eventtext,matchtable,coord,Event.idx)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
|
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
|
||||||
|
|||||||
370
Moose Development/Moose/Core/Pathline.lua
Normal file
370
Moose Development/Moose/Core/Pathline.lua
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
--- **Core** - Path from A to B.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * Path from A to B
|
||||||
|
-- * Arbitrary number of points
|
||||||
|
-- * Automatically from lines drawtool
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
-- @module Core.Pathline
|
||||||
|
-- @image CORE_Pathline.png
|
||||||
|
|
||||||
|
|
||||||
|
--- PATHLINE class.
|
||||||
|
-- @type PATHLINE
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field #string name Name of the path line.
|
||||||
|
-- @field #table points List of 3D points defining the path.
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *The shortest distance between two points is a straight line.* -- Archimedes
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The PATHLINE Concept
|
||||||
|
--
|
||||||
|
-- List of points defining a path from A to B. The pathline can consist of multiple points. Each point holds the information of its position, the surface type, the land height
|
||||||
|
-- and the water depth (if over sea).
|
||||||
|
--
|
||||||
|
-- Line drawings created in the mission editor are automatically registered as pathlines and stored in the MOOSE database.
|
||||||
|
-- They can be accessed with the @{#PATHLINE.FindByName) function.
|
||||||
|
--
|
||||||
|
-- # Constructor
|
||||||
|
--
|
||||||
|
-- The @{PATHLINE.New) function creates a new PATHLINE object. This does not hold any points. Points can be added with the @{#PATHLINE.AddPointFromVec2} and @{#PATHLINE.AddPointFromVec3}
|
||||||
|
--
|
||||||
|
-- For a given table of 2D or 3D positions, a new PATHLINE object can be created with the @{#PATHLINE.NewFromVec2Array} or @{#PATHLINE.NewFromVec3Array}, respectively.
|
||||||
|
--
|
||||||
|
-- # Line Drawings
|
||||||
|
--
|
||||||
|
-- The most convenient way to create a pathline is the draw panel feature in the DCS mission editor. You can select "Line" and then "Segments", "Segment" or "Free" to draw your lines.
|
||||||
|
-- These line drawings are then automatically added to the MOOSE database as PATHLINE objects and can be retrieved with the @{#PATHLINE.FindByName) function, where the name is the one
|
||||||
|
-- you specify in the draw panel.
|
||||||
|
--
|
||||||
|
-- # Mark on F10 map
|
||||||
|
--
|
||||||
|
-- The ponints of the PATHLINE can be marked on the F10 map with the @{#PATHLINE.MarkPoints}(`true`) function. The mark points contain information of the surface type, land height and
|
||||||
|
-- water depth.
|
||||||
|
--
|
||||||
|
-- To remove the marks, use @{#PATHLINE.MarkPoints}(`false`).
|
||||||
|
--
|
||||||
|
-- @field #PATHLINE
|
||||||
|
PATHLINE = {
|
||||||
|
ClassName = "PATHLINE",
|
||||||
|
lid = nil,
|
||||||
|
points = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Point of line.
|
||||||
|
-- @type PATHLINE.Point
|
||||||
|
-- @field DCS#Vec3 vec3 3D position.
|
||||||
|
-- @field DCS#Vec2 vec2 2D position.
|
||||||
|
-- @field #number surfaceType Surface type.
|
||||||
|
-- @field #number landHeight Land height in meters.
|
||||||
|
-- @field #number depth Water depth in meters.
|
||||||
|
-- @field #number markerID Marker ID.
|
||||||
|
|
||||||
|
|
||||||
|
--- PATHLINE class version.
|
||||||
|
-- @field #string version
|
||||||
|
PATHLINE.version="0.1.0"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- TODO list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: A lot...
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new PATHLINE object. Points need to be added later.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #string Name Name of the path.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:New(Name)
|
||||||
|
|
||||||
|
-- Inherit everything from INTEL class.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) --#PATHLINE
|
||||||
|
|
||||||
|
self.name=Name or "Unknown Path"
|
||||||
|
|
||||||
|
self.lid=string.format("PATHLINE %s | ", Name)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new PATHLINE object from a given list of 2D points.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #string Name Name of the pathline.
|
||||||
|
-- @param #table Vec2Array List of DCS#Vec2 points.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:NewFromVec2Array(Name, Vec2Array)
|
||||||
|
|
||||||
|
local self=PATHLINE:New(Name)
|
||||||
|
|
||||||
|
for i=1,#Vec2Array do
|
||||||
|
self:AddPointFromVec2(Vec2Array[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new PATHLINE object from a given list of 3D points.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #string Name Name of the pathline.
|
||||||
|
-- @param #table Vec3Array List of DCS#Vec3 points.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:NewFromVec3Array(Name, Vec3Array)
|
||||||
|
|
||||||
|
local self=PATHLINE:New(Name)
|
||||||
|
|
||||||
|
for i=1,#Vec3Array do
|
||||||
|
self:AddPointFromVec3(Vec3Array[i])
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Find a pathline in the database.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #string Name The name of the pathline.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:FindByName(Name)
|
||||||
|
local pathline = _DATABASE:FindPathline(Name)
|
||||||
|
return pathline
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
--- Add a point to the path from a given 2D position. The third dimension is determined from the land height.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param DCS#Vec2 Vec2 The 2D vector (x,y) to add.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:AddPointFromVec2(Vec2)
|
||||||
|
|
||||||
|
if Vec2 then
|
||||||
|
|
||||||
|
local point=self:_CreatePoint(Vec2)
|
||||||
|
|
||||||
|
table.insert(self.points, point)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Add a point to the path from a given 3D position.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param DCS#Vec3 Vec3 The 3D vector (x,y) to add.
|
||||||
|
-- @return #PATHLINE self
|
||||||
|
function PATHLINE:AddPointFromVec3(Vec3)
|
||||||
|
|
||||||
|
if Vec3 then
|
||||||
|
|
||||||
|
local point=self:_CreatePoint(Vec3)
|
||||||
|
|
||||||
|
table.insert(self.points, point)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get name of pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return #string Name of the pathline.
|
||||||
|
function PATHLINE:GetName()
|
||||||
|
return self.name
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get number of points.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return #number Number of points.
|
||||||
|
function PATHLINE:GetNumberOfPoints()
|
||||||
|
local N=#self.points
|
||||||
|
return N
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get points of pathline. Not that points are tables, that contain more information as just the 2D or 3D position but also the surface type etc.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return <#PATHLINE.Point> List of points.
|
||||||
|
function PATHLINE:GetPoints()
|
||||||
|
return self.points
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get 3D points of pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return <DCS#Vec3> List of DCS#Vec3 points.
|
||||||
|
function PATHLINE:GetPoints3D()
|
||||||
|
|
||||||
|
local vecs={}
|
||||||
|
|
||||||
|
for _,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
table.insert(vecs, point.vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
return vecs
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get 2D points of pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return <DCS#Vec2> List of DCS#Vec2 points.
|
||||||
|
function PATHLINE:GetPoints2D()
|
||||||
|
|
||||||
|
local vecs={}
|
||||||
|
|
||||||
|
for _,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
table.insert(vecs, point.vec2)
|
||||||
|
end
|
||||||
|
|
||||||
|
return vecs
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get COORDINATES of pathline. Note that COORDINATE objects are created when calling this function. That does involve deep copy calls and can have an impact on performance if done too often.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @return <Core.Point#COORDINATE> List of COORDINATES points.
|
||||||
|
function PATHLINE:GetCoordinats()
|
||||||
|
|
||||||
|
local vecs={}
|
||||||
|
|
||||||
|
for _,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
local coord=COORDINATE:NewFromVec3(point.vec3)
|
||||||
|
end
|
||||||
|
|
||||||
|
return vecs
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the n-th point of the pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #number n The index of the point. Default is the first point.
|
||||||
|
-- @return #PATHLINE.Point Point.
|
||||||
|
function PATHLINE:GetPointFromIndex(n)
|
||||||
|
|
||||||
|
local N=self:GetNumberOfPoints()
|
||||||
|
|
||||||
|
n=n or 1
|
||||||
|
|
||||||
|
local point=nil --#PATHLINE.Point
|
||||||
|
|
||||||
|
if n>=1 and n<=N then
|
||||||
|
point=self.point[n]
|
||||||
|
else
|
||||||
|
self:E(self.lid..string.format("ERROR: No point in pathline for N=%s", tostring(n)))
|
||||||
|
end
|
||||||
|
|
||||||
|
return point
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the 3D position of the n-th point.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #number n The n-th point.
|
||||||
|
-- @return DCS#VEC3 Position in 3D.
|
||||||
|
function PATHLINE:GetPoint3DFromIndex(n)
|
||||||
|
|
||||||
|
local point=self:GetPointFromIndex(n)
|
||||||
|
|
||||||
|
if point then
|
||||||
|
return point.vec3
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the 2D position of the n-th point.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #number n The n-th point.
|
||||||
|
-- @return DCS#VEC2 Position in 3D.
|
||||||
|
function PATHLINE:GetPoint2DFromIndex(n)
|
||||||
|
|
||||||
|
local point=self:GetPointFromIndex(n)
|
||||||
|
|
||||||
|
if point then
|
||||||
|
return point.vec2
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Mark points on F10 map.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param #boolean Switch If `true` or nil, set marks. If `false`, remove marks.
|
||||||
|
-- @return <DCS#Vec3> List of DCS#Vec3 points.
|
||||||
|
function PATHLINE:MarkPoints(Switch)
|
||||||
|
for i,_point in pairs(self.points) do
|
||||||
|
local point=_point --#PATHLINE.Point
|
||||||
|
if Switch==false then
|
||||||
|
|
||||||
|
if point.markerID then
|
||||||
|
UTILS.RemoveMark(point.markerID, Delay)
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
if point.markerID then
|
||||||
|
UTILS.RemoveMark(point.markerID)
|
||||||
|
end
|
||||||
|
|
||||||
|
point.markerID=UTILS.GetMarkID()
|
||||||
|
|
||||||
|
local text=string.format("Pathline %s: Point #%d\nSurface Type=%d\nHeight=%.1f m\nDepth=%.1f m", self.name, i, point.surfaceType, point.landHeight, point.depth)
|
||||||
|
|
||||||
|
trigger.action.markToAll(point.markerID, text, point.vec3, "")
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Private functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Get 3D points of pathline.
|
||||||
|
-- @param #PATHLINE self
|
||||||
|
-- @param DCS#Vec3 Vec Position vector. Can also be a DCS#Vec2 in which case the altitude at landheight is taken.
|
||||||
|
-- @return #PATHLINE.Point
|
||||||
|
function PATHLINE:_CreatePoint(Vec)
|
||||||
|
|
||||||
|
local point={} --#PATHLINE.Point
|
||||||
|
|
||||||
|
if Vec.z then
|
||||||
|
-- Given vec is 3D
|
||||||
|
point.vec3=UTILS.DeepCopy(Vec)
|
||||||
|
point.vec2={x=Vec.x, y=Vec.z}
|
||||||
|
else
|
||||||
|
-- Given vec is 2D
|
||||||
|
point.vec2=UTILS.DeepCopy(Vec)
|
||||||
|
point.vec3={x=Vec.x, y=land.getHeight(Vec), z=Vec.y}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get surface type.
|
||||||
|
point.surfaceType=land.getSurfaceType(point.vec2)
|
||||||
|
|
||||||
|
-- Get land height and depth.
|
||||||
|
point.landHeight, point.depth=land.getSurfaceHeightWithSeabed(point.vec2)
|
||||||
|
|
||||||
|
point.markerID=nil
|
||||||
|
|
||||||
|
return point
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -1054,28 +1054,55 @@ do -- COORDINATE
|
|||||||
return heading
|
return heading
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns the 3D wind direction vector. Note that vector points into the direction the wind in blowing to.
|
||||||
|
-- @param #COORDINATE self
|
||||||
|
-- @param #number height (Optional) parameter specifying the height ASL in meters. The minimum height will be always be the land height since the wind is zero below the ground.
|
||||||
|
-- @param #boolean turbulence (Optional) If `true`, include turbulence.
|
||||||
|
-- @return DCS#Vec3 Wind 3D vector. Components in m/s.
|
||||||
|
function COORDINATE:GetWindVec3(height, turbulence)
|
||||||
|
|
||||||
|
-- We at 0.1 meters to be sure to be above ground since wind is zero below ground level.
|
||||||
|
local landheight=self:GetLandHeight()+0.1
|
||||||
|
|
||||||
|
local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z}
|
||||||
|
|
||||||
|
-- Get wind velocity vector.
|
||||||
|
local wind = nil --DCS#Vec3
|
||||||
|
|
||||||
|
if turbulence then
|
||||||
|
wind = atmosphere.getWindWithTurbulence(point)
|
||||||
|
else
|
||||||
|
wind = atmosphere.getWind(point)
|
||||||
|
end
|
||||||
|
|
||||||
|
return wind
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the wind direction (from) and strength.
|
--- Returns the wind direction (from) and strength.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground.
|
-- @param #number height (Optional) parameter specifying the height ASL. The minimum height will be always be the land height since the wind is zero below the ground.
|
||||||
-- @return Direction the wind is blowing from in degrees.
|
-- @param #boolean turbulence If `true`, include turbulence. If `false` or `nil`, wind without turbulence.
|
||||||
-- @return Wind strength in m/s.
|
-- @return #number Direction the wind is blowing from in degrees.
|
||||||
function COORDINATE:GetWind(height)
|
-- @return #number Wind strength in m/s.
|
||||||
local landheight=self:GetLandHeight()+0.1 -- we at 0.1 meters to be sure to be above ground since wind is zero below ground level.
|
function COORDINATE:GetWind(height, turbulence)
|
||||||
local point={x=self.x, y=math.max(height or self.y, landheight), z=self.z}
|
|
||||||
-- get wind velocity vector
|
-- Get wind velocity vector
|
||||||
local wind = atmosphere.getWind(point)
|
local wind = self:GetWindVec3(height, turbulence)
|
||||||
local direction = math.deg(math.atan2(wind.z, wind.x))
|
|
||||||
if direction < 0 then
|
-- Calculate the direction of the vector.
|
||||||
direction = 360 + direction
|
local direction=UTILS.VecHdg(wind)
|
||||||
end
|
|
||||||
-- Convert to direction to from direction
|
-- Invert "to" direction to "from" direction.
|
||||||
if direction > 180 then
|
if direction > 180 then
|
||||||
direction = direction-180
|
direction = direction-180
|
||||||
else
|
else
|
||||||
direction = direction+180
|
direction = direction+180
|
||||||
end
|
end
|
||||||
local strength=math.sqrt((wind.x)^2+(wind.z)^2)
|
|
||||||
-- Return wind direction and strength km/h.
|
-- Wind strength in m/s.
|
||||||
|
local strength=UTILS.VecNorm(wind) -- math.sqrt((wind.x)^2+(wind.z)^2)
|
||||||
|
|
||||||
|
-- Return wind direction and strength.
|
||||||
return direction, strength
|
return direction, strength
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1133,12 +1160,15 @@ do -- COORDINATE
|
|||||||
|
|
||||||
--- Return the 3D distance in meters between the target COORDINATE and the COORDINATE.
|
--- Return the 3D distance in meters between the target COORDINATE and the COORDINATE.
|
||||||
-- @param #COORDINATE self
|
-- @param #COORDINATE self
|
||||||
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
|
-- @param #COORDINATE TargetCoordinate The target COORDINATE. Can also be a DCS#Vec3.
|
||||||
-- @return DCS#Distance Distance The distance in meters.
|
-- @return DCS#Distance Distance The distance in meters.
|
||||||
function COORDINATE:Get3DDistance( TargetCoordinate )
|
function COORDINATE:Get3DDistance( TargetCoordinate )
|
||||||
local TargetVec3 = TargetCoordinate:GetVec3()
|
--local TargetVec3 = TargetCoordinate:GetVec3()
|
||||||
|
local TargetVec3 = {x=TargetCoordinate.x, y=TargetCoordinate.y, z=TargetCoordinate.z}
|
||||||
local SourceVec3 = self:GetVec3()
|
local SourceVec3 = self:GetVec3()
|
||||||
return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
|
--local dist=( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.y - SourceVec3.y ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
|
||||||
|
local dist=UTILS.VecDist3D(TargetVec3, SourceVec3)
|
||||||
|
return dist
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -903,7 +903,7 @@ end
|
|||||||
|
|
||||||
do -- SET_GROUP
|
do -- SET_GROUP
|
||||||
|
|
||||||
--- @type SET_GROUP
|
--- @type SET_GROUP #SET_GROUP
|
||||||
-- @extends Core.Set#SET_BASE
|
-- @extends Core.Set#SET_BASE
|
||||||
|
|
||||||
--- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain:
|
--- Mission designers can use the @{Core.Set#SET_GROUP} class to build sets of groups belonging to certain:
|
||||||
@@ -1900,24 +1900,6 @@ do -- SET_GROUP
|
|||||||
return MGroupInclude
|
return MGroupInclude
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Iterate the SET_GROUP and set for each unit the default cargo bay weight limit.
|
|
||||||
-- Because within a group, the type of carriers can differ, each cargo bay weight limit is set on @{Wrapper.Unit} level.
|
|
||||||
-- @param #SET_GROUP self
|
|
||||||
-- @usage
|
|
||||||
-- -- Set the default cargo bay weight limits of the carrier units.
|
|
||||||
-- local MySetGroup = SET_GROUP:New()
|
|
||||||
-- MySetGroup:SetCargoBayWeightLimit()
|
|
||||||
function SET_GROUP:SetCargoBayWeightLimit()
|
|
||||||
local Set = self:GetSet()
|
|
||||||
for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP
|
|
||||||
for UnitName, UnitData in pairs( GroupData:GetUnits() ) do
|
|
||||||
-- local UnitData = UnitData -- Wrapper.Unit#UNIT
|
|
||||||
UnitData:SetCargoBayWeightLimit()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search.
|
--- Get the closest group of the set with respect to a given reference coordinate. Optionally, only groups of given coalitions are considered in the search.
|
||||||
-- @param #SET_GROUP self
|
-- @param #SET_GROUP self
|
||||||
-- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined.
|
-- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest group is determined.
|
||||||
@@ -1952,6 +1934,23 @@ do -- SET_GROUP
|
|||||||
return gmin, dmin
|
return gmin, dmin
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Iterate the SET_GROUP and set for each unit the default cargo bay weight limit.
|
||||||
|
-- Because within a group, the type of carriers can differ, each cargo bay weight limit is set on @{Wrapper.Unit} level.
|
||||||
|
-- @param #SET_GROUP self
|
||||||
|
-- @usage
|
||||||
|
-- -- Set the default cargo bay weight limits of the carrier units.
|
||||||
|
-- local MySetGroup = SET_GROUP:New()
|
||||||
|
-- MySetGroup:SetCargoBayWeightLimit()
|
||||||
|
function SET_GROUP:SetCargoBayWeightLimit()
|
||||||
|
local Set = self:GetSet()
|
||||||
|
for GroupID, GroupData in pairs( Set ) do -- For each GROUP in SET_GROUP
|
||||||
|
for UnitName, UnitData in pairs( GroupData:GetUnits() ) do
|
||||||
|
-- local UnitData = UnitData -- Wrapper.Unit#UNIT
|
||||||
|
UnitData:SetCargoBayWeightLimit()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- SET_UNIT
|
do -- SET_UNIT
|
||||||
@@ -2115,8 +2114,10 @@ do -- SET_UNIT
|
|||||||
|
|
||||||
self:Add( Unit:GetName(), Unit )
|
self:Add( Unit:GetName(), Unit )
|
||||||
|
|
||||||
|
if Unit:IsInstanceOf("UNIT") then
|
||||||
-- Set the default cargo bay limit each time a new unit is added to the set.
|
-- Set the default cargo bay limit each time a new unit is added to the set.
|
||||||
Unit:SetCargoBayWeightLimit()
|
Unit:SetCargoBayWeightLimit()
|
||||||
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -3794,6 +3795,40 @@ do -- SET_STATIC
|
|||||||
return TypeReport:Text( Delimiter )
|
return TypeReport:Text( Delimiter )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the closest static of the set with respect to a given reference coordinate. Optionally, only statics of given coalitions are considered in the search.
|
||||||
|
-- @param #SET_STATIC self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate Reference Coordinate from which the closest static is determined.
|
||||||
|
-- @return Wrapper.Static#STATIC The closest static (if any).
|
||||||
|
-- @return #number Distance in meters to the closest static.
|
||||||
|
function SET_STATIC:GetClosestStatic(Coordinate, Coalitions)
|
||||||
|
|
||||||
|
local Set = self:GetSet()
|
||||||
|
|
||||||
|
local dmin=math.huge
|
||||||
|
local gmin=nil
|
||||||
|
|
||||||
|
for GroupID, GroupData in pairs( Set ) do -- For each STATIC in SET_STATIC
|
||||||
|
local group=GroupData --Wrapper.Static#STATIC
|
||||||
|
|
||||||
|
if group and group:IsAlive() and (Coalitions==nil or UTILS.IsAnyInTable(Coalitions, group:GetCoalition())) then
|
||||||
|
|
||||||
|
local coord=group:GetCoord()
|
||||||
|
|
||||||
|
-- Distance between ref. coordinate and group coordinate.
|
||||||
|
local d=UTILS.VecDist3D(Coordinate, coord)
|
||||||
|
|
||||||
|
if d<dmin then
|
||||||
|
dmin=d
|
||||||
|
gmin=group
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return gmin, dmin
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do -- SET_CLIENT
|
do -- SET_CLIENT
|
||||||
|
|||||||
@@ -772,10 +772,8 @@ end
|
|||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- NATO helicopters engaging in the battle field.
|
-- -- NATO helicopters engaging in the battle field.
|
||||||
-- -- The KA-50 has waypoints Start point ( =0 or SP ), 1, 2, 3, 4, End point (= 5 or DP).
|
-- -- UNIT positions of this group will be randomized around the base unit #1 in a circle of 50 to 500 meters.
|
||||||
-- -- Waypoints 2 and 3 will only be randomized. The others will remain on their original position with each new spawn of the helicopter.
|
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeUnits( true, 500, 50 )
|
||||||
-- -- The randomization of waypoint 2 and 3 will take place within a radius of 2000 meters.
|
|
||||||
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitRandomizeRoute( 2, 2, 2000 )
|
|
||||||
--
|
--
|
||||||
function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )
|
function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )
|
||||||
self:F( { self.SpawnTemplatePrefix, RandomizeUnits, OuterRadius, InnerRadius } )
|
self:F( { self.SpawnTemplatePrefix, RandomizeUnits, OuterRadius, InnerRadius } )
|
||||||
@@ -791,6 +789,46 @@ function SPAWN:InitRandomizeUnits( RandomizeUnits, OuterRadius, InnerRadius )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Spawn the UNITs of this group with individual relative positions to unit #1 and individual headings.
|
||||||
|
-- @param #SPAWN self
|
||||||
|
-- @param #table Positions Table of positions, needs to one entry per unit in the group(!). The table contains one table each for each unit, with x,y, and optionally z
|
||||||
|
-- relative positions, and optionally an individual heading.
|
||||||
|
-- @return #SPAWN
|
||||||
|
-- @usage
|
||||||
|
--
|
||||||
|
-- -- NATO helicopter group of three units engaging in the battle field.
|
||||||
|
-- local Positions = { [1] = {x = 0, y = 0, heading = 0}, [2] = {x = 50, y = 50, heading = 90}, [3] = {x = -50, y = 50, heading = 180} }
|
||||||
|
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitSetUnitRelativePositions(Positions)
|
||||||
|
--
|
||||||
|
function SPAWN:InitSetUnitRelativePositions(Positions)
|
||||||
|
self:F({self.SpawnTemplatePrefix, Positions})
|
||||||
|
|
||||||
|
self.SpawnUnitsWithRelativePositions = true
|
||||||
|
self.UnitsRelativePositions = Positions
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Spawn the UNITs of this group with individual absolute positions and individual headings.
|
||||||
|
-- @param #SPAWN self
|
||||||
|
-- @param #table Positions Table of positions, needs to one entry per unit in the group(!). The table contains one table each for each unit, with x,y, and optionally z
|
||||||
|
-- absolute positions, and optionally an individual heading.
|
||||||
|
-- @return #SPAWN
|
||||||
|
-- @usage
|
||||||
|
--
|
||||||
|
-- -- NATO helicopter group of three units engaging in the battle field.
|
||||||
|
-- local Positions = { [1] = {x = 0, y = 0, heading = 0}, [2] = {x = 50, y = 50, heading = 90}, [3] = {x = -50, y = 50, heading = 180} }
|
||||||
|
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):InitSetUnitAbsolutePositions(Positions)
|
||||||
|
--
|
||||||
|
function SPAWN:InitSetUnitAbsolutePositions(Positions)
|
||||||
|
self:F({self.SpawnTemplatePrefix, Positions})
|
||||||
|
|
||||||
|
self.SpawnUnitsWithAbsolutePositions = true
|
||||||
|
self.UnitsAbsolutePositions = Positions
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- This method is rather complicated to understand. But I'll try to explain.
|
--- This method is rather complicated to understand. But I'll try to explain.
|
||||||
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
|
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
|
||||||
-- but they will all follow the same Template route and have the same prefix name.
|
-- but they will all follow the same Template route and have the same prefix name.
|
||||||
@@ -1146,7 +1184,8 @@ do -- Delay methods
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Turns the Delay On for the @{Wrapper.Group} when spawning.
|
--- Turns the Delay On for the @{Wrapper.Group} when spawning with @{SpawnScheduled}(). In effect then the 1st group will only be spawned
|
||||||
|
-- after the number of seconds given in SpawnScheduled as arguments, and not immediately.
|
||||||
-- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
-- @return #SPAWN The SPAWN object
|
-- @return #SPAWN The SPAWN object
|
||||||
function SPAWN:InitDelayOn()
|
function SPAWN:InitDelayOn()
|
||||||
@@ -1343,6 +1382,37 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Individual relative unit positions + heading
|
||||||
|
if self.SpawnUnitsWithRelativePositions and self.UnitsRelativePositions then
|
||||||
|
local BaseX = SpawnTemplate.units[1].x or 0
|
||||||
|
local BaseY = SpawnTemplate.units[1].y or 0
|
||||||
|
local BaseZ = SpawnTemplate.units[1].z or 0
|
||||||
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
|
if self.UnitsRelativePositions[UnitID].heading then
|
||||||
|
SpawnTemplate.units[UnitID].heading = math.rad(self.UnitsRelativePositions[UnitID].heading or 0)
|
||||||
|
end
|
||||||
|
SpawnTemplate.units[UnitID].x = BaseX + (self.UnitsRelativePositions[UnitID].x or 0)
|
||||||
|
SpawnTemplate.units[UnitID].y = BaseY + (self.UnitsRelativePositions[UnitID].y or 0)
|
||||||
|
if self.UnitsRelativePositions[UnitID].z then
|
||||||
|
SpawnTemplate.units[UnitID].z = BaseZ + (self.UnitsRelativePositions[UnitID].z or 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Individual asbolute unit positions + heading
|
||||||
|
if self.SpawnUnitsWithAbsolutePositions and self.UnitsAbsolutePositions then
|
||||||
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
|
if self.UnitsAbsolutePositions[UnitID].heading then
|
||||||
|
SpawnTemplate.units[UnitID].heading = math.rad(self.UnitsAbsolutePositions[UnitID].heading or 0)
|
||||||
|
end
|
||||||
|
SpawnTemplate.units[UnitID].x = self.UnitsAbsolutePositions[UnitID].x or 0
|
||||||
|
SpawnTemplate.units[UnitID].y = self.UnitsAbsolutePositions[UnitID].y or 0
|
||||||
|
if self.UnitsAbsolutePositions[UnitID].z then
|
||||||
|
SpawnTemplate.units[UnitID].z = self.UnitsAbsolutePositions[UnitID].z or 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Set livery.
|
-- Set livery.
|
||||||
if self.SpawnInitLivery then
|
if self.SpawnInitLivery then
|
||||||
for UnitID = 1, #SpawnTemplate.units do
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
@@ -1443,6 +1513,8 @@ end
|
|||||||
-- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups.
|
-- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups.
|
||||||
-- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn.
|
-- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn.
|
||||||
-- The variation is a number between 0 and 1, representing the % of variation to be applied on the time interval.
|
-- The variation is a number between 0 and 1, representing the % of variation to be applied on the time interval.
|
||||||
|
-- @param #boolen WithDelay Do not spawn the **first** group immediately, but delay the spawn as per the calculation below.
|
||||||
|
-- Effectively the same as @{InitDelayOn}().
|
||||||
-- @return #SPAWN self
|
-- @return #SPAWN self
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- NATO helicopters engaging in the battle field.
|
-- -- NATO helicopters engaging in the battle field.
|
||||||
@@ -1453,12 +1525,15 @@ end
|
|||||||
-- -- High limit: 600 * ( 1 + 0.5 / 2 ) = 750
|
-- -- High limit: 600 * ( 1 + 0.5 / 2 ) = 750
|
||||||
-- -- Between these two values, a random amount of seconds will be chosen for each new spawn of the helicopters.
|
-- -- Between these two values, a random amount of seconds will be chosen for each new spawn of the helicopters.
|
||||||
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):SpawnScheduled( 600, 0.5 )
|
-- Spawn_BE_KA50 = SPAWN:New( 'BE KA-50@RAMP-Ground Defense' ):SpawnScheduled( 600, 0.5 )
|
||||||
function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation )
|
function SPAWN:SpawnScheduled( SpawnTime, SpawnTimeVariation, WithDelay )
|
||||||
self:F( { SpawnTime, SpawnTimeVariation } )
|
self:F( { SpawnTime, SpawnTimeVariation } )
|
||||||
|
|
||||||
|
local SpawnTime = SpawnTime or 60
|
||||||
|
local SpawnTimeVariation = SpawnTimeVariation or 0.5
|
||||||
|
|
||||||
if SpawnTime ~= nil and SpawnTimeVariation ~= nil then
|
if SpawnTime ~= nil and SpawnTimeVariation ~= nil then
|
||||||
local InitialDelay = 0
|
local InitialDelay = 0
|
||||||
if self.DelayOnOff == true then
|
if WithDelay or self.DelayOnOff == true then
|
||||||
InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation )
|
InitialDelay = math.random( SpawnTime - SpawnTime * SpawnTimeVariation, SpawnTime + SpawnTime * SpawnTimeVariation )
|
||||||
end
|
end
|
||||||
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation )
|
self.SpawnScheduler = SCHEDULER:New( self, self._Scheduler, {}, InitialDelay, SpawnTime, SpawnTimeVariation )
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
-- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:
|
-- SPOT implements the DCS Spot class functionality, but adds additional luxury to be able to:
|
||||||
--
|
--
|
||||||
-- * Spot for a defined duration.
|
-- * Spot for a defined duration.
|
||||||
-- * Updates of laer spot position every 0.2 seconds for moving targets.
|
-- * Updates of laser spot position every 0.2 seconds for moving targets.
|
||||||
-- * Wiggle the spot at the target.
|
-- * Wiggle the spot at the target.
|
||||||
-- * Provide a @{Wrapper.Unit} as a target, instead of a point.
|
-- * Provide a @{Wrapper.Unit} as a target, instead of a point.
|
||||||
-- * Implement a status machine, LaseOn, LaseOff.
|
-- * Implement a status machine, LaseOn, LaseOff.
|
||||||
@@ -14,17 +14,7 @@
|
|||||||
--
|
--
|
||||||
-- # Demo Missions
|
-- # Demo Missions
|
||||||
--
|
--
|
||||||
-- ### [SPOT Demo Missions source code]()
|
-- ### [Demo Missions on GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS)
|
||||||
--
|
|
||||||
-- ### [SPOT Demo Missions, only for beta testers]()
|
|
||||||
--
|
|
||||||
-- ### [ALL Demo Missions pack of the last release](https://github.com/FlightControl-Master/MOOSE_MISSIONS/releases)
|
|
||||||
--
|
|
||||||
-- ===
|
|
||||||
--
|
|
||||||
-- # YouTube Channel
|
|
||||||
--
|
|
||||||
-- ### [SPOT YouTube Channel]()
|
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -50,14 +40,14 @@ do
|
|||||||
--- Implements the target spotting or marking functionality, but adds additional luxury to be able to:
|
--- Implements the target spotting or marking functionality, but adds additional luxury to be able to:
|
||||||
--
|
--
|
||||||
-- * Mark targets for a defined duration.
|
-- * Mark targets for a defined duration.
|
||||||
-- * Updates of laer spot position every 0.2 seconds for moving targets.
|
-- * Updates of laser spot position every 0.25 seconds for moving targets.
|
||||||
-- * Wiggle the spot at the target.
|
-- * Wiggle the spot at the target.
|
||||||
-- * Provide a @{Wrapper.Unit} as a target, instead of a point.
|
-- * Provide a @{Wrapper.Unit} as a target, instead of a point.
|
||||||
-- * Implement a status machine, LaseOn, LaseOff.
|
-- * Implement a status machine, LaseOn, LaseOff.
|
||||||
--
|
--
|
||||||
-- ## 1. SPOT constructor
|
-- ## 1. SPOT constructor
|
||||||
--
|
--
|
||||||
-- * @{#SPOT.New}(..\Presentations\SPOT\Dia2.JPG): Creates a new SPOT object.
|
-- * @{#SPOT.New}(): Creates a new SPOT object.
|
||||||
--
|
--
|
||||||
-- ## 2. SPOT is a FSM
|
-- ## 2. SPOT is a FSM
|
||||||
--
|
--
|
||||||
@@ -218,6 +208,8 @@ do
|
|||||||
|
|
||||||
self.Recce = Recce
|
self.Recce = Recce
|
||||||
|
|
||||||
|
self.RecceName = self.Recce:GetName()
|
||||||
|
|
||||||
self.LaseScheduler = SCHEDULER:New( self )
|
self.LaseScheduler = SCHEDULER:New( self )
|
||||||
|
|
||||||
self:SetEventPriority( 5 )
|
self:SetEventPriority( 5 )
|
||||||
@@ -243,6 +235,9 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
self.Target = Target
|
self.Target = Target
|
||||||
|
|
||||||
|
self.TargetName = Target:GetName()
|
||||||
|
|
||||||
self.LaserCode = LaserCode
|
self.LaserCode = LaserCode
|
||||||
|
|
||||||
self.Lasing = true
|
self.Lasing = true
|
||||||
@@ -302,12 +297,18 @@ do
|
|||||||
function SPOT:OnEventDead(EventData)
|
function SPOT:OnEventDead(EventData)
|
||||||
self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } )
|
self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } )
|
||||||
if self.Target then
|
if self.Target then
|
||||||
if EventData.IniDCSUnitName == self.Target:GetName() then
|
if EventData.IniDCSUnitName == self.TargetName then
|
||||||
self:F( {"Target dead ", self.Target:GetName() } )
|
self:F( {"Target dead ", self.TargetName } )
|
||||||
self:Destroyed()
|
self:Destroyed()
|
||||||
self:LaseOff()
|
self:LaseOff()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if self.Recce then
|
||||||
|
if EventData.IniDCSUnitName == self.RecceName then
|
||||||
|
self:F( {"Recce dead ", self.RecceName } )
|
||||||
|
self:LaseOff()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SPOT self
|
--- @param #SPOT self
|
||||||
|
|||||||
@@ -555,7 +555,7 @@ function ZONE_BASE:GetZoneMaybe()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
|
--- Returns the Value of the zone with the given PropertyName, or nil if no matching property exists.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @param #string PropertyName The name of a the TriggerZone Property to be retrieved.
|
-- @param #string PropertyName The name of a the TriggerZone Property to be retrieved.
|
||||||
-- @return #string The Value of the TriggerZone Property with the given PropertyName, or nil if absent.
|
-- @return #string The Value of the TriggerZone Property with the given PropertyName, or nil if absent.
|
||||||
@@ -569,7 +569,7 @@ function ZONE_BASE:GetProperty(PropertyName)
|
|||||||
return self.Properties[PropertyName]
|
return self.Properties[PropertyName]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Returns the zone Properties table.
|
--- Returns the zone Properties table.
|
||||||
-- @param #ZONE_BASE self
|
-- @param #ZONE_BASE self
|
||||||
-- @return #table The Key:Value table of TriggerZone properties of the zone.
|
-- @return #table The Key:Value table of TriggerZone properties of the zone.
|
||||||
function ZONE_BASE:GetAllProperties()
|
function ZONE_BASE:GetAllProperties()
|
||||||
@@ -927,7 +927,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
|||||||
local ZoneCoord = self:GetCoordinate()
|
local ZoneCoord = self:GetCoordinate()
|
||||||
local ZoneRadius = self:GetRadius()
|
local ZoneRadius = self:GetRadius()
|
||||||
|
|
||||||
self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
--self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
||||||
|
|
||||||
local SphereSearch = {
|
local SphereSearch = {
|
||||||
id = world.VolumeType.SPHERE,
|
id = world.VolumeType.SPHERE,
|
||||||
@@ -2084,13 +2084,10 @@ function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlph
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get the smallest radius encompassing all points of the polygon zone.
|
||||||
--- Get the smallest circular zone encompassing all points points of the polygon zone.
|
|
||||||
-- @param #ZONE_POLYGON_BASE self
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone.
|
-- @return #number Radius of the zone in meters.
|
||||||
-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered.
|
function ZONE_POLYGON_BASE:GetRadius()
|
||||||
-- @return #ZONE_RADIUS The circular zone.
|
|
||||||
function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone)
|
|
||||||
|
|
||||||
local center=self:GetVec2()
|
local center=self:GetVec2()
|
||||||
|
|
||||||
@@ -2107,6 +2104,20 @@ function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone)
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return radius
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the smallest circular zone encompassing all points of the polygon zone.
|
||||||
|
-- @param #ZONE_POLYGON_BASE self
|
||||||
|
-- @param #string ZoneName (Optional) Name of the zone. Default is the name of the polygon zone.
|
||||||
|
-- @param #boolean DoNotRegisterZone (Optional) If `true`, zone is not registered.
|
||||||
|
-- @return #ZONE_RADIUS The circular zone.
|
||||||
|
function ZONE_POLYGON_BASE:GetZoneRadius(ZoneName, DoNotRegisterZone)
|
||||||
|
|
||||||
|
local center=self:GetVec2()
|
||||||
|
|
||||||
|
local radius=self:GetRadius()
|
||||||
|
|
||||||
local zone=ZONE_RADIUS:New(ZoneName or self.ZoneName, center, radius, DoNotRegisterZone)
|
local zone=ZONE_RADIUS:New(ZoneName or self.ZoneName, center, radius, DoNotRegisterZone)
|
||||||
|
|
||||||
return zone
|
return zone
|
||||||
@@ -2913,7 +2924,7 @@ do -- ZONE_ELASTIC
|
|||||||
|
|
||||||
--- Add a set of groups. Positions of the group will be considered as polygon vertices when contructing the convex hull.
|
--- Add a set of groups. Positions of the group will be considered as polygon vertices when contructing the convex hull.
|
||||||
-- @param #ZONE_ELASTIC self
|
-- @param #ZONE_ELASTIC self
|
||||||
-- @param Core.Set#SET_GROUP SetGroup Set of groups.
|
-- @param Core.Set#SET_GROUP GroupSet Set of groups.
|
||||||
-- @return #ZONE_ELASTIC self
|
-- @return #ZONE_ELASTIC self
|
||||||
function ZONE_ELASTIC:AddSetGroup(GroupSet)
|
function ZONE_ELASTIC:AddSetGroup(GroupSet)
|
||||||
|
|
||||||
|
|||||||
@@ -193,21 +193,29 @@ do -- land
|
|||||||
|
|
||||||
--- [Type of surface enumerator](https://wiki.hoggitworld.com/view/DCS_singleton_land)
|
--- [Type of surface enumerator](https://wiki.hoggitworld.com/view/DCS_singleton_land)
|
||||||
-- @type land.SurfaceType
|
-- @type land.SurfaceType
|
||||||
-- @field LAND
|
-- @field LAND Land=1
|
||||||
-- @field SHALLOW_WATER
|
-- @field SHALLOW_WATER Shallow water=2
|
||||||
-- @field WATER
|
-- @field WATER Water=3
|
||||||
-- @field ROAD
|
-- @field ROAD Road=4
|
||||||
-- @field RUNWAY
|
-- @field RUNWAY Runway=5
|
||||||
|
|
||||||
--- Returns altitude MSL of the point.
|
--- Returns the distance from sea level (y-axis) of a given vec2 point.
|
||||||
-- @function [parent=#land] getHeight
|
-- @function [parent=#land] getHeight
|
||||||
-- @param #Vec2 point point on the ground.
|
-- @param #Vec2 point Point on the ground.
|
||||||
-- @return #Distance
|
-- @return #number Height in meters.
|
||||||
|
|
||||||
--- returns surface type at the given point.
|
--- Returns the surface height and depth of a point. Useful for checking if the path is deep enough to support a given ship.
|
||||||
|
-- Both values are positive. When checked over water at sea level the first value is always zero.
|
||||||
|
-- When checked over water at altitude, for example the reservoir of the Inguri Dam, the first value is the corresponding altitude the water level is at.
|
||||||
|
-- @function [parent=#land] getSurfaceHeightWithSeabed
|
||||||
|
-- @param #Vec2 point Position where to check.
|
||||||
|
-- @return #number Height in meters.
|
||||||
|
-- @return #number Depth in meters.
|
||||||
|
|
||||||
|
--- Returns surface type at the given point.
|
||||||
-- @function [parent=#land] getSurfaceType
|
-- @function [parent=#land] getSurfaceType
|
||||||
-- @param #Vec2 point Point on the land.
|
-- @param #Vec2 point Point on the land.
|
||||||
-- @return #land.SurfaceType
|
-- @return #number Enumerator value from `land.SurfaceType` (LAND=1, SHALLOW_WATER=2, WATER=3, ROAD=4, RUNWAY=5)
|
||||||
|
|
||||||
--- [DCS Singleton land](https://wiki.hoggitworld.com/view/DCS_singleton_land)
|
--- [DCS Singleton land](https://wiki.hoggitworld.com/view/DCS_singleton_land)
|
||||||
land = {} --#land
|
land = {} --#land
|
||||||
@@ -427,8 +435,8 @@ do -- Types
|
|||||||
--- Vec3 type is a 3D-vector.
|
--- Vec3 type is a 3D-vector.
|
||||||
-- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain.
|
-- DCS world has 3-dimensional coordinate system. DCS ground is an infinite plain.
|
||||||
-- @type Vec3
|
-- @type Vec3
|
||||||
-- @field #Distance x is directed to the north
|
-- @field #Distance x is directed to the North
|
||||||
-- @field #Distance z is directed to the east
|
-- @field #Distance z is directed to the East
|
||||||
-- @field #Distance y is directed up
|
-- @field #Distance y is directed up
|
||||||
|
|
||||||
--- Vec2 is a 2D-vector for the ground plane as a reference plane.
|
--- Vec2 is a 2D-vector for the ground plane as a reference plane.
|
||||||
@@ -679,10 +687,11 @@ do -- Weapon
|
|||||||
|
|
||||||
--- Weapon.Category enum that stores weapon categories.
|
--- Weapon.Category enum that stores weapon categories.
|
||||||
-- @type Weapon.Category
|
-- @type Weapon.Category
|
||||||
-- @field SHELL
|
-- @field #number SHELL Shell.
|
||||||
-- @field MISSILE
|
-- @field #number MISSILE Missile
|
||||||
-- @field ROCKET
|
-- @field #number ROCKET Rocket.
|
||||||
-- @field BOMB
|
-- @field #number BOMB Bomb.
|
||||||
|
-- @field #number TORPEDO Torpedo.
|
||||||
|
|
||||||
|
|
||||||
--- Weapon.GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB).
|
--- Weapon.GuidanceType enum that stores guidance methods. Available only for guided weapon (Weapon.Category.MISSILE and some Weapon.Category.BOMB).
|
||||||
@@ -1001,8 +1010,8 @@ do -- Unit
|
|||||||
|
|
||||||
--- Enum that stores aircraft refueling system types.
|
--- Enum that stores aircraft refueling system types.
|
||||||
-- @type Unit.RefuelingSystem
|
-- @type Unit.RefuelingSystem
|
||||||
-- @field BOOM_AND_RECEPTACLE
|
-- @field BOOM_AND_RECEPTACLE Tanker with a boom.
|
||||||
-- @field PROBE_AND_DROGUE
|
-- @field PROBE_AND_DROGUE Tanker with a probe.
|
||||||
|
|
||||||
--- Enum that stores sensor types.
|
--- Enum that stores sensor types.
|
||||||
-- @type Unit.SensorType
|
-- @type Unit.SensorType
|
||||||
|
|||||||
@@ -103,6 +103,7 @@
|
|||||||
-- @field #number coalition The coalition of the arty group.
|
-- @field #number coalition The coalition of the arty group.
|
||||||
-- @field #boolean respawnafterdeath Respawn arty group after all units are dead.
|
-- @field #boolean respawnafterdeath Respawn arty group after all units are dead.
|
||||||
-- @field #number respawndelay Respawn delay in seconds.
|
-- @field #number respawndelay Respawn delay in seconds.
|
||||||
|
-- @field #number dtTrack Time interval in seconds for weapon tracking.
|
||||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||||
|
|
||||||
--- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can
|
--- Enables mission designers easily to assign targets for artillery units. Since the implementation is based on a Finite State Model (FSM), the mission designer can
|
||||||
@@ -693,7 +694,7 @@ ARTY.db={
|
|||||||
|
|
||||||
--- Arty script version.
|
--- Arty script version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ARTY.version="1.2.0"
|
ARTY.version="1.3.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -802,6 +803,9 @@ function ARTY:New(group, alias)
|
|||||||
self.ismobile=false
|
self.ismobile=false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Set track time interval.
|
||||||
|
self.dtTrack=0.2
|
||||||
|
|
||||||
-- Set speed to 0.7 of maximum.
|
-- Set speed to 0.7 of maximum.
|
||||||
self.Speed=self.SpeedMax * 0.7
|
self.Speed=self.SpeedMax * 0.7
|
||||||
|
|
||||||
@@ -1497,6 +1501,15 @@ function ARTY:SetStatusInterval(interval)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set time interval for weapon tracking.
|
||||||
|
-- @param #ARTY self
|
||||||
|
-- @param #number interval Time interval in seconds. Default 0.2 seconds.
|
||||||
|
-- @return self
|
||||||
|
function ARTY:SetTrackInterval(interval)
|
||||||
|
self.dtTrack=interval or 0.2
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed.
|
--- Set time how it is waited a unit the first shot event happens. If no shot is fired after this time, the task to fire is aborted and the target removed.
|
||||||
-- @param #ARTY self
|
-- @param #ARTY self
|
||||||
-- @param #number waittime Time in seconds. Default 300 seconds.
|
-- @param #number waittime Time in seconds. Default 300 seconds.
|
||||||
@@ -2129,6 +2142,95 @@ end
|
|||||||
-- Event Handling
|
-- Event Handling
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Function called during tracking of weapon.
|
||||||
|
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
|
||||||
|
-- @param #ARTY self ARTY object.
|
||||||
|
-- @param #ARTY.Target target Target of the weapon.
|
||||||
|
function ARTY._FuncTrack(weapon, self, target)
|
||||||
|
|
||||||
|
-- Coordinate and distance to target.
|
||||||
|
local _coord=weapon.coordinate
|
||||||
|
local _dist=_coord:Get2DDistance(target.coord)
|
||||||
|
local _destroyweapon=false
|
||||||
|
|
||||||
|
-- Debug
|
||||||
|
self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist))
|
||||||
|
|
||||||
|
if target.weapontype==ARTY.WeaponType.IlluminationShells then
|
||||||
|
|
||||||
|
-- Check if within distace.
|
||||||
|
if _dist<target.radius then
|
||||||
|
|
||||||
|
-- Get random coordinate within certain radius of the target.
|
||||||
|
local _cr=target.coord:GetRandomCoordinateInRadius(target.radius)
|
||||||
|
|
||||||
|
-- Get random altitude over target.
|
||||||
|
local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt, self.illuMaxalt)
|
||||||
|
|
||||||
|
-- Adjust explosion height of coordinate.
|
||||||
|
local _ci=COORDINATE:New(_cr.x,_alt,_cr.z)
|
||||||
|
|
||||||
|
-- Create illumination flare.
|
||||||
|
_ci:IlluminationBomb(self.illuPower)
|
||||||
|
|
||||||
|
-- Destroy actual shell.
|
||||||
|
_destroyweapon=true
|
||||||
|
end
|
||||||
|
|
||||||
|
elseif target.weapontype==ARTY.WeaponType.SmokeShells then
|
||||||
|
|
||||||
|
if _dist<target.radius then
|
||||||
|
|
||||||
|
-- Get random coordinate within a certain radius.
|
||||||
|
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
|
||||||
|
|
||||||
|
-- Fire smoke at this coordinate.
|
||||||
|
_cr:Smoke(self.smokeColor)
|
||||||
|
|
||||||
|
-- Destroy actual shell.
|
||||||
|
_destroyweapon=true
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
if _destroyweapon then
|
||||||
|
|
||||||
|
self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.", self.groupname))
|
||||||
|
|
||||||
|
-- Destroy weapon and stop timer.
|
||||||
|
weapon:Destroy()
|
||||||
|
|
||||||
|
-- No more tracking.
|
||||||
|
weapon.tracking=false
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Function called after impact of weapon.
|
||||||
|
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
|
||||||
|
-- @param #ARTY self ARTY object.
|
||||||
|
-- @param #ARTY.Target target Target of the weapon.
|
||||||
|
function ARTY._FuncImpact(weapon, self, target)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.", self.groupname))
|
||||||
|
|
||||||
|
-- Get impact coordinate.
|
||||||
|
local _impactcoord=weapon:GetImpactCoordinate()
|
||||||
|
|
||||||
|
-- Create a "nuclear" explosion and blast at the impact point.
|
||||||
|
if target.weapontype==ARTY.WeaponType.TacticalNukes then
|
||||||
|
self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.", self.groupname))
|
||||||
|
--SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0)
|
||||||
|
self:ScheduleOnce(1.0, ARTY._NuclearBlast, self, _impactcoord)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Eventhandler for shot event.
|
--- Eventhandler for shot event.
|
||||||
-- @param #ARTY self
|
-- @param #ARTY self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
@@ -2162,128 +2264,32 @@ function ARTY:OnEventShot(EventData)
|
|||||||
self:T(self.lid..text)
|
self:T(self.lid..text)
|
||||||
MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug)
|
MESSAGE:New(text, 5):Clear():ToAllIf(self.report or self.Debug)
|
||||||
|
|
||||||
-- Last known position of the weapon fired.
|
|
||||||
local _lastpos={x=0, y=0, z=0}
|
|
||||||
|
|
||||||
--- Track the position of the weapon if it is supposed to model a tac nuke, illumination or smoke shell.
|
|
||||||
-- @param #table _weapon
|
|
||||||
local function _TrackWeapon(_data)
|
|
||||||
|
|
||||||
-- When the pcall status returns false the weapon has hit.
|
|
||||||
local _weaponalive,_currpos = pcall(
|
|
||||||
function()
|
|
||||||
return _data.weapon:getPoint()
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Debug
|
|
||||||
self:T3(self.lid..string.format("ARTY %s: Weapon still in air: %s", self.groupname, tostring(_weaponalive)))
|
|
||||||
|
|
||||||
-- Destroy weapon before impact.
|
|
||||||
local _destroyweapon=false
|
|
||||||
|
|
||||||
if _weaponalive then
|
|
||||||
|
|
||||||
-- Update last position.
|
|
||||||
_lastpos={x=_currpos.x, y=_currpos.y, z=_currpos.z}
|
|
||||||
|
|
||||||
-- Coordinate and distance to target.
|
|
||||||
local _coord=COORDINATE:NewFromVec3(_lastpos)
|
|
||||||
local _dist=_coord:Get2DDistance(_data.target.coord)
|
|
||||||
|
|
||||||
-- Debug
|
|
||||||
self:T3(self.lid..string.format("ARTY %s weapon to target dist = %d m", self.groupname,_dist))
|
|
||||||
|
|
||||||
if _data.target.weapontype==ARTY.WeaponType.IlluminationShells then
|
|
||||||
|
|
||||||
-- Check if within distace.
|
|
||||||
if _dist<_data.target.radius then
|
|
||||||
|
|
||||||
-- Get random coordinate within certain radius of the target.
|
|
||||||
local _cr=_data.target.coord:GetRandomCoordinateInRadius(_data.target.radius)
|
|
||||||
|
|
||||||
-- Get random altitude over target.
|
|
||||||
local _alt=_cr:GetLandHeight()+math.random(self.illuMinalt, self.illuMaxalt)
|
|
||||||
|
|
||||||
-- Adjust explosion height of coordinate.
|
|
||||||
local _ci=COORDINATE:New(_cr.x,_alt,_cr.z)
|
|
||||||
|
|
||||||
-- Create illumination flare.
|
|
||||||
_ci:IlluminationBomb(self.illuPower)
|
|
||||||
|
|
||||||
-- Destroy actual shell.
|
|
||||||
_destroyweapon=true
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif _data.target.weapontype==ARTY.WeaponType.SmokeShells then
|
|
||||||
|
|
||||||
if _dist<_data.target.radius then
|
|
||||||
|
|
||||||
-- Get random coordinate within a certain radius.
|
|
||||||
local _cr=_coord:GetRandomCoordinateInRadius(_data.target.radius)
|
|
||||||
|
|
||||||
-- Fire smoke at this coordinate.
|
|
||||||
_cr:Smoke(self.smokeColor)
|
|
||||||
|
|
||||||
-- Destroy actual shell.
|
|
||||||
_destroyweapon=true
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
if _destroyweapon then
|
|
||||||
|
|
||||||
self:T2(self.lid..string.format("ARTY %s destroying shell, stopping timer.", self.groupname))
|
|
||||||
|
|
||||||
-- Destroy weapon and stop timer.
|
|
||||||
_data.weapon:destroy()
|
|
||||||
return nil
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
-- TODO: Make dt input parameter.
|
|
||||||
local dt=0.02
|
|
||||||
|
|
||||||
self:T3(self.lid..string.format("ARTY %s tracking weapon again in %.3f seconds", self.groupname, dt))
|
|
||||||
|
|
||||||
-- Check again in 0.05 seconds.
|
|
||||||
return timer.getTime() + dt
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
else
|
|
||||||
|
|
||||||
-- Get impact coordinate.
|
|
||||||
local _impactcoord=COORDINATE:NewFromVec3(_lastpos)
|
|
||||||
|
|
||||||
self:I(self.lid..string.format("ARTY %s weapon NOT ALIVE any more.", self.groupname))
|
|
||||||
|
|
||||||
-- Create a "nuclear" explosion and blast at the impact point.
|
|
||||||
if _data.target.weapontype==ARTY.WeaponType.TacticalNukes then
|
|
||||||
self:T(self.lid..string.format("ARTY %s triggering nuclear explosion in one second.", self.groupname))
|
|
||||||
SCHEDULER:New(nil, ARTY._NuclearBlast, {self,_impactcoord}, 1.0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Stop timer.
|
|
||||||
return nil
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Start track the shell if we want to model a tactical nuke.
|
-- Start track the shell if we want to model a tactical nuke.
|
||||||
local _tracknuke = self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0
|
local _tracknuke = self.currentTarget.weapontype==ARTY.WeaponType.TacticalNukes and self.Nukes>0
|
||||||
local _trackillu = self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0
|
local _trackillu = self.currentTarget.weapontype==ARTY.WeaponType.IlluminationShells and self.Nillu>0
|
||||||
local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0
|
local _tracksmoke = self.currentTarget.weapontype==ARTY.WeaponType.SmokeShells and self.Nsmoke>0
|
||||||
|
|
||||||
|
|
||||||
if _tracknuke or _trackillu or _tracksmoke then
|
if _tracknuke or _trackillu or _tracksmoke then
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname))
|
self:T(self.lid..string.format("ARTY %s: Tracking of weapon starts in two seconds.", self.groupname))
|
||||||
|
|
||||||
local _peter={}
|
-- Create a weapon object.
|
||||||
_peter.weapon=EventData.weapon
|
local weapon=WEAPON:New(EventData.weapon)
|
||||||
_peter.target=UTILS.DeepCopy(self.currentTarget)
|
|
||||||
|
|
||||||
timer.scheduleFunction(_TrackWeapon, _peter, timer.getTime() + 2.0)
|
-- Set time step for tracking.
|
||||||
|
weapon:SetTimeStepTrack(self.dtTrack)
|
||||||
|
|
||||||
|
-- Copy target. We need a copy because it might already be overwritten with the next target during flight of weapon.
|
||||||
|
local target=UTILS.DeepCopy(self.currentTarget)
|
||||||
|
|
||||||
|
-- Set callback functions.
|
||||||
|
weapon:SetFuncTrack(ARTY._FuncTrack, self, target)
|
||||||
|
weapon:SetFuncImpact(ARTY._FuncImpact, self, target)
|
||||||
|
|
||||||
|
-- Start tracking in 2 sec (arty ammo should fly a bit).
|
||||||
|
weapon:StartTrack(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get current ammo.
|
-- Get current ammo.
|
||||||
@@ -3931,9 +3937,10 @@ function ARTY:GetAmmo(display)
|
|||||||
return nammo, nshells, nrockets, nmissiles
|
return nammo, nshells, nrockets, nmissiles
|
||||||
end
|
end
|
||||||
|
|
||||||
for _,unit in pairs(units) do
|
for _,_unit in pairs(units) do
|
||||||
|
local unit=_unit --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
if unit and unit:IsAlive() then
|
if unit then
|
||||||
|
|
||||||
-- Output.
|
-- Output.
|
||||||
local text=string.format("ARTY group %s - unit %s:\n", self.groupname, unit:GetName())
|
local text=string.format("ARTY group %s - unit %s:\n", self.groupname, unit:GetName())
|
||||||
|
|||||||
@@ -474,7 +474,7 @@ do -- DETECTION_BASE
|
|||||||
-- @param #string From The From State string.
|
-- @param #string From The From State string.
|
||||||
-- @param #string Event The Event string.
|
-- @param #string Event The Event string.
|
||||||
-- @param #string To The To State string.
|
-- @param #string To The To State string.
|
||||||
-- @param #table DetectedItem The DetectedItem.
|
-- @param #DetectedItem DetectedItem The DetectedItem data structure.
|
||||||
|
|
||||||
self:AddTransition( "*", "Stop", "Stopped" )
|
self:AddTransition( "*", "Stop", "Stopped" )
|
||||||
|
|
||||||
@@ -2478,14 +2478,14 @@ do -- DETECTION_AREAS
|
|||||||
--- DETECTION_AREAS constructor.
|
--- DETECTION_AREAS constructor.
|
||||||
-- @param #DETECTION_AREAS self
|
-- @param #DETECTION_AREAS self
|
||||||
-- @param Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
|
-- @param Core.Set#SET_GROUP DetectionSetGroup The @{Core.Set} of GROUPs in the Forward Air Controller role.
|
||||||
-- @param DCS#Distance DetectionZoneRange The range till which targets are grouped upon the first detected target.
|
-- @param #number DetectionZoneRange The range in meters within which targets are grouped upon the first detected target. Default 5000m.
|
||||||
-- @return #DETECTION_AREAS
|
-- @return #DETECTION_AREAS
|
||||||
function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange )
|
function DETECTION_AREAS:New( DetectionSetGroup, DetectionZoneRange )
|
||||||
|
|
||||||
-- Inherits from DETECTION_BASE
|
-- Inherits from DETECTION_BASE
|
||||||
local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) )
|
local self = BASE:Inherit( self, DETECTION_BASE:New( DetectionSetGroup ) )
|
||||||
|
|
||||||
self.DetectionZoneRange = DetectionZoneRange
|
self.DetectionZoneRange = DetectionZoneRange or 5000
|
||||||
|
|
||||||
self._SmokeDetectedUnits = false
|
self._SmokeDetectedUnits = false
|
||||||
self._FlareDetectedUnits = false
|
self._FlareDetectedUnits = false
|
||||||
@@ -2988,4 +2988,3 @@ do -- DETECTION_AREAS
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
--- FOX class.
|
--- FOX class.
|
||||||
-- @type FOX
|
-- @type FOX
|
||||||
-- @field #string ClassName Name of the class.
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #number verbose Verbosity level.
|
||||||
-- @field #boolean Debug Debug mode. Messages to all about status.
|
-- @field #boolean Debug Debug mode. Messages to all about status.
|
||||||
-- @field #string lid Class id string for output to DCS log file.
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
-- @field #table menuadded Table of groups the menu was added for.
|
-- @field #table menuadded Table of groups the menu was added for.
|
||||||
@@ -124,6 +125,7 @@
|
|||||||
-- @field #FOX
|
-- @field #FOX
|
||||||
FOX = {
|
FOX = {
|
||||||
ClassName = "FOX",
|
ClassName = "FOX",
|
||||||
|
verbose = 0,
|
||||||
Debug = false,
|
Debug = false,
|
||||||
lid = nil,
|
lid = nil,
|
||||||
menuadded = {},
|
menuadded = {},
|
||||||
@@ -168,7 +170,7 @@ FOX = {
|
|||||||
|
|
||||||
--- Missile data table.
|
--- Missile data table.
|
||||||
-- @type FOX.MissileData
|
-- @type FOX.MissileData
|
||||||
-- @field Wrapper.Unit#UNIT weapon Missile weapon unit.
|
-- @field DCS#Weapon weapon Missile weapon object.
|
||||||
-- @field #boolean active If true the missile is active.
|
-- @field #boolean active If true the missile is active.
|
||||||
-- @field #string missileType Type of missile.
|
-- @field #string missileType Type of missile.
|
||||||
-- @field #string missileName Name of missile.
|
-- @field #string missileName Name of missile.
|
||||||
@@ -185,6 +187,8 @@ FOX = {
|
|||||||
-- @field #string targetName Name of the target unit or "unknown".
|
-- @field #string targetName Name of the target unit or "unknown".
|
||||||
-- @field #string targetOrig Name of the "original" target, i.e. the one right after launched.
|
-- @field #string targetOrig Name of the "original" target, i.e. the one right after launched.
|
||||||
-- @field #FOX.PlayerData targetPlayer Player that was targeted or nil.
|
-- @field #FOX.PlayerData targetPlayer Player that was targeted or nil.
|
||||||
|
-- @field Core.Point#COORDINATE missileCoord Missile coordinate during tracking.
|
||||||
|
-- @field Wrapper.Weapon#WEAPON Weapon Weapon object.
|
||||||
|
|
||||||
--- Main radio menu on group level.
|
--- Main radio menu on group level.
|
||||||
-- @field #table MenuF10 Root menu table on group level.
|
-- @field #table MenuF10 Root menu table on group level.
|
||||||
@@ -196,7 +200,7 @@ FOX.MenuF10Root=nil
|
|||||||
|
|
||||||
--- FOX class version.
|
--- FOX class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
FOX.version="0.6.1"
|
FOX.version="0.8.0"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- ToDo list
|
-- ToDo list
|
||||||
@@ -500,6 +504,7 @@ function FOX:SetDisableF10Menu()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Enable F10 menu for all players.
|
--- Enable F10 menu for all players.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @return #FOX self
|
-- @return #FOX self
|
||||||
@@ -510,6 +515,15 @@ function FOX:SetEnableF10Menu()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set verbosity level.
|
||||||
|
-- @param #FOX self
|
||||||
|
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
|
||||||
|
-- @return #FOX self
|
||||||
|
function FOX:SetVerbosity(VerbosityLevel)
|
||||||
|
self.verbose=VerbosityLevel or 0
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Set default player setting for missile destruction.
|
--- Set default player setting for missile destruction.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param #boolean switch If true missiles are destroyed. If false/nil missiles are not destroyed.
|
-- @param #boolean switch If true missiles are destroyed. If false/nil missiles are not destroyed.
|
||||||
@@ -605,7 +619,9 @@ function FOX:onafterStatus(From, Event, To)
|
|||||||
local clock=UTILS.SecondsToClock(time)
|
local clock=UTILS.SecondsToClock(time)
|
||||||
|
|
||||||
-- Status.
|
-- Status.
|
||||||
|
if self.verbose>=1 then
|
||||||
self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate))
|
self:I(self.lid..string.format("Missile trainer status %s: %s", clock, fsmstate))
|
||||||
|
end
|
||||||
|
|
||||||
-- Check missile status.
|
-- Check missile status.
|
||||||
self:_CheckMissileStatus()
|
self:_CheckMissileStatus()
|
||||||
@@ -713,7 +729,9 @@ function FOX:_CheckMissileStatus()
|
|||||||
if #self.missiles==0 then
|
if #self.missiles==0 then
|
||||||
text=text.." none"
|
text=text.." none"
|
||||||
end
|
end
|
||||||
|
if self.verbose>=2 then
|
||||||
self:I(self.lid..text)
|
self:I(self.lid..text)
|
||||||
|
end
|
||||||
|
|
||||||
-- Remove inactive missiles.
|
-- Remove inactive missiles.
|
||||||
for i=#self.missiles,1,-1 do
|
for i=#self.missiles,1,-1 do
|
||||||
@@ -743,7 +761,7 @@ function FOX:_IsProtected(targetunit)
|
|||||||
if targetgroup then
|
if targetgroup then
|
||||||
local targetname=targetgroup:GetName()
|
local targetname=targetgroup:GetName()
|
||||||
|
|
||||||
for _,_group in pairs(self.protectedset:GetSetObjects()) do
|
for _,_group in pairs(self.protectedset:GetSet()) do
|
||||||
local group=_group --Wrapper.Group#GROUP
|
local group=_group --Wrapper.Group#GROUP
|
||||||
|
|
||||||
if group then
|
if group then
|
||||||
@@ -762,101 +780,25 @@ function FOX:_IsProtected(targetunit)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Missle launch event.
|
|
||||||
-- @param #FOX self
|
--- Function called from weapon tracking.
|
||||||
-- @param #string From From state.
|
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
|
||||||
-- @param #string Event Event.
|
-- @param #FOX self FOX object.
|
||||||
-- @param #string To To state.
|
|
||||||
-- @param #FOX.MissileData missile Fired missile
|
-- @param #FOX.MissileData missile Fired missile
|
||||||
function FOX:onafterMissileLaunch(From, Event, To, missile)
|
function FOX._FuncTrack(weapon, self, missile)
|
||||||
|
|
||||||
-- Tracking info and init of last bomb position.
|
|
||||||
local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s", missile.missileType, missile.missileName, tostring(missile.targetName), missile.shooterName)
|
|
||||||
self:I(FOX.lid..text)
|
|
||||||
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
|
||||||
|
|
||||||
-- Loop over players.
|
|
||||||
for _,_player in pairs(self.players) do
|
|
||||||
local player=_player --#FOX.PlayerData
|
|
||||||
|
|
||||||
-- Player position.
|
|
||||||
local playerUnit=player.unit
|
|
||||||
|
|
||||||
-- Check that player is alive and of the opposite coalition.
|
|
||||||
if playerUnit and playerUnit:IsAlive() and player.coalition~=missile.shooterCoalition then
|
|
||||||
|
|
||||||
-- Player missile distance.
|
|
||||||
local distance=playerUnit:GetCoordinate():Get3DDistance(missile.shotCoord)
|
|
||||||
|
|
||||||
-- Player bearing to missile.
|
|
||||||
local bearing=playerUnit:GetCoordinate():HeadingTo(missile.shotCoord)
|
|
||||||
|
|
||||||
-- Alert that missile has been launched.
|
|
||||||
if player.launchalert then
|
|
||||||
|
|
||||||
-- Alert directly targeted players or players that are within missile max range.
|
|
||||||
if (missile.targetPlayer and player.unitname==missile.targetPlayer.unitname) or (distance<missile.missileRange) then
|
|
||||||
|
|
||||||
-- Inform player.
|
|
||||||
local text=string.format("Missile launch detected! Distance %.1f NM, bearing %03d°.", UTILS.MetersToNM(distance), bearing)
|
|
||||||
|
|
||||||
-- Say notching headings.
|
|
||||||
self:ScheduleOnce(5, FOX._SayNotchingHeadings, self, player, missile.weapon)
|
|
||||||
|
|
||||||
--TODO: ALERT or INFO depending on whether this is a direct target.
|
|
||||||
--TODO: lauchalertall option.
|
|
||||||
MESSAGE:New(text, 5, "ALERT"):ToClient(player.client)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Mark coordinate.
|
|
||||||
if player.marklaunch then
|
|
||||||
local text=string.format("Missile launch coordinates:\n%s\n%s", missile.shotCoord:ToStringLLDMS(), missile.shotCoord:ToStringBULLS(player.coalition))
|
|
||||||
missile.shotCoord:MarkToGroup(text, player.group)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Init missile position.
|
|
||||||
local _lastBombPos = {x=0,y=0,z=0}
|
|
||||||
|
|
||||||
-- Missile coordinate.
|
-- Missile coordinate.
|
||||||
local missileCoord = nil --Core.Point#COORDINATE
|
local missileCoord= missile.missileCoord:UpdateFromVec3(weapon.vec3) --COORDINATE:NewFromVec3(_lastBombPos)
|
||||||
|
|
||||||
-- Target unit of the missile.
|
|
||||||
local target=nil --Wrapper.Unit#UNIT
|
|
||||||
|
|
||||||
--- Function monitoring the position of a bomb until impact.
|
|
||||||
local function trackMissile(_ordnance)
|
|
||||||
|
|
||||||
-- When the pcall returns a failure the weapon has hit.
|
|
||||||
local _status,_bombPos = pcall(
|
|
||||||
function()
|
|
||||||
return _ordnance:getPoint()
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Check if status is not nil. If so, we have a valid point.
|
|
||||||
if _status then
|
|
||||||
|
|
||||||
----------------------------------------------
|
|
||||||
-- Still in the air. Remember this position --
|
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
-- Missile position.
|
|
||||||
_lastBombPos = {x=_bombPos.x, y=_bombPos.y, z=_bombPos.z}
|
|
||||||
|
|
||||||
-- Missile coordinate.
|
|
||||||
missileCoord=COORDINATE:NewFromVec3(_lastBombPos)
|
|
||||||
|
|
||||||
-- Missile velocity in m/s.
|
-- Missile velocity in m/s.
|
||||||
local missileVelocity=UTILS.VecNorm(_ordnance:getVelocity())
|
local missileVelocity=weapon:GetSpeed() --UTILS.VecNorm(_ordnance:getVelocity())
|
||||||
|
|
||||||
-- Update missile target if necessary.
|
-- Update missile target if necessary.
|
||||||
self:GetMissileTarget(missile)
|
self:GetMissileTarget(missile)
|
||||||
|
|
||||||
|
-- Target unit of the missile.
|
||||||
|
local target=nil --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
if missile.targetUnit then
|
if missile.targetUnit then
|
||||||
|
|
||||||
-----------------------------------
|
-----------------------------------
|
||||||
@@ -947,13 +889,13 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
if unit:GetName()~=missile.shooterName then
|
if unit:GetName()~=missile.shooterName then
|
||||||
|
|
||||||
-- Player position.
|
-- Player position.
|
||||||
local playerCoord=unit:GetCoordinate()
|
local playerVec3=unit:GetVec3()
|
||||||
|
|
||||||
-- Distance.
|
-- Distance.
|
||||||
local dist=missileCoord:Get3DDistance(playerCoord)
|
local dist=missileCoord:Get3DDistance(playerVec3)
|
||||||
|
|
||||||
-- Distance from shooter to player.
|
-- Distance from shooter to player.
|
||||||
local Dshooter2player=playerCoord:Get3DDistance(missile.shotCoord)
|
local Dshooter2player=missile.shotCoord:Get3DDistance(playerVec3)
|
||||||
|
|
||||||
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
-- Update mindist if necessary. Only include players in range of missile + 50% safety margin.
|
||||||
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
|
if (mindist==nil or dist<mindist) and (Dshooter2player<=missile.missileRange*1.5 or dist<=self.explosiondist) then
|
||||||
@@ -977,23 +919,22 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
if target then
|
if target then
|
||||||
|
|
||||||
-- Target coordinate.
|
-- Target coordinate.
|
||||||
local targetCoord=target:GetCoordinate()
|
local targetVec3=target:GetVec3() --target:GetCoordinate()
|
||||||
|
|
||||||
-- Distance from missile to target.
|
-- Distance from missile to target.
|
||||||
local distance=missileCoord:Get3DDistance(targetCoord)
|
local distance=missileCoord:Get3DDistance(targetVec3)
|
||||||
|
|
||||||
-- Distance missile to shooter.
|
-- Distance missile to shooter.
|
||||||
local distShooter=nil
|
local distShooter=nil
|
||||||
if missile.shooterUnit and missile.shooterUnit:IsAlive() then
|
if missile.shooterUnit and missile.shooterUnit:IsAlive() then
|
||||||
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetCoordinate())
|
distShooter=missileCoord:Get3DDistance(missile.shooterUnit:GetVec3())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
if self.Debug then
|
if self.Debug then
|
||||||
local bearing=targetCoord:HeadingTo(missileCoord)
|
local bearing=missileCoord:HeadingTo(targetVec3)
|
||||||
local eta=distance/missileVelocity
|
local eta=distance/missileVelocity
|
||||||
|
|
||||||
-- Debug distance check.
|
-- Debug distance check.
|
||||||
self:I(self.lid..string.format("Missile %s Target %s: Distance = %.1f m, v=%.1f m/s, bearing=%03d°, ETA=%.1f sec", missile.missileType, target:GetName(), distance, missileVelocity, bearing, eta))
|
self:I(self.lid..string.format("Missile %s Target %s: Distance = %.1f m, v=%.1f m/s, bearing=%03d°, ETA=%.1f sec", missile.missileType, target:GetName(), distance, missileVelocity, bearing, eta))
|
||||||
end
|
end
|
||||||
@@ -1007,12 +948,12 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- If missile is 150 m from target ==> destroy missile if in safe zone.
|
-- If missile is 150 m from target ==> destroy missile if in safe zone.
|
||||||
if destroymissile and self:_CheckCoordSafe(targetCoord) then
|
if destroymissile and self:_CheckCoordSafe(targetVec3) then
|
||||||
|
|
||||||
-- Destroy missile.
|
-- Destroy missile.
|
||||||
self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m",
|
self:I(self.lid..string.format("Destroying missile %s(%s) fired by %s aimed at %s [player=%s] at distance %.1f m",
|
||||||
missile.missileType, missile.missileName, missile.shooterName, target:GetName(), tostring(missile.targetPlayer~=nil), distance))
|
missile.missileType, missile.missileName, missile.shooterName, target:GetName(), tostring(missile.targetPlayer~=nil), distance))
|
||||||
_ordnance:destroy()
|
weapon:Destroy()
|
||||||
|
|
||||||
-- Missile is not active any more.
|
-- Missile is not active any more.
|
||||||
missile.active=false
|
missile.active=false
|
||||||
@@ -1020,7 +961,6 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
-- Debug smoke.
|
-- Debug smoke.
|
||||||
if self.Debug then
|
if self.Debug then
|
||||||
missileCoord:SmokeRed()
|
missileCoord:SmokeRed()
|
||||||
targetCoord:SmokeGreen()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Create event.
|
-- Create event.
|
||||||
@@ -1042,8 +982,8 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
missile.targetPlayer.dead=missile.targetPlayer.dead+1
|
missile.targetPlayer.dead=missile.targetPlayer.dead+1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Terminate timer.
|
-- We could disable the tracking here but then the impact function would not be called.
|
||||||
return nil
|
--weapon.tracking=false
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
@@ -1066,30 +1006,30 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
dt=self.dt00 --0.01
|
dt=self.dt00 --0.01
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check again in dt seconds.
|
-- Set time step.
|
||||||
return timer.getTime()+dt
|
weapon:SetTimeStepTrack(dt)
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Destroy missile.
|
-- No current target.
|
||||||
self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.", missile.missileType, missile.missileName, missile.shooterName))
|
self:T(self.lid..string.format("Missile %s(%s) fired by %s has no current target. Checking back in 0.1 sec.", missile.missileType, missile.missileName, missile.shooterName))
|
||||||
return timer.getTime()+0.1
|
weapon:SetTimeStepTrack(0.1)
|
||||||
|
|
||||||
-- No target ==> terminate timer.
|
|
||||||
--return nil
|
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
end
|
||||||
|
|
||||||
-------------------------------------
|
--- Callback function on impact or destroy otherwise.
|
||||||
-- Missile does not exist any more --
|
-- @param Wrapper.Weapon#WEAPON weapon Weapon object.
|
||||||
-------------------------------------
|
-- @param #FOX self FOX object.
|
||||||
|
-- @param #FOX.MissileData missile Fired missile.
|
||||||
|
function FOX._FuncImpact(weapon, self, missile)
|
||||||
|
|
||||||
if target then
|
if missile.targetPlayer then
|
||||||
|
|
||||||
-- Get human player.
|
-- Get human player.
|
||||||
local player=self:_GetPlayerFromUnit(target)
|
local player=missile.targetPlayer
|
||||||
|
|
||||||
-- Check for player and distance < 10 km.
|
-- Check for player and distance < 10 km.
|
||||||
if player and player.unit:IsAlive() then -- and missileCoord and player.unit:GetCoordinate():Get3DDistance(missileCoord)<10*1000 then
|
if player and player.unit:IsAlive() then -- and missileCoord and player.unit:GetCoordinate():Get3DDistance(missileCoord)<10*1000 then
|
||||||
@@ -1107,15 +1047,79 @@ function FOX:onafterMissileLaunch(From, Event, To, missile)
|
|||||||
|
|
||||||
--Terminate the timer.
|
--Terminate the timer.
|
||||||
self:T(FOX.lid..string.format("Terminating missile track timer."))
|
self:T(FOX.lid..string.format("Terminating missile track timer."))
|
||||||
return nil
|
weapon.tracking=false
|
||||||
|
|
||||||
end -- _status check
|
end
|
||||||
|
|
||||||
|
--- Missle launch event.
|
||||||
|
-- @param #FOX self
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @param #string To To state.
|
||||||
|
-- @param #FOX.MissileData missile Fired missile
|
||||||
|
function FOX:onafterMissileLaunch(From, Event, To, missile)
|
||||||
|
|
||||||
|
-- Tracking info and init of last bomb position.
|
||||||
|
local text=string.format("FOX: Tracking missile %s(%s) - target %s - shooter %s", missile.missileType, missile.missileName, tostring(missile.targetName), missile.shooterName)
|
||||||
|
self:I(FOX.lid..text)
|
||||||
|
MESSAGE:New(text, 10):ToAllIf(self.Debug)
|
||||||
|
|
||||||
|
-- Loop over players.
|
||||||
|
for _,_player in pairs(self.players) do
|
||||||
|
local player=_player --#FOX.PlayerData
|
||||||
|
|
||||||
|
-- Player position.
|
||||||
|
local playerUnit=player.unit
|
||||||
|
|
||||||
|
-- Check that player is alive and of the opposite coalition.
|
||||||
|
if playerUnit and playerUnit:IsAlive() and player.coalition~=missile.shooterCoalition then
|
||||||
|
|
||||||
|
-- Player missile distance.
|
||||||
|
local distance=playerUnit:GetCoordinate():Get3DDistance(missile.shotCoord)
|
||||||
|
|
||||||
|
-- Player bearing to missile.
|
||||||
|
local bearing=playerUnit:GetCoordinate():HeadingTo(missile.shotCoord)
|
||||||
|
|
||||||
|
-- Alert that missile has been launched.
|
||||||
|
if player.launchalert then
|
||||||
|
|
||||||
|
-- Alert directly targeted players or players that are within missile max range.
|
||||||
|
if (missile.targetPlayer and player.unitname==missile.targetPlayer.unitname) or (distance<missile.missileRange) then
|
||||||
|
|
||||||
|
-- Inform player.
|
||||||
|
local text=string.format("Missile launch detected! Distance %.1f NM, bearing %03d°.", UTILS.MetersToNM(distance), bearing)
|
||||||
|
|
||||||
|
-- Say notching headings.
|
||||||
|
self:ScheduleOnce(5, FOX._SayNotchingHeadings, self, player, missile.weapon)
|
||||||
|
|
||||||
|
--TODO: ALERT or INFO depending on whether this is a direct target.
|
||||||
|
--TODO: lauchalertall option.
|
||||||
|
MESSAGE:New(text, 5, "ALERT"):ToClient(player.client)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Mark coordinate.
|
||||||
|
if player.marklaunch then
|
||||||
|
local text=string.format("Missile launch coordinates:\n%s\n%s", missile.shotCoord:ToStringLLDMS(), missile.shotCoord:ToStringBULLS(player.coalition))
|
||||||
|
missile.shotCoord:MarkToGroup(text, player.group)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set callback function for tracking.
|
||||||
|
missile.Weapon:SetFuncTrack(FOX._FuncTrack, self, missile)
|
||||||
|
|
||||||
|
-- Set callback function for impact.
|
||||||
|
missile.Weapon:SetFuncImpact(FOX._FuncImpact, self, missile)
|
||||||
|
|
||||||
end -- end function trackBomb
|
|
||||||
|
|
||||||
-- Weapon is not yet "alife" just yet. Start timer with a little delay.
|
-- Weapon is not yet "alife" just yet. Start timer with a little delay.
|
||||||
self:T(FOX.lid..string.format("Tracking of missile starts in 0.0001 seconds."))
|
self:T(FOX.lid..string.format("Tracking of missile starts in 0.0001 seconds."))
|
||||||
timer.scheduleFunction(trackMissile, missile.weapon, timer.getTime()+0.0001)
|
--timer.scheduleFunction(trackMissile, missile.weapon, timer.getTime()+0.0001)
|
||||||
|
missile.Weapon:StartTrack(0.0001)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1247,29 +1251,28 @@ end
|
|||||||
function FOX:OnEventShot(EventData)
|
function FOX:OnEventShot(EventData)
|
||||||
self:T2({eventshot=EventData})
|
self:T2({eventshot=EventData})
|
||||||
|
|
||||||
if EventData.Weapon==nil then
|
-- Nil checks.
|
||||||
return
|
if EventData.Weapon==nil or EventData.IniDCSUnit==nil or EventData.weapon==nil then
|
||||||
end
|
|
||||||
if EventData.IniDCSUnit==nil then
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Create a weapon object.
|
||||||
|
local weapon=WEAPON:New(EventData.weapon)
|
||||||
|
|
||||||
-- Weapon data.
|
-- Weapon data.
|
||||||
local _weapon = EventData.WeaponName
|
local _weapon = weapon:GetTypeName()
|
||||||
local _target = EventData.Weapon:getTarget()
|
local _target = EventData.Weapon:getTarget()
|
||||||
local _targetName = "unknown"
|
local _targetName = "unknown"
|
||||||
local _targetUnit = nil --Wrapper.Unit#UNIT
|
local _targetUnit = nil --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
-- Weapon descriptor.
|
-- Weapon descriptor.
|
||||||
local desc=EventData.Weapon:getDesc()
|
local desc=weapon.desc
|
||||||
self:T2({desc=desc})
|
self:T2({desc=desc})
|
||||||
|
|
||||||
-- Weapon category: 0=Shell, 1=Missile, 2=Rocket, 3=BOMB
|
|
||||||
local weaponcategory=desc.category
|
|
||||||
|
|
||||||
-- Missile category: 1=AAM, 2=SAM, 6=OTHER
|
-- Missile category: 1=AAM, 2=SAM, 6=OTHER
|
||||||
local missilecategory=desc.missileCategory
|
local missilecategory=desc.missileCategory
|
||||||
|
|
||||||
|
-- Missile range.
|
||||||
local missilerange=nil
|
local missilerange=nil
|
||||||
if missilecategory then
|
if missilecategory then
|
||||||
missilerange=desc.rangeMaxAltMax
|
missilerange=desc.rangeMaxAltMax
|
||||||
@@ -1279,8 +1282,8 @@ function FOX:OnEventShot(EventData)
|
|||||||
self:T2(FOX.lid.."EVENT SHOT: FOX")
|
self:T2(FOX.lid.."EVENT SHOT: FOX")
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Ini unit = %s", tostring(EventData.IniUnitName)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Ini unit = %s", tostring(EventData.IniUnitName)))
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Ini group = %s", tostring(EventData.IniGroupName)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Ini group = %s", tostring(EventData.IniGroupName)))
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s", tostring(_weapon)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon type = %s", tostring(weapon:GetTypeName())))
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s", tostring(weaponcategory)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Weapon categ = %s", tostring(weapon:GetCategory())))
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Missil categ = %s", tostring(missilecategory)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Missil categ = %s", tostring(missilecategory)))
|
||||||
self:T2(FOX.lid..string.format("EVENT SHOT: Missil range = %s", tostring(missilerange)))
|
self:T2(FOX.lid..string.format("EVENT SHOT: Missil range = %s", tostring(missilerange)))
|
||||||
|
|
||||||
@@ -1292,7 +1295,7 @@ function FOX:OnEventShot(EventData)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Track missiles of type AAM=1, SAM=2 or OTHER=6
|
-- Track missiles of type AAM=1, SAM=2 or OTHER=6
|
||||||
local _track = weaponcategory==1 and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6)
|
local _track = weapon:IsMissile() and missilecategory and (missilecategory==1 or missilecategory==2 or missilecategory==6)
|
||||||
|
|
||||||
-- Only track missiles
|
-- Only track missiles
|
||||||
if _track then
|
if _track then
|
||||||
@@ -1301,6 +1304,7 @@ function FOX:OnEventShot(EventData)
|
|||||||
|
|
||||||
missile.active=true
|
missile.active=true
|
||||||
missile.weapon=EventData.weapon
|
missile.weapon=EventData.weapon
|
||||||
|
missile.Weapon=weapon
|
||||||
missile.missileType=_weapon
|
missile.missileType=_weapon
|
||||||
missile.missileRange=missilerange
|
missile.missileRange=missilerange
|
||||||
missile.missileName=EventData.weapon:getName()
|
missile.missileName=EventData.weapon:getName()
|
||||||
@@ -1313,6 +1317,7 @@ function FOX:OnEventShot(EventData)
|
|||||||
missile.fuseDist=desc.fuseDist
|
missile.fuseDist=desc.fuseDist
|
||||||
missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass
|
missile.explosive=desc.warhead.explosiveMass or desc.warhead.shapedExplosiveMass
|
||||||
missile.targetOrig=missile.targetName
|
missile.targetOrig=missile.targetName
|
||||||
|
missile.missileCoord=COORDINATE:New(0,0,0)
|
||||||
|
|
||||||
-- Set missile target name, unit and player.
|
-- Set missile target name, unit and player.
|
||||||
self:GetMissileTarget(missile)
|
self:GetMissileTarget(missile)
|
||||||
@@ -1631,7 +1636,7 @@ end
|
|||||||
|
|
||||||
--- Check if a coordinate lies within a safe training zone.
|
--- Check if a coordinate lies within a safe training zone.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param Core.Point#COORDINATE coord Coordinate to check.
|
-- @param Core.Point#COORDINATE coord Coordinate to check. Can also be a DCS#Vec3.
|
||||||
-- @return #boolean True if safe.
|
-- @return #boolean True if safe.
|
||||||
function FOX:_CheckCoordSafe(coord)
|
function FOX:_CheckCoordSafe(coord)
|
||||||
|
|
||||||
@@ -1643,7 +1648,9 @@ function FOX:_CheckCoordSafe(coord)
|
|||||||
-- Loop over all zones.
|
-- Loop over all zones.
|
||||||
for _,_zone in pairs(self.safezones) do
|
for _,_zone in pairs(self.safezones) do
|
||||||
local zone=_zone --Core.Zone#ZONE
|
local zone=_zone --Core.Zone#ZONE
|
||||||
local inzone=zone:IsCoordinateInZone(coord)
|
local Vec2={x=coord.x, y=coord.z}
|
||||||
|
local inzone=zone:IsVec2InZone(Vec2)
|
||||||
|
--local inzone=zone:IsCoordinateInZone(coord)
|
||||||
if inzone then
|
if inzone then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -1654,7 +1661,7 @@ end
|
|||||||
|
|
||||||
--- Check if a coordinate lies within a launch zone.
|
--- Check if a coordinate lies within a launch zone.
|
||||||
-- @param #FOX self
|
-- @param #FOX self
|
||||||
-- @param Core.Point#COORDINATE coord Coordinate to check.
|
-- @param Core.Point#COORDINATE coord Coordinate to check. Can also be a DCS#Vec2.
|
||||||
-- @return #boolean True if in launch zone.
|
-- @return #boolean True if in launch zone.
|
||||||
function FOX:_CheckCoordLaunch(coord)
|
function FOX:_CheckCoordLaunch(coord)
|
||||||
|
|
||||||
@@ -1666,7 +1673,9 @@ function FOX:_CheckCoordLaunch(coord)
|
|||||||
-- Loop over all zones.
|
-- Loop over all zones.
|
||||||
for _,_zone in pairs(self.launchzones) do
|
for _,_zone in pairs(self.launchzones) do
|
||||||
local zone=_zone --Core.Zone#ZONE
|
local zone=_zone --Core.Zone#ZONE
|
||||||
local inzone=zone:IsCoordinateInZone(coord)
|
local Vec2={x=coord.x, y=coord.z}
|
||||||
|
local inzone=zone:IsVec2InZone(Vec2)
|
||||||
|
--local inzone=zone:IsCoordinateInZone(coord)
|
||||||
if inzone then
|
if inzone then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
-- @field #string ClassName Name of the Class.
|
-- @field #string ClassName Name of the Class.
|
||||||
-- @field #boolean Debug If true, debug info is sent as messages on the screen.
|
-- @field #boolean Debug If true, debug info is sent as messages on the screen.
|
||||||
-- @field #boolean verbose Verbosity level. Higher means more output to DCS log file.
|
-- @field #boolean verbose Verbosity level. Higher means more output to DCS log file.
|
||||||
-- @field #string id String id of range for output in DCS log.
|
-- @field #string lid String id of range for output in DCS log.
|
||||||
-- @field #string rangename Name of the range.
|
-- @field #string rangename Name of the range.
|
||||||
-- @field Core.Point#COORDINATE location Coordinate of the range location.
|
-- @field Core.Point#COORDINATE location Coordinate of the range location.
|
||||||
-- @field #number rangeradius Radius of range defining its total size for e.g. smoking bomb impact points and sending radio messages. Default 5 km.
|
-- @field #number rangeradius Radius of range defining its total size for e.g. smoking bomb impact points and sending radio messages. Default 5 km.
|
||||||
@@ -86,7 +86,6 @@
|
|||||||
-- @field #number illuminationmaxalt Maximum altitude in meters AGL at which illumination bombs are fired. Default is 1000 m.
|
-- @field #number illuminationmaxalt Maximum altitude in meters AGL at which illumination bombs are fired. Default is 1000 m.
|
||||||
-- @field #number scorebombdistance Distance from closest target up to which bomb hits are counted. Default 1000 m.
|
-- @field #number scorebombdistance Distance from closest target up to which bomb hits are counted. Default 1000 m.
|
||||||
-- @field #number TdelaySmoke Time delay in seconds between impact of bomb and starting the smoke. Default 3 seconds.
|
-- @field #number TdelaySmoke Time delay in seconds between impact of bomb and starting the smoke. Default 3 seconds.
|
||||||
-- @field #boolean eventmoose If true, events are handled by MOOSE. If false, events are handled directly by DCS eventhandler. Default true.
|
|
||||||
-- @field #boolean trackbombs If true (default), all bomb types are tracked and impact point to closest bombing target is evaluated.
|
-- @field #boolean trackbombs If true (default), all bomb types are tracked and impact point to closest bombing target is evaluated.
|
||||||
-- @field #boolean trackrockets If true (default), all rocket types are tracked and impact point to closest bombing target is evaluated.
|
-- @field #boolean trackrockets If true (default), all rocket types are tracked and impact point to closest bombing target is evaluated.
|
||||||
-- @field #boolean trackmissiles If true (default), all missile types are tracked and impact point to closest bombing target is evaluated.
|
-- @field #boolean trackmissiles If true (default), all missile types are tracked and impact point to closest bombing target is evaluated.
|
||||||
@@ -102,10 +101,10 @@
|
|||||||
-- @field #boolean targetsheet If true, players can save their target sheets. Rangeboss will not work if targetsheets do not save.
|
-- @field #boolean targetsheet If true, players can save their target sheets. Rangeboss will not work if targetsheets do not save.
|
||||||
-- @field #string targetpath Path where to save the target sheets.
|
-- @field #string targetpath Path where to save the target sheets.
|
||||||
-- @field #string targetprefix File prefix for target sheet files.
|
-- @field #string targetprefix File prefix for target sheet files.
|
||||||
-- @field Sound.SRS#MSRS controlmsrs
|
-- @field Sound.SRS#MSRS controlmsrs SRS wrapper for range controller.
|
||||||
-- @field Sound.SRS#MSRSQUEUE controlsrsQ
|
-- @field Sound.SRS#MSRSQUEUE controlsrsQ SRS queue for range controller.
|
||||||
-- @field Sound.SRS#MSRS instructmsrs
|
-- @field Sound.SRS#MSRS instructmsrs SRS wrapper for range instructor.
|
||||||
-- @field Sound.SRS#MSRSQUEUE instructsrsQ
|
-- @field Sound.SRS#MSRSQUEUE instructsrsQ SRS queue for range instructor.
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
--- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven
|
--- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven
|
||||||
@@ -232,8 +231,8 @@
|
|||||||
--
|
--
|
||||||
-- ## Voice output via SRS
|
-- ## Voice output via SRS
|
||||||
--
|
--
|
||||||
-- Alternatively, the voice output can be fully done via SRS, **no sound file additions needed**. Set up SRS with @{#RANGE.SetSRS}(). Range control and instructor frequencies and voices can then be
|
-- Alternatively, the voice output can be fully done via SRS, **no sound file additions needed**. Set up SRS with @{#RANGE.SetSRS}().
|
||||||
-- set via @{#RANGE.SetSRSRangeControl}() and @{#RANGE.SetSRSRangeInstructor}()
|
-- Range control and instructor frequencies and voices can then be set via @{#RANGE.SetSRSRangeControl}() and @{#RANGE.SetSRSRangeInstructor}().
|
||||||
--
|
--
|
||||||
-- # Persistence
|
-- # Persistence
|
||||||
--
|
--
|
||||||
@@ -343,7 +342,6 @@ RANGE = {
|
|||||||
illuminationmaxalt = 1000,
|
illuminationmaxalt = 1000,
|
||||||
scorebombdistance = 1000,
|
scorebombdistance = 1000,
|
||||||
TdelaySmoke = 3.0,
|
TdelaySmoke = 3.0,
|
||||||
eventmoose = true,
|
|
||||||
trackbombs = true,
|
trackbombs = true,
|
||||||
trackrockets = true,
|
trackrockets = true,
|
||||||
trackmissiles = true,
|
trackmissiles = true,
|
||||||
@@ -360,7 +358,18 @@ RANGE = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
--- Default range parameters.
|
--- Default range parameters.
|
||||||
-- @list Defaults
|
-- @type RANGE.Defaults
|
||||||
|
-- @param #number goodhitrange Radius for good hits in meters.
|
||||||
|
-- @param #number strafemaxalt Max altitude in meters for players to enter a strafing pit.
|
||||||
|
-- @param #number dtBombtrack Timer interval in seconds.
|
||||||
|
-- @param #number Tmsg Message display time in seconds.
|
||||||
|
-- @param #number ndisplayresults Number of results to display.
|
||||||
|
-- @param #number rangeradius Radius of range in meters.
|
||||||
|
-- @param #number TdelaySmoke Time delay in seconds before smoke is triggered.
|
||||||
|
-- @param #number boxlength Length of strafe pit box in meters.
|
||||||
|
-- @param #number boxwidth Width of strafe pit box in meters.
|
||||||
|
-- @param #number goodpass Number of hits for a good strafing pit pass.
|
||||||
|
-- @param #number foulline Distance of foul line in meters.
|
||||||
RANGE.Defaults = {
|
RANGE.Defaults = {
|
||||||
goodhitrange = 25,
|
goodhitrange = 25,
|
||||||
strafemaxalt = 914,
|
strafemaxalt = 914,
|
||||||
@@ -377,13 +386,15 @@ RANGE.Defaults = {
|
|||||||
|
|
||||||
--- Target type, i.e. unit, static, or coordinate.
|
--- Target type, i.e. unit, static, or coordinate.
|
||||||
-- @type RANGE.TargetType
|
-- @type RANGE.TargetType
|
||||||
-- @field #string UNIT Target is a unit.
|
-- @field #string UNIT Target is a unitobject.
|
||||||
-- @field #string STATIC Target is a static.
|
-- @field #string STATIC Target is a static object.
|
||||||
-- @field #string COORD Target is a coordinate.
|
-- @field #string COORD Target is a coordinate.
|
||||||
|
-- @field #string SCENERY Target is a scenery object.
|
||||||
RANGE.TargetType = {
|
RANGE.TargetType = {
|
||||||
UNIT = "Unit",
|
UNIT = "Unit",
|
||||||
STATIC = "Static",
|
STATIC = "Static",
|
||||||
COORD = "Coordinate"
|
COORD = "Coordinate",
|
||||||
|
SCENERY = "Scenery"
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Player settings.
|
--- Player settings.
|
||||||
@@ -395,9 +406,11 @@ RANGE.TargetType = {
|
|||||||
-- @field #boolean messages Display info messages.
|
-- @field #boolean messages Display info messages.
|
||||||
-- @field Wrapper.Client#CLIENT client Client object of player.
|
-- @field Wrapper.Client#CLIENT client Client object of player.
|
||||||
-- @field #string unitname Name of player aircraft unit.
|
-- @field #string unitname Name of player aircraft unit.
|
||||||
|
-- @field Wrapper.Unit#UNIT unit Player unit.
|
||||||
-- @field #string playername Name of player.
|
-- @field #string playername Name of player.
|
||||||
-- @field #string airframe Aircraft type name.
|
-- @field #string airframe Aircraft type name.
|
||||||
-- @field #boolean inzone If true, player is inside the range zone.
|
-- @field #boolean inzone If true, player is inside the range zone.
|
||||||
|
-- @field #boolean targeton Target on.
|
||||||
|
|
||||||
--- Bomb target data.
|
--- Bomb target data.
|
||||||
-- @type RANGE.BombTarget
|
-- @type RANGE.BombTarget
|
||||||
@@ -578,13 +591,14 @@ RANGE.MenuF10Root = nil
|
|||||||
|
|
||||||
--- Range script version.
|
--- Range script version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
RANGE.version = "2.5.1"
|
RANGE.version = "2.7.0"
|
||||||
|
|
||||||
-- TODO list:
|
-- TODO list:
|
||||||
-- TODO: Verbosity level for messages.
|
-- TODO: Verbosity level for messages.
|
||||||
-- TODO: Add option for default settings such as smoke off.
|
-- TODO: Add option for default settings such as smoke off.
|
||||||
-- TODO: Add custom weapons, which can be specified by the user.
|
-- TODO: Add custom weapons, which can be specified by the user.
|
||||||
-- TODO: Check if units are still alive.
|
-- TODO: Check if units are still alive.
|
||||||
|
-- DONE: Scenery as targets.
|
||||||
-- DONE: Add statics for strafe pits.
|
-- DONE: Add statics for strafe pits.
|
||||||
-- DONE: Add missiles.
|
-- DONE: Add missiles.
|
||||||
-- DONE: Convert env.info() to self:T()
|
-- DONE: Convert env.info() to self:T()
|
||||||
@@ -610,11 +624,11 @@ function RANGE:New( RangeName )
|
|||||||
self.rangename = RangeName or "Practice Range"
|
self.rangename = RangeName or "Practice Range"
|
||||||
|
|
||||||
-- Log id.
|
-- Log id.
|
||||||
self.id = string.format( "RANGE %s | ", self.rangename )
|
self.lid = string.format( "RANGE %s | ", self.rangename )
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
local text = string.format( "Script version %s - creating new RANGE object %s.", RANGE.version, self.rangename )
|
local text = string.format( "Script version %s - creating new RANGE object %s.", RANGE.version, self.rangename )
|
||||||
self:I( self.id .. text )
|
self:I( self.lid .. text )
|
||||||
|
|
||||||
-- Defaults
|
-- Defaults
|
||||||
self:SetDefaultPlayerSmokeBomb()
|
self:SetDefaultPlayerSmokeBomb()
|
||||||
@@ -802,7 +816,7 @@ function RANGE:onafterStart()
|
|||||||
|
|
||||||
if self.location == nil then
|
if self.location == nil then
|
||||||
local text = string.format( "ERROR! No range location found. Number of strafe targets = %d. Number of bomb targets = %d.", self.nstrafetargets, self.nbombtargets )
|
local text = string.format( "ERROR! No range location found. Number of strafe targets = %d. Number of bomb targets = %d.", self.nstrafetargets, self.nbombtargets )
|
||||||
self:E( self.id .. text )
|
self:E( self.lid .. text )
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -813,31 +827,20 @@ function RANGE:onafterStart()
|
|||||||
|
|
||||||
-- Starting range.
|
-- Starting range.
|
||||||
local text = string.format( "Starting RANGE %s. Number of strafe targets = %d. Number of bomb targets = %d.", self.rangename, self.nstrafetargets, self.nbombtargets )
|
local text = string.format( "Starting RANGE %s. Number of strafe targets = %d. Number of bomb targets = %d.", self.rangename, self.nstrafetargets, self.nbombtargets )
|
||||||
self:I( self.id .. text )
|
self:I( self.lid .. text )
|
||||||
|
|
||||||
-- Event handling.
|
-- Event handling.
|
||||||
if self.eventmoose then
|
|
||||||
-- Events are handled my MOOSE.
|
|
||||||
self:T( self.id .. "Events are handled by MOOSE." )
|
|
||||||
self:HandleEvent( EVENTS.Birth )
|
self:HandleEvent( EVENTS.Birth )
|
||||||
self:HandleEvent( EVENTS.Hit )
|
self:HandleEvent( EVENTS.Hit )
|
||||||
self:HandleEvent( EVENTS.Shot )
|
self:HandleEvent( EVENTS.Shot )
|
||||||
else
|
|
||||||
-- Events are handled directly by DCS.
|
|
||||||
self:T( self.id .. "Events are handled directly by DCS." )
|
|
||||||
world.addEventHandler( self )
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Make bomb target move randomly within the range zone.
|
-- Make bomb target move randomly within the range zone.
|
||||||
for _, _target in pairs( self.bombingTargets ) do
|
for _, _target in pairs( self.bombingTargets ) do
|
||||||
|
local target=_target --#RANGE.BombTarget
|
||||||
|
|
||||||
-- Check if it is a static object.
|
-- Check if unit and can move.
|
||||||
-- local _static=self:_CheckStatic(_target.target:GetName())
|
if target.move and target.type==RANGE.TargetType.UNIT and target.speed > 1 then
|
||||||
local _static = _target.type == RANGE.TargetType.STATIC
|
target.target:PatrolZones( { self.rangezone }, target.speed * 0.75, ENUMS.Formation.Vehicle.OffRoad )
|
||||||
|
|
||||||
if _target.move and _static == false and _target.speed > 1 then
|
|
||||||
local unit = _target.target -- Wrapper.Unit#UNIT
|
|
||||||
_target.target:PatrolZones( { self.rangezone }, _target.speed * 0.75, "Off road" )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -1064,7 +1067,7 @@ end
|
|||||||
|
|
||||||
--- Set smoke color for marking bomb targets. By default bomb targets are marked by red smoke.
|
--- Set smoke color for marking bomb targets. By default bomb targets are marked by red smoke.
|
||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default SMOKECOLOR.Red.
|
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default `SMOKECOLOR.Red`.
|
||||||
-- @return #RANGE self
|
-- @return #RANGE self
|
||||||
function RANGE:SetBombTargetSmokeColor( colorid )
|
function RANGE:SetBombTargetSmokeColor( colorid )
|
||||||
self.BombSmokeColor = colorid or SMOKECOLOR.Red
|
self.BombSmokeColor = colorid or SMOKECOLOR.Red
|
||||||
@@ -1082,7 +1085,7 @@ end
|
|||||||
|
|
||||||
--- Set smoke color for marking strafe targets. By default strafe targets are marked by green smoke.
|
--- Set smoke color for marking strafe targets. By default strafe targets are marked by green smoke.
|
||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default SMOKECOLOR.Green.
|
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default `SMOKECOLOR.Green`.
|
||||||
-- @return #RANGE self
|
-- @return #RANGE self
|
||||||
function RANGE:SetStrafeTargetSmokeColor( colorid )
|
function RANGE:SetStrafeTargetSmokeColor( colorid )
|
||||||
self.StrafeSmokeColor = colorid or SMOKECOLOR.Green
|
self.StrafeSmokeColor = colorid or SMOKECOLOR.Green
|
||||||
@@ -1091,7 +1094,7 @@ end
|
|||||||
|
|
||||||
--- Set smoke color for marking strafe pit approach boxes. By default strafe pit boxes are marked by white smoke.
|
--- Set smoke color for marking strafe pit approach boxes. By default strafe pit boxes are marked by white smoke.
|
||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default SMOKECOLOR.White.
|
-- @param Utilities.Utils#SMOKECOLOR colorid Color id. Default `SMOKECOLOR.White`.
|
||||||
-- @return #RANGE self
|
-- @return #RANGE self
|
||||||
function RANGE:SetStrafePitSmokeColor( colorid )
|
function RANGE:SetStrafePitSmokeColor( colorid )
|
||||||
self.StrafePitSmokeColor = colorid or SMOKECOLOR.White
|
self.StrafePitSmokeColor = colorid or SMOKECOLOR.White
|
||||||
@@ -1191,13 +1194,14 @@ end
|
|||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param #string PathToSRS Path to SRS directory.
|
-- @param #string PathToSRS Path to SRS directory.
|
||||||
-- @param #number Port SRS port. Default 5002.
|
-- @param #number Port SRS port. Default 5002.
|
||||||
-- @param #number Coalition Coalition side, e.g. coalition.side.BLUE or coalition.side.RED
|
-- @param #number Coalition Coalition side, e.g. `coalition.side.BLUE` or `coalition.side.RED`. Default `coalition.side.BLUE`.
|
||||||
-- @param #number Frequency Frequency to use, defaults to 256 (same as rangecontrol)
|
-- @param #number Frequency Frequency to use. Default is 256 MHz for range control and 305 MHz for instructor. If given, both control and instructor get this frequency.
|
||||||
-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM
|
-- @param #number Modulation Modulation to use, defaults to radio.modulation.AM
|
||||||
-- @param #number Volume Volume, between 0.0 and 1.0. Defaults to 1.0
|
-- @param #number Volume Volume, between 0.0 and 1.0. Defaults to 1.0
|
||||||
-- @param #string PathToGoogleKey Path to Google TTS credentials.
|
-- @param #string PathToGoogleKey Path to Google TTS credentials.
|
||||||
-- @return #RANGE self
|
-- @return #RANGE self
|
||||||
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey)
|
function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume, PathToGoogleKey)
|
||||||
|
|
||||||
if PathToSRS then
|
if PathToSRS then
|
||||||
|
|
||||||
self.useSRS=true
|
self.useSRS=true
|
||||||
@@ -1215,7 +1219,7 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume,
|
|||||||
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
|
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
|
||||||
|
|
||||||
if PathToGoogleKey then
|
if PathToGoogleKey then
|
||||||
self.instructmsrs:SetGoogle(PathToGoogleKey)
|
self.controlmsrs:SetGoogle(PathToGoogleKey)
|
||||||
self.instructmsrs:SetGoogle(PathToGoogleKey)
|
self.instructmsrs:SetGoogle(PathToGoogleKey)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1304,7 +1308,7 @@ end
|
|||||||
-- @return #RANGE self
|
-- @return #RANGE self
|
||||||
function RANGE:SetSoundfilesPath( path )
|
function RANGE:SetSoundfilesPath( path )
|
||||||
self.soundpath = tostring( path or "Range Soundfiles/" )
|
self.soundpath = tostring( path or "Range Soundfiles/" )
|
||||||
self:I( self.id .. string.format( "Setting sound files path to %s", self.soundpath ) )
|
self:I( self.lid .. string.format( "Setting sound files path to %s", self.soundpath ) )
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1342,20 +1346,20 @@ function RANGE:AddStrafePit( targetnames, boxlength, boxwidth, heading, inverseh
|
|||||||
if _isstatic == true then
|
if _isstatic == true then
|
||||||
|
|
||||||
-- Add static object.
|
-- Add static object.
|
||||||
self:T( self.id .. string.format( "Adding STATIC object %s as strafe target #%d.", _name, _i ) )
|
self:T( self.lid .. string.format( "Adding STATIC object %s as strafe target #%d.", _name, _i ) )
|
||||||
unit = STATIC:FindByName( _name, false )
|
unit = STATIC:FindByName( _name, false )
|
||||||
|
|
||||||
elseif _isstatic == false then
|
elseif _isstatic == false then
|
||||||
|
|
||||||
-- Add unit object.
|
-- Add unit object.
|
||||||
self:T( self.id .. string.format( "Adding UNIT object %s as strafe target #%d.", _name, _i ) )
|
self:T( self.lid .. string.format( "Adding UNIT object %s as strafe target #%d.", _name, _i ) )
|
||||||
unit = UNIT:FindByName( _name )
|
unit = UNIT:FindByName( _name )
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Neither unit nor static object with this name could be found.
|
-- Neither unit nor static object with this name could be found.
|
||||||
local text = string.format( "ERROR! Could not find ANY strafe target object with name %s.", _name )
|
local text = string.format( "ERROR! Could not find ANY strafe target object with name %s.", _name )
|
||||||
self:E( self.id .. text )
|
self:E( self.lid .. text )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1374,7 +1378,7 @@ function RANGE:AddStrafePit( targetnames, boxlength, boxwidth, heading, inverseh
|
|||||||
-- Check if at least one target could be found.
|
-- Check if at least one target could be found.
|
||||||
if ntargets == 0 then
|
if ntargets == 0 then
|
||||||
local text = string.format( "ERROR! No strafe target could be found when calling RANGE:AddStrafePit() for range %s", self.rangename )
|
local text = string.format( "ERROR! No strafe target could be found when calling RANGE:AddStrafePit() for range %s", self.rangename )
|
||||||
self:E( self.id .. text )
|
self:E( self.lid .. text )
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1443,7 +1447,7 @@ function RANGE:AddStrafePit( targetnames, boxlength, boxwidth, heading, inverseh
|
|||||||
|
|
||||||
-- Debug info
|
-- Debug info
|
||||||
local text = string.format( "Adding new strafe target %s with %d targets: heading = %03d, box_L = %.1f, box_W = %.1f, goodpass = %d, foul line = %.1f", _name, ntargets, heading, l, w, goodpass, foulline )
|
local text = string.format( "Adding new strafe target %s with %d targets: heading = %03d, box_L = %.1f, box_W = %.1f, goodpass = %d, foul line = %.1f", _name, ntargets, heading, l, w, goodpass, foulline )
|
||||||
self:T( self.id .. text )
|
self:T( self.lid .. text )
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@@ -1513,14 +1517,14 @@ function RANGE:AddBombingTargets( targetnames, goodhitrange, randommove )
|
|||||||
|
|
||||||
if _isstatic == true then
|
if _isstatic == true then
|
||||||
local _static = STATIC:FindByName( name )
|
local _static = STATIC:FindByName( name )
|
||||||
self:T2( self.id .. string.format( "Adding static bombing target %s with hit range %d.", name, goodhitrange, false ) )
|
self:T2( self.lid .. string.format( "Adding static bombing target %s with hit range %d.", name, goodhitrange, false ) )
|
||||||
self:AddBombingTargetUnit( _static, goodhitrange )
|
self:AddBombingTargetUnit( _static, goodhitrange )
|
||||||
elseif _isstatic == false then
|
elseif _isstatic == false then
|
||||||
local _unit = UNIT:FindByName( name )
|
local _unit = UNIT:FindByName( name )
|
||||||
self:T2( self.id .. string.format( "Adding unit bombing target %s with hit range %d.", name, goodhitrange, randommove ) )
|
self:T2( self.lid .. string.format( "Adding unit bombing target %s with hit range %d.", name, goodhitrange, randommove ) )
|
||||||
self:AddBombingTargetUnit( _unit, goodhitrange, randommove )
|
self:AddBombingTargetUnit( _unit, goodhitrange, randommove )
|
||||||
else
|
else
|
||||||
self:E( self.id .. string.format( "ERROR! Could not find bombing target %s.", name ) )
|
self:E( self.lid .. string.format( "ERROR! Could not find bombing target %s.", name ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -1530,7 +1534,7 @@ end
|
|||||||
|
|
||||||
--- Add a unit or static object as bombing target.
|
--- Add a unit or static object as bombing target.
|
||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param Wrapper.Positionable#POSITIONABLE unit Positionable (unit or static) of the strafe target.
|
-- @param Wrapper.Positionable#POSITIONABLE unit Positionable (unit or static) of the bombing target.
|
||||||
-- @param #number goodhitrange Max distance from unit which is considered as a good hit.
|
-- @param #number goodhitrange Max distance from unit which is considered as a good hit.
|
||||||
-- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
|
-- @param #boolean randommove If true, unit will move randomly within the range. Default is false.
|
||||||
-- @return #RANGE self
|
-- @return #RANGE self
|
||||||
@@ -1553,11 +1557,11 @@ function RANGE:AddBombingTargetUnit( unit, goodhitrange, randommove )
|
|||||||
|
|
||||||
-- Debug or error output.
|
-- Debug or error output.
|
||||||
if _isstatic == true then
|
if _isstatic == true then
|
||||||
self:I( self.id .. string.format( "Adding STATIC bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
self:I( self.lid .. string.format( "Adding STATIC bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
||||||
elseif _isstatic == false then
|
elseif _isstatic == false then
|
||||||
self:I( self.id .. string.format( "Adding UNIT bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
self:I( self.lid .. string.format( "Adding UNIT bombing target %s with good hit range %d. Random move = %s.", name, goodhitrange, tostring( randommove ) ) )
|
||||||
else
|
else
|
||||||
self:E( self.id .. string.format( "ERROR! No bombing target with name %s could be found. Carefully check all UNIT and STATIC names defined in the mission editor!", name ) )
|
self:E( self.lid .. string.format( "ERROR! No bombing target with name %s could be found. Carefully check all UNIT and STATIC names defined in the mission editor!", name ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get max speed of unit in km/h.
|
-- Get max speed of unit in km/h.
|
||||||
@@ -1608,6 +1612,42 @@ function RANGE:AddBombingTargetCoordinate( coord, name, goodhitrange )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Add a scenery object as bombing target.
|
||||||
|
-- @param #RANGE self
|
||||||
|
-- @param Wrapper.Scenery#SCENERY scenery Scenary object.
|
||||||
|
-- @param #number goodhitrange Max distance from unit which is considered as a good hit.
|
||||||
|
-- @return #RANGE self
|
||||||
|
function RANGE:AddBombingTargetScenery( scenery, goodhitrange)
|
||||||
|
|
||||||
|
-- Get name of positionable.
|
||||||
|
local name = scenery:GetName()
|
||||||
|
|
||||||
|
-- Default range is 25 m.
|
||||||
|
goodhitrange = goodhitrange or RANGE.Defaults.goodhitrange
|
||||||
|
|
||||||
|
-- Debug or error output.
|
||||||
|
if name then
|
||||||
|
self:I( self.lid .. string.format( "Adding SCENERY bombing target %s with good hit range %d", name, goodhitrange) )
|
||||||
|
else
|
||||||
|
self:E( self.lid .. string.format( "ERROR! No bombing target with name %s could be found!", name ) )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local target = {} -- #RANGE.BombTarget
|
||||||
|
target.name = name
|
||||||
|
target.target = scenery
|
||||||
|
target.goodhitrange = goodhitrange
|
||||||
|
target.move = false
|
||||||
|
target.speed = 0
|
||||||
|
target.coordinate = scenery:GetCoordinate()
|
||||||
|
target.type = RANGE.TargetType.SCENERY
|
||||||
|
|
||||||
|
-- Insert target to table.
|
||||||
|
table.insert( self.bombingTargets, target )
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Add all units of a group as bombing targets.
|
--- Add all units of a group as bombing targets.
|
||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param Wrapper.Group#GROUP group Group of bombing targets.
|
-- @param Wrapper.Group#GROUP group Group of bombing targets.
|
||||||
@@ -1650,7 +1690,7 @@ function RANGE:GetFoullineDistance( namepit, namefoulline )
|
|||||||
elseif _staticpit == false then
|
elseif _staticpit == false then
|
||||||
pit = UNIT:FindByName( namepit )
|
pit = UNIT:FindByName( namepit )
|
||||||
else
|
else
|
||||||
self:E( self.id .. string.format( "ERROR! Pit object %s could not be found in GetFoullineDistance function. Check the name in the ME.", namepit ) )
|
self:E( self.lid .. string.format( "ERROR! Pit object %s could not be found in GetFoullineDistance function. Check the name in the ME.", namepit ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the unit or static foul line object.
|
-- Get the unit or static foul line object.
|
||||||
@@ -1660,7 +1700,7 @@ function RANGE:GetFoullineDistance( namepit, namefoulline )
|
|||||||
elseif _staticfoul == false then
|
elseif _staticfoul == false then
|
||||||
foul = UNIT:FindByName( namefoulline )
|
foul = UNIT:FindByName( namefoulline )
|
||||||
else
|
else
|
||||||
self:E( self.id .. string.format( "ERROR! Foul line object %s could not be found in GetFoullineDistance function. Check the name in the ME.", namefoulline ) )
|
self:E( self.lid .. string.format( "ERROR! Foul line object %s could not be found in GetFoullineDistance function. Check the name in the ME.", namefoulline ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Get the distance between the two objects.
|
-- Get the distance between the two objects.
|
||||||
@@ -1668,10 +1708,10 @@ function RANGE:GetFoullineDistance( namepit, namefoulline )
|
|||||||
if pit ~= nil and foul ~= nil then
|
if pit ~= nil and foul ~= nil then
|
||||||
fouldist = pit:GetCoordinate():Get2DDistance( foul:GetCoordinate() )
|
fouldist = pit:GetCoordinate():Get2DDistance( foul:GetCoordinate() )
|
||||||
else
|
else
|
||||||
self:E( self.id .. string.format( "ERROR! Foul line distance could not be determined. Check pit object name %s and foul line object name %s in the ME.", namepit, namefoulline ) )
|
self:E( self.lid .. string.format( "ERROR! Foul line distance could not be determined. Check pit object name %s and foul line object name %s in the ME.", namepit, namefoulline ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
self:T( self.id .. string.format( "Foul line distance = %.1f m.", fouldist ) )
|
self:T( self.lid .. string.format( "Foul line distance = %.1f m.", fouldist ) )
|
||||||
return fouldist
|
return fouldist
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1679,73 +1719,6 @@ end
|
|||||||
-- Event Handling
|
-- Event Handling
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
--- General event handler.
|
|
||||||
-- @param #RANGE self
|
|
||||||
-- @param #table Event DCS event table.
|
|
||||||
function RANGE:onEvent( Event )
|
|
||||||
self:F3( Event )
|
|
||||||
|
|
||||||
if Event == nil or Event.initiator == nil then
|
|
||||||
self:T3( "Skipping onEvent. Event or Event.initiator unknown." )
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
if Unit.getByName( Event.initiator:getName() ) == nil then
|
|
||||||
self:T3( "Skipping onEvent. Initiator unit name unknown." )
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local DCSiniunit = Event.initiator
|
|
||||||
local DCStgtunit = Event.target
|
|
||||||
local DCSweapon = Event.weapon
|
|
||||||
|
|
||||||
local EventData = {}
|
|
||||||
local _playerunit = nil
|
|
||||||
local _playername = nil
|
|
||||||
|
|
||||||
if Event.initiator then
|
|
||||||
EventData.IniUnitName = Event.initiator:getName()
|
|
||||||
EventData.IniDCSGroup = Event.initiator:getGroup()
|
|
||||||
EventData.IniGroupName = Event.initiator:getGroup():getName()
|
|
||||||
-- Get player unit and name. This returns nil,nil if the event was not fired by a player unit. And these are the only events we are interested in.
|
|
||||||
_playerunit, _playername = self:_GetPlayerUnitAndName( EventData.IniUnitName )
|
|
||||||
end
|
|
||||||
|
|
||||||
if Event.target then
|
|
||||||
EventData.TgtUnitName = Event.target:getName()
|
|
||||||
EventData.TgtUnit = UNIT:FindByName( EventData.TgtUnitName )
|
|
||||||
end
|
|
||||||
|
|
||||||
if Event.weapon then
|
|
||||||
EventData.Weapon = Event.weapon
|
|
||||||
EventData.weapon = Event.weapon
|
|
||||||
EventData.WeaponTypeName = Event.weapon:getTypeName()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Event info.
|
|
||||||
self:T3( self.id .. string.format( "EVENT: Event in onEvent with ID = %s", tostring( Event.id ) ) )
|
|
||||||
self:T3( self.id .. string.format( "EVENT: Ini unit = %s", tostring( EventData.IniUnitName ) ) )
|
|
||||||
self:T3( self.id .. string.format( "EVENT: Ini group = %s", tostring( EventData.IniGroupName ) ) )
|
|
||||||
self:T3( self.id .. string.format( "EVENT: Ini player = %s", tostring( _playername ) ) )
|
|
||||||
self:T3( self.id .. string.format( "EVENT: Tgt unit = %s", tostring( EventData.TgtUnitName ) ) )
|
|
||||||
self:T3( self.id .. string.format( "EVENT: Wpn type = %s", tostring( EventData.WeaponTypeName ) ) )
|
|
||||||
|
|
||||||
-- Call event Birth function.
|
|
||||||
if Event.id == world.event.S_EVENT_BIRTH and _playername then
|
|
||||||
self:OnEventBirth( EventData )
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Call event Shot function.
|
|
||||||
if Event.id == world.event.S_EVENT_SHOT and _playername and Event.weapon then
|
|
||||||
self:OnEventShot( EventData )
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Call event Hit function.
|
|
||||||
if Event.id == world.event.S_EVENT_HIT and _playername and DCStgtunit then
|
|
||||||
self:OnEventHit( EventData )
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Range event handler for event birth.
|
--- Range event handler for event birth.
|
||||||
-- @param #RANGE self
|
-- @param #RANGE self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
@@ -1755,9 +1728,9 @@ function RANGE:OnEventBirth( EventData )
|
|||||||
local _unitName = EventData.IniUnitName
|
local _unitName = EventData.IniUnitName
|
||||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
||||||
|
|
||||||
self:T3( self.id .. "BIRTH: unit = " .. tostring( EventData.IniUnitName ) )
|
self:T3( self.lid .. "BIRTH: unit = " .. tostring( EventData.IniUnitName ) )
|
||||||
self:T3( self.id .. "BIRTH: group = " .. tostring( EventData.IniGroupName ) )
|
self:T3( self.lid .. "BIRTH: group = " .. tostring( EventData.IniGroupName ) )
|
||||||
self:T3( self.id .. "BIRTH: player = " .. tostring( _playername ) )
|
self:T3( self.lid .. "BIRTH: player = " .. tostring( _playername ) )
|
||||||
|
|
||||||
if _unit and _playername then
|
if _unit and _playername then
|
||||||
|
|
||||||
@@ -1768,7 +1741,7 @@ function RANGE:OnEventBirth( EventData )
|
|||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
local text = string.format( "Player %s, callsign %s entered unit %s (UID %d) of group %s (GID %d)", _playername, _callsign, _unitName, _uid, _group:GetName(), _gid )
|
local text = string.format( "Player %s, callsign %s entered unit %s (UID %d) of group %s (GID %d)", _playername, _callsign, _unitName, _uid, _group:GetName(), _gid )
|
||||||
self:T( self.id .. text )
|
self:T( self.lid .. text )
|
||||||
|
|
||||||
-- Reset current strafe status.
|
-- Reset current strafe status.
|
||||||
self.strafeStatus[_uid] = nil
|
self.strafeStatus[_uid] = nil
|
||||||
@@ -1786,6 +1759,7 @@ function RANGE:OnEventBirth( EventData )
|
|||||||
self.PlayerSettings[_playername].messages = true
|
self.PlayerSettings[_playername].messages = true
|
||||||
self.PlayerSettings[_playername].client = CLIENT:FindByName( _unitName, nil, true )
|
self.PlayerSettings[_playername].client = CLIENT:FindByName( _unitName, nil, true )
|
||||||
self.PlayerSettings[_playername].unitname = _unitName
|
self.PlayerSettings[_playername].unitname = _unitName
|
||||||
|
self.PlayerSettings[_playername].unit = _unit
|
||||||
self.PlayerSettings[_playername].playername = _playername
|
self.PlayerSettings[_playername].playername = _playername
|
||||||
self.PlayerSettings[_playername].airframe = EventData.IniUnit:GetTypeName()
|
self.PlayerSettings[_playername].airframe = EventData.IniUnit:GetTypeName()
|
||||||
self.PlayerSettings[_playername].inzone = false
|
self.PlayerSettings[_playername].inzone = false
|
||||||
@@ -1806,9 +1780,9 @@ function RANGE:OnEventHit( EventData )
|
|||||||
self:F( { eventhit = EventData } )
|
self:F( { eventhit = EventData } )
|
||||||
|
|
||||||
-- Debug info.
|
-- Debug info.
|
||||||
self:T3( self.id .. "HIT: Ini unit = " .. tostring( EventData.IniUnitName ) )
|
self:T3( self.lid .. "HIT: Ini unit = " .. tostring( EventData.IniUnitName ) )
|
||||||
self:T3( self.id .. "HIT: Ini group = " .. tostring( EventData.IniGroupName ) )
|
self:T3( self.lid .. "HIT: Ini group = " .. tostring( EventData.IniGroupName ) )
|
||||||
self:T3( self.id .. "HIT: Tgt target = " .. tostring( EventData.TgtUnitName ) )
|
self:T3( self.lid .. "HIT: Tgt target = " .. tostring( EventData.TgtUnitName ) )
|
||||||
|
|
||||||
-- Player info
|
-- Player info
|
||||||
local _unitName = EventData.IniUnitName
|
local _unitName = EventData.IniUnitName
|
||||||
@@ -1861,7 +1835,7 @@ function RANGE:OnEventHit( EventData )
|
|||||||
self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,2)
|
self.controlsrsQ:NewTransmission(ttstext,nil,self.controlmsrs,nil,2)
|
||||||
end
|
end
|
||||||
self:_DisplayMessageToGroup( _unit, text )
|
self:_DisplayMessageToGroup( _unit, text )
|
||||||
self:T2( self.id .. text )
|
self:T2( self.lid .. text )
|
||||||
_currentTarget.pastfoulline = true
|
_currentTarget.pastfoulline = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1894,115 +1868,14 @@ function RANGE:OnEventHit( EventData )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
|
--- Function called on impact of a tracked weapon.
|
||||||
-- @param #RANGE self
|
-- @param Wrapper.Weapon#WEAPON weapon The weapon object.
|
||||||
-- @param #table weapon Weapon
|
-- @param #RANGE self RANGE object.
|
||||||
function RANGE:_TrackWeapon(weapon)
|
-- @param #RANGE.PlayerData playerData Player data table.
|
||||||
|
-- @param #number attackHdg Attack heading.
|
||||||
end
|
-- @param #number attackAlt Attack altitude.
|
||||||
|
-- @param #number attackVel Attack velocity.
|
||||||
--- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
|
function RANGE._OnImpact(weapon, self, playerData, attackHdg, attackAlt, attackVel)
|
||||||
-- @param #RANGE self
|
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
|
||||||
function RANGE:OnEventShot( EventData )
|
|
||||||
self:F( { eventshot = EventData } )
|
|
||||||
|
|
||||||
-- Nil checks.
|
|
||||||
if EventData.Weapon == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if EventData.IniDCSUnit == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if EventData.IniPlayerName == nil then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Weapon data.
|
|
||||||
local _weapon = EventData.Weapon:getTypeName() -- should be the same as Event.WeaponTypeName
|
|
||||||
local _weaponStrArray = UTILS.Split( _weapon, "%." )
|
|
||||||
local _weaponName = _weaponStrArray[#_weaponStrArray]
|
|
||||||
|
|
||||||
-- Weapon descriptor.
|
|
||||||
local desc = EventData.Weapon:getDesc()
|
|
||||||
|
|
||||||
-- Weapon category: 0=SHELL, 1=MISSILE, 2=ROCKET, 3=BOMB (Weapon.Category.X)
|
|
||||||
local weaponcategory = desc.category
|
|
||||||
|
|
||||||
-- Debug info.
|
|
||||||
self:T( self.id .. "EVENT SHOT: Range " .. self.rangename )
|
|
||||||
self:T( self.id .. "EVENT SHOT: Ini unit = " .. EventData.IniUnitName )
|
|
||||||
self:T( self.id .. "EVENT SHOT: Ini group = " .. EventData.IniGroupName )
|
|
||||||
self:T( self.id .. "EVENT SHOT: Weapon type = " .. _weapon )
|
|
||||||
self:T( self.id .. "EVENT SHOT: Weapon name = " .. _weaponName )
|
|
||||||
self:T( self.id .. "EVENT SHOT: Weapon cate = " .. weaponcategory )
|
|
||||||
|
|
||||||
-- Tracking conditions for bombs, rockets and missiles.
|
|
||||||
local _bombs = weaponcategory == Weapon.Category.BOMB -- string.match(_weapon, "weapons.bombs")
|
|
||||||
local _rockets = weaponcategory == Weapon.Category.ROCKET -- string.match(_weapon, "weapons.nurs")
|
|
||||||
local _missiles = weaponcategory == Weapon.Category.MISSILE -- string.match(_weapon, "weapons.missiles") or _viggen
|
|
||||||
|
|
||||||
-- Check if any condition applies here.
|
|
||||||
local _track = (_bombs and self.trackbombs) or (_rockets and self.trackrockets) or (_missiles and self.trackmissiles)
|
|
||||||
|
|
||||||
-- Get unit name.
|
|
||||||
local _unitName = EventData.IniUnitName
|
|
||||||
|
|
||||||
-- Get player unit and name.
|
|
||||||
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
|
||||||
|
|
||||||
-- Attack parameters.
|
|
||||||
local attackHdg=_unit:GetHeading()
|
|
||||||
local attackAlt=_unit:GetHeight()
|
|
||||||
local attackVel=_unit:GetVelocityKNOTS()
|
|
||||||
|
|
||||||
-- Set this to larger value than the threshold.
|
|
||||||
local dPR = self.BombtrackThreshold * 2
|
|
||||||
|
|
||||||
-- Distance player to range.
|
|
||||||
if _unit and _playername then
|
|
||||||
dPR = _unit:GetCoordinate():Get2DDistance( self.location )
|
|
||||||
self:T( self.id .. string.format( "Range %s, player %s, player-range distance = %d km.", self.rangename, _playername, dPR / 1000 ) )
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Only track if distance player to range is < 25 km. Also check that a player shot. No need to track AI weapons.
|
|
||||||
if _track and dPR <= self.BombtrackThreshold and _unit and _playername then
|
|
||||||
|
|
||||||
-- Player data.
|
|
||||||
local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData
|
|
||||||
|
|
||||||
-- Tracking info and init of last bomb position.
|
|
||||||
self:T( self.id .. string.format( "RANGE %s: Tracking %s - %s.", self.rangename, _weapon, EventData.weapon:getName() ) )
|
|
||||||
|
|
||||||
-- Init bomb position.
|
|
||||||
local _lastBombPos = { x = 0, y = 0, z = 0 } -- DCS#Vec3
|
|
||||||
|
|
||||||
-- Function monitoring the position of a bomb until impact.
|
|
||||||
local function trackBomb( _ordnance )
|
|
||||||
|
|
||||||
-- When the pcall returns a failure the weapon has hit.
|
|
||||||
local _status, _bombPos = pcall( function()
|
|
||||||
return _ordnance:getPoint()
|
|
||||||
end )
|
|
||||||
|
|
||||||
self:T2( self.id .. string.format( "Range %s: Bomb still in air: %s", self.rangename, tostring( _status ) ) )
|
|
||||||
if _status then
|
|
||||||
|
|
||||||
----------------------------
|
|
||||||
-- Weapon is still in air --
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
-- Remember this position.
|
|
||||||
_lastBombPos = { x = _bombPos.x, y = _bombPos.y, z = _bombPos.z }
|
|
||||||
|
|
||||||
-- Check again in ~0.005 seconds ==> 200 checks per second.
|
|
||||||
return timer.getTime() + self.dtBombtrack
|
|
||||||
else
|
|
||||||
|
|
||||||
-----------------------------
|
|
||||||
-- Bomb did hit the ground --
|
|
||||||
-----------------------------
|
|
||||||
|
|
||||||
-- Get closet target to last position.
|
-- Get closet target to last position.
|
||||||
local _closetTarget = nil -- #RANGE.BombTarget
|
local _closetTarget = nil -- #RANGE.BombTarget
|
||||||
@@ -2011,18 +1884,18 @@ function RANGE:OnEventShot( EventData )
|
|||||||
local _hitquality = "POOR"
|
local _hitquality = "POOR"
|
||||||
|
|
||||||
-- Get callsign.
|
-- Get callsign.
|
||||||
local _callsign = self:_myname( _unitName )
|
local _callsign = self:_myname( playerData.unitname )
|
||||||
|
|
||||||
|
local _playername=playerData.playername
|
||||||
|
|
||||||
|
local _unit=playerData.unit
|
||||||
|
|
||||||
-- Coordinate of impact point.
|
-- Coordinate of impact point.
|
||||||
local impactcoord = COORDINATE:NewFromVec3( _lastBombPos )
|
local impactcoord = weapon:GetImpactCoordinate()
|
||||||
|
|
||||||
-- Check if impact happened in range zone.
|
-- Check if impact happened in range zone.
|
||||||
local insidezone = self.rangezone:IsCoordinateInZone( impactcoord )
|
local insidezone = self.rangezone:IsCoordinateInZone( impactcoord )
|
||||||
|
|
||||||
-- Impact point of bomb.
|
|
||||||
if self.Debug then
|
|
||||||
impactcoord:MarkToAll( "Bomb impact point" )
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Smoke impact point of bomb.
|
-- Smoke impact point of bomb.
|
||||||
if playerData.smokebombimpact and insidezone then
|
if playerData.smokebombimpact and insidezone then
|
||||||
@@ -2081,7 +1954,7 @@ function RANGE:OnEventShot( EventData )
|
|||||||
result.name = _closetTarget.name or "unknown"
|
result.name = _closetTarget.name or "unknown"
|
||||||
result.distance = _distance
|
result.distance = _distance
|
||||||
result.radial = _closeCoord:HeadingTo( impactcoord )
|
result.radial = _closeCoord:HeadingTo( impactcoord )
|
||||||
result.weapon = _weaponName or "unknown"
|
result.weapon = weapon:GetTypeName() or "unknown"
|
||||||
result.quality = _hitquality
|
result.quality = _hitquality
|
||||||
result.player = playerData.playername
|
result.player = playerData.playername
|
||||||
result.time = timer.getAbsTime()
|
result.time = timer.getAbsTime()
|
||||||
@@ -2096,6 +1969,7 @@ function RANGE:OnEventShot( EventData )
|
|||||||
result.attackHdg = attackHdg
|
result.attackHdg = attackHdg
|
||||||
result.attackVel = attackVel
|
result.attackVel = attackVel
|
||||||
result.attackAlt = attackAlt
|
result.attackAlt = attackAlt
|
||||||
|
result.date=os and os.date() or "n/a"
|
||||||
|
|
||||||
-- Add to table.
|
-- Add to table.
|
||||||
table.insert( _results, result )
|
table.insert( _results, result )
|
||||||
@@ -2124,22 +1998,65 @@ function RANGE:OnEventShot( EventData )
|
|||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
self:T( self.id .. "Weapon impacted outside range zone." )
|
self:T( self.lid .. "Weapon impacted outside range zone." )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Terminate the timer
|
end
|
||||||
self:T( self.id .. string.format( "Range %s, player %s: Terminating bomb track timer.", self.rangename, _playername ) )
|
|
||||||
return nil
|
|
||||||
|
|
||||||
end -- _status check
|
--- Range event handler for event shot (when a unit releases a rocket or bomb (but not a fast firing gun).
|
||||||
|
-- @param #RANGE self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
function RANGE:OnEventShot( EventData )
|
||||||
|
self:F( { eventshot = EventData } )
|
||||||
|
|
||||||
end -- end function trackBomb
|
-- Nil checks.
|
||||||
|
if EventData.Weapon == nil or EventData.IniDCSUnit == nil or EventData.IniPlayerName == nil then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
-- Weapon is not yet "alife" just yet. Start timer in one second.
|
-- Create weapon object.
|
||||||
self:T( self.id .. string.format( "Range %s, player %s: Tracking of weapon starts in 0.1 seconds.", self.rangename, _playername ) )
|
local weapon=WEAPON:New(EventData.weapon)
|
||||||
timer.scheduleFunction( trackBomb, EventData.weapon, timer.getTime() + 0.1 )
|
|
||||||
|
|
||||||
end -- if _track (string.match) and player-range distance < threshold.
|
-- Check if any condition applies here.
|
||||||
|
local _track = (weapon:IsBomb() and self.trackbombs) or (weapon:IsRocket() and self.trackrockets) or (weapon:IsMissile() and self.trackmissiles)
|
||||||
|
|
||||||
|
-- Get unit name.
|
||||||
|
local _unitName = EventData.IniUnitName
|
||||||
|
|
||||||
|
-- Get player unit and name.
|
||||||
|
local _unit, _playername = self:_GetPlayerUnitAndName( _unitName )
|
||||||
|
|
||||||
|
-- Distance Player-to-Range. Set this to larger value than the threshold.
|
||||||
|
local dPR = self.BombtrackThreshold * 2
|
||||||
|
|
||||||
|
-- Distance player to range.
|
||||||
|
if _unit and _playername then
|
||||||
|
dPR = _unit:GetCoordinate():Get2DDistance( self.location )
|
||||||
|
self:T( self.lid .. string.format( "Range %s, player %s, player-range distance = %d km.", self.rangename, _playername, dPR / 1000 ) )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Only track if distance player to range is < 25 km. Also check that a player shot. No need to track AI weapons.
|
||||||
|
if _track and dPR <= self.BombtrackThreshold and _unit and _playername then
|
||||||
|
|
||||||
|
-- Player data.
|
||||||
|
local playerData = self.PlayerSettings[_playername] -- #RANGE.PlayerData
|
||||||
|
|
||||||
|
-- Attack parameters.
|
||||||
|
local attackHdg=_unit:GetHeading()
|
||||||
|
local attackAlt=_unit:GetHeight()
|
||||||
|
local attackVel=_unit:GetVelocityKNOTS()
|
||||||
|
|
||||||
|
-- Tracking info and init of last bomb position.
|
||||||
|
self:T( self.lid .. string.format( "RANGE %s: Tracking %s - %s.", self.rangename, weapon:GetTypeName(), weapon:GetName()))
|
||||||
|
|
||||||
|
-- Set callback function on impact.
|
||||||
|
weapon:SetFuncImpact(RANGE._OnImpact, self, playerData, attackHdg, attackAlt, attackVel)
|
||||||
|
|
||||||
|
-- Weapon is not yet "alife" just yet. Start timer in 0.1 seconds.
|
||||||
|
self:T( self.lid .. string.format( "Range %s, player %s: Tracking of weapon starts in 0.1 seconds.", self.rangename, _playername ) )
|
||||||
|
weapon:StartTrack(0.1)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2183,7 +2100,7 @@ function RANGE:onafterStatus( From, Event, To )
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Check range status.
|
-- Check range status.
|
||||||
self:I( self.id .. text )
|
self:I( self.lid .. text )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2205,22 +2122,31 @@ function RANGE:onafterEnterRange( From, Event, To, player )
|
|||||||
if self.instructor and self.rangecontrol then
|
if self.instructor and self.rangecontrol then
|
||||||
|
|
||||||
if self.useSRS then
|
if self.useSRS then
|
||||||
|
|
||||||
|
|
||||||
local text = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f MHz", self.rangecontrolfreq)
|
local text = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f MHz", self.rangecontrolfreq)
|
||||||
local ttstext = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f mega hertz.", self.rangecontrolfreq)
|
local ttstext = string.format("You entered the bombing range. For hit assessment, contact the range controller at %.3f mega hertz.", self.rangecontrolfreq)
|
||||||
|
|
||||||
local group = player.client:GetGroup()
|
local group = player.client:GetGroup()
|
||||||
|
|
||||||
self.instructsrsQ:NewTransmission(ttstext, nil, self.instructmsrs, nil, 1, {group}, text, 10)
|
self.instructsrsQ:NewTransmission(ttstext, nil, self.instructmsrs, nil, 1, {group}, text, 10)
|
||||||
|
|
||||||
else
|
else
|
||||||
|
|
||||||
-- Range control radio frequency split.
|
-- Range control radio frequency split.
|
||||||
local RF = UTILS.Split( string.format( "%.3f", self.rangecontrolfreq ), "." )
|
local RF = UTILS.Split( string.format( "%.3f", self.rangecontrolfreq ), "." )
|
||||||
|
|
||||||
-- Radio message that player entered the range
|
-- Radio message that player entered the range
|
||||||
|
|
||||||
-- You entered the bombing range. For hit assessment, contact the range controller at xy MHz
|
-- You entered the bombing range. For hit assessment, contact the range controller at xy MHz
|
||||||
self.instructor:NewTransmission( RANGE.Sound.IREnterRange.filename, RANGE.Sound.IREnterRange.duration, self.soundpath )
|
self.instructor:NewTransmission( RANGE.Sound.IREnterRange.filename, RANGE.Sound.IREnterRange.duration, self.soundpath )
|
||||||
self.instructor:Number2Transmission( RF[1] )
|
self.instructor:Number2Transmission( RF[1] )
|
||||||
|
|
||||||
if tonumber( RF[2] ) > 0 then
|
if tonumber( RF[2] ) > 0 then
|
||||||
self.instructor:NewTransmission( RANGE.Sound.IRDecimal.filename, RANGE.Sound.IRDecimal.duration, self.soundpath )
|
self.instructor:NewTransmission( RANGE.Sound.IRDecimal.filename, RANGE.Sound.IRDecimal.duration, self.soundpath )
|
||||||
self.instructor:Number2Transmission( RF[2] )
|
self.instructor:Number2Transmission( RF[2] )
|
||||||
end
|
end
|
||||||
|
|
||||||
self.instructor:NewTransmission( RANGE.Sound.IRMegaHertz.filename, RANGE.Sound.IRMegaHertz.duration, self.soundpath )
|
self.instructor:NewTransmission( RANGE.Sound.IRMegaHertz.filename, RANGE.Sound.IRMegaHertz.duration, self.soundpath )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2238,9 +2164,24 @@ function RANGE:onafterExitRange( From, Event, To, player )
|
|||||||
if self.instructor then
|
if self.instructor then
|
||||||
-- You left the bombing range zone. Have a nice day!
|
-- You left the bombing range zone. Have a nice day!
|
||||||
if self.useSRS then
|
if self.useSRS then
|
||||||
local text = "You left the bombing range zone. Have a nice day!"
|
|
||||||
local group = player.client:GetGroup()
|
local text = "You left the bombing range zone. "
|
||||||
self.instructsrsQ:NewTransmission(text,nil,self.instructmsrs,nil,1,{group},text,10)
|
|
||||||
|
local r=math.random(2)
|
||||||
|
|
||||||
|
if r==1 then
|
||||||
|
text=text.."Have a nice day!"
|
||||||
|
elseif r==2 then
|
||||||
|
text=text.."Take care and bye bye!"
|
||||||
|
elseif r==3 then
|
||||||
|
text=text.."Talk to you soon!"
|
||||||
|
elseif r==4 then
|
||||||
|
text=text.."See you in two weeks!"
|
||||||
|
elseif r==5 then
|
||||||
|
text=text.."!"
|
||||||
|
end
|
||||||
|
|
||||||
|
self.instructsrsQ:NewTransmission(text, nil, self.instructmsrs, nil, 1, {player.client:GetGroup()}, text, 10)
|
||||||
else
|
else
|
||||||
self.instructor:NewTransmission( RANGE.Sound.IRExitRange.filename, RANGE.Sound.IRExitRange.duration, self.soundpath )
|
self.instructor:NewTransmission( RANGE.Sound.IRExitRange.filename, RANGE.Sound.IRExitRange.duration, self.soundpath )
|
||||||
end
|
end
|
||||||
@@ -2304,7 +2245,7 @@ function RANGE:onafterImpact( From, Event, To, result, player )
|
|||||||
|
|
||||||
-- Send message.
|
-- Send message.
|
||||||
self:_DisplayMessageToGroup( unit, text, nil, true )
|
self:_DisplayMessageToGroup( unit, text, nil, true )
|
||||||
self:T( self.id .. text )
|
self:T( self.lid .. text )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Save results.
|
-- Save results.
|
||||||
@@ -2343,7 +2284,7 @@ function RANGE:onbeforeSave( From, Event, To )
|
|||||||
if io and lfs then
|
if io and lfs then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
self:E( self.id .. string.format( "WARNING: io and/or lfs not desanitized. Cannot save player results." ) )
|
self:E( self.lid .. string.format( "WARNING: io and/or lfs not desanitized. Cannot save player results." ) )
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2360,9 +2301,9 @@ function RANGE:onafterSave( From, Event, To )
|
|||||||
if f then
|
if f then
|
||||||
f:write( data )
|
f:write( data )
|
||||||
f:close()
|
f:close()
|
||||||
self:I( self.id .. string.format( "Saving player results to file %s", tostring( filename ) ) )
|
self:I( self.lid .. string.format( "Saving player results to file %s", tostring( filename ) ) )
|
||||||
else
|
else
|
||||||
self:E( self.id .. string.format( "ERROR: Could not save results to file %s", tostring( filename ) ) )
|
self:E( self.lid .. string.format( "ERROR: Could not save results to file %s", tostring( filename ) ) )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -2388,10 +2329,7 @@ function RANGE:onafterSave( From, Event, To )
|
|||||||
local quality = result.quality
|
local quality = result.quality
|
||||||
local time = UTILS.SecondsToClock(result.time, true)
|
local time = UTILS.SecondsToClock(result.time, true)
|
||||||
local airframe = result.airframe
|
local airframe = result.airframe
|
||||||
local date = "n/a"
|
local date = result.date or "n/a"
|
||||||
if os then
|
|
||||||
date = os.date()
|
|
||||||
end
|
|
||||||
scores = scores .. string.format( "\n%s,%d,%s,%.2f,%03d,%s,%s,%s,%s,%s", playername, i, target, distance, radial, quality, weapon, airframe, time, date )
|
scores = scores .. string.format( "\n%s,%d,%s,%.2f,%03d,%s,%s,%s,%s,%s", playername, i, target, distance, radial, quality, weapon, airframe, time, date )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2408,7 +2346,7 @@ function RANGE:onbeforeLoad( From, Event, To )
|
|||||||
if io and lfs then
|
if io and lfs then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
self:E( self.id .. string.format( "WARNING: io and/or lfs not desanitized. Cannot load player results." ) )
|
self:E( self.lid .. string.format( "WARNING: io and/or lfs not desanitized. Cannot load player results." ) )
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2424,12 +2362,12 @@ function RANGE:onafterLoad( From, Event, To )
|
|||||||
local function _loadfile( filename )
|
local function _loadfile( filename )
|
||||||
local f = io.open( filename, "rb" )
|
local f = io.open( filename, "rb" )
|
||||||
if f then
|
if f then
|
||||||
-- self:I(self.id..string.format("Loading player results from file %s", tostring(filename)))
|
-- self:I(self.lid..string.format("Loading player results from file %s", tostring(filename)))
|
||||||
local data = f:read( "*all" )
|
local data = f:read( "*all" )
|
||||||
f:close()
|
f:close()
|
||||||
return data
|
return data
|
||||||
else
|
else
|
||||||
self:E( self.id .. string.format( "WARNING: Could not load player results from file %s. File might not exist just yet.", tostring( filename ) ) )
|
self:E( self.lid .. string.format( "WARNING: Could not load player results from file %s. File might not exist just yet.", tostring( filename ) ) )
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -2442,7 +2380,7 @@ function RANGE:onafterLoad( From, Event, To )
|
|||||||
|
|
||||||
-- Info message.
|
-- Info message.
|
||||||
local text = string.format( "Loading player bomb results from file %s", filename )
|
local text = string.format( "Loading player bomb results from file %s", filename )
|
||||||
self:I( self.id .. text )
|
self:I( self.lid .. text )
|
||||||
|
|
||||||
-- Load asset data from file.
|
-- Load asset data from file.
|
||||||
local data = _loadfile( filename )
|
local data = _loadfile( filename )
|
||||||
@@ -2909,7 +2847,7 @@ function RANGE:_DisplayRangeInfo( _unitname )
|
|||||||
self:_DisplayMessageToGroup( unit, text, nil, true, true, _multiplayer )
|
self:_DisplayMessageToGroup( unit, text, nil, true, true, _multiplayer )
|
||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
self:T2( self.id .. text )
|
self:T2( self.lid .. text )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -3056,9 +2994,9 @@ function RANGE:_DisplayRangeWeather( _unitname )
|
|||||||
self:_DisplayMessageToGroup( unit, text, nil, true, true, _multiplayer )
|
self:_DisplayMessageToGroup( unit, text, nil, true, true, _multiplayer )
|
||||||
|
|
||||||
-- Debug output.
|
-- Debug output.
|
||||||
self:T2( self.id .. text )
|
self:T2( self.lid .. text )
|
||||||
else
|
else
|
||||||
self:T( self.id .. string.format( "ERROR! Could not find player unit in RangeInfo! Name = %s", _unitname ) )
|
self:T( self.lid .. string.format( "ERROR! Could not find player unit in RangeInfo! Name = %s", _unitname ) )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -3453,10 +3391,10 @@ function RANGE:_AddF10Commands( _unitName )
|
|||||||
local _StrPits = MENU_GROUP_COMMAND:New( group, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName ):Refresh()
|
local _StrPits = MENU_GROUP_COMMAND:New( group, "Strafe Pits", _infoPath, self._DisplayStrafePits, self, _unitName ):Refresh()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:E( self.id .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName or "N/A")
|
self:E( self.lid .. "Could not find group or group ID in AddF10Menu() function. Unit name: " .. _unitName or "N/A")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
self:E( self.id .. "Player unit does not exist in AddF10Menu() function. Unit name: " .. _unitName or "N/A")
|
self:E( self.lid .. "Player unit does not exist in AddF10Menu() function. Unit name: " .. _unitName or "N/A")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -3475,14 +3413,15 @@ function RANGE:_GetBombTargetCoordinate( target )
|
|||||||
|
|
||||||
if target.type == RANGE.TargetType.UNIT then
|
if target.type == RANGE.TargetType.UNIT then
|
||||||
|
|
||||||
if not target.move then
|
-- Check if alive
|
||||||
-- Target should not move.
|
|
||||||
coord = target.coordinate
|
|
||||||
else
|
|
||||||
-- Moving target. Check if alive and get current position
|
|
||||||
if target.target and target.target:IsAlive() then
|
if target.target and target.target:IsAlive() then
|
||||||
|
-- Get current position.
|
||||||
coord = target.target:GetCoordinate()
|
coord = target.target:GetCoordinate()
|
||||||
end
|
-- Save as last known position in case target dies.
|
||||||
|
target.coordinate=coord
|
||||||
|
else
|
||||||
|
-- Use stored position.
|
||||||
|
coord = target.coordinate
|
||||||
end
|
end
|
||||||
|
|
||||||
elseif target.type == RANGE.TargetType.STATIC then
|
elseif target.type == RANGE.TargetType.STATIC then
|
||||||
@@ -3495,8 +3434,13 @@ function RANGE:_GetBombTargetCoordinate( target )
|
|||||||
-- Coordinates dont move.
|
-- Coordinates dont move.
|
||||||
coord = target.coordinate
|
coord = target.coordinate
|
||||||
|
|
||||||
|
elseif target.type == RANGE.TargetType.SCENERY then
|
||||||
|
|
||||||
|
-- Coordinates dont move.
|
||||||
|
coord = target.coordinate
|
||||||
|
|
||||||
else
|
else
|
||||||
self:E( self.id .. "ERROR: Unknown target type." )
|
self:E( self.lid .. "ERROR: Unknown target type." )
|
||||||
end
|
end
|
||||||
|
|
||||||
return coord
|
return coord
|
||||||
@@ -3524,7 +3468,7 @@ function RANGE:_GetAmmo( unitname )
|
|||||||
if ammotable ~= nil then
|
if ammotable ~= nil then
|
||||||
|
|
||||||
local weapons = #ammotable
|
local weapons = #ammotable
|
||||||
self:T2( self.id .. string.format( "Number of weapons %d.", weapons ) )
|
self:T2( self.lid .. string.format( "Number of weapons %d.", weapons ) )
|
||||||
|
|
||||||
for w = 1, weapons do
|
for w = 1, weapons do
|
||||||
|
|
||||||
@@ -3538,10 +3482,10 @@ function RANGE:_GetAmmo( unitname )
|
|||||||
ammo = ammo + Nammo
|
ammo = ammo + Nammo
|
||||||
|
|
||||||
local text = string.format( "Player %s has %d rounds ammo of type %s", playername, Nammo, Tammo )
|
local text = string.format( "Player %s has %d rounds ammo of type %s", playername, Nammo, Tammo )
|
||||||
self:T( self.id .. text )
|
self:T( self.lid .. text )
|
||||||
else
|
else
|
||||||
local text = string.format( "Player %s has %d ammo of type %s", playername, Nammo, Tammo )
|
local text = string.format( "Player %s has %d ammo of type %s", playername, Nammo, Tammo )
|
||||||
self:T( self.id .. text )
|
self:T( self.lid .. text )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4005,20 +3949,20 @@ function RANGE:_CheckStatic( name )
|
|||||||
|
|
||||||
-- If static is not yet in MOOSE DB, we add it. Can happen for cargo statics!
|
-- If static is not yet in MOOSE DB, we add it. Can happen for cargo statics!
|
||||||
if not _MOOSEstatic then
|
if not _MOOSEstatic then
|
||||||
self:T( self.id .. string.format( "Adding DCS static to MOOSE database. Name = %s.", name ) )
|
self:T( self.lid .. string.format( "Adding DCS static to MOOSE database. Name = %s.", name ) )
|
||||||
_DATABASE:AddStatic( name )
|
_DATABASE:AddStatic( name )
|
||||||
end
|
end
|
||||||
|
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
self:T3( self.id .. string.format( "No static object with name %s exists.", name ) )
|
self:T3( self.lid .. string.format( "No static object with name %s exists.", name ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if a unit has this name.
|
-- Check if a unit has this name.
|
||||||
if UNIT:FindByName( name ) then
|
if UNIT:FindByName( name ) then
|
||||||
return false
|
return false
|
||||||
else
|
else
|
||||||
self:T3( self.id .. string.format( "No unit object with name %s exists.", name ) )
|
self:T3( self.lid .. string.format( "No unit object with name %s exists.", name ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If not unit or static exist, we return nil.
|
-- If not unit or static exist, we return nil.
|
||||||
|
|||||||
@@ -873,8 +873,10 @@ end
|
|||||||
function SCORING:OnEventBirth( Event )
|
function SCORING:OnEventBirth( Event )
|
||||||
|
|
||||||
if Event.IniUnit then
|
if Event.IniUnit then
|
||||||
|
Event.IniUnit.ThreatLevel, Event.IniUnit.ThreatType = Event.IniUnit:GetThreatLevel()
|
||||||
if Event.IniObjectCategory == 1 then
|
if Event.IniObjectCategory == 1 then
|
||||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||||
|
Event.IniUnit.BirthTime = timer.getTime()
|
||||||
if PlayerName then
|
if PlayerName then
|
||||||
self:_AddPlayerFromUnit( Event.IniUnit )
|
self:_AddPlayerFromUnit( Event.IniUnit )
|
||||||
self:SetScoringMenu( Event.IniGroup )
|
self:SetScoringMenu( Event.IniGroup )
|
||||||
@@ -1005,7 +1007,18 @@ function SCORING:_EventOnHit( Event )
|
|||||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
|
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
|
||||||
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
||||||
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
||||||
|
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
|
||||||
|
if PlayerHit.UNIT.ThreatType == nil then
|
||||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||||
|
-- if this fails for some reason, set a good default value
|
||||||
|
if PlayerHit.ThreatType == nil then
|
||||||
|
PlayerHit.ThreatLevel = 1
|
||||||
|
PlayerHit.ThreatType = "Unknown"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
|
||||||
|
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
|
||||||
|
end
|
||||||
|
|
||||||
-- Only grant hit scores if there was more than one second between the last hit.
|
-- Only grant hit scores if there was more than one second between the last hit.
|
||||||
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
||||||
@@ -1021,27 +1034,30 @@ function SCORING:_EventOnHit( Event )
|
|||||||
|
|
||||||
if InitCoalition then -- A coalition object was hit.
|
if InitCoalition then -- A coalition object was hit.
|
||||||
if InitCoalition == TargetCoalition then
|
if InitCoalition == TargetCoalition then
|
||||||
Player.Penalty = Player.Penalty + 10
|
local Penalty = 10
|
||||||
PlayerHit.Penalty = PlayerHit.Penalty + 10
|
Player.Penalty = Player.Penalty + Penalty
|
||||||
|
PlayerHit.Penalty = PlayerHit.Penalty + Penalty
|
||||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
|
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
|
||||||
|
|
||||||
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
||||||
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
"Penalty: -" .. Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Update )
|
MESSAGE.Type.Update )
|
||||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||||
else
|
else
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
||||||
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
"Penalty: -" .. Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Update )
|
MESSAGE.Type.Update )
|
||||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||||
end
|
end
|
||||||
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
self:ScoreCSV( InitPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||||
else
|
else
|
||||||
Player.Score = Player.Score + 1
|
-- Hitting a target multiple times before destoying it should not result in a higger score
|
||||||
PlayerHit.Score = PlayerHit.Score + 1
|
-- Multiple hits is typically a results of bombs/missles missing their target but still inflict some spash damage
|
||||||
|
-- Player.Score = Player.Score + 1
|
||||||
|
-- PlayerHit.Score = PlayerHit.Score + 1
|
||||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||||
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
|
||||||
@@ -1104,7 +1120,18 @@ function SCORING:_EventOnHit( Event )
|
|||||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
|
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
|
||||||
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
|
||||||
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
|
||||||
|
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
|
||||||
|
if PlayerHit.UNIT.ThreatType == nil then
|
||||||
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
|
||||||
|
-- if this fails for some reason, set a good default value
|
||||||
|
if PlayerHit.ThreatType == nil then
|
||||||
|
PlayerHit.ThreatLevel = 1
|
||||||
|
PlayerHit.ThreatType = "Unknown"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
PlayerHit.ThreatLevel = PlayerHit.UNIT.ThreatLevel
|
||||||
|
PlayerHit.ThreatType = PlayerHit.UNIT.ThreatType
|
||||||
|
end
|
||||||
|
|
||||||
-- Only grant hit scores if there was more than one second between the last hit.
|
-- Only grant hit scores if there was more than one second between the last hit.
|
||||||
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
if timer.getTime() - PlayerHit.TimeStamp > 1 then
|
||||||
@@ -1115,25 +1142,28 @@ function SCORING:_EventOnHit( Event )
|
|||||||
if InitCoalition then -- A coalition object was hit, probably a static.
|
if InitCoalition then -- A coalition object was hit, probably a static.
|
||||||
if InitCoalition == TargetCoalition then
|
if InitCoalition == TargetCoalition then
|
||||||
-- TODO: Penalty according scale
|
-- TODO: Penalty according scale
|
||||||
Player.Penalty = Player.Penalty + 10 --* self.ScaleDestroyPenalty
|
local Penalty = 10
|
||||||
PlayerHit.Penalty = PlayerHit.Penalty + 10 --* self.ScaleDestroyPenalty
|
Player.Penalty = Player.Penalty + Penalty --* self.ScaleDestroyPenalty
|
||||||
|
PlayerHit.Penalty = PlayerHit.Penalty + Penalty --* self.ScaleDestroyPenalty
|
||||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty
|
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1 * self.ScaleDestroyPenalty
|
||||||
|
|
||||||
MESSAGE
|
MESSAGE
|
||||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
|
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
|
||||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||||
"Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty,
|
"Penalty: -" .. Penalty .. " = " .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Update
|
MESSAGE.Type.Update
|
||||||
)
|
)
|
||||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||||
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
self:ScoreCSV( Event.WeaponPlayerName, TargetPlayerName, "HIT_PENALTY", 1, -10, Event.WeaponName, Event.WeaponCoalition, Event.WeaponCategory, Event.WeaponTypeName, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||||
else
|
else
|
||||||
Player.Score = Player.Score + 1
|
-- Hitting a target multiple times before destoying it should not result in a higger score
|
||||||
PlayerHit.Score = PlayerHit.Score + 1
|
-- Multiple hits is typically a results of bombs/missles missing their target but still inflict some spash damage
|
||||||
|
-- Player.Score = Player.Score + 1
|
||||||
|
-- PlayerHit.Score = PlayerHit.Score + 1
|
||||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " .. TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||||
"Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty,
|
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Update )
|
MESSAGE.Type.Update )
|
||||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||||
@@ -1211,7 +1241,7 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
|||||||
local Destroyed = false
|
local Destroyed = false
|
||||||
|
|
||||||
-- What is the player destroying?
|
-- What is the player destroying?
|
||||||
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 then -- Was there a hit for this unit for this player before registered???
|
if Player and Player.Hit and Player.Hit[TargetCategory] and Player.Hit[TargetCategory][TargetUnitName] and Player.Hit[TargetCategory][TargetUnitName].TimeStamp ~= 0 and (TargetUnit.BirthTime == nil or Player.Hit[TargetCategory][TargetUnitName].TimeStamp > TargetUnit.BirthTime) then -- Was there a hit for this unit for this player before registered???
|
||||||
|
|
||||||
local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
|
local TargetThreatLevel = Player.Hit[TargetCategory][TargetUnitName].ThreatLevel
|
||||||
local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType
|
local TargetThreatType = Player.Hit[TargetCategory][TargetUnitName].ThreatType
|
||||||
@@ -1240,13 +1270,13 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
|||||||
|
|
||||||
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||||
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
|
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Information )
|
MESSAGE.Type.Information )
|
||||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||||
else
|
else
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||||
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
|
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Information )
|
MESSAGE.Type.Information )
|
||||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||||
@@ -1268,13 +1298,13 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
|||||||
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
|
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
|
||||||
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||||
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
|
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Information )
|
MESSAGE.Type.Information )
|
||||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||||
else
|
else
|
||||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||||
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
|
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
|
||||||
MESSAGE.Type.Information )
|
MESSAGE.Type.Information )
|
||||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||||
|
|||||||
@@ -715,7 +715,7 @@ do -- ZONE_CAPTURE_COALITION
|
|||||||
|
|
||||||
local UnitHit = EventData.TgtUnit
|
local UnitHit = EventData.TgtUnit
|
||||||
|
|
||||||
if UnitHit.ClassName ~= "SCENERY" then
|
if UnitHit and UnitHit.ClassName ~= "SCENERY" then
|
||||||
-- Check if unit is inside the capture zone and that it is of the defending coalition.
|
-- Check if unit is inside the capture zone and that it is of the defending coalition.
|
||||||
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
if UnitHit and UnitHit:IsInZone(self) and UnitHit:GetCoalition()==self.Coalition then
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ __Moose.Include( 'Scripts/Moose/Core/Goal.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Spot.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/MarkerOps_Base.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/MarkerOps_Base.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/TextAndSound.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/TextAndSound.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Core/Pathline.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Object.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Identifiable.lua' )
|
||||||
@@ -45,6 +46,8 @@ __Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Wrapper/Marker.lua' )
|
__Moose.Include( 'Scripts/Moose/Wrapper/Marker.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Wrapper/Weapon.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Wrapper/Net.lua' )
|
||||||
|
|
||||||
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
|
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )
|
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )
|
||||||
|
|||||||
@@ -10180,6 +10180,28 @@ function AIRBOSS:_GetSternCoord()
|
|||||||
return self.sterncoord
|
return self.sterncoord
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Get wire from draw argument.
|
||||||
|
-- @param #AIRBOSS self
|
||||||
|
-- @param Core.Point#COORDINATE Lcoord Landing position.
|
||||||
|
-- @return #number Trapped wire (1-4) or 99 if no wire was trapped.
|
||||||
|
function AIRBOSS:_GetWireFromDrawArg()
|
||||||
|
|
||||||
|
local wireArgs={}
|
||||||
|
wireArgs[1]=141
|
||||||
|
wireArgs[2]=142
|
||||||
|
wireArgs[3]=143
|
||||||
|
wireArgs[4]=144
|
||||||
|
|
||||||
|
for wire,drawArg in pairs(wireArgs) do
|
||||||
|
local value=self.carrier:GetDrawArgumentValue(drawArg)
|
||||||
|
if math.abs(value)>0.001 then
|
||||||
|
return wire
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return 99
|
||||||
|
end
|
||||||
|
|
||||||
--- Get wire from landing position.
|
--- Get wire from landing position.
|
||||||
-- @param #AIRBOSS self
|
-- @param #AIRBOSS self
|
||||||
-- @param Core.Point#COORDINATE Lcoord Landing position.
|
-- @param Core.Point#COORDINATE Lcoord Landing position.
|
||||||
|
|||||||
@@ -2750,12 +2750,12 @@ function CSAR:onafterLoad(From, Event, To, path, filename)
|
|||||||
vec3.z = tonumber(dataset[4])
|
vec3.z = tonumber(dataset[4])
|
||||||
local point = COORDINATE:NewFromVec3(vec3)
|
local point = COORDINATE:NewFromVec3(vec3)
|
||||||
|
|
||||||
local coalition = dataset[5]
|
local coalition = tonumber(dataset[5])
|
||||||
local country = dataset[6]
|
local country = tonumber(dataset[6])
|
||||||
local description = dataset[7]
|
local description = dataset[7]
|
||||||
local typeName = dataset[8]
|
local typeName = dataset[8]
|
||||||
local unitName = dataset[9]
|
local unitName = dataset[9]
|
||||||
local freq = dataset[10]
|
local freq = tonumber(dataset[10])
|
||||||
|
|
||||||
self:_AddCsar(coalition, country, point, typeName, unitName, playerName, freq, nil, description, nil)
|
self:_AddCsar(coalition, country, point, typeName, unitName, playerName, freq, nil, description, nil)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
-- @module Ops.CTLD
|
-- @module Ops.CTLD
|
||||||
-- @image OPS_CTLD.jpg
|
-- @image OPS_CTLD.jpg
|
||||||
|
|
||||||
-- Last Update Jan 2023
|
-- Last Update Mar 2023
|
||||||
|
|
||||||
do
|
do
|
||||||
|
|
||||||
@@ -289,6 +289,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
do
|
do
|
||||||
|
|
||||||
------------------------------------------------------
|
------------------------------------------------------
|
||||||
--- **CTLD_CARGO** class, extends Core.Base#BASE
|
--- **CTLD_CARGO** class, extends Core.Base#BASE
|
||||||
-- @type CTLD_CARGO
|
-- @type CTLD_CARGO
|
||||||
@@ -494,7 +495,7 @@ CTLD_CARGO = {
|
|||||||
|
|
||||||
--- Add Stock.
|
--- Add Stock.
|
||||||
-- @param #CTLD_CARGO self
|
-- @param #CTLD_CARGO self
|
||||||
-- @param #number Number to add, one if nil.
|
-- @param #number Number to add, none if nil.
|
||||||
-- @return #CTLD_CARGO self
|
-- @return #CTLD_CARGO self
|
||||||
function CTLD_CARGO:AddStock(Number)
|
function CTLD_CARGO:AddStock(Number)
|
||||||
if self.Stock then -- Stock nil?
|
if self.Stock then -- Stock nil?
|
||||||
@@ -506,7 +507,7 @@ CTLD_CARGO = {
|
|||||||
|
|
||||||
--- Remove Stock.
|
--- Remove Stock.
|
||||||
-- @param #CTLD_CARGO self
|
-- @param #CTLD_CARGO self
|
||||||
-- @param #number Number to reduce, one if nil.
|
-- @param #number Number to reduce, none if nil.
|
||||||
-- @return #CTLD_CARGO self
|
-- @return #CTLD_CARGO self
|
||||||
function CTLD_CARGO:RemoveStock(Number)
|
function CTLD_CARGO:RemoveStock(Number)
|
||||||
if self.Stock then -- Stock nil?
|
if self.Stock then -- Stock nil?
|
||||||
@@ -517,6 +518,15 @@ CTLD_CARGO = {
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Set Stock.
|
||||||
|
-- @param #CTLD_CARGO self
|
||||||
|
-- @param #number Number to set, nil means unlimited.
|
||||||
|
-- @return #CTLD_CARGO self
|
||||||
|
function CTLD_CARGO:SetStock(Number)
|
||||||
|
self.Stock = Number
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Query crate type for REPAIR
|
--- Query crate type for REPAIR
|
||||||
-- @param #CTLD_CARGO self
|
-- @param #CTLD_CARGO self
|
||||||
-- @param #boolean
|
-- @param #boolean
|
||||||
@@ -690,6 +700,7 @@ do
|
|||||||
-- my_ctld.useprefix = true -- (DO NOT SWITCH THIS OFF UNLESS YOU KNOW WHAT YOU ARE DOING!) Adjust **before** starting CTLD. If set to false, *all* choppers of the coalition side will be enabled for CTLD.
|
-- my_ctld.useprefix = true -- (DO NOT SWITCH THIS OFF UNLESS YOU KNOW WHAT YOU ARE DOING!) Adjust **before** starting CTLD. If set to false, *all* choppers of the coalition side will be enabled for CTLD.
|
||||||
-- my_ctld.CrateDistance = 35 -- List and Load crates in this radius only.
|
-- my_ctld.CrateDistance = 35 -- List and Load crates in this radius only.
|
||||||
-- my_ctld.dropcratesanywhere = false -- Option to allow crates to be dropped anywhere.
|
-- my_ctld.dropcratesanywhere = false -- Option to allow crates to be dropped anywhere.
|
||||||
|
-- my_ctld.dropAsCargoCrate = false -- Parachuted herc cargo is not unpacked automatically but placed as crate to be unpacked. Needs a cargo with the same name defined like the cargo that was dropped.
|
||||||
-- my_ctld.maximumHoverHeight = 15 -- Hover max this high to load.
|
-- my_ctld.maximumHoverHeight = 15 -- Hover max this high to load.
|
||||||
-- my_ctld.minimumHoverHeight = 4 -- Hover min this low to load.
|
-- my_ctld.minimumHoverHeight = 4 -- Hover min this low to load.
|
||||||
-- my_ctld.forcehoverload = true -- Crates (not: troops) can **only** be loaded while hovering.
|
-- my_ctld.forcehoverload = true -- Crates (not: troops) can **only** be loaded while hovering.
|
||||||
@@ -951,6 +962,18 @@ do
|
|||||||
--
|
--
|
||||||
-- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64}, -- 19t cargo, 64 paratroopers
|
-- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64}, -- 19t cargo, 64 paratroopers
|
||||||
--
|
--
|
||||||
|
-- ### 5.3 Don't automatically unpack dropped cargo but drop as CTLD_CARGO
|
||||||
|
--
|
||||||
|
-- Cargo can be defined to be automatically dropped as crates.
|
||||||
|
-- my_ctld.dropAsCargoCrate = true -- default is false
|
||||||
|
--
|
||||||
|
-- The idea is, to have those crate behave like brought in with a helo. So any unpack restictions apply.
|
||||||
|
-- To enable those cargo drops, the cargo types must be added manually in the CTLD configuration. So when the above defined template for "Vulcan" should be used
|
||||||
|
-- as CTLD_Cargo, the following line has to be added. NoCrates, PerCrateMass, Stock, SubCategory can be configured freely.
|
||||||
|
-- my_ctld:AddCratesCargo("Vulcan", {"Vulcan"}, CTLD_CARGO.Enum.VEHICLE, 6, 2000, nil, "SAM/AAA")
|
||||||
|
--
|
||||||
|
-- So if the Vulcan in the example now needs six crates to complete, you have to bring two Hercs with three Vulcan crates each and drop them very close together...
|
||||||
|
--
|
||||||
-- ## 6. Save and load back units - persistance
|
-- ## 6. Save and load back units - persistance
|
||||||
--
|
--
|
||||||
-- You can save and later load back units dropped or build to make your mission persistent.
|
-- You can save and later load back units dropped or build to make your mission persistent.
|
||||||
@@ -1196,7 +1219,7 @@ CTLD.UnitTypes = {
|
|||||||
|
|
||||||
--- CTLD class version.
|
--- CTLD class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
CTLD.version="1.0.29"
|
CTLD.version="1.0.32"
|
||||||
|
|
||||||
--- Instantiate a new CTLD.
|
--- Instantiate a new CTLD.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
@@ -1321,6 +1344,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
|||||||
self.forcehoverload = true
|
self.forcehoverload = true
|
||||||
self.hoverautoloading = true
|
self.hoverautoloading = true
|
||||||
self.dropcratesanywhere = false -- #1570
|
self.dropcratesanywhere = false -- #1570
|
||||||
|
self.dropAsCargoCrate = false -- Parachuted herc cargo is not unpacked automatically but placed as crate to be unpacked
|
||||||
|
|
||||||
self.smokedistance = 2000
|
self.smokedistance = 2000
|
||||||
self.movetroopstowpzone = true
|
self.movetroopstowpzone = true
|
||||||
@@ -1776,6 +1800,22 @@ function CTLD:_FindTroopsCargoObject(Name)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- (Internal) Find a crates CTLD_CARGO object in stock
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string Name of the object
|
||||||
|
-- @return #CTLD_CARGO Cargo object, nil if it cannot be found
|
||||||
|
function CTLD:_FindCratesCargoObject(Name)
|
||||||
|
self:T(self.lid .. " _FindCratesCargoObject")
|
||||||
|
local cargo = nil
|
||||||
|
for _,_cargo in pairs(self.Cargo_Crates)do
|
||||||
|
local cargo = _cargo -- #CTLD_CARGO
|
||||||
|
if cargo.Name == Name then
|
||||||
|
return cargo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
--- (User) Pre-load troops into a helo, e.g. for airstart. Unit **must** be alive in-game, i.e. player has taken the slot!
|
--- (User) Pre-load troops into a helo, e.g. for airstart. Unit **must** be alive in-game, i.e. player has taken the slot!
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object
|
-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object
|
||||||
@@ -1801,6 +1841,84 @@ function CTLD:PreloadTroops(Unit,Troopname)
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- (Internal) Pre-load crates into a helo. Do not use standalone!
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param Wrapper.Group#GROUP Group The group to load into, can be handed as Wrapper.Client#CLIENT object
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object
|
||||||
|
-- @param #CTLD_CARGO Cargo The Cargo crate object to load
|
||||||
|
-- @param #number NumberOfCrates (Optional) Number of crates to be loaded. Default - all necessary to build this object. Might overload the helo!
|
||||||
|
-- @return #CTLD self
|
||||||
|
function CTLD:_PreloadCrates(Group, Unit, Cargo, NumberOfCrates)
|
||||||
|
-- load crate into heli
|
||||||
|
local group = Group -- Wrapper.Group#GROUP
|
||||||
|
local unit = Unit -- Wrapper.Unit#UNIT
|
||||||
|
local unitname = unit:GetName()
|
||||||
|
-- see if this heli can load crates
|
||||||
|
local unittype = unit:GetTypeName()
|
||||||
|
local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitCapabilities
|
||||||
|
local cancrates = capabilities.crates -- #boolean
|
||||||
|
local cratelimit = capabilities.cratelimit -- #number
|
||||||
|
if not cancrates then
|
||||||
|
self:_SendMessage("Sorry this chopper cannot carry crates!", 10, false, Group)
|
||||||
|
return self
|
||||||
|
else
|
||||||
|
-- have we loaded stuff already?
|
||||||
|
local numberonboard = 0
|
||||||
|
local massonboard = 0
|
||||||
|
local loaded = {}
|
||||||
|
if self.Loaded_Cargo[unitname] then
|
||||||
|
loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo
|
||||||
|
numberonboard = loaded.Cratesloaded or 0
|
||||||
|
massonboard = self:_GetUnitCargoMass(Unit)
|
||||||
|
else
|
||||||
|
loaded = {} -- #CTLD.LoadedCargo
|
||||||
|
loaded.Troopsloaded = 0
|
||||||
|
loaded.Cratesloaded = 0
|
||||||
|
loaded.Cargo = {}
|
||||||
|
end
|
||||||
|
local crate = Cargo -- #CTLD_CARGO
|
||||||
|
local numbercrates = NumberOfCrates or crate:GetCratesNeeded()
|
||||||
|
for i=1,numbercrates do
|
||||||
|
loaded.Cratesloaded = loaded.Cratesloaded + 1
|
||||||
|
crate:SetHasMoved(true)
|
||||||
|
crate:SetWasDropped(false)
|
||||||
|
table.insert(loaded.Cargo, crate)
|
||||||
|
crate.Positionable = nil
|
||||||
|
self:_SendMessage(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()), 10, false, Group)
|
||||||
|
--self:__CratesPickedUp(1, Group, Unit, crate)
|
||||||
|
self.Loaded_Cargo[unitname] = loaded
|
||||||
|
self:_UpdateUnitCargoMass(Unit)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- (User) Pre-load crates into a helo, e.g. for airstart. Unit **must** be alive in-game, i.e. player has taken the slot!
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param Wrapper.Unit#UNIT Unit The unit to load into, can be handed as Wrapper.Client#CLIENT object
|
||||||
|
-- @param #string Cratesname The name of the cargo to be loaded. Must be created prior in the CTLD setup!
|
||||||
|
-- @param #number NumberOfCrates (Optional) Number of crates to be loaded. Default - all necessary to build this object. Might overload the helo!
|
||||||
|
-- @return #CTLD self
|
||||||
|
-- @usage
|
||||||
|
-- local client = UNIT:FindByName("Helo-1-1")
|
||||||
|
-- if client and client:IsAlive() then
|
||||||
|
-- myctld:PreloadCrates(client,"Humvee")
|
||||||
|
-- end
|
||||||
|
function CTLD:PreloadCrates(Unit,Cratesname,NumberOfCrates)
|
||||||
|
self:T(self.lid .. " PreloadCrates")
|
||||||
|
local name = Cratesname or "Unknown"
|
||||||
|
if Unit and Unit:IsAlive() then
|
||||||
|
local cargo = self:_FindCratesCargoObject(name)
|
||||||
|
local group = Unit:GetGroup()
|
||||||
|
if cargo then
|
||||||
|
self:_PreloadCrates(group,Unit,cargo,NumberOfCrates)
|
||||||
|
else
|
||||||
|
self:E(self.lid.." Crates preload - Cargo Object "..name.." not found!")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- (Internal) Function to load troops into a heli.
|
--- (Internal) Function to load troops into a heli.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
-- @param Wrapper.Group#GROUP Group
|
-- @param Wrapper.Group#GROUP Group
|
||||||
@@ -2199,7 +2317,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop)
|
|||||||
end
|
end
|
||||||
-- loop crates needed
|
-- loop crates needed
|
||||||
for i=1,number do
|
for i=1,number do
|
||||||
local cratealias = string.format("%s-%d", cratetemplate, math.random(1,100000))
|
local cratealias = string.format("%s-%s-%d", cratename, cratetemplate, math.random(1,100000))
|
||||||
if not self.placeCratesAhead then
|
if not self.placeCratesAhead then
|
||||||
cratedistance = (i-1)*2.5 + capabilities.length
|
cratedistance = (i-1)*2.5 + capabilities.length
|
||||||
if cratedistance > self.CrateDistance then cratedistance = self.CrateDistance end
|
if cratedistance > self.CrateDistance then cratedistance = self.CrateDistance end
|
||||||
@@ -2299,10 +2417,10 @@ function CTLD:InjectStatics(Zone, Cargo, RandomCoord)
|
|||||||
--local number = 1
|
--local number = 1
|
||||||
local cratesneeded = cargotype:GetCratesNeeded() --#number
|
local cratesneeded = cargotype:GetCratesNeeded() --#number
|
||||||
local cratetemplate = "Container"-- #string
|
local cratetemplate = "Container"-- #string
|
||||||
local cratealias = string.format("%s-%d", cratetemplate, math.random(1,100000))
|
|
||||||
local cratename = cargotype:GetName()
|
local cratename = cargotype:GetName()
|
||||||
local cgotype = cargotype:GetType()
|
local cgotype = cargotype:GetType()
|
||||||
local cgomass = cargotype:GetMass()
|
local cgomass = cargotype:GetMass()
|
||||||
|
local cratealias = string.format("%s-%s-%d", cratename, cratetemplate, math.random(1,100000))
|
||||||
local isstatic = false
|
local isstatic = false
|
||||||
if cgotype == CTLD_CARGO.Enum.STATIC then
|
if cgotype == CTLD_CARGO.Enum.STATIC then
|
||||||
cratetemplate = cargotype:GetTemplates()
|
cratetemplate = cargotype:GetTemplates()
|
||||||
@@ -2515,7 +2633,7 @@ function CTLD:_LoadCratesNearby(Group, Unit)
|
|||||||
crateind = _crate:GetID()
|
crateind = _crate:GetID()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if not _crate:HasMoved() and _crate:WasDropped() and _crate:GetID() > crateind then
|
if not _crate:HasMoved() and not _crate:WasDropped() and _crate:GetID() > crateind then
|
||||||
crateind = _crate:GetID()
|
crateind = _crate:GetID()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -4384,6 +4502,7 @@ end
|
|||||||
_troop:AddStock(number)
|
_troop:AddStock(number)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- User - function to add stock of a certain crates type
|
--- User - function to add stock of a certain crates type
|
||||||
@@ -4401,6 +4520,115 @@ end
|
|||||||
_troop:AddStock(number)
|
_troop:AddStock(number)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- User - function to add stock of a certain crates type
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string Name Name as defined in the generic cargo.
|
||||||
|
-- @param #number Number Number of units/groups to add.
|
||||||
|
-- @return #CTLD self
|
||||||
|
function CTLD:AddStockStatics(Name, Number)
|
||||||
|
local name = Name or "none"
|
||||||
|
local number = Number or 1
|
||||||
|
-- find right generic type
|
||||||
|
local gentroops = self.Cargo_Statics
|
||||||
|
for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO
|
||||||
|
if _troop.Name == name then
|
||||||
|
_troop:AddStock(number)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- User - function to set the stock of a certain crates type
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string Name Name as defined in the generic cargo.
|
||||||
|
-- @param #number Number Number of units/groups to be available. Nil equals unlimited
|
||||||
|
-- @return #CTLD self
|
||||||
|
function CTLD:SetStockCrates(Name, Number)
|
||||||
|
local name = Name or "none"
|
||||||
|
local number = Number
|
||||||
|
-- find right generic type
|
||||||
|
local gentroops = self.Cargo_Crates
|
||||||
|
for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO
|
||||||
|
if _troop.Name == name then
|
||||||
|
_troop:SetStock(number)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- User - function to set the stock of a certain troops type
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string Name Name as defined in the generic cargo.
|
||||||
|
-- @param #number Number Number of units/groups to be available. Nil equals unlimited
|
||||||
|
-- @return #CTLD self
|
||||||
|
function CTLD:SetStockTroops(Name, Number)
|
||||||
|
local name = Name or "none"
|
||||||
|
local number = Number
|
||||||
|
-- find right generic type
|
||||||
|
local gentroops = self.Cargo_Troops
|
||||||
|
for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO
|
||||||
|
if _troop.Name == name then
|
||||||
|
_troop:SetStock(number)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- User - function to set the stock of a certain statics type
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string Name Name as defined in the generic cargo.
|
||||||
|
-- @param #number Number Number of units/groups to be available. Nil equals unlimited
|
||||||
|
-- @return #CTLD self
|
||||||
|
function CTLD:SetStockStatics(Name, Number)
|
||||||
|
local name = Name or "none"
|
||||||
|
local number = Number
|
||||||
|
-- find right generic type
|
||||||
|
local gentroops = self.Cargo_Statics
|
||||||
|
for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO
|
||||||
|
if _troop.Name == name then
|
||||||
|
_troop:SetStock(number)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- User - function to get a table of crates in stock
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @return #table Table Table of Stock, indexed by cargo type name
|
||||||
|
function CTLD:GetStockCrates()
|
||||||
|
local Stock = {}
|
||||||
|
local gentroops = self.Cargo_Crates
|
||||||
|
for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO
|
||||||
|
table.insert(Stock,_troop.Name,_troop.Stock or -1)
|
||||||
|
end
|
||||||
|
return Stock
|
||||||
|
end
|
||||||
|
|
||||||
|
--- User - function to get a table of troops in stock
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @return #table Table Table of Stock, indexed by cargo type name
|
||||||
|
function CTLD:GetStockTroops()
|
||||||
|
local Stock = {}
|
||||||
|
local gentroops = self.Cargo_Troops
|
||||||
|
for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO
|
||||||
|
table.insert(Stock,_troop.Name,_troop.Stock or -1)
|
||||||
|
end
|
||||||
|
return Stock
|
||||||
|
end
|
||||||
|
|
||||||
|
--- User - function to get a table of statics cargo in stock
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @return #table Table Table of Stock, indexed by cargo type name
|
||||||
|
function CTLD:GetStockStatics()
|
||||||
|
local Stock = {}
|
||||||
|
local gentroops = self.Cargo_Statics
|
||||||
|
for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO
|
||||||
|
table.insert(Stock,_troop.Name,_troop.Stock or -1)
|
||||||
|
end
|
||||||
|
return Stock
|
||||||
end
|
end
|
||||||
|
|
||||||
--- User - function to remove stock of a certain troops type
|
--- User - function to remove stock of a certain troops type
|
||||||
@@ -4418,6 +4646,7 @@ end
|
|||||||
_troop:RemoveStock(number)
|
_troop:RemoveStock(number)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- User - function to remove stock of a certain crates type
|
--- User - function to remove stock of a certain crates type
|
||||||
@@ -4438,6 +4667,24 @@ end
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- User - function to remove stock of a certain statics type
|
||||||
|
-- @param #CTLD self
|
||||||
|
-- @param #string Name Name as defined in the generic cargo.
|
||||||
|
-- @param #number Number Number of units/groups to add.
|
||||||
|
-- @return #CTLD self
|
||||||
|
function CTLD:RemoveStockStatics(Name, Number)
|
||||||
|
local name = Name or "none"
|
||||||
|
local number = Number or 1
|
||||||
|
-- find right generic type
|
||||||
|
local gentroops = self.Cargo_Statics
|
||||||
|
for _id,_troop in pairs (gentroops) do -- #number, #CTLD_CARGO
|
||||||
|
if _troop.Name == name then
|
||||||
|
_troop:RemoveStock(number)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- (Internal) Check on engineering teams
|
--- (Internal) Check on engineering teams
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
-- @return #CTLD self
|
-- @return #CTLD self
|
||||||
@@ -5273,7 +5520,7 @@ CTLD_HERCULES = {
|
|||||||
ClassName = "CTLD_HERCULES",
|
ClassName = "CTLD_HERCULES",
|
||||||
lid = "",
|
lid = "",
|
||||||
Name = "",
|
Name = "",
|
||||||
Version = "0.0.2",
|
Version = "0.0.3",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Define cargo types.
|
--- Define cargo types.
|
||||||
@@ -5569,6 +5816,34 @@ function CTLD_HERCULES:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Drop_Positio
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [Internal] Function to spawn cargo by type at position
|
||||||
|
-- @param #CTLD_HERCULES self
|
||||||
|
-- @param #string Cargo_Type_name
|
||||||
|
-- @param Core.Point#POINT_VEC3 Cargo_Drop_Position
|
||||||
|
-- @return #CTLD_HERCULES self
|
||||||
|
function CTLD_HERCULES:Cargo_SpawnDroppedAsCargo(_name, _pos)
|
||||||
|
local theCargo = self.CTLD:_FindCratesCargoObject(_name)
|
||||||
|
if theCargo then
|
||||||
|
self.CTLD.CrateCounter = self.CTLD.CrateCounter + 1
|
||||||
|
self.CTLD.CargoCounter = self.CTLD.CargoCounter + 1
|
||||||
|
|
||||||
|
local basetype = self.CTLD.basetype or "container_cargo"
|
||||||
|
local theStatic = SPAWNSTATIC:NewFromType(basetype,"Cargos",self.cratecountry)
|
||||||
|
:InitCargoMass(theCargo.PerCrateMass)
|
||||||
|
:InitCargo(self.CTLD.enableslingload)
|
||||||
|
:InitCoordinate(_pos)
|
||||||
|
:Spawn(270,_name .. "-Container-".. math.random(1,100000))
|
||||||
|
|
||||||
|
self.CTLD.Spawned_Crates[self.CTLD.CrateCounter] = theStatic
|
||||||
|
local newCargo = CTLD_CARGO:New(self.CTLD.CargoCounter, theCargo.Name, theCargo.Templates, theCargo.CargoType, true, false, theCargo.CratesNeeded, self.CTLD.Spawned_Crates[self.CTLD.CrateCounter], true, theCargo.PerCrateMass, nil, theCargo.Subcategory)
|
||||||
|
table.insert(self.CTLD.Spawned_Cargo, newCargo)
|
||||||
|
|
||||||
|
newCargo:SetWasDropped(true)
|
||||||
|
newCargo:SetHasMoved(true)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- [Internal] Spawn cargo objects
|
--- [Internal] Spawn cargo objects
|
||||||
-- @param #CTLD_HERCULES self
|
-- @param #CTLD_HERCULES self
|
||||||
-- @param Wrapper.Group#GROUP Cargo_Drop_initiator
|
-- @param Wrapper.Group#GROUP Cargo_Drop_initiator
|
||||||
@@ -5591,8 +5866,8 @@ function CTLD_HERCULES:Cargo_SpawnObjects(Cargo_Drop_initiator,Cargo_Drop_Direct
|
|||||||
|
|
||||||
if offload_cargo == true or ParatrooperGroupSpawn == true then
|
if offload_cargo == true or ParatrooperGroupSpawn == true then
|
||||||
if ParatrooperGroupSpawn == true then
|
if ParatrooperGroupSpawn == true then
|
||||||
self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 0)
|
--self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 0)
|
||||||
self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 5)
|
--self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 5)
|
||||||
self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 10)
|
self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 10)
|
||||||
else
|
else
|
||||||
self:Cargo_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country)
|
self:Cargo_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country)
|
||||||
@@ -5617,10 +5892,14 @@ function CTLD_HERCULES:Cargo_SpawnObjects(Cargo_Drop_initiator,Cargo_Drop_Direct
|
|||||||
if Container_Enclosed == true then
|
if Container_Enclosed == true then
|
||||||
if ParatrooperGroupSpawn == true then
|
if ParatrooperGroupSpawn == true then
|
||||||
self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 0)
|
self:Soldier_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country, 0)
|
||||||
|
else
|
||||||
|
if self.CTLD.dropAsCargoCrate then
|
||||||
|
self:Cargo_SpawnDroppedAsCargo(Cargo_Type_name, Cargo_Content_position)
|
||||||
else
|
else
|
||||||
self:Cargo_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country)
|
self:Cargo_SpawnGroup(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, Cargo_Country)
|
||||||
self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position, "Hercules_Container_Parachute_Static", CargoHeading, false, Cargo_Country)
|
self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position, "Hercules_Container_Parachute_Static", CargoHeading, false, Cargo_Country)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, true, Cargo_Country)
|
self:Cargo_SpawnStatic(Cargo_Drop_initiator,Cargo_Content_position, Cargo_Type_name, CargoHeading, true, Cargo_Country)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -198,7 +198,7 @@
|
|||||||
-- The first parameter *callsignname* defines the name (1=Texaco, 2=Arco, 3=Shell). The second (optional) parameter specifies the first number and has to be between 1-9.
|
-- The first parameter *callsignname* defines the name (1=Texaco, 2=Arco, 3=Shell). The second (optional) parameter specifies the first number and has to be between 1-9.
|
||||||
-- Also see [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns) and [DCS_command_setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign).
|
-- Also see [DCS_enum_callsigns](https://wiki.hoggitworld.com/view/DCS_enum_callsigns) and [DCS_command_setCallsign](https://wiki.hoggitworld.com/view/DCS_command_setCallsign).
|
||||||
--
|
--
|
||||||
-- TexacoStennis:SetCAllsign(CALLSIGN.Tanker.Arco)
|
-- TexacoStennis:SetCallsign(CALLSIGN.Tanker.Arco)
|
||||||
--
|
--
|
||||||
-- For convenience, MOOSE has a CALLSIGN enumerator introduced.
|
-- For convenience, MOOSE has a CALLSIGN enumerator introduced.
|
||||||
--
|
--
|
||||||
|
|||||||
@@ -489,6 +489,7 @@ ENUMS.ReportingName =
|
|||||||
--Mosquito = "A-20",
|
--Mosquito = "A-20",
|
||||||
Skyhawk = "A-4E",
|
Skyhawk = "A-4E",
|
||||||
Viggen = "AJS37",
|
Viggen = "AJS37",
|
||||||
|
Harrier_B = "AV8BNA",
|
||||||
Harrier = "AV-8B",
|
Harrier = "AV-8B",
|
||||||
Spirit = "B-2",
|
Spirit = "B-2",
|
||||||
Aviojet = "C-101",
|
Aviojet = "C-101",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ SOCKET = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
--- Data type. This is the keyword the socket listener uses.
|
--- Data type. This is the keyword the socket listener uses.
|
||||||
|
-- @type SOCKET.DataType
|
||||||
-- @field #string TEXT Plain text.
|
-- @field #string TEXT Plain text.
|
||||||
-- @field #string BOMBRESULT Range bombing.
|
-- @field #string BOMBRESULT Range bombing.
|
||||||
-- @field #string STRAFERESULT Range strafeing result.
|
-- @field #string STRAFERESULT Range strafeing result.
|
||||||
|
|||||||
@@ -489,6 +489,31 @@ UTILS.hPa2inHg = function( hPa )
|
|||||||
return hPa * 0.0295299830714
|
return hPa * 0.0295299830714
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Convert indicated airspeed (IAS) to true airspeed (TAS) for a given altitude above main sea level.
|
||||||
|
-- The conversion is based on the approximation that TAS is ~2% higher than IAS with every 1000 ft altitude above sea level.
|
||||||
|
-- @param #number ias Indicated air speed in any unit (m/s, km/h, knots, ...)
|
||||||
|
-- @param #number altitude Altitude above main sea level in meters.
|
||||||
|
-- @param #number oatcorr (Optional) Outside air temperature correction factor. Default 0.017.
|
||||||
|
-- @return #number True airspeed in the same unit the IAS has been given.
|
||||||
|
UTILS.IasToTas = function( ias, altitude, oatcorr )
|
||||||
|
oatcorr=oatcorr or 0.017
|
||||||
|
local tas=ias + (ias * oatcorr * UTILS.MetersToFeet(altitude) / 1000)
|
||||||
|
return tas
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Convert true airspeed (TAS) to indicated airspeed (IAS) for a given altitude above main sea level.
|
||||||
|
-- The conversion is based on the approximation that TAS is ~2% higher than IAS with every 1000 ft altitude above sea level.
|
||||||
|
-- @param #number tas True air speed in any unit (m/s, km/h, knots, ...)
|
||||||
|
-- @param #number altitude Altitude above main sea level in meters.
|
||||||
|
-- @param #number oatcorr (Optional) Outside air temperature correction factor. Default 0.017.
|
||||||
|
-- @return #number Indicated airspeed in the same unit the TAS has been given.
|
||||||
|
UTILS.TasToIas = function( tas, altitude, oatcorr )
|
||||||
|
oatcorr=oatcorr or 0.017
|
||||||
|
local ias=tas/(1+oatcorr*UTILS.MetersToFeet(altitude)/1000)
|
||||||
|
return ias
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Convert knots to altitude corrected KIAS, e.g. for tankers.
|
--- Convert knots to altitude corrected KIAS, e.g. for tankers.
|
||||||
-- @param #number knots Speed in knots.
|
-- @param #number knots Speed in knots.
|
||||||
-- @param #number altitude Altitude in feet
|
-- @param #number altitude Altitude in feet
|
||||||
@@ -642,7 +667,10 @@ function UTILS.Round( num, idp )
|
|||||||
return math.floor( num * mult + 0.5 ) / mult
|
return math.floor( num * mult + 0.5 ) / mult
|
||||||
end
|
end
|
||||||
|
|
||||||
-- porting in Slmod's dostring
|
--- Porting in Slmod's dostring - execute a string as LUA code with error handling.
|
||||||
|
-- @param #string s The code as string to be executed
|
||||||
|
-- @return #boolean success If true, code was successfully executed, else false
|
||||||
|
-- @return #string Outcome Code outcome if successful or error string if not successful
|
||||||
function UTILS.DoString( s )
|
function UTILS.DoString( s )
|
||||||
local f, err = loadstring( s )
|
local f, err = loadstring( s )
|
||||||
if f then
|
if f then
|
||||||
@@ -652,7 +680,15 @@ function UTILS.DoString( s )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Here is a customized version of pairs, which I called spairs because it iterates over the table in a sorted order.
|
--- Here is a customized version of pairs, which I called spairs because it iterates over the table in a sorted order.
|
||||||
|
-- @param #table t The table
|
||||||
|
-- @param #string order (Optional) The sorting function
|
||||||
|
-- @return #string key The index key
|
||||||
|
-- @return #string value The value at the indexed key
|
||||||
|
-- @usage
|
||||||
|
-- for key,value in UTILS.spairs(mytable) do
|
||||||
|
-- -- your code here
|
||||||
|
-- end
|
||||||
function UTILS.spairs( t, order )
|
function UTILS.spairs( t, order )
|
||||||
-- collect the keys
|
-- collect the keys
|
||||||
local keys = {}
|
local keys = {}
|
||||||
@@ -677,7 +713,16 @@ function UTILS.spairs( t, order )
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
-- Here is a customized version of pairs, which I called kpairs because it iterates over the table in a sorted order, based on a function that will determine the keys as reference first.
|
--- Here is a customized version of pairs, which I called kpairs because it iterates over the table in a sorted order, based on a function that will determine the keys as reference first.
|
||||||
|
-- @param #table t The table
|
||||||
|
-- @param #string getkey The function to determine the keys for sorting
|
||||||
|
-- @param #string order (Optional) The sorting function itself
|
||||||
|
-- @return #string key The index key
|
||||||
|
-- @return #string value The value at the indexed key
|
||||||
|
-- @usage
|
||||||
|
-- for key,value in UTILS.kpairs(mytable, getkeyfunc) do
|
||||||
|
-- -- your code here
|
||||||
|
-- end
|
||||||
function UTILS.kpairs( t, getkey, order )
|
function UTILS.kpairs( t, getkey, order )
|
||||||
-- collect the keys
|
-- collect the keys
|
||||||
local keys = {}
|
local keys = {}
|
||||||
@@ -702,7 +747,14 @@ function UTILS.kpairs( t, getkey, order )
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Here is a customized version of pairs, which I called rpairs because it iterates over the table in a random order.
|
--- Here is a customized version of pairs, which I called rpairs because it iterates over the table in a random order.
|
||||||
|
-- @param #table t The table
|
||||||
|
-- @return #string key The index key
|
||||||
|
-- @return #string value The value at the indexed key
|
||||||
|
-- @usage
|
||||||
|
-- for key,value in UTILS.rpairs(mytable) do
|
||||||
|
-- -- your code here
|
||||||
|
-- end
|
||||||
function UTILS.rpairs( t )
|
function UTILS.rpairs( t )
|
||||||
-- collect the keys
|
-- collect the keys
|
||||||
|
|
||||||
@@ -1197,6 +1249,20 @@ function UTILS.HdgDiff(h1, h2)
|
|||||||
return math.abs(delta)
|
return math.abs(delta)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns the heading from one vec3 to another vec3.
|
||||||
|
-- @param DCS#Vec3 a From vec3.
|
||||||
|
-- @param DCS#Vec3 b To vec3.
|
||||||
|
-- @return #number Heading in degrees.
|
||||||
|
function UTILS.HdgTo(a, b)
|
||||||
|
local dz=b.z-a.z
|
||||||
|
local dx=b.x-a.x
|
||||||
|
local heading=math.deg(math.atan2(dz, dx))
|
||||||
|
if heading < 0 then
|
||||||
|
heading = 360 + heading
|
||||||
|
end
|
||||||
|
return heading
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
--- Translate 3D vector in the 2D (x,z) plane. y-component (usually altitude) unchanged.
|
||||||
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
-- @param DCS#Vec3 a Vector in 3D with x, y, z components.
|
||||||
@@ -2807,3 +2873,45 @@ function UTILS.IsAnyInTable(Table, Objects, Key)
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Helper function to plot a racetrack on the F10 Map - curtesy of Buur.
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate
|
||||||
|
-- @param #number Altitude Altitude in feet
|
||||||
|
-- @param #number Speed Speed in knots
|
||||||
|
-- @param #number Heading Heading in degrees
|
||||||
|
-- @param #number Leg Leg in NM
|
||||||
|
-- @param #number Coalition Coalition side, e.g. coaltion.side.RED or coaltion.side.BLUE
|
||||||
|
-- @param #table Color Color of the line in RGB, e.g. {1,0,0} for red
|
||||||
|
-- @param #number Alpha Transparency factor, between 0.1 and 1
|
||||||
|
-- @param #number LineType Line type to be used, line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid.
|
||||||
|
-- @param #boolean ReadOnly
|
||||||
|
function UTILS.PlotRacetrack(Coordinate, Altitude, Speed, Heading, Leg, Coalition, Color, Alpha, LineType, ReadOnly)
|
||||||
|
local fix_coordinate = Coordinate
|
||||||
|
local altitude = Altitude
|
||||||
|
local speed = Speed or 350
|
||||||
|
local heading = Heading or 270
|
||||||
|
local leg_distance = Leg or 10
|
||||||
|
|
||||||
|
local coalition = Coalition or -1
|
||||||
|
local color = Color or {1,0,0}
|
||||||
|
local alpha = Alpha or 1
|
||||||
|
local lineType = LineType or 1
|
||||||
|
|
||||||
|
|
||||||
|
speed = UTILS.IasToTas(speed, UTILS.FeetToMeters(altitude), oatcorr)
|
||||||
|
|
||||||
|
local turn_radius = 0.0211 * speed -3.01
|
||||||
|
|
||||||
|
local point_two = fix_coordinate:Translate(UTILS.NMToMeters(leg_distance), heading, true, false)
|
||||||
|
local point_three = point_two:Translate(UTILS.NMToMeters(turn_radius)*2, heading - 90, true, false)
|
||||||
|
local point_four = fix_coordinate:Translate(UTILS.NMToMeters(turn_radius)*2, heading - 90, true, false)
|
||||||
|
local circle_center_fix_four = point_two:Translate(UTILS.NMToMeters(turn_radius), heading - 90, true, false)
|
||||||
|
local circle_center_two_three = fix_coordinate:Translate(UTILS.NMToMeters(turn_radius), heading - 90, true, false)
|
||||||
|
|
||||||
|
|
||||||
|
fix_coordinate:LineToAll(point_two, coalition, color, alpha, lineType)
|
||||||
|
point_four:LineToAll(point_three, coalition, color, alpha, lineType)
|
||||||
|
circle_center_fix_four:CircleToAll(UTILS.NMToMeters(turn_radius), coalition, color, alpha, nil, 0, lineType)--, ReadOnly, Text)
|
||||||
|
circle_center_two_three:CircleToAll(UTILS.NMToMeters(turn_radius), coalition, color, alpha, nil, 0, lineType)--, ReadOnly, Text)
|
||||||
|
|
||||||
|
end
|
||||||
|
|||||||
@@ -540,8 +540,12 @@ AIRBASE.SouthAtlantic={
|
|||||||
["Rio_Chico"] = "Rio Chico",
|
["Rio_Chico"] = "Rio Chico",
|
||||||
["Franco_Bianco"] = "Franco Bianco",
|
["Franco_Bianco"] = "Franco Bianco",
|
||||||
["Goose_Green"] = "Goose Green",
|
["Goose_Green"] = "Goose Green",
|
||||||
["Hipico"] = "Hipico",
|
["Hipico_Flying_Club"] = "Hipico Flying Club",
|
||||||
["CaletaTortel"] = "CaletaTortel",
|
["CaletaTortel"] = "CaletaTortel",
|
||||||
|
["Aeropuerto_de_Gobernador_Gregores"] = "Aeropuerto de Gobernador Gregores",
|
||||||
|
["Aerodromo_O_Higgins"] = "Aerodromo O'Higgins",
|
||||||
|
["Cullen_Airport"] = "Cullen Airport",
|
||||||
|
["Gull_Point"] = "Gull Point",
|
||||||
}
|
}
|
||||||
|
|
||||||
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
--- AIRBASE.ParkingSpot ".Coordinate, ".TerminalID", ".TerminalType", ".TOAC", ".Free", ".TerminalID0", ".DistToRwy".
|
||||||
|
|||||||
@@ -95,6 +95,22 @@ function CLIENT:Find(DCSUnit, Error)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Finds a CLIENT from the _DATABASE using the relevant player name.
|
||||||
|
-- @param #CLIENT self
|
||||||
|
-- @param #string Name Name of the player
|
||||||
|
-- @return #CLIENT or nil if not found
|
||||||
|
function CLIENT:FindByPlayerName(Name)
|
||||||
|
|
||||||
|
local foundclient = nil
|
||||||
|
_DATABASE:ForEachClient(
|
||||||
|
function(client)
|
||||||
|
if client:GetPlayerName() == Name then
|
||||||
|
foundclient = client
|
||||||
|
end
|
||||||
|
end
|
||||||
|
)
|
||||||
|
return foundclient
|
||||||
|
end
|
||||||
|
|
||||||
--- Finds a CLIENT from the _DATABASE using the relevant Client Unit Name.
|
--- Finds a CLIENT from the _DATABASE using the relevant Client Unit Name.
|
||||||
-- As an optional parameter, a briefing text can be given also.
|
-- As an optional parameter, a briefing text can be given also.
|
||||||
|
|||||||
@@ -1653,7 +1653,7 @@ function GROUP:GetMinHeight()
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the current maximum height of the group.
|
--- Returns the current maximum height of the group, i.e. the highest unit height of that group.
|
||||||
-- Each unit within the group gets evaluated, and the maximum height (= the unit which is the highest elevated) is returned.
|
-- Each unit within the group gets evaluated, and the maximum height (= the unit which is the highest elevated) is returned.
|
||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @return #number Maximum height found.
|
-- @return #number Maximum height found.
|
||||||
@@ -1668,7 +1668,7 @@ function GROUP:GetMaxHeight()
|
|||||||
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
|
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
|
||||||
local UnitData = UnitData -- DCS#Unit
|
local UnitData = UnitData -- DCS#Unit
|
||||||
|
|
||||||
local UnitHeight = UnitData:getPoint()
|
local UnitHeight = UnitData:getPoint().p.y -- Height -- found by @Heavydrinker
|
||||||
|
|
||||||
if UnitHeight > GroupHeightMax then
|
if UnitHeight > GroupHeightMax then
|
||||||
GroupHeightMax = UnitHeight
|
GroupHeightMax = UnitHeight
|
||||||
@@ -2785,11 +2785,16 @@ end
|
|||||||
-- @param #GROUP self
|
-- @param #GROUP self
|
||||||
-- @param #boolean ShortCallsign Return a shortened customized callsign, i.e. "Ghostrider 9" and not "Ghostrider 9 1"
|
-- @param #boolean ShortCallsign Return a shortened customized callsign, i.e. "Ghostrider 9" and not "Ghostrider 9 1"
|
||||||
-- @param #boolean Keepnumber (Player only) Return customized callsign, incl optional numbers at the end, e.g. "Aerial 1-1#Ghostrider 109" results in "Ghostrider 109", if you want to e.g. use historical US Navy Callsigns
|
-- @param #boolean Keepnumber (Player only) Return customized callsign, incl optional numbers at the end, e.g. "Aerial 1-1#Ghostrider 109" results in "Ghostrider 109", if you want to e.g. use historical US Navy Callsigns
|
||||||
-- @param #table CallsignTranslations Table to translate between DCS standard callsigns and bespoke ones. Does not apply if using customized
|
-- @param #table CallsignTranslations Table to translate between DCS standard callsigns and bespoke ones. Overrides personal/parsed callsigns if set
|
||||||
-- callsigns from playername or group name.
|
-- callsigns from playername or group name.
|
||||||
-- @return #string Callsign
|
-- @return #string Callsign
|
||||||
-- @usage
|
-- @usage
|
||||||
-- -- Set Custom CAP Flight Callsigns for use with TTS
|
-- -- suppose there are three groups with one (client) unit each:
|
||||||
|
-- -- Slot 1 -- with mission editor callsign Enfield-1
|
||||||
|
-- -- Slot 2 # Apollo 403 -- with mission editor callsign Enfield-2
|
||||||
|
-- -- Slot 3 | Apollo -- with mission editor callsign Enfield-3
|
||||||
|
-- -- Slot 4 | Apollo -- with mission editor callsign Devil-4
|
||||||
|
-- -- and suppose these Custom CAP Flight Callsigns for use with TTS are set
|
||||||
-- mygroup:GetCustomCallSign(true,false,{
|
-- mygroup:GetCustomCallSign(true,false,{
|
||||||
-- Devil = 'Bengal',
|
-- Devil = 'Bengal',
|
||||||
-- Snake = 'Winder',
|
-- Snake = 'Winder',
|
||||||
@@ -2797,12 +2802,12 @@ end
|
|||||||
-- Enfield = 'Victory',
|
-- Enfield = 'Victory',
|
||||||
-- Uzi = 'Evil Eye'
|
-- Uzi = 'Evil Eye'
|
||||||
-- })
|
-- })
|
||||||
--
|
-- -- then GetCustomCallsign will return
|
||||||
-- results in this outcome if the group has Callsign "Enfield 9 1" on the 1st #UNIT of the group:
|
-- -- Enfield-1 for Slot 1
|
||||||
--
|
-- -- Apollo for Slot 2 or Apollo 403 if Keepnumber is set
|
||||||
-- 'Victory 9'
|
-- -- Apollo for Slot 3
|
||||||
--
|
-- -- Bengal-4 for Slot 4
|
||||||
--
|
|
||||||
function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
||||||
--self:I("GetCustomCallSign")
|
--self:I("GetCustomCallSign")
|
||||||
|
|
||||||
@@ -2810,14 +2815,18 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
|||||||
if self:IsAlive() then
|
if self:IsAlive() then
|
||||||
local IsPlayer = self:IsPlayer()
|
local IsPlayer = self:IsPlayer()
|
||||||
local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1
|
local shortcallsign = self:GetCallsign() or "unknown91" -- e.g.Uzi91, but we want Uzi 9 1
|
||||||
local callsignroot = string.match(shortcallsign, '(%a+)') -- Uzi
|
local callsignroot = string.match(shortcallsign, '(%a+)') or "Ghost" -- Uzi
|
||||||
--self:I("CallSign = " .. callsignroot)
|
--self:I("CallSign = " .. callsignroot)
|
||||||
local groupname = self:GetName()
|
local groupname = self:GetName()
|
||||||
local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91
|
local callnumber = string.match(shortcallsign, "(%d+)$" ) or "91" -- 91
|
||||||
local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9
|
local callnumbermajor = string.char(string.byte(callnumber,1)) -- 9
|
||||||
local callnumberminor = string.char(string.byte(callnumber,2)) -- 1
|
local callnumberminor = string.char(string.byte(callnumber,2)) -- 1
|
||||||
local personalized = false
|
local personalized = false
|
||||||
if IsPlayer and string.find(groupname,"#") then
|
|
||||||
|
-- prioritize bespoke callsigns over parsing, prefer parsing over default callsigns
|
||||||
|
if CallsignTranslations and CallsignTranslations[callsignroot] then
|
||||||
|
callsignroot = CallsignTranslations[callsignroot]
|
||||||
|
elseif IsPlayer and string.find(groupname,"#") then
|
||||||
-- personalized flight name in group naming
|
-- personalized flight name in group naming
|
||||||
if Keepnumber then
|
if Keepnumber then
|
||||||
shortcallsign = string.match(groupname,"#(.+)") or "Ghost 111" -- Ghostrider 219
|
shortcallsign = string.match(groupname,"#(.+)") or "Ghost 111" -- Ghostrider 219
|
||||||
@@ -2831,10 +2840,6 @@ function GROUP:GetCustomCallSign(ShortCallsign,Keepnumber,CallsignTranslations)
|
|||||||
personalized = true
|
personalized = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if (not personalized) and CallsignTranslations and CallsignTranslations[callsignroot] then
|
|
||||||
callsignroot = CallsignTranslations[callsignroot]
|
|
||||||
end
|
|
||||||
|
|
||||||
if personalized then
|
if personalized then
|
||||||
-- player personalized callsign
|
-- player personalized callsign
|
||||||
-- remove trailing/leading spaces
|
-- remove trailing/leading spaces
|
||||||
|
|||||||
833
Moose Development/Moose/Wrapper/Net.lua
Normal file
833
Moose Development/Moose/Wrapper/Net.lua
Normal file
@@ -0,0 +1,833 @@
|
|||||||
|
--- **Wrapper** - DCS net functions.
|
||||||
|
--
|
||||||
|
-- Encapsules **multiplayer server** environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net)
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **applevangelist**
|
||||||
|
-- # Last Update Feb 2023
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- @module Wrapper.Net
|
||||||
|
-- @image Utils_Profiler.jpg
|
||||||
|
|
||||||
|
do
|
||||||
|
--- The NET class
|
||||||
|
-- @type NET
|
||||||
|
-- @field #string ClassName
|
||||||
|
-- @field #string Version
|
||||||
|
-- @field #string lid
|
||||||
|
-- @field #number BlockTime
|
||||||
|
-- @field #table BlockedPilots
|
||||||
|
-- @field #table KnownPilots
|
||||||
|
-- @field #string BlockMessage
|
||||||
|
-- @field #string UnblockMessage
|
||||||
|
-- @field #table BlockedUCIDs
|
||||||
|
-- @field #table BlockedSlots
|
||||||
|
-- @field #table BlockedSides
|
||||||
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @type NET.PlayerData
|
||||||
|
-- @field #string name
|
||||||
|
-- @field #string ucid
|
||||||
|
-- @field #number id
|
||||||
|
-- @field #number side
|
||||||
|
-- @field #number slot
|
||||||
|
|
||||||
|
--- Encapsules multiplayer environment scripting functions from [net](https://wiki.hoggitworld.com/view/DCS_singleton_net)
|
||||||
|
-- with some added FSM functions and options to block/unblock players in MP environments.
|
||||||
|
--
|
||||||
|
-- @field #NET
|
||||||
|
NET = {
|
||||||
|
ClassName = "NET",
|
||||||
|
Version = "0.1.0",
|
||||||
|
BlockTime = 600,
|
||||||
|
BlockedPilots = {},
|
||||||
|
BlockedUCIDs = {},
|
||||||
|
BlockedSides = {},
|
||||||
|
BlockedSlots = {},
|
||||||
|
KnownPilots = {},
|
||||||
|
BlockMessage = nil,
|
||||||
|
UnblockMessage = nil,
|
||||||
|
lid = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- Instantiate a new NET object.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:New()
|
||||||
|
-- Inherit base.
|
||||||
|
local self = BASE:Inherit(self, FSM:New()) -- #NET
|
||||||
|
|
||||||
|
self.BlockTime = 600
|
||||||
|
self.BlockedPilots = {}
|
||||||
|
self.KnownPilots = {}
|
||||||
|
self:SetBlockMessage()
|
||||||
|
self:SetUnblockMessage()
|
||||||
|
|
||||||
|
-- Start State.
|
||||||
|
self:SetStartState("Stopped")
|
||||||
|
|
||||||
|
-- Add FSM transitions.
|
||||||
|
-- From State --> Event --> To State
|
||||||
|
self:AddTransition("Stopped", "Run", "Running") -- Start FSM.
|
||||||
|
self:AddTransition("*", "PlayerJoined", "*")
|
||||||
|
self:AddTransition("*", "PlayerLeft", "*")
|
||||||
|
self:AddTransition("*", "PlayerDied", "*")
|
||||||
|
self:AddTransition("*", "PlayerEjected", "*")
|
||||||
|
self:AddTransition("*", "PlayerBlocked", "*")
|
||||||
|
self:AddTransition("*", "PlayerUnblocked", "*")
|
||||||
|
self:AddTransition("*", "Status", "*")
|
||||||
|
self:AddTransition("*", "Stop", "Stopped")
|
||||||
|
|
||||||
|
self.lid = string.format("NET %s | ",self.Version)
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerJoined.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerJoined
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Unit#UNIT Client Unit Object.
|
||||||
|
-- @param #string Name Name of joining Pilot.
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerLeft.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerLeft
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Unit#UNIT Client Unit Object, might be nil.
|
||||||
|
-- @param #string Name Name of leaving Pilot.
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerEjected.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerEjected
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Unit#UNIT Client Unit Object, might be nil.
|
||||||
|
-- @param #string Name Name of leaving Pilot.
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerDied.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerDied
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Unit#UNIT Client Unit Object, might be nil.
|
||||||
|
-- @param #string Name Name of dead Pilot.
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerBlocked.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerBlocked
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Client#CLIENT Client Client Object, might be nil.
|
||||||
|
-- @param #string Name Name of blocked Pilot.
|
||||||
|
-- @param #number Seconds Blocked for this number of seconds
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
--- FSM Function OnAfterPlayerUnblocked.
|
||||||
|
-- @function [parent=#NET] OnAfterPlayerUnblocked
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From State.
|
||||||
|
-- @param #string Event Trigger.
|
||||||
|
-- @param #string To State.
|
||||||
|
-- @param Wrapper.Client#CLIENT Client Client Object, might be nil.
|
||||||
|
-- @param #string Name Name of unblocked Pilot.
|
||||||
|
-- @return #NET self
|
||||||
|
|
||||||
|
self:Run()
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Check any blockers
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string UCID
|
||||||
|
-- @param #string Name
|
||||||
|
-- @param #number PlayerID
|
||||||
|
-- @param #number PlayerSide
|
||||||
|
-- @param #string PlayerSlot
|
||||||
|
-- @return #boolean IsBlocked
|
||||||
|
function NET:IsAnyBlocked(UCID,Name,PlayerID,PlayerSide,PlayerSlot)
|
||||||
|
local blocked = false
|
||||||
|
local TNow = timer.getTime()
|
||||||
|
-- UCID
|
||||||
|
if UCID and self.BlockedUCIDs[UCID] and TNow < self.BlockedUCIDs[UCID] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
-- ID/Name
|
||||||
|
if PlayerID and not Name then
|
||||||
|
Name = self:GetPlayerIDByName(Name)
|
||||||
|
end
|
||||||
|
-- Name
|
||||||
|
if Name and self.BlockedPilots[Name] and TNow < self.BlockedPilots[Name] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
-- Side
|
||||||
|
if PlayerSide and self.BlockedSides[PlayerSide] and TNow < self.BlockedSides[PlayerSide] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
-- Slot
|
||||||
|
if PlayerSlot and self.BlockedSlots[PlayerSlot] and TNow < self.BlockedSlots[PlayerSlot] then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return blocked
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] Event Handler
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:_EventHandler(EventData)
|
||||||
|
self:T(self.lid .. " _EventHandler")
|
||||||
|
self:T2({Event = EventData.id})
|
||||||
|
local data = EventData -- Core.Event#EVENTDATA EventData
|
||||||
|
if data.id and data.IniUnit and (data.IniPlayerName or data.IniUnit:GetPlayerName()) then
|
||||||
|
|
||||||
|
-- Get Player Data
|
||||||
|
local name = data.IniPlayerName and data.IniPlayerName or data.IniUnit:GetPlayerName()
|
||||||
|
local ucid = self:GetPlayerUCID(nil,name)
|
||||||
|
local PlayerID = self:GetPlayerIDByName(name) or "none"
|
||||||
|
local PlayerSide, PlayerSlot = self:GetSlot(data.IniUnit)
|
||||||
|
local TNow = timer.getTime()
|
||||||
|
|
||||||
|
self:T(self.lid.."Event for: "..name.." | UCID: "..ucid)
|
||||||
|
|
||||||
|
-- Joining
|
||||||
|
if data.id == EVENTS.PlayerEnterUnit or data.id == EVENTS.PlayerEnterAircraft then
|
||||||
|
self:T(self.lid.."Pilot Joining: "..name.." | UCID: "..ucid)
|
||||||
|
-- Check for blockages
|
||||||
|
local blocked = self:IsAnyBlocked(ucid,name,PlayerID,PlayerSide,PlayerSlot)
|
||||||
|
|
||||||
|
if blocked and PlayerID and tonumber(PlayerID) ~= 1 then
|
||||||
|
-- block pilot
|
||||||
|
local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' )
|
||||||
|
else
|
||||||
|
self.KnownPilots[name] = {
|
||||||
|
name = name,
|
||||||
|
ucid = ucid,
|
||||||
|
id = PlayerID,
|
||||||
|
side = PlayerSide,
|
||||||
|
slot = PlayerSlot,
|
||||||
|
}
|
||||||
|
self:__PlayerJoined(1,data.IniUnit,name)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Leaving
|
||||||
|
if data.id == EVENTS.PlayerLeaveUnit and self.KnownPilots[name] then
|
||||||
|
self:T(self.lid.."Pilot Leaving: "..name.." | UCID: "..ucid)
|
||||||
|
self:__PlayerLeft(1,data.IniUnit,name)
|
||||||
|
self.KnownPilots[name] = false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Ejected
|
||||||
|
if data.id == EVENTS.Ejection and self.KnownPilots[name] then
|
||||||
|
self:T(self.lid.."Pilot Ejecting: "..name.." | UCID: "..ucid)
|
||||||
|
self:__PlayerEjected(1,data.IniUnit,name)
|
||||||
|
self.KnownPilots[name] = false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Dead, Crash, Suicide
|
||||||
|
if (data.id == EVENTS.PilotDead or data.id == EVENTS.SelfKillPilot or data.id == EVENTS.Crash) and self.KnownPilots[name] then
|
||||||
|
self:T(self.lid.."Pilot Dead: "..name.." | UCID: "..ucid)
|
||||||
|
self:__PlayerDied(1,data.IniUnit,name)
|
||||||
|
self.KnownPilots[name] = false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Block a player.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client CLIENT object.
|
||||||
|
-- @param #string PlayerName (optional) Name of the player.
|
||||||
|
-- @param #number Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @param #string Message (optional) Message to be sent via chat.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:BlockPlayer(Client,PlayerName,Seconds,Message)
|
||||||
|
self:T({PlayerName,Seconds,Message})
|
||||||
|
local name = PlayerName
|
||||||
|
if Client and (not PlayerName) then
|
||||||
|
name = Client:GetPlayerName()
|
||||||
|
elseif PlayerName then
|
||||||
|
name = PlayerName
|
||||||
|
else
|
||||||
|
self:F(self.lid.."Block: No Client or PlayerName given or nothing found!")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
local ucid = self:GetPlayerUCID(Client,name)
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
self.BlockedPilots[name] = timer.getTime()+addon
|
||||||
|
self.BlockedUCIDs[ucid] = timer.getTime()+addon
|
||||||
|
local message = Message or self.BlockMessage
|
||||||
|
if name then
|
||||||
|
self:SendChatToPlayer(message,name)
|
||||||
|
else
|
||||||
|
self:SendChat(name..": "..message)
|
||||||
|
end
|
||||||
|
self:__PlayerBlocked(1,Client,name,Seconds)
|
||||||
|
local PlayerID = self:GetPlayerIDByName(name)
|
||||||
|
if PlayerID and tonumber(PlayerID) ~= 1 then
|
||||||
|
local outcome = net.force_player_slot(tonumber(PlayerID), 0, '' )
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Block a SET_CLIENT of players
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Core.Set#SET_CLIENT PlayerSet The SET to block.
|
||||||
|
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @param #string Message (optional) Message to be sent via chat.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:BlockPlayerSet(PlayerSet,Seconds,Message)
|
||||||
|
self:T({PlayerSet.Set,Seconds,Message})
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
local message = Message or self.BlockMessage
|
||||||
|
for _,_client in pairs(PlayerSet.Set) do
|
||||||
|
local name = _client:GetPlayerName()
|
||||||
|
self:BlockPlayer(_client,name,addon,message)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unblock a SET_CLIENT of players
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Core.Set#SET_CLIENT PlayerSet The SET to unblock.
|
||||||
|
-- @param #string Message (optional) Message to be sent via chat.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:UnblockPlayerSet(PlayerSet,Message)
|
||||||
|
self:T({PlayerSet.Set,Seconds,Message})
|
||||||
|
local message = Message or self.UnblockMessage
|
||||||
|
for _,_client in pairs(PlayerSet.Set) do
|
||||||
|
local name = _client:GetPlayerName()
|
||||||
|
self:UnblockPlayer(_client,name,message)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Block a specific UCID of a player, does NOT automatically kick the player with the UCID if already joined.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string ucid
|
||||||
|
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:BlockUCID(ucid,Seconds)
|
||||||
|
self:T({ucid,Seconds})
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
self.BlockedUCIDs[ucid] = timer.getTime()+addon
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unblock a specific UCID of a player
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string ucid
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:UnblockUCID(ucid)
|
||||||
|
self:T({ucid})
|
||||||
|
self.BlockedUCIDs[ucid] = nil
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Block a specific coalition side, does NOT automatically kick all players of that side or kick out joined players
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #number side The side to block - 1 : Red, 2 : Blue
|
||||||
|
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:BlockSide(Side,Seconds)
|
||||||
|
self:T({Side,Seconds})
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
if Side == 1 or Side == 2 then
|
||||||
|
self.BlockedSides[Side] = timer.getTime()+addon
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unblock a specific coalition side. Does NOT unblock specifically blocked playernames or UCIDs.
|
||||||
|
-- @param #number side The side to block - 1 : Red, 2 : Blue
|
||||||
|
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:UnblockSide(Side,Seconds)
|
||||||
|
self:T({Side,Seconds})
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
if Side == 1 or Side == 2 then
|
||||||
|
self.BlockedSides[Side] = nil
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Block a specific player slot, does NOT automatically kick a player in that slot or kick out joined players
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string slot The slot to block
|
||||||
|
-- @param #number Seconds Seconds (optional) Number of seconds the player has to wait before rejoining.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:BlockSlot(Slot,Seconds)
|
||||||
|
self:T({Slot,Seconds})
|
||||||
|
local addon = Seconds or self.BlockTime
|
||||||
|
self.BlockedSlots[Slot] = timer.getTime()+addon
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unblock a specific slot.
|
||||||
|
-- @param #string slot The slot to block
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:UnblockSlot(Slot)
|
||||||
|
self:T({Slot})
|
||||||
|
self.BlockedSlots[Slot] = nil
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Unblock a player.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client CLIENT object
|
||||||
|
-- @param #string PlayerName (optional) Name of the player.
|
||||||
|
-- @param #string Message (optional) Message to be sent via chat.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:UnblockPlayer(Client,PlayerName,Message)
|
||||||
|
local name = PlayerName
|
||||||
|
if Client then
|
||||||
|
name = Client:GetPlayerName()
|
||||||
|
elseif PlayerName then
|
||||||
|
name = PlayerName
|
||||||
|
else
|
||||||
|
self:F(self.lid.."Unblock: No PlayerName given or not found!")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
local ucid = self:GetPlayerUCID(Client,name)
|
||||||
|
self.BlockedPilots[name] = nil
|
||||||
|
self.BlockedUCIDs[ucid] = nil
|
||||||
|
local message = Message or self.UnblockMessage
|
||||||
|
if name then
|
||||||
|
self:SendChatToPlayer(message,name)
|
||||||
|
else
|
||||||
|
self:SendChat(name..": "..message)
|
||||||
|
end
|
||||||
|
self:__PlayerUnblocked(1,Client,name)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set block chat message.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Text The message
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SetBlockMessage(Text)
|
||||||
|
self.BlockMessage = Text or "You are blocked from joining. Wait time is: "..self.BlockTime.." seconds!"
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set block time in seconds.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #number Seconds Numnber of seconds this block will last. Defaults to 600.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SetBlockTime(Seconds)
|
||||||
|
self.BlockTime = Seconds or 600
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set unblock chat message.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Text The message
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SetUnblockMessage(Text)
|
||||||
|
self.UnblockMessage = Text or "You are unblocked now and can join again."
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Send chat message.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Message Message to send
|
||||||
|
-- @param #boolean ToAll (Optional)
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SendChat(Message,ToAll)
|
||||||
|
if Message then
|
||||||
|
net.send_chat(Message, ToAll)
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find the PlayerID by name
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Name The player name whose ID to find
|
||||||
|
-- @return #number PlayerID or nil
|
||||||
|
function NET:GetPlayerIDByName(Name)
|
||||||
|
if not Name then return nil end
|
||||||
|
local playerList = self:GetPlayerList()
|
||||||
|
self:T({playerList})
|
||||||
|
for i=1,#playerList do
|
||||||
|
local playerName = net.get_name(i)
|
||||||
|
self:T({playerName})
|
||||||
|
if playerName == Name then
|
||||||
|
return playerList[i]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find the PlayerID from a CLIENT object.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @return #number PlayerID or nil
|
||||||
|
function NET:GetPlayerIDFromClient(Client)
|
||||||
|
if Client then
|
||||||
|
local name = Client:GetPlayerName()
|
||||||
|
local id = self:GetPlayerIDByName(name)
|
||||||
|
return id
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Send chat message to a specific player using the CLIENT object.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Message The text message
|
||||||
|
-- @param Wrapper.Client#CLIENT ToClient Client receiving the message
|
||||||
|
-- @param Wrapper.Client#CLIENT FromClient (Optional) Client sending the message
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SendChatToClient(Message, ToClient, FromClient)
|
||||||
|
local PlayerId = self:GetPlayerIDFromClient(ToClient)
|
||||||
|
local FromId = self:GetPlayerIDFromClient(FromClient)
|
||||||
|
if Message and PlayerId and FromId then
|
||||||
|
net.send_chat_to(Message, tonumber(PlayerId) , tonumber(FromId))
|
||||||
|
elseif Message and PlayerId then
|
||||||
|
net.send_chat_to(Message, tonumber(PlayerId))
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Send chat message to a specific player using the player name
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Message The text message
|
||||||
|
-- @param #string ToPlayer Player receiving the message
|
||||||
|
-- @param #string FromPlayer(Optional) Player sending the message
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:SendChatToPlayer(Message, ToPlayer, FromPlayer)
|
||||||
|
local PlayerId = self:GetPlayerIDByName(ToPlayer)
|
||||||
|
local FromId = self:GetPlayerIDByName(FromPlayer)
|
||||||
|
if Message and PlayerId and FromId then
|
||||||
|
net.send_chat_to(Message, tonumber(PlayerId) , tonumber(FromId))
|
||||||
|
elseif Message and PlayerId then
|
||||||
|
net.send_chat_to(Message, tonumber(PlayerId))
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load a specific mission.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Path and Mission
|
||||||
|
-- @return #boolean success
|
||||||
|
-- @usage
|
||||||
|
-- mynet:LoadMission(lfs.writeDir() .. 'Missions\\' .. 'MyTotallyAwesomeMission.miz')
|
||||||
|
function NET:LoadMission(Path)
|
||||||
|
local outcome = false
|
||||||
|
if Path then
|
||||||
|
outcome = net.load_mission(Path)
|
||||||
|
end
|
||||||
|
return outcome
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Load next mission. Returns false if at the end of list.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @return #boolean success
|
||||||
|
function NET:LoadNextMission()
|
||||||
|
local outcome = false
|
||||||
|
outcome = net.load_next_mission()
|
||||||
|
return outcome
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a table of players currently connected to the server.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @return #table PlayerList
|
||||||
|
function NET:GetPlayerList()
|
||||||
|
local plist = nil
|
||||||
|
plist = net.get_player_list()
|
||||||
|
return plist
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the playerID of the local player. Always returns 1 for server.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @return #number ID
|
||||||
|
function NET:GetMyPlayerID()
|
||||||
|
return net.get_my_player_id()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the playerID of the server. Currently always returns 1.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @return #number ID
|
||||||
|
function NET:GetServerID()
|
||||||
|
return net.get_server_id()
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a table of attributes for a given client. If optional attribute is present, only that value is returned.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client.
|
||||||
|
-- @param #string Attribute (Optional) The attribute to obtain. List see below.
|
||||||
|
-- @return #table PlayerInfo or nil if it cannot be found
|
||||||
|
-- @usage
|
||||||
|
-- Table holds these attributes:
|
||||||
|
--
|
||||||
|
-- 'id' : playerID
|
||||||
|
-- 'name' : player name
|
||||||
|
-- 'side' : 0 - spectators, 1 - red, 2 - blue
|
||||||
|
-- 'slot' : slotID of the player or
|
||||||
|
-- 'ping' : ping of the player in ms
|
||||||
|
-- 'ipaddr': IP address of the player, SERVER ONLY
|
||||||
|
-- 'ucid' : Unique Client Identifier, SERVER ONLY
|
||||||
|
--
|
||||||
|
function NET:GetPlayerInfo(Client,Attribute)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
if PlayerID then
|
||||||
|
return net.get_player_info(tonumber(PlayerID), Attribute)
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get player UCID from player CLIENT object or player name. Provide either one.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client object to be used.
|
||||||
|
-- @param #string Name Player name to be used.
|
||||||
|
-- @return #boolean success
|
||||||
|
function NET:GetPlayerUCID(Client,Name)
|
||||||
|
local PlayerID = nil
|
||||||
|
if Client then
|
||||||
|
PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
elseif Name then
|
||||||
|
PlayerID = self:GetPlayerIDByName(Name)
|
||||||
|
else
|
||||||
|
self:E(self.lid.."Neither client nor name provided!")
|
||||||
|
end
|
||||||
|
local ucid = net.get_player_info(tonumber(PlayerID), 'ucid')
|
||||||
|
return ucid
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Kicks a player from the server. Can display a message to the user.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @param #string Message (Optional) The message to send.
|
||||||
|
-- @return #boolean success
|
||||||
|
function NET:Kick(Client,Message)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
if PlayerID and tonumber(PlayerID) ~= 1 then
|
||||||
|
return net.kick(tonumber(PlayerID), Message)
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return a statistic for a given client.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @param #number StatisticID The statistic to obtain
|
||||||
|
-- @return #number Statistic or nil
|
||||||
|
-- @usage
|
||||||
|
-- StatisticIDs are:
|
||||||
|
--
|
||||||
|
-- net.PS_PING (0) - ping (in ms)
|
||||||
|
-- net.PS_CRASH (1) - number of crashes
|
||||||
|
-- net.PS_CAR (2) - number of destroyed vehicles
|
||||||
|
-- net.PS_PLANE (3) - ... planes/helicopters
|
||||||
|
-- net.PS_SHIP (4) - ... ships
|
||||||
|
-- net.PS_SCORE (5) - total score
|
||||||
|
-- net.PS_LAND (6) - number of landings
|
||||||
|
-- net.PS_EJECT (7) - of ejects
|
||||||
|
--
|
||||||
|
-- mynet:GetPlayerStatistic(Client,7) -- return number of ejects
|
||||||
|
function NET:GetPlayerStatistic(Client,StatisticID)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
local stats = StatisticID or 0
|
||||||
|
if stats > 7 or stats < 0 then stats = 0 end
|
||||||
|
if PlayerID then
|
||||||
|
return net.get_stat(tonumber(PlayerID),stats)
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Return the name of a given client. Effectively the same as CLIENT:GetPlayerName().
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @return #string Name or nil if not obtainable
|
||||||
|
function NET:GetName(Client)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
if PlayerID then
|
||||||
|
return net.get_name(tonumber(PlayerID))
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the SideId and SlotId of a given client.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @return #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue
|
||||||
|
-- @return #number SlotID
|
||||||
|
function NET:GetSlot(Client)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
if PlayerID then
|
||||||
|
local side,slot = net.get_slot(tonumber(PlayerID))
|
||||||
|
return side,slot
|
||||||
|
else
|
||||||
|
return nil,nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Force the slot for a specific client.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @param #number SideID i.e. 0 : spectators, 1 : Red, 2 : Blue
|
||||||
|
-- @param #number SlotID Slot number
|
||||||
|
-- @return #boolean Success
|
||||||
|
function NET:ForceSlot(Client,SideID,SlotID)
|
||||||
|
local PlayerID = self:GetPlayerIDFromClient(Client)
|
||||||
|
if PlayerID and tonumber(PlayerID) ~= 1 then
|
||||||
|
return net.force_player_slot(tonumber(PlayerID), SideID, SlotID or '' )
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Force a client back to spectators.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @return #boolean Succes
|
||||||
|
function NET:ReturnToSpectators(Client)
|
||||||
|
local outcome = self:ForceSlot(Client,0)
|
||||||
|
return outcome
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Converts a lua value to a JSON string.
|
||||||
|
-- @param #string Lua Anything lua
|
||||||
|
-- @return #table Json
|
||||||
|
function NET.Lua2Json(Lua)
|
||||||
|
return net.lua2json(Lua)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Converts a JSON string to a lua value.
|
||||||
|
-- @param #string Json Anything JSON
|
||||||
|
-- @return #table Lua
|
||||||
|
function NET.Lua2Json(Json)
|
||||||
|
return net.json2lua(Json)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Executes a lua string in a given lua environment in the game.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string State The state in which to execute - see below.
|
||||||
|
-- @param #string DoString The lua string to be executed.
|
||||||
|
-- @return #string Output
|
||||||
|
-- @usage
|
||||||
|
-- States are:
|
||||||
|
-- 'config': the state in which $INSTALL_DIR/Config/main.cfg is executed, as well as $WRITE_DIR/Config/autoexec.cfg - used for configuration settings
|
||||||
|
-- 'mission': holds current mission
|
||||||
|
-- 'export': runs $WRITE_DIR/Scripts/Export.lua and the relevant export API
|
||||||
|
function NET:DoStringIn(State,DoString)
|
||||||
|
return net.dostring_in(State,DoString)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Write an "INFO" entry to the DCS log file, with the message Message.
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string Message The message to be logged.
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:Log(Message)
|
||||||
|
net.log(Message)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get some data of pilots who have currently joined
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client Provide either the client object whose data to find **or**
|
||||||
|
-- @param #string Name The player name whose data to find
|
||||||
|
-- @return #table Table of #NET.PlayerData or nil if not found
|
||||||
|
function NET:GetKnownPilotData(Client,Name)
|
||||||
|
local name = Name
|
||||||
|
if Client and not Name then
|
||||||
|
name = Client:GetPlayerName()
|
||||||
|
end
|
||||||
|
if name then
|
||||||
|
return self.KnownPilots[name]
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Status - housekeeping
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:onafterStatus(From,Event,To)
|
||||||
|
self:T({From,Event,To})
|
||||||
|
|
||||||
|
local function HouseHold(tavolo)
|
||||||
|
local TNow = timer.getTime()
|
||||||
|
for _,entry in pairs (tavolo) do
|
||||||
|
if entry >= TNow then entry = nil end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
HouseHold(self.BlockedPilots)
|
||||||
|
HouseHold(self.BlockedSides)
|
||||||
|
HouseHold(self.BlockedSlots)
|
||||||
|
HouseHold(self.BlockedUCIDs)
|
||||||
|
|
||||||
|
if self:Is("Running") then
|
||||||
|
self:__Status(-60)
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stop the event functions
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:onafterRun(From,Event,To)
|
||||||
|
self:T({From,Event,To})
|
||||||
|
self:HandleEvent(EVENTS.PlayerEnterUnit,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.PlayerLeaveUnit,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.PilotDead,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.Ejection,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.Crash,self._EventHandler)
|
||||||
|
self:HandleEvent(EVENTS.SelfKillPilot,self._EventHandler)
|
||||||
|
self:__Status(-30)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Stop the event functions
|
||||||
|
-- @param #NET self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @return #NET self
|
||||||
|
function NET:onafterStop(From,Event,To)
|
||||||
|
self:T({From,Event,To})
|
||||||
|
self:UnHandleEvent(EVENTS.PlayerEnterUnit)
|
||||||
|
self:UnHandleEvent(EVENTS.PlayerEnterAircraft)
|
||||||
|
self:UnHandleEvent(EVENTS.PlayerLeaveUnit)
|
||||||
|
self:UnHandleEvent(EVENTS.PilotDead)
|
||||||
|
self:UnHandleEvent(EVENTS.Ejection)
|
||||||
|
self:UnHandleEvent(EVENTS.Crash)
|
||||||
|
self:UnHandleEvent(EVENTS.SelfKillPilot)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
-- End of NET
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
end
|
||||||
@@ -845,6 +845,78 @@ function POSITIONABLE:GetVelocityKNOTS()
|
|||||||
return UTILS.MpsToKnots( self:GetVelocityMPS() )
|
return UTILS.MpsToKnots( self:GetVelocityMPS() )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns the true airspeed (TAS). This is calculated from the current velocity minus wind in 3D.
|
||||||
|
-- @param #POSITIONABLE self
|
||||||
|
-- @return #number TAS in m/s. Returns 0 if the POSITIONABLE does not exist.
|
||||||
|
function POSITIONABLE:GetAirspeedTrue()
|
||||||
|
|
||||||
|
-- TAS
|
||||||
|
local tas=0
|
||||||
|
|
||||||
|
-- Get current coordinate.
|
||||||
|
local coord=self:GetCoord()
|
||||||
|
|
||||||
|
if coord then
|
||||||
|
|
||||||
|
-- Altitude in meters.
|
||||||
|
local alt=coord.y
|
||||||
|
|
||||||
|
-- Wind velocity vector.
|
||||||
|
local wvec3=coord:GetWindVec3(alt, false)
|
||||||
|
|
||||||
|
-- Velocity vector.
|
||||||
|
local vvec3=self:GetVelocityVec3()
|
||||||
|
|
||||||
|
--GS=TAS+WIND ==> TAS=GS-WIND
|
||||||
|
local tasvec3=UTILS.VecSubstract(vvec3, wvec3)
|
||||||
|
|
||||||
|
-- True airspeed in m/s
|
||||||
|
tas=UTILS.VecNorm(tasvec3)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return tas
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the indicated airspeed (IAS).
|
||||||
|
-- The IAS is calculated from the TAS under the approximation that TAS increases by ~2% with every 1000 feet altitude ASL.
|
||||||
|
-- @param #POSITIONABLE self
|
||||||
|
-- @param #number oatcorr (Optional) Outside air temperature (OAT) correction factor. Default 0.017 (=1.7%).
|
||||||
|
-- @return #number IAS in m/s. Returns 0 if the POSITIONABLE does not exist.
|
||||||
|
function POSITIONABLE:GetAirspeedIndicated(oatcorr)
|
||||||
|
|
||||||
|
-- Get true airspeed.
|
||||||
|
local tas=self:GetAirspeedTrue()
|
||||||
|
|
||||||
|
-- Get altitude.
|
||||||
|
local altitude=self:GetAltitude()
|
||||||
|
|
||||||
|
-- Convert TAS to IAS.
|
||||||
|
local ias=UTILS.TasToIas(tas, altitude, oatcorr)
|
||||||
|
|
||||||
|
return ias
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Returns the horizonal speed relative to eath's surface. The vertical component of the velocity vector is projected out (set to zero).
|
||||||
|
-- @param #POSITIONABLE self
|
||||||
|
-- @return #number Ground speed in m/s. Returns 0 if the POSITIONABLE does not exist.
|
||||||
|
function POSITIONABLE:GetGroundSpeed()
|
||||||
|
|
||||||
|
local gs=0
|
||||||
|
|
||||||
|
local vel=self:GetVelocityVec3()
|
||||||
|
|
||||||
|
if vel then
|
||||||
|
|
||||||
|
local vec2={x=vel.x, y=vel.z}
|
||||||
|
|
||||||
|
gs=UTILS.Vec2Norm(vel)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return gs
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the Angle of Attack of a POSITIONABLE.
|
--- Returns the Angle of Attack of a POSITIONABLE.
|
||||||
-- @param #POSITIONABLE self
|
-- @param #POSITIONABLE self
|
||||||
-- @return #number Angle of attack in degrees.
|
-- @return #number Angle of attack in degrees.
|
||||||
@@ -1696,6 +1768,7 @@ do -- Cargo
|
|||||||
["tt_DSHK"] = 6,
|
["tt_DSHK"] = 6,
|
||||||
["HL_KORD"] = 6,
|
["HL_KORD"] = 6,
|
||||||
["HL_DSHK"] = 6,
|
["HL_DSHK"] = 6,
|
||||||
|
["CCKW_353"] = 16, --GMC CCKW 2½-ton 6×6 truck, estimating 16 soldiers
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Assuming that each passenger weighs 95 kg on average.
|
-- Assuming that each passenger weighs 95 kg on average.
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
--
|
--
|
||||||
-- ### Author: **FlightControl**
|
-- ### Author: **FlightControl**
|
||||||
--
|
--
|
||||||
-- ### Contributions: **Applevangelist**
|
-- ### Contributions: **Applevangelist**, **funkyfranky**
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
@@ -12,12 +12,12 @@
|
|||||||
-- @image Wrapper_Scenery.JPG
|
-- @image Wrapper_Scenery.JPG
|
||||||
|
|
||||||
|
|
||||||
|
--- SCENERY Class
|
||||||
--- @type SCENERY
|
-- @type SCENERY
|
||||||
-- @field #string ClassName
|
-- @field #string ClassName Name of the class.
|
||||||
-- @field #string SceneryName
|
-- @field #string SceneryName Name of the scenery object.
|
||||||
-- @field #DCS.Object SceneryObject
|
-- @field DCS#Object SceneryObject DCS scenery object.
|
||||||
-- @field #number Life0
|
-- @field #number Life0 Initial life points.
|
||||||
-- @extends Wrapper.Positionable#POSITIONABLE
|
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||||
|
|
||||||
|
|
||||||
@@ -37,12 +37,16 @@ SCENERY = {
|
|||||||
--- Register scenery object as POSITIONABLE.
|
--- Register scenery object as POSITIONABLE.
|
||||||
--@param #SCENERY self
|
--@param #SCENERY self
|
||||||
--@param #string SceneryName Scenery name.
|
--@param #string SceneryName Scenery name.
|
||||||
--@param #DCS.Object SceneryObject DCS scenery object.
|
--@param DCS#Object SceneryObject DCS scenery object.
|
||||||
--@return #SCENERY Scenery object.
|
--@return #SCENERY Scenery object.
|
||||||
function SCENERY:Register( SceneryName, SceneryObject )
|
function SCENERY:Register( SceneryName, SceneryObject )
|
||||||
|
|
||||||
local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) )
|
local self = BASE:Inherit( self, POSITIONABLE:New( SceneryName ) )
|
||||||
|
|
||||||
self.SceneryName = SceneryName
|
self.SceneryName = SceneryName
|
||||||
|
|
||||||
self.SceneryObject = SceneryObject
|
self.SceneryObject = SceneryObject
|
||||||
|
|
||||||
if self.SceneryObject then
|
if self.SceneryObject then
|
||||||
self.Life0 = self.SceneryObject:getLife()
|
self.Life0 = self.SceneryObject:getLife()
|
||||||
else
|
else
|
||||||
@@ -53,7 +57,7 @@ end
|
|||||||
|
|
||||||
--- Obtain DCS Object from the SCENERY Object.
|
--- Obtain DCS Object from the SCENERY Object.
|
||||||
--@param #SCENERY self
|
--@param #SCENERY self
|
||||||
--@return #DCS.Object DCS scenery object.
|
--@return DCS#Object DCS scenery object.
|
||||||
function SCENERY:GetDCSObject()
|
function SCENERY:GetDCSObject()
|
||||||
return self.SceneryObject
|
return self.SceneryObject
|
||||||
end
|
end
|
||||||
@@ -69,7 +73,7 @@ function SCENERY:GetLife()
|
|||||||
return life
|
return life
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get current initial life points from the SCENERY Object.
|
--- Get initial life points of the SCENERY Object.
|
||||||
--@param #SCENERY self
|
--@param #SCENERY self
|
||||||
--@return #number life
|
--@return #number life
|
||||||
function SCENERY:GetLife0()
|
function SCENERY:GetLife0()
|
||||||
@@ -90,7 +94,7 @@ function SCENERY:IsDead()
|
|||||||
return self:GetLife() < 1 and true or false
|
return self:GetLife() < 1 and true or false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get the threat level of a SCENERY object. Always 0.
|
--- Get the threat level of a SCENERY object. Always 0 as scenery does not pose a threat to anyone.
|
||||||
--@param #SCENERY self
|
--@param #SCENERY self
|
||||||
--@return #number Threat level 0.
|
--@return #number Threat level 0.
|
||||||
--@return #string "Scenery".
|
--@return #string "Scenery".
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ end
|
|||||||
|
|
||||||
--- Get the DCS unit object.
|
--- Get the DCS unit object.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
-- @return DCS#Unit
|
-- @return DCS#Unit The DCS unit object.
|
||||||
function UNIT:GetDCSObject()
|
function UNIT:GetDCSObject()
|
||||||
|
|
||||||
local DCSUnit = Unit.getByName( self.UnitName )
|
local DCSUnit = Unit.getByName( self.UnitName )
|
||||||
@@ -325,14 +325,19 @@ function UNIT:IsAlive()
|
|||||||
local DCSUnit = self:GetDCSObject() -- DCS#Unit
|
local DCSUnit = self:GetDCSObject() -- DCS#Unit
|
||||||
|
|
||||||
if DCSUnit then
|
if DCSUnit then
|
||||||
local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive()
|
local UnitIsAlive = DCSUnit:isExist() and DCSUnit:isActive() -- and DCSUnit:getLife() > 1
|
||||||
return UnitIsAlive
|
return UnitIsAlive
|
||||||
end
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns if the Unit is dead.
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @return #boolean `true` if Unit is dead, else false or nil if the unit does not exist
|
||||||
|
function UNIT:IsDead()
|
||||||
|
return not self:IsAlive()
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the Unit's callsign - the localized string.
|
--- Returns the Unit's callsign - the localized string.
|
||||||
-- @param #UNIT self
|
-- @param #UNIT self
|
||||||
@@ -626,7 +631,7 @@ function UNIT:IsFuelSupply()
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Returns the unit's group if it exist and nil otherwise.
|
--- Returns the unit's group if it exists and nil otherwise.
|
||||||
-- @param Wrapper.Unit#UNIT self
|
-- @param Wrapper.Unit#UNIT self
|
||||||
-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist.
|
-- @return Wrapper.Group#GROUP The Group of the Unit or `nil` if the unit does not exist.
|
||||||
function UNIT:GetGroup()
|
function UNIT:GetGroup()
|
||||||
@@ -915,7 +920,7 @@ function UNIT:GetLife()
|
|||||||
|
|
||||||
local DCSUnit = self:GetDCSObject()
|
local DCSUnit = self:GetDCSObject()
|
||||||
|
|
||||||
if DCSUnit then
|
if DCSUnit and DCSUnit:isExist() then
|
||||||
local UnitLife = DCSUnit:getLife()
|
local UnitLife = DCSUnit:getLife()
|
||||||
return UnitLife
|
return UnitLife
|
||||||
end
|
end
|
||||||
@@ -967,6 +972,24 @@ function UNIT:GetDamageRelative()
|
|||||||
return 1
|
return 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Returns the current value for an animation argument on the external model of the given object.
|
||||||
|
-- Each model animation has an id tied to with different values representing different states of the model.
|
||||||
|
-- Animation arguments can be figured out by opening the respective 3d model in the modelviewer.
|
||||||
|
-- @param #UNIT self
|
||||||
|
-- @param #number AnimationArgument Number corresponding to the animated part of the unit.
|
||||||
|
-- @return #number Value of the animation argument [-1, 1]. If draw argument value is invalid for the unit in question a value of 0 will be returned.
|
||||||
|
function UNIT:GetDrawArgumentValue(AnimationArgument)
|
||||||
|
|
||||||
|
local DCSUnit = self:GetDCSObject()
|
||||||
|
|
||||||
|
if DCSUnit then
|
||||||
|
local value = DCSUnit:getDrawArgumentValue(AnimationArgument or 0)
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
--- Returns the category of the #UNIT from descriptor. Returns one of
|
--- Returns the category of the #UNIT from descriptor. Returns one of
|
||||||
--
|
--
|
||||||
-- * Unit.Category.AIRPLANE
|
-- * Unit.Category.AIRPLANE
|
||||||
|
|||||||
839
Moose Development/Moose/Wrapper/Weapon.lua
Normal file
839
Moose Development/Moose/Wrapper/Weapon.lua
Normal file
@@ -0,0 +1,839 @@
|
|||||||
|
--- **Wrapper** - Weapon functions.
|
||||||
|
--
|
||||||
|
-- ## Main Features:
|
||||||
|
--
|
||||||
|
-- * Convenient access to DCS API functions
|
||||||
|
-- * Track weapon and get impact position
|
||||||
|
-- * Get launcher and target of weapon
|
||||||
|
-- * Define callback function when weapon impacts
|
||||||
|
-- * Define callback function when tracking weapon
|
||||||
|
-- * Mark impact points on F10 map
|
||||||
|
-- * Put coloured smoke on impact points
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## Example Missions:
|
||||||
|
--
|
||||||
|
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Wrapper%20-%20Weapon).
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **funkyfranky**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
-- @module Wrapper.Weapon
|
||||||
|
-- @image Wrapper_Weapon.png
|
||||||
|
|
||||||
|
|
||||||
|
--- WEAPON class.
|
||||||
|
-- @type WEAPON
|
||||||
|
-- @field #string ClassName Name of the class.
|
||||||
|
-- @field #number verbose Verbosity level.
|
||||||
|
-- @field #string lid Class id string for output to DCS log file.
|
||||||
|
-- @field DCS#Weapon weapon The DCS weapon object.
|
||||||
|
-- @field #string name Name of the weapon object.
|
||||||
|
-- @field #string typeName Type name of the weapon.
|
||||||
|
-- @field #number category Weapon category 0=SHELL, 1=MISSILE, 2=ROCKET, 3=BOMB, 4=TORPEDO (Weapon.Category.X).
|
||||||
|
-- @field #number categoryMissile Missile category 0=AAM, 1=SAM, 2=BM, 3=ANTI_SHIP, 4=CRUISE, 5=OTHER (Weapon.MissileCategory.X).
|
||||||
|
-- @field #number coalition Coalition ID.
|
||||||
|
-- @field #number country Country ID.
|
||||||
|
-- @field DCS#Desc desc Descriptor table.
|
||||||
|
-- @field DCS#Unit launcher Launcher DCS unit.
|
||||||
|
-- @field Wrapper.Unit#UNIT launcherUnit Launcher Unit.
|
||||||
|
-- @field #string launcherName Name of launcher unit.
|
||||||
|
-- @field #number dtTrack Time step in seconds for tracking scheduler.
|
||||||
|
-- @field #function impactFunc Callback function for weapon impact.
|
||||||
|
-- @field #table impactArg Optional arguments for the impact callback function.
|
||||||
|
-- @field #function trackFunc Callback function when weapon is tracked and alive.
|
||||||
|
-- @field #table trackArg Optional arguments for the track callback function.
|
||||||
|
-- @field DCS#Vec3 vec3 Last known 3D position vector of the tracked weapon.
|
||||||
|
-- @field DCS#Position3 pos3 Last known 3D position and direction vector of the tracked weapon.
|
||||||
|
-- @field Core.Point#COORDINATE coordinate Coordinate object of the weapon. Can be used in other classes.
|
||||||
|
-- @field DCS#Vec3 impactVec3 Impact 3D vector.
|
||||||
|
-- @field Core.Point#COORDINATE impactCoord Impact coordinate.
|
||||||
|
-- @field #number trackScheduleID Tracking scheduler ID. Can be used to remove/destroy the scheduler function.
|
||||||
|
-- @field #boolean tracking If `true`, scheduler will keep tracking. Otherwise, function will return nil and stop tracking.
|
||||||
|
-- @field #boolean impactMark If `true`, the impact point is marked on the F10 map. Requires tracking to be started.
|
||||||
|
-- @field #boolean impactSmoke If `true`, the impact point is marked by smoke. Requires tracking to be started.
|
||||||
|
-- @field #number impactSmokeColor Colour of impact point smoke.
|
||||||
|
-- @field #boolean impactDestroy If `true`, destroy weapon before impact. Requires tracking to be started and sufficiently small time step.
|
||||||
|
-- @field #number impactDestroyDist Distance in meters to the estimated impact point. If smaller, then weapon is destroyed.
|
||||||
|
-- @field #number distIP Distance in meters for the intercept point estimation.
|
||||||
|
-- @field Wrapper.Unit#UNIT target Last known target.
|
||||||
|
-- @extends Wrapper.Positionable#POSITIONABLE
|
||||||
|
|
||||||
|
--- *In the long run, the sharpest weapon of all is a kind and gentle spirit.* -- Anne Frank
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- # The WEAPON Concept
|
||||||
|
--
|
||||||
|
-- The WEAPON class offers an easy-to-use wrapper interface to all DCS API functions.
|
||||||
|
--
|
||||||
|
-- Probably, the most striking highlight is that the position of the weapon can be tracked and its impact position can be determined, which is not
|
||||||
|
-- possible with the native DCS scripting engine functions.
|
||||||
|
--
|
||||||
|
-- **Note** that this wrapper class is different from most others as weapon objects cannot be found with a DCS API function like `getByName()`.
|
||||||
|
-- They can only be found in DCS events like the "Shot" event, where the weapon object is contained in the event data.
|
||||||
|
--
|
||||||
|
-- # Tracking
|
||||||
|
--
|
||||||
|
-- The status of the weapon can be tracked with the @{#WEAPON.StartTrack} function. This function will try to determin the position of the weapon in (normally) relatively
|
||||||
|
-- small time steps. The time step can be set via the @{#WEAPON.SetTimeStepTrack} function and is by default set to 0.01 seconds.
|
||||||
|
--
|
||||||
|
-- Once the position cannot be retrieved any more, the weapon has impacted (or was destroyed otherwise) and the last known position is safed as the impact point.
|
||||||
|
-- The impact point can be accessed with the @{#WEAPON.GetImpactVec3} or @{#WEAPON.GetImpactCoordinate} functions.
|
||||||
|
--
|
||||||
|
-- ## Impact Point Marking
|
||||||
|
--
|
||||||
|
-- You can mark the impact point on the F10 map with @{#WEAPON.SetMarkImpact}.
|
||||||
|
--
|
||||||
|
-- You can also trigger coloured smoke at the impact point via @{#WEAPON.SetSmokeImpact}.
|
||||||
|
--
|
||||||
|
-- ## Callback functions
|
||||||
|
--
|
||||||
|
-- It is possible to define functions that are called during the tracking of the weapon and upon impact, which help you to customize further actions.
|
||||||
|
--
|
||||||
|
-- ### Callback on Impact
|
||||||
|
--
|
||||||
|
-- The function called on impact can be set with @{#WEAPON.SetFuncImpact}
|
||||||
|
--
|
||||||
|
-- ### Callback when Tracking
|
||||||
|
--
|
||||||
|
-- The function called each time the weapon status is tracked can be set with @{#WEAPON.SetFuncTrack}
|
||||||
|
--
|
||||||
|
-- # Target
|
||||||
|
--
|
||||||
|
-- If the weapon has a specific target, you can get it with the @{#WEAPON.GetTarget} function. Note that the object, which is returned can vary. Normally, it is a UNIT
|
||||||
|
-- but it could also be a STATIC object.
|
||||||
|
--
|
||||||
|
-- Also note that the weapon does not always have a target, it can loose a target and re-aquire it and the target might change to another unit.
|
||||||
|
--
|
||||||
|
-- You can get the target name with the @{#WEAPON.GetTargetName} function.
|
||||||
|
--
|
||||||
|
-- The distance to the target is returned by the @{#WEAPON.GetTargetDistance} function.
|
||||||
|
--
|
||||||
|
-- # Category
|
||||||
|
--
|
||||||
|
-- The category (bomb, rocket, missile, shell, torpedo) of the weapon can be retrieved with the @{#WEAPON.GetCategory} function.
|
||||||
|
--
|
||||||
|
-- You can check if the weapon is a
|
||||||
|
--
|
||||||
|
-- * bomb with @{#WEAPON.IsBomb}
|
||||||
|
-- * rocket with @{#WEAPON.IsRocket}
|
||||||
|
-- * missile with @{#WEAPON.IsMissile}
|
||||||
|
-- * shell with @{#WEAPON.IsShell}
|
||||||
|
-- * torpedo with @{#WEAPON.IsTorpedo}
|
||||||
|
--
|
||||||
|
-- # Parameters
|
||||||
|
--
|
||||||
|
-- You can get various parameters of the weapon, *e.g.*
|
||||||
|
--
|
||||||
|
-- * position: @{#WEAPON.GetVec3}, @{#WEAPON.GetVec2 }, @{#WEAPON.GetCoordinate}
|
||||||
|
-- * speed: @{#WEAPON.GetSpeed}
|
||||||
|
-- * coalition: @{#WEAPON.GetCoalition}
|
||||||
|
-- * country: @{#WEAPON.GetCountry}
|
||||||
|
--
|
||||||
|
-- # Dependencies
|
||||||
|
--
|
||||||
|
-- This class is used (at least) in the MOOSE classes:
|
||||||
|
--
|
||||||
|
-- * RANGE (to determine the impact points of bombs and missiles)
|
||||||
|
-- * ARTY (to destroy and replace shells with smoke or illumination)
|
||||||
|
-- * FOX (to destroy the missile before it hits the target)
|
||||||
|
--
|
||||||
|
-- @field #WEAPON
|
||||||
|
WEAPON = {
|
||||||
|
ClassName = "WEAPON",
|
||||||
|
verbose = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
--- WEAPON class version.
|
||||||
|
-- @field #string version
|
||||||
|
WEAPON.version="0.1.0"
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- TODO list
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
-- TODO: A lot...
|
||||||
|
-- TODO: Destroy before impact.
|
||||||
|
-- TODO: Monitor target.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Constructor
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Create a new WEAPON object from the DCS weapon object.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param DCS#Weapon WeaponObject The DCS weapon object.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:New(WeaponObject)
|
||||||
|
|
||||||
|
-- Nil check on object.
|
||||||
|
if WeaponObject==nil then
|
||||||
|
env.error("ERROR: Weapon object does NOT exist")
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Inherit everything from FSM class.
|
||||||
|
local self=BASE:Inherit(self, POSITIONABLE:New("Weapon")) -- #WEAPON
|
||||||
|
|
||||||
|
-- Set DCS weapon object.
|
||||||
|
self.weapon=WeaponObject
|
||||||
|
|
||||||
|
-- Descriptors containing a lot of info.
|
||||||
|
self.desc=WeaponObject:getDesc()
|
||||||
|
|
||||||
|
-- This gives the object category which is always Object.Category.WEAPON!
|
||||||
|
--self.category=WeaponObject:getCategory()
|
||||||
|
|
||||||
|
-- Weapon category: 0=SHELL, 1=MISSILE, 2=ROCKET, 3=BOMB (Weapon.Category.X)
|
||||||
|
self.category = self.desc.category
|
||||||
|
|
||||||
|
if self:IsMissile() and self.desc.missileCategory then
|
||||||
|
self.categoryMissile=self.desc.missileCategory
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get type name.
|
||||||
|
self.typeName=WeaponObject:getTypeName() or "Unknown Type"
|
||||||
|
|
||||||
|
-- Get name of object. Usually a number like "1234567".
|
||||||
|
self.name=WeaponObject:getName()
|
||||||
|
|
||||||
|
-- Get coaliton of weapon.
|
||||||
|
self.coalition=WeaponObject:getCoalition()
|
||||||
|
|
||||||
|
-- Get country of weapon.
|
||||||
|
self.country=WeaponObject:getCountry()
|
||||||
|
|
||||||
|
-- Get DCS unit of the launcher.
|
||||||
|
self.launcher=WeaponObject:getLauncher()
|
||||||
|
|
||||||
|
-- Get launcher of weapon.
|
||||||
|
self.launcherName="Unknown Launcher"
|
||||||
|
if self.launcher then
|
||||||
|
self.launcherName=self.launcher:getName()
|
||||||
|
self.launcherUnit=UNIT:Find(self.launcher)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Init the coordinate of the weapon from that of the launcher.
|
||||||
|
self.coordinate=COORDINATE:NewFromVec3(self.launcher:getPoint())
|
||||||
|
|
||||||
|
-- Set log ID.
|
||||||
|
self.lid=string.format("[%s] %s | ", self.typeName, self.name)
|
||||||
|
|
||||||
|
-- Set default parameters
|
||||||
|
self:SetTimeStepTrack()
|
||||||
|
self:SetDistanceInterceptPoint()
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
local text=string.format("Weapon v%s\nName=%s, TypeName=%s, Category=%s, Coalition=%d, Country=%d, Launcher=%s",
|
||||||
|
self.version, self.name, self.typeName, self.category, self.coalition, self.country, self.launcherName)
|
||||||
|
self:T(self.lid..text)
|
||||||
|
|
||||||
|
-- Descriptors.
|
||||||
|
self:T2(self.desc)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- User API Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Set verbosity level.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number VerbosityLevel Level of output (higher=more). Default 0.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetVerbosity(VerbosityLevel)
|
||||||
|
self.verbose=VerbosityLevel or 0
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set track position time step.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number TimeStep Time step in seconds when the position is updated. Default 0.01 sec ==> 100 evaluations per second.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetTimeStepTrack(TimeStep)
|
||||||
|
self.dtTrack=TimeStep or 0.01
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set distance of intercept point for estimated impact point.
|
||||||
|
-- If the weapon cannot be tracked any more, the intercept point from its last known position and direction is used to get
|
||||||
|
-- a better approximation of the impact point. Can be useful when using longer time steps in the tracking and still achieve
|
||||||
|
-- a good result on the impact point.
|
||||||
|
-- It uses the DCS function [getIP](https://wiki.hoggitworld.com/view/DCS_func_getIP).
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number Distance Distance in meters. Default is 50 m. Set to 0 to deactivate.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetDistanceInterceptPoint(Distance)
|
||||||
|
self.distIP=Distance or 50
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Mark impact point on the F10 map. This requires that the tracking has been started.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #boolean Switch If `true` or nil, impact is marked.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetMarkImpact(Switch)
|
||||||
|
|
||||||
|
if Switch==false then
|
||||||
|
self.impactMark=false
|
||||||
|
else
|
||||||
|
self.impactMark=true
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Put smoke on impact point. This requires that the tracking has been started.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #boolean Switch If `true` or nil, impact is smoked.
|
||||||
|
-- @param #number SmokeColor Color of smoke. Default is `SMOKECOLOR.Red`.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetSmokeImpact(Switch, SmokeColor)
|
||||||
|
|
||||||
|
if Switch==false then
|
||||||
|
self.impactSmoke=false
|
||||||
|
else
|
||||||
|
self.impactSmoke=true
|
||||||
|
end
|
||||||
|
|
||||||
|
self.impactSmokeColor=SmokeColor or SMOKECOLOR.Red
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set callback function when weapon is tracked and still alive. The first argument will be the WEAPON object.
|
||||||
|
-- Note that this can be called many times per second. So be careful for performance reasons.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #function FuncTrack Function called during tracking.
|
||||||
|
-- @param ... Optional function arguments.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:SetFuncTrack(FuncTrack, ...)
|
||||||
|
self.trackFunc=FuncTrack
|
||||||
|
self.trackArg=arg or {}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set callback function when weapon impacted or was destroyed otherwise, *i.e.* cannot be tracked any more.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #function FuncImpact Function called once the weapon impacted.
|
||||||
|
-- @param ... Optional function arguments.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
--
|
||||||
|
-- @usage
|
||||||
|
-- -- Function called on impact.
|
||||||
|
-- local function OnImpact(Weapon)
|
||||||
|
-- Weapon:GetImpactCoordinate():MarkToAll("Impact Coordinate of weapon")
|
||||||
|
-- end
|
||||||
|
--
|
||||||
|
-- -- Set which function to call.
|
||||||
|
-- myweapon:SetFuncImpact(OnImpact)
|
||||||
|
--
|
||||||
|
-- -- Start tracking.
|
||||||
|
-- myweapon:Track()
|
||||||
|
--
|
||||||
|
function WEAPON:SetFuncImpact(FuncImpact, ...)
|
||||||
|
self.impactFunc=FuncImpact
|
||||||
|
self.impactArg=arg or {}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the unit that launched the weapon.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return Wrapper.Unit#UNIT Laucher unit.
|
||||||
|
function WEAPON:GetLauncher()
|
||||||
|
return self.launcherUnit
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the target, which the weapon is guiding to.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return Wrapper.Object#OBJECT The target object, which can be a UNIT or STATIC object.
|
||||||
|
function WEAPON:GetTarget()
|
||||||
|
|
||||||
|
local target=nil --Wrapper.Object#OBJECT
|
||||||
|
|
||||||
|
if self.weapon then
|
||||||
|
|
||||||
|
-- Get the DCS target object, which can be a Unit, Weapon, Static, Scenery, Airbase.
|
||||||
|
local object=self.weapon:getTarget()
|
||||||
|
|
||||||
|
if object then
|
||||||
|
|
||||||
|
-- Get object category.
|
||||||
|
local category=object:getCategory()
|
||||||
|
|
||||||
|
--Target name
|
||||||
|
local name=object:getName()
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Got Target Object %s, category=%d", object:getName(), category))
|
||||||
|
|
||||||
|
if category==Object.Category.UNIT then
|
||||||
|
|
||||||
|
target=UNIT:FindByName(name)
|
||||||
|
|
||||||
|
elseif category==Object.Category.STATIC then
|
||||||
|
|
||||||
|
target=STATIC:FindByName(name, false)
|
||||||
|
|
||||||
|
elseif category==Object.Category.SCENERY then
|
||||||
|
self:E(self.lid..string.format("ERROR: Scenery target not implemented yet!"))
|
||||||
|
else
|
||||||
|
self:E(self.lid..string.format("ERROR: Object category=%d is not implemented yet!", category))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the distance to the current target the weapon is guiding to.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #function ConversionFunction (Optional) Conversion function from meters to desired unit, *e.g.* `UTILS.MpsToKmph`.
|
||||||
|
-- @return #number Distance from weapon to target in meters.
|
||||||
|
function WEAPON:GetTargetDistance(ConversionFunction)
|
||||||
|
|
||||||
|
-- Get the target of the weapon.
|
||||||
|
local target=self:GetTarget() --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
local distance=nil
|
||||||
|
if target then
|
||||||
|
|
||||||
|
-- Current position of target.
|
||||||
|
local tv3=target:GetVec3()
|
||||||
|
|
||||||
|
-- Current position of weapon.
|
||||||
|
local wv3=self:GetVec3()
|
||||||
|
|
||||||
|
if tv3 and wv3 then
|
||||||
|
distance=UTILS.VecDist3D(tv3, wv3)
|
||||||
|
|
||||||
|
if ConversionFunction then
|
||||||
|
distance=ConversionFunction(distance)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return distance
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get name the current target the weapon is guiding to.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #string Name of the target or "None" if no target.
|
||||||
|
function WEAPON:GetTargetName()
|
||||||
|
|
||||||
|
-- Get the target of the weapon.
|
||||||
|
local target=self:GetTarget() --Wrapper.Unit#UNIT
|
||||||
|
|
||||||
|
local name="None"
|
||||||
|
if target then
|
||||||
|
name=target:GetName()
|
||||||
|
end
|
||||||
|
|
||||||
|
return name
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get velocity vector of weapon.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return DCS#Vec3 Velocity vector with x, y and z components in meters/second.
|
||||||
|
function WEAPON:GetVelocityVec3()
|
||||||
|
local Vvec3=nil
|
||||||
|
if self.weapon then
|
||||||
|
Vvec3=self.weapon:getVelocity()
|
||||||
|
end
|
||||||
|
return Vvec3
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get speed of weapon.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #function ConversionFunction (Optional) Conversion function from m/s to desired unit, *e.g.* `UTILS.MpsToKmph`.
|
||||||
|
-- @return #number Speed in meters per second.
|
||||||
|
function WEAPON:GetSpeed(ConversionFunction)
|
||||||
|
|
||||||
|
local speed=nil
|
||||||
|
|
||||||
|
if self.weapon then
|
||||||
|
|
||||||
|
local v=self:GetVelocityVec3()
|
||||||
|
|
||||||
|
speed=UTILS.VecNorm(v)
|
||||||
|
|
||||||
|
if ConversionFunction then
|
||||||
|
speed=ConversionFunction(speed)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return speed
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the current 3D position vector.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return DCS#Vec3 Current position vector in 3D.
|
||||||
|
function WEAPON:GetVec3()
|
||||||
|
|
||||||
|
local vec3=nil
|
||||||
|
if self.weapon then
|
||||||
|
vec3=self.weapon:getPoint()
|
||||||
|
end
|
||||||
|
|
||||||
|
return vec3
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Get the current 2D position vector.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return DCS#Vec2 Current position vector in 2D.
|
||||||
|
function WEAPON:GetVec2()
|
||||||
|
|
||||||
|
local vec3=self:GetVec3()
|
||||||
|
|
||||||
|
if vec3 then
|
||||||
|
|
||||||
|
local vec2={x=vec3.x, y=vec3.z}
|
||||||
|
|
||||||
|
return vec2
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get type name.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #string The type name.
|
||||||
|
function WEAPON:GetTypeName()
|
||||||
|
return self.typeName
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get coalition.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #number Coalition ID.
|
||||||
|
function WEAPON:GetCoalition()
|
||||||
|
return self.coalition
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get country.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #number Country ID.
|
||||||
|
function WEAPON:GetCountry()
|
||||||
|
return self.country
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get DCS object.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return DCS#Weapon The weapon object.
|
||||||
|
function WEAPON:GetDCSObject()
|
||||||
|
-- This polymorphic function is used in Wrapper.Identifiable#IDENTIFIABLE
|
||||||
|
return self.weapon
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the impact position vector. Note that this might not exist if the weapon has not impacted yet!
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return DCS#Vec3 Impact position vector (if any).
|
||||||
|
function WEAPON:GetImpactVec3()
|
||||||
|
return self.impactVec3
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the impact coordinate. Note that this might not exist if the weapon has not impacted yet!
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return Core.Point#COORDINATE Impact coordinate (if any).
|
||||||
|
function WEAPON:GetImpactCoordinate()
|
||||||
|
return self.impactCoord
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is in the air. Obviously not really useful for torpedos. Well, then again, this is DCS...
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, weapon is in the air and `false` if not. Returns `nil` if weapon object itself is `nil`.
|
||||||
|
function WEAPON:InAir()
|
||||||
|
local inAir=nil
|
||||||
|
if self.weapon then
|
||||||
|
inAir=self.weapon:inAir()
|
||||||
|
end
|
||||||
|
return inAir
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Check if weapon object (still) exists.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, the weapon object still exists and `false` otherwise. Returns `nil` if weapon object itself is `nil`.
|
||||||
|
function WEAPON:IsExist()
|
||||||
|
local isExist=nil
|
||||||
|
if self.weapon then
|
||||||
|
isExist=self.weapon:isExist()
|
||||||
|
end
|
||||||
|
return isExist
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Check if weapon is a bomb.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a bomb.
|
||||||
|
function WEAPON:IsBomb()
|
||||||
|
return self.category==Weapon.Category.BOMB
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a missile.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a missile.
|
||||||
|
function WEAPON:IsMissile()
|
||||||
|
return self.category==Weapon.Category.MISSILE
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a rocket.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a missile.
|
||||||
|
function WEAPON:IsRocket()
|
||||||
|
return self.category==Weapon.Category.ROCKET
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a shell.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a shell.
|
||||||
|
function WEAPON:IsShell()
|
||||||
|
return self.category==Weapon.Category.SHELL
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check if weapon is a torpedo.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @return #boolean If `true`, is a torpedo.
|
||||||
|
function WEAPON:IsTorpedo()
|
||||||
|
return self.category==Weapon.Category.TORPEDO
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Destroy the weapon object.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number Delay Delay before destroy in seconds.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:Destroy(Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
self:ScheduleOnce(Delay, WEAPON.Destroy, self, 0)
|
||||||
|
else
|
||||||
|
if self.weapon then
|
||||||
|
self:T(self.lid.."Destroying Weapon NOW!")
|
||||||
|
self:StopTrack()
|
||||||
|
self.weapon:destroy()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Start tracking the weapon until it impacts or is destroyed otherwise.
|
||||||
|
-- The position of the weapon is monitored in small time steps. Once the position cannot be determined anymore, the monitoring is stopped and the last known position is
|
||||||
|
-- the (approximate) impact point. Of course, the smaller the time step, the better the position can be determined. However, this can hit the performance as many
|
||||||
|
-- calculations per second need to be carried out.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number Delay Delay in seconds before the tracking starts. Default 0.001 sec.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:StartTrack(Delay)
|
||||||
|
|
||||||
|
-- Set delay before start.
|
||||||
|
Delay=math.max(Delay or 0.001, 0.001)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
self:T(self.lid..string.format("Start tracking weapon in %.4f sec", Delay))
|
||||||
|
|
||||||
|
-- Weapon is not yet "alife" just yet. Start timer in 0.001 seconds.
|
||||||
|
self.trackScheduleID=timer.scheduleFunction(WEAPON._TrackWeapon, self, timer.getTime() + Delay)
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Stop tracking the weapon by removing the scheduler function.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number Delay (Optional) Delay in seconds before the tracking is stopped.
|
||||||
|
-- @return #WEAPON self
|
||||||
|
function WEAPON:StopTrack(Delay)
|
||||||
|
|
||||||
|
if Delay and Delay>0 then
|
||||||
|
-- Delayed call.
|
||||||
|
self:ScheduleOnce(Delay, WEAPON.StopTrack, self, 0)
|
||||||
|
else
|
||||||
|
|
||||||
|
if self.trackScheduleID then
|
||||||
|
|
||||||
|
timer.removeFunction(self.trackScheduleID)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-- Private Functions
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- Track weapon until impact.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param DCS#Time time Time in seconds.
|
||||||
|
-- @return #number Time when called next or nil if not called again.
|
||||||
|
function WEAPON:_TrackWeapon(time)
|
||||||
|
|
||||||
|
-- Debug info.
|
||||||
|
if self.verbose>=20 then
|
||||||
|
self:I(self.lid..string.format("Tracking at T=%.5f", time))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Protected call to get the weapon position. If the position cannot be determined any more, the weapon has impacted and status is nil.
|
||||||
|
local status, pos3= pcall(
|
||||||
|
function()
|
||||||
|
local point=self.weapon:getPosition()
|
||||||
|
return point
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
if status then
|
||||||
|
|
||||||
|
-------------------------------
|
||||||
|
-- Weapon is still in exists --
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
-- Update last known position.
|
||||||
|
self.pos3 = pos3
|
||||||
|
|
||||||
|
-- Update last known vec3.
|
||||||
|
self.vec3 = UTILS.DeepCopy(self.pos3.p)
|
||||||
|
|
||||||
|
-- Update coordinate.
|
||||||
|
self.coordinate:UpdateFromVec3(self.vec3)
|
||||||
|
|
||||||
|
-- Keep on tracking by returning the next time below.
|
||||||
|
self.tracking=true
|
||||||
|
|
||||||
|
-- Callback function.
|
||||||
|
if self.trackFunc then
|
||||||
|
self.trackFunc(self, unpack(self.trackArg))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Verbose output.
|
||||||
|
if self.verbose>=5 then
|
||||||
|
|
||||||
|
-- Get vec2 of current position.
|
||||||
|
local vec2={x=self.vec3.x, y=self.vec3.z}
|
||||||
|
|
||||||
|
-- Land hight.
|
||||||
|
local height=land.getHeight(vec2)
|
||||||
|
|
||||||
|
-- Current height above ground level.
|
||||||
|
local agl=self.vec3.y-height
|
||||||
|
|
||||||
|
-- Estimated IP (if any)
|
||||||
|
local ip=self:_GetIP(self.distIP)
|
||||||
|
|
||||||
|
-- Distance between positon and estimated impact.
|
||||||
|
local d=0
|
||||||
|
if ip then
|
||||||
|
d=UTILS.VecDist3D(self.vec3, ip)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Output.
|
||||||
|
self:I(self.lid..string.format("T=%.3f: Height=%.3f m AGL=%.3f m, dIP=%.3f", time, height, agl, d))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
|
---------------------------
|
||||||
|
-- Weapon does NOT exist --
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
-- Get intercept point from position (p) and direction (x) in 50 meters.
|
||||||
|
local ip = self:_GetIP(self.distIP)
|
||||||
|
|
||||||
|
if self.verbose>=10 and ip then
|
||||||
|
|
||||||
|
-- Output.
|
||||||
|
self:I(self.lid.."Got intercept point!")
|
||||||
|
|
||||||
|
-- Coordinate of the impact point.
|
||||||
|
local coord=COORDINATE:NewFromVec3(ip)
|
||||||
|
|
||||||
|
-- Mark coordinate.
|
||||||
|
coord:MarkToAll("Intercept point")
|
||||||
|
coord:SmokeBlue()
|
||||||
|
|
||||||
|
-- Distance to last known pos.
|
||||||
|
local d=UTILS.VecDist3D(ip, self.vec3)
|
||||||
|
|
||||||
|
-- Output.
|
||||||
|
self:I(self.lid..string.format("FF d(ip, vec3)=%.3f meters", d))
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Safe impact vec3.
|
||||||
|
self.impactVec3=ip or self.vec3
|
||||||
|
|
||||||
|
-- Safe impact coordinate.
|
||||||
|
self.impactCoord=COORDINATE:NewFromVec3(self.vec3)
|
||||||
|
|
||||||
|
-- Mark impact point on F10 map.
|
||||||
|
if self.impactMark then
|
||||||
|
self.impactCoord:MarkToAll(string.format("Impact point of weapon %s\ntype=%s\nlauncher=%s", self.name, self.typeName, self.launcherName))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Smoke on impact point.
|
||||||
|
if self.impactSmoke then
|
||||||
|
self.impactCoord:Smoke(self.impactSmokeColor)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Call callback function.
|
||||||
|
if self.impactFunc then
|
||||||
|
self.impactFunc(self, unpack(self.impactArg or {}))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Stop tracking by returning nil below.
|
||||||
|
self.tracking=false
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return next time the function is called or nil to stop the scheduler.
|
||||||
|
if self.tracking then
|
||||||
|
if self.dtTrack and self.dtTrack>0.001 then
|
||||||
|
return time+self.dtTrack
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Compute estimated intercept/impact point (IP) based on last known position and direction.
|
||||||
|
-- @param #WEAPON self
|
||||||
|
-- @param #number Distance Distance in meters. Default 50 m.
|
||||||
|
-- @return DCS#Vec3 Estimated intercept/impact point. Can also return `nil`, if no IP can be determined.
|
||||||
|
function WEAPON:_GetIP(Distance)
|
||||||
|
|
||||||
|
Distance=Distance or 50
|
||||||
|
|
||||||
|
local ip=nil --DCS#Vec3
|
||||||
|
|
||||||
|
if Distance>0 and self.pos3 then
|
||||||
|
|
||||||
|
-- Get intercept point from position (p) and direction (x) in 20 meters.
|
||||||
|
ip = land.getIP(self.pos3.p, self.pos3.x, Distance or 20) --DCS#Vec3
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return ip
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
@@ -34,6 +34,7 @@ Core/MarkerOps_Base.lua
|
|||||||
Core/Astar.lua
|
Core/Astar.lua
|
||||||
Core/Condition.lua
|
Core/Condition.lua
|
||||||
Core/TextAndSound.lua
|
Core/TextAndSound.lua
|
||||||
|
Core/Pathline.lua
|
||||||
|
|
||||||
Wrapper/Object.lua
|
Wrapper/Object.lua
|
||||||
Wrapper/Identifiable.lua
|
Wrapper/Identifiable.lua
|
||||||
@@ -46,6 +47,8 @@ Wrapper/Static.lua
|
|||||||
Wrapper/Airbase.lua
|
Wrapper/Airbase.lua
|
||||||
Wrapper/Scenery.lua
|
Wrapper/Scenery.lua
|
||||||
Wrapper/Marker.lua
|
Wrapper/Marker.lua
|
||||||
|
Wrapper/Weapon.lua
|
||||||
|
Wrapper/Net.lua
|
||||||
|
|
||||||
Cargo/Cargo.lua
|
Cargo/Cargo.lua
|
||||||
Cargo/CargoUnit.lua
|
Cargo/CargoUnit.lua
|
||||||
|
|||||||
Reference in New Issue
Block a user