store system currency rework (#10893)
This commit is contained in:
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 =>
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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}
|
||||||
@@ -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
|
|
||||||
Reference in New Issue
Block a user