Code: Select all
require "config"
local itemSignalCache
do
local items = setmetatable({}, {
__index = function(self, key)
local entry = { type = "item", name = key }
rawset(self, key, entry)
return entry
end
})
local cache = {}
itemSignalCache = setmetatable({}, {
__index = cache,
__newindex = function(self, key, value)
if cache[key] then
cache[key].count = value
else
cache[key] = { index = 0, count = value, signal = items[key] }
end
end
})
end
local fluidSignalCache
do
local fluids = setmetatable({}, {
__index = function(self, key)
local entry = { type = "fluid", name = key }
rawset(self, key, entry)
return entry
end
})
local cache = {}
fluidSignalCache = setmetatable({}, {
__index = cache,
__newindex = function(self, key, value)
if cache[key] then
cache[key].count = value
else
cache[key] = { index = 0, count = value, signal = fluids[key] }
end
end
})
end
local function pwipe(tbl) for k in pairs(tbl) do tbl[k] = nil end; return tbl end
local ROBOPORT = "roboport"
local WAGON = "cargo-wagon"
local LOCO = "locomotive"
local TANK = "tank" -- XXX not implemented
local ASS = "assembling-machine"
local CAR = "car" -- XXX not implemented
local FURNACE = "furnace"
-- We always index this table by entity.name before entity.type. Same as for
-- updateByName/Type
local staticParameters = {
[ROBOPORT] = {
parameters = {
{index=1,signal={type="virtual",name="all-lrobots"},count = 0},
{index=2,signal={type="virtual",name="home-lrobots"},count = 0},
{index=3,signal={type="virtual",name="all-crobots"},count = 0},
{index=4,signal={type="virtual",name="home-crobots"},count = 0},
}
}
}
-- If there is an entry for the entity.name in updateByName, we only invoke
-- that one. So for "tank", we only invoke updateByName["tank"], but for other car types
-- we do updateByType["car"]
local updateByType = {}
local updateByName = {}
do
updateByType[ROBOPORT] = function(sensor, cc, read)
local p = sensor.parameters
local net = read.force.find_logistic_network_by_position(read.position, read.surface)
if net and net.valid then --logisticNetwork is nil when roboport is out of power
p.parameters[1].count = net.all_logistic_robots
p.parameters[2].count = net.available_logistic_robots
p.parameters[3].count = net.all_construction_robots
p.parameters[4].count = net.available_construction_robots
else
p.parameters[1].count = 0
p.parameters[2].count = 0
p.parameters[3].count = 0
p.parameters[4].count = 0
end
cc.get_control_behavior().parameters = p
end
end
do
local parameters = {parameters = {}}
updateByType[FURNACE] = function(sensor, cc, read)
local p = pwipe(parameters.parameters)
local counter = 1
for i = 1, 4 do
local inv = read.get_inventory(i)
if inv and inv.valid then
local contents = inv.get_contents()
for item, count in pairs(contents) do
itemSignalCache[item] = count
itemSignalCache[item].index = counter
p[counter] = itemSignalCache[item]
counter = counter + 1
end
end
end
cc.get_control_behavior().parameters = parameters
end
end
do
local parameters = {parameters = {}}
updateByType[ASS] = function(sensor, cc, read)
local p = pwipe(parameters.parameters)
local counter = 1
for i = 1, #read.fluidbox do
local fluid = read.fluidbox[i]
if fluid then
fluidSignalCache[fluid.type] = math.floor(fluid.amount + 0.5)
fluidSignalCache[fluid.type].index = counter
p[counter] = fluidSignalCache[fluid.type]
counter = counter + 1
end
end
for i = 1, 4 do
local inv = read.get_inventory(i)
if inv and inv.valid then
local contents = inv.get_contents()
for item, count in pairs(contents) do
itemSignalCache[item] = count
itemSignalCache[item].index = counter
p[counter] = itemSignalCache[item]
counter = counter + 1
end
end
end
cc.get_control_behavior().parameters = parameters
end
end
-- do
-- for i=1, 4, 1 do
-- local tempInv = entity.get_inventory(i)
-- if tempInv then
-- inventory[#inventory+1] = tempInv
-- end
-- end
-- updateByName[TANK] = function(sensor, cc, read)
-- if tostring(connectedEntity.speed) == "0" then --car isn't moving
-- signals[1] = {index=1,signal={type="virtual",name="detected-tank"},count=1}
-- signalIndex = 2
-- for i=1, #itemSensor.Inventory, 1 do
-- local contentsTable = itemSensor.Inventory[i].get_contents()
-- for k,v in pairs(contentsTable) do
-- signals[signalIndex] = {index = signalIndex, signal = {type = "item",name = k},count = v }
-- signalIndex = signalIndex+1
-- end
-- end
-- end
-- end
-- do
-- for i=1, 4, 1 do
-- local tempInv = entity.get_inventory(i)
-- if tempInv then
-- inventory[#inventory+1] = tempInv
-- end
-- end
-- updateByType[CAR] = function(sensor, cc, read)
-- if tostring(connectedEntity.speed) == "0" then --car isn't moving
-- signals[1] = {index=1,signal={type="virtual",name="detected-car"},count=1}
-- signalIndex = 2
-- for i=1, #itemSensor.Inventory, 1 do
-- local contentsTable = itemSensor.Inventory[i].get_contents()
-- for k,v in pairs(contentsTable) do
-- signals[signalIndex] = {index = signalIndex, signal = {type = "item",name = k},count = v }
-- signalIndex = signalIndex+1
-- end
-- end
-- end
-- end
do
local locomotiveSignal = {
parameters = {
{index=1,signal={type="virtual",name="detected-locomotive"},count=0}
}
}
local station = defines.train_state.wait_station
local signal = defines.train_state.wait_signal
local inventory = defines.inventory.fuel
updateByType[LOCO] = function(sensor, cc, read)
if read.train.state == station or read.train.state == signal then
local inv = read.get_inventory(inventory)
local param = locomotiveSignal
if inv and inv.valid then
local contents = inv.get_contents()
-- Index 1 is the detected-locomotive signal
local counter = 2
for item, count in pairs(contents) do
itemSignalCache[item] = count
itemSignalCache[item].index = counter
param.parameters[counter] = itemSignalCache[item]
counter = counter + 1
end
end
param.parameters[1].count = #read.train.locomotives.front_movers + #read.train.locomotives.back_movers
cc.get_control_behavior().parameters = param
else
sensor.read = nil
cc.get_control_behavior().parameters = nil
end
end
end
do
local wagonSignal = {
parameters = {
{index=1,signal={type="virtual",name="detected-wagon"},count=0}
}
}
local station = defines.train_state.wait_station
local signal = defines.train_state.wait_signal
local inventory = defines.inventory.cargo_wagon
updateByType[WAGON] = function(sensor, cc, read)
if read.train.state == station or read.train.state == signal then
local inv = read.get_inventory(inventory)
local param = wagonSignal
if inv and inv.valid then
local contents = inv.get_contents()
-- Index 1 is the detected-wagon signal
local counter = 2
for item, count in pairs(contents) do
itemSignalCache[item] = count
itemSignalCache[item].index = counter
param.parameters[counter] = itemSignalCache[item]
counter = counter + 1
end
end
param.parameters[1].count = #read.train.cargo_wagons
cc.get_control_behavior().parameters = param
else
sensor.read = nil
cc.get_control_behavior().parameters = nil
end
end
end
local findConnectedEntity
do
findConnectedEntity = function(sensor)
local found = sensor.cc.surface.find_entities(sensor.area)
if not found then return end
for i, entity in next, found do
if entity.valid and updateByType[entity.type] or updateByName[entity.name] then
sensor.read = entity
break
end
end
if not sensor.read or not sensor.read.type then return end
if staticParameters[sensor.read.name] then
sensor.parameters = staticParameters[sensor.read.name]
elseif staticParameters[sensor.read.type] then
sensor.parameters = staticParameters[sensor.read.type]
else
sensor.parameters = nil
end
end
end
local ticker
do
local function update(sensor)
local cc = sensor.cc -- constantCombinator
local read = sensor.read
if not read or not read.valid then
sensor.read = nil
cc.get_control_behavior().parameters = nil
return
end
if updateByName[read.name] then
updateByName[read.name](sensor, cc, read)
elseif updateByType[read.type] then
updateByType[read.type](sensor, cc, read)
end
end
ticker = function(event)
if event.tick % 20 == 0 then
if #global.sensors == 0 then
script.on_event(defines.events.on_tick, nil)
return
end
for i, sensor in next, global.sensors do
if not sensor.read or not sensor.read.valid then
findConnectedEntity(sensor)
end
if sensor.read then update(sensor) end
end
end
end
-- Attempt to instantly get contents of wagons regardless of tick% delay
script.on_event(defines.events.on_train_changed_state, function(event)
if not event.train.cargo_wagons then return end
for i, sensor in next, global.sensors do
if not sensor.read or not sensor.read.valid then
findConnectedEntity(sensor)
if sensor.read then update(sensor) end
end
end
end)
end
do
local function onLoad()
global.sensors = global.sensors or {}
if #global.sensors > 0 then
script.on_event(defines.events.on_tick, ticker)
end
end
script.on_init(onLoad)
script.on_load(onLoad)
end
do
-- Created on game load/sensor creation and cached until the entity disappears
local function getSearchArea(e)
if e.direction == 0 then --south
return {{e.position.x - BBox_offset, e.position.y}, {e.position.x + BBox_offset, e.position.y + BBox_range}}
elseif e.direction == 2 then --west
return {{e.position.x - BBox_range, e.position.y - BBox_offset}, {e.position.x, e.position.y + BBox_offset}}
elseif e.direction == 4 then --north
return {{e.position.x - BBox_offset, e.position.y - BBox_range}, {e.position.x + BBox_offset, e.position.y}}
elseif e.direction == 6 then --east
return {{e.position.x, e.position.y - BBox_offset}, {e.position.x + BBox_range, e.position.y + BBox_offset}}
end
end
local function createSensor(entity)
if global.sensors == nil then
global.sensors = {}
end
if #global.sensors == 0 then
script.on_event(defines.events.on_tick, ticker)
end
entity.operable = false
entity.rotatable = false
local s = {
unitId = entity.unit_number,
cc = entity,
}
s.area = getSearchArea(entity)
findConnectedEntity(s)
global.sensors[#global.sensors+1] = s
end
script.on_configuration_changed(function(data)
global.sensors = global.sensors or {}
if not global.migrated then
for k, surface in pairs(game.surfaces) do
local ents = surface.find_entities_filtered({name="item-sensor"})
if ents and type(ents) == "table" and #ents > 0 then
for i, s in next, ents do
createSensor(s)
end
end
end
global.migrated = true
end
for i, sensor in next, global.sensors do
sensor.unitId = sensor.cc.unit_number
if not sensor.area then
sensor.area = getSearchArea(sensor.cc)
end
findConnectedEntity(sensor)
end
end)
local function onCreateCheck(event)
if event.created_entity.name == "item-sensor" then
createSensor(event.created_entity)
end
end
script.on_event(defines.events.on_built_entity, onCreateCheck)
script.on_event(defines.events.on_robot_built_entity, onCreateCheck)
local function onRemoveCheck(event)
if global.sensors ~= nil then
if event.entity.name == "item-sensor" then
for i = #global.sensors, 1, -1 do
if event.entity.unit_number == global.sensors[i].unitId then
table.remove(global.sensors, i)
return
end
end
end
end
end
script.on_event(defines.events.on_preplayer_mined_item, onRemoveCheck)
script.on_event(defines.events.on_robot_pre_mined, onRemoveCheck)
script.on_event(defines.events.on_entity_died, onRemoveCheck)
end