Faction Test 100.8.0.0
This commit is contained in:
@@ -9,7 +9,7 @@ using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class CharacterHUD
|
||||
partial class CharacterHUD
|
||||
{
|
||||
const float BossHealthBarDuration = 120.0f;
|
||||
|
||||
@@ -147,8 +147,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ShouldRecreateHudTexts { get; set; } = true;
|
||||
private static bool heldDownShiftWhenGotHudTexts;
|
||||
public static bool RecreateHudTexts { get; set; } = true;
|
||||
private static bool lastHudTextsContextual;
|
||||
private static float timeHealthWindowClosed;
|
||||
|
||||
public static bool IsCampaignInterfaceOpen =>
|
||||
@@ -266,7 +266,7 @@ namespace Barotrauma
|
||||
if (focusedItemOverlayTimer <= 0.0f)
|
||||
{
|
||||
focusedItem = null;
|
||||
ShouldRecreateHudTexts = true;
|
||||
RecreateHudTexts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -388,7 +388,7 @@ namespace Barotrauma
|
||||
if (focusedItem != character.FocusedItem)
|
||||
{
|
||||
focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer);
|
||||
ShouldRecreateHudTexts = true;
|
||||
RecreateHudTexts = true;
|
||||
}
|
||||
focusedItem = character.FocusedItem;
|
||||
}
|
||||
@@ -412,14 +412,14 @@ namespace Barotrauma
|
||||
|
||||
if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld)
|
||||
{
|
||||
bool shiftDown = PlayerInput.IsShiftDown();
|
||||
if (ShouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown)
|
||||
bool hudTextsContextual = PlayerInput.IsShiftDown();
|
||||
if (RecreateHudTexts || lastHudTextsContextual != hudTextsContextual)
|
||||
{
|
||||
ShouldRecreateHudTexts = true;
|
||||
heldDownShiftWhenGotHudTexts = shiftDown;
|
||||
RecreateHudTexts = true;
|
||||
lastHudTextsContextual = hudTextsContextual;
|
||||
}
|
||||
var hudTexts = focusedItem.GetHUDTexts(character, ShouldRecreateHudTexts);
|
||||
ShouldRecreateHudTexts = false;
|
||||
var hudTexts = focusedItem.GetHUDTexts(character, RecreateHudTexts);
|
||||
RecreateHudTexts = false;
|
||||
|
||||
int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X);
|
||||
|
||||
@@ -864,5 +864,25 @@ namespace Barotrauma
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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();
|
||||
|
||||
var specializationCount = tree.TalentSubTrees.Count(t => t.Type == TalentTreeType.Specialization);
|
||||
|
||||
List<GUITextBlock> subTreeNames = new List<GUITextBlock>();
|
||||
foreach (var subTree in tree.TalentSubTrees)
|
||||
{
|
||||
@@ -310,7 +312,7 @@ namespace Barotrauma
|
||||
for (int i = 0; i < optionAmount; 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.Children.Sum(c => c.Rect.Height + subTreeLayoutGroup.AbsoluteSpacing)));
|
||||
@@ -327,7 +329,12 @@ namespace Barotrauma
|
||||
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
|
||||
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);
|
||||
|
||||
@@ -337,17 +344,17 @@ namespace Barotrauma
|
||||
{
|
||||
return specList;
|
||||
}
|
||||
|
||||
GUIListBox newSpecializationList = new GUIListBox(new RectTransform(new Vector2(1.0f, 0.5f), mainList.Content.RectTransform, Anchor.TopCenter), isHorizontal: true, style: null);
|
||||
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 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)
|
||||
{ MinSize = new Point(0, GUI.IntScale(65)) }, style: null);
|
||||
{ MinSize = new Point(0, height) }, style: null);
|
||||
|
||||
Point talentFrameSize = talentOptionFrame.RectTransform.NonScaledSize;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
@@ -117,7 +118,7 @@ namespace Barotrauma
|
||||
private void UpdateBar()
|
||||
{
|
||||
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)
|
||||
{
|
||||
@@ -129,7 +130,7 @@ namespace Barotrauma
|
||||
int second = (int)Math.Ceiling(elapsedTime);
|
||||
if (second > lastSecond)
|
||||
{
|
||||
if (msgBox != null && !msgBox.Closed)
|
||||
if (msgBox is { Closed: false })
|
||||
{
|
||||
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)
|
||||
{
|
||||
ReadyCheckState state = (ReadyCheckState)inc.ReadByte();
|
||||
@@ -154,6 +168,8 @@ namespace Barotrauma
|
||||
switch (state)
|
||||
{
|
||||
case ReadyCheckState.Start:
|
||||
CloseLingeringPopups();
|
||||
|
||||
bool isOwn = false;
|
||||
byte authorId = 0;
|
||||
|
||||
@@ -175,8 +191,8 @@ namespace Barotrauma
|
||||
clients.Add(inc.ReadByte());
|
||||
}
|
||||
|
||||
ReadyCheck rCheck = new ReadyCheck(clients,
|
||||
DateTimeOffset.FromUnixTimeSeconds(startTime).LocalDateTime,
|
||||
ReadyCheck rCheck = new ReadyCheck(clients,
|
||||
DateTimeOffset.FromUnixTimeSeconds(startTime).LocalDateTime,
|
||||
DateTimeOffset.FromUnixTimeSeconds(endTime).LocalDateTime);
|
||||
crewManager.ActiveReadyCheck = rCheck;
|
||||
|
||||
@@ -224,7 +240,7 @@ namespace Barotrauma
|
||||
if (IsFinished) { return; }
|
||||
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;
|
||||
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.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?
|
||||
foreach (GUIComponent child in userList.Content.Children)
|
||||
if (child.UserData is not byte b || b != id) { continue; }
|
||||
|
||||
if (child.GetChild<GUILayoutGroup>().FindChild(ReadySpriteData) is not GUIImage image) { continue; }
|
||||
|
||||
string style;
|
||||
switch (status)
|
||||
{
|
||||
if (!(child.UserData is byte b) || b != id) { continue; }
|
||||
|
||||
if (child.GetChild<GUILayoutGroup>().FindChild(ReadySpriteData) is GUIImage image)
|
||||
{
|
||||
string style;
|
||||
switch (status)
|
||||
{
|
||||
case ReadyStatus.Yes:
|
||||
style = "MissionCompletedIcon";
|
||||
break;
|
||||
case ReadyStatus.No:
|
||||
style = "MissionFailedIcon";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
image.ApplyStyle(GUIStyle.GetComponentStyle(style));
|
||||
}
|
||||
case ReadyStatus.Yes:
|
||||
style = "MissionCompletedIcon";
|
||||
break;
|
||||
case ReadyStatus.No:
|
||||
style = "MissionFailedIcon";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
image.ApplyStyle(GUIStyle.GetComponentStyle(style));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -327,17 +327,20 @@ namespace Barotrauma.Items.Components
|
||||
var item1 = c1.GUIComponent.UserData as FabricationRecipe;
|
||||
var item2 = c2.GUIComponent.UserData as FabricationRecipe;
|
||||
|
||||
int itemPlacement1 = FabricationDegreeOfSuccess(character, item1.RequiredSkills) >= 0.5f ? 0 : -1;
|
||||
int itemPlacement2 = FabricationDegreeOfSuccess(character, item2.RequiredSkills) >= 0.5f ? 0 : -1;
|
||||
|
||||
itemPlacement1 += item1.RequiresRecipe && !character.HasRecipeForItem(item1.TargetItem.Identifier) ? -2 : 0;
|
||||
itemPlacement2 += item2.RequiresRecipe && !character.HasRecipeForItem(item2.TargetItem.Identifier) ? -2 : 0;
|
||||
|
||||
int itemPlacement1 = calculatePlacement(item1);
|
||||
int itemPlacement2 = calculatePlacement(item2);
|
||||
if (itemPlacement1 != itemPlacement2)
|
||||
{
|
||||
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);
|
||||
});
|
||||
|
||||
@@ -372,7 +375,9 @@ namespace Barotrauma.Items.Components
|
||||
AutoScaleHorizontal = true,
|
||||
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)
|
||||
{
|
||||
requiresRecipeText.RectTransform.RepositionChildInHierarchy(itemList.Content.RectTransform.GetChildIndex(firstRequiresRecipe.RectTransform));
|
||||
|
||||
@@ -144,6 +144,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial class MiniMap : Powered
|
||||
{
|
||||
private Dictionary<Hull, HullData> hullDatas;
|
||||
private DateTime resetDataTime;
|
||||
|
||||
private GUIFrame submarineContainer;
|
||||
|
||||
private GUIFrame? hullInfoFrame;
|
||||
@@ -226,6 +229,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void InitProjSpecific()
|
||||
{
|
||||
hullDatas = new Dictionary<Hull, HullData>();
|
||||
|
||||
SetDefaultMode();
|
||||
|
||||
noPowerTip = TextManager.Get("SteeringNoPowerTip");
|
||||
@@ -551,6 +556,34 @@ namespace Barotrauma.Items.Components
|
||||
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 (PlayerInput.PrimaryMouseButtonDown() && currentMode != MiniMapMode.HullStatus)
|
||||
@@ -1736,6 +1769,67 @@ namespace Barotrauma.Items.Components
|
||||
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()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
|
||||
@@ -396,7 +396,9 @@ namespace Barotrauma.Items.Components
|
||||
ToolTip = TextManager.Get("reactor.temperatureboostup"),
|
||||
OnClicked = (_, __) =>
|
||||
{
|
||||
applyTemperatureBoost(TemperatureBoostAmount, temperatureBoostSoundUp);
|
||||
unsentChanges = true;
|
||||
sendUpdateTimer = 0.0f;
|
||||
ApplyTemperatureBoost(TemperatureBoostAmount);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -407,25 +409,13 @@ namespace Barotrauma.Items.Components
|
||||
ToolTip = TextManager.Get("reactor.temperatureboostdown"),
|
||||
OnClicked = (_, __) =>
|
||||
{
|
||||
applyTemperatureBoost(-TemperatureBoostAmount, temperatureBoostSoundDown);
|
||||
unsentChanges = true;
|
||||
sendUpdateTimer = 0.0f;
|
||||
ApplyTemperatureBoost(-TemperatureBoostAmount);
|
||||
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))
|
||||
{
|
||||
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()
|
||||
{
|
||||
@@ -895,6 +903,7 @@ namespace Barotrauma.Items.Components
|
||||
msg.WriteBoolean(PowerOn);
|
||||
msg.WriteRangedSingle(TargetFissionRate, 0.0f, 100.0f, 8);
|
||||
msg.WriteRangedSingle(TargetTurbineOutput, 0.0f, 100.0f, 8);
|
||||
msg.WriteRangedSingle(temperatureBoost, -TemperatureBoostAmount, TemperatureBoostAmount, 8);
|
||||
|
||||
correctionTimer = CorrectionDelay;
|
||||
}
|
||||
@@ -903,7 +912,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -913,6 +922,7 @@ namespace Barotrauma.Items.Components
|
||||
TargetFissionRate = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||
TargetTurbineOutput = msg.ReadRangedSingle(0.0f, 100.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)
|
||||
{
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace Barotrauma.Items.Components
|
||||
private float displayBorderSize;
|
||||
|
||||
private List<SonarBlip> sonarBlips;
|
||||
private readonly HashSet<SonarBlip> prevBlips = new HashSet<SonarBlip>();
|
||||
|
||||
private float prevPassivePingRadius;
|
||||
|
||||
@@ -817,24 +818,33 @@ namespace Barotrauma.Items.Components
|
||||
if (distSqr > t.SoundRange * t.SoundRange * 2) { continue; }
|
||||
|
||||
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,
|
||||
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);
|
||||
sonarBlips.Add(new SonarBlip(t.WorldPosition, 1.0f, 1.0f));
|
||||
//remove blips that weren't in the AITarget's sector
|
||||
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))
|
||||
{
|
||||
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),
|
||||
"", 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; }
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
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)
|
||||
{
|
||||
float chargeRatio = charge / capacity;
|
||||
float chargeRatio = charge / adjustedCapacity;
|
||||
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);
|
||||
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);
|
||||
Vector2 indicatorCenter = (indicatorPos + (scaledIndicatorSize * 0.5f)) * flip;
|
||||
Vector2 indicatorSize;
|
||||
@@ -193,7 +193,7 @@ namespace Barotrauma.Items.Components
|
||||
rechargeSpeedSlider.BarScroll = rechargeRate;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
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
|
||||
{
|
||||
|
||||
int targetSlot = Math.Max(itemContainer.ContainedStateIndicatorSlot, 0);
|
||||
var containedItem = itemContainer.Inventory.slots[targetSlot].FirstOrDefault();
|
||||
|
||||
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))
|
||||
ItemSlot containedItemSlot = null;
|
||||
if (targetSlot < itemContainer.Inventory.slots.Length)
|
||||
{
|
||||
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(targetSlot));
|
||||
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
|
||||
containedItemSlot = itemContainer.Inventory.slots[targetSlot];
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1256,11 +1256,9 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (ItemComponent ic in components)
|
||||
{
|
||||
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
|
||||
if (!ic.CanBePicked && !ic.CanBeSelected) { continue; }
|
||||
if (ic is Holdable holdable && !holdable.CanBeDeattached()) { continue; }
|
||||
if (ic is ConnectionPanel connectionPanel && !connectionPanel.CanRewire()) { continue; }
|
||||
|
||||
Color color = Color.Gray;
|
||||
if (ic.HasRequiredItems(character, false))
|
||||
{
|
||||
@@ -1273,6 +1271,7 @@ namespace Barotrauma
|
||||
color = Color.Cyan;
|
||||
}
|
||||
}
|
||||
if (ic.DisplayMsg.IsNullOrEmpty()) { continue; }
|
||||
texts.Add(new ColoredText(ic.DisplayMsg.Value, color, false, false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipes;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
static partial class ChildServerRelay
|
||||
{
|
||||
public static Process Process;
|
||||
public static bool IsProcessAlive => Process is { HasExited: false };
|
||||
|
||||
private static bool localHandlesDisposed;
|
||||
private static AnonymousPipeServerStream writePipe;
|
||||
private static AnonymousPipeServerStream readPipe;
|
||||
@@ -44,18 +47,27 @@ namespace Barotrauma.Networking
|
||||
localHandlesDisposed = true;
|
||||
}
|
||||
|
||||
public static void ClosePipes()
|
||||
public static void AttemptGracefulShutDown(int maxAttempts = 20)
|
||||
{
|
||||
writePipe?.Dispose(); writePipe = null;
|
||||
readPipe?.Dispose(); readPipe = null;
|
||||
shutDown = true;
|
||||
status = StatusEnum.RequestedShutDown;
|
||||
writeManualResetEvent?.Set();
|
||||
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;
|
||||
writePipe = null; readPipe = null;
|
||||
|
||||
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)
|
||||
{
|
||||
Quit();
|
||||
|
||||
Submarine.Unload();
|
||||
GameMain.Client = null;
|
||||
GameMain.GameSession = null;
|
||||
@@ -531,14 +548,10 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (GameMain.WindowActive)
|
||||
{
|
||||
if (ChildServerRelay.Process?.HasExited ?? true)
|
||||
if (!ChildServerRelay.IsProcessAlive)
|
||||
{
|
||||
Quit();
|
||||
if (!GUIMessageBox.MessageBoxes.Any(mb => (mb as GUIMessageBox)?.Text?.Text == ChildServerRelay.CrashMessage))
|
||||
{
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
|
||||
}
|
||||
CreateServerCrashMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -942,13 +955,6 @@ namespace Barotrauma.Networking
|
||||
|
||||
GUI.ClearCursorWait();
|
||||
|
||||
ChildServerRelay.ShutDown();
|
||||
|
||||
if (SteamManager.IsInitialized)
|
||||
{
|
||||
Steamworks.SteamFriends.ClearRichPresence();
|
||||
}
|
||||
|
||||
if (disconnectPacket.ShouldCreateAnalyticsEvent)
|
||||
{
|
||||
GameAnalyticsManager.AddErrorEventOnce(
|
||||
@@ -974,11 +980,43 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
ReturnToPreviousMenu(null, null);
|
||||
new GUIMessageBox(TextManager.Get(wasConnected ? "ConnectionLost" : "CouldNotConnectToServer"), disconnectPacket.PopupMessage)
|
||||
if (ClientPeer is SteamP2PClientPeer or SteamP2POwnerPeer)
|
||||
{
|
||||
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()
|
||||
{
|
||||
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 = 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);
|
||||
|
||||
characterInfo?.Remove();
|
||||
|
||||
VoipClient?.Dispose();
|
||||
VoipClient = null;
|
||||
GameMain.Client = null;
|
||||
GameMain.GameSession = null;
|
||||
}
|
||||
|
||||
public void SendCharacterInfo(string newName = null)
|
||||
|
||||
@@ -91,15 +91,11 @@ namespace Barotrauma.Networking
|
||||
ToolBox.ThrowIfNull(netClient);
|
||||
ToolBox.ThrowIfNull(incomingLidgrenMessages);
|
||||
|
||||
if (isOwner && !(ChildServerRelay.Process is { HasExited: false }))
|
||||
if (isOwner && !ChildServerRelay.IsProcessAlive)
|
||||
{
|
||||
var gameClient = GameMain.Client;
|
||||
Close(PeerDisconnectPacket.WithReason(DisconnectReason.ServerCrashed));
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += (btn, obj) =>
|
||||
{
|
||||
GameMain.MainMenuScreen.Select();
|
||||
return false;
|
||||
};
|
||||
gameClient?.CreateServerCrashMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -187,15 +187,11 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
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));
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += (btn, obj) =>
|
||||
{
|
||||
GameMain.MainMenuScreen.Select();
|
||||
return false;
|
||||
};
|
||||
gameClient?.CreateServerCrashMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -401,8 +397,6 @@ namespace Barotrauma.Networking
|
||||
ClosePeerSession(remotePeers[i]);
|
||||
}
|
||||
|
||||
ChildServerRelay.ClosePipes();
|
||||
|
||||
callbacks.OnDisconnect.Invoke(peerDisconnectPacket);
|
||||
|
||||
SteamManager.LeaveLobby();
|
||||
|
||||
@@ -17,9 +17,7 @@ namespace Barotrauma.Networking
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public static IReadOnlyList<string> CaptureDeviceNames =>
|
||||
Alc.GetStringList(IntPtr.Zero, OpenAL.Alc.CaptureDeviceSpecifier);
|
||||
|
||||
|
||||
private readonly IntPtr captureDevice;
|
||||
|
||||
@@ -169,6 +167,11 @@ namespace Barotrauma.Networking
|
||||
Create(GameSettings.CurrentConfig.Audio.VoiceCaptureDevice, storedBufferID);
|
||||
}
|
||||
|
||||
public static IReadOnlyList<string> GetCaptureDeviceNames()
|
||||
{
|
||||
return Alc.GetStringList(IntPtr.Zero, OpenAL.Alc.CaptureDeviceSpecifier);
|
||||
}
|
||||
|
||||
IntPtr nativeBuffer;
|
||||
readonly short[] uncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
readonly short[] prevUncompressedBuffer = new short[VoipConfig.BUFFER_SIZE];
|
||||
|
||||
@@ -11,8 +11,6 @@ namespace Barotrauma
|
||||
{
|
||||
partial class GameScreen : Screen
|
||||
{
|
||||
public override bool IsEditor => GameMain.GameSession?.GameMode is TestGameMode;
|
||||
|
||||
private RenderTarget2D renderTargetBackground;
|
||||
private RenderTarget2D renderTarget;
|
||||
private RenderTarget2D renderTargetWater;
|
||||
|
||||
@@ -47,7 +47,14 @@ namespace Barotrauma
|
||||
private GUITextBox serverNameBox, passwordBox, maxPlayersBox;
|
||||
private GUITickBox isPublicBox, wrongPasswordBanBox, karmaBox;
|
||||
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 GUIImage playstyleBanner;
|
||||
@@ -268,15 +275,29 @@ namespace Barotrauma
|
||||
RelativeSpacing = 0.035f
|
||||
};
|
||||
|
||||
#if USE_STEAM
|
||||
steamWorkshopButton = new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), customizeList.RectTransform), TextManager.Get("settingstab.mods"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
||||
modsButtonContainer = new GUIFrame(new RectTransform(Vector2.One, customizeList.RectTransform),
|
||||
style: null);
|
||||
|
||||
modsButton = new GUIButton(new RectTransform(Vector2.One, modsButtonContainer.RectTransform),
|
||||
TextManager.Get("settingstab.mods"), textAlignment: Alignment.Left, style: "MainMenuGUIButton")
|
||||
{
|
||||
ForceUpperCase = ForceUpperCase.Yes,
|
||||
Enabled = true,
|
||||
UserData = Tab.SteamWorkshop,
|
||||
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")
|
||||
{
|
||||
@@ -525,6 +546,8 @@ namespace Barotrauma
|
||||
#region Selection
|
||||
public override void Select()
|
||||
{
|
||||
ResetModUpdateButton();
|
||||
|
||||
if (WorkshopItemsToUpdate.Any())
|
||||
{
|
||||
while (WorkshopItemsToUpdate.TryDequeue(out ulong workshopId))
|
||||
@@ -711,6 +734,13 @@ namespace Barotrauma
|
||||
}
|
||||
#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)
|
||||
{
|
||||
if (fixedSeed)
|
||||
@@ -930,15 +960,32 @@ namespace Barotrauma
|
||||
|
||||
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)
|
||||
{
|
||||
hostServerButton.Enabled = Steam.SteamManager.IsInitialized;
|
||||
}
|
||||
steamWorkshopButton.Enabled = Steam.SteamManager.IsInitialized;
|
||||
#elif USE_STEAM
|
||||
steamWorkshopButton.Enabled = true;
|
||||
#endif
|
||||
|
||||
if (modUpdateTask is { IsCompletedSuccessfully: true })
|
||||
{
|
||||
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)
|
||||
{
|
||||
case Tab.NewGame:
|
||||
|
||||
@@ -103,6 +103,10 @@ namespace Barotrauma
|
||||
public GUIFrame JobPreferenceContainer;
|
||||
public GUIListBox JobList;
|
||||
|
||||
private Identifier micIconStyle;
|
||||
private float micCheckTimer;
|
||||
const float MicCheckInterval = 1.0f;
|
||||
|
||||
private float autoRestartTimer;
|
||||
|
||||
//persistent characterinfo provided by the server
|
||||
@@ -2656,27 +2660,9 @@ namespace Barotrauma
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
|
||||
if (GameMain.Client == null) { return; }
|
||||
|
||||
Identifier currMicStyle = micIcon.Style.Element.NameAsIdentifier();
|
||||
|
||||
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);
|
||||
}
|
||||
UpdateMicIcon((float)deltaTime);
|
||||
|
||||
foreach (GUIComponent child in PlayerList.Content.Children)
|
||||
{
|
||||
@@ -2738,6 +2724,35 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
if (backgroundSprite?.Texture == null) { return; }
|
||||
|
||||
@@ -354,9 +354,24 @@ namespace Barotrauma
|
||||
ToolTip = RichString.Rich(TextManager.Get("SaveSubButton") + "‖color:125,125,125‖\nCtrl + S‖color:end‖"),
|
||||
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;
|
||||
CreateSaveScreen();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -3107,7 +3122,7 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SnapToGrid()
|
||||
private static void SnapToGrid()
|
||||
{
|
||||
// First move components
|
||||
foreach (MapEntity e in MapEntity.SelectedList)
|
||||
@@ -3120,6 +3135,10 @@ namespace Barotrauma
|
||||
var wire = item.GetComponent<Wire>();
|
||||
if (wire != null) { continue; }
|
||||
item.Move(offset);
|
||||
if (item.GetComponent<Door>()?.LinkedGap is Gap linkedGap)
|
||||
{
|
||||
linkedGap.Move(item.Position - linkedGap.Position);
|
||||
}
|
||||
}
|
||||
else if (e is Structure structure)
|
||||
{
|
||||
@@ -3144,7 +3163,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<SubmarineInfo> GetLoadableSubs()
|
||||
private static IEnumerable<SubmarineInfo> GetLoadableSubs()
|
||||
{
|
||||
string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
|
||||
return SubmarineInfo.SavedSubmarines.Where(s
|
||||
@@ -3531,9 +3550,18 @@ namespace Barotrauma
|
||||
public void LoadSub(SubmarineInfo info)
|
||||
{
|
||||
Submarine.Unload();
|
||||
var selectedSub = new Submarine(info);
|
||||
MainSub = selectedSub;
|
||||
MainSub.UpdateTransform(interpolate: false);
|
||||
Submarine selectedSub = null;
|
||||
try
|
||||
{
|
||||
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();
|
||||
CreateDummyCharacter();
|
||||
|
||||
|
||||
@@ -742,8 +742,7 @@ namespace Barotrauma
|
||||
|
||||
if (Screen.Selected == null) { return "menu".ToIdentifier(); }
|
||||
|
||||
if ((Screen.Selected?.IsEditor ?? false)
|
||||
|| (Screen.Selected == GameMain.NetLobbyScreen))
|
||||
if (Screen.Selected is { IsEditor: true } || GameMain.GameSession?.GameMode is TestGameMode || Screen.Selected == GameMain.NetLobbyScreen)
|
||||
{
|
||||
return "editor".ToIdentifier();
|
||||
}
|
||||
@@ -853,7 +852,7 @@ namespace Barotrauma
|
||||
{
|
||||
return "levelend".ToIdentifier();
|
||||
}
|
||||
if (GameMain.GameSession.RoundDuration > 120.0 &&
|
||||
if (GameMain.GameSession.RoundDuration < 120.0 &&
|
||||
Level.Loaded?.Type == LevelData.LevelType.LocationConnection)
|
||||
{
|
||||
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()));
|
||||
(ContentPackage Package, bool IsUpToDate)[] outOfDatePackages = await Task.WhenAll(determiningTasks);
|
||||
@@ -126,6 +126,7 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
mutableWorkshopMenu.PopulateInstalledModLists(forceRefreshEnabled: true);
|
||||
}
|
||||
GameMain.MainMenuScreen.ResetModUpdateButton();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>100.6.0.0</Version>
|
||||
<Version>100.8.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>100.6.0.0</Version>
|
||||
<Version>100.8.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma</Product>
|
||||
<Version>100.6.0.0</Version>
|
||||
<Version>100.8.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>Barotrauma</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>100.6.0.0</Version>
|
||||
<Version>100.8.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>100.6.0.0</Version>
|
||||
<Version>100.8.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -18,20 +18,22 @@ namespace Barotrauma.Items.Components
|
||||
bool powerOn = msg.ReadBoolean();
|
||||
float fissionRate = 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; }
|
||||
|
||||
IsActive = true;
|
||||
|
||||
if (!autoTemp && AutoTemp) blameOnBroken = c;
|
||||
if (turbineOutput < TargetTurbineOutput) blameOnBroken = c;
|
||||
if (fissionRate > TargetFissionRate) blameOnBroken = c;
|
||||
if (!_powerOn && powerOn) blameOnBroken = c;
|
||||
if (!autoTemp && AutoTemp) { blameOnBroken = c; }
|
||||
if (turbineOutput < TargetTurbineOutput) { blameOnBroken = c; }
|
||||
if (fissionRate > TargetFissionRate) { blameOnBroken = c; }
|
||||
if (!_powerOn && powerOn) { blameOnBroken = c; }
|
||||
|
||||
AutoTemp = autoTemp;
|
||||
_powerOn = powerOn;
|
||||
TargetFissionRate = fissionRate;
|
||||
TargetTurbineOutput = turbineOutput;
|
||||
if (AllowTemperatureBoost) { temperatureBoost = temperatureBoostAmount; }
|
||||
|
||||
LastUser = c.Character;
|
||||
if (nextServerLogWriteTime == null)
|
||||
@@ -51,6 +53,7 @@ namespace Barotrauma.Items.Components
|
||||
msg.WriteRangedSingle(TargetFissionRate, 0.0f, 100.0f, 8);
|
||||
msg.WriteRangedSingle(TargetTurbineOutput, 0.0f, 100.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);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,9 +335,9 @@ namespace Barotrauma.Networking
|
||||
#endif
|
||||
if (!started) { return; }
|
||||
|
||||
if (OwnerConnection != null && ChildServerRelay.HasShutDown)
|
||||
if (ChildServerRelay.HasShutDown)
|
||||
{
|
||||
Quit();
|
||||
GameMain.Instance.CloseServer();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3938,6 +3938,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public void Quit()
|
||||
{
|
||||
|
||||
if (started)
|
||||
{
|
||||
started = false;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<RootNamespace>Barotrauma</RootNamespace>
|
||||
<Authors>FakeFish, Undertow Games</Authors>
|
||||
<Product>Barotrauma Dedicated Server</Product>
|
||||
<Version>100.6.0.0</Version>
|
||||
<Version>100.8.0.0</Version>
|
||||
<Copyright>Copyright © FakeFish 2018-2022</Copyright>
|
||||
<Platforms>AnyCPU;x64</Platforms>
|
||||
<AssemblyName>DedicatedServer</AssemblyName>
|
||||
|
||||
@@ -1908,7 +1908,7 @@ namespace Barotrauma
|
||||
{
|
||||
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);
|
||||
if (fallBack)
|
||||
{
|
||||
@@ -1926,10 +1926,18 @@ namespace Barotrauma
|
||||
SteeringManager.SteeringSeek(steerPos, 10);
|
||||
}
|
||||
}
|
||||
else if (!Character.InWater)
|
||||
else
|
||||
{
|
||||
SteeringManager.Reset();
|
||||
FaceTarget(SelectedAiTarget.Entity);
|
||||
if (Character.CurrentHull == null && !canAttack)
|
||||
{
|
||||
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))
|
||||
@@ -1954,7 +1962,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
Entity targetEntity = wallTarget?.Structure ?? SelectedAiTarget?.Entity;
|
||||
if (AttackLimb?.attack is Attack { Ranged: true } attack && targetEntity != null)
|
||||
if (AttackLimb?.attack is Attack { Ranged: true } attack)
|
||||
{
|
||||
AimRangedAttack(attack, targetEntity);
|
||||
}
|
||||
@@ -1981,7 +1989,7 @@ namespace Barotrauma
|
||||
|
||||
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);
|
||||
if (attack.AimRotationTorque <= 0) { return; }
|
||||
Limb limb = GetLimbToRotate(attack);
|
||||
|
||||
@@ -1907,8 +1907,8 @@ namespace Barotrauma
|
||||
visibleHulls = VisibleHulls;
|
||||
}
|
||||
bool ignoreFire = objectiveManager.CurrentOrder is AIObjectiveExtinguishFires extinguishOrder && extinguishOrder.Priority > 0 || objectiveManager.HasActiveObjective<AIObjectiveExtinguishFire>();
|
||||
bool ignoreWater = HasDivingSuit(character);
|
||||
bool ignoreOxygen = ignoreWater || HasDivingMask(character);
|
||||
bool ignoreWater = character.IsProtectedFromPressure();
|
||||
bool ignoreOxygen = HasDivingGear(character);
|
||||
bool ignoreEnemies = ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() || ObjectiveManager.IsCurrentObjective<AIObjectiveFightIntruders>();
|
||||
float safety = CalculateHullSafety(hull, visibleHulls, character, ignoreWater, ignoreOxygen, ignoreFire, ignoreEnemies);
|
||||
if (isCurrentHull)
|
||||
|
||||
@@ -77,11 +77,11 @@ namespace Barotrauma
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (GameMain.GameSession.RoundDuration > 120.0f &&
|
||||
if (GameMain.GameSession.RoundDuration < 120.0f &&
|
||||
speaker?.CurrentHull != null &&
|
||||
(speaker.TeamID == CharacterTeamType.FriendlyNPC || speaker.TeamID == CharacterTeamType.None) &&
|
||||
Character.CharacterList.Any(c => c.TeamID != speaker.TeamID && c.CurrentHull == speaker.CurrentHull))
|
||||
|
||||
@@ -876,7 +876,20 @@ namespace Barotrauma
|
||||
TargetName = Enemy.DisplayName,
|
||||
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 (Mode == CombatMode.Arrest && Enemy.IsKnockedDown)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace Barotrauma
|
||||
private readonly Item item;
|
||||
public Item ItemToContain { get; private set; }
|
||||
|
||||
public int? TargetSlot;
|
||||
|
||||
private AIObjectiveGetItem getItemObjective;
|
||||
private AIObjectiveGoTo goToObjective;
|
||||
|
||||
@@ -126,7 +128,19 @@ namespace Barotrauma
|
||||
}
|
||||
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);
|
||||
}
|
||||
@@ -136,7 +150,20 @@ namespace Barotrauma
|
||||
}
|
||||
Inventory originalInventory = ItemToContain.ParentInventory;
|
||||
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)
|
||||
{
|
||||
@@ -144,7 +171,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Item item in originalInventory.GetItemsAt(slot).ToList())
|
||||
{
|
||||
container.Inventory.TryPutItem(item, null);
|
||||
TryPutItem(container.Inventory, TargetSlot, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,8 @@ namespace Barotrauma
|
||||
|
||||
protected override float TargetEvaluation()
|
||||
{
|
||||
if (!character.IsOnPlayerTeam && !character.IsOriginallyOnPlayerTeam) { return Targets.None() ? 0 : 100; }
|
||||
int totalEnemies = Targets.Count;
|
||||
if (totalEnemies == 0) { return 0; }
|
||||
if (Targets.None()) { return 0; }
|
||||
if (!character.IsOnPlayerTeam && !character.IsOriginallyOnPlayerTeam) { return 100; }
|
||||
if (character.IsSecurity) { return 100; }
|
||||
if (objectiveManager.IsOrder(this)) { return 100; }
|
||||
// If there's any security officers onboard, leave fighting for them.
|
||||
|
||||
@@ -103,13 +103,19 @@ namespace Barotrauma
|
||||
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,
|
||||
AllowDangerousPressure = true,
|
||||
ConditionLevel = MIN_OXYGEN,
|
||||
RemoveExistingWhenNecessary = true
|
||||
};
|
||||
if (container.HasSubContainers)
|
||||
{
|
||||
objective.TargetSlot = container.FindSuitableSubContainerIndex(OXYGEN_SOURCE);
|
||||
}
|
||||
return objective;
|
||||
},
|
||||
onAbandon: () =>
|
||||
{
|
||||
|
||||
@@ -267,7 +267,7 @@ namespace Barotrauma
|
||||
{
|
||||
Character followTarget = Target as Character;
|
||||
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 (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();
|
||||
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;
|
||||
if (petBehavior != null)
|
||||
{
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace Barotrauma
|
||||
get { return aiController; }
|
||||
}
|
||||
|
||||
public AICharacter(CharacterPrefab prefab, Vector2 position, string seed, CharacterInfo characterInfo = null, ushort id = Entity.NullEntityID, bool isNetworkPlayer = false, RagdollParams ragdoll = null)
|
||||
: base(prefab, position, seed, characterInfo, id: id, isRemotePlayer: isNetworkPlayer, ragdollParams: ragdoll)
|
||||
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, spawnInitialItems)
|
||||
{
|
||||
InitProjSpecific();
|
||||
}
|
||||
|
||||
@@ -540,8 +540,17 @@ namespace Barotrauma
|
||||
private Color speechBubbleColor;
|
||||
private float speechBubbleTimer;
|
||||
|
||||
/// <summary>
|
||||
/// Prevents the character from interacting with items or characters
|
||||
/// </summary>
|
||||
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
|
||||
public LocalizedString CustomInteractHUDText { get; private set; }
|
||||
private Action<Character, Character> onCustomInteract;
|
||||
@@ -1099,9 +1108,9 @@ namespace Barotrauma
|
||||
/// <param name="isRemotePlayer">Is the character controlled by a remote player.</param>
|
||||
/// <param name="hasAi">Is the character controlled by AI.</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>
|
||||
@@ -1116,16 +1125,16 @@ namespace Barotrauma
|
||||
/// <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="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))
|
||||
{
|
||||
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);
|
||||
if (prefab == null)
|
||||
@@ -1142,29 +1151,29 @@ namespace Barotrauma
|
||||
|
||||
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;
|
||||
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);
|
||||
aiCharacter.SetAI(ai);
|
||||
newCharacter = aiCharacter;
|
||||
}
|
||||
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);
|
||||
aiCharacter.SetAI(ai);
|
||||
newCharacter = aiCharacter;
|
||||
}
|
||||
else
|
||||
{
|
||||
newCharacter = new Character(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll);
|
||||
newCharacter = new Character(prefab, position, seed, characterInfo, id, isRemotePlayer, ragdoll, spawnInitialItems);
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
@@ -1181,7 +1190,7 @@ namespace Barotrauma
|
||||
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.Seed = seed;
|
||||
@@ -1291,7 +1300,8 @@ namespace Barotrauma
|
||||
{
|
||||
Inventory = new CharacterInventory(
|
||||
inventoryElements.Count == 1 ? inventoryElements[0] : ToolBox.SelectWeightedRandom(inventoryElements, inventoryCommonness, random),
|
||||
this);
|
||||
this,
|
||||
spawnInitialItems);
|
||||
}
|
||||
if (healthElements.Count == 0)
|
||||
{
|
||||
@@ -2713,7 +2723,7 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
if (isLocalPlayer)
|
||||
{
|
||||
if (!IsMouseOnUI && (ViewTarget == null || ViewTarget == this))
|
||||
if (!IsMouseOnUI && (ViewTarget == null || ViewTarget == this) && !DisableFocusingOnEntities)
|
||||
{
|
||||
if (findFocusedTimer <= 0.0f || Screen.Selected == GameMain.SubEditorScreen)
|
||||
{
|
||||
@@ -2748,6 +2758,7 @@ namespace Barotrauma
|
||||
focusedItem = null;
|
||||
}
|
||||
findFocusedTimer -= deltaTime;
|
||||
DisableFocusingOnEntities = false;
|
||||
}
|
||||
#endif
|
||||
var head = AnimController.GetLimb(LimbType.Head);
|
||||
@@ -4243,11 +4254,14 @@ namespace Barotrauma
|
||||
|
||||
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));
|
||||
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)
|
||||
{
|
||||
BreakJoints();
|
||||
@@ -4927,7 +4941,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (TalentOption talentOption in talentSubTree.TalentOptionStages)
|
||||
{
|
||||
if (talentOption.TalentIdentifiers.None(HasTalent))
|
||||
if (!talentOption.HasMaxTalents(info.UnlockedTalents))
|
||||
{
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Barotrauma
|
||||
|
||||
bool allowCheats = false;
|
||||
#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
|
||||
if (!allowCheats && !CheatsEnabled && IsCheat)
|
||||
{
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace Barotrauma
|
||||
MaxAttachableCount,
|
||||
ExplosionRadiusMultiplier,
|
||||
ExplosionDamageMultiplier,
|
||||
FabricateMedicineSpeedMultiplier,
|
||||
FabricationSpeed,
|
||||
BallastFloraDamageMultiplier,
|
||||
HoldBreathMultiplier,
|
||||
Apprenticeship,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -53,6 +54,11 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
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()
|
||||
@@ -100,7 +106,22 @@ namespace Barotrauma
|
||||
{
|
||||
if (inventory == null) { return false; }
|
||||
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; }
|
||||
count++;
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Barotrauma
|
||||
|
||||
public bool DisableEvents
|
||||
{
|
||||
get { return IsFirstRound && GameMain.GameSession.RoundDuration > FirstRoundEventDelay; }
|
||||
get { return IsFirstRound && GameMain.GameSession.RoundDuration < FirstRoundEventDelay; }
|
||||
}
|
||||
|
||||
public bool CheatsEnabled;
|
||||
|
||||
@@ -813,7 +813,7 @@ namespace Barotrauma
|
||||
/// </remarks>
|
||||
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> bots;
|
||||
@@ -956,7 +956,7 @@ namespace Barotrauma
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "Submarine:" + (Submarine.MainSub?.Info?.Name ?? "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)
|
||||
{
|
||||
GameAnalyticsManager.AddDesignEvent(eventId + "MissionType:" + (mission.Prefab.Type.ToString() ?? "none") + ":" + mission.Prefab.Identifier + ":" + (mission.Completed ? "Completed" : "Failed"), RoundDuration);
|
||||
|
||||
@@ -56,6 +56,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
EndReadyCheck();
|
||||
|
||||
#if CLIENT
|
||||
msgBox?.Close();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ namespace Barotrauma
|
||||
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)
|
||||
{
|
||||
this.character = character;
|
||||
@@ -84,6 +84,8 @@ namespace Barotrauma
|
||||
|
||||
InitProjSpecific(element);
|
||||
|
||||
if (!spawnInitialItems) { return; }
|
||||
|
||||
#if CLIENT
|
||||
//clients don't create items until the server says so
|
||||
if (GameMain.Client != null) { return; }
|
||||
@@ -94,7 +96,7 @@ namespace Barotrauma
|
||||
if (!subElement.Name.ToString().Equals("item", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
|
||||
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.");
|
||||
continue;
|
||||
@@ -200,6 +202,7 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
CreateSlots();
|
||||
#endif
|
||||
CharacterHUD.RecreateHudTextsIfControlling(character);
|
||||
//if the item was equipped and there are more items in the same stack, equip one of those items
|
||||
if (tryEquipFromSameStack && wasEquipped)
|
||||
{
|
||||
@@ -498,6 +501,7 @@ namespace Barotrauma
|
||||
HintManager.OnObtainedItem(character, item);
|
||||
}
|
||||
#endif
|
||||
CharacterHUD.RecreateHudTextsIfControlling(character);
|
||||
if (item.CampaignInteractionType == CampaignMode.InteractionType.Cargo)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
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>();
|
||||
@@ -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).
|
||||
SetContainedActive(true);
|
||||
}
|
||||
|
||||
CharacterHUD.RecreateHudTextsIfFocused(item, containedItem);
|
||||
OnContainedItemsChanged.Invoke(this);
|
||||
}
|
||||
|
||||
@@ -386,9 +393,9 @@ namespace Barotrauma.Items.Components
|
||||
public void OnItemRemoved(Item containedItem)
|
||||
{
|
||||
activeContainedItems.RemoveAll(i => i.Item == containedItem);
|
||||
|
||||
//deactivate if the inventory is empty
|
||||
IsActive = activeContainedItems.Count > 0 || Inventory.AllItems.Any(it => it.body != null);
|
||||
CharacterHUD.RecreateHudTextsIfFocused(item, containedItem);
|
||||
OnContainedItemsChanged.Invoke(this);
|
||||
}
|
||||
|
||||
@@ -664,6 +671,18 @@ namespace Barotrauma.Items.Components
|
||||
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)
|
||||
{
|
||||
switch (connection.Name)
|
||||
|
||||
@@ -603,6 +603,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -610,8 +617,7 @@ namespace Barotrauma.Items.Components
|
||||
if (fabricableItem.RequiresRecipe)
|
||||
{
|
||||
if (character == null) { return false; }
|
||||
if (!character.HasRecipeForItem(fabricableItem.TargetItem.Identifier) &&
|
||||
GameSession.GetSessionCrewCharacters(CharacterType.Bot).None(c => c.HasRecipeForItem(fabricableItem.TargetItem.Identifier)))
|
||||
if (!AnyOneHasRecipeForItem(character, fabricableItem.TargetItem))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -678,9 +684,10 @@ namespace Barotrauma.Items.Components
|
||||
//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
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -25,12 +25,8 @@ namespace Barotrauma.Items.Components
|
||||
public List<Hull> LinkedHulls = new List<Hull>();
|
||||
}
|
||||
|
||||
private DateTime resetDataTime;
|
||||
|
||||
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.")]
|
||||
public bool RequireWaterDetectors
|
||||
{
|
||||
@@ -77,7 +73,6 @@ namespace Barotrauma.Items.Components
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
hullDatas = new Dictionary<Hull, HullData>();
|
||||
InitProjSpecific();
|
||||
}
|
||||
|
||||
@@ -85,37 +80,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
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;
|
||||
if (hasPower)
|
||||
{
|
||||
@@ -140,67 +104,5 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
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 TemperatureBoostAmount = 20;
|
||||
const float TemperatureBoostAmount = 25;
|
||||
|
||||
//the rate at which the reactor is being run on (higher rate -> higher temperature)
|
||||
private float fissionRate;
|
||||
@@ -26,10 +26,6 @@ namespace Barotrauma.Items.Components
|
||||
//amount of power generated balanced with the load)
|
||||
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 meltDownTimer, meltDownDelay;
|
||||
@@ -53,6 +49,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private float temperatureBoost;
|
||||
|
||||
public bool AllowTemperatureBoost => Math.Abs(temperatureBoost) < TemperatureBoostAmount * 0.9f;
|
||||
|
||||
private bool _powerOn;
|
||||
|
||||
[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));
|
||||
temperatureBoost = adjustValueWithoutOverShooting(temperatureBoost, 0.0f, deltaTime);
|
||||
#if CLIENT
|
||||
temperatureBoostUpButton.Enabled = temperatureBoostDownButton.Enabled = Math.Abs(temperatureBoost) < TemperatureBoostAmount * 0.9f;
|
||||
temperatureBoostUpButton.Enabled = temperatureBoostDownButton.Enabled = AllowTemperatureBoost;
|
||||
#endif
|
||||
|
||||
FissionRate = MathHelper.Lerp(fissionRate, Math.Min(TargetFissionRate, AvailableFuel), deltaTime);
|
||||
|
||||
@@ -248,6 +248,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (container?.Inventory == null) { return; }
|
||||
|
||||
bool recreateHudTexts = false;
|
||||
for (var i = 0; i < container.Inventory.Capacity; i++)
|
||||
{
|
||||
if (i < 0 || GrowableSeeds.Length <= i) { continue; }
|
||||
@@ -257,6 +258,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (growable != null)
|
||||
{
|
||||
recreateHudTexts |= GrowableSeeds[i] != growable;
|
||||
GrowableSeeds[i] = growable;
|
||||
growable.IsActive = true;
|
||||
}
|
||||
@@ -267,11 +269,14 @@ namespace Barotrauma.Items.Components
|
||||
// Kill the plant if it's somehow removed
|
||||
oldGrowable.Decayed = true;
|
||||
oldGrowable.IsActive = false;
|
||||
recreateHudTexts = true;
|
||||
}
|
||||
|
||||
GrowableSeeds[i] = null;
|
||||
}
|
||||
}
|
||||
#if CLIENT
|
||||
CharacterHUD.RecreateHudTexts |= recreateHudTexts;
|
||||
#endif
|
||||
|
||||
// server handles this
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
//[power/min]
|
||||
private float capacity;
|
||||
private float adjustedCapacity;
|
||||
|
||||
private float charge, prevCharge;
|
||||
|
||||
@@ -66,7 +67,11 @@ namespace Barotrauma.Items.Components
|
||||
public float 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.")]
|
||||
@@ -76,10 +81,10 @@ namespace Barotrauma.Items.Components
|
||||
set
|
||||
{
|
||||
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%
|
||||
if (Math.Abs(charge - lastSentCharge) / capacity > 0.05f)
|
||||
if (Math.Abs(charge - lastSentCharge) / adjustedCapacity > 0.05f)
|
||||
{
|
||||
#if SERVER
|
||||
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.")]
|
||||
public float MaxRechargeSpeed
|
||||
@@ -157,6 +162,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
adjustedCapacity = GetCapacity();
|
||||
if (item.Connections == null)
|
||||
{
|
||||
IsActive = false;
|
||||
@@ -164,7 +170,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
isRunning = true;
|
||||
float chargeRatio = charge / capacity;
|
||||
float chargeRatio = charge / adjustedCapacity;
|
||||
|
||||
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(loadReading)).ToString(), "load_value_out");
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -193,16 +199,16 @@ namespace Barotrauma.Items.Components
|
||||
if (connection == powerIn)
|
||||
{
|
||||
//Don't draw power if fully charged
|
||||
if (charge >= capacity)
|
||||
if (charge >= adjustedCapacity)
|
||||
{
|
||||
charge = capacity;
|
||||
charge = adjustedCapacity;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.Condition <= 0.0f) { return 0.0f; }
|
||||
|
||||
float missingCharge = capacity - charge;
|
||||
float missingCharge = adjustedCapacity - charge;
|
||||
float targetRechargeSpeed = rechargeSpeed;
|
||||
|
||||
if (ExponentialRechargeSpeed)
|
||||
@@ -239,7 +245,7 @@ namespace Barotrauma.Items.Components
|
||||
if (connection == powerOut)
|
||||
{
|
||||
float maxOutput;
|
||||
float chargeRatio = prevCharge / capacity;
|
||||
float chargeRatio = prevCharge / adjustedCapacity;
|
||||
if (chargeRatio < 0.1f)
|
||||
{
|
||||
maxOutput = Math.Max(chargeRatio * 10.0f, 0.0f) * MaxOutPut;
|
||||
@@ -292,7 +298,7 @@ namespace Barotrauma.Items.Components
|
||||
else
|
||||
{
|
||||
//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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,13 +677,16 @@ namespace Barotrauma.Items.Components
|
||||
ItemContainer projectileContainer = projectiles.First().Item.Container?.GetComponent<ItemContainer>();
|
||||
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
|
||||
var rootContainer = projectileContainer.Item.GetRootContainer();
|
||||
if (rootContainer != projectileContainer.Item)
|
||||
if (rootContainer != null && rootContainer != projectileContainer.Item)
|
||||
{
|
||||
rootContainer.Use(deltaTime, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
projectileContainer.Item.Use(deltaTime, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -576,11 +576,13 @@ namespace Barotrauma
|
||||
if (selectedSlot?.Inventory == this) { selectedSlot.ForceTooltipRefresh = true; }
|
||||
}
|
||||
#endif
|
||||
CharacterHUD.RecreateHudTextsIfControlling(user);
|
||||
|
||||
if (item.body != null)
|
||||
{
|
||||
item.body.Enabled = false;
|
||||
item.body.BodyType = FarseerPhysics.BodyType.Dynamic;
|
||||
item.SetTransform(item.SimPosition, rotation: 0.0f, findNewHull: false);
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
@@ -924,6 +926,7 @@ namespace Barotrauma
|
||||
if (selectedSlot?.Inventory == this) { selectedSlot.ForceTooltipRefresh = true; }
|
||||
}
|
||||
#endif
|
||||
CharacterHUD.RecreateHudTextsIfFocused(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1095,6 +1095,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();
|
||||
ItemList.Add(this);
|
||||
if (Prefab.IsDangerous) { dangerousItems.Add(this); }
|
||||
@@ -1107,7 +1113,6 @@ namespace Barotrauma
|
||||
if (HasTag("logic")) { isLogic = true; }
|
||||
|
||||
ApplyStatusEffects(ActionType.OnSpawn, 1.0f);
|
||||
Components.ForEach(c => c.ApplyStatusEffects(ActionType.OnSpawn, 1.0f));
|
||||
RecalculateConditionValues();
|
||||
#if CLIENT
|
||||
Submarine.ForceVisibilityRecheck();
|
||||
@@ -2896,11 +2901,14 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
foreach (ItemComponent ic in components) { ic.Equip(character); }
|
||||
|
||||
CharacterHUD.RecreateHudTextsIfControlling(character);
|
||||
}
|
||||
|
||||
public void Unequip(Character character)
|
||||
{
|
||||
foreach (ItemComponent ic in components) { ic.Unequip(character); }
|
||||
CharacterHUD.RecreateHudTextsIfControlling(character);
|
||||
}
|
||||
|
||||
public List<(object obj, SerializableProperty property)> GetProperties<T>()
|
||||
@@ -3442,12 +3450,15 @@ namespace Barotrauma
|
||||
//if the item was in full condition considering the unmodified health
|
||||
//(not taking possible HealthMultipliers added by mods into account),
|
||||
//make sure it stays in full condition
|
||||
bool wasFullCondition = prevCondition >= item.Prefab.Health;
|
||||
if (wasFullCondition)
|
||||
if (item.condition > 0)
|
||||
{
|
||||
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.RecalculateConditionValues();
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Barotrauma
|
||||
|
||||
public bool IgnoreInEditor { get; set; }
|
||||
|
||||
private ImmutableHashSet<Identifier> excludedIdentifiers;
|
||||
public ImmutableHashSet<Identifier> ExcludedIdentifiers { get; private set; }
|
||||
|
||||
private RelationType type;
|
||||
|
||||
@@ -87,20 +87,20 @@ namespace Barotrauma
|
||||
|
||||
public string JoinedExcludedIdentifiers
|
||||
{
|
||||
get { return string.Join(",", excludedIdentifiers); }
|
||||
get { return string.Join(",", ExcludedIdentifiers); }
|
||||
set
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (item == null) { return false; }
|
||||
if (excludedIdentifiers.Contains(item.Prefab.Identifier)) { return false; }
|
||||
foreach (var excludedIdentifier in excludedIdentifiers)
|
||||
if (ExcludedIdentifiers.Contains(item.Prefab.Identifier)) { return false; }
|
||||
foreach (var excludedIdentifier in ExcludedIdentifiers)
|
||||
{
|
||||
if (item.HasTag(excludedIdentifier)) { return false; }
|
||||
}
|
||||
@@ -118,8 +118,8 @@ namespace Barotrauma
|
||||
public bool MatchesItem(ItemPrefab itemPrefab)
|
||||
{
|
||||
if (itemPrefab == null) { return false; }
|
||||
if (excludedIdentifiers.Contains(itemPrefab.Identifier)) { return false; }
|
||||
foreach (var excludedIdentifier in excludedIdentifiers)
|
||||
if (ExcludedIdentifiers.Contains(itemPrefab.Identifier)) { return false; }
|
||||
foreach (var excludedIdentifier in ExcludedIdentifiers)
|
||||
{
|
||||
if (itemPrefab.Tags.Contains(excludedIdentifier)) { return false; }
|
||||
}
|
||||
@@ -138,7 +138,7 @@ namespace Barotrauma
|
||||
public RelatedItem(Identifier[] identifiers, Identifier[] excludedIdentifiers)
|
||||
{
|
||||
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>();
|
||||
}
|
||||
@@ -233,7 +233,7 @@ namespace Barotrauma
|
||||
element.Add(new XAttribute(nameof(ItemPos), ItemPos.Value));
|
||||
}
|
||||
|
||||
if (excludedIdentifiers.Count > 0)
|
||||
if (ExcludedIdentifiers.Count > 0)
|
||||
{
|
||||
element.Add(new XAttribute("excludedidentifiers", JoinedExcludedIdentifiers));
|
||||
}
|
||||
|
||||
@@ -228,6 +228,9 @@ namespace Barotrauma
|
||||
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,
|
||||
//don't touch this edge. Otherwise we may end up closing off small passages between cells.
|
||||
var adjacentEmptyCell = edge.AdjacentCell(cell);
|
||||
@@ -238,8 +241,10 @@ namespace Barotrauma
|
||||
//find the edge at the opposite side of the adjacent cell
|
||||
foreach (GraphEdge otherEdge in adjacentEmptyCell.Edges)
|
||||
{
|
||||
if (Vector2.Dot(adjacentEmptyCell.Center - edge.Center, adjacentEmptyCell.Center - otherEdge.Center) > 0 &&
|
||||
otherEdge.AdjacentCell(adjacentEmptyCell)?.CellType != CellType.Solid)
|
||||
if (otherEdge == edge || otherEdge.AdjacentCell(adjacentEmptyCell)?.CellType != CellType.Solid) { continue; }
|
||||
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;
|
||||
break;
|
||||
@@ -251,13 +256,11 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
List<Vector2> edgePoints = new List<Vector2>();
|
||||
Vector2 edgeNormal = edge.GetNormal(cell);
|
||||
|
||||
float edgeLength = Vector2.Distance(edge.Point1, edge.Point2);
|
||||
int pointCount = (int)Math.Max(Math.Ceiling(edgeLength / minEdgeLength), 1);
|
||||
Vector2 edgeDir = edge.Point2 - edge.Point1;
|
||||
for (int i = 0; i <= pointCount; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
@@ -275,7 +278,7 @@ namespace Barotrauma
|
||||
float randomVariance = Rand.Range(0, irregularity, Rand.RandSync.ServerAndClient);
|
||||
Vector2 extrudedPoint =
|
||||
edge.Point1 +
|
||||
edgeDir * (i / (float)pointCount) +
|
||||
edgeDiff * (i / (float)pointCount) +
|
||||
edgeNormal * edgeLength * (roundingAmount + randomVariance) * centerF;
|
||||
|
||||
var nearbyCells = Level.Loaded.GetCells(extrudedPoint, searchDepth: 2);
|
||||
|
||||
@@ -716,7 +716,7 @@ namespace Barotrauma
|
||||
if (Rand.Range(0, 10, Rand.RandSync.ServerAndClient) != 0) { continue; }
|
||||
}
|
||||
|
||||
if (!TooClose(siteX, siteY))
|
||||
if (!TooCloseToOtherSites(siteX, siteY))
|
||||
{
|
||||
siteCoordsX.Add(siteX);
|
||||
siteCoordsY.Add(siteY);
|
||||
@@ -724,14 +724,14 @@ namespace Barotrauma
|
||||
|
||||
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 caveSiteY = y2 + Rand.Int(caveSiteInterval / 2, Rand.RandSync.ServerAndClient);
|
||||
|
||||
if (!TooClose(caveSiteX, caveSiteY))
|
||||
if (!TooCloseToOtherSites(caveSiteX, caveSiteY, caveSiteInterval))
|
||||
{
|
||||
siteCoordsX.Add(caveSiteX);
|
||||
siteCoordsY.Add(caveSiteY);
|
||||
@@ -742,11 +742,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++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -738,7 +738,7 @@ namespace Barotrauma
|
||||
|
||||
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
|
||||
//(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,
|
||||
Heartbeat = 0x01,
|
||||
RequestShutdown = 0xCC,
|
||||
Crash = 0xFF
|
||||
}
|
||||
|
||||
private static ManualResetEvent writeManualResetEvent;
|
||||
|
||||
private static volatile bool shutDown;
|
||||
public static bool HasShutDown => shutDown;
|
||||
private enum StatusEnum
|
||||
{
|
||||
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 static byte[] readTempBytes;
|
||||
@@ -38,7 +47,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
private static ConcurrentQueue<byte[]> msgsToWrite;
|
||||
private static ConcurrentQueue<string> errorsToWrite;
|
||||
|
||||
|
||||
private static ConcurrentQueue<byte[]> msgsToRead;
|
||||
|
||||
private static Thread readThread;
|
||||
@@ -48,6 +57,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
private static void PrivateStart()
|
||||
{
|
||||
status = StatusEnum.Active;
|
||||
|
||||
readIncOffset = 0;
|
||||
readIncTotal = 0;
|
||||
|
||||
@@ -58,8 +69,6 @@ namespace Barotrauma.Networking
|
||||
|
||||
msgsToRead = new ConcurrentQueue<byte[]>();
|
||||
|
||||
shutDown = false;
|
||||
|
||||
readCancellationToken = new CancellationTokenSource();
|
||||
|
||||
writeManualResetEvent = new ManualResetEvent(false);
|
||||
@@ -80,7 +89,13 @@ namespace Barotrauma.Networking
|
||||
|
||||
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();
|
||||
readCancellationToken?.Cancel();
|
||||
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);
|
||||
if (readTask is null) { return -1; }
|
||||
if (readTask is null) { return Option<int>.None(); }
|
||||
|
||||
int timeOutMilliseconds = 100;
|
||||
for (int i = 0; i < 150; i++)
|
||||
{
|
||||
if (shutDown)
|
||||
if (status is StatusEnum.ShutDown)
|
||||
{
|
||||
readCancellationToken?.Cancel();
|
||||
return -1;
|
||||
return Option<int>.None();
|
||||
}
|
||||
try
|
||||
{
|
||||
@@ -115,36 +130,36 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
catch (AggregateException aggregateException)
|
||||
{
|
||||
if (aggregateException.InnerException is OperationCanceledException) { return -1; }
|
||||
if (aggregateException.InnerException is OperationCanceledException) { return Option<int>.None(); }
|
||||
throw;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return -1;
|
||||
return Option<int>.None();
|
||||
}
|
||||
}
|
||||
|
||||
if (readTask.Status != TaskStatus.RanToCompletion)
|
||||
if (readTask.Status == TaskStatus.RanToCompletion)
|
||||
{
|
||||
bool swallowException = shutDown
|
||||
&& ((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 Option<int>.Some(readTask.Result);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (!(pipe is { IsConnected: true }))
|
||||
if (status is StatusEnum.Active && pipe is not { IsConnected: true })
|
||||
{
|
||||
throw new Exception($"{name} was disconnected unexpectedly");
|
||||
}
|
||||
@@ -155,7 +170,7 @@ namespace Barotrauma.Networking
|
||||
private static void UpdateRead()
|
||||
{
|
||||
Span<byte> msgLengthSpan = stackalloc byte[4 + 1];
|
||||
while (!shutDown)
|
||||
while (!HasShutDown)
|
||||
{
|
||||
CheckPipeConnected(nameof(readStream), readStream);
|
||||
|
||||
@@ -165,10 +180,9 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
if (readIncOffset >= readIncTotal)
|
||||
{
|
||||
readIncTotal = ReadIncomingMsgs();
|
||||
if (!ReadIncomingMsgs().TryUnwrap(out readIncTotal)) { return false; }
|
||||
readIncOffset = 0;
|
||||
if (readIncTotal == 0) { Thread.Yield(); continue; }
|
||||
if (readIncTotal < 0) { return false; }
|
||||
}
|
||||
readTo[i] = readTempBytes[readIncOffset];
|
||||
readIncOffset++;
|
||||
@@ -176,7 +190,7 @@ namespace Barotrauma.Networking
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!readBytes(msgLengthSpan)) { shutDown = true; break; }
|
||||
if (!readBytes(msgLengthSpan)) { status = StatusEnum.ShutDown; break; }
|
||||
|
||||
int msgLength = msgLengthSpan[0]
|
||||
| (msgLengthSpan[1] << 8)
|
||||
@@ -184,24 +198,24 @@ namespace Barotrauma.Networking
|
||||
| (msgLengthSpan[3] << 24);
|
||||
WriteStatus writeStatus = (WriteStatus)msgLengthSpan[4];
|
||||
|
||||
if (msgLength > 0)
|
||||
{
|
||||
byte[] msg = new byte[msgLength];
|
||||
if (!readBytes(msg.AsSpan())) { shutDown = true; break; }
|
||||
byte[] msg = msgLength > 0 ? new byte[msgLength] : Array.Empty<byte>();
|
||||
if (msg.Length > 0 && !readBytes(msg.AsSpan())) { status = StatusEnum.ShutDown; break; }
|
||||
|
||||
switch (writeStatus)
|
||||
{
|
||||
case WriteStatus.Success:
|
||||
msgsToRead.Enqueue(msg);
|
||||
break;
|
||||
case WriteStatus.Heartbeat:
|
||||
//do nothing
|
||||
break;
|
||||
case WriteStatus.Crash:
|
||||
HandleCrashString(Encoding.UTF8.GetString(msg));
|
||||
shutDown = true;
|
||||
break;
|
||||
}
|
||||
switch (writeStatus)
|
||||
{
|
||||
case WriteStatus.Success:
|
||||
msgsToRead.Enqueue(msg);
|
||||
break;
|
||||
case WriteStatus.Heartbeat:
|
||||
//do nothing
|
||||
break;
|
||||
case WriteStatus.RequestShutdown:
|
||||
status = StatusEnum.ShutDown;
|
||||
break;
|
||||
case WriteStatus.Crash:
|
||||
HandleCrashString(Encoding.UTF8.GetString(msg));
|
||||
status = StatusEnum.ShutDown;
|
||||
break;
|
||||
}
|
||||
|
||||
Thread.Yield();
|
||||
@@ -210,13 +224,11 @@ namespace Barotrauma.Networking
|
||||
|
||||
private static void UpdateWrite()
|
||||
{
|
||||
while (!shutDown)
|
||||
while (!HasShutDown)
|
||||
{
|
||||
CheckPipeConnected(nameof(writeStream), writeStream);
|
||||
|
||||
byte[] msg;
|
||||
|
||||
void writeMsg(WriteStatus writeStatus)
|
||||
void writeMsg(WriteStatus writeStatus, byte[] msg)
|
||||
{
|
||||
// It's SUPER IMPORTANT that this stack allocation
|
||||
// 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
|
||||
// this method is based around would lead to a stack
|
||||
// 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);
|
||||
bytesToWrite[1] = (byte)((msg.Length >> 8) & 0xFF);
|
||||
bytesToWrite[2] = (byte)((msg.Length >> 16) & 0xFF);
|
||||
bytesToWrite[3] = (byte)((msg.Length >> 24) & 0xFF);
|
||||
headerBytes[0] = (byte)(msg.Length & 0xFF);
|
||||
headerBytes[1] = (byte)((msg.Length >> 8) & 0xFF);
|
||||
headerBytes[2] = (byte)((msg.Length >> 16) & 0xFF);
|
||||
headerBytes[3] = (byte)((msg.Length >> 24) & 0xFF);
|
||||
|
||||
bytesToWrite[4] = (byte)writeStatus;
|
||||
Span<byte> msgSlice = bytesToWrite.Slice(4 + 1, msg.Length);
|
||||
|
||||
msg.AsSpan().CopyTo(msgSlice);
|
||||
headerBytes[4] = (byte)writeStatus;
|
||||
|
||||
try
|
||||
{
|
||||
writeStream?.Write(bytesToWrite);
|
||||
writeStream?.Write(headerBytes);
|
||||
writeStream?.Write(msg);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
@@ -246,7 +256,7 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
case ObjectDisposedException _:
|
||||
case System.IO.IOException _:
|
||||
if (!shutDown) { throw; }
|
||||
if (!HasShutDown) { throw; }
|
||||
break;
|
||||
default:
|
||||
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))
|
||||
{
|
||||
msg = Encoding.UTF8.GetBytes(error);
|
||||
writeMsg(WriteStatus.Crash);
|
||||
shutDown = true;
|
||||
writeMsg(WriteStatus.Crash, Encoding.UTF8.GetBytes(error));
|
||||
status = StatusEnum.ShutDown;
|
||||
}
|
||||
|
||||
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();
|
||||
if (!writeManualResetEvent.WaitOne(1000))
|
||||
{
|
||||
if (shutDown) { return; }
|
||||
if (HasShutDown) { return; }
|
||||
|
||||
//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)
|
||||
{
|
||||
if (shutDown) { return; }
|
||||
if (HasShutDown) { return; }
|
||||
|
||||
if (msg.Length > 0x1fff_ffff)
|
||||
{
|
||||
@@ -298,7 +313,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public static bool Read(out byte[] msg)
|
||||
{
|
||||
if (shutDown) { msg = null; return false; }
|
||||
if (HasShutDown) { msg = null; return false; }
|
||||
|
||||
return msgsToRead.TryDequeue(out msg);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v100.8.0.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
- Fixed vanilla package failing to load due to a duplicate music clip in sounds.xml, leading to a crash on startup.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v100.7.0.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
No changes aside from the fixes in v0.20.9.0.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
v100.6.0.0
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
@@ -83,6 +95,65 @@ Test version of the faction overhaul:
|
||||
- There's now always two paths from biome to another, one controlled by the Coalition and one by Separatists.
|
||||
- Improvements to the campaign map.
|
||||
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
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
|
||||
---------------------------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user