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>
@@ -1,3 +1,4 @@
|
||||
using Content.Client.GameObjects.Components;
|
||||
using Content.Client.GameObjects.EntitySystems;
|
||||
using Content.Client.Interfaces;
|
||||
using Content.Shared.GameObjects.Components.Markers;
|
||||
@@ -8,6 +9,7 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using DrawDepth = Content.Shared.GameObjects.DrawDepth;
|
||||
|
||||
namespace Content.Client.Commands
|
||||
{
|
||||
@@ -27,12 +29,12 @@ namespace Content.Client.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShowWiresCommand : IConsoleCommand
|
||||
internal sealed class ShowSubFloor : IConsoleCommand
|
||||
{
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
public string Command => "showwires";
|
||||
public string Description => "Makes wires always visible.";
|
||||
public string Help => "";
|
||||
public string Command => "showsubfloor";
|
||||
public string Description => "Makes entities below the floor always visible.";
|
||||
public string Help => $"Usage: {Command}";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
{
|
||||
@@ -43,6 +45,32 @@ namespace Content.Client.Commands
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ShowSubFloorForever : IConsoleCommand
|
||||
{
|
||||
// ReSharper disable once StringLiteralTypo
|
||||
public string Command => "showsubfloorforever";
|
||||
public string Description => "Makes entities below the floor always visible until the client is restarted.";
|
||||
public string Help => $"Usage: {Command}";
|
||||
|
||||
public bool Execute(IDebugConsole console, params string[] args)
|
||||
{
|
||||
EntitySystem.Get<SubFloorHideSystem>()
|
||||
.EnableAll = true;
|
||||
|
||||
var components = IoCManager.Resolve<IEntityManager>().ComponentManager
|
||||
.EntityQuery<SubFloorHideComponent>();
|
||||
|
||||
foreach (var component in components)
|
||||
{
|
||||
if (component.Owner.TryGetComponent(out ISpriteComponent sprite))
|
||||
{
|
||||
sprite.DrawDepth = (int) DrawDepth.Overlays;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class NotifyCommand : IConsoleCommand
|
||||
{
|
||||
@@ -76,7 +104,7 @@ namespace Content.Client.Commands
|
||||
}
|
||||
|
||||
console.Commands["togglelight"].Execute(console);
|
||||
console.Commands["showwires"].Execute(console);
|
||||
console.Commands["showsubfloorforever"].Execute(console);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Conveyor;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Conveyor
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ConveyorSwitchVisualizer : AppearanceVisualizer
|
||||
{
|
||||
private string _stateForward;
|
||||
private string _stateOff;
|
||||
private string _stateReversed;
|
||||
private string _stateLoose;
|
||||
|
||||
private void ChangeState(AppearanceComponent appearance)
|
||||
{
|
||||
if (!appearance.Owner.TryGetComponent(out ISpriteComponent sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
appearance.TryGetData(ConveyorVisuals.State, out ConveyorState state);
|
||||
|
||||
var texture = state switch
|
||||
{
|
||||
ConveyorState.Off => _stateOff,
|
||||
ConveyorState.Forward => _stateForward,
|
||||
ConveyorState.Reversed => _stateReversed,
|
||||
ConveyorState.Loose => _stateLoose,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
sprite.LayerSetState(0, texture);
|
||||
}
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_stateForward = node.GetNode("state_forward").AsString();
|
||||
_stateOff = node.GetNode("state_off").AsString();
|
||||
_stateReversed = node.GetNode("state_reversed").AsString();
|
||||
_stateLoose = node.GetNode("state_loose").AsString();
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var appearance = entity.EnsureComponent<AppearanceComponent>();
|
||||
ChangeState(appearance);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.Owner.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeState(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Conveyor;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Conveyor
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class ConveyorVisualizer : AppearanceVisualizer
|
||||
{
|
||||
private string _stateRunning;
|
||||
private string _stateStopped;
|
||||
private string _stateReversed;
|
||||
private string _stateLoose;
|
||||
|
||||
private void ChangeState(AppearanceComponent appearance)
|
||||
{
|
||||
if (!appearance.Owner.TryGetComponent(out ISpriteComponent sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
appearance.TryGetData(ConveyorVisuals.State, out ConveyorState state);
|
||||
|
||||
var texture = state switch
|
||||
{
|
||||
ConveyorState.Off => _stateStopped,
|
||||
ConveyorState.Forward => _stateRunning,
|
||||
ConveyorState.Reversed => _stateReversed,
|
||||
ConveyorState.Loose => _stateLoose,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
sprite.LayerSetState(0, texture);
|
||||
}
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_stateRunning = node.GetNode("state_running").AsString();
|
||||
_stateStopped = node.GetNode("state_stopped").AsString();
|
||||
_stateReversed = node.GetNode("state_reversed").AsString();
|
||||
_stateLoose = node.GetNode("state_loose").AsString();
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var appearance = entity.EnsureComponent<AppearanceComponent>();
|
||||
ChangeState(appearance);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.Owner.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeState(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
#nullable enable
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Localization;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalUnitComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a <see cref="DisposalUnitWindow"/> and updates it when new server messages are received.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class DisposalUnitBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private DisposalUnitWindow? _window;
|
||||
|
||||
public DisposalUnitBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
}
|
||||
|
||||
private void ButtonPressed(UiButton button)
|
||||
{
|
||||
SendMessage(new UiButtonPressedMessage(button));
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_window = new DisposalUnitWindow();
|
||||
|
||||
_window.OpenCenteredMinSize();
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.Eject.OnPressed += _ => ButtonPressed(UiButton.Eject);
|
||||
_window.Engage.OnPressed += _ => ButtonPressed(UiButton.Engage);
|
||||
_window.Power.OnPressed += _ => ButtonPressed(UiButton.Power);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (!(state is DisposalUnitBoundUserInterfaceState cast))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_window?.UpdateState(cast);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class DisposalUnitComponent : SharedDisposalUnitComponent
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.GameObjects.Components.Animations;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalUnitComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class DisposalUnitVisualizer : AppearanceVisualizer
|
||||
{
|
||||
private const string AnimationKey = "disposal_unit_animation";
|
||||
|
||||
private string _stateAnchored;
|
||||
private string _stateUnAnchored;
|
||||
private string _overlayCharging;
|
||||
private string _overlayReady;
|
||||
private string _overlayFull;
|
||||
private string _overlayEngaged;
|
||||
private string _stateFlush;
|
||||
|
||||
private Animation _flushAnimation;
|
||||
|
||||
private void ChangeState(AppearanceComponent appearance)
|
||||
{
|
||||
if (!appearance.TryGetData(Visuals.VisualState, out VisualState state))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!appearance.Owner.TryGetComponent(out ISpriteComponent sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case VisualState.UnAnchored:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateUnAnchored);
|
||||
break;
|
||||
case VisualState.Anchored:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateAnchored);
|
||||
break;
|
||||
case VisualState.Flushing:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Base, _stateAnchored);
|
||||
|
||||
var animPlayer = appearance.Owner.GetComponent<AnimationPlayerComponent>();
|
||||
|
||||
if (!animPlayer.HasRunningAnimation(AnimationKey))
|
||||
{
|
||||
animPlayer.Play(_flushAnimation, AnimationKey);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (!appearance.TryGetData(Visuals.Handle, out HandleState handleState))
|
||||
{
|
||||
handleState = HandleState.Normal;
|
||||
}
|
||||
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.Handle, handleState != HandleState.Normal);
|
||||
|
||||
switch (handleState)
|
||||
{
|
||||
case HandleState.Normal:
|
||||
break;
|
||||
case HandleState.Engaged:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Handle, _overlayEngaged);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (!appearance.TryGetData(Visuals.Light, out LightState lightState))
|
||||
{
|
||||
lightState = LightState.Off;
|
||||
}
|
||||
|
||||
sprite.LayerSetVisible(DisposalUnitVisualLayers.Light, lightState != LightState.Off);
|
||||
|
||||
switch (lightState)
|
||||
{
|
||||
case LightState.Off:
|
||||
break;
|
||||
case LightState.Charging:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Light, _overlayCharging);
|
||||
break;
|
||||
case LightState.Full:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Light, _overlayFull);
|
||||
break;
|
||||
case LightState.Ready:
|
||||
sprite.LayerSetState(DisposalUnitVisualLayers.Light, _overlayReady);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_stateAnchored = node.GetNode("state_anchored").AsString();
|
||||
_stateUnAnchored = node.GetNode("state_unanchored").AsString();
|
||||
_overlayCharging = node.GetNode("overlay_charging").AsString();
|
||||
_overlayReady = node.GetNode("overlay_ready").AsString();
|
||||
_overlayFull = node.GetNode("overlay_full").AsString();
|
||||
_overlayEngaged = node.GetNode("overlay_engaged").AsString();
|
||||
_stateFlush = node.GetNode("state_flush").AsString();
|
||||
|
||||
var flushSound = node.GetNode("flush_sound").AsString();
|
||||
var flushTime = node.GetNode("flush_time").AsFloat();
|
||||
|
||||
_flushAnimation = new Animation {Length = TimeSpan.FromSeconds(flushTime)};
|
||||
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
_flushAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = DisposalUnitVisualLayers.Base;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame(_stateFlush, 0));
|
||||
|
||||
var sound = new AnimationTrackPlaySound();
|
||||
_flushAnimation.AnimationTracks.Add(sound);
|
||||
sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(flushSound, 0));
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
entity.EnsureComponent<AnimationPlayerComponent>();
|
||||
var appearance = entity.EnsureComponent<AppearanceComponent>();
|
||||
|
||||
ChangeState(appearance);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.Owner.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeState(component);
|
||||
}
|
||||
}
|
||||
|
||||
public enum DisposalUnitVisualLayers
|
||||
{
|
||||
Base,
|
||||
Handle,
|
||||
Light
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using Robust.Client.Graphics.Drawing;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalUnitComponent;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
/// <summary>
|
||||
/// Client-side UI used to control a <see cref="SharedDisposalUnitComponent"/>
|
||||
/// </summary>
|
||||
public class DisposalUnitWindow : SS14Window
|
||||
{
|
||||
private readonly Label _unitState;
|
||||
private readonly ProgressBar _pressureBar;
|
||||
private readonly Label _pressurePercentage;
|
||||
public readonly Button Engage;
|
||||
public readonly Button Eject;
|
||||
public readonly Button Power;
|
||||
|
||||
protected override Vector2? CustomSize => (300, 200);
|
||||
|
||||
public DisposalUnitWindow()
|
||||
{
|
||||
Contents.AddChild(new VBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("State: ")},
|
||||
(_unitState = new Label {Text = Loc.GetString("Ready")})
|
||||
}
|
||||
},
|
||||
new Control {CustomMinimumSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Pressure:")},
|
||||
(_pressureBar = new ProgressBar
|
||||
{
|
||||
CustomMinimumSize = (200, 20),
|
||||
SizeFlagsHorizontal = SizeFlags.ShrinkEnd,
|
||||
MinValue = 0,
|
||||
MaxValue = 1,
|
||||
Page = 0,
|
||||
Value = 0.5f,
|
||||
Children =
|
||||
{
|
||||
(_pressurePercentage = new Label())
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
new Control {CustomMinimumSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Handle:")},
|
||||
(Engage = new Button {Text = Loc.GetString("Engage")})
|
||||
}
|
||||
},
|
||||
new Control {CustomMinimumSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
new Label {Text = Loc.GetString("Eject:")},
|
||||
(Eject = new Button {Text = Loc.GetString("Eject Contents")})
|
||||
}
|
||||
},
|
||||
new Control {CustomMinimumSize = (0, 10)},
|
||||
new HBoxContainer
|
||||
{
|
||||
Children =
|
||||
{
|
||||
(Power = new CheckButton {Text = Loc.GetString("Power")}),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdatePressureBar(float pressure)
|
||||
{
|
||||
_pressureBar.Value = pressure;
|
||||
|
||||
var normalized = pressure / _pressureBar.MaxValue;
|
||||
|
||||
const float leftHue = 0.0f; // Red
|
||||
const float middleHue = 0.066f; // Orange
|
||||
const float rightHue = 0.33f; // Green
|
||||
const float saturation = 1.0f; // Uniform saturation
|
||||
const float value = 0.8f; // Uniform value / brightness
|
||||
const float alpha = 1.0f; // Uniform alpha
|
||||
|
||||
// These should add up to 1.0 or your transition won't be smooth
|
||||
const float leftSideSize = 0.5f; // Fraction of _chargeBar lerped from leftHue to middleHue
|
||||
const float rightSideSize = 0.5f; // Fraction of _chargeBar lerped from middleHue to rightHue
|
||||
|
||||
float finalHue;
|
||||
if (normalized <= leftSideSize)
|
||||
{
|
||||
normalized /= leftSideSize; // Adjust range to 0.0 to 1.0
|
||||
finalHue = FloatMath.Lerp(leftHue, middleHue, normalized);
|
||||
}
|
||||
else
|
||||
{
|
||||
normalized = (normalized - leftSideSize) / rightSideSize; // Adjust range to 0.0 to 1.0.
|
||||
finalHue = FloatMath.Lerp(middleHue, rightHue, normalized);
|
||||
}
|
||||
|
||||
// Check if null first to avoid repeatedly creating this.
|
||||
_pressureBar.ForegroundStyleBoxOverride ??= new StyleBoxFlat();
|
||||
|
||||
var foregroundStyleBoxOverride = (StyleBoxFlat) _pressureBar.ForegroundStyleBoxOverride;
|
||||
foregroundStyleBoxOverride.BackgroundColor =
|
||||
Color.FromHsv(new Vector4(finalHue, saturation, value, alpha));
|
||||
|
||||
var percentage = pressure / _pressureBar.MaxValue * 100;
|
||||
_pressurePercentage.Text = $" {percentage:0}%";
|
||||
}
|
||||
|
||||
public void UpdateState(DisposalUnitBoundUserInterfaceState state)
|
||||
{
|
||||
Title = state.UnitName;
|
||||
_unitState.Text = state.UnitState;
|
||||
UpdatePressureBar(state.Pressure);
|
||||
Power.Pressed = state.Powered;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Disposal;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Disposal
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class DisposalVisualizer : AppearanceVisualizer
|
||||
{
|
||||
private string _stateFree;
|
||||
private string _stateAnchored;
|
||||
private string _stateBroken;
|
||||
|
||||
private void ChangeState(AppearanceComponent appearance)
|
||||
{
|
||||
if (!appearance.Owner.TryGetComponent(out ISpriteComponent sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!appearance.TryGetData(DisposalTubeVisuals.VisualState, out DisposalTubeVisualState state))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var texture = state switch
|
||||
{
|
||||
DisposalTubeVisualState.Free => _stateFree,
|
||||
DisposalTubeVisualState.Anchored => _stateAnchored,
|
||||
DisposalTubeVisualState.Broken => _stateBroken,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
sprite.LayerSetState(0, texture);
|
||||
|
||||
if (state == DisposalTubeVisualState.Anchored)
|
||||
{
|
||||
appearance.Owner.EnsureComponent<SubFloorHideComponent>();
|
||||
}
|
||||
else if (appearance.Owner.HasComponent<SubFloorHideComponent>())
|
||||
{
|
||||
appearance.Owner.RemoveComponent<SubFloorHideComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
_stateFree = node.GetNode("state_free").AsString();
|
||||
_stateAnchored = node.GetNode("state_anchored").AsString();
|
||||
_stateBroken = node.GetNode("state_broken").AsString();
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
var appearance = entity.EnsureComponent<AppearanceComponent>();
|
||||
ChangeState(appearance);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (component.Owner.Deleted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ChangeState(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Client.GameObjects.Components.Storage;
|
||||
using Content.Client.GameObjects.Components.Disposal;
|
||||
using Content.Client.Interfaces.GameObjects.Components.Interaction;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
@@ -18,7 +18,7 @@ namespace Content.Client.GameObjects.Components.Items
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IItemComponent))]
|
||||
public class ItemComponent : Component, IItemComponent
|
||||
public class ItemComponent : Component, IItemComponent, IClientDraggable
|
||||
{
|
||||
public override string Name => "Item";
|
||||
public override uint? NetID => ContentNetIDs.ITEM;
|
||||
@@ -80,5 +80,15 @@ namespace Content.Client.GameObjects.Components.Items
|
||||
var itemComponentState = (ItemComponentState)curState;
|
||||
EquippedPrefix = itemComponentState.EquippedPrefix;
|
||||
}
|
||||
|
||||
bool IClientDraggable.ClientCanDropOn(CanDropEventArgs eventArgs)
|
||||
{
|
||||
return eventArgs.Target.HasComponent<DisposalUnitComponent>();
|
||||
}
|
||||
|
||||
bool IClientDraggable.ClientCanDrag(CanDragEventArgs eventArgs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using Content.Client.GameObjects.Components.Disposal;
|
||||
using Content.Client.Interfaces.GameObjects.Components.Interaction;
|
||||
using Content.Shared.GameObjects.Components.Mobs;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
@@ -5,8 +7,16 @@ namespace Content.Client.GameObjects.Components.Mobs
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedSpeciesComponent))]
|
||||
public class SpeciesComponent : SharedSpeciesComponent
|
||||
public class SpeciesComponent : SharedSpeciesComponent, IClientDraggable
|
||||
{
|
||||
bool IClientDraggable.ClientCanDropOn(CanDropEventArgs eventArgs)
|
||||
{
|
||||
return eventArgs.Target.HasComponent<DisposalUnitComponent>();
|
||||
}
|
||||
|
||||
bool IClientDraggable.ClientCanDrag(CanDragEventArgs eventArgs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
using Content.Shared.GameObjects.Components.Recycling;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Recycling
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class RecyclerVisualizer : AppearanceVisualizer
|
||||
{
|
||||
private string _stateClean;
|
||||
private string _stateBloody;
|
||||
|
||||
public override void LoadData(YamlMappingNode node)
|
||||
{
|
||||
base.LoadData(node);
|
||||
|
||||
if (node.TryGetNode("state_clean", out var child))
|
||||
{
|
||||
_stateClean = child.AsString();
|
||||
}
|
||||
|
||||
if (node.TryGetNode("state_bloody", out child))
|
||||
{
|
||||
_stateBloody = child.AsString();
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeEntity(IEntity entity)
|
||||
{
|
||||
base.InitializeEntity(entity);
|
||||
|
||||
if (!entity.TryGetComponent(out ISpriteComponent sprite) ||
|
||||
!entity.TryGetComponent(out AppearanceComponent appearance))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
appearance.TryGetData(RecyclerVisuals.Bloody, out bool bloody);
|
||||
sprite.LayerSetState(RecyclerVisualLayers.Bloody, bloody
|
||||
? _stateBloody
|
||||
: _stateClean);
|
||||
}
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (!component.Owner.TryGetComponent(out ISpriteComponent sprite))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
component.TryGetData(RecyclerVisuals.Bloody, out bool bloody);
|
||||
sprite.LayerSetState(RecyclerVisualLayers.Bloody, bloody
|
||||
? _stateBloody
|
||||
: _stateClean);
|
||||
}
|
||||
}
|
||||
|
||||
public enum RecyclerVisualLayers
|
||||
{
|
||||
Bloody
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,8 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
{
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IEyeManager _eyeManager;
|
||||
#pragma restore disable 649
|
||||
|
||||
#pragma warning restore 649
|
||||
|
||||
private AiDebugMode _tooltips = AiDebugMode.None;
|
||||
private readonly Dictionary<IEntity, PanelContainer> _aiBoxes = new Dictionary<IEntity,PanelContainer>();
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Content.Client.GameObjects.EntitySystems.AI
|
||||
{
|
||||
panel.Dispose();
|
||||
}
|
||||
|
||||
|
||||
_aiBoxes.Clear();
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Client.Interfaces.GameObjects.Components.Interaction;
|
||||
using Content.Client.State;
|
||||
using Content.Shared.GameObjects;
|
||||
@@ -55,7 +56,7 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
// entity performing the drag action
|
||||
private IEntity _dragger;
|
||||
private IEntity _draggedEntity;
|
||||
private IClientDraggable _draggable;
|
||||
private readonly List<IClientDraggable> _draggables = new List<IClientDraggable>();
|
||||
private IEntity _dragShadow;
|
||||
private DragState _state;
|
||||
// time since mouse down over the dragged entity
|
||||
@@ -146,10 +147,10 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
if (_interactionSystem.InRangeUnobstructed(dragger.Transform.MapPosition,
|
||||
entity.Transform.MapPosition, ignoredEnt: dragger) == false)
|
||||
{
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var canDrag = false;
|
||||
foreach (var draggable in entity.GetAllComponents<IClientDraggable>())
|
||||
{
|
||||
var dragEventArgs = new CanDragEventArgs(args.Session.AttachedEntity, entity);
|
||||
@@ -158,7 +159,7 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
// wait to initiate a drag
|
||||
_dragger = dragger;
|
||||
_draggedEntity = entity;
|
||||
_draggable = draggable;
|
||||
_draggables.Add(draggable);
|
||||
_mouseDownTime = 0;
|
||||
_state = DragState.MouseDown;
|
||||
_mouseDownScreenPos = _inputManager.MouseScreenPosition;
|
||||
@@ -166,9 +167,11 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
// but we will save the event so we can "re-play" it if this drag does
|
||||
// not turn into an actual drag so the click can be handled normally
|
||||
_savedMouseDown = args;
|
||||
return true;
|
||||
canDrag = true;
|
||||
}
|
||||
}
|
||||
|
||||
return canDrag;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -193,19 +196,21 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
// We don't use args.EntityUid here because drag interactions generally should
|
||||
// work even if there's something "on top" of the drop target
|
||||
if (_interactionSystem.InRangeUnobstructed(_dragger.Transform.MapPosition,
|
||||
args.Coordinates.ToMap(_mapManager), ignoredEnt: _dragger) == false)
|
||||
args.Coordinates.ToMap(_mapManager), ignoredEnt: _dragger, ignoreInsideBlocker: true) == false)
|
||||
{
|
||||
{
|
||||
CancelDrag(false, null);
|
||||
return false;
|
||||
}
|
||||
CancelDrag(false, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
var entities = GameScreenBase.GetEntitiesUnderPosition(_stateManager, args.Coordinates);
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
// check if it's able to be dropped on by current dragged entity
|
||||
if (_draggable.ClientCanDropOn(new CanDropEventArgs(_dragger, _draggedEntity, entity)))
|
||||
var canDropArgs = new CanDropEventArgs(_dragger, _draggedEntity, entity);
|
||||
var anyValidDraggable = _draggables.Any(draggable => draggable.ClientCanDropOn(canDropArgs));
|
||||
|
||||
if (anyValidDraggable)
|
||||
{
|
||||
// tell the server about the drop attempt
|
||||
RaiseNetworkEvent(new DragDropMessage(args.Coordinates, _draggedEntity.Uid,
|
||||
@@ -279,7 +284,10 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
if (inRangeSprite.Visible == false) continue;
|
||||
|
||||
// check if it's able to be dropped on by current dragged entity
|
||||
if (_draggable.ClientCanDropOn(new CanDropEventArgs(_dragger, _draggedEntity, pvsEntity)))
|
||||
var canDropArgs = new CanDropEventArgs(_dragger, _draggedEntity, pvsEntity);
|
||||
var anyValidDraggable = _draggables.Any(draggable => draggable.ClientCanDropOn(canDropArgs));
|
||||
|
||||
if (anyValidDraggable)
|
||||
{
|
||||
// highlight depending on whether its in or out of range
|
||||
var inRange = _interactionSystem.InRangeUnobstructed(_dragger.Transform.MapPosition,
|
||||
@@ -320,7 +328,7 @@ namespace Content.Client.GameObjects.EntitySystems
|
||||
|
||||
_dragShadow = null;
|
||||
_draggedEntity = null;
|
||||
_draggable = null;
|
||||
_draggables.Clear();
|
||||
_dragger = null;
|
||||
_state = DragState.NotDragging;
|
||||
|
||||
|
||||
@@ -139,7 +139,16 @@
|
||||
"Pullable",
|
||||
"CursedEntityStorage",
|
||||
"Listening",
|
||||
"Radio"
|
||||
"Radio",
|
||||
"DisposalHolder",
|
||||
"DisposalTransit",
|
||||
"DisposalEntry",
|
||||
"DisposalJunction",
|
||||
"DisposalBend",
|
||||
"Recycler",
|
||||
"Conveyor",
|
||||
"ConveyorSwitch",
|
||||
"Flippable",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,12 @@ namespace Content.Client.Interfaces.GameObjects.Components.Interaction
|
||||
|
||||
public class CanDropEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="CanDropEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The entity doing the drag and drop.</param>
|
||||
/// <param name="dragged">The entity that is being dragged and dropped.</param>
|
||||
/// <param name="target">The entity that <see cref="dragged"/> is being dropped onto.</param>
|
||||
public CanDropEventArgs(IEntity user, IEntity dragged, IEntity target)
|
||||
{
|
||||
User = user;
|
||||
@@ -40,20 +46,43 @@ namespace Content.Client.Interfaces.GameObjects.Components.Interaction
|
||||
Target = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entity doing the drag and drop.
|
||||
/// </summary>
|
||||
public IEntity User { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity that is being dragged and dropped.
|
||||
/// </summary>
|
||||
public IEntity Dragged { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity that <see cref="Dragged"/> is being dropped onto.
|
||||
/// </summary>
|
||||
public IEntity Target { get; }
|
||||
}
|
||||
|
||||
public class CanDragEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="CanDragEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The entity doing the drag and drop.</param>
|
||||
/// <param name="dragged">The entity that is being dragged and dropped.</param>
|
||||
public CanDragEventArgs(IEntity user, IEntity dragged)
|
||||
{
|
||||
User = user;
|
||||
Dragged = dragged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entity doing the drag and drop.
|
||||
/// </summary>
|
||||
public IEntity User { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity that is being dragged and dropped.
|
||||
/// </summary>
|
||||
public IEntity Dragged { get; }
|
||||
}
|
||||
}
|
||||
|
||||
130
Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components;
|
||||
using Content.Server.GameObjects.Components.Disposal;
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using NUnit.Framework;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Disposal
|
||||
{
|
||||
[TestFixture]
|
||||
[TestOf(typeof(DisposalHolderComponent))]
|
||||
[TestOf(typeof(DisposalEntryComponent))]
|
||||
[TestOf(typeof(DisposalUnitComponent))]
|
||||
public class DisposalUnitTest : ContentIntegrationTest
|
||||
{
|
||||
private void UnitInsert(DisposalUnitComponent unit, bool result, params IEntity[] entities)
|
||||
{
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
Assert.That(unit.CanInsert(entity), Is.EqualTo(result));
|
||||
Assert.That(unit.TryInsert(entity), Is.EqualTo(result));
|
||||
|
||||
if (result)
|
||||
{
|
||||
// Not in a tube yet
|
||||
Assert.That(entity.Transform.Parent == unit.Owner.Transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UnitContains(DisposalUnitComponent unit, bool result, params IEntity[] entities)
|
||||
{
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
Assert.That(unit.ContainedEntities.Contains(entity), Is.EqualTo(result));
|
||||
}
|
||||
}
|
||||
|
||||
private void UnitInsertContains(DisposalUnitComponent unit, bool result, params IEntity[] entities)
|
||||
{
|
||||
UnitInsert(unit, result, entities);
|
||||
UnitContains(unit, result, entities);
|
||||
}
|
||||
|
||||
private void Flush(DisposalUnitComponent unit, bool result, DisposalEntryComponent? entry = null, IDisposalTubeComponent? next = null, params IEntity[] entities)
|
||||
{
|
||||
Assert.That(unit.ContainedEntities, Is.SupersetOf(entities));
|
||||
Assert.AreEqual(unit.ContainedEntities.Count, entities.Length);
|
||||
|
||||
Assert.AreEqual(unit.TryFlush(), result);
|
||||
Assert.AreEqual(unit.ContainedEntities.Count == 0, entry != null || entities.Length == 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Test()
|
||||
{
|
||||
var server = StartServerDummyTicker();
|
||||
|
||||
IEntity human = null!;
|
||||
IEntity wrench = null!;
|
||||
DisposalUnitComponent unit = null!;
|
||||
DisposalEntryComponent entry = null!;
|
||||
|
||||
server.Assert(() =>
|
||||
{
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
// Spawn the entities
|
||||
human = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace);
|
||||
wrench = entityManager.SpawnEntity("Wrench", MapCoordinates.Nullspace);
|
||||
var disposalUnit = entityManager.SpawnEntity("DisposalUnit", MapCoordinates.Nullspace);
|
||||
var disposalTrunk = entityManager.SpawnEntity("DisposalTrunk", disposalUnit.Transform.MapPosition);
|
||||
|
||||
// Test for components existing
|
||||
Assert.True(disposalUnit.TryGetComponent(out unit));
|
||||
Assert.True(disposalTrunk.TryGetComponent(out entry));
|
||||
|
||||
// Can't insert, unanchored and unpowered
|
||||
UnitInsertContains(unit, false, human, wrench, disposalUnit, disposalTrunk);
|
||||
Assert.False(unit.Anchored);
|
||||
|
||||
// Anchor the disposal unit
|
||||
Assert.True(disposalUnit.TryGetComponent(out AnchorableComponent anchorableUnit));
|
||||
Assert.True(anchorableUnit.TryAnchor(human, wrench));
|
||||
Assert.True(unit.Anchored);
|
||||
|
||||
// Can't insert, unpowered
|
||||
UnitInsertContains(unit, false, human, wrench, disposalUnit, disposalTrunk);
|
||||
Assert.False(unit.Powered);
|
||||
|
||||
// Remove power need
|
||||
Assert.True(disposalUnit.TryGetComponent(out PowerReceiverComponent power));
|
||||
power.NeedsPower = false;
|
||||
Assert.True(unit.Powered);
|
||||
|
||||
// Can't insert the trunk or the unit into itself
|
||||
UnitInsertContains(unit, false, disposalUnit, disposalTrunk);
|
||||
|
||||
// Can insert mobs and items
|
||||
UnitInsertContains(unit, true, human, wrench);
|
||||
|
||||
// Move the disposal trunk away
|
||||
disposalTrunk.Transform.WorldPosition += (1, 0);
|
||||
|
||||
// Fail to flush with a mob and an item
|
||||
Flush(unit, false, null, null, human, wrench);
|
||||
|
||||
// Move the disposal trunk back
|
||||
disposalTrunk.Transform.WorldPosition -= (1, 0);
|
||||
|
||||
// Flush with a mob and an item
|
||||
Flush(unit, true, entry, null, human, wrench);
|
||||
|
||||
// Re-pressurizing
|
||||
Flush(unit, false, entry);
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -105,8 +105,7 @@ namespace Content.Server.Utility
|
||||
/// </summary>
|
||||
public static bool InRangeUnobstructed(IEntity user, MapCoordinates otherCoords,
|
||||
float range = SharedInteractionSystem.InteractionRange,
|
||||
int collisionMask = (int) CollisionGroup.Impassable, IEntity ignoredEnt = null,
|
||||
bool ignoreInsideBlocker = false)
|
||||
int collisionMask = (int) CollisionGroup.Impassable, IEntity ignoredEnt = null, bool ignoreInsideBlocker = false)
|
||||
{
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
var interactionSystem = EntitySystem.Get<SharedInteractionSystem>();
|
||||
@@ -115,13 +114,11 @@ namespace Content.Server.Utility
|
||||
{
|
||||
var localizationManager = IoCManager.Resolve<ILocalizationManager>();
|
||||
user.PopupMessage(user, localizationManager.GetString("You can't reach there!"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Conveyor
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum ConveyorVisuals
|
||||
{
|
||||
State
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum ConveyorState
|
||||
{
|
||||
Off = 0,
|
||||
Forward,
|
||||
Reversed,
|
||||
Loose
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Disposal
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum DisposalTubeVisuals
|
||||
{
|
||||
VisualState
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DisposalTubeVisualState
|
||||
{
|
||||
Free = 0,
|
||||
Anchored,
|
||||
Broken,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Disposal
|
||||
{
|
||||
public abstract class SharedDisposalUnitComponent : Component
|
||||
{
|
||||
public override string Name => "DisposalUnit";
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum Visuals
|
||||
{
|
||||
VisualState,
|
||||
Handle,
|
||||
Light
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum VisualState
|
||||
{
|
||||
UnAnchored,
|
||||
Anchored,
|
||||
Flushing
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum HandleState
|
||||
{
|
||||
Normal,
|
||||
Engaged
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum LightState
|
||||
{
|
||||
Off,
|
||||
Charging,
|
||||
Full,
|
||||
Ready
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum UiButton
|
||||
{
|
||||
Eject,
|
||||
Engage,
|
||||
Power
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum State
|
||||
{
|
||||
Ready,
|
||||
Pressurizing
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class DisposalUnitBoundUserInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public readonly string UnitName;
|
||||
public readonly string UnitState;
|
||||
public readonly float Pressure;
|
||||
public readonly bool Powered;
|
||||
|
||||
public DisposalUnitBoundUserInterfaceState(string unitName, string unitState, float pressure, bool powered)
|
||||
{
|
||||
UnitName = unitName;
|
||||
UnitState = unitState;
|
||||
Pressure = pressure;
|
||||
Powered = powered;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Message data sent from client to server when a disposal unit ui button is pressed.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class UiButtonPressedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public readonly UiButton Button;
|
||||
|
||||
public UiButtonPressedMessage(UiButton button)
|
||||
{
|
||||
Button = button;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DisposalUnitUiKey
|
||||
{
|
||||
Key
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Recycling
|
||||
{
|
||||
[NetSerializable, Serializable]
|
||||
public enum RecyclerVisuals
|
||||
{
|
||||
Bloody
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@
|
||||
public const uint PROJECTILE = 1053;
|
||||
public const uint THROWN_ITEM = 1054;
|
||||
public const uint STRAP = 1055;
|
||||
public const uint DISPOSABLE = 1056;
|
||||
|
||||
// Net IDs for integration tests.
|
||||
public const uint PREDICTION_TEST = 10001;
|
||||
|
||||
@@ -1,28 +1,47 @@
|
||||
using System;
|
||||
using System;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface allows the component's entity to be dragged and dropped by mouse onto another entity and gives it
|
||||
/// behavior when that occurs.
|
||||
/// This interface allows the component's entity to be dragged and dropped
|
||||
/// by mouse onto another entity and gives it behavior when that occurs.
|
||||
/// </summary>
|
||||
public interface IDragDrop
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked server-side when this component's entity is being dragged and dropped on another.
|
||||
///
|
||||
/// There is no other server-side drag and drop check other than a range check, so make sure to validate
|
||||
/// if this object can be dropped on the target object!
|
||||
/// Invoked server-side when this component's entity is being dragged
|
||||
/// and dropped on another before invoking <see cref="DragDrop"/>.
|
||||
/// Note that other drag and drop interactions may be attempted if
|
||||
/// this one fails.
|
||||
/// </summary>
|
||||
/// <returns>true iff an interaction occurred and no further interaction should
|
||||
/// be processed for this drop.</returns>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <returns>true if <see cref="eventArgs"/> is valid, false otherwise.</returns>
|
||||
bool CanDragDrop(DragDropEventArgs eventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked server-side when this component's entity is being dragged
|
||||
/// and dropped on another.
|
||||
/// Note that other drag and drop interactions may be attempted if
|
||||
/// this one fails.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if an interaction occurred and no further interaction should
|
||||
/// be processed for this drop.
|
||||
/// </returns>
|
||||
bool DragDrop(DragDropEventArgs eventArgs);
|
||||
}
|
||||
|
||||
public class DragDropEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="DragDropEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="user">The entity doing the drag and drop.</param>
|
||||
/// <param name="dropLocation">The location where <see cref="dropped"/> is being dropped.</param>
|
||||
/// <param name="dropped">The entity that is being dragged and dropped.</param>
|
||||
/// <param name="target">The entity that <see cref="dropped"/> is being dropped onto.</param>
|
||||
public DragDropEventArgs(IEntity user, GridCoordinates dropLocation, IEntity dropped, IEntity target)
|
||||
{
|
||||
User = user;
|
||||
@@ -31,9 +50,24 @@ namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
Target = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entity doing the drag and drop.
|
||||
/// </summary>
|
||||
public IEntity User { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The location where <see cref="Dropped"/> is being dropped.
|
||||
/// </summary>
|
||||
public GridCoordinates DropLocation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity that is being dragged and dropped.
|
||||
/// </summary>
|
||||
public IEntity Dropped { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The entity that <see cref="Dropped"/> is being dropped onto.
|
||||
/// </summary>
|
||||
public IEntity Target { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,31 @@
|
||||
namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
namespace Content.Shared.Interfaces.GameObjects.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface allows the component's entity to be dragged and dropped onto by another entity and gives it
|
||||
/// behavior when that occurs.
|
||||
/// This interface allows the component's entity to be dragged and dropped
|
||||
/// onto by another entity and gives it behavior when that occurs.
|
||||
/// </summary>
|
||||
public interface IDragDropOn
|
||||
{
|
||||
/// <summary>
|
||||
/// Invoked server-side when another entity is being dragged and dropped onto this one
|
||||
///
|
||||
/// There is no other server-side drag and drop check other than a range check, so make sure to validate
|
||||
/// if this object can be dropped on the dropped object!
|
||||
/// Invoked server-side when another entity is being dragged and dropped
|
||||
/// onto this one before invoking <see cref="DragDropOn"/>.
|
||||
/// Note that other drag and drop interactions may be attempted if
|
||||
/// this one fails.
|
||||
/// </summary>
|
||||
/// <returns>true iff an interaction occurred and no further interaction should
|
||||
/// be processed for this drop.</returns>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <returns>true if <see cref="eventArgs"/> is valid, false otherwise.</returns>
|
||||
bool CanDragDropOn(DragDropEventArgs eventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked server-side when another entity is being dragged and dropped
|
||||
/// onto this one before invoking <see cref="DragDropOn"/>
|
||||
/// Note that other drag and drop interactions may be attempted if
|
||||
/// this one fails.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// true if an interaction occurred and no further interaction should
|
||||
/// be processed for this drop.
|
||||
/// </returns>
|
||||
bool DragDropOn(DragDropEventArgs eventArgs);
|
||||
}
|
||||
}
|
||||
|
||||
38
Content.Shared/Physics/ConveyedController.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
#nullable enable
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Robust.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.Physics;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Shared.Physics
|
||||
{
|
||||
public class ConveyedController : VirtualController
|
||||
{
|
||||
public override ICollidableComponent? ControlledComponent { protected get; set; }
|
||||
|
||||
public void Move(Vector2 velocityDirection, float speed)
|
||||
{
|
||||
if (ControlledComponent?.Owner.HasComponent<MovementIgnoreGravityComponent>() == false &&
|
||||
IoCManager.Resolve<IPhysicsManager>().IsWeightless(ControlledComponent.Owner.Transform.GridPosition))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ControlledComponent?.Status == BodyStatus.InAir)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LinearVelocity = velocityDirection * speed * 100;
|
||||
}
|
||||
|
||||
public override void UpdateAfterProcessing()
|
||||
{
|
||||
base.UpdateAfterProcessing();
|
||||
|
||||
LinearVelocity = Vector2.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Resources/Audio/Effects/clang.ogg
Normal file
BIN
Resources/Audio/Machines/disposalflush.ogg
Normal file
@@ -88,6 +88,10 @@
|
||||
- mapping
|
||||
- addhand
|
||||
- removehand
|
||||
- tilepry
|
||||
- anchor
|
||||
- unanchor
|
||||
- tubeconnections
|
||||
CanViewVar: true
|
||||
CanAdminPlace: true
|
||||
|
||||
@@ -163,6 +167,10 @@
|
||||
- mapping
|
||||
- addhand
|
||||
- removehand
|
||||
- tilepry
|
||||
- anchor
|
||||
- unanchor
|
||||
- tubeconnections
|
||||
CanViewVar: true
|
||||
CanAdminPlace: true
|
||||
CanScript: true
|
||||
|
||||
@@ -3465,14 +3465,14 @@ entities:
|
||||
- parent: 384
|
||||
type: Transform
|
||||
- uid: 386
|
||||
type: CableStack
|
||||
type: CableStack1
|
||||
components:
|
||||
- parent: 0
|
||||
pos: -15.5,-0.5
|
||||
rot: -1.5707963267949 rad
|
||||
type: Transform
|
||||
- uid: 387
|
||||
type: CableStack
|
||||
type: CableStack1
|
||||
components:
|
||||
- parent: 0
|
||||
pos: -15.5,-0.5
|
||||
@@ -3756,14 +3756,14 @@ entities:
|
||||
rot: -1.5707963267949 rad
|
||||
type: Transform
|
||||
- uid: 413
|
||||
type: CableStack
|
||||
type: CableStack1
|
||||
components:
|
||||
- parent: 0
|
||||
pos: -15.5,0.5
|
||||
rot: -1.5707963267949 rad
|
||||
type: Transform
|
||||
- uid: 414
|
||||
type: CableStack
|
||||
type: CableStack1
|
||||
components:
|
||||
- parent: 0
|
||||
pos: -15.5,0.5
|
||||
@@ -8567,13 +8567,13 @@ entities:
|
||||
pos: 9.654155,21.628613
|
||||
type: Transform
|
||||
- uid: 947
|
||||
type: CableStack
|
||||
type: CableStack1
|
||||
components:
|
||||
- parent: 0
|
||||
pos: 10.561831,21.767809
|
||||
type: Transform
|
||||
- uid: 948
|
||||
type: CableStack
|
||||
type: CableStack1
|
||||
components:
|
||||
- parent: 0
|
||||
pos: 10.577456,21.424059
|
||||
|
||||
@@ -3,38 +3,38 @@
|
||||
name: gravity generator
|
||||
description: It's what keeps you to the floor.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Constructible/Power/gravity_generator.rsi
|
||||
layers:
|
||||
- state: on
|
||||
- sprite: Constructible/Power/gravity_generator_core.rsi
|
||||
state: activated
|
||||
shader: unshaded
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/gravity_generator.rsi
|
||||
state: on
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
- type: PowerReceiver
|
||||
powerLoad: 500
|
||||
- type: Collidable
|
||||
shapes:
|
||||
- !type:PhysShapeAabb
|
||||
bounds: "-1.5,-1.5,1.5,1.5"
|
||||
layer:
|
||||
- Opaque
|
||||
- Impassable
|
||||
- MobImpassable
|
||||
- VaultImpassable
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Damageable
|
||||
- type: Breakable
|
||||
threshold: 150
|
||||
- type: GravityGenerator
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.GravityGeneratorUiKey.Key
|
||||
type: GravityGeneratorBoundUserInterface
|
||||
- type: Sprite
|
||||
sprite: Constructible/Power/gravity_generator.rsi
|
||||
layers:
|
||||
- state: on
|
||||
- sprite: Constructible/Power/gravity_generator_core.rsi
|
||||
state: activated
|
||||
shader: unshaded
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/gravity_generator.rsi
|
||||
state: on
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
- type: PowerReceiver
|
||||
powerLoad: 500
|
||||
- type: Collidable
|
||||
shapes:
|
||||
- !type:PhysShapeAabb
|
||||
bounds: "-1.5,-1.5,1.5,1.5"
|
||||
layer:
|
||||
- Opaque
|
||||
- Impassable
|
||||
- MobImpassable
|
||||
- VaultImpassable
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Damageable
|
||||
- type: Breakable
|
||||
threshold: 150
|
||||
- type: GravityGenerator
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.GravityGeneratorUiKey.Key
|
||||
type: GravityGeneratorBoundUserInterface
|
||||
placement:
|
||||
mode: AlignTileAny
|
||||
|
||||
@@ -14,11 +14,9 @@
|
||||
color: "#889192"
|
||||
drawdepth: Walls
|
||||
sprite: Constructible/Structures/Walls/low_wall.rsi
|
||||
|
||||
- type: Icon
|
||||
sprite: Constructible/Structures/Walls/low_wall.rsi
|
||||
state: metal
|
||||
|
||||
- type: Collidable
|
||||
shapes:
|
||||
- !type:PhysShapeAabb
|
||||
@@ -26,10 +24,8 @@
|
||||
- type: Damageable
|
||||
- type: Destructible
|
||||
thresholdvalue: 100
|
||||
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
|
||||
- type: LowWall
|
||||
key: walls
|
||||
base: metal_
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
drawdepth: Walls
|
||||
|
||||
- type: Icon
|
||||
state: full
|
||||
|
||||
- type: Collidable
|
||||
shapes:
|
||||
- !type:PhysShapeAabb
|
||||
@@ -33,10 +31,8 @@
|
||||
- type: Occluder
|
||||
sizeX: 32
|
||||
sizeY: 32
|
||||
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
|
||||
- type: IconSmooth
|
||||
key: walls
|
||||
base: solid
|
||||
|
||||
61
Resources/Prototypes/Entities/Constructible/conveyor.yml
Normal file
@@ -0,0 +1,61 @@
|
||||
- type: entity
|
||||
id: ConveyorBelt
|
||||
name: conveyor belt
|
||||
description: A conveyor belt, commonly used to transport large numbers of items elsewhere quite quickly.
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Collidable
|
||||
hard: false
|
||||
shapes:
|
||||
- !type:PhysShapeAabb
|
||||
bounds: "-0.49,-0.49,0.49,0.49"
|
||||
mask:
|
||||
- Impassable
|
||||
- MobImpassable
|
||||
- VaultImpassable
|
||||
- SmallImpassable
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
sprite: Constructible/Power/conveyor.rsi
|
||||
state: conveyor_started_cw
|
||||
drawdepth: FloorObjects
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/conveyor.rsi
|
||||
state: conveyor_started_cw
|
||||
- type: Conveyor
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: ConveyorVisualizer
|
||||
state_running: conveyor_started_cw
|
||||
state_stopped: conveyor_stopped_cw
|
||||
state_reversed: conveyor_started_cw_r
|
||||
state_loose: conveyor_loose
|
||||
- type: PowerReceiver
|
||||
|
||||
- type: entity
|
||||
id: ConveyorSwitch
|
||||
name: conveyor switch
|
||||
description: A conveyor control switch.
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
sprite: Constructible/Power/conveyor.rsi
|
||||
state: switch-off
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/conveyor.rsi
|
||||
state: switch-off
|
||||
- type: ConveyorSwitch
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: ConveyorSwitchVisualizer
|
||||
state_forward: switch-fwd
|
||||
state_off: switch-off
|
||||
state_reversed: switch-rev
|
||||
state_loose: switch
|
||||
222
Resources/Prototypes/Entities/Constructible/disposal.yml
Normal file
@@ -0,0 +1,222 @@
|
||||
- type: entity
|
||||
id: DisposalPipeBase
|
||||
abstract: true
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
snap:
|
||||
- Disposal
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Physics
|
||||
anchored: true
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
- type: Anchorable
|
||||
- type: Damageable
|
||||
- type: Breakable
|
||||
- type: Rotatable
|
||||
|
||||
- type: entity
|
||||
id: DisposalHolder
|
||||
name: disposal holder
|
||||
components:
|
||||
- type: DisposalHolder
|
||||
|
||||
- type: entity
|
||||
id: DisposalPipe
|
||||
parent: DisposalPipeBase
|
||||
name: disposal pipe segment
|
||||
description: A huge pipe segment used for constructing disposal systems
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: BelowFloor
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-s
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-s
|
||||
- type: DisposalTransit
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: DisposalVisualizer
|
||||
state_free: conpipe-s
|
||||
state_anchored: pipe-s
|
||||
state_broken: pipe-b
|
||||
|
||||
- type: entity
|
||||
id: DisposalTrunk
|
||||
parent: DisposalPipeBase
|
||||
name: disposal trunk
|
||||
description: A pipe trunk used as an entry point for disposal systems
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: BelowFloor
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-t
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-t
|
||||
- type: DisposalEntry
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: DisposalVisualizer
|
||||
state_free: conpipe-t
|
||||
state_anchored: pipe-t
|
||||
state_broken: pipe-b
|
||||
|
||||
- type: entity
|
||||
id: DisposalUnit
|
||||
name: disposal unit
|
||||
description: A pneumatic waste disposal unit
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
snap:
|
||||
- Disposal
|
||||
components:
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
layers:
|
||||
- state: condisposal
|
||||
map: ["enum.DisposalUnitVisualLayers.Base"]
|
||||
- state: dispover-handle
|
||||
map: ["enum.DisposalUnitVisualLayers.Handle"]
|
||||
- state: dispover-ready
|
||||
map: ["enum.DisposalUnitVisualLayers.Light"]
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: disposal
|
||||
- type: PowerReceiver
|
||||
- type: DisposalUnit
|
||||
flushTime: 2
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Physics
|
||||
anchored: true
|
||||
shapes:
|
||||
- !type:PhysShapeAabb
|
||||
bounds: "-0.3,-0.3,0.3,0.3"
|
||||
layer:
|
||||
- Impassable
|
||||
- MobImpassable
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
- type: Anchorable
|
||||
- type: Destructible
|
||||
thresholdvalue: 100
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: DisposalUnitVisualizer
|
||||
state_unanchored: condisposal
|
||||
state_anchored: disposal
|
||||
overlay_charging: dispover-charge
|
||||
overlay_ready: dispover-ready
|
||||
overlay_full: dispover-full
|
||||
overlay_engaged: dispover-handle
|
||||
state_flush: disposal-flush
|
||||
flush_sound: /Audio/Machines/disposalflush.ogg
|
||||
flush_time: 2
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.DisposalUnitUiKey.Key
|
||||
type: DisposalUnitBoundUserInterface
|
||||
|
||||
- type: entity
|
||||
id: DisposalJunction
|
||||
parent: DisposalPipeBase
|
||||
name: disposal junction
|
||||
description: A three-way junction. The arrow indicates where items exit
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: BelowFloor
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-j1
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-j1
|
||||
- type: DisposalJunction
|
||||
degrees:
|
||||
- 0
|
||||
- -90
|
||||
- 180
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: DisposalVisualizer
|
||||
state_free: conpipe-j1
|
||||
state_anchored: pipe-j1
|
||||
state_broken: pipe-b
|
||||
- type: Flippable
|
||||
entity: DisposalJunctionFlipped
|
||||
|
||||
- type: entity
|
||||
id: DisposalJunctionFlipped
|
||||
parent: DisposalJunction
|
||||
name: flipped disposal junction
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: BelowFloor
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-j2
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-j2
|
||||
- type: DisposalJunction
|
||||
degrees:
|
||||
- 0
|
||||
- 90
|
||||
- 180
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: DisposalVisualizer
|
||||
state_free: conpipe-j2
|
||||
state_anchored: pipe-j2
|
||||
state_broken: pipe-b
|
||||
- type: Flippable
|
||||
entity: DisposalJunction
|
||||
|
||||
- type: entity
|
||||
id: DisposalYJunction
|
||||
parent: DisposalPipeBase
|
||||
name: disposal y-junction
|
||||
description: A three-way junction with another exit point.
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: BelowFloor
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-y
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-y
|
||||
- type: DisposalJunction
|
||||
degrees:
|
||||
- 0
|
||||
- 90
|
||||
- -90
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: DisposalVisualizer
|
||||
state_free: conpipe-y
|
||||
state_anchored: pipe-y
|
||||
state_broken: pipe-b
|
||||
|
||||
- type: entity
|
||||
id: DisposalBend
|
||||
parent: DisposalPipeBase
|
||||
name: disposal bend
|
||||
description: A tube bent at a 90 degree angle.
|
||||
components:
|
||||
- type: Sprite
|
||||
drawdepth: BelowFloor
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-c
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/disposal.rsi
|
||||
state: conpipe-c
|
||||
- type: DisposalBend
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: DisposalVisualizer
|
||||
state_free: conpipe-c
|
||||
state_anchored: pipe-c
|
||||
state_broken: pipe-b
|
||||
37
Resources/Prototypes/Entities/Constructible/recycler.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
- type: entity
|
||||
id: Recycler
|
||||
name: recycler
|
||||
description: A large crushing machine used to recycle small items inefficiently. There are lights on the side.
|
||||
placement:
|
||||
mode: SnapgridCenter
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
- type: Collidable
|
||||
hard: false
|
||||
shapes:
|
||||
- !type:PhysShapeAabb
|
||||
bounds: "-0.49,-0.49,0.49,0.49"
|
||||
layer:
|
||||
- Opaque
|
||||
- Impassable
|
||||
- MobImpassable
|
||||
- VaultImpassable
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
sprite: Constructible/Power/recycling.rsi
|
||||
layers:
|
||||
- state: grinder-o1
|
||||
map: ["enum.RecyclerVisualLayers.Bloody"]
|
||||
- type: Icon
|
||||
sprite: Constructible/Power/recycling.rsi
|
||||
state: grinder-o0
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: RecyclerVisualizer
|
||||
state_clean: grinder-o1
|
||||
state_bloody: grinder-o1bld
|
||||
- type: Recycler
|
||||
- type: PowerReceiver
|
||||
@@ -69,7 +69,8 @@
|
||||
- type: entity
|
||||
abstract: true
|
||||
parent: BaseItem
|
||||
id: CableStack
|
||||
id: CableStack1
|
||||
name: cable stack 1
|
||||
suffix: Full
|
||||
components:
|
||||
- type: Stack
|
||||
@@ -84,7 +85,7 @@
|
||||
all: -0.15,-0.15,0.15,0.15
|
||||
|
||||
- type: entity
|
||||
parent: CableStack
|
||||
parent: CableStack1
|
||||
id: HVWireStack
|
||||
name: HV Wire Coil
|
||||
components:
|
||||
@@ -95,7 +96,7 @@
|
||||
blockingWireType: HighVoltage
|
||||
|
||||
- type: entity
|
||||
parent: CableStack
|
||||
parent: CableStack1
|
||||
id: MVWireStack
|
||||
name: MV Wire Coil
|
||||
components:
|
||||
@@ -106,7 +107,7 @@
|
||||
blockingWireType: MediumVoltage
|
||||
|
||||
- type: entity
|
||||
parent: CableStack
|
||||
parent: CableStack1
|
||||
id: ApcExtensionCableStack
|
||||
name: Apc Extension Cable Coil
|
||||
components:
|
||||
@@ -115,7 +116,7 @@
|
||||
- type: WirePlacer
|
||||
wirePrototypeID: ApcExtensionCable
|
||||
blockingWireType: Apc
|
||||
|
||||
|
||||
- type: entity
|
||||
parent: HVWireStack
|
||||
id: HVWireStack1
|
||||
@@ -123,7 +124,7 @@
|
||||
components:
|
||||
- type: Stack
|
||||
count: 1
|
||||
|
||||
|
||||
- type: entity
|
||||
parent: MVWireStack
|
||||
id: MVWireStack1
|
||||
@@ -131,7 +132,7 @@
|
||||
components:
|
||||
- type: Stack
|
||||
count: 1
|
||||
|
||||
|
||||
- type: entity
|
||||
parent: ApcExtensionCableStack
|
||||
id: ApcExtensionCableStack1
|
||||
@@ -139,7 +140,7 @@
|
||||
components:
|
||||
- type: Stack
|
||||
count: 1
|
||||
|
||||
|
||||
- type: entity
|
||||
name: gold bar
|
||||
id: GoldStack
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
bounds: "-0.25,-0.25,0.25,0.25"
|
||||
layer:
|
||||
- Clickable
|
||||
|
||||
IsScrapingFloor: true
|
||||
- type: Physics
|
||||
mass: 5
|
||||
|
||||
19
Resources/Prototypes/Recipes/Construction/conveyor.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
- type: construction
|
||||
name: conveyor belt
|
||||
id: ConveyorBelt
|
||||
category: Machines/Conveyor
|
||||
keywords: [conveyor, belt]
|
||||
placementMode: SnapgridCenter
|
||||
description: A conveyor belt, commonly used to transport large numbers of items elsewhere quite quickly.
|
||||
icon:
|
||||
sprite: Constructible/Power/conveyor.rsi
|
||||
state: conveyor_stopped_cw
|
||||
result: ConveyorBelt
|
||||
steps:
|
||||
- material: Metal
|
||||
amount: 1
|
||||
icon:
|
||||
sprite: Constructible/Power/conveyor.rsi
|
||||
state: conveyor_stopped_cw
|
||||
- material: Cable
|
||||
amount: 1
|
||||
@@ -9,7 +9,6 @@
|
||||
icon:
|
||||
sprite: Constructible/Lighting/lighting.rsi
|
||||
state: on
|
||||
|
||||
result: Poweredlight
|
||||
steps:
|
||||
- material: Metal
|
||||
@@ -17,13 +16,10 @@
|
||||
icon:
|
||||
sprite: Constructible/Lighting/lighting.rsi
|
||||
state: construct
|
||||
|
||||
|
||||
- material: Cable
|
||||
amount: 1
|
||||
icon:
|
||||
sprite: Constructible/Lighting/lighting.rsi
|
||||
state: empty
|
||||
|
||||
- material: Glass
|
||||
amount: 1
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
icon: Constructible/Structures/Walls/wall_girder.png
|
||||
reverse:
|
||||
tool: Anchoring
|
||||
|
||||
- material: Metal
|
||||
amount: 2
|
||||
reverse:
|
||||
@@ -90,7 +89,6 @@
|
||||
amount: 2
|
||||
reverse:
|
||||
tool: Anchoring
|
||||
|
||||
# Should be replaced with Metal Rods when someone puts them in.
|
||||
- material: Metal
|
||||
amount: 2
|
||||
|
||||
|
After Width: | Height: | Size: 286 B |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 109 B |
@@ -0,0 +1 @@
|
||||
{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "https://github.com/discordia-space/CEV-Eris/blob/a846799c53f8ee9dcec4b075d7645f591f3ec19d/icons/obj/machines/conveyor.dmi", "states": [{"name": "conveyor_loose", "directions": 1, "delays": [[1.0]]}, {"name": "conveyor_started_ccw", "directions": 8, "delays": [[0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1]]}, {"name": "conveyor_started_ccw_r", "directions": 8, "delays": [[0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1]]}, {"name": "conveyor_started_cw", "directions": 8, "delays": [[0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1]]}, {"name": "conveyor_started_cw_r", "directions": 8, "delays": [[0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1]]}, {"name": "conveyor_stopped_ccw", "directions": 8, "delays": [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]]}, {"name": "conveyor_stopped_cw", "directions": 8, "delays": [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]]}, {"name": "greenlight", "directions": 1, "delays": [[1.0]]}, {"name": "redlight", "directions": 1, "delays": [[1.0]]}, {"name": "switch", "directions": 1, "delays": [[1.0]]}, {"name": "switch-fwd", "directions": 1, "delays": [[1.0]]}, {"name": "switch-off", "directions": 1, "delays": [[1.0]]}, {"name": "switch-rev", "directions": 1, "delays": [[1.0]]}]}
|
||||
BIN
Resources/Textures/Constructible/Power/conveyor.rsi/redlight.png
Normal file
|
After Width: | Height: | Size: 107 B |
|
After Width: | Height: | Size: 364 B |
|
After Width: | Height: | Size: 326 B |
|
After Width: | Height: | Size: 387 B |
BIN
Resources/Textures/Constructible/Power/conveyor.rsi/switch.png
Normal file
|
After Width: | Height: | Size: 207 B |
|
After Width: | Height: | Size: 415 B |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Resources/Textures/Constructible/Power/disposal.rsi/disposal.png
Normal file
|
After Width: | Height: | Size: 419 B |
|
After Width: | Height: | Size: 236 B |
|
After Width: | Height: | Size: 139 B |
|
After Width: | Height: | Size: 161 B |
|
After Width: | Height: | Size: 178 B |
|
After Width: | Height: | Size: 4.6 KiB |
BIN
Resources/Textures/Constructible/Power/disposal.rsi/intake.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1 @@
|
||||
{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "https://github.com/discordia-space/CEV-Eris/blob/bbe32606902c90f5290b57d905a3f31b84dc6d7d/icons/obj/pipes/disposal.dmi and modified by DrSmugleaf", "states": [{"name": "condisposal", "directions": 1, "delays": [[1.0]]}, {"name": "conpipe-c", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j1s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j2s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-t", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-y", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "disposal", "directions": 1, "delays": [[1.0]]}, {"name": "disposal-flush", "directions": 1, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.5, 0.1, 0.1, 0.1]]}, {"name": "dispover-charge", "directions": 1, "delays": [[0.4, 0.4]]}, {"name": "dispover-full", "directions": 1, "delays": [[0.2, 0.2]]}, {"name": "dispover-handle", "directions": 1, "delays": [[1.0]]}, {"name": "dispover-ready", "directions": 1, "delays": [[1.0]]}, {"name": "intake", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "intake-closing", "directions": 4, "delays": [[0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "outlet", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "outlet-open", "directions": 4, "delays": [[0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1], [0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1], [0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1], [0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1]]}, {"name": "pipe-b", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-bf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-c", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-cf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-d", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1f", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1sf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2f", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2sf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-sf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-t", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-tagger", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-tagger-partial", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-tf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-u", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-y", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-yf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]}
|
||||
|
After Width: | Height: | Size: 4.1 KiB |