diff --git a/Content.Client/Doors/AirlockVisualizer.cs b/Content.Client/Doors/AirlockVisualizer.cs index af40479a6c..60cb788e4a 100644 --- a/Content.Client/Doors/AirlockVisualizer.cs +++ b/Content.Client/Doors/AirlockVisualizer.cs @@ -30,6 +30,9 @@ namespace Content.Client.Doors [DataField("animatedPanel")] private bool _animatedPanel = true; + /// + /// Means the door is simply open / closed / opening / closing. No wires or access. + /// [DataField("simpleVisuals")] private bool _simpleVisuals = false; diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 3413c60249..8b938dbe07 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -120,6 +120,7 @@ namespace Content.Client.Entry "SurgeryTool", "EmitSoundOnThrow", "Flash", + "Docking", "Telecrystal", "TrashSpawner", "RCD", diff --git a/Content.Server/Doors/Components/ServerDoorComponent.cs b/Content.Server/Doors/Components/ServerDoorComponent.cs index ee9bdccf6f..a35b185885 100644 --- a/Content.Server/Doors/Components/ServerDoorComponent.cs +++ b/Content.Server/Doors/Components/ServerDoorComponent.cs @@ -11,22 +11,18 @@ using Content.Server.Construction; using Content.Server.Construction.Components; using Content.Server.Hands.Components; using Content.Server.Stunnable; -using Content.Server.Stunnable.Components; using Content.Server.Tools; using Content.Server.Tools.Components; using Content.Shared.Damage; using Content.Shared.Doors; using Content.Shared.Interaction; using Content.Shared.Sound; -using Content.Shared.Stunnable; using Content.Shared.Tools; -using Content.Shared.Tools.Components; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Log; using Robust.Shared.Maths; -using Robust.Shared.Physics; using Robust.Shared.Player; using Robust.Shared.Players; using Robust.Shared.Prototypes; @@ -276,6 +272,11 @@ namespace Content.Server.Doors.Components public void TryOpen(IEntity? user=null) { + var msg = new DoorOpenAttemptEvent(); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, msg); + + if (msg.Cancelled) return; + if (user == null) { // a machine opened it or something, idk @@ -412,6 +413,11 @@ namespace Content.Server.Doors.Components public void TryClose(IEntity? user=null) { + var msg = new DoorCloseAttemptEvent(); + Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, msg); + + if (msg.Cancelled) return; + if (user != null && !CanCloseByEntity(user)) { Deny(); @@ -495,7 +501,7 @@ namespace Content.Server.Doors.Components if (CloseSound != null) { SoundSystem.Play(Filter.Pvs(Owner), CloseSound.GetSound(), Owner, - AudioParams.Default.WithVolume(-10)); + AudioParams.Default.WithVolume(-5)); } Owner.SpawnTimer(CloseTimeOne, async () => @@ -673,8 +679,10 @@ namespace Content.Server.Doors.Components var canEv = new BeforeDoorPryEvent(eventArgs); Owner.EntityManager.EventBus.RaiseLocalEvent(Owner.Uid, canEv, false); + if (canEv.Cancelled) return false; + var successfulPry = await toolSystem.UseTool(eventArgs.Using.Uid, eventArgs.User.Uid, Owner.Uid, - 0f, ev.PryTimeModifier * PryTime, _pryingQuality, () => !canEv.Cancelled); + 0f, ev.PryTimeModifier * PryTime, _pryingQuality); if (successfulPry && !IsWeldedShut) { diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index 5c06695e26..0734b30371 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -99,7 +99,7 @@ namespace Content.Server.Physics.Controllers // inputs will do different things. // TODO: Do that float speedCap; - var angularSpeed = 0.75f; + var angularSpeed = 0.075f; // ShuttleSystem has already worked out the ratio so we'll just multiply it back by the mass. var movement = (mover.VelocityDir.walking + mover.VelocityDir.sprinting); diff --git a/Content.Server/Shuttles/DockingSystem.cs b/Content.Server/Shuttles/DockingSystem.cs new file mode 100644 index 0000000000..952f7a7a7e --- /dev/null +++ b/Content.Server/Shuttles/DockingSystem.cs @@ -0,0 +1,494 @@ +using System; +using Content.Server.Doors.Components; +using Content.Server.Power.Components; +using Content.Shared.Doors; +using Content.Shared.Shuttles; +using Content.Shared.Verbs; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Log; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Physics.Dynamics.Joints; +using Robust.Shared.Utility; +using Robust.Shared.ViewVariables; + +namespace Content.Server.Shuttles +{ + [RegisterComponent] + public sealed class DockingComponent : SharedDockingComponent + { + [ViewVariables] + public DockingComponent? DockedWith; + + [ViewVariables] + public Joint? DockJoint; + + [ViewVariables] + public override bool Docked => DockedWith != null; + } + + public sealed class DockingSystem : EntitySystem + { + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly SharedBroadphaseSystem _broadphaseSystem = default!; + [Dependency] private readonly SharedJointSystem _jointSystem = default!; + + private const string DockingFixture = "docking"; + private const string DockingJoint = "docking"; + private const float DockingRadius = 0.20f; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnPowerChange); + SubscribeLocalEvent(OnAnchorChange); + + SubscribeLocalEvent(OnVerb); + SubscribeLocalEvent(OnAutoClose); + SubscribeLocalEvent(OnDoorOpenAttempt); + SubscribeLocalEvent(OnDoorCloseAttempt); + } + + // Won't allow users to override door controls + private void OnDoorOpenAttempt(EntityUid uid, DockingComponent component, DoorOpenAttemptEvent args) + { + args.Cancel(); + } + + private void OnDoorCloseAttempt(EntityUid uid, DockingComponent component, DoorCloseAttemptEvent args) + { + args.Cancel(); + } + + private void OnAutoClose(EntityUid uid, DockingComponent component, BeforeDoorAutoCloseEvent args) + { + // We'll just pin the door open when docked. + if (component.Docked) + args.Cancel(); + } + + private void OnVerb(EntityUid uid, DockingComponent component, GetInteractionVerbsEvent args) + { + if (!args.CanInteract || + !args.CanAccess) return; + + Verb? verb; + + // TODO: Have it open the UI and have the UI do this. + if (!component.Docked && + EntityManager.TryGetComponent(uid, out PhysicsComponent? body) && + EntityManager.TryGetComponent(uid, out TransformComponent? xform)) + { + DockingComponent? otherDock = null; + + if (component.Enabled) + otherDock = GetDockable(body, xform); + + verb = new Verb + { + Disabled = otherDock == null, + Text = Loc.GetString("docking-component-dock"), + Act = () => + { + if (otherDock == null) return; + TryDock(component, otherDock); + } + }; + } + else if (component.Docked) + { + verb = new Verb + { + Disabled = !component.Docked, + Text = Loc.GetString("docking-component-undock"), + Act = () => + { + if (component.DockedWith == null || !component.Enabled) return; + + Undock(component); + } + }; + } + else + { + return; + } + + args.Verbs.Add(verb); + } + + private DockingComponent? GetDockable(PhysicsComponent body, TransformComponent dockingXform) + { + // Did you know Saltern is the most dockable station? + + // Assume the docking port itself (and its body) is valid + + if (!_mapManager.TryGetGrid(dockingXform.GridID, out var grid) || + !EntityManager.HasComponent(grid.GridEntityId)) return null; + + var transform = body.GetTransform(); + var dockingFixture = body.GetFixture(DockingFixture); + + if (dockingFixture == null) + { + DebugTools.Assert(false); + Logger.ErrorS("docking", $"Found null fixture on {EntityManager.GetEntity(body.OwnerUid)}"); + return null; + } + + Box2? aabb = null; + + for (var i = 0; i < dockingFixture.Shape.ChildCount; i++) + { + aabb = aabb?.Union(dockingFixture.Shape.ComputeAABB(transform, i)) ?? dockingFixture.Shape.ComputeAABB(transform, i); + } + + if (aabb == null) return null; + + var enlargedAABB = aabb.Value.Enlarged(DockingRadius * 1.5f); + + // Get any docking ports in range on other grids. + _mapManager.FindGridsIntersectingEnumerator(dockingXform.MapID, enlargedAABB, out var enumerator); + + while (enumerator.MoveNext(out var otherGrid)) + { + if (otherGrid.Index == dockingXform.GridID) continue; + + foreach (var ent in otherGrid.GetAnchoredEntities(enlargedAABB)) + { + if (!EntityManager.TryGetComponent(ent, out DockingComponent? otherDocking) || + !otherDocking.Enabled || + !EntityManager.TryGetComponent(ent, out PhysicsComponent? otherBody)) continue; + + var otherTransform = otherBody.GetTransform(); + var otherDockingFixture = otherBody.GetFixture(DockingFixture); + + if (otherDockingFixture == null) + { + DebugTools.Assert(false); + Logger.ErrorS("docking", $"Found null docking fixture on {EntityManager.GetEntity(ent)}"); + continue; + } + + for (var i = 0; i < otherDockingFixture.Shape.ChildCount; i++) + { + var otherAABB = otherDockingFixture.Shape.ComputeAABB(otherTransform, i); + + if (!aabb.Value.Intersects(otherAABB)) continue; + + // TODO: Need CollisionManager's GJK for accurate bounds + // Realistically I want 2 fixtures anyway but I'll deal with that later. + return otherDocking; + } + } + } + + return null; + } + + private void OnShutdown(EntityUid uid, DockingComponent component, ComponentShutdown args) + { + if (component.DockedWith == null || + EntityManager.GetComponent(uid).EntityLifeStage > EntityLifeStage.MapInitialized) return; + + Cleanup(component); + } + + private void Cleanup(DockingComponent dockA) + { + _jointSystem.RemoveJoint(dockA.DockJoint!); + + var dockB = dockA.DockedWith; + + if (dockB == null || dockA.DockJoint == null) + { + DebugTools.Assert(false); + Logger.Error("docking", $"Tried to cleanup {dockA.OwnerUid} but not docked?"); + + dockA.DockedWith = null; + if (dockA.DockJoint != null) + { + // We'll still cleanup the dock joint on release at least + _jointSystem.RemoveJoint(dockA.DockJoint); + } + + return; + } + + dockB.DockedWith = null; + dockB.DockJoint = null; + + dockA.DockJoint = null; + dockA.DockedWith = null; + + // If these grids are ever invalid then need to look at fixing ordering for unanchored events elsewhere. + var gridAUid = _mapManager.GetGrid(EntityManager.GetComponent(dockA.OwnerUid).GridID).GridEntityId; + var gridBUid = _mapManager.GetGrid(EntityManager.GetComponent(dockB.OwnerUid).GridID).GridEntityId; + + var msg = new UndockEvent + { + DockA = dockA, + DockB = dockB, + GridAUid = gridAUid, + GridBUid = gridBUid, + }; + + EntityManager.EventBus.RaiseLocalEvent(dockA.OwnerUid, msg, false); + EntityManager.EventBus.RaiseLocalEvent(dockB.OwnerUid, msg, false); + EntityManager.EventBus.RaiseEvent(EventSource.Local, msg); + } + + private void OnStartup(EntityUid uid, DockingComponent component, ComponentStartup args) + { + // Use startup so transform already initialized + if (!EntityManager.GetComponent(uid).Anchored) return; + + EnableDocking(uid, component); + } + + private void OnAnchorChange(EntityUid uid, DockingComponent component, ref AnchorStateChangedEvent args) + { + if (args.Anchored) + { + EnableDocking(uid, component); + } + else + { + DisableDocking(uid, component); + } + } + + private void OnPowerChange(EntityUid uid, DockingComponent component, PowerChangedEvent args) + { + if (args.Powered) + { + EnableDocking(uid, component); + } + else + { + DisableDocking(uid, component); + } + } + + private void DisableDocking(EntityUid uid, DockingComponent component) + { + if (!component.Enabled) return; + + component.Enabled = false; + + if (component.DockedWith != null) + { + Undock(component); + } + + if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) + { + return; + } + + _broadphaseSystem.DestroyFixture(physicsComponent, DockingFixture); + } + + private void EnableDocking(EntityUid uid, DockingComponent component) + { + if (component.Enabled) + return; + + if (!EntityManager.TryGetComponent(uid, out PhysicsComponent? physicsComponent)) + return; + + component.Enabled = true; + + // TODO: WTF IS THIS GARBAGE + var shape = new PhysShapeCircle + { + // Want half of the unit vector + Position = new Vector2(0f, -0.5f), + Radius = DockingRadius + }; + + // Listen it makes intersection tests easier; you can probably dump this but it requires a bunch more boilerplate + var fixture = new Fixture(physicsComponent, shape) + { + ID = DockingFixture, + Hard = false, + }; + + // TODO: I want this to ideally be 2 fixtures to force them to have some level of alignment buuuttt + // I also need collisionmanager for that yet again so they get dis. + _broadphaseSystem.CreateFixture(physicsComponent, fixture); + } + + /// + /// Docks 2 ports together and assumes it is valid. + /// + private void Dock(DockingComponent dockA, DockingComponent dockB) + { + Logger.DebugS("docking", $"Docking between {dockA.Owner} and {dockB.Owner}"); + + // https://gamedev.stackexchange.com/questions/98772/b2distancejoint-with-frequency-equal-to-0-vs-b2weldjoint + + // We could also potentially use a prismatic joint? Depending if we want clamps that can extend or whatever + + var dockAXform = EntityManager.GetComponent(dockA.OwnerUid); + var dockBXform = EntityManager.GetComponent(dockB.OwnerUid); + + var gridA = _mapManager.GetGrid(dockAXform.GridID).GridEntityId; + var gridB = _mapManager.GetGrid(dockBXform.GridID).GridEntityId; + + SharedJointSystem.LinearStiffness( + 2f, + 0.7f, + EntityManager.GetComponent(gridA).Mass, + EntityManager.GetComponent(gridB).Mass, + out var stiffness, + out var damping); + + // These need playing around with + // Could also potentially have collideconnected false and stiffness 0 but it was a bit more suss??? + var joint = _jointSystem.CreateWeldJoint(gridA, gridB, DockingJoint + dockA.OwnerUid); + + var gridAXform = EntityManager.GetComponent(gridA); + var gridBXform = EntityManager.GetComponent(gridB); + + var anchorA = dockAXform.LocalPosition + dockAXform.LocalRotation.ToWorldVec() / 2f; + var anchorB = dockBXform.LocalPosition + dockBXform.LocalRotation.ToWorldVec() / 2f; + + joint.LocalAnchorA = anchorA; + joint.LocalAnchorB = anchorB; + joint.ReferenceAngle = (float) (gridBXform.WorldRotation - gridAXform.WorldRotation); + joint.CollideConnected = true; + joint.Stiffness = stiffness; + joint.Damping = damping; + + dockA.DockedWith = dockB; + dockB.DockedWith = dockA; + dockA.DockJoint = joint; + dockB.DockJoint = joint; + + if (EntityManager.TryGetComponent(dockA.OwnerUid, out ServerDoorComponent? doorA)) + { + doorA.Open(); + } + + if (EntityManager.TryGetComponent(dockB.OwnerUid, out ServerDoorComponent? doorB)) + { + doorB.Open(); + } + + var msg = new DockEvent + { + DockA = dockA, + DockB = dockB, + GridAUid = gridA, + GridBUid = gridB, + }; + + EntityManager.EventBus.RaiseLocalEvent(dockA.OwnerUid, msg, false); + EntityManager.EventBus.RaiseLocalEvent(dockB.OwnerUid, msg, false); + EntityManager.EventBus.RaiseEvent(EventSource.Local, msg); + } + + /// + /// Attempts to dock 2 ports together and will return early if it's not possible. + /// + private void TryDock(DockingComponent dockA, DockingComponent dockB) + { + if (!EntityManager.TryGetComponent(dockA.OwnerUid, out PhysicsComponent? bodyA) || + !EntityManager.TryGetComponent(dockB.OwnerUid, out PhysicsComponent? bodyB) || + !dockA.Enabled || + !dockB.Enabled) + { + return; + } + + var fixtureA = bodyA.GetFixture(DockingFixture); + var fixtureB = bodyB.GetFixture(DockingFixture); + + if (fixtureA == null || fixtureB == null) + { + return; + } + + var transformA = bodyA.GetTransform(); + var transformB = bodyB.GetTransform(); + var intersect = false; + + for (var i = 0; i < fixtureA.Shape.ChildCount; i++) + { + var aabb = fixtureA.Shape.ComputeAABB(transformA, i); + + for (var j = 0; j < fixtureB.Shape.ChildCount; j++) + { + var otherAABB = fixtureB.Shape.ComputeAABB(transformB, j); + if (!aabb.Intersects(otherAABB)) continue; + + // TODO: Need collisionmanager's GJK for accurate checks don't @ me son + intersect = true; + break; + } + + if (intersect) break; + } + + if (!intersect) return; + + Dock(dockA, dockB); + } + + private void Undock(DockingComponent dock) + { + if (dock.DockedWith == null) + { + DebugTools.Assert(false); + Logger.ErrorS("docking", $"Tried to undock {dock.OwnerUid} but not docked with anything?"); + return; + } + + if (EntityManager.TryGetComponent(dock.OwnerUid, out ServerDoorComponent? doorA)) + { + doorA.Close(); + } + + if (EntityManager.TryGetComponent(dock.DockedWith.OwnerUid, out ServerDoorComponent? doorB)) + { + doorB.Close(); + } + + // Could maybe give the shuttle a light push away, or at least if there's no other docks left? + + Cleanup(dock); + } + + /// + /// Raised whenever 2 airlocks dock. + /// + public sealed class DockEvent : EntityEventArgs + { + public DockingComponent DockA = default!; + public DockingComponent DockB = default!; + + public EntityUid GridAUid = default!; + public EntityUid GridBUid = default!; + } + + /// + /// Raised whenever 2 grids undock. + /// + public sealed class UndockEvent : EntityEventArgs + { + public DockingComponent DockA = default!; + public DockingComponent DockB = default!; + + public EntityUid GridAUid = default!; + public EntityUid GridBUid = default!; + } + } +} diff --git a/Content.Server/Shuttles/ShuttleComponent.cs b/Content.Server/Shuttles/ShuttleComponent.cs index 5cbf09536d..952b3bda19 100644 --- a/Content.Server/Shuttles/ShuttleComponent.cs +++ b/Content.Server/Shuttles/ShuttleComponent.cs @@ -4,8 +4,5 @@ using Robust.Shared.GameObjects; namespace Content.Server.Shuttles { [RegisterComponent] - public class ShuttleComponent : SharedShuttleComponent - { - - } + public sealed class ShuttleComponent : SharedShuttleComponent {} } diff --git a/Content.Server/Shuttles/ShuttleSystem.cs b/Content.Server/Shuttles/ShuttleSystem.cs index e8cb2977f6..45d3e1c4c9 100644 --- a/Content.Server/Shuttles/ShuttleSystem.cs +++ b/Content.Server/Shuttles/ShuttleSystem.cs @@ -126,7 +126,7 @@ namespace Content.Server.Shuttles //component.FixedRotation = false; TODO WHEN ROTATING SHUTTLES FIXED. component.FixedRotation = false; component.LinearDamping = 0.2f; - component.AngularDamping = 0.1f; + component.AngularDamping = 0.3f; } private void Disable(PhysicsComponent component) diff --git a/Content.Shared/Doors/SharedDoorComponent.cs b/Content.Shared/Doors/SharedDoorComponent.cs index e85da53ff9..812043d6ea 100644 --- a/Content.Shared/Doors/SharedDoorComponent.cs +++ b/Content.Shared/Doors/SharedDoorComponent.cs @@ -11,7 +11,7 @@ using Robust.Shared.ViewVariables; namespace Content.Shared.Doors { - [NetworkedComponent()] + [NetworkedComponent] public abstract class SharedDoorComponent : Component { public override string Name => "Door"; @@ -177,4 +177,14 @@ namespace Content.Shared.Doors CurTime = curTime; } } + + public sealed class DoorOpenAttemptEvent : CancellableEntityEventArgs + { + + } + + public sealed class DoorCloseAttemptEvent : CancellableEntityEventArgs + { + + } } diff --git a/Content.Shared/Shuttles/SharedDockingComponent.cs b/Content.Shared/Shuttles/SharedDockingComponent.cs new file mode 100644 index 0000000000..30baf5be43 --- /dev/null +++ b/Content.Shared/Shuttles/SharedDockingComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.GameStates; +using Robust.Shared.ViewVariables; + +namespace Content.Shared.Shuttles +{ + public abstract class SharedDockingComponent : Component + { + // Yes I left this in for now because there's no overhead and we'll need a client one later anyway + // and I was too lazy to delete it. + public override string Name => "Docking"; + + [ViewVariables] + public bool Enabled = false; + + public abstract bool Docked { get; } + } +} diff --git a/Resources/Audio/Effects/docking.ogg b/Resources/Audio/Effects/docking.ogg new file mode 100644 index 0000000000..5f94a19b88 Binary files /dev/null and b/Resources/Audio/Effects/docking.ogg differ diff --git a/Resources/Locale/en-US/shuttles/docking.ftl b/Resources/Locale/en-US/shuttles/docking.ftl new file mode 100644 index 0000000000..cda14bbeac --- /dev/null +++ b/Resources/Locale/en-US/shuttles/docking.ftl @@ -0,0 +1,2 @@ +docking-component-dock = dock +docking-component-undock = undock diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml new file mode 100644 index 0000000000..0c959111aa --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/shuttle.yml @@ -0,0 +1,88 @@ +- type: entity + id: AirlockShuttle + parent: BaseStructure + name: airlock + description: Necessary for connecting two space craft together. + components: + - type: Docking + - type: InteractionOutline + - type: Sprite + netsync: false + sprite: Structures/Doors/Airlocks/Standard/shuttle.rsi + layers: + - state: closed + map: ["enum.DoorVisualLayers.Base"] + #- state: closed_unlit + # shader: unshaded + # map: ["enum.DoorVisualLayers.BaseUnlit"] + #- state: welded + # map: ["enum.DoorVisualLayers.BaseWelded"] + #- state: bolted_unlit + # shader: unshaded + # map: ["enum.DoorVisualLayers.BaseBolted"] + #- state: panel_open + # map: ["enum.WiresVisualLayers.MaintenancePanel"] + - type: Physics + fixtures: + - shape: + !type:PhysShapeAabb + bounds: "-0.49,-0.49,0.49,0.49" # don't want this colliding with walls or they won't close + mass: 100 + mask: + - MobImpassable + layer: + - Opaque + - Impassable + - MobImpassable + - VaultImpassable + - SmallImpassable + - type: Door + closeTimeTwo: 0.4 + openTimeTwo: 0.4 + board: DoorElectronics + crushDamage: + types: + Blunt: 15 + openSound: + path: /Audio/Effects/docking.ogg + closeSound: + path: /Audio/Effects/docking.ogg + # denySound: + # path: /Audio/Machines/airlock_deny.ogg + - type: Airlock + - type: Appearance + visuals: + - type: AirlockVisualizer + simpleVisuals: true + # - type: WiresVisualizer + - type: ApcPowerReceiver + - type: ExtensionCableReceiver + #- type: Wires + # BoardName: "Airlock Control" + # LayoutId: Airlock + #- type: UserInterface + # interfaces: + # - key: enum.WiresUiKey.Key + # type: WiresBoundUserInterface + - type: Airtight + fixVacuum: true + - type: Occluder + - type: Damageable + damageContainer: Inorganic + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 500 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + #- type: Construction + # graph: airlock + # node: airlock + - type: IconSmooth + key: walls + mode: NoSprite + placement: + mode: SnapgridCenter diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/closed.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/closed.png new file mode 100644 index 0000000000..979c79540e Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/closed.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/closing.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/closing.png new file mode 100644 index 0000000000..905ba30ebc Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/closing.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/meta.json b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/meta.json new file mode 100644 index 0000000000..fb51d45117 --- /dev/null +++ b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/meta.json @@ -0,0 +1,47 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from CEV-Eris at commit https://github.com/discordia-space/CEV-Eris/commit/14517938186858388656a6aee14bf47af9e9649f", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "closed" + }, + { + "name": "open" + }, + { + "name": "closing", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + }, + { + "name": "opening", + "delays": [ + [ + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2 + ] + ] + } + ] +} diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/open.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/open.png new file mode 100644 index 0000000000..4a4e534504 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/open.png differ diff --git a/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/opening.png b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/opening.png new file mode 100644 index 0000000000..2e19084260 Binary files /dev/null and b/Resources/Textures/Structures/Doors/Airlocks/Standard/shuttle.rsi/opening.png differ