Track LocalMods as part of monolith
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
-- why barotrauma's GUI libraries don't have this implemented by default? this is stupid
|
||||
|
||||
local function updateServerMessageScrollBasedOnCaret(textBox, listBox)
|
||||
local caretY = textBox.CaretScreenPos.Y
|
||||
local bottomCaretExtent = textBox.Font.LineHeight * 1.5
|
||||
local topCaretExtent = -textBox.Font.LineHeight * 0.5
|
||||
|
||||
if caretY + bottomCaretExtent > listBox.Rect.Bottom then
|
||||
listBox.ScrollBar.BarScroll = (caretY - textBox.Rect.Top - listBox.Rect.Height + bottomCaretExtent)
|
||||
/ (textBox.Rect.Height - listBox.Rect.Height)
|
||||
elseif caretY + topCaretExtent < listBox.Rect.Top then
|
||||
listBox.ScrollBar.BarScroll = (caretY - textBox.Rect.Top + topCaretExtent)
|
||||
/ (textBox.Rect.Height - listBox.Rect.Height)
|
||||
end
|
||||
end
|
||||
|
||||
local function CreateMultiLineTextBox(rectransform, text, size)
|
||||
local multineListBox = GUI.ListBox(GUI.RectTransform(Vector2(1, size or 0.2), rectransform))
|
||||
|
||||
local textBox = GUI.TextBox(
|
||||
GUI.RectTransform(Vector2(1, 1), multineListBox.Content.RectTransform),
|
||||
text,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
true,
|
||||
"GUITextBoxNoBorder"
|
||||
)
|
||||
|
||||
textBox.add_OnSelected(function()
|
||||
updateServerMessageScrollBasedOnCaret(textBox, multineListBox)
|
||||
end)
|
||||
|
||||
textBox.OnTextChangedDelegate = function()
|
||||
local textSize = textBox.Font.MeasureString(textBox.WrappedText)
|
||||
textBox.RectTransform.NonScaledSize =
|
||||
Point(textBox.RectTransform.NonScaledSize.X, math.max(multineListBox.Content.Rect.Height, textSize.Y + 10))
|
||||
multineListBox.UpdateScrollBarSize()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
textBox.OnEnterPressed = function()
|
||||
local str = textBox.Text
|
||||
local caretIndex = textBox.CaretIndex
|
||||
|
||||
textBox.Text = str:sub(1, caretIndex) .. "\n" .. str:sub(caretIndex + 1)
|
||||
textBox.CaretIndex = caretIndex + 1
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return textBox
|
||||
end
|
||||
|
||||
return CreateMultiLineTextBox
|
||||
+580
@@ -0,0 +1,580 @@
|
||||
--easysettings by Evil Factory
|
||||
local easySettings = dofile(NT.Path .. "/Lua/Scripts/Client/easysettings.lua")
|
||||
local MultiLineTextBox = dofile(NT.Path .. "/Lua/Scripts/Client/MultiLineTextBox.lua")
|
||||
local GUIComponent = LuaUserData.CreateStatic("Barotrauma.GUIComponent")
|
||||
local configUI
|
||||
|
||||
local BaseConfigPages = {
|
||||
{ name = "Item Prices", key = "prices" },
|
||||
{ name = "Item Availability", key = "availability" },
|
||||
}
|
||||
|
||||
-- Did you know the default background colour is not true black?
|
||||
local Transparent = Color(2, 2, 2)
|
||||
-- Barotrauma beige
|
||||
local DefaultTextColour = Color(210, 200, 154)
|
||||
local ExpansionNameForUI = {}
|
||||
|
||||
local function CommaStringToTable(str)
|
||||
local tbl = {}
|
||||
|
||||
for word in string.gmatch(str, "([^,]+)") do
|
||||
table.insert(tbl, word)
|
||||
end
|
||||
|
||||
return tbl
|
||||
end
|
||||
|
||||
-- Function that autofills the addon dropdown
|
||||
-- We also automatically make the UI display the names as "NT [mod]" to match their Content Package / Steam Workhop name to minimise confusion.
|
||||
local function PopulateDropdown(dropdown)
|
||||
ExpansionNameForUI = {}
|
||||
-- Goofy but it works!
|
||||
-- Ensure Neurotrauma is on top by just loading it first
|
||||
for _, expansion in ipairs(NTConfig.Expansions) do
|
||||
if expansion.Name == "Neurotrauma" then
|
||||
local name = tostring(expansion.Name)
|
||||
local uiName = name
|
||||
|
||||
table.insert(ExpansionNameForUI, { uiName, name, type = "expansion" })
|
||||
dropdown.AddItem(uiName)
|
||||
end
|
||||
end
|
||||
|
||||
-- Then get all the base pages (expansion proof!)
|
||||
for _, page in ipairs(BaseConfigPages) do
|
||||
table.insert(ExpansionNameForUI, { page.name, page.key, type = "page" })
|
||||
dropdown.AddItem(page.name)
|
||||
end
|
||||
|
||||
-- Then dump all other expansions in there
|
||||
for _, expansion in ipairs(NTConfig.Expansions) do
|
||||
if expansion.Name ~= "Neurotrauma" then
|
||||
local name = tostring(expansion.Name)
|
||||
-- First, we make the UI Name the same as their expansion name
|
||||
local uiName = name
|
||||
-- Add NT at the front if it does not already have that
|
||||
if not string.find(uiName, "^NT%s") then uiName = "NT " .. uiName end
|
||||
-- Add them to a lookup table for later
|
||||
table.insert(ExpansionNameForUI, { uiName, name, type = "expansion" })
|
||||
-- Finally, add it to the dropdown menu
|
||||
dropdown.AddItem(uiName)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: Readd the difficulty calculation I kinda dont care about it right now so it can fuck off
|
||||
|
||||
-- Function to determine how the layout should be structured
|
||||
local function PrebuildConfigLayout(entries, selectedExpansion)
|
||||
-- This table will contain subtables that determine how the ConstructUI function actually 'constructs' the UI.
|
||||
-- These chunks can be gone over using Ipairs to ensure the order is correct (a previous test made unrelated settings render together)
|
||||
local LayoutChunks = {}
|
||||
local CurrentGroup = nil
|
||||
local lastType = nil
|
||||
local selectedKey = nil
|
||||
local selectedType = nil
|
||||
|
||||
-- Grab actual expansion name based on their UIName
|
||||
for _, uiEntry in ipairs(ExpansionNameForUI) do
|
||||
if uiEntry[1] == selectedExpansion then
|
||||
selectedKey = uiEntry[2]
|
||||
selectedType = uiEntry.type
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
for key, entry in pairs(entries) do
|
||||
if selectedType == "page" then
|
||||
-- Get entries with this page set
|
||||
if not entry.page or entry.page ~= selectedKey then goto continue end
|
||||
elseif selectedType == "expansion" then
|
||||
-- Get expansion entries that do NOT have a page set (so other expansion can force their price changes into the combined page, for example)
|
||||
if entry.expansion ~= selectedKey or entry.page ~= nil then goto continue end
|
||||
end
|
||||
|
||||
-- Automatically add some white space between unique item types to prevent bleeding
|
||||
if entry.type ~= lastType and lastType ~= nil then table.insert(LayoutChunks, { type = "spacer" }) end
|
||||
|
||||
lastType = entry.type
|
||||
|
||||
-- Categories are always standalone and should be put above the settings they govern
|
||||
-- Interrupt a group if it exists
|
||||
if entry.type == "category" then
|
||||
CurrentGroup = nil
|
||||
table.insert(LayoutChunks, { type = "category", entry = entry })
|
||||
|
||||
-- Floats / Strings until now were just using up way too much screen space. We want to put these into a LayoutGroup together to maximise screen usage.
|
||||
-- If entries have 'group=true', then all entries of the same type that follow one another will be grouped together. If there are other entry types (like categories) in-between, they will not.
|
||||
-- This prevents unrelated settings from merging together + adds reverse-compat
|
||||
elseif entry.type == "float" and entry.group then
|
||||
-- Make a group and keep adding config entries that come after this as long as the criteria stands
|
||||
if not CurrentGroup or CurrentGroup.type ~= "float_group" then
|
||||
CurrentGroup = { type = "float_group", items = {} }
|
||||
table.insert(LayoutChunks, CurrentGroup)
|
||||
end
|
||||
|
||||
table.insert(CurrentGroup.items, { key = key, entry = entry })
|
||||
elseif entry.type == "string" and entry.group then
|
||||
if not CurrentGroup or CurrentGroup.type ~= "string_group" then
|
||||
CurrentGroup = { type = "string_group", items = {} }
|
||||
table.insert(LayoutChunks, CurrentGroup)
|
||||
end
|
||||
|
||||
table.insert(CurrentGroup.items, { key = key, entry = entry })
|
||||
|
||||
-- Anything else is anything that shouldn't be grouped. Interrupt a group if it exists and add the setting as standalone.
|
||||
else
|
||||
CurrentGroup = nil
|
||||
table.insert(LayoutChunks, { type = "standalone", key = key, entry = entry })
|
||||
end
|
||||
|
||||
::continue::
|
||||
end
|
||||
|
||||
return LayoutChunks
|
||||
end
|
||||
|
||||
local function PopulateSettingsIntoUI(list, selectedExpansion)
|
||||
list.Content:ClearChildren()
|
||||
|
||||
-- Infotext (should always be shown)
|
||||
local tb_BasicConfigInfo = GUI.TextBlock(
|
||||
GUI.RectTransform(Vector2(1, 0.1), list.Content.RectTransform),
|
||||
"Server config can be changed by owner or a client with manage settings permission. If the server doesn't allow writing into the config folder, then it must be edited manually.",
|
||||
DefaultTextColour,
|
||||
nil,
|
||||
GUI.Alignment.Center,
|
||||
true,
|
||||
nil,
|
||||
Transparent
|
||||
)
|
||||
|
||||
-- Prevent textblocks from lighting up the background when clicked / changing the cursor on hover.
|
||||
tb_BasicConfigInfo.CanBeFocused = false
|
||||
|
||||
local LayoutChunks = PrebuildConfigLayout(NTConfig.Entries, selectedExpansion)
|
||||
-- Go over the pre-generated Layout table and construct settings procedurally
|
||||
for _, chunk in ipairs(LayoutChunks) do
|
||||
-- Categories
|
||||
if chunk.type == "category" then
|
||||
local tb_ProceduralCategoryHeader = GUI.TextBlock(
|
||||
GUI.RectTransform(Vector2(1, 0.09), list.Content.RectTransform),
|
||||
chunk.entry.name,
|
||||
DefaultTextColour,
|
||||
GUI.GUIStyle.LargeFont,
|
||||
GUI.Alignment.BottomCenter,
|
||||
true,
|
||||
nil,
|
||||
Transparent
|
||||
)
|
||||
|
||||
tb_ProceduralCategoryHeader.CanBeFocused = false
|
||||
|
||||
-- Standalone items
|
||||
elseif chunk.type == "standalone" then
|
||||
local key = chunk.key
|
||||
local entry = chunk.entry
|
||||
|
||||
-- Ungrouped float
|
||||
if entry.type == "float" then
|
||||
local minrange = (entry.range and entry.range[1]) or ""
|
||||
-- This is fucking stupid. If the default value is the same as the minimum value of a scalar, it bugs and displays 0.
|
||||
-- So, we set the minimum to 0.99 and just render it like it were 1
|
||||
if minrange == 0.99 then minrange = 1 end
|
||||
|
||||
local maxrange = (entry.range and entry.range[2]) or ""
|
||||
|
||||
local rect = GUI.RectTransform(Vector2(1, 0.04), list.Content.RectTransform)
|
||||
|
||||
local tb_EntryInformation = GUI.TextBlock(
|
||||
rect,
|
||||
entry.name .. " (" .. minrange .. "-" .. maxrange .. ")",
|
||||
DefaultTextColour,
|
||||
nil,
|
||||
GUI.Alignment.Center,
|
||||
true,
|
||||
nil,
|
||||
Transparent
|
||||
)
|
||||
|
||||
tb_EntryInformation.CanBeFocused = false
|
||||
|
||||
if entry.description then tb_EntryInformation.ToolTip = entry.description end
|
||||
|
||||
local scalar =
|
||||
GUI.NumberInput(GUI.RectTransform(Vector2(1, 0.08), list.Content.RectTransform), NumberType.Float)
|
||||
|
||||
scalar.valueStep = 0.1
|
||||
scalar.MinValueFloat = entry.range and entry.range[1] or 0
|
||||
scalar.MaxValueFloat = entry.range and entry.range[2] or 100
|
||||
scalar.FloatValue = NTConfig.Get(key, 1)
|
||||
|
||||
scalar.OnValueChanged = function()
|
||||
NTConfig.Set(key, scalar.FloatValue)
|
||||
end
|
||||
|
||||
-- Bool
|
||||
elseif entry.type == "bool" then
|
||||
local rect = GUI.RectTransform(Vector2(0.5, 1), list.Content.RectTransform)
|
||||
local toggle = GUI.TickBox(rect, entry.name)
|
||||
|
||||
if entry.description then toggle.ToolTip = entry.description end
|
||||
|
||||
toggle.Selected = NTConfig.Get(key, false)
|
||||
|
||||
toggle.OnSelected = function()
|
||||
NTConfig.Set(key, toggle.State == GUIComponent.ComponentState.Selected)
|
||||
end
|
||||
|
||||
-- String (a textblock to input)
|
||||
elseif entry.type == "string" then
|
||||
local style = ""
|
||||
if entry.style ~= nil then style = " (" .. entry.style .. ")" end
|
||||
|
||||
local rect = GUI.RectTransform(Vector2(1, 0.05), list.Content.RectTransform)
|
||||
|
||||
local tb_StringInformation = GUI.TextBlock(
|
||||
rect,
|
||||
entry.name .. style,
|
||||
DefaultTextColour,
|
||||
nil,
|
||||
GUI.Alignment.Center,
|
||||
true,
|
||||
nil,
|
||||
Transparent
|
||||
)
|
||||
|
||||
-- By default, don't change cursor on hovering
|
||||
tb_StringInformation.CanBeFocused = false
|
||||
|
||||
-- If there's a tooltip, set it and re-enable hovering
|
||||
if entry.description then
|
||||
tb_StringInformation.ToolTip = entry.description
|
||||
tb_StringInformation.CanBeFocused = true
|
||||
end
|
||||
|
||||
local stringinput
|
||||
|
||||
-- Make MultiLineTextBox the default, but now allow normal ones too. A single line entry does not need a multi-line textblock.
|
||||
if entry.noMLTB == true then
|
||||
stringinput = GUI.TextBox(
|
||||
GUI.RectTransform(Vector2(1, entry.boxsize), list.Content.RectTransform),
|
||||
"",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
true
|
||||
)
|
||||
else
|
||||
stringinput = MultiLineTextBox(list.Content.RectTransform, "", entry.boxsize)
|
||||
end
|
||||
|
||||
stringinput.Text = table.concat(entry.value, ",")
|
||||
|
||||
stringinput.OnTextChangedDelegate = function(textBox)
|
||||
entry.value = CommaStringToTable(textBox.Text)
|
||||
end
|
||||
end
|
||||
|
||||
-- Auto-added empty space
|
||||
elseif chunk.type == "spacer" then
|
||||
GUI.LayoutGroup(GUI.RectTransform(Vector2(1, 0.02), list.Content.RectTransform), false)
|
||||
|
||||
-- Grouped Floats
|
||||
elseif chunk.type == "float_group" then
|
||||
local MaxPerRow = 2
|
||||
local row = nil
|
||||
local count = 0
|
||||
|
||||
for _, item in ipairs(chunk.items) do
|
||||
-- Make a new LayoutGroup to add entries into everytime the MaxPerRow is hit (default of 2)
|
||||
if not row or (count % MaxPerRow == 0) then
|
||||
row = GUI.LayoutGroup(GUI.RectTransform(Vector2(1, 0.09), list.Content.RectTransform), true)
|
||||
end
|
||||
|
||||
-- Safety check!
|
||||
if not row then return end
|
||||
|
||||
-- This determines how the space in the UI is used, tied together to make fucking around a bit easier
|
||||
row.RelativeSpacing = 0.01
|
||||
|
||||
local Text_space = 0.53
|
||||
local Scalar_space = 0.30
|
||||
local Reset_space = 0.07
|
||||
|
||||
local baseWidth = 1 / MaxPerRow
|
||||
|
||||
local textcellwidth = baseWidth * Text_space
|
||||
local scalarcellwidth = baseWidth * Scalar_space
|
||||
local resetcellwidth = baseWidth * Reset_space
|
||||
|
||||
local key = item.key
|
||||
local entry = item.entry
|
||||
local resetButton
|
||||
|
||||
-- Make each subdivided part of the group their own
|
||||
-- Part of the group that holds text
|
||||
local textcell = GUI.LayoutGroup(GUI.RectTransform(Vector2(textcellwidth, 1), row.RectTransform), false)
|
||||
-- Part of the group that holds the numberinput box
|
||||
local scalarcell =
|
||||
GUI.LayoutGroup(GUI.RectTransform(Vector2(scalarcellwidth, 1), row.RectTransform), false)
|
||||
-- In case a reset button is set
|
||||
local resetbuttoncell =
|
||||
GUI.LayoutGroup(GUI.RectTransform(Vector2(resetcellwidth, 0.59), row.RectTransform), false)
|
||||
-- Relativespacing but larger space for the center instead of everywhere
|
||||
local additionalspacecell =
|
||||
GUI.LayoutGroup(GUI.RectTransform(Vector2(resetcellwidth, 0.59), row.RectTransform), false)
|
||||
|
||||
local minrange = entry.range and entry.range[1] or ""
|
||||
if minrange == 0.99 then minrange = 1 end
|
||||
|
||||
local maxrange = entry.range and entry.range[2] or ""
|
||||
|
||||
local tb_EntryInformation = GUI.TextBlock(
|
||||
GUI.RectTransform(Vector2(1, 0.7), textcell.RectTransform),
|
||||
entry.name .. " (" .. minrange .. "-" .. maxrange .. ")",
|
||||
DefaultTextColour,
|
||||
nil,
|
||||
GUI.Alignment.Center,
|
||||
true,
|
||||
nil,
|
||||
Transparent
|
||||
)
|
||||
|
||||
tb_EntryInformation.CanBeFocused = false
|
||||
|
||||
local scalar =
|
||||
GUI.NumberInput(GUI.RectTransform(Vector2(1, 0.6), scalarcell.RectTransform), NumberType.Float)
|
||||
scalar.PlusButton.RectTransform.RelativeSize = Vector2(1, 0.5)
|
||||
scalar.MinusButton.RectTransform.RelativeSize = Vector2(1, 0.5)
|
||||
|
||||
scalar.valueStep = 0.1
|
||||
scalar.MinValueFloat = entry.range and entry.range[1] or 0
|
||||
scalar.MaxValueFloat = entry.range and entry.range[2] or 100
|
||||
scalar.FloatValue = NTConfig.Get(key, 1)
|
||||
|
||||
scalar.OnValueChanged = function()
|
||||
NTConfig.Set(key, scalar.FloatValue)
|
||||
end
|
||||
|
||||
-- Leftover space in the Row goes to the reset button if enabled
|
||||
if entry.resettable then
|
||||
resetButton = GUI.Button(
|
||||
GUI.RectTransform(Vector2(1, 1), resetbuttoncell.RectTransform, GUI.Anchor.BottomLeft),
|
||||
GUI.Alignment.BottomLeft,
|
||||
nil,
|
||||
Transparent
|
||||
)
|
||||
-- Give the reset button a sprite that matches its function (yoinked from basegame)
|
||||
local resetButtonStyle =
|
||||
GUI.Image(GUI.RectTransform(Vector2(1, 1), resetButton.RectTransform), "GUIButtonRefresh")
|
||||
resetButtonStyle.ToolTip = "Reset to default"
|
||||
|
||||
-- On button press, fetch default value.
|
||||
resetButton.OnClicked = function()
|
||||
local defaultValue = entry.default
|
||||
scalar.FloatValue = defaultValue
|
||||
NTConfig.Set(key, defaultValue)
|
||||
end
|
||||
end
|
||||
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
-- Grouped Strings
|
||||
elseif chunk.type == "string_group" then
|
||||
local MaxPerRow = 2
|
||||
local row = nil
|
||||
local count = 0
|
||||
|
||||
for _, item in ipairs(chunk.items) do
|
||||
-- Make a new LayoutGroup to add entries into everytime the MaxPerRow is hit (default of 2)
|
||||
if not row or (count % MaxPerRow == 0) then
|
||||
row = GUI.LayoutGroup(GUI.RectTransform(Vector2(1, 0.09), list.Content.RectTransform), true)
|
||||
end
|
||||
|
||||
-- Safety check!
|
||||
if not row then return end
|
||||
|
||||
row.RelativeSpacing = 0.01
|
||||
|
||||
local Text_space = 0.50
|
||||
-- Any less than 0.33 per string and the max value (255,255,255) or 3 digits per value will bleed (at 1920 * 1080 default)
|
||||
local String_space = 0.33
|
||||
local Reset_space = 0.07
|
||||
|
||||
local baseWidth = 1 / MaxPerRow
|
||||
|
||||
local textcellwidth = baseWidth * Text_space
|
||||
local scalarcellwidth = baseWidth * String_space
|
||||
local resetcellwidth = baseWidth * Reset_space
|
||||
|
||||
local key = item.key
|
||||
local entry = item.entry
|
||||
local resetButton
|
||||
|
||||
-- Make each subdivided part of the group their own
|
||||
-- Part of the group that holds text
|
||||
local textcell = GUI.LayoutGroup(GUI.RectTransform(Vector2(textcellwidth, 1), row.RectTransform), false)
|
||||
-- Part of the group that holds the input box
|
||||
local stringinputcell =
|
||||
GUI.LayoutGroup(GUI.RectTransform(Vector2(scalarcellwidth, 1), row.RectTransform), false)
|
||||
-- In case a reset button is set
|
||||
local resetbuttoncell =
|
||||
GUI.LayoutGroup(GUI.RectTransform(Vector2(resetcellwidth, 0.45), row.RectTransform), false)
|
||||
-- Relativespacing but larger space for the center instead of everywhere
|
||||
local additionalspacecell =
|
||||
GUI.LayoutGroup(GUI.RectTransform(Vector2(resetcellwidth, 0.59), row.RectTransform), false)
|
||||
|
||||
local style = ""
|
||||
if entry.style ~= nil then style = " (" .. entry.style .. ")" end
|
||||
|
||||
local tb_StringInformation = GUI.TextBlock(
|
||||
GUI.RectTransform(Vector2(1, 0.4), textcell.RectTransform),
|
||||
entry.name .. style,
|
||||
DefaultTextColour,
|
||||
nil,
|
||||
GUI.Alignment.Center,
|
||||
true,
|
||||
nil,
|
||||
Transparent
|
||||
)
|
||||
|
||||
tb_StringInformation.CanBeFocused = false
|
||||
|
||||
if entry.description then
|
||||
tb_StringInformation.ToolTip = entry.description
|
||||
tb_StringInformation.CanBeFocused = true
|
||||
end
|
||||
|
||||
local stringinput
|
||||
|
||||
-- Not all strings need a MultiLineTextBox, especially since they can just yoink the mouse cursor while scrolling.
|
||||
-- Make MLTB's a toggleable option
|
||||
if entry.noMLTB == true then
|
||||
stringinput = GUI.TextBox(
|
||||
GUI.RectTransform(
|
||||
Vector2(1, entry.boxsize),
|
||||
stringinputcell.RectTransform,
|
||||
GUI.Anchor.CenterLeft
|
||||
),
|
||||
"",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
true
|
||||
)
|
||||
else
|
||||
stringinput = MultiLineTextBox(stringinputcell.RectTransform, "", entry.boxsize)
|
||||
end
|
||||
|
||||
stringinput.Text = table.concat(entry.value, ",")
|
||||
|
||||
stringinput.OnTextChangedDelegate = function(textBox)
|
||||
entry.value = CommaStringToTable(textBox.Text)
|
||||
end
|
||||
|
||||
-- Leftover space in the Row goes to the reset button if enabled
|
||||
if entry.resettable then
|
||||
resetButton = GUI.Button(
|
||||
GUI.RectTransform(Vector2(1, 1), resetbuttoncell.RectTransform),
|
||||
GUI.Alignment.CenterRight,
|
||||
nil,
|
||||
Transparent
|
||||
)
|
||||
-- Give the reset button a sprite that matches its function (yoinked from basegame)
|
||||
local resetButtonStyle =
|
||||
GUI.Image(GUI.RectTransform(Vector2(1, 1), resetButton.RectTransform), "GUIButtonRefresh")
|
||||
resetButtonStyle.ToolTip = "Reset to default"
|
||||
|
||||
-- On button press, fetch default value.
|
||||
resetButton.OnClicked = function()
|
||||
entry.value = entry.default
|
||||
stringinput.Text = table.concat(entry.value, ",")
|
||||
end
|
||||
end
|
||||
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Game.IsMultiplayer and not Game.Client.HasPermission(ClientPermissions.ManageSettings) then
|
||||
for guicomponent in list.GetAllChildren() do
|
||||
guicomponent.enabled = false
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
-- Base UI construction
|
||||
local function ConstructUI(parent)
|
||||
-- Set the default to display (Neurotrauma, duh)
|
||||
local selectedExpansion = BaseConfigPages[1].name
|
||||
local list = easySettings.BasicList(parent)
|
||||
|
||||
-- Get the Title block to put the drop down menu into (could be moved tbh)
|
||||
local innerLayout = list.Parent
|
||||
local children = innerLayout.RectTransform.Children
|
||||
local title = children[1].GUIComponent
|
||||
|
||||
-- Get the amount of loaded expansions + seperate pages
|
||||
local dropdownheight = #NTConfig.Expansions + #BaseConfigPages
|
||||
|
||||
-- Don't show the dropdown if we only have Neurotrauma or no other addons that have settings to show
|
||||
if dropdownheight > 1 then
|
||||
local dropdown_AddonSelection = GUI.DropDown(
|
||||
GUI.RectTransform(Vector2(0.18, 1), title.RectTransform),
|
||||
"",
|
||||
dropdownheight - 2,
|
||||
nil,
|
||||
false,
|
||||
false,
|
||||
GUI.Alignment.CenterLeft
|
||||
)
|
||||
dropdown_AddonSelection.ListBox.RectTransform.RelativeOffset = (Vector2(0, 0.5))
|
||||
PopulateDropdown(dropdown_AddonSelection)
|
||||
dropdown_AddonSelection.Select(0)
|
||||
selectedExpansion = dropdown_AddonSelection.Text
|
||||
dropdown_AddonSelection.ToolTip = "Choose which mod's settings to display."
|
||||
|
||||
-- Using the dropdown changes a variable so we can change the page accordingly; only do so if we're not already on that page
|
||||
dropdown_AddonSelection.OnSelected = function(guiComponent)
|
||||
local newSelection = tostring(guiComponent.Text)
|
||||
|
||||
-- Check if changed
|
||||
if newSelection == selectedExpansion then return end
|
||||
|
||||
selectedExpansion = newSelection
|
||||
|
||||
-- Redo content based on new selection
|
||||
PopulateSettingsIntoUI(list, selectedExpansion)
|
||||
end
|
||||
end
|
||||
|
||||
-- Default
|
||||
PopulateSettingsIntoUI(list, selectedExpansion)
|
||||
end
|
||||
|
||||
Networking.Receive("NT.ConfigUpdate", function(msg)
|
||||
NTConfig.ReceiveConfig(msg)
|
||||
|
||||
if configUI == nil then return end
|
||||
if configUI.RectTransform == nil then return end
|
||||
|
||||
local parent = configUI.RectTransform.Parent
|
||||
|
||||
configUI = nil
|
||||
|
||||
configUI = ConstructUI(parent)
|
||||
end)
|
||||
|
||||
easySettings.AddMenu("Neurotrauma", function(parent)
|
||||
if Game.IsMultiplayer then
|
||||
local msg = Networking.Start("NT.ConfigRequest")
|
||||
Networking.Send(msg)
|
||||
end
|
||||
configUI = ConstructUI(parent)
|
||||
end)
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
local _Int32 = LuaUserData.RegisterType("System.Int32")
|
||||
|
||||
-- Stack specific items together; if you want only the left leg to be cheaper than a right one go fuck yourself
|
||||
local ItemVariants = {
|
||||
antibloodloss2 = "NT_ItemPrice_bloodpacks",
|
||||
bloodpackoplus = "NT_ItemPrice_bloodpacks",
|
||||
bloodpackaminus = "NT_ItemPrice_bloodpacks",
|
||||
bloodpackaplus = "NT_ItemPrice_bloodpacks",
|
||||
bloodpackbminus = "NT_ItemPrice_bloodpacks",
|
||||
bloodpackbplus = "NT_ItemPrice_bloodpacks",
|
||||
bloodpackabminus = "NT_ItemPrice_bloodpacks",
|
||||
bloodpackabplus = "NT_ItemPrice_bloodpacks",
|
||||
rarm = "NT_ItemPrice_arms",
|
||||
larm = "NT_ItemPrice_arms",
|
||||
rleg = "NT_ItemPrice_legs",
|
||||
lleg = "NT_ItemPrice_legs",
|
||||
rarmp = "NT_ItemPrice_bionicarms",
|
||||
larmp = "NT_ItemPrice_bionicarms",
|
||||
rlegp = "NT_ItemPrice_bioniclegs",
|
||||
llegp = "NT_ItemPrice_bioniclegs",
|
||||
}
|
||||
|
||||
-- Fetch config multipliers
|
||||
local function GetItemMultiplier(identifier)
|
||||
if identifier == nil then return 1.0 end
|
||||
|
||||
-- Add grouping so blood bags don't fucking kill me
|
||||
local configKey = ItemVariants[identifier] or ("NT_ItemPrice_" .. identifier)
|
||||
|
||||
if NTConfig.Entries[configKey] == nil then return 1.0 end
|
||||
|
||||
local value = NTConfig.Get(configKey, 1.0)
|
||||
return tonumber(value) or 1.0
|
||||
end
|
||||
|
||||
-- PRICE CHANGING
|
||||
-- Hook into the price-determining function and add a multiplier
|
||||
Hook.Patch("Barotrauma.Location+StoreInfo", "GetAdjustedItemBuyPrice", function(instance, ptable)
|
||||
local item = ptable["item"]
|
||||
if item == nil or item.Identifier == nil then return end
|
||||
|
||||
local id = item.Identifier.Value
|
||||
local mult = GetItemMultiplier(id)
|
||||
|
||||
-- Don't do extra math if the item value is unchanged
|
||||
if mult == 1.0 then return end
|
||||
|
||||
-- Get the 'actual price' after the game is done with it's calculations
|
||||
local base = ptable.OriginalReturnValue
|
||||
if base == nil then return end
|
||||
|
||||
-- Apply config-determined multiplier
|
||||
local result = math.floor(base * mult + 0.5)
|
||||
|
||||
ptable.ReturnValue = LuaUserData.CreateUserDataFromDescriptor(result, _Int32)
|
||||
end, Hook.HookMethodType.After)
|
||||
|
||||
-- FABRICATOR CHANGES
|
||||
-- You cannot fabricate the item; this is accomplished by taking the Filter that gets made whenever you open a fabricator, and hiding the specific item IDs we want to hide.
|
||||
local fabricatorType = LuaUserData.RegisterType("Barotrauma.Items.Components.Fabricator")
|
||||
LuaUserData.MakeFieldAccessible(fabricatorType, "itemList")
|
||||
Hook.Patch("Barotrauma.Items.Components.Fabricator", "FilterEntities", function(instance, ptable)
|
||||
Timer.Wait(function()
|
||||
local blockedItems = HF.DynamicUnavailableItems()
|
||||
|
||||
for child in instance.itemList.Content.Children do
|
||||
local recipe = child.UserData
|
||||
|
||||
if recipe and LuaUserData.IsTargetType(recipe, "Barotrauma.FabricationRecipe") then
|
||||
local id = recipe.TargetItem.Identifier.Value
|
||||
|
||||
if blockedItems[id] then child.Visible = false end
|
||||
end
|
||||
end
|
||||
end, 1)
|
||||
end, Hook.HookMethodType.After)
|
||||
|
||||
-- STORE CHANGES
|
||||
-- You cannot buy the item; we simply hook into store availability and hide the item.
|
||||
-- Ensure the items CANNOT be specials.
|
||||
local storeType = LuaUserData.RegisterType("Barotrauma.Store")
|
||||
LuaUserData.MakeFieldAccessible(storeType, "storeBuyList")
|
||||
LuaUserData.MakeFieldAccessible(storeType, "storeDailySpecialsGroup")
|
||||
|
||||
Hook.Patch("Barotrauma.Store", "FilterStoreItems", {
|
||||
"Barotrauma.MapEntityCategory",
|
||||
"System.String",
|
||||
}, function(instance, ptable)
|
||||
Timer.Wait(function()
|
||||
-- Fetch items to hide
|
||||
local blockedItems = HF.DynamicUnavailableItems()
|
||||
|
||||
local storeList = instance.storeBuyList
|
||||
if storeList then
|
||||
for child in storeList.Content.Children do
|
||||
local item = child.UserData
|
||||
|
||||
if item and item.ItemPrefab then
|
||||
local id = item.ItemPrefab.Identifier.Value
|
||||
-- Hide items within the table every refresh
|
||||
if blockedItems[id] then child.Visible = false end
|
||||
end
|
||||
end
|
||||
end
|
||||
end, 10)
|
||||
end, Hook.HookMethodType.After)
|
||||
|
||||
-- Force config sync on level swap to ensure things go properly
|
||||
Hook.Add("roundStart", "forcesyncconfig", function()
|
||||
if Game.IsMultiplayer then
|
||||
local msg = Networking.Start("NT.ConfigRequest")
|
||||
Networking.Send(msg)
|
||||
end
|
||||
end)
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
-- Original code by Evil Factory,
|
||||
-- Adapted for Neurotrauma
|
||||
local easySettings = {}
|
||||
|
||||
easySettings.Settings = {}
|
||||
|
||||
local GUIComponent = LuaUserData.CreateStatic("Barotrauma.GUIComponent")
|
||||
|
||||
local function GetChildren(comp)
|
||||
local tbl = {}
|
||||
for value in comp.GetAllChildren() do
|
||||
table.insert(tbl, value)
|
||||
end
|
||||
return tbl
|
||||
end
|
||||
|
||||
easySettings.SaveTable = function(path, tbl)
|
||||
File.Write(path, json.serialize(tbl))
|
||||
end
|
||||
|
||||
easySettings.LoadTable = function(path)
|
||||
if not File.Exists(path) then return {} end
|
||||
|
||||
return json.parse(File.Read(path))
|
||||
end
|
||||
|
||||
easySettings.AddMenu = function(name, onOpen)
|
||||
table.insert(easySettings.Settings, { Name = name, OnOpen = onOpen })
|
||||
end
|
||||
|
||||
-- Overhauled Config GUI
|
||||
easySettings.BasicList = function(parent, size)
|
||||
-- Menu Frame
|
||||
local menuContent =
|
||||
GUI.Frame(GUI.RectTransform(size or Vector2(0.5, 0.8), parent.RectTransform, GUI.Anchor.Center), "GUIFrame")
|
||||
|
||||
-- Main Layout
|
||||
local mainLayout = GUI.LayoutGroup(
|
||||
GUI.RectTransform(Vector2(0.95, 0.95), menuContent.RectTransform, GUI.Anchor.Center, GUI.Pivot.Center),
|
||||
false
|
||||
)
|
||||
|
||||
-- Background
|
||||
local configBackground = GUI.Frame(GUI.RectTransform(Vector2(1, 0.95), mainLayout.RectTransform), "InnerFrame")
|
||||
|
||||
-- Shrink Inner layout
|
||||
local innerLayout = GUI.LayoutGroup(
|
||||
GUI.RectTransform(Vector2(0.95, 0.95), configBackground.RectTransform, GUI.Anchor.TopCenter),
|
||||
false
|
||||
)
|
||||
|
||||
-- Title block
|
||||
local title = GUI.TextBlock(
|
||||
GUI.RectTransform(Vector2(1, 0.07), innerLayout.RectTransform),
|
||||
"Neurotrauma Config Settings",
|
||||
nil,
|
||||
GUI.GUIStyle.LargeFont
|
||||
)
|
||||
title.TextAlignment = GUI.Alignment.TopCenter
|
||||
|
||||
-- Setting list
|
||||
local menuList = GUI.ListBox(GUI.RectTransform(Vector2(1, 0.97), innerLayout.RectTransform))
|
||||
menuList.Padding = Vector4(10, 15, 10, 10)
|
||||
menuList.UpdateDimensions()
|
||||
|
||||
-- Button row
|
||||
local buttonRow = GUI.LayoutGroup(GUI.RectTransform(Vector2(1, 0.1), mainLayout.RectTransform), true)
|
||||
buttonRow.RelativeSpacing = 0.02
|
||||
easySettings.SaveButton(buttonRow)
|
||||
easySettings.CloseButton(buttonRow)
|
||||
easySettings.ResetButton(buttonRow)
|
||||
|
||||
return menuList
|
||||
end
|
||||
|
||||
-- Function for a Tickbox
|
||||
easySettings.TickBox = function(parent, text, onSelected, state)
|
||||
if state == nil then state = true end
|
||||
|
||||
local tickBox = GUI.TickBox(GUI.RectTransform(Vector2(1, 0.2), parent.RectTransform), text)
|
||||
tickBox.Selected = state
|
||||
tickBox.OnSelected = function()
|
||||
onSelected(tickBox.State == GUIComponent.ComponentState.Selected)
|
||||
end
|
||||
|
||||
return tickBox
|
||||
end
|
||||
|
||||
-- Function for a Slider
|
||||
easySettings.Slider = function(parent, min, max, onSelected, value)
|
||||
local scrollBar = GUI.ScrollBar(GUI.RectTransform(Vector2(1, 0.1), parent.RectTransform), 0.1, nil, "GUISlider")
|
||||
scrollBar.Range = Vector2(min, max)
|
||||
scrollBar.BarScrollValue = value or max / 2
|
||||
scrollBar.OnMoved = function()
|
||||
onSelected(scrollBar.BarScrollValue)
|
||||
end
|
||||
|
||||
return scrollBar
|
||||
end
|
||||
|
||||
-- Function for the Save and Exit button
|
||||
easySettings.SaveButton = function(parent)
|
||||
local button = GUI.Button(
|
||||
GUI.RectTransform(Vector2(0.32, 0.05), parent.RectTransform, GUI.Anchor.BottomLeft),
|
||||
"Save and Exit",
|
||||
GUI.Alignment.Center,
|
||||
"GUIButton"
|
||||
)
|
||||
|
||||
button.OnClicked = function()
|
||||
if Game.IsMultiplayer and Game.Client.HasPermission(ClientPermissions.ManageSettings) then
|
||||
NTConfig.SendConfig()
|
||||
elseif Game.IsSingleplayer then
|
||||
NTConfig.SaveConfig()
|
||||
end
|
||||
GUI.GUI.TogglePauseMenu()
|
||||
end
|
||||
|
||||
return button
|
||||
end
|
||||
|
||||
-- Function for the Discard and Exit button
|
||||
easySettings.CloseButton = function(parent)
|
||||
local button = GUI.Button(
|
||||
GUI.RectTransform(Vector2(0.32, 0.05), parent.RectTransform, GUI.Anchor.BottomCenter),
|
||||
"Discard and Exit",
|
||||
GUI.Alignment.Center,
|
||||
"GUIButton"
|
||||
)
|
||||
|
||||
button.OnClicked = function()
|
||||
GUI.GUI.TogglePauseMenu()
|
||||
NTConfig.LoadConfig()
|
||||
end
|
||||
|
||||
return button
|
||||
end
|
||||
|
||||
-- Function for the Reset and Exit button
|
||||
easySettings.ResetButton = function(parent)
|
||||
local button = GUI.Button(
|
||||
GUI.RectTransform(Vector2(0.32, 0.05), parent.RectTransform, GUI.Anchor.BottomRight),
|
||||
"Reset Config",
|
||||
GUI.Alignment.Center,
|
||||
"GUIButton"
|
||||
)
|
||||
|
||||
button.OnClicked = function()
|
||||
if
|
||||
Game.IsSingleplayer or (Game.IsMultiplayer and Game.Client.HasPermission(ClientPermissions.ManageSettings))
|
||||
then
|
||||
easySettings.ResetMessage(parent)
|
||||
end
|
||||
end
|
||||
|
||||
return button
|
||||
end
|
||||
|
||||
-- Confirmation popup for config reset
|
||||
easySettings.ResetMessage = function(parent)
|
||||
local ResetMessage = GUI.MessageBox(
|
||||
"Reset neurotrauma settings",
|
||||
"Are you sure you want to reset neurotrauma settings to default values?",
|
||||
{ "Yes", "No" }
|
||||
)
|
||||
ResetMessage.DrawOnTop = true
|
||||
ResetMessage.Text.TextAlignment = GUI.Alignment.Center
|
||||
|
||||
ResetMessage.Buttons[1].OnClicked = function()
|
||||
NTConfig.ResetConfig()
|
||||
if Game.IsMultiplayer and Game.Client.HasPermission(ClientPermissions.ManageSettings) then
|
||||
NTConfig.SendConfig()
|
||||
elseif Game.IsSingleplayer then
|
||||
NTConfig.SaveConfig()
|
||||
end
|
||||
GUI.GUI.TogglePauseMenu()
|
||||
ResetMessage.Close()
|
||||
end
|
||||
|
||||
ResetMessage.Buttons[2].OnClicked = function()
|
||||
ResetMessage.Close()
|
||||
end
|
||||
|
||||
return ResetMessage
|
||||
end
|
||||
|
||||
-- Add button on pause menu for NT Settings
|
||||
Hook.Patch("Barotrauma.GUI", "TogglePauseMenu", {}, function()
|
||||
if GUI.GUI.PauseMenuOpen then
|
||||
local frame = GUI.GUI.PauseMenu
|
||||
|
||||
local list = GetChildren(GetChildren(frame)[2])[1]
|
||||
|
||||
for key, value in pairs(easySettings.Settings) do
|
||||
local PauseMenuButton = GUI.Button(
|
||||
GUI.RectTransform(Vector2(1, 0.1), list.RectTransform),
|
||||
value.Name,
|
||||
GUI.Alignment.Center,
|
||||
"GUIButtonSmall"
|
||||
)
|
||||
|
||||
PauseMenuButton.OnClicked = function()
|
||||
value.OnOpen(frame)
|
||||
end
|
||||
end
|
||||
end
|
||||
end, Hook.HookMethodType.After)
|
||||
|
||||
return easySettings
|
||||
Reference in New Issue
Block a user