213 lines
8.2 KiB
Lua
213 lines
8.2 KiB
Lua
--- Tools for working with trains.
|
|
-- When this module is loaded into a mod, it automatically registers a number of new events in order to keep track of the trains as their locomotives and wagons are moved around.
|
|
-- <p>To handle the events, you should use the @{Event} module.
|
|
-- @module Trains
|
|
|
|
local Trains = {
|
|
_module_name = 'Trains'
|
|
}
|
|
setmetatable(Trains, {__index = require('stdlib/core')})
|
|
|
|
local Event = require('stdlib/event/event')
|
|
local Surface = require('stdlib/area/surface')
|
|
local Entity = require('stdlib/entity/entity')
|
|
|
|
--- This event fires when a train's ID changes.
|
|
-- <p>The train ID is a property of the main locomotive,
|
|
-- which means that when locomotives are attached or detached from their wagons or from other locomotives, the ID of the train changes.
|
|
-- <p>For example: A train with a front and rear locomotives will get its ID
|
|
-- from the front locomotive. If the front locomotive gets disconnected, the rear locomotive becomes the main one and the train's ID changes.
|
|
-- @event on_train_id_changed
|
|
-- @tparam uint old_id the ID of the train before the change
|
|
-- @tparam uint new_id the ID of the train after the change
|
|
-- @usage
|
|
---- Event.register(Trains.on_train_id_changed, my_handler)
|
|
Trains.on_train_id_changed = script.generate_event_name()
|
|
|
|
--- Given a @{criteria|search criteria}, search for trains that match the criteria.
|
|
-- * If ***criteria.surface*** is not supplied, this function searches through all existing surfaces.
|
|
-- * If ***criteria.force*** is not supplied, this function searches through all existing forces.
|
|
-- * If ***criteria.state*** is not supplied, this function gets trains in any @{defines.train_state|state}.
|
|
-- @tparam criteria criteria a table used to search for trains
|
|
-- @return (<span class="types">{@{train_details},...}</span>) an array of train IDs and LuaTrain instances
|
|
-- @usage
|
|
-- Trains.find_filtered({ surface = "nauvis", state = defines.train_state.wait_station })
|
|
function Trains.find_filtered(criteria)
|
|
criteria = criteria or {}
|
|
|
|
local surface_list = Surface.lookup(criteria.surface)
|
|
if criteria.surface == nil then
|
|
surface_list = game.surfaces
|
|
end
|
|
|
|
local results = {}
|
|
|
|
for _, surface in pairs(surface_list) do
|
|
local trains = surface.get_trains(criteria.force)
|
|
for _, train in pairs(trains) do
|
|
table.insert(results, train)
|
|
end
|
|
end
|
|
|
|
-- Apply state filters
|
|
if criteria.state then
|
|
results =
|
|
table.filter(
|
|
results,
|
|
function(train)
|
|
return train.state == criteria.state
|
|
end
|
|
)
|
|
end
|
|
|
|
-- Lastly, look up the train ids
|
|
results =
|
|
table.map(
|
|
results,
|
|
function(train)
|
|
return {train = train, id = Trains.get_main_locomotive(train).unit_number}
|
|
end
|
|
)
|
|
|
|
return results
|
|
end
|
|
|
|
---
|
|
-- This table should be passed into @{find_filtered} to find trains that match the criteria.
|
|
-- @tfield[opt] ?|nil|string|{string,...}|LuaSurface|{LuaSurface,...} surface the surfaces to look up for the trains
|
|
-- @tfield[opt] ?|nil|string|LuaForce force the force of the trains to search
|
|
-- @tfield[opt] ?|nil|defines.train_state state the state of the trains to search
|
|
-- @table criteria
|
|
|
|
---
|
|
-- @{find_filtered} returns an array with one or more of ***this*** table based on the @{criteria|search criteria}.
|
|
-- @tfield LuaTrain train an instance of the train
|
|
-- @tfield uint id the ID of the train
|
|
-- @table train_details
|
|
|
|
--- Find the ID of a LuaTrain instance.
|
|
-- @tparam LuaTrain train
|
|
-- @treturn uint the ID of the train
|
|
function Trains.get_train_id(train)
|
|
local loco = Trains.get_main_locomotive(train)
|
|
return loco and loco.unit_number
|
|
end
|
|
|
|
--- Event fired when some change happens to a locomotive.
|
|
-- @lfunction
|
|
function Trains._on_locomotive_changed()
|
|
-- For all the known trains
|
|
local renames = {}
|
|
for id, train in pairs(global._train_registry) do
|
|
-- Check if their known ID is the same as the LuaTrain's dervied id
|
|
local derived_id = Trains.get_train_id(train)
|
|
-- If it's not
|
|
if (id ~= derived_id) then
|
|
-- Capture the rename
|
|
table.insert(renames, {old_id = id, new_id = derived_id, train = train})
|
|
end
|
|
end
|
|
|
|
-- Go over the captured renaming operations
|
|
for _, renaming in pairs(renames) do
|
|
-- Rename it in the registry
|
|
-- and dispatch a renamed event
|
|
global._train_registry[renaming.new_id] = renaming.train
|
|
global._train_registry[renaming.old_id] = nil
|
|
|
|
local event_data = {
|
|
old_id = renaming.old_id,
|
|
new_id = renaming.new_id,
|
|
name = Trains.on_train_id_changed
|
|
}
|
|
Event.dispatch(event_data)
|
|
end
|
|
end
|
|
|
|
--- Get the main locomotive of a train.
|
|
-- @tparam LuaTrain train
|
|
-- @treturn LuaEntity the main locomotive
|
|
function Trains.get_main_locomotive(train)
|
|
if train and train.valid and train.locomotives and (#train.locomotives.front_movers > 0 or #train.locomotives.back_movers > 0) then
|
|
return train.locomotives.front_movers and train.locomotives.front_movers[1] or train.locomotives.back_movers[1]
|
|
end
|
|
end
|
|
|
|
--- Creates an entity from a train that is compatible with the @{Entity} module.
|
|
-- @tparam LuaTrain train
|
|
-- @return (<span class="types">@{train_entity}</span>)
|
|
function Trains.to_entity(train)
|
|
local name = 'train-' .. Trains.get_train_id(train)
|
|
return {
|
|
name = name,
|
|
valid = train.valid,
|
|
equals = function(entity)
|
|
return name == entity.name
|
|
end
|
|
}
|
|
end
|
|
|
|
------
|
|
-- @{to_entity} returns ***this*** table.
|
|
-- @tfield string name the name of the train entity with the train ID as its suffix
|
|
-- @tfield boolean valid whether or not if the train is in a valid state in the game
|
|
-- @tfield function equals — *function(entity)* — a function to check if another entity is equal to the train that ***this*** table represents
|
|
-- @table train_entity
|
|
|
|
--- Associates the user data to a train.
|
|
-- This is a helper around @{Entity.set_data}.
|
|
-- <p>The user data will be stored in the global object and it will persist between loads.
|
|
--> The user data will be removed from a train when the train becomes invalid.
|
|
-- @tparam LuaTrain train the train to set the user data for
|
|
-- @tparam ?|nil|Mixed data the user data to set, or nil to delete the user data associated with the train
|
|
-- @treturn ?|nil|Mixed the previous user data or nil if the train had no previous user data
|
|
function Trains.set_data(train, data)
|
|
return Entity.set_data(Trains.to_entity(train), data)
|
|
end
|
|
|
|
--- Gets the user data that is associated with a train.
|
|
-- This is a helper around @{Entity.get_data}.
|
|
-- <p>The user data is stored in the global object and it persists between loads.
|
|
--> The user data will be removed from a train when the train becomes invalid.
|
|
-- @tparam LuaTrain train the train to look up user data for
|
|
-- @treturn ?|nil|Mixed the user data, or nil if no user data exists for the train
|
|
function Trains.get_data(train)
|
|
return Entity.get_data(Trains.to_entity(train))
|
|
end
|
|
|
|
-- Creates a registry of known trains.
|
|
-- @return table a mapping of train id to LuaTrain object
|
|
function Trains.create_train_registry()
|
|
global._train_registry = global._train_registry or {}
|
|
|
|
local all_trains = Trains.find_filtered()
|
|
for _, trainInfo in pairs(all_trains) do
|
|
global._train_registry[tonumber(trainInfo.id)] = trainInfo.train
|
|
end
|
|
|
|
return global._train_registry
|
|
end
|
|
|
|
function Trains.on_train_created(event)
|
|
local train_id = Trains.get_train_id(event.train)
|
|
global._train_registry[train_id] = event.train
|
|
end
|
|
|
|
--- This needs to be called to register events for this module
|
|
-- @treturn Trains
|
|
function Trains.register_events()
|
|
require('stdlib/event/event')
|
|
-- When a locomotive is removed ...
|
|
local train_remove_events = {defines.events.on_entity_died, defines.events.on_pre_player_mined_item, defines.events.on_robot_pre_mined}
|
|
Event.register(train_remove_events, Event.filter_entity('entity', 'locomotive', Trains._on_locomotive_changed))
|
|
|
|
-- When a locomotive is added ...
|
|
Event.register(defines.events.on_train_created, Trains.on_train_created)
|
|
|
|
-- When the mod is initialized the first time
|
|
Event.register(Event.core_events.init_and_config, Trains.create_train_registry)
|
|
return Trains
|
|
end
|
|
|
|
return Trains
|