diff --git a/control.lua b/control.lua index 93bb59b..0cf3e5e 100644 --- a/control.lua +++ b/control.lua @@ -388,7 +388,7 @@ script.on_event(ev.on_lua_shortcut, function (event) end end) ----@param event EventData.on_custom_input +---@param event EventData.CustomInputEvent script.on_event("ctron-get-selection-tool", function (event) local name = event.input_name if name ~= "ctron-get-selection-tool" then return end diff --git a/data/collision-mask-util-extended.lua b/data/collision-mask-util-extended.lua index 96f215f..7e41cd3 100644 --- a/data/collision-mask-util-extended.lua +++ b/data/collision-mask-util-extended.lua @@ -1,4 +1,5 @@ -- luacheck: ignore +---@diagnostic disable: missing-fields, unused-local, unused-function -- File Licence: You can copy and distribute this file regardless of the mod's general licence. This licence exception does not affect other files of this mod. local collision_mask_util_extended = require("__core__/lualib/collision-mask-util") diff --git a/data/compatibility/nullius.lua b/data/compatibility/nullius.lua index 0b3acb2..01b738c 100644 --- a/data/compatibility/nullius.lua +++ b/data/compatibility/nullius.lua @@ -9,7 +9,9 @@ station_item.order = "nullius-z" .. station_item.order local ctron_recipe = data.raw.recipe["constructron"] local station_recipe = data.raw.recipe["service_station"] +---@diagnostic disable-next-line: inject-field ctron_recipe.category = "huge-crafting" +---@diagnostic disable-next-line: inject-field station_recipe.category = "large-crafting" ctron_recipe.order = ctron_item.order station_recipe.order = station_item.order diff --git a/data/constructron_pathing_proxy.lua b/data/constructron_pathing_proxy.lua index 85edf39..cd52acc 100644 --- a/data/constructron_pathing_proxy.lua +++ b/data/constructron_pathing_proxy.lua @@ -133,7 +133,7 @@ for _, type in pairs(types) do local collision_box = prototype.collision_box local width = collision_box[2][1] - collision_box[1][1] local height = collision_box[2][2] - collision_box[1][2] - -- Check if entity is larger than 7x7 tiles + -- large entities: >7 tiles in one dimension and >4 in the other if (width>7 and height>4) or (height>7 and width>4) then -- log("Entity '" .. name .. "' is larger than 7x7 tiles. Size: " .. width .. "x" .. height) local add = false @@ -173,6 +173,7 @@ local template_entity = { icon_mipmaps = 0, flags = {"placeable-neutral", "not-on-map"}, order = "z", + hidden = true, max_health = 1, render_layer = "object", collision_mask = pathing_collision_mask, diff --git a/data/selection_tool.lua b/data/selection_tool.lua index 628e522..c65711d 100644 --- a/data/selection_tool.lua +++ b/data/selection_tool.lua @@ -4,6 +4,7 @@ data:extend( type = "selection-tool", name = "ctron-selection-tool", icons = {{ icon = "__Constructron-Continued__/graphics/icon_texture.png", icon_size = 128, mipmap_count = 2 }}, + hidden = true, always_include_tiles = true, skip_fog_of_war = false, stack_size = 1, @@ -36,7 +37,7 @@ data:extend( reverse_select = { border_color = { r = 1 }, -- red cursor_box_type = "entity", - mode = { "deconstruct", "items", "trees" } + mode = { "deconstruct", "items", "trees", "any-tile" } }, -- shift right click @@ -48,4 +49,4 @@ data:extend( }, } } -) \ No newline at end of file +) diff --git a/data/station_combinator.lua b/data/station_combinator.lua index c1ffaae..9099eb9 100644 --- a/data/station_combinator.lua +++ b/data/station_combinator.lua @@ -5,5 +5,6 @@ station_combinator.selectable_in_game = false station_combinator.item_slot_count = 100 station_combinator.draw_circuit_wires = false station_combinator.flags = { "hide-alt-info", "not-blueprintable", "not-on-map", } -station_combinator.collision_mask = { layers = {}} -data:extend({station_combinator}) \ No newline at end of file +station_combinator.collision_mask = { layers = {} } +station_combinator.hidden = true +data:extend({station_combinator}) diff --git a/info.json b/info.json index 6e9500e..d855ffe 100644 --- a/info.json +++ b/info.json @@ -7,7 +7,7 @@ "description": "A spidertron that automatically moves, constructs, upgrades and/or deconstructs entities of any size, anywhere on the map with items from your logistic network for the ultimate fire and forget automation experience and unmatched scaling potential.", "factorio_version": "2.0", "dependencies": [ - "base >= 1.1.77", + "base >= 2.0.0", "! Constructron", "(?) space-exploration", "(?) Krastorio2", diff --git a/locale/en/locale.cfg b/locale/en/locale.cfg index a5f8749..b0cf9eb 100644 --- a/locale/en/locale.cfg +++ b/locale/en/locale.cfg @@ -187,4 +187,5 @@ station_no_exist=This station no longer exists. item_already_requested=This item is already requested on this station. job_no_exist=This job no longer exists. job_finished=Job is already finishing. -invalid_ammo=Invalid ammo selection. \ No newline at end of file +invalid_ammo=Invalid ammo selection. +no_requests_copied=No station requests have been copied yet. \ No newline at end of file diff --git a/migrations/Constructron-Continued_2.0.9.lua b/migrations/Constructron-Continued_2.0.9.lua index 2ba5003..62d57d3 100644 --- a/migrations/Constructron-Continued_2.0.9.lua +++ b/migrations/Constructron-Continued_2.0.9.lua @@ -11,7 +11,7 @@ for _, job in pairs(storage.jobs or {}) do end end end - local logistic_point = job.worker.get_logistic_point(0) + local logistic_point = job.worker and job.worker.valid and job.worker.get_logistic_point(0) if logistic_point then local section = logistic_point.get_section(1) if section then diff --git a/script/entity_processor.lua b/script/entity_processor.lua index c7de964..a516485 100644 --- a/script/entity_processor.lua +++ b/script/entity_processor.lua @@ -160,7 +160,7 @@ script.on_event(ev.on_robot_built_entity, entity_proc.on_built_entity, { script.on_event(ev.on_post_entity_died, function(event) local ghost_entity = event.ghost ---@cast ghost_entity -nil if not (ghost_entity and ghost_entity.valid) then return end - if not ghost_entity.force.name == "player" then return end + if ghost_entity.force.name ~= "player" then return end local surface_index = ghost_entity.surface.index if storage.rebuild_job_toggle[surface_index] and (ghost_entity.type == 'entity-ghost') then entity_proc.create_chunk(ghost_entity, storage.construction_queue[surface_index], surface_index) @@ -179,7 +179,7 @@ end, {{filter = "type", type = "fish", invert = true, mode = "or"}}) script.on_event(ev.on_marked_for_upgrade, function(event) local entity = event.entity local surface_index = entity.surface.index - if not storage.upgrade_job_toggle[surface_index] or not entity or not entity.force.name == "player" then return end + if not storage.upgrade_job_toggle[surface_index] or entity.force.name ~= "player" then return end entity_proc.create_chunk(entity, storage.upgrade_queue[surface_index], surface_index) end) @@ -273,7 +273,7 @@ script.on_event(ev.script_raised_teleported, function(event) storage.managed_surfaces[surface_index] = entity.surface.name end elseif storage.station_names[entity.name] then - storage.stations_count[event.old_surface_index] = storage.stations_count[event.old_surface_index] - 1 + storage.stations_count[event.old_surface_index] = math.max((storage.stations_count[event.old_surface_index] or 0) - 1, 0) storage.stations_count[surface_index] = storage.stations_count[surface_index] + 1 -- configure surface management if (storage.constructrons_count[surface_index] > 0) then @@ -314,7 +314,7 @@ entity_proc.on_object_destroyed = function(event) util_func.update_ctron_combinator_signals(removed_entity.surface) elseif storage.station_names[removed_entity.name] then if game.surfaces[surface_index] then - storage.stations_count[surface_index] = storage.stations_count[surface_index] - 1 + storage.stations_count[surface_index] = math.max((storage.stations_count[surface_index] or 0) - 1, 0) end storage.service_stations[event.useful_id] = nil -- surface management @@ -330,6 +330,11 @@ entity_proc.on_object_destroyed = function(event) util_func.setup_station_combinator(new_station) end end + for _, job in pairs(storage.jobs) do + if job.worker and job.worker.valid == false then job.worker = nil end + if job.station and job.station.valid == false then job.station = nil end + if job.destination_station and job.destination_station.valid == false then job.destination_station = nil end + end storage.registered_entities[event.registration_number] = nil end diff --git a/script/job.lua b/script/job.lua index 247bf41..6f9d9f3 100644 --- a/script/job.lua +++ b/script/job.lua @@ -1,7 +1,7 @@ local util_func = require("script/utility_functions") local debug_lib = require("script/debug_lib") local pathfinder = require("script/pathfinder") -local entity_proc = require("entity_processor") +local entity_proc = require("script/entity_processor") -- class for jobs local job = {} ---@class job @@ -180,7 +180,7 @@ local find_entities = { } function job:check_roboport_coverage(entity) - local networks = entity.surface.find_logistic_networks_by_construction_area(entity.position, game.players[1].force) + local networks = entity.surface.find_logistic_networks_by_construction_area(entity.position, game.forces["player"]) if next(networks) then return true end return false end @@ -228,7 +228,7 @@ function job:claim_chunk(chunk) -- check for chunk overload local total_required_slots = util_func.calculate_required_inventory_slot_count(util_func.combine_tables { self.required_items, chunk.required_items, chunk.trash_items }) if total_required_slots > self.empty_slot_count then - local divisor = math.ceil(total_required_slots / (self.empty_slot_count - 5)) -- minus 5 slots to account for rounding up of items and trash + local divisor = math.ceil(total_required_slots / math.max(1, self.empty_slot_count - 5)) -- minus 5 slots to account for rounding up of items and trash for item, value in pairs(chunk.required_items) do for quality, count in pairs(value) do chunk.required_items[item] = { @@ -772,7 +772,7 @@ function job:check_items_are_available() local logistic_network = self.station.logistic_network for request_name, value in pairs(current_requests) do for quality, count in pairs(value) do - if logistic_network.can_satisfy_request({name = request_name, quality = quality}, count, true) then + if count > 0 and logistic_network.can_satisfy_request({name = request_name, quality = quality}, count, true) then return true end end @@ -801,11 +801,9 @@ function job:check_roaming_candidate(station, current_items, current_requests) if not station_logistic_network then return false end for request_name, value in pairs(current_requests) do for quality, count in pairs(value) do - local needed_count = count - if current_items[request_name] and current_items[request_name][quality] then - needed_count = count - current_items[request_name][quality] - end - if station_logistic_network.can_satisfy_request({name = request_name, quality = quality}, needed_count, true) then + local held = (current_items[request_name] and current_items[request_name][quality]) or 0 + local needed_count = count - held + if needed_count > 0 and station_logistic_network.can_satisfy_request({name = request_name, quality = quality}, needed_count, true) then debug_lib.VisualDebugText({"ctron_status.trying_diff_station"}, self.worker, 0, 5) self.sub_state = nil self.station = station @@ -1013,12 +1011,13 @@ function job:check_ammo_count() local ammo = storage.ammo_name[self.surface_index] local ammo_name = ammo.name local ammo_quality = ammo.quality - -- Check if the current ammo count is more than 25% of the expected ammo count. - -- If the ammo count is less than or equal to 25%, update the job state to "starting" and return false. - if not (ammunition and ammunition[ammo_name] and (ammunition[ammo_name][ammo_quality] > (math.ceil(ammo_count * 25 / 100)))) then - -- Additional check in worker inventory for extra ammo + -- start a restock if less than 25% of the expected ammo is present, in either the slots or the trunk + local threshold = math.ceil(ammo_count * 25 / 100) + local ammo_in_slots = ammunition[ammo_name] and ammunition[ammo_name][ammo_quality] + if not (ammo_in_slots and ammo_in_slots > threshold) then local inventory_ammo = util_func.convert_to_item_list(self.worker_inventory.get_contents()) - if not (inventory_ammo and inventory_ammo[ammo_name] and (inventory_ammo[ammo_name][ammo_quality] > (math.ceil(ammo_count * 25 / 100)))) then + local ammo_in_inventory = inventory_ammo[ammo_name] and inventory_ammo[ammo_name][ammo_quality] + if not (ammo_in_inventory and ammo_in_inventory > threshold) then self.state = "starting" return false end @@ -1188,7 +1187,8 @@ function job:finishing() util_func.set_constructron_status(worker, 'busy', false) -- change selected constructron colour util_func.paint_constructron(worker, 'idle') - + -- clear autopilot_destination (for minion jobs) + worker.autopilot_destination = nil -- release vassals for _, vassal_job in pairs((self.vassal_jobs or {})) do vassal_job.state = "finishing" diff --git a/script/job/cargo.lua b/script/job/cargo.lua index ed4e549..9eef12d 100644 --- a/script/job/cargo.lua +++ b/script/job/cargo.lua @@ -38,7 +38,7 @@ function cargo_job.prospect_cargo_jobs() if not next(items_to_fullfill) then goto continue end -- top up all items at the station for id, request in pairs(requests) do - local current_count = station.logistic_network.get_item_count(request.name) + local current_count = station.logistic_network.get_item_count(request) if ((current_count + request.in_transit_count) < request.max) then if not (items_to_fullfill[request.name] and items_to_fullfill[request.name][request.quality]) then -- ignore if already in the list local total = request.max - current_count - request.in_transit_count @@ -140,7 +140,7 @@ function cargo_job:setup() local total_required_slots = util_func.calculate_required_inventory_slot_count(self.required_items) if total_required_slots > self.empty_slot_count then -- job will not fit in one constructron, split the job to use multiple constructrons - local divisor = math.ceil(total_required_slots / (self.empty_slot_count - 1)) + local divisor = math.ceil(total_required_slots / math.max(1, self.empty_slot_count - 1)) for item, value in pairs(self.required_items) do for quality, count in pairs(value) do self.required_items[item][quality] = math.ceil(count / divisor) @@ -152,7 +152,7 @@ function cargo_job:setup() storage.cargo_queue[self.surface_index][storage.cargo_index] = { index = storage.cargo_index, station = self.destination_station, - items = self.required_items + items = table.deepcopy(self.required_items) } end cargo_job.process_cargo_job_queue(self.surface_index) @@ -227,7 +227,7 @@ function cargo_job:deliver_items() for item, value in pairs(self.required_items) do for quality, count in pairs(value) do if (request.name == item) and (request.quality == quality) then - request.in_transit_count = request.in_transit_count - count + request.in_transit_count = math.max(0, request.in_transit_count - count) section.set_slot(slot, { value = { name = item, diff --git a/script/job/destroy.lua b/script/job/destroy.lua index ffc065e..6684426 100644 --- a/script/job/destroy.lua +++ b/script/job/destroy.lua @@ -57,7 +57,7 @@ function destroy_job:setup() local repair_tool = storage.repair_tool_name[self.surface_index] self.required_items[repair_tool.name] = { [repair_tool.quality] = 16} -- set the gun range based on the workers quality - self.gun_range = gun_range[self.worker.quality.name] + self.gun_range = gun_range[self.worker.quality.name] or 36 -- disable roboports self:disable_roboports(0) -- state change diff --git a/script/job/minion.lua b/script/job/minion.lua index ca8fad6..1be0df3 100644 --- a/script/job/minion.lua +++ b/script/job/minion.lua @@ -85,7 +85,7 @@ function minion_job:in_progress() self.job_status = "Following leader" local worker = self.worker ---@cast worker -nil -- check if the leader is alive and job is still valid - if not storage.jobs[self.primary_job.job_index] or not self.primary_job.worker then + if not storage.jobs[self.primary_job.job_index] or not (self.primary_job.worker and self.primary_job.worker.valid) then self.state = "finishing" -- IDEA: assign util to primary? worker.autopilot_destination = nil return diff --git a/script/job/upgrade.lua b/script/job/upgrade.lua index a346e7e..076370d 100644 --- a/script/job/upgrade.lua +++ b/script/job/upgrade.lua @@ -21,9 +21,9 @@ function upgrade_job:specific_action() } -- only detects entities in range local can_upgrade_entity = false for _, entity in pairs(entities) do - local target, target_quality = entity.get_upgrade_target() ---@cast target -nil - local item_name = target.items_to_place_this[1].name - if self.worker_logistic_network.can_satisfy_request({name = item_name, quality = target_quality}, 1, false) then -- does inventory have the required item? + local target, target_quality = entity.get_upgrade_target() + local item = target and target.items_to_place_this[1] + if item and self.worker_logistic_network.can_satisfy_request({name = item.name, quality = target_quality}, 1, false) then -- does inventory have the required item? can_upgrade_entity = true end end diff --git a/script/pathfinder.lua b/script/pathfinder.lua index c446edb..cdfda59 100644 --- a/script/pathfinder.lua +++ b/script/pathfinder.lua @@ -54,6 +54,10 @@ end script.on_event(defines.events.on_script_path_request_finished, (function(event) local job = storage.pathfinder_requests[event.id] if not job or not job.worker or not job.worker.valid then return end + if job.path_request_id ~= event.id then -- a newer request superseded this one; ignore the stale result + storage.pathfinder_requests[event.id] = nil + return + end local path = event.path if event.try_again_later then -- COMMENT: job should handle itself diff --git a/script/ui.lua b/script/ui.lua index ddd9945..b5d6fee 100644 --- a/script/ui.lua +++ b/script/ui.lua @@ -366,7 +366,7 @@ function gui_handlers.update_job_gui(player) local job = storage.jobs[job_window.tags.job_index] if not job then return end local job_worker = job.worker - if not job_worker then return end -- TODO: refactor this behavior + if not (job_worker and job_worker.valid) then return end local job_ui_elements = storage.user_interface[player.index].job_ui.elements -- update worker_minimap @@ -375,7 +375,7 @@ function gui_handlers.update_job_gui(player) worker_minimap.entity = job_worker -- update job_minimap - job_ui_elements["job_minimap"].position = (job.task_positions[1] or job.station.position or job_worker.position) + job_ui_elements["job_minimap"].position = (job.task_positions[1] or (job.station and job.station.valid and job.station.position) or job_worker.position) -- update job stats job_ui_elements["job_tasks_stat_label"].caption = {"ctron_gui_locale.job_job_stat_tasks_label", (#job.task_positions or "0")} @@ -500,7 +500,13 @@ function gui_handlers.ctron_locate_job(player, element) end local _, chunk = next(job.chunks) chunk = chunk or {} - local position = (chunk.midpoint or job.station.position or job.worker.position) + local station_position = job.station and job.station.valid and job.station.position + local worker_position = job.worker and job.worker.valid and job.worker.position + local position = (chunk.midpoint or station_position or worker_position) + if not position then + player.print({"ctron_warnings.job_no_exist"}) + return + end player.set_controller{ type = defines.controllers.remote, position = position, surface = job.surface_index } -- move UI for visibility gui_handlers.resize_gui(player) @@ -526,16 +532,21 @@ end function gui_handlers.ctron_map_view(player, element) local job = storage.jobs[element.tags.job_index] if not job then return end + -- worker/station may be nil or invalid if the entity was removed while the job was active + local worker = (job.worker and job.worker.valid) and job.worker or nil if element.tags.follow_entity then - player.set_controller{ type = defines.controllers.remote, surface = job.worker.surface} - player.centered_on = job.worker + if not worker then return end + player.set_controller{ type = defines.controllers.remote, surface = worker.surface} + player.centered_on = worker else local current_task = 1 if #job.task_positions > 1 then current_task = math.random(1, #job.task_positions) end - local position = (job.task_positions[current_task] or job.station.position or job.worker.position) - player.set_controller{ type = defines.controllers.remote, position = position, surface = job.worker.surface } + local station_position = job.station and job.station.valid and job.station.position + local position = (job.task_positions[current_task] or station_position or (worker and worker.position)) + if not position then return end + player.set_controller{ type = defines.controllers.remote, position = position, surface = (worker and worker.surface) or job.surface_index } -- draw chunks for _, chunk_to_draw in pairs(job.chunks) do debug_lib.draw_rectangle(chunk_to_draw.minimum, chunk_to_draw.maximum, job.surface_index, job_colors[job.job_type], true, 120) @@ -577,18 +588,23 @@ function gui_handlers.ctron_cancel_job(player, element, ctrl_clicked) if job.state ~= "finishing" then -- handle job cancellation job.state = "finishing" - if job.worker then + if job.worker and job.worker.valid then job.worker.autopilot_destination = nil end - if job.job_type == "cargo" then - local station_unit_number = job.destination_station.unit_number - for _, request in pairs(storage.station_requests[station_unit_number]) do - local requested_item = request.name - for item, value in pairs(job.required_items) do - for quality, item_count in pairs(value) do - if requested_item == item and request.quality == quality then - request.in_transit_count = request.in_transit_count - item_count - break + if job.job_type == "cargo" and job.sub_state ~= "unloading_items" then + if job.destination_station and job.destination_station.valid then + local station_unit_number = job.destination_station.unit_number + local station_requests = storage.station_requests[station_unit_number] + if station_requests then + for _, request in pairs(station_requests) do + local requested_item = request.name + for item, value in pairs(job.required_items) do + for quality, item_count in pairs(value) do + if requested_item == item and request.quality == quality then + request.in_transit_count = math.max(0, request.in_transit_count - item_count) + break + end + end end end end @@ -981,6 +997,7 @@ function gui_handlers.open_cargo_window(player, element) return end local station = storage.service_stations[element.tags.station_unit_number] + if not (station and station.valid) then return end gui_cargo.buildCargoGui(player, game.surfaces[station.surface.name]) gui_handlers.cargo_hide_stations(player, element) else @@ -1064,7 +1081,7 @@ function gui_handlers.on_gui_elem_changed(player, element) local request = storage.station_requests[station_unit_number][slot_number] -- check if item is already requested for k, v in pairs(element.parent.children) do - if v.elem_value and v.elem_value == item and v.name ~= element.name then + if v.elem_value and v.elem_value.name == item and v.elem_value.quality == quality and v.name ~= element.name then element.elem_value = nil element.style = "ctron_slot_button" player.print({"ctron_warnings.item_already_requested"}) @@ -1166,7 +1183,12 @@ function gui_handlers.copy_station_requests(player, element) end function gui_handlers.paste_station_requests(player, element) - storage.station_requests[element.tags.station_unit_number] = table.deepcopy(storage.user_interface[player.index]["settings_export"]) + local settings_export = storage.user_interface[player.index]["settings_export"] + if not settings_export then + player.print({"ctron_warnings.no_requests_copied"}) + return + end + storage.station_requests[element.tags.station_unit_number] = table.deepcopy(settings_export) -- reset in_transit count on all requests for the new station for _, request in pairs(storage.station_requests[element.tags.station_unit_number]) do request.in_transit_count = 0 @@ -1180,8 +1202,10 @@ function gui_handlers.on_gui_hover_job(player, element) if not job then return end local _, chunk = next(job.chunks) chunk = chunk or {} - local destination_station = job.destination_station or {} - local position = (chunk.midpoint or destination_station.position or job.task_positions[1] or job.worker.position) + local destination_station = job.destination_station + local station_position = destination_station and destination_station.valid and destination_station.position + local worker_position = job.worker and job.worker.valid and job.worker.position + local position = (chunk.midpoint or station_position or job.task_positions[1] or worker_position) if not position then return end if player.gui.screen.ctron_hover_frame then player.gui.screen.ctron_hover_frame.destroy() diff --git a/script/ui_job_screen.lua b/script/ui_job_screen.lua index 716482f..f45df59 100644 --- a/script/ui_job_screen.lua +++ b/script/ui_job_screen.lua @@ -156,7 +156,7 @@ function gui_job.buildJobContent(player, frame, job) follow_entity = true } } - storage.user_interface[player.index].job_ui.elements["worker_minimap"].entity = job.worker + storage.user_interface[player.index].job_ui.elements["worker_minimap"].entity = (job.worker and job.worker.valid) and job.worker or nil left_pane.add{ type = "line", @@ -186,7 +186,7 @@ function gui_job.buildJobContent(player, frame, job) tooltip = {"ctron_gui_locale.job_job_minimap_tooltip"}, zoom = 2, surface_index = job.surface_index, - position = (job.task_positions[1] or job.station.position or job.worker.position), + position = (job.task_positions[1] or (job.station and job.station.valid and job.station.position) or (job.worker and job.worker.valid and job.worker.position)), tags = { mod = "constructron", on_gui_click = "ctron_map_view", @@ -379,16 +379,17 @@ function gui_job.build_logistic_display(worker, logistic_table) local inventory = worker.get_inventory(defines.inventory.spider_trunk) assert(inventory, "Worker inventory is nil") -- get worker logistic requests - local logistic_point = worker.get_logistic_point(0) ---@cast logistic_point -nil - local section = logistic_point.get_section(1) - local slot_count = #(section.filters or {}) + local logistic_point = worker.get_logistic_point(0) + local section = logistic_point and logistic_point.get_section(1) + local filters = section and section.filters or {} + local slot_count = #filters -- Ensure the value is at least 10 local min_slot_count = math.max(slot_count, 10) -- Round up to the nearest 20 local logistic_request_count = math.ceil(min_slot_count / 20) * 20 for i = 1, logistic_request_count do - if section.filters[i] and section.filters[i].value then - local slot = section.filters[i] + if filters[i] and filters[i].value then + local slot = filters[i] local slot_item = slot.value gui_job.build_button(logistic_table, "ctron_logistic_button_" .. i, slot_item.name, slot_item.quality, ((slot.max or slot.min) - inventory.get_item_count(slot_item)), nil, worker) else diff --git a/script/ui_main_screen.lua b/script/ui_main_screen.lua index dd00616..0726f2d 100644 --- a/script/ui_main_screen.lua +++ b/script/ui_main_screen.lua @@ -432,7 +432,7 @@ function gui_main.create_job_card(job, section) label_bg.style.color = job_colors[job_type] label_bg.style.width = 180 label_bg.style.bar_width = 30 -- bar thickness - job_label = label_bg.add{ + local job_label = label_bg.add{ type = "label", name = "ctron_job_type_label", caption = {"ctron_gui_locale.job_card_" .. job_type .. "_name"}, @@ -705,13 +705,14 @@ function gui_main.BuildLogisticsContent(player, surface_index, logistics_window) for _, job in pairs(storage.jobs) do if (job.surface_index == surface_index) and job.state == "starting" and job.worker and job.worker.valid then local worker = job.worker - local logistic_point = worker.get_logistic_point(0) ---@cast logistic_point -nil - local section = logistic_point.get_section(1) + local logistic_point = worker.get_logistic_point(0) + local section = logistic_point and logistic_point.get_section(1) + local filters = section and section.filters or {} local flag = false local inventory = worker.get_inventory(defines.inventory.spider_trunk) assert(inventory, "Worker inventory is nil") - for i = 1, #section.filters do - local slot = section.filters[i] + for i = 1, #filters do + local slot = filters[i] if slot.value then local item_name = slot.value.name local quality = slot.value.quality @@ -749,8 +750,10 @@ function gui_main.BuildLogisticsContent(player, surface_index, logistics_window) column_count = 10, } + local request_count = 0 for item, value in pairs(logistics_requests) do for quality, count in pairs(value) do + request_count = request_count + 1 local button = logistic_table.add{ type = "choose-elem-button", elem_type = "item-with-quality", @@ -790,7 +793,6 @@ function gui_main.BuildLogisticsContent(player, surface_index, logistics_window) end end - local request_count = #logistics_requests or 1 -- Round up to the nearest 10 local logistic_slot_count = math.ceil(request_count / 10) * 10 if request_count == logistic_slot_count then diff --git a/script/utility_functions.lua b/script/utility_functions.lua index 7ad90dc..2ad3caa 100644 --- a/script/utility_functions.lua +++ b/script/utility_functions.lua @@ -388,13 +388,13 @@ me.string_split = function(to_split, sep) return values end --- this function is used to get the first value of a LuaCustomTable +-- returns the first key of a LuaCustomTable (e.g. the prototype name from a name-indexed prototype table) -- https://discord.com/channels/139677590393716737/306402592265732098/1279094347052220447 ---@generic K,V ---@param lct LuaCustomTable ----@return V +---@return K me.firstoflct = function(lct) - for key, value in pairs(lct) do + for key, _ in pairs(lct) do return key end end