Adds a refund button & action upgrades for stores (#24518)
* adds refunds to stores * Adds method to check for starting map * comments, datafields, some requested changes * turns event into ref event * Adds datafields * Switches to entity terminating event * Changes store entity to be nullable and checks if store is terminating to remove reference. * Tryadd instead of containskey * Adds a refund disable method, disables refund on bought ent container changes if not an action * Removes datafield specification * Readds missing using statement * Removes unused using statements * What the heck is listing data --------- Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
@@ -48,6 +48,11 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
|
|||||||
{
|
{
|
||||||
SendMessage(new StoreRequestUpdateInterfaceMessage());
|
SendMessage(new StoreRequestUpdateInterfaceMessage());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_menu.OnRefundAttempt += (_) =>
|
||||||
|
{
|
||||||
|
SendMessage(new StoreRequestRefundMessage());
|
||||||
|
};
|
||||||
}
|
}
|
||||||
protected override void UpdateState(BoundUserInterfaceState state)
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
{
|
{
|
||||||
@@ -64,6 +69,7 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
|
|||||||
|
|
||||||
_menu.UpdateListing(msg.Listings.ToList());
|
_menu.UpdateListing(msg.Listings.ToList());
|
||||||
_menu.SetFooterVisibility(msg.ShowFooter);
|
_menu.SetFooterVisibility(msg.ShowFooter);
|
||||||
|
_menu.UpdateRefund(msg.AllowRefund);
|
||||||
break;
|
break;
|
||||||
case StoreInitializeState msg:
|
case StoreInitializeState msg:
|
||||||
_windowName = msg.Name;
|
_windowName = msg.Name;
|
||||||
|
|||||||
@@ -22,6 +22,11 @@
|
|||||||
MinWidth="64"
|
MinWidth="64"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Text="{Loc 'store-ui-default-withdraw-text'}" />
|
Text="{Loc 'store-ui-default-withdraw-text'}" />
|
||||||
|
<Button
|
||||||
|
Name="RefundButton"
|
||||||
|
MinWidth="64"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
Text="Refund" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<PanelContainer VerticalExpand="True">
|
<PanelContainer VerticalExpand="True">
|
||||||
<PanelContainer.PanelOverride>
|
<PanelContainer.PanelOverride>
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ public sealed partial class StoreMenu : DefaultWindow
|
|||||||
public event Action<BaseButton.ButtonEventArgs, string>? OnCategoryButtonPressed;
|
public event Action<BaseButton.ButtonEventArgs, string>? OnCategoryButtonPressed;
|
||||||
public event Action<BaseButton.ButtonEventArgs, string, int>? OnWithdrawAttempt;
|
public event Action<BaseButton.ButtonEventArgs, string, int>? OnWithdrawAttempt;
|
||||||
public event Action<BaseButton.ButtonEventArgs>? OnRefreshButtonPressed;
|
public event Action<BaseButton.ButtonEventArgs>? OnRefreshButtonPressed;
|
||||||
|
public event Action<BaseButton.ButtonEventArgs>? OnRefundAttempt;
|
||||||
|
|
||||||
public Dictionary<string, FixedPoint2> Balance = new();
|
public Dictionary<string, FixedPoint2> Balance = new();
|
||||||
public string CurrentCategory = string.Empty;
|
public string CurrentCategory = string.Empty;
|
||||||
@@ -44,6 +45,8 @@ public sealed partial class StoreMenu : DefaultWindow
|
|||||||
|
|
||||||
WithdrawButton.OnButtonDown += OnWithdrawButtonDown;
|
WithdrawButton.OnButtonDown += OnWithdrawButtonDown;
|
||||||
RefreshButton.OnButtonDown += OnRefreshButtonDown;
|
RefreshButton.OnButtonDown += OnRefreshButtonDown;
|
||||||
|
RefundButton.OnButtonDown += OnRefundButtonDown;
|
||||||
|
|
||||||
if (Window != null)
|
if (Window != null)
|
||||||
Window.Title = name;
|
Window.Title = name;
|
||||||
}
|
}
|
||||||
@@ -116,6 +119,11 @@ public sealed partial class StoreMenu : DefaultWindow
|
|||||||
_withdrawWindow.OnWithdrawAttempt += OnWithdrawAttempt;
|
_withdrawWindow.OnWithdrawAttempt += OnWithdrawAttempt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnRefundButtonDown(BaseButton.ButtonEventArgs args)
|
||||||
|
{
|
||||||
|
OnRefundAttempt?.Invoke(args);
|
||||||
|
}
|
||||||
|
|
||||||
private void AddListingGui(ListingData listing)
|
private void AddListingGui(ListingData listing)
|
||||||
{
|
{
|
||||||
if (!listing.Categories.Contains(CurrentCategory))
|
if (!listing.Categories.Contains(CurrentCategory))
|
||||||
@@ -262,6 +270,11 @@ public sealed partial class StoreMenu : DefaultWindow
|
|||||||
_withdrawWindow?.Close();
|
_withdrawWindow?.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateRefund(bool allowRefund)
|
||||||
|
{
|
||||||
|
RefundButton.Disabled = !allowRefund;
|
||||||
|
}
|
||||||
|
|
||||||
private sealed class StoreCategoryButton : Button
|
private sealed class StoreCategoryButton : Button
|
||||||
{
|
{
|
||||||
public string? Id;
|
public string? Id;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Store;
|
using Content.Shared.Store;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
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.Dictionary;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
|
||||||
@@ -59,6 +60,30 @@ public sealed partial class StoreComponent : Component
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public HashSet<ListingData> LastAvailableListings = new();
|
public HashSet<ListingData> LastAvailableListings = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All current entities bought from this shop. Useful for keeping track of refunds and upgrades.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables, DataField]
|
||||||
|
public List<EntityUid> BoughtEntities = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total balance spent in this store. Used for refunds.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables, DataField]
|
||||||
|
public Dictionary<string, FixedPoint2> BalanceSpent = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls if the store allows refunds
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables, DataField]
|
||||||
|
public bool RefundAllowed;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The map the store was originally from, used to block refunds if the map is changed
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntityUid? StartingMap;
|
||||||
|
|
||||||
#region audio
|
#region audio
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The sound played to the buyer when a purchase is succesfully made.
|
/// The sound played to the buyer when a purchase is succesfully made.
|
||||||
@@ -78,3 +103,17 @@ public readonly record struct StoreAddedEvent;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public readonly record struct StoreRemovedEvent;
|
public readonly record struct StoreRemovedEvent;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Broadcast when an Entity with the <see cref="StoreRefundComponent"/> is deleted
|
||||||
|
/// </summary>
|
||||||
|
[ByRefEvent]
|
||||||
|
public readonly struct RefundEntityDeletedEvent
|
||||||
|
{
|
||||||
|
public EntityUid Uid { get; }
|
||||||
|
|
||||||
|
public RefundEntityDeletedEvent(EntityUid uid)
|
||||||
|
{
|
||||||
|
Uid = uid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
13
Content.Server/Store/StoreRefundComponent.cs
Normal file
13
Content.Server/Store/StoreRefundComponent.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Content.Server.Store.Systems;
|
||||||
|
|
||||||
|
namespace Content.Server.Store.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keeps track of entities bought from stores for refunds, especially useful if entities get deleted before they can be refunded.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(StoreSystem))]
|
||||||
|
public sealed partial class StoreRefundComponent : Component
|
||||||
|
{
|
||||||
|
[ViewVariables, DataField]
|
||||||
|
public EntityUid? StoreEntity;
|
||||||
|
}
|
||||||
56
Content.Server/Store/Systems/StoreSystem.Refund.cs
Normal file
56
Content.Server/Store/Systems/StoreSystem.Refund.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using Content.Server.Actions;
|
||||||
|
using Content.Server.Store.Components;
|
||||||
|
using Content.Shared.Actions;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
|
||||||
|
namespace Content.Server.Store.Systems;
|
||||||
|
|
||||||
|
public sealed partial class StoreSystem
|
||||||
|
{
|
||||||
|
private void InitializeRefund()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<StoreComponent, EntityTerminatingEvent>(OnStoreTerminating);
|
||||||
|
SubscribeLocalEvent<StoreRefundComponent, EntityTerminatingEvent>(OnRefundTerminating);
|
||||||
|
SubscribeLocalEvent<StoreRefundComponent, EntRemovedFromContainerMessage>(OnEntityRemoved);
|
||||||
|
SubscribeLocalEvent<StoreRefundComponent, EntInsertedIntoContainerMessage>(OnEntityInserted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEntityRemoved(EntityUid uid, StoreRefundComponent component, EntRemovedFromContainerMessage args)
|
||||||
|
{
|
||||||
|
if (component.StoreEntity == null || _actions.TryGetActionData(uid, out _) || !TryComp<StoreComponent>(component.StoreEntity.Value, out var storeComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DisableRefund(component.StoreEntity.Value, storeComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnEntityInserted(EntityUid uid, StoreRefundComponent component, EntInsertedIntoContainerMessage args)
|
||||||
|
{
|
||||||
|
if (component.StoreEntity == null || _actions.TryGetActionData(uid, out _) || !TryComp<StoreComponent>(component.StoreEntity.Value, out var storeComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
DisableRefund(component.StoreEntity.Value, storeComp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStoreTerminating(Entity<StoreComponent> ent, ref EntityTerminatingEvent args)
|
||||||
|
{
|
||||||
|
if (ent.Comp.BoughtEntities.Count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var boughtEnt in ent.Comp.BoughtEntities)
|
||||||
|
{
|
||||||
|
if (!TryComp<StoreRefundComponent>(boughtEnt, out var refundComp))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
refundComp.StoreEntity = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRefundTerminating(Entity<StoreRefundComponent> ent, ref EntityTerminatingEvent args)
|
||||||
|
{
|
||||||
|
if (ent.Comp.StoreEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var ev = new RefundEntityDeletedEvent(ent);
|
||||||
|
RaiseLocalEvent(ent.Comp.StoreEntity.Value, ref ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,11 +4,13 @@ using Content.Server.Administration.Logs;
|
|||||||
using Content.Server.PDA.Ringer;
|
using Content.Server.PDA.Ringer;
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Server.Store.Components;
|
using Content.Server.Store.Components;
|
||||||
using Content.Shared.UserInterface;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
|
using Content.Shared.Mind;
|
||||||
using Content.Shared.Store;
|
using Content.Shared.Store;
|
||||||
|
using Content.Shared.UserInterface;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
@@ -20,6 +22,9 @@ public sealed partial class StoreSystem
|
|||||||
[Dependency] private readonly IAdminLogManager _admin = default!;
|
[Dependency] private readonly IAdminLogManager _admin = default!;
|
||||||
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
||||||
[Dependency] private readonly ActionsSystem _actions = default!;
|
[Dependency] private readonly ActionsSystem _actions = default!;
|
||||||
|
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||||
|
[Dependency] private readonly ActionUpgradeSystem _actionUpgrade = default!;
|
||||||
|
[Dependency] private readonly SharedMindSystem _mind = 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!;
|
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||||
@@ -29,6 +34,13 @@ public sealed partial class StoreSystem
|
|||||||
SubscribeLocalEvent<StoreComponent, StoreRequestUpdateInterfaceMessage>(OnRequestUpdate);
|
SubscribeLocalEvent<StoreComponent, StoreRequestUpdateInterfaceMessage>(OnRequestUpdate);
|
||||||
SubscribeLocalEvent<StoreComponent, StoreBuyListingMessage>(OnBuyRequest);
|
SubscribeLocalEvent<StoreComponent, StoreBuyListingMessage>(OnBuyRequest);
|
||||||
SubscribeLocalEvent<StoreComponent, StoreRequestWithdrawMessage>(OnRequestWithdraw);
|
SubscribeLocalEvent<StoreComponent, StoreRequestWithdrawMessage>(OnRequestWithdraw);
|
||||||
|
SubscribeLocalEvent<StoreComponent, StoreRequestRefundMessage>(OnRequestRefund);
|
||||||
|
SubscribeLocalEvent<StoreComponent, RefundEntityDeletedEvent>(OnRefundEntityDeleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRefundEntityDeleted(Entity<StoreComponent> ent, ref RefundEntityDeletedEvent args)
|
||||||
|
{
|
||||||
|
ent.Comp.BoughtEntities.Remove(args.Uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -98,7 +110,7 @@ public sealed partial class StoreSystem
|
|||||||
|
|
||||||
// only tell operatives to lock their uplink if it can be locked
|
// only tell operatives to lock their uplink if it can be locked
|
||||||
var showFooter = HasComp<RingerUplinkComponent>(store);
|
var showFooter = HasComp<RingerUplinkComponent>(store);
|
||||||
var state = new StoreUpdateState(component.LastAvailableListings, allCurrency, showFooter);
|
var state = new StoreUpdateState(component.LastAvailableListings, allCurrency, showFooter, component.RefundAllowed);
|
||||||
_ui.SetUiState(ui, state);
|
_ui.SetUiState(ui, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,6 +130,7 @@ public sealed partial class StoreSystem
|
|||||||
private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListingMessage msg)
|
private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListingMessage msg)
|
||||||
{
|
{
|
||||||
var listing = component.Listings.FirstOrDefault(x => x.Equals(msg.Listing));
|
var listing = component.Listings.FirstOrDefault(x => x.Equals(msg.Listing));
|
||||||
|
|
||||||
if (listing == null) //make sure this listing actually exists
|
if (listing == null) //make sure this listing actually exists
|
||||||
{
|
{
|
||||||
Log.Debug("listing does not exist");
|
Log.Debug("listing does not exist");
|
||||||
@@ -149,10 +162,20 @@ public sealed partial class StoreSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!IsOnStartingMap(uid, component))
|
||||||
|
component.RefundAllowed = false;
|
||||||
|
else
|
||||||
|
component.RefundAllowed = true;
|
||||||
|
|
||||||
//subtract the cash
|
//subtract the cash
|
||||||
foreach (var currency in listing.Cost)
|
foreach (var (currency, value) in listing.Cost)
|
||||||
{
|
{
|
||||||
component.Balance[currency.Key] -= currency.Value;
|
component.Balance[currency] -= value;
|
||||||
|
|
||||||
|
component.BalanceSpent.TryAdd(currency, FixedPoint2.Zero);
|
||||||
|
|
||||||
|
component.BalanceSpent[currency] += value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//spawn entity
|
//spawn entity
|
||||||
@@ -160,13 +183,71 @@ public sealed partial class StoreSystem
|
|||||||
{
|
{
|
||||||
var product = Spawn(listing.ProductEntity, Transform(buyer).Coordinates);
|
var product = Spawn(listing.ProductEntity, Transform(buyer).Coordinates);
|
||||||
_hands.PickupOrDrop(buyer, product);
|
_hands.PickupOrDrop(buyer, product);
|
||||||
|
|
||||||
|
HandleRefundComp(uid, component, product);
|
||||||
|
|
||||||
|
var xForm = Transform(product);
|
||||||
|
|
||||||
|
if (xForm.ChildCount > 0)
|
||||||
|
{
|
||||||
|
var childEnumerator = xForm.ChildEnumerator;
|
||||||
|
while (childEnumerator.MoveNext(out var child))
|
||||||
|
{
|
||||||
|
component.BoughtEntities.Add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//give action
|
//give action
|
||||||
if (!string.IsNullOrWhiteSpace(listing.ProductAction))
|
if (!string.IsNullOrWhiteSpace(listing.ProductAction))
|
||||||
{
|
{
|
||||||
|
EntityUid? actionId;
|
||||||
// I guess we just allow duplicate actions?
|
// I guess we just allow duplicate actions?
|
||||||
_actions.AddAction(buyer, listing.ProductAction);
|
// Allow duplicate actions and just have a single list buy for the buy-once ones.
|
||||||
|
if (!_mind.TryGetMind(buyer, out var mind, out _))
|
||||||
|
actionId = _actions.AddAction(buyer, listing.ProductAction);
|
||||||
|
else
|
||||||
|
actionId = _actionContainer.AddAction(mind, listing.ProductAction);
|
||||||
|
|
||||||
|
// Add the newly bought action entity to the list of bought entities
|
||||||
|
// And then add that action entity to the relevant product upgrade listing, if applicable
|
||||||
|
if (actionId != null)
|
||||||
|
{
|
||||||
|
HandleRefundComp(uid, component, actionId.Value);
|
||||||
|
|
||||||
|
if (listing.ProductUpgradeID != null)
|
||||||
|
{
|
||||||
|
foreach (var upgradeListing in component.Listings)
|
||||||
|
{
|
||||||
|
if (upgradeListing.ID == listing.ProductUpgradeID)
|
||||||
|
{
|
||||||
|
upgradeListing.ProductActionEntity = actionId.Value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listing is { ProductUpgradeID: not null, ProductActionEntity: not null })
|
||||||
|
{
|
||||||
|
if (listing.ProductActionEntity != null)
|
||||||
|
{
|
||||||
|
component.BoughtEntities.Remove(listing.ProductActionEntity.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_actionUpgrade.TryUpgradeAction(listing.ProductActionEntity, out var upgradeActionId))
|
||||||
|
{
|
||||||
|
if (listing.ProductActionEntity != null)
|
||||||
|
HandleRefundComp(uid, component, listing.ProductActionEntity.Value);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listing.ProductActionEntity = upgradeActionId;
|
||||||
|
|
||||||
|
if (upgradeActionId != null)
|
||||||
|
HandleRefundComp(uid, component, upgradeActionId.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
//broadcast event
|
//broadcast event
|
||||||
@@ -225,4 +306,71 @@ public sealed partial class StoreSystem
|
|||||||
component.Balance[msg.Currency] -= msg.Amount;
|
component.Balance[msg.Currency] -= msg.Amount;
|
||||||
UpdateUserInterface(buyer, uid, component);
|
UpdateUserInterface(buyer, uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnRequestRefund(EntityUid uid, StoreComponent component, StoreRequestRefundMessage args)
|
||||||
|
{
|
||||||
|
// TODO: Remove guardian/holopara
|
||||||
|
|
||||||
|
if (args.Session.AttachedEntity is not { Valid: true } buyer)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!IsOnStartingMap(uid, component))
|
||||||
|
{
|
||||||
|
component.RefundAllowed = false;
|
||||||
|
UpdateUserInterface(buyer, uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!component.RefundAllowed || component.BoughtEntities.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (var i = component.BoughtEntities.Count; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var purchase = component.BoughtEntities[i];
|
||||||
|
|
||||||
|
if (!Exists(purchase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
component.BoughtEntities.RemoveAt(i);
|
||||||
|
|
||||||
|
if (_actions.TryGetActionData(purchase, out var actionComponent))
|
||||||
|
{
|
||||||
|
_actionContainer.RemoveAction(purchase, actionComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
EntityManager.DeleteEntity(purchase);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (currency, value) in component.BalanceSpent)
|
||||||
|
{
|
||||||
|
component.Balance[currency] += value;
|
||||||
|
}
|
||||||
|
// Reset store back to its original state
|
||||||
|
RefreshAllListings(component);
|
||||||
|
component.BalanceSpent = new();
|
||||||
|
UpdateUserInterface(buyer, uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HandleRefundComp(EntityUid uid, StoreComponent component, EntityUid purchase)
|
||||||
|
{
|
||||||
|
component.BoughtEntities.Add(purchase);
|
||||||
|
var refundComp = EnsureComp<StoreRefundComponent>(purchase);
|
||||||
|
refundComp.StoreEntity = uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsOnStartingMap(EntityUid store, StoreComponent component)
|
||||||
|
{
|
||||||
|
var xform = Transform(store);
|
||||||
|
return component.StartingMap == xform.MapUid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disables refunds for this store
|
||||||
|
/// </summary>
|
||||||
|
public void DisableRefund(EntityUid store, StoreComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(store, ref component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.RefundAllowed = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,12 +36,14 @@ public sealed partial class StoreSystem : EntitySystem
|
|||||||
|
|
||||||
InitializeUi();
|
InitializeUi();
|
||||||
InitializeCommand();
|
InitializeCommand();
|
||||||
|
InitializeRefund();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapInit(EntityUid uid, StoreComponent component, MapInitEvent args)
|
private void OnMapInit(EntityUid uid, StoreComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
RefreshAllListings(component);
|
RefreshAllListings(component);
|
||||||
InitializeFromPreset(component.Preset, uid, component);
|
InitializeFromPreset(component.Preset, uid, component);
|
||||||
|
component.StartingMap = Transform(uid).MapUid;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStartup(EntityUid uid, StoreComponent component, ComponentStartup args)
|
private void OnStartup(EntityUid uid, StoreComponent component, ComponentStartup args)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Linq;
|
|||||||
using Content.Shared.FixedPoint;
|
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;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
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.Dictionary;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||||
@@ -77,6 +78,20 @@ public partial class ListingData : IEquatable<ListingData>, ICloneable
|
|||||||
[DataField("productAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField("productAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string? ProductAction;
|
public string? ProductAction;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The listing ID of the related upgrade listing. Can be used to link a <see cref="ProductAction"/> to an
|
||||||
|
/// upgrade or to use standalone as an upgrade
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public ProtoId<ListingPrototype>? ProductUpgradeID;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Keeps track of the current action entity this is tied to, for action upgrades
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
[NonSerialized]
|
||||||
|
public EntityUid? ProductActionEntity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The event that is broadcast when the listing is purchased.
|
/// The event that is broadcast when the listing is purchased.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -105,6 +120,7 @@ public partial class ListingData : IEquatable<ListingData>, ICloneable
|
|||||||
Description != listing.Description ||
|
Description != listing.Description ||
|
||||||
ProductEntity != listing.ProductEntity ||
|
ProductEntity != listing.ProductEntity ||
|
||||||
ProductAction != listing.ProductAction ||
|
ProductAction != listing.ProductAction ||
|
||||||
|
ProductActionEntity != listing.ProductActionEntity ||
|
||||||
ProductEvent != listing.ProductEvent ||
|
ProductEvent != listing.ProductEvent ||
|
||||||
RestockTime != listing.RestockTime)
|
RestockTime != listing.RestockTime)
|
||||||
return false;
|
return false;
|
||||||
@@ -146,6 +162,8 @@ public partial class ListingData : IEquatable<ListingData>, ICloneable
|
|||||||
Priority = Priority,
|
Priority = Priority,
|
||||||
ProductEntity = ProductEntity,
|
ProductEntity = ProductEntity,
|
||||||
ProductAction = ProductAction,
|
ProductAction = ProductAction,
|
||||||
|
ProductUpgradeID = ProductUpgradeID,
|
||||||
|
ProductActionEntity = ProductActionEntity,
|
||||||
ProductEvent = ProductEvent,
|
ProductEvent = ProductEvent,
|
||||||
PurchaseAmount = PurchaseAmount,
|
PurchaseAmount = PurchaseAmount,
|
||||||
RestockTime = RestockTime,
|
RestockTime = RestockTime,
|
||||||
|
|||||||
@@ -18,11 +18,14 @@ public sealed class StoreUpdateState : BoundUserInterfaceState
|
|||||||
|
|
||||||
public readonly bool ShowFooter;
|
public readonly bool ShowFooter;
|
||||||
|
|
||||||
public StoreUpdateState(HashSet<ListingData> listings, Dictionary<string, FixedPoint2> balance, bool showFooter)
|
public readonly bool AllowRefund;
|
||||||
|
|
||||||
|
public StoreUpdateState(HashSet<ListingData> listings, Dictionary<string, FixedPoint2> balance, bool showFooter, bool allowRefund)
|
||||||
{
|
{
|
||||||
Listings = listings;
|
Listings = listings;
|
||||||
Balance = balance;
|
Balance = balance;
|
||||||
ShowFooter = showFooter;
|
ShowFooter = showFooter;
|
||||||
|
AllowRefund = allowRefund;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,3 +75,12 @@ public sealed class StoreRequestWithdrawMessage : BoundUserInterfaceMessage
|
|||||||
Amount = amount;
|
Amount = amount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used when the refund button is pressed
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class StoreRequestRefundMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user