Skip to content

added dynamic way to add custom constructron#297

Open
andiargetlam wants to merge 1 commit intoILLISIS:mainfrom
andiargetlam:main
Open

added dynamic way to add custom constructron#297
andiargetlam wants to merge 1 commit intoILLISIS:mainfrom
andiargetlam:main

Conversation

@andiargetlam
Copy link
Copy Markdown

added dynamic way to add custom constructron

  • added on settings.lua an input field to add an entity name (spider-vehicle types) that you want to add as constructron -edited locale
    -edited final-fixes, control and job.lua to accomodate the dynamic custom constructron feature

added dynamic way to add custom constructron

- added on settings.lua an input field to add an entity name (spider-vehicle types) that you want to add as constructron
-edited locale
-edited final-fixes, control and job.lua to accomodate the dynamic custom constructron feature
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a dynamic mechanism for users to add custom spidertron-based constructrons via a startup mod setting, allowing comma-separated entity names to be used as bases for automatically generating constructron variants during the data stage.

Changes:

  • Adds a new string-setting in settings.lua for comma-separated custom constructron base entity names
  • Generates custom constructron entities, items, and recipes from those bases in data-final-fixes.lua, and registers them at runtime in control.lua with flying-detection logic
  • Replaces the previous hardcoded ctron_entities array in command_functions.lua with a proxy metatable that reads from storage.constructron_names dynamically

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
settings.lua Adds the new custom-constructron-base string startup setting
data-final-fixes.lua Generates entity, item, and recipe prototypes for each custom constructron base
control.lua Initializes storage.flying_constructrons, adds custom constructron names to storage.constructron_names at runtime
script/job.lua Replaces hardcoded "constructron-rocket-powered" name check with a storage.flying_constructrons lookup
script/command_functions.lua Replaces the static ctron_entities list with a dynamic proxy metatable
locale/en/locale.cfg Adds setting name/description; incidentally modifies several existing locale strings
Comments suppressed due to low confidence (1)

script/command_functions.lua:208

  • The ctron_entities proxy table is passed to find_entities_filtered as name = ctron_entities. The Factorio C-side API reads the name array using raw integer indexing (e.g. rawget(table, 1), rawget(table, 2), etc.), which bypasses Lua metamethods. Since the underlying table is empty ({}), the metamethods __index, __len, __pairs, and __ipairs are never invoked by Factorio's native code, and find_entities_filtered will receive an empty array of names. This means neither reacquire_ctrons nor recall_ctrons will find any constructrons. A simple function that returns a plain array from storage.constructron_names would be the correct approach here.
 local ctron_entities = setmetatable({}, {
        __pairs = function()
            local list = {}
            if storage and storage.constructron_names then
                for name in pairs(storage.constructron_names) do
                    if prototypes.entity[name] then table.insert(list, name) end
                end
            end
            return pairs(list)
        end,
        __ipairs = function()
            local list = {}
            if storage and storage.constructron_names then
                for name in pairs(storage.constructron_names) do
                    if prototypes.entity[name] then table.insert(list, name) end
                end
            end
            return ipairs(list)
        end,
        __index = function(_, key)
            local list = {}
            if storage and storage.constructron_names then
                for name in pairs(storage.constructron_names) do
                    if prototypes.entity[name] then table.insert(list, name) end
                end
            end
            return list[key]
        end,
        __len = function()
            local list = {}
            if storage and storage.constructron_names then
                for name in pairs(storage.constructron_names) do
                    if prototypes.entity[name] then table.insert(list, name) end
                end
            end
            return #list
        end
    })

me.reacquire_ctrons = function()
    for _, surface in pairs(game.surfaces) do
        storage.constructrons_count[surface.index] = 0
        local constructrons = surface.find_entities_filtered {
            name = ctron_entities,

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

cargo_jobs_button=Cargo Jobs

logistic_requests_label=Constructrons with requests: __1__
logistic_requests_label=Constructrons with requests: 1
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __1__ placeholder in logistic_requests_label has been replaced with the literal 1. This string is used with a runtime parameter via {"ctron_gui_locale.logistic_requests_label", ctron_count} (see script/ui_main_screen.lua:737). With the placeholder removed, the label will always show "Constructrons with requests: 1" instead of the actual count. The original __1__ placeholder must be restored: logistic_requests_label=Constructrons with requests: __1__.

Copilot uses AI. Check for mistakes.
job_job_minimap_label=Job location
job_job_minimap_tooltip=Click to view job location(s)
job_job_stat_tasks_label=Number of tasks: __1__
job_job_stat_tasks_label=Number of tasks: 1
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __1__ placeholder in job_job_stat_tasks_label has been replaced with the literal 1. This string is used with a runtime parameter via {"ctron_gui_locale.job_job_stat_tasks_label", (#job.task_positions or "0")} (see script/ui.lua:381 and script/ui_job_screen.lua:202). With the placeholder removed, the task count will always show "Number of tasks: 1" instead of the actual count. The original __1__ placeholder must be restored: job_job_stat_tasks_label=Number of tasks: __1__.

Copilot uses AI. Check for mistakes.
Comment on lines 43 to +65
storage.constructron_names = storage.constructron_names or { ["constructron"] = true, ["constructron-rocket-powered"] = true}
storage.flying_constructrons = storage.flying_constructrons or { ["constructron-rocket-powered"] = true }

-- Dynamically add custom constructron if configured
local custom_bases_string = settings.startup["custom-constructron-base"].value
if custom_bases_string and custom_bases_string ~= "" then
for custom_base_name in string.gmatch(custom_bases_string, '([^,]+)') do
custom_base_name = custom_base_name:match("^%s*(.-)%s*$") -- Trim whitespace
if custom_base_name and custom_base_name ~= "" then
local custom_ctron_name = custom_base_name .. "-constructron"
storage.constructron_names[custom_ctron_name] = true

-- Check if this custom constructron flies (no water collision)
local proto = prototypes.entity[custom_ctron_name]
if proto then
local layers = proto.collision_mask and (proto.collision_mask.layers or proto.collision_mask) or {}
if not layers["water_tile"] then
storage.flying_constructrons[custom_ctron_name] = true
end
end
end
end
end
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When ensure_storages is called via on_configuration_changed (e.g., when the setting is changed to remove a custom constructron), the or pattern on line 43 means that storage.constructron_names keeps its old value including any previously-registered custom entries. Because storage.constructron_names is never cleared of old custom constructron names, removing a name from the setting will not remove it from storage.constructron_names. Consequently, the mod will continue treating that entity as a valid constructron type even after removal. The custom constructron names should be rebuilt on every ensure_storages call to reflect the current settings, rather than relying on the persistent storage value.

Copilot uses AI. Check for mistakes.
-- Copy Entity
local new_entity = table.deepcopy(base_entity)
new_entity.name = custom_ctron_name
new_entity.minable.result = custom_ctron_name
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On line 24, new_entity.minable.result = custom_ctron_name will throw a Lua error if the base entity's minable field is absent (i.e., the entity has no minable properties). While standard Factorio spider-vehicles are all minable, some modded spider-vehicles might not be, or may have a different minable structure. A nil-check on new_entity.minable before assigning result would prevent a data-stage crash for such edge cases.

Copilot uses AI. Check for mistakes.
Comment on lines +165 to +202
local ctron_entities = setmetatable({}, {
__pairs = function()
local list = {}
if storage and storage.constructron_names then
for name in pairs(storage.constructron_names) do
if prototypes.entity[name] then table.insert(list, name) end
end
end
return pairs(list)
end,
__ipairs = function()
local list = {}
if storage and storage.constructron_names then
for name in pairs(storage.constructron_names) do
if prototypes.entity[name] then table.insert(list, name) end
end
end
return ipairs(list)
end,
__index = function(_, key)
local list = {}
if storage and storage.constructron_names then
for name in pairs(storage.constructron_names) do
if prototypes.entity[name] then table.insert(list, name) end
end
end
return list[key]
end,
__len = function()
local list = {}
if storage and storage.constructron_names then
for name in pairs(storage.constructron_names) do
if prototypes.entity[name] then table.insert(list, name) end
end
end
return #list
end
})
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The list-building logic (iterating storage.constructron_names and filtering by prototypes.entity[name]) is duplicated identically in all four metamethods (__pairs, __ipairs, __index, and __len). This should be extracted into a local helper function to avoid the repeated code and make future changes easier to maintain.

Copilot uses AI. Check for mistakes.
job_card_minion_name=Minion Job
job_status=Waiting for an available worker
chunk_delay=Starting in __1__ seconds
chunk_delay=Starting in 1 seconds
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __1__ placeholder in chunk_delay has been replaced with the literal 1. This string is used as a Factorio localised string with a runtime-substituted parameter via {"ctron_gui_locale.chunk_delay", (math.floor(time / 60))} (see script/ui.lua:346 and script/ui_main_screen.lua:546). With the placeholder removed, the countdown timer will always display "Starting in 1 seconds" regardless of the actual remaining time. The original __1__ placeholder must be restored: chunk_delay=Starting in __1__ seconds.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants