4 using System.Collections.Generic;
5 using System.Collections.Immutable;
6 using System.Diagnostics;
11 using Microsoft.Xna.Framework;
12 using Microsoft.Xna.Framework.Graphics;
13 using Microsoft.Xna.Framework.Input;
20 internal sealed
class UpgradeStore
25 public readonly List<UpgradePrefab>?
Prefabs;
28 public CategoryData(UpgradeCategory category, List<UpgradePrefab> prefabs)
35 public CategoryData(UpgradeCategory category, UpgradePrefab prefab)
45 private int PlayerBalance => Campaign?.
GetBalance() ?? 0;
46 private UpgradeTab selectedUpgradeTab = UpgradeTab.Upgrade;
50 public readonly
GUIFrame ItemInfoFrame;
61 private readonly List<UpgradeCategory> applicableCategories =
new List<UpgradeCategory>();
62 private Vector2[][] subHullVertices =
new Vector2[0][];
63 private List<Structure> submarineWalls =
new List<Structure>();
66 private bool highlightWalls;
68 private UpgradeCategory? currentUpgradeCategory;
69 private GUIButton? activeItemSwapSlideDown;
71 private readonly Dictionary<Item, GUIComponent> itemPreviews =
new Dictionary<Item, GUIComponent>();
73 private static readonly Color previewWhite = Color.White * 0.5f;
75 private Point screenResolution;
77 private bool needsRefresh =
true;
79 private PlayerBalanceElement? playerBalanceElement;
81 private static ImmutableHashSet<Character> characterList = ImmutableHashSet<Character>.Empty;
87 public static bool WaitForServerUpdate;
89 private enum UpgradeTab
95 private enum UpgradeStoreUserData
105 public UpgradeStore(CampaignUI campaignUI, GUIComponent parent)
107 WaitForServerUpdate =
false;
108 characterList = GameSession.GetSessionCrewCharacters(
CharacterType.Both);
109 this.campaignUI = campaignUI;
110 GUIFrame upgradeFrame =
new GUIFrame(rectT(1, 1, parent,
Anchor.Center), style:
"OuterGlow", color: Color.Black * 0.7f)
112 CanBeFocused =
false, UserData =
"outerglow"
115 ItemInfoFrame =
new GUIFrame(
new RectTransform(
new Vector2(0.13f, 0.13f), GUI.Canvas, minSize:
new Point(250, 150)), style:
"GUIToolTip")
120 CreateUI(upgradeFrame);
122 if (Campaign ==
null) {
return; }
123 Identifier eventId =
new Identifier(nameof(UpgradeStore));
127 Campaign.
OnMoneyChanged.RegisterOverwriteExisting(eventId, _ => RequestRefresh());
130 public void RequestRefresh()
135 private void RefreshAll()
137 characterList = GameSession.GetSessionCrewCharacters(
CharacterType.Both);
138 switch (selectedUpgradeTab)
140 case UpgradeTab.Repairs:
141 SelectTab(UpgradeTab.Repairs);
143 case UpgradeTab.Upgrade:
144 RefreshUpgradeList();
145 foreach (var itemPreview
in itemPreviews)
147 if (!(itemPreview.Value is GUIImage image) || itemPreview.Key ==
null) {
continue; }
148 if (itemPreview.Key.PendingItemSwap ==
null)
150 image.Sprite = itemPreview.Key.Prefab.UpgradePreviewSprite;
152 else if (itemPreview.Key.PendingItemSwap.UpgradePreviewSprite !=
null)
154 image.Sprite = itemPreview.Key.PendingItemSwap.UpgradePreviewSprite;
159 needsRefresh =
false;
162 private void RefreshUpgradeList()
164 if (Campaign ==
null) {
return; }
166 if (selectedUpgradeCategoryLayout?.Parent !=
null && selectedUpgradeCategoryLayout.
FindChild(
"prefablist",
true) is GUIListBox listBox)
168 foreach (var component
in listBox.Content.Children)
170 if (component.UserData is CategoryData { SinglePrefab: { } prefab} data)
172 UpdateUpgradeEntry(component, prefab, data.Category, Campaign);
175 if (customizeTabOpen && selectedUpgradeCategoryLayout !=
null &&
Submarine.MainSub !=
null && currentUpgradeCategory !=
null)
177 CreateSwappableItemList(listBox, currentUpgradeCategory,
Submarine.MainSub);
178 if (activeItemSwapSlideDown?.UserData is Item prevOpenedItem)
180 var currentButton = listBox.FindChild(c => c.UserData as Item == prevOpenedItem, recursive:
true) as GUIButton;
181 currentButton?.OnClicked(currentButton, prevOpenedItem);
187 if (currentStoreLayout?.Parent !=
null)
189 UpdateCategoryList(currentStoreLayout, Campaign, drawnSubmarine, applicableCategories);
195 public static void UpdateCategoryList(GUIListBox categoryList, CampaignMode campaign, Submarine? drawnSubmarine, IEnumerable<UpgradeCategory> applicableCategories)
197 var subItems = GetSubItems();
198 foreach (GUIComponent component
in categoryList.Content.Children)
200 if (!(component.UserData is CategoryData data)) {
continue; }
201 if (component.FindChild(
"indicators",
true) is { } indicators && data.Prefabs !=
null)
204 UpdateCategoryIndicators(indicators, component, data.Prefabs, data.Category, campaign, drawnSubmarine, applicableCategories);
206 var customizeButton = component.FindChild(
"customizebutton",
true);
207 if (customizeButton !=
null)
209 customizeButton.Visible = HasSwappableItems(data.Category, subItems);
214 foreach (UpgradeCategory category
in UpgradeCategory.Categories.OrderBy(c => c.Name))
216 GUIComponent component = categoryList.Content.FindChild(c => c.UserData is CategoryData categoryData && categoryData.Category == category);
217 component?.SetAsLastChild();
221 List<GUIComponent> lastChilds = categoryList.Content.Children.Where(component => !component.Enabled).ToList();
223 foreach (var lastChild
in lastChilds)
225 lastChild.SetAsLastChild();
254 private void CreateUI(GUIComponent parent)
256 selectedUpgradeTab = UpgradeTab.Upgrade;
257 parent.ClearChildren();
259 ItemInfoFrame.ClearChildren();
274 GUILayoutGroup tooltipLayout =
new GUILayoutGroup(rectT(0.95f,0.95f, ItemInfoFrame,
Anchor.Center)) { Stretch =
true };
275 new GUITextBlock(rectT(1, 0, tooltipLayout),
string.Empty, font: GUIStyle.SubHeadingFont) { UserData =
"itemname" };
276 new GUITextBlock(rectT(1, 0, tooltipLayout), TextManager.Get(
"UpgradeUITooltip.UpgradeListHeader"));
277 new GUIListBox(rectT(1, 0.5f, tooltipLayout), style:
null) { ScrollBarVisible =
false, AutoHideScrollBar =
false, SmoothScroll =
true, UserData =
"upgradelist"};
278 new GUITextBlock(rectT(1, 0, tooltipLayout),
string.Empty) { UserData =
"moreindicator" };
279 ItemInfoFrame.Children.ForEach(c => { c.CanBeFocused =
false; c.Children.ForEach(c2 => c2.CanBeFocused =
false); });
281 GUIFrame paddedLayout =
new GUIFrame(rectT(0.95f, 0.95f, parent,
Anchor.Center), style:
null);
282 mainStoreLayout =
new GUILayoutGroup(rectT(1, 0.9f, paddedLayout,
Anchor.BottomLeft), isHorizontal:
true) { RelativeSpacing = 0.01f };
283 topHeaderLayout =
new GUILayoutGroup(rectT(1, 0.1f, paddedLayout,
Anchor.TopLeft), isHorizontal:
true);
285 storeLayout =
new GUILayoutGroup(rectT(0.2f, 0.4f, mainStoreLayout), isHorizontal:
true) { RelativeSpacing = 0.02f };
295 GUILayoutGroup leftLayout =
new GUILayoutGroup(rectT(0.4f, 1, topHeaderLayout)) { RelativeSpacing = 0.05f };
296 GUILayoutGroup locationLayout =
new GUILayoutGroup(rectT(1, 0.5f, leftLayout), isHorizontal:
true);
297 GUIImage submarineIcon =
new GUIImage(rectT(
new Point(locationLayout.Rect.Height, locationLayout.Rect.Height), locationLayout), style:
"SubmarineIcon", scaleToFit:
true);
298 var header =
new GUITextBlock(rectT(1.0f - submarineIcon.RectTransform.RelativeSize.X, 1, locationLayout), TextManager.Get(
"UpgradeUI.Title"), font: GUIStyle.LargeFont);
299 header.RectTransform.MaxSize =
new Point((
int)(header.TextSize.X + header.Padding.X + header.Padding.Z),
int.MaxValue);
300 new GUITextBlock(rectT(1.0f, 1, locationLayout), TextManager.Get(
"UpgradeUI.AllSubmarinesInfo"), font: GUIStyle.SmallFont, wrap:
true);
302 categoryButtonLayout =
new GUILayoutGroup(rectT(0.4f, 0.3f, leftLayout), isHorizontal:
true) { Stretch =
true };
303 GUIButton upgradeButton =
new GUIButton(rectT(0.5f, 1f, categoryButtonLayout), TextManager.Get(
"UICategory.Upgrades"), style:
"GUITabButton") { UserData = UpgradeTab.Upgrade,
Selected = selectedUpgradeTab == UpgradeTab.Upgrade };
304 GUIButton repairButton =
new GUIButton(rectT(0.5f, 1f, categoryButtonLayout), TextManager.Get(
"UICategory.Maintenance"), style:
"GUITabButton") { UserData = UpgradeTab.Repairs,
Selected = selectedUpgradeTab == UpgradeTab.Repairs };
316 GUILayoutGroup rightLayout =
new GUILayoutGroup(rectT(0.5f, 1, topHeaderLayout), childAnchor:
Anchor.TopRight);
317 playerBalanceElement = CampaignUI.AddBalanceElement(rightLayout,
new Vector2(1.0f, 0.8f));
318 if (playerBalanceElement is { } balanceElement)
320 balanceElement.TotalBalanceContainer.OnAddedToGUIUpdateList += (_) =>
322 playerBalanceElement = CampaignUI.UpdateBalanceElement(playerBalanceElement);
325 new GUIFrame(rectT(0.5f, 0.1f, rightLayout,
Anchor.BottomRight), style:
"HorizontalLine") { IgnoreLayoutGroups =
true };
327 repairButton.OnClicked = upgradeButton.OnClicked = (button, o) =>
329 if (o is UpgradeTab upgradeTab)
331 if (upgradeTab != selectedUpgradeTab || currentStoreLayout ==
null || currentStoreLayout.
Parent != storeLayout)
333 selectedUpgradeTab = upgradeTab;
334 SelectTab(selectedUpgradeTab);
338 repairButton.Selected = (UpgradeTab) repairButton.UserData == selectedUpgradeTab;
339 upgradeButton.Selected = (UpgradeTab) upgradeButton.UserData == selectedUpgradeTab;
348 submarinePreviewComponent =
new GUICustomComponent(rectT(0.75f, 0.75f, mainStoreLayout,
Anchor.BottomRight), onUpdate: UpdateSubmarinePreview, onDraw: DrawSubmarine)
350 IgnoreLayoutGroups =
true
353 SelectTab(UpgradeTab.Upgrade);
355 var itemSwapPreview =
new GUICustomComponent(
new RectTransform(
new Vector2(0.25f, 0.4f), mainStoreLayout.
RectTransform,
Anchor.TopLeft)
356 { RelativeOffset = new Vector2(0.52f * GUI.AspectRatioAdjustment, 0.0f) }, DrawItemSwapPreview)
358 IgnoreLayoutGroups =
true,
362 GUITextBlock.AutoScaleAndNormalize(upgradeButton.TextBlock, repairButton.TextBlock);
366 CreateRefreshButton();
367 void CreateRefreshButton()
369 new GUIButton(rectT(0.2f, 0.1f, parent,
Anchor.TopCenter),
"Recreate UI - NOT PRESENT IN RELEASE!")
371 OnClicked = (button, o) =>
381 private void DrawItemSwapPreview(SpriteBatch spriteBatch, GUICustomComponent component)
383 var selectedItem = customizeTabOpen ?
385 HoveredEntity as
Item;
386 if (selectedItem?.Prefab.SwappableItem ==
null) {
return; }
388 Sprite schematicsSprite = selectedItem.Prefab.SwappableItem.SchematicSprite;
389 if (schematicsSprite ==
null) {
return; }
390 float schematicsScale = Math.Min(component.Rect.Width / 2 / schematicsSprite.size.X, component.Rect.Height / schematicsSprite.size.Y);
391 Vector2 center =
new Vector2(component.Rect.Center.X, component.Rect.Center.Y);
392 schematicsSprite.Draw(spriteBatch,
new Vector2(component.Rect.X, center.Y), GUIStyle.Green,
new Vector2(0, schematicsSprite.size.Y / 2),
393 scale: schematicsScale);
395 var swappableItemList = selectedUpgradeCategoryLayout?.
FindChild(
"prefablist",
true) as GUIListBox;
396 var highlightedElement = swappableItemList?.Content.
FindChild(c => c.UserData is ItemPrefab && c.IsParentOf(GUI.MouseOn)) ?? GUI.MouseOn;
397 ItemPrefab swapTo = highlightedElement?.
UserData as ItemPrefab ?? selectedItem.PendingItemSwap;
398 if (swapTo?.SwappableItem ==
null) {
return; }
399 Sprite? schematicsSprite2 = swapTo.SwappableItem?.SchematicSprite;
400 schematicsSprite2?.Draw(spriteBatch,
new Vector2(component.Rect.Right, center.Y), GUIStyle.Orange,
new Vector2(schematicsSprite2.size.X, schematicsSprite2.size.Y / 2),
401 scale: Math.Min(component.Rect.Width / 2 / schematicsSprite2.size.X, component.Rect.Height / schematicsSprite2.size.Y));
403 var arrowSprite = GUIStyle.GetComponentStyle(
"GUIButtonToggleRight")?.GetDefaultSprite();
404 if (arrowSprite !=
null)
406 arrowSprite.Draw(spriteBatch, center, scale: GUI.Scale);
410 private void SelectTab(UpgradeTab tab)
412 if (currentStoreLayout !=
null)
417 if (selectedUpgradeCategoryLayout !=
null)
419 mainStoreLayout?.
RemoveChild(selectedUpgradeCategoryLayout);
424 case UpgradeTab.Upgrade:
429 case UpgradeTab.Repairs:
437 private void CreateRepairsTab()
439 if (Campaign ==
null || storeLayout ==
null) {
return; }
441 highlightWalls =
false;
442 foreach (GUIComponent itemFrame
in itemPreviews.Values)
444 itemFrame.OutlineColor = previewWhite;
447 currentStoreLayout =
new GUIListBox(
new RectTransform(
new Vector2(1.2f, 1.5f), storeLayout.
RectTransform) { MinSize = new Point(256, 0) }, style:
null)
449 AutoHideScrollBar =
false,
450 ScrollBarVisible =
false,
456 int hullRepairCost = CampaignMode.GetHullRepairCost();
457 int itemRepairCost = CampaignMode.GetItemRepairCost();
458 int shuttleRetrieveCost = CampaignMode.ShuttleReplaceCost;
459 if (location !=
null)
462 itemRepairCost = location.GetAdjustedMechanicalCost(itemRepairCost);
463 shuttleRetrieveCost = location.GetAdjustedMechanicalCost(shuttleRetrieveCost);
466 CreateRepairEntry(currentStoreLayout.
Content, TextManager.Get(
"repairallwalls"),
"RepairHullButton", hullRepairCost, (button, o) =>
469 if (Campaign.PurchasedHullRepairs || hullRepairCost <= 0)
471 button.Enabled = false;
475 if (PlayerBalance >= hullRepairCost)
477 LocalizedString body = TextManager.GetWithVariable(
"WallRepairs.PurchasePromptBody",
"[amount]", hullRepairCost.ToString());
478 currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get(
"Upgrades.PurchasePromptTitle"), body, () =>
480 if (PlayerBalance >= hullRepairCost)
482 Campaign.TryPurchase(null, hullRepairCost);
483 GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service,
"hullrepairs");
484 Campaign.PurchasedHullRepairs = true;
485 button.Enabled = false;
486 SelectTab(UpgradeTab.Repairs);
487 GameMain.Client?.SendCampaignState();
491 button.Enabled = false;
494 }, overrideConfirmButtonSound:
GUISoundType.ConfirmTransaction);
504 highlightWalls = isHovered;
508 CreateRepairEntry(currentStoreLayout.
Content, TextManager.Get(
"repairallitems"),
"RepairItemsButton", itemRepairCost, (button, o) =>
511 if (PlayerBalance >= itemRepairCost && !Campaign.PurchasedItemRepairs && itemRepairCost > 0)
513 LocalizedString body = TextManager.GetWithVariable(
"ItemRepairs.PurchasePromptBody",
"[amount]", itemRepairCost.ToString());
514 currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get(
"Upgrades.PurchasePromptTitle"), body, () =>
516 if (PlayerBalance >= itemRepairCost && !Campaign.PurchasedItemRepairs)
518 Campaign.TryPurchase(null, itemRepairCost);
519 GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service,
"devicerepairs");
520 Campaign.PurchasedItemRepairs = true;
521 button.Enabled = false;
522 SelectTab(UpgradeTab.Repairs);
523 GameMain.Client?.SendCampaignState();
527 button.Enabled = false;
530 }, overrideConfirmButtonSound: GUISoundType.ConfirmTransaction);
534 button.Enabled = false;
540 foreach (var (item, itemFrame) in itemPreviews)
542 itemFrame.OutlineColor = itemFrame.Color = isHovered && item.GetComponent<
DockingPort>() ==
null ? GUIStyle.Orange : previewWhite;
547 CreateRepairEntry(currentStoreLayout.
Content, TextManager.Get(
"replacelostshuttles"),
"ReplaceShuttlesButton", shuttleRetrieveCost, (button, o) =>
549 if (GameMain.GameSession?.SubmarineInfo != null &&
550 GameMain.GameSession.SubmarineInfo.LeftBehindSubDockingPortOccupied)
552 new GUIMessageBox(
"", TextManager.Get(
"ReplaceShuttleDockingPortOccupied"));
558 LocalizedString body = TextManager.GetWithVariable(
"ReplaceLostShuttles.PurchasePromptBody",
"[amount]", shuttleRetrieveCost.ToString());
559 currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get(
"Upgrades.PurchasePromptTitle"), body, () =>
561 if (PlayerBalance >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
563 Campaign.TryPurchase(null, shuttleRetrieveCost);
564 GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service,
"retrieveshuttle");
565 Campaign.PurchasedLostShuttles = true;
566 button.Enabled = false;
567 SelectTab(UpgradeTab.Repairs);
568 GameMain.Client?.SendCampaignState();
571 }, overrideConfirmButtonSound:
GUISoundType.ConfirmTransaction);
575 button.Enabled =
false;
580 }, Campaign.
PurchasedLostShuttles || !HasPermission || GameMain.GameSession?.SubmarineInfo ==
null || !GameMain.GameSession.SubmarineInfo.SubsLeftBehind, isHovered =>
582 if (!isHovered) {
return false; }
583 if (!(GameMain.GameSession?.SubmarineInfo is { } subInfo)) {
return false; }
585 foreach (var (item, itemFrame) in itemPreviews)
587 if (subInfo.LeftBehindDockingPortIDs.Contains(item.ID))
589 itemFrame.OutlineColor = itemFrame.Color = subInfo.BlockedDockingPortIDs.Contains(item.ID) ? GUIStyle.Red : GUIStyle.Green;
593 itemFrame.OutlineColor = itemFrame.Color = previewWhite;
597 }, disableElement:
true);
600 private void CreateRepairEntry(GUIComponent parent, LocalizedString title,
string imageStyle,
int price, GUIButton.OnClickedHandler onPressed,
bool isDisabled, Func<bool, bool>? onHover =
null,
bool disableElement =
false)
602 GUIFrame frameChild =
new GUIFrame(rectT(
new Point(parent.Rect.Width, (
int) (96 * GUI.Scale)), parent), style:
"UpgradeUIFrame");
603 frameChild.SelectedColor = frameChild.Color;
606 new GUICustomComponent(rectT(1, 1, frameChild), onUpdate: UpdateHover) { CanBeFocused =
false };
615 GUILayoutGroup contentLayout =
new GUILayoutGroup(rectT(0.9f, 0.85f, frameChild,
Anchor.Center), isHorizontal:
true);
616 var repairIcon =
new GUIFrame(rectT(
new Point(contentLayout.Rect.Height, contentLayout.Rect.Height), contentLayout), style: imageStyle);
617 GUILayoutGroup textLayout =
new GUILayoutGroup(rectT(0.8f - repairIcon.RectTransform.RelativeSize.X, 1, contentLayout)) { Stretch =
true };
618 new GUITextBlock(rectT(1, 0, textLayout), title, font: GUIStyle.SubHeadingFont) { CanBeFocused =
false, AutoScaleHorizontal =
true };
619 new GUITextBlock(rectT(1, 0, textLayout), TextManager.FormatCurrency(price));
620 GUILayoutGroup buyButtonLayout =
new GUILayoutGroup(rectT(0.2f, 1, contentLayout), childAnchor:
Anchor.Center) { UserData = UpgradeStoreUserData.BuyButtonLayout };
621 new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout),
string.Empty, style:
"RepairBuyButton") { Enabled = PlayerBalance >= price && !isDisabled, OnClicked = onPressed };
622 contentLayout.Recalculate();
623 buyButtonLayout.Recalculate();
627 frameChild.Enabled = PlayerBalance >= price && !isDisabled;
632 frameChild.Enabled =
false;
635 void UpdateHover(
float deltaTime, GUICustomComponent component)
637 onHover?.Invoke(GUI.MouseOn !=
null && frameChild.IsParentOf(GUI.MouseOn) || GUI.MouseOn == frameChild);
642 public static GUIListBox CreateUpgradeCategoryList(RectTransform rectTransform)
644 var upgradeCategoryList =
new GUIListBox(rectTransform, style:
null)
646 AutoHideScrollBar =
false,
647 ScrollBarVisible =
false,
648 HideChildrenOutsideFrame =
false,
653 ClampScrollToElements =
true,
655 PlaySoundOnSelect =
true
658 Dictionary<UpgradeCategory, List<UpgradePrefab>> upgrades =
new Dictionary<UpgradeCategory, List<UpgradePrefab>>();
660 foreach (UpgradeCategory category
in UpgradeCategory.Categories.OrderBy(c => c.Name))
662 foreach (UpgradePrefab prefab
in UpgradePrefab.Prefabs.OrderBy(p => p.Name))
664 if (prefab.UpgradeCategories.Contains(category))
666 if (upgrades.ContainsKey(category))
668 upgrades[category].Add(prefab);
672 upgrades.Add(category,
new List<UpgradePrefab> { prefab });
678 foreach (var (category, prefabs) in upgrades)
680 var frameChild =
new GUIFrame(rectT(1, 0.15f, upgradeCategoryList.Content), style:
"UpgradeUIFrame")
682 UserData =
new CategoryData(category, prefabs),
686 frameChild.DefaultColor = frameChild.Color;
687 frameChild.Color = Color.Transparent;
689 var weaponSwitchBg =
new GUIButton(
new RectTransform(
new Vector2(0.65f), frameChild.RectTransform,
Anchor.TopRight, scaleBasis:
ScaleBasis.Smallest)
690 { RelativeOffset = new Vector2(0.04f, 0.0f) }, style:
"WeaponSwitchTab")
693 CanBeSelected =
false,
694 UserData =
"customizebutton"
696 weaponSwitchBg.DefaultColor = weaponSwitchBg.Frame.DefaultColor = weaponSwitchBg.Color;
697 var weaponSwitchImg =
new GUIImage(
new RectTransform(
new Vector2(0.7f), weaponSwitchBg.RectTransform,
Anchor.Center),
"WeaponSwitchIcon", scaleToFit:
true)
701 weaponSwitchImg.DefaultColor = weaponSwitchImg.Color;
710 GUILayoutGroup contentLayout =
new GUILayoutGroup(rectT(0.9f, 0.85f, frameChild,
Anchor.Center));
711 var itemCategoryLabel =
new GUITextBlock(rectT(1, 1, contentLayout), category.Name, font: GUIStyle.SubHeadingFont) { CanBeFocused =
false };
712 GUILayoutGroup indicatorLayout =
new GUILayoutGroup(rectT(0.5f, 0.25f, contentLayout,
Anchor.BottomRight), isHorizontal:
true, childAnchor:
Anchor.TopRight) { UserData =
"indicators", IgnoreLayoutGroups =
true, RelativeSpacing = 0.01f };
714 foreach (var prefab
in prefabs)
716 GUIImage upgradeIndicator =
new GUIImage(rectT(0.1f, 1f, indicatorLayout), style:
"UpgradeIndicator", scaleToFit:
true) { UserData = prefab, CanBeFocused =
false };
717 upgradeIndicator.DefaultColor = upgradeIndicator.Color;
718 upgradeIndicator.Color = Color.Transparent;
721 itemCategoryLabel.DefaultColor = itemCategoryLabel.TextColor;
722 itemCategoryLabel.TextColor = Color.Transparent;
724 contentLayout.Recalculate();
725 indicatorLayout.Recalculate();
728 return upgradeCategoryList;
731 private void CreateUpgradeTab()
733 if (storeLayout ==
null || mainStoreLayout ==
null) {
return; }
734 currentStoreLayout = CreateUpgradeCategoryList(rectT(1.0f, 1.5f, storeLayout));
736 selectedUpgradeCategoryLayout =
new GUIFrame(rectT(0.3f * GUI.AspectRatioAdjustment, 1, mainStoreLayout), style:
null) { CanBeFocused =
false };
738 RefreshUpgradeList();
740 currentStoreLayout.
OnSelected += (component, userData) =>
742 if (!component.Enabled)
745 foreach (GUIComponent itemFrame
in itemPreviews.Values)
747 itemFrame.OutlineColor = itemFrame.Color = previewWhite;
748 itemFrame.Children.ForEach(c => c.Color = itemFrame.Color);
753 if (userData is CategoryData categoryData &&
Submarine.MainSub is { } sub && categoryData.Prefabs is { } prefabs)
755 TrySelectCategory(prefabs, categoryData.Category, sub);
758 var customizeCategoryButton = selectedUpgradeCategoryLayout?.
FindChild(
"customizebutton", recursive:
true) as GUIButton;
759 customizeCategoryButton?.OnClicked(customizeCategoryButton, customizeCategoryButton.UserData);
766 private void TrySelectCategory(List<UpgradePrefab> prefabs, UpgradeCategory category, Submarine submarine) => SelectUpgradeCategory(prefabs, category, submarine);
768 private bool customizeTabOpen;
770 private static bool HasSwappableItems(UpgradeCategory category, List<Item>? subItems =
null)
772 if (
Submarine.MainSub ==
null) {
return false; }
773 subItems ??= GetSubItems();
774 return subItems.Any(i =>
775 i.Prefab.SwappableItem !=
null &&
776 !i.IsHidden && i.AllowSwapping &&
777 (i.Prefab.SwappableItem.CanBeBought || ItemPrefab.Prefabs.Any(ip => ip.SwappableItem?.ReplacementOnUninstall == i.Prefab.Identifier)) &&
778 Submarine.MainSub.IsEntityFoundOnThisSub(i,
true) && category.ItemTags.Any(t => i.HasTag(t)));
781 private static List<Item> GetSubItems() =>
Submarine.MainSub?.GetItems(
true) ??
new List<Item>();
783 private void SelectUpgradeCategory(List<UpgradePrefab> prefabs, UpgradeCategory category, Submarine submarine)
785 if (selectedUpgradeCategoryLayout ==
null) {
return; }
787 customizeTabOpen =
false;
789 GUIComponent[] categoryFrames = GetFrames(category);
790 foreach (GUIComponent itemFrame
in itemPreviews.Values)
792 itemFrame.OutlineColor = itemFrame.Color = categoryFrames.Contains(itemFrame) ? GUIStyle.Orange : previewWhite;
793 itemFrame.Children.ForEach(c => c.Color = itemFrame.Color);
796 highlightWalls = category.IsWallUpgrade;
799 GUIFrame frame =
new GUIFrame(rectT(1.0f, 0.4f, selectedUpgradeCategoryLayout));
800 GUIFrame paddedFrame =
new GUIFrame(rectT(0.93f, 0.9f, frame,
Anchor.Center), style:
null);
802 bool hasSwappableItems = HasSwappableItems(category);
804 float listHeight = hasSwappableItems ? 0.9f : 1.0f;
806 GUIListBox prefabList =
new GUIListBox(rectT(1.0f, listHeight, paddedFrame,
Anchor.BottomLeft))
808 UserData =
"prefablist",
809 AutoHideScrollBar =
false,
810 ScrollBarVisible =
true
813 if (hasSwappableItems)
815 GUILayoutGroup buttonLayout =
new GUILayoutGroup(rectT(1.0f, 0.1f, paddedFrame, anchor:
Anchor.TopLeft), isHorizontal:
true);
817 GUIButton customizeButton =
new GUIButton(rectT(0.5f, 1f, buttonLayout), text: TextManager.Get(
"uicategory.customize"), style:
"GUITabButton")
819 UserData =
"customizebutton"
821 new GUIImage(
new RectTransform(
new Vector2(1.0f, 0.75f), customizeButton.RectTransform,
Anchor.CenterLeft, scaleBasis:
ScaleBasis.Smallest) { RelativeOffset = new Vector2(0.015f, 0.0f) },
"WeaponSwitchIcon", scaleToFit:
true);
822 customizeButton.TextBlock.RectTransform.RelativeSize =
new Vector2(0.7f, 1.0f);
824 GUIButton upgradeButton =
new GUIButton(rectT(0.5f, 1f, buttonLayout), text: TextManager.Get(
"uicategory.upgrades"), style:
"GUITabButton")
829 GUITextBlock.AutoScaleAndNormalize(upgradeButton.TextBlock, customizeButton.TextBlock);
831 upgradeButton.OnClicked = delegate
833 customizeTabOpen =
false;
834 customizeButton.Selected =
false;
835 upgradeButton.Selected =
true;
836 CreateUpgradePrefabList(prefabList, category, prefabs, submarine);
837 GUIComponent[] categoryFrames = GetFrames(category);
838 foreach (GUIComponent itemFrame
in itemPreviews.Values)
840 itemFrame.OutlineColor = itemFrame.Color = categoryFrames.Contains(itemFrame) ? GUIStyle.Orange : previewWhite;
841 itemFrame.Children.ForEach(c => c.Color = itemFrame.Color);
846 customizeButton.OnClicked = delegate
848 customizeTabOpen =
true;
849 customizeButton.Selected =
true;
850 upgradeButton.Selected =
false;
851 CreateSwappableItemList(prefabList, category, submarine);
856 CreateUpgradePrefabList(prefabList, category, prefabs, submarine);
859 private void CreateUpgradePrefabList(GUIListBox parent, UpgradeCategory category, List<UpgradePrefab> prefabs, Submarine submarine)
861 parent.Content.ClearChildren();
862 List<Item>? entitiesOnSub =
null;
863 if (!category.IsWallUpgrade)
865 entitiesOnSub = submarine.GetItems(
true).Where(i => submarine.IsEntityFoundOnThisSub(i,
true)).ToList();
868 foreach (UpgradePrefab prefab
in prefabs)
870 if (prefab.GetMaxLevelForCurrentSub() == 0) {
continue; }
871 CreateUpgradeEntry(prefab, category, parent.Content, submarine, entitiesOnSub);
875 private void CreateSwappableItemList(GUIListBox parent, UpgradeCategory category, Submarine submarine)
877 parent.Content.ClearChildren();
878 currentUpgradeCategory = category;
879 var entitiesOnSub = submarine.GetItems(
true).Where(i => submarine.IsEntityFoundOnThisSub(i,
true) && !i.IsHidden && i.AllowSwapping && i.Prefab.SwappableItem !=
null && category.ItemTags.Any(t => i.HasTag(t))).ToList();
881 foreach (Item item
in entitiesOnSub)
883 CreateSwappableItemSlideDown(parent, item, entitiesOnSub, submarine);
887 private void CreateSwappableItemSlideDown(GUIListBox parent, Item item, List<Item> swappableEntities, Submarine submarine)
889 if (Campaign ==
null || submarine ==
null) {
return; }
891 IEnumerable<ItemPrefab> availableReplacements = MapEntityPrefab.List.Where(p =>
892 p is ItemPrefab itemPrefab &&
893 itemPrefab.SwappableItem !=
null &&
894 itemPrefab.SwappableItem.CanBeBought &&
895 itemPrefab.SwappableItem.SwapIdentifier.Equals(item.Prefab.SwappableItem.SwapIdentifier, StringComparison.OrdinalIgnoreCase)).Cast<ItemPrefab>();
897 var linkedItems = UpgradeManager.GetLinkedItemsToSwap(item) ??
new List<Item>() { item };
899 if (linkedItems.Min(it => it.ID) < item.ID) {
return; }
901 var currentOrPending = item.PendingItemSwap ?? item.Prefab;
902 LocalizedString name = currentOrPending.Name;
903 LocalizedString nameWithQuantity =
"";
904 if (linkedItems.Count > 1)
906 foreach (ItemPrefab distinctItem
in linkedItems.Select(it => it.Prefab).Distinct())
908 if (nameWithQuantity !=
string.Empty)
910 nameWithQuantity +=
", ";
912 int count = linkedItems.Count(it => it.Prefab == distinctItem);
913 nameWithQuantity += distinctItem.Name;
916 nameWithQuantity +=
" " + TextManager.GetWithVariable(
"campaignstore.quantity",
"[amount]", count.ToString());
922 nameWithQuantity = name;
926 GUIButton toggleButton =
new GUIButton(rectT(1f, 0.1f, parent.Content), text:
string.Empty, style:
"SlideDown")
930 GUILayoutGroup buttonLayout =
new GUILayoutGroup(rectT(1f, 1f, toggleButton.Frame), isHorizontal:
true);
932 LocalizedString slotText =
"";
933 if (linkedItems.Count() > 1)
935 slotText = TextManager.GetWithVariable(
"weaponslot",
"[number]",
string.Join(
", ", linkedItems.Select(it => (swappableEntities.IndexOf(it) + 1).ToString())));
939 slotText = TextManager.GetWithVariable(
"weaponslot",
"[number]", (swappableEntities.IndexOf(item) + 1).ToString());
942 new GUITextBlock(rectT(0.3f, 1f, buttonLayout), text: slotText, font: GUIStyle.SubHeadingFont);
943 GUILayoutGroup group =
new GUILayoutGroup(rectT(0.7f, 1f, buttonLayout), isHorizontal:
true) { Stretch =
true };
945 var title = item.PendingItemSwap !=
null ? TextManager.GetWithVariable(
"upgrades.pendingitem",
"[itemname]", name) : nameWithQuantity;
946 GUITextBlock text =
new GUITextBlock(rectT(0.7f, 1f, group), text: RichString.Rich(title), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right)
948 TextColor = GUIStyle.Orange
950 GUIImage arrowImage =
new GUIImage(rectT(0.5f, 1f, group, scaleBasis:
ScaleBasis.BothHeight), style:
"SlideDownArrow", scaleToFit:
true);
953 if (text.TextSize.X > text.Rect.Width)
955 text.ToolTip = text.Text;
956 text.Text = ToolBox.LimitString(text.Text, text.Font, text.Rect.Width);
959 List<GUIFrame> frames =
new List<GUIFrame>();
960 if (currentOrPending !=
null)
962 bool canUninstall = item.PendingItemSwap !=
null || !(currentOrPending.SwappableItem?.ReplacementOnUninstall.IsEmpty ??
true);
964 bool isUninstallPending = item.Prefab.SwappableItem !=
null && item.PendingItemSwap?.Identifier == item.Prefab.SwappableItem.ReplacementOnUninstall;
965 if (isUninstallPending) { canUninstall =
false; }
967 frames.Add(CreateUpgradeEntry(rectT(1f, 0.35f, parent.Content), currentOrPending.UpgradePreviewSprite,
968 item.PendingItemSwap !=
null ? TextManager.GetWithVariable(
"upgrades.pendingitem",
"[itemname]", name) : TextManager.GetWithVariable(
"upgrades.installeditem",
"[itemname]", nameWithQuantity),
969 currentOrPending.Description,
970 0,
null, addBuyButton: canUninstall, addProgressBar:
false, buttonStyle:
"WeaponUninstallButton").Frame);
972 if (canUninstall && frames.Last().FindChild(c => c is GUIButton, recursive:
true) is GUIButton refundButton)
974 refundButton.Enabled =
true;
975 refundButton.OnClicked += (button, o) =>
977 string textTag = item.PendingItemSwap !=
null ?
"upgrades.cancelitemswappromptbody" :
"upgrades.itemuninstallpromptbody";
978 if (isUninstallPending) { textTag =
"upgrades.cancelitemuninstallpromptbody"; }
979 LocalizedString promptBody = TextManager.GetWithVariable(textTag,
"[itemtouninstall]", isUninstallPending ? item.Name : currentOrPending.Name);
980 currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get(
"upgrades.refundprompttitle"), promptBody, () =>
982 if (GameMain.NetworkMember !=
null)
984 WaitForServerUpdate =
true;
987 GameMain.Client?.SendCampaignState();
994 var dividerContainer =
new GUIFrame(
new RectTransform(
new Vector2(1.0f, 0.1f), parent.Content.RectTransform), style:
null);
995 new GUIFrame(
new RectTransform(
new Vector2(0.8f, 0.5f), dividerContainer.RectTransform,
Anchor.Center), style:
"HorizontalLine");
996 frames.Add(dividerContainer);
999 foreach (ItemPrefab replacement
in availableReplacements)
1001 if (replacement == currentOrPending) {
continue; }
1003 bool isPurchased = item.AvailableSwaps.Contains(replacement);
1005 int price = isPurchased || replacement == item.Prefab ? 0 : replacement.SwappableItem.GetPrice(Campaign.
Map?.
CurrentLocation) * linkedItems.Count();
1007 frames.Add(CreateUpgradeEntry(rectT(1f, 0.35f, parent.Content), replacement.UpgradePreviewSprite, replacement.Name, replacement.Description,
1010 addProgressBar:
false,
1011 buttonStyle: isPurchased ?
"WeaponInstallButton" :
"StoreAddToCrateButton").Frame);
1013 if (!(frames.Last().FindChild(c => c is GUIButton, recursive:
true) is GUIButton buyButton)) {
continue; }
1014 if (PlayerBalance >= price)
1016 buyButton.Enabled =
true;
1017 buyButton.OnClicked += (button, o) =>
1019 LocalizedString promptBody = TextManager.GetWithVariables(isPurchased ?
"upgrades.itemswappromptbody" :
"upgrades.purchaseitemswappromptbody",
1020 (
"[itemtoinstall]", replacement.Name),
1021 (
"[amount]", (replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation) * linkedItems.Count()).ToString()));
1022 currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get(
"Upgrades.PurchasePromptTitle"), promptBody, () =>
1024 if (GameMain.NetworkMember !=
null)
1026 WaitForServerUpdate =
true;
1028 if (item.Prefab == replacement && item.PendingItemSwap !=
null)
1036 GameMain.Client?.SendCampaignState();
1049 foreach (GUIFrame frame
in frames)
1051 frame.Visible =
false;
1054 toggleButton.OnClicked = delegate
1056 if (Campaign ==
null) {
return false; }
1058 toggleButton.Selected = !toggleButton.Selected;
1059 foreach (GUIFrame frame
in frames)
1061 frame.Visible = toggleButton.Selected;
1063 if (toggleButton.Selected)
1065 var linkedItems = UpgradeManager.GetLinkedItemsToSwap(item);
1066 foreach (var itemPreview
in itemPreviews)
1068 itemPreview.Value.OutlineColor = itemPreview.Value.Color = linkedItems.Contains(itemPreview.Key) ? GUIStyle.Orange : previewWhite;
1070 foreach (GUIComponent otherComponent
in toggleButton.Parent.Children)
1072 if (otherComponent == toggleButton || frames.Contains(otherComponent)) {
continue; }
1073 if (otherComponent is GUIButton otherButton)
1075 var otherArrowImage = otherComponent.FindChild(c => c is GUIImage, recursive:
true);
1076 otherArrowImage.SpriteEffects = SpriteEffects.None;
1077 otherButton.Selected =
false;
1081 otherComponent.Visible =
false;
1087 foreach (var itemPreview
in itemPreviews)
1089 if (currentStoreLayout?.SelectedData is CategoryData categoryData && !categoryData.Category.ItemTags.Any(t => itemPreview.Key.HasTag(t))) {
continue; }
1090 itemPreview.Value.OutlineColor = itemPreview.Value.Color = GUIStyle.Orange;
1093 activeItemSwapSlideDown = toggleButton.
Selected ? toggleButton :
null;
1094 arrowImage.SpriteEffects = toggleButton.Selected ? SpriteEffects.FlipVertically : SpriteEffects.None;
1095 parent.RecalculateChildren();
1096 parent.UpdateScrollBarSize();
1101 public readonly record
struct BuyButtonFrame(GUILayoutGroup Layout, GUIListBox MaterialCostList, GUIButton BuyButton, GUITextBlock PriceText);
1102 public readonly record
struct ProgressBarFrame(GUITextBlock ProgressText, GUIProgressBar ProgressBar);
1104 public readonly record
struct UpgradeFrame(GUIFrame Frame,
1107 GUITextBlock Description,
1108 Option<BuyButtonFrame> BuyButton,
1109 Option<ProgressBarFrame> ProgressBar);
1111 public static UpgradeFrame CreateUpgradeFrame(UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign, RectTransform rectTransform,
bool addBuyButton =
true)
1113 int price = prefab.Price.GetBuyPrice(prefab, campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
1114 return CreateUpgradeEntry(rectTransform, prefab.Sprite, prefab.Name, prefab.Description, price,
new CategoryData(category, prefab), addBuyButton, upgradePrefab: prefab, currentLevel: campaign.UpgradeManager.GetUpgradeLevel(prefab, category));
1117 public static UpgradeFrame CreateUpgradeEntry(RectTransform parent, Sprite sprite, LocalizedString title, LocalizedString body,
int price,
object? userData,
bool addBuyButton =
true,
bool addProgressBar =
true,
string buttonStyle =
"UpgradeBuyButton", UpgradePrefab? upgradePrefab =
null,
int currentLevel = 0)
1119 float progressBarHeight = 0.25f;
1121 if (!addProgressBar)
1123 progressBarHeight = 0f;
1134 GUIFrame prefabFrame =
new GUIFrame(parent, style:
"ListBoxElement") { SelectedColor = Color.Transparent, UserData = userData };
1135 GUILayoutGroup mainLayout =
new GUILayoutGroup(rectT(0.98f, 0.95f, prefabFrame,
Anchor.Center), isHorizontal:
false);
1136 GUILayoutGroup prefabLayout =
new GUILayoutGroup(rectT(1f, addBuyButton ? 0.65f : 1f, mainLayout,
Anchor.Center), isHorizontal:
true) { Stretch =
true };
1137 GUILayoutGroup imageLayout =
new GUILayoutGroup(rectT(
new Point(prefabLayout.Rect.Height, prefabLayout.Rect.Height), prefabLayout), childAnchor:
Anchor.Center);
1138 var icon =
new GUIImage(rectT(0.9f, 0.9f, imageLayout, scaleBasis:
ScaleBasis.BothHeight), sprite, scaleToFit:
true) { CanBeFocused =
false };
1139 GUILayoutGroup textLayout =
new GUILayoutGroup(rectT(1f - imageLayout.RectTransform.RelativeSize.X, 1, prefabLayout));
1140 var name =
new GUITextBlock(rectT(1, 0.35f, textLayout), RichString.Rich(title), font: GUIStyle.SubHeadingFont) { AutoScaleHorizontal =
true, AutoScaleVertical =
true, Padding = Vector4.Zero };
1142 GUILayoutGroup descriptionLayout =
new GUILayoutGroup(rectT(1, 0.75f - progressBarHeight, textLayout));
1143 var description =
new GUITextBlock(rectT(1, 1, descriptionLayout), body, font: GUIStyle.SmallFont, wrap:
true, textAlignment: Alignment.TopLeft) { Padding = Vector4.Zero };
1144 GUILayoutGroup? progressLayout =
null;
1145 GUILayoutGroup? buyButtonLayout =
null;
1147 Option<BuyButtonFrame> buyButtonOption = Option<BuyButtonFrame>.None();
1148 Option<ProgressBarFrame> progressBarOption = Option<ProgressBarFrame>.None();
1152 progressLayout =
new GUILayoutGroup(rectT(1, 0.25f, textLayout), isHorizontal:
true, childAnchor:
Anchor.CenterLeft) { UserData = UpgradeStoreUserData.ProgressBarLayout };
1153 GUITextBlock progressText =
new GUITextBlock(rectT(0.15f, 1, progressLayout),
string.Empty, font: GUIStyle.SmallFont, textAlignment: Alignment.Center) { Padding = Vector4.Zero };
1154 GUIProgressBar progressBar =
new GUIProgressBar(rectT(0.85f, 0.75f, progressLayout), 0.0f, GUIStyle.Orange);
1155 progressBarOption = Option.Some(
new ProgressBarFrame(progressText, progressBar));
1160 var formattedPrice = TextManager.FormatCurrency(Math.Abs(price));
1162 if (price < 0) { formattedPrice =
"+" + formattedPrice; }
1163 buyButtonLayout =
new GUILayoutGroup(rectT(1f, 0.35f, mainLayout), isHorizontal:
true) { UserData = UpgradeStoreUserData.BuyButtonLayout };;
1165 GUIListBox materialCostList;
1166 if (upgradePrefab is not
null)
1168 var increaseText =
new GUITextBlock(rectT(imageLayout.RectTransform.RelativeSize.X, 1f, buyButtonLayout),
"", textAlignment: Alignment.Center, font: GUIStyle.SubHeadingFont)
1170 UserData = UpgradeStoreUserData.IncreaseLabel
1172 UpdateUpgradePercentageText(increaseText, upgradePrefab, currentLevel);
1173 materialCostList =
new GUIListBox(rectT(0.65f - imageLayout.RectTransform.RelativeSize.X, 1f, buyButtonLayout), isHorizontal:
true, style:
null);
1177 materialCostList =
new GUIListBox(rectT(0.65f, 1f, buyButtonLayout), isHorizontal:
true, style:
null);
1180 materialCostList.Visible =
false;
1181 materialCostList.UserData = UpgradeStoreUserData.MaterialCostList;
1183 var priceText =
new GUITextBlock(rectT(0.2f, 1f, buyButtonLayout), formattedPrice, textAlignment: Alignment.CenterRight)
1185 UserData = UpgradeStoreUserData.PriceLabel,
1187 Visible = userData is ItemPrefab
1192 priceText.TextColor = GUIStyle.Green;
1194 else if (price == 0)
1196 priceText.Text =
string.Empty;
1198 GUIButton buyButton =
new GUIButton(rectT(0.15f, 1f, buyButtonLayout),
string.Empty, style: buttonStyle)
1200 UserData = UpgradeStoreUserData.BuyButton,
1204 buyButtonOption = Option.Some(
new BuyButtonFrame(buyButtonLayout, materialCostList, buyButton, priceText));
1207 description.CalculateHeightFromText();
1209 for (
int i = 100; i > 0 && description.Rect.Height > descriptionLayout.Rect.Height; i--)
1211 var lines = description.WrappedText.Split(
'\n');
1212 var newString =
string.Join(
'\n', lines.Take(lines.Count - 1));
1213 if (0 >= newString.Length - 4) {
break; }
1215 description.Text = newString.Substring(0, newString.Length - 4) +
"...";
1216 description.CalculateHeightFromText();
1217 description.ToolTip = body;
1221 if (parent.Parent.GUIComponent is GUILayoutGroup group) { group.Recalculate(); }
1223 descriptionLayout.Recalculate();
1224 prefabLayout.Recalculate();
1225 imageLayout.Recalculate();
1226 textLayout.Recalculate();
1227 progressLayout?.Recalculate();
1228 buyButtonLayout?.Recalculate();
1230 return new UpgradeFrame(prefabFrame, icon, name, description, buyButtonOption, progressBarOption);
1233 private static void UpdateUpgradePercentageText(GUITextBlock text, UpgradePrefab upgradePrefab,
int currentLevel)
1235 int maxLevel = upgradePrefab.GetMaxLevelForCurrentSub();
1236 float nextIncrease = upgradePrefab.IncreaseOnTooltip * Math.Min(currentLevel + 1, maxLevel);
1237 if (nextIncrease != 0f)
1239 text.Text = $
"{Math.Round(nextIncrease, 1)} %";
1240 if (currentLevel == maxLevel)
1242 text.TextColor = Color.Gray;
1247 private void CreateUpgradeEntry(UpgradePrefab prefab, UpgradeCategory category, GUIComponent parent, Submarine submarine, List<Item>? itemsOnSubmarine)
1250 if (Campaign is
null || sub is
null) {
return; }
1252 UpgradeFrame prefabFrame = CreateUpgradeFrame(prefab, category, Campaign, rectT(1f, 0.4f, parent));
1254 if (!prefabFrame.BuyButton.TryUnwrap(out BuyButtonFrame buyButtonFrame)) {
return; }
1256 if (!HasPermission || !prefab.IsApplicable(submarine.Info) || (itemsOnSubmarine !=
null && !itemsOnSubmarine.Any(it => category.CanBeApplied(it, prefab))))
1258 prefabFrame.Frame.Enabled =
false;
1259 prefabFrame.Description.Enabled =
false;
1260 prefabFrame.Name.Enabled =
false;
1261 prefabFrame.Icon.Color = Color.Gray;
1262 buyButtonFrame.BuyButton.Enabled =
false;
1263 buyButtonFrame.Layout.UserData =
null;
1266 buyButtonFrame.BuyButton.OnClicked += (button, o) =>
1268 LocalizedString promptBody = TextManager.GetWithVariables(
"Upgrades.PurchasePromptBody",
1269 (
"[upgradename]", prefab.Name),
1271 currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get(
"Upgrades.PurchasePromptTitle"), promptBody, () =>
1273 if (GameMain.NetworkMember !=
null)
1275 WaitForServerUpdate =
true;
1278 GameMain.Client?.SendCampaignState();
1280 }, overrideConfirmButtonSound:
GUISoundType.ConfirmTransaction);
1285 UpdateUpgradeEntry(prefabFrame.Frame, prefab, category, Campaign);
1288 private void CreateItemTooltip(MapEntity entity)
1291 if (entity is Item swappableItem && swappableItem.Prefab.SwappableItem !=
null)
1293 var entitiesOnSub =
Submarine.MainSub.GetItems(
true).Where(i => i.Prefab.SwappableItem !=
null &&
Submarine.MainSub.IsEntityFoundOnThisSub(i,
true) && i.Prefab.SwappableItem.SwapIdentifier == swappableItem.Prefab.SwappableItem?.SwapIdentifier).ToList();
1294 slotIndex = entitiesOnSub.IndexOf(entity) + 1;
1297 GUITextBlock? itemName = ItemInfoFrame.FindChild(
"itemname",
true) as GUITextBlock;
1298 GUIListBox? upgradeList = ItemInfoFrame.FindChild(
"upgradelist",
true) as GUIListBox;
1299 GUITextBlock? moreIndicator = ItemInfoFrame.FindChild(
"moreindicator",
true) as GUITextBlock;
1300 GUILayoutGroup layout = ItemInfoFrame.GetChild<GUILayoutGroup>();
1301 Debug.Assert(itemName !=
null && upgradeList !=
null && moreIndicator !=
null && layout !=
null,
"One ore more tooltip elements not found");
1303 List<Upgrade> upgrades = entity.GetUpgrades();
1304 int upgradesCount = upgrades.Count;
1305 const int maxUpgrades = 4;
1308 itemName.Text = item?.Prefab.Name ?? TextManager.Get(
"upgradecategory.walls");
1311 itemName.Text = TextManager.GetWithVariables(
"weaponslotwithname", (
"[number]", slotIndex.ToString()), (
"[weaponname]", itemName.Text));
1313 if (item?.PendingItemSwap !=
null)
1315 itemName.Text = RichString.Rich(itemName.Text +
"\n" + TextManager.GetWithVariable(
"upgrades.pendingitem",
"[itemname]", item.PendingItemSwap.Name));
1317 upgradeList.Content.ClearChildren();
1318 for (var i = 0; i < upgrades.Count && i < maxUpgrades; i++)
1320 Upgrade upgrade = upgrades[i];
1321 new GUITextBlock(rectT(1, 0.25f, upgradeList.Content), CreateListEntry(upgrade.Prefab.Name, upgrade.Level)) { AutoScaleHorizontal =
true, UserData = Tuple.Create(upgrade.Level, upgrade.Prefab) };
1324 if (!(Campaign?.UpgradeManager is { } upgradeManager)) {
return; }
1327 foreach (var (prefab, category, level) in upgradeManager.PendingUpgrades)
1329 if (item !=
null && category.CanBeApplied(item, prefab) || entity is Structure && category.IsWallUpgrade)
1332 foreach (GUITextBlock textBlock
in upgradeList.Content.Children.Where(c => c is GUITextBlock).Cast<GUITextBlock>())
1334 if (textBlock.UserData is Tuple<int, UpgradePrefab> tuple && tuple.Item2 == prefab)
1336 var tooltip = CreateListEntry(tuple.Item2.Name, level + tuple.Item1);
1337 textBlock.Text = tooltip;
1346 if (upgradeList.Content.CountChildren < maxUpgrades)
1348 new GUITextBlock(rectT(1, 0.25f, upgradeList.Content), CreateListEntry(prefab.Name, level)) { AutoScaleHorizontal =
true };
1354 if (!upgradeList.Content.Children.Any())
1356 new GUITextBlock(rectT(1, 0.25f, upgradeList.Content), TextManager.Get(
"UpgradeUITooltip.NoUpgradesElement")) { AutoScaleHorizontal =
true };
1359 moreIndicator.Text = upgradesCount > maxUpgrades ? TextManager.GetWithVariable(
"upgradeuitooltip.moreindicator",
"[amount]", $
"{upgradesCount - maxUpgrades}") :
string.Empty;
1361 itemName.CalculateHeightFromText();
1362 moreIndicator.CalculateHeightFromText();
1363 layout.Recalculate();
1365 static LocalizedString CreateListEntry(LocalizedString name,
int level) => TextManager.GetWithVariables(
"upgradeuitooltip.upgradelistelement", (
"[upgradename]", name), (
"[level]", $
"{level}"));
1368 public static IEnumerable<UpgradeCategory> GetApplicableCategories(Submarine drawnSubmarine)
1370 Item[] entitiesOnSub = drawnSubmarine.GetItems(
true).Where(i => drawnSubmarine.IsEntityFoundOnThisSub(i,
true)).ToArray();
1371 foreach (UpgradeCategory category
in UpgradeCategory.Categories)
1374 if (UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category))) {
continue; }
1375 if (entitiesOnSub.Any(item => category.CanBeApplied(item,
null)))
1377 yield
return category;
1382 private void UpdateSubmarinePreview(
float deltaTime, GUICustomComponent parent)
1384 if (Campaign ==
null) {
return; }
1386 if (!parent.Children.Any() ||
1388 GameMain.GraphicsWidth != screenResolution.X || GameMain.GraphicsHeight != screenResolution.Y)
1390 GameMain.GameSession?.SubmarineInfo?.CheckSubsLeftBehind();
1392 if (drawnSubmarine !=
null)
1394 CreateSubmarinePreview(drawnSubmarine, parent);
1395 CreateHullBorderVerticies(drawnSubmarine, parent);
1397 applicableCategories.Clear();
1398 applicableCategories.AddRange(GetApplicableCategories(drawnSubmarine));
1401 screenResolution =
new Point(GameMain.GraphicsWidth, GameMain.GraphicsHeight);
1412 if (PlayerInput.KeyHit(Keys.Enter) && GUIMessageBox.MessageBoxes.Any())
1414 for (
int i = GUIMessageBox.MessageBoxes.Count - 1; i >= 0; i--)
1416 if (GUIMessageBox.MessageBoxes[i] is GUIMessageBox msgBox && msgBox == currectConfirmation)
1419 GUIButton? firstButton = msgBox.Buttons.FirstOrDefault();
1420 if (firstButton is
null) {
continue; }
1422 firstButton.OnClicked.Invoke(firstButton, firstButton.UserData);
1428 foreach (var (item, frame) in itemPreviews)
1430 if (GUI.MouseOn == frame)
1432 if (HoveredEntity != item) { CreateItemTooltip(item); }
1433 HoveredEntity = item;
1434 if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradeTab == UpgradeTab.Upgrade && currentStoreLayout !=
null)
1436 if (customizeTabOpen)
1438 if (selectedUpgradeCategoryLayout !=
null)
1440 var linkedItems = HoveredEntity is
Item hoveredItem ? UpgradeManager.GetLinkedItemsToSwap(hoveredItem) :
new List<Item>();
1441 if (selectedUpgradeCategoryLayout.
FindChild(c => c.UserData is Item item && (item == HoveredEntity || linkedItems.Contains(item)), recursive:
true) is GUIButton itemElement)
1443 if (!itemElement.Selected) { itemElement.OnClicked(itemElement, itemElement.UserData); }
1444 (itemElement.Parent?.Parent?.Parent as GUIListBox)?.ScrollToElement(itemElement);
1448 ScrollToCategory(data => data.Category.CanBeApplied(item,
null));
1454 ScrollToCategory(data => data.Category.CanBeApplied(item,
null));
1464 bool isMouseOnStructure =
false;
1465 if (GUI.MouseOn == submarinePreviewComponent || GUI.MouseOn == subPreviewFrame)
1468 Structure? firstStructure = submarineWalls.FirstOrDefault();
1470 if (subHullVertices.Any(hullVertex => ToolBox.PointIntersectsWithPolygon(PlayerInput.MousePosition, hullVertex)))
1472 if (HoveredEntity != firstStructure && !(firstStructure is
null)) { CreateItemTooltip(firstStructure); }
1473 HoveredEntity = firstStructure;
1474 isMouseOnStructure =
true;
1477 if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradeTab == UpgradeTab.Upgrade && currentStoreLayout !=
null)
1479 ScrollToCategory(data => data.Category.IsWallUpgrade, GUIListBox.PlaySelectSound.Yes);
1484 if (!isMouseOnStructure) { HoveredEntity =
null; }
1488 ItemInfoFrame.RectTransform.ScreenSpaceOffset = (PlayerInput.MousePosition +
new Vector2(20, 20)).ToPoint();
1489 if (ItemInfoFrame.Rect.Right > GameMain.GraphicsWidth)
1491 ItemInfoFrame.RectTransform.ScreenSpaceOffset = (PlayerInput.MousePosition -
new Vector2(20 + ItemInfoFrame.Rect.Width, -20)).ToPoint();
1495 private void CreateSubmarinePreview(Submarine submarine, GUIComponent parent)
1497 if (mainStoreLayout ==
null) {
return; }
1499 if (submarineInfoFrame !=
null && mainStoreLayout == submarineInfoFrame.
Parent)
1504 parent.ClearChildren();
1517 submarineInfoFrame =
new GUILayoutGroup(rectT(0.25f, 0.2f, mainStoreLayout,
Anchor.TopRight)) { IgnoreLayoutGroups =
true };
1519 new GUITextBlock(rectT(1, 0, submarineInfoFrame), submarine.Info.DisplayName, textAlignment: Alignment.Right, font: GUIStyle.LargeFont);
1521 LocalizedString classText = $
"{TextManager.GetWithVariable("submarineclass.classsuffixformat
", "[type]
", TextManager.Get($"submarineclass.{submarine.Info.SubmarineClass}
"))}";
1523 new GUITextBlock(rectT(1.0f, 0.15f, submarineInfoFrame), classText, textAlignment: Alignment.Right, font: GUIStyle.Font)
1525 ToolTip = TextManager.Get(
"submarineclass.description") +
"\n\n" + TextManager.Get($
"submarineclass.{submarine.Info.SubmarineClass}.description")
1527 new GUITextBlock(rectT(1.0f, 0.15f, submarineInfoFrame), TextManager.Get($
"submarinetier.{submarine.Info.Tier}"), textAlignment: Alignment.Right, font: GUIStyle.Font)
1529 ToolTip = TextManager.Get(
"submarinetier.description")
1532 var description =
new GUITextBlock(rectT(1, 0, submarineInfoFrame), submarine.Info.Description, textAlignment: Alignment.Right, wrap:
true);
1535 description.Padding =
new Vector4(description.Padding.X, 24 * GUI.Scale, description.Padding.Z, description.Padding.W);
1536 List<Entity> pointsOfInterest = (from category in UpgradeCategory.Categories from item in submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs)
1537 where category.CanBeApplied(item,
null) && item.IsPlayerTeamInteractable select item).Cast<Entity>().Distinct().ToList();
1539 List<ushort> ids = GameMain.GameSession.SubmarineInfo?.LeftBehindDockingPortIDs ??
new List<ushort>();
1540 pointsOfInterest.AddRange(submarine.GetItems(UpgradeManager.UpgradeAlsoConnectedSubs).Where(item => ids.Contains(item.ID)));
1542 submarine.CreateMiniMap(parent, pointsOfInterest, ignoreOutpost:
true);
1543 subPreviewFrame = parent.
GetChild<GUIFrame>();
1544 Rectangle dockedBorders = submarine.GetDockedBorders();
1545 GUIFrame hullContainer = parent.GetChild<GUIFrame>();
1546 if (hullContainer ==
null) {
return; }
1547 itemPreviews.Clear();
1549 foreach (Entity entity
in pointsOfInterest)
1551 GUIComponent component = parent.FindChild(entity,
true);
1552 if (component !=
null && entity is Item item)
1554 GUIComponent itemFrame;
1555 if (item.Prefab.UpgradePreviewSprite is { } icon)
1557 float spriteSize = 128f * item.Prefab.UpgradePreviewScale;
1558 Point size =
new Point((
int) (spriteSize * item.Scale / dockedBorders.Width * hullContainer.Rect.Width));
1559 itemFrame =
new GUIImage(rectT(size, component,
Anchor.Center), icon, scaleToFit:
true)
1561 SelectedColor = GUIStyle.Orange,
1562 Color = previewWhite,
1564 SpriteEffects = item.Rotation > 90.0f && item.Rotation < 270.0f ? SpriteEffects.FlipVertically : SpriteEffects.None
1566 if (item.Prefab.SwappableItem !=
null)
1568 new GUIImage(
new RectTransform(
new Vector2(0.8f), itemFrame.RectTransform,
Anchor.TopLeft) { RelativeOffset = new Vector2(-0.2f) },
"WeaponSwitchIcon.DropShadow", scaleToFit:
true)
1570 SelectedColor = GUIStyle.Orange,
1571 Color = previewWhite,
1572 CanBeFocused =
false
1578 Point size =
new Point((
int) (item.Rect.Width * item.Scale / dockedBorders.Width * hullContainer.Rect.Width), (
int) (item.Rect.Height * item.Scale / dockedBorders.Height * hullContainer.Rect.Height));
1579 itemFrame =
new GUIFrame(rectT(size, component,
Anchor.Center), style:
"ScanLines")
1581 SelectedColor = GUIStyle.Orange,
1582 OutlineColor = previewWhite,
1583 Color = previewWhite,
1584 OutlineThickness = 2,
1589 if (!itemPreviews.ContainsKey(item))
1591 itemPreviews.Add(item, itemFrame);
1606 private void CreateHullBorderVerticies(Submarine sub, GUIComponent parent)
1608 submarineWalls = sub.GetWalls(UpgradeManager.UpgradeAlsoConnectedSubs);
1609 const float lineWidth = 10;
1611 if (sub.HullVertices ==
null) {
return; }
1613 Rectangle dockedBorders = sub.GetDockedBorders();
1614 dockedBorders.Location += sub.WorldPosition.ToPoint();
1616 float scale = Math.Min(parent.Rect.Width / (
float)dockedBorders.Width, parent.Rect.Height / (
float)dockedBorders.Height) * 0.9f;
1618 float displayScale = ConvertUnits.ToDisplayUnits(scale);
1619 Vector2 offset = (sub.WorldPosition -
new Vector2(dockedBorders.Center.X, dockedBorders.Y - dockedBorders.Height / 2)) * scale;
1620 Vector2 center = parent.Rect.Center.ToVector2();
1622 subHullVertices =
new Vector2[sub.HullVertices.Count][];
1624 for (
int i = 0; i < sub.HullVertices.Count; i++)
1626 Vector2 start = sub.HullVertices[i] * displayScale + offset;
1628 Vector2 end = sub.HullVertices[(i + 1) % sub.HullVertices.Count] * displayScale + offset;
1631 Vector2 edge = end - start;
1632 float length = edge.Length();
1633 float angle = (float)Math.Atan2(edge.Y, edge.X);
1634 Matrix rotate = Matrix.CreateRotationZ(angle);
1636 subHullVertices[i] =
new[]
1638 center + start + Vector2.Transform(
new Vector2(length, -lineWidth), rotate),
1639 center + end + Vector2.Transform(
new Vector2(-length, -lineWidth), rotate),
1640 center + end + Vector2.Transform(
new Vector2(-length, lineWidth), rotate),
1641 center + start + Vector2.Transform(
new Vector2(length, lineWidth), rotate),
1646 private void DrawSubmarine(SpriteBatch spriteBatch, GUICustomComponent component)
1648 foreach (Vector2[] hullVertex
in subHullVertices)
1651 Vector2 point1 = hullVertex[1] + (hullVertex[2] - hullVertex[1]) / 2;
1652 Vector2 point2 = hullVertex[0] + (hullVertex[3] - hullVertex[0]) / 2;
1653 GUI.DrawLine(spriteBatch, point1, point2, (highlightWalls ? GUIStyle.Orange * 0.6f : Color.DarkCyan * 0.3f), width: 10);
1654 if (GameMain.DebugDraw)
1657 GUI.DrawRectangle(spriteBatch, hullVertex, Color.Red);
1662 public static void UpdateUpgradeEntry(GUIComponent prefabFrame, UpgradePrefab prefab, UpgradeCategory category, CampaignMode campaign)
1664 int currentLevel = campaign.UpgradeManager.GetUpgradeLevel(prefab, category);
1666 int maxLevel = prefab.GetMaxLevelForCurrentSub();
1667 LocalizedString progressText = TextManager.GetWithVariables(
"upgrades.progressformat", (
"[level]", currentLevel.ToString()), (
"[maxlevel]", maxLevel.ToString()));
1668 if (prefabFrame.FindChild(UpgradeStoreUserData.ProgressBarLayout,
true) is { } progressParent)
1670 GUIProgressBar bar = progressParent.GetChild<GUIProgressBar>();
1673 bar.BarSize = currentLevel / (float)maxLevel;
1674 bar.Color = currentLevel >= maxLevel ? GUIStyle.Green : GUIStyle.Orange;
1677 GUITextBlock block = progressParent.GetChild<GUITextBlock>();
1678 if (block !=
null) { block.Text = progressText; }
1681 if (prefabFrame.FindChild(UpgradeStoreUserData.BuyButtonLayout,
true) is not { } buttonParent) {
return; }
1683 GUITextBlock priceLabel = (GUITextBlock)buttonParent.FindChild(UpgradeStoreUserData.PriceLabel, recursive:
true);
1684 priceLabel.Visible =
true;
1685 int price = prefab.Price.GetBuyPrice(prefab, campaign.UpgradeManager.GetUpgradeLevel(prefab, category), campaign.Map?.CurrentLocation, characterList);
1687 if (!WaitForServerUpdate)
1689 priceLabel.Text = TextManager.FormatCurrency(price);
1690 if (currentLevel >= maxLevel)
1692 priceLabel.Text = TextManager.Get(
"Upgrade.MaxedUpgrade");
1696 if (buttonParent.FindChild(UpgradeStoreUserData.IncreaseLabel, recursive:
true) is GUITextBlock increaseLabel && !WaitForServerUpdate)
1698 UpdateUpgradePercentageText(increaseLabel, prefab, currentLevel);
1701 bool isMax = currentLevel >= maxLevel;
1703 if (buttonParent.FindChild(UpgradeStoreUserData.BuyButton, recursive:
true) is GUIButton button)
1705 bool canBuy = !WaitForServerUpdate && !isMax && campaign.GetBalance() >= price && prefab.HasResourcesToUpgrade(
Character.Controlled, currentLevel + 1);
1707 button.Enabled = canBuy;
1710 if (prefabFrame.FindChild(UpgradeStoreUserData.MaterialCostList,
true) is GUIListBox itemList)
1714 itemList.Visible =
false;
1718 CreateMaterialCosts(itemList, prefab, currentLevel + 1);
1722 static void CreateMaterialCosts(GUIListBox list, UpgradePrefab prefab,
int targetLevel)
1724 list.Content.ClearChildren();
1725 var allItems = CargoManager.FindAllItemsOnPlayerAndSub(
Character.Controlled);
1727 var resources = prefab.GetApplicableResources(targetLevel);
1729 foreach (ApplicableResourceCollection collection
in resources)
1731 list.Visible =
true;
1733 int length = collection.MatchingItems.Length;
1735 if (length is 0) {
continue; }
1737 ItemPrefab defaultItemPrefab = collection.MatchingItems.First();
1739 GUILayoutGroup wrapperLayout =
new GUILayoutGroup(rectT(0.25f, 1f, list.Content));
1741 GUIFrame itemFrame =
new GUIFrame(rectT(1f, 1f, wrapperLayout), style:
null)
1743 ToolTip = defaultItemPrefab.Name
1746 bool hasItems = collection.Cost.Amount <= allItems.Count(collection.Cost.MatchesItem);
1748 Sprite icon = defaultItemPrefab.InventoryIcon ?? prefab.Sprite;
1749 Color iconColor = defaultItemPrefab.InventoryIcon is
null ? defaultItemPrefab.SpriteColor : defaultItemPrefab.InventoryIconColor;
1751 GUIImage itemIcon =
new GUIImage(
new RectTransform(Vector2.One, itemFrame.RectTransform, scaleBasis:
ScaleBasis.Smallest, anchor:
Anchor.Center), sprite: icon, scaleToFit:
true)
1753 Color = hasItems ? iconColor : iconColor * 0.9f,
1754 CanBeFocused =
false
1758 new GUITextBlock(
new RectTransform(
new Vector2(0.5f, 0.5f), itemIcon.RectTransform, anchor:
Anchor.BottomRight), $
"{collection.Count}", font: GUIStyle.Font, textAlignment: Alignment.BottomRight)
1761 CanBeFocused =
false,
1762 Padding = Vector4.Zero,
1763 TextColor = hasItems ? Color.White : GUIStyle.Red,
1766 if (length is 1) {
continue; }
1771 GUICustomComponent customComponent =
new GUICustomComponent(rectT(1f, 1f, itemFrame),
null, (deltaTime, component) =>
1773 index += deltaTime / 3f;
1774 if (index > length) { index = 0; }
1776 ItemPrefab currentPrefab = collection.MatchingItems[(int)MathF.Floor(index)];
1777 Sprite icon = currentPrefab.InventoryIcon ?? prefab.Sprite;
1778 Color iconColor = currentPrefab.InventoryIcon is
null ? currentPrefab.SpriteColor : currentPrefab.InventoryIconColor;
1779 itemIcon.Sprite = icon;
1780 itemIcon.Color = hasItems ? iconColor : iconColor * 0.9f;
1781 itemFrame.ToolTip = currentPrefab.Name;
1784 CanBeFocused =
false
1790 private static void UpdateCategoryIndicators(
1791 GUIComponent indicators,
1792 GUIComponent parent,
1793 List<UpgradePrefab> prefabs,
1794 UpgradeCategory category,
1795 CampaignMode campaign,
1796 Submarine? drawnSubmarine,
1797 IEnumerable<UpgradeCategory> applicableCategories)
1800 if (!category.IsWallUpgrade && drawnSubmarine?.Info !=
null)
1802 if (UpgradePrefab.Prefabs.None(p => p.UpgradeCategories.Contains(category) && p.GetMaxLevel(drawnSubmarine.Info) > 0))
1804 parent.ToolTip = TextManager.Get(
"upgradecategorynotapplicable");
1805 parent.Enabled =
false;
1806 parent.SelectedColor = GUIStyle.Red * 0.5f;
1808 else if (applicableCategories.Contains(category))
1810 parent.Enabled =
true;
1811 parent.SelectedColor = parent.Style.SelectedColor;
1815 parent.Enabled =
false;
1816 parent.SelectedColor = GUIStyle.Red * 0.5f;
1820 foreach (GUIComponent component
in indicators.Children)
1822 if (component is not GUIImage image) {
continue; }
1824 foreach (UpgradePrefab prefab
in prefabs)
1826 if (component.UserData != prefab) {
continue; }
1828 int maxLevel = prefab.GetMaxLevelForCurrentSub();
1831 component.Visible =
false;
1835 Dictionary<Identifier, GUIComponentStyle> styles = GUIStyle.GetComponentStyle(
"upgradeindicator").ChildStyles;
1836 if (!styles.ContainsKey(
"upgradeindicatoron") || !styles.ContainsKey(
"upgradeindicatordim") || !styles.ContainsKey(
"upgradeindicatoroff")) {
continue; }
1838 GUIComponentStyle onStyle = styles[
"upgradeindicatoron".ToIdentifier()];
1839 GUIComponentStyle dimStyle = styles[
"upgradeindicatordim".ToIdentifier()];
1840 GUIComponentStyle offStyle = styles[
"upgradeindicatoroff".ToIdentifier()];
1848 if (campaign.UpgradeManager.GetUpgradeLevel(prefab, category) >= maxLevel)
1851 if (image.Style == onStyle) {
continue; }
1852 image.ApplyStyle(onStyle);
1854 else if (campaign.UpgradeManager.GetUpgradeLevel(prefab, category) > 0)
1856 if (image.Style == dimStyle) {
continue; }
1857 image.ApplyStyle(dimStyle);
1866 if (image.Style == offStyle) {
return; }
1867 image.ApplyStyle(offStyle);
1873 private void ScrollToCategory(Predicate<CategoryData> predicate, GUIListBox.PlaySelectSound playSelectSound = GUIListBox.PlaySelectSound.No)
1875 if (currentStoreLayout ==
null) {
return; }
1877 CategoryData? mostAppropriateCategory =
null;
1878 GUIComponent? mostAppropriateChild =
null;
1881 if (child.UserData is CategoryData data && predicate(data))
1885 if (mostAppropriateCategory ==
null ||
1886 data.Category.ItemTags.Count() < mostAppropriateCategory.Value.Category.ItemTags.Count())
1888 mostAppropriateCategory = data;
1889 mostAppropriateChild = child;
1893 if (mostAppropriateChild !=
null)
1895 currentStoreLayout.
ScrollToElement(mostAppropriateChild, playSelectSound);
1904 private GUIComponent[] GetFrames(UpgradeCategory category)
1906 List<GUIComponent> frames =
new List<GUIComponent>();
1907 foreach (var (item, guiFrame) in itemPreviews)
1909 if (category.CanBeApplied(item,
null))
1911 frames.Add(guiFrame);
1915 return frames.ToArray();
1918 private bool HasPermission =>
true;
1921 private static RectTransform rectT(
float x,
float y, GUIComponent parentComponent, Anchor anchor =
Anchor.TopLeft, ScaleBasis scaleBasis =
ScaleBasis.Normal)
1923 return new RectTransform(
new Vector2(x, y), parentComponent.RectTransform, anchor, scaleBasis: scaleBasis);
1926 private static RectTransform rectT(Point point, GUIComponent parentComponent, Anchor anchor =
Anchor.TopLeft)
1928 return new RectTransform(point, parentComponent.RectTransform, anchor);
UpgradeManager UpgradeManager
virtual int GetBalance(Client client=null)
virtual bool PurchasedItemRepairs
readonly NamedEvent< WalletChangedEvent > OnMoneyChanged
virtual bool PurchasedHullRepairs
virtual bool PurchasedLostShuttles
readonly CargoManager CargoManager
readonly NamedEvent< CargoManager > OnPurchasedItemsChanged
readonly NamedEvent< CargoManager > OnSoldItemsChanged
GUIComponent GetChild(int index)
virtual void RemoveChild(GUIComponent child)
virtual void ClearChildren()
GUIComponent FindChild(Func< GUIComponent, bool > predicate, bool recursive=false)
RectTransform RectTransform
IEnumerable< GUIComponent > Children
GUIComponent that can be used to render custom content on the UI
OnSelectedHandler OnSelected
GUIFrame Content
A frame that contains the contents of the listbox. The frame itself is not rendered.
void ScrollToElement(GUIComponent component, PlaySelectSound playSelectSound=PlaySelectSound.No)
Scrolls the list to the specific element.
int GetAdjustedMechanicalCost(int cost)
int GetUpgradeLevel(UpgradePrefab prefab, UpgradeCategory category, SubmarineInfo? info=null)
Gets the progress that is shown on the store interface. Includes values stored in the metadata and Pe...
void PurchaseItemSwap(Item itemToRemove, ItemPrefab itemToInstall, bool isNetworkMessage=false, Client? client=null)
Purchases an item swap and handles logic for deducting the credit.
void CancelItemSwap(Item itemToRemove, bool force=false)
Cancels the currently pending item swap, or uninstalls the item if there's no swap pending
readonly NamedEvent< UpgradeManager > OnUpgradesChanged
void PurchaseUpgrade(UpgradePrefab prefab, UpgradeCategory category, bool force=false, Client? client=null)
Purchases an upgrade and handles logic for deducting the credit.
readonly? List< UpgradePrefab > Prefabs
CategoryData(UpgradeCategory category, List< UpgradePrefab > prefabs)
CategoryData(UpgradeCategory category, UpgradePrefab prefab)
readonly? UpgradePrefab SinglePrefab
readonly UpgradeCategory Category