Delta-v/Content.Shared/_DV/Waypointer/SharedWaypointerSystem.cs

170 lines
7.1 KiB
C#

using System.Linq;
using Content.Shared._DV.Waypointer.Components;
using Content.Shared._DV.Waypointer.Events;
using Content.Shared.Actions;
using Content.Shared.Actions.Components;
using Content.Shared.Clothing;
using Content.Shared.Inventory;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
namespace Content.Shared._DV.Waypointer;
/// <summary>
/// This solely handles giving the Waypoint component to equipees. This cannot be done on client, or else it would.
/// </summary>
public abstract class SharedWaypointerSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] protected readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
public override void Initialize()
{
SubscribeLocalEvent<InnateWaypointerComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<ActiveWaypointerComponent, ActionManageWaypointersEvent>(OnActionPressed);
SubscribeLocalEvent<ActionComponent, WaypointersToggledMessage>(OnWaypointersToggled);
SubscribeLocalEvent<ActionComponent, WaypointerStatusChangedMessage>(OnWaypointersStatusChanged);
SubscribeLocalEvent<ClothingShowWaypointerComponent, ClothingGotEquippedEvent>(OnEquip);
SubscribeLocalEvent<ClothingShowWaypointerComponent, ClothingGotUnequippedEvent>(OnUnequip);
SubscribeLocalEvent<InnateWaypointerComponent, WaypointerChangedEvent>(OnWaypointerChanged);
SubscribeLocalEvent<ClothingShowWaypointerComponent, InventoryRelayedEvent<WaypointerChangedEvent>>(OnWaypointerChanged);
}
private void OnMapInit(Entity<InnateWaypointerComponent> player, ref MapInitEvent args)
{
SetWaypointerComponent(player);
}
private void OnActionPressed(Entity<ActiveWaypointerComponent> player, ref ActionManageWaypointersEvent args)
{
if (args.Handled)
return;
// To avoid adding UserInterfaceComponent on the BaseMob, we open the interface on the action entity, not the player.
_ui.OpenUi(args.Action.Owner, WaypointerUiKey.Key, player.Owner);
args.Handled = true;
}
protected virtual void OnWaypointersToggled(Entity<ActionComponent> action, ref WaypointersToggledMessage args)
{
// Messages are sent to the action entity - So we need to get the player from the component.
if (!TryComp<ActiveWaypointerComponent>(action.Comp.Container, out var waypointer)
|| waypointer.WaypointerProtoIds == null)
return;
waypointer.Active = args.IsActive;
_actions.SetToggled(action.AsNullable(), args.IsActive);
Dirty(action.Comp.Container.Value, waypointer);
}
private void OnWaypointersStatusChanged(Entity<ActionComponent> action, ref WaypointerStatusChangedMessage args)
{
// Messages are sent to the action entity - So we need to get the player from the component.
if (!TryComp<ActiveWaypointerComponent>(action.Comp.Container, out var waypointer)
|| waypointer.WaypointerProtoIds == null)
return;
waypointer.WaypointerProtoIds[args.ToggledWaypointerProtoId] = !waypointer.WaypointerProtoIds[args.ToggledWaypointerProtoId];
Dirty(action.Comp.Container.Value, waypointer);
}
private void OnEquip(Entity<ClothingShowWaypointerComponent> clothing, ref ClothingGotEquippedEvent args)
{
SetWaypointerComponent(args.Wearer);
}
private void OnUnequip(Entity<ClothingShowWaypointerComponent> clothing, ref ClothingGotUnequippedEvent args)
{
SetWaypointerComponent(args.Wearer);
}
private void OnWaypointerChanged(Entity<InnateWaypointerComponent> clothing, ref WaypointerChangedEvent args)
{
args.Waypointers.UnionWith(clothing.Comp.WaypointerProtoIds);
}
private void OnWaypointerChanged(Entity<ClothingShowWaypointerComponent> clothing, ref InventoryRelayedEvent<WaypointerChangedEvent> args)
{
args.Args.Waypointers.UnionWith(clothing.Comp.WaypointerProtoIds);
}
private void SetWaypointerComponent(EntityUid player)
{
if (_timing.ApplyingState)
return;
var comp = EnsureComp<ActiveWaypointerComponent>(player);
// The following is much easier to do with HashSets.
HashSet<ProtoId<WaypointerPrototype>>? previousTable = null;
HashSet<ProtoId<WaypointerPrototype>>? overridesToRemove = null;
if (comp.WaypointerProtoIds != null)
{
// Store the hashset for comparison later, if not null
previousTable = comp.WaypointerProtoIds.Keys.ToHashSet();
// The same as above. As an example, these have the values A and B. { A, B }
overridesToRemove = comp.WaypointerProtoIds.Keys.ToHashSet();
}
// We raise this on the entity to check for anything that could give the entity a waypointer.
var ev = new WaypointerChangedEvent();
RaiseLocalEvent(player, ref ev); // For example purposes, this will return { B, C }
if (overridesToRemove != null)
{
// Now we remove all the waypointers that the event gathered from the previous hashset.
// Removing { B, C } will leave us with { A }, as we don't have the waypointer A anymore.
overridesToRemove.ExceptWith(ev.Waypointers);
// We remove the overrides for the waypointer A.
RemoveOverrides(player, overridesToRemove);
}
if (ev.Waypointers.Count == 0) // Self-Explanatory - If there are no waypointers left, remove the component.
{
RemComp<ActiveWaypointerComponent>(player);
return;
}
// We need to turn it into a dictionary so we can keep track of every waypointer status, not important for overrides.
var newDict = ev.Waypointers.ToDictionary(key => key, _ => true);
if (comp.WaypointerProtoIds != null)
{
foreach (var pair in comp.WaypointerProtoIds.Where(pair => newDict.ContainsKey(pair.Key)))
{
// We set the status of every waypointer that persisted to their original value, not important for overrides.
newDict[pair.Key] = pair.Value;
}
}
// Then replace the old dictionary with the new one
comp.WaypointerProtoIds = newDict;
// Here we now remove every waypointer that doesn't need new overrides.
// The waypointer B was already overriden, since it's in the earlier hashset { A, B }
// So, by removing { A, b} from { B, C }, we get { C }, which is the only new waypointer - thus needing overrides.
if (previousTable != null)
ev.Waypointers.ExceptWith(previousTable);
AddOverrides(player, ev.Waypointers);
Dirty(player, comp);
}
protected virtual void AddOverrides(EntityUid player, HashSet<ProtoId<WaypointerPrototype>> waypointers)
{
}
protected virtual void RemoveOverrides(EntityUid player, HashSet<ProtoId<WaypointerPrototype>> waypointers)
{
}
}
[Serializable, NetSerializable]
public enum WaypointerUiKey : byte
{
Key,
}