v0.14.6.0

This commit is contained in:
Joonas Rikkonen
2021-06-17 17:54:52 +03:00
parent 3f324b14e8
commit c27e2ea5ab
348 changed files with 13156 additions and 4266 deletions
@@ -208,6 +208,19 @@ namespace Barotrauma
get { return pauseMenuOpen; }
}
public static bool InputBlockingMenuOpen
{
get
{
return PauseMenuOpen ||
SettingsMenuOpen ||
DebugConsole.IsOpen ||
GameSession.IsTabMenuOpen ||
(GameMain.GameSession?.GameMode?.Paused ?? false) ||
CharacterHUD.IsCampaignInterfaceOpen;
}
}
public static bool PreventPauseMenuToggle = false;
public static Color ScreenOverlayColor
@@ -226,6 +239,9 @@ namespace Barotrauma
private static SavingIndicatorState savingIndicatorState = SavingIndicatorState.None;
private static float? timeUntilSavingIndicatorDisabled;
private static string loadedSpritesText;
private static DateTime loadedSpritesUpdateTime;
private enum SavingIndicatorState
{
None,
@@ -441,9 +457,12 @@ namespace Barotrauma
"Particle count: " + GameMain.ParticleManager.ParticleCount + "/" + GameMain.ParticleManager.MaxParticles,
Color.Lerp(GUI.Style.Green, GUI.Style.Red, (GameMain.ParticleManager.ParticleCount / (float)GameMain.ParticleManager.MaxParticles)), Color.Black * 0.5f, 0, SmallFont);
DrawString(spriteBatch, new Vector2(10, 115),
"Loaded sprites: " + Sprite.LoadedSprites.Count() + "\n(" + Sprite.LoadedSprites.Select(s => s.FilePath).Distinct().Count() + " unique textures)",
Color.White, Color.Black * 0.5f, 0, SmallFont);
if (loadedSpritesText == null || DateTime.Now > loadedSpritesUpdateTime)
{
loadedSpritesText = "Loaded sprites: " + Sprite.LoadedSprites.Count() + "\n(" + Sprite.LoadedSprites.Select(s => s.FilePath).Distinct().Count() + " unique textures)";
loadedSpritesUpdateTime = DateTime.Now + new TimeSpan(0, 0, seconds: 5);
}
DrawString(spriteBatch, new Vector2(10, 115), loadedSpritesText, Color.White, Color.Black * 0.5f, 0, SmallFont);
if (debugDrawSounds)
{
@@ -978,8 +997,7 @@ namespace Barotrauma
return editor.GetMouseCursorState();
// Portrait area during gameplay
case GameScreen _ when !(Character.Controlled?.ShouldLockHud() ?? true):
if (HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) ||
Rectangle.Union(HUDLayoutSettings.AfflictionAreaLeft, HUDLayoutSettings.HealthBarArea).Contains(PlayerInput.MousePosition))
if (HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) || CharacterHealth.IsMouseOnHealthBar())
{
return CursorState.Hand;
}
@@ -1331,7 +1349,7 @@ namespace Barotrauma
/// <param name="createOffset">Should the indicator move based on the camera position?</param>
/// <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,
public static void DrawIndicator(SpriteBatch spriteBatch, in Vector2 worldPosition, Camera cam, in Vector2 visibleRange, Sprite sprite, in Color color,
bool createOffset = true, float scaleMultiplier = 1.0f, float? overrideAlpha = null)
{
Vector2 diff = worldPosition - cam.WorldViewCenter;
@@ -1339,9 +1357,9 @@ namespace Barotrauma
float symbolScale = Math.Min(64.0f / sprite.size.X, 1.0f) * scaleMultiplier * Scale;
if (overrideAlpha.HasValue || dist > hideDist)
if (overrideAlpha.HasValue || (dist > visibleRange.X && dist < visibleRange.Y))
{
float alpha = overrideAlpha ?? Math.Min((dist - hideDist) / 100.0f, 1.0f);
float alpha = overrideAlpha ?? MathUtils.Min((dist - visibleRange.X) / 100.0f, 1.0f - ((dist - visibleRange.Y + 100f) / 100.0f), 1.0f);
Vector2 targetScreenPos = cam.WorldToScreen(worldPosition);
if (!createOffset)
@@ -1352,8 +1370,9 @@ namespace Barotrauma
float screenDist = Vector2.Distance(cam.WorldToScreen(cam.WorldViewCenter), targetScreenPos);
float angle = MathUtils.VectorToAngle(diff);
float originalAngle = angle;
float minAngleDiff = 0.05f;
const float minAngleDiff = 0.05f;
bool overlapFound = true;
int iterations = 0;
while (overlapFound && iterations < 10)
@@ -1375,18 +1394,24 @@ namespace Barotrauma
usedIndicatorAngles.Add(angle);
Vector2 unclampedDiff = new Vector2(
(float)Math.Cos(angle) * screenDist,
(float)-Math.Sin(angle) * screenDist);
Vector2 iconDiff = new Vector2(
(float)Math.Cos(angle) * Math.Min(GameMain.GraphicsWidth * 0.4f, screenDist + 10),
(float)-Math.Sin(angle) * Math.Min(GameMain.GraphicsHeight * 0.4f, screenDist + 10));
angle = MathHelper.Lerp(originalAngle, angle, MathHelper.Clamp(((screenDist + 10f) - iconDiff.Length()) / 10f, 0f, 1f));
/*Vector2 unclampedDiff = new Vector2(
(float)Math.Cos(angle) * screenDist,
(float)-Math.Sin(angle) * screenDist);*/
iconDiff = new Vector2(
(float)Math.Cos(angle) * Math.Min(GameMain.GraphicsWidth * 0.4f, screenDist),
(float)-Math.Sin(angle) * Math.Min(GameMain.GraphicsHeight * 0.4f, screenDist));
Vector2 iconPos = cam.WorldToScreen(cam.WorldViewCenter) + iconDiff;
sprite.Draw(spriteBatch, iconPos, color * alpha, rotate: 0.0f, scale: symbolScale);
if (unclampedDiff.Length() - 10 > iconDiff.Length())
if (/*unclampedDiff.Length()*/ screenDist - 10 > iconDiff.Length())
{
Vector2 normalizedDiff = Vector2.Normalize(targetScreenPos - iconPos);
Vector2 arrowOffset = normalizedDiff * sprite.size.X * symbolScale * 0.7f;
@@ -1395,6 +1420,12 @@ namespace Barotrauma
}
}
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)
{
DrawIndicator(spriteBatch, worldPosition, cam, new Vector2(hideDist, float.PositiveInfinity), sprite, color, createOffset, scaleMultiplier, overrideAlpha);
}
public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color clr, float depth = 0.0f, float width = 1)
{
DrawLine(sb, t, start, end, clr, depth, (int)width);
@@ -1495,6 +1526,22 @@ namespace Barotrauma
}
}
public static void DrawFilledRectangle(SpriteBatch sb, Vector2 start, Vector2 size, Color clr, float depth = 0.0f)
{
if (size.X < 0)
{
start.X += size.X;
size.X = -size.X;
}
if (size.Y < 0)
{
start.Y += size.Y;
size.Y = -size.Y;
}
sb.Draw(t, start, null, clr, 0f, Vector2.Zero, size, SpriteEffects.None, depth);
}
public static void DrawRectangle(SpriteBatch sb, Vector2 center, float width, float height, float rotation, Color clr, float depth = 0.0f, float thickness = 1)
{
Matrix rotate = Matrix.CreateRotationZ(rotation);
@@ -1971,6 +2018,30 @@ namespace Barotrauma
}
return frame;
}
public static GUIMessageBox AskForConfirmation(string header, string body, Action onConfirm, Action onDeny = null)
{
string[] buttons = { TextManager.Get("Ok"), TextManager.Get("Cancel") };
GUIMessageBox msgBox = new GUIMessageBox(header, body, buttons, new Vector2(0.2f, 0.175f), minSize: new Point(300, 175));
// Cancel button
msgBox.Buttons[1].OnClicked = delegate
{
onDeny?.Invoke();
msgBox.Close();
return true;
};
// Ok button
msgBox.Buttons[0].OnClicked = delegate
{
onConfirm.Invoke();
msgBox.Close();
return true;
};
return msgBox;
}
#endregion
#region Element positioning
@@ -2098,7 +2169,7 @@ namespace Barotrauma
for (int j = i + 1; j < elements.Count; j++)
{
Rectangle rect2 = elements[j].Rect;
if (!rect1.Intersects(rect2)) continue;
if (!rect1.Intersects(rect2)) { continue; }
intersections = true;
Point centerDiff = rect1.Center - rect2.Center;
@@ -2127,10 +2198,10 @@ namespace Barotrauma
elements[j].RectTransform.ScreenSpaceOffset += moveAmount2.ToPoint();
}
if (disallowedAreas == null) continue;
if (disallowedAreas == null) { continue; }
foreach (Rectangle rect2 in disallowedAreas)
{
if (!rect1.Intersects(rect2)) continue;
if (!rect1.Intersects(rect2)) { continue; }
intersections = true;
Point centerDiff = rect1.Center - rect2.Center;
@@ -2148,7 +2219,7 @@ namespace Barotrauma
iterations++;
}
Vector2 ClampMoveAmount(Rectangle Rect, Rectangle clampTo, Vector2 moveAmount)
static Vector2 ClampMoveAmount(Rectangle Rect, Rectangle clampTo, Vector2 moveAmount)
{
if (Rect.Y < clampTo.Y)
{
@@ -2221,6 +2292,7 @@ namespace Barotrauma
}
};
bool IsOutpostLevel() => GameMain.GameSession != null && Level.IsLoadedOutpost;
if (Screen.Selected == GameMain.GameScreen && GameMain.GameSession != null)
{
if (GameMain.GameSession.GameMode is SinglePlayerCampaign spMode)
@@ -2253,45 +2325,22 @@ namespace Barotrauma
};
return true;
};
var saveAndQuitButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuSaveQuit"))
if (IsOutpostLevel())
{
UserData = "save"
};
saveAndQuitButton.OnClicked += (btn, userdata) =>
{
//Only allow saving mid-round in outpost levels. Quitting in the middle of a mission reset progress to the start of the round.
if (GameMain.GameSession == null)
var saveAndQuitButton = new GUIButton(new RectTransform(new Vector2(1.0f, 0.1f), buttonContainer.RectTransform), TextManager.Get("PauseMenuSaveQuit"))
{
pauseMenuOpen = false;
}
else if (GameMain.GameSession?.Campaign == null || Level.IsLoadedOutpost)
{
pauseMenuOpen = false;
GameMain.QuitToMainMenu(save: true);
}
else
{
var msgBox = new GUIMessageBox("", TextManager.Get("PauseMenuSaveAndQuitVerification", fallBackTag: "pausemenuquitverification"), new string[] { TextManager.Get("Yes"), TextManager.Get("Cancel") })
{
UserData = "verificationprompt"
};
msgBox.Buttons[0].OnClicked = (_, userdata) =>
UserData = "save",
OnClicked = (btn, userData) =>
{
pauseMenuOpen = false;
GameMain.QuitToMainMenu(save: false);
if (IsOutpostLevel())
{
GameMain.QuitToMainMenu(save: true);
}
return true;
};
msgBox.Buttons[0].OnClicked += msgBox.Close;
msgBox.Buttons[1].OnClicked = (_, userdata) =>
{
pauseMenuOpen = false;
msgBox.Close();
return true;
};
}
return true;
};
}
};
}
}
else if (GameMain.GameSession.GameMode is TestGameMode)
{
@@ -2313,7 +2362,7 @@ namespace Barotrauma
OnClicked = (btn, userdata) =>
{
if (!GameMain.Client.HasPermission(ClientPermissions.ManageRound)) { return false; }
if (GameMain.GameSession.GameMode is CampaignMode || (!Submarine.MainSub.AtStartExit && !Submarine.MainSub.AtEndExit))
if (GameMain.GameSession.GameMode is CampaignMode && !IsOutpostLevel() || (!Submarine.MainSub.AtStartExit && !Submarine.MainSub.AtEndExit))
{
var msgBox = new GUIMessageBox("",
TextManager.Get(GameMain.GameSession.GameMode is CampaignMode ? "PauseMenuReturnToServerLobbyVerification" : "EndRoundSubNotAtLevelEnd"),
@@ -201,7 +201,7 @@ namespace Barotrauma
{
base.ApplyStyle(style);
if (frame != null) { frame.ApplyStyle(style); }
frame?.ApplyStyle(style);
}
public override void Flash(Color? color = null, float flashDuration = 1.5f, bool useRectangleFlash = false, bool useCircularFlash = false, Vector2? flashRectInflate = null)
@@ -59,9 +59,7 @@ namespace Barotrauma
private bool useGridLayout;
private float targetScroll;
private GUIComponent pendingScroll;
private GUIComponent scrollToElement;
public bool AllowMouseWheelScroll { get; set; } = true;
@@ -238,8 +236,6 @@ namespace Barotrauma
public GUIComponent DraggedElement => draggedElement;
private bool scheduledScroll = false;
private readonly bool isHorizontal;
/// <param name="isScrollBarOnDefaultSide">For horizontal listbox, default side is on the bottom. For vertical, it's on the right.</param>
@@ -429,7 +425,14 @@ namespace Barotrauma
int index = children.IndexOf(component);
if (index < 0) { return; }
targetScroll = MathHelper.Clamp(MathHelper.Lerp(0, 1, MathUtils.InverseLerp(0, (children.Count - 0.9f), index)), ScrollBar.MinValue, ScrollBar.MaxValue);
if (!Content.Children.Contains(component) || !component.Visible)
{
scrollToElement = null;
}
else
{
scrollToElement = component;
}
}
public void ScrollToEnd(float duration)
@@ -533,7 +536,7 @@ namespace Barotrauma
}
}
if (SelectTop && Content.Children.Any() && pendingScroll == null)
if (SelectTop && Content.Children.Any() && scrollToElement == null)
{
GUIComponent component = Content.Children.FirstOrDefault(c => (c.Rect.Y - Content.Rect.Y) / (float)c.Rect.Height > -0.1f);
@@ -563,7 +566,6 @@ namespace Barotrauma
{
if (SelectTop)
{
pendingScroll = child;
ScrollToElement(child);
Select(i, autoScroll: false, takeKeyBoardFocus: true);
}
@@ -728,25 +730,29 @@ namespace Barotrauma
}
}
}
if (SmoothScroll)
{
if (targetScroll > -1)
{
float distance = Math.Abs(targetScroll - BarScroll);
float speed = Math.Max(distance * BarSize, 0.1f);
BarScroll = (1.0f - speed) * BarScroll + speed * targetScroll;
if (MathUtils.NearlyEqual(BarScroll, targetScroll) || GUIScrollBar.DraggingBar != null)
{
targetScroll = -1;
pendingScroll = null;
}
}
}
if (scrollToElement != null)
{
if (!scrollToElement.Visible || !Content.Children.Contains(scrollToElement))
{
scrollToElement = null;
}
else
{
float diff = isHorizontal ? scrollToElement.Rect.X - Content.Rect.X : scrollToElement.Rect.Y - Content.Rect.Y;
float speed = MathHelper.Clamp(Math.Abs(diff) * 0.1f, 5.0f, 100.0f);
System.Diagnostics.Debug.WriteLine(speed);
if (Math.Abs(diff) < speed || GUIScrollBar.DraggingBar != null)
{
speed = Math.Abs(diff);
scrollToElement = null;
}
BarScroll += speed * Math.Sign(diff) / TotalSize;
}
}
if ((GUI.IsMouseOn(this) || GUI.IsMouseOn(ScrollBar)) && AllowMouseWheelScroll && PlayerInput.ScrollWheelSpeed != 0)
{
float speed = PlayerInput.ScrollWheelSpeed / 500.0f * BarSize;
if (SmoothScroll)
{
if (ClampScrollToElements)
@@ -762,13 +768,6 @@ namespace Barotrauma
SelectNext(takeKeyBoardFocus: true);
}
}
else
{
pendingScroll = null;
if (targetScroll < 0) { targetScroll = BarScroll; }
targetScroll -= speed;
targetScroll = Math.Clamp(targetScroll, ScrollBar.MinValue, ScrollBar.MaxValue);
}
}
else
{
@@ -799,7 +798,6 @@ namespace Barotrauma
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
if (SmoothScroll)
{
pendingScroll = child;
ScrollToElement(child);
}
break;
@@ -819,7 +817,6 @@ namespace Barotrauma
Select(index, force, !SmoothScroll && autoScroll, takeKeyBoardFocus: takeKeyBoardFocus);
if (SmoothScroll)
{
pendingScroll = child;
ScrollToElement(child);
}
break;
@@ -551,7 +551,7 @@ namespace Barotrauma
}
if (openState >= 2.0f)
{
if (Parent != null) { Parent.RemoveChild(this); }
Parent?.RemoveChild(this);
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
}
@@ -604,7 +604,7 @@ namespace Barotrauma
}
else
{
if (Parent != null) { Parent.RemoveChild(this); }
Parent?.RemoveChild(this);
if (MessageBoxes.Contains(this)) { MessageBoxes.Remove(this); }
}
@@ -140,6 +140,7 @@ namespace Barotrauma
{
if (value == intValue) { return; }
intValue = value;
ClampIntValue();
UpdateText();
}
}
@@ -658,10 +658,7 @@ namespace Barotrauma
currentTextColor * (currentTextColor.A / 255.0f), 0.0f, origin, TextScale, SpriteEffects.None, textDepth, RichTextData);
}
if (Strikethrough != null)
{
Strikethrough.Draw(spriteBatch, (int)Math.Ceiling(TextSize.X / 2f), pos.X, ForceUpperCase ? pos.Y : pos.Y + GUI.Scale * 2f);
}
Strikethrough?.Draw(spriteBatch, (int)Math.Ceiling(TextSize.X / 2f), pos.X, ForceUpperCase ? pos.Y : pos.Y + GUI.Scale * 2f);
}
if (overflowClipActive)
@@ -49,8 +49,11 @@ namespace Barotrauma
get { return _caretIndex; }
set
{
_caretIndex = value;
caretPosDirty = true;
if (value >= 0)
{
_caretIndex = value;
caretPosDirty = true;
}
}
}
private bool caretPosDirty;
@@ -454,7 +457,7 @@ namespace Barotrauma
}
if (!isSelecting)
{
isSelecting = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift);
isSelecting = PlayerInput.IsShiftDown();
}
if (mouseHeldInside && !PlayerInput.PrimaryMouseButtonHeld())
@@ -879,15 +882,22 @@ namespace Barotrauma
selectionEndIndex = Math.Min(CaretIndex, textDrawn.Length);
selectionEndPos = caretPos;
selectedCharacters = Math.Abs(selectionStartIndex - selectionEndIndex);
if (IsLeftToRight)
try
{
selectedText = Text.Substring(selectionStartIndex, selectedCharacters);
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionStartIndex, selectedCharacters)) * TextBlock.TextScale;
if (IsLeftToRight)
{
selectedText = Text.Substring(selectionStartIndex, Math.Min(selectedCharacters, Text.Length));
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionStartIndex, Math.Min(selectedCharacters, textDrawn.Length))) * TextBlock.TextScale;
}
else
{
selectedText = Text.Substring(selectionEndIndex, Math.Min(selectedCharacters, Text.Length));
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionEndIndex, Math.Min(selectedCharacters, textDrawn.Length))) * TextBlock.TextScale;
}
}
else
catch (ArgumentOutOfRangeException exception)
{
selectedText = Text.Substring(selectionEndIndex, Math.Min(selectedCharacters, textDrawn.Length - selectionEndIndex));
selectionRectSize = Font.MeasureString(textDrawn.Substring(selectionEndIndex, selectedCharacters)) * TextBlock.TextScale;
DebugConsole.ThrowError($"GUITextBox: Invalid selection: ({exception})");
}
}
}
@@ -17,47 +17,63 @@ namespace Barotrauma
private readonly Dictionary<StoreTab, GUIListBox> tabLists = new Dictionary<StoreTab, GUIListBox>();
private readonly Dictionary<StoreTab, SortingMethod> tabSortingMethods = new Dictionary<StoreTab, SortingMethod>();
private readonly List<PurchasedItem> itemsToSell = new List<PurchasedItem>();
private readonly List<PurchasedItem> itemsToSellFromSub = new List<PurchasedItem>();
private StoreTab activeTab = StoreTab.Buy;
private MapEntityCategory? selectedItemCategory;
private bool suppressBuySell;
private int buyTotal, sellTotal;
private int buyTotal, sellTotal, sellFromSubTotal;
private GUITextBlock merchantBalanceBlock;
private GUITextBlock currentSellValueBlock, newSellValueBlock;
private GUIImage sellValueChangeArrow;
private GUIDropDown sortingDropDown;
private GUITextBox searchBox;
private GUIListBox storeBuyList, storeSellList;
private GUIListBox storeBuyList, storeSellList, storeSellFromSubList;
/// <summary>
/// Can be null when there are no deals at the current location
/// </summary>
private GUILayoutGroup storeDailySpecialsGroup, storeRequestedGoodGroup;
private GUILayoutGroup storeDailySpecialsGroup, storeRequestedGoodGroup, storeRequestedSubGoodGroup;
private Color storeSpecialColor;
private GUIListBox shoppingCrateBuyList, shoppingCrateSellList;
private GUIListBox shoppingCrateBuyList, shoppingCrateSellList, shoppingCrateSellFromSubList;
private GUITextBlock shoppingCrateTotal;
private GUIButton clearAllButton, confirmButton;
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh;
private bool needsRefresh, needsBuyingRefresh, needsSellingRefresh, needsItemsToSellRefresh, needsSellingFromSubRefresh, needsItemsToSellFromSubRefresh;
private Point resolutionWhenCreated;
private bool hadPermissions;
private Dictionary<ItemPrefab, int> OwnedItems { get; } = new Dictionary<ItemPrefab, int>();
private Dictionary<ItemPrefab, int> OwnedItems { get; } = new Dictionary<ItemPrefab, int>();
private CargoManager CargoManager => campaignUI.Campaign.CargoManager;
private Location CurrentLocation => campaignUI.Campaign.Map?.CurrentLocation;
private int PlayerMoney => campaignUI.Campaign.Money;
private bool HasPermissions => campaignUI.Campaign.AllowedToManageCampaign();
private bool IsBuying => activeTab != StoreTab.Sell;
private bool IsSelling => activeTab == StoreTab.Sell;
private GUIListBox ActiveShoppingCrateList => IsBuying ? shoppingCrateBuyList : shoppingCrateSellList;
private bool IsBuying => activeTab switch
{
StoreTab.Buy => true,
StoreTab.Sell => false,
StoreTab.SellFromSub => false,
_ => throw new NotImplementedException()
};
private bool IsSelling => !IsBuying;
private GUIListBox ActiveShoppingCrateList => activeTab switch
{
StoreTab.Buy => shoppingCrateBuyList,
StoreTab.Sell => shoppingCrateSellList,
StoreTab.SellFromSub => shoppingCrateSellFromSubList,
_ => throw new NotImplementedException()
};
private enum StoreTab
private bool IsTabUnavailable(StoreTab tab) => !tabLists.ContainsKey(tab);
public enum StoreTab
{
Buy,
Sell
Sell,
SellFromSub
}
private enum SortingMethod
@@ -73,11 +89,8 @@ namespace Barotrauma
{
this.campaignUI = campaignUI;
this.parentComponent = parentComponent;
hadPermissions = HasPermissions;
CreateUI();
campaignUI.Campaign.Map.OnLocationChanged += UpdateLocation;
if (CurrentLocation?.Reputation != null)
{
@@ -89,8 +102,10 @@ namespace Barotrauma
campaignUI.Campaign.CargoManager.OnSoldItemsChanged += () =>
{
needsItemsToSellRefresh = true;
needsItemsToSellFromSubRefresh = true;
needsRefresh = true;
};
campaignUI.Campaign.CargoManager.OnItemsInSellFromSubCrateChanged += () => { needsSellingFromSubRefresh = true; };
}
public void Refresh(bool updateOwned = true)
@@ -99,6 +114,7 @@ namespace Barotrauma
if (updateOwned) { UpdateOwnedItems(); }
RefreshBuying(updateOwned: false);
RefreshSelling(updateOwned: false);
RefreshSellingFromSub(updateOwned: false);
needsRefresh = false;
}
@@ -124,6 +140,20 @@ namespace Barotrauma
needsSellingRefresh = false;
}
private void RefreshSellingFromSub(bool updateOwned = true, bool updateItemsToSellFromSub = true)
{
if (IsTabUnavailable(StoreTab.SellFromSub)) { return; }
if (updateOwned) { UpdateOwnedItems(); }
if (updateItemsToSellFromSub) RefreshItemsToSellFromSub();
RefreshShoppingCrateSellFromSubList();
RefreshStoreSellFromSubList();
// TODO: Separate permissions from regular campaign permissions
var hasPermissions = HasPermissions;
storeSellFromSubList.Enabled = hasPermissions;
shoppingCrateSellFromSubList.Enabled = hasPermissions;
needsSellingFromSubRefresh = false;
}
private void CreateUI()
{
if (parentComponent.FindChild(c => c.UserData as string == "glow") is GUIComponent glowChild)
@@ -236,9 +266,13 @@ namespace Barotrauma
{
if (CurrentLocation != null)
{
int balanceAfterTransaction = IsBuying ?
CurrentLocation.StoreCurrentBalance + buyTotal :
CurrentLocation.StoreCurrentBalance - sellTotal;
int balanceAfterTransaction = activeTab switch
{
StoreTab.Buy => CurrentLocation.StoreCurrentBalance + buyTotal,
StoreTab.Sell => CurrentLocation.StoreCurrentBalance - sellTotal,
StoreTab.SellFromSub => CurrentLocation.StoreCurrentBalance - sellFromSubTotal,
_ => throw new NotImplementedException(),
};
if (balanceAfterTransaction != CurrentLocation.StoreCurrentBalance)
{
var newStatus = Location.GetStoreBalanceStatus(balanceAfterTransaction);
@@ -300,8 +334,14 @@ namespace Barotrauma
tabSortingMethods.Clear();
foreach (StoreTab tab in tabs)
{
if (tab == StoreTab.SellFromSub && GameMain.IsMultiplayer) { continue; }
string text = tab switch
{
StoreTab.SellFromSub => TextManager.Get("submarine"),
_ => TextManager.Get("campaignstoretab." + tab)
};
var tabButton = new GUIButton(new RectTransform(new Vector2(1.0f / (tabs.Length + 1), 1.0f), modeButtonContainer.RectTransform),
text: TextManager.Get("campaignstoretab." + tab), style: "GUITabButton")
text: text, style: "GUITabButton")
{
UserData = tab,
OnClicked = (button, userData) =>
@@ -416,6 +456,17 @@ namespace Barotrauma
storeRequestedGoodGroup = CreateDealsGroup(storeSellList);
tabLists.Add(StoreTab.Sell, storeSellList);
if (GameMain.IsSingleplayer)
{
storeSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, storeItemListContainer.RectTransform))
{
AutoHideScrollBar = false,
Visible = false
};
storeRequestedSubGoodGroup = CreateDealsGroup(storeSellFromSubList);
tabLists.Add(StoreTab.SellFromSub, storeSellFromSubList);
}
// Shopping Crate ------------------------------------------------------------------------------------------------------------------------------------------
var shoppingCrateContent = new GUILayoutGroup(new RectTransform(new Vector2(0.45f, 1.0f), campaignUI.GetTabContainer(CampaignMode.InteractionType.Store).RectTransform, anchor: Anchor.TopRight)
@@ -475,6 +526,10 @@ namespace Barotrauma
var shoppingCrateListContainer = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.85f), shoppingCrateInventoryContainer.RectTransform), style: null);
shoppingCrateBuyList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
shoppingCrateSellList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
if (GameMain.IsSingleplayer)
{
shoppingCrateSellFromSubList = new GUIListBox(new RectTransform(Vector2.One, shoppingCrateListContainer.RectTransform)) { Visible = false };
}
var totalContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.05f), shoppingCrateInventoryContainer.RectTransform), isHorizontal: true)
{
@@ -504,7 +559,13 @@ namespace Barotrauma
OnClicked = (button, userData) =>
{
if (!HasPermissions) { return false; }
var itemsToRemove = new List<PurchasedItem>(IsBuying ? CargoManager.ItemsInBuyCrate : CargoManager.ItemsInSellCrate);
var itemsToRemove = activeTab switch
{
StoreTab.Buy => new List<PurchasedItem>(CargoManager.ItemsInBuyCrate),
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
StoreTab.SellFromSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
_ => throw new NotImplementedException(),
};
itemsToRemove.ForEach(i => ClearFromShoppingCrate(i));
return true;
}
@@ -560,6 +621,7 @@ namespace Barotrauma
private void ChangeStoreTab(StoreTab tab)
{
if (IsTabUnavailable(tab)) { return; }
activeTab = tab;
foreach (GUIButton tabButton in storeTabButtons)
{
@@ -571,24 +633,56 @@ namespace Barotrauma
SetConfirmButtonBehavior();
SetConfirmButtonStatus();
FilterStoreItems();
if (tab == StoreTab.Buy)
switch (tab)
{
storeSellList.Visible = false;
storeBuyList.Visible = true;
shoppingCrateSellList.Visible = false;
shoppingCrateBuyList.Visible = true;
}
else if (tab == StoreTab.Sell)
{
storeBuyList.Visible = false;
storeSellList.Visible = true;
shoppingCrateBuyList.Visible = false;
shoppingCrateSellList.Visible = true;
case StoreTab.Buy:
storeSellList.Visible = false;
if (storeSellFromSubList != null)
{
storeSellFromSubList.Visible = false;
}
storeBuyList.Visible = true;
shoppingCrateSellList.Visible = false;
if (shoppingCrateSellFromSubList != null)
{
shoppingCrateSellFromSubList.Visible = false;
}
shoppingCrateBuyList.Visible = true;
break;
case StoreTab.Sell:
storeBuyList.Visible = false;
if (storeSellFromSubList != null)
{
storeSellFromSubList.Visible = false;
}
storeSellList.Visible = true;
shoppingCrateBuyList.Visible = false;
if (shoppingCrateSellFromSubList != null)
{
shoppingCrateSellFromSubList.Visible = false;
}
shoppingCrateSellList.Visible = true;
break;
case StoreTab.SellFromSub:
storeBuyList.Visible = false;
storeSellList.Visible = false;
if (storeSellFromSubList != null)
{
storeSellFromSubList.Visible = true;
}
shoppingCrateBuyList.Visible = false;
shoppingCrateSellList.Visible = false;
if (shoppingCrateSellFromSubList != null)
{
shoppingCrateSellFromSubList.Visible = true;
}
break;
}
}
private void FilterStoreItems(MapEntityCategory? category, string filter)
{
if (IsTabUnavailable(activeTab)) { return; }
selectedItemCategory = category;
var list = tabLists[activeTab];
filter = filter?.ToLower();
@@ -668,7 +762,7 @@ namespace Barotrauma
if (itemFrame == null)
{
var parentComponent = isDailySpecial ? storeDailySpecialsGroup : storeBuyList as GUIComponent;
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, quantity), priceInfo, parentComponent, forceDisable: !hasPermissions);
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, quantity), parentComponent, StoreTab.Buy, forceDisable: !hasPermissions);
}
else
{
@@ -688,7 +782,7 @@ namespace Barotrauma
removedItemFrames.AddRange(storeDailySpecialsGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
}
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
if (IsBuying) { FilterStoreItems(); }
if (activeTab == StoreTab.Buy) { FilterStoreItems(); }
SortItems(StoreTab.Buy);
storeBuyList.BarScroll = prevBuyListScroll;
@@ -743,7 +837,7 @@ namespace Barotrauma
if (itemFrame == null)
{
var parentComponent = isRequestedGood ? storeRequestedGoodGroup : storeSellList as GUIComponent;
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), priceInfo, parentComponent, forceDisable: !hasPermissions);
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), parentComponent, StoreTab.Sell, forceDisable: !hasPermissions);
}
else
{
@@ -766,13 +860,91 @@ namespace Barotrauma
removedItemFrames.AddRange(storeRequestedGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
}
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
if (IsSelling) { FilterStoreItems(); }
if (activeTab == StoreTab.Sell) { FilterStoreItems(); }
SortItems(StoreTab.Sell);
storeSellList.BarScroll = prevSellListScroll;
shoppingCrateSellList.BarScroll = prevShoppingCrateScroll;
}
private void RefreshStoreSellFromSubList()
{
float prevSellListScroll = storeSellFromSubList.BarScroll;
float prevShoppingCrateScroll = shoppingCrateSellFromSubList.BarScroll;
bool hasPermissions = HasPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
if ((storeRequestedSubGoodGroup != null) != CurrentLocation.RequestedGoods.Any())
{
if (storeRequestedSubGoodGroup == null)
{
storeRequestedSubGoodGroup = CreateDealsGroup(storeSellList);
storeRequestedSubGoodGroup.Parent.SetAsFirstChild();
}
else
{
storeSellFromSubList.RemoveChild(storeRequestedSubGoodGroup.Parent);
storeRequestedSubGoodGroup = null;
}
storeSellFromSubList.RecalculateChildren();
}
foreach (PurchasedItem item in itemsToSellFromSub)
{
CreateOrUpdateItemFrame(item.ItemPrefab, item.Quantity);
}
foreach (var requestedGood in CurrentLocation.RequestedGoods)
{
if (itemsToSellFromSub.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 ?
storeRequestedSubGoodGroup.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab) :
storeSellFromSubList.Content.FindChild(c => c.UserData is PurchasedItem pi && pi.ItemPrefab == itemPrefab);
if (CargoManager.ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem itemInSellFromSubCrate)
{
itemQuantity = Math.Max(itemQuantity - itemInSellFromSubCrate.Quantity, 0);
}
if (itemFrame == null)
{
var parentComponent = isRequestedGood ? storeRequestedSubGoodGroup : storeSellFromSubList as GUIComponent;
itemFrame = CreateItemFrame(new PurchasedItem(itemPrefab, itemQuantity), parentComponent, StoreTab.SellFromSub, forceDisable: !hasPermissions);
}
else
{
(itemFrame.UserData as PurchasedItem).Quantity = itemQuantity;
SetQuantityLabelText(StoreTab.SellFromSub, itemFrame);
SetOwnedLabelText(itemFrame);
SetPriceGetters(itemFrame, false);
}
SetItemFrameStatus(itemFrame, hasPermissions && itemQuantity > 0);
if (itemQuantity < 1 && !isRequestedGood)
{
itemFrame.Visible = false;
}
existingItemFrames.Add(itemFrame);
}
var removedItemFrames = storeSellFromSubList.Content.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList();
if (storeRequestedSubGoodGroup != null)
{
removedItemFrames.AddRange(storeRequestedSubGoodGroup.Children.Where(c => c.UserData is PurchasedItem).Except(existingItemFrames).ToList());
}
removedItemFrames.ForEach(f => f.RectTransform.Parent = null);
if (activeTab == StoreTab.SellFromSub) { FilterStoreItems(); }
SortItems(StoreTab.SellFromSub);
storeSellFromSubList.BarScroll = prevSellListScroll;
shoppingCrateSellFromSubList.BarScroll = prevShoppingCrateScroll;
}
private void SetPriceGetters(GUIComponent itemFrame, bool buying)
{
if (itemFrame == null || !(itemFrame.UserData is PurchasedItem pi)) { return; }
@@ -834,7 +1006,38 @@ namespace Barotrauma
needsItemsToSellRefresh = false;
}
private void RefreshShoppingCrateList(List<PurchasedItem> items, GUIListBox listBox)
public void RefreshItemsToSellFromSub()
{
itemsToSellFromSub.Clear();
var subItems = CargoManager.GetSellableItemsFromSub();
foreach (Item subItem in subItems)
{
if (itemsToSellFromSub.FirstOrDefault(i => i.ItemPrefab == subItem.Prefab) is PurchasedItem item)
{
item.Quantity += 1;
}
else if (subItem.Prefab.GetPriceInfo(CurrentLocation) != null)
{
itemsToSellFromSub.Add(new PurchasedItem(subItem.Prefab, 1));
}
}
// Remove items from sell crate if they aren't on the sub anymore
var itemsInCrate = new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate);
foreach (PurchasedItem crateItem in itemsInCrate)
{
var subItem = itemsToSellFromSub.Find(i => i.ItemPrefab == crateItem.ItemPrefab);
var subItemQuantity = subItem != null ? subItem.Quantity : 0;
if (crateItem.Quantity > subItemQuantity)
{
CargoManager.ModifyItemQuantityInSellFromSubCrate(crateItem.ItemPrefab, subItemQuantity - crateItem.Quantity);
}
}
sellableItemsFromSubUpdateTimer = 0.0f;
needsItemsToSellFromSubRefresh = false;
}
private void RefreshShoppingCrateList(List<PurchasedItem> items, GUIListBox listBox, StoreTab tab)
{
bool hasPermissions = HasPermissions;
HashSet<GUIComponent> existingItemFrames = new HashSet<GUIComponent>();
@@ -848,7 +1051,7 @@ namespace Barotrauma
GUINumberInput numInput = null;
if (itemFrame == null)
{
itemFrame = CreateItemFrame(item, priceInfo, listBox, forceDisable: !hasPermissions);
itemFrame = CreateItemFrame(item, listBox, tab, forceDisable: !hasPermissions);
numInput = itemFrame.FindChild(c => c is GUINumberInput, recursive: true) as GUINumberInput;
}
else
@@ -859,6 +1062,7 @@ namespace Barotrauma
{
numInput.UserData = item;
numInput.Enabled = hasPermissions;
numInput.MaxValueInt = GetMaxAvailable(item.ItemPrefab, tab);
}
SetOwnedLabelText(itemFrame);
SetItemFrameStatus(itemFrame, hasPermissions);
@@ -873,7 +1077,7 @@ namespace Barotrauma
}
suppressBuySell = false;
var price = listBox == shoppingCrateBuyList ?
var price = tab == StoreTab.Buy ?
CurrentLocation.GetAdjustedItemBuyPrice(item.ItemPrefab, priceInfo: priceInfo) :
CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
totalPrice += item.Quantity * price;
@@ -883,24 +1087,32 @@ namespace Barotrauma
removedItemFrames.ForEach(f => listBox.Content.RemoveChild(f));
SortItems(listBox, SortingMethod.CategoryAsc);
listBox.UpdateScrollBarSize();
if (listBox == shoppingCrateBuyList)
listBox.UpdateScrollBarSize();
switch (tab)
{
buyTotal = totalPrice;
if (IsBuying) { SetShoppingCrateTotalText(); }
case StoreTab.Buy:
buyTotal = totalPrice;
break;
case StoreTab.Sell:
sellTotal = totalPrice;
break;
case StoreTab.SellFromSub:
sellFromSubTotal = totalPrice;
break;
}
else
if (activeTab == tab)
{
sellTotal = totalPrice;
if(IsSelling) { SetShoppingCrateTotalText(); }
SetShoppingCrateTotalText();
}
SetClearAllButtonStatus();
SetConfirmButtonStatus();
}
private void RefreshShoppingCrateBuyList() => RefreshShoppingCrateList(CargoManager.ItemsInBuyCrate, shoppingCrateBuyList);
private void RefreshShoppingCrateBuyList() => RefreshShoppingCrateList(CargoManager.ItemsInBuyCrate, shoppingCrateBuyList, StoreTab.Buy);
private void RefreshShoppingCrateSellList() => RefreshShoppingCrateList(CargoManager.ItemsInSellCrate, shoppingCrateSellList);
private void RefreshShoppingCrateSellList() => RefreshShoppingCrateList(CargoManager.ItemsInSellCrate, shoppingCrateSellList, StoreTab.Sell);
private void RefreshShoppingCrateSellFromSubList() => RefreshShoppingCrateList(CargoManager.ItemsInSellFromSubCrate, shoppingCrateSellFromSubList, StoreTab.SellFromSub);
private void SortItems(GUIListBox list, SortingMethod sortingMethod)
{
@@ -932,7 +1144,7 @@ namespace Barotrauma
else if (sortingMethod == SortingMethod.PriceAsc || sortingMethod == SortingMethod.PriceDesc)
{
SortItems(list, SortingMethod.AlphabeticalAsc);
if (list == storeSellList || list == shoppingCrateSellList)
if (list != storeBuyList && list != shoppingCrateBuyList)
{
list.Content.RectTransform.SortChildren(CompareBySellPrice);
if (GetSpecialsGroup() is GUILayoutGroup specialsGroup)
@@ -1014,6 +1226,10 @@ namespace Barotrauma
{
return storeRequestedGoodGroup;
}
else if (list == storeSellFromSubList)
{
return storeRequestedSubGoodGroup;
}
else
{
return null;
@@ -1045,15 +1261,20 @@ namespace Barotrauma
private void SortItems(StoreTab tab, SortingMethod sortingMethod)
{
if (IsTabUnavailable(tab)) { return; }
tabSortingMethods[tab] = sortingMethod;
SortItems(tabLists[tab], sortingMethod);
}
private void SortItems(StoreTab tab) => SortItems(tab, tabSortingMethods[tab]);
private void SortItems(StoreTab tab)
{
if (IsTabUnavailable(tab)) { return; }
SortItems(tab, tabSortingMethods[tab]);
}
private void SortActiveTabItems(SortingMethod sortingMethod) => SortItems(activeTab, sortingMethod);
private GUIComponent CreateItemFrame(PurchasedItem pi, PriceInfo priceInfo, GUIComponent parentComponent, bool forceDisable = false)
private GUIComponent CreateItemFrame(PurchasedItem pi, GUIComponent parentComponent, StoreTab containingTab, bool forceDisable = false)
{
var tooltip = pi.ItemPrefab.Name;
if (!string.IsNullOrWhiteSpace(pi.ItemPrefab.Description))
@@ -1114,8 +1335,8 @@ namespace Barotrauma
CanBeFocused = false,
Stretch = true
};
var isSellingRelatedList = parentComponent == storeSellList || parentComponent == storeRequestedGoodGroup || parentComponent == shoppingCrateSellList;
var locationHasDealOnItem = isSellingRelatedList ?
bool isSellingRelatedList = containingTab != StoreTab.Buy;
bool 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)
@@ -1140,14 +1361,15 @@ namespace Barotrauma
};
dealIcon.SetAsFirstChild();
}
var isParentOnLeftSideOfInterface = parentComponent == storeBuyList || parentComponent == storeDailySpecialsGroup ||
parentComponent == storeSellList || parentComponent == storeRequestedGoodGroup;
bool isParentOnLeftSideOfInterface = parentComponent == storeBuyList || parentComponent == storeDailySpecialsGroup ||
parentComponent == storeSellList || parentComponent == storeRequestedGoodGroup ||
parentComponent == storeSellFromSubList || parentComponent == storeRequestedSubGoodGroup;
GUILayoutGroup shoppingCrateAmountGroup = null;
GUINumberInput amountInput = null;
if (isParentOnLeftSideOfInterface)
{
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.3f), nameAndQuantityGroup.RectTransform),
CreateQuantityLabelText(isSellingRelatedList ? StoreTab.Sell : StoreTab.Buy, pi.Quantity), font: GUI.Font, textAlignment: Alignment.BottomLeft)
CreateQuantityLabelText(containingTab, pi.Quantity), font: GUI.Font, textAlignment: Alignment.BottomLeft)
{
CanBeFocused = false,
Shadow = locationHasDealOnItem,
@@ -1156,7 +1378,7 @@ namespace Barotrauma
UserData = "quantitylabel"
};
}
else if (!isParentOnLeftSideOfInterface)
else
{
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) },
@@ -1167,7 +1389,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, isSellingRelatedList ? StoreTab.Sell : StoreTab.Buy),
MaxValueInt = GetMaxAvailable(pi.ItemPrefab, containingTab),
UserData = pi,
IntValue = pi.Quantity
};
@@ -1394,7 +1616,7 @@ namespace Barotrauma
}
}
private string CreateQuantityLabelText(StoreTab mode, int quantity) => mode == StoreTab.Sell ?
private string CreateQuantityLabelText(StoreTab mode, int quantity) => mode != StoreTab.Buy ?
TextManager.GetWithVariable("campaignstore.quantity", "[amount]", quantity.ToString()) :
TextManager.GetWithVariable("campaignstore.instock", "[amount]", quantity.ToString());
@@ -1417,10 +1639,16 @@ namespace Barotrauma
private int GetMaxAvailable(ItemPrefab itemPrefab, StoreTab mode)
{
var list = mode == StoreTab.Sell ? itemsToSell : CurrentLocation.StoreStock;
var list = mode switch
{
StoreTab.Buy => CurrentLocation.StoreStock,
StoreTab.Sell => itemsToSell,
StoreTab.SellFromSub => itemsToSellFromSub,
_ => throw new NotImplementedException()
};
if (list.Find(i => i.ItemPrefab == itemPrefab) is PurchasedItem item)
{
if (mode != StoreTab.Sell)
if (mode == StoreTab.Buy)
{
var purchasedItem = CargoManager.PurchasedItems.Find(i => i.ItemPrefab == item.ItemPrefab);
if (purchasedItem != null) { return Math.Max(item.Quantity - purchasedItem.Quantity, 0); }
@@ -1469,11 +1697,37 @@ namespace Barotrauma
return false;
}
private bool AddToShoppingCrate(PurchasedItem item, int quantity = 1) => IsBuying ?
ModifyBuyQuantity(item, quantity) : ModifySellQuantity(item, quantity);
private bool ModifySellFromSubQuantity(PurchasedItem item, int quantity)
{
if (item == null || item.ItemPrefab == null) { return false; }
if (!HasPermissions) { return false; }
if (quantity > 0)
{
// Make sure there's enough available to sell
var itemToSell = CargoManager.ItemsInSellFromSubCrate.Find(i => i.ItemPrefab == item.ItemPrefab);
var totalQuantityToSell = itemToSell != null ? itemToSell.Quantity + quantity : quantity;
if (totalQuantityToSell > GetMaxAvailable(item.ItemPrefab, StoreTab.SellFromSub)) { return false; }
}
CargoManager.ModifyItemQuantityInSellFromSubCrate(item.ItemPrefab, quantity);
// TODO: GameMain.Client?.SendCampaignState();
return false;
}
private bool ClearFromShoppingCrate(PurchasedItem item) => IsBuying ?
ModifyBuyQuantity(item, -item.Quantity) : ModifySellQuantity(item, -item.Quantity);
private bool AddToShoppingCrate(PurchasedItem item, int quantity = 1) => activeTab switch
{
StoreTab.Buy => ModifyBuyQuantity(item, quantity),
StoreTab.Sell => ModifySellQuantity(item, quantity),
StoreTab.SellFromSub => ModifySellFromSubQuantity(item, quantity),
_ => throw new NotImplementedException(),
};
private bool ClearFromShoppingCrate(PurchasedItem item) => activeTab switch
{
StoreTab.Buy => ModifyBuyQuantity(item, -item.Quantity),
StoreTab.Sell => ModifySellQuantity(item, -item.Quantity),
StoreTab.SellFromSub => ModifySellFromSubQuantity(item, -item.Quantity),
_ => throw new NotImplementedException(),
};
private bool BuyItems()
{
@@ -1510,18 +1764,17 @@ namespace Barotrauma
private bool SellItems()
{
if (!HasPermissions) { return false; }
var itemsToSell = new List<PurchasedItem>(CargoManager.ItemsInSellCrate);
var itemsToSell = activeTab switch
{
StoreTab.Sell => new List<PurchasedItem>(CargoManager.ItemsInSellCrate),
StoreTab.SellFromSub => new List<PurchasedItem>(CargoManager.ItemsInSellFromSubCrate),
_ => throw new NotImplementedException()
};
var itemsToRemove = new List<PurchasedItem>();
var totalValue = 0;
foreach (PurchasedItem item in itemsToSell)
{
if (item?.ItemPrefab == null)
{
itemsToRemove.Add(item);
continue;
}
if (item.ItemPrefab.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo)
if (item?.ItemPrefab?.GetPriceInfo(CurrentLocation) is PriceInfo priceInfo)
{
totalValue += item.Quantity * CurrentLocation.GetAdjustedItemSellPrice(item.ItemPrefab, priceInfo: priceInfo);
}
@@ -1531,12 +1784,13 @@ namespace Barotrauma
}
}
itemsToRemove.ForEach(i => itemsToSell.Remove(i));
if (itemsToSell.None() || totalValue > CurrentLocation.StoreCurrentBalance) { return false; }
CargoManager.SellItems(itemsToSell);
GameMain.Client?.SendCampaignState();
CargoManager.SellItems(itemsToSell, activeTab);
if (activeTab == StoreTab.Sell)
{
// TODO: Implement selling sub items in multiplayer
GameMain.Client?.SendCampaignState();
}
return false;
}
@@ -1549,8 +1803,14 @@ namespace Barotrauma
}
else
{
shoppingCrateTotal.Text = GetCurrencyFormatted(sellTotal);
shoppingCrateTotal.TextColor = CurrentLocation != null && sellTotal > CurrentLocation.StoreCurrentBalance ? Color.Red : Color.White;
int total = activeTab switch
{
StoreTab.Sell => sellTotal,
StoreTab.SellFromSub => sellFromSubTotal,
_ => throw new NotImplementedException(),
};
shoppingCrateTotal.Text = GetCurrencyFormatted(total);
shoppingCrateTotal.TextColor = CurrentLocation != null && total > CurrentLocation.StoreCurrentBalance ? Color.Red : Color.White;
}
}
@@ -1580,13 +1840,19 @@ namespace Barotrauma
private void SetConfirmButtonStatus() => confirmButton.Enabled =
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any() &&
((IsBuying && buyTotal <= PlayerMoney) || (IsSelling && CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance));
activeTab switch
{
StoreTab.Buy => buyTotal <= PlayerMoney,
StoreTab.Sell => CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance,
StoreTab.SellFromSub => CurrentLocation != null && sellFromSubTotal <= CurrentLocation.StoreCurrentBalance,
_ => throw new NotImplementedException(),
};
private void SetClearAllButtonStatus() => clearAllButton.Enabled =
HasPermissions && ActiveShoppingCrateList.Content.RectTransform.Children.Any();
private float ownedItemsUpdateTimer = 0.0f;
private readonly float ownedItemsUpdateInterval = 1.5f;
private float ownedItemsUpdateTimer = 0.0f, sellableItemsFromSubUpdateTimer = 0.0f;
private readonly float timerUpdateInterval = 1.5f;
public void Update(float deltaTime)
{
@@ -1598,7 +1864,7 @@ namespace Barotrauma
{
// Update the owned items at short intervals and check if the interface should be refreshed
ownedItemsUpdateTimer += deltaTime;
if (ownedItemsUpdateTimer >= ownedItemsUpdateInterval)
if (ownedItemsUpdateTimer >= timerUpdateInterval)
{
var prevOwnedItems = new Dictionary<ItemPrefab, int>(OwnedItems);
UpdateOwnedItems();
@@ -1612,12 +1878,21 @@ namespace Barotrauma
needsRefresh = true;
}
}
// Update the sellable sub items at short intervals and check if the interface should be refreshed
sellableItemsFromSubUpdateTimer += deltaTime;
if (sellableItemsFromSubUpdateTimer >= timerUpdateInterval)
{
needsItemsToSellFromSubRefresh = true;
needsRefresh = true;
}
}
if (needsItemsToSellRefresh) { RefreshItemsToSell(); }
if (needsItemsToSellFromSubRefresh) { RefreshItemsToSellFromSub(); }
if (needsRefresh || hadPermissions != HasPermissions) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
if (needsBuyingRefresh) { RefreshBuying(); }
if (needsSellingRefresh) { RefreshSelling(); }
if (needsSellingFromSubRefresh) { RefreshSellingFromSub(updateItemsToSellFromSub: sellableItemsFromSubUpdateTimer > 0.0f); }
}
}
}
@@ -12,7 +12,8 @@ namespace Barotrauma
private const int submarinesPerPage = 4;
private int currentPage = 1;
private int pageCount;
private bool transferService, purchaseService, initialized;
private readonly bool transferService, purchaseService;
private bool initialized;
private int deliveryFee;
private string deliveryLocationName;
@@ -27,12 +28,12 @@ namespace Barotrauma
private int selectionIndicatorThickness;
private GUIImage listBackground;
private List<SubmarineInfo> subsToShow;
private SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage];
private readonly List<SubmarineInfo> subsToShow;
private readonly SubmarineDisplayContent[] submarineDisplays = new SubmarineDisplayContent[submarinesPerPage];
private SubmarineInfo selectedSubmarine = null;
private string purchaseAndSwitchText, purchaseOnlyText, deliveryText, currentSubText, deliveryFeeText, priceText, switchText, missingPreviewText, currencyShorthandText, currencyLongText;
private RectTransform parent;
private Action closeAction;
private readonly RectTransform parent;
private readonly Action closeAction;
private Sprite pageIndicator;
public static readonly string[] DeliveryTextVariables = new string[] { "[submarinename1]", "[location1]", "[location2]", "[submarinename2]", "[amount]", "[currencyname]" };
@@ -42,7 +43,7 @@ namespace Barotrauma
private static readonly string[] notEnoughCreditsDeliveryTextVariables = new string[] { "[currencyname]", "[submarinename]", "[location1]", "[location2]" };
private static readonly string[] notEnoughCreditsPurchaseTextVariables = new string[] { "[currencyname]", "[submarinename]" };
private string[] messageBoxOptions;
private readonly string[] messageBoxOptions;
public const int DeliveryFeePerDistanceTravelled = 1000;
public static bool ContentRefreshRequired = false;
@@ -65,7 +66,7 @@ namespace Barotrauma
public SubmarineSelection(bool transfer, Action closeAction, RectTransform parent)
{
if (GameMain.GameSession.Campaign == null) return;
if (GameMain.GameSession.Campaign == null) { return; }
transferService = transfer;
purchaseService = !transfer;
@@ -83,7 +84,7 @@ namespace Barotrauma
messageBoxOptions = new string[2] { TextManager.Get("Yes") + " " + TextManager.Get("initiatevoting"), TextManager.Get("Cancel") };
}
if (Submarine.MainSub?.Info == null) return;
if (Submarine.MainSub?.Info == null) { return; }
Initialize();
}
@@ -184,8 +185,10 @@ namespace Barotrauma
for (int i = 0; i < submarineDisplays.Length; i++)
{
SubmarineDisplayContent submarineDisplayElement = new SubmarineDisplayContent();
submarineDisplayElement.background = new GUIFrame(new RectTransform(new Vector2(1f / submarinesPerPage, 1f), submarineHorizontalGroup.RectTransform), style: null, new Color(8, 13, 19));
SubmarineDisplayContent submarineDisplayElement = new SubmarineDisplayContent
{
background = new GUIFrame(new RectTransform(new Vector2(1f / submarinesPerPage, 1f), submarineHorizontalGroup.RectTransform), style: null, new Color(8, 13, 19))
};
submarineDisplayElement.submarineImage = new GUIImage(new RectTransform(new Vector2(0.8f, 1f), submarineDisplayElement.background.RectTransform, Anchor.Center), null, true);
submarineDisplayElement.middleTextBlock = new GUITextBlock(new RectTransform(new Vector2(0.8f, 1f), submarineDisplayElement.background.RectTransform, Anchor.Center), string.Empty, textAlignment: Alignment.Center);
submarineDisplayElement.submarineName = new GUITextBlock(new RectTransform(new Vector2(1f, 0.1f), submarineDisplayElement.background.RectTransform, Anchor.TopCenter, Pivot.TopCenter) { AbsoluteOffset = new Point(0, HUDLayoutSettings.Padding) }, string.Empty, textAlignment: Alignment.Center, font: GUI.SubHeadingFont);
@@ -433,7 +436,7 @@ namespace Barotrauma
private SubmarineInfo GetSubToDisplay(int index)
{
if (subsToShow.Count <= index || index < 0) return null;
if (subsToShow.Count <= index || index < 0) { return null; }
return subsToShow[index];
}
@@ -626,7 +629,6 @@ namespace Barotrauma
if (GameMain.Client == null)
{
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, deliveryFee);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub);
RefreshSubmarineDisplay(true);
}
else
@@ -661,7 +663,6 @@ namespace Barotrauma
{
GameMain.GameSession.PurchaseSubmarine(selectedSubmarine);
SubmarineInfo newSub = GameMain.GameSession.SwitchSubmarine(selectedSubmarine, 0);
GameMain.GameSession.Campaign.UpgradeManager.RefundResetAndReload(newSub);
RefreshSubmarineDisplay(true);
}
else
@@ -228,7 +228,10 @@ namespace Barotrauma
var crewButton = createTabButton(InfoFrameTab.Crew, "crew");
var missionButton = createTabButton(InfoFrameTab.Mission, "mission");
if (!(GameMain.GameSession?.GameMode is TestGameMode))
{
createTabButton(InfoFrameTab.Mission, "mission");
}
if (GameMain.GameSession?.GameMode is CampaignMode campaignMode)
{
@@ -903,51 +906,68 @@ namespace Barotrauma
infoFrame.ClearChildren();
GUIFrame missionFrame = new GUIFrame(new RectTransform(Vector2.One, infoFrame.RectTransform, Anchor.TopCenter), style: "GUIFrameListBox");
int padding = (int)(0.0245f * missionFrame.Rect.Height);
Location location = GameMain.GameSession.EndLocation != null ? GameMain.GameSession.EndLocation : GameMain.GameSession.StartLocation;
GUIFrame missionFrameContent = new GUIFrame(new RectTransform(new Point(missionFrame.Rect.Width - padding * 2, missionFrame.Rect.Height - padding * 2), infoFrame.RectTransform, Anchor.Center), style: null);
Location location = GameMain.GameSession.EndLocation ?? GameMain.GameSession.StartLocation;
GUILayoutGroup locationInfoContainer = new GUILayoutGroup(new RectTransform(new Vector2(1.0f, 0.3f), missionFrameContent.RectTransform))
{
AbsoluteSpacing = GUI.IntScale(10)
};
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Name, font: GUI.LargeFont);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), locationInfoContainer.RectTransform), location.Type.Name, font: GUI.SubHeadingFont);
var biomeLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
TextManager.Get("Biome", fallBackTag: "location"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), biomeLabel.RectTransform), Level.Loaded.LevelData.Biome.DisplayName, textAlignment: Alignment.CenterRight);
var difficultyLabel = new GUITextBlock(new RectTransform(new Vector2(0.5f, 0.0f), locationInfoContainer.RectTransform),
TextManager.Get("LevelDifficulty"), font: GUI.SubHeadingFont, textAlignment: Alignment.CenterLeft);
new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), difficultyLabel.RectTransform), ((int)Level.Loaded.LevelData.Difficulty) + " %", textAlignment: Alignment.CenterRight);
new GUIFrame(new RectTransform(new Vector2(1.0f, 0.01f), missionFrameContent.RectTransform) { AbsoluteOffset = new Point(0, locationInfoContainer.Rect.Height + padding) }, style: "HorizontalLine")
{
CanBeFocused = false
};
int locationInfoYOffset = locationInfoContainer.Rect.Height + padding * 2;
Sprite portrait = location.Type.GetPortrait(location.PortraitId);
bool hasPortrait = portrait != null && portrait.SourceRect.Width > 0 && portrait.SourceRect.Height > 0;
int contentWidth = hasPortrait ? (int)(missionFrame.Rect.Width * 0.951f) : missionFrame.Rect.Width - padding * 2;
Vector2 locationNameSize = GUI.LargeFont.MeasureString(location.Name);
Vector2 locationTypeSize = GUI.SubHeadingFont.MeasureString(location.Name);
GUITextBlock locationNameText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationNameSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, padding) }, location.Name, font: GUI.LargeFont);
GUITextBlock locationTypeText = new GUITextBlock(new RectTransform(new Point(contentWidth, (int)locationTypeSize.Y), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationNameText.Rect.Height + padding) }, location.Type.Name, font: GUI.SubHeadingFont);
int locationInfoYOffset = locationNameText.Rect.Height + locationTypeText.Rect.Height + padding * 2;
GUIListBox missionList;
int contentWidth = missionFrameContent.Rect.Width;
if (hasPortrait)
{
GUIFrame portraitHolder = new GUIFrame(new RectTransform(new Point(contentWidth, (int)(missionFrame.Rect.Height * 0.588f)), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
float portraitAspectRatio = portrait.SourceRect.Width / portrait.SourceRect.Height;
GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(1.0f, 1f), portraitHolder.RectTransform), portrait, scaleToFit: true);
portraitHolder.RectTransform.NonScaledSize = new Point(portraitImage.Rect.Size.X, (int)(portraitImage.Rect.Size.X / portraitAspectRatio));
GUIImage portraitImage = new GUIImage(new RectTransform(new Vector2(0.5f, 1f), locationInfoContainer.RectTransform, Anchor.CenterRight), portrait, scaleToFit: true)
{
IgnoreLayoutGroups = true
};
locationInfoContainer.Recalculate();
portraitImage.RectTransform.NonScaledSize = new Point(Math.Min((int)(portraitImage.Rect.Size.Y * portraitAspectRatio), portraitImage.Rect.Width), portraitImage.Rect.Size.Y);
}
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Bottom - portraitHolder.Rect.Bottom - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, portraitHolder.RectTransform.AbsoluteOffset.Y + portraitHolder.Rect.Height + padding) });
}
else
{
missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrame.Rect.Height - locationInfoYOffset - padding), missionFrame.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
}
GUIListBox missionList = new GUIListBox(new RectTransform(new Point(contentWidth, missionFrameContent.Rect.Height - locationInfoYOffset), missionFrameContent.RectTransform, Anchor.TopCenter) { AbsoluteOffset = new Point(0, locationInfoYOffset) });
missionList.ContentBackground.Color = Color.Transparent;
missionList.Spacing = GUI.IntScale(15);
if (GameMain.GameSession?.Missions != null)
{
int spacing = GUI.IntScale(5);
int iconSize = (int)(GUI.LargeFont.MeasureChar('T').Y + GUI.Font.MeasureChar('T').Y * 4 + spacing * 4);
foreach (Mission mission in GameMain.GameSession.Missions)
{
GUIFrame missionDescriptionHolder = new GUIFrame(new RectTransform(Vector2.One, missionList.Content.RectTransform), style: null);
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { RelativeOffset = new Vector2(0.225f, 0f) }, false, childAnchor: Anchor.TopLeft)
GUILayoutGroup missionTextGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.744f, 0f), missionDescriptionHolder.RectTransform, Anchor.CenterLeft) { AbsoluteOffset = new Point(iconSize + spacing, 0) }, false, childAnchor: Anchor.TopLeft)
{
AbsoluteSpacing = GUI.IntScale(5)
AbsoluteSpacing = spacing
};
string descriptionText = mission.Description;
foreach (string missionMessage in mission.ShownMessages)
{
descriptionText += "\n\n" + missionMessage;
}
string rewardText = mission.GetMissionRewardText();
string rewardText = mission.GetMissionRewardText(Submarine.MainSub);
string reputationText = mission.GetReputationRewardText(mission.Locations[0]);
var missionNameRichTextData = RichTextData.GetRichTextData(mission.Name, out string missionNameString);
@@ -974,12 +994,12 @@ namespace Barotrauma
if (mission.Prefab.Icon != null)
{
float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
/*float iconAspectRatio = mission.Prefab.Icon.SourceRect.Width / mission.Prefab.Icon.SourceRect.Height;
int iconWidth = (int)(0.225f * missionDescriptionHolder.RectTransform.NonScaledSize.X);
int iconHeight = Math.Max(missionTextGroup.RectTransform.NonScaledSize.Y, (int)(iconWidth * iconAspectRatio));
Point iconSize = new Point(iconWidth, iconHeight);
Point iconSize = new Point(iconWidth, iconHeight);*/
new GUIImage(new RectTransform(iconSize, missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
new GUIImage(new RectTransform(new Point(iconSize), missionDescriptionHolder.RectTransform), mission.Prefab.Icon, null, true)
{
Color = mission.Prefab.IconColor,
HoverColor = mission.Prefab.IconColor,
File diff suppressed because it is too large Load Diff