230 lines
7.9 KiB
C#
230 lines
7.9 KiB
C#
using Content.Shared.DoAfter;
|
|
using Content.Shared.Eye;
|
|
using Content.Shared.Movement.Components;
|
|
using Content.Shared.Movement.Systems;
|
|
using Content.Shared.RatKing;
|
|
using Content.Shared.Verbs;
|
|
using Content.Shared.Whitelist;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Network;
|
|
using Robust.Shared.Physics.Systems;
|
|
using Robust.Shared.Prototypes;
|
|
|
|
namespace Content.Shared._DV.NodeCrawl;
|
|
|
|
/// <summary>
|
|
/// Manages entry & exit of node crawlers into node networks
|
|
/// </summary>
|
|
public abstract class SharedNodeCrawlSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly INetManager _net = default!;
|
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
|
[Dependency] private readonly SharedMoverController _mover = default!;
|
|
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
|
|
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
|
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
|
[Dependency] private readonly SharedEyeSystem _eye = default!;
|
|
[Dependency] private readonly NodeCrawlerMovementSystem _nodeCrawler = default!;
|
|
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
|
|
|
private const string MoverContainer = "mover-container";
|
|
private static readonly EntProtoId MoverProto = "DVNodeCrawlMover";
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<CrawlableNodeComponent, GetVerbsEvent<AlternativeVerb>>(OnGetVerbs);
|
|
SubscribeLocalEvent<NodeCrawlerComponent, NodeCrawlEnterDoAfterEvent>(OnNodeCrawlEntryDoAfter);
|
|
SubscribeLocalEvent<NodeCrawlerComponent, NodeCrawlerArrivedAtNodeEvent>(OnArrivedAtNode);
|
|
SubscribeLocalEvent<NodeCrawlerComponent, GetVisMaskEvent>(OnGetVisMask);
|
|
|
|
SubscribeLocalEvent<CrawlableNodeComponent, ComponentShutdown>(OnCrawlableShutdown);
|
|
SubscribeLocalEvent<NodeCrawlerMovementComponent, ComponentShutdown>(OnMovementShutdown);
|
|
SubscribeLocalEvent<NodeCrawlerComponent, ComponentShutdown>(OnCrawlerShutdown);
|
|
|
|
SubscribeLocalEvent<CrawlableNodeComponent, AnchorStateChangedEvent>(OnCrawlableAnchorChanged);
|
|
|
|
SubscribeLocalEvent<RatKingComponent, NodeCrawlerStartedCrawlingEvent>(OnRatKingStartedCrawling);
|
|
}
|
|
|
|
private void OnGetVerbs(Entity<CrawlableNodeComponent> ent, ref GetVerbsEvent<AlternativeVerb> args)
|
|
{
|
|
var user = args.User;
|
|
var target = args.Target;
|
|
if (!TryComp<NodeCrawlerComponent>(user, out var nodeCrawler))
|
|
return;
|
|
|
|
if (!_entityWhitelist.IsWhitelistPass(nodeCrawler.ExitNodes, target))
|
|
return;
|
|
|
|
if (!args.CanAccess)
|
|
return;
|
|
|
|
args.Verbs.Add(new AlternativeVerb
|
|
{
|
|
Act = () => StartEntryDoAfter((user, nodeCrawler), target),
|
|
Text = Loc.GetString("node-crawl-enter", ("target", target)),
|
|
});
|
|
}
|
|
|
|
private void StartEntryDoAfter(Entity<NodeCrawlerComponent> ent, EntityUid target)
|
|
{
|
|
var doAfterArgs = new DoAfterArgs(EntityManager, ent.Owner, ent.Comp.EnterDelay, new NodeCrawlEnterDoAfterEvent(), ent.Owner, target)
|
|
{
|
|
BreakOnMove = true,
|
|
BreakOnDamage = true,
|
|
};
|
|
|
|
_doAfter.TryStartDoAfter(doAfterArgs);
|
|
}
|
|
|
|
private void OnNodeCrawlEntryDoAfter(Entity<NodeCrawlerComponent> ent, ref NodeCrawlEnterDoAfterEvent args)
|
|
{
|
|
if (args.Cancelled || args.Target is not { } target)
|
|
return;
|
|
|
|
NodeCrawl(ent, target);
|
|
}
|
|
|
|
private void NodeCrawl(Entity<NodeCrawlerComponent> ent, EntityUid target)
|
|
{
|
|
if (!_net.IsServer)
|
|
return;
|
|
|
|
var mover = Spawn(MoverProto, Transform(target).Coordinates);
|
|
var crawler = Comp<NodeCrawlerMovementComponent>(mover);
|
|
|
|
var container = _container.GetContainer(mover, MoverContainer);
|
|
_container.Insert(ent.Owner, container);
|
|
|
|
ent.Comp.Mover = mover;
|
|
Dirty(ent);
|
|
|
|
var evt = new NodeCrawlerStartedCrawlingEvent((mover, crawler));
|
|
RaiseLocalEvent(ent, ref evt);
|
|
|
|
_nodeCrawler.SetNode((mover, crawler), target);
|
|
_nodeCrawler.SetHeldCrawler((mover, crawler), ent);
|
|
|
|
_mover.SetRelay(ent, mover);
|
|
_physics.SetCanCollide(ent.Owner, false);
|
|
_physics.SetCanCollide(mover, false);
|
|
_eye.RefreshVisibilityMask(ent.Owner);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Causes this node crawler to exit its node crawl.
|
|
/// </summary>
|
|
/// <param name="ent">The crawler to exit node-crawl from.</param>
|
|
public void ExitNodeCrawl(Entity<NodeCrawlerComponent> ent)
|
|
{
|
|
if (ent.Comp.Mover is not { } mover)
|
|
return;
|
|
|
|
ent.Comp.Mover = null;
|
|
Dirty(ent);
|
|
|
|
var container = _container.GetContainer(mover, MoverContainer);
|
|
_container.Remove(ent.Owner, container);
|
|
|
|
foreach (var other in _container.EmptyContainer(container))
|
|
{
|
|
if (!TryComp<NodeCrawlerComponent>(other, out var otherCrawler))
|
|
continue;
|
|
|
|
otherCrawler.Mover = null;
|
|
Dirty(other, otherCrawler);
|
|
}
|
|
|
|
RemComp<RelayInputMoverComponent>(ent);
|
|
if (_net.IsServer && !TerminatingOrDeleted(mover))
|
|
QueueDel(mover); // deletion isn't predicted because client queued deletion doesn't interact well with container stuff
|
|
|
|
_physics.SetCanCollide(ent.Owner, true);
|
|
_eye.RefreshVisibilityMask(ent.Owner);
|
|
}
|
|
|
|
private void OnArrivedAtNode(Entity<NodeCrawlerComponent> ent, ref NodeCrawlerArrivedAtNodeEvent args)
|
|
{
|
|
if (!_entityWhitelist.IsWhitelistPass(ent.Comp.ExitNodes, args.Node))
|
|
return;
|
|
|
|
ExitNodeCrawl(ent);
|
|
}
|
|
|
|
private void OnGetVisMask(Entity<NodeCrawlerComponent> ent, ref GetVisMaskEvent args)
|
|
{
|
|
if (ent.Comp.Mover is null)
|
|
return;
|
|
|
|
args.VisibilityMask |= (int)VisibilityFlags.Subfloor;
|
|
}
|
|
|
|
private void OnCrawlableShutdown(Entity<CrawlableNodeComponent> ent, ref ComponentShutdown args)
|
|
{
|
|
foreach (var crawler in ent.Comp.Crawlers)
|
|
{
|
|
var movement = Comp<NodeCrawlerMovementComponent>(crawler);
|
|
if (movement.HeldCrawler is not { } held)
|
|
continue;
|
|
|
|
_nodeCrawler.SetNode((crawler, movement), null);
|
|
ExitNodeCrawl((held, Comp<NodeCrawlerComponent>(held)));
|
|
}
|
|
}
|
|
|
|
private void OnMovementShutdown(Entity<NodeCrawlerMovementComponent> ent, ref ComponentShutdown args)
|
|
{
|
|
if (ent.Comp.Node is { } node)
|
|
{
|
|
var nodeComp = Comp<CrawlableNodeComponent>(node);
|
|
nodeComp.Crawlers.Remove(ent);
|
|
Dirty(node, nodeComp);
|
|
}
|
|
|
|
if (ent.Comp.HeldCrawler is { } crawler)
|
|
{
|
|
ExitNodeCrawl((crawler, Comp<NodeCrawlerComponent>(crawler)));
|
|
}
|
|
}
|
|
|
|
private void OnCrawlerShutdown(Entity<NodeCrawlerComponent> ent, ref ComponentShutdown args)
|
|
{
|
|
ExitNodeCrawl(ent);
|
|
}
|
|
|
|
private void OnCrawlableAnchorChanged(Entity<CrawlableNodeComponent> ent, ref AnchorStateChangedEvent args)
|
|
{
|
|
if (args.Anchored)
|
|
return;
|
|
|
|
foreach (var crawler in ent.Comp.Crawlers)
|
|
{
|
|
var movement = Comp<NodeCrawlerMovementComponent>(crawler);
|
|
if (movement.HeldCrawler is not { } held)
|
|
continue;
|
|
|
|
ExitNodeCrawl((held, Comp<NodeCrawlerComponent>(held)));
|
|
}
|
|
}
|
|
|
|
private void OnRatKingStartedCrawling(Entity<RatKingComponent> ent, ref NodeCrawlerStartedCrawlingEvent args)
|
|
{
|
|
var entities = new HashSet<Entity<RatKingServantComponent>>();
|
|
_entityLookup.GetEntitiesInRange(Transform(ent).Coordinates,
|
|
ent.Comp.VentCrawlRecruitRadius,
|
|
entities);
|
|
|
|
var container = _container.GetContainer(args.Mover, MoverContainer);
|
|
|
|
foreach (var servant in entities)
|
|
{
|
|
if (servant.Comp.King != ent)
|
|
continue;
|
|
|
|
_container.Insert(servant.Owner, container);
|
|
}
|
|
}
|
|
}
|