(457e4ebe5) Fixed: Submarine navigation labels being translated when loading a sub in editor

This commit is contained in:
Joonas Rikkonen
2019-05-16 06:13:49 +03:00
parent fc584c1bc4
commit 5f524ddd3f
42 changed files with 631 additions and 379 deletions
@@ -108,6 +108,61 @@ namespace Barotrauma
} }
} }
//unconscious/dead characters can't correct their position using AnimController movement
// -> we need to correct it manually
if (!character.AllowInput)
{
float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition);
float mainLimbErrorTolerance = 0.1f;
//if the main limb is roughly at the correct position and the collider isn't moving (much at least),
//don't attempt to correct the position.
if (mainLimbDistSqrd > mainLimbErrorTolerance || Collider.LinearVelocity.LengthSquared() > 0.05f)
{
MainLimb.PullJointWorldAnchorB = Collider.SimPosition;
MainLimb.PullJointEnabled = true;
}
character.SelectedConstruction = character.MemState[0].SelectedItem;
}
if (character.MemState[0].Animation == AnimController.Animation.CPR)
{
character.AnimController.Anim = AnimController.Animation.CPR;
}
else if (character.AnimController.Anim == AnimController.Animation.CPR)
{
character.AnimController.Anim = AnimController.Animation.None;
}
Vector2 newVelocity = Collider.LinearVelocity;
Vector2 newPosition = Collider.SimPosition;
float newRotation = Collider.Rotation;
float newAngularVelocity = Collider.AngularVelocity;
Collider.CorrectPosition(character.MemState, out newPosition, out newVelocity, out newRotation, out newAngularVelocity);
newVelocity = newVelocity.ClampLength(100.0f);
if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; }
overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero;
Collider.LinearVelocity = newVelocity;
Collider.AngularVelocity = newAngularVelocity;
float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition);
float errorTolerance = character.AllowInput ? 0.01f : 0.2f;
if (distSqrd > errorTolerance)
{
if (distSqrd > 10.0f || !character.AllowInput)
{
Collider.TargetRotation = newRotation;
SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false);
}
else
{
Collider.TargetRotation = newRotation;
Collider.TargetPosition = newPosition;
Collider.MoveToTargetPosition(true);
}
}
//unconscious/dead characters can't correct their position using AnimController movement //unconscious/dead characters can't correct their position using AnimController movement
// -> we need to correct it manually // -> we need to correct it manually
if (!character.AllowInput) if (!character.AllowInput)
@@ -151,32 +206,34 @@ namespace Barotrauma
} }
} }
character.MemLocalState.Clear();
if (character.MemLocalState.Count > 120) character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120);
character.MemState.Clear();
} }
} else
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); } //remove states with a timestamp (there may still timestamp-based states
} //in the list if the controlled character switches from timestamp-based interpolation to ID-based)
else if (body.UserData is Limb || body == Collider.FarseerBody) character.MemState.RemoveAll(m => m.Timestamp > 0.0f);
{
if (!character.IsRemotePlayer && impact > ImpactTolerance) for (int i = 0; i < character.MemLocalState.Count; i++)
{ {
SoundPlayer.PlayDamageSound("LimbBlunt", strongestImpact, Collider); if (character.Submarine == null)
{
//transform in-sub coordinates to outside coordinates
if (character.MemLocalState[i].Position.Y > lowestSubPos)
{
character.MemLocalState[i].TransformInToOutside();
}
}
else if (currentHull?.Submarine != null)
{
//transform outside coordinates to in-sub coordinates
if (character.MemLocalState[i].Position.Y < lowestSubPos)
{
character.MemLocalState[i].TransformOutToInside(currentHull.Submarine);
}
}
} }
}
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; if (character.MemState.Count < 1) return;
@@ -198,7 +198,9 @@ namespace Barotrauma
focusName = character.FocusedCharacter.Info.DisplayName; focusName = character.FocusedCharacter.Info.DisplayName;
} }
Vector2 textPos = startPos; Vector2 textPos = startPos;
textPos -= new Vector2(GUI.Font.MeasureString(focusName).X / 2, 20); Vector2 offset = GUI.Font.MeasureString(focusName);
textPos -= new Vector2(offset.X / 2, offset.Y);
Color nameColor = Color.White; Color nameColor = Color.White;
if (character.TeamID != character.FocusedCharacter.TeamID) if (character.TeamID != character.FocusedCharacter.TeamID)
@@ -207,23 +209,23 @@ namespace Barotrauma
} }
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2); GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2);
textPos.Y += 20; textPos.Y += offset.Y;
if (character.FocusedCharacter.CanInventoryBeAccessed) if (character.FocusedCharacter.CanInventoryBeAccessed)
{ {
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBind(InputType.Grab).ToString()), GUI.DrawString(spriteBatch, textPos, GetCachedHudText("GrabHint", GameMain.Config.KeyBind(InputType.Grab).ToString()),
Color.LightGreen, Color.Black, 2, GUI.SmallFont); Color.LightGreen, Color.Black, 2, GUI.SmallFont);
textPos.Y += 15; textPos.Y += offset.Y;
} }
if (character.FocusedCharacter.CharacterHealth.UseHealthWindow) if (character.FocusedCharacter.CharacterHealth.UseHealthWindow)
{ {
GUI.DrawString(spriteBatch, textPos, GetCachedHudText("HealHint", GameMain.Config.KeyBind(InputType.Health).ToString()), GUI.DrawString(spriteBatch, textPos, GetCachedHudText("HealHint", GameMain.Config.KeyBind(InputType.Health).ToString()),
Color.LightGreen, Color.Black, 2, GUI.SmallFont); Color.LightGreen, Color.Black, 2, GUI.SmallFont);
textPos.Y += 15; textPos.Y += offset.Y;
} }
if (!string.IsNullOrEmpty(character.FocusedCharacter.customInteractHUDText)) if (!string.IsNullOrEmpty(character.FocusedCharacter.customInteractHUDText))
{ {
GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.customInteractHUDText, Color.LightGreen, Color.Black, 2, GUI.SmallFont); GUI.DrawString(spriteBatch, textPos, character.FocusedCharacter.customInteractHUDText, Color.LightGreen, Color.Black, 2, GUI.SmallFont);
textPos.Y += 15; textPos.Y += offset.Y;
} }
} }
@@ -259,8 +261,10 @@ namespace Barotrauma
var hudTexts = focusedItem.GetHUDTexts(character); var hudTexts = focusedItem.GetHUDTexts(character);
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X); int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
Vector2 offset = GUI.Font.MeasureString(focusedItem.Name);
Vector2 startPos = cam.WorldToScreen(focusedItem.DrawPosition); Vector2 startPos = cam.WorldToScreen(focusedItem.DrawPosition);
startPos.Y -= (hudTexts.Count + 1) * 20; startPos.Y -= (hudTexts.Count + 1) * offset.Y;
if (focusedItem.Sprite != null) if (focusedItem.Sprite != null)
{ {
startPos.X += (int)(circleSize * 0.4f * dir); startPos.X += (int)(circleSize * 0.4f * dir);
@@ -268,17 +272,17 @@ namespace Barotrauma
} }
Vector2 textPos = startPos; Vector2 textPos = startPos;
if (dir == -1) textPos.X -= (int)GUI.Font.MeasureString(focusedItem.Name).X; if (dir == -1) textPos.X -= offset.X;
float alpha = MathHelper.Clamp((focusedItemOverlayTimer - ItemOverlayDelay) * 2.0f, 0.0f, 1.0f); 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); GUI.DrawString(spriteBatch, textPos, focusedItem.Name, Color.White * alpha, Color.Black * alpha * 0.7f, 2);
textPos.Y += 20.0f; textPos.Y += offset.Y;
foreach (ColoredText coloredText in hudTexts) foreach (ColoredText coloredText in hudTexts)
{ {
if (dir == -1) textPos.X = (int)(startPos.X - GUI.SmallFont.MeasureString(coloredText.Text).X); 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); GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUI.SmallFont);
textPos.Y += 20; textPos.Y += offset.Y;
} }
} }
} }
@@ -74,12 +74,17 @@ namespace Barotrauma
public CrewManager(XElement element, bool isSinglePlayer) public CrewManager(XElement element, bool isSinglePlayer)
: this(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; return;
} }
if (string.IsNullOrEmpty(text)) { return; } List<Character> 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); var characterInfo = new CharacterInfo(subElement);
characterInfos.Add(characterInfo); characterInfos.Add(characterInfo);
@@ -90,7 +95,6 @@ namespace Barotrauma
break; break;
} }
} }
ChatBox.AddMessage(ChatMessage.Create(senderName, text, messageType, sender));
} }
partial void InitProjectSpecific() partial void InitProjectSpecific()
@@ -239,27 +243,24 @@ namespace Barotrauma
public IEnumerable<Character> GetCharacters() public IEnumerable<Character> 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<WifiComponent>() != null); characterInfos.Add(characterInfo);
if (radioItem == null) return null;
if (requireEquipped && !character.HasEquippedItem(radioItem)) return null;
return radioItem.GetComponent<WifiComponent>();
} }
public IEnumerable<CharacterInfo> GetCharacterInfos() public IEnumerable<CharacterInfo> 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; return;
} }
List<Character> availableSpeakers = Character.CharacterList.FindAll(c => characters.Remove(character);
c.AIController is HumanAIController && if (removeInfo) characterInfos.Remove(character.Info);
!c.IsDead &&
c.SpeechImpediment <= 100.0f);
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
} }
public void AddCharacter(Character character) public void AddCharacter(Character character)
@@ -633,9 +634,183 @@ namespace Barotrauma
{ {
characterListBox.BarScroll = roundedPos; characterListBox.BarScroll = roundedPos;
} }
soundIcon.Visible = !muted && !mutedLocally; var characterArea = new GUIButton(new RectTransform(new Point(characterInfoWidth, frame.Rect.Height), frame.RectTransform, Anchor.CenterLeft), style: "GUITextBox")
soundIconDisabled.Visible = muted || mutedLocally; {
soundIconDisabled.ToolTip = TextManager.Get(mutedLocally ? "MutedLocally" : "MutedGlobally"); 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<object> KillCharacterAnim(GUIComponent component) private IEnumerable<object> KillCharacterAnim(GUIComponent component)
@@ -779,6 +954,12 @@ namespace Barotrauma
} }
return; return;
} }
List<Character> 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); character.SetOrder(order, option, orderGiver, speak: orderGiver != character);
if (IsSinglePlayer) 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) orderGiver?.Speak(
{ AbsoluteOffset = new Point(orderButton.Rect.Center.X, orderButton.Rect.Bottom) }, order.GetChatMessage(character.Name, orderGiver.CurrentHull?.DisplayName, givingOrderToSelf: character == orderGiver, orderOption: option), null);
isHorizontal: true, childAnchor: Anchor.BottomLeft) }
else if (orderGiver != null)
{
OrderChatMessage msg = new OrderChatMessage(order, option, order.TargetItemComponent?.Item, character, orderGiver);
if (GameMain.Client != null)
{ {
UserData = character, GameMain.Client.SendChatMessage(msg);
Stretch = true }
}; }
//line connecting the order button to the option buttons DisplayCharacterOrder(character, order);
//TODO: sprite }
new GUIFrame(new RectTransform(new Vector2(0.5f, 1.0f), orderTargetFrame.RectTransform), style: null);
/// <summary> /// <summary>
/// Create the UI panel that's used to select the target and options for a given order /// Create the UI panel that's used to select the target and options for a given order
@@ -35,7 +35,7 @@ namespace Barotrauma
private static Dictionary<InvSlotType, Sprite> limbSlotIcons; private static Dictionary<InvSlotType, Sprite> limbSlotIcons;
const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head; public const InvSlotType PersonalSlots = InvSlotType.Card | InvSlotType.Headset | InvSlotType.InnerClothes | InvSlotType.OuterClothes | InvSlotType.Head;
private Point screenResolution; private Point screenResolution;
@@ -448,7 +448,8 @@ namespace Barotrauma
{ {
if (Items[i]?.OwnInventory != null && Items[i].OwnInventory.Capacity == 1 && PersonalSlots.HasFlag(SlotTypes[i])) if (Items[i]?.OwnInventory != null && Items[i].OwnInventory.Capacity == 1 && PersonalSlots.HasFlag(SlotTypes[i]))
{ {
if (Items[i].OwnInventory.Items[0].Condition > 0.0f && if (Items[i].OwnInventory.Items[0] != null &&
Items[i].OwnInventory.Items[0].Condition > 0.0f &&
Items[i].OwnInventory.Items[0].Condition / Items[i].OwnInventory.Items[0].MaxCondition < 0.15f) Items[i].OwnInventory.Items[0].Condition / Items[i].OwnInventory.Items[0].MaxCondition < 0.15f)
{ {
hidePersonalSlots = false; hidePersonalSlots = false;
@@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components
Stretch = true, Stretch = true,
UserData = "filterarea" UserData = "filterarea"
}; };
new GUITextBlock(new RectTransform(new Vector2(0.25f, 1.0f), filterArea.RectTransform), TextManager.Get("FilterMapEntities"), font: GUI.Font); new GUITextBlock(new RectTransform(new Vector2(0.25f, 1.0f), filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.Font);
itemFilterBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), 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; }; itemFilterBox.OnTextChanged += (textBox, text) => { FilterEntities(text); return true; };
var clearButton = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), filterArea.RectTransform), "x") var clearButton = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), filterArea.RectTransform), "x")
@@ -20,7 +20,7 @@ namespace Barotrauma
{ {
Rectangle drawRect = entity.Second; Rectangle drawRect = entity.Second;
drawRect = new Rectangle( drawRect = new Rectangle(
(int)(drawRect.X * scale) + drawArea.Center.X, -((int)((drawRect.Y - drawRect.Height) * scale) + drawArea.Center.Y), (int)(drawRect.X * scale) + drawArea.Center.X, (int)((drawRect.Y) * scale) - drawArea.Center.Y,
(int)(drawRect.Width * scale), (int)(drawRect.Height * scale)); (int)(drawRect.Width * scale), (int)(drawRect.Height * scale));
entity.First.DrawPlacing(spriteBatch, drawRect, entity.First.Scale * scale); entity.First.DrawPlacing(spriteBatch, drawRect, entity.First.Scale * scale);
} }
@@ -440,6 +440,8 @@ namespace Barotrauma
private GUILayoutGroup subPreviewContainer; private GUILayoutGroup subPreviewContainer;
private GUILayoutGroup subPreviewContainer;
private GUIButton loadGameButton; private GUIButton loadGameButton;
public Action<Submarine, string, string> StartNewGame; public Action<Submarine, string, string> StartNewGame;
@@ -498,7 +500,7 @@ namespace Barotrauma
}; };
subList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.65f), leftColumn.RectTransform)) { ScrollBarVisible = true }; 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("FilterMapEntities"), textAlignment: Alignment.CenterLeft, font: GUI.Font); 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 searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform, Anchor.CenterRight), 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.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; }; searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };
@@ -180,7 +180,7 @@ namespace Barotrauma
{ {
Stretch = true Stretch = true
}; };
var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("FilterMapEntities"), textAlignment: Alignment.CenterLeft, font: GUI.Font); var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("serverlog.filter"), textAlignment: Alignment.CenterLeft, font: GUI.Font);
searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform), 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.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; }; searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };
@@ -789,6 +789,10 @@ namespace Barotrauma
GUI.Draw(Cam, spriteBatch); GUI.Draw(Cam, spriteBatch);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, GameMain.ScissorTestEnable);
GUI.Draw(Cam, spriteBatch);
GUI.Draw(Cam, spriteBatch); GUI.Draw(Cam, spriteBatch);
#if DEBUG #if DEBUG
@@ -300,7 +300,7 @@ namespace Barotrauma
UserData = "filterarea" UserData = "filterarea"
}; };
new GUITextBlock(new RectTransform(new Vector2(0.05f, 1.0f), filterArea.RectTransform), TextManager.Get("FilterMapEntities"), font: GUI.Font); new GUITextBlock(new RectTransform(new Vector2(0.05f, 1.0f), filterArea.RectTransform), TextManager.Get("serverlog.filter"), font: GUI.Font);
entityFilterBox = new GUITextBox(new RectTransform(new Vector2(0.8f, 1.0f), filterArea.RectTransform), 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; }; entityFilterBox.OnTextChanged += (textBox, text) => { FilterEntities(text); return true; };
var clearButton = new GUIButton(new RectTransform(new Vector2(0.02f, 1.0f), filterArea.RectTransform), "x") var clearButton = new GUIButton(new RectTransform(new Vector2(0.02f, 1.0f), filterArea.RectTransform), "x")
@@ -835,6 +835,7 @@ namespace Barotrauma
//make space for the entity menu //make space for the entity menu
for (int i = 0; i < dummyCharacter.Inventory.SlotPositions.Length; i++) 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) if (dummyCharacter.Inventory.SlotPositions[i].Y > GameMain.GraphicsHeight / 2)
{ {
dummyCharacter.Inventory.SlotPositions[i].Y -= 50 * GUI.Scale; dummyCharacter.Inventory.SlotPositions[i].Y -= 50 * GUI.Scale;
@@ -1333,7 +1334,7 @@ namespace Barotrauma
} }
}; };
var searchTitle = new GUITextBlock(new RectTransform(new Vector2(0.001f, 1.0f), filterContainer.RectTransform), TextManager.Get("FilterMapEntities"), textAlignment: Alignment.CenterLeft, font: GUI.Font); 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 searchBox = new GUITextBox(new RectTransform(new Vector2(1.0f, 1.0f), filterContainer.RectTransform), 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.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; }; searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };
@@ -74,6 +74,7 @@
<Character file="Content/Characters/Hammerhead/Hammerhead.xml" /> <Character file="Content/Characters/Hammerhead/Hammerhead.xml" />
<Outpost file="Content/Map/Outposts/Outpost.sub" /> <Outpost file="Content/Map/Outposts/Outpost.sub" />
<Outpost file="Content/Map/Outposts/Outpost2.sub" /> <Outpost file="Content/Map/Outposts/Outpost2.sub" />
<Outpost file="Content/Map/Outposts/Outpost3.sub" />
<Submarine file="Submarines/Orca.sub" /> <Submarine file="Submarines/Orca.sub" />
<Submarine file="Submarines/Typhon.sub" /> <Submarine file="Submarines/Typhon.sub" />
<Submarine file="Submarines/Selkie.sub" /> <Submarine file="Submarines/Selkie.sub" />
@@ -391,6 +391,9 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Command\NavUI.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Items\Command\NavUI.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Containers\DivingSuitLocker2.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Door\door2.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Items\Door\door2.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@@ -493,6 +496,7 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Map\OutpostWall_C.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Map\OutpostWall_C.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\SubRearBow2.png" />
<Content Include="$(MSBuildThisFileDirectory)Content\Map\Zones\Ice\Zone4BaseTexture.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Map\Zones\Ice\Zone4BaseTexture.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@@ -2171,6 +2175,9 @@
<None Include="$(MSBuildThisFileDirectory)Content\Map\Outposts\Outpost2.sub"> <None Include="$(MSBuildThisFileDirectory)Content\Map\Outposts\Outpost2.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Include="$(MSBuildThisFileDirectory)Content\Map\Outposts\Outpost3.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Sounds\Damage\Gore1.ogg"> <None Include="$(MSBuildThisFileDirectory)Content\Sounds\Damage\Gore1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
@@ -106,18 +106,6 @@ namespace Barotrauma
subObjectives.Add(objective); subObjectives.Add(objective);
} }
public void RemoveSubObjective<T>(ref T objective) where T : AIObjective
{
if (objective != null)
{
if (subObjectives.Contains(objective))
{
subObjectives.Remove(objective);
}
objective = null;
}
}
public void SortSubObjectives() public void SortSubObjectives()
{ {
if (subObjectives.None()) { return; } if (subObjectives.None()) { return; }
@@ -11,10 +11,9 @@ namespace Barotrauma
class AIObjectiveCombat : AIObjective class AIObjectiveCombat : AIObjective
{ {
public override string DebugTag => "combat"; public override string DebugTag => "combat";
public bool useCoolDown = true;
private readonly CombatMode initialMode; const float CoolDown = 10.0f;
const float coolDown = 10.0f;
public Character Enemy { get; private set; } public Character Enemy { get; private set; }
@@ -26,7 +25,14 @@ namespace Barotrauma
{ {
_weapon = value; _weapon = value;
_weaponComponent = null; _weaponComponent = null;
RemoveSubObjective(ref seekAmmunition); if (reloadWeaponObjective != null)
{
if (subObjectives.Contains(reloadWeaponObjective))
{
subObjectives.Remove(reloadWeaponObjective);
}
reloadWeaponObjective = null;
}
} }
} }
private ItemComponent _weaponComponent; private ItemComponent _weaponComponent;
@@ -34,7 +40,6 @@ namespace Barotrauma
{ {
get get
{ {
if (Weapon == null) { return null; }
if (_weaponComponent == null) if (_weaponComponent == null)
{ {
_weaponComponent = _weaponComponent =
@@ -53,14 +58,12 @@ namespace Barotrauma
private readonly HashSet<MeleeWeapon> meleeWeapons = new HashSet<MeleeWeapon>(); private readonly HashSet<MeleeWeapon> meleeWeapons = new HashSet<MeleeWeapon>();
private readonly HashSet<Item> adHocWeapons = new HashSet<Item>(); private readonly HashSet<Item> adHocWeapons = new HashSet<Item>();
private AIObjectiveContainItem seekAmmunition; private AIObjectiveContainItem reloadWeaponObjective;
private AIObjectiveGoTo retreatObjective; private AIObjectiveGoTo retreatObjective;
private AIObjectiveGoTo followTargetObjective; private AIObjectiveGoTo followTargetObjective;
private Hull retreatTarget; private Hull retreatTarget;
private float coolDownTimer; private float coolDownTimer;
private IEnumerable<FarseerPhysics.Dynamics.Body> myBodies;
private float aimTimer;
public enum CombatMode public enum CombatMode
{ {
@@ -75,7 +78,7 @@ namespace Barotrauma
: base(character, objectiveManager, priorityModifier) : base(character, objectiveManager, priorityModifier)
{ {
Enemy = enemy; Enemy = enemy;
coolDownTimer = coolDown; coolDownTimer = CoolDown;
findSafety = objectiveManager.GetObjective<AIObjectiveFindSafety>(); findSafety = objectiveManager.GetObjective<AIObjectiveFindSafety>();
if (findSafety != null) if (findSafety != null)
{ {
@@ -83,7 +86,6 @@ namespace Barotrauma
findSafety.unreachable.Clear(); findSafety.unreachable.Clear();
} }
Mode = mode; Mode = mode;
initialMode = Mode;
if (Enemy == null) if (Enemy == null)
{ {
Mode = CombatMode.Retreat; Mode = CombatMode.Retreat;
@@ -102,7 +104,7 @@ namespace Barotrauma
public override bool IsCompleted() public override bool IsCompleted()
{ {
bool completed = (Enemy != null && (Enemy.Removed || Enemy.IsDead)) || (initialMode != CombatMode.Offensive && coolDownTimer <= 0); bool completed = (Enemy != null && (Enemy.Removed || Enemy.IsDead)) || (useCoolDown && coolDownTimer <= 0);
if (completed) if (completed)
{ {
if (objectiveManager.CurrentOrder == this && Enemy != null && Enemy.IsDead) if (objectiveManager.CurrentOrder == this && Enemy != null && Enemy.IsDead)
@@ -119,83 +121,39 @@ namespace Barotrauma
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
if (initialMode != CombatMode.Offensive) if (useCoolDown)
{ {
coolDownTimer -= deltaTime; coolDownTimer -= deltaTime;
} }
if (abandon) { return; } if (abandon) { return; }
TryArm(); Arm(deltaTime);
if (seekAmmunition == null || !subObjectives.Contains(seekAmmunition)) Move(deltaTime);
{
Move();
if (Weapon != null)
{
OperateWeapon(deltaTime);
}
}
} }
private void Move() private void Arm(float deltaTime)
{ {
switch (Mode) switch (Mode)
{ {
case CombatMode.Offensive: case CombatMode.Offensive:
Engage();
break;
case CombatMode.Defensive: case CombatMode.Defensive:
case CombatMode.Retreat: if (Weapon != null && !character.Inventory.Items.Contains(_weapon) || _weaponComponent != null && !_weaponComponent.HasRequiredContainedItems(false))
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))
{ {
if (seekAmmunition != null && subObjectives.Contains(seekAmmunition)) Weapon = null;
{ }
return false; if (Weapon == null)
} {
else Weapon = GetWeapon();
{ }
Weapon = null; if (Weapon == null)
} {
Mode = CombatMode.Retreat;
} }
}
}
if (Weapon == null)
{
Weapon = GetWeapon();
}
if (Weapon == null)
{
Weapon = GetWeapon(ignoreRequiredItems: true);
}
Mode = Weapon == null ? CombatMode.Retreat : initialMode;
return Weapon != null;
}
private void OperateWeapon(float deltaTime)
{
switch (Mode)
{
case CombatMode.Offensive:
case CombatMode.Defensive:
if (Equip()) if (Equip())
{ {
Attack(deltaTime); if (Reload(deltaTime))
{
Attack(deltaTime);
}
} }
break; break;
case CombatMode.Retreat: case CombatMode.Retreat:
@@ -205,7 +163,23 @@ namespace Barotrauma
} }
} }
private Item GetWeapon(bool ignoreRequiredItems = false) private void Move(float deltaTime)
{
switch (Mode)
{
case CombatMode.Offensive:
Engage(deltaTime);
break;
case CombatMode.Defensive:
case CombatMode.Retreat:
Retreat(deltaTime);
break;
default:
throw new NotImplementedException();
}
}
private Item GetWeapon()
{ {
rangedWeapons.Clear(); rangedWeapons.Clear();
meleeWeapons.Clear(); meleeWeapons.Clear();
@@ -219,14 +193,14 @@ namespace Barotrauma
{ {
if (component is RangedWeapon rw) if (component is RangedWeapon rw)
{ {
if (ignoreRequiredItems || rw.HasRequiredContainedItems(false)) if (rw.HasRequiredContainedItems(false))
{ {
rangedWeapons.Add(rw); rangedWeapons.Add(rw);
} }
} }
else if (component is MeleeWeapon mw) else if (component is MeleeWeapon mw)
{ {
if (ignoreRequiredItems || mw.HasRequiredContainedItems(false)) if (mw.HasRequiredContainedItems(false))
{ {
meleeWeapons.Add(mw); meleeWeapons.Add(mw);
} }
@@ -242,7 +216,7 @@ namespace Barotrauma
{ {
if (statusEffect.Afflictions.Any()) if (statusEffect.Afflictions.Any())
{ {
if (ignoreRequiredItems || component.HasRequiredContainedItems(false)) if (component.HasRequiredContainedItems(false))
{ {
adHocWeapons.Add(item); adHocWeapons.Add(item);
} }
@@ -279,29 +253,19 @@ namespace Barotrauma
Weapon.Drop(character); Weapon.Drop(character);
} }
} }
return true;
} }
private bool Equip() private bool Equip()
{ {
if (!WeaponComponent.HasRequiredContainedItems(false)) if (!character.SelectedItems.Contains(Weapon))
{ {
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)); var slots = Weapon.AllowedSlots.FindAll(s => s == InvSlotType.LeftHand || s == InvSlotType.RightHand || s == (InvSlotType.LeftHand | InvSlotType.RightHand));
if (character.Inventory.TryPutItem(Weapon, character, slots)) if (character.Inventory.TryPutItem(Weapon, character, slots))
{ {
Weapon.Equip(character); Weapon.Equip(character);
aimTimer = Rand.Range(0.5f, 1f);
} }
else else
{ {
Weapon = null;
Mode = CombatMode.Retreat; Mode = CombatMode.Retreat;
return false; return false;
} }
@@ -309,10 +273,16 @@ namespace Barotrauma
return true; return true;
} }
private void Retreat() private void Retreat(float deltaTime)
{ {
RemoveSubObjective(ref followTargetObjective); if (followTargetObjective != null)
RemoveSubObjective(ref seekAmmunition); {
if (subObjectives.Contains(followTargetObjective))
{
subObjectives.Remove(followTargetObjective);
}
followTargetObjective = null;
}
if (retreatObjective != null && retreatObjective.Target != retreatTarget) if (retreatObjective != null && retreatObjective.Target != retreatTarget)
{ {
retreatObjective = null; retreatObjective = null;
@@ -324,115 +294,59 @@ namespace Barotrauma
TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true)); TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true));
} }
private void Engage() private void Engage(float deltaTime)
{ {
retreatTarget = null; retreatTarget = null;
RemoveSubObjective(ref retreatObjective); if (retreatObjective != null)
RemoveSubObjective(ref seekAmmunition);
if (followTargetObjective != null && followTargetObjective.Target != Enemy)
{ {
followTargetObjective = null; if (subObjectives.Contains(retreatObjective))
{
subObjectives.Remove(retreatObjective);
}
retreatObjective = null;
} }
TryAddSubObjective(ref followTargetObjective, TryAddSubObjective(ref followTargetObjective,
constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true) constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true)
{ {
AllowGoingOutside = true, AllowGoingOutside = true,
IgnoreIfTargetDead = true, IgnoreIfTargetDead = true,
CheckVisibility = 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
}, },
onAbandon: () => onAbandon: () =>
{ {
Mode = CombatMode.Retreat;
SteeringManager.Reset(); 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;
}
}
/// <summary>
/// Seeks for more ammunition. Creates a new subobjective.
/// </summary>
private void SeekAmmunition(string[] ammunitionIdentifiers)
{
retreatTarget = null;
RemoveSubObjective(ref retreatObjective);
RemoveSubObjective(ref followTargetObjective);
TryAddSubObjective(ref seekAmmunition,
constructor: () => new AIObjectiveContainItem(character, ammunitionIdentifiers, Weapon.GetComponent<ItemContainer>(), objectiveManager)
{
targetItemCount = Weapon.GetComponent<ItemContainer>().Capacity,
checkInventory = false
},
onAbandon: () =>
{
Weapon = null;
Mode = CombatMode.Retreat; Mode = CombatMode.Retreat;
SteeringManager.Reset();
}); });
} }
/// <summary> private bool Reload(float deltaTime)
/// 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.
/// </summary>
private bool Reload(bool seekAmmo, Func<bool> condition = null)
{ {
if (WeaponComponent == null) { return false; } if (WeaponComponent != null && WeaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained))
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)); var containedItems = Weapon.ContainedItems;
if (ammunition != null) foreach (RelatedItem requiredItem in WeaponComponent.requiredItems[RelatedItem.RelationType.Contained])
{ {
// Ammunition still remaining Item containedItem = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
return true; if (containedItem == null)
}
item = requiredItem;
ammunitionIdentifiers = requiredItem.Identifiers;
}
// No ammo
if (ammunition == null)
{
var container = Weapon.GetComponent<ItemContainer>();
// Try reload ammunition in inventory
foreach (string identifier in ammunitionIdentifiers)
{
foreach (var i in character.Inventory.Items)
{ {
if (i == null) { continue; } TryAddSubObjective(ref reloadWeaponObjective,
if (i.Prefab.Identifier == identifier || i.HasTag(identifier)) constructor: () => new AIObjectiveContainItem(character, requiredItem.Identifiers, Weapon.GetComponent<ItemContainer>(), objectiveManager),
{ onAbandon: () =>
if (i.Condition > 0)
{ {
container.Inventory.TryPutItem(ammunition, null); SteeringManager.Reset();
} Mode = CombatMode.Retreat;
} });
} }
} }
} }
if (WeaponComponent.HasRequiredContainedItems(false)) return reloadWeaponObjective == null || reloadWeaponObjective.IsCompleted();
{
return true;
}
else if (ammunition == null)
{
if (seekAmmo && ammunitionIdentifiers != null && (condition == null || condition()))
{
SeekAmmunition(ammunitionIdentifiers);
}
}
return false;
} }
private IEnumerable<FarseerPhysics.Dynamics.Body> myBodies;
private void Attack(float deltaTime) private void Attack(float deltaTime)
{ {
float squaredDistance = Vector2.DistanceSquared(character.Position, Enemy.Position); float squaredDistance = Vector2.DistanceSquared(character.Position, Enemy.Position);
@@ -457,16 +371,6 @@ namespace Barotrauma
character.SetInput(InputType.Aim, false, true); 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 (WeaponComponent is MeleeWeapon meleeWeapon)
{ {
if (squaredDistance <= meleeWeapon.Range * meleeWeapon.Range) if (squaredDistance <= meleeWeapon.Range * meleeWeapon.Range)
@@ -481,14 +385,14 @@ namespace Barotrauma
{ {
if (squaredDistance > repairTool.Range * repairTool.Range) { return; } if (squaredDistance > repairTool.Range * repairTool.Range) { return; }
} }
if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - Weapon.Position) < MathHelper.PiOver4) if (VectorExtensions.Angle(VectorExtensions.Forward(Weapon.body.TransformedRotation), Enemy.Position - character.Position) < MathHelper.PiOver4)
{ {
if (myBodies == null) if (myBodies == null)
{ {
myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody); myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
} }
var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall; var collisionCategories = Physics.CollisionCharacter | Physics.CollisionWall;
var pickedBody = Submarine.PickBody(Weapon.SimPosition, Enemy.SimPosition, myBodies, collisionCategories); var pickedBody = Submarine.PickBody(character.SimPosition, Enemy.SimPosition, myBodies, collisionCategories);
if (pickedBody != null) if (pickedBody != null)
{ {
Character target = null; Character target = null;
@@ -500,11 +404,10 @@ namespace Barotrauma
{ {
target = limb.character; target = limb.character;
} }
if (target != null && (target == Enemy || !HumanAIController.IsFriendly(target))) if (target != null && target == Enemy)
{ {
character.SetInput(InputType.Shoot, false, true); character.SetInput(InputType.Shoot, false, true);
Weapon.Use(deltaTime, character); Weapon.Use(deltaTime, character);
aimTimer = Rand.Range(0.25f, 0.5f);
} }
} }
} }
@@ -11,13 +11,12 @@ namespace Barotrauma
public Func<Item, float> GetItemPriority; public Func<Item, float> GetItemPriority;
public int targetItemCount = 1; public int MinContainedAmount = 1;
public string[] ignoredContainerIdentifiers; public string[] ignoredContainerIdentifiers;
public bool checkInventory = true;
//can either be a tag or an identifier //can either be a tag or an identifier
public readonly string[] itemIdentifiers; private readonly string[] itemIdentifiers;
public readonly ItemContainer container; private readonly ItemContainer container;
private bool isCompleted; private bool isCompleted;
private AIObjectiveGetItem getItemObjective; private AIObjectiveGetItem getItemObjective;
@@ -40,6 +39,7 @@ namespace Barotrauma
public override bool IsCompleted() public override bool IsCompleted()
{ {
if (isCompleted) { return true; }
int containedItemCount = 0; int containedItemCount = 0;
foreach (Item item in container.Inventory.Items) foreach (Item item in container.Inventory.Items)
{ {
@@ -48,7 +48,7 @@ namespace Barotrauma
containedItemCount++; containedItemCount++;
} }
} }
return containedItemCount >= targetItemCount; return containedItemCount >= MinContainedAmount;
} }
public override float GetPriority() public override float GetPriority()
@@ -62,6 +62,7 @@ namespace Barotrauma
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
if (isCompleted) { return; }
//get the item that should be contained //get the item that should be contained
Item itemToContain = null; Item itemToContain = null;
foreach (string identifier in itemIdentifiers) foreach (string identifier in itemIdentifiers)
@@ -71,13 +72,8 @@ namespace Barotrauma
} }
if (itemToContain == null) if (itemToContain == null)
{ {
if (getItemObjective != null && getItemObjective.IsCompleted())
{
getItemObjective = null;
targetItemCount--;
}
TryAddSubObjective(ref getItemObjective, () => TryAddSubObjective(ref getItemObjective, () =>
new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager, checkInventory: checkInventory) new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager)
{ {
GetItemPriority = GetItemPriority, GetItemPriority = GetItemPriority,
ignoredContainerIdentifiers = ignoredContainerIdentifiers ignoredContainerIdentifiers = ignoredContainerIdentifiers
@@ -86,6 +82,13 @@ namespace Barotrauma
} }
if (container.Item.ParentInventory == character.Inventory) 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); character.Inventory.RemoveItem(itemToContain);
container.Inventory.TryPutItem(itemToContain, null); container.Inventory.TryPutItem(itemToContain, null);
} }
@@ -99,6 +102,7 @@ namespace Barotrauma
} }
container.Combine(itemToContain); container.Combine(itemToContain);
} }
isCompleted = true;
} }
public override bool IsDuplicate(AIObjective otherObjective) public override bool IsDuplicate(AIObjective otherObjective)
@@ -10,7 +10,6 @@ namespace Barotrauma
class AIObjectiveFightIntruders : AIObjectiveLoop<Character> class AIObjectiveFightIntruders : AIObjectiveLoop<Character>
{ {
public override string DebugTag => "fight intruders"; public override string DebugTag => "fight intruders";
protected override float IgnoreListClearInterval => 30;
public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1) public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier) { } : base(character, objectiveManager, priorityModifier) { }
@@ -30,7 +29,7 @@ namespace Barotrauma
protected override IEnumerable<Character> GetList() => Character.CharacterList; protected override IEnumerable<Character> GetList() => Character.CharacterList;
protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier); protected override AIObjective ObjectiveConstructor(Character target) => new AIObjectiveCombat(character, target, AIObjectiveCombat.CombatMode.Offensive, objectiveManager, PriorityModifier) { useCoolDown = false };
protected override float TargetEvaluation() protected override float TargetEvaluation()
{ {
@@ -275,6 +275,31 @@ namespace Barotrauma
{ {
#if DEBUG #if DEBUG
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool"); 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<RepairTool>();
if (repairTool == null)
{
#if DEBUG
DebugConsole.ThrowError("AIObjectiveFixLeak failed - the item \"" + weldingTool + "\" has no RepairTool component but is tagged as a welding tool");
#endif #endif
abandon = true; abandon = true;
return; return;
@@ -41,10 +41,10 @@ namespace Barotrauma
this.targetItem = targetItem; this.targetItem = targetItem;
} }
public AIObjectiveGetItem(Character character, string itemIdentifier, AIObjectiveManager objectiveManager, bool equip = false, bool checkInventory = true, float priorityModifier = 1) public AIObjectiveGetItem(Character character, string itemIdentifier, AIObjectiveManager objectiveManager, bool equip = false, float priorityModifier = 1)
: this(character, new string[] { itemIdentifier }, objectiveManager, equip, checkInventory, priorityModifier) { } : this(character, new string[] { itemIdentifier }, objectiveManager, equip, priorityModifier) { }
public AIObjectiveGetItem(Character character, string[] itemIdentifiers, AIObjectiveManager objectiveManager, bool equip = false, bool checkInventory = true, float priorityModifier = 1) public AIObjectiveGetItem(Character character, string[] itemIdentifiers, AIObjectiveManager objectiveManager, bool equip = false, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier) : base(character, objectiveManager, priorityModifier)
{ {
currSearchIndex = -1; currSearchIndex = -1;
@@ -54,10 +54,7 @@ namespace Barotrauma
{ {
itemIdentifiers[i] = itemIdentifiers[i].ToLowerInvariant(); itemIdentifiers[i] = itemIdentifiers[i].ToLowerInvariant();
} }
if (checkInventory) CheckInventory();
{
CheckInventory();
}
} }
private void CheckInventory() private void CheckInventory()
@@ -228,7 +225,8 @@ namespace Barotrauma
public override bool IsDuplicate(AIObjective otherObjective) public override bool IsDuplicate(AIObjective otherObjective)
{ {
if (!(otherObjective is AIObjectiveGetItem getItem)) { return false; } AIObjectiveGetItem getItem = otherObjective as AIObjectiveGetItem;
if (getItem == null) { return false; }
if (getItem.equip != equip) { return false; } if (getItem.equip != equip) { return false; }
if (getItem.itemIdentifiers != null && itemIdentifiers != null) if (getItem.itemIdentifiers != null && itemIdentifiers != null)
{ {
@@ -248,11 +246,7 @@ namespace Barotrauma
public override bool IsCompleted() public override bool IsCompleted()
{ {
if (targetItem != null) if (itemIdentifiers != null)
{
return HasItem(targetItem);
}
else if (itemIdentifiers != null)
{ {
foreach (string itemName in itemIdentifiers) foreach (string itemName in itemIdentifiers)
{ {
@@ -264,6 +258,10 @@ namespace Barotrauma
} }
return false; return false;
} }
else if (targetItem != null)
{
return character.Inventory.Items.Contains(targetItem) && (!equip || character.HasEquippedItem(targetItem));
}
return false; return false;
} }
@@ -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 IsCompleted() => false;
public override bool CanBeCompleted => true; public override bool CanBeCompleted => true;
@@ -282,6 +282,10 @@ namespace Barotrauma
{ {
isCompleted = true; isCompleted = true;
} }
if (component.AIOperate(deltaTime, character, this))
{
isCompleted = true;
}
} }
else else
{ {
@@ -4,7 +4,6 @@ using System;
using System.Linq; using System.Linq;
using Barotrauma.Extensions; using Barotrauma.Extensions;
using FarseerPhysics; using FarseerPhysics;
using Barotrauma.Items.Components;
namespace Barotrauma namespace Barotrauma
{ {
@@ -15,7 +14,6 @@ namespace Barotrauma
public Item Item { get; private set; } public Item Item { get; private set; }
private AIObjectiveGoTo goToObjective; private AIObjectiveGoTo goToObjective;
private AIObjectiveContainItem refuelObjective;
private float previousCondition = -1; private float previousCondition = -1;
private RepairTool repairTool; private RepairTool repairTool;
@@ -73,46 +71,11 @@ namespace Barotrauma
} }
} }
// Only continue when the get item sub objectives have been completed. // Only continue when the get item sub objectives have been completed.
if (subObjectives.Any()) { return; } if (subObjectives.Any(so => so is AIObjectiveGetItem)) { return; }
if (repairTool == null) if (repairTool == null)
{ {
FindRepairTool(); 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<ItemContainer>(), objectiveManager));
return;
}
}
if (character.CurrentHull == Item.CurrentHull && character.CanInteractWith(Item)) if (character.CurrentHull == Item.CurrentHull && character.CanInteractWith(Item))
{ {
if (repairTool != null) if (repairTool != null)
@@ -149,7 +112,6 @@ namespace Barotrauma
} }
else else
{ {
RemoveSubObjective(ref refuelObjective);
// If cannot reach the item, approach it. // If cannot reach the item, approach it.
TryAddSubObjective(ref goToObjective, TryAddSubObjective(ref goToObjective,
constructor: () => constructor: () =>
@@ -203,9 +203,7 @@ namespace Barotrauma
if (startNode == null) if (startNode == null)
{ {
#if DEBUG
DebugConsole.NewMessage("Pathfinding error, couldn't find a start node. "+ errorMsgStr, Color.DarkRed); DebugConsole.NewMessage("Pathfinding error, couldn't find a start node. "+ errorMsgStr, Color.DarkRed);
#endif
return new SteeringPath(true); return new SteeringPath(true);
} }
@@ -255,9 +253,7 @@ namespace Barotrauma
if (endNode == null) if (endNode == null)
{ {
#if DEBUG
DebugConsole.NewMessage("Pathfinding error, couldn't find an end node. " + errorMsgStr, Color.DarkRed); DebugConsole.NewMessage("Pathfinding error, couldn't find an end node. " + errorMsgStr, Color.DarkRed);
#endif
return new SteeringPath(true); return new SteeringPath(true);
} }
@@ -285,9 +281,7 @@ namespace Barotrauma
if (startNode == null || endNode == null) if (startNode == null || endNode == null)
{ {
#if DEBUG
DebugConsole.NewMessage("Pathfinding error, couldn't find matching pathnodes to waypoints.", Color.DarkRed); DebugConsole.NewMessage("Pathfinding error, couldn't find matching pathnodes to waypoints.", Color.DarkRed);
#endif
return new SteeringPath(true); return new SteeringPath(true);
} }
@@ -132,11 +132,13 @@ namespace Barotrauma
#else #else
if (!CrewManager.GetCharacters().Contains(character)) { continue; } if (!CrewManager.GetCharacters().Contains(character)) { continue; }
#endif #endif
if (character.Submarine == Level.Loaded.StartOutpost && character.CurrentHull == startWatchman.CurrentHull) if (character.Submarine == Level.Loaded.StartOutpost &&
Vector2.DistanceSquared(character.WorldPosition, startWatchman.WorldPosition) < 500.0f * 500.0f)
{ {
CreateDialog(new List<Character> { startWatchman }, "EnterStartOutpost", 5 * 60.0f); CreateDialog(new List<Character> { startWatchman }, "EnterStartOutpost", 5 * 60.0f);
} }
else if (character.Submarine == Level.Loaded.EndOutpost && character.CurrentHull == endWatchman.CurrentHull) else if (character.Submarine == Level.Loaded.EndOutpost &&
Vector2.DistanceSquared(character.WorldPosition, endWatchman.WorldPosition) < 500.0f * 500.0f)
{ {
CreateDialog(new List<Character> { endWatchman }, "EnterEndOutpost", 5 * 60.0f); CreateDialog(new List<Character> { endWatchman }, "EnterEndOutpost", 5 * 60.0f);
} }
@@ -905,6 +905,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()) if (targetItem.Prefab.DeconstructItems.Any())
{ {
inputContainer.Inventory.RemoveItem(targetItem); inputContainer.Inventory.RemoveItem(targetItem);
@@ -501,7 +501,7 @@ namespace Barotrauma.Items.Components
{ {
var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent<ItemContainer>(), objective.objectiveManager) var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent<ItemContainer>(), objective.objectiveManager)
{ {
targetItemCount = item.ContainedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1, MinContainedAmount = item.ContainedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1,
GetItemPriority = (Item fuelItem) => GetItemPriority = (Item fuelItem) =>
{ {
if (fuelItem.ParentInventory?.Owner is Item) if (fuelItem.ParentInventory?.Owner is Item)
@@ -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 //edge point 1, edge point 2, avoid strength
private List<ObstacleDebugInfo> debugDrawObstacles = new List<ObstacleDebugInfo>(); private List<ObstacleDebugInfo> debugDrawObstacles = new List<ObstacleDebugInfo>();
@@ -125,7 +125,14 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < labels.Length; i++) for (int i = 0; i < labels.Length; i++)
{ {
labels[i] = i < newLabels.Length ? newLabels[i] : customInterfaceElementList[i].Label; labels[i] = i < newLabels.Length ? newLabels[i] : customInterfaceElementList[i].Label;
customInterfaceElementList[i].Label = TextManager.Get(labels[i], returnNull: true) ?? labels[i]; if (Screen.Selected != GameMain.SubEditorScreen)
{
customInterfaceElementList[i].Label = TextManager.Get(labels[i], returnNull: true) ?? labels[i];
}
else
{
customInterfaceElementList[i].Label = labels[i];
}
} }
UpdateLabelsProjSpecific(); UpdateLabelsProjSpecific();
} }
@@ -460,7 +460,7 @@ namespace Barotrauma.Items.Components
var containShellObjective = new AIObjectiveContainItem(character, container.ContainableItems[0].Identifiers[0], container, objective.objectiveManager); 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); character?.Speak(TextManager.Get("DialogLoadTurret").Replace("[itemname]", item.Name), null, 0.0f, "loadturret", 30.0f);
containShellObjective.targetItemCount = usableProjectileCount + 1; containShellObjective.MinContainedAmount = usableProjectileCount + 1;
containShellObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier }; containShellObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier };
objective.AddSubObjective(containShellObjective); objective.AddSubObjective(containShellObjective);
return false; return false;
@@ -1204,6 +1204,38 @@ namespace Barotrauma
} }
} }
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));
}
}
/// <summary> /// <summary>
/// Applies buoyancy, drag and angular drag caused by water /// Applies buoyancy, drag and angular drag caused by water
/// </summary> /// </summary>
@@ -443,12 +443,22 @@ namespace Barotrauma
identifier = element.GetAttributeString("identifier", ""); 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 //nameidentifier can be used to make multiple items use the same names and descriptions
string nameIdentifier = element.GetAttributeString("nameidentifier", ""); 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); Aliases = element.GetAttributeStringArray("aliases", new string[0], convertToLowerInvariant: true);
if (!Enum.TryParse(element.GetAttributeString("category", "Misc"), true, out MapEntityCategory category)) if (!Enum.TryParse(element.GetAttributeString("category", "Misc"), true, out MapEntityCategory category))
@@ -481,7 +491,15 @@ namespace Barotrauma
SerializableProperty.DeserializeProperties(this, element); SerializableProperty.DeserializeProperties(this, element);
string translatedDescription = TextManager.Get("EntityDescription." + identifier, true); string translatedDescription = "";
if (string.IsNullOrEmpty(nameIdentifier))
{
translatedDescription = TextManager.Get("EntityDescription." + identifier, true);
}
else
{
translatedDescription = TextManager.Get("EntityDescription." + nameIdentifier, true);
}
if (!string.IsNullOrEmpty(translatedDescription)) Description = translatedDescription; if (!string.IsNullOrEmpty(translatedDescription)) Description = translatedDescription;
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
@@ -246,6 +246,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 public override Rectangle Rect
{ {
get get
@@ -1364,7 +1364,7 @@ namespace Barotrauma
foreach (Hull hull in matchingHulls) foreach (Hull hull in matchingHulls)
{ {
if (string.IsNullOrEmpty(hull.RoomName)) if (string.IsNullOrEmpty(hull.RoomName) || !hull.RoomName.Contains("RoomName."))
{ {
hull.RoomName = hull.CreateRoomName(); hull.RoomName = hull.CreateRoomName();
} }
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+28
View File
@@ -1,3 +1,31 @@
---------------------------------------------------------------------------------------------------------
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 v0.8.10.0
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------