Advertisement
Guest User

Untitled

a guest
Mar 1st, 2019
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.80 KB | None | 0 0
  1. local versionNumber = "v2.5 - Doo Edit, BA specific"
  2.  
  3. function widget:GetInfo()
  4.   return {
  5.     name      = "Area Mex",
  6.     desc      = versionNumber .. " Adds a command to cap mexes in an area.",
  7.     author    = "Google Frog, NTG (file handling), Chojin (metal map), Doo Edit on Dec 13, 2017 (multiple enhancements)",
  8.     date      = "Oct 23, 2010",
  9.     license   = "GNU GPL, v2 or later",
  10.     handler   = true,
  11.     layer     = 0,
  12.     enabled   = true  --  loaded by default?
  13.   }
  14. end
  15.  
  16. --changelog (starting from 2.4)
  17. -- v2.5 (21/04/2018)
  18. -- Use Mex Snap's code to detect available positions for mexes
  19. --
  20. -- v2.4 (03/19/18 - Doo)
  21. -- Using WG.metalSpots instead of analyzing map at initialize()
  22. -- Erased non BA configs (since this version is only packed within BA)
  23.  
  24.  
  25. local maxMetalData = 40000 --2500000
  26. local pathToSave = "LuaUI/Widgets_BA/MetalMaps/" -- where to store mexmaps (MaDDoX: edited for BA 9.5x)
  27. -----------------
  28. --command notification and mex placement
  29.  
  30. local CMD_AREA_MEX       = 10100
  31.  
  32. local CMD_OPT_SHIFT = CMD.OPT_SHIFT
  33.  
  34. local spGetSelectedUnits = Spring.GetSelectedUnits
  35. local spInsertUnitCmdDesc = Spring.InsertUnitCmdDesc
  36. local spGetGroundHeight = Spring.GetGroundHeight
  37. local spGiveOrderToUnit = Spring.GiveOrderToUnit
  38. local spGetUnitPosition = Spring.GetUnitPosition
  39. local spGetTeamUnits = Spring.GetTeamUnits
  40. local spGetMyTeamID = Spring.GetMyTeamID
  41. local spGetUnitDefID = Spring.GetUnitDefID
  42. local team = Spring.GetMyTeamID()
  43. local spTestBuildOrder = Spring.TestBuildOrder
  44.  
  45. local spGetActiveCommand = Spring.GetActiveCommand
  46. local spGetMapDrawMode = Spring.GetMapDrawMode
  47. local spSendCommands = Spring.SendCommands
  48.  
  49. local toggledMetal
  50.  
  51. local unba = false
  52. if (Spring.GetModOptions().unba or "disabled") == "enabled" then
  53.     unba = true
  54. end
  55.  
  56. local mexIds = {}
  57. local mexes = {}
  58.  
  59. local sqrt = math.sqrt
  60. local tasort = table.sort
  61. local taremove = table.remove
  62.  
  63. local mexBuilderDef = {}
  64.  
  65. local mexBuilder = {}
  66.  
  67. local UnbaCommandersMexesDefs = { -- UnbaCommandersMexesDefs[unitDefID][level] = mexBuildDef = {buildings = 2, {[1] = landmexid * -1, [2]= seamexid* -1}}
  68.     [UnitDefNames["armcom"].id] = {
  69.         [1] = {buildings = 2, building = { [1] = UnitDefNames["armmex"].id * -1, [2]= UnitDefNames["armuwmex"].id* -1}},
  70.         [2] = {buildings = 2, building = { [1] = UnitDefNames["armmex"].id* -1, [2]= UnitDefNames["armuwmex"].id* -1}},
  71.         [3] = {buildings = 2, building = { [1] = UnitDefNames["armmex"].id* -1, [2]= UnitDefNames["armuwmex"].id* -1}},
  72.         [4] = {buildings = 2, building = { [1] = UnitDefNames["armmoho"].id* -1, [2]= UnitDefNames["armuwmme"].id* -1}},
  73.         [5] = {buildings = 2, building = { [1] = UnitDefNames["armmoho"].id* -1, [2]= UnitDefNames["armuwmme"].id* -1}},
  74.         [6] = {buildings = 2, building = { [1] = UnitDefNames["armmoho"].id* -1, [2]= UnitDefNames["armuwmme"].id* -1}},
  75.         [7] = {buildings = 2, building = { [1] = UnitDefNames["armmoho"].id* -1, [2]= UnitDefNames["armuwmme"].id* -1}},
  76.         [8] = {buildings = 2, building = { [1] = UnitDefNames["armmoho"].id* -1, [2]= UnitDefNames["armuwmme"].id* -1}},
  77.         [9] = {buildings = 2, building = { [1] = UnitDefNames["armmoho"].id* -1, [2]= UnitDefNames["armuwmme"].id* -1}},
  78.         [10] = {buildings = 2, building = { [1] = UnitDefNames["armmoho"].id* -1, [2]= UnitDefNames["armuwmme"].id* -1}},
  79.         [11] = {buildings = 2, building = { [1] = UnitDefNames["armmoho"].id* -1, [2]= UnitDefNames["armuwmme"].id* -1}},
  80.         },
  81.     [UnitDefNames["corcom"].id] = {
  82.         [1] = {buildings = 2, building = { [1] = UnitDefNames["cormex"].id* -1, [2]= UnitDefNames["coruwmex"].id* -1}},
  83.         [2] = {buildings = 2, building = { [1] = UnitDefNames["cormex"].id* -1, [2]= UnitDefNames["coruwmex"].id* -1}},
  84.         [3] = {buildings = 2, building = { [1] = UnitDefNames["cormex"].id* -1, [2]= UnitDefNames["coruwmex"].id* -1}},
  85.         [4] = {buildings = 2, building = { [1] = UnitDefNames["cormoho"].id* -1, [2]= UnitDefNames["coruwmme"].id* -1}},
  86.         [5] = {buildings = 2, building = { [1] = UnitDefNames["cormoho"].id* -1, [2]= UnitDefNames["coruwmme"].id* -1}},
  87.         [6] = {buildings = 2, building = { [1] = UnitDefNames["cormoho"].id* -1, [2]= UnitDefNames["coruwmme"].id* -1}},
  88.         [7] = {buildings = 2, building = { [1] = UnitDefNames["cormoho"].id* -1, [2]= UnitDefNames["coruwmme"].id* -1}},
  89.         [8] = {buildings = 2, building = { [1] = UnitDefNames["cormoho"].id* -1, [2]= UnitDefNames["coruwmme"].id* -1}},
  90.         [9] = {buildings = 2, building = { [1] = UnitDefNames["cormoho"].id* -1, [2]= UnitDefNames["coruwmme"].id* -1}},
  91.         [10] = {buildings = 2, building = { [1] = UnitDefNames["cormoho"].id* -1, [2]= UnitDefNames["coruwmme"].id* -1}},
  92.         [11] = {buildings = 2, building = { [1] = UnitDefNames["cormoho"].id* -1, [2]= UnitDefNames["coruwmme"].id* -1}},
  93.         }
  94.     }
  95. local isUnbaCommander = {}
  96.  
  97.  
  98. local function Distance(x1,z1,x2,z2)
  99.     local dis = (x1-x2)*(x1-x2)+(z1-z2)*(z1-z2)
  100.     return dis
  101. end
  102.  
  103. function widget:UnitCreated(unitID, unitDefID)
  104.  
  105.     local ud = UnitDefs[unitDefID]
  106.     if (Spring.GetModOptions().unba or "disabled") == "enabled" and (UnitDefs[unitDefID].name == "armcom" or UnitDefs[unitDefID].name == "corcom") then
  107.         if UnbaCommandersMexesDefs[unitDefID][1] then
  108.             mexBuilder[unitID] = UnbaCommandersMexesDefs[unitDefID][1]
  109.         end
  110.         return
  111.     else
  112.     if mexBuilderDef[ud] then
  113.         mexBuilder[unitID] = mexBuilderDef[ud]
  114.         return
  115.     end
  116.    
  117.     if ud.buildOptions then
  118.         for i, option in ipairs(ud.buildOptions) do
  119.             if mexIds[option] then
  120.                 if mexBuilderDef[ud] then
  121.                     mexBuilderDef[ud].buildings = mexBuilderDef[ud].buildings+1
  122.                     mexBuilderDef[ud].building[mexBuilderDef[ud].buildings] = mexIds[option]*-1
  123.                 else
  124.                     mexBuilderDef[ud] = {buildings = 1, building = {[1] = mexIds[option]*-1}}
  125.                 end
  126.                 mexBuilder[unitID] = mexBuilderDef[ud]
  127.             end
  128.         end
  129.     end
  130.  
  131.     end
  132. end
  133.  
  134. function widget:UnitTaken(unitID, unitDefID, oldTeam, newTeam)
  135.     if not mexBuilder[unitID] then
  136.     widget:UnitCreated(unitID, unitDefID, newTeam)
  137.     end
  138. end
  139.  
  140. function widget:UnitGiven(unitID, unitDefID, newTeam, oldTeam)
  141.     if not mexBuilder[unitID] then
  142.     widget:UnitCreated(unitID, unitDefID, newTeam)
  143.     end
  144. end
  145.  
  146. function widget:Update()
  147.     if unba then
  148.         local commanderTable = Spring.GetTeamUnitsByDefs(Spring.GetMyTeamID(), {UnitDefNames["armcom"].id, UnitDefNames["corcom"].id})
  149.         for ct, unitID in pairs (commanderTable) do
  150.             local _, realxp = Spring.GetUnitExperience(unitID)
  151.             local level = (realxp < 0.99) and (math.floor(realxp * 10) + 1 ) or 11
  152.             if UnbaCommandersMexesDefs[Spring.GetUnitDefID(unitID)][level] then
  153.                 mexBuilder[unitID] = UnbaCommandersMexesDefs[Spring.GetUnitDefID(unitID)][level]
  154.             end
  155.         end
  156.     end
  157.  
  158.     local _,cmd,_ = spGetActiveCommand()
  159.     if (cmd == CMD_AREA_MEX) then
  160.         if (spGetMapDrawMode() ~= 'metal') then
  161.             if Spring.GetMapDrawMode() == "los" then
  162.                 retoggleLos = true
  163.             end
  164.             spSendCommands({'ShowMetalMap'})
  165.             toggledMetal = true
  166.         end
  167.     else
  168.         if toggledMetal then
  169.             spSendCommands({'ShowStandard'})
  170.             if retoggleLos then
  171.                 Spring.SendCommands("togglelos")
  172.                 retoggleLos = nil
  173.             end
  174.             toggledMetal = false
  175.         end
  176.     end
  177. end
  178.  
  179. function AreAlliedUnits(unitID) -- Is unitID allied with me ?
  180. return Spring.AreTeamsAllied(Spring.GetMyTeamID(), Spring.GetUnitTeam(unitID))
  181. end
  182.  
  183. function NoAlliedMex(x,z, batchextracts) -- Is there any better and allied mex at this location (returns false if there is)
  184.     local mexesatspot = Spring.GetUnitsInCylinder(x,z, Game.extractorRadius)
  185.         for ct, uid in pairs(mexesatspot) do
  186.             if mexIds[Spring.GetUnitDefID(uid)] and AreAlliedUnits(uid) and UnitDefs[Spring.GetUnitDefID(uid)].extractsMetal >= batchextracts then
  187.                 return false
  188.             end
  189.         end
  190.     return true
  191. end
  192. local function GetClosestMetalSpot(x, z)
  193.     local bestSpot
  194.     local bestDist = math.huge
  195.     local metalSpots = WG.metalSpots
  196.     for i = 1, #metalSpots do
  197.         local spot = metalSpots[i]
  198.         local dx, dz = x - spot.x, z - spot.z
  199.         local dist = dx*dx + dz*dz
  200.         if dist < bestDist then
  201.             bestSpot = spot
  202.             bestDist = dist
  203.         end
  204.     end
  205.     return bestSpot
  206. end
  207.  
  208. local function GetClosestMexPosition(spot, x, z, uDefID, facing)
  209.     local bestPos
  210.     local bestDist = math.huge
  211.     local positions = WG.GetMexPositions(spot, uDefID, facing, true)
  212.     for i = 1, #positions do
  213.         local pos = positions[i]
  214.         local dx, dz = x - pos[1], z - pos[3]
  215.         local dist = dx*dx + dz*dz
  216.         if dist < bestDist then
  217.             bestPos = pos
  218.             bestDist = dist
  219.         end
  220.     end
  221.     return bestPos
  222. end
  223.  
  224. function widget:CommandNotify(id, params, options) 
  225.     if (id == CMD_AREA_MEX) then
  226.     mexes = WG.metalSpots
  227.  
  228.         local cx, cy, cz, cr = params[1], params[2], params[3], params[4]
  229.         if (not cr) or (cr < Game.extractorRadius) then
  230.             cr = Game.extractorRadius
  231.         end
  232.         local cr = cr
  233.        
  234.         local xmin = cx-cr
  235.         local xmax = cx+cr
  236.         local zmin = cz-cr
  237.         local zmax = cz+cr
  238.        
  239.         local commands = {}
  240.         local orderedCommands = {}
  241.         local dis = {}
  242.        
  243.         local ux = 0
  244.         local uz = 0
  245.         local us = 0
  246.        
  247.         local aveX = 0
  248.         local aveZ = 0
  249.        
  250.         local units=spGetSelectedUnits()
  251.         local maxbatchextracts = 0
  252.         local batchMexBuilder = {}
  253.         local lastprocessedbestbuilder = nil
  254.        
  255.         for i, id in pairs(units) do
  256.         if mexBuilder[id] then -- Get best extract rates, save best builderID
  257.             if UnitDefs[(mexBuilder[id].building[1])*-1].extractsMetal > maxbatchextracts then
  258.                 maxbatchextracts = UnitDefs[(mexBuilder[id].building[1])*-1].extractsMetal
  259.                 lastprocessedbestbuilder = id
  260.             end
  261.         end
  262.         end
  263.        
  264.         local batchSize = 0
  265.         local shift = options.shift
  266.  
  267.         for i, id in pairs(units) do -- Check position, apply guard orders to "inferiors" builders and adds superior builders to current batch builders
  268.             if mexBuilder[id] then
  269.                 if UnitDefs[(mexBuilder[id].building[1])*-1].extractsMetal == maxbatchextracts then
  270.                 local x,_,z = spGetUnitPosition(id)
  271.                 ux = ux+x
  272.                 uz = uz+z
  273.                 us = us+1
  274.                 lastprocessedbestbuilder = id
  275.                 batchSize = batchSize + 1
  276.                 batchMexBuilder[batchSize] = id
  277.                 else
  278.                     if not shift then
  279.                     spGiveOrderToUnit(id, CMD.STOP, {} , CMD.OPT_RIGHT )
  280.                     end
  281.                 local cmdQueue = Spring.GetUnitCommands(id, 1)
  282.                 spGiveOrderToUnit(id, CMD.GUARD, {lastprocessedbestbuilder} , {"shift"})
  283.                 end
  284.             end
  285.         end
  286.        
  287.    
  288.         if (us == 0) then
  289.             return
  290.         else
  291.             aveX = ux/us
  292.             aveZ = uz/us
  293.         end
  294.    
  295.         for k, mex in pairs(mexes) do      
  296.             if not (mex.x%16 == 8) then
  297.                 mexes[k].x = mexes[k].x + 8 - (mex.x%16)
  298.             end
  299.             if not (mex.z%16 == 8) then
  300.                 mexes[k].z = mexes[k].z + 8 - (mex.z%16)
  301.             end
  302.             mex.x = mexes[k].x
  303.             mex.z = mexes[k].z
  304.             if (Distance(cx,cz,mex.x,mex.z) < cr^2) then -- circle area, slower
  305.                 if NoAlliedMex(mex.x, mex.z, maxbatchextracts) == true then
  306.                     commands[#commands+1] = {x = mex.x, z = mex.z, d = Distance(aveX,aveZ,mex.x,mex.z)}
  307.                 end
  308.            
  309.             end
  310.         end
  311.    
  312.         local noCommands = #commands
  313.         while noCommands > 0 do
  314.      
  315.             tasort(commands, function(a,b) return a.d < b.d end)
  316.             orderedCommands[#orderedCommands+1] = commands[1]
  317.             aveX = commands[1].x
  318.             aveZ = commands[1].z
  319.             taremove(commands, 1)
  320.             for k, com in pairs(commands) do       
  321.                 com.d = Distance(aveX,aveZ,com.x,com.z)
  322.             end
  323.             noCommands = noCommands-1
  324.         end
  325.    
  326.         local shift = options.shift
  327.         local ctrl = options.ctrl or options.meta
  328.         for ct, id in pairs(batchMexBuilder) do
  329.             if not shift then
  330.                 spGiveOrderToUnit(id, CMD.STOP, {} , CMD.OPT_RIGHT )
  331.             end
  332.         end
  333.        
  334.             local shift = true
  335.         for ct, id in pairs(batchMexBuilder) do
  336.  
  337.                 for i, command in ipairs(orderedCommands) do
  338.                     local Y = Spring.GetGroundHeight(command.x, command.z)
  339.                     local spotSize = 0 -- GetSpotSize(x, z)
  340.                     if ((i % batchSize == ct % batchSize or i % #orderedCommands == ct % #orderedCommands) and ctrl) or not ctrl then
  341.                     for j=1, mexBuilder[id].buildings do
  342.                         local def = UnitDefs[-mexBuilder[id].building[j]]
  343.                         local buildable = 0
  344.                         newx, newz = command.x, command.z
  345.                         if not (buildable ~= 0) then -- If location unavailable, check surroundings (extractorRadius - 25). Should consider replacing 25 with avg mex x,z sizes
  346.                             local bestPos = GetClosestMexPosition(GetClosestMetalSpot(newx, newz), newx-2*Game.extractorRadius, newz-2*Game.extractorRadius, -mexBuilder[id].building[j], "s")
  347.                             if bestPos then
  348.                                 newx, newz = bestPos[1], bestPos[3]
  349.                                 buildable = true
  350.                             end
  351.                         end
  352.  
  353.                         -- for ox = 0, Game.extractorRadius do
  354.                                 -- for oz = 0, Game.extractorRadius do
  355.                                         -- if math.sqrt(ox^2 + oz^2) <= math.sqrt(Game.extractorRadius^2)-spotSize and spTestBuildOrder(-mexBuilder[id].building[j],command.x + ox,spGetGroundHeight(command.x + ox,command.z + oz),command.z + oz,1) ~= 0 then
  356.                                             -- buildable = spTestBuildOrder(-mexBuilder[id].building[j],command.x + ox,spGetGroundHeight(command.x + ox,command.z + oz),command.z + oz,1)
  357.                                             -- newx = command.x + ox
  358.                                             -- newz = command.z + oz
  359.                                             -- break
  360.                                         -- elseif math.sqrt(ox^2 + oz^2) <= math.sqrt(Game.extractorRadius^2)-spotSize and spTestBuildOrder(-mexBuilder[id].building[j],command.x - ox,spGetGroundHeight(command.x - ox,command.z + oz),command.z + oz,1) ~= 0 then
  361.                                             -- buildable = spTestBuildOrder(-mexBuilder[id].building[j],command.x + ox,spGetGroundHeight(command.x + ox,command.z + oz),command.z + oz,1)
  362.                                             -- newx = command.x - ox
  363.                                             -- newz = command.z + oz
  364.                                             -- break
  365.                                        
  366.                                         -- elseif math.sqrt(ox^2 + oz^2) <= math.sqrt(Game.extractorRadius^2)-spotSize and spTestBuildOrder(-mexBuilder[id].building[j],command.x + ox,spGetGroundHeight(command.x + ox,command.z - oz),command.z - oz,1) ~= 0 then
  367.                                             -- buildable = spTestBuildOrder(-mexBuilder[id].building[j],command.x + ox,spGetGroundHeight(command.x + ox,command.z + oz),command.z + oz,1)
  368.                                             -- newx = command.x + ox
  369.                                             -- newz = command.z - oz
  370.                                             -- break
  371.                                        
  372.                                         -- elseif math.sqrt(ox^2 + oz^2) <= math.sqrt(Game.extractorRadius^2)-spotSize and spTestBuildOrder(-mexBuilder[id].building[j],command.x - ox,spGetGroundHeight(command.x - ox,command.z - oz),command.z - oz,1) ~= 0 then
  373.                                             -- buildable = spTestBuildOrder(-mexBuilder[id].building[j],command.x + ox,spGetGroundHeight(command.x + ox,command.z + oz),command.z + oz,1)
  374.                                             -- command.x = command.x - ox
  375.                                             -- command.z = command.z - oz
  376.                                             -- break
  377.                                         -- end         
  378.                                     -- if buildable ~= 0 then
  379.                                         -- break
  380.                                     -- end
  381.                                 -- end
  382.                                 -- if buildable ~= 0 then
  383.                                     -- break
  384.                                 -- end
  385.                             -- end
  386.                         -- end
  387.                         if buildable ~= 0 then
  388.                             spGiveOrderToUnit(id, mexBuilder[id].building[j], {newx,spGetGroundHeight(newx,newz),newz} , {"shift"})
  389.                             break
  390.                         elseif def.maxWaterDepth and -def.maxWaterDepth < Y and def.minWaterDepth and -def.minWaterDepth > Y then
  391.                             local hsize = def.xsize*4
  392.                             local unitsatmex = Spring.GetUnitsInRectangle(command.x-hsize, command.z-hsize, command.x+hsize, command.z + hsize)
  393.                             local blockers = {}
  394.                             for x = command.x - hsize, command.x + hsize, 8 do
  395.                                 for z = command.z - hsize, command.z + hsize, 8 do
  396.                                     local _,blocker = Spring.GetGroundBlocked(x,z,x+7,z+7)
  397.                                     if blocker and not blockers[blocker] then
  398.                                         spGiveOrderToUnit(id, CMD.RECLAIM, {blocker}, {"shift"})
  399.                                         blockers[blocker] = true
  400.                                     end
  401.                                 end
  402.                             end
  403.                             spGiveOrderToUnit(id, CMD.INSERT, {-1, mexBuilder[id].building[j], CMD.OPT_INTERNAL, command.x,spGetGroundHeight(command.x,command.z),command.z} , {shift = true, internal = true, alt = true})
  404.                             break
  405.                         end
  406.                     end
  407.                 end
  408.                 end
  409.             end
  410.        
  411.  
  412.         return true
  413.  
  414.     end
  415.  
  416. end
  417.  
  418. --------------------
  419.  
  420. function widget:CommandsChanged()
  421.     local units=spGetSelectedUnits()
  422.     for i, id in pairs(units) do
  423.         if mexBuilder[id] then
  424.             local customCommands = widgetHandler.customCommands
  425.            
  426.             table.insert(customCommands, {         
  427.                 id      = CMD_AREA_MEX,
  428.                 type    = CMDTYPE.ICON_AREA,
  429.                 tooltip = 'Define an area to make mexes in',
  430.                 name    = 'Mex',
  431.                 cursor  = 'Repair',
  432.                 action  = 'areamex',
  433.             })
  434.             return
  435.         end
  436.     end
  437. end
  438.  
  439. function widget:Initialize()
  440.     if not WG.metalSpots then
  441.         Spring.Echo("<Area Mex> This widget requires the 'Metalspot Finder' widget to run.")
  442.         widgetHandler:RemoveWidget(self)
  443.     end
  444.     mexIds[UnitDefNames['armmex'].id] = UnitDefNames['armmex'].id
  445.     mexIds[UnitDefNames['cormex'].id] = UnitDefNames['cormex'].id
  446.     mexIds[UnitDefNames['armmoho'].id] = UnitDefNames['armmoho'].id
  447.     mexIds[UnitDefNames['cormoho'].id] = UnitDefNames['cormoho'].id        
  448.     mexIds[UnitDefNames['armuwmex'].id] = UnitDefNames['armuwmex'].id
  449.     mexIds[UnitDefNames['coruwmex'].id] = UnitDefNames['coruwmex'].id
  450.     mexIds[UnitDefNames['armuwmme'].id] = UnitDefNames['armuwmme'].id
  451.     mexIds[UnitDefNames['coruwmme'].id] = UnitDefNames['coruwmme'].id
  452.     local units = spGetTeamUnits(spGetMyTeamID())
  453.     for i, id in ipairs(units) do
  454.         widget:UnitCreated(id, spGetUnitDefID(id))
  455.     end
  456. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement