using System.Linq; using Content.Server.Store.Systems; using Content.Server.StoreDiscount.Systems; using Content.Shared.FixedPoint; using Content.Shared.Hands.EntitySystems; using Content.Shared.Implants; using Content.Shared.Inventory; using Content.Shared.Mind; using Content.Shared.PDA; using Content.Shared.Store; using Content.Shared.Store.Components; using Robust.Shared.Prototypes; namespace Content.Server.Traitor.Uplink; public sealed class UplinkSystem : EntitySystem { [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly StoreSystem _store = default!; [Dependency] private readonly SharedSubdermalImplantSystem _subdermalImplant = default!; [Dependency] private readonly SharedMindSystem _mind = default!; [ValidatePrototypeId] public const string TelecrystalCurrencyPrototype = "Telecrystal"; private const string FallbackUplinkImplant = "UplinkImplant"; private const string FallbackUplinkCatalog = "UplinkUplinkImplanter"; /// /// Adds an uplink to the target /// /// The person who is getting the uplink /// The amount of currency on the uplink. If null, will just use the amount specified in the preset. /// The entity that will actually have the uplink functionality. Defaults to the PDA if null. /// Marker that enables discounts for uplink items. /// Whether or not the uplink was added successfully public bool AddUplink( EntityUid user, FixedPoint2 balance, EntityUid? uplinkEntity = null, bool giveDiscounts = false) { // Try to find target item if none passed uplinkEntity ??= FindUplinkTarget(user); if (uplinkEntity == null) return ImplantUplink(user, balance, giveDiscounts); EnsureComp(uplinkEntity.Value); SetUplink(user, uplinkEntity.Value, balance, giveDiscounts); // TODO add BUI. Currently can't be done outside of yaml -_- // ^ What does this even mean? return true; } /// /// Configure TC for the uplink /// private void SetUplink(EntityUid user, EntityUid uplink, FixedPoint2 balance, bool giveDiscounts) { if (!_mind.TryGetMind(user, out var mind, out _)) return; var store = EnsureComp(uplink); store.AccountOwner = mind; store.Balance.Clear(); _store.TryAddCurrency(new Dictionary { { TelecrystalCurrencyPrototype, balance } }, uplink, store); var uplinkInitializedEvent = new StoreInitializedEvent( TargetUser: mind, Store: uplink, UseDiscounts: giveDiscounts, Listings: _store.GetAvailableListings(mind, uplink, store) .ToArray()); RaiseLocalEvent(ref uplinkInitializedEvent); } /// /// Implant an uplink as a fallback measure if the traitor had no PDA /// private bool ImplantUplink(EntityUid user, FixedPoint2 balance, bool giveDiscounts) { var implantProto = new string(FallbackUplinkImplant); if (!_proto.TryIndex(FallbackUplinkCatalog, out var catalog)) return false; if (!catalog.Cost.TryGetValue(TelecrystalCurrencyPrototype, out var cost)) return false; if (balance < cost) // Can't use Math functions on FixedPoint2 balance = 0; else balance = balance - cost; var implant = _subdermalImplant.AddImplant(user, implantProto); if (!HasComp(implant)) return false; SetUplink(user, implant.Value, balance, giveDiscounts); return true; } /// /// Finds the entity that can hold an uplink for a user. /// Usually this is a pda in their pda slot, but can also be in their hands. (but not pockets or inside bag, etc.) /// public EntityUid? FindUplinkTarget(EntityUid user) { // Try to find PDA in inventory if (_inventorySystem.TryGetContainerSlotEnumerator(user, out var containerSlotEnumerator)) { while (containerSlotEnumerator.MoveNext(out var pdaUid)) { if (!pdaUid.ContainedEntity.HasValue) continue; if (HasComp(pdaUid.ContainedEntity.Value) || HasComp(pdaUid.ContainedEntity.Value)) return pdaUid.ContainedEntity.Value; } } // Also check hands foreach (var item in _handsSystem.EnumerateHeld(user)) { if (HasComp(item) || HasComp(item)) return item; } return null; } }