Files
tbd-station-14/Content.Server/GameObjects/Components/Medical/CloningPodComponent.cs
DrSmugleaf dd385a0511 Change all of body system to use entities and components (#2074)
* Early commit

* Early commit 2

* merging master broke my git

* does anyone even read these

* life is fleeting

* it just works

* this time passing integration tests

* Remove hashset yaml serialization for now

* You got a license for those nullables?

* No examine, no context menu, part and mechanism parenting and visibility

* Fix wrong brain sprite state

* Removing layers was a mistake

* just tear body system a new one and see if it still breathes

* Remove redundant code

* Add that comment back

* Separate damage and body, component states, stomach rework

* Add containers for body parts

* Bring layers back pls

* Fix parts magically changing color

* Reimplement sprite layer visibility

* Fix tests

* Add leg test

* Active legs is gone

Crab rave

* Merge fixes, rename DamageState to CurrentState

* Remove IShowContextMenu and ICanExamine
2020-10-10 15:25:13 +02:00

233 lines
8.0 KiB
C#

#nullable enable
using System;
using System.Linq;
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Observer;
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Mobs;
using Content.Server.Utility;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Medical;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Preferences;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Medical
{
[RegisterComponent]
[ComponentReference(typeof(IActivate))]
public class CloningPodComponent : SharedCloningPodComponent, IActivate
{
[Dependency] private readonly IServerPreferencesManager _prefsManager = null!;
[Dependency] private readonly IEntityManager _entityManager = null!;
[Dependency] private readonly IPlayerManager _playerManager = null!;
[ViewVariables]
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
[ViewVariables]
private BoundUserInterface? UserInterface =>
Owner.GetUIOrNull(CloningPodUIKey.Key);
private ContainerSlot _bodyContainer = default!;
private Mind? _capturedMind;
private CloningPodStatus _status;
private float _cloningProgress = 0;
private float _cloningTime;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _cloningTime, "cloningTime", 10f);
}
public override void Initialize()
{
base.Initialize();
if (UserInterface != null)
{
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
}
_bodyContainer = ContainerManagerComponent.Ensure<ContainerSlot>($"{Name}-bodyContainer", Owner);
//TODO: write this so that it checks for a change in power events for GORE POD cases
var newState = GetUserInterfaceState();
UserInterface?.SetState(newState);
UpdateUserInterface();
Owner.EntityManager.EventBus.SubscribeEvent<GhostComponent.GhostReturnMessage>(EventSource.Local, this,
HandleGhostReturn);
}
public void Update(float frametime)
{
if (_bodyContainer.ContainedEntity != null &&
Powered)
{
_cloningProgress += frametime;
_cloningProgress = MathHelper.Clamp(_cloningProgress, 0f, _cloningTime);
}
if (_cloningProgress >= _cloningTime &&
_bodyContainer.ContainedEntity != null &&
_capturedMind?.Session.AttachedEntity == _bodyContainer.ContainedEntity &&
Powered)
{
_bodyContainer.Remove(_bodyContainer.ContainedEntity);
_capturedMind = null;
_cloningProgress = 0f;
_status = CloningPodStatus.Idle;
UpdateAppearance();
}
UpdateUserInterface();
}
public override void OnRemove()
{
if (UserInterface != null)
{
UserInterface.OnReceiveMessage -= OnUiReceiveMessage;
}
Owner.EntityManager.EventBus.UnsubscribeEvent<GhostComponent.GhostReturnMessage>(EventSource.Local, this);
base.OnRemove();
}
private void UpdateUserInterface()
{
if (!Powered) return;
UserInterface?.SetState(GetUserInterfaceState());
}
private CloningPodBoundUserInterfaceState GetUserInterfaceState()
{
return new CloningPodBoundUserInterfaceState(CloningSystem.getIdToUser(), _cloningProgress,
(_status == CloningPodStatus.Cloning));
}
private void UpdateAppearance()
{
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
{
appearance.SetData(CloningPodVisuals.Status, _status);
}
}
public void Activate(ActivateEventArgs eventArgs)
{
if (!Powered ||
!eventArgs.User.TryGetComponent(out IActorComponent? actor))
{
return;
}
UserInterface?.Open(actor.playerSession);
}
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
{
if (!(obj.Message is CloningPodUiButtonPressedMessage message)) return;
switch (message.Button)
{
case UiButton.Clone:
if (message.ScanId == null) return;
if (_bodyContainer.ContainedEntity != null ||
!CloningSystem.Minds.TryGetValue(message.ScanId.Value, out var mind))
{
return;
}
var dead =
mind.OwnedEntity.TryGetComponent<IDamageableComponent>(out var damageable) &&
damageable.CurrentState == DamageState.Dead;
if (!dead) return;
var mob = _entityManager.SpawnEntity("HumanMob_Content", Owner.Transform.MapPosition);
var client = _playerManager.GetSessionByUserId(mind.UserId!.Value);
var profile = GetPlayerProfileAsync(client.UserId);
mob.GetComponent<HumanoidAppearanceComponent>().UpdateFromProfile(profile);
mob.Name = profile.Name;
_bodyContainer.Insert(mob);
_capturedMind = mind;
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local,
new CloningStartedMessage(_capturedMind));
_status = CloningPodStatus.NoMind;
UpdateAppearance();
break;
case UiButton.Eject:
if (_bodyContainer.ContainedEntity == null || _cloningProgress < _cloningTime) break;
_bodyContainer.Remove(_bodyContainer.ContainedEntity!);
_capturedMind = null;
_cloningProgress = 0f;
_status = CloningPodStatus.Idle;
UpdateAppearance();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
public class CloningStartedMessage : EntitySystemMessage
{
public CloningStartedMessage(Mind capturedMind)
{
CapturedMind = capturedMind;
}
public Mind CapturedMind { get; }
}
private HumanoidCharacterProfile GetPlayerProfileAsync(NetUserId userId)
{
return (HumanoidCharacterProfile) _prefsManager.GetPreferences(userId).SelectedCharacter;
}
private void HandleGhostReturn(GhostComponent.GhostReturnMessage message)
{
if (message.Sender == _capturedMind)
{
//If the captured mind is in a ghost, we want to get rid of it.
_capturedMind.VisitingEntity?.Delete();
//Transfer the mind to the new mob
_capturedMind.TransferTo(_bodyContainer.ContainedEntity);
_status = CloningPodStatus.Cloning;
UpdateAppearance();
}
}
}
}