Track LocalMods as part of monolith

This commit is contained in:
2026-06-08 18:50:16 +03:00
parent 143f2fed7c
commit 1b214b44c2
1287 changed files with 139255 additions and 1 deletions
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+25
View File
@@ -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
View File
@@ -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
View File
@@ -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)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+90
View File
@@ -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)
+34
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
+7
View File
@@ -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
View File
@@ -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)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+322
View File
@@ -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