cfrag Posted November 30, 2023 Author Posted November 30, 2023 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. 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 1
tsuyopooh Posted November 30, 2023 Posted November 30, 2023 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.
cfrag Posted November 30, 2023 Author Posted November 30, 2023 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?
tsuyopooh Posted November 30, 2023 Posted November 30, 2023 (edited) >Let me try to set something up for you. Thank you Edited November 30, 2023 by tsuyopooh
cfrag Posted November 30, 2023 Author Posted November 30, 2023 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
cfrag Posted November 30, 2023 Author Posted November 30, 2023 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
tsuyopooh Posted December 1, 2023 Posted December 1, 2023 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.
tsuyopooh Posted December 1, 2023 Posted December 1, 2023 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!
mimamema Posted December 2, 2023 Posted December 2, 2023 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 .
cfrag Posted December 2, 2023 Author Posted December 2, 2023 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 1
mimamema Posted December 2, 2023 Posted December 2, 2023 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 ^^
cfrag Posted December 2, 2023 Author Posted December 2, 2023 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' ) 2
mimamema Posted December 3, 2023 Posted December 3, 2023 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
cfrag Posted December 3, 2023 Author Posted December 3, 2023 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 1
ProtoTypeK7 Posted December 7, 2023 Posted December 7, 2023 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.
cfrag Posted December 7, 2023 Author Posted December 7, 2023 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.
cfrag Posted December 7, 2023 Author Posted December 7, 2023 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 1
ProtoTypeK7 Posted December 8, 2023 Posted December 8, 2023 (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 December 8, 2023 by ProtoTypeK7 sentence structure
SPAS79 Posted December 14, 2023 Posted December 14, 2023 Would it be possible to clean out dead infantry? both wipe and declutter seem to not do much?
cfrag Posted December 14, 2023 Author Posted December 14, 2023 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 1
SPAS79 Posted December 14, 2023 Posted December 14, 2023 I sure can. As far as I can tell, this does not work. I haven't thoroughly checked with the other groups so it might be a noobness issue (been using this framework for two whole days now!) Thanks for looking into this! HELI Target Practice DML.miz
cfrag Posted December 14, 2023 Author Posted December 14, 2023 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.
SPAS79 Posted December 14, 2023 Posted December 14, 2023 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?
cfrag Posted December 14, 2023 Author Posted December 14, 2023 (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 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 December 14, 2023 by cfrag
SPAS79 Posted December 14, 2023 Posted December 14, 2023 (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 December 14, 2023 by SPAS79
Recommended Posts