FlightControl-Master / MOOSE

Mission Object Oriented Scripting Environment (MOOSE) for lua mission scripting design in DCS World
http://flightcontrol-master.github.io/MOOSE/
GNU General Public License v3.0
291 stars 96 forks source link

Infrared Beacons/Markers for Units #2129

Closed fbrinker closed 1 month ago

fbrinker commented 4 months ago

Hey everyone,

I've scripted IR Beacons/Markers for Units. They need some fine-tuning and error handling, but I'm thinking of implementing them in Moose to help people using them too... I'm using Timers and some Wrapper classes.

https://youtu.be/dUNseYJOewo

Since I'm new to contributing to Moose, what would be a good way to implement this? On a unit wrapper level like unit:IRMarkerEnable() (and group:IRMarkersEnable(), or should this be a new "Component", passing Units into the IRMarker class like IRMarker:New(unit)?

Or should this be an additional script I'm sharing in the DCS forums and not get integrated into Moose at all?

Best regards, Florian

Applevangelist commented 2 months ago

Apologies for ignoring that so far. I would like to see the code, the we can decide where it makes sense to add that. Thanks!

Applevangelist commented 1 month ago

Any updates on this @fbrinker Florian?

fbrinker commented 1 month ago

Oh sorry, missed the first notification :)

Since I have no idea where to place this right now, see my class in the following code, instead of a proper Fork:

--- **Functional** - IRMarker for units.
--
-- **Main Features:**
--
--    * Create blinking infrared markers for units
--    * Easy to set up
--
-- ===
--
-- @module Functional.IRMarker
-- @image Functional_IRMarker.JPG

--- IRMarker class.
-- @type IRMarker
-- @field #UNIT unit Unit using the IR marker
-- @field #GROUP group Group of units with an IR marker
-- @field #IRMarker[] groupMarkers List of IRMarkers of all units inside the group
-- @field #Spot spot Spot object for the laser
-- @field #TIMER timer Timer object to control the blinking
-- @extends Core.Base#BASE

IRMarker = {}

--- Create a new IRMarker object for the given unit
function IRMarker:NewUnit(unit)
    local self = BASE:Inherit(self, BASE:New())

    self.unit = unit
    self.group = nil
    self.groupMarkers = {}
    self.spot = nil
    self.timer = nil

    return self
end

--- Create a new IRMarker object for the given unit
function IRMarker:NewGroup(group)
    local self = BASE:Inherit(self, BASE:New())

    self.unit = nil
    self.group = group
    self.groupMarkers = {}
    self.spot = nil
    self.timer = nil

    local units = group:GetUnits()
    for i = 1, #units do
        self.groupMarkers[#self.groupMarkers + 1] = IRMarker:NewUnit(units[i])
    end

    return self
end

-- Enable the IR marker
function IRMarker:Enable()
    if (self.group ~= nil) then
        self:EnableGroup()
        return
    end

    self.timer = TIMER:New(IRMarker._markerBlink, self);
    self.timer:Start(nil, 1 - math.random(1, 5) / 10 / 2) -- start randomized
end

-- Disable the IR marker
function IRMarker:Disable()
    if (self.group ~= nil) then
        self:DisableGroup()
        return
    end

    self.spot = nil
    if self.timer ~= nil then
        self.timer:Stop()
    end
end

function IRMarker:EnableGroup()
    for i = 1, #self.groupMarkers do
        self.groupMarkers[i]:Enable()
    end
end

function IRMarker:DisableGroup()
    for i = 1, #self.groupMarkers do
        self.groupMarkers[i]:Disable()
    end
end

-- This method is called by the scheduler after enabling the IR marker
function IRMarker._markerBlink(markerObject)
    if markerObject.unit == nil or markerObject.unit:IsAlive() == nil then
        markerObject:Disable()
        return
    end

    markerObject.timer.dT = 1 - (math.random(1, 2) / 10 / 2) -- randomize the blinking by a small amount

    local _, _, unitBBHeight, _ = markerObject.unit:GetObjectSize()
    local unitPos = markerObject.unit:GetPositionVec3()

    markerObject.spot = Spot.createInfraRed(
        markerObject.unit.DCSUnit,
        { x = 0, y = (unitBBHeight + 0.1), z = 0 },
        { x = unitPos.x, y = (unitPos.y + unitBBHeight), z = unitPos.z }
    )

    local offTimer = TIMER:New(function() markerObject.spot:destroy() end)
    offTimer:Start(0.1)
end

Usage examples:

-- single units
IRMarker:NewUnit(UNIT:FindByName("TestIR-1")):Enable()
IRMarker:NewUnit(UNIT:FindByName("TestIR-2")):Enable()
IRMarker:NewUnit(UNIT:FindByName("TestIR-3")):Enable()
IRMarker:NewUnit(UNIT:FindByName("TestIR-4")):Disable()
IRMarker:NewUnit(UNIT:FindByName("TestIR-5")):Enable()

local test = IRMarker:NewUnit(UNIT:FindByName("TestIR-V-1"))
test:Enable()

-- entire group
IRMarker:NewGroup(GROUP:FindByName("TestIR-3")):Enable()

:)

Applevangelist commented 1 month ago

Ok interesting. I’d possibly rather add this to CONTROLLABLE then it is inherited by units and groups and the like. Then the new and start need to go together, to make it user-friendly. Small criticism : The setup lacks some basics, like all caps class name, the ClassName field is missing, function names are somewhat ambiguous and intellisense docu on the functions is zero. But can be worked upon.

fbrinker commented 1 month ago

Thank you for your fast feedback.

CONTROLLABLE is a nice idea, could fit there very well, esp. since it is only a small script. I'll give it a try when I have some time on my hands and open a pull request.

Applevangelist commented 1 month ago

:)

Applevangelist commented 1 month ago

I took the liberty to adjust and extent your code to work with Controllable

fbrinker commented 1 month ago

Awesome, thank you :)