Files
factorio-scenario-ExpCluster/exp_legacy/module/modules/control/vlayer.lua
T
bbassieandClaude Opus 5 ab89b9feca Type vlayer forward declarations and config entries
The gui element locals are declared at the top and assigned far below,
so emmylua inferred them as nil at every use site.

circuit_oc was reassigned from a control behaviour to a logistic
section, which hid every section method behind the wrong type.

allowed_items entries carry heterogeneous optional properties, now
described by a class.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 15:31:18 +00:00

807 lines
32 KiB
Lua

--[[-- Control Module - vlayer
- Adds a virtual layer to store power to save space.
@control vlayer
@alias vlayer
]]
local ExpUtil = require("modules/exp_util")
local Storage = require("modules/exp_util/storage")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local config = require("modules.exp_legacy.config.vlayer") --- @dep config.vlayer
local mega = 1000000
local vlayer = {}
local vlayer_data = {
entity_interfaces = {
energy = {}, --- @type LuaEntity[]
circuit = {}, --- @type LuaEntity[]
storage_input = {}, --- @type LuaEntity[]
storage_output = {}, --- @type LuaEntity[]
},
properties = {
total_surface_area = 0,
used_surface_area = 0,
total_production = 0,
production = 0,
discharge = 0,
capacity = 0,
},
storage = {
items = {},
power_items = {},
energy = 0,
unallocated = {},
},
surface = table.deep_copy(config.surface),
}
Storage.register(vlayer_data, function(tbl)
vlayer_data = tbl
end)
for name, properties in pairs(config.allowed_items) do
properties.modded = false
if properties.power then
vlayer_data.storage.power_items[name] = {
value = properties.fuel_value * 1000000,
count = 0,
}
end
end
-- For all modded items, create a config for them
for item_name, properties in pairs(config.modded_items) do
local base_properties = config.allowed_items[properties.base_game_equivalent]
local m = properties.multiplier
config.allowed_items[item_name] = {
starting_value = properties.starting_value or 0,
required_area = base_properties.required_area or 0,
surface_area = (base_properties.surface_area or 0) * m,
production = (base_properties.production or 0) * m,
capacity = (base_properties.capacity or 0) * m,
modded = true,
}
end
--- Get all items in storage, do not modify
-- @treturn table a dictionary of all items stored in the vlayer
function vlayer.get_items()
return vlayer_data.storage.items
end
--- Get all unallocated items in storage
-- @treturn table a dictionary of all unallocated items stored in the vlayer
function vlayer.get_unallocated_items()
return vlayer_data.storage.unallocated
end
--- Get all allocated items in storage
-- @treturn table a dictionary of all allocated items stored in the vlayer
function vlayer.get_allocated_items()
local r = {}
for k, v in pairs(vlayer_data.storage.items) do
r[k] = v
if vlayer_data.storage.unallocated[k] then
r[k] = r[k] - vlayer_data.storage.unallocated[k]
end
end
return r
end
--- Get the actual defecit of land
local function get_actual_land_defecit()
local n = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
for k, v in pairs(vlayer.get_unallocated_items()) do
n = n - (config.allowed_items[k].required_area * v)
end
return n
end
--- Get interface counts
-- @treturn table a dictionary of the vlayer interface counts
function vlayer.get_interface_counts()
local interfaces = vlayer_data.entity_interfaces
return {
energy = #interfaces.energy,
circuit = #interfaces.circuit,
storage_input = #interfaces.storage_input,
storage_output = #interfaces.storage_output,
}
end
--- Get interfaces
-- @treturn table a dictionary of the vlayer interfaces
function vlayer.get_interfaces()
local interfaces = vlayer_data.entity_interfaces
return {
energy = interfaces.energy,
circuit = interfaces.circuit,
storage_input = interfaces.storage_input,
storage_output = interfaces.storage_output,
}
end
--[[
25,200 / 420 s
昼 210秒 ソーラー効率100%
夕方 84秒 1秒ごとにソーラー発電量が約1.2%ずつ下がり、やがて0%になる
夜 42秒 ソーラー発電量が0%になる
朝方 84秒 1秒ごとにソーラー発電量が約1.2%ずつ上がり、やがて100%になる
(surface.dawn) 0.75 18,900 Day 12,600 210s
0.00 0 Noon
(surface.dusk) 0.25 6,300 Sunset 5,040 84s
(surface.evening) 0.45 11,340 Night 2,520 42s
(surface.morning) 0.55 13,860 Sunrise 5,040 84s
]]
--- Get the power multiplier based on the surface time
local function get_production_multiplier()
local mul = vlayer_data.surface.solar_power_multiplier
local surface = vlayer_data.surface
if surface.always_day then
-- Surface is always day, so full production is used
return mul
end
if surface.darkness then
-- We are using a real surface, our config does not contain "darkness"
local brightness = 1 - surface.darkness
if brightness >= surface.min_brightness then
return mul * (brightness - surface.min_brightness) / (1 - surface.min_brightness)
else
return 0
end
end
-- Caused by using a set config rather than a surface
local tick = game.tick % surface.ticks_per_day
local daytime = tick / surface.ticks_per_day
surface.daytime = daytime
if daytime <= surface.dusk then -- Noon to Sunset
return mul
elseif daytime <= surface.evening then -- Sunset to Night
return mul * (1 - ((daytime - surface.dusk) / (surface.evening - surface.dusk)))
elseif daytime <= surface.morning then -- Night to Sunrise
return 0
elseif daytime <= surface.dawn then -- Sunrise to Morning
return mul * ((surface.daytime - surface.morning) / (surface.dawn - surface.morning))
else -- Morning to Noon
return mul
end
end
--- Get the sustained power multiplier, this needs improving
local function get_sustained_multiplier()
local mul = vlayer_data.surface.solar_power_multiplier
local surface = vlayer_data.surface
if surface.always_day then
-- Surface is always day, so full production is used
return mul
end
-- For nauvis vanilla: 210s + (1/2 x (84s + 84s))
local day_duration = 1 - surface.dawn + surface.dusk
local sunset_duration = surface.evening - surface.dusk
local sunrise_duration = surface.dawn - surface.morning
return mul * (day_duration + (0.5 * (sunset_duration + sunrise_duration)))
end
--- Internal, Allocate items in the vlayer, this will increase the property values of the vlayer such as production and capacity
-- Does not increment item storage, so should not be called before insert_item unless during init
-- Does not validate area requirements, so checks must be performed before calling this function
-- Accepts negative count for deallocating items
-- @tparam string item_name The name of the item to allocate
-- @tparam number count The count of the item to allocate
function vlayer.allocate_item(item_name, count)
local item_properties = config.allowed_items[item_name]
assert(item_properties, "Item not allowed in vlayer: " .. tostring(item_name))
if item_properties.production then
local nc = item_properties.production * count
vlayer_data.properties.production = vlayer_data.properties.production + nc
vlayer_data.properties.total_production = vlayer_data.properties.total_production + nc
end
if item_properties.capacity then
vlayer_data.properties.capacity = vlayer_data.properties.capacity + item_properties.capacity * count
end
if item_properties.discharge then
vlayer_data.properties.discharge = vlayer_data.properties.discharge + item_properties.discharge * count
end
if item_properties.surface_area then
vlayer_data.properties.total_surface_area = vlayer_data.properties.total_surface_area + item_properties.surface_area * count
end
if item_properties.required_area and item_properties.required_area > 0 then
vlayer_data.properties.used_surface_area = vlayer_data.properties.used_surface_area + item_properties.required_area * count
end
end
function vlayer.unable_alloc_item_pwr_calc(item_name, count)
local item_properties = config.allowed_items[item_name]
if item_properties.production then
vlayer_data.properties.total_production = vlayer_data.properties.total_production + item_properties.production * count
end
end
-- For all allowed items, setup their starting values, default 0
for item_name, properties in pairs(config.allowed_items) do
vlayer_data.storage.items[item_name] = properties.starting_value or 0
if properties.required_area and properties.required_area > 0 then
vlayer_data.storage.unallocated[item_name] = 0
end
vlayer.allocate_item(item_name, properties.starting_value)
end
--- Insert an item into the vlayer, this will increment its count in storage and allocate it if possible
-- @tparam string item_name The name of the item to insert
-- @tparam number count The count of the item to insert
function vlayer.insert_item(item_name, count)
local item_properties = config.allowed_items[item_name]
assert(item_properties, "Item not allowed in vlayer: " .. tostring(item_name))
vlayer_data.storage.items[item_name] = vlayer_data.storage.items[item_name] + count
if not config.unlimited_surface_area and item_properties.required_area and item_properties.required_area > 0 then
-- Calculate how many can be allocated
local surplus_area = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
local allocate_count = math.min(count, math.floor(surplus_area / item_properties.required_area))
if allocate_count > 0 then
vlayer.allocate_item(item_name, allocate_count)
end
local unallocated = count - allocate_count
vlayer_data.storage.unallocated[item_name] = vlayer_data.storage.unallocated[item_name] + unallocated
vlayer.unable_alloc_item_pwr_calc(item_name, unallocated)
else
vlayer.allocate_item(item_name, count)
end
end
--- Remove an item from the vlayer, this will decrement its count in storage and prioritise unallocated items over deallocation
-- Can not always fulfil the remove request for items which provide surface area, therefore returns the amount actually removed
-- @tparam string item_name The name of the item to remove
-- @tparam number count The count of the item to remove
-- @treturn number The count of the item actually removed
function vlayer.remove_item(item_name, count)
local item_properties = config.allowed_items[item_name]
assert(item_properties, "Item not allowed in vlayer: " .. tostring(item_name))
local remove_unallocated = 0
if not config.unlimited_surface_area and item_properties.required_area and item_properties.required_area > 0 then
-- Remove from the unallocated storage first
remove_unallocated = math.min(count, vlayer_data.storage.unallocated[item_name])
if remove_unallocated > 0 then
vlayer_data.storage.items[item_name] = vlayer_data.storage.items[item_name] - count
vlayer_data.storage.unallocated[item_name] = vlayer_data.storage.unallocated[item_name] - count
end
-- Check if any more items need to be removed
count = count - remove_unallocated
if count == 0 then
return remove_unallocated
end
end
-- Calculate the amount to remove based on items in storage
local remove_count = math.min(count, vlayer_data.storage.items[item_name])
if item_properties.surface_area and item_properties.surface_area > 0 then
-- If the item provides surface area then it has additional limitations
local surplus_area = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
remove_count = math.min(remove_count, math.floor(surplus_area / item_properties.surface_area))
if remove_count <= 0 then
return remove_unallocated
end
end
-- Remove the item from allocated storage
vlayer_data.storage.items[item_name] = vlayer_data.storage.items[item_name] - remove_count
vlayer.allocate_item(item_name, -remove_count)
return remove_unallocated + remove_count
end
--- Create a new storage input interface
-- @tparam LuaSurface surface The surface to place the interface onto
-- @tparam MapPosition position The position on the surface to place the interface at
-- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
-- @treturn LuaEntity The entity that was created for the interface
function vlayer.create_input_interface(surface, position, circuit, last_user)
local interface = surface.create_entity{ name = "storage-chest", position = position, force = "neutral" }
interface.storage_filter = { name = "deconstruction-planner", quality = "normal" }
table.insert(vlayer_data.entity_interfaces.storage_input, interface)
if last_user then
interface.last_user = last_user
end
if circuit then
for k, _ in pairs(circuit) do
for _, v in pairs(circuit[k]) do
interface.connect_neighbour{ wire = defines.wire_type[k], target_entity = v }
end
end
end
interface.destructible = false
interface.minable_flag = false
interface.operable = true
return interface
end
--- Handle all input interfaces, will take their contents and insert it into the vlayer storage
local function handle_input_interfaces()
for index, interface in pairs(vlayer_data.entity_interfaces.storage_input) do
if not interface.valid then
vlayer_data.entity_interfaces.storage_input[index] = nil
else
local inventory = interface.get_inventory(defines.inventory.chest)
for _, v in pairs(inventory.get_contents()) do
if config.allowed_items[v.name] then
--[[
there are no quality support currently.
so instead, using the stats projection value for higher quality.
]]
local count_deduct
local count_add
if prototypes.quality[v.quality].level == 0 then
count_deduct = v.count
count_add = v.count
elseif prototypes.quality[v.quality].level > 0 and v.count >= 10 then
local batch = math.floor(v.count / 10)
count_deduct = batch * 10
count_add = batch * (10 + (prototypes.quality[v.quality].level * 3))
end
if count_deduct and count_add then
if config.allowed_items[v.name].modded then
if config.modded_auto_downgrade then
vlayer.insert_item(config.modded_items[v.name].base_game_equivalent, count_add * config.modded_items[v.name].multiplier)
else
vlayer.insert_item(v.name, count_add)
end
else
if vlayer_data.storage.power_items[v.name] then
vlayer_data.storage.power_items[v.name].count = vlayer_data.storage.power_items[v.name].count + count_add
else
vlayer.insert_item(v.name, count_add)
end
end
inventory.remove{ name = v.name, count = count_deduct, quality = v.quality }
end
end
end
end
end
end
--- Create a new storage output interface
-- @tparam LuaSurface surface The surface to place the interface onto
-- @tparam MapPosition position The position on the surface to place the interface at
-- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
-- @treturn LuaEntity The entity that was created for the interface
function vlayer.create_output_interface(surface, position, circuit, last_user)
local interface = surface.create_entity{ name = "requester-chest", position = position, force = "neutral" }
table.insert(vlayer_data.entity_interfaces.storage_output, interface)
if last_user then
interface.last_user = last_user
end
if circuit then
for k, _ in pairs(circuit) do
for _, v in pairs(circuit[k]) do
interface.connect_neighbour{ wire = defines.wire_type[k], target_entity = v }
end
end
end
interface.destructible = false
interface.minable_flag = false
interface.operable = true
return interface
end
--- Handle all output interfaces, will take their requests and remove it from the vlayer storage
local function handle_output_interfaces()
for index, interface in pairs(vlayer_data.entity_interfaces.storage_output) do
if not interface.valid then
vlayer_data.entity_interfaces.storage_output[index] = nil
else
local inventory = interface.get_inventory(defines.inventory.chest)
local inventory_request_sections = interface.get_logistic_sections().sections
for i = 1, #inventory_request_sections do
for _, v in pairs(inventory_request_sections[i].filters) do
if v.value and config.allowed_items[v.value.name] then
local current_amount = inventory.get_item_count(v.value.name)
local request_amount = math.min(v.min - current_amount, vlayer_data.storage.items[v.value.name])
if request_amount > 0 and inventory.can_insert{ name = v.value.name, count = request_amount } then
local removed_item_count = vlayer.remove_item(v.value.name, request_amount)
if removed_item_count > 0 then
inventory.insert{ name = v.value.name, count = removed_item_count, quality = "normal" }
end
end
end
end
end
end
end
end
--- Handle the unallocated items because more surface area may have been added
local function handle_unallocated()
-- unallocated cant happen when its unlimited
if config.unlimited_surface_area then
return
end
-- Get the total unallocated area so items can be allocated in equal amounts
local unallocated_area = 0
for item_name, count in pairs(vlayer_data.storage.unallocated) do
local item_properties = config.allowed_items[item_name]
unallocated_area = unallocated_area + item_properties.required_area * count
end
if unallocated_area == 0 then
return
end
-- Allocate items in an equal distribution
local surplus_area = vlayer_data.properties.total_surface_area - vlayer_data.properties.used_surface_area
for item_name, count in pairs(vlayer_data.storage.unallocated) do
local allocation_count = math.min(count, math.floor(count * surplus_area / unallocated_area))
if allocation_count > 0 then
vlayer_data.storage.unallocated[item_name] = vlayer_data.storage.unallocated[item_name] - allocation_count
vlayer.allocate_item(item_name, allocation_count)
vlayer.unable_alloc_item_pwr_calc(item_name, -allocation_count)
end
end
end
--- Get the statistics for the vlayer
function vlayer.get_statistics()
local vdp = vlayer_data.properties.production * mega
local gdm = get_production_multiplier()
local gsm = get_sustained_multiplier()
local gald = get_actual_land_defecit()
return {
total_surface_area = vlayer_data.properties.total_surface_area,
used_surface_area = vlayer_data.properties.used_surface_area,
remaining_surface_area = gald,
surface_area = vlayer_data.properties.total_surface_area - gald,
production_multiplier = gdm,
energy_max = vdp,
energy_production = vdp * gdm,
energy_total_production = vlayer_data.properties.total_production * gsm * mega,
energy_sustained = vdp * gsm,
energy_capacity = vlayer_data.properties.capacity * mega,
energy_storage = vlayer_data.storage.energy,
}
end
--- add or reduce vlayer power
--- @param power number
--- @return number
function vlayer.energy_changed(power)
local new_value = vlayer_data.storage.energy + power
vlayer_data.storage.energy = new_value
return new_value
end
--- Circuit signals used for the statistics
function vlayer.get_circuits()
return {
total_surface_area = "signal-A",
used_surface_area = "signal-U",
remaining_surface_area = "signal-R",
production_multiplier = "signal-M",
energy_production = "signal-P",
energy_sustained = "signal-S",
energy_capacity = "signal-C",
energy_storage = "signal-E",
}
end
local vlayer_circuits_string = ""
for key, value in pairs(vlayer.get_circuits()) do
vlayer_circuits_string = vlayer_circuits_string .. string.format("[virtual-signal=%s] = %s\n", value, key:gsub("_", " "))
end
--- Create a new circuit interface
-- @tparam LuaSurface surface The surface to place the interface onto
-- @tparam MapPosition position The position on the surface to place the interface at
-- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
-- @treturn LuaEntity The entity that was created for the interface
function vlayer.create_circuit_interface(surface, position, circuit, last_user)
local interface = surface.create_entity{ name = "constant-combinator", position = position, force = "neutral" }
table.insert(vlayer_data.entity_interfaces.circuit, interface)
if last_user then
interface.last_user = last_user
end
if circuit then
for k, _ in pairs(circuit) do
for _, v in pairs(circuit[k]) do
interface.connect_neighbour{ wire = defines.wire_type[k], target_entity = v }
end
end
end
interface.destructible = false
interface.minable_flag = false
interface.operable = true
return interface
end
--- Handle all circuit interfaces, updating their signals to match the vlayer statistics
local function handle_circuit_interfaces()
local stats = vlayer.get_statistics()
for index, interface in pairs(vlayer_data.entity_interfaces.circuit) do
if not interface.valid then
vlayer_data.entity_interfaces.circuit[index] = nil
else
local control = interface.get_or_create_control_behavior()
if control.sections_count == 0 then
control.add_section()
end
local circuit_oc = control.sections[1]
local signal_index = 1
local circuit = vlayer.get_circuits()
-- Set the virtual signals based on the vlayer stats
for stat_name, signal_name in pairs(circuit) do
if stat_name:find("energy") then
circuit_oc.set_slot(signal_index, { value = { type = "virtual", name = signal_name, quality = "normal" }, min = math.floor(stats[stat_name] / mega) })
elseif stat_name == "production_multiplier" then
circuit_oc.set_slot(signal_index, { value = { type = "virtual", name = signal_name, quality = "normal" }, min = math.floor(stats[stat_name] * 10000) })
else
circuit_oc.set_slot(signal_index, { value = { type = "virtual", name = signal_name, quality = "normal" }, min = math.floor(stats[stat_name]) })
end
signal_index = signal_index + 1
end
-- Set the item signals based on stored items
for item_name, count in pairs(vlayer_data.storage.items) do
if prototypes.item[item_name] and count > 0 then
local existing_index = circuit_oc.set_slot(signal_index, { value = { type = "item", name = item_name, quality = "normal" }, min = count })
if existing_index then
circuit_oc.clear_slot(existing_index)
circuit_oc.set_slot(signal_index, { value = { type = "item", name = item_name, quality = "normal" }, min = count })
end
signal_index = signal_index + 1
end
end
-- Clear remaining signals to prevent outdated values being present (caused by count > 0 check)
for clear_index = signal_index, #circuit do
if not circuit_oc.get_slot(clear_index).signal then
break -- There are no more signals to clear
end
circuit_oc.clear_slot(clear_index)
end
interface.combinator_description = vlayer_circuits_string
end
end
end
--- Create a new energy interface
-- @tparam LuaSurface surface The surface to place the interface onto
-- @tparam MapPosition position The position on the surface to place the interface at
-- @tparam[opt] LuaPlayer player The player to show as the last user of the interface
-- @treturn LuaEntity The entity that was created for the interface, or nil if it could not be created
function vlayer.create_energy_interface(surface, position, last_user)
if not surface.can_place_entity{ name = "electric-energy-interface", position = position } then
return nil
end
local interface = surface.create_entity{ name = "electric-energy-interface", position = position, force = "neutral" }
table.insert(vlayer_data.entity_interfaces.energy, interface)
if last_user then
interface.last_user = last_user
end
interface.destructible = false
interface.minable_flag = false
interface.operable = false
interface.electric_buffer_size = 0
interface.power_production = 0
interface.power_usage = 0
interface.energy = 0
return interface
end
--- Handle all energy interfaces as well as the energy storage
local function handle_energy_interfaces()
-- Add the newly produced power
local production = vlayer_data.properties.production * mega * (config.update_tick_energy / 60)
vlayer_data.storage.energy = vlayer_data.storage.energy + math.floor(production * get_production_multiplier())
-- Calculate how much power is present in the network, that is storage + all interfaces
if #vlayer_data.entity_interfaces.energy > 0 then
local available_energy = vlayer_data.storage.energy
for index, interface in pairs(vlayer_data.entity_interfaces.energy) do
if not interface.valid then
vlayer_data.entity_interfaces.energy[index] = nil
else
available_energy = available_energy + interface.energy
end
end
-- Distribute the energy between all interfaces
local discharge_rate = 2 * (production + vlayer_data.properties.discharge * mega) / #vlayer_data.entity_interfaces.energy
local fill_to = math.min(discharge_rate, math.floor(available_energy / #vlayer_data.entity_interfaces.energy))
for _, interface in pairs(vlayer_data.entity_interfaces.energy) do
interface.electric_buffer_size = math.max(discharge_rate, interface.energy) -- prevent energy loss
local delta = fill_to - interface.energy -- positive means storage to interface
vlayer_data.storage.energy = vlayer_data.storage.energy - delta
interface.energy = interface.energy + delta
end
end
-- Cap the stored energy to the allowed capacity
if not config.unlimited_capacity and vlayer_data.storage.energy > vlayer_data.properties.capacity * mega then
vlayer_data.storage.energy = vlayer_data.properties.capacity * mega
-- burn the trash to produce power
elseif vlayer_data.storage.power_items then
for k, v in pairs(vlayer_data.storage.power_items) do
local max_burning = (vlayer_data.properties.capacity * mega / 2) - vlayer_data.storage.energy
if v.count > 0 and max_burning > 0 then
local to_burn = math.min(v.count, math.floor(max_burning / v.value))
vlayer_data.storage.energy = vlayer_data.storage.energy + (to_burn * v.value)
vlayer_data.storage.power_items[k].count = vlayer_data.storage.power_items[k].count - to_burn
end
end
end
end
--- Remove the entity interface using the given position
-- @tparam LuaSurface surface The surface to search for an interface on
-- @tparam MapPosition position The position of the item
-- @treturn string The type of interface that was removed, or nil if no interface was found
-- @treturn MapPosition The position the interface was at, or nil if no interface was found
function vlayer.remove_interface(surface, position)
local entities = surface.find_entities_filtered{
name = { "storage-chest", "requester-chest", "constant-combinator", "electric-energy-interface" },
force = "neutral",
position = position,
radius = 2,
limit = 1,
}
-- Get the details which will be returned
if #entities == 0 then
return nil, nil, nil
end
local interface = entities[1]
local name = interface.name
local sur = interface.surface
local pos = interface.position
-- Return the type of interface removed and do some clean up
if name == "storage-chest" then
local inventory = assert(interface.get_inventory(defines.inventory.chest))
ExpUtil.transfer_inventory_to_surface{
inventory = inventory,
surface = interface.surface,
name = "iron-chest",
allow_creation = true,
}
table.remove_element(vlayer_data.entity_interfaces.storage_input, interface)
interface.destroy()
return "storage-input", sur, pos
elseif name == "requester-chest" then
local inventory = assert(interface.get_inventory(defines.inventory.chest))
ExpUtil.transfer_inventory_to_surface{
inventory = inventory,
surface = interface.surface,
name = "iron-chest",
allow_creation = true,
}
table.remove_element(vlayer_data.entity_interfaces.storage_output, interface)
interface.destroy()
return "storage-output", sur, pos
elseif name == "constant-combinator" then
table.remove_element(vlayer_data.entity_interfaces.circuit, interface)
interface.destroy()
return "circuit", sur, pos
elseif name == "electric-energy-interface" then
vlayer_data.storage.energy = vlayer_data.storage.energy + interface.energy
table.remove_element(vlayer_data.entity_interfaces.energy, interface)
interface.destroy()
return "energy", sur, pos
end
end
local function on_surface_event()
if config.mimic_surface then
local surface = game.get_surface(config.mimic_surface)
if surface then
vlayer_data.surface = surface
return
end
end
if not vlayer_data.surface.index then
-- Our fake surface data never has an index, we test for this to avoid unneeded copies from the config
vlayer_data.surface = table.deep_copy(config.surface)
end
end
--- Handle all storage IO and attempt allocation of unallocated items
Event.on_nth_tick(config.update_tick_storage, function(_)
handle_input_interfaces()
handle_output_interfaces()
handle_unallocated()
end)
--- Handle all energy and circuit updates
Event.on_nth_tick(config.update_tick_energy, function(_)
handle_circuit_interfaces()
handle_energy_interfaces()
end)
Event.add(defines.events.on_surface_created, on_surface_event)
Event.add(defines.events.on_surface_renamed, on_surface_event)
Event.add(defines.events.on_surface_imported, on_surface_event)
Event.on_init(on_surface_event) -- Default surface always exists, does not trigger on_surface_created
return vlayer