Clean up store system (#28463)

This commit is contained in:
Nemanja
2024-06-01 11:29:17 -04:00
committed by GitHub
parent 19be94c9ea
commit be6f55a090
26 changed files with 127 additions and 209 deletions

View File

@@ -1,6 +1,7 @@
using Content.Shared.Store;
using JetBrains.Annotations;
using System.Linq;
using Content.Shared.Store.Components;
using Robust.Shared.Prototypes;
namespace Content.Client.Store.Ui;
@@ -13,9 +14,6 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
[ViewVariables]
private StoreMenu? _menu;
[ViewVariables]
private string _windowName = Loc.GetString("store-ui-default-title");
[ViewVariables]
private string _search = string.Empty;
@@ -28,7 +26,9 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
protected override void Open()
{
_menu = new StoreMenu(_windowName);
_menu = new StoreMenu();
if (EntMan.TryGetComponent<StoreComponent>(Owner, out var store))
_menu.Title = Loc.GetString(store.Name);
_menu.OpenCentered();
_menu.OnClose += Close;
@@ -64,25 +64,15 @@ public sealed class StoreBoundUserInterface : BoundUserInterface
{
base.UpdateState(state);
if (_menu == null)
return;
switch (state)
{
case StoreUpdateState msg:
_listings = msg.Listings;
_menu.UpdateBalance(msg.Balance);
_menu?.UpdateBalance(msg.Balance);
UpdateListingsWithSearchFilter();
_menu.SetFooterVisibility(msg.ShowFooter);
_menu.UpdateRefund(msg.AllowRefund);
break;
case StoreInitializeState msg:
_windowName = msg.Name;
if (_menu != null && _menu.Window != null)
{
_menu.Window.Title = msg.Name;
}
_menu?.SetFooterVisibility(msg.ShowFooter);
_menu?.UpdateRefund(msg.AllowRefund);
break;
}
}

View File

@@ -32,7 +32,7 @@ public sealed partial class StoreMenu : DefaultWindow
private List<ListingData> _cachedListings = new();
public StoreMenu(string name)
public StoreMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
@@ -40,9 +40,6 @@ public sealed partial class StoreMenu : DefaultWindow
WithdrawButton.OnButtonDown += OnWithdrawButtonDown;
RefundButton.OnButtonDown += OnRefundButtonDown;
SearchBar.OnTextChanged += _ => SearchTextUpdated?.Invoke(this, SearchBar.Text);
if (Window != null)
Window.Title = name;
}
public void UpdateBalance(Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> balance)

View File

@@ -25,6 +25,7 @@ using Robust.Shared.Random;
using Robust.Shared.Utility;
using System.Linq;
using Content.Server.GameTicking.Components;
using Content.Shared.Store.Components;
namespace Content.Server.GameTicking.Rules;

View File

@@ -20,6 +20,7 @@ using Robust.Shared.Random;
using System.Numerics;
using Content.Shared.Movement.Pulling.Components;
using Content.Shared.Movement.Pulling.Systems;
using Content.Shared.Store.Components;
using Robust.Shared.Collections;
using Robust.Shared.Map.Components;

View File

@@ -8,6 +8,7 @@ using Content.Server.PDA.Ringer;
using Content.Server.Station.Systems;
using Content.Server.Store.Components;
using Content.Server.Store.Systems;
using Content.Server.Traitor.Uplink;
using Content.Shared.Access.Components;
using Content.Shared.CartridgeLoader;
using Content.Shared.Chat;
@@ -15,6 +16,7 @@ using Content.Shared.Light;
using Content.Shared.Light.Components;
using Content.Shared.Light.EntitySystems;
using Content.Shared.PDA;
using Content.Shared.Store.Components;
using Robust.Server.Containers;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
@@ -152,7 +154,7 @@ namespace Content.Server.PDA
var address = GetDeviceNetAddress(uid);
var hasInstrument = HasComp<InstrumentComponent>(uid);
var showUplink = HasComp<StoreComponent>(uid) && IsUnlocked(uid);
var showUplink = HasComp<UplinkComponent>(uid) && IsUnlocked(uid);
UpdateStationName(uid, pda);
UpdateAlertLevel(uid, pda);
@@ -237,8 +239,8 @@ namespace Content.Server.PDA
return;
// check if its locked again to prevent malicious clients opening locked uplinks
if (TryComp<StoreComponent>(uid, out var store) && IsUnlocked(uid))
_store.ToggleUi(msg.Actor, uid, store);
if (HasComp<UplinkComponent>(uid) && IsUnlocked(uid))
_store.ToggleUi(msg.Actor, uid);
}
private void OnUiMessage(EntityUid uid, PdaComponent pda, PdaLockUplinkMessage msg)

View File

@@ -6,6 +6,7 @@ using Content.Shared.PDA;
using Content.Shared.PDA.Ringer;
using Content.Shared.Popups;
using Content.Shared.Store;
using Content.Shared.Store.Components;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Network;

View File

@@ -17,6 +17,7 @@ using Content.Shared.Popups;
using Content.Shared.Revenant;
using Content.Shared.Revenant.Components;
using Content.Shared.StatusEffect;
using Content.Shared.Store.Components;
using Content.Shared.Stunnable;
using Content.Shared.Tag;
using Robust.Server.GameObjects;

View File

@@ -1,6 +1,7 @@
using Content.Server.Store.Components;
using Content.Server.Store.Systems;
using Content.Shared.Store;
using Content.Shared.Store.Components;
using Robust.Shared.Prototypes;
namespace Content.Server.Store.Conditions;

View File

@@ -1,7 +1,9 @@
using System.Linq;
using Content.Server.Store.Components;
using Content.Shared.FixedPoint;
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.Store.Components;
using Robust.Shared.Console;
namespace Content.Server.Store.Systems;
@@ -58,7 +60,7 @@ public sealed partial class StoreSystem
if (args.Length == 2 && NetEntity.TryParse(args[0], out var uidNet) && TryGetEntity(uidNet, out var uid))
{
if (TryComp<StoreComponent>(uid, out var store))
return CompletionResult.FromHintOptions(store.CurrencyWhitelist, "<currency prototype>");
return CompletionResult.FromHintOptions(store.CurrencyWhitelist.Select(p => p.ToString()), "<currency prototype>");
}
return CompletionResult.Empty;

View File

@@ -1,5 +1,6 @@
using Content.Server.Store.Components;
using Content.Shared.Store;
using Content.Shared.Store.Components;
using Robust.Shared.Prototypes;
namespace Content.Server.Store.Systems;
@@ -80,7 +81,11 @@ public sealed partial class StoreSystem
/// <param name="categories">What categories to filter by.</param>
/// <param name="storeEntity">The physial entity of the store. Can be null.</param>
/// <returns>The available listings.</returns>
public IEnumerable<ListingData> GetAvailableListings(EntityUid buyer, HashSet<ListingData>? listings, HashSet<string> categories, EntityUid? storeEntity = null)
public IEnumerable<ListingData> GetAvailableListings(
EntityUid buyer,
HashSet<ListingData>? listings,
HashSet<ProtoId<StoreCategoryPrototype>> categories,
EntityUid? storeEntity = null)
{
listings ??= GetAllListings();
@@ -117,7 +122,7 @@ public sealed partial class StoreSystem
/// <param name="listing">The listing itself.</param>
/// <param name="categories">The categories to check through.</param>
/// <returns>If the listing was present in one of the categories.</returns>
public bool ListingHasCategory(ListingData listing, HashSet<string> categories)
public bool ListingHasCategory(ListingData listing, HashSet<ProtoId<StoreCategoryPrototype>> categories)
{
foreach (var cat in categories)
{

View File

@@ -1,4 +1,5 @@
using Content.Server.Store.Components;
using Content.Shared.Store.Components;
using Robust.Shared.Containers;
namespace Content.Server.Store.Systems;

View File

@@ -10,6 +10,7 @@ using Content.Shared.FixedPoint;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Mind;
using Content.Shared.Store;
using Content.Shared.Store.Components;
using Content.Shared.UserInterface;
using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
@@ -82,16 +83,11 @@ public sealed partial class StoreSystem
/// <param name="user">The person who if opening the store ui. Listings are filtered based on this.</param>
/// <param name="store">The store entity itself</param>
/// <param name="component">The store component being refreshed.</param>
/// <param name="ui"></param>
public void UpdateUserInterface(EntityUid? user, EntityUid store, StoreComponent? component = null)
{
if (!Resolve(store, ref component))
return;
// TODO: Why is the state not being set unless this?
if (!_ui.HasUi(store, StoreUiKey.Key))
return;
//this is the person who will be passed into logic for all listing filtering.
if (user != null) //if we have no "buyer" for this update, then don't update the listings
{
@@ -259,7 +255,8 @@ public sealed partial class StoreSystem
}
//log dat shit.
_admin.Add(LogType.StorePurchase, LogImpact.Low,
_admin.Add(LogType.StorePurchase,
LogImpact.Low,
$"{ToPrettyString(buyer):player} purchased listing \"{ListingLocalisationHelpers.GetLocalisedNameOrEntityName(listing, _prototypeManager)}\" from {ToPrettyString(uid)}");
listing.PurchaseAmount++; //track how many times something has been purchased

View File

@@ -5,11 +5,10 @@ using Content.Shared.Implants.Components;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Content.Shared.Stacks;
using Content.Shared.Store;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
using System.Linq;
using Content.Shared.Store.Components;
using Robust.Shared.Utility;
namespace Content.Server.Store.Systems;
@@ -44,7 +43,6 @@ public sealed partial class StoreSystem : EntitySystem
private void OnMapInit(EntityUid uid, StoreComponent component, MapInitEvent args)
{
RefreshAllListings(component);
InitializeFromPreset(component.Preset, uid, component);
component.StartingMap = Transform(uid).MapUid;
}
@@ -54,7 +52,6 @@ public sealed partial class StoreSystem : EntitySystem
if (MetaData(uid).EntityLifeStage == EntityLifeStage.MapInitialized)
{
RefreshAllListings(component);
InitializeFromPreset(component.Preset, uid, component);
}
var ev = new StoreAddedEvent();
@@ -167,43 +164,6 @@ public sealed partial class StoreSystem : EntitySystem
UpdateUserInterface(null, uid, store);
return true;
}
/// <summary>
/// Initializes a store based on a preset ID
/// </summary>
/// <param name="preset">The ID of a store preset prototype</param>
/// <param name="uid"></param>
/// <param name="component">The store being initialized</param>
public void InitializeFromPreset(string? preset, EntityUid uid, StoreComponent component)
{
if (preset == null)
return;
if (!_proto.TryIndex<StorePresetPrototype>(preset, out var proto))
return;
InitializeFromPreset(proto, uid, component);
}
/// <summary>
/// Initializes a store based on a given preset
/// </summary>
/// <param name="preset">The StorePresetPrototype</param>
/// <param name="uid"></param>
/// <param name="component">The store being initialized</param>
public void InitializeFromPreset(StorePresetPrototype preset, EntityUid uid, StoreComponent component)
{
component.Preset = preset.ID;
component.CurrencyWhitelist.UnionWith(preset.CurrencyWhitelist);
component.Categories.UnionWith(preset.Categories);
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);
if (_ui.HasUi(uid, StoreUiKey.Key))
{
_ui.SetUiState(uid, StoreUiKey.Key, new StoreInitializeState(preset.StoreName));
}
}
}
public sealed class CurrencyInsertAttemptEvent : CancellableEntityEventArgs

View File

@@ -1,6 +1,3 @@
using Content.Shared.Store;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Traitor.Uplink.SurplusBundle;
/// <summary>
@@ -12,14 +9,6 @@ public sealed partial class SurplusBundleComponent : Component
/// <summary>
/// Total price of all content inside bundle.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
[DataField("totalPrice")]
[DataField]
public int TotalPrice = 20;
/// <summary>
/// The preset that will be used to get all the listings.
/// Currently just defaults to the basic uplink.
/// </summary>
[DataField("storePreset", customTypeSerializer: typeof(PrototypeIdSerializer<StorePresetPrototype>))]
public string StorePreset = "StorePresetUplink";
}

View File

@@ -3,76 +3,67 @@ using Content.Server.Storage.EntitySystems;
using Content.Server.Store.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Store;
using Robust.Shared.Prototypes;
using Content.Shared.Store.Components;
using Robust.Shared.Random;
namespace Content.Server.Traitor.Uplink.SurplusBundle;
public sealed class SurplusBundleSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly EntityStorageSystem _entityStorage = default!;
[Dependency] private readonly StoreSystem _store = default!;
private ListingData[] _listings = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SurplusBundleComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<SurplusBundleComponent, ComponentInit>(OnInit);
}
private void OnInit(EntityUid uid, SurplusBundleComponent component, ComponentInit args)
{
var storePreset = _prototypeManager.Index<StorePresetPrototype>(component.StorePreset);
_listings = _store.GetAvailableListings(uid, null, storePreset.Categories).ToArray();
Array.Sort(_listings, (a, b) => (int) (b.Cost.Values.Sum() - a.Cost.Values.Sum())); //this might get weird with multicurrency but don't think about it
}
private void OnMapInit(EntityUid uid, SurplusBundleComponent component, MapInitEvent args)
{
FillStorage(uid, component);
}
private void FillStorage(EntityUid uid, SurplusBundleComponent? component = null)
{
if (!Resolve(uid, ref component))
if (!TryComp<StoreComponent>(uid, out var store))
return;
var cords = Transform(uid).Coordinates;
FillStorage((uid, component, store));
}
var content = GetRandomContent(component.TotalPrice);
private void FillStorage(Entity<SurplusBundleComponent, StoreComponent> ent)
{
var cords = Transform(ent).Coordinates;
var content = GetRandomContent(ent);
foreach (var item in content)
{
var ent = EntityManager.SpawnEntity(item.ProductEntity, cords);
_entityStorage.Insert(ent, uid);
var dode = Spawn(item.ProductEntity, cords);
_entityStorage.Insert(dode, ent);
}
}
// wow, is this leetcode reference?
private List<ListingData> GetRandomContent(FixedPoint2 targetCost)
private List<ListingData> GetRandomContent(Entity<SurplusBundleComponent, StoreComponent> ent)
{
var ret = new List<ListingData>();
if (_listings.Length == 0)
var listings = _store.GetAvailableListings(ent, null, ent.Comp2.Categories)
.OrderBy(p => p.Cost.Values.Sum())
.ToList();
if (listings.Count == 0)
return ret;
var totalCost = FixedPoint2.Zero;
var index = 0;
while (totalCost < targetCost)
while (totalCost < ent.Comp1.TotalPrice)
{
// All data is sorted in price descending order
// Find new item with the lowest acceptable price
// All expansive items will be before index, all acceptable after
var remainingBudget = targetCost - totalCost;
while (_listings[index].Cost.Values.Sum() > remainingBudget)
var remainingBudget = ent.Comp1.TotalPrice - totalCost;
while (listings[index].Cost.Values.Sum() > remainingBudget)
{
index++;
if (index >= _listings.Length)
if (index >= listings.Count)
{
// Looks like no cheap items left
// It shouldn't be case for ss14 content
@@ -82,8 +73,8 @@ public sealed class SurplusBundleSystem : EntitySystem
}
// Select random listing and add into crate
var randomIndex = _random.Next(index, _listings.Length);
var randomItem = _listings[randomIndex];
var randomIndex = _random.Next(index, listings.Count);
var randomItem = listings[randomIndex];
ret.Add(randomItem);
totalCost += randomItem.Cost.Values.Sum();
}

View File

@@ -0,0 +1,7 @@
namespace Content.Server.Traitor.Uplink;
/// <summary>
/// This is used for identifying something as a hidden uplink and showing the UI.
/// </summary>
[RegisterComponent]
public sealed partial class UplinkComponent : Component;

View File

@@ -5,6 +5,7 @@ using Content.Shared.PDA;
using Content.Server.Store.Components;
using Content.Shared.FixedPoint;
using Content.Shared.Store;
using Content.Shared.Store.Components;
namespace Content.Server.Traitor.Uplink
{
@@ -17,18 +18,6 @@ namespace Content.Server.Traitor.Uplink
[ValidatePrototypeId<CurrencyPrototype>]
public const string TelecrystalCurrencyPrototype = "Telecrystal";
/// <summary>
/// Gets the amount of TC on an "uplink"
/// Mostly just here for legacy systems based on uplink.
/// </summary>
/// <param name="component"></param>
/// <returns>the amount of TC</returns>
public int GetTCBalance(StoreComponent component)
{
FixedPoint2? tcBalance = component.Balance.GetValueOrDefault(TelecrystalCurrencyPrototype);
return tcBalance?.Int() ?? 0;
}
/// <summary>
/// Adds an uplink to the target
/// </summary>
@@ -37,7 +26,7 @@ namespace Content.Server.Traitor.Uplink
/// <param name="uplinkPresetId">The id of the storepreset</param>
/// <param name="uplinkEntity">The entity that will actually have the uplink functionality. Defaults to the PDA if null.</param>
/// <returns>Whether or not the uplink was added successfully</returns>
public bool AddUplink(EntityUid user, FixedPoint2? balance, string uplinkPresetId = "StorePresetUplink", EntityUid? uplinkEntity = null)
public bool AddUplink(EntityUid user, FixedPoint2? balance, EntityUid? uplinkEntity = null)
{
// Try to find target item
if (uplinkEntity == null)
@@ -47,11 +36,10 @@ namespace Content.Server.Traitor.Uplink
return false;
}
EnsureComp<UplinkComponent>(uplinkEntity.Value);
var store = EnsureComp<StoreComponent>(uplinkEntity.Value);
_store.InitializeFromPreset(uplinkPresetId, uplinkEntity.Value, store);
store.AccountOwner = user;
store.Balance.Clear();
if (balance != null)
{
store.Balance.Clear();

View File

@@ -1,46 +1,41 @@
using Content.Shared.FixedPoint;
using Content.Shared.Store;
using Robust.Shared.Audio;
using Robust.Shared.Map;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
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.Set;
namespace Content.Server.Store.Components;
namespace Content.Shared.Store.Components;
/// <summary>
/// This component manages a store which players can use to purchase different listings
/// through the ui. The currency, listings, and categories are defined in yaml.
/// </summary>
[RegisterComponent]
[RegisterComponent, NetworkedComponent]
public sealed partial class StoreComponent : Component
{
/// <summary>
/// The default preset for the store. Is overriden by default values specified on the component.
/// </summary>
[DataField("preset", customTypeSerializer: typeof(PrototypeIdSerializer<StorePresetPrototype>))]
public string? Preset;
[DataField]
public LocId Name = "store-ui-default-title";
/// <summary>
/// All the listing categories that are available on this store.
/// The available listings are partially based on the categories.
/// </summary>
[DataField("categories", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<StoreCategoryPrototype>))]
public HashSet<string> Categories = new();
[DataField]
public HashSet<ProtoId<StoreCategoryPrototype>> Categories = new();
/// <summary>
/// The total amount of currency that can be used in the store.
/// The string represents the ID of te currency prototype, where the
/// float is that amount.
/// </summary>
[ViewVariables(VVAccess.ReadWrite), DataField("balance", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<FixedPoint2, CurrencyPrototype>))]
public Dictionary<string, FixedPoint2> Balance = new();
[DataField]
public Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> Balance = new();
/// <summary>
/// The list of currencies that can be inserted into this store.
/// </summary>
[ViewVariables(VVAccess.ReadOnly), DataField("currencyWhitelist", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<CurrencyPrototype>))]
public HashSet<string> CurrencyWhitelist = new();
[DataField]
public HashSet<ProtoId<CurrencyPrototype>> CurrencyWhitelist = new();
/// <summary>
/// The person who "owns" the store/account. Used if you want the listings to be fixed
@@ -52,6 +47,7 @@ public sealed partial class StoreComponent : Component
/// <summary>
/// All listings, including those that aren't available to the buyer
/// </summary>
[DataField]
public HashSet<ListingData> Listings = new();
/// <summary>
@@ -70,7 +66,7 @@ public sealed partial class StoreComponent : Component
/// The total balance spent in this store. Used for refunds.
/// </summary>
[ViewVariables, DataField]
public Dictionary<string, FixedPoint2> BalanceSpent = new();
public Dictionary<ProtoId<CurrencyPrototype>, FixedPoint2> BalanceSpent = new();
/// <summary>
/// Controls if the store allows refunds
@@ -95,7 +91,7 @@ public sealed partial class StoreComponent : Component
/// <summary>
/// The sound played to the buyer when a purchase is succesfully made.
/// </summary>
[DataField("buySuccessSound")]
[DataField]
public SoundSpecifier BuySuccessSound = new SoundPathSpecifier("/Audio/Effects/kaching.ogg");
#endregion
}

View File

@@ -30,20 +30,6 @@ public sealed class StoreUpdateState : BoundUserInterfaceState
}
}
/// <summary>
/// initializes miscellaneous data about the store.
/// </summary>
[Serializable, NetSerializable]
public sealed class StoreInitializeState : BoundUserInterfaceState
{
public readonly string Name;
public StoreInitializeState(string name)
{
Name = name;
}
}
[Serializable, NetSerializable]
public sealed class StoreRequestUpdateInterfaceMessage : BoundUserInterfaceMessage
{

View File

@@ -8,3 +8,6 @@ store-ui-traitor-warning = Operatives must lock their uplinks after use to avoid
store-withdraw-button-ui = Withdraw {$currency}
store-ui-button-out-of-stock = {""} (Out of Stock)
store-not-account-owner = This {$store} is not bound to you!
store-preset-name-uplink = Uplink
store-preset-name-spellbook = Spellbook

View File

@@ -1,6 +1,6 @@
- type: entity
id: CrateSyndicateSurplusBundle
parent: CrateSyndicate
parent: [ CrateSyndicate, StorePresetUplink ]
name: Syndicate surplus crate
description: Contains 50 telecrystals worth of completely random Syndicate items. It can be useless junk or really good.
components:
@@ -24,7 +24,7 @@
- type: entity
id: CrateSyndicateSuperSurplusBundle
parent: CrateSyndicate
parent: [ CrateSyndicate, StorePresetUplink ]
name: Syndicate super surplus crate
description: Contains 125 telecrystals worth of completely random Syndicate items.
components:

View File

@@ -1,6 +1,6 @@
- type: entity
abstract: true
parent: BaseItem
parent: [ BaseItem, StorePresetUplink ] #PDA's have uplinks so they have to inherit the data.
id: BasePDA
name: PDA
description: Personal Data Assistant.

View File

@@ -25,7 +25,7 @@
id: WizardsGrimoire
name: wizards grimoire
suffix: Wizard
parent: BaseItem
parent: [ BaseItem, StorePresetSpellbook ]
components:
- type: Sprite
sprite: Objects/Misc/books.rsi
@@ -46,7 +46,6 @@
- type: Store
refundAllowed: true
ownerOnly: true # get your own tome!
preset: StorePresetSpellbook
balance:
WizCoin: 10 # prices are balanced around this 10 point maximum and how strong the spells are
@@ -55,12 +54,11 @@
id: WizardsGrimoireNoRefund
name: wizards grimoire
suffix: Wizard, No Refund
parent: WizardsGrimoire
parent: [ WizardsGrimoire, StorePresetSpellbook ]
components:
- type: Store
refundAllowed: false
ownerOnly: true # get your own tome!
preset: StorePresetSpellbook
balance:
WizCoin: 10 # prices are balanced around this 10 point maximum and how strong the spells are

View File

@@ -143,7 +143,7 @@
- Cuffable # useless if you cant be cuffed
- type: entity
parent: BaseSubdermalImplant
parent: [ BaseSubdermalImplant, StorePresetUplink ]
id: UplinkImplant
name: uplink implant
description: This implant lets the user access a hidden Syndicate uplink at will.
@@ -155,7 +155,6 @@
components:
- Hands # prevent mouse buying grenade penguin since its not telepathic
- type: Store
preset: StorePresetUplink
balance:
Telecrystal: 0
- type: UserInterface

View File

@@ -48,7 +48,7 @@
# Uplinks
- type: entity
parent: BaseItem
parent: [ BaseItem, StorePresetUplink ]
id: BaseUplinkRadio
name: syndicate uplink
description: Suspiciously looking old radio...
@@ -68,7 +68,6 @@
- type: ActivatableUI
key: enum.StoreUiKey.Key
- type: Store
preset: StorePresetUplink
balance:
Telecrystal: 0
@@ -78,7 +77,6 @@
suffix: 20 TC
components:
- type: Store
preset: StorePresetUplink
balance:
Telecrystal: 20
@@ -88,7 +86,6 @@
suffix: 25 TC
components:
- type: Store
preset: StorePresetUplink
balance:
Telecrystal: 25
@@ -99,7 +96,6 @@
suffix: 40 TC, NukeOps
components:
- type: Store
preset: StorePresetUplink
balance:
Telecrystal: 40
- type: Tag
@@ -112,7 +108,6 @@
suffix: 60 TC, LoneOps
components:
- type: Store
preset: StorePresetUplink
balance:
Telecrystal: 60
- type: Tag
@@ -125,6 +120,5 @@
suffix: DEBUG
components:
- type: Store
preset: StorePresetUplink
balance:
Telecrystal: 99999

View File

@@ -1,6 +1,9 @@
- type: storePreset
- type: entity
id: StorePresetUplink
storeName: Uplink
abstract: true
components:
- type: Store
name: store-preset-name-uplink
categories:
- UplinkWeaponry
- UplinkAmmo
@@ -15,10 +18,15 @@
- UplinkPointless
currencyWhitelist:
- Telecrystal
balance:
Telecrystal: 0
- type: storePreset
- type: entity
id: StorePresetSpellbook
storeName: Spellbook
abstract: true
components:
- type: Store
name: store-preset-name-spellbook
categories:
- SpellbookOffensive #Fireball, Rod Form
- SpellbookDefensive #Magic Missile, Wall of Force