Unstable 0.15.13.0

This commit is contained in:
Markus Isberg
2021-10-29 21:57:18 +09:00
parent 1718dbc1c1
commit a43b540d4b
67 changed files with 467 additions and 222 deletions
@@ -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++)
@@ -770,8 +770,13 @@ namespace Barotrauma
BarScroll += speed * Math.Sign(diff) / TotalSize; BarScroll += speed * Math.Sign(diff) / TotalSize;
} }
} }
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)
@@ -579,15 +582,17 @@ 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,25 +223,30 @@ 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);
} }
c.CharacterInfo = characterInfo; c.CharacterInfo = characterInfo;
characterData.RemoveAll(cd => cd.MatchesClient(c)); characterData.RemoveAll(cd => cd.MatchesClient(c));
characterData.Add(new CharacterCampaignData(c)); characterData.Add(new CharacterCampaignData(c));
} }
//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
{ {
@@ -202,7 +202,7 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.Select(); GameMain.NetLobbyScreen.Select();
GameMain.NetLobbyScreen.RandomizeSettings(); GameMain.NetLobbyScreen.RandomizeSettings();
if (!string.IsNullOrEmpty(serverSettings.SelectedSubmarine)) if (!string.IsNullOrEmpty(serverSettings.SelectedSubmarine))
{ {
SubmarineInfo sub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == serverSettings.SelectedSubmarine); SubmarineInfo sub = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.Name == serverSettings.SelectedSubmarine);
if (sub != null) { GameMain.NetLobbyScreen.SelectedSub = sub; } if (sub != null) { GameMain.NetLobbyScreen.SelectedSub = sub; }
@@ -536,7 +536,7 @@ namespace Barotrauma.Networking
initiatedStartGame = false; initiatedStartGame = false;
} }
} }
else if (Screen.Selected == GameMain.NetLobbyScreen && !gameStarted && !initiatedStartGame && else if (Screen.Selected == GameMain.NetLobbyScreen && !gameStarted && !initiatedStartGame &&
(GameMain.NetLobbyScreen.SelectedMode != GameModePreset.MultiPlayerCampaign || GameMain.GameSession?.GameMode is MultiPlayerCampaign)) (GameMain.NetLobbyScreen.SelectedMode != GameModePreset.MultiPlayerCampaign || GameMain.GameSession?.GameMode is MultiPlayerCampaign))
{ {
if (serverSettings.AutoRestart) if (serverSettings.AutoRestart)
@@ -970,9 +970,9 @@ namespace Barotrauma.Networking
{ {
var spawnData = entityEvent.Data[0] as EntitySpawner.SpawnOrRemove; var spawnData = entityEvent.Data[0] as EntitySpawner.SpawnOrRemove;
errorLines.Add( errorLines.Add(
entityEvent.ID + ": " + entityEvent.ID + ": " +
(spawnData.Remove ? "Remove " : "Create ") + (spawnData.Remove ? "Remove " : "Create ") +
spawnData.Entity.ToString() + spawnData.Entity.ToString() +
" (" + spawnData.OriginalID + ", " + spawnData.Entity.ID + ")"); " (" + spawnData.OriginalID + ", " + spawnData.Entity.ID + ")");
} }
} }
@@ -1221,7 +1221,7 @@ namespace Barotrauma.Networking
sender.WaitForNextRoundRespawn = null; sender.WaitForNextRoundRespawn = null;
} }
} }
private void ClientReadServerCommand(IReadMessage inc) private void ClientReadServerCommand(IReadMessage inc)
{ {
Client sender = ConnectedClients.Find(x => x.Connection == inc.Sender); Client sender = ConnectedClients.Find(x => x.Connection == inc.Sender);
@@ -1326,7 +1326,7 @@ namespace Barotrauma.Networking
GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine); GameMain.GameSession.SubmarineInfo = new SubmarineInfo(GameMain.GameSession.Submarine);
SaveUtil.SaveGame(GameMain.GameSession.SavePath); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
} }
EndGame(); EndGame();
} }
} }
else else
@@ -1457,7 +1457,7 @@ namespace Barotrauma.Networking
} }
break; break;
case ClientPermissions.ManageCampaign: case ClientPermissions.ManageCampaign:
(GameMain.GameSession.GameMode as MultiPlayerCampaign)?.ServerRead(inc, sender); (GameMain.GameSession.GameMode as MultiPlayerCampaign)?.ServerRead(inc, sender);
break; break;
case ClientPermissions.ConsoleCommands: case ClientPermissions.ConsoleCommands:
{ {
@@ -1813,7 +1813,7 @@ namespace Barotrauma.Networking
outmsg.Write(client.InGame); outmsg.Write(client.InGame);
outmsg.Write(client.Permissions != ClientPermissions.None); outmsg.Write(client.Permissions != ClientPermissions.None);
outmsg.Write(client.Connection == OwnerConnection); outmsg.Write(client.Connection == OwnerConnection);
outmsg.Write(client.Connection != OwnerConnection && outmsg.Write(client.Connection != OwnerConnection &&
!client.HasPermission(ClientPermissions.Ban) && !client.HasPermission(ClientPermissions.Ban) &&
!client.HasPermission(ClientPermissions.Kick) && !client.HasPermission(ClientPermissions.Kick) &&
!client.HasPermission(ClientPermissions.Unban)); //is kicking the player allowed !client.HasPermission(ClientPermissions.Unban)); //is kicking the player allowed
@@ -2193,8 +2193,8 @@ namespace Barotrauma.Networking
bool isOutpost = campaign != null && campaign.NextLevel?.Type == LevelData.LevelType.Outpost; bool isOutpost = campaign != null && campaign.NextLevel?.Type == LevelData.LevelType.Outpost;
if (serverSettings.AllowRespawn && missionAllowRespawn) if (serverSettings.AllowRespawn && missionAllowRespawn)
{ {
respawnManager = new RespawnManager(this, serverSettings.UseRespawnShuttle && !isOutpost ? selectedShuttle : null); respawnManager = new RespawnManager(this, serverSettings.UseRespawnShuttle && !isOutpost ? selectedShuttle : null);
} }
if (campaign != null) if (campaign != null)
{ {
@@ -2286,7 +2286,7 @@ namespace Barotrauma.Networking
} }
AssignBotJobs(bots, teamID); AssignBotJobs(bots, teamID);
if (campaign != null) if (campaign != null)
{ {
foreach (CharacterInfo bot in bots) foreach (CharacterInfo bot in bots)
{ {
@@ -2303,7 +2303,7 @@ namespace Barotrauma.Networking
List<WayPoint> spawnWaypoints = null; List<WayPoint> spawnWaypoints = null;
List<WayPoint> mainSubWaypoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSubs[n]).ToList(); List<WayPoint> mainSubWaypoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSubs[n]).ToList();
if (Level.Loaded?.StartOutpost != null && if (Level.Loaded?.StartOutpost != null &&
Level.Loaded.Type == LevelData.LevelType.Outpost && Level.Loaded.Type == LevelData.LevelType.Outpost &&
(Level.Loaded.StartOutpost.Info.OutpostGenerationParams?.SpawnCrewInsideOutpost ?? false) && (Level.Loaded.StartOutpost.Info.OutpostGenerationParams?.SpawnCrewInsideOutpost ?? false) &&
Level.Loaded.StartOutpost.GetConnectedSubs().Any(s => s.Info.Type == SubmarineType.Player)) Level.Loaded.StartOutpost.GetConnectedSubs().Any(s => s.Info.Type == SubmarineType.Player))
@@ -2805,7 +2805,7 @@ namespace Barotrauma.Networking
//reset karma to a neutral value, so if/when the ban is revoked the client wont get immediately punished by low karma again //reset karma to a neutral value, so if/when the ban is revoked the client wont get immediately punished by low karma again
previousPlayer.Karma = Math.Max(previousPlayer.Karma, 50.0f); previousPlayer.Karma = Math.Max(previousPlayer.Karma, 50.0f);
if (!string.IsNullOrEmpty(previousPlayer.EndPoint) && (previousPlayer.SteamID == 0 || range)) if (!string.IsNullOrEmpty(previousPlayer.EndPoint) && (previousPlayer.SteamID == 0 || range))
{ {
string ip = previousPlayer.EndPoint; string ip = previousPlayer.EndPoint;
@@ -3142,7 +3142,7 @@ namespace Barotrauma.Networking
modifiedMessage, modifiedMessage,
(ChatMessageType)type, (ChatMessageType)type,
senderCharacter, senderCharacter,
senderClient, senderClient,
changeType); changeType);
SendDirectChatMessage(chatMsg, client); SendDirectChatMessage(chatMsg, client);
@@ -3435,7 +3435,7 @@ namespace Barotrauma.Networking
{ {
newCharacter.LastNetworkUpdateID = client.Character.LastNetworkUpdateID; newCharacter.LastNetworkUpdateID = client.Character.LastNetworkUpdateID;
} }
if (newCharacter.Info != null && newCharacter.Info.Character == null) if (newCharacter.Info != null && newCharacter.Info.Character == null)
{ {
newCharacter.Info.Character = newCharacter; newCharacter.Info.Character = newCharacter;
@@ -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);
}
} }
} }
@@ -3689,7 +3675,7 @@ namespace Barotrauma.Networking
{ {
c.AssignedJob = preferredJob; c.AssignedJob = preferredJob;
assignedClientCount[preferredJob.First]++; assignedClientCount[preferredJob.First]++;
break; break;
} }
} }
else //none of the client's preferred jobs available, choose a random job else //none of the client's preferred jobs available, choose a random job
@@ -3746,10 +3732,10 @@ namespace Barotrauma.Networking
unassignedBots[0].Job = new Job(jobPrefab, variant); unassignedBots[0].Job = new Job(jobPrefab, variant);
assignedPlayerCount[jobPrefab]++; assignedPlayerCount[jobPrefab]++;
unassignedBots.Remove(unassignedBots[0]); unassignedBots.Remove(unassignedBots[0]);
canAssign = true; canAssign = true;
} }
} while (unassignedBots.Count > 0 && canAssign); } while (unassignedBots.Count > 0 && canAssign);
//find a suitable job for the rest of the bots //find a suitable job for the rest of the bots
foreach (CharacterInfo c in unassignedBots) foreach (CharacterInfo c in unassignedBots)
{ {
@@ -3857,7 +3843,7 @@ namespace Barotrauma.Networking
string submarinesString = string.Empty; string submarinesString = string.Empty;
for (int i = 0; i < GameMain.NetLobbyScreen.CampaignSubmarines.Count; i++) for (int i = 0; i < GameMain.NetLobbyScreen.CampaignSubmarines.Count; i++)
{ {
submarinesString += GameMain.NetLobbyScreen.CampaignSubmarines[i].Name + ServerSettings.SubmarineSeparatorChar; submarinesString += GameMain.NetLobbyScreen.CampaignSubmarines[i].Name + ServerSettings.SubmarineSeparatorChar;
} }
submarinesString.Trim(ServerSettings.SubmarineSeparatorChar); submarinesString.Trim(ServerSettings.SubmarineSeparatorChar);
serverSettings.CampaignSubmarines = submarinesString; serverSettings.CampaignSubmarines = submarinesString;
@@ -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,15 @@ 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();
}
}
} }
} }
else else
@@ -340,14 +348,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
{ {
@@ -309,13 +309,15 @@ namespace Barotrauma
} }
if (currentPath.Finished) if (currentPath.Finished)
{ {
Vector2 pos2 = host.SimPosition; var lastNode = currentPath.Nodes.LastOrDefault();
if (character != null && character.Submarine == null && if (lastNode == null)
CurrentPath.Nodes.Count > 0 && CurrentPath.Nodes.Last().Submarine != null)
{ {
pos2 -= CurrentPath.Nodes.Last().Submarine.SimPosition; return Vector2.Zero;
}
else
{
return ConvertUnits.ToSimUnits(lastNode.WorldPosition - host.WorldPosition);
} }
return currentTarget - pos2;
} }
bool doorsChecked = false; bool doorsChecked = false;
if (!character.LockHands && buttonPressCooldown <= 0.0f) if (!character.LockHands && buttonPressCooldown <= 0.0f)
@@ -323,29 +325,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 +364,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 +377,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 +410,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 +461,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();
@@ -317,8 +310,8 @@ namespace Barotrauma
Character targetCharacter = Target as Character; Character targetCharacter = Target as Character;
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
{ {
@@ -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);
} }
} }
@@ -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() });
@@ -112,13 +112,13 @@ namespace Barotrauma
} }
return prefab; return prefab;
} }
this.Prefabs = prefabIdentifiers this.Prefabs = prefabIdentifiers
.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,7 +72,7 @@ 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.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;
}); });
@@ -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.
+54
View File
@@ -1,3 +1,57 @@
---------------------------------------------------------------------------------------------------------
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.
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 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 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.
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"