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
Triggers when some element is clicked on the listbox. Note that SelectedData is not set yet when this...
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.
@ Character
Characters only
@ Structure
Structures and hulls, but also items (for backwards support)!
readonly? List< UpgradePrefab > Prefabs
CategoryData(UpgradeCategory category, List< UpgradePrefab > prefabs)
CategoryData(UpgradeCategory category, UpgradePrefab prefab)
readonly? UpgradePrefab SinglePrefab
readonly UpgradeCategory Category