MBot Posted June 2, 2013 Posted June 2, 2013 (edited) This small script makes infantry, MANPADS, ZU-23 and Ural-375 ZU-23 suppressable by fire. The approach to simulate a suppressed state is to control ROE, therefore making the suppressed group unable to return fire. Each time an infantry, MANPADS or ZU-23 unit is hit by a weapon (either by direct hit or by a close by explosion), the group of this unit is set to ROE hold fire and will therefore cease fire. After 15 second the suppressed state ends and the group is set back to ROE open fire again. Subsequent hits on an already suppressed group will prolong the suppressed period. Initial additional hits add significantly more suppression, but subsequent hits will add less additional suppression each. The approach with ROE is rather crude and will sometimes cause unrealistic results. Yet I think that this is still better than having no suppression at all. Usage of script: Simply run it once in the ME with "do script" or "do script file". Note: Currently hits with the M134 Miniguns of the Huey are not tracked by the game. This makes Miniguns not trigger suppression effects. This is a bug with DCS that will hopefully be solved soon. Huey rockets and door guns already work fine with this script. Edit: No longer the case, miniguns work too. do local SuppressedGroups = {} --Table to temporary store data for suppressed groups --Function to end suppression and let group open fire again local function SuppressionEnd(id) id.ctrl:setOption(AI.Option.Ground.id.ROE , AI.Option.Ground.val.ROE.OPEN_FIRE) SuppressedGroups[id.groupName] = nil --trigger.action.outText(id.groupName .. " suppression end", 2) --Info for debug end local SuppressionEndCounter = 0 --Since SuppressionEnd() is a scheduled function it can exist in multiple instances at the same time. This counter will be used to identify each instance with the subsequent number. --Function to run suppress a group local function SuppressGroup(tgt) local delay = math.random(15, 45) --Time in seconds the group of a hit unit will be unable to fire local id = { groupName = tgt:getGroup():getName(), ctrl = tgt:getGroup():getController() } if SuppressedGroups[id.groupName] == nil then --If group is currently not suppressed, add to table. SuppressionEndCounter = SuppressionEndCounter + 1 --Increase counter to indentify instance of comming SuppressionEnd() scheduled function SuppressedGroups[id.groupName] = { SuppressionEndTime = timer.getTime() + delay, SuppressionEndN = SuppressionEndCounter --Store instance of SuppressionEnd() scheduled function } timer.scheduleFunction(SuppressionEnd, id, SuppressedGroups[id.groupName].SuppressionEndTime) --Schedule the SuppressionEnd() function else --If group is already suppressed, update table and increase delay local timeleft = SuppressedGroups[id.groupName].SuppressionEndTime - timer.getTime() --Get how long to the end of the suppression local addDelay = (delay / timeleft) * delay --The longer the suppression is going to last, the smaller it is going to be increased by additional hits if timeleft < delay then --But if the time left is shorter than a complete delay, add another full delay addDelay = delay end SuppressedGroups[id.groupName].SuppressionEndTime = SuppressedGroups[id.groupName].SuppressionEndTime + addDelay timer.setFunctionTime(SuppressedGroups[id.groupName].SuppressionEndN, SuppressedGroups[id.groupName].SuppressionEndTime) --Update the execution time of the existing instance of the SuppressionEnd() scheduled function end id.ctrl:setOption(AI.Option.Ground.id.ROE , AI.Option.Ground.val.ROE.WEAPON_HOLD) --Set ROE weapons hold to initate suppression --trigger.action.outText(id.groupName .. " suppressed until " .. SuppressedGroups[id.groupName].SuppressionEndTime, 2) --Info for debug end --Handler to get when units are hit SuppressionHandler = {} function SuppressionHandler:onEvent(event) if event.id == world.event.S_EVENT_HIT then local tgt = event.target local tgtType = tgt:getTypeName() if tgt:hasAttribute("Infantry") or tgt:hasAttribute("Static AAA") or (tgtType == "Ural-375 ZU-23") then --Check if hit unit is infantry, static or mobile ZU-23 SuppressGroup(tgt) --Run suppression of hit unit (group) end end end world.addEventHandler(SuppressionHandler) end SuppressionFireScript.lua Edited December 13, 2013 by MBot Script updated to work with DCS 1.2.6 9
gregzagk Posted June 2, 2013 Posted June 2, 2013 Thanks a lot! Will try it asap :thumbup: "ARGO" DCS UH-1H DLC SP Campaign 373vFS DCS World squadron (Greece) - www.buddyspike.net "ARGO 2.0 Project Phoenix" UH-1H DLC Campaign - WIP
RagnarDa Posted June 2, 2013 Posted June 2, 2013 Very nice! I did something similar for Arma 1 ages ago... How about adding a chance that the group under fire will flee from its attackers? You could put a waypoint 180 degrees from the attackers bearing and move in "Green"-mode as groups in this state will move regardless if they are under attack. Will try your script as soon I have some time. DCS AJS37 HACKERMAN There will always be bugs. If everything is a priority nothing is.
Stuka Posted June 2, 2013 Posted June 2, 2013 If you change line 15 to: local delay = math.random(15,80) You make the time of suppression random. In the example above, the minimum time is 15 seconds, the max time 80 seconds. Windows 11 | i9 12900KF | 64GB DDR4 | RTX 3090 | TM Warthog HOTAS | Saitek Combat Rudder Pedals | TM MFDs + Lilliput 8" | TIR5 Pro
tintifaxl Posted June 3, 2013 Posted June 3, 2013 This could add a lot of immersion for a mission (definately for a Huey pilot). Thanks a lot for coming up with that .lua and the idea in the first place. Windows 10 64bit, Intel i9-9900@5Ghz, 32 Gig RAM, MSI RTX 3080 TI, 2 TB SSD, 43" 2160p@1440p monitor.
Anastasiuss Posted June 3, 2013 Posted June 3, 2013 Nice Script MBot, will try this skript. Nice hint Stuka. [sIGPIC][/sIGPIC] 360th TFW Falconeers last video -> ASUS P6X58D Premium, Intel Core i7 920, 6GB DDR3, SAPPHIRE TOXIC HD 5850, Win7 64 Bit. X52, Track IR 4, Momo Racing. ArmA1+2+3, DCS: World, K-50, A-10C, CA, P-51D, UH-1H, Mi-8FC1+2+3, FalconAF, FC1+FC2, IL2'46, rFactor.
MBot Posted June 3, 2013 Author Posted June 3, 2013 If you change line 15 to: local delay = math.random(15,80) You make the time of suppression random. In the example above, the minimum time is 15 seconds, the max time 80 seconds. Good idea, I updated the script. Thank you.
RagnarDa Posted June 6, 2013 Posted June 6, 2013 Very nice! I did something similar for Arma 1 ages ago... How about adding a chance that the group under fire will flee from its attackers? You could put a waypoint 180 degrees from the attackers bearing and move in "Green"-mode as groups in this state will move regardless if they are under attack. Will try your script as soon I have some time. So I did a little modification of your script MBot. Hope you don't mind: do -- Finds a point betweem two points according to a given blend (0.5 = right between, 0.3 = a third from point1) function getpointbetween(point1, point2, blend) return { x = point1.x + blend * (point2.x - point1.x), y = point1.y + blend * (point2.y - point1.y), z = point1.z + blend * (point2.z - point1.z) } end -- Measures distance between two points function measuredistance(v1, v2) local distance = 0 local v1x = v1.x local v2x = v2.x local v1z = v1.z local v2z = v2.z if v1x > v2x then distance = distance + (v1x - v2x) else distance = distance + (v2x - v1x) end if v1z > v2z then distance = distance + (v1z - v2z) else distance = distance + (v2z - v1z) end return distance end -- Get tablelength function tablelength(T) local count = 0 for _ in pairs(T) do count = count + 1 end return count end -- Stolen from MiST (by Speed and Grimes) function getGroupPoints(groupname) -- if groupname exists in env.mission, then returns table of the group's points in numerical order, such as: { [1] = {x = 299435.224, y = -1146632.6773}, [2] = { x = 663324.6563, y = 322424.1112}} for coa_name, coa_data in pairs(env.mission.coalition) do if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then if coa_data.country then --there is a country table for cntry_id, cntry_data in pairs(coa_data.country) do for obj_type_name, obj_type_data in pairs(cntry_data) do if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! for group_num, group_data in pairs(obj_type_data.group) do if group_data and group_data.name and group_data.name == groupname then -- this is the group we are looking for if group_data.route and group_data.route.points and #group_data.route.points > 0 then local points = {} for point_num, point in pairs(group_data.route.points) do if not point.point then points[point_num] = { x = point.x, y = point.y } else points[point_num] = point.point --it's possible that the ME could move to the point = Vec2 notation. end end return points end return end --if group_data and group_data.name and group_data.name == 'groupname' end --for group_num, group_data in pairs(obj_type_data.group) do end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then end --for obj_type_name, obj_type_data in pairs(cntry_data) do end --for cntry_id, cntry_data in pairs(coa_data.country) do end --if coa_data.country then --there is a country table end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then end --for coa_name, coa_data in pairs(mission.coalition) do end -- Returns a guess of the remaining waypoints function getRemainingWaypoints(_group) local _returningwps = {} -- Table to return -- Get all waypoints as planned in the mission editor local _allwps = getGroupPoints(_group:getName()) if (_allwps == nil) then return end local _curpos = _group:getUnits()[1]:getPosition().p -- Loop through all waypoints and find the most likely next wp local _nextwpnr = 0 local _lastwppos for _wpnr, _point in pairs(_allwps) do if (_allwps[_wpnr - 1] ~= nil) then local _distbetweenwps = measuredistance(_allwps[_wpnr - 1], _point) local _disttothiswp = measuredistance(_curpos, {x = _point.x, y = _point.z}) if (_distbetweenwps > _disttothiswp) then -- Group is between wps, break loop _nextwpnr = _wpnr break end end end -- Loop through the remaining wps and fill the new table local _nrofwps = #_allwps while (_nextwpnr <= _nrofwps) do _returningwps[#_returningwps+1] = {_allwps[_nextwpnr]} _nextwpnr = _nextwpnr + 1 end end local SuppressedGroups = {} --Table to temporary store data for suppressed groups --Function to end suppression and let group open fire again local function SuppressionEnd(id) id.ctrl:setOption(AI.Option.Ground.id.ROE , AI.Option.Ground.val.ROE.OPEN_FIRE) id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO) SuppressedGroups[id.groupName] = nil --trigger.action.outText(id.groupName .. " suppression end", 2) --Info for debug end local SuppressionEndCounter = 0 --Since SuppressionEnd() is a scheduled function it can exist in multiple instances at the same time. This counter will be used to identify each instance with the subsequent number. --Function to run suppress a group local function SuppressGroup(tgt, initiator) local delay = 15 --math.random(5,60) --Time in seconds the group of a hit unit will be unable to fire local id = { groupName = tgt:getGroup():getName(), ctrl = tgt:getGroup():getController() } if SuppressedGroups[id.groupName] == nil then --If group is currently not suppressed, add to table. SuppressionEndCounter = SuppressionEndCounter + 1 --Increase counter to indentify instance of comming SuppressionEnd() scheduled function SuppressedGroups[id.groupName] = { SuppressionEndTime = timer.getTime() + delay, SuppressionEndN = SuppressionEndCounter --Store instance of SuppressionEnd() scheduled function } timer.scheduleFunction(SuppressionEnd, id, SuppressedGroups[id.groupName].SuppressionEndTime) --Schedule the SuppressionEnd() function else --If group is already suppressed, update table and increase delay local timeleft = SuppressedGroups[id.groupName].SuppressionEndTime - timer.getTime() --Get how long to the end of the suppression local addDelay = (delay / timeleft) * delay --The longer the suppression is going to last, the smaller it is going to be increased by additional hits if timeleft < delay then --But if the time left is shorter than a complete delay, add another full delay addDelay = delay end SuppressedGroups[id.groupName].SuppressionEndTime = SuppressedGroups[id.groupName].SuppressionEndTime + addDelay timer.setFunctionTime(SuppressedGroups[id.groupName].SuppressionEndN, SuppressedGroups[id.groupName].SuppressionEndTime) --Update the execution time of the existing instance of the SuppressionEnd() scheduled function end -- Get distance between the two points local _distance = measuredistance(Group.getUnits(tgt:getGroup())[1]:getPosition().p, initiator:getPosition().p) local _moveblend = 0 - (1/(_distance/200)) local _moveto = getpointbetween(Group.getUnits(tgt:getGroup())[1]:getPosition().p, initiator:getPosition().p, _moveblend) --trigger.action.smoke(_moveto, 0) local _wrappedaction = { id = 'WrappedAction', params = { action = { id = 'Script', params = { command = "Group.getByName(\"" .. id.groupName .. "\"):getController():setOption(AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO)" --"trigger.action.outText(\"Interceptor is attacking target.\", 10)" } } } } local _controlledtask = { id = 'ControlledTask', params = { task = { id = 'Hold', params = { } }, stopCondition = { duration = 30, }, } } local _combotask = { id = 'ComboTask', params = { tasks = { [1] = _wrappedaction, [2] = _controlledtask, } } } local _wps = { [1] = { action = AI.Task.VehicleFormation.Vee, x = Group.getUnits(tgt:getGroup())[1]:getPosition().p.x, y = Group.getUnits(tgt:getGroup())[1]:getPosition().p.z, speed = 25, ETA = 100, ETA_locked = false, name = "Starting point", task = nil }, [2] = { action = AI.Task.VehicleFormation.Vee, x = _moveto.x, y = _moveto.z, speed = 25, ETA = 100, ETA_locked = false, name = "Flee", task = _combotask }, [3] = { action = AI.Task.VehicleFormation.Vee, x = Group.getUnits(tgt:getGroup())[1]:getPosition().p.x, y = Group.getUnits(tgt:getGroup())[1]:getPosition().p.z, speed = 25, ETA = 100, ETA_locked = false, name = "Fight back", task = nil }, } -- Get remaining waypoints local _remainingwps = getRemainingWaypoints(tgt:getGroup()) -- Loop through the remaining wps and create proper waypoints if (_remainingwps ~= nil) then local _nrofwps = #_remainingwps local _rwpscount = 1 local _cwp = 3 while (_cwp <= (_nrofwps + 3)) do _wps[#_wps+1] = {[_cwp] = { action = AI.Task.VehicleFormation.Vee, x = _remainingwps[_rwpscount].x, y = _remainingwps[_rwpscount].y, speed = 25, ETA = 100, ETA_locked = false, name = "Remaining waypoint", task = nil }} _cwp = _cwp + 1 _rwpscount = _rwpscount + 1 end end local Mission = { id = 'Mission', params = { route = { points = _wps }, } } --id.ctrl:setOption(AI.Option.Ground.id.ROE , AI.Option.Ground.val.ROE.WEAPON_HOLD) --Set ROE weapons hold to initate suppression if (math.random(0,100) < 30) then id.ctrl:setTask(Mission) -- Flee!! id.ctrl:setOption(AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.GREEN) end --trigger.action.outText(id.groupName .. " moves to X:" .. _moveto.x .. " Y:" .. _moveto.z, 60) --trigger.action.outText(id.groupName .. " suppressed until " .. SuppressedGroups[id.groupName].SuppressionEndTime, 2) --Info for debug end --Handler to get when units are hit SuppressionHandler = {} function SuppressionHandler:onEvent(event) if event.id == world.event.S_EVENT_HIT then local tgt = event.target if tgt ~= nil then local tgtType = tgt:getTypeName() local initiator = event.initiator if tgt:hasAttribute("Infantry") or tgt:hasAttribute("Static AAA") or (tgtType == "Ural-375 ZU-23") then --Check if hit unit is infantry, static or mobile ZU-23 SuppressGroup(tgt, initiator) --Run suppression of hit unit (group) end end end end world.addEventHandler(SuppressionHandler) end I'm not 100% happy with the result of my mod. What it does is that it makes units under fire sometimes flee for a while, then wait, and then return and carry on with their mission. DCS AJS37 HACKERMAN There will always be bugs. If everything is a priority nothing is.
Stuka Posted September 15, 2013 Posted September 15, 2013 Any idea why I get this error in 1.2.6 ? Windows 11 | i9 12900KF | 64GB DDR4 | RTX 3090 | TM Warthog HOTAS | Saitek Combat Rudder Pedals | TM MFDs + Lilliput 8" | TIR5 Pro
ENO Posted September 16, 2013 Posted September 16, 2013 I don't know anything and I'm not trying to make work for anyone- but would there be a way of establishing the random time units are suppressed by correlating it with their skill level? Average skill would be for 2-3 minutes, good would be 1-2 minutes... etc? Only reason I ask is this looks PERFECT for my applications (even in its current form) but if I were to polish the cannonball I think it would be fantastic to have another function of AI skill applied. "ENO" Type in anger and you will make the greatest post you will ever regret. "Sweetest's" Military Aviation Art
MBot Posted September 29, 2013 Author Posted September 29, 2013 (edited) Any idea why I get this error in 1.2.6 ? Not sure why it doesn't work with 1.2.6 anymore. Has there been a change to timer.setFunctionTime()? The wiki doesn't make much sense regarding the first argument of this function, but I don't know if this indicates a recent change or if the wiki was simply faulty from the very beginning. function timer.setFunctionTime(FunctionId functionId, Time time) re-schedules function to call at another model time. functionToCall: Lua-function to call. Must have prototype of FunctionToCall. time: Model time of the function call. Should re-scheduled functions no longer be addressed by their sequential number (first scheduled function is "1" etc.)? Edited September 29, 2013 by MBot
MBot Posted December 8, 2013 Author Posted December 8, 2013 The follow up on this, there seems to be a change since 1.2.6 how IDs are applied to functions. Previously this was a sequential number (second scheduled function has functionID 2 etc.). This doesn't seem to be the case anymore. Could Grimes or anyone else comment how the functionID is now determined? function PrintText(text) trigger.action.outText(text, 1) end timer.scheduleFunction(PrintText, "hello 1", 5) timer.scheduleFunction(PrintText, "hello 2", 10) timer.setFunctionTime(1, 7) timer.setFunctionTime(2, 12) --doesn't work anymore. What is the functionID of the second instance of PrintText()?
Grimes Posted December 8, 2013 Posted December 8, 2013 Its working but its skipping the even numbers. I've reported the bug to the relevant dev. You can get the functionId via the following: local funcId = timer.scheduleFunction(PrintText, "hello 1", 5) The right man in the wrong place makes all the difference in the world. Current Projects: Grayflag Server, Scripting Wiki Useful Links: Mission Scripting Tools MIST-(GitHub) MIST-(Thread) SLMOD, Wiki wishlist, Mission Editing Wiki!, Mission Building Forum
MBot Posted December 8, 2013 Author Posted December 8, 2013 Thanks a lot. With this info I will adjust the Suppression Fire script to work flawless again.
MBot Posted December 10, 2013 Author Posted December 10, 2013 The script in the first post of this thread is updated to work with DCS 1.2.6 again. Have fun :) 1
ENO Posted December 10, 2013 Posted December 10, 2013 Yeah I'm learning to hate that. "ENO" Type in anger and you will make the greatest post you will ever regret. "Sweetest's" Military Aviation Art
Stuka Posted December 11, 2013 Posted December 11, 2013 Awesome ! Thanks Mbot. "Must spread some rep around before you can give it to mBot again" Same here. Windows 11 | i9 12900KF | 64GB DDR4 | RTX 3090 | TM Warthog HOTAS | Saitek Combat Rudder Pedals | TM MFDs + Lilliput 8" | TIR5 Pro
ENO Posted December 11, 2013 Posted December 11, 2013 Just to confirm- is this the only place you need to put in the group ID? (bolded) local id = { groupName = tgt:getGroup():getName(), ctrl = tgt:getGroup():getController() } "ENO" Type in anger and you will make the greatest post you will ever regret. "Sweetest's" Military Aviation Art
MBot Posted December 12, 2013 Author Posted December 12, 2013 Well there is actually no DCS group ID used in the script. The above code is simply a custom table called id (but could be called anything else, perhaps another name would have been better) that holds the group name and group controller. Both could easily exist as their own variables, but the advantage of combining them into a single table is that this way both can be sent over to functions as a single argument.
ENO Posted December 12, 2013 Posted December 12, 2013 Oh I missed a part at the very beginning- so this will apply to all ground vehicles except with the minigun... just run once at mission start in a do- script action. "ENO" Type in anger and you will make the greatest post you will ever regret. "Sweetest's" Military Aviation Art
MBot Posted December 13, 2013 Author Posted December 13, 2013 Exactly, the script will apply to all units without any costomization to specific groups. Since 1.2.6 it will also apply to the Huey Miniguns.
Neon67 Posted December 13, 2013 Posted December 13, 2013 Can you specify which units are gonna be affected by that script ? I don't wanna see a ship holding fire because it took a bullet for exemple . Just a particulary group or type of units thanks :)
MBot Posted December 13, 2013 Author Posted December 13, 2013 Suppression will be initiated if a infantry, MANPADS or ZU-23 unit is being hit. Suppression will be applied to whole group that such a unit belongs to. So if you have a group that consists of a T-72 and a ZU-23, both will be supressed if the ZU-23 is hit and none will be supressed if the tank is hit.
Recommended Posts