diff --git a/Barotrauma/BarotraumaClient/Launch_Barotrauma b/Barotrauma/BarotraumaClient/Launch_Barotrauma index 8792fad24..4e95158fd 100644 --- a/Barotrauma/BarotraumaClient/Launch_Barotrauma +++ b/Barotrauma/BarotraumaClient/Launch_Barotrauma @@ -1,3 +1,3 @@ #!/bin/sh -exec mono "./Barotrauma.exe" MONO_LOG_LEVEL=debug "$@" +./Barotrauma diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index 411dec235..c74cd30c3 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -186,6 +186,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs index 667d29d62..7951bcf8e 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs @@ -98,7 +98,7 @@ namespace Barotrauma if (distSqrd > 10.0f || !character.AllowInput) { Collider.TargetRotation = newRotation; - SetPosition(newPosition, lerp: distSqrd < 5.0f); + SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false); } else { @@ -149,6 +149,62 @@ namespace Barotrauma character.MemLocalState[i].TransformOutToInside(currentHull.Submarine); } } + + if (localPos.Animation != serverPos.Animation) + { + if (serverPos.Animation == AnimController.Animation.CPR) + { + character.AnimController.Anim = AnimController.Animation.CPR; + } + else if (character.AnimController.Anim == AnimController.Animation.CPR) + { + character.AnimController.Anim = AnimController.Animation.None; + } + } + + Hull serverHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(serverPos.Position), character.CurrentHull, serverPos.Position.Y < lowestSubPos); + Hull clientHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(localPos.Position), serverHull, localPos.Position.Y < lowestSubPos); + + if (serverHull != null && clientHull != null && serverHull.Submarine != clientHull.Submarine) + { + //hull subs don't match => teleport the camera to the other sub + character.Submarine = serverHull.Submarine; + character.CurrentHull = currentHull = serverHull; + SetPosition(serverPos.Position); + character.MemLocalState.Clear(); + } + else + { + Vector2 positionError = serverPos.Position - localPos.Position; + float rotationError = serverPos.Rotation.HasValue && localPos.Rotation.HasValue ? + serverPos.Rotation.Value - localPos.Rotation.Value : + 0.0f; + + for (int i = localPosIndex; i < character.MemLocalState.Count; i++) + { + Hull pointHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(character.MemLocalState[i].Position), clientHull, character.MemLocalState[i].Position.Y < lowestSubPos); + if (pointHull != clientHull && ((pointHull == null) || (clientHull == null) || (pointHull.Submarine == clientHull.Submarine))) break; + character.MemLocalState[i].Translate(positionError, rotationError); + } + + float errorMagnitude = positionError.Length(); + if (errorMagnitude > 0.01f) + { + Collider.TargetPosition = Collider.SimPosition + positionError; + Collider.TargetRotation = Collider.Rotation + rotationError; + Collider.MoveToTargetPosition(lerp: true); + if (errorMagnitude > 0.5f) + { + character.MemLocalState.Clear(); + foreach (Limb limb in Limbs) + { + limb.body.TargetPosition = limb.body.SimPosition + positionError; + limb.body.MoveToTargetPosition(lerp: true); + } + } + } + } + } if (character.MemState.Count < 1) return; @@ -238,20 +294,16 @@ namespace Barotrauma } float errorMagnitude = positionError.Length(); - if (errorMagnitude > 0.01f) + if (errorMagnitude > 0.5f) + { + character.MemLocalState.Clear(); + SetPosition(serverPos.Position, lerp: true, ignorePlatforms: false); + } + else if (errorMagnitude > 0.01f) { Collider.TargetPosition = Collider.SimPosition + positionError; Collider.TargetRotation = Collider.Rotation + rotationError; Collider.MoveToTargetPosition(lerp: true); - if (errorMagnitude > 0.5f) - { - character.MemLocalState.Clear(); - foreach (Limb limb in Limbs) - { - limb.body.TargetPosition = limb.body.SimPosition + positionError; - limb.body.MoveToTargetPosition(lerp: true); - } - } } } diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Character.cs b/Barotrauma/BarotraumaClient/Source/Characters/Character.cs index 9e5265ec3..385bac977 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Character.cs @@ -46,8 +46,7 @@ namespace Barotrauma if (controlled == value) return; controlled = value; if (controlled != null) controlled.Enabled = true; - CharacterHealth.OpenHealthWindow = null; - + CharacterHealth.OpenHealthWindow = null; } } diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs index 441ae6a6e..e180f44e6 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs @@ -85,7 +85,8 @@ namespace Barotrauma { if (character.Inventory != null) { - if (!character.LockHands && character.Stun < 0.1f) + if (!character.LockHands && character.Stun < 0.1f && + (character.SelectedConstruction == null || character.SelectedConstruction.GetComponent() == null)) { character.Inventory.Update(deltaTime, cam); } @@ -320,6 +321,7 @@ namespace Barotrauma } if (character.Inventory != null && !character.LockHands) { + character.Inventory.Locked = (character.SelectedConstruction != null && character.SelectedConstruction.GetComponent() != null); character.Inventory.DrawOwn(spriteBatch); character.Inventory.CurrentLayout = CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null ? CharacterInventory.Layout.Default : diff --git a/Barotrauma/BarotraumaClient/Source/GameMain.cs b/Barotrauma/BarotraumaClient/Source/GameMain.cs index 2741cee63..bae4cb528 100644 --- a/Barotrauma/BarotraumaClient/Source/GameMain.cs +++ b/Barotrauma/BarotraumaClient/Source/GameMain.cs @@ -526,11 +526,20 @@ namespace Barotrauma } /// - /// Returns the file paths of all files of the given type in the currently selected content packages. + /// Returns the file paths of all files of the given type in the content packages. /// - public IEnumerable GetFilesOfType(ContentType type) + /// + /// If true, also returns files in content packages that are installed but not currently selected. + public IEnumerable GetFilesOfType(ContentType type, bool searchAllContentPackages = false) { - return ContentPackage.GetFilesOfType(SelectedPackages, type); + if (searchAllContentPackages) + { + return ContentPackage.GetFilesOfType(ContentPackage.List, type); + } + else + { + return ContentPackage.GetFilesOfType(SelectedPackages, type); + } } /// diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index 5300dbd54..c888d06cd 100644 --- a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs +++ b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs @@ -74,12 +74,17 @@ namespace Barotrauma public CrewManager(XElement element, bool isSinglePlayer) : this(isSinglePlayer) { - if (!isSinglePlayer) + if (GameMain.Client != null) { - DebugConsole.ThrowError("Cannot add messages to single player chat box in multiplayer mode!\n" + Environment.StackTrace); + //let the server create random conversations in MP return; } - if (string.IsNullOrEmpty(text)) { return; } + List availableSpeakers = Character.CharacterList.FindAll(c => + c.AIController is HumanAIController && + !c.IsDead && + c.SpeechImpediment <= 100.0f); + pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers)); + } var characterInfo = new CharacterInfo(subElement); characterInfos.Add(characterInfo); @@ -90,7 +95,6 @@ namespace Barotrauma break; } } - ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender)); } partial void InitProjectSpecific() @@ -238,27 +242,24 @@ namespace Barotrauma public IEnumerable GetCharacters() { - if (character?.Inventory == null) return null; + if (characterInfos.Contains(characterInfo)) + { + DebugConsole.ThrowError("Tried to add the same character info to CrewManager twice.\n" + Environment.StackTrace); + return; + } - var radioItem = character.Inventory.Items.FirstOrDefault(it => it != null && it.GetComponent() != null); - if (radioItem == null) return null; - if (requireEquipped && !character.HasEquippedItem(radioItem)) return null; - - return radioItem.GetComponent(); + characterInfos.Add(characterInfo); } public IEnumerable GetCharacterInfos() { - if (GameMain.Client != null) + if (character == null) { - //let the server create random conversations in MP + DebugConsole.ThrowError("Tried to remove a null character from CrewManager.\n" + Environment.StackTrace); return; } - List availableSpeakers = Character.CharacterList.FindAll(c => - c.AIController is HumanAIController && - !c.IsDead && - c.SpeechImpediment <= 100.0f); - pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers)); + characters.Remove(character); + if (removeInfo) characterInfos.Remove(character.Info); } public void AddCharacter(Character character) @@ -632,9 +633,183 @@ namespace Barotrauma { characterListBox.BarScroll = roundedPos; } - soundIcon.Visible = !muted && !mutedLocally; - soundIconDisabled.Visible = muted || mutedLocally; - soundIconDisabled.ToolTip = TextManager.Get(mutedLocally ? "MutedLocally" : "MutedGlobally"); + var characterArea = new GUIButton(new RectTransform(new Point(characterInfoWidth, frame.Rect.Height), frame.RectTransform, Anchor.CenterLeft), style: "GUITextBox") + { + UserData = character, + Color = frame.Color, + SelectedColor = frame.SelectedColor, + HoverColor = frame.HoverColor, + ToolTip = characterToolTip + }; + + var soundIcon = new GUIImage(new RectTransform(new Point((int)(characterArea.Rect.Height * 0.5f)), characterArea.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(5, 0) }, + "GUISoundIcon") + { + UserData = "soundicon", + CanBeFocused = false, + Visible = true + }; + soundIcon.Color = new Color(soundIcon.Color, 0.0f); + new GUIImage(new RectTransform(new Point((int)(characterArea.Rect.Height * 0.5f)), characterArea.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(5, 0) }, + "GUISoundIconDisabled") + { + UserData = "soundicondisabled", + CanBeFocused = true, + Visible = false + }; + + if (isSinglePlayer) + { + characterArea.OnClicked = CharacterClicked; + } + else + { + characterArea.CanBeFocused = false; + characterArea.CanBeSelected = false; + } + + var characterImage = new GUICustomComponent(new RectTransform(new Point(characterArea.Rect.Height), characterArea.RectTransform, Anchor.CenterLeft), + onDraw: (sb, component) => character.Info.DrawIcon(sb, component.Rect.Center.ToVector2(), targetAreaSize: component.Rect.Size.ToVector2())) + { + CanBeFocused = false, + HoverColor = Color.White, + SelectedColor = Color.White, + ToolTip = characterToolTip + }; + + var characterName = new GUITextBlock(new RectTransform(new Point(characterArea.Rect.Width - characterImage.Rect.Width - soundIcon.Rect.Width - 10, characterArea.Rect.Height), + characterArea.RectTransform, Anchor.CenterRight) { AbsoluteOffset = new Point(soundIcon.Rect.Width + 10, 0) }, + character.Name, textColor: frame.Color, font: GUI.SmallFont, wrap: true) + { + Color = frame.Color, + HoverColor = Color.Transparent, + SelectedColor = Color.Transparent, + CanBeFocused = false, + ToolTip = characterToolTip, + AutoScale = true + }; + + //---------------- order buttons ---------------- + + var orderButtonFrame = new GUILayoutGroup(new RectTransform(new Point(100, frame.Rect.Height), frame.RectTransform) + { AbsoluteOffset = new Point(characterInfoWidth + spacing, 0) }, + isHorizontal: true, childAnchor: Anchor.CenterLeft) + { + AbsoluteSpacing = (int)(10 * GUI.Scale), + UserData = "orderbuttons", + CanBeFocused = false + }; + + //listbox for holding the orders inappropriate for this character + //(so we can easily toggle their visibility) + var wrongOrderList = new GUIListBox(new RectTransform(new Point(50, orderButtonFrame.Rect.Height), orderButtonFrame.RectTransform), isHorizontal: true, style: null) + { + ScrollBarEnabled = false, + ScrollBarVisible = false, + Enabled = false, + Spacing = spacing, + ClampMouseRectToParent = false + }; + wrongOrderList.Content.ClampMouseRectToParent = false; + + for (int i = 0; i < orders.Count; i++) + { + var order = orders[i]; + if (order.TargetAllCharacters) continue; + + RectTransform btnParent = (i >= correctOrderCount + neutralOrderCount) ? + wrongOrderList.Content.RectTransform : + orderButtonFrame.RectTransform; + + var btn = new GUIButton(new RectTransform(new Point(iconSize, iconSize), btnParent, Anchor.CenterLeft), + style: null) + { + UserData = order + }; + + new GUIFrame(new RectTransform(new Vector2(1.5f), btn.RectTransform, Anchor.Center), "OuterGlow") + { + Color = Color.Lerp(order.Color, frame.Color, 0.5f) * 0.8f, + HoverColor = Color.Lerp(order.Color, frame.Color, 0.5f) * 1.0f, + PressedColor = Color.Lerp(order.Color, frame.Color, 0.5f) * 0.6f, + UserData = "selected", + CanBeFocused = false, + Visible = false + }; + + var img = new GUIImage(new RectTransform(Vector2.One, btn.RectTransform), order.Prefab.SymbolSprite); + img.Scale = iconSize / (float)img.SourceRect.Width; + img.Color = Color.Lerp(order.Color, frame.Color, 0.5f); + img.ToolTip = order.Name; + img.HoverColor = Color.Lerp(img.Color, Color.White, 0.5f); + + btn.OnClicked += (GUIButton button, object userData) => + { + if (Character.Controlled == null || Character.Controlled.SpeechImpediment >= 100.0f) return false; + + if (btn.GetChildByUserData("selected").Visible) + { + SetCharacterOrder(character, Order.PrefabList.Find(o => o.AITag == "dismissed"), null, Character.Controlled); + } + else + { + if (order.ItemComponentType != null || order.ItemIdentifiers.Length > 0 || order.Options.Length > 1) + { + CreateOrderTargetFrame(button, character, order); + } + else + { + SetCharacterOrder(character, order, null, Character.Controlled); + } + } + return true; + }; + btn.UserData = order; + btn.ToolTip = order.Name; + + //divider between different groups of orders + if (i == correctOrderCount - 1 || i == correctOrderCount + neutralOrderCount - 1) + { + //TODO: divider sprite + new GUIFrame(new RectTransform(new Point(8, iconSize), orderButtonFrame.RectTransform), style: "GUIButton"); + } + } + + var toggleWrongOrderBtn = new GUIButton(new RectTransform(new Point((int)(30 * GUI.Scale), wrongOrderList.Rect.Height), wrongOrderList.Content.RectTransform), + "", style: "UIToggleButton") + { + UserData = "togglewrongorder", + CanBeFocused = false + }; + + wrongOrderList.RectTransform.NonScaledSize = new Point( + wrongOrderList.Content.Children.Sum(c => c.Rect.Width + wrongOrderList.Spacing), + wrongOrderList.RectTransform.NonScaledSize.Y); + wrongOrderList.RectTransform.SetAsLastChild(); + + new GUIFrame(new RectTransform(new Point( + wrongOrderList.Rect.Width - toggleWrongOrderBtn.Rect.Width - wrongOrderList.Spacing * 2, + wrongOrderList.Rect.Height), wrongOrderList.Content.RectTransform), + style: null) + { + CanBeFocused = false + }; + + //scale to fit the content + orderButtonFrame.RectTransform.NonScaledSize = new Point( + orderButtonFrame.Children.Sum(c => c.Rect.Width + orderButtonFrame.AbsoluteSpacing), + orderButtonFrame.RectTransform.NonScaledSize.Y); + + frame.RectTransform.NonScaledSize = new Point( + characterInfoWidth + spacing + (orderButtonFrame.Rect.Width - wrongOrderList.Rect.Width), + frame.RectTransform.NonScaledSize.Y); + + characterListBox.RectTransform.NonScaledSize = new Point( + characterListBox.Content.Children.Max(c => c.Rect.Width) + wrongOrderList.Rect.Width, + characterListBox.RectTransform.NonScaledSize.Y); + characterListBox.Content.RectTransform.NonScaledSize = characterListBox.RectTransform.NonScaledSize; + characterListBox.UpdateScrollBarSize(); + return frame; } private IEnumerable KillCharacterAnim(GUIComponent component) @@ -778,6 +953,12 @@ namespace Barotrauma } return; } + List availableSpeakers = Character.CharacterList.FindAll(c => + c.AIController is HumanAIController && + !c.IsDead && + c.SpeechImpediment <= 100.0f); + pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers)); + } character.SetOrder(order, option, orderGiver, speak: orderGiver != character); if (IsSinglePlayer) @@ -835,19 +1016,23 @@ namespace Barotrauma } } } - //only one target (or an order with no particular targets), just show options - else + + character.SetOrder(order, option, orderGiver, speak: orderGiver != character); + if (IsSinglePlayer) { - orderTargetFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.2f + order.Options.Length * 0.1f, 0.18f), GUI.Canvas) - { AbsoluteOffset = new Point(orderButton.Rect.Center.X, orderButton.Rect.Bottom) }, - isHorizontal: true, childAnchor: Anchor.BottomLeft) + orderGiver?.Speak( + order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null); + } + else if (orderGiver != null) + { + OrderChatMessage msg = new OrderChatMessage(order, option, order.TargetItemComponent?.Item, character, orderGiver); + if (GameMain.Client != null) { - UserData = character, - Stretch = true - }; - //line connecting the order button to the option buttons - //TODO: sprite - new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), orderTargetFrame.RectTransform), style: null); + GameMain.Client.SendChatMessage(msg); + } + } + DisplayCharacterOrder(character, order); + } /// /// Create the UI panel that's used to select the target and options for a given order diff --git a/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs b/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs index 3e479aa80..c33382963 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs @@ -795,7 +795,7 @@ namespace Barotrauma base.Draw(spriteBatch); - if (hideButton != null && hideButton.Visible) + if (hideButton != null && hideButton.Visible && !Locked) { hideButton.DrawManually(spriteBatch, alsoChildren: true); } @@ -837,6 +837,7 @@ namespace Barotrauma color = Color.White; highlightedQuickUseSlot = slots[i]; } + if (Locked) { color *= 0.3f; } var quickUseIndicator = Items[i].AllowedSlots.Any(a => a == InvSlotType.Any) ? EquipIndicator : DropIndicator; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs b/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs index 8fb30030a..e3c4ea985 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Inventory.cs @@ -820,8 +820,9 @@ namespace Barotrauma else { Sprite slotSprite = slot.SlotSprite ?? slotSpriteSmall; - - spriteBatch.Draw(slotSprite.Texture, rect, slotSprite.SourceRect, slot.IsHighlighted ? Color.White : Color.White * 0.8f); + Color slotColor = slot.IsHighlighted ? Color.White : Color.White * 0.8f; + if (inventory != null && inventory.Locked) { slotColor = Color.Gray * 0.5f; } + spriteBatch.Draw(slotSprite.Texture, rect, slotSprite.SourceRect, slotColor); if (item != null && drawItem) { @@ -873,14 +874,17 @@ namespace Barotrauma } indicatorSprite.Draw(spriteBatch, containedIndicatorArea.Center.ToVector2(), - Color.DarkGray * 0.9f, + (inventory != null && inventory.Locked) ? Color.DarkGray * 0.5f : Color.DarkGray * 0.9f, origin: indicatorSprite.size / 2, rotate: 0.0f, scale: indicatorScale); - + + Color indicatorColor = ToolBox.GradientLerp(containedState, Color.Red, Color.Orange, Color.Green); + if (inventory != null && inventory.Locked) { indicatorColor *= 0.5f; } + spriteBatch.Draw(indicatorSprite.Texture, containedIndicatorArea.Center.ToVector2(), sourceRectangle: new Rectangle(indicatorSprite.SourceRect.Location, new Point((int)(indicatorSprite.SourceRect.Width * containedState), indicatorSprite.SourceRect.Height)), - color: ToolBox.GradientLerp(containedState, Color.Red, Color.Orange, Color.Green), + color: indicatorColor, rotation: 0.0f, origin: indicatorSprite.size / 2, scale: indicatorScale, @@ -920,6 +924,7 @@ namespace Barotrauma } Color spriteColor = sprite == item.Sprite ? item.GetSpriteColor() : item.GetInventoryIconColor(); + if (inventory != null && inventory.Locked) { spriteColor *= 0.5f; } if (CharacterHealth.OpenHealthWindow != null && !item.UseInHealthInterface) { spriteColor = Color.Lerp(spriteColor, Color.TransparentBlack, 0.5f); @@ -931,7 +936,10 @@ namespace Barotrauma sprite.Draw(spriteBatch, itemPos, spriteColor, rotation, scale); } - if (inventory != null && Character.Controlled?.Inventory == inventory && slot.QuickUseKey != Keys.None) + if (inventory != null && + !inventory.Locked && + Character.Controlled?.Inventory == inventory && + slot.QuickUseKey != Keys.None) { GUI.DrawString(spriteBatch, rect.Location.ToVector2(), slot.QuickUseKey.ToString().Substring(1, 1), diff --git a/Barotrauma/BarotraumaClient/Source/Map/Structure.cs b/Barotrauma/BarotraumaClient/Source/Map/Structure.cs index 08a9b5ae0..99b7bbedd 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/Structure.cs @@ -1,6 +1,9 @@ using Barotrauma.Extensions; using Barotrauma.Lights; using Barotrauma.Networking; +using FarseerPhysics; +using FarseerPhysics.Dynamics; +using FarseerPhysics.Dynamics.Contacts; using Lidgren.Network; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; @@ -165,6 +168,20 @@ namespace Barotrauma { Vector2 pos = ConvertUnits.ToDisplayUnits(f2.Body.Position); + int section = FindSectionIndex(pos); + if (section > -1) + { + Vector2 normal = contact.Manifold.LocalNormal; + + float impact = Vector2.Dot(f2.Body.LinearVelocity, -normal) * f2.Body.Mass * 0.1f; + if (impact > 10.0f) + { + SoundPlayer.PlayDamageSound("StructureBlunt", impact, SectionPosition(section, true), tags: Tags); + } + } + } + } + public override bool IsVisible(Rectangle worldView) { Rectangle worldRect = WorldRect; diff --git a/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs b/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs index 9a729b108..94131a5b3 100644 --- a/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs +++ b/Barotrauma/BarotraumaClient/Source/Networking/SteamManager.cs @@ -601,7 +601,7 @@ namespace Barotrauma.Steam { // TODO: If you create a new mod via the workshop interface and enable it, it will show the error msg, but still allows you to enable the content. - if (File.Exists(newContentPackagePath)) + if (File.Exists(newContentPackagePath) && !CheckFileEquality(newContentPackagePath, metaDataFilePath)) { errorMsg = TextManager.Get("WorkshopErrorOverwriteOnEnable") .Replace("[itemname]", item.Title) @@ -613,7 +613,7 @@ namespace Barotrauma.Steam foreach (ContentFile contentFile in contentPackage.Files) { string sourceFile = Path.Combine(item.Directory.FullName, contentFile.Path); - if (File.Exists(sourceFile) && File.Exists(contentFile.Path)) + if (File.Exists(sourceFile) && File.Exists(contentFile.Path) && !CheckFileEquality(sourceFile, contentFile.Path)) { errorMsg = TextManager.Get("WorkshopErrorOverwriteOnEnable") .Replace("[itemname]", item.Title) @@ -685,6 +685,22 @@ namespace Barotrauma.Steam return true; } + private static bool CheckFileEquality(string filePath1, string filePath2) + { + if (filePath1 == filePath2) + { + return true; + } + + using (FileStream fs1 = File.OpenRead(filePath1)) + using (FileStream fs2 = File.OpenRead(filePath2)) + { + Md5Hash hash1 = new Md5Hash(fs1); + Md5Hash hash2 = new Md5Hash(fs2); + return hash1.Hash == hash2.Hash; + } + } + /// /// Disables a workshop item by removing the files from the game folder. /// diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs b/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs index bb177e46c..f6052096e 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs @@ -28,6 +28,8 @@ namespace Barotrauma private GUILayoutGroup subPreviewContainer; + private GUILayoutGroup subPreviewContainer; + private GUIButton loadGameButton; public Action StartNewGame; diff --git a/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs b/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs index 3c1705000..515bc987a 100644 --- a/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs +++ b/Barotrauma/BarotraumaClient/Source/Sounds/SoundPlayer.cs @@ -238,7 +238,7 @@ namespace Barotrauma } float ambienceVolume = 0.8f; - if (Character.Controlled != null) + if (Character.Controlled != null && !Character.Controlled.Removed) { AnimController animController = Character.Controlled.AnimController; if (animController.HeadInWater) diff --git a/Barotrauma/BarotraumaServer/Server.csproj b/Barotrauma/BarotraumaServer/Server.csproj index 3647a5b6d..bc97ad59e 100644 --- a/Barotrauma/BarotraumaServer/Server.csproj +++ b/Barotrauma/BarotraumaServer/Server.csproj @@ -216,7 +216,7 @@ - + PreserveNewest diff --git a/Barotrauma/BarotraumaServer/Source/GameMain.cs b/Barotrauma/BarotraumaServer/Source/GameMain.cs index 279693165..ebf4486e2 100644 --- a/Barotrauma/BarotraumaServer/Source/GameMain.cs +++ b/Barotrauma/BarotraumaServer/Source/GameMain.cs @@ -145,6 +145,23 @@ namespace Barotrauma } } + /// + /// Returns the file paths of all files of the given type in the content packages. + /// + /// + /// If true, also returns files in content packages that are installed but not currently selected. + public IEnumerable GetFilesOfType(ContentType type, bool searchAllContentPackages = false) + { + if (searchAllContentPackages) + { + return ContentPackage.GetFilesOfType(ContentPackage.List, type); + } + else + { + return ContentPackage.GetFilesOfType(SelectedPackages, type); + } + } + /// /// Returns the file paths of all files of the given type in the currently selected content packages. /// diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs index 0fdae7613..f92358571 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/EnemyAIController.cs @@ -320,8 +320,9 @@ namespace Barotrauma { State = AIState.Idle; } - else if (Character.Health < fleeHealthThreshold) + else if (Character.Health < fleeHealthThreshold && SwarmBehavior == null) { + // Don't flee from damage if in a swarm. State = AIState.Escape; } else if (targetingPriority != null) @@ -330,8 +331,6 @@ namespace Barotrauma } } - LatchOntoAI?.Update(this, deltaTime); - if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed)) { State = AIState.Idle; @@ -369,8 +368,15 @@ namespace Barotrauma default: throw new NotImplementedException(); } - - SwarmBehavior?.Update(deltaTime); + + LatchOntoAI?.Update(this, deltaTime); + IsSteeringThroughGap = false; + if (SwarmBehavior != null) + { + SwarmBehavior.IsActive = State == AIState.Idle && Character.CurrentHull == null; + SwarmBehavior.Refresh(); + SwarmBehavior.UpdateSteering(deltaTime); + } steeringManager.Update(Character.AnimController.GetCurrentSpeed(run)); } @@ -493,7 +499,7 @@ namespace Barotrauma } else { - if (!IsProperlyLatchedOnSub) + if (!IsLatchedOnSub) { UpdateWallTarget(); } @@ -552,7 +558,7 @@ namespace Barotrauma { WallSection section = wallTarget.Structure.GetSection(wallTarget.SectionIndex); Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true); - if (section?.gap != null && section.gap.IsRoomToRoom && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime)) + if (section?.gap != null && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime)) { return; } @@ -794,9 +800,21 @@ namespace Barotrauma return false; } + public bool IsSteeringThroughGap { get; private set; } private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime) { + IsSteeringThroughGap = true; + SelectedAiTarget = wall.AiTarget; + wallTarget = null; + LatchOntoAI?.DeattachFromBody(); + Character.AnimController.ReleaseStuckLimbs(); Hull targetHull = section.gap?.FlowTargetHull; + float distance = Vector2.Distance(Character.WorldPosition, targetWorldPos); + float maxDistance = Math.Min(wall.Rect.Width, wall.Rect.Height); + if (distance > maxDistance) + { + return false; + } if (targetHull != null) { if (wall.IsHorizontal) @@ -807,16 +825,7 @@ namespace Barotrauma { targetWorldPos.X = targetHull.WorldRect.Center.X; } - LatchOntoAI?.DeattachFromBody(); - Character.AnimController.ReleaseStuckLimbs(); - if (steeringManager is IndoorsSteeringManager) - { - steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition)); - } - else - { - steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetWorldPos)); - } + steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition)); return true; } return false; @@ -1048,87 +1057,43 @@ namespace Barotrauma #region Targeting private bool IsLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub; - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - - private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure; - //goes through all the AItargets, evaluates how preferable it is to attack the target, //whether the Character can see/hear the target and chooses the most preferable target within //sight/hearing range public AITarget UpdateTargets(Character character, out TargetingPriority priority) { - if (IsProperlyLatchedOnSub) + if ((SelectedAiTarget != null || wallTarget != null) && IsLatchedOnSub) { - // If attached to a valid target, just keep the target. - // Priority not used in this case. - priority = null; - return SelectedAiTarget; + var wall = SelectedAiTarget.Entity as Structure; + if (wall == null) + { + wall = wallTarget.Structure; + } + // The target is not a wall or it's not the same as we are attached to -> release + bool releaseTarget = wall == null || !wall.Bodies.Contains(LatchOntoAI.AttachJoints[0].BodyB); + if (!releaseTarget) + { + for (int i = 0; i < wall.Sections.Length; i++) + { + if (CanPassThroughHole(wall, i)) + { + releaseTarget = true; + } + } + } + if (releaseTarget) + { + SelectedAiTarget = null; + wallTarget = null; + LatchOntoAI.DeattachFromBody(); + } + else if (SelectedAiTarget?.Entity == wallTarget?.Structure) + { + // If attached to a valid target, just keep the target. + // Priority not used in this case. + priority = null; + return SelectedAiTarget; + } } AITarget newTarget = null; priority = null; @@ -1211,7 +1176,7 @@ namespace Barotrauma else if (target.Entity != null) { //skip the target if it's a room and the character is already inside a sub - if (character.CurrentHull != null && target.Entity is Hull) continue; + if (character.CurrentHull != null && target.Entity is Hull) { continue; } Door door = null; if (target.Entity is Item item) @@ -1240,23 +1205,37 @@ namespace Barotrauma // Ignore structures that doesn't have a body (not walls) continue; } - // Ignore walls when inside. - valueModifier = character.CurrentHull == null ? 1 : 0; + if (s.IsPlatform) + { + continue; + } + if (character.CurrentHull != null) + { + // Ignore walls when inside. + continue; + } + valueModifier = 1; + float wallMaxHealth = 400; // Anything more than this is ignored -> 200 = 1 + // Prefer weaker targets. + valueModifier *= MathHelper.Lerp(1.5f, 0.5f, MathUtils.InverseLerp(0, 1, s.Health / wallMaxHealth)); if (aggressiveBoarding) { + var hulls = s.Submarine.GetHulls(false); for (int i = 0; i < s.Sections.Length; i++) { var section = s.Sections[i]; - if (CanPassThroughHole(s, i)) + if (section.gap != null) { - // Ignore walls that can be passed through - valueModifier = 0; - break; - } - else if (section.gap != null) - { - // up to 100% priority increase for every gap in the wall - valueModifier *= 1 + section.gap.Open; + if (CanPassThroughHole(s, i)) + { + bool leadsInside = !section.gap.IsRoomToRoom && section.gap.FlowTargetHull != null && hulls.Any(h => h.Rect.Intersects(section.rect)); + valueModifier *= leadsInside ? 5 : 0; + } + else + { + // up to 100% priority increase for every gap in the wall + valueModifier *= 1 + section.gap.Open; + } } } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs index c85b2caa7..e68f9a3a2 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/LatchOntoAI.cs @@ -54,7 +54,7 @@ namespace Barotrauma get { return attachJoints.Count > 0; } } - public bool IsAttachedToSub => IsAttached && attachTargetBody?.UserData is Entity entity && (entity is Submarine sub || entity?.Submarine != null); + public bool IsAttachedToSub => IsAttached && (attachTargetBody?.UserData is Submarine || attachTargetBody?.UserData is Entity entity && entity.Submarine != null); public LatchOntoAI(XElement element, EnemyAIController enemyAI) { @@ -190,7 +190,7 @@ namespace Barotrauma case AIController.AIState.Attack: if (enemyAI.AttackingLimb != null) { - if (attachToSub && wallAttachPos != Vector2.Zero && attachTargetBody != null) + if (attachToSub && !enemyAI.IsSteeringThroughGap && wallAttachPos != Vector2.Zero && attachTargetBody != null) { // is not attached or is attached to something else if (!IsAttached || IsAttached && attachJoints[0].BodyB == attachTargetBody) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/SwarmBehavior.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/SwarmBehavior.cs index 3eb217589..76adf9000 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/SwarmBehavior.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/SwarmBehavior.cs @@ -14,11 +14,16 @@ namespace Barotrauma private float maxDistFromCenter; private float cohesion; - private List members = new List(); + public List Members { get; private set; } = new List(); + public HashSet ActiveMembers { get; private set; } = new HashSet(); - private AIController ai; + private EnemyAIController ai; - public SwarmBehavior(XElement element, AIController ai) + public bool IsActive { get; set; } + public bool IsEnoughMembers => ActiveMembers.Count > 1; + + + public SwarmBehavior(XElement element, EnemyAIController ai) { this.ai = ai; minDistFromClosest = ConvertUnits.ToSimUnits(element.GetAttributeFloat("mindistfromclosest", 10.0f)); @@ -32,21 +37,36 @@ namespace Barotrauma { if (character.AIController is EnemyAIController enemyAI && enemyAI.SwarmBehavior != null) { - enemyAI.SwarmBehavior.members = swarm.ToList(); + enemyAI.SwarmBehavior.Members = swarm.ToList(); } } } - public void Update(float deltaTime) + public void Refresh() { - members.RemoveAll(m => m.IsDead || m.Removed); - if (members.Count < 2) { return; } + Members.RemoveAll(m => m.IsDead || m.Removed); + foreach (var member in Members) + { + if (!member.AIController.Enabled && member.IsRemotePlayer || Character.Controlled == member || !((EnemyAIController)member.AIController).SwarmBehavior.IsActive) + { + ActiveMembers.Remove(member); + } + else + { + ActiveMembers.Add(member); + } + } + } + public void UpdateSteering(float deltaTime) + { + if (!IsActive) { return; } + if (!IsEnoughMembers) { return; } //calculate the "center of mass" of the swarm and the distance to the closest character in the swarm float closestDistSqr = float.MaxValue; Vector2 center = Vector2.Zero; AICharacter closest = null; - foreach (AICharacter member in members) + foreach (AICharacter member in Members) { center += member.SimPosition; if (member == ai.Character) { continue; } @@ -57,7 +77,7 @@ namespace Barotrauma closest = member; } } - center /= members.Count; + center /= Members.Count; if (closest == null) { return; } @@ -83,11 +103,11 @@ namespace Barotrauma if (cohesion > 0.0f) { Vector2 avgVel = Vector2.Zero; - foreach (AICharacter member in members) + foreach (AICharacter member in Members) { avgVel += member.AnimController.TargetMovement; } - avgVel /= members.Count; + avgVel /= Members.Count; ai.SteeringManager.SteeringManual(deltaTime, avgVel * cohesion); } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs index 5de8c9140..3530df734 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Animation/HumanoidAnimController.cs @@ -560,7 +560,8 @@ namespace Barotrauma //TODO: take into account that the feet aren't necessarily in CurrentHull //full slowdown (1.5f) when water is up to the torso surfaceY = ConvertUnits.ToSimUnits(currentHull.Surface); - slowdownAmount = MathHelper.Clamp((surfaceY - colliderPos.Y) / TorsoPosition.Value, 0.0f, 1.0f) * 1.5f; + float bottomPos = Math.Max(colliderPos.Y, currentHull.Rect.Y - currentHull.Rect.Height); + slowdownAmount = MathHelper.Clamp((surfaceY - bottomPos) / TorsoPosition.Value, 0.0f, 1.0f) * 1.5f; } float maxSpeed = Math.Max(TargetMovement.Length() - slowdownAmount, 1.0f); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs index b0d652965..2d85ced37 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/Character.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/Character.cs @@ -853,7 +853,7 @@ namespace Barotrauma { if (characterConfigFiles == null) { - characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character); + characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character, searchAllContentPackages: true); } return characterConfigFiles; } @@ -2593,152 +2593,14 @@ namespace Barotrauma if (info != null) { info.Remove(); } -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - -#if CLIENT - GameMain.GameSession?.CrewManager?.RemoveCharacter(this); -#endif - #if CLIENT GameMain.GameSession?.CrewManager?.RemoveCharacter(this); #endif CharacterList.Remove(this); + if (Controlled == this) { Controlled = null; } + if (Inventory != null) { foreach (Item item in Inventory.Items) diff --git a/Barotrauma/BarotraumaShared/Source/Events/Missions/MonsterMission.cs b/Barotrauma/BarotraumaShared/Source/Events/Missions/MonsterMission.cs index 01905d2cf..6e8445416 100644 --- a/Barotrauma/BarotraumaShared/Source/Events/Missions/MonsterMission.cs +++ b/Barotrauma/BarotraumaShared/Source/Events/Missions/MonsterMission.cs @@ -34,14 +34,33 @@ namespace Barotrauma { Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos); - bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient; + //bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient; + //for (int i = 0; i < monsterCount; i++) + //{ + // monsters.Add(Character.Create(monsterFile, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false)); + //} + //monsters.ForEach(m => m.Enabled = false); + //SwarmBehavior.CreateSwarm(monsters.Cast()); + //sonarPositions.Add(spawnPos); + + float offsetAmount = 500; for (int i = 0; i < monsterCount; i++) { - monsters.Add(Character.Create(monsterFile, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false)); + CoroutineManager.InvokeAfter(() => + { + bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient; + var monster = Character.Create(monsterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true); + monster.Enabled = false; + monsters.Add(monster); + if (monsters.Count == monsterCount) + { + //this will do nothing if the monsters have no swarm behavior defined, + //otherwise it'll make the spawned characters act as a swarm + SwarmBehavior.CreateSwarm(monsters.Cast()); + sonarPositions.Add(spawnPos); + } + }, Rand.Range(0f, monsterCount / 2, Rand.RandSync.Server)); } - monsters.ForEach(m => m.Enabled = false); - SwarmBehavior.CreateSwarm(monsters.Cast()); - sonarPositions.Add(spawnPos); } public override void Update(float deltaTime) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs index c1b77efe2..a52279d9b 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs @@ -183,6 +183,25 @@ namespace Barotrauma.Items.Components } } + if (targetItem.Prefab.DeconstructItems.Any()) + { + inputContainer.Inventory.RemoveItem(targetItem); + Entity.Spawner.AddToRemoveQueue(targetItem); + MoveInputQueue(); + PutItemsToLinkedContainer(); + } + else + { + if (outputContainer.Inventory.Items.All(i => i != null)) + { + targetItem.Drop(dropper: null); + } + else + { + outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true); + } + } + if (targetItem.Prefab.DeconstructItems.Any()) { inputContainer.Inventory.RemoveItem(targetItem); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs index 795440616..0674979bd 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs @@ -627,6 +627,19 @@ namespace Barotrauma.Items.Components return true; } + public override void OnItemLoaded() + { + sonar = item.GetComponent(); + } + + public override bool Select(Character character) + { + if (!CanBeSelected) return false; + + user = character; + return true; + } + public override void Update(float deltaTime, Camera cam) { networkUpdateTimer -= deltaTime; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index 039f3a699..9f7be76bd 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -1216,6 +1216,10 @@ namespace Barotrauma { ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime); } + if (!broken) + { + ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime); + } ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime); if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; } diff --git a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs index c79dffd0c..22934e772 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs @@ -736,6 +736,25 @@ namespace Barotrauma } } + public string DisplayName + { + get; + private set; + } + + private string roomName; + [Editable, Serialize("", true, translationTextTag: "RoomName.")] + public string RoomName + { + get { return roomName; } + set + { + if (roomName == value) { return; } + roomName = value; + DisplayName = TextManager.Get(roomName, returnNull: true) ?? roomName; + } + } + public override Rectangle Rect { get diff --git a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs index b07e79468..ad9a18c72 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Structure.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Structure.cs @@ -635,24 +635,6 @@ namespace Barotrauma var character = ((Limb)f2.Body.UserData).character; if (character.DisableImpactDamageTimer > 0.0f || ((Limb)f2.Body.UserData).Mass < 100.0f) return true; } - - if (!Prefab.Platform && Prefab.StairDirection == Direction.None) - { - Vector2 pos = ConvertUnits.ToDisplayUnits(f2.Body.Position); - - int section = FindSectionIndex(pos); - if (section > -1) - { - Vector2 normal = contact.Manifold.LocalNormal; - - float impact = Vector2.Dot(f2.Body.LinearVelocity, -normal) * f2.Body.Mass * 0.1f; - if (impact < 10.0f) return true; -#if CLIENT - SoundPlayer.PlayDamageSound("StructureBlunt", impact, SectionPosition(section, true), tags: Tags); -#endif - AddDamage(section, impact); - } - } OnImpactProjSpecific(f1, f2, contact); diff --git a/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs b/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs index d423f76a6..95575a22c 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/SubmarineBody.cs @@ -468,7 +468,7 @@ namespace Barotrauma var gaps = newHull?.ConnectedGaps ?? Gap.GapList.Where(g => g.Submarine == submarine); targetPos = character.WorldPosition; - Gap adjacentGap = Gap.FindAdjacent(gaps, targetPos, 200.0f); + Gap adjacentGap = Gap.FindAdjacent(gaps, targetPos, 500.0f); if (adjacentGap == null) return true; if (newHull != null) diff --git a/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs b/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs index c2cd7710d..e89da30e1 100644 --- a/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs +++ b/Barotrauma/BarotraumaShared/Source/Networking/EntitySpawner.cs @@ -102,7 +102,7 @@ namespace Barotrauma { string errorMsg = "Attempted to add a null item to entity spawn queue.\n" + Environment.StackTrace; DebugConsole.ThrowError(errorMsg); - GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue1:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, worldPosition, condition)); @@ -115,7 +115,7 @@ namespace Barotrauma { string errorMsg = "Attempted to add a null item to entity spawn queue.\n" + Environment.StackTrace; DebugConsole.ThrowError(errorMsg); - GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); + GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue2:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return; } spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, position, sub, condition)); diff --git a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs index 8ad826eea..e13028523 100644 --- a/Barotrauma/BarotraumaShared/Source/PlayerInput.cs +++ b/Barotrauma/BarotraumaShared/Source/PlayerInput.cs @@ -162,21 +162,6 @@ namespace Barotrauma get { return binding; } } - public void SetState() - { - hit = binding.IsHit(); - if (hit) hitQueue = true; - - held = binding.IsDown(); - if (held) heldQueue = true; - } -#endif - - public KeyOrMouse State - { - get { return binding; } - } - public void SetState() { hit = binding.IsHit(); diff --git a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub index cce7eff49..36963f395 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub and b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Humpback.sub b/Barotrauma/BarotraumaShared/Submarines/Humpback.sub index 028008485..50175a38b 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Humpback.sub and b/Barotrauma/BarotraumaShared/Submarines/Humpback.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Orca.sub b/Barotrauma/BarotraumaShared/Submarines/Orca.sub index 75badce72..c7380b459 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Orca.sub and b/Barotrauma/BarotraumaShared/Submarines/Orca.sub differ