1
0
mirror of https://github.com/ComfyFactory/ComfyFactorio.git synced 2024-12-30 23:17:53 +02:00
ComfyFactorio/modules/infinity_chest.lua
2021-03-24 16:46:00 +01:00

763 lines
21 KiB
Lua

local Event = require 'utils.event'
local Global = require 'utils.global'
local this = {
inf_chests = {},
inf_storage = {},
inf_mode = {},
inf_gui = {},
storage = {},
chest = {
['infinity-chest'] = 'infinity-chest'
},
stop = false,
editor = {},
limits = {},
debug = false
}
local default_limit = 1000
local Public = {}
Public.storage = {}
Global.register(
this,
function(tbl)
this = tbl
end
)
function Public.get_table()
return this
end
function Public.create_chest(surface, position, storage)
local entity = surface.create_entity {name = 'infinity-chest', position = position, force = 'player'}
this.inf_chests[entity.unit_number] = {entity = entity, storage = storage}
return entity
end
function Public.err_msg(string)
local debug = this.debug
if not debug then
return
end
log('[Infinity] ' .. string)
end
local function has_value(tab)
local count = 0
for _, k 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
local temp
temp = value
tab[index] = nil
return temp
end
end
return false
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
if not game.players[player.index] then
return false
end
return true
end
local function built_entity(event)
local entity = event.created_entity
if not entity.valid then
return
end
if entity.name ~= this.chest[entity.name] then
return
end
if event.player_index then
local player = game.get_player(event.player_index)
if this.storage[player.index] and has_value(this.storage[player.index].chests) then
if this.stop then
goto continue
end
local chest_index = this.storage[player.index].chests
local limit_index = this.storage[player.index].limits
this.inf_storage[entity.unit_number] = return_value(chest_index)
this.limits[entity.unit_number] = return_value(limit_index)
end
::continue::
entity.active = false
if not this.limits[entity.unit_number] then
this.limits[entity.unit_number] = default_limit
end
this.inf_chests[entity.unit_number] = entity
this.inf_mode[entity.unit_number] = 1
rendering.draw_text {
text = '',
surface = entity.surface,
target = entity,
target_offset = {0, -0.6},
scale = 2,
color = {r = 0, g = 0.6, b = 1},
alignment = 'center'
}
end
end
local function built_entity_robot(event)
local entity = event.created_entity
if not entity.valid then
return
end
if entity.name ~= this.chest[entity.name] then
return
end
entity.destroy()
end
local function item(item_name, item_count, inv, unit_number)
local item_stack = game.item_prototypes[item_name].stack_size
local diff = item_count - item_stack
if not this.inf_storage[unit_number] then
this.inf_storage[unit_number] = {}
end
local storage = this.inf_storage[unit_number]
local mode = this.inf_mode[unit_number]
if mode == 2 then
diff = 2 ^ 31
elseif mode == 4 then
diff = 2 ^ 31
end
if diff > 0 then
if not storage[item_name] then
local count = inv.remove({name = item_name, count = diff})
this.inf_storage[unit_number][item_name] = count
else
if this.inf_storage[unit_number][item_name] >= this.limits[unit_number] then
Public.err_msg('Limit for entity: ' .. unit_number .. 'and item: ' .. item_name .. ' is limited. ')
if mode == 1 then
this.inf_mode[unit_number] = 3
end
if inv.can_insert({name = item_name, count = item_stack}) then
local count = inv.insert({name = item_name, count = item_stack})
this.inf_storage[unit_number][item_name] = storage[item_name] - count
end
return
end
local count = inv.remove({name = item_name, count = diff})
this.inf_storage[unit_number][item_name] = storage[item_name] + count
end
elseif diff < 0 then
if not storage[item_name] then
return
end
if storage[item_name] > (diff * -1) then
local inserted = inv.insert({name = item_name, count = (diff * -1)})
this.inf_storage[unit_number][item_name] = storage[item_name] - inserted
else
inv.insert({name = item_name, count = storage[item_name]})
this.inf_storage[unit_number][item_name] = nil
end
end
end
local function is_chest_empty(entity, player)
local number = entity.unit_number
local inv = this.inf_mode[number]
if inv == 2 then
for k, v in pairs(this.inf_storage) do
if k == number then
if not v then
goto no_storage
end
if (has_value(v) >= 1) then
this.storage[player].chests[number] = this.inf_storage[number]
this.storage[player].limits[number] = this.limits[number]
end
end
end
::no_storage::
this.inf_chests[number] = nil
this.inf_storage[number] = nil
this.limits[number] = nil
this.inf_mode[number] = nil
else
this.inf_chests[number] = nil
this.inf_storage[number] = nil
this.limits[number] = nil
this.inf_mode[number] = nil
end
end
local function on_entity_died(event)
local entity = event.entity
if not entity then
return
end
if entity.name ~= this.chest[entity.name] then
return
end
local number = entity.unit_number
this.inf_mode[number] = nil
this.inf_chests[number] = nil
this.inf_storage[number] = nil
this.limits[number] = nil
end
local function on_pre_player_mined_item(event)
local entity = event.entity
local player = game.players[event.player_index]
if not player then
return
end
if not this.storage[player.index] then
this.storage[player.index] = {
chests = {},
limits = {}
}
end
if entity.name ~= this.chest[entity.name] then
return
end
is_chest_empty(entity, player.index)
local data = this.inf_gui[player.name]
if not data then
return
end
data.frame.destroy()
end
local function update_chest()
for unit_number, chest in pairs(this.inf_chests) do
if not chest.valid then
goto continue
end
local inv = chest.get_inventory(defines.inventory.chest)
local content = inv.get_contents()
local mode = this.inf_mode[chest.unit_number]
if mode then
if mode == 1 then
inv.set_bar()
chest.destructible = false
chest.minable = false
elseif mode == 2 then
inv.set_bar(1)
chest.destructible = true
chest.minable = true
elseif mode == 3 then
inv.set_bar(2)
chest.destructible = false
chest.minable = false
end
end
for item_name, item_count in pairs(content) do
item(item_name, item_count, inv, unit_number)
end
local storage = this.inf_storage[unit_number]
if not storage then
goto continue
end
for item_name, _ in pairs(storage) do
if storage[item_name] <= this.limits[unit_number] and mode == 3 then
this.inf_mode[unit_number] = 1
end
if not content[item_name] then
item(item_name, 0, inv, unit_number)
end
end
::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.players[event.player_index]
local data = this.inf_gui[player.name]
if not data then
return
end
if not data.text_field or not data.text_field.valid then
return
end
if not data.text_field.text then
return
end
local value = tonumber(element.text)
if not value then
return
end
if value ~= '' and value >= default_limit then
data.text_field.text = value
local entity = data.entity
if not entity or not entity.valid then
return
end
local unit_number = entity.unit_number
this.limits[unit_number] = value
elseif value ~= '' and value <= default_limit then
return
end
this.inf_gui[player.name].updated = false
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 then
return
end
if not entity.valid or entity.name ~= this.chest[entity.name] then
return
end
local player = game.players[event.player_index]
local frame =
player.gui.center.add {
type = 'frame',
caption = 'Unlimited Chest',
direction = 'vertical',
name = entity.unit_number
}
local controls = frame.add {type = 'flow', direction = 'horizontal'}
local items = frame.add {type = 'flow', direction = 'vertical'}
local mode = this.inf_mode[entity.unit_number]
local selected = mode and mode or 1
local tbl = controls.add {type = 'table', column_count = 1}
local limit_tooltip = '[color=yellow]Limit Info:[/color]\nThis is only usable if you intend to use this chest for one item.'
local mode_tooltip =
'[color=yellow]Mode Info:[/color]\nEnabled: will active the chest and allow for insertions.\nDisabled: will deactivate the chest and let´s the player utilize the GUI to retrieve items.\nLimited: will deactivate the chest as per limit.'
local btn =
tbl.add {
type = 'sprite-button',
tooltip = '[color=blue]Info![/color]\nThis chest stores unlimited quantity of items (up to 48 different item types).\nThe chest is best used with an inserter to add / remove items.\nThe chest is mineable if state is disabled.\nContent is kept when mined.\n[color=yellow]Limit:[/color]\nThis is only usable if you intend to use this chest for one item.',
sprite = 'utility/questionmark'
}
btn.style.height = 20
btn.style.width = 20
btn.enabled = false
btn.focus()
local tbl_2 = tbl.add {type = 'table', column_count = 4}
tbl_2.add {type = 'label', caption = 'Mode: ', tooltip = mode_tooltip}
local drop_down
if player.admin and this.editor[player.name] then
drop_down =
tbl_2.add {
type = 'drop-down',
items = {'Enabled', 'Disabled', 'Limited', 'Editor'},
selected_index = selected,
name = entity.unit_number,
tooltip = mode_tooltip
}
else
drop_down =
tbl_2.add {
type = 'drop-down',
items = {'Enabled', 'Disabled', 'Limited'},
selected_index = selected,
name = entity.unit_number,
tooltip = mode_tooltip
}
end
tbl_2.add({type = 'label', caption = ' Limit: ', tooltip = limit_tooltip})
local text_field = tbl_2.add({type = 'textfield', text = this.limits[entity.unit_number]})
text_field.style.width = 80
text_field.numeric = true
text_field.tooltip = limit_tooltip
this.inf_mode[entity.unit_number] = drop_down.selected_index
player.opened = frame
this.inf_gui[player.name] = {
item_frame = items,
frame = frame,
text_field = text_field,
entity = entity,
updated = false
}
end
local function update_gui()
for _, player in pairs(game.connected_players) do
local chest_gui_data = this.inf_gui[player.name]
if not chest_gui_data then
goto continue
end
local frame = chest_gui_data.item_frame
local entity = chest_gui_data.entity
if not frame then
goto continue
end
if not entity or not entity.valid then
goto continue
end
local mode = this.inf_mode[entity.unit_number]
if (mode == 2 or mode == 3 or mode == 4) and this.inf_gui[player.name].updated then
goto continue
end
frame.clear()
local tbl = frame.add {type = 'table', column_count = 10, name = 'infinity_chest_inventory'}
local total = 0
local items = {}
local storage = this.inf_storage[entity.unit_number]
local inv = entity.get_inventory(defines.inventory.chest)
local content = inv.get_contents()
local limit = this.limits[entity.unit_number]
local full
if not storage then
goto no_storage
end
for item_name, item_count in pairs(storage) do
total = total + 1
items[item_name] = item_count
if storage[item_name] >= limit then
full = true
end
end
::no_storage::
if full then
goto full
end
for item_name, item_count in pairs(content) do
if not items[item_name] then
total = total + 1
items[item_name] = item_count
else
items[item_name] = items[item_name] + item_count
end
end
::full::
local btn
for item_name, item_count in pairs(items) do
if mode == 1 or mode == 3 then
btn =
tbl.add {
type = 'sprite-button',
sprite = 'item/' .. item_name,
style = 'slot_button',
number = item_count,
name = item_name,
tooltip = 'Withdrawal is possible when state is disabled!'
}
btn.enabled = false
elseif mode == 2 or mode == 4 then
btn =
tbl.add {
type = 'sprite-button',
sprite = 'item/' .. item_name,
style = 'slot_button',
number = item_count,
name = item_name
}
btn.enabled = true
end
end
while total < 48 do
local btns
if mode == 1 or mode == 2 or mode == 3 then
btns = tbl.add {type = 'sprite-button', style = 'slot_button'}
btns.enabled = false
elseif mode == 4 then
btns = tbl.add {type = 'choose-elem-button', style = 'slot_button', elem_type = 'item'}
btns.enabled = true
end
total = total + 1
end
this.inf_gui[player.name].updated = true
::continue::
end
end
local function gui_closed(event)
local player = game.players[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 element = event.element
if not element.valid then
return
end
if not element.selected_index then
return
end
local unit_number = tonumber(element.name)
if not unit_number then
return
end
if not this.inf_mode[unit_number] then
return
end
this.inf_mode[unit_number] = element.selected_index
local mode = this.inf_mode[unit_number]
if mode >= 2 then
local player = game.players[event.player_index]
if not validate_player(player) then
return
end
this.inf_gui[player.name].updated = false
return
end
end
local function gui_click(event)
local element = event.element
local player = game.players[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_chest_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 name = element.name
local storage = this.inf_storage[unit_number]
local mode = this.inf_mode[unit_number]
if not storage then
return
end
if player.admin then
if mode == 4 then
if not storage[name] then
return
end
if ctrl then
storage[name] = storage[name] + 5000000
goto update
elseif shift then
storage[name] = storage[name] - 5000000
if storage[name] <= 0 then
storage[name] = nil
end
goto update
end
end
end
if mode == 1 then
return
end
if ctrl then
local count = storage[name]
if not count then
return
end
local inserted = player.insert {name = name, count = count}
if not inserted then
return
end
if inserted == count then
storage[name] = nil
else
storage[name] = storage[name] - inserted
end
elseif shift then
local count = storage[name]
local stack = game.item_prototypes[name].stack_size
if not count then
return
end
if not stack then
return
end
if count > stack then
local inserted = player.insert {name = name, count = stack}
storage[name] = storage[name] - inserted
else
player.insert {name = name, count = count}
storage[name] = nil
end
else
if not storage[name] then
return
end
storage[name] = storage[name] - 1
player.insert {name = name, count = 1}
if storage[name] <= 0 then
storage[name] = nil
end
end
::update::
for _, p in pairs(game.connected_players) do
if this.inf_gui[p.name] then
this.inf_gui[p.name].updated = false
end
end
end
local function on_gui_elem_changed(event)
local element = event.element
local player = game.players[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_chest_inventory' then
return
end
local unit_number = tonumber(parent.parent.parent.name)
if tonumber(element.name) == unit_number then
return
end
local button = event.button
local storage = this.inf_storage[unit_number]
if not storage then
this.inf_storage[unit_number] = {}
storage = this.inf_storage[unit_number]
end
local name = element.elem_value
if button == defines.mouse_button_type.right then
storage[name] = nil
return
end
if not name then
return
end
storage[name] = 5000000
if this.inf_gui[player.name] then
this.inf_gui[player.name].updated = false
end
end
local function on_entity_settings_pasted(event)
local player = game.get_player(event.player_index)
if not player or not player.valid then
return
end
local source = event.source
if not source or not source.valid then
return
end
local destination = event.destination
if not destination or not destination.valid then
return
end
local source_number = source.unit_number
local destination_number = destination.unit_number
local source_limit = this.limits[source_number]
this.limits[destination_number] = source_limit
end
local function tick()
update_chest()
update_gui()
end
Event.add(defines.events.on_tick, tick)
Event.add(defines.events.on_gui_click, gui_click)
Event.add(defines.events.on_gui_opened, gui_opened)
Event.add(defines.events.on_gui_closed, gui_closed)
Event.add(defines.events.on_built_entity, built_entity)
Event.add(defines.events.on_robot_built_entity, built_entity_robot)
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_text_changed, text_changed)
Event.add(defines.events.on_entity_settings_pasted, on_entity_settings_pasted)
return Public