Add the trash man (#1367)

* Add disposal.rsi

* Rename disposal resource to disposal.rsi and create basic components

* Add disposal nets

* Add pushing entities along the disposal network

* Add disposal unit

* Unregister disposable component

* Add flush and selfinsert verbs to disposal unit

* Add gradual disposals movement

* Fix being able to walk through space for a while after exiting disposals

* Multiply disposals speed by 10

And fix early returns when moving an entity

* Rename Disposable component to InDisposals

* Remove DisposalNet and add on anchor events

* Remove anchored events, moved to interfaces

* Code cleanup

* Fix adjacent tubes' connections when a tube connects

* Fix jittery movement in disposals

* Remove Logger.Debug call

* Move disposals updates to InDisposalsComponent

* Fix adjacent connection valid directions check

* Disposal tubes now throw you out where they are facing

* Add disposal unit exit cooldown

* Set different disposal pipe sprite state depending on anchored value

* Add recycler

* Add recycler animation

* Add bloody texture to the recycler when grinding a living being

* Add PowerDevice component to the disposal unit

* Made the Recycler center on the grid

* Add disposal junction

* Add picking a random direction if junction is entered from the output side

* Add disposal flush and clang sounds

Taken from VGStation

* Move disposal flush and clang sound file names to exposedata

* Add disposalsmap.yml to test with

* Add summaries to DisposalUnit fields

* Add sideDegrees yaml property to disposal junctions

* Fix outdated usings

* Add conveyor resources

* Update RobustToolbox

* More merge fixes

Add conveyor collision masks

* Add ConveyorComponent

* Fix crash when reentering a body

* Merge branch 'master' into disposals-1147

* Reduce recycler bounds, set hard to false, add summary and expose "safe" to yaml

* Move IAnchored and IUnAnchored to AnchorableComponent

* Update power components and remove old disposals map

* Remove redundant sprite layers

* Add tile pry command

* Fix tilepry command

* Fix DisposalJunctionComponent missing a component reference

* Add anchor by radius command

* Add Y-Junctions

* Add disposal bend

* Add unanchor command

* Change DisposalJunction prototypes to specify their angles

* Fix disposal units being hidden below the floor

* Removed IAnhored and IUnAnchored interfaces

* Replace CanBeNull annotation with nullable reference types

* Update showwires command

* Add recycler recycling items

* Added angle and speed properties to ConveyorComponent

* Fix conveyort textures

* Add animation to the disposal unit

* Fix anchor and unanchor commands sometimes not finding any entities

* Fix not reading flush_time from disposal unit prototype

* Fix merge conflict wrong using

* Fix disposal, recycling and conveyor texture paths

Delete diverters

* Update visualizer names

* Add DisposableComponent, change drag and drop to work with multiple components

Ignoreinsideblocker client side for drag and drops, like on the server
Add more comments

* Add conveyor belts properly moving entities on top

* Anchorr wires

* Change conveyor bounds to 0.49

* Anchor catwalks, airlocks, gravity generators, low walls, wires and windows

* Add starting/stopping conveyors

* Add reversed conveyors

* Add conveyor switches

* Move InDisposalsComponent code to DisposableComponent

* Add ExitVector method to tubes

* Fix not updating tube references when disconnecting one

* Replace IoCManager call with dependency

* Add tubes disconnecting if they move too far apart from one another

* Move disposals action blocking to shared

* Add rotating and flipping pipes

* Make conveyor intersection calculations approximate

* Fix 1% chance of the server crashing when initializing the map

Happens when emergency lockers remove themselves

* Add disposal unit interface

* Make disposal units refuse items if not powered

* Make disposal tubes hide only when anchored

* Make disposal junction arrows visible to mere mortals

* Add disposal tubes breaking

* Add tubeconnections command

* Add missing verb attribute

* Add flipped disposal junction

* Add ids and linking to conveyors and switches

* Add conveyor switch prying and placing

* Add anchoring conveyor switches and refactor placing them

* Add missing serializable attributes from DisposableComponentState

* Make conveyor speed VV ReadWrite

* Change drawdepth of conveyors to FloorObjects

* Make conveyor anchored check consistent

* Remove anchoring interaction from switches

* Add conveyor switch id syncing and move switches slightly when pried

* Make entities in containers not able to be moved by conveyors

* Add conveyor and switches loose textures

* Merge conflict fixes

* Add disposal unit test

* Add flushing test to disposal unit test

* Add disposal unit flush fail test

* Add disposals to the saltern map

* Fix saltern disposal junctions

* Add power checks to the recycler

* Fix disposal unit placement in maintenance closet

* Remove disposal junctions from saltern

* Readd junctions to saltern

* Add the chemmaster to saltern at the request of Ike

* Move the chemistry disposal unit

* Fix casing of disposal flush sound

* More merge conflict fixes

* Fix a compiler warning.

* Remove popup invocation from buckle

* Remove showPopup parameter from InteractionChecks

* Remove unnecessary physics components

Fixes the physics system dying

* Replace PhysicsComponent usages with CollidableComponent

* Update existing code for the new controller system

* Change conveyors to use a VirtualController instead of teleporting the entity

* Remove visualizer 2d suffix and update physics code

* Transition code to new controller system

* Fix shuttles not moving

* Fix throwing

* Fix guns

* Change hands to use physics.Stop() and remove item fumble method

* Add syncing conveyor switches states

* Fix the recycler wanting to be a conveyor too hard

* Fix showwires > showsubfloor rename in mapping command

* Fix wifi air conveyors

* Fix test error

* Add showsubfloorforever command

Changes drawdepth of the relevant entities

* Disable opening the disposal unit interface while inside

* Add closing the disposal unit interface when getting inside

* Add closing the interface when the disposal unit component is removed

* Add removing entities on disposal unit component removal

* Delay disposal unit flush and fix serialization

* Implement pressure in disposal units

* Fix chain engaging a disposal unit

* Implement states to the disposal unit

* Fix missing imports from merge conflict

* Update Content.Server/GameObjects/Components/Conveyor/ConveyorComponent.cs

Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>

* Address some reviews

* Fix za buildo

* Use container helper to detach disposables

* Make conveyors use the construction system

* Make conveyor groups and syncing sane

* Make flip flip

brave

* Add activate interface to conveyor switches

* Fix not removing the switch from its group when it's deleted

* Fix not registering conveyors and switches on initialize

* Stop using 0 as null

* Disconnect conveyors and switches when disposing of a group

* Make disposal units not able to be exited when flushing

* Make disposal units flush after a configurable 30 seconds

* Add handle and light layers to the disposal unit

* Merge engaging and flushing

* Update saltern.yml

* I love using 0 as null

* Make disposal unit visual layers make sense

* Remove duplicate remove method in disposal units and update light

* Replace DisposableComponent with disposal holders

* Fix disposal holders deleting their contents on deletion

* Account for disposal unit pressure in tests and make a failed flush autoengage

* Rename disposable to holder

* Fix junction connections

* Disable self insert and flush verbs when inside a disposal unit

* Fix spamming the engage button making the animation reset

* Make the recycler take materials into account properly

Fix cablestack1 not existing

* Merge conflict fixes

* Fix pipes not being saved anchored

* Change conveyors and groups to not use an id

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers@gmail.com>
This commit is contained in:
DrSmugleaf
2020-07-30 23:45:28 +02:00
committed by GitHub
parent 2ba86c6476
commit bda5ce655f
140 changed files with 19810 additions and 12029 deletions

View File

@@ -1,8 +1,11 @@
using Content.Server.GameObjects.Components.Interactable;
#nullable enable
using System.Diagnostics.CodeAnalysis;
using Content.Server.GameObjects.Components.Interactable;
using Content.Shared.GameObjects.Components.Interactable;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.GameObjects.Components
{
@@ -11,24 +14,100 @@ namespace Content.Server.GameObjects.Components
{
public override string Name => "Anchorable";
/// <summary>
/// Checks if a tool can change the anchored status.
/// </summary>
/// <param name="user">The user doing the action</param>
/// <param name="utilizing">The tool being used, can be null if forcing it</param>
/// <param name="collidable">The physics component of the owning entity</param>
/// <param name="force">Whether or not to check if the tool is valid</param>
/// <returns>true if it is valid, false otherwise</returns>
private bool Valid(IEntity user, IEntity? utilizing, [MaybeNullWhen(false)] out ICollidableComponent collidable, bool force = false)
{
if (!Owner.TryGetComponent(out collidable))
{
return false;
}
if (!force)
{
if (utilizing == null ||
!utilizing.TryGetComponent(out ToolComponent tool) ||
!tool.UseTool(user, Owner, ToolQuality.Anchoring))
{
return false;
}
}
return true;
}
/// <summary>
/// Tries to anchor the owner of this component.
/// </summary>
/// <param name="user">The entity doing the anchoring</param>
/// <param name="utilizing">The tool being used, if any</param>
/// <param name="force">Whether or not to ignore valid tool checks</param>
/// <returns>true if anchored, false otherwise</returns>
public bool TryAnchor(IEntity user, IEntity? utilizing = null, bool force = false)
{
if (!Valid(user, utilizing, out var physics, force))
{
return false;
}
physics.Anchored = true;
return true;
}
/// <summary>
/// Tries to unanchor the owner of this component.
/// </summary>
/// <param name="user">The entity doing the unanchoring</param>
/// <param name="utilizing">The tool being used, if any</param>
/// <param name="force">Whether or not to ignore valid tool checks</param>
/// <returns>true if unanchored, false otherwise</returns>
public bool TryUnAnchor(IEntity user, IEntity? utilizing = null, bool force = false)
{
if (!Valid(user, utilizing, out var physics, force))
{
return false;
}
physics.Anchored = false;
return true;
}
/// <summary>
/// Tries to toggle the anchored status of this component's owner.
/// </summary>
/// <param name="user">The entity doing the unanchoring</param>
/// <param name="utilizing">The tool being used, if any</param>
/// <param name="force">Whether or not to ignore valid tool checks</param>
/// <returns>true if toggled, false otherwise</returns>
private bool TryToggleAnchor(IEntity user, IEntity? utilizing = null, bool force = false)
{
if (!Owner.TryGetComponent(out ICollidableComponent collidable))
{
return false;
}
return collidable.Anchored ?
TryUnAnchor(user, utilizing, force) :
TryAnchor(user, utilizing, force);
}
public override void Initialize()
{
base.Initialize();
Owner.EnsureComponent<PhysicsComponent>();
}
public bool InteractUsing(InteractUsingEventArgs eventArgs)
bool IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
{
if (!Owner.TryGetComponent(out IPhysicsComponent physics)
|| !eventArgs.Using.TryGetComponent(out ToolComponent tool))
return false;
if (!tool.UseTool(eventArgs.User, Owner, ToolQuality.Anchoring))
return false;
physics.Anchored = !physics.Anchored;
return true;
return TryToggleAnchor(eventArgs.User, eventArgs.Using);
}
}
}

View File

@@ -1,5 +1,6 @@
#nullable enable
using System;
using System.Diagnostics.CodeAnalysis;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.Components.Strap;
@@ -159,19 +160,10 @@ namespace Content.Server.GameObjects.Components.Buckle
}
}
/// <summary>
/// Tries to make an entity buckle the owner of this component to another.
/// </summary>
/// <param name="user">
/// The entity buckling the owner of this component, can be the owner itself.
/// </param>
/// <param name="to">The entity to buckle the owner of this component to.</param>
/// <returns>
/// true if the owner was buckled, otherwise false even if the owner was
/// previously already buckled.
/// </returns>
public bool TryBuckle(IEntity user, IEntity to)
private bool CanBuckle(IEntity user, IEntity to, [MaybeNullWhen(false)] out StrapComponent strap)
{
strap = null;
if (user == null || user == to)
{
return false;
@@ -181,22 +173,25 @@ namespace Content.Server.GameObjects.Components.Buckle
{
_notifyManager.PopupMessage(user, user,
Loc.GetString("You can't do that!"));
return false;
}
if (!to.TryGetComponent(out StrapComponent strap))
if (!to.TryGetComponent(out strap))
{
_notifyManager.PopupMessage(Owner, user,
Loc.GetString(Owner == user
? "You can't buckle yourself there!"
: "You can't buckle {0:them} there!", Owner));
return false;
}
var ownerPosition = Owner.Transform.MapPosition;
var strapPosition = strap.Owner.Transform.MapPosition;
var interaction = EntitySystem.Get<SharedInteractionSystem>();
bool Ignored(IEntity entity) => entity == Owner || entity == user || entity == strap.Owner;
var component = strap;
bool Ignored(IEntity entity) => entity == Owner || entity == user || entity == component.Owner;
if (!interaction.InRangeUnobstructed(ownerPosition, strapPosition, _range, predicate: Ignored))
{
@@ -213,8 +208,8 @@ namespace Content.Server.GameObjects.Components.Buckle
if (!ContainerHelpers.TryGetContainer(strap.Owner, out var strapContainer) ||
ownerContainer != strapContainer)
{
_notifyManager.PopupMessage(strap.Owner, user,
Loc.GetString("You can't reach there!"));
_notifyManager.PopupMessage(strap.Owner, user, Loc.GetString("You can't reach there!"));
return false;
}
}
@@ -223,6 +218,7 @@ namespace Content.Server.GameObjects.Components.Buckle
{
_notifyManager.PopupMessage(user, user,
Loc.GetString("You don't have hands!"));
return false;
}
@@ -232,6 +228,7 @@ namespace Content.Server.GameObjects.Components.Buckle
Loc.GetString(Owner == user
? "You are already buckled in!"
: "{0:They} are already buckled in!", Owner));
return false;
}
@@ -244,6 +241,7 @@ namespace Content.Server.GameObjects.Components.Buckle
Loc.GetString(Owner == user
? "You can't buckle yourself there!"
: "You can't buckle {0:them} there!", Owner));
return false;
}
@@ -256,6 +254,28 @@ namespace Content.Server.GameObjects.Components.Buckle
Loc.GetString(Owner == user
? "You can't fit there!"
: "{0:They} can't fit there!", Owner));
return false;
}
return true;
}
/// <summary>
/// Tries to make an entity buckle the owner of this component to another.
/// </summary>
/// <param name="user">
/// The entity buckling the owner of this component, can be the owner itself.
/// </param>
/// <param name="to">The entity to buckle the owner of this component to.</param>
/// <returns>
/// true if the owner was buckled, otherwise false even if the owner was
/// previously already buckled.
/// </returns>
public bool TryBuckle(IEntity user, IEntity to)
{
if (!CanBuckle(user, to, out var strap))
{
return false;
}
@@ -545,6 +565,11 @@ namespace Content.Server.GameObjects.Components.Buckle
return TryUnbuckle(eventArgs.User);
}
bool IDragDrop.CanDragDrop(DragDropEventArgs eventArgs)
{
return eventArgs.Target.HasComponent<StrapComponent>();
}
bool IDragDrop.DragDrop(DragDropEventArgs eventArgs)
{
return TryBuckle(eventArgs.User, eventArgs.Target);

View File

@@ -0,0 +1,257 @@
#nullable enable
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Interactable;
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Components.Conveyor;
using Content.Shared.GameObjects.Components.Interactable;
using Content.Shared.Interfaces.GameObjects.Components;
using Content.Shared.Physics;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Components.Map;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Conveyor
{
[RegisterComponent]
public class ConveyorComponent : Component, IInteractUsing
{
#pragma warning disable 649
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
#pragma warning restore 649
public override string Name => "Conveyor";
/// <summary>
/// The angle to move entities by in relation to the owner's rotation.
/// </summary>
[ViewVariables]
private Angle _angle;
/// <summary>
/// The amount of units to move the entity by per second.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
private float _speed;
private ConveyorState _state;
/// <summary>
/// The current state of this conveyor
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
private ConveyorState State
{
get => _state;
set
{
_state = value;
if (!Owner.TryGetComponent(out AppearanceComponent appearance))
{
return;
}
appearance.SetData(ConveyorVisuals.State, value);
}
}
private ConveyorGroup? _group = new ConveyorGroup();
/// <summary>
/// Calculates the angle in which entities on top of this conveyor
/// belt are pushed in
/// </summary>
/// <returns>
/// The angle when taking into account if the conveyor is reversed
/// </returns>
private Angle GetAngle()
{
var adjustment = _state == ConveyorState.Reversed ? MathHelper.Pi : 0;
var radians = MathHelper.DegreesToRadians(_angle);
return new Angle(Owner.Transform.LocalRotation.Theta + radians + adjustment);
}
private bool CanRun()
{
if (State == ConveyorState.Off)
{
return false;
}
if (Owner.TryGetComponent(out PowerReceiverComponent receiver) &&
!receiver.Powered)
{
return false;
}
if (Owner.HasComponent<ItemComponent>())
{
return false;
}
return true;
}
private bool CanMove(IEntity entity)
{
if (entity == Owner)
{
return false;
}
if (!entity.TryGetComponent(out ICollidableComponent collidable) ||
collidable.Anchored)
{
return false;
}
if (entity.HasComponent<ConveyorComponent>())
{
return false;
}
if (entity.HasComponent<IMapGridComponent>())
{
return false;
}
if (ContainerHelpers.IsInContainer(entity))
{
return false;
}
return true;
}
public void Update()
{
if (!CanRun())
{
return;
}
var intersecting = _entityManager.GetEntitiesIntersecting(Owner, true);
var direction = GetAngle().ToVec();
foreach (var entity in intersecting)
{
if (!CanMove(entity))
{
continue;
}
if (entity.TryGetComponent(out ICollidableComponent collidable))
{
var controller = collidable.EnsureController<ConveyedController>();
controller.Move(direction, _speed);
}
}
}
private bool ToolUsed(IEntity user, ToolComponent tool)
{
if (!Owner.HasComponent<ItemComponent>() &&
tool.UseTool(user, Owner, ToolQuality.Prying))
{
State = ConveyorState.Loose;
Owner.AddComponent<ItemComponent>();
_group?.RemoveConveyor(this);
Owner.Transform.WorldPosition += (_random.NextFloat() * 0.4f - 0.2f, _random.NextFloat() * 0.4f - 0.2f);
return true;
}
return false;
}
public void Sync(ConveyorGroup group)
{
_group = group;
if (State == ConveyorState.Loose)
{
return;
}
State = group.State == ConveyorState.Loose
? ConveyorState.Off
: group.State;
}
/// <summary>
/// Disconnects this conveyor from any switch.
/// </summary>
private void Disconnect()
{
_group?.RemoveConveyor(this);
_group = null;
State = ConveyorState.Off;
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataReadWriteFunction(
"switches",
new List<IEntity>(),
switches =>
{
if (switches == null)
{
return;
}
foreach (var @switch in switches)
{
if (!@switch.TryGetComponent(out ConveyorSwitchComponent component))
{
continue;
}
component.Connect(this);
}
},
() => _group?.Switches.Select(@switch => @switch.Owner));
serializer.DataField(ref _angle, "angle", 0);
serializer.DataField(ref _speed, "speed", 2);
}
public override void OnRemove()
{
base.OnRemove();
Disconnect();
}
bool IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
{
if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent conveyorSwitch))
{
conveyorSwitch.Connect(this, eventArgs.User);
return true;
}
if (eventArgs.Using.TryGetComponent(out ToolComponent tool))
{
return ToolUsed(eventArgs.User, tool);
}
return false;
}
}
}

View File

@@ -0,0 +1,208 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.GameObjects.Components.Conveyor;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Conveyor
{
[RegisterComponent]
public class ConveyorSwitchComponent : Component, IInteractHand, IInteractUsing, IActivate
{
public override string Name => "ConveyorSwitch";
private ConveyorState _state;
/// <summary>
/// The current state of this switch
/// </summary>
[ViewVariables]
public ConveyorState State
{
get => _state;
private set
{
_state = value;
if (Owner.TryGetComponent(out AppearanceComponent appearance))
{
appearance.SetData(ConveyorVisuals.State, value);
}
}
}
private ConveyorGroup? _group;
public void Sync(ConveyorGroup group)
{
_group = group;
if (State == ConveyorState.Loose)
{
return;
}
State = group.State == ConveyorState.Loose
? ConveyorState.Off
: group.State;
}
/// <summary>
/// Disconnects this switch from any conveyors and other switches.
/// </summary>
private void Disconnect()
{
_group?.RemoveSwitch(this);
_group = null;
State = ConveyorState.Off;
}
/// <summary>
/// Connects a conveyor to this switch.
/// </summary>
/// <param name="conveyor">The conveyor to be connected.</param>
/// <param name="user">The user doing the connecting, if any.</param>
public void Connect(ConveyorComponent conveyor, IEntity? user = null)
{
if (_group == null)
{
_group = new ConveyorGroup();
_group.AddSwitch(this);
}
_group.AddConveyor(conveyor);
user?.PopupMessage(user, Loc.GetString("Conveyor linked."));
}
/// <summary>
/// Cycles this conveyor switch to its next valid state
/// </summary>
/// <returns>
/// true if the switch can be operated and the state could be cycled,
/// false otherwise
/// </returns>
private bool NextState()
{
State = State switch
{
ConveyorState.Off => ConveyorState.Forward,
ConveyorState.Forward => ConveyorState.Reversed,
ConveyorState.Reversed => ConveyorState.Off,
ConveyorState.Loose => ConveyorState.Off,
_ => throw new ArgumentOutOfRangeException()
};
_group?.SetState(this);
return true;
}
/// <summary>
/// Moves this switch to the group of another.
/// </summary>
/// <param name="other">The conveyor switch to synchronize with.</param>
/// <param name="user">The user doing the syncing, if any.</param>
private void SyncWith(ConveyorSwitchComponent other, IEntity? user = null)
{
other._group?.AddSwitch(this);
if (user == null)
{
return;
}
Owner.PopupMessage(user, Loc.GetString("Switches synchronized."));
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataReadWriteFunction(
"conveyors",
new List<IEntity>(),
conveyors =>
{
if (conveyors == null)
{
return;
}
foreach (var conveyor in conveyors)
{
if (!conveyor.TryGetComponent(out ConveyorComponent component))
{
continue;
}
Connect(component);
}
},
() => _group?.Conveyors.Select(conveyor => conveyor.Owner));
serializer.DataReadWriteFunction(
"switches",
new List<IEntity>(),
switches =>
{
if (switches == null)
{
return;
}
foreach (var @switch in switches)
{
if (!@switch.TryGetComponent(out ConveyorSwitchComponent component))
{
continue;
}
component.SyncWith(this);
}
},
() => _group?.Switches.Select(@switch => @switch.Owner));
}
public override void OnRemove()
{
base.OnRemove();
Disconnect();
}
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
{
return NextState();
}
bool IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
{
if (eventArgs.Using.TryGetComponent(out ConveyorComponent conveyor))
{
Connect(conveyor, eventArgs.User);
return true;
}
if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent otherSwitch))
{
SyncWith(otherSwitch, eventArgs.User);
return true;
}
return true;
}
void IActivate.Activate(ActivateEventArgs eventArgs)
{
NextState();
}
}
}

View File

@@ -0,0 +1,44 @@
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Disposal
{
[RegisterComponent]
[ComponentReference(typeof(IDisposalTubeComponent))]
public class DisposalBendComponent : DisposalTubeComponent
{
private int _sideDegrees;
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 || !Connected.ContainsValue(previousTube))
{
return directions[0];
}
var first = Connected.GetValueOrDefault(directions[0]);
return previousTube == first ? directions[1] : directions[0];
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _sideDegrees, "sideDegrees", -90);
}
}
}

View File

@@ -0,0 +1,54 @@
#nullable enable
using Content.Server.Interfaces;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
namespace Content.Server.GameObjects.Components.Disposal
{
public class TubeConnectionsCommand : IClientCommand
{
public string Command => "tubeconnections";
public string Description => Loc.GetString("Shows all the directions that a tube can connect in.");
public string Help => $"Usage: {Command} <entityUid>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
{
if (player?.AttachedEntity == null)
{
shell.SendText(player, Loc.GetString("Only players can use this command"));
return;
}
if (args.Length < 1)
{
shell.SendText(player, Help);
return;
}
if (!EntityUid.TryParse(args[0], out var id))
{
shell.SendText(player, Loc.GetString("{0} isn't a valid entity uid", args[0]));
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
if (!entityManager.TryGetEntity(id, out var entity))
{
shell.SendText(player, Loc.GetString("No entity exists with uid {0}", id));
return;
}
if (!entity.TryGetComponent(out IDisposalTubeComponent tube))
{
shell.SendText(player, Loc.GetString("Entity with uid {0} doesn't have a {1} component", id, nameof(IDisposalTubeComponent)));
return;
}
tube.PopupDirections(player.AttachedEntity);
}
}
}

View File

@@ -0,0 +1,51 @@
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
namespace Content.Server.GameObjects.Components.Disposal
{
[RegisterComponent]
[ComponentReference(typeof(IDisposalTubeComponent))]
public class DisposalEntryComponent : DisposalTubeComponent
{
private const string HolderPrototypeId = "DisposalHolder";
public override string Name => "DisposalEntry";
public bool TryInsert(IReadOnlyCollection<IEntity> entities)
{
var holder = Owner.EntityManager.SpawnEntity(HolderPrototypeId, Owner.Transform.MapPosition);
var holderComponent = holder.GetComponent<DisposalHolderComponent>();
foreach (var entity in entities)
{
holderComponent.TryInsert(entity);
}
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()};
}
public override Direction NextDirection(DisposalHolderComponent holder)
{
return ConnectableDirections()[0];
}
}
}

View File

@@ -0,0 +1,150 @@
#nullable enable
using System.Linq;
using Robust.Server.GameObjects.Components.Container;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Disposal
{
// TODO: Add gas
[RegisterComponent]
public class DisposalHolderComponent : Component
{
public override string Name => "DisposalHolder";
private Container _contents = null!;
/// <summary>
/// The total amount of time that it will take for this entity to
/// be pushed to the next tube
/// </summary>
[ViewVariables]
private float StartingTime { get; set; }
/// <summary>
/// Time left until the entity is pushed to the next tube
/// </summary>
[ViewVariables]
private float TimeLeft { get; set; }
[ViewVariables]
public IDisposalTubeComponent? PreviousTube { get; set; }
[ViewVariables]
public IDisposalTubeComponent? CurrentTube { get; private set; }
[ViewVariables]
public IDisposalTubeComponent? NextTube { get; set; }
private bool CanInsert(IEntity entity)
{
if (!_contents.CanInsert(entity))
{
return false;
}
return entity.HasComponent<ItemComponent>() ||
entity.HasComponent<SpeciesComponent>();
}
public bool TryInsert(IEntity entity)
{
if (!CanInsert(entity) || !_contents.Insert(entity))
{
return false;
}
return true;
}
public void EnterTube(IDisposalTubeComponent tube)
{
if (CurrentTube != null)
{
PreviousTube = CurrentTube;
}
Owner.Transform.GridPosition = tube.Owner.Transform.GridPosition;
CurrentTube = tube;
NextTube = tube.NextTube(this);
StartingTime = 0.1f;
TimeLeft = 0.1f;
}
public void ExitDisposals()
{
PreviousTube = null;
CurrentTube = null;
NextTube = null;
StartingTime = 0;
TimeLeft = 0;
foreach (var entity in _contents.ContainedEntities.ToArray())
{
_contents.ForceRemove(entity);
if (entity.Transform.Parent == Owner.Transform)
{
ContainerHelpers.AttachParentToContainerOrGrid(entity.Transform);
}
}
Owner.Delete();
}
public void Update(float frameTime)
{
while (frameTime > 0)
{
var time = frameTime;
if (time > TimeLeft)
{
time = TimeLeft;
}
TimeLeft -= time;
frameTime -= time;
if (CurrentTube == null)
{
ExitDisposals();
break;
}
if (TimeLeft > 0)
{
var progress = 1 - TimeLeft / StartingTime;
var origin = CurrentTube.Owner.Transform.WorldPosition;
var destination = CurrentTube.NextDirection(this).ToVec();
var newPosition = destination * progress;
Owner.Transform.WorldPosition = origin + newPosition;
continue;
}
if (NextTube == null || !CurrentTube.TransferTo(this, NextTube))
{
CurrentTube.Remove(this);
break;
}
}
}
public override void OnRemove()
{
base.OnRemove();
ExitDisposals();
}
public override void Initialize()
{
base.Initialize();
_contents = ContainerManagerComponent.Ensure<Container>(nameof(DisposalHolderComponent), Owner);
}
}
}

View File

@@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Disposal
{
[RegisterComponent]
[ComponentReference(typeof(IDisposalTubeComponent))]
public class DisposalJunctionComponent : DisposalTubeComponent
{
#pragma warning disable 649
[Dependency] private readonly IRobustRandom _random;
#pragma warning restore 649
/// <summary>
/// The angles to connect to.
/// </summary>
[ViewVariables]
private List<Angle> _degrees;
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;
var directions = ConnectableDirections().Skip(1).ToArray();
if (Connected.TryGetValue(next.GetDir(), out var forwardTube) &&
holder.PreviousTube == forwardTube)
{
return _random.Pick(directions);
}
return next.GetDir();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _degrees, "degrees", null);
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Content.Server.GameObjects.Components.Disposal
{
// 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 || !Connected.ContainsValue(previousTube))
{
return forward;
}
var forwardTube = Connected.GetValueOrDefault(forward);
var backward = directions[1];
return previousTube == forwardTube ? backward : forward;
}
}
}

View File

@@ -0,0 +1,380 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Disposal;
using Content.Shared.Interfaces;
using Robust.Server.Console;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Disposal
{
// TODO: Make unanchored pipes pullable
public abstract class DisposalTubeComponent : Component, IDisposalTubeComponent, IBreakAct
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
private static readonly TimeSpan ClangDelay = TimeSpan.FromSeconds(0.5);
private TimeSpan _lastClang;
private bool _connected;
private bool _broken;
private string _clangSound;
/// <summary>
/// Container of entities that are currently inside this tube
/// </summary>
[ViewVariables]
public Container Contents { get; private set; }
/// <summary>
/// Dictionary of tubes connecting to this one mapped by their direction
/// </summary>
[ViewVariables]
protected Dictionary<Direction, IDisposalTubeComponent> Connected { get; } =
new Dictionary<Direction, IDisposalTubeComponent>();
[ViewVariables]
private bool Anchored =>
!Owner.TryGetComponent(out CollidableComponent collidable) ||
collidable.Anchored;
/// <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();
}
public IDisposalTubeComponent NextTube(DisposalHolderComponent holder)
{
var nextDirection = NextDirection(holder);
return Connected.GetValueOrDefault(nextDirection);
}
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;
var snapGrid = Owner.GetComponent<SnapGridComponent>();
foreach (var direction in ConnectableDirections())
{
var tube = snapGrid
.GetInDir(direction)
.Select(x => x.TryGetComponent(out IDisposalTubeComponent c) ? c : null)
.FirstOrDefault(x => x != null && x != this);
if (tube == null)
{
continue;
}
var oppositeDirection = new Angle(direction.ToAngle().Theta + Math.PI).GetDir();
if (!tube.AdjacentConnected(oppositeDirection, this))
{
continue;
}
Connected.Add(direction, tube);
}
}
public bool AdjacentConnected(Direction direction, IDisposalTubeComponent tube)
{
if (_broken)
{
return false;
}
if (Connected.ContainsKey(direction) ||
!ConnectableDirections().Contains(direction))
{
return false;
}
Connected.Add(direction, tube);
return true;
}
private void Disconnect()
{
if (!_connected)
{
return;
}
_connected = false;
foreach (var entity in Contents.ContainedEntities)
{
if (!entity.TryGetComponent(out DisposalHolderComponent holder))
{
continue;
}
holder.ExitDisposals();
}
foreach (var connected in Connected.Values)
{
connected.AdjacentDisconnected(this);
}
Connected.Clear();
}
public void AdjacentDisconnected(IDisposalTubeComponent adjacent)
{
foreach (var pair in Connected)
{
foreach (var entity in Contents.ContainedEntities)
{
if (!entity.TryGetComponent(out DisposalHolderComponent holder))
{
continue;
}
if (holder.PreviousTube == adjacent)
{
holder.PreviousTube = null;
}
if (holder.NextTube == adjacent)
{
holder.NextTube = null;
}
}
if (pair.Value == adjacent)
{
Connected.Remove(pair.Key);
}
}
}
public void MoveEvent(MoveEvent moveEvent)
{
if (!_connected)
{
return;
}
foreach (var tube in Connected.Values)
{
var distance = (tube.Owner.Transform.WorldPosition - Owner.Transform.WorldPosition).Length;
// Disconnect distance threshold
if (distance < 1.25)
{
continue;
}
AdjacentDisconnected(tube);
tube.AdjacentDisconnected(this);
}
}
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);
}
private void AnchoredChanged()
{
if (!Owner.TryGetComponent(out CollidableComponent collidable))
{
return;
}
if (collidable.Anchored)
{
OnAnchor();
}
else
{
OnUnAnchor();
}
}
private void OnAnchor()
{
Connect();
UpdateVisualState();
}
private void OnUnAnchor()
{
Disconnect();
UpdateVisualState();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _clangSound, "clangSound", "/Audio/effects/clang.ogg");
}
public override void Initialize()
{
base.Initialize();
Contents = ContainerManagerComponent.Ensure<Container>(Name, Owner);
Owner.EnsureComponent<AnchorableComponent>();
var collidable = Owner.EnsureComponent<CollidableComponent>();
collidable.AnchoredChanged += AnchoredChanged;
}
protected override void Startup()
{
base.Startup();
if (!Owner.GetComponent<CollidableComponent>().Anchored)
{
return;
}
Connect();
UpdateVisualState();
}
public override void OnRemove()
{
base.OnRemove();
var collidable = Owner.EnsureComponent<CollidableComponent>();
collidable.AnchoredChanged -= AnchoredChanged;
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;
EntitySystem.Get<AudioSystem>().PlayAtCoords(_clangSound, Owner.Transform.GridPosition);
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 = "Tube Directions";
data.CategoryData = VerbCategories.Debug;
data.Visibility = VerbVisibility.Invisible;
var groupController = IoCManager.Resolve<IConGroupController>();
if (user.TryGetComponent<IActorComponent>(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<IActorComponent>(out var player))
{
if (groupController.CanCommand(player.playerSession, "tubeconnections"))
{
component.PopupDirections(user);
}
}
}
}
}
}

View File

@@ -0,0 +1,563 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Disposal;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
using Timer = Robust.Shared.Timers.Timer;
namespace Content.Server.GameObjects.Components.Disposal
{
[RegisterComponent]
[ComponentReference(typeof(IInteractUsing))]
public class DisposalUnitComponent : SharedDisposalUnitComponent, IInteractHand, IInteractUsing, IDragDropOn
{
#pragma warning disable 649
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
#pragma warning restore 649
public override string Name => "DisposalUnit";
/// <summary>
/// The delay for an entity trying to move out of this unit.
/// </summary>
private static readonly TimeSpan ExitAttemptDelay = TimeSpan.FromSeconds(0.5);
/// <summary>
/// Last time that an entity tried to exit this disposal unit.
/// </summary>
private TimeSpan _lastExitAttempt;
/// <summary>
/// The current pressure of this disposal unit.
/// Prevents it from flushing if it is not equal to or bigger than 1.
/// </summary>
private float _pressure;
private bool _engaged;
[ViewVariables]
private TimeSpan _engageTime;
[ViewVariables]
private TimeSpan _automaticEngageTime;
/// <summary>
/// Token used to cancel the automatic engage of a disposal unit
/// after an entity enters it.
/// </summary>
private CancellationTokenSource? _automaticEngageToken;
/// <summary>
/// Container of entities inside this disposal unit.
/// </summary>
[ViewVariables]
private Container _container = default!;
[ViewVariables] public IReadOnlyList<IEntity> ContainedEntities => _container.ContainedEntities;
[ViewVariables]
private BoundUserInterface _userInterface = default!;
[ViewVariables]
public bool Powered =>
!Owner.TryGetComponent(out PowerReceiverComponent receiver) ||
receiver.Powered;
[ViewVariables]
public bool Anchored =>
!Owner.TryGetComponent(out CollidableComponent collidable) ||
collidable.Anchored;
[ViewVariables]
private State State => _pressure >= 1 ? State.Ready : State.Pressurizing;
[ViewVariables]
private bool Engaged
{
get => _engaged;
set
{
var oldEngaged = _engaged;
_engaged = value;
if (oldEngaged == value)
{
return;
}
UpdateVisualState();
}
}
public bool CanInsert(IEntity entity)
{
if (!Powered || !Anchored)
{
return false;
}
if (!entity.HasComponent<ItemComponent>() &&
!entity.HasComponent<SpeciesComponent>())
{
return false;
}
return _container.CanInsert(entity);
}
private void AfterInsert(IEntity entity)
{
_automaticEngageToken = new CancellationTokenSource();
Timer.Spawn(_automaticEngageTime, () => TryFlush(), _automaticEngageToken.Token);
if (entity.TryGetComponent(out IActorComponent actor))
{
_userInterface.Close(actor.playerSession);
}
UpdateVisualState();
}
public bool TryInsert(IEntity entity)
{
if (!CanInsert(entity) || !_container.Insert(entity))
{
return false;
}
AfterInsert(entity);
return true;
}
private bool TryDrop(IEntity user, IEntity entity)
{
if (!user.TryGetComponent(out HandsComponent hands))
{
return false;
}
if (!CanInsert(entity) || !hands.Drop(entity, _container))
{
return false;
}
AfterInsert(entity);
return true;
}
private void Remove(IEntity entity)
{
_container.Remove(entity);
if (ContainedEntities.Count == 0)
{
_automaticEngageToken?.Cancel();
_automaticEngageToken = null;
}
UpdateVisualState();
}
private bool CanFlush()
{
return _pressure >= 1 && Powered && Anchored;
}
public bool TryFlush()
{
if (!CanFlush())
{
Engaged = true;
return false;
}
var snapGrid = Owner.GetComponent<SnapGridComponent>();
var entry = snapGrid
.GetLocal()
.FirstOrDefault(entity => entity.HasComponent<DisposalEntryComponent>());
if (entry == null)
{
return false;
}
var entryComponent = entry.GetComponent<DisposalEntryComponent>();
var entities = _container.ContainedEntities.ToList();
foreach (var entity in _container.ContainedEntities.ToList())
{
_container.Remove(entity);
}
entryComponent.TryInsert(entities);
_automaticEngageToken?.Cancel();
_automaticEngageToken = null;
_pressure = 0;
Engaged = false;
UpdateVisualState(true);
UpdateInterface();
return true;
}
private void TryEjectContents()
{
foreach (var entity in _container.ContainedEntities.ToArray())
{
Remove(entity);
}
}
private void TogglePower()
{
if (!Owner.TryGetComponent(out PowerReceiverComponent receiver))
{
return;
}
receiver.PowerDisabled = !receiver.PowerDisabled;
UpdateInterface();
}
private DisposalUnitBoundUserInterfaceState GetInterfaceState()
{
return new DisposalUnitBoundUserInterfaceState(Owner.Name, Loc.GetString($"{State}"), _pressure, Powered);
}
private void UpdateInterface()
{
var state = GetInterfaceState();
_userInterface.SetState(state);
}
private bool PlayerCanUse(IEntity player)
{
if (player == null)
{
return false;
}
if (!ActionBlockerSystem.CanInteract(player) ||
!ActionBlockerSystem.CanUse(player))
{
return false;
}
return true;
}
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
{
if (obj.Session.AttachedEntity == null)
{
return;
}
if (!PlayerCanUse(obj.Session.AttachedEntity))
{
return;
}
if (!(obj.Message is UiButtonPressedMessage message))
{
return;
}
switch (message.Button)
{
case UiButton.Eject:
TryEjectContents();
break;
case UiButton.Engage:
TryFlush();
break;
case UiButton.Power:
TogglePower();
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f));
break;
default:
throw new ArgumentOutOfRangeException();
}
}
private void UpdateVisualState()
{
UpdateVisualState(false);
}
private void UpdateVisualState(bool flush)
{
if (!Owner.TryGetComponent(out AppearanceComponent appearance))
{
return;
}
appearance.SetData(Visuals.Handle, Engaged
? HandleState.Engaged
: HandleState.Normal);
if (!Anchored)
{
appearance.SetData(Visuals.VisualState, VisualState.UnAnchored);
appearance.SetData(Visuals.Handle, HandleState.Normal);
appearance.SetData(Visuals.Light, LightState.Off);
return;
}
if (flush)
{
appearance.SetData(Visuals.VisualState, VisualState.Flushing);
appearance.SetData(Visuals.Light, LightState.Off);
}
else
{
appearance.SetData(Visuals.VisualState, VisualState.Anchored);
if (ContainedEntities.Count > 0)
{
appearance.SetData(Visuals.Light, LightState.Full);
return;
}
appearance.SetData(Visuals.Light, _pressure < 1
? LightState.Charging
: LightState.Ready);
}
}
public void Update(float frameTime)
{
if (!Powered)
{
return;
}
var oldPressure = _pressure;
_pressure = _pressure + frameTime > 1
? 1
: _pressure + 0.05f * frameTime;
if (oldPressure < 1 && _pressure >= 1)
{
UpdateVisualState();
if (Engaged)
{
TryFlush();
}
}
UpdateInterface();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataReadWriteFunction(
"pressure",
1.0f,
pressure => _pressure = pressure,
() => _pressure);
serializer.DataReadWriteFunction(
"engageTime",
2,
seconds => _engageTime = TimeSpan.FromSeconds(seconds),
() => (int) _engageTime.TotalSeconds);
serializer.DataReadWriteFunction(
"automaticEngageTime",
30,
seconds => _automaticEngageTime = TimeSpan.FromSeconds(seconds),
() => (int) _automaticEngageTime.TotalSeconds);
}
public override void Initialize()
{
base.Initialize();
_container = ContainerManagerComponent.Ensure<Container>(Name, Owner);
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>()
.GetBoundUserInterface(DisposalUnitUiKey.Key);
_userInterface.OnReceiveMessage += OnUiReceiveMessage;
UpdateInterface();
}
protected override void Startup()
{
base.Startup();
Owner.EnsureComponent<AnchorableComponent>();
var collidable = Owner.EnsureComponent<CollidableComponent>();
collidable.AnchoredChanged += UpdateVisualState;
UpdateVisualState();
}
public override void OnRemove()
{
foreach (var entity in _container.ContainedEntities.ToArray())
{
_container.ForceRemove(entity);
}
_userInterface.CloseAll();
_automaticEngageToken?.Cancel();
_automaticEngageToken = null;
_container = null!;
base.OnRemove();
}
public override void HandleMessage(ComponentMessage message, IComponent? component)
{
base.HandleMessage(message, component);
switch (message)
{
case RelayMovementEntityMessage msg:
var timing = IoCManager.Resolve<IGameTiming>();
if (Engaged ||
!msg.Entity.HasComponent<HandsComponent>() ||
timing.CurTime < _lastExitAttempt + ExitAttemptDelay)
{
break;
}
_lastExitAttempt = timing.CurTime;
Remove(msg.Entity);
break;
}
}
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
{
if (!ActionBlockerSystem.CanInteract(eventArgs.User))
{
_notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User,
Loc.GetString("You can't do that!"));
return false;
}
if (ContainerHelpers.IsInContainer(eventArgs.User))
{
_notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User,
Loc.GetString("You can't reach there!"));
return false;
}
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
{
return false;
}
if (!eventArgs.User.HasComponent<IHandsComponent>())
{
_notifyManager.PopupMessage(Owner.Transform.GridPosition, eventArgs.User,
Loc.GetString("You have no hands!"));
return false;
}
_userInterface.Open(actor.playerSession);
return true;
}
bool IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
{
return TryDrop(eventArgs.User, eventArgs.Using);
}
bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs)
{
return CanInsert(eventArgs.Dropped);
}
bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs)
{
return TryInsert(eventArgs.Dropped);
}
[Verb]
private sealed class SelfInsertVerb : Verb<DisposalUnitComponent>
{
protected override void GetData(IEntity user, DisposalUnitComponent component, VerbData data)
{
data.Visibility = VerbVisibility.Invisible;
if (!ActionBlockerSystem.CanInteract(user) ||
component.ContainedEntities.Contains(user))
{
return;
}
data.Visibility = VerbVisibility.Visible;
data.Text = Loc.GetString("Jump inside");
}
protected override void Activate(IEntity user, DisposalUnitComponent component)
{
component.TryInsert(user);
}
}
[Verb]
private sealed class FlushVerb : Verb<DisposalUnitComponent>
{
protected override void GetData(IEntity user, DisposalUnitComponent component, VerbData data)
{
data.Visibility = VerbVisibility.Invisible;
if (!ActionBlockerSystem.CanInteract(user) ||
component.ContainedEntities.Contains(user))
{
return;
}
data.Visibility = VerbVisibility.Visible;
data.Text = Loc.GetString("Flush");
}
protected override void Activate(IEntity user, DisposalUnitComponent component)
{
component.TryFlush();
}
}
}
}

View File

@@ -0,0 +1,23 @@
#nullable enable
using Robust.Server.GameObjects.Components.Container;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Maths;
namespace Content.Server.GameObjects.Components.Disposal
{
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 AdjacentConnected(Direction direction, IDisposalTubeComponent tube);
void AdjacentDisconnected(IDisposalTubeComponent adjacent);
void MoveEvent(MoveEvent moveEvent);
void PopupDirections(IEntity entity);
}
}

View File

@@ -0,0 +1,161 @@
#nullable enable
using System.Linq;
using Content.Shared.Maps;
using JetBrains.Annotations;
using Robust.Server.Interfaces.Console;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
namespace Content.Server.GameObjects.Components.Interactable
{
/// <summary>
/// <see cref="TilePryingComponent.TryPryTile"/>
/// </summary>
[UsedImplicitly]
class TilePryCommand : IClientCommand
{
public string Command => "tilepry";
public string Description => "Pries up all tiles in a radius around the user.";
public string Help => $"Usage: {Command} <radius>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
{
if (player?.AttachedEntity == null)
{
return;
}
if (args.Length != 1)
{
shell.SendText(player, Help);
return;
}
if (!int.TryParse(args[0], out var radius))
{
shell.SendText(player, $"{args[0]} isn't a valid integer.");
return;
}
if (radius < 0)
{
shell.SendText(player, "Radius must be positive.");
return;
}
var mapManager = IoCManager.Resolve<IMapManager>();
var playerGrid = player.AttachedEntity.Transform.GridID;
var mapGrid = mapManager.GetGrid(playerGrid);
var playerPosition = player.AttachedEntity.Transform.GridPosition;
var tileDefinitionManager = IoCManager.Resolve<ITileDefinitionManager>();
for (var i = -radius; i <= radius; i++)
{
for (var j = -radius; j <= radius; j++)
{
var tile = mapGrid.GetTileRef(playerPosition.Offset((i, j)));
var coordinates = mapGrid.GridTileToLocal(tile.GridIndices);
var tileDef = (ContentTileDefinition) tileDefinitionManager[tile.Tile.TypeId];
if (!tileDef.CanCrowbar) continue;
var underplating = tileDefinitionManager["underplating"];
mapGrid.SetTile(coordinates, new Tile(underplating.TileId));
}
}
}
}
[UsedImplicitly]
class AnchorCommand : IClientCommand
{
public string Command => "anchor";
public string Description => "Anchors all entities in a radius around the user";
public string Help => $"Usage: {Command} <radius>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
{
if (player?.AttachedEntity == null)
{
return;
}
if (args.Length != 1)
{
shell.SendText(player, Help);
return;
}
if (!int.TryParse(args[0], out var radius))
{
shell.SendText(player, $"{args[0]} isn't a valid integer.");
return;
}
if (radius < 0)
{
shell.SendText(player, "Radius must be positive.");
return;
}
var serverEntityManager = IoCManager.Resolve<IServerEntityManager>();
var entities = serverEntityManager.GetEntitiesInRange(player.AttachedEntity, radius).ToList();
foreach (var entity in entities)
{
if (entity.TryGetComponent(out AnchorableComponent anchorable))
{
anchorable.TryAnchor(player.AttachedEntity, force: true);
}
}
}
}
[UsedImplicitly]
class UnAnchorCommand : IClientCommand
{
public string Command => "unanchor";
public string Description => "Unanchors all anchorable entities in a radius around the user";
public string Help => $"Usage: {Command} <radius>";
public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args)
{
if (player?.AttachedEntity == null)
{
return;
}
if (args.Length != 1)
{
shell.SendText(player, Help);
return;
}
if (!int.TryParse(args[0], out var radius))
{
shell.SendText(player, $"{args[0]} isn't a valid integer.");
return;
}
if (radius < 0)
{
shell.SendText(player, "Radius must be positive.");
return;
}
var serverEntityManager = IoCManager.Resolve<IServerEntityManager>();
var entities = serverEntityManager.GetEntitiesInRange(player.AttachedEntity, radius).ToList();
foreach (var entity in entities)
{
if (entity.TryGetComponent(out AnchorableComponent anchorable))
{
anchorable.TryUnAnchor(player.AttachedEntity, force: true);
}
}
}
}
}

View File

@@ -1,7 +1,6 @@
using Content.Server.GameObjects.Components.GUI;
using Content.Server.GameObjects.Components.Items.Storage;
using Content.Server.Interfaces.GameObjects.Components.Interaction;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Interfaces.GameObjects.Components.Items;
using Content.Server.Throw;
using Content.Server.Utility;
@@ -17,8 +16,6 @@ using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components
@@ -86,12 +83,22 @@ namespace Content.Server.GameObjects.Components
public bool CanPickup(IEntity user)
{
if (!ActionBlockerSystem.CanPickup(user)) return false;
if (!ActionBlockerSystem.CanPickup(user))
{
return false;
}
if (user.Transform.MapID != Owner.Transform.MapID)
{
return false;
}
if (Owner.TryGetComponent(out PhysicsComponent physics) &&
physics.Anchored)
{
return false;
}
var userPos = user.Transform.MapPosition;
var itemPos = Owner.Transform.MapPosition;
return InteractionChecks.InRangeUnobstructed(user, itemPos, ignoredEnt: Owner, ignoreInsideBlocker:true);

View File

@@ -503,6 +503,12 @@ namespace Content.Server.GameObjects.Components.Items.Storage
}
}
bool IDragDrop.CanDragDrop(DragDropEventArgs eventArgs)
{
return eventArgs.Target.TryGetComponent(out PlaceableSurfaceComponent placeable) &&
placeable.IsPlaceable;
}
bool IDragDrop.DragDrop(DragDropEventArgs eventArgs)
{
if (!ActionBlockerSystem.CanInteract(eventArgs.User))
@@ -523,7 +529,8 @@ namespace Content.Server.GameObjects.Components.Items.Storage
return false;
}
foreach (var storedEntity in storedEntities)
// empty everything out
foreach (var storedEntity in StoredEntities.ToList())
{
if (Remove(storedEntity))
{

View File

@@ -0,0 +1,202 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.GameObjects.Components.Conveyor;
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Construction;
using Content.Shared.GameObjects.Components.Recycling;
using Content.Shared.Physics;
using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.GameObjects.Components.Map;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Recycling
{
// TODO: Add sound and safe beep
[RegisterComponent]
public class RecyclerComponent : Component, ICollideBehavior
{
#pragma warning disable 649
[Dependency] private readonly IEntityManager _entityManager = default!;
#pragma warning restore 649
public override string Name => "Recycler";
/// <summary>
/// Whether or not sentient beings will be recycled
/// </summary>
[ViewVariables]
private bool _safe;
/// <summary>
/// The percentage of material that will be recovered
/// </summary>
[ViewVariables]
private int _efficiency; // TODO
private bool Powered =>
!Owner.TryGetComponent(out PowerReceiverComponent receiver) ||
receiver.Powered;
private void Bloodstain()
{
if (Owner.TryGetComponent(out AppearanceComponent appearance))
{
appearance.SetData(RecyclerVisuals.Bloody, true);
}
}
private void Clean()
{
if (Owner.TryGetComponent(out AppearanceComponent appearance))
{
appearance.SetData(RecyclerVisuals.Bloody, false);
}
}
private bool CanGib(IEntity entity)
{
return entity.HasComponent<SpeciesComponent>() &&
!_safe &&
Powered;
}
private bool CanRecycle(IEntity entity, [MaybeNullWhen(false)] out ConstructionPrototype prototype)
{
prototype = null;
var constructionSystem = EntitySystem.Get<ConstructionSystem>();
var entityId = entity.MetaData.EntityPrototype?.ID;
if (entityId == null ||
!constructionSystem.CraftRecipes.TryGetValue(entityId, out prototype))
{
return false;
}
return Powered;
}
private void Recycle(IEntity entity)
{
// TODO: Prevent collision with recycled items
if (CanGib(entity))
{
entity.Delete(); // TODO: Gib
Bloodstain();
return;
}
if (!CanRecycle(entity, out var prototype))
{
return;
}
var constructionSystem = EntitySystem.Get<ConstructionSystem>();
var recyclerPosition = Owner.Transform.MapPosition;
foreach (var stage in prototype.Stages)
{
if (!(stage.Forward is ConstructionStepMaterial step))
{
continue;
}
constructionSystem.SpawnIngredient(recyclerPosition, step);
}
entity.Delete();
}
private bool CanRun()
{
if (Owner.TryGetComponent(out PowerReceiverComponent receiver) &&
!receiver.Powered)
{
return false;
}
if (Owner.HasComponent<ItemComponent>())
{
return false;
}
return true;
}
private bool CanMove(IEntity entity)
{
if (entity == Owner)
{
return false;
}
if (!entity.TryGetComponent(out ICollidableComponent collidable) ||
collidable.Anchored)
{
return false;
}
if (entity.HasComponent<ConveyorComponent>())
{
return false;
}
if (entity.HasComponent<IMapGridComponent>())
{
return false;
}
if (ContainerHelpers.IsInContainer(entity))
{
return false;
}
return true;
}
public void Update(float frameTime)
{
if (!CanRun())
{
return;
}
var intersecting = _entityManager.GetEntitiesIntersecting(Owner, true);
var direction = Vector2.UnitX;
foreach (var entity in intersecting)
{
if (!CanMove(entity))
{
continue;
}
if (entity.TryGetComponent(out ICollidableComponent collidable))
{
var controller = collidable.EnsureController<ConveyedController>();
controller.Move(direction, frameTime);
}
}
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _safe, "safe", true);
serializer.DataField(ref _efficiency, "efficiency", 25);
}
void ICollideBehavior.CollideWith(IEntity collidedWith)
{
Recycle(collidedWith);
}
}
}

View File

@@ -0,0 +1,70 @@
#nullable enable
using Content.Server.Interfaces;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Rotatable
{
[RegisterComponent]
public class FlippableComponent : Component
{
#pragma warning disable 649
[Dependency] private readonly IServerNotifyManager _notifyManager = default!;
#pragma warning restore 649
public override string Name => "Flippable";
private string? _entity;
private void TryFlip(IEntity user)
{
if (Owner.TryGetComponent(out ICollidableComponent collidable) &&
collidable.Anchored)
{
_notifyManager.PopupMessage(Owner.Transform.GridPosition, user, Loc.GetString("It's stuck."));
return;
}
if (_entity == null)
{
return;
}
Owner.EntityManager.SpawnEntity(_entity, Owner.Transform.GridPosition);
Owner.Delete();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _entity, "entity", Owner.Prototype?.ID);
}
[Verb]
private sealed class FlippableVerb : Verb<FlippableComponent>
{
protected override void GetData(IEntity user, FlippableComponent component, VerbData data)
{
if (!ActionBlockerSystem.CanInteract(user))
{
data.Visibility = VerbVisibility.Invisible;
return;
}
data.Text = Loc.GetString("Flip");
}
protected override void Activate(IEntity user, FlippableComponent component)
{
component.TryFlip(user);
}
}
}
}

View File

@@ -8,7 +8,7 @@ using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
namespace Content.Server.GameObjects.Components
namespace Content.Server.GameObjects.Components.Rotatable
{
[RegisterComponent]
public class RotatableComponent : Component

View File

@@ -76,13 +76,21 @@ namespace Content.Server.GameObjects.EntitySystems.Click
// trigger dragdrops on the dropped entity
foreach (var dragDrop in dropped.GetAllComponents<IDragDrop>())
{
if (dragDrop.DragDrop(interactionArgs)) return;
if (dragDrop.CanDragDrop(interactionArgs) &&
dragDrop.DragDrop(interactionArgs))
{
return;
}
}
// trigger dragdropons on the targeted entity
foreach (var dragDropOn in target.GetAllComponents<IDragDropOn>())
{
if (dragDropOn.DragDropOn(interactionArgs)) return;
if (dragDropOn.CanDragDropOn(interactionArgs) &&
dragDropOn.DragDropOn(interactionArgs))
{
return;
}
}
}

View File

@@ -41,6 +41,8 @@ namespace Content.Server.GameObjects.EntitySystems
private readonly Dictionary<string, ConstructionPrototype> _craftRecipes = new Dictionary<string, ConstructionPrototype>();
public IReadOnlyDictionary<string, ConstructionPrototype> CraftRecipes => _craftRecipes;
/// <inheritdoc />
public override void Initialize()
{
@@ -207,7 +209,7 @@ namespace Content.Server.GameObjects.EntitySystems
spriteComp.AddLayerWithSprite(prototype.Icon);
}
private void SpawnIngredient(MapCoordinates position, ConstructionStepMaterial lastStep)
public void SpawnIngredient(MapCoordinates position, ConstructionStepMaterial lastStep)
{
if(lastStep is null)
return;

View File

@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Conveyor;
using Content.Shared.GameObjects.Components.Conveyor;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class ConveyorSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(ConveyorComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
{
if (!entity.TryGetComponent(out ConveyorComponent conveyor))
{
continue;
}
conveyor.Update();
}
}
}
public class ConveyorGroup
{
private readonly HashSet<ConveyorComponent> _conveyors;
private readonly HashSet<ConveyorSwitchComponent> _switches;
public ConveyorGroup()
{
_conveyors = new HashSet<ConveyorComponent>(0);
_switches = new HashSet<ConveyorSwitchComponent>(0);
State = ConveyorState.Off;
}
public IReadOnlyCollection<ConveyorComponent> Conveyors => _conveyors;
public IReadOnlyCollection<ConveyorSwitchComponent> Switches => _switches;
public ConveyorState State { get; private set; }
public void AddConveyor(ConveyorComponent conveyor)
{
_conveyors.Add(conveyor);
conveyor.Sync(this);
}
public void RemoveConveyor(ConveyorComponent conveyor)
{
_conveyors.Remove(conveyor);
}
public void AddSwitch(ConveyorSwitchComponent conveyorSwitch)
{
_switches.Add(conveyorSwitch);
if (_switches.Count == 1)
{
SetState(conveyorSwitch);
}
conveyorSwitch.Sync(this);
}
public void RemoveSwitch(ConveyorSwitchComponent conveyorSwitch)
{
_switches.Remove(conveyorSwitch);
}
public void SetState(ConveyorSwitchComponent conveyorSwitch)
{
var state = conveyorSwitch.State;
if (state == ConveyorState.Loose)
{
if (_switches.Count > 0)
{
return;
}
state = ConveyorState.Off;
}
State = state;
foreach (var conveyor in Conveyors)
{
conveyor.Sync(this);
}
foreach (var connectedSwitch in _switches)
{
connectedSwitch.Sync(this);
}
}
}
}

View File

@@ -0,0 +1,26 @@
using Content.Server.GameObjects.Components.Disposal;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class DisposableSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(DisposalHolderComponent));
}
public override void Update(float frameTime)
{
foreach (var disposable in RelevantEntities)
{
disposable.GetComponent<DisposalHolderComponent>().Update(frameTime);
}
}
}
}

View File

@@ -0,0 +1,33 @@
using Content.Server.GameObjects.Components.Disposal;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class DisposalTubeSystem : EntitySystem
{
private void MoveEvent(MoveEvent moveEvent)
{
if (moveEvent.Sender.TryGetComponent(out IDisposalTubeComponent tube))
{
tube.MoveEvent(moveEvent);
}
}
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<MoveEvent>(MoveEvent);
}
public override void Shutdown()
{
base.Shutdown();
UnsubscribeLocalEvent<MoveEvent>();
}
}
}

View File

@@ -0,0 +1,26 @@
using Content.Server.GameObjects.Components.Disposal;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class DisposalUnitSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(DisposalUnitComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
{
entity.GetComponent<DisposalUnitComponent>().Update(frameTime);
}
}
}
}

View File

@@ -0,0 +1,26 @@
using Content.Server.GameObjects.Components.Recycling;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
namespace Content.Server.GameObjects.EntitySystems
{
[UsedImplicitly]
public class RecyclerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
EntityQuery = new TypeEntityQuery(typeof(RecyclerComponent));
}
public override void Update(float frameTime)
{
foreach (var entity in RelevantEntities)
{
entity.GetComponent<RecyclerComponent>().Update(frameTime);
}
}
}
}