285 lines
8.6 KiB
Lua

require 'gui'
local shared = require 'shared'
local update_rate = shared.update_rate
local update_slots = shared.update_slots
local compactify = shared.compactify
local validity_check = shared.validity_check
local function setup()
global.units = global.units or {}
if remote.interfaces['PickerDollies'] then
remote.call('PickerDollies', 'add_blacklist_name', 'memory-unit', true)
remote.call('PickerDollies', 'add_blacklist_name', 'memory-unit-combinator', true)
end
end
script.on_init(setup)
script.on_configuration_changed(function()
setup()
for unit_number, unit_data in pairs(global.units) do
if unit_data.item and not validity_check(unit_number, unit_data) then
local prototype = game.item_prototypes[unit_data.item]
if prototype then
unit_data.stack_size = prototype.stack_size
unit_data.comfortable = unit_data.stack_size * #unit_data.inventory / 2
else
shared.memory_unit_corruption(unit_number, unit_data)
end
end
end
end)
local function update_unit_exterior(unit_data, inventory_count)
local entity = unit_data.entity
unit_data.previous_inventory_count = inventory_count
local total_count = unit_data.count + inventory_count
shared.update_combinator(unit_data.combinator, {type = 'item', name = unit_data.item}, total_count)
shared.update_display_text(unit_data, entity, compactify(total_count))
shared.update_power_usage(unit_data, total_count)
end
function set_filter(unit_data)
local inventory = unit_data.inventory
local item = unit_data.item
local entity = unit_data.entity
for i = 1, #inventory do
local stack = inventory[i]
if not inventory.set_filter(i, item) or (stack.valid_for_read and stack.name ~= item) then
entity.surface.spill_item_stack(entity.position, stack)
stack.clear()
inventory.set_filter(i, item)
end
end
end
local function detect_item(unit_data)
local inventory = unit_data.inventory
for name, count in pairs(inventory.get_contents()) do
if shared.check_for_basic_item(name) then
unit_data.item = name
unit_data.stack_size = game.item_prototypes[name].stack_size
unit_data.comfortable = unit_data.stack_size * #inventory / 2
set_filter(unit_data)
return true
end
end
return false
end
function update_unit(unit_data, unit_number, force)
local entity = unit_data.entity
local powersource = unit_data.powersource
local combinator = unit_data.combinator
local container = unit_data.container
local inventory = unit_data.inventory
if validity_check(unit_number, unit_data, force) then return end
local changed = false
if unit_data.item == nil then changed = detect_item(unit_data) end
local item = unit_data.item
if item == nil then return end
local comfortable = unit_data.comfortable
local inventory_count = inventory.get_item_count(item)
if inventory_count > comfortable then
local amount_removed = inventory.remove{name = item, count = inventory_count - comfortable}
unit_data.count = unit_data.count + amount_removed
inventory_count = inventory_count - amount_removed
changed = true
elseif inventory_count < comfortable then
if unit_data.previous_inventory_count ~= inventory_count then
changed = true
end
local to_add = comfortable - inventory_count
if unit_data.count < to_add then
to_add = unit_data.count
end
if to_add ~= 0 then
local amount_added = entity.insert{name = item, count = to_add}
unit_data.count = unit_data.count - amount_added
inventory_count = inventory_count + amount_added
end
end
if force or changed then
inventory.sort_and_merge()
update_unit_exterior(unit_data, inventory_count)
end
end
script.on_nth_tick(update_rate, function(event)
local smooth_ups = event.tick % update_slots
for unit_number, unit_data in pairs(global.units) do
if unit_data.lag_id == smooth_ups then
update_unit(unit_data, unit_number)
end
end
end)
local combinator_shift_x = 2.25
local combinator_shift_y = 1.75
local function on_created(event)
local entity = event.created_entity or event.entity
if entity.name ~= 'memory-unit' then return end
local position = entity.position
local surface = entity.surface
local force = entity.force
local combinator = surface.create_entity{
name = 'memory-unit-combinator',
position = {position.x + combinator_shift_x, position.y + combinator_shift_y},
force = force
}
combinator.operable = false
combinator.destructible = false
local powersource = surface.create_entity{
name = 'memory-unit-powersource',
position = position,
force = force
}
powersource.destructible = false
local unit_data = {
entity = entity,
count = 0,
powersource = powersource,
combinator = combinator,
inventory = entity.get_inventory(defines.inventory.chest),
lag_id = math.random(0, update_slots - 1)
}
global.units[entity.unit_number] = unit_data
local stack = event.stack
local tags = stack and stack.valid_for_read and stack.type == 'item-with-tags' and stack.tags
if tags and tags.name then
unit_data.count = tags.count
unit_data.item = tags.name
unit_data.stack_size = game.item_prototypes[tags.name].stack_size
unit_data.comfortable = unit_data.stack_size * #unit_data.inventory / 2
set_filter(unit_data)
update_unit(unit_data, entity.unit_number, true)
else
shared.update_power_usage(unit_data, 0)
end
end
script.on_event(defines.events.on_built_entity, on_created)
script.on_event(defines.events.on_robot_built_entity, on_created)
script.on_event(defines.events.script_raised_built, on_created)
script.on_event(defines.events.script_raised_revive, on_created)
script.on_event(defines.events.on_entity_cloned, function(event)
local entity = event.source
if entity.name ~= 'memory-unit' then return end
local destination = event.destination
local unit_data = global.units[entity.unit_number]
local position = destination.position
local surface = destination.surface
local powersource, combinator = unit_data.powersource, unit_data.combinator
if powersource.valid then
powersource = powersource.clone{position = position, surface = surface}
else
powersource = surface.create_entity{
name = 'memory-unit-powersource',
position = position,
force = force
}
powersource.destructible = false
end
if combinator.valid then
combinator = combinator.clone{position = {position.x + combinator_shift_x, position.y + combinator_shift_y}, surface = surface}
else
combinator = surface.create_entity{
name = 'memory-unit-combinator',
position = {position.x + combinator_shift_x, position.y + combinator_shift_y},
force = force
}
combinator.destructible = false
combinator.operable = false
end
local item = unit_data.item
unit_data = {
powersource = powersource,
combinator = combinator,
item = item,
count = unit_data.count,
entity = destination,
comfortable = unit_data.comfortable,
stack_size = unit_data.stack_size,
inventory = destination.get_inventory(defines.inventory.chest),
lag_id = math.random(0, update_slots - 1)
}
global.units[destination.unit_number] = unit_data
if item then
set_filter(unit_data)
update_unit(global.units[destination.unit_number], destination.unit_number, true)
end
end)
local function on_destroyed(event)
local entity = event.entity
if entity.name ~= 'memory-unit' then return end
local unit_data = global.units[entity.unit_number]
global.units[entity.unit_number] = nil
unit_data.powersource.destroy()
unit_data.combinator.destroy()
local item = unit_data.item
local count = unit_data.count
local buffer = event.buffer
if buffer and item and count ~= 0 then
buffer.clear()
buffer.insert('memory-unit-with-tags')
local stack = buffer.find_item_stack('memory-unit-with-tags')
stack.tags = {name = item, count = count}
stack.custom_description = {
'item-description.memory-unit-with-tags',
compactify(count),
item
}
end
end
script.on_event(defines.events.on_player_mined_entity, on_destroyed)
script.on_event(defines.events.on_robot_mined_entity, on_destroyed)
script.on_event(defines.events.on_entity_died, on_destroyed)
script.on_event(defines.events.script_raised_destroy, on_destroyed)
local function pre_mined(event)
local entity = event.entity
if entity.name ~= 'memory-unit' then return end
local unit_data = global.units[entity.unit_number]
local item = unit_data.item
if item then
local inventory = unit_data.inventory
local in_inventory = inventory.get_item_count(item)
if in_inventory > 0 then
unit_data.count = unit_data.count + inventory.remove{name = item, count = in_inventory}
end
end
end
script.on_event(defines.events.on_pre_player_mined_item, pre_mined)
script.on_event(defines.events.on_robot_pre_mined, pre_mined)
script.on_event(defines.events.on_marked_for_deconstruction, pre_mined)