2021-05-04 19:51:11 +02:00
local Public = { }
local Event = require ' utils.event '
local Token = require ' utils.token '
local Gui = require ' utils.gui '
local Task = require ' utils.task '
local Global = require ' utils.global '
local format = string.format
local chests = { }
local chests_next = { }
Global.register (
2024-09-25 15:38:14 +01:00
{ chests = chests , chests_next = chests_next } ,
function ( tbl )
2021-05-04 19:51:11 +02:00
chests = tbl.chests
chests_next = tbl.chests_next
end
)
local chest_gui_frame_name = Gui.uid_name ( )
local chest_content_table_name = Gui.uid_name ( )
function Public . create_chest ( surface , position , storage )
2024-09-25 15:38:14 +01:00
local entity = surface.create_entity { name = ' infinity-chest ' , position = position , force = ' neutral ' }
chests [ entity.unit_number ] = { entity = entity , storage = storage }
2021-05-04 19:51:11 +02:00
return entity
end
local function built_entity ( event )
local entity = event.created_entity
if not entity or not entity.valid or entity.name ~= ' infinity-chest ' then
return
end
entity.active = false
2024-09-25 15:38:14 +01:00
chests [ entity.unit_number ] = { entity = entity , storage = { } }
2021-05-04 19:51:11 +02:00
end
local function get_stack_size ( name )
2024-09-25 15:38:14 +01:00
local proto = prototypes.item [ name ]
2021-05-04 19:51:11 +02:00
if not proto then
log ( ' item prototype ' .. name .. ' not found ' )
return 1
end
return proto.stack_size
end
local function do_item ( name , count , inv , storage )
local size = get_stack_size ( name )
local diff = count - size
if diff == 0 then
return
end
local new_amount = 0
if diff > 0 then
2024-09-25 15:38:14 +01:00
inv.remove ( { name = name , count = diff } )
2021-05-04 19:51:11 +02:00
local prev = storage [ name ] or 0
new_amount = prev + diff
elseif diff < 0 then
local prev = storage [ name ]
if not prev then
return
end
diff = math.min ( prev , - diff )
2024-09-25 15:38:14 +01:00
local inserted = inv.insert ( { name = name , count = diff } )
2021-05-04 19:51:11 +02:00
new_amount = prev - inserted
end
if new_amount == 0 then
storage [ name ] = nil
else
storage [ name ] = new_amount
end
end
local function tick ( )
local chest_id , chest_data = next ( chests , chests_next [ 1 ] )
chests_next [ 1 ] = chest_id
if not chest_id then
return
end
local entity = chest_data.entity
if not entity or not entity.valid then
chests [ chest_id ] = nil
else
local storage = chest_data.storage
local inv = entity.get_inventory ( 1 ) --defines.inventory.chest
local contents = inv.get_contents ( )
for name , count in pairs ( contents ) do
do_item ( name , count , inv , storage )
end
for name , _ in pairs ( storage ) do
if not contents [ name ] then
do_item ( name , 0 , inv , storage )
end
end
end
end
local function create_chest_gui_content ( frame , player , chest )
local storage = chest.storage
local inv = chest.entity . get_inventory ( 1 ) . get_contents ( )
local grid = frame [ chest_content_table_name ]
if grid then
grid.clear ( )
else
2024-09-25 15:38:14 +01:00
grid = frame.add { type = ' table ' , name = chest_content_table_name , column_count = 10 , style = ' slot_table ' }
2021-05-04 19:51:11 +02:00
end
for name , count in pairs ( storage ) do
local number = count + ( inv [ name ] or 0 )
grid.add {
type = ' sprite-button ' ,
sprite = ' item/ ' .. name ,
number = number ,
tooltip = name ,
--style = 'slot_button'
enabled = false
}
end
for name , count in pairs ( inv ) do
if not storage [ name ] then
grid.add {
type = ' sprite-button ' ,
sprite = ' item/ ' .. name ,
number = count ,
tooltip = name ,
--style = 'slot_button'
enabled = false
}
end
end
player.opened = frame
end
local chest_gui_content_callback
chest_gui_content_callback =
Token.register (
2024-09-25 15:38:14 +01:00
function ( data )
local player = data.player
2021-05-04 19:51:11 +02:00
2024-09-25 15:38:14 +01:00
if not player or not player.valid then
return
end
2021-05-04 19:51:11 +02:00
2024-09-25 15:38:14 +01:00
local opened = data.opened
if not opened or not opened.valid then
return
end
2021-05-04 19:51:11 +02:00
2024-09-25 15:38:14 +01:00
local entity = data.chest . entity
if not entity.valid then
player.opened = nil
opened.destroy ( )
return
end
2021-05-04 19:51:11 +02:00
2024-09-25 15:38:14 +01:00
if not player.connected then
player.opened = nil
opened.destroy ( )
return
end
2021-05-04 19:51:11 +02:00
2024-09-25 15:38:14 +01:00
create_chest_gui_content ( opened , player , data.chest )
2021-05-04 19:51:11 +02:00
2024-09-25 15:38:14 +01:00
Task.set_timeout_in_ticks ( 60 , chest_gui_content_callback , data )
end
)
2021-05-04 19:51:11 +02:00
local function gui_opened ( event )
if not event.gui_type == defines.gui_type . entity then
return
end
local entity = event.entity
if not entity or not entity.valid or entity.name ~= ' infinity-chest ' then
return
end
local chest = chests [ entity.unit_number ]
if not chest then
return
end
local player = game.get_player ( event.player_index )
if not player or not player.valid then
return
end
local frame =
player.gui . center.add {
2024-09-25 15:38:14 +01:00
type = ' frame ' ,
name = chest_gui_frame_name ,
caption = ' Infinite Storage Chest ' ,
direction = ' vertical '
}
2021-05-04 19:51:11 +02:00
local text =
frame.add {
2024-09-25 15:38:14 +01:00
type = ' label ' ,
caption = format (
' This chest stores unlimited quantity of items (up to 48 different item types). \n The chest is best used with an inserter to add / remove items. \n If the chest is mined or destroyed the items are lost. '
)
}
2021-05-04 19:51:11 +02:00
text.style . single_line = false
2024-09-25 15:38:14 +01:00
local content_header = frame.add { type = ' label ' , caption = ' Content ' }
2021-05-04 19:51:11 +02:00
content_header.style . font = ' default-listbox '
create_chest_gui_content ( frame , player , chest )
2024-09-25 15:38:14 +01:00
Task.set_timeout_in_ticks ( 60 , chest_gui_content_callback , { player = player , chest = chest , opened = frame } )
2021-05-04 19:51:11 +02:00
end
Event.add ( defines.events . on_built_entity , built_entity )
Event.add ( defines.events . on_robot_built_entity , built_entity )
Event.add ( defines.events . on_tick , tick )
Event.add ( defines.events . on_gui_opened , gui_opened )
Event.add (
defines.events . on_player_died ,
2024-09-25 15:38:14 +01:00
function ( event )
2021-05-04 19:51:11 +02:00
local player = game.get_player ( event.player_index or 0 )
if not player or not player.valid then
return
end
local element = player.gui . center
if element and element.valid then
element = element [ chest_gui_frame_name ]
if element and element.valid then
element.destroy ( )
end
end
end
)
Gui.on_custom_close (
chest_gui_frame_name ,
2024-09-25 15:38:14 +01:00
function ( event )
2021-05-04 19:51:11 +02:00
event.element . destroy ( )
end
)
return Public