store system currency rework (#10893)

This commit is contained in:
Nemanja
2022-09-11 02:54:16 -04:00
committed by GitHub
parent 5760151fb2
commit e43ee216f6
9 changed files with 80 additions and 97 deletions

View File

@@ -43,16 +43,14 @@ public sealed partial class StoreMenu : DefaultWindow
{ {
Balance = balance; Balance = balance;
var currency = new Dictionary<(string, FixedPoint2), CurrencyPrototype>(); var currency = balance.ToDictionary(type =>
foreach (var type in balance) (type.Key, type.Value), type => _prototypeManager.Index<CurrencyPrototype>(type.Key));
{
currency.Add((type.Key, type.Value), _prototypeManager.Index<CurrencyPrototype>(type.Key));
}
var balanceStr = string.Empty; 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()); BalanceInfo.SetMarkup(balanceStr.TrimEnd());
@@ -60,7 +58,7 @@ public sealed partial class StoreMenu : DefaultWindow
var disabled = true; var disabled = true;
foreach (var type in currency) 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; disabled = false;
} }
@@ -155,14 +153,17 @@ public sealed partial class StoreMenu : DefaultWindow
{ {
var text = string.Empty; var text = string.Empty;
foreach (var type in listing.Cost)
{
var currency = _prototypeManager.Index<CurrencyPrototype>(type.Key);
text += $"{Loc.GetString(currency.PriceDisplay, ("amount", type.Value))}\n";
}
if (listing.Cost.Count < 1) if (listing.Cost.Count < 1)
text = Loc.GetString("store-currency-free"); text = Loc.GetString("store-currency-free");
else
{
foreach (var (type, amount) in listing.Cost)
{
var currency = _prototypeManager.Index<CurrencyPrototype>(type);
text += Loc.GetString("store-ui-price-display", ("amount", amount),
("currency", Loc.GetString(currency.DisplayName, ("amount", amount))));
}
}
return text.TrimEnd(); return text.TrimEnd();
} }

View File

@@ -2,18 +2,8 @@ using System.Linq;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Localization;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Store; 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.Client.UserInterface.Controls;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -57,13 +47,12 @@ public sealed partial class StoreWithdrawWindow : DefaultWindow
_buttons.Clear(); _buttons.Clear();
foreach (var currency in _validCurrencies) foreach (var currency in _validCurrencies)
{ {
Logger.Debug((currency.Value.PriceDisplay));
var button = new CurrencyWithdrawButton() var button = new CurrencyWithdrawButton()
{ {
Id = currency.Value.ID, Id = currency.Value.ID,
Amount = currency.Key, Amount = currency.Key,
MinHeight = 20, 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.Disabled = false;
button.OnPressed += args => button.OnPressed += args =>

View File

@@ -71,13 +71,6 @@ public sealed class StoreComponent : Component
[ViewVariables] [ViewVariables]
[DataField("buySuccessSound")] [DataField("buySuccessSound")]
public SoundSpecifier BuySuccessSound = new SoundPathSpecifier("/Audio/Effects/kaching.ogg"); public SoundSpecifier BuySuccessSound = new SoundPathSpecifier("/Audio/Effects/kaching.ogg");
/// <summary>
/// The sound played to the buyer when a purchase fails.
/// </summary>
[ViewVariables]
[DataField("insufficientFundsSound")]
public SoundSpecifier InsufficientFundsSound = new SoundPathSpecifier("/Audio/Effects/error.ogg");
#endregion #endregion
} }

View File

@@ -2,7 +2,6 @@ using Content.Server.Actions;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Mind.Components; using Content.Server.Mind.Components;
using Content.Server.Store.Components; using Content.Server.Store.Components;
using Content.Server.UserInterface;
using Content.Shared.Actions.ActionTypes; using Content.Shared.Actions.ActionTypes;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Hands.EntitySystems; using Content.Shared.Hands.EntitySystems;
@@ -24,6 +23,7 @@ public sealed partial class StoreSystem : EntitySystem
[Dependency] private readonly ActionsSystem _actions = default!; [Dependency] private readonly ActionsSystem _actions = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly StackSystem _stack = default!; [Dependency] private readonly StackSystem _stack = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
private void InitializeUi() private void InitializeUi()
{ {
@@ -42,10 +42,10 @@ public sealed partial class StoreSystem : EntitySystem
if (!TryComp<ActorComponent>(user, out var actor)) if (!TryComp<ActorComponent>(user, out var actor))
return; return;
var ui = component.Owner.GetUIOrNull(StoreUiKey.Key); if (!_ui.TryToggleUi(component.Owner, StoreUiKey.Key, actor.PlayerSession))
ui?.Toggle(actor.PlayerSession); return;
UpdateUserInterface(user, component, ui); UpdateUserInterface(user, component);
} }
/// <summary> /// <summary>
@@ -58,7 +58,7 @@ public sealed partial class StoreSystem : EntitySystem
{ {
if (ui == null) if (ui == null)
{ {
ui = component.Owner.GetUIOrNull(StoreUiKey.Key); ui = _ui.GetUiOrNull(component.Owner, StoreUiKey.Key);
if (ui == null) if (ui == null)
return; return;
} }
@@ -92,7 +92,7 @@ public sealed partial class StoreSystem : EntitySystem
} }
var state = new StoreUpdateState(buyer, component.LastAvailableListings, allCurrency); var state = new StoreUpdateState(buyer, component.LastAvailableListings, allCurrency);
ui.SetState(state); _ui.SetUiState(ui, state);
} }
/// <summary> /// <summary>
@@ -114,10 +114,7 @@ public sealed partial class StoreSystem : EntitySystem
if (listing.Conditions != null) if (listing.Conditions != null)
{ {
var args = new ListingConditionArgs(msg.Buyer, component.Owner, listing, EntityManager); var args = new ListingConditionArgs(msg.Buyer, component.Owner, listing, EntityManager);
var conditionsMet = true; var conditionsMet = listing.Conditions.All(condition => condition.Condition(args));
foreach (var condition in listing.Conditions.Where(condition => !condition.Condition(args)))
conditionsMet = false;
if (!conditionsMet) if (!conditionsMet)
return; return;
@@ -128,7 +125,6 @@ public sealed partial class StoreSystem : EntitySystem
{ {
if (!component.Balance.TryGetValue(currency.Key, out var balance) || balance < currency.Value) if (!component.Balance.TryGetValue(currency.Key, out var balance) || balance < currency.Value)
{ {
_audio.Play(component.InsufficientFundsSound, Filter.SinglePlayer(msg.Session), uid);
return; return;
} }
} }
@@ -187,35 +183,46 @@ public sealed partial class StoreSystem : EntitySystem
return; return;
//we need an actually valid entity to spawn. This check has been done earlier, but just in case. //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; return;
var entproto = _proto.Index<EntityPrototype>(proto.EntityId); FixedPoint2 amountRemaining = msg.Amount;
var amountRemaining = msg.Amount;
var coordinates = Transform(msg.Buyer).Coordinates; var coordinates = Transform(msg.Buyer).Coordinates;
if (entproto.HasComponent<StackComponent>())
{
while (amountRemaining > 0)
{
var ent = Spawn(proto.EntityId, coordinates);
var stackComponent = Comp<StackComponent>(ent); //we already know it exists
var amountPerStack = Math.Min(stackComponent.MaxCount, amountRemaining); var sortedCashValues = proto.Cash.Keys.OrderByDescending(x => x).ToList();
foreach (var value in sortedCashValues)
_stack.SetCount(ent, amountPerStack, stackComponent);
amountRemaining -= amountPerStack;
_hands.TryPickupAnyHand(msg.Buyer, ent);
}
}
else //please for the love of christ give your currency stack component
{ {
while (amountRemaining > 0) var cashId = proto.Cash[value];
if (!_proto.TryIndex<EntityPrototype>(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<StackComponent>())
{ {
var ent = Spawn(proto.EntityId, coordinates); var amountToRemove = amountToSpawn; //we don't want to modify amountToSpawn, as we use it for calculations
_hands.TryPickupAnyHand(msg.Buyer, ent); while (amountToRemove > 0)
amountRemaining--; {
var ent = Spawn(cashId, coordinates);
if (!TryComp<StackComponent>(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; component.Balance[msg.Currency] -= msg.Amount;

View File

@@ -54,6 +54,7 @@ public sealed partial class StoreSystem : EntitySystem
//if you somehow are inserting cash before the store initializes. //if you somehow are inserting cash before the store initializes.
if (!store.Opened) if (!store.Opened)
{ {
RefreshAllListings(store);
InitializeFromPreset(store.Preset, store); InitializeFromPreset(store.Preset, store);
store.Opened = true; store.Opened = true;
} }
@@ -147,7 +148,8 @@ public sealed partial class StoreSystem : EntitySystem
if (component.Balance == new Dictionary<string, FixedPoint2>() && preset.InitialBalance != null) //if we don't have a value stored, use the preset if (component.Balance == new Dictionary<string, FixedPoint2>() && preset.InitialBalance != null) //if we don't have a value stored, use the preset
TryAddCurrency(preset.InitialBalance, component); TryAddCurrency(preset.InitialBalance, component);
var ui = component.Owner.GetUIOrNull(StoreUiKey.Key); var ui = _ui.GetUiOrNull(component.Owner, StoreUiKey.Key);
ui?.SetState(new StoreInitializeState(preset.StoreName)); if (ui != null)
_ui.SetUiState(ui, new StoreInitializeState(preset.StoreName));
} }
} }

View File

@@ -1,6 +1,6 @@
using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Store; namespace Content.Shared.Store;
@@ -18,22 +18,18 @@ public sealed class CurrencyPrototype : IPrototype
public string ID { get; } = default!; public string ID { get; } = default!;
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
[DataField("balanceDisplay")] [DataField("displayName")]
public string BalanceDisplay { get; } = string.Empty; public string DisplayName { get; } = string.Empty;
/// <summary>
/// The Loc string used for displaying the price of listings in store UI
/// </summary>
[DataField("priceDisplay")]
public string PriceDisplay { get; } = string.Empty;
/// <summary> /// <summary>
/// The physical entity of the currency /// The physical entity of the currency
/// </summary> /// </summary>
[DataField("entityId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))] [DataField("cash")] //TODO: you get your customTypeSerializer when FixedPoint2 works in them! -emo
public string? EntityId { get; } public Dictionary<FixedPoint2, string>? Cash { get; }
/// <summary> /// <summary>
/// Whether or not this currency can be withdrawn from a shop by a player. Requires a valid entityId. /// Whether or not this currency can be withdrawn from a shop by a player. Requires a valid entityId.

View File

@@ -1,14 +1,9 @@
store-currency-inserted = {CAPITALIZE(THE($used))} is inserted into the {THE($target)}. store-currency-inserted = {CAPITALIZE(THE($used))} is inserted into the {THE($target)}.
store-currency-free = Free store-currency-free = Free
store-currency-balance-display-debugdollar = Debug Dollar: {$amount} store-currency-display-debugdollar = {$amount ->
store-currency-price-display-debugdollar = {$amount -> [one] Debug Dollar
[one] {$amount} Debug Dollar *[other] Debug Dollars
*[other] {$amount} Debug Dollars
} }
store-currency-display-telecrystal = TC
store-currency-balance-display-telecrystal = TC: {$amount} store-currency-display-stolen-essence = Stolen Essence
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

View File

@@ -1,4 +1,6 @@
store-ui-default-title = Store store-ui-default-title = Store
store-ui-default-withdraw-text = Withdraw store-ui-default-withdraw-text = Withdraw
store-ui-balance-display = {$currency}: {$amount}
store-ui-price-display = {$amount} {$currency}
store-withdraw-button-ui = Withdraw {$currency} store-withdraw-button-ui = Withdraw {$currency}

View File

@@ -1,18 +1,16 @@
- type: currency - type: currency
id: Telecrystal id: Telecrystal
balanceDisplay: store-currency-balance-display-telecrystal displayName: store-currency-display-telecrystal
priceDisplay: store-currency-price-display-telecrystal cash:
entityId: Telecrystal1 1: Telecrystal1
canWithdraw: true canWithdraw: true
- type: currency - type: currency
id: StolenEssence id: StolenEssence
balanceDisplay: store-currency-balance-display-stolen-essence displayName: store-currency-display-stolen-essence
priceDisplay: store-currency-price-display-stolen-essence
canWithdraw: false canWithdraw: false
#debug #debug
- type: currency - type: currency
id: DebugDollar id: DebugDollar
balanceDisplay: store-currency-balance-display-debugdollar displayName: store-currency-display-debugdollar
priceDisplay: store-currency-price-display-debugdollar