diff --git a/Content.Server/Construction/Conditions/AirlockBolted.cs b/Content.Server/Construction/Conditions/AirlockBolted.cs new file mode 100644 index 0000000000..d39f3fad02 --- /dev/null +++ b/Content.Server/Construction/Conditions/AirlockBolted.cs @@ -0,0 +1,43 @@ +#nullable enable +using Content.Server.GameObjects.Components.Doors; +using Content.Shared.Construction; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.Localization; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Utility; +using System.Threading.Tasks; + +namespace Content.Server.Construction.Conditions +{ + [UsedImplicitly] + [DataDefinition] + public class AirlockBolted : IEdgeCondition + { + [DataField("value")] + public bool Value { get; private set; } = true; + + public async Task Condition(IEntity entity) + { + if (!entity.TryGetComponent(out AirlockComponent? airlock)) return true; + + return airlock.BoltsDown == Value; + } + + public bool DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange) + { + if (!entity.TryGetComponent(out AirlockComponent? airlock)) return false; + + if (airlock.BoltsDown != Value) + { + if (Value == true) + message.AddMarkup(Loc.GetString("construction-condition-airlock-bolt", ("entityName", entity.Name)) + "\n"); + else + message.AddMarkup(Loc.GetString("construction-condition-airlock-unbolt", ("entityName", entity.Name)) + "\n"); + return true; + } + + return false; + } + } +} diff --git a/Content.Server/Construction/Conditions/DoorWelded.cs b/Content.Server/Construction/Conditions/DoorWelded.cs index 9de8d209f7..b7b0661721 100644 --- a/Content.Server/Construction/Conditions/DoorWelded.cs +++ b/Content.Server/Construction/Conditions/DoorWelded.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +#nullable enable +using System.Threading.Tasks; using Content.Server.GameObjects.Components.Doors; using Content.Shared.Construction; using JetBrains.Annotations; @@ -14,29 +15,30 @@ namespace Content.Server.Construction.Conditions [DataDefinition] public class DoorWelded : IEdgeCondition { - [DataField("welded")] public bool Welded { get; private set; } = true; + [DataField("welded")] + public bool Welded { get; private set; } = true; public async Task Condition(IEntity entity) { if (!entity.TryGetComponent(out ServerDoorComponent? doorComponent)) return false; + return doorComponent.IsWeldedShut == Welded; } public bool DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange) { - if (!entity.TryGetComponent(out ServerDoorComponent? doorComponent)) return false; + if (!entity.TryGetComponent(out ServerDoorComponent? door)) return false; - if (doorComponent.State == DoorState.Closed && Welded) + if (door.IsWeldedShut != Welded) { - message.AddMarkup(Loc.GetString("First, weld the door.\n")); + if (Welded == true) + message.AddMarkup(Loc.GetString("construction-condition-door-weld", ("entityName", entity.Name)) + "\n"); + else + message.AddMarkup(Loc.GetString("construction-condition-door-unweld", ("entityName", entity.Name)) + "\n"); return true; } - if (!doorComponent.IsWeldedShut || Welded) return false; - - message.AddMarkup(Loc.GetString("First, unweld the door.\n")); - return true; - + return false; } } } diff --git a/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs b/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs index b1593b5e47..f7ba30ff9a 100644 --- a/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs +++ b/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using System; using System.Linq; using System.Threading; @@ -31,6 +31,8 @@ using Robust.Shared.Physics.Collision; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; using Timer = Robust.Shared.Timing.Timer; +using Content.Server.GameObjects.Components.Construction; +using Robust.Shared.Containers; namespace Content.Server.GameObjects.Components.Doors { @@ -42,6 +44,10 @@ namespace Content.Server.GameObjects.Components.Doors [ComponentDependency] private readonly IDoorCheck? _doorCheck = null; + [ViewVariables] + [DataField("board")] + private string? _boardPrototype; + public override DoorState State { get => base.State; @@ -164,6 +170,8 @@ namespace Content.Server.GameObjects.Components.Doors } SetAppearance(DoorVisualState.Welded); } + + CreateDoorElectronicsBoard(); } public override void OnRemove() @@ -185,6 +193,8 @@ namespace Content.Server.GameObjects.Components.Doors } QuickOpen(); } + + CreateDoorElectronicsBoard(); } void IActivate.Activate(ActivateEventArgs eventArgs) @@ -638,6 +648,39 @@ namespace Content.Server.GameObjects.Components.Doors return false; } + /// + /// Creates the corresponding door electronics board on the door. + /// This exists so when you deconstruct doors that were serialized with the map, + /// you can retrieve the door electronics board. + /// + private void CreateDoorElectronicsBoard() + { + // Ensure that the construction component is aware of the board container. + if (Owner.TryGetComponent(out ConstructionComponent? construction)) + construction.AddContainer("board"); + + // We don't do anything if this is null or empty. + if (string.IsNullOrEmpty(_boardPrototype)) + return; + + var container = Owner.EnsureContainer("board", out var existed); + + return; + /* // TODO ShadowCommander: Re-enable when access is added to boards. Requires map update. + if (existed) + { + // We already contain a board. Note: We don't check if it's the right one! + if (container.ContainedEntities.Count != 0) + return; + } + + var board = Owner.EntityManager.SpawnEntity(_boardPrototype, Owner.Transform.Coordinates); + + if(!container.Insert(board)) + Logger.Warning($"Couldn't insert board {board} into door {Owner}!"); + */ + } + public override ComponentState GetComponentState(ICommonSession player) { return new DoorComponentState(State, StateChangeStartTime, CurrentlyCrushing, GameTiming.CurTime); diff --git a/Resources/Locale/en-US/construction/conditions.ftl b/Resources/Locale/en-US/construction/conditions.ftl new file mode 100644 index 0000000000..c9af878fb3 --- /dev/null +++ b/Resources/Locale/en-US/construction/conditions.ftl @@ -0,0 +1,7 @@ +# DoorWelded +construction-condition-door-weld = First, weld the {$entityName}. +construction-condition-door-unweld = First, unweld the {$entityName}. + +# AirlockBolted +construction-condition-airlock-bolt = First, bolt the {$entityName}. +construction-condition-airlock-unbolt = First, unbolt the {$entityName}. diff --git a/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml b/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml index 326da5d7d5..ec54f10959 100644 --- a/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml +++ b/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml @@ -24,6 +24,7 @@ - state: panel_open map: ["enum.WiresVisualLayers.MaintenancePanel"] - type: Physics + mass: 100 fixtures: - shape: !type:PhysShapeAabb @@ -37,6 +38,7 @@ - VaultImpassable - SmallImpassable - type: Door + board: DoorElectronics - type: Airlock - type: Appearance visuals: @@ -68,8 +70,58 @@ behaviors: - !type:DoActsBehavior acts: ["Destruction"] + - type: Construction + graph: airlock + node: airlock - type: IconSmooth key: walls mode: NoSprite placement: mode: SnapgridCenter + +- type: entity + id: AirlockAssembly + name: airlock assembly + description: It opens, it closes, and maybe crushes you. + components: + - type: Clickable + - type: RCDDeconstructWhitelist + - type: InteractionOutline + - type: Sprite + netsync: false + drawdepth: Mobs + sprite: Constructible/Structures/Doors/Standard/basic.rsi + state: "assembly" + - type: Physics + mass: 100 + fixtures: + - shape: + !type:PhysShapeAabb + bounds: "-0.49,-0.49,0.49,0.49" + mask: + - MobImpassable + layer: + - MobImpassable + - VaultImpassable + - type: Anchorable + snap: true + - type: Pullable + - type: SnapGrid + offset: Center + - type: Damageable + resistances: metallicResistances + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - type: Construction + graph: airlock + node: assembly + placement: + mode: SnapgridCenter + + diff --git a/Resources/Prototypes/Entities/Objects/Devices/Electronics/door.yml b/Resources/Prototypes/Entities/Objects/Devices/Electronics/door.yml new file mode 100644 index 0000000000..e44eca397b --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/Electronics/door.yml @@ -0,0 +1,12 @@ +- type: entity + id: DoorElectronics + parent: BaseItem + name: door electronics + components: + - type: Tag + tags: + - DoorElectronics + - type: Sprite + sprite: Constructible/Misc/module.rsi + state: door_electronics + - type: AccessReader diff --git a/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml b/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml index b439ea6730..feda6998d2 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/cable_coils.yml @@ -79,6 +79,10 @@ - coillv-10 - coillv-20 - coillv-30 + - type: Material + materials: + - key: enum.MaterialKeys.Stack + mat: Cable - type: entity parent: ApcExtensionCableStack @@ -90,7 +94,6 @@ - type: Item size: 3 - type: Stack - stackType: ApcExtensionCableStack1 count: 1 - type: entity diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/airlock.yml b/Resources/Prototypes/Recipes/Construction/Graphs/airlock.yml new file mode 100644 index 0000000000..cd744c5983 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/airlock.yml @@ -0,0 +1,107 @@ +- type: constructionGraph + id: airlock + start: start + graph: + - node: start + edges: + - to: assembly + completed: + - !type:SetAnchor + value: false + steps: + - material: Steel + amount: 4 + doAfter: 2 + + - node: assembly + entity: AirlockAssembly + actions: + - !type:SpriteStateChange + state: assembly + - !type:SnapToGrid {} + - !type:SetAnchor {} + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + steps: + - material: Cable + amount: 5 + doAfter: 1 + - to: start + conditions: + - !type:EntityAnchored + anchored: false + completed: + - !type:SpawnPrototype + prototype: SheetSteel1 + amount: 4 + - !type:DeleteEntity {} + steps: + - tool: Welding + doAfter: 2 + + - node: wired + entity: AirlockAssembly + edges: + - to: electronics + conditions: + - !type:EntityAnchored {} + steps: + - tag: DoorElectronics + store: board + name: "door electronics circuit board" + icon: + sprite: "Constructible/Misc/module.rsi" + state: "door_electronics" + doAfter: 1 + - to: assembly + completed: + - !type:SpawnPrototype + prototype: ApcExtensionCableStack1 + amount: 5 + steps: + - tool: Cutting + doAfter: 1 + + - node: electronics + edges: + - to: airlock + conditions: + - !type:EntityAnchored {} + steps: + - tool: Screwing + doAfter: 2 + + - node: airlock + entity: Airlock + edges: + - to: wired + conditions: + - !type:EntityAnchored {} + - !type:DoorWelded {} + - !type:AirlockBolted + value: false + - !type:WirePanel {} + - !type:ContainerNotEmpty # TODO ShadowCommander: Remove when map gets updated + container: board + completed: + - !type:EmptyAllContainers {} + steps: + - tool: Prying + doAfter: 1 + - to: wired # TODO ShadowCommander: Remove when board spawning is implemented in ServerDoorComponent.cs. Needs a map update. + conditions: + - !type:EntityAnchored {} + - !type:DoorWelded {} + - !type:AirlockBolted + value: false + - !type:WirePanel {} + - !type:ContainerEmpty + container: board + completed: + - !type:SpawnPrototype + prototype: DoorElectronics + steps: + - tool: Prying + doAfter: 1 diff --git a/Resources/Prototypes/Recipes/Construction/structures.yml b/Resources/Prototypes/Recipes/Construction/structures.yml index f3adfd4023..8c4bb39fa7 100644 --- a/Resources/Prototypes/Recipes/Construction/structures.yml +++ b/Resources/Prototypes/Recipes/Construction/structures.yml @@ -213,3 +213,20 @@ canBuildInImpassable: false conditions: - !type:TileNotBlocked {} + +- type: construction + name: Airlock + id: airlock + graph: airlock + startNode: start + targetNode: airlock + category: Structures + description: It opens, it closes, and maybe crushes you. + icon: + sprite: Constructible/Structures/Doors/Standard/basic.rsi + state: assembly + objectType: Structure + placementMode: SnapgridCenter + canBuildInImpassable: false + conditions: + - !type:TileNotBlocked {} diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 576b057a1f..ba94898ed7 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -4,6 +4,9 @@ - type: Tag id: ConveyorAssembly +- type: Tag + id: DoorElectronics + - type: Tag id: ExplosivePassable