function getAverage(items) local count = 0 local sum = 0 for k, v in pairs(items) do count = count + 1 sum = sum + v end return sum / count end function getItemProtoFromName(n) return game.item_prototypes[n] end function setGUISize(element, w, h) if (not element.style) then return end if (w) then element.style.width = w end if (h) then element.style.height = h end end function getAllIdleRobotsInNetwork(logistic_network) local robots = {} if (logistic_network == nil) then return robots end for k, cell in pairs(logistic_network.cells) do local inv = cell.owner.get_inventory(defines.inventory.roboport_robot) for i = 1, #inv do local itemstack = inv[i] if (itemstack.valid_for_read) then if robots[itemstack.name] then robots[itemstack.name].count = robots[itemstack.name].count + itemstack.count else robots[itemstack.name] = { count = itemstack.count, item = itemstack.prototype, ent = itemstack.prototype.place_result } end end end end return robots end function getDistanceBetweenVectors(a, b) local x = a.x - b.x local y = a.y - b.y return math.abs(math.sqrt((x * x) + (y * y))) end function getBotsBeingRecalled(recall_chest) local tbl = {} for k, v in pairs(global.teleportQueue) do if (tbl.destination == recall_chest) then tbl[v.itemname] = tbl[v.itemname] and tbl[v.itemname] + v.count or v.count end end return tbl end function addToTeleportQueue(source, destination, itemstack) local currentTick = game.tick local destinationInv = destination.get_inventory(defines.inventory.chest) local dist = getDistanceBetweenVectors(source.position, destination.position) local robotEnt = itemstack.prototype.place_result local unitsPerTick = (source["force"]["worker_robots_speed_modifier"] + 1) * robotEnt.speed * settings.global["recall-speed-modifier"].value local can_insert = destinationInv.can_insert(itemstack) -- game.print("" .. itemstack.count .. " " .. itemstack.name .. -- "to teleport queue") if (not can_insert) then return end -- game.print("Can't recall, no space!") local item_name = itemstack.prototype.name local queueEntry = { source = source, srcPos = source.position, destination = destination, destPos = destination.position, startTick = currentTick, endTick = math.abs(currentTick + (dist / unitsPerTick)), -- itemstack = itemstack, itemname = item_name, surface = destination.surface, count = itemstack.count } table.insert(global.teleportQueue, queueEntry) itemstack.clear() global.teleportQueueEntryCount = global.teleportQueueEntryCount + 1 -- local timeTo end function buildRecallGui(baseGUI, entity) if (not entity) then return end if (entity.name ~= 'robot-recall-chest') then return end local logistic_network = entity.logistic_network if (baseGUI['robot-recall-chest'] ~= nil) then baseGUI['robot-recall-chest'].destroy() end local recallFrame = baseGUI.add({ type = "frame", name = "robot-recall-chest", direction = "vertical", -- style="standalone_inner_frame_in_outer_frame" }) recallFrame.caption = {'robot-recall.chest-title'} -- ply.opened = recallFrame -- this is for vanilla at 1x scale local INV_DIMENSIONS = {width = 874, height = 436, verticalOffset = -88} local WIDTH = 300 local HEIGHT = INV_DIMENSIONS.height local recallScrollFlowFrame = recallFrame.add( { type = "frame", name = "frame", style = "inside_shallow_frame", direction = "vertical" }) local recallScrollFlow = recallScrollFlowFrame.add { type = "scroll-pane", name = "scrollpane" } setGUISize(recallFrame, WIDTH, HEIGHT) setGUISize(recallScrollFlow, WIDTH - 20, HEIGHT - 50) -- game.print(ply.gui) local ply = game.players[baseGUI.player_index] local res = ply.display_resolution local scl = ply.display_scale global.openedGUIPlayers[baseGUI.player_index] = {ply = game.players[baseGUI.player_index], ent = entity} recallFrame.location = { (res.width / 2) - (INV_DIMENSIONS.width * scl * 0.5) - WIDTH * scl, (res.height / 2) - (INV_DIMENSIONS.height / 2 * scl) + (INV_DIMENSIONS.verticalOffset * scl) } local robots = getAllIdleRobotsInNetwork(logistic_network) updateRecallGuiList(baseGUI, robots, logistic_network) end function updateRecallGuiListEntry(itemname, count, baseGui) local scrollPane = baseGui['robot-recall-chest']['frame']['scrollpane'] local ply = game.players[baseGui.player_index] local flow = baseGui['robot-recall-chest']['frame']['scrollpane'][itemname] or scrollPane.add({type = "flow", name = itemname}) local spritebutton = flow['spritebutton'] or flow.add( { type = "sprite-button", tooltip = { "robot-recall.recall-button-tooltip", getItemProtoFromName(itemname).localised_name }, name = "spritebutton" }) if (spritebutton.sprite == "") then spritebutton.sprite = "item/" .. itemname end local progressbar = baseGui['robot-recall-chest']['frame']['scrollpane'][itemname .. '-progressbar'] or scrollPane.add({ type = "progressbar", name = itemname .. '-progressbar', visible = false, value = 0 }) if (ply.opened and ply.opened.name == "robot-recall-chest") then if (ply.opened.get_inventory(defines.inventory.chest).can_insert( {name = itemname})) then spritebutton.enabled = true else spritebutton.enabled = false end end local label = flow['label'] or flow.add({type = "label", name = "label"}) label.caption = {"robot-recall.recall-count", getItemProtoFromName(itemname).localised_name, count} label.style.single_line = false end function updateRecallGuiList(baseGui, robots, logistic_network) local scrollPane = baseGui['robot-recall-chest']['frame']['scrollpane'] local ply = game.players[baseGui.player_index] if (logistic_network == nil or not logistic_network.valid) then local label = scrollPane['no-network'] or scrollPane.add( { type = "label", caption = {"robot-recall.chest-no-network", ":("}, name = "no-network" }) label.style.horizontal_align = "center" label.style.width = scrollPane.style.maximal_width - 10 return end if (scrollPane['no-network'] and scrollPane['no-network'].valid) then scrollPane['no-network'].destroy() end local count = 0 local recalled = getBotsBeingRecalled() for k, v in pairs(recalled) do if (robots[k]) then robots[k].count = robots[k].count + v else robots[k] = { count = v, item = getItemProtoFromName(k), ent = getItemProtoFromName(k).place_result } end end for k, v in pairs(robots) do updateRecallGuiListEntry(k, v.count, baseGui) end local count = table_size(robots) if (count * 2 ~= table_size(scrollPane)) then for k, v in pairs(baseGui['robot-recall-chest']['frame']['scrollpane'] .children) do if (v.valid and v.type == "flow" and not robots[v.name]) then local progress = baseGui['robot-recall-chest']['frame']['scrollpane'][v.name .. '-progressbar'] v.destroy() progress.destroy() -- baseGui['robot-recall-chest']['frame']['scrollpane'][k..'-progressbar'].destroy() end end end if (count == 0 and not scrollPane['no-robots-label']) then local label = scrollPane.add({ type = "label", caption = {'robot-recall.chest-no-robot-in-roboport', ":("}, name = "no-robots-label" }) -- baseGUI.style.height label.style.single_line = false label.style.horizontal_align = 'center' label.style.width = scrollPane.style.maximal_width - 10 return elseif (count > 0 and scrollPane['no-robots-label'] and scrollPane['no-robots-label'].valid) then scrollPane['no-robots-label'].destroy() end -- if (scrollPane['no-robots-label']) then -- game.print(scrollPane['no-robots-label'].valid) -- end end function updateRecallGuiListProgress(baseGui, robots, logistic_network) if (not baseGui or not baseGui['robot-recall-chest']) then return end local scrollPane = baseGui['robot-recall-chest']['frame']['scrollpane'] local ply = game.players[baseGui.player_index] for _, element in pairs(scrollPane.children) do if (element.type == "progressbar") then local progressbar = element local itemname = string.sub(element.name, 0, -1 - string.len("-progressbar")) local totalProgress = {} for k, v in pairs(global.teleportQueue) do -- if (global.teleportQueue.destination) then -- end if (v.destination and v.destination.valid and ply.opened and ply.opened.valid and v.destination.unit_number == ply.opened.unit_number and v.itemname == itemname) then local currentTick = game.tick - v.startTick local finishTick = v.endTick - v.startTick -- game.print("TELEPORT QUEUE LOl") table.insert(totalProgress, currentTick / finishTick) end end if (table_size(totalProgress) ~= 0) then progressbar.visible = true local newprog = getAverage(totalProgress) progressbar.value = math.max(newprog, progressbar.value) else progressbar.visible = false progressbar.value = 0 -- local robots = getAllIdleRobotsInNetwork(ply.opened.ent) -- updateRecallGuiList(v.ply.gui.screen, robots, v.ent.logistic_network) end end end end function createRobotRecallGUI(ent, ply, gui) -- if (not (ent and ent.name == "robot-recall-chest")) then return end if (gui['robot-recall-chest'] ~= nil) then gui['robot-recall-chest'].destroy() end local recallFrame = gui.add({ type = "frame", name = "robot-recall-chest", direction = "vertical" }) recallFrame.caption = {'robot-recall.chest-title'} -- ply.opened = recallFrame -- this is for vanilla at 1x scale local INV_DIMENSIONS = {width = 874, height = 436, verticalOffset = -88} local WIDTH = 300 local HEIGHT = INV_DIMENSIONS.height local recallScrollFlowFrame = recallFrame.add( { type = "frame", name = "frame", style = "inside_shallow_frame_with_padding", direction = "vertical" }) local recallScrollFlow = recallScrollFlowFrame.add { type = "scroll-pane", name = "scrollpane" } setGUISize(recallFrame, WIDTH, HEIGHT) setGUISize(recallScrollFlow, WIDTH - 20, HEIGHT - 50) -- game.print(ply.gui) local res = ply.display_resolution local scl = ply.display_scale recallFrame.location = { (res.width / 2) - (INV_DIMENSIONS.width * scl * 0.5) - WIDTH * scl, (res.height / 2) - (INV_DIMENSIONS.height / 2 * scl) + (INV_DIMENSIONS.verticalOffset * scl) } -- if (ent and ent.logistic_network) then -- -- game.print(ent.logistic_network.robots) -- -- recallFrame.direction = "vertical" -- -- drawRobotRecallGui(recallScrollFlow, ent.logistic_network) -- end end function callRobotsToEntity(location_ent, logisticNetwork, robotItem) for k, cell in pairs(logisticNetwork.cells) do local roboport = cell.owner local inv = roboport.get_inventory(defines.inventory.roboport_robot) for i = 1, #inv do local itemstack = inv[i] if (itemstack.valid_for_read) then if (itemstack.prototype == robotItem) then addToTeleportQueue(roboport, location_ent, itemstack) end end end end end function updateTeleportJobs(event) local warning = false for k, e in ipairs(global.teleportQueue) do -- if (not itemstack.valid) if ((not e.destination or not e.destination.valid)) then -- game.print("Source or destination not valid! Removing queue item!") local count if (not warning) then -- if (__DebugAdapter) then __DebugAdapter.print("Hello!") end game.print("Robot Recall Chest cannot be found! Robots have been returned to their source position.") warning = true end if (e.source.valid) then count = e.source.insert({name=e.itemname, count=e.count}) end if (not count or count ~= e.count) then count = count and (e.count - count) or e.count e.surface.spill_item_stack(e.srcPos, {name=e.itemname, count=count}) game.print(count .. " robots have been dropped at their source position, there is no room in the source.") end global.teleportQueue[k] = nil global.teleportQueueEntryCount = global.teleportQueueEntryCount - 1 end if (event.tick >= e.endTick) then -- game.print("Teleport job finished!") -- if () then return end local destinationInv = e.destination.get_inventory( defines.inventory.roboport_robot) or e.destination.get_inventory( defines.inventory.chest) local sourceInv = e.source.get_inventory( defines.inventory.roboport_robot) or e.source .get_inventory(defines.inventory.chest) -- if (destinationInv.can_insert({name = e.itemname, count = e.count})) then local amnt = destinationInv.insert({name = e.itemname, count = e.count}) if (amnt ~= e.count) then local initialRemainder = e.count - amnt local remainder = e.count - amnt remainder = remainder - sourceInv.insert({name = e.itemname, count = remainder}) if (remainder ~= 0) then game.print("Recall of " .. e.count .. " '" .. e.itemname .. "' robots has (partially) failed. " .. remainder .. ' could not be recalled.') if (e.destination.logistic_network and e.destination.logistic_network.valid) then for _, cell in pairs(e.destination.logistic_network.cells) do if (remainder ~= 0) then local inv = cell.owner.get_inventory( defines.inventory.roboport_robot) remainder = remainder - inv.insert({name = e.itemname, count = remainder}) end end if (remainder ~= initialRemainder) then -- game.print("Recall of " .. e.count .. " " .. e.itemname .. " has failed.") game.print( (initialRemainder - remainder) .. " robots have been redeployed to roboports") end end if (remainder ~= 0) then game.print(remainder .. " robots have been dropped in front of the recall station.") local ent = e.destination.surface.spill_item_stack(e.destination.position, {name = e.itemname, count = remainder}) -- game.print(ent.position) end end end -- end -- for _, v in pairs(global.openedGUIPlayers) do -- -- getAllIdleRobotsInNetwork(p.ply.opened) -- -- local robots = getAllIdleRobotsInNetwork(v.ent.logistic_network) -- -- updateRecallGuiList(v.ply.gui.screen, robots, v.ent.logistic_network) -- end table.remove(global.teleportQueue, k) global.teleportQueueEntryCount = global.teleportQueueEntryCount - 1 end end end function initRecallChest(event) end script.on_event({defines.events.on_built_entity}, function(event) -- game.print("Hello!") if (event.created_entity and event.created_entity.name == "robot-recall-chest") then initRecallChest(event) end end) script.on_event({defines.events.on_robot_built_entity}, function(event) if (event.created_entity and event.created_entity.name == "robot-recall-chest") then initRecallChest(event) end end) script.on_event({defines.events.on_gui_opened}, function(event) local ent = event.entity local ply = game.players[event.player_index] local gui = ply.gui.screen buildRecallGui(gui, ent) -- recallFrame.add({}) -- local closeButton = recallFrame.add({type="button",name="robot-recall-chest.close", caption="Close!"}) end) script.on_event({defines.events.on_gui_closed}, function(event) local ent = event.entity local ply = game.players[event.player_index] local gui = ply.gui.screen if (gui['robot-recall-chest'] ~= nil) then if (global.openedGUIPlayers[event.player_index]) then table.remove(global.openedGUIPlayers, event.player_index) end gui['robot-recall-chest'].destroy() end -- game.print('on_gui_close') end) script.on_event({defines.events.on_gui_click}, function(event) -- game.print(event) local ply = game.players[event.player_index] -- local items = game.item_prototypes if (event.element.type == "sprite-button" and ply.opened and ply.opened.name == "robot-recall-chest") then local itemname = event.element.parent.name local item = game.item_prototypes[itemname] -- game.print('recalling "' .. itemname .. '"') callRobotsToEntity(ply.opened, ply.opened.logistic_network, item, event.tick) end if (event.element.name == "robot-recall-chest.close") then event.element.parent.destroy() end end) -- script.on_nth_tick(10, function(event) -- if (event.tick % 5 == 0) then -- for k, v in pairs(game.players) do -- -- local = v.gui.screen -- local gui = v.gui.screen -- updateRecallGui(event, gui, v) -- end -- end -- end) -- script.on_event({defines.events.on_tick}, function(event) -- if (global.teleportQueueEntryCount > 0) then -- for k, v in pairs(global.openedGUIPlayers) do -- -- local = v.gui.screen -- local gui = v.ply.gui.screen -- -- __DebugAdapter.print("Updating every tick!") -- if (v.ent.logistic_network and v.ent.logistic_network.valid) then -- local robots = getAllIdleRobotsInNetwork(v.ent.logistic_network) -- updateRecallGuiList(v.ply.gui.screen, robots, -- v.ent.logistic_network) -- end -- end -- end -- end) script.on_nth_tick(2, function(event) if (not global.teleportQueueEntryCount or not global.teleportQueue) then return end if (global.teleportQueueEntryCount == 0 and global.hasChanged) then global.hasChanged = false for k, v in pairs(global.openedGUIPlayers) do updateRecallGuiListProgress(v.ply.gui.screen) end elseif (global.teleportQueueEntryCount > 0) then global.hasChanged = true for k, v in pairs(global.openedGUIPlayers) do updateRecallGuiListProgress(v.ply.gui.screen) end end end) script.on_nth_tick(10, function(event) -- if (not global.teleportQueueEntryCount if (global.teleportQueueEntryCount and global.teleportQueueEntryCount > 0) then updateTeleportJobs(event) end end) script.on_nth_tick(180, function(event) for k, v in pairs(global.openedGUIPlayers) do -- __DebugAdapter.print("Updating every 10 ticks!") -- local = v.gui.screen local gui = v.ply.gui.screen if (v.ent.logistic_network and v.ent.logistic_network.valid) then local robots = getAllIdleRobotsInNetwork(v.ent.logistic_network) updateRecallGuiList(v.ply.gui.screen, robots, v.ent.logistic_network) end end -- end end)