Advertisement
Guest User

modtools/arty-mod

a guest
Sep 30th, 2018
212
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 21.33 KB | None | 0 0
  1. --[[modtools/arty-mod]]
  2. --Allows a unit to use fixed artillery.
  3. --modified version of zaporozhets' awesome cannon-smoke script
  4. --based in turn on scripts by Roses, expwnent and Fleeting Frames
  5. --now with miltibarrel support! more blam, more fun!
  6.  
  7. local usage = [====[
  8.  
  9. modtools/arty-mod
  10. ===========================
  11. Turns buildings into artillery pieces.
  12.     -clear
  13.         unregister all entries
  14.     -list
  15.         list current ammo/weapons/material entries and their values
  16.     -artyType
  17.         argument is barrel item subtype, such as ITEM_TOOL_BARREL_BOMBARD. will pass properties to any building containing this item, and set the item to be loaded with ammunition. type of ammunition used and the buildings themselves are defined in raws; reactions prefixed with ARTILLERY_LOAD will load their reagents into an empty barrel, ARTILLERY_FIRE will fire the gun and ARTILLERY_ROTATE_R and ARTILLERY_ROTATE_L will rotate the building, if the names of its 4 directions end in _N, _E, _S and _W. 1x1 guns will fire in all directions and do not need rotate reactions. multiple barrels can be housed in a single building, setting the load and fire jobs to repeat will load/fire until all barrels are loaded/unloaded.
  18.         -shootForce - amount of force the projectile is give, works exactly the same as ranged weapon raw shoot force token (shoot force * 50000 / size * density)
  19.         -range - minrange, maxrange. affects targeting and projectile behavior, targetting is done within range, actual projectile maxrange is maxrange + 3 to prevent guns wasting ammunition on targets moving around the edge of targeting range.
  20.         -loadTime - delay, skill scaling. time in ticks it takes to load a barrel and how strongly this scales with skill. defined as [ number scale ] or just number, in which case scaling defaults to 1.
  21.         -rotateTime - delay, skill scaling. same as loadTime, only for building rotation.
  22.         -baseRof - delay, skill scaling. delay between shots for multibarrel buildings, if fire is set to repeat.
  23.         -altRof - delay, interval, skill scaling. every (interval) many shots this delay is used instead.
  24.         -windupRof - delay multiplier, shots before wound up, ticks per winddown. multiplies delay until a certain number of shots is fired, and winds back down once the gun stops firing.
  25.  
  26.     examples:
  27.         -artyType ITEM_TOOL_BARREL_BOMBARD -loadTime 1000 -range 100 -shootForce 72000 -rotateTime 600
  28.         -artyType ITEM_TOOL_BARREL_HANDGONNE -loadTime 180 -baseRof 30 -range 40 -shootForce 4000 -rotateTime 200
  29.        
  30. ]====]
  31.  
  32. local utils = require 'utils'
  33. local eventful = require 'plugins.eventful'
  34. eventful.enableEvent(eventful.eventType.JOB_INITIATED, 5)
  35.  
  36. artyProperties = artyProperties or {}
  37. jobTable = false
  38. gunTable = {}
  39.  
  40. local validArgs = utils.invert({
  41.     'help',
  42.     'clear',
  43.     'list',
  44.     'artyType',
  45.     'shootForce',
  46.     'range',
  47.     'loadTime',
  48.     'rotateTime',
  49.     'baseRof',
  50.     'altRof',
  51.     'windupRof'
  52. })
  53.  
  54. local args = utils.processArgs({...}, validArgs)
  55.  
  56. if args.artyType then
  57.     artyProperties[args.artyType] = { shootForce = tonumber(args.shootForce) }
  58.     if args.loadTime then artyProperties[args.artyType].loadTime = { tonumber(args.loadTime[1]) or tonumber(args.loadTime), tonumber(args.loadTime[2]) or 1 } end
  59.     if args.rotateTime then artyProperties[args.artyType].rotateTime = { tonumber(args.rotateTime[1]) or tonumber(args.rotateTime), tonumber(args.rotateTime[2]) or 1 } end
  60.     if args.range then artyProperties[args.artyType].range = { tonumber(args.range[1]) or 0, tonumber(args.range[2]) or tonumber(args.range) } end
  61.     if args.baseRof then artyProperties[args.artyType].baseRof = { tonumber(args.baseRof[1]) or tonumber(args.baseRof), tonumber(args.baseRof[2]) or 1 } end
  62.     if args.altRof then artyProperties[args.artyType].altRof = { tonumber(args.altRof[1]) or tonumber(args.altRof), tonumber(args.altRof[2]) or 2, tonumber(args.altRof[3]) or 1 } end
  63.     if args.windupRof then artyProperties[args.artyType].windupRof = { tonumber(args.windupRof[1]) or tonumber(args.windupRof), tonumber(args.windupRof[2]) or 10, tonumber(args.windupRof[3]) or 40 } end
  64. end
  65.  
  66. if args.clear then
  67.     artyProperties = {}
  68.     return
  69. end
  70.  
  71. if args.list then
  72.     for rer,ere in pairs(artyProperties) do
  73.         print("- "..rer..":")
  74.         for rerrer,rerere in pairs(ere) do
  75.             print("    "..rerrer,rerere)
  76.         end
  77.         print()
  78.     end
  79.     return
  80. end
  81.  
  82. if args.help then
  83.     print(usage)
  84.     return
  85. end
  86.  
  87. eventful.onJobInitiated.blam = function(job)
  88.     if string.match(job.reaction_name, "ARTILLERY") then
  89.         if jobTable == false then
  90.             jobTable = {}
  91.             jobChecker()
  92.         end
  93.     end
  94. end
  95.  
  96. function jobChecker()
  97.     jobTable = {}
  98.     for link,job in utils.listpairs(df.global.world.jobs.list) do
  99.         if string.match(job.reaction_name, "ARTILLERY") then
  100.             jobTable[#jobTable + 1] = job
  101.         end
  102.     end
  103.     for _,job in pairs(jobTable) do
  104.         local funcy = {nop, nop, nop}
  105.         local mode = 1
  106.         if not gunTable[job.general_refs[0].building_id] then funcy[1] = updateGunTable end
  107.         if #job.general_refs > 1 then makeFearless(job.general_refs[1].unit_id, 1) end
  108.         if job.completion_timer > 0 and job.flags.suspend == false and #job.general_refs > 0 then
  109.             if job.reaction_name == "ARTILLERY_FIRE" then
  110.                 funcy[3] = findTarget
  111.             elseif job.reaction_name == "ARTILLERY_ROTATE_L" or job.reaction_name == "ARTILLERY_ROTATE_R" then
  112.                 funcy[3] = rotGun
  113.             elseif string.match(job.reaction_name, "ARTILLERY_LOAD") then
  114.                 funcy[3] = loadGun
  115.             end
  116.             for _,func in ipairs(funcy) do
  117.                 func(job, df.building.find(job.general_refs[0].building_id), df.unit.find(job.general_refs[1].unit_id), mode)
  118.             end
  119.         end
  120.     end
  121.     --for rer,ere in pairs(jobTable) do print('checking '..ere.reaction_name,"id: "..ere.id, "timer: "..ere.completion_timer) end
  122.     for _,rer in pairs(jobTable) do
  123.         dfhack.timeout(50,'ticks',function() jobChecker() end)
  124.         return
  125.     end
  126.     jobTable = false
  127.     --print('killed jobChecker')
  128. end
  129.  
  130. function nop() end --function table iterator will break if it finds a field with a nothing, but is just fine running a nothing
  131.  
  132. function updateGunTable(job, gun, firer, mode)
  133.     if mode == 1 then
  134.         gunTable[gun.id] =  {props = {increment = 1, windup = 0, baseRof = {80, 1}}, loaded = {}, active = false, targetPos = {}}
  135.         gunTable[gun.id].muzzlePos = checkDirection(gun, nil, 2)
  136.         for _, barrelItem in ipairs(gun.contained_items) do
  137.             if barrelItem.item:getSubtype() ~= -1 and artyProperties[barrelItem.item.subtype.id] and barrelItem.use_mode == 2 then
  138.                 barrelItem.item.flags.container = true
  139.                 for field,value in pairs(artyProperties[barrelItem.item.subtype.id]) do
  140.                     gunTable[gun.id].props[field] = value
  141.                 end
  142.             end
  143.         end
  144.     end
  145.     for _, barrelItem in ipairs(gun.contained_items) do
  146.         if barrelItem.item.flags.container == true and barrelItem.use_mode == 2 and #dfhack.items.getContainedItems(barrelItem.item) > 0  then
  147.             for _, loadItem in ipairs(dfhack.items.getContainedItems(barrelItem.item)) do
  148.                 if tostring(loadItem._type) == "<type: item_barst>" and loadItem.mat_type == 8 then
  149.                     gunTable[gun.id].loaded[#gunTable[gun.id].loaded].powder = loadItem
  150.                 else
  151.                     gunTable[gun.id].loaded[#gunTable[gun.id].loaded + 1] = {}
  152.                     gunTable[gun.id].loaded[#gunTable[gun.id].loaded].ammo = loadItem
  153.                 end
  154.             end
  155.         end
  156.     end
  157. end
  158.  
  159. function makeFearless(unitId, mode)
  160.     local firer = df.unit.find(unitId)
  161.     if not firer then return end
  162.     if mode == 1 then
  163.         if firer.flags2.vision_missing == false then
  164.             firer.flags2.vision_good = false
  165.             firer.flags2.vision_missing = true
  166.             dfhack.timeout(1000, "ticks", function() makeFearless(firer.id, 2) end)
  167.         end
  168.     else
  169.         firer.flags2.vision_good = true
  170.         firer.flags2.vision_missing = false
  171.     end
  172. end
  173.  
  174. function findTarget(job, gun, firer, mode)
  175.     --mode 2 is run after a delay, and needs a sanity check to see if job, firer aren't canceled/dead/on fire/etc
  176.     if mode == 1 then
  177.         if gunTable[gun.id].active == true then return end
  178.     else
  179.         local isJobDead = sanityCheck(job)
  180.         if isJobDead == true then
  181.             gunTable[gun.id].active = false
  182.             return
  183.         end
  184.     end
  185.     if #gunTable[gun.id].loaded == 0 then
  186.         dfhack.gui.showAnnouncement(getFullName(firer).." cancels fire cannon: Not loaded.", 12)
  187.         job.flags['repeat'] = false
  188.         job.completion_timer = 0
  189.         gunTable[gun.id].active = false
  190.         return
  191.     end
  192.     local range = gunTable[gun.id].props.range or {0, 100}
  193.     local nearestUnit = false
  194.     for _,target in ipairs(df.global.world.units.active) do
  195.         if checkDirection(gun, target, 1) then
  196.             local targetDistance = findDistance(firer.pos, target.pos)
  197.             if targetDistance >= range[1] and targetDistance <= range[2] then
  198.                 if isHostile(firer, target, job.reaction_name) then
  199.                     if canPathTo(firer.pos, target.pos, targetDistance) then
  200.                         nearestUnit = target
  201.                     end
  202.                 end
  203.             end
  204.         end
  205.     end
  206.     if nearestUnit then
  207.         job.completion_timer = -1
  208.         gunTable[gun.id].targetPos = xyz2pos(nearestUnit.pos.x,nearestUnit.pos.y,nearestUnit.pos.z)
  209.         firefirefire(job, gun, firer, mode)
  210.     else
  211.         job.completion_timer = -1
  212.         gunTable[gun.id].active = false
  213.     end
  214. end
  215.  
  216. function firefirefire(job, gun, firer, mode)
  217.     local ammoItem = gunTable[gun.id].loaded[#gunTable[gun.id].loaded].ammo
  218.     local powderItem = gunTable[gun.id].loaded[#gunTable[gun.id].loaded].powder
  219.     local barrelId = dfhack.items.getContainer(ammoItem).id
  220.     if powderItem then powderItem.flags.garbage_collect = true end
  221.     ammoItem.flags.forbid = false
  222.     ammoItem:moveToGround(gunTable[gun.id].muzzlePos.x,gunTable[gun.id].muzzlePos.y,gunTable[gun.id].muzzlePos.z)
  223.     local projectile = dfhack.items.makeProjectile(ammoItem)
  224.     --same formula ranged weapons use
  225.     projectile.unk22 = math.floor(gunTable[gun.id].props.shootForce * 50000 / (projectile.item.weight * 1000000 + projectile.item.weight_fraction))
  226.     --not the same formula ranged weapons use, building guns get a high hitrate bonus through ranged-mod
  227.     local firerSkill = 0
  228.     for _,skill in ipairs(firer.status.current_soul.skills) do
  229.         if skill.id == 48 then firerSkill = skill.rating end
  230.     end
  231.     projectile.hit_rating = math.floor(math.random(0,10) + firerSkill * 3.5)
  232.     projectile.origin_pos = gunTable[gun.id].muzzlePos
  233.     projectile.prev_pos = gunTable[gun.id].muzzlePos
  234.     projectile.cur_pos = gunTable[gun.id].muzzlePos
  235.     projectile.target_pos = gunTable[gun.id].targetPos
  236.     projectile.min_hit_distance = gunTable[gun.id].props.range[1]
  237.     projectile.fall_threshold = gunTable[gun.id].props.range[2] + 3
  238.     projectile.min_ground_distance = gunTable[gun.id].props.range[2] + 2
  239.     projectile.bow_id = barrelId
  240.     projectile.firer = firer
  241.     projectile.distance_flown = 0
  242.     projectile.fall_counter = 0
  243.     projectile.fall_delay = 0
  244.     gunTable[gun.id].loaded[#gunTable[gun.id].loaded] = nil
  245.     if job.flags['repeat'] == true then --'job.flags.repeat' makes lua very confused, good thing theres another way to check this
  246.         gunTable[gun.id].active = true
  247.         local completionTime = getCompletionTime(job, gun, firer, 1)
  248.         dfhack.timeout(completionTime,'ticks',function() findTarget(job, gun, firer, 2) end)
  249.     else
  250.         job.completion_timer = 0
  251.         gunTable[gun.id].active = false
  252.     end
  253. end
  254.  
  255. function loadGun(job, gun, firer, mode)
  256.     if mode == 1 then
  257.         if gunTable[gun.id].active == true then return else gunTable[gun.id].active = true end
  258.         local completionTime = getCompletionTime(job, gun, firer, 3)
  259.         dfhack.timeout(completionTime,'ticks',function() loadGun(job, gun, firer, 2) end)
  260.         job.completion_timer = 400
  261.         return
  262.     else
  263.         local isJobDead = sanityCheck(job)
  264.         if isJobDead == true then
  265.             gunTable[gun.id].active = false
  266.             return
  267.         end
  268.     end
  269.     if #job.items == 0 then
  270.         gunTable[gun.id].active = false
  271.         job.completion_timer = 0
  272.     end
  273.     local alreadyLoaded = true
  274.     for _, barrelItem in ipairs(gun.contained_items) do
  275.         if barrelItem.item.flags.container == true and barrelItem.use_mode == 2 and #dfhack.items.getContainedItems(barrelItem.item) == 0 then
  276.             alreadyLoaded = false
  277.             for _, loadItem in ipairs(job.items) do
  278.                 local deletThis = false
  279.                 if loadItem.item:getStackSize() > 1 then
  280.                     loadItem.item:addStackSize(-1)
  281.                 else
  282.                     deletThis = true
  283.                 end
  284.                 local loadItemCopy = df.item.find(dfhack.items.createItem(loadItem.item:getType(), loadItem.item:getSubtype(), loadItem.item.mat_type, loadItem.item.mat_index, firer))
  285.                 if loadItemCopy:getSubtype() > -1 then loadItemCopy.quality = loadItem.item.quality end
  286.                 dfhack.items.moveToContainer(loadItemCopy,barrelItem.item)
  287.                 updateGunTable(job, gun, firer, 2)
  288.                 if deletThis == true then loadItem.item.flags.garbage_collect = true end
  289.             end
  290.             break
  291.         end
  292.     end
  293.     if alreadyLoaded then
  294.         dfhack.gui.showAnnouncement(getFullName(firer).." cancels loading artillery: Fully loaded.", 12)
  295.         job.flags['repeat'] = false
  296.         gunTable[gun.id].active = false
  297.         job.completion_timer = 0
  298.         --df.global.world.jobs:cancel_job(job) explodes the game, bad idea
  299.         return
  300.     elseif job.flags['repeat'] == true then
  301.         gunTable[gun.id].active = true
  302.         local completionTime = getCompletionTime(job, gun, firer, 2)
  303.         dfhack.timeout(completionTime,'ticks',function() loadGun(job, gun, firer, 2) end)
  304.         job.completion_timer = 400
  305.     else
  306.         gunTable[gun.id].active = false
  307.         job.completion_timer = 0
  308.     end
  309. end
  310.  
  311. function rotGun(job, gun, firer, mode)
  312.     if mode == 1 then
  313.         if gunTable[gun.id].active == true then return else gunTable[gun.id].active = true end
  314.         local completionTime = getCompletionTime(job, gun, firer, 1)
  315.         dfhack.timeout(completionTime,'ticks',function() rotGun(job, gun, firer, 2) end)
  316.         job.completion_timer = 400
  317.         return
  318.     end
  319.     gunTable[gun.id].active = false
  320.     local isJobDead = sanityCheck(job)
  321.     if isJobDead == true then return end
  322.     local gunDirection = string.sub(df.global.world.raws.buildings.all[gun.custom_type].code, -2)
  323.     if job.reaction_name == "ARTILLERY_ROTATE_R" then
  324.         if gunDirection == "_N" or gunDirection == "_E" or gunDirection == "_S" then
  325.             gun.custom_type = gun.custom_type + 1
  326.         else
  327.             gun.custom_type = gun.custom_type - 3
  328.         end
  329.     else
  330.         if gunDirection == "_E" or gunDirection == "_S" or gunDirection == "_W" then
  331.             gun.custom_type = gun.custom_type - 1
  332.         else
  333.             gun.custom_type = gun.custom_type + 3
  334.         end
  335.     end
  336.     gunTable[gun.id].muzzlePos = checkDirection(gun, nil, 2)
  337.     job.completion_timer = 0
  338. end
  339.  
  340. function getCompletionTime(job, gun, firer, mode)
  341.     local completionTime
  342.     local skillMult = 1
  343.     local artyProps = gunTable[gun.id].props
  344.     for _,firerSkill in ipairs(firer.status.current_soul.skills) do
  345.         if firerSkill.id == 48 then
  346.             skillMult = (30 - firerSkill.rating) / 30
  347.         end
  348.     end
  349.     if mode == 1 then
  350.         local windupMult = 1
  351.         if artyProps.windupRof then windupMult = windupRof(artyProps, 1) end
  352.         if not artyProps.altRof then
  353.             completionTime = math.floor(artyProps.baseRof[1] * windupMult * (1 + (skillMult - 1) * artyProps.baseRof[2]))
  354.         elseif artyProps.increment < artyProps.altRof[2] then
  355.             artyProps.increment = artyProps.increment + 1
  356.             completionTime = math.floor(artyProps.baseRof[1] * windupMult * (1 + (skillMult - 1) * artyProps.baseRof[2]))
  357.         else
  358.             artyProps.increment = 1
  359.             completionTime = math.floor(artyProps.altRof[1] * windupMult * (1 + (skillMult - 1) * artyProps.altRof[3]))
  360.         end
  361.     elseif mode == 2 then
  362.         completionTime = math.floor(artyProps.loadTime[1] * (1 + (skillMult - 1) * artyProps.loadTime[2]))
  363.     elseif mode == 3 then
  364.         completionTime = math.floor(artyProps.rotateTime[1] * (1 + (skillMult - 1) * artyProps.rotateTime[2]))
  365.     end
  366.     return completionTime
  367. end
  368.  
  369. function windupRof(artyProps, mode)
  370.     if mode == 1 then
  371.         local windupMult = 1 + (artyProps.windupRof[1] - 1) * (artyProps.windupRof[2] - math.min(artyProps.windupRof[2],artyProps.windup)) / artyProps.windupRof[2]
  372.         if artyProps.windup == 0 then
  373.             dfhack.timeout(artyProps.windupRof[3],'ticks',function() windupRof(artyProps, 2) end)
  374.         end
  375.         artyProps.windup = math.min(artyProps.windup + 1, artyProps.windupRof[2] + 1)
  376.         return windupMult
  377.     end
  378.     artyProps.windup = artyProps.windup - 1
  379.     if artyProps.windup > 0 then
  380.         dfhack.timeout(artyProps.windupRof[3],'ticks',function() windupRof(artyProps, 2) end)
  381.     end
  382. end
  383.  
  384. function canPathTo(originPos, targetPos, targetDistance)
  385.     local directionVector = df.coord:new()
  386.     directionVector.x = targetPos.x - originPos.x
  387.     directionVector.y = targetPos.y - originPos.y
  388.     directionVector.z = targetPos.z - originPos.z
  389.     local stepAmount = 1 / targetDistance
  390.     local step = 0
  391.     local passCount = 0
  392.     local linePath = df.coord:new()
  393.     while step <= 1 do
  394.         linePath.x = math.floor(originPos.x + (directionVector.x * step) + 0.5)
  395.         linePath.y = math.floor(originPos.y + (directionVector.y * step) + 0.5)
  396.         linePath.z = math.floor(originPos.z + (directionVector.z * step) + 0.5)
  397.         if tilePassable(linePath) then
  398.             passCount = passCount + 1
  399.         end
  400.         step = step + stepAmount
  401.     end
  402.     if passCount >= 1 / stepAmount then
  403.         return true
  404.     else
  405.         return false
  406.     end
  407. end
  408.  
  409. function sanityCheck(job)
  410.     local isJobDead = true
  411.     for link,joblisting in utils.listpairs(df.global.world.jobs.list) do
  412.         if joblisting == job then
  413.             isJobDead = false
  414.             break
  415.         end
  416.     end
  417.     if isJobDead == true or #job.general_refs < 1 then return isJobDead end
  418. end
  419.  
  420. function tilePassable(position)
  421.     local isPassable = false
  422.     if dfhack.maps.getTileBlock(position) ~= nil then
  423.         if dfhack.maps.getTileBlock(position).walkable[position.x%16][position.y%16] ~= 0 or --found walkable tile
  424.         dfhack.maps.getTileBlock(position).tiletype[position.x%16][position.y%16] == 32 or --found empty space
  425.         dfhack.maps.getTileBlock(position).tiletype[position.x%16][position.y%16] == 1 then --found stairs
  426.             isPassable = true
  427.         end
  428.     end
  429.     return isPassable
  430. end
  431.  
  432. function findDistance(originPos, targetPos)
  433.     return math.floor(math.sqrt(((originPos.x - targetPos.x)^2) + ((originPos.y - targetPos.y)^2) + ((originPos.z - targetPos.z)^2)) + 0.5 )
  434. end
  435.  
  436. function isHostile(firer, target, reaction) --TODO: implement better hostility check
  437.     if reaction == "ARTILLERY_FIRE" then
  438.         if target.flags2.killed == false then
  439.             if target.civ_id == firer.civ_id or target.flags1.diplomat == true or target.flags1.merchant == true or target.flags2.visitor == true or target.flags2.resident == true then
  440.                 return false
  441.             else
  442.                 return true
  443.             end
  444.         end
  445.     elseif reaction == "ARTILLERY_FIRE_TEST" then
  446.         if target.flags2.killed == false then
  447.             if target.name.first_name ~= "" or target.flags1.diplomat == true or target.flags1.merchant == true or target.flags2.visitor == true or target.flags2.resident == true then
  448.                 return false
  449.             else
  450.                 return true
  451.             end
  452.         end
  453.     end
  454. end
  455.  
  456. function checkDirection(gun, target, mode) --mode 1 checks direction of target against cannon orientation, mode 2 returns muzzle position
  457.     if gun.centerx == gun.x1 then -- If your top right is also your center square, then your a 1x1 gun and able to turn 360 degrees.
  458.         if mode == 1 then return true
  459.         elseif mode == 2 then
  460.             return xyz2pos(gun.centerx, gun.centery, gun.z)
  461.         end
  462.     end
  463.     local gunDirection = string.sub(df.global.world.raws.buildings.all[gun.custom_type].code, -2)
  464.     if gunDirection == "_N" then --Never
  465.         if mode == 1 then
  466.             if target.pos.y < gun.centery and math.abs(target.pos.x - gun.centerx) < math.abs(target.pos.y - gun.centery) * 1.2 then
  467.                 return true else return false
  468.             end
  469.         elseif mode == 2 then
  470.             return xyz2pos(gun.centerx, gun.centery - 1, gun.z)
  471.         end
  472.     elseif gunDirection == "_E" then --Eat
  473.         if mode == 1 then
  474.             if target.pos.x > gun.centerx and math.abs(target.pos.y - gun.centery) < math.abs(target.pos.x - gun.centerx) * 1.2 then
  475.                 return true else return false
  476.             end
  477.         elseif mode == 2 then
  478.             return xyz2pos(gun.centerx + 1, gun.centery, gun.z)
  479.         end
  480.     elseif gunDirection == "_S" then --Shredded
  481.         if mode == 1 then
  482.             if target.pos.y > gun.centery and math.abs(target.pos.x - gun.centerx) < math.abs(target.pos.y - gun.centery) * 1.2 then
  483.                 return true else return false
  484.             end
  485.         elseif mode == 2 then
  486.             return xyz2pos(gun.centerx, gun.centery + 1, gun.z)
  487.         end
  488.     elseif gunDirection == "_W" then --Wheat
  489.         if mode == 1 then
  490.             if target.pos.x < gun.centerx and math.abs(target.pos.y - gun.centery) < math.abs(target.pos.x - gun.centerx) * 1.2 then
  491.                 return true else return false
  492.             end
  493.         elseif mode == 2 then
  494.             return xyz2pos(gun.centerx - 1, gun.centery, gun.z)
  495.         end
  496.     end
  497. end
  498.  
  499. function getFullName(unit) --doesn't account for heroic names yet because I wasn't sure if they replace the normal surname
  500.     local firstName = unit.name.first_name
  501.     firstName = firstName:sub(1,1):upper()..firstName:sub(2)
  502.     local surname = ""
  503.     for indx, wordID in pairs(unit.name.words) do
  504.         if wordID ~= -1 then
  505.             local word = df.global.world.raws.language.translations[unit.name.language].words[unit.name.words[indx]].value
  506.             if indx == 0 then
  507.                 surname = word:sub(1,1):upper()..word:sub(2)
  508.             elseif indx == 1 then
  509.                 surname = surname..word
  510.             end
  511.         end
  512.     end
  513.     return firstName.." "..surname..", Siege Operator"
  514. end
  515.  
  516. function printTargetDebug(firer, nearestUnit, nearestDistance)
  517.     print(firer.name.first_name.." fired cannon from: ")
  518.     print(pos2xyz(firer.pos))
  519.     print("  at:")
  520.     if nearestUnit.name.first_name ~= "" then
  521.         print(" "..nearestUnit.name.first_name)
  522.     else
  523.         print(" Creature")
  524.     end
  525.     print(" race: "..tostring(df.global.world.raws.creatures.all[nearestUnit.race].name[0]))
  526.     print(" distance: "..tostring(nearestDistance))
  527. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement