Re-organize all projects (#4166)
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
using Content.Server.Disposal.Unit.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Disposal.Tube.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalBendComponent : DisposalTubeComponent
|
||||
{
|
||||
[DataField("sideDegrees")]
|
||||
private int _sideDegrees = -90;
|
||||
|
||||
public override string Name => "DisposalBend";
|
||||
|
||||
protected override Direction[] ConnectableDirections()
|
||||
{
|
||||
var direction = Owner.Transform.LocalRotation;
|
||||
var side = new Angle(MathHelper.DegreesToRadians(direction.Degrees + _sideDegrees));
|
||||
|
||||
return new[] {direction.GetDir(), side.GetDir()};
|
||||
}
|
||||
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
var directions = ConnectableDirections();
|
||||
var previousTube = holder.PreviousTube;
|
||||
|
||||
if (previousTube == null)
|
||||
{
|
||||
return directions[0];
|
||||
}
|
||||
|
||||
var previousDirection = DirectionTo(previousTube);
|
||||
return previousDirection == directions[0] ? directions[1] : directions[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Server.Disposal.Unit.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Disposal.Tube.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalEntryComponent : DisposalTubeComponent
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
private const string HolderPrototypeId = "DisposalHolder";
|
||||
|
||||
public override string Name => "DisposalEntry";
|
||||
|
||||
public bool TryInsert(DisposalUnitComponent from)
|
||||
{
|
||||
var holder = Owner.EntityManager.SpawnEntity(HolderPrototypeId, Owner.Transform.MapPosition);
|
||||
var holderComponent = holder.GetComponent<DisposalHolderComponent>();
|
||||
|
||||
foreach (var entity in from.ContainedEntities.ToArray())
|
||||
{
|
||||
holderComponent.TryInsert(entity);
|
||||
}
|
||||
|
||||
holderComponent.Air.Merge(from.Air);
|
||||
from.Air.Clear();
|
||||
|
||||
return TryInsert(holderComponent);
|
||||
}
|
||||
|
||||
public bool TryInsert(DisposalHolderComponent holder)
|
||||
{
|
||||
if (!Contents.Insert(holder.Owner))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
holder.EnterTube(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override Direction[] ConnectableDirections()
|
||||
{
|
||||
return new[] {Owner.Transform.LocalRotation.GetDir()};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ejects contents when they come from the same direction the entry is facing.
|
||||
/// </summary>
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
if (holder.PreviousTube != null && DirectionTo(holder.PreviousTube) == ConnectableDirections()[0])
|
||||
{
|
||||
var invalidDirections = new[] { ConnectableDirections()[0], Direction.Invalid };
|
||||
var directions = Enum.GetValues(typeof(Direction))
|
||||
.Cast<Direction>().Except(invalidDirections).ToList();
|
||||
return _random.Pick(directions);
|
||||
}
|
||||
|
||||
return ConnectableDirections()[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Disposal.Unit.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Disposal.Tube.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalJunctionComponent : DisposalTubeComponent
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The angles to connect to.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("degrees")]
|
||||
private List<Angle> _degrees = new();
|
||||
|
||||
public override string Name => "DisposalJunction";
|
||||
|
||||
protected override Direction[] ConnectableDirections()
|
||||
{
|
||||
var direction = Owner.Transform.LocalRotation;
|
||||
|
||||
return _degrees.Select(degree => new Angle(degree.Theta + direction.Theta).GetDir()).ToArray();
|
||||
}
|
||||
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
var next = Owner.Transform.LocalRotation.GetDir();
|
||||
var directions = ConnectableDirections().Skip(1).ToArray();
|
||||
|
||||
if (holder.PreviousTube == null ||
|
||||
DirectionTo(holder.PreviousTube) == next)
|
||||
{
|
||||
return _random.Pick(directions);
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Content.Server.Disposal.Unit.Components;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Notification;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.Console;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.Disposal.Components.SharedDisposalRouterComponent;
|
||||
|
||||
namespace Content.Server.Disposal.Tube.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalRouterComponent : DisposalJunctionComponent, IActivate
|
||||
{
|
||||
public override string Name => "DisposalRouter";
|
||||
|
||||
[ViewVariables]
|
||||
private readonly HashSet<string> _tags = new();
|
||||
|
||||
[ViewVariables]
|
||||
public bool Anchored =>
|
||||
!Owner.TryGetComponent(out IPhysBody? physics) ||
|
||||
physics.BodyType == BodyType.Static;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(DisposalRouterUiKey.Key);
|
||||
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
var directions = ConnectableDirections();
|
||||
|
||||
if (holder.Tags.Overlaps(_tags))
|
||||
{
|
||||
return directions[1];
|
||||
}
|
||||
|
||||
return Owner.Transform.LocalRotation.GetDir();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles ui messages from the client. For things such as button presses
|
||||
/// which interact with the world and require server action.
|
||||
/// </summary>
|
||||
/// <param name="obj">A user interface message from the client.</param>
|
||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
if (obj.Session.AttachedEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = (UiActionMessage) obj.Message;
|
||||
|
||||
if (!PlayerCanUseDisposalTagger(obj.Session))
|
||||
return;
|
||||
|
||||
//Check for correct message and ignore maleformed strings
|
||||
if (msg.Action == UiAction.Ok && TagRegex.IsMatch(msg.Tags))
|
||||
{
|
||||
_tags.Clear();
|
||||
foreach (var tag in msg.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
_tags.Add(tag.Trim());
|
||||
ClickSound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the player entity is able to use the configuration interface of the pipe tagger.
|
||||
/// </summary>
|
||||
/// <param name="IPlayerSession">The player session.</param>
|
||||
/// <returns>Returns true if the entity can use the configuration interface, and false if it cannot.</returns>
|
||||
private bool PlayerCanUseDisposalTagger(IPlayerSession session)
|
||||
{
|
||||
//Need player entity to check if they are still able to use the configuration interface
|
||||
if (session.AttachedEntity == null)
|
||||
return false;
|
||||
if (!Anchored)
|
||||
return false;
|
||||
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
//Check if player can interact in their current state
|
||||
if (!groupController.CanAdminMenu(session) && (!ActionBlockerSystem.CanInteract(session.AttachedEntity) || !ActionBlockerSystem.CanUse(session.AttachedEntity)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets component data to be used to update the user interface client-side.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <see cref="DisposalRouterUserInterfaceState"/></returns>
|
||||
private DisposalRouterUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
if(_tags.Count <= 0)
|
||||
{
|
||||
return new DisposalRouterUserInterfaceState("");
|
||||
}
|
||||
|
||||
var taglist = new StringBuilder();
|
||||
|
||||
foreach (var tag in _tags)
|
||||
{
|
||||
taglist.Append(tag);
|
||||
taglist.Append(", ");
|
||||
}
|
||||
|
||||
taglist.Remove(taglist.Length - 2, 2);
|
||||
|
||||
return new DisposalRouterUserInterfaceState(taglist.ToString());
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
var state = GetUserInterfaceState();
|
||||
UserInterface?.SetState(state);
|
||||
}
|
||||
|
||||
private void ClickSound()
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when you click the owner entity with an empty hand. Opens the UI client-side if possible.
|
||||
/// </summary>
|
||||
/// <param name="args">Data relevant to the event such as the actor which triggered it.</param>
|
||||
void IActivate.Activate(ActivateEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out ActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
Owner.PopupMessage(args.User, Loc.GetString("You have no hands."));
|
||||
return;
|
||||
}
|
||||
|
||||
var activeHandEntity = hands.GetActiveHand?.Owner;
|
||||
if (activeHandEntity == null)
|
||||
{
|
||||
OpenUserInterface(actor);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
UserInterface?.CloseAll();
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
private void OpenUserInterface(ActorComponent actor)
|
||||
{
|
||||
UpdateUserInterface();
|
||||
UserInterface?.Open(actor.PlayerSession);
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class ConfigureVerb : Verb<DisposalRouterComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, DisposalRouterComponent component, VerbData data)
|
||||
{
|
||||
var session = user.PlayerSession();
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
if (session == null || !groupController.CanAdminMenu(session))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("Open Configuration");
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/settings.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, DisposalRouterComponent component)
|
||||
{
|
||||
if (user.TryGetComponent(out ActorComponent? actor))
|
||||
{
|
||||
component.OpenUserInterface(actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
#nullable enable
|
||||
using Content.Server.Disposal.Unit.Components;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Notification;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.Console;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using static Content.Shared.Disposal.Components.SharedDisposalTaggerComponent;
|
||||
|
||||
namespace Content.Server.Disposal.Tube.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalTaggerComponent : DisposalTransitComponent, IActivate
|
||||
{
|
||||
public override string Name => "DisposalTagger";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private string _tag = "";
|
||||
|
||||
[ViewVariables]
|
||||
public bool Anchored =>
|
||||
!Owner.TryGetComponent(out PhysicsComponent? physics) ||
|
||||
physics.BodyType == BodyType.Static;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(DisposalTaggerUiKey.Key);
|
||||
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
holder.Tags.Add(_tag);
|
||||
return base.NextDirection(holder);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles ui messages from the client. For things such as button presses
|
||||
/// which interact with the world and require server action.
|
||||
/// </summary>
|
||||
/// <param name="obj">A user interface message from the client.</param>
|
||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
var msg = (UiActionMessage) obj.Message;
|
||||
|
||||
if (!PlayerCanUseDisposalTagger(obj.Session))
|
||||
return;
|
||||
|
||||
//Check for correct message and ignore maleformed strings
|
||||
if (msg.Action == UiAction.Ok && TagRegex.IsMatch(msg.Tag))
|
||||
{
|
||||
_tag = msg.Tag;
|
||||
ClickSound();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the player entity is able to use the configuration interface of the pipe tagger.
|
||||
/// </summary>
|
||||
/// <param name="IPlayerSession">The player entity.</param>
|
||||
/// <returns>Returns true if the entity can use the configuration interface, and false if it cannot.</returns>
|
||||
private bool PlayerCanUseDisposalTagger(IPlayerSession session)
|
||||
{
|
||||
//Need player entity to check if they are still able to use the configuration interface
|
||||
if (session.AttachedEntity == null)
|
||||
return false;
|
||||
if (!Anchored)
|
||||
return false;
|
||||
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
//Check if player can interact in their current state
|
||||
if (!groupController.CanAdminMenu(session) && (!ActionBlockerSystem.CanInteract(session.AttachedEntity) || !ActionBlockerSystem.CanUse(session.AttachedEntity)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets component data to be used to update the user interface client-side.
|
||||
/// </summary>
|
||||
/// <returns>Returns a <see cref="DisposalTaggerUserInterfaceState"/></returns>
|
||||
private DisposalTaggerUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
return new(_tag);
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
var state = GetUserInterfaceState();
|
||||
UserInterface?.SetState(state);
|
||||
}
|
||||
|
||||
private void ClickSound()
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when you click the owner entity with an empty hand. Opens the UI client-side if possible.
|
||||
/// </summary>
|
||||
/// <param name="args">Data relevant to the event such as the actor which triggered it.</param>
|
||||
void IActivate.Activate(ActivateEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out ActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!args.User.TryGetComponent(out IHandsComponent? hands))
|
||||
{
|
||||
Owner.PopupMessage(args.User, Loc.GetString("You have no hands."));
|
||||
return;
|
||||
}
|
||||
|
||||
var activeHandEntity = hands.GetActiveHand?.Owner;
|
||||
if (activeHandEntity == null)
|
||||
{
|
||||
OpenUserInterface(actor);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
UserInterface?.CloseAll();
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class ConfigureVerb : Verb<DisposalTaggerComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, DisposalTaggerComponent component, VerbData data)
|
||||
{
|
||||
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
if (!user.TryGetComponent(out ActorComponent? actor) || !groupController.CanAdminMenu(actor.PlayerSession))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("Open Configuration");
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/settings.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, DisposalTaggerComponent component)
|
||||
{
|
||||
if (user.TryGetComponent(out ActorComponent? actor))
|
||||
{
|
||||
component.OpenUserInterface(actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OpenUserInterface(ActorComponent actor)
|
||||
{
|
||||
UpdateUserInterface();
|
||||
UserInterface?.Open(actor.PlayerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Content.Server.Disposal.Unit.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Server.Disposal.Tube.Components
|
||||
{
|
||||
// TODO: Different types of tubes eject in random direction with no exit point
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IDisposalTubeComponent))]
|
||||
public class DisposalTransitComponent : DisposalTubeComponent
|
||||
{
|
||||
public override string Name => "DisposalTransit";
|
||||
|
||||
protected override Direction[] ConnectableDirections()
|
||||
{
|
||||
var rotation = Owner.Transform.LocalRotation;
|
||||
var opposite = new Angle(rotation.Theta + Math.PI);
|
||||
|
||||
return new[] {rotation.GetDir(), opposite.GetDir()};
|
||||
}
|
||||
|
||||
public override Direction NextDirection(DisposalHolderComponent holder)
|
||||
{
|
||||
var directions = ConnectableDirections();
|
||||
var previousTube = holder.PreviousTube;
|
||||
var forward = directions[0];
|
||||
|
||||
if (previousTube == null)
|
||||
{
|
||||
return forward;
|
||||
}
|
||||
|
||||
var backward = directions[1];
|
||||
return DirectionTo(previousTube) == forward ? backward : forward;
|
||||
}
|
||||
}
|
||||
}
|
||||
313
Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
Normal file
313
Content.Server/Disposal/Tube/Components/DisposalTubeComponent.cs
Normal file
@@ -0,0 +1,313 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Server.Anchor;
|
||||
using Content.Server.Disposal.Unit.Components;
|
||||
using Content.Shared.Acts;
|
||||
using Content.Shared.Disposal.Components;
|
||||
using Content.Shared.Notification;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Server.Console;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Disposal.Tube.Components
|
||||
{
|
||||
public abstract class DisposalTubeComponent : Component, IDisposalTubeComponent, IBreakAct
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
private static readonly TimeSpan ClangDelay = TimeSpan.FromSeconds(0.5);
|
||||
private TimeSpan _lastClang;
|
||||
|
||||
private bool _connected;
|
||||
private bool _broken;
|
||||
[DataField("clangSound")]
|
||||
private string _clangSound = "/Audio/Effects/clang.ogg";
|
||||
|
||||
/// <summary>
|
||||
/// Container of entities that are currently inside this tube
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Container Contents { get; private set; } = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private bool Anchored =>
|
||||
!Owner.TryGetComponent(out PhysicsComponent? physics) ||
|
||||
physics.BodyType == BodyType.Static;
|
||||
|
||||
/// <summary>
|
||||
/// The directions that this tube can connect to others from
|
||||
/// </summary>
|
||||
/// <returns>a new array of the directions</returns>
|
||||
protected abstract Direction[] ConnectableDirections();
|
||||
|
||||
public abstract Direction NextDirection(DisposalHolderComponent holder);
|
||||
|
||||
public virtual Vector2 ExitVector(DisposalHolderComponent holder)
|
||||
{
|
||||
return NextDirection(holder).ToVec();
|
||||
}
|
||||
|
||||
protected Direction DirectionTo(IDisposalTubeComponent other)
|
||||
{
|
||||
return (other.Owner.Transform.WorldPosition - Owner.Transform.WorldPosition).GetDir();
|
||||
}
|
||||
|
||||
public IDisposalTubeComponent? NextTube(DisposalHolderComponent holder)
|
||||
{
|
||||
var nextDirection = NextDirection(holder);
|
||||
var oppositeDirection = new Angle(nextDirection.ToAngle().Theta + Math.PI).GetDir();
|
||||
|
||||
var grid = _mapManager.GetGrid(Owner.Transform.GridID);
|
||||
var position = Owner.Transform.Coordinates;
|
||||
foreach (var entity in grid.GetInDir(position, nextDirection))
|
||||
{
|
||||
if (!Owner.EntityManager.ComponentManager.TryGetComponent(entity, out IDisposalTubeComponent? tube))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!tube.CanConnect(oppositeDirection, this))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CanConnect(nextDirection, tube))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return tube;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool Remove(DisposalHolderComponent holder)
|
||||
{
|
||||
var removed = Contents.Remove(holder.Owner);
|
||||
holder.ExitDisposals();
|
||||
return removed;
|
||||
}
|
||||
|
||||
public bool TransferTo(DisposalHolderComponent holder, IDisposalTubeComponent to)
|
||||
{
|
||||
var position = holder.Owner.Transform.LocalPosition;
|
||||
if (!to.Contents.Insert(holder.Owner))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
holder.Owner.Transform.LocalPosition = position;
|
||||
|
||||
Contents.Remove(holder.Owner);
|
||||
holder.EnterTube(to);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Make disposal pipes extend the grid
|
||||
private void Connect()
|
||||
{
|
||||
if (_connected || _broken)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_connected = true;
|
||||
}
|
||||
|
||||
public bool CanConnect(Direction direction, IDisposalTubeComponent with)
|
||||
{
|
||||
if (!_connected)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_broken)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ConnectableDirections().Contains(direction))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Disconnect()
|
||||
{
|
||||
if (!_connected)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_connected = false;
|
||||
|
||||
foreach (var entity in Contents.ContainedEntities.ToArray())
|
||||
{
|
||||
if (!entity.TryGetComponent(out DisposalHolderComponent? holder))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
holder.ExitDisposals();
|
||||
}
|
||||
}
|
||||
|
||||
public void PopupDirections(IEntity entity)
|
||||
{
|
||||
var directions = string.Join(", ", ConnectableDirections());
|
||||
|
||||
Owner.PopupMessage(entity, Loc.GetString("{0}", directions));
|
||||
}
|
||||
|
||||
private void UpdateVisualState()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var state = _broken
|
||||
? DisposalTubeVisualState.Broken
|
||||
: Anchored
|
||||
? DisposalTubeVisualState.Anchored
|
||||
: DisposalTubeVisualState.Free;
|
||||
|
||||
appearance.SetData(DisposalTubeVisuals.VisualState, state);
|
||||
}
|
||||
|
||||
public void AnchoredChanged()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out PhysicsComponent? physics))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (physics.BodyType == BodyType.Static)
|
||||
{
|
||||
OnAnchor();
|
||||
}
|
||||
else
|
||||
{
|
||||
OnUnAnchor();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnAnchor()
|
||||
{
|
||||
Connect();
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
private void OnUnAnchor()
|
||||
{
|
||||
Disconnect();
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Contents = ContainerHelpers.EnsureContainer<Container>(Owner, Name);
|
||||
Owner.EnsureComponent<AnchorableComponent>();
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
Owner.EnsureComponent<PhysicsComponent>(out var physicsComponent);
|
||||
if (physicsComponent.BodyType != BodyType.Static)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Connect();
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
base.OnRemove();
|
||||
|
||||
Disconnect();
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case RelayMovementEntityMessage _:
|
||||
if (_gameTiming.CurTime < _lastClang + ClangDelay)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_lastClang = _gameTiming.CurTime;
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _clangSound, Owner.Transform.Coordinates);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void IBreakAct.OnBreak(BreakageEventArgs eventArgs)
|
||||
{
|
||||
_broken = true; // TODO: Repair
|
||||
Disconnect();
|
||||
UpdateVisualState();
|
||||
}
|
||||
|
||||
[Verb]
|
||||
private sealed class TubeDirectionsVerb : Verb<IDisposalTubeComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, IDisposalTubeComponent component, VerbData data)
|
||||
{
|
||||
data.Text = Loc.GetString("Tube Directions");
|
||||
data.CategoryData = VerbCategories.Debug;
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
|
||||
if (user.TryGetComponent<ActorComponent>(out var player))
|
||||
{
|
||||
if (groupController.CanCommand(player.PlayerSession, "tubeconnections"))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, IDisposalTubeComponent component)
|
||||
{
|
||||
var groupController = IoCManager.Resolve<IConGroupController>();
|
||||
|
||||
if (user.TryGetComponent<ActorComponent>(out var player))
|
||||
{
|
||||
if (groupController.CanCommand(player.PlayerSession, "tubeconnections"))
|
||||
{
|
||||
component.PopupDirections(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
#nullable enable
|
||||
using Content.Server.Disposal.Unit.Components;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Server.Disposal.Tube.Components
|
||||
{
|
||||
public interface IDisposalTubeComponent : IComponent
|
||||
{
|
||||
Container Contents { get; }
|
||||
|
||||
Direction NextDirection(DisposalHolderComponent holder);
|
||||
Vector2 ExitVector(DisposalHolderComponent holder);
|
||||
IDisposalTubeComponent? NextTube(DisposalHolderComponent holder);
|
||||
bool Remove(DisposalHolderComponent holder);
|
||||
bool TransferTo(DisposalHolderComponent holder, IDisposalTubeComponent to);
|
||||
bool CanConnect(Direction direction, IDisposalTubeComponent with);
|
||||
void PopupDirections(IEntity entity);
|
||||
}
|
||||
}
|
||||
30
Content.Server/Disposal/Tube/DisposalTubeSystem.cs
Normal file
30
Content.Server/Disposal/Tube/DisposalTubeSystem.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Content.Server.Disposal.Tube.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Disposal.Tube
|
||||
{
|
||||
public sealed class DisposalTubeSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DisposalTubeComponent, PhysicsBodyTypeChangedEvent>(BodyTypeChanged);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
UnsubscribeLocalEvent<DisposalTubeComponent, PhysicsBodyTypeChangedEvent>();
|
||||
}
|
||||
|
||||
private static void BodyTypeChanged(
|
||||
EntityUid uid,
|
||||
DisposalTubeComponent component,
|
||||
PhysicsBodyTypeChangedEvent args)
|
||||
{
|
||||
component.AnchoredChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user