296 lines
11 KiB
Lua
296 lines
11 KiB
Lua
--[[ Copyright (c) 2017 Optera
|
|
* Part of Logistics Train Network
|
|
*
|
|
* See LICENSE.md in the project directory for license information.
|
|
--]]
|
|
|
|
|
|
|
|
--create stop
|
|
function CreateStop(entity)
|
|
if global.LogisticTrainStops[entity.unit_number] then
|
|
if message_level >= 1 then printmsg({"ltn-message.error-duplicated-unit_number", entity.unit_number}, entity.force) end
|
|
if debug_log then log("(CreateStop) duplicate stop unit number "..entity.unit_number) end
|
|
return
|
|
end
|
|
local stop_offset = ltn_stop_entity_names[entity.name]
|
|
local posIn, posOut, rotOut, search_area
|
|
--log("Stop created at "..entity.position.x.."/"..entity.position.y..", orientation "..entity.direction)
|
|
if entity.direction == 0 then --SN
|
|
posIn = {entity.position.x + stop_offset, entity.position.y - 1}
|
|
posOut = {entity.position.x - 1 + stop_offset, entity.position.y - 1}
|
|
rotOut = 0
|
|
search_area = {
|
|
{entity.position.x + 0.001 - 1 + stop_offset, entity.position.y + 0.001 - 1},
|
|
{entity.position.x - 0.001 + 1 + stop_offset, entity.position.y - 0.001}
|
|
}
|
|
elseif entity.direction == 2 then --WE
|
|
posIn = {entity.position.x, entity.position.y + stop_offset}
|
|
posOut = {entity.position.x, entity.position.y - 1 + stop_offset}
|
|
rotOut = 2
|
|
search_area = {
|
|
{entity.position.x + 0.001, entity.position.y + 0.001 - 1 + stop_offset},
|
|
{entity.position.x - 0.001 + 1, entity.position.y - 0.001 + 1 + stop_offset}
|
|
}
|
|
elseif entity.direction == 4 then --NS
|
|
posIn = {entity.position.x - 1 - stop_offset, entity.position.y}
|
|
posOut = {entity.position.x - stop_offset, entity.position.y}
|
|
rotOut = 4
|
|
search_area = {
|
|
{entity.position.x + 0.001 - 1 - stop_offset, entity.position.y + 0.001},
|
|
{entity.position.x - 0.001 + 1 - stop_offset, entity.position.y - 0.001 + 1}
|
|
}
|
|
elseif entity.direction == 6 then --EW
|
|
posIn = {entity.position.x - 1, entity.position.y - 1 - stop_offset}
|
|
posOut = {entity.position.x - 1, entity.position.y - stop_offset}
|
|
rotOut = 6
|
|
search_area = {
|
|
{entity.position.x + 0.001 - 1, entity.position.y + 0.001 - 1 - stop_offset},
|
|
{entity.position.x - 0.001, entity.position.y - 0.001 + 1 - stop_offset}
|
|
}
|
|
else --invalid orientation
|
|
if message_level >= 1 then printmsg({"ltn-message.error-stop-orientation", tostring(entity.direction)}, entity.force) end
|
|
if debug_log then log("(CreateStop) invalid train stop orientation "..tostring(entity.direction) ) end
|
|
entity.destroy()
|
|
return
|
|
end
|
|
|
|
local input, output, lampctrl
|
|
-- handle blueprint ghosts and existing IO entities preserving circuit connections
|
|
local ghosts = entity.surface.find_entities(search_area)
|
|
for _,ghost in pairs (ghosts) do
|
|
if ghost.valid then
|
|
if ghost.name == "entity-ghost" then
|
|
if ghost.ghost_name == ltn_stop_input then
|
|
-- log("reviving ghost input at "..ghost.position.x..", "..ghost.position.y)
|
|
_, input = ghost.revive()
|
|
elseif ghost.ghost_name == ltn_stop_output then
|
|
-- log("reviving ghost output at "..ghost.position.x..", "..ghost.position.y)
|
|
_, output = ghost.revive()
|
|
elseif ghost.ghost_name == ltn_stop_output_controller then
|
|
-- log("reviving ghost lamp-control at "..ghost.position.x..", "..ghost.position.y)
|
|
_, lampctrl = ghost.revive()
|
|
end
|
|
-- something has built I/O already (e.g.) Creative Mode Instant Blueprint
|
|
elseif ghost.name == ltn_stop_input then
|
|
input = ghost
|
|
-- log("Found existing input at "..ghost.position.x..", "..ghost.position.y)
|
|
elseif ghost.name == ltn_stop_output then
|
|
output = ghost
|
|
-- log("Found existing output at "..ghost.position.x..", "..ghost.position.y)
|
|
elseif ghost.name == ltn_stop_output_controller then
|
|
lampctrl = ghost
|
|
-- log("Found existing lamp-control at "..ghost.position.x..", "..ghost.position.y)
|
|
end
|
|
end
|
|
end
|
|
|
|
if input == nil then -- create new
|
|
input = entity.surface.create_entity
|
|
{
|
|
name = ltn_stop_input,
|
|
|
|
position = posIn,
|
|
force = entity.force
|
|
}
|
|
end
|
|
input.operable = false -- disable gui
|
|
input.minable = false
|
|
input.destructible = false -- don't bother checking if alive
|
|
|
|
if lampctrl == nil then
|
|
lampctrl = entity.surface.create_entity
|
|
{
|
|
name = ltn_stop_output_controller,
|
|
position = {input.position.x + 0.45, input.position.y + 0.45}, -- slight offset so adjacent lamps won't connect
|
|
force = entity.force
|
|
}
|
|
-- log("building lamp-control at "..lampctrl.position.x..", "..lampctrl.position.y)
|
|
end
|
|
lampctrl.operable = false -- disable gui
|
|
lampctrl.minable = false
|
|
lampctrl.destructible = false -- don't bother checking if alive
|
|
|
|
-- connect lamp and control
|
|
lampctrl.get_control_behavior().parameters = {{index = 1, signal = {type="virtual",name="signal-white"}, count = 1 }}
|
|
input.connect_neighbour({target_entity=lampctrl, wire=defines.wire_type.green})
|
|
input.connect_neighbour({target_entity=lampctrl, wire=defines.wire_type.red})
|
|
input.get_or_create_control_behavior().use_colors = true
|
|
input.get_or_create_control_behavior().circuit_condition = {condition = {comparator=">",first_signal={type="virtual",name="signal-anything"}}}
|
|
|
|
if output == nil then -- create new
|
|
output = entity.surface.create_entity
|
|
{
|
|
name = ltn_stop_output,
|
|
position = posOut,
|
|
direction = rotOut,
|
|
force = entity.force
|
|
}
|
|
end
|
|
output.operable = false -- disable gui
|
|
output.minable = false
|
|
output.destructible = false -- don't bother checking if alive
|
|
|
|
-- enable reading contents and sending signals to trains
|
|
entity.get_or_create_control_behavior().send_to_train = true
|
|
entity.get_or_create_control_behavior().read_from_train = true
|
|
|
|
global.LogisticTrainStops[entity.unit_number] = {
|
|
entity = entity,
|
|
input = input,
|
|
output = output,
|
|
lamp_control = lampctrl,
|
|
parked_train = nil,
|
|
parked_train_id = nil,
|
|
active_deliveries = {}, --delivery IDs to/from stop
|
|
error_code = -1, --key to error_codes table
|
|
is_depot = false,
|
|
depot_priority = 0,
|
|
network_id = default_network,
|
|
min_carriages = 0,
|
|
max_carriages = 0,
|
|
max_trains = 0,
|
|
requesting_threshold = min_requested,
|
|
requesting_threshold_stacks = 0,
|
|
requester_priority = 0,
|
|
no_warnings = false,
|
|
providing_threshold = min_provided,
|
|
providing_threshold_stacks = 0,
|
|
provider_priority = 0,
|
|
locked_slots = 0,
|
|
}
|
|
UpdateStopOutput(global.LogisticTrainStops[entity.unit_number])
|
|
|
|
-- register events
|
|
-- script.on_event(defines.events.on_tick, OnTick)
|
|
script.on_nth_tick(nil)
|
|
script.on_nth_tick(dispatcher_nth_tick, OnTick)
|
|
script.on_event(defines.events.on_train_changed_state, OnTrainStateChanged)
|
|
script.on_event(defines.events.on_train_created, OnTrainCreated)
|
|
if debug_log then log("(OnEntityCreated) on_nth_tick("..dispatcher_nth_tick.."), on_train_changed_state, on_train_created registered") end
|
|
end
|
|
|
|
function OnEntityCreated(event)
|
|
local entity = event.created_entity or event.entity or event.destination
|
|
if not entity or not entity.valid then return end
|
|
|
|
if ltn_stop_entity_names[entity.name] then
|
|
CreateStop(entity)
|
|
end
|
|
end
|
|
|
|
|
|
-- stop removed
|
|
function RemoveStop(stopID, create_ghosts)
|
|
local stop = global.LogisticTrainStops[stopID]
|
|
|
|
-- clean lookup tables
|
|
for k,v in pairs(global.StopDistances) do
|
|
if k:find(stopID) then
|
|
global.StopDistances[k] = nil
|
|
end
|
|
end
|
|
|
|
-- remove available train
|
|
if stop and stop.is_depot and stop.parked_train_id and global.Dispatcher.availableTrains[stop.parked_train_id] then
|
|
global.Dispatcher.availableTrains_total_capacity = global.Dispatcher.availableTrains_total_capacity - global.Dispatcher.availableTrains[stop.parked_train_id].capacity
|
|
global.Dispatcher.availableTrains_total_fluid_capacity = global.Dispatcher.availableTrains_total_fluid_capacity - global.Dispatcher.availableTrains[stop.parked_train_id].fluid_capacity
|
|
global.Dispatcher.availableTrains[stop.parked_train_id] = nil
|
|
end
|
|
|
|
-- destroy IO entities, broken IO entities should be sufficiently handled in initializeTrainStops()
|
|
if stop then
|
|
if stop.input and stop.input.valid then
|
|
if create_ghosts then
|
|
stop.input.destructible = true
|
|
stop.input.die()
|
|
else
|
|
stop.input.destroy()
|
|
end
|
|
end
|
|
if stop.output and stop.output.valid then
|
|
if create_ghosts then
|
|
stop.output.destructible = true
|
|
stop.output.die()
|
|
else
|
|
stop.output.destroy()
|
|
end
|
|
end
|
|
if stop.lamp_control and stop.lamp_control.valid then stop.lamp_control.destroy() end
|
|
end
|
|
|
|
global.LogisticTrainStops[stopID] = nil
|
|
|
|
if not next(global.LogisticTrainStops) then
|
|
-- reset tick indexes
|
|
global.tick_state = 0
|
|
global.tick_stop_index = nil
|
|
global.tick_request_index = nil
|
|
|
|
-- unregister events
|
|
script.on_nth_tick(nil)
|
|
script.on_event(defines.events.on_train_changed_state, nil)
|
|
script.on_event(defines.events.on_train_created, nil)
|
|
if debug_log then log("(OnEntityRemoved) Removed last LTN Stop: on_nth_tick, on_train_changed_state, on_train_created unregistered") end
|
|
end
|
|
end
|
|
|
|
function OnEntityRemoved(event, create_ghosts)
|
|
local entity = event.entity
|
|
if not entity or not entity.valid then return end
|
|
|
|
if entity.train then
|
|
local trainID = entity.train.id
|
|
-- remove from stop if parked
|
|
if global.StoppedTrains[trainID] then
|
|
TrainLeaves(trainID)
|
|
end
|
|
-- removing any carriage fails a delivery
|
|
-- otherwise I'd have to handle splitting and merging a delivery across train parts
|
|
local delivery = global.Dispatcher.Deliveries[trainID]
|
|
if delivery then
|
|
script.raise_event(on_delivery_failed_event, {train_id = trainID, shipment = delivery.shipment})
|
|
RemoveDelivery(trainID)
|
|
end
|
|
|
|
elseif ltn_stop_entity_names[entity.name] then
|
|
RemoveStop(entity.unit_number, create_ghosts)
|
|
end
|
|
end
|
|
|
|
--rename stop
|
|
local function renamedStop(targetID, old_name, new_name)
|
|
-- find identical stop names
|
|
local duplicateName = false
|
|
local renameDeliveries = true
|
|
for stopID, stop in pairs(global.LogisticTrainStops) do
|
|
if not stop.entity.valid or not stop.input.valid or not stop.output.valid or not stop.lamp_control.valid then
|
|
RemoveStop(stopID)
|
|
elseif stop.entity.backer_name == old_name then
|
|
renameDeliveries = false
|
|
end
|
|
end
|
|
-- rename deliveries only if no other LTN stop old_name exists
|
|
if renameDeliveries then
|
|
if debug_log then log("(OnEntityRenamed) last LTN stop "..old_name.." renamed, updating deliveries to "..new_name..".") end
|
|
for trainID, delivery in pairs(global.Dispatcher.Deliveries) do
|
|
if delivery.to == old_name then
|
|
delivery.to = new_name
|
|
end
|
|
if delivery.from == old_name then
|
|
delivery.from = new_name
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
script.on_event(defines.events.on_entity_renamed, function(event)
|
|
local uid = event.entity.unit_number
|
|
local oldName = event.old_name
|
|
local newName = event.entity.backer_name
|
|
if ltn_stop_entity_names[event.entity.name] then
|
|
renamedStop(uid, oldName, newName)
|
|
end
|
|
end)
|
|
|