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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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))
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
108
Content.Server/GameObjects/EntitySystems/ConveyorSystem.cs
Normal file
108
Content.Server/GameObjects/EntitySystems/ConveyorSystem.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Content.Server/GameObjects/EntitySystems/DisposableSystem.cs
Normal file
26
Content.Server/GameObjects/EntitySystems/DisposableSystem.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Content.Server/GameObjects/EntitySystems/RecyclerSystem.cs
Normal file
26
Content.Server/GameObjects/EntitySystems/RecyclerSystem.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user