diff --git a/.gitignore b/.gitignore index 2ee92f352..5b6ea656d 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,9 @@ bld/ *.shproj.user *.vcxproj.user +# Rider +.idea/ + # Platform-specific webm_mem_playback files Libraries/webm_mem_playback/libvpx_x64_linux/ Libraries/webm_mem_playback/libvpx_x64_vs15/ @@ -40,7 +43,13 @@ Libraries/webm_mem_playback/opus_x64_linux/ # Win desktop.ini -#Merge script +# Merge script temp.txt docs/html +# Private assets +Barotrauma/BarotraumaShared/Content/* +.github/ISSUE_TEMPLATE/release-checklist.md + +#Rider +*.DotSettings.user diff --git a/Barotrauma/BarotraumaClient/ClientSource/Camera.cs b/Barotrauma/BarotraumaClient/ClientSource/Camera.cs index 8d7e7d4e0..f0182f806 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Camera.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Camera.cs @@ -338,15 +338,18 @@ namespace Barotrauma previousOffset = offset; } - //how much to zoom out (zoom completely out when offset is 1000) - float zoomOutAmount = GetZoomAmount(offset); - //scaled zoom amount - float scaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount) * globalZoomScale; - //zoom in further if zoomOutAmount is low and resolution is lower than reference - float newZoom = scaledZoom * (MathHelper.Lerp(0.3f * (1f - Math.Min(globalZoomScale, 1f)), 0f, - (GameMain.Config == null || GameMain.Config.EnableMouseLook) ? (float)Math.Sqrt(offsetUnscaledLen) : 0.3f) + 1f); + if (allowZoom) + { + //how much to zoom out (zoom completely out when offset is 1000) + float zoomOutAmount = GetZoomAmount(offset); + //scaled zoom amount + float scaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount) * globalZoomScale; + //zoom in further if zoomOutAmount is low and resolution is lower than reference + float newZoom = scaledZoom * (MathHelper.Lerp(0.3f * (1f - Math.Min(globalZoomScale, 1f)), 0f, + (GameMain.Config == null || GameMain.Config.EnableMouseLook) ? (float)Math.Sqrt(offsetUnscaledLen) : 0.3f) + 1f); - Zoom += (newZoom - zoom) / ZoomSmoothness; + Zoom += (newZoom - zoom) / ZoomSmoothness; + } //force targetzoom to the current zoom value, so the camera stays at the same zoom when switching to freecam targetZoom = Zoom; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs index 3ee39ef88..fb1cd7d6b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Character.cs @@ -332,7 +332,7 @@ namespace Barotrauma { cam.OffsetAmount = targetOffsetAmount = 0.0f; cursorPosition = - SelectedConstruction.Position + + Position + PlayerInput.MouseSpeed.ClampLength(10.0f); //apply a little bit of movement to the cursor pos to prevent AFK kicking } else if (!GameMain.Config.EnableMouseLook) @@ -1096,6 +1096,7 @@ namespace Barotrauma private SoundChannel soundChannel; public void PlaySound(CharacterSound.SoundType soundType, float soundIntervalFactor = 1.0f, float maxInterval = 0) { + if (Removed) { return; } if (sounds == null || sounds.Count == 0) { return; } if (soundChannel != null && soundChannel.IsPlaying) { return; } if (GameMain.SoundManager?.Disabled ?? true) { return; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs index 5897105f1..1c618e46e 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/CharacterNetworking.cs @@ -452,11 +452,17 @@ namespace Barotrauma ushort itemID = msg.ReadUInt16(); if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; } item.AllowStealing = true; - var wifiComponent = item.GetComponent(); + var wifiComponent = item.GetComponent(); if (wifiComponent != null) { wifiComponent.TeamID = teamID; } + var idCard = item.GetComponent(); + if (idCard != null) + { + idCard.TeamID = teamID; + idCard.SubmarineSpecificID = 0; + } } break; case 10: //NetEntityEvent.Type.UpdateExperience diff --git a/Barotrauma/BarotraumaClient/ClientSource/Characters/Jobs/JobPrefab.cs b/Barotrauma/BarotraumaClient/ClientSource/Characters/Jobs/JobPrefab.cs index f4ceb3ce9..248bbe71d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Characters/Jobs/JobPrefab.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Characters/Jobs/JobPrefab.cs @@ -83,7 +83,13 @@ namespace Barotrauma var equipIdentifiers = Element.GetChildElements("ItemSet").Elements().Where(e => e.GetAttributeBool("outfit", false)).Select(e => e.GetAttributeString("identifier", "")); - var outfitPrefabs = ItemPrefab.Prefabs.Where(itemPrefab => equipIdentifiers.Contains(itemPrefab.Identifier)).ToList(); + List outfitPrefabs = new List(); + foreach (var equipIdentifier in equipIdentifiers) + { + var itemPrefab = ItemPrefab.Prefabs.Find(ip => ip.Identifier == equipIdentifier); + if (itemPrefab != null) { outfitPrefabs.Add(itemPrefab); } + } + if (!outfitPrefabs.Any()) { return null; } for (int i = 0; i < outfitPrefabs.Count; i++) diff --git a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs index 2a010e707..e58a115fc 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GUI/GUIListBox.cs @@ -256,6 +256,15 @@ namespace Barotrauma /// public bool CanInteractWhenUnfocusable { get; set; } = false; + public override Rectangle MouseRect + { + get + { + if (!CanBeFocused && !CanInteractWhenUnfocusable) { return Rectangle.Empty; } + return ClampMouseRectToParent ? ClampRect(Rect) : Rect; + } + } + /// For horizontal listbox, default side is on the bottom. For vertical, it's on the right. public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT) { @@ -770,8 +779,13 @@ namespace Barotrauma BarScroll += speed * Math.Sign(diff) / TotalSize; } } - - if (PlayerInput.ScrollWheelSpeed != 0 && AllowMouseWheelScroll && (FindScrollableParentListBox(GUI.MouseOn) == this || GUI.IsMouseOn(ScrollBar))) + + bool IsMouseOn() => + FindScrollableParentListBox(GUI.MouseOn) == this || + GUI.IsMouseOn(ScrollBar) || + (CanInteractWhenUnfocusable && Content.Rect.Contains(PlayerInput.MousePosition)); + + if (PlayerInput.ScrollWheelSpeed != 0 && AllowMouseWheelScroll && IsMouseOn()) { if (SmoothScroll) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs index c51a4a68d..3aa3f47ea 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/GameSession/GameModes/SinglePlayerCampaign.cs @@ -463,7 +463,6 @@ namespace Barotrauma { SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo; GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch; - PendingSubmarineSwitch = null; for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++) { @@ -476,6 +475,7 @@ namespace Barotrauma } SaveUtil.SaveGame(GameMain.GameSession.SavePath); + PendingSubmarineSwitch = null; } else { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs index 2e3782d15..9257d4209 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/CharacterInventory.cs @@ -516,6 +516,9 @@ namespace Barotrauma } } + private readonly static List hideSubInventories = new List(); + private readonly static List tempHighlightedSubInventorySlots = new List(); + public override void Update(float deltaTime, Camera cam, bool isSubInventory = false) { if (!AccessibleWhenAlive && !character.IsDead) @@ -579,15 +582,17 @@ namespace Barotrauma } } } - - List hideSubInventories = new List(); + + hideSubInventories.Clear(); //remove highlighted subinventory slots that can no longer be accessed highlightedSubInventorySlots.RemoveWhere(s => s.ParentInventory == this && ((s.SlotIndex < 0 || s.SlotIndex >= slots.Length || slots[s.SlotIndex] == null) || (Character.Controlled != null && !Character.Controlled.CanAccessInventory(s.Inventory)))); //remove highlighted subinventory slots that refer to items no longer in this inventory highlightedSubInventorySlots.RemoveWhere(s => s.Item != null && s.ParentInventory == this && s.Item.ParentInventory != this); - foreach (var highlightedSubInventorySlot in highlightedSubInventorySlots) + tempHighlightedSubInventorySlots.Clear(); + tempHighlightedSubInventorySlots.AddRange(highlightedSubInventorySlots); + foreach (var highlightedSubInventorySlot in tempHighlightedSubInventorySlots) { if (highlightedSubInventorySlot.ParentInventory == this) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs index 53c770df7..7da3659a6 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/Fabricator.cs @@ -300,6 +300,8 @@ namespace Barotrauma.Items.Components { requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform)); } + + HideEmptyItemListCategories(); } private void DrawInputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent) @@ -479,6 +481,13 @@ namespace Barotrauma.Items.Components child.Visible = recipe.DisplayName.ToLower().Contains(filter); } + HideEmptyItemListCategories(); + + return true; + } + + private void HideEmptyItemListCategories() + { //go through the elements backwards, and disable the labels ("insufficient skills to fabricate", "recipe required...") if there's no items below them bool recipeVisible = false; foreach (GUIComponent child in itemList.Content.Children.Reverse()) @@ -490,14 +499,12 @@ namespace Barotrauma.Items.Components } else { - recipeVisible = child.Visible; + recipeVisible |= child.Visible; } } itemList.UpdateScrollBarSize(); itemList.BarScroll = 0.0f; - - return true; } public bool ClearFilter() diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/MiniMap.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/MiniMap.cs index 890b2910a..5e0ff3193 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/MiniMap.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Machines/MiniMap.cs @@ -36,7 +36,7 @@ namespace Barotrauma.Items.Components internal readonly struct MiniMapSprite { - public readonly Sprite Sprite; + public readonly Sprite? Sprite; public readonly Color Color; public MiniMapSprite(JobPrefab prefab) @@ -1302,6 +1302,8 @@ namespace Barotrauma.Items.Components int i = 0; foreach (MiniMapSprite info in cardsToDraw) { + if (info.Sprite is null) { continue; } + float spriteSize = info.Sprite.size.X * (parentWidth / info.Sprite.size.X) + padding; if (totalWidth + spriteSize > frame.Rect.Width) { break; } @@ -1318,7 +1320,8 @@ namespace Barotrauma.Items.Components foreach (MiniMapSprite info in cardsToDraw) { - Sprite sprite = info.Sprite; + Sprite? sprite = info.Sprite; + if (sprite is null) { continue; } float scale = parentWidth / sprite.size.X; float spriteSize = sprite.size.X * scale; float posX = adjustedCenterX + offset; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Rope.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Rope.cs index 25d45c773..436e99507 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Rope.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Components/Rope.cs @@ -88,7 +88,7 @@ namespace Barotrauma.Items.Components Vector2 startPos = GetSourcePos(); startPos.Y = -startPos.Y; - if (source is Item sourceItem) + if (source is Item sourceItem && !sourceItem.Removed) { var turret = sourceItem.GetComponent(); var weapon = sourceItem.GetComponent(); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs index a8d965902..2b50d7e4a 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Inventory.cs @@ -540,7 +540,7 @@ namespace Barotrauma } }*/ - bool mouseOn = interactRect.Contains(PlayerInput.MousePosition) && !Locked && !mouseOnGUI && !slot.Disabled; + bool mouseOn = interactRect.Contains(PlayerInput.MousePosition) && !Locked && !mouseOnGUI && !slot.Disabled && IsMouseOnInventory; // Delete item from container in sub editor if (SubEditorScreen.IsSubEditor() && PlayerInput.IsCtrlDown()) @@ -863,6 +863,8 @@ namespace Barotrauma { return false; } + if (GameSession.IsTabMenuOpen) { return false; } + if (CrewManager.IsCommandInterfaceOpen) { return false; } if (Character.Controlled == null) { return false; } @@ -1313,6 +1315,10 @@ namespace Barotrauma private static bool CanSelectSlot(SlotReference selectedSlot) { + if (!IsMouseOnInventory) + { + return false; + } if (!selectedSlot.Slot.MouseOn()) { return false; @@ -1335,7 +1341,8 @@ namespace Barotrauma if ((parentItem?.GetRootInventoryOwner() is Character ownerCharacter) && ownerCharacter == Character.Controlled && CharacterHealth.OpenHealthWindow?.Character != ownerCharacter && - ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface)) + ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface) && + Screen.Selected != GameMain.SubEditorScreen) { highlightedSubInventorySlots.RemoveWhere(s => s.Item == parentItem); return false; diff --git a/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs b/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs index 900731c83..eab3b9777 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Items/Item.cs @@ -543,6 +543,7 @@ namespace Barotrauma partial void OnCollisionProjSpecific(float impact) { if (impact > 1.0f && + Container == null && !string.IsNullOrEmpty(Prefab.ImpactSoundTag) && Timing.TotalTime > LastImpactSoundTime + ImpactSoundInterval) { diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Explosion.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Explosion.cs index 57d7b3e44..070748dfb 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Explosion.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Explosion.cs @@ -98,12 +98,10 @@ namespace Barotrauma private IEnumerable DimLight(LightSource light) { float currBrightness = 1.0f; - float startRange = light.Range; - while (light.Color.A > 0.0f && flashDuration > 0.0f) { - light.Color = new Color(light.Color.R, light.Color.G, light.Color.B, currBrightness); - currBrightness -= (1.0f / flashDuration) * CoroutineManager.DeltaTime; + light.Color = new Color(light.Color.R, light.Color.G, light.Color.B, (byte)(currBrightness * 255)); + currBrightness -= 1.0f / flashDuration * CoroutineManager.DeltaTime; yield return CoroutineStatus.Running; } diff --git a/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs b/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs index 8ad1a0596..f178bf7f5 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Map/Lights/LightManager.cs @@ -529,7 +529,7 @@ namespace Barotrauma.Lights graphics.Clear(Color.Black); Vector2 diff = lookAtPosition - ViewTarget.WorldPosition; diff.Y = -diff.Y; - if (diff.LengthSquared() > 30.0f) { losOffset = diff; } + if (diff.LengthSquared() > 20.0f * 20.0f) { losOffset = diff; } float rotation = MathUtils.VectorToAngle(losOffset); Vector2 scale = new Vector2( diff --git a/Barotrauma/BarotraumaClient/ClientSource/Networking/ChatMessage.cs b/Barotrauma/BarotraumaClient/ClientSource/Networking/ChatMessage.cs index 67c758aa6..d76d85470 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Networking/ChatMessage.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Networking/ChatMessage.cs @@ -64,7 +64,18 @@ namespace Barotrauma.Networking string orderOption = orderMessageInfo.OrderOption; orderOption ??= orderMessageInfo.OrderOptionIndex.HasValue && orderMessageInfo.OrderOptionIndex >= 0 && orderMessageInfo.OrderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value] : ""; - txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, + string targetRoom; + + if (orderMessageInfo.TargetEntity is Hull targetHull) + { + targetRoom = targetHull.DisplayName; + } + else + { + targetRoom = senderCharacter?.CurrentHull?.DisplayName; + } + + txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, targetRoom, givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter, orderOption: orderOption, priority: orderMessageInfo.Priority); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Program.cs b/Barotrauma/BarotraumaClient/ClientSource/Program.cs index b8beb4aa6..23a9d554d 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Program.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Program.cs @@ -1,6 +1,7 @@ #region Using Statements using System; +using System.Collections.Generic; using Barotrauma.IO; using System.Linq; using System.Text; @@ -132,8 +133,11 @@ namespace Barotrauma { XElement newElement = new XElement(doc.Root.Name); newElement.Add(doc.Root.Attributes()); - newElement.Add(doc.Root.Elements().Where(e => !e.Name.LocalName.Equals("contentpackage", StringComparison.InvariantCultureIgnoreCase))); - newElement.Add(baseDoc.Root.Elements().Where(e => e.Name.LocalName.Equals("contentpackage", StringComparison.InvariantCultureIgnoreCase))); + string[] contentPackageTags = { "contentpackage", "contentpackages" }; + bool elementNameMatches(XElement element) + => contentPackageTags.Any(t => element.Name.LocalName.Equals(t, StringComparison.InvariantCultureIgnoreCase)); + newElement.Add(doc.Root.Elements().Where(e => !elementNameMatches(e))); + newElement.Add(baseDoc.Root.Elements().Where(e => elementNameMatches(e))); XDocument newDoc = new XDocument(newElement); newDoc.Save(GameSettings.PlayerSavePath); sb.AppendLine("To prevent further startup errors, installed mods will be disabled the next time you launch the game."); diff --git a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs index e72eb2623..76be7c17f 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Screens/SubEditorScreen.cs @@ -866,6 +866,8 @@ namespace Barotrauma return true; } + CloseItem(); + backedUpSubInfo = new SubmarineInfo(Submarine.MainSub); GameMain.GameScreen.Select(); @@ -1328,6 +1330,8 @@ namespace Barotrauma { base.Deselect(); + CloseItem(); + autoSaveLabel?.Parent?.RemoveChild(autoSaveLabel); autoSaveLabel = null; @@ -3057,9 +3061,24 @@ namespace Barotrauma new ContextMenuOption("SubEditor.PasteAssembly", isEnabled: true, () => PasteAssembly()), new ContextMenuOption("Editor.SelectSame", isEnabled: targets.Count > 0, onSelected: delegate { + bool doorGapSelected = targets.Any(t => t is Gap gap && gap.ConnectedDoor != null); foreach (MapEntity match in MapEntity.mapEntityList.Where(e => e.prefab != null && targets.Any(t => t.prefab?.Identifier == e.prefab.Identifier) && !MapEntity.SelectedList.Contains(e))) { if (MapEntity.SelectedList.Contains(match)) { continue; } + if (match is Gap gap) + { + //don't add non-door gaps if we've selected a door gap (and vice versa) + if ((gap.ConnectedDoor == null) == doorGapSelected) { continue; } + } + else if (match is Item item) + { + //add door gaps too if we're selecting doors + var door = item.GetComponent(); + if (door?.LinkedGap != null && !MapEntity.SelectedList.Contains(door.LinkedGap)) + { + MapEntity.SelectedList.Add(door.LinkedGap); + } + } MapEntity.SelectedList.Add(match); } }), @@ -4171,6 +4190,11 @@ namespace Barotrauma UpdateEntityList(); } + if (OpenedItem != null && OpenedItem.Removed) + { + OpenedItem = null; + } + if (WiringMode && dummyCharacter != null) { Wire equippedWire = diff --git a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs index 172454cc2..377de997b 100644 --- a/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs +++ b/Barotrauma/BarotraumaClient/ClientSource/Sounds/SoundPlayer.cs @@ -960,7 +960,7 @@ namespace Barotrauma targetMusic[i] = null; break; } - musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "" : "music"); + musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "default" : "music"); if (targetMusic[i].ContinueFromPreviousTime) { musicChannel[i].StreamSeekPos = targetMusic[i].PreviousTime; @@ -974,7 +974,7 @@ namespace Barotrauma if (musicChannel[i] == null || !musicChannel[i].IsPlaying) { musicChannel[i]?.Dispose(); - musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "" : "music"); + musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "default" : "music"); musicChannel[i].Looping = true; } float targetGain = targetMusic[i].Volume; diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index 1af334808..d7446d96c 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.15.12.0 + 0.15.13.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index 194cf61c6..9e8888dc5 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.15.12.0 + 0.15.13.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index 8fea287a3..f768ce3e1 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma - 0.15.12.0 + 0.15.13.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 Barotrauma diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index 425e1f28a..7c5ab18b1 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.15.12.0 + 0.15.13.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index 1dd8c8d75..246a1e1b6 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.15.12.0 + 0.15.13.0 Copyright © FakeFish 2018-2020 AnyCPU;x64 DedicatedServer diff --git a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs index 308dbf1c4..04e53169e 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/DebugConsole.cs @@ -1601,6 +1601,10 @@ namespace Barotrauma tpCharacter.Submarine = null; tpCharacter.AnimController.SetPosition(ConvertUnits.ToSimUnits(cursorWorldPos)); tpCharacter.AnimController.FindHull(cursorWorldPos, true); + if (tpCharacter.AIController?.SteeringManager is IndoorsSteeringManager pathSteering) + { + pathSteering.ResetPath(); + } } ); diff --git a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs index 7dafc70d0..d3c2fbed0 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/GameSession/GameModes/MultiPlayerCampaign.cs @@ -202,13 +202,11 @@ namespace Barotrauma public void SavePlayers() { - List prevCharacterData = new List(characterData); - //client character has spawned this round -> remove old data (and replace with an up-to-date one if the client still has a character) - characterData.RemoveAll(cd => cd.HasSpawned); - //refresh the character data of clients who are still in the server foreach (Client c in GameMain.Server.ConnectedClients) { + //ignore if the character is controlling a monster + //(we'll just use the previously saved campaign data if there's any) if (c.Character != null && c.Character.Info == null) { c.Character = null; @@ -225,25 +223,30 @@ namespace Barotrauma continue; } } - var characterInfo = c.Character?.Info ?? c.CharacterInfo; + //use the info of the character the client is currently controlling + // or the previously saved info if not (e.g. if the client has been spectating or died) + var characterInfo = c.Character?.Info ?? characterData.Find(d => d.MatchesClient(c))?.CharacterInfo; if (characterInfo == null) { continue; } - if (c.CharacterInfo.CauseOfDeath != null && characterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected) + //reduce skills if the character has died + if (characterInfo.CauseOfDeath != null && characterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected) { RespawnManager.ReduceCharacterSkills(characterInfo); } c.CharacterInfo = characterInfo; characterData.RemoveAll(cd => cd.MatchesClient(c)); - characterData.Add(new CharacterCampaignData(c)); + characterData.Add(new CharacterCampaignData(c)); } //refresh the character data of clients who aren't in the server anymore + List prevCharacterData = new List(characterData); foreach (CharacterCampaignData data in prevCharacterData) { - if (data.HasSpawned && !characterData.Any(cd => cd.IsDuplicate(data))) + if (data.HasSpawned && !GameMain.Server.ConnectedClients.Any(c => data.MatchesClient(c))) { var character = Character.CharacterList.Find(c => c.Info == data.CharacterInfo && !c.IsHusk); if (character != null && (!character.IsDead || character.CauseOfDeath?.Type == CauseOfDeathType.Disconnected)) { + characterData.RemoveAll(cd => cd.IsDuplicate(data)); data.Refresh(character); characterData.Add(data); } @@ -345,7 +348,6 @@ namespace Barotrauma { SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo; GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch; - PendingSubmarineSwitch = null; for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++) { @@ -358,6 +360,7 @@ namespace Barotrauma } SaveUtil.SaveGame(GameMain.GameSession.SavePath); + PendingSubmarineSwitch = null; } else { diff --git a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs index e6b822610..7410a9c88 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Networking/GameServer.cs @@ -205,7 +205,7 @@ namespace Barotrauma.Networking GameMain.NetLobbyScreen.Select(); GameMain.NetLobbyScreen.RandomizeSettings(); - if (!string.IsNullOrEmpty(serverSettings.SelectedSubmarine)) + if (!string.IsNullOrEmpty(serverSettings.SelectedSubmarine)) { SubmarineInfo sub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == serverSettings.SelectedSubmarine); if (sub != null) { GameMain.NetLobbyScreen.SelectedSub = sub; } @@ -542,7 +542,7 @@ namespace Barotrauma.Networking initiatedStartGame = false; } } - else if (Screen.Selected == GameMain.NetLobbyScreen && !gameStarted && !initiatedStartGame && + else if (Screen.Selected == GameMain.NetLobbyScreen && !gameStarted && !initiatedStartGame && (GameMain.NetLobbyScreen.SelectedMode != GameModePreset.MultiPlayerCampaign || GameMain.GameSession?.GameMode is MultiPlayerCampaign)) { if (serverSettings.AutoRestart) @@ -979,9 +979,9 @@ namespace Barotrauma.Networking { var spawnData = entityEvent.Data[0] as EntitySpawner.SpawnOrRemove; errorLines.Add( - entityEvent.ID + ": " + - (spawnData.Remove ? "Remove " : "Create ") + - spawnData.Entity.ToString() + + entityEvent.ID + ": " + + (spawnData.Remove ? "Remove " : "Create ") + + spawnData.Entity.ToString() + " (" + spawnData.OriginalID + ", " + spawnData.Entity.ID + ")"); } } @@ -1230,7 +1230,7 @@ namespace Barotrauma.Networking sender.WaitForNextRoundRespawn = null; } } - + private void ClientReadServerCommand(IReadMessage inc) { Client sender = ConnectedClients.Find(x => x.Connection == inc.Sender); @@ -1335,7 +1335,7 @@ namespace Barotrauma.Networking GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine); SaveUtil.SaveGame(GameMain.GameSession.SavePath); } - EndGame(); + EndGame(); } } else @@ -1466,7 +1466,7 @@ namespace Barotrauma.Networking } break; case ClientPermissions.ManageCampaign: - (GameMain.GameSession.GameMode as MultiPlayerCampaign)?.ServerRead(inc, sender); + (GameMain.GameSession.GameMode as MultiPlayerCampaign)?.ServerRead(inc, sender); break; case ClientPermissions.ConsoleCommands: { @@ -1822,7 +1822,7 @@ namespace Barotrauma.Networking outmsg.Write(client.InGame); outmsg.Write(client.Permissions != ClientPermissions.None); outmsg.Write(client.Connection == OwnerConnection); - outmsg.Write(client.Connection != OwnerConnection && + outmsg.Write(client.Connection != OwnerConnection && !client.HasPermission(ClientPermissions.Ban) && !client.HasPermission(ClientPermissions.Kick) && !client.HasPermission(ClientPermissions.Unban)); //is kicking the player allowed @@ -2202,8 +2202,8 @@ namespace Barotrauma.Networking bool isOutpost = campaign != null && campaign.NextLevel?.Type == LevelData.LevelType.Outpost; if (serverSettings.AllowRespawn && missionAllowRespawn) - { - respawnManager = new RespawnManager(this, serverSettings.UseRespawnShuttle && !isOutpost ? selectedShuttle : null); + { + respawnManager = new RespawnManager(this, serverSettings.UseRespawnShuttle && !isOutpost ? selectedShuttle : null); } if (campaign != null) { @@ -2295,7 +2295,7 @@ namespace Barotrauma.Networking } AssignBotJobs(bots, teamID); - if (campaign != null) + if (campaign != null) { foreach (CharacterInfo bot in bots) { @@ -2312,7 +2312,7 @@ namespace Barotrauma.Networking List spawnWaypoints = null; List mainSubWaypoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSubs[n]).ToList(); - if (Level.Loaded?.StartOutpost != null && + if (Level.Loaded?.StartOutpost != null && Level.Loaded.Type == LevelData.LevelType.Outpost && (Level.Loaded.StartOutpost.Info.OutpostGenerationParams?.SpawnCrewInsideOutpost ?? false) && Level.Loaded.StartOutpost.GetConnectedSubs().Any(s => s.Info.Type == SubmarineType.Player)) @@ -2824,7 +2824,7 @@ namespace Barotrauma.Networking //reset karma to a neutral value, so if/when the ban is revoked the client wont get immediately punished by low karma again previousPlayer.Karma = Math.Max(previousPlayer.Karma, 50.0f); - + if (!string.IsNullOrEmpty(previousPlayer.EndPoint) && (previousPlayer.SteamID == 0 || range)) { string ip = previousPlayer.EndPoint; @@ -3172,7 +3172,7 @@ namespace Barotrauma.Networking modifiedMessage, (ChatMessageType)type, senderCharacter, - senderClient, + senderClient, changeType); SendDirectChatMessage(chatMsg, client); @@ -3465,7 +3465,7 @@ namespace Barotrauma.Networking { newCharacter.LastNetworkUpdateID = client.Character.LastNetworkUpdateID; } - + if (newCharacter.Info != null && newCharacter.Info.Character == null) { newCharacter.Info.Character = newCharacter; @@ -3629,7 +3629,6 @@ namespace Barotrauma.Networking List availableSpawnPoints = WayPoint.WayPointList.FindAll(wp => wp.SpawnType == SpawnType.Human && wp.Submarine != null && wp.Submarine.TeamID == teamID); - List unassignedSpawnPoints = new List(availableSpawnPoints); /*bool canAssign = false; do @@ -3657,10 +3656,8 @@ namespace Barotrauma.Networking // First evaluate all the primary preferences, then all the secondary etc. for (int preferenceIndex = 0; preferenceIndex < 3; preferenceIndex++) { - if (unassignedSpawnPoints.None()) { break; } for (int i = unassigned.Count - 1; i >= 0; i--) { - if (unassignedSpawnPoints.None()) { break; } Client client = unassigned[i]; if (preferenceIndex >= client.JobPreferences.Count) { continue; } var preferredJob = client.JobPreferences[preferenceIndex]; @@ -3670,21 +3667,10 @@ namespace Barotrauma.Networking //can't assign this job if maximum number has reached or the clien't karma is too low continue; } - //give the client their preferred job if there's a spawnpoint available for that job - var matchingSpawnPoint = unassignedSpawnPoints.Find(s => s.AssignedJob == jobPrefab); - if (matchingSpawnPoint == null && !availableSpawnPoints.Any(s => s.AssignedJob == jobPrefab)) - { - //if the job is not available in any spawnpoint (custom job?), treat empty spawnpoints - //as a matching ones - matchingSpawnPoint = unassignedSpawnPoints.Find(s => s.AssignedJob == null); - } - if (matchingSpawnPoint != null) - { - unassignedSpawnPoints.Remove(matchingSpawnPoint); - client.AssignedJob = preferredJob; - assignedClientCount[jobPrefab]++; - unassigned.RemoveAt(i); - } + + client.AssignedJob = preferredJob; + assignedClientCount[jobPrefab]++; + unassigned.RemoveAt(i); } } @@ -3719,7 +3705,7 @@ namespace Barotrauma.Networking { c.AssignedJob = preferredJob; assignedClientCount[preferredJob.First]++; - break; + break; } } else //none of the client's preferred jobs available, choose a random job @@ -3776,10 +3762,10 @@ namespace Barotrauma.Networking unassignedBots[0].Job = new Job(jobPrefab, variant); assignedPlayerCount[jobPrefab]++; unassignedBots.Remove(unassignedBots[0]); - canAssign = true; + canAssign = true; } } while (unassignedBots.Count > 0 && canAssign); - + //find a suitable job for the rest of the bots foreach (CharacterInfo c in unassignedBots) { @@ -3890,7 +3876,7 @@ namespace Barotrauma.Networking string submarinesString = string.Empty; for (int i = 0; i < GameMain.NetLobbyScreen.CampaignSubmarines.Count; i++) { - submarinesString += GameMain.NetLobbyScreen.CampaignSubmarines[i].Name + ServerSettings.SubmarineSeparatorChar; + submarinesString += GameMain.NetLobbyScreen.CampaignSubmarines[i].Name + ServerSettings.SubmarineSeparatorChar; } submarinesString.Trim(ServerSettings.SubmarineSeparatorChar); serverSettings.CampaignSubmarines = submarinesString; diff --git a/Barotrauma/BarotraumaServer/ServerSource/Traitors/Goals/GoalFindItem.cs b/Barotrauma/BarotraumaServer/ServerSource/Traitors/Goals/GoalFindItem.cs index a95f200bc..06f43f91b 100644 --- a/Barotrauma/BarotraumaServer/ServerSource/Traitors/Goals/GoalFindItem.cs +++ b/Barotrauma/BarotraumaServer/ServerSource/Traitors/Goals/GoalFindItem.cs @@ -185,7 +185,7 @@ namespace Barotrauma { existingItems.Add(item); } - Entity.Spawner.AddToSpawnQueue(targetPrefab, targetContainer.OwnInventory, null, item => + Entity.Spawner.AddToSpawnQueue(targetPrefab, targetContainer.OwnInventory, onSpawned: item => { item.AddTag("traitormissionitem"); }); diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index 538fd90ef..5793a4254 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -6,7 +6,7 @@ Barotrauma FakeFish, Undertow Games Barotrauma Dedicated Server - 0.15.12.0 + 0.15.13.0 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 719417830..35ffe424e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/AIController.cs @@ -104,6 +104,7 @@ namespace Barotrauma !pathSteering.CurrentPath.Unreachable && (!requireNonDirty || !pathSteering.IsPathDirty); + public bool IsCurrentPathNullOrUnreachable => IsCurrentPathUnreachable || steeringManager is IndoorsSteeringManager pathSteering && pathSteering.CurrentPath == null; public bool IsCurrentPathUnreachable => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Unreachable; public bool IsCurrentPathFinished => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Finished; @@ -431,7 +432,7 @@ namespace Barotrauma Vector2 diff = EscapeTarget.WorldPosition - Character.WorldPosition; float sqrDist = diff.LengthSquared(); bool isClose = sqrDist < MathUtils.Pow2(100); - if (Character.CurrentHull == null || isClose && !isClosedDoor || pathSteering == null || IsCurrentPathUnreachable || IsCurrentPathFinished) + if (Character.CurrentHull == null || isClose && !isClosedDoor || pathSteering == null || IsCurrentPathNullOrUnreachable || IsCurrentPathFinished) { // Very close to the target, outside, or at the end of the path -> try to steer through the gap SteeringManager.Reset(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs index 440bf76ce..611ba59f0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/EnemyAIController.cs @@ -3659,15 +3659,15 @@ namespace Barotrauma { if (SelectedAiTarget == null) { return; } Vector2 escapeDir = Vector2.Normalize(WorldPosition - SelectedAiTarget.WorldPosition); + if (!MathUtils.IsValid(escapeDir)) + { + escapeDir = Vector2.UnitY; + } if (Character.CurrentHull != null && !Character.AnimController.InWater) { // Inside escapeDir = new Vector2(Math.Sign(escapeDir.X), 0); } - if (!MathUtils.IsValid(escapeDir)) - { - escapeDir = Vector2.UnitY; - } SteeringManager.Reset(); SteeringManager.SteeringManual(deltaTime, escapeDir); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs index eaaf5d744..1387566ac 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/HumanAIController.cs @@ -250,7 +250,17 @@ namespace Barotrauma { rayEnd += SelectedAiTarget.Entity.Submarine.SimPosition; } - UseIndoorSteeringOutside = Submarine.PickBody(SimPosition, rayEnd, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null; + IEnumerable ignoredBodies = null; + if (SelectedAiTarget.Entity is ISpatialEntity spatialTarget) + { + Submarine targetSub = spatialTarget.Submarine; + if (targetSub != null) + { + ignoredBodies = targetSub.PhysicsBody.FarseerBody.ToEnumerable(); + } + } + var obstacle = Submarine.PickBody(SimPosition, rayEnd, ignoredBodies, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall); + UseIndoorSteeringOutside = obstacle != null; } } else @@ -340,14 +350,21 @@ namespace Barotrauma IsInsideCave = Character.CurrentHull == null && Level.Loaded?.Caves.FirstOrDefault(c => c.Area.Contains(Character.WorldPosition)) is Level.Cave; } - if (UseIndoorSteeringOutside || IsInsideCave || Character.CurrentHull?.Submarine != null || hasValidPath && IsCloseEnoughToTarget(maxSteeringBuffer) || IsCloseEnoughToTarget(steeringBuffer)) + if (UseIndoorSteeringOutside || IsInsideCave || Character.CurrentHull?.Submarine != null || hasValidPath || IsCloseEnoughToTarget(steeringBuffer)) { if (steeringManager != insideSteering) { insideSteering.Reset(); steeringManager = insideSteering; } - steeringBuffer += steeringBufferIncreaseSpeed * deltaTime; + if (IsCloseEnoughToTarget(maxSteeringBuffer)) + { + steeringBuffer += steeringBufferIncreaseSpeed * deltaTime; + } + else + { + steeringBuffer = minSteeringBuffer; + } } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs index cf2635e9d..d2b1edb72 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/IndoorsSteeringManager.cs @@ -201,9 +201,9 @@ namespace Barotrauma currentTarget = target; Vector2 currentPos = host.SimPosition; pathFinder.InsideSubmarine = character.Submarine != null && !character.Submarine.Info.IsRuin; - pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && character.PressureProtection <= 0; + pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && character.PressureProtection <= 0; var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", minGapSize, startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility); - bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.X) <= 0; + bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.Combine()) <= 0; if (newPath.Unreachable || newPath.Nodes.None()) { useNewPath = false; @@ -220,10 +220,12 @@ namespace Barotrauma // Use the new path if it has significantly lower cost (don't change the path if it has marginally smaller cost. This reduces navigating backwards due to new path that is calculated from the node just behind us). float t = (float)currentPath.CurrentIndex / (currentPath.Nodes.Count - 1); useNewPath = newPath.Cost < currentPath.Cost * MathHelper.Lerp(0.95f, 0, t); - if (!useNewPath && character.Submarine != null) + if (!useNewPath && character.Submarine != null && !character.IsClimbing) { // It's possible that the current path was calculated from a start point that is no longer valid. // Therefore, let's accept also paths with a greater cost than the current, if the current node is much farther than the new start node. + // This is a special case for cases e.g. where the character falls and thus needs a new path. + // Don't do this outside or when climbing ladders, because both cause issues. useNewPath = Vector2.DistanceSquared(character.WorldPosition, currentPath.CurrentNode.WorldPosition) > Math.Pow(Vector2.Distance(character.WorldPosition, newPath.Nodes.First().WorldPosition) * 3, 2); } } @@ -310,8 +312,7 @@ namespace Barotrauma if (currentPath.Finished) { Vector2 pos2 = host.SimPosition; - if (character != null && character.Submarine == null && - CurrentPath.Nodes.Count > 0 && CurrentPath.Nodes.Last().Submarine != null) + if (character != null && character.Submarine == null && CurrentPath.Nodes.Count > 0 && CurrentPath.Nodes.Last().Submarine != null) { pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition; } @@ -323,29 +324,7 @@ namespace Barotrauma CheckDoorsInPath(); doorsChecked = true; } - Vector2 pos = host.SimPosition; - if (character != null && CurrentPath.CurrentNode != null) - { - var nodeSub = CurrentPath.CurrentNode.Submarine; - if (nodeSub != null) - { - if (character.Submarine == null) - { - // Going inside - pos -= ConvertUnits.ToSimUnits(nodeSub.Position); - } - else if (character.Submarine != nodeSub) - { - // Different subs - pos -= ConvertUnits.ToSimUnits(nodeSub.Position - character.Submarine.Position); - } - } - else if (character.Submarine != null) - { - // Going outside - pos += ConvertUnits.ToSimUnits(character.Submarine.Position); - } - } + Vector2 pos = host.WorldPosition; bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater; // Only humanoids can climb ladders bool canClimb = character.AnimController is HumanoidAnimController && !character.LockHands; @@ -384,7 +363,7 @@ namespace Barotrauma } if (character.IsClimbing && useLadders) { - Vector2 diff = currentPath.CurrentNode.SimPosition - pos; + Vector2 diff = currentPath.CurrentNode.WorldPosition - pos; bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent; if (nextLadderSameAsCurrent) { @@ -397,7 +376,7 @@ namespace Barotrauma float heightFromFloor = character.AnimController.GetColliderBottom().Y - character.AnimController.FloorY; if (heightFromFloor <= 0.0f) { - diff.Y = Math.Max(diff.Y, 1.0f); + diff.Y = Math.Max(diff.Y, 100); } // We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative. bool isAboveFloor = heightFromFloor > -0.1f; @@ -430,7 +409,7 @@ namespace Barotrauma NextNode(!doorsChecked); } } - return diff; + return ConvertUnits.ToSimUnits(diff); } else if (character.AnimController.InWater) { @@ -481,7 +460,7 @@ namespace Barotrauma { return Vector2.Zero; } - return currentPath.CurrentNode.SimPosition - pos; + return ConvertUnits.ToSimUnits(currentPath.CurrentNode.WorldPosition - pos); } private void NextNode(bool checkDoors) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs index dd98bf747..afc834674 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGetItem.cs @@ -45,6 +45,10 @@ namespace Barotrauma /// public bool AllowStealing { get; set; } public bool TakeWholeStack { get; set; } + /// + /// Are variants of the specified item allowed + /// + public bool AllowVariants { get; set; } public bool Equip { get; set; } public bool Wear { get; set; } @@ -265,7 +269,7 @@ namespace Barotrauma } float priority = Math.Clamp(objectiveManager.GetCurrentPriority(), 10, 100); - bool checkPath = priority >= AIObjectiveManager.LowestOrderPriority && (objectiveManager.IsCurrentOrder() || objectiveManager.CurrentOrder is AIObjectiveGoTo gotoOrder && gotoOrder.followControlledCharacter); + bool checkPath = priority >= AIObjectiveManager.LowestOrderPriority && (objectiveManager.IsCurrentOrder() || objectiveManager.CurrentOrder is AIObjectiveGoTo gotoOrder && gotoOrder.isFollowOrderObjective); bool hasCalledPathFinder = false; int itemsPerFrame = (int)priority; for (int i = 0; i < itemsPerFrame && currSearchIndex < Item.ItemList.Count - 1; i++) @@ -408,7 +412,7 @@ namespace Barotrauma if (ignoredItems.Contains(item)) { return false; }; if (item.Condition < TargetCondition) { return false; } if (ItemFilter != null && !ItemFilter(item)) { return false; } - return identifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id)); + return identifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id) || (AllowVariants && item.Prefab.VariantOf?.Identifier == id)); } public override void Reset() diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs index a67611486..154e88ec9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveGoTo.cs @@ -24,7 +24,7 @@ namespace Barotrauma public Func priorityGetter; - public bool followControlledCharacter; + public bool isFollowOrderObjective; public bool mimic; public bool SpeakIfFails { get; set; } = true; public bool UsePathingOutside { get; set; } = true; @@ -165,17 +165,10 @@ namespace Barotrauma protected override void Act(float deltaTime) { - if (followControlledCharacter) + if (Target == null) { - if (Character.Controlled != null && HumanAIController.IsFriendly(Character.Controlled)) - { - Target = Character.Controlled; - } - if (Target == null) - { - Abandon = true; - return; - } + Abandon = true; + return; } if (Target == character || character.SelectedBy != null && HumanAIController.IsFriendly(character.SelectedBy)) { @@ -205,7 +198,7 @@ namespace Barotrauma } } Hull targetHull = GetTargetHull(); - if (!followControlledCharacter) + if (!isFollowOrderObjective) { // Abandon if going through unsafe paths. Note ignores unsafe nodes when following an order or when the objective is set to ignore unsafe hulls. bool containsUnsafeNodes = character.IsDismissed && !HumanAIController.ObjectiveManager.CurrentObjective.IgnoreUnsafeHulls @@ -223,7 +216,7 @@ namespace Barotrauma { Abandon = true; } - else if (HumanAIController.IsCurrentPathUnreachable) + else if (HumanAIController.IsCurrentPathNullOrUnreachable) { waitUntilPathUnreachable -= deltaTime; SteeringManager.Reset(); @@ -317,8 +310,8 @@ namespace Barotrauma Character targetCharacter = Target as Character; if (character.AnimController.InWater) { - if (character.CurrentHull == null || - followControlledCharacter && + if (character.CurrentHull == null || + isFollowOrderObjective && targetCharacter != null && (targetCharacter.CurrentHull == null) != (character.CurrentHull == null) && Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition) < maxGapDistance * maxGapDistance) { @@ -361,7 +354,7 @@ namespace Barotrauma } if (TargetGap != null) { - if (TargetGap.FlowTargetHull != null && HumanAIController.SteerThroughGap(TargetGap, followControlledCharacter ? Target.WorldPosition : TargetGap.FlowTargetHull.WorldPosition, deltaTime)) + if (TargetGap.FlowTargetHull != null && HumanAIController.SteerThroughGap(TargetGap, isFollowOrderObjective ? Target.WorldPosition : TargetGap.FlowTargetHull.WorldPosition, deltaTime)) { SteeringManager.SteeringAvoid(deltaTime, avoidLookAheadDistance, weight: 1); return; @@ -595,7 +588,7 @@ namespace Barotrauma { if (gap.Open < 1) { continue; } if (gap.Submarine == null) { continue; } - if (!followControlledCharacter) + if (!isFollowOrderObjective) { if (gap.FlowTargetHull == null) { continue; } if (gap.Submarine != Target.Submarine) { continue; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs index 4cb710919..383264b6f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveManager.cs @@ -452,7 +452,7 @@ namespace Barotrauma extraDistanceWhileSwimming = 100, AllowGoingOutside = true, IgnoreIfTargetDead = true, - followControlledCharacter = true, + isFollowOrderObjective = true, mimic = true, DialogueIdentifier = "dialogcannotreachplace" }; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRepairItem.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRepairItem.cs index fd42eafd6..a6132f8cb 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRepairItem.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRepairItem.cs @@ -107,7 +107,10 @@ namespace Barotrauma { foreach (RelatedItem requiredItem in kvp.Value) { - var getItemObjective = new AIObjectiveGetItem(character, requiredItem.Identifiers, objectiveManager, true); + var getItemObjective = new AIObjectiveGetItem(character, requiredItem.Identifiers, objectiveManager, true) + { + AllowVariants = requiredItem.AllowVariants + }; if (objectiveManager.IsCurrentOrder()) { if (character.IsOnPlayerTeam) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs index 66cc99a4a..3d7b364ca 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescue.cs @@ -408,7 +408,7 @@ namespace Barotrauma } bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter) || - targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Strength <= a.Prefab.TreatmentThreshold); + targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Prefab.IsBuff || a.Strength <= a.Prefab.TreatmentThreshold); if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescueAll.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescueAll.cs index 48936b9f6..eb7504c08 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescueAll.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/AI/Objectives/AIObjectiveRescueAll.cs @@ -83,7 +83,7 @@ namespace Barotrauma if (character.AIController is HumanAIController humanAI) { if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target) || - target.CharacterHealth.GetAllAfflictions().All(a => a.Strength <= a.Prefab.TreatmentThreshold)) + target.CharacterHealth.GetAllAfflictions().All(a => a.Prefab.IsBuff || a.Strength <= a.Prefab.TreatmentThreshold)) { return false; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs index a3975aa27..96c4838ec 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Character.cs @@ -1371,7 +1371,7 @@ namespace Barotrauma info.Job.GiveJobItems(this, spawnPoint); } - public void GiveIdCardTags(WayPoint spawnPoint) + public void GiveIdCardTags(WayPoint spawnPoint, bool createNetworkEvent = false) { if (info?.Job == null || spawnPoint == null) { return; } @@ -1382,6 +1382,10 @@ namespace Barotrauma { item.AddTag(s); } + if (createNetworkEvent && (GameMain.NetworkMember?.IsServer ?? false)) + { + GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ChangeProperty, item.SerializableProperties["tags"] }); + } } } @@ -1760,6 +1764,7 @@ namespace Barotrauma } else if (IsPlayer) { + float dist = -1; Vector2 attackPos = SimPosition + ConvertUnits.ToSimUnits(cursorPosition - Position); List ignoredBodies = AnimController.Limbs.Select(l => l.body.FarseerBody).ToList(); ignoredBodies.Add(AnimController.Collider.FarseerBody); @@ -1791,13 +1796,13 @@ namespace Barotrauma } else { - if (body.UserData is IDamageable) + if (body.UserData is IDamageable damageable) { - attackTarget = (IDamageable)body.UserData; + attackTarget = damageable; } - else if (body.UserData is Limb) + else if (body.UserData is Limb limb) { - attackTarget = ((Limb)body.UserData).character; + attackTarget = limb.character; } } } @@ -1826,7 +1831,20 @@ namespace Barotrauma var attackLimb = sortedLimbs.FirstOrDefault(); if (attackLimb != null) { - attackLimb.UpdateAttack(deltaTime, attackPos, attackTarget, out AttackResult attackResult); + if (attackTarget is Character targetCharacter) + { + dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(Submarine.LastPickedPosition, attackLimb.SimPosition)); + foreach (Limb limb in targetCharacter.AnimController.Limbs) + { + if (limb.IsSevered || limb.Removed) { continue; } + float tempDist = ConvertUnits.ToDisplayUnits(Vector2.Distance(limb.SimPosition, attackLimb.SimPosition)); + if (tempDist < dist) + { + dist = tempDist; + } + } + } + attackLimb.UpdateAttack(deltaTime, attackPos, attackTarget, out AttackResult attackResult, dist); if (!attackLimb.attack.IsRunning) { attackCoolDown = 1.0f; @@ -3442,7 +3460,7 @@ namespace Barotrauma var attackResult = targetLimb == null ? AddDamage(worldPosition, attackAfflictions, attack.Stun, playSound, attackImpulse, out limbHit, attacker, attack.DamageMultiplier * attackData.DamageMultiplier) : - DamageLimb(worldPosition, targetLimb, attackAfflictions, attack.Stun, playSound, attackImpulse, attacker, attack.DamageMultiplier * attackData.DamageMultiplier, penetration: penetration + attackData.AddedPenetration); + DamageLimb(worldPosition, targetLimb, attackAfflictions, attack.Stun, playSound, attackImpulse, attacker, attack.DamageMultiplier * attackData.DamageMultiplier, penetration: penetration + attackData.AddedPenetration, shouldImplode: attackData.ShouldImplode); if (attacker != null) { @@ -3562,12 +3580,12 @@ namespace Barotrauma public void RecordKill(Character target) { - var abilityCharacter = new AbilityCharacter(target); + var abilityCharacterKill = new AbilityCharacterKill(target, this); foreach (Character attackerCrewmember in GetFriendlyCrew(this)) { - attackerCrewmember.CheckTalents(AbilityEffectType.OnCrewKillCharacter, abilityCharacter); + attackerCrewmember.CheckTalents(AbilityEffectType.OnCrewKillCharacter, abilityCharacterKill); } - CheckTalents(AbilityEffectType.OnKillCharacter, abilityCharacter); + CheckTalents(AbilityEffectType.OnKillCharacter, abilityCharacterKill); if (!IsOnPlayerTeam) { return; } if (GameMain.Config.KilledCreatures.Any(name => name.Equals(target.SpeciesName, StringComparison.OrdinalIgnoreCase))) { return; } @@ -3583,7 +3601,7 @@ namespace Barotrauma GameMain.Config.RecentlyEncounteredCreatures.Add(other.SpeciesName); } - public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, IEnumerable afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null, float damageMultiplier = 1, bool allowStacking = true, float penetration = 0f) + public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, IEnumerable afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null, float damageMultiplier = 1, bool allowStacking = true, float penetration = 0f, bool shouldImplode = false) { if (Removed) { return new AttackResult(); } @@ -3642,6 +3660,12 @@ namespace Barotrauma float prevVitality = CharacterHealth.Vitality; AttackResult attackResult = hitLimb.AddDamage(simPos, afflictions, playSound, damageMultiplier: damageMultiplier, penetration: penetration, attacker: attacker); CharacterHealth.ApplyDamage(hitLimb, attackResult, allowStacking); + if (shouldImplode) + { + // Only used by assistant's True Potential talent. Has to run here in order to properly give kill credit when it activates. + Implode(); + } + if (attacker != this) { OnAttacked?.Invoke(attacker, attackResult); @@ -3790,7 +3814,7 @@ namespace Barotrauma } } - public void Implode(bool isNetworkMessage = false) + private void Implode(bool isNetworkMessage = false) { if (CharacterHealth.Unkillable || GodMode || IsDead) { return; } @@ -4631,4 +4655,16 @@ namespace Barotrauma AggressiveBehavior = aggressiveBehavior; } } + + class AbilityCharacterKill : AbilityObject, IAbilityCharacter + { + public AbilityCharacterKill(Character character, Character killer) + { + Character = character; + Killer = killer; + } + public Character Character { get; set; } + public Character Killer { get; set; } + } + } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionHusk.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionHusk.cs index 3878de39b..5203f1728 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionHusk.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/Afflictions/AfflictionHusk.cs @@ -291,7 +291,7 @@ namespace Barotrauma husk.SetStun(5); yield return new WaitForSeconds(5, false); #if CLIENT - husk.PlaySound(CharacterSound.SoundType.Idle); + husk?.PlaySound(CharacterSound.SoundType.Idle); #endif yield return CoroutineStatus.Success; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs index eff3bba62..cea7a1bf5 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Health/CharacterHealth.cs @@ -867,6 +867,8 @@ namespace Barotrauma FaceTint = DefaultFaceTint; BodyTint = Color.TransparentBlack; + if (!(Character?.Params?.Health.ApplyAfflictionColors ?? false)) { return; } + for (int i = 0; i < limbHealths.Count; i++) { for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs index 39016b7d6..be1df08e5 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Params/CharacterParams.cs @@ -466,6 +466,9 @@ namespace Barotrauma [Serialize(false, true), Editable] public bool StunImmunity { get; set; } + [Serialize(false, true, description: "Can afflictions affect the face/body tint of the character."), Editable] + public bool ApplyAfflictionColors { get; private set; } + // TODO: limbhealths, sprite? public HealthParams(XElement element, CharacterParams character) : base(element, character) { } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionCharacter.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionCharacter.cs index 2670ea21e..407446785 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionCharacter.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionCharacter.cs @@ -14,8 +14,9 @@ namespace Barotrauma.Abilities protected override bool MatchesConditionSpecific(AbilityObject abilityObject) { - if ((abilityObject as IAbilityCharacter)?.Character is Character character) + if (abilityObject is IAbilityCharacter abilityCharacter) { + if (!(abilityCharacter.Character is Character character)) { return false; } if (!IsViableTarget(targetTypes, character)) { return false; } return true; diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionGeneHarvester.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionGeneHarvester.cs new file mode 100644 index 000000000..6ea6dd5e9 --- /dev/null +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityConditionals/AbilityConditionData/AbilityConditionGeneHarvester.cs @@ -0,0 +1,23 @@ +using System.Xml.Linq; + +namespace Barotrauma.Abilities +{ + class AbilityConditionGeneHarvester : AbilityConditionData + { + + public AbilityConditionGeneHarvester(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement) { } + + protected override bool MatchesConditionSpecific(AbilityObject abilityObject) + { + if (abilityObject is AbilityCharacterKill abilityCharacterKill) + { + return abilityCharacterKill.Killer.Submarine == null || abilityCharacterKill.Killer.TeamID != abilityCharacterKill.Killer.Submarine.TeamID; + } + else + { + LogAbilityConditionError(abilityObject, typeof(AbilityCharacterKill)); + return false; + } + } + } +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityObjects.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityObjects.cs index 49431bb1e..db98b843d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityObjects.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/AbilityObjects.cs @@ -136,6 +136,7 @@ namespace Barotrauma.Abilities public float DamageMultiplier { get; set; } = 1f; public float AddedPenetration { get; set; } = 0f; public List Afflictions { get; set; } + public bool ShouldImplode { get; set; } = false; public Attack SourceAttack { get; } public Character Character { get; set; } public Character Attacker { get; set; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyAttackData.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyAttackData.cs index 2e742bb3f..30b6eb7c4 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyAttackData.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyAttackData.cs @@ -37,12 +37,7 @@ namespace Barotrauma.Abilities attackData.DamageMultiplier += addedDamageMultiplier; attackData.AddedPenetration += addedPenetration; - if (implode) - { - // might have issues, as the method used to be private and only used for pressure death - attackData.Character?.Implode(); - } - + attackData.ShouldImplode = implode; } else { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyFlag.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyFlag.cs index d9953bf23..8993f0ccd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyFlag.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyFlag.cs @@ -8,6 +8,7 @@ namespace Barotrauma.Abilities private readonly AbilityFlags abilityFlag; private bool lastState; + public override bool AllowClientSimulation => true; public CharacterAbilityModifyFlag(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyReduceAffliction.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyReduceAffliction.cs index affb06085..cc36bce31 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyReduceAffliction.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyReduceAffliction.cs @@ -6,6 +6,7 @@ namespace Barotrauma.Abilities class CharacterAbilityModifyReduceAffliction : CharacterAbility { float addedAmountMultiplier; + public override bool AllowClientSimulation => true; public CharacterAbilityModifyReduceAffliction(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyResistance.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyResistance.cs index 4317f6745..010f04f2f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyResistance.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyResistance.cs @@ -7,6 +7,7 @@ namespace Barotrauma.Abilities private readonly string resistanceId; private readonly float resistance; bool lastState; + public override bool AllowClientSimulation => true; // should probably be split to different classes public CharacterAbilityModifyResistance(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStat.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStat.cs index c61a5a646..74e04a098 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStat.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStat.cs @@ -7,6 +7,7 @@ namespace Barotrauma.Abilities private readonly StatTypes statType; private readonly float value; bool lastState; + public override bool AllowClientSimulation => true; public CharacterAbilityModifyStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToFlooding.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToFlooding.cs index 20dbf654d..3269e078a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToFlooding.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToFlooding.cs @@ -8,6 +8,7 @@ namespace Barotrauma.Abilities private readonly StatTypes statType; private readonly float maxValue; private float lastValue = 0f; + public override bool AllowClientSimulation => true; public CharacterAbilityModifyStatToFlooding(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToLevel.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToLevel.cs index a8630dc2c..14ac0324c 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToLevel.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToLevel.cs @@ -9,6 +9,7 @@ namespace Barotrauma.Abilities private readonly float statPerLevel; private readonly int maxLevel; private float lastValue = 0f; + public override bool AllowClientSimulation => true; public CharacterAbilityModifyStatToLevel(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToSkill.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToSkill.cs index 44435e95d..b2a01a4c1 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToSkill.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CharacterAbilityModifyStatToSkill.cs @@ -10,6 +10,7 @@ namespace Barotrauma.Abilities private readonly string skillIdentifier; private readonly bool useAll; private float lastValue = 0f; + public override bool AllowClientSimulation => true; public CharacterAbilityModifyStatToSkill(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityInsurancePolicy.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityInsurancePolicy.cs index 1583bd53b..94a89cc5b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityInsurancePolicy.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityInsurancePolicy.cs @@ -19,11 +19,10 @@ namespace Barotrauma.Abilities moneyPerMission = abilityElement.GetAttributeInt("moneypermission", 0); } - protected override void ApplyEffect() + protected override void ApplyEffect(AbilityObject abilityObject) { if (Character?.Info is CharacterInfo info) { - Character.GiveMoney(moneyPerMission * info.MissionsCompletedSinceDeath); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityPsychoClown.cs b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityPsychoClown.cs index 61e9d9cf6..0f49b2e56 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityPsychoClown.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Characters/Talents/Abilities/CustomAbilities/CharacterAbilityPsychoClown.cs @@ -8,6 +8,7 @@ namespace Barotrauma.Abilities private float maxValue; private string afflictionIdentifier; private float lastValue = 0f; + public override bool AllowClientSimulation => true; public CharacterAbilityPsychoClown(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCChangeTeamAction.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCChangeTeamAction.cs index f4aa41aa8..b7c31e334 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCChangeTeamAction.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventActions/NPCChangeTeamAction.cs @@ -45,6 +45,20 @@ namespace Barotrauma { wifiComponent.TeamID = TeamTag; } + var idCard = item.GetComponent(); + if (idCard != null) + { + idCard.TeamID = TeamTag; + idCard.SubmarineSpecificID = 0; + } + } + + WayPoint subWaypoint = + WayPoint.WayPointList.Find(wp => wp.Submarine == Submarine.MainSub && wp.SpawnType == SpawnType.Human && wp.AssignedJob == npc.Info.Job?.Prefab) ?? + WayPoint.WayPointList.Find(wp => wp.Submarine == Submarine.MainSub && wp.SpawnType == SpawnType.Human); + if (subWaypoint != null) + { + npc.GiveIdCardTags(subWaypoint, createNetworkEvent: true); } #if SERVER GameMain.NetworkMember.CreateEntityEvent(npc, new object[] { NetEntityEvent.Type.AddToCrew, TeamTag, npc.Inventory.AllItems.Select(it => it.ID).ToArray() }); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs index 7431b31b3..ca7667dca 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/EventSet.cs @@ -112,13 +112,13 @@ namespace Barotrauma } return prefab; } - + this.Prefabs = prefabIdentifiers .Select(tryFindPrefab) .Where(p => p != null) .ToImmutableArray(); - this.Commonness = commonness ?? this.Prefabs.Select(p => p.Commonness).Max(); - this.Probability = probability ?? this.Prefabs.Select(p => p.Probability).Max(); + this.Commonness = commonness ?? this.Prefabs.Select(p => p.Commonness).MaxOrNull() ?? 0.0f; + this.Probability = probability ?? this.Prefabs.Select(p => p.Probability).MaxOrNull() ?? 0.0f; } public SubEventPrefab(EventPrefab prefab, float commonness, float probability) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/PirateMission.cs b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/PirateMission.cs index e9d3cef40..39a4e2d01 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/PirateMission.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Events/Missions/PirateMission.cs @@ -390,7 +390,7 @@ namespace Barotrauma private bool DeadOrCaptured(Character character) { - return character != null && !character.Removed && (character.IsDead || (character.LockHands && character.Submarine == Submarine.MainSub)); + return character == null || character.Removed || character.IsDead || (character.LockHands && character.Submarine == Submarine.MainSub); } public override void End() diff --git a/Barotrauma/BarotraumaShared/SharedSource/Extensions/IEnumerableExtensions.cs b/Barotrauma/BarotraumaShared/SharedSource/Extensions/IEnumerableExtensions.cs index c3d164708..823a3149f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Extensions/IEnumerableExtensions.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Extensions/IEnumerableExtensions.cs @@ -171,5 +171,21 @@ namespace Barotrauma.Extensions } return false; } + + /// + /// Returns the maximum element in a given enumerable, or null if there + /// aren't any elements in the input. + /// + /// Input collection + /// Maximum element or null + public static T? MaxOrNull(this IEnumerable enumerable) where T : struct, IComparable + { + T? retVal = null; + foreach (T v in enumerable) + { + if (!retVal.HasValue || v.CompareTo(retVal.Value) > 0) { retVal = v; } + } + return retVal; + } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs index 49abae0d4..55170d2ea 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/Pickable.cs @@ -80,6 +80,8 @@ namespace Barotrauma.Items.Components if ((picker.PickingItem == null || picker.PickingItem == item) && PickingTime <= float.MaxValue) { #if SERVER + // Set active picker before creating the server event to make sure it's set correctly + activePicker = picker; item.CreateServerEvent(this); #endif pickingCoroutine = CoroutineManager.StartCoroutine(WaitForPick(picker, abilityPickingTime.Value)); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs index a07db8e19..abb8dd7ef 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Holdable/RangedWeapon.cs @@ -72,9 +72,9 @@ namespace Barotrauma.Items.Components { get { - Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation); + Matrix bodyTransform = Matrix.CreateRotationZ(item.body == null ? MathHelper.ToRadians(item.Rotation) : item.body.Rotation); Vector2 flippedPos = barrelPos; - if (item.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; } + if (item.body != null && item.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; } return Vector2.Transform(flippedPos, bodyTransform) * item.Scale; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Deconstructor.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Deconstructor.cs index e3eb12bcd..f8eb62c33 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Deconstructor.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Deconstructor.cs @@ -202,7 +202,7 @@ namespace Barotrauma.Items.Components { float percentageHealth = targetItem.Condition / targetItem.MaxCondition; - if (percentageHealth <= deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) { return; } + if (percentageHealth < deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) { return; } if (!(MapEntityPrefab.Find(null, deconstructProduct.ItemIdentifier) is ItemPrefab itemPrefab)) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs index 461ae0617..c7d83ec42 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Machines/Fabricator.cs @@ -347,22 +347,20 @@ namespace Barotrauma.Items.Components float outCondition = fabricatedItem.OutCondition; if (i < amountFittingContainer) { - Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, + Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality, onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); - spawnedItem.Quality = quality; //reset the condition in case the max condition is higher than the prefab's due to e.g. quality modifiers spawnedItem.Condition = spawnedItem.MaxCondition * outCondition; }); } else { - Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * outCondition, + Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * outCondition, quality, onSpawned: (Item spawnedItem) => { onItemSpawned(spawnedItem, tempUser); - spawnedItem.Quality = quality; //reset the condition in case the max condition is higher than the prefab's due to e.g. quality modifiers spawnedItem.Condition = spawnedItem.MaxCondition * outCondition; }); diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/RemoteController.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/RemoteController.cs index 783253c89..3643d43b1 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/RemoteController.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/RemoteController.cs @@ -75,6 +75,7 @@ namespace Barotrauma.Items.Components float closestDist = float.PositiveInfinity; foreach (Item targetItem in Item.ItemList) { + if (targetItem.NonInteractable || targetItem.NonPlayerTeamInteractable || targetItem.HiddenInGame) { continue; } if (OnlyInOwnSub) { if (targetItem.Submarine != item.Submarine) { continue; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs index 3ff601550..38331c971 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Repairable.cs @@ -547,6 +547,11 @@ namespace Barotrauma.Items.Components //power transfer items (junction boxes, relays) don't deteriorate if they're no carrying any power if (Math.Abs(pt.CurrPowerConsumption) > 0.1f) { return true; } } + else if (ic is PowerContainer pc) + { + //batteries don't deteriorate if they're not charging/discharging + if (Math.Abs(pc.CurrPowerConsumption) > 0.1f || Math.Abs(pc.CurrPowerOutput) > 0.1f) { return true; } + } else if (ic is Engine engine) { //engines don't deteriorate if they're not running @@ -555,7 +560,7 @@ namespace Barotrauma.Items.Components else if (ic is Pump pump) { //pumps don't deteriorate if they're not running - if (Math.Abs(pump.FlowPercentage) > 1.0f && pump.IsActive) { return true; } + if (Math.Abs(pump.FlowPercentage) > 1.0f && pump.IsActive && pump.HasPower) { return true; } } else if (ic is Reactor reactor) { diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Rope.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Rope.cs index 6e9836551..1e696bf14 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Rope.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Rope.cs @@ -127,6 +127,8 @@ namespace Barotrauma.Items.Components if (source == null || target == null || target.Removed || (source is Entity sourceEntity && sourceEntity.Removed)) { + source = null; + target = null; IsActive = false; return; } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/ConnectionPanel.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/ConnectionPanel.cs index a52a9600b..79bc7f6a9 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/ConnectionPanel.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/Components/Signal/ConnectionPanel.cs @@ -225,10 +225,23 @@ namespace Barotrauma.Items.Components //no electrocution in sub editor if (Screen.Selected == GameMain.SubEditorScreen) { return true; } - var powered = item.GetComponent(); - if (powered != null) + var reactor = item.GetComponent(); + if (reactor != null) { - //unpowered panels can be rewired without a risk of electrical shock + //reactors that arent generating power atm can be rewired without the risk of electrical shock + if (MathUtils.NearlyEqual(reactor.CurrPowerConsumption, 0.0f)) { return true; } + } + var powerContainer = item.GetComponent(); + if (powerContainer != null) + { + //empty batteries/supercapacitors can be rewired without the risk of electrical shock + //non-empty ones always have a chance of zapping the user + if (powerContainer.Charge <= 0.0f) { return true; } + } + var powered = item.GetComponent(); + if (powered != null && powerContainer == null) + { + //unpowered panels can be rewired without the risk of electrical shock if (powered.Voltage < 0.1f) { return true; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs index 122eebb5b..8b501cd71 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/ItemPrefab.cs @@ -13,7 +13,7 @@ namespace Barotrauma struct DeconstructItem { public readonly string ItemIdentifier; - //minCondition does <= check, meaning that below or equeal to min condition will be skipped. + //minCondition does <= check, meaning that below or equal to min condition will be skipped. public readonly float MinCondition; //maxCondition does > check, meaning that above this max the deconstruct item will be skipped. public readonly float MaxCondition; @@ -50,7 +50,6 @@ namespace Barotrauma ActivateButtonText = element.GetAttributeString("activatebuttontext", string.Empty); InfoText = element.GetAttributeString("infotext", string.Empty); InfoTextOnOtherItemMissing = element.GetAttributeString("infotextonotheritemmissing", string.Empty); - } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Items/RelatedItem.cs b/Barotrauma/BarotraumaShared/SharedSource/Items/RelatedItem.cs index ad9b19866..a5c4b8974 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Items/RelatedItem.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Items/RelatedItem.cs @@ -36,7 +36,7 @@ namespace Barotrauma /// public bool ExcludeBroken { get; private set; } - private bool allowVariants = true; + public bool AllowVariants { get; private set; } = true; public RelationType Type { @@ -84,13 +84,13 @@ namespace Barotrauma { if (item == null) { return false; } if (excludedIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { return false; } - return Identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id) || (allowVariants && item.Prefab.VariantOf?.Identifier == id)); + return Identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id) || (AllowVariants && item.Prefab.VariantOf?.Identifier == id)); } public bool MatchesItem(ItemPrefab itemPrefab) { if (itemPrefab == null) { return false; } if (excludedIdentifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id))) { return false; } - return Identifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id) || (allowVariants && itemPrefab.VariantOf?.Identifier == id)); + return Identifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id) || (AllowVariants && itemPrefab.VariantOf?.Identifier == id)); } public RelatedItem(string[] identifiers, string[] excludedIdentifiers) @@ -171,7 +171,7 @@ namespace Barotrauma new XAttribute("ignoreineditor", IgnoreInEditor), new XAttribute("excludebroken", ExcludeBroken), new XAttribute("targetslot", TargetSlot), - new XAttribute("allowvariants", allowVariants)); + new XAttribute("allowvariants", AllowVariants)); if (excludedIdentifiers.Length > 0) { @@ -235,7 +235,7 @@ namespace Barotrauma RelatedItem ri = new RelatedItem(identifiers, excludedIdentifiers) { ExcludeBroken = element.GetAttributeBool("excludebroken", true), - allowVariants = element.GetAttributeBool("allowvariants", true) + AllowVariants = element.GetAttributeBool("allowvariants", true) }; string typeStr = element.GetAttributeString("type", ""); if (string.IsNullOrEmpty(typeStr)) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs index b5adbe740..710b21946 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Creatures/BallastFloraBehavior.cs @@ -510,14 +510,14 @@ namespace Barotrauma.MapCreatures.Behavior List list = branches[hull]; if (!list.Any(HasAcidEmitter)) { - BallastFloraBranch randomBranh = branches[hull].GetRandom(); - randomBranh.SpawningItem = true; + BallastFloraBranch randomBranch = branches[hull].GetRandom(); + randomBranch.SpawningItem = true; ItemPrefab prefab = ItemPrefab.Find(null, AttackItemPrefab); - Entity.Spawner?.AddToSpawnQueue(prefab, Parent.Position + Offset + randomBranh.Position, Parent.Submarine, null, item => + Entity.Spawner?.AddToSpawnQueue(prefab, Parent.Position + Offset + randomBranch.Position, Parent.Submarine, onSpawned: item => { - randomBranh.AttackItem = item; - randomBranh.SpawningItem = false; + randomBranch.AttackItem = item; + randomBranch.SpawningItem = false; }); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelObjects/LevelTrigger.cs b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelObjects/LevelTrigger.cs index 71a30f339..87d9e6887 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelObjects/LevelTrigger.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Map/Levels/LevelObjects/LevelTrigger.cs @@ -213,9 +213,8 @@ namespace Barotrauma }; PhysicsBody.FarseerBody.OnCollision += PhysicsBody_OnCollision; PhysicsBody.FarseerBody.OnSeparation += PhysicsBody_OnSeparation; - PhysicsBody.FarseerBody.SetIsSensor(true); + PhysicsBody.FarseerBody.SetIsSensor(element.GetAttributeBool("sensor", true)); PhysicsBody.FarseerBody.BodyType = BodyType.Static; - PhysicsBody.FarseerBody.BodyType = BodyType.Kinematic; ColliderRadius = ConvertUnits.ToDisplayUnits(Math.Max(Math.Max(PhysicsBody.radius, PhysicsBody.width / 2.0f), PhysicsBody.height / 2.0f)); @@ -532,10 +531,9 @@ namespace Barotrauma } } - if (triggerOnce) + if (triggerOnce && triggeredOnce) { - if (triggeredOnce) { return; } - if (triggerers.Count > 0) { triggeredOnce = true; } + return; } foreach (Entity triggerer in triggerers) @@ -577,6 +575,12 @@ namespace Barotrauma GameMain.GameScreen.Cam.Shake = Math.Max(GameMain.GameScreen.Cam.Shake, cameraShake); } } + + if (triggerOnce && triggerers.Count > 0) + { + PhysicsBody.Enabled = false; + triggeredOnce = true; + } } public static void RemoveDistantTriggerers(PhysicsBody physicsBody, HashSet triggerers, Vector2 calculateDistanceTo) diff --git a/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs b/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs index a4f18118c..b5f155b33 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Networking/EntitySpawner.cs @@ -26,6 +26,7 @@ namespace Barotrauma public readonly Inventory Inventory; public readonly Submarine Submarine; public readonly float Condition; + public readonly int Quality; public bool SpawnIfInventoryFull = true; public bool IgnoreLimbSlots = false; @@ -33,28 +34,31 @@ namespace Barotrauma private readonly Action onSpawned; - public ItemSpawnInfo(ItemPrefab prefab, Vector2 worldPosition, Action onSpawned, float? condition = null) + public ItemSpawnInfo(ItemPrefab prefab, Vector2 worldPosition, Action onSpawned, float? condition = null, int? quality = null) { Prefab = prefab ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null."); Position = worldPosition; Condition = condition ?? prefab.Health; + Quality = quality ?? 0; this.onSpawned = onSpawned; } - public ItemSpawnInfo(ItemPrefab prefab, Vector2 position, Submarine sub, Action onSpawned, float? condition = null) + public ItemSpawnInfo(ItemPrefab prefab, Vector2 position, Submarine sub, Action onSpawned, float? condition = null, int? quality = null) { Prefab = prefab ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null."); Position = position; Submarine = sub; Condition = condition ?? prefab.Health; + Quality = quality ?? 0; this.onSpawned = onSpawned; } - public ItemSpawnInfo(ItemPrefab prefab, Inventory inventory, Action onSpawned, float? condition = null) + public ItemSpawnInfo(ItemPrefab prefab, Inventory inventory, Action onSpawned, float? condition = null, int? quality = null) { Prefab = prefab ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null."); Inventory = inventory; Condition = condition ?? prefab.Health; + Quality = quality ?? 0; this.onSpawned = onSpawned; } @@ -73,7 +77,8 @@ namespace Barotrauma } spawnedItem = new Item(Prefab, Vector2.Zero, null) { - Condition = Condition + Condition = Condition, + Quality = Quality }; var slot = Slot != InvSlotType.None ? Slot.ToEnumerable() : spawnedItem.AllowedSlots; if (!Inventory.Owner.Removed && !Inventory.TryPutItem(spawnedItem, null, slot)) @@ -94,7 +99,11 @@ namespace Barotrauma } else { - spawnedItem = new Item(Prefab, Position, Submarine); + spawnedItem = new Item(Prefab, Position, Submarine) + { + Condition = Condition, + Quality = Quality + }; } return spawnedItem; } @@ -241,7 +250,7 @@ namespace Barotrauma return "EntitySpawner"; } - public void AddToSpawnQueue(ItemPrefab itemPrefab, Vector2 worldPosition, float? condition = null, Action onSpawned = null) + public void AddToSpawnQueue(ItemPrefab itemPrefab, Vector2 worldPosition, float? condition = null, int? quality = null, Action onSpawned = null) { if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } if (itemPrefab == null) @@ -251,10 +260,10 @@ namespace Barotrauma GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue1:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } - spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, worldPosition, onSpawned, condition)); + spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, worldPosition, onSpawned, condition, quality)); } - public void AddToSpawnQueue(ItemPrefab itemPrefab, Vector2 position, Submarine sub, float? condition = null, Action onSpawned = null) + public void AddToSpawnQueue(ItemPrefab itemPrefab, Vector2 position, Submarine sub, float? condition = null, int? quality = null, Action onSpawned = null) { if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } if (itemPrefab == null) @@ -264,10 +273,10 @@ namespace Barotrauma GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue2:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } - spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, position, sub, onSpawned, condition)); + spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, position, sub, onSpawned, condition, quality)); } - public void AddToSpawnQueue(ItemPrefab itemPrefab, Inventory inventory, float? condition = null, Action onSpawned = null, bool spawnIfInventoryFull = true, bool ignoreLimbSlots = false, InvSlotType slot = InvSlotType.None) + public void AddToSpawnQueue(ItemPrefab itemPrefab, Inventory inventory, float? condition = null, int? quality = null, Action onSpawned = null, bool spawnIfInventoryFull = true, bool ignoreLimbSlots = false, InvSlotType slot = InvSlotType.None) { if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } if (itemPrefab == null) @@ -277,7 +286,7 @@ namespace Barotrauma GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } - spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, inventory, onSpawned, condition) + spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, inventory, onSpawned, condition, quality) { SpawnIfInventoryFull = spawnIfInventoryFull, IgnoreLimbSlots = ignoreLimbSlots, @@ -395,10 +404,6 @@ namespace Barotrauma { CreateNetworkEventProjSpecific(spawnedEntity, false); } - if (spawnedEntity is Item item) - { - item.Condition = ((ItemSpawnInfo)entitySpawnInfo).Condition; - } entitySpawnInfo.OnSpawned(spawnedEntity); } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs b/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs index f506479fd..eecd32a46 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/Screens/GameScreen.cs @@ -244,7 +244,7 @@ namespace Barotrauma cam.TargetPos = targetPos; } - cam.MoveCamera((float)deltaTime, allowZoom: GUI.MouseOn == null); + cam.MoveCamera((float)deltaTime, allowZoom: GUI.MouseOn == null && !Inventory.IsMouseOnInventory); #endif foreach (Submarine sub in Submarine.Loaded) diff --git a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub index 0d93cd70f..88858bb97 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub and b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index eefe78651..997719739 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,3 +1,68 @@ +--------------------------------------------------------------------------------------------------------- +v0.15.13.0 +--------------------------------------------------------------------------------------------------------- + +Changes: +- Remove spawnpoint-based job assignment logic. Previously, the number of job-specific spawnpoints in the sub affected how many players the server would try to assign to a given job, which would often lead to players not getting the job they wanted to. +- Made the captain job optional (i.e. if no-one has captain in their preferences, no-one gets forced to play as one). +- Improvements to the Korean localization. +- Added rewards to side objective missions (hunting grounds, beacon). +- Reduce Pyromaniac's burning damage increase from 40% to 25%. + +Fixes: +- Fixed clients' characters getting reset in the multiplayer campaign if they're spectating at the end of the round. +- Fixed crashing when an event set fails to load any of its sub events. Caused certain mods to crash the game at 42% in the loading screen. +- Fixed crashing on startup when using mods that remove the small icons from job prefabs. +- Fixed bots being unable to find their way to the submarine if the switch to the "find safety" state outside the sub. +- Fixed bots often being unable to find a way to leaks they're trying to weld. +- Fixed crashing when a monster was just about to turn to a husk when the round ends. +- Fixed opened item disappearing when switching to the test mode from the sub editor. +- Fixed Artie Dolittle's ID card not working in the player's sub after he's hired. +- Fixed category labels (sufficient skills to fabricate, requires recipe, etc) disappearing from the fabricator's item list when searching. +- Fixed prisoner's uniform using a wrong texture file. +- Fixed inability to edit pulse laser's power consumption in the sub editor. +- Fixed an exploit in Pressure Stabilizer crafting recipe. +- Fixed an exploit in Fixfoam Grenade deconstruction recipe. +- Fixed crashing when selecting a gene splicer and hovering over its inventory slot in the sub editor. +- Fixed bots being unable to use hardened and dementonite tools. +- Fixed a typo in Tinkering cooldown reduction description. +- Fixed inactive reactors electrocuting low-skill characters when rewired. +- Fixed ignore orders carrying over when switching subs, causing them to target random items in the new sub. +- Fixed dementonite knives being sold in stores. +- Fixed contained items' impact sounds being played when the item they're inside hits the floor. +- Fixed True Potential instant kills not properly giving kill credit (achievements, other talents). +- Fixed Gene Harvester incorrectly checking the owner of the talent's submarine rather than the killer's. +- Fixed certain talents not appearing to have an effect client-side, causing e.g. the high-pressure effects to appear when swimming outside with the Water Prankster talent. +- Fix Scavenger's buff duration multiplier. +- Fixed Insurance Policy not triggering properly. +- Fixed issues with input going through interfaces drawn over inventory slots. +- Fixed bots trying to treat talent afflictions. +- Fixed player-controlled creature attacks sometimes not hitting characters when they should. +- Fixed "x in command room" spam when dragging and dropping orders in multiplayer. +- Fixed pirate missions not being considered completed if any of the pirates have been removed (e.g. eaten or despawned). +- Fixed high-quality items not stacking in the fabricator's output slot. +- Fixed ice shards' colliders taying active after the shard has shattered. +- Fixed bots following the controlled character instead of the order giver in singleplayer. +- Fixed purchased medals spawning on the floor. +- Fixed vision obstruction effect "flickering" when moving the cursor around when an item UI is open. +- Fixed some pumps in Dugong having 0 power consumption. +- Fixed pumps deteriorating when they don't have power. +- Fixed batteries/supercapacitors deteriorating when not charging/discharging. +- Fixed afflictions applying face/body tints on monsters. +- Fixed "select matching items" selecting all gaps if you've selected both a door and its gap in the sub editor. +- Fixed door gaps not being selected if you use "select matching items" on a door in the sub editor. +- Fixed biome ambience loop volume not being affected by the sound volume setting. +- Fixed players wearing a PUCS not using up hull oxygen when no tank is equipped. +- Fixed bots getting stuck on long outpost ladders. +- Restored the sounds for legacy fractal guardians. Fixes console errors when they are spawned/viewed in the editor. +- Fixed Reactor PDA showing hidden and non-interactable reactors. +- Fixed welding tool scale being forced to 0.5. +- Fix crew list content being repositioned when selected character was hovered. + +Modding: +- Made deconstruction recipes' mincondition accept items whose condition equals to the mincondition, not just items whose condition is higher. +- Fixed color values being used incorrectly in explosion flashes. + --------------------------------------------------------------------------------------------------------- v0.15.12.0 --------------------------------------------------------------------------------------------------------- diff --git a/Barotrauma/BarotraumaShared/config.xml b/Barotrauma/BarotraumaShared/config.xml index 4f6ecf964..8e811973a 100644 --- a/Barotrauma/BarotraumaShared/config.xml +++ b/Barotrauma/BarotraumaShared/config.xml @@ -21,7 +21,10 @@ losmode="Transparent" hudscale="1" inventoryscale="1" /> - + + +