Faction Test 100.13.0.0

This commit is contained in:
Markus Isberg
2023-01-11 15:36:23 +02:00
parent 1650735468
commit caa5a2f762
145 changed files with 2100 additions and 1111 deletions
@@ -444,10 +444,20 @@ namespace Barotrauma
{
foreach (Limb limb in Limbs)
{
if (limb == null || limb.IsSevered || limb.ActiveSprite == null || !limb.DoesFlip) { continue; }
Vector2 spriteOrigin = limb.ActiveSprite.Origin;
spriteOrigin.X = limb.ActiveSprite.SourceRect.Width - spriteOrigin.X;
limb.ActiveSprite.Origin = spriteOrigin;
if (limb == null || limb.IsSevered || !limb.DoesMirror) { continue; }
FlipSprite(limb.DeformSprite?.Sprite ?? limb.Sprite);
foreach (var conditionalSprite in limb.ConditionalSprites)
{
FlipSprite(conditionalSprite.DeformableSprite?.Sprite ?? conditionalSprite.Sprite);
}
}
static void FlipSprite(Sprite sprite)
{
if (sprite == null) { return; }
Vector2 spriteOrigin = sprite.Origin;
spriteOrigin.X = sprite.SourceRect.Width - spriteOrigin.X;
sprite.Origin = spriteOrigin;
}
}
@@ -946,7 +946,7 @@ namespace Barotrauma
var headPreset = obj as HeadPreset;
if (info.Head.Preset != headPreset)
{
info.Head = new HeadInfo(info, headPreset)
info.Head = new HeadInfo(info, headPreset, info.Head.HairIndex, info.Head.BeardIndex, info.Head.MoustacheIndex, info.Head.FaceAttachmentIndex)
{
SkinColor = info.Head.SkinColor,
HairColor = info.Head.HairColor,
@@ -640,8 +640,7 @@ namespace Barotrauma
else
{
forceAfflictionContainerUpdate = true;
currentDisplayedAfflictions = GetAllAfflictions(mergeSameAfflictions: true)
.FindAll(a => a.ShouldShowIcon(Character) && a.Prefab.Icon != null);
currentDisplayedAfflictions = GetAllAfflictions(mergeSameAfflictions: true, predicate: a => a.ShouldShowIcon(Character) && a.Prefab.Icon != null);
currentDisplayedAfflictions.Sort((a1, a2) =>
{
int dmgPerSecond = Math.Sign(a1.DamagePerSecond - a2.DamagePerSecond);
@@ -1275,7 +1274,7 @@ namespace Barotrauma
//displaying an affliction we no longer have -> dirty
foreach ((Affliction affliction, float strength) in displayedAfflictions)
{
if (!afflictions.Any(a => a.Key == affliction)) { return true; }
if (afflictions.None(a => a.Key == affliction && a.Key.ShouldShowIcon(Character))) { return true; }
}
return false;
}
@@ -83,6 +83,7 @@ namespace Barotrauma
GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox;
Debug.Assert(conversationList != null);
DisableButtons(conversationList.Content.GetAllChildren<GUIButton>(), selectedButton: null);
// gray out the last text block
if (conversationList.Content.Children.LastOrDefault() is GUILayoutGroup lastElement)
{
@@ -269,14 +270,7 @@ namespace Barotrauma
if (actionInstance != null)
{
actionInstance.selectedOption = selectedOption;
foreach (GUIButton otherButton in optionButtons)
{
otherButton.CanBeFocused = false;
if (otherButton != btn)
{
otherButton.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
}
}
DisableButtons(optionButtons, btn);
btn.ExternalHighlight = true;
return true;
}
@@ -286,14 +280,7 @@ namespace Barotrauma
SendResponse(actionId.Value, selectedOption);
btn.CanBeFocused = false;
btn.ExternalHighlight = true;
foreach (GUIButton otherButton in optionButtons)
{
otherButton.CanBeFocused = false;
if (otherButton != btn)
{
otherButton.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
}
}
DisableButtons(optionButtons, btn);
return true;
}
//should not happen
@@ -305,6 +292,18 @@ namespace Barotrauma
}
}
public static void SelectOption(ushort actionId, int option)
{
if (lastMessageBox.UserData is Pair<string, ushort> userData)
{
if (userData.Second != actionId) { return; }
GUIListBox conversationList = lastMessageBox.FindChild("conversationlist", true) as GUIListBox;
Debug.Assert(conversationList != null);
DisableButtons(conversationList.Content.GetAllChildren<GUIButton>(), (btn) => btn.UserData is int i && i == option);
}
}
private static Tuple<Vector2, Point> GetSizes(DialogTypes dialogTypes)
{
return dialogTypes switch
@@ -383,6 +382,30 @@ namespace Barotrauma
return buttons;
}
private static void DisableButtons(IEnumerable<GUIButton> buttons, GUIButton selectedButton)
{
DisableButtons(buttons, (btn) => btn == selectedButton);
}
private static void DisableButtons(IEnumerable<GUIButton> buttons, Func<GUIButton, bool> isSelectedButton)
{
foreach (GUIButton btn in buttons)
{
if (btn.CanBeFocused)
{
btn.CanBeFocused = false;
if (isSelectedButton(btn))
{
btn.Selected = true;
}
else
{
btn.TextBlock.OverrideTextColor(Color.DarkGray * 0.8f);
}
}
}
}
private static void SendResponse(UInt16 actionId, int selectedOption)
{
IWriteMessage outmsg = new WriteOnlyMessage();
@@ -608,47 +608,56 @@ namespace Barotrauma
}
break;
case NetworkEventType.CONVERSATION:
UInt16 identifier = msg.ReadUInt16();
string eventSprite = msg.ReadString();
byte dialogType = msg.ReadByte();
bool continueConversation = msg.ReadBoolean();
UInt16 speakerId = msg.ReadUInt16();
string text = msg.ReadString();
bool fadeToBlack = msg.ReadBoolean();
byte optionCount = msg.ReadByte();
List<string> options = new List<string>();
for (int i = 0; i < optionCount; i++)
{
options.Add(msg.ReadString());
}
byte endCount = msg.ReadByte();
int[] endings = new int[endCount];
for (int i = 0; i < endCount; i++)
{
endings[i] = msg.ReadByte();
}
if (string.IsNullOrEmpty(text) && optionCount == 0)
{
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
UInt16 identifier = msg.ReadUInt16();
string eventSprite = msg.ReadString();
byte dialogType = msg.ReadByte();
bool continueConversation = msg.ReadBoolean();
UInt16 speakerId = msg.ReadUInt16();
string text = msg.ReadString();
bool fadeToBlack = msg.ReadBoolean();
byte optionCount = msg.ReadByte();
List<string> options = new List<string>();
for (int i = 0; i < optionCount; i++)
{
if (mb.UserData is Pair<string, UInt16> pair && pair.First == "ConversationAction" && pair.Second == identifier)
options.Add(msg.ReadString());
}
byte endCount = msg.ReadByte();
int[] endings = new int[endCount];
for (int i = 0; i < endCount; i++)
{
endings[i] = msg.ReadByte();
}
if (string.IsNullOrEmpty(text) && optionCount == 0)
{
GUIMessageBox.MessageBoxes.ForEachMod(mb =>
{
(mb as GUIMessageBox)?.Close();
}
});
if (mb.UserData is Pair<string, UInt16> pair && pair.First == "ConversationAction" && pair.Second == identifier)
{
(mb as GUIMessageBox)?.Close();
}
});
}
else
{
ConversationAction.CreateDialog(text, Entity.FindEntityByID(speakerId) as Character, options, endings, eventSprite, identifier, fadeToBlack, (ConversationAction.DialogTypes)dialogType, continueConversation);
}
if (Entity.FindEntityByID(speakerId) is Character speaker)
{
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
speaker.SetCustomInteract(null, null);
}
break;
}
else
case NetworkEventType.CONVERSATION_SELECTED_OPTION:
{
ConversationAction.CreateDialog(text, Entity.FindEntityByID(speakerId) as Character, options, endings, eventSprite, identifier, fadeToBlack, (ConversationAction.DialogTypes)dialogType, continueConversation);
UInt16 identifier = msg.ReadUInt16();
int selectedOption = msg.ReadByte() - 1;
ConversationAction.SelectOption(identifier, selectedOption);
break;
}
if (Entity.FindEntityByID(speakerId) is Character speaker)
{
speaker.CampaignInteractionType = CampaignMode.InteractionType.None;
speaker.SetCustomInteract(null, null);
}
break;
case NetworkEventType.MISSION:
Identifier missionIdentifier = msg.ReadIdentifier();
int locationIndex = msg.ReadInt32();
@@ -38,7 +38,7 @@ namespace Barotrauma
return RichString.Rich(TextManager.GetWithVariable("missionreward", "[reward]", "‖color:gui.orange‖"+rewardText+"‖end‖"));
}
public RichString GetReputationRewardText(Location currLocation)
public RichString GetReputationRewardText()
{
List<LocalizedString> reputationRewardTexts = new List<LocalizedString>();
foreach (var reputationReward in ReputationRewards)
@@ -46,7 +46,7 @@ namespace Barotrauma
FactionPrefab targetFaction;
if (reputationReward.Key == "location" )
{
targetFaction = currLocation.Faction?.Prefab;
targetFaction = OriginLocation.Faction?.Prefab;
}
else
{
@@ -1563,7 +1563,7 @@ namespace Barotrauma
descriptionText += "\n\n" + missionMessage;
}
RichString rewardText = mission.GetMissionRewardText(Submarine.MainSub);
RichString reputationText = mission.GetReputationRewardText(mission.Locations[0]);
RichString reputationText = mission.GetReputationRewardText();
Func<string, string> wrapMissionText(GUIFont font)
{
@@ -1773,7 +1773,7 @@ namespace Barotrauma
{
foreach (UpgradePrefab prefab in categoryData.Prefabs)
{
var frame = UpgradeStore.CreateUpgradeFrame(prefab, categoryData.Category, campaign, new RectTransform(new Vector2(1f, 0.3f), upgradePanel.Content.RectTransform), addBuyButton: false);
var frame = UpgradeStore.CreateUpgradeFrame(prefab, categoryData.Category, campaign, new RectTransform(new Vector2(1f, 0.3f), upgradePanel.Content.RectTransform), addBuyButton: false).Frame;
UpgradeStore.UpdateUpgradeEntry(frame, prefab, categoryData.Category, campaign);
}
}
@@ -796,7 +796,7 @@ namespace Barotrauma
CharacterInfo? ownCharacterInfo = Character.Controlled?.Info ?? GameMain.Client?.CharacterInfo;
if (ownCharacterInfo is null) { return false; }
return info == ownCharacterInfo;
return info.GetIdentifierUsingOriginalName() == ownCharacterInfo.GetIdentifierUsingOriginalName();
}
public static bool CanManageTalents(CharacterInfo targetInfo)
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
@@ -93,6 +92,16 @@ namespace Barotrauma
Repairs
}
private enum UpgradeStoreUserData
{
BuyButton,
BuyButtonLayout,
ProgressBarLayout,
IncreaseLabel,
PriceLabel,
MaterialCostList
}
public UpgradeStore(CampaignUI campaignUI, GUIComponent parent)
{
WaitForServerUpdate = false;
@@ -605,7 +614,7 @@ namespace Barotrauma
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(0.8f - repairIcon.RectTransform.RelativeSize.X, 1, contentLayout)) { Stretch = true };
new GUITextBlock(rectT(1, 0, textLayout), title, font: GUIStyle.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true };
new GUITextBlock(rectT(1, 0, textLayout), TextManager.FormatCurrency(price));
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, contentLayout), childAnchor: Anchor.Center) { UserData = "buybutton" };
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, contentLayout), childAnchor: Anchor.Center) { UserData = UpgradeStoreUserData.BuyButtonLayout };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { Enabled = PlayerBalance >= price && !isDisabled, OnClicked = onPressed };
contentLayout.Recalculate();
buyButtonLayout.Recalculate();
@@ -955,7 +964,7 @@ namespace Barotrauma
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), currentOrPending.UpgradePreviewSprite,
item.PendingItemSwap != null ? TextManager.GetWithVariable("upgrades.pendingitem", "[itemname]", name) : TextManager.GetWithVariable("upgrades.installeditem", "[itemname]", nameWithQuantity),
currentOrPending.Description,
0, null, addBuyButton: canUninstall, addProgressBar: false, buttonStyle: "WeaponUninstallButton"));
0, null, addBuyButton: canUninstall, addProgressBar: false, buttonStyle: "WeaponUninstallButton").Frame);
if (canUninstall && frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton refundButton)
{
@@ -992,11 +1001,11 @@ namespace Barotrauma
int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.Map?.CurrentLocation) * linkedItems.Count();
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
price, replacement,
addBuyButton: true,
frames.Add(CreateUpgradeEntry(rectT(1f, 0.25f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
price, replacement,
addBuyButton: true,
addProgressBar: false,
buttonStyle: isPurchased ? "WeaponInstallButton" : "StoreAddToCrateButton"));
buttonStyle: isPurchased ? "WeaponInstallButton" : "StoreAddToCrateButton").Frame);
if (!(frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton buyButton)) { continue; }
if (PlayerBalance >= price)
@@ -1086,13 +1095,23 @@ namespace Barotrauma
};
}
public static GUIFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
public readonly record struct BuyButtonFrame(GUILayoutGroup Layout, GUIListBox MaterialCostList, GUIButton BuyButton, GUITextBlock PriceText);
public readonly record struct ProgressBarFrame(GUITextBlock ProgressText, GUIProgressBar ProgressBar);
public readonly record struct UpgradeFrame(GUIFrame Frame,
GUIImage Icon,
GUITextBlock Name,
GUITextBlock Description,
Option<BuyButtonFrame> BuyButton,
Option<ProgressBarFrame> ProgressBar);
public static UpgradeFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform, bool addBuyButton = true)
{
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price, new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
}
public static GUIFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, LocalizedString title, LocalizedString body, int price, object? userData, bool addBuyButton = true, bool addProgressBar = true, string buttonStyle = "UpgradeBuyButton", UpgradePrefab? upgradePrefab = null, int currentLevel = 0)
public static UpgradeFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, LocalizedString title, LocalizedString body, int price, object? userData, bool addBuyButton = true, bool addProgressBar = true, string buttonStyle = "UpgradeBuyButton", UpgradePrefab? upgradePrefab = null, int currentLevel = 0)
{
float progressBarHeight = 0.25f;
@@ -1110,21 +1129,26 @@ namespace Barotrauma
* |------------------------------------------------------------------|
*/
GUIFrame prefabFrame = new GUIFrame(parent, style: "ListBoxElement") { SelectedColor = Color.Transparent, UserData = userData };
GUILayoutGroup prefabLayout = new GUILayoutGroup(rectT(0.98f, 0.95f, prefabFrame, Anchor.Center), isHorizontal: true) { Stretch = true };
GUILayoutGroup imageLayout = new GUILayoutGroup(rectT(new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor: Anchor.Center);
var icon = new GUIImage(rectT(0.9f, 0.9f, imageLayout, scaleBasis: ScaleBasis.BothHeight), sprite, scaleToFit: true) { CanBeFocused = false };
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(0.8f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
var name = new GUITextBlock(rectT(1, 0.25f, textLayout), RichString.Rich(title), font: GUIStyle.SubHeadingFont) { AutoScaleHorizontal = true, AutoScaleVertical = true, Padding = Vector4.Zero };
GUILayoutGroup descriptionLayout = new GUILayoutGroup(rectT(1, 0.75f - progressBarHeight, textLayout));
var description = new GUITextBlock(rectT(1, 1, descriptionLayout), body, font: GUIStyle.SmallFont, wrap: true, textAlignment: Alignment.TopLeft) { Padding = Vector4.Zero };
GUILayoutGroup? progressLayout = null;
GUILayoutGroup mainLayout = new GUILayoutGroup(rectT(0.98f, 0.95f, prefabFrame, Anchor.Center), isHorizontal: false);
GUILayoutGroup prefabLayout = new GUILayoutGroup(rectT(1f, addBuyButton ? 0.65f : 1f, mainLayout, Anchor.Center), isHorizontal: true) { Stretch = true };
GUILayoutGroup imageLayout = new GUILayoutGroup(rectT(new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor: Anchor.Center);
var icon = new GUIImage(rectT(0.9f, 0.9f, imageLayout, scaleBasis: ScaleBasis.BothHeight), sprite, scaleToFit: true) { CanBeFocused = false };
GUILayoutGroup textLayout = new GUILayoutGroup(rectT(1f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
var name = new GUITextBlock(rectT(1, 0.25f, textLayout), RichString.Rich(title), font: GUIStyle.SubHeadingFont) { AutoScaleHorizontal = true, AutoScaleVertical = true, Padding = Vector4.Zero };
GUILayoutGroup descriptionLayout = new GUILayoutGroup(rectT(1, 0.75f - progressBarHeight, textLayout));
var description = new GUITextBlock(rectT(1, 1, descriptionLayout), body, font: GUIStyle.SmallFont, wrap: true, textAlignment: Alignment.TopLeft) { Padding = Vector4.Zero };
GUILayoutGroup? progressLayout = null;
GUILayoutGroup? buyButtonLayout = null;
Option<BuyButtonFrame> buyButtonOption = Option<BuyButtonFrame>.None();
Option<ProgressBarFrame> progressBarOption = Option<ProgressBarFrame>.None();
if (addProgressBar)
{
progressLayout = new GUILayoutGroup(rectT(1, 0.25f, textLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft) { UserData = "progressbar" };
new GUIProgressBar(rectT(0.8f, 0.75f, progressLayout), 0.0f, GUIStyle.Orange);
new GUITextBlock(rectT(0.2f, 1, progressLayout), string.Empty, font: GUIStyle.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
progressLayout = new GUILayoutGroup(rectT(1, 0.25f, textLayout), isHorizontal: true, childAnchor: Anchor.CenterLeft) { UserData = UpgradeStoreUserData.ProgressBarLayout };
GUITextBlock progressText = new GUITextBlock(rectT(0.15f, 1, progressLayout), string.Empty, font: GUIStyle.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
GUIProgressBar progressBar = new GUIProgressBar(rectT(0.85f, 0.75f, progressLayout), 0.0f, GUIStyle.Orange);
progressBarOption = Option.Some(new ProgressBarFrame(progressText, progressBar));
}
if (addBuyButton)
@@ -1132,12 +1156,33 @@ namespace Barotrauma
var formattedPrice = TextManager.FormatCurrency(Math.Abs(price));
//negative price = refund
if (price < 0) { formattedPrice = "+" + formattedPrice; }
buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, prefabLayout), childAnchor: Anchor.TopCenter) { UserData = "buybutton" };
var priceText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), formattedPrice, textAlignment: Alignment.Center)
buyButtonLayout = new GUILayoutGroup(rectT(1f, 0.35f, mainLayout), isHorizontal: true) { UserData = UpgradeStoreUserData.BuyButtonLayout };;
GUIListBox materialCostList;
if (upgradePrefab is not null)
{
var increaseText = new GUITextBlock(rectT(imageLayout.RectTransform.RelativeSize.X, 1f, buyButtonLayout), "", textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont)
{
UserData = UpgradeStoreUserData.IncreaseLabel
};
UpdateUpgradePercentageText(increaseText, upgradePrefab, currentLevel);
materialCostList = new GUIListBox(rectT(0.65f - imageLayout.RectTransform.RelativeSize.X, 1f, buyButtonLayout), isHorizontal: true, style: null);
}
else
{
materialCostList = new GUIListBox(rectT(0.65f, 1f, buyButtonLayout), isHorizontal: true, style: null);
}
materialCostList.Visible = false;
materialCostList.UserData = UpgradeStoreUserData.MaterialCostList;
var priceText = new GUITextBlock(rectT(0.2f, 1f, buyButtonLayout), formattedPrice)
{
UserData = UpgradeStoreUserData.PriceLabel,
//prices on swappable items are always visible, upgrade prices are enabled in UpdateUpgradeEntry for purchasable upgrades
Visible = userData is ItemPrefab
};
if (price < 0)
{
priceText.TextColor = GUIStyle.Green;
@@ -1146,15 +1191,13 @@ namespace Barotrauma
{
priceText.Text = string.Empty;
}
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: buttonStyle)
GUIButton buyButton = new GUIButton(rectT(0.15f, 1f, buyButtonLayout), string.Empty, style: buttonStyle)
{
UserData = UpgradeStoreUserData.BuyButton,
Enabled = false
};
if (upgradePrefab != null)
{
var increaseText = new GUITextBlock(rectT(1, 0.2f, buyButtonLayout), "", textAlignment: Alignment.Center);
UpdateUpgradePercentageText(increaseText, upgradePrefab, currentLevel);
}
buyButtonOption = Option.Some(new BuyButtonFrame(buyButtonLayout, materialCostList, buyButton, priceText));
}
description.CalculateHeightFromText();
@@ -1180,7 +1223,7 @@ namespace Barotrauma
progressLayout?.Recalculate();
buyButtonLayout?.Recalculate();
return prefabFrame;
return new UpgradeFrame(prefabFrame, icon, name, description, buyButtonOption, progressBarOption);
}
private static void UpdateUpgradePercentageText(GUITextBlock text, UpgradePrefab upgradePrefab, int currentLevel)
@@ -1202,31 +1245,21 @@ namespace Barotrauma
Submarine? sub = GameMain.GameSession?.Submarine ?? Submarine.MainSub;
if (Campaign is null || sub is null) { return; }
GUIFrame prefabFrame = CreateUpgradeFrame(prefab, category, Campaign, rectT(1f, 0.25f, parent));
var prefabLayout = prefabFrame.GetChild<GUILayoutGroup>();
GUILayoutGroup[] childLayouts = prefabLayout.GetAllChildren<GUILayoutGroup>().ToArray();
var imageLayout = childLayouts[0];
var icon = imageLayout.GetChild<GUIImage>();
var textLayout = childLayouts[1];
var name = textLayout.GetChild<GUITextBlock>();
GUILayoutGroup[] textChildLayouts = textLayout.GetAllChildren<GUILayoutGroup>().ToArray();
var descriptionLayout = textChildLayouts[0];
var description = descriptionLayout.GetChild<GUITextBlock>();
var progressLayout = textChildLayouts[1];
var buyButtonLayout = childLayouts[2];
var buyButton = buyButtonLayout.GetChild<GUIButton>();
UpgradeFrame prefabFrame = CreateUpgradeFrame(prefab, category, Campaign, rectT(1f, 0.4f, parent));
if (!prefabFrame.BuyButton.TryUnwrap(out BuyButtonFrame buyButtonFrame)) { return; }
if (!HasPermission || !prefab.IsApplicable(submarine.Info) || (itemsOnSubmarine != null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab))))
{
prefabFrame.Enabled = false;
description.Enabled = false;
name.Enabled = false;
icon.Color = Color.Gray;
buyButton.Enabled = false;
buyButtonLayout.UserData = null; // prevent UpdateUpgradeEntry() from enabling the button
prefabFrame.Frame.Enabled = false;
prefabFrame.Description.Enabled = false;
prefabFrame.Name.Enabled = false;
prefabFrame.Icon.Color = Color.Gray;
buyButtonFrame.BuyButton.Enabled = false;
buyButtonFrame.Layout.UserData = null; // prevent UpdateUpgradeEntry() from enabling the button
}
buyButton.OnClicked += (button, o) =>
buyButtonFrame.BuyButton.OnClicked += (button, o) =>
{
LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody",
("[upgradename]", prefab.Name),
@@ -1245,7 +1278,7 @@ namespace Barotrauma
return true;
};
UpdateUpgradeEntry(prefabFrame, prefab, category, Campaign);
UpdateUpgradeEntry(prefabFrame.Frame, prefab, category, Campaign);
}
private void CreateItemTooltip(MapEntity entity)
@@ -1628,7 +1661,7 @@ namespace Barotrauma
int maxLevel = prefab.GetMaxLevelForCurrentSub();
LocalizedString progressText = TextManager.GetWithVariables("upgrades.progressformat", ("[level]", currentLevel.ToString()), ("[maxlevel]", maxLevel.ToString()));
if (prefabFrame.FindChild("progressbar", true) is { } progressParent)
if (prefabFrame.FindChild(UpgradeStoreUserData.ProgressBarLayout, true) is { } progressParent)
{
GUIProgressBar bar = progressParent.GetChild<GUIProgressBar>();
if (bar != null)
@@ -1641,36 +1674,111 @@ namespace Barotrauma
if (block != null) { block.Text = progressText; }
}
if (prefabFrame.FindChild("buybutton", true) is { } buttonParent)
if (prefabFrame.FindChild(UpgradeStoreUserData.BuyButtonLayout, true) is not { } buttonParent) { return; }
GUITextBlock priceLabel = (GUITextBlock)buttonParent.FindChild(UpgradeStoreUserData.PriceLabel, recursive: true);
priceLabel.Visible = true;
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
if (!WaitForServerUpdate)
{
List<GUITextBlock> textBlocks = buttonParent.GetAllChildren<GUITextBlock>().ToList();
GUITextBlock priceLabel = textBlocks[0];
priceLabel.Visible = true;
int price = prefab.Price.GetBuyPrice(campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
if (priceLabel != null && !WaitForServerUpdate)
priceLabel.Text = TextManager.FormatCurrency(price);
if (currentLevel >= maxLevel)
{
priceLabel.Text = TextManager.FormatCurrency(price);
if (currentLevel >= maxLevel)
{
priceLabel.Text = TextManager.Get("Upgrade.MaxedUpgrade");
}
priceLabel.Text = TextManager.Get("Upgrade.MaxedUpgrade");
}
}
GUIButton button = buttonParent.GetChild<GUIButton>();
if (button != null)
if (buttonParent.FindChild(UpgradeStoreUserData.IncreaseLabel, recursive: true) is GUITextBlock increaseLabel && !WaitForServerUpdate)
{
UpdateUpgradePercentageText(increaseLabel, prefab, currentLevel);
}
bool isMax = currentLevel >= maxLevel;
if (buttonParent.FindChild(UpgradeStoreUserData.BuyButton, recursive: true) is GUIButton button)
{
bool canBuy = !WaitForServerUpdate && !isMax && campaign.GetBalance() >= price && prefab.HasResourcesToUpgrade(Character.Controlled, currentLevel + 1);
button.Enabled = canBuy;
}
if (prefabFrame.FindChild(UpgradeStoreUserData.MaterialCostList, true) is GUIListBox itemList)
{
if (isMax)
{
button.Enabled = currentLevel < maxLevel;
if (WaitForServerUpdate || campaign.GetBalance() < price)
{
button.Enabled = false;
}
itemList.Visible = false;
}
GUITextBlock increaseLabel = textBlocks[1];
if (increaseLabel != null && !WaitForServerUpdate)
else
{
UpdateUpgradePercentageText(increaseLabel, prefab, currentLevel);
CreateMaterialCosts(itemList, prefab, currentLevel + 1);
}
}
static void CreateMaterialCosts(GUIListBox list, UpgradePrefab prefab, int targetLevel)
{
list.Content.ClearChildren();
List<Item> allItems = Character.Controlled?.Inventory?.FindAllItems(recursive: true) ?? new List<Item>();
var resources = prefab.GetApplicableResources(targetLevel);
foreach (ApplicableResourceCollection collection in resources)
{
list.Visible = true;
int length = collection.MatchingItems.Length;
if (length is 0) { continue; }
ItemPrefab defaultItemPrefab = collection.MatchingItems.First();
GUILayoutGroup wrapperLayout = new GUILayoutGroup(rectT(0.25f, 1f, list.Content));
GUIFrame itemFrame = new GUIFrame(rectT(1f, 1f, wrapperLayout), style: null)
{
ToolTip = defaultItemPrefab.Name
};
bool hasItems = collection.Cost.Amount <= allItems.Count(collection.Cost.MatchesItem);
Sprite icon = defaultItemPrefab.InventoryIcon ?? prefab.Sprite;
Color iconColor = defaultItemPrefab.InventoryIcon is null ? defaultItemPrefab.SpriteColor : defaultItemPrefab.InventoryIconColor;
GUIImage itemIcon = new GUIImage(new RectTransform(Vector2.One, itemFrame.RectTransform, scaleBasis: ScaleBasis.Smallest, anchor: Anchor.Center), sprite: icon, scaleToFit: true)
{
Color = hasItems ? iconColor : iconColor * 0.9f,
CanBeFocused = false
};
// item count text
new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.5f), itemIcon.RectTransform, anchor: Anchor.BottomRight), $"{collection.Count}", font: GUIStyle.Font, textAlignment: Alignment.BottomRight)
{
Shadow = true,
CanBeFocused = false,
Padding = Vector4.Zero,
TextColor = hasItems ? Color.White : GUIStyle.Red,
};
if (length is 1) { continue; }
// we have more than 1 item, show a "slideshow" of the items
float index = 0f;
GUICustomComponent customComponent = new GUICustomComponent(rectT(1f, 1f, itemFrame), null, (deltaTime, component) =>
{
index += deltaTime / 3f;
if (index > length) { index = 0; }
ItemPrefab currentPrefab = collection.MatchingItems[(int)MathF.Floor(index)];
Sprite icon = currentPrefab.InventoryIcon ?? prefab.Sprite;
Color iconColor = currentPrefab.InventoryIcon is null ? currentPrefab.SpriteColor : currentPrefab.InventoryIconColor;
itemIcon.Sprite = icon;
itemIcon.Color = hasItems ? iconColor : iconColor * 0.9f;
itemFrame.ToolTip = currentPrefab.Name;
})
{
CanBeFocused = false
};
}
}
}
@@ -735,8 +735,8 @@ namespace Barotrauma
{
Client.Quit();
Client = null;
MainMenuScreen.Select();
}
MainMenuScreen.Select();
if (connectCommand.EndpointOrLobby.TryGet(out ulong lobbyId))
{
@@ -1099,37 +1099,6 @@ namespace Barotrauma
GameSession = null;
}
public void ShowEditorDisclaimer()
{
var msgBox = new GUIMessageBox(TextManager.Get("EditorDisclaimerTitle"), TextManager.Get("EditorDisclaimerText"));
var linkHolder = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.25f), msgBox.Content.RectTransform)) { Stretch = true, RelativeSpacing = 0.025f };
linkHolder.RectTransform.MaxSize = new Point(int.MaxValue, linkHolder.Rect.Height);
List<(LocalizedString Caption, string Url)> links = new List<(LocalizedString, string)>()
{
(TextManager.Get("EditorDisclaimerWikiLink"), TextManager.Get("EditorDisclaimerWikiUrl").Fallback("https://barotraumagame.com/wiki").Value),
(TextManager.Get("EditorDisclaimerDiscordLink"), TextManager.Get("EditorDisclaimerDiscordUrl").Fallback("https://discordapp.com/invite/undertow").Value),
};
foreach (var link in links)
{
new GUIButton(new RectTransform(new Vector2(1.0f, 0.2f), linkHolder.RectTransform), link.Caption, style: "MainMenuGUIButton", textAlignment: Alignment.Left)
{
UserData = link.Url,
OnClicked = (btn, userdata) =>
{
ShowOpenUrlInWebBrowserPrompt(userdata as string);
return true;
}
};
}
msgBox.InnerFrame.RectTransform.MinSize = new Point(0,
msgBox.InnerFrame.Rect.Height + linkHolder.Rect.Height + msgBox.Content.AbsoluteSpacing * 2 + 10);
var config = GameSettings.CurrentConfig;
config.EditorDisclaimerShown = true;
GameSettings.SetCurrentConfig(config);
GameSettings.SaveCurrentConfig();
}
public void ShowBugReporter()
{
if (GUIMessageBox.VisibleBox != null && GUIMessageBox.VisibleBox.UserData as string == "bugreporter")
@@ -85,7 +85,6 @@ namespace Barotrauma
/// </summary>
private SinglePlayerCampaign(string mapSeed, CampaignSettings settings) : base(GameModePreset.SinglePlayerCampaign, settings)
{
CampaignMetadata = new CampaignMetadata();
UpgradeManager = new UpgradeManager(this);
Settings = settings;
InitFactions();
@@ -107,18 +106,16 @@ namespace Barotrauma
private SinglePlayerCampaign(XElement element) : base(GameModePreset.SinglePlayerCampaign, CampaignSettings.Empty)
{
IsFirstRound = false;
foreach (var subElement in element.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "metadata":
CampaignMetadata = new CampaignMetadata(subElement);
CampaignMetadata.Load(subElement);
break;
}
}
CampaignMetadata ??= new CampaignMetadata();
InitFactions();
foreach (var subElement in element.Elements())
@@ -21,7 +21,7 @@ namespace Barotrauma
private readonly GameMode gameMode;
private readonly Dictionary<Faction, float> initialFactionReputations = new Dictionary<Faction, float>();
private readonly Dictionary<Identifier, float> initialFactionReputations = new Dictionary<Identifier, float>();
public GUILayoutGroup ButtonArea { get; private set; }
@@ -39,7 +39,7 @@ namespace Barotrauma
{
foreach (Faction faction in campaignMode.Factions)
{
initialFactionReputations.Add(faction, faction.Reputation.Value);
initialFactionReputations.Add(faction.Prefab.Identifier, faction.Reputation.Value);
}
}
}
@@ -312,17 +312,26 @@ namespace Barotrauma
var missionDescription = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform),
RichString.Rich(missionMessage), wrap: true);
int reward = displayedMission.GetReward(Submarine.MainSub);
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed && reward > 0)
if (selectedMissions.Contains(displayedMission) && displayedMission.Completed)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub)));
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled)
RichString reputationText = displayedMission.GetReputationRewardText();
if (!reputationText.IsNullOrEmpty())
{
var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(reward));
if (share > 0)
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), reputationText);
}
if (reward > 0)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub)));
if (GameMain.IsMultiplayer && Character.Controlled is { } controlled)
{
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
RichString yourShareString = RichString.Rich(TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}")));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), yourShareString);
var (share, percentage, _) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, GameSession.GetSessionCrewCharacters(CharacterType.Player).Where(c => c != controlled), Option<int>.Some(reward));
if (share > 0)
{
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
RichString yourShareString = RichString.Rich(TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}")));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), yourShareString);
}
}
}
}
@@ -400,26 +409,10 @@ namespace Barotrauma
};
reputationList.ContentBackground.Color = Color.Transparent;
/*if (startLocation.Type.HasOutpost && startLocation.Reputation != null)
{
var iconStyle = GUIStyle.GetComponentStyle("LocationReputationIcon");
var locationFrame = CreateReputationElement(
reputationList.Content,
startLocation.Name,
startLocation.Reputation.Value, startLocation.Reputation.NormalizedValue, initialLocationReputation,
startLocation.Type.Name, "",
iconStyle?.GetDefaultSprite(), startLocation.Type.GetPortrait(0), iconStyle?.Color ?? Color.White);
CreatePathUnlockElement(locationFrame, null, startLocation);
}*/
foreach (Faction faction in campaignMode.Factions.OrderBy(f => f.Prefab.MenuOrder).ThenBy(f => f.Prefab.Name))
{
float initialReputation = faction.Reputation.Value;
if (initialFactionReputations.ContainsKey(faction))
{
initialReputation = initialFactionReputations[faction];
}
else
if (!initialFactionReputations.TryGetValue(faction.Prefab.Identifier, out initialReputation))
{
DebugConsole.AddWarning($"Could not determine reputation change for faction \"{faction.Prefab.Name}\" (faction was not present at the start of the round).");
}
@@ -454,50 +447,60 @@ namespace Barotrauma
void CreatePathUnlockElement(GUIComponent reputationFrame, Faction faction, Location location)
{
if (GameMain.GameSession?.Campaign?.Map != null)
if (GameMain.GameSession?.Campaign?.Map == null) { return; }
IEnumerable<LocationConnection> connectionsBetweenBiomes =
GameMain.GameSession.Campaign.Map.Connections.Where(c => c.Locations[0].Biome != c.Locations[1].Biome);
foreach (LocationConnection connection in connectionsBetweenBiomes)
{
foreach (LocationConnection connection in GameMain.GameSession.Campaign.Map.Connections)
if (!connection.Locked || (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered)) { continue; }
//don't show the "reputation required to unlock" text if another connection between the biomes has already been unlocked
if (connectionsBetweenBiomes.Where(c => !c.Locked).Any(c =>
(c.Locations[0].Biome == connection.Locations[0].Biome && c.Locations[1].Biome == connection.Locations[1].Biome) ||
(c.Locations[1].Biome == connection.Locations[0].Biome && c.Locations[0].Biome == connection.Locations[1].Biome)))
{
if (!connection.Locked || (!connection.Locations[0].Discovered && !connection.Locations[1].Discovered)) { continue; }
continue;
}
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
var unlockEvent = EventPrefab.GetUnlockPathEvent(gateLocation.LevelData.Biome.Identifier, gateLocation.Faction);
var gateLocation = connection.Locations[0].IsGateBetweenBiomes ? connection.Locations[0] : connection.Locations[1];
var unlockEvent = EventPrefab.GetUnlockPathEvent(gateLocation.LevelData.Biome.Identifier, gateLocation.Faction);
if (unlockEvent == null) { continue; }
if (unlockEvent.Faction.IsEmpty)
if (unlockEvent == null) { continue; }
if (unlockEvent.Faction.IsEmpty)
{
if (location == null || gateLocation != location) { continue; }
}
else
{
if (faction == null || faction.Prefab.Identifier != unlockEvent.Faction) { continue; }
}
if (unlockEvent != null)
{
Reputation unlockReputation = gateLocation.Reputation;
Faction unlockFaction = null;
if (!unlockEvent.Faction.IsEmpty)
{
if (location == null || gateLocation != location) { continue; }
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.Faction);
unlockReputation = unlockFaction?.Reputation;
}
else
float normalizedUnlockReputation = MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation);
RichString unlockText = RichString.Rich(TextManager.GetWithVariables(
"lockedpathreputationrequirement",
("[reputation]", Reputation.GetFormattedReputationText(normalizedUnlockReputation, unlockEvent.UnlockPathReputation, addColorTags: true)),
("[biomename]", $"‖color:gui.orange‖{connection.LevelData.Biome.DisplayName}‖end‖")));
var unlockInfoPanel = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.0f), reputationFrame.RectTransform, Anchor.BottomCenter) { MinSize = new Point(0, GUI.IntScale(30)), AbsoluteOffset = new Point(0, GUI.IntScale(3)) },
unlockText, style: "GUIButtonRound", textAlignment: Alignment.Center, textColor: GUIStyle.TextColorNormal);
unlockInfoPanel.Color = Color.Lerp(unlockInfoPanel.Color, Color.Black, 0.8f);
unlockInfoPanel.UserData = "unlockinfo";
if (unlockInfoPanel.TextSize.X > unlockInfoPanel.Rect.Width * 0.7f)
{
if (faction == null || faction.Prefab.Identifier != unlockEvent.Faction) { continue; }
}
if (unlockEvent != null)
{
Reputation unlockReputation = gateLocation.Reputation;
Faction unlockFaction = null;
if (!unlockEvent.Faction.IsEmpty)
{
unlockFaction = GameMain.GameSession.Campaign.Factions.Find(f => f.Prefab.Identifier == unlockEvent.Faction);
unlockReputation = unlockFaction?.Reputation;
}
float normalizedUnlockReputation = MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation);
RichString unlockText = RichString.Rich(TextManager.GetWithVariables(
"lockedpathreputationrequirement",
("[reputation]", Reputation.GetFormattedReputationText(normalizedUnlockReputation, unlockEvent.UnlockPathReputation, addColorTags: true)),
("[biomename]", $"‖color:gui.orange‖{connection.LevelData.Biome.DisplayName}‖end‖")));
var unlockInfoPanel = new GUITextBlock(new RectTransform(new Vector2(0.8f, 0.0f), reputationFrame.RectTransform, Anchor.BottomCenter) { MinSize = new Point(0, GUI.IntScale(30)), AbsoluteOffset = new Point(0, GUI.IntScale(3)) },
unlockText, style: "GUIButtonRound", textAlignment: Alignment.Center, textColor: GUIStyle.TextColorNormal);
unlockInfoPanel.Color = Color.Lerp(unlockInfoPanel.Color, Color.Black, 0.8f);
unlockInfoPanel.UserData = "unlockinfo";
if (unlockInfoPanel.TextSize.X > unlockInfoPanel.Rect.Width * 0.7f)
{
unlockInfoPanel.Font = GUIStyle.SmallFont;
}
unlockInfoPanel.Font = GUIStyle.SmallFont;
}
}
}
}
}
}
@@ -965,7 +965,7 @@ namespace Barotrauma
break;
case QuickUseAction.PutToEquippedItem:
//order by the condition of the contained item to prefer putting into the item with the emptiest ammo/battery/tank
foreach (Item heldItem in character.HeldItems.OrderBy(it => it.ContainedItems.FirstOrDefault()?.Condition ?? 0.0f))
foreach (Item heldItem in character.HeldItems.OrderBy(it => it.GetComponent<ItemContainer>()?.GetContainedIndicatorState() ?? 0.0f))
{
if (heldItem.OwnInventory == null) { continue; }
//don't allow swapping if we're moving items into an item with 1 slot holding a stack of items
@@ -55,7 +55,7 @@ namespace Barotrauma.Items.Components
public void DrawElectricity(SpriteBatch spriteBatch)
{
if (timer <= 0.0f) { return; }
if (timer <= 0.0f && Screen.Selected is { IsEditor: false }) { return; }
for (int i = 0; i < nodes.Count; i++)
{
if (nodes[i].Length <= 1.0f) { continue; }
@@ -1,7 +1,9 @@
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections;
using System.Linq;
using static Barotrauma.Inventory;
namespace Barotrauma.Items.Components
{
@@ -250,6 +252,83 @@ namespace Barotrauma.Items.Components
return true;
}
public float GetContainedIndicatorState()
{
if (ShowConditionInContainedStateIndicator)
{
return item.Condition / item.MaxCondition;
}
int targetSlot = Math.Max(ContainedStateIndicatorSlot, 0);
if (targetSlot >= Inventory.Capacity) { return 0.0f; }
var containedItems = Inventory.GetItemsAt(targetSlot);
if (containedItems == null) { return 0.0f; }
Item containedItem = containedItems.FirstOrDefault();
if (ShowTotalStackCapacityInContainedStateIndicator)
{
// No item on the defined slot, check if the items on other slots can be used.
containedItem ??=
containedItems.FirstOrDefault() ??
Inventory.AllItems.FirstOrDefault(it => CanBeContained(it, targetSlot));
if (containedItem == null) { return 0.0f; }
int ignoredItemCount = 0;
var subContainableItems = AllSubContainableItems;
float capacity = GetMaxStackSize(targetSlot);
if (subContainableItems != null)
{
bool useMainContainerCapacity = true;
foreach (Item it in 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 *= MainContainerCapacity;
}
else
{
// Ignore all items in the main container.
ignoredItemCount = Inventory.AllItems.Count(it => subContainableItems.Any(ri => !ri.MatchesItem(it)));
capacity *= Capacity - MainContainerCapacity;
}
}
int itemCount = Inventory.AllItems.Count() - ignoredItemCount;
return Math.Min(itemCount / Math.Max(capacity, 1), 1);
}
else
{
if (containedItem != null && (Inventory.Capacity == 1 || HasSubContainers))
{
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, GetMaxStackSize(targetSlot));
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
{
return containedItems.Count() / (float)maxStackSize;
}
}
return Inventory.Capacity == 1 || ContainedStateIndicatorSlot > -1 ?
(containedItem == null ? 0.0f : containedItem.Condition / containedItem.MaxCondition) :
Inventory.EmptySlotCount / (float)Inventory.Capacity;
}
}
public void Draw(SpriteBatch spriteBatch, bool editing = false, float itemDepth = -1)
{
if (hideItems || (item.body != null && !item.body.Enabled)) { return; }
@@ -14,8 +14,6 @@ namespace Barotrauma.Items.Components
private CoroutineHandle resetPredictionCoroutine;
private float resetPredictionTimer;
private float currentBrightness;
public Vector2 DrawSize
{
get { return new Vector2(Light.Range * 2, Light.Range * 2); }
@@ -29,14 +27,21 @@ namespace Barotrauma.Items.Components
Light.Position = ParentBody != null ? ParentBody.Position : item.Position;
}
partial void SetLightSourceState(bool enabled, float brightness)
partial void SetLightSourceState(bool enabled, float? brightness)
{
if (Light == null) { return; }
Light.Enabled = enabled;
currentBrightness = brightness;
if (brightness.HasValue)
{
lightBrightness = brightness.Value;
}
else
{
lightBrightness = enabled ? 1.0f : 0.0f;
}
if (enabled)
{
Light.Color = LightColor.Multiply(brightness);
Light.Color = LightColor.Multiply(lightBrightness);
}
}
@@ -519,7 +519,10 @@ namespace Barotrauma.Items.Components
Color color = !hasPower ? NoPowerColor : turret.ActiveUser is null ? Color.DimGray : GUIStyle.Green;
weaponSprite.Draw(batch, center, color, origin, rotation, scale, SpriteEffects.None);
}
});
})
{
CanBeFocused = false
};
weaponChilds.Add(component, frame);
}
@@ -1181,13 +1181,18 @@ namespace Barotrauma.Items.Components
if (dockingPort.Item.Submarine == null) { continue; }
if (dockingPort.Item.Submarine.Info.IsWreck) { continue; }
// docking ports should be shown even if defined as not, if the submarine is the same as the sonar's
if (!dockingPort.Item.Submarine.ShowSonarMarker && dockingPort.Item.Submarine != item.Submarine && !dockingPort.Item.Submarine.Info.IsOutpost) { continue; }
if (!dockingPort.Item.Submarine.ShowSonarMarker && dockingPort.Item.Submarine != item.Submarine &&
!dockingPort.Item.Submarine.Info.IsOutpost && !dockingPort.Item.Submarine.Info.IsBeacon)
{
continue;
}
//don't show the docking ports of the opposing team on the sonar
if (item.Submarine != null &&
item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
dockingPort.Item.Submarine != GameMain.NetworkMember?.RespawnManager?.RespawnShuttle &&
dockingPort.Item.Submarine.Info.Type != SubmarineType.Outpost)
!dockingPort.Item.Submarine.Info.IsOutpost &&
!dockingPort.Item.Submarine.Info.IsBeacon)
{
// specifically checking for friendlyNPC seems more logical here
if (dockingPort.Item.Submarine.TeamID != item.Submarine.TeamID && dockingPort.Item.Submarine.TeamID != CharacterTeamType.FriendlyNPC) { continue; }
@@ -1603,88 +1603,7 @@ namespace Barotrauma
if (itemContainer != null && itemContainer.ShowContainedStateIndicator && itemContainer.Capacity > 0)
{
float containedState = 0.0f;
if (itemContainer.ShowConditionInContainedStateIndicator)
{
containedState = item.Condition / item.MaxCondition;
}
else
{
int targetSlot = Math.Max(itemContainer.ContainedStateIndicatorSlot, 0);
ItemSlot containedItemSlot = null;
if (targetSlot < itemContainer.Inventory.slots.Length)
{
containedItemSlot = itemContainer.Inventory.slots[targetSlot];
}
if (containedItemSlot != null)
{
Item containedItem = containedItemSlot.FirstOrDefault();
if (itemContainer.ShowTotalStackCapacityInContainedStateIndicator)
{
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;
}
}
}
}
}
float containedState = itemContainer.GetContainedIndicatorState();
int dir = slot.SubInventoryDir;
Rectangle containedIndicatorArea = new Rectangle(rect.X,
dir < 0 ? rect.Bottom + HUDLayoutSettings.Padding / 2 : rect.Y - HUDLayoutSettings.Padding / 2 - ContainedIndicatorHeight, rect.Width, ContainedIndicatorHeight);
@@ -1807,6 +1726,7 @@ namespace Barotrauma
}
}
private static void DrawItemStateIndicator(
SpriteBatch spriteBatch, Inventory inventory,
Sprite indicatorSprite, Sprite emptyIndicatorSprite, Rectangle containedIndicatorArea, float containedState,
@@ -260,16 +260,16 @@ namespace Barotrauma
public override void UpdatePlacing(Camera cam)
{
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
if (PlayerInput.SecondaryMouseButtonClicked())
{
Selected = null;
return;
}
var potentialContainer = MapEntity.GetPotentialContainer(cam.ScreenToWorld(PlayerInput.MousePosition));
var potentialContainer = MapEntity.GetPotentialContainer(position);
Vector2 position = Submarine.MouseToWorldGrid(cam, Submarine.MainSub);
if (!ResizeHorizontal && !ResizeVertical)
{
if (PlayerInput.PrimaryMouseButtonClicked() && GUI.MouseOn == null)
@@ -293,11 +293,11 @@ namespace Barotrauma
GUI.DrawRectangle(spriteBatch,
new Vector2(drawRect.X, -drawRect.Y),
new Vector2(rect.Width, rect.Height),
Color.Blue * alpha, false, (ID % 255) * 0.000001f, (int)Math.Max(1.5f / Screen.Selected.Cam.Zoom, 1.0f));
Color.Blue * alpha, false, (ID % 255) * 0.000001f, (int)Math.Max(MathF.Ceiling(1.5f / Screen.Selected.Cam.Zoom), 1.0f));
GUI.DrawRectangle(spriteBatch,
new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height),
GUIStyle.Red * ((100.0f - OxygenPercentage) / 400.0f) * alpha, true, 0, (int)Math.Max(1.5f / Screen.Selected.Cam.Zoom, 1.0f));
GUIStyle.Red * ((100.0f - OxygenPercentage) / 400.0f) * alpha, true, 0, (int)Math.Max(MathF.Ceiling(1.5f / Screen.Selected.Cam.Zoom), 1.0f));
if (GameMain.DebugDraw)
{
@@ -544,9 +544,6 @@ namespace Barotrauma
Vector2 rectCenter = new Vector2(rect.Center.X, rect.Center.Y);
Vector2 viewOffset = DrawOffset + drawOffsetNoise;
bool cursorOnOverlay = false;
if (HighlightedLocation != null)
{
Vector2 highlightedLocationDrawPos = rectCenter + (HighlightedLocation.MapPosition + viewOffset) * zoom;
@@ -574,52 +571,41 @@ namespace Barotrauma
{
locationInfoRt.Pivot = locationInfoRt.Pivot == Pivot.TopLeft ? Pivot.TopRight : Pivot.BottomRight;
}
Rectangle highlightedLocationRect = new Rectangle(highlightedLocationDrawPos.ToPoint(), new Point(GUI.IntScale(25)));
Rectangle overlayRect = Rectangle.Union(highlightedLocationRect, locationInfoRt.Rect);
if (overlayRect.Contains(PlayerInput.MousePosition))
{
cursorOnOverlay = true;
}
locationInfoOverlay?.AddToGUIUpdateList(order: 1);
}
if (!cursorOnOverlay)
float closestDist = 0.0f;
HighlightedLocation = null;
if ((GUI.MouseOn == null || GUI.MouseOn == mapContainer))
{
float closestDist = 0.0f;
HighlightedLocation = null;
if ((GUI.MouseOn == null || GUI.MouseOn == mapContainer))
for (int i = 0; i < Locations.Count; i++)
{
for (int i = 0; i < Locations.Count; i++)
Location location = Locations[i];
if (IsInFogOfWar(location) && !(currentDisplayLocation?.Connections.Any(c => c.Locations.Contains(location)) ?? false) && !GameMain.DebugDraw) { continue; }
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
if (!rect.Contains(pos)) { continue; }
Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite;
float iconScale = generationParams.LocationIconSize / locationSprite.size.X;
if (location == currentDisplayLocation) { iconScale *= 1.2f; }
Rectangle drawRect = locationSprite.SourceRect;
drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f);
drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f);
drawRect.X = (int)pos.X - drawRect.Width / 2;
drawRect.Y = (int)pos.Y - drawRect.Width / 2;
if (!drawRect.Contains(PlayerInput.MousePosition)) { continue; }
float dist = Vector2.Distance(PlayerInput.MousePosition, pos);
if (HighlightedLocation == null || dist < closestDist)
{
Location location = Locations[i];
if (IsInFogOfWar(location) && !(currentDisplayLocation?.Connections.Any(c => c.Locations.Contains(location)) ?? false) && !GameMain.DebugDraw) { continue; }
Vector2 pos = rectCenter + (location.MapPosition + viewOffset) * zoom;
if (!rect.Contains(pos)) { continue; }
Sprite locationSprite = location.IsCriticallyRadiated() ? location.Type.RadiationSprite ?? location.Type.Sprite : location.Type.Sprite;
float iconScale = generationParams.LocationIconSize / locationSprite.size.X;
if (location == currentDisplayLocation) { iconScale *= 1.2f; }
Rectangle drawRect = locationSprite.SourceRect;
drawRect.Width = (int)(drawRect.Width * iconScale * zoom * 1.4f);
drawRect.Height = (int)(drawRect.Height * iconScale * zoom * 1.4f);
drawRect.X = (int)pos.X - drawRect.Width / 2;
drawRect.Y = (int)pos.Y - drawRect.Width / 2;
if (!drawRect.Contains(PlayerInput.MousePosition)) { continue; }
float dist = Vector2.Distance(PlayerInput.MousePosition, pos);
if (HighlightedLocation == null || dist < closestDist)
{
closestDist = dist;
HighlightedLocation = location;
}
closestDist = dist;
HighlightedLocation = location;
}
}
}
if (SelectedConnection != null)
{
@@ -528,11 +528,11 @@ namespace Barotrauma
Item targetContainer = null;
bool isShiftDown = PlayerInput.IsShiftDown();
if (!isShiftDown) return null;
if (!isShiftDown) { return null; }
foreach (MapEntity e in mapEntityList)
{
if (!e.SelectableInEditor ||!(e is Item potentialContainer)) { continue; }
if (!e.SelectableInEditor || e is not Item potentialContainer) { continue; }
if (e.IsMouseOn(position))
{
@@ -83,7 +83,10 @@ namespace Barotrauma
{
string errorMsg = "Failed to load sound file \"" + filename + "\" (file not found).";
DebugConsole.ThrowError(errorMsg, e);
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:FileNotFound" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
if (!ContentPackageManager.ModsEnabled)
{
GameAnalyticsManager.AddErrorEventOnce("RoundSound.LoadRoundSound:FileNotFound" + filename, GameAnalyticsManager.ErrorSeverity.Error, errorMsg + "\n" + Environment.StackTrace.CleanupStackTrace());
}
return null;
}
catch (System.IO.InvalidDataException e)
@@ -66,9 +66,20 @@ namespace Barotrauma.Networking
};
var addressOrAccountId = bannedPlayer.AddressOrAccountId;
GUITextBlock textBlock = new GUITextBlock(
new RectTransform(new Vector2(0.5f, 1.0f), topArea.RectTransform),
bannedPlayer.Name + " (" + addressOrAccountId + ")") { CanBeFocused = true };
string nameText = bannedPlayer.Name;
if (addressOrAccountId.TryCast(out Address address))
{
nameText += $" ({address.StringRepresentation})";
}
else if (addressOrAccountId.TryCast(out AccountId accountId))
{
nameText += $" ({accountId.StringRepresentation})";
}
GUITextBlock textBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), topArea.RectTransform), nameText)
{
CanBeFocused = true
};
textBlock.RectTransform.MinSize = new Point(
(int)textBlock.Font.MeasureString(textBlock.Text.SanitizedValue).X, 0);
@@ -106,7 +117,7 @@ namespace Barotrauma.Networking
private bool RemoveBan(GUIButton button, object obj)
{
if (!(obj is BannedPlayer banned)) { return false; }
if (obj is not BannedPlayer banned) { return false; }
localRemovedBans.Add(banned.UniqueIdentifier);
RecreateBanFrame();
@@ -34,6 +34,7 @@ namespace Barotrauma.Networking
catch
{
DebugConsole.ThrowError($"Failed to start ChildServerRelay Process. File: {processInfo.FileName}, arguments: {processInfo.Arguments}");
ForceShutDown();
throw;
}
@@ -1102,11 +1102,7 @@ namespace Barotrauma.Networking
VoipClient = new VoipClient(this, ClientPeer);
//if we're still in the game, roundsummary or lobby screen, we don't need to redownload the mods
if (!(Screen.Selected is GameScreen) && !(Screen.Selected is RoundSummaryScreen) && !(Screen.Selected is NetLobbyScreen))
{
GameMain.ModDownloadScreen.Select();
}
else
if (Screen.Selected is GameScreen or RoundSummaryScreen or NetLobbyScreen)
{
EntityEventManager.ClearSelf();
foreach (Character c in Character.CharacterList)
@@ -1114,6 +1110,10 @@ namespace Barotrauma.Networking
c.ResetNetState();
}
}
else
{
GameMain.ModDownloadScreen.Select();
}
chatBox.InputBox.Enabled = true;
if (GameMain.NetLobbyScreen?.ChatInput != null)
@@ -1535,8 +1535,9 @@ namespace Barotrauma.Networking
roundInitStatus = RoundInitStatus.WaitingForStartGameFinalize;
DateTime? timeOut = null;
//wait for up to 30 seconds for the server to send the STARTGAMEFINALIZE message
TimeSpan timeOutDuration = new TimeSpan(0, 0, seconds: 30);
DateTime timeOut = DateTime.Now + timeOutDuration;
DateTime requestFinalizeTime = DateTime.Now;
TimeSpan requestFinalizeInterval = new TimeSpan(0, 0, 2);
IWriteMessage msg = new WriteOnlyMessage();
@@ -1545,11 +1546,15 @@ namespace Barotrauma.Networking
GUIMessageBox interruptPrompt = null;
while (true)
if (includesFinalize)
{
try
ReadStartGameFinalize(inc);
}
else
{
while (true)
{
if (timeOut.HasValue)
try
{
if (DateTime.Now > requestFinalizeTime)
{
@@ -1583,41 +1588,30 @@ namespace Barotrauma.Networking
return true;
};
}
}
else
{
if (includesFinalize)
if (!connected)
{
ReadStartGameFinalize(inc);
roundInitStatus = RoundInitStatus.Interrupted;
break;
}
//wait for up to 30 seconds for the server to send the STARTGAMEFINALIZE message
timeOut = DateTime.Now + timeOutDuration;
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize) { break; }
}
if (!connected)
catch (Exception e)
{
roundInitStatus = RoundInitStatus.Interrupted;
DebugConsole.ThrowError("There was an error initializing the round.", e, true);
roundInitStatus = RoundInitStatus.Error;
break;
}
if (roundInitStatus != RoundInitStatus.WaitingForStartGameFinalize) { break; }
//waiting for a STARTGAMEFINALIZE message
yield return CoroutineStatus.Running;
}
catch (Exception e)
{
DebugConsole.ThrowError("There was an error initializing the round.", e, true);
roundInitStatus = RoundInitStatus.Error;
break;
}
//waiting for a STARTGAMEFINALIZE message
yield return CoroutineStatus.Running;
}
interruptPrompt?.Close();
interruptPrompt = null;
if (roundInitStatus != RoundInitStatus.Started)
{
if (roundInitStatus != RoundInitStatus.Interrupted)
@@ -2101,13 +2095,12 @@ namespace Barotrauma.Networking
case ServerNetSegment.EntityPosition:
inc.ReadPadBits(); //padding is required here to make sure any padding bits within tempBuffer are read correctly
bool isItem = inc.ReadBoolean(); inc.ReadPadBits();
UInt32 incomingUintIdentifier = inc.ReadUInt32();
UInt16 id = inc.ReadUInt16();
uint msgLength = inc.ReadVariableUInt32();
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
var entity = Entity.FindEntityByID(id) as IServerPositionSync;
var header = INetSerializableStruct.Read<EntityPositionHeader>(inc);
var entity = Entity.FindEntityByID(header.EntityId) as IServerPositionSync;
if (msgEndPos > inc.LengthBits)
{
DebugConsole.ThrowError($"Error while reading a position update for the entity \"({entity?.ToString() ?? "null"})\". Message length exceeds the size of the buffer.");
@@ -2117,15 +2110,15 @@ namespace Barotrauma.Networking
debugEntityList.Add(entity);
if (entity != null)
{
if (entity is Item != isItem)
if (entity is Item != header.IsItem)
{
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message. Entity type does not match (server entity is {(isItem ? "an item" : "not an item")}, client entity is {(entity?.GetType().ToString() ?? "null")}). Ignoring the message...");
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message. Entity type does not match (server entity is {(header.IsItem ? "an item" : "not an item")}, client entity is {(entity?.GetType().ToString() ?? "null")}). Ignoring the message...");
}
else if (entity is MapEntity { Prefab: { UintIdentifier: { } uintIdentifier } } me &&
uintIdentifier != incomingUintIdentifier)
else if (entity is MapEntity { Prefab.UintIdentifier: var uintIdentifier } me &&
uintIdentifier != header.PrefabUintIdentifier)
{
DebugConsole.AddWarning($"Received a potentially invalid ENTITY_POSITION message."
+$"Entity identifier does not match (server entity is {MapEntityPrefab.List.FirstOrDefault(p => p.UintIdentifier == incomingUintIdentifier)?.Identifier.Value ?? "[not found]"}, "
+$"Entity identifier does not match (server entity is {MapEntityPrefab.List.FirstOrDefault(p => p.UintIdentifier == header.PrefabUintIdentifier)?.Identifier.Value ?? "[not found]"}, "
+$"client entity is {me.Prefab.Identifier}). Ignoring the message...");
}
else
@@ -2133,7 +2126,6 @@ namespace Barotrauma.Networking
entity.ClientReadPosition(inc, sendingTime);
}
}
//force to the correct position in case the entity doesn't exist
//or the message wasn't read correctly for whatever reason
inc.BitPosition = msgEndPos;
@@ -2144,7 +2136,7 @@ namespace Barotrauma.Networking
break;
case ServerNetSegment.EntityEvent:
case ServerNetSegment.EntityEventInitial:
if (!EntityEventManager.Read(segment, inc, sendingTime, debugEntityList))
if (!EntityEventManager.Read(segment, inc, sendingTime))
{
return SegmentTableReader<ServerNetSegment>.BreakSegmentReading.Yes;
}
@@ -109,16 +109,15 @@ namespace Barotrauma.Networking
private UInt16? firstNewID;
private readonly List<IServerSerializable> tempEntityList = new List<IServerSerializable>();
/// <summary>
/// Read the events from the message, ignoring ones we've already received. Returns false if reading the events fails.
/// </summary>
public bool Read(ServerNetSegment type, IReadMessage msg, float sendingTime, List<IServerSerializable> entities)
public bool Read(ServerNetSegment type, IReadMessage msg, float sendingTime)
{
UInt16 unreceivedEntityEventCount = 0;
if (type == ServerNetSegment.EntityEventInitial)
{
unreceivedEntityEventCount = msg.ReadUInt16();
UInt16 unreceivedEntityEventCount = msg.ReadUInt16();
firstNewID = msg.ReadUInt16();
if (GameSettings.CurrentConfig.VerboseLogging)
@@ -143,7 +142,7 @@ namespace Barotrauma.Networking
}
}
entities.Clear();
tempEntityList.Clear();
msg.ReadPadBits();
UInt16 firstEventID = msg.ReadUInt16();
@@ -156,9 +155,9 @@ namespace Barotrauma.Networking
{
string errorMsg = $"Error while reading a message from the server. Entity event data exceeds the size of the buffer (current position: {msg.BitPosition}, length: {msg.LengthBits}).";
errorMsg += "\nPrevious entities:";
for (int j = entities.Count - 1; j >= 0; j--)
for (int j = tempEntityList.Count - 1; j >= 0; j--)
{
errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
errorMsg += "\n" + (tempEntityList[j] == null ? "NULL" : tempEntityList[j].ToString());
}
DebugConsole.ThrowError(errorMsg);
return false;
@@ -174,7 +173,7 @@ namespace Barotrauma.Networking
DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
Microsoft.Xna.Framework.Color.Orange);
}
entities.Add(null);
tempEntityList.Add(null);
if (thisEventID == (UInt16)(lastReceivedID + 1)) { lastReceivedID++; }
continue;
}
@@ -182,7 +181,7 @@ namespace Barotrauma.Networking
int msgLength = (int)msg.ReadVariableUInt32();
IServerSerializable entity = Entity.FindEntityByID(entityID) as IServerSerializable;
entities.Add(entity);
tempEntityList.Add(entity);
//skip the event if we've already received it or if the entity isn't found
if (thisEventID != (UInt16)(lastReceivedID + 1) || entity == null)
@@ -223,7 +222,7 @@ namespace Barotrauma.Networking
if (msg.BitPosition != msgPosition + msgLength * 8)
{
var prevEntity = entities.Count >= 2 ? entities[entities.Count - 2] : null;
var prevEntity = tempEntityList.Count >= 2 ? tempEntityList[tempEntityList.Count - 2] : null;
ushort prevId = prevEntity is Entity p ? p.ID : (ushort)0;
string errorMsg = $"Message byte position incorrect after reading an event for the entity \"{entity}\" (ID {(entity is Entity e ? e.ID : 0)}). "
+$"The previous entity was \"{prevEntity}\" (ID {prevId}) "
@@ -30,6 +30,8 @@ namespace Barotrauma.Networking
protected readonly bool isOwner;
protected readonly Option<int> ownerKey;
public bool IsActive => isActive;
protected bool isActive;
public ClientPeer(Endpoint serverEndpoint, Callbacks callbacks, Option<int> ownerKey)
@@ -303,7 +303,7 @@ namespace Barotrauma
bool ChangeValue(GUIButton btn, object userData)
{
if (!(userData is int change)) { return false; }
if (userData is not int change) { return false; }
int hiddenOptions = 0;
@@ -365,7 +365,7 @@ namespace Barotrauma
private void CreateCustomizeWindow(CampaignSettings prevSettings, Action<CampaignSettings> onClosed = null)
{
CampaignCustomizeSettings = new GUIMessageBox("", "", new[] { TextManager.Get("OK") }, new Vector2(0.25f, 0.3f), minSize: new Point(450, 350));
CampaignCustomizeSettings = new GUIMessageBox("", "", new[] { TextManager.Get("OK") }, new Vector2(0.25f, 0.5f), minSize: new Point(450, 350));
GUILayoutGroup campaignSettingContent = new GUILayoutGroup(new RectTransform(new Vector2(0.95f, 0.8f), CampaignCustomizeSettings.Content.RectTransform, Anchor.TopCenter));
@@ -474,7 +474,7 @@ namespace Barotrauma
};
missionRewardTexts.Add(rewardText);
LocalizedString reputationText = mission.GetReputationRewardText(mission.Locations[0]);
LocalizedString reputationText = mission.GetReputationRewardText();
if (!reputationText.IsNullOrEmpty())
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(reputationText), wrap: true);
@@ -125,7 +125,7 @@ namespace Barotrauma.CharacterEditor
{
ResetVariables();
var subInfo = new SubmarineInfo("Content/AnimEditor.sub");
Submarine.MainSub = new Submarine(subInfo);
Submarine.MainSub = new Submarine(subInfo, showErrorMessages: false);
if (Submarine.MainSub.PhysicsBody != null)
{
Submarine.MainSub.PhysicsBody.Enabled = false;
@@ -162,11 +162,6 @@ namespace Barotrauma.CharacterEditor
OpenDoors();
GameMain.Instance.ResolutionChanged += OnResolutionChanged;
Instance = this;
if (!GameSettings.CurrentConfig.EditorDisclaimerShown)
{
GameMain.Instance.ShowEditorDisclaimer();
}
}
private void ResetVariables()
@@ -2688,10 +2683,6 @@ namespace Barotrauma.CharacterEditor
// Character selection
var characterLabel = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), content.RectTransform), GetCharacterEditorTranslation("CharacterPanel"), font: GUIStyle.LargeFont);
var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(0.2f, 0.7f), characterLabel.RectTransform, Anchor.CenterRight), style: "GUINotificationButton")
{
OnClicked = (btn, userdata) => { GameMain.Instance.ShowEditorDisclaimer(); return true; }
};
var characterDropDown = new GUIDropDown(new RectTransform(new Vector2(1, 0.2f), content.RectTransform)
{
@@ -820,6 +820,15 @@ namespace Barotrauma
};
valueInput.Text = newValue?.ToString() ?? "<type here>";
}
else if (type == typeof(Identifier))
{
GUITextBox valueInput = new GUITextBox(new RectTransform(Vector2.One, layout.RectTransform), newValue?.ToString() ?? string.Empty);
valueInput.OnTextChanged += (component, o) =>
{
newValue = new Identifier(o);
return true;
};
}
else if (type == typeof(float) || type == typeof(int))
{
GUINumberInput valueInput = new GUINumberInput(new RectTransform(Vector2.One, layout.RectTransform), NumberType.Float) { FloatValue = (float) (newValue ?? 0.0f) };
@@ -51,9 +51,8 @@ namespace Barotrauma
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 (DateTime WhenToRefresh, int Count) modUpdateStatus = (DateTime.Now, 0);
private static readonly TimeSpan ModUpdateInterval = TimeSpan.FromSeconds(60.0f);
private readonly GameMain game;
@@ -736,8 +735,7 @@ namespace Barotrauma
public void ResetModUpdateButton()
{
modUpdateTask = null;
modUpdateTimer = 0;
modUpdateStatus = (DateTime.Now, 0);
modUpdatesButton.Visible = false;
}
@@ -875,7 +873,25 @@ namespace Barotrauma
GameMain.ResetNetLobbyScreen();
try
{
string exeName = serverExecutableDropdown.SelectedComponent?.UserData is ServerExecutableFile f ? f.Path.Value : "DedicatedServer";
string fileName;
if (serverExecutableDropdown.SelectedComponent?.UserData is ServerExecutableFile f &&
f.ContentPackage != GameMain.VanillaContent)
{
fileName = Path.Combine(
Path.GetDirectoryName(f.Path.Value),
Path.GetFileNameWithoutExtension(f.Path.Value));
#if WINDOWS
fileName += ".exe";
#endif
}
else
{
#if WINDOWS
fileName = "DedicatedServer.exe";
#else
fileName = "./DedicatedServer";
#endif
}
string arguments = "-name \"" + ToolBox.EscapeCharacters(name) + "\"" +
" -public " + isPublicBox.Selected.ToString() +
@@ -899,19 +915,10 @@ namespace Barotrauma
}
int ownerKey = Math.Max(CryptoRandom.Instance.Next(), 1);
arguments += " -ownerkey " + ownerKey;
string filename = Path.Combine(
Path.GetDirectoryName(exeName),
Path.GetFileNameWithoutExtension(exeName));
#if WINDOWS
filename += ".exe";
#else
filename = "./" + exeName;
#endif
var processInfo = new ProcessStartInfo
{
FileName = filename,
FileName = fileName,
Arguments = arguments,
WorkingDirectory = Directory.GetCurrentDirectory(),
#if !DEBUG
@@ -958,15 +965,42 @@ namespace Barotrauma
}
}
private void UpdateOutOfDateWorkshopItemCount()
{
if (DateTime.Now < modUpdateStatus.WhenToRefresh) { return; }
if (!SteamManager.IsInitialized) { return; }
var installedPackages = ContentPackageManager.WorkshopPackages;
var ids = SteamManager.Workshop.GetSubscribedItemIds()
.Select(id => id.Value)
.Union(installedPackages
.Select(pkg => pkg.UgcId)
.NotNone()
.OfType<SteamWorkshopId>()
.Select(id => id.Value));
var count = ids
// Deliberately construct Steamworks.Ugc.Item directly
// to not immediately generate a Workshop data request
.Select(id => new Steamworks.Ugc.Item(id))
.Count(item =>
installedPackages.FirstOrDefault(p
=> p.UgcId.TryUnwrap(out SteamWorkshopId id) && id.Value == item.Id)
is { } pkg
// Checking that this item is downloading, waiting to be downloaded
// or is newer than the currently installed copy should be good enough,
// and should still not make a Workshop data request
&& (item.IsDownloading
|| item.IsDownloadPending
|| (item.InstallTime.TryGetValue(out var workshopInstallTime)
&& pkg.InstallTime.TryUnwrap(out var localInstallTime)
&& localInstallTime < workshopInstallTime)));
modUpdateStatus = (DateTime.Now + ModUpdateInterval, count);
}
public override void Update(double deltaTime)
{
modUpdateTimer -= (float)deltaTime;
if (modUpdateTimer <= 0.0f && modUpdateTask is not { IsCompleted: false })
{
modUpdateTask = BulkDownloader.GetItemsThatNeedUpdating();
modUpdateTimer = ModUpdateInterval;
}
#if DEBUG
hostServerButton.Enabled = true;
#else
@@ -976,10 +1010,8 @@ namespace Barotrauma
}
#endif
if (modUpdateTask is { IsCompletedSuccessfully: true })
{
modUpdatesButton.Visible = modUpdateTask.Result.Count > 0;
}
UpdateOutOfDateWorkshopItemCount();
modUpdatesButton.Visible = modUpdateStatus.Count > 0;
if (modUpdatesButton.Visible)
{
@@ -106,7 +106,7 @@ namespace Barotrauma
{
var slide = slideshowPrefab.Slides[Math.Min(state, slideshowPrefab.Slides.Length - 1)];
currentText = slide.Text
.Replace("[submarine]", Submarine.MainSub?.Info.Name ?? "Unknown")
.Replace("[submarine]", Submarine.MainSub?.Info.Name ?? GameMain.GameSession?.SubmarineInfo?.Name ?? "Unknown")
.Replace("[location]", Level.Loaded?.StartOutpost?.Info.Name ?? "Unknown");
}
@@ -543,13 +543,6 @@ namespace Barotrauma
}
};
var disclaimerBtn = new GUIButton(new RectTransform(new Vector2(0.1f, 1.0f), paddedTopPanel.RectTransform, Anchor.CenterRight), style: "GUINotificationButton")
{
IgnoreLayoutGroups = true,
OnClicked = (btn, userdata) => { GameMain.Instance.ShowEditorDisclaimer(); return true; }
};
disclaimerBtn.RectTransform.MaxSize = new Point(disclaimerBtn.Rect.Height);
TopPanel.RectTransform.MinSize = new Point(0, (int)(paddedTopPanel.RectTransform.Children.Max(c => c.MinSize.Y) / paddedTopPanel.RectTransform.RelativeSize.Y));
paddedTopPanel.Recalculate();
@@ -1425,7 +1418,7 @@ namespace Barotrauma
else if (MainSub == null)
{
var subInfo = new SubmarineInfo();
MainSub = new Submarine(subInfo);
MainSub = new Submarine(subInfo, showErrorMessages: false);
}
MainSub.UpdateTransform(interpolate: false);
@@ -1462,11 +1455,6 @@ namespace Barotrauma
ImageManager.OnEditorSelected();
ReconstructLayers();
if (!GameSettings.CurrentConfig.EditorDisclaimerShown)
{
GameMain.Instance.ShowEditorDisclaimer();
}
}
public override void OnFileDropped(string filePath, string extension)
@@ -5646,8 +5634,7 @@ namespace Barotrauma
MouseDragStart = Vector2.Zero;
}
if (!saveAssemblyFrame.Rect.Contains(PlayerInput.MousePosition)
&& !snapToGridFrame.Rect.Contains(PlayerInput.MousePosition)
if ((GUI.MouseOn == null || !GUI.MouseOn.IsChildOf(TopPanel))
&& dummyCharacter?.SelectedItem == null && !WiringMode
&& (GUI.MouseOn == null || MapEntity.SelectedAny || MapEntity.SelectionPos != Vector2.Zero))
{
@@ -779,6 +779,8 @@ namespace Barotrauma
workshopMenu = Screen.Selected is MainMenuScreen
? (WorkshopMenu)new MutableWorkshopMenu(content)
: (WorkshopMenu)new ImmutableWorkshopMenu(content);
GameMain.MainMenuScreen.ResetModUpdateButton();
}
private void CreateBottomButtons()
@@ -904,17 +904,18 @@ namespace Barotrauma
public static void PlayDamageSound(string damageType, float damage, Vector2 position, float range = 2000.0f, IEnumerable<Identifier> tags = null)
{
var suitableSounds = damageSounds.Where(s =>
s.DamageType == damageType &&
(s.RequiredTag.IsEmpty || (tags == null ? s.RequiredTag.IsEmpty : tags.Contains(s.RequiredTag))));
//if the damage is too low for any sound, don't play anything
if (damageSounds.All(d => damage < d.DamageRange.X)) { return; }
if (suitableSounds.All(d => damage < d.DamageRange.X)) { return; }
//allow the damage to differ by 10 from the configured damage range,
//so the same amount of damage doesn't always play the same sound
float randomizedDamage = MathHelper.Clamp(damage + Rand.Range(-10.0f, 10.0f), 0.0f, 100.0f);
var suitableSounds = damageSounds.Where(s =>
s.DamageType == damageType &&
(s.DamageRange == Vector2.Zero || (randomizedDamage >= s.DamageRange.X && randomizedDamage <= s.DamageRange.Y)) &&
(s.RequiredTag.IsEmpty || (tags == null ? s.RequiredTag.IsEmpty : tags.Contains(s.RequiredTag))));
suitableSounds = suitableSounds.Where(s =>
s.DamageRange == Vector2.Zero || (randomizedDamage >= s.DamageRange.X && randomizedDamage <= s.DamageRange.Y));
var damageSound = suitableSounds.GetRandomUnsynced();
damageSound?.Sound?.Play(1.0f, range, position, muffle: !damageSound.IgnoreMuffling && ShouldMuffleSound(Character.Controlled, position, range, null));
@@ -45,18 +45,26 @@ namespace Barotrauma.Steam
protected static readonly Regex bbTagRegex = new Regex(@"\[(.+?)\]",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
protected static GUICustomComponent CreateBBCodeElement(string bbCode, GUIListBox container)
protected static void CreateBBCodeElement(Steamworks.Ugc.Item workshopItem, GUIListBox container)
{
Point cachedContainerSize = Point.Zero;
List<BBWord> bbWords = new List<BBWord>();
Stack<BBWord.TagType> tagStack = new Stack<BBWord.TagType>();
void recalculate()
string bbCode = "";
void forceReset()
{
if (cachedContainerSize == container.Content.RectTransform.NonScaledSize) { return; }
bbWords.Clear();
cachedContainerSize = Point.Zero;
}
void recalculate(GUICustomComponent component)
{
if (cachedContainerSize == component.RectTransform.NonScaledSize) { return; }
bbWords.Clear();
cachedContainerSize = container.Content.RectTransform.NonScaledSize;
cachedContainerSize = component.RectTransform.NonScaledSize;
var matches = new Stack<Match>(bbTagRegex.Matches(bbCode).Reverse());
Match? nextTag = null;
@@ -133,11 +141,14 @@ namespace Barotrauma.Steam
{
bbWords.Add(new BBWord(finalWord, currTagType));
}
container.RecalculateChildren();
container.UpdateScrollBarSize();
}
void draw(SpriteBatch spriteBatch, GUICustomComponent component)
{
recalculate();
recalculate(component);
Vector2 currPos = Vector2.Zero;
Vector2 rectPos = component.Rect.Location.ToVector2();
for (int i = 0; i < bbWords.Count; i++)
@@ -180,7 +191,19 @@ namespace Barotrauma.Steam
= component.RectTransform.NonScaledSize.ToVector2() / component.Parent.Rect.Size.ToVector2();
}
return new GUICustomComponent(new RectTransform(Vector2.One, container.Content.RectTransform),
TaskPool.Add(
$"GetWorkshopItemLongDescriptionFor{workshopItem.Id.Value}",
SteamManager.Workshop.GetItemAsap(workshopItem.Id.Value, withLongDescription: true),
t =>
{
if (!t.TryGetResult(out Steamworks.Ugc.Item? workshopItemWithDescription)) { return; }
bbCode = workshopItemWithDescription?.Description ?? "";
forceReset();
});
new GUICustomComponent(
new RectTransform(Vector2.One, container.Content.RectTransform),
onDraw: draw);
}
}
@@ -34,7 +34,7 @@ namespace Barotrauma.Steam
if (numSubscribedMods == memSubscribedModCount) { return; }
memSubscribedModCount = numSubscribedMods;
var subscribedIds = SteamManager.GetSubscribedItems().ToHashSet();
var subscribedIds = SteamManager.Workshop.GetSubscribedItemIds();
var installedIds = ContentPackageManager.WorkshopPackages
.Select(p => p.UgcId)
.NotNone()
@@ -773,7 +773,7 @@ namespace Barotrauma.Steam
#endregion
var descriptionListBox = new GUIListBox(new RectTransform((1.0f, 0.38f), verticalLayout.RectTransform));
CreateBBCodeElement(workshopItem.Description, descriptionListBox);
CreateBBCodeElement(workshopItem, descriptionListBox);
var showInSteamContainer
= new GUIFrame(new RectTransform((1.0f, 0.05f), verticalLayout.RectTransform), style: null);
@@ -218,6 +218,20 @@ namespace Barotrauma.Steam
var descriptionTextBox
= ScrollableTextBox(rightTop, 6.0f, workshopItem.Description ?? string.Empty);
if (workshopItem.Id != 0)
{
TaskPool.Add(
$"GetFullDescription{workshopItem.Id}",
SteamManager.Workshop.GetItemAsap(workshopItem.Id.Value, withLongDescription: true),
t =>
{
if (!t.TryGetResult(out Steamworks.Ugc.Item? itemWithDescription)) { return; }
descriptionTextBox.Text = itemWithDescription?.Description ?? descriptionTextBox.Text;
descriptionTextBox.Deselect();
});
}
var (leftBottom, _, rightBottom)
= CreateSidebars(mainLayout, leftWidth: 0.49f, centerWidth: 0.01f, rightWidth: 0.5f, height: 0.5f);
leftBottom.Stretch = true;
@@ -2,7 +2,7 @@
namespace Barotrauma
{
partial class UpgradePrefab
sealed partial class UpgradePrefab
{
public readonly ImmutableArray<DecorativeSprite> DecorativeSprites = new ImmutableArray<DecorativeSprite>();
public Sprite Sprite { get; private set; }
@@ -23,36 +23,55 @@ namespace Barotrauma
public static void ConvertMasterLocalizationKit(string outputTextsDirectory, string outputConversationsDirectory, bool convertConversations)
{
string textFilePath = Path.Combine(infoTextPath, "Texts.csv");
string conversationFilePath = Path.Combine(infoTextPath, "NPCConversations.csv");
List<string> languages = new List<string>();
for (int i = 0; i < 2; i++)
{
string textFilePath;
string outputFileName;
switch (i)
{
case 0:
textFilePath = Path.Combine(infoTextPath, "Texts.csv");
outputFileName = "Vanilla.xml";
break;
case 1:
textFilePath = Path.Combine(infoTextPath, "EditorTexts.csv");
outputFileName = "VanillaEditorTexts.xml";
break;
default:
throw new NotImplementedException();
}
Dictionary<string, List<string>> xmlContent;
try
{
xmlContent = ConvertInfoTextToXML(File.ReadAllLines(textFilePath, Encoding.UTF8));
}
catch (Exception e)
{
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath, e);
return;
}
if (xmlContent == null)
{
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath);
return;
}
foreach (string language in xmlContent.Keys)
{
string languageNoWhitespace = language.Replace(" ", "");
string xmlFileFullPath = Path.Combine(outputTextsDirectory, $"{languageNoWhitespace}/{languageNoWhitespace}Vanilla.xml");
File.WriteAllLines(xmlFileFullPath, xmlContent[language], Encoding.UTF8);
DebugConsole.NewMessage("InfoText localization .xml file successfully created at: " + xmlFileFullPath);
Dictionary<string, List<string>> xmlContent;
try
{
xmlContent = ConvertInfoTextToXML(File.ReadAllLines(textFilePath, Encoding.UTF8));
}
catch (Exception e)
{
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath, e);
return;
}
if (xmlContent == null)
{
DebugConsole.ThrowError("InfoText Localization .csv to .xml conversion failed for: " + textFilePath);
return;
}
foreach (string language in xmlContent.Keys)
{
languages.Add(language);
string languageNoWhitespace = language.Replace(" ", "");
string xmlFileFullPath = Path.Combine(outputTextsDirectory, $"{languageNoWhitespace}/{languageNoWhitespace}{outputFileName}");
File.WriteAllLines(xmlFileFullPath, xmlContent[language], Encoding.UTF8);
DebugConsole.NewMessage("InfoText localization .xml file successfully created at: " + xmlFileFullPath);
}
}
if (convertConversations)
{
string conversationFilePath = Path.Combine(infoTextPath, "NPCConversations.csv");
var conversationLinesAll = File.ReadAllLines(conversationFilePath, Encoding.UTF8);
foreach (string language in xmlContent.Keys)
foreach (string language in languages)
{
List<string> convXmlContent = ConvertConversationsToXML(conversationLinesAll, language);
if (convXmlContent == null)
@@ -61,7 +80,7 @@ namespace Barotrauma
continue;
}
string languageNoWhitespace = language.Replace(" ", "");
string xmlFileFullPath = Path.Combine(outputTextsDirectory, $"NpcConversations_{languageNoWhitespace}.xml");
string xmlFileFullPath = Path.Combine(outputConversationsDirectory, languageNoWhitespace, $"NpcConversations_{languageNoWhitespace}.xml");
File.WriteAllLines(xmlFileFullPath, convXmlContent, Encoding.UTF8);
DebugConsole.NewMessage("Conversation localization .xml file successfully created at: " + xmlFileFullPath);
}
@@ -339,7 +358,8 @@ namespace Barotrauma
string[] headerSplit = csvContent[0].Split(separator);
for (int i = 0; i < headerSplit.Length; i++)
{
if (headerSplit[i] == language)
if (headerSplit[i] == language ||
(language == "English" && headerSplit[i]== "Line (Original)"))
{
languageColumn = i;
break;