Programmable Train Stops


This mod allows you to program train stop names with a vanilla signal, effectively allowing multi item stations.

Tweaks
1 year, 2 months ago
2.0
289
Transportation Trains Logistic network Circuit network

i Custom names

2 months ago

I would like to be able to have names like "<item-or-liquid> In 1-1-1", so I don't have the hard-to-see white arrows on the yellow background in the property window's name field and can use automatic train stop names with different train compositions (I also use 2-4-0 trains in my current save).
Optimally, I could enter a name pattern or just a postfix in a text field in the GUI where I currently check the dynamic name checkbox.

That's a good idea. Next time I work on this mod I'll add it.

19 hours ago

Hi,

Would it be possible to add a custom name pattern field for dynamic train stop names?

Right now the mod uses the detected signal and adds the pickup/dropoff arrow. That works, but for larger rail networks it would be very useful if we could define the final station name ourselves.

Example patterns:

{signal} 1-4 Receiver

{signal} 1-4 Provider

{signal} In 1-1-1

{signal} Out 2-4-0

Where {signal} would be replaced by the detected item/fluid/signal rich text icon.

Reason/use case:
I use Cybersyn. Cybersyn itself does not need the train stop name for routing, but I still use clear station names for readability. My naming scheme is normally:

<Item> <Train length> <Provider/Requester>

For example:

Iron Plate 1-4 Receiver

Copper Plate 2-4 Provider

A custom pattern field would allow the dynamic name to stay readable while also supporting different train lengths per station.

Possible simple UI idea:
In the Dynamic Name section, add a text field called “Name pattern” with a default like:

{signal}{arrow}

That would keep current behavior as the default, but allow users to change it per station to something like:

{signal} 1-4 Receiver

Optional extra tokens could be:

{signal} = detected signal icon
{arrow} = current up/down arrow
{direction} = pickup/dropoff text

Very rough code idea, not tested, but something like this:

function storageHelper.get_name_pattern(train_stop)
    if not train_stop or not train_stop.valid then
        return "{signal}{arrow}"
    end

    local data = storage.train_stop_data[train_stop.unit_number]
    if data and data.name_pattern and data.name_pattern ~= "" then
        return data.name_pattern
    end

    -- Default keeps current behavior
    return "{signal}{arrow}"
end

Then in signalProcessing.create_new_name_from_signal():

function signalProcessing.create_new_name_from_signal(signal, train_stop)
    if not signal or not signal.signal or not signal.signal.signal or not signal.signal.signal.name then
        return nil
    end

    local signal_name = signal.signal.signal.name
    local signalType = signalProcessing.get_signal_type(signal_name)

    if not signalType then
        return nil
    end

    local signal_icon = "[" .. signalType .. "=" .. signal_name .. "]"

    local dropoff_setting = storageHelper.is_dropoff_set(train_stop)
    local arrow = dropoff_setting and "[virtual-signal=down-arrow]" or "[virtual-signal=up-arrow]"
    local direction = dropoff_setting and "Receiver" or "Provider"

    local pattern = storageHelper.get_name_pattern(train_stop)

    pattern = string.gsub(pattern, "{signal}", signal_icon)
    pattern = string.gsub(pattern, "{arrow}", arrow)
    pattern = string.gsub(pattern, "{direction}", direction)

    return pattern
end

And in the GUI, maybe something like:

settings_frame.add {
    type = "textfield",
    name = "dynamic_name_pattern_textfield",
    caption = "Name pattern",
    text = storageHelper.get_name_pattern(train_stop)
}

Then store it when the text changes:

if event.element.name == "dynamic_name_pattern_textfield" then
    local player = game.players[event.player_index]
    local train_stop = player.opened

    if train_stop and train_stop.valid and train_stop.type == "train-stop" then
        if not storage.train_stop_data[train_stop.unit_number] then
            storage.train_stop_data[train_stop.unit_number] = {}
        end

        storage.train_stop_data[train_stop.unit_number].name_pattern = event.element.text
    end
end

This would make the mod much more flexible for Cybersyn/LTN-style multi-item stations and for bases using multiple train lengths.

19 hours ago

Small extra feature suggestion:

Would it also be possible to add a mod setting for whether Dynamic Name should be enabled by default for newly placed train stops?

Right now, Dynamic Name is off by default and has to be enabled manually per station. That is safe behavior, so I would keep the current default as disabled.

But for players building a base around this mod, especially with blueprints or many Cybersyn/LTN-style stations, it would be useful to have a setting like:

Enable Dynamic Name by default for new train stops: true/false

Default value could stay false, so existing behavior does not change.

Very rough code idea, not tested, but something like:

-- settings.lua

data:extend({
    {
        type = "bool-setting",
        name = "programmable-train-stop-default-enabled",
        setting_type = "runtime-global",
        default_value = false,
        order = "a"
    }
})

Then when a new train stop is initialized:

local default_enabled = settings.global["programmable-train-stop-default-enabled"].value

storage.train_stop_data[train_stop.unit_number] = {
    enableProgrammableName = default_enabled,
    dropoff_setting = true,
    name_pattern = "{signal}{arrow}"
}

if default_enabled then
    storageHelper.add_programmable_train_stop(train_stop)
end

And then hook that into newly built train stops:

local function initialize_train_stop(train_stop)
    if not train_stop or not train_stop.valid or train_stop.type ~= "train-stop" then
        return
    end

    if not storage.train_stop_data[train_stop.unit_number] then
        local default_enabled = settings.global["programmable-train-stop-default-enabled"].value

        storage.train_stop_data[train_stop.unit_number] = {
            enableProgrammableName = default_enabled,
            dropoff_setting = true,
            name_pattern = "{signal}{arrow}"
        }

        if default_enabled then
            storageHelper.add_programmable_train_stop(train_stop)
        end
    end
end

script.on_event(defines.events.on_built_entity, function(event)
    initialize_train_stop(event.entity)
end)

script.on_event(defines.events.on_robot_built_entity, function(event)
    initialize_train_stop(event.entity)
end)

script.on_event(defines.events.script_raised_built, function(event)
    initialize_train_stop(event.entity)
end)

This would be nice for blueprint-heavy bases, while still allowing the safe default behavior to remain unchanged for everyone else.

New response