Files
barotrauma-localmods/Real Sonar/Lua/functions.lua
2025-03-31 13:19:47 +02:00

555 lines
20 KiB
Lua

LuaUserData.RegisterType("Barotrauma.DamageModifier")
LuaUserData.MakeFieldAccessible(Descriptors["Barotrauma.Items.Components.Sonar"], "pingDirection")
-- Returns true if there is a valid path from a hull breach to the character.
function RealSonar.pathToBreach(character)
-- Creature damage.
if not RealSonar.Config.CreatureDamage and not character.isHuman then
return false
end
-- Player damage.
if not RealSonar.Config.PlayerDamage and character.isPlayer then
return false
end
-- Bot damage.
if not RealSonar.Config.BotDamage and character.isBot then
return false
end
-- Returns false for ignored players.
if character.isPlayer then
for name in RealSonar.Config.IgnoredPlayers do
if name == character.name then
return false
end
end
end
-- Returns false for ignored characters.
for identifier in RealSonar.Config.IgnoredCharacters do
if identifier == character.Prefab.Identifier.Value then
return false
end
end
-- Returns true for characters outside.
if not character.AnimController.currentHull then
return true
end
-- Creature hull detection.
if not RealSonar.Config.CreatureHullDetection and not character.isHuman and character.InWater then
return true
end
-- Human hull detection.
if not RealSonar.Config.HumanHullDetection and character.isHuman and character.InWater then
return true
end
-- This code looks absolutely atrocious. It's so bad that I'm afraid to go near it to refactor it.
local hulls = character.AnimController.currentHull.GetConnectedHulls(true, 100, true)
local hullCount = 0
local waterFilledHulls = 0
for hull in hulls do
hullCount = hullCount + 1
if hull.WaterPercentage >= 50 then
waterFilledHulls = waterFilledHulls + 1
end
for gap in hull.ConnectedGaps do
-- If breach is external, more than 40% open, and water level is above the lower boundary of the gap .
if gap.IsRoomToRoom == false and gap.open >= 0.4 and hull.Surface + hull.WaveY[#hull.WaveY - 1] > gap.rect.Y - gap.Size then
-- If character position is below the gap.
if character.Position.Y < gap.rect.Y - gap.Size then
-- If the majority of the connected hulls are filled with water.
if waterFilledHulls >= hullCount - 1 then
return true
end
else
return true
end
end
end
end
return false
end
-- Returns true if there's a mild external hull breach in the character's structure.
function RealSonar.hullBreached(character)
if not character.AnimController.currentHull then
return true
end
local hulls = character.AnimController.currentHull.GetConnectedHulls(true, 100, false)
for hull in hulls do
for gap in hull.ConnectedGaps do
if gap.IsRoomToRoom == false and gap.open >= 0.4 then
return true
end
end
end
return false
end
-- Returns the distance between two points in 2D space.
function RealSonar.distanceBetween(point1, point2)
local xd = point1.X - point2.X
local yd = point1.Y - point2.Y
return math.sqrt(xd*xd + yd*yd)
end
-- Returns true if the character is dangerously close to any active sonar source.
-- Ideally this checks if the gap is in range instead of the character, but I can't find a way to pass it in.
function RealSonar.characterInSonarRange(character)
local inSonar = false
local sonar
for item in RealSonar.sonarItems do
sonar = item.GetComponentString("Sonar")
if tostring(sonar.currentMode) == "Active" and sonar.Voltage > sonar.minVoltage and RealSonar.distanceBetween(item.worldPosition, character.worldPosition) <= sonar.Range * 0.75 then
if sonar.UseDirectionalPing then
if RealSonar.inDirectionalSector(character, item) then
inSonar = true
break
end
else
inSonar = true
break
end
end
end
return inSonar
end
-- Returns true if the charcter is inside the direction ping sector.
function RealSonar.inDirectionalSector(character, item)
local sonar = item.GetComponentString("Sonar")
local px, py = character.worldPosition.X, character.worldPosition.Y
local cx, cy = item.Submarine.WorldPosition.X, item.Submarine.WorldPosition.Y
local r = sonar.Range
local theta = math.atan2(-sonar.pingDirection.Y, sonar.pingDirection.X) * 180 / math.pi
local dx = px - cx
local dy = py - cy
local angle = math.atan2(dy, dx) * 180 / math.pi
if angle < 0 then
angle = angle + 360
end
local angle_diff = math.abs(angle - theta)
if angle_diff <= 15 or angle_diff >= 345 then
local dist = math.sqrt((px - cx)^2 + (py - cy)^2)
if dist <= r then
return true
end
end
return false
end
function RealSonar.getSonarResistanceData(character)
local resistanceData = {}
local damageModifier = 1
local muffleSonar = false
if not character or not character.Inventory then
table.insert(resistanceData, damageModifier)
table.insert(resistanceData, muffleSonar)
return resistanceData
end
local outerClothes = character.Inventory.GetItemInLimbSlot(InvSlotType.OuterClothes)
local innerClothes = character.Inventory.GetItemInLimbSlot(InvSlotType.InnerClothes)
local head = character.Inventory.GetItemInLimbSlot(InvSlotType.Head)
local equippedClothing = {}
if outerClothes then table.insert(equippedClothing, outerClothes) end
if innerClothes then table.insert(equippedClothing, innerClothes) end
if head then table.insert(equippedClothing, head) end
local identifier
local wearable
local configOverride = false
for clothing in equippedClothing do
identifier = clothing.Prefab.Identifier.Value
wearable = clothing.GetComponentString("Wearable")
-- Checking config.
for id, data in pairs(RealSonar.Config.WearableProtections) do
if id == identifier then
damageModifier = damageModifier - data["damageMultiplier"]
configOverride = true
if data["anechoic"] == true then
muffleSonar = true
end
break
end
end
-- Checking items.
if not configOverride then
if clothing.HasTag("anechoic") then
muffleSonar = true
end
if wearable ~= nil then -- Prevents errors if the player equips a flashlight on their head etc.
for modifier in wearable.DamageModifiers do
if modifier.AfflictionTypes == "activesonar" then
damageModifier = damageModifier - (1 - modifier.DamageMultiplier)
end
end
end
end
configOverride = false
end
-- Constraints. Having 2 be the max allows for negative protection values on diving suits up to -1.0
if damageModifier < 0 then
damageModifier = 0
elseif damageModifier > 2 then
damageModifier = 2
end
table.insert(resistanceData, damageModifier)
table.insert(resistanceData, muffleSonar)
return resistanceData
end
function RealSonar.getTerminalFromSub(sub)
for terminal in RealSonar.sonarItems do
if terminal.Submarine == sub then
return terminal
end
end
end
function RealSonar.isEnabledForTerminal(terminal_id)
if terminal_id == "navterminal" then
if RealSonar.Config.SubmarineSonar then
return true
else
return false
end
elseif terminal_id == "sonarmonitor" then
if RealSonar.Config.BeaconSonar then
return true
else
return false
end
elseif terminal_id =="shuttlenavterminal" then
if RealSonar.Config.ShuttleSonar then
return true
else
return false
end
elseif RealSonar.Config.SonarTerminals[terminal_id] then
if RealSonar.Config.CustomSonar then
return true
else
return false
end
-- The default return value is true because all terminals should be enabled by default.
else
return true
end
end
function RealSonar.getSonarItems()
RealSonar.sonarItems = {}
for item in Item.RepairableItems do
local item_id = item.Prefab.Identifier.Value
for terminal_id in pairs(RealSonar.Config.SonarTerminals) do
if item_id == terminal_id then
table.insert(RealSonar.sonarItems, item)
end
end
end
end
function RealSonar.getCaptain(sub)
for _, character in pairs(Character.CharacterList) do
if character.isHuman and character.Submarine == sub and character.IsCaptain then
return character
end
end
end
function RealSonar.getVFXPresetString()
if RealSonar.Config.VFXPreset == 2 then
return ""
elseif RealSonar.Config.VFXPreset == 1 then
return "lowfx"
else
return "nofx"
end
end
-- General reminder: It might be worth globally changing the naming convention of "", "shuttle", and "beacon" to match "high", "medium", "low"
function RealSonar.getTerminalType(terminal_id)
local damagePreset = "high"
if RealSonar.Config.SonarTerminals[terminal_id] then
damagePreset = RealSonar.Config.SonarTerminals[terminal_id]["damage"]
end
local terminal_type = ""
if damagePreset == "medium" then
terminal_type = "shuttle"
elseif damagePreset == "low" then
terminal_type = "beacon"
end
return terminal_type
end
function RealSonar.getEnemySub()
RealSonar.EnemySub = nil
if Submarine.MainSubs[2] then
local sub = Submarine.MainSubs[2]
RealSonar.EnemySub = {}
RealSonar.EnemySub.terminal = RealSonar.getTerminalFromSub(sub)
RealSonar.EnemySub.sonar = RealSonar.EnemySub.terminal.GetComponentString("Sonar")
RealSonar.EnemySub.captain = RealSonar.getCaptain(sub)
end
end
function RealSonar.updateEnemySonarMode()
local terminal = RealSonar.EnemySub.terminal
local sonar = RealSonar.EnemySub.sonar
local captain = RealSonar.EnemySub.captain
if tostring(sonar.currentMode) == "Passive" and captain and not captain.IsUnconscious and not captain.isDead and RealSonar.distanceBetween(captain.WorldPosition, terminal.WorldPosition) < 700 and not RealSonar.hullBreached(captain) then
sonar.currentMode = 0
if Game.IsMultiplayer then
terminal.CreateServerEvent(Components.Sonar, sonar)
end
return math.random(1,3)
elseif tostring(sonar.currentMode) == "Active" and captain and not captain.IsUnconscious and not captain.isDead then
sonar.currentMode = 1
if Game.IsMultiplayer then
terminal.CreateServerEvent(Components.Sonar, sonar)
end
end
return math.random(6,12)
end
function RealSonar.setSonarRanges()
if Game.IsMultiplayer then return end
local defaultRange = 12000
local terminal_id
local sonar
for terminal in RealSonar.sonarItems do
terminal_id = terminal.Prefab.Identifier.Value
if RealSonar.Config.SonarTerminals[terminal_id] then
defaultRange = RealSonar.Config.SonarTerminals[terminal_id]["range"]
end
sonar = terminal.GetComponentString("Sonar")
sonar.Range = defaultRange * RealSonar.Config.SonarRange
--if Game.IsMultiplayer then terminal.CreateServerEvent(Components.Sonar, sonar) end
end
end
-- This function adds the "anechoic" tag to specified items in the config file.
-- Allowing bots to wear these items while doing repairs with the sonar on.
function RealSonar.setItemTags()
local property
for item in Item.ItemList do
for id, data in pairs(RealSonar.Config.WearableProtections) do
if item.Prefab.Identifier.Value == id and data["anechoic"] then
item.AddTag("anechoic")
if Game.IsMultiplayer then
property = item.SerializableProperties[Identifier("Tags")]
Networking.CreateEntityEvent(item, Item.ChangePropertyEventData(property, item))
end
end
end
end
end
-- This function corrects the brain hemorrhage and serversidelua afflictions.
function RealSonar.updateAfflictions()
local brainHemorrhages
local hasServerSideLua
local fx_preset = RealSonar.getVFXPresetString()
for _, character in pairs(Character.CharacterList) do
brainHemorrhages = {}
hasServerSideLua = false
for affliction in character.CharacterHealth.GetAllAfflictions() do
if (character.isHuman and not character.isDead) and (affliction.Prefab.Identifier.Value == "brainhemorrhage" or affliction.Prefab.Identifier.Value == "brainhemorrhagelowfx" or affliction.Prefab.Identifier == "brainhemorrhagenofx") then
table.insert(brainHemorrhages, affliction)
end
if affliction.Prefab.Identifier.Value == "serversidelua" then
hasServerSideLua = true
end
end
for _, brainHemorrhage in pairs(brainHemorrhages) do
if brainHemorrhage.Prefab.Identifier.Value ~= string.format("brainhemorrhage%s", fx_preset) then
RealSonar.GiveAfflictionHead(character, string.format("brainhemorrhage%s", fx_preset), brainHemorrhage.Strength)
brainHemorrhage.Strength = 0
end
end
if not hasServerSideLua then
RealSonar.SetAffliction(character, "serversidelua", 1)
end
end
return 2
end
function RealSonar.GiveAffliction(character, identifier, strength)
local prefab = AfflictionPrefab.Prefabs[identifier]
local affliction = prefab.Instantiate(strength)
character.CharacterHealth.ApplyAffliction(character.AnimController.GetLimb(LimbType.Torso), affliction, true)
end
function RealSonar.SetAffliction(character, identifier, strength)
local prefab = AfflictionPrefab.Prefabs[identifier]
local affliction = prefab.Instantiate(strength)
character.CharacterHealth.ApplyAffliction(character.AnimController.GetLimb(LimbType.Torso), affliction, false)
end
function RealSonar.GiveAfflictionHead(character, identifier, strength)
local prefab = AfflictionPrefab.Prefabs[identifier]
local affliction = prefab.Instantiate(strength)
character.CharacterHealth.ApplyAffliction(character.AnimController.GetLimb(LimbType.Head), affliction, true)
end
-- Nav terminal sounds.
function RealSonar.airSounds(character, strength)
if strength > 95 then
RealSonar.GiveAffliction(character, "sonarPingAirClose", 1)
elseif strength > 25 then
RealSonar.GiveAffliction(character, "sonarPingAirMedium", 1)
else
RealSonar.GiveAffliction(character, "sonarPingAirFar", 1)
end
end
function RealSonar.airSoundsDirectional(character, strength)
if strength > 95 then
RealSonar.GiveAffliction(character, "sonarPingAirCloseDirectional", 1)
elseif strength > 25 then
RealSonar.GiveAffliction(character, "sonarPingAirMediumDirectional", 1)
else
RealSonar.GiveAffliction(character, "sonarPingAirFarDirectional", 1)
end
end
function RealSonar.waterSounds(character, strength)
if strength > 75 then
RealSonar.GiveAffliction(character, "sonarPingWaterVeryClose", 1)
elseif strength > 50 then
RealSonar.GiveAffliction(character, "sonarPingWaterClose", 1)
elseif strength > 25 then
RealSonar.GiveAffliction(character, "sonarPingWaterMedium", 1)
else
RealSonar.GiveAffliction(character, "SonarPingWaterFar", 1)
end
end
function RealSonar.waterSoundsDirectional(character, strength)
if strength > 75 then
RealSonar.GiveAffliction(character, "sonarPingWaterVeryCloseDirectional", 1)
elseif strength > 50 then
RealSonar.GiveAffliction(character, "sonarPingWaterCloseDirectional", 1)
elseif strength > 25 then
RealSonar.GiveAffliction(character, "sonarPingWaterMediumDirectional", 1)
else
RealSonar.GiveAffliction(character, "SonarPingWaterFarDirectional", 1)
end
end
function RealSonar.suitSounds(character, strength)
if strength > 75 then
RealSonar.GiveAffliction(character, "sonarPingSuitVeryClose", 1)
elseif strength > 50 then
RealSonar.GiveAffliction(character, "sonarPingSuitClose", 1)
elseif strength > 25 then
RealSonar.GiveAffliction(character, "sonarPingSuitMedium", 1)
else
RealSonar.GiveAffliction(character, "SonarPingSuitFar", 1)
end
end
function RealSonar.suitSoundsDirectional(character, strength)
if strength > 75 then
RealSonar.GiveAffliction(character, "sonarPingSuitVeryCloseDirectional", 1)
elseif strength > 50 then
RealSonar.GiveAffliction(character, "sonarPingSuitCloseDirectional", 1)
elseif strength > 25 then
RealSonar.GiveAffliction(character, "sonarPingSuitMediumDirectional", 1)
else
RealSonar.GiveAffliction(character, "SonarPingSuitFarDirectional", 1)
end
end
-- Shuttle sounds
function RealSonar.airSoundsShuttle(character, strength)
if strength > 95 then
RealSonar.GiveAffliction(character, "sonarPingAirCloseShuttle", 1)
elseif strength > 50 then
RealSonar.GiveAffliction(character, "sonarPingAirMediumShuttle", 1)
else
RealSonar.GiveAffliction(character, "sonarPingAirFarShuttle", 1)
end
end
function RealSonar.airSoundsShuttleDirectional(character, strength)
if strength > 95 then
RealSonar.GiveAffliction(character, "sonarPingAirCloseShuttleDirectional", 1)
elseif strength > 50 then
RealSonar.GiveAffliction(character, "sonarPingAirMediumShuttleDirectional", 1)
else
RealSonar.GiveAffliction(character, "sonarPingAirFarShuttleDirectional", 1)
end
end
function RealSonar.waterSoundsShuttle(character, strength)
if strength > 75 then
RealSonar.GiveAffliction(character, "sonarPingWaterVeryCloseShuttle", 1)
elseif strength > 50 then
RealSonar.GiveAffliction(character, "sonarPingWaterCloseShuttle", 1)
elseif strength > 25 then
RealSonar.GiveAffliction(character, "sonarPingWaterMediumShuttle", 1)
else
RealSonar.GiveAffliction(character, "SonarPingWaterFarShuttle", 1)
end
end
function RealSonar.waterSoundsShuttleDirectional(character, strength)
if strength > 75 then
RealSonar.GiveAffliction(character, "sonarPingWaterVeryCloseShuttleDirectional", 1)
elseif strength > 50 then
RealSonar.GiveAffliction(character, "sonarPingWaterCloseShuttleDirectional", 1)
elseif strength > 25 then
RealSonar.GiveAffliction(character, "sonarPingWaterMediumShuttleDirectional", 1)
else
RealSonar.GiveAffliction(character, "SonarPingWaterFarShuttleDirectional", 1)
end
end
function RealSonar.suitSoundsShuttle(character, strength)
if strength > 75 then
RealSonar.GiveAffliction(character, "sonarPingSuitVeryCloseShuttle", 1)
elseif strength > 50 then
RealSonar.GiveAffliction(character, "sonarPingSuitCloseShuttle", 1)
elseif strength > 25 then
RealSonar.GiveAffliction(character, "sonarPingSuitMediumShuttle", 1)
else
RealSonar.GiveAffliction(character, "SonarPingSuitFarShuttle", 1)
end
end
function RealSonar.suitSoundsShuttleDirectional(character, strength)
if strength > 75 then
RealSonar.GiveAffliction(character, "sonarPingSuitVeryCloseShuttleDirectional", 1)
elseif strength > 50 then
RealSonar.GiveAffliction(character, "sonarPingSuitCloseShuttleDirectional", 1)
elseif strength > 25 then
RealSonar.GiveAffliction(character, "sonarPingSuitMediumShuttleDirectional", 1)
else
RealSonar.GiveAffliction(character, "SonarPingSuitFarShuttleDirectional", 1)
end
end