Polymorphs and Transformation (#8185)
This commit is contained in:
41
Content.Server/Polymorph/Systems/PolymorphableSystem.Map.cs
Normal file
41
Content.Server/Polymorph/Systems/PolymorphableSystem.Map.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Content.Shared.GameTicking;
|
||||
|
||||
namespace Content.Server.Polymorph.Systems
|
||||
{
|
||||
public sealed partial class PolymorphableSystem : EntitySystem
|
||||
{
|
||||
public EntityUid? PausedMap { get; private set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Used to subscribe to the round restart event
|
||||
/// </summary>
|
||||
private void InitializeMap()
|
||||
{
|
||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
|
||||
}
|
||||
|
||||
private void OnRoundRestart(RoundRestartCleanupEvent _)
|
||||
{
|
||||
if (PausedMap == null || !Exists(PausedMap))
|
||||
return;
|
||||
|
||||
EntityManager.DeleteEntity(PausedMap.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used internally to ensure a paused map that is
|
||||
/// stores polymorphed entities.
|
||||
/// </summary>
|
||||
private void EnsurePausesdMap()
|
||||
{
|
||||
if (PausedMap != null && Exists(PausedMap))
|
||||
return;
|
||||
|
||||
var newmap = _mapManager.CreateMap();
|
||||
_mapManager.SetMapPaused(newmap, true);
|
||||
PausedMap = _mapManager.GetMapEntityId(newmap);
|
||||
|
||||
Dirty(PausedMap.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
194
Content.Server/Polymorph/Systems/PolymorphableSystem.cs
Normal file
194
Content.Server/Polymorph/Systems/PolymorphableSystem.cs
Normal file
@@ -0,0 +1,194 @@
|
||||
using Content.Server.Actions;
|
||||
using Content.Server.Buckle.Components;
|
||||
using Content.Server.Inventory;
|
||||
using Content.Server.Mind.Commands;
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Server.Polymorph.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Polymorph;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Polymorph.Systems
|
||||
{
|
||||
public sealed partial class PolymorphableSystem : EntitySystem
|
||||
{
|
||||
private readonly ISawmill _saw = default!;
|
||||
|
||||
[Dependency] private readonly ActionsSystem _actions = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
[Dependency] private readonly ServerInventorySystem _inventory = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _sharedHands = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PolymorphableComponent, ComponentStartup>(OnStartup);
|
||||
SubscribeLocalEvent<PolymorphableComponent, PolymorphActionEvent>(OnPolymorphActionEvent);
|
||||
|
||||
InitializeMap();
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, PolymorphableComponent component, ComponentStartup args)
|
||||
{
|
||||
if (component.InnatePolymorphs != null)
|
||||
{
|
||||
foreach (var morph in component.InnatePolymorphs)
|
||||
{
|
||||
CreatePolymorphAction(morph, uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPolymorphActionEvent(EntityUid uid, PolymorphableComponent component, PolymorphActionEvent args)
|
||||
{
|
||||
PolymorphEntity(uid, args.Prototype);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Polymorphs the target entity into the specific polymorph prototype
|
||||
/// </summary>
|
||||
/// <param name="target">The entity that will be transformed</param>
|
||||
/// <param name="id">The id of the polymorph prototype</param>
|
||||
public void PolymorphEntity(EntityUid target, String id)
|
||||
{
|
||||
if (!_proto.TryIndex<PolymorphPrototype>(id, out var proto))
|
||||
{
|
||||
_saw.Error("Invalid polymorph prototype");
|
||||
return;
|
||||
}
|
||||
|
||||
PolymorphEntity(target, proto);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Polymorphs the target entity into the specific polymorph prototype
|
||||
/// </summary>
|
||||
/// <param name="target">The entity that will be transformed</param>
|
||||
/// <param name="proto">The polymorph prototype</param>
|
||||
public void PolymorphEntity(EntityUid target, PolymorphPrototype proto)
|
||||
{
|
||||
// mostly just for vehicles
|
||||
if (TryComp<BuckleComponent>(target, out var buckle))
|
||||
buckle.TryUnbuckle(target, true);
|
||||
|
||||
var targetTransformComp = Transform(target);
|
||||
|
||||
var child = Spawn(proto.Entity, targetTransformComp.Coordinates);
|
||||
MakeSentientCommand.MakeSentient(child, EntityManager);
|
||||
|
||||
var comp = EnsureComp<PolymorphedEntityComponent>(child);
|
||||
comp.Parent = target;
|
||||
comp.Prototype = proto;
|
||||
RaiseLocalEvent(child, new PolymorphComponentSetupEvent());
|
||||
|
||||
//Transfers all damage from the original to the new one
|
||||
if (TryComp<DamageableComponent>(child, out var damageParent) &&
|
||||
_damageable.GetScaledDamage(target, child, out var damage) &&
|
||||
damage != null)
|
||||
{
|
||||
_damageable.SetDamage(damageParent, damage);
|
||||
}
|
||||
|
||||
if (proto.DropInventory)
|
||||
{
|
||||
//drops everything in the user's inventory
|
||||
if (_inventory.TryGetContainerSlotEnumerator(target, out var enumerator))
|
||||
{
|
||||
while (enumerator.MoveNext(out var containerSlot))
|
||||
{
|
||||
containerSlot.EmptyContainer();
|
||||
}
|
||||
}
|
||||
//drops everything in the user's hands
|
||||
foreach (var hand in _sharedHands.EnumerateHeld(target))
|
||||
{
|
||||
hand.TryRemoveFromContainer();
|
||||
}
|
||||
}
|
||||
|
||||
if (TryComp<MindComponent>(target, out var mind) && mind.Mind != null)
|
||||
mind.Mind.TransferTo(child);
|
||||
|
||||
//Ensures a map to banish the entity to
|
||||
EnsurePausesdMap();
|
||||
if(PausedMap != null)
|
||||
targetTransformComp.AttachParent(Transform(PausedMap.Value));
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("polymorph-popup-generic", ("parent", target), ("child", child)), child, Filter.Pvs(child));
|
||||
}
|
||||
|
||||
public void CreatePolymorphAction(string id, EntityUid target)
|
||||
{
|
||||
if (!_proto.TryIndex<PolymorphPrototype>(id, out var polyproto))
|
||||
{
|
||||
_saw.Error("Invalid polymorph prototype");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!TryComp<PolymorphableComponent>(target, out var polycomp))
|
||||
return;
|
||||
|
||||
var entproto = _proto.Index<EntityPrototype>(polyproto.Entity);
|
||||
|
||||
var act = new InstantAction()
|
||||
{
|
||||
Event = new PolymorphActionEvent(polyproto),
|
||||
Name = Loc.GetString("polymorph-self-action-name", ("target", entproto.Name)),
|
||||
Description = Loc.GetString("polymorph-self-action-description", ("target", entproto.Name)),
|
||||
Icon = new SpriteSpecifier.EntityPrototype(polyproto.Entity),
|
||||
ItemIconStyle = ItemActionIconStyle.NoItem,
|
||||
};
|
||||
|
||||
if (polycomp.PolymorphActions == null)
|
||||
polycomp.PolymorphActions = new();
|
||||
|
||||
polycomp.PolymorphActions.Add(id, act);
|
||||
_actions.AddAction(target, act, target);
|
||||
}
|
||||
|
||||
public void RemovePolymorphAction(string id, EntityUid target)
|
||||
{
|
||||
if (!_proto.TryIndex<PolymorphPrototype>(id, out var polyproto))
|
||||
return;
|
||||
if (!TryComp<PolymorphableComponent>(target, out var comp))
|
||||
return;
|
||||
if (comp.PolymorphActions == null)
|
||||
return;
|
||||
|
||||
comp.PolymorphActions.TryGetValue(id, out var val);
|
||||
if (val != null)
|
||||
_actions.RemoveAction(target, val);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used after the polymorphedEntity component has it's data set up.
|
||||
/// </summary>
|
||||
public sealed class PolymorphComponentSetupEvent : InstantActionEvent { };
|
||||
|
||||
public sealed class PolymorphActionEvent : InstantActionEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// The polymorph prototype containing all the information about
|
||||
/// the specific polymorph.
|
||||
/// </summary>
|
||||
public readonly PolymorphPrototype Prototype;
|
||||
|
||||
public PolymorphActionEvent(PolymorphPrototype prototype)
|
||||
{
|
||||
Prototype = prototype;
|
||||
}
|
||||
};
|
||||
}
|
||||
102
Content.Server/Polymorph/Systems/PolymorphedEntitySystem.cs
Normal file
102
Content.Server/Polymorph/Systems/PolymorphedEntitySystem.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using Content.Server.Actions;
|
||||
using Content.Server.Mind.Components;
|
||||
using Content.Server.Polymorph.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Actions.ActionTypes;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Polymorph.Systems
|
||||
{
|
||||
public sealed class PolymorphedEntitySystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionsSystem _actions = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PolymorphedEntityComponent, PolymorphComponentSetupEvent>(OnInit);
|
||||
SubscribeLocalEvent<PolymorphedEntityComponent, RevertPolymorphActionEvent>(OnRevertPolymorphActionEvent);
|
||||
}
|
||||
|
||||
private void OnRevertPolymorphActionEvent(EntityUid uid, PolymorphedEntityComponent component, RevertPolymorphActionEvent args)
|
||||
{
|
||||
Revert(uid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reverts a polymorphed entity back into its original form
|
||||
/// </summary>
|
||||
/// <param name="uid">The entityuid of the entity being reverted</param>
|
||||
public void Revert(EntityUid uid)
|
||||
{
|
||||
if (!TryComp<PolymorphedEntityComponent>(uid, out var component))
|
||||
return;
|
||||
|
||||
var uidXform = Transform(uid);
|
||||
var parentXform = Transform(component.Parent);
|
||||
|
||||
parentXform.AttachParent(uidXform.ParentUid);
|
||||
parentXform.Coordinates = uidXform.Coordinates;
|
||||
|
||||
if (TryComp<DamageableComponent>(component.Parent, out var damageParent) &&
|
||||
_damageable.GetScaledDamage(uid, component.Parent, out var damage) &&
|
||||
damage != null)
|
||||
{
|
||||
_damageable.SetDamage(damageParent, damage);
|
||||
}
|
||||
|
||||
if (TryComp<MindComponent>(uid, out var mind) && mind.Mind != null)
|
||||
{
|
||||
mind.Mind.TransferTo(component.Parent);
|
||||
}
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("polymorph-revert-popup-generic", ("parent", uid), ("child", component.Parent)), component.Parent, Filter.Pvs(component.Parent));
|
||||
QueueDel(uid);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, PolymorphedEntityComponent component, PolymorphComponentSetupEvent args)
|
||||
{
|
||||
if (component.Prototype.Forced)
|
||||
return;
|
||||
|
||||
var act = new InstantAction()
|
||||
{
|
||||
Event = new RevertPolymorphActionEvent(),
|
||||
EntityIcon = component.Parent,
|
||||
Name = Loc.GetString("polymorph-revert-action-name"),
|
||||
Description = Loc.GetString("polymorph-revert-action-description"),
|
||||
UseDelay = TimeSpan.FromSeconds(component.Prototype.Delay),
|
||||
};
|
||||
|
||||
_actions.AddAction(uid, act, null);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
foreach (var entity in EntityQuery<PolymorphedEntityComponent>())
|
||||
{
|
||||
entity.Time += frameTime;
|
||||
|
||||
if(entity.Prototype.Duration != null && entity.Time >= entity.Prototype.Duration)
|
||||
Revert(entity.Owner);
|
||||
|
||||
if (!TryComp<MobStateComponent>(entity.Owner, out var mob))
|
||||
continue;
|
||||
|
||||
if ((entity.Prototype.RevertOnDeath && mob.IsDead()) ||
|
||||
(entity.Prototype.RevertOnCrit && mob.IsCritical()))
|
||||
Revert(entity.Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class RevertPolymorphActionEvent : InstantActionEvent { };
|
||||
}
|
||||
Reference in New Issue
Block a user