217 lines
7.8 KiB
C#
217 lines
7.8 KiB
C#
using Content.Server.Actions;
|
|
using Content.Server.Ghost.Roles.Events;
|
|
using Content.Server.Pinpointer;
|
|
using Content.Server.Popups;
|
|
using Content.Server.Stunnable;
|
|
using Content.Shared.Actions;
|
|
using Content.Shared.Body.Part;
|
|
using Content.Shared.CombatMode;
|
|
using Content.Shared.Emp;
|
|
using Content.Shared.Hands.Components;
|
|
using Content.Shared.Hands.EntitySystems;
|
|
using Content.Shared.Interaction.Events;
|
|
using Content.Shared.Inventory;
|
|
using Content.Shared.Mind.Components;
|
|
using Content.Shared.Mobs;
|
|
using Content.Shared.Mobs.Systems;
|
|
using Content.Shared.Pinpointer;
|
|
using Content.Shared.Popups;
|
|
using Content.Shared._Impstation.Replicator;
|
|
using Content.Shared._Impstation.SpawnedFromTracker;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Timing;
|
|
using System.Linq;
|
|
|
|
namespace Content.Server._Impstation.Replicator;
|
|
|
|
public sealed class ReplicatorSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly IGameTiming _timing = default!;
|
|
[Dependency] private readonly ActionsSystem _actions = default!;
|
|
[Dependency] private readonly AppearanceSystem _appearance = default!;
|
|
[Dependency] private readonly PopupSystem _popup = default!;
|
|
[Dependency] private readonly StunSystem _stun = default!;
|
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
|
[Dependency] private readonly PinpointerSystem _pinpointer = default!;
|
|
[Dependency] private readonly SharedHandsSystem _hands = default!;
|
|
[Dependency] private readonly SharedReplicatorNestSystem _replicatorNest = default!;
|
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<ReplicatorComponent, MindAddedMessage>(OnMindAdded);
|
|
SubscribeLocalEvent<ReplicatorComponent, MindRemovedMessage>(OnMindRemoved);
|
|
SubscribeLocalEvent<ReplicatorComponent, AttackAttemptEvent>(OnAttackAttempt);
|
|
SubscribeLocalEvent<ReplicatorComponent, ToggleCombatActionEvent>(OnCombatToggle);
|
|
SubscribeLocalEvent<ReplicatorComponent, GhostRoleSpawnerUsedEvent>(OnGhostRoleSpawnerUsed);
|
|
SubscribeLocalEvent<ReplicatorComponent, ReplicatorSpawnNestActionEvent>(OnSpawnNestAction);
|
|
SubscribeLocalEvent<ReplicatorComponent, EmpPulseEvent>(OnEmpPulse);
|
|
SubscribeLocalEvent<ReplicatorComponent, MobStateChangedEvent>(OnMobStateChanged);
|
|
SubscribeLocalEvent<ReplicatorComponent, MapInitEvent>(OnReplicatorMapInit);
|
|
SubscribeLocalEvent<ReplicatorComponent, BodyPartAddedEvent>(OnBodyPartAdded);
|
|
}
|
|
|
|
private void OnReplicatorMapInit(Entity<ReplicatorComponent> ent, ref MapInitEvent args)
|
|
{
|
|
CleanupInheritedHands(ent);
|
|
}
|
|
|
|
private void OnBodyPartAdded(Entity<ReplicatorComponent> ent, ref BodyPartAddedEvent args)
|
|
{
|
|
CleanupInheritedHands(ent);
|
|
}
|
|
|
|
private void OnMindAdded(Entity<ReplicatorComponent> ent, ref MindAddedMessage args)
|
|
{
|
|
CleanupInheritedHands(ent);
|
|
|
|
if (ent.Comp.HasSpawnedNest)
|
|
return;
|
|
|
|
if (!ent.Comp.Queen)
|
|
return;
|
|
|
|
ent.Comp.Actions.Add(_actions.AddAction(ent, ent.Comp.SpawnNewNestAction));
|
|
|
|
ent.Comp.HasSpawnedNest = true;
|
|
}
|
|
|
|
private void OnMindRemoved(Entity<ReplicatorComponent> ent, ref MindRemovedMessage args)
|
|
{
|
|
foreach (var action in ent.Comp.Actions)
|
|
{
|
|
QueueDel(action);
|
|
}
|
|
}
|
|
|
|
private void OnSpawnNestAction(Entity<ReplicatorComponent> ent, ref ReplicatorSpawnNestActionEvent args)
|
|
{
|
|
if (!_timing.IsFirstTimePredicted)
|
|
return;
|
|
|
|
var xform = Transform(ent);
|
|
var coords = xform.Coordinates;
|
|
if (!coords.IsValid(EntityManager) || xform.MapID == MapId.Nullspace)
|
|
return;
|
|
|
|
var myNest = Spawn("ReplicatorNest", xform.Coordinates);
|
|
var myNestComp = EnsureComp<ReplicatorNestComponent>(myNest);
|
|
|
|
if (ent.Comp.RelatedReplicators.Count <= 0 || ent.Comp.Queen && !ent.Comp.RelatedReplicators.Contains(ent))
|
|
ent.Comp.RelatedReplicators.Add(ent);
|
|
|
|
HashSet<EntityUid> newMinions = [];
|
|
HashSet<(EntityUid, ReplicatorComponent)> livingReplicators = [];
|
|
var query = EntityQueryEnumerator<ReplicatorComponent>();
|
|
while (query.MoveNext(out var uid, out var comp))
|
|
{
|
|
livingReplicators.Add((uid, comp));
|
|
}
|
|
|
|
foreach (var (uid, comp) in livingReplicators)
|
|
{
|
|
newMinions.Add(uid);
|
|
|
|
if (_inventory.TryGetSlotEntity(uid, "pocket1", out var pocket1) && TryComp<PinpointerComponent>(pocket1, out var pinpointer))
|
|
_pinpointer.SetTarget(pocket1.Value, myNest, pinpointer);
|
|
|
|
var pinpointerQuery = EntityQueryEnumerator<PinpointerComponent, TransformComponent>();
|
|
while (pinpointerQuery.MoveNext(out var pinUid, out var pinComp, out var pinXform))
|
|
{
|
|
if (pinXform.ParentUid == uid)
|
|
_pinpointer.SetTarget(pinUid, myNest, pinComp);
|
|
}
|
|
|
|
comp.MyNest = myNest;
|
|
}
|
|
|
|
myNestComp.SpawnedMinions = newMinions;
|
|
myNestComp.SpawnedMinions.Add(ent);
|
|
ent.Comp.MyNest = myNest;
|
|
ent.Comp.RelatedReplicators.Clear();
|
|
ent.Comp.Queen = false;
|
|
|
|
_replicatorNest.ForceUpgrade(ent, ent.Comp.FirstStage);
|
|
}
|
|
|
|
private void OnGhostRoleSpawnerUsed(Entity<ReplicatorComponent> ent, ref GhostRoleSpawnerUsedEvent args)
|
|
{
|
|
CleanupInheritedHands(ent);
|
|
|
|
if (!TryComp<SpawnedFromTrackerComponent>(args.Spawner, out var tracker) ||
|
|
!TryComp<ReplicatorNestComponent>(tracker.SpawnedFrom, out var nestComp))
|
|
return;
|
|
|
|
nestComp.SpawnedMinions.Add(ent);
|
|
nestComp.UnclaimedSpawners.Remove(args.Spawner);
|
|
ent.Comp.MyNest = tracker.SpawnedFrom;
|
|
}
|
|
|
|
private void OnAttackAttempt(Entity<ReplicatorComponent> ent, ref AttackAttemptEvent args)
|
|
{
|
|
if (HasComp<ReplicatorComponent>(args.Target))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("replicator-on-replicator-attack-fail"), ent, ent, PopupType.MediumCaution);
|
|
args.Cancel();
|
|
}
|
|
|
|
if (HasComp<ReplicatorNestComponent>(args.Target))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("replicator-on-nest-attack-fail"), ent, ent, PopupType.MediumCaution);
|
|
args.Cancel();
|
|
}
|
|
}
|
|
|
|
private void OnCombatToggle(Entity<ReplicatorComponent> ent, ref ToggleCombatActionEvent args)
|
|
{
|
|
if (!TryComp<CombatModeComponent>(ent, out var combat))
|
|
return;
|
|
|
|
_appearance.SetData(ent, ReplicatorVisuals.Combat, combat.IsInCombatMode);
|
|
}
|
|
|
|
private void OnMobStateChanged(Entity<ReplicatorComponent> ent, ref MobStateChangedEvent args)
|
|
{
|
|
if (_mobState.IsAlive(ent))
|
|
return;
|
|
|
|
_appearance.SetData(ent, ReplicatorVisuals.Combat, false);
|
|
|
|
var query = EntityQueryEnumerator<ReplicatorComponent>();
|
|
while (query.MoveNext(out var uid, out var replicatorComp))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString(replicatorComp.QueenDiedMessage), uid, uid, PopupType.LargeCaution);
|
|
}
|
|
}
|
|
|
|
private void OnEmpPulse(Entity<ReplicatorComponent> ent, ref EmpPulseEvent args)
|
|
{
|
|
args.Affected = true;
|
|
args.Disabled = true;
|
|
_stun.TryUpdateParalyzeDuration(ent, ent.Comp.EmpStunTime);
|
|
}
|
|
|
|
private void CleanupInheritedHands(Entity<ReplicatorComponent> ent)
|
|
{
|
|
if (!TryComp<HandsComponent>(ent, out var handsComp))
|
|
return;
|
|
|
|
foreach (var handId in handsComp.Hands.Keys.ToArray())
|
|
{
|
|
if (ent.Comp.Queen)
|
|
{
|
|
_hands.RemoveHand((ent.Owner, handsComp), handId);
|
|
continue;
|
|
}
|
|
|
|
if (handId.Contains("-hand-"))
|
|
continue;
|
|
|
|
_hands.RemoveHand((ent.Owner, handsComp), handId);
|
|
}
|
|
}
|
|
}
|