Advertisement
Guest User

modtools/ranged-mod

a guest
Sep 30th, 2018
284
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 25.92 KB | None | 0 0
  1. --projectile-mod--
  2. --creates smoke and modifies various properties of any projectile based on ammo and weapon properties
  3. --author Grimlocke
  4. --based on scripts by zaporozhets
  5. --who on turn based his work on scripts by expwnent and Putnam
  6.  
  7. local usage = [====[
  8.  
  9. modtools/projectile-mod
  10. ===========================
  11. Creates smoke and modifies various properties of any projectile, based on ammunition, weapon or material.
  12.     -clear
  13.         unregister all entries
  14.     -list
  15.         list current ammo/weapons/material entries and their values
  16.     -ammoType, -weaponType or -materialType
  17.         register projectile, weapon or material with subtype (eg ITEM_AMMO_BOLTS) or full material token (INORGANIC:IRON or CREATURE_MAT:DWARF:LIVER). Applies to ANY projectile subtype or material, and anything used as a ranged weapon (including non-weapon item set as bow_id).
  18.         if ammo, weapons or materials have the same value defined, they will either be added, multiplied or overwritten with the following precedence: ammo > weapon > material
  19.         valid arguments:
  20.             -deviation - max number of tiles at 50 tiles distance. Additive, decreases with weapon/ammo item quality if qualityScale is > 0.
  21.             -range - minimum range, maximum range. projectile will not hit targets until minimum range is reached, and drop from the air triggering any impact effects at max range. defined as [ min max ] or just max (no brackets) to keep default minrange behaviour.
  22.             -falloff - projectile velocity is multiplied by this every 4 tiles of distance flown. 1 is constant speed (default behavior), below 1 has speed drop over distance, above 1 accelerates the projectile.
  23.             gravitates towards 1 depending on ammo item quality and qualityscale. Falloff stops happening at qualityScale 2 and masterwork quality. Strange things start happening beyond that number.
  24.             -hitrateAdd - 0(default) to 100, adds or substracts hitrate. increases with firer skill. Stacks additively, increases with weapon/ammo item quality if qualityScale is > 0.
  25.             -velocity - multiplies the projectile velocity, can exceed weapons max velocity. Stacks multiplicatively, increases with weapon/ammo item quality if qualityScale is > 0.
  26.             -qualityScale - multiplier, scales accuracy, hitrate and velocity with item quality. 0 for no scaling, 1 for default scaling. Accepts fractions and values higher than 1. Additive, does nothing on materials.
  27.             -breakChance - 0 to 1(default), chance for the projectile to break. Multiplicative.
  28.             -piercing - true/false, sets true if no argument. whether or not a projectile will pass through targets to hit another. causes projectiles to hit one target multiple times, may be a bug. Overrides by precedence.
  29.             -deleteMe - true/false, sets true if no argument. if set with no argument, deletes projectile on impact. if set with a number, deletes it at that range without triggering impact effects. Overrides by precedence.
  30.             -setType - TYPE:SUBTYPE, transform projectile(s) into this item. Changing item type not currently supported. Overrides by precedence.
  31.             -setMat - change the projectile's material. Overrides by precedence.
  32.             -shotCount - spawn extra projectiles if > 1, no theoretical limit the number. Multiplicative, rounds downwards.
  33.             -shotRof - if set, each extra shot is fired with this many ticks delay. if not set, all shots fire at once. Overrides by precedence.
  34.             -rofMult - multiplies baseRof and altRof, multiply rate of fire. Multiplicative.
  35.     -weaponType only values
  36.         -baseRof - delay in ticks between shots and firer skill scaling defaults are 80 and 1, skill scaling can be a fraction and more than 1.
  37.         -altRof - delay in ticks, interval and firer skill scaling. each shot in the number for interval will take this long instead. defaults for interval skill scaling are 2 and 1 respectively.
  38.         -windupRof - multiplier, shot-until-woundup, ticks per winddown. multiplies fire delay. delay gravitates towards 1 each shot until the second value is reached, and steps back by 1 shot each delay.
  39.        
  40.     -trailFlow or -impactFlow
  41.         add flow effects to projectiles during flight or at impact. arguments is [ ammo/weapon/material subtype/materialtoken ] or just subtype, which will default to ammo trigger.
  42.         multiple of each can be added with seperate commands, define flows AFTER the above properties, as they will override the entry for the same ammo/weapon/material.
  43.         valid arguments:
  44.         -minmaxint - minimum range, maximum range and interval. flow triggers within range, at the specified interval of tiles flown.
  45.         minimum and interval default to 1, so a range of 1-20 can be set as [ 1 20 1 ], [ 1 20 ], or just 20.
  46.         -flowType TODO
  47.         -flowMat - set flow material if the flow type permits it.
  48.         -volume - pretty self-explanatory.
  49.         -conical - multiply volume by distance flown and this multiplier.
  50.         -expand - if set, the flow will expand drift out to more than 1 tile.
  51.         -direction - works on fire and dragonfire (type 6 and 7) only, sets direction for the flow to the direction of the projectile.
  52.        
  53.     examples:
  54.         make arrows less accurate, have a 50% break chance and lose 6% speed each 4 tiles. also fires 20% faster.
  55.         -ammoType ITEM_AMMO_ARROWS -deviation 2 -breakChance 0.5 -falloff 0.94 -rofMult 1.2
  56.        
  57.         a slower, more accurate crossbow. firer skill only increase fire rate by half as much as normal. make steel hit harder and more durable.
  58.         -weaponType ITEM_WEAPON_CROSSBOW_ARBALEST \-deviation -1 -hitrateAdd 6 -velocity 1.2 -baseRof [ 145 0.5 ] -breakChance 1.33
  59.         -materialType INORGANIC:STEEL -breakChance 0.64 -velocity 1.2 -deviation \-1
  60.        
  61.         make shot of 8 pellets that are deleted after impact and produce smoke for 3 tiles that expands and increases in volume over distance
  62.         -ammoType ITEM_AMMO_GUN_20MM_SHOT -deviation 5 -hitrateAdd 5 -velocity 0.6 -deleteMe -subtype AMMO:ITEM_AMMO_GUN_SHOT -shotCount 8
  63.         -trailFlow ITEM_AMMO_GUN_20MM_SHOT -minmaxint 3 -flowType 5 -volume 6 -expand -conical 1
  64.        
  65.         a rocket that starts slow but flies faster and faster, hits a single target multiple times (faking explosive effect), turns into a different mat, produces smoke each 2 tiles and a dragonfire explosion. quality only matters half as much in deviation/velocity/hirate as normal. it explodes after traveling 20 tiles.
  66.         -ammoType ITEM_AMMO_ROCKET -velocity 0.5 -falloff 1.2 -piercing 1 -deleteMe -range 20 -setMat INORGANIC:EXPLOSIVE_MAT -qualityScale 0.5
  67.         -trailFlow [ ammo ITEM_AMMO_ROCKET ] -minmaxint [ 0 20 2 ] -flowType 5 -volume 20 -expand
  68.         -impactFlow [ ammo ITEM_AMMO_ROCKET ] -flowType 6 -volume 30 -expand
  69.        
  70.         make a minigun, because why not. each shot produces 3 projectiles at a 5 tick delay, has a 5 tick delay itself and every shot in 20 (so 60 projectiles) has a 500 tick delay. Only the last delay is decreased with firer skill. delays are multiplied by 6, multiplier decreases to 1 at the 8th shot, and winds back down by 1 each 40 ticks. add a material for incendiery ammo for good measure.
  71.         -weaponType ITEM_WEAPON_MINIGUN -deviation 5 -hitrateAdd 10 -qualityScale 1.4 -baseRof [ 5 0 ] -altRof [ 500 20 ] -windupRof [ 6 8 40 ]
  72.         -ammoType ITEM_AMMO_MINIGUN_AMMO -deleteMe -falloff 0.85 -shotCount 3 -shotRof [ 5 0 ]
  73.         -materialType INORGANIC:AP-I -velocity 0.67 -piercing
  74.         -impactFlow material INORGANIC:AP-I -flowType 6 -volume 10
  75. ]====]
  76.  
  77. local eventful = require 'plugins.eventful'
  78. local utils = require 'utils'
  79.  
  80. ammoProperties = ammoProperties or {}
  81. weaponProperties = weaponProperties or {}
  82. materialProperties = materialProperties or {}
  83. persistTable = persistTable or {}
  84. noInit = noInit or false
  85.  
  86. local validArgs = utils.invert({
  87.     'help',
  88.     'clear',
  89.     'list',
  90.     'weaponType',
  91.     'ammoType',
  92.     'materialType',
  93.     'deviation',
  94.     'hitrateAdd',
  95.     'qualityScale',
  96.     'breakChance',
  97.     'velocity',
  98.     'falloff',
  99.     'range',
  100.     'piercing',
  101.     'deleteMe',
  102.     'setItem',
  103.     'setMat',
  104.     'shotCount',
  105.     'shotRof',
  106.     'rofMult',
  107.     'baseRof',
  108.     'altRof',
  109.     'windupRof',
  110.     'trailFlow',
  111.     'impactFlow',
  112.     'minmaxint',
  113.     'flowType',
  114.     'flowMat',
  115.     'volume',
  116.     'conical',
  117.     'expand',
  118.     'direction'
  119. })
  120.  
  121. local args = utils.processArgs({...}, validArgs)
  122.  
  123. if args.ammoType or args.weaponType or args.materialType then
  124.     local things = { {args.ammoType, ammoProperties}, {args.weaponType, weaponProperties}, {args.materialType, materialProperties} }
  125.     for _,propsStr in pairs(things) do
  126.         if propsStr[1] then
  127.             propsStr[2][propsStr[1]] = { deviation = tonumber(args.deviation),
  128.             hitrate = tonumber(args.hitrateAdd),
  129.             breakChance = tonumber(args.breakChance),
  130.             velocity = tonumber(args.velocity),
  131.             falloff = tonumber(args.falloff),
  132.             qualityScale = tonumber(args.qualityScale),
  133.             shotCount = tonumber(args.shotCount),
  134.             rofMult = tonumber(args.rofMult)
  135.             }
  136.             if args.range then propsStr[2][propsStr[1]].range = { tonumber(args.range[1]) or 0, tonumber(args.range[2]) or tonumber(args.range) } end
  137.             if args.setItem then propsStr[2][propsStr[1]].setItem = { dfhack.items.findType(args.setItem), dfhack.items.findSubtype(args.setItem) } end
  138.             if args.setMat then propsStr[2][propsStr[1]].setMat = { dfhack.matinfo.find(args.setMat)['type'], dfhack.matinfo.find(args.setMat).index }  end
  139.             if args.piercing then propsStr[2][propsStr[1]].piercing = true end
  140.             if args.shotRof then propsStr[2][propsStr[1]].shotRof = { tonumber(args.shotRof[1]) or tonumber(args.shotRof), tonumber(args.shotRof[2]) or 1 } end
  141.             if args.piercing then propsStr[2][propsStr[1]].piercing = true end
  142.             if args.deleteMe then propsStr[2][propsStr[1]].deleteMe = tonumber(args.deleteMe)
  143.                 if not propsStr[2][propsStr[1]].deleteMe then propsStr[2][propsStr[1]].deleteMe = -1 end
  144.             end
  145.             if args.weaponType then
  146.                 if args.baseRof then propsStr[2][propsStr[1]].baseRof = { tonumber(args.baseRof[1]) or tonumber(args.baseRof), tonumber(args.baseRof[2]) or 1 } end
  147.                 if args.altRof then propsStr[2][propsStr[1]].altRof = { tonumber(args.altRof[1]) or tonumber(args.altRof), tonumber(args.altRof[2]) or 2, tonumber(args.altRof[3]) or 1 } end
  148.                 if args.windupRof then propsStr[2][propsStr[1]].windupRof = { tonumber(args.windupRof[1]) or tonumber(args.windupRof), tonumber(args.windupRof[2]) or 10, tonumber(args.windupRof[3]) or 40 } end
  149.             end
  150.             if noInit then return end
  151.         end
  152.     end
  153. end
  154.  
  155. if args.trailFlow or args.impactFlow then
  156.     local propsStr = {}
  157.     if args.trailFlow then
  158.         propsStr = {args.trailFlow[1] or 'ammo', args.trailFlow[2] or args.trailFlow, 'trailFlows'}
  159.     else
  160.         propsStr = {args.impactFlow[1] or 'ammo', args.impactFlow[2] or args.impactFlow, 'impactFlows'}
  161.     end
  162.     if propsStr[1] == 'ammo' then propsStr[1] = ammoProperties end
  163.     if propsStr[1] == 'weapon' then propsStr[1] = weaponProperties end
  164.     if propsStr[1] == 'material' then propsStr[1] = materialProperties end
  165.     propsStr[1][propsStr[2]] = propsStr[1][propsStr[2]] or {}
  166.     propsStr[1][propsStr[2]][propsStr[3]] = propsStr[1][propsStr[2]][propsStr[3]] or {}
  167.     local path = propsStr[1][propsStr[2]][propsStr[3]]
  168.     path[#path + 1] = {flowType = tonumber(args.flowType),
  169.     volume = tonumber(args.volume),
  170.     conical = tonumber(args.conical) or 0,
  171.     flowQuality = tonumber(args.flowQuality) or 1,
  172.     expand = args.expand,
  173.     direction = args.direction,
  174.     }
  175.     if args.trailFlow then
  176.         if not args.minmaxint then
  177.             path[#path].minmaxint = { 0, 666, 1 }
  178.         else
  179.             path[#path].minmaxint = { tonumber(args.minmaxint[1]) or 0, tonumber(args.minmaxint[2]) or tonumber(args.minmaxint) or 666, tonumber(args.minmaxint[3]) or 1 }
  180.         end
  181.     end
  182.     if args.flowMat then
  183.         path[#path].flowMat = { dfhack.matinfo.find(args.flowMat)['type'], dfhack.matinfo.find(args.flowMat).index }   
  184.     else
  185.         path[#path].flowMat = { -1, -1 }
  186.     end
  187.     if noInit then return end
  188. end
  189.  
  190. if args.clear then
  191.     ammoProperties = {}
  192.     weaponProperties = {}
  193.     materialProperties = {}
  194.     persistTable = {}
  195.     noInit = false
  196.     return
  197. end
  198.  
  199. if args.list then
  200.     local allthethings = { Ammo = ammoProperties, Weapon = weaponProperties, Material = materialProperties }
  201.     for rer,ere in pairs(allthethings) do
  202.         print("- "..rer.." Entries:")
  203.         for rer,ere in pairs(ere) do
  204.             print("    "..rer..":")
  205.             for rer,ere in pairs(ere) do
  206.                 print("        "..rer,ere)
  207.             end
  208.             print()
  209.         end
  210.         print()
  211.     end
  212.     return
  213. end
  214.  
  215. if args.help then
  216.     print(usage)
  217.     return
  218. end
  219.  
  220. eventful.enableEvent(eventful.eventType.UNLOAD, 1)
  221. eventful.onUnload.blam = function()
  222.     ammoProperties = {}
  223.     weaponProperties = {}
  224.     materialProperties  = {}
  225.     persistTable = {}
  226.     noInit = false
  227. end
  228.  
  229. eventful.onProjItemCheckMovement.blam = function(projectile)
  230.     if projectile.distance_flown == 0 then
  231.         if not projectile.firer or persistTable[projectile.bow_id] and persistTable[projectile.bow_id][projectile.item.id] then
  232.             return --from whence thou came!
  233.         elseif projectile.item:getSubtype() == -1 and
  234.         not materialProperties[dfhack.matinfo.decode(projectile.item):getToken()] and
  235.         not weaponProperties[df.item.find(projectile.bow_id).subtype.id] then
  236.             return
  237.         elseif not ammoProperties[projectile.item.subtype.id] and
  238.         not materialProperties[dfhack.matinfo.decode(projectile.item):getToken()] and
  239.         not weaponProperties[df.item.find(projectile.bow_id).subtype.id] then
  240.             return
  241.         else
  242.             local props = {}
  243.             local mode = 1
  244.             persistTable[projectile.bow_id] = persistTable[projectile.bow_id] or {}
  245.             props = initProps(projectile, props)
  246.             persistTable[projectile.bow_id][projectile.item.id] = props
  247.             for rer,func in ipairs(persistTable[projectile.bow_id][projectile.item.id].funcTable[mode]) do
  248.                 func(projectile, persistTable[projectile.bow_id][projectile.item.id], mode)
  249.             end
  250.         end
  251.     elseif persistTable[projectile.bow_id] and persistTable[projectile.bow_id][projectile.item.id] then
  252.         local mode = 2
  253.         if projectile.distance_flown >= projectile.fall_threshold then mode = 3 end
  254.         for rer,func in ipairs(persistTable[projectile.bow_id][projectile.item.id].funcTable[mode]) do
  255.             func(projectile, persistTable[projectile.bow_id][projectile.item.id], mode)
  256.         end
  257.     end
  258. end
  259.  
  260. eventful.onProjItemCheckImpact.blam = function(projectile)
  261.     if not persistTable[projectile.bow_id] or not persistTable[projectile.bow_id][projectile.item.id] then
  262.         return
  263.     else
  264.         local mode = 3
  265.         for rer,func in ipairs(persistTable[projectile.bow_id][projectile.item.id].funcTable[mode]) do
  266.             func(projectile, persistTable[projectile.bow_id][projectile.item.id], mode)
  267.         end
  268.     end
  269. end
  270.  
  271. function initProps(projectile, props)
  272.     --need some fields defined for things not to throw their cookies over finding a nil value
  273.     props = { deviation = 0, hitrate = 0, velocity = 1, shotCount = 1, rofMult = 1, bowId = projectile.bow_id, firerId = projectile.firer.id }
  274.     props.funcTable = { {fireMod}, {}, {} }
  275.     props.originPos = xyz2pos(projectile.origin_pos.x, projectile.origin_pos.y, projectile.origin_pos.z)
  276.     props.targetPos = xyz2pos(projectile.target_pos.x, projectile.target_pos.y, projectile.target_pos.z)
  277.     if projectile.item.quality then props.quality = projectile.item.quality end
  278.     local propertyTables = { {}, {}, {} }
  279.     propertyTables[1] = materialProperties[dfhack.matinfo.decode(projectile.item):getToken()] or {}
  280.     propertyTables[2] = weaponProperties[df.item.find(projectile.bow_id).subtype.id] or {}
  281.     if projectile.item:getSubtype() > -1 then propertyTables[3] = ammoProperties[projectile.item.subtype.id] or {} end
  282.     for propertyType,propertyTable in ipairs(propertyTables) do
  283.         for field,value in pairs(propertyTable) do
  284.             --Values that need to be multiplied or added
  285.             if field == "deviation" or field == "hitrate" then
  286.                 if not props[field] then props[field] = value else props[field] = props[field] + value end
  287.             elseif field == "velocity" or field == "falloff" or field == "breakChance" or field == "shotCount" or field == "rofMult" then
  288.                 if not props[field] then props[field] = value else props[field] = props[field] * value end
  289.             --All other values will override the previous
  290.             else
  291.                 props[field] = value
  292.             end
  293.         end
  294.     end
  295.     props.qualityScale = { propertyTables[1].qualityscale or 1, propertyTables[2].qualityscale or 1, propertyTables[3].qualityscale or 1 }
  296.     if props.baseRof or props.altRof or props.rofMult ~= 1 then
  297.         props.funcTable[1][#props.funcTable[1] + 1] = weaponRof
  298.         if not persistTable[projectile.bow_id].weaponProps and projectile.bow_id > -1 then
  299.             persistTable[projectile.bow_id].weaponProps = { increment = 1, windup = 0, baseRof = props.baseRof, altRof = props.altRof, windupRof = props.windupRof }
  300.         end
  301.     end
  302.     if props.setMat then props.funcTable[1][#props.funcTable[1] + 1] = setMat end
  303.     if props.shotCount >= 2 or props.setItem then props.funcTable[1][#props.funcTable[1] + 1] = multiInit end
  304.     if props.deleteMe == 0 then props.funcTable[1][#props.funcTable[1] + 1] = terminate end
  305.     if props.falloff then props.funcTable[2][#props.funcTable[2] + 1] = falloff end
  306.     if props.trailFlows then props.funcTable[2][#props.funcTable[2] + 1] = spawnFlow end
  307.     if props.deleteMe > 0 then props.funcTable[2][#props.funcTable[2] + 1] = terminate end
  308.     if props.impactFlows then props.funcTable[3][#props.funcTable[3] + 1] = spawnFlow end
  309.     props.funcTable[3][#props.funcTable[3] + 1] = terminate
  310.     --print("Blam! "..dfhack.items.getSubtypeDef(props.setItem[1],props.setItem[2]).id.." id:"..props.projectile.id.." from:"..df.item.find(projectile.bow_id).subtype.id, "V:"..props.projectile.unk22, "dev:"..props.deviation.."@"..props.distance)
  311.     --print(projectile.item.subtype.id, for rer,ere in pairs(props) do print(rer,ere) end,  print()
  312.     return props
  313. end
  314.  
  315. function fireMod(projectile, props, mode)
  316.     props.totalQuality = props.qualityScale[1] * ( props.qualityScale[2] * df.item.find(projectile.bow_id).quality + props.qualityScale[3] * projectile.item.quality )
  317.     projectile.hit_rating = math.floor(projectile.hit_rating + props.hitrate + props.totalQuality)
  318.     props.hitrate = projectile.hit_rating
  319.     projectile.unk22 = math.floor(projectile.unk22 * props.velocity * (1 + props.totalQuality / 50))
  320.     props.velocity = projectile.unk22
  321.     if props.deviation ~= 0 then
  322.         --Devided deviation by target max distance on x,y,z, relevant in case of cannons and a few AI targeting edge cases. If below 0, sets it to 0.
  323.         props.distance = math.max(math.abs(projectile.origin_pos.x - projectile.target_pos.x),math.abs(projectile.origin_pos.y - projectile.target_pos.y),math.abs(projectile.origin_pos.z - projectile.target_pos.z))
  324.         props.deviation = math.max(0, math.floor(0.5 + props.deviation / (1 + props.totalQuality / 25) / (50 / props.distance)))
  325.         projectile.target_pos.x = projectile.target_pos.x + math.random(-props.deviation, props.deviation)
  326.         projectile.target_pos.y = projectile.target_pos.y + math.random(-props.deviation, props.deviation)
  327.     end
  328.     if props.range then
  329.         projectile.min_hit_distance = math.max(props.range[1], projectile.min_hit_distance)
  330.         projectile.fall_threshold = math.min(props.range[2], projectile.fall_threshold)
  331.         projectile.min_ground_distance = props.range[2] - 1
  332.     end
  333.     props.range = { projectile.min_hit_distance, projectile.fall_threshold }
  334.     if props.breakChance then projectile.flags.no_impact_destroy = math.floor((1 - props.breakChance) / math.random(0, 1)) end
  335.     if props.falloff then props.falloff = props.falloff - (1 - props.falloff) * (props.qualityScale[3] * projectile.item.quality / 10) end -- Gravitate to 1 depending on ammo quality * qualityScale
  336.     if props.piercing then projectile.flags.piercing = props.piercing end
  337. end
  338.  
  339. function weaponRof(projectile, props, mode)
  340.     if projectile.firer then
  341.         local weaponProps = persistTable[projectile.bow_id].weaponProps or {baseRof = {80, 1}}
  342.         local windupMult
  343.         if weaponProps.windupRof then windupMult = windupRof(weaponProps, 1) else windupMult = 1 end
  344.         if not weaponProps.altRof then
  345.             projectile.firer.counters.think_counter = math.floor(weaponProps.baseRof[1] * props.rofMult * windupMult / (1 + (80 / projectile.firer.counters.think_counter - 1) * weaponProps.baseRof[2]))
  346.         elseif weaponProps.increment < weaponProps.altRof[2] then
  347.             weaponProps.increment = weaponProps.increment + 1
  348.             projectile.firer.counters.think_counter = math.floor(weaponProps.baseRof[1] * props.rofMult * windupMult / (1 + (80 / projectile.firer.counters.think_counter - 1) * weaponProps.baseRof[2]))
  349.         else
  350.             weaponProps.increment = 1
  351.             projectile.firer.counters.think_counter = math.floor(weaponProps.altRof[1] * props.rofMult * windupMult / (1 + (80 / projectile.firer.counters.think_counter - 1) * weaponProps.altRof[3]))
  352.         end
  353.     end
  354. end
  355.  
  356. function windupRof(weaponProps, mode)
  357.     if mode == 1 then
  358.         local windupMult = 1 + (weaponProps.windupRof[1] - 1) * (weaponProps.windupRof[2] - math.min(weaponProps.windupRof[2],weaponProps.windup)) / weaponProps.windupRof[2]
  359.         if weaponProps.windup == 0 then
  360.             dfhack.timeout(weaponProps.windupRof[3],'ticks',function() windupRof(weaponProps, 2) end)
  361.         end
  362.         weaponProps.windup = math.min(weaponProps.windup + 1, weaponProps.windupRof[2] + 1)
  363.         return windupMult
  364.     end
  365.     weaponProps.windup = weaponProps.windup - 1
  366.     if weaponProps.windup > 0 then
  367.         dfhack.timeout(weaponProps.windupRof[3],'ticks',function() windupRof(weaponProps, 2) end)
  368.     end
  369. end
  370.  
  371. function setMat(projectile, props, mode)
  372.     projectile.item:setMaterial(props.setMat[1])
  373.     projectile.item:setMaterialIndex(props.setMat[2])
  374. end
  375.  
  376. function multiInit(projectile, props, mode)
  377.     local firer = df.unit.find(props.firerId)
  378.     if not props.setMat then props.setMat = { projectile.item.mat_type, projectile.item.mat_index } end
  379.     if not props.setItem then props.setItem = { projectile.item:getType(), projectile.item:getSubtype() }
  380.     else
  381.         props.shotCount = props.shotCount + 1
  382.         props.funcTable[1][#props.funcTable[1] + 1] = terminate
  383.     end
  384.     if props.shotCount >= 2 then
  385.         if not props.shotRof then
  386.             multiShot(props, mode, firer)
  387.         else
  388.             local weaponProps = persistTable[projectile.bow_id].weaponProps
  389.             local windupMult
  390.             if weaponProps.windupRof then windupMult = windupRof(weaponProps, 1) end
  391.             local rof = props.shotRof[1]
  392.             if firer.counters.think_counter > 0 then
  393.                 rof = rof * props.rofMult / (1 + (80 / firer.counters.think_counter - 1) * props.shotRof[2])
  394.             end
  395.             if props.funcTable[1][#props.funcTable[1]] == terminate then
  396.                 multiShot(props, mode, firer, rof, weaponProps)
  397.             else
  398.                 if weaponProps.windupRof then windupMult = windupRof(weaponProps, 1) else windupMult = 1 end
  399.                 if firer.counters.think_counter > 0 then firer.counters.think_counter = math.floor(firer.counters.think_counter + 0.5 + rof * windupMult) end
  400.                 dfhack.timeout(math.floor(0.5 + rof * windupMult),'ticks',function() multiShot(props, mode, firer, rof, weaponProps) end)
  401.             end
  402.         end
  403.     end
  404. end
  405.  
  406. function multiShot(props, mode, firer, rof, weaponProps)
  407.     if firer.flags2.killed == true then return end
  408.     local projectileItem = df.item.find(dfhack.items.createItem(props.setItem[1], props.setItem[2], props.setMat[1], props.setMat[2], firer))
  409.     local projectile = dfhack.items.makeProjectile(projectileItem)
  410.     projectile.origin_pos = props.originPos
  411.     projectile.prev_pos = props.originPos
  412.     projectile.cur_pos = props.originPos
  413.     projectile.target_pos = xyz2pos(props.targetPos.x + math.random(-props.deviation, props.deviation), props.targetPos.y + math.random(-props.deviation, props.deviation), props.targetPos.z)
  414.     projectile.flags.piercing = props.piercing
  415.     projectile.min_hit_distance = props.range[1]
  416.     projectile.fall_threshold = props.range[2]
  417.     projectile.min_ground_distance = props.range[2] - 1
  418.     projectile.hit_rating = props.hitrate
  419.     projectile.unk22 = props.velocity
  420.     projectile.bow_id = props.bowId
  421.     projectile.firer = firer
  422.     if props.quality then projectile.item.quality = props.quality end
  423.     projectile.distance_flown = 0
  424.     projectile.fall_counter = 0
  425.     projectile.fall_delay = 0
  426.     props.shotCount = props.shotCount - 1
  427.     persistTable[props.bowId][projectileItem.id] = props
  428.     if props.shotCount >= 2 then
  429.         if not rof then
  430.             multiShot(props, mode, firer)
  431.         else
  432.             local windupMult = 1
  433.             if weaponProps.windupRof then windupMult = windupRof(weaponProps, 1) end
  434.             if firer ~= df.unit.find(0) then firer.counters.think_counter = math.floor(0.5 + firer.counters.think_counter + rof * windupMult) end
  435.             dfhack.timeout(math.floor(0.5 + rof * windupMult),'ticks',function() multiShot(props, mode, firer, rof, weaponProps) end)
  436.         end
  437.     end
  438. end
  439.  
  440. function falloff(projectile, props, mode)
  441.     if projectile.distance_flown % 4 == 0 then
  442.         --print("id:"..projectile.item.id.." @"..projectile.distance_flown, "V:"..projectile.unk22, "fall:"..props.falloff)
  443.         projectile.unk22 = math.floor(projectile.unk22 * props.falloff)
  444.     end
  445. end
  446.  
  447. function spawnFlow(projectile, props, mode)
  448.     if mode == 2 then mode = "trailFlows" else mode = "impactFlows" end
  449.     for _,flow in ipairs(props[mode]) do
  450.         if mode == "trailFlows" then
  451.             if projectile.distance_flown < tonumber(flow.minmaxint[1]) or projectile.distance_flown > tonumber(flow.minmaxint[2]) or projectile.distance_flown % flow.minmaxint[3] ~= 0 then
  452.                 return
  453.             end
  454.         end
  455.         local volume = math.floor(flow.volume * (1 + flow.flowQuality * projectile.item.quality) * (1 + flow.conical * projectile.distance_flown))
  456.         local flowski = dfhack.maps.spawnFlow(projectile.cur_pos, flow.flowType, flow.flowMat[1], flow.flowMat[2], volume)
  457.         if not flow.direction then
  458.             flowski.dest = xyz2pos(flowski.pos.x, flowski.pos.y, flowski.pos.z)
  459.         else
  460.             --determine distance per axis, devide all by largest distance to get the direction, increase by a factor of the volume and apply to cur_position.
  461.             local xyzdelta = { x = projectile.target_pos.x - projectile.origin_pos.x, y = projectile.target_pos.y - projectile.origin_pos.y, z = projectile.target_pos.z - projectile.origin_pos.z }
  462.             local flowreach = math.max(math.abs(xyzdelta.x), math.abs(xyzdelta.y), math.abs(xyzdelta.z)) / 0.3 * volume
  463.             xyzdelta = { x = math.floor(projectile.cur_pos.x + xyzdelta.x / flowreach), y = math.floor(projectile.cur_pos.y + xyzdelta.y / flowreach), z = math.floor(projectile.cur_pos.z + xyzdelta.z / flowreach) }
  464.             flowski.dest = xyz2pos(xyzdelta.x, xyzdelta.y, xyzdelta.z)
  465.         end
  466.         if not flow.expand then flowski.expanding = false end
  467.     end
  468. end
  469.  
  470. function terminate(projectile, props, mode)
  471.     if mode < 3 or props.deleteMe == -1 then
  472.         projectile.flags.to_be_deleted = true
  473.         projectile.item.flags.garbage_collect = true
  474.     end
  475.     props = nil
  476. end
  477.  
  478. noInit = true
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement