Files
tbd-station-14/Content.Server/Body/Systems/BodyReassembleSystem.cs
Kara 2d5ec7f85c Id[entity] 2.0 (real) (#9612)
* starter API

* network ID cards

* Port more stuff from old identity

* Re-implement identity representation + name updating

* move

* proper name returning for `IdentityName`

* move everything important to server, give in to  temptation

* shared / server / client split sadly. move ensure to shared and spawn to server

* identity update queueing + identityblocker

* fixes

* and just like that it's usable for admins

* huge identity pass

* pass dos

* jesus christ

* figs :D

* fuck u

* fix bad merge.

Co-authored-by: Moony <moonheart08@users.noreply.github.com>
2022-07-10 20:36:53 -05:00

230 lines
8.8 KiB
C#

using System.Threading;
using Content.Server.Body.Components;
using Content.Server.Cloning;
using Content.Server.DoAfter;
using Content.Server.Mind.Components;
using Content.Server.Popups;
using Content.Server.Preferences.Managers;
using Content.Shared.Actions;
using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.IdentityManagement;
using Content.Shared.Preferences;
using Content.Shared.Species;
using Content.Shared.Verbs;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
/// <remarks>
/// Fair warning, this is all kinda shitcode, but it'll have to wait for a major
/// refactor until proper body systems get added. The current implementation is
/// definitely not ideal and probably will be prone to weird bugs.
/// </remarks>
namespace Content.Server.Body.Systems
{
public sealed class BodyReassembleSystem : EntitySystem
{
[Dependency] private readonly IServerPreferencesManager _prefsManager = null!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedActionsSystem _actions = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedHumanoidAppearanceSystem _humanoidAppearance = default!;
private const float SelfReassembleMultiplier = 2f;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BodyReassembleComponent, PartGibbedEvent>(OnPartGibbed);
SubscribeLocalEvent<BodyReassembleComponent, ReassembleActionEvent>(StartReassemblyAction);
SubscribeLocalEvent<BodyReassembleComponent, GetVerbsEvent<AlternativeVerb>>(AddReassembleVerbs);
SubscribeLocalEvent<BodyReassembleComponent, ReassembleCompleteEvent>(ReassembleComplete);
SubscribeLocalEvent<BodyReassembleComponent, ReassembleCancelledEvent>(ReassembleCancelled);
}
private void StartReassemblyAction(EntityUid uid, BodyReassembleComponent component, ReassembleActionEvent args)
{
args.Handled = true;
StartReassembly(uid, component, SelfReassembleMultiplier);
}
private void ReassembleCancelled(EntityUid uid, BodyReassembleComponent component, ReassembleCancelledEvent args)
{
component.CancelToken = null;
}
private void OnPartGibbed(EntityUid uid, BodyReassembleComponent component, PartGibbedEvent args)
{
if (!TryComp<MindComponent>(args.EntityToGib, out var mindComp) || mindComp?.Mind == null)
return;
component.BodyParts = args.GibbedParts;
UpdateDNAEntry(uid, args.EntityToGib);
mindComp.Mind.TransferTo(uid);
if (component.ReassembleAction == null)
return;
_actions.AddAction(uid, component.ReassembleAction, null);
}
private void StartReassembly(EntityUid uid, BodyReassembleComponent component, float multiplier = 1f)
{
if (component.CancelToken != null)
return;
if (!GetNearbyParts(uid, component, out var partList))
return;
if (partList == null)
return;
var doAfterTime = component.DoAfterTime * multiplier;
var cancelToken = new CancellationTokenSource();
component.CancelToken = cancelToken;
var doAfterEventArgs = new DoAfterEventArgs(component.Owner, doAfterTime, cancelToken.Token, component.Owner)
{
BreakOnTargetMove = true,
BreakOnUserMove = true,
BreakOnDamage = true,
BreakOnStun = true,
NeedHand = false,
TargetCancelledEvent = new ReassembleCancelledEvent(),
TargetFinishedEvent = new ReassembleCompleteEvent(uid, uid, partList),
};
_doAfterSystem.DoAfter(doAfterEventArgs);
}
/// <summary>
/// Adds the custom verb for reassembling body parts
/// </summary>
private void AddReassembleVerbs(EntityUid uid, BodyReassembleComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanAccess || !args.CanInteract)
return;
if (!TryComp<MindComponent>(uid, out var mind) ||
!mind.HasMind ||
component.CancelToken != null)
return;
// doubles the time if you reconstruct yourself
var multiplier = args.User == uid ? SelfReassembleMultiplier : 1f;
// Custom verb
AlternativeVerb custom = new()
{
Text = Loc.GetString("reassemble-action"),
Act = () =>
{
StartReassembly(uid, component, multiplier);
},
IconEntity = uid,
Priority = 1
};
args.Verbs.Add(custom);
}
private bool GetNearbyParts(EntityUid uid, BodyReassembleComponent component, out HashSet<EntityUid>? partList)
{
partList = new HashSet<EntityUid>();
if (component.BodyParts == null)
return false;
// Ensures all of the old body part pieces are there
var xformQuery = GetEntityQuery<TransformComponent>();
var bodyXform = xformQuery.GetComponent(uid);
foreach (var part in component.BodyParts)
{
if (!xformQuery.TryGetComponent(part, out var xform) ||
!bodyXform.Coordinates.InRange(EntityManager, xform.Coordinates,2f))
{
_popupSystem.PopupEntity(Loc.GetString("reassemble-fail"), uid, Filter.Entities(uid));
return false;
}
partList.Add(part);
}
return true;
}
private void ReassembleComplete(EntityUid uid, BodyReassembleComponent component, ReassembleCompleteEvent args)
{
component.CancelToken = null;
if (component.DNA == null || component.BodyParts == null)
return;
// Creates the new entity and transfers the mind component
var speciesProto = _prototype.Index<SpeciesPrototype>(component.DNA.Value.Profile.Species).Prototype;
var mob = EntityManager.SpawnEntity(speciesProto, EntityManager.GetComponent<TransformComponent>(component.Owner).MapPosition);
_humanoidAppearance.UpdateFromProfile(mob, component.DNA.Value.Profile);
MetaData(mob).EntityName = component.DNA.Value.Profile.Name;
if (TryComp<MindComponent>(uid, out var mindcomp) && mindcomp.Mind != null)
mindcomp.Mind.TransferTo(mob);
// Cleans up all the body part pieces
foreach (var entity in component.BodyParts)
{
EntityManager.DeleteEntity(entity);
}
_popupSystem.PopupEntity(Loc.GetString("reassemble-success", ("user", Identity.Entity(mob, EntityManager))), mob, Filter.Entities(mob));
}
/// <summary>
/// Called before the skeleton entity is gibbed in order to save
/// the dna for reassembly later
/// </summary>
/// <param name="uid"> the entity that the player will transfer to</param>
/// <param name="body"> the entity whose DNA is being saved</param>
private void UpdateDNAEntry(EntityUid uid, EntityUid body)
{
if (!TryComp<BodyReassembleComponent>(uid, out var skelBodyComp) || !TryComp<MindComponent>(body, out var mindcomp))
return;
if (mindcomp.Mind == null)
return;
if (mindcomp.Mind.UserId == null)
return;
var profile = (HumanoidCharacterProfile) _prefsManager.GetPreferences(mindcomp.Mind.UserId.Value).SelectedCharacter;
skelBodyComp.DNA = new ClonerDNAEntry(mindcomp.Mind, profile);
}
private sealed class ReassembleCompleteEvent : EntityEventArgs
{
/// <summary>
/// The entity being reassembled
/// </summary>
public readonly EntityUid Uid;
/// <summary>
/// The user performing the reassembly
/// </summary>
public readonly EntityUid User;
public readonly HashSet<EntityUid> PartList;
public ReassembleCompleteEvent(EntityUid uid, EntityUid user, HashSet<EntityUid> partList)
{
Uid = uid;
User = user;
PartList = partList;
}
}
private sealed class ReassembleCancelledEvent : EntityEventArgs {}
}
}
public sealed class ReassembleActionEvent : InstantActionEvent { }