528 lines
16 KiBLFS
Lua
Executable File
528 lines
16 KiBLFS
Lua
Executable File
|
|
local LockedStr = TextManager.ContainsTag("rawconfig.enforcedtooltip") and TextManager.Get("rawconfig.enforcedtooltip") or "This setting is enforced by server"
|
|
local UnlockedStr = TextManager.ContainsTag("rawconfig.notenforcedtooltip") and TextManager.Get("rawconfig.notenforcedtooltip") or "This setting is not enforced by server"
|
|
|
|
local Version = 0.2
|
|
|
|
local rawconfig = {}
|
|
rawconfig.Version = Version
|
|
|
|
|
|
local configDirectoryPath = Game.SaveFolder .. "/ModConfigs"
|
|
local configFilePath = configDirectoryPath .. "/Neurotrauma.json"
|
|
|
|
|
|
rawconfig.Configs = {}
|
|
rawconfig.gui = {}
|
|
rawconfig.util = {}
|
|
rawconfig.util.DirectoryPath = Game.SaveFolder .. "/ModConfigs"
|
|
rawconfig.util.Buffer = {}
|
|
|
|
|
|
--Which side provides config value. Optional adds a Lock icon which can be toggled by Owner.
|
|
--Clientside entires can be bypassed with some "hacking" but also by not having clientside lua so either use Clientsidelua Enforced
|
|
--Or dont use clientside entries for anythign critical
|
|
rawconfig.Enforcment = {
|
|
Client = 0,
|
|
Server = 1,
|
|
Optional = 2,
|
|
}
|
|
|
|
|
|
if CLIENT then
|
|
Hook.Patch("rawconfig" .. Version .. "_Pausemenu", "Barotrauma.GUI", "TogglePauseMenu", {}, function(instance, ptable)
|
|
if not GUI.GUI.PauseMenuOpen then return end
|
|
local PMframe = GUI.GUI.PauseMenu
|
|
local PMInner = PMframe.GetChild(Int32(1))
|
|
local PMbuttonContainer = PMInner.GetChild(Int32(0))
|
|
--local textblocks = {}
|
|
|
|
for config in rawconfig.Configs do
|
|
local button = GUI.Button(
|
|
GUI.RectTransform(Vector2(1, 0.1), PMbuttonContainer.RectTransform),
|
|
config.Label or config.Name,
|
|
GUI.Alignment.Center,
|
|
"GUIButtonSmall"
|
|
)
|
|
|
|
button.OnClicked = function()
|
|
config:OpenMenu()
|
|
if Game.IsMultiplayer then
|
|
rawconfig.util.RequestConfig(config)
|
|
end
|
|
end
|
|
config.PauseMenuButton = button
|
|
end
|
|
|
|
--GUI.TextBlock.AutoScaleAndNormalize(textblocks)
|
|
|
|
local PMSizeY = 0
|
|
for child in PMbuttonContainer.Children do
|
|
PMSizeY = PMSizeY + child.Rect.Height + PMbuttonContainer.AbsoluteSpacing
|
|
end
|
|
PMSizeY = math.floor(PMSizeY / PMbuttonContainer.RectTransform.RelativeSize.Y)
|
|
PMInner.RectTransform.MinSize = Point(PMInner.RectTransform.MinSize.X, math.max(PMSizeY, PMInner.RectTransform.MinSize.X));
|
|
end, Hook.HookMethodType.After)
|
|
end
|
|
|
|
|
|
--Allows indexing util and gui table values from config table itself
|
|
--Makes possible stuff like Config:Set(key, value) without needing to touch underlying util
|
|
--This also allows overriding any gui/util function by adding your own implementation to Config as __index meta is only used for unspecified keys
|
|
metatable = {
|
|
__index = function(self, key)
|
|
return rawconfig.util[key] or rawconfig.gui[key]
|
|
end,
|
|
}
|
|
|
|
|
|
---Add a button to the bottom of pause menu for your config menu
|
|
---@param string how your button would be labeled. Supports localization tags.
|
|
---@param function(button) function called when button is pressed with Barotrauma.GUIButton passed as argument
|
|
function rawconfig.addConfig(config)
|
|
config.Label = config.Label and TextManager.ContainsTag(config.Label) and TextManager.Get(config.Label) or Label
|
|
config.EnableGUI = config.EnableGUI or true
|
|
config.CanBeReset = config.CanBeReset or false
|
|
config.FilePath = config.FilePath or rawconfig.util.DirectoryPath .. "/" .. config.Name .. ".json"
|
|
|
|
config.PauseMenuButton = nil
|
|
|
|
|
|
for entry in config.Entries do
|
|
entry.value = entry.default
|
|
entry.name = TextManager.ContainsTag(entry.name) and TextManager.Get(entry.name) or entry.name
|
|
entry.description = TextManager.ContainsTag(entry.description) and TextManager.Get(entry.description) or entry.description
|
|
if entry.enforcment == rawconfig.Enforcment.Optional then
|
|
entry.enforced = entry.enforced or false
|
|
else
|
|
entry.enforced = entry.enforcment == rawconfig.Enforcment.Server
|
|
end
|
|
end
|
|
|
|
setmetatable(config, metatable)
|
|
|
|
rawconfig.Configs[config.Name] = config
|
|
return rawconfig.Configs[config.Name]
|
|
end
|
|
|
|
|
|
function rawconfig.util.RequestConfig(config)
|
|
if Game.IsMultiplayer then
|
|
local msg = Networking.Start("rawconfig.ConfigRequest")
|
|
msg.WriteString(config.Name)
|
|
Networking.Send(msg)
|
|
end
|
|
end
|
|
|
|
|
|
function rawconfig.util.LoadConfig(config)
|
|
if not File.Exists(config.FilePath) then
|
|
return
|
|
end
|
|
|
|
local readConfig = json.parse(File.Read(config.FilePath))
|
|
|
|
for key, entry in pairs(readConfig) do
|
|
if config.Entries[key] then
|
|
config.Entries[key].value = entry.value
|
|
if entry.enforced ~= nil and config.Entries[key].enforcment == rawconfig.Enforcment.Optional then
|
|
config.Entries[key].enforced = entry.enforced
|
|
end
|
|
end
|
|
end
|
|
|
|
if Game.IsMultiplayer and CLIENT then
|
|
rawconfig.util.RequestConfig(config)
|
|
end
|
|
end
|
|
|
|
|
|
function rawconfig.util.SaveConfig(config, preserveUnusedKeys)
|
|
--prevent both owner client and server saving config at the same time and potentially erroring from file access
|
|
if Game.IsMultiplayer and CLIENT and Game.Client.MyClient.IsOwner then
|
|
return
|
|
end
|
|
|
|
preserveUnusedKeys = preserveUnusedKeys or true
|
|
|
|
local tableToSave = {}
|
|
if preserveUnusedKeys and File.Exists(config.FilePath) then
|
|
tableToSave = json.parse(File.Read(config.FilePath))
|
|
end
|
|
|
|
for key, entry in pairs(config.Entries) do
|
|
tableToSave[key] = {}
|
|
--Only save clientside keys when in mp
|
|
if Game.IsMultiplayer and CLIENT then
|
|
if not entry.enforced then
|
|
tableToSave[key].value = entry.value
|
|
end
|
|
else
|
|
tableToSave[key].value = entry.value
|
|
if entry.enforcment == rawconfig.Enforcment.Optional then
|
|
tableToSave[key].enforced = entry.enforced
|
|
end
|
|
end
|
|
end
|
|
|
|
File.CreateDirectory(config.DirectoryPath)
|
|
|
|
--Apparently access error can still happen if player hosts dedicated and joins it on same pc.
|
|
--Haven't found how to check file being in use properly so just delay to avoid conflict
|
|
if CLIENT then
|
|
File.Write(config.FilePath, json.serialize(tableToSave))
|
|
else
|
|
Timer.Wait(function()
|
|
File.Write(config.FilePath, json.serialize(tableToSave))
|
|
end, 100)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
function rawconfig.util.ResetConfig(config)
|
|
for key, entry in pairs(config.Entries) do
|
|
config.Entries[key].value = entry.default
|
|
end
|
|
end
|
|
|
|
|
|
function rawconfig.util.SendConfig(config, reciverClient)
|
|
local tableToSend = {}
|
|
for key, entry in pairs(config.Entries) do
|
|
tableToSend[key] = {}
|
|
tableToSend[key].value = entry.value
|
|
tableToSend[key].enforced = entry.enforced
|
|
end
|
|
|
|
local msg = Networking.Start("rawconfig.ConfigUpdate")
|
|
msg.WriteString(config.Name)
|
|
msg.WriteString(json.serialize(tableToSend))
|
|
if SERVER then
|
|
Networking.Send(msg, reciverClient and reciverClient.Connection or nil)
|
|
else
|
|
Networking.Send(msg)
|
|
end
|
|
end
|
|
|
|
|
|
function rawconfig.util.ReceiveConfig(msg)
|
|
local RecivedTable = {}
|
|
local targetConfig = rawconfig.Configs[msg.ReadString()]
|
|
RecivedTable = json.parse(msg.ReadString())
|
|
|
|
for key, entry in pairs(RecivedTable) do
|
|
if entry.enforced then
|
|
targetConfig.Entries[key].value = entry.value
|
|
end
|
|
if entry.enforced ~= nil and targetConfig.Entries[key].enforcment == rawconfig.Enforcment.Optional then
|
|
targetConfig.Entries[key].enforced = entry.enforced
|
|
end
|
|
end
|
|
|
|
return targetConfig
|
|
end
|
|
|
|
|
|
if CLIENT then
|
|
Networking.Receive("rawconfig.ConfigUpdate", function(msg)
|
|
config = rawconfig.util.ReceiveConfig(msg)
|
|
if config.Frame then
|
|
config:UpdateMenu()
|
|
end
|
|
end)
|
|
end
|
|
|
|
|
|
if SERVER then
|
|
Networking.Receive("rawconfig.ConfigUpdate", function(msg, sender)
|
|
if not rawconfig.util.ClientHasAccess(sender) then
|
|
return
|
|
end
|
|
config = rawconfig.util.ReceiveConfig(msg)
|
|
config:SaveConfig()
|
|
end)
|
|
|
|
Networking.Receive("rawconfig.ConfigRequest", function(msg, sender)
|
|
if not sender then
|
|
return
|
|
end
|
|
local targetConfig = rawconfig.Configs[msg.ReadString()]
|
|
if targetConfig then targetConfig:SendConfig(sender) end
|
|
end)
|
|
end
|
|
|
|
|
|
|
|
function rawconfig.util.Get(config, key, default)
|
|
if config.Entries[key] ~= nil and config.Entries[key].value ~= nil then
|
|
return config.Entries[key].value
|
|
end
|
|
return default
|
|
end
|
|
|
|
|
|
function rawconfig.util.Set(config, key, value)
|
|
if config.Entries[key] ~= nil then
|
|
config.Entries[key].value = value
|
|
end
|
|
end
|
|
|
|
function rawconfig.util.ClearBuffer()
|
|
rawconfig.util.Buffer = {}
|
|
end
|
|
|
|
|
|
function rawconfig.util.DumpBuffer(config)
|
|
for key, entry in pairs(rawconfig.util.Buffer) do
|
|
if config.Entries[key] ~= nil then
|
|
for datakey, value in pairs(entry) do
|
|
config.Entries[key][datakey] = value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function rawconfig.util.ClientHasAccess(client)
|
|
return client.HasPermission(ClientPermissions.ManageSettings)
|
|
end
|
|
|
|
|
|
rawconfig.gui = {
|
|
CloseButton = nil,
|
|
SaveButton = nil,
|
|
ResetButton = nil,
|
|
PauseMenuButton = function(config) return config.PauseMenuButton end,
|
|
Frame = nil,
|
|
ListContainer = nil,
|
|
ControlGroup = nil,
|
|
}
|
|
|
|
---Creates empty box and returns a GUI.Frame to attach other guis into
|
|
---@param Barotrauma.RectTransform
|
|
---@param Vector2
|
|
---@param bool show or hide Reset button
|
|
function rawconfig.gui.OpenMenu(config, parent, size)
|
|
local menuFrame = GUI.Frame(GUI.RectTransform(size or Vector2(0.3, 0.6), parent or GUI.GUI.PauseMenu.RectTransform, GUI.Anchor.Center))
|
|
local menuList = GUI.ListBox(GUI.RectTransform(Vector2(1, 0.9), menuFrame.RectTransform, GUI.Anchor.TopCenter))
|
|
menuList.Padding = Vector4(10,15,10,10)
|
|
menuList.UpdateDimensions()
|
|
local menuControlGroup = GUI.LayoutGroup(GUI.RectTransform(Vector2(0.9, 0.1), menuFrame.RectTransform, GUI.Anchor.BottomCenter), true, GUI.Anchor.BottomLeft)
|
|
menuControlGroup.RectTransform.AbsoluteOffset = Point(0, 7)
|
|
menuControlGroup.Stretch = true
|
|
menuControlGroup.AbsoluteSpacing = 7
|
|
rawconfig.gui.SaveButton = config:CreateSaveButton(menuControlGroup)
|
|
rawconfig.gui.CloseButton = config:CreateCloseButton(menuControlGroup)
|
|
|
|
rawconfig.gui.Frame = menuFrame
|
|
rawconfig.gui.ListContainer = menuList
|
|
rawconfig.gui.ControlGroup = menuControlGroup
|
|
|
|
if config.CanBeReset then
|
|
--rawconfig.ResetButton(menuFrame)
|
|
end
|
|
|
|
menuControlGroup.Recalculate()
|
|
|
|
config:PopulateMenu()
|
|
|
|
return menuFrame
|
|
end
|
|
|
|
|
|
---Adds save button
|
|
function rawconfig.gui.CreateSaveButton(config, parent)
|
|
local button = GUI.Button(
|
|
GUI.RectTransform(Vector2(0.33, 0.05), parent.RectTransform, GUI.Anchor.BottomCenter),
|
|
"Save",
|
|
GUI.Alignment.Center,
|
|
"GUIButton"
|
|
)
|
|
|
|
button.OnClicked = function()
|
|
rawconfig.util.DumpBuffer(config)
|
|
rawconfig.util.ClearBuffer()
|
|
if Game.IsMultiplayer and Game.Client.HasPermission(ClientPermissions.ManageSettings) then
|
|
config:SendConfig()
|
|
end
|
|
config:SaveConfig()
|
|
config:CloseMenu()
|
|
end
|
|
|
|
return button
|
|
end
|
|
|
|
|
|
---Adds close button
|
|
function rawconfig.gui.CreateCloseButton(config, parent)
|
|
local button = GUI.Button(
|
|
GUI.RectTransform(Vector2(0.33, 0.05), parent.RectTransform, GUI.Anchor.BottomCenter),
|
|
"Discard",
|
|
GUI.Alignment.Center,
|
|
"GUIButton"
|
|
)
|
|
|
|
button.OnClicked = function()
|
|
config:CloseMenu()
|
|
end
|
|
|
|
return button
|
|
end
|
|
|
|
|
|
function rawconfig.gui.CloseMenu(config)
|
|
config.Frame.RectTransform.Parent = nil
|
|
|
|
rawconfig.gui.CloseButton = nil
|
|
rawconfig.gui.SaveButton = nil
|
|
rawconfig.gui.ResetButton = nil
|
|
rawconfig.gui.Frame = nil
|
|
rawconfig.gui.ListContainer = nil
|
|
rawconfig.gui.ControlGroup = nil
|
|
|
|
for key, entry in pairs(config.Entries) do
|
|
entry.gui = nil
|
|
end
|
|
end
|
|
|
|
---@param Barotrauma.RectTransform
|
|
function rawconfig.gui.PopulateMenu(config)
|
|
for key, entry in pairs(config.Entries) do
|
|
local EntryGroup = GUI.LayoutGroup(GUI.RectTransform(Vector2(0.98, 0.08), config.ListContainer.Content.RectTransform, GUI.Anchor.TopCenter), true, GUI.Anchor.CenterLeft)
|
|
--EntryGroup.RectTransform.AbsoluteOffset = Point(0,100)
|
|
--EntryGroup.AbsoluteSpacing = 5
|
|
--EntryGroup.Stretch = true
|
|
|
|
local Lock = config:CreateLock(EntryGroup, key, entry)
|
|
if entry.type == "bool" then
|
|
config:CreateTickBox(EntryGroup, key, entry)
|
|
elseif entry.type == "float" then
|
|
end
|
|
|
|
entry.gui = EntryGroup
|
|
end
|
|
end
|
|
|
|
|
|
---@param Barotrauma.RectTransform
|
|
function rawconfig.gui.CreateLock(config, parent, key, entry)
|
|
local rect = GUI.RectTransform(Vector2(0.05, 1), parent.RectTransform)
|
|
local toggle = GUI.TickBox(rect, "", nil, "GUILockToggle")
|
|
|
|
if Game.IsSingleplayer or entry.enforcment == rawconfig.Enforcment.Client then
|
|
toggle.Selected = false
|
|
toggle.Visible = false
|
|
return toggle
|
|
end
|
|
|
|
toggle.Selected = entry.enforced
|
|
toggle.Enabled = entry.enforcment == rawconfig.Enforcment.Optional and rawconfig.util.ClientHasAccess(Game.Client)
|
|
|
|
|
|
if toggle.Selected and not toggle.Enabled then
|
|
toggle.Box.DisabledColor = Color(255,106,106,255)
|
|
end
|
|
|
|
if entry.description then
|
|
toggle.ToolTip = toggle.Selected and LockedStr or UnlockedStr
|
|
end
|
|
|
|
toggle.OnSelected = function()
|
|
toggle.ToolTip = toggle.Selected and LockedStr or UnlockedStr
|
|
rawconfig.util.Buffer[key].enforced = toggle.Selected
|
|
end
|
|
|
|
return toggle
|
|
end
|
|
|
|
---@param Barotrauma.RectTransform
|
|
function rawconfig.gui.CreateTickBox(config, parent, key, entry)
|
|
-- toggle
|
|
local rect = GUI.RectTransform(Vector2(0.8, 1), parent.RectTransform)
|
|
local toggle = GUI.TickBox(rect, entry.name)
|
|
if entry.description then
|
|
toggle.ToolTip = entry.description
|
|
end
|
|
|
|
toggle.Selected = config:Get(key, entry.default or false)
|
|
|
|
toggle.Enabled = Game.IsSingleplayer or not entry.enforced or rawconfig.util.ClientHasAccess(Game.Client)
|
|
|
|
toggle.OnSelected = function()
|
|
if not rawconfig.util.Buffer[key] then rawconfig.util.Buffer[key] = {} end
|
|
rawconfig.util.Buffer[key].value = toggle.State == GUI.Component.ComponentState.Selected
|
|
end
|
|
|
|
return toggle
|
|
end
|
|
|
|
|
|
---@param Barotrauma.RectTransform
|
|
function rawconfig.gui.UpdateMenu(config)
|
|
for key, entry in pairs(config.Entries) do
|
|
if entry.type == "bool" then
|
|
local lock = entry.gui.GetChild(Int32(0))
|
|
local tickbox = entry.gui.GetChild(Int32(1))
|
|
|
|
tickbox.Selected = entry.value
|
|
tickbox.Enabled = Game.IsSingleplayer or not entry.enforced or rawconfig.util.ClientHasAccess(Game.Client)
|
|
lock.Selected = entry.enforced
|
|
if lock.Selected and not lock.Enabled then
|
|
lock.Box.DisabledColor = Color(255,106,106,255)
|
|
end
|
|
|
|
elseif entry.type == "float" then
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--[[
|
|
testcfg = {
|
|
Name = "testcfg", --Internal name, used for rawconfig.Configs.Name
|
|
Label = "pausemenuquitverification", --String displayed in menus, can be localization tag
|
|
Entries = {
|
|
TST_tickbox = {
|
|
name = "pausemenuquitverification",
|
|
default = false,
|
|
type = "bool",
|
|
description = "pausemenuquitverification",
|
|
enforcment = rawconfig.Enforcment.Client,
|
|
},
|
|
TST_field = {
|
|
name = "Test name2, can also be a localization tag",
|
|
default = false,
|
|
type = "bool",
|
|
description = "Whatever whatever 2, can also be a localization tag",
|
|
--enforced = true,
|
|
enforcment = rawconfig.Enforcment.Optional,
|
|
},
|
|
|
|
},
|
|
}
|
|
|
|
|
|
rawconfig.addConfig(testcfg)
|
|
rawconfig.Configs.testcfg:LoadConfig()
|
|
]]
|
|
|
|
|
|
return rawconfig
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|