174 lines
5.7 KiB
Lua

local RecipeChange = {}
local function add_contents(contents1, contents2)
for name, count in pairs(contents2) do
if contents1[name] then
contents1[name] = contents1[name] + count
else
contents1[name] = count
end
end
end
local function get_inventory_contents(entity, inventory_defines)
local inventory = entity.get_inventory(inventory_defines)
if inventory then
return inventory.get_contents()
else
return {}
end
end
local function get_machine_contents(entity)
local contents = {}
add_contents(contents, get_inventory_contents(entity, defines.inventory.assembling_machine_input))
add_contents(contents, get_inventory_contents(entity, defines.inventory.assembling_machine_output))
add_contents(contents, get_inventory_contents(entity, defines.inventory.assembling_machine_modules))
-- Also add internal items currently being used to craft
if entity.crafting_progress > 0 then
local recipe = entity.get_recipe()
if recipe then
for _, ingredient in pairs(recipe.ingredients) do
if ingredient.type == "item" then
add_contents(contents, {[ingredient.name] = ingredient.amount})
end
end
end
end
return contents
end
local function get_player_contents(entity)
local contents = {}
add_contents(contents, get_inventory_contents(entity, defines.inventory.character_main))
add_contents(contents, get_inventory_contents(entity, defines.inventory.character_guns))
add_contents(contents, get_inventory_contents(entity, defines.inventory.character_ammo))
add_contents(contents, get_inventory_contents(entity, defines.inventory.character_armor))
add_contents(contents, get_inventory_contents(entity, defines.inventory.character_trash))
return contents
end
local function diff_contents(old_contents, new_contents)
local diff = {}
for name, count in pairs(old_contents) do
if new_contents[name] then
if new_contents[name] ~= count and (new_contents[name] - count ~= 0) then
diff[name] = new_contents[name] - count
end
else
diff[name] = -count
end
end
for name, count in pairs(new_contents) do
if not old_contents[name] then
diff[name] = count
end
end
return diff
end
local function ensure_positive(diff)
for name, count in pairs(diff) do
if count <= 0 then
diff[name] = nil
end
end
return diff
end
function RecipeChange.on_remote_gui_opened(player)
local entity = player.opened
if entity.type == "assembling-machine" then
local recipe = entity.get_recipe() or {name = "rc-no-recipe"}
if recipe then
global[player.index] = {
player = player,
recipe = recipe.name,
entity = entity,
player_contents = get_player_contents(player),
entity_contents = get_machine_contents(entity),
}
end
end
end
function RecipeChange.on_gui_closed(player)
global[player.index] = nil
end
local function on_recipe_changed(player_data)
local entity = player_data.entity
local entity_diff = ensure_positive(diff_contents(get_machine_contents(entity), player_data.entity_contents))
if table_size(entity_diff) == 0 then return end
-- Entity contents changed
local player_diff = diff_contents(player_data.player_contents, get_player_contents(player_data.player))
-- Positive count in entity_diff means items were lost
-- Positive count in player_diff means items were gained
-- We need to find the subset of items lost in entity_diff that were gained in player_diff
local surface = entity.surface
local position = entity.position
local player = player_data.player
local force = player.force
for name, count in pairs(entity_diff) do
if player_diff[name] then
local to_spill = math.min(count, player_diff[name])
--game.print(game.tick .. " Trying to spill " .. to_spill .. " " .. name)
local removed = player.remove_item({name = name, count = to_spill}) -- Handles main, ammo, cursor
to_spill = to_spill - removed
if to_spill > 0 then
-- Handle guns, armor, trash
removed = player.get_inventory(defines.inventory.character_trash).remove({name = name, count = to_spill})
to_spill = to_spill - removed
if to_spill > 0 then
removed = player.get_inventory(defines.inventory.character_armor).remove({name = name, count = to_spill})
to_spill = to_spill - removed
if to_spill > 0 then
removed = player.get_inventory(defines.inventory.character_guns).remove({name = name, count = to_spill})
to_spill = to_spill - removed
end
end
end
if removed > 0 then
--game.print(game.tick .. " Spilling " .. removed .. " " .. name)
surface.spill_item_stack(
position,
{name = name, count = removed},
true, -- enabled_looted
force, -- force for deconstruction
false -- allow_on_belts
)
end
end
end
end
local function process_player(player_data)
local entity = player_data.entity
if not entity.valid then return end
local recipe = entity.get_recipe() or {name = "rc-no-recipe"}
if recipe.name ~= player_data.recipe then
on_recipe_changed(player_data)
player_data.recipe = recipe.name
--game.print(game.tick .. " Recipe changed to " .. recipe.name)
end
-- Update stored info
player_data.player_contents = get_player_contents(player_data.player)
player_data.entity_contents = get_machine_contents(player_data.entity)
end
script.on_event(defines.events.on_tick,
function(event)
global.my_data = true
for _, player_data in pairs(global) do
if type(player_data) == "table" and player_data.player then
process_player(player_data)
end
end
end
)
return RecipeChange