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
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
-- Neurotrauma blood types functions
|
||||
-- Hooks Lua event "characterCreated" to create a randomized blood type for spawned character and sets their immunity to 100
|
||||
---@diagnostic disable: lowercase-global, undefined-global
|
||||
NT.BLOODTYPE = { -- blood types and chance in percent
|
||||
{ "ominus", 7 },
|
||||
{ "oplus", 37 },
|
||||
{ "aminus", 6 },
|
||||
{ "aplus", 36 },
|
||||
{ "bminus", 2 },
|
||||
{ "bplus", 8 },
|
||||
{ "abminus", 1 },
|
||||
{ "abplus", 3 },
|
||||
}
|
||||
NT.setBlood = {}
|
||||
NT.foundAny = false
|
||||
|
||||
-- Insert all blood types in one table for RandomizeBlood()
|
||||
for index, value in ipairs(NT.BLOODTYPE) do
|
||||
-- print(index," : ",value[1],", ",value[2],"%")
|
||||
table.insert(NT.setBlood, index, { value[2], value[1] })
|
||||
end
|
||||
|
||||
-- Applies math.random() blood type.
|
||||
-- returns the applied bloodtype as an affliction identifier
|
||||
function NT.RandomizeBlood(character)
|
||||
rand = math.random(0, 99)
|
||||
local i = 0
|
||||
for index, value in ipairs(NT.setBlood) do
|
||||
i = i + value[1]
|
||||
if i > rand then
|
||||
HF.SetAffliction(character, value[2], 100)
|
||||
return value[2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Hook.Add("characterCreated", "NT.BloodAndImmunity", function(createdCharacter)
|
||||
Timer.Wait(function()
|
||||
if createdCharacter.IsHuman and not createdCharacter.IsDead then
|
||||
NT.TryRandomizeBlood(createdCharacter)
|
||||
|
||||
-- add immunity
|
||||
local conditional2 = createdCharacter.CharacterHealth.GetAffliction("immunity")
|
||||
if conditional2 == nil then HF.SetAffliction(createdCharacter, "immunity", 100) end
|
||||
end
|
||||
end, 1000)
|
||||
end)
|
||||
|
||||
-- applies a new bloodtype only if the character doesnt already have one
|
||||
function NT.TryRandomizeBlood(character)
|
||||
NT.GetBloodtype(character)
|
||||
end
|
||||
|
||||
-- returns the bloodtype of the character as an affliction identifier string
|
||||
-- generates blood type if none present
|
||||
function NT.GetBloodtype(character)
|
||||
for index, affliction in ipairs(NT.BLOODTYPE) do
|
||||
local conditional = character.CharacterHealth.GetAffliction(affliction[1])
|
||||
|
||||
if conditional ~= nil and conditional.Strength > 0 then
|
||||
return affliction[1] -- TODO: give out abplus (AB+) to enemy team for blood infusions
|
||||
end
|
||||
end
|
||||
|
||||
return NT.RandomizeBlood(character)
|
||||
end
|
||||
|
||||
function NT.HasBloodtype(character)
|
||||
for index, affliction in ipairs(NT.BLOODTYPE) do
|
||||
local conditional = character.CharacterHealth.GetAffliction(affliction[1])
|
||||
|
||||
if conditional ~= nil and conditional.Strength > 0 then return true end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
Hook.Add("OnInsertedIntoBloodAnalyzer", "NT.BloodAnalyzer", function(effect, deltaTime, item, targets, position)
|
||||
-- Hematology Analyzer (bloodanalyzer) can scan inserted blood bags
|
||||
local owner = item.GetRootInventoryOwner()
|
||||
if owner == nil then return end
|
||||
if not LuaUserData.IsTargetType(owner, "Barotrauma.Character") then return end
|
||||
if not owner.IsPlayer then return end
|
||||
|
||||
local character = owner
|
||||
local contained = item.OwnInventory.GetItemAt(0)
|
||||
|
||||
local BaseColor = "127,255,255"
|
||||
local NameColor = "127,255,255"
|
||||
local LowColor = "127,255,255"
|
||||
local HighColor = "127,255,255"
|
||||
local VitalColor = "127,255,255"
|
||||
|
||||
if NTConfig.Get("NTSCAN_enablecoloredscanner", 1) then
|
||||
BaseColor = table.concat(NTConfig.Get("NTSCAN_basecolor", 1), ",")
|
||||
NameColor = table.concat(NTConfig.Get("NTSCAN_namecolor", 1), ",")
|
||||
LowColor = table.concat(NTConfig.Get("NTSCAN_lowcolor", 1), ",")
|
||||
HighColor = table.concat(NTConfig.Get("NTSCAN_highcolor", 1), ",")
|
||||
VitalColor = table.concat(NTConfig.Get("NTSCAN_vitalcolor", 1), ",")
|
||||
end
|
||||
|
||||
-- NT adds bloodbag; NT Blood Work or 'Real Sonar Medical Item Recipes Patch for Neurotrauma' add allblood, lets check for either
|
||||
if contained ~= nil and (contained.HasTag("bloodbag") or contained.HasTag("allblood")) then
|
||||
HF.GiveItem(character, "ntsfx_syringe")
|
||||
Timer.Wait(function()
|
||||
if item == nil or character == nil or item.OwnInventory.GetItemAt(0) ~= contained then return end
|
||||
|
||||
local identifier = contained.Prefab.Identifier.Value
|
||||
local packtype = "o-"
|
||||
if identifier ~= "antibloodloss2" then packtype = string.sub(identifier, string.len("bloodpack") + 1) end
|
||||
local bloodTypeDisplay = string.gsub(packtype, "abc", "c")
|
||||
bloodTypeDisplay = string.gsub(bloodTypeDisplay, "plus", "+")
|
||||
bloodTypeDisplay = string.gsub(bloodTypeDisplay, "minus", "-")
|
||||
bloodTypeDisplay = string.upper(bloodTypeDisplay)
|
||||
|
||||
local readoutString = "‖color:"
|
||||
.. BaseColor
|
||||
.. "‖"
|
||||
.. "Bloodpack: "
|
||||
.. "‖color:end‖"
|
||||
.. "‖color:"
|
||||
.. NameColor
|
||||
.. "‖"
|
||||
.. bloodTypeDisplay
|
||||
.. "‖color:end‖"
|
||||
-- check if acidosis, alkalosis or sepsis
|
||||
local tags = HF.SplitString(contained.Tags, ",")
|
||||
local defects = ""
|
||||
for tag in tags do
|
||||
if tag == "sepsis" then
|
||||
defects = defects .. "‖color:" .. VitalColor .. "‖" .. "\nSepsis detected" .. "‖color:end‖"
|
||||
end
|
||||
|
||||
if HF.StartsWith(tag, "acid") then
|
||||
local split = HF.SplitString(tag, ":")
|
||||
if split[2] ~= nil then
|
||||
defects = defects
|
||||
.. "‖color:"
|
||||
.. HighColor
|
||||
.. "‖"
|
||||
.. "\nAcidosis: "
|
||||
.. tonumber(split[2])
|
||||
.. "%"
|
||||
.. "‖color:end‖"
|
||||
end
|
||||
elseif HF.StartsWith(tag, "alkal") then
|
||||
local split = HF.SplitString(tag, ":")
|
||||
if split[2] ~= nil then
|
||||
defects = defects
|
||||
.. "‖color:"
|
||||
.. HighColor
|
||||
.. "‖"
|
||||
.. "\nAlkalosis: "
|
||||
.. tonumber(split[2])
|
||||
.. "%"
|
||||
.. "‖color:end‖"
|
||||
end
|
||||
end
|
||||
end
|
||||
if defects ~= "" then
|
||||
readoutString = readoutString .. defects
|
||||
else
|
||||
readoutString = readoutString
|
||||
.. "‖color:"
|
||||
.. LowColor
|
||||
.. "‖"
|
||||
.. "\nNo blood defects"
|
||||
.. "‖color:end‖"
|
||||
end
|
||||
|
||||
HF.DMClient(HF.CharacterToClient(character), readoutString, Color(127, 255, 255, 255))
|
||||
end, 1500)
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,32 @@
|
||||
-- Hooks CalculateMovementPenalty method of Barotrauma.Character
|
||||
-- when painless enough, disable weapon sway / movement hindrance limb penalties
|
||||
-- !!! Lags the game, though GetAimWobble seems to be ok to patch
|
||||
|
||||
-- Disable movement penalties for painless characters
|
||||
-- Has about 2 ms performance drop on a many character save
|
||||
--Hook.Patch("Barotrauma.Character", "CalculateMovementPenalty", function(instance, ptable)
|
||||
-- if HF.HasAffliction(instance, "analgesia", 20) then
|
||||
-- ptable.PreventExecution = true
|
||||
-- return 0
|
||||
-- end
|
||||
--end, Hook.HookMethodType.Before)
|
||||
|
||||
-- Disable aim penalties for painless characters
|
||||
Hook.Patch("Barotrauma.AnimController", "GetAimWobble", function(instance, ptable)
|
||||
if HF.HasAffliction(instance.Character, "analgesia", 20) then
|
||||
ptable.PreventExecution = true
|
||||
return 0
|
||||
end
|
||||
end, Hook.HookMethodType.Before)
|
||||
|
||||
-- Patch to cause unconscious from the game rather than stun
|
||||
-- Lags the game by 6 times (on the same save)
|
||||
--Hook.Patch("Barotrauma.CharacterHealth", "get_IsUnconscious", function(instance, ptable)
|
||||
-- local isUnconscious = HF.HasAffliction(instance.Character, "sym_unconsciousness")
|
||||
-- ptable.PreventExecution = true
|
||||
-- return instance.Character.IsDead
|
||||
-- or (
|
||||
-- (instance.Character.Vitality <= 0.0 or isUnconscious)
|
||||
-- and not instance.Character.HasAbilityFlag(AbilityFlags.AlwaysStayConscious)
|
||||
-- )
|
||||
--end, Hook.HookMethodType.After)
|
||||
@@ -0,0 +1,108 @@
|
||||
-- THIS FILE IS NO LONGER IN USE
|
||||
-- the defunct item in question has been removed from the mod
|
||||
-- i'm keeping it here for...safekeeping i guess
|
||||
|
||||
LuaUserData.RegisterTypeBarotrauma("PurchasedItem")
|
||||
LuaUserData.RegisterType("System.Xml.Linq.XElement")
|
||||
|
||||
local VANILLA_PREFAB_ID = "antibloodloss2"
|
||||
local DEFUNCT_PREFAB_ID = "bloodpackominus"
|
||||
|
||||
-- Removes vanilla bloodpacks from cached stores in case the user installed
|
||||
-- this mod mid-campaign.
|
||||
-- NOTE: stores that had their stocks generated before installing this mod
|
||||
-- won't have any new medical items added.
|
||||
Hook.HookMethod("Barotrauma.Location", "LoadStores", function(instance, ptable)
|
||||
if instance.Stores == nil then return end
|
||||
|
||||
for storeId, store in pairs(instance.Stores) do
|
||||
for _, purchasedItem in pairs(store.Stock) do
|
||||
local itemId = purchasedItem.ItemPrefabIdentifier
|
||||
if itemId == DEFUNCT_PREFAB_ID then
|
||||
-- print("Removing defunct bloodpack (qty " .. purchasedItem.Quantity .. ") from " .. tostring(storeId))
|
||||
store.RemoveStock({ purchasedItem })
|
||||
end
|
||||
end
|
||||
end
|
||||
end, Hook.HookMethodType.After)
|
||||
|
||||
-- Replaces all vanilla bloodpack items with O- blood
|
||||
local function replaceItems()
|
||||
local ntBloodPrefab = ItemPrefab.Prefabs[DEFUNCT_PREFAB_ID]
|
||||
local vanillaBloodPrefab = ItemPrefab.Prefabs[VANILLA_PREFAB_ID]
|
||||
if ntBloodPrefab == nil then
|
||||
print("ERROR: couldn't find " .. DEFUNCT_PREFAB_ID)
|
||||
return
|
||||
end
|
||||
|
||||
for _, item in pairs(Item.ItemList) do
|
||||
local id = tostring(item.Prefab.Identifier)
|
||||
if id == DEFUNCT_PREFAB_ID then
|
||||
-- Don't replace decorative blood packs
|
||||
if item.NonInteractable then return end
|
||||
|
||||
local pos = item.WorldPosition
|
||||
local inv = item.ParentInventory
|
||||
local condition = item.ConditionPercentage
|
||||
local quality = item.Quality
|
||||
-- print("replacing blood pack (pos=" .. tostring(pos) .. ", inv=" .. tostring(inv) .. ")")
|
||||
|
||||
local slotIdx = -1
|
||||
if inv ~= nil then
|
||||
slotIdx = inv.FindIndex(item)
|
||||
if slotIdx < 0 then
|
||||
print(
|
||||
"ERROR: couldn't find item ("
|
||||
.. tostring(item)
|
||||
.. ", pos "
|
||||
.. tostring(pos)
|
||||
.. ") in inventory ("
|
||||
.. tostring(inv)
|
||||
.. ")"
|
||||
)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- We call `Drop()` first in case the inventory is full because
|
||||
-- `AddEntityToRemoveQueue` may not remove the item before we
|
||||
-- insert the new one, causing the inventory to overflow.
|
||||
item.Drop()
|
||||
Entity.Spawner.AddEntityToRemoveQueue(item)
|
||||
|
||||
Entity.Spawner.AddItemToSpawnQueue(vanillaBloodPrefab, pos, condition, quality, function(newItem)
|
||||
newItem.Rotation = item.Rotation
|
||||
-- Stolen items stay stolen
|
||||
newItem.AllowStealing = item.AllowStealing
|
||||
newItem.OriginalOutpost = item.OriginalOutpost
|
||||
|
||||
if inv ~= nil then
|
||||
if not inv.TryPutItem(newItem, slotIdx, false, true, nil) then
|
||||
print(
|
||||
"ERROR: failed to replace neurotrauma bloodpack ("
|
||||
.. tostring(item)
|
||||
.. ", pos "
|
||||
.. tostring(pos)
|
||||
.. ", slotIdx "
|
||||
.. tostring(slotIdx)
|
||||
.. ", inv "
|
||||
.. tostring(inv)
|
||||
.. ") with new item: "
|
||||
.. tostring(newItem)
|
||||
)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Hook.Add("roundStart", "NT.ConvertBloodPacks", function()
|
||||
replaceItems()
|
||||
end)
|
||||
|
||||
-- Hook.Add("chatMessage", "NT.BloodPackTesting", function(msg, client)
|
||||
-- if (msg == "convertblood") then
|
||||
-- replaceItems()
|
||||
-- end
|
||||
-- end)
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
-- Hooks Lua event "human.CPRSuccess" to prevent fractures from ragdoll jank, and
|
||||
-- apply NT affliction cpr_buff or cause rib fractures in Hooked Lua event "human.CPRFailed"
|
||||
-- human.CPRSuccess was changed to character.CPRSuccess? Way above my paygrade - Lukako
|
||||
|
||||
Hook.Add("character.CPRSuccess", "NT.CPRSuccess", function(animcontroller)
|
||||
if
|
||||
animcontroller == nil
|
||||
or animcontroller.Character == nil
|
||||
or animcontroller.Character.SelectedCharacter == nil
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
local character = animcontroller.Character.SelectedCharacter
|
||||
|
||||
if not HF.HasAffliction(character, "luabotomy") then HF.SetAffliction(character, "luabotomy", 1) end
|
||||
|
||||
if not HF.HasAffliction(character, "cpr_buff_auto") then HF.AddAffliction(character, "cpr_buff", 2) end
|
||||
|
||||
HF.AddAffliction(character, "cpr_fracturebuff", 2) -- prevent fractures during CPR (fuck baro physics)
|
||||
end)
|
||||
|
||||
Hook.Add("character.CPRFailed", "NT.CPRFailed", function(animcontroller)
|
||||
if
|
||||
animcontroller == nil
|
||||
or animcontroller.Character == nil
|
||||
or animcontroller.Character.SelectedCharacter == nil
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
local character = animcontroller.Character.SelectedCharacter
|
||||
|
||||
if not HF.HasAffliction(character, "luabotomy") then HF.SetAffliction(character, "luabotomy", 1) end
|
||||
|
||||
HF.AddAffliction(character, "cpr_fracturebuff", 2) -- prevent fractures during CPR (fuck baro physics)
|
||||
HF.AddAfflictionLimb(character, "blunttrauma", LimbType.Torso, 0.3)
|
||||
|
||||
if
|
||||
HF.Chance(
|
||||
NTConfig.Get("NT_fractureChance", 1)
|
||||
* NTConfig.Get("NT_CPRFractureChance", 1)
|
||||
* 0.2
|
||||
/ HF.GetSkillLevel(animcontroller.Character, "medical")
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "t_fracture", 1)
|
||||
end
|
||||
end)
|
||||
@@ -0,0 +1,25 @@
|
||||
-- Based on config settings, which items ought to be destroyed
|
||||
local function DynamicRemoveItems()
|
||||
local blockedItems = HF.DynamicUnavailableItems()
|
||||
|
||||
for _, item in pairs(Item.ItemList) do
|
||||
local id = item.Prefab.Identifier.Value
|
||||
if blockedItems[id] then item.Remove() end
|
||||
end
|
||||
end
|
||||
|
||||
-- On level swap, remove any items that shouldn't be there
|
||||
Hook.Add("roundEnd", "nt_dynamicremoveitems", function()
|
||||
DynamicRemoveItems()
|
||||
end)
|
||||
|
||||
-- Recreate stores command to make sure newly added items are actually in stores
|
||||
Game.AddCommand("nt_recreatestores", "Recreate all stores.", function()
|
||||
if Game.GameSession.Map ~= nil then
|
||||
for location in Game.GameSession.Map.Locations do
|
||||
location.CreateStores(true)
|
||||
end
|
||||
else
|
||||
print("nt_recreatestores: Tried to recreate stores in the campaign map, but there is none!")
|
||||
end
|
||||
end)
|
||||
+300
@@ -0,0 +1,300 @@
|
||||
-- Hooks Lua event "changeFallDamage" to cause more damage and NT afflictions like fractures and artery cuts on extremities depending on severity
|
||||
local limbtypes = {
|
||||
LimbType.Torso,
|
||||
LimbType.Head,
|
||||
LimbType.LeftArm,
|
||||
LimbType.RightArm,
|
||||
LimbType.LeftLeg,
|
||||
LimbType.RightLeg,
|
||||
}
|
||||
local function HasLungs(c)
|
||||
return not HF.HasAffliction(c, "lungremoved")
|
||||
end
|
||||
|
||||
local function getCalculatedReductionSuit(armor, strength, limbtype)
|
||||
if armor == nil then return 0 end
|
||||
local reduction = 0
|
||||
|
||||
if armor.HasTag("deepdivinglarge") or armor.HasTag("deepdiving") then
|
||||
local modifiers = armor.GetComponentString("Wearable").DamageModifiers
|
||||
for modifier in modifiers do
|
||||
if string.find(modifier.AfflictionIdentifiers, "blunttrauma") ~= nil then
|
||||
reduction = strength - strength * modifier.DamageMultiplier
|
||||
end
|
||||
end
|
||||
elseif armor.HasTag("clothing") and armor.HasTag("smallitem") and limbtype == LimbType.Torso then
|
||||
local modifiers = armor.GetComponentString("Wearable").DamageModifiers
|
||||
for modifier in modifiers do
|
||||
if string.find(modifier.AfflictionIdentifiers, "blunttrauma") ~= nil then
|
||||
reduction = strength - strength * modifier.DamageMultiplier
|
||||
end
|
||||
end
|
||||
end
|
||||
return reduction
|
||||
end
|
||||
local function getCalculatedReductionClothes(armor, strength, limbtype)
|
||||
if armor == nil then return 0 end
|
||||
local reduction = 0
|
||||
if armor.HasTag("deepdiving") or armor.HasTag("diving") then
|
||||
local modifiers = armor.GetComponentString("Wearable").DamageModifiers
|
||||
for modifier in modifiers do
|
||||
if string.find(modifier.AfflictionIdentifiers, "blunttrauma") ~= nil then
|
||||
reduction = strength - strength * modifier.DamageMultiplier
|
||||
end
|
||||
end
|
||||
elseif armor.HasTag("clothing") and armor.HasTag("smallitem") then
|
||||
local modifiers = armor.GetComponentString("Wearable").DamageModifiers
|
||||
for modifier in modifiers do
|
||||
if string.find(modifier.AfflictionIdentifiers, "blunttrauma") ~= nil then
|
||||
reduction = strength - strength * modifier.DamageMultiplier
|
||||
end
|
||||
end
|
||||
end
|
||||
return reduction
|
||||
end
|
||||
local function getCalculatedReductionHelmet(armor, strength)
|
||||
if armor == nil then return 0 end
|
||||
local reduction = 0
|
||||
|
||||
if armor.HasTag("smallitem") then
|
||||
local modifiers = armor.GetComponentString("Wearable").DamageModifiers
|
||||
for modifier in modifiers do
|
||||
if string.find(modifier.AfflictionIdentifiers, "blunttrauma") ~= nil then
|
||||
reduction = strength - strength * modifier.DamageMultiplier
|
||||
end
|
||||
end
|
||||
end
|
||||
return reduction
|
||||
end
|
||||
local function getCalculatedConcussionReduction(armor, strength)
|
||||
if armor == nil then return 0 end
|
||||
local reduction = 0
|
||||
|
||||
if armor.HasTag("deepdiving") or armor.HasTag("deepdivinglarge") then
|
||||
local modifiers = armor.GetComponentString("Wearable").DamageModifiers
|
||||
for modifier in modifiers do
|
||||
if string.find(modifier.AfflictionIdentifiers, "concussion") ~= nil then
|
||||
reduction = strength - strength * modifier.DamageMultiplier
|
||||
end
|
||||
end
|
||||
elseif armor.HasTag("smallitem") then
|
||||
local modifiers = armor.GetComponentString("Wearable").DamageModifiers
|
||||
for modifier in modifiers do
|
||||
if string.find(modifier.AfflictionIdentifiers, "concussion") ~= nil then
|
||||
reduction = strength - strength * modifier.DamageMultiplier
|
||||
end
|
||||
end
|
||||
end
|
||||
return reduction
|
||||
end
|
||||
Hook.Add("changeFallDamage", "NT.falldamage", function(impactDamage, character, impactPos, velocity)
|
||||
-- don't run the code if we ignore the code
|
||||
if not NTConfig.Get("NT_Calculations", true) then return 0 end
|
||||
|
||||
-- dont bother with creatures
|
||||
if not character.IsHuman then return 0 end
|
||||
|
||||
-- dont apply fall damage in water
|
||||
if character.InWater then return 0 end
|
||||
|
||||
-- dont apply fall damage when dragged by someone
|
||||
if character.SelectedBy ~= nil then return 0 end
|
||||
|
||||
-- don't apply fall damage if were specifically immune to it
|
||||
if HF.HasAffliction(character, "cpr_fracturebuff") or HF.HasAffliction(character, "stopcreatureabuse") then
|
||||
return 0
|
||||
end
|
||||
|
||||
if not HF.HasAffliction(character, "luabotomy") then HF.SetAffliction(character, "luabotomy", 1) end
|
||||
|
||||
local velocityMagnitude = HF.Magnitude(velocity)
|
||||
velocityMagnitude = velocityMagnitude ^ 1.3
|
||||
|
||||
-- apply fall damage to all limbs based on fall direction
|
||||
local mainlimbPos = character.AnimController.MainLimb.WorldPosition
|
||||
|
||||
local limbDotResults = {}
|
||||
local minDotRes = 1000
|
||||
|
||||
for limb in character.AnimController.Limbs do
|
||||
for type in limbtypes do
|
||||
if limb.type == type then
|
||||
-- fetch the direction of each limb relative to the torso
|
||||
local limbPosition = limb.WorldPosition
|
||||
local posDif = limbPosition - mainlimbPos
|
||||
posDif.X = posDif.X / 100
|
||||
posDif.Y = posDif.Y / 100
|
||||
local posDifMagnitude = HF.Magnitude(posDif)
|
||||
if posDifMagnitude > 1 then posDif.Normalize() end
|
||||
|
||||
local normalizedVelocity = Vector2(velocity.X, velocity.Y)
|
||||
normalizedVelocity.Normalize()
|
||||
|
||||
-- compare those directions to the direction we're moving
|
||||
-- this will later be used to hurt the limbs facing impact more than the others
|
||||
local limbDot = Vector2.Dot(posDif, normalizedVelocity)
|
||||
limbDotResults[type] = limbDot
|
||||
if minDotRes > limbDot then minDotRes = limbDot end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- shift all weights out of the negatives
|
||||
-- increase the weight of all limbs if speed is high
|
||||
-- the effect of this is that, at higher speeds, all limbs take damage instead of mainly the ones facing the impact site
|
||||
for type, dotResult in pairs(limbDotResults) do
|
||||
limbDotResults[type] = dotResult - minDotRes + math.max(0, (velocityMagnitude - 30) / 10)
|
||||
end
|
||||
|
||||
-- count weight so we're able to distribute the damage fractionally
|
||||
local weightsum = 0
|
||||
for dotResult in limbDotResults do
|
||||
weightsum = weightsum + dotResult
|
||||
end
|
||||
|
||||
for type, dotResult in pairs(limbDotResults) do
|
||||
local relativeWeight = dotResult / weightsum
|
||||
|
||||
-- lets limit the numbers to the max value of blunttrauma so that resistances make sense
|
||||
local damageInflictedToThisLimb = math.min(
|
||||
relativeWeight * math.max(0, velocityMagnitude - 10) ^ 1.5 * NTConfig.Get("NT_falldamage", 1) * 0.5,
|
||||
NTConfig.Get("NT_falldamageCeiling", 1) * 60
|
||||
)
|
||||
NT.CauseFallDamage(character, type, damageInflictedToThisLimb)
|
||||
end
|
||||
|
||||
-- make the normal damage not run
|
||||
return 0
|
||||
end)
|
||||
NT.CauseFallDamage = function(character, limbtype, strength)
|
||||
local armor1 = character.Inventory.GetItemInLimbSlot(InvSlotType.OuterClothes)
|
||||
local armor2 = character.Inventory.GetItemInLimbSlot(InvSlotType.InnerClothes)
|
||||
if limbtype ~= LimbType.Head then
|
||||
strength = math.max(
|
||||
strength
|
||||
- getCalculatedReductionSuit(armor1, strength, limbtype)
|
||||
- getCalculatedReductionClothes(armor2, strength, limbtype),
|
||||
0
|
||||
)
|
||||
else
|
||||
armor2 = character.Inventory.GetItemInLimbSlot(InvSlotType.Head)
|
||||
strength = math.max(
|
||||
strength
|
||||
- getCalculatedReductionSuit(armor1, strength, limbtype)
|
||||
- getCalculatedReductionHelmet(armor2, strength, limbtype),
|
||||
0
|
||||
)
|
||||
end
|
||||
|
||||
-- additionally calculate the affliction reduced damage
|
||||
local prefab = AfflictionPrefab.Prefabs["blunttrauma"]
|
||||
local resistance = character.CharacterHealth.GetResistance(prefab, limbtype)
|
||||
if resistance >= 1 then return end
|
||||
strength = strength * (1 - resistance)
|
||||
HF.AddAfflictionLimb(character, "blunttrauma", limbtype, strength)
|
||||
|
||||
-- return earlier if the strength value is not high enough for damage checks
|
||||
if strength < 1 then return end
|
||||
|
||||
local fractureImmune = false
|
||||
|
||||
local injuryChanceMultiplier = NTConfig.Get("NT_falldamageSeriousInjuryChance", 1)
|
||||
|
||||
-- torso
|
||||
if not fractureImmune and strength >= 1 and limbtype == LimbType.Torso then
|
||||
if
|
||||
HF.Chance(
|
||||
(strength - 15)
|
||||
/ 100
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
* injuryChanceMultiplier
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
if
|
||||
HasLungs(character)
|
||||
and strength >= 5
|
||||
and HF.Chance(
|
||||
strength
|
||||
/ 70
|
||||
* NTC.GetMultiplier(character, "pneumothoraxchance")
|
||||
* NTConfig.Get("NT_pneumothoraxChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "pneumothorax", 5)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- head
|
||||
if not fractureImmune and strength >= 1 and limbtype == LimbType.Head then
|
||||
if strength >= 15 and HF.Chance(math.min(strength / 100, 0.7)) then
|
||||
HF.AddAfflictionResisted(
|
||||
character,
|
||||
"concussion",
|
||||
math.max(
|
||||
10
|
||||
- getCalculatedConcussionReduction(armor1, 10, limbtype)
|
||||
- getCalculatedConcussionReduction(armor2, 10, limbtype),
|
||||
0
|
||||
)
|
||||
)
|
||||
end
|
||||
if
|
||||
strength >= 15
|
||||
and HF.Chance(
|
||||
math.min((strength - 15) / 100, 0.7)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
* injuryChanceMultiplier
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
strength >= 55
|
||||
and HF.Chance(
|
||||
math.min((strength - 15) / 100, 0.7)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
* injuryChanceMultiplier
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "n_fracture", 5)
|
||||
end
|
||||
if strength >= 5 and HF.Chance(0.7) then
|
||||
HF.AddAffliction(character, "cerebralhypoxia", strength * HF.RandomRange(0.1, 0.4))
|
||||
end
|
||||
end
|
||||
|
||||
-- extremities
|
||||
if not fractureImmune and strength >= 1 and HF.LimbIsExtremity(limbtype) then
|
||||
if
|
||||
HF.Chance(
|
||||
(strength - 15)
|
||||
/ 100
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
* injuryChanceMultiplier
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
if HF.Chance((strength - 2) / 60) then
|
||||
-- this is here to simulate open fractures
|
||||
NT.ArteryCutLimb(character, limbtype)
|
||||
end
|
||||
end
|
||||
if
|
||||
HF.Chance(
|
||||
HF.Clamp((strength - 5) / 120, 0, 0.5)
|
||||
* NTC.GetMultiplier(character, "dislocationchance")
|
||||
* NTConfig.Get("NT_dislocationChance", 1)
|
||||
* injuryChanceMultiplier
|
||||
) and not NT.LimbIsAmputated(character, limbtype)
|
||||
then
|
||||
NT.DislocateLimb(character, limbtype)
|
||||
end
|
||||
end
|
||||
end
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
LuaUserData.MakeMethodAccessible(Descriptors["Barotrauma.HumanAIController"], "SpeakAboutIssues")
|
||||
|
||||
-- hopefully this stops bots from doing any rescuing at all.
|
||||
-- and also hopefully my assumption that this very specific thing
|
||||
-- about bots is what is causing them to eat frames is correct.
|
||||
|
||||
if NTConfig.Get("NT_disableBotAlgorithms", true) then
|
||||
Hook.Patch("Barotrauma.AIObjectiveRescueAll", "IsValidTarget", {
|
||||
"Barotrauma.Character",
|
||||
"Barotrauma.Character",
|
||||
"out System.Boolean",
|
||||
}, function(instance, ptable)
|
||||
-- TODO: some bot behavior
|
||||
-- make it hostile act if:
|
||||
-- surgery without corresponding ailments
|
||||
-- treatment without ailments
|
||||
|
||||
-- basic self treatments:
|
||||
-- find items to treat each other for blood loss or bleeding or suturable damage or fractures and dislocations
|
||||
-- ^ would possibly need items to have proper suitable treatments too, and yk bots dont spawn with enough meds...
|
||||
|
||||
ptable.PreventExecution = true
|
||||
return false
|
||||
end, Hook.HookMethodType.Before)
|
||||
end
|
||||
|
||||
local afflictions = {
|
||||
"n_fracture", -- urgent perceivable afflictions
|
||||
"h_arterialcut",
|
||||
"ll_arterialcut",
|
||||
"rl_arterialcut",
|
||||
"ra_arterialcut",
|
||||
"la_arterialcut",
|
||||
"sym_hematemesis", -- urgent causes
|
||||
"sym_paleskin",
|
||||
"sym_confusion",
|
||||
"sym_lightheadedness",
|
||||
"pain_abdominal",
|
||||
"inflammation",
|
||||
"gangrene",
|
||||
"fever",
|
||||
"sym_headache",
|
||||
"sym_blurredvision",
|
||||
"t_fracture", -- not urgent afflictions
|
||||
"h_fracture",
|
||||
"ra_fracture",
|
||||
"la_fracture",
|
||||
"rl_fracture",
|
||||
"ll_fracture",
|
||||
"dislocation1",
|
||||
"dislocation2",
|
||||
"dislocation3",
|
||||
"dislocation4",
|
||||
"pain_chest", -- not urgent causes
|
||||
"sym_weakness",
|
||||
"sym_sweating",
|
||||
"dyspnea",
|
||||
"sym_bloating",
|
||||
"sym_legswelling",
|
||||
"sym_craving",
|
||||
"sym_palpitations",
|
||||
}
|
||||
NT.SymsForNPC = { ntaffs = afflictions }
|
||||
|
||||
-- How to add own symptoms example:
|
||||
--local goobertable = { "goober", "gooberer" }
|
||||
--table.insert(NT.SymsForNPC, goobertable)
|
||||
|
||||
-- allows npcs to talk about their neuro afflictions
|
||||
|
||||
Hook.Patch("Barotrauma.HumanAIController", "SpeakAboutIssues", function(instance)
|
||||
local character = instance.Character
|
||||
|
||||
if not HF.HasAffliction(character, "luabotomy", 1) then return end
|
||||
|
||||
local message = ""
|
||||
local chatType = ChatMessageType.Default
|
||||
|
||||
if ChatMessage.CanUseRadio(character) then chatType = ChatMessageType.Radio end
|
||||
|
||||
for identifier in NT.SymsForNPC.ntaffs do
|
||||
if HF.HasAffliction(character, identifier, 1) then
|
||||
message = TextManager.Get("npcdialogsym." .. identifier)
|
||||
character.Speak(message, chatType, math.random(0, 5), Identifier(identifier .. "DialogSym"), 600.0)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
for table in NT.SymsForNPC do
|
||||
for identifier in table do
|
||||
if HF.HasAffliction(character, identifier, 1) then
|
||||
message = TextManager.Get("npcdialogsym." .. identifier)
|
||||
character.Speak(message, chatType, math.random(0, 5), Identifier(identifier .. "DialogSym"), 600.0)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end, Hook.HookMethodType.After)
|
||||
+2096
File diff suppressed because it is too large
Load Diff
+2784
File diff suppressed because it is too large
Load Diff
+90
@@ -0,0 +1,90 @@
|
||||
-- Spawns items inside medstartercrate
|
||||
-- Hooks XML Lua event "NT.medstartercrate.spawn" to create medstartercrate items and put them inside it
|
||||
Hook.Add(
|
||||
"NT.medstartercrate.spawn",
|
||||
"NT.medstartercrate.spawn",
|
||||
function(effect, deltaTime, item, targets, worldPosition)
|
||||
Timer.Wait(function()
|
||||
if item == nil then return end
|
||||
|
||||
-- check if the item already got populated before
|
||||
-- got broken somehow and is no longer needed, handled with oneshot="true" for the StatusEffect inside the medstartercrate item that calls this hook on spawn
|
||||
|
||||
-- local populated = item.HasTag("used")
|
||||
-- if populated then return end
|
||||
|
||||
-- add used tag
|
||||
|
||||
-- local tags = HF.SplitString(item.Tags,",")
|
||||
-- table.insert(tags,"used")
|
||||
-- local tagstring = ""
|
||||
-- for index, value in ipairs(tags) do
|
||||
-- tagstring = tagstring..value
|
||||
-- if index < #tags then tagstring=tagstring.."," end
|
||||
-- end
|
||||
-- item.Tags = tagstring
|
||||
|
||||
-- populate with goodies!!
|
||||
|
||||
if item.Scale == 0.5 then return end
|
||||
item.Scale = 0.5
|
||||
HF.SpawnItemPlusFunction("medtoolbox", function(params)
|
||||
HF.SpawnItemPlusFunction("defibrillator", nil, nil, params.item.OwnInventory, 0)
|
||||
HF.SpawnItemPlusFunction("autocpr", nil, nil, params.item.OwnInventory, 1)
|
||||
for i = 1, 2, 1 do
|
||||
HF.SpawnItemPlusFunction("tourniquet", nil, nil, params.item.OwnInventory, 2)
|
||||
end
|
||||
for i = 1, 2, 1 do
|
||||
HF.SpawnItemPlusFunction("ringerssolution", nil, nil, params.item.OwnInventory, 3)
|
||||
end
|
||||
HF.SpawnItemPlusFunction("surgicaldrill", nil, nil, params.item.OwnInventory, 4)
|
||||
HF.SpawnItemPlusFunction("surgerysaw", nil, nil, params.item.OwnInventory, 5)
|
||||
end, nil, item.OwnInventory, 0)
|
||||
|
||||
HF.SpawnItemPlusFunction("medtoolbox", function(params)
|
||||
HF.SpawnItemPlusFunction("antibleeding1", nil, nil, params.item.OwnInventory, 0)
|
||||
HF.SpawnItemPlusFunction("gypsum", nil, nil, params.item.OwnInventory, 1)
|
||||
HF.SpawnItemPlusFunction("opium", nil, nil, params.item.OwnInventory, 2)
|
||||
HF.SpawnItemPlusFunction("antibiotics", nil, nil, params.item.OwnInventory, 3)
|
||||
HF.SpawnItemPlusFunction("ointment", nil, nil, params.item.OwnInventory, 4)
|
||||
end, nil, item.OwnInventory, 1)
|
||||
|
||||
HF.SpawnItemPlusFunction("surgerytoolbox", function(params)
|
||||
HF.SpawnItemPlusFunction("advscalpel", nil, nil, params.item.OwnInventory, 0)
|
||||
HF.SpawnItemPlusFunction("advhemostat", nil, nil, params.item.OwnInventory, 1)
|
||||
HF.SpawnItemPlusFunction("advretractors", nil, nil, params.item.OwnInventory, 2)
|
||||
for i = 1, 16, 1 do
|
||||
HF.SpawnItemPlusFunction("suture", nil, nil, params.item.OwnInventory, 3)
|
||||
end
|
||||
HF.SpawnItemPlusFunction("tweezers", nil, nil, params.item.OwnInventory, 4)
|
||||
HF.SpawnItemPlusFunction("traumashears", nil, nil, params.item.OwnInventory, 5)
|
||||
HF.SpawnItemPlusFunction("drainage", nil, nil, params.item.OwnInventory, 6)
|
||||
HF.SpawnItemPlusFunction("needle", nil, nil, params.item.OwnInventory, 7)
|
||||
end, nil, item.OwnInventory, 3)
|
||||
|
||||
HF.SpawnItemPlusFunction("bloodanalyzer", nil, nil, item.OwnInventory, 6)
|
||||
HF.SpawnItemPlusFunction("healthscanner", function(params)
|
||||
local prefab = ItemPrefab.GetItemPrefab("batterycell")
|
||||
Entity.Spawner.AddItemToSpawnQueue(prefab, params["item"].WorldPosition, nil, nil, function(batteryItem)
|
||||
params["item"].OwnInventory.TryPutItem(batteryItem)
|
||||
end)
|
||||
end, nil, item.OwnInventory, 7)
|
||||
end, 35)
|
||||
end
|
||||
)
|
||||
|
||||
Hook.Add("character.giveJobItems", "NT.giveHealthScannersBatteries", function(character)
|
||||
Timer.Wait(function()
|
||||
for item in character.Inventory.AllItems do
|
||||
local thisIdentifier = item.Prefab.Identifier.Value
|
||||
if thisIdentifier == "healthscanner" then
|
||||
if item.OwnInventory ~= nil and item.OwnInventory.GetItemAt(0) == nil then
|
||||
local prefab = ItemPrefab.GetItemPrefab("batterycell")
|
||||
Entity.Spawner.AddItemToSpawnQueue(prefab, character.WorldPosition, nil, nil, function(batteryItem)
|
||||
item.OwnInventory.TryPutItem(batteryItem, character)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end, 1000)
|
||||
end)
|
||||
@@ -0,0 +1,34 @@
|
||||
-- Modders, please use ModDir:Neurotrauma when taking dependencies, and
|
||||
-- name your patches with the word "neurotrauma" (letter case doesnt matter)
|
||||
|
||||
-- sets NT.modconflict to true if incompatible mod detected
|
||||
-- this applies meta affliction "modconflict" every round
|
||||
-- prints out the warning and incompatible mod on server startup
|
||||
-- Hooks Lua event "roundStart" to do the above each round
|
||||
NT.modconflict = false
|
||||
function NT.CheckModConflicts()
|
||||
NT.modconflict = false
|
||||
if NTConfig.Get("NT_ignoreModConflicts", false) then return end
|
||||
|
||||
local itemsToCheck = { "antidama2", "opdeco_hospitalbed" }
|
||||
|
||||
for prefab in ItemPrefab.Prefabs do
|
||||
if HF.TableContains(itemsToCheck, prefab.Identifier.Value) then
|
||||
local mod = prefab.ConfigElement.ContentPackage.Name
|
||||
if not string.find(string.lower(mod), "neurotrauma") then
|
||||
NT.modconflict = true
|
||||
print("Found Neurotrauma incompatibility with mod: ", mod)
|
||||
print("WARNING! mod conflict detected! Neurotrauma may not function correctly and requires a patch!")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Timer.Wait(function()
|
||||
NT.CheckModConflicts()
|
||||
end, 1000)
|
||||
Hook.Add("roundStart", "NT.RoundStart.modconflicts", function()
|
||||
Timer.Wait(function()
|
||||
NT.CheckModConflicts()
|
||||
end, 10000)
|
||||
end)
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
-- NT functions for multiscalpel mode setting
|
||||
-- Hooks XML Lua events defined in the multiscalpel item.xml
|
||||
-- Hooks Lua event "roundStart" to RefreshAllMultiscalpels descriptions
|
||||
function NT.SetMultiscalpelFunction(item, func)
|
||||
if func ~= "" then
|
||||
item.Tags = "multiscalpel_" .. func
|
||||
else
|
||||
item.Tags = ""
|
||||
end
|
||||
NT.RefreshScalpelDescription(item)
|
||||
end
|
||||
|
||||
local function GetMultiscalpelMode(item)
|
||||
local functiontag = ""
|
||||
local tags = HF.SplitString(item.Tags, ",")
|
||||
for tag in tags do
|
||||
if HF.StartsWith(tag, "multiscalpel_") then
|
||||
functiontag = HF.SplitString(tag, "_")[2]
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return functiontag
|
||||
end
|
||||
|
||||
function NT.RefreshScalpelDescription(item)
|
||||
-- if not HF.ItemHasTag(item,"init") then return end
|
||||
-- hostside only
|
||||
if Game.IsMultiplayer and CLIENT then return end
|
||||
|
||||
if not Entity.Spawner then
|
||||
Timer.Wait(function()
|
||||
NT.RefreshScalpelDescription(item)
|
||||
end, 35)
|
||||
return
|
||||
end
|
||||
|
||||
local functiontag = GetMultiscalpelMode(item)
|
||||
|
||||
if functiontag == "" then return end
|
||||
|
||||
local targetinventory = item.ParentInventory
|
||||
local targetslot = 0
|
||||
if targetinventory ~= nil then targetslot = targetinventory.FindIndex(item) end
|
||||
|
||||
local function SpawnFunc(newscalpelitem, targetinventory)
|
||||
if targetinventory ~= nil then targetinventory.TryPutItem(newscalpelitem, targetslot, true, true, nil) end
|
||||
newscalpelitem.DescriptionTag = "multiscalpel." .. functiontag
|
||||
newscalpelitem.Tags = "multiscalpel_" .. functiontag
|
||||
end
|
||||
HF.RemoveItem(item)
|
||||
Timer.Wait(function()
|
||||
local prefab = item.Prefab
|
||||
Entity.Spawner.AddItemToSpawnQueue(prefab, item.WorldPosition, nil, nil, function(newscalpelitem)
|
||||
SpawnFunc(newscalpelitem, targetinventory)
|
||||
end)
|
||||
end, 35)
|
||||
end
|
||||
|
||||
-- Let's faithfully believe Ydrec that said the item descriptions are already serialized by base game
|
||||
--Hook.Add("roundStart", "NT.RoundStart.Multiscalpels", function()
|
||||
-- Timer.Wait(function()
|
||||
-- NT.RefreshAllMultiscalpels()
|
||||
-- end, 10000) -- maybe 10 seconds is enough?
|
||||
--end)
|
||||
|
||||
function NT.RefreshAllMultiscalpels()
|
||||
-- descriptions dont get serialized, so i have to respawn
|
||||
-- every scalpel every round to keep their descriptions (big oof)
|
||||
|
||||
-- fetch scalpel items
|
||||
local scalpelItems = {}
|
||||
for item in Item.ItemList do
|
||||
if item.Prefab.Identifier.Value == "multiscalpel" then table.insert(scalpelItems, item) end
|
||||
end
|
||||
-- refresh items
|
||||
for scalpel in scalpelItems do
|
||||
NT.RefreshScalpelDescription(scalpel)
|
||||
end
|
||||
end
|
||||
--Timer.Wait(function()
|
||||
-- NT.RefreshAllMultiscalpels()
|
||||
--end, 50)
|
||||
|
||||
Hook.Add(
|
||||
"NT.multiscalpel.incision",
|
||||
"NT.multiscalpel.incision",
|
||||
function(effect, deltaTime, item, targets, worldPosition)
|
||||
NT.SetMultiscalpelFunction(item, "incision")
|
||||
end
|
||||
)
|
||||
Hook.Add("NT.multiscalpel.kidneys", "NT.multiscalpel.kidneys", function(effect, deltaTime, item, targets, worldPosition)
|
||||
NT.SetMultiscalpelFunction(item, "kidneys")
|
||||
end)
|
||||
Hook.Add("NT.multiscalpel.liver", "NT.multiscalpel.liver", function(effect, deltaTime, item, targets, worldPosition)
|
||||
NT.SetMultiscalpelFunction(item, "liver")
|
||||
end)
|
||||
Hook.Add("NT.multiscalpel.lungs", "NT.multiscalpel.lungs", function(effect, deltaTime, item, targets, worldPosition)
|
||||
NT.SetMultiscalpelFunction(item, "lungs")
|
||||
end)
|
||||
Hook.Add("NT.multiscalpel.heart", "NT.multiscalpel.heart", function(effect, deltaTime, item, targets, worldPosition)
|
||||
NT.SetMultiscalpelFunction(item, "heart")
|
||||
end)
|
||||
Hook.Add("NT.multiscalpel.brain", "NT.multiscalpel.brain", function(effect, deltaTime, item, targets, worldPosition)
|
||||
NT.SetMultiscalpelFunction(item, "brain")
|
||||
end)
|
||||
Hook.Add("NT.multiscalpel.bandage", "NT.multiscalpel.bandage", function(effect, deltaTime, item, targets, worldPosition)
|
||||
NT.SetMultiscalpelFunction(item, "bandage")
|
||||
end)
|
||||
Hook.Add(
|
||||
"NT.multiscalpel.speedflex",
|
||||
"NT.multiscalpel.speedflex",
|
||||
function(effect, deltaTime, item, targets, worldPosition)
|
||||
NT.SetMultiscalpelFunction(item, "speedflex")
|
||||
end
|
||||
)
|
||||
|
||||
NT.ItemMethods.multiscalpel = function(item, usingCharacter, targetCharacter, limb)
|
||||
local limbtype = HF.NormalizeLimbType(limb.type)
|
||||
|
||||
local mode = GetMultiscalpelMode(item)
|
||||
if mode == "" then mode = "none" end
|
||||
|
||||
local modeFunctions = {
|
||||
none = function(item, usingCharacter, targetCharacter, limb) end,
|
||||
incision = NT.ItemMethods.advscalpel,
|
||||
kidneys = NT.ItemMethods.organscalpel_kidneys,
|
||||
liver = NT.ItemMethods.organscalpel_liver,
|
||||
lungs = NT.ItemMethods.organscalpel_lungs,
|
||||
heart = NT.ItemMethods.organscalpel_heart,
|
||||
brain = NT.ItemMethods.organscalpel_brain,
|
||||
bandage = function(item, usingCharacter, targetCharacter, limb)
|
||||
-- remove casts, bandages, and if none of those apply, cause some damage
|
||||
|
||||
-- code snippet taken from NT.ItemMethods.traumashears
|
||||
-- does the target have any cuttable afflictions?
|
||||
local cuttables = HF.CombineArrays(NT.CuttableAfflictions, NT.TraumashearsAfflictions)
|
||||
local canCut = false
|
||||
for val in cuttables do
|
||||
local prefab = AfflictionPrefab.Prefabs[val]
|
||||
if prefab ~= nil then
|
||||
if prefab.LimbSpecific then
|
||||
if HF.HasAfflictionLimb(targetCharacter, val, limbtype, 0.1) then
|
||||
canCut = true
|
||||
break
|
||||
end
|
||||
elseif limbtype == prefab.IndicatorLimb then
|
||||
if HF.HasAffliction(targetCharacter, val, 0.1) then
|
||||
canCut = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if canCut then
|
||||
NT.ItemMethods.traumashears(item, usingCharacter, targetCharacter, limb)
|
||||
else
|
||||
-- malpractice time!!!!
|
||||
local open = HF.HasAfflictionLimb(targetCharacter, "retractedskin", limbtype, 1)
|
||||
local istorso = limbtype == LimbType.Torso
|
||||
local ishead = limbtype == LimbType.Head
|
||||
|
||||
if not open then
|
||||
HF.AddAfflictionLimb(targetCharacter, "bleeding", limbtype, 6 + math.random() * 4, usingCharacter)
|
||||
HF.AddAfflictionLimb(
|
||||
targetCharacter,
|
||||
"lacerations",
|
||||
limbtype,
|
||||
2.5 + math.random() * 5,
|
||||
usingCharacter
|
||||
)
|
||||
HF.GiveItem(targetCharacter, "ntsfx_slash")
|
||||
else
|
||||
if istorso then
|
||||
-- stabbing an open torso (not good for the organs therein!)
|
||||
|
||||
HF.AddAffliction(targetCharacter, "internalbleeding", 6 + math.random() * 12, usingCharacter)
|
||||
HF.AddAfflictionLimb(
|
||||
targetCharacter,
|
||||
"lacerations",
|
||||
limbtype,
|
||||
4 + math.random() * 6,
|
||||
usingCharacter
|
||||
)
|
||||
HF.AddAfflictionLimb(
|
||||
targetCharacter,
|
||||
"internaldamage",
|
||||
limbtype,
|
||||
4 + math.random() * 6,
|
||||
usingCharacter
|
||||
)
|
||||
|
||||
local case = math.random()
|
||||
local casecount = 4
|
||||
if case < 1 / casecount then
|
||||
HF.AddAffliction(targetCharacter, "kidneydamage", 10 + math.random() * 10, usingCharacter)
|
||||
elseif case < 2 / casecount then
|
||||
HF.AddAffliction(targetCharacter, "liverdamage", 10 + math.random() * 10, usingCharacter)
|
||||
elseif case < 3 / casecount then
|
||||
HF.AddAffliction(targetCharacter, "lungdamage", 10 + math.random() * 10, usingCharacter)
|
||||
elseif case < 4 / casecount then
|
||||
HF.AddAffliction(targetCharacter, "heartdamage", 10 + math.random() * 10, usingCharacter)
|
||||
end
|
||||
elseif ishead then
|
||||
-- stabbing an open head (brain surgery done right!)
|
||||
|
||||
HF.AddAffliction(targetCharacter, "cerebralhypoxia", 15 + math.random() * 15, usingCharacter)
|
||||
HF.AddAfflictionLimb(
|
||||
targetCharacter,
|
||||
"internaldamage",
|
||||
limbtype,
|
||||
10 + math.random() * 10,
|
||||
usingCharacter
|
||||
)
|
||||
HF.AddAfflictionLimb(
|
||||
targetCharacter,
|
||||
"bleeding",
|
||||
limbtype,
|
||||
6 + math.random() * 12,
|
||||
usingCharacter
|
||||
)
|
||||
else
|
||||
-- stabbing an open arm or leg (how to cause fractures)
|
||||
|
||||
HF.AddAfflictionLimb(
|
||||
targetCharacter,
|
||||
"bleeding",
|
||||
limbtype,
|
||||
6 + math.random() * 6,
|
||||
usingCharacter
|
||||
)
|
||||
HF.AddAfflictionLimb(
|
||||
targetCharacter,
|
||||
"lacerations",
|
||||
limbtype,
|
||||
4 + math.random() * 6,
|
||||
usingCharacter
|
||||
)
|
||||
HF.AddAfflictionLimb(
|
||||
targetCharacter,
|
||||
"internaldamage",
|
||||
limbtype,
|
||||
4 + math.random() * 6,
|
||||
usingCharacter
|
||||
)
|
||||
if HF.Chance(0.1) then NT.BreakLimb(targetCharacter, limbtype) end
|
||||
end
|
||||
|
||||
HF.GiveItem(targetCharacter, "ntsfx_slash")
|
||||
end
|
||||
end
|
||||
end,
|
||||
speedflex = function(item, usingCharacter, targetCharacter, limb)
|
||||
local animcontroller = targetCharacter.AnimController
|
||||
local torsoLimb = limb
|
||||
if animcontroller ~= nil then torsoLimb = animcontroller.MainLimb end
|
||||
|
||||
if limbtype == LimbType.Head then
|
||||
NT.ItemMethods.organscalpel_brain(item, usingCharacter, targetCharacter, limb)
|
||||
elseif limbtype == LimbType.LeftArm then
|
||||
NT.ItemMethods.organscalpel_kidneys(item, usingCharacter, targetCharacter, torsoLimb)
|
||||
elseif limbtype == LimbType.Torso then
|
||||
NT.ItemMethods.organscalpel_liver(item, usingCharacter, targetCharacter, torsoLimb)
|
||||
elseif limbtype == LimbType.RightArm then
|
||||
NT.ItemMethods.organscalpel_heart(item, usingCharacter, targetCharacter, torsoLimb)
|
||||
elseif limbtype == LimbType.LeftLeg then
|
||||
NT.ItemMethods.organscalpel_lungs(item, usingCharacter, targetCharacter, torsoLimb)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
if modeFunctions[mode] ~= nil then modeFunctions[mode](item, usingCharacter, targetCharacter, limb) end
|
||||
|
||||
if mode ~= "none" then Timer.Wait(function()
|
||||
item.Tags = "multiscalpel_" .. mode
|
||||
end, 50) end
|
||||
end
|
||||
+334
@@ -0,0 +1,334 @@
|
||||
NTC = {} -- a class containing compatibility functions for other mods to make use of neurotraumas symptom system
|
||||
|
||||
-- use this function to register your expansion mod to be displayed by the
|
||||
-- console lua startup readout of neurotrauma expansions
|
||||
|
||||
-- check surgery plus or cybernetics for an example
|
||||
-- example of code for registering your expansion in init.lua:
|
||||
|
||||
-- MyExp = {} -- Example Expansions
|
||||
-- MyExp.Name="My Expansion"
|
||||
-- MyExp.Version = "A1.0"
|
||||
-- MyExp.VersionNum = 01000000 -- split into two digits (01->1.; 00->0.; 00->0h; 00->0) -> 1.0.0h0
|
||||
-- MyExp.MinNTVersion = "A1.7.1"
|
||||
-- MyExp.MinNTVersionNum = 01070100 -- 01.07.01.00 -> A1.7.1h0
|
||||
-- Timer.Wait(function() if NT ~= nil then NTC.RegisterExpansion(MyExp) end end,1)
|
||||
|
||||
NTC.RegisteredExpansions = {}
|
||||
-- The function to add your addon to NT, see above for more info.
|
||||
---@param expansionMainObject table The table of the addon.
|
||||
function NTC.RegisterExpansion(expansionMainObject)
|
||||
table.insert(NTC.RegisteredExpansions, expansionMainObject)
|
||||
end
|
||||
|
||||
-- a table of tables, each character that has some custom data has an entry
|
||||
NTC.CharacterData = {}
|
||||
|
||||
-- use this function to induce symptoms temporarily
|
||||
-- duration is in humanupdates (~2 seconds), should at least be 2 to prevent symptom flickering
|
||||
---@param character Character The character to set the symptom on.
|
||||
---@param symptomidentifer string The identifier of the symptom.
|
||||
---@param duration integer The number of human updates it lasts for.
|
||||
function NTC.SetSymptomTrue(character, symptomidentifer, duration)
|
||||
if duration == nil then duration = 2 end
|
||||
|
||||
NTC.AddEmptyCharacterData(character)
|
||||
local data = NTC.GetCharacterData(character)
|
||||
data[symptomidentifer] = duration
|
||||
|
||||
NTC.CharacterData[character.ID] = data
|
||||
end
|
||||
|
||||
-- use this function to suppress symptoms temporarily. this takes precedence over NTC.SetSymptomTrue.
|
||||
-- duration is in humanupdates (~2 seconds), should at least be 2 to prevent symptom flickering
|
||||
---@param character Character The character to set the symptom on.
|
||||
---@param symptomidentifer string The identifier of the symptom.
|
||||
---@param duration integer The number of human updates it lasts for.
|
||||
function NTC.SetSymptomFalse(character, symptomidentifer, duration)
|
||||
if duration == nil then duration = 2 end
|
||||
|
||||
NTC.AddEmptyCharacterData(character)
|
||||
local data = NTC.GetCharacterData(character)
|
||||
data["!" .. symptomidentifer] = duration
|
||||
|
||||
NTC.CharacterData[character.ID] = data
|
||||
end
|
||||
|
||||
-- usage example: anywhere in your lua code, cause 4 seconds (2 humanupdates) of pale skin with this:
|
||||
-- NTC.SetSymptomTrue(targetCharacter,"sym_paleskin",2)
|
||||
|
||||
-- a list of possible symptom identifiers:
|
||||
|
||||
-- sym_unconsciousness
|
||||
-- tachycardia
|
||||
-- hyperventilation
|
||||
-- hypoventilation
|
||||
-- dyspnea
|
||||
-- sym_cough
|
||||
-- sym_paleskin
|
||||
-- sym_lightheadedness
|
||||
-- sym_blurredvision
|
||||
-- sym_confusion
|
||||
-- sym_headache
|
||||
-- sym_legswelling
|
||||
-- sym_weakness
|
||||
-- sym_wheezing
|
||||
-- sym_vomiting
|
||||
-- sym_nausea
|
||||
-- sym_hematemesis
|
||||
-- sym_fever
|
||||
-- sym_abdomdiscomfort
|
||||
-- sym_bloating
|
||||
-- sym_jaundice
|
||||
-- sym_sweating
|
||||
-- sym_palpitations
|
||||
-- sym_craving
|
||||
-- pain_abdominal
|
||||
-- pain_chest
|
||||
-- lockleftarm
|
||||
-- lockrightarm
|
||||
-- lockleftleg
|
||||
-- lockrightleg
|
||||
|
||||
-- with the following identifiers you can either cause things or prevent them.
|
||||
-- i recommend setting the duration when using these to cause things to 1.
|
||||
|
||||
-- triggersym_seizure
|
||||
-- triggersym_coma
|
||||
-- triggersym_stroke
|
||||
-- triggersym_heartattack
|
||||
-- triggersym_cardiacarrest
|
||||
-- triggersym_respiratoryarrest
|
||||
|
||||
-- prints all of the current compatibility data in the chat
|
||||
-- might be useful for debugging
|
||||
function NTC.DebugPrintAllData()
|
||||
local res = "neurotrauma compatibility data:\n"
|
||||
for key, value in pairs(NTC.CharacterData) do
|
||||
res = res .. "\n" .. value["character"].Name
|
||||
for key2, value2 in pairs(value) do
|
||||
res = res .. "\n " .. tostring(key2) .. " : " .. tostring(value2)
|
||||
end
|
||||
end
|
||||
|
||||
PrintChat(res)
|
||||
end
|
||||
|
||||
NTC.PreHumanUpdateHooks = {}
|
||||
-- use this function to add a function to be executed before humanupdate with a character parameter
|
||||
---@param func function The actual function to be called before a human update.
|
||||
function NTC.AddPreHumanUpdateHook(func)
|
||||
NTC.PreHumanUpdateHooks[#NTC.PreHumanUpdateHooks + 1] = func
|
||||
end
|
||||
|
||||
NTC.HumanUpdateHooks = {}
|
||||
-- use this function to add a function to be executed after humanupdate with a character parameter
|
||||
---@param func function The actual function to be called after a human update.
|
||||
function NTC.AddHumanUpdateHook(func)
|
||||
NTC.HumanUpdateHooks[#NTC.HumanUpdateHooks + 1] = func
|
||||
end
|
||||
|
||||
NTC.OnDamagedHooks = {}
|
||||
-- use this function to add a function to be executed after ondamaged
|
||||
-- with a characterhealth, attack result and limb parameter
|
||||
---@param func function The actual function to be called on damaged.
|
||||
function NTC.AddOnDamagedHook(func)
|
||||
NTC.OnDamagedHooks[#NTC.OnDamagedHooks + 1] = func
|
||||
end
|
||||
|
||||
NTC.ModifyingOnDamagedHooks = {}
|
||||
-- use this function to add a function to be executed before ondamaged
|
||||
-- with a characterhealth, afflictions and limb parameter, and afflictions return type
|
||||
---@param func function The actual function to be called on damaged. Modifying.
|
||||
function NTC.AddModifyingOnDamagedHook(func)
|
||||
NTC.ModifyingOnDamagedHooks[#NTC.ModifyingOnDamagedHooks + 1] = func
|
||||
end
|
||||
|
||||
NTC.CharacterSpeedMultipliers = {}
|
||||
-- use this function to multiply a characters speed for one human update.
|
||||
-- should always be called from within a prehumanupdate hook
|
||||
---@param character Character The character to set the speed multiplier to.
|
||||
---@param multiplier number The value of the multiplier.
|
||||
function NTC.MultiplySpeed(character, multiplier)
|
||||
if NTC.CharacterSpeedMultipliers[character] == nil then
|
||||
NTC.CharacterSpeedMultipliers[character] = multiplier
|
||||
else
|
||||
NTC.CharacterSpeedMultipliers[character] = NTC.CharacterSpeedMultipliers[character] * multiplier
|
||||
end
|
||||
end
|
||||
|
||||
-- use this function to register an affliction to be detected by the hematology analyzer
|
||||
---@param identifier string The identifier of the affliction to be visible on hematology.
|
||||
function NTC.AddHematologyAffliction(identifier)
|
||||
Timer.Wait(function()
|
||||
if not HF.TableContains(NT.HematologyDetectable, identifier) then
|
||||
table.insert(NT.HematologyDetectable, identifier)
|
||||
end
|
||||
end, 1)
|
||||
end
|
||||
|
||||
-- use this function to register an affliction to be healed by sutures
|
||||
-- identifier: the identifier of the affliction to be healed
|
||||
-- surgeryskillgain: how much surgery skill is gained by healing this affliction (optional, default: 0)
|
||||
-- requiredaffliction: what affliction has to be present alongside the healed affliction for it to get healed (optional, default: none)
|
||||
-- func: a function that gets run if the affliction is present. if provided, doesnt heal the affliction automatically (optional, default: none)
|
||||
-- func(item, usingCharacter, targetCharacter, limb)
|
||||
---@param identifier string The identifier of the affliction to be cured by sutures.
|
||||
---@param surgeryskillgain number The surgeryskill increase from sutured affliction.
|
||||
---@param requiredaffliction string The identifier of the requiredaffliction (Might need more documentation!)
|
||||
---@param func function The function to be called when the affliction is cured with sutures.
|
||||
function NTC.AddSuturedAffliction(identifier, surgeryskillgain, requiredaffliction, func)
|
||||
Timer.Wait(function()
|
||||
if not HF.TableContains(NT.SutureAfflictions, identifier) then
|
||||
NT.SutureAfflictions[identifier] = {
|
||||
xpgain = surgeryskillgain,
|
||||
case = requiredaffliction,
|
||||
func = func,
|
||||
}
|
||||
end
|
||||
end, 1)
|
||||
end
|
||||
|
||||
-- use this function to register an affliction to be healed by drainage
|
||||
---@param identifier string The identifier of the affliction to be healed.
|
||||
function NTC.AddDrainageAffliction(identifier)
|
||||
Timer.Wait(function()
|
||||
if not HF.TableContains(NT.DrainageAfflictions, identifier) then
|
||||
table.insert(NT.DrainageAfflictions, identifier)
|
||||
end
|
||||
end, 1)
|
||||
end
|
||||
|
||||
NTC.AfflictionsAffectingVitality = {
|
||||
bleeding = true,
|
||||
bleedingnonstop = true,
|
||||
burn = true,
|
||||
acidburn = true,
|
||||
lacerations = true,
|
||||
gunshotwound = true,
|
||||
bitewounds = true,
|
||||
explosiondamage = true,
|
||||
blunttrauma = true,
|
||||
internaldamage = true,
|
||||
organdamage = true,
|
||||
cerebralhypoxia = true,
|
||||
gangrene = true,
|
||||
th_amputation = true,
|
||||
sh_amputation = true,
|
||||
suturedw = true,
|
||||
alcoholaddiction = true,
|
||||
opiateaddiction = true,
|
||||
}
|
||||
-- use this function to register an affliction that will cause vitality damage. (Might need more documentation!)
|
||||
---@param identifier string The identifier of the affliction to cause damage.
|
||||
function NTC.AddAfflictionAffectingVitality(identifier)
|
||||
NTC.AfflictionsAffectingVitality[identifier] = true
|
||||
end
|
||||
|
||||
-- these functions are used by neurotrauma to check for symptom overrides
|
||||
---@param character Character The character to check the symptom on.
|
||||
---@param symptomidentifer string The identifier of the symptom.
|
||||
---@return boolean
|
||||
function NTC.GetSymptom(character, symptomidentifer)
|
||||
local chardata = NTC.GetCharacterData(character)
|
||||
if chardata == nil then return false end
|
||||
|
||||
local durationleft = chardata[symptomidentifer]
|
||||
|
||||
if durationleft == nil then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
---@param character Character The character to check the symptom false on.
|
||||
---@param symptomidentifer string The identifier of the symptom false.
|
||||
---@return boolean
|
||||
function NTC.GetSymptomFalse(character, symptomidentifer)
|
||||
local chardata = NTC.GetCharacterData(character)
|
||||
if chardata == nil then return false end
|
||||
|
||||
local durationleft = chardata["!" .. symptomidentifer]
|
||||
|
||||
if durationleft == nil then return false end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
-- sets multiplier data for one humanupdate, should be called from within a humanupdate hook
|
||||
---@param character Character The character to set the multiplier on.
|
||||
---@param multiplieridentifier string The identifier of the multiplier.
|
||||
---@param multiplier number The multiplier number.
|
||||
function NTC.SetMultiplier(character, multiplieridentifier, multiplier)
|
||||
NTC.AddEmptyCharacterData(character)
|
||||
local data = NTC.GetCharacterData(character)
|
||||
data["mult_" .. multiplieridentifier] = NTC.GetMultiplier(character, multiplieridentifier) * multiplier
|
||||
NTC.CharacterData[character.ID] = data
|
||||
end
|
||||
|
||||
---@param character Character The character to set the multiplier on.
|
||||
---@param multiplieridentifier string The identifier of the multiplier.
|
||||
function NTC.GetMultiplier(character, multiplieridentifier)
|
||||
local data = NTC.GetCharacterData(character)
|
||||
if data == nil or data["mult_" .. multiplieridentifier] == nil then return 1 end
|
||||
return data["mult_" .. multiplieridentifier]
|
||||
end
|
||||
|
||||
-- sets tag data for one humanupdate, should be called from within a humanupdate hook
|
||||
---@param character Character The character to set the tag on.
|
||||
---@param multiplieridentifier string The identifier of the tag.
|
||||
function NTC.SetTag(character, tagidentifier)
|
||||
NTC.AddEmptyCharacterData(character)
|
||||
local data = NTC.GetCharacterData(character)
|
||||
data["tag_" .. tagidentifier] = 1
|
||||
end
|
||||
function NTC.HasTag(character, tagidentifier)
|
||||
local data = NTC.GetCharacterData(character)
|
||||
if data == nil or data["tag_" .. tagidentifier] == nil then return false end
|
||||
return true
|
||||
end
|
||||
|
||||
-- // Utility functions //
|
||||
-- don't concern yourself with these
|
||||
function NTC.AddEmptyCharacterData(character)
|
||||
if NTC.GetCharacterData(character) ~= nil then return end
|
||||
local newdat = {}
|
||||
newdat["character"] = character
|
||||
NTC.CharacterData[character.ID] = newdat
|
||||
end
|
||||
function NTC.CheckChardataEmpty(character)
|
||||
local chardat = NTC.GetCharacterData(character)
|
||||
if chardat == nil or HF.TableSize(chardat) > 1 then return end
|
||||
|
||||
-- remove entry from data
|
||||
NTC.CharacterData[character.ID] = nil
|
||||
end
|
||||
function NTC.GetCharacterData(character)
|
||||
return NTC.CharacterData[character.ID]
|
||||
end
|
||||
function NTC.TickCharacter(character)
|
||||
local chardata = NTC.GetCharacterData(character)
|
||||
if chardata == nil then return end
|
||||
|
||||
for key, value in pairs(chardata) do
|
||||
if key ~= "character" then
|
||||
if HF.StartsWith(key, "mult_") then -- multipliers
|
||||
chardata[key] = nil
|
||||
NTC.CheckChardataEmpty(character)
|
||||
else -- symptoms
|
||||
local durationleft = value
|
||||
if durationleft ~= nil and durationleft > 1 then
|
||||
chardata[key] = durationleft - 1
|
||||
else
|
||||
chardata[key] = nil
|
||||
NTC.CheckChardataEmpty(character)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
NTC.CharacterData[character.ID] = chardata
|
||||
end
|
||||
function NTC.GetSpeedMultiplier(character)
|
||||
if NTC.CharacterSpeedMultipliers[character] ~= nil then return NTC.CharacterSpeedMultipliers[character] end
|
||||
return 1
|
||||
end
|
||||
+733
@@ -0,0 +1,733 @@
|
||||
-- Hooks Lua event "character.applyDamage" to cause NT afflictions after attacks depending on the damaging affliction defined here in NT.OnDamagedMethods
|
||||
local function getCalculatedConcussionReduction(armor, strength)
|
||||
if armor == nil then return 0 end
|
||||
local reduction = 0
|
||||
|
||||
if armor.HasTag("deepdiving") or armor.HasTag("deepdivinglarge") then
|
||||
local modifiers = armor.GetComponentString("Wearable").DamageModifiers
|
||||
for modifier in modifiers do
|
||||
if string.find(modifier.AfflictionIdentifiers, "concussion") ~= nil then
|
||||
reduction = strength - strength * modifier.DamageMultiplier
|
||||
end
|
||||
end
|
||||
elseif armor.HasTag("smallitem") then
|
||||
local modifiers = armor.GetComponentString("Wearable").DamageModifiers
|
||||
for modifier in modifiers do
|
||||
if string.find(modifier.AfflictionIdentifiers, "concussion") ~= nil then
|
||||
reduction = strength - strength * modifier.DamageMultiplier
|
||||
end
|
||||
end
|
||||
end
|
||||
return reduction
|
||||
end
|
||||
Hook.Add(
|
||||
"character.damageLimb",
|
||||
"NT.ondamagedby",
|
||||
function(
|
||||
character,
|
||||
worldPosition,
|
||||
hitLimb,
|
||||
afflictions,
|
||||
stun,
|
||||
playSound,
|
||||
attackImpulse,
|
||||
attacker,
|
||||
damageMultiplier,
|
||||
allowStacking,
|
||||
penetration,
|
||||
shouldImplode
|
||||
)
|
||||
if -- invalid attack data, don't do anything
|
||||
character == nil
|
||||
or character.IsDead
|
||||
or not character.IsHuman
|
||||
or afflictions == nil
|
||||
or hitLimb == nil
|
||||
or hitLimb.IsSevered
|
||||
or attacker == nil
|
||||
or not NTConfig.Get("NT_Calculations", true)
|
||||
then
|
||||
return
|
||||
end
|
||||
local creatureCategory = NTConfig.Get("NT_creatureNoFallDamage", 1)
|
||||
-- they make the game miserable with falldamage on
|
||||
for val in creatureCategory do
|
||||
if attacker.SpeciesName == val then
|
||||
HF.AddAffliction(character, "stopcreatureabuse", 2)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
Hook.Add("character.applyDamage", "NT.ondamaged", function(characterHealth, attackResult, hitLimb)
|
||||
--print(hitLimb.HealthIndex or hitLimb ~= nil)
|
||||
|
||||
if -- invalid attack data, don't do anything
|
||||
characterHealth == nil
|
||||
or characterHealth.Character == nil
|
||||
or characterHealth.Character.IsDead
|
||||
or not characterHealth.Character.IsHuman
|
||||
or attackResult == nil
|
||||
or attackResult.Afflictions == nil
|
||||
or #attackResult.Afflictions <= 0
|
||||
or hitLimb == nil
|
||||
or hitLimb.IsSevered
|
||||
or not NTConfig.Get("NT_Calculations", true)
|
||||
then
|
||||
return
|
||||
end
|
||||
|
||||
if not HF.HasAffliction(characterHealth.Character, "luabotomy") then
|
||||
HF.SetAffliction(characterHealth.Character, "luabotomy", 1)
|
||||
end
|
||||
|
||||
local afflictions = attackResult.Afflictions
|
||||
|
||||
-- ntc
|
||||
-- modifying ondamaged hooks
|
||||
for key, val in pairs(NTC.ModifyingOnDamagedHooks) do
|
||||
afflictions = val(characterHealth, afflictions, hitLimb)
|
||||
end
|
||||
|
||||
local identifier = ""
|
||||
local methodtorun = nil
|
||||
for value in afflictions do
|
||||
-- execute fitting method, if available
|
||||
identifier = value.Prefab.Identifier.Value
|
||||
methodtorun = NT.OnDamagedMethods[identifier]
|
||||
if methodtorun ~= nil then
|
||||
-- make resistance from afflictions apply
|
||||
local resistance = HF.GetResistance(characterHealth.Character, identifier, hitLimb.type)
|
||||
local strength = value.Strength * (1 - resistance)
|
||||
|
||||
methodtorun(characterHealth.Character, strength, hitLimb.type)
|
||||
end
|
||||
end
|
||||
|
||||
-- ntc
|
||||
-- ondamaged hooks
|
||||
for key, val in pairs(NTC.OnDamagedHooks) do
|
||||
val(characterHealth, attackResult, hitLimb)
|
||||
end
|
||||
end)
|
||||
|
||||
NT.OnDamagedMethods = {}
|
||||
|
||||
local function HasLungs(c)
|
||||
return not HF.HasAffliction(c, "lungremoved")
|
||||
end
|
||||
local function HasHeart(c)
|
||||
return not HF.HasAffliction(c, "heartremoved")
|
||||
end
|
||||
|
||||
-- cause foreign bodies, rib fractures, pneumothorax, tamponade, internal bleeding, fractures, neurotrauma
|
||||
NT.OnDamagedMethods.gunshotwound = function(character, strength, limbtype)
|
||||
limbtype = HF.NormalizeLimbType(limbtype)
|
||||
|
||||
local causeFullForeignBody = false
|
||||
|
||||
-- torso specific
|
||||
if strength >= 1 and limbtype == LimbType.Torso then
|
||||
local hitOrgan = false
|
||||
if
|
||||
HF.Chance(
|
||||
HF.Clamp(strength * 0.02, 0, 0.3)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
causeFullForeignBody = true
|
||||
end
|
||||
if
|
||||
HasLungs(character)
|
||||
and HF.Chance(
|
||||
0.3 * NTC.GetMultiplier(character, "pneumothoraxchance") * NTConfig.Get("NT_pneumothoraxChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "pneumothorax", 5)
|
||||
HF.AddAffliction(character, "lungdamage", strength)
|
||||
HF.AddAffliction(character, "organdamage", strength / 4)
|
||||
hitOrgan = true
|
||||
end
|
||||
if
|
||||
HasHeart(character)
|
||||
and hitOrgan == false
|
||||
and strength >= 5
|
||||
and HF.Chance(
|
||||
strength / 50 * NTC.GetMultiplier(character, "tamponadechance") * NTConfig.Get("NT_tamponadeChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "tamponade", 5)
|
||||
HF.AddAffliction(character, "heartdamage", strength)
|
||||
HF.AddAffliction(character, "organdamage", strength / 4)
|
||||
hitOrgan = true
|
||||
end
|
||||
if strength >= 5 then HF.AddAffliction(character, "internalbleeding", strength * HF.RandomRange(0.3, 0.6)) end
|
||||
|
||||
-- liver and kidney damage
|
||||
if hitOrgan == false and strength >= 2 and HF.Chance(0.5) then
|
||||
HF.AddAfflictionLimb(character, "organdamage", limbtype, strength / 4)
|
||||
if HF.Chance(0.5) then
|
||||
HF.AddAffliction(character, "liverdamage", strength)
|
||||
else
|
||||
HF.AddAffliction(character, "kidneydamage", strength)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- head
|
||||
if strength >= 1 and limbtype == LimbType.Head then
|
||||
if
|
||||
HF.Chance(
|
||||
strength / 90 * NTC.GetMultiplier(character, "anyfracturechance") * NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
causeFullForeignBody = true
|
||||
end
|
||||
if strength >= 5 and HF.Chance(0.7) then
|
||||
HF.AddAffliction(character, "cerebralhypoxia", strength * HF.RandomRange(0.1, 0.4))
|
||||
end
|
||||
end
|
||||
|
||||
-- extremities
|
||||
if strength >= 1 and HF.LimbIsExtremity(limbtype) then
|
||||
if
|
||||
NT.LimbIsBroken(character, limbtype)
|
||||
and not NT.LimbIsAmputated(character, limbtype)
|
||||
and HF.Chance(
|
||||
strength
|
||||
/ 60
|
||||
* NTC.GetMultiplier(character, "traumamputatechance")
|
||||
* NTConfig.Get("NT_traumaticAmputationChance", 1)
|
||||
)
|
||||
then
|
||||
NT.TraumamputateLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HF.Chance(
|
||||
strength / 60 * NTC.GetMultiplier(character, "anyfracturechance") * NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
causeFullForeignBody = true
|
||||
end
|
||||
end
|
||||
|
||||
-- foreign bodies
|
||||
if causeFullForeignBody then
|
||||
HF.AddAfflictionLimb(
|
||||
character,
|
||||
"foreignbody",
|
||||
limbtype,
|
||||
HF.Clamp(strength, 0, 30) * NTC.GetMultiplier(character, "foreignbodymultiplier")
|
||||
)
|
||||
else
|
||||
if HF.Chance(0.75) then
|
||||
HF.AddAfflictionLimb(
|
||||
character,
|
||||
"foreignbody",
|
||||
limbtype,
|
||||
HF.Clamp(strength / 4, 0, 20) * NTC.GetMultiplier(character, "foreignbodymultiplier")
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- cause foreign bodies, rib fractures, pneumothorax, internal bleeding, concussion, fractures
|
||||
NT.OnDamagedMethods.explosiondamage = function(character, strength, limbtype)
|
||||
limbtype = HF.NormalizeLimbType(limbtype)
|
||||
|
||||
if HF.Chance(0.75) then
|
||||
HF.AddAfflictionLimb(
|
||||
character,
|
||||
"foreignbody",
|
||||
limbtype,
|
||||
strength / 2 * NTC.GetMultiplier(character, "foreignbodymultiplier")
|
||||
)
|
||||
end
|
||||
|
||||
-- torso specific
|
||||
if strength >= 1 and limbtype == LimbType.Torso then
|
||||
if
|
||||
strength >= 10
|
||||
and HF.Chance(
|
||||
strength / 50 * NTC.GetMultiplier(character, "anyfracturechance") * NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HasLungs(character)
|
||||
and strength >= 5
|
||||
and HF.Chance(
|
||||
strength
|
||||
/ 50
|
||||
* NTC.GetMultiplier(character, "pneumothoraxchance")
|
||||
* NTConfig.Get("NT_pneumothoraxChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "pneumothorax", 5)
|
||||
end
|
||||
if strength >= 5 then HF.AddAffliction(character, "internalbleeding", strength * HF.RandomRange(0.2, 0.5)) end
|
||||
end
|
||||
|
||||
-- head
|
||||
if strength >= 1 and limbtype == LimbType.Head then
|
||||
if strength >= 15 and HF.Chance(math.min(strength / 60, 0.7)) then
|
||||
local armor1 = character.Inventory.GetItemInLimbSlot(InvSlotType.OuterClothes)
|
||||
local armor2 = character.Inventory.GetItemInLimbSlot(InvSlotType.Head)
|
||||
local reduceddmg = math.max(
|
||||
10
|
||||
- getCalculatedConcussionReduction(armor1, 10, limbtype)
|
||||
- getCalculatedConcussionReduction(armor2, 10, limbtype),
|
||||
0
|
||||
)
|
||||
HF.AddAfflictionResisted(character, "concussion", reduceddmg)
|
||||
end
|
||||
if
|
||||
strength >= 15
|
||||
and HF.Chance(
|
||||
math.min(strength / 60, 0.7)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
strength >= 15
|
||||
and HF.Chance(
|
||||
math.min(strength / 60, 0.7)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "n_fracture", 5)
|
||||
end
|
||||
if strength >= 75 and HF.Chance(0.25) then
|
||||
-- drop previously held item
|
||||
local previtem = HF.GetHeadWear(character)
|
||||
if previtem ~= nil then previtem.Drop(character, true) end
|
||||
NT.TraumamputateLimb(character, limbtype)
|
||||
end
|
||||
end
|
||||
|
||||
-- extremities
|
||||
if strength >= 1 and HF.LimbIsExtremity(limbtype) then
|
||||
if
|
||||
NT.LimbIsBroken(character, limbtype)
|
||||
and not NT.LimbIsAmputated(character, limbtype)
|
||||
and HF.Chance(
|
||||
strength
|
||||
/ 60
|
||||
* NTC.GetMultiplier(character, "traumamputatechance")
|
||||
* NTConfig.Get("NT_traumaticAmputationChance", 1)
|
||||
)
|
||||
then
|
||||
NT.TraumamputateLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HF.Chance(
|
||||
strength / 60 * NTC.GetMultiplier(character, "anyfracturechance") * NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HF.Chance(
|
||||
0.35 * NTC.GetMultiplier(character, "dislocationchance") * NTConfig.Get("NT_dislocationChance", 1)
|
||||
) and not NT.LimbIsAmputated(character, limbtype)
|
||||
then
|
||||
NT.DislocateLimb(character, limbtype)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- cause rib fractures, pneumothorax, internal bleeding, concussion, fractures
|
||||
NT.OnDamagedMethods.bitewounds = function(character, strength, limbtype)
|
||||
limbtype = HF.NormalizeLimbType(limbtype)
|
||||
|
||||
-- torso specific
|
||||
if strength >= 1 and limbtype == LimbType.Torso then
|
||||
if
|
||||
strength >= 10
|
||||
and HF.Chance(
|
||||
(strength - 10)
|
||||
/ 50
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HasLungs(character)
|
||||
and strength >= 5
|
||||
and HF.Chance(
|
||||
(strength - 5)
|
||||
/ 50
|
||||
* NTC.GetMultiplier(character, "pneumothoraxchance")
|
||||
* NTConfig.Get("NT_pneumothoraxChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "pneumothorax", 5)
|
||||
end
|
||||
if strength >= 5 then HF.AddAffliction(character, "internalbleeding", strength * HF.RandomRange(0.2, 0.5)) end
|
||||
end
|
||||
|
||||
-- head
|
||||
if strength >= 1 and limbtype == LimbType.Head then
|
||||
if strength >= 15 and HF.Chance(math.min(strength / 60, 0.7)) then
|
||||
local armor1 = character.Inventory.GetItemInLimbSlot(InvSlotType.OuterClothes)
|
||||
local armor2 = character.Inventory.GetItemInLimbSlot(InvSlotType.Head)
|
||||
local reduceddmg = math.max(
|
||||
10
|
||||
- getCalculatedConcussionReduction(armor1, 10, limbtype)
|
||||
- getCalculatedConcussionReduction(armor2, 10, limbtype),
|
||||
0
|
||||
)
|
||||
HF.AddAfflictionResisted(character, "concussion", reduceddmg)
|
||||
end
|
||||
if
|
||||
strength >= 15
|
||||
and HF.Chance(
|
||||
math.min((strength - 10) / 60, 0.7)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
end
|
||||
|
||||
-- extremities
|
||||
if strength >= 1 and HF.LimbIsExtremity(limbtype) then
|
||||
if
|
||||
NT.LimbIsBroken(character, limbtype)
|
||||
and not NT.LimbIsAmputated(character, limbtype)
|
||||
and HF.Chance(
|
||||
(strength - 5)
|
||||
/ 60
|
||||
* NTC.GetMultiplier(character, "traumamputatechance")
|
||||
* NTConfig.Get("NT_traumaticAmputationChance", 1)
|
||||
)
|
||||
then
|
||||
NT.TraumamputateLimb(character, limbtype, character.LastAttacker)
|
||||
end
|
||||
if
|
||||
HF.Chance(
|
||||
(strength - 5)
|
||||
/ 60
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- cause rib fractures, pneumothorax, tamponade, internal bleeding, fractures
|
||||
NT.OnDamagedMethods.lacerations = function(character, strength, limbtype)
|
||||
limbtype = HF.NormalizeLimbType(limbtype)
|
||||
|
||||
-- torso specific
|
||||
if strength >= 1 and limbtype == LimbType.Torso then
|
||||
if
|
||||
strength >= 10
|
||||
and HF.Chance(
|
||||
(strength - 10)
|
||||
/ 50
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HasLungs(character)
|
||||
and strength >= 5
|
||||
and HF.Chance(
|
||||
(strength - 5)
|
||||
/ 50
|
||||
* NTC.GetMultiplier(character, "pneumothoraxchance")
|
||||
* NTConfig.Get("NT_pneumothoraxChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "pneumothorax", 5)
|
||||
end
|
||||
if
|
||||
HasHeart(character)
|
||||
and strength >= 5
|
||||
and HF.Chance(
|
||||
(strength - 5)
|
||||
/ 50
|
||||
* NTC.GetMultiplier(character, "tamponadechance")
|
||||
* NTConfig.Get("NT_tamponadeChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "tamponade", 5)
|
||||
end
|
||||
if strength >= 5 then HF.AddAffliction(character, "internalbleeding", strength * HF.RandomRange(0.2, 0.5)) end
|
||||
end
|
||||
|
||||
-- head
|
||||
if strength >= 1 and limbtype == LimbType.Head then
|
||||
if
|
||||
strength >= 15
|
||||
and HF.Chance(
|
||||
math.min((strength - 15) / 60, 0.7)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
end
|
||||
|
||||
-- extremities
|
||||
if strength >= 1 and HF.LimbIsExtremity(limbtype) then
|
||||
if
|
||||
NT.LimbIsBroken(character, limbtype)
|
||||
and not NT.LimbIsAmputated(character, limbtype)
|
||||
and HF.Chance(
|
||||
strength
|
||||
/ 60
|
||||
* NTC.GetMultiplier(character, "traumamputatechance")
|
||||
* NTConfig.Get("NT_traumaticAmputationChance", 1)
|
||||
)
|
||||
then
|
||||
NT.TraumamputateLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HF.Chance(
|
||||
(strength - 5)
|
||||
/ 60
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- cause rib fractures, organ damage, pneumothorax, concussion, fractures, neurotrauma
|
||||
NT.OnDamagedMethods.blunttrauma = function(character, strength, limbtype)
|
||||
limbtype = HF.NormalizeLimbType(limbtype)
|
||||
|
||||
local fractureImmune = HF.HasAffliction(character, "cpr_fracturebuff")
|
||||
|
||||
-- torso
|
||||
if not fractureImmune and strength >= 1 and limbtype == LimbType.Torso then
|
||||
if
|
||||
HF.Chance(
|
||||
strength / 50 * NTC.GetMultiplier(character, "anyfracturechance") * NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
|
||||
HF.AddAffliction(character, "lungdamage", strength * HF.RandomRange(0, 1))
|
||||
HF.AddAffliction(character, "heartdamage", strength * HF.RandomRange(0, 1))
|
||||
HF.AddAffliction(character, "liverdamage", strength * HF.RandomRange(0, 1))
|
||||
HF.AddAffliction(character, "kidneydamage", strength * HF.RandomRange(0, 1))
|
||||
HF.AddAffliction(character, "organdamage", strength * HF.RandomRange(0, 1))
|
||||
|
||||
if
|
||||
HasLungs(character)
|
||||
and strength >= 5
|
||||
and HF.Chance(
|
||||
strength
|
||||
/ 50
|
||||
* NTC.GetMultiplier(character, "pneumothoraxchance")
|
||||
* NTConfig.Get("NT_pneumothoraxChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "pneumothorax", 5)
|
||||
end
|
||||
end
|
||||
|
||||
-- head
|
||||
if not fractureImmune and strength >= 1 and limbtype == LimbType.Head then
|
||||
if strength >= 15 and HF.Chance(math.min(strength / 60, 0.7)) then
|
||||
local armor1 = character.Inventory.GetItemInLimbSlot(InvSlotType.OuterClothes)
|
||||
local armor2 = character.Inventory.GetItemInLimbSlot(InvSlotType.Head)
|
||||
local reduceddmg = math.max(
|
||||
10
|
||||
- getCalculatedConcussionReduction(armor1, 10, limbtype)
|
||||
- getCalculatedConcussionReduction(armor2, 10, limbtype),
|
||||
0
|
||||
)
|
||||
HF.AddAfflictionResisted(character, "concussion", reduceddmg)
|
||||
end
|
||||
if
|
||||
strength >= 15
|
||||
and HF.Chance(
|
||||
math.min((strength - 10) / 60, 0.7)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
strength >= 15
|
||||
and HF.Chance(
|
||||
math.min((strength - 10) / 60, 0.7)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "n_fracture", 5)
|
||||
end
|
||||
if strength >= 5 and HF.Chance(0.7) then
|
||||
HF.AddAffliction(character, "cerebralhypoxia", strength * HF.RandomRange(0.1, 0.4))
|
||||
end
|
||||
end
|
||||
|
||||
-- extremities
|
||||
if not fractureImmune and strength >= 1 and HF.LimbIsExtremity(limbtype) then
|
||||
if
|
||||
strength > 15
|
||||
and NT.LimbIsBroken(character, limbtype)
|
||||
and not NT.LimbIsAmputated(character, limbtype)
|
||||
and HF.Chance(
|
||||
strength
|
||||
/ 100
|
||||
* NTC.GetMultiplier(character, "traumamputatechance")
|
||||
* NTConfig.Get("NT_traumaticAmputationChance", 1)
|
||||
)
|
||||
then
|
||||
NT.TraumamputateLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HF.Chance(
|
||||
(strength - 2)
|
||||
/ 60
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HF.Chance(
|
||||
HF.Clamp((strength - 2) / 80, 0, 0.5)
|
||||
* NTC.GetMultiplier(character, "dislocationchance")
|
||||
* NTConfig.Get("NT_dislocationChance", 1)
|
||||
) and not NT.LimbIsAmputated(character, limbtype)
|
||||
then
|
||||
NT.DislocateLimb(character, limbtype)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- cause rib fractures, organ damage, pneumothorax, concussion, fractures
|
||||
NT.OnDamagedMethods.internaldamage = function(character, strength, limbtype)
|
||||
limbtype = HF.NormalizeLimbType(limbtype)
|
||||
|
||||
-- torso
|
||||
if strength >= 1 and limbtype == LimbType.Torso then
|
||||
if
|
||||
HF.Chance(
|
||||
(strength - 5)
|
||||
/ 50
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
|
||||
HF.AddAffliction(character, "lungdamage", strength * HF.RandomRange(0, 1))
|
||||
HF.AddAffliction(character, "heartdamage", strength * HF.RandomRange(0, 1))
|
||||
HF.AddAffliction(character, "liverdamage", strength * HF.RandomRange(0, 1))
|
||||
HF.AddAffliction(character, "kidneydamage", strength * HF.RandomRange(0, 1))
|
||||
HF.AddAffliction(character, "organdamage", strength * HF.RandomRange(0, 1))
|
||||
|
||||
if
|
||||
HasLungs(character)
|
||||
and strength >= 5
|
||||
and HF.Chance(
|
||||
(strength - 5)
|
||||
/ 50
|
||||
* NTC.GetMultiplier(character, "pneumothoraxchance")
|
||||
* NTConfig.Get("NT_pneumothoraxChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "pneumothorax", 5)
|
||||
end
|
||||
end
|
||||
|
||||
-- head
|
||||
if strength >= 1 and limbtype == LimbType.Head then
|
||||
if strength >= 15 and HF.Chance(math.min(strength / 60, 0.7)) then
|
||||
local armor1 = character.Inventory.GetItemInLimbSlot(InvSlotType.OuterClothes)
|
||||
local armor2 = character.Inventory.GetItemInLimbSlot(InvSlotType.Head)
|
||||
local reduceddmg = math.max(
|
||||
10
|
||||
- getCalculatedConcussionReduction(armor1, 10, limbtype)
|
||||
- getCalculatedConcussionReduction(armor2, 10, limbtype),
|
||||
0
|
||||
)
|
||||
HF.AddAfflictionResisted(character, "concussion", reduceddmg)
|
||||
end
|
||||
if
|
||||
strength >= 15
|
||||
and HF.Chance(
|
||||
math.min((strength - 5) / 60, 0.7)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
strength >= 15
|
||||
and HF.Chance(
|
||||
math.min((strength - 5) / 60, 0.7)
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
HF.AddAffliction(character, "n_fracture", 5)
|
||||
end
|
||||
end
|
||||
|
||||
-- extremities
|
||||
if strength >= 1 and HF.LimbIsExtremity(limbtype) then
|
||||
if
|
||||
strength > 10
|
||||
and NT.LimbIsBroken(character, limbtype)
|
||||
and not NT.LimbIsAmputated(character, limbtype)
|
||||
and HF.Chance(
|
||||
(strength - 10)
|
||||
/ 60
|
||||
* NTC.GetMultiplier(character, "traumamputatechance")
|
||||
* NTConfig.Get("NT_traumaticAmputationChance", 1)
|
||||
)
|
||||
then
|
||||
NT.TraumamputateLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HF.Chance(
|
||||
(strength - 5)
|
||||
/ 60
|
||||
* NTC.GetMultiplier(character, "anyfracturechance")
|
||||
* NTConfig.Get("NT_fractureChance", 1)
|
||||
)
|
||||
then
|
||||
NT.BreakLimb(character, limbtype)
|
||||
end
|
||||
if
|
||||
HF.Chance(
|
||||
0.25 * NTC.GetMultiplier(character, "dislocationchance") * NTConfig.Get("NT_dislocationChance", 1)
|
||||
) and not NT.LimbIsAmputated(character, limbtype)
|
||||
then
|
||||
NT.DislocateLimb(character, limbtype)
|
||||
end
|
||||
end
|
||||
end
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
-- Hooks Lua event "Barotrauma.Character" to apply vanilla burning (formerly NT onfire) affliction and set a human on fire
|
||||
Hook.HookMethod("Barotrauma.Character", "ApplyStatusEffects", function(instance, ptable)
|
||||
if ptable.actionType == ActionType.OnFire then
|
||||
local function ApplyBurn(character, limbtype)
|
||||
HF.AddAfflictionLimb(character, "burning", limbtype, ptable.deltaTime * 3)
|
||||
end
|
||||
|
||||
if instance.IsHuman then
|
||||
if not HF.HasAffliction(instance, "luabotomy") then HF.SetAffliction(instance, "luabotomy", 1) end
|
||||
ApplyBurn(instance, LimbType.Torso)
|
||||
ApplyBurn(instance, LimbType.Head)
|
||||
ApplyBurn(instance, LimbType.LeftArm)
|
||||
ApplyBurn(instance, LimbType.RightArm)
|
||||
ApplyBurn(instance, LimbType.LeftLeg)
|
||||
ApplyBurn(instance, LimbType.RightLeg)
|
||||
else
|
||||
HF.AddAfflictionLimb(instance, "burning", instance.AnimController.MainLimb.type, ptable.deltaTime * 5)
|
||||
end
|
||||
end
|
||||
end, Hook.HookMethodType.After)
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Hooks XML Lua event "NT.causeScreams" to cause character to scream if config has enabled screaming
|
||||
Hook.Add("NT.causeScreams", "NT.causeScreams", function(...)
|
||||
if not NTConfig.Get("NT_screams", true) then return end
|
||||
|
||||
local character = table.pack(...)[3]
|
||||
HF.SetAffliction(character, "screaming", 10)
|
||||
end)
|
||||
@@ -0,0 +1,13 @@
|
||||
Hook.Patch("Barotrauma.Character", "Control", function(instance)
|
||||
if instance.CharacterHealth.GetAfflictionStrengthByIdentifier("forceprone") > 1 then
|
||||
instance.SetInput(InputType.Crouch, false, true)
|
||||
end
|
||||
end)
|
||||
|
||||
Hook.Patch("Barotrauma.Ragdoll", "get_ColliderHeightFromFloor", function(instance, ptable)
|
||||
if instance.Character and instance.Character.CharacterHealth then
|
||||
if instance.Character.CharacterHealth.GetAfflictionStrengthByIdentifier("forceprone") > 1 then
|
||||
return Single(0.1)
|
||||
end
|
||||
end
|
||||
end, Hook.HookMethodType.After)
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
-- Hooks a XML Lua event "surgerytable.update" to use for getting
|
||||
-- Neurotrauma and vanilla character data with the surgical table or hospital bed
|
||||
-- lifted and translated from betterhealthui
|
||||
local NormalHeartrate = 60
|
||||
local MaxTachycardiaHeartrate = 180
|
||||
local MaxFibrillationHeartrate = 300
|
||||
local function GetHeartrate(character)
|
||||
if character == nil or character.CharacterHealth == nil or character.IsDead then return 0 end
|
||||
|
||||
local rate = NormalHeartrate
|
||||
|
||||
local cardiacarrest = character.CharacterHealth.GetAffliction("cardiacarrest")
|
||||
|
||||
-- return 0 rate if in cardiac arrest
|
||||
if cardiacarrest ~= nil and cardiacarrest.Strength >= 0.5 then return 0 end
|
||||
|
||||
local tachycardia = character.CharacterHealth.GetAffliction("tachycardia")
|
||||
local fibrillation = character.CharacterHealth.GetAffliction("fibrillation")
|
||||
|
||||
if fibrillation ~= nil then
|
||||
rate = HF.Lerp(
|
||||
MaxTachycardiaHeartrate,
|
||||
MaxFibrillationHeartrate,
|
||||
fibrillation.Strength / 100 * (1 + math.random() * 0.5)
|
||||
)
|
||||
elseif tachycardia ~= nil then
|
||||
rate = HF.Lerp(NormalHeartrate, MaxTachycardiaHeartrate, tachycardia.Strength / 100)
|
||||
end
|
||||
|
||||
return rate
|
||||
end
|
||||
|
||||
local function GetPH(character)
|
||||
if character == nil or character.CharacterHealth == nil then return 0 end
|
||||
|
||||
local acidosis = HF.GetAfflictionStrength(character, "acidosis", 0)
|
||||
local alkalosis = HF.GetAfflictionStrength(character, "alkalosis", 0)
|
||||
|
||||
return alkalosis - acidosis
|
||||
end
|
||||
|
||||
Hook.Add("surgerytable.update", "surgerytable.update", function(effect, deltaTime, item, targets, worldPosition)
|
||||
-- fetch controller component
|
||||
local controllerComponent = item.GetComponentString("Controller")
|
||||
if controllerComponent == nil then
|
||||
item.SendSignal("0", "state_out")
|
||||
return
|
||||
end
|
||||
|
||||
-- check if targets present
|
||||
-- laying on the table
|
||||
local target = controllerComponent.User
|
||||
-- noone one the table? check the targets array for the one with the least vitality
|
||||
if target == nil or not target.IsHuman then
|
||||
local minVitality = 999
|
||||
for index, value in ipairs(targets) do
|
||||
if value.Name ~= nil and value.IsHuman and (value.Vitality < minVitality) then
|
||||
minVitality = value.Vitality
|
||||
target = value
|
||||
end
|
||||
end
|
||||
end
|
||||
-- no target found
|
||||
if target == nil or not target.IsHuman then
|
||||
item.SendSignal("0", "state_out")
|
||||
return
|
||||
end
|
||||
|
||||
-- send signals
|
||||
item.SendSignal("1", "state_out")
|
||||
if target.IsDead then
|
||||
item.SendSignal("0", "alive_out")
|
||||
else
|
||||
item.SendSignal("1", "alive_out")
|
||||
end
|
||||
if target.IsDead or HF.HasAffliction(target, "sym_unconsciousness", 0.1) then
|
||||
item.SendSignal("0", "conscious_out")
|
||||
else
|
||||
item.SendSignal("1", "conscious_out")
|
||||
end
|
||||
item.SendSignal(target.Name, "name_out")
|
||||
item.SendSignal(tostring(HF.Round(target.Vitality)), "vitality_out")
|
||||
if target.IsDead then
|
||||
item.SendSignal("0", "bloodpressure_out")
|
||||
else
|
||||
item.SendSignal(tostring(HF.Round(HF.GetAfflictionStrength(target, "bloodpressure", 100))), "bloodpressure_out")
|
||||
end
|
||||
item.SendSignal(tostring(HF.Round(100 - HF.GetAfflictionStrength(target, "hypoxemia", 0))), "bloodoxygen_out")
|
||||
item.SendSignal(tostring(HF.Round(HF.GetAfflictionStrength(target, "cerebralhypoxia", 0))), "neurotrauma_out")
|
||||
item.SendSignal(tostring(HF.Round(HF.GetAfflictionStrength(target, "organdamage", 0))), "organdamage_out")
|
||||
|
||||
local heartrate = HF.Round(GetHeartrate(target))
|
||||
item.SendSignal(tostring(heartrate), "heartrate_out")
|
||||
|
||||
local breathingrate = math.random(15, 18)
|
||||
if HF.HasAffliction(target, "respiratoryarrest") or target.IsDead then
|
||||
breathingrate = 0
|
||||
elseif HF.HasAffliction(target, "hyperventilation") then
|
||||
breathingrate = breathingrate + math.random(6, 8)
|
||||
elseif HF.HasAffliction(target, "hypoventilation") then
|
||||
breathingrate = breathingrate - math.random(6, 8)
|
||||
end
|
||||
item.SendSignal(tostring(breathingrate), "breathingrate_out")
|
||||
|
||||
item.SendSignal(tostring(HF.BoolToNum(HF.HasAffliction(target, "surgeryincision"), 1)), "insurgery_out")
|
||||
|
||||
if target.IsDead and target.causeOfDeath ~= nil then
|
||||
item.SendSignal(HF.CauseOfDeathToString(target.causeOfDeath), "causeofdeath_out")
|
||||
end
|
||||
|
||||
local bloodph = HF.Round(GetPH(target))
|
||||
item.SendSignal(tostring(bloodph), "bloodph_out")
|
||||
end)
|
||||
|
||||
--Hook.Add("surgerytable.forceon", "surgerytable.forceon", function (effect, deltaTime, item, targets, worldPosition)
|
||||
-- -- fetch controller component
|
||||
-- local controllerComponent = item.GetComponentString("Controller")
|
||||
-- if controllerComponent == nil or controllerComponent.IsActive then return end
|
||||
--
|
||||
-- -- check if targets present
|
||||
-- if targets == nil or #targets <= 0 then return end
|
||||
-- local target = nil
|
||||
-- for index, value in ipairs(targets) do
|
||||
-- target=value
|
||||
-- if target ~=nil then break end
|
||||
-- end
|
||||
-- if target == nil then return end
|
||||
--
|
||||
-- -- was experimenting with forcing the patient into laying down here, sort of worked... until 0 vitality.
|
||||
-- -- it's too janky to be released.
|
||||
--
|
||||
-- -- target.Stun = 0
|
||||
-- -- HF.SetAffliction(target,"givein",0)
|
||||
-- -- controllerComponent.Select(target)
|
||||
-- -- target.SelectedConstruction = item
|
||||
--end)
|
||||
+1286
File diff suppressed because it is too large
Load Diff
+1218
File diff suppressed because it is too large
Load Diff
+322
@@ -0,0 +1,322 @@
|
||||
-- set the below variable to true to enable debug and testing features
|
||||
NT.TestingEnabled = false
|
||||
|
||||
Hook.Add("chatMessage", "NT.testing", function(msg, client)
|
||||
if msg == "nt test" then -- a glorified suicide button
|
||||
if client.Character == nil then return true end
|
||||
|
||||
HF.SetAfflictionLimb(client.Character, "gate_ta_ra", LimbType.RightArm, 100)
|
||||
HF.SetAfflictionLimb(client.Character, "gate_ta_la", LimbType.LeftArm, 100)
|
||||
HF.SetAfflictionLimb(client.Character, "gate_ta_rl", LimbType.RightLeg, 100)
|
||||
HF.SetAfflictionLimb(client.Character, "gate_ta_ll", LimbType.LeftLeg, 100)
|
||||
|
||||
return true -- hide message
|
||||
elseif msg == "nt unfuck" then -- a command to remove non-sensical stuff
|
||||
if client.Character == nil then return true end
|
||||
|
||||
HF.SetAfflictionLimb(client.Character, "tll_amputation", LimbType.Head, 0)
|
||||
HF.SetAfflictionLimb(client.Character, "trl_amputation", LimbType.Head, 0)
|
||||
HF.SetAfflictionLimb(client.Character, "tla_amputation", LimbType.Head, 0)
|
||||
HF.SetAfflictionLimb(client.Character, "tra_amputation", LimbType.Head, 0)
|
||||
|
||||
HF.SetAfflictionLimb(client.Character, "tll_amputation", LimbType.Torso, 0)
|
||||
HF.SetAfflictionLimb(client.Character, "trl_amputation", LimbType.Torso, 0)
|
||||
HF.SetAfflictionLimb(client.Character, "tla_amputation", LimbType.Torso, 0)
|
||||
HF.SetAfflictionLimb(client.Character, "tra_amputation", LimbType.Torso, 0)
|
||||
|
||||
for key, character in pairs(Character.CharacterList) do
|
||||
if not character.IsDead then
|
||||
if character.IsHuman then
|
||||
HF.AddAffliction(character, "luabotomypurger", 2)
|
||||
if character.TeamID == 1 or character.TeamID == 2 then
|
||||
Timer.Wait(function()
|
||||
HF.SetAffliction(character, "luabotomy", 0.1)
|
||||
end, 4000)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true -- hide message
|
||||
elseif msg == "nt1" then
|
||||
if not NT.TestingEnabled then return end
|
||||
-- insert testing stuff here
|
||||
|
||||
local test = { val = "true" }
|
||||
|
||||
local function testfunc(param)
|
||||
param.val = "false"
|
||||
end
|
||||
|
||||
print(test.val)
|
||||
testfunc(test)
|
||||
print(test.val)
|
||||
|
||||
return true
|
||||
elseif msg == "nt2" then
|
||||
if not NT.TestingEnabled then return end
|
||||
-- insert other testing stuff here
|
||||
local crewenum = Character.GetFriendlyCrew(client.Character)
|
||||
local targetchar = nil
|
||||
local i = 0
|
||||
for char in crewenum do
|
||||
print(char.Name)
|
||||
targetchar = char
|
||||
i = i + 1
|
||||
if i == 2 then break end
|
||||
end
|
||||
|
||||
client.SetClientCharacter(nil)
|
||||
|
||||
print(targetchar)
|
||||
|
||||
Timer.Wait(function()
|
||||
client.SetClientCharacter(targetchar)
|
||||
end, 50)
|
||||
|
||||
return true
|
||||
end
|
||||
end)
|
||||
|
||||
DebugConsole = LuaUserData.CreateStatic("Barotrauma.DebugConsole")
|
||||
|
||||
local function registerDebugCommands()
|
||||
LuaUserData.MakeMethodAccessible(Descriptors["Barotrauma.DebugConsole"], "GetCharacterNames")
|
||||
LuaUserData.MakeMethodAccessible(Descriptors["Barotrauma.DebugConsole"], "FindMatchingCharacter")
|
||||
|
||||
LuaUserData.MakeFieldAccessible(Descriptors["Barotrauma.CharacterHealth"], "afflictions")
|
||||
LuaUserData.MakeFieldAccessible(Descriptors["Barotrauma.CharacterHealth"], "limbHealths")
|
||||
LuaUserData.MakeMethodAccessible(
|
||||
Descriptors["Barotrauma.CharacterHealth"],
|
||||
"GetVitalityDecreaseWithVitalityMultipliers"
|
||||
)
|
||||
LuaUserData.RegisterType(
|
||||
"System.Collections.Generic.Dictionary`2[[Barotrauma.Affliction],[Barotrauma.CharacterHealth+LimbHealth]]"
|
||||
)
|
||||
LuaUserData.RegisterType(
|
||||
"System.Collections.Generic.KeyValuePair`2[[Barotrauma.Affliction],[Barotrauma.CharacterHealth+LimbHealth]]"
|
||||
)
|
||||
|
||||
local function findCharacter(str)
|
||||
local character = nil
|
||||
if not str or str == "" or str == "/me" then
|
||||
character = Character.Controlled
|
||||
else
|
||||
character = DebugConsole.FindMatchingCharacter({ str })
|
||||
end
|
||||
return character
|
||||
end
|
||||
|
||||
Game.AddCommand(
|
||||
"nt_listafflictions",
|
||||
"nt_listafflictions [character name] [client/server]: Lists all afflictions on a character",
|
||||
function(args)
|
||||
if CLIENT and args[2] == "server" then
|
||||
if Game.IsMultiplayer then
|
||||
if not args[1] or args[1] == "/me" then
|
||||
args[1] = Character.Controlled and Character.Controlled.Name or ""
|
||||
end
|
||||
Game.client.SendConsoleCommand("nt_listafflictions " .. '"' .. args[1] .. '"')
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
local target = findCharacter(args[1])
|
||||
if not target then return end
|
||||
|
||||
print(target.Name, " vitality: ", target.Vitality, "/", target.MaxVitality, " Mass: ", target.Mass)
|
||||
local genericafflictions, limbafflictions = {}, {}
|
||||
for kvp in target.CharacterHealth.afflictions do
|
||||
if kvp.Value then
|
||||
if not limbafflictions[kvp.Value] then limbafflictions[kvp.Value] = {} end
|
||||
table.insert(limbafflictions[kvp.Value], kvp.Key)
|
||||
else
|
||||
table.insert(genericafflictions, kvp.Key)
|
||||
end
|
||||
end
|
||||
for limbhealth, afflictions in pairs(limbafflictions) do
|
||||
print(limbhealth.Name or "Unnamed limb")
|
||||
for affliction in afflictions do
|
||||
print(
|
||||
"# ",
|
||||
affliction.Name,
|
||||
" = ",
|
||||
affliction.Strength,
|
||||
" (vitality decrease: ",
|
||||
target.CharacterHealth.GetVitalityDecreaseWithVitalityMultipliers(affliction),
|
||||
")"
|
||||
)
|
||||
end
|
||||
end
|
||||
print("Generic afflictions")
|
||||
for affliction in genericafflictions do
|
||||
print(
|
||||
"# ",
|
||||
affliction.Name,
|
||||
" = ",
|
||||
affliction.Strength,
|
||||
" (vitality decrease: ",
|
||||
affliction.GetVitalityDecrease(target.CharacterHealth),
|
||||
")"
|
||||
)
|
||||
end
|
||||
end,
|
||||
--GetValidArguments
|
||||
function()
|
||||
return { DebugConsole.GetCharacterNames(), { "client", "server" } }
|
||||
end,
|
||||
true
|
||||
)
|
||||
|
||||
Game.AddCommand(
|
||||
"nt_listcreatures",
|
||||
"nt_listcreatures [printafflictionsgeneric/printafflictionsfull]: Lists all non-human creatures currently on the server",
|
||||
function(args)
|
||||
if CLIENT and Game.IsMultiplayer then
|
||||
Game.client.SendConsoleCommand("nt_listcreatures " .. '"' .. args[1] .. '"')
|
||||
return
|
||||
end
|
||||
|
||||
local function printAfflictions(target, args)
|
||||
local genericafflictions, limbafflictions = {}, {}
|
||||
for kvp in target.CharacterHealth.afflictions do
|
||||
if kvp.Value then
|
||||
if not limbafflictions[kvp.Value] then limbafflictions[kvp.Value] = {} end
|
||||
table.insert(limbafflictions[kvp.Value], kvp.Key)
|
||||
else
|
||||
table.insert(genericafflictions, kvp.Key)
|
||||
end
|
||||
end
|
||||
if args[1] == "printafflictionsgeneric" or args[1] == "printafflictionsfull" then
|
||||
print("Generic afflictions")
|
||||
for affliction in genericafflictions do
|
||||
print(
|
||||
"# ",
|
||||
affliction.Name,
|
||||
" = ",
|
||||
affliction.Strength,
|
||||
" (vitality decrease: ",
|
||||
affliction.GetVitalityDecrease(target.CharacterHealth),
|
||||
")"
|
||||
)
|
||||
end
|
||||
end
|
||||
if args[1] == "printafflictionsfull" then
|
||||
print("Limb afflictions")
|
||||
for limbhealth, afflictions in pairs(limbafflictions) do
|
||||
print(limbhealth.Name or "Unnamed limb")
|
||||
for affliction in afflictions do
|
||||
print(
|
||||
"# ",
|
||||
affliction.Name,
|
||||
" = ",
|
||||
affliction.Strength,
|
||||
" (vitality decrease: ",
|
||||
target.CharacterHealth.GetVitalityDecreaseWithVitalityMultipliers(affliction),
|
||||
")"
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for key, character in pairs(Character.CharacterList) do
|
||||
if not character.IsHuman then
|
||||
print(
|
||||
character.SpeciesName,
|
||||
" vitality: ",
|
||||
character.Vitality,
|
||||
"/",
|
||||
character.MaxVitality,
|
||||
" Mass: ",
|
||||
character.Mass
|
||||
)
|
||||
if args[1] == "printafflictionsgeneric" or args[1] == "printafflictionsfull" then
|
||||
printAfflictions(character, args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
--GetValidArguments
|
||||
function()
|
||||
return { { "printafflictionsgeneric", "printafflictionsfull" } }
|
||||
end,
|
||||
true
|
||||
)
|
||||
|
||||
Game.AddCommand(
|
||||
"nt_nugget",
|
||||
"nt_nugget [character name]: Nuggets the character",
|
||||
function(args)
|
||||
if CLIENT and Game.IsMultiplayer then
|
||||
if not args[1] or args[1] == "/me" then
|
||||
args[1] = Character.Controlled and Character.Controlled.Name or ""
|
||||
end
|
||||
Game.client.SendConsoleCommand("nt_nugget " .. '"' .. args[1] .. '"')
|
||||
return
|
||||
end
|
||||
|
||||
local target = findCharacter(args[1])
|
||||
if not target then return end
|
||||
|
||||
HF.SetAfflictionLimb(target, "gate_ta_ra", LimbType.RightArm, 100)
|
||||
HF.SetAfflictionLimb(target, "gate_ta_la", LimbType.LeftArm, 100)
|
||||
HF.SetAfflictionLimb(target, "gate_ta_rl", LimbType.RightLeg, 100)
|
||||
HF.SetAfflictionLimb(target, "gate_ta_ll", LimbType.LeftLeg, 100)
|
||||
end,
|
||||
--GetValidArguments
|
||||
function()
|
||||
return { DebugConsole.GetCharacterNames() }
|
||||
end,
|
||||
true
|
||||
)
|
||||
|
||||
Game.AddCommand(
|
||||
"nt_unnugget",
|
||||
"nt_unnugget [character name]: Unnuggets the character",
|
||||
function(args)
|
||||
if CLIENT and Game.IsMultiplayer then
|
||||
if not args[1] or args[1] == "/me" then
|
||||
args[1] = Character.Controlled and Character.Controlled.Name or ""
|
||||
end
|
||||
Game.client.SendConsoleCommand("nt_unnugget " .. '"' .. args[1] .. '"')
|
||||
return
|
||||
end
|
||||
|
||||
local target = findCharacter(args[1])
|
||||
if not target then return end
|
||||
|
||||
HF.SetAfflictionLimb(target, "tll_amputation", LimbType.Head, 0)
|
||||
HF.SetAfflictionLimb(target, "trl_amputation", LimbType.Head, 0)
|
||||
HF.SetAfflictionLimb(target, "tla_amputation", LimbType.Head, 0)
|
||||
HF.SetAfflictionLimb(target, "tra_amputation", LimbType.Head, 0)
|
||||
|
||||
HF.SetAfflictionLimb(target, "tll_amputation", LimbType.Torso, 0)
|
||||
HF.SetAfflictionLimb(target, "trl_amputation", LimbType.Torso, 0)
|
||||
HF.SetAfflictionLimb(target, "tla_amputation", LimbType.Torso, 0)
|
||||
HF.SetAfflictionLimb(target, "tra_amputation", LimbType.Torso, 0)
|
||||
end,
|
||||
--GetValidArguments
|
||||
function()
|
||||
return { DebugConsole.GetCharacterNames() }
|
||||
end,
|
||||
true
|
||||
)
|
||||
end
|
||||
|
||||
Game.AddCommand("nt_debug", "nt_debug : Enables debug neurotrauma commands", function()
|
||||
if not NT.TestingEnabled then
|
||||
print("neurotrauma debug enabled")
|
||||
registerDebugCommands()
|
||||
NT.TestingEnabled = true
|
||||
|
||||
local msg = Networking.Start("NT_debug")
|
||||
Networking.Send(msg)
|
||||
end
|
||||
end, nil, true)
|
||||
|
||||
if CLIENT and Game.IsMultiplayer then Networking.Receive("NT_debug", function(msg)
|
||||
registerDebugCommands()
|
||||
end) end
|
||||
|
||||
if NT.TestingEnabled then registerDebugCommands() end
|
||||
Reference in New Issue
Block a user