local algorithm = require("algorithm") local mpp_util = require("mpp.mpp_util") local enums = require("mpp.enums") local blueprint_meta = require("mpp.blueprintmeta") local compatibility = require("mpp.compatibility") local common = require("layouts.common") local layouts = algorithm.layouts local gui = {} --[[ tag explanations: mpp_action - a choice between several settings for a "*_choice" mpp_toggle - a toggle for a boolean "*_choice" ]] ---@alias MppTagAction ---| "mpp_advanced_settings" ---| "mpp_entity_filtering_mode" ---| "mpp_action" ---| "mpp_toggle" ---| "mpp_blueprint_add_mode" ---| "mpp_blueprint_receptacle" ---| "mpp_fake_blueprint_button" ---| "mpp_delete_blueprint_button" ---| "mpp_drop_down" ---| "mpp_undo" ---@alias MppSettingSections ---| "layout" ---| "direction" ---| "miner" ---| "belt" ---| "space_belt" ---| "logistics" ---| "pole" ---| "misc" ---| "debugging" ---@type table local entity_sections = { miner = true, belt=true, space_belt=true, logistics=true, pole=true } ---@class SettingValueEntry ---@field type string|nil Button type ---@field value string Value name ---@field tooltip LocalisedString ---@field icon SpritePath ---@field icon_enabled SpritePath? ---@field order string? ---@field default number? For "drop-down" element ---@field refresh boolean? Update selections? ---@field filterable boolean? Can entity be hidden ---@field disabled boolean? Is button disabled ---@class SettingValueEntryPrototype : SettingValueEntry ---@field action "mpp_prototype" Action tag override ---@field elem_type string ---@field elem_filters PrototypeFilter? ---@field elem_value string? ---@class TagsSimpleChoiceButton ---@field value string ---@field default string ---@field mpp_filterable boolean ---Creates a setting section (label + table) ---Can be hidden ---@param player_data PlayerData ---@param root any ---@param name MppSettingSections ---@return LuaGuiElement, LuaGuiElement local function create_setting_section(player_data, root, name, opts) opts = opts or {} local section = root.add{type="flow", direction="vertical", style="mpp_section"} player_data.gui.section[name] = section section.add{type="label", style="subheader_caption_label", caption={"mpp.settings_"..name.."_label"}} local table_root = section.add{ type="table", direction=opts.direction or "horizontal", style="filter_slot_table", column_count=opts.column_count or 6, } player_data.gui.tables[name] = table_root return table_root, section end local function style_helper_selection(check) if check then return "yellow_slot_button" end return "recipe_slot_button" end local function style_helper_advanced_toggle(check) return check and "mpp_selected_frame_action_button" or "frame_action_button" end local function style_helper_blueprint_toggle(check) return check and "mpp_blueprint_mode_button_active" or "mpp_blueprint_mode_button" end ---@param player_data PlayerData local function helper_undo_available(player_data) return player_data.last_state and #player_data.last_state._collected_ghosts > 0 end ---@param player_data PlayerData global player GUI reference object ---@param root LuaGuiElement ---@param action_type string Default action tag ---@param action MppSettingSections ---@param values (SettingValueEntry | SettingValueEntryPrototype)[] local function create_setting_selector(player_data, root, action_type, action, values) local action_class = {} player_data.gui.selections[action] = action_class root.clear() local selected = player_data.choices[action.."_choice"] for _, value in ipairs(values) do local is_filtered = mpp_util.get_entity_hidden(player_data, action, value.value) if not player_data.entity_filtering_mode and is_filtered then goto continue end local action_type_override = value.action or action_type local toggle_value = action_type == "mpp_toggle" and player_data.choices[value.value.."_choice"] local style_check = value.value == selected or toggle_value local button if value.type == "choose-elem-button" then ---@type LuaGuiElement button = root.add{ type="choose-elem-button", style=style_helper_selection(), tooltip=mpp_util.wrap_tooltip(value.tooltip), elem_type=value.elem_type, elem_filters=value.elem_filters, item=value.elem_value, -- duplicate them all; entity=value.elem_value, -- and let Wube sort them out tags={[action_type_override]=action, value=value.value, default=value.default}, } local fake_placeholder = button.add{ type="sprite", sprite=value.icon, ignored_by_interaction=true, style="mpp_fake_item_placeholder", visible=not value.elem_value, } else local icon = value.icon if style_check and value.icon_enabled then icon = value.icon_enabled end ---@type LuaGuiElement button = root.add{ type="sprite-button", style=style_helper_selection(style_check), sprite=icon, tags={ [action_type_override]=action, value=value.value, default=value.default, refresh=value.refresh, mpp_icon_default=value.icon, mpp_icon_enabled=value.icon_enabled, mpp_filterable=value.filterable, }, tooltip=mpp_util.tooltip_entity_not_available(value.disabled, value.tooltip), enabled=not value.disabled, } if is_filtered then ---@type LuaGuiElement local hidden = button.add{ type="sprite", style="mpp_filtered_entity", name="filtered", sprite="mpp_entity_filtered_overlay", ignored_by_interaction=true, } end end action_class[value.value] = button ::continue:: end end ---@param player_data PlayerData ---@param button LuaGuiElement local function set_player_blueprint(player_data, button) local choices = player_data.choices local player_blueprints = player_data.blueprints local blueprint_number = button.tags.mpp_fake_blueprint_button local blueprint_flow = player_blueprints.flow[button.parent.index] local current_blueprint = choices.blueprint_choice if current_blueprint == blueprint_number then return nil end if current_blueprint and current_blueprint.valid then local current_blueprint_button = player_blueprints.button[current_blueprint.item_number] current_blueprint_button.style = "mpp_fake_blueprint_button" end local blueprint = player_blueprints.mapping[blueprint_number] button.style = "mpp_fake_blueprint_button_selected" choices.blueprint_choice = blueprint end ---@param player_data PlayerData ---@param table_root LuaGuiElement ---@param blueprint_item LuaItemStack ---@param cursor_stack LuaItemStack|nil local function create_blueprint_entry(player_data, table_root, blueprint_item, cursor_stack) local blueprint_line = table_root.add{type="flow"} local item_number = blueprint_item.item_number --[[@as number]] player_data.blueprints.flow[item_number] = blueprint_line player_data.blueprints.mapping[item_number] = blueprint_item local blueprint_button = blueprint_line.add{ type="button", style=(player_data.choices.blueprint_choice == blueprint_item and "mpp_fake_blueprint_button_selected" or "mpp_fake_blueprint_button"), tags={mpp_fake_blueprint_button=item_number}, } player_data.blueprints.button[item_number] = blueprint_button if table_size(blueprint_item.blueprint_icons) > 1 then local fake_table = blueprint_button.add{ type="table", style="mpp_fake_blueprint_table", direction="horizontal", column_count=2, tags={mpp_fake_blueprint_table=true}, ignored_by_interaction=true, } for k, v in pairs(blueprint_item.blueprint_icons) do local s = v.signal local sprite = s.name or "" if s.type == "virtual" then sprite = "virtual-signal/"..sprite --wube pls else sprite = s.type .. "/" .. sprite end if not fake_table.gui.is_valid_sprite_path(sprite) then sprite = "item/item-unknown" end fake_table.add{ type="sprite", sprite=(sprite), style="mpp_fake_blueprint_sprite", tags={mpp_fake_blueprint_sprite=true}, } end else local bp_signal = ({next(blueprint_item.blueprint_icons)})[2].signal --[[@as SignalID]] local sprite = bp_signal.name if bp_signal.type == "virtual" then sprite = "virtual-signal/"..sprite --wube pls else sprite = bp_signal.type .. "/" .. sprite end blueprint_button.add{ type="sprite", sprite=(sprite), ignored_by_interaction=true, style="mpp_fake_item_placeholder_blueprint", tags={mpp_fake_blueprint_sprite=true}, } local a = false end local delete_button = blueprint_line.add{ type="sprite-button", sprite="mpp_cross", style="mpp_delete_blueprint_button", tags={mpp_delete_blueprint_button=item_number}, tooltip=mpp_util.wrap_tooltip{"gui.delete-blueprint-record"}, } player_data.blueprints.delete[item_number] = delete_button local label, tooltip = mpp_util.blueprint_label(blueprint_item) blueprint_line.add{ type="label", caption=label, tooltip=mpp_util.wrap_tooltip(tooltip), } local cached = player_data.blueprints.cache[item_number] if not cached then cached = blueprint_meta:new(blueprint_item) player_data.blueprints.cache[item_number] = cached else if not cached:check_valid() then blueprint_button.style = "mpp_fake_blueprint_button_invalid" if player_data.choices.blueprint_choice == blueprint_item then player_data.choices.blueprint_choice = nil end end end if cursor_stack and cursor_stack.valid and cached.valid then player_data.blueprints.original_id[item_number] = cursor_stack.item_number end if not player_data.choices.blueprint_choice and cached.valid then set_player_blueprint(player_data, blueprint_button) end end ---@param player LuaPlayer function gui.create_interface(player) ---@type LuaGuiElement local frame = player.gui.screen.add{type="frame", name="mpp_settings_frame", direction="vertical"} ---@type PlayerData local player_data = global.players[player.index] local player_gui = player_data.gui local titlebar = frame.add{type="flow", name="mpp_titlebar", direction="horizontal"} titlebar.add{type="label", style="frame_title", name="mpp_titlebar_label", caption={"mpp.settings_frame"}} titlebar.add{type="empty-widget", name="mpp_titlebar_spacer", horizontally_strechable=true} player_gui.advanced_settings = titlebar.add{ type="sprite-button", style=style_helper_advanced_toggle(player_data.advanced), sprite="mpp_advanced_settings", tooltip=mpp_util.wrap_tooltip{"mpp.advanced_settings"}, tags={mpp_advanced_settings=true}, } player_gui.filtering_settings = titlebar.add{ type="sprite-button", style=style_helper_advanced_toggle(player_data.entity_filtering_mode), sprite="mpp_entity_filtering_mode_enabled", tooltip=mpp_util.wrap_tooltip{"mpp.entity_filtering_mode"}, tags={mpp_entity_filtering_mode=true}, } player_gui.undo_button = titlebar.add{ type="sprite-button", style=style_helper_advanced_toggle(), sprite="mpp_undo_enabled", tooltip=mpp_util.wrap_tooltip{"controls.undo"}, tags={mpp_undo=true}, enabled=helper_undo_available(player_data), } do -- layout selection local table_root, section = create_setting_section(player_data, frame, "layout") local choices = {} local index = 0 for i, layout in ipairs(layouts) do if player_data.choices.layout_choice == layout.name then index = i end choices[#choices+1] = layout.translation end local flow = table_root.add{type="flow", direction="horizontal"} player_gui.layout_dropdown = flow.add{ type="drop-down", items=choices, selected_index=index --[[@as uint]], tags={mpp_drop_down="layout", default=1}, } player_gui.blueprint_add_button = flow.add{ type="sprite-button", name="blueprint_add_button", sprite="mpp_plus", style=style_helper_blueprint_toggle(), tooltip=mpp_util.wrap_tooltip{"mpp.blueprint_add_mode"}, tags={mpp_blueprint_add_mode=true}, } player_gui.blueprint_add_button.visible = player_data.choices.layout_choice == "blueprints" end do -- Direction selection local table_root = create_setting_section(player_data, frame, "direction") create_setting_selector(player_data, table_root, "mpp_action", "direction", { {value="north", icon="mpp_direction_north"}, {value="south", icon="mpp_direction_south"}, {value="west", icon="mpp_direction_west"}, {value="east", icon="mpp_direction_east"}, }) end do -- Miner selection local table_root, section = create_setting_section(player_data, frame, "miner") end do -- Belt selection local table_root, section = create_setting_section(player_data, frame, "belt") end do -- Space belt selection local table_root, section = create_setting_section(player_data, frame, "space_belt") end do -- Logistics selection local table_root, section = create_setting_section(player_data, frame, "logistics") end do -- Electric pole selection local table_root, section = create_setting_section(player_data, frame, "pole") end do -- Blueprint settings ---@type LuaGuiElement, LuaGuiElement --local table_root, section = create_setting_section(player_data, frame, "blueprints") local section = frame.add{type="flow", direction="vertical"} player_data.gui.section["blueprints"] = section section.add{type="label", style="subheader_caption_label", caption={"mpp.settings_blueprints_label"}} local root = section.add{type="flow", direction="vertical"} player_data.gui.tables["blueprints"] = root player_gui.blueprint_add_section = section.add{ type="flow", direction="horizontal", } player_gui.blueprint_receptacle = player_gui.blueprint_add_section.add{ type="sprite-button", sprite="mpp_blueprint_add", tags={mpp_blueprint_receptacle=true}, } local blueprint_label = player_gui.blueprint_add_section.add{ type="label", caption={"mpp.label_add_blueprint", }, } player_gui.blueprint_add_section.visible = player_data.blueprint_add_mode for i = 1, #player_data.blueprint_items do ---@type LuaItemStack local item = player_data.blueprint_items[i] if item.valid and item.is_blueprint then create_blueprint_entry(player_data, root, item) end end end do -- Misc selection local table_root, section = create_setting_section(player_data, frame, "misc") end do -- Debugging rendering options local table_root, section = create_setting_section(player_data, frame, "debugging") end end ---@param player_data PlayerData local function update_miner_selection(player_data) local player_choices = player_data.choices local layout = layouts[player_choices.layout_choice] local restrictions = layout.restrictions player_data.gui.section["miner"].visible = restrictions.miner_available if not restrictions.miner_available then return end local near_radius_min, near_radius_max = restrictions.miner_size[1], restrictions.miner_size[2] local far_radius_min, far_radius_max = restrictions.miner_radius[1], restrictions.miner_radius[2] ---@type SettingValueEntry[] local values = {} local existing_choice_is_valid = false local cached_miners, cached_resources = enums.get_available_miners() for _, miner_proto in pairs(cached_miners) do if mpp_util.check_filtered(miner_proto) then goto skip_miner end if mpp_util.check_entity_hidden(player_data, "miner", miner_proto) then goto skip_miner end local miner = mpp_util.miner_struct(miner_proto.name) local is_restricted = common.is_miner_restricted(miner, restrictions) or not layout:restriction_miner(miner) if not player_data.entity_filtering_mode and is_restricted then goto skip_miner end local tooltip = List{ "", miner_proto.localised_name, "\n", "[img=mpp_tooltip_category_size] ", {"description.tile-size"}, (": %ix%i\n"):format(miner.size, miner.size), "[img=mpp_tooltip_category_mining_area] ", {"description.mining-area"}, (": %ix%i"):format(miner.area, miner.area), } tooltip :contitional_append(miner.power_source_tooltip,"\n", miner.power_source_tooltip) :contitional_append(miner.area <= miner.size, "\n[color=yellow]", {"mpp.label_insufficient_area"}, "[/color]") :contitional_append(not miner.supports_fluids, "\n[color=yellow]", {"mpp.label_no_fluid_mining"}, "[/color]") :contitional_append(miner.skip_outer, "\n[color=yellow]", {"mpp.label_oversized_drill"}, "[/color]") values[#values+1] = { value=miner.name, tooltip=tooltip, icon=("entity/"..miner.name), order=miner_proto.order, filterable=true, disabled=is_restricted, } if miner.name == player_choices.miner_choice then existing_choice_is_valid = true end ::skip_miner:: end if not existing_choice_is_valid and #values > 0 then if mpp_util.table_find(values, function(v) return v.value == layout.defaults.miner end) then player_choices.miner_choice = layout.defaults.miner else player_choices.miner_choice = values[1].value end existing_choice_is_valid = true elseif #values == 0 then player_choices.miner_choice = "none" values[#values+1] = { value="none", tooltip={"mpp.msg_miner_err_3"}, icon="mpp_no_entity", order="", } end local table_root = player_data.gui.tables["miner"] create_setting_selector(player_data, table_root, "mpp_action", "miner", values) end ---@param player LuaPlayer local function update_belt_selection(player) local player_data = global.players[player.index] local choices = player_data.choices local layout = layouts[choices.layout_choice] local restrictions = layout.restrictions local is_space = compatibility.is_space(player.surface.index) player_data.gui.section["belt"].visible = restrictions.belt_available and not is_space if not restrictions.belt_available or is_space then return end local values = {} local existing_choice_is_valid = false local belts = game.get_filtered_entity_prototypes{{filter="type", type="transport-belt"}} for _, belt in pairs(belts) do local belt_struct = mpp_util.belt_struct(belt.name) if mpp_util.check_entity_hidden(player_data, "belt", belt) then goto skip_belt end local is_restricted = common.is_belt_restricted(belt_struct, restrictions) if not player_data.entity_filtering_mode and is_restricted then goto skip_belt end local belt_speed = belt.belt_speed * 60 * 8 local specifier = belt_speed % 1 == 0 and ": %.0f " or ": %.1f " local tooltip = { "", belt.localised_name, "\n", {"description.belt-speed"}, specifier:format(belt_speed), {"description.belt-items"}, "/s", } if restrictions.uses_underground_belts and not belt_struct.related_underground_belt then table.insert(tooltip, {"", "\n[color=yellow]", {"mpp.label_belt_find_underground"}, "[/color]"}) end values[#values+1] = { value=belt.name, tooltip=tooltip, icon=("entity/"..belt.name), order=belt.order, filterable=true, disabled=is_restricted, } if belt.name == choices.belt_choice then existing_choice_is_valid = true end ::skip_belt:: end if not existing_choice_is_valid and #values > 0 then if mpp_util.table_find(values, function(v) return v.value == layout.defaults.belt end) then choices.belt_choice = layout.defaults.belt else choices.belt_choice = values[1].value end existing_choice_is_valid = true elseif #values == 0 then player_data.choices.belt_choice = "none" values[#values+1] = { value="none", tooltip={"mpp.choice_none"}, icon="mpp_no_entity", order="", } end local table_root = player_data.gui.tables["belt"] create_setting_selector(player_data, table_root, "mpp_action", "belt", values) end ---@param player LuaPlayer local function update_space_belt_selection(player) local player_data = global.players[player.index] local choices = player_data.choices local layout = layouts[choices.layout_choice] local restrictions = layout.restrictions local is_space = compatibility.is_space(player.surface.index) player_data.gui.section["space_belt"].visible = restrictions.belt_available and is_space if not restrictions.belt_available or not is_space then return end local values = {} local existing_choice_is_valid = false local belts = game.get_filtered_entity_prototypes{{filter="type", type="transport-belt"}} for _, belt in pairs(belts) do --if not compatibility.is_buildable_in_space(belt.name) then goto skip_belt end local belt_struct = mpp_util.belt_struct(belt.name) if mpp_util.check_entity_hidden(player_data, "space_belt", belt) then goto skip_belt end local is_restricted = common.is_belt_restricted(belt_struct, restrictions) or not compatibility.is_buildable_in_space(belt.name) if not player_data.entity_filtering_mode and is_restricted then goto skip_belt end --if is_space and not string.match(belt.name, "^se%-") then goto skip_belt end local belt_speed = belt.belt_speed * 60 * 8 local specifier = belt_speed % 1 == 0 and ": %.0f " or ": %.1f " local tooltip = { "", belt.localised_name, "\n", {"description.belt-speed"}, specifier:format(belt_speed), {"description.belt-items"}, "/s", } values[#values+1] = { value=belt.name, tooltip=tooltip, icon=("entity/"..belt.name), order=belt.order, filterable=true, disabled=is_restricted, } if belt.name == choices.space_belt_choice then existing_choice_is_valid = true end ::skip_belt:: end if not existing_choice_is_valid and #values > 0 then if mpp_util.table_find(values, function(v) return v.value == layout.defaults.belt end) then choices.space_belt_choice = "se-space-transport-belt" else choices.space_belt_choice = values[1].value end elseif #values == 0 then player_data.choices.belt_choice = "none" values[#values+1] = { value="none", tooltip={"mpp.choice_none"}, icon="mpp_no_entity", order="", } end local table_root = player_data.gui.tables["space_belt"] create_setting_selector(player_data, table_root, "mpp_action", "space_belt", values) end ---@param player_data PlayerData local function update_logistics_selection(player_data) local choices = player_data.choices local layout = layouts[choices.layout_choice] local restrictions = layout.restrictions local values = {} player_data.gui.section["logistics"].visible = restrictions.logistics_available if not restrictions.logistics_available then return end local filter = { ["passive-provider"]=true, ["active-provider"]=true, ["storage"] = true, } local existing_choice_is_valid = false local logistics = game.get_filtered_entity_prototypes{{filter="type", type="logistic-container"}} for _, chest in pairs(logistics) do if mpp_util.check_entity_hidden(player_data, "logistics", chest) then goto skip_chest end local cbox = chest.collision_box local size = math.ceil(cbox.right_bottom.x - cbox.left_top.x) if size > 1 then goto skip_chest end if not filter[chest.logistic_mode] then goto skip_chest end values[#values+1] = { value=chest.name, tooltip=chest.localised_name, icon=("entity/"..chest.name), order=chest.order, filterable=true, } if chest.name == choices.logistics_choice then existing_choice_is_valid = true end ::skip_chest:: end if not existing_choice_is_valid and #values > 0 then if mpp_util.table_find(values, function(v) return v.value == layout.defaults.logistics end) then choices.logistics_choice = layout.defaults.logistics else choices.logistics_choice = values[1].value end elseif #values == 0 then player_data.choices.belt_choice = "none" values[#values+1] = { value="none", tooltip={"mpp.choice_none"}, icon="mpp_no_entity", order="", } end local table_root = player_data.gui.tables["logistics"] create_setting_selector(player_data, table_root, "mpp_action", "logistics", values) end ---@param player_data PlayerData local function update_pole_selection(player_data) local choices = player_data.choices local layout = layouts[choices.layout_choice] local restrictions = layout.restrictions player_data.gui.section["pole"].visible = restrictions.pole_available if not restrictions.pole_available then return end local values = {} values[1] = { value="none", tooltip={"mpp.choice_none"}, icon="mpp_no_entity", order="", } local existing_choice_is_valid = ("none" == choices.pole_choice) local poles = game.get_filtered_entity_prototypes{{filter="type", type="electric-pole"}} for _, pole_proto in pairs(poles) do if mpp_util.check_filtered(pole_proto) then goto skip_pole end if mpp_util.check_entity_hidden(player_data, "pole", pole_proto) then goto skip_pole end if pole_proto.supply_area_distance < 0.5 then goto skip_pole end local pole = mpp_util.pole_struct(pole_proto.name) local is_restricted = common.is_pole_restricted(pole, restrictions) if not player_data.entity_filtering_mode and is_restricted then goto skip_pole end local specifier = (pole.wire % 1 == 0 and ": %.0f ") or (pole.wire * 10 % 1 == 0 and ": %.1f ") or ": %.2f " local tooltip = { "", pole_proto.localised_name, "\n", "[img=mpp_tooltip_category_size] ", {"description.tile-size"}, (": %ix%i\n"):format(pole.size, pole.size), "[img=mpp_tooltip_category_supply_area] ", {"description.supply-area"}, (": %ix%i\n"):format(pole.supply_width, pole.supply_width), " [img=tooltip-category-electricity] ", {"description.wire-reach"}, specifier:format(pole.wire), } values[#values+1] = { value=pole_proto.name, tooltip=tooltip, icon=("entity/"..pole_proto.name), order=pole_proto.order, filterable=true, disabled=is_restricted, } if pole_proto.name == choices.pole_choice then existing_choice_is_valid = true end ::skip_pole:: end if not existing_choice_is_valid then choices.pole_choice = layout.defaults.pole end local table_root = player_data.gui.tables["pole"] create_setting_selector(player_data, table_root, "mpp_action", "pole", values) end ---@param player LuaPlayer local function update_misc_selection(player) local player_data = global.players[player.index] local choices = player_data.choices local layout = layouts[choices.layout_choice] ---@type SettingValueEntry[] local values = {} if layout.restrictions.module_available then ---@type string|nil local existing_choice = choices.module_choice if not game.item_prototypes[existing_choice] then existing_choice = nil choices.module_choice = "none" end values[#values+1] = { action="mpp_prototype", value="module", tooltip={"gui.module"}, icon=("mpp_no_module"), elem_type="item", elem_filters={{filter="type", type="module"}}, elem_value = existing_choice, type="choose-elem-button", } end if layout.restrictions.lamp_available then values[#values+1] = { value="lamp", tooltip={"mpp.choice_lamp"}, icon=("mpp_no_lamp"), icon_enabled=("entity/small-lamp"), } end if layout.restrictions.pipe_available then ---@type string | nil local existing_choice = choices.pipe_choice if not game.entity_prototypes[existing_choice] then existing_choice = nil choices.pipe_choice = "none" end values[#values+1] = { action="mpp_prototype", value="pipe", tooltip={"entity-name.pipe"}, icon=("mpp_no_pipe"), elem_type="entity", elem_filters={{filter="type", type="pipe"}}, elem_value = existing_choice, type="choose-elem-button", } end if layout.restrictions.deconstruction_omit_available then values[#values+1] = { value="deconstruction", tooltip={"mpp.choice_deconstruction"}, icon=("mpp_deconstruct"), icon_enabled=("mpp_omit_deconstruct"), } end if compatibility.is_space(player.surface_index) and layout.restrictions.landfill_omit_available then local existing_choice = choices.space_landfill_choice if not game.entity_prototypes[existing_choice] then existing_choice = "se-space-platform-scaffold" choices.space_landfill_choice = existing_choice end values[#values+1] = { action="mpp_prototype", value="space_landfill", icon=("item/"..existing_choice), elem_type="item", elem_filters={ {filter="name", name="se-space-platform-scaffold"}, {filter="name", name="se-space-platform-plating", mode="or"}, {filter="name", name="se-spaceship-floor", mode="or"}, }, elem_value = choices.space_landfill_choice, type="choose-elem-button", } end if layout.restrictions.landfill_omit_available then values[#values+1] = { value="landfill", tooltip={"mpp.choice_landfill"}, icon=("item/landfill"), icon_enabled=("mpp_omit_landfill") } end if layout.restrictions.coverage_tuning then values[#values+1] = { value="coverage", tooltip={"mpp.choice_coverage"}, icon=("mpp_miner_coverage_disabled"), icon_enabled=("mpp_miner_coverage"), } end if layout.restrictions.start_alignment_tuning then values[#values+1] = { value="start", tooltip={"mpp.choice_start"}, icon=("mpp_align_start"), } end if layout.restrictions.placement_info_available then values[#values+1] = { value="print_placement_info", tooltip={"mpp.print_placement_info"}, icon=("mpp_print_placement_info_disabled"), icon_enabled=("mpp_print_placement_info_enabled"), } end if layout.restrictions.lane_filling_info_available then values[#values+1] = { value="display_lane_filling", tooltip={"mpp.display_lane_filling"}, icon=("mpp_display_lane_filling_disabled"), icon_enabled=("mpp_display_lane_filling_enabled"), } end if player_data.advanced and layout.restrictions.pipe_available then values[#values+1] = { value="force_pipe_placement", tooltip={"mpp.force_pipe_placement"}, icon=("mpp_force_pipe_disabled"), icon_enabled=("mpp_force_pipe_enabled"), } end if player_data.advanced and false then values[#values+1] = { value="dumb_power_connectivity", tooltip={"mpp.dumb_power_connectivity"}, icon=("entity/medium-power-pole"), } end local misc_section = player_data.gui.section["misc"] misc_section.visible = #values > 0 local table_root = player_data.gui.tables["misc"] create_setting_selector(player_data, table_root, "mpp_toggle", "misc", values) end ---@param player_data PlayerData local function update_blueprint_selection(player_data) local choices = player_data.choices local player_blueprints = player_data.blueprints player_data.gui.section["blueprints"].visible = choices.layout_choice == "blueprints" player_data.gui["blueprint_add_section"].visible = player_data.blueprint_add_mode player_data.gui["blueprint_add_button"].style = style_helper_blueprint_toggle(player_data.blueprint_add_mode) for key, value in pairs(player_blueprints.delete) do value.visible = player_data.blueprint_add_mode end end local function update_debugging_selection(player_data) local is_debugging = not not __DebugAdapter player_data.gui.section["debugging"].visible = is_debugging if not is_debugging then return end ---@type SettingValueEntry[] local values = { { value="draw_clear_rendering", tooltip="Clear rendering", icon=("mpp_no_entity"), }, { value="draw_drill_struct", tooltip="Draw drill struct overlay", icon=("entity/electric-mining-drill"), }, { value="draw_pole_layout", tooltip="Draw power pole layout", icon=("entity/medium-electric-pole"), }, { value="draw_pole_layout_compact", tooltip="Draw power pole layout compact", icon=("entity/medium-electric-pole"), }, { value="draw_built_things", tooltip="Draw built tile values", icon=("mpp_print_placement_info_enabled"), }, { value="draw_drill_convolution", tooltip="Draw a drill convolution preview", icon=("mpp_debugging_grid_convolution"), }, { value="draw_power_grid", tooltip="Draw power grid connectivity", icon=("entity/substation"), }, { value="draw_centricity", tooltip="Draw layout centricity", icon=("mpp_plus"), }, { value="draw_blueprint_data", tooltip="Draw blueprint entities", icon=("item/blueprint"), }, { value="draw_deconstruct_preview", tooltip="Draw deconstruct preview", icon=("item/deconstruction-planner"), }, { value="draw_can_place_entity", tooltip="Draw entity placement checks", icon=("item/blueprint"), }, } local debugging_section = player_data.gui.section["debugging"] debugging_section.visible = #values > 0 local table_root = player_data.gui.tables["debugging"] create_setting_selector(player_data, table_root, "mpp_action", "debugging", values) end ---@param player LuaPlayer local function update_selections(player) ---@type PlayerData local player_data = global.players[player.index] player_data.gui.blueprint_add_button.visible = player_data.choices.layout_choice == "blueprints" mpp_util.update_undo_button(player_data) update_miner_selection(player_data) update_belt_selection(player) update_space_belt_selection(player) update_logistics_selection(player_data) update_pole_selection(player_data) update_blueprint_selection(player_data) update_misc_selection(player) update_debugging_selection(player_data) end ---@param player LuaPlayer function gui.show_interface(player) ---@type LuaGuiElement local frame = player.gui.screen["mpp_settings_frame"] local player_data = global.players[player.index] player_data.blueprint_add_mode = false if frame then frame.visible = true else gui.create_interface(player) end update_selections(player) end ---@param player LuaPlayer local function abort_blueprint_mode(player) local player_data = global.players[player.index] if not player_data.blueprint_add_mode then return end player_data.blueprint_add_mode = false update_blueprint_selection(player_data) local cursor_stack = player.cursor_stack if cursor_stack == nil then return end player.clear_cursor() cursor_stack.set_stack("mining-patch-planner") end ---@param player LuaPlayer function gui.hide_interface(player) ---@type LuaGuiElement local frame = player.gui.screen["mpp_settings_frame"] local player_data = global.players[player.index] player_data.blueprint_add_mode = false if frame then frame.visible = false end end ---@param event EventDataGuiClick local function on_gui_click(event) local player = game.players[event.player_index] ---@type PlayerData local player_data = global.players[event.player_index] local evt_ele_tags = event.element.tags if evt_ele_tags["mpp_advanced_settings"] then abort_blueprint_mode(player) if __DebugAdapter and event.alt then local setting = player.mod_settings["mpp-dump-heuristics-data"].value player.mod_settings["mpp-dump-heuristics-data"] = {value = not setting} player.print("Set dumping option to "..tostring(not setting)) return end local value = not player_data.advanced player_data.advanced = value update_selections(player) player_data.gui["advanced_settings"].style = style_helper_advanced_toggle(value) elseif evt_ele_tags["mpp_entity_filtering_mode"] then abort_blueprint_mode(player) local value = not player_data.entity_filtering_mode player_data.entity_filtering_mode = value update_selections(player) player_data.gui["filtering_settings"].style = style_helper_advanced_toggle(value) elseif evt_ele_tags["mpp_action"] then abort_blueprint_mode(player) local action = evt_ele_tags["mpp_action"] local value = evt_ele_tags["value"] local last_value = player_data.choices[action.."_choice"] if player_data.gui.selections[action][last_value] then player_data.gui.selections[action][last_value].style = style_helper_selection(false) end if event.shift and event.button == defines.mouse_button_type.right and evt_ele_tags.mpp_filterable then local entity = action..":"..value local is_filtered = player_data.filtered_entities[entity] local visible_values = 0 for _, element in pairs(event.element.parent.children) do ---@cast element LuaGuiElement if not element.filtered and element.enabled then visible_values = visible_values + 1 end end if #event.element.parent.children < 2 then player.print({"mpp.msg_print_cant_hide_last_choice"}) elseif is_filtered then player_data.filtered_entities[entity] = nil elseif visible_values > 1 then player_data.filtered_entities[entity] = true else player.print({"mpp.msg_print_cant_hide_last_choice"}) end update_selections(player) return end event.element.style = style_helper_selection(true) player_data.choices[action.."_choice"] = value update_selections(player) elseif evt_ele_tags["mpp_toggle"] then abort_blueprint_mode(player) local action = evt_ele_tags["mpp_toggle"] local value = evt_ele_tags["value"] local last_value = player_data.choices[value.."_choice"] if evt_ele_tags.mpp_icon_enabled then if not last_value then event.element.sprite = evt_ele_tags.mpp_icon_enabled --[[@as string]] else event.element.sprite = evt_ele_tags.mpp_icon_default --[[@as string]] end end player_data.choices[value.."_choice"] = not last_value event.element.style = style_helper_selection(not last_value) if evt_ele_tags.refresh then update_selections(player) end elseif evt_ele_tags["mpp_blueprint_add_mode"] then player_data.blueprint_add_mode = not player_data.blueprint_add_mode player.clear_cursor() if not player_data.blueprint_add_mode then player.cursor_stack.set_stack("mining-patch-planner") end player_data.gui["blueprint_add_section"].visible = player_data.blueprint_add_mode player_data.gui["blueprint_add_button"].style = style_helper_blueprint_toggle(player_data.blueprint_add_mode) update_blueprint_selection(player_data) elseif evt_ele_tags["mpp_blueprint_receptacle"] then local cursor_stack = player.cursor_stack if ( not cursor_stack or not cursor_stack.valid or not cursor_stack.valid_for_read or not cursor_stack.is_blueprint ) then if cursor_stack and not cursor_stack.is_blueprint then player.print({"mpp.msg_blueprint_valid"}) end return nil elseif not mpp_util.validate_blueprint(player, cursor_stack) then return nil end local player_blueprints = player_data.blueprint_items local pending_slot = player_blueprints.find_empty_stack() ---@param bp LuaItemStack local function check_existing(bp) for k, v in pairs(player_data.blueprints.original_id) do if v == bp.item_number then return true end end end if check_existing(cursor_stack) then player.print({"mpp.msg_blueprint_existing"}) return nil end if pending_slot == nil then player_blueprints.resize(#player_blueprints+1--[[@as uint16]]) pending_slot = player_blueprints.find_empty_stack() end if pending_slot then pending_slot.set_stack(cursor_stack) local blueprint_table = player_data.gui.tables["blueprints"] create_blueprint_entry(player_data, blueprint_table, pending_slot, cursor_stack) else player.print({"mpp.msg_blueprint_fatal_error"}, {r=1,g=0,b=0}) end elseif evt_ele_tags["mpp_fake_blueprint_button"] then local button = event.element if button.style.name ~= "mpp_fake_blueprint_button_invalid" then set_player_blueprint(player_data, button) abort_blueprint_mode(player) end elseif evt_ele_tags["mpp_delete_blueprint_button"] then local choices = player_data.choices local deleted_number = evt_ele_tags["mpp_delete_blueprint_button"] local player_blueprints = player_data.blueprints if choices.blueprint_choice and choices.blueprint_choice.item_number == deleted_number then choices.blueprint_choice = nil end player_blueprints.mapping[deleted_number].clear() player_blueprints.flow[deleted_number].destroy() player_blueprints.mapping[deleted_number] = nil player_blueprints.flow[deleted_number] = nil player_blueprints.button[deleted_number] = nil player_blueprints.delete[deleted_number] = nil player_blueprints.cache[deleted_number] = nil player_blueprints.original_id[deleted_number] = nil elseif evt_ele_tags["mpp_undo"] then local state = player_data.last_state if not state then return end algorithm.cleanup_last_state(player_data) return end end script.on_event(defines.events.on_gui_click, on_gui_click) ---@param event EventDataGuiSelectionStateChanged local function on_gui_selection_state_changed(event) local player = game.players[event.player_index] if event.element.tags["mpp_drop_down"] then abort_blueprint_mode(player) ---@type PlayerData local player_data = global.players[event.player_index] local action = event.element.tags["mpp_drop_down"] local value = layouts[event.element.selected_index].name player_data.choices.layout_choice = value update_selections(player) end end script.on_event(defines.events.on_gui_selection_state_changed, on_gui_selection_state_changed) ---@param event EventData.on_gui_elem_changed local function on_gui_elem_changed(event) local element = event.element local player = game.players[event.player_index] ---@type PlayerData local player_data = global.players[event.player_index] local evt_ele_tags = element.tags if evt_ele_tags["mpp_prototype"] then local action = evt_ele_tags.value local old_choice = player_data.choices[action.."_choice"] local choice = element.elem_value element.children[1].visible = not choice player_data.choices[action.."_choice"] = choice or "none" end end script.on_event(defines.events.on_gui_elem_changed, on_gui_elem_changed) return gui