diff --git a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs index 8f9276859..bcf133b30 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/Animation/Ragdoll.cs @@ -152,6 +152,32 @@ namespace Barotrauma } + if (character.MemLocalState.Count > 120) character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120); + character.MemState.Clear(); + } + } + + partial void ImpactProjSpecific(float impact, Body body) + { + float volume = MathHelper.Clamp(impact - 3.0f, 0.5f, 1.0f); + + if (body.UserData is Limb limb && character.Stun <= 0f) + { + if (impact > 3.0f) { PlayImpactSound(limb); } + } + else if (body.UserData is Limb || body == Collider.FarseerBody) + { + if (!character.IsRemotePlayer && impact > ImpactTolerance) + { + SoundPlayer.PlayDamageSound("LimbBlunt", strongestImpact, Collider); + } + } + if (Character.Controlled == character) + { + GameMain.GameScreen.Cam.Shake = Math.Min(Math.Max(strongestImpact, GameMain.GameScreen.Cam.Shake), 3.0f); + } + } + if (character.MemState.Count < 1) return; overrideTargetMovement = Vector2.Zero; diff --git a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs index d6be94ac8..a82be27e8 100644 --- a/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs +++ b/Barotrauma/BarotraumaClient/Source/Characters/CharacterHUD.cs @@ -198,9 +198,7 @@ namespace Barotrauma focusName = character.FocusedCharacter.Info.DisplayName; } Vector2 textPos = startPos; - Vector2 offset = GUI.Font.MeasureString(focusName); - - textPos -= new Vector2(offset.X / 2, offset.Y); + textPos -= new Vector2(GUI.Font.MeasureString(focusName).X / 2, 20); Color nameColor = Color.White; if (character.TeamID != character.FocusedCharacter.TeamID) @@ -209,23 +207,23 @@ namespace Barotrauma } GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2); - textPos.Y += offset.Y; + textPos.Y += 20; if (character.FocusedCharacter.CanInventoryBeAccessed) { GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBind(InputType.Grab).ToString()), Color.LightGreen, Color.Black, 2, GUI.SmallFont); - textPos.Y += offset.Y; + textPos.Y += 15; } if (character.FocusedCharacter.CharacterHealth.UseHealthWindow) { GUI.DrawString(spriteBatch, textPos, GetCachedHudText("HealHint", GameMain.Config.KeyBind(InputType.Health).ToString()), Color.LightGreen, Color.Black, 2, GUI.SmallFont); - textPos.Y += offset.Y; + textPos.Y += 15; } if (!string.IsNullOrEmpty(character.FocusedCharacter.customInteractHUDText)) { GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.customInteractHUDText, Color.LightGreen, Color.Black, 2, GUI.SmallFont); - textPos.Y += offset.Y; + textPos.Y += 15; } } @@ -261,10 +259,8 @@ namespace Barotrauma var hudTexts = focusedItem.GetHUDTexts(character); int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X); - - Vector2 offset = GUI.Font.MeasureString(focusedItem.Name); Vector2 startPos = cam.WorldToScreen(focusedItem.DrawPosition); - startPos.Y -= (hudTexts.Count + 1) * offset.Y; + startPos.Y -= (hudTexts.Count + 1) * 20; if (focusedItem.Sprite != null) { startPos.X += (int)(circleSize * 0.4f * dir); @@ -272,17 +268,17 @@ namespace Barotrauma } Vector2 textPos = startPos; - if (dir == -1) textPos.X -= offset.X; + if (dir == -1) textPos.X -= (int)GUI.Font.MeasureString(focusedItem.Name).X; float alpha = MathHelper.Clamp((focusedItemOverlayTimer - ItemOverlayDelay) * 2.0f, 0.0f, 1.0f); GUI.DrawString(spriteBatch, textPos, focusedItem.Name, Color.White * alpha, Color.Black * alpha * 0.7f, 2); - textPos.Y += offset.Y; + textPos.Y += 20.0f; foreach (ColoredText coloredText in hudTexts) { if (dir == -1) textPos.X = (int)(startPos.X - GUI.SmallFont.MeasureString(coloredText.Text).X); GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUI.SmallFont); - textPos.Y += offset.Y; + textPos.Y += 20; } } } diff --git a/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs b/Barotrauma/BarotraumaClient/Source/GameSession/CrewManager.cs index e9bd2ba83..8b52b1fd9 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() @@ -239,27 +243,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) @@ -633,9 +634,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) @@ -779,6 +954,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) @@ -836,19 +1017,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 9da0010c3..c33382963 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/CharacterInventory.cs @@ -35,7 +35,7 @@ namespace Barotrauma private static Dictionary limbSlotIcons; - public const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head; + const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head; private Point screenResolution; @@ -448,8 +448,7 @@ namespace Barotrauma { if (Items[i]?.OwnInventory != null && Items[i].OwnInventory.Capacity == 1 && PersonalSlots.HasFlag(SlotTypes[i])) { - if (Items[i].OwnInventory.Items[0] != null && - Items[i].OwnInventory.Items[0].Condition > 0.0f && + if (Items[i].OwnInventory.Items[0].Condition > 0.0f && Items[i].OwnInventory.Items[0].Condition / Items[i].OwnInventory.Items[0].MaxCondition < 0.15f) { hidePersonalSlots = false; diff --git a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs index 3f95b5103..6b447391c 100644 --- a/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs +++ b/Barotrauma/BarotraumaClient/Source/Items/Components/Machines/Fabricator.cs @@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components Stretch = true, UserData = "filterarea" }; - new GUITextBlock(new RectTransform(new Vector2(0.25f, 1.0f), filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.Font); + new GUITextBlock(new RectTransform(new Vector2(0.25f, 1.0f), filterArea.RectTransform), TextManager.Get("FilterMapEntities"), font: GUI.Font); itemFilterBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), font: GUI.Font); itemFilterBox.OnTextChanged += (textBox, text) => { FilterEntities(text); return true; }; var clearButton = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), filterArea.RectTransform), "x") diff --git a/Barotrauma/BarotraumaClient/Source/Map/ItemAssemblyPrefab.cs b/Barotrauma/BarotraumaClient/Source/Map/ItemAssemblyPrefab.cs index 00eb633c9..02184babd 100644 --- a/Barotrauma/BarotraumaClient/Source/Map/ItemAssemblyPrefab.cs +++ b/Barotrauma/BarotraumaClient/Source/Map/ItemAssemblyPrefab.cs @@ -20,7 +20,7 @@ namespace Barotrauma { Rectangle drawRect = entity.Second; drawRect = new Rectangle( - (int)(drawRect.X * scale) + drawArea.Center.X, (int)((drawRect.Y) * scale) - drawArea.Center.Y, + (int)(drawRect.X * scale) + drawArea.Center.X, -((int)((drawRect.Y - drawRect.Height) * scale) + drawArea.Center.Y), (int)(drawRect.Width * scale), (int)(drawRect.Height * scale)); entity.First.DrawPlacing(spriteBatch, drawRect, entity.First.Scale * scale); } diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs b/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs index 1a03dace0..f3d5780e1 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CampaignSetupUI.cs @@ -444,6 +444,8 @@ namespace Barotrauma private GUILayoutGroup subPreviewContainer; + private GUILayoutGroup subPreviewContainer; + private GUIButton loadGameButton; public Action StartNewGame; @@ -502,7 +504,7 @@ namespace Barotrauma }; subList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.65f), leftColumn.RectTransform)) { ScrollBarVisible = true }; - var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("serverlog.filter"), textAlignment: Alignment.CenterLeft, font: GUI.Font); + var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("FilterMapEntities"), textAlignment: Alignment.CenterLeft, font: GUI.Font); var searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform, Anchor.CenterRight), font: GUI.Font); searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; }; searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; }; diff --git a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs index 11a3a3cc1..95ba8dd5a 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/CampaignUI.cs @@ -180,7 +180,7 @@ namespace Barotrauma { Stretch = true }; - var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("serverlog.filter"), textAlignment: Alignment.CenterLeft, font: GUI.Font); + var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("FilterMapEntities"), textAlignment: Alignment.CenterLeft, font: GUI.Font); searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform), font: GUI.Font); searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; }; searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; }; diff --git a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs index 287ac0265..96c441896 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/MainMenuScreen.cs @@ -789,6 +789,10 @@ namespace Barotrauma GUI.Draw(Cam, spriteBatch); + spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, GameMain.ScissorTestEnable); + + GUI.Draw(Cam, spriteBatch); + GUI.Draw(Cam, spriteBatch); #if DEBUG diff --git a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs index 55cc013c7..f0ae24fe4 100644 --- a/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs +++ b/Barotrauma/BarotraumaClient/Source/Screens/SubEditorScreen.cs @@ -300,7 +300,7 @@ namespace Barotrauma UserData = "filterarea" }; - new GUITextBlock(new RectTransform(new Vector2(0.05f, 1.0f), filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.Font); + new GUITextBlock(new RectTransform(new Vector2(0.05f, 1.0f), filterArea.RectTransform), TextManager.Get("FilterMapEntities"), font: GUI.Font); entityFilterBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), font: GUI.Font); entityFilterBox.OnTextChanged += (textBox, text) => { FilterEntities(text); return true; }; var clearButton = new GUIButton(new RectTransform(new Vector2(0.02f, 1.0f), filterArea.RectTransform), "x") @@ -835,7 +835,6 @@ namespace Barotrauma //make space for the entity menu for (int i = 0; i < dummyCharacter.Inventory.SlotPositions.Length; i++) { - if (CharacterInventory.PersonalSlots.HasFlag(dummyCharacter.Inventory.SlotTypes[i])) { continue; } if (dummyCharacter.Inventory.SlotPositions[i].Y > GameMain.GraphicsHeight / 2) { dummyCharacter.Inventory.SlotPositions[i].Y -= 50 * GUI.Scale; @@ -1334,7 +1333,7 @@ namespace Barotrauma } }; - var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("serverlog.filter"), textAlignment: Alignment.CenterLeft, font: GUI.Font); + var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("FilterMapEntities"), textAlignment: Alignment.CenterLeft, font: GUI.Font); var searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform), font: GUI.Font); searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; }; searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; }; diff --git a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml index 9cd0afff7..3bd2357dd 100644 --- a/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml +++ b/Barotrauma/BarotraumaShared/Data/ContentPackages/Vanilla 0.9.xml @@ -74,7 +74,6 @@ - diff --git a/Barotrauma/BarotraumaShared/SharedContent.projitems b/Barotrauma/BarotraumaShared/SharedContent.projitems index f22b55289..6313bfa09 100644 --- a/Barotrauma/BarotraumaShared/SharedContent.projitems +++ b/Barotrauma/BarotraumaShared/SharedContent.projitems @@ -391,9 +391,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -496,9 +493,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -2177,9 +2171,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs index 75179846e..9574f72fa 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/HumanAIController.cs @@ -409,9 +409,40 @@ namespace Barotrauma CurrentOrderOption = option; CurrentOrder = order; objectiveManager.SetOrder(order, option, orderGiver); - if (speak && Character.SpeechImpediment < 100.0f) + if (ObjectiveManager.CurrentOrder != null && speak && Character.SpeechImpediment < 100.0f) { - Character.Speak(TextManager.Get("DialogAffirmative"), null, 1.0f); + if (ObjectiveManager.CurrentOrder is AIObjectiveRepairItems repairItems && repairItems.Targets.None()) + { + Character.Speak(TextManager.Get("DialogNoRepairTargets"), null, 3.0f, "norepairtargets"); + } + else if (ObjectiveManager.CurrentOrder is AIObjectiveChargeBatteries chargeBatteries && chargeBatteries.Targets.None()) + { + Character.Speak(TextManager.Get("DialogNoBatteries"), null, 3.0f, "nobatteries"); + } + else if (ObjectiveManager.CurrentOrder is AIObjectiveExtinguishFires extinguishFires && extinguishFires.Targets.None()) + { + Character.Speak(TextManager.Get("DialogNoFire"), null, 3.0f, "nofire"); + } + else if (ObjectiveManager.CurrentOrder is AIObjectiveFixLeaks fixLeaks && fixLeaks.Targets.None()) + { + Character.Speak(TextManager.Get("DialogNoLeaks"), null, 3.0f, "noleaks"); + } + else if (ObjectiveManager.CurrentOrder is AIObjectiveFightIntruders fightIntruders && fightIntruders.Targets.None()) + { + Character.Speak(TextManager.Get("DialogNoEnemies"), null, 3.0f, "noenemies"); + } + else if (ObjectiveManager.CurrentOrder is AIObjectiveRescueAll rescueAll && rescueAll.Targets.None()) + { + Character.Speak(TextManager.Get("DialogNoRescueTargets"), null, 3.0f, "norescuetargets"); + } + else if (ObjectiveManager.CurrentOrder is AIObjectivePumpWater pumpWater && pumpWater.Targets.None()) + { + Character.Speak(TextManager.Get("DialogNoPumps"), null, 3.0f, "nopumps"); + } + else + { + Character.Speak(TextManager.Get("DialogAffirmative"), null, 1.0f); + } } SetOrderProjSpecific(order); } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs index b991f524f..9f23a116a 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjective.cs @@ -106,6 +106,18 @@ namespace Barotrauma subObjectives.Add(objective); } + public void RemoveSubObjective(ref T objective) where T : AIObjective + { + if (objective != null) + { + if (subObjectives.Contains(objective)) + { + subObjectives.Remove(objective); + } + objective = null; + } + } + public void SortSubObjectives() { if (subObjectives.None()) { return; } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveChargeBatteries.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveChargeBatteries.cs index 92522e7b2..095989fbe 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveChargeBatteries.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveChargeBatteries.cs @@ -20,15 +20,6 @@ namespace Barotrauma return otherObjective is AIObjectiveChargeBatteries other && other.Option == Option; } - protected override void FindTargets() - { - base.FindTargets(); - if (targets.None() && objectiveManager.CurrentOrder == this) - { - character.Speak(TextManager.Get("DialogNoBatteries"), null, 4.0f, "nobatteries", 10.0f); - } - } - protected override bool Filter(PowerContainer battery) { if (battery == null) { return false; } @@ -54,12 +45,12 @@ namespace Barotrauma { if (Option == "charge") { - return targets.Max(t => MathHelper.Lerp(100, 0, Math.Abs(PowerContainer.aiRechargeTargetRatio - t.RechargeRatio))); + return Targets.Max(t => MathHelper.Lerp(100, 0, Math.Abs(PowerContainer.aiRechargeTargetRatio - t.RechargeRatio))); } else { - return targets.Max(t => MathHelper.Lerp(0, 100, t.RechargeRatio)); + return Targets.Max(t => MathHelper.Lerp(0, 100, t.RechargeRatio)); } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs index ce7122b63..68ee23641 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveCombat.cs @@ -11,9 +11,10 @@ namespace Barotrauma class AIObjectiveCombat : AIObjective { public override string DebugTag => "combat"; - public bool useCoolDown = true; - const float CoolDown = 10.0f; + private readonly CombatMode initialMode; + + const float coolDown = 10.0f; public Character Enemy { get; private set; } @@ -25,14 +26,7 @@ namespace Barotrauma { _weapon = value; _weaponComponent = null; - if (reloadWeaponObjective != null) - { - if (subObjectives.Contains(reloadWeaponObjective)) - { - subObjectives.Remove(reloadWeaponObjective); - } - reloadWeaponObjective = null; - } + RemoveSubObjective(ref seekAmmunition); } } private ItemComponent _weaponComponent; @@ -40,6 +34,7 @@ namespace Barotrauma { get { + if (Weapon == null) { return null; } if (_weaponComponent == null) { _weaponComponent = @@ -58,12 +53,14 @@ namespace Barotrauma private readonly HashSet meleeWeapons = new HashSet(); private readonly HashSet adHocWeapons = new HashSet(); - private AIObjectiveContainItem reloadWeaponObjective; + private AIObjectiveContainItem seekAmmunition; private AIObjectiveGoTo retreatObjective; private AIObjectiveGoTo followTargetObjective; private Hull retreatTarget; private float coolDownTimer; + private IEnumerable myBodies; + private float aimTimer; public enum CombatMode { @@ -78,7 +75,7 @@ namespace Barotrauma : base(character, objectiveManager, priorityModifier) { Enemy = enemy; - coolDownTimer = CoolDown; + coolDownTimer = coolDown; findSafety = objectiveManager.GetObjective(); if (findSafety != null) { @@ -86,6 +83,7 @@ namespace Barotrauma findSafety.unreachable.Clear(); } Mode = mode; + initialMode = Mode; if (Enemy == null) { Mode = CombatMode.Retreat; @@ -104,7 +102,7 @@ namespace Barotrauma public override bool IsCompleted() { - bool completed = (Enemy != null && (Enemy.Removed || Enemy.IsDead)) || (useCoolDown && coolDownTimer <= 0); + bool completed = (Enemy != null && (Enemy.Removed || Enemy.IsDead)) || (initialMode != CombatMode.Offensive && coolDownTimer <= 0); if (completed) { if (objectiveManager.CurrentOrder == this && Enemy != null && Enemy.IsDead) @@ -121,65 +119,93 @@ namespace Barotrauma protected override void Act(float deltaTime) { - if (useCoolDown) + if (initialMode != CombatMode.Offensive) { coolDownTimer -= deltaTime; } if (abandon) { return; } - Arm(deltaTime); - Move(deltaTime); + TryArm(); + if (seekAmmunition == null || !subObjectives.Contains(seekAmmunition)) + { + Move(); + if (Weapon != null) + { + OperateWeapon(deltaTime); + } + } } - private void Arm(float deltaTime) + private void Move() { switch (Mode) { case CombatMode.Offensive: + Engage(); + break; case CombatMode.Defensive: - if (Weapon != null && !character.Inventory.Items.Contains(_weapon) || _weaponComponent != null && !_weaponComponent.HasRequiredContainedItems(false)) + case CombatMode.Retreat: + Retreat(); + break; + default: + throw new NotImplementedException(); + } + } + + private bool TryArm() + { + if (Weapon != null) + { + if (!character.Inventory.Items.Contains(Weapon) || WeaponComponent == null) + { + Weapon = null; + } + else if (!WeaponComponent.HasRequiredContainedItems(false)) + { + // Seek ammunition only if cannot find a new weapon + if (!Reload(true, () => GetWeapon() == null)) { - Weapon = null; - } - if (Weapon == null) - { - Weapon = GetWeapon(); - } - if (Weapon == null) - { - Mode = CombatMode.Retreat; - } - if (Equip()) - { - if (Reload(deltaTime)) + if (seekAmmunition != null && subObjectives.Contains(seekAmmunition)) { - Attack(deltaTime); + return false; + } + else + { + Weapon = null; } } - break; - case CombatMode.Retreat: - break; - default: - throw new NotImplementedException(); + } } + if (Weapon == null) + { + Weapon = GetWeapon(); + } + if (Weapon == null) + { + Weapon = GetWeapon(ignoreRequiredItems: true); + } + Mode = Weapon == null ? CombatMode.Retreat : initialMode; + return Weapon != null; } - private void Move(float deltaTime) + private void OperateWeapon(float deltaTime) { switch (Mode) { case CombatMode.Offensive: - Engage(deltaTime); - break; case CombatMode.Defensive: + if (Equip()) + { + Attack(deltaTime); + } + break; case CombatMode.Retreat: - Retreat(deltaTime); break; default: throw new NotImplementedException(); } } - private Item GetWeapon() + private Item GetWeapon(bool ignoreRequiredItems = false) { rangedWeapons.Clear(); meleeWeapons.Clear(); @@ -193,14 +219,14 @@ namespace Barotrauma { if (component is RangedWeapon rw) { - if (rw.HasRequiredContainedItems(false)) + if (ignoreRequiredItems || rw.HasRequiredContainedItems(false)) { rangedWeapons.Add(rw); } } else if (component is MeleeWeapon mw) { - if (mw.HasRequiredContainedItems(false)) + if (ignoreRequiredItems || mw.HasRequiredContainedItems(false)) { meleeWeapons.Add(mw); } @@ -216,7 +242,7 @@ namespace Barotrauma { if (statusEffect.Afflictions.Any()) { - if (component.HasRequiredContainedItems(false)) + if (ignoreRequiredItems || component.HasRequiredContainedItems(false)) { adHocWeapons.Add(item); } @@ -253,19 +279,29 @@ namespace Barotrauma Weapon.Drop(character); } } + return true; } private bool Equip() { - if (!character.SelectedItems.Contains(Weapon)) + if (!WeaponComponent.HasRequiredContainedItems(false)) { + Mode = CombatMode.Retreat; + return false; + } + //if (!character.SelectedItems.Contains(Weapon)) + if (!character.HasEquippedItem(Weapon)) + { + Weapon.TryInteract(character, forceSelectKey: true); var slots = Weapon.AllowedSlots.FindAll(s => s == InvSlotType.LeftHand || s == InvSlotType.RightHand || s == (InvSlotType.LeftHand | InvSlotType.RightHand)); if (character.Inventory.TryPutItem(Weapon, character, slots)) { Weapon.Equip(character); + aimTimer = Rand.Range(0.5f, 1f); } else { + Weapon = null; Mode = CombatMode.Retreat; return false; } @@ -273,16 +309,10 @@ namespace Barotrauma return true; } - private void Retreat(float deltaTime) + private void Retreat() { - if (followTargetObjective != null) - { - if (subObjectives.Contains(followTargetObjective)) - { - subObjectives.Remove(followTargetObjective); - } - followTargetObjective = null; - } + RemoveSubObjective(ref followTargetObjective); + RemoveSubObjective(ref seekAmmunition); if (retreatObjective != null && retreatObjective.Target != retreatTarget) { retreatObjective = null; @@ -294,59 +324,115 @@ namespace Barotrauma TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true)); } - private void Engage(float deltaTime) + private void Engage() { retreatTarget = null; - if (retreatObjective != null) + RemoveSubObjective(ref retreatObjective); + RemoveSubObjective(ref seekAmmunition); + if (followTargetObjective != null && followTargetObjective.Target != Enemy) { - if (subObjectives.Contains(retreatObjective)) - { - subObjectives.Remove(retreatObjective); - } - retreatObjective = null; + followTargetObjective = null; } TryAddSubObjective(ref followTargetObjective, constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true) { AllowGoingOutside = true, IgnoreIfTargetDead = true, - CheckVisibility = true, - CloseEnough = - WeaponComponent is RangedWeapon ? 3 : - WeaponComponent is MeleeWeapon mw ? ConvertUnits.ToSimUnits(mw.Range) : - WeaponComponent is RepairTool rt ? ConvertUnits.ToSimUnits(rt.Range) : 0.5f + CheckVisibility = true }, onAbandon: () => { - SteeringManager.Reset(); Mode = CombatMode.Retreat; + SteeringManager.Reset(); }); + if (followTargetObjective != null && subObjectives.Contains(followTargetObjective)) + { + followTargetObjective.CloseEnough = + WeaponComponent is RangedWeapon ? 3 : + WeaponComponent is MeleeWeapon mw ? ConvertUnits.ToSimUnits(mw.Range) : + WeaponComponent is RepairTool rt ? ConvertUnits.ToSimUnits(rt.Range) : 0.5f; + } } - private bool Reload(float deltaTime) + /// + /// Seeks for more ammunition. Creates a new subobjective. + /// + private void SeekAmmunition(string[] ammunitionIdentifiers) { - if (WeaponComponent != null && WeaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained)) - { - var containedItems = Weapon.ContainedItems; - foreach (RelatedItem requiredItem in WeaponComponent.requiredItems[RelatedItem.RelationType.Contained]) + retreatTarget = null; + RemoveSubObjective(ref retreatObjective); + RemoveSubObjective(ref followTargetObjective); + TryAddSubObjective(ref seekAmmunition, + constructor: () => new AIObjectiveContainItem(character, ammunitionIdentifiers, Weapon.GetComponent(), objectiveManager) { - Item containedItem = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it)); - if (containedItem == null) + targetItemCount = Weapon.GetComponent().Capacity, + checkInventory = false + }, + onAbandon: () => + { + Weapon = null; + Mode = CombatMode.Retreat; + SteeringManager.Reset(); + }); + } + + /// + /// Reloads the ammunition found in the inventory. + /// If seekAmmo is true and the condition is met or not provided, tries to get find the ammo elsewhere. + /// + private bool Reload(bool seekAmmo, Func condition = null) + { + if (WeaponComponent == null) { return false; } + if (!WeaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained)) { return false; } + var containedItems = Weapon.ContainedItems; + RelatedItem item = null; + Item ammunition = null; + string[] ammunitionIdentifiers = null; + foreach (RelatedItem requiredItem in WeaponComponent.requiredItems[RelatedItem.RelationType.Contained]) + { + ammunition = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it)); + if (ammunition != null) + { + // Ammunition still remaining + return true; + } + item = requiredItem; + ammunitionIdentifiers = requiredItem.Identifiers; + } + // No ammo + if (ammunition == null) + { + var container = Weapon.GetComponent(); + // Try reload ammunition in inventory + foreach (string identifier in ammunitionIdentifiers) + { + foreach (var i in character.Inventory.Items) { - TryAddSubObjective(ref reloadWeaponObjective, - constructor: () => new AIObjectiveContainItem(character, requiredItem.Identifiers, Weapon.GetComponent(), objectiveManager), - onAbandon: () => + if (i == null) { continue; } + if (i.Prefab.Identifier == identifier || i.HasTag(identifier)) + { + if (i.Condition > 0) { - SteeringManager.Reset(); - Mode = CombatMode.Retreat; - }); + container.Inventory.TryPutItem(ammunition, null); + } + } } } } - return reloadWeaponObjective == null || reloadWeaponObjective.IsCompleted(); + if (WeaponComponent.HasRequiredContainedItems(false)) + { + return true; + } + else if (ammunition == null) + { + if (seekAmmo && ammunitionIdentifiers != null && (condition == null || condition())) + { + SeekAmmunition(ammunitionIdentifiers); + } + } + return false; } - private IEnumerable myBodies; private void Attack(float deltaTime) { float squaredDistance = Vector2.DistanceSquared(character.Position, Enemy.Position); @@ -371,6 +457,16 @@ namespace Barotrauma character.SetInput(InputType.Aim, false, true); } } + bool isFacing = character.AnimController.Dir > 0 && Enemy.WorldPosition.X > character.WorldPosition.X || character.AnimController.Dir < 0 && Enemy.WorldPosition.X < character.WorldPosition.X; + if (!isFacing) + { + aimTimer = Rand.Range(1f, 1.5f); + } + if (aimTimer > 0) + { + aimTimer -= deltaTime; + return; + } if (WeaponComponent is MeleeWeapon meleeWeapon) { if (squaredDistance <= meleeWeapon.Range * meleeWeapon.Range) @@ -385,14 +481,14 @@ namespace Barotrauma { if (squaredDistance > repairTool.Range * repairTool.Range) { return; } } - if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - character.Position) < MathHelper.PiOver4) + if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - Weapon.Position) < MathHelper.PiOver4) { if (myBodies == null) { myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody); } var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall; - var pickedBody = Submarine.PickBody(character.SimPosition, Enemy.SimPosition, myBodies, collisionCategories); + var pickedBody = Submarine.PickBody(Weapon.SimPosition, Enemy.SimPosition, myBodies, collisionCategories); if (pickedBody != null) { Character target = null; @@ -404,10 +500,11 @@ namespace Barotrauma { target = limb.character; } - if (target != null && target == Enemy) + if (target != null && (target == Enemy || !HumanAIController.IsFriendly(target))) { character.SetInput(InputType.Shoot, false, true); Weapon.Use(deltaTime, character); + aimTimer = Rand.Range(0.25f, 0.5f); } } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs index 903c1cbb7..290c28393 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveContainItem.cs @@ -11,12 +11,13 @@ namespace Barotrauma public Func GetItemPriority; - public int MinContainedAmount = 1; + public int targetItemCount = 1; public string[] ignoredContainerIdentifiers; + public bool checkInventory = true; //can either be a tag or an identifier - private readonly string[] itemIdentifiers; - private readonly ItemContainer container; + public readonly string[] itemIdentifiers; + public readonly ItemContainer container; private bool isCompleted; private AIObjectiveGetItem getItemObjective; @@ -39,7 +40,6 @@ namespace Barotrauma public override bool IsCompleted() { - if (isCompleted) { return true; } int containedItemCount = 0; foreach (Item item in container.Inventory.Items) { @@ -48,7 +48,7 @@ namespace Barotrauma containedItemCount++; } } - return containedItemCount >= MinContainedAmount; + return containedItemCount >= targetItemCount; } public override float GetPriority() @@ -62,7 +62,6 @@ namespace Barotrauma protected override void Act(float deltaTime) { - if (isCompleted) { return; } //get the item that should be contained Item itemToContain = null; foreach (string identifier in itemIdentifiers) @@ -72,8 +71,13 @@ namespace Barotrauma } if (itemToContain == null) { + if (getItemObjective != null && getItemObjective.IsCompleted()) + { + getItemObjective = null; + targetItemCount--; + } TryAddSubObjective(ref getItemObjective, () => - new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager) + new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager, checkInventory: checkInventory) { GetItemPriority = GetItemPriority, ignoredContainerIdentifiers = ignoredContainerIdentifiers @@ -81,14 +85,7 @@ namespace Barotrauma return; } if (container.Item.ParentInventory == character.Inventory) - { - var containedItems = container.Inventory.Items; - //if there's already something in the mask (empty oxygen tank?), drop it - var existingItem = containedItems.FirstOrDefault(i => i != null); - if (existingItem != null) - { - existingItem.Drop(character); - } + { character.Inventory.RemoveItem(itemToContain); container.Inventory.TryPutItem(itemToContain, null); } @@ -102,7 +99,6 @@ namespace Barotrauma } container.Combine(itemToContain); } - isCompleted = true; } public override bool IsDuplicate(AIObjective otherObjective) diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveExtinguishFires.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveExtinguishFires.cs index 521da313c..fc5e33548 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveExtinguishFires.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveExtinguishFires.cs @@ -11,18 +11,9 @@ namespace Barotrauma public AIObjectiveExtinguishFires(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { } - protected override void FindTargets() - { - base.FindTargets(); - if (targets.None() && objectiveManager.CurrentOrder == this) - { - character.Speak(TextManager.Get("DialogNoFire"), null, 3.0f, "nofire", 30.0f); - } - } - protected override bool Filter(Hull hull) => IsValidTarget(hull, character); - protected override float TargetEvaluation() => objectiveManager.CurrentObjective == this ? 100 : targets.Sum(t => GetFireSeverity(t)); + protected override float TargetEvaluation() => objectiveManager.CurrentObjective == this ? 100 : Targets.Sum(t => GetFireSeverity(t)); public static float GetFireSeverity(Hull hull) => hull.FireSources.Sum(fs => fs.Size.X); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFightIntruders.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFightIntruders.cs index 91ebb94f6..6fbaab3d2 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFightIntruders.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFightIntruders.cs @@ -10,26 +10,18 @@ namespace Barotrauma class AIObjectiveFightIntruders : AIObjectiveLoop { public override string DebugTag => "fight intruders"; + protected override float IgnoreListClearInterval => 30; public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { } public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveFightIntruders; - protected override void FindTargets() - { - base.FindTargets(); - if (targets.None() && objectiveManager.CurrentOrder == this) - { - character.Speak(TextManager.Get("DialogNoEnemies"), null, 3.0f, "noenemies", 30.0f); - } - } - protected override bool Filter(Character target) => IsValidTarget(target, character); protected override IEnumerable GetList() => Character.CharacterList; - protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier) { useCoolDown = false }; + protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier); protected override float TargetEvaluation() { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs index 824f4046d..1aaeff372 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeak.cs @@ -325,6 +325,31 @@ namespace Barotrauma { #if DEBUG DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool"); +#endif + abandon = true; + return; + } + Vector2 gapDiff = Leak.WorldPosition - character.WorldPosition; + // TODO: use the collider size/reach? + if (!character.AnimController.InWater && Math.Abs(gapDiff.X) < 100 && gapDiff.Y < 0.0f && gapDiff.Y > -150) + { + HumanAIController.AnimController.Crouching = true; + } + float reach = ConvertUnits.ToSimUnits(repairTool.Range); + bool canOperate = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach; + if (canOperate) + { + TryAddSubObjective(ref operateObjective, () => new AIObjectiveOperateItem(repairTool, character, objectiveManager, option: "", requireEquip: true, operateTarget: Leak)); + } + else + { + TryAddSubObjective(ref gotoObjective, () => new AIObjectiveGoTo(ConvertUnits.ToSimUnits(GetStandPosition()), character, objectiveManager) { CloseEnough = reach * 0.75f }); + } + var repairTool = weldingTool.GetComponent(); + if (repairTool == null) + { +#if DEBUG + DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool"); #endif abandon = true; return; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeaks.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeaks.cs index 9255e072d..34c7093ea 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeaks.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveFixLeaks.cs @@ -13,15 +13,6 @@ namespace Barotrauma public AIObjectiveFixLeaks(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { } - protected override void FindTargets() - { - base.FindTargets(); - if (targets.None() && objectiveManager.CurrentOrder == this) - { - character.Speak(TextManager.Get("DialogNoLeaks"), null, 3.0f, "noleaks", 30.0f); - } - } - protected override bool Filter(Gap gap) => IsValidTarget(gap, character); public static float GetLeakSeverity(Gap leak) @@ -34,7 +25,7 @@ namespace Barotrauma } public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveFixLeaks; - protected override float TargetEvaluation() => targets.Max(t => GetLeakSeverity(t)); + protected override float TargetEvaluation() => Targets.Max(t => GetLeakSeverity(t)); protected override IEnumerable GetList() => Gap.GapList; protected override AIObjective ObjectiveConstructor(Gap gap) => new AIObjectiveFixLeak(gap, character, objectiveManager, PriorityModifier); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs index 616b01b7c..66273676a 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveGetItem.cs @@ -41,10 +41,10 @@ namespace Barotrauma this.targetItem = targetItem; } - public AIObjectiveGetItem(Character character, string itemIdentifier, AIObjectiveManager objectiveManager, bool equip = false, float priorityModifier = 1) - : this(character, new string[] { itemIdentifier }, objectiveManager, equip, priorityModifier) { } + public AIObjectiveGetItem(Character character, string itemIdentifier, AIObjectiveManager objectiveManager, bool equip = false, bool checkInventory = true, float priorityModifier = 1) + : this(character, new string[] { itemIdentifier }, objectiveManager, equip, checkInventory, priorityModifier) { } - public AIObjectiveGetItem(Character character, string[] itemIdentifiers, AIObjectiveManager objectiveManager, bool equip = false, float priorityModifier = 1) + public AIObjectiveGetItem(Character character, string[] itemIdentifiers, AIObjectiveManager objectiveManager, bool equip = false, bool checkInventory = true, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { currSearchIndex = -1; @@ -54,7 +54,10 @@ namespace Barotrauma { itemIdentifiers[i] = itemIdentifiers[i].ToLowerInvariant(); } - CheckInventory(); + if (checkInventory) + { + CheckInventory(); + } } private void CheckInventory() @@ -225,8 +228,7 @@ namespace Barotrauma public override bool IsDuplicate(AIObjective otherObjective) { - AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem; - if (getItem == null) { return false; } + if (!(otherObjective is AIObjectiveGetItem getItem)) { return false; } if (getItem.equip != equip) { return false; } if (getItem.itemIdentifiers != null && itemIdentifiers != null) { @@ -246,7 +248,11 @@ namespace Barotrauma public override bool IsCompleted() { - if (itemIdentifiers != null) + if (targetItem != null) + { + return HasItem(targetItem); + } + else if (itemIdentifiers != null) { foreach (string itemName in itemIdentifiers) { @@ -258,10 +264,6 @@ namespace Barotrauma } return false; } - else if (targetItem != null) - { - return character.Inventory.Items.Contains(targetItem) && (!equip || character.HasEquippedItem(targetItem)); - } return false; } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs index eeef8131c..1e1e961f6 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveIdle.cs @@ -73,6 +73,21 @@ namespace Barotrauma } } + public override void Update(float deltaTime) + { + if (objectiveManager.CurrentObjective == this) + { + if (randomTimer > 0) + { + randomTimer -= deltaTime; + } + else + { + SetRandom(); + } + } + } + public override bool IsCompleted() => false; public override bool CanBeCompleted => true; diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveLoop.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveLoop.cs index 60e619d78..cbcd1305f 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveLoop.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveLoop.cs @@ -7,8 +7,8 @@ namespace Barotrauma { abstract class AIObjectiveLoop : AIObjective { - protected HashSet targets = new HashSet(); - protected Dictionary objectives = new Dictionary(); + public HashSet Targets { get; private set; } = new HashSet(); + public Dictionary Objectives { get; private set; } = new Dictionary(); protected HashSet ignoreList = new HashSet(); private float ignoreListTimer; private float targetUpdateTimer; @@ -67,7 +67,7 @@ namespace Barotrauma targetUpdateTimer -= deltaTime; } // Sync objectives, subobjectives and targets - foreach (var objective in objectives) + foreach (var objective in Objectives) { var target = objective.Key; if (!objective.Value.CanBeCompleted) @@ -75,13 +75,13 @@ namespace Barotrauma ignoreList.Add(target); targetUpdateTimer = 0; } - if (!targets.Contains(target)) + if (!Targets.Contains(target)) { subObjectives.Remove(objective.Value); } } - SyncRemovedObjectives(objectives, GetList()); - if (objectives.None() && targets.Any()) + SyncRemovedObjectives(Objectives, GetList()); + if (Objectives.None() && Targets.Any()) { CreateObjectives(); } @@ -109,7 +109,7 @@ namespace Barotrauma public override float GetPriority() { if (character.Submarine == null) { return 0; } - if (targets.None()) { return 0; } + if (Targets.None()) { return 0; } // Allow the target value to be more than 100. float targetValue = TargetEvaluation(); // If the target value is less than 1% of the max value, let's just treat it as zero. @@ -127,7 +127,7 @@ namespace Barotrauma protected void UpdateTargets() { SetTargetUpdateTimer(); - targets.Clear(); + Targets.Clear(); FindTargets(); CreateObjectives(); } @@ -146,20 +146,20 @@ namespace Barotrauma if (!Filter(target)) { continue; } if (!ignoreList.Contains(target)) { - targets.Add(target); + Targets.Add(target); } } } protected virtual void CreateObjectives() { - foreach (T target in targets) + foreach (T target in Targets) { - if (!objectives.TryGetValue(target, out AIObjective objective)) + if (!Objectives.TryGetValue(target, out AIObjective objective)) { objective = ObjectiveConstructor(target); objective.Completed += () => ReportedTargets.Remove(target); - objectives.Add(target, objective); + Objectives.Add(target, objective); AddSubObjective(objective); } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs index 18c0533e5..09162a259 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveOperateItem.cs @@ -290,6 +290,10 @@ namespace Barotrauma { isCompleted = true; } + if (component.AIOperate(deltaTime, character, this)) + { + isCompleted = true; + } } else { diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectivePumpWater.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectivePumpWater.cs index 74d49f667..5dd4d4768 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectivePumpWater.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectivePumpWater.cs @@ -12,7 +12,7 @@ namespace Barotrauma public override string DebugTag => "pump water"; private IEnumerable pumpList; - public AIObjectivePumpWater(Character character, AIObjectiveManager objectiveManager, string option, float priorityModifier = 1) + public AIObjectivePumpWater(Character character, AIObjectiveManager objectiveManager, string option, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier, option) { } public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectivePumpWater && otherObjective.Option == Option; @@ -21,10 +21,6 @@ namespace Barotrauma { if (Option == null) { return; } base.FindTargets(); - if (targets.None() && objectiveManager.CurrentOrder == this) - { - character.Speak(TextManager.Get("DialogNoPumps"), null, 3.0f, "nopumps", 30.0f); - } } protected override bool Filter(Pump pump) @@ -63,11 +59,11 @@ namespace Barotrauma { if (Option == "stoppumping") { - return targets.Max(t => MathHelper.Lerp(0, 100, Math.Abs(t.FlowPercentage / 100))); + return Targets.Max(t => MathHelper.Lerp(0, 100, Math.Abs(t.FlowPercentage / 100))); } else { - return targets.Max(t => MathHelper.Lerp(100, 0, Math.Abs(-t.FlowPercentage / 100))); + return Targets.Max(t => MathHelper.Lerp(100, 0, Math.Abs(-t.FlowPercentage / 100))); } } } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs index b64af04a5..010c5f5ee 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItem.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using Barotrauma.Extensions; using FarseerPhysics; +using Barotrauma.Items.Components; namespace Barotrauma { @@ -14,6 +15,7 @@ namespace Barotrauma public Item Item { get; private set; } private AIObjectiveGoTo goToObjective; + private AIObjectiveContainItem refuelObjective; private float previousCondition = -1; private RepairTool repairTool; @@ -71,11 +73,46 @@ namespace Barotrauma } } // Only continue when the get item sub objectives have been completed. - if (subObjectives.Any(so => so is AIObjectiveGetItem)) { return; } + if (subObjectives.Any()) { return; } if (repairTool == null) { FindRepairTool(); } + if (repairTool != null) + { + var containedItems = repairTool.Item.ContainedItems; + if (containedItems == null) + { +#if DEBUG + DebugConsole.ThrowError("AIObjectiveRepairItem failed - the item \"" + repairTool + "\" has no proper inventory"); +#endif + abandon = true; + return; + } + // Drop empty tanks + foreach (Item containedItem in containedItems) + { + if (containedItem == null) { continue; } + if (containedItem.Condition <= 0.0f) + { + containedItem.Drop(character); + } + } + RelatedItem item = null; + Item fuel = null; + foreach (RelatedItem requiredItem in repairTool.requiredItems[RelatedItem.RelationType.Contained]) + { + item = requiredItem; + fuel = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it)); + if (fuel != null) { break; } + } + if (fuel == null) + { + RemoveSubObjective(ref goToObjective); + TryAddSubObjective(ref refuelObjective, () => new AIObjectiveContainItem(character, item.Identifiers, repairTool.Item.GetComponent(), objectiveManager)); + return; + } + } if (character.CurrentHull == Item.CurrentHull && character.CanInteractWith(Item)) { if (repairTool != null) @@ -112,6 +149,7 @@ namespace Barotrauma } else { + RemoveSubObjective(ref refuelObjective); // If cannot reach the item, approach it. TryAddSubObjective(ref goToObjective, constructor: () => diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItems.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItems.cs index fe45dbfb9..77c4326c0 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItems.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRepairItems.cs @@ -18,25 +18,16 @@ namespace Barotrauma public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveRepairItems repairItems && repairItems.RequireAdequateSkills == RequireAdequateSkills; - protected override void FindTargets() - { - base.FindTargets(); - if (targets.None() && objectiveManager.CurrentOrder == this) - { - character.Speak(TextManager.Get("DialogNoRepairTargets"), null, 3.0f, "norepairtargets", 30.0f); - } - } - protected override void CreateObjectives() { - foreach (var item in targets) + foreach (var item in Targets) { foreach (Repairable repairable in item.Repairables) { - if (!objectives.TryGetValue(item, out AIObjective objective)) + if (!Objectives.TryGetValue(item, out AIObjective objective)) { objective = ObjectiveConstructor(item); - objectives.Add(item, objective); + Objectives.Add(item, objective); AddSubObjective(objective); } break; @@ -50,7 +41,7 @@ namespace Barotrauma if (item.CurrentHull.FireSources.Count > 0) { return false; } // Don't repair items in rooms that have enemies inside. if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c))) { return false; } - if (!objectives.ContainsKey(item)) + if (!Objectives.ContainsKey(item)) { if (item.Repairables.All(r => item.Condition > r.ShowRepairUIThreshold)) { return false; } } @@ -61,7 +52,7 @@ namespace Barotrauma return true; } - protected override float TargetEvaluation() => targets.Max(t => 100 - t.ConditionPercentage); + protected override float TargetEvaluation() => Targets.Max(t => 100 - t.ConditionPercentage); protected override IEnumerable GetList() => Item.ItemList; protected override AIObjective ObjectiveConstructor(Item item) => new AIObjectiveRepairItem(character, item, objectiveManager, PriorityModifier); diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs index 336fdc373..a8921a9b7 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescue.cs @@ -201,7 +201,7 @@ namespace Barotrauma public override bool IsCompleted() { - bool isCompleted = targetCharacter.Vitality / targetCharacter.MaxVitality > AIObjectiveRescueAll.VitalityThreshold; + bool isCompleted = targetCharacter.Vitality / targetCharacter.MaxVitality > AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager); if (isCompleted) { character.Speak(TextManager.Get("DialogTargetHealed").Replace("[targetname]", targetCharacter.Name), diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescueAll.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescueAll.cs index 0eff6361b..0d7cea7ec 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescueAll.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/Objectives/AIObjectiveRescueAll.cs @@ -9,30 +9,22 @@ namespace Barotrauma public override string DebugTag => "rescue all"; public override bool ForceRun => true; - //only treat characters whose vitality is below this (0.9 = 90% of max vitality) - public const float VitalityThreshold = 0.9f; + private const float vitalityThreshold = 0.85f; + private const float vitalityThresholdForOrders = 0.95f; + public static float GetVitalityThreshold(AIObjectiveManager manager) => manager.CurrentOrder is AIObjectiveRescueAll ? vitalityThresholdForOrders : vitalityThreshold; public AIObjectiveRescueAll(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) : base(character, objectiveManager, priorityModifier) { } public override bool IsDuplicate(AIObjective otherObjective) => otherObjective is AIObjectiveRescueAll; - protected override void FindTargets() - { - base.FindTargets(); - if (targets.None() && objectiveManager.CurrentOrder == this) - { - character.Speak(TextManager.Get("DialogNoRescueTargets"), null, 3.0f, "norescuetargets", 30.0f); - } - } - protected override bool Filter(Character target) => IsValidTarget(target, character); protected override IEnumerable GetList() => Character.CharacterList; protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveRescue(character, target, objectiveManager, PriorityModifier); - protected override float TargetEvaluation() => targets.Max(t => GetVitalityFactor(t)) * 100; + protected override float TargetEvaluation() => Targets.Max(t => GetVitalityFactor(t)) * 100; public static float GetVitalityFactor(Character character) => (character.MaxVitality - character.Vitality) / character.MaxVitality; @@ -41,7 +33,8 @@ namespace Barotrauma if (target == null || target.IsDead || target.Removed) { return false; } if (target == character) { return false; } // TODO: enable healing self if (!HumanAIController.IsFriendly(character, target)) { return false; } - if (target.Vitality / target.MaxVitality > VitalityThreshold) { return false; } + if (!(character.AIController is HumanAIController humanAI)) { return false; } + if (target.Vitality / target.MaxVitality > GetVitalityThreshold(humanAI.ObjectiveManager)) { return false; } if (target.Submarine == null) { return false; } if (target.Submarine.TeamID != character.Submarine.TeamID) { return false; } if (target.CurrentHull == null) { return false; } diff --git a/Barotrauma/BarotraumaShared/Source/Characters/AI/PathFinder.cs b/Barotrauma/BarotraumaShared/Source/Characters/AI/PathFinder.cs index a014a6137..8e822ac4f 100644 --- a/Barotrauma/BarotraumaShared/Source/Characters/AI/PathFinder.cs +++ b/Barotrauma/BarotraumaShared/Source/Characters/AI/PathFinder.cs @@ -203,7 +203,9 @@ namespace Barotrauma if (startNode == null) { +#if DEBUG DebugConsole.NewMessage("Pathfinding error, couldn't find a start node. "+ errorMsgStr, Color.DarkRed); +#endif return new SteeringPath(true); } @@ -253,7 +255,9 @@ namespace Barotrauma if (endNode == null) { +#if DEBUG DebugConsole.NewMessage("Pathfinding error, couldn't find an end node. " + errorMsgStr, Color.DarkRed); +#endif return new SteeringPath(true); } @@ -281,7 +285,9 @@ namespace Barotrauma if (startNode == null || endNode == null) { +#if DEBUG DebugConsole.NewMessage("Pathfinding error, couldn't find matching pathnodes to waypoints.", Color.DarkRed); +#endif return new SteeringPath(true); } diff --git a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs index d0cd28725..f43235f81 100644 --- a/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs +++ b/Barotrauma/BarotraumaShared/Source/GameSession/GameModes/CampaignMode.cs @@ -132,13 +132,11 @@ namespace Barotrauma #else if (!CrewManager.GetCharacters().Contains(character)) { continue; } #endif - if (character.Submarine == Level.Loaded.StartOutpost && - Vector2.DistanceSquared(character.WorldPosition, startWatchman.WorldPosition) < 500.0f * 500.0f) + if (character.Submarine == Level.Loaded.StartOutpost && character.CurrentHull == startWatchman.CurrentHull) { CreateDialog(new List { startWatchman }, "EnterStartOutpost", 5 * 60.0f); } - else if (character.Submarine == Level.Loaded.EndOutpost && - Vector2.DistanceSquared(character.WorldPosition, endWatchman.WorldPosition) < 500.0f * 500.0f) + else if (character.Submarine == Level.Loaded.EndOutpost && character.CurrentHull == endWatchman.CurrentHull) { CreateDialog(new List { endWatchman }, "EnterEndOutpost", 5 * 60.0f); } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs index 151100514..c6646effc 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Deconstructor.cs @@ -943,6 +943,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/Reactor.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs index 427f81c40..8b2647587 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Reactor.cs @@ -501,7 +501,7 @@ namespace Barotrauma.Items.Components { var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent(), objective.objectiveManager) { - MinContainedAmount = item.ContainedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1, + targetItemCount = item.ContainedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1, GetItemPriority = (Item fuelItem) => { if (fuelItem.ParentInventory?.Owner is Item) diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs index a251165ce..2aafee648 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Machines/Steering.cs @@ -212,33 +212,6 @@ namespace Barotrauma.Items.Components } } - public Vector2? PosToMaintain - { - get { return posToMaintain; } - set { posToMaintain = value; } - } - - struct ObstacleDebugInfo - { - public Vector2 Point1; - public Vector2 Point2; - - public Vector2? Intersection; - - public float Dot; - - public Vector2 AvoidStrength; - - public ObstacleDebugInfo(GraphEdge edge, Vector2? intersection, float dot, Vector2 avoidStrength) - { - Point1 = edge.Point1; - Point2 = edge.Point2; - Intersection = intersection; - Dot = dot; - AvoidStrength = avoidStrength; - } - } - //edge point 1, edge point 2, avoid strength private List debugDrawObstacles = new List(); diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/CustomInterface.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/CustomInterface.cs index a91d38da2..c521952de 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/CustomInterface.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Signal/CustomInterface.cs @@ -125,14 +125,7 @@ namespace Barotrauma.Items.Components for (int i = 0; i < labels.Length; i++) { labels[i] = i < newLabels.Length ? newLabels[i] : customInterfaceElementList[i].Label; - if (Screen.Selected != GameMain.SubEditorScreen) - { - customInterfaceElementList[i].Label = TextManager.Get(labels[i], returnNull: true) ?? labels[i]; - } - else - { - customInterfaceElementList[i].Label = labels[i]; - } + customInterfaceElementList[i].Label = TextManager.Get(labels[i], returnNull: true) ?? labels[i]; } UpdateLabelsProjSpecific(); } diff --git a/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs b/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs index 9019d488a..2e500837e 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Components/Turret.cs @@ -460,7 +460,7 @@ namespace Barotrauma.Items.Components var containShellObjective = new AIObjectiveContainItem(character, container.ContainableItems[0].Identifiers[0], container, objective.objectiveManager); character?.Speak(TextManager.Get("DialogLoadTurret").Replace("[itemname]", item.Name), null, 0.0f, "loadturret", 30.0f); - containShellObjective.MinContainedAmount = usableProjectileCount + 1; + containShellObjective.targetItemCount = usableProjectileCount + 1; containShellObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier }; objective.AddSubObjective(containShellObjective); return false; diff --git a/Barotrauma/BarotraumaShared/Source/Items/Item.cs b/Barotrauma/BarotraumaShared/Source/Items/Item.cs index 4392ee683..71bbfaf9b 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/Item.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/Item.cs @@ -1128,10 +1128,6 @@ namespace Barotrauma { ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime); } - if (!broken) - { - ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime); - } if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; } @@ -1176,59 +1172,6 @@ namespace Barotrauma } } - public void UpdateTransform() - { - Submarine prevSub = Submarine; - - FindHull(); - - if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; } - - Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition); - rect.X = (int)(displayPos.X - rect.Width / 2.0f); - rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); - - if (Math.Abs(body.LinearVelocity.X) > NetConfig.MaxPhysicsBodyVelocity || - Math.Abs(body.LinearVelocity.Y) > NetConfig.MaxPhysicsBodyVelocity) - { - body.LinearVelocity = new Vector2( - MathHelper.Clamp(body.LinearVelocity.X, -NetConfig.MaxPhysicsBodyVelocity, NetConfig.MaxPhysicsBodyVelocity), - MathHelper.Clamp(body.LinearVelocity.Y, -NetConfig.MaxPhysicsBodyVelocity, NetConfig.MaxPhysicsBodyVelocity)); - } - } - - public void UpdateTransform() - { - Submarine prevSub = Submarine; - - FindHull(); - - if (Submarine == null && prevSub != null) - { - body.SetTransform(body.SimPosition + prevSub.SimPosition, body.Rotation); - } - else if (Submarine != null && prevSub == null) - { - body.SetTransform(body.SimPosition - Submarine.SimPosition, body.Rotation); - } - else if (Submarine != null && prevSub != null && Submarine != prevSub) - { - body.SetTransform(body.SimPosition + prevSub.SimPosition - Submarine.SimPosition, body.Rotation); - } - - Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition); - rect.X = (int)(displayPos.X - rect.Width / 2.0f); - rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); - - if (Math.Abs(body.LinearVelocity.X) > NetConfig.MaxPhysicsBodyVelocity || - Math.Abs(body.LinearVelocity.Y) > NetConfig.MaxPhysicsBodyVelocity) - { - body.LinearVelocity = new Vector2( - MathHelper.Clamp(body.LinearVelocity.X, -NetConfig.MaxPhysicsBodyVelocity, NetConfig.MaxPhysicsBodyVelocity), - MathHelper.Clamp(body.LinearVelocity.Y, -NetConfig.MaxPhysicsBodyVelocity, NetConfig.MaxPhysicsBodyVelocity)); - } - } - public void UpdateTransform() { Submarine prevSub = Submarine; diff --git a/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs b/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs index 738fe2034..3301e26d0 100644 --- a/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs +++ b/Barotrauma/BarotraumaShared/Source/Items/ItemPrefab.cs @@ -443,22 +443,12 @@ namespace Barotrauma identifier = element.GetAttributeString("identifier", ""); + name = TextManager.Get("EntityName." + identifier, true) ?? element.GetAttributeString("name", ""); + if (name == "") DebugConsole.ThrowError("Unnamed item in " + filePath + "!"); + //nameidentifier can be used to make multiple items use the same names and descriptions string nameIdentifier = element.GetAttributeString("nameidentifier", ""); - if (string.IsNullOrEmpty(nameIdentifier)) - { - name = TextManager.Get("EntityName." + identifier, true) ?? element.GetAttributeString("name", ""); - } - else - { - name = TextManager.Get("EntityName." + nameIdentifier, true) ?? element.GetAttributeString("name", ""); - } - - if (name == "") { DebugConsole.ThrowError("Unnamed item in " + filePath + "!"); } - - DebugConsole.Log(" " + name); - Aliases = element.GetAttributeStringArray("aliases", new string[0], convertToLowerInvariant: true); if (!Enum.TryParse(element.GetAttributeString("category", "Misc"), true, out MapEntityCategory category)) @@ -491,15 +481,7 @@ namespace Barotrauma SerializableProperty.DeserializeProperties(this, element); - string translatedDescription = ""; - if (string.IsNullOrEmpty(nameIdentifier)) - { - translatedDescription = TextManager.Get("EntityDescription." + identifier, true); - } - else - { - translatedDescription = TextManager.Get("EntityDescription." + nameIdentifier, true); - } + string translatedDescription = TextManager.Get("EntityDescription." + identifier, true); if (!string.IsNullOrEmpty(translatedDescription)) Description = translatedDescription; foreach (XElement subElement in element.Elements()) diff --git a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs index 879bc5d2f..109153362 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Hull.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Hull.cs @@ -284,6 +284,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/Submarine.cs b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs index 6d94989cc..0cb0320ed 100644 --- a/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs +++ b/Barotrauma/BarotraumaShared/Source/Map/Submarine.cs @@ -1364,7 +1364,7 @@ namespace Barotrauma foreach (Hull hull in matchingHulls) { - if (string.IsNullOrEmpty(hull.RoomName) || !hull.RoomName.Contains("RoomName.")) + if (string.IsNullOrEmpty(hull.RoomName)) { hull.RoomName = hull.CreateRoomName(); } diff --git a/Barotrauma/BarotraumaShared/Submarines/Berilia.sub b/Barotrauma/BarotraumaShared/Submarines/Berilia.sub index b1d3cf6e9..e7d6c5629 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Berilia.sub and b/Barotrauma/BarotraumaShared/Submarines/Berilia.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub b/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub index 39e135e77..0963b9725 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub and b/Barotrauma/BarotraumaShared/Submarines/Bunyip.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Dugong.sub b/Barotrauma/BarotraumaShared/Submarines/Dugong.sub index 89171f5dd..f3c656388 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 c1c916e02..77b32bf89 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 db0a93245..45b257e88 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Orca.sub and b/Barotrauma/BarotraumaShared/Submarines/Orca.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Remora.sub b/Barotrauma/BarotraumaShared/Submarines/Remora.sub index 8f8cf0035..92d2dbbb6 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Remora.sub and b/Barotrauma/BarotraumaShared/Submarines/Remora.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Selkie.sub b/Barotrauma/BarotraumaShared/Submarines/Selkie.sub index 2439092f5..2c29b9f4a 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Selkie.sub and b/Barotrauma/BarotraumaShared/Submarines/Selkie.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Typhon.sub b/Barotrauma/BarotraumaShared/Submarines/Typhon.sub index 17194ec8f..1123d84b8 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Typhon.sub and b/Barotrauma/BarotraumaShared/Submarines/Typhon.sub differ diff --git a/Barotrauma/BarotraumaShared/Submarines/Venture.sub b/Barotrauma/BarotraumaShared/Submarines/Venture.sub index cf8bb3935..7fe946301 100644 Binary files a/Barotrauma/BarotraumaShared/Submarines/Venture.sub and b/Barotrauma/BarotraumaShared/Submarines/Venture.sub differ diff --git a/Barotrauma/BarotraumaShared/changelog.txt b/Barotrauma/BarotraumaShared/changelog.txt index 59edb7328..beb985789 100644 --- a/Barotrauma/BarotraumaShared/changelog.txt +++ b/Barotrauma/BarotraumaShared/changelog.txt @@ -1,31 +1,3 @@ ---------------------------------------------------------------------------------------------------------- -v0.9.0.0 ---------------------------------------------------------------------------------------------------------- - -Additions and changes: -- Translations to German, Russian, French, Brazilian Portuguese, Traditional Chinese and Simplified Chinese. -- New logo and main menu art. -- 2 new outposts. -- New docking system. -- Numerous fixes and improvements to the crew AI. The biggest change is that the AI characters can now do -tasks by themselves even when they haven't been assigned an order. -- Lock the inventory when aiming with a railgun or a coilgun. -- Swap the order of the starting location & destination in nav terminals when playing in a mirrored sub. - -Bugfixes: -- Fixed splash screens and tutorial videos crashing the Linux version. -- Fixed damaged item sprites not updating client-side after being repaired. -- Fixed characters occasionally taking impact damage when hitting walls while climbing. -- Improved the syncing of ragdolled characters. -- Fixed characters running more slowly when their torso is in a different hull than the feet (for example -in Humpback's bilge). -- Fixed items "vanishing" if they move directly from sub to another without going outside first. -- Fixed content package hash calculation failing if the package is not enabled and contains new monster files. -- Fixed inability to enable content packages if some of the files included in the package are already in -the game folder (which may happen, for example, if enabling a content package fails). -- Fixed AllowRagdollButton settings not being synced with clients, leading to strange ragdolling behavior -client-side if the server has disabled ragdolling. - --------------------------------------------------------------------------------------------------------- v0.8.10.0 ---------------------------------------------------------------------------------------------------------