Add other mods

This commit is contained in:
2025-03-31 13:19:47 +02:00
parent bdc5488720
commit be593696b2
2266 changed files with 109313 additions and 512 deletions

View File

@@ -0,0 +1,100 @@
--if Game.IsMultiplayer and CLIENT then return end
NT = {} -- Neurotrauma
NT.Name = "Neurotrauma"
NT.Version = "A1.12.1"
NT.VersionNum = 01120100
NT.Path = table.pack(...)[1]
dofile(NT.Path .. "/Lua/Scripts/helperfunctions.lua")
-- all things config
dofile(NT.Path .. "/Lua/Scripts/configdata.lua")
-- server-side code (also run in singleplayer)
if (Game.IsMultiplayer and SERVER) or not Game.IsMultiplayer then
-- Version and expansion display
Timer.Wait(function()
Timer.Wait(function()
local runstring = "\n/// Running Neurotrauma V " .. NT.Version .. " ///\n"
-- add dashes
local linelength = string.len(runstring) + 4
local i = 0
while i < linelength do
runstring = runstring .. "-"
i = i + 1
end
local hasAddons = #NTC.RegisteredExpansions > 0
-- add expansions
for val in NTC.RegisteredExpansions do
runstring = runstring .. "\n+ " .. (val.Name or "Unnamed expansion") .. " V " .. (val.Version or "???")
if val.MinNTVersion ~= nil and NT.VersionNum < (val.MinNTVersionNum or 1) then
runstring = runstring
.. "\n-- WARNING! Neurotrauma version "
.. val.MinNTVersion
.. " or higher required!"
end
end
-- No expansions
runstring = runstring .. "\n"
if not hasAddons then
runstring = runstring .. "- Not running any expansions\n"
end
print(runstring)
end, 1)
end, 1)
--dofile(NT.Path .. "/Lua/Scripts/Server/characterpatches.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/ntcompat.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/blood.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/humanupdate.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/ondamaged.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/items.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/onfire.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/cpr.lua")
--dofile(NT.Path.."/Lua/Scripts/Server/surgerytable.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/fuckbots.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/lootcrates.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/multiscalpel.lua") -- its important for this to run after items.lua
dofile(NT.Path .. "/Lua/Scripts/Server/falldamage.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/screams.lua")
dofile(NT.Path .. "/Lua/Scripts/Server/modconflict.lua")
dofile(NT.Path .. "/Lua/Scripts/testing.lua")
end
-- server-side code only
if SERVER then
Networking.Receive("NT.ConfigUpdate", function(msg, sender)
if not sender.HasPermission(ClientPermissions.ManageSettings) then
return
end
NTConfig.ReceiveConfig(msg)
NTConfig.SaveConfig()
end)
Networking.Receive("NT.ConfigRequest", function(msg, sender)
if not sender then
return
end
NTConfig.SendConfig(sender)
end)
end
-- client-side code
if CLIENT then
dofile(NT.Path .. "/Lua/Scripts/Client/configgui.lua")
end
-- Shared and singleplayer code
dofile(NT.Path .. "/Lua/Scripts/Shared/surgerytable.lua") -- Heelge: fix for https://github.com/OlegBSTU/Neurotrauma/issues/15
--dofile(NT.Path .. "/Lua/Scripts/Shared/pronecolliderfix.lua") -- Heelge: fix collider on prone (thx Lua man), but it has 2 ms perf drop so commented out
-- Consent Required Extended with adjustments
-- mod page: https://steamcommunity.com/sharedfiles/filedetails/?id=2892602084
dofile(NT.Path .. "/Lua/ConsentRequiredExtended/init.lua")

View File

@@ -0,0 +1,146 @@
-- Consent Required API
-- Any Lua script can access this API by adding this line:
-- local Api = require "com.github.cintique.ConsentRequired.Api"
local Environment = require("ConsentRequiredExtended.Util.Environment")
local Barotrauma = require("ConsentRequiredExtended.Util.Barotrauma")
local _ENV = Environment.PrepareEnvironment(_ENV)
-- Table of identifiers (strings) of items that when used
-- as a treatment on an NPC from a different team,
-- causes that NPC (and their allies) to become hostile
-- towards the player.
local affectedItems = {}
---Adds an item (by identifier string) to `affectedItems`.
---@param identifier string
function AddAffectedItem(identifier)
table.insert(affectedItems, identifier)
end
LuaUserData.MakeFieldAccessible(Descriptors["Barotrauma.AbandonedOutpostMission"], "requireRescue")
-- Character type doesn't have tags we can assign a custom "rescuetarget" tag to
-- So instead we just hold characters which need rescue in a table and compare their entity IDs
-- This table is only resfreshed on roundstart
local rescuetargets = {}
---Returns a boolean indicating whether a given item is affected or not.
---@param identifier string The identifier of the item that we are testing.
---@return boolean isAffected True if the item is affected, false otherwise.
function IsItemAffected(identifier)
for _, item in pairs(affectedItems) do
if item == identifier or HF.StartsWith(identifier, item) then
return true
end
end
return false
end
local ADD_ATTACKER_DAMAGE = 130 -- Heelge: this used to max out negative rep gain, now only around 4 negative rep, any less negative rep is too forgiving.
---@param aiChar Barotrauma_Character The AI character to be made hostile.
---@param instigator Barotrauma_Character The character to be the target of the AI's wrath.
function makeHostile(aiChar, instigator)
aiChar.AIController.OnAttacked(instigator, Barotrauma.AttackResult.NewAttackResultFromDamage(ADD_ATTACKER_DAMAGE))
aiChar.AddAttacker(instigator, ADD_ATTACKER_DAMAGE)
end
---@param char1 Barotrauma_Character Character one.
---@param char2 Barotrauma_Character Character two.
---@return boolean charactersAreOnSameTeam True if characters one & two are on the same team, false otherwise.
function isOnSameTeam(char1, char2)
local team1 = char1.TeamID
local team2 = char2.TeamID
return team1 == team2
end
---Updates current rescue targets list, separate so we dont cycle thru all missions every time we apply item to chacter. Use IsRescueTarget(target) after this.
function UpdateRescueTargets()
rescuetargets = {}
for mission in Game.GameSession.Missions do
if LuaUserData.IsTargetType(mission.Prefab.MissionClass, "Barotrauma.AbandonedOutpostMission") then
for character in mission.requireRescue do
rescuetargets[character.ID] = character
--table.insert(rescuetargets, character)
end
end
end
-- print('rescue targets =')
-- for char in rescuetargets do print(char.Name) end
end
---@param target Barotrauma_Character The character we want to confirm as being rescued
---@return boolean consent True if target is rescue mission target, false otherwise
function IsRescueTarget(target)
-- for char in rescuetargets do
-- if target.ID == char.ID then return true end
-- end
if rescuetargets[target.ID] ~= nil then
return true
end
return false
end
---@param user Barotrauma_Character The character who desires consent.
---@param target Barotrauma_Character The character who gives consent
---@return boolean consent True if consent is given, false otherwise.
function hasConsent(user, target)
return isOnSameTeam(user, target) or target.IsEscorted or IsRescueTarget(target) -- No longer needs to be shared.
end
---@param aiChar Barotrauma_Character The (AI but not necessarily) character whose sight is being tested.
---@param target Barotrauma_Character The character to be seen.
---@return boolean aiCanSeeTarget True if the AI can see the target character.
function canAiSeeTarget(aiChar, target)
-- I'll just use what Barotrauma uses for witness line of sight
local aiVisibleHulls = aiChar.GetVisibleHulls()
local targetCurrentHull = target.CurrentHull
for _, visibleHull in pairs(aiVisibleHulls) do
if targetCurrentHull == visibleHull then
return true
end
end
return false
end
---@param user Barotrauma_Character The character of the instigator being witnessed.
---@param victim Barotrauma_Character The character of the victim of the crime being witnessed.
---@return Barotrauma_Character[] Characters that have witnessed the crime.
function getWitnessesToCrime(user, victim)
local witnesses = {}
for _, char in pairs(Character.CharacterList) do
if
not char.Removed
and not char.IsUnconscious
and char.IsBot
and char.IsHuman
and isOnSameTeam(char, victim)
then
local isWitnessingUser = canAiSeeTarget(char, user)
if isWitnessingUser then
table.insert(witnesses, char)
end
end
end
return witnesses
end
---@param user Barotrauma_Character The character that is applying the affected item.
---@param target Barotrauma_Character The character of the target of the affected item's application.
function onAffectedItemApplied(user, target)
if not hasConsent(user, target) and target.IsBot and target.IsHuman then
if not target.IsIncapacitated and target.Stun <= 10 then
makeHostile(target, user)
else
-- Vanilla Barotrauma Human AI doesn't care what you do to their unconscious teammates, even shooting them in the head
-- Let's fix that for this particular case of mistreatment
local witnesses = getWitnessesToCrime(user, target)
for _, witness in pairs(witnesses) do
makeHostile(witness, user)
end
end
end
end
return Environment.Export(_ENV)

View File

@@ -0,0 +1,64 @@
-- User edited configuration file.
local Environment = require("ConsentRequiredExtended.Util.Environment")
local _ENV = Environment.PrepareEnvironment(_ENV)
--------- Start editing here ---------
AffectedItems = {
-- Neurotrauma
-- "healthscanner", --健康扫描仪 -- whats a tiny bit of radiation damage between friends?
"bloodanalyzer", --血液分析仪
"opium", --药用鸦片
"antidama1", --吗啡
"antidama2", --芬太尼
"antibleeding3", --抗生素凝膠
"propofol", -- 异丙酚
"mannitol", -- 甘露醇
"pressuremeds", -- 压力药物
"multiscalpel", -- 多功能手术刀
"advscalpel", -- 手术刀
"advhemostat", -- 止血钳
"advretractors", -- 皮肤牵引器
"tweezers", -- 镊子
"surgicaldrill", -- 骨钻
"surgerysaw", -- 手术锯
"organscalpel_liver", -- 器官切割刀:肝脏
"organscalpel_lungs", -- 器官切割刀:肺
"organscalpel_kidneys", -- 器官切割刀:肾脏
"organscalpel_heart", -- 器官切割刀:心脏
"organscalpel_brain", -- 器官切割刀:大脑
"emptybloodpack", -- 空血袋
"bloodpack",
"alienblood", -- 异星血浆
"tourniquet", -- 止血带
"defibrillator", -- 手动除颤器
"aed", -- 智能除颤器
"bvm", -- 人工呼吸器
"antibiotics", -- 广谱抗生素
"sulphuricacid", -- 硫酸
"divingknife", -- 潜水刀
"divingknifedementonite", -- 攝魂潛水刀
"divingknifehardened", -- 硬化潛水刀
"crowbar", -- 潜水刀
"crowbardementonite", -- 攝魂撬棍
"crowbarhardened", -- 硬化撬棍
"stasisbag", -- 冷藏袋
"autocpr", -- 全自动CPR
-- NeuroEyes
"organscalpel_eyes", -- 器官切割刀:眼睛
-- blahaj 布罗艾鲨鱼
-- "blahaj", -- 布罗艾鲨鱼 -- Blahaj never hurt anyone
-- "blahajplus", -- 大鲨鲨
"blahajplusplus", -- 超大鲨鲨
-- Pharmacy 制药大师
"custompill", -- 自制药丸
"custompill_horsepill", -- 大药丸
"custompill_tablets", -- 药片
-- vanilla 原版
"toyhammer", -- 玩具锤子
}
--------- Stop editing here ---------
return Environment.Export(_ENV)

View File

@@ -0,0 +1,31 @@
-- Do not take my blood or organs without my consent, thanks.
-- Causes AI to get angry at you if you use certain medical items on them.
-- These items are those related to organ and blood removal.
-- This mod is meant to be accompanied by Neurotrauma, and aims to
-- resolve the issue of being freely able to steal blood/organs from
-- neutral NPCs (e.g. outposts, VIPs) without them getting mad at you.
local Api = require("ConsentRequiredExtended.Api")
local OnItemApplied = require("ConsentRequiredExtended.OnItemApplied")
local onMeleeWeaponHandleImpact = require("ConsentRequiredExtended.onMeleeWeaponHandleImpact")
local Config = require("ConsentRequiredExtended.Config")
local LUA_EVENT_ITEM_APPLYTREATMENT = "item.ApplyTreatment"
local HOOK_NAME_ITEM_APPLYTREATMENT = "ConsentRequiredExtended.onItemApplyTreatment"
local LUA_EVENT_MELEEWEAPON_HANDLEIMPACT = "meleeWeapon.handleImpact"
local HOOK_NAME_MELEEWEAPON_HANDLEIMPACT = "ConsentRequiredExtended.onMeleeWeaponHandleImpact"
local LUA_EVENT_ROUNDSTART = "roundStart"
local HOOK_NAME_UPDATE_RESCUETARGETS = "ConsentRequiredExtended.onUpdateRescueTargets"
-- Set up affected items from config.
for _, affectedItem in pairs(Config.AffectedItems) do
Api.AddAffectedItem(affectedItem)
end
Hook.Add(LUA_EVENT_ITEM_APPLYTREATMENT, HOOK_NAME_ITEM_APPLYTREATMENT, OnItemApplied)
-- damn meleeWeapon
Hook.Add(LUA_EVENT_MELEEWEAPON_HANDLEIMPACT, HOOK_NAME_MELEEWEAPON_HANDLEIMPACT, onMeleeWeaponHandleImpact)
Hook.Add(LUA_EVENT_ROUNDSTART, HOOK_NAME_UPDATE_RESCUETARGETS, Api.UpdateRescueTargets)

View File

@@ -0,0 +1,20 @@
local Api = require("ConsentRequiredExtended.Api")
local function isItemAffected(identifier)
return Api.IsItemAffected(identifier)
end
---@param item Barotrauma_Item Item being applied.
---@param user Barotrauma_Character The character that is applying the item.
---@param target Barotrauma_Character The character of the target of the item's application.
local function OnItemApplied(item, user, target)
if not NTConfig.Get("NTCRE_ConsentRequired", true) then
return
end
local itemIdentifier = item.Prefab.Identifier.Value
if isItemAffected(itemIdentifier) then
Api.onAffectedItemApplied(user, target)
end
end
return OnItemApplied

View File

@@ -0,0 +1,90 @@
-- Functions for interfacing with Barotrauma.
local Environment = require 'ConsentRequiredExtended.Util.Environment'
local _ENV = Environment.PrepareEnvironment(_ENV)
-- local Clr = require 'ConsentRequiredExtended.Util.Clr'
-- local UserData = require 'ConsentRequiredExtended.Util.UserData'
---Functions related to working with Barotrauma.AttackResult.
AttackResult = {}
---Initialise AttackResults.
-- local function Init_AttackResult()
-- -- Registrations.
-- UserData.RegisterStandardType("System.Reflection.FieldInfo")
-- -- Construct a List<Affliction> generic type.
-- local afflictionsListClrType = Clr.CreateConstructedGenericType("System.Collections.Generic.List`1", "Barotrauma.Affliction")
-- local attackResultAfflictionsField = Clr.GetRawClrType("Barotrauma.AttackResult").GetField("Afflictions")
-- ---Instantiates a new AttackResult with damage and empty afflictions.
-- ---@param damage number An amount of damage.
-- function AttackResult.NewAttackResultFromDamage(damage)
-- -- Instantiate a new AttackResult.
-- local attackResult = _G.AttackResult(damage, nil)
-- -- Instantiate an empty List<Afflictions> (this is to prevent NREs),
-- -- and set it to attackResult.Afflictions. This is a readonly field,
-- -- hence the use of reflection.
-- local afflictionsList = UserData.FromClrType({}, afflictionsListClrType)
-- attackResultAfflictionsField.SetValue(attackResult, afflictionsList)
-- return attackResult
-- end
-- end
---Initialise AttackResults without needing to register system.type and reflections
local function Init_AttackResult()
-- Registrations.
LuaUserData.MakePropertyAccessible(Descriptors['Barotrauma.AttackResult'], 'Damage')
---Instantiates a new AttackResult with damage and empty afflictions.
---@param damage number An amount of damage.
function AttackResult.NewAttackResultFromDamage(damage)
-- I have not noticed any NREs from affliction list being null
-- but just in case here is version which intializes with empty list
-- Also uncomment MakePropertyAccessible Damage above
local attackResult = _G.AttackResult({}, nil, {})
attackResult.Damage = damage
--local attackResult = _G.AttackResult(damage)
return attackResult
end
end
---Runs at start-up, handles registrations, etc.
function Init()
Init_AttackResult()
end
function Test()
local errors = {}
local function AssertEquals(testDescription, expected, got)
if expected ~= got then
local errorString = string.format(
"Test Error: %s\n\texpected = %s\n\tgot = %s",
testDescription,
tostring(expected),
tostring(got)
)
table.insert(errors, errorString)
end
end
local atkRes = AttackResult.NewAttackResultFromDamage(10)
AssertEquals("atkRes.Damage", 10, atkRes.Damage)
AssertEquals("atkRes.Afflictions is null", true, atkRes.Afflictions ~= nil)
AssertEquals("#atkRes.Afflictions is non-zero", 0, #atkRes.Afflictions)
if #errors == 0 then
print("Tests successful")
else
for _, err in pairs(errors) do
print(err)
end
end
end
Init()
return Environment.Export(_ENV)

View File

@@ -0,0 +1,74 @@
-- Functions for working with CLR types.
local Environment = require 'ConsentRequiredExtended.Util.Environment'
local _ENV = Environment.PrepareEnvironment(_ENV)
local UserData = require 'ConsentRequiredExtended.Util.UserData'
---Construct ClrType wrapper table for CLR types.
---@param underlyingType userdata The underlying type (CLR type: System.Type).
---@return ClrType Wrapper table for working with CLR types.
local function New(underlyingType)
---@class ClrType
local clrType = {}
---Instantiate an object for the given CLR type.
---@return any An instance of the CLR type. Actual Lua type depends on MoonSharp conversions.
function clrType:Instantiate()
-- TODO: Implement args.
return underlyingType.Assembly.CreateInstance(underlyingType.FullName)
end
---Get the underlying type as a raw CLR type object.
---@return userdata The raw underlying type (CLR type: System.Type).
function clrType:GetUnderlyingType()
return underlyingType
end
---Get the full name of the CLR type.
---@return string The full name of the type.
function clrType:GetFullName()
return underlyingType.FullName
end
return clrType
end
---Create a constructed generic type.
---@param genericTypeName string The name of the generic type to construct and register.
---@vararg string
---@return ClrType The constructed generic type.
function CreateConstructedGenericType(genericTypeName, ...)
local genericTypeArgumentsString = table.pack(...)
local genericTypeArgumentsType = {}
for _, typeString in pairs(genericTypeArgumentsString) do
table.insert(genericTypeArgumentsType, GetRawClrType(typeString))
end
local genericTypeDefinition = GetRawClrType(genericTypeName)
local constructedGenericType = genericTypeDefinition.MakeGenericType(table.unpack(genericTypeArgumentsType))
return New(constructedGenericType)
end
---Get a System.Type object from a type name.
---@param typeName string Name of the type.
---@return userdata The type object (CLR type: System.Type).
function GetRawClrType(typeName)
return LuaUserData.GetType(typeName)
end
---Get a ClrType instance wrapping a Type object that matches the type name.
---@param typeName string Name of the type.
---@return ClrType The CLR type.
function GetClrType(typeName)
return New(GetRawClrType(typeName))
end
local function Init()
UserData.RegisterStandardType("System.Type")
UserData.RegisterStandardType("System.Reflection.RuntimeAssembly")
end
Init()
return Environment.Export(_ENV)

View File

@@ -0,0 +1,32 @@
-- Functions for managing the mod's environment.
---Isolate a function or module's environment from Global.
---@param env table The _ENV table.
local function prepareEnvironment(env)
return setmetatable(
{},
{
__index = _G,
}
)
end
local _ENV = prepareEnvironment(_ENV)
PrepareEnvironment = prepareEnvironment
---Create an empty table whose metatable indexes non-local variables declared within
---a function/module's environment, and is immutable to any changes.
---@param env table The _ENV table.
---@return table Empty table that interfaces with _ENV.
function Export(env)
return setmetatable(
{},
{
__index = function(t, k) return env[k] end,
__newindex = function() error("Attempted to modify a protected table.") end
}
)
end
return Export(_ENV)

View File

@@ -0,0 +1,35 @@
-- Functions for creating userdata.
local Environment = require 'ConsentRequiredExtended.Util.Environment'
local _ENV = Environment.PrepareEnvironment(_ENV)
---Create a userdata that references the type in a static context.
---@param clrTypeName string The name of the type to point to.
---@return userdata A userdata that references the type in a static context.
function FromStringStatic(clrTypeName)
LuaUserData.CreateStatic(clrTypeName)
end
---Create a userdata that references the type described by a ClrType in a static context.
---@param clrType ClrType ClrType that describes the type being referenced.
---@return userdata A userdata that references the type in a static context.
function FromClrTypeStatic(clrType)
return FromStringStatic(clrType:GetFullName())
end
---Create a userdata that references an instance of a CLR type with conversion.
---@param value any A Lua value to convert to a CLR object of the given type and wrap up in a userdata.
---@param clrType ClrType The CLR type to instantiate and wrap in the userdata.
---@return userdata A userdata that references the an instance of the CLR type.
function FromClrType(value, clrType)
return LuaUserData.CreateUserDataFromType(value, clrType:GetUnderlyingType())
end
---Register standard types with MoonSharp. For generics use RegisterClrType.ConstructedGenericType.
---@param typeName string Name of the type to register.
function RegisterStandardType(typeName)
local desc = LuaUserData.RegisterType(typeName)
_G.Descriptors[typeName] = desc
end
return Environment.Export(_ENV)

View File

@@ -0,0 +1,16 @@
local SRC_NAMESPACE = "ConsentRequiredExtended."
local MAIN = "Main"
local LUA_EVENT_LOADED = "loaded"
local HOOK_NAME_ON_LOADED = "ConsentRequiredExtended.onLoaded"
local function onLoaded()
-- Only run client side if not multiplayer
---@diagnostic disable-next-line: undefined-global
-- if Game.IsMultiplayer and CLIENT then return end
local requireStr = SRC_NAMESPACE .. MAIN
require(requireStr)
end
Hook.Add(LUA_EVENT_LOADED, HOOK_NAME_ON_LOADED, onLoaded)

View File

@@ -0,0 +1,38 @@
local Api = require("ConsentRequiredExtended.Api")
local function isItemAffected(identifier)
return Api.IsItemAffected(identifier)
end
---@param meleeweapon Weapon target
---@param target The target of the hit could be a limb or just a character.
local function onMeleeWeaponHandleImpact(meleeweapon, target)
if not NTConfig.Get("NTCRE_ConsentRequired", true) then
return
end
if meleeweapon == nil or target == nil then
return
end
local itemIdentifier = meleeweapon.item.Prefab.Identifier.Value
if isItemAffected(itemIdentifier) then
local user = meleeweapon.picker
if user == nil then
return
end
local targetUserData = target.UserData
if targetUserData == nil then
return
end
local targetUser = nil
if LuaUserData.IsTargetType(targetUserData, "Barotrauma.Limb") then
targetUser = targetUserData.character
elseif LuaUserData.IsTargetType(targetUserData, "Barotrauma.Character") then
targetUser = targetUserData
end
if targetUser ~= nil then
Api.onAffectedItemApplied(user, targetUser)
end
end
end
return onMeleeWeaponHandleImpact

View File

@@ -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

View File

@@ -0,0 +1,242 @@
--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 function CommaStringToTable(str)
local tbl = {}
for word in string.gmatch(str, "([^,]+)") do
table.insert(tbl, word)
end
return tbl
end
--calculate difficulty
local function DetermineDifficulty()
local difficulty = 0
local defaultDifficulty = 0
local res = ""
for key, entry in pairs(NTConfig.Entries) do
if entry.difficultyCharacteristics then
local entryValue = entry.value
local entryValueDefault = entry.default
local diffMultiplier = 1
if entry.type == "bool" then
entryValue = HF.BoolToNum(entry.value)
entryValueDefault = HF.BoolToNum(entry.default)
end
if entry.difficultyCharacteristics.multiplier then
diffMultiplier = entry.difficultyCharacteristics.multiplier
end
defaultDifficulty = defaultDifficulty + entryValueDefault * diffMultiplier
difficulty = difficulty + math.min(entryValue * diffMultiplier, entry.difficultyCharacteristics.max or 1)
end
end
-- normalize to 10
difficulty = difficulty / defaultDifficulty * 10
if difficulty > 23 then
res = "Impossible"
elseif difficulty > 16 then
res = "Very hard"
elseif difficulty > 11 then
res = "Hard"
elseif difficulty > 8 then
res = "Normal"
elseif difficulty > 6 then
res = "Easy"
elseif difficulty > 4 then
res = "Very easy"
elseif difficulty > 2 then
res = "Barely different"
else
res = "Vanilla but sutures"
end
res = res .. " (" .. HF.Round(difficulty, 1) .. ")"
return res
end
--bulk of the GUI code
local function ConstructUI(parent)
local list = easySettings.BasicList(parent)
--info text
local userBlock = GUI.TextBlock(
GUI.RectTransform(Vector2(1, 0.2), 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.",
Color(200, 255, 255),
nil,
GUI.Alignment.Center,
true,
nil,
Color(0, 0, 0)
)
local difficultyBlock = GUI.TextBlock(
GUI.RectTransform(Vector2(1, 0.1), list.Content.RectTransform),
"",
Color(200, 255, 255),
nil,
GUI.Alignment.Center,
true,
nil,
Color(0, 0, 0)
)
--set difficulty text (why does this even exist in the first place)
local function OnChanged()
difficultyRate = "Calculated difficulty rating: " .. DetermineDifficulty()
difficultyBlock.Text = difficultyRate
end
OnChanged()
--empty space
--GUI.TextBlock(GUI.RectTransform(Vector2(0.2, 0.1), list.Content.RectTransform), "", Color(255,255,255), nil, GUI.Alignment.Center, true, nil, Color(0,0,0))
-- procedurally construct config UI
for key, entry in pairs(NTConfig.Entries) do
if entry.type == "float" then
-- scalar value
--grab range
local minrange = ""
local maxrange = ""
local count = 0
for _, rangegrab in pairs(entry.range) do
if count == 0 then
minrange = rangegrab
end
if count == 1 then
maxrange = rangegrab
end
count = count + 1
end
local rect = GUI.RectTransform(Vector2(1, 0.05), list.Content.RectTransform)
local textBlock = GUI.TextBlock(
rect,
entry.name .. " (" .. minrange .. "-" .. maxrange .. ")",
Color(230, 230, 170),
nil,
GUI.Alignment.Center,
true,
nil,
Color(0, 0, 0)
)
if entry.description then
textBlock.ToolTip = entry.description
end
local scalar =
GUI.NumberInput(GUI.RectTransform(Vector2(1, 0.08), list.Content.RectTransform), NumberType.Float)
local key2 = key
scalar.valueStep = 0.1
scalar.MinValueFloat = 0
scalar.MaxValueFloat = 100
if entry.range then
scalar.MinValueFloat = entry.range[1]
scalar.MaxValueFloat = entry.range[2]
end
scalar.FloatValue = NTConfig.Get(key2, 1)
scalar.OnValueChanged = function()
NTConfig.Set(key2, scalar.FloatValue)
OnChanged()
end
elseif entry.type == "string" then
--user string input
local style = ""
--get custom style
if entry.style ~= nil then
style = " (" .. entry.style .. ")"
end
local rect = GUI.RectTransform(Vector2(1, 0.05), list.Content.RectTransform)
local textBlock = GUI.TextBlock(
rect,
entry.name .. style,
Color(230, 230, 170),
nil,
GUI.Alignment.Center,
true,
nil,
Color(0, 0, 0)
)
if entry.description then
textBlock.ToolTip = entry.description
end
local stringinput = MultiLineTextBox(list.Content.RectTransform, "", entry.boxsize)
stringinput.Text = table.concat(entry.value, ",")
stringinput.OnTextChangedDelegate = function(textBox)
entry.value = CommaStringToTable(textBox.Text)
end
elseif entry.type == "bool" then
-- toggle
local rect = GUI.RectTransform(Vector2(1, 0.2), list.Content.RectTransform)
local toggle = GUI.TickBox(rect, entry.name)
if entry.description then
toggle.ToolTip = entry.description
end
local key2 = key
toggle.Selected = NTConfig.Get(key2, false)
toggle.OnSelected = function()
NTConfig.Set(key2, toggle.State == GUIComponent.ComponentState.Selected)
OnChanged()
end
elseif entry.type == "category" then
-- visual separation
GUI.TextBlock(
GUI.RectTransform(Vector2(1, 0.05), list.Content.RectTransform),
entry.name,
Color(255, 255, 255),
nil,
GUI.Alignment.Center,
true,
nil,
Color(0, 0, 0)
)
end
end
--empty space as last tickbox was getting cutoff
GUI.TextBlock(
GUI.RectTransform(Vector2(1, 0.05), list.Content.RectTransform),
"",
Color(255, 255, 255),
nil,
GUI.Alignment.Center,
true,
nil,
Color(0, 0, 0)
)
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
Networking.Receive("NT.ConfigUpdate", function(msg)
NTConfig.ReceiveConfig(msg)
local parent = configUI.Parent.Parent
configUI.RectTransform.Parent = nil
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)

View File

@@ -0,0 +1,170 @@
--original code by Evil Factory,
--adapted to NT
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
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 button = GUI.Button(
GUI.RectTransform(Vector2(1, 0.1), list.RectTransform),
value.Name,
GUI.Alignment.Center,
"GUIButtonSmall"
)
button.OnClicked = function()
value.OnOpen(frame)
end
end
end
end, Hook.HookMethodType.After)
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
easySettings.BasicList = function(parent, size)
local menuContent = GUI.Frame(GUI.RectTransform(size or Vector2(0.3, 0.6), parent.RectTransform, GUI.Anchor.Center))
local menuList = GUI.ListBox(GUI.RectTransform(Vector2(1, 0.95), menuContent.RectTransform, GUI.Anchor.TopCenter))
easySettings.SaveButton(menuContent)
easySettings.CloseButton(menuContent)
easySettings.ResetButton(menuContent)
return menuList
end
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
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
--save and exit
easySettings.SaveButton = function(parent)
local button = GUI.Button(
GUI.RectTransform(Vector2(0.33, 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
--discard and exit
easySettings.CloseButton = function(parent)
local button = GUI.Button(
GUI.RectTransform(Vector2(0.33, 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
--reset and exit
easySettings.ResetButton = function(parent)
local button = GUI.Button(
GUI.RectTransform(Vector2(0.33, 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
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
return easySettings

View File

@@ -0,0 +1,182 @@
-- 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)

View File

@@ -0,0 +1,32 @@
-- Hooks CalculateMovementPenalty method of Barotrauma.Character
-- when painless enough, disable weapon sway / movement hindrance limb penalties
-- !!! Lags the game, skipping this file, and there is no Lua perf tracker to potentially fix, screw it for now
-- 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)

View File

@@ -0,0 +1,112 @@
-- 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)

View File

@@ -0,0 +1,41 @@
-- 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"
Hook.Add("human.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, "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("human.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
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)

View File

@@ -0,0 +1,312 @@
-- 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)
-- dont bother with creatures
if not character.IsHuman then
return
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
local velocityMagnitude = HF.Magnitude(velocity)
velocityMagnitude = velocityMagnitude ^ 1.5
-- 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,
200
)
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 - 55) / 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

View File

@@ -0,0 +1,23 @@
-- 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,101 @@
-- 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)
HF.SpawnItemPlusFunction("antisepticspray", function(params2)
HF.SpawnItemPlusFunction("antiseptic", nil, nil, params2.item.OwnInventory, 0)
end, nil, params.item.OwnInventory, 5)
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)
HF.SpawnItemPlusFunction("organscalpel_kidneys", nil, nil, params.item.OwnInventory, 8)
HF.SpawnItemPlusFunction("organscalpel_liver", nil, nil, params.item.OwnInventory, 9)
HF.SpawnItemPlusFunction("organscalpel_lungs", nil, nil, params.item.OwnInventory, 10)
HF.SpawnItemPlusFunction("organscalpel_heart", nil, nil, params.item.OwnInventory, 11)
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)

View File

@@ -0,0 +1,36 @@
-- 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)

View File

@@ -0,0 +1,302 @@
-- 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)
local description = ""
if functiontag ~= "" then
description = HF.GetText("multiscalpel." .. functiontag)
end
if description == "" 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.Description = description
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
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

View File

@@ -0,0 +1,311 @@
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 = {}
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
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
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
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
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
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
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
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
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)
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
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,
}
function NTC.AddAfflictionAffectingVitality(identifier)
NTC.AfflictionsAffectingVitality[identifier] = true
end
-- these functions are used by neurotrauma to check for symptom overrides
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
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
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
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
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
-- 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

View File

@@ -0,0 +1,671 @@
-- 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.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
then
return
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"))
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
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
)
if strength >= 15 and HF.Chance(math.min(strength / 60, 0.7)) then
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 >= 25 and HF.Chance(0.25) then
-- drop previously held item
local previtem = HF.GetHeadWear(character)
if previtem ~= nil then
previtem.Drop(character, true)
end
HF.AddAfflictionLimb(character, "gate_ta_h", limbtype, 5)
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"))
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
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
)
if strength >= 15 and HF.Chance(math.min(strength / 60, 0.7)) then
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"))
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, 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"))
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
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
)
if strength >= 15 and HF.Chance(math.min(strength / 60, 0.7)) then
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"))
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
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
)
if strength >= 15 and HF.Chance(math.min(strength / 60, 0.7)) then
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"))
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

View File

@@ -0,0 +1,19 @@
-- 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
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)

View File

@@ -0,0 +1,9 @@
-- 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)

View File

@@ -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)

View File

@@ -0,0 +1,142 @@
-- 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)

View File

@@ -0,0 +1,408 @@
NTConfig = { Entries = {}, Expansions = {} } -- contains all config options, their default, type, valid ranges, difficulty influence
local configDirectoryPath = Game.SaveFolder .. "/ModConfigs"
local configFilePath = configDirectoryPath .. "/Neurotrauma.json"
-- this is the function that gets used in other mods to add their own settings to the config
function NTConfig.AddConfigOptions(expansion)
table.insert(NTConfig.Expansions, expansion)
for key, entry in pairs(expansion.ConfigData) do
NTConfig.Entries[key] = entry
NTConfig.Entries[key].value = entry.default
end
end
function NTConfig.SaveConfig()
--prevent both owner client and server saving config at the same time and potentially erroring from file access
if Game.IsMultiplayer and CLIENT and Game.Client.MyClient.IsOwner then
return
end
local tableToSave = {}
for key, entry in pairs(NTConfig.Entries) do
tableToSave[key] = entry.value
end
File.CreateDirectory(configDirectoryPath)
File.Write(configFilePath, json.serialize(tableToSave))
end
function NTConfig.ResetConfig()
local tableToSave = {}
for key, entry in pairs(NTConfig.Entries) do
tableToSave[key] = entry.default
NTConfig.Entries[key] = entry
NTConfig.Entries[key].value = entry.default
end
-- File.CreateDirectory(configDirectoryPath)
-- File.Write(configFilePath, json.serialize(tableToSave))
end
function NTConfig.LoadConfig()
if not File.Exists(configFilePath) then
return
end
local readConfig = json.parse(File.Read(configFilePath))
for key, value in pairs(readConfig) do
if NTConfig.Entries[key] then
NTConfig.Entries[key].value = value
end
end
end
function NTConfig.Get(key, default)
if NTConfig.Entries[key] then
return NTConfig.Entries[key].value
end
return default
end
function NTConfig.Set(key, value)
if NTConfig.Entries[key] then
NTConfig.Entries[key].value = value
end
end
function NTConfig.SendConfig(reciverClient)
local tableToSend = {}
for key, entry in pairs(NTConfig.Entries) do
tableToSend[key] = entry.value
end
local msg = Networking.Start("NT.ConfigUpdate")
msg.WriteString(json.serialize(tableToSend))
if SERVER then
Networking.Send(msg, reciverClient and reciverClient.Connection or nil)
else
Networking.Send(msg)
end
end
function NTConfig.ReceiveConfig(msg)
local RecivedTable = {}
RecivedTable = json.parse(msg.ReadString())
for key, value in pairs(RecivedTable) do
NTConfig.Set(key, value)
end
end
NT.ConfigData = {
NT_header1 = { name = "Neurotrauma", type = "category" },
NT_dislocationChance = {
name = "Dislocation chance",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { max = 5 },
},
NT_fractureChance = {
name = "Fracture chance",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 2, max = 5 },
},
NT_pneumothoraxChance = {
name = "Pneumothorax chance",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { max = 5 },
},
NT_tamponadeChance = {
name = "Tamponade chance",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { max = 3 },
},
NT_heartattackChance = {
name = "Heart attack chance",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 0.5, max = 1 },
},
NT_strokeChance = {
name = "Stroke chance",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 0.5, max = 1 },
},
NT_infectionRate = {
name = "Infection rate",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 1.5, max = 5 },
},
NT_CPRFractureChance = {
name = "CPR fracture chance",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 0.5, max = 1 },
},
NT_traumaticAmputationChance = {
name = "Traumatic amputation chance",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { max = 3 },
},
NT_neurotraumaGain = {
name = "Neurotrauma gain",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 3, max = 10 },
},
NT_organDamageGain = {
name = "Organ damage gain",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 2, max = 8 },
},
NT_fibrillationSpeed = {
name = "Fibrillation rate",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 1.5, max = 8 },
},
NT_gangrenespeed = {
name = "Gangrene rate",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 0.5, max = 5 },
},
NT_falldamage = {
name = "Falldamage",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 0.5, max = 5 },
},
NT_falldamageSeriousInjuryChance = {
name = "Falldamage serious injury chance",
default = 1,
range = { 0, 100 },
type = "float",
difficultyCharacteristics = { multiplier = 0.5, max = 5 },
},
NT_vanillaSkillCheck = {
name = "Vanilla skill check formula",
default = false,
type = "bool",
description = "Changes the chance to succeed a lua skillcheck from skill/requiredskill to 100-(requiredskill-skill))/100",
},
NT_disableBotAlgorithms = {
name = "Disable bot treatment algorithms",
default = true,
type = "bool",
description = "Prevents bots from attempting to treat afflictions.\nThis is desireable, because bots suck at treating things, and their bad attempts lag out the game immensely.",
},
NT_screams = { name = "Screams", default = true, type = "bool", description = "Characters scream when in pain." },
NT_ignoreModConflicts = {
name = "Ignore mod conflicts",
default = false,
type = "bool",
description = "Prevent the mod conflict affliction from showing up.",
},
NT_organRejection = {
name = "Organ rejection",
default = false,
type = "bool",
difficultyCharacteristics = { multiplier = 0.5 },
description = "When transplanting an organ, there is a chance that the organ gets rejected.\nThe higher the patients immunity at the time of the transplant, the higher the chance.",
},
NT_fracturesRemoveCasts = {
name = "Fractures remove casts",
default = true,
type = "bool",
difficultyCharacteristics = { multiplier = 0.5 },
description = "When receiving damage that would cause a fracture, remove plaster casts on the limb",
},
NTCRE_header1 = { name = "Consent Required", type = "category" },
NTCRE_ConsentRequired = {
name = "Enable Consent Required",
default = true,
type = "bool",
description = "Integrated consent required mod.\nIf disabled, none of NPCs will get aggravated by medical interactions.",
},
NTSCAN_header1 = { name = "Scanner Settings", type = "category" },
NTSCAN_enablecoloredscanner = {
name = "Enable Colored Scanner",
default = true,
type = "bool",
description = "Enable colored health scanner text messages.",
},
NTSCAN_lowmedThreshold = {
name = "Low-Medium Text Threshold",
default = 25,
range = { 0, 100 },
type = "float",
description = "Where the Low progress color ends and Medium progress color begins.",
},
NT_medhighThreshold = {
name = "Medium-High Text Threshold",
default = 65,
range = { 0, 100 },
type = "float",
description = "Where the Medium progress color ends and High progress color begins.",
},
NTSCAN_basecolor = {
name = "Base Text Color",
default = { "100,100,200" },
style = "R,G,B",
type = "string",
boxsize = 0.05,
description = "Scanner text color.",
},
NTSCAN_namecolor = {
name = "Name Text Color",
default = { "125,125,225" },
style = "R,G,B",
type = "string",
boxsize = 0.05,
description = "Scanner text color for player names.",
},
NTSCAN_lowcolor = {
name = "Low Priority Color",
default = { "100,200,100" },
style = "R,G,B",
type = "string",
boxsize = 0.05,
description = "Scanner text color for afflictions that have low progress.",
},
NTSCAN_medcolor = {
name = "Medium Priority Color",
default = { "200,200,100" },
style = "R,G,B",
type = "string",
boxsize = 0.05,
description = "Scanner text color for afflictions that have medium progress.",
},
NTSCAN_highcolor = {
name = "High Priority Color",
default = { "250,100,100" },
style = "R,G,B",
type = "string",
boxsize = 0.05,
description = "Scanner text color for afflictions that have high progress.",
},
NTSCAN_vitalcolor = {
name = "Vital Priority Color",
default = { "255,0,0" },
style = "R,G,B",
type = "string",
boxsize = 0.05,
description = "Scanner text color for vital afflictions (Arterial bleed, Traumatic amputation).",
},
NTSCAN_removalcolor = {
name = "Removed Organ Color",
default = { "0,255,255" },
style = "R,G,B",
type = "string",
boxsize = 0.05,
description = "Scanner text color for removed organs (Heart removed, leg amputation).",
},
NTSCAN_customcolor = {
name = "Custom Category Color",
default = { "180,50,200" },
style = "R,G,B",
type = "string",
boxsize = 0.05,
description = "Scanner text color for the custom category.",
},
NTSCAN_VitalCategory = {
name = "Included Vital Afflictions",
default = {
"cardiacarrest",
"ll_arterialcut",
"rl_arterialcut",
"la_arterialcut",
"ra_arterialcut",
"t_arterialcut",
"h_arterialcut",
"tra_amputation",
"tla_amputation",
"trl_amputation",
"tll_amputation",
"th_amputation",
},
style = "identifier,identifier",
type = "string",
boxsize = 0.1,
description = "You can add or remove afflictions to customize this list to your liking.",
},
NTSCAN_RemovalCategory = {
name = "Included Removal Affictions",
default = {
"heartremoved",
"brainremoved",
"lungremoved",
"kidneyremoved",
"liverremoved",
"sra_amputation",
"sla_amputation",
"srl_amputation",
"sll_amputation",
"sh_amputation",
},
style = "identifier, identifier",
type = "string",
boxsize = 0.1,
description = "You can add or remove afflictions to customize this list to your liking.",
},
NTSCAN_CustomCategory = {
name = "Custom Affliction Category",
default = {""},
style = "identifier,identifier",
type = "string",
boxsize = 0.1,
description = "You can add or remove afflictions to customize this list to your liking.",
},
NTSCAN_IgnoredCategory = {
name = "Ignored Afflictions",
default = { "" },
style = "identifier,identifier",
type = "string",
boxsize = 0.1,
description = "Afflictions added to this category will be ignored by the health scanner.",
},
}
NTConfig.AddConfigOptions(NT)
-- wait a bit before loading the config so all options have had time to be added
-- do note that this unintentionally causes a couple ticks time on load during which the config is always the default
-- remember to put default values in your NTConfig.Get calls!
Timer.Wait(function()
NTConfig.LoadConfig()
Timer.Wait(function()
NTConfig.SaveConfig()
end, 1000)
end, 50)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,76 @@
-- 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 extremity amputations on the head and torso
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)
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)

4
Neurotrauma/Lua/todo.md Normal file
View File

@@ -0,0 +1,4 @@
# TODO:
- implement body temp
- rebalance skill gain amount from existing treatments
- rest are probably in .lua files themselves