diff --git a/Content.Client/Store/Ui/StoreMenu.xaml.cs b/Content.Client/Store/Ui/StoreMenu.xaml.cs index b916b8e890..ca51c174b0 100644 --- a/Content.Client/Store/Ui/StoreMenu.xaml.cs +++ b/Content.Client/Store/Ui/StoreMenu.xaml.cs @@ -43,16 +43,14 @@ public sealed partial class StoreMenu : DefaultWindow { Balance = balance; - var currency = new Dictionary<(string, FixedPoint2), CurrencyPrototype>(); - foreach (var type in balance) - { - currency.Add((type.Key, type.Value), _prototypeManager.Index(type.Key)); - } + var currency = balance.ToDictionary(type => + (type.Key, type.Value), type => _prototypeManager.Index(type.Key)); var balanceStr = string.Empty; - foreach (var type in currency) + foreach (var ((type, amount),proto) in currency) { - balanceStr += $"{Loc.GetString(type.Value.BalanceDisplay, ("amount", type.Key.Item2))}\n"; + balanceStr += Loc.GetString("store-ui-balance-display", ("amount", amount), + ("currency", Loc.GetString(proto.DisplayName, ("amount", 1)))); } BalanceInfo.SetMarkup(balanceStr.TrimEnd()); @@ -60,7 +58,7 @@ public sealed partial class StoreMenu : DefaultWindow var disabled = true; foreach (var type in currency) { - if (type.Value.CanWithdraw && type.Value.EntityId != null && type.Key.Item2 > 0) + if (type.Value.CanWithdraw && type.Value.Cash != null && type.Key.Item2 > 0) disabled = false; } @@ -155,14 +153,17 @@ public sealed partial class StoreMenu : DefaultWindow { var text = string.Empty; - foreach (var type in listing.Cost) - { - var currency = _prototypeManager.Index(type.Key); - text += $"{Loc.GetString(currency.PriceDisplay, ("amount", type.Value))}\n"; - } - if (listing.Cost.Count < 1) text = Loc.GetString("store-currency-free"); + else + { + foreach (var (type, amount) in listing.Cost) + { + var currency = _prototypeManager.Index(type); + text += Loc.GetString("store-ui-price-display", ("amount", amount), + ("currency", Loc.GetString(currency.DisplayName, ("amount", amount)))); + } + } return text.TrimEnd(); } diff --git a/Content.Client/Store/Ui/StoreWithdrawWindow.xaml.cs b/Content.Client/Store/Ui/StoreWithdrawWindow.xaml.cs index 968e3ed610..db0e7e6807 100644 --- a/Content.Client/Store/Ui/StoreWithdrawWindow.xaml.cs +++ b/Content.Client/Store/Ui/StoreWithdrawWindow.xaml.cs @@ -2,18 +2,8 @@ using System.Linq; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.Localization; using Content.Shared.FixedPoint; using Content.Shared.Store; -using Robust.Client.UserInterface; -using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; -using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.CustomControls; -using Robust.Client.UserInterface.XAML; -using Robust.Shared.Prototypes; -using Robust.Client.Graphics; -using Content.Shared.Actions.ActionTypes; using Robust.Client.UserInterface.Controls; using Robust.Shared.Prototypes; @@ -57,13 +47,12 @@ public sealed partial class StoreWithdrawWindow : DefaultWindow _buttons.Clear(); foreach (var currency in _validCurrencies) { - Logger.Debug((currency.Value.PriceDisplay)); var button = new CurrencyWithdrawButton() { Id = currency.Value.ID, Amount = currency.Key, MinHeight = 20, - Text = Loc.GetString("store-withdraw-button-ui", ("currency",Loc.GetString(currency.Value.PriceDisplay))), + Text = Loc.GetString("store-withdraw-button-ui", ("currency",Loc.GetString(currency.Value.DisplayName, ("amount", currency.Key)))), }; button.Disabled = false; button.OnPressed += args => diff --git a/Content.Server/Store/Components/StoreComponent.cs b/Content.Server/Store/Components/StoreComponent.cs index 10b46e3e05..efc0844f24 100644 --- a/Content.Server/Store/Components/StoreComponent.cs +++ b/Content.Server/Store/Components/StoreComponent.cs @@ -71,13 +71,6 @@ public sealed class StoreComponent : Component [ViewVariables] [DataField("buySuccessSound")] public SoundSpecifier BuySuccessSound = new SoundPathSpecifier("/Audio/Effects/kaching.ogg"); - - /// - /// The sound played to the buyer when a purchase fails. - /// - [ViewVariables] - [DataField("insufficientFundsSound")] - public SoundSpecifier InsufficientFundsSound = new SoundPathSpecifier("/Audio/Effects/error.ogg"); #endregion } diff --git a/Content.Server/Store/Systems/StoreSystem.Ui.cs b/Content.Server/Store/Systems/StoreSystem.Ui.cs index b2a755e63e..16504ac279 100644 --- a/Content.Server/Store/Systems/StoreSystem.Ui.cs +++ b/Content.Server/Store/Systems/StoreSystem.Ui.cs @@ -2,7 +2,6 @@ using Content.Server.Actions; using Content.Server.Administration.Logs; using Content.Server.Mind.Components; using Content.Server.Store.Components; -using Content.Server.UserInterface; using Content.Shared.Actions.ActionTypes; using Content.Shared.FixedPoint; using Content.Shared.Hands.EntitySystems; @@ -24,6 +23,7 @@ public sealed partial class StoreSystem : EntitySystem [Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly StackSystem _stack = default!; + [Dependency] private readonly UserInterfaceSystem _ui = default!; private void InitializeUi() { @@ -42,10 +42,10 @@ public sealed partial class StoreSystem : EntitySystem if (!TryComp(user, out var actor)) return; - var ui = component.Owner.GetUIOrNull(StoreUiKey.Key); - ui?.Toggle(actor.PlayerSession); + if (!_ui.TryToggleUi(component.Owner, StoreUiKey.Key, actor.PlayerSession)) + return; - UpdateUserInterface(user, component, ui); + UpdateUserInterface(user, component); } /// @@ -58,7 +58,7 @@ public sealed partial class StoreSystem : EntitySystem { if (ui == null) { - ui = component.Owner.GetUIOrNull(StoreUiKey.Key); + ui = _ui.GetUiOrNull(component.Owner, StoreUiKey.Key); if (ui == null) return; } @@ -92,7 +92,7 @@ public sealed partial class StoreSystem : EntitySystem } var state = new StoreUpdateState(buyer, component.LastAvailableListings, allCurrency); - ui.SetState(state); + _ui.SetUiState(ui, state); } /// @@ -114,10 +114,7 @@ public sealed partial class StoreSystem : EntitySystem if (listing.Conditions != null) { var args = new ListingConditionArgs(msg.Buyer, component.Owner, listing, EntityManager); - var conditionsMet = true; - - foreach (var condition in listing.Conditions.Where(condition => !condition.Condition(args))) - conditionsMet = false; + var conditionsMet = listing.Conditions.All(condition => condition.Condition(args)); if (!conditionsMet) return; @@ -128,7 +125,6 @@ public sealed partial class StoreSystem : EntitySystem { if (!component.Balance.TryGetValue(currency.Key, out var balance) || balance < currency.Value) { - _audio.Play(component.InsufficientFundsSound, Filter.SinglePlayer(msg.Session), uid); return; } } @@ -187,35 +183,46 @@ public sealed partial class StoreSystem : EntitySystem return; //we need an actually valid entity to spawn. This check has been done earlier, but just in case. - if (proto.EntityId == null || !proto.CanWithdraw) + if (proto.Cash == null || !proto.CanWithdraw) return; - var entproto = _proto.Index(proto.EntityId); - - var amountRemaining = msg.Amount; + FixedPoint2 amountRemaining = msg.Amount; var coordinates = Transform(msg.Buyer).Coordinates; - if (entproto.HasComponent()) - { - while (amountRemaining > 0) - { - var ent = Spawn(proto.EntityId, coordinates); - var stackComponent = Comp(ent); //we already know it exists - var amountPerStack = Math.Min(stackComponent.MaxCount, amountRemaining); - - _stack.SetCount(ent, amountPerStack, stackComponent); - amountRemaining -= amountPerStack; - _hands.TryPickupAnyHand(msg.Buyer, ent); - } - } - else //please for the love of christ give your currency stack component + var sortedCashValues = proto.Cash.Keys.OrderByDescending(x => x).ToList(); + foreach (var value in sortedCashValues) { - while (amountRemaining > 0) + var cashId = proto.Cash[value]; + + if (!_proto.TryIndex(cashId, out var cashProto)) + continue; + + //how many times this subdivision fits in the amount remaining + var amountToSpawn = (int) Math.Floor((double) (amountRemaining / value)); + if (cashProto.HasComponent()) { - var ent = Spawn(proto.EntityId, coordinates); - _hands.TryPickupAnyHand(msg.Buyer, ent); - amountRemaining--; + var amountToRemove = amountToSpawn; //we don't want to modify amountToSpawn, as we use it for calculations + while (amountToRemove > 0) + { + var ent = Spawn(cashId, coordinates); + if (!TryComp(ent, out var stack)) + return; //you really fucked up if you got here + + var maxAmount = Math.Min(amountToRemove, stack.MaxCount); //limit it based on max stack amount + _stack.SetCount(ent, maxAmount, stack); + _hands.TryPickupAnyHand(msg.Buyer, ent); + amountToRemove -= maxAmount; + } } + else //please for the love of christ give your currency stack component + { + for (var i = 0; i < amountToSpawn; i++) + { + var ent = Spawn(cashId, coordinates); + _hands.TryPickupAnyHand(msg.Buyer, ent); + } + } + amountRemaining -= value * amountToSpawn; } component.Balance[msg.Currency] -= msg.Amount; diff --git a/Content.Server/Store/Systems/StoreSystem.cs b/Content.Server/Store/Systems/StoreSystem.cs index e55a5d228e..aecb36e5be 100644 --- a/Content.Server/Store/Systems/StoreSystem.cs +++ b/Content.Server/Store/Systems/StoreSystem.cs @@ -54,6 +54,7 @@ public sealed partial class StoreSystem : EntitySystem //if you somehow are inserting cash before the store initializes. if (!store.Opened) { + RefreshAllListings(store); InitializeFromPreset(store.Preset, store); store.Opened = true; } @@ -147,7 +148,8 @@ public sealed partial class StoreSystem : EntitySystem if (component.Balance == new Dictionary() && preset.InitialBalance != null) //if we don't have a value stored, use the preset TryAddCurrency(preset.InitialBalance, component); - var ui = component.Owner.GetUIOrNull(StoreUiKey.Key); - ui?.SetState(new StoreInitializeState(preset.StoreName)); + var ui = _ui.GetUiOrNull(component.Owner, StoreUiKey.Key); + if (ui != null) + _ui.SetUiState(ui, new StoreInitializeState(preset.StoreName)); } } diff --git a/Content.Shared/Store/CurrencyPrototype.cs b/Content.Shared/Store/CurrencyPrototype.cs index 79438e5a75..b01b2969e9 100644 --- a/Content.Shared/Store/CurrencyPrototype.cs +++ b/Content.Shared/Store/CurrencyPrototype.cs @@ -1,6 +1,6 @@ +using Content.Shared.FixedPoint; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Store; @@ -18,22 +18,18 @@ public sealed class CurrencyPrototype : IPrototype public string ID { get; } = default!; /// - /// The Loc string used for displaying the balance of a certain currency at the top of the store ui + /// The Loc string used for displaying the currency in the store ui. + /// doesn't necessarily refer to the full name of the currency, only + /// that which is displayed to the user. /// - [DataField("balanceDisplay")] - public string BalanceDisplay { get; } = string.Empty; - - /// - /// The Loc string used for displaying the price of listings in store UI - /// - [DataField("priceDisplay")] - public string PriceDisplay { get; } = string.Empty; + [DataField("displayName")] + public string DisplayName { get; } = string.Empty; /// /// The physical entity of the currency /// - [DataField("entityId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? EntityId { get; } + [DataField("cash")] //TODO: you get your customTypeSerializer when FixedPoint2 works in them! -emo + public Dictionary? Cash { get; } /// /// Whether or not this currency can be withdrawn from a shop by a player. Requires a valid entityId. diff --git a/Resources/Locale/en-US/store/currency.ftl b/Resources/Locale/en-US/store/currency.ftl index 0bfe32bf8a..a5843a84e5 100644 --- a/Resources/Locale/en-US/store/currency.ftl +++ b/Resources/Locale/en-US/store/currency.ftl @@ -1,14 +1,9 @@ store-currency-inserted = {CAPITALIZE(THE($used))} is inserted into the {THE($target)}. store-currency-free = Free -store-currency-balance-display-debugdollar = Debug Dollar: {$amount} -store-currency-price-display-debugdollar = {$amount -> - [one] {$amount} Debug Dollar - *[other] {$amount} Debug Dollars +store-currency-display-debugdollar = {$amount -> + [one] Debug Dollar + *[other] Debug Dollars } - -store-currency-balance-display-telecrystal = TC: {$amount} -store-currency-price-display-telecrystal = {$amount} TC - -store-currency-balance-display-stolen-essence = Stolen Essence: {$amount} -store-currency-price-display-stolen-essence = {$amount} Stolen Essence \ No newline at end of file +store-currency-display-telecrystal = TC +store-currency-display-stolen-essence = Stolen Essence \ No newline at end of file diff --git a/Resources/Locale/en-US/store/store.ftl b/Resources/Locale/en-US/store/store.ftl index b7dc76cb08..96778ef47f 100644 --- a/Resources/Locale/en-US/store/store.ftl +++ b/Resources/Locale/en-US/store/store.ftl @@ -1,4 +1,6 @@ store-ui-default-title = Store store-ui-default-withdraw-text = Withdraw +store-ui-balance-display = {$currency}: {$amount} +store-ui-price-display = {$amount} {$currency} store-withdraw-button-ui = Withdraw {$currency} \ No newline at end of file diff --git a/Resources/Prototypes/Store/currency.yml b/Resources/Prototypes/Store/currency.yml index 8911b46475..91039a75e6 100644 --- a/Resources/Prototypes/Store/currency.yml +++ b/Resources/Prototypes/Store/currency.yml @@ -1,18 +1,16 @@ - type: currency id: Telecrystal - balanceDisplay: store-currency-balance-display-telecrystal - priceDisplay: store-currency-price-display-telecrystal - entityId: Telecrystal1 + displayName: store-currency-display-telecrystal + cash: + 1: Telecrystal1 canWithdraw: true - type: currency id: StolenEssence - balanceDisplay: store-currency-balance-display-stolen-essence - priceDisplay: store-currency-price-display-stolen-essence + displayName: store-currency-display-stolen-essence canWithdraw: false #debug - type: currency id: DebugDollar - balanceDisplay: store-currency-balance-display-debugdollar - priceDisplay: store-currency-price-display-debugdollar \ No newline at end of file + displayName: store-currency-display-debugdollar \ No newline at end of file