1
0
mirror of https://github.com/Oarcinae/FactorioScenarioMultiplayerSpawn.git synced 2026-04-24 20:44:46 +02:00
Files

623 lines
23 KiB
Lua

-- Contains the GUI for the controlling various settings of the mod.
---Creates the content in the tab used by AddOarcGuiTab
---@param tab_container LuaGuiElement
---@param player LuaPlayer
function CreateSettingsControlsTab(tab_container, player)
if (player.admin) then
local label = AddLabel(tab_container, nil, { "oarc-settings-tab-admin-warning" }, my_warning_style)
else
local label = AddLabel(tab_container, nil, { "oarc-settings-tab-player-warning" }, my_warning_style)
end
local label = AddLabel(tab_container, nil, { "oarc-settings-tab-description" }, my_label_style)
label.style.bottom_padding = 5
label.style.maximal_width = 600
local flow = tab_container.add { type = "flow", direction = "horizontal", }
local scroll_pane_left = flow.add {
type = "scroll-pane",
direction = "vertical",
vertical_scroll_policy = "always",
}
scroll_pane_left.style.maximal_height = GENERIC_GUI_MAX_HEIGHT
scroll_pane_left.style.padding = 5
scroll_pane_left.style.right_margin = 2
CreateModSettingsSection(scroll_pane_left, player)
local scroll_pane_right = flow.add {
type = "scroll-pane",
direction = "vertical",
vertical_scroll_policy = "always",
}
scroll_pane_right.style.maximal_height = GENERIC_GUI_MAX_HEIGHT
scroll_pane_right.style.padding = 5
scroll_pane_right.style.left_margin = 2
CreateSurfaceSettingsSection(scroll_pane_right, player)
AddSpacerLine(scroll_pane_right)
if (player.admin) then
CreateSettingsExportSection(scroll_pane_right, player)
end
end
---Create the content for the mod settings section
---@param container LuaGuiElement
---@param player LuaPlayer
---@return nil
function CreateModSettingsSection(container, player)
AddLabel(container, nil, { "oarc-settings-tab-title-mod-settings" }, my_label_header2_style)
for index,entry in pairs(OCFG_KEYS) do
if (entry.type == "header") then
AddSpacerLine(container)
AddLabel(container, nil, entry.text, "caption_label")
elseif (entry.type == "subheader") then
AddLabel(container, nil, entry.text, "bold_label")
elseif (entry.type == "boolean") then
AddCheckboxSetting(container, index, entry, player.admin)
elseif (entry.type == "string") then
AddTextfieldSetting(container, index, entry, player.admin)
elseif (entry.type == "integer") then
AddIntegerSetting(container, index, entry, player.admin)
elseif (entry.type == "double") then
AddDoubleSetting(container, index, entry, player.admin)
elseif (entry.type == "string-list") then
AddStringListDropdownSetting(container, index, entry, player.admin)
end
end
end
---Create the content for the surface settings section
---@param container LuaGuiElement
---@param player LuaPlayer
---@return nil
function CreateSurfaceSettingsSection(container, player)
AddLabel(container, nil, { "oarc-settings-tab-title-surface" }, my_label_header2_style)
local warning = AddLabel(container, nil, { "oarc-settings-tab-surfaces-warning" }, my_warning_style)
warning.style.maximal_width = 300
--- Create a table with 3 columns. Surface Name, Spawning Enabled, Regrowth Enabled
local surface_table = container.add {
type = "table",
name = "surface_table",
column_count = 4,
style = "bordered_table",
}
--- Add the header row
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-column-header"}, "caption_label")
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-spawning-enabled"}, "caption_label")
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-secondary-enabled"}, "caption_label")
AddLabel(surface_table, nil, {"oarc-settings-tab-surface-regrowth-enabled"}, "caption_label")
--- Add the rows
for name, allowed in pairs(storage.oarc_surfaces) do
AddLabel(surface_table, nil, {"", "[planet=", name, "] ", {"space-location-name."..name}}, my_label_style)
AddSurfaceCheckboxSetting(surface_table, name, "spawn_enabled", allowed.primary, player.admin,
{ "oarc-settings-tab-surface-checkbox-tooltip" })
AddSurfaceCheckboxSetting(surface_table, name, "secondary_enabled", allowed.secondary, player.admin,
{ "oarc-settings-tab-surface-secondary-checkbox-tooltip" })
local surface_regrowth = IsRegrowthEnabledOnSurface(name)
local global_regrowth = storage.ocfg.regrowth.enable_regrowth
AddSurfaceCheckboxSetting(surface_table, name, "regrowth_enabled", surface_regrowth, player.admin and global_regrowth,
{"oarc-settings-tab-surface-regrowth-checkbox-tooltip"})
end
end
---Create the content for the settings export section. Exports the entire storage.ocfg table into a string.
---@param container LuaGuiElement
---@param player LuaPlayer
---@return nil
function CreateSettingsExportSection(container, player)
AddLabel(container, nil, { "oarc-settings-tab-title-export" }, my_label_header2_style)
local horizontal_flow = container.add {
type = "flow",
direction = "horizontal",
}
local export_button = horizontal_flow.add {
type = "button",
caption = { "oarc-settings-tab-export-button" },
style = "green_button",
tooltip = { "oarc-settings-tab-export-button-tooltip" },
tags = {
action = "oarc_settings_tab_right_pane",
setting = "oarc_settings_export"
},
}
local import_button = horizontal_flow.add {
type = "button",
caption = { "oarc-settings-tab-import-button" },
tooltip = { "oarc-settings-tab-import-button-tooltip" },
style = "red_button",
tags = {
action = "oarc_settings_tab_right_pane",
setting = "oarc_settings_import"
},
}
local export_textfield = container.add {
type = "text-box",
name = "export_textfield",
text = " ",
tags = {
action = "oarc_settings_tab_right_pane",
setting = "oarc_settings_textfield"
},
}
export_textfield.style.horizontally_stretchable = true
export_textfield.style.maximal_width = 500
export_textfield.style.minimal_height = 100
end
---Handles the click event for the tab used by AddOarcGuiTab
---@param event EventData.on_gui_click
---@return nil
function SettingsControlsTabGuiClick(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab_left_pane") then return end
local index = gui_elem.tags.setting
local entry = OCFG_KEYS[index]
if (entry.type == "boolean") then
if (entry.mod_key ~= "") then
settings.global[entry.mod_key] = { value = gui_elem.state }
else
SetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys, gui_elem.state)
end
end
end
---Handles the text entry event for the tab used by AddOarcGuiTab
---@param event EventData.on_gui_text_changed
---@return nil
function SettingsControlsTabGuiTextChanged(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab_left_pane") then return end
local index = gui_elem.tags.setting
local value = gui_elem.text
local entry = OCFG_KEYS[index]
if (entry.type == "string") then
gui_elem.style = "invalid_value_textfield"
elseif (entry.type == "integer") then
gui_elem.style = "invalid_value_textfield"
gui_elem.style.width = 50
end
end
---Handles the confirmed text entry event
---@param event EventData.on_gui_confirmed
---@return nil
function SettingsControlsTabGuiTextconfirmed(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab_left_pane") then return end
local index = gui_elem.tags.setting
local value = gui_elem.text
local entry = OCFG_KEYS[index]
if (entry.type == "string") then
if value == "" then -- Force a non-empty string!
value = " "
gui_elem.text = " "
end
gui_elem.style = "textbox"
settings.global[entry.mod_key] = { value = gui_elem.text }
elseif (entry.type == "integer") then
local safe_value = GetSafeIntValueForModSetting(value, entry.mod_key)
if not pcall(function() settings.global[entry.mod_key] = { value = safe_value } end) then
settings.global[entry.mod_key] = { value = prototypes.mod_setting[entry.mod_key].default_value }
log("Error setting value for " .. entry.mod_key .. " to " .. safe_value)
end
gui_elem.text = tostring(settings.global[entry.mod_key].value)
gui_elem.style = "textbox"
gui_elem.style.width = 50
local slider = gui_elem.parent["slider"]
slider.slider_value = settings.global[entry.mod_key].value --[[@as integer]]
elseif (entry.type == "double") then
local safe_value = GetSafeDoubleValueForModSetting(value, entry.mod_key)
if not pcall(function() settings.global[entry.mod_key] = { value = safe_value } end) then
settings.global[entry.mod_key] = { value = prototypes.mod_setting[entry.mod_key].default_value }
log("Error setting value for " .. entry.mod_key .. " to " .. safe_value)
end
gui_elem.text = string.format("%.2f", settings.global[entry.mod_key].value)
gui_elem.style = "textbox"
gui_elem.style.width = 50
local slider = gui_elem.parent["slider"]
slider.slider_value = settings.global[entry.mod_key].value --[[@as number]]
end
end
---Handles slider value changes
---@param event EventData.on_gui_value_changed
---@return nil
function SettingsControlsTabGuiValueChanged(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab_slider") then return end
local index = gui_elem.tags.setting
local value = gui_elem.slider_value
local entry = OCFG_KEYS[index]
if (entry.type == "integer") then
local textfield = gui_elem.parent["textfield"]
settings.global[entry.mod_key] = { value = value } -- Assumes that the slider can only produce valid inputs!
textfield.text = tostring(value)
elseif (entry.type == "double") then
local textfield = gui_elem.parent["textfield"]
settings.global[entry.mod_key] = { value = value } -- Assumes that the slider can only produce valid inputs!
textfield.text = string.format("%.2f", value)
end
end
---Makes sure a given value is within the min/max range of a mod setting
---@param input string|number|integer
---@param mod_key string
---@return integer
function GetSafeIntValueForModSetting(input, mod_key)
local value_num = tonumber(input)
if not value_num then
value_num = tonumber(prototypes.mod_setting[mod_key].default_value)
else
local minimum = prototypes.mod_setting[mod_key].minimum_value
local maximum = prototypes.mod_setting[mod_key].maximum_value
if minimum ~= nil then
value_num = math.max(value_num, minimum)
end
if maximum ~= nil then
value_num = math.min(value_num, maximum)
end
value_num = math.floor(value_num)
end
return value_num --[[@as integer]]
end
---Makes sure a given value is within the min/max range of a mod setting (double)
---@param input string|number|integer
---@param mod_key string
---@return number
function GetSafeDoubleValueForModSetting(input, mod_key)
local value_num = tonumber(input)
if not value_num then
value_num = tonumber(prototypes.mod_setting[mod_key].default_value)
else
local minimum = prototypes.mod_setting[mod_key].minimum_value
local maximum = prototypes.mod_setting[mod_key].maximum_value
if minimum ~= nil then
value_num = math.max(value_num, minimum)
end
if maximum ~= nil then
value_num = math.min(value_num, maximum)
end
end
return value_num --[[@as number]]
end
---Creates a checkbox setting
---@param tab_container LuaGuiElement
---@param index string
---@param entry OarcSettingsLookup
---@param enabled boolean
---@return nil
function AddCheckboxSetting(tab_container, index, entry, enabled)
local caption, tooltip = GetCaptionAndTooltip(entry)
tab_container.add{
type = "checkbox",
caption = caption,
state = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
enabled = enabled,
tooltip = tooltip,
tags = { action = "oarc_settings_tab_left_pane", setting = index },
}
end
---Gets the caption and tooltip for a setting entry whether it is a mod setting or not.
---@param entry OarcSettingsLookup
---@return LocalisedString, LocalisedString
function GetCaptionAndTooltip(entry)
local caption
local tooltip
if (entry.mod_key == "") then
caption = entry.caption
tooltip = entry.tooltip
else
caption = { "mod-setting-name."..entry.mod_key }
tooltip = { "mod-setting-description."..entry.mod_key }
end
return caption, tooltip
end
---Creates a textfield setting
---@param tab_container LuaGuiElement
---@param index string
---@param entry OarcSettingsLookup
---@param enabled boolean
---@return nil
function AddTextfieldSetting(tab_container, index, entry, enabled)
local horizontal_flow = tab_container.add {
type = "flow",
direction = "horizontal",
}
horizontal_flow.add {
type = "label",
caption = { "mod-setting-name."..entry.mod_key },
tooltip = { "mod-setting-description."..entry.mod_key },
}
local dragger = horizontal_flow.add {
type = "empty-widget",
}
dragger.style.horizontally_stretchable = true
local tooltip = {"", {"mod-setting-description."..entry.mod_key }, " ", { "oarc-settings-tab-text-field-enter-tooltip" }}
horizontal_flow.add {
type = "textfield",
caption = { "mod-setting-name."..entry.mod_key },
text = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
enabled = enabled,
tooltip = tooltip,
tags = { action = "oarc_settings_tab_left_pane", setting = index },
}
end
---Creates an integer setting
---@param tab_container LuaGuiElement
---@param index string
---@param entry OarcSettingsLookup
---@param enabled boolean
---@return nil
function AddIntegerSetting(tab_container, index, entry, enabled)
local horizontal_flow = tab_container.add {
type = "flow",
direction = "horizontal",
}
horizontal_flow.add {
type = "label",
caption = { "mod-setting-name."..entry.mod_key },
tooltip = { "mod-setting-description."..entry.mod_key },
}
local dragger = horizontal_flow.add {
type = "empty-widget",
}
dragger.style.horizontally_stretchable = true
local slider = horizontal_flow.add {
name = "slider",
type = "slider",
minimum_value = prototypes.mod_setting[entry.mod_key].minimum_value,
maximum_value = prototypes.mod_setting[entry.mod_key].maximum_value,
value = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
enabled = enabled,
tooltip = { "mod-setting-description."..entry.mod_key },
tags = { action = "oarc_settings_tab_slider", setting = index },
discrete_values = true,
value_step = 1,
}
local tooltip = {"", {"mod-setting-description."..entry.mod_key }, " ", { "oarc-settings-tab-text-field-enter-tooltip" }}
local textfield = horizontal_flow.add {
name = "textfield",
type = "textfield",
numeric = true,
caption = { "mod-setting-name."..entry.mod_key },
text = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
enabled = enabled,
tooltip = tooltip,
tags = { action = "oarc_settings_tab_left_pane", setting = index },
}
textfield.style.width = 50
end
---Creates a double setting
---@param tab_container LuaGuiElement
---@param index string
---@param entry OarcSettingsLookup
---@param enabled boolean
---@return nil
function AddDoubleSetting(tab_container, index, entry, enabled)
local horizontal_flow = tab_container.add {
type = "flow",
direction = "horizontal",
}
horizontal_flow.add {
type = "label",
caption = { "mod-setting-name."..entry.mod_key },
tooltip = { "mod-setting-description."..entry.mod_key },
}
local dragger = horizontal_flow.add {
type = "empty-widget",
}
dragger.style.horizontally_stretchable = true
local slider = horizontal_flow.add {
name = "slider",
type = "slider",
minimum_value = prototypes.mod_setting[entry.mod_key].minimum_value,
maximum_value = prototypes.mod_setting[entry.mod_key].maximum_value,
value = GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys),
enabled = enabled,
tooltip = { "mod-setting-description."..entry.mod_key },
tags = { action = "oarc_settings_tab_slider", setting = index },
discrete_values = false,
value_step = 0.01,
}
local tooltip = {"", {"mod-setting-description."..entry.mod_key }, " ", { "oarc-settings-tab-text-field-enter-tooltip" }}
local textfield = horizontal_flow.add {
name = "textfield",
type = "textfield",
numeric = true,
allow_decimal = true,
caption = { "mod-setting-name."..entry.mod_key },
text = string.format("%.2f", GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys)),
enabled = enabled,
tooltip = tooltip,
tags = { action = "oarc_settings_tab_left_pane", setting = index },
}
textfield.style.width = 50
end
---Create a dropdown setting for a string setting with allowed_values set
---@param tab_container LuaGuiElement
---@param index string
---@param entry OarcSettingsLookup
---@param enabled boolean
---@return nil
function AddStringListDropdownSetting(tab_container, index, entry, enabled)
local horizontal_flow = tab_container.add {
type = "flow",
direction = "horizontal",
}
horizontal_flow.add {
type = "label",
caption = { "mod-setting-name."..entry.mod_key },
tooltip = { "mod-setting-description."..entry.mod_key },
}
local dragger = horizontal_flow.add {
type = "empty-widget",
}
dragger.style.horizontally_stretchable = true
local allowed_values = prototypes.mod_setting[entry.mod_key].allowed_values --[[@as string[] ]]
local selected_index = 1
for i,v in pairs(allowed_values) do
if (v == GetGlobalOarcConfigUsingKeyTable(entry.ocfg_keys)) then
selected_index = i
break
end
end
local dropdown = horizontal_flow.add {
type = "drop-down",
items = allowed_values,
selected_index = selected_index,
enabled = enabled,
tooltip = { "mod-setting-description."..entry.mod_key },
tags = { action = "oarc_settings_tab_left_pane", setting = index },
}
end
---Creates a checkbox setting for surface related settings.
---@param parent LuaGuiElement
---@param surface_name string
---@param setting_name string
---@param state boolean
---@param admin boolean
---@param tooltip LocalisedString
---@return nil
function AddSurfaceCheckboxSetting(parent, surface_name, setting_name, state, admin, tooltip)
parent.add{
name = surface_name.."_"..setting_name,
type = "checkbox",
state = state,
tags = { action = "oarc_settings_tab_right_pane", setting = setting_name, surface = surface_name },
enabled = admin,
tooltip = tooltip,
}
end
---Handles the click event for surface related settings
---@param event EventData.on_gui_click
---@return nil
function SettingsSurfaceControlsTabGuiClick(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab_right_pane") then return end
local setting_name = gui_elem.tags.setting
if (setting_name == "spawn_enabled") then
local surface_name = gui_elem.tags.surface --[[@as string]]
storage.oarc_surfaces[surface_name].primary = gui_elem.state
if (#GetAllowedSurfaces() == 0) then
log("Warning - GetAllowedSurfaces() - No surfaces found! Forcing default surface!")
storage.oarc_surfaces[storage.ocfg.gameplay.default_surface].primary = true
event.element.parent[storage.ocfg.gameplay.default_surface.."_spawn_enabled"].state = true
end
elseif (setting_name == "secondary_enabled") then
local surface_name = gui_elem.tags.surface --[[@as string]]
storage.oarc_surfaces[surface_name].secondary = gui_elem.state
elseif (setting_name == "regrowth_enabled") then
local surface_name = gui_elem.tags.surface --[[@as string]]
if (gui_elem.state) then
if not IsRegrowthEnabledOnSurface(surface_name) then
RegrowthEnableSurface(surface_name)
end
else
if IsRegrowthEnabledOnSurface(surface_name) then
RegrowthDisableSurface(surface_name)
end
end
elseif (setting_name == "oarc_settings_textfield") then
gui_elem.select_all() -- Select all text when clicked for easy copy-pasting
elseif (setting_name == "oarc_settings_export") then
log("Exported settings!")
local export_textfield = gui_elem.parent.parent["export_textfield"]
export_textfield.text = serpent.block(storage.ocfg, {compact = true, sparse = true})
elseif (setting_name == "oarc_settings_import") then
local player = game.players[event.player_index]
local export_textfield = gui_elem.parent.parent["export_textfield"]
local import_text = export_textfield.text
local ok, copy = serpent.load(import_text)
if (not ok) or (type(copy) ~= "table") or (next(copy) == nil) then
log("Error importing settings!")
SendErrorMsg(player, "Error importing settings!")
else
storage.ocfg = table.deepcopy(copy)
ValidateSettings() -- Some basic validation, not 100% foolproof
SyncModSettingsToOCFG() -- Sync the mod settings.
script.raise_event("oarc-mod-on-config-changed", {})
log("Imported settings!")
CompatSend(player, "Imported settings!")
OarcGuiRefreshContent(player)
end
end
end
---Handles dropdown selection events
---@param event EventData.on_gui_selection_state_changed
---@return nil
function SettingsControlsTabGuiSelectionStateChanged(event)
if not (event.element.valid) then return end
local gui_elem = event.element
if (gui_elem.tags.action ~= "oarc_settings_tab_left_pane") then return end
local index = gui_elem.tags.setting
local entry = OCFG_KEYS[index]
if (entry.type == "string-list") then
settings.global[entry.mod_key] = { value = gui_elem.items[gui_elem.selected_index] }
end
end