]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
uplink and store freshening (#26444)
authorNemanja <98561806+EmoGarbage404@users.noreply.github.com>
Fri, 12 Apr 2024 07:07:25 +0000 (03:07 -0400)
committerGitHub <noreply@github.com>
Fri, 12 Apr 2024 07:07:25 +0000 (17:07 +1000)
* uplink and store freshening

* more

* im gonna POOOOOOGGGGGGG

* we love it

12 files changed:
Content.Client/Store/Ui/StoreBoundUserInterface.cs
Content.Client/Store/Ui/StoreListingControl.xaml
Content.Client/Store/Ui/StoreListingControl.xaml.cs
Content.Client/Store/Ui/StoreMenu.xaml
Content.Client/Store/Ui/StoreMenu.xaml.cs
Content.Client/Store/Ui/StoreWithdrawWindow.xaml.cs
Content.Server/Store/Systems/StoreSystem.Ui.cs
Content.Shared/Store/ListingLocalisationHelpers.cs
Content.Shared/Store/ListingPrototype.cs
Content.Shared/Store/StoreUi.cs
Resources/Locale/en-US/store/store.ftl
Resources/Prototypes/Catalog/uplink_catalog.yml

index f87b92bc615ef9940eb07e3563cf9b26f888559d..88ad0e3de8b5b9b1eb5efecd94e6c9518f815d9e 100644 (file)
@@ -17,7 +17,7 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
     private string _windowName = Loc.GetString("store-ui-default-title");
 
     [ViewVariables]
-    private string _search = "";
+    private string _search = string.Empty;
 
     [ViewVariables]
     private HashSet<ListingData> _listings = new();
@@ -41,7 +41,7 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
         _menu.OnCategoryButtonPressed += (_, category) =>
         {
             _menu.CurrentCategory = category;
-            SendMessage(new StoreRequestUpdateInterfaceMessage());
+            _menu?.UpdateListing();
         };
 
         _menu.OnWithdrawAttempt += (_, type, amount) =>
@@ -49,11 +49,6 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
             SendMessage(new StoreRequestWithdrawMessage(type, amount));
         };
 
-        _menu.OnRefreshButtonPressed += (_) =>
-        {
-            SendMessage(new StoreRequestUpdateInterfaceMessage());
-        };
-
         _menu.SearchTextUpdated += (_, search) =>
         {
             _search = search.Trim().ToLowerInvariant();
index aefeec17cc8e4e526e0761f521ec8ef11f455cdb..12b4d7b5b3002c8edd4f55505875f376bc024870 100644 (file)
@@ -15,6 +15,7 @@
                 Margin="0,0,4,0"
                 MinSize="48 48"
                 Stretch="KeepAspectCentered" />
+            <Control MinWidth="5"/>
             <RichTextLabel Name="StoreItemDescription" />
         </BoxContainer>
     </BoxContainer>
index bb600588e042becc4f8e95a6d5b3d61b2f9c1ccd..030f07dc7ca95c0cee59f60312ba0deb88c6f745 100644 (file)
@@ -1,25 +1,91 @@
+using Content.Client.GameTicking.Managers;
+using Content.Shared.Store;
 using Robust.Client.AutoGenerated;
 using Robust.Client.Graphics;
 using Robust.Client.UserInterface;
 using Robust.Client.UserInterface.XAML;
-using Robust.Shared.Graphics;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Timing;
 
 namespace Content.Client.Store.Ui;
 
 [GenerateTypedNameReferences]
 public sealed partial class StoreListingControl : Control
 {
-    public StoreListingControl(string itemName, string itemDescription,
-        string price, bool canBuy, Texture? texture = null)
+    [Dependency] private readonly IPrototypeManager _prototype = default!;
+    [Dependency] private readonly IEntityManager _entity = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    private readonly ClientGameTicker _ticker;
+
+    private readonly ListingData _data;
+
+    private readonly bool _hasBalance;
+    private readonly string _price;
+    public StoreListingControl(ListingData data, string price, bool hasBalance, Texture? texture = null)
     {
+        IoCManager.InjectDependencies(this);
         RobustXamlLoader.Load(this);
 
-        StoreItemName.Text = itemName;
-        StoreItemDescription.SetMessage(itemDescription);
+        _ticker = _entity.System<ClientGameTicker>();
+
+        _data = data;
+        _hasBalance = hasBalance;
+        _price = price;
 
-        StoreItemBuyButton.Text = price;
-        StoreItemBuyButton.Disabled = !canBuy;
+        StoreItemName.Text = ListingLocalisationHelpers.GetLocalisedNameOrEntityName(_data, _prototype);
+        StoreItemDescription.SetMessage(ListingLocalisationHelpers.GetLocalisedDescriptionOrEntityDescription(_data, _prototype));
+
+        UpdateBuyButtonText();
+        StoreItemBuyButton.Disabled = !CanBuy();
 
         StoreItemTexture.Texture = texture;
     }
+
+    private bool CanBuy()
+    {
+        if (!_hasBalance)
+            return false;
+
+        var stationTime = _timing.CurTime.Subtract(_ticker.RoundStartTimeSpan);
+        if (_data.RestockTime > stationTime)
+            return false;
+
+        return true;
+    }
+
+    private void UpdateBuyButtonText()
+    {
+        var stationTime = _timing.CurTime.Subtract(_ticker.RoundStartTimeSpan);
+        if (_data.RestockTime > stationTime)
+        {
+            var timeLeftToBuy = stationTime - _data.RestockTime;
+            StoreItemBuyButton.Text =  timeLeftToBuy.Duration().ToString(@"mm\:ss");
+        }
+        else
+        {
+            StoreItemBuyButton.Text = _price;
+        }
+    }
+
+    private void UpdateName()
+    {
+        var name = ListingLocalisationHelpers.GetLocalisedNameOrEntityName(_data, _prototype);
+
+        var stationTime = _timing.CurTime.Subtract(_ticker.RoundStartTimeSpan);
+        if (_data.RestockTime > stationTime)
+        {
+            name += Loc.GetString("store-ui-button-out-of-stock");
+        }
+
+        StoreItemName.Text = name;
+    }
+
+    protected override void FrameUpdate(FrameEventArgs args)
+    {
+        base.FrameUpdate(args);
+
+        UpdateBuyButtonText();
+        UpdateName();
+        StoreItemBuyButton.Disabled = !CanBuy();
+    }
 }
index fc4cbe444fc573522dfd3183dd37f982592b67d5..843c9dc0296a5c4245c14c896ec60366ee9add68 100644 (file)
                     HorizontalAlignment="Left"
                     Access="Public"
                     HorizontalExpand="True" />
-                <Button
-                    Name="RefreshButton"
-                    MinWidth="64"
-                    HorizontalAlignment="Right"
-                    Text="Refresh" />
                 <Button
                     Name="WithdrawButton"
                     MinWidth="64"
index 67e5d360a3a27f8bea8b420da35e7f26e8e4407c..b7a2c285fe5dbe47653965d75710406f99377a27 100644 (file)
@@ -1,6 +1,5 @@
 using System.Linq;
 using Content.Client.Actions;
-using Content.Client.GameTicking.Managers;
 using Content.Client.Message;
 using Content.Shared.FixedPoint;
 using Content.Shared.Store;
@@ -11,7 +10,6 @@ using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.CustomControls;
 using Robust.Client.UserInterface.XAML;
 using Robust.Shared.Prototypes;
-using Robust.Shared.Timing;
 
 namespace Content.Client.Store.Ui;
 
@@ -20,9 +18,6 @@ public sealed partial class StoreMenu : DefaultWindow
 {
     [Dependency] private readonly IEntityManager _entityManager = default!;
     [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
-    [Dependency] private readonly IGameTiming _gameTiming = default!;
-    [Dependency] private readonly IEntitySystemManager _entitySystem = default!;
-    private readonly ClientGameTicker _gameTicker;
 
     private StoreWithdrawWindow? _withdrawWindow;
 
@@ -30,21 +25,19 @@ public sealed partial class StoreMenu : DefaultWindow
     public event Action<BaseButton.ButtonEventArgs, ListingData>? OnListingButtonPressed;
     public event Action<BaseButton.ButtonEventArgs, string>? OnCategoryButtonPressed;
     public event Action<BaseButton.ButtonEventArgs, string, int>? OnWithdrawAttempt;
-    public event Action<BaseButton.ButtonEventArgs>? OnRefreshButtonPressed;
     public event Action<BaseButton.ButtonEventArgs>? OnRefundAttempt;
 
-    public Dictionary<string, FixedPoint2> Balance = new();
+    public Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> Balance = new();
     public string CurrentCategory = string.Empty;
 
+    private List<ListingData> _cachedListings = new();
+
     public StoreMenu(string name)
     {
         RobustXamlLoader.Load(this);
         IoCManager.InjectDependencies(this);
 
-        _gameTicker = _entitySystem.GetEntitySystem<ClientGameTicker>();
-
         WithdrawButton.OnButtonDown += OnWithdrawButtonDown;
-        RefreshButton.OnButtonDown += OnRefreshButtonDown;
         RefundButton.OnButtonDown += OnRefundButtonDown;
         SearchBar.OnTextChanged += _ => SearchTextUpdated?.Invoke(this, SearchBar.Text);
 
@@ -52,12 +45,12 @@ public sealed partial class StoreMenu : DefaultWindow
             Window.Title = name;
     }
 
-    public void UpdateBalance(Dictionary<string, FixedPoint2> balance)
+    public void UpdateBalance(Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> balance)
     {
         Balance = balance;
 
         var currency = balance.ToDictionary(type =>
-            (type.Key, type.Value), type => _prototypeManager.Index<CurrencyPrototype>(type.Key));
+            (type.Key, type.Value), type => _prototypeManager.Index(type.Key));
 
         var balanceStr = string.Empty;
         foreach (var ((_, amount), proto) in currency)
@@ -80,7 +73,13 @@ public sealed partial class StoreMenu : DefaultWindow
 
     public void UpdateListing(List<ListingData> listings)
     {
-        var sorted = listings.OrderBy(l => l.Priority).ThenBy(l => l.Cost.Values.Sum());
+        _cachedListings = listings;
+        UpdateListing();
+    }
+
+    public void UpdateListing()
+    {
+        var sorted = _cachedListings.OrderBy(l => l.Priority).ThenBy(l => l.Cost.Values.Sum());
 
         // should probably chunk these out instead. to-do if this clogs the internet tubes.
         // maybe read clients prototypes instead?
@@ -96,12 +95,6 @@ public sealed partial class StoreMenu : DefaultWindow
         TraitorFooter.Visible = visible;
     }
 
-
-    private void OnRefreshButtonDown(BaseButton.ButtonEventArgs args)
-    {
-        OnRefreshButtonPressed?.Invoke(args);
-    }
-
     private void OnWithdrawButtonDown(BaseButton.ButtonEventArgs args)
     {
         // check if window is already open
@@ -129,10 +122,8 @@ public sealed partial class StoreMenu : DefaultWindow
         if (!listing.Categories.Contains(CurrentCategory))
             return;
 
-        var listingName = ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, _prototypeManager);
-        var listingDesc = ListingLocalisationHelpers.GetLocalisedDescriptionOrEntityDescription(listing, _prototypeManager);
         var listingPrice = listing.Cost;
-        var canBuy = CanBuyListing(Balance, listingPrice);
+        var hasBalance = HasListingPrice(Balance, listingPrice);
 
         var spriteSys = _entityManager.EntitySysManager.GetEntitySystem<SpriteSystem>();
 
@@ -154,39 +145,15 @@ public sealed partial class StoreMenu : DefaultWindow
                 texture = spriteSys.Frame0(action.Icon);
             }
         }
-        var listingInStock = ListingInStock(listing);
-        if (listingInStock != GetListingPriceString(listing))
-        {
-            listingName += " (Out of stock)";
-            canBuy = false;
-        }
 
-        var newListing = new StoreListingControl(listingName, listingDesc, listingInStock, canBuy, texture);
+        var newListing = new StoreListingControl(listing, GetListingPriceString(listing), hasBalance, texture);
         newListing.StoreItemBuyButton.OnButtonDown += args
             => OnListingButtonPressed?.Invoke(args, listing);
 
         StoreListingsContainer.AddChild(newListing);
     }
 
-    /// <summary>
-    /// Return time until available or the cost.
-    /// </summary>
-    /// <param name="listing"></param>
-    /// <returns></returns>
-    public string ListingInStock(ListingData listing)
-    {
-        var stationTime = _gameTiming.CurTime.Subtract(_gameTicker.RoundStartTimeSpan);
-
-        TimeSpan restockTimeSpan = TimeSpan.FromMinutes(listing.RestockTime);
-        if (restockTimeSpan > stationTime)
-        {
-            var timeLeftToBuy = stationTime - restockTimeSpan;
-            return timeLeftToBuy.Duration().ToString(@"mm\:ss");
-        }
-
-        return GetListingPriceString(listing);
-    }
-    public bool CanBuyListing(Dictionary<string, FixedPoint2> currency, Dictionary<string, FixedPoint2> price)
+    public bool HasListingPrice(Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> currency, Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> price)
     {
         foreach (var type in price)
         {
@@ -208,7 +175,7 @@ public sealed partial class StoreMenu : DefaultWindow
         {
             foreach (var (type, amount) in listing.Cost)
             {
-                var currency = _prototypeManager.Index<CurrencyPrototype>(type);
+                var currency = _prototypeManager.Index(type);
                 text += Loc.GetString("store-ui-price-display", ("amount", amount),
                     ("currency", Loc.GetString(currency.DisplayName, ("amount", amount))));
             }
@@ -229,7 +196,7 @@ public sealed partial class StoreMenu : DefaultWindow
         {
             foreach (var cat in listing.Categories)
             {
-                var proto = _prototypeManager.Index<StoreCategoryPrototype>(cat);
+                var proto = _prototypeManager.Index(cat);
                 if (!allCategories.Contains(proto))
                     allCategories.Add(proto);
             }
@@ -248,12 +215,17 @@ public sealed partial class StoreMenu : DefaultWindow
         if (allCategories.Count < 1)
             return;
 
+        var group = new ButtonGroup();
         foreach (var proto in allCategories)
         {
             var catButton = new StoreCategoryButton
             {
                 Text = Loc.GetString(proto.Name),
-                Id = proto.ID
+                Id = proto.ID,
+                Pressed = proto.ID == CurrentCategory,
+                Group = group,
+                ToggleMode = true,
+                StyleClasses = { "OpenBoth" }
             };
 
             catButton.OnPressed += args => OnCategoryButtonPressed?.Invoke(args, catButton.Id);
@@ -269,7 +241,7 @@ public sealed partial class StoreMenu : DefaultWindow
 
     public void UpdateRefund(bool allowRefund)
     {
-        RefundButton.Disabled = !allowRefund;
+        RefundButton.Visible = allowRefund;
     }
 
     private sealed class StoreCategoryButton : Button
index db0e7e68070b1980bd7c1f4fdf43d7322548fd31..793685575621b9bc1898fa37076536ecd8c9c9de 100644 (file)
@@ -28,12 +28,12 @@ public sealed partial class StoreWithdrawWindow : DefaultWindow
         IoCManager.InjectDependencies(this);
     }
 
-    public void CreateCurrencyButtons(Dictionary<string, FixedPoint2> balance)
+    public void CreateCurrencyButtons(Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> balance)
     {
         _validCurrencies.Clear();
         foreach (var currency in balance)
         {
-            if (!_prototypeManager.TryIndex<CurrencyPrototype>(currency.Key, out var proto))
+            if (!_prototypeManager.TryIndex(currency.Key, out var proto))
                 continue;
 
             _validCurrencies.Add(currency.Value, proto);
index e6c4eb0ccea71fffcef63013ae6800da82d5b862..25f64ba4b643ece5349ff6f90f3929b25e0618bf 100644 (file)
@@ -5,7 +5,6 @@ using Content.Server.PDA.Ringer;
 using Content.Server.Stack;
 using Content.Server.Store.Components;
 using Content.Shared.Actions;
-using Content.Shared.Administration.Logs;
 using Content.Shared.Database;
 using Content.Shared.FixedPoint;
 using Content.Shared.Hands.EntitySystems;
@@ -99,13 +98,13 @@ public sealed partial class StoreSystem
         }
 
         //dictionary for all currencies, including 0 values for currencies on the whitelist
-        Dictionary<string, FixedPoint2> allCurrency = new();
+        Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> allCurrency = new();
         foreach (var supported in component.CurrencyWhitelist)
         {
             allCurrency.Add(supported, FixedPoint2.Zero);
 
-            if (component.Balance.ContainsKey(supported))
-                allCurrency[supported] = component.Balance[supported];
+            if (component.Balance.TryGetValue(supported, out var value))
+                allCurrency[supported] = value;
         }
 
         // TODO: if multiple users are supposed to be able to interact with a single BUI & see different
index 3ac75cd80108d0cd38b20b44422492049e03bb51..882300109cebf272d4e1d75d9ec63819aba0363f 100644 (file)
@@ -11,15 +11,14 @@ public static class ListingLocalisationHelpers
     /// </summary>
     public static string GetLocalisedNameOrEntityName(ListingData listingData, IPrototypeManager prototypeManager)
     {
-        bool wasLocalised = Loc.TryGetString(listingData.Name, out string? listingName);
+        var name = string.Empty;
 
-        if (!wasLocalised && listingData.ProductEntity != null)
-        {
-            var proto = prototypeManager.Index<EntityPrototype>(listingData.ProductEntity);
-            listingName = proto.Name;
-        }
+        if (listingData.Name != null)
+            name = Loc.GetString(listingData.Name);
+        else if (listingData.ProductEntity != null)
+            name = prototypeManager.Index(listingData.ProductEntity.Value).Name;
 
-        return listingName ?? listingData.Name;
+        return name;
     }
 
     /// <summary>
@@ -29,14 +28,13 @@ public static class ListingLocalisationHelpers
     /// </summary>
     public static string GetLocalisedDescriptionOrEntityDescription(ListingData listingData, IPrototypeManager prototypeManager)
     {
-        bool wasLocalised = Loc.TryGetString(listingData.Description, out string? listingDesc);
+        var desc = string.Empty;
 
-        if (!wasLocalised && listingData.ProductEntity != null)
-        {
-            var proto = prototypeManager.Index<EntityPrototype>(listingData.ProductEntity);
-            listingDesc = proto.Description;
-        }
+        if (listingData.Description != null)
+            desc = Loc.GetString(listingData.Description);
+        else if (listingData.ProductEntity != null)
+            desc = prototypeManager.Index(listingData.ProductEntity.Value).Description;
 
-        return listingDesc ?? listingData.Description;
+        return desc;
     }
 }
index 25245327ce9c7fcc1663ea48edad77e884a5ec2e..d3d2e13cdfdcc6f803546cdb265122f8c7b270fd 100644 (file)
@@ -2,10 +2,6 @@ using System.Linq;
 using Content.Shared.FixedPoint;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
-using Robust.Shared.Serialization.TypeSerializers.Implementations;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
 using Robust.Shared.Utility;
 
 namespace Content.Shared.Store;
@@ -26,57 +22,57 @@ public partial class ListingData : IEquatable<ListingData>, ICloneable
     /// <summary>
     /// The name of the listing. If empty, uses the entity's name (if present)
     /// </summary>
-    [DataField("name")]
-    public string Name = string.Empty;
+    [DataField]
+    public string? Name;
 
     /// <summary>
     /// The description of the listing. If empty, uses the entity's description (if present)
     /// </summary>
-    [DataField("description")]
-    public string Description = string.Empty;
+    [DataField]
+    public string? Description;
 
     /// <summary>
     /// The categories that this listing applies to. Used for filtering a listing for a store.
     /// </summary>
-    [DataField("categories", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer<StoreCategoryPrototype>))]
-    public List<string> Categories = new();
+    [DataField]
+    public List<ProtoId<StoreCategoryPrototype>> Categories = new();
 
     /// <summary>
     /// The cost of the listing. String represents the currency type while the FixedPoint2 represents the amount of that currency.
     /// </summary>
-    [DataField("cost", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<FixedPoint2, CurrencyPrototype>))]
-    public Dictionary<string, FixedPoint2> Cost = new();
+    [DataField]
+    public Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> Cost = new();
 
     /// <summary>
-    /// Specific customizeable conditions that determine whether or not the listing can be purchased.
+    /// Specific customizable conditions that determine whether or not the listing can be purchased.
     /// </summary>
     [NonSerialized]
-    [DataField("conditions", serverOnly: true)]
+    [DataField(serverOnly: true)]
     public List<ListingCondition>? Conditions;
 
     /// <summary>
     /// The icon for the listing. If null, uses the icon for the entity or action.
     /// </summary>
-    [DataField("icon")]
+    [DataField]
     public SpriteSpecifier? Icon;
 
     /// <summary>
     /// The priority for what order the listings will show up in on the menu.
     /// </summary>
-    [DataField("priority")]
-    public int Priority = 0;
+    [DataField]
+    public int Priority;
 
     /// <summary>
     /// The entity that is given when the listing is purchased.
     /// </summary>
-    [DataField("productEntity", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
-    public string? ProductEntity;
+    [DataField]
+    public EntProtoId? ProductEntity;
 
     /// <summary>
     /// The action that is given when the listing is purchased.
     /// </summary>
-    [DataField("productAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
-    public string? ProductAction;
+    [DataField]
+    public EntProtoId? ProductAction;
 
     /// <summary>
     ///     The listing ID of the related upgrade listing. Can be used to link a <see cref="ProductAction"/> to an
@@ -95,7 +91,7 @@ public partial class ListingData : IEquatable<ListingData>, ICloneable
     /// <summary>
     /// The event that is broadcast when the listing is purchased.
     /// </summary>
-    [DataField("productEvent")]
+    [DataField]
     public object? ProductEvent;
 
     [DataField]
@@ -104,13 +100,14 @@ public partial class ListingData : IEquatable<ListingData>, ICloneable
     /// <summary>
     /// used internally for tracking how many times an item was purchased.
     /// </summary>
-    public int PurchaseAmount = 0;
+    [DataField]
+    public int PurchaseAmount;
 
     /// <summary>
     /// Used to delay purchase of some items.
     /// </summary>
-    [DataField("restockTime")]
-    public int RestockTime;
+    [DataField]
+    public TimeSpan RestockTime = TimeSpan.Zero;
 
     public bool Equals(ListingData? listing)
     {
@@ -173,14 +170,10 @@ public partial class ListingData : IEquatable<ListingData>, ICloneable
     }
 }
 
-//<inheritdoc>
 /// <summary>
 ///     Defines a set item listing that is available in a store
 /// </summary>
 [Prototype("listing")]
 [Serializable, NetSerializable]
 [DataDefinition]
-public sealed partial class ListingPrototype : ListingData, IPrototype
-{
-
-}
+public sealed partial class ListingPrototype : ListingData, IPrototype;
index 27a8ada1855abf737214f4f2168daa9e5686338c..ee4da6991f6cb79eab74a7ea75a6a7035f5c843d 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.FixedPoint;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization;
 
 namespace Content.Shared.Store;
@@ -14,13 +15,13 @@ public sealed class StoreUpdateState : BoundUserInterfaceState
 {
     public readonly HashSet<ListingData> Listings;
 
-    public readonly Dictionary<string, FixedPoint2> Balance;
+    public readonly Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> Balance;
 
     public readonly bool ShowFooter;
 
     public readonly bool AllowRefund;
 
-    public StoreUpdateState(HashSet<ListingData> listings, Dictionary<string, FixedPoint2> balance, bool showFooter, bool allowRefund)
+    public StoreUpdateState(HashSet<ListingData> listings, Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> balance, bool showFooter, bool allowRefund)
     {
         Listings = listings;
         Balance = balance;
@@ -46,9 +47,7 @@ public sealed class StoreInitializeState : BoundUserInterfaceState
 [Serializable, NetSerializable]
 public sealed class StoreRequestUpdateInterfaceMessage : BoundUserInterfaceMessage
 {
-    public StoreRequestUpdateInterfaceMessage()
-    {
-    }
+
 }
 
 [Serializable, NetSerializable]
index d663cc61f71e551c665d339c33943fe2ee01fb22..997afedfc063faa3d9d6965e9c80006a8f8dbe31 100644 (file)
@@ -6,5 +6,5 @@ store-ui-traitor-flavor = Copyright (C) NT -30643
 store-ui-traitor-warning = Operatives must lock their uplinks after use to avoid detection.
 
 store-withdraw-button-ui = Withdraw {$currency}
-
+store-ui-button-out-of-stock = {""} (Out of Stock)
 store-not-account-owner = This {$store} is not bound to you!
index 7765196bc3c0cdf8218e861c4da3315a2068a58d..b1e6b88435beae36262fe85f67a2465e2d32bafe 100644 (file)
       blacklist:
         tags:
           - NukeOpsUplink
-  
+
 - type: listing
   id: UplinkEshield
   name: uplink-eshield-name
     Telecrystal: 11
   categories:
     - UplinkExplosives
-  restockTime: 30
+  restockTime: 1800
   conditions:
   - !type:StoreWhitelistCondition
     blacklist:
     Telecrystal: 6
   categories:
   - UplinkChemicals
-  
+
 - type: listing
   id: UplinkHypoDart
   name: uplink-hypodart-name
     Telecrystal: 4
   categories:
   - UplinkChemicals
-  
+
 - type: listing
   id: UplinkZombieBundle
   name: uplink-zombie-bundle-name
     Telecrystal: 12
   categories:
   - UplinkChemicals
-  
+
 - type: listing
   id: UplinkCigarettes
   name: uplink-cigarettes-name
           - SurplusBundle
 
 # Deception
-  
+
 - type: listing
   id: UplinkAgentIDCard
   name: uplink-agent-id-card-name
     Telecrystal: 3
   categories:
   - UplinkDeception
-  
+
 - type: listing
   id: UplinkStealthBox
   name: uplink-stealth-box-name
   description: uplink-binary-translator-key-desc
   icon: { sprite: /Textures/Objects/Devices/encryption_keys.rsi, state: rd_label }
   productEntity: EncryptionKeyBinary
-  cost: 
+  cost:
     Telecrystal: 1
   categories:
   - UplinkDeception
-  
+
 - type: listing
   id: UplinkCyberpen
   name: uplink-cyberpen-name
     Telecrystal: 1
   categories:
   - UplinkDeception
-  
+
 - type: listing
   id: UplinkUltrabrightLantern
   name: uplink-ultrabright-lantern-name
     Telecrystal: 8
   categories:
   - UplinkDisruption
-  
+
 - type: listing
   id: UplinkRadioJammer
   name: uplink-radio-jammer-name
     Telecrystal: 3
   categories:
   - UplinkDisruption
-  
+
 - type: listing
   id: UplinkToolbox
   name: uplink-toolbox-name
       whitelist:
         tags:
           - NukeOpsUplink
+
 - type: listing
   id: UplinkUplinkImplanter # uplink uplink real
   name: uplink-uplink-implanter-name