142 lines
6.2 KiB
Lua
142 lines
6.2 KiB
Lua
--[[ Copyright (c) 2017 Optera
|
|
* Part of Logistics Train Network
|
|
*
|
|
* See LICENSE.md in the project directory for license information.
|
|
--]]
|
|
|
|
|
|
---Finds the next logistic stop in the schedule of the given train. Returns nil if the train is not executing a delivery or has no further logistic stops in its schedule.
|
|
---@param train LuaTrain
|
|
---@param schedule_index integer? the index in the schedule to search from, `schedule.current` if omitted. Starts from the next position if the train is currently stopping at that station.
|
|
---@return integer? schedule_index the index of next logistic stop in the schedule or nil
|
|
---@return integer? id the unit_number of the logistic stop
|
|
---@return "provider"|"requester"|nil type
|
|
function GetNextLogisticStop(train, schedule_index)
|
|
if not (train and train.valid) then
|
|
if debug_log then log("(GetNextLogisticStop) train not valid") end
|
|
return
|
|
end
|
|
|
|
if not train.schedule then
|
|
if debug_log then log(format("(GetNextLogisticStop) train [%d] has no schedule.", train.id)) end
|
|
return
|
|
end
|
|
|
|
local delivery = global.Dispatcher.Deliveries[train.id]
|
|
if not delivery then
|
|
if debug_log then log(format("(GetNextLogisticStop) train [%d] not found in deliveries.", train.id)) end
|
|
return
|
|
end
|
|
|
|
local item = next(delivery.shipment)
|
|
if not item then
|
|
-- this can happen when the train was unable to load anything at the provider
|
|
if debug_log then log(format("(GetNextLogisticStop) train [%d] no longer has a shipment list.", train.id)) end
|
|
return
|
|
end
|
|
|
|
-- Comparing stop names is not enough to find the provider and the requester,
|
|
-- they might share names with each other or another stop in the schedule.
|
|
-- So use a heuristic that also looks at the wait conditions
|
|
local itype, iname = match(item, match_string)
|
|
local records = train.schedule.records
|
|
|
|
local record_index = schedule_index or train.schedule.current or 2 -- defaulting to 1 is pointless because that's the depot
|
|
if record_index == train.schedule.current and train.state == defines.train_state.wait_station then
|
|
record_index = record_index + 1
|
|
end
|
|
|
|
local function get_wait_count_comparator(record)
|
|
if record.wait_conditions then
|
|
for _, wait_condition in pairs(record.wait_conditions) do
|
|
local condition = wait_condition.condition
|
|
if condition and condition.constant and (wait_condition.type == "item_count" or wait_condition.type == "fluid_count") then
|
|
local signal = condition.first_signal
|
|
return signal and signal.type == itype and signal.name == iname and condition.comparator
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local record = records[record_index]
|
|
while record do
|
|
if record.station == delivery.from and get_wait_count_comparator(record) == "≥" then
|
|
return record_index, delivery.from_id, "provider"
|
|
end
|
|
if record.station == delivery.to and get_wait_count_comparator(record) == "=" then
|
|
return record_index, delivery.to_id, "requester"
|
|
end
|
|
|
|
record_index = record_index + 1
|
|
record = records[record_index]
|
|
end
|
|
end
|
|
|
|
local temp_wait_condition = {{type = "time", compare_type = "and", ticks = 0}}
|
|
|
|
---Ensures the next logistic stop in the schedule has a temporary stop if is on the same surface as the train.
|
|
---@param train LuaTrain
|
|
---@param schedule_index integer? the index in the schedule to search from, `schedule.current` if omitted. Starts from the next index if the train is currently stopping at that station.
|
|
---@return integer? stop_position index of created or existing temporary stop for next found logistic stop that was handled, nil if there is no further logistic stop or the next logistic stop is not on the same surface.
|
|
function GetOrCreateNextTempStop(train, schedule_index)
|
|
local stop_schedule_index, stop_id = GetNextLogisticStop(train, schedule_index)
|
|
if not stop_schedule_index then return end
|
|
|
|
--unlike ProcessDelivery we need to consider that the stop entity might be gone
|
|
local stop = global.LogisticTrainStops[stop_id]
|
|
if not stop or not stop.entity.valid then
|
|
if debug_log then log(format("(UpdateSchedule) skipping stop [%d] for train [%d], stop-entity not valid", stop_id, train.id)) end
|
|
return
|
|
end
|
|
|
|
local rail = stop.entity.connected_rail
|
|
local rail_direction = stop.entity.connected_rail_direction
|
|
if not rail or not rail_direction then
|
|
if debug_log then log(format("(UpdateSchedule) skipping stop [%d] for train [%d], not connected to a rail", stop_id, train.id)) end
|
|
return
|
|
end
|
|
|
|
-- the engine does not allow temp_stops on different surfaces
|
|
-- locomotive might not work here, a new train on another surface could still be incomplete
|
|
if train.carriages[1].surface ~= stop.entity.surface then
|
|
if debug_log then log(format("(UpdateSchedule) stop [%d] is on a different surface than train [%d]", stop_id, train.id)) end
|
|
return
|
|
end
|
|
|
|
-- insert temp stop in schedule
|
|
local schedule = train.schedule
|
|
local previous_record = schedule.records[stop_schedule_index-1]
|
|
if previous_record and previous_record.temporary then return stop_schedule_index-1 end -- schedule already up-to-date for stop_position
|
|
|
|
if debug_log then log(format("(UpdateSchedule) adding new temp-stop before stop [%d] at rail [%d] to train [%d] ", stop_id, rail.unit_number, train.id)) end
|
|
table.insert(schedule.records, stop_schedule_index, {
|
|
wait_conditions = temp_wait_condition,
|
|
rail = rail,
|
|
rail_direction = rail_direction,
|
|
temporary = true,
|
|
})
|
|
train.schedule = schedule
|
|
return stop_schedule_index
|
|
end
|
|
|
|
---reassigns an existing delivery from one train to another
|
|
---@param old_train_id integer
|
|
---@param new_train LuaTrain
|
|
---@return boolean reassigned true if the old train was executing a delivery, false otherwise
|
|
function ReassignDelivery(old_train_id, new_train)
|
|
-- check if delivery exists for given train id
|
|
if not (old_train_id and global.Dispatcher.Deliveries[old_train_id]) then
|
|
if debug_log then log(format("(ReassignDelivery) train [%d] not found in deliveries.", old_train_id)) end
|
|
return false
|
|
end
|
|
-- check if new train is valid
|
|
if not (new_train and new_train.valid and new_train.object_name == "LuaTrain") then
|
|
if debug_log then log("(ReassignDelivery) Received new_train was invalid.") end
|
|
return false
|
|
end
|
|
|
|
local delivery = Update_Delivery(old_train_id, new_train)
|
|
return delivery and true
|
|
end
|
|
|