574 lines
22 KiB
Lua

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)