1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2026-04-28 21:04:39 +02:00
Files
ComfyFactorio/modules/infinity_storage.lua
2026-03-29 18:32:13 +02:00

1976 lines
67 KiB
Lua

local Event = require 'utils.event'
local Color = require 'utils.color_presets'
local Global = require 'utils.global'
local Gui = require 'utils.gui'
local Task = require 'utils.task'
local Token = require 'utils.token'
local Commands = require 'utils.commands'
local this =
{
main_containers = {},
inf_gui = {},
saved_containers = {},
valid_storage =
{
['storage-tank'] = true
},
enabled = true,
editor = {},
disable_normal_placement = true,
debug = false,
cost_to_convert = 200
}
local default_limit = 50000
local converter_frame_for_player_name = Gui.uid_name()
local convert_to_infinite_container = Gui.uid_name()
local item_name_frame_name = Gui.uid_name()
local master_storage_btn_name = Gui.uid_name()
local toggle_render
local module_name = '[Infinity Storage] '
local insert = table.insert
local floor = math.floor
local pairs = pairs
local Public = {}
Global.register(
this,
function (tbl)
this = tbl
end
)
local remove_container
local refresh_main_frame
local delay_refresh_main_frame_task_token =
Token.register(
function (event)
local player_index = event.player_index
local player = game.get_player(player_index)
if not player or not player.valid then
return
end
local unit_number = event.unit_number
if not unit_number then
return
end
refresh_main_frame({ unit_number = unit_number, player = player })
end
)
function Public.get_table()
return this
end
local function draw_convert_button(parent, entity)
local frame = parent[converter_frame_for_player_name]
if frame and frame.valid then
Gui.destroy(frame)
end
local anchor =
{
gui = defines.relative_gui_type.storage_tank_gui,
position = defines.relative_gui_position.right
}
frame =
parent.add
{
type = 'frame',
name = converter_frame_for_player_name,
anchor = anchor,
direction = 'vertical'
}
local button =
frame.add
{
type = 'sprite-button',
sprite = 'item/' .. entity.name,
name = convert_to_infinite_container,
style = Gui.button_style,
tooltip = '[color=blue][Infinity storage][/color]\nYou can easily convert this container to an infinity storage.\n\nCosts ' .. this.cost_to_convert .. ' coins.'
}
Gui.set_data(button, entity)
end
local function validate()
if not this or not this.main_containers then
this =
{
main_containers = {},
inf_gui = {},
saved_containers = {},
valid_storage =
{
['storage-tank'] = true
},
enabled = true,
editor = {},
disable_normal_placement = true,
debug = false,
cost_to_convert = 200
}
end
end
local function has_value(tab)
local count = 0
for _, _ in pairs(tab) do
count = count + 1
end
return count
end
local function return_value(tab)
for index, value in pairs(tab) do
if value then
tab[index] = nil
return value, index
end
end
end
local function detect_item(container)
local fluidbox = container.entity.fluidbox
local fluid = fluidbox[1]
if fluid then
fluidbox.set_filter(1, { name = fluid.name, force = true })
container.item = fluid.name
container.temperature = fluid.temperature
return true, container.item
end
return false
end
local function combine_tempatures(first_count, first_tempature, second_count, second_tempature)
if first_tempature == second_tempature then
return first_tempature
end
if first_tempature == nil then
return second_tempature
end
local total_count = first_count + second_count
return (first_tempature * first_count / total_count) + (second_tempature * second_count / total_count)
end
local function update_single_container(container)
local entity = container.entity
if not entity or not entity.valid then
return
end
if container.direction.state == 'none' then
return
end
if container.item == nil then
detect_item(container)
end
local item = container.item
if item == nil then
return
end
local capacity = container.capacity
local max_capacity = entity.fluidbox.get_capacity(1) * 0.5
if capacity > max_capacity then
container.capacity = max_capacity
capacity = max_capacity
end
local inventory_count = entity.get_fluid_count(item)
if inventory_count > capacity then
local amount_removed = entity.remove_fluid { name = item, amount = inventory_count - capacity }
container.temperature = combine_tempatures(container.count, container.temperature, amount_removed,
entity.fluidbox[1].temperature)
container.count = container.count + amount_removed
elseif inventory_count < capacity then
local to_add = capacity - inventory_count
if container.count < to_add then
to_add = container.count
end
if to_add ~= 0 then
local amount_added = entity.insert_fluid { name = item, amount = to_add, temperature = container.temperature }
container.count = container.count - amount_added
end
end
end
local function make_master_storage(player, source_container, destination_container)
local master, slave
if source_container.linked_to == destination_container.unit_number then
master = destination_container
slave = source_container
else
player.print('This storage is not linked to the destination storage (0).', Color.warning)
return
end
if not master or not slave then
player.print('This storage is not a valid storage (1).', Color.warning)
return
end
if master.mode ~= 1 or slave.mode ~= 3 then
player.print('This storage is not a valid storage (2).', Color.warning)
return
end
local links = master.links
if not links then
player.print('This storage is not linked to the destination storage (3).', Color.warning)
return
end
local slave_un = slave.unit_number
if not links[slave_un] and not links[tostring(slave_un)] then
player.print('This storage is not linked to the destination storage (4).', Color.warning)
return
end
local old_master_un = master.unit_number
local new_master_un = slave_un
local stored_count = master.count
master.count = 0
local new_links = {}
for unit, _ in pairs(links) do
local u = tonumber(unit) or unit
if u ~= new_master_un then
new_links[u] = true
end
end
new_links[old_master_un] = true
local share_name = master.share and master.share.name
master.links = nil
master.linked_to = new_master_un
master.mode = 3
master.requested_fluid = slave.requested_fluid
if master.share then
master.share.state = false
master.share.name = old_master_un
end
slave.linked_to = nil
slave.mode = 1
slave.requested_fluid = nil
slave.links = new_links
slave.limit = master.limit
slave.count = stored_count
if slave.share then
slave.share.state = true
if share_name ~= nil then
slave.share.name = share_name
end
end
for _, data in pairs(this.main_containers) do
if data and data.linked_to == old_master_un and data.unit_number ~= new_master_un then
data.linked_to = new_master_un
end
end
toggle_render(master)
toggle_render(slave)
end
local function validate_player(player)
if not player then
return false
end
if not player.valid then
return false
end
if not player.character then
return false
end
if not player.connected then
return false
end
return true
end
local function does_exists(unit_number)
local containers = this.main_containers
if containers.index == 1 then
return false
end
if containers[unit_number] then
return true
else
return false
end
end
local function add_object(unit_number, state)
local containers = this.main_containers
if not containers[unit_number] then
containers[unit_number] = state
end
return containers[unit_number]
end
local function remove_object(unit_number)
this.main_containers[unit_number] = nil
end
local function fetch_container(unit_number)
return this.main_containers[unit_number]
end
local function fetch_share(player, text)
local containers = this.main_containers
for unit_number, container in pairs(containers) do
if container.share.name == text and container.owner == player.force.index then
return true, unit_number
end
end
return false
end
toggle_render = function (container)
if not container.entity or not container.entity.valid then
remove_container(container.unit_number)
return
end
if container.render then
container.render.destroy()
end
if container.share.state then
container.render =
rendering.draw_text
{
text = 'M',
surface = container.entity.surface,
target = container.entity,
target_offset = { 0, -0.6 },
scale = 2,
color = { r = 0, g = 0.6, b = 1 },
alignment = 'center'
}
elseif container.linked_to then
container.render =
rendering.draw_text
{
text = 'L',
surface = container.entity.surface,
target = container.entity,
target_offset = { 0, -0.6 },
scale = 2,
color = { r = 0, g = 0.6, b = 1 },
alignment = 'center'
}
else
container.render =
rendering.draw_text
{
text = '',
surface = container.entity.surface,
target = container.entity,
target_offset = { 0, -0.6 },
scale = 2,
color = { r = 0, g = 0.6, b = 1 },
alignment = 'center'
}
end
end
local function create_container(entity, stack, player)
entity.active = false
local unit_number = entity.unit_number
if not does_exists(unit_number) then
local container =
{
entity = entity,
capacity = 0.5 * entity.fluidbox.get_capacity(1),
count = 0,
owner = player.force.index,
unit_number = unit_number,
clear_fluid = { state = false },
limit = { state = true, number = default_limit },
direction =
{
state = 'import'
},
share =
{
state = false,
name = entity.unit_number
},
private = { state = true, owner = player.index },
mode = 1,
total_slots = 48
}
local tags = stack and stack.valid_for_read and stack.type == 'item-with-tags' and stack.tags
if tags and tags.name then
container.count = tags.count
container.temperature = tags.temperature
container.item = tags.name
entity.fluidbox.set_filter(1, { name = tags.name, force = true })
update_single_container(container)
end
local c = add_object(unit_number, container)
toggle_render(c)
return true
end
return false
end
local function get_share(entity, player)
local unit_number = entity.unit_number
local container = fetch_container(unit_number)
if not container.share then
create_container(entity, nil, player)
end
return container.share
end
local function restore_container(entity, player)
if this.saved_containers[player.index] and has_value(this.saved_containers[player.index]) >= 1 then
if not this.enabled then
goto continue
end
local container_index = this.saved_containers[player.index]
local container_to_place = return_value(container_index)
local container =
{
entity = entity,
owner = player.force.index,
capacity = container_to_place.capacity,
item = container_to_place.item,
count = container_to_place.count,
limit = container_to_place.limit,
unit_number = entity.unit_number,
clear_fluid = container_to_place.clear_fluid,
direction = container_to_place.direction,
share = container_to_place.share,
private = container_to_place.private,
mode = 1
}
local c = add_object(entity.unit_number, container)
toggle_render(c)
return true
end
::continue::
return false
end
local function remove_link(unit_number)
local container = fetch_container(unit_number)
if not container then
return
end
local links = container.links
-- container.share.name = default_share_name
container.share.state = false
if links then
for unit, _ in pairs(links) do
unit = tonumber(unit)
if unit then
local l_container = fetch_container(unit)
if l_container then
l_container.linked_to = nil
l_container.mode = 1
links[unit] = nil
toggle_render(l_container)
end
end
end
end
end
remove_container = function (unit_number)
remove_link(unit_number)
remove_object(unit_number)
end
local function item_links(data)
local destination_direction = data.destination_container.direction
local destination_requested_fluid = data.destination_container.requested_fluid
if data.source_container.item == nil and data.destination_container.item ~= nil then
data.source_container.item = data.destination_container.item
end
if destination_direction.state == 'import' then -- import
if destination_requested_fluid then
if data.destination_container.item == destination_requested_fluid then
local source_item = data.source_container.item == destination_requested_fluid
if not source_item and destination_requested_fluid then
data.source_container.item = destination_requested_fluid
data.source_container.count = 0
end
local inventory_count = data.destination_container.entity.get_fluid_count(data.destination_container
.item)
if inventory_count then
local amount_removed = data.destination_container.entity.remove_fluid { name = data.destination_container.item, amount = inventory_count }
data.source_container.count = data.source_container.count + amount_removed
if data.source_container.item == nil then
data.source_container.item = destination_requested_fluid
end
end
end
else
local source_item = data.source_container.item == destination_requested_fluid
if not source_item and destination_requested_fluid then
data.source_container.item = destination_requested_fluid
data.source_container.count = 0
end
if not data.destination_container.entity or not data.destination_container.entity.valid then return end
if data.destination_container.item == nil then
-- detect_item(data.destination_container)
data.destination_container.item = data.source_container.item
end
local inventory_count = data.destination_container.entity.get_fluid_count(data.destination_container.item)
if inventory_count then
local amount_removed = data.destination_container.entity.remove_fluid { name = data.destination_container.item, amount = inventory_count }
data.source_container.count = data.source_container.count + amount_removed
end
end
else
if not prototypes.fluid[data.source_container.item] then
return
end
local item_stack = 10000
if not data.destination_container.item or data.destination_container.item ~= destination_requested_fluid then
data.destination_container.item = destination_requested_fluid
end
if not data.destination_container.temperature or data.destination_container.temperature ~= data.source_container.temperature then
data.destination_container.temperature = data.source_container.temperature
end
if destination_requested_fluid then
if data.source_container.item == destination_requested_fluid then
if data.source_container.count and data.source_container.count > 0 then
local dest_inventory_count = data.destination_container.entity.get_fluid_count(data
.destination_container.item)
if not dest_inventory_count or dest_inventory_count < (item_stack / 2) then
local to_insert = data.source_container.count
if to_insert > item_stack then
to_insert = floor(item_stack / 2)
elseif to_insert > 1 then
to_insert = floor(to_insert / 2)
end
if to_insert < 0 or to_insert == 0 then
return
end
local amount_added = data.destination_container.entity.insert_fluid { name = data.destination_container.item, amount = to_insert, temperature = data.destination_container.temperature }
data.source_container.count = data.source_container.count - amount_added
if data.source_container.count < 0 then
data.source_container.count = 0
data.source_container.item = nil
data.source_container.entity.clear_fluid_inside()
end
else
local dest_diff_remote_storage = data.source_container.count - data.destination_container.count
local dest_diff_local_storage = item_stack - data.destination_container.count
if dest_diff_remote_storage > 0 and dest_diff_local_storage > 0 and dest_diff_local_storage < item_stack then
if data.source_container.count >= dest_diff_local_storage then
local amount_added = data.destination_container.entity.insert_fluid { name = data.destination_container.item, amount = dest_diff_local_storage, temperature = data.destination_container.temperature }
data.source_container.count = data.source_container.count - amount_added
if data.source_container.count < 0 then
data.source_container.count = 0
data.source_container.item = nil
data.source_container.entity.clear_fluid_inside()
end
end
end
end
end
end
else
if data.source_container.count > 0 then
local dest_inventory_count = data.destination_container.entity.get_fluid_count(data
.destination_container.item)
if not dest_inventory_count or dest_inventory_count == 0 then
local to_insert = 50
if to_insert > item_stack then
to_insert = floor(item_stack / 2)
elseif to_insert > 1 then
to_insert = floor(to_insert / 2)
end
if item_stack == 1 then
to_insert = 1
end
if data.destination_container.item == nil then
-- detect_item(data.destination_container)
data.destination_container.item = data.source_container.item
end
local amount_added = data.destination_container.entity.insert_fluid { name = data.destination_container.item, amount = to_insert, temperature = data.destination_container.temperature }
data.source_container.count = data.source_container.count - amount_added
if data.source_container.count < 0 then
data.source_container.count = 0
data.source_container.item = nil
data.source_container.entity.clear_fluid_inside()
end
else
if not data.destination_container.item then
detect_item(data.destination_container)
end
local dest_diff_remote_storage = data.source_container.count - data.destination_container.count
local dest_diff_local_storage = item_stack - data.destination_container.count
if dest_diff_remote_storage > 0 and dest_diff_local_storage > 0 and dest_diff_local_storage < item_stack then
if data.source_container.count >= dest_diff_local_storage then
local amount_added = data.destination_container.entity.insert_fluid { name = data.destination_container.item, amount = dest_diff_local_storage, temperature = data.destination_container.temperature }
data.source_container.count = data.source_container.count - amount_added
if data.source_container.count < 0 then
data.source_container.count = 0
data.source_container.item = nil
data.source_container.entity.clear_fluid_inside()
end
end
end
end
end
end
end
end
local function refund_player(entity)
local unit_number = entity.unit_number
local container = fetch_container(unit_number)
if not container then
return
end
local player = game.get_player(container.private.owner)
if player and player.valid then
player.insert({ name = 'coin', count = this.cost_to_convert })
return
end
end
local function is_container_empty(entity, player)
local unit_number = entity.unit_number
local container = fetch_container(unit_number)
if not container then
return
end
local mode = container.mode
local fluids = container.item
if not fluids then
goto no_storage
end
if mode == 2 then
if container.count >= 1000 then
if not this.saved_containers[player.index] then
this.saved_containers[player.index] = {}
end
container.entity = nil
this.saved_containers[player.index][unit_number] = container
end
end
::no_storage::
remove_container(unit_number)
end
local function on_entity_died(event)
local entity = event.entity
if not entity then
return
end
if not this.valid_storage[entity.type] then
return
end
local unit_number = entity.unit_number
remove_container(unit_number)
end
local function check_limit_on_source_and_content_on_destination(data)
local source_links = data.source_container.links
if source_links then
if data.source_container and data.source_container.count and data.source_container.limit and data.source_container.limit.number and data.destination_container.mode ~= 1 then
if data.source_container.limit.state and data.source_container.count > data.source_container.limit.number and data.destination_container.direction.state == 'import' then
data.destination_container.direction.previous = data.destination_container.direction.state
data.destination_container.direction.state = 'none'
elseif data.source_container.limit.state then
if data.source_container.count < data.source_container.limit.number and data.destination_container.direction.state == 'none' then
data.destination_container.direction.state = data.destination_container.direction.previous
end
elseif data.destination_container.direction.previous then
data.destination_container.direction.state = data.destination_container.direction.previous
data.destination_container.direction.previous = nil
end
else
if not data.source_container.limit then
data.source_container.limit = { state = true, number = default_limit }
end
end
end
end
local function on_pre_player_mined_item(event)
local entity = event.entity
local player = game.get_player(event.player_index)
if not player then
return
end
if not this.valid_storage[entity.type] then
return
end
refund_player(entity)
is_container_empty(entity, player)
local data = this.inf_gui[player.name]
if not data then
return
end
data.frame.destroy()
end
local function get_link(data)
if data.destination_container and data.destination_container.linked_to then
local linked_to = tonumber(data.destination_container.linked_to)
if not linked_to then
return false
end
local source_container = fetch_container(linked_to)
if not source_container then
remove_container(linked_to)
return false
end
data.source_container = source_container
end
end
local function check_links(data)
get_link(data)
if data.source_container then
check_limit_on_source_and_content_on_destination(data)
item_links(data)
end
end
local function check_mode_on_container(data)
local container = data.container.entity
local mode = data.container.mode
if mode == 1 then
container.destructible = false
container.minable = false
elseif mode == 2 then
container.destructible = true
container.minable = true
elseif mode == 3 then
container.destructible = false
container.minable = false
end
end
local function update_container(fluid_prototypes)
local containers = this.main_containers
for unit_number, container in next, containers do
if container and not container.entity.valid then
remove_container(unit_number)
goto continue
end
local inv = container.content
check_mode_on_container({ container = container, count = 0, inv = inv })
check_links({ destination_container = container, inv = inv, fluid_prototypes = fluid_prototypes })
update_single_container(container)
::continue::
end
end
local function text_changed(event)
local element = event.element
if not element then
return
end
if not element.valid then
return
end
local player = game.get_player(event.player_index)
local data = this.inf_gui[player.name]
if not data then
return
end
local name = element.name
local entity = data.entity
if not entity or not entity.valid then
return
end
if string.len(element.text) > 50 then
element.text = ''
end
local unit_number = entity.unit_number
local container = fetch_container(unit_number)
if name and name == 'share_name' and element.text then
if string.len(element.text) > 2 then
if not fetch_share(player, element.text) then
container.share.name = element.text
else
player.print(module_name .. 'A share with name "' .. element.text .. '" already exists.', Color.fail)
end
end
end
local value = tonumber(element.text)
if not value then
this.inf_gui[player.name].updated = false
return
end
if value ~= '' then
if name and name == 'limit_number' then
if value >= 1 then
data.text_field.text = tostring(value)
container.limit.number = value
elseif value <= default_limit then
return
end
end
end
this.inf_gui[player.name].updated = false
end
--- Iterates over player containers.
---@param index any
---@param unit_number any
---@return table
local function get_owner_containers(index, unit_number)
local t = {}
local containers = this.main_containers
for check_unit_number, container in pairs(containers) do
if container.owner == index then
if container.entity and container.entity.valid then
if check_unit_number ~= unit_number and container.share.state then
insert(t, container)
end
end
end
end
return t
end
refresh_main_frame = function (data)
local player = data.player
local unit_number = data.unit_number
local container = fetch_container(unit_number)
local entity = container.entity
if not entity or not entity.valid then
return
end
local player_gui = this.inf_gui[player.name]
local volatile_tbl = player_gui.volatile_tbl
volatile_tbl.clear()
local mode = container.mode
if mode ~= 1 then
if mode ~= 4 then
remove_link(unit_number)
end
end
if mode ~= 3 then
if container and container.linked_to then
container.linked_to = nil
end
end
if mode == 1 then
local limit_tooltip = '[color=yellow]Limit Info:[/color]\nThis will stop the input after the limit is reached.'
local clear_tooltip = '[color=yellow]Info:[/color]\nThis will clear all fluids.'
local share_tooltip =
'[color=yellow]Share Info:[/color]\nA name for the share so you can easy find it when you want to link it with another container.'
local primary_tbl = volatile_tbl.add { type = 'table', column_count = 8, name = 'primary_tbl' }
local limit_one_label = primary_tbl.add({ type = 'label', caption = 'Limit Enabled: ', tooltip = limit_tooltip })
limit_one_label.style.font = 'heading-2'
local limit_one_checkbox = primary_tbl.add(
{
type = 'checkbox',
name = 'limit_storage',
state = container.limit
.state
})
limit_one_checkbox.tooltip = limit_tooltip
limit_one_checkbox.style.minimal_height = 25
limit_one_checkbox.style.minimal_width = 25
local bottom_flow = primary_tbl.add { type = 'flow' }
bottom_flow.style.minimal_width = 40
local limit_two_label = bottom_flow.add({ type = 'label', caption = 'Limit: ', tooltip = limit_tooltip })
limit_two_label.style.font = 'heading-2'
local limit_two_text = bottom_flow.add(
{
type = 'textfield',
name = 'limit_number',
text = container.limit
.number,
})
limit_two_text.style.width = 80
limit_two_text.numeric = true
limit_two_text.tooltip = limit_tooltip
limit_two_text.style.minimal_width = 25
this.inf_gui[player.name].text_field = limit_two_text
this.inf_gui[player.name].limited = limit_one_checkbox
local private_tooltip =
'[color=yellow]Private Info:[/color]\nThis will make it so no one else other than you can open this container.'
local clear_tbl = volatile_tbl.add { type = 'table', column_count = 8, name = 'clear_tbl' }
local clear_one_label = clear_tbl.add({ type = 'label', caption = 'Clear fluids: ', tooltip = clear_tooltip })
clear_one_label.style.font = 'heading-2'
local clear_checkbox = clear_tbl.add(
{
type = 'checkbox',
name = 'clear_checkbox',
state = container
.clear_fluid.state
})
clear_checkbox.tooltip = clear_tooltip
clear_checkbox.style.minimal_height = 25
clear_checkbox.style.minimal_width = 25
if not container.item then
clear_checkbox.enabled = false
clear_checkbox.tooltip = 'No fluids to clear.'
end
local private_label = clear_tbl.add(
{
type = 'label',
caption = 'Private container? ',
tooltip =
private_tooltip
})
private_label.style.font = 'heading-2'
local private_checkbox = clear_tbl.add(
{
type = 'checkbox',
name = 'private_container',
state = container
.private.state
})
private_checkbox.tooltip = private_tooltip
private_checkbox.style.minimal_height = 25
local share_tbl = volatile_tbl.add { type = 'table', column_count = 8, name = 'share_tbl' }
local share_one_label = share_tbl.add({ type = 'label', caption = 'Share Enabled: ', tooltip = share_tooltip })
share_one_label.style.font = 'heading-2'
local share_one_checkbox = share_tbl.add(
{
type = 'checkbox',
name = 'share_container',
state = get_share(entity,
player).state
})
share_one_checkbox.tooltip = share_tooltip
share_one_checkbox.style.minimal_height = 25
share_one_checkbox.style.minimal_width = 25
local share_one_bottom_flow = share_tbl.add { type = 'flow' }
share_one_bottom_flow.style.minimal_width = 40
local share_two_label = share_one_bottom_flow.add(
{
type = 'label',
caption = 'Share Name: ',
tooltip =
share_tooltip
})
share_two_label.style.font = 'heading-2'
local share_two_text = share_one_bottom_flow.add(
{
type = 'textfield',
name = 'share_name',
text = get_share(
entity, player).name
})
share_two_text.style.width = 150
share_two_text.allow_decimal = true
share_two_text.allow_negative = false
share_two_text.tooltip = share_tooltip
share_two_text.style.minimal_width = 25
elseif mode == 3 then
local linker_tooltip =
'[color=yellow]Link Info:[/color]\nThis will only work with containers that you have placed.'
local directions_tooltip =
'[color=yellow]Direction Info:[/color]\nChoose whether to import the item(s) to the main storage or export the item(s) from the main storage.'
local linker = volatile_tbl.add { type = 'table', column_count = 8, name = 'linker' }
if container then
if container and container.owner ~= player.force.index then
local link_label = linker.add(
{
type = 'label',
caption = 'Not owner of container. ',
tooltip =
linker_tooltip
})
link_label.style.font = 'heading-2'
else
local directions_tbl = volatile_tbl.add { type = 'table', column_count = 8, name = 'directions_tbl' }
local state
if container.direction and container.direction.state then
state = container.direction.state
else
state = 'export'
end
local directions_switch =
directions_tbl.add(
{
type = 'switch',
name = 'directions_container',
allow_none_state = false,
switch_state = state == 'import' and 'left' or 'right',
left_label_caption = 'Import',
right_label_caption = 'Export'
}
)
directions_switch.tooltip = directions_tooltip
this.inf_gui[player.name].directions = directions_switch
local sublinker = volatile_tbl.add { type = 'table', column_count = 1, name = 'sublinker' }
local itemdesc = volatile_tbl.add { type = 'table', column_count = 2, name = 'itemdesc' }
local containers = get_owner_containers(container.owner, unit_number)
local linked_container = fetch_container(container.linked_to)
if not next(containers) then
local link_label = sublinker.add({ type = 'label', caption = 'No containers found.' })
link_label.style.font = 'heading-2'
return
end
if container.linked_to and linked_container then
if container.requested_fluid then
local localized_name = prototypes.fluid[container.requested_fluid].localised_name
local link_label = sublinker.add(
{
type = 'label',
caption = 'Linked with: [color=yellow]' ..
linked_container.share.name .. '[/color]',
tooltip = linker_tooltip
})
link_label.style.font = 'heading-2'
link_label = itemdesc.add(
{
type = 'label',
caption = 'Linked item:',
tooltip =
linker_tooltip
})
link_label.style.font = 'heading-2'
link_label = itemdesc.add(
{
type = 'label',
caption = localized_name,
tooltip =
linker_tooltip
})
link_label.style.font_color = Color.green
link_label.style.font = 'heading-2'
else
local link_label = sublinker.add(
{
type = 'label',
caption = 'Linked with: [color=yellow]' ..
linked_container.share.name .. '[/color]',
tooltip = linker_tooltip
})
link_label.style.font = 'heading-2'
end
else
local link_item_label = sublinker.add(
{
type = 'label',
caption = 'Link with specific item:\n',
tooltip =
linker_tooltip
})
link_item_label.style.font = 'heading-2'
local item_scroll_pane =
sublinker.add
{
type = 'scroll-pane',
vertical_scroll_policy = 'auto',
horizontal_scroll_policy = 'never'
}
local item_scroll_style = item_scroll_pane.style
item_scroll_style.maximal_height = 150
item_scroll_style.vertically_squashable = true
item_scroll_style.bottom_padding = 2
item_scroll_style.left_padding = 2
item_scroll_style.right_padding = 2
item_scroll_style.top_padding = 2
local itemlinker = item_scroll_pane.add { type = 'table', column_count = 8, name = 'itemlinker' }
for i = 1, #containers do
if containers then
local container_chest = containers[i]
if type(container_chest) ~= 'string' then
if container_chest.item then
local localized_name = prototypes.fluid[container_chest.item].localised_name
local flowlinker = itemlinker.add { type = 'flow' }
local container_item =
flowlinker.add
{
type = 'sprite-button',
name = item_name_frame_name,
style = 'slot_button',
sprite = 'fluid/' .. container_chest.item,
tooltip = { '', localized_name, '\n', container_chest.share.name, '\n', container_chest.count }
}
Gui.set_data(container_item,
{ name = container_chest.item, unit_number = unit_number, share = container_chest.share.name })
end
end
end
end
-- if directions_switch.switch_state == 'import' then
sublinker.add({ type = 'line' })
local link_container_label = sublinker.add(
{
type = 'label',
caption = 'Link with container:\n',
tooltip =
linker_tooltip
})
link_container_label.style.font = 'heading-2'
local container_scroll_pane =
sublinker.add
{
type = 'scroll-pane',
vertical_scroll_policy = 'auto',
horizontal_scroll_policy = 'never'
}
local container_scroll_style = container_scroll_pane.style
container_scroll_style.maximal_height = 150
container_scroll_style.vertically_squashable = true
container_scroll_style.bottom_padding = 2
container_scroll_style.left_padding = 2
container_scroll_style.right_padding = 2
container_scroll_style.top_padding = 2
local containerlinker = container_scroll_pane.add { type = 'table', column_count = 8, name = 'containerlinker' }
for i = 1, #containers do
if containers then
local container_chest = containers[i]
if type(container_chest) ~= 'string' then
local flowlinker = containerlinker.add { type = 'flow' }
local containeritem = flowlinker.add { type = 'sprite-button', name = item_name_frame_name, style = 'slot_button', sprite = 'item/' .. container_chest.entity.name, tooltip = 'Container share name: ' .. container_chest.share.name }
Gui.set_data(containeritem,
{ name = nil, unit_number = unit_number, share = container_chest.share.name })
end
-- end
end
end
end
end
end
end
end
local function gui_opened(event)
if not event.gui_type == defines.gui_type.entity then
return
end
local entity = event.entity
if not (entity and entity.valid) then
return
end
if not this.valid_storage[entity.type] then
return
end
local unit_number = entity.unit_number
local player = game.get_player(event.player_index)
local container = fetch_container(unit_number)
if not container then
return
end
if container.private.state then
if player.index ~= container.private.owner and not player.admin then
player.opened = nil
return
end
end
local frame = player.gui.center[tostring(unit_number)]
if not frame or not frame.valid then
frame =
player.gui.center.add
{
type = 'frame',
caption = 'Unlimited Storage',
direction = 'vertical',
name = tostring(unit_number)
}
end
local controls = frame.add { type = 'flow', direction = 'horizontal' }
local controls2 = frame.add { type = 'flow', direction = 'horizontal' }
local fluids = frame.add { type = 'flow', direction = 'vertical' }
local mode = container.mode
local selected = mode and mode or 1
local controltbl = controls.add { type = 'table', column_count = 1 }
local btntbl = controltbl.add { type = 'table', column_count = 2 }
local modetbl = controltbl.add { type = 'table', column_count = 2 }
local volatile_tbl = controls2.add { type = 'table', column_count = 1 }
local mode_tooltip =
'[color=yellow]Mode Info:[/color]\nEnabled: will active the container and allow for insertions.\nDisabled: will deactivate the container and let´s the player utilize the GUI to retrieve fluids.\nLink: Link a container with another container. Content is divided between them.'
local btn =
btntbl.add
{
type = 'sprite-button',
tooltip = '[color=blue]Info![/color]\nContainer ID: ' .. unit_number .. '\nThis container stores unlimited quantity of a fluid',
sprite = Gui.info_icon
}
btn.style.height = 20
btn.style.width = 20
btn.enabled = false
btn.focus()
local mode_label = modetbl.add { type = 'label', caption = 'Mode: ', tooltip = mode_tooltip }
mode_label.style.font = 'heading-2'
local drop_down_fluids
if player.admin and (this.editor[player.name]) then
drop_down_fluids = { 'Enabled', 'Disabled', 'Link', 'Editor' }
else
drop_down_fluids = { 'Enabled', 'Disabled', 'Link' }
end
if container.mode == 3 and container.linked_to then
local master_storage_btn =
btntbl.add
{
type = 'sprite-button',
name = master_storage_btn_name,
tooltip = 'Click to make this storage a master storage.',
sprite = 'utility/bookmark'
}
master_storage_btn.style.height = 20
master_storage_btn.style.width = 20
master_storage_btn.enabled = true
end
local drop_down =
modetbl.add
{
type = 'drop-down',
items = drop_down_fluids,
selected_index = selected,
name = unit_number,
tooltip = mode_tooltip
}
this.inf_gui[player.name] =
{
fluid_frame = fluids,
frame = frame,
volatile_tbl = volatile_tbl,
drop_down = drop_down,
entity = entity,
updated = false
}
container.mode = drop_down.selected_index
player.opened = frame
refresh_main_frame({ unit_number = unit_number, player = player })
end
local function on_built_entity(event, raised)
if this.disable_normal_placement and not raised then
return
end
local entity = event.entity
local stack = event.stack
if not entity.valid then
return
end
if not this.valid_storage[entity.type] then
return
end
if event.player_index then
local player = game.get_player(event.player_index)
local c = restore_container(entity, player)
if c then
gui_opened(event)
end
local s = create_container(entity, stack, player)
if s then
gui_opened(event)
end
end
end
local function update_gui()
for _, player in pairs(game.connected_players) do
local container_gui_data = this.inf_gui[player.name]
if not container_gui_data then
goto continue
end
local frame = container_gui_data.fluid_frame
local entity = container_gui_data.entity
if not frame then
goto continue
end
if not entity or not entity.valid then
goto continue
end
local unit_number = entity.unit_number
local container = fetch_container(unit_number)
local mode = container.mode
if (mode == 2 or mode == 4) and this.inf_gui[player.name].updated then
goto continue
end
if not frame or not frame.valid then
goto continue
end
frame.clear()
local tbl = frame.add { type = 'table', column_count = 10, name = 'infinity_storage_inventory' }
if not container.item or not container.count then
if mode == 1 or mode == 2 then
local no_fluid = tbl.add { type = 'sprite-button', style = 'slot_button' }
no_fluid.enabled = false
elseif mode == 4 then
local no_fluid = tbl.add { type = 'choose-elem-button', style = 'slot_button', elem_type = 'fluid' }
no_fluid.enabled = true
end
this.inf_gui[player.name].updated = true
goto continue
end
local btn
if container.requested_fluid then
local localized_name = prototypes.fluid[container.requested_fluid].localised_name
local flow = tbl.add { type = 'flow' }
flow.style.horizontally_stretchable = true
btn =
flow.add
{
type = 'sprite-button',
sprite = 'fluid/' .. container.requested_fluid,
style = 'slot_button',
name = container.requested_fluid,
tooltip = localized_name
}
btn.enabled = false
end
local inventory_count = entity.get_fluid_count(container.item)
local total_fluid_count = inventory_count + container.count
if mode == 1 or mode == 3 then
local localized_name = prototypes.fluid[container.item].localised_name
if container.requested_fluid and tbl[container.requested_fluid] then
tbl[container.requested_fluid].number = total_fluid_count
else
btn =
tbl.add
{
type = 'sprite-button',
sprite = 'fluid/' .. container.item,
style = 'slot_button',
number = total_fluid_count,
name = container.item,
tooltip = localized_name
}
btn.enabled = false
end
elseif mode == 2 or mode == 4 then
local localized_name = prototypes.fluid[container.item].localised_name
btn =
tbl.add
{
type = 'sprite-button',
sprite = 'fluid/' .. container.item,
tooltip = localized_name,
style = 'slot_button',
number = total_fluid_count,
name = container.item
}
btn.enabled = true
end
this.inf_gui[player.name].updated = true
::continue::
end
end
local function gui_closed(event)
local player = game.get_player(event.player_index)
local type = event.gui_type
if type == defines.gui_type.custom then
local data = this.inf_gui[player.name]
if not data then
return
end
data.frame.destroy()
this.inf_gui[player.name] = nil
end
end
local function state_changed(event)
local player = game.get_player(event.player_index)
if not validate_player(player) then
return
end
local element = event.element
if not element.valid then
return
end
if not element.selected_index then
return
end
local name = element.name
if name == 'linker' then
local fluids = element.fluids
if not fluids then
return
end
local selected = fluids[element.selected_index]
if not selected then
return
end
if element.selected_index == 1 then
return
end
local unit_number = this.inf_gui[player.name] and this.inf_gui[player.name].entity and
this.inf_gui[player.name].entity.unit_number
local container = fetch_container(unit_number)
if container then
local _, _unit_number = fetch_share(player, selected)
if _unit_number then
container.linked_to = _unit_number
local linked_container = fetch_container(_unit_number)
if linked_container then
if not linked_container.links then
linked_container.links = {}
end
if not linked_container.links[unit_number] then
linked_container.links[unit_number] = true
end
end
else
container.linked_to = selected
end
this.inf_gui[player.name].updated = false
toggle_render(container)
refresh_main_frame({ unit_number = unit_number, player = player })
return
end
end
local unit_number = tonumber(element.name)
if unit_number then
local container = fetch_container(unit_number)
if not container or not container.mode then
return
end
container.mode = element.selected_index
local mode = container.mode
refresh_main_frame({ unit_number = unit_number, player = player })
toggle_render(container)
if mode >= 2 then
this.inf_gui[player.name].updated = false
return
end
end
end
function Public.remove_player(index)
local containers = this.main_containers
if next(containers) then
for unit_number, container in pairs(containers) do
if container.private.owner == index then
if container.entity and container.entity.valid then
container.entity.destroy()
end
remove_container(unit_number)
end
end
end
this.saved_containers[index] = nil
end
local function gui_click(event)
local element = event.element
local player = game.get_player(event.player_index)
if not validate_player(player) then
return
end
if not element.valid then
return
end
if element.name == 'directions_container' then
local pGui = this.inf_gui[player.name]
if not pGui then
return
end
local entity = pGui.entity
if not (entity and entity.valid) then
return
end
local unit_number = entity.unit_number
local container = fetch_container(unit_number)
container.direction.state = element.switch_state
this.inf_gui[player.name].updated = false
return
end
local parent = element.parent
if not parent then
return
end
if parent.name ~= 'infinity_storage_inventory' then
return
end
local unit_number = tonumber(parent.parent.parent.name)
if tonumber(element.name) == unit_number then
return
end
local shift = event.shift
local ctrl = event.control
local container = fetch_container(unit_number)
local mode = container.mode
if not container.item then
return
end
if player.admin then
if mode == 4 then
if ctrl then
container.count = container.count + 500000
goto update
elseif shift then
container.count = container.count - 500000
if container.count <= 0 then
container.item = nil
container.count = 0
container.entity.clear_fluid_inside()
end
goto update
end
end
end
if mode == 1 then
return
end
::update::
this.inf_gui[player.name].updated = false
end
local function on_gui_elem_changed(event)
local element = event.element
local player = game.get_player(event.player_index)
if not validate_player(player) then
return
end
if not element.valid then
return
end
local parent = element.parent
if not parent then
return
end
if parent.name ~= 'infinity_storage_inventory' then
return
end
local unit_number = tonumber(parent.parent.parent.name)
if tonumber(element.name) == unit_number then
return
end
local container = fetch_container(unit_number)
local button = event.button
local name = element.elem_value
if button == defines.mouse_button_type.right then
container.item = nil
container.count = 0
container.entity.clear_fluid_inside()
if this.inf_gui[player.name] then
this.inf_gui[player.name].updated = false
end
return
end
if not name then
return
end
container.item = name
container.count = 500000
if this.inf_gui[player.name] then
this.inf_gui[player.name].updated = false
end
end
local function on_gui_checked_state_changed(event)
local element = event.element
local player = game.get_player(event.player_index)
if not validate_player(player) then
return
end
if not element.valid then
return
end
local state = element.state and true or false
local pGui = this.inf_gui[player.name]
if not pGui then
return
end
local entity = pGui.entity
if not (entity and entity.valid) then
return
end
local unit_number = entity.unit_number
local container = fetch_container(unit_number)
if not container then
return
end
if element.name == 'private_container' then
container.private.state = state
elseif element.name == 'limit_storage' then
container.limit.state = state
elseif element.name == 'clear_checkbox' then
if container.item then
if not pGui.validate then
player.print(module_name .. 'Are you sure you want to clear all fluids?', Color.warning)
pGui.validate = true
Task.set_timeout_in_ticks(30, delay_refresh_main_frame_task_token,
{ player_index = player.index, unit_number = container.unit_number })
return
end
pGui.validate = nil
player.print(module_name .. 'Cleared all fluids.')
container.clear_fluid.state = false
container.item = nil
container.count = 0
container.entity.clear_fluid_inside()
Task.set_timeout_in_ticks(30, delay_refresh_main_frame_task_token,
{ player_index = player.index, unit_number = container.unit_number })
else
Task.set_timeout_in_ticks(30, delay_refresh_main_frame_task_token,
{ player_index = player.index, unit_number = container.unit_number })
end
return
elseif element.name == 'share_container' then
if container.share.name ~= 'Share name' then
container.direction.state = 'import'
if container.share.state then
remove_link(unit_number)
container.direction.state = 'export'
end
container.share.state = state
toggle_render(container)
else
player.print(module_name .. 'Please provide a valid share name.', Color.warning)
element.state = false
end
end
pGui.updated = false
end
Event.on_nth_tick(
10,
function ()
if not this or not this.enabled then
validate()
return
end
local fluid_prototypes = prototypes.fluid
update_container(fluid_prototypes)
end
)
Event.on_nth_tick(
5,
function ()
if not this or not this.enabled then
validate()
return
end
update_gui()
end
)
Gui.on_click(
convert_to_infinite_container,
function (event)
local player = event.player
local inventory = player.get_main_inventory()
local player_item_count = inventory.get_item_count('coin')
if player_item_count >= this.cost_to_convert then
local entity = Gui.get_data(event.element)
if entity and entity.valid then
player.remove_item({ name = 'coin', count = this.cost_to_convert })
player.opened = nil
event.entity = entity
event.entity = entity
on_built_entity(event, true)
end
else
player.print(module_name .. 'Not enough coins.', Color.warning)
end
end
)
Event.add(
defines.events.on_gui_opened,
function (event)
local player = game.get_player(event.player_index)
if not player or not player.valid then
return
end
local panel = player.gui.relative
local entity = event.entity
if entity and entity.valid and this.valid_storage[entity.type] then
draw_convert_button(panel, entity)
end
gui_opened(event)
end
)
Event.add(
defines.events.on_gui_closed,
function (event)
local player = game.get_player(event.player_index)
if not player or not player.valid then
return
end
gui_closed(event)
local relative = player.gui.relative
local panel = relative[converter_frame_for_player_name]
if panel and panel.valid then
Gui.destroy(panel)
end
end
)
Gui.on_click(
master_storage_btn_name,
function (event)
local player = event.player
local chest_gui_data = this.inf_gui[player.name]
if not chest_gui_data then
return
end
if not chest_gui_data.validation then
player.print('Are you sure you want to make this storage a master storage?', Color.warning)
chest_gui_data.validation = true
return
end
local entity = chest_gui_data.entity
if not entity or not entity.valid then
return
end
local source_container = fetch_container(entity.unit_number)
if not source_container then
player.print('This storage is not a valid storage.', Color.warning)
return
end
if source_container.mode ~= 3 then
player.print('This storage is not a slave storage.', Color.warning)
return
end
local destination_container = fetch_container(source_container.linked_to)
if not destination_container then
player.print('This storage is not a valid storage.', Color.warning)
return
end
if destination_container.mode ~= 1 then
player.print('This storage is not a master storage.', Color.warning)
return
end
make_master_storage(player, source_container, destination_container)
player.print('Successfully made this storage a master storage.', Color.success)
chest_gui_data.validation = nil
if chest_gui_data.frame and chest_gui_data.frame.valid then
chest_gui_data.frame.destroy()
end
end
)
Gui.on_click(
item_name_frame_name,
function (event)
local data = Gui.get_data(event.element)
if not data then
return
end
local _, _unit_number = fetch_share(event.player, data.share)
if _unit_number then
local container = fetch_container(data.unit_number)
container.linked_to = _unit_number
container.requested_fluid = data.name
local linked_container = fetch_container(_unit_number)
if linked_container then
if not linked_container.links then
linked_container.links = {}
end
if not linked_container.links[container.unit_number] then
linked_container.links[container.unit_number] = true
end
end
this.inf_gui[event.player.name].updated = false
toggle_render(container)
refresh_main_frame({ unit_number = container.unit_number, player = event.player })
end
end
)
Event.add(defines.events.on_gui_click, gui_click)
Event.add(defines.events.on_built_entity, on_built_entity)
Event.add(defines.events.on_pre_player_mined_item, on_pre_player_mined_item)
Event.add(defines.events.on_gui_selection_state_changed, state_changed)
Event.add(defines.events.on_entity_died, on_entity_died)
Event.add(defines.events.on_gui_elem_changed, on_gui_elem_changed)
Event.add(defines.events.on_gui_checked_state_changed, on_gui_checked_state_changed)
Event.add(defines.events.on_gui_text_changed, text_changed)
Event.add(
defines.events.on_player_removed,
function (event)
Public.remove_player(event.player_index)
end
)
Event.on_configuration_changed(
function ()
validate()
end
)
Commands.new('increase_fluid', 'Increases fluid capacity')
:require_role('infinity_storage')
:callback(function (player)
if player.selected and player.selected.unit_number then
local linked_container = fetch_container(player.selected.unit_number)
if linked_container and linked_container.count then
linked_container.count = linked_container.count + 5000000
player.print('Fluid capacity increased by 5000000.', Color.success)
else
player.print('Please select a container.', Color.warning)
end
else
player.print('Please select a container.', Color.warning)
end
end
)
Commands.new('toggle_storage_render', 'Changes the render state of the storage')
:require_role('infinity_storage')
:callback(function (player)
for _, container in pairs(this.main_containers) do
toggle_render(container)
end
player.print('[Infinity Storage] Render state changed.', Color.success)
end
)
return Public