(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

View File

@@ -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
// -> we need to correct it manually
if (!character.AllowInput)
@@ -151,32 +206,34 @@ namespace Barotrauma
}
}
if (character.MemLocalState.Count > 120) character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120);
character.MemState.Clear();
character.MemLocalState.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)
else
{
if (impact > 3.0f) { PlayImpactSound(limb); }
}
else if (body.UserData is Limb || body == Collider.FarseerBody)
{
if (!character.IsRemotePlayer && impact > ImpactTolerance)
//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)
character.MemState.RemoveAll(m => m.Timestamp > 0.0f);
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;

View File

@@ -198,7 +198,9 @@ namespace Barotrauma
focusName = character.FocusedCharacter.Info.DisplayName;
}
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;
if (character.TeamID != character.FocusedCharacter.TeamID)
@@ -207,23 +209,23 @@ namespace Barotrauma
}
GUI.DrawString(spriteBatch, textPos, focusName, nameColor, Color.Black * 0.7f, 2);
textPos.Y += 20;
textPos.Y += offset.Y;
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 += 15;
textPos.Y += offset.Y;
}
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 += 15;
textPos.Y += offset.Y;
}
if (!string.IsNullOrEmpty(character.FocusedCharacter.customInteractHUDText))
{
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);
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) * 20;
startPos.Y -= (hudTexts.Count + 1) * offset.Y;
if (focusedItem.Sprite != null)
{
startPos.X += (int)(circleSize * 0.4f * dir);
@@ -268,17 +272,17 @@ namespace Barotrauma
}
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);
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)
{
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 += 20;
textPos.Y += offset.Y;
}
}
}

View File

@@ -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<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);
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<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);
if (radioItem == null) return null;
if (requireEquipped && !character.HasEquippedItem(radioItem)) return null;
return radioItem.GetComponent<WifiComponent>();
characterInfos.Add(characterInfo);
}
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;
}
List<Character> 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<object> KillCharacterAnim(GUIComponent component)
@@ -779,6 +954,12 @@ namespace Barotrauma
}
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);
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);
}
/// <summary>
/// Create the UI panel that's used to select the target and options for a given order

View File

@@ -35,7 +35,7 @@ namespace Barotrauma
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;
@@ -448,7 +448,8 @@ namespace Barotrauma
{
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)
{
hidePersonalSlots = false;

View File

@@ -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("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.OnTextChanged += (textBox, text) => { FilterEntities(text); return true; };
var clearButton = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), filterArea.RectTransform), "x")

View File

@@ -20,7 +20,7 @@ namespace Barotrauma
{
Rectangle drawRect = entity.Second;
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));
entity.First.DrawPlacing(spriteBatch, drawRect, entity.First.Scale * scale);
}

View File

@@ -440,6 +440,8 @@ namespace Barotrauma
private GUILayoutGroup subPreviewContainer;
private GUILayoutGroup subPreviewContainer;
private GUIButton loadGameButton;
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 };
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);
searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };

View File

@@ -180,7 +180,7 @@ namespace Barotrauma
{
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.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };

View File

@@ -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

View File

@@ -300,7 +300,7 @@ namespace Barotrauma
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.OnTextChanged += (textBox, text) => { FilterEntities(text); return true; };
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
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;
@@ -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);
searchBox.OnSelected += (sender, userdata) => { searchTitle.Visible = false; };
searchBox.OnDeselected += (sender, userdata) => { searchTitle.Visible = true; };

View File

@@ -74,6 +74,7 @@
<Character file="Content/Characters/Hammerhead/Hammerhead.xml" />
<Outpost file="Content/Map/Outposts/Outpost.sub" />
<Outpost file="Content/Map/Outposts/Outpost2.sub" />
<Outpost file="Content/Map/Outposts/Outpost3.sub" />
<Submarine file="Submarines/Orca.sub" />
<Submarine file="Submarines/Typhon.sub" />
<Submarine file="Submarines/Selkie.sub" />

View File

@@ -391,6 +391,9 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Command\NavUI.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Containers\DivingSuitLocker2.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Door\door2.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -493,6 +496,7 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Map\OutpostWall_C.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Map\SubRearBow2.png" />
<Content Include="$(MSBuildThisFileDirectory)Content\Map\Zones\Ice\Zone4BaseTexture.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -2171,6 +2175,9 @@
<None Include="$(MSBuildThisFileDirectory)Content\Map\Outposts\Outpost2.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Map\Outposts\Outpost3.sub">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="$(MSBuildThisFileDirectory)Content\Sounds\Damage\Gore1.ogg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>

View File

@@ -106,18 +106,6 @@ namespace Barotrauma
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()
{
if (subObjectives.None()) { return; }

View File

@@ -11,10 +11,9 @@ namespace Barotrauma
class AIObjectiveCombat : AIObjective
{
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; }
@@ -26,7 +25,14 @@ namespace Barotrauma
{
_weapon = value;
_weaponComponent = null;
RemoveSubObjective(ref seekAmmunition);
if (reloadWeaponObjective != null)
{
if (subObjectives.Contains(reloadWeaponObjective))
{
subObjectives.Remove(reloadWeaponObjective);
}
reloadWeaponObjective = null;
}
}
}
private ItemComponent _weaponComponent;
@@ -34,7 +40,6 @@ namespace Barotrauma
{
get
{
if (Weapon == null) { return null; }
if (_weaponComponent == null)
{
_weaponComponent =
@@ -53,14 +58,12 @@ namespace Barotrauma
private readonly HashSet<MeleeWeapon> meleeWeapons = new HashSet<MeleeWeapon>();
private readonly HashSet<Item> adHocWeapons = new HashSet<Item>();
private AIObjectiveContainItem seekAmmunition;
private AIObjectiveContainItem reloadWeaponObjective;
private AIObjectiveGoTo retreatObjective;
private AIObjectiveGoTo followTargetObjective;
private Hull retreatTarget;
private float coolDownTimer;
private IEnumerable<FarseerPhysics.Dynamics.Body> myBodies;
private float aimTimer;
public enum CombatMode
{
@@ -75,7 +78,7 @@ namespace Barotrauma
: base(character, objectiveManager, priorityModifier)
{
Enemy = enemy;
coolDownTimer = coolDown;
coolDownTimer = CoolDown;
findSafety = objectiveManager.GetObjective<AIObjectiveFindSafety>();
if (findSafety != null)
{
@@ -83,7 +86,6 @@ namespace Barotrauma
findSafety.unreachable.Clear();
}
Mode = mode;
initialMode = Mode;
if (Enemy == null)
{
Mode = CombatMode.Retreat;
@@ -102,7 +104,7 @@ namespace Barotrauma
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 (objectiveManager.CurrentOrder == this && Enemy != null && Enemy.IsDead)
@@ -119,83 +121,39 @@ namespace Barotrauma
protected override void Act(float deltaTime)
{
if (initialMode != CombatMode.Offensive)
if (useCoolDown)
{
coolDownTimer -= deltaTime;
}
if (abandon) { return; }
TryArm();
if (seekAmmunition == null || !subObjectives.Contains(seekAmmunition))
{
Move();
if (Weapon != null)
{
OperateWeapon(deltaTime);
}
}
Arm(deltaTime);
Move(deltaTime);
}
private void Move()
private void Arm(float deltaTime)
{
switch (Mode)
{
case CombatMode.Offensive:
Engage();
break;
case CombatMode.Defensive:
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))
if (Weapon != null && !character.Inventory.Items.Contains(_weapon) || _weaponComponent != null && !_weaponComponent.HasRequiredContainedItems(false))
{
if (seekAmmunition != null && subObjectives.Contains(seekAmmunition))
{
return false;
}
else
{
Weapon = null;
}
Weapon = null;
}
if (Weapon == null)
{
Weapon = GetWeapon();
}
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())
{
Attack(deltaTime);
if (Reload(deltaTime))
{
Attack(deltaTime);
}
}
break;
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();
meleeWeapons.Clear();
@@ -219,14 +193,14 @@ namespace Barotrauma
{
if (component is RangedWeapon rw)
{
if (ignoreRequiredItems || rw.HasRequiredContainedItems(false))
if (rw.HasRequiredContainedItems(false))
{
rangedWeapons.Add(rw);
}
}
else if (component is MeleeWeapon mw)
{
if (ignoreRequiredItems || mw.HasRequiredContainedItems(false))
if (mw.HasRequiredContainedItems(false))
{
meleeWeapons.Add(mw);
}
@@ -242,7 +216,7 @@ namespace Barotrauma
{
if (statusEffect.Afflictions.Any())
{
if (ignoreRequiredItems || component.HasRequiredContainedItems(false))
if (component.HasRequiredContainedItems(false))
{
adHocWeapons.Add(item);
}
@@ -279,29 +253,19 @@ namespace Barotrauma
Weapon.Drop(character);
}
}
return true;
}
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));
if (character.Inventory.TryPutItem(Weapon, character, slots))
{
Weapon.Equip(character);
aimTimer = Rand.Range(0.5f, 1f);
}
else
{
Weapon = null;
Mode = CombatMode.Retreat;
return false;
}
@@ -309,10 +273,16 @@ namespace Barotrauma
return true;
}
private void Retreat()
private void Retreat(float deltaTime)
{
RemoveSubObjective(ref followTargetObjective);
RemoveSubObjective(ref seekAmmunition);
if (followTargetObjective != null)
{
if (subObjectives.Contains(followTargetObjective))
{
subObjectives.Remove(followTargetObjective);
}
followTargetObjective = null;
}
if (retreatObjective != null && retreatObjective.Target != retreatTarget)
{
retreatObjective = null;
@@ -324,115 +294,59 @@ namespace Barotrauma
TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true));
}
private void Engage()
private void Engage(float deltaTime)
{
retreatTarget = null;
RemoveSubObjective(ref retreatObjective);
RemoveSubObjective(ref seekAmmunition);
if (followTargetObjective != null && followTargetObjective.Target != Enemy)
if (retreatObjective != null)
{
followTargetObjective = null;
if (subObjectives.Contains(retreatObjective))
{
subObjectives.Remove(retreatObjective);
}
retreatObjective = null;
}
TryAddSubObjective(ref followTargetObjective,
constructor: () => new AIObjectiveGoTo(Enemy, character, objectiveManager, repeat: true, getDivingGearIfNeeded: true)
{
AllowGoingOutside = 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: () =>
{
Mode = CombatMode.Retreat;
SteeringManager.Reset();
Mode = CombatMode.Retreat;
});
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)
private bool Reload(float deltaTime)
{
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;
SteeringManager.Reset();
});
}
/// <summary>
/// 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.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])
if (WeaponComponent != null && WeaponComponent.requiredItems.ContainsKey(RelatedItem.RelationType.Contained))
{
ammunition = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
if (ammunition != null)
var containedItems = Weapon.ContainedItems;
foreach (RelatedItem requiredItem in WeaponComponent.requiredItems[RelatedItem.RelationType.Contained])
{
// Ammunition still remaining
return true;
}
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)
Item containedItem = containedItems.FirstOrDefault(it => it.Condition > 0.0f && requiredItem.MatchesItem(it));
if (containedItem == null)
{
if (i == null) { continue; }
if (i.Prefab.Identifier == identifier || i.HasTag(identifier))
{
if (i.Condition > 0)
TryAddSubObjective(ref reloadWeaponObjective,
constructor: () => new AIObjectiveContainItem(character, requiredItem.Identifiers, Weapon.GetComponent<ItemContainer>(), objectiveManager),
onAbandon: () =>
{
container.Inventory.TryPutItem(ammunition, null);
}
}
SteeringManager.Reset();
Mode = CombatMode.Retreat;
});
}
}
}
if (WeaponComponent.HasRequiredContainedItems(false))
{
return true;
}
else if (ammunition == null)
{
if (seekAmmo && ammunitionIdentifiers != null && (condition == null || condition()))
{
SeekAmmunition(ammunitionIdentifiers);
}
}
return false;
return reloadWeaponObjective == null || reloadWeaponObjective.IsCompleted();
}
private IEnumerable<FarseerPhysics.Dynamics.Body> myBodies;
private void Attack(float deltaTime)
{
float squaredDistance = Vector2.DistanceSquared(character.Position, Enemy.Position);
@@ -457,16 +371,6 @@ 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)
@@ -481,14 +385,14 @@ namespace Barotrauma
{
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)
{
myBodies = character.AnimController.Limbs.Select(l => l.body.FarseerBody);
}
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)
{
Character target = null;
@@ -500,11 +404,10 @@ namespace Barotrauma
{
target = limb.character;
}
if (target != null && (target == Enemy || !HumanAIController.IsFriendly(target)))
if (target != null && target == Enemy)
{
character.SetInput(InputType.Shoot, false, true);
Weapon.Use(deltaTime, character);
aimTimer = Rand.Range(0.25f, 0.5f);
}
}
}

View File

@@ -11,13 +11,12 @@ namespace Barotrauma
public Func<Item, float> GetItemPriority;
public int targetItemCount = 1;
public int MinContainedAmount = 1;
public string[] ignoredContainerIdentifiers;
public bool checkInventory = true;
//can either be a tag or an identifier
public readonly string[] itemIdentifiers;
public readonly ItemContainer container;
private readonly string[] itemIdentifiers;
private readonly ItemContainer container;
private bool isCompleted;
private AIObjectiveGetItem getItemObjective;
@@ -40,6 +39,7 @@ 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 >= targetItemCount;
return containedItemCount >= MinContainedAmount;
}
public override float GetPriority()
@@ -62,6 +62,7 @@ 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)
@@ -71,13 +72,8 @@ namespace Barotrauma
}
if (itemToContain == null)
{
if (getItemObjective != null && getItemObjective.IsCompleted())
{
getItemObjective = null;
targetItemCount--;
}
TryAddSubObjective(ref getItemObjective, () =>
new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager, checkInventory: checkInventory)
new AIObjectiveGetItem(character, itemIdentifiers, objectiveManager)
{
GetItemPriority = GetItemPriority,
ignoredContainerIdentifiers = ignoredContainerIdentifiers
@@ -85,7 +81,14 @@ 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);
}
@@ -99,6 +102,7 @@ namespace Barotrauma
}
container.Combine(itemToContain);
}
isCompleted = true;
}
public override bool IsDuplicate(AIObjective otherObjective)

View File

@@ -10,7 +10,6 @@ namespace Barotrauma
class AIObjectiveFightIntruders : AIObjectiveLoop<Character>
{
public override string DebugTag => "fight intruders";
protected override float IgnoreListClearInterval => 30;
public AIObjectiveFightIntruders(Character character, AIObjectiveManager objectiveManager, float priorityModifier = 1)
: base(character, objectiveManager, priorityModifier) { }
@@ -30,7 +29,7 @@ namespace Barotrauma
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()
{

View File

@@ -275,6 +275,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<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
abandon = true;
return;

View File

@@ -41,10 +41,10 @@ namespace Barotrauma
this.targetItem = targetItem;
}
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 itemIdentifier, AIObjectiveManager objectiveManager, bool equip = false, float priorityModifier = 1)
: 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)
{
currSearchIndex = -1;
@@ -54,10 +54,7 @@ namespace Barotrauma
{
itemIdentifiers[i] = itemIdentifiers[i].ToLowerInvariant();
}
if (checkInventory)
{
CheckInventory();
}
CheckInventory();
}
private void CheckInventory()
@@ -228,7 +225,8 @@ namespace Barotrauma
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.itemIdentifiers != null && itemIdentifiers != null)
{
@@ -248,11 +246,7 @@ namespace Barotrauma
public override bool IsCompleted()
{
if (targetItem != null)
{
return HasItem(targetItem);
}
else if (itemIdentifiers != null)
if (itemIdentifiers != null)
{
foreach (string itemName in itemIdentifiers)
{
@@ -264,6 +258,10 @@ namespace Barotrauma
}
return false;
}
else if (targetItem != null)
{
return character.Inventory.Items.Contains(targetItem) && (!equip || character.HasEquippedItem(targetItem));
}
return false;
}

View File

@@ -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;

View File

@@ -282,6 +282,10 @@ namespace Barotrauma
{
isCompleted = true;
}
if (component.AIOperate(deltaTime, character, this))
{
isCompleted = true;
}
}
else
{

View File

@@ -4,7 +4,6 @@ using System;
using System.Linq;
using Barotrauma.Extensions;
using FarseerPhysics;
using Barotrauma.Items.Components;
namespace Barotrauma
{
@@ -15,7 +14,6 @@ namespace Barotrauma
public Item Item { get; private set; }
private AIObjectiveGoTo goToObjective;
private AIObjectiveContainItem refuelObjective;
private float previousCondition = -1;
private RepairTool repairTool;
@@ -73,46 +71,11 @@ namespace Barotrauma
}
}
// 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)
{
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 (repairTool != null)
@@ -149,7 +112,6 @@ namespace Barotrauma
}
else
{
RemoveSubObjective(ref refuelObjective);
// If cannot reach the item, approach it.
TryAddSubObjective(ref goToObjective,
constructor: () =>

View File

@@ -203,9 +203,7 @@ 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);
}
@@ -255,9 +253,7 @@ 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);
}
@@ -285,9 +281,7 @@ 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);
}

View File

@@ -132,11 +132,13 @@ namespace Barotrauma
#else
if (!CrewManager.GetCharacters().Contains(character)) { continue; }
#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);
}
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);
}

View File

@@ -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())
{
inputContainer.Inventory.RemoveItem(targetItem);

View File

@@ -501,7 +501,7 @@ namespace Barotrauma.Items.Components
{
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) =>
{
if (fuelItem.ParentInventory?.Owner is Item)

View File

@@ -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<ObstacleDebugInfo> debugDrawObstacles = new List<ObstacleDebugInfo>();

View File

@@ -125,7 +125,14 @@ namespace Barotrauma.Items.Components
for (int i = 0; i < labels.Length; i++)
{
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();
}

View File

@@ -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.targetItemCount = usableProjectileCount + 1;
containShellObjective.MinContainedAmount = usableProjectileCount + 1;
containShellObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier };
objective.AddSubObjective(containShellObjective);
return false;

View File

@@ -1172,6 +1172,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));
}
}
public void UpdateTransform()
{
Submarine prevSub = Submarine;

View File

@@ -443,12 +443,22 @@ 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))
@@ -481,7 +491,15 @@ namespace Barotrauma
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;
foreach (XElement subElement in element.Elements())

View File

@@ -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
{
get

View File

@@ -1364,7 +1364,7 @@ namespace Barotrauma
foreach (Hull hull in matchingHulls)
{
if (string.IsNullOrEmpty(hull.RoomName))
if (string.IsNullOrEmpty(hull.RoomName) || !hull.RoomName.Contains("RoomName."))
{
hull.RoomName = hull.CreateRoomName();
}

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
---------------------------------------------------------------------------------------------------------