Build 0.17.13.0

This commit is contained in:
Markus Isberg
2022-04-22 21:48:04 +09:00
parent 6cc100d98c
commit 7a09cf3260
58 changed files with 506 additions and 184 deletions
@@ -116,7 +116,7 @@ namespace Barotrauma
Stretch = true Stretch = true
}; };
var skills = Job.Skills; var skills = Job.GetSkills().ToList();
skills.Sort((s1, s2) => -s1.Level.CompareTo(s2.Level)); skills.Sort((s1, s2) => -s1.Level.CompareTo(s2.Level));
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillsArea.RectTransform), TextManager.AddPunctuation(':', TextManager.Get("skills"), string.Empty), font: font) { Padding = Vector4.Zero }; new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), skillsArea.RectTransform), TextManager.AddPunctuation(':', TextManager.Get("skills"), string.Empty), font: font) { Padding = Vector4.Zero };
@@ -560,17 +560,7 @@ namespace Barotrauma
ch.SetPersonalityTrait(); ch.SetPersonalityTrait();
if (ch.Job != null) if (ch.Job != null)
{ {
foreach (KeyValuePair<Identifier, float> skill in skillLevels) ch.Job.OverrideSkills(skillLevels);
{
Skill matchingSkill = ch.Job.Skills.Find(s => s.Identifier == skill.Key);
if (matchingSkill == null)
{
ch.Job.Skills.Add(new Skill(skill.Key, skill.Value));
continue;
}
matchingSkill.Level = skill.Value;
}
ch.Job.Skills.RemoveAll(s => !skillLevels.ContainsKey(s.Identifier));
} }
ch.ExperiencePoints = inc.ReadUInt16(); ch.ExperiencePoints = inc.ReadUInt16();
@@ -172,7 +172,7 @@ namespace Barotrauma
{ {
AutoScaleVertical = true, AutoScaleVertical = true,
TextScale = 1.1f, TextScale = 1.1f,
TextGetter = () => TextManager.FormatCurrency(campaign.Wallet.Balance) TextGetter = () => TextManager.FormatCurrency(campaign.GetBalance())
}; };
var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center, var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
@@ -344,7 +344,7 @@ namespace Barotrauma
Color? jobColor = null; Color? jobColor = null;
if (characterInfo.Job != null) if (characterInfo.Job != null)
{ {
skill = characterInfo.Job?.PrimarySkill ?? characterInfo.Job.Skills.OrderByDescending(s => s.Level).FirstOrDefault(); skill = characterInfo.Job?.PrimarySkill ?? characterInfo.Job.GetSkills().OrderByDescending(s => s.Level).FirstOrDefault();
jobColor = characterInfo.Job.Prefab.UIColor; jobColor = characterInfo.Job.Prefab.UIColor;
} }
@@ -547,8 +547,8 @@ namespace Barotrauma
GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.475f), mainGroup.RectTransform), isHorizontal: true); GUILayoutGroup skillGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.475f), mainGroup.RectTransform), isHorizontal: true);
GUILayoutGroup skillNameGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), skillGroup.RectTransform)); GUILayoutGroup skillNameGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.8f, 1.0f), skillGroup.RectTransform));
GUILayoutGroup skillLevelGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 1.0f), skillGroup.RectTransform)); GUILayoutGroup skillLevelGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.2f, 1.0f), skillGroup.RectTransform));
List<Skill> characterSkills = characterInfo.Job.Skills; var characterSkills = characterInfo.Job.GetSkills();
blockHeight = 1.0f / characterSkills.Count; blockHeight = 1.0f / characterSkills.Count();
foreach (Skill skill in characterSkills) foreach (Skill skill in characterSkills)
{ {
new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), skillNameGroup.RectTransform), TextManager.Get("SkillName." + skill.Identifier)); new GUITextBlock(new RectTransform(new Vector2(1.0f, blockHeight), skillNameGroup.RectTransform), TextManager.Get("SkillName." + skill.Identifier));
@@ -630,7 +630,7 @@ namespace Barotrauma
total += ((InfoSkill)c.UserData).CharacterInfo.Salary; total += ((InfoSkill)c.UserData).CharacterInfo.Salary;
}); });
totalBlock.Text = TextManager.FormatCurrency(total); totalBlock.Text = TextManager.FormatCurrency(total);
bool enoughMoney = campaign == null || campaign.Wallet.CanAfford(total); bool enoughMoney = campaign == null || campaign.CanAfford(total);
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red; totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
validateHiresButton.Enabled = enoughMoney && HasPermission && pendingList.Content.RectTransform.Children.Any(); validateHiresButton.Enabled = enoughMoney && HasPermission && pendingList.Content.RectTransform.Children.Any();
} }
@@ -652,7 +652,7 @@ namespace Barotrauma
int total = nonDuplicateHires.Aggregate(0, (total, info) => total + info.Salary); int total = nonDuplicateHires.Aggregate(0, (total, info) => total + info.Salary);
if (!campaign.Wallet.CanAfford(total)) { return false; } if (!campaign.CanAfford(total)) { return false; }
bool atLeastOneHired = false; bool atLeastOneHired = false;
foreach (CharacterInfo ci in nonDuplicateHires) foreach (CharacterInfo ci in nonDuplicateHires)
@@ -395,14 +395,13 @@ namespace Barotrauma
origin = TextSize * 0.5f; origin = TextSize * 0.5f;
origin.X = 0; origin.X = 0;
if (textAlignment.HasFlag(Alignment.Left) && !overflowClipActive) if (textAlignment.HasFlag(Alignment.Left))
{ {
textPos.X = padding.X; textPos.X = padding.X;
} }
if (textAlignment.HasFlag(Alignment.Right) || overflowClipActive) if (textAlignment.HasFlag(Alignment.Right))
{ {
textPos.X = rect.Width - padding.Z; textPos.X = rect.Width - padding.Z;
//origin.X = TextSize.X;
} }
if (textAlignment.HasFlag(Alignment.Top)) if (textAlignment.HasFlag(Alignment.Top))
{ {
@@ -443,24 +443,7 @@ namespace Barotrauma
if (CaretEnabled) if (CaretEnabled)
{ {
if (textBlock.OverflowClipActive) HandleCaretBoundsOverflow();
{
float left = textBlock.Rect.X + textBlock.Padding.X;
if (CaretScreenPos.X < left)
{
float diff = left - CaretScreenPos.X;
textBlock.TextPos = new Vector2(textBlock.TextPos.X + diff, textBlock.TextPos.Y);
CalculateCaretPos();
}
float right = textBlock.Rect.Right - textBlock.Padding.Z;
if (CaretScreenPos.X > right)
{
float diff = CaretScreenPos.X - right;
textBlock.TextPos = new Vector2(textBlock.TextPos.X - diff, textBlock.TextPos.Y);
CalculateCaretPos();
}
}
caretTimer += deltaTime; caretTimer += deltaTime;
caretVisible = ((caretTimer * 1000.0f) % 1000) < 500; caretVisible = ((caretTimer * 1000.0f) % 1000) < 500;
if (caretVisible && caretPosDirty) if (caretVisible && caretPosDirty)
@@ -486,6 +469,29 @@ namespace Barotrauma
textBlock.State = State; textBlock.State = State;
} }
private void HandleCaretBoundsOverflow()
{
if (textBlock.OverflowClipActive)
{
CalculateCaretPos();
float left = textBlock.Rect.X + textBlock.Padding.X;
if (CaretScreenPos.X < left)
{
float diff = left - CaretScreenPos.X;
textBlock.TextPos = new Vector2(textBlock.TextPos.X + diff, textBlock.TextPos.Y);
CalculateCaretPos();
}
float right = textBlock.Rect.Right - textBlock.Padding.Z;
if (CaretScreenPos.X > right)
{
float diff = CaretScreenPos.X - right;
textBlock.TextPos = new Vector2(textBlock.TextPos.X - diff, textBlock.TextPos.Y);
CalculateCaretPos();
}
}
}
private void DrawCaretAndSelection(SpriteBatch spriteBatch, GUICustomComponent customComponent) private void DrawCaretAndSelection(SpriteBatch spriteBatch, GUICustomComponent customComponent)
{ {
if (!Visible) { return; } if (!Visible) { return; }
@@ -554,15 +560,33 @@ namespace Barotrauma
{ {
RemoveSelectedText(); RemoveSelectedText();
} }
Vector2 textPos = textBlock.TextPos; using var _ = new TextPosPreservation(this);
bool wasOverflowClipActive = textBlock.OverflowClipActive;
if (SetText(Text.Insert(CaretIndex, input))) if (SetText(Text.Insert(CaretIndex, input)))
{ {
CaretIndex = Math.Min(Text.Length, CaretIndex + input.Length); CaretIndex = Math.Min(Text.Length, CaretIndex + input.Length);
OnTextChanged?.Invoke(this, Text); OnTextChanged?.Invoke(this, Text);
}
}
private readonly ref struct TextPosPreservation
{
private readonly GUITextBox textBox;
private GUITextBlock textBlock => textBox.TextBlock;
private readonly bool wasOverflowClipActive;
private readonly Vector2 textPos;
public TextPosPreservation(GUITextBox tb)
{
textBox = tb;
wasOverflowClipActive = tb.TextBlock.OverflowClipActive;
textPos = tb.TextBlock.TextPos;
}
public void Dispose()
{
if (textBlock.OverflowClipActive && wasOverflowClipActive && !MathUtils.NearlyEqual(textBlock.TextPos, textPos)) if (textBlock.OverflowClipActive && wasOverflowClipActive && !MathUtils.NearlyEqual(textBlock.TextPos, textPos))
{ {
textBlock.TextPos = textPos + Vector2.UnitX * Font.MeasureString(input).X * TextBlock.TextScale; textBlock.TextPos = textPos;
} }
} }
} }
@@ -577,6 +601,8 @@ namespace Barotrauma
switch (command) switch (command)
{ {
case '\b' when !Readonly: //backspace case '\b' when !Readonly: //backspace
{
using var _ = new TextPosPreservation(this);
if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl)) if (PlayerInput.KeyDown(Keys.LeftControl) || PlayerInput.KeyDown(Keys.RightControl))
{ {
SetText(string.Empty, false); SetText(string.Empty, false);
@@ -595,6 +621,7 @@ namespace Barotrauma
} }
OnTextChanged?.Invoke(this, Text); OnTextChanged?.Invoke(this, Text);
break; break;
}
case (char)0x3: // ctrl-c case (char)0x3: // ctrl-c
CopySelectedText(); CopySelectedText();
break; break;
@@ -271,7 +271,7 @@ namespace Barotrauma
healList.PriceBlock.Text = TextManager.FormatCurrency(totalCost); healList.PriceBlock.Text = TextManager.FormatCurrency(totalCost);
healList.PriceBlock.TextColor = GUIStyle.Red; healList.PriceBlock.TextColor = GUIStyle.Red;
healList.HealButton.Enabled = false; healList.HealButton.Enabled = false;
if (medicalClinic.GetWallet().CanAfford(totalCost)) if (medicalClinic.GetBalance() >= totalCost)
{ {
healList.PriceBlock.TextColor = GUIStyle.TextColorNormal; healList.PriceBlock.TextColor = GUIStyle.TextColorNormal;
if (medicalClinic.PendingHeals.Any()) if (medicalClinic.PendingHeals.Any())
@@ -467,7 +467,7 @@ namespace Barotrauma
GUITextBlock moneyLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), balanceLayout.RectTransform), string.Empty, textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont) GUITextBlock moneyLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), balanceLayout.RectTransform), string.Empty, textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont)
{ {
TextGetter = () => TextManager.FormatCurrency(medicalClinic.GetWallet().Balance), TextGetter = () => TextManager.FormatCurrency(medicalClinic.GetBalance()),
AutoScaleVertical = true, AutoScaleVertical = true,
TextScale = 1.1f TextScale = 1.1f
}; };
@@ -577,7 +577,7 @@ namespace Barotrauma
GUILayoutGroup buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), footerLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterRight); GUILayoutGroup buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), footerLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterRight);
GUIButton healButton = new GUIButton(new RectTransform(new Vector2(0.33f, 1f), buttonLayout.RectTransform), TextManager.Get("medicalclinic.heal")) GUIButton healButton = new GUIButton(new RectTransform(new Vector2(0.33f, 1f), buttonLayout.RectTransform), TextManager.Get("medicalclinic.heal"))
{ {
Enabled = medicalClinic.PendingHeals.Any() && medicalClinic.GetWallet().CanAfford(medicalClinic.GetTotalCost()), Enabled = medicalClinic.PendingHeals.Any() && medicalClinic.GetBalance() >= medicalClinic.GetTotalCost(),
OnClicked = (button, _) => OnClicked = (button, _) =>
{ {
button.Enabled = false; button.Enabled = false;
@@ -71,7 +71,8 @@ namespace Barotrauma
private CargoManager CargoManager => campaignUI.Campaign.CargoManager; private CargoManager CargoManager => campaignUI.Campaign.CargoManager;
private Location CurrentLocation => campaignUI.Campaign.Map?.CurrentLocation; private Location CurrentLocation => campaignUI.Campaign.Map?.CurrentLocation;
private Wallet PlayerWallet => campaignUI.Campaign.Wallet; private int Balance => campaignUI.Campaign.GetBalance();
private bool IsBuying => activeTab switch private bool IsBuying => activeTab switch
{ {
StoreTab.Buy => true, StoreTab.Buy => true,
@@ -207,7 +208,7 @@ namespace Barotrauma
{ {
if (CurrentLocation?.Stores != null) if (CurrentLocation?.Stores != null)
{ {
if (CurrentLocation.GetStore(identifier) is { } store) if (!identifier.IsEmpty && CurrentLocation.GetStore(identifier) is { } store)
{ {
ActiveStore = store; ActiveStore = store;
if (storeNameBlock != null) if (storeNameBlock != null)
@@ -223,14 +224,45 @@ namespace Barotrauma
else else
{ {
ActiveStore = null; ActiveStore = null;
string msg = $"Error selecting store with identifier \"{identifier}\" at {CurrentLocation}: store with the identifier doesn't exist at the location."; string errorId, msg;
if (identifier.IsEmpty)
{
errorId = "Store.SelectStore:IdentifierEmpty";
msg = $"Error selecting store at {CurrentLocation}: identifier is empty.";
}
else
{
errorId = "Store.SelectStore:StoreDoesntExist";
msg = $"Error selecting store with identifier \"{identifier}\" at {CurrentLocation}: store with the identifier doesn't exist at the location.";
}
DebugConsole.ShowError(msg); DebugConsole.ShowError(msg);
GameAnalyticsManager.AddErrorEventOnce("Store.SelectStore:StoreDoesntExist", GameAnalyticsManager.ErrorSeverity.Error, msg); GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg);
} }
} }
else else
{ {
ActiveStore = null; ActiveStore = null;
string errorId = "", msg = "";
if (campaignUI.Campaign.Map == null)
{
errorId = "Store.SelectStore:MapNull";
msg = $"Error selecting store with identifier \"{identifier}\": Map is null.";
}
else if (CurrentLocation == null)
{
errorId = "Store.SelectStore:CurrentLocationNull";
msg = $"Error selecting store with identifier \"{identifier}\": CurrentLocation is null.";
}
else if (CurrentLocation.Stores == null)
{
errorId = "Store.SelectStore:StoresNull";
msg = $"Error selecting store with identifier \"{identifier}\": CurrentLocation.Stores is null.";
}
if (!msg.IsNullOrEmpty())
{
DebugConsole.ShowError(msg);
GameAnalyticsManager.AddErrorEventOnce(errorId, GameAnalyticsManager.ErrorSeverity.Error, msg);
}
} }
RefreshItemsToSell(); RefreshItemsToSell();
Refresh(); Refresh();
@@ -712,7 +744,7 @@ namespace Barotrauma
private LocalizedString GetMerchantBalanceText() => TextManager.FormatCurrency(ActiveStore?.Balance ?? 0); private LocalizedString GetMerchantBalanceText() => TextManager.FormatCurrency(ActiveStore?.Balance ?? 0);
private LocalizedString GetPlayerBalanceText() => TextManager.FormatCurrency(PlayerWallet.Balance); private LocalizedString GetPlayerBalanceText() => TextManager.FormatCurrency(Balance);
private GUILayoutGroup CreateDealsGroup(GUIListBox parentList, int elementCount) private GUILayoutGroup CreateDealsGroup(GUIListBox parentList, int elementCount)
{ {
@@ -2037,7 +2069,7 @@ namespace Barotrauma
totalPrice += item.Quantity * ActiveStore.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo); totalPrice += item.Quantity * ActiveStore.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo);
} }
itemsToRemove.ForEach(i => itemsToPurchase.Remove(i)); itemsToRemove.ForEach(i => itemsToPurchase.Remove(i));
if (itemsToPurchase.None() || !PlayerWallet.CanAfford(totalPrice)) { return false; } if (itemsToPurchase.None() || Balance < totalPrice) { return false; }
CargoManager.PurchaseItems(ActiveStore.Identifier, itemsToPurchase, true); CargoManager.PurchaseItems(ActiveStore.Identifier, itemsToPurchase, true);
GameMain.Client?.SendCampaignState(); GameMain.Client?.SendCampaignState();
var dialog = new GUIMessageBox( var dialog = new GUIMessageBox(
@@ -2091,7 +2123,7 @@ namespace Barotrauma
if (IsBuying) if (IsBuying)
{ {
shoppingCrateTotal.Text = TextManager.FormatCurrency(buyTotal); shoppingCrateTotal.Text = TextManager.FormatCurrency(buyTotal);
shoppingCrateTotal.TextColor = !PlayerWallet.CanAfford(buyTotal) ? Color.Red : Color.White; shoppingCrateTotal.TextColor = Balance < buyTotal ? Color.Red : Color.White;
} }
else else
{ {
@@ -2137,7 +2169,7 @@ namespace Barotrauma
ActiveShoppingCrateList.Content.RectTransform.Children.Any() && ActiveShoppingCrateList.Content.RectTransform.Children.Any() &&
activeTab switch activeTab switch
{ {
StoreTab.Buy => PlayerWallet.CanAfford(buyTotal), StoreTab.Buy => Balance >= buyTotal,
StoreTab.Sell => CurrentLocation != null && sellTotal <= ActiveStore.Balance, StoreTab.Sell => CurrentLocation != null && sellTotal <= ActiveStore.Balance,
StoreTab.SellSub => CurrentLocation != null && sellFromSubTotal <= ActiveStore.Balance, StoreTab.SellSub => CurrentLocation != null && sellFromSubTotal <= ActiveStore.Balance,
_ => false _ => false
@@ -4,6 +4,7 @@ using Microsoft.Xna.Framework;
using System.Linq; using System.Linq;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using System.Globalization;
namespace Barotrauma namespace Barotrauma
{ {
@@ -31,7 +32,7 @@ namespace Barotrauma
private readonly List<SubmarineInfo> subsToShow; private readonly List<SubmarineInfo> subsToShow;
private readonly SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage]; private readonly SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage];
private SubmarineInfo selectedSubmarine = null; private SubmarineInfo selectedSubmarine = null;
private LocalizedString purchaseAndSwitchText, purchaseOnlyText, deliveryText, currentSubText, deliveryFeeText, priceText, switchText, missingPreviewText, currencyName; private LocalizedString purchaseAndSwitchText, purchaseOnlyText, deliveryText, currentSubText, switchText, missingPreviewText, currencyName;
private readonly RectTransform parent; private readonly RectTransform parent;
private readonly Action closeAction; private readonly Action closeAction;
private Sprite pageIndicator; private Sprite pageIndicator;
@@ -85,12 +86,10 @@ namespace Barotrauma
{ {
initialized = true; initialized = true;
currentSubText = TextManager.Get("currentsub"); currentSubText = TextManager.Get("currentsub");
deliveryFeeText = TextManager.Get("deliveryfee");
deliveryText = TextManager.Get("requestdeliverybutton"); deliveryText = TextManager.Get("requestdeliverybutton");
switchText = TextManager.Get("switchtosubmarinebutton"); switchText = TextManager.Get("switchtosubmarinebutton");
purchaseAndSwitchText = TextManager.Get("purchaseandswitch"); purchaseAndSwitchText = TextManager.Get("purchaseandswitch");
purchaseOnlyText = TextManager.Get("purchase"); purchaseOnlyText = TextManager.Get("purchase");
priceText = TextManager.Get("price");
if (transferService) if (transferService)
{ {
deliveryFee = CalculateDeliveryFee(); deliveryFee = CalculateDeliveryFee();
@@ -327,12 +326,12 @@ namespace Barotrauma
}; };
submarineDisplays[i].submarineName.Text = subToDisplay.DisplayName; submarineDisplays[i].submarineName.Text = subToDisplay.DisplayName;
submarineDisplays[i].submarineClass.Text = $"{TextManager.GetWithVariable("submarineclass.classsuffixformat", "[type]", TextManager.Get($"submarineclass.{subToDisplay.SubmarineClass}"))}"; submarineDisplays[i].submarineClass.Text = TextManager.GetWithVariable("submarineclass.classsuffixformat", "[type]", TextManager.Get($"submarineclass.{subToDisplay.SubmarineClass}"));
if (!GameMain.GameSession.IsSubmarineOwned(subToDisplay)) if (!GameMain.GameSession.IsSubmarineOwned(subToDisplay))
{ {
LocalizedString amountString = TextManager.FormatCurrency(subToDisplay.Price); LocalizedString amountString = TextManager.FormatCurrency(subToDisplay.Price);
submarineDisplays[i].submarineFee.Text = priceText.Replace("[amount]", amountString).Replace("[currencyname]", string.Empty).TrimEnd(); submarineDisplays[i].submarineFee.Text = TextManager.GetWithVariable("price", "[amount]", amountString);
} }
else else
{ {
@@ -341,7 +340,7 @@ namespace Barotrauma
if (deliveryFee > 0) if (deliveryFee > 0)
{ {
LocalizedString amountString = TextManager.FormatCurrency(deliveryFee); LocalizedString amountString = TextManager.FormatCurrency(deliveryFee);
submarineDisplays[i].submarineFee.Text = deliveryFeeText.Replace("[amount]", amountString).Replace("[currencyname]", string.Empty).TrimEnd(); submarineDisplays[i].submarineFee.Text = TextManager.GetWithVariable("deliveryfee", "[amount]", amountString);
} }
else else
{ {
@@ -577,7 +576,7 @@ namespace Barotrauma
private void ShowTransferPrompt() private void ShowTransferPrompt()
{ {
if (!GameMain.GameSession.Campaign.Wallet.CanAfford(deliveryFee) && deliveryFee > 0) if (!GameMain.GameSession.Campaign.CanAfford(deliveryFee) && deliveryFee > 0)
{ {
new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("notenoughmoneyfordeliverytext", new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("notenoughmoneyfordeliverytext",
("[currencyname]", currencyName), ("[currencyname]", currencyName),
@@ -625,7 +624,7 @@ namespace Barotrauma
private void ShowBuyPrompt(bool purchaseOnly) private void ShowBuyPrompt(bool purchaseOnly)
{ {
if (!GameMain.GameSession.Campaign.Wallet.CanAfford(selectedSubmarine.Price)) if (!GameMain.GameSession.Campaign.CanAfford(selectedSubmarine.Price))
{ {
new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("notenoughmoneyforpurchasetext", new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("notenoughmoneyforpurchasetext",
("[currencyname]", currencyName), ("[currencyname]", currencyName),
@@ -698,6 +698,12 @@ namespace Barotrauma
Color = Color.Transparent Color = Color.Transparent
}; };
frame.OnSecondaryClicked += (component, data) =>
{
NetLobbyScreen.CreateModerationContextMenu(client);
return true;
};
var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true) var paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.9f), frame.RectTransform, Anchor.Center), isHorizontal: true)
{ {
AbsoluteSpacing = 2 AbsoluteSpacing = 2
@@ -1057,7 +1063,7 @@ namespace Barotrauma
return; return;
} }
bool hasMoneyPermissions = campaign.AllowedToManageCampaign(ClientPermissions.ManageMoney); bool hasMoneyPermissions = CampaignMode.AllowedToManageWallets();
salarySlider.Enabled = hasMoneyPermissions; salarySlider.Enabled = hasMoneyPermissions;
Wallet otherWallet; Wallet otherWallet;
@@ -1402,6 +1408,12 @@ namespace Barotrauma
private void CreateMissionInfo(GUIFrame infoFrame) private void CreateMissionInfo(GUIFrame infoFrame)
{ {
if (Level.Loaded?.LevelData == null)
{
DebugConsole.ThrowError("Failed to display mission info in the tab menu (no level loaded).\n" + Environment.StackTrace);
return;
}
infoFrame.ClearChildren(); infoFrame.ClearChildren();
GUIFrame missionFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox"); GUIFrame missionFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
int padding = (int)(0.0245f * missionFrame.Rect.Height); int padding = (int)(0.0245f * missionFrame.Rect.Height);
@@ -2016,7 +2028,7 @@ namespace Barotrauma
{ {
parent.Content.ClearChildren(); parent.Content.ClearChildren();
List<GUITextBlock> skillNames = new List<GUITextBlock>(); List<GUITextBlock> skillNames = new List<GUITextBlock>();
foreach (Skill skill in character.Info.Job.Skills) foreach (Skill skill in character.Info.Job.GetSkills())
{ {
GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = false }; GUILayoutGroup skillContainer = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.2f), parent.Content.RectTransform), isHorizontal: true) { CanBeFocused = false };
@@ -41,7 +41,7 @@ namespace Barotrauma
private readonly CampaignUI campaignUI; private readonly CampaignUI campaignUI;
private CampaignMode? Campaign => campaignUI.Campaign; private CampaignMode? Campaign => campaignUI.Campaign;
private Wallet PlayerWallet => Campaign?.Wallet ?? Wallet.Invalid; private int PlayerBalance => Campaign?.GetBalance() ?? 0;
private UpgradeTab selectedUpgradeTab = UpgradeTab.Upgrade; private UpgradeTab selectedUpgradeTab = UpgradeTab.Upgrade;
private GUIMessageBox? currectConfirmation; private GUIMessageBox? currectConfirmation;
@@ -295,7 +295,7 @@ namespace Barotrauma
GUILayoutGroup rightLayout = new GUILayoutGroup(rectT(0.5f, 1, topHeaderLayout), childAnchor: Anchor.TopRight); GUILayoutGroup rightLayout = new GUILayoutGroup(rectT(0.5f, 1, topHeaderLayout), childAnchor: Anchor.TopRight);
GUILayoutGroup priceLayout = new GUILayoutGroup(rectT(1, 0.8f, rightLayout), childAnchor: Anchor.Center) { RelativeSpacing = 0.08f }; GUILayoutGroup priceLayout = new GUILayoutGroup(rectT(1, 0.8f, rightLayout), childAnchor: Anchor.Center) { RelativeSpacing = 0.08f };
new GUITextBlock(rectT(1f, 0f, priceLayout), TextManager.Get("CampaignStore.Balance"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right); new GUITextBlock(rectT(1f, 0f, priceLayout), TextManager.Get("CampaignStore.Balance"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right);
new GUITextBlock(rectT(1f, 0f, priceLayout), TextManager.FormatCurrency(PlayerWallet.Balance), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right) { TextGetter = () => TextManager.FormatCurrency(PlayerWallet.Balance) }; new GUITextBlock(rectT(1f, 0f, priceLayout), TextManager.FormatCurrency(PlayerBalance), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right) { TextGetter = () => TextManager.FormatCurrency(PlayerBalance) };
new GUIFrame(rectT(0.5f, 0.1f, rightLayout, Anchor.BottomRight), style: "HorizontalLine") { IgnoreLayoutGroups = true }; new GUIFrame(rectT(0.5f, 0.1f, rightLayout, Anchor.BottomRight), style: "HorizontalLine") { IgnoreLayoutGroups = true };
repairButton.OnClicked = upgradeButton.OnClicked = (button, o) => repairButton.OnClicked = upgradeButton.OnClicked = (button, o) =>
@@ -435,14 +435,14 @@ namespace Barotrauma
return false; return false;
} }
if (PlayerWallet.CanAfford(hullRepairCost)) if (PlayerBalance >= hullRepairCost)
{ {
LocalizedString body = TextManager.GetWithVariable("WallRepairs.PurchasePromptBody", "[amount]", hullRepairCost.ToString()); LocalizedString body = TextManager.GetWithVariable("WallRepairs.PurchasePromptBody", "[amount]", hullRepairCost.ToString());
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () => currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
{ {
if (PlayerWallet.Balance >= hullRepairCost) if (PlayerBalance >= hullRepairCost)
{ {
PlayerWallet.TryDeduct(hullRepairCost); Campaign.TryPurchase(null, hullRepairCost);
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs"); GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
Campaign.PurchasedHullRepairs = true; Campaign.PurchasedHullRepairs = true;
button.Enabled = false; button.Enabled = false;
@@ -470,14 +470,14 @@ namespace Barotrauma
CreateRepairEntry(currentStoreLayout.Content, TextManager.Get("repairallitems"), "RepairItemsButton", itemRepairCost, (button, o) => CreateRepairEntry(currentStoreLayout.Content, TextManager.Get("repairallitems"), "RepairItemsButton", itemRepairCost, (button, o) =>
{ {
if (PlayerWallet.Balance >= itemRepairCost && !Campaign.PurchasedItemRepairs) if (PlayerBalance >= itemRepairCost && !Campaign.PurchasedItemRepairs)
{ {
LocalizedString body = TextManager.GetWithVariable("ItemRepairs.PurchasePromptBody", "[amount]", itemRepairCost.ToString()); LocalizedString body = TextManager.GetWithVariable("ItemRepairs.PurchasePromptBody", "[amount]", itemRepairCost.ToString());
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () => currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
{ {
if (PlayerWallet.Balance >= itemRepairCost && !Campaign.PurchasedItemRepairs) if (PlayerBalance >= itemRepairCost && !Campaign.PurchasedItemRepairs)
{ {
PlayerWallet.TryDeduct(itemRepairCost); Campaign.TryPurchase(null, itemRepairCost);
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs"); GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
Campaign.PurchasedItemRepairs = true; Campaign.PurchasedItemRepairs = true;
button.Enabled = false; button.Enabled = false;
@@ -516,14 +516,14 @@ namespace Barotrauma
return false; return false;
} }
if (PlayerWallet.CanAfford(shuttleRetrieveCost) && !Campaign.PurchasedLostShuttles) if (PlayerBalance >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
{ {
LocalizedString body = TextManager.GetWithVariable("ReplaceLostShuttles.PurchasePromptBody", "[amount]", shuttleRetrieveCost.ToString()); LocalizedString body = TextManager.GetWithVariable("ReplaceLostShuttles.PurchasePromptBody", "[amount]", shuttleRetrieveCost.ToString());
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () => currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
{ {
if (PlayerWallet.Balance >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles) if (PlayerBalance >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
{ {
PlayerWallet.TryDeduct(shuttleRetrieveCost); Campaign.TryPurchase(null, shuttleRetrieveCost);
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle"); GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
Campaign.PurchasedLostShuttles = true; Campaign.PurchasedLostShuttles = true;
button.Enabled = false; button.Enabled = false;
@@ -581,13 +581,13 @@ namespace Barotrauma
new GUITextBlock(rectT(1, 0, textLayout), title, font: GUIStyle.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true }; new GUITextBlock(rectT(1, 0, textLayout), title, font: GUIStyle.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true };
new GUITextBlock(rectT(1, 0, textLayout), TextManager.FormatCurrency(price)); 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 = "buybutton" };
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { ClickSound = GUISoundType.HireRepairClick, Enabled = PlayerWallet.Balance >= price && !isDisabled, OnClicked = onPressed }; new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { ClickSound = GUISoundType.HireRepairClick, Enabled = PlayerBalance >= price && !isDisabled, OnClicked = onPressed };
contentLayout.Recalculate(); contentLayout.Recalculate();
buyButtonLayout.Recalculate(); buyButtonLayout.Recalculate();
if (disableElement) if (disableElement)
{ {
frameChild.Enabled = PlayerWallet.Balance >= price && !isDisabled; frameChild.Enabled = PlayerBalance >= price && !isDisabled;
} }
if (!HasPermission) if (!HasPermission)
@@ -972,7 +972,7 @@ namespace Barotrauma
buttonStyle: isPurchased ? "WeaponInstallButton" : "StoreAddToCrateButton")); buttonStyle: isPurchased ? "WeaponInstallButton" : "StoreAddToCrateButton"));
if (!(frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton buyButton)) { continue; } if (!(frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton buyButton)) { continue; }
if (PlayerWallet.CanAfford(price)) if (PlayerBalance >= price)
{ {
buyButton.Enabled = true; buyButton.Enabled = true;
buyButton.OnClicked += (button, o) => buyButton.OnClicked += (button, o) =>
@@ -1602,7 +1602,7 @@ namespace Barotrauma
if (button != null) if (button != null)
{ {
button.Enabled = currentLevel < prefab.MaxLevel; button.Enabled = currentLevel < prefab.MaxLevel;
if (WaitForServerUpdate || !campaign.Wallet.CanAfford(price)) if (WaitForServerUpdate || campaign.GetBalance() < price)
{ {
button.Enabled = false; button.Enabled = false;
} }
@@ -1104,10 +1104,10 @@ namespace Barotrauma
var msgBox = new GUIMessageBox(TextManager.Get("EditorDisclaimerTitle"), TextManager.Get("EditorDisclaimerText")); 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 }; 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); linkHolder.RectTransform.MaxSize = new Point(int.MaxValue, linkHolder.Rect.Height);
List<(LocalizedString Caption, LocalizedString Url)> links = new List<(LocalizedString, LocalizedString)>() List<(LocalizedString Caption, string Url)> links = new List<(LocalizedString, string)>()
{ {
(TextManager.Get("EditorDisclaimerWikiLink"), TextManager.Get("EditorDisclaimerWikiUrl")), (TextManager.Get("EditorDisclaimerWikiLink"), TextManager.Get("EditorDisclaimerWikiUrl").Fallback("https://barotraumagame.com/wiki").Value),
(TextManager.Get("EditorDisclaimerDiscordLink"), TextManager.Get("EditorDisclaimerDiscordUrl")), (TextManager.Get("EditorDisclaimerDiscordLink"), TextManager.Get("EditorDisclaimerDiscordUrl").Fallback("https://discordapp.com/invite/undertow").Value),
}; };
foreach (var link in links) foreach (var link in links)
{ {
@@ -5,6 +5,7 @@ using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Barotrauma namespace Barotrauma
@@ -28,6 +29,8 @@ namespace Barotrauma
protected GUIFrame campaignUIContainer; protected GUIFrame campaignUIContainer;
public CampaignUI CampaignUI; public CampaignUI CampaignUI;
public static CancellationTokenSource StartRoundCancellationToken { get; private set; }
public bool ForceMapUI public bool ForceMapUI
{ {
get; get;
@@ -99,6 +102,16 @@ namespace Barotrauma
GameMain.Client.ConnectedClients.None(c => c.InGame && (c.IsOwner || c.HasPermission(permissions))); GameMain.Client.ConnectedClients.None(c => c.InGame && (c.IsOwner || c.HasPermission(permissions)));
} }
public static bool AllowedToManageWallets()
{
if (GameMain.Client == null) { return true; }
return
GameMain.Client.HasPermission(ClientPermissions.ManageMoney) ||
GameMain.Client.HasPermission(ClientPermissions.ManageCampaign) ||
GameMain.Client.IsServerOwner;
}
public override void Draw(SpriteBatch spriteBatch) public override void Draw(SpriteBatch spriteBatch)
{ {
if (overlayColor.A > 0) if (overlayColor.A > 0)
@@ -245,10 +258,11 @@ namespace Barotrauma
GUI.ClearCursorWait(); GUI.ClearCursorWait();
StartRoundCancellationToken = new CancellationTokenSource();
var loadTask = Task.Run(async () => var loadTask = Task.Run(async () =>
{ {
await Task.Yield(); await Task.Yield();
Rand.ThreadId = System.Threading.Thread.CurrentThread.ManagedThreadId; Rand.ThreadId = Thread.CurrentThread.ManagedThreadId;
try try
{ {
GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror); GameMain.GameSession.StartRound(newLevel, mirrorLevel: mirror);
@@ -258,7 +272,7 @@ namespace Barotrauma
roundSummaryScreen.LoadException = e; roundSummaryScreen.LoadException = e;
} }
Rand.ThreadId = 0; Rand.ThreadId = 0;
}); }, StartRoundCancellationToken.Token);
TaskPool.Add("AsyncCampaignStartRound", loadTask, (t) => TaskPool.Add("AsyncCampaignStartRound", loadTask, (t) =>
{ {
overlayColor = Color.Transparent; overlayColor = Color.Transparent;
@@ -37,6 +37,16 @@ namespace Barotrauma
public Wallet PersonalWallet => Character.Controlled?.Wallet ?? Wallet.Invalid; public Wallet PersonalWallet => Character.Controlled?.Wallet ?? Wallet.Invalid;
public override Wallet Wallet => GetWallet(); public override Wallet Wallet => GetWallet();
public override int GetBalance(Client client = null)
{
if (!AllowedToManageWallets())
{
return PersonalWallet.Balance;
}
return PersonalWallet.Balance + Bank.Balance;
}
public override Wallet GetWallet(Client client = null) public override Wallet GetWallet(Client client = null)
{ {
return PersonalWallet; return PersonalWallet;
@@ -913,6 +923,31 @@ namespace Barotrauma
} }
} }
public override bool TryPurchase(Client client, int price)
{
if (!AllowedToManageCampaign(ClientPermissions.ManageCampaign))
{
return PersonalWallet.TryDeduct(price);
}
int balance = PersonalWallet.Balance;
if (balance >= price)
{
return PersonalWallet.TryDeduct(price);
}
if (balance + Bank.Balance >= price)
{
int remainder = price - balance;
if (balance > 0) { PersonalWallet.Deduct(balance); }
Bank.Deduct(remainder);
return true ;
}
return false;
}
public override void Save(XElement element) public override void Save(XElement element)
{ {
//do nothing, the clients get the save files from the server //do nothing, the clients get the save files from the server
@@ -1550,9 +1550,10 @@ namespace Barotrauma
DebugConsole.Log($"Received entity spawn message for item \"{itemName}\" (identifier: {itemIdentifier}, id: {itemId})"); DebugConsole.Log($"Received entity spawn message for item \"{itemName}\" (identifier: {itemIdentifier}, id: {itemId})");
var itemPrefab = string.IsNullOrEmpty(itemIdentifier) ? ItemPrefab itemPrefab =
MapEntityPrefab.Find(itemName, null, showErrorMessages: false) as ItemPrefab : string.IsNullOrEmpty(itemIdentifier) ?
MapEntityPrefab.Find(itemName, itemIdentifier, showErrorMessages: false) as ItemPrefab; ItemPrefab.Find(itemName, Identifier.Empty) :
ItemPrefab.Find(itemName, itemIdentifier.ToIdentifier());
Vector2 pos = Vector2.Zero; Vector2 pos = Vector2.Zero;
Submarine sub = null; Submarine sub = null;
@@ -18,8 +18,8 @@ namespace Barotrauma
foreach ((Identifier identifier, Rectangle rect) in DisplayEntities) foreach ((Identifier identifier, Rectangle rect) in DisplayEntities)
{ {
var entityPrefab = MapEntityPrefab.FindByIdentifier(identifier); var entityPrefab = FindByIdentifier(identifier);
if (entityPrefab is CoreEntityPrefab) { continue; } if (entityPrefab is CoreEntityPrefab || entityPrefab == null) { continue; }
var drawRect = new Rectangle( var drawRect = new Rectangle(
(int)(rect.X * scale) + drawArea.Center.X, (int)((rect.Y) * scale) - drawArea.Center.Y, (int)(rect.X * scale) + drawArea.Center.X, (int)((rect.Y) * scale) - drawArea.Center.Y,
(int)(rect.Width * scale), (int)(rect.Height * scale)); (int)(rect.Width * scale), (int)(rect.Height * scale));
@@ -1,10 +1,12 @@
using Barotrauma.IO; using System;
using Barotrauma.IO;
using Barotrauma.Items.Components; using Barotrauma.Items.Components;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Input;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
using Barotrauma.Extensions;
namespace Barotrauma namespace Barotrauma
{ {
@@ -107,6 +109,25 @@ namespace Barotrauma
} }
var pathContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), isHorizontal: true); var pathContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.2f), paddedFrame.RectTransform), isHorizontal: true);
string filePath = this.filePath;
if (filePath.StartsWith("Submarines"))
{
//this is the old submarines path, try to find a local mod that has a submarine with this name
string subName = Path.GetFileNameWithoutExtension(filePath);
string foundPath = ContentPackageManager.LocalPackages.Concat(ContentPackageManager.VanillaCorePackage.ToEnumerable())
.SelectMany(p => p.GetFiles<SubmarineFile>())
.FirstOrDefault(f => Path.GetFileNameWithoutExtension(f.Path.Value).Equals(subName, StringComparison.OrdinalIgnoreCase))
?.Path.Value;
if (foundPath.IsNullOrEmpty())
{
//no such sub found among the local mods, just guess the correct path
foundPath = Path.Combine(ContentPackage.LocalModsDir, subName, $"{subName}.sub");
}
filePath = foundPath;
}
var pathBox = new GUITextBox(new RectTransform(new Vector2(0.75f, 1.0f), pathContainer.RectTransform), filePath, font: GUIStyle.SmallFont); var pathBox = new GUITextBox(new RectTransform(new Vector2(0.75f, 1.0f), pathContainer.RectTransform), filePath, font: GUIStyle.SmallFont);
var reloadButton = new GUIButton(new RectTransform(new Vector2(0.25f / pathBox.RectTransform.RelativeSize.X, 1.0f), pathBox.RectTransform, Anchor.CenterRight, Pivot.CenterLeft), var reloadButton = new GUIButton(new RectTransform(new Vector2(0.25f / pathBox.RectTransform.RelativeSize.X, 1.0f), pathBox.RectTransform, Anchor.CenterRight, Pivot.CenterLeft),
TextManager.Get("ReloadLinkedSub"), style: "GUIButtonSmall") TextManager.Get("ReloadLinkedSub"), style: "GUIButtonSmall")
@@ -2709,6 +2709,8 @@ namespace Barotrauma.Networking
SteamManager.LeaveLobby(); SteamManager.LeaveLobby();
} }
CampaignMode.StartRoundCancellationToken?.Cancel();
clientPeer?.Close(); clientPeer?.Close();
clientPeer = null; clientPeer = null;
@@ -149,7 +149,7 @@ namespace Barotrauma
} }
else else
{ {
if (Campaign.Wallet.TryDeduct(CampaignMode.HullRepairCost)) if (Campaign.TryPurchase(null, CampaignMode.HullRepairCost))
{ {
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.HullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs"); GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.HullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
Campaign.PurchasedHullRepairs = true; Campaign.PurchasedHullRepairs = true;
@@ -194,7 +194,7 @@ namespace Barotrauma
} }
else else
{ {
if (Campaign.Wallet.TryDeduct(CampaignMode.ItemRepairCost)) if (Campaign.TryPurchase(null, CampaignMode.ItemRepairCost))
{ {
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.ItemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs"); GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.ItemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
Campaign.PurchasedItemRepairs = true; Campaign.PurchasedItemRepairs = true;
@@ -246,7 +246,7 @@ namespace Barotrauma
} }
else else
{ {
if (Campaign.Wallet.TryDeduct(CampaignMode.ShuttleReplaceCost)) if (Campaign.TryPurchase(null, CampaignMode.ShuttleReplaceCost))
{ {
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.ShuttleReplaceCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle"); GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.ShuttleReplaceCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
Campaign.PurchasedLostShuttles = true; Campaign.PurchasedLostShuttles = true;
@@ -736,7 +736,7 @@ namespace Barotrauma
public static LocalizedString GetMoney() public static LocalizedString GetMoney()
{ {
return TextManager.GetWithVariable("PlayerCredits", "[credits]", (GameMain.GameSession?.Campaign == null) ? "0" : string.Format(CultureInfo.InvariantCulture, "{0:N0}", GameMain.GameSession.Campaign.Wallet.Balance)); return TextManager.GetWithVariable("PlayerCredits", "[credits]", (GameMain.GameSession?.Campaign == null) ? "0" : string.Format(CultureInfo.InvariantCulture, "{0:N0}", GameMain.GameSession.Campaign.GetBalance()));
} }
private void UpdateMaxMissions(Location location) private void UpdateMaxMissions(Location location)
@@ -1559,7 +1559,7 @@ namespace Barotrauma
}; };
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContainer.RectTransform), TextManager.Get("Skills"), font: GUIStyle.SubHeadingFont); new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContainer.RectTransform), TextManager.Get("Skills"), font: GUIStyle.SubHeadingFont);
foreach (Skill skill in characterInfo.Job.Skills) foreach (Skill skill in characterInfo.Job.GetSkills())
{ {
Color textColor = Color.White * (0.5f + skill.Level / 200.0f); Color textColor = Color.White * (0.5f + skill.Level / 200.0f);
var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContainer.RectTransform), var skillText = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), infoContainer.RectTransform),
@@ -2190,17 +2190,17 @@ namespace Barotrauma
canKick = canBan = canPromo = false; canKick = canBan = canPromo = false;
} }
List<ContextMenuOption> options = new List<ContextMenuOption>(); List<ContextMenuOption> options = new List<ContextMenuOption>
options.Add(new ContextMenuOption("ViewSteamProfile", isEnabled: hasSteam, onSelected: delegate
{ {
Steamworks.SteamFriends.OpenWebOverlay($"https://steamcommunity.com/profiles/{client.SteamID}"); new ContextMenuOption("ViewSteamProfile", isEnabled: hasSteam, onSelected: delegate
})); {
Steamworks.SteamFriends.OpenWebOverlay($"https://steamcommunity.com/profiles/{client.SteamID}");
options.Add(new ContextMenuOption("ModerationMenu.ManagePlayer", isEnabled: true, onSelected: delegate }),
{ new ContextMenuOption("ModerationMenu.ManagePlayer", isEnabled: true, onSelected: delegate
GameMain.NetLobbyScreen?.SelectPlayer(client); {
})); GameMain.NetLobbyScreen?.SelectPlayer(client);
})
};
// Creates sub context menu options for all the ranks // Creates sub context menu options for all the ranks
List<ContextMenuOption> rankOptions = new List<ContextMenuOption>(); List<ContextMenuOption> rankOptions = new List<ContextMenuOption>();
@@ -1167,6 +1167,9 @@ namespace Barotrauma
#endif #endif
CreateEntityElement(ep, entitiesPerRow, allEntityList.Content); CreateEntityElement(ep, entitiesPerRow, allEntityList.Content);
} }
allEntityList.Content.RectTransform.SortChildren((i1, i2) =>
string.Compare(((MapEntityPrefab)i1.GUIComponent.UserData)?.Name.Value, (i2.GUIComponent.UserData as MapEntityPrefab)?.Name.Value, StringComparison.Ordinal));
} }
private void CreateEntityElement(MapEntityPrefab ep, int entitiesPerRow, GUIComponent parent) private void CreateEntityElement(MapEntityPrefab ep, int entitiesPerRow, GUIComponent parent)
@@ -1906,6 +1909,7 @@ namespace Barotrauma
} }
SubmarineInfo.RefreshSavedSub(savePath); SubmarineInfo.RefreshSavedSub(savePath);
if (prevSavePath != null && prevSavePath != savePath) { SubmarineInfo.RefreshSavedSub(prevSavePath); } if (prevSavePath != null && prevSavePath != savePath) { SubmarineInfo.RefreshSavedSub(prevSavePath); }
MainSub.Info.PreviewImage = SubmarineInfo.SavedSubmarines.FirstOrDefault(s => s.FilePath == savePath)?.PreviewImage;
string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder); string downloadFolder = Path.GetFullPath(SaveUtil.SubmarineDownloadFolder);
linkedSubBox.ClearChildren(); linkedSubBox.ClearChildren();
@@ -93,10 +93,17 @@ namespace Barotrauma.Steam
if (!t.TryGetResult(out ISet<Steamworks.Ugc.Item> publishedItems)) { return; } if (!t.TryGetResult(out ISet<Steamworks.Ugc.Item> publishedItems)) { return; }
var allRequiredInstalled = subscribedIds.Union(publishedItems.Select(it => it.Id)).ToHashSet(); var allRequiredInstalled = subscribedIds.Union(publishedItems.Select(it => it.Id)).ToHashSet();
bool needsRefresh = false;
foreach (var id in installedIds.Where(id2 => !allRequiredInstalled.Contains(id2))) foreach (var id in installedIds.Where(id2 => !allRequiredInstalled.Contains(id2)))
{ {
Steamworks.Ugc.Item item = new Steamworks.Ugc.Item(id); Steamworks.Ugc.Item item = new Steamworks.Ugc.Item(id);
SteamManager.Workshop.Uninstall(item); SteamManager.Workshop.Uninstall(item);
needsRefresh = true;
}
if (needsRefresh)
{
PopulateInstalledModLists();
} }
}); });
} }
@@ -214,9 +214,9 @@ namespace Barotrauma
//try to place commands of the same texture //try to place commands of the same texture
//contiguously for optimal buffer generation //contiguously for optimal buffer generation
//while maintaining the same visual result //while maintaining the same visual result
for (int i=1;i<commandList.Count;i++) for (int i = 1; i < commandList.Count; i++)
{ {
if (commandList[i].Texture != commandList[i-1].Texture) if (commandList[i].Texture != commandList[i - 1].Texture)
{ {
for (int j = i - 1; j >= 0; j--) for (int j = i - 1; j >= 0; j--)
{ {
@@ -244,6 +244,7 @@ namespace Barotrauma
//requires a vertex buffer to be rendered //requires a vertex buffer to be rendered
CrossThread.RequestExecutionOnMainThread(() => CrossThread.RequestExecutionOnMainThread(() =>
{ {
if (isDisposed) { return; }
if (commandList.Count == 0) { return; } if (commandList.Count == 0) { return; }
int startIndex = 0; int startIndex = 0;
for (int i = 1; i < commandList.Count; i++) for (int i = 1; i < commandList.Count; i++)
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.17.12.0</Version> <Version>0.17.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.17.12.0</Version> <Version>0.17.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma</Product> <Product>Barotrauma</Product>
<Version>0.17.12.0</Version> <Version>0.17.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>Barotrauma</AssemblyName> <AssemblyName>Barotrauma</AssemblyName>
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.17.12.0</Version> <Version>0.17.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
+1 -1
View File
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.17.12.0</Version> <Version>0.17.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -63,8 +63,9 @@ namespace Barotrauma
{ {
msg.Write(Job.Prefab.Identifier); msg.Write(Job.Prefab.Identifier);
msg.Write((byte)Job.Variant); msg.Write((byte)Job.Variant);
msg.Write((byte)Job.Skills.Count); var skills = Job.GetSkills();
foreach (Skill skill in Job.Skills) msg.Write((byte)skills.Count());
foreach (Skill skill in skills)
{ {
msg.Write(skill.Identifier); msg.Write(skill.Identifier);
msg.Write(skill.Level); msg.Write(skill.Level);
@@ -434,8 +434,9 @@ namespace Barotrauma
} }
else else
{ {
msg.Write((byte)Info.Job.Skills.Count); var skills = Info.Job.GetSkills();
foreach (Skill skill in Info.Job.Skills) msg.Write((byte)skills.Count());
foreach (Skill skill in skills)
{ {
msg.Write(skill.Identifier); msg.Write(skill.Identifier);
msg.Write(skill.Level); msg.Write(skill.Level);
@@ -2391,7 +2391,7 @@ namespace Barotrauma
if (isMax) { level = 100; } if (isMax) { level = 100; }
if (skillIdentifier == "all") if (skillIdentifier == "all")
{ {
foreach (Skill skill in character.Info.Job.Skills) foreach (Skill skill in character.Info.Job.GetSkills())
{ {
character.Info.SetSkillLevel(skill.Identifier, level); character.Info.SetSkillLevel(skill.Identifier, level);
} }
@@ -35,7 +35,7 @@ namespace Barotrauma
int itemValue = sellValues[item.ItemPrefab]; int itemValue = sellValues[item.ItemPrefab];
if (store.Balance < itemValue || item.Removed) { continue; } if (store.Balance < itemValue || item.Removed) { continue; }
store.Balance += itemValue; store.Balance += itemValue;
campaign.GetWallet(client).TryDeduct(itemValue); campaign.TryPurchase(client, itemValue);
storeSpecificItems.Remove(item); storeSpecificItems.Remove(item);
} }
} }
@@ -178,6 +178,14 @@ namespace Barotrauma
GameMain.Server.ConnectedClients.None(c => c.InGame && (IsOwner(c) || c.HasPermission(permissions))); GameMain.Server.ConnectedClients.None(c => c.InGame && (IsOwner(c) || c.HasPermission(permissions)));
} }
public bool AllowedToManageWallets(Client client)
{
return
client.HasPermission(ClientPermissions.ManageCampaign) ||
client.HasPermission(ClientPermissions.ManageMoney) ||
IsOwner(client);
}
public void SaveExperiencePoints(Client client) public void SaveExperiencePoints(Client client)
{ {
ClearSavedExperiencePoints(client); ClearSavedExperiencePoints(client);
@@ -430,7 +438,7 @@ namespace Barotrauma
} }
public bool CanPurchaseSub(SubmarineInfo info, Client client) public bool CanPurchaseSub(SubmarineInfo info, Client client)
=> GetWallet(client).CanAfford(info.Price) && GetCampaignSubs().Contains(info); => CanAfford(info.Price, client) && GetCampaignSubs().Contains(info);
private readonly List<CharacterCampaignData> discardedCharacters = new List<CharacterCampaignData>(); private readonly List<CharacterCampaignData> discardedCharacters = new List<CharacterCampaignData>();
public void DiscardClientCharacterData(Client client) public void DiscardClientCharacterData(Client client)
@@ -755,8 +763,8 @@ namespace Barotrauma
{ {
switch (purchasedHullRepairs) switch (purchasedHullRepairs)
{ {
case true when personalWallet.CanAfford(hullRepairCost): case true when GetBalance(sender) >= hullRepairCost:
personalWallet.Deduct(hullRepairCost); TryPurchase(sender, hullRepairCost);
PurchasedHullRepairs = true; PurchasedHullRepairs = true;
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs"); GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
break; break;
@@ -771,8 +779,8 @@ namespace Barotrauma
{ {
switch (purchasedItemRepairs) switch (purchasedItemRepairs)
{ {
case true when personalWallet.CanAfford(itemRepairCost): case true when GetBalance(sender) >= itemRepairCost:
personalWallet.Deduct(itemRepairCost); TryPurchase(sender, itemRepairCost);
PurchasedItemRepairs = true; PurchasedItemRepairs = true;
GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs"); GameAnalyticsManager.AddMoneySpentEvent(itemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
break; break;
@@ -789,7 +797,7 @@ namespace Barotrauma
{ {
GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox); GameMain.Server.SendDirectChatMessage(TextManager.FormatServerMessage("ReplaceShuttleDockingPortOccupied"), sender, ChatMessageType.MessageBox);
} }
else if (purchasedLostShuttles && personalWallet.TryDeduct(shuttleRetrieveCost)) else if (purchasedLostShuttles && TryPurchase(sender, shuttleRetrieveCost))
{ {
PurchasedLostShuttles = true; PurchasedLostShuttles = true;
GameAnalyticsManager.AddMoneySpentEvent(shuttleRetrieveCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle"); GameAnalyticsManager.AddMoneySpentEvent(shuttleRetrieveCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
@@ -972,7 +980,7 @@ namespace Barotrauma
switch (transfer.Sender) switch (transfer.Sender)
{ {
case Some<ushort> { Value: var id }: case Some<ushort> { Value: var id }:
if (id != sender.CharacterID && !AllowedToManageCampaign(sender, ClientPermissions.ManageMoney)) { return; } if (id != sender.CharacterID && !AllowedToManageWallets(sender)) { return; }
Wallet wallet = GetWalletByID(id); Wallet wallet = GetWalletByID(id);
if (wallet is InvalidWallet) { return; } if (wallet is InvalidWallet) { return; }
@@ -980,7 +988,7 @@ namespace Barotrauma
TransferMoney(wallet); TransferMoney(wallet);
break; break;
case None<ushort> _: case None<ushort> _:
if (!AllowedToManageCampaign(sender, ClientPermissions.ManageMoney)) if (!AllowedToManageWallets(sender))
{ {
if (transfer.Receiver is Some<ushort> { Value: var receiverId } && receiverId == sender.CharacterID) if (transfer.Receiver is Some<ushort> { Value: var receiverId } && receiverId == sender.CharacterID)
{ {
@@ -1025,7 +1033,7 @@ namespace Barotrauma
{ {
NetWalletSetSalaryUpdate update = INetSerializableStruct.Read<NetWalletSetSalaryUpdate>(msg); NetWalletSetSalaryUpdate update = INetSerializableStruct.Read<NetWalletSetSalaryUpdate>(msg);
if (!AllowedToManageCampaign(sender, ClientPermissions.ManageMoney)) { return; } if (!AllowedToManageWallets(sender)) { return; }
Character targetCharacter = Character.CharacterList.FirstOrDefault(c => c.ID == update.Target); Character targetCharacter = Character.CharacterList.FirstOrDefault(c => c.ID == update.Target);
targetCharacter?.Wallet.SetRewardDistribution(update.NewRewardDistribution); targetCharacter?.Wallet.SetRewardDistribution(update.NewRewardDistribution);
@@ -1231,6 +1239,45 @@ namespace Barotrauma
} }
} }
public override bool TryPurchase(Client client, int price)
{
Wallet wallet = GetWallet(client);
if (!AllowedToManageWallets(client))
{
return wallet.TryDeduct(price);
}
int balance = wallet.Balance;
if (balance >= price)
{
return wallet.TryDeduct(price);
}
if (balance + Bank.Balance >= price)
{
int remainder = price - balance;
if (balance > 0) { wallet.Deduct(balance); }
Bank.Deduct(remainder);
return true ;
}
return false;
}
public override int GetBalance(Client client = null)
{
if (client is null) { return 0; }
Wallet wallet = GetWallet(client);
if (!AllowedToManageWallets(client))
{
return wallet.Balance;
}
return wallet.Balance + Bank.Balance;
}
public override void Save(XElement element) public override void Save(XElement element)
{ {
element.Add(new XAttribute("campaignid", CampaignID)); element.Add(new XAttribute("campaignid", CampaignID));
@@ -188,11 +188,10 @@ namespace Barotrauma.Items.Components
//already connected, no need to do anything //already connected, no need to do anything
if (Connections[i].Wires.Contains(newWire)) { continue; } if (Connections[i].Wires.Contains(newWire)) { continue; }
Connections[i].TryAddLink(newWire);
newWire.Connect(Connections[i], true, true); newWire.Connect(Connections[i], true, true);
Connections[i].TryAddLink(newWire);
var otherConnection = newWire.OtherConnection(Connections[i]); var otherConnection = newWire.OtherConnection(Connections[i]);
if (otherConnection == null) if (otherConnection == null)
{ {
GameServer.Log(GameServer.CharacterLogName(c.Character) + " connected a wire to " + GameServer.Log(GameServer.CharacterLogName(c.Character) + " connected a wire to " +
@@ -79,6 +79,7 @@ namespace Barotrauma
Vector2? worldPosition = applyStatusEffectEventData.WorldPosition; Vector2? worldPosition = applyStatusEffectEventData.WorldPosition;
Character targetCharacter = applyStatusEffectEventData.TargetCharacter; Character targetCharacter = applyStatusEffectEventData.TargetCharacter;
if (targetCharacter != null && targetCharacter.Removed) { targetCharacter = null; }
byte targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255; byte targetLimbIndex = targetLimb != null && targetCharacter != null ? (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb) : (byte)255;
msg.WriteRangedInteger((int)actionType, 0, Enum.GetValues(typeof(ActionType)).Length - 1); msg.WriteRangedInteger((int)actionType, 0, Enum.GetValues(typeof(ActionType)).Length - 1);
@@ -520,7 +520,7 @@ namespace Barotrauma.Networking
public static void ReduceCharacterSkills(CharacterInfo characterInfo) public static void ReduceCharacterSkills(CharacterInfo characterInfo)
{ {
if (characterInfo?.Job == null) { return; } if (characterInfo?.Job == null) { return; }
foreach (Skill skill in characterInfo.Job.Skills) foreach (Skill skill in characterInfo.Job.GetSkills())
{ {
var skillPrefab = characterInfo.Job.Prefab.Skills.Find(s => skill.Identifier == s.Identifier); var skillPrefab = characterInfo.Job.Prefab.Skills.Find(s => skill.Identifier == s.Identifier);
if (skillPrefab == null) { continue; } if (skillPrefab == null) { continue; }
@@ -6,7 +6,7 @@
<RootNamespace>Barotrauma</RootNamespace> <RootNamespace>Barotrauma</RootNamespace>
<Authors>FakeFish, Undertow Games</Authors> <Authors>FakeFish, Undertow Games</Authors>
<Product>Barotrauma Dedicated Server</Product> <Product>Barotrauma Dedicated Server</Product>
<Version>0.17.12.0</Version> <Version>0.17.13.0</Version>
<Copyright>Copyright © FakeFish 2018-2022</Copyright> <Copyright>Copyright © FakeFish 2018-2022</Copyright>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<AssemblyName>DedicatedServer</AssemblyName> <AssemblyName>DedicatedServer</AssemblyName>
@@ -838,7 +838,7 @@ namespace Barotrauma
if (container == null) { return 0; } if (container == null) { return 0; }
if (!container.Inventory.CanBePut(containableItem)) { return 0; } if (!container.Inventory.CanBePut(containableItem)) { return 0; }
var rootContainer = container.Item.GetRootContainer(); var rootContainer = container.Item.GetRootContainer();
if (rootContainer?.GetComponent<Fabricator>() != null || rootContainer?.GetComponent<Fabricator>() != null) { return 0; } if (rootContainer?.GetComponent<Fabricator>() != null || rootContainer?.GetComponent<Deconstructor>() != null) { return 0; }
if (container.ShouldBeContained(containableItem, out bool isRestrictionsDefined)) if (container.ShouldBeContained(containableItem, out bool isRestrictionsDefined))
{ {
if (isRestrictionsDefined) if (isRestrictionsDefined)
@@ -1033,7 +1033,7 @@ namespace Barotrauma
if (Name == null || Job == null) { return 0; } if (Name == null || Job == null) { return 0; }
int salary = 0; int salary = 0;
foreach (Skill skill in Job.Skills) foreach (Skill skill in Job.GetSkills())
{ {
salary += (int)(skill.Level * skill.PriceMultiplier); salary += (int)(skill.Level * skill.PriceMultiplier);
} }
@@ -1076,10 +1076,10 @@ namespace Barotrauma
{ {
if (Job == null) { return; } if (Job == null) { return; }
var skill = Job.Skills.Find(s => s.Identifier == skillIdentifier); var skill = Job.GetSkill(skillIdentifier);
if (skill == null) if (skill == null)
{ {
Job.Skills.Add(new Skill(skillIdentifier, level)); Job.IncreaseSkillLevel(skillIdentifier, level, increasePastMax: false);
OnSkillChanged(skillIdentifier, 0.0f, level); OnSkillChanged(skillIdentifier, 0.0f, level);
} }
else else
@@ -384,7 +384,7 @@ namespace Barotrauma
foreach (var itemPrefab in ItemPrefab.Prefabs) foreach (var itemPrefab in ItemPrefab.Prefabs)
{ {
float suitability = Math.Max(itemPrefab.GetTreatmentSuitability(Identifier), itemPrefab.GetTreatmentSuitability(AfflictionType)); float suitability = Math.Max(itemPrefab.GetTreatmentSuitability(Identifier), itemPrefab.GetTreatmentSuitability(AfflictionType));
if (suitability > 0.0f) if (!MathUtils.NearlyEqual(suitability, 0.0f))
{ {
yield return new KeyValuePair<Identifier, float>(itemPrefab.Identifier, suitability); yield return new KeyValuePair<Identifier, float>(itemPrefab.Identifier, suitability);
} }
@@ -154,10 +154,14 @@ namespace Barotrauma
public void GiveItems(Character character, Submarine submarine, Rand.RandSync randSync = Rand.RandSync.Unsynced, bool createNetworkEvents = true) public void GiveItems(Character character, Submarine submarine, Rand.RandSync randSync = Rand.RandSync.Unsynced, bool createNetworkEvents = true)
{ {
if (ItemSets == null || !ItemSets.Any()) { return; }
var spawnItems = ToolBox.SelectWeightedRandom(ItemSets.Keys.ToList(), ItemSets.Values.ToList(), randSync); var spawnItems = ToolBox.SelectWeightedRandom(ItemSets.Keys.ToList(), ItemSets.Values.ToList(), randSync);
foreach (XElement itemElement in spawnItems.GetChildElements("item")) if (spawnItems != null)
{ {
InitializeItem(character, itemElement, submarine, this, createNetworkEvents: createNetworkEvents); foreach (XElement itemElement in spawnItems.GetChildElements("item"))
{
InitializeItem(character, itemElement, submarine, this, createNetworkEvents: createNetworkEvents);
}
} }
} }
@@ -17,8 +17,6 @@ namespace Barotrauma
public JobPrefab Prefab => prefab; public JobPrefab Prefab => prefab;
public List<Skill> Skills => skills.Values.ToList();
public int Variant; public int Variant;
public Skill PrimarySkill { get; } public Skill PrimarySkill { get; }
@@ -82,6 +80,11 @@ namespace Barotrauma
return new Job(prefab, randSync, variant); return new Job(prefab, randSync, variant);
} }
public IEnumerable<Skill> GetSkills()
{
return skills.Values;
}
public float GetSkillLevel(Identifier skillIdentifier) public float GetSkillLevel(Identifier skillIdentifier)
{ {
if (skillIdentifier.IsEmpty) { return 0.0f; } if (skillIdentifier.IsEmpty) { return 0.0f; }
@@ -89,6 +92,22 @@ namespace Barotrauma
return skill?.Level ?? 0.0f; return skill?.Level ?? 0.0f;
} }
public Skill GetSkill(Identifier skillIdentifier)
{
if (skillIdentifier.IsEmpty) { return null; }
skills.TryGetValue(skillIdentifier, out Skill skill);
return skill;
}
public void OverrideSkills(Dictionary<Identifier, float> newSkills)
{
skills.Clear();
foreach (var newSkill in newSkills)
{
skills.Add(newSkill.Key, new Skill(newSkill.Key, newSkill.Value));
}
}
public void IncreaseSkillLevel(Identifier skillIdentifier, float increase, bool increasePastMax) public void IncreaseSkillLevel(Identifier skillIdentifier, float increase, bool increasePastMax)
{ {
if (skills.TryGetValue(skillIdentifier, out Skill skill)) if (skills.TryGetValue(skillIdentifier, out Skill skill))
@@ -171,7 +190,7 @@ namespace Barotrauma
character.Inventory.TryPutItem(item, null, item.AllowedSlots); character.Inventory.TryPutItem(item, null, item.AllowedSlots);
} }
Wearable wearable = ((List<ItemComponent>)item.Components)?.Find(c => c is Wearable) as Wearable; Wearable wearable = item.GetComponent<Wearable>();
if (wearable != null) if (wearable != null)
{ {
if (Variant > 0 && Variant <= wearable.Variants) if (Variant > 0 && Variant <= wearable.Variants)
@@ -293,6 +293,10 @@ namespace Barotrauma
{ {
return Create<HumanSwimFastParams>(fullPath, speciesName, animationType); return Create<HumanSwimFastParams>(fullPath, speciesName, animationType);
} }
if (type == typeof(HumanCrouchParams))
{
return Create<HumanCrouchParams>(fullPath, speciesName, animationType);
}
if (type == typeof(FishWalkParams)) if (type == typeof(FishWalkParams))
{ {
return Create<FishWalkParams>(fullPath, speciesName, animationType); return Create<FishWalkParams>(fullPath, speciesName, animationType);
@@ -47,7 +47,7 @@ namespace Barotrauma.Abilities
{ {
if (skillIdentifier == "random") if (skillIdentifier == "random")
{ {
var skill = character.Info?.Job?.Skills?.GetRandomUnsynced(); var skill = character.Info?.Job?.GetSkills()?.GetRandomUnsynced();
if (skill == null) { return; } if (skill == null) { return; }
character.Info?.IncreaseSkillLevel(skill.Identifier, skillIncrease, gainedFromAbility: true); character.Info?.IncreaseSkillLevel(skill.Identifier, skillIncrease, gainedFromAbility: true);
} }
@@ -30,11 +30,12 @@ namespace Barotrauma.Abilities
if (useAll && Character.Info?.Job != null) if (useAll && Character.Info?.Job != null)
{ {
foreach (Skill skill in Character.Info.Job.Skills) var skills = Character.Info.Job.GetSkills();
foreach (Skill skill in skills)
{ {
skillTotal += Character.GetSkillLevel(skill.Identifier); skillTotal += Character.GetSkillLevel(skill.Identifier);
} }
skillTotal /= Character.Info.Job.Skills.Count; skillTotal /= skills.Count();
} }
else else
{ {
@@ -824,7 +824,7 @@ namespace Barotrauma
if (isMax) { level = 100; } if (isMax) { level = 100; }
if (skillIdentifier == "all") if (skillIdentifier == "all")
{ {
foreach (Skill skill in character.Info.Job.Skills) foreach (Skill skill in character.Info.Job.GetSkills())
{ {
character.Info.SetSkillLevel(skill.Identifier, level); character.Info.SetSkillLevel(skill.Identifier, level);
} }
@@ -844,7 +844,7 @@ namespace Barotrauma
{ {
return new[] return new[]
{ {
Character.Controlled?.Info?.Job?.Skills?.Select(skill => skill.Identifier.Value).ToArray() ?? Array.Empty<string>(), Character.Controlled?.Info?.Job?.GetSkills()?.Select(skill => skill.Identifier.Value).ToArray() ?? Array.Empty<string>(),
new[]{ "max" }, new[]{ "max" },
Character.CharacterList.Select(c => c.Name).Distinct().OrderBy(n => n).ToArray(), Character.CharacterList.Select(c => c.Name).Distinct().OrderBy(n => n).ToArray(),
}; };
@@ -316,7 +316,7 @@ namespace Barotrauma
} }
// Exchange money // Exchange money
int itemValue = item.Quantity * buyValues[item.ItemPrefab]; int itemValue = item.Quantity * buyValues[item.ItemPrefab];
campaign.GetWallet(client).TryDeduct(itemValue); campaign.TryPurchase(client, itemValue);
GameAnalyticsManager.AddMoneySpentEvent(itemValue, GameAnalyticsManager.MoneySink.Store, item.ItemPrefab.Identifier.Value); GameAnalyticsManager.AddMoneySpentEvent(itemValue, GameAnalyticsManager.MoneySink.Store, item.ItemPrefab.Identifier.Value);
store.Balance += itemValue; store.Balance += itemValue;
if (removeFromCrate) if (removeFromCrate)
@@ -227,6 +227,21 @@ namespace Barotrauma
return Bank; return Bank;
} }
public virtual bool TryPurchase(Client client, int price)
{
return GetWallet(client).TryDeduct(price);
}
public virtual int GetBalance(Client client = null)
{
return GetWallet(client).Balance;
}
public bool CanAfford(int cost, Client client = null)
{
return GetBalance(client) >= cost;
}
/// <summary> /// <summary>
/// The location that's displayed as the "current one" in the map screen. Normally the current outpost or the location at the start of the level, /// The location that's displayed as the "current one" in the map screen. Normally the current outpost or the location at the start of the level,
/// but when selecting the next destination at the end of the level at an uninhabited location we use the location at the end /// but when selecting the next destination at the end of the level at an uninhabited location we use the location at the end
@@ -766,7 +781,7 @@ namespace Barotrauma
public bool TryHireCharacter(Location location, CharacterInfo characterInfo, Client client = null) public bool TryHireCharacter(Location location, CharacterInfo characterInfo, Client client = null)
{ {
if (characterInfo == null) { return false; } if (characterInfo == null) { return false; }
if (!GetWallet(client).TryDeduct(characterInfo.Salary)) { return false; } if (!TryPurchase(client, characterInfo.Salary)) { return false; }
characterInfo.IsNewHire = true; characterInfo.IsNewHire = true;
location.RemoveHireableCharacter(characterInfo); location.RemoveHireableCharacter(characterInfo);
CrewManager.AddCharacterInfo(characterInfo); CrewManager.AddCharacterInfo(characterInfo);
@@ -292,7 +292,7 @@ namespace Barotrauma
if ((GameMain.NetworkMember is null || GameMain.NetworkMember is { IsServer: true }) && cost > 0) if ((GameMain.NetworkMember is null || GameMain.NetworkMember is { IsServer: true }) && cost > 0)
{ {
Campaign!.GetWallet(client).TryDeduct(cost); Campaign!.TryPurchase(client, cost);
} }
GameAnalyticsManager.AddMoneySpentEvent(cost, GameAnalyticsManager.MoneySink.SubmarineSwitch, newSubmarine.Name); GameAnalyticsManager.AddMoneySpentEvent(cost, GameAnalyticsManager.MoneySink.SubmarineSwitch, newSubmarine.Name);
Campaign!.PendingSubmarineSwitch = newSubmarine; Campaign!.PendingSubmarineSwitch = newSubmarine;
@@ -303,7 +303,7 @@ namespace Barotrauma
public void PurchaseSubmarine(SubmarineInfo newSubmarine, Client? client = null) public void PurchaseSubmarine(SubmarineInfo newSubmarine, Client? client = null)
{ {
if (Campaign is null) { return; } if (Campaign is null) { return; }
if ((GameMain.NetworkMember is null || GameMain.NetworkMember is { IsServer: true }) && !Campaign.GetWallet(client).TryDeduct(newSubmarine.Price)) { return; } if ((GameMain.NetworkMember is null || GameMain.NetworkMember is { IsServer: true }) && !Campaign.TryPurchase(client, newSubmarine.Price)) { return; }
if (!OwnedSubmarines.Any(s => s.Name == newSubmarine.Name)) if (!OwnedSubmarines.Any(s => s.Name == newSubmarine.Name))
{ {
GameAnalyticsManager.AddMoneySpentEvent(newSubmarine.Price, GameAnalyticsManager.MoneySink.SubmarinePurchase, newSubmarine.Name); GameAnalyticsManager.AddMoneySpentEvent(newSubmarine.Price, GameAnalyticsManager.MoneySink.SubmarinePurchase, newSubmarine.Name);
@@ -213,7 +213,7 @@ namespace Barotrauma
if (!force) if (!force)
{ {
if (IsOutpostInCombat()) { return HealRequestResult.Refused; } if (IsOutpostInCombat()) { return HealRequestResult.Refused; }
if (!GetWallet(client).TryDeduct(totalCost)) { return HealRequestResult.InsufficientFunds; } if (!(campaign?.TryPurchase(client, totalCost) ?? false)) { return HealRequestResult.InsufficientFunds; }
} }
ImmutableArray<CharacterInfo> crew = GetCrewCharacters(); ImmutableArray<CharacterInfo> crew = GetCrewCharacters();
@@ -314,10 +314,7 @@ namespace Barotrauma
private int GetAdjustedPrice(int price) => campaign?.Map?.CurrentLocation is { Type: { HasOutpost: true } } currentLocation ? currentLocation.GetAdjustedHealCost(price) : int.MaxValue; private int GetAdjustedPrice(int price) => campaign?.Map?.CurrentLocation is { Type: { HasOutpost: true } } currentLocation ? currentLocation.GetAdjustedHealCost(price) : int.MaxValue;
public Wallet GetWallet(Client? c = null) public int GetBalance() => campaign?.GetBalance() ?? 0;
{
return campaign?.GetWallet(c) ?? Wallet.Invalid;
}
public static ImmutableArray<CharacterInfo> GetCrewCharacters() public static ImmutableArray<CharacterInfo> GetCrewCharacters()
{ {
@@ -216,7 +216,7 @@ namespace Barotrauma
price = 0; price = 0;
} }
if (Campaign.GetWallet(client).TryDeduct(price)) if (Campaign.TryPurchase(client, price))
{ {
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
{ {
@@ -313,7 +313,7 @@ namespace Barotrauma
price = 0; price = 0;
} }
if (Campaign.GetWallet(client).TryDeduct(price)) if (Campaign.TryPurchase(client, price))
{ {
PurchasedItemSwaps.RemoveAll(p => linkedItems.Contains(p.ItemToRemove)); PurchasedItemSwaps.RemoveAll(p => linkedItems.Contains(p.ItemToRemove));
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
@@ -3,7 +3,6 @@ using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
@@ -18,6 +17,8 @@ namespace Barotrauma.Items.Components
const int MaxNodes = 100; const int MaxNodes = 100;
const float MaxNodeDistance = 150.0f; const float MaxNodeDistance = 150.0f;
private bool waitForVoltageRecalculation;
public struct Node public struct Node
{ {
public Vector2 WorldPosition; public Vector2 WorldPosition;
@@ -120,6 +121,7 @@ namespace Barotrauma.Items.Components
CurrPowerConsumption = powerConsumption; CurrPowerConsumption = powerConsumption;
Voltage = 0.0f; Voltage = 0.0f;
waitForVoltageRecalculation = true;
charging = true; charging = true;
timer = Duration; timer = Duration;
IsActive = true; IsActive = true;
@@ -134,6 +136,12 @@ namespace Barotrauma.Items.Components
#if CLIENT #if CLIENT
frameOffset = Rand.Int(electricitySprite.FrameCount); frameOffset = Rand.Int(electricitySprite.FrameCount);
#endif #endif
if (waitForVoltageRecalculation)
{
waitForVoltageRecalculation = false;
return;
}
if (timer <= 0.0f) if (timer <= 0.0f)
{ {
IsActive = false; IsActive = false;
@@ -98,6 +98,7 @@ namespace Barotrauma.Items.Components
OwnerName = info.Name; OwnerName = info.Name;
OwnerJobId = info.Job?.Prefab.Identifier ?? Identifier.Empty; OwnerJobId = info.Job?.Prefab.Identifier ?? Identifier.Empty;
item.AddTag($"jobid:{OwnerJobId}");
OwnerTagSet = info.Head.Preset.TagSet; OwnerTagSet = info.Head.Preset.TagSet;
OwnerHairIndex = head.HairIndex; OwnerHairIndex = head.HairIndex;
OwnerBeardIndex = head.BeardIndex; OwnerBeardIndex = head.BeardIndex;
@@ -315,6 +315,15 @@ namespace Barotrauma.Items.Components
} }
} }
private Client GetUsingClient()
{
#if SERVER
return GameMain.Server.ConnectedClients.Find(c => c.Character == user);
#elif CLIENT
return null;
#endif
}
private void Fabricate() private void Fabricate()
{ {
RefreshAvailableIngredients(); RefreshAvailableIngredients();
@@ -327,9 +336,20 @@ namespace Barotrauma.Items.Components
if (fabricatedItem.RequiredMoney > 0) if (fabricatedItem.RequiredMoney > 0)
{ {
if (user == null) { return; } if (user == null) { return; }
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign) if (GameMain.GameSession?.GameMode is MultiPlayerCampaign mpCampaign)
{ {
user.Wallet.Deduct(fabricatedItem.RequiredMoney); #if CLIENT
mpCampaign.TryPurchase(null, fabricatedItem.RequiredMoney);
#elif SERVER
if (GetUsingClient() is { } client)
{
mpCampaign.TryPurchase(client, fabricatedItem.RequiredMoney);
}
else
{
user.Wallet.Deduct(fabricatedItem.RequiredMoney);
}
#endif
} }
else if (GameMain.GameSession?.GameMode is CampaignMode campaign) else if (GameMain.GameSession?.GameMode is CampaignMode campaign)
{ {
@@ -530,6 +550,10 @@ namespace Barotrauma.Items.Components
{ {
floatQuality += user.Info.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, tag); floatQuality += user.Info.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, tag);
} }
if (!fabricatedItem.TargetItem.Tags.Contains(fabricatedItem.TargetItem.Identifier))
{
floatQuality += user.Info.GetSavedStatValue(StatTypes.IncreaseFabricationQuality, fabricatedItem.TargetItem.Identifier);
}
quality = (int)floatQuality; quality = (int)floatQuality;
const int MaxCraftingSkill = 100; const int MaxCraftingSkill = 100;
@@ -548,17 +572,22 @@ namespace Barotrauma.Items.Components
if (fabricableItem.RequiredMoney > 0) if (fabricableItem.RequiredMoney > 0)
{ {
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign) switch (GameMain.GameSession?.GameMode)
{ {
if (character?.Wallet == null || character.Wallet.Balance < fabricableItem.RequiredMoney) { return false; } case MultiPlayerCampaign mpCampaign:
} {
else if (GameMain.GameSession?.GameMode is CampaignMode campaign) if (!mpCampaign.CanAfford(fabricableItem.RequiredMoney, GetUsingClient())) { return false; }
{
if (campaign.Bank.Balance < fabricableItem.RequiredMoney) { return false; } break;
} }
else case CampaignMode campaign:
{ {
return false; if (campaign.Bank.Balance < fabricableItem.RequiredMoney) { return false; }
break;
}
default:
return false;
} }
} }
@@ -565,16 +565,20 @@ namespace Barotrauma.Items.Components
//Iterate through all connections in the group to get their minmax power and sum them //Iterate through all connections in the group to get their minmax power and sum them
foreach (Connection c in scrGroup.Connections) foreach (Connection c in scrGroup.Connections)
{ {
Powered device = c.Item.GetComponent<Powered>(); foreach (var device in c.Item.GetComponents<Powered>())
scrGroup.MinMaxPower += device.MinMaxPowerOut(c, grid.Load); {
scrGroup.MinMaxPower += device.MinMaxPowerOut(c, grid.Load);
}
} }
//Iterate through all connections to get their final power out provided the min max information //Iterate through all connections to get their final power out provided the min max information
float addedPower = 0; float addedPower = 0;
foreach (Connection c in scrGroup.Connections) foreach (Connection c in scrGroup.Connections)
{ {
Powered device = c.Item.GetComponent<Powered>(); foreach (var device in c.Item.GetComponents<Powered>())
addedPower += device.GetConnectionPowerOut(c, grid.Power, scrGroup.MinMaxPower, grid.Load); {
addedPower += device.GetConnectionPowerOut(c, grid.Power, scrGroup.MinMaxPower, grid.Load);
}
} }
//Add the power to the grid //Add the power to the grid
@@ -591,10 +595,12 @@ namespace Barotrauma.Items.Components
grid.Voltage = newVoltage; grid.Voltage = newVoltage;
//Iterate through all connections on that grid and run their gridResolved function //Iterate through all connections on that grid and run their gridResolved function
foreach (Connection con in grid.Connections) foreach (Connection c in grid.Connections)
{ {
Powered device = con.Item.GetComponent<Powered>(); foreach (var device in c.Item.GetComponents<Powered>())
device?.GridResolved(con); {
device?.GridResolved(c);
}
} }
} }
@@ -760,6 +760,9 @@ namespace Barotrauma
{ {
if (slots[j].Contains(item)) { visualSlots[j].ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.9f); } if (slots[j].Contains(item)) { visualSlots[j].ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.9f); }
} }
}
if (otherInventory.visualSlots != null)
{
for (int j = 0; j < otherInventory.capacity; j++) for (int j = 0; j < otherInventory.capacity; j++)
{ {
if (otherInventory.slots[j].Contains(existingItems.FirstOrDefault())) { otherInventory.visualSlots[j].ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.9f); } if (otherInventory.slots[j].Contains(existingItems.FirstOrDefault())) { otherInventory.visualSlots[j].ShowBorderHighlight(GUIStyle.Green, 0.1f, 0.9f); }
@@ -43,7 +43,7 @@ namespace Barotrauma
{ {
Config config = new Config Config config = new Config
{ {
Language = LanguageIdentifier.None, Language = TextManager.DefaultLanguage,
SubEditorUndoBuffer = 32, SubEditorUndoBuffer = 32,
MaxAutoSaves = 8, MaxAutoSaves = 8,
AutoSaveIntervalSeconds = 300, AutoSaveIntervalSeconds = 300,
@@ -99,6 +99,10 @@ namespace Barotrauma
Config retVal = fallback ?? GetDefault(); Config retVal = fallback ?? GetDefault();
retVal.DeserializeElement(element); retVal.DeserializeElement(element);
if (retVal.Language == LanguageIdentifier.None)
{
retVal.Language = TextManager.DefaultLanguage;
}
retVal.Graphics = GraphicsSettings.FromElements(element.GetChildElements("graphicsmode", "graphicssettings"), retVal.Graphics); retVal.Graphics = GraphicsSettings.FromElements(element.GetChildElements("graphicsmode", "graphicssettings"), retVal.Graphics);
retVal.Audio = AudioSettings.FromElements(element.GetChildElements("audio"), retVal.Audio); retVal.Audio = AudioSettings.FromElements(element.GetChildElements("audio"), retVal.Audio);
@@ -1454,7 +1454,7 @@ namespace Barotrauma
Identifier GetRandomSkill() Identifier GetRandomSkill()
{ {
return targetCharacter.Info?.Job?.Skills.Select(s => s.Identifier).GetRandomUnsynced() ?? Identifier.Empty; return targetCharacter.Info?.Job?.GetSkills().GetRandomUnsynced()?.Identifier ?? Identifier.Empty;
} }
} }
} }
@@ -250,12 +250,15 @@ namespace Barotrauma.Steam
public static void DeleteFailedCopies() public static void DeleteFailedCopies()
{ {
foreach (var dir in Directory.EnumerateDirectories(ContentPackage.WorkshopModsDir, "**")) if (Directory.Exists(ContentPackage.WorkshopModsDir))
{ {
string copyingIndicatorPath = Path.Combine(dir, ContentPackageManager.CopyIndicatorFileName); foreach (var dir in Directory.EnumerateDirectories(ContentPackage.WorkshopModsDir, "**"))
if (File.Exists(copyingIndicatorPath))
{ {
Directory.Delete(dir, recursive: true); string copyingIndicatorPath = Path.Combine(dir, ContentPackageManager.CopyIndicatorFileName);
if (File.Exists(copyingIndicatorPath))
{
Directory.Delete(dir, recursive: true);
}
} }
} }
} }
+35
View File
@@ -1,3 +1,38 @@
---------------------------------------------------------------------------------------------------------
v0.17.13.0
---------------------------------------------------------------------------------------------------------
Changes:
- Players who are allowed to manage money in the multiplayer campaign can use money directly from the bank without having to transfer it to their wallet first.
- Managing money always requires permissions: unlike other campaign-related permissions, not having anyone with permissions on the server doesn't give everyone permissions.
Fixes:
- Fixed deconstructors, fabricators and research stations not being powered in some colony modules.
- Fixed power connections not getting recalculated server-side when disconnecting and reconnecting a wire.
- Fixed client context menu not working in the tab menu if the client's not controlling a character.
- Fixed preview image disappearing when saving a sub.
- Fixed custom jobid tags not working on ID cards.
- Fixed crashing with the error "Coroutine Barotrauma.SinglePlayerCampaign+<DoLoadInitialLevel>d__16 threw an exception" when trying to give items to a human prefab instance that has no item sets configured.
- Fixed installed mods list not refreshing when uninstalling an unsubscribed mod.
- Fixed outpost reactors using mechanical skill for repairs instead of electrical.
- Fixed outdated Dugong preview image.
- Fixed Deadeye Carbine firing an inconsistent number of rounds per burst in multiplayer.
- Fixed crashing when launching the server with a fresh config file due to the language being set to None.
- Fixed crashing on startup if the workshop mod directory doesn't exist.
- Fixed "Canned Heat" not having an effect on oxygenite tanks.
- Fixed entity list's search results being in a random order in the sub editor.
- Fixed crashing when trying to view an item assembly that contains entities that can't be found in the sub editor.
- Fixed bots not taking medical items' negative effects into account when determining which meds to use, often leading to overdoses/suffocation when using opiates.
- Fixed bots trying to clean up items into deconstructors.
- Fixed clients failing to spawn items with console commands when there's a structure prefab with the same identifier (e.g. ladders).
- Fixed characters being unable to gain skills added by a mod if the job doesn't initially have those skills defined.
- Automatically correct linked submarine paths in the submarine editor.
- Fixed electrical discharge coils sometimes working with insufficient power.
- Fixed crashing when creating a humanoid character in character editor.
- Fixed cargo missions putting cargo in non-interactable and hidden-in-game containers.
- Fixed only the first Powered component being considered when determining how much power an item is supplying to the grid. Prevented alien generators from working.
- Fixed textbox's text position breaking when there's overflow and you're editing in the middle of the string.
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------
v0.17.12.0 v0.17.12.0
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------