diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs index 8fbf5a636..e370c2eee 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs @@ -215,8 +215,10 @@ namespace Barotrauma public Inventory Inventory; public readonly Item Item; public readonly bool IsSubSlot; - public string Tooltip; - public List TooltipRichTextData; + public string Tooltip { get; private set; } + public List TooltipRichTextData { get; private set;} + + public int tooltipDisplayedCondition; public SlotReference(Inventory parentInventory, VisualSlot slot, int slotIndex, bool isSubSlot, Inventory subInventory = null) { @@ -227,13 +229,26 @@ namespace Barotrauma IsSubSlot = isSubSlot; Item = ParentInventory.GetItemAt(slotIndex); - IEnumerable itemsInSlot = null; - if (parentInventory != null && Item != null) - { - itemsInSlot = parentInventory.GetItemsAt(slotIndex); - } + RefreshTooltip(); + } - TooltipRichTextData = RichTextData.GetRichTextData(GetTooltip(Item, itemsInSlot), out Tooltip); + public bool TooltipNeedsRefresh() + { + if (Item == null) { return false; } + return (int)Item.ConditionPercentage != tooltipDisplayedCondition; + } + + public void RefreshTooltip() + { + if (Item == null) { return; } + IEnumerable itemsInSlot = null; + if (ParentInventory != null && Item != null) + { + itemsInSlot = ParentInventory.GetItemsAt(SlotIndex); + } + TooltipRichTextData = RichTextData.GetRichTextData(GetTooltip(Item, itemsInSlot), out string newTooltip); + Tooltip = newTooltip; + tooltipDisplayedCondition = (int)Item.ConditionPercentage; } private string GetTooltip(Item item, IEnumerable itemsInSlot) @@ -1370,6 +1385,10 @@ namespace Barotrauma { Rectangle slotRect = selectedSlot.Slot.Rect; slotRect.Location += selectedSlot.Slot.DrawOffset.ToPoint(); + if (selectedSlot.TooltipNeedsRefresh()) + { + selectedSlot.RefreshTooltip(); + } DrawToolTip(spriteBatch, selectedSlot.Tooltip, slotRect, selectedSlot.TooltipRichTextData); } } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs index 37f5ee59d..4cf5cde4c 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/GameClient.cs @@ -3288,9 +3288,12 @@ namespace Barotrauma.Networking if (respawnManager != null) { string respawnText = string.Empty; - float textScale = 1.0f; Color textColor = Color.White; - bool canChooseRespawn = GameMain.GameSession.GameMode is CampaignMode && Character.Controlled == null && Level.Loaded?.Type != LevelData.LevelType.Outpost; + bool canChooseRespawn = + GameMain.GameSession.GameMode is CampaignMode && + Character.Controlled == null && + Level.Loaded?.Type != LevelData.LevelType.Outpost && + (characterInfo == null || HasSpawned); if (respawnManager.CurrentState == RespawnManager.State.Waiting && respawnManager.RespawnCountdownStarted) { diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index ab3b1bf00..1803396b8 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.1300.0.9 + 0.1300.0.10 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index d6bba2a26..58f7b70c2 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.1300.0.9 + 0.1300.0.10 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index 6cadcf9de..92fb3a9d1 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.1300.0.9 + 0.1300.0.10 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index f2b90e174..83db60119 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.1300.0.9 + 0.1300.0.10 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index 4e57d6419..d3ce6feed 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.1300.0.9 + 0.1300.0.10 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs index fa62c76bf..2f4426c74 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs @@ -1333,15 +1333,23 @@ namespace Barotrauma.Networking else if (mpCampaign != null) { var availableTransition = mpCampaign.GetAvailableTransition(out _, out _); + //don't force location if we've teleported + bool forceLocation = !mpCampaign.Map.AllowDebugTeleport || mpCampaign.Map.CurrentLocation == Level.Loaded.StartLocation; switch (availableTransition) { case CampaignMode.TransitionType.ReturnToPreviousEmptyLocation: - mpCampaign.Map.SelectLocation( - mpCampaign.Map.CurrentLocation.Connections.Find(c => c.LevelData == Level.Loaded?.LevelData).OtherLocation(mpCampaign.Map.CurrentLocation)); + if (forceLocation) + { + mpCampaign.Map.SelectLocation( + mpCampaign.Map.CurrentLocation.Connections.Find(c => c.LevelData == Level.Loaded?.LevelData).OtherLocation(mpCampaign.Map.CurrentLocation)); + } mpCampaign.LoadNewLevel(); break; case CampaignMode.TransitionType.ProgressToNextEmptyLocation: - mpCampaign.Map.SetLocation(mpCampaign.Map.Locations.IndexOf(Level.Loaded.EndLocation)); + if (forceLocation) + { + mpCampaign.Map.SetLocation(mpCampaign.Map.Locations.IndexOf(Level.Loaded.EndLocation)); + } mpCampaign.LoadNewLevel(); break; case CampaignMode.TransitionType.None: diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/RespawnManager.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/RespawnManager.cs index 295f9eb90..4db85237b 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/RespawnManager.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/RespawnManager.cs @@ -76,9 +76,14 @@ namespace Barotrauma.Networking return botsToRespawn; } - private bool RespawnPending() + private bool ShouldStartRespawnCountdown() { int characterToRespawnCount = GetClientsToRespawn().Count(); + return ShouldStartRespawnCountdown(characterToRespawnCount); + } + + private bool ShouldStartRespawnCountdown(int characterToRespawnCount) + { int totalCharacterCount = GameMain.Server.ConnectedClients.Count; return (float)characterToRespawnCount >= Math.Max((float)totalCharacterCount * GameMain.Server.ServerSettings.MinRespawnRatio, 1.0f); } @@ -90,12 +95,27 @@ namespace Barotrauma.Networking RespawnShuttle.Velocity = Vector2.Zero; } - bool respawnPending = RespawnPending(); - if (respawnPending != RespawnCountdownStarted) + int clientsToRespawn = GetClientsToRespawn().Count(); + if (RespawnCountdownStarted) { - RespawnCountdownStarted = respawnPending; - RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, (int)(GameMain.Server.ServerSettings.RespawnInterval * 1000.0f)); - GameMain.Server.CreateEntityEvent(this); + if (clientsToRespawn == 0) + { + RespawnCountdownStarted = false; + GameMain.Server.CreateEntityEvent(this); + } + } + else + { + bool shouldStartCountdown = ShouldStartRespawnCountdown(clientsToRespawn); + if (shouldStartCountdown) + { + RespawnCountdownStarted = true; + if (RespawnTime < DateTime.Now) + { + RespawnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, (int)(GameMain.Server.ServerSettings.RespawnInterval * 1000.0f)); + } + GameMain.Server.CreateEntityEvent(this); + } } if (RespawnCountdownStarted && DateTime.Now > RespawnTime) @@ -198,7 +218,7 @@ namespace Barotrauma.Networking ReturnTime = DateTime.Now; ReturnCountdownStarted = true; } - else if (!RespawnPending()) + else if (!ShouldStartRespawnCountdown()) { //don't start counting down until someone else needs to respawn ReturnTime = DateTime.Now + new TimeSpan(0, 0, 0, 0, milliseconds: (int)(maxTransportTime * 1000)); @@ -340,7 +360,7 @@ namespace Barotrauma.Networking } var characterData = campaign?.GetClientCharacterData(clients[i]); - if (characterData != null && Level.Loaded?.Type != LevelData.LevelType.Outpost) + if (characterData != null && Level.Loaded?.Type != LevelData.LevelType.Outpost && characterData.HasSpawned) { var respawnPenaltyAffliction = AfflictionPrefab.List.FirstOrDefault(a => a.AfflictionType.Equals("respawnpenalty", StringComparison.OrdinalIgnoreCase)); if (respawnPenaltyAffliction != null) diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index 6f6e5c078..660058e51 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.1300.0.9 + 0.1300.0.10 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AIController.cs index 4b6234ea5..b4624d8ad 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AIController.cs @@ -267,7 +267,7 @@ namespace Barotrauma public void UnequipEmptyItems(Item parentItem, bool avoidDroppingInSea = true) => UnequipEmptyItems(Character, parentItem, avoidDroppingInSea); - public void UnequipContainedItems(Item parentItem, Func predicate, bool avoidDroppingInSea = true) => UnequipContainedItems(Character, parentItem, predicate, avoidDroppingInSea); + public void UnequipContainedItems(Item parentItem, Func predicate = null, bool avoidDroppingInSea = true) => UnequipContainedItems(Character, parentItem, predicate, avoidDroppingInSea); public static void UnequipEmptyItems(Character character, Item parentItem, bool avoidDroppingInSea = true) => UnequipContainedItems(character, parentItem, it => it.Condition <= 0, avoidDroppingInSea); @@ -275,14 +275,14 @@ namespace Barotrauma { var inventory = parentItem.OwnInventory; if (inventory == null) { return; } - if (inventory.AllItems.Any(predicate)) + if (predicate == null || inventory.AllItems.Any(predicate)) { foreach (Item containedItem in inventory.AllItemsMod) { if (containedItem == null) { continue; } - if (predicate(containedItem)) + if (predicate == null || predicate(containedItem)) { - if (character.Submarine == null && avoidDroppingInSea) + if (character.Submarine != Submarine.MainSub && avoidDroppingInSea) { // If we are outside of main sub, try to put the item in the inventory instead dropping it in the sea. if (character.Inventory.TryPutItem(containedItem, character, CharacterInventory.anySlot)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs index 4859536a1..9a53951ea 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveCombat.cs @@ -186,11 +186,14 @@ namespace Barotrauma { findSafety.Priority = 0; } - distanceTimer -= deltaTime; - if (distanceTimer < 0) + if (!character.IsOnPlayerTeam && !objectiveManager.IsCurrentObjective()) { - distanceTimer = distanceCheckInterval; - sqrDistance = Vector2.DistanceSquared(character.WorldPosition, Enemy.WorldPosition); + distanceTimer -= deltaTime; + if (distanceTimer < 0) + { + distanceTimer = distanceCheckInterval; + sqrDistance = Vector2.DistanceSquared(character.WorldPosition, Enemy.WorldPosition); + } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveContainItem.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveContainItem.cs index f330e61bd..705ad946b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveContainItem.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveContainItem.cs @@ -34,6 +34,7 @@ namespace Barotrauma public float ConditionLevel { get; set; } = 1; public bool Equip { get; set; } public bool RemoveEmpty { get; set; } = true; + public bool RemoveExisting { get; set; } public bool MoveWholeStack { get; set; } @@ -105,7 +106,11 @@ namespace Barotrauma } if (character.CanInteractWith(container.Item, checkLinked: false)) { - if (RemoveEmpty) + if (RemoveExisting) + { + HumanAIController.UnequipContainedItems(container.Item); + } + else if (RemoveEmpty) { HumanAIController.UnequipEmptyItems(container.Item); } @@ -128,7 +133,7 @@ namespace Barotrauma } else { - if (ItemToContain.ParentInventory == character.Inventory) + if (ItemToContain.ParentInventory == character.Inventory && character.Submarine == Submarine.MainSub) { ItemToContain.Drop(character); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs index 9e970389e..fd29e08db 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindDivingGear.cs @@ -60,11 +60,12 @@ namespace Barotrauma { HumanAIController.UnequipContainedItems(targetItem, it => !it.HasTag("oxygensource")); HumanAIController.UnequipEmptyItems(targetItem); - float min = character.Submarine == null ? 0.01f : MIN_OXYGEN; + // Seek oxygen that has at least 10% condition left, if we are inside a friendly sub. + // The margin helps us to survive, because we might need some oxygen before we can find more oxygen. + // When we are venturing outside of our sub, let's just suppose that we have enough oxygen with us and optimize it so that we don't keep switching off half used tanks. + float min = character.Submarine != Submarine.MainSub ? 0.01f : MIN_OXYGEN; if (targetItem.OwnInventory != null && targetItem.OwnInventory.AllItems.None(it => it != null && it.HasTag(OXYGEN_SOURCE) && it.Condition > min)) { - // No valid oxygen source loaded. - // Seek oxygen that has at least 10% condition left. TryAddSubObjective(ref getOxygen, () => { if (character.IsOnPlayerTeam) @@ -82,20 +83,22 @@ namespace Barotrauma { AllowToFindDivingGear = false, AllowDangerousPressure = true, - ConditionLevel = MIN_OXYGEN + ConditionLevel = MIN_OXYGEN, + RemoveExisting = true }; }, onAbandon: () => { getOxygen = null; int remainingTanks = ReportOxygenTankCount(); - // Try to seek any oxygen sources. + // Try to seek any oxygen sources, even if they have minimal amount of oxygen. TryAddSubObjective(ref getOxygen, () => { return new AIObjectiveContainItem(character, OXYGEN_SOURCE, targetItem.GetComponent(), objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC) { AllowToFindDivingGear = false, - AllowDangerousPressure = true + AllowDangerousPressure = true, + RemoveExisting = true }; }, onAbandon: () => diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindSafety.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindSafety.cs index 3fa283a4d..e8d0f4fb4 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindSafety.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveFindSafety.cs @@ -50,7 +50,10 @@ namespace Barotrauma } else { - if (HumanAIController.NeedsDivingGear(character.CurrentHull, out _) && !HumanAIController.HasDivingGear(character, conditionPercentage: AIObjectiveFindDivingGear.MIN_OXYGEN)) + if (HumanAIController.NeedsDivingGear(character.CurrentHull, out bool needsSuit) && + (needsSuit ? + !HumanAIController.HasDivingSuit(character, conditionPercentage: AIObjectiveFindDivingGear.MIN_OXYGEN) : + !HumanAIController.HasDivingMask(character, conditionPercentage: AIObjectiveFindDivingGear.MIN_OXYGEN))) { Priority = 100; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs index 83f66b7d2..45b90df47 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs @@ -279,9 +279,13 @@ namespace Barotrauma if (character.TeamID == CharacterTeamType.FriendlyNPC != item.SpawnedInOutpost) { continue; } } if (!CheckItem(item)) { continue; } - if (ignoredContainerIdentifiers != null && item.Container != null) + if (item.Container != null) { - if (ignoredContainerIdentifiers.Contains(item.ContainerIdentifier)) { continue; } + if (item.Container.HasTag("donttakeitems")) { continue; } + if (ignoredContainerIdentifiers != null) + { + if (ignoredContainerIdentifiers.Contains(item.ContainerIdentifier)) { continue; } + } } // Don't allow going into another sub, unless it's connected and of the same team and type. if (!character.Submarine.IsEntityFoundOnThisSub(item, includingConnectedSubs: true)) { continue; } @@ -423,7 +427,7 @@ namespace Barotrauma // TODO: Use the item name as the variable here. if (character.IsOnPlayerTeam && objectiveManager.CurrentOrder == objectiveManager.CurrentObjective) { - string msg = TextManager.Get("dialogcannotreachtarget", true); + string msg = TextManager.Get("dialogcannotfinditem", true); if (msg != null) { character.Speak(msg, identifier: "dialogcannotfinditem", minDurationBetweenSimilar: 20.0f); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs index 2f616c89d..7e2666efa 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/CharacterInventory.cs @@ -124,7 +124,7 @@ namespace Barotrauma public override bool CanBePut(Item item, int i) { return - base.CanBePut(item, i) && item.AllowedSlots.Contains(SlotTypes[i]) && + base.CanBePut(item, i) && item.AllowedSlots.Any(s => s.HasFlag(SlotTypes[i])) && (SlotTypes[i] == InvSlotType.Any || slots[i].ItemCount < 1); } @@ -206,7 +206,7 @@ namespace Barotrauma if (allowedSlots != null && !allowedSlots.Contains(InvSlotType.Any)) { int slot = FindLimbSlot(allowedSlots.First()); - if (slot > -1 && slots[slot].Items.Any(it => it != item) && slots[slot].First().Prefab.AllowDroppingOnSwap) + if (slot > -1 && slots[slot].Items.Any(it => it != item) && slots[slot].First().AllowDroppingOnSwapWith(item)) { foreach (Item existingItem in slots[slot].Items.ToList()) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs index c61efaf2c..eced05614 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Steering.cs @@ -12,11 +12,11 @@ namespace Barotrauma.Items.Components { partial class Steering : Powered, IServerSerializable, IClientSerializable { + public const float AutopilotMinDistToPathNode = 30.0f; + private const float AutopilotRayCastInterval = 0.5f; private const float RecalculatePathInterval = 5.0f; - private const float AutopilotMinDistToPathNode = 30.0f; - private const float AutoPilotSteeringLerp = 0.1f; private const float AutoPilotMaxSpeed = 0.5f; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Terminal.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Terminal.cs index ea713c3f9..fb93543ab 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Terminal.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/Terminal.cs @@ -1,5 +1,6 @@ using Barotrauma.Networking; using System.Collections.Generic; +using System.Linq; using System.Xml.Linq; namespace Barotrauma.Items.Components @@ -31,6 +32,19 @@ namespace Barotrauma.Items.Components } } + /// + /// Can be used to display messages on the terminal via status effects + /// + public string ShowMessage + { + get { return messageHistory.Count == 0 ? string.Empty : messageHistory.Last(); } + set + { + if (string.IsNullOrEmpty(value)) { return; } + ShowOnDisplay(value); + } + } + private string OutputValue { get; set; } public Terminal(Item item, XElement element) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs index 4701fafc1..6c6ebc229 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Inventory.cs @@ -659,12 +659,12 @@ namespace Barotrauma else { swapSuccessful = - (existingItems.All(existingItem => otherInventory.TryPutItem(existingItem, otherIndex, false, false, user, createNetworkEvent)) || - existingItems.Count == 1 && otherInventory.TryPutItem(existingItems.First(),user, CharacterInventory.anySlot, createNetworkEvent)) + (existingItems.All(existingItem => otherInventory.TryPutItem(existingItem, otherIndex, false, false, user, createNetworkEvent)) || + existingItems.Count == 1 && otherInventory.TryPutItem(existingItems.First(), user, CharacterInventory.anySlot, createNetworkEvent)) && stackedItems.Distinct().All(stackedItem => TryPutItem(stackedItem, index, false, false, user, createNetworkEvent)); - if (!swapSuccessful && existingItems.Count == 1 && existingItems[0].Prefab.AllowDroppingOnSwap) + if (!swapSuccessful && existingItems.Count == 1 && existingItems[0].AllowDroppingOnSwapWith(item)) { existingItems[0].Drop(user, createNetworkEvent); swapSuccessful = stackedItems.Distinct().Any(stackedItem => TryPutItem(stackedItem, index, false, false, user, createNetworkEvent)); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs index b0b666faf..75a55da54 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Item.cs @@ -1102,6 +1102,27 @@ namespace Barotrauma if (findNewHull) { FindHull(); } } + /// + /// Is dropping the item allowed when trying to swap it with the other item + /// + public bool AllowDroppingOnSwapWith(Item otherItem) + { + if (!Prefab.AllowDroppingOnSwap || otherItem == null) { return false; } + if (Prefab.AllowDroppingOnSwapWith.Any()) + { + foreach (string tagOrIdentifier in Prefab.AllowDroppingOnSwapWith) + { + if (otherItem.prefab.Identifier.Equals(tagOrIdentifier, StringComparison.OrdinalIgnoreCase)) { return true; } + if (otherItem.HasTag(tagOrIdentifier)) { return true; } + } + return false; + } + else + { + return true; + } + } + public void SetActiveSprite() { SetActiveSpriteProjSpecific(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs index 3717fa3e4..b89df2b70 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs @@ -517,6 +517,12 @@ namespace Barotrauma [Serialize(false, false)] public bool AllowDroppingOnSwap { get; private set; } + private readonly HashSet allowDroppingOnSwapWith = new HashSet(); + public IEnumerable AllowDroppingOnSwapWith + { + get { return allowDroppingOnSwapWith; } + } + public Vector2 Size => size; public bool CanBeBought => (DefaultPrice != null && DefaultPrice.CanBeBought) || (locationPrices != null && locationPrices.Any(p => p.Value.CanBeBought)); @@ -657,7 +663,7 @@ namespace Barotrauma { category = MapEntityCategory.Misc; } - Category = category; + Category = category; var parentType = element.Parent?.GetAttributeString("itemtype", "") ?? string.Empty; @@ -695,7 +701,7 @@ namespace Barotrauma identifier = GenerateLegacyIdentifier(originalName); } } - + if (string.Equals(parentType, "wrecked", StringComparison.OrdinalIgnoreCase)) { if (!string.IsNullOrEmpty(name)) @@ -703,7 +709,7 @@ namespace Barotrauma name = TextManager.GetWithVariable("wreckeditemformat", "[name]", name); } } - + if (string.IsNullOrEmpty(name)) { DebugConsole.ThrowError($"Unnamed item ({identifier}) in {filePath}!"); @@ -715,11 +721,11 @@ namespace Barotrauma (element.GetAttributeStringArray("aliases", null, convertToLowerInvariant: true) ?? element.GetAttributeStringArray("Aliases", new string[0], convertToLowerInvariant: true)); Aliases.Add(originalName.ToLowerInvariant()); - - Triggers = new List(); - DeconstructItems = new List(); - FabricationRecipes = new List(); - DeconstructTime = 1.0f; + + Triggers = new List(); + DeconstructItems = new List(); + FabricationRecipes = new List(); + DeconstructTime = 1.0f; if (element.Attribute("allowasextracargo") != null) { @@ -755,6 +761,16 @@ namespace Barotrauma } } + var allowDroppingOnSwapWith = element.GetAttributeStringArray("allowdroppingonswapwith", new string[0]); + if (allowDroppingOnSwapWith.Any()) + { + AllowDroppingOnSwap = true; + foreach (string tag in allowDroppingOnSwapWith) + { + this.allowDroppingOnSwapWith.Add(tag); + } + } + foreach (XElement subElement in element.Elements()) { switch (subElement.Name.ToString().ToLowerInvariant()) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs index 8da12066f..068076ada 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Gap.cs @@ -203,9 +203,9 @@ namespace Barotrauma public void AutoOrient() { Vector2 searchPosLeft = new Vector2(rect.X, rect.Y - rect.Height / 2); - Hull hullLeft = Hull.FindHullOld(searchPosLeft, null, false); + Hull hullLeft = Hull.FindHullUnoptimized(searchPosLeft, null, false); Vector2 searchPosRight = new Vector2(rect.Right, rect.Y - rect.Height / 2); - Hull hullRight = Hull.FindHullOld(searchPosRight, null, false); + Hull hullRight = Hull.FindHullUnoptimized(searchPosRight, null, false); if (hullLeft != null && hullRight != null && hullLeft != hullRight) { @@ -214,9 +214,9 @@ namespace Barotrauma } Vector2 searchPosTop = new Vector2(rect.Center.X, rect.Y); - Hull hullTop = Hull.FindHullOld(searchPosTop, null, false); + Hull hullTop = Hull.FindHullUnoptimized(searchPosTop, null, false); Vector2 searchPosBottom = new Vector2(rect.Center.X, rect.Y - rect.Height); - Hull hullBottom = Hull.FindHullOld(searchPosBottom, null, false); + Hull hullBottom = Hull.FindHullUnoptimized(searchPosBottom, null, false); if (hullTop != null && hullBottom != null && hullTop != hullBottom) { @@ -261,8 +261,8 @@ namespace Barotrauma for (int i = 0; i < 2; i++) { - hulls[i] = Hull.FindHullOld(searchPos[i], null, false); - if (hulls[i] == null) hulls[i] = Hull.FindHullOld(searchPos[i], null, false, true); + hulls[i] = Hull.FindHullUnoptimized(searchPos[i], null, false); + if (hulls[i] == null) hulls[i] = Hull.FindHullUnoptimized(searchPos[i], null, false, true); } if (hulls[0] == null && hulls[1] == null) { return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs index 8b3803f99..991acbcc4 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Hull.cs @@ -990,7 +990,13 @@ namespace Barotrauma return float.MaxValue; } - //returns the water block which contains the point (or null if it isn't inside any) + /// + /// Returns the hull which contains the point (or null if it isn't inside any) + /// + /// The position to check + /// This hull is checked first: if the current hull is known, this can be used as an optimization + /// Should world coordinates or the sub's local coordinates be used? + /// Does being exactly at the edge of the hull count as being inside? public static Hull FindHull(Vector2 position, Hull guess = null, bool useWorldCoordinates = true, bool inclusive = true) { if (EntityGrids == null) return null; @@ -1038,20 +1044,19 @@ namespace Barotrauma return null; } - //returns the water block which contains the point (or null if it isn't inside any) - public static Hull FindHullOld(Vector2 position, Hull guess = null, bool useWorldCoordinates = true, bool inclusive = true) + /// + /// Returns the hull which contains the point (or null if it isn't inside any). The difference to FindHull is that this method goes through all hulls without trying + /// to first find the sub the point is inside and checking the hulls in that sub. + /// = This is slower, use with caution in situations where the sub's extents or hulls may have changed after it was loaded. + /// + public static Hull FindHullUnoptimized(Vector2 position, Hull guess = null, bool useWorldCoordinates = true, bool inclusive = true) { - return FindHullOld(position, hullList, guess, useWorldCoordinates, inclusive); - } - - public static Hull FindHullOld(Vector2 position, List hulls, Hull guess = null, bool useWorldCoordinates = true, bool inclusive = true) - { - if (guess != null && hulls.Contains(guess)) + if (guess != null && hullList.Contains(guess)) { if (Submarine.RectContains(useWorldCoordinates ? guess.WorldRect : guess.rect, position, inclusive)) return guess; } - foreach (Hull hull in hulls) + foreach (Hull hull in hullList) { if (Submarine.RectContains(useWorldCoordinates ? hull.WorldRect : hull.rect, position, inclusive)) return hull; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs index 8f0249de4..da0727689 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/Level.cs @@ -166,7 +166,7 @@ namespace Barotrauma public class AbyssIsland { - public readonly Rectangle Area; + public Rectangle Area; public readonly List Cells; public AbyssIsland(Rectangle area, List cells) @@ -844,6 +844,11 @@ namespace Barotrauma } } + foreach (AbyssIsland island in AbyssIslands) + { + island.Area = new Rectangle(borders.Width - island.Area.Right, island.Area.Y, island.Area.Width, island.Area.Height); + } + foreach (Cave cave in Caves) { cave.Area = new Rectangle(borders.Width - cave.Area.Right, cave.Area.Y, cave.Area.Width, cave.Area.Height); @@ -1328,7 +1333,6 @@ namespace Barotrauma for (int i = 0; i < tunnel.Cells.Count; i++) { tunnel.Cells[i].CellType = CellType.Path; - var newWaypoint = new WayPoint(new Rectangle((int)tunnel.Cells[i].Site.Coord.X, (int)tunnel.Cells[i].Center.Y, 10, 10), null) { Tunnel = tunnel @@ -1385,8 +1389,23 @@ namespace Barotrauma ConvertUnits.ToSimUnits(wayPoint.WorldPosition), ConvertUnits.ToSimUnits(closestWaypoint.WorldPosition), collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) == null) { - wayPoint.linkedTo.Add(closestWaypoint); - closestWaypoint.linkedTo.Add(wayPoint); + Vector2 diff = closestWaypoint.WorldPosition - wayPoint.WorldPosition; + float dist = diff.Length(); + float step = ConvertUnits.ToDisplayUnits(Steering.AutopilotMinDistToPathNode) * 0.8f; + + WayPoint prevWaypoint = wayPoint; + for (float x = step; x < dist - step; x += step) + { + var newWaypoint = new WayPoint(wayPoint.WorldPosition + (diff / dist * x), SpawnType.Path, submarine: null) + { + Tunnel = tunnel + }; + prevWaypoint.linkedTo.Add(newWaypoint); + newWaypoint.linkedTo.Add(prevWaypoint); + prevWaypoint = newWaypoint; + } + prevWaypoint.linkedTo.Add(closestWaypoint); + closestWaypoint.linkedTo.Add(prevWaypoint); } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs index 39b06e020..bef44a09f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Outposts/OutpostGenerator.cs @@ -1104,8 +1104,35 @@ namespace Barotrauma DebugConsole.ThrowError($"Failed to connect waypoints between outpost modules. No waypoint in the {GetOpposingGapPosition(module.ThisGapPosition).ToString().ToLower()} gap of the module \"{module.PreviousModule.Info.Name}\"."); continue; } - startWaypoint.linkedTo.Add(endWaypoint); - endWaypoint.linkedTo.Add(startWaypoint); + + if (startWaypoint.WorldPosition.X > endWaypoint.WorldPosition.X) + { + var temp = startWaypoint; + startWaypoint = endWaypoint; + endWaypoint = temp; + } + + if (hallwayLength > 100 && isHorizontal) + { + WayPoint prevWayPoint = startWaypoint; + for (float x = leftHull.Rect.Right + 50; x < rightHull.Rect.X - 50; x += 100.0f) + { + var newWayPoint = new WayPoint(new Vector2(x, hullBounds.Y + 110.0f), SpawnType.Path, sub); + prevWayPoint.linkedTo.Add(newWayPoint); + newWayPoint.linkedTo.Add(prevWayPoint); + prevWayPoint = newWayPoint; + } + if (prevWayPoint != null) + { + prevWayPoint.linkedTo.Add(endWaypoint); + endWaypoint.linkedTo.Add(prevWayPoint); + } + } + else + { + startWaypoint.linkedTo.Add(endWaypoint); + endWaypoint.linkedTo.Add(startWaypoint); + } WayPoint closestWaypoint = null; float closestDistSqr = 30.0f * 30.0f; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs index 9fc32beb5..0d3bf3e32 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/WayPoint.cs @@ -786,6 +786,13 @@ namespace Barotrauma public void FindHull() { CurrentHull = Hull.FindHull(WorldPosition, CurrentHull); +#if CLIENT + //we may not be able to find the hull with the optimized method in the sub editor if new hulls have been added, use the unoptimized method + if (Screen.Selected == GameMain.SubEditorScreen) + { + CurrentHull ??= Hull.FindHullUnoptimized(WorldPosition); + } +#endif } public override void OnMapLoaded() diff --git a/Barotrauma/BarotraumaShared/Submarines/Remora.sub b/Barotrauma/BarotraumaShared/Submarines/Remora.sub index 2b5732065..c70b638d3 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Remora.sub and b/Barotrauma/BarotraumaShared/Submarines/Remora.sub differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index e366aa5b2..d84457951 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,3 +1,31 @@ +--------------------------------------------------------------------------------------------------------- +v0.1300.0.10 (unstable) +--------------------------------------------------------------------------------------------------------- + +Changes: +- Only allow dropping a diving suit when trying to swap it with another suit, not when trying to swap it with for example body armor (#5519). +- Don't show the "respawn with penalty" tickbox when spawning as a returning player. +- Made abandoned outposts a bit more common. +- Once the respawn countdown starts, it doesn't stop even if some players change their mind and decide to wait for the next round as long as there's at least one player waiting to respawn. + +Fixes: +- Fixed logbooks being empty in the wreck salvage missions. +- Addressed the lag spikes occasionally caused by Spineling's spikes when they hit/get stuck to something. +- Fixed bots throw full oxygen tanks away when they are not in the main sub (e.g. in wrecks), resulting in suffocation because they can't use the oxygen (#5525). +- Fixed bots waiting for the oxygen tanks to be entirely drained before swapping new tanks in (#5524). +- Fixed water particle effects not showing up when water flows between certain rooms in Remora. +- Fixed misaligned trigger area on Remora's smaller engine (#5514). +- Fixed players who spawn as their previous character mid-round always getting the Reaper's Tax affliction in mp campaign (#5517). +- Fixed resizeable outpost hallway modules not having waypoints and thus causing navigation issues. +- Fixed bots abandoning the combat objectives when the target is farther than 2000px away. The behavior was only intended for non-friendly npcs, like bandits or outpost security. +- Fixed items that are being held in both hands (e.g. underwater scooter) getting duffelbagged between campaign rounds (#5511). +- Fixed clients failing to select the correct connection if you teleport from an empty location to another with console commands (#5510). +- Revert the previous fix to prevent the bots to take items contained inside fabricators/deconstructors (#5431) and use "donttakeitems" tag to fix it. +- Fixed bots saying "Can't reach target [name]". +- Fixed inventory tooltip not changing when the cursor is hovered on the slot and the condition of the item changes (#5508). +- Fixed waypoints not being able to find hulls that have been added since the sub was last saved in the sub editor, causing them to appear blue (#5194). +- Fixed abyss islands not showing up on sonar in mirrored levels. + --------------------------------------------------------------------------------------------------------- v0.1300.0.9 (unstable) ---------------------------------------------------------------------------------------------------------