mirror of
https://github.com/Xevion/project-cybersyn.git
synced 2025-12-06 21:16:04 -06:00
559 lines
16 KiB
Lua
559 lines
16 KiB
Lua
--By Mami
|
|
local area = require("__flib__.area")
|
|
local abs = math.abs
|
|
local floor = math.floor
|
|
local ceil = math.ceil
|
|
local string_find = string.find
|
|
local string_sub = string.sub
|
|
|
|
|
|
local function table_compare(t0, t1)
|
|
if #t0 ~= #t1 then
|
|
return false
|
|
end
|
|
for i = 0, #t0 do
|
|
if t0[i] ~= t1[i] then
|
|
return false
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function iterr(a, i)
|
|
i = i + 1
|
|
if i <= #a then
|
|
return i, a[#a - i + 1]
|
|
end
|
|
end
|
|
|
|
local function irpairs(a)
|
|
return iterr, a, 0
|
|
end
|
|
|
|
|
|
local function is_layout_accepted(layout_pattern, layout)
|
|
local valid = true
|
|
for i, v in ipairs(layout) do
|
|
local p = layout_pattern[i] or 0
|
|
if (v == 0 and p == 2) or (v == 1 and (p == 0 or p == 2)) or (v == 2 and (p == 0 or p == 1)) then
|
|
valid = false
|
|
break
|
|
end
|
|
end
|
|
if valid or not layout[0] then return valid end
|
|
for i, v in irpairs(layout) do
|
|
local p = layout_pattern[i] or 0
|
|
if (v == 0 and p == 2) or (v == 1 and (p == 0 or p == 2)) or (v == 2 and (p == 0 or p == 1)) then
|
|
valid = false
|
|
break
|
|
end
|
|
end
|
|
return valid
|
|
end
|
|
|
|
---@param map_data MapData
|
|
---@param train_id uint
|
|
---@param train Train
|
|
function remove_train(map_data, train_id, train)
|
|
local parked_at_depot_id = train.parked_at_depot_id
|
|
if parked_at_depot_id then
|
|
local depot = map_data.depots[parked_at_depot_id]
|
|
depot.available_train_id = nil
|
|
end
|
|
remove_available_train(map_data, train_id, train)
|
|
local layout_id = train.layout_id
|
|
local count = map_data.layout_train_count[layout_id]
|
|
if count <= 1 then
|
|
map_data.layout_train_count[layout_id] = nil
|
|
map_data.layouts[layout_id] = nil
|
|
for station_id, station in pairs(map_data.stations) do
|
|
station.accepted_layouts[layout_id] = nil
|
|
end
|
|
else
|
|
map_data.layout_train_count[layout_id] = count - 1
|
|
end
|
|
map_data.trains[train_id] = nil
|
|
end
|
|
|
|
|
|
---@param map_data MapData
|
|
---@param train Train
|
|
function update_train_layout(map_data, train)
|
|
local carriages = train.entity.carriages
|
|
local layout = {}
|
|
local i = 1
|
|
local item_slot_capacity = 0
|
|
local fluid_capacity = 0
|
|
for _, carriage in pairs(carriages) do
|
|
if carriage.type == "cargo-wagon" then
|
|
layout[#layout + 1] = 1
|
|
local inv = carriage.get_inventory(defines.inventory.cargo_wagon)
|
|
item_slot_capacity = item_slot_capacity + #inv
|
|
elseif carriage.type == "fluid-wagon" then
|
|
layout[#layout + 1] = 2
|
|
fluid_capacity = fluid_capacity + carriage.prototype.fluid_capacity
|
|
else
|
|
layout[#layout + 1] = 0
|
|
end
|
|
i = i + 1
|
|
end
|
|
local back_movers = train.entity.locomotives["back_movers"]
|
|
if #back_movers > 0 then
|
|
--mark the layout as reversible
|
|
layout[0] = true
|
|
end
|
|
|
|
local layout_id = 0
|
|
for id, cur_layout in pairs(map_data.layouts) do
|
|
if table_compare(layout, cur_layout) then
|
|
layout = cur_layout
|
|
layout_id = id
|
|
break
|
|
end
|
|
end
|
|
if layout_id == 0 then
|
|
--define new layout
|
|
layout_id = map_data.layout_top_id
|
|
map_data.layout_top_id = map_data.layout_top_id + 1
|
|
|
|
map_data.layouts[layout_id] = layout
|
|
map_data.layout_train_count[layout_id] = 1
|
|
for _, station in pairs(map_data.stations) do
|
|
if station.layout_pattern then
|
|
station.accepted_layouts[layout_id] = is_layout_accepted(station.layout_pattern, layout) or nil
|
|
end
|
|
end
|
|
else
|
|
map_data.layout_train_count[layout_id] = map_data.layout_train_count[layout_id] + 1
|
|
end
|
|
train.layout_id = layout_id
|
|
train.item_slot_capacity = item_slot_capacity
|
|
train.fluid_capacity = fluid_capacity
|
|
end
|
|
|
|
---@param stop LuaEntity
|
|
---@param train LuaTrain
|
|
local function get_train_direction(stop, train)
|
|
local back_rail = train.back_rail
|
|
|
|
if back_rail then
|
|
local back_pos = back_rail.position
|
|
local stop_pos = stop.position
|
|
if abs(back_pos.x - stop_pos.x) < 3 and abs(back_pos.y - stop_pos.y) < 3 then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
---@param map_data MapData
|
|
---@param station Station
|
|
---@param train Train
|
|
function set_p_wagon_combs(map_data, station, train)
|
|
if not station.wagon_combs or not next(station.wagon_combs) then return end
|
|
local carriages = train.entity.carriages
|
|
local manifest = train.manifest
|
|
|
|
local is_reversed = get_train_direction(station.entity_stop, train.entity)
|
|
|
|
local item_i = 1
|
|
local item = manifest[item_i]
|
|
local item_count = item.count
|
|
local fluid_i = 1
|
|
local fluid = manifest[fluid_i]
|
|
local fluid_count = fluid.count
|
|
|
|
local ivpairs = is_reversed and irpairs or ipairs
|
|
for carriage_i, carriage in ivpairs(carriages) do
|
|
--NOTE: we are not checking valid
|
|
---@type LuaEntity?
|
|
local comb = station.wagon_combs[carriage_i]
|
|
if comb and not comb.valid then
|
|
comb = nil
|
|
station.wagon_combs[carriage_i] = nil
|
|
if next(station.wagon_combs) == nil then
|
|
station.wagon_combs = nil
|
|
break
|
|
end
|
|
end
|
|
if carriage.type == "cargo-wagon" and item_i <= #manifest then
|
|
local signals = {}
|
|
|
|
local inv = carriage.get_inventory(defines.inventory.cargo_wagon)
|
|
if inv then
|
|
local inv_filter_i = 1
|
|
local item_slots_capacity = #inv - station.locked_slots
|
|
while item_slots_capacity > 0 do
|
|
local do_inc = false
|
|
if item.type == "item" then
|
|
local stack_size = game.item_prototypes[item.name].stack_size
|
|
local item_slots = ceil(item_count/stack_size)
|
|
local i = #signals + 1
|
|
local slots_to_filter
|
|
if item_slots > item_slots_capacity then
|
|
if comb then
|
|
signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item_slots_capacity*stack_size}
|
|
end
|
|
item_slots_capacity = 0
|
|
item_count = item_count - item_slots_capacity*stack_size
|
|
slots_to_filter = item_slots_capacity
|
|
else
|
|
if comb then
|
|
signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = item_count}
|
|
end
|
|
item_slots_capacity = item_slots_capacity - item_slots
|
|
do_inc = true
|
|
slots_to_filter = item_slots
|
|
end
|
|
for j = 1, slots_to_filter do
|
|
inv.set_filter(inv_filter_i, item.name)
|
|
inv_filter_i = inv_filter_i + 1
|
|
end
|
|
train.has_filtered_wagon = true
|
|
else
|
|
do_inc = true
|
|
end
|
|
if do_inc then
|
|
item_i = item_i + 1
|
|
if item_i <= #manifest then
|
|
item = manifest[item_i]
|
|
item_count = item.count
|
|
else
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if comb then
|
|
set_combinator_output(map_data, comb, signals)
|
|
end
|
|
end
|
|
elseif carriage.type == "fluid-wagon" and fluid_i <= #manifest then
|
|
local fluid_capacity = carriage.prototype.fluid_capacity
|
|
local signals = {}
|
|
|
|
while fluid_capacity > 0 do
|
|
local do_inc = false
|
|
if fluid.type == "fluid" then
|
|
if fluid_count > fluid_capacity then
|
|
if comb then
|
|
signals[1] = {index = 1, signal = {type = fluid.type, name = fluid.name}, count = fluid_capacity}
|
|
end
|
|
fluid_capacity = 0
|
|
fluid_count = fluid_count - fluid_capacity
|
|
else
|
|
if comb then
|
|
signals[1] = {index = 1, signal = {type = fluid.type, name = fluid.name}, count = item_count}
|
|
end
|
|
fluid_capacity = fluid_capacity - fluid_count
|
|
fluid_i = fluid_i + 1
|
|
if fluid_i <= #manifest then
|
|
fluid = manifest[fluid_i]
|
|
fluid_count = fluid.count
|
|
end
|
|
end
|
|
break
|
|
else
|
|
fluid_i = fluid_i + 1
|
|
if fluid_i <= #manifest then
|
|
fluid = manifest[fluid_i]
|
|
fluid_count = fluid.count
|
|
end
|
|
end
|
|
end
|
|
|
|
if comb then
|
|
set_combinator_output(map_data, comb, signals)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param map_data MapData
|
|
---@param station Station
|
|
---@param train Train
|
|
function set_r_wagon_combs(map_data, station, train)
|
|
if not station.wagon_combs then return end
|
|
local carriages = train.entity.carriages
|
|
|
|
local is_reversed = get_train_direction(station.entity_stop, train.entity)
|
|
|
|
local ivpairs = is_reversed and irpairs or ipairs
|
|
for carriage_i, carriage in ivpairs(carriages) do
|
|
---@type LuaEntity?
|
|
local comb = station.wagon_combs[carriage_i]
|
|
if comb and not comb.valid then
|
|
comb = nil
|
|
station.wagon_combs[carriage_i] = nil
|
|
if next(station.wagon_combs) == nil then
|
|
station.wagon_combs = nil
|
|
break
|
|
end
|
|
end
|
|
if comb and carriage.type == "cargo-wagon" then
|
|
local inv = carriage.get_inventory(defines.inventory.cargo_wagon)
|
|
if inv then
|
|
local signals = {}
|
|
for stack_i = 1, #inv do
|
|
local stack = inv[stack_i]
|
|
if stack.valid_for_read then
|
|
local i = #signals + 1
|
|
signals[i] = {index = i, signal = {type = stack.type, name = stack.name}, count = -stack.count}
|
|
end
|
|
end
|
|
set_combinator_output(map_data, comb, signals)
|
|
end
|
|
elseif comb and carriage.type == "fluid-wagon" then
|
|
local signals = {}
|
|
|
|
local inv = carriage.get_fluid_contents()
|
|
for fluid_name, count in pairs(inv) do
|
|
local i = #signals + 1
|
|
signals[i] = {index = i, signal = {type = "fluid", name = fluid_name}, count = -floor(count)}
|
|
end
|
|
set_combinator_output(map_data, comb, signals)
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param map_data MapData
|
|
---@param station Station
|
|
function unset_wagon_combs(map_data, station)
|
|
if not station.wagon_combs then return end
|
|
|
|
for i, comb in pairs(station.wagon_combs) do
|
|
if comb.valid then
|
|
set_combinator_output(map_data, comb, nil)
|
|
else
|
|
station.wagon_combs[i] = nil
|
|
end
|
|
end
|
|
if next(station.wagon_combs) == nil then
|
|
station.wagon_combs = nil
|
|
end
|
|
end
|
|
|
|
|
|
---@param map_data MapData
|
|
---@param station Station
|
|
---@param forbidden_entity LuaEntity?
|
|
function reset_station_layout(map_data, station, forbidden_entity)
|
|
--NOTE: station must be in auto mode
|
|
local station_rail = station.entity_stop.connected_rail
|
|
if station_rail == nil then
|
|
--cannot accept deliveries
|
|
station.layout_pattern = nil
|
|
station.accepted_layouts = {}
|
|
return
|
|
end
|
|
local rail_direction_from_station
|
|
if station.entity_stop.connected_rail_direction == defines.rail_direction.front then
|
|
rail_direction_from_station = defines.rail_direction.back
|
|
else
|
|
rail_direction_from_station = defines.rail_direction.front
|
|
end
|
|
local station_direction = station.entity_stop.direction
|
|
local surface = station.entity_stop.surface
|
|
local middle_x = station_rail.position.x
|
|
local middle_y = station_rail.position.y
|
|
local reach = LONGEST_INSERTER_REACH + 1
|
|
local search_area
|
|
local area_delta
|
|
local is_ver
|
|
if station_direction == defines.direction.north then
|
|
search_area = {left_top = {x = middle_x - reach, y = middle_y}, right_bottom = {x = middle_x + reach, y = middle_y + 6}}
|
|
area_delta = {x = 0, y = 7}
|
|
is_ver = true
|
|
elseif station_direction == defines.direction.east then
|
|
search_area = {left_top = {y = middle_y - reach, x = middle_x - 6}, right_bottom = {y = middle_y + reach, x = middle_x}}
|
|
area_delta = {x = -7, y = 0}
|
|
is_ver = false
|
|
elseif station_direction == defines.direction.south then
|
|
search_area = {left_top = {x = middle_x - reach, y = middle_y - 6}, right_bottom = {x = middle_x + reach, y = middle_y}}
|
|
area_delta = {x = 0, y = -7}
|
|
is_ver = true
|
|
elseif station_direction == defines.direction.west then
|
|
search_area = {left_top = {y = middle_y - reach, x = middle_x}, right_bottom = {y = middle_y + reach, x = middle_x + 6}}
|
|
area_delta = {x = 7, y = 0}
|
|
is_ver = false
|
|
else
|
|
assert(false, "cybersyn: invalid station direction")
|
|
end
|
|
local length = 2
|
|
local pre_rail = station_rail
|
|
local layout_pattern = {0}
|
|
local type_filter = {"inserter", "pump", "arithmetic-combinator"}
|
|
local wagon_number = 0
|
|
for i = 1, 112 do
|
|
local rail, rail_direction, rail_connection_direction = pre_rail.get_connected_rail({rail_direction = rail_direction_from_station, rail_connection_direction = defines.rail_connection_direction.straight})
|
|
if not rail or rail_connection_direction ~= defines.rail_connection_direction.straight or not rail.valid then
|
|
is_break = true
|
|
break
|
|
end
|
|
pre_rail = rail
|
|
length = length + 2
|
|
if length%7 <= 1 then
|
|
wagon_number = wagon_number + 1
|
|
local supports_cargo = false
|
|
local supports_fluid = false
|
|
local entities = surface.find_entities_filtered({
|
|
area = search_area,
|
|
type = type_filter,
|
|
})
|
|
for _, entity in pairs(entities) do
|
|
if entity.valid and entity ~= forbidden_entity then
|
|
if entity.type == "inserter" then
|
|
if not supports_cargo then
|
|
local pos = entity.pickup_position
|
|
local is_there
|
|
if is_ver then
|
|
is_there = middle_x - 1 <= pos.x and pos.x <= middle_x + 1
|
|
else
|
|
is_there = middle_y - 1 <= pos.y and pos.y <= middle_y + 1
|
|
end
|
|
if is_there then
|
|
supports_cargo = true
|
|
else
|
|
pos = entity.drop_position
|
|
if is_ver then
|
|
is_there = middle_x - 1 <= pos.x and pos.x <= middle_x + 1
|
|
else
|
|
is_there = middle_y - 1 <= pos.y and pos.y <= middle_y + 1
|
|
end
|
|
if is_there then
|
|
supports_cargo = true
|
|
end
|
|
end
|
|
end
|
|
elseif entity.type == "pump" then
|
|
if not supports_fluid and entity.pump_rail_target then
|
|
local direction = entity.direction
|
|
if is_ver then
|
|
if direction == defines.direction.east or direction == defines.direction.west then
|
|
supports_fluid = true
|
|
end
|
|
elseif direction == defines.direction.north or direction == defines.direction.south then
|
|
supports_fluid = true
|
|
end
|
|
end
|
|
elseif entity.name == COMBINATOR_NAME then
|
|
local param = map_data.to_comb_params[entity.unit_number]
|
|
if param.operation == OPERATION_WAGON_MANIFEST then
|
|
local pos = entity.position
|
|
local is_there
|
|
if is_ver then
|
|
is_there = middle_x - 2.1 <= pos.x and pos.x <= middle_x + 2.1
|
|
else
|
|
is_there = middle_y - 2.1 <= pos.y and pos.y <= middle_y + 2.1
|
|
end
|
|
if is_there then
|
|
if not station.wagon_combs then
|
|
station.wagon_combs = {}
|
|
end
|
|
station.wagon_combs[wagon_number] = entity
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if supports_cargo then
|
|
if supports_fluid then
|
|
layout_pattern[wagon_number] = 3
|
|
else
|
|
layout_pattern[wagon_number] = 1
|
|
end
|
|
elseif supports_fluid then
|
|
layout_pattern[wagon_number] = 2
|
|
else
|
|
--layout_pattern[wagon_number] = nil
|
|
end
|
|
search_area = area.move(search_area, area_delta)
|
|
end
|
|
end
|
|
station.layout_pattern = layout_pattern
|
|
for id, layout in pairs(map_data.layouts) do
|
|
station.accepted_layouts[id] = is_layout_accepted(layout_pattern, layout) or nil
|
|
end
|
|
end
|
|
|
|
---@param map_data MapData
|
|
---@param station Station
|
|
---@param forbidden_entity LuaEntity?
|
|
function update_station_if_auto(map_data, station, forbidden_entity)
|
|
if not station.allows_all_trains then
|
|
reset_station_layout(map_data, station, forbidden_entity)
|
|
end
|
|
end
|
|
|
|
---@param map_data MapData
|
|
---@param rail LuaEntity
|
|
---@param forbidden_entity LuaEntity?
|
|
---@param force boolean?
|
|
function update_station_from_rail(map_data, rail, forbidden_entity, force)
|
|
--NOTE: is this a correct way to figure out the direction?
|
|
---@type defines.rail_direction
|
|
local rail_direction = defines.rail_direction.back
|
|
local entity = rail.get_rail_segment_entity(rail_direction, false)
|
|
if not entity then
|
|
rail_direction = defines.rail_direction.front
|
|
entity = rail.get_rail_segment_entity(rail_direction, false)
|
|
end
|
|
for i = 1, 112 do
|
|
if not entity or not entity.valid then
|
|
return
|
|
end
|
|
if entity.name == "train-stop" then
|
|
local station = map_data.stations[entity.unit_number]
|
|
if station then
|
|
if force then
|
|
reset_station_layout(map_data, station, forbidden_entity)
|
|
else
|
|
update_station_if_auto(map_data, station, forbidden_entity)
|
|
end
|
|
end
|
|
return
|
|
end
|
|
|
|
rail = rail.get_connected_rail({rail_direction = rail_direction, rail_connection_direction = defines.rail_connection_direction.straight})--[[@as LuaEntity]]
|
|
if not rail or not rail.valid then
|
|
return
|
|
end
|
|
entity = rail.get_rail_segment_entity(rail_direction, false)
|
|
end
|
|
end
|
|
|
|
---@param map_data MapData
|
|
---@param pump LuaEntity
|
|
---@param forbidden_entity LuaEntity?
|
|
function update_station_from_pump(map_data, pump, forbidden_entity)
|
|
if pump.pump_rail_target then
|
|
update_station_from_rail(map_data, pump.pump_rail_target, forbidden_entity)
|
|
end
|
|
end
|
|
---@param map_data MapData
|
|
---@param inserter LuaEntity
|
|
---@param forbidden_entity LuaEntity?
|
|
function update_station_from_inserter(map_data, inserter, forbidden_entity)
|
|
local surface = inserter.surface
|
|
|
|
--NOTE: we don't use find_entity solely for miniloader compat
|
|
local rails = surface.find_entities_filtered({
|
|
type = "straight-rail",
|
|
position = inserter.pickup_position,
|
|
radius = 1,
|
|
})
|
|
if rails[1] then
|
|
update_station_from_rail(map_data, rails[1], forbidden_entity)
|
|
end
|
|
rails = surface.find_entities_filtered({
|
|
type = "straight-rail",
|
|
position = inserter.drop_position,
|
|
radius = 1,
|
|
})
|
|
if rails[1] then
|
|
update_station_from_rail(map_data, rails[1], forbidden_entity)
|
|
end
|
|
end
|