Moving PDA to ECS (#4538)
* Moved pen slot to separate component * Moved it all to more generic item slot class * Add sounds * Item slots now supports many slots * Some clean-up * Refactored slots a bit * Moving ID card out * Moving pda to system * Moving PDA owner to ECS * Moved PDA flashlight to separate component * Toggle lights work through events * Fixing UI * Moving uplink to separate component * Continue moving uplink to separate component * More cleaning * Removing pda shared * Nuked shared pda component * Fixed flashlight * Pen slot now showed in UI * Light toggle now shows correctly in UI * Small refactoring of item slots * Added contained entity * Fixed tests * Finished with PDA * Moving PDA uplink to separate window * Adding-removing uplink should show new button * Working on a better debug * Debug command to add uplink * Uplink send state to UI * Almost working UI * Uplink correcty updates when you buy-sell items * Ups * Moved localization to separate file * Minor fixes * Removed item slots methods events * Removed PDA owner name * Removed one uplink event * Deleted all uplink events * Removed flashlight events * Update Content.Shared/Traitor/Uplink/UplinkVisuals.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Content.Server/Containers/ItemSlot/ItemSlotsSystem.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Content.Server/Containers/ItemSlot/ItemSlotsSystem.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Content.Server/GameTicking/Presets/PresetTraitorDeathMatch.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Item slots system review * Flashlight review * PDA to XAML * Move UplinkMenu to seperate class, fix WeightedColors methods * Move UI to XAML * Moved events to entity id * Address review * Removed uplink extensions * Minor fix * Moved item slots to shared * My bad Robust... * Fixed pda sound * Fixed pda tests * Fixed pda test again Co-authored-by: Alexander Evgrashin <evgrashin.adl@gmail.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Visne <vincefvanwijk@gmail.com>
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.PDA;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.PDA.Managers
|
||||
{
|
||||
public interface IPDAUplinkManager
|
||||
{
|
||||
public IReadOnlyDictionary<string, UplinkListingData> FetchListings { get; }
|
||||
|
||||
void Initialize();
|
||||
|
||||
public bool AddNewAccount(UplinkAccount acc);
|
||||
|
||||
public bool ChangeBalance(UplinkAccount acc, int amt);
|
||||
|
||||
public bool TryPurchaseItem(
|
||||
UplinkAccount? acc,
|
||||
string itemId,
|
||||
EntityCoordinates spawnCoords,
|
||||
[NotNullWhen(true)] out IEntity? purchasedItem);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Shared.PDA;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.PDA.Managers
|
||||
{
|
||||
public class PDAUplinkManager : IPDAUplinkManager
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
|
||||
private readonly List<UplinkAccount> _accounts = new();
|
||||
private readonly Dictionary<string, UplinkListingData> _listings = new();
|
||||
|
||||
public IReadOnlyDictionary<string, UplinkListingData> FetchListings => _listings;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
foreach (var item in _prototypeManager.EnumeratePrototypes<UplinkStoreListingPrototype>())
|
||||
{
|
||||
var newListing = new UplinkListingData(item.ListingName, item.ItemId, item.Price, item.Category,
|
||||
item.Description);
|
||||
|
||||
RegisterUplinkListing(newListing);
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterUplinkListing(UplinkListingData listing)
|
||||
{
|
||||
if (!ContainsListing(listing))
|
||||
{
|
||||
_listings.Add(listing.ItemId, listing);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ContainsListing(UplinkListingData listing)
|
||||
{
|
||||
return _listings.ContainsKey(listing.ItemId);
|
||||
}
|
||||
|
||||
public bool AddNewAccount(UplinkAccount acc)
|
||||
{
|
||||
var entity = _entityManager.GetEntity(acc.AccountHolder);
|
||||
|
||||
if (entity.TryGetComponent(out MindComponent? mindComponent) && !mindComponent.HasMind)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_accounts.Contains(acc))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_accounts.Add(acc);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ChangeBalance(UplinkAccount acc, int amt)
|
||||
{
|
||||
var account = _accounts.Find(uplinkAccount => uplinkAccount.AccountHolder == acc.AccountHolder);
|
||||
|
||||
if (account == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (account.Balance + amt < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
account.ModifyAccountBalance(account.Balance + amt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryPurchaseItem(UplinkAccount? acc, string itemId, EntityCoordinates spawnCoords, [NotNullWhen(true)] out IEntity? purchasedItem)
|
||||
{
|
||||
purchasedItem = null;
|
||||
if (acc == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_listings.TryGetValue(itemId, out var listing))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (acc.Balance < listing.Price)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ChangeBalance(acc, -listing.Price))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
purchasedItem = _entityManager.SpawnEntity(listing.ItemId, spawnCoords);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,443 +1,44 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Access.Components;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Items;
|
||||
using Content.Server.PDA.Managers;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Sound;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.PDA
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IAccess))]
|
||||
public class PDAComponent : SharedPDAComponent, IInteractUsing, IActivate, IUse, IAccess, IMapInit
|
||||
public class PDAComponent : Component, IAccess
|
||||
{
|
||||
[Dependency] private readonly IPDAUplinkManager _uplinkManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
public override string Name => "PDA";
|
||||
|
||||
[ViewVariables] private ContainerSlot _idSlot = default!;
|
||||
[ViewVariables] private ContainerSlot _penSlot = default!;
|
||||
public const string IDSlotName = "pda_id_slot";
|
||||
public const string PenSlotName = "pda_pen_slot";
|
||||
|
||||
[ViewVariables] private bool _lightOn;
|
||||
[ViewVariables] [DataField("idCard")] public string? StartingIdCard;
|
||||
|
||||
[ViewVariables] [DataField("idCard")] private string? _startingIdCard = "AssistantIDCard";
|
||||
[ViewVariables] [DataField("pen")] private string? _startingPen = "Pen";
|
||||
[ViewVariables] public IdCardComponent? ContainedID;
|
||||
[ViewVariables] public bool PenInserted;
|
||||
[ViewVariables] public bool FlashlightOn;
|
||||
|
||||
[ViewVariables] public string? OwnerName { get; private set; }
|
||||
[ViewVariables] public string? OwnerName;
|
||||
|
||||
[ViewVariables] public IdCardComponent? ContainedID { get; private set; }
|
||||
[ViewVariables] public bool IdSlotEmpty => _idSlot.ContainedEntity == null;
|
||||
[ViewVariables] public bool PenSlotEmpty => _penSlot.ContainedEntity == null;
|
||||
|
||||
private UplinkAccount? _syndicateUplinkAccount;
|
||||
|
||||
[ViewVariables] public UplinkAccount? SyndicateUplinkAccount => _syndicateUplinkAccount;
|
||||
|
||||
[ViewVariables] private readonly PdaAccessSet _accessSet;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(PDAUiKey.Key);
|
||||
|
||||
[DataField("insertIdSound")] private SoundSpecifier _insertIdSound = new SoundPathSpecifier("/Audio/Weapons/Guns/MagIn/batrifle_magin.ogg");
|
||||
[DataField("toggleFlashlightSound")] private SoundSpecifier _toggleFlashlightSound = new SoundPathSpecifier("/Audio/Items/flashlight_pda.ogg");
|
||||
[DataField("ejectIdSound")] private SoundSpecifier _ejectIdSound = new SoundPathSpecifier("/Audio/Machines/id_swipe.ogg");
|
||||
// TODO: Move me to ECS after Access refactoring
|
||||
#region Acces Logic
|
||||
[ViewVariables] private readonly PDAAccessSet _accessSet;
|
||||
|
||||
public PDAComponent()
|
||||
{
|
||||
_accessSet = new PdaAccessSet(this);
|
||||
_accessSet = new PDAAccessSet(this);
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_idSlot = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "pda_entity_container");
|
||||
_penSlot = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "pda_pen_slot");
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
|
||||
}
|
||||
|
||||
UpdatePDAAppearance();
|
||||
}
|
||||
|
||||
public void MapInit()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_startingIdCard))
|
||||
{
|
||||
var idCard = _entityManager.SpawnEntity(_startingIdCard, Owner.Transform.Coordinates);
|
||||
var idCardComponent = idCard.GetComponent<IdCardComponent>();
|
||||
_idSlot.Insert(idCardComponent.Owner);
|
||||
ContainedID = idCardComponent;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_startingPen))
|
||||
{
|
||||
var pen = _entityManager.SpawnEntity(_startingPen, Owner.Transform.Coordinates);
|
||||
_penSlot.Insert(pen);
|
||||
}
|
||||
}
|
||||
|
||||
private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
{
|
||||
switch (message.Message)
|
||||
{
|
||||
case PDARequestUpdateInterfaceMessage _:
|
||||
{
|
||||
UpdatePDAUserInterface();
|
||||
break;
|
||||
}
|
||||
case PDAToggleFlashlightMessage _:
|
||||
{
|
||||
ToggleLight();
|
||||
break;
|
||||
}
|
||||
|
||||
case PDAEjectIDMessage _:
|
||||
{
|
||||
HandleIDEjection(message.Session.AttachedEntity!);
|
||||
break;
|
||||
}
|
||||
|
||||
case PDAEjectPenMessage _:
|
||||
{
|
||||
HandlePenEjection(message.Session.AttachedEntity!);
|
||||
break;
|
||||
}
|
||||
|
||||
case PDAUplinkBuyListingMessage buyMsg:
|
||||
{
|
||||
var player = message.Session.AttachedEntity;
|
||||
if (player == null) break;
|
||||
|
||||
if (!_uplinkManager.TryPurchaseItem(_syndicateUplinkAccount, buyMsg.ItemId,
|
||||
player.Transform.Coordinates, out var entity))
|
||||
{
|
||||
SendNetworkMessage(new PDAUplinkInsufficientFundsMessage(), message.Session.ConnectedClient);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!player.TryGetComponent(out HandsComponent? hands) ||
|
||||
!entity.TryGetComponent(out ItemComponent? item))
|
||||
break;
|
||||
|
||||
hands.PutInHandOrDrop(item);
|
||||
|
||||
SendNetworkMessage(new PDAUplinkBuySuccessMessage(), message.Session.ConnectedClient);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePDAUserInterface()
|
||||
{
|
||||
var ownerInfo = new PDAIdInfoText
|
||||
{
|
||||
ActualOwnerName = OwnerName,
|
||||
IdOwner = ContainedID?.FullName,
|
||||
JobTitle = ContainedID?.JobTitle
|
||||
};
|
||||
|
||||
//Do we have an account? If so provide the info.
|
||||
if (_syndicateUplinkAccount != null)
|
||||
{
|
||||
var accData = new UplinkAccountData(_syndicateUplinkAccount.AccountHolder,
|
||||
_syndicateUplinkAccount.Balance);
|
||||
var listings = _uplinkManager.FetchListings.Values.ToArray();
|
||||
UserInterface?.SetState(new PDAUpdateState(_lightOn, !PenSlotEmpty, ownerInfo, accData, listings));
|
||||
}
|
||||
else
|
||||
{
|
||||
UserInterface?.SetState(new PDAUpdateState(_lightOn, !PenSlotEmpty, ownerInfo));
|
||||
}
|
||||
|
||||
UpdatePDAAppearance();
|
||||
}
|
||||
|
||||
private void UpdatePDAAppearance()
|
||||
{
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(PDAVisuals.FlashlightLit, _lightOn);
|
||||
appearance.SetData(PDAVisuals.IDCardInserted, !IdSlotEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryInsertIdCard(InteractUsingEventArgs eventArgs, IdCardComponent idCardComponent)
|
||||
{
|
||||
var item = eventArgs.Using;
|
||||
if (_idSlot.Contains(item))
|
||||
return false;
|
||||
|
||||
if (!eventArgs.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("comp-pda-ui-try-insert-id-card-no-hands"));
|
||||
return true;
|
||||
}
|
||||
|
||||
IEntity? swap = null;
|
||||
if (!IdSlotEmpty)
|
||||
{
|
||||
// Swap
|
||||
swap = _idSlot.ContainedEntities[0];
|
||||
}
|
||||
|
||||
if (!hands.Drop(item))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (swap != null)
|
||||
{
|
||||
hands.PutInHand(swap.GetComponent<ItemComponent>());
|
||||
}
|
||||
|
||||
InsertIdCard(idCardComponent);
|
||||
|
||||
UpdatePDAUserInterface();
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryInsertPen(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
var item = eventArgs.Using;
|
||||
if (_penSlot.Contains(item))
|
||||
return false;
|
||||
|
||||
if (!eventArgs.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("comp-pda-ui-try-insert-pen-no-hands"));
|
||||
return true;
|
||||
}
|
||||
|
||||
IEntity? swap = null;
|
||||
if (!PenSlotEmpty)
|
||||
{
|
||||
// Swap
|
||||
swap = _penSlot.ContainedEntities[0];
|
||||
}
|
||||
|
||||
if (!hands.Drop(item))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (swap != null)
|
||||
{
|
||||
hands.PutInHand(swap.GetComponent<ItemComponent>());
|
||||
}
|
||||
|
||||
// Insert Pen
|
||||
_penSlot.Insert(item);
|
||||
|
||||
UpdatePDAUserInterface();
|
||||
return true;
|
||||
}
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
var item = eventArgs.Using;
|
||||
|
||||
if (item.TryGetComponent<IdCardComponent>(out var idCardComponent))
|
||||
{
|
||||
return TryInsertIdCard(eventArgs, idCardComponent);
|
||||
}
|
||||
|
||||
if (item.HasTag("Write"))
|
||||
{
|
||||
return TryInsertPen(eventArgs);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out ActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UserInterface?.Toggle(actor.PlayerSession);
|
||||
UpdatePDAAppearance();
|
||||
}
|
||||
|
||||
bool IUse.UseEntity(UseEntityEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out ActorComponent? actor))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
UserInterface?.Toggle(actor.PlayerSession);
|
||||
UpdatePDAAppearance();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SetPDAOwner(string name)
|
||||
{
|
||||
OwnerName = name;
|
||||
UpdatePDAUserInterface();
|
||||
}
|
||||
|
||||
public void InsertIdCard(IdCardComponent card)
|
||||
{
|
||||
_idSlot.Insert(card.Owner);
|
||||
ContainedID = card;
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _insertIdSound.GetSound(), Owner);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize the PDA's syndicate uplink account.
|
||||
/// </summary>
|
||||
/// <param name="acc"></param>
|
||||
public void InitUplinkAccount(UplinkAccount acc)
|
||||
{
|
||||
_syndicateUplinkAccount = acc;
|
||||
_uplinkManager.AddNewAccount(_syndicateUplinkAccount);
|
||||
|
||||
_syndicateUplinkAccount.BalanceChanged += account =>
|
||||
{
|
||||
UpdatePDAUserInterface();
|
||||
};
|
||||
|
||||
UpdatePDAUserInterface();
|
||||
}
|
||||
|
||||
private void ToggleLight()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out PointLightComponent? light))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lightOn = !_lightOn;
|
||||
light.Enabled = _lightOn;
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _toggleFlashlightSound.GetSound(), Owner);
|
||||
UpdatePDAUserInterface();
|
||||
}
|
||||
|
||||
private void HandleIDEjection(IEntity pdaUser)
|
||||
{
|
||||
if (ContainedID == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var cardEntity = ContainedID.Owner;
|
||||
_idSlot.Remove(cardEntity);
|
||||
|
||||
var hands = pdaUser.GetComponent<HandsComponent>();
|
||||
var cardItemComponent = cardEntity.GetComponent<ItemComponent>();
|
||||
hands.PutInHandOrDrop(cardItemComponent);
|
||||
ContainedID = null;
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _ejectIdSound.GetSound(), Owner);
|
||||
UpdatePDAUserInterface();
|
||||
}
|
||||
|
||||
private void HandlePenEjection(IEntity pdaUser)
|
||||
{
|
||||
if (PenSlotEmpty)
|
||||
return;
|
||||
|
||||
var pen = _penSlot.ContainedEntities[0];
|
||||
_penSlot.Remove(pen);
|
||||
|
||||
var hands = pdaUser.GetComponent<HandsComponent>();
|
||||
var itemComponent = pen.GetComponent<ItemComponent>();
|
||||
hands.PutInHandOrDrop(itemComponent);
|
||||
|
||||
UpdatePDAUserInterface();
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectIDVerb : Verb<PDAComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("eject-id-verb-get-data-text");
|
||||
data.Visibility = component.IdSlotEmpty ? VerbVisibility.Invisible : VerbVisibility.Visible;
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, PDAComponent component)
|
||||
{
|
||||
component.HandleIDEjection(user);
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectPenVerb : Verb<PDAComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("eject-pen-verb-get-data-text");
|
||||
data.Visibility = component.PenSlotEmpty ? VerbVisibility.Invisible : VerbVisibility.Visible;
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, PDAComponent component)
|
||||
{
|
||||
component.HandlePenEjection(user);
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class ToggleFlashlightVerb : Verb<PDAComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
|
||||
{
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("toggle-flashlight-verb-get-data-text");
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/light.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, PDAComponent component)
|
||||
{
|
||||
component.ToggleLight();
|
||||
}
|
||||
}
|
||||
|
||||
private ISet<string>? GetContainedAccess()
|
||||
public ISet<string>? GetContainedAccess()
|
||||
{
|
||||
return ContainedID?.Owner?.GetComponent<AccessComponent>()?.Tags;
|
||||
}
|
||||
@@ -450,117 +51,77 @@ namespace Content.Server.PDA
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
#endregion
|
||||
|
||||
private sealed class PdaAccessSet : ISet<string>
|
||||
// TODO: replace me with dynamic verbs for ItemSlotsSystem
|
||||
#region Verbs
|
||||
[Verb]
|
||||
public sealed class EjectPenVerb : Verb<PDAComponent>
|
||||
{
|
||||
private readonly PDAComponent _pdaComponent;
|
||||
private static readonly HashSet<string> EmptySet = new();
|
||||
|
||||
public PdaAccessSet(PDAComponent pdaComponent)
|
||||
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
|
||||
{
|
||||
_pdaComponent = pdaComponent;
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
if (!component.Owner.TryGetComponent(out SharedItemSlotsComponent? slots))
|
||||
return;
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
return;
|
||||
|
||||
var item = EntitySystem.Get<SharedItemSlotsSystem>().PeekItemInSlot(slots, PenSlotName);
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = Loc.GetString("eject-item-verb-text-default", ("item", item.Name));
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
}
|
||||
|
||||
public IEnumerator<string> GetEnumerator()
|
||||
protected override void Activate(IEntity user, PDAComponent pda)
|
||||
{
|
||||
var contained = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return contained.GetEnumerator();
|
||||
var entityManager = pda.Owner.EntityManager;
|
||||
if (pda.Owner.TryGetComponent(out SharedItemSlotsComponent? itemSlots))
|
||||
{
|
||||
entityManager.EntitySysManager.GetEntitySystem<SharedItemSlotsSystem>().
|
||||
TryEjectContent(itemSlots, PenSlotName, user);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection<string>.Add(string item)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public void ExceptWith(IEnumerable<string> other)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public void IntersectWith(IEnumerable<string> other)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public bool IsProperSubsetOf(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.IsProperSubsetOf(other);
|
||||
}
|
||||
|
||||
public bool IsProperSupersetOf(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.IsProperSupersetOf(other);
|
||||
}
|
||||
|
||||
public bool IsSubsetOf(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.IsSubsetOf(other);
|
||||
}
|
||||
|
||||
public bool IsSupersetOf(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.IsSupersetOf(other);
|
||||
}
|
||||
|
||||
public bool Overlaps(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.Overlaps(other);
|
||||
}
|
||||
|
||||
public bool SetEquals(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.SetEquals(other);
|
||||
}
|
||||
|
||||
public void SymmetricExceptWith(IEnumerable<string> other)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public void UnionWith(IEnumerable<string> other)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
bool ISet<string>.Add(string item)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public bool Contains(string item)
|
||||
{
|
||||
return _pdaComponent.GetContainedAccess()?.Contains(item) ?? false;
|
||||
}
|
||||
|
||||
public void CopyTo(string[] array, int arrayIndex)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
set.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(string item)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public int Count => _pdaComponent.GetContainedAccess()?.Count ?? 0;
|
||||
public bool IsReadOnly => true;
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectIDVerb : Verb<PDAComponent>
|
||||
{
|
||||
public override bool AlternativeInteraction => true;
|
||||
|
||||
protected override void GetData(IEntity user, PDAComponent component, VerbData data)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
if (!component.Owner.TryGetComponent(out SharedItemSlotsComponent? slots))
|
||||
return;
|
||||
|
||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
||||
return;
|
||||
|
||||
var item = EntitySystem.Get<SharedItemSlotsSystem>().PeekItemInSlot(slots, IDSlotName);
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
data.Text = Loc.GetString("eject-item-verb-text-default", ("item", item.Name));
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, PDAComponent pda)
|
||||
{
|
||||
var entityManager = pda.Owner.EntityManager;
|
||||
if (pda.Owner.TryGetComponent(out SharedItemSlotsComponent? itemSlots))
|
||||
{
|
||||
entityManager.EntitySysManager.GetEntitySystem<SharedItemSlotsSystem>().
|
||||
TryEjectContent(itemSlots, IDSlotName, user);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
183
Content.Server/PDA/PDASystem.cs
Normal file
183
Content.Server/PDA/PDASystem.cs
Normal file
@@ -0,0 +1,183 @@
|
||||
using Content.Server.Access.Components;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Server.Light.Components;
|
||||
using Content.Server.Light.EntitySystems;
|
||||
using Content.Server.Light.Events;
|
||||
using Content.Server.Traitor.Uplink;
|
||||
using Content.Server.Traitor.Uplink.Components;
|
||||
using Content.Server.Traitor.Uplink.Systems;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.PDA;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.PDA
|
||||
{
|
||||
public class PDASystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedItemSlotsSystem _slotsSystem = default!;
|
||||
[Dependency] private readonly UplinkSystem _uplinkSystem = default!;
|
||||
[Dependency] private readonly UnpoweredFlashlightSystem _unpoweredFlashlight = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PDAComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<PDAComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<PDAComponent, ActivateInWorldEvent>(OnActivateInWorld);
|
||||
SubscribeLocalEvent<PDAComponent, UseInHandEvent>(OnUse);
|
||||
SubscribeLocalEvent<PDAComponent, ItemSlotChanged>(OnItemSlotChanged);
|
||||
SubscribeLocalEvent<PDAComponent, LightToggleEvent>(OnLightToggle);
|
||||
|
||||
SubscribeLocalEvent<PDAComponent, UplinkInitEvent>(OnUplinkInit);
|
||||
SubscribeLocalEvent<PDAComponent, UplinkRemovedEvent>(OnUplinkRemoved);
|
||||
}
|
||||
|
||||
private void OnComponentInit(EntityUid uid, PDAComponent pda, ComponentInit args)
|
||||
{
|
||||
var ui = pda.Owner.GetUIOrNull(PDAUiKey.Key);
|
||||
if (ui != null)
|
||||
ui.OnReceiveMessage += (msg) => OnUIMessage(pda, msg);
|
||||
|
||||
UpdatePDAAppearance(pda);
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, PDAComponent pda, MapInitEvent args)
|
||||
{
|
||||
// try to place ID inside item slot
|
||||
if (!string.IsNullOrEmpty(pda.StartingIdCard))
|
||||
{
|
||||
// if pda prototype doesn't have slots, ID will drop down on ground
|
||||
var idCard = EntityManager.SpawnEntity(pda.StartingIdCard, pda.Owner.Transform.Coordinates);
|
||||
if (EntityManager.TryGetComponent(uid, out SharedItemSlotsComponent? itemSlots))
|
||||
_slotsSystem.TryInsertContent(itemSlots, idCard, PDAComponent.IDSlotName);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnUse(EntityUid uid, PDAComponent pda, UseInHandEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
args.Handled = OpenUI(pda, args.User);
|
||||
}
|
||||
|
||||
private void OnActivateInWorld(EntityUid uid, PDAComponent pda, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
args.Handled = OpenUI(pda, args.User);
|
||||
}
|
||||
|
||||
private void OnItemSlotChanged(EntityUid uid, PDAComponent pda, ItemSlotChanged args)
|
||||
{
|
||||
// check if ID slot changed
|
||||
if (args.SlotName == PDAComponent.IDSlotName)
|
||||
{
|
||||
var item = args.ContainedItem;
|
||||
if (item == null || !EntityManager.TryGetComponent(item.Value, out IdCardComponent ? idCard))
|
||||
pda.ContainedID = null;
|
||||
else
|
||||
pda.ContainedID = idCard;
|
||||
}
|
||||
else if (args.SlotName == PDAComponent.PenSlotName)
|
||||
{
|
||||
var item = args.ContainedItem;
|
||||
pda.PenInserted = item != null;
|
||||
}
|
||||
|
||||
UpdatePDAAppearance(pda);
|
||||
UpdatePDAUserInterface(pda);
|
||||
}
|
||||
|
||||
private void OnLightToggle(EntityUid uid, PDAComponent pda, LightToggleEvent args)
|
||||
{
|
||||
pda.FlashlightOn = args.IsOn;
|
||||
UpdatePDAUserInterface(pda);
|
||||
}
|
||||
|
||||
public void SetOwner(PDAComponent pda, string ownerName)
|
||||
{
|
||||
pda.OwnerName = ownerName;
|
||||
UpdatePDAUserInterface(pda);
|
||||
}
|
||||
|
||||
private void OnUplinkInit(EntityUid uid, PDAComponent pda, UplinkInitEvent args)
|
||||
{
|
||||
UpdatePDAUserInterface(pda);
|
||||
}
|
||||
|
||||
private void OnUplinkRemoved(EntityUid uid, PDAComponent pda, UplinkRemovedEvent args)
|
||||
{
|
||||
UpdatePDAUserInterface(pda);
|
||||
}
|
||||
|
||||
private bool OpenUI(PDAComponent pda, IEntity user)
|
||||
{
|
||||
if (!user.TryGetComponent(out ActorComponent? actor))
|
||||
return false;
|
||||
|
||||
var ui = pda.Owner.GetUIOrNull(PDAUiKey.Key);
|
||||
ui?.Toggle(actor.PlayerSession);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void UpdatePDAAppearance(PDAComponent pda)
|
||||
{
|
||||
if (pda.Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
appearance.SetData(PDAVisuals.IDCardInserted, pda.ContainedID != null);
|
||||
}
|
||||
|
||||
private void UpdatePDAUserInterface(PDAComponent pda)
|
||||
{
|
||||
var ownerInfo = new PDAIdInfoText
|
||||
{
|
||||
ActualOwnerName = pda.OwnerName,
|
||||
IdOwner = pda.ContainedID?.FullName,
|
||||
JobTitle = pda.ContainedID?.JobTitle
|
||||
};
|
||||
|
||||
var hasUplink = pda.Owner.HasComponent<UplinkComponent>();
|
||||
|
||||
var ui = pda.Owner.GetUIOrNull(PDAUiKey.Key);
|
||||
ui?.SetState(new PDAUpdateState(pda.FlashlightOn, pda.PenInserted, ownerInfo, hasUplink));
|
||||
}
|
||||
|
||||
private void OnUIMessage(PDAComponent pda, ServerBoundUserInterfaceMessage msg)
|
||||
{
|
||||
switch (msg.Message)
|
||||
{
|
||||
case PDARequestUpdateInterfaceMessage _:
|
||||
UpdatePDAUserInterface(pda);
|
||||
break;
|
||||
case PDAToggleFlashlightMessage _:
|
||||
{
|
||||
if (pda.Owner.TryGetComponent(out UnpoweredFlashlightComponent? flashlight))
|
||||
_unpoweredFlashlight.ToggleLight(flashlight);
|
||||
break;
|
||||
}
|
||||
|
||||
case PDAEjectIDMessage _:
|
||||
{
|
||||
if (pda.Owner.TryGetComponent(out SharedItemSlotsComponent? itemSlots))
|
||||
_slotsSystem.TryEjectContent(itemSlots, PDAComponent.IDSlotName, msg.Session.AttachedEntity);
|
||||
break;
|
||||
}
|
||||
case PDAEjectPenMessage _:
|
||||
{
|
||||
if (pda.Owner.TryGetComponent(out SharedItemSlotsComponent? itemSlots))
|
||||
_slotsSystem.TryEjectContent(itemSlots, PDAComponent.PenSlotName, msg.Session.AttachedEntity);
|
||||
break;
|
||||
}
|
||||
case PDAShowUplinkMessage _:
|
||||
{
|
||||
if (pda.Owner.TryGetComponent(out UplinkComponent? uplink))
|
||||
_uplinkSystem.ToggleUplinkUI(uplink, msg.Session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
118
Content.Server/PDA/PdaAccessSet.cs
Normal file
118
Content.Server/PDA/PdaAccessSet.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Server.PDA
|
||||
{
|
||||
public sealed class PDAAccessSet : ISet<string>
|
||||
{
|
||||
private readonly PDAComponent _pdaComponent;
|
||||
private static readonly HashSet<string> EmptySet = new();
|
||||
|
||||
public PDAAccessSet(PDAComponent pdaComponent)
|
||||
{
|
||||
_pdaComponent = pdaComponent;
|
||||
}
|
||||
|
||||
public IEnumerator<string> GetEnumerator()
|
||||
{
|
||||
var contained = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return contained.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
void ICollection<string>.Add(string item)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public void ExceptWith(IEnumerable<string> other)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public void IntersectWith(IEnumerable<string> other)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public bool IsProperSubsetOf(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.IsProperSubsetOf(other);
|
||||
}
|
||||
|
||||
public bool IsProperSupersetOf(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.IsProperSupersetOf(other);
|
||||
}
|
||||
|
||||
public bool IsSubsetOf(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.IsSubsetOf(other);
|
||||
}
|
||||
|
||||
public bool IsSupersetOf(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.IsSupersetOf(other);
|
||||
}
|
||||
|
||||
public bool Overlaps(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.Overlaps(other);
|
||||
}
|
||||
|
||||
public bool SetEquals(IEnumerable<string> other)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
return set.SetEquals(other);
|
||||
}
|
||||
|
||||
public void SymmetricExceptWith(IEnumerable<string> other)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public void UnionWith(IEnumerable<string> other)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
bool ISet<string>.Add(string item)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public bool Contains(string item)
|
||||
{
|
||||
return _pdaComponent.GetContainedAccess()?.Contains(item) ?? false;
|
||||
}
|
||||
|
||||
public void CopyTo(string[] array, int arrayIndex)
|
||||
{
|
||||
var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
|
||||
set.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool Remove(string item)
|
||||
{
|
||||
throw new NotSupportedException("PDA access list is read-only.");
|
||||
}
|
||||
|
||||
public int Count => _pdaComponent.GetContainedAccess()?.Count ?? 0;
|
||||
public bool IsReadOnly => true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user