v0.12.0.2

This commit is contained in:
Joonas Rikkonen
2021-02-10 17:08:21 +02:00
parent 5c80a59bdd
commit 694cdfee7b
353 changed files with 12897 additions and 5028 deletions
@@ -287,6 +287,7 @@ namespace Barotrauma
{
lock (mutex)
{
usedIndicatorAngles.Clear();
if (ScreenChanged)
{
@@ -1117,15 +1118,26 @@ namespace Barotrauma
/// Set the cursor to an hourglass.
/// Will automatically revert after 10 seconds or when <see cref="ClearCursorWait"/> is called.
/// </summary>
public static void SetCursorWaiting()
public static void SetCursorWaiting(int waitSeconds = 10, Func<bool> endCondition = null)
{
CoroutineManager.StartCoroutine(WaitCursorCoroutine(), "WaitCursorTimeout");
static IEnumerable<object> WaitCursorCoroutine()
IEnumerable<object> WaitCursorCoroutine()
{
MouseCursor = CursorState.Waiting;
var timeOut = DateTime.Now + new TimeSpan(0, 0, 10);
while (DateTime.Now < timeOut) { yield return CoroutineStatus.Running; }
var timeOut = DateTime.Now + new TimeSpan(0, 0, waitSeconds);
while (DateTime.Now < timeOut)
{
if (endCondition != null)
{
try
{
if (endCondition.Invoke()) { break; }
}
catch { break; }
}
yield return CoroutineStatus.Running;
}
if (MouseCursor == CursorState.Waiting) { MouseCursor = CursorState.Default; }
yield return CoroutineStatus.Success;
}
@@ -1219,7 +1231,7 @@ namespace Barotrauma
{
foreach (GUIMessage msg in messages)
{
if (msg.WorldSpace) continue;
if (msg.WorldSpace) { continue; }
msg.Timer -= deltaTime;
if (msg.Size.X > HUDLayoutSettings.MessageAreaTop.Width)
@@ -1244,7 +1256,7 @@ namespace Barotrauma
foreach (GUIMessage msg in messages)
{
if (!msg.WorldSpace) continue;
if (!msg.WorldSpace) { continue; }
msg.Timer -= deltaTime;
msg.Pos += msg.Velocity * deltaTime;
}
@@ -1256,18 +1268,21 @@ namespace Barotrauma
#region Element drawing
private static List<float> usedIndicatorAngles = new List<float>();
/// <param name="createOffset">Should the indicator move based on the camera position?</param>
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color, bool createOffset = true, float scaleMultiplier = 1.0f)
/// <param name="overrideAlpha">Override the distance-based alpha value with the specified alpha value</param>
public static void DrawIndicator(SpriteBatch spriteBatch, Vector2 worldPosition, Camera cam, float hideDist, Sprite sprite, Color color,
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null)
{
Vector2 diff = worldPosition - cam.WorldViewCenter;
float dist = diff.Length();
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier;
if (dist > hideDist)
if (overrideAlpha.HasValue || dist > hideDist)
{
float alpha = Math.Min((dist - hideDist) / 100.0f, 1.0f);
float alpha = overrideAlpha ?? Math.Min((dist - hideDist) / 100.0f, 1.0f);
Vector2 targetScreenPos = cam.WorldToScreen(worldPosition);
if (!createOffset)
@@ -1279,6 +1294,28 @@ namespace Barotrauma
float screenDist = Vector2.Distance(cam.WorldToScreen(cam.WorldViewCenter), targetScreenPos);
float angle = MathUtils.VectorToAngle(diff);
float minAngleDiff = 0.05f;
bool overlapFound = true;
int iterations = 0;
while (overlapFound && iterations < 10)
{
overlapFound = false;
foreach (float usedIndicatorAngle in usedIndicatorAngles)
{
float shortestAngle = MathUtils.GetShortestAngle(angle, usedIndicatorAngle);
if (MathUtils.NearlyEqual(shortestAngle, 0.0f)) { shortestAngle = 0.01f; }
if (Math.Abs(shortestAngle) < minAngleDiff)
{
angle -= Math.Sign(shortestAngle) * (minAngleDiff - Math.Abs(shortestAngle));
overlapFound = true;
break;
}
}
iterations++;
}
usedIndicatorAngles.Add(angle);
Vector2 unclampedDiff = new Vector2(
(float)Math.Cos(angle) * screenDist,
(float)-Math.Sin(angle) * screenDist);
@@ -1489,12 +1526,12 @@ namespace Barotrauma
foreach (GUIMessage msg in messages)
{
if (msg.WorldSpace) continue;
if (msg.WorldSpace) { continue; }
Vector2 drawPos = new Vector2(HUDLayoutSettings.MessageAreaTop.Right, HUDLayoutSettings.MessageAreaTop.Center.Y);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.Pos + Vector2.One, Color.Black, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.Pos, msg.Color, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.DrawPos + Vector2.One, Color.Black, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + msg.DrawPos, msg.Color, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
break;
}
@@ -1507,14 +1544,14 @@ namespace Barotrauma
foreach (GUIMessage msg in messages)
{
if (!msg.WorldSpace) continue;
if (!msg.WorldSpace) { continue; }
if (cam != null)
{
float alpha = 1.0f;
if (msg.Timer < 1.0f) alpha -= 1.0f - msg.Timer;
if (msg.Timer < 1.0f) { alpha -= 1.0f - msg.Timer; }
Vector2 drawPos = cam.WorldToScreen(msg.Pos);
Vector2 drawPos = cam.WorldToScreen(msg.DrawPos);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos + Vector2.One, Color.Black * alpha, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
msg.Font.DrawString(spriteBatch, msg.Text, drawPos, msg.Color * alpha, 0, msg.Origin, 1.0f, SpriteEffects.None, 0);
}
@@ -1608,7 +1645,8 @@ namespace Barotrauma
public static Texture2D CreateCapsule(int radius, int height)
{
int textureWidth = radius * 2, textureHeight = height + radius * 2;
int textureWidth = Math.Max(radius * 2, 1);
int textureHeight = Math.Max(height + radius * 2, 1);
Color[] data = new Color[textureWidth * textureHeight];
@@ -2079,7 +2117,7 @@ namespace Barotrauma
if (pauseMenuOpen)
{
Inventory.draggingItem = null;
Inventory.DraggingItems.Clear();
Inventory.DraggingInventory = null;
PauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas, Anchor.Center), style: null);
@@ -2288,9 +2326,10 @@ namespace Barotrauma
if (playSound) SoundPlayer.PlayUISound(GUISoundType.UIMessage);
}
public static void AddMessage(string message, Color color, Vector2 worldPos, Vector2 velocity, float lifeTime = 3.0f, bool playSound = true, GUISoundType soundType = GUISoundType.UIMessage)
public static void AddMessage(string message, Color color, Vector2 pos, Vector2 velocity, float lifeTime = 3.0f, bool playSound = true, GUISoundType soundType = GUISoundType.UIMessage, int subId = -1)
{
messages.Add(new GUIMessage(message, color, worldPos, velocity, lifeTime, Alignment.Center, LargeFont));
Submarine sub = Submarine.Loaded.FirstOrDefault(s => s.ID == subId);
messages.Add(new GUIMessage(message, color, pos, velocity, lifeTime, Alignment.Center, LargeFont, sub: sub));
if (playSound) SoundPlayer.PlayUISound(soundType);
}
@@ -0,0 +1,210 @@
#nullable enable
using System;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
namespace Barotrauma
{
public class GUIColorPicker : GUIComponent
{
public delegate bool OnColorSelectedHandler(GUIColorPicker component, Color color);
public OnColorSelectedHandler? OnColorSelected;
public float SelectedHue;
public float SelectedSaturation;
public float SelectedValue;
public Color CurrentColor = Color.Black;
private Rectangle MainArea,
HueArea;
private Texture2D? mainTexture,
hueTexture;
private Color[]? colorData;
private Rectangle selectedRect;
private bool mouseHeld;
private bool isInitialized;
private readonly Color transparentWhite = Color.White * 0.8f,
transparentBlack = Color.Black * 0.8f;
public GUIColorPicker(RectTransform rectT, string? style = null) : base(style, rectT) { }
~GUIColorPicker()
{
DisposeTextures();
}
private void Init()
{
int tWidth = Rect.Width;
int sliceWidth = Rect.Width / 8;
int mainWidth = tWidth - sliceWidth;
int hueWidth = sliceWidth;
MainArea = new Rectangle(0, 0, mainWidth, Rect.Height);
HueArea = new Rectangle(mainWidth, 0, hueWidth, Rect.Height);
colorData = new Color[MainArea.Width * MainArea.Height];
if (mainTexture == null)
{
int width = MainArea.Width,
height = MainArea.Height;
GenerateGradient(ref colorData!, width, height, DrawHVArea);
mainTexture = CreateGradientTexture(colorData!, MainArea.Width, MainArea.Height);
}
if (hueTexture == null)
{
int width = HueArea.Width,
height = HueArea.Height;
Color[] hueData = new Color[width * height];
GenerateGradient(ref hueData, width, height, DrawHueArea);
hueTexture = CreateGradientTexture(hueData, width, height);
}
}
protected override void Draw(SpriteBatch spriteBatch)
{
if (mainTexture == null || hueTexture == null || !isInitialized) { return; }
Rectangle mainArea = MainArea,
hueArea = HueArea;
hueArea.Location += Rect.Location;
mainArea.Location += Rect.Location;
Vector2 mainLocation = mainArea.Location.ToVector2(),
hueLocation = hueArea.Location.ToVector2();
spriteBatch.Draw(mainTexture, mainLocation, Color.White);
spriteBatch.Draw(hueTexture, hueLocation, Color.White);
float hueY = hueLocation.Y + ((SelectedHue / 360f) * hueArea.Height);
spriteBatch.DrawLine(hueArea.Left, hueY, hueArea.Right, hueY, transparentWhite, thickness: 3);
spriteBatch.DrawLine(hueArea.Left, hueY, hueArea.Right, hueY, transparentBlack, thickness: 1);
float saturationX = mainLocation.X + SelectedSaturation * MainArea.Width;
float valueY = mainLocation.Y + (1.0f - SelectedValue) * MainArea.Height;
spriteBatch.DrawLine(saturationX, mainArea.Top,saturationX, mainArea.Bottom, transparentWhite, thickness: 3);
spriteBatch.DrawLine(mainArea.Left,valueY, mainArea.Right, valueY, transparentWhite, thickness: 3);
spriteBatch.DrawLine(saturationX, mainArea.Top,saturationX, mainArea.Bottom, transparentBlack, thickness: 1);
spriteBatch.DrawLine(mainArea.Left,valueY, mainArea.Right, valueY, transparentBlack, thickness: 1);
}
protected override void Update(float deltaTime)
{
base.Update(deltaTime);
if (!isInitialized)
{
Init();
isInitialized = true;
}
if (!PlayerInput.PrimaryMouseButtonHeld())
{
mouseHeld = false;
}
if (GUI.MouseOn != this) { return; }
Rectangle mainArea = MainArea,
hueArea = HueArea;
hueArea.Location += Rect.Location;
mainArea.Location += Rect.Location;
if (PlayerInput.PrimaryMouseButtonDown())
{
mouseHeld = true;
if (hueArea.Contains(PlayerInput.MousePosition))
{
selectedRect = HueArea;
}
else if (mainArea.Contains(PlayerInput.MousePosition))
{
selectedRect = MainArea;
}
else
{
mouseHeld = false;
}
}
if (!PlayerInput.PrimaryMouseButtonHeld())
{
mouseHeld = false;
}
if (mouseHeld && (PlayerInput.MouseSpeed != Vector2.Zero || PlayerInput.PrimaryMouseButtonDown()))
{
if (selectedRect == HueArea)
{
Vector2 pos = PlayerInput.MousePosition - hueArea.Location.ToVector2();
SelectedHue = Math.Clamp(pos.Y / hueArea.Height * 360f, 0, 360);
RefreshHue();
}
else if (selectedRect == MainArea)
{
var (x, y) = PlayerInput.MousePosition - mainArea.Location.ToVector2();
SelectedSaturation = Math.Clamp(x / mainArea.Width, 0, 1);
SelectedValue = Math.Clamp(1f - (y / mainArea.Height), 0, 1);
}
CurrentColor = ToolBox.HSVToRGB(SelectedHue, SelectedSaturation, SelectedValue);
OnColorSelected?.Invoke(this, CurrentColor);
}
}
public void DisposeTextures()
{
mainTexture?.Dispose();
hueTexture?.Dispose();
}
public void RefreshHue()
{
if (colorData == null || mainTexture == null) { return; }
GenerateGradient(ref colorData, mainTexture.Width, mainTexture.Height, DrawHVArea);
mainTexture.SetData(colorData);
}
private Texture2D CreateGradientTexture(Color[] data, int width, int height)
{
Texture2D texture = new Texture2D(GameMain.GraphicsDeviceManager.GraphicsDevice, width, height);
texture.SetData(data);
return texture;
}
private void GenerateGradient(ref Color[] data, int width, int height, Func<float, float, Color> algorithm)
{
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
float relativeX = x / (float) width,
relativeY = y / (float) height;
data[y * width + x] = algorithm(relativeX, relativeY);
}
}
}
private Color DrawHVArea(float x, float y) => ToolBox.HSVToRGB(SelectedHue, x, 1.0f - y);
private Color DrawHueArea(float x, float y) => ToolBox.HSVToRGB(y * 360f, 1f, 1f);
}
}
@@ -510,10 +510,10 @@ namespace Barotrauma
for (int i = 0; i < Content.CountChildren; i++)
{
var child = Content.RectTransform.GetChild(i)?.GUIComponent;
if (child == null) continue;
if (child == null || !child.Visible) { continue; }
// selecting
if (Enabled && CanBeFocused && child.CanBeFocused && (GUI.IsMouseOn(child)) && child.Rect.Contains(PlayerInput.MousePosition))
if (Enabled && CanBeFocused && child.CanBeFocused && child.Rect.Contains(PlayerInput.MousePosition) && GUI.IsMouseOn(child))
{
child.State = ComponentState.Hover;
@@ -55,6 +55,20 @@ namespace Barotrauma
private set;
}
public Submarine Submarine
{
get;
private set;
}
public Vector2 DrawPos
{
get
{
return Submarine == null ? Pos : Pos + Submarine.DrawPosition;
}
}
public GUIMessage(string text, Color color, float lifeTime, ScalableFont font = null)
{
coloredText = new ColoredText(text, color, false, false);
@@ -67,11 +81,11 @@ namespace Barotrauma
Font = font;
}
public GUIMessage(string text, Color color, Vector2 worldPosition, Vector2 velocity, float lifeTime, Alignment textAlignment = Alignment.Center, ScalableFont font = null)
public GUIMessage(string text, Color color, Vector2 position, Vector2 velocity, float lifeTime, Alignment textAlignment = Alignment.Center, ScalableFont font = null, Submarine sub = null)
{
coloredText = new ColoredText(text, color, false, false);
WorldSpace = true;
pos = worldPosition;
pos = position;
Timer = lifeTime;
Velocity = velocity;
this.lifeTime = lifeTime;
@@ -92,6 +106,8 @@ namespace Barotrauma
if (textAlignment.HasFlag(Alignment.Bottom))
Origin.Y += size.Y * 0.5f;
Submarine = sub;
}
}
}
@@ -67,6 +67,8 @@ namespace Barotrauma
private readonly Type type;
public Type MessageBoxType => type;
public static GUIComponent VisibleBox => MessageBoxes.LastOrDefault();
public GUIMessageBox(string headerText, string text, Vector2? relativeSize = null, Point? minSize = null)
@@ -210,6 +212,29 @@ namespace Barotrauma
}
};
InputType? closeInput = null;
if (GameMain.Config.KeyBind(InputType.Use).MouseButton == MouseButton.None)
{
closeInput = InputType.Use;
}
else if (GameMain.Config.KeyBind(InputType.Select).MouseButton == MouseButton.None)
{
closeInput = InputType.Select;
}
if (closeInput.HasValue)
{
Buttons[0].ToolTip = TextManager.ParseInputTypes($"{TextManager.Get("Close")} ([InputType.{closeInput.Value}])");
Buttons[0].OnAddedToGUIUpdateList += (GUIComponent component) =>
{
if (!closing && openState >= 1.0f && PlayerInput.KeyHit(closeInput.Value))
{
GUIButton btn = component as GUIButton;
btn?.OnClicked(btn, btn.UserData);
btn?.Flash(GUI.Style.Green);
}
};
}
Header = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), Content.RectTransform), headerText, wrap: true);
GUI.Style.Apply(Header, "", this);
Header.RectTransform.MinSize = new Point(0, Header.Rect.Height);
@@ -291,7 +316,8 @@ namespace Barotrauma
{
if (Draggable)
{
if ((GUI.MouseOn == InnerFrame || InnerFrame.IsParentOf(GUI.MouseOn)) && !(GUI.MouseOn is GUIButton))
GUIComponent parent = GUI.MouseOn?.Parent?.Parent;
if ((GUI.MouseOn == InnerFrame || InnerFrame.IsParentOf(GUI.MouseOn)) && !(GUI.MouseOn is GUIButton || GUI.MouseOn is GUIColorPicker || GUI.MouseOn is GUITextBox || parent is GUITextBox))
{
GUI.MouseCursor = CursorState.Move;
if (PlayerInput.PrimaryMouseButtonDown())
@@ -70,6 +70,7 @@ namespace Barotrauma
public Color ColorInventoryHalf { get; private set; } = Color.Orange;
public Color ColorInventoryFull { get; private set; } = Color.LightGreen;
public Color ColorInventoryBackground { get; private set; } = Color.Gray;
public Color ColorInventoryEmptyOverlay { get; private set; } = Color.Red;
public Color TextColor { get; private set; } = Color.White * 0.8f;
public Color TextColorBright { get; private set; } = Color.White * 0.9f;
@@ -150,6 +151,9 @@ namespace Barotrauma
case "colorinventorybackground":
ColorInventoryBackground = subElement.GetAttributeColor("color", ColorInventoryBackground);
break;
case "colorinventoryemptyoverlay":
ColorInventoryEmptyOverlay = subElement.GetAttributeColor("color", ColorInventoryEmptyOverlay);
break;
case "textcolordark":
TextColorDark = subElement.GetAttributeColor("color", TextColorDark);
break;
@@ -344,7 +348,7 @@ namespace Barotrauma
if (GameMain.Config.Language.Equals(subElement.GetAttributeString("language", ""), StringComparison.OrdinalIgnoreCase))
{
uint overrideFontSize = GetFontSize(subElement, 0);
if (overrideFontSize > 0) { return overrideFontSize; }
if (overrideFontSize > 0) { return (uint)Math.Round(overrideFontSize * GameSettings.TextScale); }
}
}
@@ -354,10 +358,10 @@ namespace Barotrauma
Point maxResolution = subElement.GetAttributePoint("maxresolution", new Point(int.MaxValue, int.MaxValue));
if (GameMain.GraphicsWidth <= maxResolution.X && GameMain.GraphicsHeight <= maxResolution.Y)
{
return (uint)subElement.GetAttributeInt("size", 14);
return (uint)Math.Round(subElement.GetAttributeInt("size", 14) * GameSettings.TextScale);
}
}
return defaultSize;
return (uint)Math.Round(defaultSize * GameSettings.TextScale);
}
private string GetFontFilePath(XElement element)
@@ -240,20 +240,20 @@ namespace Barotrauma
public class StrikethroughSettings
{
private Color color = GUI.Style.Red;
public Color Color { get; set; } = GUI.Style.Red;
private int thickness;
private int expand;
public StrikethroughSettings(Color? color = null, int thickness = 1, int expand = 0)
{
if (color != null) this.color = color.Value;
if (color != null) { Color = color.Value; }
this.thickness = thickness;
this.expand = expand;
}
public void Draw(SpriteBatch spriteBatch, float textSizeHalf, float xPos, float yPos)
{
ShapeExtensions.DrawLine(spriteBatch, new Vector2(xPos - textSizeHalf - expand, yPos), new Vector2(xPos + textSizeHalf + expand, yPos), color, thickness);
ShapeExtensions.DrawLine(spriteBatch, new Vector2(xPos - textSizeHalf - expand, yPos), new Vector2(xPos + textSizeHalf + expand, yPos), Color, thickness);
}
}
@@ -251,7 +251,7 @@ namespace Barotrauma
public bool Readonly { get; set; }
public GUITextBox(RectTransform rectT, string text = "", Color? textColor = null, ScalableFont font = null,
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool createClearButton = false)
Alignment textAlignment = Alignment.Left, bool wrap = false, string style = "", Color? color = null, bool createClearButton = false, bool createPenIcon = true)
: base(style, rectT)
{
HoverCursor = CursorState.IBeam;
@@ -283,7 +283,7 @@ namespace Barotrauma
clearButtonWidth = (int)(clearButton.Rect.Width * 1.2f);
}
if (this.style != null && this.style.ChildStyles.ContainsKey("textboxicon"))
if (this.style != null && this.style.ChildStyles.ContainsKey("textboxicon") && createPenIcon)
{
icon = new GUIImage(new RectTransform(new Vector2(0.6f, 0.6f), frame.RectTransform, Anchor.CenterRight, scaleBasis: ScaleBasis.BothHeight) { AbsoluteOffset = new Point(5 + clearButtonWidth, 0) }, null, scaleToFit: true);
icon.ApplyStyle(this.style.ChildStyles["textboxicon"]);
@@ -457,6 +457,11 @@ namespace Barotrauma
isSelecting = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift);
}
if (mouseHeldInside && !PlayerInput.PrimaryMouseButtonHeld())
{
mouseHeldInside = false;
}
if (CaretEnabled)
{
if (textBlock.OverflowClipActive)
@@ -24,9 +24,17 @@ namespace Barotrauma
private int buyTotal, sellTotal;
private GUITextBlock merchantBalanceBlock;
private GUILayoutGroup valueChangeGroup;
private GUITextBlock currentSellValueBlock, newSellValueBlock;
private GUIImage sellValueChangeArrow;
private GUIDropDown sortingDropDown;
private GUITextBox searchBox;
private GUIListBox storeDealsList, storeBuyList, storeSellList;
private GUIListBox storeBuyList, storeSellList;
/// <summary>
/// Can be null when there are no deals at the current location
/// </summary>
private GUILayoutGroup storeDailySpecialsGroup, storeRequestedGoodGroup;
private Color storeSpecialColor;
private GUIListBox shoppingCrateBuyList, shoppingCrateSellList;
private GUITextBlock shoppingCrateTotal;
@@ -49,7 +57,6 @@ namespace Barotrauma
private enum StoreTab
{
Deals,
Buy,
Sell
}
@@ -78,19 +85,19 @@ namespace Barotrauma
CurrentLocation.Reputation.OnReputationValueChanged += () => { needsRefresh = true; };
}
campaignUI.Campaign.CargoManager.OnItemsInBuyCrateChanged += () => { needsBuyingRefresh = true; };
campaignUI.Campaign.CargoManager.OnPurchasedItemsChanged += () => { needsBuyingRefresh = true; };
campaignUI.Campaign.CargoManager.OnPurchasedItemsChanged += () => { needsRefresh = true; };
campaignUI.Campaign.CargoManager.OnItemsInSellCrateChanged += () => { needsSellingRefresh = true; };
campaignUI.Campaign.CargoManager.OnSoldItemsChanged += () =>
{
needsItemsToSellRefresh = true;
needsSellingRefresh = true;
needsRefresh = true;
};
}
public void Refresh()
public void Refresh(bool updateOwned = true)
{
hadPermissions = HasPermissions;
UpdateOwnedItems();
if (updateOwned) { UpdateOwnedItems(); }
RefreshBuying(updateOwned: false);
RefreshSelling(updateOwned: false);
needsRefresh = false;
@@ -100,10 +107,8 @@ namespace Barotrauma
{
if (updateOwned) { UpdateOwnedItems(); }
RefreshShoppingCrateBuyList();
//RefreshStoreDealsList();
RefreshStoreBuyList();
var hasPermissions = HasPermissions;
//storeDealsList.Enabled = hasPermissions;
storeBuyList.Enabled = hasPermissions;
shoppingCrateBuyList.Enabled = hasPermissions;
needsBuyingRefresh = false;
@@ -166,7 +171,12 @@ namespace Barotrauma
};
// Merchant balance ------------------------------------------------
var merchantBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), storeContent.RectTransform))
var balanceAndValueGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.75f / 14.0f), storeContent.RectTransform), isHorizontal: true)
{
RelativeSpacing = 0.005f
};
var merchantBalanceContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), balanceAndValueGroup.RectTransform))
{
RelativeSpacing = 0.005f
};
@@ -177,29 +187,111 @@ namespace Barotrauma
ForceUpperCase = true
};
merchantBalanceBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), merchantBalanceContainer.RectTransform),
"", font: GUI.SubHeadingFont, textAlignment: Alignment.TopLeft)
"", font: GUI.SubHeadingFont)
{
AutoScaleVertical = true,
TextScale = 1.1f,
TextGetter = () =>
{
var balance = CurrentLocation != null ? CurrentLocation.StoreCurrentBalance : 0;
if (balance < (int)(0.25f * Location.StoreInitialBalance))
if (CurrentLocation != null)
{
merchantBalanceBlock.TextColor = Color.Red;
}
else if (balance < (int)(0.5f * Location.StoreInitialBalance))
{
merchantBalanceBlock.TextColor = Color.Orange;
merchantBalanceBlock.TextColor = CurrentLocation.BalanceColor;
return GetCurrencyFormatted(CurrentLocation.StoreCurrentBalance);
}
else
{
merchantBalanceBlock.TextColor = Color.White;
merchantBalanceBlock.TextColor = Color.Red;
return GetCurrencyFormatted(0);
}
return GetCurrencyFormatted(balance);
}
};
// Item sell value ------------------------------------------------
var sellValueContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1.0f), balanceAndValueGroup.RectTransform))
{
CanBeFocused = false,
RelativeSpacing = 0.005f
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform),
TextManager.Get("campaignstore.sellvalue"), font: GUI.Font, textAlignment: Alignment.BottomLeft)
{
AutoScaleVertical = true,
CanBeFocused = false,
ForceUpperCase = true
};
valueChangeGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.5f), sellValueContainer.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
{
CanBeFocused = true,
RelativeSpacing = 0.02f
};
float blockWidth = GUI.IsFourByThree() ? 0.32f : 0.28f;
Point blockMaxSize = new Point((int)(GameSettings.TextScale * 60), valueChangeGroup.Rect.Height);
currentSellValueBlock = new GUITextBlock(new RectTransform(new Vector2(blockWidth, 1.0f), valueChangeGroup.RectTransform) { MaxSize = blockMaxSize },
"", font: GUI.SubHeadingFont)
{
AutoScaleVertical = true,
CanBeFocused = false,
TextScale = 1.1f,
TextGetter = () =>
{
if (CurrentLocation != null)
{
int balanceAfterTransaction = IsBuying ?
CurrentLocation.StoreCurrentBalance + buyTotal :
CurrentLocation.StoreCurrentBalance - sellTotal;
if (balanceAfterTransaction != CurrentLocation.StoreCurrentBalance)
{
var newStatus = Location.GetStoreBalanceStatus(balanceAfterTransaction);
if (CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier != newStatus.SellPriceModifier)
{
string tooltipTag = newStatus.SellPriceModifier > CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier ?
"campaingstore.valueincreasetooltip" : "campaingstore.valuedecreasetooltip";
valueChangeGroup.ToolTip = TextManager.Get(tooltipTag);
currentSellValueBlock.TextColor = newStatus.Color;
sellValueChangeArrow.Color = newStatus.Color;
sellValueChangeArrow.Visible = true;
newSellValueBlock.TextColor = newStatus.Color;
newSellValueBlock.Text = $"{(newStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
return $"{(CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
}
}
valueChangeGroup.ToolTip = null;
currentSellValueBlock.TextColor = CurrentLocation.BalanceColor;
sellValueChangeArrow.Visible = false;
newSellValueBlock.Text = null;
return $"{(CurrentLocation.ActiveStoreBalanceStatus.SellPriceModifier * 100).FormatZeroDecimal()} %";
}
else
{
valueChangeGroup.ToolTip = null;
sellValueChangeArrow.Visible = false;
newSellValueBlock.Text = null;
return null;
}
}
};
Vector4 newPadding = currentSellValueBlock.Padding;
newPadding.Z = 0;
currentSellValueBlock.Padding = newPadding;
float relativeHeight = 0.45f;
float relativeWidth = (relativeHeight * valueChangeGroup.Rect.Height) / valueChangeGroup.Rect.Width;
sellValueChangeArrow = new GUIImage(new RectTransform(new Vector2(relativeWidth, relativeHeight), valueChangeGroup.RectTransform), "StoreArrow", scaleToFit: true)
{
CanBeFocused = false,
Visible = false
};
newSellValueBlock = new GUITextBlock(new RectTransform(new Vector2(blockWidth, 1.0f), valueChangeGroup.RectTransform) { MaxSize = blockMaxSize },
"", font: GUI.SubHeadingFont)
{
AutoScaleVertical = true,
CanBeFocused = false,
TextScale = 1.1f
};
newPadding = newSellValueBlock.Padding;
newPadding.X = 0;
newSellValueBlock.Padding = newPadding;
// Store mode buttons ------------------------------------------------
var modeButtonFrame = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.6f / 14.0f), storeContent.RectTransform), style: null);
var modeButtonContainer = new GUILayoutGroup(new RectTransform(Vector2.One, modeButtonFrame.RectTransform), isHorizontal: true);
@@ -209,8 +301,6 @@ namespace Barotrauma
tabSortingMethods.Clear();
foreach (StoreTab tab in tabs)
{
// TODO: Remove the row below once the deal page is implemented
if (tab == StoreTab.Deals) { continue; }
var tabButton = new GUIButton(new RectTransform(new Vector2(1.0f / (tabs.Length + 1), 1.0f), modeButtonContainer.RectTransform),
text: TextManager.Get("campaignstoretab." + tab), style: "GUITabButton")
{
@@ -309,24 +399,22 @@ namespace Barotrauma
searchBox.OnTextChanged += (textBox, text) => { FilterStoreItems(null, text); return true; };
var storeItemListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.92f), sortFilterListContainer.RectTransform), style: null);
storeDealsList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{
AutoHideScrollBar = false,
Visible = false
};
tabLists.Clear();
tabLists.Add(StoreTab.Deals, storeDealsList);
storeBuyList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{
AutoHideScrollBar = false,
Visible = false
};
storeDailySpecialsGroup = CreateDealsGroup(storeBuyList);
tabLists.Add(StoreTab.Buy, storeBuyList);
storeSellList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{
AutoHideScrollBar = false,
Visible = false
};
storeRequestedGoodGroup = CreateDealsGroup(storeSellList);
tabLists.Add(StoreTab.Sell, storeSellList);
// Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------
@@ -428,6 +516,26 @@ namespace Barotrauma
resolutionWhenCreated = new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
}
private GUILayoutGroup CreateDealsGroup(GUIListBox parentList)
{
var elementHeight = (int)(GUI.yScale * 80);
var frame = new GUIFrame(new RectTransform(new Point(parentList.Content.Rect.Width, 4 * elementHeight + 3), parent: parentList.Content.RectTransform), style: null);
frame.UserData = "deals";
var dealsGroup = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter);
var dealsHeader = new GUILayoutGroup(new RectTransform(new Point((int)(0.95f * parentList.Content.Rect.Width), elementHeight), parent: dealsGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
dealsHeader.UserData = "header";
var iconWidth = (0.9f * dealsHeader.Rect.Height) / dealsHeader.Rect.Width;
var dealsIcon = new GUIImage(new RectTransform(new Vector2(iconWidth, 0.9f), dealsHeader.RectTransform), "StoreDealIcon", scaleToFit: true);
var text = TextManager.Get(parentList == storeBuyList ? "campaignstore.dailyspecials" : "campaignstore.requestedgoods");
var dealsText = new GUITextBlock(new RectTransform(new Vector2(1.0f - iconWidth, 0.9f), dealsHeader.RectTransform), text, font: GUI.LargeFont);
storeSpecialColor = dealsIcon.Color;
dealsText.TextColor = storeSpecialColor;
var divider = new GUIImage(new RectTransform(new Point(dealsGroup.Rect.Width, 3), dealsGroup.RectTransform), "HorizontalLine");
divider.UserData = "divider";
frame.CanBeFocused = dealsGroup.CanBeFocused = dealsHeader.CanBeFocused = dealsIcon.CanBeFocused = dealsText.CanBeFocused = divider.CanBeFocused = false;
return dealsGroup;
}
private void UpdateLocation(Location prevLocation, Location newLocation)
{
if (prevLocation == newLocation) { return; }
@@ -464,17 +572,8 @@ namespace Barotrauma
SetConfirmButtonBehavior();
SetConfirmButtonStatus();
FilterStoreItems();
if (tab == StoreTab.Deals)
if (tab == StoreTab.Buy)
{
storeBuyList.Visible = false;
storeSellList.Visible = false;
storeDealsList.Visible = true;
shoppingCrateSellList.Visible = false;
shoppingCrateBuyList.Visible = true;
}
else if (tab == StoreTab.Buy)
{
storeDealsList.Visible = false;
storeSellList.Visible = false;
storeBuyList.Visible = true;
shoppingCrateSellList.Visible = false;
@@ -482,7 +581,6 @@ namespace Barotrauma
}
else if (tab == StoreTab.Sell)
{
storeDealsList.Visible = false;
storeBuyList.Visible = false;
storeSellList.Visible = true;
shoppingCrateBuyList.Visible = false;
@@ -525,37 +623,71 @@ namespace Barotrauma
bool hasPermissions = HasPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
if ((storeDailySpecialsGroup != null) != CurrentLocation.DailySpecials.Any())
{
if (storeDailySpecialsGroup == null)
{
storeDailySpecialsGroup = CreateDealsGroup(storeBuyList);
storeDailySpecialsGroup.Parent.SetAsFirstChild();
}
else
{
storeBuyList.RemoveChild(storeDailySpecialsGroup.Parent);
storeDailySpecialsGroup = null;
}
storeBuyList.RecalculateChildren();
}
foreach (PurchasedItem item in CurrentLocation.StoreStock)
{
if (item.ItemPrefab.CanBeBoughtAtLocation(CurrentLocation, out PriceInfo priceInfo))
CreateOrUpdateItemFrame(item.ItemPrefab, item.Quantity);
}
foreach (ItemPrefab itemPrefab in CurrentLocation.DailySpecials)
{
if (CurrentLocation.StoreStock.Any(pi => pi.ItemPrefab == itemPrefab)) { continue; }
CreateOrUpdateItemFrame(itemPrefab, 0);
}
void CreateOrUpdateItemFrame(ItemPrefab itemPrefab, int quantity)
{
if (itemPrefab.CanBeBoughtAtLocation(CurrentLocation, out PriceInfo priceInfo))
{
var itemFrame = storeBuyList.Content.Children.FirstOrDefault(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == item.ItemPrefab);
var quantity = item.Quantity;
if (CargoManager.PurchasedItems.Find(i => i.ItemPrefab == item.ItemPrefab) is PurchasedItem purchasedItem)
var isDailySpecial = CurrentLocation.DailySpecials.Contains(itemPrefab);
var itemFrame = isDailySpecial ?
storeDailySpecialsGroup.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab) :
storeBuyList.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab);
if (CargoManager.PurchasedItems.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem purchasedItem)
{
quantity = Math.Max(quantity - purchasedItem.Quantity, 0);
}
if (CargoManager.ItemsInBuyCrate.Find(i => i.ItemPrefab == item.ItemPrefab) is PurchasedItem itemInBuyCrate)
if (CargoManager.ItemsInBuyCrate.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem itemInBuyCrate)
{
quantity = Math.Max(quantity - itemInBuyCrate.Quantity, 0);
}
if (itemFrame == null)
{
itemFrame = CreateItemFrame(new PurchasedItem(item.ItemPrefab, quantity), priceInfo, storeBuyList, forceDisable: !hasPermissions);
var parentComponent = isDailySpecial ? storeDailySpecialsGroup : storeBuyList as GUIComponent;
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, quantity), priceInfo, parentComponent, forceDisable: !hasPermissions);
}
else
{
(itemFrame.UserData as PurchasedItem).Quantity = quantity;
SetQuantityLabelText(StoreTab.Buy, itemFrame);
SetOwnedLabelText(itemFrame);
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0);
}
SetItemFrameStatus(itemFrame, hasPermissions && quantity > 0);
existingItemFrames.Add(itemFrame);
}
}
var removedItemFrames = storeBuyList.Content.Children.Except(existingItemFrames).ToList();
removedItemFrames.ForEach(f => storeBuyList.Content.RemoveChild(f));
var removedItemFrames = storeBuyList.Content.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList();
if (storeDailySpecialsGroup != null)
{
removedItemFrames.AddRange(storeDailySpecialsGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
}
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
if (IsBuying) { FilterStoreItems(); }
SortItems(StoreTab.Buy);
@@ -567,36 +699,72 @@ namespace Barotrauma
{
float prevSellListScroll = storeSellList.BarScroll;
float prevShoppingCrateScroll = shoppingCrateSellList.BarScroll;
bool hasPermissions = HasPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
foreach (PurchasedItem item in itemsToSell)
if ((storeRequestedGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
{
PriceInfo priceInfo = item.ItemPrefab.GetPriceInfo(CurrentLocation);
if (priceInfo == null) { continue; }
var itemFrame = storeSellList.Content.FindChild(c => c.UserData is PurchasedItem i && i.ItemPrefab == item.ItemPrefab);
var quantity = item.Quantity;
if (CargoManager.ItemsInSellCrate.Find(i => i.ItemPrefab == item.ItemPrefab) is PurchasedItem itemInSellCrate)
if (storeRequestedGoodGroup == null)
{
quantity = Math.Max(quantity - itemInSellCrate.Quantity, 0);
}
if (itemFrame == null)
{
itemFrame = CreateItemFrame(new PurchasedItem(item.ItemPrefab, quantity), priceInfo, storeSellList, forceDisable: !hasPermissions);
storeRequestedGoodGroup = CreateDealsGroup(storeSellList);
storeRequestedGoodGroup.Parent.SetAsFirstChild();
}
else
{
(itemFrame.UserData as PurchasedItem).Quantity = quantity;
storeSellList.RemoveChild(storeRequestedGoodGroup.Parent);
storeRequestedGoodGroup = null;
}
storeSellList.RecalculateChildren();
}
foreach (PurchasedItem item in itemsToSell)
{
CreateOrUpdateItemFrame(item.ItemPrefab, item.Quantity);
}
foreach (var requestedGood in CurrentLocation.RequestedGoods)
{
if (itemsToSell.Any(pi => pi.ItemPrefab == requestedGood)) { continue; }
CreateOrUpdateItemFrame(requestedGood, 0);
}
void CreateOrUpdateItemFrame(ItemPrefab itemPrefab, int itemQuantity)
{
PriceInfo priceInfo = itemPrefab.GetPriceInfo(CurrentLocation);
if (priceInfo == null) { return; }
var isRequestedGood = CurrentLocation.RequestedGoods.Contains(itemPrefab);
var itemFrame = isRequestedGood ?
storeRequestedGoodGroup.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab) :
storeSellList.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab);
if (CargoManager.ItemsInSellCrate.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem itemInSellCrate)
{
itemQuantity = Math.Max(itemQuantity - itemInSellCrate.Quantity, 0);
}
if (itemFrame == null)
{
var parentComponent = isRequestedGood ? storeRequestedGoodGroup : storeSellList as GUIComponent;
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), priceInfo, parentComponent, forceDisable: !hasPermissions);
}
else
{
(itemFrame.UserData as PurchasedItem).Quantity = itemQuantity;
SetQuantityLabelText(StoreTab.Sell, itemFrame);
SetOwnedLabelText(itemFrame);
SetItemFrameStatus(itemFrame, hasPermissions);
}
if (quantity < 1) { itemFrame.Visible = false; }
SetItemFrameStatus(itemFrame, hasPermissions && itemQuantity > 0);
if (itemQuantity < 1 && !isRequestedGood)
{
itemFrame.Visible = false;
}
existingItemFrames.Add(itemFrame);
}
var removedItemFrames = storeSellList.Content.Children.Except(existingItemFrames).ToList();
removedItemFrames.ForEach(f => storeSellList.Content.RemoveChild(f));
var removedItemFrames = storeSellList.Content.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList();
if (storeRequestedGoodGroup != null)
{
removedItemFrames.AddRange(storeRequestedGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
}
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
if (IsSelling) { FilterStoreItems(); }
SortItems(StoreTab.Sell);
@@ -673,13 +841,10 @@ namespace Barotrauma
}
suppressBuySell = false;
if (priceInfo != null)
{
var price = listBox == shoppingCrateBuyList ?
CurrentLocation.GetAdjustedItemBuyPrice(priceInfo) :
CurrentLocation.GetAdjustedItemSellPrice(priceInfo);
totalPrice += item.Quantity * price;
}
var price = listBox == shoppingCrateBuyList ?
CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo) :
CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
totalPrice += item.Quantity * price;
}
var removedItemFrames = listBox.Content.Children.Except(existingItemFrames).ToList();
@@ -711,32 +876,138 @@ namespace Barotrauma
if (sortingMethod == SortingMethod.AlphabeticalAsc || sortingMethod == SortingMethod.AlphabeticalDesc)
{
list.Content.RectTransform.SortChildren(
(x, y) => (x.GUIComponent.UserData as PurchasedItem).ItemPrefab.Name.CompareTo((y.GUIComponent.UserData as PurchasedItem).ItemPrefab.Name));
if (sortingMethod == SortingMethod.AlphabeticalDesc) { list.Content.RectTransform.ReverseChildren(); }
list.Content.RectTransform.SortChildren(CompareByName);
if (GetSpecialsGroup() is GUILayoutGroup specialsGroup)
{
specialsGroup.RectTransform.SortChildren(CompareByName);
specialsGroup.Recalculate();
}
int CompareByName(RectTransform x, RectTransform y)
{
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
{
var sortResult = itemX.ItemPrefab.Name.CompareTo(itemY.ItemPrefab.Name);
if (sortingMethod == SortingMethod.AlphabeticalDesc) { sortResult *= -1; }
return sortResult;
}
else
{
return CompareByElement(x, y);
}
}
}
else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
{
SortItems(list, SortingMethod.AlphabeticalAsc);
if (list == storeSellList || list == shoppingCrateSellList)
{
list.Content.RectTransform.SortChildren(
(x, y) => CurrentLocation.GetAdjustedItemSellPrice((x.GUIComponent.UserData as PurchasedItem).ItemPrefab).CompareTo(
CurrentLocation.GetAdjustedItemSellPrice((y.GUIComponent.UserData as PurchasedItem).ItemPrefab)));
list.Content.RectTransform.SortChildren(CompareBySellPrice);
if (GetSpecialsGroup() is GUILayoutGroup specialsGroup)
{
specialsGroup.RectTransform.SortChildren(CompareBySellPrice);
specialsGroup.Recalculate();
}
int CompareBySellPrice(RectTransform x, RectTransform y)
{
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
{
var sortResult = CurrentLocation.GetAdjustedItemSellPrice(itemX.ItemPrefab).CompareTo(
CurrentLocation.GetAdjustedItemSellPrice(itemY.ItemPrefab));
if (sortingMethod == SortingMethod.PriceDesc) { sortResult *= -1; }
return sortResult;
}
else
{
return CompareByElement(x, y);
}
}
}
else
{
list.Content.RectTransform.SortChildren(
(x, y) => CurrentLocation.GetAdjustedItemBuyPrice((x.GUIComponent.UserData as PurchasedItem).ItemPrefab).CompareTo(
CurrentLocation.GetAdjustedItemBuyPrice((y.GUIComponent.UserData as PurchasedItem).ItemPrefab)));
list.Content.RectTransform.SortChildren(CompareByBuyPrice);
if (GetSpecialsGroup() is GUILayoutGroup specialsGroup)
{
specialsGroup.RectTransform.SortChildren(CompareByBuyPrice);
specialsGroup.Recalculate();
}
int CompareByBuyPrice(RectTransform x, RectTransform y)
{
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
{
var sortResult = CurrentLocation.GetAdjustedItemBuyPrice(itemX.ItemPrefab).CompareTo(
CurrentLocation.GetAdjustedItemBuyPrice(itemY.ItemPrefab));
if (sortingMethod == SortingMethod.PriceDesc) { sortResult *= -1; }
return sortResult;
}
else
{
return CompareByElement(x, y);
}
}
}
if (sortingMethod == SortingMethod.PriceDesc) { list.Content.RectTransform.ReverseChildren(); }
}
else if (sortingMethod == SortingMethod.CategoryAsc)
{
SortItems(list, SortingMethod.AlphabeticalAsc);
list.Content.RectTransform.SortChildren((x, y) =>
(x.GUIComponent.UserData as PurchasedItem).ItemPrefab.Category.CompareTo((y.GUIComponent.UserData as PurchasedItem).ItemPrefab.Category));
list.Content.RectTransform.SortChildren(CompareByCategory);
if (GetSpecialsGroup() is GUILayoutGroup specialsGroup)
{
specialsGroup.RectTransform.SortChildren(CompareByCategory);
specialsGroup.Recalculate();
}
static int CompareByCategory(RectTransform x, RectTransform y)
{
if (x.GUIComponent.UserData is PurchasedItem itemX && y.GUIComponent.UserData is PurchasedItem itemY)
{
return itemX.ItemPrefab.Category.CompareTo(itemY.ItemPrefab.Category);
}
else
{
return CompareByElement(x, y);
}
}
}
GUILayoutGroup GetSpecialsGroup()
{
if (list == storeBuyList)
{
return storeDailySpecialsGroup;
}
else if (list == storeSellList)
{
return storeRequestedGoodGroup;
}
else
{
return null;
}
}
static int CompareByElement(RectTransform x, RectTransform y)
{
if (ShouldBeOnTop(x) || ShouldBeOnBottom(y))
{
return -1;
}
else if (ShouldBeOnBottom(x) || ShouldBeOnTop(y))
{
return 1;
}
else
{
return 0;
}
static bool ShouldBeOnTop(RectTransform rt) =>
rt.GUIComponent.UserData is string id && (id == "deals" || id == "header");
static bool ShouldBeOnBottom(RectTransform rt) =>
rt.GUIComponent.UserData is string id && id == "divider";
}
}
@@ -750,7 +1021,7 @@ namespace Barotrauma
private void SortActiveTabItems(SortingMethod sortingMethod) => SortItems(activeTab, sortingMethod);
private GUIComponent CreateItemFrame(PurchasedItem pi, PriceInfo priceInfo, GUIListBox listBox, bool forceDisable = false)
private GUIComponent CreateItemFrame(PurchasedItem pi, PriceInfo priceInfo, GUIComponent parentComponent, bool forceDisable = false)
{
var tooltip = pi.ItemPrefab.Name;
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
@@ -758,7 +1029,21 @@ namespace Barotrauma
tooltip += "\n" + pi.ItemPrefab.Description;
}
GUIFrame frame = new GUIFrame(new RectTransform(new Point(listBox.Content.Rect.Width, (int)(GUI.yScale * 80)), parent: listBox.Content.RectTransform), style: "ListBoxElement")
GUIListBox parentListBox = parentComponent as GUIListBox;
int width = 0;
RectTransform parent = null;
if (parentListBox != null)
{
width = parentListBox.Content.Rect.Width;
parent = parentListBox.Content.RectTransform;
}
else
{
width = parentComponent.Rect.Width;
parent = parentComponent.RectTransform;
}
GUIFrame frame = new GUIFrame(new RectTransform(new Point(width, (int)(GUI.yScale * 80)), parent: parent), style: "ListBoxElement")
{
ToolTip = tooltip,
UserData = pi
@@ -788,33 +1073,58 @@ namespace Barotrauma
img.RectTransform.MaxSize = img.Rect.Size;
}
GUILayoutGroup nameAndQuantityGroup = new GUILayoutGroup(new RectTransform(new Vector2(nameAndIconRelativeWidth - iconRelativeWidth, 1.0f), mainGroup.RectTransform))
GUIFrame nameAndQuantityFrame = new GUIFrame(new RectTransform(new Vector2(nameAndIconRelativeWidth - iconRelativeWidth, 1.0f), mainGroup.RectTransform), style: null)
{
CanBeFocused = false
};
GUILayoutGroup nameAndQuantityGroup = new GUILayoutGroup(new RectTransform(Vector2.One, nameAndQuantityFrame.RectTransform))
{
CanBeFocused = false,
Stretch = true
};
var isSellingRelatedList = parentComponent == storeSellList || parentComponent == storeRequestedGoodGroup || parentComponent == shoppingCrateSellList;
var locationHasDealOnItem = isSellingRelatedList ?
CurrentLocation.RequestedGoods.Contains(pi.ItemPrefab) : CurrentLocation.DailySpecials.Contains(pi.ItemPrefab);
GUITextBlock nameBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.4f), nameAndQuantityGroup.RectTransform),
pi.ItemPrefab.Name, font: GUI.SubHeadingFont, textAlignment: Alignment.BottomLeft)
{
CanBeFocused = false,
Shadow = locationHasDealOnItem,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
TextScale = 0.85f,
UserData = "name"
};
if (locationHasDealOnItem)
{
var relativeWidth = (0.9f * nameAndQuantityFrame.Rect.Height) / nameAndQuantityFrame.Rect.Width;
var dealIcon = new GUIImage(
new RectTransform(new Vector2(relativeWidth, 0.9f), nameAndQuantityFrame.RectTransform, anchor: Anchor.CenterLeft)
{
AbsoluteOffset = new Point((int)nameBlock.Padding.X, 0)
},
"StoreDealIcon", scaleToFit: true)
{
CanBeFocused = false
};
dealIcon.SetAsFirstChild();
}
var isParentOnLeftSideOfInterface = parentComponent == storeBuyList || parentComponent == storeDailySpecialsGroup ||
parentComponent == storeSellList || parentComponent == storeRequestedGoodGroup;
GUILayoutGroup shoppingCrateAmountGroup = null;
GUINumberInput amountInput = null;
if (listBox == storeBuyList || listBox == storeSellList)
if (isParentOnLeftSideOfInterface)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), nameAndQuantityGroup.RectTransform),
CreateQuantityLabelText(listBox == storeSellList ? StoreTab.Sell : StoreTab.Buy, pi.Quantity), font: GUI.Font, textAlignment: Alignment.BottomLeft)
CreateQuantityLabelText(isSellingRelatedList ? StoreTab.Sell : StoreTab.Buy, pi.Quantity), font: GUI.Font, textAlignment: Alignment.BottomLeft)
{
CanBeFocused = false,
Shadow = locationHasDealOnItem,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
TextScale = 0.85f,
UserData = "quantitylabel"
};
}
else if (listBox == shoppingCrateBuyList || listBox == shoppingCrateSellList)
else if (!isParentOnLeftSideOfInterface)
{
var relativePadding = nameBlock.Padding.X / nameBlock.Rect.Width;
shoppingCrateAmountGroup = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - relativePadding, 0.6f), nameAndQuantityGroup.RectTransform) { RelativeOffset = new Vector2(relativePadding, 0) },
@@ -825,7 +1135,7 @@ namespace Barotrauma
amountInput = new GUINumberInput(new RectTransform(new Vector2(0.4f, 1.0f), shoppingCrateAmountGroup.RectTransform), GUINumberInput.NumberType.Int)
{
MinValueInt = 0,
MaxValueInt = GetMaxAvailable(pi.ItemPrefab, listBox == shoppingCrateBuyList ? StoreTab.Buy : StoreTab.Sell),
MaxValueInt = GetMaxAvailable(pi.ItemPrefab, isSellingRelatedList ? StoreTab.Sell : StoreTab.Buy),
UserData = pi,
IntValue = pi.Quantity
};
@@ -856,6 +1166,7 @@ namespace Barotrauma
textAlignment: shoppingCrateAmountGroup == null ? Alignment.TopLeft : Alignment.CenterLeft)
{
CanBeFocused = false,
Shadow = locationHasDealOnItem,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
TextScale = 0.85f,
UserData = "owned"
@@ -864,22 +1175,53 @@ namespace Barotrauma
var buttonRelativeWidth = (0.9f * mainGroup.Rect.Height) / mainGroup.Rect.Width;
var priceBlock = new GUITextBlock(new RectTransform(new Vector2(priceAndButtonRelativeWidth - buttonRelativeWidth, 1.0f), mainGroup.RectTransform), "", font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
var priceFrame = new GUIFrame(new RectTransform(new Vector2(priceAndButtonRelativeWidth - buttonRelativeWidth, 1.0f), mainGroup.RectTransform), style: null)
{
CanBeFocused = false
};
var priceBlock = new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.5f), priceFrame.RectTransform, anchor: Anchor.Center),
"0 MK", font: GUI.SubHeadingFont, textAlignment: Alignment.Right)
{
CanBeFocused = false,
TextColor = Color.White * (forceDisable ? 0.5f : 1.0f),
TextColor = locationHasDealOnItem ? storeSpecialColor : Color.White,
UserData = "price"
};
if (listBox == storeSellList || listBox == shoppingCrateSellList)
priceBlock.Color *= (forceDisable ? 0.5f : 1.0f);
priceBlock.CalculateHeightFromText();
if (isSellingRelatedList)
{
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(priceInfo) ?? 0);
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, priceInfo: priceInfo) ?? 0);
}
else
{
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(priceInfo) ?? 0);
priceBlock.TextGetter = () => GetCurrencyFormatted(CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, priceInfo: priceInfo) ?? 0);
}
if (locationHasDealOnItem)
{
var undiscounterPriceBlock = new GUITextBlock(
new RectTransform(new Vector2(1.0f, 0.25f), priceFrame.RectTransform, anchor: Anchor.Center)
{
AbsoluteOffset = new Point(0, priceBlock.RectTransform.ScaledSize.Y)
}, "", font: GUI.SmallFont, textAlignment: Alignment.Center)
{
CanBeFocused = false,
Strikethrough = new GUITextBlock.StrikethroughSettings(color: priceBlock.TextColor, expand: 1),
TextColor = priceBlock.TextColor,
UserData = "undiscountedprice"
};
if (isSellingRelatedList)
{
undiscounterPriceBlock.TextGetter = () => GetCurrencyFormatted(
CurrentLocation?.GetAdjustedItemSellPrice(pi.ItemPrefab, priceInfo: priceInfo, considerRequestedGoods: false) ?? 0);
}
else
{
undiscounterPriceBlock.TextGetter = () => GetCurrencyFormatted(
CurrentLocation?.GetAdjustedItemBuyPrice(pi.ItemPrefab, priceInfo: priceInfo, considerDailySpecials: false) ?? 0);
}
}
if (listBox == storeDealsList || listBox == storeBuyList || listBox == storeSellList)
if (isParentOnLeftSideOfInterface)
{
new GUIButton(new RectTransform(new Vector2(buttonRelativeWidth, 0.9f), mainGroup.RectTransform), style: "StoreAddToCrateButton")
{
@@ -902,7 +1244,14 @@ namespace Barotrauma
};
}
listBox.RecalculateChildren();
if (parentListBox != null)
{
parentListBox.RecalculateChildren();
}
else if (parentComponent is GUILayoutGroup parentLayoutGroup)
{
parentLayoutGroup.Recalculate();
}
mainGroup.Recalculate();
mainGroup.RectTransform.RecalculateChildren(true, true);
amountInput?.LayoutGroup.Recalculate();
@@ -923,15 +1272,20 @@ namespace Barotrauma
.ForEach(i => AddToOwnedItems(i.Prefab));
// Add items in character inventories
foreach (Character c in GameMain.GameSession.CrewManager.GetCharacters())
foreach (var item in Item.ItemList)
{
Item.ItemList.Where(i => i != null && i.GetRootInventoryOwner() == c)
.ForEach(i => AddToOwnedItems(i.Prefab));
if (item == null || item.Removed) { continue; }
var rootInventoryOwner = item.GetRootInventoryOwner();
var ownedByCrewMember = GameMain.GameSession.CrewManager.GetCharacters().Any(c => c == rootInventoryOwner);
if (!ownedByCrewMember) { continue; }
AddToOwnedItems(item.Prefab);
}
// Add items already purchased
CargoManager?.PurchasedItems?.ForEach(pi => AddToOwnedItems(pi.ItemPrefab, amount: pi.Quantity));
ownedItemsUpdateTimer = 0.0f;
void AddToOwnedItems(ItemPrefab itemPrefab, int amount = 1)
{
if (OwnedItems.ContainsKey(itemPrefab))
@@ -977,14 +1331,22 @@ namespace Barotrauma
numberInput.Enabled = enabled;
}
if (itemFrame.FindChild("owned", recursive: true) is GUITextBlock owned)
if (itemFrame.FindChild("owned", recursive: true) is GUITextBlock ownedBlock)
{
owned.TextColor = color;
ownedBlock.TextColor = color;
}
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock price)
var isDiscounted = false;
if (itemFrame.FindChild("undiscountedprice", recursive: true) is GUITextBlock undiscountedPriceBlock)
{
price.TextColor = color;
undiscountedPriceBlock.TextColor = color;
undiscountedPriceBlock.Strikethrough.Color = color;
isDiscounted = true;
}
if (itemFrame.FindChild("price", recursive: true) is GUITextBlock priceBlock)
{
priceBlock.TextColor = isDiscounted ? storeSpecialColor * (enabled ? 1.0f : 0.5f) : color;
}
if (itemFrame.FindChild("addbutton", recursive: true) is GUIButton addButton)
@@ -1101,7 +1463,7 @@ namespace Barotrauma
itemsToRemove.Add(item);
continue;
}
totalPrice += item.Quantity * CurrentLocation.GetAdjustedItemBuyPrice(priceInfo);
totalPrice += item.Quantity * CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo);
}
itemsToRemove.ForEach(i => itemsToPurchase.Remove(i));
@@ -1135,7 +1497,7 @@ namespace Barotrauma
}
if (item.ItemPrefab.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo)
{
totalValue += item.Quantity * CurrentLocation.GetAdjustedItemSellPrice(priceInfo);
totalValue += item.Quantity * CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
}
else
{
@@ -1197,16 +1559,38 @@ namespace Barotrauma
private void SetClearAllButtonStatus() => clearAllButton.Enabled =
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any();
public void Update()
private float ownedItemsUpdateTimer = 0.0f;
private readonly float ownedItemsUpdateInterval = 1.5f;
public void Update(float deltaTime)
{
if (GameMain.GraphicsWidth != resolutionWhenCreated.X || GameMain.GraphicsHeight != resolutionWhenCreated.Y)
{
CreateUI();
needsRefresh = false;
}
if (needsRefresh || hadPermissions != HasPermissions) { Refresh(); }
if (needsBuyingRefresh) { RefreshBuying(); }
else
{
// Update the owned items at short intervals and check if the interface should be refreshed
ownedItemsUpdateTimer += deltaTime;
if (ownedItemsUpdateTimer >= ownedItemsUpdateInterval)
{
var prevOwnedItems = new Dictionary<ItemPrefab, int>(OwnedItems);
UpdateOwnedItems();
var refresh = (prevOwnedItems.Count != OwnedItems.Count) ||
(prevOwnedItems.Select(kvp => kvp.Value).Sum() != OwnedItems.Select(kvp => kvp.Value).Sum()) ||
(OwnedItems.Any(kvp => kvp.Value > 0 && !prevOwnedItems.ContainsKey(kvp.Key)) ||
prevOwnedItems.Any(kvp => !OwnedItems.TryGetValue(kvp.Key, out var itemCount) || kvp.Value != itemCount));
if (refresh)
{
needsItemsToSellRefresh = true;
needsRefresh = true;
}
}
}
if (needsItemsToSellRefresh) { RefreshItemsToSell(); }
if (needsRefresh || hadPermissions != HasPermissions) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsBuyingRefresh) { RefreshBuying(); }
if (needsSellingRefresh) { RefreshSelling(); }
}
}
@@ -610,8 +610,8 @@ namespace Barotrauma
{
if (GameMain.Client == null)
{
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(selectedSubmarine);
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub);
RefreshSubmarineDisplay(true);
}
else
@@ -645,8 +645,8 @@ namespace Barotrauma
if (GameMain.Client == null)
{
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(selectedSubmarine);
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub);
RefreshSubmarineDisplay(true);
}
else
@@ -29,7 +29,7 @@ namespace Barotrauma
private float sizeMultiplier = 1f;
private IEnumerable<Character> crew;
private List<Character.TeamType> teamIDs;
private List<CharacterTeamType> teamIDs;
private const string inLobbyString = "\u2022 \u2022 \u2022";
public static Color OwnCharacterBGColor = Color.Gold * 0.7f;
@@ -281,11 +281,11 @@ namespace Barotrauma
// Show own team first when there's more than one team
if (teamIDs.Count > 1 && GameMain.Client?.Character != null)
{
Character.TeamType ownTeam = GameMain.Client.Character.TeamID;
CharacterTeamType ownTeam = GameMain.Client.Character.TeamID;
teamIDs = teamIDs.OrderBy(i => i != ownTeam).ThenBy(i => i).ToList();
}
if (!teamIDs.Any()) teamIDs.Add(Character.TeamType.None);
if (!teamIDs.Any()) { teamIDs.Add(CharacterTeamType.None); }
var content = new GUILayoutGroup(new RectTransform(Vector2.One, crewFrame.RectTransform));
@@ -465,15 +465,14 @@ namespace Barotrauma
{
foreach (Character character in crew.Where(c => c.TeamID == teamIDs[i]))
{
if (!(character is AICharacter) && connectedClients.Find(c => c.Character == null && c.Name == character.Name) != null) continue;
CreateMultiPlayerCharacterElement(character, GameMain.Client.ConnectedClients.Find(c => c.Character == character), i);
if (!(character is AICharacter) && connectedClients.Any(c => c.Character == null && c.Name == character.Name)) { continue; }
CreateMultiPlayerCharacterElement(character, GameMain.Client.PreviouslyConnectedClients.FirstOrDefault(c => c.Character == character), i);
}
}
for (int j = 0; j < connectedClients.Count; j++)
{
Client client = connectedClients[j];
if (!client.InGame || client.Character == null || client.Character.IsDead)
{
CreateMultiPlayerClientElement(client);
@@ -565,7 +564,7 @@ namespace Barotrauma
private int GetTeamIndex(Client client)
{
if (teamIDs.Count <= 1) return 0;
if (teamIDs.Count <= 1) { return 0; }
if (client.Character != null)
{
@@ -707,7 +706,7 @@ namespace Barotrauma
{
GUIComponent paddedFrame;
if (client.Character == null)
if (client.Character?.Info == null)
{
paddedFrame = new GUILayoutGroup(new RectTransform(new Vector2(0.874f, 0.58f), frame.RectTransform, Anchor.TopCenter) { RelativeOffset = new Vector2(0.0f, 0.05f) })
{
@@ -625,13 +625,19 @@ namespace Barotrauma
selectedUpgradeCategoryLayout?.ClearChildren();
GUIFrame frame = new GUIFrame(rectT(1, 0.4f, selectedUpgradeCategoryLayout));
GUIListBox prefabList = new GUIListBox(rectT(0.93f, 0.9f, frame, Anchor.Center)) { UserData = "prefablist" };
List<Item> entitiesOnSub = null;
if (!category.IsWallUpgrade)
{
entitiesOnSub = submarine.GetItems(true).Where(i => submarine.IsEntityFoundOnThisSub(i, true)).ToList();
}
foreach (UpgradePrefab prefab in prefabs)
{
CreateUpgradeEntry(prefab, category, prefabList.Content);
CreateUpgradeEntry(prefab, category, prefabList.Content, entitiesOnSub);
}
}
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent)
private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, List<Item> itemsOnSubmarine)
{
/* UPGRADE PREFAB ENTRY
* |------------------------------------------------------------------|
@@ -680,12 +686,14 @@ namespace Barotrauma
progressLayout.Recalculate();
buyButtonLayout.Recalculate();
if (!HasPermission)
if (!HasPermission || 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
}
buyButton.OnClicked += (button, o) =>
@@ -731,7 +739,7 @@ namespace Barotrauma
// include pending upgrades into the tooltip
foreach (var (prefab, category, level) in Campaign.UpgradeManager.PendingUpgrades)
{
if (entity is Item item && category.CanBeApplied(item) || entity is Structure && category.IsWallUpgrade)
if (entity is Item item && category.CanBeApplied(item, prefab) || entity is Structure && category.IsWallUpgrade)
{
bool found = false;
foreach (GUITextBlock textBlock in upgradeList.Content.Children.Where(c => c is GUITextBlock).Cast<GUITextBlock>())
@@ -786,7 +794,7 @@ namespace Barotrauma
foreach (UpgradeCategory category in UpgradeCategory.Categories)
{
if (entitiesOnSub.Any(item => category.CanBeApplied(item) && !item.disallowedUpgrades.Contains(category.Identifier)))
if (entitiesOnSub.Any(item => category.CanBeApplied(item, null)))
{
applicableCategories.Add(category);
}
@@ -826,7 +834,7 @@ namespace Barotrauma
HoveredItem = item;
if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradTab == UpgradeTab.Upgrade && currentStoreLayout != null)
{
ScrollToCategory(data => data.Category.CanBeApplied(item));
ScrollToCategory(data => data.Category.CanBeApplied(item, null));
}
found = true;
break;
@@ -895,7 +903,7 @@ namespace Barotrauma
submarineInfoFrame.RectTransform.ScreenSpaceOffset = new Point(0, (int)(16 * GUI.Scale));
description.Padding = new Vector4(description.Padding.X, 24 * GUI.Scale, description.Padding.Z, description.Padding.W);
List<Entity> pointsOfInterest = (from category in UpgradeCategory.Categories from item in submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs) where category.CanBeApplied(item) && !item.NonInteractable select item).Cast<Entity>().ToList();
List<Entity> pointsOfInterest = (from category in UpgradeCategory.Categories from item in submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs) where category.CanBeApplied(item, null) && item.IsPlayerTeamInteractable select item).Cast<Entity>().ToList();
List<ushort> ids = GameMain.GameSession.SubmarineInfo?.LeftBehindDockingPortIDs ?? new List<ushort>();
pointsOfInterest.AddRange(submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs).Where(item => ids.Contains(item.ID)));
@@ -1112,7 +1120,7 @@ namespace Barotrauma
List<GUIFrame> frames = new List<GUIFrame>();
foreach (var (item, guiFrame) in itemPreviews)
{
if (category.CanBeApplied(item))
if (category.CanBeApplied(item, null))
{
frames.Add(guiFrame);
}