Jump to content

Recommended Posts

Posted

Version 1.5.1 - Minor Update - 20231130

When DCS version 2.9 update 2 dropped a couple of days ago, it contained some unobtrusive correction that had wide-ranging consequences for many missions: ED got around to fix a bug in their Mission Scripting Environment (MSE) that was so long-standing, that over the years many scripters had come to rely on its presence. Now that it was gone, and if you (often unknowingly) relied on it, your scripts could start behaving strangely but would not create an error. 

I'm going through the modules on a case-by-case basis and harden the code when I come across lines that may be affected; so far, it seems, I got mostly lucky (the usual suspects being helicopter troop handlers), yet I'm sure that there will be some cases where this bug-fix will rear its angelic face and cause trouble.

image.png

Other than that, I've finally found the time to document TDZ, another drop-in module for mission creators who want to create training missions, and I'm continuing the slow journey to upgrade all modules to dmlZones (the OOP variant of cfxZones). And supporting me is the DML debugger, which I now include in an experimental 2.0 version. It has a couple of really nice (and exceedingly powerful) new features, but documenting them will take some more time. 

So here's the contents of today's update in detail:

Manual
    - Main
        - updates to chapters 
        - Landing Lessons demo documentation 
        - PlayerScore to Win demo documentation 
        
    - QuickRef 
        - updates to modules 
        


Demos 
    - Landing Lessons (TDZ) - new 
    - PlayerScore to Win - new 

 

Modules
    - autoCSAR 2.0.0 
        - update to dmlZones, OOP 
        - clean-up
    
    - PlayerScore 
        - update to OOP 
        - added redScore# direct output 
        - added blueScore# direct output 
    
    - cfxZones 4.0.10 
        - getBoolFromZoneProperty  also understands "off" and "on"
        
    - civAir 3.0.1 
        - optional protest message when someone shoots down a civilian flight
    
    - CSARmanager 2.3.2
        - DCS 2.9 fix fix
        
    - dcsCommon 2.9.8 
        - createSimpleRoutePointData() expansion 
        - DCS 2.9 hardening for isSceneryObject() 
        
    - pulseFlag 2.0.1
        - activateZoneFlag  fix
    
    - TDZ 1.0.1
        - initial release 
        
    - THE DEBUGGER 2.0 exp 
        - experimental 
 

Enjoy,

-ch

 

  • Thanks 1
Posted
5 hours ago, cfrag said:

I've finally found the time to document TDZ, another drop-in module for mission creators who want to create training missions

Hi,cflag san

Can you make it possible to specify the information used there with Property for the new TDZ, which is obtaining information about nearby runways? It would be great if this could be used for a Private Airfield created with Static Object. It might be a special case, so you may not see the significance of addressing it, but if the information obtained is limited to runway square information, height, direction, etc., please consider specifying it from Property.

P.S. An Error Dialog was displayed when there was no runway nearby. It wasn't a message on the regular screen. I have loaded dcsCommon and cfxZones.

I want to use it on a runway like this.

DCS 2023-11-30 22-52-30.jpg

Posted
24 minutes ago, tsuyopooh said:

Can you make it possible to specify the information used there with Property for the new TDZ

Well, that's quite an edge case. I should be able to create a script for you that takes the runway's center as x & y (from the trigger zone itself), and heading, width and length from attributes to set this up. Let me try to set something up for you.

26 minutes ago, tsuyopooh said:

An Error Dialog was displayed when there was no runway nearby

Usually, there's always a runway around unless you modified the map to remove all airfields - the TDZ simply latches on to the closest airfield, and within that airfield, to the closest runway. Can you attach a screenshot of the error message so I can investigate?

 

Posted
4 hours ago, tsuyopooh said:

P.S. An Error Dialog was displayed when there was no runway nearby

It seems that I stupidly do not ignore FARPs, and that you may have a FARP closer to the TDZ than the next airfield. That's fixed now, and I see if I can give you the ability to put TDZ wherever you please

Posted
4 hours ago, tsuyopooh said:

It would be great if this could be used for a Private Airfield created with Static Object.

Done. Please see the example mission. You now can "manually" create a runway by adding the 'manual = true' attribute, and then supplying "length", "width", and "course" (in degrees). Plus, the script will no longer get irritated by nearby FARPS.

Spoiler

tdz = {}
tdz.version = "1.0.2"
tdz.requiredLibs = {
    "dcsCommon", -- always
    "cfxZones", -- Zones, of course 
}
--[[--
VERSION HISTORY 
 1.0.0 - Initial version 
 1.0.1 - visible 
         rwFill, rwFrame 
         tdzFill, tdzFrame 
         extend, expand 
         left, right 
         multiple zone support
         hops detection improvement
         helo attribute 
 1.0.2 - manual placement option 
         filters FARPs 

--]]--

tdz.allTdz = {}
tdz.watchlist = {}
tdz.watching = false 
tdz.timeoutAfter = 120 -- seconds.
-- 
-- rwy draw procs
--
function tdz.rotateXZPolyInRads(thePoly, rads)
    local c = math.cos(rads)
    local s = math.sin(rads)
    for idx, p in pairs(thePoly) do     
        local nx = p.x * c - p.z * s
        local nz = p.x * s + p.z * c
        p.x = nx
        p.z = nz 
    end 
end

function tdz.rotateXZPolyAroundCenterInRads(thePoly, center, rads)
    local negCtr = {x = -center.x, y = -center.y, z = -center.z}
    tdz.translatePoly(thePoly, negCtr)
    tdz.rotateXZPolyInRads(thePoly, rads)
    tdz.translatePoly(thePoly, center)
end

--function tdz.rotateXZPolyAroundCenterInDegrees(thePoly, center, degrees)
--    tdz.rotateXZPolyAroundCenterInRads(thePoly, center, degrees * 0.0174533)
--end

function tdz.translatePoly(thePoly, v) -- straight rot, translate to 0 first
    for idx, aPoint in pairs(thePoly) do 
        aPoint.x = aPoint.x + v.x 
        if aPoint.y then aPoint.y = aPoint.y + v.y end 
        if aPoint.z then aPoint.z = aPoint.z + v.z end  
    end
end

function tdz.calcTDZone(name, center, length, width, rads, a, b)
    if not a then a = 0 end 
    if not b then b = 1 end 
    -- create a 0-rotated centered poly 
    local poly = {}
    local half = length / 2
    local leftEdge = -half
    poly[1] = { x = leftEdge + a * length, z = width / 2, y = 0}
    poly[2] = { x = leftEdge + b * length, z = width / 2, y = 0}
    poly[3] = { x = leftEdge + b * length, z = -width / 2, y = 0}
    poly[4] = { x = leftEdge + a * length, z = -width / 2, y = 0}
    -- move it to center in map 
    tdz.translatePoly(poly, center)    
    -- rotate it 
    tdz.rotateXZPolyAroundCenterInRads(poly, center, rads)
    -- make it a dml zone 
    local theNewZone = cfxZones.createSimplePolyZone(name, center, poly)
    return theNewZone
end

--
-- create a tdz
--
function tdz.createTDZ(theZone)
    local p = theZone:getPoint()
    local theBase = dcsCommon.getClosestAirbaseTo(p, 0) -- never get FARPS
    theZone.base = theBase 
    theZone.baseName = theBase:getName()
    theZone.helos = false 
    
    local nearestRwy = nil 
    -- see if this is a manually placed runway 
    if theZone:getBoolFromZoneProperty("manual", true) then
        -- construct runway from trigger zone attributes 
        if theZone.verbose or tdz.verbose then 
            trigger.action.outText("+++TDZ: runway for <" .. theZone.name .. "> is manually placed", 30)
        end 
        nearestRwy = {}
        nearestRwy.length = theZone:getNumberFromZoneProperty("length", 2500)
        nearestRwy.width = theZone:getNumberFromZoneProperty("width", 60)
        local hdgRaw = theZone:getNumberFromZoneProperty("course", 0) -- in degrees 
        hdgRaw = hdgRaw * 0.0174533 -- rads 
        nearestRwy.course = hdgRaw * (-1) -- why? because DCS.
        nearestRwy.position = theZone:getPoint(true)
        theZone.baseName = theZone.name .. "(manual placement)"
    else 
        -- get closest runway to TDZ
        -- may get a bit hairy, so let's find a good way 
        local allRwys = theBase:getRunways()
        
        local minDist = math.huge
        for idx, aRwy in pairs(allRwys) do 
            local rp = aRwy.position
            local dist = dcsCommon.distFlat(p, rp)
            if dist < minDist then 
                nearestRwy = aRwy 
                minDist = dist
            end
        end
    end 
    local bearing = nearestRwy.course * (-1)
    if bearing < 0 then bearing = bearing + math.pi * 2 end 
    theZone.bearing = bearing 
    rwname = math.floor(dcsCommon.bearing2degrees(bearing)/10 + 0.5) -- nice number
    degrees = math.floor(dcsCommon.bearing2degrees(bearing) * 10) / 10
    if degrees < 0 then degrees = degrees + 360 end 
    if degrees > 360 then degrees = degrees - 360 end 
    if rwname < 0 then rwname = rwname + 36 end 
    if rwname > 36 then rwname = rwname - 36 end 
    
    if tdz.verbose or theZone.verbose then 
        trigger.action.outText("TDZ: <" .. theZone.name .. "> attached to airfield " .. theZone.baseName .. " RW main (LEFT) is " .. rwname .. "0", 30)
    end 
    
    local opName = rwname + 18 
    if opName > 36 then opName = opName - 36 end 
    if rwname < 10 then rwname = "0"..rwname end 
    if opName < 10 then opName = "0" .. opName end  
    theZone.rwName = rwname .. "/" .. opName    
    theZone.opName = opName .. "/" .. rwname
    local rwLen = nearestRwy.length
    rwLen = rwLen + 2 * theZone:getNumberFromZoneProperty("extend", 0)
    local rwWid = nearestRwy.width 
    rwWid = rwWid + 2 * theZone:getNumberFromZoneProperty("expand", 0)
    local pos = nearestRwy.position
    -- p1 is for distance to centerline calculation, defining a point 
    -- length away in direction bearing, setting up the line
    -- theZone.rwCenter, theZone.p1
    theZone.rwCenter = pos 
    local p1 = {x = pos.x + math.cos(bearing) * rwLen, y = 0, z = pos.z + math.sin(bearing) * rwLen}
    theZone.visible = theZone:getBoolFromZoneProperty("visible", true)
    theZone.rwP1 = p1 
    theZone.starts = theZone:getNumberFromZoneProperty("starts", 0)
    theZone.ends = theZone:getNumberFromZoneProperty("ends", 610) -- m = 2000 ft
    theZone.left = theZone:getBoolFromZoneProperty("left", true)
    theZone.right = theZone:getBoolFromZoneProperty("right", true)

    theZone.runwayZone = tdz.calcTDZone(theZone.name .. "-" .. rwname .. "main", pos, rwLen, rwWid, bearing)
    theZone.rwFrame = theZone:getRGBAVectorFromZoneProperty("rwFrame", {0, 0, 0, 1}) -- black
    theZone.rwFill = theZone:getRGBAVectorFromZoneProperty("rwFill", {0, 0, 0, 0}) -- nothing
    if theZone.visible then 
        theZone.runwayZone:drawZone(theZone.rwFrame, theZone.rwFill)
    end 
    local theTDZone = tdz.calcTDZone(theZone.name .. "-" .. rwname, pos, rwLen, rwWid, bearing, theZone.starts / rwLen, theZone.ends/rwLen)

    theZone.tdzFrame = theZone:getRGBAVectorFromZoneProperty("tdzFrame", {0, 1, 0, 1}) -- green 100%
    theZone.tdzFill = theZone:getRGBAVectorFromZoneProperty("tdzFill", {0, 1, 0, 0.25}) -- 25% green
    if theZone.visible and theZone.left then 
        theTDZone:drawZone(theZone.tdzFrame, theZone.tdzFill)
    end 
    
    theZone.normTDZone = theTDZone
    theTDZone = tdz.calcTDZone(theZone.name .. "-" .. opName, pos, rwLen, rwWid, bearing + math.pi, theZone.starts / rwLen, theZone.ends/rwLen)
    if theZone.visible and theZone.right then 
        theTDZone:drawZone(theZone.tdzFrame, theZone.tdzFill)
    end 
    theZone.opTDZone = theTDZone
    theZone.opBearing = bearing + math.pi
    if theZone.opBearing > 2 * math.pi then theZone.opBearing = theZone.opBearing - math.pi * 2 end 

    if theZone:hasProperty("landed!") then 
        theZone.landedFlag = theZone:getStringFromZoneProperty("landed!", "none")
    end
    if theZone:hasProperty("touchdown!") then 
        theZone.touchDownFlag = theZone:getStringFromZoneProperty("touchDown!", "none")
    end
    if theZone:hasProperty("fail!") then 
        theZone.failFlag = theZone:getStringFromZoneProperty("fail!", "none")
    end

    theZone.method = theZone:getStringFromZoneProperty("method", "inc")
end

--
-- event handler
--

function tdz.playerLanded(theUnit, playerName)
    if tdz.watchlist[playerName] then 
        -- this is not a new landing, for now ignore, increment bump count 
        -- make sure unit names match?
        local entry = tdz.watchlist[playerName]
        entry.hops = entry.hops + 1 -- uh oh. 
        return 
    end 
    
    -- we may want to filter helicopters
    
    -- see if we touched down inside of one of our watched zones 
    -- and the directionality (left = landing dir, right = opDir)
    -- matches
    local p = theUnit:getPoint()
    local theGroup = theUnit:getGroup()
    local cat = theGroup:getCategory() -- DCS 2.9: no issues with groups...
    local gID = theGroup:getID()
    local hdg = dcsCommon.getUnitHeading(theUnit)    
    local msg = ""
    local theZone = nil 
    local opposite = false 
    local dHdg, dOpHdg
    for idx, aRunway in pairs(tdz.allTdz) do 
        local theRunway = aRunway.runwayZone 
        local allowUnit = (cat ~= 1) or aRunway.helos -- 1 = helos
        if allowUnit and theRunway:pointInZone(p) then -- touched down
            dHdg = math.abs(aRunway.bearing - hdg) -- 0..Pi
            dOpHdg = math.abs(aRunway.opBearing - hdg)
            opposite = false 
            if tdz.verbose or aRunway.verbose then 
                trigger.action.outText("TDZ: landing inside <" .. aRunway.name .. ">, myHdg = <" .. math.floor(hdg  * 57.2958) .. ">, dHdg = <" .. dHdg * 57.29 .. ">, dOpHdg = <" .. dOpHdg * 57.29 .. ">, rw = <" .. math.floor(aRunway.bearing  * 57.2958) .. ">, rwOp = <" .. math.floor(aRunway.opBearing  * 57.2958) .. ">", 30)
            end

            if dOpHdg < dHdg then 
                opposite = true 
                dHdg = dOpHdg
                if tdz.verbose or aRunway.verbose then 
                    trigger.action.outText("TDZ: landing inside <" .. aRunway.name .. ">, *OPPOSING*", 30)
                end
            else 
                if tdz.verbose or aRunway.verbose then 
                    trigger.action.outText("TDZ: landing inside <" .. aRunway.name .. ">, ---INLINE---", 30)
                end
            end 
            -- see if directionality matches 
            if ((opposite == false) and aRunway.left) or 
               ((opposite == true) and aRunway.right) 
            then 
                theZone = aRunway -- FOUND!
                if theZone.touchDownFlag then 
                    theZone.pollFlag(theZone.touchDownFlag, theZone.method)
                end
                trigger.action.outTextForGroup(gID, "Touchdown! Come to a FULL STOP for evaluation", 30)
            else 
                if aRunway.verbose or tdz.verbose then 
                    trigger.action.outText("TDZ: ignored touchdown in runway for zone <" .. aRunway.name .. ">, directionality filtered.", 30)
                end
            end
        end 
    end
    if not theZone then 
        if tdz.verbose then 
            trigger.action.outText("TDZ: no touchdown inside zones registered", 30)
        end
        return 
    end -- no landing eval zone hit 
    -- Warning: finds the LAST that matches inZone and left/right 
    
    -- start a new watchlist entry 
    local entry = {}
    entry.msg = ""
    entry.playerName = playerName 
    entry.unitName = theUnit:getName()
    entry.theType = theUnit:getTypeName()
    entry.gID = gID 
    entry.theTime = timer.getTime() 
    entry.tdPoint = p 
    entry.tdVel = theUnit:getVelocity() -- vector 
    entry.hops = 1
    entry.theZone = theZone
    
    -- see if we are in main or opposite direction 
    if dHdg > math.pi * 1.5 then -- > 270+ 
        dHdg = dHdg - math.pi * 1.5
    elseif dHdg > math.pi / 2 then -- > 90+ 
        dHdg = dHdg - math.pi / 2 
    end
    dHdg = math.floor(dHdg * 572.958) / 10 -- in degrees
    local lHdg = math.floor(hdg * 572.958) / 10 -- also in deg 
    -- now see how far off centerline. 
    local offcenter = dcsCommon.distanceOfPointPToLineXZ(p, theZone.rwCenter, theZone.rwP1)
    offcenter = math.floor(offcenter * 10)/10 
    local vel = dcsCommon.vMag(entry.tdVel)
    local vkm = math.floor(vel * 36) / 10 
    local kkm = math.floor(vel * 19.4383) / 10
    entry.msg = entry.msg .. "\nLanded heading " .. lHdg .. "°, diverging by " .. dHdg .. "° from runway heading, velocity at touchdown " .. vkm .. " kmh/" .. kkm .. " kts, touchdown " .. offcenter ..  " m off centerline\n"
    
    -- inside TDZ? Directionality was already checked
    local tdZone = theZone.normTDZone
    if opposite then 
        tdZone = theZone.opTDZone
    end 
    
    if tdZone:pointInZone(p) then 
        -- yes, how far behind threshold
        -- project point onto line to see how far inside 
        local distBehind = dcsCommon.distanceOfPointPToLineXZ(p, tdZone.poly[1], tdZone.poly[4])
        local zonelen = math.abs(theZone.starts-theZone.ends)
        local percentile = math.floor(distBehind / zonelen * 100)
        local rating = ""
        if percentile < 5 or percentile > 90 then rating = "marginal"
        elseif percentile < 15 or percentile > 80 then rating = "pass"
        elseif percentile < 25 or percentile > 60 then rating = "good"
        else rating = "excellent" end 
        entry.msg = entry.msg .. "Touchdown inside TD-Zone, <" .. math.floor(distBehind) .. " m> behind threshold, rating = " .. rating .. "\n"    
    end
    
    tdz.watchlist[playerName] = entry 
    if not tdz.watching then 
        tdz.watching = true 
        timer.scheduleFunction(tdz.watchLandings, {}, timer.getTime() + 0.2)
    end
end 

function tdz:onEvent(event)
    if not event.initiator then return end 
    local theUnit = event.initiator 
    if not theUnit.getPlayerName then return end 
    local playerName = theUnit:getPlayerName() 
    if not playerName then return end 
    if event.id == 4 then 
        -- player landed 
        tdz.playerLanded(theUnit, playerName) 
    end
end

--
-- Monitor landings in progress
--
function tdz.watchLandings() 
    local filtered = {}
    local count = 0
    local transfer = false
    local success = false    
    local now = timer.getTime() 
    for playerName, aLanding in pairs (tdz.watchlist) do 
        -- see if landing timed out 
        local tdiff = now - aLanding.theTime 
        if tdiff < tdz.timeoutAfter then 
            local theUnit = Unit.getByName(aLanding.unitName)
            if theUnit and Unit.isExist(theUnit) then 
                local vel = theUnit:getVelocity()
                local vel = dcsCommon.vMag(vel)
                local p = theUnit:getPoint() 
                if aLanding.theZone.runwayZone:pointInZone(p) then     
                    -- we must slow down to below 3.6 km/h 
                    if vel < 1 then 
                        -- make sure that we are still inside the runway 
                        success = true 
                    else 
                        transfer = true 
                    end
                else 
                    trigger.action.outTextForGroup(aLanding.gID, "Ran off runway.", 30)
                end
            end 
        end 
        if transfer then 
            count = count + 1
            filtered[playerName] = aLanding
        else 
            local theZone = aLanding.theZone
            if success then
                local theUnit = Unit.getByName(aLanding.unitName)
                local p = theUnit:getPoint()
                local tdist = math.floor(dcsCommon.distFlat(p, aLanding.tdPoint))
                aLanding.msg = aLanding.msg .."\nSuccessful landing for " .. aLanding.playerName .." in a " .. aLanding.theType .. ". Landing run = <" .. tdist .. " m>, <" .. math.floor(tdiff*10)/10 .. "> seconds from touch-down to standstill."

                if aLanding.hops > 1 then 
                    aLanding.msg = aLanding.msg .. "\nNumber of hops: " .. aLanding.hops
                end 
                if theZone.landedFlag then 
                    theZone:pollFlag(theZone.landedFlag, theZone.method)
                end
                aLanding.msg = aLanding.msg .."\n"
                trigger.action.outTextForGroup(aLanding.gID, aLanding.msg, 30)
            else 
                if theZone.failFlag then 
                    theZone:pollFlag(theZone.failFlag, theZone.method)
                end
                trigger.action.outTextForGroup(aLanding.gID, "Landing for " .. aLanding.playerName .." incomplete.", 30)
            end 
        end
    end
    
    tdz.watchlist = filtered
    
    if count > 0 then 
        timer.scheduleFunction(tdz.watchLandings, {}, timer.getTime() + 0.2)
    else 
        tdz.watching = false
    end
end
--
-- Start
--
function tdz.readConfigZone()
    local theZone = cfxZones.getZoneByName("tdzConfig") 
    if not theZone then 
        theZone = cfxZones.createSimpleZone("tdzConfig") 
    end 
    tdz.verbose = theZone.verbose 
end 

function tdz.start()
    if not dcsCommon.libCheck("cfx TDZ", 
        tdz.requiredLibs) then
        return false 
    end
    
    -- read config 
    tdz.readConfigZone()

    -- collect all wp target zones 
    local attrZones = cfxZones.getZonesWithAttributeNamed("TDZ")
    
    for k, aZone in pairs(attrZones) do 
        tdz.createTDZ(aZone) -- process attribute and add to zone
        table.insert(tdz.allTdz, aZone) -- remember it so we can smoke it
    end
        
    -- add event handler
    world.addEventHandler(tdz)

    trigger.action.outText("cf/x TDZ version " .. tdz.version .. " running", 30)
    return true 
end 

if not tdz.start() then 
    trigger.action.outText("cf/x TDZ aborted: missing libraries", 30)
    tdz = nil 
end
 

 Please see if this works for you.

 

TDZ by hand.miz

Posted
14 hours ago, cfrag said:

and that you may have a FARP closer to the TDZ than the next airfield. That's fixed now, and I see if I can give you the ability to put TDZ wherever you please

cflag san,

Yes, the Runway mod is a Static Object based on FARP. The error has been resolved.

14 hours ago, cfrag said:

Done. Please see the example mission. You now can "manually" create a runway by adding the 'manual = true' attribute, and then supplying "length", "width", and "course" (in degrees). Plus, the script will no longer get irritated by nearby FARPS.

Great 🙂
I haven't landed in reality yet, but I manually configured it and confirmed that it is displayed on the map. I will try landing when it becomes night.

 

DCS 2023-12-01 17-56-48.jpg

 

Posted
5 hours ago, tsuyopooh said:

I will try landing when it becomes night.

Great! I successfully landed on the Private Airfield, and the display appeared correctly. Awesome!

I also resolved the issue with garbled characters by remembering to change the character encoding and saving it again. Thank you!

DCS 2023-12-01 22-41-27.jpg

DCS 2023-12-01 22-42-00.jpg

DCS 2023-12-01 22-47-19.jpg

DCS 2023-12-01 22-47-25.jpg

DCS 2023-12-01 23-05-28.jpg

DCS 2023-12-01 23-20-16.jpg

Posted

hi @cfrag !, here another great admirer of DML, you always manage to surprise us with new and amazing functionalities.  Have you ever thought about making a logistic system? I´m not refering to use the warehouses but something a bit more "simple" as with Pretense missions, were the logistic bit easily regulate the way a zone is able to create units or repair itself, something that I could see added to your Factoryzone module or any other zone with similar behaviour. Or maybe you have any other idea in mind that could work better?, or not any interest at all, which is fair enough 😄😉.

Posted
5 hours ago, mimamema said:

Have you ever thought about making a logistic system?

Yes, and pretty much from day one. You can see traces of it appearing in the old 'ownedZones' (now 'factory'), helo troops etc. So far, I've simply not found an easily deployable system, but I keep working at it. I'm looking at point-based systems where players can spend their side's points on upgrading zones. With the announced Herc and Nook coming, I see this as a great way to heighten their roles and make fun missions for them in the context of multi-player missions. Maybe I'll have some inspiration over the coming holiday season, when I'm hitting the really good wine 🙂 

  • Like 1
Posted
6 hours ago, cfrag said:

Yes, and pretty much from day one. You can see traces of it appearing in the old 'ownedZones' (now 'factory'), helo troops etc. So far, I've simply not found an easily deployable system, but I keep working at it. I'm looking at point-based systems where players can spend their side's points on upgrading zones. With the announced Herc and Nook coming, I see this as a great way to heighten their roles and make fun missions for them in the context of multi-player missions. Maybe I'll have some inspiration over the coming holiday season, when I'm hitting the really good wine 🙂 

Well in that case I would suggest you try any good Ribera del Duero from Spain, my home country, and then let me know what kind of inspiration it brings to you 😃. Thank you for answering, it´s good to know that it was already in your mind ^^

 

Posted
6 hours ago, mimamema said:

try any good Ribera del Duero

Indeed. I've long since switched from Bordeaux to (IMHO much better) Spanish wines. Currently I prefer the Rueda or Ribera del Duero regions (I have a Casa Lebai "La Nava" picked out for tomorrow, if it's as good as promised, it'll become part of the 'strategy selection' 🙂 

  • Like 2
Posted
3 hours ago, cfrag said:

Indeed. I've long since switched from Bordeaux to (IMHO much better) Spanish wines. Currently I prefer the Rueda or Ribera del Duero regions (I have a Casa Lebai "La Nava" picked out for tomorrow, if it's as good as promised, it'll become part of the 'strategy selection' 🙂 

hahahah that sounds magnífico! Enjoy it and bring us good news in the next days 😝

Posted

So, bad news: that "La Nava" did not meet my (probably unduly heightened) expectations, it couldn't hold a candle to much more mundane (yet still, to me, exquisite) wines like average Pesqueras or Marques de Riscal. On the other hand,  there's more to come over the next few days as we approach winter equinox 🙂 

  • Like 1
Posted

I have a question if I may; when trying to setup and use the factory module, is there any way to dictate what skin a spawned unit will utilize? For example, if I wanted spawned infantry to use a winter livery or a spawned tank to use a desert livery, is there an attribute setting/command that specifies that? As of my present understanding, I can have a factory zone spawn a specific unit such as 

Soldier M4 GRG

but am aware of no method to dictate that such use a winter livery for example...is such possible with the factory module?

 

Thanks in advance.

Posted
2 hours ago, ProtoTypeK7 said:

is there any way to dictate what skin a spawned unit will utilize?

Livery Support is currently lacking in most DML modules (with the exception of the new CivAir) due mostly to the non-standard way that liveries are added to units (you can't for example simply tell a unit to assume winter textures if they are present, and unfortunately, DCS's spawning algorithm also does not support smart selection of seasonal textures by default).

I'll see if I can come up with some livery support for spawners, but currently, I can't see a good, comfortable way to do so. If you have thoughts or ideas, I'm more than happy to hear them, of course 🙂 - a quite clunky way would be a look-up table akin to a config zone where spawners like the factory can get their liveries from (i.e. use a livery if the type to spawn has an entry in the livery table, and then use that livery). Let me see what I can do.

 

Posted
5 hours ago, ProtoTypeK7 said:

am aware of no method to dictate that such use a winter livery for example.

There now is. You can now tell the factory module which livery ("paint scheme") to use for a type through a "factoryLiveries" zone (see early demo below). Note that this is global, so if you change the livery of "Soldier M4" to "winter", all factories on the map that produce a unit of type "Soldier M4" 

Note that DML can only do what DCS can do, so it can not attach a livery from one object to another. The "winter" livery is only available for the "Soldier M4" type, just like it is in Mission Editor. The Soldier M4 GRG type only has a default livery accessible, so no amount of DML magic can overcome that, you cannot apply the "Soldier M4's" winter scheme to the "Soldier M4 GRG" type spawn.

The included demo works, and is still early alpha (meaning that it contains early builds of new dcsCommon, zones and factory versions), so caveat emptor.

winter soldier.miz

  • Thanks 1
Posted (edited)

Thank you so much! Just tested it, including with this mod:

Toms Infantry Mod V-Western Style 1.0

https://www.digitalcombatsimulator.com/en/files/3333050/

...and it works as advertised, I am able to make the factory spawn any of the chosen "WESTERN ARMY 01" through "WESTERN ARMY 05" skins available with the mod! (which replaces the default Georgian infantry models and skins)

It also seems that the single factory livery zone allows for the defining of liveries across even different unit types spawned by the same factory just by adding an additional attribute to the zone and defining the unit type and chosen livery name, very impressive!

 

Again, thank you very much for your timely assistance! I've got to go mess around with this now...

Edited by ProtoTypeK7
sentence structure
Posted
Just now, SPAS79 said:

both wipe and declutter seem to not do much? 

[referring to options for the cloner module?]

Usually (but not always) the preWipe and declutter options should remove dead infantry, but DCS's mechanics in this regard can be quite mysterious. Can you give me a simple mission example where dead Infs do not get cleaned up, and I'll try to investigate.

Cheers,

-ch

 

  • Like 1
Posted
34 minutes ago, SPAS79 said:

As far as I can tell, this [wiping dead infantry soldiers at re-spawning] does not work.

Oh joy, it seems like the good people at ED changed more inside DCS, and, just like trees, destroyed and dead objects cannot (for the time being) be removed. Wiper sees them, and tries to erase them, but to no avail, even if we set wipeCat to 3 (static)

Other than that, just an unrelated observation as that may impact you down the road: the cloner "Static Inf 1" (and some others)  has two "clone?" attributes. Only one will be chosen at random. I removed doCloneAll (global) from the one I tested.

The rest looks fine, it's just DCS acting up.

Posted

Heh, at least it's not me. I've been sweating like a madman with DCS server bots in the last few days, I didn't need another complex thing in my life. Your framework is surprisingly easy to use. The problem I have is with the docs, I have found myself looking at the quick reference, the full manual and the example mission to work out how stuff works.
One thing that is especially difficult is working out where the name of the zones goes (is it an attribute or does it go in the trigger zone's NAME field. That is not spelt out most of the times). Also on the same registers, some sections do not explicitly say "load this xxx.lua like the others". The latest example being

delayFlag

where I scratched my head for a good 30 minutes before realizing it might have had a DOSCRIPT requirement like the others. I know, it's called a module and should imply loading stuff but I'm a literal kind of guy. 

All in all impressive work! Also with the docs. 

Yeah I put two in there as I want to command that with two flags, one automatic, the other with an F10 command. Do you reckon that could be done? 

 

Posted (edited)
40 minutes ago, SPAS79 said:

One thing that is especially difficult is working out where the name of the zones goes

Thank you for using DML and your kind encouragement. I'm currently working on DML 2.0, which I intend to finish over the coming weeks, and a big part of that is a re-write of the initial chapters, bringing the 'how to use DML' to a more coherent whole. 

Now, except when you use a config zone (which often I hope isn't required because each module's defaults fit 90% of all use cases), the name of the trigger zones should not matter (there are some exceptions of course when you reference one zone from another, like for example in cloners that use templates - as you already dicovered and mastered 🙂 ) and it is edited at the very top of the trigger zone editor.

40 minutes ago, SPAS79 said:

Also on the same registers, some sections do not explicitly say "load this xxx.lua like the others".

Yeah, the manual is far from complete and makes assumptions about user inference where it shouldn't. That being said, any module that you intend to use must be added with a DOSCRIPT before you can use it in your mission, there are no 'included' bonus-modules.

40 minutes ago, SPAS79 said:

The latest example being

delayFlag

where I scratched my head for a good 30 minutes before realizing it might have had a DOSCRIPT requirement like the others. I know, it's called a module and should imply loading stuff but I'm a literal kind of guy. 

Well, I'm pleading "not guilty' on this one  😎

image.png

Now, I realize that I probably got lucky 🙂 - any part of the docs can contain massive omissions and plain stupid errors, and I'm grateful to anyone who points out any goofs. 

40 minutes ago, SPAS79 said:

Yeah I put two in there as I want to command that with two flags, one automatic, the other with an F10 command. Do you reckon that could be done? 

[I'm assuming that with "I put two in there" you are referencing my comment wrt the two 'clone?' inputs that would compete with each other]

Well, it can be done, but for that you'll have to level up to some more advanced stuff: you want that two different flags can trigger a cloner's single input. So what you are looking at is somehow triggering the cloner's "clone?" input with two different flags, not just one. How is this done in old-school electronics? You need to process the two flags to provide a single signal: you need an OR function.

It's going to be slightly messy - you need to stack another module on your cloners - and your zones have already impressive "height". But since you have already mastered local flag names, it's not going to be that messy: use the xFlags module to OR [require = any] the two flags "*doClone" (local) and (global) "doCloneAll" and wire that result into the cloner's clone? input. If you can't get it to work, let me know and we can try to work it out. And since you are already successfully using local flags, you can then copy/paste the working stack all over the place, demonstrating how powerful DML's advanced features really are. It's a shame that few people ever use DML at that level, or I'd be trumpeting that feature more 🙂 

Hope this helps,

-ch

 

Edited by cfrag
Posted (edited)
2 hours ago, cfrag said:

Well, I'm pleading "not guilty' on this one  😎

 

LOL that was probably my ADHD. I also forgot to take the meds today (a perk of the syndrome, you forget to take your daily prescription of otherwise illegal, very sought after and funny drugs)...

 

2 hours ago, cfrag said:

you need an OR function.

Wait, are you some kind of wizard? I was thinking "it would sure be nice if I could do some logic gating type stuff with DML... Yeah that's on the list of things to mess around with! 

Thanks! I'll let you know how that goes either here of if you want to pop in the discord (it's probably in the introductory mission messages) we can have a chat in there! 

This thing can do great stuff, methinks. 

Edited by SPAS79
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...