2021-01-07 17:48:24 +02:00
--this adds a button that stashes/sorts your inventory into nearby chests in some kind of intelligent way - mewmew
-- modified by gerkiz
local Global = require ' utils.global '
local Event = require ' utils.event '
local math_floor = math.floor
local print_color = { r = 120 , g = 255 , b = 0 }
local autostash = {
floating_text_y_offsets = { } ,
whitelist = { } ,
insert_into_wagon = false
}
local Public = { }
Global.register (
autostash ,
function ( t )
autostash = t
end
)
local function create_floaty_text ( surface , position , name , count )
if autostash.floating_text_y_offsets [ position.x .. ' _ ' .. position.y ] then
2021-03-24 17:46:00 +02:00
autostash.floating_text_y_offsets [ position.x .. ' _ ' .. position.y ] = autostash.floating_text_y_offsets [ position.x .. ' _ ' .. position.y ] - 0.5
2021-01-07 17:48:24 +02:00
else
autostash.floating_text_y_offsets [ position.x .. ' _ ' .. position.y ] = 0
end
surface.create_entity (
{
name = ' flying-text ' ,
position = {
position.x ,
position.y + autostash.floating_text_y_offsets [ position.x .. ' _ ' .. position.y ]
} ,
text = { ' ' , ' - ' , count , ' ' , game.item_prototypes [ name ] . localised_name } ,
color = { r = 255 , g = 255 , b = 255 }
}
)
end
local function chest_is_valid ( chest )
if chest.type == ' cargo-wagon ' then
local t = { }
local chest_inventory = chest.get_inventory ( defines.inventory . cargo_wagon )
for index = 1 , 40 do
if chest_inventory.get_filter ( index ) ~= nil then
local n = chest_inventory.get_filter ( index )
t [ n ] = true
end
end
if not next ( t ) then
return false , { }
end
return true , t
end
for _ , e in pairs (
chest.surface . find_entities_filtered (
{
type = { ' inserter ' , ' loader ' } ,
area = { { chest.position . x - 1 , chest.position . y - 1 } , { chest.position . x + 1 , chest.position . y + 1 } }
}
)
) do
if e.name ~= ' long-handed-inserter ' then
if e.position . x == chest.position . x then
if e.direction == 0 or e.direction == 4 then
return false
end
end
if e.position . y == chest.position . y then
if e.direction == 2 or e.direction == 6 then
return false
end
end
end
end
local i1 = chest.surface . find_entity ( ' long-handed-inserter ' , { chest.position . x - 2 , chest.position . y } )
if i1 then
if i1.direction == 2 or i1.direction == 6 then
return false
end
end
local i2 = chest.surface . find_entity ( ' long-handed-inserter ' , { chest.position . x + 2 , chest.position . y } )
if i2 then
if i2.direction == 2 or i2.direction == 6 then
return false
end
end
local i3 = chest.surface . find_entity ( ' long-handed-inserter ' , { chest.position . x , chest.position . y - 2 } )
if i3 then
if i3.direction == 0 or i3.direction == 4 then
return false
end
end
local i4 = chest.surface . find_entity ( ' long-handed-inserter ' , { chest.position . x , chest.position . y + 2 } )
if i4 then
if i4.direction == 0 or i4.direction == 4 then
return false
end
end
return true
end
local function sort_entities_by_distance ( position , entities )
local t = { }
local distance
local index
local size_of_entities = # entities
if size_of_entities < 2 then
return
end
for _ , entity in pairs ( entities ) do
distance = ( entity.position . x - position.x ) ^ 2 + ( entity.position . y - position.y ) ^ 2
index = math_floor ( distance ) + 1
if not t [ index ] then
t [ index ] = { }
end
table.insert ( t [ index ] , entity )
end
local i = 0
for _ , range in pairs ( t ) do
for _ , entity in pairs ( range ) do
i = i + 1
entities [ i ] = entity
end
end
end
local function get_nearby_chests ( player , a )
local r = player.force . character_reach_distance_bonus + 10
local r_square = r * r
local chests = { }
local size_of_chests = 0
local area = { { player.position . x - r , player.position . y - r } , { player.position . x + r , player.position . y + r } }
area = a or area
local container_type = { ' container ' , ' logistic-container ' , ' furnace ' }
local containers = { }
local i = 0
if autostash.insert_into_wagon then
table.insert ( container_type , ' cargo-wagon ' )
end
for _ , e in pairs ( player.surface . find_entities_filtered ( { type = container_type , area = area , force = ' player ' } ) ) do
if ( ( player.position . x - e.position . x ) ^ 2 + ( player.position . y - e.position . y ) ^ 2 ) <= r_square then
i = i + 1
containers [ i ] = e
end
end
sort_entities_by_distance ( player.position , containers )
for _ , entity in pairs ( containers ) do
size_of_chests = size_of_chests + 1
chests [ size_of_chests ] = entity
end
return chests
end
local function does_inventory_contain_item_type ( inventory , item_subgroup )
for name , _ in pairs ( inventory.get_contents ( ) ) do
local t = game.item_prototypes [ name ]
if t and t.subgroup . name == item_subgroup then
return true
end
end
return false
end
local function insert_item_into_chest ( player_inventory , chests , filtered_chests , name , count , wagon )
local container = {
[ ' container ' ] = true ,
[ ' logistic-container ' ] = true
}
local to_insert = math.floor ( count / # chests )
local variator = count % # chests
if wagon then
-- Attempt to load filtered cargo wagon
for _ , chest in pairs ( chests ) do
if chest.type == ' cargo-wagon ' then
local chest_inventory = chest.get_inventory ( defines.inventory . cargo_wagon )
if chest_inventory.can_insert ( { name = name , count = count } ) then
local inserted_count = chest_inventory.insert ( { name = name , count = count } )
player_inventory.remove ( { name = name , count = inserted_count } )
create_floaty_text ( chest.surface , chest.position , name , inserted_count )
count = count - inserted_count
if count <= 0 then
return
end
end
end
end
end
--Attempt to store into furnaces.
for _ , chest in pairs ( chests ) do
local chest_inventory = chest.get_inventory ( defines.inventory . furnace_source )
if chest_inventory and chest.type == ' furnace ' then
if chest_inventory.can_insert ( { name = name , count = count } ) then
local inserted_count = chest_inventory.insert ( { name = name , count = count } )
player_inventory.remove ( { name = name , count = inserted_count } )
create_floaty_text ( chest.surface , chest.position , name , inserted_count )
count = count - inserted_count
if count <= 0 then
return
end
end
end
end
for _ , chest in pairs ( chests ) do
if chest.type == ' furnace ' then
local amount = to_insert
if variator > 0 then
amount = amount + 1
variator = variator - 1
end
if amount <= 0 then
return
end
local chest_inventory = chest.get_inventory ( defines.inventory . chest )
if chest_inventory.can_insert ( { name = name , count = amount } ) then
local inserted_count = chest_inventory.insert ( { name = name , count = amount } )
player_inventory.remove ( { name = name , count = inserted_count } )
create_floaty_text ( chest.surface , chest.position , name , inserted_count )
end
end
end
--Attempt to store in chests that already have the same item.
for _ , chest in pairs ( chests ) do
if container [ chest.type ] then
local chest_inventory = chest.get_inventory ( defines.inventory . chest )
if chest_inventory.can_insert ( { name = name , count = count } ) then
if chest_inventory.find_item_stack ( name ) then
local inserted_count = chest_inventory.insert ( { name = name , count = count } )
player_inventory.remove ( { name = name , count = inserted_count } )
create_floaty_text ( chest.surface , chest.position , name , inserted_count )
count = count - inserted_count
if count <= 0 then
return
end
end
end
end
end
--Attempt to store in empty chests.
for _ , chest in pairs ( filtered_chests ) do
if container [ chest.type ] then
local chest_inventory = chest.get_inventory ( defines.inventory . chest )
if chest_inventory.can_insert ( { name = name , count = count } ) then
if chest_inventory.is_empty ( ) then
local inserted_count = chest_inventory.insert ( { name = name , count = count } )
player_inventory.remove ( { name = name , count = inserted_count } )
create_floaty_text ( chest.surface , chest.position , name , inserted_count )
count = count - inserted_count
if count <= 0 then
return
end
end
end
end
end
--Attempt to store in chests with same item subgroup.
local item_subgroup = game.item_prototypes [ name ] . subgroup.name
if item_subgroup then
for _ , chest in pairs ( filtered_chests ) do
if container [ chest.type ] then
local chest_inventory = chest.get_inventory ( defines.inventory . chest )
if chest_inventory.can_insert ( { name = name , count = count } ) then
if does_inventory_contain_item_type ( chest_inventory , item_subgroup ) then
local inserted_count = chest_inventory.insert ( { name = name , count = count } )
player_inventory.remove ( { name = name , count = inserted_count } )
create_floaty_text ( chest.surface , chest.position , name , inserted_count )
count = count - inserted_count
if count <= 0 then
return
end
end
end
end
end
end
--Attempt to store in mixed chests.
for _ , chest in pairs ( filtered_chests ) do
if container [ chest.type ] then
local chest_inventory = chest.get_inventory ( defines.inventory . chest )
if chest_inventory.can_insert ( { name = name , count = count } ) then
local inserted_count = chest_inventory.insert ( { name = name , count = count } )
player_inventory.remove ( { name = name , count = inserted_count } )
create_floaty_text ( chest.surface , chest.position , name , inserted_count )
count = count - inserted_count
if count <= 0 then
return
end
end
end
end
end
local function auto_stash ( player , event )
local button = event.button
local shift = event.shift
if not player.character then
player.print ( ' It seems that you are not in the realm of the living. ' , print_color )
return
end
if not player.character . valid then
player.print ( ' It seems that you are not in the realm of the living. ' , print_color )
return
end
local inventory = player.get_inventory ( defines.inventory . character_main )
if inventory.is_empty ( ) then
player.print ( ' Inventory is empty. ' , print_color )
return
end
local chests
local r = 1
local area = { { player.position . x - r , player.position . y - r } , { player.position . x + r , player.position . y + r } }
if shift then
2021-03-24 17:46:00 +02:00
if button == defines.mouse_button_type . right or button == defines.mouse_button_type . left and autostash.insert_into_wagon then
2021-01-07 17:48:24 +02:00
chests = get_nearby_chests ( player , area )
end
else
chests = get_nearby_chests ( player )
end
if not chests [ 1 ] then
player.print ( ' No valid nearby containers found. ' , print_color )
return
end
local filtered_chests = { }
local filtered_allowed
for _ , e in pairs ( chests ) do
local is_valid , t = chest_is_valid ( e )
filtered_allowed = t
if is_valid then
filtered_chests [ # filtered_chests + 1 ] = e
end
end
autostash.floating_text_y_offsets = { }
local hotbar_items = { }
for i = 1 , 100 , 1 do
local prototype = player.get_quick_bar_slot ( i )
if prototype then
hotbar_items [ prototype.name ] = true
end
end
for name , count in pairs ( inventory.get_contents ( ) ) do
local is_resource = autostash.whitelist [ name ]
if not inventory.find_item_stack ( name ) . grid and not hotbar_items [ name ] then
if shift and autostash.insert_into_wagon then
if button == defines.mouse_button_type . left then
if is_resource then
insert_item_into_chest ( inventory , chests , filtered_chests , name , count , true )
end
end
if button == defines.mouse_button_type . right then
if filtered_allowed and is_resource and filtered_allowed [ name ] then
insert_item_into_chest ( inventory , chests , filtered_chests , name , count , true )
end
end
elseif button == defines.mouse_button_type . right then
if is_resource then
insert_item_into_chest ( inventory , chests , filtered_chests , name , count )
end
elseif button == defines.mouse_button_type . left then
insert_item_into_chest ( inventory , chests , filtered_chests , name , count )
end
end
end
local c = autostash.floating_text_y_offsets
for k , _ in pairs ( c ) do
autostash.floating_text_y_offsets [ k ] = nil
end
end
local function create_gui_button ( player )
if player.gui . top.auto_stash then
return
end
local tooltip
if autostash.insert_into_wagon then
tooltip =
' Sort your inventory into nearby chests. \n LMB: Everything, excluding quickbar items. \n RMB: Only ores. \n SHIFT+LMB: Only ores to wagon \n SHIFT+RMB: Only ores onto filtered slots to wagon. '
else
tooltip = ' Sort your inventory into nearby chests. \n LMB: Everything, excluding quickbar items. \n RMB: Only ores. '
end
local b =
player.gui . top.add (
{
type = ' sprite-button ' ,
sprite = ' item/wooden-chest ' ,
name = ' auto_stash ' ,
tooltip = tooltip
}
)
b.style . font_color = { r = 0.11 , g = 0.8 , b = 0.44 }
b.style . font = ' heading-1 '
b.style . minimal_height = 38
b.style . minimal_width = 38
b.style . maximal_height = 38
b.style . maximal_width = 38
b.style . padding = 1
b.style . margin = 0
end
local function do_whitelist ( )
local resources = game.entity_prototypes
autostash.whitelist = { }
for k , _ in pairs ( resources ) do
if resources [ k ] and resources [ k ] . type == ' resource ' and resources [ k ] . mineable_properties then
if resources [ k ] . mineable_properties.products [ 1 ] then
local r = resources [ k ] . mineable_properties.products [ 1 ] . name
autostash.whitelist [ r ] = true
elseif resources [ k ] . mineable_properties.products [ 2 ] then
local r = resources [ k ] . mineable_properties.products [ 2 ] . name
autostash.whitelist [ r ] = true
end
end
end
end
local function on_player_joined_game ( event )
create_gui_button ( game.players [ event.player_index ] )
end
local function on_gui_click ( event )
if not event.element then
return
end
if not event.element . valid then
return
end
if event.element . name == ' auto_stash ' then
auto_stash ( game.players [ event.player_index ] , event )
end
end
function Public . insert_into_wagon ( value )
if value then
autostash.insert_into_wagon = value or false
end
end
Event.on_configuration_changed = function ( )
do_whitelist ( )
log ( ' [Autostash] on_configuration_changed was called, rebuilding resource whitelist. ' )
end
Event.on_init ( do_whitelist )
Event.add ( defines.events . on_player_joined_game , on_player_joined_game )
Event.add ( defines.events . on_gui_click , on_gui_click )
return Public