Build 0.20.9.0
This commit is contained in:
@@ -9,7 +9,7 @@ using System.Linq;
|
|||||||
|
|
||||||
namespace Barotrauma
|
namespace Barotrauma
|
||||||
{
|
{
|
||||||
class CharacterHUD
|
partial class CharacterHUD
|
||||||
{
|
{
|
||||||
const float BossHealthBarDuration = 120.0f;
|
const float BossHealthBarDuration = 120.0f;
|
||||||
|
|
||||||
@@ -99,8 +99,8 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool ShouldRecreateHudTexts { get; set; } = true;
|
public static bool RecreateHudTexts { get; set; } = true;
|
||||||
private static bool heldDownShiftWhenGotHudTexts;
|
private static bool lastHudTextsContextual;
|
||||||
private static float timeHealthWindowClosed;
|
private static float timeHealthWindowClosed;
|
||||||
|
|
||||||
public static bool IsCampaignInterfaceOpen =>
|
public static bool IsCampaignInterfaceOpen =>
|
||||||
@@ -218,7 +218,7 @@ namespace Barotrauma
|
|||||||
if (focusedItemOverlayTimer <= 0.0f)
|
if (focusedItemOverlayTimer <= 0.0f)
|
||||||
{
|
{
|
||||||
focusedItem = null;
|
focusedItem = null;
|
||||||
ShouldRecreateHudTexts = true;
|
RecreateHudTexts = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,7 +340,7 @@ namespace Barotrauma
|
|||||||
if (focusedItem != character.FocusedItem)
|
if (focusedItem != character.FocusedItem)
|
||||||
{
|
{
|
||||||
focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer);
|
focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer);
|
||||||
ShouldRecreateHudTexts = true;
|
RecreateHudTexts = true;
|
||||||
}
|
}
|
||||||
focusedItem = character.FocusedItem;
|
focusedItem = character.FocusedItem;
|
||||||
}
|
}
|
||||||
@@ -364,14 +364,14 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
|
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
|
||||||
{
|
{
|
||||||
bool shiftDown = PlayerInput.IsShiftDown();
|
bool hudTextsContextual = PlayerInput.IsShiftDown();
|
||||||
if (ShouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown)
|
if (RecreateHudTexts || lastHudTextsContextual != hudTextsContextual)
|
||||||
{
|
{
|
||||||
ShouldRecreateHudTexts = true;
|
RecreateHudTexts = true;
|
||||||
heldDownShiftWhenGotHudTexts = shiftDown;
|
lastHudTextsContextual = hudTextsContextual;
|
||||||
}
|
}
|
||||||
var hudTexts = focusedItem.GetHUDTexts(character, ShouldRecreateHudTexts);
|
var hudTexts = focusedItem.GetHUDTexts(character, RecreateHudTexts);
|
||||||
ShouldRecreateHudTexts = false;
|
RecreateHudTexts = false;
|
||||||
|
|
||||||
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
|
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
|
||||||
|
|
||||||
@@ -790,5 +790,25 @@ namespace Barotrauma
|
|||||||
Vector2 drawPos = objectiveEntity.Entity.WorldPosition;// + Vector2.UnitX * objectiveEntity.Sprite.size.X * 1.5f;
|
Vector2 drawPos = objectiveEntity.Entity.WorldPosition;// + Vector2.UnitX * objectiveEntity.Sprite.size.X * 1.5f;
|
||||||
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, objectiveEntity.Sprite, objectiveEntity.Color * iconAlpha);
|
GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, objectiveEntity.Sprite, objectiveEntity.Color * iconAlpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static partial void RecreateHudTextsIfControllingProjSpecific(Character character)
|
||||||
|
{
|
||||||
|
if (character == Character.Controlled)
|
||||||
|
{
|
||||||
|
RecreateHudTexts = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static partial void RecreateHudTextsIfFocusedProjSpecific(params Item[] items)
|
||||||
|
{
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
if (item == Character.Controlled?.FocusedItem)
|
||||||
|
{
|
||||||
|
RecreateHudTexts = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void ClientExecute(string[] args)
|
public void ClientExecute(string[] args)
|
||||||
{
|
{
|
||||||
bool allowCheats = GameMain.NetworkMember == null && (GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected is EditorScreen);
|
bool allowCheats = GameMain.NetworkMember == null && (GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected is { IsEditor: true });
|
||||||
if (!allowCheats && !CheatsEnabled && IsCheat)
|
if (!allowCheats && !CheatsEnabled && IsCheat)
|
||||||
{
|
{
|
||||||
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", Color.Red);
|
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", Color.Red);
|
||||||
|
|||||||
@@ -269,6 +269,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
selectedTalents = info.GetUnlockedTalentsInTree().ToHashSet();
|
selectedTalents = info.GetUnlockedTalentsInTree().ToHashSet();
|
||||||
|
|
||||||
|
var specializationCount = tree.TalentSubTrees.Count(t => t.Type == TalentTreeType.Specialization);
|
||||||
|
|
||||||
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
|
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
|
||||||
foreach (var subTree in tree.TalentSubTrees)
|
foreach (var subTree in tree.TalentSubTrees)
|
||||||
{
|
{
|
||||||
@@ -310,7 +312,7 @@ namespace Barotrauma
|
|||||||
for (int i = 0; i < optionAmount; i++)
|
for (int i = 0; i < optionAmount; i++)
|
||||||
{
|
{
|
||||||
TalentOption option = subTree.TalentOptionStages[i];
|
TalentOption option = subTree.TalentOptionStages[i];
|
||||||
CreateTalentOption(subTreeLayoutGroup, subTree, i, option, info);
|
CreateTalentOption(subTreeLayoutGroup, subTree, i, option, info, specializationCount);
|
||||||
}
|
}
|
||||||
subTreeLayoutGroup.RectTransform.Resize(new Point(subTreeLayoutGroup.Rect.Width,
|
subTreeLayoutGroup.RectTransform.Resize(new Point(subTreeLayoutGroup.Rect.Width,
|
||||||
subTreeLayoutGroup.Children.Sum(c => c.Rect.Height + subTreeLayoutGroup.AbsoluteSpacing)));
|
subTreeLayoutGroup.Children.Sum(c => c.Rect.Height + subTreeLayoutGroup.AbsoluteSpacing)));
|
||||||
@@ -327,7 +329,12 @@ namespace Barotrauma
|
|||||||
var specializationList = GetSpecializationList();
|
var specializationList = GetSpecializationList();
|
||||||
//resize (scale up) children if there's less than 3 of them to make them cover the whole width of the menu
|
//resize (scale up) children if there's less than 3 of them to make them cover the whole width of the menu
|
||||||
specializationList.Content.RectTransform.Resize(new Point(specializationList.Content.Children.Sum(static c => c.Rect.Width), specializationList.Rect.Height),
|
specializationList.Content.RectTransform.Resize(new Point(specializationList.Content.Children.Sum(static c => c.Rect.Width), specializationList.Rect.Height),
|
||||||
resizeChildren: specializationList.Content.Children.Count() < 3);
|
resizeChildren: specializationCount < 3);
|
||||||
|
//make room for scrollbar if there's more than the default amount of specializations
|
||||||
|
if (specializationCount > 3)
|
||||||
|
{
|
||||||
|
specializationList.RectTransform.MinSize = new Point(specializationList.Rect.Width, specializationList.Content.Rect.Height + (int)(specializationList.ScrollBar.Rect.Height * 0.9f));
|
||||||
|
}
|
||||||
|
|
||||||
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
|
GUITextBlock.AutoScaleAndNormalize(subTreeNames);
|
||||||
|
|
||||||
@@ -337,17 +344,17 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
return specList;
|
return specList;
|
||||||
}
|
}
|
||||||
|
|
||||||
GUIListBox newSpecializationList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), mainList.Content.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
|
GUIListBox newSpecializationList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), mainList.Content.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
|
||||||
return newSpecializationList;
|
return newSpecializationList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateTalentOption(GUIComponent parent, TalentSubTree subTree, int index, TalentOption talentOption, CharacterInfo info)
|
private void CreateTalentOption(GUIComponent parent, TalentSubTree subTree, int index, TalentOption talentOption, CharacterInfo info, int specializationCount)
|
||||||
{
|
{
|
||||||
int elementPadding = GUI.IntScale(8);
|
int elementPadding = GUI.IntScale(8);
|
||||||
|
int height = GUI.IntScale((GameMain.GameSession?.Campaign == null ? 65 : 60) * (specializationCount > 3 ? 0.97f : 1.0f));
|
||||||
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.01f), parent.RectTransform, anchor: Anchor.TopCenter)
|
GUIFrame talentOptionFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.01f), parent.RectTransform, anchor: Anchor.TopCenter)
|
||||||
{ MinSize = new Point(0, GUI.IntScale(65)) }, style: null);
|
{ MinSize = new Point(0, height) }, style: null);
|
||||||
|
|
||||||
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
|
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Barotrauma.Networking;
|
using Barotrauma.Networking;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
@@ -117,7 +118,7 @@ namespace Barotrauma
|
|||||||
private void UpdateBar()
|
private void UpdateBar()
|
||||||
{
|
{
|
||||||
double elapsedTime = (DateTime.Now - startTime).TotalSeconds;
|
double elapsedTime = (DateTime.Now - startTime).TotalSeconds;
|
||||||
if (msgBox != null && !msgBox.Closed && GUIMessageBox.MessageBoxes.Contains(msgBox))
|
if (msgBox is { Closed: false } && GUIMessageBox.MessageBoxes.Contains(msgBox))
|
||||||
{
|
{
|
||||||
if (msgBox.FindChild(TimerData, true) is GUIProgressBar bar)
|
if (msgBox.FindChild(TimerData, true) is GUIProgressBar bar)
|
||||||
{
|
{
|
||||||
@@ -129,7 +130,7 @@ namespace Barotrauma
|
|||||||
int second = (int)Math.Ceiling(elapsedTime);
|
int second = (int)Math.Ceiling(elapsedTime);
|
||||||
if (second > lastSecond)
|
if (second > lastSecond)
|
||||||
{
|
{
|
||||||
if (msgBox != null && !msgBox.Closed)
|
if (msgBox is { Closed: false })
|
||||||
{
|
{
|
||||||
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
|
SoundPlayer.PlayUISound(GUISoundType.PopupMenu);
|
||||||
}
|
}
|
||||||
@@ -137,6 +138,19 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void CloseLingeringPopups()
|
||||||
|
{
|
||||||
|
foreach (GUIComponent box in GUIMessageBox.MessageBoxes.ToImmutableArray())
|
||||||
|
{
|
||||||
|
if (box is not GUIMessageBox msgBox) { continue; }
|
||||||
|
|
||||||
|
if (msgBox.UserData is PromptData or ResultData)
|
||||||
|
{
|
||||||
|
msgBox.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void ClientRead(IReadMessage inc)
|
public static void ClientRead(IReadMessage inc)
|
||||||
{
|
{
|
||||||
ReadyCheckState state = (ReadyCheckState)inc.ReadByte();
|
ReadyCheckState state = (ReadyCheckState)inc.ReadByte();
|
||||||
@@ -154,6 +168,8 @@ namespace Barotrauma
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ReadyCheckState.Start:
|
case ReadyCheckState.Start:
|
||||||
|
CloseLingeringPopups();
|
||||||
|
|
||||||
bool isOwn = false;
|
bool isOwn = false;
|
||||||
byte authorId = 0;
|
byte authorId = 0;
|
||||||
|
|
||||||
@@ -224,7 +240,7 @@ namespace Barotrauma
|
|||||||
if (IsFinished) { return; }
|
if (IsFinished) { return; }
|
||||||
IsFinished = true;
|
IsFinished = true;
|
||||||
|
|
||||||
int readyCount = Clients.Count(pair => pair.Value == ReadyStatus.Yes);
|
int readyCount = Clients.Count(static pair => pair.Value == ReadyStatus.Yes);
|
||||||
int totalCount = Clients.Count;
|
int totalCount = Clients.Count;
|
||||||
GameMain.Client.AddChatMessage(ChatMessage.Create(string.Empty, readyCheckStatus(readyCount, totalCount).Value, ChatMessageType.Server, null));
|
GameMain.Client.AddChatMessage(ChatMessage.Create(string.Empty, readyCheckStatus(readyCount, totalCount).Value, ChatMessageType.Server, null));
|
||||||
}
|
}
|
||||||
@@ -238,31 +254,29 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (resultsBox == null || resultsBox.Closed || !GUIMessageBox.MessageBoxes.Contains(resultsBox)) { return; }
|
if (resultsBox == null || resultsBox.Closed || !GUIMessageBox.MessageBoxes.Contains(resultsBox)) { return; }
|
||||||
|
|
||||||
if (resultsBox.Content.FindChild(UserListData) is GUIListBox userList)
|
if (resultsBox.Content.FindChild(UserListData) is not GUIListBox userList) { return; }
|
||||||
|
|
||||||
|
// for some reason FindChild doesn't work here?
|
||||||
|
foreach (GUIComponent child in userList.Content.Children)
|
||||||
{
|
{
|
||||||
// for some reason FindChild doesn't work here?
|
if (child.UserData is not byte b || b != id) { continue; }
|
||||||
foreach (GUIComponent child in userList.Content.Children)
|
|
||||||
|
if (child.GetChild<GUILayoutGroup>().FindChild(ReadySpriteData) is not GUIImage image) { continue; }
|
||||||
|
|
||||||
|
string style;
|
||||||
|
switch (status)
|
||||||
{
|
{
|
||||||
if (!(child.UserData is byte b) || b != id) { continue; }
|
case ReadyStatus.Yes:
|
||||||
|
style = "MissionCompletedIcon";
|
||||||
if (child.GetChild<GUILayoutGroup>().FindChild(ReadySpriteData) is GUIImage image)
|
break;
|
||||||
{
|
case ReadyStatus.No:
|
||||||
string style;
|
style = "MissionFailedIcon";
|
||||||
switch (status)
|
break;
|
||||||
{
|
default:
|
||||||
case ReadyStatus.Yes:
|
return;
|
||||||
style = "MissionCompletedIcon";
|
|
||||||
break;
|
|
||||||
case ReadyStatus.No:
|
|
||||||
style = "MissionFailedIcon";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
image.ApplyStyle(GUIStyle.GetComponentStyle(style));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
image.ApplyStyle(GUIStyle.GetComponentStyle(style));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -327,17 +327,20 @@ namespace Barotrauma.Items.Components
|
|||||||
var item1 = c1.GUIComponent.UserData as FabricationRecipe;
|
var item1 = c1.GUIComponent.UserData as FabricationRecipe;
|
||||||
var item2 = c2.GUIComponent.UserData as FabricationRecipe;
|
var item2 = c2.GUIComponent.UserData as FabricationRecipe;
|
||||||
|
|
||||||
int itemPlacement1 = FabricationDegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f ? 0 : -1;
|
int itemPlacement1 = calculatePlacement(item1);
|
||||||
int itemPlacement2 = FabricationDegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f ? 0 : -1;
|
int itemPlacement2 = calculatePlacement(item2);
|
||||||
|
|
||||||
itemPlacement1 += item1.RequiresRecipe && !character.HasRecipeForItem(item1.TargetItem.Identifier) ? -2 : 0;
|
|
||||||
itemPlacement2 += item2.RequiresRecipe && !character.HasRecipeForItem(item2.TargetItem.Identifier) ? -2 : 0;
|
|
||||||
|
|
||||||
if (itemPlacement1 != itemPlacement2)
|
if (itemPlacement1 != itemPlacement2)
|
||||||
{
|
{
|
||||||
return itemPlacement1 > itemPlacement2 ? -1 : 1;
|
return itemPlacement1 > itemPlacement2 ? -1 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int calculatePlacement(FabricationRecipe recipe)
|
||||||
|
{
|
||||||
|
int placement = FabricationDegreeOfSuccess(character, recipe.RequiredSkills) >= 0.5f ? 0 : -1;
|
||||||
|
placement += recipe.RequiresRecipe && !AnyOneHasRecipeForItem(character, recipe.TargetItem) ? -2 : 0;
|
||||||
|
return placement;
|
||||||
|
}
|
||||||
|
|
||||||
return string.Compare(item1.DisplayName.Value, item2.DisplayName.Value);
|
return string.Compare(item1.DisplayName.Value, item2.DisplayName.Value);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -372,7 +375,9 @@ namespace Barotrauma.Items.Components
|
|||||||
AutoScaleHorizontal = true,
|
AutoScaleHorizontal = true,
|
||||||
CanBeFocused = false
|
CanBeFocused = false
|
||||||
};
|
};
|
||||||
var firstRequiresRecipe = itemList.Content.Children.FirstOrDefault(c => c.UserData is FabricationRecipe fabricableItem && (fabricableItem.RequiresRecipe && !character.HasRecipeForItem(fabricableItem.TargetItem.Identifier)));
|
var firstRequiresRecipe = itemList.Content.Children.FirstOrDefault(c =>
|
||||||
|
c.UserData is FabricationRecipe fabricableItem &&
|
||||||
|
fabricableItem.RequiresRecipe && !AnyOneHasRecipeForItem(character, fabricableItem.TargetItem));
|
||||||
if (firstRequiresRecipe != null)
|
if (firstRequiresRecipe != null)
|
||||||
{
|
{
|
||||||
requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform));
|
requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform));
|
||||||
|
|||||||
@@ -144,6 +144,9 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
partial class MiniMap : Powered
|
partial class MiniMap : Powered
|
||||||
{
|
{
|
||||||
|
private Dictionary<Hull, HullData> hullDatas;
|
||||||
|
private DateTime resetDataTime;
|
||||||
|
|
||||||
private GUIFrame submarineContainer;
|
private GUIFrame submarineContainer;
|
||||||
|
|
||||||
private GUIFrame? hullInfoFrame;
|
private GUIFrame? hullInfoFrame;
|
||||||
@@ -226,6 +229,8 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
partial void InitProjSpecific()
|
partial void InitProjSpecific()
|
||||||
{
|
{
|
||||||
|
hullDatas = new Dictionary<Hull, HullData>();
|
||||||
|
|
||||||
SetDefaultMode();
|
SetDefaultMode();
|
||||||
|
|
||||||
noPowerTip = TextManager.Get("SteeringNoPowerTip");
|
noPowerTip = TextManager.Get("SteeringNoPowerTip");
|
||||||
@@ -551,6 +556,34 @@ namespace Barotrauma.Items.Components
|
|||||||
CreateHUD();
|
CreateHUD();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//reset data if we haven't received anything in a while
|
||||||
|
//(so that outdated hull info won't be shown if detectors stop sending signals)
|
||||||
|
if (DateTime.Now > resetDataTime)
|
||||||
|
{
|
||||||
|
foreach (HullData hullData in hullDatas.Values)
|
||||||
|
{
|
||||||
|
if (!hullData.Distort)
|
||||||
|
{
|
||||||
|
if (Timing.TotalTime > hullData.LastOxygenDataTime + 1.0) { hullData.ReceivedOxygenAmount = null; }
|
||||||
|
if (Timing.TotalTime > hullData.LastWaterDataTime + 1.0) { hullData.ReceivedWaterAmount = null; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resetDataTime = DateTime.Now + new TimeSpan(0, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cardRefreshTimer > cardRefreshDelay)
|
||||||
|
{
|
||||||
|
if (item.Submarine is { } sub)
|
||||||
|
{
|
||||||
|
UpdateIDCards(sub);
|
||||||
|
}
|
||||||
|
cardRefreshTimer = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cardRefreshTimer += deltaTime;
|
||||||
|
}
|
||||||
|
|
||||||
if (scissorComponent != null)
|
if (scissorComponent != null)
|
||||||
{
|
{
|
||||||
if (PlayerInput.PrimaryMouseButtonDown() && currentMode != MiniMapMode.HullStatus)
|
if (PlayerInput.PrimaryMouseButtonDown() && currentMode != MiniMapMode.HullStatus)
|
||||||
@@ -1736,6 +1769,67 @@ namespace Barotrauma.Items.Components
|
|||||||
return new MiniMapHullData(scaledPolygon, worldRect, parentRect.Size, snappedRectangles, hullRefs.ToImmutableArray());
|
return new MiniMapHullData(scaledPolygon, worldRect, parentRect.Size, snappedRectangles, hullRefs.ToImmutableArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||||
|
{
|
||||||
|
Item source = signal.source;
|
||||||
|
if (source == null || source.CurrentHull == null) { return; }
|
||||||
|
|
||||||
|
Hull sourceHull = source.CurrentHull;
|
||||||
|
if (!hullDatas.TryGetValue(sourceHull, out HullData? hullData))
|
||||||
|
{
|
||||||
|
hullData = new HullData();
|
||||||
|
hullDatas.Add(sourceHull, hullData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hullData.Distort) { return; }
|
||||||
|
|
||||||
|
switch (connection.Name)
|
||||||
|
{
|
||||||
|
case "water_data_in":
|
||||||
|
//cheating a bit because water detectors don't actually send the water level
|
||||||
|
bool fromWaterDetector = source.GetComponent<WaterDetector>() != null;
|
||||||
|
hullData.ReceivedWaterAmount = null;
|
||||||
|
hullData.LastWaterDataTime = Timing.TotalTime;
|
||||||
|
if (fromWaterDetector)
|
||||||
|
{
|
||||||
|
hullData.ReceivedWaterAmount = WaterDetector.GetWaterPercentage(sourceHull);
|
||||||
|
}
|
||||||
|
foreach (var linked in sourceHull.linkedTo)
|
||||||
|
{
|
||||||
|
if (linked is not Hull linkedHull) { continue; }
|
||||||
|
if (!hullDatas.TryGetValue(linkedHull, out HullData? linkedHullData))
|
||||||
|
{
|
||||||
|
linkedHullData = new HullData();
|
||||||
|
hullDatas.Add(linkedHull, linkedHullData);
|
||||||
|
}
|
||||||
|
linkedHullData.ReceivedWaterAmount = null;
|
||||||
|
if (fromWaterDetector)
|
||||||
|
{
|
||||||
|
linkedHullData.ReceivedWaterAmount = WaterDetector.GetWaterPercentage(linkedHull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "oxygen_data_in":
|
||||||
|
if (!float.TryParse(signal.value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float oxy))
|
||||||
|
{
|
||||||
|
oxy = Rand.Range(0.0f, 100.0f);
|
||||||
|
}
|
||||||
|
hullData.ReceivedOxygenAmount = oxy;
|
||||||
|
hullData.LastOxygenDataTime = Timing.TotalTime;
|
||||||
|
foreach (var linked in sourceHull.linkedTo)
|
||||||
|
{
|
||||||
|
if (linked is not Hull linkedHull) { continue; }
|
||||||
|
if (!hullDatas.TryGetValue(linkedHull, out HullData? linkedHullData))
|
||||||
|
{
|
||||||
|
linkedHullData = new HullData();
|
||||||
|
hullDatas.Add(linkedHull, linkedHullData);
|
||||||
|
}
|
||||||
|
linkedHullData.ReceivedOxygenAmount = oxy;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override void RemoveComponentSpecific()
|
protected override void RemoveComponentSpecific()
|
||||||
{
|
{
|
||||||
base.RemoveComponentSpecific();
|
base.RemoveComponentSpecific();
|
||||||
|
|||||||
@@ -396,7 +396,9 @@ namespace Barotrauma.Items.Components
|
|||||||
ToolTip = TextManager.Get("reactor.temperatureboostup"),
|
ToolTip = TextManager.Get("reactor.temperatureboostup"),
|
||||||
OnClicked = (_, __) =>
|
OnClicked = (_, __) =>
|
||||||
{
|
{
|
||||||
applyTemperatureBoost(TemperatureBoostAmount, temperatureBoostSoundUp);
|
unsentChanges = true;
|
||||||
|
sendUpdateTimer = 0.0f;
|
||||||
|
ApplyTemperatureBoost(TemperatureBoostAmount);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -407,25 +409,13 @@ namespace Barotrauma.Items.Components
|
|||||||
ToolTip = TextManager.Get("reactor.temperatureboostdown"),
|
ToolTip = TextManager.Get("reactor.temperatureboostdown"),
|
||||||
OnClicked = (_, __) =>
|
OnClicked = (_, __) =>
|
||||||
{
|
{
|
||||||
applyTemperatureBoost(-TemperatureBoostAmount, temperatureBoostSoundDown);
|
unsentChanges = true;
|
||||||
|
sendUpdateTimer = 0.0f;
|
||||||
|
ApplyTemperatureBoost(-TemperatureBoostAmount);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void applyTemperatureBoost(float amount, RoundSound sound)
|
|
||||||
{
|
|
||||||
temperatureBoost = amount;
|
|
||||||
if (sound != null)
|
|
||||||
{
|
|
||||||
SoundPlayer.PlaySound(
|
|
||||||
sound.Sound,
|
|
||||||
item.WorldPosition,
|
|
||||||
sound.Volume,
|
|
||||||
sound.Range,
|
|
||||||
hullGuess: item.CurrentHull);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var graphArea = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), bottomRightArea.RectTransform))
|
var graphArea = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 1.0f), bottomRightArea.RectTransform))
|
||||||
{
|
{
|
||||||
Stretch = true,
|
Stretch = true,
|
||||||
@@ -471,6 +461,24 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
private void ApplyTemperatureBoost(float amount)
|
||||||
|
{
|
||||||
|
if (Math.Abs(temperatureBoost) <= TemperatureBoostAmount * 0.9f &&
|
||||||
|
Math.Abs(amount) > TemperatureBoostAmount * 0.9f)
|
||||||
|
{
|
||||||
|
var sound = amount > 0 ? temperatureBoostSoundUp : temperatureBoostSoundDown;
|
||||||
|
if (sound != null)
|
||||||
|
{
|
||||||
|
SoundPlayer.PlaySound(
|
||||||
|
sound.Sound,
|
||||||
|
item.WorldPosition,
|
||||||
|
sound.Volume,
|
||||||
|
sound.Range,
|
||||||
|
hullGuess: item.CurrentHull);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
temperatureBoost = amount;
|
||||||
|
}
|
||||||
|
|
||||||
private void InitInventoryUI()
|
private void InitInventoryUI()
|
||||||
{
|
{
|
||||||
@@ -895,6 +903,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.WriteBoolean(PowerOn);
|
msg.WriteBoolean(PowerOn);
|
||||||
msg.WriteRangedSingle(TargetFissionRate, 0.0f, 100.0f, 8);
|
msg.WriteRangedSingle(TargetFissionRate, 0.0f, 100.0f, 8);
|
||||||
msg.WriteRangedSingle(TargetTurbineOutput, 0.0f, 100.0f, 8);
|
msg.WriteRangedSingle(TargetTurbineOutput, 0.0f, 100.0f, 8);
|
||||||
|
msg.WriteRangedSingle(temperatureBoost, -TemperatureBoostAmount, TemperatureBoostAmount, 8);
|
||||||
|
|
||||||
correctionTimer = CorrectionDelay;
|
correctionTimer = CorrectionDelay;
|
||||||
}
|
}
|
||||||
@@ -903,7 +912,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
if (correctionTimer > 0.0f)
|
if (correctionTimer > 0.0f)
|
||||||
{
|
{
|
||||||
StartDelayedCorrection(msg.ExtractBits(1 + 1 + 8 + 8 + 8 + 8), sendingTime);
|
StartDelayedCorrection(msg.ExtractBits(1 + 1 + 8 + 8 + 8 + 8 + 8), sendingTime);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -913,6 +922,7 @@ namespace Barotrauma.Items.Components
|
|||||||
TargetFissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
TargetFissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||||
TargetTurbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
TargetTurbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||||
degreeOfSuccess = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
degreeOfSuccess = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||||
|
ApplyTemperatureBoost(msg.ReadRangedSingle(-TemperatureBoostAmount, TemperatureBoostAmount, 8));
|
||||||
|
|
||||||
if (Math.Abs(FissionRateScrollBar.BarScroll - TargetFissionRate / 100.0f) > 0.01f)
|
if (Math.Abs(FissionRateScrollBar.BarScroll - TargetFissionRate / 100.0f) > 0.01f)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ namespace Barotrauma.Items.Components
|
|||||||
private float displayBorderSize;
|
private float displayBorderSize;
|
||||||
|
|
||||||
private List<SonarBlip> sonarBlips;
|
private List<SonarBlip> sonarBlips;
|
||||||
|
private readonly HashSet<SonarBlip> prevBlips = new HashSet<SonarBlip>();
|
||||||
|
|
||||||
private float prevPassivePingRadius;
|
private float prevPassivePingRadius;
|
||||||
|
|
||||||
@@ -812,24 +813,33 @@ namespace Barotrauma.Items.Components
|
|||||||
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
|
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
|
||||||
|
|
||||||
float dist = (float)Math.Sqrt(distSqr);
|
float dist = (float)Math.Sqrt(distSqr);
|
||||||
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500 && t.IsWithinSector(transducerCenter))
|
if (dist > prevPassivePingRadius * Range && dist <= passivePingRadius * Range && Rand.Int(sonarBlips.Count) < 500)
|
||||||
{
|
{
|
||||||
int prevBlipCount = sonarBlips.Count;
|
prevBlips.Clear();
|
||||||
|
foreach (var blip in sonarBlips)
|
||||||
|
{
|
||||||
|
prevBlips.Add(blip);
|
||||||
|
}
|
||||||
|
|
||||||
Ping(t.WorldPosition, transducerCenter,
|
Ping(t.WorldPosition, transducerCenter,
|
||||||
Math.Min(t.SoundRange, range * 0.5f) * displayScale, 0, displayScale, Math.Min(t.SoundRange, range * 0.5f),
|
t.SoundRange * displayScale, 0, displayScale, range,
|
||||||
passive: true, pingStrength: 0.5f);
|
passive: true, pingStrength: 0.5f);
|
||||||
sonarBlips.Add(new SonarBlip(t.WorldPosition, 1.0f, 1.0f));
|
|
||||||
//remove blips that weren't in the AITarget's sector
|
//remove blips that weren't in the AITarget's sector
|
||||||
if (t.HasSector())
|
if (t.HasSector())
|
||||||
{
|
{
|
||||||
for (int i = sonarBlips.Count - 1; i >= prevBlipCount; i--)
|
for (int i = sonarBlips.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
|
if (prevBlips.Contains(sonarBlips[i])) { continue; }
|
||||||
if (!t.IsWithinSector(sonarBlips[i].Position))
|
if (!t.IsWithinSector(sonarBlips[i].Position))
|
||||||
{
|
{
|
||||||
sonarBlips.RemoveAt(i);
|
sonarBlips.RemoveAt(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (t.IsWithinSector(transducerCenter))
|
||||||
|
{
|
||||||
|
sonarBlips.Add(new SonarBlip(t.WorldPosition, fadeTimer: 1.0f, scale: MathHelper.Clamp(t.SoundRange / 2000, 1.0f, 5.0f)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,7 +97,7 @@ namespace Barotrauma.Items.Components
|
|||||||
var chargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), chargeTextContainer.RectTransform, Anchor.CenterRight),
|
var chargeText = new GUITextBlock(new RectTransform(new Vector2(0.6f, 1), chargeTextContainer.RectTransform, Anchor.CenterRight),
|
||||||
"", textColor: GUIStyle.TextColorNormal, font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
|
"", textColor: GUIStyle.TextColorNormal, font: GUIStyle.Font, textAlignment: Alignment.CenterRight)
|
||||||
{
|
{
|
||||||
TextGetter = () => $"{(int)MathF.Round(charge)}/{(int)capacity} {kWmin} ({(int)MathF.Round(MathUtils.Percentage(charge, capacity))} %)"
|
TextGetter = () => $"{(int)MathF.Round(charge)}/{(int)adjustedCapacity} {kWmin} ({(int)MathF.Round(MathUtils.Percentage(charge, adjustedCapacity))} %)"
|
||||||
};
|
};
|
||||||
if (chargeText.TextSize.X > chargeText.Rect.Width) { chargeText.Font = GUIStyle.SmallFont; }
|
if (chargeText.TextSize.X > chargeText.Rect.Width) { chargeText.Font = GUIStyle.SmallFont; }
|
||||||
|
|
||||||
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
ProgressGetter = () =>
|
ProgressGetter = () =>
|
||||||
{
|
{
|
||||||
return capacity <= 0.0f ? 1.0f : charge / capacity;
|
return adjustedCapacity <= 0.0f ? 1.0f : charge / adjustedCapacity;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
if (chargeIndicator != null)
|
if (chargeIndicator != null)
|
||||||
{
|
{
|
||||||
float chargeRatio = charge / capacity;
|
float chargeRatio = charge / adjustedCapacity;
|
||||||
chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
|
chargeIndicator.Color = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,9 +144,9 @@ namespace Barotrauma.Items.Components
|
|||||||
Matrix rotate = Matrix.CreateRotationZ(item.RotationRad);
|
Matrix rotate = Matrix.CreateRotationZ(item.RotationRad);
|
||||||
Vector2 center = Vector2.Transform((indicatorPos + (scaledIndicatorSize * 0.5f)) * flip, rotate) + itemPosition;
|
Vector2 center = Vector2.Transform((indicatorPos + (scaledIndicatorSize * 0.5f)) * flip, rotate) + itemPosition;
|
||||||
|
|
||||||
if (charge > 0 && capacity > 0)
|
if (charge > 0 && adjustedCapacity > 0)
|
||||||
{
|
{
|
||||||
float chargeRatio = MathHelper.Clamp(charge / capacity, 0.0f, 1.0f);
|
float chargeRatio = MathHelper.Clamp(charge / adjustedCapacity, 0.0f, 1.0f);
|
||||||
Color indicatorColor = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
|
Color indicatorColor = ToolBox.GradientLerp(chargeRatio, Color.Red, Color.Orange, Color.Green);
|
||||||
Vector2 indicatorCenter = (indicatorPos + (scaledIndicatorSize * 0.5f)) * flip;
|
Vector2 indicatorCenter = (indicatorPos + (scaledIndicatorSize * 0.5f)) * flip;
|
||||||
Vector2 indicatorSize;
|
Vector2 indicatorSize;
|
||||||
@@ -193,7 +193,7 @@ namespace Barotrauma.Items.Components
|
|||||||
rechargeSpeedSlider.BarScroll = rechargeRate;
|
rechargeSpeedSlider.BarScroll = rechargeRate;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Charge = msg.ReadRangedSingle(0.0f, 1.0f, 8) * capacity;
|
Charge = msg.ReadRangedSingle(0.0f, 1.0f, 8) * adjustedCapacity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1608,28 +1608,79 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
containedState = item.Condition / item.MaxCondition;
|
containedState = item.Condition / item.MaxCondition;
|
||||||
}
|
}
|
||||||
else if (itemContainer.ShowTotalStackCapacityInContainedStateIndicator)
|
|
||||||
{
|
|
||||||
int ignoredItems = itemContainer.AllSubContainableItems == null ? 0 : itemContainer.AllSubContainableItems.Count;
|
|
||||||
int itemCount = itemContainer.Inventory.AllItems.Count() - ignoredItems;
|
|
||||||
containedState = itemCount / (float)(itemContainer.GetMaxStackSize(0) * itemContainer.MainContainerCapacity);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
int targetSlot = Math.Max(itemContainer.ContainedStateIndicatorSlot, 0);
|
int targetSlot = Math.Max(itemContainer.ContainedStateIndicatorSlot, 0);
|
||||||
var containedItem = itemContainer.Inventory.slots[targetSlot].FirstOrDefault();
|
ItemSlot containedItemSlot = null;
|
||||||
|
if (targetSlot < itemContainer.Inventory.slots.Length)
|
||||||
containedState = itemContainer.Inventory.Capacity == 1 || itemContainer.ContainedStateIndicatorSlot > -1 ?
|
|
||||||
(containedItem == null ? 0.0f : containedItem.Condition / containedItem.MaxCondition) :
|
|
||||||
itemContainer.Inventory.slots.Count(i => !i.Empty()) / (float)itemContainer.Inventory.capacity;
|
|
||||||
|
|
||||||
if (containedItem != null && (itemContainer.Inventory.Capacity == 1 || itemContainer.HasSubContainers))
|
|
||||||
{
|
{
|
||||||
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(targetSlot));
|
containedItemSlot = itemContainer.Inventory.slots[targetSlot];
|
||||||
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
|
}
|
||||||
|
if (containedItemSlot != null)
|
||||||
|
{
|
||||||
|
Item containedItem = containedItemSlot.FirstOrDefault();
|
||||||
|
if (itemContainer.ShowTotalStackCapacityInContainedStateIndicator)
|
||||||
{
|
{
|
||||||
containedState = itemContainer.Inventory.slots[targetSlot].Items.Count / (float)maxStackSize;
|
if (containedItem == null)
|
||||||
|
{
|
||||||
|
// No item on the defined slot, check if the items on other slots can be used.
|
||||||
|
containedItem = containedItemSlot.FirstOrDefault() ?? itemContainer.Inventory.AllItems.FirstOrDefault(it => itemContainer.CanBeContained(it, targetSlot));
|
||||||
|
}
|
||||||
|
if (containedItem != null)
|
||||||
|
{
|
||||||
|
int ignoredItemCount = 0;
|
||||||
|
var subContainableItems = itemContainer.AllSubContainableItems;
|
||||||
|
float capacity = itemContainer.GetMaxStackSize(targetSlot);
|
||||||
|
if (subContainableItems != null)
|
||||||
|
{
|
||||||
|
bool useMainContainerCapacity = true;
|
||||||
|
foreach (Item it in itemContainer.Inventory.AllItems)
|
||||||
|
{
|
||||||
|
// Ignore all items in the sub containers.
|
||||||
|
foreach (RelatedItem ri in subContainableItems)
|
||||||
|
{
|
||||||
|
if (ri.MatchesItem(containedItem))
|
||||||
|
{
|
||||||
|
// The target item is in a subcontainer -> inverse the logic.
|
||||||
|
useMainContainerCapacity = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ri.MatchesItem(it))
|
||||||
|
{
|
||||||
|
ignoredItemCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!useMainContainerCapacity) { break; }
|
||||||
|
}
|
||||||
|
if (useMainContainerCapacity)
|
||||||
|
{
|
||||||
|
capacity *= itemContainer.MainContainerCapacity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Ignore all items in the main container.
|
||||||
|
ignoredItemCount = itemContainer.Inventory.AllItems.Count(it => subContainableItems.Any(ri => !ri.MatchesItem(it)));
|
||||||
|
capacity *= itemContainer.Capacity - itemContainer.MainContainerCapacity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int itemCount = itemContainer.Inventory.AllItems.Count() - ignoredItemCount;
|
||||||
|
containedState = Math.Min(itemCount / Math.Max(capacity, 1), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
containedState = itemContainer.Inventory.Capacity == 1 || itemContainer.ContainedStateIndicatorSlot > -1 ?
|
||||||
|
(containedItem == null ? 0.0f : containedItem.Condition / containedItem.MaxCondition) :
|
||||||
|
itemContainer.Inventory.slots.Count(i => !i.Empty()) / (float)itemContainer.Inventory.capacity;
|
||||||
|
|
||||||
|
if (containedItem != null && (itemContainer.Inventory.Capacity == 1 || itemContainer.HasSubContainers))
|
||||||
|
{
|
||||||
|
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(targetSlot));
|
||||||
|
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
|
||||||
|
{
|
||||||
|
containedState = containedItemSlot.Items.Count / (float)maxStackSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1251,11 +1251,9 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (ItemComponent ic in components)
|
foreach (ItemComponent ic in components)
|
||||||
{
|
{
|
||||||
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
|
|
||||||
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
|
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
|
||||||
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
|
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
|
||||||
if (ic is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { continue; }
|
if (ic is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { continue; }
|
||||||
|
|
||||||
Color color = Color.Gray;
|
Color color = Color.Gray;
|
||||||
if (ic.HasRequiredItems(character, false))
|
if (ic.HasRequiredItems(character, false))
|
||||||
{
|
{
|
||||||
@@ -1268,6 +1266,7 @@ namespace Barotrauma
|
|||||||
color = Color.Cyan;
|
color = Color.Cyan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
|
||||||
texts.Add(new ColoredText(ic.DisplayMsg.Value, color, false, false));
|
texts.Add(new ColoredText(ic.DisplayMsg.Value, color, false, false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO.Pipes;
|
using System.IO.Pipes;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Barotrauma.Networking
|
namespace Barotrauma.Networking
|
||||||
{
|
{
|
||||||
static partial class ChildServerRelay
|
static partial class ChildServerRelay
|
||||||
{
|
{
|
||||||
public static Process Process;
|
public static Process Process;
|
||||||
|
public static bool IsProcessAlive => Process is { HasExited: false };
|
||||||
|
|
||||||
private static bool localHandlesDisposed;
|
private static bool localHandlesDisposed;
|
||||||
private static AnonymousPipeServerStream writePipe;
|
private static AnonymousPipeServerStream writePipe;
|
||||||
private static AnonymousPipeServerStream readPipe;
|
private static AnonymousPipeServerStream readPipe;
|
||||||
@@ -44,18 +47,27 @@ namespace Barotrauma.Networking
|
|||||||
localHandlesDisposed = true;
|
localHandlesDisposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ClosePipes()
|
public static void AttemptGracefulShutDown(int maxAttempts = 20)
|
||||||
{
|
{
|
||||||
writePipe?.Dispose(); writePipe = null;
|
status = StatusEnum.RequestedShutDown;
|
||||||
readPipe?.Dispose(); readPipe = null;
|
writeManualResetEvent?.Set();
|
||||||
shutDown = true;
|
int checks = 0;
|
||||||
|
while (Process is { HasExited: false })
|
||||||
|
{
|
||||||
|
if (checks >= maxAttempts)
|
||||||
|
{
|
||||||
|
DebugConsole.AddWarning("Server could not be shut down gracefully");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Thread.Sleep(100);
|
||||||
|
checks++;
|
||||||
|
}
|
||||||
|
ForceShutDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ShutDown()
|
public static void ForceShutDown()
|
||||||
{
|
{
|
||||||
Process?.Kill(); Process = null;
|
Process?.Kill(); Process = null;
|
||||||
writePipe = null; readPipe = null;
|
|
||||||
|
|
||||||
PrivateShutDown();
|
PrivateShutDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -332,10 +332,27 @@ namespace Barotrauma.Networking
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void CreateServerCrashMessage()
|
||||||
|
{
|
||||||
|
// Close any message boxes that say "The server has crashed."
|
||||||
|
var basicServerCrashMsg = TextManager.Get($"{nameof(DisconnectReason)}.{nameof(DisconnectReason.ServerCrashed)}");
|
||||||
|
GUIMessageBox.MessageBoxes
|
||||||
|
.OfType<GUIMessageBox>()
|
||||||
|
.Where(mb => mb.Text?.Text == basicServerCrashMsg)
|
||||||
|
.ToArray()
|
||||||
|
.ForEach(mb => mb.Close());
|
||||||
|
|
||||||
|
// Open a new message box with the crash report path
|
||||||
|
if (GUIMessageBox.MessageBoxes.All(
|
||||||
|
mb => (mb as GUIMessageBox)?.Text?.Text != ChildServerRelay.CrashMessage))
|
||||||
|
{
|
||||||
|
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||||
|
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool ReturnToPreviousMenu(GUIButton button, object obj)
|
private bool ReturnToPreviousMenu(GUIButton button, object obj)
|
||||||
{
|
{
|
||||||
Quit();
|
|
||||||
|
|
||||||
Submarine.Unload();
|
Submarine.Unload();
|
||||||
GameMain.Client = null;
|
GameMain.Client = null;
|
||||||
GameMain.GameSession = null;
|
GameMain.GameSession = null;
|
||||||
@@ -531,14 +548,10 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
if (GameMain.WindowActive)
|
if (GameMain.WindowActive)
|
||||||
{
|
{
|
||||||
if (ChildServerRelay.Process?.HasExited ?? true)
|
if (!ChildServerRelay.IsProcessAlive)
|
||||||
{
|
{
|
||||||
Quit();
|
Quit();
|
||||||
if (!GUIMessageBox.MessageBoxes.Any(mb => (mb as GUIMessageBox)?.Text?.Text == ChildServerRelay.CrashMessage))
|
CreateServerCrashMessage();
|
||||||
{
|
|
||||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
|
||||||
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -942,13 +955,6 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
GUI.ClearCursorWait();
|
GUI.ClearCursorWait();
|
||||||
|
|
||||||
ChildServerRelay.ShutDown();
|
|
||||||
|
|
||||||
if (SteamManager.IsInitialized)
|
|
||||||
{
|
|
||||||
Steamworks.SteamFriends.ClearRichPresence();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disconnectPacket.ShouldCreateAnalyticsEvent)
|
if (disconnectPacket.ShouldCreateAnalyticsEvent)
|
||||||
{
|
{
|
||||||
GameAnalyticsManager.AddErrorEventOnce(
|
GameAnalyticsManager.AddErrorEventOnce(
|
||||||
@@ -974,11 +980,43 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ReturnToPreviousMenu(null, null);
|
if (ClientPeer is SteamP2PClientPeer or SteamP2POwnerPeer)
|
||||||
new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage)
|
|
||||||
{
|
{
|
||||||
DisplayInLoadingScreens = true
|
SteamManager.LeaveLobby();
|
||||||
};
|
}
|
||||||
|
|
||||||
|
GameMain.ModDownloadScreen.Reset();
|
||||||
|
ContentPackageManager.EnabledPackages.Restore();
|
||||||
|
|
||||||
|
CampaignMode.StartRoundCancellationToken?.Cancel();
|
||||||
|
|
||||||
|
if (SteamManager.IsInitialized)
|
||||||
|
{
|
||||||
|
Steamworks.SteamFriends.ClearRichPresence();
|
||||||
|
}
|
||||||
|
foreach (var fileTransfer in FileReceiver.ActiveTransfers.ToArray())
|
||||||
|
{
|
||||||
|
FileReceiver.StopTransfer(fileTransfer, deleteFile: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChildServerRelay.AttemptGracefulShutDown();
|
||||||
|
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
|
||||||
|
|
||||||
|
characterInfo?.Remove();
|
||||||
|
|
||||||
|
VoipClient?.Dispose();
|
||||||
|
VoipClient = null;
|
||||||
|
GameMain.Client = null;
|
||||||
|
GameMain.GameSession = null;
|
||||||
|
|
||||||
|
ReturnToPreviousMenu(null, null);
|
||||||
|
if (disconnectPacket.DisconnectReason != DisconnectReason.Disconnected)
|
||||||
|
{
|
||||||
|
new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage)
|
||||||
|
{
|
||||||
|
DisplayInLoadingScreens = true
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2538,46 +2576,9 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
public void Quit()
|
public void Quit()
|
||||||
{
|
{
|
||||||
if (ClientPeer is SteamP2PClientPeer || ClientPeer is SteamP2POwnerPeer)
|
|
||||||
{
|
|
||||||
SteamManager.LeaveLobby();
|
|
||||||
}
|
|
||||||
|
|
||||||
GameMain.ModDownloadScreen.Reset();
|
|
||||||
ContentPackageManager.EnabledPackages.Restore();
|
|
||||||
|
|
||||||
CampaignMode.StartRoundCancellationToken?.Cancel();
|
|
||||||
|
|
||||||
ClientPeer?.Close(PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
|
ClientPeer?.Close(PeerDisconnectPacket.WithReason(DisconnectReason.Disconnected));
|
||||||
ClientPeer = null;
|
|
||||||
|
|
||||||
foreach (var fileTransfer in FileReceiver.ActiveTransfers.ToArray())
|
|
||||||
{
|
|
||||||
FileReceiver.StopTransfer(fileTransfer, deleteFile: true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ChildServerRelay.Process != null)
|
|
||||||
{
|
|
||||||
int checks = 0;
|
|
||||||
while (ChildServerRelay.Process is { HasExited: false })
|
|
||||||
{
|
|
||||||
if (checks > 10)
|
|
||||||
{
|
|
||||||
ChildServerRelay.ShutDown();
|
|
||||||
}
|
|
||||||
Thread.Sleep(100);
|
|
||||||
checks++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ChildServerRelay.ShutDown();
|
|
||||||
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
|
GUIMessageBox.MessageBoxes.RemoveAll(c => c?.UserData is RoundSummary);
|
||||||
|
|
||||||
characterInfo?.Remove();
|
|
||||||
|
|
||||||
VoipClient?.Dispose();
|
|
||||||
VoipClient = null;
|
|
||||||
GameMain.Client = null;
|
|
||||||
GameMain.GameSession = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SendCharacterInfo(string newName = null)
|
public void SendCharacterInfo(string newName = null)
|
||||||
|
|||||||
+3
-7
@@ -91,15 +91,11 @@ namespace Barotrauma.Networking
|
|||||||
ToolBox.ThrowIfNull(netClient);
|
ToolBox.ThrowIfNull(netClient);
|
||||||
ToolBox.ThrowIfNull(incomingLidgrenMessages);
|
ToolBox.ThrowIfNull(incomingLidgrenMessages);
|
||||||
|
|
||||||
if (isOwner && !(ChildServerRelay.Process is { HasExited: false }))
|
if (isOwner && !ChildServerRelay.IsProcessAlive)
|
||||||
{
|
{
|
||||||
|
var gameClient = GameMain.Client;
|
||||||
Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
|
Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
|
||||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
gameClient?.CreateServerCrashMessage();
|
||||||
msgBox.Buttons[0].OnClicked += (btn, obj) =>
|
|
||||||
{
|
|
||||||
GameMain.MainMenuScreen.Select();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-9
@@ -187,15 +187,11 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
if (!isActive) { return; }
|
if (!isActive) { return; }
|
||||||
|
|
||||||
if (ChildServerRelay.HasShutDown || !(ChildServerRelay.Process is { HasExited: false }))
|
if (ChildServerRelay.HasShutDown || !ChildServerRelay.IsProcessAlive)
|
||||||
{
|
{
|
||||||
|
var gameClient = GameMain.Client;
|
||||||
Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
|
Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
|
||||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
gameClient?.CreateServerCrashMessage();
|
||||||
msgBox.Buttons[0].OnClicked += (btn, obj) =>
|
|
||||||
{
|
|
||||||
GameMain.MainMenuScreen.Select();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,8 +397,6 @@ namespace Barotrauma.Networking
|
|||||||
ClosePeerSession(remotePeers[i]);
|
ClosePeerSession(remotePeers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChildServerRelay.ClosePipes();
|
|
||||||
|
|
||||||
callbacks.OnDisconnect.Invoke(peerDisconnectPacket);
|
callbacks.OnDisconnect.Invoke(peerDisconnectPacket);
|
||||||
|
|
||||||
SteamManager.LeaveLobby();
|
SteamManager.LeaveLobby();
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ namespace Barotrauma.Networking
|
|||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IReadOnlyList<string> CaptureDeviceNames =>
|
|
||||||
Alc.GetStringList(IntPtr.Zero, OpenAL.Alc.CaptureDeviceSpecifier);
|
|
||||||
|
|
||||||
private readonly IntPtr captureDevice;
|
private readonly IntPtr captureDevice;
|
||||||
|
|
||||||
@@ -169,6 +167,11 @@ namespace Barotrauma.Networking
|
|||||||
Create(GameSettings.CurrentConfig.Audio.VoiceCaptureDevice, storedBufferID);
|
Create(GameSettings.CurrentConfig.Audio.VoiceCaptureDevice, storedBufferID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IReadOnlyList<string> GetCaptureDeviceNames()
|
||||||
|
{
|
||||||
|
return Alc.GetStringList(IntPtr.Zero, OpenAL.Alc.CaptureDeviceSpecifier);
|
||||||
|
}
|
||||||
|
|
||||||
IntPtr nativeBuffer;
|
IntPtr nativeBuffer;
|
||||||
readonly short[] uncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
readonly short[] uncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||||
readonly short[] prevUncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
readonly short[] prevUncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||||
|
|||||||
@@ -10,8 +10,6 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
partial class GameScreen : Screen
|
partial class GameScreen : Screen
|
||||||
{
|
{
|
||||||
public override bool IsEditor => GameMain.GameSession?.GameMode is TestGameMode;
|
|
||||||
|
|
||||||
private RenderTarget2D renderTargetBackground;
|
private RenderTarget2D renderTargetBackground;
|
||||||
private RenderTarget2D renderTarget;
|
private RenderTarget2D renderTarget;
|
||||||
private RenderTarget2D renderTargetWater;
|
private RenderTarget2D renderTargetWater;
|
||||||
|
|||||||
@@ -47,7 +47,14 @@ namespace Barotrauma
|
|||||||
private GUITextBox serverNameBox, passwordBox, maxPlayersBox;
|
private GUITextBox serverNameBox, passwordBox, maxPlayersBox;
|
||||||
private GUITickBox isPublicBox, wrongPasswordBanBox, karmaBox;
|
private GUITickBox isPublicBox, wrongPasswordBanBox, karmaBox;
|
||||||
private GUIDropDown serverExecutableDropdown;
|
private GUIDropDown serverExecutableDropdown;
|
||||||
private readonly GUIButton joinServerButton, hostServerButton, steamWorkshopButton;
|
private readonly GUIButton joinServerButton, hostServerButton;
|
||||||
|
|
||||||
|
private readonly GUIFrame modsButtonContainer;
|
||||||
|
private readonly GUIButton modsButton, modUpdatesButton;
|
||||||
|
private Task<IReadOnlyList<Steamworks.Ugc.Item>> modUpdateTask;
|
||||||
|
private float modUpdateTimer = 0.0f;
|
||||||
|
private const float ModUpdateInterval = 60.0f;
|
||||||
|
|
||||||
private readonly GameMain game;
|
private readonly GameMain game;
|
||||||
|
|
||||||
private GUIImage playstyleBanner;
|
private GUIImage playstyleBanner;
|
||||||
@@ -268,15 +275,29 @@ namespace Barotrauma
|
|||||||
RelativeSpacing = 0.035f
|
RelativeSpacing = 0.035f
|
||||||
};
|
};
|
||||||
|
|
||||||
#if USE_STEAM
|
modsButtonContainer = new GUIFrame(new RectTransform(Vector2.One, customizeList.RectTransform),
|
||||||
steamWorkshopButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("settingstab.mods"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
style: null);
|
||||||
|
|
||||||
|
modsButton = new GUIButton(new RectTransform(Vector2.One, modsButtonContainer.RectTransform),
|
||||||
|
TextManager.Get("settingstab.mods"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
||||||
{
|
{
|
||||||
ForceUpperCase = ForceUpperCase.Yes,
|
ForceUpperCase = ForceUpperCase.Yes,
|
||||||
Enabled = true,
|
Enabled = true,
|
||||||
UserData = Tab.SteamWorkshop,
|
UserData = Tab.SteamWorkshop,
|
||||||
OnClicked = SelectTab
|
OnClicked = SelectTab
|
||||||
};
|
};
|
||||||
#endif
|
|
||||||
|
modUpdatesButton = new GUIButton(new RectTransform(Vector2.One * 0.95f, modsButtonContainer.RectTransform, scaleBasis: ScaleBasis.BothHeight),
|
||||||
|
style: "GUIUpdateButton")
|
||||||
|
{
|
||||||
|
ToolTip = TextManager.Get("ModUpdatesAvailable"),
|
||||||
|
OnClicked = (_, _) =>
|
||||||
|
{
|
||||||
|
BulkDownloader.PrepareUpdates();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
Visible = false
|
||||||
|
};
|
||||||
|
|
||||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("SubEditorButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("SubEditorButton"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
||||||
{
|
{
|
||||||
@@ -525,6 +546,8 @@ namespace Barotrauma
|
|||||||
#region Selection
|
#region Selection
|
||||||
public override void Select()
|
public override void Select()
|
||||||
{
|
{
|
||||||
|
ResetModUpdateButton();
|
||||||
|
|
||||||
if (WorkshopItemsToUpdate.Any())
|
if (WorkshopItemsToUpdate.Any())
|
||||||
{
|
{
|
||||||
while (WorkshopItemsToUpdate.TryDequeue(out ulong workshopId))
|
while (WorkshopItemsToUpdate.TryDequeue(out ulong workshopId))
|
||||||
@@ -711,6 +734,13 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
public void ResetModUpdateButton()
|
||||||
|
{
|
||||||
|
modUpdateTask = null;
|
||||||
|
modUpdateTimer = 0;
|
||||||
|
modUpdatesButton.Visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
public void QuickStart(bool fixedSeed = false, Identifier sub = default, float difficulty = 50, LevelGenerationParams levelGenerationParams = null)
|
public void QuickStart(bool fixedSeed = false, Identifier sub = default, float difficulty = 50, LevelGenerationParams levelGenerationParams = null)
|
||||||
{
|
{
|
||||||
if (fixedSeed)
|
if (fixedSeed)
|
||||||
@@ -930,15 +960,32 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public override void Update(double deltaTime)
|
public override void Update(double deltaTime)
|
||||||
{
|
{
|
||||||
#if !DEBUG && USE_STEAM
|
modUpdateTimer -= (float)deltaTime;
|
||||||
|
if (modUpdateTimer <= 0.0f && modUpdateTask is not { IsCompleted: false })
|
||||||
|
{
|
||||||
|
modUpdateTask = BulkDownloader.GetItemsThatNeedUpdating();
|
||||||
|
modUpdateTimer = ModUpdateInterval;
|
||||||
|
}
|
||||||
|
|
||||||
if (GameSettings.CurrentConfig.UseSteamMatchmaking)
|
if (GameSettings.CurrentConfig.UseSteamMatchmaking)
|
||||||
{
|
{
|
||||||
hostServerButton.Enabled = Steam.SteamManager.IsInitialized;
|
hostServerButton.Enabled = Steam.SteamManager.IsInitialized;
|
||||||
}
|
}
|
||||||
steamWorkshopButton.Enabled = Steam.SteamManager.IsInitialized;
|
|
||||||
#elif USE_STEAM
|
if (modUpdateTask is { IsCompletedSuccessfully: true })
|
||||||
steamWorkshopButton.Enabled = true;
|
{
|
||||||
#endif
|
modUpdatesButton.Visible = modUpdateTask.Result.Count > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modUpdatesButton.Visible)
|
||||||
|
{
|
||||||
|
var modButtonLabelSize =
|
||||||
|
modsButton.Font.MeasureString(modsButton.Text).ToPoint()
|
||||||
|
+ new Point(GUI.IntScale(25));
|
||||||
|
modUpdatesButton.RectTransform.AbsoluteOffset =
|
||||||
|
(modButtonLabelSize.X, modsButton.Rect.Height / 2 - modUpdatesButton.Rect.Height / 2);
|
||||||
|
}
|
||||||
|
|
||||||
switch (selectedTab)
|
switch (selectedTab)
|
||||||
{
|
{
|
||||||
case Tab.NewGame:
|
case Tab.NewGame:
|
||||||
|
|||||||
@@ -103,6 +103,10 @@ namespace Barotrauma
|
|||||||
public GUIFrame JobPreferenceContainer;
|
public GUIFrame JobPreferenceContainer;
|
||||||
public GUIListBox JobList;
|
public GUIListBox JobList;
|
||||||
|
|
||||||
|
private Identifier micIconStyle;
|
||||||
|
private float micCheckTimer;
|
||||||
|
const float MicCheckInterval = 1.0f;
|
||||||
|
|
||||||
private float autoRestartTimer;
|
private float autoRestartTimer;
|
||||||
|
|
||||||
//persistent characterinfo provided by the server
|
//persistent characterinfo provided by the server
|
||||||
@@ -2656,27 +2660,9 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public override void Update(double deltaTime)
|
public override void Update(double deltaTime)
|
||||||
{
|
{
|
||||||
base.Update(deltaTime);
|
|
||||||
|
|
||||||
if (GameMain.Client == null) { return; }
|
if (GameMain.Client == null) { return; }
|
||||||
|
|
||||||
Identifier currMicStyle = micIcon.Style.Element.NameAsIdentifier();
|
UpdateMicIcon((float)deltaTime);
|
||||||
|
|
||||||
Identifier targetMicStyle = "GUIMicrophoneEnabled".ToIdentifier();
|
|
||||||
var voipCaptureDeviceNames = VoipCapture.CaptureDeviceNames;
|
|
||||||
if (voipCaptureDeviceNames.Count == 0)
|
|
||||||
{
|
|
||||||
targetMicStyle = "GUIMicrophoneUnavailable".ToIdentifier();
|
|
||||||
}
|
|
||||||
else if (GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.Disabled)
|
|
||||||
{
|
|
||||||
targetMicStyle = "GUIMicrophoneDisabled".ToIdentifier();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetMicStyle != currMicStyle)
|
|
||||||
{
|
|
||||||
GUIStyle.Apply(micIcon, targetMicStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (GUIComponent child in PlayerList.Content.Children)
|
foreach (GUIComponent child in PlayerList.Content.Children)
|
||||||
{
|
{
|
||||||
@@ -2738,6 +2724,35 @@ namespace Barotrauma
|
|||||||
if (!mouseRect.Contains(PlayerInput.MousePosition)) { jobVariantTooltip = null; }
|
if (!mouseRect.Contains(PlayerInput.MousePosition)) { jobVariantTooltip = null; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateMicIcon(float deltaTime)
|
||||||
|
{
|
||||||
|
micCheckTimer -= deltaTime;
|
||||||
|
if (micCheckTimer > 0.0f) { return; }
|
||||||
|
|
||||||
|
Identifier newMicIconStyle = "GUIMicrophoneEnabled".ToIdentifier();
|
||||||
|
if (GameSettings.CurrentConfig.Audio.VoiceSetting == VoiceMode.Disabled)
|
||||||
|
{
|
||||||
|
newMicIconStyle = "GUIMicrophoneDisabled".ToIdentifier();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var voipCaptureDeviceNames = VoipCapture.GetCaptureDeviceNames();
|
||||||
|
if (voipCaptureDeviceNames.Count == 0)
|
||||||
|
{
|
||||||
|
newMicIconStyle = "GUIMicrophoneUnavailable".ToIdentifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newMicIconStyle != micIconStyle)
|
||||||
|
{
|
||||||
|
micIconStyle = newMicIconStyle;
|
||||||
|
GUIStyle.Apply(micIcon, newMicIconStyle);
|
||||||
|
}
|
||||||
|
|
||||||
|
micCheckTimer = MicCheckInterval;
|
||||||
|
}
|
||||||
|
|
||||||
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
|
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
|
||||||
{
|
{
|
||||||
graphics.Clear(Color.Black);
|
graphics.Clear(Color.Black);
|
||||||
|
|||||||
@@ -354,9 +354,24 @@ namespace Barotrauma
|
|||||||
ToolTip = RichString.Rich(TextManager.Get("SaveSubButton") + "‖color:125,125,125‖\nCtrl + S‖color:end‖"),
|
ToolTip = RichString.Rich(TextManager.Get("SaveSubButton") + "‖color:125,125,125‖\nCtrl + S‖color:end‖"),
|
||||||
OnClicked = (btn, data) =>
|
OnClicked = (btn, data) =>
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (ContentPackageManager.EnabledPackages.All.Any(cp => cp != ContentPackageManager.VanillaCorePackage && cp.Files.Any(f => f is not BaseSubFile)))
|
||||||
|
{
|
||||||
|
var msgBox = new GUIMessageBox("DEBUG-ONLY WARNING", "You currently have some mods enabled. Are you sure you want to save the submarine? If the mods override any vanilla content, saving the submarine may cause unintended changes.",
|
||||||
|
new LocalizedString[] { "Yes, I know what I'm doing", "Cancel" });
|
||||||
|
msgBox.Buttons[0].OnClicked = (btn, data) =>
|
||||||
|
{
|
||||||
|
msgBox.Close();
|
||||||
|
loadFrame = null;
|
||||||
|
CreateSaveScreen();
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
msgBox.Buttons[1].OnClicked += msgBox.Close;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
loadFrame = null;
|
loadFrame = null;
|
||||||
CreateSaveScreen();
|
CreateSaveScreen();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3096,7 +3111,7 @@ namespace Barotrauma
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SnapToGrid()
|
private static void SnapToGrid()
|
||||||
{
|
{
|
||||||
// First move components
|
// First move components
|
||||||
foreach (MapEntity e in MapEntity.SelectedList)
|
foreach (MapEntity e in MapEntity.SelectedList)
|
||||||
@@ -3109,6 +3124,10 @@ namespace Barotrauma
|
|||||||
var wire = item.GetComponent<Wire>();
|
var wire = item.GetComponent<Wire>();
|
||||||
if (wire != null) { continue; }
|
if (wire != null) { continue; }
|
||||||
item.Move(offset);
|
item.Move(offset);
|
||||||
|
if (item.GetComponent<Door>()?.LinkedGap is Gap linkedGap)
|
||||||
|
{
|
||||||
|
linkedGap.Move(item.Position - linkedGap.Position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (e is Structure structure)
|
else if (e is Structure structure)
|
||||||
{
|
{
|
||||||
@@ -3133,7 +3152,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<SubmarineInfo> GetLoadableSubs()
|
private static IEnumerable<SubmarineInfo> GetLoadableSubs()
|
||||||
{
|
{
|
||||||
string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
|
string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
|
||||||
return SubmarineInfo.SavedSubmarines.Where(s
|
return SubmarineInfo.SavedSubmarines.Where(s
|
||||||
@@ -3520,9 +3539,18 @@ namespace Barotrauma
|
|||||||
public void LoadSub(SubmarineInfo info)
|
public void LoadSub(SubmarineInfo info)
|
||||||
{
|
{
|
||||||
Submarine.Unload();
|
Submarine.Unload();
|
||||||
var selectedSub = new Submarine(info);
|
Submarine selectedSub = null;
|
||||||
MainSub = selectedSub;
|
try
|
||||||
MainSub.UpdateTransform(interpolate: false);
|
{
|
||||||
|
selectedSub = new Submarine(info);
|
||||||
|
MainSub = selectedSub;
|
||||||
|
MainSub.UpdateTransform(interpolate: false);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError("Failed to load the submarine. The submarine file might be corrupted.", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
ClearUndoBuffer();
|
ClearUndoBuffer();
|
||||||
CreateDummyCharacter();
|
CreateDummyCharacter();
|
||||||
|
|
||||||
|
|||||||
@@ -716,8 +716,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (Screen.Selected == null) { return "menu".ToIdentifier(); }
|
if (Screen.Selected == null) { return "menu".ToIdentifier(); }
|
||||||
|
|
||||||
if ((Screen.Selected?.IsEditor ?? false)
|
if (Screen.Selected is { IsEditor: true } || GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected == GameMain.NetLobbyScreen)
|
||||||
|| (Screen.Selected == GameMain.NetLobbyScreen))
|
|
||||||
{
|
{
|
||||||
return "editor".ToIdentifier();
|
return "editor".ToIdentifier();
|
||||||
}
|
}
|
||||||
@@ -814,7 +813,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
return "levelend".ToIdentifier();
|
return "levelend".ToIdentifier();
|
||||||
}
|
}
|
||||||
if (GameMain.GameSession.RoundDuration > 120.0 &&
|
if (GameMain.GameSession.RoundDuration < 120.0 &&
|
||||||
Level.Loaded?.Type == LevelData.LevelType.LocationConnection)
|
Level.Loaded?.Type == LevelData.LevelType.LocationConnection)
|
||||||
{
|
{
|
||||||
return "start".ToIdentifier();
|
return "start".ToIdentifier();
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ namespace Barotrauma.Steam
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IReadOnlyList<Steamworks.Ugc.Item>> GetItemsThatNeedUpdating()
|
public static async Task<IReadOnlyList<Steamworks.Ugc.Item>> GetItemsThatNeedUpdating()
|
||||||
{
|
{
|
||||||
var determiningTasks = ContentPackageManager.WorkshopPackages.Select(async p => (p, await p.IsUpToDate()));
|
var determiningTasks = ContentPackageManager.WorkshopPackages.Select(async p => (p, await p.IsUpToDate()));
|
||||||
(ContentPackage Package, bool IsUpToDate)[] outOfDatePackages = await Task.WhenAll(determiningTasks);
|
(ContentPackage Package, bool IsUpToDate)[] outOfDatePackages = await Task.WhenAll(determiningTasks);
|
||||||
@@ -126,6 +126,7 @@ namespace Barotrauma.Steam
|
|||||||
{
|
{
|
||||||
mutableWorkshopMenu.PopulateInstalledModLists(forceRefreshEnabled: true);
|
mutableWorkshopMenu.PopulateInstalledModLists(forceRefreshEnabled: true);
|
||||||
}
|
}
|
||||||
|
GameMain.MainMenuScreen.ResetModUpdateButton();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.20.8.0</Version>
|
<Version>0.20.9.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</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.20.8.0</Version>
|
<Version>0.20.9.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</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.20.8.0</Version>
|
<Version>0.20.9.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</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.20.8.0</Version>
|
<Version>0.20.9.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</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.20.8.0</Version>
|
<Version>0.20.9.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -18,20 +18,22 @@ namespace Barotrauma.Items.Components
|
|||||||
bool powerOn = msg.ReadBoolean();
|
bool powerOn = msg.ReadBoolean();
|
||||||
float fissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
float fissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||||
float turbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
float turbineOutput = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||||
|
float temperatureBoostAmount = msg.ReadRangedSingle(-TemperatureBoostAmount, TemperatureBoostAmount, 8);
|
||||||
|
|
||||||
if (!item.CanClientAccess(c)) { return; }
|
if (!item.CanClientAccess(c)) { return; }
|
||||||
|
|
||||||
IsActive = true;
|
IsActive = true;
|
||||||
|
|
||||||
if (!autoTemp && AutoTemp) blameOnBroken = c;
|
if (!autoTemp && AutoTemp) { blameOnBroken = c; }
|
||||||
if (turbineOutput < TargetTurbineOutput) blameOnBroken = c;
|
if (turbineOutput < TargetTurbineOutput) { blameOnBroken = c; }
|
||||||
if (fissionRate > TargetFissionRate) blameOnBroken = c;
|
if (fissionRate > TargetFissionRate) { blameOnBroken = c; }
|
||||||
if (!_powerOn && powerOn) blameOnBroken = c;
|
if (!_powerOn && powerOn) { blameOnBroken = c; }
|
||||||
|
|
||||||
AutoTemp = autoTemp;
|
AutoTemp = autoTemp;
|
||||||
_powerOn = powerOn;
|
_powerOn = powerOn;
|
||||||
TargetFissionRate = fissionRate;
|
TargetFissionRate = fissionRate;
|
||||||
TargetTurbineOutput = turbineOutput;
|
TargetTurbineOutput = turbineOutput;
|
||||||
|
if (AllowTemperatureBoost) { temperatureBoost = temperatureBoostAmount; }
|
||||||
|
|
||||||
LastUser = c.Character;
|
LastUser = c.Character;
|
||||||
if (nextServerLogWriteTime == null)
|
if (nextServerLogWriteTime == null)
|
||||||
@@ -51,6 +53,7 @@ namespace Barotrauma.Items.Components
|
|||||||
msg.WriteRangedSingle(TargetFissionRate, 0.0f, 100.0f, 8);
|
msg.WriteRangedSingle(TargetFissionRate, 0.0f, 100.0f, 8);
|
||||||
msg.WriteRangedSingle(TargetTurbineOutput, 0.0f, 100.0f, 8);
|
msg.WriteRangedSingle(TargetTurbineOutput, 0.0f, 100.0f, 8);
|
||||||
msg.WriteRangedSingle(degreeOfSuccess, 0.0f, 1.0f, 8);
|
msg.WriteRangedSingle(degreeOfSuccess, 0.0f, 1.0f, 8);
|
||||||
|
msg.WriteRangedSingle(temperatureBoost, -TemperatureBoostAmount, TemperatureBoostAmount, 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
msg.WriteRangedInteger((int)(rechargeSpeed / MaxRechargeSpeed * 10), 0, 10);
|
msg.WriteRangedInteger((int)(rechargeSpeed / MaxRechargeSpeed * 10), 0, 10);
|
||||||
|
|
||||||
float chargeRatio = MathHelper.Clamp(charge / capacity, 0.0f, 1.0f);
|
float chargeRatio = MathHelper.Clamp(charge / adjustedCapacity, 0.0f, 1.0f);
|
||||||
msg.WriteRangedSingle(chargeRatio, 0.0f, 1.0f, 8);
|
msg.WriteRangedSingle(chargeRatio, 0.0f, 1.0f, 8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -335,9 +335,9 @@ namespace Barotrauma.Networking
|
|||||||
#endif
|
#endif
|
||||||
if (!started) { return; }
|
if (!started) { return; }
|
||||||
|
|
||||||
if (OwnerConnection != null && ChildServerRelay.HasShutDown)
|
if (ChildServerRelay.HasShutDown)
|
||||||
{
|
{
|
||||||
Quit();
|
GameMain.Instance.CloseServer();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3939,6 +3939,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
public void Quit()
|
public void Quit()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (started)
|
if (started)
|
||||||
{
|
{
|
||||||
started = false;
|
started = false;
|
||||||
|
|||||||
@@ -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.20.8.0</Version>
|
<Version>0.20.9.0</Version>
|
||||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||||
<Platforms>AnyCPU;x64</Platforms>
|
<Platforms>AnyCPU;x64</Platforms>
|
||||||
<AssemblyName>DedicatedServer</AssemblyName>
|
<AssemblyName>DedicatedServer</AssemblyName>
|
||||||
|
|||||||
@@ -1838,7 +1838,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
if (selectedTargetingParams.AttackPattern == AttackPattern.Straight && AttackLimb is Limb attackLimb && attackLimb.attack.Ranged)
|
if (selectedTargetingParams.AttackPattern == AttackPattern.Straight && AttackLimb is Limb attackLimb && attackLimb.attack.Ranged)
|
||||||
{
|
{
|
||||||
bool advance = !canAttack && Character.InWater || distance > attackLimb.attack.Range * 0.9f;
|
bool advance = !canAttack && Character.CurrentHull == null || distance > attackLimb.attack.Range * 0.9f;
|
||||||
bool fallBack = canAttack && distance < Math.Min(250, attackLimb.attack.Range * 0.25f);
|
bool fallBack = canAttack && distance < Math.Min(250, attackLimb.attack.Range * 0.25f);
|
||||||
if (fallBack)
|
if (fallBack)
|
||||||
{
|
{
|
||||||
@@ -1856,10 +1856,18 @@ namespace Barotrauma
|
|||||||
SteeringManager.SteeringSeek(steerPos, 10);
|
SteeringManager.SteeringSeek(steerPos, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!Character.InWater)
|
else
|
||||||
{
|
{
|
||||||
SteeringManager.Reset();
|
if (Character.CurrentHull == null && !canAttack)
|
||||||
FaceTarget(SelectedAiTarget.Entity);
|
{
|
||||||
|
SteeringManager.SteeringWander();
|
||||||
|
SteeringManager.SteeringAvoid(deltaTime, lookAheadDistance: avoidLookAheadDistance, weight: 5);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SteeringManager.Reset();
|
||||||
|
FaceTarget(SelectedAiTarget.Entity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!canAttack || distance > Math.Min(AttackLimb.attack.Range * 0.9f, 100))
|
else if (!canAttack || distance > Math.Min(AttackLimb.attack.Range * 0.9f, 100))
|
||||||
@@ -1882,7 +1890,7 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
Entity targetEntity = wallTarget?.Structure ?? SelectedAiTarget?.Entity;
|
Entity targetEntity = wallTarget?.Structure ?? SelectedAiTarget?.Entity;
|
||||||
IDamageable damageTarget = targetEntity as IDamageable;
|
IDamageable damageTarget = targetEntity as IDamageable;
|
||||||
if (AttackLimb?.attack is Attack { Ranged: true} attack && targetEntity != null)
|
if (AttackLimb?.attack is Attack { Ranged: true} attack)
|
||||||
{
|
{
|
||||||
AimRangedAttack(attack, targetEntity);
|
AimRangedAttack(attack, targetEntity);
|
||||||
}
|
}
|
||||||
@@ -1901,7 +1909,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public void AimRangedAttack(Attack attack, Entity targetEntity)
|
public void AimRangedAttack(Attack attack, Entity targetEntity)
|
||||||
{
|
{
|
||||||
if (attack == null || attack.Ranged == false || targetEntity == null) { return; }
|
if (attack is not { Ranged: true } || targetEntity is not { Removed: false }) { return; }
|
||||||
Character.SetInput(InputType.Aim, false, true);
|
Character.SetInput(InputType.Aim, false, true);
|
||||||
Limb limb = GetLimbToRotate(attack);
|
Limb limb = GetLimbToRotate(attack);
|
||||||
if (limb != null)
|
if (limb != null)
|
||||||
|
|||||||
@@ -1907,8 +1907,8 @@ namespace Barotrauma
|
|||||||
visibleHulls = VisibleHulls;
|
visibleHulls = VisibleHulls;
|
||||||
}
|
}
|
||||||
bool ignoreFire = objectiveManager.CurrentOrder is AIObjectiveExtinguishFires extinguishOrder && extinguishOrder.Priority > 0 || objectiveManager.HasActiveObjective<AIObjectiveExtinguishFire>();
|
bool ignoreFire = objectiveManager.CurrentOrder is AIObjectiveExtinguishFires extinguishOrder && extinguishOrder.Priority > 0 || objectiveManager.HasActiveObjective<AIObjectiveExtinguishFire>();
|
||||||
bool ignoreWater = HasDivingSuit(character);
|
bool ignoreWater = character.IsProtectedFromPressure();
|
||||||
bool ignoreOxygen = ignoreWater || HasDivingMask(character);
|
bool ignoreOxygen = HasDivingGear(character);
|
||||||
bool ignoreEnemies = ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() || ObjectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>();
|
bool ignoreEnemies = ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() || ObjectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>();
|
||||||
float safety = CalculateHullSafety(hull, visibleHulls, character, ignoreWater, ignoreOxygen, ignoreFire, ignoreEnemies);
|
float safety = CalculateHullSafety(hull, visibleHulls, character, ignoreWater, ignoreOxygen, ignoreFire, ignoreEnemies);
|
||||||
if (isCurrentHull)
|
if (isCurrentHull)
|
||||||
|
|||||||
@@ -77,11 +77,11 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (Level.Loaded.Type == LevelData.LevelType.LocationConnection)
|
if (Level.Loaded.Type == LevelData.LevelType.LocationConnection)
|
||||||
{
|
{
|
||||||
if (GameMain.GameSession.RoundDuration > 30.0f) { currentFlags.Add("Initial".ToIdentifier()); }
|
if (GameMain.GameSession.RoundDuration < 30.0f) { currentFlags.Add("Initial".ToIdentifier()); }
|
||||||
}
|
}
|
||||||
else if (Level.Loaded.Type == LevelData.LevelType.Outpost)
|
else if (Level.Loaded.Type == LevelData.LevelType.Outpost)
|
||||||
{
|
{
|
||||||
if (GameMain.GameSession.RoundDuration > 120.0f &&
|
if (GameMain.GameSession.RoundDuration < 120.0f &&
|
||||||
speaker?.CurrentHull != null &&
|
speaker?.CurrentHull != null &&
|
||||||
(speaker.TeamID == CharacterTeamType.FriendlyNPC || speaker.TeamID == CharacterTeamType.None) &&
|
(speaker.TeamID == CharacterTeamType.FriendlyNPC || speaker.TeamID == CharacterTeamType.None) &&
|
||||||
Character.CharacterList.Any(c => c.TeamID != speaker.TeamID && c.CurrentHull == speaker.CurrentHull))
|
Character.CharacterList.Any(c => c.TeamID != speaker.TeamID && c.CurrentHull == speaker.CurrentHull))
|
||||||
|
|||||||
+14
-1
@@ -876,7 +876,20 @@ namespace Barotrauma
|
|||||||
TargetName = Enemy.DisplayName,
|
TargetName = Enemy.DisplayName,
|
||||||
AlwaysUseEuclideanDistance = false
|
AlwaysUseEuclideanDistance = false
|
||||||
},
|
},
|
||||||
onAbandon: () => Abandon = true);
|
onAbandon: () =>
|
||||||
|
{
|
||||||
|
if (Enemy != null && HumanAIController.VisibleHulls.Contains(Enemy.CurrentHull))
|
||||||
|
{
|
||||||
|
// If in the same room with an enemy -> don't try to escape because we'd want to fight it
|
||||||
|
SteeringManager.Reset();
|
||||||
|
RemoveSubObjective(ref followTargetObjective);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// else abandon and fall back to find safety mode
|
||||||
|
Abandon = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
if (followTargetObjective == null) { return; }
|
if (followTargetObjective == null) { return; }
|
||||||
if (Mode == CombatMode.Arrest && Enemy.IsKnockedDown)
|
if (Mode == CombatMode.Arrest && Enemy.IsKnockedDown)
|
||||||
{
|
{
|
||||||
|
|||||||
+30
-3
@@ -25,6 +25,8 @@ namespace Barotrauma
|
|||||||
private readonly Item item;
|
private readonly Item item;
|
||||||
public Item ItemToContain { get; private set; }
|
public Item ItemToContain { get; private set; }
|
||||||
|
|
||||||
|
public int? TargetSlot;
|
||||||
|
|
||||||
private AIObjectiveGetItem getItemObjective;
|
private AIObjectiveGetItem getItemObjective;
|
||||||
private AIObjectiveGoTo goToObjective;
|
private AIObjectiveGoTo goToObjective;
|
||||||
|
|
||||||
@@ -126,7 +128,19 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
if (character.CanInteractWith(container.Item, checkLinked: false))
|
if (character.CanInteractWith(container.Item, checkLinked: false))
|
||||||
{
|
{
|
||||||
if (RemoveExisting || (RemoveExistingWhenNecessary && !container.Inventory.CanBePut(ItemToContain)))
|
static bool CanBePut(Inventory inventory, int? targetSlot, Item itemToContain)
|
||||||
|
{
|
||||||
|
if (targetSlot.HasValue)
|
||||||
|
{
|
||||||
|
return inventory.CanBePutInSlot(itemToContain, targetSlot.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return inventory.CanBePut(itemToContain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RemoveExisting || (RemoveExistingWhenNecessary && !CanBePut(container.Inventory, TargetSlot, ItemToContain)))
|
||||||
{
|
{
|
||||||
HumanAIController.UnequipContainedItems(container.Item, predicate: RemoveExistingPredicate, unequipMax: RemoveMax);
|
HumanAIController.UnequipContainedItems(container.Item, predicate: RemoveExistingPredicate, unequipMax: RemoveMax);
|
||||||
}
|
}
|
||||||
@@ -136,7 +150,20 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
Inventory originalInventory = ItemToContain.ParentInventory;
|
Inventory originalInventory = ItemToContain.ParentInventory;
|
||||||
var slots = originalInventory?.FindIndices(ItemToContain);
|
var slots = originalInventory?.FindIndices(ItemToContain);
|
||||||
if (container.Inventory.TryPutItem(ItemToContain, null))
|
|
||||||
|
static bool TryPutItem(Inventory inventory, int? targetSlot, Item itemToContain)
|
||||||
|
{
|
||||||
|
if (targetSlot.HasValue)
|
||||||
|
{
|
||||||
|
return inventory.TryPutItem(itemToContain, targetSlot.Value, allowSwapping: false, allowCombine: false, user: null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return inventory.TryPutItem(itemToContain, user: null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryPutItem(container.Inventory, TargetSlot, ItemToContain))
|
||||||
{
|
{
|
||||||
if (MoveWholeStack && slots != null)
|
if (MoveWholeStack && slots != null)
|
||||||
{
|
{
|
||||||
@@ -144,7 +171,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (Item item in originalInventory.GetItemsAt(slot).ToList())
|
foreach (Item item in originalInventory.GetItemsAt(slot).ToList())
|
||||||
{
|
{
|
||||||
container.Inventory.TryPutItem(item, null);
|
TryPutItem(container.Inventory, TargetSlot, item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -23,9 +23,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
protected override float TargetEvaluation()
|
protected override float TargetEvaluation()
|
||||||
{
|
{
|
||||||
if (!character.IsOnPlayerTeam) { return Targets.None() ? 0 : 100; }
|
if (Targets.None()) { return 0; }
|
||||||
int totalEnemies = Targets.Count();
|
if (!character.IsOnPlayerTeam) { return 100; }
|
||||||
if (totalEnemies == 0) { return 0; }
|
|
||||||
if (character.IsSecurity) { return 100; }
|
if (character.IsSecurity) { return 100; }
|
||||||
if (objectiveManager.IsOrder(this)) { return 100; }
|
if (objectiveManager.IsOrder(this)) { return 100; }
|
||||||
// If there's any security officers onboard, leave fighting for them.
|
// If there's any security officers onboard, leave fighting for them.
|
||||||
|
|||||||
+7
-1
@@ -103,13 +103,19 @@ namespace Barotrauma
|
|||||||
character.Speak(TextManager.Get("DialogGetOxygenTank").Value, null, 0, "getoxygentank".ToIdentifier(), 30.0f);
|
character.Speak(TextManager.Get("DialogGetOxygenTank").Value, null, 0, "getoxygentank".ToIdentifier(), 30.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new AIObjectiveContainItem(character, OXYGEN_SOURCE, targetItem.GetComponent<ItemContainer>(), objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC)
|
var container = targetItem.GetComponent<ItemContainer>();
|
||||||
|
var objective = new AIObjectiveContainItem(character, OXYGEN_SOURCE, container, objectiveManager, spawnItemIfNotFound: character.TeamID == CharacterTeamType.FriendlyNPC)
|
||||||
{
|
{
|
||||||
AllowToFindDivingGear = false,
|
AllowToFindDivingGear = false,
|
||||||
AllowDangerousPressure = true,
|
AllowDangerousPressure = true,
|
||||||
ConditionLevel = MIN_OXYGEN,
|
ConditionLevel = MIN_OXYGEN,
|
||||||
RemoveExistingWhenNecessary = true
|
RemoveExistingWhenNecessary = true
|
||||||
};
|
};
|
||||||
|
if (container.HasSubContainers)
|
||||||
|
{
|
||||||
|
objective.TargetSlot = container.FindSuitableSubContainerIndex(OXYGEN_SOURCE);
|
||||||
|
}
|
||||||
|
return objective;
|
||||||
},
|
},
|
||||||
onAbandon: () =>
|
onAbandon: () =>
|
||||||
{
|
{
|
||||||
|
|||||||
+1
-1
@@ -262,7 +262,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
Character followTarget = Target as Character;
|
Character followTarget = Target as Character;
|
||||||
bool needsDivingSuit = (!isInside || hasOutdoorNodes) && character.NeedsAir && !character.HasAbilityFlag(AbilityFlags.ImmuneToPressure);
|
bool needsDivingSuit = (!isInside || hasOutdoorNodes) && character.NeedsAir && !character.HasAbilityFlag(AbilityFlags.ImmuneToPressure);
|
||||||
bool needsDivingGear = (needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit));
|
bool needsDivingGear = needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit);
|
||||||
if (Mimic)
|
if (Mimic)
|
||||||
{
|
{
|
||||||
if (HumanAIController.HasDivingSuit(followTarget))
|
if (HumanAIController.HasDivingSuit(followTarget))
|
||||||
|
|||||||
@@ -435,7 +435,7 @@ namespace Barotrauma
|
|||||||
spawnPoint ??= WayPoint.WayPointList.Where(wp => wp.SpawnType == SpawnType.Human && wp.Submarine?.Info.Type == SubmarineType.Player).GetRandomUnsynced();
|
spawnPoint ??= WayPoint.WayPointList.Where(wp => wp.SpawnType == SpawnType.Human && wp.Submarine?.Info.Type == SubmarineType.Player).GetRandomUnsynced();
|
||||||
spawnPos = spawnPoint?.WorldPosition ?? Submarine.MainSub.WorldPosition;
|
spawnPos = spawnPoint?.WorldPosition ?? Submarine.MainSub.WorldPosition;
|
||||||
}
|
}
|
||||||
var pet = Character.Create(speciesName, spawnPos, seed);
|
var pet = Character.Create(speciesName, spawnPos, seed, spawnInitialItems: false);
|
||||||
var petBehavior = (pet?.AIController as EnemyAIController)?.PetBehavior;
|
var petBehavior = (pet?.AIController as EnemyAIController)?.PetBehavior;
|
||||||
if (petBehavior != null)
|
if (petBehavior != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ namespace Barotrauma
|
|||||||
get { return aiController; }
|
get { return aiController; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public AICharacter(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isNetworkPlayer = false, RagdollParams ragdoll = null)
|
public AICharacter(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isNetworkPlayer = false, RagdollParams ragdoll = null, bool spawnInitialItems = true)
|
||||||
: base(prefab, position, seed, characterInfo, id: id, isRemotePlayer: isNetworkPlayer, ragdollParams: ragdoll)
|
: base(prefab, position, seed, characterInfo, id: id, isRemotePlayer: isNetworkPlayer, ragdollParams: ragdoll, spawnInitialItems)
|
||||||
{
|
{
|
||||||
InitProjSpecific();
|
InitProjSpecific();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -529,8 +529,17 @@ namespace Barotrauma
|
|||||||
private Color speechBubbleColor;
|
private Color speechBubbleColor;
|
||||||
private float speechBubbleTimer;
|
private float speechBubbleTimer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prevents the character from interacting with items or characters
|
||||||
|
/// </summary>
|
||||||
public bool DisableInteract { get; set; }
|
public bool DisableInteract { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prevents the character from highlighting items or characters with the cursor,
|
||||||
|
/// meaning it can't interact with anything but the things it has currently selected/equipped
|
||||||
|
/// </summary>
|
||||||
|
public bool DisableFocusingOnEntities { get; set; }
|
||||||
|
|
||||||
//text displayed when the character is highlighted if custom interact is set
|
//text displayed when the character is highlighted if custom interact is set
|
||||||
public LocalizedString CustomInteractHUDText { get; private set; }
|
public LocalizedString CustomInteractHUDText { get; private set; }
|
||||||
private Action<Character, Character> onCustomInteract;
|
private Action<Character, Character> onCustomInteract;
|
||||||
@@ -1088,9 +1097,9 @@ namespace Barotrauma
|
|||||||
/// <param name="isRemotePlayer">Is the character controlled by a remote player.</param>
|
/// <param name="isRemotePlayer">Is the character controlled by a remote player.</param>
|
||||||
/// <param name="hasAi">Is the character controlled by AI.</param>
|
/// <param name="hasAi">Is the character controlled by AI.</param>
|
||||||
/// <param name="ragdoll">Ragdoll configuration file. If null, will select the default.</param>
|
/// <param name="ragdoll">Ragdoll configuration file. If null, will select the default.</param>
|
||||||
public static Character Create(CharacterInfo characterInfo, Vector2 position, string seed, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, RagdollParams ragdoll = null)
|
public static Character Create(CharacterInfo characterInfo, Vector2 position, string seed, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, RagdollParams ragdoll = null, bool spawnInitialItems = true)
|
||||||
{
|
{
|
||||||
return Create(characterInfo.SpeciesName, position, seed, characterInfo, id, isRemotePlayer, hasAi, true, ragdoll);
|
return Create(characterInfo.SpeciesName, position, seed, characterInfo, id, isRemotePlayer, hasAi, createNetworkEvent: true, ragdoll, spawnInitialItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1105,16 +1114,16 @@ namespace Barotrauma
|
|||||||
/// <param name="hasAi">Is the character controlled by AI.</param>
|
/// <param name="hasAi">Is the character controlled by AI.</param>
|
||||||
/// <param name="createNetworkEvent">Should clients receive a network event about the creation of this character?</param>
|
/// <param name="createNetworkEvent">Should clients receive a network event about the creation of this character?</param>
|
||||||
/// <param name="ragdoll">Ragdoll configuration file. If null, will select the default.</param>
|
/// <param name="ragdoll">Ragdoll configuration file. If null, will select the default.</param>
|
||||||
public static Character Create(string speciesName, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null, bool throwErrorIfNotFound = true)
|
public static Character Create(string speciesName, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null, bool throwErrorIfNotFound = true, bool spawnInitialItems = true)
|
||||||
{
|
{
|
||||||
if (speciesName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
|
if (speciesName.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
speciesName = Path.GetFileNameWithoutExtension(speciesName);
|
speciesName = Path.GetFileNameWithoutExtension(speciesName);
|
||||||
}
|
}
|
||||||
return Create(speciesName.ToIdentifier(), position, seed, characterInfo, id, isRemotePlayer, hasAi, createNetworkEvent, ragdoll, throwErrorIfNotFound);
|
return Create(speciesName.ToIdentifier(), position, seed, characterInfo, id, isRemotePlayer, hasAi, createNetworkEvent, ragdoll, throwErrorIfNotFound, spawnInitialItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Character Create(Identifier speciesName, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null, bool throwErrorIfNotFound = true)
|
public static Character Create(Identifier speciesName, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null, bool throwErrorIfNotFound = true, bool spawnInitialItems = true)
|
||||||
{
|
{
|
||||||
var prefab = CharacterPrefab.FindBySpeciesName(speciesName);
|
var prefab = CharacterPrefab.FindBySpeciesName(speciesName);
|
||||||
if (prefab == null)
|
if (prefab == null)
|
||||||
@@ -1131,29 +1140,29 @@ namespace Barotrauma
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return Create(prefab, position, seed, characterInfo, id, isRemotePlayer, hasAi, createNetworkEvent, ragdoll);
|
return Create(prefab, position, seed, characterInfo, id, isRemotePlayer, hasAi, createNetworkEvent, ragdoll, spawnInitialItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Character Create(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null)
|
public static Character Create(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, bool hasAi = true, bool createNetworkEvent = true, RagdollParams ragdoll = null, bool spawnInitialItems = true)
|
||||||
{
|
{
|
||||||
Character newCharacter = null;
|
Character newCharacter = null;
|
||||||
if (prefab.Identifier != CharacterPrefab.HumanSpeciesName)
|
if (prefab.Identifier != CharacterPrefab.HumanSpeciesName)
|
||||||
{
|
{
|
||||||
var aiCharacter = new AICharacter(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll);
|
var aiCharacter = new AICharacter(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll, spawnInitialItems);
|
||||||
var ai = new EnemyAIController(aiCharacter, seed);
|
var ai = new EnemyAIController(aiCharacter, seed);
|
||||||
aiCharacter.SetAI(ai);
|
aiCharacter.SetAI(ai);
|
||||||
newCharacter = aiCharacter;
|
newCharacter = aiCharacter;
|
||||||
}
|
}
|
||||||
else if (hasAi)
|
else if (hasAi)
|
||||||
{
|
{
|
||||||
var aiCharacter = new AICharacter(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll);
|
var aiCharacter = new AICharacter(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll, spawnInitialItems);
|
||||||
var ai = new HumanAIController(aiCharacter);
|
var ai = new HumanAIController(aiCharacter);
|
||||||
aiCharacter.SetAI(ai);
|
aiCharacter.SetAI(ai);
|
||||||
newCharacter = aiCharacter;
|
newCharacter = aiCharacter;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newCharacter = new Character(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll);
|
newCharacter = new Character(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll, spawnInitialItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SERVER
|
#if SERVER
|
||||||
@@ -1170,7 +1179,7 @@ namespace Barotrauma
|
|||||||
wallet = new Wallet(Option<Character>.Some(this));
|
wallet = new Wallet(Option<Character>.Some(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Character(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, RagdollParams ragdollParams = null)
|
protected Character(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isRemotePlayer = false, RagdollParams ragdollParams = null, bool spawnInitialItems = true)
|
||||||
: this(null, id)
|
: this(null, id)
|
||||||
{
|
{
|
||||||
this.Seed = seed;
|
this.Seed = seed;
|
||||||
@@ -1280,7 +1289,8 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
Inventory = new CharacterInventory(
|
Inventory = new CharacterInventory(
|
||||||
inventoryElements.Count == 1 ? inventoryElements[0] : ToolBox.SelectWeightedRandom(inventoryElements, inventoryCommonness, random),
|
inventoryElements.Count == 1 ? inventoryElements[0] : ToolBox.SelectWeightedRandom(inventoryElements, inventoryCommonness, random),
|
||||||
this);
|
this,
|
||||||
|
spawnInitialItems);
|
||||||
}
|
}
|
||||||
if (healthElements.Count == 0)
|
if (healthElements.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -2719,7 +2729,7 @@ namespace Barotrauma
|
|||||||
#if CLIENT
|
#if CLIENT
|
||||||
if (isLocalPlayer)
|
if (isLocalPlayer)
|
||||||
{
|
{
|
||||||
if (!IsMouseOnUI && (ViewTarget == null || ViewTarget == this))
|
if (!IsMouseOnUI && (ViewTarget == null || ViewTarget == this) && !DisableFocusingOnEntities)
|
||||||
{
|
{
|
||||||
if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen)
|
if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen)
|
||||||
{
|
{
|
||||||
@@ -2754,6 +2764,7 @@ namespace Barotrauma
|
|||||||
focusedItem = null;
|
focusedItem = null;
|
||||||
}
|
}
|
||||||
findFocusedTimer -= deltaTime;
|
findFocusedTimer -= deltaTime;
|
||||||
|
DisableFocusingOnEntities = false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
var head = AnimController.GetLimb(LimbType.Head);
|
var head = AnimController.GetLimb(LimbType.Head);
|
||||||
@@ -4249,11 +4260,14 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (!isNetworkMessage)
|
if (!isNetworkMessage)
|
||||||
{
|
{
|
||||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
if (GameMain.NetworkMember is { IsClient: true }) { return; }
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterHealth.ApplyAffliction(null, new Affliction(AfflictionPrefab.Pressure, AfflictionPrefab.Pressure.MaxStrength));
|
CharacterHealth.ApplyAffliction(null, new Affliction(AfflictionPrefab.Pressure, AfflictionPrefab.Pressure.MaxStrength));
|
||||||
if (isNetworkMessage && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && Vitality <= CharacterHealth.MinVitality) { Kill(CauseOfDeathType.Pressure, null, isNetworkMessage: true); }
|
if (GameMain.NetworkMember is not { IsClient: true } || isNetworkMessage)
|
||||||
|
{
|
||||||
|
Kill(CauseOfDeathType.Pressure, null, isNetworkMessage: true);
|
||||||
|
}
|
||||||
if (IsDead)
|
if (IsDead)
|
||||||
{
|
{
|
||||||
BreakJoints();
|
BreakJoints();
|
||||||
@@ -4930,7 +4944,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
foreach (TalentOption talentOption in talentSubTree.TalentOptionStages)
|
foreach (TalentOption talentOption in talentSubTree.TalentOptionStages)
|
||||||
{
|
{
|
||||||
if (talentOption.TalentIdentifiers.None(HasTalent))
|
if (!talentOption.HasMaxTalents(info.UnlockedTalents))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Barotrauma;
|
||||||
|
|
||||||
|
partial class CharacterHUD
|
||||||
|
{
|
||||||
|
static partial void RecreateHudTextsIfControllingProjSpecific(Character character);
|
||||||
|
|
||||||
|
static partial void RecreateHudTextsIfFocusedProjSpecific(params Item[] items);
|
||||||
|
|
||||||
|
public static void RecreateHudTextsIfControlling(Character character) => RecreateHudTextsIfControllingProjSpecific(character);
|
||||||
|
|
||||||
|
public static void RecreateHudTextsIfFocused(params Item[] items) => RecreateHudTextsIfFocusedProjSpecific(items);
|
||||||
|
}
|
||||||
@@ -132,7 +132,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public bool IsUnconscious
|
public bool IsUnconscious
|
||||||
{
|
{
|
||||||
get { return (Vitality <= 0.0f || Character.IsDead) && !Character.HasAbilityFlag(AbilityFlags.AlwaysStayConscious); }
|
get { return Character.IsDead || (Vitality <= 0.0f && !Character.HasAbilityFlag(AbilityFlags.AlwaysStayConscious)); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public float PressureKillDelay { get; private set; } = 5.0f;
|
public float PressureKillDelay { get; private set; } = 5.0f;
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
bool allowCheats = false;
|
bool allowCheats = false;
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
allowCheats = GameMain.NetworkMember == null && (GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected is EditorScreen);
|
allowCheats = GameMain.NetworkMember == null && (GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected is { IsEditor: true });
|
||||||
#endif
|
#endif
|
||||||
if (!allowCheats && !CheatsEnabled && IsCheat)
|
if (!allowCheats && !CheatsEnabled && IsCheat)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ namespace Barotrauma
|
|||||||
MaxAttachableCount,
|
MaxAttachableCount,
|
||||||
ExplosionRadiusMultiplier,
|
ExplosionRadiusMultiplier,
|
||||||
ExplosionDamageMultiplier,
|
ExplosionDamageMultiplier,
|
||||||
FabricateMedicineSpeedMultiplier,
|
FabricationSpeed,
|
||||||
BallastFloraDamageMultiplier,
|
BallastFloraDamageMultiplier,
|
||||||
HoldBreathMultiplier,
|
HoldBreathMultiplier,
|
||||||
Apprenticeship,
|
Apprenticeship,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Barotrauma.Extensions;
|
||||||
using Barotrauma.Items.Components;
|
using Barotrauma.Items.Components;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -53,6 +54,11 @@ namespace Barotrauma
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
conditionals = conditionalList;
|
conditionals = conditionalList;
|
||||||
|
|
||||||
|
if (itemTags.None() && ItemIdentifiers.None())
|
||||||
|
{
|
||||||
|
DebugConsole.ThrowError($"Error in event \"{ParentEvent.Prefab.Identifier}\". {nameof(CheckItemAction)} does't define either tags or identifiers of the item to check.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool? DetermineSuccess()
|
protected override bool? DetermineSuccess()
|
||||||
@@ -100,7 +106,22 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
if (inventory == null) { return false; }
|
if (inventory == null) { return false; }
|
||||||
int count = 0;
|
int count = 0;
|
||||||
foreach (Item item in inventory.FindAllItems(it => itemTags.Any(it.HasTag) || itemIdentifierSplit.Contains(it.Prefab.Identifier), recursive: Recursive))
|
HashSet<Item> eventTargets = new HashSet<Item>();
|
||||||
|
foreach (Identifier tag in itemTags)
|
||||||
|
{
|
||||||
|
foreach (var target in ParentEvent.GetTargets(tag))
|
||||||
|
{
|
||||||
|
if (target is Item item)
|
||||||
|
{
|
||||||
|
eventTargets.Add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (Item item in inventory.FindAllItems(it =>
|
||||||
|
itemTags.Any(it.HasTag) ||
|
||||||
|
itemIdentifierSplit.Contains(it.Prefab.Identifier) ||
|
||||||
|
eventTargets.Contains(it),
|
||||||
|
recursive: Recursive))
|
||||||
{
|
{
|
||||||
if (!ConditionalsMatch(item, character)) { continue; }
|
if (!ConditionalsMatch(item, character)) { continue; }
|
||||||
count++;
|
count++;
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public bool DisableEvents
|
public bool DisableEvents
|
||||||
{
|
{
|
||||||
get { return IsFirstRound && GameMain.GameSession.RoundDuration > FirstRoundEventDelay; }
|
get { return IsFirstRound && GameMain.GameSession.RoundDuration < FirstRoundEventDelay; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CheatsEnabled;
|
public bool CheatsEnabled;
|
||||||
|
|||||||
@@ -764,7 +764,7 @@ namespace Barotrauma
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static ImmutableHashSet<Character> GetSessionCrewCharacters(CharacterType type)
|
public static ImmutableHashSet<Character> GetSessionCrewCharacters(CharacterType type)
|
||||||
{
|
{
|
||||||
if (GameMain.GameSession.CrewManager is not { } crewManager) { return ImmutableHashSet<Character>.Empty; }
|
if (GameMain.GameSession?.CrewManager is not { } crewManager) { return ImmutableHashSet<Character>.Empty; }
|
||||||
|
|
||||||
IEnumerable<Character> players;
|
IEnumerable<Character> players;
|
||||||
IEnumerable<Character> bots;
|
IEnumerable<Character> bots;
|
||||||
@@ -907,7 +907,7 @@ namespace Barotrauma
|
|||||||
{
|
{
|
||||||
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"), RoundDuration);
|
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "none"), RoundDuration);
|
||||||
GameAnalyticsManager.AddDesignEvent(eventId + "GameMode:" + (GameMode?.Name.Value ?? "none"), RoundDuration);
|
GameAnalyticsManager.AddDesignEvent(eventId + "GameMode:" + (GameMode?.Name.Value ?? "none"), RoundDuration);
|
||||||
GameAnalyticsManager.AddDesignEvent(eventId + "CrewSize:" + (CrewManager?.CharacterInfos?.Count() ?? 0), RoundDuration);
|
GameAnalyticsManager.AddDesignEvent(eventId + "CrewSize:" + (CrewManager?.CharacterInfos?.Count ?? 0), RoundDuration);
|
||||||
foreach (Mission mission in missions)
|
foreach (Mission mission in missions)
|
||||||
{
|
{
|
||||||
GameAnalyticsManager.AddDesignEvent(eventId + "MissionType:" + (mission.Prefab.Type.ToString() ?? "none") + ":" + mission.Prefab.Identifier + ":" + (mission.Completed ? "Completed" : "Failed"), RoundDuration);
|
GameAnalyticsManager.AddDesignEvent(eventId + "MissionType:" + (mission.Prefab.Type.ToString() ?? "none") + ":" + mission.Prefab.Identifier + ":" + (mission.Completed ? "Completed" : "Failed"), RoundDuration);
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
EndReadyCheck();
|
EndReadyCheck();
|
||||||
|
|
||||||
|
#if CLIENT
|
||||||
|
msgBox?.Close();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,7 @@ namespace Barotrauma
|
|||||||
return slotString == null ? Array.Empty<string>() : slotString.Split(',');
|
return slotString == null ? Array.Empty<string>() : slotString.Split(',');
|
||||||
}
|
}
|
||||||
|
|
||||||
public CharacterInventory(XElement element, Character character)
|
public CharacterInventory(XElement element, Character character, bool spawnInitialItems)
|
||||||
: base(character, ParseSlotTypes(element).Length)
|
: base(character, ParseSlotTypes(element).Length)
|
||||||
{
|
{
|
||||||
this.character = character;
|
this.character = character;
|
||||||
@@ -84,6 +84,8 @@ namespace Barotrauma
|
|||||||
|
|
||||||
InitProjSpecific(element);
|
InitProjSpecific(element);
|
||||||
|
|
||||||
|
if (!spawnInitialItems) { return; }
|
||||||
|
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
//clients don't create items until the server says so
|
//clients don't create items until the server says so
|
||||||
if (GameMain.Client != null) { return; }
|
if (GameMain.Client != null) { return; }
|
||||||
@@ -94,7 +96,7 @@ namespace Barotrauma
|
|||||||
if (!subElement.Name.ToString().Equals("item", StringComparison.OrdinalIgnoreCase)) { continue; }
|
if (!subElement.Name.ToString().Equals("item", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||||
|
|
||||||
string itemIdentifier = subElement.GetAttributeString("identifier", "");
|
string itemIdentifier = subElement.GetAttributeString("identifier", "");
|
||||||
if (!(MapEntityPrefab.Find(null, itemIdentifier) is ItemPrefab itemPrefab))
|
if (!ItemPrefab.Prefabs.TryGet(itemIdentifier, out var itemPrefab))
|
||||||
{
|
{
|
||||||
DebugConsole.ThrowError("Error in character inventory \"" + character.SpeciesName + "\" - item \"" + itemIdentifier + "\" not found.");
|
DebugConsole.ThrowError("Error in character inventory \"" + character.SpeciesName + "\" - item \"" + itemIdentifier + "\" not found.");
|
||||||
continue;
|
continue;
|
||||||
@@ -200,6 +202,7 @@ namespace Barotrauma
|
|||||||
#if CLIENT
|
#if CLIENT
|
||||||
CreateSlots();
|
CreateSlots();
|
||||||
#endif
|
#endif
|
||||||
|
CharacterHUD.RecreateHudTextsIfControlling(character);
|
||||||
//if the item was equipped and there are more items in the same stack, equip one of those items
|
//if the item was equipped and there are more items in the same stack, equip one of those items
|
||||||
if (tryEquipFromSameStack && wasEquipped)
|
if (tryEquipFromSameStack && wasEquipped)
|
||||||
{
|
{
|
||||||
@@ -498,6 +501,7 @@ namespace Barotrauma
|
|||||||
HintManager.OnObtainedItem(character, item);
|
HintManager.OnObtainedItem(character, item);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
CharacterHUD.RecreateHudTextsIfControlling(character);
|
||||||
if (item.CampaignInteractionType == CampaignMode.InteractionType.Cargo)
|
if (item.CampaignInteractionType == CampaignMode.InteractionType.Cargo)
|
||||||
{
|
{
|
||||||
item.CampaignInteractionType = CampaignMode.InteractionType.None;
|
item.CampaignInteractionType = CampaignMode.InteractionType.None;
|
||||||
|
|||||||
@@ -47,6 +47,13 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
return ContainableItems == null || ContainableItems.Count == 0 || ContainableItems.Any(c => c.MatchesItem(itemPrefab));
|
return ContainableItems == null || ContainableItems.Count == 0 || ContainableItems.Any(c => c.MatchesItem(itemPrefab));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool MatchesItem(Identifier identifierOrTag)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
ContainableItems == null || ContainableItems.Count == 0 ||
|
||||||
|
ContainableItems.Any(c => c.Identifiers.Contains(identifierOrTag) && !c.ExcludedIdentifiers.Contains(identifierOrTag));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly NamedEvent<ItemContainer> OnContainedItemsChanged = new NamedEvent<ItemContainer>();
|
public readonly NamedEvent<ItemContainer> OnContainedItemsChanged = new NamedEvent<ItemContainer>();
|
||||||
@@ -374,7 +381,7 @@ namespace Barotrauma.Items.Components
|
|||||||
// Set the contained items active if there's an item inserted inside the container. Enables e.g. the rifle flashlight when it's attached to the rifle (put inside of it).
|
// Set the contained items active if there's an item inserted inside the container. Enables e.g. the rifle flashlight when it's attached to the rifle (put inside of it).
|
||||||
SetContainedActive(true);
|
SetContainedActive(true);
|
||||||
}
|
}
|
||||||
|
CharacterHUD.RecreateHudTextsIfFocused(item, containedItem);
|
||||||
OnContainedItemsChanged.Invoke(this);
|
OnContainedItemsChanged.Invoke(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,9 +393,9 @@ namespace Barotrauma.Items.Components
|
|||||||
public void OnItemRemoved(Item containedItem)
|
public void OnItemRemoved(Item containedItem)
|
||||||
{
|
{
|
||||||
activeContainedItems.RemoveAll(i => i.Item == containedItem);
|
activeContainedItems.RemoveAll(i => i.Item == containedItem);
|
||||||
|
|
||||||
//deactivate if the inventory is empty
|
//deactivate if the inventory is empty
|
||||||
IsActive = activeContainedItems.Count > 0 || Inventory.AllItems.Any(it => it.body != null);
|
IsActive = activeContainedItems.Count > 0 || Inventory.AllItems.Any(it => it.body != null);
|
||||||
|
CharacterHUD.RecreateHudTextsIfFocused(item, containedItem);
|
||||||
OnContainedItemsChanged.Invoke(this);
|
OnContainedItemsChanged.Invoke(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,6 +671,18 @@ namespace Barotrauma.Items.Components
|
|||||||
return relatedItem;
|
return relatedItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the index of the first slot whose restrictions match the specified tag or identifier
|
||||||
|
/// </summary>
|
||||||
|
public int? FindSuitableSubContainerIndex(Identifier itemTagOrIdentifier)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < slotRestrictions.Length; i++)
|
||||||
|
{
|
||||||
|
if (slotRestrictions[i].MatchesItem(itemTagOrIdentifier)) { return i; }
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||||
{
|
{
|
||||||
switch (connection.Name)
|
switch (connection.Name)
|
||||||
|
|||||||
@@ -604,14 +604,20 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
partial void UpdateRequiredTimeProjSpecific();
|
partial void UpdateRequiredTimeProjSpecific();
|
||||||
|
|
||||||
|
private static bool AnyOneHasRecipeForItem(Character user, ItemPrefab item)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(user != null && user.HasRecipeForItem(item.Identifier)) ||
|
||||||
|
GameSession.GetSessionCrewCharacters(CharacterType.Bot).Any(c => c.HasRecipeForItem(item.Identifier));
|
||||||
|
}
|
||||||
|
|
||||||
private bool CanBeFabricated(FabricationRecipe fabricableItem, IReadOnlyDictionary<Identifier, List<Item>> availableIngredients, Character character)
|
private bool CanBeFabricated(FabricationRecipe fabricableItem, IReadOnlyDictionary<Identifier, List<Item>> availableIngredients, Character character)
|
||||||
{
|
{
|
||||||
if (fabricableItem == null) { return false; }
|
if (fabricableItem == null) { return false; }
|
||||||
if (fabricableItem.RequiresRecipe)
|
if (fabricableItem.RequiresRecipe)
|
||||||
{
|
{
|
||||||
if (character == null) { return false; }
|
if (character == null) { return false; }
|
||||||
if (!character.HasRecipeForItem(fabricableItem.TargetItem.Identifier) &&
|
if (!AnyOneHasRecipeForItem(character, fabricableItem.TargetItem))
|
||||||
GameSession.GetSessionCrewCharacters(CharacterType.Bot).None(c => c.HasRecipeForItem(fabricableItem.TargetItem.Identifier)))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -678,9 +684,10 @@ namespace Barotrauma.Items.Components
|
|||||||
//fabricating takes 100 times longer if degree of success is close to 0
|
//fabricating takes 100 times longer if degree of success is close to 0
|
||||||
//characters with a higher skill than required can fabricate up to 100% faster
|
//characters with a higher skill than required can fabricate up to 100% faster
|
||||||
float time = fabricableItem.RequiredTime / item.StatManager.GetAdjustedValue(ItemTalentStats.FabricationSpeed, FabricationSpeed) / MathHelper.Clamp(t, 0.01f, 2.0f);
|
float time = fabricableItem.RequiredTime / item.StatManager.GetAdjustedValue(ItemTalentStats.FabricationSpeed, FabricationSpeed) / MathHelper.Clamp(t, 0.01f, 2.0f);
|
||||||
if (user is not null && fabricableItem.TargetItem is { } it && it.Tags.Contains("medical"))
|
|
||||||
|
if (user?.Info is { } info && fabricableItem.TargetItem is { } it)
|
||||||
{
|
{
|
||||||
time *= 1f + user.GetStatValue(StatTypes.FabricateMedicineSpeedMultiplier);
|
time /= 1f + it.Tags.Sum(tag => info.GetSavedStatValue(StatTypes.FabricationSpeed, tag));
|
||||||
}
|
}
|
||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,12 +25,8 @@ namespace Barotrauma.Items.Components
|
|||||||
public List<Hull> LinkedHulls = new List<Hull>();
|
public List<Hull> LinkedHulls = new List<Hull>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private DateTime resetDataTime;
|
|
||||||
|
|
||||||
private bool hasPower;
|
private bool hasPower;
|
||||||
|
|
||||||
private readonly Dictionary<Hull, HullData> hullDatas;
|
|
||||||
|
|
||||||
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Does the machine require inputs from water detectors in order to show the water levels inside rooms.")]
|
[Editable, Serialize(false, IsPropertySaveable.Yes, description: "Does the machine require inputs from water detectors in order to show the water levels inside rooms.")]
|
||||||
public bool RequireWaterDetectors
|
public bool RequireWaterDetectors
|
||||||
{
|
{
|
||||||
@@ -77,7 +73,6 @@ namespace Barotrauma.Items.Components
|
|||||||
: base(item, element)
|
: base(item, element)
|
||||||
{
|
{
|
||||||
IsActive = true;
|
IsActive = true;
|
||||||
hullDatas = new Dictionary<Hull, HullData>();
|
|
||||||
InitProjSpecific();
|
InitProjSpecific();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,37 +80,6 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
public override void Update(float deltaTime, Camera cam)
|
public override void Update(float deltaTime, Camera cam)
|
||||||
{
|
{
|
||||||
//reset data if we haven't received anything in a while
|
|
||||||
//(so that outdated hull info won't be shown if detectors stop sending signals)
|
|
||||||
if (DateTime.Now > resetDataTime)
|
|
||||||
{
|
|
||||||
foreach (HullData hullData in hullDatas.Values)
|
|
||||||
{
|
|
||||||
if (!hullData.Distort)
|
|
||||||
{
|
|
||||||
if (Timing.TotalTime > hullData.LastOxygenDataTime + 1.0) { hullData.ReceivedOxygenAmount = null; }
|
|
||||||
if (Timing.TotalTime > hullData.LastWaterDataTime + 1.0) { hullData.ReceivedWaterAmount = null; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resetDataTime = DateTime.Now + new TimeSpan(0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if CLIENT
|
|
||||||
if (cardRefreshTimer > cardRefreshDelay)
|
|
||||||
{
|
|
||||||
if (item.Submarine is { } sub)
|
|
||||||
{
|
|
||||||
UpdateIDCards(sub);
|
|
||||||
}
|
|
||||||
|
|
||||||
cardRefreshTimer = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cardRefreshTimer += deltaTime;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
hasPower = Voltage > MinVoltage;
|
hasPower = Voltage > MinVoltage;
|
||||||
if (hasPower)
|
if (hasPower)
|
||||||
{
|
{
|
||||||
@@ -140,67 +104,5 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
return picker != null;
|
return picker != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
|
||||||
{
|
|
||||||
Item source = signal.source;
|
|
||||||
if (source == null || source.CurrentHull == null) { return; }
|
|
||||||
|
|
||||||
Hull sourceHull = source.CurrentHull;
|
|
||||||
if (!hullDatas.TryGetValue(sourceHull, out HullData hullData))
|
|
||||||
{
|
|
||||||
hullData = new HullData();
|
|
||||||
hullDatas.Add(sourceHull, hullData);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hullData.Distort) { return; }
|
|
||||||
|
|
||||||
switch (connection.Name)
|
|
||||||
{
|
|
||||||
case "water_data_in":
|
|
||||||
//cheating a bit because water detectors don't actually send the water level
|
|
||||||
bool fromWaterDetector = source.GetComponent<WaterDetector>() != null;
|
|
||||||
hullData.ReceivedWaterAmount = null;
|
|
||||||
hullData.LastWaterDataTime = Timing.TotalTime;
|
|
||||||
if (fromWaterDetector)
|
|
||||||
{
|
|
||||||
hullData.ReceivedWaterAmount = WaterDetector.GetWaterPercentage(sourceHull);
|
|
||||||
}
|
|
||||||
foreach (var linked in sourceHull.linkedTo)
|
|
||||||
{
|
|
||||||
if (!(linked is Hull linkedHull)) { continue; }
|
|
||||||
if (!hullDatas.TryGetValue(linkedHull, out HullData linkedHullData))
|
|
||||||
{
|
|
||||||
linkedHullData = new HullData();
|
|
||||||
hullDatas.Add(linkedHull, linkedHullData);
|
|
||||||
}
|
|
||||||
linkedHullData.ReceivedWaterAmount = null;
|
|
||||||
if (fromWaterDetector)
|
|
||||||
{
|
|
||||||
linkedHullData.ReceivedWaterAmount = WaterDetector.GetWaterPercentage(linkedHull);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "oxygen_data_in":
|
|
||||||
if (!float.TryParse(signal.value, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out float oxy))
|
|
||||||
{
|
|
||||||
oxy = Rand.Range(0.0f, 100.0f);
|
|
||||||
}
|
|
||||||
hullData.ReceivedOxygenAmount = oxy;
|
|
||||||
hullData.LastOxygenDataTime = Timing.TotalTime;
|
|
||||||
foreach (var linked in sourceHull.linkedTo)
|
|
||||||
{
|
|
||||||
if (linked is not Hull linkedHull) { continue; }
|
|
||||||
if (!hullDatas.TryGetValue(linkedHull, out HullData linkedHullData))
|
|
||||||
{
|
|
||||||
linkedHullData = new HullData();
|
|
||||||
hullDatas.Add(linkedHull, linkedHullData);
|
|
||||||
}
|
|
||||||
linkedHullData.ReceivedOxygenAmount = oxy;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
const float NetworkUpdateIntervalHigh = 0.5f;
|
const float NetworkUpdateIntervalHigh = 0.5f;
|
||||||
|
|
||||||
const float TemperatureBoostAmount = 20;
|
const float TemperatureBoostAmount = 25;
|
||||||
|
|
||||||
//the rate at which the reactor is being run on (higher rate -> higher temperature)
|
//the rate at which the reactor is being run on (higher rate -> higher temperature)
|
||||||
private float fissionRate;
|
private float fissionRate;
|
||||||
@@ -26,10 +26,6 @@ namespace Barotrauma.Items.Components
|
|||||||
//amount of power generated balanced with the load)
|
//amount of power generated balanced with the load)
|
||||||
private bool autoTemp;
|
private bool autoTemp;
|
||||||
|
|
||||||
//automatical adjustment to the power output when
|
|
||||||
//turbine output and temperature are in the optimal range
|
|
||||||
private float autoAdjustAmount;
|
|
||||||
|
|
||||||
private float fuelConsumptionRate;
|
private float fuelConsumptionRate;
|
||||||
|
|
||||||
private float meltDownTimer, meltDownDelay;
|
private float meltDownTimer, meltDownDelay;
|
||||||
@@ -53,6 +49,8 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
private float temperatureBoost;
|
private float temperatureBoost;
|
||||||
|
|
||||||
|
public bool AllowTemperatureBoost => Math.Abs(temperatureBoost) < TemperatureBoostAmount * 0.9f;
|
||||||
|
|
||||||
private bool _powerOn;
|
private bool _powerOn;
|
||||||
|
|
||||||
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes)]
|
[Serialize(defaultValue: false, isSaveable: IsPropertySaveable.Yes)]
|
||||||
@@ -314,7 +312,7 @@ namespace Barotrauma.Items.Components
|
|||||||
Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff));
|
Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff));
|
||||||
temperatureBoost = adjustValueWithoutOverShooting(temperatureBoost, 0.0f, deltaTime);
|
temperatureBoost = adjustValueWithoutOverShooting(temperatureBoost, 0.0f, deltaTime);
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
temperatureBoostUpButton.Enabled = temperatureBoostDownButton.Enabled = Math.Abs(temperatureBoost) < TemperatureBoostAmount * 0.9f;
|
temperatureBoostUpButton.Enabled = temperatureBoostDownButton.Enabled = AllowTemperatureBoost;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FissionRate = MathHelper.Lerp(fissionRate, Math.Min(TargetFissionRate, AvailableFuel), deltaTime);
|
FissionRate = MathHelper.Lerp(fissionRate, Math.Min(TargetFissionRate, AvailableFuel), deltaTime);
|
||||||
|
|||||||
@@ -248,6 +248,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
if (container?.Inventory == null) { return; }
|
if (container?.Inventory == null) { return; }
|
||||||
|
|
||||||
|
bool recreateHudTexts = false;
|
||||||
for (var i = 0; i < container.Inventory.Capacity; i++)
|
for (var i = 0; i < container.Inventory.Capacity; i++)
|
||||||
{
|
{
|
||||||
if (i < 0 || GrowableSeeds.Length <= i) { continue; }
|
if (i < 0 || GrowableSeeds.Length <= i) { continue; }
|
||||||
@@ -257,6 +258,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
if (growable != null)
|
if (growable != null)
|
||||||
{
|
{
|
||||||
|
recreateHudTexts |= GrowableSeeds[i] != growable;
|
||||||
GrowableSeeds[i] = growable;
|
GrowableSeeds[i] = growable;
|
||||||
growable.IsActive = true;
|
growable.IsActive = true;
|
||||||
}
|
}
|
||||||
@@ -267,11 +269,14 @@ namespace Barotrauma.Items.Components
|
|||||||
// Kill the plant if it's somehow removed
|
// Kill the plant if it's somehow removed
|
||||||
oldGrowable.Decayed = true;
|
oldGrowable.Decayed = true;
|
||||||
oldGrowable.IsActive = false;
|
oldGrowable.IsActive = false;
|
||||||
|
recreateHudTexts = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
GrowableSeeds[i] = null;
|
GrowableSeeds[i] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#if CLIENT
|
||||||
|
CharacterHUD.RecreateHudTexts |= recreateHudTexts;
|
||||||
|
#endif
|
||||||
|
|
||||||
// server handles this
|
// server handles this
|
||||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ namespace Barotrauma.Items.Components
|
|||||||
{
|
{
|
||||||
//[power/min]
|
//[power/min]
|
||||||
private float capacity;
|
private float capacity;
|
||||||
|
private float adjustedCapacity;
|
||||||
|
|
||||||
private float charge, prevCharge;
|
private float charge, prevCharge;
|
||||||
|
|
||||||
@@ -66,7 +67,11 @@ namespace Barotrauma.Items.Components
|
|||||||
public float Capacity
|
public float Capacity
|
||||||
{
|
{
|
||||||
get => capacity;
|
get => capacity;
|
||||||
set { capacity = Math.Max(value, 1.0f); }
|
set
|
||||||
|
{
|
||||||
|
capacity = Math.Max(value, 1.0f);
|
||||||
|
adjustedCapacity = GetCapacity();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "The current charge of the device.")]
|
[Editable, Serialize(0.0f, IsPropertySaveable.Yes, description: "The current charge of the device.")]
|
||||||
@@ -76,10 +81,10 @@ namespace Barotrauma.Items.Components
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (!MathUtils.IsValid(value)) return;
|
if (!MathUtils.IsValid(value)) return;
|
||||||
charge = MathHelper.Clamp(value, 0.0f, capacity);
|
charge = MathHelper.Clamp(value, 0.0f, adjustedCapacity);
|
||||||
|
|
||||||
//send a network event if the charge has changed by more than 5%
|
//send a network event if the charge has changed by more than 5%
|
||||||
if (Math.Abs(charge - lastSentCharge) / capacity > 0.05f)
|
if (Math.Abs(charge - lastSentCharge) / adjustedCapacity > 0.05f)
|
||||||
{
|
{
|
||||||
#if SERVER
|
#if SERVER
|
||||||
if (GameMain.Server != null && (!item.Submarine?.Loading ?? true)) { item.CreateServerEvent(this); }
|
if (GameMain.Server != null && (!item.Submarine?.Loading ?? true)) { item.CreateServerEvent(this); }
|
||||||
@@ -89,7 +94,7 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public float ChargePercentage => MathUtils.Percentage(Charge, GetCapacity());
|
public float ChargePercentage => MathUtils.Percentage(Charge, adjustedCapacity);
|
||||||
|
|
||||||
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, description: "How fast the device can be recharged. For example, a recharge speed of 100 kW and a capacity of 1000 kW*min would mean it takes 10 minutes to fully charge the device.")]
|
[Editable, Serialize(10.0f, IsPropertySaveable.Yes, description: "How fast the device can be recharged. For example, a recharge speed of 100 kW and a capacity of 1000 kW*min would mean it takes 10 minutes to fully charge the device.")]
|
||||||
public float MaxRechargeSpeed
|
public float MaxRechargeSpeed
|
||||||
@@ -157,6 +162,7 @@ namespace Barotrauma.Items.Components
|
|||||||
|
|
||||||
public override void Update(float deltaTime, Camera cam)
|
public override void Update(float deltaTime, Camera cam)
|
||||||
{
|
{
|
||||||
|
adjustedCapacity = GetCapacity();
|
||||||
if (item.Connections == null)
|
if (item.Connections == null)
|
||||||
{
|
{
|
||||||
IsActive = false;
|
IsActive = false;
|
||||||
@@ -164,7 +170,7 @@ namespace Barotrauma.Items.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
float chargeRatio = charge / capacity;
|
float chargeRatio = charge / adjustedCapacity;
|
||||||
|
|
||||||
if (chargeRatio > 0.0f)
|
if (chargeRatio > 0.0f)
|
||||||
{
|
{
|
||||||
@@ -180,7 +186,7 @@ namespace Barotrauma.Items.Components
|
|||||||
item.SendSignal(((int)Math.Round(CurrPowerOutput)).ToString(), "power_value_out");
|
item.SendSignal(((int)Math.Round(CurrPowerOutput)).ToString(), "power_value_out");
|
||||||
item.SendSignal(((int)Math.Round(loadReading)).ToString(), "load_value_out");
|
item.SendSignal(((int)Math.Round(loadReading)).ToString(), "load_value_out");
|
||||||
item.SendSignal(((int)Math.Round(Charge)).ToString(), "charge");
|
item.SendSignal(((int)Math.Round(Charge)).ToString(), "charge");
|
||||||
item.SendSignal(((int)Math.Round(Charge / capacity * 100)).ToString(), "charge_%");
|
item.SendSignal(((int)Math.Round(Charge / adjustedCapacity * 100)).ToString(), "charge_%");
|
||||||
item.SendSignal(((int)Math.Round(RechargeSpeed / maxRechargeSpeed * 100)).ToString(), "charge_rate");
|
item.SendSignal(((int)Math.Round(RechargeSpeed / maxRechargeSpeed * 100)).ToString(), "charge_rate");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,16 +199,16 @@ namespace Barotrauma.Items.Components
|
|||||||
if (connection == powerIn)
|
if (connection == powerIn)
|
||||||
{
|
{
|
||||||
//Don't draw power if fully charged
|
//Don't draw power if fully charged
|
||||||
if (charge >= capacity)
|
if (charge >= adjustedCapacity)
|
||||||
{
|
{
|
||||||
charge = capacity;
|
charge = adjustedCapacity;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (item.Condition <= 0.0f) { return 0.0f; }
|
if (item.Condition <= 0.0f) { return 0.0f; }
|
||||||
|
|
||||||
float missingCharge = capacity - charge;
|
float missingCharge = adjustedCapacity - charge;
|
||||||
float targetRechargeSpeed = rechargeSpeed;
|
float targetRechargeSpeed = rechargeSpeed;
|
||||||
|
|
||||||
if (ExponentialRechargeSpeed)
|
if (ExponentialRechargeSpeed)
|
||||||
@@ -239,7 +245,7 @@ namespace Barotrauma.Items.Components
|
|||||||
if (connection == powerOut)
|
if (connection == powerOut)
|
||||||
{
|
{
|
||||||
float maxOutput;
|
float maxOutput;
|
||||||
float chargeRatio = prevCharge / capacity;
|
float chargeRatio = prevCharge / adjustedCapacity;
|
||||||
if (chargeRatio < 0.1f)
|
if (chargeRatio < 0.1f)
|
||||||
{
|
{
|
||||||
maxOutput = Math.Max(chargeRatio * 10.0f, 0.0f) * MaxOutPut;
|
maxOutput = Math.Max(chargeRatio * 10.0f, 0.0f) * MaxOutPut;
|
||||||
@@ -292,7 +298,7 @@ namespace Barotrauma.Items.Components
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//Decrease charge based on how much power is leaving the device
|
//Decrease charge based on how much power is leaving the device
|
||||||
Charge = Math.Clamp(Charge - CurrPowerOutput / 60 * UpdateInterval, 0, GetCapacity());
|
Charge = Math.Clamp(Charge - CurrPowerOutput / 60 * UpdateInterval, 0, adjustedCapacity);
|
||||||
prevCharge = Charge;
|
prevCharge = Charge;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -639,13 +639,16 @@ namespace Barotrauma.Items.Components
|
|||||||
ItemContainer projectileContainer = projectiles.First().Item.Container?.GetComponent<ItemContainer>();
|
ItemContainer projectileContainer = projectiles.First().Item.Container?.GetComponent<ItemContainer>();
|
||||||
if (projectileContainer != null && projectileContainer.Item != item)
|
if (projectileContainer != null && projectileContainer.Item != item)
|
||||||
{
|
{
|
||||||
projectileContainer.Item.Use(deltaTime, null);
|
|
||||||
//Use root container (e.g. loader) too in case it needs to react to firing somehow
|
//Use root container (e.g. loader) too in case it needs to react to firing somehow
|
||||||
var rootContainer = projectileContainer.Item.GetRootContainer();
|
var rootContainer = projectileContainer.Item.GetRootContainer();
|
||||||
if (rootContainer != projectileContainer.Item)
|
if (rootContainer != null && rootContainer != projectileContainer.Item)
|
||||||
{
|
{
|
||||||
rootContainer.Use(deltaTime, null);
|
rootContainer.Use(deltaTime, null);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
projectileContainer.Item.Use(deltaTime, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -576,11 +576,13 @@ namespace Barotrauma
|
|||||||
if (selectedSlot?.Inventory == this) { selectedSlot.ForceTooltipRefresh = true; }
|
if (selectedSlot?.Inventory == this) { selectedSlot.ForceTooltipRefresh = true; }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
CharacterHUD.RecreateHudTextsIfControlling(user);
|
||||||
|
|
||||||
if (item.body != null)
|
if (item.body != null)
|
||||||
{
|
{
|
||||||
item.body.Enabled = false;
|
item.body.Enabled = false;
|
||||||
item.body.BodyType = FarseerPhysics.BodyType.Dynamic;
|
item.body.BodyType = FarseerPhysics.BodyType.Dynamic;
|
||||||
|
item.SetTransform(item.SimPosition, rotation: 0.0f, findNewHull: false);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if SERVER
|
#if SERVER
|
||||||
@@ -924,6 +926,7 @@ namespace Barotrauma
|
|||||||
if (selectedSlot?.Inventory == this) { selectedSlot.ForceTooltipRefresh = true; }
|
if (selectedSlot?.Inventory == this) { selectedSlot.ForceTooltipRefresh = true; }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
CharacterHUD.RecreateHudTextsIfFocused(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1092,6 +1092,12 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var holdables = components.Where(c => c is Holdable);
|
||||||
|
if (holdables.Count() > 1)
|
||||||
|
{
|
||||||
|
DebugConsole.AddWarning($"Item {Prefab.Identifier} has multiple {nameof(Holdable)} components ({string.Join(", ", holdables.Select(h => h.GetType().Name))}).");
|
||||||
|
}
|
||||||
|
|
||||||
InsertToList();
|
InsertToList();
|
||||||
ItemList.Add(this);
|
ItemList.Add(this);
|
||||||
if (Prefab.IsDangerous) { dangerousItems.Add(this); }
|
if (Prefab.IsDangerous) { dangerousItems.Add(this); }
|
||||||
@@ -1104,7 +1110,6 @@ namespace Barotrauma
|
|||||||
if (HasTag("logic")) { isLogic = true; }
|
if (HasTag("logic")) { isLogic = true; }
|
||||||
|
|
||||||
ApplyStatusEffects(ActionType.OnSpawn, 1.0f);
|
ApplyStatusEffects(ActionType.OnSpawn, 1.0f);
|
||||||
Components.ForEach(c => c.ApplyStatusEffects(ActionType.OnSpawn, 1.0f));
|
|
||||||
RecalculateConditionValues();
|
RecalculateConditionValues();
|
||||||
#if CLIENT
|
#if CLIENT
|
||||||
Submarine.ForceVisibilityRecheck();
|
Submarine.ForceVisibilityRecheck();
|
||||||
@@ -2881,11 +2886,14 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (ItemComponent ic in components) { ic.Equip(character); }
|
foreach (ItemComponent ic in components) { ic.Equip(character); }
|
||||||
|
|
||||||
|
CharacterHUD.RecreateHudTextsIfControlling(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unequip(Character character)
|
public void Unequip(Character character)
|
||||||
{
|
{
|
||||||
foreach (ItemComponent ic in components) { ic.Unequip(character); }
|
foreach (ItemComponent ic in components) { ic.Unequip(character); }
|
||||||
|
CharacterHUD.RecreateHudTextsIfControlling(character);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<(object obj, SerializableProperty property)> GetProperties<T>()
|
public List<(object obj, SerializableProperty property)> GetProperties<T>()
|
||||||
@@ -3427,12 +3435,15 @@ namespace Barotrauma
|
|||||||
//if the item was in full condition considering the unmodified health
|
//if the item was in full condition considering the unmodified health
|
||||||
//(not taking possible HealthMultipliers added by mods into account),
|
//(not taking possible HealthMultipliers added by mods into account),
|
||||||
//make sure it stays in full condition
|
//make sure it stays in full condition
|
||||||
bool wasFullCondition = prevCondition >= item.Prefab.Health;
|
if (item.condition > 0)
|
||||||
if (wasFullCondition)
|
|
||||||
{
|
{
|
||||||
item.condition = item.MaxCondition;
|
bool wasFullCondition = prevCondition >= item.Prefab.Health;
|
||||||
|
if (wasFullCondition)
|
||||||
|
{
|
||||||
|
item.condition = item.MaxCondition;
|
||||||
|
}
|
||||||
|
item.condition = MathHelper.Clamp(item.condition, 0, item.MaxCondition);
|
||||||
}
|
}
|
||||||
item.condition = MathHelper.Clamp(item.condition, 0, item.MaxCondition);
|
|
||||||
}
|
}
|
||||||
item.lastSentCondition = item.condition;
|
item.lastSentCondition = item.condition;
|
||||||
item.RecalculateConditionValues();
|
item.RecalculateConditionValues();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public bool IgnoreInEditor { get; set; }
|
public bool IgnoreInEditor { get; set; }
|
||||||
|
|
||||||
private ImmutableHashSet<Identifier> excludedIdentifiers;
|
public ImmutableHashSet<Identifier> ExcludedIdentifiers { get; private set; }
|
||||||
|
|
||||||
private RelationType type;
|
private RelationType type;
|
||||||
|
|
||||||
@@ -87,20 +87,20 @@ namespace Barotrauma
|
|||||||
|
|
||||||
public string JoinedExcludedIdentifiers
|
public string JoinedExcludedIdentifiers
|
||||||
{
|
{
|
||||||
get { return string.Join(",", excludedIdentifiers); }
|
get { return string.Join(",", ExcludedIdentifiers); }
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value == null) return;
|
if (value == null) return;
|
||||||
|
|
||||||
excludedIdentifiers = value.Split(',').Select(s => s.Trim()).ToIdentifiers().ToImmutableHashSet();
|
ExcludedIdentifiers = value.Split(',').Select(s => s.Trim()).ToIdentifiers().ToImmutableHashSet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool MatchesItem(Item item)
|
public bool MatchesItem(Item item)
|
||||||
{
|
{
|
||||||
if (item == null) { return false; }
|
if (item == null) { return false; }
|
||||||
if (excludedIdentifiers.Contains(item.Prefab.Identifier)) { return false; }
|
if (ExcludedIdentifiers.Contains(item.Prefab.Identifier)) { return false; }
|
||||||
foreach (var excludedIdentifier in excludedIdentifiers)
|
foreach (var excludedIdentifier in ExcludedIdentifiers)
|
||||||
{
|
{
|
||||||
if (item.HasTag(excludedIdentifier)) { return false; }
|
if (item.HasTag(excludedIdentifier)) { return false; }
|
||||||
}
|
}
|
||||||
@@ -118,8 +118,8 @@ namespace Barotrauma
|
|||||||
public bool MatchesItem(ItemPrefab itemPrefab)
|
public bool MatchesItem(ItemPrefab itemPrefab)
|
||||||
{
|
{
|
||||||
if (itemPrefab == null) { return false; }
|
if (itemPrefab == null) { return false; }
|
||||||
if (excludedIdentifiers.Contains(itemPrefab.Identifier)) { return false; }
|
if (ExcludedIdentifiers.Contains(itemPrefab.Identifier)) { return false; }
|
||||||
foreach (var excludedIdentifier in excludedIdentifiers)
|
foreach (var excludedIdentifier in ExcludedIdentifiers)
|
||||||
{
|
{
|
||||||
if (itemPrefab.Tags.Contains(excludedIdentifier)) { return false; }
|
if (itemPrefab.Tags.Contains(excludedIdentifier)) { return false; }
|
||||||
}
|
}
|
||||||
@@ -138,7 +138,7 @@ namespace Barotrauma
|
|||||||
public RelatedItem(Identifier[] identifiers, Identifier[] excludedIdentifiers)
|
public RelatedItem(Identifier[] identifiers, Identifier[] excludedIdentifiers)
|
||||||
{
|
{
|
||||||
this.Identifiers = identifiers.Select(id => id.Value.Trim().ToIdentifier()).ToImmutableHashSet();
|
this.Identifiers = identifiers.Select(id => id.Value.Trim().ToIdentifier()).ToImmutableHashSet();
|
||||||
this.excludedIdentifiers = excludedIdentifiers.Select(id => id.Value.Trim().ToIdentifier()).ToImmutableHashSet();
|
this.ExcludedIdentifiers = excludedIdentifiers.Select(id => id.Value.Trim().ToIdentifier()).ToImmutableHashSet();
|
||||||
|
|
||||||
statusEffects = new List<StatusEffect>();
|
statusEffects = new List<StatusEffect>();
|
||||||
}
|
}
|
||||||
@@ -230,7 +230,7 @@ namespace Barotrauma
|
|||||||
element.Add(new XAttribute(nameof(ItemPos), ItemPos.Value));
|
element.Add(new XAttribute(nameof(ItemPos), ItemPos.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (excludedIdentifiers.Count > 0)
|
if (ExcludedIdentifiers.Count > 0)
|
||||||
{
|
{
|
||||||
element.Add(new XAttribute("excludedidentifiers", JoinedExcludedIdentifiers));
|
element.Add(new XAttribute("excludedidentifiers", JoinedExcludedIdentifiers));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,6 +228,9 @@ namespace Barotrauma
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Vector2 edgeDiff = edge.Point2 - edge.Point1;
|
||||||
|
Vector2 edgeDir = Vector2.Normalize(edgeDiff);
|
||||||
|
|
||||||
//If the edge is next to an empty cell and there's another solid cell at the other side of the empty one,
|
//If the edge is next to an empty cell and there's another solid cell at the other side of the empty one,
|
||||||
//don't touch this edge. Otherwise we may end up closing off small passages between cells.
|
//don't touch this edge. Otherwise we may end up closing off small passages between cells.
|
||||||
var adjacentEmptyCell = edge.AdjacentCell(cell);
|
var adjacentEmptyCell = edge.AdjacentCell(cell);
|
||||||
@@ -238,8 +241,10 @@ namespace Barotrauma
|
|||||||
//find the edge at the opposite side of the adjacent cell
|
//find the edge at the opposite side of the adjacent cell
|
||||||
foreach (GraphEdge otherEdge in adjacentEmptyCell.Edges)
|
foreach (GraphEdge otherEdge in adjacentEmptyCell.Edges)
|
||||||
{
|
{
|
||||||
if (Vector2.Dot(adjacentEmptyCell.Center - edge.Center, adjacentEmptyCell.Center - otherEdge.Center) > 0 &&
|
if (otherEdge == edge || otherEdge.AdjacentCell(adjacentEmptyCell)?.CellType != CellType.Solid) { continue; }
|
||||||
otherEdge.AdjacentCell(adjacentEmptyCell)?.CellType != CellType.Solid)
|
Vector2 otherEdgeDir = Vector2.Normalize(otherEdge.Point2 - otherEdge.Point1);
|
||||||
|
//dot product is > 0.7 if the edges are roughly parallel
|
||||||
|
if (Math.Abs(Vector2.Dot(otherEdgeDir, edgeDir)) > 0.7f)
|
||||||
{
|
{
|
||||||
adjacentEdge = otherEdge;
|
adjacentEdge = otherEdge;
|
||||||
break;
|
break;
|
||||||
@@ -251,13 +256,11 @@ namespace Barotrauma
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Vector2> edgePoints = new List<Vector2>();
|
List<Vector2> edgePoints = new List<Vector2>();
|
||||||
Vector2 edgeNormal = edge.GetNormal(cell);
|
Vector2 edgeNormal = edge.GetNormal(cell);
|
||||||
|
|
||||||
float edgeLength = Vector2.Distance(edge.Point1, edge.Point2);
|
float edgeLength = Vector2.Distance(edge.Point1, edge.Point2);
|
||||||
int pointCount = (int)Math.Max(Math.Ceiling(edgeLength / minEdgeLength), 1);
|
int pointCount = (int)Math.Max(Math.Ceiling(edgeLength / minEdgeLength), 1);
|
||||||
Vector2 edgeDir = edge.Point2 - edge.Point1;
|
|
||||||
for (int i = 0; i <= pointCount; i++)
|
for (int i = 0; i <= pointCount; i++)
|
||||||
{
|
{
|
||||||
if (i == 0)
|
if (i == 0)
|
||||||
@@ -275,7 +278,7 @@ namespace Barotrauma
|
|||||||
float randomVariance = Rand.Range(0, irregularity, Rand.RandSync.ServerAndClient);
|
float randomVariance = Rand.Range(0, irregularity, Rand.RandSync.ServerAndClient);
|
||||||
Vector2 extrudedPoint =
|
Vector2 extrudedPoint =
|
||||||
edge.Point1 +
|
edge.Point1 +
|
||||||
edgeDir * (i / (float)pointCount) +
|
edgeDiff * (i / (float)pointCount) +
|
||||||
edgeNormal * edgeLength * (roundingAmount + randomVariance) * centerF;
|
edgeNormal * edgeLength * (roundingAmount + randomVariance) * centerF;
|
||||||
|
|
||||||
var nearbyCells = Level.Loaded.GetCells(extrudedPoint, searchDepth: 2);
|
var nearbyCells = Level.Loaded.GetCells(extrudedPoint, searchDepth: 2);
|
||||||
|
|||||||
@@ -709,7 +709,7 @@ namespace Barotrauma
|
|||||||
if (Rand.Range(0, 10, Rand.RandSync.ServerAndClient) != 0) { continue; }
|
if (Rand.Range(0, 10, Rand.RandSync.ServerAndClient) != 0) { continue; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TooClose(siteX, siteY))
|
if (!TooCloseToOtherSites(siteX, siteY))
|
||||||
{
|
{
|
||||||
siteCoordsX.Add(siteX);
|
siteCoordsX.Add(siteX);
|
||||||
siteCoordsY.Add(siteY);
|
siteCoordsY.Add(siteY);
|
||||||
@@ -717,14 +717,14 @@ namespace Barotrauma
|
|||||||
|
|
||||||
if (closeToCave)
|
if (closeToCave)
|
||||||
{
|
{
|
||||||
for (int x2 = x; x2 < x + siteInterval.X; x2 += caveSiteInterval)
|
for (int x2 = x - siteInterval.X; x2 < x + siteInterval.X; x2 += caveSiteInterval)
|
||||||
{
|
{
|
||||||
for (int y2 = y; y2 < y + siteInterval.Y; y2 += caveSiteInterval)
|
for (int y2 = y - siteInterval.Y; y2 < y + siteInterval.Y; y2 += caveSiteInterval)
|
||||||
{
|
{
|
||||||
int caveSiteX = x2 + Rand.Int(caveSiteInterval / 2, Rand.RandSync.ServerAndClient);
|
int caveSiteX = x2 + Rand.Int(caveSiteInterval / 2, Rand.RandSync.ServerAndClient);
|
||||||
int caveSiteY = y2 + Rand.Int(caveSiteInterval / 2, Rand.RandSync.ServerAndClient);
|
int caveSiteY = y2 + Rand.Int(caveSiteInterval / 2, Rand.RandSync.ServerAndClient);
|
||||||
|
|
||||||
if (!TooClose(caveSiteX, caveSiteY))
|
if (!TooCloseToOtherSites(caveSiteX, caveSiteY, caveSiteInterval))
|
||||||
{
|
{
|
||||||
siteCoordsX.Add(caveSiteX);
|
siteCoordsX.Add(caveSiteX);
|
||||||
siteCoordsY.Add(caveSiteY);
|
siteCoordsY.Add(caveSiteY);
|
||||||
@@ -735,11 +735,12 @@ namespace Barotrauma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TooClose(double siteX, double siteY)
|
bool TooCloseToOtherSites(double siteX, double siteY, float minDistance = 10.0f)
|
||||||
{
|
{
|
||||||
|
float minDistanceSqr = minDistance * minDistance;
|
||||||
for (int i = 0; i < siteCoordsX.Count; i++)
|
for (int i = 0; i < siteCoordsX.Count; i++)
|
||||||
{
|
{
|
||||||
if (MathUtils.DistanceSquared(siteCoordsX[i], siteCoordsY[i], siteX, siteY) < 10.0f * 10.0f)
|
if (MathUtils.DistanceSquared(siteCoordsX[i], siteCoordsY[i], siteX, siteY) < minDistanceSqr)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -720,7 +720,7 @@ namespace Barotrauma
|
|||||||
|
|
||||||
private void HandleLevelCollision(Impact impact, VoronoiCell cell = null)
|
private void HandleLevelCollision(Impact impact, VoronoiCell cell = null)
|
||||||
{
|
{
|
||||||
if (GameMain.GameSession != null && GameMain.GameSession.RoundDuration > 10)
|
if (GameMain.GameSession != null && GameMain.GameSession.RoundDuration < 10)
|
||||||
{
|
{
|
||||||
//ignore level collisions for the first 10 seconds of the round in case the sub spawns in a way that causes it to hit a wall
|
//ignore level collisions for the first 10 seconds of the round in case the sub spawns in a way that causes it to hit a wall
|
||||||
//(e.g. level without outposts to dock to and an incorrectly configured ballast that makes the sub go up)
|
//(e.g. level without outposts to dock to and an incorrectly configured ballast that makes the sub go up)
|
||||||
|
|||||||
@@ -23,13 +23,22 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
Success = 0x00,
|
Success = 0x00,
|
||||||
Heartbeat = 0x01,
|
Heartbeat = 0x01,
|
||||||
|
RequestShutdown = 0xCC,
|
||||||
Crash = 0xFF
|
Crash = 0xFF
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ManualResetEvent writeManualResetEvent;
|
private static ManualResetEvent writeManualResetEvent;
|
||||||
|
|
||||||
private static volatile bool shutDown;
|
private enum StatusEnum
|
||||||
public static bool HasShutDown => shutDown;
|
{
|
||||||
|
NeverStarted,
|
||||||
|
Active,
|
||||||
|
RequestedShutDown,
|
||||||
|
ShutDown
|
||||||
|
}
|
||||||
|
|
||||||
|
private static volatile StatusEnum status = StatusEnum.NeverStarted;
|
||||||
|
public static bool HasShutDown => status is StatusEnum.ShutDown;
|
||||||
|
|
||||||
private const int ReadBufferSize = MsgConstants.MTU * 2;
|
private const int ReadBufferSize = MsgConstants.MTU * 2;
|
||||||
private static byte[] readTempBytes;
|
private static byte[] readTempBytes;
|
||||||
@@ -48,6 +57,8 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
private static void PrivateStart()
|
private static void PrivateStart()
|
||||||
{
|
{
|
||||||
|
status = StatusEnum.Active;
|
||||||
|
|
||||||
readIncOffset = 0;
|
readIncOffset = 0;
|
||||||
readIncTotal = 0;
|
readIncTotal = 0;
|
||||||
|
|
||||||
@@ -58,8 +69,6 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
msgsToRead = new ConcurrentQueue<byte[]>();
|
msgsToRead = new ConcurrentQueue<byte[]>();
|
||||||
|
|
||||||
shutDown = false;
|
|
||||||
|
|
||||||
readCancellationToken = new CancellationTokenSource();
|
readCancellationToken = new CancellationTokenSource();
|
||||||
|
|
||||||
writeManualResetEvent = new ManualResetEvent(false);
|
writeManualResetEvent = new ManualResetEvent(false);
|
||||||
@@ -80,7 +89,13 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
private static void PrivateShutDown()
|
private static void PrivateShutDown()
|
||||||
{
|
{
|
||||||
shutDown = true;
|
if (Thread.CurrentThread != GameMain.MainThread)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Cannot call {nameof(ChildServerRelay)}.{nameof(PrivateShutDown)} from a thread other than the main one");
|
||||||
|
}
|
||||||
|
if (status is StatusEnum.NeverStarted) { return; }
|
||||||
|
status = StatusEnum.ShutDown;
|
||||||
writeManualResetEvent?.Set();
|
writeManualResetEvent?.Set();
|
||||||
readCancellationToken?.Cancel();
|
readCancellationToken?.Cancel();
|
||||||
readThread?.Join(); readThread = null;
|
readThread?.Join(); readThread = null;
|
||||||
@@ -93,18 +108,18 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static int ReadIncomingMsgs()
|
private static Option<int> ReadIncomingMsgs()
|
||||||
{
|
{
|
||||||
Task<int> readTask = readStream?.ReadAsync(readTempBytes, 0, readTempBytes.Length, readCancellationToken.Token);
|
Task<int> readTask = readStream?.ReadAsync(readTempBytes, 0, readTempBytes.Length, readCancellationToken.Token);
|
||||||
if (readTask is null) { return -1; }
|
if (readTask is null) { return Option<int>.None(); }
|
||||||
|
|
||||||
int timeOutMilliseconds = 100;
|
int timeOutMilliseconds = 100;
|
||||||
for (int i = 0; i < 150; i++)
|
for (int i = 0; i < 150; i++)
|
||||||
{
|
{
|
||||||
if (shutDown)
|
if (status is StatusEnum.ShutDown)
|
||||||
{
|
{
|
||||||
readCancellationToken?.Cancel();
|
readCancellationToken?.Cancel();
|
||||||
return -1;
|
return Option<int>.None();
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -115,36 +130,36 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
catch (AggregateException aggregateException)
|
catch (AggregateException aggregateException)
|
||||||
{
|
{
|
||||||
if (aggregateException.InnerException is OperationCanceledException) { return -1; }
|
if (aggregateException.InnerException is OperationCanceledException) { return Option<int>.None(); }
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
return -1;
|
return Option<int>.None();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (readTask.Status != TaskStatus.RanToCompletion)
|
if (readTask.Status == TaskStatus.RanToCompletion)
|
||||||
{
|
{
|
||||||
bool swallowException = shutDown
|
return Option<int>.Some(readTask.Result);
|
||||||
&& ((readTask.Exception?.InnerException is ObjectDisposedException)
|
|
||||||
|| (readTask.Exception?.InnerException is System.IO.IOException));
|
|
||||||
if (swallowException)
|
|
||||||
{
|
|
||||||
readCancellationToken?.Cancel();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
throw new Exception(
|
|
||||||
$"ChildServerRelay readTask did not run to completion: status was {readTask.Status}.",
|
|
||||||
readTask.Exception);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return readTask.Result;
|
bool swallowException =
|
||||||
|
status is not StatusEnum.Active
|
||||||
|
&& readTask.Exception?.InnerException is ObjectDisposedException or System.IO.IOException;
|
||||||
|
if (swallowException)
|
||||||
|
{
|
||||||
|
readCancellationToken?.Cancel();
|
||||||
|
return Option<int>.None();
|
||||||
|
}
|
||||||
|
throw new Exception(
|
||||||
|
$"ChildServerRelay readTask did not run to completion: status was {readTask.Status}.",
|
||||||
|
readTask.Exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void CheckPipeConnected(string name, PipeType pipe)
|
private static void CheckPipeConnected(string name, PipeType pipe)
|
||||||
{
|
{
|
||||||
if (!(pipe is { IsConnected: true }))
|
if (status is StatusEnum.Active && pipe is not { IsConnected: true })
|
||||||
{
|
{
|
||||||
throw new Exception($"{name} was disconnected unexpectedly");
|
throw new Exception($"{name} was disconnected unexpectedly");
|
||||||
}
|
}
|
||||||
@@ -155,7 +170,7 @@ namespace Barotrauma.Networking
|
|||||||
private static void UpdateRead()
|
private static void UpdateRead()
|
||||||
{
|
{
|
||||||
Span<byte> msgLengthSpan = stackalloc byte[4 + 1];
|
Span<byte> msgLengthSpan = stackalloc byte[4 + 1];
|
||||||
while (!shutDown)
|
while (!HasShutDown)
|
||||||
{
|
{
|
||||||
CheckPipeConnected(nameof(readStream), readStream);
|
CheckPipeConnected(nameof(readStream), readStream);
|
||||||
|
|
||||||
@@ -165,10 +180,9 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
if (readIncOffset >= readIncTotal)
|
if (readIncOffset >= readIncTotal)
|
||||||
{
|
{
|
||||||
readIncTotal = ReadIncomingMsgs();
|
if (!ReadIncomingMsgs().TryUnwrap(out readIncTotal)) { return false; }
|
||||||
readIncOffset = 0;
|
readIncOffset = 0;
|
||||||
if (readIncTotal == 0) { Thread.Yield(); continue; }
|
if (readIncTotal == 0) { Thread.Yield(); continue; }
|
||||||
if (readIncTotal < 0) { return false; }
|
|
||||||
}
|
}
|
||||||
readTo[i] = readTempBytes[readIncOffset];
|
readTo[i] = readTempBytes[readIncOffset];
|
||||||
readIncOffset++;
|
readIncOffset++;
|
||||||
@@ -176,7 +190,7 @@ namespace Barotrauma.Networking
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!readBytes(msgLengthSpan)) { shutDown = true; break; }
|
if (!readBytes(msgLengthSpan)) { status = StatusEnum.ShutDown; break; }
|
||||||
|
|
||||||
int msgLength = msgLengthSpan[0]
|
int msgLength = msgLengthSpan[0]
|
||||||
| (msgLengthSpan[1] << 8)
|
| (msgLengthSpan[1] << 8)
|
||||||
@@ -184,24 +198,24 @@ namespace Barotrauma.Networking
|
|||||||
| (msgLengthSpan[3] << 24);
|
| (msgLengthSpan[3] << 24);
|
||||||
WriteStatus writeStatus = (WriteStatus)msgLengthSpan[4];
|
WriteStatus writeStatus = (WriteStatus)msgLengthSpan[4];
|
||||||
|
|
||||||
if (msgLength > 0)
|
byte[] msg = msgLength > 0 ? new byte[msgLength] : Array.Empty<byte>();
|
||||||
{
|
if (msg.Length > 0 && !readBytes(msg.AsSpan())) { status = StatusEnum.ShutDown; break; }
|
||||||
byte[] msg = new byte[msgLength];
|
|
||||||
if (!readBytes(msg.AsSpan())) { shutDown = true; break; }
|
|
||||||
|
|
||||||
switch (writeStatus)
|
switch (writeStatus)
|
||||||
{
|
{
|
||||||
case WriteStatus.Success:
|
case WriteStatus.Success:
|
||||||
msgsToRead.Enqueue(msg);
|
msgsToRead.Enqueue(msg);
|
||||||
break;
|
break;
|
||||||
case WriteStatus.Heartbeat:
|
case WriteStatus.Heartbeat:
|
||||||
//do nothing
|
//do nothing
|
||||||
break;
|
break;
|
||||||
case WriteStatus.Crash:
|
case WriteStatus.RequestShutdown:
|
||||||
HandleCrashString(Encoding.UTF8.GetString(msg));
|
status = StatusEnum.ShutDown;
|
||||||
shutDown = true;
|
break;
|
||||||
break;
|
case WriteStatus.Crash:
|
||||||
}
|
HandleCrashString(Encoding.UTF8.GetString(msg));
|
||||||
|
status = StatusEnum.ShutDown;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.Yield();
|
Thread.Yield();
|
||||||
@@ -210,13 +224,11 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
private static void UpdateWrite()
|
private static void UpdateWrite()
|
||||||
{
|
{
|
||||||
while (!shutDown)
|
while (!HasShutDown)
|
||||||
{
|
{
|
||||||
CheckPipeConnected(nameof(writeStream), writeStream);
|
CheckPipeConnected(nameof(writeStream), writeStream);
|
||||||
|
|
||||||
byte[] msg;
|
void writeMsg(WriteStatus writeStatus, byte[] msg)
|
||||||
|
|
||||||
void writeMsg(WriteStatus writeStatus)
|
|
||||||
{
|
{
|
||||||
// It's SUPER IMPORTANT that this stack allocation
|
// It's SUPER IMPORTANT that this stack allocation
|
||||||
// remains in this local function and is never inlined,
|
// remains in this local function and is never inlined,
|
||||||
@@ -224,21 +236,19 @@ namespace Barotrauma.Networking
|
|||||||
// when the function returns; placing it in the loop
|
// when the function returns; placing it in the loop
|
||||||
// this method is based around would lead to a stack
|
// this method is based around would lead to a stack
|
||||||
// overflow real quick!
|
// overflow real quick!
|
||||||
Span<byte> bytesToWrite = stackalloc byte[4 + 1 + msg.Length];
|
Span<byte> headerBytes = stackalloc byte[4 + 1];
|
||||||
|
|
||||||
bytesToWrite[0] = (byte)(msg.Length & 0xFF);
|
headerBytes[0] = (byte)(msg.Length & 0xFF);
|
||||||
bytesToWrite[1] = (byte)((msg.Length >> 8) & 0xFF);
|
headerBytes[1] = (byte)((msg.Length >> 8) & 0xFF);
|
||||||
bytesToWrite[2] = (byte)((msg.Length >> 16) & 0xFF);
|
headerBytes[2] = (byte)((msg.Length >> 16) & 0xFF);
|
||||||
bytesToWrite[3] = (byte)((msg.Length >> 24) & 0xFF);
|
headerBytes[3] = (byte)((msg.Length >> 24) & 0xFF);
|
||||||
|
|
||||||
bytesToWrite[4] = (byte)writeStatus;
|
headerBytes[4] = (byte)writeStatus;
|
||||||
Span<byte> msgSlice = bytesToWrite.Slice(4 + 1, msg.Length);
|
|
||||||
|
|
||||||
msg.AsSpan().CopyTo(msgSlice);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
writeStream?.Write(bytesToWrite);
|
writeStream?.Write(headerBytes);
|
||||||
|
writeStream?.Write(msg);
|
||||||
}
|
}
|
||||||
catch (Exception exception)
|
catch (Exception exception)
|
||||||
{
|
{
|
||||||
@@ -246,7 +256,7 @@ namespace Barotrauma.Networking
|
|||||||
{
|
{
|
||||||
case ObjectDisposedException _:
|
case ObjectDisposedException _:
|
||||||
case System.IO.IOException _:
|
case System.IO.IOException _:
|
||||||
if (!shutDown) { throw; }
|
if (!HasShutDown) { throw; }
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw;
|
throw;
|
||||||
@@ -254,29 +264,34 @@ namespace Barotrauma.Networking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status is StatusEnum.RequestedShutDown)
|
||||||
|
{
|
||||||
|
writeMsg(WriteStatus.RequestShutdown, Array.Empty<byte>());
|
||||||
|
status = StatusEnum.ShutDown;
|
||||||
|
}
|
||||||
|
|
||||||
while (errorsToWrite.TryDequeue(out var error))
|
while (errorsToWrite.TryDequeue(out var error))
|
||||||
{
|
{
|
||||||
msg = Encoding.UTF8.GetBytes(error);
|
writeMsg(WriteStatus.Crash, Encoding.UTF8.GetBytes(error));
|
||||||
writeMsg(WriteStatus.Crash);
|
status = StatusEnum.ShutDown;
|
||||||
shutDown = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (msgsToWrite.TryDequeue(out msg))
|
while (msgsToWrite.TryDequeue(out var msg))
|
||||||
{
|
{
|
||||||
writeMsg(WriteStatus.Success);
|
writeMsg(WriteStatus.Success, msg);
|
||||||
|
|
||||||
if (shutDown) { break; }
|
if (HasShutDown) { break; }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!shutDown)
|
if (!HasShutDown)
|
||||||
{
|
{
|
||||||
writeManualResetEvent.Reset();
|
writeManualResetEvent.Reset();
|
||||||
if (!writeManualResetEvent.WaitOne(1000))
|
if (!writeManualResetEvent.WaitOne(1000))
|
||||||
{
|
{
|
||||||
if (shutDown) { return; }
|
if (HasShutDown) { return; }
|
||||||
|
|
||||||
//heartbeat to keep the other end alive
|
//heartbeat to keep the other end alive
|
||||||
msg = Array.Empty<byte>(); writeMsg(WriteStatus.Heartbeat);
|
writeMsg(WriteStatus.Heartbeat, Array.Empty<byte>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,7 +299,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
public static void Write(byte[] msg)
|
public static void Write(byte[] msg)
|
||||||
{
|
{
|
||||||
if (shutDown) { return; }
|
if (HasShutDown) { return; }
|
||||||
|
|
||||||
if (msg.Length > 0x1fff_ffff)
|
if (msg.Length > 0x1fff_ffff)
|
||||||
{
|
{
|
||||||
@@ -298,7 +313,7 @@ namespace Barotrauma.Networking
|
|||||||
|
|
||||||
public static bool Read(out byte[] msg)
|
public static bool Read(out byte[] msg)
|
||||||
{
|
{
|
||||||
if (shutDown) { msg = null; return false; }
|
if (HasShutDown) { msg = null; return false; }
|
||||||
|
|
||||||
return msgsToRead.TryDequeue(out msg);
|
return msgsToRead.TryDequeue(out msg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,62 @@
|
|||||||
|
---------------------------------------------------------------------------------------------------------
|
||||||
|
v0.20.9.0
|
||||||
|
---------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Changes and additions:
|
||||||
|
- Added a button to the main menu that can be used to update all installed mods when there's updates available.
|
||||||
|
- Optimized the server lobby: there was an issue in the logic that updates the microphone icon that caused the game to check available audio devices every frame.
|
||||||
|
- Optimized status monitors: previously some parts of their UI were always updated regardless if anyone is viewing the UI.
|
||||||
|
- Made flak cannons a bit more quiet (so they don't drown out all other sounds).
|
||||||
|
|
||||||
|
Unstable only:
|
||||||
|
- Don't allow placing most of the new creature loot in cabinets/containers.
|
||||||
|
- Fixed some more vanilla subs' reactors having 1000 condition instead of 100.
|
||||||
|
- Adjustments to Harpoon Coil-Rifle sounds.
|
||||||
|
- Two new music tracks (one "default" one for missions and another for colonies).
|
||||||
|
- Fixed no events triggering in the first campaign outpost.
|
||||||
|
- Fixed crashing when firing a turret that doesn't use ammo boxes + ammo boxes getting used twice when firing.
|
||||||
|
- Fixed water-reactant items emitting particles when submerged even when inside a waterproof container + optimized the effects a bit.
|
||||||
|
- Allow recycling ammo boxes that are below 10% full.
|
||||||
|
- Fixed autoshotgun's ammunition indicator showing 1 item less than it should when there's no flashlight in the flashlight slot.
|
||||||
|
- Fixed "censorship" event (in which a clown asks you to retrieve a confiscated crate) being impossible to complete.
|
||||||
|
- Fixed crash when opening the fabricator UI in the sub editor.
|
||||||
|
- Improvements to the new beacon station clown event.
|
||||||
|
- Fixed passive sonar still revealing more than it should when there's directional pings active.
|
||||||
|
- Fixed inventory being visible when using a periscope in the sub editor test mode.
|
||||||
|
- Fixed inability to get out of the clown crate in multiplayer.
|
||||||
|
- Fixed "Lab Contacts" increasing the medical fabrication speed instead of reducing it.
|
||||||
|
- Fixed "All talents unlocked" showing before you had unlocked all talents.
|
||||||
|
- Fixed "Tinkerer" not working at all.
|
||||||
|
- Fixed "Quickfixer" not doubling the repair speed.
|
||||||
|
- Fixed exosuit draining the battery when not worn.
|
||||||
|
- Fixed exosuit consuming all contained tanks when there's a battery in it.
|
||||||
|
- Fixed bots failing to swap tanks in the exosuit.
|
||||||
|
- Removed the flashlight slot from the machine pistol.
|
||||||
|
- Fixed Defensebot's movement outside of the submarine.
|
||||||
|
- Petraptors poop.
|
||||||
|
- Fix a console error after a loaded game when a defense bot is present. Happened because we tried to spawn the initial items in the inventory, which was full.
|
||||||
|
- Fixed softlock if you get killed by barotrauma when under the effect of Miracle Worker.
|
||||||
|
- Fixed fabricator displaying items that can be crafted using a bot's talents as requiring a recipe to craft.
|
||||||
|
- Fixed fabricating nuclear shells and nuclear depth charges with the cheaper recipe unlocked by "Nuclear Option" not fully using up the fuel rods.
|
||||||
|
|
||||||
|
Bugfixes:
|
||||||
|
- Fixed occasional crashes when shutting down a server (for example with the error messages "pipe is broken" or "ChildServerRelay readTask did not run to completion")
|
||||||
|
- Fixed junction boxes not getting damaged by water since the power rework.
|
||||||
|
- Fixed opiate withdrawal only reducing down to 20%, but never fully healing by itself.
|
||||||
|
- Fixed engines reverting back to the non-damaged sprite when they're damaged badly enough that the sprite starts shaking.
|
||||||
|
- Fixed walls being set up incorrectly in vertical abandoned outpost hallway modules, causing them to stick out into the connected modules.
|
||||||
|
- Attempt to fix items sometimes ending up rotated inside a container (e.g. diving suit sprite appearing rotated on a diving suit locker).
|
||||||
|
- Fixed "man and his raptor" outpost event giving 1000 marks in an incorrect branch of the dialog (the one where you immediately accept the NPC on board, instead of the one where the NPC says they'll pay you 1000 mk).
|
||||||
|
- Fixed cases of interaction texts for focused item (most notoriously, the planter) not being updated correctly.
|
||||||
|
- Fixed "snap to grid" causing door gaps to get misaligned.
|
||||||
|
- Fixed weird equipping behavior on fruit and paints, causing them to be equipped in both hands when trying to unequip.
|
||||||
|
- Yet another fix to cave tunnels sometimes being too narrow to pass through.
|
||||||
|
- Fixed minerals sometimes being placed outside the level in mineral missions.
|
||||||
|
- Fixed reactor temperature boost not working in multiplayer.
|
||||||
|
|
||||||
|
Modding:
|
||||||
|
- Fixed item's OnSpawn effects being applied twice.
|
||||||
|
|
||||||
---------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------
|
||||||
v0.20.8.0
|
v0.20.8.0
|
||||||
---------------------------------------------------------------------------------------------------------
|
---------------------------------------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user