Advertisement
Guest User

Merging Chests v1.7.0-code-based blueprint-buildable update

a guest
May 18th, 2017
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.18 KB | None | 0 0
  1. if not MergingChests then MergingChests = {} end
  2. if not MergingChests.Migrations then MergingChests.Migrations = {} end
  3. if not MergingChests.Migrations._1_3_0 then MergingChests.Migrations_1_3_0 = false end
  4.  
  5. require("config")
  6.  
  7. function sortByX(entityA, entityB)
  8.     local xResult = entityA.position.x - entityB.position.x
  9.     if xResult == 0 then
  10.         return entityA.position.y < entityB.position.y
  11.     else
  12.         return xResult < 0
  13.     end
  14. end
  15.  
  16. function sortByY(entityA, entityB)
  17.     local yResult = entityA.position.y - entityB.position.y
  18.     if yResult == 0 then
  19.         return entityA.position.x < entityB.position.x
  20.     else
  21.         return yResult < 0
  22.     end
  23. end
  24.  
  25. function string.starts(String,Start)
  26.    return string.sub(String,1,string.len(Start))==Start
  27. end
  28.  
  29. function math.round(num)
  30.     if num >= 0 then
  31.         return math.floor(num + 0.5)
  32.     else
  33.         return math.ceil(num - 0.5)
  34.     end
  35. end
  36.  
  37. function MergingChests.OnTick()
  38.     if not MergingChests.Migrations._1_3_0 then
  39.             for _, force in pairs(game.forces) do
  40.                 if force.technologies["steel-processing"] then
  41.                     if force.technologies["steel-processing"].researched then
  42.                         for __, size in pairs(MergingChests.ChestRecipes) do
  43.                             force.recipes["wide-chest-"..size].enabled = true
  44.                             force.recipes["high-chest-"..size].enabled = true
  45.                         end
  46.                     end
  47.                 end
  48.             end
  49.         MergingChests.Migrations._1_3_0 = true
  50.     end
  51.     --############################################# Apriori code start #############################################
  52.     MergingChests.ProcessTasksToMerge()
  53.     --############################################# Apriori code end #############################################
  54. end
  55.  
  56. function MergingChests.GetChestSize(entity, ghost_mode)
  57.     if entity.name == "steel-chest" then
  58.         return 1
  59.     elseif string.starts(entity.name, "wide-chest-") or string.starts(entity.name, "high-chest-") then
  60.         -- skip "????-chest-" to get length
  61.         return tonumber(string.sub(entity.name, 12))
  62.     --############################################# Apriori code start #############################################
  63.     elseif ghost_mode and entity.name == "entity-ghost" and (string.starts(entity.ghost_name, "wide-chest-") or string.starts(entity.ghost_name, "high-chest-")) then
  64.         return tonumber(string.sub(entity.ghost_name, 12))
  65.     --############################################# Apriori code end #############################################
  66.     else
  67.         return 0
  68.     end
  69. end
  70.  
  71. function MergingChests.FindBoundingBoxSize(entities)
  72.     -- TODO calculate with wide chests
  73.     local min = entities[1].position
  74.     local max = entities[1].position
  75.     for i = 2, #entities do
  76.         --local size = MergingChests.GetChestSize(entities[i])
  77.         if min.x > entities[i].position.x then min.x = entities[i].position.x end
  78.         if min.y > entities[i].position.y then min.y = entities[i].position.y end
  79.         if max.x < entities[i].position.x then max.x = entities[i].position.x end
  80.         if max.y < entities[i].position.y then max.y = entities[i].position.y end
  81.     end
  82.    
  83.     local width = math.ceil(max.x) - math.floor(min.x)
  84.     local height = math.ceil(max.y) - math.floor(min.y)
  85.  
  86.     return width, height
  87. end
  88.  
  89. function MergingChests.SortIntoGroups(entities, mergeHorizontaly)
  90.     -- TODO calculate with wide chests
  91.     local result = {}
  92.     local maxXDiff = 0
  93.     local maxYDiff = 0
  94.     if mergeHorizontaly then
  95.         table.sort(entities, sortByY)
  96.         maxXDiff = 1.1
  97.         maxYDiff = 0.1
  98.     else
  99.         table.sort(entities, sortByX)
  100.         maxXDiff = 0.1
  101.         maxYDiff = 1.1
  102.     end
  103.  
  104.     local group = { entities[1] }
  105.     local lastX = entities[1].position.x
  106.     local lastY = entities[1].position.y
  107.     for i = 2, #entities do
  108.         if lastX + maxXDiff > entities[i].position.x and lastY + maxYDiff > entities[i].position.y then
  109.             if #group == MergingChests.MaxSize then
  110.                 table.insert(result, group)
  111.                 group = { entities[i] }
  112.             else
  113.                 table.insert(group, entities[i])
  114.             end
  115.         else
  116.             -- if there was something to merge, add group
  117.             if #group > 1 then
  118.                 table.insert(result, group)
  119.             end
  120.  
  121.             group = { entities[i] }
  122.         end
  123.        
  124.         lastX = entities[i].position.x
  125.         lastY = entities[i].position.y
  126.     end
  127.    
  128.     if #group > 1 then
  129.         table.insert(result, group)
  130.     end
  131.  
  132.     return result
  133. end
  134.  
  135. -- merging of chests
  136. function MergingChests.OnPlayerSelectedArea(event)
  137.     if event.item and event.item == "merge-chest-selector" then
  138.         local player = game.players[event.player_index]
  139.         if event.area.left_top.x ~= event.area.right_bottom.x and event.area.left_top.y ~= event.area.right_bottom.y then
  140.  
  141.             -- use event entities and remove everything but steel-chest
  142.             local entities = event.entities
  143.             for i = #entities, 1, -1 do
  144.                 if entities[i].name ~= "steel-chest" then
  145.                     table.remove(entities, i)
  146.                 end
  147.             end
  148.  
  149.             if #entities > 1 then
  150.                 --############################################# Apriori moved this block of code lower: #############################################
  151.                 MergingChests.PerformMerging(entities, player)
  152.             end
  153.         end
  154.     end
  155. end
  156.  
  157. function MergingChests.PerformMerging(entities, player)
  158.     -- find new bounding box
  159.     local width, height = MergingChests.FindBoundingBoxSize(entities)
  160.  
  161.     if width ~= height then
  162.         local mergeHorizontaly = width > height
  163.  
  164.         local entityGroups = MergingChests.SortIntoGroups(entities, mergeHorizontaly)
  165.         for _, group in ipairs(entityGroups) do
  166.             -- calculate new chest middle offset
  167.             local offset = 0
  168.             for j = 2, #group do
  169.                 offset = offset + MergingChests.GetChestSize(group[j]) / 2
  170.             end
  171.  
  172.             if mergeHorizontaly then
  173.                 MergingChests.MergeHorizontal(group, player, offset)
  174.             else
  175.                 MergingChests.MergeVertical(group, player, offset)
  176.             end
  177.         end
  178.     else
  179.         player.print("Merging of multiple lines of chests require selection width to be different from height.")
  180.     end
  181. end
  182.  
  183. function MergingChests.MergeHorizontal(entities, player, offset)
  184.     local newChest = player.surface.create_entity{name = "wide-chest-"..#entities, position = {entities[1].position.x + offset, entities[1].position.y}, force = player.force}
  185.    
  186.     MergingChests.MoveToInventory(entities, newChest)
  187. end
  188.  
  189. function MergingChests.MergeVertical(entities, player, offset)
  190.     local newChest = player.surface.create_entity{name = "high-chest-"..#entities, position = {entities[1].position.x, entities[1].position.y + offset}, force = player.force}
  191.  
  192.     MergingChests.MoveToInventory(entities, newChest)
  193. end
  194.  
  195. function MergingChests.MoveToInventory(fromEntities, toEntity)
  196.     local toInventory = toEntity.get_inventory(1)
  197.     local destinationStack = 1
  198.  
  199.     for i, entity in ipairs(fromEntities) do
  200.         local oldInventory = entity.get_inventory(1)
  201.         for j = 1, #oldInventory do
  202.             if destinationStack > #toInventory then
  203.                 break
  204.             elseif oldInventory[j].valid_for_read then
  205.                 toInventory[destinationStack].set_stack(oldInventory[j])
  206.                 destinationStack = destinationStack + 1
  207.             end
  208.         end
  209.         entity.destroy()
  210.     end
  211. end
  212.  
  213. -- splitting of chests
  214. function MergingChests.OnPlayerAltSelectedArea(event)
  215.     if event.item and event.item == "merge-chest-selector" then
  216.         local player = game.players[event.player_index]
  217.         if event.area.left_top.x ~= event.area.right_bottom.x and event.area.left_top.y ~= event.area.right_bottom.y then
  218.            
  219.             -- use event entities and remove everything but wide-chests and high-chests
  220.             local entities = event.entities
  221.             for i = #entities, 1, -1 do
  222.                 if not string.starts(entities[i].name, "wide-chest-") and not string.starts(entities[i].name, "high-chest-") then
  223.                     table.remove(entities, i)
  224.                 end
  225.             end
  226.  
  227.             if #entities > 0 then
  228.                 for i, entity in ipairs(entities) do
  229.                     local size = MergingChests.GetChestSize(entity)
  230.  
  231.                     if string.starts(entities[i].name, "wide-chest-") then
  232.                         MergingChests.SplitHorizontaly(entity, player, size)
  233.                     elseif string.starts(entities[i].name, "high-chest-") then
  234.                         MergingChests.SplitVerticaly(entity, player, size)
  235.                     end
  236.                 end
  237.             end
  238.         end
  239.     end
  240. end
  241.  
  242. function MergingChests.SplitHorizontaly(entity, player, count)
  243.     local newEntities = { }
  244.     for i = 1, count do
  245.         table.insert(newEntities, player.surface.create_entity{name = "steel-chest", position = {entity.position.x - (count + 1) / 2 + i, entity.position.y}, force = player.force})
  246.     end
  247.  
  248.     MergingChests.MoveToInventories(entity, newEntities)
  249. end
  250.  
  251. function MergingChests.SplitVerticaly(entity, player, count)
  252.     local newEntities = { }
  253.     for i = 1, count do
  254.         table.insert(newEntities, player.surface.create_entity{name = "steel-chest", position = {entity.position.x, entity.position.y - (count + 1) / 2 + i}, force = player.force})
  255.     end
  256.  
  257.     MergingChests.MoveToInventories(entity, newEntities)
  258. end
  259.  
  260. --############################################# Apriori code start #############################################
  261. function MergingChests.SplitGhostHorizontaly(entity, player, count)
  262.     for i = 1, count do
  263.         player.surface.create_entity{name = "entity-ghost", inner_name = "steel-chest", expires = false, position = {entity.position.x - (count + 1) / 2 + i, entity.position.y}, force = player.force}
  264.     end
  265.     entity.destroy()
  266. end
  267.  
  268. function MergingChests.SplitGhostVerticaly(entity, player, count)
  269.     for i = 1, count do
  270.         player.surface.create_entity{name = "entity-ghost", inner_name = "steel-chest", expires = false, position = {entity.position.x, entity.position.y - (count + 1) / 2 + i}, force = player.force}
  271.     end
  272.     entity.destroy()
  273. end
  274. --############################################# Apriori code end #############################################
  275.  
  276. function MergingChests.MoveToInventories(fromEntity, toEntities)
  277.     local oldInventory = fromEntity.get_inventory(1)
  278.     local destinationStack = 1
  279.     local j = 1
  280.     local toInventory = toEntities[j].get_inventory(1)
  281.  
  282.     for i = 1, #oldInventory do
  283.         if oldInventory[i].valid_for_read then
  284.             toInventory[destinationStack].set_stack(oldInventory[i])
  285.             destinationStack = destinationStack + 1
  286.         end
  287.  
  288.         if destinationStack > #toInventory then
  289.             j = j + 1
  290.             if j > #toEntities then
  291.                 break
  292.             end
  293.             toInventory = toEntities[j].get_inventory(1)
  294.             destinationStack = 1
  295.         end
  296.     end
  297.     fromEntity.destroy()
  298. end
  299.  
  300. --############################################# Apriori code start #############################################
  301. function MergingChests.OnBuilt(event)
  302.     local player = game.players[event.player_index]
  303.     local entity = event.created_entity
  304.     if entity and entity.valid and entity.name == "entity-ghost" and (string.starts(entity.ghost_name, "wide-chest-") or string.starts(entity.ghost_name, "high-chest-")) then
  305.         local size = MergingChests.GetChestSize(entity, true)
  306.         local task_to_merge = {
  307.       surface_index = entity.surface.index,
  308.       bounding_box  = entity.bounding_box,
  309.       chest_size    = size,
  310.       player_index  = event.player_index,
  311.       created_at    = event.tick
  312.     }
  313.         if string.starts(entity.ghost_name, "wide-chest-") then
  314.             MergingChests.SplitGhostHorizontaly(entity, player, size)
  315.         elseif string.starts(entity.ghost_name, "high-chest-") then
  316.             MergingChests.SplitGhostVerticaly(entity, player, size)
  317.         end
  318.         if not global.tasks_to_merge then
  319.             global.tasks_to_merge = {}
  320.         end
  321.         table.insert(global.tasks_to_merge, task_to_merge)
  322.     end
  323. end
  324.  
  325. function MergingChests.ProcessTasksToMerge()
  326.     if not global.tasks_to_merge then return end
  327.     for counter = #global.tasks_to_merge, 1, -1 do
  328.         local task = global.tasks_to_merge[counter]
  329.         if game.tick - task.created_at > 18000 then -- 5*60*60 = 5 minutes
  330.             table.remove(global.tasks_to_merge, counter)
  331.         else
  332.             local entities = game.surfaces[task.surface_index].find_entities(task.bounding_box)
  333.             for i = #entities, 1, -1 do
  334.                 if entities[i].name ~= "steel-chest" then
  335.                     table.remove(entities, i)
  336.                 end
  337.             end
  338.             if #entities == task.chest_size then
  339.                 MergingChests.PerformMerging(entities, game.players[task.player_index])
  340.                 table.remove(global.tasks_to_merge, counter)
  341.             end
  342.         end
  343.     end
  344.     if #global.tasks_to_merge < 1 then global.tasks_to_merge = nil end
  345. end
  346.  
  347. script.on_event(defines.events.on_built_entity, MergingChests.OnBuilt)
  348. --############################################# Apriori code end #############################################
  349.  
  350. script.on_event(defines.events.on_player_selected_area, MergingChests.OnPlayerSelectedArea)
  351. script.on_event(defines.events.on_player_alt_selected_area, MergingChests.OnPlayerAltSelectedArea)
  352. script.on_event(defines.events.on_tick, MergingChests.OnTick)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement