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