added dynamic way to add custom constructron#297
added dynamic way to add custom constructron#297andiargetlam wants to merge 1 commit intoILLISIS:mainfrom
Conversation
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
There was a problem hiding this comment.
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-settinginsettings.luafor 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 incontrol.luawith flying-detection logic - Replaces the previous hardcoded
ctron_entitiesarray incommand_functions.luawith a proxy metatable that reads fromstorage.constructron_namesdynamically
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_entitiesproxy table is passed tofind_entities_filteredasname = ctron_entities. The Factorio C-side API reads thenamearray 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__ipairsare never invoked by Factorio's native code, andfind_entities_filteredwill receive an empty array of names. This means neitherreacquire_ctronsnorrecall_ctronswill find any constructrons. A simple function that returns a plain array fromstorage.constructron_nameswould 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 |
There was a problem hiding this comment.
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__.
| 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 |
There was a problem hiding this comment.
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__.
| 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 |
There was a problem hiding this comment.
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.
| -- Copy Entity | ||
| local new_entity = table.deepcopy(base_entity) | ||
| new_entity.name = custom_ctron_name | ||
| new_entity.minable.result = custom_ctron_name |
There was a problem hiding this comment.
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.
| 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 | ||
| }) |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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.
added dynamic way to add custom constructron
-edited final-fixes, control and job.lua to accomodate the dynamic custom constructron feature