mirror of
https://github.com/Xevion/project-cybersyn.git
synced 2025-12-07 03:16:06 -06:00
490 lines
16 KiB
Lua
490 lines
16 KiB
Lua
--By Mami
|
|
local min = math.min
|
|
local INF = math.huge
|
|
local btest = bit32.btest
|
|
|
|
---@param map_data MapData
|
|
---@param station Station
|
|
---@param manifest Manifest?
|
|
---@param sign int?
|
|
local function set_comb1(map_data, station, manifest, sign)
|
|
local comb = station.entity_comb1
|
|
if comb.valid then
|
|
if manifest then
|
|
local signals = {}
|
|
for i, item in ipairs(manifest) do
|
|
signals[i] = {index = i, signal = {type = item.type, name = item.name}, count = sign*item.count}
|
|
end
|
|
set_combinator_output(map_data, comb, signals)
|
|
else
|
|
set_combinator_output(map_data, comb, nil)
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param map_data MapData
|
|
---@param train_id uint
|
|
---@param train Train
|
|
function on_failed_delivery(map_data, train_id, train)
|
|
--NOTE: must either change this train's status or remove it after this call
|
|
local p_station_id = train.p_station_id--[[@as uint]]
|
|
local r_station_id = train.r_station_id--[[@as uint]]
|
|
local manifest = train.manifest--[[@as Manifest]]
|
|
local is_p_in_progress = train.status == STATUS_TO_P or train.status == STATUS_P
|
|
local is_r_in_progress = is_p_in_progress or train.status == STATUS_TO_R or train.status == STATUS_R
|
|
if is_p_in_progress then
|
|
local station = map_data.stations[p_station_id]
|
|
if station.entity_comb1.valid and (not station.entity_comb2 or station.entity_comb2.valid) then
|
|
remove_manifest(map_data, station, manifest, 1)
|
|
if train.status == STATUS_P then
|
|
set_comb1(map_data, station, nil)
|
|
unset_wagon_combs(map_data, station)
|
|
end
|
|
end
|
|
end
|
|
if is_r_in_progress then
|
|
local station = map_data.stations[r_station_id]
|
|
if station.entity_comb1.valid and (not station.entity_comb2 or station.entity_comb2.valid) then
|
|
remove_manifest(map_data, station, manifest, -1)
|
|
if train.status == STATUS_R then
|
|
set_comb1(map_data, station, nil)
|
|
unset_wagon_combs(map_data, station)
|
|
end
|
|
end
|
|
end
|
|
if train.has_filtered_wagon then
|
|
train.has_filtered_wagon = nil
|
|
for carriage_i, carriage in ipairs(train.entity.cargo_wagons) do
|
|
local inv = carriage.get_inventory(defines.inventory.cargo_wagon)
|
|
if inv then
|
|
---@type uint
|
|
for i = 1, inv.get_bar() - 1 do
|
|
inv.set_filter(i, nil)
|
|
end
|
|
inv.set_bar()
|
|
end
|
|
end
|
|
end
|
|
train.r_station_id = nil
|
|
train.p_station_id = nil
|
|
train.manifest = nil
|
|
interface_raise_train_failed_delivery(train_id, is_p_in_progress, p_station_id, is_r_in_progress, r_station_id, manifest)
|
|
end
|
|
|
|
|
|
|
|
---@param map_data MapData
|
|
---@param train_id uint
|
|
---@param train Train
|
|
function add_available_train(map_data, train_id, train)
|
|
if train.network_name then
|
|
local f, a
|
|
if train.network_name == NETWORK_EACH then
|
|
f, a = next, train.network_mask
|
|
else
|
|
f, a = once, train.network_name
|
|
end
|
|
for network_name in f, a do
|
|
local network = map_data.available_trains[network_name]
|
|
if not network then
|
|
network = {}
|
|
map_data.available_trains[network_name] = network
|
|
end
|
|
network[train_id] = true
|
|
end
|
|
train.is_available = true
|
|
interface_raise_train_available(train_id)
|
|
end
|
|
end
|
|
---@param map_data MapData
|
|
---@param mod_settings CybersynModSettings
|
|
---@param depot_id uint
|
|
---@param depot Depot
|
|
---@param train_id uint
|
|
---@param train Train
|
|
function add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
|
|
local comb = depot.entity_comb
|
|
if comb.valid then
|
|
set_train_from_comb(mod_settings, train, comb)
|
|
end
|
|
depot.available_train_id = train_id
|
|
train.depot_id = depot_id
|
|
train.status = STATUS_D
|
|
|
|
add_available_train(map_data, train_id, train)
|
|
end
|
|
---@param map_data MapData
|
|
---@param train_id uint
|
|
---@param train Train
|
|
function remove_available_train(map_data, train_id, train)
|
|
if train.is_available then
|
|
train.is_available = nil
|
|
local f, a
|
|
if train.network_name == NETWORK_EACH then
|
|
f, a = next, train.network_mask
|
|
else
|
|
f, a = once, train.network_name
|
|
end
|
|
for network_name in f, a do
|
|
local network = map_data.available_trains[network_name]
|
|
if network then
|
|
network[train_id] = nil
|
|
if next(network) == nil then
|
|
map_data.available_trains[network_name] = nil
|
|
end
|
|
end
|
|
end
|
|
local depot = map_data.depots[train.depot_id]
|
|
if depot.available_train_id == train_id then
|
|
depot.available_train_id = nil
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
|
|
|
|
---@param map_data MapData
|
|
---@param depot_id uint
|
|
---@param depot Depot
|
|
---@param train_entity LuaTrain
|
|
local function on_train_arrives_depot(map_data, depot_id, depot, train_entity)
|
|
local is_train_empty = next(train_entity.get_contents()) == nil and next(train_entity.get_fluid_contents()) == nil
|
|
local train_id = train_entity.id
|
|
local train = map_data.trains[train_id]
|
|
if train then
|
|
if train.status == STATUS_TO_D then
|
|
--shouldn't be possible to get train.status == STATUS_D
|
|
elseif train.status == STATUS_TO_D_BYPASS or train.status == STATUS_D then
|
|
remove_available_train(map_data, train_id, train)
|
|
elseif mod_settings.react_to_train_early_to_depot then
|
|
if train.manifest then
|
|
on_failed_delivery(map_data, train_id, train)
|
|
end
|
|
send_alert_unexpected_train(train.entity)
|
|
else
|
|
return
|
|
end
|
|
if is_train_empty or mod_settings.allow_cargo_in_depot then
|
|
local old_status = train.status
|
|
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
|
|
set_depot_schedule(train_entity, depot.entity_stop.backer_name)
|
|
interface_raise_train_status_changed(train_id, old_status, STATUS_D)
|
|
else
|
|
--train still has cargo
|
|
lock_train_to_depot(train_entity)
|
|
remove_train(map_data, train_id, train)
|
|
send_alert_nonempty_train_in_depot(map_data, train_entity)
|
|
end
|
|
elseif is_train_empty or mod_settings.allow_cargo_in_depot then
|
|
--NOTE: only place where new Train
|
|
train = {
|
|
entity = train_entity,
|
|
--layout_id = set_train_layout,
|
|
--item_slot_capacity = set_train_layout,
|
|
--fluid_capacity = set_train_layout,
|
|
--status = add_available_train_to_depot,
|
|
p_station_id = 0,
|
|
r_station_id = 0,
|
|
manifest = nil,
|
|
last_manifest_tick = map_data.total_ticks,
|
|
has_filtered_wagon = nil,
|
|
--is_available = add_available_train_to_depot,
|
|
--depot_id = add_available_train_to_depot,
|
|
--use_any_depot = add_available_train_to_depot,
|
|
--disable_bypass = add_available_train_to_depot,
|
|
--network_name = add_available_train_to_depot,
|
|
--network_mask = add_available_train_to_depot,
|
|
--priority = add_available_train_to_depot,
|
|
}--[[@as Train]]
|
|
set_train_layout(map_data, train)
|
|
map_data.trains[train_id] = train
|
|
add_available_train_to_depot(map_data, mod_settings, train_id, train, depot_id, depot)
|
|
|
|
set_depot_schedule(train_entity, depot.entity_stop.backer_name)
|
|
interface_raise_train_created(train_id, depot_id)
|
|
else
|
|
lock_train_to_depot(train_entity)
|
|
send_alert_nonempty_train_in_depot(map_data, train_entity)
|
|
end
|
|
if not is_train_empty then
|
|
interface_raise_train_nonempty_in_depot(depot_id, train_entity)
|
|
end
|
|
end
|
|
---@param map_data MapData
|
|
---@param station Station
|
|
---@param train_id uint
|
|
---@param train Train
|
|
local function on_train_arrives_station(map_data, station, train_id, train)
|
|
---@type uint
|
|
if train.status == STATUS_TO_P then
|
|
train.status = STATUS_P
|
|
set_comb1(map_data, station, train.manifest, mod_settings.invert_sign and 1 or -1)
|
|
set_p_wagon_combs(map_data, station, train)
|
|
interface_raise_train_status_changed(train_id, STATUS_TO_P, STATUS_P)
|
|
elseif train.status == STATUS_TO_R then
|
|
train.status = STATUS_R
|
|
set_comb1(map_data, station, train.manifest, mod_settings.invert_sign and -1 or 1)
|
|
set_r_wagon_combs(map_data, station, train)
|
|
interface_raise_train_status_changed(train_id, STATUS_TO_R, STATUS_R)
|
|
end
|
|
end
|
|
|
|
---@param map_data MapData
|
|
---@param refueler Refueler
|
|
---@param train_id uint
|
|
---@param train Train
|
|
local function on_train_arrives_refueler(map_data, refueler, train_id, train)
|
|
if train.status == STATUS_TO_F then
|
|
train.status = STATUS_F
|
|
set_refueler_combs(map_data, refueler, train)
|
|
interface_raise_train_status_changed(train_id, STATUS_TO_F, STATUS_F)
|
|
end
|
|
end
|
|
|
|
---@param map_data MapData
|
|
---@param mod_settings CybersynModSettings
|
|
---@param train_id uint
|
|
---@param train Train
|
|
local function on_train_leaves_stop(map_data, mod_settings, train_id, train)
|
|
if train.status == STATUS_P then
|
|
train.status = STATUS_TO_R
|
|
local station = map_data.stations[train.p_station_id]
|
|
remove_manifest(map_data, station, train.manifest, 1)
|
|
set_comb1(map_data, station, nil)
|
|
unset_wagon_combs(map_data, station)
|
|
if train.has_filtered_wagon then
|
|
train.has_filtered_wagon = nil
|
|
for carriage_i, carriage in ipairs(train.entity.cargo_wagons) do
|
|
local inv = carriage.get_inventory(defines.inventory.cargo_wagon)
|
|
if inv then
|
|
---@type uint
|
|
for i = 1, inv.get_bar() - 1 do
|
|
inv.set_filter(i, nil)
|
|
end
|
|
inv.set_bar()
|
|
end
|
|
end
|
|
end
|
|
interface_raise_train_status_changed(train_id, STATUS_P, STATUS_TO_R)
|
|
elseif train.status == STATUS_R then
|
|
local station = map_data.stations[train.r_station_id]
|
|
remove_manifest(map_data, station, train.manifest, -1)
|
|
set_comb1(map_data, station, nil)
|
|
unset_wagon_combs(map_data, station)
|
|
--complete delivery
|
|
train.p_station_id = nil
|
|
train.r_station_id = nil
|
|
train.manifest = nil
|
|
--add to available trains for depot bypass
|
|
local fuel_fill = 1
|
|
if mod_settings.fuel_threshold < 1 then
|
|
for _, v in pairs(train.entity.locomotives) do
|
|
for _, loco in pairs(v) do
|
|
local inv = loco.get_fuel_inventory()
|
|
if inv then
|
|
local inv_size = #inv
|
|
if inv_size > 0 then
|
|
local fuel_total = 0
|
|
---@type uint
|
|
for i = 1, inv_size do
|
|
local item = inv[i]
|
|
if item.valid_for_read then
|
|
fuel_total = fuel_total + item.count/get_stack_size(map_data, item.name)
|
|
end
|
|
end
|
|
fuel_fill = min(fuel_fill, fuel_total/inv_size)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if fuel_fill > mod_settings.fuel_threshold then
|
|
--if fuel_fill == 1, it's probably a modded electric train
|
|
if not train.disable_bypass then
|
|
train.status = STATUS_TO_D_BYPASS
|
|
add_available_train(map_data, train_id, train)
|
|
interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_D_BYPASS)
|
|
return
|
|
end
|
|
else
|
|
local f, a
|
|
if train.network_name == NETWORK_EACH then
|
|
f, a = next, train.network_mask
|
|
else
|
|
f, a = once, train.network_name
|
|
end
|
|
for network_name in f, a do
|
|
local refuelers = map_data.to_refuelers[network_name]
|
|
if refuelers then
|
|
local best_refueler_id = nil
|
|
local best_dist = INF
|
|
local best_prior = -INF
|
|
for id, _ in pairs(refuelers) do
|
|
local refueler = map_data.refuelers[id]
|
|
if not refueler.entity_stop.valid or not refueler.entity_comb.valid then
|
|
on_refueler_broken(map_data, id, refueler)
|
|
else
|
|
set_refueler_from_comb(map_data, mod_settings, id, refueler)
|
|
|
|
local refueler_network_mask = get_network_mask(refueler, network_name)
|
|
local train_network_mask = get_network_mask(train, network_name)
|
|
if btest(train_network_mask, refueler_network_mask) and (refueler.allows_all_trains or refueler.accepted_layouts[train.layout_id]) and refueler.trains_total < refueler.entity_stop.trains_limit then
|
|
if refueler.priority >= best_prior then
|
|
local t = get_any_train_entity(train.entity)
|
|
local dist = t and get_dist(t, refueler.entity_stop) or INF
|
|
if refueler.priority > best_prior or dist < best_dist then
|
|
best_refueler_id = id
|
|
best_dist = dist
|
|
best_prior = refueler.priority
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if best_refueler_id then
|
|
local refueler = map_data.refuelers[best_refueler_id]
|
|
if add_refueler_schedule(map_data, train.entity, refueler.entity_stop) then
|
|
train.status = STATUS_TO_F
|
|
train.refueler_id = best_refueler_id
|
|
refueler.trains_total = refueler.trains_total + 1
|
|
interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_F)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
--the train has not qualified for depot bypass nor refueling
|
|
train.status = STATUS_TO_D
|
|
interface_raise_train_status_changed(train_id, STATUS_R, STATUS_TO_D)
|
|
elseif train.status == STATUS_F then
|
|
local refueler = map_data.refuelers[train.refueler_id]
|
|
train.refueler_id = nil
|
|
refueler.trains_total = refueler.trains_total - 1
|
|
unset_wagon_combs(map_data, refueler)
|
|
if refueler.entity_comb.valid then
|
|
set_combinator_output(map_data, refueler.entity_comb, nil)
|
|
end
|
|
if not train.disable_bypass then
|
|
train.status = STATUS_TO_D_BYPASS
|
|
add_available_train(map_data, train_id, train)
|
|
else
|
|
train.status = STATUS_TO_D
|
|
end
|
|
interface_raise_train_status_changed(train_id, STATUS_F, train.status)
|
|
elseif train.status == STATUS_D then
|
|
--The train is leaving the depot without a manifest, the player likely intervened
|
|
remove_train(map_data, train_id, train)
|
|
end
|
|
end
|
|
|
|
|
|
---@param map_data MapData
|
|
---@param train_id uint
|
|
---@param train Train
|
|
function on_train_broken(map_data, train_id, train)
|
|
--NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0
|
|
if not train.se_is_being_teleported then
|
|
remove_train(map_data, train_id, train)
|
|
end
|
|
end
|
|
---@param map_data MapData
|
|
---@param pre_train_id uint
|
|
local function on_train_modified(map_data, pre_train_id)
|
|
local train = map_data.trains[pre_train_id]
|
|
--NOTE: train.entity is only absent if the train is climbing a space elevator as of 0.5.0
|
|
if train and not train.se_is_being_teleported then
|
|
remove_train(map_data, pre_train_id, train)
|
|
end
|
|
end
|
|
|
|
|
|
function on_train_built(event)
|
|
local train_e = event.train
|
|
if event.old_train_id_1 then
|
|
on_train_modified(storage, event.old_train_id_1)
|
|
end
|
|
if event.old_train_id_2 then
|
|
on_train_modified(storage, event.old_train_id_2)
|
|
end
|
|
end
|
|
function on_train_changed(event)
|
|
---@type MapData
|
|
local map_data = storage
|
|
local train_e = event.train--[[@as LuaTrain]]
|
|
if not train_e.valid then return end
|
|
local train_id = train_e.id
|
|
|
|
if map_data.active_alerts then
|
|
--remove the alert if the train is interacted with at all
|
|
local data = map_data.active_alerts[train_id]
|
|
if data then
|
|
--we need to wait for the train to come to a stop from being locked
|
|
if data[3] + 10*mod_settings.tps < map_data.total_ticks then
|
|
map_data.active_alerts[train_id] = nil
|
|
if next(map_data.active_alerts) == nil then
|
|
map_data.active_alerts = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if train_e.state == defines.train_state.wait_station then
|
|
local stop = train_e.station
|
|
if stop and stop.valid and stop.name == "train-stop" then
|
|
local id = stop.unit_number--[[@as uint]]
|
|
local depot = map_data.depots[id]
|
|
if depot then
|
|
if depot.entity_comb.valid and depot.entity_stop.valid then
|
|
on_train_arrives_depot(map_data, id, depot, train_e)
|
|
else
|
|
on_depot_broken(map_data, id, depot)
|
|
end
|
|
end
|
|
else
|
|
local train = map_data.trains[train_id]
|
|
if train then
|
|
local schedule = train_e.schedule
|
|
if schedule then
|
|
local rail = schedule.records[schedule.current].rail
|
|
if rail then
|
|
local id, station, is_station
|
|
if train.status == STATUS_TO_P then
|
|
id = train.p_station_id
|
|
station = map_data.stations[id]
|
|
is_station = true
|
|
elseif train.status == STATUS_TO_R then
|
|
id = train.r_station_id
|
|
station = map_data.stations[id]
|
|
is_station = true
|
|
elseif train.status == STATUS_TO_F then
|
|
id = train.refueler_id
|
|
station = map_data.refuelers[id]
|
|
is_station = false
|
|
end
|
|
if id and station.entity_stop.valid and station.entity_stop.connected_rail == rail then
|
|
if is_station then
|
|
if station.entity_comb1 and (not station.entity_comb2 or station.entity_comb2.valid) then
|
|
on_train_arrives_station(map_data, station, train_id, train)
|
|
end
|
|
elseif station.entity_comb.valid then
|
|
on_train_arrives_refueler(map_data, station, train_id, train)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
elseif event.old_state == defines.train_state.wait_station then
|
|
local path = train_e.path
|
|
if path and path.total_distance > 4 then
|
|
local train = map_data.trains[train_id]
|
|
if train then
|
|
on_train_leaves_stop(map_data, mod_settings, train_id, train)
|
|
end
|
|
end
|
|
end
|
|
end
|