Release v0.15.13.0

This commit is contained in:
Joonas Rikkonen
2021-11-02 13:19:48 +02:00
parent a52a23f8c2
commit 3a35dbfe6c
77 changed files with 509 additions and 224 deletions
+11 -1
View File
@@ -20,6 +20,9 @@ bld/
*.shproj.user *.shproj.user
*.vcxproj.user *.vcxproj.user
# Rider
.idea/
# Platform-specific webm_mem_playback files # Platform-specific webm_mem_playback files
Libraries/webm_mem_playback/libvpx_x64_linux/ Libraries/webm_mem_playback/libvpx_x64_linux/
Libraries/webm_mem_playback/libvpx_x64_vs15/ Libraries/webm_mem_playback/libvpx_x64_vs15/
@@ -40,5 +43,12 @@ Libraries/webm_mem_playback/opus_x64_linux/
# Win # Win
desktop.ini desktop.ini
#Merge script # Merge script
temp.txt temp.txt
# Private assets
Barotrauma/BarotraumaShared/Content/*
.github/ISSUE_TEMPLATE/release-checklist.md
#Rider
*.DotSettings.user
@@ -338,15 +338,18 @@ namespace Barotrauma
previousOffset = offset; previousOffset = offset;
} }
//how much to zoom out (zoom completely out when offset is 1000) if (allowZoom)
float zoomOutAmount = GetZoomAmount(offset); {
//scaled zoom amount //how much to zoom out (zoom completely out when offset is 1000)
float scaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount) * globalZoomScale; float zoomOutAmount = GetZoomAmount(offset);
//zoom in further if zoomOutAmount is low and resolution is lower than reference //scaled zoom amount
float newZoom = scaledZoom * (MathHelper.Lerp(0.3f * (1f - Math.Min(globalZoomScale, 1f)), 0f, float scaledZoom = MathHelper.Lerp(DefaultZoom, MinZoom, zoomOutAmount) * globalZoomScale;
(GameMain.Config == null || GameMain.Config.EnableMouseLook) ? (float)Math.Sqrt(offsetUnscaledLen) : 0.3f) + 1f); //zoom in further if zoomOutAmount is low and resolution is lower than reference
float newZoom = scaledZoom * (MathHelper.Lerp(0.3f * (1f - Math.Min(globalZoomScale, 1f)), 0f,
(GameMain.Config == null || GameMain.Config.EnableMouseLook) ? (float)Math.Sqrt(offsetUnscaledLen) : 0.3f) + 1f);
Zoom += (newZoom - zoom) / ZoomSmoothness; Zoom += (newZoom - zoom) / ZoomSmoothness;
}
//force targetzoom to the current zoom value, so the camera stays at the same zoom when switching to freecam //force targetzoom to the current zoom value, so the camera stays at the same zoom when switching to freecam
targetZoom = Zoom; targetZoom = Zoom;
@@ -332,7 +332,7 @@ namespace Barotrauma
{ {
cam.OffsetAmount = targetOffsetAmount = 0.0f; cam.OffsetAmount = targetOffsetAmount = 0.0f;
cursorPosition = cursorPosition =
SelectedConstruction.Position + Position +
PlayerInput.MouseSpeed.ClampLength(10.0f); //apply a little bit of movement to the cursor pos to prevent AFK kicking PlayerInput.MouseSpeed.ClampLength(10.0f); //apply a little bit of movement to the cursor pos to prevent AFK kicking
} }
else if (!GameMain.Config.EnableMouseLook) else if (!GameMain.Config.EnableMouseLook)
@@ -1096,6 +1096,7 @@ namespace Barotrauma
private SoundChannel soundChannel; private SoundChannel soundChannel;
public void PlaySound(CharacterSound.SoundType soundType, float soundIntervalFactor = 1.0f, float maxInterval = 0) public void PlaySound(CharacterSound.SoundType soundType, float soundIntervalFactor = 1.0f, float maxInterval = 0)
{ {
if (Removed) { return; }
if (sounds == null || sounds.Count == 0) { return; } if (sounds == null || sounds.Count == 0) { return; }
if (soundChannel != null && soundChannel.IsPlaying) { return; } if (soundChannel != null && soundChannel.IsPlaying) { return; }
if (GameMain.SoundManager?.Disabled ?? true) { return; } if (GameMain.SoundManager?.Disabled ?? true) { return; }
@@ -452,11 +452,17 @@ namespace Barotrauma
ushort itemID = msg.ReadUInt16(); ushort itemID = msg.ReadUInt16();
if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; } if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; }
item.AllowStealing = true; item.AllowStealing = true;
var wifiComponent = item.GetComponent<Items.Components.WifiComponent>(); var wifiComponent = item.GetComponent<WifiComponent>();
if (wifiComponent != null) if (wifiComponent != null)
{ {
wifiComponent.TeamID = teamID; wifiComponent.TeamID = teamID;
} }
var idCard = item.GetComponent<IdCard>();
if (idCard != null)
{
idCard.TeamID = teamID;
idCard.SubmarineSpecificID = 0;
}
} }
break; break;
case 10: //NetEntityEvent.Type.UpdateExperience case 10: //NetEntityEvent.Type.UpdateExperience
@@ -83,7 +83,13 @@ namespace Barotrauma
var equipIdentifiers = Element.GetChildElements("ItemSet").Elements().Where(e => e.GetAttributeBool("outfit", false)).Select(e => e.GetAttributeString("identifier", "")); var equipIdentifiers = Element.GetChildElements("ItemSet").Elements().Where(e => e.GetAttributeBool("outfit", false)).Select(e => e.GetAttributeString("identifier", ""));
var outfitPrefabs = ItemPrefab.Prefabs.Where(itemPrefab => equipIdentifiers.Contains(itemPrefab.Identifier)).ToList(); List<ItemPrefab> outfitPrefabs = new List<ItemPrefab>();
foreach (var equipIdentifier in equipIdentifiers)
{
var itemPrefab = ItemPrefab.Prefabs.Find(ip => ip.Identifier == equipIdentifier);
if (itemPrefab != null) { outfitPrefabs.Add(itemPrefab); }
}
if (!outfitPrefabs.Any()) { return null; } if (!outfitPrefabs.Any()) { return null; }
for (int i = 0; i < outfitPrefabs.Count; i++) for (int i = 0; i < outfitPrefabs.Count; i++)
@@ -256,6 +256,15 @@ namespace Barotrauma
/// </summary> /// </summary>
public bool CanInteractWhenUnfocusable { get; set; } = false; public bool CanInteractWhenUnfocusable { get; set; } = false;
public override Rectangle MouseRect
{
get
{
if (!CanBeFocused && !CanInteractWhenUnfocusable) { return Rectangle.Empty; }
return ClampMouseRectToParent ? ClampRect(Rect) : Rect;
}
}
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param> /// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT) public GUIListBox(RectTransform rectT, bool isHorizontal = false, Color? color = null, string style = "", bool isScrollBarOnDefaultSide = true, bool useMouseDownToSelect = false) : base(style, rectT)
{ {
@@ -771,7 +780,12 @@ namespace Barotrauma
} }
} }
if (PlayerInput.ScrollWheelSpeed != 0 && AllowMouseWheelScroll && (FindScrollableParentListBox(GUI.MouseOn) == this || GUI.IsMouseOn(ScrollBar))) bool IsMouseOn() =>
FindScrollableParentListBox(GUI.MouseOn) == this ||
GUI.IsMouseOn(ScrollBar) ||
(CanInteractWhenUnfocusable && Content.Rect.Contains(PlayerInput.MousePosition));
if (PlayerInput.ScrollWheelSpeed != 0 && AllowMouseWheelScroll && IsMouseOn())
{ {
if (SmoothScroll) if (SmoothScroll)
{ {
@@ -463,7 +463,6 @@ namespace Barotrauma
{ {
SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo; SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo;
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch; GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
PendingSubmarineSwitch = null;
for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++) for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++)
{ {
@@ -476,6 +475,7 @@ namespace Barotrauma
} }
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
PendingSubmarineSwitch = null;
} }
else else
{ {
@@ -516,6 +516,9 @@ namespace Barotrauma
} }
} }
private readonly static List<SlotReference> hideSubInventories = new List<SlotReference>();
private readonly static List<SlotReference> tempHighlightedSubInventorySlots = new List<SlotReference>();
public override void Update(float deltaTime, Camera cam, bool isSubInventory = false) public override void Update(float deltaTime, Camera cam, bool isSubInventory = false)
{ {
if (!AccessibleWhenAlive && !character.IsDead) if (!AccessibleWhenAlive && !character.IsDead)
@@ -580,14 +583,16 @@ namespace Barotrauma
} }
} }
List<SlotReference> hideSubInventories = new List<SlotReference>(); hideSubInventories.Clear();
//remove highlighted subinventory slots that can no longer be accessed //remove highlighted subinventory slots that can no longer be accessed
highlightedSubInventorySlots.RemoveWhere(s => highlightedSubInventorySlots.RemoveWhere(s =>
s.ParentInventory == this && s.ParentInventory == this &&
((s.SlotIndex < 0 || s.SlotIndex >= slots.Length || slots[s.SlotIndex] == null) || (Character.Controlled != null && !Character.Controlled.CanAccessInventory(s.Inventory)))); ((s.SlotIndex < 0 || s.SlotIndex >= slots.Length || slots[s.SlotIndex] == null) || (Character.Controlled != null && !Character.Controlled.CanAccessInventory(s.Inventory))));
//remove highlighted subinventory slots that refer to items no longer in this inventory //remove highlighted subinventory slots that refer to items no longer in this inventory
highlightedSubInventorySlots.RemoveWhere(s => s.Item != null && s.ParentInventory == this && s.Item.ParentInventory != this); highlightedSubInventorySlots.RemoveWhere(s => s.Item != null && s.ParentInventory == this && s.Item.ParentInventory != this);
foreach (var highlightedSubInventorySlot in highlightedSubInventorySlots) tempHighlightedSubInventorySlots.Clear();
tempHighlightedSubInventorySlots.AddRange(highlightedSubInventorySlots);
foreach (var highlightedSubInventorySlot in tempHighlightedSubInventorySlots)
{ {
if (highlightedSubInventorySlot.ParentInventory == this) if (highlightedSubInventorySlot.ParentInventory == this)
{ {
@@ -300,6 +300,8 @@ namespace Barotrauma.Items.Components
{ {
requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform)); requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform));
} }
HideEmptyItemListCategories();
} }
private void DrawInputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent) private void DrawInputOverLay(SpriteBatch spriteBatch, GUICustomComponent overlayComponent)
@@ -479,6 +481,13 @@ namespace Barotrauma.Items.Components
child.Visible = recipe.DisplayName.ToLower().Contains(filter); child.Visible = recipe.DisplayName.ToLower().Contains(filter);
} }
HideEmptyItemListCategories();
return true;
}
private void HideEmptyItemListCategories()
{
//go through the elements backwards, and disable the labels ("insufficient skills to fabricate", "recipe required...") if there's no items below them //go through the elements backwards, and disable the labels ("insufficient skills to fabricate", "recipe required...") if there's no items below them
bool recipeVisible = false; bool recipeVisible = false;
foreach (GUIComponent child in itemList.Content.Children.Reverse()) foreach (GUIComponent child in itemList.Content.Children.Reverse())
@@ -490,14 +499,12 @@ namespace Barotrauma.Items.Components
} }
else else
{ {
recipeVisible = child.Visible; recipeVisible |= child.Visible;
} }
} }
itemList.UpdateScrollBarSize(); itemList.UpdateScrollBarSize();
itemList.BarScroll = 0.0f; itemList.BarScroll = 0.0f;
return true;
} }
public bool ClearFilter() public bool ClearFilter()
@@ -36,7 +36,7 @@ namespace Barotrauma.Items.Components
internal readonly struct MiniMapSprite internal readonly struct MiniMapSprite
{ {
public readonly Sprite Sprite; public readonly Sprite? Sprite;
public readonly Color Color; public readonly Color Color;
public MiniMapSprite(JobPrefab prefab) public MiniMapSprite(JobPrefab prefab)
@@ -1302,6 +1302,8 @@ namespace Barotrauma.Items.Components
int i = 0; int i = 0;
foreach (MiniMapSprite info in cardsToDraw) foreach (MiniMapSprite info in cardsToDraw)
{ {
if (info.Sprite is null) { continue; }
float spriteSize = info.Sprite.size.X * (parentWidth / info.Sprite.size.X) + padding; float spriteSize = info.Sprite.size.X * (parentWidth / info.Sprite.size.X) + padding;
if (totalWidth + spriteSize > frame.Rect.Width) { break; } if (totalWidth + spriteSize > frame.Rect.Width) { break; }
@@ -1318,7 +1320,8 @@ namespace Barotrauma.Items.Components
foreach (MiniMapSprite info in cardsToDraw) foreach (MiniMapSprite info in cardsToDraw)
{ {
Sprite sprite = info.Sprite; Sprite? sprite = info.Sprite;
if (sprite is null) { continue; }
float scale = parentWidth / sprite.size.X; float scale = parentWidth / sprite.size.X;
float spriteSize = sprite.size.X * scale; float spriteSize = sprite.size.X * scale;
float posX = adjustedCenterX + offset; float posX = adjustedCenterX + offset;
@@ -88,7 +88,7 @@ namespace Barotrauma.Items.Components
Vector2 startPos = GetSourcePos(); Vector2 startPos = GetSourcePos();
startPos.Y = -startPos.Y; startPos.Y = -startPos.Y;
if (source is Item sourceItem) if (source is Item sourceItem && !sourceItem.Removed)
{ {
var turret = sourceItem.GetComponent<Turret>(); var turret = sourceItem.GetComponent<Turret>();
var weapon = sourceItem.GetComponent<RangedWeapon>(); var weapon = sourceItem.GetComponent<RangedWeapon>();
@@ -540,7 +540,7 @@ namespace Barotrauma
} }
}*/ }*/
bool mouseOn = interactRect.Contains(PlayerInput.MousePosition) && !Locked && !mouseOnGUI && !slot.Disabled; bool mouseOn = interactRect.Contains(PlayerInput.MousePosition) && !Locked && !mouseOnGUI && !slot.Disabled && IsMouseOnInventory;
// Delete item from container in sub editor // Delete item from container in sub editor
if (SubEditorScreen.IsSubEditor() && PlayerInput.IsCtrlDown()) if (SubEditorScreen.IsSubEditor() && PlayerInput.IsCtrlDown())
@@ -863,6 +863,8 @@ namespace Barotrauma
{ {
return false; return false;
} }
if (GameSession.IsTabMenuOpen) { return false; }
if (CrewManager.IsCommandInterfaceOpen) { return false; }
if (Character.Controlled == null) { return false; } if (Character.Controlled == null) { return false; }
@@ -1313,6 +1315,10 @@ namespace Barotrauma
private static bool CanSelectSlot(SlotReference selectedSlot) private static bool CanSelectSlot(SlotReference selectedSlot)
{ {
if (!IsMouseOnInventory)
{
return false;
}
if (!selectedSlot.Slot.MouseOn()) if (!selectedSlot.Slot.MouseOn())
{ {
return false; return false;
@@ -1335,7 +1341,8 @@ namespace Barotrauma
if ((parentItem?.GetRootInventoryOwner() is Character ownerCharacter) && if ((parentItem?.GetRootInventoryOwner() is Character ownerCharacter) &&
ownerCharacter == Character.Controlled && ownerCharacter == Character.Controlled &&
CharacterHealth.OpenHealthWindow?.Character != ownerCharacter && CharacterHealth.OpenHealthWindow?.Character != ownerCharacter &&
ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface)) ownerCharacter.Inventory.IsInLimbSlot(parentItem, InvSlotType.HealthInterface) &&
Screen.Selected != GameMain.SubEditorScreen)
{ {
highlightedSubInventorySlots.RemoveWhere(s => s.Item == parentItem); highlightedSubInventorySlots.RemoveWhere(s => s.Item == parentItem);
return false; return false;
@@ -543,6 +543,7 @@ namespace Barotrauma
partial void OnCollisionProjSpecific(float impact) partial void OnCollisionProjSpecific(float impact)
{ {
if (impact > 1.0f && if (impact > 1.0f &&
Container == null &&
!string.IsNullOrEmpty(Prefab.ImpactSoundTag) && !string.IsNullOrEmpty(Prefab.ImpactSoundTag) &&
Timing.TotalTime > LastImpactSoundTime + ImpactSoundInterval) Timing.TotalTime > LastImpactSoundTime + ImpactSoundInterval)
{ {
@@ -98,12 +98,10 @@ namespace Barotrauma
private IEnumerable<object> DimLight(LightSource light) private IEnumerable<object> DimLight(LightSource light)
{ {
float currBrightness = 1.0f; float currBrightness = 1.0f;
float startRange = light.Range;
while (light.Color.A > 0.0f && flashDuration > 0.0f) while (light.Color.A > 0.0f && flashDuration > 0.0f)
{ {
light.Color = new Color(light.Color.R, light.Color.G, light.Color.B, currBrightness); light.Color = new Color(light.Color.R, light.Color.G, light.Color.B, (byte)(currBrightness * 255));
currBrightness -= (1.0f / flashDuration) * CoroutineManager.DeltaTime; currBrightness -= 1.0f / flashDuration * CoroutineManager.DeltaTime;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
} }
@@ -529,7 +529,7 @@ namespace Barotrauma.Lights
graphics.Clear(Color.Black); graphics.Clear(Color.Black);
Vector2 diff = lookAtPosition - ViewTarget.WorldPosition; Vector2 diff = lookAtPosition - ViewTarget.WorldPosition;
diff.Y = -diff.Y; diff.Y = -diff.Y;
if (diff.LengthSquared() > 30.0f) { losOffset = diff; } if (diff.LengthSquared() > 20.0f * 20.0f) { losOffset = diff; }
float rotation = MathUtils.VectorToAngle(losOffset); float rotation = MathUtils.VectorToAngle(losOffset);
Vector2 scale = new Vector2( Vector2 scale = new Vector2(
@@ -64,7 +64,18 @@ namespace Barotrauma.Networking
string orderOption = orderMessageInfo.OrderOption; string orderOption = orderMessageInfo.OrderOption;
orderOption ??= orderMessageInfo.OrderOptionIndex.HasValue && orderMessageInfo.OrderOptionIndex >= 0 && orderMessageInfo.OrderOptionIndex < orderPrefab.Options.Length ? orderOption ??= orderMessageInfo.OrderOptionIndex.HasValue && orderMessageInfo.OrderOptionIndex >= 0 && orderMessageInfo.OrderOptionIndex < orderPrefab.Options.Length ?
orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value] : ""; orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value] : "";
txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, string targetRoom;
if (orderMessageInfo.TargetEntity is Hull targetHull)
{
targetRoom = targetHull.DisplayName;
}
else
{
targetRoom = senderCharacter?.CurrentHull?.DisplayName;
}
txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, targetRoom,
givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter, givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter,
orderOption: orderOption, orderOption: orderOption,
priority: orderMessageInfo.Priority); priority: orderMessageInfo.Priority);
@@ -1,6 +1,7 @@
#region Using Statements #region Using Statements
using System; using System;
using System.Collections.Generic;
using Barotrauma.IO; using Barotrauma.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -132,8 +133,11 @@ namespace Barotrauma
{ {
XElement newElement = new XElement(doc.Root.Name); XElement newElement = new XElement(doc.Root.Name);
newElement.Add(doc.Root.Attributes()); newElement.Add(doc.Root.Attributes());
newElement.Add(doc.Root.Elements().Where(e => !e.Name.LocalName.Equals("contentpackage", StringComparison.InvariantCultureIgnoreCase))); string[] contentPackageTags = { "contentpackage", "contentpackages" };
newElement.Add(baseDoc.Root.Elements().Where(e => e.Name.LocalName.Equals("contentpackage", StringComparison.InvariantCultureIgnoreCase))); bool elementNameMatches(XElement element)
=> contentPackageTags.Any(t => element.Name.LocalName.Equals(t, StringComparison.InvariantCultureIgnoreCase));
newElement.Add(doc.Root.Elements().Where(e => !elementNameMatches(e)));
newElement.Add(baseDoc.Root.Elements().Where(e => elementNameMatches(e)));
XDocument newDoc = new XDocument(newElement); XDocument newDoc = new XDocument(newElement);
newDoc.Save(GameSettings.PlayerSavePath); newDoc.Save(GameSettings.PlayerSavePath);
sb.AppendLine("To prevent further startup errors, installed mods will be disabled the next time you launch the game."); sb.AppendLine("To prevent further startup errors, installed mods will be disabled the next time you launch the game.");
@@ -866,6 +866,8 @@ namespace Barotrauma
return true; return true;
} }
CloseItem();
backedUpSubInfo = new SubmarineInfo(Submarine.MainSub); backedUpSubInfo = new SubmarineInfo(Submarine.MainSub);
GameMain.GameScreen.Select(); GameMain.GameScreen.Select();
@@ -1328,6 +1330,8 @@ namespace Barotrauma
{ {
base.Deselect(); base.Deselect();
CloseItem();
autoSaveLabel?.Parent?.RemoveChild(autoSaveLabel); autoSaveLabel?.Parent?.RemoveChild(autoSaveLabel);
autoSaveLabel = null; autoSaveLabel = null;
@@ -3057,9 +3061,24 @@ namespace Barotrauma
new ContextMenuOption("SubEditor.PasteAssembly", isEnabled: true, () => PasteAssembly()), new ContextMenuOption("SubEditor.PasteAssembly", isEnabled: true, () => PasteAssembly()),
new ContextMenuOption("Editor.SelectSame", isEnabled: targets.Count > 0, onSelected: delegate new ContextMenuOption("Editor.SelectSame", isEnabled: targets.Count > 0, onSelected: delegate
{ {
bool doorGapSelected = targets.Any(t => t is Gap gap && gap.ConnectedDoor != null);
foreach (MapEntity match in MapEntity.mapEntityList.Where(e => e.prefab != null && targets.Any(t => t.prefab?.Identifier == e.prefab.Identifier) && !MapEntity.SelectedList.Contains(e))) foreach (MapEntity match in MapEntity.mapEntityList.Where(e => e.prefab != null && targets.Any(t => t.prefab?.Identifier == e.prefab.Identifier) && !MapEntity.SelectedList.Contains(e)))
{ {
if (MapEntity.SelectedList.Contains(match)) { continue; } if (MapEntity.SelectedList.Contains(match)) { continue; }
if (match is Gap gap)
{
//don't add non-door gaps if we've selected a door gap (and vice versa)
if ((gap.ConnectedDoor == null) == doorGapSelected) { continue; }
}
else if (match is Item item)
{
//add door gaps too if we're selecting doors
var door = item.GetComponent<Door>();
if (door?.LinkedGap != null && !MapEntity.SelectedList.Contains(door.LinkedGap))
{
MapEntity.SelectedList.Add(door.LinkedGap);
}
}
MapEntity.SelectedList.Add(match); MapEntity.SelectedList.Add(match);
} }
}), }),
@@ -4171,6 +4190,11 @@ namespace Barotrauma
UpdateEntityList(); UpdateEntityList();
} }
if (OpenedItem != null && OpenedItem.Removed)
{
OpenedItem = null;
}
if (WiringMode && dummyCharacter != null) if (WiringMode && dummyCharacter != null)
{ {
Wire equippedWire = Wire equippedWire =
@@ -960,7 +960,7 @@ namespace Barotrauma
targetMusic[i] = null; targetMusic[i] = null;
break; break;
} }
musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "" : "music"); musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "default" : "music");
if (targetMusic[i].ContinueFromPreviousTime) if (targetMusic[i].ContinueFromPreviousTime)
{ {
musicChannel[i].StreamSeekPos = targetMusic[i].PreviousTime; musicChannel[i].StreamSeekPos = targetMusic[i].PreviousTime;
@@ -974,7 +974,7 @@ namespace Barotrauma
if (musicChannel[i] == null || !musicChannel[i].IsPlaying) if (musicChannel[i] == null || !musicChannel[i].IsPlaying)
{ {
musicChannel[i]?.Dispose(); musicChannel[i]?.Dispose();
musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "" : "music"); musicChannel[i] = currentMusic[i].Play(0.0f, i == noiseLoopIndex ? "default" : "music");
musicChannel[i].Looping = true; musicChannel[i].Looping = true;
} }
float targetGain = targetMusic[i].Volume; float targetGain = targetMusic[i].Volume;
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.15.12.0</Version> <Version>0.15.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.15.12.0</Version> <Version>0.15.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.15.12.0</Version> <Version>0.15.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.15.12.0</Version> <Version>0.15.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.15.12.0</Version> <Version>0.15.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -1585,6 +1585,10 @@ namespace Barotrauma
tpCharacter.Submarine = null; tpCharacter.Submarine = null;
tpCharacter.AnimController.SetPosition(ConvertUnits.ToSimUnits(cursorWorldPos)); tpCharacter.AnimController.SetPosition(ConvertUnits.ToSimUnits(cursorWorldPos));
tpCharacter.AnimController.FindHull(cursorWorldPos, true); tpCharacter.AnimController.FindHull(cursorWorldPos, true);
if (tpCharacter.AIController?.SteeringManager is IndoorsSteeringManager pathSteering)
{
pathSteering.ResetPath();
}
} }
); );
@@ -202,13 +202,11 @@ namespace Barotrauma
public void SavePlayers() public void SavePlayers()
{ {
List<CharacterCampaignData> prevCharacterData = new List<CharacterCampaignData>(characterData);
//client character has spawned this round -> remove old data (and replace with an up-to-date one if the client still has a character)
characterData.RemoveAll(cd => cd.HasSpawned);
//refresh the character data of clients who are still in the server //refresh the character data of clients who are still in the server
foreach (Client c in GameMain.Server.ConnectedClients) foreach (Client c in GameMain.Server.ConnectedClients)
{ {
//ignore if the character is controlling a monster
//(we'll just use the previously saved campaign data if there's any)
if (c.Character != null && c.Character.Info == null) if (c.Character != null && c.Character.Info == null)
{ {
c.Character = null; c.Character = null;
@@ -225,9 +223,12 @@ namespace Barotrauma
continue; continue;
} }
} }
var characterInfo = c.Character?.Info ?? c.CharacterInfo; //use the info of the character the client is currently controlling
// or the previously saved info if not (e.g. if the client has been spectating or died)
var characterInfo = c.Character?.Info ?? characterData.Find(d => d.MatchesClient(c))?.CharacterInfo;
if (characterInfo == null) { continue; } if (characterInfo == null) { continue; }
if (c.CharacterInfo.CauseOfDeath != null && characterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected) //reduce skills if the character has died
if (characterInfo.CauseOfDeath != null && characterInfo.CauseOfDeath.Type != CauseOfDeathType.Disconnected)
{ {
RespawnManager.ReduceCharacterSkills(characterInfo); RespawnManager.ReduceCharacterSkills(characterInfo);
} }
@@ -237,13 +238,15 @@ namespace Barotrauma
} }
//refresh the character data of clients who aren't in the server anymore //refresh the character data of clients who aren't in the server anymore
List<CharacterCampaignData> prevCharacterData = new List<CharacterCampaignData>(characterData);
foreach (CharacterCampaignData data in prevCharacterData) foreach (CharacterCampaignData data in prevCharacterData)
{ {
if (data.HasSpawned && !characterData.Any(cd => cd.IsDuplicate(data))) if (data.HasSpawned && !GameMain.Server.ConnectedClients.Any(c => data.MatchesClient(c)))
{ {
var character = Character.CharacterList.Find(c => c.Info == data.CharacterInfo && !c.IsHusk); var character = Character.CharacterList.Find(c => c.Info == data.CharacterInfo && !c.IsHusk);
if (character != null && (!character.IsDead || character.CauseOfDeath?.Type == CauseOfDeathType.Disconnected)) if (character != null && (!character.IsDead || character.CauseOfDeath?.Type == CauseOfDeathType.Disconnected))
{ {
characterData.RemoveAll(cd => cd.IsDuplicate(data));
data.Refresh(character); data.Refresh(character);
characterData.Add(data); characterData.Add(data);
} }
@@ -345,7 +348,6 @@ namespace Barotrauma
{ {
SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo; SubmarineInfo previousSub = GameMain.GameSession.SubmarineInfo;
GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch; GameMain.GameSession.SubmarineInfo = PendingSubmarineSwitch;
PendingSubmarineSwitch = null;
for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++) for (int i = 0; i < GameMain.GameSession.OwnedSubmarines.Count; i++)
{ {
@@ -358,6 +360,7 @@ namespace Barotrauma
} }
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
PendingSubmarineSwitch = null;
} }
else else
{ {
@@ -3599,7 +3599,6 @@ namespace Barotrauma.Networking
List<WayPoint> availableSpawnPoints = WayPoint.WayPointList.FindAll(wp => List<WayPoint> availableSpawnPoints = WayPoint.WayPointList.FindAll(wp =>
wp.SpawnType == SpawnType.Human && wp.SpawnType == SpawnType.Human &&
wp.Submarine != null && wp.Submarine.TeamID == teamID); wp.Submarine != null && wp.Submarine.TeamID == teamID);
List<WayPoint> unassignedSpawnPoints = new List<WayPoint>(availableSpawnPoints);
/*bool canAssign = false; /*bool canAssign = false;
do do
@@ -3627,10 +3626,8 @@ namespace Barotrauma.Networking
// First evaluate all the primary preferences, then all the secondary etc. // First evaluate all the primary preferences, then all the secondary etc.
for (int preferenceIndex = 0; preferenceIndex < 3; preferenceIndex++) for (int preferenceIndex = 0; preferenceIndex < 3; preferenceIndex++)
{ {
if (unassignedSpawnPoints.None()) { break; }
for (int i = unassigned.Count - 1; i >= 0; i--) for (int i = unassigned.Count - 1; i >= 0; i--)
{ {
if (unassignedSpawnPoints.None()) { break; }
Client client = unassigned[i]; Client client = unassigned[i];
if (preferenceIndex >= client.JobPreferences.Count) { continue; } if (preferenceIndex >= client.JobPreferences.Count) { continue; }
var preferredJob = client.JobPreferences[preferenceIndex]; var preferredJob = client.JobPreferences[preferenceIndex];
@@ -3640,21 +3637,10 @@ namespace Barotrauma.Networking
//can't assign this job if maximum number has reached or the clien't karma is too low //can't assign this job if maximum number has reached or the clien't karma is too low
continue; continue;
} }
//give the client their preferred job if there's a spawnpoint available for that job
var matchingSpawnPoint = unassignedSpawnPoints.Find(s => s.AssignedJob == jobPrefab); client.AssignedJob = preferredJob;
if (matchingSpawnPoint == null && !availableSpawnPoints.Any(s => s.AssignedJob == jobPrefab)) assignedClientCount[jobPrefab]++;
{ unassigned.RemoveAt(i);
//if the job is not available in any spawnpoint (custom job?), treat empty spawnpoints
//as a matching ones
matchingSpawnPoint = unassignedSpawnPoints.Find(s => s.AssignedJob == null);
}
if (matchingSpawnPoint != null)
{
unassignedSpawnPoints.Remove(matchingSpawnPoint);
client.AssignedJob = preferredJob;
assignedClientCount[jobPrefab]++;
unassigned.RemoveAt(i);
}
} }
} }
@@ -185,7 +185,7 @@ namespace Barotrauma
{ {
existingItems.Add(item); existingItems.Add(item);
} }
Entity.Spawner.AddToSpawnQueue(targetPrefab, targetContainer.OwnInventory, null, item => Entity.Spawner.AddToSpawnQueue(targetPrefab, targetContainer.OwnInventory, onSpawned: item =>
{ {
item.AddTag("traitormissionitem"); item.AddTag("traitormissionitem");
}); });
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.15.12.0</Version> <Version>0.15.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2020</Copyright> <Copyright>Copyright © FakeFish 2018-2020</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -104,6 +104,7 @@ namespace Barotrauma
!pathSteering.CurrentPath.Unreachable && !pathSteering.CurrentPath.Unreachable &&
(!requireNonDirty || !pathSteering.IsPathDirty); (!requireNonDirty || !pathSteering.IsPathDirty);
public bool IsCurrentPathNullOrUnreachable => IsCurrentPathUnreachable || steeringManager is IndoorsSteeringManager pathSteering && pathSteering.CurrentPath == null;
public bool IsCurrentPathUnreachable => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Unreachable; public bool IsCurrentPathUnreachable => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Unreachable;
public bool IsCurrentPathFinished => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Finished; public bool IsCurrentPathFinished => steeringManager is IndoorsSteeringManager pathSteering && !pathSteering.IsPathDirty && pathSteering.CurrentPath != null && pathSteering.CurrentPath.Finished;
@@ -431,7 +432,7 @@ namespace Barotrauma
Vector2 diff = EscapeTarget.WorldPosition - Character.WorldPosition; Vector2 diff = EscapeTarget.WorldPosition - Character.WorldPosition;
float sqrDist = diff.LengthSquared(); float sqrDist = diff.LengthSquared();
bool isClose = sqrDist < MathUtils.Pow2(100); bool isClose = sqrDist < MathUtils.Pow2(100);
if (Character.CurrentHull == null || isClose && !isClosedDoor || pathSteering == null || IsCurrentPathUnreachable || IsCurrentPathFinished) if (Character.CurrentHull == null || isClose && !isClosedDoor || pathSteering == null || IsCurrentPathNullOrUnreachable || IsCurrentPathFinished)
{ {
// Very close to the target, outside, or at the end of the path -> try to steer through the gap // Very close to the target, outside, or at the end of the path -> try to steer through the gap
SteeringManager.Reset(); SteeringManager.Reset();
@@ -3659,15 +3659,15 @@ namespace Barotrauma
{ {
if (SelectedAiTarget == null) { return; } if (SelectedAiTarget == null) { return; }
Vector2 escapeDir = Vector2.Normalize(WorldPosition - SelectedAiTarget.WorldPosition); Vector2 escapeDir = Vector2.Normalize(WorldPosition - SelectedAiTarget.WorldPosition);
if (!MathUtils.IsValid(escapeDir))
{
escapeDir = Vector2.UnitY;
}
if (Character.CurrentHull != null && !Character.AnimController.InWater) if (Character.CurrentHull != null && !Character.AnimController.InWater)
{ {
// Inside // Inside
escapeDir = new Vector2(Math.Sign(escapeDir.X), 0); escapeDir = new Vector2(Math.Sign(escapeDir.X), 0);
} }
if (!MathUtils.IsValid(escapeDir))
{
escapeDir = Vector2.UnitY;
}
SteeringManager.Reset(); SteeringManager.Reset();
SteeringManager.SteeringManual(deltaTime, escapeDir); SteeringManager.SteeringManual(deltaTime, escapeDir);
} }
@@ -250,7 +250,17 @@ namespace Barotrauma
{ {
rayEnd += SelectedAiTarget.Entity.Submarine.SimPosition; rayEnd += SelectedAiTarget.Entity.Submarine.SimPosition;
} }
UseIndoorSteeringOutside = Submarine.PickBody(SimPosition, rayEnd, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall) != null; IEnumerable<FarseerPhysics.Dynamics.Body> ignoredBodies = null;
if (SelectedAiTarget.Entity is ISpatialEntity spatialTarget)
{
Submarine targetSub = spatialTarget.Submarine;
if (targetSub != null)
{
ignoredBodies = targetSub.PhysicsBody.FarseerBody.ToEnumerable();
}
}
var obstacle = Submarine.PickBody(SimPosition, rayEnd, ignoredBodies, collisionCategory: Physics.CollisionLevel | Physics.CollisionWall);
UseIndoorSteeringOutside = obstacle != null;
} }
} }
else else
@@ -340,14 +350,21 @@ namespace Barotrauma
IsInsideCave = Character.CurrentHull == null && Level.Loaded?.Caves.FirstOrDefault(c => c.Area.Contains(Character.WorldPosition)) is Level.Cave; IsInsideCave = Character.CurrentHull == null && Level.Loaded?.Caves.FirstOrDefault(c => c.Area.Contains(Character.WorldPosition)) is Level.Cave;
} }
if (UseIndoorSteeringOutside || IsInsideCave || Character.CurrentHull?.Submarine != null || hasValidPath && IsCloseEnoughToTarget(maxSteeringBuffer) || IsCloseEnoughToTarget(steeringBuffer)) if (UseIndoorSteeringOutside || IsInsideCave || Character.CurrentHull?.Submarine != null || hasValidPath || IsCloseEnoughToTarget(steeringBuffer))
{ {
if (steeringManager != insideSteering) if (steeringManager != insideSteering)
{ {
insideSteering.Reset(); insideSteering.Reset();
steeringManager = insideSteering; steeringManager = insideSteering;
} }
steeringBuffer += steeringBufferIncreaseSpeed * deltaTime; if (IsCloseEnoughToTarget(maxSteeringBuffer))
{
steeringBuffer += steeringBufferIncreaseSpeed * deltaTime;
}
else
{
steeringBuffer = minSteeringBuffer;
}
} }
else else
{ {
@@ -201,9 +201,9 @@ namespace Barotrauma
currentTarget = target; currentTarget = target;
Vector2 currentPos = host.SimPosition; Vector2 currentPos = host.SimPosition;
pathFinder.InsideSubmarine = character.Submarine != null && !character.Submarine.Info.IsRuin; pathFinder.InsideSubmarine = character.Submarine != null && !character.Submarine.Info.IsRuin;
pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && character.PressureProtection <= 0; pathFinder.ApplyPenaltyToOutsideNodes = character.Submarine != null && character.PressureProtection <= 0;
var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", minGapSize, startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility); var newPath = pathFinder.FindPath(currentPos, target, character.Submarine, "(Character: " + character.Name + ")", minGapSize, startNodeFilter, endNodeFilter, nodeFilter, checkVisibility: checkVisibility);
bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.X) <= 0; bool useNewPath = needsNewPath || currentPath == null || currentPath.CurrentNode == null || character.Submarine != null && findPathTimer < -1 && Math.Abs(character.AnimController.TargetMovement.Combine()) <= 0;
if (newPath.Unreachable || newPath.Nodes.None()) if (newPath.Unreachable || newPath.Nodes.None())
{ {
useNewPath = false; useNewPath = false;
@@ -220,10 +220,12 @@ namespace Barotrauma
// Use the new path if it has significantly lower cost (don't change the path if it has marginally smaller cost. This reduces navigating backwards due to new path that is calculated from the node just behind us). // Use the new path if it has significantly lower cost (don't change the path if it has marginally smaller cost. This reduces navigating backwards due to new path that is calculated from the node just behind us).
float t = (float)currentPath.CurrentIndex / (currentPath.Nodes.Count - 1); float t = (float)currentPath.CurrentIndex / (currentPath.Nodes.Count - 1);
useNewPath = newPath.Cost < currentPath.Cost * MathHelper.Lerp(0.95f, 0, t); useNewPath = newPath.Cost < currentPath.Cost * MathHelper.Lerp(0.95f, 0, t);
if (!useNewPath && character.Submarine != null) if (!useNewPath && character.Submarine != null && !character.IsClimbing)
{ {
// It's possible that the current path was calculated from a start point that is no longer valid. // It's possible that the current path was calculated from a start point that is no longer valid.
// Therefore, let's accept also paths with a greater cost than the current, if the current node is much farther than the new start node. // Therefore, let's accept also paths with a greater cost than the current, if the current node is much farther than the new start node.
// This is a special case for cases e.g. where the character falls and thus needs a new path.
// Don't do this outside or when climbing ladders, because both cause issues.
useNewPath = Vector2.DistanceSquared(character.WorldPosition, currentPath.CurrentNode.WorldPosition) > Math.Pow(Vector2.Distance(character.WorldPosition, newPath.Nodes.First().WorldPosition) * 3, 2); useNewPath = Vector2.DistanceSquared(character.WorldPosition, currentPath.CurrentNode.WorldPosition) > Math.Pow(Vector2.Distance(character.WorldPosition, newPath.Nodes.First().WorldPosition) * 3, 2);
} }
} }
@@ -310,8 +312,7 @@ namespace Barotrauma
if (currentPath.Finished) if (currentPath.Finished)
{ {
Vector2 pos2 = host.SimPosition; Vector2 pos2 = host.SimPosition;
if (character != null && character.Submarine == null && if (character != null && character.Submarine == null && CurrentPath.Nodes.Count > 0 && CurrentPath.Nodes.Last().Submarine != null)
CurrentPath.Nodes.Count > 0 && CurrentPath.Nodes.Last().Submarine != null)
{ {
pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition; pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition;
} }
@@ -323,29 +324,7 @@ namespace Barotrauma
CheckDoorsInPath(); CheckDoorsInPath();
doorsChecked = true; doorsChecked = true;
} }
Vector2 pos = host.SimPosition; Vector2 pos = host.WorldPosition;
if (character != null && CurrentPath.CurrentNode != null)
{
var nodeSub = CurrentPath.CurrentNode.Submarine;
if (nodeSub != null)
{
if (character.Submarine == null)
{
// Going inside
pos -= ConvertUnits.ToSimUnits(nodeSub.Position);
}
else if (character.Submarine != nodeSub)
{
// Different subs
pos -= ConvertUnits.ToSimUnits(nodeSub.Position - character.Submarine.Position);
}
}
else if (character.Submarine != null)
{
// Going outside
pos += ConvertUnits.ToSimUnits(character.Submarine.Position);
}
}
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater; bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
// Only humanoids can climb ladders // Only humanoids can climb ladders
bool canClimb = character.AnimController is HumanoidAnimController && !character.LockHands; bool canClimb = character.AnimController is HumanoidAnimController && !character.LockHands;
@@ -384,7 +363,7 @@ namespace Barotrauma
} }
if (character.IsClimbing && useLadders) if (character.IsClimbing && useLadders)
{ {
Vector2 diff = currentPath.CurrentNode.SimPosition - pos; Vector2 diff = currentPath.CurrentNode.WorldPosition - pos;
bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent; bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent;
if (nextLadderSameAsCurrent) if (nextLadderSameAsCurrent)
{ {
@@ -397,7 +376,7 @@ namespace Barotrauma
float heightFromFloor = character.AnimController.GetColliderBottom().Y - character.AnimController.FloorY; float heightFromFloor = character.AnimController.GetColliderBottom().Y - character.AnimController.FloorY;
if (heightFromFloor <= 0.0f) if (heightFromFloor <= 0.0f)
{ {
diff.Y = Math.Max(diff.Y, 1.0f); diff.Y = Math.Max(diff.Y, 100);
} }
// We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative. // We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative.
bool isAboveFloor = heightFromFloor > -0.1f; bool isAboveFloor = heightFromFloor > -0.1f;
@@ -430,7 +409,7 @@ namespace Barotrauma
NextNode(!doorsChecked); NextNode(!doorsChecked);
} }
} }
return diff; return ConvertUnits.ToSimUnits(diff);
} }
else if (character.AnimController.InWater) else if (character.AnimController.InWater)
{ {
@@ -481,7 +460,7 @@ namespace Barotrauma
{ {
return Vector2.Zero; return Vector2.Zero;
} }
return currentPath.CurrentNode.SimPosition - pos; return ConvertUnits.ToSimUnits(currentPath.CurrentNode.WorldPosition - pos);
} }
private void NextNode(bool checkDoors) private void NextNode(bool checkDoors)
@@ -45,6 +45,10 @@ namespace Barotrauma
/// </summary> /// </summary>
public bool AllowStealing { get; set; } public bool AllowStealing { get; set; }
public bool TakeWholeStack { get; set; } public bool TakeWholeStack { get; set; }
/// <summary>
/// Are variants of the specified item allowed
/// </summary>
public bool AllowVariants { get; set; }
public bool Equip { get; set; } public bool Equip { get; set; }
public bool Wear { get; set; } public bool Wear { get; set; }
@@ -265,7 +269,7 @@ namespace Barotrauma
} }
float priority = Math.Clamp(objectiveManager.GetCurrentPriority(), 10, 100); float priority = Math.Clamp(objectiveManager.GetCurrentPriority(), 10, 100);
bool checkPath = priority >= AIObjectiveManager.LowestOrderPriority && (objectiveManager.IsCurrentOrder<AIObjectiveFixLeaks>() || objectiveManager.CurrentOrder is AIObjectiveGoTo gotoOrder && gotoOrder.followControlledCharacter); bool checkPath = priority >= AIObjectiveManager.LowestOrderPriority && (objectiveManager.IsCurrentOrder<AIObjectiveFixLeaks>() || objectiveManager.CurrentOrder is AIObjectiveGoTo gotoOrder && gotoOrder.isFollowOrderObjective);
bool hasCalledPathFinder = false; bool hasCalledPathFinder = false;
int itemsPerFrame = (int)priority; int itemsPerFrame = (int)priority;
for (int i = 0; i < itemsPerFrame && currSearchIndex < Item.ItemList.Count - 1; i++) for (int i = 0; i < itemsPerFrame && currSearchIndex < Item.ItemList.Count - 1; i++)
@@ -408,7 +412,7 @@ namespace Barotrauma
if (ignoredItems.Contains(item)) { return false; }; if (ignoredItems.Contains(item)) { return false; };
if (item.Condition < TargetCondition) { return false; } if (item.Condition < TargetCondition) { return false; }
if (ItemFilter != null && !ItemFilter(item)) { return false; } if (ItemFilter != null && !ItemFilter(item)) { return false; }
return identifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id)); return identifiersOrTags.Any(id => id == item.Prefab.Identifier || item.HasTag(id) || (AllowVariants && item.Prefab.VariantOf?.Identifier == id));
} }
public override void Reset() public override void Reset()
@@ -24,7 +24,7 @@ namespace Barotrauma
public Func<float> priorityGetter; public Func<float> priorityGetter;
public bool followControlledCharacter; public bool isFollowOrderObjective;
public bool mimic; public bool mimic;
public bool SpeakIfFails { get; set; } = true; public bool SpeakIfFails { get; set; } = true;
public bool UsePathingOutside { get; set; } = true; public bool UsePathingOutside { get; set; } = true;
@@ -165,17 +165,10 @@ namespace Barotrauma
protected override void Act(float deltaTime) protected override void Act(float deltaTime)
{ {
if (followControlledCharacter) if (Target == null)
{ {
if (Character.Controlled != null && HumanAIController.IsFriendly(Character.Controlled)) Abandon = true;
{ return;
Target = Character.Controlled;
}
if (Target == null)
{
Abandon = true;
return;
}
} }
if (Target == character || character.SelectedBy != null && HumanAIController.IsFriendly(character.SelectedBy)) if (Target == character || character.SelectedBy != null && HumanAIController.IsFriendly(character.SelectedBy))
{ {
@@ -205,7 +198,7 @@ namespace Barotrauma
} }
} }
Hull targetHull = GetTargetHull(); Hull targetHull = GetTargetHull();
if (!followControlledCharacter) if (!isFollowOrderObjective)
{ {
// Abandon if going through unsafe paths. Note ignores unsafe nodes when following an order or when the objective is set to ignore unsafe hulls. // Abandon if going through unsafe paths. Note ignores unsafe nodes when following an order or when the objective is set to ignore unsafe hulls.
bool containsUnsafeNodes = character.IsDismissed && !HumanAIController.ObjectiveManager.CurrentObjective.IgnoreUnsafeHulls bool containsUnsafeNodes = character.IsDismissed && !HumanAIController.ObjectiveManager.CurrentObjective.IgnoreUnsafeHulls
@@ -223,7 +216,7 @@ namespace Barotrauma
{ {
Abandon = true; Abandon = true;
} }
else if (HumanAIController.IsCurrentPathUnreachable) else if (HumanAIController.IsCurrentPathNullOrUnreachable)
{ {
waitUntilPathUnreachable -= deltaTime; waitUntilPathUnreachable -= deltaTime;
SteeringManager.Reset(); SteeringManager.Reset();
@@ -318,7 +311,7 @@ namespace Barotrauma
if (character.AnimController.InWater) if (character.AnimController.InWater)
{ {
if (character.CurrentHull == null || if (character.CurrentHull == null ||
followControlledCharacter && isFollowOrderObjective &&
targetCharacter != null && (targetCharacter.CurrentHull == null) != (character.CurrentHull == null) && targetCharacter != null && (targetCharacter.CurrentHull == null) != (character.CurrentHull == null) &&
Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition) < maxGapDistance * maxGapDistance) Vector2.DistanceSquared(character.WorldPosition, Target.WorldPosition) < maxGapDistance * maxGapDistance)
{ {
@@ -361,7 +354,7 @@ namespace Barotrauma
} }
if (TargetGap != null) if (TargetGap != null)
{ {
if (TargetGap.FlowTargetHull != null && HumanAIController.SteerThroughGap(TargetGap, followControlledCharacter ? Target.WorldPosition : TargetGap.FlowTargetHull.WorldPosition, deltaTime)) if (TargetGap.FlowTargetHull != null && HumanAIController.SteerThroughGap(TargetGap, isFollowOrderObjective ? Target.WorldPosition : TargetGap.FlowTargetHull.WorldPosition, deltaTime))
{ {
SteeringManager.SteeringAvoid(deltaTime, avoidLookAheadDistance, weight: 1); SteeringManager.SteeringAvoid(deltaTime, avoidLookAheadDistance, weight: 1);
return; return;
@@ -595,7 +588,7 @@ namespace Barotrauma
{ {
if (gap.Open < 1) { continue; } if (gap.Open < 1) { continue; }
if (gap.Submarine == null) { continue; } if (gap.Submarine == null) { continue; }
if (!followControlledCharacter) if (!isFollowOrderObjective)
{ {
if (gap.FlowTargetHull == null) { continue; } if (gap.FlowTargetHull == null) { continue; }
if (gap.Submarine != Target.Submarine) { continue; } if (gap.Submarine != Target.Submarine) { continue; }
@@ -452,7 +452,7 @@ namespace Barotrauma
extraDistanceWhileSwimming = 100, extraDistanceWhileSwimming = 100,
AllowGoingOutside = true, AllowGoingOutside = true,
IgnoreIfTargetDead = true, IgnoreIfTargetDead = true,
followControlledCharacter = true, isFollowOrderObjective = true,
mimic = true, mimic = true,
DialogueIdentifier = "dialogcannotreachplace" DialogueIdentifier = "dialogcannotreachplace"
}; };
@@ -107,7 +107,10 @@ namespace Barotrauma
{ {
foreach (RelatedItem requiredItem in kvp.Value) foreach (RelatedItem requiredItem in kvp.Value)
{ {
var getItemObjective = new AIObjectiveGetItem(character, requiredItem.Identifiers, objectiveManager, true); var getItemObjective = new AIObjectiveGetItem(character, requiredItem.Identifiers, objectiveManager, true)
{
AllowVariants = requiredItem.AllowVariants
};
if (objectiveManager.IsCurrentOrder<AIObjectiveRepairItems>()) if (objectiveManager.IsCurrentOrder<AIObjectiveRepairItems>())
{ {
if (character.IsOnPlayerTeam) if (character.IsOnPlayerTeam)
@@ -408,7 +408,7 @@ namespace Barotrauma
} }
bool isCompleted = bool isCompleted =
AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter) || AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter) ||
targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Strength <= a.Prefab.TreatmentThreshold); targetCharacter.CharacterHealth.GetAllAfflictions().All(a => a.Prefab.IsBuff || a.Strength <= a.Prefab.TreatmentThreshold);
if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam) if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam)
{ {
@@ -83,7 +83,7 @@ namespace Barotrauma
if (character.AIController is HumanAIController humanAI) if (character.AIController is HumanAIController humanAI)
{ {
if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target) || if (GetVitalityFactor(target) >= GetVitalityThreshold(humanAI.ObjectiveManager, character, target) ||
target.CharacterHealth.GetAllAfflictions().All(a => a.Strength <= a.Prefab.TreatmentThreshold)) target.CharacterHealth.GetAllAfflictions().All(a => a.Prefab.IsBuff || a.Strength <= a.Prefab.TreatmentThreshold))
{ {
return false; return false;
} }
@@ -1367,7 +1367,7 @@ namespace Barotrauma
info.Job.GiveJobItems(this, spawnPoint); info.Job.GiveJobItems(this, spawnPoint);
} }
public void GiveIdCardTags(WayPoint spawnPoint) public void GiveIdCardTags(WayPoint spawnPoint, bool createNetworkEvent = false)
{ {
if (info?.Job == null || spawnPoint == null) { return; } if (info?.Job == null || spawnPoint == null) { return; }
@@ -1378,6 +1378,10 @@ namespace Barotrauma
{ {
item.AddTag(s); item.AddTag(s);
} }
if (createNetworkEvent && (GameMain.NetworkMember?.IsServer ?? false))
{
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ChangeProperty, item.SerializableProperties["tags"] });
}
} }
} }
@@ -1756,6 +1760,7 @@ namespace Barotrauma
} }
else if (IsPlayer) else if (IsPlayer)
{ {
float dist = -1;
Vector2 attackPos = SimPosition + ConvertUnits.ToSimUnits(cursorPosition - Position); Vector2 attackPos = SimPosition + ConvertUnits.ToSimUnits(cursorPosition - Position);
List<Body> ignoredBodies = AnimController.Limbs.Select(l => l.body.FarseerBody).ToList(); List<Body> ignoredBodies = AnimController.Limbs.Select(l => l.body.FarseerBody).ToList();
ignoredBodies.Add(AnimController.Collider.FarseerBody); ignoredBodies.Add(AnimController.Collider.FarseerBody);
@@ -1787,13 +1792,13 @@ namespace Barotrauma
} }
else else
{ {
if (body.UserData is IDamageable) if (body.UserData is IDamageable damageable)
{ {
attackTarget = (IDamageable)body.UserData; attackTarget = damageable;
} }
else if (body.UserData is Limb) else if (body.UserData is Limb limb)
{ {
attackTarget = ((Limb)body.UserData).character; attackTarget = limb.character;
} }
} }
} }
@@ -1822,7 +1827,20 @@ namespace Barotrauma
var attackLimb = sortedLimbs.FirstOrDefault(); var attackLimb = sortedLimbs.FirstOrDefault();
if (attackLimb != null) if (attackLimb != null)
{ {
attackLimb.UpdateAttack(deltaTime, attackPos, attackTarget, out AttackResult attackResult); if (attackTarget is Character targetCharacter)
{
dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(Submarine.LastPickedPosition, attackLimb.SimPosition));
foreach (Limb limb in targetCharacter.AnimController.Limbs)
{
if (limb.IsSevered || limb.Removed) { continue; }
float tempDist = ConvertUnits.ToDisplayUnits(Vector2.Distance(limb.SimPosition, attackLimb.SimPosition));
if (tempDist < dist)
{
dist = tempDist;
}
}
}
attackLimb.UpdateAttack(deltaTime, attackPos, attackTarget, out AttackResult attackResult, dist);
if (!attackLimb.attack.IsRunning) if (!attackLimb.attack.IsRunning)
{ {
attackCoolDown = 1.0f; attackCoolDown = 1.0f;
@@ -3438,7 +3456,7 @@ namespace Barotrauma
var attackResult = targetLimb == null ? var attackResult = targetLimb == null ?
AddDamage(worldPosition, attackAfflictions, attack.Stun, playSound, attackImpulse, out limbHit, attacker, attack.DamageMultiplier * attackData.DamageMultiplier) : AddDamage(worldPosition, attackAfflictions, attack.Stun, playSound, attackImpulse, out limbHit, attacker, attack.DamageMultiplier * attackData.DamageMultiplier) :
DamageLimb(worldPosition, targetLimb, attackAfflictions, attack.Stun, playSound, attackImpulse, attacker, attack.DamageMultiplier * attackData.DamageMultiplier, penetration: penetration + attackData.AddedPenetration); DamageLimb(worldPosition, targetLimb, attackAfflictions, attack.Stun, playSound, attackImpulse, attacker, attack.DamageMultiplier * attackData.DamageMultiplier, penetration: penetration + attackData.AddedPenetration, shouldImplode: attackData.ShouldImplode);
if (attacker != null) if (attacker != null)
{ {
@@ -3558,12 +3576,12 @@ namespace Barotrauma
public void RecordKill(Character target) public void RecordKill(Character target)
{ {
var abilityCharacter = new AbilityCharacter(target); var abilityCharacterKill = new AbilityCharacterKill(target, this);
foreach (Character attackerCrewmember in GetFriendlyCrew(this)) foreach (Character attackerCrewmember in GetFriendlyCrew(this))
{ {
attackerCrewmember.CheckTalents(AbilityEffectType.OnCrewKillCharacter, abilityCharacter); attackerCrewmember.CheckTalents(AbilityEffectType.OnCrewKillCharacter, abilityCharacterKill);
} }
CheckTalents(AbilityEffectType.OnKillCharacter, abilityCharacter); CheckTalents(AbilityEffectType.OnKillCharacter, abilityCharacterKill);
if (!IsOnPlayerTeam) { return; } if (!IsOnPlayerTeam) { return; }
if (GameMain.Config.KilledCreatures.Any(name => name.Equals(target.SpeciesName, StringComparison.OrdinalIgnoreCase))) { return; } if (GameMain.Config.KilledCreatures.Any(name => name.Equals(target.SpeciesName, StringComparison.OrdinalIgnoreCase))) { return; }
@@ -3579,7 +3597,7 @@ namespace Barotrauma
GameMain.Config.RecentlyEncounteredCreatures.Add(other.SpeciesName); GameMain.Config.RecentlyEncounteredCreatures.Add(other.SpeciesName);
} }
public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null, float damageMultiplier = 1, bool allowStacking = true, float penetration = 0f) public AttackResult DamageLimb(Vector2 worldPosition, Limb hitLimb, IEnumerable<Affliction> afflictions, float stun, bool playSound, float attackImpulse, Character attacker = null, float damageMultiplier = 1, bool allowStacking = true, float penetration = 0f, bool shouldImplode = false)
{ {
if (Removed) { return new AttackResult(); } if (Removed) { return new AttackResult(); }
@@ -3638,6 +3656,12 @@ namespace Barotrauma
float prevVitality = CharacterHealth.Vitality; float prevVitality = CharacterHealth.Vitality;
AttackResult attackResult = hitLimb.AddDamage(simPos, afflictions, playSound, damageMultiplier: damageMultiplier, penetration: penetration, attacker: attacker); AttackResult attackResult = hitLimb.AddDamage(simPos, afflictions, playSound, damageMultiplier: damageMultiplier, penetration: penetration, attacker: attacker);
CharacterHealth.ApplyDamage(hitLimb, attackResult, allowStacking); CharacterHealth.ApplyDamage(hitLimb, attackResult, allowStacking);
if (shouldImplode)
{
// Only used by assistant's True Potential talent. Has to run here in order to properly give kill credit when it activates.
Implode();
}
if (attacker != this) if (attacker != this)
{ {
OnAttacked?.Invoke(attacker, attackResult); OnAttacked?.Invoke(attacker, attackResult);
@@ -3786,7 +3810,7 @@ namespace Barotrauma
} }
} }
public void Implode(bool isNetworkMessage = false) private void Implode(bool isNetworkMessage = false)
{ {
if (CharacterHealth.Unkillable || GodMode || IsDead) { return; } if (CharacterHealth.Unkillable || GodMode || IsDead) { return; }
@@ -4626,4 +4650,16 @@ namespace Barotrauma
AggressiveBehavior = aggressiveBehavior; AggressiveBehavior = aggressiveBehavior;
} }
} }
class AbilityCharacterKill : AbilityObject, IAbilityCharacter
{
public AbilityCharacterKill(Character character, Character killer)
{
Character = character;
Killer = killer;
}
public Character Character { get; set; }
public Character Killer { get; set; }
}
} }
@@ -291,7 +291,7 @@ namespace Barotrauma
husk.SetStun(5); husk.SetStun(5);
yield return new WaitForSeconds(5, false); yield return new WaitForSeconds(5, false);
#if CLIENT #if CLIENT
husk.PlaySound(CharacterSound.SoundType.Idle); husk?.PlaySound(CharacterSound.SoundType.Idle);
#endif #endif
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
@@ -844,6 +844,8 @@ namespace Barotrauma
FaceTint = DefaultFaceTint; FaceTint = DefaultFaceTint;
BodyTint = Color.TransparentBlack; BodyTint = Color.TransparentBlack;
if (!(Character?.Params?.Health.ApplyAfflictionColors ?? false)) { return; }
for (int i = 0; i < limbHealths.Count; i++) for (int i = 0; i < limbHealths.Count; i++)
{ {
for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--) for (int j = limbHealths[i].Afflictions.Count - 1; j >= 0; j--)
@@ -466,6 +466,9 @@ namespace Barotrauma
[Serialize(false, true), Editable] [Serialize(false, true), Editable]
public bool StunImmunity { get; set; } public bool StunImmunity { get; set; }
[Serialize(false, true, description: "Can afflictions affect the face/body tint of the character."), Editable]
public bool ApplyAfflictionColors { get; private set; }
// TODO: limbhealths, sprite? // TODO: limbhealths, sprite?
public HealthParams(XElement element, CharacterParams character) : base(element, character) { } public HealthParams(XElement element, CharacterParams character) : base(element, character) { }
@@ -14,8 +14,9 @@ namespace Barotrauma.Abilities
protected override bool MatchesConditionSpecific(AbilityObject abilityObject) protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
{ {
if ((abilityObject as IAbilityCharacter)?.Character is Character character) if (abilityObject is IAbilityCharacter abilityCharacter)
{ {
if (!(abilityCharacter.Character is Character character)) { return false; }
if (!IsViableTarget(targetTypes, character)) { return false; } if (!IsViableTarget(targetTypes, character)) { return false; }
return true; return true;
@@ -0,0 +1,23 @@
using System.Xml.Linq;
namespace Barotrauma.Abilities
{
class AbilityConditionGeneHarvester : AbilityConditionData
{
public AbilityConditionGeneHarvester(CharacterTalent characterTalent, XElement conditionElement) : base(characterTalent, conditionElement) { }
protected override bool MatchesConditionSpecific(AbilityObject abilityObject)
{
if (abilityObject is AbilityCharacterKill abilityCharacterKill)
{
return abilityCharacterKill.Killer.Submarine == null || abilityCharacterKill.Killer.TeamID != abilityCharacterKill.Killer.Submarine.TeamID;
}
else
{
LogAbilityConditionError(abilityObject, typeof(AbilityCharacterKill));
return false;
}
}
}
}
@@ -136,6 +136,7 @@ namespace Barotrauma.Abilities
public float DamageMultiplier { get; set; } = 1f; public float DamageMultiplier { get; set; } = 1f;
public float AddedPenetration { get; set; } = 0f; public float AddedPenetration { get; set; } = 0f;
public List<Affliction> Afflictions { get; set; } public List<Affliction> Afflictions { get; set; }
public bool ShouldImplode { get; set; } = false;
public Attack SourceAttack { get; } public Attack SourceAttack { get; }
public Character Character { get; set; } public Character Character { get; set; }
public Character Attacker { get; set; } public Character Attacker { get; set; }
@@ -37,12 +37,7 @@ namespace Barotrauma.Abilities
attackData.DamageMultiplier += addedDamageMultiplier; attackData.DamageMultiplier += addedDamageMultiplier;
attackData.AddedPenetration += addedPenetration; attackData.AddedPenetration += addedPenetration;
if (implode) attackData.ShouldImplode = implode;
{
// might have issues, as the method used to be private and only used for pressure death
attackData.Character?.Implode();
}
} }
else else
{ {
@@ -8,6 +8,7 @@ namespace Barotrauma.Abilities
private readonly AbilityFlags abilityFlag; private readonly AbilityFlags abilityFlag;
private bool lastState; private bool lastState;
public override bool AllowClientSimulation => true;
public CharacterAbilityModifyFlag(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) public CharacterAbilityModifyFlag(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{ {
@@ -6,6 +6,7 @@ namespace Barotrauma.Abilities
class CharacterAbilityModifyReduceAffliction : CharacterAbility class CharacterAbilityModifyReduceAffliction : CharacterAbility
{ {
float addedAmountMultiplier; float addedAmountMultiplier;
public override bool AllowClientSimulation => true;
public CharacterAbilityModifyReduceAffliction(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) public CharacterAbilityModifyReduceAffliction(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{ {
@@ -7,6 +7,7 @@ namespace Barotrauma.Abilities
private readonly string resistanceId; private readonly string resistanceId;
private readonly float resistance; private readonly float resistance;
bool lastState; bool lastState;
public override bool AllowClientSimulation => true;
// should probably be split to different classes // should probably be split to different classes
public CharacterAbilityModifyResistance(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) public CharacterAbilityModifyResistance(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
@@ -7,6 +7,7 @@ namespace Barotrauma.Abilities
private readonly StatTypes statType; private readonly StatTypes statType;
private readonly float value; private readonly float value;
bool lastState; bool lastState;
public override bool AllowClientSimulation => true;
public CharacterAbilityModifyStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) public CharacterAbilityModifyStat(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{ {
@@ -8,6 +8,7 @@ namespace Barotrauma.Abilities
private readonly StatTypes statType; private readonly StatTypes statType;
private readonly float maxValue; private readonly float maxValue;
private float lastValue = 0f; private float lastValue = 0f;
public override bool AllowClientSimulation => true;
public CharacterAbilityModifyStatToFlooding(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) public CharacterAbilityModifyStatToFlooding(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{ {
@@ -9,6 +9,7 @@ namespace Barotrauma.Abilities
private readonly float statPerLevel; private readonly float statPerLevel;
private readonly int maxLevel; private readonly int maxLevel;
private float lastValue = 0f; private float lastValue = 0f;
public override bool AllowClientSimulation => true;
public CharacterAbilityModifyStatToLevel(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) public CharacterAbilityModifyStatToLevel(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{ {
@@ -10,6 +10,7 @@ namespace Barotrauma.Abilities
private readonly string skillIdentifier; private readonly string skillIdentifier;
private readonly bool useAll; private readonly bool useAll;
private float lastValue = 0f; private float lastValue = 0f;
public override bool AllowClientSimulation => true;
public CharacterAbilityModifyStatToSkill(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) public CharacterAbilityModifyStatToSkill(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{ {
@@ -19,11 +19,10 @@ namespace Barotrauma.Abilities
moneyPerMission = abilityElement.GetAttributeInt("moneypermission", 0); moneyPerMission = abilityElement.GetAttributeInt("moneypermission", 0);
} }
protected override void ApplyEffect() protected override void ApplyEffect(AbilityObject abilityObject)
{ {
if (Character?.Info is CharacterInfo info) if (Character?.Info is CharacterInfo info)
{ {
Character.GiveMoney(moneyPerMission * info.MissionsCompletedSinceDeath); Character.GiveMoney(moneyPerMission * info.MissionsCompletedSinceDeath);
} }
} }
@@ -8,6 +8,7 @@ namespace Barotrauma.Abilities
private float maxValue; private float maxValue;
private string afflictionIdentifier; private string afflictionIdentifier;
private float lastValue = 0f; private float lastValue = 0f;
public override bool AllowClientSimulation => true;
public CharacterAbilityPsychoClown(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement) public CharacterAbilityPsychoClown(CharacterAbilityGroup characterAbilityGroup, XElement abilityElement) : base(characterAbilityGroup, abilityElement)
{ {
@@ -45,6 +45,20 @@ namespace Barotrauma
{ {
wifiComponent.TeamID = TeamTag; wifiComponent.TeamID = TeamTag;
} }
var idCard = item.GetComponent<Items.Components.IdCard>();
if (idCard != null)
{
idCard.TeamID = TeamTag;
idCard.SubmarineSpecificID = 0;
}
}
WayPoint subWaypoint =
WayPoint.WayPointList.Find(wp => wp.Submarine == Submarine.MainSub && wp.SpawnType == SpawnType.Human && wp.AssignedJob == npc.Info.Job?.Prefab) ??
WayPoint.WayPointList.Find(wp => wp.Submarine == Submarine.MainSub && wp.SpawnType == SpawnType.Human);
if (subWaypoint != null)
{
npc.GiveIdCardTags(subWaypoint, createNetworkEvent: true);
} }
#if SERVER #if SERVER
GameMain.NetworkMember.CreateEntityEvent(npc, new object[] { NetEntityEvent.Type.AddToCrew, TeamTag, npc.Inventory.AllItems.Select(it => it.ID).ToArray() }); GameMain.NetworkMember.CreateEntityEvent(npc, new object[] { NetEntityEvent.Type.AddToCrew, TeamTag, npc.Inventory.AllItems.Select(it => it.ID).ToArray() });
@@ -117,8 +117,8 @@ namespace Barotrauma
.Select(tryFindPrefab) .Select(tryFindPrefab)
.Where(p => p != null) .Where(p => p != null)
.ToImmutableArray(); .ToImmutableArray();
this.Commonness = commonness ?? this.Prefabs.Select(p => p.Commonness).Max(); this.Commonness = commonness ?? this.Prefabs.Select(p => p.Commonness).MaxOrNull() ?? 0.0f;
this.Probability = probability ?? this.Prefabs.Select(p => p.Probability).Max(); this.Probability = probability ?? this.Prefabs.Select(p => p.Probability).MaxOrNull() ?? 0.0f;
} }
public SubEventPrefab(EventPrefab prefab, float commonness, float probability) public SubEventPrefab(EventPrefab prefab, float commonness, float probability)
@@ -390,7 +390,7 @@ namespace Barotrauma
private bool DeadOrCaptured(Character character) private bool DeadOrCaptured(Character character)
{ {
return character != null && !character.Removed && (character.IsDead || (character.LockHands && character.Submarine == Submarine.MainSub)); return character == null || character.Removed || character.IsDead || (character.LockHands && character.Submarine == Submarine.MainSub);
} }
public override void End() public override void End()
@@ -171,5 +171,21 @@ namespace Barotrauma.Extensions
} }
return false; return false;
} }
/// <summary>
/// Returns the maximum element in a given enumerable, or null if there
/// aren't any elements in the input.
/// </summary>
/// <param name="enumerable">Input collection</param>
/// <returns>Maximum element or null</returns>
public static T? MaxOrNull<T>(this IEnumerable<T> enumerable) where T : struct, IComparable<T>
{
T? retVal = null;
foreach (T v in enumerable)
{
if (!retVal.HasValue || v.CompareTo(retVal.Value) > 0) { retVal = v; }
}
return retVal;
}
} }
} }
@@ -80,6 +80,8 @@ namespace Barotrauma.Items.Components
if ((picker.PickingItem == null || picker.PickingItem == item) && PickingTime <= float.MaxValue) if ((picker.PickingItem == null || picker.PickingItem == item) && PickingTime <= float.MaxValue)
{ {
#if SERVER #if SERVER
// Set active picker before creating the server event to make sure it's set correctly
activePicker = picker;
item.CreateServerEvent(this); item.CreateServerEvent(this);
#endif #endif
pickingCoroutine = CoroutineManager.StartCoroutine(WaitForPick(picker, abilityPickingTime.Value)); pickingCoroutine = CoroutineManager.StartCoroutine(WaitForPick(picker, abilityPickingTime.Value));
@@ -72,9 +72,9 @@ namespace Barotrauma.Items.Components
{ {
get get
{ {
Matrix bodyTransform = Matrix.CreateRotationZ(item.body.Rotation); Matrix bodyTransform = Matrix.CreateRotationZ(item.body == null ? MathHelper.ToRadians(item.Rotation) : item.body.Rotation);
Vector2 flippedPos = barrelPos; Vector2 flippedPos = barrelPos;
if (item.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; } if (item.body != null && item.body.Dir < 0.0f) { flippedPos.X = -flippedPos.X; }
return Vector2.Transform(flippedPos, bodyTransform) * item.Scale; return Vector2.Transform(flippedPos, bodyTransform) * item.Scale;
} }
} }
@@ -202,7 +202,7 @@ namespace Barotrauma.Items.Components
{ {
float percentageHealth = targetItem.Condition / targetItem.MaxCondition; float percentageHealth = targetItem.Condition / targetItem.MaxCondition;
if (percentageHealth <= deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) { return; } if (percentageHealth < deconstructProduct.MinCondition || percentageHealth > deconstructProduct.MaxCondition) { return; }
if (!(MapEntityPrefab.Find(null, deconstructProduct.ItemIdentifier) is ItemPrefab itemPrefab)) if (!(MapEntityPrefab.Find(null, deconstructProduct.ItemIdentifier) is ItemPrefab itemPrefab))
{ {
@@ -347,22 +347,20 @@ namespace Barotrauma.Items.Components
float outCondition = fabricatedItem.OutCondition; float outCondition = fabricatedItem.OutCondition;
if (i < amountFittingContainer) if (i < amountFittingContainer)
{ {
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * outCondition, quality,
onSpawned: (Item spawnedItem) => onSpawned: (Item spawnedItem) =>
{ {
onItemSpawned(spawnedItem, tempUser); onItemSpawned(spawnedItem, tempUser);
spawnedItem.Quality = quality;
//reset the condition in case the max condition is higher than the prefab's due to e.g. quality modifiers //reset the condition in case the max condition is higher than the prefab's due to e.g. quality modifiers
spawnedItem.Condition = spawnedItem.MaxCondition * outCondition; spawnedItem.Condition = spawnedItem.MaxCondition * outCondition;
}); });
} }
else else
{ {
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * outCondition, Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, item.Position, item.Submarine, fabricatedItem.TargetItem.Health * outCondition, quality,
onSpawned: (Item spawnedItem) => onSpawned: (Item spawnedItem) =>
{ {
onItemSpawned(spawnedItem, tempUser); onItemSpawned(spawnedItem, tempUser);
spawnedItem.Quality = quality;
//reset the condition in case the max condition is higher than the prefab's due to e.g. quality modifiers //reset the condition in case the max condition is higher than the prefab's due to e.g. quality modifiers
spawnedItem.Condition = spawnedItem.MaxCondition * outCondition; spawnedItem.Condition = spawnedItem.MaxCondition * outCondition;
}); });
@@ -75,6 +75,7 @@ namespace Barotrauma.Items.Components
float closestDist = float.PositiveInfinity; float closestDist = float.PositiveInfinity;
foreach (Item targetItem in Item.ItemList) foreach (Item targetItem in Item.ItemList)
{ {
if (targetItem.NonInteractable || targetItem.NonPlayerTeamInteractable || targetItem.HiddenInGame) { continue; }
if (OnlyInOwnSub) if (OnlyInOwnSub)
{ {
if (targetItem.Submarine != item.Submarine) { continue; } if (targetItem.Submarine != item.Submarine) { continue; }
@@ -547,6 +547,11 @@ namespace Barotrauma.Items.Components
//power transfer items (junction boxes, relays) don't deteriorate if they're no carrying any power //power transfer items (junction boxes, relays) don't deteriorate if they're no carrying any power
if (Math.Abs(pt.CurrPowerConsumption) > 0.1f) { return true; } if (Math.Abs(pt.CurrPowerConsumption) > 0.1f) { return true; }
} }
else if (ic is PowerContainer pc)
{
//batteries don't deteriorate if they're not charging/discharging
if (Math.Abs(pc.CurrPowerConsumption) > 0.1f || Math.Abs(pc.CurrPowerOutput) > 0.1f) { return true; }
}
else if (ic is Engine engine) else if (ic is Engine engine)
{ {
//engines don't deteriorate if they're not running //engines don't deteriorate if they're not running
@@ -555,7 +560,7 @@ namespace Barotrauma.Items.Components
else if (ic is Pump pump) else if (ic is Pump pump)
{ {
//pumps don't deteriorate if they're not running //pumps don't deteriorate if they're not running
if (Math.Abs(pump.FlowPercentage) > 1.0f && pump.IsActive) { return true; } if (Math.Abs(pump.FlowPercentage) > 1.0f && pump.IsActive && pump.HasPower) { return true; }
} }
else if (ic is Reactor reactor) else if (ic is Reactor reactor)
{ {
@@ -127,6 +127,8 @@ namespace Barotrauma.Items.Components
if (source == null || target == null || target.Removed || if (source == null || target == null || target.Removed ||
(source is Entity sourceEntity && sourceEntity.Removed)) (source is Entity sourceEntity && sourceEntity.Removed))
{ {
source = null;
target = null;
IsActive = false; IsActive = false;
return; return;
} }
@@ -225,10 +225,23 @@ namespace Barotrauma.Items.Components
//no electrocution in sub editor //no electrocution in sub editor
if (Screen.Selected == GameMain.SubEditorScreen) { return true; } if (Screen.Selected == GameMain.SubEditorScreen) { return true; }
var powered = item.GetComponent<Powered>(); var reactor = item.GetComponent<Reactor>();
if (powered != null) if (reactor != null)
{ {
//unpowered panels can be rewired without a risk of electrical shock //reactors that arent generating power atm can be rewired without the risk of electrical shock
if (MathUtils.NearlyEqual(reactor.CurrPowerConsumption, 0.0f)) { return true; }
}
var powerContainer = item.GetComponent<PowerContainer>();
if (powerContainer != null)
{
//empty batteries/supercapacitors can be rewired without the risk of electrical shock
//non-empty ones always have a chance of zapping the user
if (powerContainer.Charge <= 0.0f) { return true; }
}
var powered = item.GetComponent<Powered>();
if (powered != null && powerContainer == null)
{
//unpowered panels can be rewired without the risk of electrical shock
if (powered.Voltage < 0.1f) { return true; } if (powered.Voltage < 0.1f) { return true; }
} }
@@ -13,7 +13,7 @@ namespace Barotrauma
struct DeconstructItem struct DeconstructItem
{ {
public readonly string ItemIdentifier; public readonly string ItemIdentifier;
//minCondition does <= check, meaning that below or equeal to min condition will be skipped. //minCondition does <= check, meaning that below or equal to min condition will be skipped.
public readonly float MinCondition; public readonly float MinCondition;
//maxCondition does > check, meaning that above this max the deconstruct item will be skipped. //maxCondition does > check, meaning that above this max the deconstruct item will be skipped.
public readonly float MaxCondition; public readonly float MaxCondition;
@@ -50,7 +50,6 @@ namespace Barotrauma
ActivateButtonText = element.GetAttributeString("activatebuttontext", string.Empty); ActivateButtonText = element.GetAttributeString("activatebuttontext", string.Empty);
InfoText = element.GetAttributeString("infotext", string.Empty); InfoText = element.GetAttributeString("infotext", string.Empty);
InfoTextOnOtherItemMissing = element.GetAttributeString("infotextonotheritemmissing", string.Empty); InfoTextOnOtherItemMissing = element.GetAttributeString("infotextonotheritemmissing", string.Empty);
} }
} }
@@ -36,7 +36,7 @@ namespace Barotrauma
/// </summary> /// </summary>
public bool ExcludeBroken { get; private set; } public bool ExcludeBroken { get; private set; }
private bool allowVariants = true; public bool AllowVariants { get; private set; } = true;
public RelationType Type public RelationType Type
{ {
@@ -84,13 +84,13 @@ namespace Barotrauma
{ {
if (item == null) { return false; } if (item == null) { return false; }
if (excludedIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { return false; } if (excludedIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) { return false; }
return Identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id) || (allowVariants && item.Prefab.VariantOf?.Identifier == id)); return Identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id) || (AllowVariants && item.Prefab.VariantOf?.Identifier == id));
} }
public bool MatchesItem(ItemPrefab itemPrefab) public bool MatchesItem(ItemPrefab itemPrefab)
{ {
if (itemPrefab == null) { return false; } if (itemPrefab == null) { return false; }
if (excludedIdentifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id))) { return false; } if (excludedIdentifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id))) { return false; }
return Identifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id) || (allowVariants && itemPrefab.VariantOf?.Identifier == id)); return Identifiers.Any(id => itemPrefab.Identifier == id || itemPrefab.Tags.Contains(id) || (AllowVariants && itemPrefab.VariantOf?.Identifier == id));
} }
public RelatedItem(string[] identifiers, string[] excludedIdentifiers) public RelatedItem(string[] identifiers, string[] excludedIdentifiers)
@@ -171,7 +171,7 @@ namespace Barotrauma
new XAttribute("ignoreineditor", IgnoreInEditor), new XAttribute("ignoreineditor", IgnoreInEditor),
new XAttribute("excludebroken", ExcludeBroken), new XAttribute("excludebroken", ExcludeBroken),
new XAttribute("targetslot", TargetSlot), new XAttribute("targetslot", TargetSlot),
new XAttribute("allowvariants", allowVariants)); new XAttribute("allowvariants", AllowVariants));
if (excludedIdentifiers.Length > 0) if (excludedIdentifiers.Length > 0)
{ {
@@ -235,7 +235,7 @@ namespace Barotrauma
RelatedItem ri = new RelatedItem(identifiers, excludedIdentifiers) RelatedItem ri = new RelatedItem(identifiers, excludedIdentifiers)
{ {
ExcludeBroken = element.GetAttributeBool("excludebroken", true), ExcludeBroken = element.GetAttributeBool("excludebroken", true),
allowVariants = element.GetAttributeBool("allowvariants", true) AllowVariants = element.GetAttributeBool("allowvariants", true)
}; };
string typeStr = element.GetAttributeString("type", ""); string typeStr = element.GetAttributeString("type", "");
if (string.IsNullOrEmpty(typeStr)) if (string.IsNullOrEmpty(typeStr))
@@ -510,14 +510,14 @@ namespace Barotrauma.MapCreatures.Behavior
List<BallastFloraBranch> list = branches[hull]; List<BallastFloraBranch> list = branches[hull];
if (!list.Any(HasAcidEmitter)) if (!list.Any(HasAcidEmitter))
{ {
BallastFloraBranch randomBranh = branches[hull].GetRandom(); BallastFloraBranch randomBranch = branches[hull].GetRandom();
randomBranh.SpawningItem = true; randomBranch.SpawningItem = true;
ItemPrefab prefab = ItemPrefab.Find(null, AttackItemPrefab); ItemPrefab prefab = ItemPrefab.Find(null, AttackItemPrefab);
Entity.Spawner?.AddToSpawnQueue(prefab, Parent.Position + Offset + randomBranh.Position, Parent.Submarine, null, item => Entity.Spawner?.AddToSpawnQueue(prefab, Parent.Position + Offset + randomBranch.Position, Parent.Submarine, onSpawned: item =>
{ {
randomBranh.AttackItem = item; randomBranch.AttackItem = item;
randomBranh.SpawningItem = false; randomBranch.SpawningItem = false;
}); });
} }
@@ -213,9 +213,8 @@ namespace Barotrauma
}; };
PhysicsBody.FarseerBody.OnCollision += PhysicsBody_OnCollision; PhysicsBody.FarseerBody.OnCollision += PhysicsBody_OnCollision;
PhysicsBody.FarseerBody.OnSeparation += PhysicsBody_OnSeparation; PhysicsBody.FarseerBody.OnSeparation += PhysicsBody_OnSeparation;
PhysicsBody.FarseerBody.SetIsSensor(true); PhysicsBody.FarseerBody.SetIsSensor(element.GetAttributeBool("sensor", true));
PhysicsBody.FarseerBody.BodyType = BodyType.Static; PhysicsBody.FarseerBody.BodyType = BodyType.Static;
PhysicsBody.FarseerBody.BodyType = BodyType.Kinematic;
ColliderRadius = ConvertUnits.ToDisplayUnits(Math.Max(Math.Max(PhysicsBody.radius, PhysicsBody.width / 2.0f), PhysicsBody.height / 2.0f)); ColliderRadius = ConvertUnits.ToDisplayUnits(Math.Max(Math.Max(PhysicsBody.radius, PhysicsBody.width / 2.0f), PhysicsBody.height / 2.0f));
@@ -532,10 +531,9 @@ namespace Barotrauma
} }
} }
if (triggerOnce) if (triggerOnce && triggeredOnce)
{ {
if (triggeredOnce) { return; } return;
if (triggerers.Count > 0) { triggeredOnce = true; }
} }
foreach (Entity triggerer in triggerers) foreach (Entity triggerer in triggerers)
@@ -577,6 +575,12 @@ namespace Barotrauma
GameMain.GameScreen.Cam.Shake = Math.Max(GameMain.GameScreen.Cam.Shake, cameraShake); GameMain.GameScreen.Cam.Shake = Math.Max(GameMain.GameScreen.Cam.Shake, cameraShake);
} }
} }
if (triggerOnce && triggerers.Count > 0)
{
PhysicsBody.Enabled = false;
triggeredOnce = true;
}
} }
public static void RemoveDistantTriggerers(PhysicsBody physicsBody, HashSet<Entity> triggerers, Vector2 calculateDistanceTo) public static void RemoveDistantTriggerers(PhysicsBody physicsBody, HashSet<Entity> triggerers, Vector2 calculateDistanceTo)
@@ -26,6 +26,7 @@ namespace Barotrauma
public readonly Inventory Inventory; public readonly Inventory Inventory;
public readonly Submarine Submarine; public readonly Submarine Submarine;
public readonly float Condition; public readonly float Condition;
public readonly int Quality;
public bool SpawnIfInventoryFull = true; public bool SpawnIfInventoryFull = true;
public bool IgnoreLimbSlots = false; public bool IgnoreLimbSlots = false;
@@ -33,28 +34,31 @@ namespace Barotrauma
private readonly Action<Item> onSpawned; private readonly Action<Item> onSpawned;
public ItemSpawnInfo(ItemPrefab prefab, Vector2 worldPosition, Action<Item> onSpawned, float? condition = null) public ItemSpawnInfo(ItemPrefab prefab, Vector2 worldPosition, Action<Item> onSpawned, float? condition = null, int? quality = null)
{ {
Prefab = prefab ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null."); Prefab = prefab ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null.");
Position = worldPosition; Position = worldPosition;
Condition = condition ?? prefab.Health; Condition = condition ?? prefab.Health;
Quality = quality ?? 0;
this.onSpawned = onSpawned; this.onSpawned = onSpawned;
} }
public ItemSpawnInfo(ItemPrefab prefab, Vector2 position, Submarine sub, Action<Item> onSpawned, float? condition = null) public ItemSpawnInfo(ItemPrefab prefab, Vector2 position, Submarine sub, Action<Item> onSpawned, float? condition = null, int? quality = null)
{ {
Prefab = prefab ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null."); Prefab = prefab ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null.");
Position = position; Position = position;
Submarine = sub; Submarine = sub;
Condition = condition ?? prefab.Health; Condition = condition ?? prefab.Health;
Quality = quality ?? 0;
this.onSpawned = onSpawned; this.onSpawned = onSpawned;
} }
public ItemSpawnInfo(ItemPrefab prefab, Inventory inventory, Action<Item> onSpawned, float? condition = null) public ItemSpawnInfo(ItemPrefab prefab, Inventory inventory, Action<Item> onSpawned, float? condition = null, int? quality = null)
{ {
Prefab = prefab ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null."); Prefab = prefab ?? throw new ArgumentException("ItemSpawnInfo prefab cannot be null.");
Inventory = inventory; Inventory = inventory;
Condition = condition ?? prefab.Health; Condition = condition ?? prefab.Health;
Quality = quality ?? 0;
this.onSpawned = onSpawned; this.onSpawned = onSpawned;
} }
@@ -73,7 +77,8 @@ namespace Barotrauma
} }
spawnedItem = new Item(Prefab, Vector2.Zero, null) spawnedItem = new Item(Prefab, Vector2.Zero, null)
{ {
Condition = Condition Condition = Condition,
Quality = Quality
}; };
var slot = Slot != InvSlotType.None ? Slot.ToEnumerable() : spawnedItem.AllowedSlots; var slot = Slot != InvSlotType.None ? Slot.ToEnumerable() : spawnedItem.AllowedSlots;
if (!Inventory.Owner.Removed && !Inventory.TryPutItem(spawnedItem, null, slot)) if (!Inventory.Owner.Removed && !Inventory.TryPutItem(spawnedItem, null, slot))
@@ -94,7 +99,11 @@ namespace Barotrauma
} }
else else
{ {
spawnedItem = new Item(Prefab, Position, Submarine); spawnedItem = new Item(Prefab, Position, Submarine)
{
Condition = Condition,
Quality = Quality
};
} }
return spawnedItem; return spawnedItem;
} }
@@ -241,7 +250,7 @@ namespace Barotrauma
return "EntitySpawner"; return "EntitySpawner";
} }
public void AddToSpawnQueue(ItemPrefab itemPrefab, Vector2 worldPosition, float? condition = null, Action<Item> onSpawned = null) public void AddToSpawnQueue(ItemPrefab itemPrefab, Vector2 worldPosition, float? condition = null, int? quality = null, Action<Item> onSpawned = null)
{ {
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (itemPrefab == null) if (itemPrefab == null)
@@ -251,10 +260,10 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue1:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue1:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return; return;
} }
spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, worldPosition, onSpawned, condition)); spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, worldPosition, onSpawned, condition, quality));
} }
public void AddToSpawnQueue(ItemPrefab itemPrefab, Vector2 position, Submarine sub, float? condition = null, Action<Item> onSpawned = null) public void AddToSpawnQueue(ItemPrefab itemPrefab, Vector2 position, Submarine sub, float? condition = null, int? quality = null, Action<Item> onSpawned = null)
{ {
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (itemPrefab == null) if (itemPrefab == null)
@@ -264,10 +273,10 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue2:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue2:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return; return;
} }
spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, position, sub, onSpawned, condition)); spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, position, sub, onSpawned, condition, quality));
} }
public void AddToSpawnQueue(ItemPrefab itemPrefab, Inventory inventory, float? condition = null, Action<Item> onSpawned = null, bool spawnIfInventoryFull = true, bool ignoreLimbSlots = false, InvSlotType slot = InvSlotType.None) public void AddToSpawnQueue(ItemPrefab itemPrefab, Inventory inventory, float? condition = null, int? quality = null, Action<Item> onSpawned = null, bool spawnIfInventoryFull = true, bool ignoreLimbSlots = false, InvSlotType slot = InvSlotType.None)
{ {
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; } if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
if (itemPrefab == null) if (itemPrefab == null)
@@ -277,7 +286,7 @@ namespace Barotrauma
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
return; return;
} }
spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, inventory, onSpawned, condition) spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, inventory, onSpawned, condition, quality)
{ {
SpawnIfInventoryFull = spawnIfInventoryFull, SpawnIfInventoryFull = spawnIfInventoryFull,
IgnoreLimbSlots = ignoreLimbSlots, IgnoreLimbSlots = ignoreLimbSlots,
@@ -395,10 +404,6 @@ namespace Barotrauma
{ {
CreateNetworkEventProjSpecific(spawnedEntity, false); CreateNetworkEventProjSpecific(spawnedEntity, false);
} }
if (spawnedEntity is Item item)
{
item.Condition = ((ItemSpawnInfo)entitySpawnInfo).Condition;
}
entitySpawnInfo.OnSpawned(spawnedEntity); entitySpawnInfo.OnSpawned(spawnedEntity);
} }
} }
@@ -244,7 +244,7 @@ namespace Barotrauma
cam.TargetPos = targetPos; cam.TargetPos = targetPos;
} }
cam.MoveCamera((float)deltaTime, allowZoom: GUI.MouseOn == null); cam.MoveCamera((float)deltaTime, allowZoom: GUI.MouseOn == null && !Inventory.IsMouseOnInventory);
#endif #endif
foreach (Submarine sub in Submarine.Loaded) foreach (Submarine sub in Submarine.Loaded)
Binary file not shown.
+65
View File
@@ -1,3 +1,68 @@
---------------------------------------------------------------------------------------------------------
v0.15.13.0
---------------------------------------------------------------------------------------------------------
Changes:
- Remove spawnpoint-based job assignment logic. Previously, the number of job-specific spawnpoints in the sub affected how many players the server would try to assign to a given job, which would often lead to players not getting the job they wanted to.
- Made the captain job optional (i.e. if no-one has captain in their preferences, no-one gets forced to play as one).
- Improvements to the Korean localization.
- Added rewards to side objective missions (hunting grounds, beacon).
- Reduce Pyromaniac's burning damage increase from 40% to 25%.
Fixes:
- Fixed clients' characters getting reset in the multiplayer campaign if they're spectating at the end of the round.
- Fixed crashing when an event set fails to load any of its sub events. Caused certain mods to crash the game at 42% in the loading screen.
- Fixed crashing on startup when using mods that remove the small icons from job prefabs.
- Fixed bots being unable to find their way to the submarine if the switch to the "find safety" state outside the sub.
- Fixed bots often being unable to find a way to leaks they're trying to weld.
- Fixed crashing when a monster was just about to turn to a husk when the round ends.
- Fixed opened item disappearing when switching to the test mode from the sub editor.
- Fixed Artie Dolittle's ID card not working in the player's sub after he's hired.
- Fixed category labels (sufficient skills to fabricate, requires recipe, etc) disappearing from the fabricator's item list when searching.
- Fixed prisoner's uniform using a wrong texture file.
- Fixed inability to edit pulse laser's power consumption in the sub editor.
- Fixed an exploit in Pressure Stabilizer crafting recipe.
- Fixed an exploit in Fixfoam Grenade deconstruction recipe.
- Fixed crashing when selecting a gene splicer and hovering over its inventory slot in the sub editor.
- Fixed bots being unable to use hardened and dementonite tools.
- Fixed a typo in Tinkering cooldown reduction description.
- Fixed inactive reactors electrocuting low-skill characters when rewired.
- Fixed ignore orders carrying over when switching subs, causing them to target random items in the new sub.
- Fixed dementonite knives being sold in stores.
- Fixed contained items' impact sounds being played when the item they're inside hits the floor.
- Fixed True Potential instant kills not properly giving kill credit (achievements, other talents).
- Fixed Gene Harvester incorrectly checking the owner of the talent's submarine rather than the killer's.
- Fixed certain talents not appearing to have an effect client-side, causing e.g. the high-pressure effects to appear when swimming outside with the Water Prankster talent.
- Fix Scavenger's buff duration multiplier.
- Fixed Insurance Policy not triggering properly.
- Fixed issues with input going through interfaces drawn over inventory slots.
- Fixed bots trying to treat talent afflictions.
- Fixed player-controlled creature attacks sometimes not hitting characters when they should.
- Fixed "x in command room" spam when dragging and dropping orders in multiplayer.
- Fixed pirate missions not being considered completed if any of the pirates have been removed (e.g. eaten or despawned).
- Fixed high-quality items not stacking in the fabricator's output slot.
- Fixed ice shards' colliders taying active after the shard has shattered.
- Fixed bots following the controlled character instead of the order giver in singleplayer.
- Fixed purchased medals spawning on the floor.
- Fixed vision obstruction effect "flickering" when moving the cursor around when an item UI is open.
- Fixed some pumps in Dugong having 0 power consumption.
- Fixed pumps deteriorating when they don't have power.
- Fixed batteries/supercapacitors deteriorating when not charging/discharging.
- Fixed afflictions applying face/body tints on monsters.
- Fixed "select matching items" selecting all gaps if you've selected both a door and its gap in the sub editor.
- Fixed door gaps not being selected if you use "select matching items" on a door in the sub editor.
- Fixed biome ambience loop volume not being affected by the sound volume setting.
- Fixed players wearing a PUCS not using up hull oxygen when no tank is equipped.
- Fixed bots getting stuck on long outpost ladders.
- Restored the sounds for legacy fractal guardians. Fixes console errors when they are spawned/viewed in the editor.
- Fixed Reactor PDA showing hidden and non-interactable reactors.
- Fixed welding tool scale being forced to 0.5.
- Fix crew list content being repositioned when selected character was hovered.
Modding:
- Made deconstruction recipes' mincondition accept items whose condition equals to the mincondition, not just items whose condition is higher.
- Fixed color values being used incorrectly in explosion flashes.
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
v0.15.12.0 v0.15.12.0
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
+4 -1
View File
@@ -21,7 +21,10 @@
losmode="Transparent" losmode="Transparent"
hudscale="1" hudscale="1"
inventoryscale="1" /> inventoryscale="1" />
<contentpackage path="Data/ContentPackages/Vanilla 0.9.xml" /> <contentpackages>
<core
name="Vanilla 0.9" />
</contentpackages>
<player name=""/> <player name=""/>
<keymapping <keymapping
Select="PrimaryMouse" Select="PrimaryMouse"