diff --git a/Moose Development/Moose/Core/Database.lua b/Moose Development/Moose/Core/Database.lua index 5997f05ca..0628a1e37 100644 --- a/Moose Development/Moose/Core/Database.lua +++ b/Moose Development/Moose/Core/Database.lua @@ -298,24 +298,81 @@ do -- Zones -- @return #DATABASE self function DATABASE:_RegisterZones() - for ZoneID, ZoneData in pairs( env.mission.triggers.zones ) do + for ZoneID, ZoneData in pairs(env.mission.triggers.zones) do local ZoneName = ZoneData.name + + -- Color + local color=ZoneData.color or {1, 0, 0, 0.15} + + -- Create new Zone + local Zone=nil --Core.Zone#ZONE_BASE + + if ZoneData.type==0 then + + --- + -- Circular zone + --- + + self:I(string.format("Register ZONE: %s (Circular)", ZoneName)) + + Zone=ZONE:New(ZoneName) + + else - self:I( { "Register ZONE:", Name = ZoneName } ) - local Zone = ZONE:New( ZoneName ) - self.ZONENAMES[ZoneName] = ZoneName - self:AddZone( ZoneName, Zone ) + --- + -- Quad-point zone + --- + + self:I(string.format("Register ZONE: %s (Polygon, Quad)", ZoneName)) + + Zone=ZONE_POLYGON_BASE:New(ZoneName, ZoneData.verticies) + + for i,vec2 in pairs(ZoneData.verticies) do + local coord=COORDINATE:NewFromVec2(vec2) + coord:MarkToAll(string.format("%s Point %d", ZoneName, i)) + end + + end + + if Zone then + + -- Debug output. + --self:I({"Register ZONE: %s (", Name = ZoneName}) + + Zone.Color=color + + + -- Store in DB. + self.ZONENAMES[ZoneName] = ZoneName + + -- Add zone. + self:AddZone(ZoneName, Zone) + + end + end + -- Polygon zones defined by late activated groups. for ZoneGroupName, ZoneGroup in pairs( self.GROUPS ) do if ZoneGroupName:match("#ZONE_POLYGON") then + local ZoneName1 = ZoneGroupName:match("(.*)#ZONE_POLYGON") local ZoneName2 = ZoneGroupName:match(".*#ZONE_POLYGON(.*)") local ZoneName = ZoneName1 .. ( ZoneName2 or "" ) - self:I( { "Register ZONE_POLYGON:", Name = ZoneName } ) + -- Debug output + self:I(string.format("Register ZONE: %s (Polygon)", ZoneName)) + + -- Create a new polygon zone. local Zone_Polygon = ZONE_POLYGON:New( ZoneName, ZoneGroup ) + + -- Set color. + Zone_Polygon:SetColor({1, 0, 0}, 0.15) + + -- Store name in DB. self.ZONENAMES[ZoneName] = ZoneName + + -- Add zone to DB. self:AddZone( ZoneName, Zone_Polygon ) end end diff --git a/Moose Development/Moose/Core/Event.lua b/Moose Development/Moose/Core/Event.lua index 9d10bdbbc..02dab0afc 100644 --- a/Moose Development/Moose/Core/Event.lua +++ b/Moose Development/Moose/Core/Event.lua @@ -1,98 +1,98 @@ --- **Core** - Models DCS event dispatching using a publish-subscribe model. --- +-- -- === --- +-- -- ## Features: --- +-- -- * Capture DCS events and dispatch them to the subscribed objects. -- * Generate DCS events to the subscribed objects from within the code. --- +-- -- === --- +-- -- # Event Handling Overview --- +-- -- ![Objects](..\Presentations\EVENT\Dia2.JPG) --- +-- -- Within a running mission, various DCS events occur. Units are dynamically created, crash, die, shoot stuff, get hit etc. -- This module provides a mechanism to dispatch those events occuring within your running mission, to the different objects orchestrating your mission. --- +-- -- ![Objects](..\Presentations\EVENT\Dia3.JPG) --- +-- -- Objects can subscribe to different events. The Event dispatcher will publish the received DCS events to the subscribed MOOSE objects, in a specified order. -- In this way, the subscribed MOOSE objects are kept in sync with your evolving running mission. --- +-- -- ## 1. Event Dispatching --- +-- -- ![Objects](..\Presentations\EVENT\Dia4.JPG) --- --- The _EVENTDISPATCHER object is automatically created within MOOSE, --- and handles the dispatching of DCS Events occurring --- in the simulator to the subscribed objects +-- +-- The _EVENTDISPATCHER object is automatically created within MOOSE, +-- and handles the dispatching of DCS Events occurring +-- in the simulator to the subscribed objects -- in the correct processing order. -- -- ![Objects](..\Presentations\EVENT\Dia5.JPG) --- +-- -- There are 5 levels of kind of objects that the _EVENTDISPATCHER services: --- +-- -- * _DATABASE object: The core of the MOOSE objects. Any object that is created, deleted or updated, is done in this database. -- * SET_ derived classes: Subsets of the _DATABASE object. These subsets are updated by the _EVENTDISPATCHER as the second priority. -- * UNIT objects: UNIT objects can subscribe to DCS events. Each DCS event will be directly published to teh subscribed UNIT object. -- * GROUP objects: GROUP objects can subscribe to DCS events. Each DCS event will be directly published to the subscribed GROUP object. -- * Any other object: Various other objects can subscribe to DCS events. Each DCS event triggered will be published to each subscribed object. --- +-- -- ![Objects](..\Presentations\EVENT\Dia6.JPG) --- +-- -- For most DCS events, the above order of updating will be followed. --- +-- -- ![Objects](..\Presentations\EVENT\Dia7.JPG) --- +-- -- But for some DCS events, the publishing order is reversed. This is due to the fact that objects need to be **erased** instead of added. --- +-- -- # 2. Event Handling --- +-- -- ![Objects](..\Presentations\EVENT\Dia8.JPG) --- +-- -- The actual event subscribing and handling is not facilitated through the _EVENTDISPATCHER, but it is done through the @{BASE} class, @{UNIT} class and @{GROUP} class. -- The _EVENTDISPATCHER is a component that is quietly working in the background of MOOSE. --- +-- -- ![Objects](..\Presentations\EVENT\Dia9.JPG) --- --- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator, +-- +-- The BASE class provides methods to catch DCS Events. These are events that are triggered from within the DCS simulator, -- and handled through lua scripting. MOOSE provides an encapsulation to handle these events more efficiently. --- +-- -- ## 2.1. Subscribe to / Unsubscribe from DCS Events. --- +-- -- At first, the mission designer will need to **Subscribe** to a specific DCS event for the class. -- So, when the DCS event occurs, the class will be notified of that event. -- There are two functions which you use to subscribe to or unsubscribe from an event. --- +-- -- * @{Core.Base#BASE.HandleEvent}(): Subscribe to a DCS Event. -- * @{Core.Base#BASE.UnHandleEvent}(): Unsubscribe from a DCS Event. --- +-- -- Note that for a UNIT, the event will be handled **for that UNIT only**! -- Note that for a GROUP, the event will be handled **for all the UNITs in that GROUP only**! --- +-- -- For all objects of other classes, the subscribed events will be handled for **all UNITs within the Mission**! --- So if a UNIT within the mission has the subscribed event for that object, +-- So if a UNIT within the mission has the subscribed event for that object, -- then the object event handler will receive the event for that UNIT! --- +-- -- ## 2.2 Event Handling of DCS Events --- +-- -- Once the class is subscribed to the event, an **Event Handling** method on the object or class needs to be written that will be called -- when the DCS event occurs. The Event Handling method receives an @{Core.Event#EVENTDATA} structure, which contains a lot of information -- about the event that occurred. --- --- Find below an example of the prototype how to write an event handling function for two units: +-- +-- Find below an example of the prototype how to write an event handling function for two units: -- -- local Tank1 = UNIT:FindByName( "Tank A" ) -- local Tank2 = UNIT:FindByName( "Tank B" ) --- +-- -- -- Here we subscribe to the Dead events. So, if one of these tanks dies, the Tank1 or Tank2 objects will be notified. -- Tank1:HandleEvent( EVENTS.Dead ) -- Tank2:HandleEvent( EVENTS.Dead ) --- +-- -- --- This function is an Event Handling function that will be called when Tank1 is Dead. --- -- @param Wrapper.Unit#UNIT self +-- -- @param Wrapper.Unit#UNIT self -- -- @param Core.Event#EVENTDATA EventData -- function Tank1:OnEventDead( EventData ) -- @@ -100,73 +100,73 @@ -- end -- -- --- This function is an Event Handling function that will be called when Tank2 is Dead. --- -- @param Wrapper.Unit#UNIT self +-- -- @param Wrapper.Unit#UNIT self -- -- @param Core.Event#EVENTDATA EventData -- function Tank2:OnEventDead( EventData ) -- -- self:SmokeBlue() -- end --- +-- -- ## 2.3 Event Handling methods that are automatically called upon subscribed DCS events. --- +-- -- ![Objects](..\Presentations\EVENT\Dia10.JPG) --- +-- -- The following list outlines which EVENTS item in the structure corresponds to which Event Handling method. -- Always ensure that your event handling methods align with the events being subscribed to, or nothing will be executed. --- +-- -- # 3. EVENTS type --- --- The EVENTS structure contains names for all the different DCS events that objects can subscribe to using the +-- +-- The EVENTS structure contains names for all the different DCS events that objects can subscribe to using the -- @{Core.Base#BASE.HandleEvent}() method. --- +-- -- # 4. EVENTDATA type --- --- The @{Core.Event#EVENTDATA} structure contains all the fields that are populated with event information before +-- +-- The @{Core.Event#EVENTDATA} structure contains all the fields that are populated with event information before -- an Event Handler method is being called by the event dispatcher. -- The Event Handler received the EVENTDATA object as a parameter, and can be used to investigate further the different events. -- There are basically 4 main categories of information stored in the EVENTDATA structure: --- +-- -- * Initiator Unit data: Several fields documenting the initiator unit related to the event. -- * Target Unit data: Several fields documenting the target unit related to the event. -- * Weapon data: Certain events populate weapon information. -- * Place data: Certain events populate place information. --- +-- -- --- This function is an Event Handling function that will be called when Tank1 is Dead. -- -- EventData is an EVENTDATA structure. -- -- We use the EventData.IniUnit to smoke the tank Green. --- -- @param Wrapper.Unit#UNIT self +-- -- @param Wrapper.Unit#UNIT self -- -- @param Core.Event#EVENTDATA EventData -- function Tank1:OnEventDead( EventData ) -- -- EventData.IniUnit:SmokeGreen() -- end --- --- +-- +-- -- Find below an overview which events populate which information categories: --- +-- -- ![Objects](..\Presentations\EVENT\Dia14.JPG) --- --- **IMPORTANT NOTE:** Some events can involve not just UNIT objects, but also STATIC objects!!! +-- +-- **IMPORTANT NOTE:** Some events can involve not just UNIT objects, but also STATIC objects!!! -- In that case the initiator or target unit fields will refer to a STATIC object! -- In case a STATIC object is involved, the documentation indicates which fields will and won't not be populated. -- The fields **IniObjectCategory** and **TgtObjectCategory** contain the indicator which **kind of object is involved** in the event. -- You can use the enumerator **Object.Category.UNIT** and **Object.Category.STATIC** to check on IniObjectCategory and TgtObjectCategory. -- Example code snippet: --- +-- -- if Event.IniObjectCategory == Object.Category.UNIT then -- ... -- end -- if Event.IniObjectCategory == Object.Category.STATIC then -- ... --- end --- +-- end +-- -- When a static object is involved in the event, the Group and Player fields won't be populated. --- +-- -- === --- +-- -- ### Author: **FlightControl** --- ### Contributions: --- +-- ### Contributions: +-- -- === -- -- @module Core.Event @@ -236,11 +236,11 @@ EVENTS = { RemoveUnit = world.event.S_EVENT_REMOVE_UNIT, PlayerEnterAircraft = world.event.S_EVENT_PLAYER_ENTER_AIRCRAFT, -- Added with DCS 2.5.6 - DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier - Kill = world.event.S_EVENT_KILL or -1, - Score = world.event.S_EVENT_SCORE or -1, - UnitLost = world.event.S_EVENT_UNIT_LOST or -1, - LandingAfterEjection = world.event.S_EVENT_LANDING_AFTER_EJECTION or -1, + DetailedFailure = world.event.S_EVENT_DETAILED_FAILURE or -1, --We set this to -1 for backward compatibility to DCS 2.5.5 and earlier + Kill = world.event.S_EVENT_KILL or -1, + Score = world.event.S_EVENT_SCORE or -1, + UnitLost = world.event.S_EVENT_UNIT_LOST or -1, + LandingAfterEjection = world.event.S_EVENT_LANDING_AFTER_EJECTION or -1, -- Added with DCS 2.7.0 ParatrooperLanding = world.event.S_EVENT_PARATROOPER_LENDING or -1, DiscardChairAfterEjection = world.event.S_EVENT_DISCARD_CHAIR_AFTER_EJECTION or -1, @@ -252,13 +252,13 @@ EVENTS = { --- The Event structure -- Note that at the beginning of each field description, there is an indication which field will be populated depending on the object type involved in the Event: --- +-- -- * A (Object.Category.)UNIT : A UNIT object type is involved in the Event. -- * A (Object.Category.)STATIC : A STATIC object type is involved in the Event. --- +-- -- @type EVENTDATA -- @field #number id The identifier of the event. --- +-- -- @field DCS#Unit initiator (UNIT/STATIC/SCENERY) The initiating @{DCS#Unit} or @{DCS#StaticObject}. -- @field DCS#Object.Category IniObjectCategory (UNIT/STATIC/SCENERY) The initiator object category ( Object.Category.UNIT or Object.Category.STATIC ). -- @field DCS#Unit IniDCSUnit (UNIT/STATIC) The initiating @{DCS#Unit} or @{DCS#StaticObject}. @@ -273,7 +273,7 @@ EVENTS = { -- @field DCS#coalition.side IniCoalition (UNIT) The coalition of the initiator. -- @field DCS#Unit.Category IniCategory (UNIT) The category of the initiator. -- @field #string IniTypeName (UNIT) The type name of the initiator. --- +-- -- @field DCS#Unit target (UNIT/STATIC) The target @{DCS#Unit} or @{DCS#StaticObject}. -- @field DCS#Object.Category TgtObjectCategory (UNIT/STATIC) The target object category ( Object.Category.UNIT or Object.Category.STATIC ). -- @field DCS#Unit TgtDCSUnit (UNIT/STATIC) The target @{DCS#Unit} or @{DCS#StaticObject}. @@ -288,19 +288,19 @@ EVENTS = { -- @field DCS#coalition.side TgtCoalition (UNIT) The coalition of the target. -- @field DCS#Unit.Category TgtCategory (UNIT) The category of the target. -- @field #string TgtTypeName (UNIT) The type name of the target. --- +-- -- @field DCS#Airbase place The @{DCS#Airbase} -- @field Wrapper.Airbase#AIRBASE Place The MOOSE airbase object. -- @field #string PlaceName The name of the airbase. --- +-- -- @field #table weapon The weapon used during the event. -- @field #table Weapon -- @field #string WeaponName Name of the weapon. -- @field DCS#Unit WeaponTgtDCSUnit Target DCS unit of the weapon. --- +-- -- @field Cargo.Cargo#CARGO Cargo The cargo object. -- @field #string CargoName The name of the cargo object. --- +-- -- @field Core.ZONE#ZONE Zone The zone object. -- @field #string ZoneName The name of the zone. @@ -311,255 +311,255 @@ local _EVENTMETA = { Order = 1, Side = "I", Event = "OnEventShot", - Text = "S_EVENT_SHOT" + Text = "S_EVENT_SHOT" }, [world.event.S_EVENT_HIT] = { Order = 1, Side = "T", Event = "OnEventHit", - Text = "S_EVENT_HIT" + Text = "S_EVENT_HIT" }, [world.event.S_EVENT_TAKEOFF] = { Order = 1, Side = "I", Event = "OnEventTakeoff", - Text = "S_EVENT_TAKEOFF" + Text = "S_EVENT_TAKEOFF" }, [world.event.S_EVENT_LAND] = { Order = 1, Side = "I", Event = "OnEventLand", - Text = "S_EVENT_LAND" + Text = "S_EVENT_LAND" }, [world.event.S_EVENT_CRASH] = { Order = -1, Side = "I", Event = "OnEventCrash", - Text = "S_EVENT_CRASH" + Text = "S_EVENT_CRASH" }, [world.event.S_EVENT_EJECTION] = { Order = 1, Side = "I", Event = "OnEventEjection", - Text = "S_EVENT_EJECTION" + Text = "S_EVENT_EJECTION" }, [world.event.S_EVENT_REFUELING] = { Order = 1, Side = "I", Event = "OnEventRefueling", - Text = "S_EVENT_REFUELING" + Text = "S_EVENT_REFUELING" }, [world.event.S_EVENT_DEAD] = { Order = -1, Side = "I", Event = "OnEventDead", - Text = "S_EVENT_DEAD" + Text = "S_EVENT_DEAD" }, [world.event.S_EVENT_PILOT_DEAD] = { Order = 1, Side = "I", Event = "OnEventPilotDead", - Text = "S_EVENT_PILOT_DEAD" + Text = "S_EVENT_PILOT_DEAD" }, [world.event.S_EVENT_BASE_CAPTURED] = { Order = 1, Side = "I", Event = "OnEventBaseCaptured", - Text = "S_EVENT_BASE_CAPTURED" + Text = "S_EVENT_BASE_CAPTURED" }, [world.event.S_EVENT_MISSION_START] = { Order = 1, Side = "N", Event = "OnEventMissionStart", - Text = "S_EVENT_MISSION_START" + Text = "S_EVENT_MISSION_START" }, [world.event.S_EVENT_MISSION_END] = { Order = 1, Side = "N", Event = "OnEventMissionEnd", - Text = "S_EVENT_MISSION_END" + Text = "S_EVENT_MISSION_END" }, [world.event.S_EVENT_TOOK_CONTROL] = { Order = 1, Side = "N", Event = "OnEventTookControl", - Text = "S_EVENT_TOOK_CONTROL" + Text = "S_EVENT_TOOK_CONTROL" }, [world.event.S_EVENT_REFUELING_STOP] = { Order = 1, Side = "I", Event = "OnEventRefuelingStop", - Text = "S_EVENT_REFUELING_STOP" + Text = "S_EVENT_REFUELING_STOP" }, [world.event.S_EVENT_BIRTH] = { Order = 1, Side = "I", Event = "OnEventBirth", - Text = "S_EVENT_BIRTH" + Text = "S_EVENT_BIRTH" }, [world.event.S_EVENT_HUMAN_FAILURE] = { Order = 1, Side = "I", Event = "OnEventHumanFailure", - Text = "S_EVENT_HUMAN_FAILURE" + Text = "S_EVENT_HUMAN_FAILURE" }, [world.event.S_EVENT_ENGINE_STARTUP] = { Order = 1, Side = "I", Event = "OnEventEngineStartup", - Text = "S_EVENT_ENGINE_STARTUP" + Text = "S_EVENT_ENGINE_STARTUP" }, [world.event.S_EVENT_ENGINE_SHUTDOWN] = { Order = 1, Side = "I", Event = "OnEventEngineShutdown", - Text = "S_EVENT_ENGINE_SHUTDOWN" + Text = "S_EVENT_ENGINE_SHUTDOWN" }, [world.event.S_EVENT_PLAYER_ENTER_UNIT] = { Order = 1, Side = "I", Event = "OnEventPlayerEnterUnit", - Text = "S_EVENT_PLAYER_ENTER_UNIT" + Text = "S_EVENT_PLAYER_ENTER_UNIT" }, [world.event.S_EVENT_PLAYER_LEAVE_UNIT] = { Order = -1, Side = "I", Event = "OnEventPlayerLeaveUnit", - Text = "S_EVENT_PLAYER_LEAVE_UNIT" + Text = "S_EVENT_PLAYER_LEAVE_UNIT" }, [world.event.S_EVENT_PLAYER_COMMENT] = { Order = 1, Side = "I", Event = "OnEventPlayerComment", - Text = "S_EVENT_PLAYER_COMMENT" + Text = "S_EVENT_PLAYER_COMMENT" }, [world.event.S_EVENT_SHOOTING_START] = { Order = 1, Side = "I", Event = "OnEventShootingStart", - Text = "S_EVENT_SHOOTING_START" + Text = "S_EVENT_SHOOTING_START" }, [world.event.S_EVENT_SHOOTING_END] = { Order = 1, Side = "I", Event = "OnEventShootingEnd", - Text = "S_EVENT_SHOOTING_END" + Text = "S_EVENT_SHOOTING_END" }, [world.event.S_EVENT_MARK_ADDED] = { Order = 1, Side = "I", Event = "OnEventMarkAdded", - Text = "S_EVENT_MARK_ADDED" + Text = "S_EVENT_MARK_ADDED" }, [world.event.S_EVENT_MARK_CHANGE] = { Order = 1, Side = "I", Event = "OnEventMarkChange", - Text = "S_EVENT_MARK_CHANGE" + Text = "S_EVENT_MARK_CHANGE" }, [world.event.S_EVENT_MARK_REMOVED] = { Order = 1, Side = "I", Event = "OnEventMarkRemoved", - Text = "S_EVENT_MARK_REMOVED" + Text = "S_EVENT_MARK_REMOVED" }, [EVENTS.NewCargo] = { Order = 1, Event = "OnEventNewCargo", - Text = "S_EVENT_NEW_CARGO" + Text = "S_EVENT_NEW_CARGO" }, [EVENTS.DeleteCargo] = { Order = 1, Event = "OnEventDeleteCargo", - Text = "S_EVENT_DELETE_CARGO" + Text = "S_EVENT_DELETE_CARGO" }, [EVENTS.NewZone] = { Order = 1, Event = "OnEventNewZone", - Text = "S_EVENT_NEW_ZONE" + Text = "S_EVENT_NEW_ZONE" }, [EVENTS.DeleteZone] = { Order = 1, Event = "OnEventDeleteZone", - Text = "S_EVENT_DELETE_ZONE" + Text = "S_EVENT_DELETE_ZONE" }, [EVENTS.NewZoneGoal] = { Order = 1, Event = "OnEventNewZoneGoal", - Text = "S_EVENT_NEW_ZONE_GOAL" + Text = "S_EVENT_NEW_ZONE_GOAL" }, [EVENTS.DeleteZoneGoal] = { Order = 1, Event = "OnEventDeleteZoneGoal", - Text = "S_EVENT_DELETE_ZONE_GOAL" + Text = "S_EVENT_DELETE_ZONE_GOAL" }, [EVENTS.RemoveUnit] = { Order = -1, Event = "OnEventRemoveUnit", - Text = "S_EVENT_REMOVE_UNIT" + Text = "S_EVENT_REMOVE_UNIT" }, [EVENTS.PlayerEnterAircraft] = { Order = 1, Event = "OnEventPlayerEnterAircraft", - Text = "S_EVENT_PLAYER_ENTER_AIRCRAFT" - }, + Text = "S_EVENT_PLAYER_ENTER_AIRCRAFT" + }, -- Added with DCS 2.5.6 [EVENTS.DetailedFailure] = { Order = 1, Event = "OnEventDetailedFailure", - Text = "S_EVENT_DETAILED_FAILURE" + Text = "S_EVENT_DETAILED_FAILURE" }, [EVENTS.Kill] = { Order = 1, Event = "OnEventKill", - Text = "S_EVENT_KILL" + Text = "S_EVENT_KILL" }, [EVENTS.Score] = { Order = 1, Event = "OnEventScore", - Text = "S_EVENT_SCORE" + Text = "S_EVENT_SCORE" }, [EVENTS.UnitLost] = { Order = 1, Event = "OnEventUnitLost", - Text = "S_EVENT_UNIT_LOST" + Text = "S_EVENT_UNIT_LOST" }, [EVENTS.LandingAfterEjection] = { Order = 1, Event = "OnEventLandingAfterEjection", - Text = "S_EVENT_LANDING_AFTER_EJECTION" + Text = "S_EVENT_LANDING_AFTER_EJECTION" }, -- Added with DCS 2.7.0 [EVENTS.ParatrooperLanding] = { Order = 1, Event = "OnEventParatrooperLanding", - Text = "S_EVENT_PARATROOPER_LENDING" + Text = "S_EVENT_PARATROOPER_LENDING" }, [EVENTS.DiscardChairAfterEjection] = { Order = 1, Event = "OnEventDiscardChairAfterEjection", - Text = "S_EVENT_DISCARD_CHAIR_AFTER_EJECTION" + Text = "S_EVENT_DISCARD_CHAIR_AFTER_EJECTION" }, [EVENTS.WeaponAdd] = { Order = 1, Event = "OnEventWeaponAdd", - Text = "S_EVENT_WEAPON_ADD" + Text = "S_EVENT_WEAPON_ADD" }, [EVENTS.TriggerZone] = { Order = 1, Event = "OnEventTriggerZone", - Text = "S_EVENT_TRIGGER_ZONE" + Text = "S_EVENT_TRIGGER_ZONE" }, [EVENTS.LandingQualityMark] = { Order = 1, Event = "OnEventLandingQualityMark", - Text = "S_EVENT_LANDING_QUALITYMARK" + Text = "S_EVENT_LANDING_QUALITYMARK" }, [EVENTS.BDA] = { Order = 1, Event = "OnEventBDA", - Text = "S_EVENT_BDA" - }, + Text = "S_EVENT_BDA" + }, } @@ -574,10 +574,10 @@ function EVENT:New() -- Inherit base. local self = BASE:Inherit( self, BASE:New() ) - + -- Add world event handler. self.EventHandler = world.addEventHandler(self) - + return self end @@ -590,22 +590,22 @@ end function EVENT:Init( EventID, EventClass ) self:F3( { _EVENTMETA[EventID].Text, EventClass } ) - if not self.Events[EventID] then + if not self.Events[EventID] then -- Create a WEAK table to ensure that the garbage collector is cleaning the event links when the object usage is cleaned. self.Events[EventID] = {} end - + -- Each event has a subtable of EventClasses, ordered by EventPriority. local EventPriority = EventClass:GetEventPriority() - + if not self.Events[EventID][EventPriority] then self.Events[EventID][EventPriority] = setmetatable( {}, { __mode = "k" } ) - end + end if not self.Events[EventID][EventPriority][EventClass] then self.Events[EventID][EventPriority][EventClass] = {} end - + return self.Events[EventID][EventPriority][EventClass] end @@ -625,11 +625,11 @@ function EVENT:RemoveEvent( EventClass, EventID ) -- Events. self.Events = self.Events or {} self.Events[EventID] = self.Events[EventID] or {} - self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {} - + self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {} + -- Remove self.Events[EventID][EventPriority][EventClass] = nil - + return self end @@ -643,7 +643,7 @@ function EVENT:Reset( EventObject ) --R2.1 self:F( { "Resetting subscriptions for class: ", EventObject:GetClassNameAndID() } ) local EventPriority = EventObject:GetEventPriority() - + for EventID, EventData in pairs( self.Events ) do if self.EventsDead then if self.EventsDead[EventID] then @@ -665,14 +665,14 @@ end function EVENT:RemoveAll(EventClass) local EventClassName = EventClass:GetClassNameAndID() - + -- Get Event prio. local EventPriority = EventClass:GetEventPriority() - + for EventID, EventData in pairs( self.Events ) do self.Events[EventID][EventPriority][EventClass] = nil end - + return self end @@ -705,7 +705,7 @@ function EVENT:OnEventGeneric( EventFunction, EventClass, EventID ) local EventData = self:Init( EventID, EventClass ) EventData.EventFunction = EventFunction - + return self end @@ -753,12 +753,12 @@ do -- OnBirth -- @return #EVENT self function EVENT:OnBirthForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Birth ) - + return self end - + end do -- OnCrash @@ -771,16 +771,16 @@ do -- OnCrash -- @return #EVENT function EVENT:OnCrashForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Crash ) - + return self end end do -- OnDead - + --- Create an OnDead event handler for a group -- @param #EVENT self -- @param Wrapper.Group#GROUP EventGroup The GROUP object. @@ -789,12 +789,12 @@ do -- OnDead -- @return #EVENT self function EVENT:OnDeadForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Dead ) - + return self end - + end @@ -808,12 +808,12 @@ do -- OnLand -- @return #EVENT self function EVENT:OnLandForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Land ) - + return self end - + end do -- OnTakeOff @@ -826,12 +826,12 @@ do -- OnTakeOff -- @return #EVENT self function EVENT:OnTakeOffForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.Takeoff ) - + return self end - + end do -- OnEngineShutDown @@ -844,12 +844,12 @@ do -- OnEngineShutDown -- @return #EVENT function EVENT:OnEngineShutDownForTemplate( EventTemplate, EventFunction, EventClass ) self:F2( EventTemplate.name ) - + self:OnEventForTemplate( EventTemplate, EventFunction, EventClass, EVENTS.EngineShutdown ) - + return self end - + end do -- Event Creation @@ -859,13 +859,13 @@ do -- Event Creation -- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created. function EVENT:CreateEventNewCargo( Cargo ) self:F( { Cargo } ) - + local Event = { id = EVENTS.NewCargo, time = timer.getTime(), cargo = Cargo, } - + world.onEvent( Event ) end @@ -874,13 +874,13 @@ do -- Event Creation -- @param AI.AI_Cargo#AI_CARGO Cargo The Cargo created. function EVENT:CreateEventDeleteCargo( Cargo ) self:F( { Cargo } ) - + local Event = { id = EVENTS.DeleteCargo, time = timer.getTime(), cargo = Cargo, } - + world.onEvent( Event ) end @@ -889,13 +889,13 @@ do -- Event Creation -- @param Core.Zone#ZONE_BASE Zone The Zone created. function EVENT:CreateEventNewZone( Zone ) self:F( { Zone } ) - + local Event = { id = EVENTS.NewZone, time = timer.getTime(), zone = Zone, } - + world.onEvent( Event ) end @@ -904,13 +904,13 @@ do -- Event Creation -- @param Core.Zone#ZONE_BASE Zone The Zone created. function EVENT:CreateEventDeleteZone( Zone ) self:F( { Zone } ) - + local Event = { id = EVENTS.DeleteZone, time = timer.getTime(), zone = Zone, } - + world.onEvent( Event ) end @@ -919,13 +919,13 @@ do -- Event Creation -- @param Core.Functional#ZONE_GOAL ZoneGoal The ZoneGoal created. function EVENT:CreateEventNewZoneGoal( ZoneGoal ) self:F( { ZoneGoal } ) - + local Event = { id = EVENTS.NewZoneGoal, time = timer.getTime(), ZoneGoal = ZoneGoal, } - + world.onEvent( Event ) end @@ -935,13 +935,13 @@ do -- Event Creation -- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal created. function EVENT:CreateEventDeleteZoneGoal( ZoneGoal ) self:F( { ZoneGoal } ) - + local Event = { id = EVENTS.DeleteZoneGoal, time = timer.getTime(), ZoneGoal = ZoneGoal, } - + world.onEvent( Event ) end @@ -951,30 +951,30 @@ do -- Event Creation -- @param Wrapper.Unit#UNIT PlayerUnit. function EVENT:CreateEventPlayerEnterUnit( PlayerUnit ) self:F( { PlayerUnit } ) - + local Event = { id = EVENTS.PlayerEnterUnit, time = timer.getTime(), initiator = PlayerUnit:GetDCSObject() } - + world.onEvent( Event ) end - + --- Creation of a S_EVENT_PLAYER_ENTER_AIRCRAFT event. -- @param #EVENT self -- @param Wrapper.Unit#UNIT PlayerUnit The aircraft unit the player entered. function EVENT:CreateEventPlayerEnterAircraft( PlayerUnit ) self:F( { PlayerUnit } ) - + local Event = { id = EVENTS.PlayerEnterAircraft, time = timer.getTime(), initiator = PlayerUnit:GetDCSObject() } - + world.onEvent( Event ) - end + end end @@ -989,31 +989,27 @@ function EVENT:onEvent( Event ) if BASE.Debug ~= nil then env.info( debug.traceback() ) end - + return errmsg end -- Get event meta data. local EventMeta = _EVENTMETA[Event.id] - + -- Check if this is a known event? if EventMeta then - - if self and - self.Events and - self.Events[Event.id] and - self.MissionEnd == false and - ( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then - + + if self and self.Events and self.Events[Event.id] and self.MissionEnd==false and (Event.initiator~=nil or (Event.initiator==nil and Event.id~=EVENTS.PlayerLeaveUnit)) then + if Event.id and Event.id == EVENTS.MissionEnd then self.MissionEnd = true end - - if Event.initiator then - + + if Event.initiator then + Event.IniObjectCategory = Event.initiator:getCategory() - + if Event.IniObjectCategory == Object.Category.UNIT then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() @@ -1037,11 +1033,12 @@ function EVENT:onEvent( Event ) Event.IniTypeName = Event.IniDCSUnit:getTypeName() Event.IniCategory = Event.IniDCSUnit:getDesc().category end - + if Event.IniObjectCategory == Object.Category.STATIC then + if Event.id==31 then - --env.info("FF event 31") - -- Event.initiator is a Static object representing the pilot. But getName() error due to DCS bug. + + -- Event.initiator is a Static object representing the pilot. But getName() errors due to DCS bug. Event.IniDCSUnit = Event.initiator local ID=Event.initiator.id_ Event.IniDCSUnitName = string.format("Ejected Pilot ID %s", tostring(ID)) @@ -1067,7 +1064,7 @@ function EVENT:onEvent( Event ) Event.IniTypeName = Event.IniDCSUnit:getTypeName() end end - + if Event.IniObjectCategory == Object.Category.CARGO then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() @@ -1077,7 +1074,7 @@ function EVENT:onEvent( Event ) Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniTypeName = Event.IniDCSUnit:getTypeName() end - + if Event.IniObjectCategory == Object.Category.SCENERY then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() @@ -1086,7 +1083,7 @@ function EVENT:onEvent( Event ) Event.IniCategory = Event.IniDCSUnit:getDesc().category Event.IniTypeName = Event.initiator:isExist() and Event.IniDCSUnit:getTypeName() or "SCENERY" -- TODO: Bug fix for 2.1! end - + if Event.IniObjectCategory == Object.Category.BASE then Event.IniDCSUnit = Event.initiator Event.IniDCSUnitName = Event.IniDCSUnit:getName() @@ -1094,15 +1091,15 @@ function EVENT:onEvent( Event ) Event.IniUnit = AIRBASE:FindByName(Event.IniDCSUnitName) Event.IniCoalition = Event.IniDCSUnit:getCoalition() Event.IniCategory = Event.IniDCSUnit:getDesc().category - Event.IniTypeName = Event.IniDCSUnit:getTypeName() + Event.IniTypeName = Event.IniDCSUnit:getTypeName() end end - + if Event.target then - + Event.TgtObjectCategory = Event.target:getCategory() - - if Event.TgtObjectCategory == Object.Category.UNIT then + + if Event.TgtObjectCategory == Object.Category.UNIT then Event.TgtDCSUnit = Event.target Event.TgtDCSGroup = Event.TgtDCSUnit:getGroup() Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() @@ -1121,38 +1118,37 @@ function EVENT:onEvent( Event ) Event.TgtCategory = Event.TgtDCSUnit:getDesc().category Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() end - + if Event.TgtObjectCategory == Object.Category.STATIC then - BASE:T({StaticTgtEvent = Event.id}) -- get base data - Event.TgtDCSUnit = Event.target - if Event.target:isExist() and Event.id ~= 33 then -- leave out ejected seat object - Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() + Event.TgtDCSUnit = Event.target + if Event.target:isExist() and Event.id ~= 33 then -- leave out ejected seat object + Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() + Event.TgtUnitName = Event.TgtDCSUnitName + Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false ) + Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() + Event.TgtCategory = Event.TgtDCSUnit:getDesc().category + Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() + else + Event.TgtDCSUnitName = string.format("No target object for Event ID %s", tostring(Event.id)) + Event.TgtUnitName = Event.TgtDCSUnitName + Event.TgtUnit = nil + Event.TgtCoalition = 0 + Event.TgtCategory = 0 + if Event.id == 6 then + Event.TgtTypeName = "Ejected Pilot" + Event.TgtDCSUnitName = string.format("Ejected Pilot ID %s", tostring(Event.IniDCSUnitName)) + Event.TgtUnitName = Event.TgtDCSUnitName + elseif Event.id == 33 then + Event.TgtTypeName = "Ejection Seat" + Event.TgtDCSUnitName = string.format("Ejection Seat ID %s", tostring(Event.IniDCSUnitName)) Event.TgtUnitName = Event.TgtDCSUnitName - Event.TgtUnit = STATIC:FindByName( Event.TgtDCSUnitName, false ) - Event.TgtCoalition = Event.TgtDCSUnit:getCoalition() - Event.TgtCategory = Event.TgtDCSUnit:getDesc().category - Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() else - Event.TgtDCSUnitName = string.format("No target object for Event ID %s", tostring(Event.id)) - Event.TgtUnitName = Event.TgtDCSUnitName - Event.TgtUnit = nil - Event.TgtCoalition = 0 - Event.TgtCategory = 0 - if Event.id == 6 then - Event.TgtTypeName = "Ejected Pilot" - Event.TgtDCSUnitName = string.format("Ejected Pilot ID %s", tostring(Event.IniDCSUnitName)) - Event.TgtUnitName = Event.TgtDCSUnitName - elseif Event.id == 33 then - Event.TgtTypeName = "Ejection Seat" - Event.TgtDCSUnitName = string.format("Ejection Seat ID %s", tostring(Event.IniDCSUnitName)) - Event.TgtUnitName = Event.TgtDCSUnitName - else - Event.TgtTypeName = "Static" - end - end + Event.TgtTypeName = "Static" + end + end end - + if Event.TgtObjectCategory == Object.Category.SCENERY then Event.TgtDCSUnit = Event.target Event.TgtDCSUnitName = Event.TgtDCSUnit:getName() @@ -1162,7 +1158,7 @@ function EVENT:onEvent( Event ) Event.TgtTypeName = Event.TgtDCSUnit:getTypeName() end end - + if Event.weapon then Event.Weapon = Event.weapon Event.WeaponName = Event.Weapon:getTypeName() @@ -1173,20 +1169,20 @@ function EVENT:onEvent( Event ) Event.WeaponTypeName = Event.WeaponUNIT and Event.Weapon:getTypeName() --Event.WeaponTgtDCSUnit = Event.Weapon:getTarget() end - - -- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase. - if Event.place then + + -- Place should be given for takeoff and landing events as well as base captured. It should be a DCS airbase. + if Event.place then if Event.id==EVENTS.LandingAfterEjection then -- Place is here the UNIT of which the pilot ejected. --local name=Event.place:getName() -- This returns a DCS error "Airbase doesn't exit" :( -- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground. --Event.Place=UNIT:Find(Event.place) - else + else Event.Place=AIRBASE:Find(Event.place) Event.PlaceName=Event.Place:GetName() end end - + -- Mark points. if Event.idx then Event.MarkID=Event.idx @@ -1196,80 +1192,80 @@ function EVENT:onEvent( Event ) Event.MarkCoalition=Event.coalition Event.MarkGroupID = Event.groupID end - + if Event.cargo then Event.Cargo = Event.cargo Event.CargoName = Event.cargo.Name end - + if Event.zone then Event.Zone = Event.zone Event.ZoneName = Event.zone.ZoneName end - + local PriorityOrder = EventMeta.Order local PriorityBegin = PriorityOrder == -1 and 5 or 1 local PriorityEnd = PriorityOrder == -1 and 1 or 5 - + if Event.IniObjectCategory ~= Object.Category.STATIC then self:F( { EventMeta.Text, Event, Event.IniDCSUnitName, Event.TgtDCSUnitName, PriorityOrder } ) end - + for EventPriority = PriorityBegin, PriorityEnd, PriorityOrder do - + if self.Events[Event.id][EventPriority] then - + -- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called. for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do - + --if Event.IniObjectCategory ~= Object.Category.STATIC then -- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } ) --end - + Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName ) Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName ) - + -- If the EventData is for a UNIT, the call directly the EventClass EventFunction for that UNIT. if EventData.EventUnit then - + -- So now the EventClass must be a UNIT class!!! We check if it is still "Alive". if EventClass:IsAlive() or - Event.id == EVENTS.PlayerEnterUnit or - Event.id == EVENTS.Crash or - Event.id == EVENTS.Dead or + Event.id == EVENTS.PlayerEnterUnit or + Event.id == EVENTS.Crash or + Event.id == EVENTS.Dead or Event.id == EVENTS.RemoveUnit then - + local UnitName = EventClass:GetName() - - if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or + + if ( EventMeta.Side == "I" and UnitName == Event.IniDCSUnitName ) or ( EventMeta.Side == "T" and UnitName == Event.TgtDCSUnitName ) then - + -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventFunction then - + if Event.IniObjectCategory ~= 3 then self:F( { "Calling EventFunction for UNIT ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) end - - local Result, Value = xpcall( - function() - return EventData.EventFunction( EventClass, Event ) + + local Result, Value = xpcall( + function() + return EventData.EventFunction( EventClass, Event ) end, ErrorHandler ) - + else - + -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. local EventFunction = EventClass[ EventMeta.Event ] if EventFunction and type( EventFunction ) == "function" then - + -- Now call the default event function. if Event.IniObjectCategory ~= 3 then self:F( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event ) + + local Result, Value = xpcall( + function() + return EventFunction( EventClass, Event ) end, ErrorHandler ) end end @@ -1278,102 +1274,102 @@ function EVENT:onEvent( Event ) -- The EventClass is not alive anymore, we remove it from the EventHandlers... self:RemoveEvent( EventClass, Event.id ) end - + else - + --- If the EventData is for a GROUP, the call directly the EventClass EventFunction for the UNIT in that GROUP. if EventData.EventGroup then - + -- So now the EventClass must be a GROUP class!!! We check if it is still "Alive". if EventClass:IsAlive() or Event.id == EVENTS.PlayerEnterUnit or Event.id == EVENTS.Crash or Event.id == EVENTS.Dead or Event.id == EVENTS.RemoveUnit then - + -- We can get the name of the EventClass, which is now always a GROUP object. local GroupName = EventClass:GetName() - - if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or + + if ( EventMeta.Side == "I" and GroupName == Event.IniDCSGroupName ) or ( EventMeta.Side == "T" and GroupName == Event.TgtDCSGroupName ) then - + -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventFunction then - + if Event.IniObjectCategory ~= 3 then self:F( { "Calling EventFunction for GROUP ", EventClass:GetClassNameAndID(), ", Unit ", Event.IniUnitName, EventPriority } ) end - - local Result, Value = xpcall( - function() - return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) ) + + local Result, Value = xpcall( + function() + return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) ) end, ErrorHandler ) - + else - + -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. local EventFunction = EventClass[ EventMeta.Event ] if EventFunction and type( EventFunction ) == "function" then - + -- Now call the default event function. if Event.IniObjectCategory ~= 3 then self:F( { "Calling " .. EventMeta.Event .. " for GROUP ", EventClass:GetClassNameAndID(), EventPriority } ) end - - local Result, Value = xpcall( - function() - return EventFunction( EventClass, Event, unpack( EventData.Params ) ) + + local Result, Value = xpcall( + function() + return EventFunction( EventClass, Event, unpack( EventData.Params ) ) end, ErrorHandler ) end end end else -- The EventClass is not alive anymore, we remove it from the EventHandlers... - --self:RemoveEvent( EventClass, Event.id ) + --self:RemoveEvent( EventClass, Event.id ) end else - + -- If the EventData is not bound to a specific unit, then call the EventClass EventFunction. -- Note that here the EventFunction will need to implement and determine the logic for the relevant source- or target unit, or weapon. if not EventData.EventUnit then - + -- First test if a EventFunction is Set, otherwise search for the default function if EventData.EventFunction then - + -- There is an EventFunction defined, so call the EventFunction. if Event.IniObjectCategory ~= 3 then self:F2( { "Calling EventFunction for Class ", EventClass:GetClassNameAndID(), EventPriority } ) - end - local Result, Value = xpcall( - function() - return EventData.EventFunction( EventClass, Event ) + end + local Result, Value = xpcall( + function() + return EventData.EventFunction( EventClass, Event ) end, ErrorHandler ) else - + -- There is no EventFunction defined, so try to find if a default OnEvent function is defined on the object. local EventFunction = EventClass[ EventMeta.Event ] if EventFunction and type( EventFunction ) == "function" then - + -- Now call the default event function. if Event.IniObjectCategory ~= 3 then self:F2( { "Calling " .. EventMeta.Event .. " for Class ", EventClass:GetClassNameAndID(), EventPriority } ) end - - local Result, Value = xpcall( - function() + + local Result, Value = xpcall( + function() local Result, Value = EventFunction( EventClass, Event ) - return Result, Value + return Result, Value end, ErrorHandler ) end end - + end end end end end end - + -- When cargo was deleted, it may probably be because of an S_EVENT_DEAD. -- However, in the loading logic, an S_EVENT_DEAD is also generated after a Destroy() call. -- And this is a problem because it will remove all entries from the SET_CARGOs. @@ -1384,12 +1380,12 @@ function EVENT:onEvent( Event ) Event.Cargo.NoDestroy = nil end else - self:T( { EventMeta.Text, Event } ) + self:T( { EventMeta.Text, Event } ) end else self:E(string.format("WARNING: Could not get EVENTMETA data for event ID=%d! Is this an unknown/new DCS event?", tostring(Event.id))) end - + Event = nil end diff --git a/Moose Development/Moose/Core/Fsm.lua b/Moose Development/Moose/Core/Fsm.lua index 0b49c3449..0ada59e71 100644 --- a/Moose Development/Moose/Core/Fsm.lua +++ b/Moose Development/Moose/Core/Fsm.lua @@ -540,7 +540,7 @@ do -- FSM --- Returns a table with the scores defined. -- @param #FSM self - -- @param #table Scores. + -- @return #table Scores. function FSM:GetScores() return self._Scores or {} end diff --git a/Moose Development/Moose/Core/Point.lua b/Moose Development/Moose/Core/Point.lua index dff81d026..833d976cf 100644 --- a/Moose Development/Moose/Core/Point.lua +++ b/Moose Development/Moose/Core/Point.lua @@ -164,6 +164,7 @@ do -- COORDINATE -- -- * @{#COORDINATE.WaypointAir}(): Build an air route point. -- * @{#COORDINATE.WaypointGround}(): Build a ground route point. + -- * @{#COORDINATE.WaypointNaval}(): Build a naval route point. -- -- Route points can be used in the Route methods of the @{Wrapper.Group#GROUP} class. -- @@ -183,10 +184,18 @@ do -- COORDINATE -- -- ## 9) Coordinate text generation -- - -- -- * @{#COORDINATE.ToStringBR}(): Generates a Bearing & Range text in the format of DDD for DI where DDD is degrees and DI is distance. -- * @{#COORDINATE.ToStringLL}(): Generates a Latutude & Longutude text. -- + -- ## 10) Drawings on F10 map + -- + -- * @{#COORDINATE.CircleToAll}(): Draw a circle on the F10 map. + -- * @{#COORDINATE.LineToAll}(): Draw a line on the F10 map. + -- * @{#COORDINATE.RectToAll}(): Draw a rectangle on the F10 map. + -- * @{#COORDINATE.QuadToAll}(): Draw a shape with four points on the F10 map. + -- * @{#COORDINATE.TextToAll}(): Write some text on the F10 map. + -- * @{#COORDINATE.ArrowToAll}(): Draw an arrow on the F10 map. + -- -- @field #COORDINATE COORDINATE = { ClassName = "COORDINATE", @@ -675,9 +684,9 @@ do -- COORDINATE --- Return a random Coordinate within an Outer Radius and optionally NOT within an Inner Radius of the COORDINATE. -- @param #COORDINATE self - -- @param DCS#Distance OuterRadius - -- @param DCS#Distance InnerRadius - -- @return #COORDINATE + -- @param DCS#Distance OuterRadius Outer radius in meters. + -- @param DCS#Distance InnerRadius Inner radius in meters. + -- @return #COORDINATE self function COORDINATE:GetRandomCoordinateInRadius( OuterRadius, InnerRadius ) self:F2( { OuterRadius, InnerRadius } ) @@ -2039,13 +2048,13 @@ do -- COORDINATE -- @param #COORDINATE self -- @param #COORDINATE Endpoint COORDIANTE to where the line is drawn. -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. - -- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). -- @param #number Alpha Transparency [0,1]. Default 1. + -- @param #number LineType 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 (Optional) Mark is readonly and cannot be removed by users. Default false. -- @param #string Text (Optional) Text displayed when mark is added. Default none. - -- @return #number The resulting Mark ID which is a number. - function COORDINATE:LineToAll(Endpoint, Coalition, LineType, Color, Alpha, ReadOnly, Text) + -- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again. + function COORDINATE:LineToAll(Endpoint, Coalition, Color, Alpha, LineType, ReadOnly, Text) local MarkID = UTILS.GetMarkID() if ReadOnly==nil then ReadOnly=false @@ -2062,18 +2071,17 @@ do -- COORDINATE --- Circle to all. -- Creates a circle on the map with a given radius, color, fill color, and outline. -- @param #COORDINATE self - -- @param #COORDINATE Center COORDIANTE of the center of the circle. -- @param #numberr Radius Radius in meters. Default 1000 m. -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. - -- @param #number LineType Line type: 0=No line, 1=Solid, 2=Dashed, 3=Dotted, 4=Dot dash, 5=Long dash, 6=Two dash. Default 1=Solid. -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). -- @param #number Alpha Transparency [0,1]. Default 1. - -- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red (default). - -- @param #number FillAlpha Transparency [0,1]. Default 0.5. + -- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. + -- @param #number FillAlpha Transparency [0,1]. Default 0.15. + -- @param #number LineType 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 (Optional) Mark is readonly and cannot be removed by users. Default false. -- @param #string Text (Optional) Text displayed when mark is added. Default none. - -- @return #number The resulting Mark ID which is a number. - function COORDINATE:CircleToAll(Radius, Coalition, LineType, Color, Alpha, FillColor, FillAlpha, ReadOnly, Text) + -- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again. + function COORDINATE:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text) local MarkID = UTILS.GetMarkID() if ReadOnly==nil then ReadOnly=false @@ -2084,14 +2092,188 @@ do -- COORDINATE Color=Color or {1,0,0} Color[4]=Alpha or 1.0 LineType=LineType or 1 - FillColor=FillColor or {1,0,0} - FillColor[4]=FillAlpha or 0.5 + FillColor=FillColor or Color + FillColor[4]=FillAlpha or 0.15 trigger.action.circleToAll(Coalition, MarkID, vec3, Radius, Color, FillColor, LineType, ReadOnly, Text or "") return MarkID end end -- Markings + --- Rectangle to all. Creates a rectangle on the map from the COORDINATE in one corner to the end COORDINATE in the opposite corner. + -- Creates a line on the F10 map from one point to another. + -- @param #COORDINATE self + -- @param #COORDINATE Endpoint COORDIANTE in the opposite corner. + -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. + -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). + -- @param #number Alpha Transparency [0,1]. Default 1. + -- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. + -- @param #number FillAlpha Transparency [0,1]. Default 0.15. + -- @param #number LineType 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 (Optional) Mark is readonly and cannot be removed by users. Default false. + -- @param #string Text (Optional) Text displayed when mark is added. Default none. + -- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again. + function COORDINATE:RectToAll(Endpoint, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text) + local MarkID = UTILS.GetMarkID() + if ReadOnly==nil then + ReadOnly=false + end + local vec3=Endpoint:GetVec3() + Coalition=Coalition or -1 + Color=Color or {1,0,0} + Color[4]=Alpha or 1.0 + LineType=LineType or 1 + FillColor=FillColor or Color + FillColor[4]=FillAlpha or 0.15 + trigger.action.rectToAll(Coalition, MarkID, self:GetVec3(), vec3, Color, FillColor, LineType, ReadOnly, Text or "") + return MarkID + end + + --- Creates a shape defined by 4 points on the F10 map. The first point is the current COORDINATE. The remaining three points need to be specified. + -- @param #COORDINATE self + -- @param #COORDINATE Coord2 Second COORDIANTE of the quad shape. + -- @param #COORDINATE Coord3 Third COORDIANTE of the quad shape. + -- @param #COORDINATE Coord4 Fourth COORDIANTE of the quad shape. + -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. + -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). + -- @param #number Alpha Transparency [0,1]. Default 1. + -- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. + -- @param #number FillAlpha Transparency [0,1]. Default 0.15. + -- @param #number LineType 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 (Optional) Mark is readonly and cannot be removed by users. Default false. + -- @param #string Text (Optional) Text displayed when mark is added. Default none. + -- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again. + function COORDINATE:QuadToAll(Coord2, Coord3, Coord4, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text) + local MarkID = UTILS.GetMarkID() + if ReadOnly==nil then + ReadOnly=false + end + local point1=self:GetVec3() + local point2=Coord2:GetVec3() + local point3=Coord3:GetVec3() + local point4=Coord4:GetVec3() + Coalition=Coalition or -1 + Color=Color or {1,0,0} + Color[4]=Alpha or 1.0 + LineType=LineType or 1 + FillColor=FillColor or Color + FillColor[4]=FillAlpha or 0.15 + trigger.action.quadToAll(Coalition, MarkID, self:GetVec3(), point2, point3, point4, Color, FillColor, LineType, ReadOnly, Text or "") + return MarkID + end + + --- Creates a free form shape on the F10 map. The first point is the current COORDINATE. The remaining points need to be specified. + -- **NOTE**: A free form polygon must have **at least three points** in total and currently only **up to 10 points** in total are supported. + -- @param #COORDINATE self + -- @param #table Coordinates Table of coordinates of the remaining points of the shape. + -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. + -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). + -- @param #number Alpha Transparency [0,1]. Default 1. + -- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. + -- @param #number FillAlpha Transparency [0,1]. Default 0.15. + -- @param #number LineType 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 (Optional) Mark is readonly and cannot be removed by users. Default false. + -- @param #string Text (Optional) Text displayed when mark is added. Default none. + -- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again. + function COORDINATE:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text) + local MarkID = UTILS.GetMarkID() + if ReadOnly==nil then + ReadOnly=false + end + Coalition=Coalition or -1 + Color=Color or {1,0,0} + Color[4]=Alpha or 1.0 + LineType=LineType or 1 + FillColor=FillColor or Color + FillColor[4]=FillAlpha or 0.15 + + local vecs={} + table.insert(vecs, self:GetVec3()) + for _,coord in ipairs(Coordinates) do + table.insert(vecs, coord:GetVec3()) + end + + if #vecs<3 then + self:E("ERROR: A free form polygon needs at least three points!") + elseif #vecs==3 then + trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], Color, FillColor, LineType, ReadOnly, Text or "") + elseif #vecs==4 then + trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], Color, FillColor, LineType, ReadOnly, Text or "") + elseif #vecs==5 then + trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], Color, FillColor, LineType, ReadOnly, Text or "") + elseif #vecs==6 then + trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], Color, FillColor, LineType, ReadOnly, Text or "") + elseif #vecs==7 then + trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], Color, FillColor, LineType, ReadOnly, Text or "") + elseif #vecs==8 then + trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], vecs[8], Color, FillColor, LineType, ReadOnly, Text or "") + elseif #vecs==9 then + trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], vecs[8], vecs[9], Color, FillColor, LineType, ReadOnly, Text or "") + elseif #vecs==10 then + trigger.action.markupToAll(7, Coalition, MarkID, vecs[1], vecs[2], vecs[3], vecs[4], vecs[5], vecs[6], vecs[7], vecs[8], vecs[9], vecs[10], Color, FillColor, LineType, ReadOnly, Text or "") + else + self:E("ERROR: Currently a free form polygon can only have 10 points in total!") + -- Unfortunately, unpack(vecs) does not work! So no idea how to generalize this :( + trigger.action.markupToAll(7, Coalition, MarkID, unpack(vecs), Color, FillColor, LineType, ReadOnly, Text or "") + end + + return MarkID + end + + --- Text to all. Creates a text imposed on the map at the COORDINATE. Text scales with the map. + -- @param #COORDINATE self + -- @param #string Text Text displayed on the F10 map. + -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. + -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). + -- @param #number Alpha Transparency [0,1]. Default 1. + -- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. + -- @param #number FillAlpha Transparency [0,1]. Default 0.3. + -- @param #number FontSize Font size. Default 14. + -- @param #boolean ReadOnly (Optional) Mark is readonly and cannot be removed by users. Default false. + -- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again. + function COORDINATE:TextToAll(Text, Coalition, Color, Alpha, FillColor, FillAlpha, FontSize, ReadOnly) + local MarkID = UTILS.GetMarkID() + if ReadOnly==nil then + ReadOnly=false + end + Coalition=Coalition or -1 + Color=Color or {1,0,0} + Color[4]=Alpha or 1.0 + FillColor=FillColor or Color + FillColor[4]=FillAlpha or 0.3 + FontSize=FontSize or 14 + trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World") + return MarkID + end + + --- Arrow to all. Creates an arrow from the COORDINATE to the endpoint COORDINATE on the F10 map. There is no control over other dimensions of the arrow. + -- @param #COORDINATE self + -- @param #COORDINATE Endpoint COORDINATE where the tip of the arrow is pointing at. + -- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. + -- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red (default). + -- @param #number Alpha Transparency [0,1]. Default 1. + -- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. + -- @param #number FillAlpha Transparency [0,1]. Default 0.15. + -- @param #number LineType 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 (Optional) Mark is readonly and cannot be removed by users. Default false. + -- @param #string Text (Optional) Text displayed when mark is added. Default none. + -- @return #number The resulting Mark ID, which is a number. Can be used to remove the object again. + function COORDINATE:ArrowToAll(Endpoint, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly, Text) + local MarkID = UTILS.GetMarkID() + if ReadOnly==nil then + ReadOnly=false + end + local vec3=Endpoint:GetVec3() + Coalition=Coalition or -1 + Color=Color or {1,0,0} + Color[4]=Alpha or 1.0 + LineType=LineType or 1 + FillColor=FillColor or Color + FillColor[4]=FillAlpha or 0.15 + --trigger.action.textToAll(Coalition, MarkID, self:GetVec3(), Color, FillColor, FontSize, ReadOnly, Text or "Hello World") + trigger.action.arrowToAll(Coalition, MarkID, vec3, self:GetVec3(), Color, FillColor, LineType, ReadOnly, Text or "") + return MarkID + end --- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate. -- @param #COORDINATE self diff --git a/Moose Development/Moose/Core/Zone.lua b/Moose Development/Moose/Core/Zone.lua index 88d487622..70d087b4b 100644 --- a/Moose Development/Moose/Core/Zone.lua +++ b/Moose Development/Moose/Core/Zone.lua @@ -1,9 +1,9 @@ --- **Core** - Define zones within your mission of various forms, with various capabilities. --- +-- -- === --- +-- -- ## Features: --- +-- -- * Create radius zones. -- * Create trigger zones. -- * Create polygon zones. @@ -17,24 +17,25 @@ -- * Get zone properties. -- * Get zone bounding box. -- * Set/get zone name. --- --- +-- * Draw zones (circular and polygon) on the F10 map. +-- +-- -- There are essentially two core functions that zones accomodate: --- +-- -- * Test if an object is within the zone boundaries. -- * Provide the zone behaviour. Some zones are static, while others are moveable. --- +-- -- The object classes are using the zone classes to test the zone boundaries, which can take various forms: --- +-- -- * Test if completely within the zone. -- * Test if partly within the zone (for @{Wrapper.Group#GROUP} objects). -- * Test if not in the zone. -- * Distance to the nearest intersecting point of the zone. -- * Distance to the center of the zone. -- * ... --- +-- -- Each of these ZONE classes have a zone name, and specific parameters defining the zone type: --- +-- -- * @{#ZONE_BASE}: The ZONE_BASE class defining the base for all other zone classes. -- * @{#ZONE_RADIUS}: The ZONE_RADIUS class defined by a zone name, a location and a radius. -- * @{#ZONE}: The ZONE class, defined by the zone name as defined within the Mission Editor. @@ -42,47 +43,48 @@ -- * @{#ZONE_GROUP}: The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. -- * @{#ZONE_POLYGON}: The ZONE_POLYGON class defines by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- --- === --- --- ### Author: **FlightControl** --- ### Contributions: --- -- === --- +-- +-- ### Author: **FlightControl** +-- ### Contributions: +-- +-- === +-- -- @module Core.Zone --- @image Core_Zones.JPG +-- @image Core_Zones.JPG --- @type ZONE_BASE -- @field #string ZoneName Name of the zone. -- @field #number ZoneProbability A value between 0 and 1. 0 = 0% and 1 = 100% probability. --- @field Core.Point#COORDINATE Coordinate object of the zone. +-- @field #number DrawID Unique ID of the drawn zone on the F10 map. +-- @field #table Color Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value. -- @extends Core.Fsm#FSM --- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- +-- -- ## Each zone has a name: --- +-- -- * @{#ZONE_BASE.GetName}(): Returns the name of the zone. -- * @{#ZONE_BASE.SetName}(): Sets the name of the zone. --- --- +-- +-- -- ## Each zone implements two polymorphic functions defined in @{Core.Zone#ZONE_BASE}: --- +-- -- * @{#ZONE_BASE.IsVec2InZone}(): Returns if a 2D vector is within the zone. -- * @{#ZONE_BASE.IsVec3InZone}(): Returns if a 3D vector is within the zone. -- * @{#ZONE_BASE.IsPointVec2InZone}(): Returns if a 2D point vector is within the zone. -- * @{#ZONE_BASE.IsPointVec3InZone}(): Returns if a 3D point vector is within the zone. --- +-- -- ## A zone has a probability factor that can be set to randomize a selection between zones: --- +-- -- * @{#ZONE_BASE.SetZoneProbability}(): Set the randomization probability of a zone to be selected, taking a value between 0 and 1 ( 0 = 0%, 1 = 100% ) -- * @{#ZONE_BASE.GetZoneProbability}(): Get the randomization probability of a zone to be selected, passing a value between 0 and 1 ( 0 = 0%, 1 = 100% ) -- * @{#ZONE_BASE.GetZoneMaybe}(): Get the zone taking into account the randomization probability. nil is returned if this zone is not a candidate. --- +-- -- ## A zone manages vectors: --- +-- -- * @{#ZONE_BASE.GetVec2}(): Returns the 2D vector coordinate of the zone. -- * @{#ZONE_BASE.GetVec3}(): Returns the 3D vector coordinate of the zone. -- * @{#ZONE_BASE.GetPointVec2}(): Returns the 2D point vector coordinate of the zone. @@ -90,22 +92,24 @@ -- * @{#ZONE_BASE.GetRandomVec2}(): Define a random 2D vector within the zone. -- * @{#ZONE_BASE.GetRandomPointVec2}(): Define a random 2D point vector within the zone. -- * @{#ZONE_BASE.GetRandomPointVec3}(): Define a random 3D point vector within the zone. --- +-- -- ## A zone has a bounding square: --- +-- -- * @{#ZONE_BASE.GetBoundingSquare}(): Get the outer most bounding square of the zone. --- --- ## A zone can be marked: --- +-- +-- ## A zone can be marked: +-- -- * @{#ZONE_BASE.SmokeZone}(): Smokes the zone boundaries in a color. -- * @{#ZONE_BASE.FlareZone}(): Flares the zone boundaries in a color. --- +-- -- @field #ZONE_BASE ZONE_BASE = { ClassName = "ZONE_BASE", ZoneName = "", ZoneProbability = 1, - } + DrawID=nil, + Color={} +} --- The ZONE_BASE.BoundingSquare @@ -125,7 +129,7 @@ function ZONE_BASE:New( ZoneName ) self:F( ZoneName ) self.ZoneName = ZoneName - + return self end @@ -202,7 +206,7 @@ end -- @param #ZONE_BASE self -- @return #nil. function ZONE_BASE:GetVec2() - return nil + return nil end --- Returns a @{Core.Point#POINT_VEC2} of the zone. @@ -211,14 +215,14 @@ end -- @return Core.Point#POINT_VEC2 The PointVec2 of the zone. function ZONE_BASE:GetPointVec2() self:F2( self.ZoneName ) - + local Vec2 = self:GetVec2() local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 ) self:T2( { PointVec2 } ) - - return PointVec2 + + return PointVec2 end @@ -228,16 +232,16 @@ end -- @return DCS#Vec3 The Vec3 of the zone. function ZONE_BASE:GetVec3( Height ) self:F2( self.ZoneName ) - + Height = Height or 0 - + local Vec2 = self:GetVec2() local Vec3 = { x = Vec2.x, y = Height and Height or land.getHeight( self:GetVec2() ), z = Vec2.y } self:T2( { Vec3 } ) - - return Vec3 + + return Vec3 end --- Returns a @{Core.Point#POINT_VEC3} of the zone. @@ -246,14 +250,14 @@ end -- @return Core.Point#POINT_VEC3 The PointVec3 of the zone. function ZONE_BASE:GetPointVec3( Height ) self:F2( self.ZoneName ) - + local Vec3 = self:GetVec3( Height ) local PointVec3 = POINT_VEC3:NewFromVec3( Vec3 ) self:T2( { PointVec3 } ) - - return PointVec3 + + return PointVec3 end --- Returns a @{Core.Point#COORDINATE} of the zone. @@ -262,25 +266,25 @@ end -- @return Core.Point#COORDINATE The Coordinate of the zone. function ZONE_BASE:GetCoordinate( Height ) --R2.1 self:F2(self.ZoneName) - + local Vec3 = self:GetVec3( Height ) - + if self.Coordinate then - + -- Update coordinates. self.Coordinate.x=Vec3.x self.Coordinate.y=Vec3.y self.Coordinate.z=Vec3.z - + --env.info("FF GetCoordinate NEW for ZONE_BASE "..tostring(self.ZoneName)) else - -- Create a new coordinate object. + -- Create a new coordinate object. self.Coordinate=COORDINATE:NewFromVec3(Vec3) - + --env.info("FF GetCoordinate NEW for ZONE_BASE "..tostring(self.ZoneName)) end - + return self.Coordinate end @@ -321,12 +325,82 @@ function ZONE_BASE:BoundZone() end + +--- Set color of zone. +-- @param #ZONE_BASE self +-- @param #table RGBcolor RGB color table. Default `{1, 0, 0}`. +-- @param #number Alpha Transparacy between 0 and 1. Default 0.15. +-- @return #ZONE_BASE self +function ZONE_BASE:SetColor(RGBcolor, Alpha) + + RGBcolor=RGBcolor or {1, 0, 0} + Alpha=Alpha or 0.15 + + self.Color={} + self.Color[1]=RGBcolor[1] + self.Color[2]=RGBcolor[2] + self.Color[3]=RGBcolor[3] + self.Color[4]=Alpha + + return self +end + +--- Get color table of the zone. +-- @param #ZONE_BASE self +-- @return #table Table with four entries, e.g. {1, 0, 0, 0.15}. First three are RGB color code. Fourth is the transparency Alpha value. +function ZONE_BASE:GetColor() + return self.Color +end + +--- Get RGB color of zone. +-- @param #ZONE_BASE self +-- @return #table Table with three entries, e.g. {1, 0, 0}, which is the RGB color code. +function ZONE_BASE:GetColorRGB() + local rgb={} + rgb[1]=self.Color[1] + rgb[2]=self.Color[2] + rgb[3]=self.Color[3] + return rgb +end + +--- Get transperency Alpha value of zone. +-- @param #ZONE_BASE self +-- @return #number Alpha value. +function ZONE_BASE:GetColorAlpha() + local alpha=self.Color[4] + return alpha +end + +--- Remove the drawing of the zone from the F10 map. +-- @param #ZONE_BASE self +-- @param #number Delay (Optional) Delay before the drawing is removed. +-- @return #ZONE_BASE self +function ZONE_BASE:UndrawZone(Delay) + if Delay and Delay>0 then + self:ScheduleOnce(Delay, ZONE_BASE.UndrawZone, self) + else + if self.DrawID then + UTILS.RemoveMark(self.DrawID) + end + end + return self +end + +--- Get ID of the zone object drawn on the F10 map. +-- The ID can be used to remove the drawn object from the F10 map view via `UTILS.RemoveMark(MarkID)`. +-- @param #ZONE_BASE self +-- @return #number Unique ID of the +function ZONE_BASE:GetDrawID() + return self.DrawID +end + + --- Smokes the zone boundaries in a color. -- @param #ZONE_BASE self -- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color. function ZONE_BASE:SmokeZone( SmokeColor ) self:F2( SmokeColor ) - + end --- Set the randomization probability of a zone to be selected. @@ -335,7 +409,7 @@ end -- @return #ZONE_BASE self function ZONE_BASE:SetZoneProbability( ZoneProbability ) self:F( { self:GetName(), ZoneProbability = ZoneProbability } ) - + self.ZoneProbability = ZoneProbability or 1 return self end @@ -344,8 +418,8 @@ end -- @param #ZONE_BASE self -- @return #number A value between 0 and 1. 0 = 0% and 1 = 100% probability. function ZONE_BASE:GetZoneProbability() - self:F2() - + self:F2() + return self.ZoneProbability end @@ -354,15 +428,15 @@ end -- @return #ZONE_BASE The zone is selected taking into account the randomization probability factor. -- @return #nil The zone is not selected taking into account the randomization probability factor. -- @usage --- +-- -- local ZoneArray = { ZONE:New( "Zone1" ), ZONE:New( "Zone2" ) } --- +-- -- -- We set a zone probability of 70% to the first zone and 30% to the second zone. -- ZoneArray[1]:SetZoneProbability( 0.5 ) -- ZoneArray[2]:SetZoneProbability( 0.5 ) --- +-- -- local ZoneSelected = nil --- +-- -- while ZoneSelected == nil do -- for _, Zone in pairs( ZoneArray ) do -- ZoneSelected = Zone:GetZoneMaybe() @@ -371,12 +445,12 @@ end -- end -- end -- end --- +-- -- -- The result should be that Zone1 would be more probable selected than Zone2. --- +-- function ZONE_BASE:GetZoneMaybe() self:F2() - + local Randomization = math.random() if Randomization <= self.ZoneProbability then return self @@ -394,30 +468,34 @@ end --- The ZONE_RADIUS class defined by a zone name, a location and a radius. -- This class implements the inherited functions from Core.Zone#ZONE_BASE taking into account the own zone format and properties. --- +-- -- ## ZONE_RADIUS constructor --- +-- -- * @{#ZONE_RADIUS.New}(): Constructor. --- +-- -- ## Manage the radius of the zone --- +-- -- * @{#ZONE_RADIUS.SetRadius}(): Sets the radius of the zone. -- * @{#ZONE_RADIUS.GetRadius}(): Returns the radius of the zone. --- +-- -- ## Manage the location of the zone --- +-- -- * @{#ZONE_RADIUS.SetVec2}(): Sets the @{DCS#Vec2} of the zone. -- * @{#ZONE_RADIUS.GetVec2}(): Returns the @{DCS#Vec2} of the zone. -- * @{#ZONE_RADIUS.GetVec3}(): Returns the @{DCS#Vec3} of the zone, taking an additional height parameter. --- +-- -- ## Zone point randomization --- +-- -- Various functions exist to find random points within the zone. --- +-- -- * @{#ZONE_RADIUS.GetRandomVec2}(): Gets a random 2D point in the zone. -- * @{#ZONE_RADIUS.GetRandomPointVec2}(): Gets a @{Core.Point#POINT_VEC2} object representing a random 2D point in the zone. -- * @{#ZONE_RADIUS.GetRandomPointVec3}(): Gets a @{Core.Point#POINT_VEC3} object representing a random 3D point in the zone. Note that the height of the point is at landheight. --- +-- +-- ## Draw zone +-- +-- * @{#ZONE_RADIUS.DrawZone}(): Draws the zone on the F10 map. +-- -- @field #ZONE_RADIUS ZONE_RADIUS = { ClassName="ZONE_RADIUS", @@ -437,9 +515,9 @@ function ZONE_RADIUS:New( ZoneName, Vec2, Radius ) self.Radius = Radius self.Vec2 = Vec2 - + --self.Coordinate=COORDINATE:NewFromVec2(Vec2) - + return self end @@ -491,18 +569,44 @@ function ZONE_RADIUS:MarkZone(Points) local Angle local RadialBase = math.pi*2 - + for Angle = 0, 360, (360 / Points ) do - + local Radial = Angle * RadialBase / 360 - + Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() - + COORDINATE:NewFromVec2(Point):MarkToAll(self:GetName()) end - + +end + +--- Draw the zone circle on the F10 map. +-- @param #ZONE_RADIUS self +-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. +-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. +-- @param #number Alpha Transparency [0,1]. Default 1. +-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. +-- @param #number FillAlpha Transparency [0,1]. Default 0.15. +-- @param #number LineType 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 (Optional) Mark is readonly and cannot be removed by users. Default false. +-- @return #ZONE_RADIUS self +function ZONE_RADIUS:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) + + local coordinate=self:GetCoordinate() + + local Radius=self:GetRadius() + + Color=Color or self:GetColorRGB() + Alpha=Alpha or 1 + FillColor=FillColor or Color + FillAlpha=FillAlpha or self:GetColorAlpha() + + self.DrawID=coordinate:CircleToAll(Radius, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) + + return self end --- Bounds the zone with tires. @@ -520,17 +624,17 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound ) local Angle local RadialBase = math.pi*2 - + -- for Angle = 0, 360, (360 / Points ) do local Radial = Angle * RadialBase / 360 Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius() - + local CountryName = _DATABASE.COUNTRY_NAME[CountryID] - + local Tire = { - ["country"] = CountryName, + ["country"] = CountryName, ["category"] = "Fortifications", ["canCargo"] = false, ["shape_name"] = "H-tyre_B_WF", @@ -564,7 +668,7 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset ) local Point = {} local Vec2 = self:GetVec2() - + AddHeight = AddHeight or 0 AngleOffset = AngleOffset or 0 @@ -572,7 +676,7 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset ) local Angle local RadialBase = math.pi*2 - + for Angle = 0, 360, 360 / Points do local Radial = ( Angle + AngleOffset ) * RadialBase / 360 Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() @@ -596,14 +700,14 @@ function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth, AddHeight ) local Point = {} local Vec2 = self:GetVec2() - + AddHeight = AddHeight or 0 - + Points = Points and Points or 360 local Angle local RadialBase = math.pi*2 - + for Angle = 0, 360, 360 / Points do local Radial = Angle * RadialBase / 360 Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() @@ -645,8 +749,8 @@ function ZONE_RADIUS:GetVec2() self:F2( self.ZoneName ) self:T2( { self.Vec2 } ) - - return self.Vec2 + + return self.Vec2 end --- Sets the @{DCS#Vec2} of the zone. @@ -655,12 +759,12 @@ end -- @return DCS#Vec2 The new location of the zone. function ZONE_RADIUS:SetVec2( Vec2 ) self:F2( self.ZoneName ) - + self.Vec2 = Vec2 self:T2( { self.Vec2 } ) - - return self.Vec2 + + return self.Vec2 end --- Returns the @{DCS#Vec3} of the ZONE_RADIUS. @@ -676,8 +780,8 @@ function ZONE_RADIUS:GetVec3( Height ) local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } self:T2( { Vec3 } ) - - return Vec3 + + return Vec3 end @@ -686,7 +790,7 @@ end --- Scan the zone for the presence of units of the given ObjectCategories. -- Note that after a zone has been scanned, the zone can be evaluated by: --- +-- -- * @{ZONE_RADIUS.IsAllInZoneOfCoalition}(): Scan the presence of units in the zone of a coalition. -- * @{ZONE_RADIUS.IsAllInZoneOfOtherCoalition}(): Scan the presence of units in the zone of an other coalition. -- * @{ZONE_RADIUS.IsSomeInZoneOfCoalition}(): Scan if there is some presence of units in the zone of the given coalition. @@ -708,7 +812,7 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) local ZoneCoord = self:GetCoordinate() local ZoneRadius = self:GetRadius() - + self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()}) local SphereSearch = { @@ -721,18 +825,18 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) local function EvaluateZone( ZoneObject ) --if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5 - if ZoneObject then - + if ZoneObject then + local ObjectCategory = ZoneObject:getCategory() - + --local name=ZoneObject:getName() --env.info(string.format("Zone object %s", tostring(name))) --self:E(ZoneObject) - + if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then - + local CoalitionDCSUnit = ZoneObject:getCoalition() - + local Include = false if not UnitCategories then -- Anythink found is included. @@ -740,29 +844,29 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) else -- Check if found object is in specified categories. local CategoryDCSUnit = ZoneObject:getDesc().category - + for UnitCategoryID, UnitCategory in pairs( UnitCategories ) do if UnitCategory == CategoryDCSUnit then Include = true break end end - + end - + if Include then - + local CoalitionDCSUnit = ZoneObject:getCoalition() - + -- This coalition is inside the zone. self.ScanData.Coalitions[CoalitionDCSUnit] = true - + self.ScanData.Units[ZoneObject] = ZoneObject - + self:F2( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } ) end end - + if ObjectCategory == Object.Category.SCENERY then local SceneryType = ZoneObject:getTypeName() local SceneryName = ZoneObject:getName() @@ -770,15 +874,15 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories ) self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject ) self:F2( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } ) end - + end - + return true end -- Search objects. world.searchObjects( ObjectCategories, SphereSearch, EvaluateZone ) - + end --- Count the number of different coalitions inside the zone. @@ -823,14 +927,14 @@ end function ZONE_RADIUS:GetScannedSetGroup() self.ScanSetGroup=self.ScanSetGroup or SET_GROUP:New() --Core.Set#SET_GROUP - + self.ScanSetGroup.Set={} if self.ScanData then for ObjectID, UnitObject in pairs( self.ScanData.Units ) do local UnitObject = UnitObject -- DCS#Unit if UnitObject:isExist() then - + local FoundUnit=UNIT:FindByName(UnitObject:getName()) if FoundUnit then local group=FoundUnit:GetGroup() @@ -850,11 +954,11 @@ end function ZONE_RADIUS:CountScannedCoalitions() local Count = 0 - + for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do Count = Count + 1 end - + return Count end @@ -882,16 +986,16 @@ function ZONE_RADIUS:GetScannedCoalition( Coalition ) else local Count = 0 local ReturnCoalition = nil - + for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do Count = Count + 1 ReturnCoalition = CoalitionID end - + if Count ~= 1 then ReturnCoalition = nil end - + return ReturnCoalition end end @@ -1002,7 +1106,7 @@ function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories ) local ZoneCoord = self:GetCoordinate() local ZoneRadius = self:GetRadius() - + self:F({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()}) local SphereSearch = { @@ -1014,8 +1118,8 @@ function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories ) } local function EvaluateZone( ZoneDCSUnit ) - - + + local ZoneUnit = UNIT:Find( ZoneDCSUnit ) return EvaluateFunction( ZoneUnit ) @@ -1031,15 +1135,15 @@ end -- @return #boolean true if the location is within the zone. function ZONE_RADIUS:IsVec2InZone( Vec2 ) self:F2( Vec2 ) - + local ZoneVec2 = self:GetVec2() - + if ZoneVec2 then if (( Vec2.x - ZoneVec2.x )^2 + ( Vec2.y - ZoneVec2.y ) ^2 ) ^ 0.5 <= self:GetRadius() then return true end end - + return false end @@ -1071,9 +1175,9 @@ function ZONE_RADIUS:GetRandomVec2( inner, outer ) local angle = math.random() * math.pi * 2; Point.x = Vec2.x + math.cos( angle ) * math.random(_inner, _outer); Point.y = Vec2.y + math.sin( angle ) * math.random(_inner, _outer); - + self:T( { Point } ) - + return Point end @@ -1088,7 +1192,7 @@ function ZONE_RADIUS:GetRandomPointVec2( inner, outer ) local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2( inner, outer ) ) self:T3( { PointVec2 } ) - + return PointVec2 end @@ -1103,7 +1207,7 @@ function ZONE_RADIUS:GetRandomVec3( inner, outer ) local Vec2 = self:GetRandomVec2( inner, outer ) self:T3( { x = Vec2.x, y = self.y, z = Vec2.y } ) - + return { x = Vec2.x, y = self.y, z = Vec2.y } end @@ -1119,7 +1223,7 @@ function ZONE_RADIUS:GetRandomPointVec3( inner, outer ) local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2( inner, outer ) ) self:T3( { PointVec3 } ) - + return PointVec3 end @@ -1135,7 +1239,7 @@ function ZONE_RADIUS:GetRandomCoordinate( inner, outer ) local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2(inner, outer) ) self:T3( { Coordinate = Coordinate } ) - + return Coordinate end @@ -1147,55 +1251,70 @@ end --- The ZONE class, defined by the zone name as defined within the Mission Editor. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. --- +-- -- ## ZONE constructor --- +-- -- * @{#ZONE.New}(): Constructor. This will search for a trigger zone with the name given, and will return for you a ZONE object. --- +-- -- ## Declare a ZONE directly in the DCS mission editor! --- +-- -- You can declare a ZONE using the DCS mission editor by adding a trigger zone in the mission editor. --- +-- -- Then during mission startup, when loading Moose.lua, this trigger zone will be detected as a ZONE declaration. -- Within the background, a ZONE object will be created within the @{Core.Database}. -- The ZONE name will be the trigger zone name. --- +-- -- So, you can search yourself for the ZONE object by using the @{#ZONE.FindByName}() method. -- In this example, `local TriggerZone = ZONE:FindByName( "DefenseZone" )` would return the ZONE object --- that was created at mission startup, and reference it into the `TriggerZone` local object. --- +-- that was created at mission startup, and reference it into the `TriggerZone` local object. +-- -- Refer to mission `ZON-110` for a demonstration. --- +-- -- This is especially handy if you want to quickly setup a SET_ZONE... -- So when you would declare `local SetZone = SET_ZONE:New():FilterPrefixes( "Defense" ):FilterStart()`, -- then SetZone would contain the ZONE object `DefenseZone` as part of the zone collection, --- without much scripting overhead!!! --- --- --- @field #ZONE +-- without much scripting overhead!!! +-- +-- +-- @field #ZONE ZONE = { ClassName="ZONE", } ---- Constructor of ZONE, taking the zone name. +--- Constructor of ZONE taking the zone name. -- @param #ZONE self -- @param #string ZoneName The name of the zone as defined within the mission editor. --- @return #ZONE +-- @return #ZONE self function ZONE:New( ZoneName ) + -- First try to find the zone in the DB. + local zone=_DATABASE:FindZone(ZoneName) + + if zone then + --env.info("FF found zone in DB") + return zone + end + + -- Get zone from DCS trigger function. local Zone = trigger.misc.getZone( ZoneName ) - + + -- Error! if not Zone then error( "Zone " .. ZoneName .. " does not exist." ) return nil end - local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, { x = Zone.point.x, y = Zone.point.z }, Zone.radius ) ) - self:F( ZoneName ) + -- Create a new ZONE_RADIUS. + local self=BASE:Inherit( self, ZONE_RADIUS:New(ZoneName, {x=Zone.point.x, y=Zone.point.z}, Zone.radius)) + self:F(ZoneName) + -- Color of zone. + self.Color={1, 0, 0, 0.15} + + -- DCS zone. self.Zone = Zone - + return self end @@ -1204,7 +1323,7 @@ end -- @param #string ZoneName The name of the zone. -- @return #ZONE_BASE self function ZONE:FindByName( ZoneName ) - + local ZoneFound = _DATABASE:FindZone( ZoneName ) return ZoneFound end @@ -1217,15 +1336,15 @@ end --- # ZONE_UNIT class, extends @{Zone#ZONE_RADIUS} --- +-- -- The ZONE_UNIT class defined by a zone attached to a @{Wrapper.Unit#UNIT} with a radius and optional offsets. -- This class implements the inherited functions from @{#ZONE_RADIUS} taking into account the own zone format and properties. --- +-- -- @field #ZONE_UNIT ZONE_UNIT = { ClassName="ZONE_UNIT", } - + --- Constructor to create a ZONE_UNIT instance, taking the zone name, a zone unit and a radius and optional offsets in X and Y directions. -- @param #ZONE_UNIT self -- @param #string ZoneName Name of the zone. @@ -1240,30 +1359,30 @@ ZONE_UNIT = { -- dx, dy OR rho, theta may be used, not both. -- @return #ZONE_UNIT self function ZONE_UNIT:New( ZoneName, ZoneUNIT, Radius, Offset) - + if Offset then - -- check if the inputs was reasonable, either (dx, dy) or (rho, theta) can be given, else raise an exception. + -- check if the inputs was reasonable, either (dx, dy) or (rho, theta) can be given, else raise an exception. if (Offset.dx or Offset.dy) and (Offset.rho or Offset.theta) then - error("Cannot use (dx, dy) with (rho, theta)") + error("Cannot use (dx, dy) with (rho, theta)") end - + self.dy = Offset.dy or 0.0 self.dx = Offset.dx or 0.0 self.rho = Offset.rho or 0.0 self.theta = (Offset.theta or 0.0) * math.pi / 180.0 self.relative_to_unit = Offset.relative_to_unit or false end - + local self = BASE:Inherit( self, ZONE_RADIUS:New( ZoneName, ZoneUNIT:GetVec2(), Radius ) ) self:F( { ZoneName, ZoneUNIT:GetVec2(), Radius } ) self.ZoneUNIT = ZoneUNIT self.LastVec2 = ZoneUNIT:GetVec2() - + -- Zone objects are added to the _DATABASE and SET_ZONE objects. _EVENTDISPATCHER:CreateEventNewZone( self ) - + return self end @@ -1273,32 +1392,32 @@ end -- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Unit#UNIT}location and the offset, if any. function ZONE_UNIT:GetVec2() self:F2( self.ZoneName ) - + local ZoneVec2 = self.ZoneUNIT:GetVec2() if ZoneVec2 then - + local heading if self.relative_to_unit then heading = ( self.ZoneUNIT:GetHeading() or 0.0 ) * math.pi / 180.0 else heading = 0.0 end - + -- update the zone position with the offsets. if (self.dx or self.dy) then - + -- use heading to rotate offset relative to unit using rotation matrix in 2D. -- see: https://en.wikipedia.org/wiki/Rotation_matrix - ZoneVec2.x = ZoneVec2.x + self.dx * math.cos( -heading ) + self.dy * math.sin( -heading ) - ZoneVec2.y = ZoneVec2.y - self.dx * math.sin( -heading ) + self.dy * math.cos( -heading ) + ZoneVec2.x = ZoneVec2.x + self.dx * math.cos( -heading ) + self.dy * math.sin( -heading ) + ZoneVec2.y = ZoneVec2.y - self.dx * math.sin( -heading ) + self.dy * math.cos( -heading ) end - + -- if using the polar coordinates - if (self.rho or self.theta) then + if (self.rho or self.theta) then ZoneVec2.x = ZoneVec2.x + self.rho * math.cos( self.theta + heading ) ZoneVec2.y = ZoneVec2.y + self.rho * math.sin( self.theta + heading ) end - + self.LastVec2 = ZoneVec2 return ZoneVec2 else @@ -1307,7 +1426,7 @@ function ZONE_UNIT:GetVec2() self:T2( { ZoneVec2 } ) - return nil + return nil end --- Returns a random location within the zone. @@ -1319,7 +1438,7 @@ function ZONE_UNIT:GetRandomVec2() local RandomVec2 = {} --local Vec2 = self.ZoneUNIT:GetVec2() -- FF: This does not take care of the new offset feature! local Vec2 = self:GetVec2() - + if not Vec2 then Vec2 = self.LastVec2 end @@ -1327,9 +1446,9 @@ function ZONE_UNIT:GetRandomVec2() local angle = math.random() * math.pi*2; RandomVec2.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); RandomVec2.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius(); - + self:T( { RandomVec2 } ) - + return RandomVec2 end @@ -1339,16 +1458,16 @@ end -- @return DCS#Vec3 The point of the zone. function ZONE_UNIT:GetVec3( Height ) self:F2( self.ZoneName ) - + Height = Height or 0 - + local Vec2 = self:GetVec2() local Vec3 = { x = Vec2.x, y = land.getHeight( self:GetVec2() ) + Height, z = Vec2.y } self:T2( { Vec3 } ) - - return Vec3 + + return Vec3 end --- @type ZONE_GROUP @@ -1357,12 +1476,12 @@ end --- The ZONE_GROUP class defines by a zone around a @{Wrapper.Group#GROUP} with a radius. The current leader of the group defines the center of the zone. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- +-- -- @field #ZONE_GROUP ZONE_GROUP = { ClassName="ZONE_GROUP", } - + --- Constructor to create a ZONE_GROUP instance, taking the zone name, a zone @{Wrapper.Group#GROUP} and a radius. -- @param #ZONE_GROUP self -- @param #string ZoneName Name of the zone. @@ -1378,7 +1497,7 @@ function ZONE_GROUP:New( ZoneName, ZoneGROUP, Radius ) -- Zone objects are added to the _DATABASE and SET_ZONE objects. _EVENTDISPATCHER:CreateEventNewZone( self ) - + return self end @@ -1388,9 +1507,9 @@ end -- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. function ZONE_GROUP:GetVec2() self:F( self.ZoneName ) - + local ZoneVec2 = nil - + if self._.ZoneGROUP:IsAlive() then ZoneVec2 = self._.ZoneGROUP:GetVec2() self._.ZoneVec2Cache = ZoneVec2 @@ -1399,7 +1518,7 @@ function ZONE_GROUP:GetVec2() end self:T( { ZoneVec2 } ) - + return ZoneVec2 end @@ -1415,9 +1534,9 @@ function ZONE_GROUP:GetRandomVec2() local angle = math.random() * math.pi*2; Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius(); - + self:T( { Point } ) - + return Point end @@ -1432,7 +1551,7 @@ function ZONE_GROUP:GetRandomPointVec2( inner, outer ) local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() ) self:T3( { PointVec2 } ) - + return PointVec2 end @@ -1445,15 +1564,20 @@ end --- The ZONE_POLYGON_BASE class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. -- This class is an abstract BASE class for derived classes, and is not meant to be instantiated. --- +-- -- ## Zone point randomization --- +-- -- Various functions exist to find random points within the zone. --- +-- -- * @{#ZONE_POLYGON_BASE.GetRandomVec2}(): Gets a random 2D point in the zone. -- * @{#ZONE_POLYGON_BASE.GetRandomPointVec2}(): Return a @{Core.Point#POINT_VEC2} object representing a random 2D point within the zone. -- * @{#ZONE_POLYGON_BASE.GetRandomPointVec3}(): Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. --- +-- +-- ## Draw zone +-- +-- * @{#ZONE_POLYGON_BASE.DrawZone}(): Draws the zone on the F10 map. +-- +-- -- @field #ZONE_POLYGON_BASE ZONE_POLYGON_BASE = { ClassName="ZONE_POLYGON_BASE", @@ -1482,13 +1606,13 @@ function ZONE_POLYGON_BASE:New( ZoneName, PointsArray ) if PointsArray then self._.Polygon = {} - + for i = 1, #PointsArray do self._.Polygon[i] = {} self._.Polygon[i].x = PointsArray[i].x self._.Polygon[i].y = PointsArray[i].y end - + end return self @@ -1501,7 +1625,7 @@ end function ZONE_POLYGON_BASE:UpdateFromVec2(Vec2Array) self._.Polygon = {} - + for i=1,#Vec2Array do self._.Polygon[i] = {} self._.Polygon[i].x=Vec2Array[i].x @@ -1518,7 +1642,7 @@ end function ZONE_POLYGON_BASE:UpdateFromVec3(Vec3Array) self._.Polygon = {} - + for i=1,#Vec3Array do self._.Polygon[i] = {} self._.Polygon[i].x=Vec3Array[i].x @@ -1529,14 +1653,86 @@ function ZONE_POLYGON_BASE:UpdateFromVec3(Vec3Array) end --- Returns the center location of the polygon. --- @param #ZONE_GROUP self +-- @param #ZONE_POLYGON_BASE self -- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. function ZONE_POLYGON_BASE:GetVec2() self:F( self.ZoneName ) local Bounds = self:GetBoundingSquare() - - return { x = ( Bounds.x2 + Bounds.x1 ) / 2, y = ( Bounds.y2 + Bounds.y1 ) / 2 } + + return { x = ( Bounds.x2 + Bounds.x1 ) / 2, y = ( Bounds.y2 + Bounds.y1 ) / 2 } +end + +--- Get a vertex of the polygon. +-- @param #ZONE_POLYGON_BASE self +-- @param #number Index Index of the vertex. Default 1. +-- @return DCS#Vec2 Vertex of the polygon. +function ZONE_POLYGON_BASE:GetVertexVec2(Index) + return self._.Polygon[Index or 1] +end + +--- Get a vertex of the polygon. +-- @param #ZONE_POLYGON_BASE self +-- @param #number Index Index of the vertex. Default 1. +-- @return DCS#Vec3 Vertex of the polygon. +function ZONE_POLYGON_BASE:GetVertexVec3(Index) + local vec2=self:GetVertexVec2(Index) + if vec2 then + local vec3={x=vec2.x, y=land.getHeight(vec2), z=vec2.y} + return vec3 + end + return nil +end + +--- Get a vertex of the polygon. +-- @param #ZONE_POLYGON_BASE self +-- @param #number Index Index of the vertex. Default 1. +-- @return Core.Point#COORDINATE Vertex of the polygon. +function ZONE_POLYGON_BASE:GetVertexCoordinate(Index) + local vec2=self:GetVertexVec2(Index) + if vec2 then + local coord=COORDINATE:NewFromVec2(vec2) + return coord + end + return nil +end + + +--- Get a list of verticies of the polygon. +-- @param #ZONE_POLYGON_BASE self +-- @return List of DCS#Vec2 verticies defining the edges of the polygon. +function ZONE_POLYGON_BASE:GetVerticiesVec2() + return self._.Polygon +end + +--- Get a list of verticies of the polygon. +-- @param #ZONE_POLYGON_BASE self +-- @return #table List of DCS#Vec3 verticies defining the edges of the polygon. +function ZONE_POLYGON_BASE:GetVerticiesVec3() + + local coords={} + + for i,vec2 in ipairs(self._.Polygon) do + local vec3={x=vec2.x, y=land.getHeight(vec2), z=vec2.y} + table.insert(coords, vec3) + end + + return coords +end + +--- Get a list of verticies of the polygon. +-- @param #ZONE_POLYGON_BASE self +-- @return #table List of COORDINATES verticies defining the edges of the polygon. +function ZONE_POLYGON_BASE:GetVerticiesCoordinates() + + local coords={} + + for i,vec2 in ipairs(self._.Polygon) do + local coord=COORDINATE:NewFromVec2(vec2) + table.insert(coords, coord) + end + + return coords end --- Flush polygon coordinates as a table in DCS.log. @@ -1556,24 +1752,24 @@ end -- @return #ZONE_POLYGON_BASE self function ZONE_POLYGON_BASE:BoundZone( UnBound ) - local i - local j + local i + local j local Segments = 10 - + i = 1 j = #self._.Polygon - + while i <= #self._.Polygon do self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } ) - + local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y - + for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line. local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments ) local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments ) local Tire = { - ["country"] = "USA", + ["country"] = "USA", ["category"] = "Fortifications", ["canCargo"] = false, ["shape_name"] = "H-tyre_B_WF", @@ -1583,12 +1779,12 @@ function ZONE_POLYGON_BASE:BoundZone( UnBound ) ["name"] = string.format( "%s-Tire #%0d", self:GetName(), ((i - 1) * Segments) + Segment ), ["heading"] = 0, } -- end of ["group"] - + local Group = coalition.addStaticObject( country.id.USA, Tire ) if UnBound and UnBound == true then Group:destroy() end - + end j = i i = i + 1 @@ -1598,6 +1794,46 @@ function ZONE_POLYGON_BASE:BoundZone( UnBound ) end +--- Draw the zone on the F10 map. **NOTE** Currently, only polygons with **exactly four points** are supported! +-- @param #ZONE_POLYGON_BASE self +-- @param #number Coalition Coalition: All=-1, Neutral=0, Red=1, Blue=2. Default -1=All. +-- @param #table Color RGB color table {r, g, b}, e.g. {1,0,0} for red. +-- @param #number Alpha Transparency [0,1]. Default 1. +-- @param #table FillColor RGB color table {r, g, b}, e.g. {1,0,0} for red. Default is same as `Color` value. +-- @param #number FillAlpha Transparency [0,1]. Default 0.15. +-- @param #number LineType 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 (Optional) Mark is readonly and cannot be removed by users. Default false. +-- @return #ZONE_POLYGON_BASE self +function ZONE_POLYGON_BASE:DrawZone(Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) + + local coordinate=COORDINATE:NewFromVec2(self._.Polygon[1]) + + Color=Color or self:GetColorRGB() + Alpha=Alpha or 1 + FillColor=FillColor or Color + FillAlpha=FillAlpha or self:GetColorAlpha() + + + if #self._.Polygon==4 then + + local Coord2=COORDINATE:NewFromVec2(self._.Polygon[2]) + local Coord3=COORDINATE:NewFromVec2(self._.Polygon[3]) + local Coord4=COORDINATE:NewFromVec2(self._.Polygon[4]) + + self.DrawID=coordinate:QuadToAll(Coord2, Coord3, Coord4, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) + + else + + local Coordinates=self:GetVerticiesCoordinates() + table.remove(Coordinates, 1) + + self.DrawID=coordinate:MarkupToAllFreeForm(Coordinates, Coalition, Color, Alpha, FillColor, FillAlpha, LineType, ReadOnly) + + end + + + return self +end --- Smokes the zone boundaries in a color. -- @param #ZONE_POLYGON_BASE self @@ -1608,16 +1844,16 @@ function ZONE_POLYGON_BASE:SmokeZone( SmokeColor, Segments ) self:F2( SmokeColor ) Segments=Segments or 10 - + local i=1 local j=#self._.Polygon - + while i <= #self._.Polygon do self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } ) - + local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y - + for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line. local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments ) local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments ) @@ -1642,18 +1878,18 @@ function ZONE_POLYGON_BASE:FlareZone( FlareColor, Segments, Azimuth, AddHeight ) self:F2(FlareColor) Segments=Segments or 10 - + AddHeight = AddHeight or 0 - + local i=1 local j=#self._.Polygon - + while i <= #self._.Polygon do self:T( { i, j, self._.Polygon[i], self._.Polygon[j] } ) - + local DeltaX = self._.Polygon[j].x - self._.Polygon[i].x local DeltaY = self._.Polygon[j].y - self._.Polygon[i].y - + for Segment = 0, Segments do -- We divide each line in 5 segments and smoke a point on the line. local PointX = self._.Polygon[i].x + ( Segment * DeltaX / Segments ) local PointY = self._.Polygon[i].y + ( Segment * DeltaY / Segments ) @@ -1677,17 +1913,17 @@ end function ZONE_POLYGON_BASE:IsVec2InZone( Vec2 ) self:F2( Vec2 ) - local Next - local Prev + local Next + local Prev local InPolygon = false - + Next = 1 Prev = #self._.Polygon - + while Next <= #self._.Polygon do self:T( { Next, Prev, self._.Polygon[Next], self._.Polygon[Prev] } ) if ( ( ( self._.Polygon[Next].y > Vec2.y ) ~= ( self._.Polygon[Prev].y > Vec2.y ) ) and - ( Vec2.x < ( self._.Polygon[Prev].x - self._.Polygon[Next].x ) * ( Vec2.y - self._.Polygon[Next].y ) / ( self._.Polygon[Prev].y - self._.Polygon[Next].y ) + self._.Polygon[Next].x ) + ( Vec2.x < ( self._.Polygon[Prev].x - self._.Polygon[Next].x ) * ( Vec2.y - self._.Polygon[Next].y ) / ( self._.Polygon[Prev].y - self._.Polygon[Next].y ) + self._.Polygon[Next].x ) ) then InPolygon = not InPolygon end @@ -1710,9 +1946,9 @@ function ZONE_POLYGON_BASE:GetRandomVec2() local Vec2Found = false local Vec2 local BS = self:GetBoundingSquare() - + self:T2( BS ) - + while Vec2Found == false do Vec2 = { x = math.random( BS.x1, BS.x2 ), y = math.random( BS.y1, BS.y2 ) } self:T2( Vec2 ) @@ -1720,7 +1956,7 @@ function ZONE_POLYGON_BASE:GetRandomVec2() Vec2Found = true end end - + self:T2( Vec2 ) return Vec2 @@ -1733,7 +1969,7 @@ function ZONE_POLYGON_BASE:GetRandomPointVec2() self:F2() local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() ) - + self:T2( PointVec2 ) return PointVec2 @@ -1746,7 +1982,7 @@ function ZONE_POLYGON_BASE:GetRandomPointVec3() self:F2() local PointVec3 = POINT_VEC3:NewFromVec2( self:GetRandomVec2() ) - + self:T2( PointVec3 ) return PointVec3 @@ -1760,7 +1996,7 @@ function ZONE_POLYGON_BASE:GetRandomCoordinate() self:F2() local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2() ) - + self:T2( Coordinate ) return Coordinate @@ -1776,14 +2012,14 @@ function ZONE_POLYGON_BASE:GetBoundingSquare() local y1 = self._.Polygon[1].y local x2 = self._.Polygon[1].x local y2 = self._.Polygon[1].y - + for i = 2, #self._.Polygon do self:T2( { self._.Polygon[i], x1, y1, x2, y2 } ) x1 = ( x1 > self._.Polygon[i].x ) and self._.Polygon[i].x or x1 x2 = ( x2 < self._.Polygon[i].x ) and self._.Polygon[i].x or x2 y1 = ( y1 > self._.Polygon[i].y ) and self._.Polygon[i].y or y1 y2 = ( y2 < self._.Polygon[i].y ) and self._.Polygon[i].y or y2 - + end return { x1 = x1, y1 = y1, x2 = x2, y2 = y2 } @@ -1796,27 +2032,27 @@ end --- The ZONE_POLYGON class defined by a sequence of @{Wrapper.Group#GROUP} waypoints within the Mission Editor, forming a polygon. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. --- +-- -- ## Declare a ZONE_POLYGON directly in the DCS mission editor! --- +-- -- You can declare a ZONE_POLYGON using the DCS mission editor by adding the ~ZONE_POLYGON tag in the group name. --- +-- -- So, imagine you have a group declared in the mission editor, with group name `DefenseZone~ZONE_POLYGON`. -- Then during mission startup, when loading Moose.lua, this group will be detected as a ZONE_POLYGON declaration. -- Within the background, a ZONE_POLYGON object will be created within the @{Core.Database} using the properties of the group. -- The ZONE_POLYGON name will be the group name without the ~ZONE_POLYGON tag. --- +-- -- So, you can search yourself for the ZONE_POLYGON by using the @{#ZONE_POLYGON.FindByName}() method. -- In this example, `local PolygonZone = ZONE_POLYGON:FindByName( "DefenseZone" )` would return the ZONE_POLYGON object -- that was created at mission startup, and reference it into the `PolygonZone` local object. --- +-- -- Mission `ZON-510` shows a demonstration of this feature or method. --- +-- -- This is especially handy if you want to quickly setup a SET_ZONE... -- So when you would declare `local SetZone = SET_ZONE:New():FilterPrefixes( "Defense" ):FilterStart()`, -- then SetZone would contain the ZONE_POLYGON object `DefenseZone` as part of the zone collection, --- without much scripting overhead!!! --- +-- without much scripting overhead! +-- -- @field #ZONE_POLYGON ZONE_POLYGON = { ClassName="ZONE_POLYGON", @@ -1868,7 +2104,7 @@ end -- @param #string ZoneName The name of the polygon zone. -- @return #ZONE_POLYGON self function ZONE_POLYGON:FindByName( ZoneName ) - + local ZoneFound = _DATABASE:FindZone( ZoneName ) return ZoneFound end @@ -1877,85 +2113,85 @@ do -- ZONE_AIRBASE --- @type ZONE_AIRBASE -- @extends #ZONE_RADIUS - - + + --- The ZONE_AIRBASE class defines by a zone around a @{Wrapper.Airbase#AIRBASE} with a radius. -- This class implements the inherited functions from @{Core.Zone#ZONE_RADIUS} taking into account the own zone format and properties. - -- + -- -- @field #ZONE_AIRBASE ZONE_AIRBASE = { ClassName="ZONE_AIRBASE", } - - - + + + --- Constructor to create a ZONE_AIRBASE instance, taking the zone name, a zone @{Wrapper.Airbase#AIRBASE} and a radius. -- @param #ZONE_AIRBASE self -- @param #string AirbaseName Name of the airbase. -- @param DCS#Distance Radius (Optional)The radius of the zone in meters. Default 4000 meters. -- @return #ZONE_AIRBASE self function ZONE_AIRBASE:New( AirbaseName, Radius ) - + Radius=Radius or 4000 - + local Airbase = AIRBASE:FindByName( AirbaseName ) - + local self = BASE:Inherit( self, ZONE_RADIUS:New( AirbaseName, Airbase:GetVec2(), Radius ) ) - + self._.ZoneAirbase = Airbase self._.ZoneVec2Cache = self._.ZoneAirbase:GetVec2() - + -- Zone objects are added to the _DATABASE and SET_ZONE objects. _EVENTDISPATCHER:CreateEventNewZone( self ) - + return self end - + --- Get the airbase as part of the ZONE_AIRBASE object. -- @param #ZONE_AIRBASE self -- @return Wrapper.Airbase#AIRBASE The airbase. function ZONE_AIRBASE:GetAirbase() return self._.ZoneAirbase - end - + end + --- Returns the current location of the @{Wrapper.Group}. -- @param #ZONE_AIRBASE self -- @return DCS#Vec2 The location of the zone based on the @{Wrapper.Group} location. function ZONE_AIRBASE:GetVec2() self:F( self.ZoneName ) - + local ZoneVec2 = nil - + if self._.ZoneAirbase:IsAlive() then ZoneVec2 = self._.ZoneAirbase:GetVec2() self._.ZoneVec2Cache = ZoneVec2 else ZoneVec2 = self._.ZoneVec2Cache end - + self:T( { ZoneVec2 } ) - + return ZoneVec2 end - + --- Returns a random location within the zone of the @{Wrapper.Group}. -- @param #ZONE_AIRBASE self -- @return DCS#Vec2 The random location of the zone based on the @{Wrapper.Group} location. function ZONE_AIRBASE:GetRandomVec2() self:F( self.ZoneName ) - + local Point = {} local Vec2 = self._.ZoneAirbase:GetVec2() - + local angle = math.random() * math.pi*2; Point.x = Vec2.x + math.cos( angle ) * math.random() * self:GetRadius(); Point.y = Vec2.y + math.sin( angle ) * math.random() * self:GetRadius(); - + self:T( { Point } ) - + return Point end - + --- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. -- @param #ZONE_AIRBASE self -- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. @@ -1963,11 +2199,11 @@ do -- ZONE_AIRBASE -- @return Core.Point#POINT_VEC2 The @{Core.Point#POINT_VEC2} object reflecting the random 3D location within the zone. function ZONE_AIRBASE:GetRandomPointVec2( inner, outer ) self:F( self.ZoneName, inner, outer ) - + local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() ) - + self:T3( { PointVec2 } ) - + return PointVec2 end diff --git a/Moose Development/Moose/DCS.lua b/Moose Development/Moose/DCS.lua index 92052d5cf..01661a678 100644 --- a/Moose Development/Moose/DCS.lua +++ b/Moose Development/Moose/DCS.lua @@ -295,6 +295,17 @@ do -- country -- @field QATAR -- @field OMAN -- @field UNITED_ARAB_EMIRATES + -- @field SOUTH_AFRICA + -- @field CUBA + -- @field PORTUGAL + -- @field GDR + -- @field LEBANON + -- @field CJTF_BLUE + -- @field CJTF_RED + -- @field UN_PEACEKEEPERS + -- @field Argentinia + -- @field Cyprus + -- @field Slovenia country = {} --#country diff --git a/Moose Development/Moose/Modules.lua b/Moose Development/Moose/Modules.lua index 9c84d7f15..0774e005e 100644 --- a/Moose Development/Moose/Modules.lua +++ b/Moose Development/Moose/Modules.lua @@ -2,6 +2,7 @@ __Moose.Include( 'Scripts/Moose/Utilities/Enums.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Routines.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Utils.lua' ) __Moose.Include( 'Scripts/Moose/Utilities/Profiler.lua' ) +__Moose.Include( 'Scripts/Moose/Utilities/Templates.lua' ) __Moose.Include( 'Scripts/Moose/Core/Base.lua' ) __Moose.Include( 'Scripts/Moose/Core/UserFlag.lua' ) diff --git a/Moose Development/Moose/Utilities/Templates.lua b/Moose Development/Moose/Utilities/Templates.lua new file mode 100644 index 000000000..182925689 --- /dev/null +++ b/Moose Development/Moose/Utilities/Templates.lua @@ -0,0 +1,612 @@ +--- **Utils** Templates +-- +-- DCS unit templates +-- +-- @module Utilities.Templates +-- @image MOOSE.JPG + +--- TEMPLATE class. +-- @type TEMPLATE +-- @field #string ClassName Name of the class. + +--- *Templates* +-- +-- === +-- +-- ![Banner Image](..\Presentations\Utilities\PROFILER_Main.jpg) +-- +-- Get DCS templates from thin air. +-- +-- # Ground Units +-- +-- Ground units. +-- +-- # Naval Units +-- +-- Ships are not implemented yet. +-- +-- # Aircraft +-- +-- ## Airplanes +-- +-- Airplanes are not implemented yet. +-- +-- ## Helicopters +-- +-- Helicopters are not implemented yet. +-- +-- @field #TEMPLATE +TEMPLATE = { + ClassName = "TEMPLATE", + Ground = {}, + Naval = {}, + Airplane = {}, + Helicopter = {}, +} + +--- Ground unit type names. +-- @type TEMPLATE.TypeGround +-- @param #string InfantryAK +TEMPLATE.TypeGround={ + InfantryAK="Infantry AK", + ParatrooperAKS74="Paratrooper AKS-74", + ParatrooperRPG16="Paratrooper RPG-16", + SoldierWWIIUS="soldier_wwii_us", + InfantryM248="Infantry M249", + SoldierM4="Soldier M4", +} + +--- Naval unit type names. +-- @type TEMPLATE.TypeNaval +-- @param #string Ticonderoga +TEMPLATE.TypeNaval={ + Ticonderoga="TICONDEROG", +} + +--- Rotary wing unit type names. +-- @type TEMPLATE.TypeAirplane +-- @param #string A10C +TEMPLATE.TypeAirplane={ + A10C="A-10C", +} + +--- Rotary wing unit type names. +-- @type TEMPLATE.TypeHelicopter +-- @param #string AH1W +TEMPLATE.TypeHelicopter={ + AH1W="AH-1W", +} + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Ground Template +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get template for ground units. +-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`. +-- @param #string GroupName Name of the spawned group. **Must be unique!** +-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to. +-- @param DCS#Vec3 Vec3 Position of the group and the first unit. +-- @param #number Nunits Number of units. Default 1. +-- @param #number Radius Spawn radius for additonal units in meters. Default 50 m. +-- @return #table Template Template table. +function TEMPLATE.GetGround(TypeName, GroupName, CountryID, Vec3, Nunits, Radius) + + -- Defaults. + TypeName=TypeName or TEMPLATE.TypeGround.SoldierM4 + GroupName=GroupName or "Ground-1" + CountryID=CountryID or country.id.USA + Vec3=Vec3 or {x=0, y=0, z=0} + Nunits=Nunits or 1 + Radius=Radius or 50 + + + -- Get generic template. + local template=UTILS.DeepCopy(TEMPLATE.GenericGround) + + -- Set group name. + template.name=GroupName + + -- These are additional entries required by the MOOSE _DATABASE:Spawn() function. + template.CountryID=CountryID + template.CoalitionID=coalition.getCountryCoalition(template.CountryID) + template.CategoryID=Unit.Category.GROUND_UNIT + + -- Set first unit. + template.units[1].type=TypeName + template.units[1].name=GroupName.."-1" + + if Vec3 then + TEMPLATE.SetPositionFromVec3(template, Vec3) + end + + TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius) + + return template +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Naval Template +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get template for ground units. +-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`. +-- @param #string GroupName Name of the spawned group. **Must be unique!** +-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to. +-- @param DCS#Vec3 Vec3 Position of the group and the first unit. +-- @param #number Nunits Number of units. Default 1. +-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m. +-- @return #table Template Template table. +function TEMPLATE.GetNaval(TypeName, GroupName, CountryID, Vec3, Nunits, Radius) + + -- Defaults. + TypeName=TypeName or TEMPLATE.TypeNaval.Ticonderoga + GroupName=GroupName or "Naval-1" + CountryID=CountryID or country.id.USA + Vec3=Vec3 or {x=0, y=0, z=0} + Nunits=Nunits or 1 + Radius=Radius or 500 + + + -- Get generic template. + local template=UTILS.DeepCopy(TEMPLATE.GenericNaval) + + -- Set group name. + template.name=GroupName + + -- These are additional entries required by the MOOSE _DATABASE:Spawn() function. + template.CountryID=CountryID + template.CoalitionID=coalition.getCountryCoalition(template.CountryID) + template.CategoryID=Unit.Category.SHIP + + -- Set first unit. + template.units[1].type=TypeName + template.units[1].name=GroupName.."-1" + + if Vec3 then + TEMPLATE.SetPositionFromVec3(template, Vec3) + end + + TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius) + + return template +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Aircraft Template +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Get template for fixed wing units. +-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`. +-- @param #string GroupName Name of the spawned group. **Must be unique!** +-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to. +-- @param DCS#Vec3 Vec3 Position of the group and the first unit. +-- @param #number Nunits Number of units. Default 1. +-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m. +-- @return #table Template Template table. +function TEMPLATE.GetAirplane(TypeName, GroupName, CountryID, Vec3, Nunits, Radius) + + -- Defaults. + TypeName=TypeName or TEMPLATE.TypeAirplane.A10C + GroupName=GroupName or "Airplane-1" + CountryID=CountryID or country.id.USA + Vec3=Vec3 or {x=0, y=1000, z=0} + Nunits=Nunits or 1 + Radius=Radius or 100 + + local template=TEMPLATE._GetAircraft(true, TypeName, GroupName, CountryID, Vec3, Nunits, Radius) + + return template +end + +--- Get template for fixed wing units. +-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`. +-- @param #string GroupName Name of the spawned group. **Must be unique!** +-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to. +-- @param DCS#Vec3 Vec3 Position of the group and the first unit. +-- @param #number Nunits Number of units. Default 1. +-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m. +-- @return #table Template Template table. +function TEMPLATE.GetHelicopter(TypeName, GroupName, CountryID, Vec3, Nunits, Radius) + + -- Defaults. + TypeName=TypeName or TEMPLATE.TypeHelicopter.AH1W + GroupName=GroupName or "Helicopter-1" + CountryID=CountryID or country.id.USA + Vec3=Vec3 or {x=0, y=500, z=0} + Nunits=Nunits or 1 + Radius=Radius or 100 + + -- Limit unis to 4. + Nunits=math.min(Nunits, 4) + + local template=TEMPLATE._GetAircraft(false, TypeName, GroupName, CountryID, Vec3, Nunits, Radius) + + return template +end + + +--- Get template for aircraft units. +-- @param #boolean Airplane If true, this is a fixed wing. Else, rotary wing. +-- @param #string TypeName Type name of the unit(s) in the groups. See `TEMPLATE.Ground`. +-- @param #string GroupName Name of the spawned group. **Must be unique!** +-- @param #number CountryID Country ID. Default `country.id.USA`. Coalition is automatically determined by the one the country belongs to. +-- @param DCS#Vec3 Vec3 Position of the group and the first unit. +-- @param #number Nunits Number of units. Default 1. +-- @param #number Radius Spawn radius for additonal units in meters. Default 500 m. +-- @return #table Template Template table. +function TEMPLATE._GetAircraft(Airplane, TypeName, GroupName, CountryID, Vec3, Nunits, Radius) + + -- Defaults. + TypeName=TypeName + GroupName=GroupName or "Aircraft-1" + CountryID=CountryID or country.id.USA + Vec3=Vec3 or {x=0, y=0, z=0} + Nunits=Nunits or 1 + Radius=Radius or 100 + + -- Get generic template. + local template=UTILS.DeepCopy(TEMPLATE.GenericAircraft) + + -- Set group name. + template.name=GroupName + + -- These are additional entries required by the MOOSE _DATABASE:Spawn() function. + template.CountryID=CountryID + template.CoalitionID=coalition.getCountryCoalition(template.CountryID) + if Airplane then + template.CategoryID=Unit.Category.AIRPLANE + else + template.CategoryID=Unit.Category.HELICOPTER + end + + -- Set first unit. + template.units[1].type=TypeName + template.units[1].name=GroupName.."-1" + + -- Set position. + if Vec3 then + TEMPLATE.SetPositionFromVec3(template, Vec3) + end + + -- Set number of units. + TEMPLATE.SetUnits(template, Nunits, COORDINATE:NewFromVec3(Vec3), Radius) + + return template +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Misc Functions +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +--- Set the position of the template. +-- @param #table Template The template to be modified. +-- @param DCS#Vec2 Vec2 2D Position vector with x and y components of the group. +function TEMPLATE.SetPositionFromVec2(Template, Vec2) + + Template.x=Vec2.x + Template.y=Vec2.y + + for _,unit in pairs(Template.units) do + unit.x=Vec2.x + unit.y=Vec2.y + end + + Template.route.points[1].x=Vec2.x + Template.route.points[1].y=Vec2.y + Template.route.points[1].alt=0 --TODO: Use land height. + +end + +--- Set the position of the template. +-- @param #table Template The template to be modified. +-- @param DCS#Vec3 Vec3 Position vector of the group. +function TEMPLATE.SetPositionFromVec3(Template, Vec3) + + local Vec2={x=Vec3.x, y=Vec3.z} + + TEMPLATE.SetPositionFromVec2(Template, Vec2) + +end + +--- Set the position of the template. +-- @param #table Template The template to be modified. +-- @param #number N Total number of units in the group. +-- @param Core.Point#COORDINATE Coordinate Position of the first unit. +-- @param #number Radius Radius in meters to randomly place the additional units. +function TEMPLATE.SetUnits(Template, N, Coordinate, Radius) + + local units=Template.units + + local unit1=units[1] + + local Vec3=Coordinate:GetVec3() + + unit1.x=Vec3.x + unit1.y=Vec3.z + unit1.alt=Vec3.y + + for i=2,N do + units[i]=UTILS.DeepCopy(unit1) + end + + for i=1,N do + local unit=units[i] + unit.name=string.format("%s-%d", Template.name, i) + if i>1 then + local vec2=Coordinate:GetRandomCoordinateInRadius(Radius, 5):GetVec2() + unit.x=vec2.x + unit.y=vec2.y + unit.alt=unit1.alt + end + end + +end + +--- Set the position of the template. +-- @param #table Template The template to be modified. +-- @param Wrapper.Airbase#AIRBASE AirBase The airbase where the aircraft are spawned. +-- @param #table ParkingSpots List of parking spot IDs. Every unit needs one! +-- @param #boolean EngineOn If true, aircraft are spawned hot. +function TEMPLATE.SetAirbase(Template, AirBase, ParkingSpots, EngineOn) + + -- Airbase ID. + local AirbaseID=AirBase:GetID() + + -- Spawn point. + local point=Template.route.points[1] + + -- Set ID. + if AirBase:IsAirdrome() then + point.airdromeId=AirbaseID + else + point.helipadId=AirbaseID + point.linkUnit=AirbaseID + end + + if EngineOn then + point.action=COORDINATE.WaypointAction.FromParkingAreaHot + point.type=COORDINATE.WaypointType.TakeOffParkingHot + else + point.action=COORDINATE.WaypointAction.FromParkingArea + point.type=COORDINATE.WaypointType.TakeOffParking + end + + for i,unit in ipairs(Template.units) do + unit.parking_id=ParkingSpots[i] + end + +end + +--- Add a waypoint. +-- @param #table Template The template to be modified. +-- @param #table Waypoint Waypoint table. +function TEMPLATE.AddWaypoint(Template, Waypoint) + + table.insert(Template.route.points, Waypoint) + +end + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Generic Ground Template +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +TEMPLATE.GenericGround= +{ + ["visible"] = false, + ["tasks"] = {}, -- end of ["tasks"] + ["uncontrollable"] = false, + ["task"] = "Ground Nothing", + ["route"] = + { + ["spans"] = {}, -- end of ["spans"] + ["points"] = + { + [1] = + { + ["alt"] = 0, + ["type"] = "Turning Point", + ["ETA"] = 0, + ["alt_type"] = "BARO", + ["formation_template"] = "", + ["y"] = 0, + ["x"] = 0, + ["ETA_locked"] = true, + ["speed"] = 0, + ["action"] = "Off Road", + ["task"] = + { + ["id"] = "ComboTask", + ["params"] = + { + ["tasks"] = + { + }, -- end of ["tasks"] + }, -- end of ["params"] + }, -- end of ["task"] + ["speed_locked"] = true, + }, -- end of [1] + }, -- end of ["points"] + }, -- end of ["route"] + ["groupId"] = nil, + ["hidden"] = false, + ["units"] = + { + [1] = + { + ["transportable"] = + { + ["randomTransportable"] = false, + }, -- end of ["transportable"] + ["skill"] = "Average", + ["type"] = "Infantry AK", + ["unitId"] = nil, + ["y"] = 0, + ["x"] = 0, + ["name"] = "Infantry AK-47 Rus", + ["heading"] = 0, + ["playerCanDrive"] = false, + }, -- end of [1] + }, -- end of ["units"] + ["y"] = 0, + ["x"] = 0, + ["name"] = "Infantry AK-47 Rus", + ["start_time"] = 0, +} + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Generic Ship Template +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +TEMPLATE.GenericNaval= +{ + ["visible"] = false, + ["tasks"] = {}, -- end of ["tasks"] + ["uncontrollable"] = false, + ["route"] = + { + ["points"] = + { + [1] = + { + ["alt"] = 0, + ["type"] = "Turning Point", + ["ETA"] = 0, + ["alt_type"] = "BARO", + ["formation_template"] = "", + ["y"] = 0, + ["x"] = 0, + ["ETA_locked"] = true, + ["speed"] = 0, + ["action"] = "Turning Point", + ["task"] = + { + ["id"] = "ComboTask", + ["params"] = + { + ["tasks"] = + { + }, -- end of ["tasks"] + }, -- end of ["params"] + }, -- end of ["task"] + ["speed_locked"] = true, + }, -- end of [1] + }, -- end of ["points"] + }, -- end of ["route"] + ["groupId"] = nil, + ["hidden"] = false, + ["units"] = + { + [1] = + { + ["transportable"] = + { + ["randomTransportable"] = false, + }, -- end of ["transportable"] + ["skill"] = "Average", + ["type"] = "TICONDEROG", + ["unitId"] = nil, + ["y"] = 0, + ["x"] = 0, + ["name"] = "Naval-1-1", + ["heading"] = 0, + ["modulation"] = 0, + ["frequency"] = 127500000, + }, -- end of [1] + }, -- end of ["units"] + ["y"] = 0, + ["x"] = 0, + ["name"] = "Naval-1", + ["start_time"] = 0, +} + +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +-- Generic Aircraft Template +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +TEMPLATE.GenericAircraft= +{ + ["groupId"] = nil, + ["name"] = "Rotary-1", + ["uncontrolled"] = false, + ["hidden"] = false, + ["task"] = "Nothing", + ["y"] = 0, + ["x"] = 0, + ["start_time"] = 0, + ["communication"] = true, + ["radioSet"] = false, + ["frequency"] = 127.5, + ["modulation"] = 0, + ["taskSelected"] = true, + ["tasks"] = {}, -- end of ["tasks"] + ["route"] = + { + ["points"] = + { + [1] = + { + ["y"] = 0, + ["x"] = 0, + ["alt"] = 1000, + ["alt_type"] = "BARO", + ["action"] = "Turning Point", + ["type"] = "Turning Point", + ["airdromeId"] = nil, + ["task"] = + { + ["id"] = "ComboTask", + ["params"] = + { + ["tasks"] = {}, -- end of ["tasks"] + }, -- end of ["params"] + }, -- end of ["task"] + ["ETA"] = 0, + ["ETA_locked"] = true, + ["speed"] = 100, + ["speed_locked"] = true, + ["formation_template"] = "", + }, -- end of [1] + }, -- end of ["points"] + }, -- end of ["route"] + ["units"] = + { + [1] = + { + ["name"] = "Rotary-1-1", + ["unitId"] = nil, + ["type"] = "AH-1W", + ["onboard_num"] = "050", + ["livery_id"] = "USA X Black", + ["skill"] = "High", + ["ropeLength"] = 15, + ["speed"] = 0, + ["x"] = 0, + ["y"] = 0, + ["alt"] = 10, + ["alt_type"] = "BARO", + ["heading"] = 0, + ["psi"] = 0, + ["parking"] = nil, + ["parking_id"] = nil, + ["payload"] = + { + ["pylons"] = {}, -- end of ["pylons"] + ["fuel"] = "1250.0", + ["flare"] = 30, + ["chaff"] = 30, + ["gun"] = 100, + }, -- end of ["payload"] + ["callsign"] = + { + [1] = 2, + [2] = 1, + [3] = 1, + ["name"] = "Springfield11", + }, -- end of ["callsign"] + }, -- end of [1] + }, -- end of ["units"] +} +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + diff --git a/Moose Development/Moose/Utilities/Utils.lua b/Moose Development/Moose/Utilities/Utils.lua index 1e9c2597b..c5ade4b22 100644 --- a/Moose Development/Moose/Utilities/Utils.lua +++ b/Moose Development/Moose/Utilities/Utils.lua @@ -668,6 +668,17 @@ function UTILS.GetMarkID() end +--- Remove an object (marker, circle, arrow, text, quad, ...) on the F10 map. +-- @param #number MarkID Unique ID of the object. +-- @param #number Delay (Optional) Delay in seconds before the mark is removed. +function UTILS.RemoveMark(MarkID, Delay) + if Delay and Delay>0 then + TIMER:New(UTILS.RemoveMark, MarkID):Start(Delay) + else + trigger.action.removeMark(MarkID) + end +end + -- Test if a Vec2 is in a radius of another Vec2 function UTILS.IsInRadius( InVec2, Vec2, Radius ) diff --git a/Moose Development/Moose/Wrapper/Group.lua b/Moose Development/Moose/Wrapper/Group.lua index 1a2f00fa8..263b3a0ec 100644 --- a/Moose Development/Moose/Wrapper/Group.lua +++ b/Moose Development/Moose/Wrapper/Group.lua @@ -2551,9 +2551,10 @@ do -- Players end ---- GROUND - Switch on/off radar emissions +--- GROUND - Switch on/off radar emissions for the group. -- @param #GROUP self --- @param #boolean switch +-- @param #boolean switch If true, emission is enabled. If false, emission is disabled. +-- @return #GROUP self function GROUP:EnableEmission(switch) self:F2( self.GroupName ) local switch = switch or false @@ -2566,6 +2567,7 @@ function GROUP:EnableEmission(switch) end + return self end --do -- Smoke diff --git a/Moose Development/Moose/Wrapper/Unit.lua b/Moose Development/Moose/Wrapper/Unit.lua index 65199d97d..eb6d472d8 100644 --- a/Moose Development/Moose/Wrapper/Unit.lua +++ b/Moose Development/Moose/Wrapper/Unit.lua @@ -761,40 +761,6 @@ function UNIT:GetFuel() return nil end ---- Sets the passed group or unit objects radar emitters on or off. Can be used on sam sites for example to shut down the radar without setting AI off or changing the alarm state. --- @param #UNIT self --- @param #boolean Switch If `true` or `nil`, emission is enabled. If `false`, emission is turned off. --- @return #UNIT self -function UNIT:SetEmission(Switch) - - if Switch==nil then - Switch=true - end - - local DCSUnit = self:GetDCSObject() - - if DCSUnit then - DCSUnit:enableEmission(Switch) - end - - return self -end - ---- Sets the passed group or unit objects radar emitters ON. Can be used on sam sites for example to shut down the radar without setting AI off or changing the alarm state. --- @param #UNIT self --- @return #UNIT self -function UNIT:EnableEmission() - self:SetEmission(true) - return self -end - ---- Sets the passed group or unit objects radar emitters OFF. Can be used on sam sites for example to shut down the radar without setting AI off or changing the alarm state. --- @param #UNIT self --- @return #UNIT self -function UNIT:DisableEmission() - self:SetEmission(false) - return self -end --- Returns a list of one @{Wrapper.Unit}. -- @param #UNIT self @@ -1429,9 +1395,10 @@ function UNIT:GetTemplateFuel() return nil end ---- GROUND - Switch on/off radar emissions. +--- GROUND - Switch on/off radar emissions of a unit. -- @param #UNIT self --- @param #boolean switch +-- @param #boolean switch If true, emission is enabled. If false, emission is disabled. +-- @return #UNIT self function UNIT:EnableEmission(switch) self:F2( self.UnitName ) @@ -1445,4 +1412,5 @@ function UNIT:EnableEmission(switch) end + return self end diff --git a/Moose Setup/Moose.files b/Moose Setup/Moose.files index da76cfd49..a99e9065e 100644 --- a/Moose Setup/Moose.files +++ b/Moose Setup/Moose.files @@ -3,6 +3,7 @@ Utilities/Routines.lua Utilities/Utils.lua Utilities/Enums.lua Utilities/Profiler.lua +Utilities/Templates.lua Core/Base.lua Core/UserFlag.lua