Skip to content

Commit

Permalink
[Port] Uplink Discounts From White Dream (#930)
Browse files Browse the repository at this point in the history
# Description

This is a port of
WWhiteDreamProject/wwdpublic#11 from White
Dream. This feature selects random items in the traitor uplink each
round to be discounted and moved to the Discount tab, which are the same
for every traitor. This in theory helps encourage players to be
spontaneous, and use items that they otherwise might not normally
consider using, which helps mix things up from round to round.

<details><summary><h1>Media</h1></summary>
<p>

> # Описание PR
> Порт скидок в аплинке.
> 
> # Изменения
> 🆑 Spatison
> 
> * add: Added discounts in uplink / Добавлены скидки в аплинк

</p>
</details>

# Changelog

:cl: Spatison
add: Added discounts in uplink / Добавлены скидки в аплинк

---------

Signed-off-by: VMSolidus <[email protected]>
Co-authored-by: Spatison <[email protected]>
Co-authored-by: DEATHB4DEFEAT <[email protected]>
  • Loading branch information
3 people committed Sep 21, 2024
1 parent 49d128c commit f126bb4
Show file tree
Hide file tree
Showing 13 changed files with 189 additions and 2 deletions.
5 changes: 5 additions & 0 deletions Content.Client/Store/Ui/StoreMenu.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Content.Client.Message;
using Content.Shared.FixedPoint;
using Content.Shared.Store;
using Content.Client.Stylesheets;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
Expand Down Expand Up @@ -147,6 +148,10 @@ private void AddListingGui(ListingData listing)
}

var newListing = new StoreListingControl(listing, GetListingPriceString(listing), hasBalance, texture);

if (listing.DiscountValue > 0)
newListing.StoreItemBuyButton.AddStyleClass(StyleNano.ButtonColorDangerDefault.ToString());

newListing.StoreItemBuyButton.OnButtonDown += args
=> OnListingButtonPressed?.Invoke(args, listing);

Expand Down
6 changes: 6 additions & 0 deletions Content.Server/Store/Systems/StoreSystem.Ui.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,12 @@ private void OnBuyRequest(EntityUid uid, StoreComponent component, StoreBuyListi
listing.PurchaseAmount++; //track how many times something has been purchased
_audio.PlayEntity(component.BuySuccessSound, msg.Session, uid); //cha-ching!

if (listing.SaleLimit != 0 && listing.DiscountValue > 0 && listing.PurchaseAmount >= listing.SaleLimit)
{
listing.DiscountValue = 0;
listing.Cost = listing.OldCost;
}

UpdateUserInterface(buyer, uid, component);
}

Expand Down
6 changes: 5 additions & 1 deletion Content.Server/Store/Systems/StoreSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
using System.Linq;
using Content.Server.StoreDiscount;
using Robust.Shared.Utility;

namespace Content.Server.Store.Systems;
Expand All @@ -22,6 +23,7 @@ public sealed partial class StoreSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly StoreDiscountSystem _storeDiscount = default!;

public override void Initialize()
{
Expand Down Expand Up @@ -199,6 +201,8 @@ public void InitializeFromPreset(StorePresetPrototype preset, EntityUid uid, Sto
if (component.Balance == new Dictionary<string, FixedPoint2>() && preset.InitialBalance != null) //if we don't have a value stored, use the preset
TryAddCurrency(preset.InitialBalance, uid, component);

_storeDiscount.ApplyDiscounts(component.Listings, preset);

var ui = _ui.GetUiOrNull(uid, StoreUiKey.Key);
if (ui != null)
{
Expand All @@ -225,7 +229,7 @@ public CurrencyInsertAttemptEvent(EntityUid user, EntityUid target, EntityUid us


/// <summary>
/// Nyano/DeltaV Code. For penguin bombs and what not.
/// Nyano/DeltaV Code. For penguin bombs and what not.
/// Raised on an item when it is purchased.
/// An item may need to set it upself up for its purchaser.
/// For example, to make sure it isn't hostile to them or
Expand Down
55 changes: 55 additions & 0 deletions Content.Server/StoreDiscount/StoreDiscountSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Linq;
using Content.Shared.FixedPoint;
using Content.Shared.Store;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;

namespace Content.Server.StoreDiscount;

public sealed class StoreDiscountSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;

public void ApplyDiscounts(IEnumerable<ListingData> listings, StorePresetPrototype store)
{
if (!store.Sales.Enabled)
return;

var count = _random.Next(store.Sales.MinItems, store.Sales.MaxItems + 1);

listings = listings
.Where(l =>
!l.SaleBlacklist
&& l.Cost.Any(x => x.Value > 1)
&& store.Categories.Overlaps(ChangedFormatCategories(l.Categories)))
.OrderBy(_ => _random.Next()).Take(count).ToList();

foreach (var listing in listings)
{
var sale = GetDiscount(store.Sales.MinMultiplier, store.Sales.MaxMultiplier);
var newCost = listing.Cost.ToDictionary(x => x.Key,
x => FixedPoint2.New(Math.Max(1, (int) MathF.Round(x.Value.Float() * sale))));

if (listing.Cost.All(x => x.Value.Int() == newCost[x.Key].Int()))
continue;

var key = listing.Cost.First(x => x.Value > 0).Key;
listing.OldCost = listing.Cost;
listing.DiscountValue = 100 - (newCost[key] / listing.Cost[key] * 100).Int();
listing.Cost = newCost;
listing.Categories = new() {store.Sales.SalesCategory};
}
}

private IEnumerable<string> ChangedFormatCategories(List<ProtoId<StoreCategoryPrototype>> categories)
{
var modified = from p in categories select p.Id;

return modified;
}

private float GetDiscount(float minMultiplier, float maxMultiplier)
{
return _random.NextFloat() * (maxMultiplier - minMultiplier) + minMultiplier;
}
}
5 changes: 5 additions & 0 deletions Content.Shared/Store/ListingLocalisationHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public static string GetLocalisedNameOrEntityName(ListingData listingData, IProt
else if (listingData.ProductEntity != null)
name = prototypeManager.Index(listingData.ProductEntity.Value).Name;

if (listingData.DiscountValue > 0)
name += " " + Loc.GetString("store-sales-amount", ("amount", listingData.DiscountValue));
else if (listingData.OldCost.Count > 0)
name += " " + Loc.GetString("store-sales-over");

return name;
}

Expand Down
18 changes: 18 additions & 0 deletions Content.Shared/Store/ListingPrototype.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,19 @@ public partial class ListingData : IEquatable<ListingData>, ICloneable
[DataField]
public TimeSpan RestockTime = TimeSpan.Zero;

[DataField]
public int SaleLimit = 3;

[DataField]
public bool SaleBlacklist;

public int DiscountValue;

public Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> OldCost = new();

[DataField]
public List<string> Components = new();

public bool Equals(ListingData? listing)
{
if (listing == null)
Expand Down Expand Up @@ -166,6 +179,11 @@ public object Clone()
ProductEvent = ProductEvent,
PurchaseAmount = PurchaseAmount,
RestockTime = RestockTime,
SaleLimit = SaleLimit,
SaleBlacklist = SaleBlacklist,
DiscountValue = DiscountValue,
OldCost = OldCost,
Components = Components,
};
}
}
Expand Down
4 changes: 4 additions & 0 deletions Content.Shared/Store/StorePresetPrototype.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Content.Shared.StoreDiscount;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
Expand Down Expand Up @@ -38,4 +39,7 @@ public sealed partial class StorePresetPrototype : IPrototype
/// </summary>
[DataField("currencyWhitelist", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<CurrencyPrototype>))]
public HashSet<string> CurrencyWhitelist { get; private set; } = new();

[DataField]
public SalesSpecifier Sales { get; private set; } = new();
}
38 changes: 38 additions & 0 deletions Content.Shared/StoreDiscount/SalesSpecifier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace Content.Shared.StoreDiscount;

[DataDefinition]
public sealed partial class SalesSpecifier
{
[DataField]
public bool Enabled { get; private set; }

[DataField]
public float MinMultiplier { get; private set; }

[DataField]
public float MaxMultiplier { get; private set; }

[DataField]
public int MinItems { get; private set; }

[DataField]
public int MaxItems { get; private set; }

[DataField]
public string SalesCategory { get; private set; } = string.Empty;

public SalesSpecifier()
{
}

public SalesSpecifier(bool enabled, float minMultiplier, float maxMultiplier, int minItems, int maxItems,
string salesCategory)
{
Enabled = enabled;
MinMultiplier = minMultiplier;
MaxMultiplier = maxMultiplier;
MinItems = minItems;
MaxItems = maxItems;
SalesCategory = salesCategory;
}
}
2 changes: 2 additions & 0 deletions Resources/Locale/en-US/store/sales.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
store-sales-amount = [DISCOUNT] { $amount }%!
store-sales-over = [The sale is over]
2 changes: 2 additions & 0 deletions Resources/Locale/ru-RU/store/sales.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
store-sales-amount = [СКИДКА] { $amount }%!
store-sales-over = [Скидка закончилась]
Loading

0 comments on commit f126bb4

Please sign in to comment.