v1.1.19.3 (Treacherous Tides Hotfix 2)
This commit is contained in:
@@ -336,7 +336,10 @@ namespace Barotrauma
|
||||
float pressure = AnimController.CurrentHull == null ? 100.0f : AnimController.CurrentHull.LethalPressure;
|
||||
if (pressure > 0.0f)
|
||||
{
|
||||
float zoomInEffectStrength = MathHelper.Clamp(pressure / 100.0f, 0.1f, 1.0f);
|
||||
//lerp in during the 1st second of the pressure timer so the zoom doesn't
|
||||
//"flicker" in and out if the pressure fluctuates around the minimum threshold
|
||||
float timerMultiplier = (PressureTimer / 100.0f);
|
||||
float zoomInEffectStrength = MathHelper.Clamp(pressure / 100.0f * timerMultiplier, 0.0f, 1.0f);
|
||||
cam.Zoom = MathHelper.Lerp(cam.Zoom,
|
||||
cam.DefaultZoom + (Math.Max(pressure, 10) / 150.0f) * Rand.Range(0.9f, 1.1f),
|
||||
zoomInEffectStrength);
|
||||
|
||||
@@ -8,24 +8,25 @@ namespace Barotrauma
|
||||
{
|
||||
internal abstract partial class CircuitBoxConnection
|
||||
{
|
||||
public string Name => Label.Value.Value;
|
||||
public string Name => Connection.Name;
|
||||
|
||||
public CircuitBoxLabel Label { get; private set; }
|
||||
|
||||
private Sprite? knobSprite,
|
||||
screwSprite,
|
||||
connectorSprite;
|
||||
|
||||
private static int padding => GUI.IntScale(8);
|
||||
private static int Padding => GUI.IntScale(8);
|
||||
|
||||
private Option<LocalizedString> tooltip = Option.None;
|
||||
|
||||
private partial void InitProjSpecific(CircuitBox circuitBox)
|
||||
{
|
||||
Label = new CircuitBoxLabel(Connection.Name, GUIStyle.SubHeadingFont);
|
||||
Label = new CircuitBoxLabel(Connection.DisplayName, GUIStyle.SubHeadingFont);
|
||||
knobSprite = circuitBox.ConnectionSprite;
|
||||
screwSprite = circuitBox.ConnectionScrewSprite;
|
||||
connectorSprite = circuitBox.WireConnectorSprite;
|
||||
Length = Rect.Width + padding + Label.Size.X;
|
||||
Length = Rect.Width + Padding + Label.Size.X;
|
||||
}
|
||||
|
||||
public void Draw(SpriteBatch spriteBatch, Vector2 drawPos, Vector2 parentPos, Color color)
|
||||
@@ -41,11 +42,11 @@ namespace Barotrauma
|
||||
float xPos;
|
||||
if (IsOutput)
|
||||
{
|
||||
xPos = drawRect.Left - padding - Label.Size.X;
|
||||
xPos = drawRect.Left - Padding - Label.Size.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
xPos = drawRect.Right + padding;
|
||||
xPos = drawRect.Right + Padding;
|
||||
}
|
||||
|
||||
Vector2 stringPos = new Vector2(xPos, drawRect.Center.Y - Label.Size.Y / 2f);
|
||||
|
||||
@@ -98,7 +98,7 @@ namespace Barotrauma
|
||||
if (gameSession.Missions.Any(m => m is CombatMission))
|
||||
{
|
||||
crewHeader.Text = CombatMission.GetTeamName(CharacterTeamType.Team1);
|
||||
GUIFrame crewFrame2 = new GUIFrame(new RectTransform(new Vector2(0.35f, 0.45f), background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
|
||||
GUIFrame crewFrame2 = new GUIFrame(new RectTransform(crewFrame.RectTransform.RelativeSize, background.RectTransform, Anchor.TopCenter, minSize: new Point(minWidth, minHeight)));
|
||||
rightPanels.Add(crewFrame2);
|
||||
GUIFrame crewFrameInner2 = new GUIFrame(new RectTransform(new Point(crewFrame2.Rect.Width - padding * 2, crewFrame2.Rect.Height - padding * 2), crewFrame2.RectTransform, Anchor.Center), style: "InnerFrame");
|
||||
var crewContent2 = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.95f), crewFrameInner2.RectTransform, Anchor.Center))
|
||||
|
||||
@@ -269,7 +269,7 @@ namespace Barotrauma.Items.Components
|
||||
resource = ItemPrefab.Prefabs[Tags.FPGACircuit];
|
||||
}
|
||||
|
||||
AddComponentInternal(ICircuitBoxIdentifiable.FindFreeID(Components), prefab, resource, pos, static delegate { });
|
||||
AddComponentInternal(ICircuitBoxIdentifiable.FindFreeID(Components), prefab, resource, pos, Character.Controlled, onItemSpawned: null);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1505,14 +1505,28 @@ namespace Barotrauma
|
||||
int stackAmount = DraggingItems.Count;
|
||||
if (selectedSlot?.ParentInventory != null)
|
||||
{
|
||||
stackAmount = Math.Min(
|
||||
stackAmount,
|
||||
selectedSlot.ParentInventory.HowManyCanBePut(draggedItem.Prefab, selectedSlot.SlotIndex, draggedItem.Condition));
|
||||
if (selectedSlot.Item?.OwnInventory != null)
|
||||
{
|
||||
int maxAmountPerSlot = 0;
|
||||
for (int i = 0; i < SelectedSlot.Item.OwnInventory.Capacity; i++)
|
||||
{
|
||||
maxAmountPerSlot = Math.Max(
|
||||
maxAmountPerSlot,
|
||||
selectedSlot.Item.OwnInventory.HowManyCanBePut(draggedItem.Prefab, i, draggedItem.Condition, ignoreItemsInSlot: true));
|
||||
}
|
||||
stackAmount = Math.Min(stackAmount, maxAmountPerSlot);
|
||||
}
|
||||
else
|
||||
{
|
||||
stackAmount = Math.Min(
|
||||
stackAmount,
|
||||
selectedSlot.ParentInventory.HowManyCanBePut(draggedItem.Prefab, selectedSlot.SlotIndex, draggedItem.Condition, ignoreItemsInSlot: true));
|
||||
}
|
||||
}
|
||||
Vector2 stackCountPos = itemPos + Vector2.One * iconSize * 0.25f;
|
||||
string stackCountText = "x" + stackAmount;
|
||||
GUIStyle.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos + Vector2.One, Color.Black);
|
||||
GUIStyle.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos, GUIStyle.TextColorBright);
|
||||
GUIStyle.SmallFont.DrawString(spriteBatch, stackCountText, stackCountPos, GUIStyle.TextColorBright);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,13 +300,27 @@ namespace Barotrauma
|
||||
" - Oxygen: " + ((int)OxygenPercentage), new Vector2(drawRect.X + 5, -drawRect.Y + 5), Color.White);
|
||||
GUIStyle.SmallFont.DrawString(spriteBatch, waterVolume + " / " + Volume, new Vector2(drawRect.X + 5, -drawRect.Y + 20), Color.White);
|
||||
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, (int)(100 * Math.Min(waterVolume / Volume, 1.0f))), Color.Cyan, true);
|
||||
if (WaterVolume > Volume)
|
||||
if (WaterVolume > 0)
|
||||
{
|
||||
float maxExcessWater = Volume * MaxCompress;
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, (int)(100 * (waterVolume - Volume) / maxExcessWater)), GUIStyle.Red, true);
|
||||
drawProgressBar(50, new Point(0, 0), Math.Min(waterVolume / Volume, 1.0f), Color.Cyan);
|
||||
if (WaterVolume > Volume)
|
||||
{
|
||||
float maxExcessWater = Volume * MaxCompress;
|
||||
drawProgressBar(50, new Point(0, 0), (waterVolume - Volume) / maxExcessWater, GUIStyle.Red);
|
||||
}
|
||||
}
|
||||
if (lethalPressure > 0)
|
||||
{
|
||||
drawProgressBar(50, new Point(20, 0), lethalPressure / 100.0f, Color.Red);
|
||||
}
|
||||
|
||||
void drawProgressBar(int height, Point offset, float fillAmount, Color color)
|
||||
{
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X - 2 + offset.X, -drawRect.Y - 2 + drawRect.Height / 2 + offset.Y, 14, height+4), Color.Black * 0.8f, depth: 0.01f, isFilled: true);
|
||||
|
||||
int barHeight = (int)(fillAmount * height);
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X + offset.X, -drawRect.Y + drawRect.Height / 2 + height - barHeight + offset.Y, 10, barHeight), color, isFilled: true);
|
||||
}
|
||||
GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, 100), Color.Black);
|
||||
|
||||
foreach (FireSource fs in FireSources)
|
||||
{
|
||||
|
||||
@@ -261,6 +261,9 @@ namespace Barotrauma
|
||||
{
|
||||
crashHeader += " " + exception.TargetSite.ToString();
|
||||
}
|
||||
//log the message separately, so the same error messages get grouped as the same error in GA
|
||||
//(the full crash report tends to always have some differences between clients, so they get displayed separately)
|
||||
GameAnalyticsManager.AddErrorEvent(GameAnalyticsManager.ErrorSeverity.Critical, crashHeader);
|
||||
GameAnalyticsManager.AddErrorEvent(GameAnalyticsManager.ErrorSeverity.Critical, crashHeader + "\n\n" + sb.ToString());
|
||||
GameAnalyticsManager.ShutDown();
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.1.18.1</Version>
|
||||
<Version>1.1.19.3</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.1.18.1</Version>
|
||||
<Version>1.1.19.3</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>1.1.18.1</Version>
|
||||
<Version>1.1.19.3</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.1.18.1</Version>
|
||||
<Version>1.1.19.3</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.1.18.1</Version>
|
||||
<Version>1.1.19.3</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#nullable enable
|
||||
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -138,7 +138,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
bool result = AddComponentInternal(id, prefab, resource.Prefab, data.Position, it =>
|
||||
bool result = AddComponentInternal(id, prefab, resource.Prefab, data.Position, c.Character, it =>
|
||||
{
|
||||
CreateServerEvent(new CircuitBoxServerCreateComponentEvent(it.ID, resource.Prefab.UintIdentifier, id, data.Position));
|
||||
});
|
||||
|
||||
@@ -80,58 +80,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10);
|
||||
}
|
||||
|
||||
float similarity = 0.0f;
|
||||
for (int i = 0; i < c.LastSentChatMessages.Count; i++)
|
||||
{
|
||||
float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i);
|
||||
|
||||
if (string.IsNullOrEmpty(txt))
|
||||
{
|
||||
similarity += closeFactor;
|
||||
}
|
||||
else
|
||||
{
|
||||
int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]);
|
||||
similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f);
|
||||
}
|
||||
}
|
||||
//order/report messages can be sent a little faster than normal messages without triggering the spam filter
|
||||
if (orderMsg != null)
|
||||
{
|
||||
similarity *= 0.25f;
|
||||
}
|
||||
|
||||
bool isSpamExempt = RateLimiter.IsExempt(c);
|
||||
|
||||
if (similarity + c.ChatSpamSpeed > 5.0f && !isSpamExempt)
|
||||
{
|
||||
GameMain.Server.KarmaManager.OnSpamFilterTriggered(c);
|
||||
|
||||
c.ChatSpamCount++;
|
||||
if (c.ChatSpamCount > 3)
|
||||
{
|
||||
//kick for spamming too much
|
||||
GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked").Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.Server, null);
|
||||
c.ChatSpamTimer = 10.0f;
|
||||
GameMain.Server.SendDirectChatMessage(denyMsg, c);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
c.ChatSpamSpeed += similarity + 0.5f;
|
||||
|
||||
if (c.ChatSpamTimer > 0.0f && !isSpamExempt)
|
||||
{
|
||||
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.Server, null);
|
||||
c.ChatSpamTimer = 10.0f;
|
||||
GameMain.Server.SendDirectChatMessage(denyMsg, c);
|
||||
return;
|
||||
}
|
||||
//order/report messages can be sent a little faster than normal messages without triggering the spam filter;
|
||||
float similarityMultiplier = orderMsg != null ? 0.25f : 1.0f;
|
||||
HandleSpamFilter(c, txt, out bool flaggedAsSpam, similarityMultiplier);
|
||||
if (flaggedAsSpam) { return; }
|
||||
|
||||
if (type == ChatMessageType.Order)
|
||||
{
|
||||
@@ -177,6 +129,65 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Increase the client's chat spam speed and check whether the spam filter should kick in
|
||||
/// </summary>
|
||||
public static void HandleSpamFilter(Client c, string messageText, out bool flaggedAsSpam, float similarityMultiplier = 1.0f)
|
||||
{
|
||||
float similarity = 0.0f;
|
||||
for (int i = 0; i < c.LastSentChatMessages.Count; i++)
|
||||
{
|
||||
float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i);
|
||||
|
||||
if (string.IsNullOrEmpty(messageText))
|
||||
{
|
||||
similarity += closeFactor;
|
||||
}
|
||||
else
|
||||
{
|
||||
int levenshteinDist = ToolBox.LevenshteinDistance(messageText, c.LastSentChatMessages[i]);
|
||||
similarity += Math.Max((messageText.Length - levenshteinDist) / (float)messageText.Length * closeFactor, 0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
similarity *= similarityMultiplier;
|
||||
|
||||
bool isSpamExempt = RateLimiter.IsExempt(c);
|
||||
|
||||
if (similarity + c.ChatSpamSpeed > 5.0f && !isSpamExempt)
|
||||
{
|
||||
GameMain.Server.KarmaManager.OnSpamFilterTriggered(c);
|
||||
|
||||
c.ChatSpamCount++;
|
||||
if (c.ChatSpamCount > 3)
|
||||
{
|
||||
//kick for spamming too much
|
||||
GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked").Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.Server, null);
|
||||
c.ChatSpamTimer = 10.0f;
|
||||
GameMain.Server.SendDirectChatMessage(denyMsg, c);
|
||||
}
|
||||
flaggedAsSpam = true;
|
||||
return;
|
||||
}
|
||||
|
||||
c.ChatSpamSpeed += similarity + 0.5f;
|
||||
|
||||
if (c.ChatSpamTimer > 0.0f && !isSpamExempt)
|
||||
{
|
||||
ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked").Value, ChatMessageType.Server, null);
|
||||
c.ChatSpamTimer = 10.0f;
|
||||
GameMain.Server.SendDirectChatMessage(denyMsg, c);
|
||||
flaggedAsSpam = true;
|
||||
return;
|
||||
}
|
||||
|
||||
flaggedAsSpam = false;
|
||||
}
|
||||
|
||||
public int EstimateLengthBytesServer(Client c)
|
||||
{
|
||||
int length = 1 + //(byte)ServerNetObject.CHAT_MESSAGE
|
||||
|
||||
@@ -369,6 +369,9 @@ namespace Barotrauma.Networking
|
||||
if (!character.ClientDisconnected) { continue; }
|
||||
|
||||
Client owner = connectedClients.Find(c => (c.Character == null || c.Character == character) && character.IsClientOwner(c));
|
||||
bool canOwnerTakeControl =
|
||||
owner != null && owner.InGame && !owner.NeedsMidRoundSync &&
|
||||
(!ServerSettings.AllowSpectating || !owner.SpectateOnly);
|
||||
if (!character.IsDead)
|
||||
{
|
||||
character.KillDisconnectedTimer += deltaTime;
|
||||
@@ -379,18 +382,19 @@ namespace Barotrauma.Networking
|
||||
character.Kill(CauseOfDeathType.Disconnected, null);
|
||||
continue;
|
||||
}
|
||||
if (owner != null && owner.InGame && !owner.NeedsMidRoundSync &&
|
||||
(!ServerSettings.AllowSpectating || !owner.SpectateOnly))
|
||||
if (canOwnerTakeControl)
|
||||
{
|
||||
SetClientCharacter(owner, character);
|
||||
}
|
||||
}
|
||||
else if (owner != null &&
|
||||
else if (canOwnerTakeControl &&
|
||||
character.CauseOfDeath?.Type == CauseOfDeathType.Disconnected &&
|
||||
character.CharacterHealth.VitalityDisregardingDeath > 0)
|
||||
{
|
||||
//create network event immediately to ensure the character is revived client-side
|
||||
//before the client gains control of it (normally status events are created periodically)
|
||||
character.Revive(removeAfflictions: false, createNetworkEvent: true);
|
||||
SetClientCharacter(owner, character);
|
||||
character.Revive(removeAfflictions: false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -345,9 +345,15 @@ namespace Barotrauma
|
||||
sender.SetVote(voteType, client);
|
||||
if (client?.Character != null)
|
||||
{
|
||||
GameMain.Server.SendChatMessage(
|
||||
TextManager.GetWithVariable("traitor.blamebutton.dialog", "[name]", client.Character.DisplayName).Value,
|
||||
ChatMessageType.Radio, senderClient: sender, senderCharacter: sender.Character);
|
||||
string msg = TextManager.GetWithVariable("traitor.blamebutton.dialog", "[name]", client.Character.DisplayName).Value;
|
||||
ChatMessage.HandleSpamFilter(sender, msg, out bool flaggedAsSpam);
|
||||
if (!flaggedAsSpam)
|
||||
{
|
||||
GameMain.Server.SendChatMessage(
|
||||
msg,
|
||||
ChatMessageType.Radio, senderClient: sender, senderCharacter: sender.Character);
|
||||
sender.LastSentChatMessages.Add(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>1.1.18.1</Version>
|
||||
<Version>1.1.19.3</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2023</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -167,10 +167,6 @@ namespace Barotrauma
|
||||
|
||||
public HumanAIController(Character c) : base(c)
|
||||
{
|
||||
if (!c.IsHuman)
|
||||
{
|
||||
throw new Exception($"Tried to create a human ai controller for a non-human: {c.SpeciesName}!");
|
||||
}
|
||||
insideSteering = new IndoorsSteeringManager(this, true, false);
|
||||
outsideSteering = new SteeringManager(this);
|
||||
objectiveManager = new AIObjectiveManager(c);
|
||||
|
||||
@@ -1223,19 +1223,15 @@ namespace Barotrauma
|
||||
public static Character Create(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null, bool spawnInitialItems = true)
|
||||
{
|
||||
Character newCharacter = null;
|
||||
if (prefab.Identifier != CharacterPrefab.HumanSpeciesName)
|
||||
if (prefab.Identifier != CharacterPrefab.HumanSpeciesName || hasAi)
|
||||
{
|
||||
var aiCharacter = new AICharacter(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll, spawnInitialItems);
|
||||
var ai = new EnemyAIController(aiCharacter, seed);
|
||||
|
||||
var ai = (prefab.Identifier == CharacterPrefab.HumanSpeciesName || aiCharacter.Params.UseHumanAI) ?
|
||||
new HumanAIController(aiCharacter) as AIController :
|
||||
new EnemyAIController(aiCharacter, seed);
|
||||
aiCharacter.SetAI(ai);
|
||||
newCharacter = aiCharacter;
|
||||
}
|
||||
else if (hasAi)
|
||||
{
|
||||
var aiCharacter = new AICharacter(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll, spawnInitialItems);
|
||||
var ai = new HumanAIController(aiCharacter);
|
||||
aiCharacter.SetAI(ai);
|
||||
newCharacter = aiCharacter;
|
||||
newCharacter = aiCharacter;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4656,7 +4652,7 @@ namespace Barotrauma
|
||||
}
|
||||
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log);
|
||||
|
||||
public void Revive(bool removeAfflictions = true)
|
||||
public void Revive(bool removeAfflictions = true, bool createNetworkEvent = false)
|
||||
{
|
||||
if (Removed)
|
||||
{
|
||||
@@ -4705,7 +4701,11 @@ namespace Barotrauma
|
||||
limb.IsSevered = false;
|
||||
}
|
||||
|
||||
GameMain.GameSession?.ReviveCharacter(this);
|
||||
GameMain.GameSession?.ReviveCharacter(this);
|
||||
if (createNetworkEvent && GameMain.NetworkMember is { IsServer: true })
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new CharacterStatusEventData());
|
||||
}
|
||||
}
|
||||
|
||||
public override void Remove()
|
||||
|
||||
@@ -148,11 +148,6 @@ namespace Barotrauma
|
||||
return minVitality;
|
||||
}
|
||||
return vitality;
|
||||
|
||||
}
|
||||
private set
|
||||
{
|
||||
vitality = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +249,7 @@ namespace Barotrauma
|
||||
public CharacterHealth(Character character)
|
||||
{
|
||||
this.Character = character;
|
||||
Vitality = 100.0f;
|
||||
vitality = 100.0f;
|
||||
|
||||
DoesBleed = true;
|
||||
UseHealthWindow = false;
|
||||
@@ -271,7 +266,7 @@ namespace Barotrauma
|
||||
this.Character = character;
|
||||
InitIrremovableAfflictions();
|
||||
|
||||
Vitality = UnmodifiedMaxVitality;
|
||||
vitality = UnmodifiedMaxVitality;
|
||||
|
||||
minVitality = character.IsHuman ? -100.0f : 0.0f;
|
||||
|
||||
@@ -971,7 +966,7 @@ namespace Barotrauma
|
||||
|
||||
public void CalculateVitality()
|
||||
{
|
||||
Vitality = MaxVitality;
|
||||
vitality = MaxVitality;
|
||||
IsParalyzed = false;
|
||||
if (Unkillable || Character.GodMode) { return; }
|
||||
|
||||
@@ -984,7 +979,7 @@ namespace Barotrauma
|
||||
{
|
||||
vitalityDecrease *= GetVitalityMultiplier(affliction, limbHealth);
|
||||
}
|
||||
Vitality -= vitalityDecrease;
|
||||
vitality -= vitalityDecrease;
|
||||
affliction.CalculateDamagePerSecond(vitalityDecrease);
|
||||
|
||||
if (affliction.Strength >= affliction.Prefab.MaxStrength &&
|
||||
|
||||
@@ -50,6 +50,9 @@ namespace Barotrauma
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Can the creature live without water or does it die on dry land?"), Editable]
|
||||
public bool NeedsWater { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Note: non-humans with a human AI aren't fully supported. Enabling this on a non-human character may lead to issues.")]
|
||||
public bool UseHumanAI { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Is this creature an artificial creature, like robot or machine that shouldn't be affected by afflictions that affect only organic creatures? Overrides DoesBleed."), Editable]
|
||||
public bool IsMachine { get; set; }
|
||||
|
||||
|
||||
@@ -36,7 +36,19 @@ namespace Barotrauma
|
||||
|
||||
private bool isFinished = false;
|
||||
|
||||
private bool targetNotFound = false;
|
||||
/// <summary>
|
||||
/// If the action tags some entities directly (not trying to find targets on the fly),
|
||||
/// we may be able to determine that targets can not be found even if we'd recheck
|
||||
/// </summary>
|
||||
private bool cantFindTargets = false;
|
||||
|
||||
/// <summary>
|
||||
/// If the TagAction adds a target predicate (a criteria that keeps finding targets on the fly),
|
||||
/// we must keep checking if targets have been found to determine if the action can continue or not
|
||||
/// </summary>
|
||||
private bool mustRecheckTargets = false;
|
||||
|
||||
private bool taggingDone = false;
|
||||
|
||||
public TagAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
|
||||
{
|
||||
@@ -198,6 +210,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
ParentEvent.AddTargetPredicate(tag, predicate);
|
||||
mustRecheckTargets = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +218,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (entities.None())
|
||||
{
|
||||
targetNotFound = true;
|
||||
cantFindTargets = true;
|
||||
return;
|
||||
}
|
||||
if (ChoosePercentage > 0.0f)
|
||||
@@ -230,7 +243,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (entities.None())
|
||||
{
|
||||
targetNotFound = true;
|
||||
cantFindTargets = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -250,7 +263,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (entities.None())
|
||||
{
|
||||
targetNotFound = true;
|
||||
cantFindTargets = true;
|
||||
return;
|
||||
}
|
||||
ParentEvent.AddTarget(tag, entities.GetRandomUnsynced());
|
||||
@@ -260,26 +273,29 @@ namespace Barotrauma
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (isFinished || targetNotFound) { return; }
|
||||
if (isFinished || cantFindTargets) { return; }
|
||||
|
||||
string[] criteriaSplit = Criteria.Split(';');
|
||||
|
||||
targetNotFound = false;
|
||||
foreach (string entry in criteriaSplit)
|
||||
if (!taggingDone)
|
||||
{
|
||||
string[] kvp = entry.Split(':');
|
||||
Identifier key = kvp[0].Trim().ToIdentifier();
|
||||
Identifier value = kvp.Length > 1 ? kvp[1].Trim().ToIdentifier() : Identifier.Empty;
|
||||
if (Taggers.TryGetValue(key, out Action<Identifier> tagger))
|
||||
cantFindTargets = false;
|
||||
string[] criteriaSplit = Criteria.Split(';');
|
||||
foreach (string entry in criteriaSplit)
|
||||
{
|
||||
tagger(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMessage = $"Error in TagAction (event \"{ParentEvent.Prefab.Identifier}\") - unrecognized target criteria \"{key}\".";
|
||||
DebugConsole.ThrowError(errorMessage);
|
||||
GameAnalyticsManager.AddErrorEventOnce($"TagAction.Update:InvalidCriteria_{ParentEvent.Prefab.Identifier}_{key}", GameAnalyticsManager.ErrorSeverity.Error, errorMessage);
|
||||
string[] kvp = entry.Split(':');
|
||||
Identifier key = kvp[0].Trim().ToIdentifier();
|
||||
Identifier value = kvp.Length > 1 ? kvp[1].Trim().ToIdentifier() : Identifier.Empty;
|
||||
if (Taggers.TryGetValue(key, out Action<Identifier> tagger))
|
||||
{
|
||||
tagger(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMessage = $"Error in TagAction (event \"{ParentEvent.Prefab.Identifier}\") - unrecognized target criteria \"{key}\".";
|
||||
DebugConsole.ThrowError(errorMessage);
|
||||
GameAnalyticsManager.AddErrorEventOnce($"TagAction.Update:InvalidCriteria_{ParentEvent.Prefab.Identifier}_{key}", GameAnalyticsManager.ErrorSeverity.Error, errorMessage);
|
||||
}
|
||||
}
|
||||
taggingDone = true;
|
||||
}
|
||||
|
||||
if (ContinueIfNoTargetsFound)
|
||||
@@ -288,7 +304,14 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
isFinished = !targetNotFound;
|
||||
if (mustRecheckTargets)
|
||||
{
|
||||
isFinished = ParentEvent.GetTargets(Tag).Any();
|
||||
}
|
||||
else
|
||||
{
|
||||
isFinished = !cantFindTargets;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1353,6 +1353,7 @@ namespace Barotrauma
|
||||
if (item.HiddenInGame) { continue; }
|
||||
if (!connectedSubs.Contains(item.Submarine)) { continue; }
|
||||
if (item.Prefab.DontTransferBetweenSubs) { continue; }
|
||||
if (AnyParentInventoryDisableTransfer(item)) { continue; }
|
||||
var rootOwner = item.GetRootInventoryOwner();
|
||||
if (rootOwner is Character) { continue; }
|
||||
if (rootOwner is Item ownerItem && (ownerItem.NonInteractable || item.NonPlayerTeamInteractable || ownerItem.HiddenInGame)) { continue; }
|
||||
@@ -1362,6 +1363,15 @@ namespace Barotrauma
|
||||
if (item.Components.Any(c => c is Wire w && w.Connections.Any(c => c != null))) { continue; }
|
||||
itemsToTransfer.Add((item, item.Container));
|
||||
item.Submarine = null;
|
||||
|
||||
static bool AnyParentInventoryDisableTransfer(Item item)
|
||||
{
|
||||
if (item.ParentInventory?.Owner is not Item parentOwner) { return false; }
|
||||
return HasProblematicComponent(parentOwner) || AnyParentInventoryDisableTransfer(parentOwner);
|
||||
|
||||
static bool HasProblematicComponent(Item it)
|
||||
=> it.Components.Any(static c => c.DontTransferInventoryBetweenSubs);
|
||||
}
|
||||
}
|
||||
foreach (var (item, container) in itemsToTransfer)
|
||||
{
|
||||
@@ -1369,8 +1379,15 @@ namespace Barotrauma
|
||||
{
|
||||
// Drop the item if it's not inside another item set to be transferred.
|
||||
item.Drop(null, createNetworkEvent: false, setTransform: false);
|
||||
//dropping items sets the sub, set it back to null
|
||||
item.Submarine = null;
|
||||
foreach (var itemContainer in item.GetComponents<ItemContainer>())
|
||||
{
|
||||
itemContainer.Inventory.FindAllItems((_) => true, recursive: true).ForEach(it => it.Submarine = null);
|
||||
}
|
||||
}
|
||||
}
|
||||
System.Diagnostics.Debug.Assert(itemsToTransfer.None(it => it.item.Submarine != null), "Item that was set to be transferred was not removed from the sub!");
|
||||
currentSub.Info.NoItems = true;
|
||||
}
|
||||
// Serialize the current sub
|
||||
@@ -1408,6 +1425,7 @@ namespace Barotrauma
|
||||
{
|
||||
newContainer = newSub.FindContainerFor(item, onlyPrimary: true, checkTransferConditions: true, allowConnectedSubs: true);
|
||||
}
|
||||
string newContainerName = newContainer == null ? "(null)" : $"{newContainer.Prefab.Identifier} ({newContainer.Tags})";
|
||||
if (item.Container == null && (newContainer == null || !newContainer.OwnInventory.TryPutItem(item, user: null, createNetworkEvent: false)))
|
||||
{
|
||||
var cargoContainer = CargoManager.GetOrCreateCargoContainerFor(item.Prefab, spawnHull, ref availableContainers);
|
||||
@@ -1416,13 +1434,16 @@ namespace Barotrauma
|
||||
Vector2 simPos = ConvertUnits.ToSimUnits(CargoManager.GetCargoPos(spawnHull, item.Prefab));
|
||||
item.SetTransform(simPos, 0.0f, findNewHull: false, setPrevTransform: false);
|
||||
}
|
||||
else if (cargoContainer.Item.Submarine is Submarine containerSub)
|
||||
else
|
||||
{
|
||||
// Use the item's sub in case the sub consists of multiple linked subs.
|
||||
item.Submarine = containerSub;
|
||||
if (cargoContainer.Item.Submarine is Submarine containerSub)
|
||||
{
|
||||
// Use the item's sub in case the sub consists of multiple linked subs.
|
||||
item.Submarine = containerSub;
|
||||
}
|
||||
newContainerName = cargoContainer.Item.Prefab.Identifier.ToString();
|
||||
}
|
||||
}
|
||||
string newContainerName = newContainer == null ? "(null)" : $"{newContainer.Prefab.Identifier} ({newContainer.Tags})";
|
||||
string msg = "Item transfer log error.";
|
||||
if (oldContainer != null)
|
||||
{
|
||||
|
||||
@@ -71,6 +71,12 @@ namespace Barotrauma.Items.Components
|
||||
protected const float CorrectionDelay = 1.0f;
|
||||
protected CoroutineHandle delayedCorrectionCoroutine;
|
||||
|
||||
/// <summary>
|
||||
/// If enabled, the contents of the item are not transferred when the player transfers items between subs.
|
||||
/// Use this if this component uses item containers in a way where removing the item from the container via external means would cause problems.
|
||||
/// </summary>
|
||||
public virtual bool DontTransferInventoryBetweenSubs => false;
|
||||
|
||||
[Editable, Serialize(0.0f, IsPropertySaveable.No, description: "How long it takes to pick up the item (in seconds).")]
|
||||
public float PickingTime
|
||||
{
|
||||
|
||||
@@ -416,7 +416,7 @@ namespace Barotrauma.Items.Components
|
||||
if (requiredItem.UseCondition && suitableIngredient.ConditionPercentage - requiredItem.MinCondition * 100 > 0.0f)
|
||||
{
|
||||
suitableIngredient.Condition -= suitableIngredient.Prefab.Health * requiredItem.MinCondition;
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (suitableIngredient.OwnInventory != null)
|
||||
{
|
||||
|
||||
@@ -82,6 +82,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private List<LightComponent>? lightComponents;
|
||||
|
||||
// We don't want the seeds to be transferred to a new submarine as seeds are not supposed to leave the container after they have been planted.
|
||||
public override bool DontTransferInventoryBetweenSubs => true;
|
||||
|
||||
public Planter(Item item, ContentXElement element) : base(item, element)
|
||||
{
|
||||
canBePicked = true;
|
||||
|
||||
@@ -26,19 +26,37 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool IsActive => true;
|
||||
|
||||
// We don't want the components and wires to transfer between subs as it would cause issues.
|
||||
public override bool DontTransferInventoryBetweenSubs => true;
|
||||
|
||||
public Option<CircuitBoxConnection> FindInputOutputConnection(Identifier connectionName)
|
||||
{
|
||||
foreach (CircuitBoxInputConnection input in Inputs)
|
||||
{
|
||||
if (input.Name != connectionName) { continue; }
|
||||
|
||||
return Option.Some<CircuitBoxConnection>(input);
|
||||
}
|
||||
|
||||
foreach (CircuitBoxOutputConnection output in Outputs)
|
||||
{
|
||||
if (output.Name != connectionName) { continue; }
|
||||
return Option.Some<CircuitBoxConnection>(output);
|
||||
}
|
||||
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
public Option<CircuitBoxConnection> FindInputOutputConnection(Connection connection)
|
||||
{
|
||||
foreach (CircuitBoxInputConnection input in Inputs)
|
||||
{
|
||||
if (input.Connection != connection) { continue; }
|
||||
return Option.Some<CircuitBoxConnection>(input);
|
||||
}
|
||||
|
||||
foreach (CircuitBoxOutputConnection output in Outputs)
|
||||
{
|
||||
if (output.Connection != connection) { continue; }
|
||||
return Option.Some<CircuitBoxConnection>(output);
|
||||
}
|
||||
|
||||
@@ -338,7 +356,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
SpawnItem(this, prefab, WireContainer, wire =>
|
||||
SpawnItem(prefab, user: null, container: WireContainer, onSpawned: wire =>
|
||||
{
|
||||
AddWireDirect(wireId, prefab, Option.Some(wire), one, two);
|
||||
onItemSpawned(wire);
|
||||
@@ -359,7 +377,7 @@ namespace Barotrauma.Items.Components
|
||||
private void AddWireDirect(ushort id, ItemPrefab prefab, Option<Item> backingItem, CircuitBoxConnection one, CircuitBoxConnection two)
|
||||
=> Wires.Add(new CircuitBoxWire(this, id, backingItem, one, two, prefab));
|
||||
|
||||
private bool AddComponentInternal(ushort id, ItemPrefab prefab, ItemPrefab usedResource, Vector2 pos, Action<Item> onItemSpawned)
|
||||
private bool AddComponentInternal(ushort id, ItemPrefab prefab, ItemPrefab usedResource, Vector2 pos, Character? user, Action<Item>? onItemSpawned)
|
||||
{
|
||||
if (id is ICircuitBoxIdentifiable.NullComponentID)
|
||||
{
|
||||
@@ -373,10 +391,10 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
SpawnItem(this, prefab, ComponentContainer, spawnedItem =>
|
||||
SpawnItem(prefab, user, ComponentContainer, spawnedItem =>
|
||||
{
|
||||
Components.Add(new CircuitBoxComponent(id, spawnedItem, pos, this, usedResource));
|
||||
onItemSpawned(spawnedItem);
|
||||
onItemSpawned?.Invoke(spawnedItem);
|
||||
});
|
||||
|
||||
OnViewUpdateProjSpecific();
|
||||
@@ -646,7 +664,7 @@ namespace Barotrauma.Items.Components
|
||||
return Option.None;
|
||||
}
|
||||
|
||||
public static void SpawnItem(CircuitBox circuitBox, ItemPrefab prefab, ItemContainer? container, Action<Item> onSpawned)
|
||||
public static void SpawnItem(ItemPrefab prefab, Character? user, ItemContainer? container, Action<Item> onSpawned)
|
||||
{
|
||||
if (container is null)
|
||||
{
|
||||
@@ -655,13 +673,27 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (IsInGame())
|
||||
{
|
||||
Entity.Spawner?.AddItemToSpawnQueue(prefab, container.Inventory, onSpawned: onSpawned);
|
||||
Entity.Spawner?.AddItemToSpawnQueue(prefab, container.Inventory, onSpawned: it =>
|
||||
{
|
||||
AssignWifiComponentTeam(it, user);
|
||||
onSpawned(it);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Item forceSpawnedItem = new Item(prefab, Vector2.Zero, null);
|
||||
container.Inventory.TryPutItem(forceSpawnedItem, null);
|
||||
onSpawned(forceSpawnedItem);
|
||||
AssignWifiComponentTeam(forceSpawnedItem, user);
|
||||
|
||||
static void AssignWifiComponentTeam(Item item, Character? user)
|
||||
{
|
||||
if (user == null) { return; }
|
||||
foreach (WifiComponent wifiComponent in item.GetComponents<WifiComponent>())
|
||||
{
|
||||
wifiComponent.TeamID = user.TeamID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveItem(Item item)
|
||||
|
||||
@@ -98,11 +98,11 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <param name="maxStackSize">Defaults to <see cref="ItemPrefab.MaxStackSize"/> if null</param>
|
||||
public int HowManyCanBePut(ItemPrefab itemPrefab, int? maxStackSize = null, float? condition = null)
|
||||
public int HowManyCanBePut(ItemPrefab itemPrefab, int? maxStackSize = null, float? condition = null, bool ignoreItemsInSlot = false)
|
||||
{
|
||||
if (itemPrefab == null) { return 0; }
|
||||
maxStackSize ??= itemPrefab.GetMaxStackSize(inventory);
|
||||
if (items.Count > 0)
|
||||
if (items.Count > 0 && !ignoreItemsInSlot)
|
||||
{
|
||||
if (condition.HasValue)
|
||||
{
|
||||
@@ -517,10 +517,10 @@ namespace Barotrauma
|
||||
return count;
|
||||
}
|
||||
|
||||
public virtual int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition)
|
||||
public virtual int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition, bool ignoreItemsInSlot = false)
|
||||
{
|
||||
if (i < 0 || i >= slots.Length) { return 0; }
|
||||
return slots[i].HowManyCanBePut(itemPrefab, condition: condition);
|
||||
return slots[i].HowManyCanBePut(itemPrefab, condition: condition, ignoreItemsInSlot: ignoreItemsInSlot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -862,7 +862,25 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
return ownInventory?.AllItems ?? Enumerable.Empty<Item>();
|
||||
if (OwnInventories.Length < 2)
|
||||
{
|
||||
if (OwnInventory == null) { yield break; }
|
||||
|
||||
foreach (var item in OwnInventory.AllItems)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var inventory in OwnInventories)
|
||||
{
|
||||
foreach (var item in inventory.AllItems)
|
||||
{
|
||||
yield return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -871,6 +889,8 @@ namespace Barotrauma
|
||||
get { return ownInventory; }
|
||||
}
|
||||
|
||||
public readonly ImmutableArray<ItemInventory> OwnInventories = ImmutableArray<ItemInventory>.Empty;
|
||||
|
||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description:
|
||||
"Enable if you want to display the item HUD side by side with another item's HUD, when linked together. " +
|
||||
"Disclaimer: It's possible or even likely that the views block each other, if they were not designed to be viewed together!")]
|
||||
@@ -1192,6 +1212,8 @@ namespace Barotrauma
|
||||
ownInventory = itemContainer.Inventory;
|
||||
}
|
||||
|
||||
OwnInventories = GetComponents<ItemContainer>().Select(ic => ic.Inventory).ToImmutableArray();
|
||||
|
||||
qualityComponent = GetComponent<Quality>();
|
||||
|
||||
IsLadder = GetComponent<Ladder>() != null;
|
||||
@@ -2533,8 +2555,7 @@ namespace Barotrauma
|
||||
foreach (Connection c in connectionPanel.Connections)
|
||||
{
|
||||
if (connectionFilter != null && !connectionFilter.Invoke(c)) { continue; }
|
||||
var recipients = c.Recipients;
|
||||
foreach (Connection recipient in recipients)
|
||||
foreach (Connection recipient in c.Recipients)
|
||||
{
|
||||
var component = recipient.Item.GetComponent<T>();
|
||||
if (component != null && !connectedComponents.Contains(component))
|
||||
@@ -2587,9 +2608,20 @@ namespace Barotrauma
|
||||
private void GetConnectedComponentsRecursive<T>(Connection c, HashSet<Connection> alreadySearched, List<T> connectedComponents, bool ignoreInactiveRelays, bool allowTraversingBackwards = true) where T : ItemComponent
|
||||
{
|
||||
alreadySearched.Add(c);
|
||||
|
||||
var recipients = c.Recipients;
|
||||
foreach (Connection recipient in recipients)
|
||||
static IEnumerable<Connection> GetRecipients(Connection c)
|
||||
{
|
||||
foreach (Connection recipient in c.Recipients)
|
||||
{
|
||||
yield return recipient;
|
||||
}
|
||||
//check circuit box inputs/outputs this connection is connected to
|
||||
foreach (var circuitBoxConnection in c.CircuitBoxConnections)
|
||||
{
|
||||
yield return circuitBoxConnection.Connection;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Connection recipient in GetRecipients(c))
|
||||
{
|
||||
if (alreadySearched.Contains(recipient)) { continue; }
|
||||
var component = recipient.Item.GetComponent<T>();
|
||||
@@ -2598,23 +2630,53 @@ namespace Barotrauma
|
||||
connectedComponents.Add(component);
|
||||
}
|
||||
|
||||
//connected to a wifi component -> see which other wifi components it can communicate with
|
||||
var wifiComponent = recipient.Item.GetComponent<WifiComponent>();
|
||||
if (wifiComponent != null && wifiComponent.CanTransmit())
|
||||
var circuitBox = recipient.Item.GetComponent<CircuitBox>();
|
||||
if (circuitBox != null)
|
||||
{
|
||||
foreach (var wifiReceiver in wifiComponent.GetTransmittersInRange())
|
||||
//if this is a circuit box, check what the connection is connected to inside the box
|
||||
var potentialCbConnection = circuitBox.FindInputOutputConnection(recipient);
|
||||
if (potentialCbConnection.TryUnwrap(out var cbConnection))
|
||||
{
|
||||
var receiverConnections = wifiReceiver.Item.Connections;
|
||||
if (receiverConnections == null) { continue; }
|
||||
foreach (Connection wifiOutput in receiverConnections)
|
||||
if (cbConnection is CircuitBoxInputConnection inputConnection)
|
||||
{
|
||||
if ((wifiOutput.IsOutput == recipient.IsOutput) || alreadySearched.Contains(wifiOutput)) { continue; }
|
||||
GetConnectedComponentsRecursive(wifiOutput, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
|
||||
foreach (var connectedTo in inputConnection.ExternallyConnectedTo)
|
||||
{
|
||||
if (alreadySearched.Contains(connectedTo.Connection)) { continue; }
|
||||
CheckRecipient(connectedTo.Connection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var connectedFrom in cbConnection.ExternallyConnectedFrom)
|
||||
{
|
||||
if (alreadySearched.Contains(connectedFrom.Connection) || !allowTraversingBackwards) { continue; }
|
||||
CheckRecipient(connectedFrom.Connection);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CheckRecipient(recipient);
|
||||
|
||||
recipient.Item.GetConnectedComponentsRecursive(recipient, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
|
||||
void CheckRecipient(Connection recipient)
|
||||
{
|
||||
//connected to a wifi component -> see which other wifi components it can communicate with
|
||||
var wifiComponent = recipient.Item.GetComponent<WifiComponent>();
|
||||
if (wifiComponent != null && wifiComponent.CanTransmit())
|
||||
{
|
||||
foreach (var wifiReceiver in wifiComponent.GetTransmittersInRange())
|
||||
{
|
||||
var receiverConnections = wifiReceiver.Item.Connections;
|
||||
if (receiverConnections == null) { continue; }
|
||||
foreach (Connection wifiOutput in receiverConnections)
|
||||
{
|
||||
if ((wifiOutput.IsOutput == recipient.IsOutput) || alreadySearched.Contains(wifiOutput)) { continue; }
|
||||
GetConnectedComponentsRecursive(wifiOutput, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recipient.Item.GetConnectedComponentsRecursive(recipient, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
|
||||
}
|
||||
}
|
||||
|
||||
if (ignoreInactiveRelays)
|
||||
|
||||
@@ -58,12 +58,12 @@ namespace Barotrauma
|
||||
return itemPrefab != null && slots[i].CanBePut(itemPrefab, condition, quality) && slots[i].Items.Count < container.GetMaxStackSize(i);
|
||||
}
|
||||
|
||||
public override int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition)
|
||||
public override int HowManyCanBePut(ItemPrefab itemPrefab, int i, float? condition, bool ignoreItemsInSlot = false)
|
||||
{
|
||||
if (itemPrefab == null) { return 0; }
|
||||
if (i < 0 || i >= slots.Length) { return 0; }
|
||||
if (!container.CanBeContained(itemPrefab, i)) { return 0; }
|
||||
return slots[i].HowManyCanBePut(itemPrefab, maxStackSize: Math.Min(itemPrefab.GetMaxStackSize(this), container.GetMaxStackSize(i)), condition);
|
||||
return slots[i].HowManyCanBePut(itemPrefab, maxStackSize: Math.Min(itemPrefab.GetMaxStackSize(this), container.GetMaxStackSize(i)), condition, ignoreItemsInSlot);
|
||||
}
|
||||
|
||||
public override bool IsFull(bool takeStacksIntoAccount = false)
|
||||
|
||||
@@ -843,7 +843,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private int maxStackSizeCharacterInventory;
|
||||
[Serialize(-1, IsPropertySaveable.No)]
|
||||
[Serialize(-1, IsPropertySaveable.No, description: "Maximum stack size when the item is in a character inventory.")]
|
||||
public int MaxStackSizeCharacterInventory
|
||||
{
|
||||
get { return maxStackSizeCharacterInventory; }
|
||||
@@ -851,7 +851,9 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
private int maxStackSizeHoldableOrWearableInventory;
|
||||
[Serialize(-1, IsPropertySaveable.No)]
|
||||
[Serialize(-1, IsPropertySaveable.No, description:
|
||||
"Maximum stack size when the item is inside a holdable or wearable item. "+
|
||||
"If not set, defaults to MaxStackSizeCharacterInventory.")]
|
||||
public int MaxStackSizeHoldableOrWearableInventory
|
||||
{
|
||||
get { return maxStackSizeHoldableOrWearableInventory; }
|
||||
@@ -864,15 +866,20 @@ namespace Barotrauma
|
||||
{
|
||||
return maxStackSizeCharacterInventory;
|
||||
}
|
||||
else if (maxStackSizeHoldableOrWearableInventory > 0 &&
|
||||
inventory?.Owner is Item item && (item.GetComponent<Holdable>() != null || item.GetComponent<Wearable>() != null))
|
||||
else if (inventory?.Owner is Item item &&
|
||||
(item.GetComponent<Holdable>() is { Attachable: false } || item.GetComponent<Wearable>() != null))
|
||||
{
|
||||
return maxStackSizeHoldableOrWearableInventory;
|
||||
}
|
||||
else
|
||||
{
|
||||
return maxStackSize;
|
||||
if (maxStackSizeHoldableOrWearableInventory > 0)
|
||||
{
|
||||
return maxStackSizeHoldableOrWearableInventory;
|
||||
}
|
||||
else if (maxStackSizeCharacterInventory > 0)
|
||||
{
|
||||
//if maxStackSizeHoldableOrWearableInventory is not set, it defaults to maxStackSizeCharacterInventory
|
||||
return maxStackSizeCharacterInventory;
|
||||
}
|
||||
}
|
||||
return maxStackSize;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
@@ -880,7 +887,7 @@ namespace Barotrauma
|
||||
|
||||
public ImmutableHashSet<Identifier> AllowDroppingOnSwapWith { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
[Serialize(false, IsPropertySaveable.No, "If enabled, the item is not transferred when the player transfers items between subs.")]
|
||||
public bool DontTransferBetweenSubs { get; private set; }
|
||||
|
||||
[Serialize(true, IsPropertySaveable.No)]
|
||||
@@ -1032,6 +1039,7 @@ namespace Barotrauma
|
||||
var levelCommonness = new Dictionary<Identifier, CommonnessInfo>();
|
||||
var levelQuantity = new Dictionary<Identifier, FixedQuantityResourceInfo>();
|
||||
|
||||
List<FabricationRecipe> loadedRecipes = new List<FabricationRecipe>();
|
||||
foreach (ContentXElement subElement in ConfigElement.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -1114,9 +1122,10 @@ namespace Barotrauma
|
||||
var newRecipe = new FabricationRecipe(subElement, Identifier);
|
||||
if (fabricationRecipes.TryGetValue(newRecipe.RecipeHash, out var prevRecipe))
|
||||
{
|
||||
int prevRecipeIndex = loadedRecipes.IndexOf(prevRecipe);
|
||||
DebugConsole.ThrowError(
|
||||
$"Error in item prefab \"{ToString()}\": " +
|
||||
$"{prevRecipe.TargetItemPrefabIdentifier} has the same hash as {newRecipe.TargetItemPrefabIdentifier}. " +
|
||||
$"Fabrication recipe #{loadedRecipes.Count + 1} has the same hash as recipe #{prevRecipeIndex + 1}. This is most likely caused by identical, duplicate recipes." +
|
||||
$"This will cause issues with fabrication."
|
||||
);
|
||||
}
|
||||
@@ -1124,6 +1133,7 @@ namespace Barotrauma
|
||||
{
|
||||
fabricationRecipes.Add(newRecipe.RecipeHash, newRecipe);
|
||||
}
|
||||
loadedRecipes.Add(newRecipe);
|
||||
break;
|
||||
case "preferredcontainer":
|
||||
var preferredContainer = new PreferredContainer(subElement);
|
||||
|
||||
@@ -130,10 +130,17 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// When set to true, the explosion don't deal less damage when the target is behind a solid object.
|
||||
/// </summary>
|
||||
public bool IgnoreCover
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public bool IgnoreCover { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Does the damage from the explosion decrease with distance from the origin of the explosion?
|
||||
/// </summary>
|
||||
public bool DistanceFalloff { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Structures that don't count as "cover" that reduces damage from the explosion. Only relevant if IgnoreCover is set to false.
|
||||
/// </summary>
|
||||
public IEnumerable<Structure> IgnoredCover;
|
||||
|
||||
/// <summary>
|
||||
/// How long the light source created by the explosion lasts.
|
||||
@@ -311,12 +318,15 @@ namespace Barotrauma
|
||||
|
||||
if (!MathUtils.NearlyEqual(Attack.GetStructureDamage(1.0f), 0.0f) || !MathUtils.NearlyEqual(Attack.GetLevelWallDamage(1.0f), 0.0f))
|
||||
{
|
||||
RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker, IgnoredSubmarines, Attack.EmitStructureDamageParticles);
|
||||
RangedStructureDamage(worldPosition, displayRange, Attack.GetStructureDamage(1.0f), Attack.GetLevelWallDamage(1.0f), attacker,
|
||||
IgnoredSubmarines,
|
||||
Attack.EmitStructureDamageParticles,
|
||||
DistanceFalloff);
|
||||
}
|
||||
|
||||
if (BallastFloraDamage > 0.0f)
|
||||
{
|
||||
RangedBallastFloraDamage(worldPosition, displayRange, BallastFloraDamage, attacker);
|
||||
RangedBallastFloraDamage(worldPosition, displayRange, BallastFloraDamage, attacker, DistanceFalloff);
|
||||
}
|
||||
|
||||
if (EmpStrength > 0.0f)
|
||||
@@ -326,7 +336,7 @@ namespace Barotrauma
|
||||
{
|
||||
float distSqr = Vector2.DistanceSquared(item.WorldPosition, worldPosition);
|
||||
if (distSqr > displayRangeSqr) { continue; }
|
||||
float distFactor = CalculateDistanceFactor(distSqr, displayRange);
|
||||
float distFactor = DistanceFalloff ? CalculateDistanceFactor(distSqr, displayRange) : 1.0f;
|
||||
|
||||
//damage repairable power-consuming items
|
||||
var powered = item.GetComponent<Powered>();
|
||||
@@ -362,7 +372,10 @@ namespace Barotrauma
|
||||
float distSqr = Vector2.DistanceSquared(item.WorldPosition, worldPosition);
|
||||
if (distSqr > displayRangeSqr) { continue; }
|
||||
|
||||
float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange;
|
||||
float distFactor =
|
||||
DistanceFalloff ?
|
||||
1.0f - (float)Math.Sqrt(distSqr) / displayRange :
|
||||
1.0f;
|
||||
//repair repairable items
|
||||
if (item.Repairables.Any())
|
||||
{
|
||||
@@ -415,13 +428,16 @@ namespace Barotrauma
|
||||
|
||||
if (item.Prefab.DamagedByExplosions && !item.Indestructible)
|
||||
{
|
||||
float distFactor = 1.0f - dist / displayRange;
|
||||
float distFactor =
|
||||
DistanceFalloff ?
|
||||
1.0f - dist / displayRange :
|
||||
1.0f;
|
||||
float damageAmount = Attack.GetItemDamage(1.0f, item.Prefab.ExplosionDamageMultiplier);
|
||||
|
||||
Vector2 explosionPos = worldPosition;
|
||||
if (item.Submarine != null) { explosionPos -= item.Submarine.Position; }
|
||||
|
||||
damageAmount *= GetObstacleDamageMultiplier(ConvertUnits.ToSimUnits(explosionPos), worldPosition, item.SimPosition);
|
||||
damageAmount *= GetObstacleDamageMultiplier(ConvertUnits.ToSimUnits(explosionPos), worldPosition, item.SimPosition, IgnoredCover);
|
||||
item.Condition -= damageAmount * distFactor;
|
||||
}
|
||||
}
|
||||
@@ -482,12 +498,15 @@ namespace Barotrauma
|
||||
|
||||
if (dist > attack.Range) { continue; }
|
||||
|
||||
float distFactor = 1.0f - dist / attack.Range;
|
||||
float distFactor =
|
||||
DistanceFalloff ?
|
||||
1.0f - dist / attack.Range :
|
||||
1.0f;
|
||||
|
||||
//solid obstacles between the explosion and the limb reduce the effect of the explosion
|
||||
if (!IgnoreCover)
|
||||
{
|
||||
distFactor *= GetObstacleDamageMultiplier(explosionPos, worldPosition, limb.SimPosition);
|
||||
distFactor *= GetObstacleDamageMultiplier(explosionPos, worldPosition, limb.SimPosition, IgnoredCover);
|
||||
}
|
||||
if (distFactor > 0)
|
||||
{
|
||||
@@ -602,7 +621,8 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Returns a dictionary where the keys are the structures that took damage and the values are the amount of damage taken
|
||||
/// </summary>
|
||||
public static Dictionary<Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null, IEnumerable<Submarine> ignoredSubmarines = null, bool emitWallDamageParticles = true)
|
||||
public static Dictionary<Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null, IEnumerable<Submarine> ignoredSubmarines = null,
|
||||
bool emitWallDamageParticles = true, bool distanceFalloff = true)
|
||||
{
|
||||
float dist = 600.0f;
|
||||
damagedStructures.Clear();
|
||||
@@ -616,7 +636,10 @@ namespace Barotrauma
|
||||
{
|
||||
for (int i = 0; i < structure.SectionCount; i++)
|
||||
{
|
||||
float distFactor = 1.0f - (Vector2.Distance(structure.SectionPosition(i, true), worldPosition) / worldRange);
|
||||
float distFactor =
|
||||
distanceFalloff ?
|
||||
1.0f - (Vector2.Distance(structure.SectionPosition(i, true), worldPosition) / worldRange) :
|
||||
1.0f;
|
||||
if (distFactor <= 0.0f) { continue; }
|
||||
|
||||
structure.AddDamage(i, damage * distFactor, attacker, emitParticles: emitWallDamageParticles);
|
||||
@@ -680,7 +703,7 @@ namespace Barotrauma
|
||||
return damagedStructures;
|
||||
}
|
||||
|
||||
public static void RangedBallastFloraDamage(Vector2 worldPosition, float worldRange, float damage, Character attacker = null)
|
||||
public static void RangedBallastFloraDamage(Vector2 worldPosition, float worldRange, float damage, Character attacker = null, bool distanceFalloff = true)
|
||||
{
|
||||
List<BallastFloraBehavior> ballastFlorae = new List<BallastFloraBehavior>();
|
||||
|
||||
@@ -698,7 +721,10 @@ namespace Barotrauma
|
||||
float branchDist = Vector2.Distance(branchWorldPos, worldPosition);
|
||||
if (branchDist < worldRange)
|
||||
{
|
||||
float distFactor = 1.0f - (branchDist / worldRange);
|
||||
float distFactor =
|
||||
distanceFalloff ?
|
||||
1.0f - (branchDist / worldRange) :
|
||||
1.0f;
|
||||
if (distFactor <= 0.0f) { return; }
|
||||
|
||||
Vector2 explosionPos = worldPosition;
|
||||
@@ -715,7 +741,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private static float GetObstacleDamageMultiplier(Vector2 explosionSimPos, Vector2 explosionWorldPos, Vector2 targetSimPos)
|
||||
private static float GetObstacleDamageMultiplier(Vector2 explosionSimPos, Vector2 explosionWorldPos, Vector2 targetSimPos, IEnumerable<Structure> ignoredCover = null)
|
||||
{
|
||||
float damageMultiplier = 1.0f;
|
||||
var obstacles = Submarine.PickBodies(targetSimPos, explosionSimPos, collisionCategory: Physics.CollisionItem | Physics.CollisionItemBlocking | Physics.CollisionWall);
|
||||
@@ -728,6 +754,10 @@ namespace Barotrauma
|
||||
}
|
||||
else if (body.UserData is Structure structure)
|
||||
{
|
||||
if (ignoredCover != null)
|
||||
{
|
||||
if (ignoredCover.Contains(structure)) { continue; }
|
||||
}
|
||||
int sectionIndex = structure.FindSectionIndex(explosionWorldPos, world: true, clamp: true);
|
||||
if (structure.SectionBodyDisabled(sectionIndex))
|
||||
{
|
||||
|
||||
@@ -109,6 +109,8 @@ namespace Barotrauma
|
||||
|
||||
public float Size => IsHorizontal ? Rect.Height : Rect.Width;
|
||||
|
||||
public float PressureDistributionSpeed => Size / 100.0f * open;
|
||||
|
||||
private Door connectedDoor;
|
||||
public Door ConnectedDoor
|
||||
{
|
||||
@@ -427,11 +429,9 @@ namespace Barotrauma
|
||||
|
||||
if (hull1.WaterVolume <= 0.0 && hull2.WaterVolume <= 0.0) { return; }
|
||||
|
||||
float size = IsHorizontal ? rect.Height : rect.Width;
|
||||
|
||||
//a variable affecting the water flow through the gap
|
||||
//the larger the gap is, the faster the water flows
|
||||
float sizeModifier = size / 100.0f * open;
|
||||
float sizeModifier = Size / 100.0f * open;
|
||||
|
||||
//horizontal gap (such as a regular door)
|
||||
if (IsHorizontal)
|
||||
@@ -440,7 +440,7 @@ namespace Barotrauma
|
||||
float delta = 0.0f;
|
||||
|
||||
//water level is above the lower boundary of the gap
|
||||
if (Math.Max(hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1], hull2.Surface + subOffset.Y + hull2.WaveY[0]) > rect.Y - size)
|
||||
if (Math.Max(hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1], hull2.Surface + subOffset.Y + hull2.WaveY[0]) > rect.Y - Size)
|
||||
{
|
||||
int dir = (hull1.Pressure > hull2.Pressure + subOffset.Y) ? 1 : -1;
|
||||
|
||||
@@ -569,27 +569,35 @@ namespace Barotrauma
|
||||
|
||||
if (open > 0.0f)
|
||||
{
|
||||
if (hull1.WaterVolume > hull1.Volume / Hull.MaxCompress && hull2.WaterVolume > hull2.Volume / Hull.MaxCompress)
|
||||
if (hull1.WaterVolume > hull1.Volume / Hull.MaxCompress &&
|
||||
hull2.WaterVolume > hull2.Volume / Hull.MaxCompress)
|
||||
{
|
||||
//both hulls full -> distribute pressure
|
||||
float avgLethality = (hull1.LethalPressure + hull2.LethalPressure) / 2.0f;
|
||||
hull1.LethalPressure = avgLethality;
|
||||
hull2.LethalPressure = avgLethality;
|
||||
changePressure(hull1, avgLethality, PressureDistributionSpeed, deltaTime);
|
||||
changePressure(hull2, avgLethality, PressureDistributionSpeed, deltaTime);
|
||||
|
||||
static void changePressure(Hull hull, float target, float speed, float deltaTime)
|
||||
{
|
||||
float diff = target - hull.LethalPressure;
|
||||
float maxChange = Hull.PressureBuildUpSpeed * speed * deltaTime;
|
||||
hull.LethalPressure += MathHelper.Clamp(diff, -maxChange, maxChange);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
hull1.LethalPressure -= Hull.PressureDropSpeed * deltaTime;
|
||||
hull2.LethalPressure -= Hull.PressureDropSpeed * deltaTime;
|
||||
//either hull not full -> pressure drops
|
||||
hull1.LethalPressure -= Hull.PressureDropSpeed * PressureDistributionSpeed * deltaTime;
|
||||
hull2.LethalPressure -= Hull.PressureDropSpeed * PressureDistributionSpeed * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateRoomToOut(float deltaTime, Hull hull1)
|
||||
{
|
||||
float size = IsHorizontal ? rect.Height : rect.Width;
|
||||
|
||||
//a variable affecting the water flow through the gap
|
||||
//the larger the gap is, the faster the water flows
|
||||
float sizeModifier = size * open * open;
|
||||
float sizeModifier = Size * open * open;
|
||||
|
||||
float delta = 500.0f * sizeModifier * deltaTime;
|
||||
|
||||
@@ -642,7 +650,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : Hull.PressureBuildUpSpeed) * deltaTime;
|
||||
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : Hull.PressureBuildUpSpeed) * PressureDistributionSpeed * deltaTime;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -657,7 +665,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (hull1.WaterVolume >= hull1.Volume / Hull.MaxCompress)
|
||||
{
|
||||
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : Hull.PressureBuildUpSpeed) * deltaTime;
|
||||
hull1.LethalPressure += ((Submarine != null && Submarine.AtDamageDepth) ? 100.0f : Hull.PressureBuildUpSpeed) * PressureDistributionSpeed * deltaTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1016,7 +1016,11 @@ namespace Barotrauma
|
||||
|
||||
if (waterVolume < Volume)
|
||||
{
|
||||
LethalPressure -= PressureDropSpeed * deltaTime;
|
||||
//pressure drop speed is inversely proportionate to water percentage
|
||||
//= pressure drops very fast if the hull is nowhere near full
|
||||
float waterVolumeFactor = Math.Max((100.0f - WaterPercentage) / 10.0f, 1.0f);
|
||||
LethalPressure -=
|
||||
PressureDropSpeed * waterVolumeFactor * deltaTime;
|
||||
if (WaterVolume <= 0.0f)
|
||||
{
|
||||
#if CLIENT
|
||||
|
||||
@@ -365,7 +365,7 @@ namespace Barotrauma
|
||||
if (FinishedEvents.Any())
|
||||
{
|
||||
var finishedEventsElement = new XElement(nameof(FinishedEvents));
|
||||
foreach (var (set, count) in FinishedEvents.DistinctBy(f => f.Key.Identifier))
|
||||
foreach (var (set, count) in FinishedEvents)
|
||||
{
|
||||
var element = new XElement(nameof(FinishedEvents),
|
||||
new XAttribute("set", set.Identifier),
|
||||
|
||||
@@ -1245,7 +1245,7 @@ namespace Barotrauma
|
||||
|
||||
private static void CreateWallDamageExplosion(Gap gap, Character attacker)
|
||||
{
|
||||
const float explosionRange = 750.0f;
|
||||
const float explosionRange = 500.0f;
|
||||
float explosionStrength = gap.Open;
|
||||
|
||||
var linkedHull = gap.linkedTo.FirstOrDefault() as Hull;
|
||||
@@ -1264,20 +1264,22 @@ namespace Barotrauma
|
||||
|
||||
if (explosionOnBroken == null)
|
||||
{
|
||||
explosionOnBroken = new Explosion(explosionRange, force: 10.0f, damage: 0.0f, structureDamage: 0.0f, itemDamage: 0.0f);
|
||||
explosionOnBroken = new Explosion(explosionRange, force: 5.0f, damage: 0.0f, structureDamage: 0.0f, itemDamage: 0.0f);
|
||||
if (AfflictionPrefab.Prefabs.TryGet("lacerations".ToIdentifier(), out AfflictionPrefab lacerations))
|
||||
{
|
||||
explosionOnBroken.Attack.Afflictions.Add(lacerations.Instantiate(3.0f), null);
|
||||
explosionOnBroken.Attack.Afflictions.Add(lacerations.Instantiate(5.0f), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
explosionOnBroken.Attack.Afflictions.Add(AfflictionPrefab.InternalDamage.Instantiate(3.0f), null);
|
||||
explosionOnBroken.Attack.Afflictions.Add(AfflictionPrefab.InternalDamage.Instantiate(5.0f), null);
|
||||
}
|
||||
explosionOnBroken.IgnoreCover = true;
|
||||
explosionOnBroken.IgnoreCover = false;
|
||||
explosionOnBroken.OnlyInside = true;
|
||||
explosionOnBroken.DistanceFalloff = false;
|
||||
explosionOnBroken.DisableParticles();
|
||||
}
|
||||
|
||||
explosionOnBroken.IgnoredCover = gap.ConnectedWall?.ToEnumerable();
|
||||
explosionOnBroken.Attack.Range = explosionRange * gap.Open;
|
||||
explosionOnBroken.Attack.DamageMultiplier = explosionStrength;
|
||||
explosionOnBroken.Attack.Stun = MathHelper.Clamp(explosionStrength, 0.5f, 1.0f);
|
||||
|
||||
@@ -1606,28 +1606,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removeItem)
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
if (targets[i] is Item item) { Entity.Spawner?.AddItemToRemoveQueue(item); }
|
||||
}
|
||||
}
|
||||
if (removeCharacter)
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
var target = targets[i];
|
||||
if (target is Character character)
|
||||
{
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(character);
|
||||
}
|
||||
else if (target is Limb limb)
|
||||
{
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(limb.character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (breakLimb || hideLimb)
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
@@ -2292,6 +2271,30 @@ namespace Barotrauma
|
||||
|
||||
ApplyProjSpecific(deltaTime, entity, targets, hull, position, playSound: true);
|
||||
|
||||
//do this last - the entities spawned by the effect might need the entity for something, so better to remove it last
|
||||
if (removeItem)
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
if (targets[i] is Item item) { Entity.Spawner?.AddItemToRemoveQueue(item); }
|
||||
}
|
||||
}
|
||||
if (removeCharacter)
|
||||
{
|
||||
for (int i = 0; i < targets.Count; i++)
|
||||
{
|
||||
var target = targets[i];
|
||||
if (target is Character character)
|
||||
{
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(character);
|
||||
}
|
||||
else if (target is Limb limb)
|
||||
{
|
||||
Entity.Spawner?.AddEntityToRemoveQueue(limb.character);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (oneShot)
|
||||
{
|
||||
Disabled = true;
|
||||
|
||||
@@ -1,3 +1,29 @@
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
v1.1.19.3
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
- Fixed shrapnel from flak cannon ammo always launching upwards.
|
||||
- Fixed wall damage shrapnel going through walls and being a little excessive overall.
|
||||
- Fixes to pressure distribution logic: fixes pressure sometimes being lethal in breached rooms that weren't full of water.
|
||||
- Fixed clients not regaining control of their braindead character after rejoining if the character's taken any amount of damage while braindead.
|
||||
- Fixes to inconsistent stack sizes (e.g. certain ammo types stacking up to 12 in character inventories, but only 8 holdable/wearable items like backpacks and toolbelts.
|
||||
- Fixed console errors when switching subs with circuit boxes on board.
|
||||
- Fixed items that are inside a container that doesn't get transferred getting duplicated during item transfer when switching subs.
|
||||
- Fixed bots not cleaning up circuit boxes from the floor.
|
||||
- Fixed stack size being displayed incorrectly in the tooltip when dragging a stack of items to an already-occupied slot.
|
||||
- Fixed nav terminal's docking button not working if the signal is routed from the terminal to the docking port through a circuit box.
|
||||
- Fixed double-clicking a component while a circuit box is equipped making the component vanish inside the circuit box.
|
||||
- Fixed chat messages sent when accusing someone as a traitor not triggering the spam filter.
|
||||
- Fixed connection names not being translated in circuit boxes (always showed up in English).
|
||||
- Fixed right-side crew panel overlapping with the mission panel in the PvP mode round summary.
|
||||
- Fixed depleted and fulgurium fuel rods stacking up to 32 (should be 8 like all other fuel rods).
|
||||
|
||||
Modding:
|
||||
- Added "UseHumanAI" property to characters (can be used to enable the human AI on characters other than humans). While using the human AI on non-humans isn't a fully supported or tested feature, it was previously possible to do that by creating a human prefab using a different species than human, but that no longer worked as of the Treacherous Tides update.
|
||||
- Attachable holdable items don't count as "HoldableOrWearableInventories". Fixes e.g. tanks only stacking up to 1 in things like movable cabinets and shelves. Does not affect any vanilla content.
|
||||
- Fixed fabricator reducing the condition of all the available ingredients if the crafting recipe reduces condition instead of consuming the whole item.
|
||||
- Fixed TagAction continuing in some situations when it can't find targets, even if it's been configured not to with the "ContinueIfNoTargetsFound" attribute.
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
v1.1.18.1
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
@@ -336,25 +362,6 @@ Fixes:
|
||||
- Fixed crashing when you e.g. use a pet from some mod in the campaign, disable the mod and reload the save.
|
||||
- Waypoint adjustments to most submarines, outposts, wrecks, and beacons. Especially on ladders. Should take care of the remaining AI issues on ladders (the old subs in the saves don't get updated, but the fixes apply to new subs that you don't yet own. And ofc all the subs in a new game!)
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v1.0.21.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Fixes:
|
||||
- Fixed LOS effect sometimes "lagging behind" when the sub is moving fast.
|
||||
- Fixed some minor visual issues (occasional jitter/flickering) on the LOS effect.
|
||||
- Fixed some issues in the bot AI that we're causing a large performance hit particularly in situations when there's lots of bots in a sub with leaks.
|
||||
- Fixed bots abandoning their orders (such as operating a turret) if the room is unsafe (e.g. flooded).
|
||||
- Fixed an issue in character syncing that occasionally caused disconnects with the error message "Exception thrown while reading segment EntityPosition, tried to read too much data from segment".
|
||||
- Fixed wires set to be hidden in-game (e.g. invisible circuits built outside the sub) being visible on the Electrician's Goggles.
|
||||
- Fixed an issue with level resources that caused crashes with certain mods (e.g. ones that include subs with piezo crystals).
|
||||
- Fixed NPCs waiting on some outpost modules never reaching their targets, causing peculiar behavior.
|
||||
- Fixed waypoints sometimes not getting connected between outpost modules if there's a very short hallway between them. Addresses some cities missing connections between waypoints, causing AI to be unable to navigate through the modules.
|
||||
- Fixed some UI layout issues (most noticeably, ultra-wide crew list) on certain resolutions like 3440x1440.
|
||||
- Fixed campaign saves occasionally failing to load with the error "an item with the same key has already been added". Seemed to only occur when using certain mods.
|
||||
- Fixed crashing when you e.g. use a pet from some mod in the campaign, disable the mod and reload the save.
|
||||
- Waypoint adjustments to most submarines, outposts, wrecks, and beacons. Especially on ladders. Should take care of the remaining AI issues on ladders (the old subs in the saves don't get updated, but the fixes apply to new subs that you don't yet own. And ofc all the subs in a new game!)
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v1.0.20.1
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user