From 68682f1f55e201b1f72c5ab768d6c25977aeb0b0 Mon Sep 17 00:00:00 2001 From: Aiden Date: Tue, 15 Oct 2024 18:05:13 -0500 Subject: [PATCH 01/76] Fix Reverse Engineering UI (#1053) # Description Title Upstream cherry pick fucked it, this fixes it. ---

Media

### Before ![image](https://github.com/user-attachments/assets/b8d3876a-2c73-44e0-b9de-9650ee5074ae) ### After ![image](https://github.com/user-attachments/assets/49859d45-c6e0-4d03-84fa-de8bcb00de11)

--- # Changelog :cl: - fix: Reverse engineering machine UI works again. --- .../ReverseEngineering/ReverseEngineeringSystem.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Content.Server/Nyanotrasen/ReverseEngineering/ReverseEngineeringSystem.cs b/Content.Server/Nyanotrasen/ReverseEngineering/ReverseEngineeringSystem.cs index f4e5a1c419..88da3093d7 100644 --- a/Content.Server/Nyanotrasen/ReverseEngineering/ReverseEngineeringSystem.cs +++ b/Content.Server/Nyanotrasen/ReverseEngineering/ReverseEngineeringSystem.cs @@ -187,22 +187,18 @@ private void UpdateUserInterface(EntityUid uid, ReverseEngineeringMachineCompone if (!Resolve(uid, ref component)) return; - if (!_ui.TryGetOpenUi(uid, ReverseEngineeringMachineUiKey.Key, out var bui)) - return; - EntityUid? item = component.CurrentItem; if (component.CachedMessage == null) component.CachedMessage = GetReverseEngineeringScanMessage(component); - var totalTime = TimeSpan.Zero; var scanning = TryComp(uid, out var active); - var canScan = (item != null && !scanning); + var canScan = item != null && !scanning; var remaining = active != null ? _timing.CurTime - active.StartTime : TimeSpan.Zero; EntityManager.TryGetNetEntity(item, out var netItem); var state = new ReverseEngineeringMachineScanUpdateState(netItem, canScan, component.CachedMessage, scanning, component.SafetyOn, component.AutoScan, component.Progress, remaining, component.AnalysisDuration); - _ui.SetUiState(bui.Owner, bui.UiKey, state); + _ui.SetUiState(uid, ReverseEngineeringMachineUiKey.Key, state); } private ReverseEngineeringTickResult Roll(ReverseEngineeringMachineComponent component, out int actualRoll) From 92c22ab325e68e65d268c059fed7ae4b601bbecf Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Tue, 15 Oct 2024 23:05:37 +0000 Subject: [PATCH 02/76] Automatic Changelog Update (#1053) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9afa8c8ad1..e25d4add57 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7249,3 +7249,10 @@ Entries: id: 6448 time: '2024-10-14T16:10:08.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/944 +- author: Aidenkrz + changes: + - type: Fix + message: Reverse engineering machine UI works again. + id: 6449 + time: '2024-10-15T23:05:13.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1053 From e98e9ff7f67afba7402d510102569b1a5b0b322d Mon Sep 17 00:00:00 2001 From: Aiden Date: Tue, 15 Oct 2024 18:05:44 -0500 Subject: [PATCH 03/76] Fix Arachne Turning Into Errors when They Take Damage (#1054) # Description title --- # Media im lazy, imagine arachne but they arent big errors. --- # Changelog :cl: - fix: Arachne no longer turn into errors when they take damage --- Resources/Prototypes/Entities/Mobs/Species/arachne.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachne.yml b/Resources/Prototypes/Entities/Mobs/Species/arachne.yml index a42e40b5a4..e704f15725 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachne.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachne.yml @@ -113,7 +113,6 @@ webRequired: true - type: Arachne - type: DamageVisuals - thresholds: [ 20, 40, 100 ] targetLayers: - "enum.HumanoidVisualLayers.Chest" - "enum.HumanoidVisualLayers.Head" From 3466b3912be549cdbae1d80c6c837245c32c5424 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Tue, 15 Oct 2024 23:06:07 +0000 Subject: [PATCH 04/76] Automatic Changelog Update (#1054) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index e25d4add57..9396b4b4e0 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7256,3 +7256,10 @@ Entries: id: 6449 time: '2024-10-15T23:05:13.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1053 +- author: Aidenkrz + changes: + - type: Fix + message: Arachne no longer turn into errors when they take damage + id: 6450 + time: '2024-10-15T23:05:44.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1054 From db5cfce4d301bd4478231580dd5ea6176db4a736 Mon Sep 17 00:00:00 2001 From: Aiden Date: Tue, 15 Oct 2024 18:07:02 -0500 Subject: [PATCH 05/76] Cocoon Sizing Bettering (#1055) # Description Cocoon size is now based on the humanoids height/width, or the sprite scale/size. Also makes it so we aren't calling a command to change the scale. ---

Media

### Large Oni https://github.com/user-attachments/assets/c26f6b34-39f7-437c-af86-715185855aff ### Small Felinid https://github.com/user-attachments/assets/5a82affb-b8b7-4b79-aa39-37f5a7a2e35e ### Mouse https://github.com/user-attachments/assets/42d7b41e-7333-4052-a1d1-698ce337706e ### Dragon https://github.com/user-attachments/assets/210eab03-416c-4795-a384-9e502fe794a2

--- # Changelog :cl: - tweak: Cocoon sizing has been changed to reflect the size of the entity inside better. --- Content.Client/Arachne/CocoonSystem.cs | 33 +++++++++++++++++++ Content.Server/Arachne/ArachneSystem.cs | 16 --------- .../Arachne/CocoonComponent.cs | 2 +- 3 files changed, 34 insertions(+), 17 deletions(-) create mode 100644 Content.Client/Arachne/CocoonSystem.cs rename {Content.Server => Content.Shared}/Arachne/CocoonComponent.cs (89%) diff --git a/Content.Client/Arachne/CocoonSystem.cs b/Content.Client/Arachne/CocoonSystem.cs new file mode 100644 index 0000000000..7645c5bc81 --- /dev/null +++ b/Content.Client/Arachne/CocoonSystem.cs @@ -0,0 +1,33 @@ +using Content.Shared.Arachne; +using Content.Shared.Humanoid; +using Robust.Client.GameObjects; +using Robust.Shared.Containers; +using System.Numerics; + +namespace Content.Client.Cocoon +{ + public sealed class CocoonSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnCocEntInserted); + } + + private void OnCocEntInserted(EntityUid uid, CocoonComponent component, EntInsertedIntoContainerMessage args) + { + if (!TryComp(uid, out var cocoonSprite)) + return; + + if (TryComp(args.Entity, out var humanoidAppearance)) // If humanoid, use height and width + cocoonSprite.Scale = new Vector2(humanoidAppearance.Width, humanoidAppearance.Height); + else if (!TryComp(args.Entity, out var entSprite)) + return; + else if (entSprite.BaseRSI != null) // Set scale based on sprite scale + sprite dimensions. Ideally we would somehow get a bounding box from the sprite size not including transparent pixels, but FUCK figuring that out. + cocoonSprite.Scale = entSprite.Scale * (entSprite.BaseRSI.Size / 32); + else if (entSprite.Scale != cocoonSprite.Scale) // if basersi somehow not found (?) just use scale + cocoonSprite.Scale = entSprite.Scale; + } + } +} diff --git a/Content.Server/Arachne/ArachneSystem.cs b/Content.Server/Arachne/ArachneSystem.cs index 9cdefb441b..27e8851247 100644 --- a/Content.Server/Arachne/ArachneSystem.cs +++ b/Content.Server/Arachne/ArachneSystem.cs @@ -1,5 +1,4 @@ using Content.Shared.Arachne; -using Content.Shared.Actions; using Content.Shared.IdentityManagement; using Content.Shared.Verbs; using Content.Shared.Buckle.Components; @@ -8,20 +7,16 @@ using Content.Shared.Eye.Blinding.Systems; using Content.Shared.Containers.ItemSlots; using Content.Shared.Damage; -using Content.Shared.Inventory; using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Humanoid; -using Content.Shared.Nutrition.EntitySystems; using Content.Server.Buckle.Systems; using Content.Server.Popups; using Content.Server.DoAfter; using Content.Server.Body.Components; using Content.Server.Vampiric; using Content.Server.Speech.Components; -using Robust.Shared.Physics.Components; using Robust.Shared.Containers; -using Robust.Shared.Map; using Robust.Shared.Utility; using Robust.Server.Console; @@ -207,17 +202,6 @@ private void OnCocoonDoAfter(EntityUid uid, ArachneComponent component, ArachneC if (!TryComp(cocoon, out var slots)) return; - // todo: our species should use scale visuals probably... - // TODO: We need a client-accessible notion of scale influence here. - /* if (spawnProto == "CocoonedHumanoid" && TryComp(args.Args.Target.Value, out var sprite)) */ - /* { */ - /* // why the fuck is this only available as a console command. */ - /* _host.ExecuteCommand(null, "scale " + cocoon + " " + sprite.Scale.Y); */ - if (TryComp(args.Args.Target.Value, out var physics)) - { - var scale = Math.Clamp(1 / (35 / physics.FixturesMass), 0.35, 2.5); - _host.ExecuteCommand(null, "scale " + cocoon + " " + scale); - } _itemSlots.SetLock(cocoon, BodySlot, false, slots); _itemSlots.TryInsert(cocoon, BodySlot, args.Args.Target.Value, args.Args.User); _itemSlots.SetLock(cocoon, BodySlot, true, slots); diff --git a/Content.Server/Arachne/CocoonComponent.cs b/Content.Shared/Arachne/CocoonComponent.cs similarity index 89% rename from Content.Server/Arachne/CocoonComponent.cs rename to Content.Shared/Arachne/CocoonComponent.cs index 42ecf27971..e22e393ffd 100644 --- a/Content.Server/Arachne/CocoonComponent.cs +++ b/Content.Shared/Arachne/CocoonComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Arachne +namespace Content.Shared.Arachne { [RegisterComponent] public sealed partial class CocoonComponent : Component From 0ebdfb05066380fddbbd07940c6959d64905a6c9 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Tue, 15 Oct 2024 23:08:14 +0000 Subject: [PATCH 06/76] Automatic Changelog Update (#1055) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 9396b4b4e0..86977434ca 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7263,3 +7263,12 @@ Entries: id: 6450 time: '2024-10-15T23:05:44.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1054 +- author: Aidenkrz + changes: + - type: Tweak + message: >- + Cocoon sizing has been changed to reflect the size of the entity inside + better. + id: 6451 + time: '2024-10-15T23:07:03.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1055 From 1d1e6c910dc341021c3a1d3c6e66a5e6bcc76a10 Mon Sep 17 00:00:00 2001 From: Ichaie <167008606+Ichaie@users.noreply.github.com> Date: Tue, 15 Oct 2024 20:12:44 -0300 Subject: [PATCH 07/76] Update Gax (#1048) # Description This PR updates the Gax map, fixing some important things. --- # TODO ---

Media

![Example Media Embed](https://example.com/thisimageisntreal.png)

--- # Changelog :cl: - fix: Gax station: fixed arrivals being unable to dock due to meteor shielding and translated security camera names to English. --- Resources/Maps/gaxstation.yml | 546 ++++++++++++++++------------------ 1 file changed, 261 insertions(+), 285 deletions(-) diff --git a/Resources/Maps/gaxstation.yml b/Resources/Maps/gaxstation.yml index 2c46ebe9e7..1e2d999ab2 100644 --- a/Resources/Maps/gaxstation.yml +++ b/Resources/Maps/gaxstation.yml @@ -190,7 +190,7 @@ entities: version: 6 0,-1: ind: 0,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAA version: 6 1,-1: ind: 1,-1 @@ -230,11 +230,11 @@ entities: version: 6 4,0: ind: 4,0 - tiles: WQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAJgAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAA + tiles: WQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAWQAAAAAAeQAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAJgAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAeQAAAAAAeQAAAAAAaAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAA version: 6 4,-1: ind: 4,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAAAAAAAAAAAAAAAAA version: 6 3,-2: ind: 3,-2 @@ -294,7 +294,7 @@ entities: version: 6 5,0: ind: 5,0 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 -1,-1: ind: -1,-1 @@ -318,7 +318,7 @@ entities: version: 6 5,-1: ind: 5,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAeQAAAAAAeQAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeQAAAAAAeAAAAAAAeAAAAAAAeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 -3,5: ind: -3,5 @@ -11793,7 +11793,6 @@ entities: pos: -4.5,15.5 parent: 2 - type: DeviceNetwork - deviceLists: - 80 - uid: 381 @@ -40997,6 +40996,11 @@ entities: - type: Transform pos: -12.5,23.5 parent: 2 + - uid: 5068 + components: + - type: Transform + pos: 7.5,0.5 + parent: 2 - uid: 5989 components: - type: Transform @@ -41287,16 +41291,6 @@ entities: - type: Transform pos: 6.5,0.5 parent: 2 - - uid: 6061 - components: - - type: Transform - pos: 6.5,-0.5 - parent: 2 - - uid: 6062 - components: - - type: Transform - pos: 6.5,-1.5 - parent: 2 - uid: 6063 components: - type: Transform @@ -41347,11 +41341,6 @@ entities: - type: Transform pos: 11.5,-1.5 parent: 2 - - uid: 6073 - components: - - type: Transform - pos: 6.5,-2.5 - parent: 2 - uid: 6074 components: - type: Transform @@ -45259,11 +45248,6 @@ entities: - type: Transform pos: 83.5,14.5 parent: 2 - - uid: 18581 - components: - - type: Transform - pos: 88.5,6.5 - parent: 2 - uid: 18582 components: - type: Transform @@ -45284,21 +45268,6 @@ entities: - type: Transform pos: 89.5,8.5 parent: 2 - - uid: 18586 - components: - - type: Transform - pos: 88.5,5.5 - parent: 2 - - uid: 18587 - components: - - type: Transform - pos: 88.5,2.5 - parent: 2 - - uid: 18588 - components: - - type: Transform - pos: 88.5,1.5 - parent: 2 - uid: 18589 components: - type: Transform @@ -45324,31 +45293,6 @@ entities: - type: Transform pos: 87.5,-9.5 parent: 2 - - uid: 18594 - components: - - type: Transform - pos: 85.5,-9.5 - parent: 2 - - uid: 18595 - components: - - type: Transform - pos: 83.5,-8.5 - parent: 2 - - uid: 18596 - components: - - type: Transform - pos: 81.5,-8.5 - parent: 2 - - uid: 18597 - components: - - type: Transform - pos: 79.5,-9.5 - parent: 2 - - uid: 18598 - components: - - type: Transform - pos: 77.5,-9.5 - parent: 2 - uid: 18599 components: - type: Transform @@ -45364,16 +45308,6 @@ entities: - type: Transform pos: 87.5,-8.5 parent: 2 - - uid: 18602 - components: - - type: Transform - pos: 83.5,-6.5 - parent: 2 - - uid: 18603 - components: - - type: Transform - pos: 79.5,-6.5 - parent: 2 - uid: 18604 components: - type: Transform @@ -88493,6 +88427,21 @@ entities: rot: 3.141592653589793 rad pos: -33.5,80.5 parent: 2 + - uid: 6061 + components: + - type: Transform + pos: 88.5,5.5 + parent: 2 + - uid: 6062 + components: + - type: Transform + pos: 88.5,6.5 + parent: 2 + - uid: 6073 + components: + - type: Transform + pos: 88.5,2.5 + parent: 2 - uid: 8530 components: - type: Transform @@ -89374,45 +89323,40 @@ entities: rot: 3.141592653589793 rad pos: -37.5,77.5 parent: 2 - - uid: 17916 - components: - - type: Transform - pos: 86.5,8.5 - parent: 2 - - uid: 17917 + - uid: 17450 components: - type: Transform - pos: 86.5,-0.5 + pos: 88.5,1.5 parent: 2 - - uid: 17918 + - uid: 17916 components: - type: Transform - pos: 86.5,0.5 + pos: 83.5,-9.5 parent: 2 - uid: 17919 components: - type: Transform - pos: 86.5,5.5 + pos: 84.5,-9.5 parent: 2 - uid: 17920 components: - type: Transform - pos: 86.5,10.5 + pos: 86.5,-9.5 parent: 2 - uid: 17921 components: - type: Transform - pos: 86.5,11.5 + pos: 77.5,-9.5 parent: 2 - - uid: 17922 + - uid: 17923 components: - type: Transform - pos: 86.5,6.5 + pos: 88.5,13.5 parent: 2 - - uid: 17923 + - uid: 17924 components: - type: Transform - pos: 86.5,9.5 + pos: 88.5,8.5 parent: 2 - uid: 17925 components: @@ -89424,6 +89368,11 @@ entities: - type: Transform pos: 81.5,13.5 parent: 2 + - uid: 17927 + components: + - type: Transform + pos: 88.5,7.5 + parent: 2 - uid: 17928 components: - type: Transform @@ -89439,60 +89388,55 @@ entities: - type: Transform pos: 86.5,13.5 parent: 2 - - uid: 17934 + - uid: 17931 components: - type: Transform - pos: 80.5,-5.5 + pos: 88.5,-1.5 parent: 2 - - uid: 17935 + - uid: 17933 components: - type: Transform - pos: 81.5,-5.5 + pos: 88.5,-4.5 parent: 2 - - uid: 17936 + - uid: 17934 components: - type: Transform - pos: 86.5,2.5 + pos: 88.5,-2.5 parent: 2 - - uid: 17937 + - uid: 17935 components: - type: Transform - pos: 79.5,-5.5 + pos: 88.5,-5.5 parent: 2 - uid: 17938 components: - type: Transform - pos: 86.5,1.5 - parent: 2 - - uid: 17939 - components: - - type: Transform - pos: 84.5,-5.5 + pos: 88.5,4.5 parent: 2 - uid: 17940 components: - type: Transform - pos: 86.5,-2.5 + pos: 88.5,0.5 parent: 2 - uid: 17941 components: - type: Transform - pos: 86.5,-3.5 + pos: 88.5,10.5 parent: 2 - uid: 17943 components: - type: Transform - pos: 83.5,-5.5 + pos: 88.5,11.5 parent: 2 - - uid: 17944 + - uid: 17977 components: - type: Transform - pos: 86.5,-5.5 + pos: 88.5,9.5 parent: 2 - - uid: 17977 + - uid: 17978 components: - type: Transform - pos: 86.5,12.5 + pos: 88.5,-0.5 parent: 2 - uid: 17982 components: @@ -89507,52 +89451,47 @@ entities: - uid: 17984 components: - type: Transform - pos: 86.5,-1.5 + pos: 88.5,12.5 parent: 2 - uid: 17985 components: - type: Transform - pos: 86.5,3.5 + pos: 88.5,-3.5 parent: 2 - - uid: 17989 + - uid: 17986 components: - type: Transform - pos: 86.5,-4.5 + pos: 88.5,3.5 parent: 2 - uid: 17992 components: - type: Transform pos: 83.5,13.5 parent: 2 - - uid: 17993 - components: - - type: Transform - pos: 86.5,7.5 - parent: 2 - - uid: 17994 - components: - - type: Transform - pos: 86.5,4.5 - parent: 2 - - uid: 17996 + - uid: 18528 components: - type: Transform - pos: 82.5,-5.5 + pos: 80.5,-9.5 parent: 2 - uid: 18553 components: - type: Transform - pos: 85.5,-5.5 + pos: 79.5,-9.5 parent: 2 - uid: 18554 components: - type: Transform - pos: 78.5,-5.5 + pos: 78.5,-9.5 parent: 2 - uid: 18555 components: - type: Transform - pos: 77.5,-5.5 + pos: 82.5,-9.5 + parent: 2 + - uid: 18586 + components: + - type: Transform + pos: 85.5,-9.5 parent: 2 - uid: 19373 components: @@ -106569,7 +106508,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Sala do Gerador de Gravidade + id: Gravity - uid: 14962 components: - type: Transform @@ -106580,7 +106519,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Escritório do Engenheiro Chefe + id: CE - uid: 14963 components: - type: Transform @@ -106590,7 +106529,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Sala do Chefe de segurança + id: HOS - uid: 14964 components: - type: Transform @@ -106598,7 +106537,7 @@ entities: pos: -1.5,16.5 parent: 2 - type: SurveillanceCamera - id: Sala do Itendente de Carga + id: LO - uid: 14965 components: - type: Transform @@ -106609,7 +106548,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Servidores D&P + id: R&D Server - uid: 14966 components: - type: Transform @@ -106619,7 +106558,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Servidores de pesquisa + id: Research Server Room - uid: 14967 components: - type: Transform @@ -106629,7 +106568,7 @@ entities: - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraCommand - id: Escritório do Diretor de Pesquisas + id: RD - type: ActiveUserInterface - uid: 14968 components: @@ -106638,7 +106577,7 @@ entities: pos: 30.5,56.5 parent: 2 - type: SurveillanceCamera - id: Cofre + id: Vault - uid: 14969 components: - type: Transform @@ -106646,7 +106585,7 @@ entities: pos: 18.5,39.5 parent: 2 - type: SurveillanceCamera - id: Sala do Médico Chefe + id: CMO - uid: 14970 components: - type: Transform @@ -106654,7 +106593,7 @@ entities: pos: 76.5,45.5 parent: 2 - type: SurveillanceCamera - id: Sala da E.V.A + id: EVA Storage - uid: 14971 components: - type: Transform @@ -106662,14 +106601,14 @@ entities: pos: 76.5,40.5 parent: 2 - type: SurveillanceCamera - id: Sala do Chefe dos Funcionarios + id: HOP - uid: 14972 components: - type: Transform pos: 67.5,33.5 parent: 2 - type: SurveillanceCamera - id: Recepção + id: Front Desk - uid: 14973 components: - type: Transform @@ -106677,7 +106616,7 @@ entities: pos: 76.5,31.5 parent: 2 - type: SurveillanceCamera - id: Sala do capitão + id: Captain - uid: 14974 components: - type: Transform @@ -106685,7 +106624,7 @@ entities: pos: 78.5,25.5 parent: 2 - type: SurveillanceCamera - id: Quarto do capitão + id: Captain Bedroom - uid: 14976 components: - type: Transform @@ -106696,7 +106635,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Telecoms + id: Telecomms - uid: 14977 components: - type: Transform @@ -106706,7 +106645,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Monitoramento da Station AI + id: Station AI monitoring - uid: 14978 components: - type: Transform @@ -106716,7 +106655,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Exterior norte 1 do núcleo IA + id: IA Core North Exterior 1 - uid: 14979 components: - type: Transform @@ -106727,7 +106666,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Exterior oeste do núcleo IA + id: Western exterior of the IA core - uid: 14980 components: - type: Transform @@ -106738,7 +106677,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Exterior sul 2 do núcleo IA + id: IA Core South Exterior 2 - uid: 14981 components: - type: Transform @@ -106749,7 +106688,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Exterior sul 1 do núcleo IA + id: IA Core South Exterior 1 - uid: 14982 components: - type: Transform @@ -106760,7 +106699,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Exterior leste do núcleo IA + id: Eastern exterior of the IA core - uid: 14983 components: - type: Transform @@ -106770,7 +106709,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Exterior norte 2 do núcleo IA + id: IA Core North Exterior 2 - uid: 14984 components: - type: Transform @@ -106781,7 +106720,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Manutenção da IA + id: AI Maintenance - uid: 14985 components: - type: Transform @@ -106792,7 +106731,17 @@ entities: setupAvailableNetworks: - SurveillanceCameraCommand nameSet: True - id: Núcleo IA + id: AI Core + - uid: 18571 + components: + - type: Transform + pos: 69.5,23.5 + parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraCommand + nameSet: True + id: Captain Front Desk - proto: SurveillanceCameraEngineering entities: - uid: 14986 @@ -106805,14 +106754,14 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Câmara de combustão + id: Combustion Chamber - uid: 14987 components: - type: Transform pos: -7.5,38.5 parent: 2 - type: SurveillanceCamera - id: Entrada Da SMES + id: SMES Room Entrance - uid: 14988 components: - type: Transform @@ -106823,7 +106772,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Atmos Norte + id: Atmos North - uid: 14989 components: - type: Transform @@ -106833,14 +106782,14 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Atmos Sul + id: Atmos South - uid: 14990 components: - type: Transform pos: -16.5,18.5 parent: 2 - type: SurveillanceCamera - id: Corredor da Atmos + id: Atmos Corridor - uid: 14991 components: - type: Transform @@ -106851,7 +106800,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Recepção da Atmos + id: Atmos Reception - uid: 14992 components: - type: Transform @@ -106861,7 +106810,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Entrada da Atmosfera + id: Atmosphere Entry - uid: 14993 components: - type: Transform @@ -106872,7 +106821,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Sala dos SMES + id: SMES Room - uid: 14994 components: - type: Transform @@ -106883,7 +106832,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Oficina de Naves + id: Ship Workshop - uid: 14995 components: - type: Transform @@ -106894,7 +106843,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Recepção + id: Front desk - uid: 14996 components: - type: Transform @@ -106905,7 +106854,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Reator Super-Matéria norte + id: North Super-Matter Reactor - uid: 14997 components: - type: Transform @@ -106915,7 +106864,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Reator Super-Matéria sul + id: South Super-Matter Reactor - uid: 14998 components: - type: Transform @@ -106926,7 +106875,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Reator Super-Matéria oeste + id: West Super-Matter Reactor - uid: 14999 components: - type: Transform @@ -106937,7 +106886,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Reator Super-Matéria leste + id: East Super-Matter Reactor - uid: 15000 components: - type: Transform @@ -106948,7 +106897,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraEngineering nameSet: True - id: Gerador AME + id: AME Generator - proto: SurveillanceCameraGeneral entities: - uid: 15001 @@ -106957,14 +106906,14 @@ entities: pos: 19.5,47.5 parent: 2 - type: SurveillanceCamera - id: Corredor da Medbay Norte + id: Medbay North Corridor - uid: 15002 components: - type: Transform pos: 0.5,19.5 parent: 2 - type: SurveillanceCamera - id: Corredor cargo e engenharia + id: Cargo and engineering corridor - uid: 15003 components: - type: Transform @@ -106972,7 +106921,7 @@ entities: pos: 1.5,49.5 parent: 2 - type: SurveillanceCamera - id: Corredor da brig, Medbay e Engenharia + id: Sec Corridor, Medbay and Engineering - uid: 15004 components: - type: Transform @@ -106983,7 +106932,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraGeneral nameSet: True - id: Entrada da sala do detetive + id: Entrance to the detective's room - uid: 15005 components: - type: Transform @@ -106993,7 +106942,7 @@ entities: - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraGeneral - id: Entrada Norte da segurança + id: North security entrance - type: ActiveUserInterface - uid: 15006 components: @@ -107001,7 +106950,7 @@ entities: pos: 3.5,70.5 parent: 2 - type: SurveillanceCamera - id: Parque Sul + id: South Park - uid: 15007 components: - type: Transform @@ -107009,21 +106958,21 @@ entities: pos: 9.5,79.5 parent: 2 - type: SurveillanceCamera - id: Parque Norte + id: North Park - uid: 15008 components: - type: Transform pos: 9.5,15.5 parent: 2 - type: SurveillanceCamera - id: Recepçao da cargo + id: Logistics Front desk - uid: 15009 components: - type: Transform pos: 24.5,9.5 parent: 2 - type: SurveillanceCamera - id: Dormitorio + id: Dorms - uid: 15010 components: - type: Transform @@ -107031,7 +106980,7 @@ entities: pos: 33.5,21.5 parent: 2 - type: SurveillanceCamera - id: Corredor da Medbay sul + id: South Medbay Corridor - uid: 15011 components: - type: Transform @@ -107042,7 +106991,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraGeneral nameSet: True - id: Entrada da medbay + id: Medbay entrance - uid: 15012 components: - type: Transform @@ -107053,7 +107002,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraGeneral nameSet: True - id: Corredor do Cofre + id: Vault Corridor - uid: 15013 components: - type: Transform @@ -107061,7 +107010,7 @@ entities: pos: 46.5,35.5 parent: 2 - type: SurveillanceCamera - id: Recepção da botanica + id: Botany reception - uid: 15014 components: - type: Transform @@ -107077,7 +107026,7 @@ entities: pos: 64.5,44.5 parent: 2 - type: SurveillanceCamera - id: Entrada E.V.A + id: Entrance of the E.V.A. - uid: 15016 components: - type: Transform @@ -107093,7 +107042,7 @@ entities: pos: 64.5,25.5 parent: 2 - type: SurveillanceCamera - id: Entrada do BlueShield + id: Cap Reception Entrance - uid: 15018 components: - type: Transform @@ -107112,7 +107061,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraGeneral nameSet: True - id: Podporto + id: Pod port - proto: SurveillanceCameraMedical entities: - uid: 15020 @@ -107122,7 +107071,7 @@ entities: pos: 14.5,41.5 parent: 2 - type: SurveillanceCamera - id: Sala de cirugia + id: Operating room - uid: 15021 components: - type: Transform @@ -107133,7 +107082,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraMedical nameSet: True - id: Recepção + id: Front desk - uid: 15022 components: - type: Transform @@ -107143,7 +107092,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraMedical nameSet: True - id: Enfermaria Oeste + id: West Infirmary - uid: 15023 components: - type: Transform @@ -107151,7 +107100,7 @@ entities: pos: 18.5,45.5 parent: 2 - type: SurveillanceCamera - id: Necroterio + id: Morgue - uid: 15024 components: - type: Transform @@ -107162,7 +107111,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraMedical nameSet: True - id: Sala do Paramedico + id: Paramedic's room - uid: 15025 components: - type: Transform @@ -107173,7 +107122,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraMedical nameSet: True - id: Refeitorio + id: Cafeteria - uid: 15026 components: - type: Transform @@ -107184,7 +107133,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraMedical nameSet: True - id: Corredor da recepção + id: Reception corridor - uid: 15027 components: - type: Transform @@ -107192,7 +107141,7 @@ entities: pos: 16.5,27.5 parent: 2 - type: SurveillanceCamera - id: entrada da medbay + id: medbay entrance - uid: 15028 components: - type: Transform @@ -107200,7 +107149,7 @@ entities: pos: 19.5,35.5 parent: 2 - type: SurveillanceCamera - id: Enfermaria Leste + id: East Infirmary - uid: 15029 components: - type: Transform @@ -107208,7 +107157,7 @@ entities: pos: 9.5,26.5 parent: 2 - type: SurveillanceCamera - id: Laboratorio Quimico + id: Chemical Laboratory - uid: 15030 components: - type: Transform @@ -107219,7 +107168,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraMedical nameSet: True - id: Entrada da Virologia + id: Virology Entry - uid: 15031 components: - type: Transform @@ -107227,7 +107176,7 @@ entities: pos: 27.5,45.5 parent: 2 - type: SurveillanceCamera - id: Virologia + id: Virology - uid: 15032 components: - type: Transform @@ -107237,7 +107186,7 @@ entities: - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraMedical - id: Area do Genecista + id: Genecist Area - type: ActiveUserInterface - uid: 15033 components: @@ -107248,7 +107197,7 @@ entities: - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraMedical - id: Sala do Clonador + id: Cloning Room - type: ActiveUserInterface - proto: SurveillanceCameraRouterCommand entities: @@ -107318,7 +107267,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraScience nameSet: True - id: Sala de Pesquisa + id: Research Room - uid: 15043 components: - type: Transform @@ -107329,7 +107278,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraScience nameSet: True - id: 'Salão de #-tefa$tos' + id: Xenobiology - uid: 15044 components: - type: Transform @@ -107340,7 +107289,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraScience nameSet: True - id: Ala da Recepção + id: Front Desk - uid: 15045 components: - type: Transform @@ -107348,7 +107297,7 @@ entities: pos: 40.5,10.5 parent: 2 - type: SurveillanceCamera - id: Sala da Xenoarqueologia + id: Xenoarchaeology Room - uid: 15046 components: - type: Transform @@ -107359,7 +107308,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraScience nameSet: True - id: Corredor da Sala de Pesquisa + id: Research Room Corridor - uid: 15047 components: - type: Transform @@ -107370,7 +107319,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraScience nameSet: True - id: Entrada da sala de Anomalia + id: Entrance to the Anomaly room - uid: 15048 components: - type: Transform @@ -107380,15 +107329,9 @@ entities: setupAvailableNetworks: - SurveillanceCameraScience nameSet: True - id: Robotica + id: Robotics - proto: SurveillanceCameraSecurity entities: - - uid: 5068 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: -33.5,65.5 - parent: 2 - uid: 7187 components: - type: Transform @@ -107405,7 +107348,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Posto de segurança da cargo + id: Cargo Checkpoint - uid: 15050 components: - type: Transform @@ -107416,7 +107359,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Sala de "interogatorio" + id: interrogation room - uid: 15051 components: - type: Transform @@ -107427,7 +107370,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Vestiário + id: Lockers - uid: 15052 components: - type: Transform @@ -107438,7 +107381,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Arsenal + id: Armory - uid: 15053 components: - type: Transform @@ -107449,7 +107392,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Recepção da entrada Sul + id: South entrance reception - uid: 15054 components: - type: Transform @@ -107474,7 +107417,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Posto da Ciência + id: Epistemics Checkpoint - uid: 15057 components: - type: Transform @@ -107482,7 +107425,7 @@ entities: pos: -4.5,31.5 parent: 2 - type: SurveillanceCamera - id: Posto da Engenharia + id: Eng Checkpoint - uid: 15058 components: - type: Transform @@ -107493,7 +107436,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Ala de prisões + id: Brig - uid: 15059 components: - type: Transform @@ -107504,7 +107447,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Sala do detetive + id: Detective's room - uid: 15060 components: - type: Transform @@ -107515,7 +107458,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Sala de investigação + id: Investigation room - uid: 15061 components: - type: Transform @@ -107525,7 +107468,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Sala do advogado + id: Lawyer's room - uid: 15062 components: - type: Transform @@ -107535,7 +107478,7 @@ entities: - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraSecurity - id: Corredor da perma + id: Perma Corridor - type: ActiveUserInterface - uid: 15063 components: @@ -107546,7 +107489,7 @@ entities: - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraSecurity - id: 'Corredor da delegacia: 1' + id: Security Corridor - type: ActiveUserInterface - uid: 15064 components: @@ -107557,7 +107500,7 @@ entities: - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraSecurity - id: Lazer da segurança + id: Rest area - type: ActiveUserInterface - uid: 15066 components: @@ -107569,7 +107512,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSecurity nameSet: True - id: Entrega de equipamento + id: Equipment delivery - uid: 15068 components: - type: Transform @@ -107588,18 +107531,61 @@ entities: - SurveillanceCameraSecurity nameSet: True id: BrigMed - - uid: 19537 + - uid: 17922 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -33.5,60.5 + parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraSecurity + nameSet: True + id: Prisioner Bedroom 1 + - uid: 17993 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -33.5,65.5 + parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraSecurity + nameSet: True + id: Perma + - uid: 17994 components: - type: Transform rot: 3.141592653589793 rad pos: -31.5,77.5 parent: 2 - - uid: 19538 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraSecurity + nameSet: True + id: Perma Botany + - uid: 17996 components: - type: Transform - rot: 3.141592653589793 rad + rot: 1.5707963267948966 rad pos: -21.5,74.5 parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraSecurity + nameSet: True + id: Perma Vending Machines + - uid: 18581 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -29.5,60.5 + parent: 2 + - type: SurveillanceCamera + setupAvailableNetworks: + - SurveillanceCameraSecurity + nameSet: True + id: Prisioner Bedroom 2 - proto: SurveillanceCameraService entities: - uid: 15070 @@ -107609,7 +107595,7 @@ entities: pos: 6.5,4.5 parent: 2 - type: SurveillanceCamera - id: Lixo da estação + id: Disposals - uid: 15071 components: - type: Transform @@ -107617,7 +107603,7 @@ entities: pos: 62.5,38.5 parent: 2 - type: SurveillanceCamera - id: Balcão do bar + id: Bar Counter - uid: 15072 components: - type: Transform @@ -107628,7 +107614,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraService nameSet: True - id: Sala das ferramentas + id: Tool Room - uid: 15073 components: - type: Transform @@ -107636,7 +107622,7 @@ entities: pos: 59.5,43.5 parent: 2 - type: SurveillanceCamera - id: Sala do Bartender + id: Bartender - uid: 15074 components: - type: Transform @@ -107644,7 +107630,7 @@ entities: pos: 43.5,37.5 parent: 2 - type: SurveillanceCamera - id: Hidroponia + id: Hydroponics - uid: 15075 components: - type: Transform @@ -107652,7 +107638,7 @@ entities: pos: 50.5,48.5 parent: 2 - type: SurveillanceCamera - id: Vestuario da Botanica + id: Botanica Cabinets - uid: 15076 components: - type: Transform @@ -107667,7 +107653,7 @@ entities: pos: 54.5,42.5 parent: 2 - type: SurveillanceCamera - id: Cozinha + id: Kitchen - uid: 15078 components: - type: Transform @@ -107675,7 +107661,7 @@ entities: pos: 41.5,37.5 parent: 2 - type: SurveillanceCamera - id: Teatro + id: Theater - uid: 15079 components: - type: Transform @@ -107686,7 +107672,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraService nameSet: True - id: Sala da palhaçada + id: clown room - uid: 15080 components: - type: Transform @@ -107696,7 +107682,7 @@ entities: - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraService - id: Sala da diversão + id: fun room - uid: 15081 components: - type: Transform @@ -107704,7 +107690,7 @@ entities: pos: 39.5,54.5 parent: 2 - type: SurveillanceCamera - id: Sala do zelador + id: Janitor's room - uid: 15082 components: - type: Transform @@ -107712,7 +107698,7 @@ entities: pos: 12.5,61.5 parent: 2 - type: SurveillanceCamera - id: Sala do Padre + id: Priest's Room - uid: 15083 components: - type: Transform @@ -107720,7 +107706,7 @@ entities: pos: 17.5,63.5 parent: 2 - type: SurveillanceCamera - id: Crematório + id: Crematorium - uid: 15084 components: - type: Transform @@ -107728,7 +107714,7 @@ entities: pos: 8.5,54.5 parent: 2 - type: SurveillanceCamera - id: Igreja + id: Chapel - uid: 15085 components: - type: Transform @@ -107739,7 +107725,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraService nameSet: True - id: Biblioteca + id: Library - proto: SurveillanceCameraSupply entities: - uid: 15086 @@ -107749,7 +107735,7 @@ entities: pos: -10.5,14.5 parent: 2 - type: SurveillanceCamera - id: Estoque da Salvatagem + id: Salvage Stock - uid: 15087 components: - type: Transform @@ -107759,7 +107745,7 @@ entities: - type: SurveillanceCamera setupAvailableNetworks: - SurveillanceCameraSupply - id: Salvatagem + id: Salvage - type: ActiveUserInterface - uid: 15088 components: @@ -107771,14 +107757,14 @@ entities: setupAvailableNetworks: - SurveillanceCameraSupply nameSet: True - id: Correios + id: Mail - uid: 15089 components: - type: Transform pos: 0.5,3.5 parent: 2 - type: SurveillanceCamera - id: Armazém da cargo + id: Cargo warehouse - uid: 15090 components: - type: Transform @@ -107789,7 +107775,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSupply nameSet: True - id: Armazem da salvatagem + id: Asalvage warehouse - uid: 15091 components: - type: Transform @@ -107800,7 +107786,7 @@ entities: setupAvailableNetworks: - SurveillanceCameraSupply nameSet: True - id: Exterior da salvatagem + id: Exterior of the salvage - proto: Syringe entities: - uid: 15092 @@ -121208,11 +121194,6 @@ entities: - type: Transform pos: 14.5,-1.5 parent: 2 - - uid: 17450 - components: - - type: Transform - pos: 7.5,-3.5 - parent: 2 - uid: 17451 components: - type: Transform @@ -123545,30 +123526,35 @@ entities: - type: Transform pos: 87.5,13.5 parent: 2 - - uid: 17924 + - uid: 17917 components: - type: Transform - pos: 86.5,-6.5 + pos: 85.5,-8.5 parent: 2 - - uid: 17927 + - uid: 17918 components: - type: Transform - pos: 85.5,-6.5 + pos: 86.5,-8.5 parent: 2 - - uid: 17931 + - uid: 17936 components: - type: Transform - pos: 84.5,-6.5 + pos: 80.5,-8.5 parent: 2 - - uid: 17933 + - uid: 17937 components: - type: Transform - pos: 80.5,-6.5 + pos: 78.5,-8.5 parent: 2 - - uid: 17978 + - uid: 17939 + components: + - type: Transform + pos: 77.5,-8.5 + parent: 2 + - uid: 17944 components: - type: Transform - pos: 81.5,-6.5 + pos: 82.5,-8.5 parent: 2 - uid: 17979 components: @@ -123580,21 +123566,21 @@ entities: - type: Transform pos: 87.5,11.5 parent: 2 - - uid: 17986 - components: - - type: Transform - pos: 78.5,-6.5 - parent: 2 - uid: 17987 components: - type: Transform - pos: 82.5,-6.5 + pos: 81.5,-8.5 parent: 2 - uid: 17988 components: - type: Transform pos: 87.5,12.5 parent: 2 + - uid: 17989 + components: + - type: Transform + pos: 84.5,-8.5 + parent: 2 - uid: 17990 components: - type: Transform @@ -123620,11 +123606,6 @@ entities: - type: Transform pos: 86.5,14.5 parent: 2 - - uid: 18528 - components: - - type: Transform - pos: 77.5,-6.5 - parent: 2 - uid: 18556 components: - type: Transform @@ -123700,11 +123681,6 @@ entities: - type: Transform pos: 81.5,-9.5 parent: 2 - - uid: 18571 - components: - - type: Transform - pos: 83.5,-9.5 - parent: 2 - uid: 18572 components: - type: Transform @@ -124697,7 +124673,7 @@ entities: lastSignals: DoorStatus: True - type: Door - secondsUntilStateChange: -79352.93 + secondsUntilStateChange: -79958.195 state: Opening - uid: 18112 components: From 7e1d9c4b6745bb20a929c2d4be298545b15068ea Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Tue, 15 Oct 2024 19:13:13 -0400 Subject: [PATCH 08/76] Fix Psionic Power Rolling (#1047) # Description Apparently the psionic power rolls were not including a D100 roll in the baseline, so people were getting new psionic powers waaaaaay slower than was intended. Also, makes late-join psions use the glimmer calculation. # Changelog :cl: - tweak: Psionic Rolls should now generate larger, random amounts of Potentia. This should make it a lot easier to obtain powers. --- Content.Server/Psionics/PsionicsSystem.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Content.Server/Psionics/PsionicsSystem.cs b/Content.Server/Psionics/PsionicsSystem.cs index 0d05000a3c..90f69cf796 100644 --- a/Content.Server/Psionics/PsionicsSystem.cs +++ b/Content.Server/Psionics/PsionicsSystem.cs @@ -55,7 +55,7 @@ public override void Update(float frameTime) { base.Update(frameTime); foreach (var roller in _rollers) - RollPsionics(roller.uid, roller.component, false); + RollPsionics(roller.uid, roller.component, true); _rollers.Clear(); } public override void Initialize() @@ -214,7 +214,8 @@ public void RollPsionics(EntityUid uid, PsionicComponent component, bool applyGl // Calculate the initial odds based on the innate potential var baselineChance = component.Chance * component.PowerRollMultiplier - + component.PowerRollFlatBonus; + + component.PowerRollFlatBonus + + _random.NextFloat(0, 100); // Increase the initial odds based on Glimmer. // TODO: Change this equation when I do my Glimmer Refactor From 6a54e8cc048a5bbcdc5355eeb73f02f7159e0766 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Tue, 15 Oct 2024 23:14:14 +0000 Subject: [PATCH 09/76] Automatic Changelog Update (#1048) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 86977434ca..44c332cd7d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7272,3 +7272,12 @@ Entries: id: 6451 time: '2024-10-15T23:07:03.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1055 +- author: Ichaie + changes: + - type: Fix + message: >- + Gax station: fixed arrivals being unable to dock due to meteor shielding + and translated security camera names to English. + id: 6452 + time: '2024-10-15T23:12:44.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1048 From 3ddd185c5013cd5215a9affabfc7c021cb70b32b Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Tue, 15 Oct 2024 23:15:54 +0000 Subject: [PATCH 10/76] Automatic Changelog Update (#1047) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 44c332cd7d..3cd9601795 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7281,3 +7281,12 @@ Entries: id: 6452 time: '2024-10-15T23:12:44.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1048 +- author: VMSolidus + changes: + - type: Tweak + message: >- + Psionic Rolls should now generate larger, random amounts of Potentia. + This should make it a lot easier to obtain powers. + id: 6453 + time: '2024-10-15T23:13:13.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1047 From 6cbaf2f540865764a4fc49292f8dc49054095332 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Tue, 15 Oct 2024 20:42:06 -0400 Subject: [PATCH 11/76] Database Fixes (#1051) * Check for flavor text equality in profile. * Fix some characters being deleted from flavor text Sometimes the last few characters of the flavor text would get deleted. This fixes this issue. (cherry picked from commit 9f1d95e3353493f28d579b562de348a322312885) # Description This PR fixes some database issues present in The Wizmerge. # Changelog No changelog, this isn't player facing. --------- Co-authored-by: Pieter-Jan Briers Co-authored-by: Evgencheg <7064926@gmail.com> Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Co-authored-by: dffdff2423 --- .github/labeler.yml | 2 +- Content.Client/FlavorText/FlavorText.xaml.cs | 2 +- .../20240623005121_BanTemplate.Designer.cs | 1847 +++++++++++++++++ .../Postgres/20240623005121_BanTemplate.cs | 42 + .../PostgresServerDbContextModelSnapshot.cs | 45 + .../20240623005113_BanTemplate.Designer.cs | 1774 ++++++++++++++++ .../Sqlite/20240623005113_BanTemplate.cs | 41 + .../SqliteServerDbContextModelSnapshot.cs | 43 + Content.Server.Database/Model.cs | 54 + .../Preferences/HumanoidCharacterProfile.cs | 3 +- .../Entities/Objects/Misc/torch.yml | 2 + 11 files changed, 3852 insertions(+), 3 deletions(-) create mode 100644 Content.Server.Database/Migrations/Postgres/20240623005121_BanTemplate.Designer.cs create mode 100644 Content.Server.Database/Migrations/Postgres/20240623005121_BanTemplate.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20240623005113_BanTemplate.Designer.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20240623005113_BanTemplate.cs diff --git a/.github/labeler.yml b/.github/labeler.yml index 6b87aa2655..4cfe775ed4 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -48,7 +48,7 @@ - any-glob-to-any-file: - "**/*.yml" - all-globs-to-all-files: - - "!Resources/Maps/**/*.yml", + - "!Resources/Maps/**/*.yml" - "!Resources/Prototypes/Maps/**/*.yml" "Changes: Workflow": diff --git a/Content.Client/FlavorText/FlavorText.xaml.cs b/Content.Client/FlavorText/FlavorText.xaml.cs index ffcf653f11..91b59046a4 100644 --- a/Content.Client/FlavorText/FlavorText.xaml.cs +++ b/Content.Client/FlavorText/FlavorText.xaml.cs @@ -17,7 +17,7 @@ public FlavorText() var loc = IoCManager.Resolve(); CFlavorTextInput.Placeholder = new Rope.Leaf(loc.GetString("flavor-text-placeholder")); - CFlavorTextInput.OnKeyBindDown += _ => FlavorTextChanged(); + CFlavorTextInput.OnTextChanged += _ => FlavorTextChanged(); } public void FlavorTextChanged() diff --git a/Content.Server.Database/Migrations/Postgres/20240623005121_BanTemplate.Designer.cs b/Content.Server.Database/Migrations/Postgres/20240623005121_BanTemplate.Designer.cs new file mode 100644 index 0000000000..2fb55ddfec --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20240623005121_BanTemplate.Designer.cs @@ -0,0 +1,1847 @@ +// +using System; +using System.Net; +using System.Text.Json; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + [DbContext(typeof(PostgresServerDbContext))] + [Migration("20240623005113_BanTemplate")] + partial class BanTemplate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Title") + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminId") + .HasColumnType("uuid") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("boolean") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("admin_log_id"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("smallint") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("RoundId", "Id") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Message") + .HasAnnotation("Npgsql:TsVectorConfig", "english"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Message"), "GIN"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("LogId") + .HasColumnType("integer") + .HasColumnName("log_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.HasKey("RoundId", "LogId", "PlayerUserId") + .HasName("PK_admin_log_player"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_log_player_player_user_id"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_messages_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("boolean") + .HasColumnName("seen"); + + b.HasKey("Id") + .HasName("PK_admin_messages"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_messages_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_messages_round_id"); + + b.ToTable("admin_messages", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_notes_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("boolean") + .HasColumnName("secret"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_watchlists_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_admin_watchlists"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_watchlists_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_watchlists_round_id"); + + b.ToTable("admin_watchlists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("antag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("assigned_user_id_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_template_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("interval") + .HasColumnName("length"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_ban_template"); + + b.ToTable("ban_template", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("connection_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("smallint") + .HasColumnName("denied"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("timestamp with time zone") + .HasColumnName("time"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_connection_log_server_id"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("job_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("JobName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Loadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("loadout_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LoadoutName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("loadout_name"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_loadout"); + + b.HasIndex("ProfileId", "LoadoutName") + .IsUnique(); + + b.ToTable("loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("play_time_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PlayerId") + .HasColumnType("uuid") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("interval") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("text") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("player_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FirstSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenHWId") + .HasColumnType("bytea") + .HasColumnName("last_seen_hwid"); + + b.Property("LastSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", null, t => + { + t.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("preference_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("integer") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("integer") + .HasColumnName("age"); + + b.Property("Backpack") + .IsRequired() + .HasColumnType("text") + .HasColumnName("backpack"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("char_name"); + + b.Property("Clothing") + .IsRequired() + .HasColumnType("text") + .HasColumnName("clothing"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("text") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_name"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("PreferenceId") + .HasColumnType("integer") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("integer") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("text") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("integer") + .HasColumnName("slot"); + + b.Property("SpawnPriority") + .HasColumnType("integer") + .HasColumnName("spawn_priority"); + + b.Property("Species") + .IsRequired() + .HasColumnType("text") + .HasColumnName("species"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("round_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ServerId") + .HasColumnType("integer") + .HasColumnName("server_id"); + + b.Property("StartDate") + .ValueGeneratedOnAdd() + .HasColumnType("timestamp with time zone") + .HasDefaultValue(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)) + .HasColumnName("start_date"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.HasIndex("StartDate"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_ban_round_id"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("integer") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_hit_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("integer") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_role_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_role_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_role_ban_round_id"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("role_unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("trait_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("uploaded_resource_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("integer") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("integer") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("RoundId", "LogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminMessagesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminMessagesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminMessagesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminMessagesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_messages_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_messages_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminWatchlistsCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminWatchlistsDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminWatchlistsLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminWatchlistsReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_watchlists_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_watchlists_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("ConnectionLogs") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired() + .HasConstraintName("FK_connection_log_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Loadout", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Loadouts") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_loadout_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerRoleBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerRoleBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_role_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminServerRoleBansCreated"); + + b.Navigation("AdminServerRoleBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/20240623005121_BanTemplate.cs b/Content.Server.Database/Migrations/Postgres/20240623005121_BanTemplate.cs new file mode 100644 index 0000000000..192e87ac96 --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20240623005121_BanTemplate.cs @@ -0,0 +1,42 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + /// + public partial class BanTemplate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ban_template", + columns: table => new + { + ban_template_id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + title = table.Column(type: "text", nullable: false), + length = table.Column(type: "interval", nullable: false), + reason = table.Column(type: "text", nullable: false), + exempt_flags = table.Column(type: "integer", nullable: false), + severity = table.Column(type: "integer", nullable: false), + auto_delete = table.Column(type: "boolean", nullable: false), + hidden = table.Column(type: "boolean", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ban_template", x => x.ban_template_id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ban_template"); + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs index ce17fced09..4a13022fec 100644 --- a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs @@ -512,6 +512,51 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("assigned_user_id", (string)null); }); + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_template_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("interval") + .HasColumnName("length"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_ban_template"); + + b.ToTable("ban_template", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => { b.Property("Id") diff --git a/Content.Server.Database/Migrations/Sqlite/20240623005113_BanTemplate.Designer.cs b/Content.Server.Database/Migrations/Sqlite/20240623005113_BanTemplate.Designer.cs new file mode 100644 index 0000000000..0bf74f84a7 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20240623005113_BanTemplate.Designer.cs @@ -0,0 +1,1774 @@ +// +using System; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + [DbContext(typeof(SqliteServerDbContext))] + [Migration("20240623005113_BanTemplate")] + partial class BanTemplate + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Title") + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_flag_id"); + + b.Property("AdminId") + .HasColumnType("TEXT") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("INTEGER") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Id") + .HasColumnType("INTEGER") + .HasColumnName("admin_log_id"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("INTEGER") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("INTEGER") + .HasColumnName("type"); + + b.HasKey("RoundId", "Id") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("LogId") + .HasColumnType("INTEGER") + .HasColumnName("log_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.HasKey("RoundId", "LogId", "PlayerUserId") + .HasName("PK_admin_log_player"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_log_player_player_user_id"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_messages_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("INTEGER") + .HasColumnName("seen"); + + b.HasKey("Id") + .HasName("PK_admin_messages"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_messages_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_messages_round_id"); + + b.ToTable("admin_messages", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_notes_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("INTEGER") + .HasColumnName("secret"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_flag_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_watchlists_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_admin_watchlists"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_watchlists_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_watchlists_round_id"); + + b.ToTable("admin_watchlists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("antag_id"); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("assigned_user_id_id"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_template_id"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("TEXT") + .HasColumnName("length"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_ban_template"); + + b.ToTable("ban_template", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("connection_log_id"); + + b.Property("Address") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("INTEGER") + .HasColumnName("denied"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("TEXT") + .HasColumnName("time"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_connection_log_server_id"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("job_id"); + + b.Property("JobName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Loadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("loadout_id"); + + b.Property("LoadoutName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("loadout_name"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_loadout"); + + b.HasIndex("ProfileId", "LoadoutName") + .IsUnique(); + + b.ToTable("loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("play_time_id"); + + b.Property("PlayerId") + .HasColumnType("TEXT") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("TEXT") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("player_id"); + + b.Property("FirstSeenTime") + .HasColumnType("TEXT") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("TEXT") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenHWId") + .HasColumnType("BLOB") + .HasColumnName("last_seen_hwid"); + + b.Property("LastSeenTime") + .HasColumnType("TEXT") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("INTEGER") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("Age") + .HasColumnType("INTEGER") + .HasColumnName("age"); + + b.Property("Backpack") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("backpack"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("char_name"); + + b.Property("Clothing") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("clothing"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_name"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("PreferenceId") + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("INTEGER") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("INTEGER") + .HasColumnName("slot"); + + b.Property("SpawnPriority") + .HasColumnType("INTEGER") + .HasColumnName("spawn_priority"); + + b.Property("Species") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("species"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("ServerId") + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("StartDate") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasDefaultValue(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)) + .HasColumnName("start_date"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.HasIndex("StartDate"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_ban_round_id"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("INTEGER") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_hit_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("INTEGER") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_role_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_role_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_role_ban_round_id"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("role_unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("trait_id"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("uploaded_resource_log_id"); + + b.Property("Data") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("INTEGER") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("INTEGER") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("RoundId", "LogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminMessagesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminMessagesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminMessagesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminMessagesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_messages_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_messages_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminWatchlistsCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminWatchlistsDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminWatchlistsLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminWatchlistsReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_watchlists_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_watchlists_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("ConnectionLogs") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired() + .HasConstraintName("FK_connection_log_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Loadout", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Loadouts") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_loadout_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerRoleBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerRoleBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_role_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminServerRoleBansCreated"); + + b.Navigation("AdminServerRoleBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/20240623005113_BanTemplate.cs b/Content.Server.Database/Migrations/Sqlite/20240623005113_BanTemplate.cs new file mode 100644 index 0000000000..ffe5a47550 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20240623005113_BanTemplate.cs @@ -0,0 +1,41 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + /// + public partial class BanTemplate : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ban_template", + columns: table => new + { + ban_template_id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + title = table.Column(type: "TEXT", nullable: false), + length = table.Column(type: "TEXT", nullable: false), + reason = table.Column(type: "TEXT", nullable: false), + exempt_flags = table.Column(type: "INTEGER", nullable: false), + severity = table.Column(type: "INTEGER", nullable: false), + auto_delete = table.Column(type: "INTEGER", nullable: false), + hidden = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ban_template", x => x.ban_template_id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "ban_template"); + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs index d23c5b0a38..d2d47f60b4 100644 --- a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs @@ -483,6 +483,49 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("assigned_user_id", (string)null); }); + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_template_id"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("TEXT") + .HasColumnName("length"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_ban_template"); + + b.ToTable("ban_template", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => { b.Property("Id") diff --git a/Content.Server.Database/Model.cs b/Content.Server.Database/Model.cs index d544009a26..187fb81a20 100644 --- a/Content.Server.Database/Model.cs +++ b/Content.Server.Database/Model.cs @@ -40,6 +40,7 @@ protected ServerDbContext(DbContextOptions options) : base(options) public DbSet AdminNotes { get; set; } = null!; public DbSet AdminWatchlists { get; set; } = null!; public DbSet AdminMessages { get; set; } = null!; + public DbSet BanTemplate { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -1025,4 +1026,57 @@ public class AdminMessage : IAdminRemarksCommon /// public bool Dismissed { get; set; } } + + /// + /// Defines a template that admins can use to quickly fill out ban information. + /// + /// + /// + /// This information is not currently used by the game itself, but it is used by SS14.Admin. + /// + /// + public sealed class BanTemplate + { + public int Id { get; set; } + + /// + /// Title of the ban template. This is purely for reference by admins and not copied into the ban. + /// + public required string Title { get; set; } + + /// + /// How long the ban should last. 0 for permanent. + /// + public TimeSpan Length { get; set; } + + /// + /// The reason for the ban. + /// + /// + public string Reason { get; set; } = ""; + + /// + /// Exemptions granted to the ban. + /// + /// + public ServerBanExemptFlags ExemptFlags { get; set; } + + /// + /// Severity of the ban + /// + /// + public NoteSeverity Severity { get; set; } + + /// + /// Ban will be automatically deleted once expired. + /// + /// + public bool AutoDelete { get; set; } + + /// + /// Ban is not visible to players in the remarks menu. + /// + /// + public bool Hidden { get; set; } + } } diff --git a/Content.Shared/Preferences/HumanoidCharacterProfile.cs b/Content.Shared/Preferences/HumanoidCharacterProfile.cs index 79eaacf65f..4feb193a65 100644 --- a/Content.Shared/Preferences/HumanoidCharacterProfile.cs +++ b/Content.Shared/Preferences/HumanoidCharacterProfile.cs @@ -343,7 +343,8 @@ public bool MemberwiseEquals(ICharacterProfile maybeOther) && _antagPreferences.SequenceEqual(other._antagPreferences) && _traitPreferences.SequenceEqual(other._traitPreferences) && LoadoutPreferences.SequenceEqual(other.LoadoutPreferences) - && Appearance.MemberwiseEquals(other.Appearance); + && Appearance.MemberwiseEquals(other.Appearance) + && FlavorText == other.FlavorText; } public void EnsureValid(ICommonSession session, IDependencyCollection collection) diff --git a/Resources/Prototypes/Entities/Objects/Misc/torch.yml b/Resources/Prototypes/Entities/Objects/Misc/torch.yml index 0efb753e0d..50e8f65890 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/torch.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/torch.yml @@ -25,6 +25,8 @@ color: "#FFFFFF" visible: false shader: unshaded + - map: [ enum.ExpendableLightVisualLayers.Overlay ] + state: torch_nocloth - type: Icon sprite: Objects/Misc/torch.rsi state: icon From f09d26cdf1c41ce82c5e1dd87dbb1aaf43ba7bf2 Mon Sep 17 00:00:00 2001 From: Aiden Date: Tue, 15 Oct 2024 22:08:07 -0500 Subject: [PATCH 12/76] Fix Crawling on Tables (#1057) # Description Crawling on tables was janky, if you stood up while crawling, or laid down while climbing, it would push you to the edge of the table and break. Solves #995 ---

Media

https://github.com/user-attachments/assets/08aa0e22-0363-45d8-810f-c69f7a796d7c https://github.com/user-attachments/assets/5a31df3c-f01c-4500-94fb-f4beb3c6e007

--- # Changelog :cl: - fix: You can properly lay down and stand up on tables now. Co-authored-by: VMSolidus --- .../Climbing/Systems/ClimbSystem.cs | 5 ++++ .../Standing/StandingStateSystem.cs | 30 +++++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index e3c494e955..c570a821a6 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -474,6 +474,11 @@ public void ForciblySetClimbing(EntityUid uid, EntityUid climbable, ClimbingComp Climb(uid, uid, climbable, true, component); } + public void ForciblyStopClimbing(EntityUid uid, ClimbingComponent? climbing = null, FixturesComponent? fixtures = null) + { + StopClimb(uid, climbing, fixtures); + } + private void OnBuckleChange(EntityUid uid, ClimbingComponent component, ref BuckleChangeEvent args) { if (!args.Buckling) diff --git a/Content.Shared/Standing/StandingStateSystem.cs b/Content.Shared/Standing/StandingStateSystem.cs index aed6ce372f..5abbf53f1b 100644 --- a/Content.Shared/Standing/StandingStateSystem.cs +++ b/Content.Shared/Standing/StandingStateSystem.cs @@ -1,5 +1,7 @@ using Content.Shared.Buckle; using Content.Shared.Buckle.Components; +using Content.Shared.Climbing.Systems; +using Content.Shared.Climbing.Components; using Content.Shared.Hands.Components; using Content.Shared.Movement.Systems; using Content.Shared.Physics; @@ -7,6 +9,7 @@ using Robust.Shared.Audio.Systems; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; +using System.Linq; namespace Content.Shared.Standing; @@ -17,9 +20,11 @@ public sealed class StandingStateSystem : EntitySystem [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly MovementSpeedModifierSystem _movement = default!; [Dependency] private readonly SharedBuckleSystem _buckle = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly ClimbSystem _climb = default!; // If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited. - private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable; + private const int StandingCollisionLayer = (int)CollisionGroup.MidImpassable; public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) { @@ -88,6 +93,9 @@ public bool Down(EntityUid uid, bool playSound = true, bool dropHeldItems = true _audio.PlayPredicted(standingState.DownSound, uid, null); _movement.RefreshMovementSpeedModifiers(uid); + + Climb(uid); + return true; } @@ -118,6 +126,7 @@ public bool Stand(EntityUid uid, } standingState.CurrentState = StandingState.Standing; + Dirty(uid, standingState); RaiseLocalEvent(uid, new StoodEvent(), false); @@ -134,10 +143,27 @@ public bool Stand(EntityUid uid, standingState.ChangedFixtures.Clear(); _movement.RefreshMovementSpeedModifiers(uid); + Climb(uid); + return true; } + + private void Climb(EntityUid uid) + { + _climb.ForciblyStopClimbing(uid); + + var entityDistances = new Dictionary(); + + foreach (var entity in _lookup.GetEntitiesInRange(uid, 0.3f)) + if (HasComp(entity)) + entityDistances[entity] = (Transform(uid).Coordinates.Position - Transform(entity).Coordinates.Position).LengthSquared(); + + if (entityDistances.Count > 0) + _climb.ForciblySetClimbing(uid, entityDistances.OrderBy(e => e.Value).First().Key); + } } + public sealed class DropHandItemsEvent : EventArgs { } /// @@ -158,4 +184,4 @@ public sealed class StoodEvent : EntityEventArgs { } /// /// Raised when an entity is not standing /// -public sealed class DownedEvent : EntityEventArgs { } +public sealed class DownedEvent : EntityEventArgs { } From 7c1ff617783f0555970854eade807cb22111dd79 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Wed, 16 Oct 2024 03:08:39 +0000 Subject: [PATCH 13/76] Automatic Changelog Update (#1057) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3cd9601795..5e659ff847 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7290,3 +7290,10 @@ Entries: id: 6453 time: '2024-10-15T23:13:13.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1047 +- author: Aidenkrz + changes: + - type: Fix + message: You can properly lay down and stand up on tables now. + id: 6454 + time: '2024-10-16T03:08:07.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1057 From 0865e473d1476d12d7b0bd879b2afd2740d1fa60 Mon Sep 17 00:00:00 2001 From: Aiden Date: Wed, 16 Oct 2024 07:54:15 -0500 Subject: [PATCH 14/76] Constrict Height and Width via a Size Ratio & Fix Humanoids Not Having A Fixture (#1049) # Description Title Intended to stop players from making slender men type characters with height maxed and width at the minimum, and vice versa The ratio can be modified via the species' prototype Also fixes the issue of players being able to noclip because they're fixture has no radius by setting the Height and Width to the species default if none is provided. (technically this also fixes Urists always being as small as possible) Solves #865 #497 Partially resolves #995 ---

Media

https://github.com/user-attachments/assets/1b3b32f1-5976-41f8-935b-7d53106e5452

--- # Changelog :cl: - tweak: Height and width are now constrained by each other. - fix: Humanoids can no longer phase through walls. --------- Signed-off-by: Aiden Co-authored-by: VMSolidus Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> --- .../Lobby/UI/HumanoidProfileEditor.xaml.cs | 135 ++++++++---------- .../Humanoid/Prototypes/SpeciesPrototype.cs | 6 + .../SharedHumanoidAppearanceSystem.cs | 9 +- 3 files changed, 73 insertions(+), 77 deletions(-) diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index 3ce9d8e6d5..012d894aee 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -187,6 +187,7 @@ public HumanoidProfileEditor( UpdateHairPickers(); OnSkinColorOnValueChanged(); UpdateCustomSpecieNameEdit(); + UpdateHeightWidthSliders(); }; #endregion Species @@ -195,76 +196,24 @@ public HumanoidProfileEditor( var prototype = _species.Find(x => x.ID == Profile?.Species) ?? _species.First(); - HeightSlider.MinValue = prototype.MinHeight; - HeightSlider.MaxValue = prototype.MaxHeight; - HeightSlider.Value = Profile?.Height ?? prototype.DefaultHeight; - var height = MathF.Round(prototype.AverageHeight * HeightSlider.Value); - HeightLabel.Text = Loc.GetString("humanoid-profile-editor-height-label", ("height", (int) height)); - - HeightSlider.OnValueChanged += args => - { - if (Profile is null) - return; - - prototype = _species.Find(x => x.ID == Profile.Species) ?? _species.First(); // Just in case + UpdateHeightWidthSliders(); + UpdateDimensions(SliderUpdate.Both); - var value = Math.Clamp(args.Value, prototype.MinHeight, prototype.MaxHeight); - var height = MathF.Round(prototype.AverageHeight * value); - HeightLabel.Text = Loc.GetString("humanoid-profile-editor-height-label", ("height", (int) height)); - SetProfileHeight(value); - UpdateWeight(); - }; + HeightSlider.OnValueChanged += _ => UpdateDimensions(SliderUpdate.Height); + WidthSlider.OnValueChanged += _ => UpdateDimensions(SliderUpdate.Width); HeightReset.OnPressed += _ => { HeightSlider.Value = prototype.DefaultHeight; - SetProfileHeight(prototype.DefaultHeight); - UpdateWeight(); - }; - - - WidthSlider.MinValue = prototype.MinWidth; - WidthSlider.MaxValue = prototype.MaxWidth; - WidthSlider.Value = Profile?.Width ?? prototype.DefaultWidth; - var width = MathF.Round(prototype.AverageWidth * WidthSlider.Value); - WidthLabel.Text = Loc.GetString("humanoid-profile-editor-width-label", ("width", width)); - - WidthSlider.OnValueChanged += args => - { - if (Profile is null) - return; - - prototype = _species.Find(x => x.ID == Profile.Species) ?? _species.First(); // Just in case - - var value = Math.Clamp(args.Value, prototype.MinWidth, prototype.MaxWidth); - var width = MathF.Round(prototype.AverageWidth * value); - WidthLabel.Text = Loc.GetString("humanoid-profile-editor-width-label", ("width", width)); - SetProfileWidth(value); - UpdateWeight(); + UpdateDimensions(SliderUpdate.Height); }; WidthReset.OnPressed += _ => { WidthSlider.Value = prototype.DefaultWidth; - SetProfileWidth(prototype.DefaultWidth); - UpdateWeight(); + UpdateDimensions(SliderUpdate.Width); }; - prototypeManager.Index(prototype.Prototype).TryGetComponent(out var fixture); - if (fixture != null) - { - var radius = fixture.Fixtures["fix1"].Shape.Radius; - var density = fixture.Fixtures["fix1"].Density; - var avg = (WidthSlider.Value + HeightSlider.Value) / 2; - var weight = MathF.Round(MathF.PI * MathF.Pow(radius * avg, 2) * density); - WeightLabel.Text = Loc.GetString("humanoid-profile-editor-weight-label", ("weight", (int) weight)); - } - else - { - // Whelp, the fixture doesn't exist, guesstimate it instead - WeightLabel.Text = Loc.GetString("humanoid-profile-editor-weight-label", ("weight", (int) 71)); - } - #endregion Height #region Skin @@ -697,8 +646,7 @@ public void SetProfile(HumanoidCharacterProfile? profile, int? slot) UpdateHairPickers(); UpdateCMarkingsHair(); UpdateCMarkingsFacialHair(); - UpdateHeightControls(); - UpdateWidthControls(); + UpdateHeightWidthSliders(); UpdateWeight(); UpdateCharacterRequired(); @@ -1193,8 +1141,7 @@ private void SetSpecies(string newSpecies) UpdateSexControls(); // Update sex for new species UpdateCharacterRequired(); // Changing species provides inaccurate sliders without these - UpdateHeightControls(); - UpdateWidthControls(); + UpdateHeightWidthSliders(); UpdateWeight(); UpdateSpeciesGuidebookIcon(); IsDirty = true; @@ -1411,34 +1358,68 @@ private void UpdateSpawnPriorityControls() SpawnPriorityButton.SelectId((int) Profile.SpawnPriority); } - private void UpdateHeightControls() + private void UpdateHeightWidthSliders() { - if (Profile == null) - return; - - var species = _species.Find(x => x.ID == Profile.Species) ?? _species.First(); + var species = _species.Find(x => x.ID == Profile?.Species) ?? _species.First(); HeightSlider.MinValue = species.MinHeight; - HeightSlider.Value = Profile.Height; HeightSlider.MaxValue = species.MaxHeight; + HeightSlider.Value = Profile?.Height ?? species.DefaultHeight; + + WidthSlider.MinValue = species.MinWidth; + WidthSlider.MaxValue = species.MaxWidth; + WidthSlider.Value = Profile?.Width ?? species.DefaultWidth; var height = MathF.Round(species.AverageHeight * HeightSlider.Value); HeightLabel.Text = Loc.GetString("humanoid-profile-editor-height-label", ("height", (int) height)); + + var width = MathF.Round(species.AverageWidth * WidthSlider.Value); + WidthLabel.Text = Loc.GetString("humanoid-profile-editor-width-label", ("width", (int) width)); } - private void UpdateWidthControls() + private enum SliderUpdate { - if (Profile == null) - return; + Height, + Width, + Both + } - var species = _species.Find(x => x.ID == Profile.Species) ?? _species.First(); + private void UpdateDimensions(SliderUpdate updateType) + { + var species = _species.Find(x => x.ID == Profile?.Species) ?? _species.First(); - WidthSlider.MinValue = species.MinWidth; - WidthSlider.Value = Profile.Width; - WidthSlider.MaxValue = species.MaxWidth; + if (Profile == null) return; + + var heightValue = Math.Clamp(HeightSlider.Value, species.MinHeight, species.MaxHeight); + var widthValue = Math.Clamp(WidthSlider.Value, species.MinWidth, species.MaxWidth); + var sizeRatio = species.SizeRatio; + var ratio = heightValue / widthValue; + + if (updateType == SliderUpdate.Height || updateType == SliderUpdate.Both) + if (ratio < 1 / sizeRatio || ratio > sizeRatio) + widthValue = heightValue / (ratio < 1 / sizeRatio ? (1 / sizeRatio) : sizeRatio); + + if (updateType == SliderUpdate.Width || updateType == SliderUpdate.Both) + if (ratio < 1 / sizeRatio || ratio > sizeRatio) + heightValue = widthValue * (ratio < 1 / sizeRatio ? (1 / sizeRatio) : sizeRatio); + + + heightValue = Math.Clamp(heightValue, species.MinHeight, species.MaxHeight); + widthValue = Math.Clamp(widthValue, species.MinWidth, species.MaxWidth); + + HeightSlider.Value = heightValue; + WidthSlider.Value = widthValue; + + SetProfileHeight(heightValue); + SetProfileWidth(widthValue); + + var height = MathF.Round(species.AverageHeight * HeightSlider.Value); + HeightLabel.Text = Loc.GetString("humanoid-profile-editor-height-label", ("height", (int) height)); var width = MathF.Round(species.AverageWidth * WidthSlider.Value); WidthLabel.Text = Loc.GetString("humanoid-profile-editor-width-label", ("width", (int) width)); + + UpdateWeight(); } private void UpdateWeight() @@ -1456,7 +1437,9 @@ private void UpdateWeight() var avg = (Profile.Width + Profile.Height) / 2; var weight = MathF.Round(MathF.PI * MathF.Pow(radius * avg, 2) * density); WeightLabel.Text = Loc.GetString("humanoid-profile-editor-weight-label", ("weight", (int) weight)); - } + } + else // Whelp, the fixture doesn't exist, guesstimate it instead + WeightLabel.Text = Loc.GetString("humanoid-profile-editor-weight-label", ("weight", (int) 71)); SpriteView.InvalidateMeasure(); } diff --git a/Content.Shared/Humanoid/Prototypes/SpeciesPrototype.cs b/Content.Shared/Humanoid/Prototypes/SpeciesPrototype.cs index 121af8d629..36b1e2387b 100644 --- a/Content.Shared/Humanoid/Prototypes/SpeciesPrototype.cs +++ b/Content.Shared/Humanoid/Prototypes/SpeciesPrototype.cs @@ -127,6 +127,12 @@ public sealed partial class SpeciesPrototype : IPrototype [DataField] public int MaxAge = 120; + /// + /// The minimum height and width ratio for this species + /// + [DataField] + public float SizeRatio = 1.2f; + /// /// The minimum height for this species /// diff --git a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs index 862114a413..dc1e6b736c 100644 --- a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs +++ b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs @@ -419,7 +419,14 @@ public virtual void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, humanoid.CustomSpecieName = profile.Customspeciename; - _heightAdjust.SetScale(uid, new Vector2(profile.Width, profile.Height)); + var species = _proto.Index(humanoid.Species); + + if (profile.Height <= 0 || profile.Width <= 0) + SetScale(uid, new Vector2(species.DefaultWidth, species.DefaultHeight), true, humanoid); + else + SetScale(uid, new Vector2(profile.Width, profile.Height), true, humanoid); + + _heightAdjust.SetScale(uid, new Vector2(humanoid.Width, humanoid.Height)); humanoid.LastProfileLoaded = profile; // DeltaV - let paradox anomaly be cloned From 344d03588068818bdc8b96041e7cfe899c76d5bc Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Wed, 16 Oct 2024 12:54:57 +0000 Subject: [PATCH 15/76] Automatic Changelog Update (#1049) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 5e659ff847..08e1e84290 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7297,3 +7297,12 @@ Entries: id: 6454 time: '2024-10-16T03:08:07.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1057 +- author: Aidenkrz + changes: + - type: Tweak + message: Height and width are now constrained by each other. + - type: Fix + message: Humanoids can no longer phase through walls. + id: 6455 + time: '2024-10-16T12:54:15.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1049 From 5eff873c6e7f91d12d5ff2c546e1bedfefcf1d5c Mon Sep 17 00:00:00 2001 From: Aiden Date: Wed, 16 Oct 2024 07:55:19 -0500 Subject: [PATCH 16/76] Bluespace and Normality Tweaks/Fixes + Bluespace Part Crafting (#1052) # Description Bluespace crystals would turn into ore when ejected from autolathe, lead me down a rabbit hole of issues with them. - fixes above mentioned - normality crystals dont appear as bluespace when inserted into lathes anymore - adds textures for ores and crystals (grey cuz its "normal" duh) - fixes glimmer drain using a DIFFERENT normality crystal that was made with chems - the old chem version now gives new normality crystals - add crafting recipes for bluespace parts (fixes not being able to reverse engineer them) Fixes #980 ---

Media

![image](https://github.com/user-attachments/assets/4464948b-edbe-4b8b-93ad-21aae192b2aa)

--- # Changelog :cl: - fix: Bluespace crystals now properly eject from lathes. - fix: Bluespace parts can be reverse engineered. --------- Signed-off-by: Aiden Co-authored-by: VMSolidus Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> --- .../Locale/en-US/materials/materials.ftl | 2 + .../Markers/Spawners/Random/salvage.yml | 6 +- .../Entities/Objects/Materials/bluespace.yml | 59 ++++++++++- .../Entities/Objects/Materials/ore.yml | 8 +- .../Entities/Objects/Misc/machine_parts.yml | 73 +------------- .../Entities/Structures/Machines/lathe.yml | 5 + .../Objects/Specific/Research/crystals.yml | 93 ------------------ .../Nyanotrasen/Recipes/Lathes/bluespace.yml | 33 ------- .../Nyanotrasen/Recipes/Reactions/psionic.yml | 2 +- .../Nyanotrasen/Stacks/materials.yml | 29 ------ .../Reagents/Materials/materials.yml | 18 ++++ .../Prototypes/Reagents/Materials/ores.yml | 8 +- Resources/Prototypes/Recipes/Lathes/Parts.yml | 31 ++++++ Resources/Prototypes/Recipes/Lathes/sheet.yml | 34 +++++++ .../Prototypes/Stacks/Materials/crystals.yml | 16 +++ Resources/Prototypes/Stacks/Materials/ore.yml | 17 +++- .../XenoArch/Effects/normal_effects.yml | 4 +- .../Objects/Materials/materials.rsi/meta.json | 20 ---- .../Materials/materials.rsi/bluespace.png | Bin .../Materials/materials.rsi/bluespace_2.png | Bin .../Materials/materials.rsi/bluespace_3.png | Bin .../Objects/Materials/materials.rsi/meta.json | 20 +++- .../Materials/materials.rsi/normality.png | Bin 0 -> 231 bytes .../Materials/materials.rsi/normality_2.png | Bin 0 -> 256 bytes .../Materials/materials.rsi/normality_3.png | Bin 0 -> 265 bytes .../Objects/Materials/ore.rsi/meta.json | 5 +- .../Objects/Materials/ore.rsi/normality.png | Bin 0 -> 248 bytes 27 files changed, 221 insertions(+), 262 deletions(-) delete mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Research/crystals.yml delete mode 100644 Resources/Prototypes/Nyanotrasen/Recipes/Lathes/bluespace.yml delete mode 100644 Resources/Prototypes/Nyanotrasen/Stacks/materials.yml delete mode 100644 Resources/Textures/Nyanotrasen/Objects/Materials/materials.rsi/meta.json rename Resources/Textures/{Nyanotrasen => }/Objects/Materials/materials.rsi/bluespace.png (100%) rename Resources/Textures/{Nyanotrasen => }/Objects/Materials/materials.rsi/bluespace_2.png (100%) rename Resources/Textures/{Nyanotrasen => }/Objects/Materials/materials.rsi/bluespace_3.png (100%) create mode 100644 Resources/Textures/Objects/Materials/materials.rsi/normality.png create mode 100644 Resources/Textures/Objects/Materials/materials.rsi/normality_2.png create mode 100644 Resources/Textures/Objects/Materials/materials.rsi/normality_3.png create mode 100644 Resources/Textures/Objects/Materials/ore.rsi/normality.png diff --git a/Resources/Locale/en-US/materials/materials.ftl b/Resources/Locale/en-US/materials/materials.ftl index f8d54ee21a..b213e7d1e9 100644 --- a/Resources/Locale/en-US/materials/materials.ftl +++ b/Resources/Locale/en-US/materials/materials.ftl @@ -25,6 +25,8 @@ materials-meat = meat materials-web = silk materials-bones = bone materials-coal = coal +materials-bluespace = bluespace +materials-normality = normality # Ores materials-raw-iron = raw iron diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/salvage.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/salvage.yml index e00d7ba32d..4a611fe026 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/salvage.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/salvage.yml @@ -90,8 +90,8 @@ state: super_matter_bin - type: RandomSpawner rarePrototypes: - - QuadraticCapacitorStockPart - - FemtoManipulatorStockPart + - BluespaceCapacitorStockPart + - BluespaceManipulatorStockPart - BluespaceMatterBinStockPart rareChance: 0.05 prototypes: @@ -130,7 +130,7 @@ state: bluespace_matter_bin - type: RandomSpawner prototypes: - - QuadraticCapacitorStockPart + - BluespaceCapacitorStockPart - PicoManipulatorStockPart - BluespaceMatterBinStockPart offset: 0.0 diff --git a/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml b/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml index f93534ecd5..bc0ed8f03c 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml @@ -5,7 +5,7 @@ name: bluespace crystal components: - type: Sprite - sprite: Nyanotrasen/Objects/Materials/materials.rsi + sprite: Objects/Materials/materials.rsi layers: - state: bluespace_3 map: ["base"] @@ -54,3 +54,60 @@ components: - type: Stack count: 5 + +- type: entity + parent: MaterialBase + id: MaterialNormality + suffix: Full + name: normality crystal + components: + - type: Sprite + sprite: Objects/Materials/materials.rsi + layers: + - state: normality_3 + map: ["base"] + - type: Appearance + - type: Material + - type: PhysicalComposition + materialComposition: + Normality: 100 + - type: Tag + tags: + - NormalityCrystal + - RawMaterial + - type: Stack + stackType: Normality + baseLayer: base + layerStates: + - normality + - normality_2 + - normality_3 + count: 5 + - type: Item + size: Small + +- type: entity + parent: MaterialNormality + id: MaterialNormality1 + suffix: 1 + components: + - type: Sprite + state: normality + - type: Stack + count: 1 + +- type: entity + parent: MaterialNormality1 + id: MaterialNormality3 + suffix: 3 + components: + - type: Stack + count: 3 + +- type: entity + parent: MaterialNormality1 + id: MaterialNormality5 + suffix: 5 + components: + - type: Stack + count: 5 diff --git a/Resources/Prototypes/Entities/Objects/Materials/ore.yml b/Resources/Prototypes/Entities/Objects/Materials/ore.yml index 503f4707d5..521d3cf1ec 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/ore.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/ore.yml @@ -360,7 +360,7 @@ - type: Material - type: PhysicalComposition materialComposition: - Bluespace: 100 + RawBluespace: 100 - type: entity parent: BluespaceOre @@ -379,12 +379,12 @@ - type: Stack stackType: NormalityOre - type: Sprite - state: bluespace + state: normality - type: Item - type: Material - type: PhysicalComposition materialComposition: - Normality: 100 + RawNormality: 100 - type: entity parent: NormalityOre @@ -392,4 +392,4 @@ suffix: Single components: - type: Stack - count: 1 \ No newline at end of file + count: 1 diff --git a/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml b/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml index d90b03528e..8538a4342d 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/machine_parts.yml @@ -177,7 +177,7 @@ # Rating 4 - type: entity - id: QuadraticCapacitorStockPart + id: BluespaceCapacitorStockPart name: bluespace capacitor parent: CapacitorStockPart description: A bluespace capacitor used in the construction of a variety of devices. @@ -190,10 +190,10 @@ - type: ReverseEngineering # Nyano difficulty: 4 recipes: - - QuadraticCapacitorStockPart + - BluespaceCapacitorStockPart - type: entity - id: FemtoManipulatorStockPart + id: BluespaceManipulatorStockPart name: bluespace manipulator parent: MicroManipulatorStockPart description: A bluespace manipulator used in the construction of a variety of devices. @@ -206,7 +206,7 @@ - type: ReverseEngineering # Nyano difficulty: 4 recipes: - - FemtoManipulatorStockPart + - BluespaceManipulatorStockPart - type: entity id: BluespaceMatterBinStockPart @@ -223,68 +223,3 @@ difficulty: 4 recipes: - BluespaceMatterBinStockPart - -# Subspace stock parts (REMOVE THESE) - -- type: entity - id: AnsibleSubspaceStockPart - name: subspace ansible - parent: BaseStockPart - description: A compact module capable of sensing extradimensional activity. - components: - - type: Sprite - state: subspace_ansible - -- type: entity - id: FilterSubspaceStockPart - name: hyperwave filter - parent: BaseStockPart - description: A tiny device capable of filtering and converting super-intense radiowaves. - components: - - type: Sprite - state: hyperwave_filter - -- type: entity - id: AmplifierSubspaceStockPart - name: subspace amplifier - parent: BaseStockPart - description: A compact micro-machine capable of amplifying weak subspace transmissions. - components: - - type: Sprite - state: subspace_amplifier - -- type: entity - id: TreatmentSubspaceStockPart - name: subspace treatment disk - parent: BaseStockPart - description: A compact micro-machine capable of stretching out hyper-compressed radio waves. - components: - - type: Sprite - state: treatment_disk - -- type: entity - id: AnalyzerSubspaceStockPart - name: subspace wavelength analyzer - parent: BaseStockPart - description: A sophisticated analyzer capable of analyzing cryptic subspace wavelengths. - components: - - type: Sprite - state: wavelength_analyzer - -- type: entity - id: CrystalSubspaceStockPart - name: ansible crystal - parent: BaseStockPart - description: A crystal made from pure glass used to transmit laser databursts to subspace. - components: - - type: Sprite - state: ansible_crystal - -- type: entity - id: TransmitterSubspaceStockPart - name: subspace transmitter - parent: BaseStockPart - description: A large piece of equipment used to open a window into the subspace dimension. - components: - - type: Sprite - state: subspace_transmitter diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 0f2b059717..35434003d2 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -294,6 +294,9 @@ - SuperCapacitorStockPart - SuperMatterBinStockPart - PicoManipulatorStockPart + - BluespaceCapacitorStockPart + - BluespaceManipulatorStockPart + - BluespaceMatterBinStockPart - AdvMopItem - WeaponSprayNozzle - ClothingBackpackWaterTank @@ -1307,6 +1310,8 @@ materialUseMultiplier: 0.75 timeMultiplier: 0.5 staticRecipes: + - BluespaceCrystal + - NormalityCrystal - SheetSteel30 - SheetGlass30 - SheetRGlass30 diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Research/crystals.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Research/crystals.yml deleted file mode 100644 index 3f6539c502..0000000000 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Research/crystals.yml +++ /dev/null @@ -1,93 +0,0 @@ -- type: entity - parent: BaseItem - id: CrystalNormality - name: normality crystal - description: It looks... normal. Placeholder sprite. - components: - - type: Sprite - sprite: Nyanotrasen/Objects/Materials/materials.rsi - state: bluespace - color: gray - - type: Tag - tags: - - NormalityCrystal - -# - type: entity -# parent: BaseItem -# id: CrystalSoul -# name: soul crystal -# description: Contains a soul. Placeholder sprite. -# components: -# - type: Sprite -# sprite: Nyanotrasen/Objects/Materials/materials.rsi -# state: bluespace -# color: purple -# - type: Speech -# - type: Psionic -# - type: SoulCrystal -# - type: SolutionContainerManager -# solutions: -# ectoplasm: -# maxvol: 50 -# reagents: -# - ReagentId: Ectoplasm -# Quantity: 50 -# - type: Extractable -# grindableSolutionName: ectoplasm - -- type: entity - parent: MaterialBase - id: MaterialNormality - suffix: Full - name: normality crystal - components: - - type: Sprite - sprite: Nyanotrasen/Objects/Materials/materials.rsi - layers: - - state: bluespace_3 - map: ["base"] - - type: Appearance - - type: Material - - type: PhysicalComposition - materialComposition: - Bluespace: 100 - - type: Tag - tags: - - NormalityCrystal - - RawMaterial - - type: Stack - stackType: Normality - baseLayer: base - layerStates: - - bluespace - - bluespace_2 - - bluespace_3 - count: 5 - - type: Item - size: Small - -- type: entity - parent: MaterialNormality - id: MaterialNormality1 - suffix: 1 - components: - - type: Sprite - state: bluespace - - type: Stack - count: 1 - -- type: entity - parent: MaterialNormality1 - id: MaterialNormality3 - suffix: 3 - components: - - type: Stack - count: 3 - -- type: entity - parent: MaterialNormality1 - id: MaterialNormality5 - suffix: 5 - components: - - type: Stack - count: 5 diff --git a/Resources/Prototypes/Nyanotrasen/Recipes/Lathes/bluespace.yml b/Resources/Prototypes/Nyanotrasen/Recipes/Lathes/bluespace.yml deleted file mode 100644 index 094e4f557c..0000000000 --- a/Resources/Prototypes/Nyanotrasen/Recipes/Lathes/bluespace.yml +++ /dev/null @@ -1,33 +0,0 @@ -- type: latheRecipe - id: CoreSilver - icon: - sprite: Objects/Misc/guardian_info.rsi - state: icon - result: CoreSilver - completetime: 10 - materials: - Bluespace: 500 - Silver: 1000 - Plastic: 1000 - -- type: latheRecipe - id: BluespaceCrystal - icon: - sprite: Nyanotrasen/Objects/Materials/materials.rsi - state: bluespace - result: MaterialBluespace1 - applyMaterialDiscount: false - completetime: 4 - materials: - Bluespace: 100 - -- type: latheRecipe - id: NormalityCrystal - icon: - sprite: Nyanotrasen/Objects/Materials/materials.rsi - state: bluespace - result: MaterialNormality1 - applyMaterialDiscount: false - completetime: 4 - materials: - Normality: 100 diff --git a/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/psionic.yml b/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/psionic.yml index 304c9b686a..d9d2ed51b5 100644 --- a/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/psionic.yml +++ b/Resources/Prototypes/Nyanotrasen/Recipes/Reactions/psionic.yml @@ -30,6 +30,6 @@ catalyst: true effects: - !type:CreateEntityReactionEffect - entity: CrystalNormality + entity: MaterialNormality1 - !type:ChangeGlimmerReactionEffect count: -10 diff --git a/Resources/Prototypes/Nyanotrasen/Stacks/materials.yml b/Resources/Prototypes/Nyanotrasen/Stacks/materials.yml deleted file mode 100644 index 35f50575a5..0000000000 --- a/Resources/Prototypes/Nyanotrasen/Stacks/materials.yml +++ /dev/null @@ -1,29 +0,0 @@ -- type: stack - id: Bluespace - name: bluespace - icon: { sprite: Nyanotrasen/Objects/Materials/materials.rsi, state: bluespace } - spawn: MaterialBluespace1 - maxCount: 5 - itemSize: 1 - -- type: stack - id: BluespaceOre - name: rough bluespace - icon: { sprite: Objects/Materials/ore.rsi, state: bluespace } - spawn: BluespaceOre1 - maxCount: 30 - -- type: stack - id: Normality - name: normality - icon: { sprite: Nyanotrasen/Objects/Materials/materials.rsi, state: bluespace } - spawn: MaterialNormality1 - maxCount: 5 - itemSize: 1 - -- type: stack - id: NormalityOre - name: rough normality - icon: { sprite: Objects/Materials/ore.rsi, state: bluespace } - spawn: NormalityOre1 - maxCount: 30 \ No newline at end of file diff --git a/Resources/Prototypes/Reagents/Materials/materials.yml b/Resources/Prototypes/Reagents/Materials/materials.yml index fa51608a22..f56a712cd8 100644 --- a/Resources/Prototypes/Reagents/Materials/materials.yml +++ b/Resources/Prototypes/Reagents/Materials/materials.yml @@ -116,3 +116,21 @@ icon: { sprite: Objects/Materials/ore.rsi, state: coal } color: "#404040" price: 0 + +- type: material + id: Bluespace + stackEntity: MaterialBluespace1 + name: materials-bluespace + unit: materials-unit-piece + icon: { sprite: Objects/Materials/materials.rsi, state: bluespace } + color: "#53a9ff" + price: 7.5 + +- type: material + id: Normality + stackEntity: MaterialNormality1 + name: materials-normality + unit: materials-unit-piece + icon: { sprite: Objects/Materials/materials.rsi, state: normality } + color: "#53a9ff" + price: 7.5 diff --git a/Resources/Prototypes/Reagents/Materials/ores.yml b/Resources/Prototypes/Reagents/Materials/ores.yml index efba840639..7b887b2f43 100644 --- a/Resources/Prototypes/Reagents/Materials/ores.yml +++ b/Resources/Prototypes/Reagents/Materials/ores.yml @@ -70,19 +70,19 @@ price: 0.075 - type: material - id: Bluespace + id: RawBluespace stackEntity: BluespaceOre1 name: materials-raw-bluespace unit: materials-unit-piece - icon: { sprite: Nyanotrasen/Objects/Materials/materials.rsi, state: bluespace } # TODO actual ore sprite + icon: { sprite: Objects/Materials/ore.rsi, state: bluespace } color: "#53a9ff" price: 7.5 - type: material - id: Normality + id: RawNormality stackEntity: NormalityOre1 name: materials-raw-normality unit: materials-unit-piece - icon: { sprite: Nyanotrasen/Objects/Materials/materials.rsi, state: bluespace } # TODO actual ore sprite + icon: { sprite: Objects/Materials/ore.rsi, state: normality } color: "#53a9ff" price: 7.5 diff --git a/Resources/Prototypes/Recipes/Lathes/Parts.yml b/Resources/Prototypes/Recipes/Lathes/Parts.yml index 496bc3a8a4..4534bf6097 100644 --- a/Resources/Prototypes/Recipes/Lathes/Parts.yml +++ b/Resources/Prototypes/Recipes/Lathes/Parts.yml @@ -84,3 +84,34 @@ Plastic: 150 Plasma: 75 Gold: 75 + +#Rating 4 +- type: latheRecipe + id: BluespaceCapacitorStockPart + result: BluespaceCapacitorStockPart + completetime: 3 + materials: + Steel: 150 + Plastic: 150 + Gold: 75 + Bluespace: 300 + +- type: latheRecipe + id: BluespaceMatterBinStockPart + result: BluespaceMatterBinStockPart + completetime: 3 + materials: + Steel: 150 + Plastic: 150 + Gold: 75 + Bluespace: 300 + +- type: latheRecipe + id: BluespaceManipulatorStockPart + result: BluespaceManipulatorStockPart + completetime: 3 + materials: + Steel: 150 + Plastic: 150 + Gold: 75 + Bluespace: 300 diff --git a/Resources/Prototypes/Recipes/Lathes/sheet.yml b/Resources/Prototypes/Recipes/Lathes/sheet.yml index 053715a181..9f83c58e66 100644 --- a/Resources/Prototypes/Recipes/Lathes/sheet.yml +++ b/Resources/Prototypes/Recipes/Lathes/sheet.yml @@ -176,3 +176,37 @@ completetime: 1 materials: Wood: 50 + +- type: latheRecipe + id: CoreSilver + icon: + sprite: Objects/Misc/guardian_info.rsi + state: icon + result: CoreSilver + completetime: 10 + materials: + Bluespace: 500 + Silver: 1000 + Plastic: 1000 + +- type: latheRecipe + id: BluespaceCrystal + icon: + sprite: Objects/Materials/materials.rsi + state: bluespace + result: MaterialBluespace1 + applyMaterialDiscount: false + completetime: 4 + materials: + RawBluespace: 100 + +- type: latheRecipe + id: NormalityCrystal + icon: + sprite: Objects/Materials/materials.rsi + state: normality + result: MaterialNormality1 + applyMaterialDiscount: false + completetime: 4 + materials: + RawNormality: 100 diff --git a/Resources/Prototypes/Stacks/Materials/crystals.yml b/Resources/Prototypes/Stacks/Materials/crystals.yml index 274f9c10ea..e4c9e48718 100644 --- a/Resources/Prototypes/Stacks/Materials/crystals.yml +++ b/Resources/Prototypes/Stacks/Materials/crystals.yml @@ -4,3 +4,19 @@ icon: Objects/Specific/Syndicate/telecrystal.rsi spawn: Telecrystal1 itemSize: 1 + +- type: stack + id: Bluespace + name: bluespace + icon: { sprite: Objects/Materials/materials.rsi, state: bluespace } + spawn: MaterialBluespace1 + maxCount: 5 + itemSize: 1 + +- type: stack + id: Normality + name: normality + icon: { sprite: Objects/Materials/materials.rsi, state: normality } + spawn: MaterialNormality1 + maxCount: 5 + itemSize: 1 diff --git a/Resources/Prototypes/Stacks/Materials/ore.yml b/Resources/Prototypes/Stacks/Materials/ore.yml index 2a95393c27..cf7fcb0483 100644 --- a/Resources/Prototypes/Stacks/Materials/ore.yml +++ b/Resources/Prototypes/Stacks/Materials/ore.yml @@ -46,7 +46,6 @@ maxCount: 30 itemSize: 2 - - type: stack id: BananiumOre name: bananium ore @@ -70,3 +69,19 @@ spawn: Salt1 maxCount: 30 itemSize: 2 + +- type: stack + id: BluespaceOre + name: raw bluespace + icon: { sprite: Objects/Materials/ore.rsi, state: bluespace } + spawn: BluespaceOre1 + maxCount: 30 + itemSize: 2 + +- type: stack + id: NormalityOre + name: raw normality + icon: { sprite: Objects/Materials/ore.rsi, state: normality } + spawn: NormalityOre1 + maxCount: 30 + itemSize: 2 diff --git a/Resources/Prototypes/XenoArch/Effects/normal_effects.yml b/Resources/Prototypes/XenoArch/Effects/normal_effects.yml index 4dcdd78435..a343ebd77b 100644 --- a/Resources/Prototypes/XenoArch/Effects/normal_effects.yml +++ b/Resources/Prototypes/XenoArch/Effects/normal_effects.yml @@ -592,10 +592,10 @@ - type: SpawnArtifact maxSpawns: 10 spawns: - - id: QuadraticCapacitorStockPart + - id: BluespaceCapacitorStockPart prob: 0.5 maxAmount: 3 - - id: FemtoManipulatorStockPart + - id: BluespaceManipulatorStockPart prob: 0.5 maxAmount: 3 - id: BluespaceMatterBinStockPart diff --git a/Resources/Textures/Nyanotrasen/Objects/Materials/materials.rsi/meta.json b/Resources/Textures/Nyanotrasen/Objects/Materials/materials.rsi/meta.json deleted file mode 100644 index 87a4078ff7..0000000000 --- a/Resources/Textures/Nyanotrasen/Objects/Materials/materials.rsi/meta.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "bluespace" - }, - { - "name": "bluespace_2" - }, - { - "name": "bluespace_3" - } - ] -} diff --git a/Resources/Textures/Nyanotrasen/Objects/Materials/materials.rsi/bluespace.png b/Resources/Textures/Objects/Materials/materials.rsi/bluespace.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Materials/materials.rsi/bluespace.png rename to Resources/Textures/Objects/Materials/materials.rsi/bluespace.png diff --git a/Resources/Textures/Nyanotrasen/Objects/Materials/materials.rsi/bluespace_2.png b/Resources/Textures/Objects/Materials/materials.rsi/bluespace_2.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Materials/materials.rsi/bluespace_2.png rename to Resources/Textures/Objects/Materials/materials.rsi/bluespace_2.png diff --git a/Resources/Textures/Nyanotrasen/Objects/Materials/materials.rsi/bluespace_3.png b/Resources/Textures/Objects/Materials/materials.rsi/bluespace_3.png similarity index 100% rename from Resources/Textures/Nyanotrasen/Objects/Materials/materials.rsi/bluespace_3.png rename to Resources/Textures/Objects/Materials/materials.rsi/bluespace_3.png diff --git a/Resources/Textures/Objects/Materials/materials.rsi/meta.json b/Resources/Textures/Objects/Materials/materials.rsi/meta.json index 78f497c0cd..016ccddc29 100644 --- a/Resources/Textures/Objects/Materials/materials.rsi/meta.json +++ b/Resources/Textures/Objects/Materials/materials.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24 , bear pelt remade by Alekshhh, wood sprite modified by MisterMecky, wood_2 and wood_3 made by MisterMecky based on wood sprite, cardboard sprites made by MisterMecky, bananium, bananium_1 and peel made by brainfood1183 (github) for ss14. Pyrotton sprites are drawn by Ubaser, using the cotton material sprites as a base.", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/c6e3401f2e7e1e55c57060cdf956a98ef1fefc24 , bear pelt remade by Alekshhh, wood sprite modified by MisterMecky, wood_2 and wood_3 made by MisterMecky based on wood sprite, cardboard sprites made by MisterMecky, bananium, bananium_1 and peel made by brainfood1183 (github) for ss14. Pyrotton sprites are drawn by Ubaser, using the cotton material sprites as a base. Bluespace and Normality taken from tgstation at https://github.com/tgstation/tgstation/commit/f8f4aeda930fcd0805ca4cc76d9bc9412a5b3428, reshaded by Aidenkrz", "size": { "x": 32, "y": 32 @@ -159,6 +159,24 @@ }, { "name": "bones_3" + }, + { + "name": "bluespace" + }, + { + "name": "bluespace_2" + }, + { + "name": "bluespace_3" + }, + { + "name": "normality" + }, + { + "name": "normality_2" + }, + { + "name": "normality_3" } ] } diff --git a/Resources/Textures/Objects/Materials/materials.rsi/normality.png b/Resources/Textures/Objects/Materials/materials.rsi/normality.png new file mode 100644 index 0000000000000000000000000000000000000000..3a91811667ba276c3670d32c351b52373f606c5b GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCiji0(?ST3kwS~GBT!4oqG82;kLH6nKNfbMn(c9`hbPaA?*3jBIb*d3-%bJ9ANDZ*tW+y3uqpL Mr>mdKI;Vst0HuOUq5uE@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Materials/materials.rsi/normality_2.png b/Resources/Textures/Objects/Materials/materials.rsi/normality_2.png new file mode 100644 index 0000000000000000000000000000000000000000..fd672a9b09dbd77688a87d50789aea402b4e3aa0 GIT binary patch literal 256 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCiji0(?ST3kwS~GBT!4oqG82;kLH6nKNfbMn(c9`ZPM;uvD#|Mt8i-vI>-fvtVjB}BuX3;b+EovR0Mp4+6ek!ngf$s0NSB{zdB2*W#*5MJpGx!G nM(M_>mi-dVTey>03+fqi)1+?LcD>jEw1~me)z4*}Q$iB}GB#SB literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Materials/materials.rsi/normality_3.png b/Resources/Textures/Objects/Materials/materials.rsi/normality_3.png new file mode 100644 index 0000000000000000000000000000000000000000..8895885954dc4ebc0bb0431371fa7b5f68ae9e26 GIT binary patch literal 265 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCiji0(?ST3kwS~GBT!4oqG82;kLH6nKNfbMn(c9`<=Rn8*|NG7Lyu;3_+}vC{ z@zP|GOckq!*X#~En3hMbp8PY{6KRWBb!DUM zkLU-T4K*(tuKs5@!*Khs7sDOTRVxl0tL=7M@5FH6g>6TXmNri@>s~uWh2#A1zgYx- gxWKsjz2h_1IVw^g^|(X*fEF-#y85}Sb4q9e04|VKfB*mh literal 0 HcmV?d00001 From 1d9c7a8b77d31b166de01f3f75e4656efdcf9f02 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Wed, 16 Oct 2024 12:55:45 +0000 Subject: [PATCH 17/76] Automatic Changelog Update (#1052) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 08e1e84290..bdd73a7aa3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7306,3 +7306,12 @@ Entries: id: 6455 time: '2024-10-16T12:54:15.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1049 +- author: Aidenkrz + changes: + - type: Fix + message: Bluespace crystals now properly eject from lathes. + - type: Fix + message: Bluespace parts can be reverse engineered. + id: 6456 + time: '2024-10-16T12:55:19.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1052 From ae58bf49d5dfa97b7a6be852851ab25c5fe93d5d Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Wed, 16 Oct 2024 17:45:22 -0400 Subject: [PATCH 18/76] Fix RSI Validator (#1060) # Description There were some missing meta.json entries. --- .../Clothing/Head/Hardsuits/atmospherics.rsi/meta.json | 4 ++++ .../Textures/Clothing/Head/Hardsuits/spatiohelm.rsi/meta.json | 4 ++++ .../Clothing/OuterClothing/Hardsuits/cburn.rsi/meta.json | 4 ++++ .../OuterClothing/Hardsuits/syndieelite.rsi/meta.json | 4 ++++ .../Clothing/OuterClothing/Suits/eva_emergency.rsi/meta.json | 4 ++++ .../Textures/Clothing/OuterClothing/Suits/fire.rsi/meta.json | 4 ++++ .../Textures/Clothing/OuterClothing/Suits/rad.rsi/meta.json | 4 ++++ 7 files changed, 28 insertions(+) diff --git a/Resources/Textures/Clothing/Head/Hardsuits/atmospherics.rsi/meta.json b/Resources/Textures/Clothing/Head/Hardsuits/atmospherics.rsi/meta.json index 8236090f7a..c24aa39b80 100644 --- a/Resources/Textures/Clothing/Head/Hardsuits/atmospherics.rsi/meta.json +++ b/Resources/Textures/Clothing/Head/Hardsuits/atmospherics.rsi/meta.json @@ -31,6 +31,10 @@ "name": "equipped-head-unshaded", "directions": 4 }, + { + "name": "equipped-head-unshaded-vox", + "directions": 4 + }, { "name": "equipped-head-vox", "directions": 4 diff --git a/Resources/Textures/Clothing/Head/Hardsuits/spatiohelm.rsi/meta.json b/Resources/Textures/Clothing/Head/Hardsuits/spatiohelm.rsi/meta.json index 29335fd904..64ba4dc5d6 100644 --- a/Resources/Textures/Clothing/Head/Hardsuits/spatiohelm.rsi/meta.json +++ b/Resources/Textures/Clothing/Head/Hardsuits/spatiohelm.rsi/meta.json @@ -32,6 +32,10 @@ "name": "equipped-head-unshaded", "directions": 4 }, + { + "name": "equipped-head-unshaded-vox", + "directions": 4 + }, { "name": "equipped-head-vox", "directions": 4 diff --git a/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/meta.json index 458fb144f7..2edd20e1dc 100644 --- a/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Hardsuits/cburn.rsi/meta.json @@ -18,6 +18,10 @@ "name": "equipped-OUTERCLOTHING-harpy", "directions": 4 }, + { + "name": "equipped-OUTERCLOTHING-vox", + "directions": 4 + }, { "name": "inhand-left", "directions": 4 diff --git a/Resources/Textures/Clothing/OuterClothing/Hardsuits/syndieelite.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Hardsuits/syndieelite.rsi/meta.json index 226372c9d3..cde1c05977 100644 --- a/Resources/Textures/Clothing/OuterClothing/Hardsuits/syndieelite.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Hardsuits/syndieelite.rsi/meta.json @@ -18,6 +18,10 @@ "name": "equipped-OUTERCLOTHING-harpy", "directions": 4 }, + { + "name": "equipped-OUTERCLOTHING-vox", + "directions": 4 + }, { "name": "inhand-left", "directions": 4 diff --git a/Resources/Textures/Clothing/OuterClothing/Suits/eva_emergency.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Suits/eva_emergency.rsi/meta.json index 8d09b75006..87ae5d0fb0 100644 --- a/Resources/Textures/Clothing/OuterClothing/Suits/eva_emergency.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Suits/eva_emergency.rsi/meta.json @@ -18,6 +18,10 @@ "name": "equipped-OUTERCLOTHING-harpy", "directions": 4 }, + { + "name": "equipped-OUTERCLOTHING-vox", + "directions": 4 + }, { "name": "inhand-left", "directions": 4 diff --git a/Resources/Textures/Clothing/OuterClothing/Suits/fire.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Suits/fire.rsi/meta.json index 9f4fd0c063..5edb377409 100644 --- a/Resources/Textures/Clothing/OuterClothing/Suits/fire.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Suits/fire.rsi/meta.json @@ -18,6 +18,10 @@ "name": "equipped-OUTERCLOTHING-harpy", "directions": 4 }, + { + "name": "equipped-OUTERCLOTHING-vox", + "directions": 4 + }, { "name": "inhand-left", "directions": 4 diff --git a/Resources/Textures/Clothing/OuterClothing/Suits/rad.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Suits/rad.rsi/meta.json index 8c3ddde70a..e3387cff96 100644 --- a/Resources/Textures/Clothing/OuterClothing/Suits/rad.rsi/meta.json +++ b/Resources/Textures/Clothing/OuterClothing/Suits/rad.rsi/meta.json @@ -18,6 +18,10 @@ "name": "equipped-OUTERCLOTHING-harpy", "directions": 4 }, + { + "name": "equipped-OUTERCLOTHING-vox", + "directions": 4 + }, { "name": "inhand-left", "directions": 4 From 549d0557090bfab50204002fc1d885c82f533d88 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Wed, 16 Oct 2024 18:51:18 -0400 Subject: [PATCH 19/76] Chirp (#1056) # Description This PR fixes the Singer trait, as well as the Harpy singing ability. As a bonus, the action button is no longer hardcoded to the species. # Changelog :cl: - fix: Fixed Harpy singing not opening the Midi Player. --- .../Traits/Assorted/Components/SingerComponent.cs | 3 +++ .../Traits/Assorted/Systems/SharedSingerSystem.cs | 7 +------ Resources/Prototypes/Entities/Mobs/Species/base.yml | 3 +++ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Content.Shared/Traits/Assorted/Components/SingerComponent.cs b/Content.Shared/Traits/Assorted/Components/SingerComponent.cs index 9c79166ef6..f36969e742 100644 --- a/Content.Shared/Traits/Assorted/Components/SingerComponent.cs +++ b/Content.Shared/Traits/Assorted/Components/SingerComponent.cs @@ -11,6 +11,9 @@ public sealed partial class SingerComponent : Component [DataField(required: true), AutoNetworkedField] public ProtoId Proto = string.Empty; + [DataField(serverOnly: true)] + public EntProtoId? MidiActionId = "ActionHarpyPlayMidi"; + [DataField(serverOnly: true)] public EntityUid? MidiAction; } diff --git a/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs b/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs index f772075dfb..56f76af9bb 100644 --- a/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs +++ b/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs @@ -33,17 +33,12 @@ private void OnStartup(Entity ent, ref ComponentStartup args) if (!ProtoMan.TryIndex(ent.Comp.Proto, out var singer)) return; - _actionsSystem.AddAction(ent, ref ent.Comp.MidiAction, singer.MidiActionId); + _actionsSystem.AddAction(ent, ref ent.Comp.MidiAction, ent.Comp.MidiActionId); var instrumentComp = EnsureInstrumentComp(ent); var defaultData = singer.InstrumentList[singer.DefaultInstrument]; _instrument.SetInstrumentProgram(instrumentComp, defaultData.Item1, defaultData.Item2); SetUpSwappableInstrument(ent, singer); - - EntityManager.TryGetComponent(ent.Owner, out var comp); - var entui = new Entity(ent.Owner, comp); - if (singer.MidiUi is { } uiKey && !_ui.IsUiOpen(entui, uiKey)) - _ui.OpenUi(entui, uiKey, entui); } private void OnShutdown(Entity ent, ref ComponentShutdown args) diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 271ef0c1bd..c0d23c489c 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -197,6 +197,9 @@ type: HumanoidMarkingModifierBoundUserInterface enum.StrippingUiKey.Key: type: StrippableBoundUserInterface + enum.InstrumentUiKey.Key: + type: InstrumentBoundUserInterface + requireInputValidation: false - type: Puller - type: Speech speechSounds: Alto From 5e8b1a379a38a8ee323f3b162648ffb30b3853ac Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Wed, 16 Oct 2024 22:51:43 +0000 Subject: [PATCH 20/76] Automatic Changelog Update (#1056) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index bdd73a7aa3..0bc4875ceb 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7315,3 +7315,10 @@ Entries: id: 6456 time: '2024-10-16T12:55:19.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1052 +- author: VMSolidus + changes: + - type: Fix + message: 'Fixed Harpy singing not opening the Midi Player. ' + id: 6457 + time: '2024-10-16T22:51:18.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1056 From f3a3f4e27f18d929568b52814e45998e8b07b644 Mon Sep 17 00:00:00 2001 From: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com> Date: Thu, 17 Oct 2024 01:54:33 +0300 Subject: [PATCH 21/76] Fix Forensics Cleaning (#1061) # Description Fixes cleaning forensics off of an object being impossible unless it has a scent. Explained in the latest (to date) comment to #715. Also removes some unnecessarily duplicated code and inverts an if condition. # Changelog :cl: - fix: Soap once again can be used to clean evidence off. --- .../Forensics/Systems/ForensicsSystem.cs | 66 +++++++------------ 1 file changed, 23 insertions(+), 43 deletions(-) diff --git a/Content.Server/Forensics/Systems/ForensicsSystem.cs b/Content.Server/Forensics/Systems/ForensicsSystem.cs index b26a361d5a..8f91ec41e8 100644 --- a/Content.Server/Forensics/Systems/ForensicsSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicsSystem.cs @@ -131,53 +131,32 @@ public void CopyForensicsFrom(ForensicsComponent src, EntityUid target) private void OnAfterInteract(EntityUid uid, CleansForensicsComponent component, AfterInteractEvent args) { - if (args.Handled || !args.CanReach) + if (args.Handled || !args.CanReach || !TryComp(args.Target, out var forensicsComp)) return; - if (TryComp(args.Target, out var forensicsComp) - && forensicsComp.DNAs.Count > 0 && forensicsComp.CanDnaBeCleaned - && forensicsComp.Fingerprints.Count + forensicsComp.Fibers.Count > 0 - && forensicsComp.Scent != string.Empty) - { - var cleanDelay = component.CleanDelay; - if (HasComp(args.Target)) - cleanDelay += 30; + if ((forensicsComp.DNAs.Count <= 0 || !forensicsComp.CanDnaBeCleaned) + && forensicsComp.Fingerprints.Count + forensicsComp.Fibers.Count <= 0 + && forensicsComp.Scent == string.Empty) + return; // Nothing to do if there is no DNAs, fibers, and scent - var doAfterArgs = new DoAfterArgs(EntityManager, args.User, cleanDelay, new CleanForensicsDoAfterEvent(), uid, target: args.Target, used: args.Used) - { - BreakOnHandChange = true, - NeedHand = true, - BreakOnDamage = true, - BreakOnTargetMove = true, - MovementThreshold = 0.01f, - DistanceThreshold = forensicsComp.CleanDistance, - }; - - _doAfterSystem.TryStartDoAfter(doAfterArgs); - _popupSystem.PopupEntity(Loc.GetString("forensics-cleaning", ("target", args.Target)), args.User, args.User); - - args.Handled = true; - return; - } + var cleanDelay = component.CleanDelay; + if (HasComp(args.Target)) + cleanDelay += 30; - if (TryComp(args.Target, out var scentComp)) + var doAfterArgs = new DoAfterArgs(EntityManager, args.User, cleanDelay, new CleanForensicsDoAfterEvent(), uid, target: args.Target, used: args.Used) { - var cleanDelay = component.CleanDelay + 30; - var doAfterArgs = new DoAfterArgs(EntityManager, args.User, cleanDelay, new CleanForensicsDoAfterEvent(), uid, target: args.Target, used: args.Used) - { - BreakOnHandChange = true, - NeedHand = true, - BreakOnDamage = true, - BreakOnTargetMove = true, - MovementThreshold = 0.01f, - DistanceThreshold = 1.5f, - }; - - _doAfterSystem.TryStartDoAfter(doAfterArgs); - _popupSystem.PopupEntity(Loc.GetString("forensics-cleaning", ("target", args.Target)), args.User, args.User); - - args.Handled = true; - } + BreakOnHandChange = true, + NeedHand = true, + BreakOnDamage = true, + BreakOnTargetMove = true, + MovementThreshold = 0.01f, + DistanceThreshold = forensicsComp.CleanDistance, + }; + + _doAfterSystem.TryStartDoAfter(doAfterArgs); + _popupSystem.PopupEntity(Loc.GetString("forensics-cleaning", ("target", args.Target)), args.User, args.User); + + args.Handled = true; } private void OnCleanForensicsDoAfter(EntityUid uid, ForensicsComponent component, CleanForensicsDoAfterEvent args) @@ -202,7 +181,8 @@ private void OnCleanForensicsDoAfter(EntityUid uid, ForensicsComponent component if (TryComp(args.Used, out var residue)) targetComp.Residues.Add(string.IsNullOrEmpty(residue.ResidueColor) ? Loc.GetString("forensic-residue", ("adjective", residue.ResidueAdjective)) : Loc.GetString("forensic-residue-colored", ("color", residue.ResidueColor), ("adjective", residue.ResidueAdjective))); - // If the ent has a Scent Component, we compleatly generate a new one and apply the new scent to all currently weared items. + // If the ent has a Scent Component, we completely generate a new one and apply the new scent to all currently worn items. + // TODO this is never gonna work unless you like, wash yourself with the soap??? if (TryComp(args.Target, out var scentComp)) { var generatedscent = GenerateFingerprint(length: 5); From e8f7c11643889ed845ada66ded9ad9c1979936e7 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Wed, 16 Oct 2024 22:54:58 +0000 Subject: [PATCH 22/76] Automatic Changelog Update (#1061) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0bc4875ceb..96ce3622f1 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7322,3 +7322,10 @@ Entries: id: 6457 time: '2024-10-16T22:51:18.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1056 +- author: Mnemotechnician + changes: + - type: Fix + message: Soap once again can be used to clean evidence off. + id: 6458 + time: '2024-10-16T22:54:33.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1061 From a78775bc9978cb295e5b39aed271bae99317b552 Mon Sep 17 00:00:00 2001 From: Aiden Date: Thu, 17 Oct 2024 14:21:01 -0500 Subject: [PATCH 23/76] Cocoon Cleanup & Minor Bloodsucker Tweaks (#1058) # Description - Generalizes cocooning - Allows any mob to be cocooned - Cocoon bloodsucking moved to vampirism system - Any blood sucker can drink from cocoons - Vampirism no longer fails if bloodstream isn't normal blood, but gives a pop up - Vampirism `WebRequired` actually works in a way that makes sense - Adds cocooning and bloodsucker to all spider mobs + Arachnids resolves #978 --- # Changelog :cl: - tweak: All spiders, arachne, and arachnids can cocoon mobs, and drink their blood. --------- Co-authored-by: VMSolidus --- .../{Arachne => Cocoon}/CocoonSystem.cs | 2 +- .../CocoonerSystem.cs} | 127 +++--------------- Content.Server/Vampire/BloodSuckerSystem.cs | 48 +++---- Content.Shared/Arachne/ArachneComponent.cs | 21 --- Content.Shared/Arachne/Events.cs | 11 -- Content.Shared/Arachne/WebComponent.cs | 8 -- .../{Arachne => Cocoon}/CocoonComponent.cs | 7 +- Content.Shared/Cocoon/CocoonDoAfterEvent.cs | 10 ++ Content.Shared/Cocoon/CocoonerComponent.cs | 14 ++ .../Locale/en-US/abilities/bloodsucker.ftl | 6 +- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 3 + .../Prototypes/Entities/Mobs/NPCs/space.yml | 3 + .../Entities/Mobs/Species/arachne.yml | 2 +- .../Entities/Mobs/Species/arachnid.yml | 3 + .../Entities/Mobs/NPCs/mutants.yml | 2 +- 15 files changed, 85 insertions(+), 182 deletions(-) rename Content.Client/{Arachne => Cocoon}/CocoonSystem.cs (98%) rename Content.Server/{Arachne/ArachneSystem.cs => Cocoon/CocoonerSystem.cs} (51%) delete mode 100644 Content.Shared/Arachne/ArachneComponent.cs delete mode 100644 Content.Shared/Arachne/Events.cs delete mode 100644 Content.Shared/Arachne/WebComponent.cs rename Content.Shared/{Arachne => Cocoon}/CocoonComponent.cs (61%) create mode 100644 Content.Shared/Cocoon/CocoonDoAfterEvent.cs create mode 100644 Content.Shared/Cocoon/CocoonerComponent.cs diff --git a/Content.Client/Arachne/CocoonSystem.cs b/Content.Client/Cocoon/CocoonSystem.cs similarity index 98% rename from Content.Client/Arachne/CocoonSystem.cs rename to Content.Client/Cocoon/CocoonSystem.cs index 7645c5bc81..d3eb4a8205 100644 --- a/Content.Client/Arachne/CocoonSystem.cs +++ b/Content.Client/Cocoon/CocoonSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Arachne; +using Content.Shared.Cocoon; using Content.Shared.Humanoid; using Robust.Client.GameObjects; using Robust.Shared.Containers; diff --git a/Content.Server/Arachne/ArachneSystem.cs b/Content.Server/Cocoon/CocoonerSystem.cs similarity index 51% rename from Content.Server/Arachne/ArachneSystem.cs rename to Content.Server/Cocoon/CocoonerSystem.cs index 27e8851247..a53ebbb5f5 100644 --- a/Content.Server/Arachne/ArachneSystem.cs +++ b/Content.Server/Cocoon/CocoonerSystem.cs @@ -1,7 +1,6 @@ -using Content.Shared.Arachne; +using Content.Shared.Cocoon; using Content.Shared.IdentityManagement; using Content.Shared.Verbs; -using Content.Shared.Buckle.Components; using Content.Shared.DoAfter; using Content.Shared.Stunnable; using Content.Shared.Eye.Blinding.Systems; @@ -10,29 +9,21 @@ using Content.Shared.Administration.Logs; using Content.Shared.Database; using Content.Shared.Humanoid; -using Content.Server.Buckle.Systems; using Content.Server.Popups; using Content.Server.DoAfter; -using Content.Server.Body.Components; -using Content.Server.Vampiric; using Content.Server.Speech.Components; using Robust.Shared.Containers; -using Robust.Shared.Utility; -using Robust.Server.Console; +using Content.Shared.Mobs.Components; -namespace Content.Server.Arachne +namespace Content.Server.Cocoon { - public sealed class ArachneSystem : EntitySystem + public sealed class CocooningSystem : EntitySystem { [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly DoAfterSystem _doAfter = default!; - [Dependency] private readonly BuckleSystem _buckleSystem = default!; [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] private readonly BlindableSystem _blindableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; - - [Dependency] private readonly IServerConsoleHost _host = default!; - [Dependency] private readonly BloodSuckerSystem _bloodSuckerSystem = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; private const string BodySlot = "body_slot"; @@ -40,27 +31,16 @@ public sealed class ArachneSystem : EntitySystem public override void Initialize() { base.Initialize(); - SubscribeLocalEvent>(AddCocoonVerb); - + SubscribeLocalEvent>(AddCocoonVerb); SubscribeLocalEvent(OnCocEntInserted); SubscribeLocalEvent(OnCocEntRemoved); SubscribeLocalEvent(OnDamageChanged); - SubscribeLocalEvent>(AddSuccVerb); - SubscribeLocalEvent(OnCocoonDoAfter); + SubscribeLocalEvent(OnCocoonDoAfter); } - private void AddCocoonVerb(EntityUid uid, ArachneComponent component, GetVerbsEvent args) + private void AddCocoonVerb(EntityUid uid, CocoonerComponent component, GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract) - return; - - if (args.Target == uid) - return; - - if (!TryComp(args.Target, out var bloodstream)) - return; - - if (bloodstream.BloodReagent != component.WebBloodReagent) + if (!args.CanAccess || !args.CanInteract || !HasComp(args.Target)) return; InnateVerb verb = new() @@ -77,31 +57,23 @@ private void AddCocoonVerb(EntityUid uid, ArachneComponent component, GetVerbsEv private void OnCocEntInserted(EntityUid uid, CocoonComponent component, EntInsertedIntoContainerMessage args) { - _blindableSystem.UpdateIsBlind(args.Entity); - EnsureComp(args.Entity); + component.Victim = args.Entity; if (TryComp(args.Entity, out var currentAccent)) - { - component.WasReplacementAccent = true; component.OldAccent = currentAccent.Accent; - currentAccent.Accent = "mumble"; - } else - { - component.WasReplacementAccent = false; - var replacement = EnsureComp(args.Entity); - replacement.Accent = "mumble"; - } + + EnsureComp(args.Entity).Accent = "mumble"; + EnsureComp(args.Entity); + + _blindableSystem.UpdateIsBlind(args.Entity); } private void OnCocEntRemoved(EntityUid uid, CocoonComponent component, EntRemovedFromContainerMessage args) { - if (component.WasReplacementAccent && TryComp(args.Entity, out var replacement)) - { - replacement.Accent = component.OldAccent; - } else - { + if (TryComp(args.Entity, out var replacement)) + replacement.Accent = component.OldAccent ?? replacement.Accent; + else RemComp(args.Entity); - } RemComp(args.Entity); _blindableSystem.UpdateIsBlind(args.Entity); @@ -109,79 +81,24 @@ private void OnCocEntRemoved(EntityUid uid, CocoonComponent component, EntRemove private void OnDamageChanged(EntityUid uid, CocoonComponent component, DamageChangedEvent args) { - if (!args.DamageIncreased) - return; - - if (args.DamageDelta == null) - return; - - var body = _itemSlots.GetItemOrNull(uid, BodySlot); - - if (body == null) + if (!args.DamageIncreased || args.DamageDelta == null || component.Victim == null) return; var damage = args.DamageDelta * component.DamagePassthrough; - _damageableSystem.TryChangeDamage(body, damage); + _damageableSystem.TryChangeDamage(component.Victim, damage); } - private void AddSuccVerb(EntityUid uid, CocoonComponent component, GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - if (!TryComp(args.User, out var sucker)) - return; - - if (!sucker.WebRequired) - return; - - var victim = _itemSlots.GetItemOrNull(uid, BodySlot); - - if (victim == null) - return; - - if (!TryComp(victim, out var stream)) - return; - - AlternativeVerb verb = new() - { - Act = () => - { - _bloodSuckerSystem.StartSuccDoAfter(args.User, victim.Value, sucker, stream, false); // start doafter - }, - Text = Loc.GetString("action-name-suck-blood"), - Icon = new SpriteSpecifier.Texture(new ("/Textures/Nyanotrasen/Icons/verbiconfangs.png")), - Priority = 2 - }; - args.Verbs.Add(verb); - } - - private void OnEntRemoved(EntityUid uid, WebComponent web, EntRemovedFromContainerMessage args) - { - if (!TryComp(uid, out var strap)) - return; - - if (HasComp(args.Entity)) - _buckleSystem.StrapSetEnabled(uid, false, strap); - } - - private void StartCocooning(EntityUid uid, ArachneComponent component, EntityUid target) + private void StartCocooning(EntityUid uid, CocoonerComponent component, EntityUid target) { _popupSystem.PopupEntity(Loc.GetString("cocoon-start-third-person", ("target", Identity.Entity(target, EntityManager)), ("spider", Identity.Entity(uid, EntityManager))), uid, Shared.Popups.PopupType.MediumCaution); - _popupSystem.PopupEntity(Loc.GetString("cocoon-start-second-person", ("target", Identity.Entity(target, EntityManager))), uid, uid, Shared.Popups.PopupType.Medium); - var delay = component.CocoonDelay; if (HasComp(target)) delay *= component.CocoonKnockdownMultiplier; - // Is it good practice to use empty data just to disambiguate doafters - // Who knows, there's no docs! - var ev = new ArachneCocoonDoAfterEvent(); - - var args = new DoAfterArgs(EntityManager, uid, delay, ev, uid, target: target) + var args = new DoAfterArgs(EntityManager, uid, delay, new CocoonDoAfterEvent(), uid, target: target) { BreakOnUserMove = true, BreakOnTargetMove = true, @@ -190,7 +107,7 @@ private void StartCocooning(EntityUid uid, ArachneComponent component, EntityUid _doAfter.TryStartDoAfter(args); } - private void OnCocoonDoAfter(EntityUid uid, ArachneComponent component, ArachneCocoonDoAfterEvent args) + private void OnCocoonDoAfter(EntityUid uid, CocoonerComponent component, CocoonDoAfterEvent args) { if (args.Handled || args.Cancelled || args.Args.Target == null) return; diff --git a/Content.Server/Vampire/BloodSuckerSystem.cs b/Content.Server/Vampire/BloodSuckerSystem.cs index 81d2854318..41afe0d666 100644 --- a/Content.Server/Vampire/BloodSuckerSystem.cs +++ b/Content.Server/Vampire/BloodSuckerSystem.cs @@ -6,6 +6,7 @@ using Content.Shared.Inventory; using Content.Shared.Administration.Logs; using Content.Shared.Vampiric; +using Content.Shared.Cocoon; using Content.Server.Atmos.Components; using Content.Server.Body.Components; using Content.Server.Body.Systems; @@ -45,23 +46,28 @@ public override void Initialize() private void AddSuccVerb(EntityUid uid, BloodSuckerComponent component, GetVerbsEvent args) { - if (args.User == args.Target) - return; - if (component.WebRequired) - return; // handled elsewhere - if (!TryComp(args.Target, out var bloodstream)) + + var victim = args.Target; + var ignoreClothes = false; + + if (TryComp(args.Target, out var cocoon)) + { + victim = cocoon.Victim ?? args.Target; + ignoreClothes = cocoon.Victim != null; + } else if (component.WebRequired) return; - if (!args.CanAccess) + + if (!TryComp(victim, out var bloodstream) || args.User == victim || !args.CanAccess) return; InnateVerb verb = new() { Act = () => { - StartSuccDoAfter(uid, args.Target, component, bloodstream); // start doafter + StartSuccDoAfter(uid, victim, component, bloodstream, !ignoreClothes); // start doafter }, Text = Loc.GetString("action-name-suck-blood"), - Icon = new SpriteSpecifier.Texture(new ("/Textures/Nyanotrasen/Icons/verbiconfangs.png")), + Icon = new SpriteSpecifier.Texture(new("/Textures/Nyanotrasen/Icons/verbiconfangs.png")), Priority = 2 }; args.Verbs.Add(verb); @@ -80,10 +86,8 @@ private void OnDamageChanged(EntityUid uid, BloodSuckedComponent component, Dama if (_prototypeManager.TryIndex("Brute", out var brute) && args.Damageable.Damage.TryGetDamageInGroup(brute, out var bruteTotal) && _prototypeManager.TryIndex("Airloss", out var airloss) && args.Damageable.Damage.TryGetDamageInGroup(airloss, out var airlossTotal)) - { if (bruteTotal == 0 && airlossTotal == 0) RemComp(uid); - } } private void OnDoAfter(EntityUid uid, BloodSuckerComponent component, BloodSuckDoAfterEvent args) @@ -96,18 +100,13 @@ private void OnDoAfter(EntityUid uid, BloodSuckerComponent component, BloodSuckD public void StartSuccDoAfter(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponent? bloodSuckerComponent = null, BloodstreamComponent? stream = null, bool doChecks = true) { - if (!Resolve(bloodsucker, ref bloodSuckerComponent)) - return; - - if (!Resolve(victim, ref stream)) + if (!Resolve(bloodsucker, ref bloodSuckerComponent) || !Resolve(victim, ref stream)) return; if (doChecks) { if (!_interactionSystem.InRangeUnobstructed(bloodsucker, victim)) - { return; - } if (_inventorySystem.TryGetSlotEntity(victim, "head", out var headUid) && HasComp(headUid)) { @@ -125,19 +124,15 @@ public void StartSuccDoAfter(EntityUid bloodsucker, EntityUid victim, BloodSucke } if (stream.BloodReagent != "Blood") - { - _popups.PopupEntity(Loc.GetString("bloodsucker-fail-not-blood", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); - return; - } - - if (_solutionSystem.PercentFull(stream.Owner) != 0) + _popups.PopupEntity(Loc.GetString("bloodsucker-not-blood", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); + else if (_solutionSystem.PercentFull(victim) != 0) _popups.PopupEntity(Loc.GetString("bloodsucker-fail-no-blood", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); + else + _popups.PopupEntity(Loc.GetString("bloodsucker-doafter-start", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); _popups.PopupEntity(Loc.GetString("bloodsucker-doafter-start-victim", ("sucker", bloodsucker)), victim, victim, Shared.Popups.PopupType.LargeCaution); - _popups.PopupEntity(Loc.GetString("bloodsucker-doafter-start", ("target", victim)), victim, bloodsucker, Shared.Popups.PopupType.Medium); - var ev = new BloodSuckDoAfterEvent(); - var args = new DoAfterArgs(EntityManager, bloodsucker, bloodSuckerComponent.Delay, ev, bloodsucker, target: victim) + var args = new DoAfterArgs(EntityManager, bloodsucker, bloodSuckerComponent.Delay, new BloodSuckDoAfterEvent(), bloodsucker, target: victim) { BreakOnTargetMove = true, BreakOnUserMove = false, @@ -206,8 +201,5 @@ public bool TrySucc(EntityUid bloodsucker, EntityUid victim, BloodSuckerComponen //} return true; } - - private record struct BloodSuckData() - {} } } diff --git a/Content.Shared/Arachne/ArachneComponent.cs b/Content.Shared/Arachne/ArachneComponent.cs deleted file mode 100644 index 04c369cc45..0000000000 --- a/Content.Shared/Arachne/ArachneComponent.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Arachne -{ - [RegisterComponent, NetworkedComponent] - public sealed partial class ArachneComponent : Component - { - [DataField("cocoonDelay")] - public float CocoonDelay = 12f; - - [DataField("cocoonKnockdownMultiplier")] - public float CocoonKnockdownMultiplier = 0.5f; - - /// - /// Blood reagent required to web up a mob. - /// - - [DataField("webBloodReagent")] - public string WebBloodReagent = "Blood"; - } -} diff --git a/Content.Shared/Arachne/Events.cs b/Content.Shared/Arachne/Events.cs deleted file mode 100644 index 02001286ac..0000000000 --- a/Content.Shared/Arachne/Events.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.Map; -using Robust.Shared.Serialization; -using Content.Shared.DoAfter; - -namespace Content.Shared.Arachne -{ - [Serializable, NetSerializable] - public sealed partial class ArachneCocoonDoAfterEvent : SimpleDoAfterEvent - { - } -} diff --git a/Content.Shared/Arachne/WebComponent.cs b/Content.Shared/Arachne/WebComponent.cs deleted file mode 100644 index c8284f3943..0000000000 --- a/Content.Shared/Arachne/WebComponent.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Arachne -{ - [RegisterComponent, NetworkedComponent] - public sealed partial class WebComponent : Component - {} -} diff --git a/Content.Shared/Arachne/CocoonComponent.cs b/Content.Shared/Cocoon/CocoonComponent.cs similarity index 61% rename from Content.Shared/Arachne/CocoonComponent.cs rename to Content.Shared/Cocoon/CocoonComponent.cs index e22e393ffd..66ba6e6dd3 100644 --- a/Content.Shared/Arachne/CocoonComponent.cs +++ b/Content.Shared/Cocoon/CocoonComponent.cs @@ -1,13 +1,14 @@ -namespace Content.Shared.Arachne +namespace Content.Shared.Cocoon { [RegisterComponent] public sealed partial class CocoonComponent : Component { - public bool WasReplacementAccent = false; + public string? OldAccent; - public string OldAccent = ""; + public EntityUid? Victim; [DataField("damagePassthrough")] public float DamagePassthrough = 0.5f; + } } diff --git a/Content.Shared/Cocoon/CocoonDoAfterEvent.cs b/Content.Shared/Cocoon/CocoonDoAfterEvent.cs new file mode 100644 index 0000000000..55986d2894 --- /dev/null +++ b/Content.Shared/Cocoon/CocoonDoAfterEvent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.Serialization; +using Content.Shared.DoAfter; + +namespace Content.Shared.Cocoon +{ + [Serializable, NetSerializable] + public sealed partial class CocoonDoAfterEvent : SimpleDoAfterEvent + { + } +} diff --git a/Content.Shared/Cocoon/CocoonerComponent.cs b/Content.Shared/Cocoon/CocoonerComponent.cs new file mode 100644 index 0000000000..17cce97309 --- /dev/null +++ b/Content.Shared/Cocoon/CocoonerComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Cocoon +{ + [RegisterComponent, NetworkedComponent] + public sealed partial class CocoonerComponent : Component + { + [DataField("cocoonDelay")] + public float CocoonDelay = 12f; + + [DataField("cocoonKnockdownMultiplier")] + public float CocoonKnockdownMultiplier = 0.5f; + } +} diff --git a/Resources/Locale/en-US/abilities/bloodsucker.ftl b/Resources/Locale/en-US/abilities/bloodsucker.ftl index d956eaff84..c8aa0ed854 100644 --- a/Resources/Locale/en-US/abilities/bloodsucker.ftl +++ b/Resources/Locale/en-US/abilities/bloodsucker.ftl @@ -4,9 +4,9 @@ action-description-suck-blood = Suck the blood of the victim in your hand. bloodsucker-fail-helmet = You'd need to remove {THE($helmet)}. bloodsucker-fail-mask = You'd need to remove your mask! -bloodsucker-fail-not-blood = { CAPITALIZE(SUBJECT($target)) } doesn't have delicious, nourishing mortal blood. -bloodsucker-fail-no-blood = { CAPITALIZE(SUBJECT($target)) } has no blood in { POSS-ADJ($target) } body. -bloodsucker-fail-no-blood-bloodsucked = { CAPITALIZE(SUBJECT($target)) } has been sucked dry. +bloodsucker-not-blood = {$target} doesn't have delicious, nourishing blood. +bloodsucker-fail-no-blood = {$target} has no blood in { POSS-ADJ($target) } body. +bloodsucker-fail-no-blood-bloodsucked = {$target} has been sucked dry. bloodsucker-blood-sucked = You suck some blood from {$target}. bloodsucker-doafter-start = You try to suck blood from {$target}. diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index d6c274751d..8dd348f47e 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -2357,6 +2357,9 @@ - type: RandomBark barkType: hissing barkMultiplier: 0.3 + - type: BloodSucker + webRequired: true + - type: Cocooner - type: entity name: tarantula diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/space.yml b/Resources/Prototypes/Entities/Mobs/NPCs/space.yml index 919441dff5..4ea766c314 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/space.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/space.yml @@ -277,6 +277,9 @@ Unsexed: UnisexArachnid - type: TypingIndicator proto: spider + - type: BloodSucker + webRequired: true + - type: Cocooner - type: entity id: MobSpiderSpaceSalvage diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachne.yml b/Resources/Prototypes/Entities/Mobs/Species/arachne.yml index e704f15725..24ebafd91d 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachne.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachne.yml @@ -111,7 +111,7 @@ bloodRegenerationThirst: 4 # 1 unit of demon's blood satiates 4 thirst - type: BloodSucker webRequired: true - - type: Arachne + - type: Cocooner - type: DamageVisuals targetLayers: - "enum.HumanoidVisualLayers.Chest" diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml index bfb7796f44..d576101e65 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml @@ -44,6 +44,9 @@ methods: [Touch] effects: - !type:WashCreamPieReaction + - type: BloodSucker + webRequired: true + - type: Cocooner # Damage (Self) - type: Bloodstream bloodReagent: CopperBlood diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/NPCs/mutants.yml b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/NPCs/mutants.yml index ee75dd3c8e..b3de9e0191 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/NPCs/mutants.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/NPCs/mutants.yml @@ -109,7 +109,7 @@ # path: /Audio/Animals/snake_hiss.ogg # - type: Puller # needsHands: false -# - type: Arachne +# - type: Cocooner # cocoonDelay: 8 # - type: SolutionContainerManager # solutions: From 603b86e1469d43dbda6cf664aaf6ae4735e85969 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Thu, 17 Oct 2024 19:21:34 +0000 Subject: [PATCH 24/76] Automatic Changelog Update (#1058) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 96ce3622f1..6101976f33 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7329,3 +7329,12 @@ Entries: id: 6458 time: '2024-10-16T22:54:33.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1061 +- author: Aidenkrz + changes: + - type: Tweak + message: >- + All spiders, arachne, and arachnids can cocoon mobs, and drink their + blood. + id: 6459 + time: '2024-10-17T19:21:01.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1058 From 0f73ebfee55ddf3f750e1cd648f7621c18202426 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Thu, 17 Oct 2024 16:39:25 -0400 Subject: [PATCH 25/76] Cherry-Pick Wizden PR #27960 (#1067) * fix orphaned storage grid pieces getting stuck to the cursor * instead of denying it, update it smartly # Description This cherry-picks https://github.com/space-wizards/space-station-14/pull/27960 Which should fix our storage UI bugs. Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> --- .../Storage/Controls/StorageContainer.cs | 33 ++++++++++++------- .../Systems/Storage/StorageUIController.cs | 3 +- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs b/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs index e39ac5d322..a9d7e09826 100644 --- a/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs +++ b/Content.Client/UserInterface/Systems/Storage/Controls/StorageContainer.cs @@ -254,7 +254,7 @@ public void BuildItemPieces() //todo. at some point, we may want to only rebuild the pieces that have actually received new data. - _pieceGrid.Children.Clear(); + _pieceGrid.RemoveAllChildren(); _pieceGrid.Rows = boundingGrid.Height + 1; _pieceGrid.Columns = boundingGrid.Width + 1; for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++) @@ -275,18 +275,29 @@ public void BuildItemPieces() if (_entity.TryGetComponent(itemEnt, out var itemEntComponent)) { - var gridPiece = new ItemGridPiece((itemEnt, itemEntComponent), itemPos, _entity) + ItemGridPiece gridPiece; + + if (_storageController.CurrentlyDragging?.Entity is { } dragging + && dragging == itemEnt) + { + _storageController.CurrentlyDragging.Orphan(); + gridPiece = _storageController.CurrentlyDragging; + } + else { - MinSize = size, - Marked = Array.IndexOf(containedEntities, itemEnt) switch + gridPiece = new ItemGridPiece((itemEnt, itemEntComponent), itemPos, _entity) { - 0 => ItemGridPieceMarks.First, - 1 => ItemGridPieceMarks.Second, - _ => null, - } - }; - gridPiece.OnPiecePressed += OnPiecePressed; - gridPiece.OnPieceUnpressed += OnPieceUnpressed; + MinSize = size, + Marked = Array.IndexOf(containedEntities, itemEnt) switch + { + 0 => ItemGridPieceMarks.First, + 1 => ItemGridPieceMarks.Second, + _ => null, + } + }; + gridPiece.OnPiecePressed += OnPiecePressed; + gridPiece.OnPieceUnpressed += OnPieceUnpressed; + } control.AddChild(gridPiece); } diff --git a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs index 346469d29d..97c9d8b795 100644 --- a/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs +++ b/Content.Client/UserInterface/Systems/Storage/StorageUIController.cs @@ -314,15 +314,16 @@ private void OnPieceUnpressed(GUIBoundKeyEventArgs args, ItemGridPiece control) _entity.GetNetEntity(storageEnt))); } + _menuDragHelper.EndDrag(); _container?.BuildItemPieces(); } else //if we just clicked, then take it out of the bag. { + _menuDragHelper.EndDrag(); _entity.RaisePredictiveEvent(new StorageInteractWithItemEvent( _entity.GetNetEntity(control.Entity), _entity.GetNetEntity(storageEnt))); } - _menuDragHelper.EndDrag(); args.Handle(); } From f92a126e61c06659bad6565681c0e0346e65bc06 Mon Sep 17 00:00:00 2001 From: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:21:46 -0700 Subject: [PATCH 26/76] Update CDN Scripts & Automatically Build Releases (#1097) --- .github/workflows/publish.yml | 42 +++++------------- Tools/publish_multi_request.py | 78 ++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 32 deletions(-) create mode 100644 Tools/publish_multi_request.py diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 177e6a0fe6..d9cfd3b25b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,39 +41,17 @@ jobs: - name: Package client run: dotnet run --project Content.Packaging client --no-wipe-release - - name: Update Build Info - run: Tools/gen_build_info.py - - - name: Shuffle files around - run: | - mkdir "release/${{ github.sha }}" - mv release/*.zip "release/${{ github.sha }}" - - - name: Upload files to centcomm - uses: appleboy/scp-action@master - with: - host: ${{ secrets.PUBLISH_HOST }} - username: ${{ secrets.PUBLISH_USER }} - key: ${{ secrets.PUBLISH_KEY }} - port: ${{ secrets.PUBLISH_PORT }} - source: "release/${{ github.sha }}" - target: "/var/www/builds.delta-v.org/delta-v/builds/" - strip_components: 1 - - - name: Update manifest JSON - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.PUBLISH_HOST }} - username: ${{ secrets.PUBLISH_USER }} - key: ${{ secrets.PUBLISH_KEY }} - port: ${{ secrets.PUBLISH_PORT }} - script: /home/deltav/publish/push.ps1 ${{ github.sha }} - - - name: Publish changelog (Discord) - run: Tools/actions_changelogs_since_last_run.py + - name: Publish version + run: Tools/publish_multi_request.py env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DISCORD_WEBHOOK_URL: ${{ secrets.CHANGELOG_DISCORD_WEBHOOK }} + PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }} + + # - name: Publish changelog (Discord) + # run: Tools/actions_changelogs_since_last_run.py + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # DISCORD_WEBHOOK_URL: ${{ secrets.CHANGELOG_DISCORD_WEBHOOK }} - name: Publish changelog (RSS) run: Tools/actions_changelog_rss.py diff --git a/Tools/publish_multi_request.py b/Tools/publish_multi_request.py new file mode 100644 index 0000000000..131d1f7f76 --- /dev/null +++ b/Tools/publish_multi_request.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +import requests +import os +import subprocess +from typing import Iterable + +PUBLISH_TOKEN = os.environ["PUBLISH_TOKEN"] +VERSION = os.environ["GITHUB_SHA"] + +RELEASE_DIR = "release" + +# +# CONFIGURATION PARAMETERS +# Forks should change these to publish to their own infrastructure. +# +ROBUST_CDN_URL = "https://cdn.simplestation.org/" +FORK_ID = "einstein-engines" + +def main(): + session = requests.Session() + session.headers = { + "Authorization": f"Bearer {PUBLISH_TOKEN}", + } + + print(f"Starting publish on Robust.Cdn for version {VERSION}") + + data = { + "version": VERSION, + "engineVersion": get_engine_version(), + } + headers = { + "Content-Type": "application/json" + } + resp = session.post(f"{ROBUST_CDN_URL}fork/{FORK_ID}/publish/start", json=data, headers=headers) + resp.raise_for_status() + print("Publish successfully started, adding files...") + + for file in get_files_to_publish(): + print(f"Publishing {file}") + with open(file, "rb") as f: + headers = { + "Content-Type": "application/octet-stream", + "Robust-Cdn-Publish-File": os.path.basename(file), + "Robust-Cdn-Publish-Version": VERSION + } + resp = session.post(f"{ROBUST_CDN_URL}fork/{FORK_ID}/publish/file", data=f, headers=headers) + + resp.raise_for_status() + + print("Successfully pushed files, finishing publish...") + + data = { + "version": VERSION + } + headers = { + "Content-Type": "application/json" + } + resp = session.post(f"{ROBUST_CDN_URL}fork/{FORK_ID}/publish/finish", json=data, headers=headers) + resp.raise_for_status() + + print("SUCCESS!") + + +def get_files_to_publish() -> Iterable[str]: + for file in os.listdir(RELEASE_DIR): + yield os.path.join(RELEASE_DIR, file) + + +def get_engine_version() -> str: + proc = subprocess.run(["git", "describe","--tags", "--abbrev=0"], stdout=subprocess.PIPE, cwd="RobustToolbox", check=True, encoding="UTF-8") + tag = proc.stdout.strip() + assert tag.startswith("v") + return tag[1:] # Cut off v prefix. + + +if __name__ == '__main__': + main() From 00e374c33b5427fad6e9c713473094b3228394d8 Mon Sep 17 00:00:00 2001 From: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:35:00 -0700 Subject: [PATCH 27/76] Fix the CDN Publish Script Being Unrunnable (#1098) --- Tools/publish_multi_request.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Tools/publish_multi_request.py diff --git a/Tools/publish_multi_request.py b/Tools/publish_multi_request.py old mode 100644 new mode 100755 From fce71cd261f5e3a2f5c802ef0e6d7c38772b41d1 Mon Sep 17 00:00:00 2001 From: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> Date: Sat, 19 Oct 2024 04:25:26 -0400 Subject: [PATCH 28/76] Adjust Air Alarm Pressure Thresholds (#996) # Description - Adjusts the "danger" status threshold in Air Alarms to 20 kPa, resulting in "warning" status at the expected 90 kPa. - Fixes the issue where air alarms had "danger" status at 20 kPa and "warning" status at 21 kPa --- # Changelog :cl: zelezniciar - fix: Fixes Air Alarms not entering danger/warning state when pressure is low --------- Co-authored-by: VMSolidus --- Resources/Prototypes/Atmospherics/thresholds.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Atmospherics/thresholds.yml b/Resources/Prototypes/Atmospherics/thresholds.yml index 22ca42869e..81f7bda4d2 100644 --- a/Resources/Prototypes/Atmospherics/thresholds.yml +++ b/Resources/Prototypes/Atmospherics/thresholds.yml @@ -19,7 +19,7 @@ upperWarnAround: !type:AlarmThresholdSetting threshold: 0.7 # 385 kPa, WarningHighPressure from Atmospherics.cs lowerWarnAround: !type:AlarmThresholdSetting - threshold: 1.05 # ~90 kPa + threshold: 4.5 # ~90 kPa # a reminder that all of these are percentages (where 1 is 100%), # so 0.01 is 1%, From a3e467b002307c2c5f282ec01479c1cb3143511d Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sat, 19 Oct 2024 08:25:52 +0000 Subject: [PATCH 29/76] Automatic Changelog Update (#996) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 6101976f33..4d5675c213 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7338,3 +7338,10 @@ Entries: id: 6459 time: '2024-10-17T19:21:01.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1058 +- author: zelezniciar + changes: + - type: Fix + message: Fixes Air Alarms not entering danger/warning state when pressure is low + id: 6460 + time: '2024-10-19T08:25:27.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/996 From 0aae2206c9aab8513bdfa3dd975682b6cdba1a4a Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:26:03 -0400 Subject: [PATCH 30/76] Personal Ai Languages (#1066) # Description This PR adds Languages to Personal Ai devices, allowing them to act as an interpreter for their owner. PAIs carry knowledge of all "Common" languages(Not including anything species specific, or Rare), plus the ability to understand(but not speak) Sign language. # Changelog :cl: - add: Personal Ai devices now come loaded with knowledge of all "Common" languages, including Sign language, allowing them to interpret for their owner. --- .../Prototypes/Entities/Objects/Fun/pai.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Resources/Prototypes/Entities/Objects/Fun/pai.yml b/Resources/Prototypes/Entities/Objects/Fun/pai.yml index 1b9c5303c6..b73767fd81 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/pai.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/pai.yml @@ -72,6 +72,22 @@ Searching: { state: pai-searching-overlay } On: { state: pai-on-overlay } - type: StationMap + - type: LanguageKnowledge + speaks: + - TauCetiBasic + - SolCommon + - Tradeband + - Freespeak + - Elyran + - RobotTalk + understands: + - TauCetiBasic + - SolCommon + - Tradeband + - Freespeak + - Elyran + - RobotTalk + - Sign # It's intentional that they don't "Speak" sign language. - type: entity parent: PersonalAI From 7f60931aa06a7bf490bcfb8865e12bbcd3b02265 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:26:31 -0400 Subject: [PATCH 31/76] Fix Melee Contests (#1069) # Description Default values for melee weapon contests were causing balance issues with Mobs that interacted with it, such as Carps being able to defeat the damage threshold of reinforced walls when the intention is that they shouldn't be able to. Also the logic on ContestDisadvantages was inverted, so this fixes that. # Changelog :cl: - fix: Fixed issues with Contests System that allowed mobs such as Space Carps to smash through barriers that were not intended to be breakable by said mobs. --- Content.Shared/Contests/ContestsSystem.Utilities.cs | 12 ++++++------ Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs | 3 --- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Content.Shared/Contests/ContestsSystem.Utilities.cs b/Content.Shared/Contests/ContestsSystem.Utilities.cs index 0278d75e94..2bcb6dcc32 100644 --- a/Content.Shared/Contests/ContestsSystem.Utilities.cs +++ b/Content.Shared/Contests/ContestsSystem.Utilities.cs @@ -30,27 +30,27 @@ public float ContestConstructor(EntityUid user, ContestArgs args) return 1; if (!args.DoEveryInteraction) - return args.DoMassInteraction ? ((!args.MassDisadvantage + return args.DoMassInteraction ? ((args.MassDisadvantage ? MassContest(user, args.MassBypassClamp, args.MassRangeModifier) : 1 / MassContest(user, args.MassBypassClamp, args.MassRangeModifier)) + args.MassOffset) : 1 - * (args.DoStaminaInteraction ? ((!args.StaminaDisadvantage + * (args.DoStaminaInteraction ? ((args.StaminaDisadvantage ? StaminaContest(user, args.StaminaBypassClamp, args.StaminaRangeModifier) : 1 / StaminaContest(user, args.StaminaBypassClamp, args.StaminaRangeModifier)) + args.StaminaOffset) : 1) - * (args.DoHealthInteraction ? ((!args.HealthDisadvantage + * (args.DoHealthInteraction ? ((args.HealthDisadvantage ? HealthContest(user, args.HealthBypassClamp, args.HealthRangeModifier) : 1 / HealthContest(user, args.HealthBypassClamp, args.HealthRangeModifier)) + args.HealthOffset) : 1) - * (args.DoMindInteraction ? ((!args.MindDisadvantage + * (args.DoMindInteraction ? ((args.MindDisadvantage ? MindContest(user, args.MindBypassClamp, args.MindRangeModifier) : 1 / MindContest(user, args.MindBypassClamp, args.MindRangeModifier)) + args.MindOffset) : 1) - * (args.DoMoodInteraction ? ((!args.MoodDisadvantage + * (args.DoMoodInteraction ? ((args.MoodDisadvantage ? MoodContest(user, args.MoodBypassClamp, args.MoodRangeModifier) : 1 / MoodContest(user, args.MoodBypassClamp, args.MoodRangeModifier)) + args.MoodOffset) @@ -280,4 +280,4 @@ public sealed partial class ContestArgs ///
[DataField] public bool EveryInteractionSumOrMultiply; -} \ No newline at end of file +} diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs index 694273e5bf..5fc92ce37a 100644 --- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs +++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs @@ -166,10 +166,7 @@ public sealed partial class MeleeWeaponComponent : Component { DoStaminaInteraction = true, StaminaDisadvantage = true, - StaminaRangeModifier = 2, - StaminaOffset = 0.25f, DoHealthInteraction = true, - HealthRangeModifier = 1.5f, }; /// From fc27a8b50f7c26a8b1e9e95e4a37b068da91a159 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sat, 19 Oct 2024 08:26:57 +0000 Subject: [PATCH 32/76] Automatic Changelog Update (#1069) --- Resources/Changelog/Changelog.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 4d5675c213..d7e6007674 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7345,3 +7345,13 @@ Entries: id: 6460 time: '2024-10-19T08:25:27.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/996 +- author: VMSolidus + changes: + - type: Fix + message: >- + Fixed issues with Contests System that allowed mobs such as Space Carps + to smash through barriers that were not intended to be breakable by said + mobs. + id: 6461 + time: '2024-10-19T08:26:32.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1069 From 4c79a6f3abc9f4603dd3f2a5b493574985572519 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:27:32 -0400 Subject: [PATCH 33/76] Cherry-Pick Wizden PR #28447 (#1077) make it so you cant build disposal pipes in impassable objects (like walls) # Description This Fixes #1072 By cherry-picking https://github.com/space-wizards/space-station-14/pull/28447 Co-authored-by: NazrinNya <137837419+NazrinNya@users.noreply.github.com> --- Resources/Prototypes/Recipes/Construction/utilities.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Recipes/Construction/utilities.yml b/Resources/Prototypes/Recipes/Construction/utilities.yml index 1647e98bce..19f2fee183 100644 --- a/Resources/Prototypes/Recipes/Construction/utilities.yml +++ b/Resources/Prototypes/Recipes/Construction/utilities.yml @@ -178,7 +178,7 @@ targetNode: pipe category: construction-category-utilities placementMode: SnapgridCenter - canBuildInImpassable: true + canBuildInImpassable: false icon: sprite: Structures/Piping/disposal.rsi state: conpipe-s From 528ef89420dbf3eb240d35f8686ff98ff7d26307 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:28:28 -0400 Subject: [PATCH 34/76] Fix Cryptobiolin (#1081) # Description Closes #1080 This PR fixes Cryptobiolin so that it correctly adds a temporary "PsionicsDisabled" and "PsionicallyInsulated" status effect to the consumer. This effect lasts for 15 minutes, and is intended to be a highly effective means of providing protection from psionics, especially in the case of a Code White call. # Changelog :cl: - fix: Fixed Cryptobiolin not providing temporary Psionic Insulation. It now provides protection from psionic abilities and events for 15 minutes. --- .../Prototypes/Entities/Mobs/Species/base.yml | 6 ++++-- Resources/Prototypes/Reagents/medicine.yml | 14 +++++++++----- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index c0d23c489c..c66b00fab4 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -141,8 +141,8 @@ - TemporaryBlindness - Pacified - StaminaModifier - - PsionicsDisabled #Nyano - Summary: PCs can have psionics disabled. - - PsionicallyInsulated #Nyano - Summary: PCs can be made insulated from psionic powers. + - PsionicsDisabled + - PsionicallyInsulated - type: Reflect enabled: false reflectProb: 0 @@ -273,6 +273,8 @@ - Muted - Pacified - StaminaModifier + - PsionicsDisabled + - PsionicallyInsulated - type: Blindable # Other - type: Temperature diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index 12c694e09e..6b441711e1 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -11,16 +11,20 @@ metabolismRate: 0.1 effects: - !type:GenericStatusEffect - key: PsionicallyInsulated #Nyano - Summary: makes the user psionically insulated from effects. - component: PsionicInsulation #Nyano - Summary: see above. + key: PsionicallyInsulated + component: PsionicInsulation type: Add + time: 900 - !type:GenericStatusEffect key: Stutter component: ScrambledAccent + type: Add + time: 900 - !type:GenericStatusEffect - key: PsionicsDisabled #Nyano - Summary: disables psinoics from being used by the wearer. - component: PsionicsDisabled #Nyano - Summary: see above. + key: PsionicsDisabled + component: PsionicsDisabled type: Add + time: 900 - !type:Drunk slurSpeech: false boozePower: 20 @@ -1120,7 +1124,7 @@ conditions: - !type:ReagentThreshold min: 12 - + - type: reagent id: Opporozidone #Name based of an altered version of the startreck chem "Opporozine" name: reagent-name-opporozidone From 4561092e3054d8f793672a42cec276f1ee2c73de Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:29:15 -0400 Subject: [PATCH 35/76] Fix Nicotine Addiction (#1082) # Description It takes 10 minutes to smoke a cigarette, and by the time the cigarette is done smoking, the nicotine addiction benefit wears off, meaning that the instant you stopped smoking, you would immediately suffer the need to smoke again. A better fix would be a more comprehensive refactor of addictions into its own special system, but we're currently in a state of "Rapid fire fixes" so this will do for now by bumping the benefit time up to 17 minutes. I've also done the same for Loto Enthrallment. # Changelog :cl: - tweak: The mood benefit from Nicotine and Lotophagoi Oil has had its duration increased from 10 minutes to 17 minutes. You should no longer IMMEDIATELY feel a need to smoke as soon as the first cigarette is done. --- Resources/Prototypes/Mood/drugs.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/Mood/drugs.yml b/Resources/Prototypes/Mood/drugs.yml index a9818d2021..379dd96006 100644 --- a/Resources/Prototypes/Mood/drugs.yml +++ b/Resources/Prototypes/Mood/drugs.yml @@ -4,7 +4,7 @@ - type: moodEffect id: LotoTranscendence moodChange: 30 - timeout: 600 #10 minutes + timeout: 1020 #17 minutes moodletOnEnd: LotoEnthrallment category: "LotophagoiAddiction" @@ -21,7 +21,7 @@ - type: moodEffect id: NicotineBenefit moodChange: 5 - timeout: 600 #10 minutes + timeout: 1020 #17 minutes moodletOnEnd: NicotineWithdrawal category: "NicotineAddiction" @@ -42,4 +42,4 @@ - type: moodEffect id: SpaceDrugsBenefit moodChange: 7 - timeout: 300 #5 minutes \ No newline at end of file + timeout: 300 #5 minutes From a390ed7a883607e5e7193f9d21bf4aba24ed5bab Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:29:55 -0400 Subject: [PATCH 36/76] Fix Too Much Food And Drink (#1083) # Description Closes #1075 It was basically impossible to eat or drink without triggering the negative moodlet, and this feels entirely the wrong direction for players to be penalized for these actions. So I've simply inverted the penalty into a bonus. # Changelog :cl: - tweak: Completely satiating all hunger or thirst now provides a mood bonus instead of a penalty. --- Resources/Locale/en-US/mood/mood.ftl | 4 ++-- Resources/Prototypes/Mood/genericNeeds.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Resources/Locale/en-US/mood/mood.ftl b/Resources/Locale/en-US/mood/mood.ftl index 5c31af0a4d..fbd41fed12 100644 --- a/Resources/Locale/en-US/mood/mood.ftl +++ b/Resources/Locale/en-US/mood/mood.ftl @@ -5,7 +5,7 @@ mood-effect-HungerOkay = I am feeling full. mood-effect-HungerPeckish = I could go for a snack right about now. mood-effect-HungerStarving = I NEED FOOD! -mood-effect-ThirstOverHydrated = I feel dizzy after drinking too much. +mood-effect-ThirstOverHydrated = I've had my fill of water. mood-effect-ThirstOkay = I'm feeling refreshed. mood-effect-ThirstThirsty = My lips are a little dry. mood-effect-ThirstParched = I NEED WATER! @@ -72,4 +72,4 @@ mood-effect-NicotineWithdrawal = mood-effect-EthanolBenefit = I feel so relaxed from drinking. mood-effect-SpaceDrugsBenefit = - Woaaaah, such pretty colors maaaaan. It's like I can hear color and taste sound maaan. \ No newline at end of file + Woaaaah, such pretty colors maaaaan. It's like I can hear color and taste sound maaan. diff --git a/Resources/Prototypes/Mood/genericNeeds.yml b/Resources/Prototypes/Mood/genericNeeds.yml index d0b24b7d7f..e0b8c25c08 100644 --- a/Resources/Prototypes/Mood/genericNeeds.yml +++ b/Resources/Prototypes/Mood/genericNeeds.yml @@ -1,7 +1,7 @@ # Hunger - type: moodEffect id: HungerOverfed - moodChange: -10 + moodChange: 10 category: "Hunger" - type: moodEffect @@ -22,7 +22,7 @@ # Thirst - type: moodEffect id: ThirstOverHydrated - moodChange: -3 + moodChange: 10 category: "Thirst" - type: moodEffect From d74f7199c038ef56e91aae5a443adc58ad4f4407 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:30:22 -0400 Subject: [PATCH 37/76] Arena Arrivals Dock (#1086) # Description For #1070 This PR adds a proper arrivals dock to the Arena map, with actual Arrivals airlocks. This allows the map to function properly with servers having the Arrivals shuttle enabled. # TODO

Media

![image](https://github.com/user-attachments/assets/58976cdf-9869-487c-8b38-de0a3eca2307)

# Changelog :cl: - add: Arena now has a properly functional Arrivals Dock. --- Resources/Maps/arena.yml | 3475 +++++++++++++++----------------------- 1 file changed, 1364 insertions(+), 2111 deletions(-) diff --git a/Resources/Maps/arena.yml b/Resources/Maps/arena.yml index ab548d14f2..362de16141 100644 --- a/Resources/Maps/arena.yml +++ b/Resources/Maps/arena.yml @@ -73,14 +73,15 @@ entities: - type: MetaData - type: Transform - type: Map + mapPaused: True - type: PhysicsMap + - type: GridTree + - type: MovedGrids - type: Broadphase - type: OccluderTree - type: Parallax parallax: ArenaStation - type: LoadedMap - - type: GridTree - - type: MovedGrids - uid: 6747 components: - type: MetaData @@ -263,11 +264,11 @@ entities: version: 6 4,-2: ind: 4,-2 - tiles: fgAAAAAAAAAAAAAAfwAAAAAAdgAAAAAALwAAAAAAdgAAAAABfwAAAAAAAAAAAAAAfwAAAAAAMAAAAAACXgAAAAADMAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAALwAAAAAAUAAAAAAALwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAXgAAAAACXgAAAAAAXgAAAAADUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXgAAAAAAXgAAAAAAMAAAAAABXgAAAAACXgAAAAACXgAAAAACMAAAAAAAXgAAAAADXgAAAAAAXgAAAAABMAAAAAADXgAAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAABXgAAAAAAXgAAAAAAXgAAAAACMAAAAAACXgAAAAACXgAAAAADXgAAAAADMAAAAAABXgAAAAACXgAAAAADXgAAAAACUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXgAAAAAAXgAAAAAAMAAAAAACXgAAAAABXgAAAAABXgAAAAACMAAAAAADXgAAAAACXgAAAAAAXgAAAAACMAAAAAADXgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAXgAAAAACXgAAAAAAXgAAAAABfwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAADXgAAAAACXgAAAAAAXgAAAAADXgAAAAACXgAAAAAAXgAAAAACXgAAAAACXgAAAAADfwAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAACXgAAAAADXgAAAAABXgAAAAADXgAAAAABXgAAAAABXgAAAAADXgAAAAABXgAAAAADfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAACXgAAAAAAXgAAAAAAXgAAAAACXgAAAAABXgAAAAABXgAAAAABXgAAAAAAXgAAAAABfwAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAADXgAAAAACXgAAAAADXgAAAAABXgAAAAAAXgAAAAACXgAAAAAAXgAAAAABXgAAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAABXgAAAAADXgAAAAACXgAAAAADXgAAAAADXgAAAAABXgAAAAADXgAAAAACXgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAAAXgAAAAACXgAAAAAAXgAAAAABXgAAAAAAXgAAAAACXgAAAAADXgAAAAADXgAAAAADfwAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAABXgAAAAABXgAAAAABXgAAAAAAXgAAAAACXgAAAAAAXgAAAAADXgAAAAAAXgAAAAACfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAACXgAAAAADXgAAAAAAXgAAAAACXgAAAAACXgAAAAACXgAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: fgAAAAAAAAAAAAAAfwAAAAAAdgAAAAAALwAAAAAAdgAAAAABfwAAAAAAAAAAAAAAfwAAAAAAMAAAAAACXgAAAAADMAAAAAAAUAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfwAAAAAAfwAAAAAAfwAAAAAALwAAAAAAUAAAAAAALwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAXgAAAAACXgAAAAAAXgAAAAADUAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAXgAAAAAAXgAAAAAAMAAAAAABXgAAAAACXgAAAAACXgAAAAACMAAAAAAAXgAAAAADXgAAAAAAXgAAAAABMAAAAAADXgAAAAABUAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAMAAAAAABXgAAAAAAXgAAAAAAXgAAAAACMAAAAAACXgAAAAACXgAAAAADXgAAAAADMAAAAAABXgAAAAACXgAAAAADXgAAAAACUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXgAAAAAAXgAAAAAAMAAAAAACXgAAAAABXgAAAAABXgAAAAACMAAAAAADXgAAAAACXgAAAAAAXgAAAAACMAAAAAADXgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAXgAAAAACXgAAAAAAXgAAAAABfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAADXgAAAAACXgAAAAAAXgAAAAADXgAAAAACXgAAAAAAXgAAAAACXgAAAAACXgAAAAADfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAACXgAAAAADXgAAAAABXgAAAAADXgAAAAABXgAAAAABXgAAAAADXgAAAAABXgAAAAADfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAACXgAAAAAAXgAAAAAAXgAAAAACXgAAAAABXgAAAAABXgAAAAABXgAAAAAAXgAAAAABfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAADXgAAAAACXgAAAAADXgAAAAABXgAAAAAAXgAAAAACXgAAAAAAXgAAAAABXgAAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAABXgAAAAADXgAAAAACXgAAAAADXgAAAAADXgAAAAABXgAAAAADXgAAAAACXgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAAAXgAAAAACXgAAAAAAXgAAAAABXgAAAAAAXgAAAAACXgAAAAADXgAAAAADXgAAAAADfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAABXgAAAAABXgAAAAABXgAAAAAAXgAAAAACXgAAAAAAXgAAAAADXgAAAAAAXgAAAAACfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAACXgAAAAADXgAAAAAAXgAAAAACXgAAAAACXgAAAAACXgAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 4,-3: ind: 4,-3 - tiles: XgAAAAABMAAAAAABXgAAAAACXgAAAAADXgAAAAAAMAAAAAACXgAAAAACXgAAAAABXgAAAAADMAAAAAADXgAAAAAAMAAAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXgAAAAACXgAAAAADXgAAAAACMAAAAAAAXgAAAAACXgAAAAAAXgAAAAADMAAAAAADXgAAAAADXgAAAAAAXgAAAAAAXgAAAAACUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXgAAAAABMAAAAAADXgAAAAADXgAAAAABXgAAAAABMAAAAAAAXgAAAAADXgAAAAAAXgAAAAADXgAAAAADMAAAAAADXgAAAAABfwAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAXgAAAAACXgAAAAADXgAAAAADfwAAAAAAXgAAAAAAXgAAAAACXgAAAAADXgAAAAABXgAAAAABXgAAAAACXgAAAAACfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAXgAAAAABXgAAAAACXgAAAAAAUAAAAAAAXgAAAAADXgAAAAADXgAAAAACXgAAAAACMAAAAAADXgAAAAADMAAAAAACfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAXgAAAAAAXgAAAAACXgAAAAABUAAAAAAAXgAAAAABXgAAAAABXgAAAAABXgAAAAACXgAAAAABXgAAAAADXgAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAUAAAAAAAXgAAAAACXgAAAAADXgAAAAACUAAAAAAAXgAAAAABXgAAAAABXgAAAAABXgAAAAAAXgAAAAAAMAAAAAAAXgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAUAAAAAAAXgAAAAAAXgAAAAAAXgAAAAABUAAAAAAAXgAAAAABXgAAAAAAXgAAAAACXgAAAAACXgAAAAADXgAAAAAAXgAAAAADUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAAAXgAAAAACXgAAAAAAUAAAAAAAXgAAAAABXgAAAAACXgAAAAACXgAAAAADMAAAAAADXgAAAAACMAAAAAACUAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAXgAAAAADXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAADMAAAAAAAXgAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAfgAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfgAAAAAAUAAAAAAAXgAAAAABXgAAAAAAXgAAAAABfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAAAAAAAAAUAAAAAAALwAAAAAAdgAAAAADLwAAAAAAUAAAAAAAAAAAAAAAUAAAAAAAMAAAAAADXgAAAAADMAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAAAAAAAAAUAAAAAAALwAAAAAAdgAAAAABLwAAAAAAUAAAAAAAAAAAAAAAUAAAAAAAXgAAAAABXgAAAAADXgAAAAACfwAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAUAAAAAAAAAAAAAAAUAAAAAAALwAAAAAAdgAAAAABLwAAAAAAUAAAAAAAAAAAAAAAfwAAAAAAXgAAAAACMAAAAAAAXgAAAAADfwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfgAAAAAAUAAAAAAAdgAAAAAAdgAAAAABdgAAAAAAUAAAAAAAfgAAAAAAfwAAAAAAXgAAAAADXgAAAAADXgAAAAACUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: XgAAAAABMAAAAAABXgAAAAACXgAAAAADXgAAAAAAMAAAAAACXgAAAAACXgAAAAABXgAAAAADMAAAAAADXgAAAAAAMAAAAAABUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXgAAAAACXgAAAAADXgAAAAACMAAAAAAAXgAAAAACXgAAAAAAXgAAAAADMAAAAAADXgAAAAADXgAAAAAAXgAAAAAAXgAAAAACUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAXgAAAAABMAAAAAADXgAAAAADXgAAAAABXgAAAAABMAAAAAAAXgAAAAADXgAAAAAAXgAAAAADXgAAAAADMAAAAAADXgAAAAABfwAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAXgAAAAACXgAAAAADXgAAAAADfwAAAAAAXgAAAAAAXgAAAAACXgAAAAADXgAAAAABXgAAAAABXgAAAAACXgAAAAACfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAXgAAAAABXgAAAAACXgAAAAAAUAAAAAAAXgAAAAADXgAAAAADXgAAAAACXgAAAAACMAAAAAADXgAAAAADMAAAAAACfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAXgAAAAAAXgAAAAACXgAAAAABUAAAAAAAXgAAAAABXgAAAAABXgAAAAABXgAAAAACXgAAAAABXgAAAAADXgAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAUAAAAAAAXgAAAAACXgAAAAADXgAAAAACUAAAAAAAXgAAAAABXgAAAAABXgAAAAABXgAAAAAAXgAAAAAAMAAAAAAAXgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAUAAAAAAAXgAAAAAAXgAAAAAAXgAAAAABUAAAAAAAXgAAAAABXgAAAAAAXgAAAAACXgAAAAACXgAAAAADXgAAAAAAXgAAAAADUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAXgAAAAAAXgAAAAACXgAAAAAAUAAAAAAAXgAAAAABXgAAAAACXgAAAAACXgAAAAADMAAAAAADXgAAAAACMAAAAAACUAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAXgAAAAADXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAADMAAAAAAAXgAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAfgAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfgAAAAAAUAAAAAAAXgAAAAABXgAAAAAAXgAAAAABXgAAAAAAXgAAAAAAXgAAAAAAXgAAAAAAUAAAAAAAAAAAAAAAUAAAAAAALwAAAAAAdgAAAAADLwAAAAAAUAAAAAAAAAAAAAAAUAAAAAAAMAAAAAADXgAAAAADMAAAAAAAXgAAAAAAXgAAAAAAXgAAAAAAMAAAAAAAUAAAAAAAAAAAAAAAUAAAAAAALwAAAAAAdgAAAAABLwAAAAAAUAAAAAAAAAAAAAAAUAAAAAAAXgAAAAABXgAAAAADXgAAAAACfwAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAUAAAAAAAAAAAAAAAUAAAAAAALwAAAAAAdgAAAAABLwAAAAAAUAAAAAAAAAAAAAAAfwAAAAAAXgAAAAACMAAAAAAAXgAAAAADfwAAAAAAfgAAAAAAAAAAAAAAfgAAAAAAfwAAAAAAfgAAAAAAUAAAAAAAdgAAAAAAdgAAAAABdgAAAAAAUAAAAAAAfgAAAAAAfwAAAAAAXgAAAAADXgAAAAADXgAAAAACUAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAA version: 6 2,-4: ind: 2,-4 @@ -315,7 +316,7 @@ entities: version: 6 5,-3: ind: 5,-3 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAXgAAAAAAMAAAAAAAXgAAAAAAMAAAAAAAXgAAAAAAXgAAAAAAXgAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAXgAAAAAAMAAAAAAAXgAAAAAAXgAAAAAAXgAAAAAAXgAAAAAAXgAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAMAAAAAAAMAAAAAAAfwAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAA version: 6 0,-6: ind: 0,-6 @@ -339,7 +340,7 @@ entities: version: 6 4,-1: ind: 4,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfwAAAAAAUAAAAAAAVQAAAAAAVQAAAAAAUAAAAAAAfwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfwAAAAAAWgAAAAAAVQAAAAAAVQAAAAAAWgAAAAAAfwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAVQAAAAAAVQAAAAAAVQAAAAAAVQAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAWgAAAAAAWgAAAAAAWgAAAAAAWgAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAfwAAAAAAfwAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfwAAAAAAUAAAAAAAVQAAAAAAVQAAAAAAUAAAAAAAfwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfwAAAAAAWgAAAAAAVQAAAAAAVQAAAAAAWgAAAAAAfwAAAAAAfgAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAVQAAAAAAVQAAAAAAVQAAAAAAVQAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAWgAAAAAAVQAAAAAAWgAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAWgAAAAAAWgAAAAAAWgAAAAAAWgAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAfwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 -4,-2: ind: -4,-2 @@ -491,11 +492,11 @@ entities: version: 6 5,-1: ind: 5,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAA version: 6 5,-2: ind: 5,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAA + tiles: fgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfwAAAAAAMAAAAAAAMAAAAAAAfwAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAMAAAAAAAMAAAAAAAfwAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAMAAAAAAAMAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAMAAAAAAAMAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAXgAAAAAAXgAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAA version: 6 6,-1: ind: 6,-1 @@ -519,7 +520,11 @@ entities: version: 6 6,-2: ind: 6,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + version: 6 + 6,-3: + ind: 6,-3 + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 - type: Broadphase - type: Physics @@ -11312,8 +11317,6 @@ entities: - 13077 - 8433 - 7909 - - type: AtmosDevice - joinedGrid: 6747 - uid: 490 components: - type: Transform @@ -11334,8 +11337,6 @@ entities: - 14634 - 14896 - 23520 - - type: AtmosDevice - joinedGrid: 6747 - uid: 1755 components: - type: Transform @@ -11359,8 +11360,6 @@ entities: - 21751 - 13451 - 13162 - - type: AtmosDevice - joinedGrid: 6747 - uid: 3694 components: - type: Transform @@ -11378,8 +11377,6 @@ entities: - 13867 - 13866 - 13951 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7456 components: - type: Transform @@ -11403,8 +11400,6 @@ entities: - 27297 - 27396 - 27395 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8860 components: - type: Transform @@ -11444,8 +11439,6 @@ entities: - 22284 - 13376 - 13246 - - type: AtmosDevice - joinedGrid: 6747 - uid: 9063 components: - type: Transform @@ -11474,8 +11467,6 @@ entities: - 26056 - 22745 - 25606 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13485 components: - type: Transform @@ -11509,8 +11500,6 @@ entities: - 15810 - 23752 - 23751 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13930 components: - type: Transform @@ -11547,8 +11536,6 @@ entities: - 24134 - 24138 - 24178 - - type: AtmosDevice - joinedGrid: 6747 - uid: 21858 components: - type: Transform @@ -11564,8 +11551,6 @@ entities: - 15006 - 15004 - 14712 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22036 components: - type: Transform @@ -11586,8 +11571,6 @@ entities: - 14231 - 14279 - 14264 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22037 components: - type: Transform @@ -11619,8 +11602,6 @@ entities: - 13588 - 13667 - 13586 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22038 components: - type: Transform @@ -11649,8 +11630,6 @@ entities: - 13658 - 13659 - 13561 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22041 components: - type: Transform @@ -11669,8 +11648,6 @@ entities: - 13496 - 13500 - 13499 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22043 components: - type: Transform @@ -11703,8 +11680,6 @@ entities: - 22323 - 22324 - 21389 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22087 components: - type: Transform @@ -11725,8 +11700,6 @@ entities: - 17141 - 25308 - 25307 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22089 components: - type: Transform @@ -11749,8 +11722,6 @@ entities: - 21797 - 21795 - 21796 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22091 components: - type: Transform @@ -11780,8 +11751,6 @@ entities: - 25302 - 25303 - 25304 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22093 components: - type: Transform @@ -11818,8 +11787,6 @@ entities: - 14526 - 14733 - 14515 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22096 components: - type: Transform @@ -11846,8 +11813,6 @@ entities: - 15412 - 15339 - 15411 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22097 components: - type: Transform @@ -11867,8 +11832,6 @@ entities: - 16656 - 16657 - 27085 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22099 components: - type: Transform @@ -11888,8 +11851,6 @@ entities: - 15170 - 15172 - 15168 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22101 components: - type: Transform @@ -11906,8 +11867,6 @@ entities: - 14631 - 14630 - 14829 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22103 components: - type: Transform @@ -11925,8 +11884,6 @@ entities: - 14841 - 14842 - 14629 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22105 components: - type: Transform @@ -11945,15 +11902,11 @@ entities: - 15131 - 15133 - 15132 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22111 components: - type: Transform pos: 24.5,-62.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22113 components: - type: Transform @@ -11970,8 +11923,6 @@ entities: - 14364 - 14365 - 14727 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22115 components: - type: Transform @@ -11985,8 +11936,6 @@ entities: - 21782 - 14394 - 14717 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22117 components: - type: Transform @@ -12004,8 +11953,6 @@ entities: - 13953 - 13874 - 13954 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22119 components: - type: Transform @@ -12020,8 +11967,6 @@ entities: - 21907 - 13875 - 13960 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22121 components: - type: Transform @@ -12062,8 +12007,6 @@ entities: - 14293 - 13595 - 13673 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22123 components: - type: Transform @@ -12084,8 +12027,6 @@ entities: - 21908 - 13959 - 13873 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22125 components: - type: Transform @@ -12110,8 +12051,6 @@ entities: - 25258 - 22295 - 1148 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22130 components: - type: Transform @@ -12135,8 +12074,6 @@ entities: - 14324 - 14396 - 14439 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22132 components: - type: Transform @@ -12152,8 +12089,6 @@ entities: - 15005 - 14680 - 15007 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22158 components: - type: Transform @@ -12165,8 +12100,6 @@ entities: - 10125 - 22157 - 21810 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22163 components: - type: Transform @@ -12186,8 +12119,6 @@ entities: - 16537 - 15574 - 16536 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22223 components: - type: Transform @@ -12219,8 +12150,6 @@ entities: - 17074 - 17367 - 17362 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22225 components: - type: Transform @@ -12236,8 +12165,6 @@ entities: - 14647 - 15011 - 23041 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22229 components: - type: Transform @@ -12258,8 +12185,6 @@ entities: - 14525 - 14524 - 14735 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22234 components: - type: Transform @@ -12277,8 +12202,6 @@ entities: - 438 - 25595 - 25608 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22239 components: - type: Transform @@ -12296,8 +12219,6 @@ entities: - 15431 - 15359 - 21428 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22288 components: - type: Transform @@ -12319,8 +12240,6 @@ entities: - 14234 - 14032 - 14232 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22293 components: - type: Transform @@ -12335,8 +12254,6 @@ entities: - 21911 - 13958 - 13872 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22317 components: - type: Transform @@ -12359,8 +12276,6 @@ entities: - 14088 - 14131 - 14130 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22330 components: - type: Transform @@ -12373,8 +12288,6 @@ entities: - 21931 - 15716 - 15989 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22346 components: - type: Transform @@ -12385,8 +12298,6 @@ entities: - 9416 - 9417 - 9418 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25271 components: - type: Transform @@ -12410,8 +12321,6 @@ entities: - 23685 - 24135 - 24119 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25275 components: - type: Transform @@ -12425,8 +12334,6 @@ entities: - 25207 - 24185 - 24179 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25278 components: - type: Transform @@ -12441,8 +12348,6 @@ entities: - 24196 - 24199 - 24200 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25281 components: - type: Transform @@ -12470,8 +12375,6 @@ entities: - 24296 - 24323 - 24295 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25283 components: - type: Transform @@ -12490,8 +12393,6 @@ entities: - 24269 - 24350 - 24268 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25286 components: - type: Transform @@ -12512,8 +12413,6 @@ entities: - 24433 - 24432 - 24417 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25287 components: - type: Transform @@ -12534,8 +12433,6 @@ entities: - 24287 - 24450 - 25289 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25290 components: - type: Transform @@ -12563,8 +12460,6 @@ entities: - 24699 - 24711 - 24701 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25293 components: - type: Transform @@ -12585,8 +12480,6 @@ entities: - 24850 - 24844 - 24839 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25295 components: - type: Transform @@ -12602,8 +12495,6 @@ entities: - 25236 - 24880 - 24879 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25298 components: - type: Transform @@ -12623,8 +12514,6 @@ entities: - 24908 - 24909 - 24889 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25299 components: - type: Transform @@ -12649,8 +12538,6 @@ entities: - 24848 - 24847 - 25018 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25305 components: - type: Transform @@ -12678,8 +12565,6 @@ entities: - 16024 - 24924 - 24478 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25309 components: - type: Transform @@ -12720,8 +12605,6 @@ entities: - 25028 - 25096 - 25148 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25311 components: - type: Transform @@ -12747,8 +12630,6 @@ entities: - 25039 - 24991 - 25065 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25315 components: - type: Transform @@ -12768,8 +12649,6 @@ entities: - 25117 - 25020 - 25116 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25317 components: - type: Transform @@ -12793,8 +12672,6 @@ entities: - 24988 - 25074 - 24970 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25319 components: - type: Transform @@ -12814,8 +12691,6 @@ entities: - 25253 - 25147 - 25178 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25321 components: - type: Transform @@ -12827,8 +12702,6 @@ entities: - 15911 - 25184 - 25185 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25323 components: - type: Transform @@ -12847,8 +12720,6 @@ entities: - 24085 - 24089 - 24083 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25325 components: - type: Transform @@ -12865,8 +12736,6 @@ entities: - 25258 - 13955 - 1148 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26597 components: - type: Transform @@ -12876,8 +12745,6 @@ entities: - type: DeviceList devices: - 27051 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26945 components: - type: Transform @@ -12893,8 +12760,6 @@ entities: - 26058 - 26064 - 26131 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26958 components: - type: Transform @@ -12913,16 +12778,12 @@ entities: - 26226 - 26130 - 26069 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26963 components: - type: Transform rot: 1.5707963267948966 rad pos: 39.5,16.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26970 components: - type: Transform @@ -12954,8 +12815,6 @@ entities: - 26317 - 26275 - 26276 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26989 components: - type: Transform @@ -12973,8 +12832,6 @@ entities: - 26949 - 26030 - 26049 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26991 components: - type: Transform @@ -12996,8 +12853,6 @@ entities: - 26952 - 26028 - 26051 - - type: AtmosDevice - joinedGrid: 6747 - uid: 27003 components: - type: Transform @@ -13020,8 +12875,6 @@ entities: - 27020 - 26329 - 27021 - - type: AtmosDevice - joinedGrid: 6747 - uid: 27005 components: - type: Transform @@ -13044,8 +12897,6 @@ entities: - 26316 - 13333 - 22062 - - type: AtmosDevice - joinedGrid: 6747 - uid: 27009 components: - type: Transform @@ -13061,8 +12912,6 @@ entities: - 26199 - 13217 - 26345 - - type: AtmosDevice - joinedGrid: 6747 - uid: 27036 components: - type: Transform @@ -13077,8 +12926,6 @@ entities: - 27035 - 26831 - 26824 - - type: AtmosDevice - joinedGrid: 6747 - uid: 27087 components: - type: Transform @@ -13098,8 +12945,6 @@ entities: - 14910 - 14908 - 27086 - - type: AtmosDevice - joinedGrid: 6747 - proto: AirCanister entities: - uid: 7790 @@ -13107,43 +12952,31 @@ entities: - type: Transform pos: 48.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8184 components: - type: Transform pos: 32.5,33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11336 components: - type: Transform pos: 57.5,-19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11372 components: - type: Transform pos: 34.5,22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 12029 components: - type: Transform pos: 33.5,24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 16663 components: - type: Transform pos: 13.5,-20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: Airlock entities: - uid: 1212 @@ -13683,16 +13516,6 @@ entities: - type: Transform pos: 69.5,-51.5 parent: 6747 - - uid: 588 - components: - - type: Transform - pos: 76.5,-24.5 - parent: 6747 - - uid: 880 - components: - - type: Transform - pos: 76.5,-19.5 - parent: 6747 - uid: 6216 components: - type: Transform @@ -13777,11 +13600,6 @@ entities: - type: Transform pos: 76.5,-44.5 parent: 6747 - - uid: 40 - components: - - type: Transform - pos: 76.5,-35.5 - parent: 6747 - uid: 514 components: - type: Transform @@ -13792,21 +13610,6 @@ entities: - type: Transform pos: 79.5,-43.5 parent: 6747 - - uid: 548 - components: - - type: Transform - pos: 76.5,-36.5 - parent: 6747 - - uid: 567 - components: - - type: Transform - pos: 79.5,-36.5 - parent: 6747 - - uid: 928 - components: - - type: Transform - pos: 79.5,-35.5 - parent: 6747 - uid: 994 components: - type: Transform @@ -13907,25 +13710,39 @@ entities: - type: Transform pos: -52.5,-18.5 parent: 6747 -- proto: AirlockExternalGlassShuttleEscape +- proto: AirlockExternalGlassShuttleArrivals entities: - - uid: 1129 + - uid: 5978 components: - type: Transform - rot: 3.141592653589793 rad - pos: 6.5,12.5 + rot: 1.5707963267948966 rad + pos: 76.5,-18.5 parent: 6747 - - uid: 6196 + - uid: 5999 components: - type: Transform - rot: 1.5707963267948966 rad - pos: 78.5,-19.5 + rot: -1.5707963267948966 rad + pos: 86.5,-25.5 parent: 6747 - - uid: 6197 + - uid: 6001 components: - type: Transform rot: 1.5707963267948966 rad - pos: 78.5,-24.5 + pos: 76.5,-25.5 + parent: 6747 + - uid: 19446 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-18.5 + parent: 6747 +- proto: AirlockExternalGlassShuttleEscape + entities: + - uid: 1129 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 6.5,12.5 parent: 6747 - uid: 7439 components: @@ -13959,24 +13776,12 @@ entities: rot: 3.141592653589793 rad pos: 21.5,36.5 parent: 6747 - - uid: 1081 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 82.5,-36.5 - parent: 6747 - uid: 1091 components: - type: Transform rot: 1.5707963267948966 rad pos: 82.5,-44.5 parent: 6747 - - uid: 1092 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 82.5,-35.5 - parent: 6747 - proto: AirlockExternalLocked entities: - uid: 7375 @@ -14035,6 +13840,18 @@ entities: parent: 6747 - proto: AirlockGlass entities: + - uid: 567 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 76.5,-36.5 + parent: 6747 + - uid: 588 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 76.5,-35.5 + parent: 6747 - uid: 1449 components: - type: Transform @@ -14140,6 +13957,12 @@ entities: - type: Transform pos: 62.5,-48.5 parent: 6747 + - uid: 5881 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-36.5 + parent: 6747 - uid: 7084 components: - type: Transform @@ -14150,6 +13973,12 @@ entities: - type: Transform pos: 24.5,4.5 parent: 6747 + - uid: 11804 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-35.5 + parent: 6747 - uid: 12906 components: - type: Transform @@ -16066,6 +15895,28 @@ entities: - type: Transform pos: 14.5,-54.5 parent: 6747 +- proto: AlwaysPoweredLightPostSmallRed + entities: + - uid: 5996 + components: + - type: Transform + pos: 76.5,-11.5 + parent: 6747 + - uid: 27687 + components: + - type: Transform + pos: 86.5,-11.5 + parent: 6747 + - uid: 27688 + components: + - type: Transform + pos: 78.5,-30.5 + parent: 6747 + - uid: 27689 + components: + - type: Transform + pos: 84.5,-30.5 + parent: 6747 - proto: AmeController entities: - uid: 17261 @@ -17588,30 +17439,22 @@ entities: - type: Transform pos: -1.5,-23.5 parent: 6747 - - uid: 5889 - components: - - type: Transform - pos: 78.5,-19.5 - parent: 6747 - - uid: 5902 + - uid: 5894 components: - type: Transform - pos: 82.5,-35.5 + rot: 1.5707963267948966 rad + pos: 76.5,-25.5 parent: 6747 - uid: 5959 components: - type: Transform pos: 82.5,-43.5 parent: 6747 - - uid: 5973 - components: - - type: Transform - pos: 82.5,-36.5 - parent: 6747 - - uid: 5982 + - uid: 5997 components: - type: Transform - pos: 78.5,-24.5 + rot: 1.5707963267948966 rad + pos: 76.5,-18.5 parent: 6747 - uid: 6098 components: @@ -18212,8 +18055,6 @@ entities: - type: Transform pos: 10.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: Beaker entities: - uid: 4364 @@ -18265,7 +18106,6 @@ entities: solutions: beaker: temperature: 293.15 - canMix: True canReact: True maxVol: 50 name: null @@ -19527,6 +19367,8 @@ entities: - type: Transform pos: 30.5,-13.5 parent: 6747 + - type: SpamEmitSound + enabled: False - proto: BoardGameSpawner entities: - uid: 6565 @@ -20363,6 +20205,11 @@ entities: parent: 6747 - proto: CableApcExtension entities: + - uid: 40 + components: + - type: Transform + pos: 84.5,-36.5 + parent: 6747 - uid: 346 components: - type: Transform @@ -20478,6 +20325,11 @@ entities: - type: Transform pos: 18.5,8.5 parent: 6747 + - uid: 4218 + components: + - type: Transform + pos: 85.5,-36.5 + parent: 6747 - uid: 4317 components: - type: Transform @@ -20516,13 +20368,23 @@ entities: - uid: 5891 components: - type: Transform - pos: 76.5,-19.5 + pos: 76.5,-18.5 + parent: 6747 + - uid: 5899 + components: + - type: Transform + pos: 75.5,-25.5 parent: 6747 - uid: 5901 components: - type: Transform pos: 71.5,-7.5 parent: 6747 + - uid: 5902 + components: + - type: Transform + pos: 76.5,-25.5 + parent: 6747 - uid: 5903 components: - type: Transform @@ -20553,6 +20415,11 @@ entities: - type: Transform pos: 76.5,-36.5 parent: 6747 + - uid: 5980 + components: + - type: Transform + pos: 86.5,-36.5 + parent: 6747 - uid: 6000 components: - type: Transform @@ -20603,10 +20470,10 @@ entities: - type: Transform pos: 69.5,-6.5 parent: 6747 - - uid: 6198 + - uid: 6173 components: - type: Transform - pos: 76.5,-24.5 + pos: 82.5,-36.5 parent: 6747 - uid: 6200 components: @@ -20628,10 +20495,10 @@ entities: - type: Transform pos: 72.5,-10.5 parent: 6747 - - uid: 6443 + - uid: 6539 components: - type: Transform - pos: 75.5,-24.5 + pos: 81.5,-36.5 parent: 6747 - uid: 6584 components: @@ -20773,6 +20640,11 @@ entities: - type: Transform pos: 8.5,0.5 parent: 6747 + - uid: 11803 + components: + - type: Transform + pos: 83.5,-36.5 + parent: 6747 - uid: 11806 components: - type: Transform @@ -25348,11 +25220,6 @@ entities: - type: Transform pos: 74.5,-20.5 parent: 6747 - - uid: 19446 - components: - - type: Transform - pos: 75.5,-26.5 - parent: 6747 - uid: 19447 components: - type: Transform @@ -36143,6 +36010,116 @@ entities: - type: Transform pos: -42.5,-17.5 parent: 6747 + - uid: 27724 + components: + - type: Transform + pos: 87.5,-36.5 + parent: 6747 + - uid: 27725 + components: + - type: Transform + pos: 87.5,-35.5 + parent: 6747 + - uid: 27726 + components: + - type: Transform + pos: 88.5,-35.5 + parent: 6747 + - uid: 27727 + components: + - type: Transform + pos: 88.5,-34.5 + parent: 6747 + - uid: 27728 + components: + - type: Transform + pos: 88.5,-33.5 + parent: 6747 + - uid: 27729 + components: + - type: Transform + pos: 88.5,-32.5 + parent: 6747 + - uid: 27730 + components: + - type: Transform + pos: 88.5,-31.5 + parent: 6747 + - uid: 27731 + components: + - type: Transform + pos: 88.5,-29.5 + parent: 6747 + - uid: 27732 + components: + - type: Transform + pos: 88.5,-30.5 + parent: 6747 + - uid: 27733 + components: + - type: Transform + pos: 88.5,-28.5 + parent: 6747 + - uid: 27734 + components: + - type: Transform + pos: 88.5,-27.5 + parent: 6747 + - uid: 27735 + components: + - type: Transform + pos: 88.5,-26.5 + parent: 6747 + - uid: 27736 + components: + - type: Transform + pos: 88.5,-25.5 + parent: 6747 + - uid: 27737 + components: + - type: Transform + pos: 88.5,-24.5 + parent: 6747 + - uid: 27738 + components: + - type: Transform + pos: 88.5,-22.5 + parent: 6747 + - uid: 27739 + components: + - type: Transform + pos: 88.5,-23.5 + parent: 6747 + - uid: 27740 + components: + - type: Transform + pos: 88.5,-21.5 + parent: 6747 + - uid: 27741 + components: + - type: Transform + pos: 88.5,-20.5 + parent: 6747 + - uid: 27742 + components: + - type: Transform + pos: 88.5,-19.5 + parent: 6747 + - uid: 27743 + components: + - type: Transform + pos: 87.5,-19.5 + parent: 6747 + - uid: 27744 + components: + - type: Transform + pos: 87.5,-18.5 + parent: 6747 + - uid: 27745 + components: + - type: Transform + pos: 86.5,-18.5 + parent: 6747 - proto: CableApcStack entities: - uid: 5081 @@ -47901,8 +47878,6 @@ entities: - type: Transform pos: 4.5,-82.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: CargoPallet entities: - uid: 4595 @@ -53624,6 +53599,146 @@ entities: - type: Transform pos: -52.5,16.5 parent: 6747 + - uid: 27761 + components: + - type: Transform + pos: 92.5,-28.5 + parent: 6747 + - uid: 27762 + components: + - type: Transform + pos: 92.5,-26.5 + parent: 6747 + - uid: 27763 + components: + - type: Transform + pos: 92.5,-27.5 + parent: 6747 + - uid: 27764 + components: + - type: Transform + pos: 92.5,-29.5 + parent: 6747 + - uid: 27765 + components: + - type: Transform + pos: 92.5,-30.5 + parent: 6747 + - uid: 27766 + components: + - type: Transform + pos: 92.5,-31.5 + parent: 6747 + - uid: 27767 + components: + - type: Transform + pos: 92.5,-32.5 + parent: 6747 + - uid: 27768 + components: + - type: Transform + pos: 92.5,-33.5 + parent: 6747 + - uid: 27769 + components: + - type: Transform + pos: 92.5,-34.5 + parent: 6747 + - uid: 27770 + components: + - type: Transform + pos: 92.5,-35.5 + parent: 6747 + - uid: 27771 + components: + - type: Transform + pos: 92.5,-36.5 + parent: 6747 + - uid: 27772 + components: + - type: Transform + pos: 92.5,-37.5 + parent: 6747 + - uid: 27773 + components: + - type: Transform + pos: 92.5,-38.5 + parent: 6747 + - uid: 27774 + components: + - type: Transform + pos: 92.5,-39.5 + parent: 6747 + - uid: 27775 + components: + - type: Transform + pos: 93.5,-39.5 + parent: 6747 + - uid: 27776 + components: + - type: Transform + pos: 93.5,-38.5 + parent: 6747 + - uid: 27777 + components: + - type: Transform + pos: 93.5,-37.5 + parent: 6747 + - uid: 27778 + components: + - type: Transform + pos: 93.5,-36.5 + parent: 6747 + - uid: 27779 + components: + - type: Transform + pos: 93.5,-35.5 + parent: 6747 + - uid: 27780 + components: + - type: Transform + pos: 93.5,-34.5 + parent: 6747 + - uid: 27781 + components: + - type: Transform + pos: 93.5,-33.5 + parent: 6747 + - uid: 27782 + components: + - type: Transform + pos: 93.5,-32.5 + parent: 6747 + - uid: 27783 + components: + - type: Transform + pos: 93.5,-30.5 + parent: 6747 + - uid: 27784 + components: + - type: Transform + pos: 93.5,-31.5 + parent: 6747 + - uid: 27785 + components: + - type: Transform + pos: 93.5,-29.5 + parent: 6747 + - uid: 27786 + components: + - type: Transform + pos: 93.5,-28.5 + parent: 6747 + - uid: 27787 + components: + - type: Transform + pos: 93.5,-27.5 + parent: 6747 + - uid: 27788 + components: + - type: Transform + pos: 93.5,-26.5 + parent: 6747 - proto: Cautery entities: - uid: 23896 @@ -55655,6 +55770,12 @@ entities: - type: Transform pos: -19.450268,-35.39411 parent: 6747 + - uid: 27810 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 93.521675,-29.405575 + parent: 6747 - proto: Cigarette entities: - uid: 3419 @@ -55813,16 +55934,6 @@ entities: - type: Transform pos: 69.5,-11.5 parent: 6747 - - uid: 6538 - components: - - type: Transform - pos: 75.5,-18.5 - parent: 6747 - - uid: 6539 - components: - - type: Transform - pos: 67.5,-18.5 - parent: 6747 - uid: 8724 components: - type: Transform @@ -55910,16 +56021,6 @@ entities: - type: Transform pos: 72.5,-11.5 parent: 6747 - - uid: 6541 - components: - - type: Transform - pos: 74.5,-18.5 - parent: 6747 - - uid: 6542 - components: - - type: Transform - pos: 68.5,-18.5 - parent: 6747 - uid: 8216 components: - type: Transform @@ -56365,6 +56466,16 @@ entities: - type: Transform pos: -8.5,-82.5 parent: 6747 + - uid: 5968 + components: + - type: Transform + pos: 69.5,-17.5 + parent: 6747 + - uid: 6197 + components: + - type: Transform + pos: 72.5,-17.5 + parent: 6747 - uid: 6621 components: - type: Transform @@ -56704,25 +56815,6 @@ entities: - type: Transform pos: 30.735046,7.834367 parent: 6747 -- proto: ClothingHeadHatHairflower - entities: - - uid: 7182 - components: - - type: Transform - pos: 36.539196,-16.411207 - parent: 6747 - - uid: 12967 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: -1.258544,-30.379358 - parent: 6747 - - uid: 17532 - components: - - type: Transform - rot: -1.5707963267948966 rad - pos: 36.309444,-94.22372 - parent: 6747 - proto: ClothingHeadHatHardhatWhite entities: - uid: 4362 @@ -56842,6 +56934,13 @@ entities: - type: Transform pos: -15.503069,13.299613 parent: 6747 +- proto: ClothingHeadHelmetBasic + entities: + - uid: 11582 + components: + - type: Transform + pos: 32.51328,-89.3608 + parent: 6747 - proto: ClothingHeadHelmetCosmonaut entities: - uid: 21864 @@ -56914,13 +57013,6 @@ entities: rot: 3.141592653589793 rad pos: -33.477093,1.431968 parent: 6747 -- proto: ClothingHeadHelmetScaf - entities: - - uid: 11582 - components: - - type: Transform - pos: 32.51328,-89.3608 - parent: 6747 - proto: ClothingHeadHelmetTemplar entities: - uid: 21866 @@ -57905,6 +57997,18 @@ entities: rot: 3.141592653589793 rad pos: 50.5,-43.5 parent: 6747 + - uid: 27813 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 93.5,-30.5 + parent: 6747 + - uid: 27814 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 93.5,-28.5 + parent: 6747 - proto: CommsComputerCircuitboard entities: - uid: 17712 @@ -59445,7 +59549,7 @@ entities: - type: Transform pos: -44.5,5.5 parent: 6747 -- proto: CrateFunPlushie +- proto: CrateFunToyBox entities: - uid: 11269 components: @@ -59962,8 +60066,6 @@ entities: - type: Transform pos: 12.5,-17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: CryoPodMachineCircuitboard entities: - uid: 17743 @@ -60184,8 +60286,6 @@ entities: - type: Transform pos: 67.5,14.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconAnomalyGenerator entities: - uid: 27409 @@ -60193,8 +60293,6 @@ entities: - type: Transform pos: 26.5,-66.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconArmory entities: - uid: 27410 @@ -60202,8 +60300,6 @@ entities: - type: Transform pos: -31.5,2.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconArrivals entities: - uid: 6925 @@ -60211,8 +60307,6 @@ entities: - type: Transform pos: 72.5,-22.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconArtifactLab entities: - uid: 27411 @@ -60220,8 +60314,6 @@ entities: - type: Transform pos: -6.5,-83.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconAtmospherics entities: - uid: 27077 @@ -60229,8 +60321,6 @@ entities: - type: Transform pos: 46.5,29.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconBar entities: - uid: 12090 @@ -60238,8 +60328,6 @@ entities: - type: Transform pos: -20.5,-30.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconBotany entities: - uid: 8864 @@ -60247,15 +60335,11 @@ entities: - type: Transform pos: -3.5,-16.5 parent: 6747 - missingComponents: - - WarpPoint - uid: 10931 components: - type: Transform pos: -18.5,-17.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconBridge entities: - uid: 12094 @@ -60263,8 +60347,6 @@ entities: - type: Transform pos: -25.5,-48.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconCameraServerRoom entities: - uid: 8862 @@ -60272,8 +60354,6 @@ entities: - type: Transform pos: -18.5,-57.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconCaptainsQuarters entities: - uid: 8858 @@ -60281,8 +60361,6 @@ entities: - type: Transform pos: -30.5,-54.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconCargoBay entities: - uid: 534 @@ -60290,8 +60368,6 @@ entities: - type: Transform pos: 20.5,35.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconCargoReception entities: - uid: 27567 @@ -60299,8 +60375,6 @@ entities: - type: Transform pos: 24.5,18.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconCERoom entities: - uid: 27412 @@ -60308,8 +60382,6 @@ entities: - type: Transform pos: 55.5,7.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconChapel entities: - uid: 27415 @@ -60317,8 +60389,6 @@ entities: - type: Transform pos: 14.5,-53.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconChemistry entities: - uid: 12093 @@ -60326,8 +60396,6 @@ entities: - type: Transform pos: 11.5,-26.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconCMORoom entities: - uid: 27413 @@ -60335,8 +60403,6 @@ entities: - type: Transform pos: 24.5,-36.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconConferenceRoom entities: - uid: 8721 @@ -60344,8 +60410,6 @@ entities: - type: Transform pos: -22.5,-52.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconCourtroom entities: - uid: 10934 @@ -60353,8 +60417,6 @@ entities: - type: Transform pos: -12.5,-44.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconCryonics entities: - uid: 12095 @@ -60362,8 +60424,6 @@ entities: - type: Transform pos: 10.5,-18.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconCryosleep entities: - uid: 27416 @@ -60371,8 +60431,6 @@ entities: - type: Transform pos: 68.5,-32.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconDetectiveRoom entities: - uid: 27417 @@ -60380,8 +60438,6 @@ entities: - type: Transform pos: -18.5,5.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconDisposals entities: - uid: 27418 @@ -60389,8 +60445,6 @@ entities: - type: Transform pos: 40.5,-0.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconDorms entities: - uid: 10935 @@ -60398,8 +60452,6 @@ entities: - type: Transform pos: 41.5,-14.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconEngineering entities: - uid: 649 @@ -60407,8 +60459,6 @@ entities: - type: Transform pos: 43.5,10.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconEscapePod entities: - uid: 10937 @@ -60416,15 +60466,11 @@ entities: - type: Transform pos: 6.5,11.5 parent: 6747 - missingComponents: - - WarpPoint - uid: 12097 components: - type: Transform pos: -53.5,-12.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconEvac entities: - uid: 7708 @@ -60432,8 +60478,6 @@ entities: - type: Transform pos: 66.5,-44.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconEVAStorage entities: - uid: 12096 @@ -60443,8 +60487,6 @@ entities: parent: 6747 - type: NavMapBeacon text: EVA - missingComponents: - - WarpPoint - proto: DefaultStationBeaconExam entities: - uid: 12098 @@ -60452,8 +60494,6 @@ entities: - type: Transform pos: 16.5,-24.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconGravGen entities: - uid: 27420 @@ -60461,8 +60501,6 @@ entities: - type: Transform pos: 66.5,6.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconHOPOffice entities: - uid: 12107 @@ -60470,8 +60508,6 @@ entities: - type: Transform pos: 41.5,-35.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconHOSRoom entities: - uid: 27421 @@ -60479,8 +60515,6 @@ entities: - type: Transform pos: -11.5,-6.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconJanitorsOffice entities: - uid: 22365 @@ -60488,8 +60522,6 @@ entities: - type: Transform pos: 30.5,2.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconKitchen entities: - uid: 22363 @@ -60497,8 +60529,6 @@ entities: - type: Transform pos: -7.5,-24.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconLawOffice entities: - uid: 10915 @@ -60506,8 +60536,6 @@ entities: - type: Transform pos: -8.5,-62.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconLibrary entities: - uid: 27422 @@ -60515,8 +60543,6 @@ entities: - type: Transform pos: 30.5,-56.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconMailroom entities: - uid: 3687 @@ -60524,8 +60550,6 @@ entities: - type: Transform pos: 32.5,15.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconMantis entities: - uid: 12109 @@ -60533,8 +60557,6 @@ entities: - type: Transform pos: 2.5,-55.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconMedbay entities: - uid: 1608 @@ -60542,8 +60564,6 @@ entities: - type: Transform pos: 25.5,-28.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconMedicalOutpost entities: - uid: 10927 @@ -60551,15 +60571,11 @@ entities: - type: Transform pos: -21.5,-10.5 parent: 6747 - missingComponents: - - WarpPoint - uid: 10928 components: - type: Transform pos: 49.5,-42.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconMetempsychosis entities: - uid: 10933 @@ -60567,8 +60583,6 @@ entities: - type: Transform pos: 19.5,-33.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconMorgue entities: - uid: 27423 @@ -60576,8 +60590,6 @@ entities: - type: Transform pos: 13.5,-38.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconPark entities: - uid: 22367 @@ -60585,8 +60597,6 @@ entities: - type: Transform pos: 13.5,-43.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconPermaBrig entities: - uid: 10921 @@ -60594,8 +60604,6 @@ entities: - type: Transform pos: -48.5,2.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconPowerBank entities: - uid: 27427 @@ -60603,8 +60611,6 @@ entities: - type: Transform pos: 59.5,20.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconPsychologist entities: - uid: 23899 @@ -60612,8 +60618,6 @@ entities: - type: Transform pos: 49.5,-52.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconQMRoom entities: - uid: 27424 @@ -60621,8 +60625,6 @@ entities: - type: Transform pos: 14.5,20.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconRDRoom entities: - uid: 27425 @@ -60630,8 +60632,6 @@ entities: - type: Transform pos: 14.5,-68.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconReporter entities: - uid: 22378 @@ -60639,8 +60639,6 @@ entities: - type: Transform pos: 37.5,-51.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconRND entities: - uid: 24100 @@ -60648,8 +60646,6 @@ entities: - type: Transform pos: 4.5,-75.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconRobotics entities: - uid: 27426 @@ -60657,8 +60653,6 @@ entities: - type: Transform pos: 2.5,-70.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconSalvage entities: - uid: 24101 @@ -60666,8 +60660,6 @@ entities: - type: Transform pos: -2.5,37.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconScience entities: - uid: 10936 @@ -60675,8 +60667,6 @@ entities: - type: Transform pos: 22.5,-50.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconSecurity entities: - uid: 22370 @@ -60686,15 +60676,11 @@ entities: parent: 6747 - type: NavMapBeacon text: Shooting Range - missingComponents: - - WarpPoint - uid: 24102 components: - type: Transform pos: 0.5,-4.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconService entities: - uid: 6477 @@ -60704,8 +60690,6 @@ entities: parent: 6747 - type: NavMapBeacon text: Barber - missingComponents: - - WarpPoint - uid: 27428 components: - type: Transform @@ -60713,8 +60697,6 @@ entities: parent: 6747 - type: NavMapBeacon text: Service Lounge - missingComponents: - - WarpPoint - proto: DefaultStationBeaconSolars entities: - uid: 25590 @@ -60722,22 +60704,16 @@ entities: - type: Transform pos: 40.5,49.5 parent: 6747 - missingComponents: - - WarpPoint - uid: 27429 components: - type: Transform pos: 78.5,18.5 parent: 6747 - missingComponents: - - WarpPoint - uid: 27431 components: - type: Transform pos: -53.5,-24.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconSurgery entities: - uid: 24103 @@ -60745,8 +60721,6 @@ entities: - type: Transform pos: 11.5,-31.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconTechVault entities: - uid: 27432 @@ -60754,8 +60728,6 @@ entities: - type: Transform pos: 72.5,9.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconTEG entities: - uid: 24980 @@ -60763,8 +60735,6 @@ entities: - type: Transform pos: 58.5,27.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconTelecoms entities: - uid: 27433 @@ -60772,8 +60742,6 @@ entities: - type: Transform pos: 62.5,8.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconTheater entities: - uid: 26333 @@ -60783,8 +60751,6 @@ entities: parent: 6747 - type: NavMapBeacon text: Arena - missingComponents: - - WarpPoint - proto: DefaultStationBeaconToolRoom entities: - uid: 27434 @@ -60792,8 +60758,6 @@ entities: - type: Transform pos: 54.5,-25.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconVault entities: - uid: 27435 @@ -60801,8 +60765,6 @@ entities: - type: Transform pos: -34.5,-46.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconVirology entities: - uid: 27076 @@ -60810,8 +60772,6 @@ entities: - type: Transform pos: 21.5,-15.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefaultStationBeaconWardensOffice entities: - uid: 27436 @@ -60819,8 +60779,6 @@ entities: - type: Transform pos: -21.5,-6.5 parent: 6747 - missingComponents: - - WarpPoint - proto: DefibrillatorCabinet entities: - uid: 3246 @@ -70845,7 +70803,6 @@ entities: solutions: drink: temperature: 293.15 - canMix: False canReact: True maxVol: 100 name: null @@ -71036,7 +70993,6 @@ entities: solutions: drink: temperature: 293.15 - canMix: True canReact: True maxVol: 20 name: null @@ -71197,7 +71153,6 @@ entities: solutions: drink: temperature: 293.15 - canMix: False canReact: True maxVol: 20 name: null @@ -71214,7 +71169,6 @@ entities: solutions: drink: temperature: 293.15 - canMix: False canReact: True maxVol: 20 name: null @@ -71238,7 +71192,6 @@ entities: solutions: drink: temperature: 293.15 - canMix: False canReact: True maxVol: 100 name: null @@ -71325,7 +71278,6 @@ entities: solutions: drink: temperature: 293.15 - canMix: False canReact: True maxVol: 20 name: null @@ -71344,7 +71296,6 @@ entities: solutions: drink: temperature: 293.15 - canMix: False canReact: True maxVol: 20 name: null @@ -71363,7 +71314,6 @@ entities: solutions: drink: temperature: 293.15 - canMix: False canReact: True maxVol: 20 name: null @@ -71376,29 +71326,11 @@ entities: - type: Transform pos: -59.004444,-0.28683114 parent: 6747 - - type: SolutionContainerManager - solutions: - drink: - temperature: 293.15 - canMix: False - canReact: True - maxVol: 20 - name: null - reagents: [] - uid: 23573 components: - type: Transform pos: -58.941944,-0.5161569 parent: 6747 - - type: SolutionContainerManager - solutions: - drink: - temperature: 293.15 - canMix: False - canReact: True - maxVol: 20 - name: null - reagents: [] - proto: DrinkWaterJug entities: - uid: 17594 @@ -72674,8 +72606,6 @@ entities: - 26981 - 26982 - 26983 - - type: AtmosDevice - joinedGrid: 6747 - uid: 295 components: - type: Transform @@ -72698,8 +72628,6 @@ entities: - 21749 - 21750 - 21751 - - type: AtmosDevice - joinedGrid: 6747 - uid: 767 components: - type: Transform @@ -72729,8 +72657,6 @@ entities: - 15913 - 25266 - 25268 - - type: AtmosDevice - joinedGrid: 6747 - uid: 1781 components: - type: Transform @@ -72753,8 +72679,6 @@ entities: - 25209 - 1958 - 25206 - - type: AtmosDevice - joinedGrid: 6747 - uid: 3693 components: - type: Transform @@ -72768,8 +72692,6 @@ entities: - 21775 - 22292 - 21906 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7457 components: - type: Transform @@ -72785,8 +72707,6 @@ entities: - 5324 - 5866 - 5878 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7458 components: - type: Transform @@ -72802,8 +72722,6 @@ entities: - 7446 - 7445 - 7444 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13176 components: - type: Transform @@ -72819,8 +72737,6 @@ entities: - 26988 - 1959 - 2421 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13869 components: - type: Transform @@ -72842,8 +72758,6 @@ entities: - 21749 - 21750 - 21751 - - type: AtmosDevice - joinedGrid: 6747 - uid: 17095 components: - type: Transform @@ -72860,8 +72774,6 @@ entities: - 22151 - 21882 - 23520 - - type: AtmosDevice - joinedGrid: 6747 - uid: 17357 components: - type: Transform @@ -72877,8 +72789,6 @@ entities: - 25257 - 25258 - 1148 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22035 components: - type: Transform @@ -72894,8 +72804,6 @@ entities: - 21769 - 21770 - 22290 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22039 components: - type: Transform @@ -72910,8 +72818,6 @@ entities: - 21918 - 8989 - 8988 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22040 components: - type: Transform @@ -72927,8 +72833,6 @@ entities: - 9007 - 22184 - 21917 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22042 components: - type: Transform @@ -72943,8 +72847,6 @@ entities: - 22188 - 22328 - 22327 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22044 components: - type: Transform @@ -72971,8 +72873,6 @@ entities: - 8984 - 21916 - 21389 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22088 components: - type: Transform @@ -72987,8 +72887,6 @@ entities: - 17141 - 25308 - 25307 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22090 components: - type: Transform @@ -73005,8 +72903,6 @@ entities: - 21797 - 21795 - 21796 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22092 components: - type: Transform @@ -73030,8 +72926,6 @@ entities: - 25302 - 25303 - 25304 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22094 components: - type: Transform @@ -73060,8 +72954,6 @@ entities: - 8966 - 21784 - 21783 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22095 components: - type: Transform @@ -73075,8 +72967,6 @@ entities: - 22161 - 22160 - 22159 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22098 components: - type: Transform @@ -73087,8 +72977,6 @@ entities: - 21788 - 21891 - 27085 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22100 components: - type: Transform @@ -73102,8 +72990,6 @@ entities: - 21890 - 21787 - 21889 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22102 components: - type: Transform @@ -73116,8 +73002,6 @@ entities: - 22236 - 22237 - 21886 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22104 components: - type: Transform @@ -73129,8 +73013,6 @@ entities: - 21888 - 21887 - 22154 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22106 components: - type: Transform @@ -73141,15 +73023,11 @@ entities: - 21885 - 22147 - 21786 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22112 components: - type: Transform pos: 28.5,-62.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22114 components: - type: Transform @@ -73162,8 +73040,6 @@ entities: - 22142 - 22146 - 21783 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22116 components: - type: Transform @@ -73175,8 +73051,6 @@ entities: - 21879 - 22144 - 21782 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22118 components: - type: Transform @@ -73191,8 +73065,6 @@ entities: - 22174 - 22178 - 21909 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22120 components: - type: Transform @@ -73205,8 +73077,6 @@ entities: - 22176 - 22177 - 21907 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22122 components: - type: Transform @@ -73239,8 +73109,6 @@ entities: - 8975 - 8973 - 22177 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22124 components: - type: Transform @@ -73259,8 +73127,6 @@ entities: - 21781 - 21772 - 21908 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22126 components: - type: Transform @@ -73281,8 +73147,6 @@ entities: - 25257 - 25258 - 1148 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22127 components: - type: Transform @@ -73292,8 +73156,6 @@ entities: devices: - 22321 - 21931 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22131 components: - type: Transform @@ -73305,8 +73167,6 @@ entities: - 22136 - 22137 - 22139 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22140 components: - type: Transform @@ -73318,8 +73178,6 @@ entities: - 22136 - 22138 - 22135 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22164 components: - type: Transform @@ -73335,8 +73193,6 @@ entities: - 21414 - 21415 - 22246 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22224 components: - type: Transform @@ -73358,8 +73214,6 @@ entities: - 17052 - 17367 - 17362 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22226 components: - type: Transform @@ -73373,8 +73227,6 @@ entities: - 21811 - 22143 - 23041 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22230 components: - type: Transform @@ -73389,8 +73241,6 @@ entities: - 22228 - 22231 - 21881 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22235 components: - type: Transform @@ -73403,8 +73253,6 @@ entities: - 22153 - 22152 - 438 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22238 components: - type: Transform @@ -73416,8 +73264,6 @@ entities: - 10125 - 22157 - 21810 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22240 components: - type: Transform @@ -73431,8 +73277,6 @@ entities: - 22157 - 10125 - 10126 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22289 components: - type: Transform @@ -73450,8 +73294,6 @@ entities: - 6533 - 9007 - 21915 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22294 components: - type: Transform @@ -73464,8 +73306,6 @@ entities: - 22181 - 22180 - 21911 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22318 components: - type: Transform @@ -73482,8 +73322,6 @@ entities: - 21818 - 21819 - 22186 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25272 components: - type: Transform @@ -73499,8 +73337,6 @@ entities: - 25204 - 25212 - 25205 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25276 components: - type: Transform @@ -73512,8 +73348,6 @@ entities: - 25212 - 25277 - 25207 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25279 components: - type: Transform @@ -73524,8 +73358,6 @@ entities: devices: - 25212 - 25208 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25282 components: - type: Transform @@ -73547,8 +73379,6 @@ entities: - 25232 - 25220 - 25221 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25284 components: - type: Transform @@ -73559,8 +73389,6 @@ entities: devices: - 25219 - 25216 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25285 components: - type: Transform @@ -73571,8 +73399,6 @@ entities: devices: - 25219 - 25217 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25288 components: - type: Transform @@ -73585,8 +73411,6 @@ entities: - 25222 - 22246 - 25224 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25291 components: - type: Transform @@ -73598,8 +73422,6 @@ entities: - 25226 - 25229 - 25230 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25294 components: - type: Transform @@ -73614,8 +73436,6 @@ entities: - 25236 - 22162 - 25235 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25296 components: - type: Transform @@ -73629,8 +73449,6 @@ entities: - 25234 - 25233 - 25236 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25297 components: - type: Transform @@ -73644,8 +73462,6 @@ entities: - 25241 - 25238 - 25237 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25300 components: - type: Transform @@ -73662,8 +73478,6 @@ entities: - 25219 - 25221 - 25220 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25306 components: - type: Transform @@ -73688,8 +73502,6 @@ entities: - 25301 - 22249 - 25307 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25310 components: - type: Transform @@ -73725,8 +73537,6 @@ entities: - 15908 - 15907 - 15905 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25312 components: - type: Transform @@ -73748,8 +73558,6 @@ entities: - 25264 - 25314 - 25313 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25318 components: - type: Transform @@ -73767,8 +73575,6 @@ entities: - 25252 - 25260 - 25261 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25320 components: - type: Transform @@ -73786,8 +73592,6 @@ entities: - 25261 - 25254 - 25253 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25322 components: - type: Transform @@ -73798,8 +73602,6 @@ entities: devices: - 25262 - 15911 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25324 components: - type: Transform @@ -73812,8 +73614,6 @@ entities: - 25268 - 25219 - 25267 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25386 components: - type: Transform @@ -73829,8 +73629,6 @@ entities: - 15916 - 15915 - 15914 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26947 components: - type: Transform @@ -73842,8 +73640,6 @@ entities: - 21921 - 26953 - 26952 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26959 components: - type: Transform @@ -73854,16 +73650,12 @@ entities: devices: - 21921 - 26954 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26964 components: - type: Transform rot: 1.5707963267948966 rad pos: 39.5,15.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26971 components: - type: Transform @@ -73889,8 +73681,6 @@ entities: - 26972 - 22433 - 26974 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26990 components: - type: Transform @@ -73905,8 +73695,6 @@ entities: - 26946 - 26948 - 26949 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26992 components: - type: Transform @@ -73926,8 +73714,6 @@ entities: - 26951 - 26960 - 26952 - - type: AtmosDevice - joinedGrid: 6747 - uid: 27004 components: - type: Transform @@ -73941,8 +73727,6 @@ entities: - 1820 - 27002 - 27001 - - type: AtmosDevice - joinedGrid: 6747 - uid: 27006 components: - type: Transform @@ -73961,8 +73745,6 @@ entities: - 26969 - 26972 - 26973 - - type: AtmosDevice - joinedGrid: 6747 - uid: 27008 components: - type: Transform @@ -73975,8 +73757,6 @@ entities: - 26974 - 21928 - 27010 - - type: AtmosDevice - joinedGrid: 6747 - uid: 27037 components: - type: Transform @@ -73989,8 +73769,6 @@ entities: - 27034 - 25212 - 27035 - - type: AtmosDevice - joinedGrid: 6747 - uid: 27088 components: - type: Transform @@ -74004,8 +73782,6 @@ entities: - 23520 - 5046 - 27086 - - type: AtmosDevice - joinedGrid: 6747 - proto: FireAlarmElectronics entities: - uid: 7654 @@ -76228,7 +76004,7 @@ entities: - type: Transform pos: 110.47042,36.19293 parent: 6747 -- proto: FoodChili +- proto: FoodChiliPepper entities: - uid: 3862 components: @@ -76606,6 +76382,25 @@ entities: - type: Transform pos: 48.306625,-15.94488 parent: 6747 +- proto: FoodPoppy + entities: + - uid: 7182 + components: + - type: Transform + pos: 36.539196,-16.411207 + parent: 6747 + - uid: 12967 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -1.258544,-30.379358 + parent: 6747 + - uid: 17532 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 36.309444,-94.22372 + parent: 6747 - proto: FoodPotato entities: - uid: 218 @@ -76809,8 +76604,6 @@ entities: - type: Transform pos: 11.5,-20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#99FFFFFF' - uid: 7925 @@ -76819,8 +76612,6 @@ entities: rot: 1.5707963267948966 rad pos: 50.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 11423 @@ -76829,8 +76620,6 @@ entities: rot: -1.5707963267948966 rad pos: 43.5,30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#66FF66FF' - uid: 11971 @@ -76839,8 +76628,6 @@ entities: rot: 3.141592653589793 rad pos: 58.5,38.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 13117 @@ -76849,16 +76636,12 @@ entities: rot: -1.5707963267948966 rad pos: 60.5,31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13152 components: - type: Transform rot: -1.5707963267948966 rad pos: 59.5,31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 13364 @@ -76866,8 +76649,6 @@ entities: - type: Transform pos: 60.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 13369 @@ -76876,8 +76657,6 @@ entities: rot: 3.141592653589793 rad pos: 62.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 26913 @@ -76885,8 +76664,6 @@ entities: - type: Transform pos: 11.5,-19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#99FFFFFF' - proto: GasFilterFlipped @@ -76897,8 +76674,6 @@ entities: rot: -1.5707963267948966 rad pos: 36.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7868 @@ -76907,8 +76682,6 @@ entities: rot: -1.5707963267948966 rad pos: 38.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 7869 @@ -76917,8 +76690,6 @@ entities: rot: -1.5707963267948966 rad pos: 40.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 7870 @@ -76927,8 +76698,6 @@ entities: rot: -1.5707963267948966 rad pos: 42.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 7871 @@ -76937,8 +76706,6 @@ entities: rot: -1.5707963267948966 rad pos: 44.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 7872 @@ -76947,8 +76714,6 @@ entities: rot: -1.5707963267948966 rad pos: 46.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 8120 @@ -76956,8 +76721,6 @@ entities: - type: Transform pos: 53.5,43.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - proto: GasMinerNitrogenStation @@ -76967,8 +76730,6 @@ entities: - type: Transform pos: 38.5,41.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: GasMinerOxygenStation entities: - uid: 6693 @@ -76976,8 +76737,6 @@ entities: - type: Transform pos: 36.5,41.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: GasMixer entities: - uid: 11233 @@ -76986,8 +76745,6 @@ entities: rot: -1.5707963267948966 rad pos: 2.5,-85.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: GasMixerFlipped entities: - uid: 7887 @@ -76999,8 +76756,6 @@ entities: - type: GasMixer inletTwoConcentration: 0.79 inletOneConcentration: 0.21 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 11232 @@ -77009,8 +76764,6 @@ entities: rot: 3.141592653589793 rad pos: 1.5,-85.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: GasOutletInjector entities: - uid: 250 @@ -77019,15 +76772,11 @@ entities: rot: 1.5707963267948966 rad pos: -20.5,9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7259 components: - type: Transform pos: 42.5,40.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF9999FF' - uid: 7261 @@ -77035,8 +76784,6 @@ entities: - type: Transform pos: 40.5,40.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF5500FF' - uid: 7262 @@ -77044,8 +76791,6 @@ entities: - type: Transform pos: 38.5,40.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF3333FF' - uid: 7263 @@ -77053,8 +76798,6 @@ entities: - type: Transform pos: 36.5,40.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 7938 @@ -77062,8 +76805,6 @@ entities: - type: Transform pos: 44.5,40.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#3399FFFF' - uid: 7939 @@ -77071,15 +76812,11 @@ entities: - type: Transform pos: 46.5,40.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7940 components: - type: Transform pos: 48.5,40.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8107 @@ -77087,8 +76824,6 @@ entities: - type: Transform pos: 53.5,49.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 8108 @@ -77096,48 +76831,36 @@ entities: - type: Transform pos: 54.5,49.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11251 components: - type: Transform rot: 3.141592653589793 rad pos: 3.5,-90.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11252 components: - type: Transform rot: 3.141592653589793 rad pos: 1.5,-90.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13284 components: - type: Transform rot: -1.5707963267948966 rad pos: 65.5,28.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13370 components: - type: Transform rot: -1.5707963267948966 rad pos: 65.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 15078 components: - type: Transform rot: 1.5707963267948966 rad pos: -10.5,-73.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15079 @@ -77146,8 +76869,6 @@ entities: rot: 3.141592653589793 rad pos: -9.5,-78.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15482 @@ -77156,45 +76877,33 @@ entities: rot: 3.141592653589793 rad pos: -34.5,-71.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 15483 components: - type: Transform rot: -1.5707963267948966 rad pos: -23.5,-68.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 15484 components: - type: Transform pos: -42.5,-53.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 15485 components: - type: Transform rot: 3.141592653589793 rad pos: -48.5,-82.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 16591 components: - type: Transform pos: -31.5,17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 16592 components: - type: Transform pos: -26.5,15.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: GasPassiveGate entities: - uid: 7933 @@ -77203,8 +76912,6 @@ entities: rot: 3.141592653589793 rad pos: 46.5,31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#66FF66FF' - uid: 23527 @@ -77212,8 +76919,6 @@ entities: - type: Transform pos: 43.5,33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#66FF66FF' - uid: 25075 @@ -77222,8 +76927,6 @@ entities: rot: 3.141592653589793 rad pos: -1.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - proto: GasPassiveVent @@ -77233,8 +76936,6 @@ entities: - type: Transform pos: 55.5,48.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 7945 @@ -77243,8 +76944,6 @@ entities: rot: 1.5707963267948966 rad pos: 36.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 7946 @@ -77253,8 +76952,6 @@ entities: rot: 1.5707963267948966 rad pos: 38.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF3333FF' - uid: 7947 @@ -77263,8 +76960,6 @@ entities: rot: 1.5707963267948966 rad pos: 40.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF5500FF' - uid: 7948 @@ -77273,8 +76968,6 @@ entities: rot: 1.5707963267948966 rad pos: 42.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF9999FF' - uid: 7949 @@ -77283,8 +76976,6 @@ entities: rot: 1.5707963267948966 rad pos: 44.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#3399FFFF' - uid: 7950 @@ -77293,16 +76984,12 @@ entities: rot: 1.5707963267948966 rad pos: 46.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7951 components: - type: Transform rot: 1.5707963267948966 rad pos: 48.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8031 @@ -77310,8 +76997,6 @@ entities: - type: Transform pos: 51.5,38.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 8151 @@ -77319,23 +77004,17 @@ entities: - type: Transform pos: 53.5,44.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11253 components: - type: Transform rot: 3.141592653589793 rad pos: 2.5,-89.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13272 components: - type: Transform pos: 57.5,41.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: GasPipeBend entities: - uid: 344 @@ -108146,16 +107825,12 @@ entities: rot: 3.141592653589793 rad pos: 10.5,-25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7258 components: - type: Transform rot: -1.5707963267948966 rad pos: 52.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 7260 @@ -108164,8 +107839,6 @@ entities: rot: -1.5707963267948966 rad pos: 52.5,33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 7753 @@ -108174,8 +107847,6 @@ entities: rot: -1.5707963267948966 rad pos: 35.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7864 @@ -108184,8 +107855,6 @@ entities: rot: -1.5707963267948966 rad pos: 35.5,33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7865 @@ -108194,8 +107863,6 @@ entities: rot: -1.5707963267948966 rad pos: 35.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7883 @@ -108204,8 +107871,6 @@ entities: rot: 1.5707963267948966 rad pos: 36.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 7888 @@ -108214,8 +107879,6 @@ entities: rot: 1.5707963267948966 rad pos: 38.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF3333FF' - uid: 7910 @@ -108224,56 +107887,42 @@ entities: rot: 3.141592653589793 rad pos: 49.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7913 components: - type: Transform rot: 3.141592653589793 rad pos: 45.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7917 components: - type: Transform rot: 3.141592653589793 rad pos: 41.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7918 components: - type: Transform rot: 3.141592653589793 rad pos: 47.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7926 components: - type: Transform rot: 3.141592653589793 rad pos: 48.5,31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8128 components: - type: Transform rot: 3.141592653589793 rad pos: 54.5,44.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8144 components: - type: Transform rot: 1.5707963267948966 rad pos: 53.5,33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 8145 @@ -108282,8 +107931,6 @@ entities: rot: 1.5707963267948966 rad pos: 53.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 11226 @@ -108291,44 +107938,32 @@ entities: - type: Transform pos: 1.5,-83.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11227 components: - type: Transform pos: 2.5,-83.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11228 components: - type: Transform pos: 3.5,-83.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11229 components: - type: Transform pos: 4.5,-83.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11256 components: - type: Transform pos: 2.5,-86.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11427 components: - type: Transform rot: 1.5707963267948966 rad pos: 49.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 11428 @@ -108337,8 +107972,6 @@ entities: rot: 1.5707963267948966 rad pos: 49.5,33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 11442 @@ -108347,8 +107980,6 @@ entities: rot: 1.5707963267948966 rad pos: 42.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#66FF66FF' - uid: 11769 @@ -108356,16 +107987,12 @@ entities: - type: Transform pos: -8.5,-84.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13084 components: - type: Transform rot: 1.5707963267948966 rad pos: 59.5,33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 13145 @@ -108373,16 +108000,12 @@ entities: - type: Transform pos: 60.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13150 components: - type: Transform rot: 3.141592653589793 rad pos: 61.5,28.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 13151 @@ -108391,8 +108014,6 @@ entities: rot: 3.141592653589793 rad pos: 60.5,28.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 13282 @@ -108401,24 +108022,18 @@ entities: rot: 1.5707963267948966 rad pos: 62.5,28.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13340 components: - type: Transform rot: 1.5707963267948966 rad pos: 59.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13341 components: - type: Transform rot: 1.5707963267948966 rad pos: 59.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 14914 @@ -108427,24 +108042,18 @@ entities: rot: 3.141592653589793 rad pos: 13.5,-20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22211 components: - type: Transform rot: 1.5707963267948966 rad pos: 10.5,-20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 23526 components: - type: Transform rot: 1.5707963267948966 rad pos: 42.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF9999FF' - uid: 23897 @@ -108453,8 +108062,6 @@ entities: rot: 1.5707963267948966 rad pos: 10.5,-19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: GasPressurePump entities: - uid: 246 @@ -108463,8 +108070,6 @@ entities: rot: -1.5707963267948966 rad pos: -19.5,9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7880 @@ -108472,8 +108077,6 @@ entities: - type: Transform pos: 37.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 7881 @@ -108481,8 +108084,6 @@ entities: - type: Transform pos: 39.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF3333FF' - uid: 7901 @@ -108491,8 +108092,6 @@ entities: rot: 3.141592653589793 rad pos: 48.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7903 @@ -108500,8 +108099,6 @@ entities: - type: Transform pos: 48.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7907 @@ -108509,8 +108106,6 @@ entities: - type: Transform pos: 49.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7914 @@ -108518,15 +108113,11 @@ entities: - type: Transform pos: 47.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7915 components: - type: Transform pos: 41.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF5500FF' - uid: 7919 @@ -108534,8 +108125,6 @@ entities: - type: Transform pos: 43.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF9999FF' - uid: 7920 @@ -108543,8 +108132,6 @@ entities: - type: Transform pos: 45.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#3399FFFF' - uid: 7931 @@ -108553,8 +108140,6 @@ entities: rot: -1.5707963267948966 rad pos: 48.5,30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 8121 @@ -108563,8 +108148,6 @@ entities: rot: 3.141592653589793 rad pos: 53.5,46.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 8127 @@ -108573,15 +108156,11 @@ entities: rot: 3.141592653589793 rad pos: 54.5,45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8141 components: - type: Transform pos: 54.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 11245 @@ -108589,31 +108168,23 @@ entities: - type: Transform pos: 1.5,-87.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11246 components: - type: Transform pos: 3.5,-87.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11255 components: - type: Transform rot: 3.141592653589793 rad pos: 2.5,-87.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11425 components: - type: Transform rot: 1.5707963267948966 rad pos: 44.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#66FF66FF' - uid: 11439 @@ -108622,8 +108193,6 @@ entities: rot: 3.141592653589793 rad pos: 40.5,29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 11441 @@ -108631,8 +108200,6 @@ entities: - type: Transform pos: 41.5,30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13283 @@ -108641,16 +108208,12 @@ entities: rot: 1.5707963267948966 rad pos: 63.5,28.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 15071 components: - type: Transform rot: -1.5707963267948966 rad pos: -8.5,-73.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15486 @@ -108659,8 +108222,6 @@ entities: rot: 1.5707963267948966 rad pos: -24.5,-68.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15521 @@ -108669,8 +108230,6 @@ entities: rot: 3.141592653589793 rad pos: -42.5,-54.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15538 @@ -108678,8 +108237,6 @@ entities: - type: Transform pos: -34.5,-70.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15546 @@ -108687,8 +108244,6 @@ entities: - type: Transform pos: -48.5,-81.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 16571 @@ -108697,8 +108252,6 @@ entities: rot: 3.141592653589793 rad pos: -31.5,16.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 16572 @@ -108707,8 +108260,6 @@ entities: rot: 3.141592653589793 rad pos: -26.5,14.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26911 @@ -108717,8 +108268,6 @@ entities: rot: 3.141592653589793 rad pos: 13.5,-19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#99FFFFFF' - proto: GasRecycler @@ -108728,8 +108277,6 @@ entities: - type: Transform pos: 45.5,31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#66FF66FF' - proto: GasThermoMachineFreezer @@ -108741,8 +108288,6 @@ entities: parent: 6747 - type: AtmosPipeColor color: '#0335FCFF' - - type: AtmosDevice - joinedGrid: 6747 - uid: 8147 components: - type: Transform @@ -108750,22 +108295,16 @@ entities: parent: 6747 - type: AtmosPipeColor color: '#CC6600FF' - - type: AtmosDevice - joinedGrid: 6747 - uid: 11224 components: - type: Transform pos: 0.5,-83.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11601 components: - type: Transform pos: -3.5,-86.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22329 components: - type: Transform @@ -108773,8 +108312,6 @@ entities: parent: 6747 - type: AtmosPipeColor color: '#99FFFFFF' - - type: AtmosDevice - joinedGrid: 6747 - proto: GasThermoMachineFreezerEnabled entities: - uid: 11825 @@ -108784,8 +108321,6 @@ entities: parent: 6747 - type: GasThermoMachine targetTemperature: 249.8167 - - type: AtmosDevice - joinedGrid: 6747 - proto: GasThermoMachineHeater entities: - uid: 7935 @@ -108796,8 +108331,6 @@ entities: parent: 6747 - type: AtmosPipeColor color: '#66FF66FF' - - type: AtmosDevice - joinedGrid: 6747 - uid: 8146 components: - type: Transform @@ -108805,22 +108338,16 @@ entities: parent: 6747 - type: AtmosPipeColor color: '#CC6600FF' - - type: AtmosDevice - joinedGrid: 6747 - uid: 11225 components: - type: Transform pos: 0.5,-85.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11602 components: - type: Transform pos: -11.5,-86.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22502 components: - type: Transform @@ -108828,8 +108355,6 @@ entities: parent: 6747 - type: AtmosPipeColor color: '#0335FCFF' - - type: AtmosDevice - joinedGrid: 6747 - proto: GasValve entities: - uid: 7542 @@ -108839,8 +108364,6 @@ entities: parent: 6747 - type: GasValve open: False - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 7862 @@ -108848,8 +108371,6 @@ entities: - type: Transform pos: 34.5,31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7892 @@ -108858,8 +108379,6 @@ entities: rot: 3.141592653589793 rad pos: 41.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7893 @@ -108870,8 +108389,6 @@ entities: parent: 6747 - type: GasValve open: False - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8030 @@ -108879,8 +108396,6 @@ entities: - type: Transform pos: 51.5,37.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 8210 @@ -108891,8 +108406,6 @@ entities: parent: 6747 - type: GasValve open: False - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 11944 @@ -108902,8 +108415,6 @@ entities: parent: 6747 - type: GasValve open: False - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#CC6600FF' - uid: 14625 @@ -108913,8 +108424,6 @@ entities: parent: 6747 - type: GasValve open: False - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14628 @@ -108924,8 +108433,6 @@ entities: parent: 6747 - type: GasValve open: False - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24983 @@ -108934,8 +108441,6 @@ entities: rot: -1.5707963267948966 rad pos: -0.5,-21.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#E5CCFFFF' - proto: GasVentPump @@ -108949,8 +108454,6 @@ entities: - type: DeviceNetwork deviceLists: - 7456 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 7254 @@ -108958,8 +108461,6 @@ entities: - type: Transform pos: 47.5,17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 8433 @@ -108968,8 +108469,6 @@ entities: rot: -1.5707963267948966 rad pos: 42.5,29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 11770 @@ -108978,15 +108477,11 @@ entities: rot: 3.141592653589793 rad pos: -8.5,-87.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 13062 components: - type: Transform pos: 53.5,17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13246 @@ -108995,8 +108490,6 @@ entities: rot: 1.5707963267948966 rad pos: 23.5,-2.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13451 @@ -109005,8 +108498,6 @@ entities: rot: -1.5707963267948966 rad pos: 39.5,10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13452 @@ -109015,8 +108506,6 @@ entities: rot: 1.5707963267948966 rad pos: 19.5,10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13484 @@ -109025,8 +108514,6 @@ entities: rot: 1.5707963267948966 rad pos: 10.5,27.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13486 @@ -109034,8 +108521,6 @@ entities: - type: Transform pos: -0.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13487 @@ -109044,8 +108529,6 @@ entities: rot: 3.141592653589793 rad pos: -56.5,-42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13496 @@ -109053,8 +108536,6 @@ entities: - type: Transform pos: 32.5,1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13499 @@ -109063,8 +108544,6 @@ entities: rot: -1.5707963267948966 rad pos: 37.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13658 @@ -109072,8 +108551,6 @@ entities: - type: Transform pos: 36.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13659 @@ -109081,8 +108558,6 @@ entities: - type: Transform pos: 39.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13660 @@ -109090,8 +108565,6 @@ entities: - type: Transform pos: 41.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13661 @@ -109099,8 +108572,6 @@ entities: - type: Transform pos: 48.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13662 @@ -109109,8 +108580,6 @@ entities: rot: -1.5707963267948966 rad pos: 51.5,-10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13664 @@ -109119,8 +108588,6 @@ entities: rot: -1.5707963267948966 rad pos: 50.5,-21.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13665 @@ -109129,8 +108596,6 @@ entities: rot: 3.141592653589793 rad pos: 46.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13666 @@ -109139,8 +108604,6 @@ entities: rot: 3.141592653589793 rad pos: 41.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13667 @@ -109149,8 +108612,6 @@ entities: rot: -1.5707963267948966 rad pos: 45.5,-17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13668 @@ -109159,8 +108620,6 @@ entities: rot: -1.5707963267948966 rad pos: 45.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13669 @@ -109169,8 +108628,6 @@ entities: rot: 3.141592653589793 rad pos: 36.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13670 @@ -109178,8 +108635,6 @@ entities: - type: Transform pos: 36.5,-17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13671 @@ -109188,8 +108643,6 @@ entities: rot: 3.141592653589793 rad pos: 38.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13672 @@ -109198,8 +108651,6 @@ entities: rot: 3.141592653589793 rad pos: 36.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13673 @@ -109208,8 +108659,6 @@ entities: rot: -1.5707963267948966 rad pos: 29.5,-14.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13674 @@ -109217,8 +108666,6 @@ entities: - type: Transform pos: 28.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13676 @@ -109226,8 +108673,6 @@ entities: - type: Transform pos: 38.5,-20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13774 @@ -109236,8 +108681,6 @@ entities: rot: -1.5707963267948966 rad pos: 26.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13784 @@ -109246,8 +108689,6 @@ entities: rot: 3.141592653589793 rad pos: 32.5,-22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13951 @@ -109256,8 +108697,6 @@ entities: rot: 1.5707963267948966 rad pos: 5.5,-37.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13952 @@ -109266,8 +108705,6 @@ entities: rot: -1.5707963267948966 rad pos: 17.5,-37.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13953 @@ -109276,8 +108713,6 @@ entities: rot: 1.5707963267948966 rad pos: 14.5,-34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13954 @@ -109286,8 +108721,6 @@ entities: rot: 3.141592653589793 rad pos: 21.5,-33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13955 @@ -109296,8 +108729,6 @@ entities: rot: 1.5707963267948966 rad pos: 12.5,-26.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13956 @@ -109305,8 +108736,6 @@ entities: - type: Transform pos: 17.5,-25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13957 @@ -109314,8 +108743,6 @@ entities: - type: Transform pos: 18.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13958 @@ -109323,8 +108750,6 @@ entities: - type: Transform pos: 25.5,-22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13959 @@ -109333,8 +108758,6 @@ entities: rot: 3.141592653589793 rad pos: 26.5,-27.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 13960 @@ -109343,8 +108766,6 @@ entities: rot: 3.141592653589793 rad pos: 26.5,-35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14115 @@ -109352,8 +108773,6 @@ entities: - type: Transform pos: 58.5,-1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14125 @@ -109362,8 +108781,6 @@ entities: rot: 3.141592653589793 rad pos: 58.5,-10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14130 @@ -109372,8 +108789,6 @@ entities: rot: -1.5707963267948966 rad pos: 58.5,-19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14230 @@ -109382,8 +108797,6 @@ entities: rot: 3.141592653589793 rad pos: 37.5,-29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14231 @@ -109392,8 +108805,6 @@ entities: rot: 3.141592653589793 rad pos: 46.5,-32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14232 @@ -109401,8 +108812,6 @@ entities: - type: Transform pos: 53.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14234 @@ -109411,8 +108820,6 @@ entities: rot: 3.141592653589793 rad pos: 53.5,-29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14235 @@ -109420,8 +108827,6 @@ entities: - type: Transform pos: 74.5,-20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14236 @@ -109433,8 +108838,6 @@ entities: - type: DeviceNetwork deviceLists: - 7456 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14264 @@ -109443,8 +108846,6 @@ entities: rot: 1.5707963267948966 rad pos: 36.5,-39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14293 @@ -109453,8 +108854,6 @@ entities: rot: -1.5707963267948966 rad pos: 30.5,-36.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14294 @@ -109463,8 +108862,6 @@ entities: rot: -1.5707963267948966 rad pos: 41.5,-32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14397 @@ -109472,8 +108869,6 @@ entities: - type: Transform pos: 67.5,-41.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14439 @@ -109482,8 +108877,6 @@ entities: rot: 1.5707963267948966 rad pos: 58.5,-41.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14717 @@ -109492,8 +108885,6 @@ entities: rot: 3.141592653589793 rad pos: 36.5,-51.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14718 @@ -109501,8 +108892,6 @@ entities: - type: Transform pos: 36.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14719 @@ -109510,8 +108899,6 @@ entities: - type: Transform pos: 54.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14720 @@ -109520,8 +108907,6 @@ entities: rot: -1.5707963267948966 rad pos: 74.5,-47.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14721 @@ -109529,8 +108914,6 @@ entities: - type: Transform pos: 72.5,-41.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14726 @@ -109539,8 +108922,6 @@ entities: rot: 1.5707963267948966 rad pos: 28.5,-50.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14727 @@ -109549,8 +108930,6 @@ entities: rot: 3.141592653589793 rad pos: 29.5,-61.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14733 @@ -109558,8 +108937,6 @@ entities: - type: Transform pos: 25.5,-44.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14735 @@ -109568,8 +108945,6 @@ entities: rot: 3.141592653589793 rad pos: 25.5,-56.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14743 @@ -109578,8 +108953,6 @@ entities: rot: -1.5707963267948966 rad pos: 26.5,-49.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14744 @@ -109587,8 +108960,6 @@ entities: - type: Transform pos: 19.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14759 @@ -109597,8 +108968,6 @@ entities: rot: 3.141592653589793 rad pos: -11.5,-48.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14760 @@ -109606,8 +108975,6 @@ entities: - type: Transform pos: -9.5,-42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14763 @@ -109615,8 +108982,6 @@ entities: - type: Transform pos: -11.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14774 @@ -109624,8 +108989,6 @@ entities: - type: Transform pos: -5.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14806 @@ -109633,8 +108996,6 @@ entities: - type: Transform pos: 5.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14829 @@ -109643,8 +109004,6 @@ entities: rot: 3.141592653589793 rad pos: 1.5,-82.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14830 @@ -109652,8 +109011,6 @@ entities: - type: Transform pos: 3.5,-75.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14841 @@ -109662,8 +109019,6 @@ entities: rot: 1.5707963267948966 rad pos: -7.5,-83.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14842 @@ -109672,8 +109027,6 @@ entities: rot: -1.5707963267948966 rad pos: -4.5,-81.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14896 @@ -109682,8 +109035,6 @@ entities: rot: 1.5707963267948966 rad pos: 8.5,-62.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14897 @@ -109691,8 +109042,6 @@ entities: - type: Transform pos: 6.5,-72.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14899 @@ -109701,8 +109050,6 @@ entities: rot: 1.5707963267948966 rad pos: 8.5,-67.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14902 @@ -109711,8 +109058,6 @@ entities: rot: 3.141592653589793 rad pos: 19.5,-66.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14903 @@ -109721,8 +109066,6 @@ entities: rot: 1.5707963267948966 rad pos: 19.5,-58.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14906 @@ -109731,8 +109074,6 @@ entities: rot: 1.5707963267948966 rad pos: 19.5,-51.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14910 @@ -109741,8 +109082,6 @@ entities: rot: 3.141592653589793 rad pos: 20.5,-68.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14920 @@ -109751,8 +109090,6 @@ entities: rot: 1.5707963267948966 rad pos: 14.5,-67.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 14926 @@ -109761,8 +109098,6 @@ entities: rot: 3.141592653589793 rad pos: 28.5,-66.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15004 @@ -109771,8 +109106,6 @@ entities: rot: 1.5707963267948966 rad pos: 24.5,-87.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15005 @@ -109781,8 +109114,6 @@ entities: rot: 1.5707963267948966 rad pos: 32.5,-81.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15006 @@ -109790,8 +109121,6 @@ entities: - type: Transform pos: 27.5,-78.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15007 @@ -109800,8 +109129,6 @@ entities: rot: 3.141592653589793 rad pos: 37.5,-83.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15011 @@ -109810,8 +109137,6 @@ entities: rot: 3.141592653589793 rad pos: 18.5,-74.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15131 @@ -109819,8 +109144,6 @@ entities: - type: Transform pos: 10.5,-55.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15132 @@ -109829,8 +109152,6 @@ entities: rot: -1.5707963267948966 rad pos: 15.5,-56.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15141 @@ -109839,8 +109160,6 @@ entities: rot: 3.141592653589793 rad pos: 10.5,-59.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15144 @@ -109849,8 +109168,6 @@ entities: rot: 3.141592653589793 rad pos: 14.5,-58.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15162 @@ -109859,8 +109176,6 @@ entities: rot: 1.5707963267948966 rad pos: 2.5,-56.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15170 @@ -109869,8 +109184,6 @@ entities: rot: 1.5707963267948966 rad pos: -0.5,-58.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15172 @@ -109879,8 +109192,6 @@ entities: rot: 3.141592653589793 rad pos: 3.5,-60.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15208 @@ -109889,8 +109200,6 @@ entities: rot: -1.5707963267948966 rad pos: 9.5,-42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15211 @@ -109899,8 +109208,6 @@ entities: rot: 1.5707963267948966 rad pos: -0.5,-44.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15230 @@ -109909,8 +109216,6 @@ entities: rot: -1.5707963267948966 rad pos: -5.5,-54.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15267 @@ -109919,8 +109224,6 @@ entities: rot: -1.5707963267948966 rad pos: 1.5,-65.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15391 @@ -109928,8 +109231,6 @@ entities: - type: Transform pos: -33.5,-47.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15392 @@ -109937,8 +109238,6 @@ entities: - type: Transform pos: -31.5,-47.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15393 @@ -109947,8 +109246,6 @@ entities: rot: -1.5707963267948966 rad pos: -23.5,-49.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15394 @@ -109960,8 +109257,6 @@ entities: - type: DeviceNetwork deviceLists: - 22089 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15395 @@ -109973,8 +109268,6 @@ entities: - type: DeviceNetwork deviceLists: - 22089 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15396 @@ -109986,8 +109279,6 @@ entities: - type: DeviceNetwork deviceLists: - 22089 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15403 @@ -109996,8 +109287,6 @@ entities: rot: 1.5707963267948966 rad pos: -32.5,-54.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15411 @@ -110006,8 +109295,6 @@ entities: rot: -1.5707963267948966 rad pos: -27.5,-58.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15412 @@ -110016,8 +109303,6 @@ entities: rot: 3.141592653589793 rad pos: -31.5,-59.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15431 @@ -110029,8 +109314,6 @@ entities: - type: DeviceNetwork deviceLists: - 22239 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15445 @@ -110039,8 +109322,6 @@ entities: rot: 3.141592653589793 rad pos: 61.5,-52.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15555 @@ -110049,8 +109330,6 @@ entities: rot: 3.141592653589793 rad pos: -45.5,-37.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15574 @@ -110058,8 +109337,6 @@ entities: - type: Transform pos: -44.5,-40.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15989 @@ -110067,8 +109344,6 @@ entities: - type: Transform pos: 16.5,-1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 15990 @@ -110077,8 +109352,6 @@ entities: rot: 3.141592653589793 rad pos: 10.5,-10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 16067 @@ -110087,8 +109360,6 @@ entities: rot: -1.5707963267948966 rad pos: 2.5,-36.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 16623 @@ -110097,8 +109368,6 @@ entities: rot: 3.141592653589793 rad pos: -9.5,-65.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 16624 @@ -110107,8 +109376,6 @@ entities: rot: 3.141592653589793 rad pos: -7.5,-65.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 16625 @@ -110117,8 +109384,6 @@ entities: rot: -1.5707963267948966 rad pos: -6.5,-61.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 16626 @@ -110127,8 +109392,6 @@ entities: rot: -1.5707963267948966 rad pos: -5.5,-58.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 16723 @@ -110136,8 +109399,6 @@ entities: - type: Transform pos: 13.5,-21.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 16844 @@ -110146,8 +109407,6 @@ entities: rot: 1.5707963267948966 rad pos: 46.5,-41.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 16846 @@ -110156,8 +109415,6 @@ entities: rot: -1.5707963267948966 rad pos: 51.5,-43.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 17062 @@ -110166,8 +109423,6 @@ entities: rot: 3.141592653589793 rad pos: 54.5,-42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 17395 @@ -110176,8 +109431,6 @@ entities: rot: 3.141592653589793 rad pos: 47.5,-51.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 21428 @@ -110188,8 +109441,6 @@ entities: - type: DeviceNetwork deviceLists: - 22239 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 22062 @@ -110197,8 +109448,6 @@ entities: - type: Transform pos: 12.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 22268 @@ -110207,8 +109456,6 @@ entities: rot: -1.5707963267948966 rad pos: 6.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 22284 @@ -110217,8 +109464,6 @@ entities: rot: 1.5707963267948966 rad pos: 15.5,5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 22310 @@ -110226,8 +109471,6 @@ entities: - type: Transform pos: 22.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 22311 @@ -110236,8 +109479,6 @@ entities: rot: 1.5707963267948966 rad pos: 21.5,-16.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 22312 @@ -110246,8 +109487,6 @@ entities: rot: 3.141592653589793 rad pos: 22.5,-19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 22324 @@ -110255,8 +109494,6 @@ entities: - type: Transform pos: 18.5,-7.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 22326 @@ -110265,8 +109502,6 @@ entities: rot: -1.5707963267948966 rad pos: 33.5,-14.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 23663 @@ -110275,8 +109510,6 @@ entities: rot: 1.5707963267948966 rad pos: -2.5,-21.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#E5CCFFFF' - uid: 23664 @@ -110284,8 +109517,6 @@ entities: - type: Transform pos: -19.5,-46.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 23671 @@ -110294,8 +109525,6 @@ entities: rot: 3.141592653589793 rad pos: 4.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 23683 @@ -110304,8 +109533,6 @@ entities: rot: 1.5707963267948966 rad pos: -2.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 23684 @@ -110314,8 +109541,6 @@ entities: rot: 3.141592653589793 rad pos: -0.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 23752 @@ -110323,8 +109548,6 @@ entities: - type: Transform pos: -12.5,-10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24049 @@ -110333,8 +109556,6 @@ entities: rot: 3.141592653589793 rad pos: 34.5,-94.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24050 @@ -110342,8 +109563,6 @@ entities: - type: Transform pos: 27.5,-91.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24051 @@ -110352,8 +109571,6 @@ entities: rot: 3.141592653589793 rad pos: 27.5,-94.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24087 @@ -110362,8 +109579,6 @@ entities: rot: 1.5707963267948966 rad pos: -25.5,-11.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24088 @@ -110371,8 +109586,6 @@ entities: - type: Transform pos: -20.5,-10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24089 @@ -110380,8 +109593,6 @@ entities: - type: Transform pos: -18.5,-10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24119 @@ -110390,8 +109601,6 @@ entities: rot: 3.141592653589793 rad pos: -5.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24120 @@ -110399,8 +109608,6 @@ entities: - type: Transform pos: -5.5,-1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24133 @@ -110409,8 +109616,6 @@ entities: rot: -1.5707963267948966 rad pos: -5.5,2.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24175 @@ -110419,8 +109624,6 @@ entities: rot: 3.141592653589793 rad pos: -17.5,-2.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24178 @@ -110429,8 +109632,6 @@ entities: rot: -1.5707963267948966 rad pos: -6.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24179 @@ -110439,8 +109640,6 @@ entities: rot: 1.5707963267948966 rad pos: -13.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24200 @@ -110449,8 +109648,6 @@ entities: rot: 1.5707963267948966 rad pos: -23.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24201 @@ -110459,8 +109656,6 @@ entities: rot: 3.141592653589793 rad pos: -20.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24259 @@ -110468,8 +109663,6 @@ entities: - type: Transform pos: -38.5,7.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24268 @@ -110477,8 +109670,6 @@ entities: - type: Transform pos: -33.5,3.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24269 @@ -110487,8 +109678,6 @@ entities: rot: 1.5707963267948966 rad pos: -33.5,6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24270 @@ -110497,8 +109686,6 @@ entities: rot: 1.5707963267948966 rad pos: -33.5,9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24323 @@ -110507,8 +109694,6 @@ entities: rot: -1.5707963267948966 rad pos: -29.5,-12.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24324 @@ -110517,8 +109702,6 @@ entities: rot: -1.5707963267948966 rad pos: -29.5,-8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24325 @@ -110527,8 +109710,6 @@ entities: rot: -1.5707963267948966 rad pos: -29.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24384 @@ -110536,8 +109717,6 @@ entities: - type: Transform pos: -37.5,0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24407 @@ -110546,8 +109725,6 @@ entities: rot: 3.141592653589793 rad pos: -43.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24408 @@ -110556,8 +109733,6 @@ entities: rot: 3.141592653589793 rad pos: -41.5,0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24409 @@ -110566,8 +109741,6 @@ entities: rot: 3.141592653589793 rad pos: -45.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24410 @@ -110576,8 +109749,6 @@ entities: rot: 3.141592653589793 rad pos: -47.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24432 @@ -110586,8 +109757,6 @@ entities: rot: -1.5707963267948966 rad pos: -42.5,9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24433 @@ -110595,8 +109764,6 @@ entities: - type: Transform pos: -46.5,8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24435 @@ -110605,8 +109772,6 @@ entities: rot: -1.5707963267948966 rad pos: -46.5,4.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24457 @@ -110615,8 +109780,6 @@ entities: rot: -1.5707963267948966 rad pos: -41.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24459 @@ -110624,8 +109787,6 @@ entities: - type: Transform pos: -42.5,-4.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24461 @@ -110634,8 +109795,6 @@ entities: rot: 3.141592653589793 rad pos: -41.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24643 @@ -110644,8 +109803,6 @@ entities: rot: 1.5707963267948966 rad pos: -67.5,-3.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24705 @@ -110653,8 +109810,6 @@ entities: - type: Transform pos: -63.5,12.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24706 @@ -110663,8 +109818,6 @@ entities: rot: 1.5707963267948966 rad pos: -64.5,7.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24707 @@ -110672,8 +109825,6 @@ entities: - type: Transform pos: -60.5,6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24708 @@ -110681,8 +109832,6 @@ entities: - type: Transform pos: -66.5,7.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24709 @@ -110690,8 +109839,6 @@ entities: - type: Transform pos: -71.5,5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24710 @@ -110700,8 +109847,6 @@ entities: rot: 3.141592653589793 rad pos: -68.5,0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24711 @@ -110710,8 +109855,6 @@ entities: rot: 3.141592653589793 rad pos: -63.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24844 @@ -110720,8 +109863,6 @@ entities: rot: -1.5707963267948966 rad pos: -42.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24847 @@ -110730,8 +109871,6 @@ entities: rot: -1.5707963267948966 rad pos: -34.5,-30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24848 @@ -110740,8 +109879,6 @@ entities: rot: -1.5707963267948966 rad pos: -34.5,-27.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24849 @@ -110749,8 +109886,6 @@ entities: - type: Transform pos: -37.5,-26.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24850 @@ -110758,8 +109893,6 @@ entities: - type: Transform pos: -39.5,-22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24879 @@ -110768,8 +109901,6 @@ entities: rot: 3.141592653589793 rad pos: -37.5,-36.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24887 @@ -110778,8 +109909,6 @@ entities: rot: -1.5707963267948966 rad pos: -25.5,-35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24888 @@ -110788,8 +109917,6 @@ entities: rot: 3.141592653589793 rad pos: -26.5,-39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24889 @@ -110798,8 +109925,6 @@ entities: rot: 3.141592653589793 rad pos: -32.5,-39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24924 @@ -110808,8 +109933,6 @@ entities: rot: 1.5707963267948966 rad pos: -14.5,-36.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 24940 @@ -110818,8 +109941,6 @@ entities: rot: 1.5707963267948966 rad pos: -0.5,-31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25038 @@ -110828,8 +109949,6 @@ entities: rot: -1.5707963267948966 rad pos: -4.5,-30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25039 @@ -110838,8 +109957,6 @@ entities: rot: 1.5707963267948966 rad pos: -21.5,-28.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25065 @@ -110848,8 +109965,6 @@ entities: rot: 3.141592653589793 rad pos: -19.5,-35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25074 @@ -110858,8 +109973,6 @@ entities: rot: -1.5707963267948966 rad pos: 3.5,-25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25096 @@ -110868,8 +109981,6 @@ entities: rot: 1.5707963267948966 rad pos: -12.5,-23.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25114 @@ -110878,8 +109989,6 @@ entities: rot: 1.5707963267948966 rad pos: -29.5,-17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25115 @@ -110888,8 +109997,6 @@ entities: rot: 1.5707963267948966 rad pos: -29.5,-22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25116 @@ -110897,8 +110004,6 @@ entities: - type: Transform pos: -24.5,-18.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25117 @@ -110907,8 +110012,6 @@ entities: rot: 3.141592653589793 rad pos: -24.5,-29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25131 @@ -110917,8 +110020,6 @@ entities: rot: 3.141592653589793 rad pos: -27.5,-23.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25136 @@ -110926,8 +110027,6 @@ entities: - type: Transform pos: -6.5,-22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25178 @@ -110936,8 +110035,6 @@ entities: rot: -1.5707963267948966 rad pos: -3.5,-17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25179 @@ -110945,8 +110042,6 @@ entities: - type: Transform pos: -11.5,-14.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25185 @@ -110954,8 +110049,6 @@ entities: - type: Transform pos: -19.5,-19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25289 @@ -110964,8 +110057,6 @@ entities: rot: 3.141592653589793 rad pos: -35.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25441 @@ -110974,8 +110065,6 @@ entities: rot: -1.5707963267948966 rad pos: -53.5,-11.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25442 @@ -110984,8 +110073,6 @@ entities: rot: -1.5707963267948966 rad pos: -53.5,-7.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25595 @@ -110993,8 +110080,6 @@ entities: - type: Transform pos: 3.5,-70.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 25606 @@ -111003,8 +110088,6 @@ entities: rot: 3.141592653589793 rad pos: 47.5,8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26028 @@ -111013,8 +110096,6 @@ entities: rot: -1.5707963267948966 rad pos: 59.5,18.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26030 @@ -111023,8 +110104,6 @@ entities: rot: -1.5707963267948966 rad pos: 58.5,26.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26031 @@ -111033,8 +110112,6 @@ entities: rot: 1.5707963267948966 rad pos: 54.5,26.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26056 @@ -111043,8 +110120,6 @@ entities: rot: 3.141592653589793 rad pos: 61.5,11.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26058 @@ -111053,8 +110128,6 @@ entities: rot: 3.141592653589793 rad pos: 66.5,11.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26064 @@ -111063,8 +110136,6 @@ entities: rot: 3.141592653589793 rad pos: 71.5,6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26069 @@ -111073,8 +110144,6 @@ entities: rot: 1.5707963267948966 rad pos: 68.5,9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26070 @@ -111083,8 +110152,6 @@ entities: rot: 3.141592653589793 rad pos: 50.5,8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26072 @@ -111093,8 +110160,6 @@ entities: rot: 3.141592653589793 rad pos: 55.5,8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26074 @@ -111103,8 +110168,6 @@ entities: rot: 3.141592653589793 rad pos: 60.5,8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26169 @@ -111113,8 +110176,6 @@ entities: rot: -1.5707963267948966 rad pos: 35.5,25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26170 @@ -111123,8 +110184,6 @@ entities: rot: 1.5707963267948966 rad pos: 28.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26172 @@ -111133,8 +110192,6 @@ entities: rot: 1.5707963267948966 rad pos: 28.5,24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26276 @@ -111143,8 +110200,6 @@ entities: rot: 1.5707963267948966 rad pos: 33.5,14.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26317 @@ -111153,8 +110208,6 @@ entities: rot: 3.141592653589793 rad pos: 26.5,19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26337 @@ -111163,8 +110216,6 @@ entities: rot: -1.5707963267948966 rad pos: 17.5,25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26345 @@ -111172,8 +110223,6 @@ entities: - type: Transform pos: 18.5,22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26346 @@ -111182,8 +110231,6 @@ entities: rot: 1.5707963267948966 rad pos: 11.5,21.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26367 @@ -111191,8 +110238,6 @@ entities: - type: Transform pos: 23.5,27.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26828 @@ -111201,8 +110246,6 @@ entities: rot: 1.5707963267948966 rad pos: -25.5,7.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26829 @@ -111211,8 +110254,6 @@ entities: rot: 1.5707963267948966 rad pos: -25.5,4.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26830 @@ -111221,8 +110262,6 @@ entities: rot: 1.5707963267948966 rad pos: -25.5,1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26831 @@ -111231,8 +110270,6 @@ entities: rot: -1.5707963267948966 rad pos: -17.5,6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 26997 @@ -111240,8 +110277,6 @@ entities: - type: Transform pos: 16.5,14.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 27011 @@ -111250,8 +110285,6 @@ entities: rot: 1.5707963267948966 rad pos: 4.5,29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 27020 @@ -111260,8 +110293,6 @@ entities: rot: 3.141592653589793 rad pos: 3.5,24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 27021 @@ -111270,8 +110301,6 @@ entities: rot: 3.141592653589793 rad pos: 6.5,24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 27297 @@ -111283,8 +110312,6 @@ entities: - type: DeviceNetwork deviceLists: - 7456 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - uid: 27395 @@ -111296,8 +110323,6 @@ entities: - type: DeviceNetwork deviceLists: - 7456 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#0335FCFF' - proto: GasVentScrubber @@ -111311,8 +110336,6 @@ entities: - type: DeviceNetwork deviceLists: - 7456 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 3755 @@ -111324,8 +110347,6 @@ entities: - type: DeviceNetwork deviceLists: - 7456 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 7909 @@ -111333,8 +110354,6 @@ entities: - type: Transform pos: 36.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 11508 @@ -111343,8 +110362,6 @@ entities: rot: 3.141592653589793 rad pos: 36.5,-29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13077 @@ -111352,8 +110369,6 @@ entities: - type: Transform pos: 51.5,25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13162 @@ -111362,8 +110377,6 @@ entities: rot: -1.5707963267948966 rad pos: 39.5,9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13163 @@ -111371,8 +110384,6 @@ entities: - type: Transform pos: 30.5,33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13174 @@ -111381,8 +110392,6 @@ entities: rot: -1.5707963267948966 rad pos: 35.5,26.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13208 @@ -111390,8 +110399,6 @@ entities: - type: Transform pos: 30.5,1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13217 @@ -111400,8 +110407,6 @@ entities: rot: 1.5707963267948966 rad pos: 15.5,18.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13231 @@ -111409,8 +110414,6 @@ entities: - type: Transform pos: 52.5,17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13332 @@ -111418,8 +110421,6 @@ entities: - type: Transform pos: -1.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13333 @@ -111428,8 +110429,6 @@ entities: rot: -1.5707963267948966 rad pos: 13.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13334 @@ -111438,8 +110437,6 @@ entities: rot: 3.141592653589793 rad pos: 11.5,25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13335 @@ -111448,8 +110445,6 @@ entities: rot: 1.5707963267948966 rad pos: 19.5,9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13376 @@ -111458,8 +110453,6 @@ entities: rot: 1.5707963267948966 rad pos: 23.5,-1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13500 @@ -111468,8 +110461,6 @@ entities: rot: -1.5707963267948966 rad pos: 37.5,1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13561 @@ -111478,8 +110469,6 @@ entities: rot: 3.141592653589793 rad pos: 38.5,-10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13576 @@ -111488,8 +110477,6 @@ entities: rot: -1.5707963267948966 rad pos: 51.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13577 @@ -111497,8 +110484,6 @@ entities: - type: Transform pos: 47.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13578 @@ -111506,8 +110491,6 @@ entities: - type: Transform pos: 42.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13579 @@ -111515,8 +110498,6 @@ entities: - type: Transform pos: 37.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13580 @@ -111525,8 +110506,6 @@ entities: rot: -1.5707963267948966 rad pos: 26.5,-8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13586 @@ -111535,8 +110514,6 @@ entities: rot: -1.5707963267948966 rad pos: 45.5,-16.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13587 @@ -111545,8 +110522,6 @@ entities: rot: -1.5707963267948966 rad pos: 45.5,-12.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13588 @@ -111555,8 +110530,6 @@ entities: rot: -1.5707963267948966 rad pos: 50.5,-20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13589 @@ -111565,8 +110538,6 @@ entities: rot: 3.141592653589793 rad pos: 37.5,-21.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13595 @@ -111575,8 +110546,6 @@ entities: rot: 1.5707963267948966 rad pos: 29.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13596 @@ -111584,8 +110553,6 @@ entities: - type: Transform pos: 30.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13635 @@ -111594,8 +110561,6 @@ entities: rot: 3.141592653589793 rad pos: 47.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13636 @@ -111604,8 +110569,6 @@ entities: rot: 3.141592653589793 rad pos: 42.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13637 @@ -111614,8 +110577,6 @@ entities: rot: 3.141592653589793 rad pos: 39.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13638 @@ -111624,8 +110585,6 @@ entities: rot: 3.141592653589793 rad pos: 35.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13639 @@ -111633,8 +110592,6 @@ entities: - type: Transform pos: 37.5,-17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13675 @@ -111643,8 +110600,6 @@ entities: rot: 3.141592653589793 rad pos: 37.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13783 @@ -111653,8 +110608,6 @@ entities: rot: 3.141592653589793 rad pos: 33.5,-22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13866 @@ -111663,8 +110616,6 @@ entities: rot: 1.5707963267948966 rad pos: 5.5,-38.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13867 @@ -111673,8 +110624,6 @@ entities: rot: -1.5707963267948966 rad pos: 17.5,-38.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13868 @@ -111683,8 +110632,6 @@ entities: rot: 1.5707963267948966 rad pos: 14.5,-30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13870 @@ -111692,8 +110639,6 @@ entities: - type: Transform pos: 17.5,-22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13871 @@ -111701,8 +110646,6 @@ entities: - type: Transform pos: 19.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13872 @@ -111710,8 +110653,6 @@ entities: - type: Transform pos: 24.5,-22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13873 @@ -111720,8 +110661,6 @@ entities: rot: -1.5707963267948966 rad pos: 25.5,-30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13874 @@ -111730,8 +110669,6 @@ entities: rot: 3.141592653589793 rad pos: 22.5,-33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 13875 @@ -111740,8 +110677,6 @@ entities: rot: 3.141592653589793 rad pos: 24.5,-35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14021 @@ -111753,8 +110688,6 @@ entities: - type: DeviceNetwork deviceLists: - 7456 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14022 @@ -111762,8 +110695,6 @@ entities: - type: Transform pos: 75.5,-20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14031 @@ -111772,8 +110703,6 @@ entities: rot: 3.141592653589793 rad pos: 54.5,-29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14032 @@ -111781,8 +110710,6 @@ entities: - type: Transform pos: 54.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14041 @@ -111791,8 +110718,6 @@ entities: rot: 3.141592653589793 rad pos: 47.5,-32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14074 @@ -111801,8 +110726,6 @@ entities: rot: -1.5707963267948966 rad pos: 58.5,3.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14088 @@ -111811,8 +110734,6 @@ entities: rot: 3.141592653589793 rad pos: 57.5,-10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14131 @@ -111821,8 +110742,6 @@ entities: rot: -1.5707963267948966 rad pos: 58.5,-20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14277 @@ -111831,8 +110750,6 @@ entities: rot: -1.5707963267948966 rad pos: 30.5,-37.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14278 @@ -111841,8 +110758,6 @@ entities: rot: -1.5707963267948966 rad pos: 41.5,-33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14279 @@ -111851,8 +110766,6 @@ entities: rot: 3.141592653589793 rad pos: 40.5,-39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14316 @@ -111861,8 +110774,6 @@ entities: rot: -1.5707963267948966 rad pos: 74.5,-46.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14324 @@ -111870,16 +110781,12 @@ entities: - type: Transform pos: 65.5,-41.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 14364 components: - type: Transform rot: -1.5707963267948966 rad pos: 32.5,-50.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14365 @@ -111888,8 +110795,6 @@ entities: rot: 3.141592653589793 rad pos: 30.5,-61.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14393 @@ -111897,8 +110802,6 @@ entities: - type: Transform pos: 35.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14394 @@ -111907,8 +110810,6 @@ entities: rot: 3.141592653589793 rad pos: 40.5,-51.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14395 @@ -111916,8 +110817,6 @@ entities: - type: Transform pos: 53.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14396 @@ -111925,8 +110824,6 @@ entities: - type: Transform pos: 58.5,-40.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14398 @@ -111934,8 +110831,6 @@ entities: - type: Transform pos: 69.5,-41.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14473 @@ -111943,8 +110838,6 @@ entities: - type: Transform pos: -12.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14474 @@ -111953,8 +110846,6 @@ entities: rot: 3.141592653589793 rad pos: -12.5,-49.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14515 @@ -111963,8 +110854,6 @@ entities: rot: 3.141592653589793 rad pos: 25.5,-43.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14524 @@ -111973,8 +110862,6 @@ entities: rot: 3.141592653589793 rad pos: 24.5,-54.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14525 @@ -111983,8 +110870,6 @@ entities: rot: -1.5707963267948966 rad pos: 26.5,-50.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14526 @@ -111992,8 +110877,6 @@ entities: - type: Transform pos: 21.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14559 @@ -112001,8 +110884,6 @@ entities: - type: Transform pos: -9.5,-83.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14626 @@ -112011,24 +110892,18 @@ entities: rot: 3.141592653589793 rad pos: -9.5,-86.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 14627 components: - type: Transform rot: 3.141592653589793 rad pos: -5.5,-86.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 14629 components: - type: Transform rot: 3.141592653589793 rad pos: -1.5,-78.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14630 @@ -112037,8 +110912,6 @@ entities: rot: 3.141592653589793 rad pos: 2.5,-82.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14631 @@ -112047,8 +110920,6 @@ entities: rot: 3.141592653589793 rad pos: 4.5,-80.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14633 @@ -112057,8 +110928,6 @@ entities: rot: 3.141592653589793 rad pos: 7.5,-73.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14634 @@ -112066,8 +110935,6 @@ entities: - type: Transform pos: 10.5,-62.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14635 @@ -112076,8 +110943,6 @@ entities: rot: 3.141592653589793 rad pos: 21.5,-66.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14643 @@ -112086,8 +110951,6 @@ entities: rot: 3.141592653589793 rad pos: 24.5,-67.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14647 @@ -112096,8 +110959,6 @@ entities: rot: 3.141592653589793 rad pos: 17.5,-74.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14680 @@ -112106,8 +110967,6 @@ entities: rot: -1.5707963267948966 rad pos: 37.5,-88.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14711 @@ -112116,8 +110975,6 @@ entities: rot: -1.5707963267948966 rad pos: 27.5,-77.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14712 @@ -112126,8 +110983,6 @@ entities: rot: 1.5707963267948966 rad pos: 24.5,-85.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14713 @@ -112136,8 +110991,6 @@ entities: rot: -1.5707963267948966 rad pos: 29.5,-84.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14761 @@ -112145,8 +110998,6 @@ entities: - type: Transform pos: -10.5,-42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14775 @@ -112154,8 +111005,6 @@ entities: - type: Transform pos: -4.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14805 @@ -112163,8 +111012,6 @@ entities: - type: Transform pos: 4.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14898 @@ -112173,8 +111020,6 @@ entities: rot: 1.5707963267948966 rad pos: 8.5,-68.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14904 @@ -112183,8 +111028,6 @@ entities: rot: 1.5707963267948966 rad pos: 19.5,-57.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14905 @@ -112193,8 +111036,6 @@ entities: rot: 1.5707963267948966 rad pos: 19.5,-50.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14908 @@ -112203,8 +111044,6 @@ entities: rot: -1.5707963267948966 rad pos: 20.5,-69.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14915 @@ -112213,8 +111052,6 @@ entities: rot: 1.5707963267948966 rad pos: -4.5,-72.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 14917 @@ -112223,8 +111060,6 @@ entities: rot: -1.5707963267948966 rad pos: 17.5,-67.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15133 @@ -112233,8 +111068,6 @@ entities: rot: -1.5707963267948966 rad pos: 15.5,-55.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15134 @@ -112243,8 +111076,6 @@ entities: rot: 3.141592653589793 rad pos: 9.5,-56.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15142 @@ -112253,8 +111084,6 @@ entities: rot: 3.141592653589793 rad pos: 8.5,-59.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15143 @@ -112263,8 +111092,6 @@ entities: rot: 3.141592653589793 rad pos: 13.5,-58.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15161 @@ -112273,8 +111100,6 @@ entities: rot: -1.5707963267948966 rad pos: 5.5,-56.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15163 @@ -112283,8 +111108,6 @@ entities: rot: 1.5707963267948966 rad pos: -0.5,-57.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15168 @@ -112293,8 +111116,6 @@ entities: rot: 3.141592653589793 rad pos: 4.5,-60.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15207 @@ -112303,8 +111124,6 @@ entities: rot: -1.5707963267948966 rad pos: 9.5,-41.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15210 @@ -112313,8 +111132,6 @@ entities: rot: 1.5707963267948966 rad pos: -0.5,-43.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15266 @@ -112322,8 +111139,6 @@ entities: - type: Transform pos: 3.5,-65.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15279 @@ -112331,8 +111146,6 @@ entities: - type: Transform pos: -6.5,-49.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15304 @@ -112344,8 +111157,6 @@ entities: - type: DeviceNetwork deviceLists: - 22089 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15313 @@ -112353,8 +111164,6 @@ entities: - type: Transform pos: -27.5,-46.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15317 @@ -112363,8 +111172,6 @@ entities: rot: 1.5707963267948966 rad pos: -33.5,-46.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15320 @@ -112372,8 +111179,6 @@ entities: - type: Transform pos: -29.5,-45.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15330 @@ -112382,8 +111187,6 @@ entities: rot: 1.5707963267948966 rad pos: -32.5,-51.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15338 @@ -112392,8 +111195,6 @@ entities: rot: 3.141592653589793 rad pos: -29.5,-58.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15339 @@ -112402,8 +111203,6 @@ entities: rot: -1.5707963267948966 rad pos: -27.5,-57.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15340 @@ -112414,8 +111213,6 @@ entities: - type: DeviceNetwork deviceLists: - 22089 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15341 @@ -112426,8 +111223,6 @@ entities: - type: DeviceNetwork deviceLists: - 22089 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15358 @@ -112439,8 +111234,6 @@ entities: - type: DeviceNetwork deviceLists: - 22239 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15359 @@ -112452,8 +111245,6 @@ entities: - type: DeviceNetwork deviceLists: - 22239 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15716 @@ -112461,8 +111252,6 @@ entities: - type: Transform pos: 19.5,-1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 15810 @@ -112471,8 +111260,6 @@ entities: rot: 3.141592653589793 rad pos: 9.5,-10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16024 @@ -112481,8 +111268,6 @@ entities: rot: -1.5707963267948966 rad pos: 2.5,-37.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16144 @@ -112491,8 +111276,6 @@ entities: rot: 3.141592653589793 rad pos: 62.5,-52.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16468 @@ -112501,8 +111284,6 @@ entities: rot: 3.141592653589793 rad pos: -20.5,-46.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16535 @@ -112510,8 +111291,6 @@ entities: - type: Transform pos: -56.5,-39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16536 @@ -112520,8 +111299,6 @@ entities: rot: -1.5707963267948966 rad pos: -45.5,-39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16537 @@ -112530,8 +111307,6 @@ entities: rot: 1.5707963267948966 rad pos: -47.5,-37.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16654 @@ -112539,8 +111314,6 @@ entities: - type: Transform pos: -8.5,-56.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16655 @@ -112549,8 +111322,6 @@ entities: rot: 1.5707963267948966 rad pos: -11.5,-62.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16656 @@ -112559,8 +111330,6 @@ entities: rot: 3.141592653589793 rad pos: -10.5,-65.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16657 @@ -112569,8 +111338,6 @@ entities: rot: 3.141592653589793 rad pos: -6.5,-65.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 16897 @@ -112579,8 +111346,6 @@ entities: rot: 3.141592653589793 rad pos: 53.5,-43.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 17074 @@ -112589,8 +111354,6 @@ entities: rot: 1.5707963267948966 rad pos: 46.5,-42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 17075 @@ -112599,8 +111362,6 @@ entities: rot: -1.5707963267948966 rad pos: 49.5,-43.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 17363 @@ -112609,16 +111370,12 @@ entities: rot: 3.141592653589793 rad pos: 48.5,-52.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 22267 components: - type: Transform rot: -1.5707963267948966 rad pos: 6.5,-14.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 22283 @@ -112627,8 +111384,6 @@ entities: rot: 1.5707963267948966 rad pos: 15.5,6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 22298 @@ -112637,8 +111392,6 @@ entities: rot: -1.5707963267948966 rad pos: 22.5,-15.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 22299 @@ -112646,8 +111399,6 @@ entities: - type: Transform pos: 21.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 22300 @@ -112656,8 +111407,6 @@ entities: rot: 3.141592653589793 rad pos: 21.5,-18.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 22323 @@ -112665,8 +111414,6 @@ entities: - type: Transform pos: 17.5,-7.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 22325 @@ -112675,8 +111422,6 @@ entities: rot: 1.5707963267948966 rad pos: 32.5,-15.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 22440 @@ -112684,8 +111429,6 @@ entities: - type: Transform pos: 10.5,-21.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 22571 @@ -112694,8 +111437,6 @@ entities: rot: 1.5707963267948966 rad pos: 6.5,29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 22745 @@ -112704,8 +111445,6 @@ entities: rot: 3.141592653589793 rad pos: 48.5,8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 23659 @@ -112714,8 +111453,6 @@ entities: rot: 3.141592653589793 rad pos: 3.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 23685 @@ -112724,8 +111461,6 @@ entities: rot: 3.141592653589793 rad pos: -1.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 23686 @@ -112734,8 +111469,6 @@ entities: rot: 1.5707963267948966 rad pos: -2.5,-1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 23751 @@ -112744,8 +111477,6 @@ entities: rot: 3.141592653589793 rad pos: -12.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24029 @@ -112754,8 +111485,6 @@ entities: rot: 3.141592653589793 rad pos: 35.5,-94.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24030 @@ -112764,8 +111493,6 @@ entities: rot: 3.141592653589793 rad pos: 26.5,-95.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24031 @@ -112773,8 +111500,6 @@ entities: - type: Transform pos: 26.5,-90.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24083 @@ -112783,8 +111508,6 @@ entities: rot: 3.141592653589793 rad pos: -18.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24085 @@ -112793,8 +111516,6 @@ entities: rot: 3.141592653589793 rad pos: -20.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24086 @@ -112803,8 +111524,6 @@ entities: rot: 1.5707963267948966 rad pos: -25.5,-10.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24134 @@ -112813,8 +111532,6 @@ entities: rot: 1.5707963267948966 rad pos: -5.5,-2.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24135 @@ -112823,8 +111540,6 @@ entities: rot: 3.141592653589793 rad pos: -4.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24138 @@ -112833,8 +111548,6 @@ entities: rot: 1.5707963267948966 rad pos: -6.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24144 @@ -112842,8 +111555,6 @@ entities: - type: Transform pos: -4.5,2.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24174 @@ -112852,8 +111563,6 @@ entities: rot: 3.141592653589793 rad pos: -16.5,-2.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24185 @@ -112862,8 +111571,6 @@ entities: rot: 1.5707963267948966 rad pos: -12.5,-3.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24196 @@ -112872,8 +111579,6 @@ entities: rot: 3.141592653589793 rad pos: -19.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24199 @@ -112882,8 +111587,6 @@ entities: rot: 3.141592653589793 rad pos: -23.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24287 @@ -112892,8 +111595,6 @@ entities: rot: -1.5707963267948966 rad pos: -39.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24288 @@ -112902,8 +111603,6 @@ entities: rot: 1.5707963267948966 rad pos: -41.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24289 @@ -112911,8 +111610,6 @@ entities: - type: Transform pos: -40.5,-4.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24295 @@ -112921,8 +111618,6 @@ entities: rot: 1.5707963267948966 rad pos: -32.5,-12.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24296 @@ -112931,8 +111626,6 @@ entities: rot: 1.5707963267948966 rad pos: -32.5,-8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24297 @@ -112940,8 +111633,6 @@ entities: - type: Transform pos: -31.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24348 @@ -112950,8 +111641,6 @@ entities: rot: -1.5707963267948966 rad pos: -30.5,9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24349 @@ -112960,8 +111649,6 @@ entities: rot: -1.5707963267948966 rad pos: -30.5,6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24350 @@ -112970,8 +111657,6 @@ entities: rot: -1.5707963267948966 rad pos: -29.5,1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24351 @@ -112979,8 +111664,6 @@ entities: - type: Transform pos: -36.5,7.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24383 @@ -112988,8 +111671,6 @@ entities: - type: Transform pos: -36.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24399 @@ -112997,8 +111678,6 @@ entities: - type: Transform pos: -48.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24400 @@ -113006,8 +111685,6 @@ entities: - type: Transform pos: -46.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24401 @@ -113015,8 +111692,6 @@ entities: - type: Transform pos: -44.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24402 @@ -113024,8 +111699,6 @@ entities: - type: Transform pos: -41.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24417 @@ -113034,8 +111707,6 @@ entities: rot: -1.5707963267948966 rad pos: -42.5,7.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24418 @@ -113043,8 +111714,6 @@ entities: - type: Transform pos: -44.5,8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24420 @@ -113053,8 +111722,6 @@ entities: rot: 1.5707963267948966 rad pos: -45.5,5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24450 @@ -113062,8 +111729,6 @@ entities: - type: Transform pos: -36.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24478 @@ -113072,8 +111737,6 @@ entities: rot: -1.5707963267948966 rad pos: -13.5,-39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24697 @@ -113082,8 +111745,6 @@ entities: rot: 3.141592653589793 rad pos: -71.5,0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24698 @@ -113092,8 +111753,6 @@ entities: rot: 3.141592653589793 rad pos: -67.5,0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24699 @@ -113102,8 +111761,6 @@ entities: rot: 1.5707963267948966 rad pos: -66.5,-2.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24700 @@ -113112,8 +111769,6 @@ entities: rot: -1.5707963267948966 rad pos: -63.5,-2.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24701 @@ -113122,8 +111777,6 @@ entities: rot: 3.141592653589793 rad pos: -64.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24702 @@ -113132,8 +111785,6 @@ entities: rot: 3.141592653589793 rad pos: -60.5,-0.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24703 @@ -113142,8 +111793,6 @@ entities: rot: 1.5707963267948966 rad pos: -66.5,5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24704 @@ -113151,8 +111800,6 @@ entities: - type: Transform pos: -64.5,12.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24828 @@ -113161,8 +111808,6 @@ entities: rot: -1.5707963267948966 rad pos: -34.5,-22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24829 @@ -113170,8 +111815,6 @@ entities: - type: Transform pos: -36.5,-17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24836 @@ -113180,8 +111823,6 @@ entities: rot: -1.5707963267948966 rad pos: -37.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24838 @@ -113189,8 +111830,6 @@ entities: - type: Transform pos: -39.5,-26.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24839 @@ -113199,8 +111838,6 @@ entities: rot: 1.5707963267948966 rad pos: -43.5,-29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24880 @@ -113209,8 +111846,6 @@ entities: rot: 3.141592653589793 rad pos: -38.5,-38.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24907 @@ -113219,8 +111854,6 @@ entities: rot: -1.5707963267948966 rad pos: -25.5,-37.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24908 @@ -113229,8 +111862,6 @@ entities: rot: 3.141592653589793 rad pos: -27.5,-39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24909 @@ -113239,8 +111870,6 @@ entities: rot: 3.141592653589793 rad pos: -31.5,-39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24966 @@ -113249,8 +111878,6 @@ entities: rot: -1.5707963267948966 rad pos: -1.5,-32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24967 @@ -113258,8 +111885,6 @@ entities: - type: Transform pos: -4.5,-31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24970 @@ -113268,8 +111893,6 @@ entities: rot: -1.5707963267948966 rad pos: 3.5,-26.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24988 @@ -113278,8 +111901,6 @@ entities: rot: 3.141592653589793 rad pos: -1.5,-27.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 24991 @@ -113288,8 +111909,6 @@ entities: rot: 3.141592653589793 rad pos: -18.5,-35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25002 @@ -113297,8 +111916,6 @@ entities: - type: Transform pos: -18.5,-30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25017 @@ -113307,8 +111924,6 @@ entities: rot: 3.141592653589793 rad pos: -23.5,-29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25018 @@ -113317,8 +111932,6 @@ entities: rot: 1.5707963267948966 rad pos: -29.5,-30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25019 @@ -113327,8 +111940,6 @@ entities: rot: 1.5707963267948966 rad pos: -29.5,-27.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25020 @@ -113336,8 +111947,6 @@ entities: - type: Transform pos: -23.5,-18.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25028 @@ -113345,8 +111954,6 @@ entities: - type: Transform pos: -12.5,-25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25076 @@ -113354,8 +111961,6 @@ entities: - type: Transform pos: -0.5,-22.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25135 @@ -113363,8 +111968,6 @@ entities: - type: Transform pos: -27.5,-25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25147 @@ -113372,8 +111975,6 @@ entities: - type: Transform pos: -1.5,-17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25148 @@ -113381,8 +111982,6 @@ entities: - type: Transform pos: -10.5,-14.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25184 @@ -113390,8 +111989,6 @@ entities: - type: Transform pos: -18.5,-19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25425 @@ -113400,8 +111997,6 @@ entities: rot: 1.5707963267948966 rad pos: -53.5,-9.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25426 @@ -113410,8 +112005,6 @@ entities: rot: 3.141592653589793 rad pos: -52.5,-12.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 25608 @@ -113419,8 +112012,6 @@ entities: - type: Transform pos: 1.5,-70.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26049 @@ -113429,8 +112020,6 @@ entities: rot: 1.5707963267948966 rad pos: 61.5,26.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26051 @@ -113439,8 +112028,6 @@ entities: rot: 3.141592653589793 rad pos: 60.5,18.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26130 @@ -113449,8 +112036,6 @@ entities: rot: 1.5707963267948966 rad pos: 68.5,7.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26131 @@ -113459,8 +112044,6 @@ entities: rot: 3.141592653589793 rad pos: 73.5,6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26136 @@ -113469,8 +112052,6 @@ entities: rot: 3.141592653589793 rad pos: 66.5,17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26143 @@ -113479,8 +112060,6 @@ entities: rot: 3.141592653589793 rad pos: 63.5,11.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26145 @@ -113489,8 +112068,6 @@ entities: rot: -1.5707963267948966 rad pos: 45.5,17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26199 @@ -113499,8 +112076,6 @@ entities: rot: 1.5707963267948966 rad pos: 11.5,20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26226 @@ -113508,8 +112083,6 @@ entities: - type: Transform pos: 61.5,8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26227 @@ -113517,8 +112090,6 @@ entities: - type: Transform pos: 54.5,8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26228 @@ -113527,8 +112098,6 @@ entities: rot: 3.141592653589793 rad pos: 51.5,6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26254 @@ -113537,8 +112106,6 @@ entities: rot: 1.5707963267948966 rad pos: 18.5,14.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26275 @@ -113547,8 +112114,6 @@ entities: rot: -1.5707963267948966 rad pos: 33.5,17.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26308 @@ -113557,8 +112122,6 @@ entities: rot: -1.5707963267948966 rad pos: 23.5,19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26316 @@ -113566,8 +112129,6 @@ entities: - type: Transform pos: 22.5,27.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26318 @@ -113575,8 +112136,6 @@ entities: - type: Transform pos: 19.5,25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26329 @@ -113585,8 +112144,6 @@ entities: rot: 3.141592653589793 rad pos: 7.5,24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26336 @@ -113595,8 +112152,6 @@ entities: rot: 3.141592653589793 rad pos: 4.5,24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26824 @@ -113605,8 +112160,6 @@ entities: rot: -1.5707963267948966 rad pos: -17.5,4.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26825 @@ -113615,8 +112168,6 @@ entities: rot: 1.5707963267948966 rad pos: -25.5,2.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26826 @@ -113625,8 +112176,6 @@ entities: rot: 1.5707963267948966 rad pos: -25.5,5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26827 @@ -113635,8 +112184,6 @@ entities: rot: 1.5707963267948966 rad pos: -25.5,8.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 26916 @@ -113645,8 +112192,6 @@ entities: rot: 3.141592653589793 rad pos: 12.5,-24.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - uid: 27396 @@ -113658,8 +112203,6 @@ entities: - type: DeviceNetwork deviceLists: - 7456 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF1212FF' - proto: GasVolumePump @@ -113670,8 +112213,6 @@ entities: rot: 1.5707963267948966 rad pos: 63.5,29.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF5500FF' - uid: 13366 @@ -113680,8 +112221,6 @@ entities: rot: 3.141592653589793 rad pos: 62.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - proto: Gauze @@ -114125,6 +112664,12 @@ entities: - type: Transform pos: 69.5,6.5 parent: 6747 + - uid: 880 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 84.5,-34.5 + parent: 6747 - uid: 932 components: - type: Transform @@ -114315,6 +112860,12 @@ entities: - type: Transform pos: 8.5,26.5 parent: 6747 + - uid: 1092 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 84.5,-37.5 + parent: 6747 - uid: 1105 components: - type: Transform @@ -115570,6 +114121,12 @@ entities: - type: Transform pos: -5.5,-72.5 parent: 6747 + - uid: 4508 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 85.5,-34.5 + parent: 6747 - uid: 4525 components: - type: Transform @@ -115970,11 +114527,6 @@ entities: - type: Transform pos: 39.5,-48.5 parent: 6747 - - uid: 5968 - components: - - type: Transform - pos: 77.5,-18.5 - parent: 6747 - uid: 5969 components: - type: Transform @@ -115995,25 +114547,17 @@ entities: - type: Transform pos: 76.5,-31.5 parent: 6747 - - uid: 5978 - components: - - type: Transform - pos: 76.5,-30.5 - parent: 6747 - - uid: 5979 - components: - - type: Transform - pos: 77.5,-20.5 - parent: 6747 - uid: 5994 components: - type: Transform - pos: 77.5,-23.5 + rot: -1.5707963267948966 rad + pos: 85.5,-37.5 parent: 6747 - - uid: 5996 + - uid: 6002 components: - type: Transform - pos: 77.5,-25.5 + rot: -1.5707963267948966 rad + pos: 82.5,-34.5 parent: 6747 - uid: 6006 components: @@ -116108,7 +114652,8 @@ entities: - uid: 6072 components: - type: Transform - pos: 76.5,-29.5 + rot: -1.5707963267948966 rad + pos: 82.5,-37.5 parent: 6747 - uid: 6103 components: @@ -119680,6 +118225,294 @@ entities: - type: Transform pos: -19.5,-56.5 parent: 6747 + - uid: 27642 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-23.5 + parent: 6747 + - uid: 27646 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 87.5,-37.5 + parent: 6747 + - uid: 27647 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 88.5,-37.5 + parent: 6747 + - uid: 27648 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-35.5 + parent: 6747 + - uid: 27649 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-34.5 + parent: 6747 + - uid: 27650 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-32.5 + parent: 6747 + - uid: 27651 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-31.5 + parent: 6747 + - uid: 27652 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-29.5 + parent: 6747 + - uid: 27653 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-28.5 + parent: 6747 + - uid: 27654 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-26.5 + parent: 6747 + - uid: 27655 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-25.5 + parent: 6747 + - uid: 27656 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-22.5 + parent: 6747 + - uid: 27657 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-20.5 + parent: 6747 + - uid: 27658 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-19.5 + parent: 6747 + - uid: 27659 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 88.5,-17.5 + parent: 6747 + - uid: 27660 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 87.5,-17.5 + parent: 6747 + - uid: 27667 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 76.5,-23.5 + parent: 6747 + - uid: 27668 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 76.5,-20.5 + parent: 6747 + - uid: 27669 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-20.5 + parent: 6747 + - uid: 27670 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-21.5 + parent: 6747 + - uid: 27671 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-22.5 + parent: 6747 + - uid: 27672 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-23.5 + parent: 6747 + - uid: 27680 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-32.5 + parent: 6747 + - uid: 27681 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-31.5 + parent: 6747 + - uid: 27682 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-28.5 + parent: 6747 + - uid: 27683 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-27.5 + parent: 6747 + - uid: 27746 + components: + - type: Transform + pos: 98.5,-25.5 + parent: 6747 + - uid: 27747 + components: + - type: Transform + pos: 98.5,-26.5 + parent: 6747 + - uid: 27748 + components: + - type: Transform + pos: 98.5,-27.5 + parent: 6747 + - uid: 27749 + components: + - type: Transform + pos: 98.5,-28.5 + parent: 6747 + - uid: 27750 + components: + - type: Transform + pos: 98.5,-30.5 + parent: 6747 + - uid: 27751 + components: + - type: Transform + pos: 98.5,-31.5 + parent: 6747 + - uid: 27752 + components: + - type: Transform + pos: 98.5,-32.5 + parent: 6747 + - uid: 27753 + components: + - type: Transform + pos: 98.5,-33.5 + parent: 6747 + - uid: 27754 + components: + - type: Transform + pos: 98.5,-35.5 + parent: 6747 + - uid: 27755 + components: + - type: Transform + pos: 98.5,-36.5 + parent: 6747 + - uid: 27756 + components: + - type: Transform + pos: 98.5,-37.5 + parent: 6747 + - uid: 27757 + components: + - type: Transform + pos: 98.5,-38.5 + parent: 6747 + - uid: 27792 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,-39.5 + parent: 6747 + - uid: 27793 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 97.5,-40.5 + parent: 6747 + - uid: 27794 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 96.5,-40.5 + parent: 6747 + - uid: 27795 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 95.5,-41.5 + parent: 6747 + - uid: 27796 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 94.5,-41.5 + parent: 6747 + - uid: 27797 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 93.5,-42.5 + parent: 6747 + - uid: 27798 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 92.5,-42.5 + parent: 6747 + - uid: 27799 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 91.5,-41.5 + parent: 6747 + - uid: 27800 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 90.5,-41.5 + parent: 6747 + - uid: 27801 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 89.5,-40.5 + parent: 6747 + - uid: 27802 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 89.5,-39.5 + parent: 6747 + - uid: 27803 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 89.5,-38.5 + parent: 6747 - proto: GrilleBroken entities: - uid: 9970 @@ -120250,8 +119083,6 @@ entities: rot: 1.5707963267948966 rad pos: 66.5,30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF5500FF' - uid: 13089 @@ -120260,8 +119091,6 @@ entities: rot: 1.5707963267948966 rad pos: 67.5,31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF5500FF' - uid: 13090 @@ -120270,8 +119099,6 @@ entities: rot: 1.5707963267948966 rad pos: 67.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF5500FF' - uid: 13091 @@ -120280,8 +119107,6 @@ entities: rot: 1.5707963267948966 rad pos: 66.5,33.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#FF5500FF' - uid: 13318 @@ -120290,8 +119115,6 @@ entities: rot: -1.5707963267948966 rad pos: 62.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 13319 @@ -120300,8 +119123,6 @@ entities: rot: -1.5707963267948966 rad pos: 62.5,40.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - uid: 13320 @@ -120310,8 +119131,6 @@ entities: rot: -1.5707963267948966 rad pos: 62.5,39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - type: AtmosPipeColor color: '#00FFFFFF' - proto: HelicopterInstrument @@ -121052,7 +119871,6 @@ entities: solutions: beaker: temperature: 293.15 - canMix: True canReact: True maxVol: 200 name: null @@ -121334,7 +120152,6 @@ entities: solutions: tank: temperature: 293.15 - canMix: False canReact: True maxVol: 1500 name: null @@ -121509,7 +120326,6 @@ entities: solutions: beaker: temperature: 293.15 - canMix: True canReact: True maxVol: 100 name: null @@ -125209,78 +124025,56 @@ entities: - type: Transform pos: 8.5,-73.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 5108 components: - type: Transform pos: 30.5,-74.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 5315 components: - type: Transform pos: 49.5,-32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7756 components: - type: Transform pos: 38.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7878 components: - type: Transform pos: -35.5,-57.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8183 components: - type: Transform pos: 33.5,34.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 10992 components: - type: Transform pos: 50.5,-39.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11190 components: - type: Transform pos: 41.5,-80.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11353 components: - type: Transform pos: 52.5,-1.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 15935 components: - type: Transform pos: 8.5,-6.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25414 components: - type: Transform pos: -51.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: NitrogenTankFilled entities: - uid: 1301 @@ -125344,15 +124138,11 @@ entities: - type: Transform pos: 42.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8189 components: - type: Transform pos: 32.5,30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: NitrousOxideTank entities: - uid: 7877 @@ -125440,7 +124230,7 @@ entities: - type: Transform pos: 3.5,-69.5 parent: 6747 -- proto: Oracle +- proto: OracleSpawner entities: - uid: 8917 components: @@ -125491,92 +124281,66 @@ entities: - type: Transform pos: 0.5,12.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 5069 components: - type: Transform pos: 9.5,-73.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 5093 components: - type: Transform pos: 31.5,-74.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 5314 components: - type: Transform pos: 49.5,-31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7786 components: - type: Transform pos: 36.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8180 components: - type: Transform pos: 32.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8181 components: - type: Transform pos: 33.5,35.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 10130 components: - type: Transform pos: -49.5,-69.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11340 components: - type: Transform pos: 4.5,4.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11356 components: - type: Transform pos: 53.5,-15.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11378 components: - type: Transform pos: 43.5,-37.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11409 components: - type: Transform pos: -35.5,-58.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 25415 components: - type: Transform pos: -52.5,-13.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: OxygenTankFilled entities: - uid: 11534 @@ -130299,7 +129063,6 @@ entities: solutions: food: temperature: 293.15 - canMix: False canReact: True maxVol: 25 name: null @@ -130317,7 +129080,6 @@ entities: solutions: food: temperature: 293.15 - canMix: False canReact: True maxVol: 25 name: null @@ -130335,7 +129097,6 @@ entities: solutions: food: temperature: 293.15 - canMix: False canReact: True maxVol: 25 name: null @@ -130353,7 +129114,6 @@ entities: solutions: food: temperature: 293.15 - canMix: False canReact: True maxVol: 25 name: null @@ -130371,7 +129131,6 @@ entities: solutions: food: temperature: 293.15 - canMix: False canReact: True maxVol: 25 name: null @@ -130467,8 +129226,6 @@ entities: - type: Transform pos: 59.5,25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: PlasmaOre1 entities: - uid: 25553 @@ -134084,32 +132841,32 @@ entities: rot: 1.5707963267948966 rad pos: 57.5,-34.5 parent: 6747 -- proto: PoweredLightBlueInterior - entities: - - uid: 2197 + - uid: 27673 components: - type: Transform rot: 1.5707963267948966 rad - pos: 77.5,-33.5 + pos: 87.5,-23.5 parent: 6747 - - type: ApcPowerReceiver - powerLoad: 0 - - uid: 4218 + - uid: 27789 components: - type: Transform rot: 1.5707963267948966 rad - pos: 77.5,-17.5 + pos: 87.5,-31.5 parent: 6747 - - type: ApcPowerReceiver - powerLoad: 0 - - uid: 4508 + - uid: 27790 components: - type: Transform - rot: 3.141592653589793 rad - pos: 76.5,-16.5 + rot: 1.5707963267948966 rad + pos: 90.5,-33.5 parent: 6747 - - type: ApcPowerReceiver - powerLoad: 0 + - uid: 27791 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 90.5,-21.5 + parent: 6747 +- proto: PoweredLightBlueInterior + entities: - uid: 4509 components: - type: Transform @@ -134259,14 +133016,6 @@ entities: parent: 6747 - type: ApcPowerReceiver powerLoad: 0 - - uid: 11804 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 77.5,-26.5 - parent: 6747 - - type: ApcPowerReceiver - powerLoad: 0 - uid: 11805 components: - type: Transform @@ -134483,6 +133232,23 @@ entities: rot: 1.5707963267948966 rad pos: -77.5,3.5 parent: 6747 + - uid: 27684 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 79.5,-33.5 + parent: 6747 + - uid: 27685 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 83.5,-33.5 + parent: 6747 + - uid: 27686 + components: + - type: Transform + pos: 83.5,-38.5 + parent: 6747 - proto: PoweredLightColoredBlack entities: - uid: 6279 @@ -135753,7 +134519,6 @@ entities: solutions: puddle: temperature: 293.15 - canMix: False canReact: True maxVol: 1000 name: null @@ -135772,7 +134537,6 @@ entities: solutions: puddle: temperature: 293.15 - canMix: False canReact: True maxVol: 1000 name: null @@ -135801,7 +134565,6 @@ entities: solutions: puddle: temperature: 293.15 - canMix: False canReact: True maxVol: 1000 name: null @@ -135822,7 +134585,6 @@ entities: solutions: puddle: temperature: 293.15 - canMix: False canReact: True maxVol: 1000 name: null @@ -137220,6 +135982,123 @@ entities: - type: Transform pos: 79.5,16.5 parent: 6747 + - uid: 27690 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 76.5,-16.5 + parent: 6747 + - uid: 27691 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 76.5,-15.5 + parent: 6747 + - uid: 27698 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-16.5 + parent: 6747 + - uid: 27699 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-15.5 + parent: 6747 + - uid: 27700 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 84.5,-12.5 + parent: 6747 + - uid: 27701 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 78.5,-12.5 + parent: 6747 + - uid: 27702 + components: + - type: Transform + pos: 80.5,-10.5 + parent: 6747 + - uid: 27703 + components: + - type: Transform + pos: 81.5,-10.5 + parent: 6747 + - uid: 27704 + components: + - type: Transform + pos: 82.5,-10.5 + parent: 6747 + - uid: 27715 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 78.5,-30.5 + parent: 6747 + - uid: 27716 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 79.5,-30.5 + parent: 6747 + - uid: 27717 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 80.5,-30.5 + parent: 6747 + - uid: 27718 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 82.5,-30.5 + parent: 6747 + - uid: 27719 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 81.5,-30.5 + parent: 6747 + - uid: 27720 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 83.5,-30.5 + parent: 6747 + - uid: 27721 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 84.5,-30.5 + parent: 6747 + - uid: 27808 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 93.5,-29.5 + parent: 6747 + - uid: 27811 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 92.5,-30.5 + parent: 6747 + - uid: 27815 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 92.5,-29.5 + parent: 6747 + - uid: 27817 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 92.5,-28.5 + parent: 6747 - proto: RailingCorner entities: - uid: 379 @@ -137334,6 +136213,51 @@ entities: - type: Transform pos: 80.5,16.5 parent: 6747 + - uid: 27692 + components: + - type: Transform + pos: 78.5,-13.5 + parent: 6747 + - uid: 27693 + components: + - type: Transform + pos: 77.5,-14.5 + parent: 6747 + - uid: 27694 + components: + - type: Transform + pos: 79.5,-11.5 + parent: 6747 + - uid: 27695 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 83.5,-11.5 + parent: 6747 + - uid: 27696 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 84.5,-13.5 + parent: 6747 + - uid: 27697 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 85.5,-14.5 + parent: 6747 + - uid: 27713 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 77.5,-29.5 + parent: 6747 + - uid: 27714 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 85.5,-29.5 + parent: 6747 - proto: RailingCornerSmall entities: - uid: 9019 @@ -137382,6 +136306,76 @@ entities: rot: 1.5707963267948966 rad pos: 29.5,-42.5 parent: 6747 + - uid: 27705 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 76.5,-14.5 + parent: 6747 + - uid: 27706 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 77.5,-13.5 + parent: 6747 + - uid: 27707 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 78.5,-11.5 + parent: 6747 + - uid: 27708 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 79.5,-10.5 + parent: 6747 + - uid: 27709 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 83.5,-10.5 + parent: 6747 + - uid: 27710 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-11.5 + parent: 6747 + - uid: 27711 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 85.5,-13.5 + parent: 6747 + - uid: 27712 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 86.5,-14.5 + parent: 6747 + - uid: 27722 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 77.5,-30.5 + parent: 6747 + - uid: 27723 + components: + - type: Transform + pos: 85.5,-30.5 + parent: 6747 + - uid: 27809 + components: + - type: Transform + pos: 92.5,-31.5 + parent: 6747 + - uid: 27816 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 92.5,-27.5 + parent: 6747 - proto: RandomAnimalSpawner entities: - uid: 1707 @@ -143095,6 +142089,45 @@ entities: - type: Transform pos: 98.5,-24.5 parent: 6747 + - uid: 27758 + components: + - type: Transform + pos: 98.5,-29.5 + parent: 6747 + - uid: 27759 + components: + - type: Transform + pos: 98.5,-34.5 + parent: 6747 + - uid: 27760 + components: + - type: Transform + pos: 98.5,-39.5 + parent: 6747 + - uid: 27804 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 95.5,-40.5 + parent: 6747 + - uid: 27805 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 94.5,-42.5 + parent: 6747 + - uid: 27806 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 91.5,-42.5 + parent: 6747 + - uid: 27807 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 89.5,-41.5 + parent: 6747 - proto: ReinforcedPlasmaWindow entities: - uid: 887 @@ -143534,6 +142567,12 @@ entities: - type: Transform pos: -32.5,-38.5 parent: 6747 + - uid: 548 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-26.5 + parent: 6747 - uid: 591 components: - type: Transform @@ -143589,6 +142628,12 @@ entities: - type: Transform pos: 42.5,10.5 parent: 6747 + - uid: 928 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 82.5,-34.5 + parent: 6747 - uid: 981 components: - type: Transform @@ -144262,7 +143307,8 @@ entities: - uid: 3387 components: - type: Transform - pos: 77.5,-20.5 + rot: -1.5707963267948966 rad + pos: 84.5,-34.5 parent: 6747 - uid: 3450 components: @@ -144529,11 +143575,6 @@ entities: - type: Transform pos: 76.5,-22.5 parent: 6747 - - uid: 5894 - components: - - type: Transform - pos: 77.5,-18.5 - parent: 6747 - uid: 5905 components: - type: Transform @@ -144569,10 +143610,17 @@ entities: - type: Transform pos: 78.5,-37.5 parent: 6747 - - uid: 5990 + - uid: 5973 components: - type: Transform - pos: 77.5,-23.5 + rot: -1.5707963267948966 rad + pos: 76.5,-20.5 + parent: 6747 + - uid: 5979 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-21.5 parent: 6747 - uid: 5991 components: @@ -144584,16 +143632,6 @@ entities: - type: Transform pos: 76.5,-27.5 parent: 6747 - - uid: 5995 - components: - - type: Transform - pos: 76.5,-30.5 - parent: 6747 - - uid: 5997 - components: - - type: Transform - pos: 77.5,-25.5 - parent: 6747 - uid: 6024 components: - type: Transform @@ -144602,7 +143640,8 @@ entities: - uid: 6030 components: - type: Transform - pos: 76.5,-29.5 + rot: -1.5707963267948966 rad + pos: 88.5,-37.5 parent: 6747 - uid: 6037 components: @@ -144834,6 +143873,12 @@ entities: - type: Transform pos: 76.5,-31.5 parent: 6747 + - uid: 6198 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 85.5,-37.5 + parent: 6747 - uid: 6201 components: - type: Transform @@ -144984,6 +144029,12 @@ entities: - type: Transform pos: 57.5,-26.5 parent: 6747 + - uid: 6443 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 85.5,-34.5 + parent: 6747 - uid: 6457 components: - type: Transform @@ -145059,6 +144110,36 @@ entities: - type: Transform pos: 67.5,-38.5 parent: 6747 + - uid: 6538 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 87.5,-37.5 + parent: 6747 + - uid: 6541 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-20.5 + parent: 6747 + - uid: 6542 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 76.5,-23.5 + parent: 6747 + - uid: 6566 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 84.5,-37.5 + parent: 6747 + - uid: 6583 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-29.5 + parent: 6747 - uid: 6592 components: - type: Transform @@ -145304,6 +144385,12 @@ entities: - type: Transform pos: 64.5,21.5 parent: 6747 + - uid: 8311 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 82.5,-37.5 + parent: 6747 - uid: 8316 components: - type: Transform @@ -146314,6 +145401,114 @@ entities: - type: Transform pos: -19.5,-56.5 parent: 6747 + - uid: 27624 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-35.5 + parent: 6747 + - uid: 27625 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-34.5 + parent: 6747 + - uid: 27627 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-32.5 + parent: 6747 + - uid: 27628 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-31.5 + parent: 6747 + - uid: 27630 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-28.5 + parent: 6747 + - uid: 27632 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-25.5 + parent: 6747 + - uid: 27634 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-23.5 + parent: 6747 + - uid: 27635 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-22.5 + parent: 6747 + - uid: 27637 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-20.5 + parent: 6747 + - uid: 27638 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-19.5 + parent: 6747 + - uid: 27640 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 88.5,-17.5 + parent: 6747 + - uid: 27641 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 87.5,-17.5 + parent: 6747 + - uid: 27663 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-22.5 + parent: 6747 + - uid: 27664 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-23.5 + parent: 6747 + - uid: 27676 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-27.5 + parent: 6747 + - uid: 27677 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-28.5 + parent: 6747 + - uid: 27678 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-31.5 + parent: 6747 + - uid: 27679 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-32.5 + parent: 6747 - proto: RemoteSignaller entities: - uid: 1379 @@ -149825,18 +149020,6 @@ entities: rot: 3.141592653589793 rad pos: 4.5,12.5 parent: 6747 - - uid: 6566 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 76.5,-20.5 - parent: 6747 - - uid: 6583 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 76.5,-23.5 - parent: 6747 - uid: 18891 components: - type: Transform @@ -151249,7 +150432,7 @@ entities: - type: Transform pos: 103.5,18.5 parent: 6747 -- proto: SophicScribe +- proto: SophicScribeSpawner entities: - uid: 8916 components: @@ -151386,6 +150569,8 @@ entities: - type: Transform pos: 28.5,-13.5 parent: 6747 + - type: SpamEmitSound + enabled: False - proto: SpaceVillainArcadeFilled entities: - uid: 12178 @@ -151394,6 +150579,8 @@ entities: rot: 1.5707963267948966 rad pos: -39.5,-36.5 parent: 6747 + - type: SpamEmitSound + enabled: False - proto: SpareIdCabinetFilled entities: - uid: 27349 @@ -152785,99 +151972,71 @@ entities: - type: Transform pos: 22.5,-59.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 4418 components: - type: Transform pos: 22.5,-60.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7669 components: - type: Transform pos: 61.5,25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7788 components: - type: Transform pos: 44.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 7789 components: - type: Transform pos: 46.5,42.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8186 components: - type: Transform pos: 32.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8187 components: - type: Transform pos: 33.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8191 components: - type: Transform pos: 33.5,30.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11259 components: - type: Transform pos: 4.5,-91.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11355 components: - type: Transform pos: 53.5,-18.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 15857 components: - type: Transform pos: -50.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 15858 components: - type: Transform pos: -49.5,-5.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 16210 components: - type: Transform pos: 10.5,-20.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 26917 components: - type: Transform pos: 10.5,-19.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: StrangePill entities: - uid: 20202 @@ -155148,6 +154307,14 @@ entities: - type: Transform pos: -51.5,7.5 parent: 6747 +- proto: TableFancyBlack + entities: + - uid: 27812 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 93.5,-29.5 + parent: 6747 - proto: TableFancyBlue entities: - uid: 10932 @@ -157354,8 +156521,6 @@ entities: rot: 3.141592653589793 rad pos: 61.5,32.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: TegCirculator entities: - uid: 13000 @@ -160221,6 +159386,12 @@ entities: - type: Transform pos: 10.5,23.5 parent: 6747 + - uid: 1081 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-17.5 + parent: 6747 - uid: 1085 components: - type: Transform @@ -161246,6 +160417,12 @@ entities: - type: Transform pos: 21.5,-36.5 parent: 6747 + - uid: 2197 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 83.5,-37.5 + parent: 6747 - uid: 2198 components: - type: Transform @@ -164241,11 +163418,6 @@ entities: - type: Transform pos: 56.5,-38.5 parent: 6747 - - uid: 5881 - components: - - type: Transform - pos: 78.5,-23.5 - parent: 6747 - uid: 5882 components: - type: Transform @@ -164271,10 +163443,11 @@ entities: - type: Transform pos: 76.5,-17.5 parent: 6747 - - uid: 5899 + - uid: 5889 components: - type: Transform - pos: 78.5,-18.5 + rot: -1.5707963267948966 rad + pos: 86.5,-37.5 parent: 6747 - uid: 5907 components: @@ -164331,30 +163504,29 @@ entities: - type: Transform pos: 76.5,-38.5 parent: 6747 - - uid: 5980 + - uid: 5982 components: - type: Transform - pos: 78.5,-25.5 + rot: -1.5707963267948966 rad + pos: 76.5,-29.5 parent: 6747 - uid: 5988 components: - type: Transform - pos: 76.5,-20.5 - parent: 6747 - - uid: 5999 - components: - - type: Transform - pos: 76.5,-23.5 + rot: -1.5707963267948966 rad + pos: 86.5,-34.5 parent: 6747 - - uid: 6001 + - uid: 5990 components: - type: Transform - pos: 78.5,-20.5 + rot: -1.5707963267948966 rad + pos: 83.5,-34.5 parent: 6747 - - uid: 6002 + - uid: 5995 components: - type: Transform - pos: 76.5,-18.5 + rot: -1.5707963267948966 rad + pos: 76.5,-30.5 parent: 6747 - uid: 6015 components: @@ -164424,17 +163596,19 @@ entities: - uid: 6169 components: - type: Transform - pos: 82.5,-37.5 + rot: -1.5707963267948966 rad + pos: 86.5,-30.5 parent: 6747 - uid: 6170 components: - type: Transform pos: 79.5,-37.5 parent: 6747 - - uid: 6173 + - uid: 6196 components: - type: Transform - pos: 76.5,-25.5 + rot: -1.5707963267948966 rad + pos: 86.5,-33.5 parent: 6747 - uid: 6341 components: @@ -167111,11 +166285,6 @@ entities: - type: Transform pos: -12.5,-89.5 parent: 6747 - - uid: 11803 - components: - - type: Transform - pos: 82.5,-34.5 - parent: 6747 - uid: 11818 components: - type: Transform @@ -168541,6 +167710,96 @@ entities: - type: Transform pos: -43.5,15.5 parent: 6747 + - uid: 27626 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-33.5 + parent: 6747 + - uid: 27629 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-30.5 + parent: 6747 + - uid: 27631 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-27.5 + parent: 6747 + - uid: 27633 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-24.5 + parent: 6747 + - uid: 27636 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-21.5 + parent: 6747 + - uid: 27639 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-17.5 + parent: 6747 + - uid: 27643 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-18.5 + parent: 6747 + - uid: 27644 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-36.5 + parent: 6747 + - uid: 27645 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 89.5,-37.5 + parent: 6747 + - uid: 27661 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 76.5,-24.5 + parent: 6747 + - uid: 27662 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 76.5,-19.5 + parent: 6747 + - uid: 27665 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-19.5 + parent: 6747 + - uid: 27666 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-24.5 + parent: 6747 + - uid: 27674 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-29.5 + parent: 6747 + - uid: 27675 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 86.5,-26.5 + parent: 6747 - proto: WallShuttle entities: - uid: 6231 @@ -174456,22 +173715,16 @@ entities: - type: Transform pos: 32.5,31.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 8270 components: - type: Transform pos: 60.5,25.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - uid: 11258 components: - type: Transform pos: 3.5,-82.5 parent: 6747 - - type: AtmosDevice - joinedGrid: 6747 - proto: WeaponCapacitorRecharger entities: - uid: 120 From f2c5574e791f3189e0663c3377a0e6e3b71d89a7 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sat, 19 Oct 2024 08:30:48 +0000 Subject: [PATCH 38/76] Automatic Changelog Update (#1081) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index d7e6007674..16c760b446 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7355,3 +7355,12 @@ Entries: id: 6461 time: '2024-10-19T08:26:32.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1069 +- author: VMSolidus + changes: + - type: Fix + message: >- + Fixed Cryptobiolin not providing temporary Psionic Insulation. It now + provides protection from psionic abilities and events for 15 minutes. + id: 6462 + time: '2024-10-19T08:28:28.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1081 From 945b1034b9ac2ca68c037156fe28373b901f69e7 Mon Sep 17 00:00:00 2001 From: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com> Date: Sat, 19 Oct 2024 11:30:56 +0300 Subject: [PATCH 39/76] Cherry-Pick Space-Wizards/Space-Station-14#26001 (#1087) # Description Need someone to test this change first. Resolves #863 Closes #149 # Changelog :cl: - fix: Sechud should no longer stop working when someone in your PVS is set to "wanted". --- Resources/Prototypes/StatusEffects/security.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Resources/Prototypes/StatusEffects/security.yml b/Resources/Prototypes/StatusEffects/security.yml index 2a6695387b..31a22e5df3 100644 --- a/Resources/Prototypes/StatusEffects/security.yml +++ b/Resources/Prototypes/StatusEffects/security.yml @@ -9,33 +9,33 @@ parent: SecurityIcon id: SecurityIconDischarged icon: - sprite: Interface/Misc/security_icons.rsi + sprite: /Textures/Interface/Misc/security_icons.rsi state: hud_discharged - type: statusIcon parent: SecurityIcon id: SecurityIconIncarcerated icon: - sprite: Interface/Misc/security_icons.rsi + sprite: /Textures/Interface/Misc/security_icons.rsi state: hud_incarcerated - type: statusIcon parent: SecurityIcon id: SecurityIconParoled icon: - sprite: Interface/Misc/security_icons.rsi + sprite: /Textures/Interface/Misc/security_icons.rsi state: hud_paroled - type: statusIcon parent: SecurityIcon id: SecurityIconSuspected icon: - sprite: Interface/Misc/security_icons.rsi + sprite: /Textures/Interface/Misc/security_icons.rsi state: hud_suspected - type: statusIcon parent: SecurityIcon id: SecurityIconWanted icon: - sprite: Interface/Misc/security_icons.rsi + sprite: /Textures/Interface/Misc/security_icons.rsi state: hud_wanted From 000b5f4cfdf6b99e9e3a799c1df9786c7b255b1a Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:31:17 -0400 Subject: [PATCH 40/76] Fix Mech Weight (#1090) # Description Mechs have a more realistic weight now. :) Fixes #666 # Changelog :cl: - add: Ripley now weighs a few tonnes. Good luck ever pulling it. --- Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml b/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml index dab6f9f20f..6b5c634e93 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml @@ -72,7 +72,7 @@ shape: !type:PhysShapeCircle radius: 0.45 - density: 1000 + density: 21390 # 15 Tonnes mask: - MobMask layer: From 0cb7c3ffb789ec720aac219106a193351c29a116 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:31:32 -0400 Subject: [PATCH 41/76] Pistol Ammo Counter (#1091) # Description All pistols were lacking the AmmoCounterComponent, needed to display their ammo in the UI like all the rest of the guns. # Changelog :cl: - fix: Handguns(Such as Mk58, Viper, Cobra, etc.) now display their ammo counter in the UI. --- .../Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml index 1fe6ab10a9..9657a8cf68 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml @@ -65,6 +65,7 @@ - type: Appearance - type: StaticPrice price: 500 + - type: AmmoCounter - type: entity name: viper From 482d8b36e4240469eb959a1c5dd1c2554cf782ba Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:33:40 -0400 Subject: [PATCH 42/76] Stop Harpy Singing On Disconnect (#1079) # Description This fixes a bug reported by @Durk over discord, whereby if a Harpy's player disconnects, their midi player does not correctly close, causing the song to perpetually play. # Changelog :cl: - fix: Fixed Harpies not ceasing with their singing when a player disconnects. --- .../Traits/Assorted/Systems/SharedSingerSystem.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs b/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs index 56f76af9bb..13270ae45d 100644 --- a/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs +++ b/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Traits.Assorted.Components; using Content.Shared.Traits.Assorted.Prototypes; using Content.Shared.Zombies; +using Robust.Shared.Player; using Robust.Shared.Prototypes; namespace Content.Shared.Traits.Assorted.Systems; @@ -15,7 +16,6 @@ public abstract class SharedSingerSystem : EntitySystem [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedInstrumentSystem _instrument = default!; - [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; public override void Initialize() { @@ -26,6 +26,7 @@ public override void Initialize() SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnBoundUIClosed); SubscribeLocalEvent(OnBoundUIOpened); + SubscribeLocalEvent(OnPlayerDetached); } private void OnStartup(Entity ent, ref ComponentStartup args) @@ -69,6 +70,11 @@ private void OnBoundUIOpened(EntityUid uid, SingerComponent component, BoundUIOp _appearance.SetData(uid, HarpyVisualLayers.Singing, SingingVisualLayer.True, appearance); } + private void OnPlayerDetached(EntityUid uid, SingerComponent component, PlayerDetachedEvent args) + { + CloseMidiUi(uid); + } + /// /// Closes the MIDI UI if it is open. Does nothing on client side. /// From 790772bd1c0fc1a9db588c41fc284bea09547c8b Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:34:03 -0400 Subject: [PATCH 43/76] Fix Add Psionic Power Command (#1095) # Description Someone came up with the fix for this 3 days ago, and then never PR'ed it here. So I guess I'm PR'ing it now while I'm still on a bugfixing binge. # Changelog :cl: - fix: addpsionicpower command now works. --- Content.Server/Psionics/PsionicsCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Psionics/PsionicsCommands.cs b/Content.Server/Psionics/PsionicsCommands.cs index 2894343c66..565146629b 100644 --- a/Content.Server/Psionics/PsionicsCommands.cs +++ b/Content.Server/Psionics/PsionicsCommands.cs @@ -38,7 +38,7 @@ public sealed class AddPsionicPowerCommand : IConsoleCommand public async void Execute(IConsoleShell shell, string argStr, string[] args) { var entMan = IoCManager.Resolve(); - var psionicPowers = IoCManager.Resolve(); + var psionicPowers = entMan.System(); var protoMan = IoCManager.Resolve(); if (args.Length != 2) From 9e57664fb1236e2c6addf57c091b4094feb493a6 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sat, 19 Oct 2024 08:35:51 +0000 Subject: [PATCH 44/76] Automatic Changelog Update (#1086) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 16c760b446..2b13422435 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7364,3 +7364,10 @@ Entries: id: 6462 time: '2024-10-19T08:28:28.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1081 +- author: VMSolidus + changes: + - type: Add + message: Arena now has a properly functional Arrivals Dock. + id: 6463 + time: '2024-10-19T08:30:22.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1086 From 9b779591bfcb1390c6cd9ac16d564174dc7af8e9 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:36:42 -0400 Subject: [PATCH 45/76] Chaplain Bible Loadouts (#1068) # Description Since you get your Backpack from your Loadout, Chaplain no longer gets his bible from his backpack. Actually NOBODY gets their Filled backpacks anymore. Every job that previously had a Filled backpack needs to have their shit added to Loadouts. This PR adds loadout categories for the Chaplain to get his Bible and Stamp back. I spent literally 2 fucking hours trying to figure out why I can't add job specific categories to Science, and have nothing to show for it. So Uhh.. Fuck. I don't know why I can add job specific categories for service, but can't for Science. # Changelog :cl: - add: Chaplain's Bible and Stamp is now found in Loadouts. --- .../Locale/en-US/loadouts/itemgroups.ftl | 6 +- .../Fills/Backpacks/StarterGear/backpack.yml | 5 -- .../CharacterItemGroups/scienceGroups.yml | 24 ++++- .../Prototypes/Loadouts/Jobs/science.yml | 87 ++++++++++++++++++- .../Roles/Jobs/Civilian/chaplain.yml | 2 +- 5 files changed, 115 insertions(+), 9 deletions(-) diff --git a/Resources/Locale/en-US/loadouts/itemgroups.ftl b/Resources/Locale/en-US/loadouts/itemgroups.ftl index 9e66e200bc..dba4cf72a9 100644 --- a/Resources/Locale/en-US/loadouts/itemgroups.ftl +++ b/Resources/Locale/en-US/loadouts/itemgroups.ftl @@ -42,6 +42,10 @@ character-item-group-LoadoutUniformsScience = Epistemics Uniforms # Epistemics - Cataloguer character-item-group-LoadoutCataloguerUniforms = Cataloguer Uniforms +# Epistemics - Chaplain +character-item-group-LoadoutChaplainUniforms = Chaplain Uniforms +character-item-group-LoadoutChaplainEquipment = Chaplain Equipment + # Medical character-item-group-LoadoutEyesMedical = Medical Eyewear character-item-group-LoadoutGlovesMedical = Medical Gloves @@ -98,4 +102,4 @@ character-item-group-LoadoutMusicianInstruments = Musician Instruments # Traits - Languages character-item-group-TraitsLanguagesBasic = Basic Languages -character-item-group-TraitsAccents = Accents \ No newline at end of file +character-item-group-TraitsAccents = Accents diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml index 95f8cf87f2..6713ed1e22 100644 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml +++ b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml @@ -178,11 +178,6 @@ noSpawn: true parent: ClothingBackpack id: ClothingBackpackChaplainFilled - components: - - type: StorageFill - contents: - - id: Bible - - id: RubberStampChaplain - type: entity noSpawn: true diff --git a/Resources/Prototypes/CharacterItemGroups/scienceGroups.yml b/Resources/Prototypes/CharacterItemGroups/scienceGroups.yml index ac41a9286a..26cb07dae9 100644 --- a/Resources/Prototypes/CharacterItemGroups/scienceGroups.yml +++ b/Resources/Prototypes/CharacterItemGroups/scienceGroups.yml @@ -119,4 +119,26 @@ - type: loadout id: LoadoutScienceJumpsuitLibrarianZav - type: loadout - id: LoadoutScienceJumpsuitLibrarianZeng \ No newline at end of file + id: LoadoutScienceJumpsuitLibrarianZeng + - type: loadout + id: LoadoutScienceJumpsuitLibrarian + - type: loadout + id: LoadoutScienceJumpskirtLibrarian + +# Chaplain +- type: characterItemGroup + id: LoadoutChaplainUniforms + items: + - type: loadout + id: LoadoutChaplainJumpsuit + - type: loadout + id: LoadoutChaplainJumpskirt + +- type: characterItemGroup + id: LoadoutChaplainEquipment + maxItems: 2 + items: + - type: loadout + id: LoadoutChaplainBible + - type: loadout + id: LoadoutChaplainStamp diff --git a/Resources/Prototypes/Loadouts/Jobs/science.yml b/Resources/Prototypes/Loadouts/Jobs/science.yml index 198d7f9cc6..3f376ec872 100644 --- a/Resources/Prototypes/Loadouts/Jobs/science.yml +++ b/Resources/Prototypes/Loadouts/Jobs/science.yml @@ -625,4 +625,89 @@ jobs: - Librarian items: - - ClothingUniformJumpsuitLibrarianZeng \ No newline at end of file + - ClothingUniformJumpsuitLibrarianZeng + +- type: loadout + id: LoadoutScienceJumpsuitLibrarian + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCataloguerUniforms + - !type:CharacterJobRequirement + jobs: + - Librarian + items: + - ClothingUniformJumpsuitLibrarian + +- type: loadout + id: LoadoutScienceJumpskirtLibrarian + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCataloguerUniforms + - !type:CharacterJobRequirement + jobs: + - Librarian + items: + - ClothingUniformJumpskirtLibrarian + +# Chaplain +- type: loadout + id: LoadoutChaplainJumpsuit + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChaplainUniforms + - !type:CharacterJobRequirement + jobs: + - Chaplain + items: + - ClothingUniformJumpsuitChaplain + +- type: loadout + id: LoadoutChaplainJumpskirt + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChaplainUniforms + - !type:CharacterJobRequirement + jobs: + - Chaplain + items: + - ClothingUniformJumpskirtChaplain + +- type: loadout + id: LoadoutChaplainBible + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChaplainEquipment + - !type:CharacterJobRequirement + jobs: + - Chaplain + items: + - Bible + +- type: loadout + id: LoadoutChaplainStamp + category: JobsScience + cost: 0 + exclusive: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChaplainEquipment + - !type:CharacterJobRequirement + jobs: + - Chaplain + items: + - RubberStampChaplain diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml index d4f8bdb067..b4d849b92d 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml @@ -38,7 +38,7 @@ id: ChaplainGear equipment: jumpsuit: ClothingUniformJumpsuitChaplain - back: ClothingBackpackChaplainFilled + back: ClothingBackpack shoes: ClothingShoesColorBlack id: ChaplainPDA ears: ClothingHeadsetScience From 3e626f62ba60c0fd206731214389896ee5334829 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:40:40 -0400 Subject: [PATCH 46/76] Glacier Dedicated Arrivals Dock (#1088) # Description For #1070 This PR adds a dedicated Arrivals docking area, notably dropping people off at the "Village" rather than the Evac site. I felt it was more appropriate to drop people off at the village than near any industrial area, as it helps add to some of the map's charm that the first thing people see is a wintery wonderland village area. # TODO

Media

![image](https://github.com/user-attachments/assets/2d672116-95ea-4c36-91fc-1e6c66de7506)

# Changelog :cl: - add: Glacier now has a dedicated Arrivals dock that leads into the Village. --- Resources/Maps/glacier.yml | 1068 ++++++++++++++++++++++-------------- 1 file changed, 641 insertions(+), 427 deletions(-) diff --git a/Resources/Maps/glacier.yml b/Resources/Maps/glacier.yml index d9e4276b4d..572aca8582 100644 --- a/Resources/Maps/glacier.yml +++ b/Resources/Maps/glacier.yml @@ -316,7 +316,7 @@ entities: version: 6 5,-1: ind: 5,-1 - tiles: FQAAAAAAFQAAAAAEFQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQAAAAAEFQAAAAACFQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAFQAAAAAGFQAAAAAGPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAKAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAKAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAKAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAKAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPQAAAAAAPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAACKAAAAAACKAAAAAABPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: FQAAAAAAFQAAAAAEPwAAAAAAPwAAAAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQAAAAAEFQAAAAACPwAAAAAAPwAAAAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAPQAAAAAAPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAACKAAAAAACKAAAAAABPQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 4,-3: ind: 4,-3 @@ -328,7 +328,7 @@ entities: version: 6 5,-2: ind: 5,-2 - tiles: YQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYAAAAAAAYAAAAAAAYAAAAAAAYQAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAABQAAAAAABQAAAAAABQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAABQAAAAAABQAAAAAABQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYAAAAAAAYAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAABQAAAAAABQAAAAAABQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYAAAAAAAYAAAAAAAYAAAAAAAYQAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAACQAAAAAJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: YQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYAAAAAAAYAAAAAAAYAAAAAAAYQAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAABQAAAAAABQAAAAAABQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAABQAAAAAABQAAAAAABQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYAAAAAAAYAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAABQAAAAAABQAAAAAABQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYQAAAAAAYAAAAAAAYAAAAAAAYAAAAAAAYQAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAACQAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAARwAAAAAARwAAAAAARwAAAAAARwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACQAAAAAARwAAAAAAPwAAAAAAPwAAAAAAPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 -4,1: ind: -4,1 @@ -2751,11 +2751,6 @@ entities: 3815: -39.85408,-7.657089 3816: -38.037838,-6.831524 3817: -40.293243,-5.1342893 - - node: - color: '#2559C7D0' - id: Flowersbr1 - decals: - 1414: 80.20305,-16.79779 - node: color: '#2B72B4BD' id: Flowersbr1 @@ -2879,8 +2874,6 @@ entities: 1408: 62.92135,-38.572655 1409: 67.42135,-35.61953 1410: 70.712,-35.933105 - 1411: 78.82805,-19.23529 - 1412: 79.20305,-17.26654 - node: color: '#3AB3DA7F' id: Flowersbr2 @@ -3037,8 +3030,6 @@ entities: id: Flowersy1 decals: 1404: 52,-32 - 1413: 78.14055,-18.438416 - 1415: 80.03117,-18.375916 - node: color: '#3375A4BD' id: Flowersy1 @@ -8790,6 +8781,7 @@ entities: 5325: -56.75957,1.4266167 5326: -56.923634,1.0281792 5327: -48.51849,2.093442 + 5979: 80.11802,-21.809238 - node: color: '#FFFFFFE6' id: grasssnowb1 @@ -8888,7 +8880,6 @@ entities: 2285: 3.9030752,-5.9041996 2291: 19.729828,-33.013443 2294: 45.10163,-30.335632 - 2295: 79.1315,-17.643627 2297: 14.678892,11.182862 - node: zIndex: 1 @@ -8901,6 +8892,7 @@ entities: id: grasssnowc1 decals: 3742: -44.000923,5.2940826 + 5978: 81.227394,-23.777988 - node: color: '#FFFFFFE6' id: grasssnowc2 @@ -8908,6 +8900,11 @@ entities: 2270: 45.595287,-13.74883 2271: 62.10621,-9.182607 2286: 5.9226108,-1.9519272 + - node: + color: '#FFFFFFFF' + id: grasssnowc2 + decals: + 5977: 80.352394,-24.840488 - node: color: '#FFFFFFCD' id: grasssnowc3 @@ -8925,6 +8922,11 @@ entities: 2266: -19.638706,-9.343069 2267: 35.956028,-9.619087 2499: 41.014008,22.813168 + - node: + color: '#FFFFFFFF' + id: grasssnowc3 + decals: + 5980: 79.93052,-21.152988 - node: cleanable: True color: '#FF4A3AFF' @@ -9683,9 +9685,9 @@ entities: 0: 111 -4,12: 0: 30583 - 3: 34952 + 2: 34952 -4,10: - 2: 12 + 3: 12 0: 52224 -4,11: 0: 3276 @@ -9693,12 +9695,12 @@ entities: 0: 118 -3,10: 0: 4352 - 3: 26214 + 2: 26214 -3,11: 0: 281 - 3: 26214 + 2: 26214 -3,12: - 3: 63351 + 2: 63351 -2,9: 0: 2039 -2,10: @@ -9707,7 +9709,7 @@ entities: 0: 30591 -2,12: 0: 119 - 3: 61440 + 2: 61440 -1,9: 0: 30711 -1,10: @@ -9716,16 +9718,16 @@ entities: 0: 30503 -1,12: 0: 119 - 3: 61440 + 2: 61440 0,8: 0: 63351 0,9: 0: 63736 0,10: - 3: 61712 + 2: 61712 0: 232 0,11: - 3: 16255 + 2: 16255 0,-4: 0: 60428 0,-3: @@ -9841,17 +9843,17 @@ entities: 4,7: 0: 61663 0,12: - 3: 64443 + 2: 64443 1,9: 0: 32767 1,10: 0: 35507 - 3: 12288 + 2: 12288 1,11: - 3: 8994 + 2: 8994 0: 34944 1,12: - 3: 291 + 2: 291 2,9: 0: 4095 2,10: @@ -9868,7 +9870,7 @@ entities: 0: 65534 3,12: 0: 28927 - 3: 32768 + 2: 32768 4,8: 0: 65294 4,9: @@ -9983,7 +9985,7 @@ entities: 0: 56831 4,12: 0: 255 - 3: 61440 + 2: 61440 5,9: 0: 3838 5,10: @@ -9992,7 +9994,7 @@ entities: 0: 65528 5,12: 0: 33535 - 3: 28672 + 2: 28672 6,9: 0: 61439 6,10: @@ -10149,16 +10151,16 @@ entities: 0: 43008 10,12: 0: 19 - 3: 27784 + 2: 27784 11,9: 0: 65534 11,10: 0: 56559 11,11: 0: 477 - 3: 17408 + 2: 17408 11,12: - 3: 4407 + 2: 4407 12,8: 0: 30583 12,9: @@ -10168,73 +10170,73 @@ entities: 12,11: 0: 3327 0,13: - 3: 43929 + 2: 43929 -1,13: - 3: 2039 + 2: 2039 0,14: - 3: 61038 + 2: 61038 0: 128 -1,14: 0: 65535 0,15: - 3: 61166 + 2: 61166 -1,15: 0: 15 4: 65280 0,16: - 3: 43942 + 2: 43942 0: 8 1,13: - 3: 273 + 2: 273 0: 8 1,14: - 3: 51711 + 2: 51711 1,15: - 3: 39127 + 2: 39127 0: 8 1,16: - 3: 25823 + 2: 25823 2,13: 0: 3067 2,14: - 3: 60303 + 2: 60303 3,13: 0: 2867 - 3: 136 + 2: 136 3,14: 0: 65535 4,13: - 3: 255 + 2: 255 0: 36608 4,14: 0: 65535 5,13: - 3: 30583 + 2: 30583 0: 34952 5,14: - 3: 1911 + 2: 1911 0: 63624 6,13: 0: 61167 6,14: - 3: 12784 + 2: 12784 5,15: 0: 8 6,15: 0: 15 7,13: 0: 13107 - 3: 34952 + 2: 34952 7,14: - 3: 62200 + 2: 62200 7,15: 0: 27718 7,16: 0: 98 8,13: - 3: 17615 + 2: 17615 8,14: - 3: 7748 + 2: 7748 0: 57344 12,4: 0: 6 @@ -10252,14 +10254,14 @@ entities: 0: 61439 15,6: 0: 20480 - 3: 35840 + 2: 35840 15,7: 0: 61661 16,6: - 3: 12288 + 2: 12288 16,7: 0: 61489 - 3: 70 + 2: 70 13,9: 0: 28671 13,10: @@ -10284,10 +10286,10 @@ entities: 0: 28912 16,9: 0: 61489 - 3: 70 + 2: 70 16,10: 0: 12528 - 3: 16384 + 2: 16384 16,11: 0: 1 13,0: @@ -10298,7 +10300,7 @@ entities: 0: 12407 13,3: 0: 51 - 3: 1024 + 2: 1024 13,-1: 0: 34835 -8,4: @@ -10402,20 +10404,20 @@ entities: 4,-6: 0: 33075 -8,-3: - 3: 12544 + 2: 12544 -9,-3: 0: 64250 - 3: 1285 + 2: 1285 -8,-1: 0: 61731 -9,-2: 0: 64170 - 3: 1365 + 2: 1365 -9,-1: 0: 64174 - 3: 1361 + 2: 1361 -8,-2: - 3: 512 + 2: 512 -8,0: 0: 65526 -7,-3: @@ -10424,7 +10426,7 @@ entities: 0: 62321 -7,-5: 0: 24576 - 3: 119 + 2: 119 -7,-4: 0: 49152 -7,-2: @@ -10445,27 +10447,27 @@ entities: 0: 30513 -9,0: 0: 64170 - 3: 1365 + 2: 1365 -8,1: - 3: 1799 + 2: 1799 0: 2296 -9,1: 0: 64250 - 3: 1285 + 2: 1285 -8,2: 0: 64384 -9,2: 0: 51200 - 3: 1 + 2: 1 -9,3: 0: 47308 -7,1: - 3: 1799 + 2: 1799 0: 2296 -7,2: 0: 65392 -6,1: - 3: 263 + 2: 263 0: 248 -6,2: 0: 52424 @@ -10474,9 +10476,9 @@ entities: 8,15: 0: 49356 9,13: - 3: 34959 + 2: 34959 9,14: - 3: 3464 + 2: 3464 0: 61440 9,15: 0: 4351 @@ -10485,23 +10487,23 @@ entities: 9,16: 0: 4095 10,13: - 3: 3 + 2: 3 10,14: - 3: 33568 + 2: 33568 0: 28672 10,15: 0: 61503 10,16: 0: 311 11,14: - 3: 8177 + 2: 8177 0: 57344 11,15: 0: 207 11,13: - 3: 4401 + 2: 4401 12,14: - 3: 12561 + 2: 12561 12,15: 0: 8209 11,16: @@ -10522,10 +10524,10 @@ entities: 0: 65535 -12,3: 0: 49151 - 3: 16384 + 2: 16384 -13,3: 0: 9215 - 3: 56320 + 2: 56320 -11,0: 0: 65535 -11,1: @@ -10533,7 +10535,7 @@ entities: -11,2: 0: 65535 -11,3: - 3: 1799 + 2: 1799 0: 63736 -11,-1: 0: 65535 @@ -10543,12 +10545,12 @@ entities: 0: 65535 -10,1: 0: 63999 - 3: 1536 + 2: 1536 -10,2: - 3: 1285 + 2: 1285 0: 31482 -10,3: - 3: 773 + 2: 773 0: 12338 -10,-1: 0: 65535 @@ -10562,19 +10564,19 @@ entities: 0: 61183 -13,5: 0: 58468 - 3: 2 + 2: 2 -13,6: 0: 26188 - 3: 48 + 2: 48 -12,7: 0: 45056 - 3: 16384 + 2: 16384 -12,8: - 3: 1799 + 2: 1799 0: 12536 -13,7: 0: 57344 - 3: 4914 + 2: 4914 -11,5: 0: 30583 -11,6: @@ -10582,7 +10584,7 @@ entities: -11,7: 0: 61986 -11,8: - 3: 1797 + 2: 1797 0: 59642 -10,6: 0: 52479 @@ -10591,7 +10593,7 @@ entities: -10,5: 0: 52430 -10,8: - 3: 771 + 2: 771 0: 29812 -9,6: 0: 12545 @@ -10600,7 +10602,7 @@ entities: -12,-4: 0: 61440 -12,-3: - 3: 1799 + 2: 1799 0: 63736 -13,-3: 0: 3935 @@ -10612,28 +10614,28 @@ entities: 0: 65535 -11,-4: 0: 12288 - 3: 16384 + 2: 16384 -11,-3: - 3: 1807 + 2: 1807 0: 63728 -11,-2: 0: 65535 -10,-3: - 3: 1797 + 2: 1797 0: 63736 -10,-2: 0: 65535 -10,-4: - 3: 6152 + 2: 6152 0: 49152 -10,-5: 0: 34952 -9,-4: - 3: 1365 + 2: 1365 0: 64170 -9,-5: 0: 64175 - 3: 1360 + 2: 1360 -16,-4: 0: 28671 -16,-5: @@ -10650,19 +10652,19 @@ entities: 0: 28398 -16,-1: 0: 49138 - 3: 16384 + 2: 16384 -17,-1: 0: 2240 - 3: 32768 + 2: 32768 -16,0: - 3: 1 + 2: 1 0: 1150 -15,-4: 0: 819 - 3: 32768 + 2: 32768 -15,-3: 0: 819 - 3: 34952 + 2: 34952 -15,-2: 0: 13107 -15,-1: @@ -10677,41 +10679,41 @@ entities: 0: 65534 -14,-4: 0: 57344 - 3: 64 + 2: 64 -14,-2: - 3: 15 + 2: 15 0: 51328 -14,0: 0: 65535 -13,-4: 0: 28672 - 3: 144 + 2: 144 -10,-6: 0: 32768 -9,-6: 0: 61440 -8,-5: - 3: 255 + 2: 255 -16,-6: - 3: 15 + 2: 15 0: 63232 -17,-6: - 3: 74 + 2: 74 0: 61696 -17,-5: 0: 57561 -16,-7: - 3: 8192 + 2: 8192 -15,-6: - 3: 16400 + 2: 16400 0: 45056 -14,-6: 0: 4096 - 3: 8192 + 2: 8192 -14,-5: - 3: 4352 + 2: 4352 -17,0: - 3: 8 + 2: 8 0: 2240 -16,1: 0: 3824 @@ -10728,7 +10730,7 @@ entities: -16,4: 0: 65535 -15,1: - 3: 752 + 2: 752 0: 8456 -15,3: 0: 20447 @@ -10736,29 +10738,29 @@ entities: 0: 50786 -15,4: 0: 9830 - 3: 2048 + 2: 2048 -14,1: 0: 61439 -14,2: 0: 62606 - 3: 320 + 2: 320 -14,3: 0: 8191 - 3: 57344 + 2: 57344 -14,4: - 3: 1834 + 2: 1834 0: 34944 -13,4: 0: 24578 - 3: 544 + 2: 544 -18,-7: - 3: 2112 + 2: 2112 -18,-6: 0: 51396 -18,-5: 0: 12 -17,-7: - 3: 40064 + 2: 40064 -19,2: 0: 9319 -19,3: @@ -10826,7 +10828,7 @@ entities: 0: 7103 8,-9: 0: 57344 - 3: 224 + 2: 224 9,-8: 0: 53755 9,-7: @@ -10835,7 +10837,7 @@ entities: 0: 4081 9,-9: 0: 47296 - 3: 16 + 2: 16 10,-8: 0: 56343 10,-7: @@ -10965,22 +10967,22 @@ entities: 17,-2: 0: 34955 17,-1: - 3: 22016 + 2: 22016 0: 43208 17,0: 0: 30475 - 3: 4 + 2: 4 18,-3: 0: 30591 18,-2: 0: 25207 - 3: 5376 + 2: 5376 18,-1: 0: 61203 - 3: 4196 + 2: 4196 18,0: 0: 49117 - 3: 16418 + 2: 16418 19,-3: 0: 49087 19,-1: @@ -11015,7 +11017,7 @@ entities: 0: 65532 -13,8: 0: 43257 - 3: 1798 + 2: 1798 -11,9: 0: 3104 -10,9: @@ -11038,40 +11040,40 @@ entities: 0: 65394 -14,7: 0: 45736 - 3: 19522 + 2: 19522 -14,8: 0: 9386 - 3: 51780 + 2: 51780 -4,13: 0: 119 - 3: 62600 + 2: 62600 -5,13: 0: 30632 -4,14: - 3: 65023 + 2: 65023 -5,14: - 3: 21882 + 2: 21882 0: 5 -4,15: - 3: 4383 + 2: 4383 -5,15: - 3: 29772 + 2: 29772 -4,16: - 3: 18255 + 2: 18255 -3,13: - 3: 14071 + 2: 14071 -3,14: - 3: 13107 + 2: 13107 0: 34952 -3,15: - 3: 8739 + 2: 8739 0: 8 4: 34816 -3,16: - 3: 60963 + 2: 60963 4: 8 -2,13: - 3: 3324 + 2: 3324 0: 49152 -2,14: 0: 65535 @@ -11080,64 +11082,64 @@ entities: 4: 65280 -2,16: 4: 15 - 3: 65280 + 2: 65280 -1,16: 4: 15 - 3: 7936 + 2: 7936 -5,16: - 3: 7962 + 2: 7962 0: 16452 -4,17: - 3: 47332 + 2: 47332 0: 16384 -5,17: - 3: 42567 + 2: 42567 0: 16384 -4,18: - 3: 35820 + 2: 35820 0: 1024 -5,18: - 3: 4030 + 2: 4030 -4,19: - 3: 51400 + 2: 51400 -3,17: - 3: 49722 + 2: 49722 0: 12288 -3,18: - 3: 3631 + 2: 3631 0: 256 -3,19: - 3: 4112 + 2: 4112 -4,20: - 3: 51400 + 2: 51400 -2,17: - 3: 61453 + 2: 61453 -2,18: - 3: 3455 + 2: 3455 0: 512 -1,17: - 3: 57617 + 2: 57617 0: 4096 -1,18: - 3: 36767 + 2: 36767 -1,19: - 3: 51400 + 2: 51400 0,17: - 3: 30891 + 2: 30891 0: 32768 0,18: - 3: 185 + 2: 185 0: 256 0,19: - 3: 4112 + 2: 4112 -1,20: - 3: 51400 + 2: 51400 1,17: - 3: 4372 + 2: 4372 1,18: - 3: 99 + 2: 99 2,16: - 3: 1 + 2: 1 -8,12: 0: 65328 -9,12: @@ -11153,9 +11155,9 @@ entities: -6,14: 0: 35055 -6,15: - 3: 34952 + 2: 34952 -6,16: - 3: 7960 + 2: 7960 0: 32896 21,-4: 0: 4096 @@ -11202,14 +11204,14 @@ entities: 15,-13: 0: 65409 21,-8: - 3: 32903 + 2: 32903 0: 32624 21,-7: - 3: 7 + 2: 7 22,-8: - 3: 18241 + 2: 18241 22,-7: - 3: 1 + 2: 1 -16,5: 0: 14199 -17,5: @@ -11220,23 +11222,23 @@ entities: 0: 61198 -15,5: 0: 4402 - 3: 17984 + 2: 17984 -15,6: - 3: 16 + 2: 16 0: 236 -14,6: 0: 8208 - 3: 224 + 2: 224 -14,5: - 3: 8 + 2: 8 -8,16: - 3: 20288 + 2: 20288 -7,16: - 3: 24400 + 2: 24400 -6,17: - 3: 34952 + 2: 34952 -6,18: - 3: 136 + 2: 136 0: 2048 12,16: 0: 23955 @@ -11248,23 +11250,23 @@ entities: 0: 16 -7,-6: 0: 36864 - 3: 26222 + 2: 26222 -7,-8: - 3: 26342 + 2: 26342 -7,-9: - 3: 28262 + 2: 28262 -7,-7: - 3: 26222 + 2: 26222 -6,-8: - 3: 8244 + 2: 8244 0: 52424 -6,-7: - 3: 17 + 2: 17 0: 63342 -6,-6: 0: 179 -6,-9: - 3: 20300 + 2: 20300 0: 32768 -5,-8: 0: 63863 @@ -11272,7 +11274,7 @@ entities: 0: 34968 -5,-9: 0: 16382 - 3: 1 + 2: 1 -4,-8: 0: 17417 -4,-6: @@ -11346,20 +11348,20 @@ entities: 17,-14: 0: 4096 -3,20: - 3: 4112 + 2: 4112 -4,21: - 3: 136 + 2: 136 0,20: - 3: 4112 + 2: 4112 -1,21: - 3: 136 + 2: 136 -7,-10: 0: 2050 - 3: 26348 + 2: 26348 -6,-10: - 3: 17663 + 2: 17663 -5,-10: - 3: 17 + 2: 17 0: 51456 -5,-11: 0: 4096 @@ -11385,7 +11387,7 @@ entities: 1: 3 0: 3712 16,0: - 3: 4 + 2: 4 0: 51208 16,1: 0: 2248 @@ -11393,30 +11395,30 @@ entities: 0: 1911 18,1: 0: 4081 - 3: 14 + 2: 14 19,1: - 3: 13 + 2: 13 0: 3826 20,0: 0: 62208 - 3: 3072 + 2: 3072 20,1: 0: 1023 - 3: 3072 + 2: 3072 21,0: - 3: 9472 + 2: 9472 0: 4096 21,1: 0: 19 - 3: 1324 + 2: 1324 22,1: - 3: 31 + 2: 31 22,0: - 3: 4096 + 2: 4096 23,1: - 3: 17 + 2: 17 23,0: - 3: 4096 + 2: 4096 uniqueMixes: - volume: 2500 temperature: 293.15 @@ -11449,10 +11451,11 @@ entities: - 0 - 0 - volume: 2500 - temperature: 293.15 + immutable: True + temperature: 213.15 moles: - - 0 - - 6666.982 + - 21.824879 + - 82.10312 - 0 - 0 - 0 @@ -11464,11 +11467,10 @@ entities: - 0 - 0 - volume: 2500 - immutable: True - temperature: 213.15 + temperature: 293.15 moles: - - 21.824879 - - 82.10312 + - 0 + - 6666.982 - 0 - 0 - 0 @@ -12253,6 +12255,20 @@ entities: - type: Transform pos: 51.5,3.5 parent: 2 +- proto: AirlockExternalGlassShuttleArrivals + entities: + - uid: 5846 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-16.5 + parent: 2 + - uid: 12802 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-9.5 + parent: 2 - proto: AirlockExternalGlassShuttleEmergencyLocked entities: - uid: 107 @@ -14446,6 +14462,30 @@ entities: - type: Transform pos: -69.5,22.5 parent: 2 + - uid: 17404 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 75.5,-11.5 + parent: 2 + - uid: 17406 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 75.5,-12.5 + parent: 2 + - uid: 17407 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 84.5,-9.5 + parent: 2 + - uid: 17408 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 84.5,-16.5 + parent: 2 - proto: AtmosFixBlockerMarker entities: - uid: 551 @@ -15181,7 +15221,6 @@ entities: solutions: beaker: temperature: 293.15 - canMix: False canReact: True maxVol: 50 name: null @@ -28681,6 +28720,61 @@ entities: - type: Transform pos: -69.5,26.5 parent: 2 + - uid: 17388 + components: + - type: Transform + pos: 82.5,-10.5 + parent: 2 + - uid: 17389 + components: + - type: Transform + pos: 83.5,-10.5 + parent: 2 + - uid: 17390 + components: + - type: Transform + pos: 83.5,-9.5 + parent: 2 + - uid: 17391 + components: + - type: Transform + pos: 84.5,-9.5 + parent: 2 + - uid: 17392 + components: + - type: Transform + pos: 82.5,-12.5 + parent: 2 + - uid: 17393 + components: + - type: Transform + pos: 82.5,-13.5 + parent: 2 + - uid: 17394 + components: + - type: Transform + pos: 83.5,-13.5 + parent: 2 + - uid: 17395 + components: + - type: Transform + pos: 83.5,-14.5 + parent: 2 + - uid: 17396 + components: + - type: Transform + pos: 83.5,-15.5 + parent: 2 + - uid: 17397 + components: + - type: Transform + pos: 83.5,-16.5 + parent: 2 + - uid: 17398 + components: + - type: Transform + pos: 84.5,-16.5 + parent: 2 - proto: CableApcStack entities: - uid: 3087 @@ -43323,18 +43417,6 @@ entities: rot: 1.5707963267948966 rad pos: 80.5,-10.5 parent: 2 - - uid: 5846 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 84.5,-11.5 - parent: 2 - - uid: 5847 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 84.5,-9.5 - parent: 2 - uid: 5848 components: - type: Transform @@ -44324,6 +44406,14 @@ entities: rot: 1.5707963267948966 rad pos: 28.5,-15.5 parent: 2 +- proto: CigarCase + entities: + - uid: 12432 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 80.47377,-7.4654655 + parent: 2 - proto: Cigarette entities: - uid: 5985 @@ -45975,8 +46065,11 @@ entities: - uid: 6259 components: - type: Transform - pos: 80.79694,-16.95121 + pos: 81.71729,-23.264704 parent: 2 + - type: Physics + angularDamping: 0 + linearDamping: 0 - proto: ClothingUniformJumpsuitMonasticRobeDark entities: - uid: 6260 @@ -46043,6 +46136,12 @@ entities: parent: 2 - type: Physics bodyType: Dynamic + - uid: 5847 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 79.5,-7.5 + parent: 2 - uid: 6271 components: - type: Transform @@ -47630,6 +47729,11 @@ entities: - type: Transform pos: 55.5,27.5 parent: 2 + - uid: 17386 + components: + - type: Transform + pos: 79.5,-12.5 + parent: 2 - proto: DefaultStationBeaconArtifactLab entities: - uid: 14128 @@ -47763,13 +47867,6 @@ entities: - type: Transform pos: 16.5,39.5 parent: 2 -- proto: DefaultStationBeaconEvac - entities: - - uid: 4014 - components: - - type: Transform - pos: 64.5,36.5 - parent: 2 - proto: DefaultStationBeaconExam entities: - uid: 6835 @@ -53503,6 +53600,12 @@ entities: parent: 2 - proto: FloraTreeConifer01 entities: + - uid: 4014 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 80.01541,-17.046658 + parent: 2 - uid: 7320 components: - type: Transform @@ -53573,11 +53676,6 @@ entities: - type: Transform pos: -46.620007,3.2367477 parent: 2 - - uid: 7334 - components: - - type: Transform - pos: 79.78117,-17.17279 - parent: 2 - uid: 7335 components: - type: Transform @@ -53588,6 +53686,12 @@ entities: - type: Transform pos: 8.925767,5.7541437 parent: 2 + - uid: 12810 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 80.63917,-23.639704 + parent: 2 - proto: FloraTreeConifer02 entities: - uid: 7337 @@ -54217,11 +54321,6 @@ entities: - type: Transform pos: 72.679436,-17.541761 parent: 2 - - uid: 7460 - components: - - type: Transform - pos: 81.68742,-16.92279 - parent: 2 - uid: 7461 components: - type: Transform @@ -54237,6 +54336,18 @@ entities: - type: Transform pos: 5.2628946,14.259887 parent: 2 + - uid: 11794 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 80.09354,-18.656033 + parent: 2 + - uid: 12801 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 81.63917,-22.170954 + parent: 2 - proto: FloraTreeSnow06 entities: - uid: 7464 @@ -72104,11 +72215,6 @@ entities: - type: Transform pos: 69.5,-6.5 parent: 2 - - uid: 9975 - components: - - type: Transform - pos: 84.5,-13.5 - parent: 2 - uid: 9976 components: - type: Transform @@ -72354,6 +72460,24 @@ entities: - type: Transform pos: -15.5,64.5 parent: 2 + - uid: 11783 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 82.5,-17.5 + parent: 2 + - uid: 11784 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-17.5 + parent: 2 + - uid: 11793 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 83.5,-17.5 + parent: 2 - uid: 12427 components: - type: Transform @@ -72399,6 +72523,30 @@ entities: - type: Transform pos: 73.5,7.5 parent: 2 + - uid: 17382 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-14.5 + parent: 2 + - uid: 17383 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-13.5 + parent: 2 + - uid: 17384 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-12.5 + parent: 2 + - uid: 17385 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-11.5 + parent: 2 - proto: GrilleBroken entities: - uid: 10026 @@ -73463,7 +73611,6 @@ entities: solutions: beaker: temperature: 293.15 - canMix: False canReact: True maxVol: 100 name: null @@ -81999,16 +82146,6 @@ entities: - type: Transform pos: 75.5,-13.5 parent: 2 - - uid: 11783 - components: - - type: Transform - pos: 82.5,-13.5 - parent: 2 - - uid: 11784 - components: - - type: Transform - pos: 83.5,-13.5 - parent: 2 - uid: 11785 components: - type: Transform @@ -82049,16 +82186,6 @@ entities: - type: Transform pos: 81.5,-15.5 parent: 2 - - uid: 11793 - components: - - type: Transform - pos: 82.5,-14.5 - parent: 2 - - uid: 11794 - components: - - type: Transform - pos: 82.5,-15.5 - parent: 2 - uid: 11795 components: - type: Transform @@ -83807,7 +83934,7 @@ entities: - type: Transform pos: -7.5,-5.5 parent: 2 -- proto: Oracle +- proto: OracleSpawner entities: - uid: 12108 components: @@ -86097,12 +86224,6 @@ entities: rot: 3.141592653589793 rad pos: -6.5,24.5 parent: 2 - - uid: 12432 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 79.5,-8.5 - parent: 2 - uid: 12433 components: - type: Transform @@ -86471,6 +86592,18 @@ entities: rot: 3.141592653589793 rad pos: 56.5,31.5 parent: 2 + - uid: 17399 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 83.5,-15.5 + parent: 2 + - uid: 17400 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 83.5,-10.5 + parent: 2 - proto: PoweredLightBlueInterior entities: - uid: 9259 @@ -88545,24 +88678,6 @@ entities: rot: 3.141592653589793 rad pos: -12.5,-22.5 parent: 2 - - uid: 12801 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 84.5,-11.5 - parent: 2 - - uid: 12802 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 84.5,-9.5 - parent: 2 - - uid: 12803 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 84.5,-12.5 - parent: 2 - uid: 12804 components: - type: Transform @@ -88583,36 +88698,6 @@ entities: - type: Transform pos: 74.5,-9.5 parent: 2 - - uid: 12810 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 84.5,-10.5 - parent: 2 - - uid: 12811 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 81.5,-7.5 - parent: 2 - - uid: 12812 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 79.5,-7.5 - parent: 2 - - uid: 12813 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 82.5,-7.5 - parent: 2 - - uid: 12814 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 80.5,-7.5 - parent: 2 - uid: 12815 components: - type: Transform @@ -91492,31 +91577,108 @@ entities: - type: Transform pos: 21.5,10.5 parent: 2 -- proto: RandomVending +- proto: RandomVendingSnacks entities: - uid: 13123 components: - type: Transform + rot: 1.5707963267948966 rad pos: -15.5,-5.5 parent: 2 - uid: 13124 components: - type: Transform + rot: 3.141592653589793 rad pos: 23.5,4.5 parent: 2 -- proto: RandomVendingDrinks - entities: - uid: 13196 components: - type: Transform - pos: 52.5,36.5 + rot: 1.5707963267948966 rad + pos: 52.5,33.5 parent: 2 -- proto: RandomVendingSnacks - entities: - uid: 13220 components: - type: Transform - pos: 52.5,33.5 + rot: 1.5707963267948966 rad + pos: 52.5,36.5 + parent: 2 + - uid: 14621 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 59.5,34.5 + parent: 2 + - uid: 14628 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 24.5,24.5 + parent: 2 + - uid: 14630 + components: + - type: Transform + pos: 54.5,-10.5 + parent: 2 + - uid: 14634 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 81.5,-13.5 + parent: 2 + - uid: 14635 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 6.5,12.5 + parent: 2 + - uid: 14638 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 37.5,44.5 + parent: 2 + - uid: 14639 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 24.5,25.5 + parent: 2 + - uid: 14646 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 37.5,45.5 + parent: 2 + - uid: 14666 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 29.5,12.5 + parent: 2 + - uid: 14669 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 79.5,-13.5 + parent: 2 + - uid: 14670 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 8.5,-4.5 + parent: 2 + - uid: 17403 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 30.5,12.5 + parent: 2 + - uid: 17405 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 8.5,-5.5 parent: 2 - proto: RCD entities: @@ -91907,6 +92069,12 @@ entities: - type: Transform pos: 55.5,9.5 parent: 2 + - uid: 9975 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-17.5 + parent: 2 - uid: 12688 components: - type: Transform @@ -91917,6 +92085,12 @@ entities: - type: Transform pos: 54.5,11.5 parent: 2 + - uid: 12803 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 83.5,-17.5 + parent: 2 - uid: 13203 components: - type: Transform @@ -93175,7 +93349,8 @@ entities: - uid: 13473 components: - type: Transform - pos: 84.5,-13.5 + rot: 1.5707963267948966 rad + pos: 82.5,-17.5 parent: 2 - uid: 13474 components: @@ -93347,6 +93522,30 @@ entities: - type: Transform pos: 73.5,7.5 parent: 2 + - uid: 17378 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-11.5 + parent: 2 + - uid: 17379 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-12.5 + parent: 2 + - uid: 17380 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-13.5 + parent: 2 + - uid: 17381 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-14.5 + parent: 2 - proto: ResearchAndDevelopmentServer entities: - uid: 13502 @@ -95358,7 +95557,7 @@ entities: - type: Transform pos: -0.5,85.5 parent: 2 -- proto: SophicScribe +- proto: SophicScribeSpawner entities: - uid: 13789 components: @@ -95425,6 +95624,13 @@ entities: - type: Transform pos: -7.5,45.5 parent: 2 +- proto: SpaceHeaterEnabled + entities: + - uid: 17409 + components: + - type: Transform + pos: 83.5,-12.5 + parent: 2 - proto: SpawnMobArcticFoxSiobhan entities: - uid: 14071 @@ -95810,33 +96016,6 @@ entities: - type: Transform pos: -23.5,-10.5 parent: 2 -- proto: SpawnPointMailCarrier - entities: - - uid: 13932 - components: - - type: Transform - pos: 41.5,14.5 - parent: 2 - - uid: 13933 - components: - - type: Transform - pos: 41.5,15.5 - parent: 2 - - uid: 13934 - components: - - type: Transform - pos: 42.5,15.5 - parent: 2 - - uid: 13935 - components: - - type: Transform - pos: 44.5,15.5 - parent: 2 - - uid: 13936 - components: - - type: Transform - pos: 43.5,15.5 - parent: 2 - proto: SpawnPointDetective entities: - uid: 15402 @@ -96050,6 +96229,33 @@ entities: - type: Transform pos: -57.5,2.5 parent: 2 +- proto: SpawnPointMailCarrier + entities: + - uid: 13932 + components: + - type: Transform + pos: 41.5,14.5 + parent: 2 + - uid: 13933 + components: + - type: Transform + pos: 41.5,15.5 + parent: 2 + - uid: 13934 + components: + - type: Transform + pos: 42.5,15.5 + parent: 2 + - uid: 13935 + components: + - type: Transform + pos: 44.5,15.5 + parent: 2 + - uid: 13936 + components: + - type: Transform + pos: 43.5,15.5 + parent: 2 - proto: SpawnPointMedicalBorg entities: - uid: 16486 @@ -98851,6 +99057,14 @@ entities: - type: Transform pos: 58.5,-45.5 parent: 2 +- proto: TableFancyRed + entities: + - uid: 16249 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 80.5,-7.5 + parent: 2 - proto: TableGlass entities: - uid: 786 @@ -100842,13 +101056,6 @@ entities: - type: Transform pos: 30.5,21.5 parent: 2 -- proto: VendingMachineChang - entities: - - uid: 14621 - components: - - type: Transform - pos: 59.5,34.5 - parent: 2 - proto: VendingMachineChapel entities: - uid: 14622 @@ -100891,11 +101098,6 @@ entities: parent: 2 - proto: VendingMachineCigs entities: - - uid: 14628 - components: - - type: Transform - pos: 24.5,24.5 - parent: 2 - uid: 14629 components: - type: MetaData @@ -100903,11 +101105,6 @@ entities: - type: Transform pos: 56.5,38.5 parent: 2 - - uid: 14630 - components: - - type: Transform - pos: 54.5,-10.5 - parent: 2 - uid: 14631 components: - type: Transform @@ -100925,35 +101122,13 @@ entities: - type: Transform pos: 71.5,-20.5 parent: 2 -- proto: VendingMachineCoffee - entities: - - uid: 14634 - components: - - type: Transform - pos: 79.5,-13.5 - parent: 2 - proto: VendingMachineCola entities: - - uid: 14635 - components: - - type: Transform - pos: 6.5,12.5 - parent: 2 - uid: 14636 components: - type: Transform pos: -63.5,-8.5 parent: 2 - - uid: 14638 - components: - - type: Transform - pos: 37.5,44.5 - parent: 2 - - uid: 14639 - components: - - type: Transform - pos: 24.5,25.5 - parent: 2 - uid: 14641 components: - type: Transform @@ -101006,13 +101181,6 @@ entities: - type: Transform pos: 11.5,12.5 parent: 2 -- proto: VendingMachineDonut - entities: - - uid: 14646 - components: - - type: Transform - pos: 37.5,45.5 - parent: 2 - proto: VendingMachineEngiDrobe entities: - uid: 14647 @@ -101025,6 +101193,7 @@ entities: - uid: 14648 components: - type: Transform + rot: 3.141592653589793 rad pos: 18.5,37.5 parent: 2 - proto: VendingMachineGames @@ -101151,26 +101320,11 @@ entities: parent: 2 - proto: VendingMachineSnack entities: - - uid: 14666 - components: - - type: Transform - pos: 29.5,12.5 - parent: 2 - uid: 14667 components: - type: Transform pos: -63.5,-7.5 parent: 2 - - uid: 14669 - components: - - type: Transform - pos: 81.5,-13.5 - parent: 2 - - uid: 14670 - components: - - type: Transform - pos: 8.5,-4.5 - parent: 2 - proto: VendingMachineSovietSoda entities: - uid: 17140 @@ -101274,11 +101428,22 @@ entities: - type: Transform pos: -15.5,-6.5 parent: 2 + - uid: 17401 + components: + - type: Transform + pos: 81.5,-7.5 + parent: 2 + - uid: 17402 + components: + - type: Transform + pos: 82.5,-7.5 + parent: 2 - proto: VendingMachineYouTool entities: - uid: 14685 components: - type: Transform + rot: 3.141592653589793 rad pos: 17.5,37.5 parent: 2 - uid: 14686 @@ -106214,6 +106379,18 @@ entities: - type: Transform pos: 1.5,-10.5 parent: 2 + - uid: 7334 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 81.5,-16.5 + parent: 2 + - uid: 7460 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 81.5,-17.5 + parent: 2 - uid: 9457 components: - type: Transform @@ -108164,6 +108341,18 @@ entities: - type: Transform pos: -5.5,-6.5 parent: 2 + - uid: 16290 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-15.5 + parent: 2 + - uid: 16971 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 84.5,-10.5 + parent: 2 - uid: 17012 components: - type: Transform @@ -109667,13 +109856,6 @@ entities: parent: 2 - type: WarpPoint location: Perma - - uid: 16249 - components: - - type: Transform - pos: 64.5,36.5 - parent: 2 - - type: WarpPoint - location: Evac - uid: 16250 components: - type: Transform @@ -109730,6 +109912,14 @@ entities: parent: 2 - type: WarpPoint location: West Docks +- proto: WarpPointArrivals + entities: + - uid: 17387 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 80.5,-12.5 + parent: 2 - proto: WarpPointBombing entities: - uid: 767 @@ -110953,6 +111143,30 @@ entities: rot: 3.141592653589793 rad pos: -10.5,47.5 parent: 2 + - uid: 12811 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 81.5,-7.5 + parent: 2 + - uid: 12812 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 79.5,-7.5 + parent: 2 + - uid: 12813 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 82.5,-7.5 + parent: 2 + - uid: 12814 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 80.5,-7.5 + parent: 2 - uid: 16441 components: - type: Transform From 951e67075688a22f9c53cdc64a7f2c16c1878b2d Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sat, 19 Oct 2024 08:43:19 +0000 Subject: [PATCH 47/76] Automatic Changelog Update (#1095) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 2b13422435..4f175f014d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7371,3 +7371,10 @@ Entries: id: 6463 time: '2024-10-19T08:30:22.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1086 +- author: VMSolidus + changes: + - type: Fix + message: 'addpsionicpower command now works. ' + id: 6464 + time: '2024-10-19T08:34:03.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1095 From 9442d4087092d3b100dd4f40e2dbdffa3ac4fe3d Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 04:45:44 -0400 Subject: [PATCH 48/76] Syndicate Listening Outpost Version 2 (#1065) # Description This PR does two things. First it updates the code behind the Syndicate Listening Outpost event so that it isn't completely dogshit anymore. Thanks to my college for teaching me Trigonometry, I now actually know how to write a system like this from scratch. A key note is that the new system for spawning the outpost actually accepts a List of shuttle paths, and will randomly select a shuttle from said list to be spawned. This allows people to potentially make "Variant" listening outposts, such as a much larger 8-person outpost. It also now has support for Multi-grid or map stations, via the GetSpawnableStations function, which it uses to select a random station to spawn in reference to. This PR also makes significant updates to the ListeningPost, extending the outpost bridge to include new consoles, such as the Syndicate Communications Console, which allows the LP operators to troll the station even harder. As well as camera consoles, assuming we ever get cross-grid cameras working... The listening post also gets a new cargo dock, with concealed turret shutters inside the dock, and a window facing into the cavern entrance. The cargo dock can be docked by ships, presumably for directly exchanging supplies and equipment with the listening post. To make use of this dock, the Listening Post now has actual syndicate supplies, in the form of their surplus crates, which will serve either as loot for salvage, or supplies to be given to syndicate agents should they have some way of getting to the outpost. Woe upon a station should they ever team up with Nukies. The asteroid has also had its defenses beefed up, with turrets in various positions to guard it from spaceborne attackers. Salvage players are REALLY going to have to work to crack the outposts open and get to their goodies.

Media

![image](https://github.com/user-attachments/assets/67e1048a-1b5a-4495-a2f2-b0fcac90badb)

# Changelog :cl: - add: The Syndicate has significantly expanded their Listening Outpost operations, featuring larger, better defended outposts. These new outposts also serve as supply depots for Syndicate agents in the field. --- .../PirateRadioSpawnRuleComponent.cs | 30 - .../Events/PirateRadioSpawnRule.cs | 96 -- .../PirateRadioSpawnRuleComponent.cs | 37 + .../Events/PirateRadioSpawnRule.cs | 84 ++ .../DV-pirateradio.yml => pirateradio.yml} | 1317 ++++++++++------- .../Prototypes/DeltaV/GameRules/events.yml | 2 - 6 files changed, 932 insertions(+), 634 deletions(-) delete mode 100644 Content.Server/DeltaV/StationEvents/Components/PirateRadioSpawnRuleComponent.cs delete mode 100644 Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs create mode 100644 Content.Server/StationEvents/Components/PirateRadioSpawnRuleComponent.cs create mode 100644 Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs rename Resources/Maps/Shuttles/{DeltaV/DV-pirateradio.yml => pirateradio.yml} (91%) diff --git a/Content.Server/DeltaV/StationEvents/Components/PirateRadioSpawnRuleComponent.cs b/Content.Server/DeltaV/StationEvents/Components/PirateRadioSpawnRuleComponent.cs deleted file mode 100644 index fb4c1751f6..0000000000 --- a/Content.Server/DeltaV/StationEvents/Components/PirateRadioSpawnRuleComponent.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Content.Server.StationEvents.Events; - -namespace Content.Server.StationEvents.Components; - -[RegisterComponent, Access(typeof(PirateRadioSpawnRule))] -public sealed partial class PirateRadioSpawnRuleComponent : Component -{ - [DataField("PirateRadioShuttlePath")] - public string PirateRadioShuttlePath = "Maps/Shuttles/DeltaV/DV-pirateradio.yml"; - - [DataField("additionalRule")] - public EntityUid? AdditionalRule; - - [DataField("debrisCount")] - public int DebrisCount { get; set; } - - [DataField("distanceModifier")] - public float DistanceModifier { get; set; } - - [DataField("debrisDistanceModifier")] - public float DebrisDistanceModifier { get; set; } - - /// - /// "Stations of Unusual Size Constant", derived from the AABB.Width of Shoukou. - /// This Constant is used to check the size of a station relative to the reference point - /// - [DataField("sousk")] - public float SOUSK = 123.44f; - -} diff --git a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs b/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs deleted file mode 100644 index 43e7c3b474..0000000000 --- a/Content.Server/DeltaV/StationEvents/Events/PirateRadioSpawnRule.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Robust.Server.GameObjects; -using Robust.Server.Maps; -using Robust.Shared.Configuration; -using Robust.Shared.Map; -using Robust.Shared.Map.Components; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Utility; -using Content.Server.GameTicking; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.StationEvents.Components; -using Content.Server.Station.Components; -using Content.Shared.Salvage; -using Content.Shared.Random.Helpers; -using System.Linq; -using Content.Server.GameTicking.Components; -using Content.Shared.CCVar; - -namespace Content.Server.StationEvents.Events; - -public sealed class PirateRadioSpawnRule : StationEventSystem -{ - [Dependency] private readonly IEntityManager _entities = default!; - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly MapLoaderSystem _map = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IConfigurationManager _confMan = default!; - - protected override void Started(EntityUid uid, PirateRadioSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) - { - //Start of Syndicate Listening Outpost spawning system - base.Started(uid, component, gameRule, args); - var xformQuery = GetEntityQuery(); - var aabbs = EntityQuery().SelectMany(x => - x.Grids.Select(x => - xformQuery.GetComponent(x).WorldMatrix.TransformBox(_entities.GetComponent(x).LocalAABB))) - .ToArray(); - if (aabbs.Length < 1) return; - var aabb = aabbs[0]; - - for (var i = 1; i < aabbs.Length; i++) - { - aabb.Union(aabbs[i]); - } - var distanceFactorCoefficient = component.SOUSK / aabb.Width; - var distanceModifier = Math.Clamp(component.DistanceModifier, 1, 25); - var distanceModifierNormalized = distanceModifier * distanceFactorCoefficient; - var a = MathF.Max(aabb.Height / 2f, aabb.Width / 2f) * distanceModifierNormalized; - var randomoffset = _random.NextVector2(a, a * 2.5f); - var outpostOptions = new MapLoadOptions - { - Offset = aabb.Center + randomoffset, - LoadMap = false, - }; - if (!_map.TryLoad(GameTicker.DefaultMap, component.PirateRadioShuttlePath, out var outpostids, outpostOptions)) return; - //End of Syndicate Listening Outpost spawning system - - //Start of Debris Field Generation - var debrisSpawner = _confMan.GetCVar(CCVars.WorldgenEnabled); - if (debrisSpawner == true) return; - var debrisCount = Math.Clamp(component.DebrisCount, 0, 6); - if (debrisCount == 0) return; - var debrisDistanceModifier = Math.Clamp(component.DebrisDistanceModifier, 3, 10); - foreach (var id in outpostids) - { - if (!TryComp(id, out var grid)) return; - var outpostaabb = _entities.GetComponent(id).WorldMatrix.TransformBox(grid.LocalAABB); - var b = MathF.Max(outpostaabb.Height / 2f, aabb.Width / 2f) * debrisDistanceModifier; - var k = 1; - while (k < debrisCount + 1) - { - var debrisRandomOffset = _random.NextVector2(b, b * 2.5f); - var randomer = _random.NextVector2(b, b * 5f); //Second random vector to ensure the outpost isn't perfectly centered in the debris field - var debrisOptions = new MapLoadOptions - { - Offset = outpostaabb.Center + debrisRandomOffset + randomer, - LoadMap = false, - }; - - var salvageProto = _random.Pick(_prototypeManager.EnumeratePrototypes().ToList()); - _map.TryLoad(GameTicker.DefaultMap, salvageProto.MapPath.ToString(), out _, debrisOptions); - k++; - } - } - //End of Debris Field generation - } - - protected override void Ended(EntityUid uid, PirateRadioSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) - { - base.Ended(uid, component, gameRule, args); - - if (component.AdditionalRule != null) - GameTicker.EndGameRule(component.AdditionalRule.Value); - } -} diff --git a/Content.Server/StationEvents/Components/PirateRadioSpawnRuleComponent.cs b/Content.Server/StationEvents/Components/PirateRadioSpawnRuleComponent.cs new file mode 100644 index 0000000000..3bfa189ec6 --- /dev/null +++ b/Content.Server/StationEvents/Components/PirateRadioSpawnRuleComponent.cs @@ -0,0 +1,37 @@ +using Content.Server.StationEvents.Events; + +namespace Content.Server.StationEvents.Components; + +[RegisterComponent, Access(typeof(PirateRadioSpawnRule))] +public sealed partial class PirateRadioSpawnRuleComponent : Component +{ + [DataField] + public List PirateRadioShuttlePath { get; private set; } = new() + { + "Maps/Shuttles/pirateradio.yml", + }; + + [DataField] + public EntityUid? AdditionalRule; + + [DataField] + public int DebrisCount { get; set; } + + [DataField] + public float MinimumDistance { get; set; } = 750f; + + [DataField] + public float MaximumDistance { get; set; } = 1250f; + + [DataField] + public float MinimumDebrisDistance { get; set; } = 150f; + + [DataField] + public float MaximumDebrisDistance { get; set; } = 250f; + + [DataField] + public float DebrisMinimumOffset { get; set; } = 50f; + + [DataField] + public float DebrisMaximumOffset { get; set; } = 100f; +} diff --git a/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs b/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs new file mode 100644 index 0000000000..e080313204 --- /dev/null +++ b/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs @@ -0,0 +1,84 @@ +using Robust.Server.GameObjects; +using Robust.Server.Maps; +using Robust.Shared.Configuration; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Content.Server.GameTicking; +using Content.Server.StationEvents.Components; +using Content.Shared.Salvage; +using Content.Shared.Random.Helpers; +using System.Linq; +using Content.Server.GameTicking.Components; +using Content.Shared.CCVar; + +namespace Content.Server.StationEvents.Events; + +public sealed class PirateRadioSpawnRule : StationEventSystem +{ + [Dependency] private readonly MapLoaderSystem _map = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IConfigurationManager _confMan = default!; + [Dependency] private readonly GameTicker _gameTicker = default!; + [Dependency] private readonly TransformSystem _xform = default!; + + protected override void Started(EntityUid uid, PirateRadioSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var stations = _gameTicker.GetSpawnableStations(); + if (stations is null || stations.Count <= 0) + return; + + var targetStation = _random.Pick(stations); + var randomOffset = _random.NextVector2(component.MinimumDistance, component.MaximumDistance); + + var outpostOptions = new MapLoadOptions + { + Offset = _xform.GetWorldPosition(targetStation) + randomOffset, + LoadMap = false, + }; + + if (!_map.TryLoad(GameTicker.DefaultMap, _random.Pick(component.PirateRadioShuttlePath), out var outpostids, outpostOptions)) + return; + + SpawnDebris(component, outpostids); + } + + private void SpawnDebris(PirateRadioSpawnRuleComponent component, IReadOnlyList outpostids) + { + if (_confMan.GetCVar(CCVars.WorldgenEnabled) + || component.DebrisCount <= 0) + return; + + foreach (var id in outpostids) + { + var outpostaabb = _xform.GetWorldPosition(id); + var k = 0; + while (k < component.DebrisCount) + { + var debrisRandomOffset = _random.NextVector2(component.MinimumDebrisDistance, component.MaximumDebrisDistance); + var randomer = _random.NextVector2(component.DebrisMinimumOffset, component.DebrisMaximumOffset); //Second random vector to ensure the outpost isn't perfectly centered in the debris field + var debrisOptions = new MapLoadOptions + { + Offset = outpostaabb + debrisRandomOffset + randomer, + LoadMap = false, + }; + + var salvageProto = _random.Pick(_prototypeManager.EnumeratePrototypes().ToList()); + if (!_map.TryLoad(GameTicker.DefaultMap, salvageProto.MapPath.ToString(), out _, debrisOptions)) + return; + + k++; + } + } + } + + protected override void Ended(EntityUid uid, PirateRadioSpawnRuleComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) + { + base.Ended(uid, component, gameRule, args); + + if (component.AdditionalRule != null) + GameTicker.EndGameRule(component.AdditionalRule.Value); + } +} diff --git a/Resources/Maps/Shuttles/DeltaV/DV-pirateradio.yml b/Resources/Maps/Shuttles/pirateradio.yml similarity index 91% rename from Resources/Maps/Shuttles/DeltaV/DV-pirateradio.yml rename to Resources/Maps/Shuttles/pirateradio.yml index 9cff388fc8..4507675d91 100644 --- a/Resources/Maps/Shuttles/DeltaV/DV-pirateradio.yml +++ b/Resources/Maps/Shuttles/pirateradio.yml @@ -25,32 +25,31 @@ entities: - type: MetaData name: unknown - type: Transform - parent: invalid - type: MapGrid chunks: 0,0: ind: 0,0 - tiles: QwAAAAAAewAAAAADQQAAAAAAQQAAAAAAewAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAKQAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAKQAAAAADUAAAAAAAfgAAAAAAfgAAAAAAQwAAAAAAewAAAAAAQQAAAAAAQQAAAAAAewAAAAADQwAAAAAAWQAAAAAAQwAAAAAAKQAAAAABJgAAAAAAJgAAAAAAJgAAAAAAKQAAAAABUAAAAAAAfgAAAAAAfgAAAAAAQwAAAAAAewAAAAAAQQAAAAAAQQAAAAAAewAAAAABKQAAAAABWQAAAAAAQwAAAAAAKQAAAAADKQAAAAACKQAAAAACKQAAAAACKQAAAAACUAAAAAAAfgAAAAAAfgAAAAAAKQAAAAAAewAAAAADewAAAAABewAAAAAAewAAAAABQwAAAAAAWQAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAGQAAAAABfgAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAKQAAAAABQwAAAAAAQwAAAAAABwAAAAAABwAAAAAAGQAAAAAAGQAAAAABGQAAAAADGQAAAAADGQAAAAACGQAAAAABGQAAAAACGQAAAAAEQwAAAAAAbQAAAAAAQwAAAAAAbQAAAAAAbQAAAAAABwAAAAAAGQAAAAACGQAAAAAGGQAAAAACGQAAAAAEGQAAAAAFGQAAAAAEGQAAAAACGQAAAAAEGQAAAAABGQAAAAADQwAAAAAABwAAAAAAQwAAAAAAQwAAAAAAQwAAAAAABwAAAAAAfgAAAAAAGQAAAAADGQAAAAAAGQAAAAAGGQAAAAABGQAAAAABGQAAAAAEGQAAAAADGQAAAAAAGQAAAAADGQAAAAABGQAAAAAAGQAAAAACGQAAAAAEGQAAAAACGQAAAAAEGQAAAAACGQAAAAAEGQAAAAAAGQAAAAAFGQAAAAAEGQAAAAACGQAAAAAFGQAAAAAFGQAAAAAAGQAAAAAGGQAAAAABGQAAAAAEGQAAAAAAGQAAAAABGQAAAAAAGQAAAAAAGQAAAAABGQAAAAAGGQAAAAAAGQAAAAACGQAAAAAFGQAAAAAEGQAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAADGQAAAAAFGQAAAAADGQAAAAABGQAAAAAGGQAAAAADGQAAAAAFGQAAAAABAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFGQAAAAAEGQAAAAAAGQAAAAABfgAAAAAAGQAAAAACGQAAAAAFGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAACGQAAAAABGQAAAAAFGQAAAAAFGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAEAAAAAAAAGQAAAAAAGQAAAAAGAAAAAAAAGQAAAAACGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: QwAAAAAAewAAAAADQQAAAAAAQQAAAAAAewAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAKQAAAAAAJgAAAAAAJgAAAAAAJgAAAAAAKQAAAAADUAAAAAAAfgAAAAAAfgAAAAAAQwAAAAAAewAAAAAAQQAAAAAAQQAAAAAAewAAAAADQwAAAAAAWQAAAAAAQwAAAAAAKQAAAAABJgAAAAAAJgAAAAAAJgAAAAAAKQAAAAABUAAAAAAAfgAAAAAAfgAAAAAAQwAAAAAAewAAAAAAQQAAAAAAQQAAAAAAewAAAAABKQAAAAABWQAAAAAAQwAAAAAAKQAAAAADKQAAAAACKQAAAAACKQAAAAACKQAAAAACUAAAAAAAfgAAAAAAfgAAAAAAKQAAAAAAewAAAAADewAAAAABewAAAAAAewAAAAABQwAAAAAAWQAAAAAAQwAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAQwAAAAAAGQAAAAABfgAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAKQAAAAABQwAAAAAAQwAAAAAABwAAAAAABwAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAGQAAAAABGQAAAAACGQAAAAAEQwAAAAAAbQAAAAAAQwAAAAAAbQAAAAAAbQAAAAAABwAAAAAAGQAAAAACGQAAAAAGGQAAAAACGQAAAAAEGQAAAAAFKQAAAAAAGQAAAAACGQAAAAAEGQAAAAABGQAAAAADQwAAAAAABwAAAAAAQwAAAAAAQwAAAAAAQwAAAAAABwAAAAAAfgAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAGQAAAAADGQAAAAABGQAAAAAAGQAAAAACGQAAAAAEGQAAAAACGQAAAAAEGQAAAAACGQAAAAAEKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAGQAAAAAGGQAAAAABGQAAAAAEGQAAAAAAGQAAAAABGQAAAAAAGQAAAAAAGQAAAAABGQAAAAAGKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAKQAAAAAAGQAAAAAEGQAAAAADGQAAAAAFGQAAAAADGQAAAAABGQAAAAAGGQAAAAADGQAAAAAFGQAAAAABGQAAAAAAKQAAAAAAKQAAAAAAbQAAAAAAGQAAAAAAGQAAAAAAGQAAAAAAGQAAAAAAGQAAAAAFGQAAAAAEGQAAAAAAGQAAAAABfgAAAAAAGQAAAAACGQAAAAAFGQAAAAAAGQAAAAAAGQAAAAAAGQAAAAAAbQAAAAAAGQAAAAAAGQAAAAAAGQAAAAAAGQAAAAAAAAAAAAAAGQAAAAAEGQAAAAACGQAAAAABGQAAAAAFGQAAAAAFGQAAAAAFGQAAAAAAGQAAAAAAGQAAAAAAGQAAAAAAbQAAAAAAGQAAAAAAfgAAAAAAGQAAAAAAAAAAAAAAGQAAAAAAGQAAAAAEAAAAAAAAGQAAAAAAGQAAAAAGGQAAAAAAGQAAAAACGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 0,-1: ind: 0,-1 - tiles: GQAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGAAAAAAAAGQAAAAAGGQAAAAABAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAGQAAAAACAAAAAAAAAAAAAAAAGQAAAAABGQAAAAADAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAFGQAAAAAEGQAAAAACGQAAAAABGQAAAAAAGQAAAAABGQAAAAACGQAAAAABGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAADGQAAAAAGGQAAAAADGQAAAAABGQAAAAAAGQAAAAAFGQAAAAACGQAAAAABGQAAAAACGQAAAAAFAAAAAAAAGQAAAAACGQAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFGQAAAAAEGQAAAAAAGQAAAAAFfgAAAAAAGQAAAAABGQAAAAACGQAAAAADGQAAAAAGGQAAAAADGQAAAAAGGQAAAAAEGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAAFGQAAAAADGQAAAAAAGQAAAAAFGQAAAAAFGQAAAAAGGQAAAAAGGQAAAAABGQAAAAAEGQAAAAAEGQAAAAAEGQAAAAAFGQAAAAAFGQAAAAAEAAAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAGQAAAAABGQAAAAAEGQAAAAABGQAAAAAGGQAAAAABGQAAAAAAGQAAAAACGQAAAAABGQAAAAAEGQAAAAABGQAAAAAEGQAAAAAAWQAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAWQAAAAAAQwAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAGQAAAAAEGQAAAAADGQAAAAAGGQAAAAADGQAAAAAGGQAAAAACGQAAAAABWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAQwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAQwAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAGQAAAAAEGQAAAAABQwAAAAAAQwAAAAAAKQAAAAACQwAAAAAAQwAAAAAAQwAAAAAAewAAAAAAewAAAAACewAAAAACQwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAQwAAAAAAGQAAAAABQwAAAAAAYAAAAAACYAAAAAAAYAAAAAADYAAAAAAAQwAAAAAAewAAAAACewAAAAADewAAAAABQwAAAAAAKQAAAAAAKQAAAAABKQAAAAADKQAAAAAAQwAAAAAAGQAAAAAGQwAAAAAAYAAAAAACYAAAAAACYAAAAAAAYAAAAAABQwAAAAAAewAAAAACewAAAAABewAAAAADKQAAAAACKQAAAAAAKQAAAAACKQAAAAADKQAAAAADQwAAAAAAGQAAAAAGQwAAAAAAYAAAAAACYAAAAAADYAAAAAACYAAAAAACQwAAAAAAewAAAAACewAAAAACewAAAAAAQwAAAAAAKQAAAAADKQAAAAAAKQAAAAABKQAAAAABQwAAAAAAGQAAAAABQwAAAAAAQwAAAAAAKQAAAAACQwAAAAAAQwAAAAAAQwAAAAAAKQAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAUAAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAGQAAAAADKQAAAAACewAAAAABewAAAAABewAAAAACewAAAAADKQAAAAAAKQAAAAACKQAAAAABKQAAAAACKQAAAAABKQAAAAACKQAAAAACKQAAAAAAUAAAAAAAfgAAAAAAfgAAAAAA + tiles: GQAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGAAAAAAAAGQAAAAAGGQAAAAABAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAGQAAAAACAAAAAAAAAAAAAAAAGQAAAAABGQAAAAADAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAFGQAAAAAEGQAAAAACGQAAAAABGQAAAAAAGQAAAAABGQAAAAACGQAAAAABGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAADGQAAAAAGGQAAAAADGQAAAAABGQAAAAAAGQAAAAAFGQAAAAACGQAAAAABGQAAAAACGQAAAAAFAAAAAAAAGQAAAAACGQAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFGQAAAAAEGQAAAAAAGQAAAAAFfgAAAAAAGQAAAAABGQAAAAACGQAAAAADGQAAAAAGGQAAAAADGQAAAAAGGQAAAAAEGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAAFGQAAAAADGQAAAAAAGQAAAAAFGQAAAAAFKQAAAAAAGQAAAAAGGQAAAAABGQAAAAAEGQAAAAAEGQAAAAAEGQAAAAAFGQAAAAAFGQAAAAAEAAAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAGQAAAAABGQAAAAAEGQAAAAABGQAAAAAGGQAAAAABGQAAAAAAGQAAAAACGQAAAAABGQAAAAAEGQAAAAABGQAAAAAEGQAAAAAAWQAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAWQAAAAAAQwAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAGQAAAAAEGQAAAAADGQAAAAAGGQAAAAADGQAAAAAGGQAAAAACGQAAAAABWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAQwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAQwAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAGQAAAAAEGQAAAAABQwAAAAAAQwAAAAAAKQAAAAACQwAAAAAAQwAAAAAAQwAAAAAAewAAAAAAewAAAAACewAAAAACQwAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAUAAAAAAAQwAAAAAAGQAAAAABQwAAAAAAYAAAAAACYAAAAAAAYAAAAAADYAAAAAAAQwAAAAAAewAAAAACewAAAAADewAAAAABQwAAAAAAKQAAAAAAKQAAAAABKQAAAAADKQAAAAAAQwAAAAAAGQAAAAAGQwAAAAAAYAAAAAACYAAAAAACYAAAAAAAYAAAAAABQwAAAAAAewAAAAACewAAAAABewAAAAADKQAAAAACKQAAAAAAKQAAAAACKQAAAAADKQAAAAADQwAAAAAAGQAAAAAGQwAAAAAAYAAAAAACYAAAAAADYAAAAAACYAAAAAACQwAAAAAAewAAAAACewAAAAACewAAAAAAQwAAAAAAKQAAAAADKQAAAAAAKQAAAAABKQAAAAABQwAAAAAAGQAAAAABQwAAAAAAQwAAAAAAKQAAAAACQwAAAAAAQwAAAAAAQwAAAAAAKQAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAUAAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAGQAAAAADKQAAAAACewAAAAABewAAAAABewAAAAACewAAAAADKQAAAAAAKQAAAAACKQAAAAABKQAAAAACKQAAAAABKQAAAAACKQAAAAACKQAAAAAAUAAAAAAAfgAAAAAAfgAAAAAA version: 6 -1,0: ind: -1,0 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADAAAAAAAAGQAAAAACGQAAAAAFGQAAAAAEGQAAAAADQwAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAACGQAAAAABGQAAAAAAfgAAAAAAGQAAAAADQwAAAAAAQwAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAAGQAAAAAFGQAAAAAEQwAAAAAAQwAAAAAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAGQAAAAAGGQAAAAABGQAAAAAAGQAAAAAFQwAAAAAAbQAAAAAAbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFGQAAAAAAGQAAAAAEAAAAAAAAGQAAAAABGQAAAAAFGQAAAAAFGQAAAAADQwAAAAAAbQAAAAAAbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAAFfgAAAAAAGQAAAAACQwAAAAAAbQAAAAAAbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAAGQAAAAAEGQAAAAACGQAAAAAAGQAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAACGQAAAAAGGQAAAAAGGQAAAAAEGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAAFGQAAAAABGQAAAAAFGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAABGQAAAAAAGQAAAAACGQAAAAAFGQAAAAABGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAACGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADAAAAAAAAGQAAAAACGQAAAAAFGQAAAAAEGQAAAAADQwAAAAAAWQAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAACGQAAAAABGQAAAAAAfgAAAAAAGQAAAAADQwAAAAAAQwAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAAGQAAAAAFGQAAAAAEQwAAAAAAQwAAAAAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAGQAAAAAGGQAAAAABGQAAAAAAGQAAAAAFQwAAAAAAbQAAAAAAbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFGQAAAAAAGQAAAAAEAAAAAAAAGQAAAAABGQAAAAAFGQAAAAAFGQAAAAADQwAAAAAAbQAAAAAAbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAAFfgAAAAAAGQAAAAACQwAAAAAAbQAAAAAAbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAAGQAAAAAEGQAAAAACGQAAAAAAGQAAAAAAQwAAAAAAQwAAAAAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAACGQAAAAAGGQAAAAAGGQAAAAAEGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAAFGQAAAAABGQAAAAAFGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAABGQAAAAAAfgAAAAAAGQAAAAAFGQAAAAABGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAACGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 -1,-1: ind: -1,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEAAAAAAAAAAAAAAAAGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAACAAAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAADAAAAAAAAAAAAAAAAGQAAAAACGQAAAAACGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAGQAAAAADGQAAAAABGQAAAAADGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABGQAAAAAGGQAAAAAAGQAAAAAFGQAAAAABGQAAAAADGQAAAAAGGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAGGQAAAAAEGQAAAAAAGQAAAAAAGQAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAADGQAAAAAFGQAAAAACQwAAAAAAQwAAAAAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAAGQAAAAAAGQAAAAAAQwAAAAAAIAAAAAADIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAACGQAAAAAAGQAAAAAFGQAAAAABQwAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAAGQAAAAAAGQAAAAADGQAAAAACGQAAAAAGGQAAAAAEQwAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAABGQAAAAACGQAAAAABGQAAAAAEQwAAAAAAQwAAAAAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAAGfgAAAAAAGQAAAAAAQwAAAAAAWgAAAAAAWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAFGQAAAAABGQAAAAABGQAAAAACGQAAAAABGQAAAAAEQwAAAAAAWgAAAAAAWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAGQAAAAADGQAAAAAFQwAAAAAAKQAAAAACQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAEGQAAAAABQwAAAAAAQwAAAAAAWQAAAAAAWQAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEAAAAAAAAAAAAAAAAGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAACAAAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAADAAAAAAAAAAAAAAAAGQAAAAACGQAAAAACGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAGQAAAAADGQAAAAABGQAAAAADGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABGQAAAAAGGQAAAAAAGQAAAAAFGQAAAAABGQAAAAADGQAAAAAGGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAGGQAAAAAEGQAAAAAAGQAAAAAAGQAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAGQAAAAADGQAAAAAFGQAAAAACQwAAAAAAQwAAAAAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAAGQAAAAAAGQAAAAAAQwAAAAAAIAAAAAADIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAACGQAAAAAAGQAAAAAFGQAAAAABQwAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAAGQAAAAAAGQAAAAADGQAAAAACGQAAAAAGGQAAAAAEQwAAAAAAWQAAAAAAWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAABGQAAAAACGQAAAAABGQAAAAAEQwAAAAAAQwAAAAAAQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAAGfgAAAAAAGQAAAAAAQwAAAAAAWgAAAAAAWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAFGQAAAAABGQAAAAABGQAAAAACGQAAAAABGQAAAAAEQwAAAAAAWgAAAAAAWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAGQAAAAADGQAAAAAFQwAAAAAAKQAAAAACQwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAEGQAAAAABQwAAAAAAQwAAAAAAWQAAAAAAWQAAAAAA version: 6 1,-1: ind: 1,-1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAAFAAAAAAAAGQAAAAABGQAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAABGQAAAAACGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAGGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAAGGQAAAAABAAAAAAAAAAAAAAAAGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAGGQAAAAACAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFGQAAAAAFGQAAAAAAGQAAAAAAGQAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAADGQAAAAAFGQAAAAABGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACGQAAAAABGQAAAAADGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAAFAAAAAAAAGQAAAAABGQAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAABGQAAAAACGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAGGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAAGGQAAAAABAAAAAAAAAAAAAAAAGQAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAGfgAAAAAAfgAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFGQAAAAAFGQAAAAAAGQAAAAAAGQAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAADGQAAAAAFGQAAAAABGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACGQAAAAABGQAAAAADGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 1,0: ind: 1,0 - tiles: GQAAAAADGQAAAAABGQAAAAADGQAAAAADGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAGQAAAAAEGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAAGQAAAAAGGQAAAAADGQAAAAACGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAACGQAAAAABGQAAAAABGQAAAAAFGQAAAAAFGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAEGQAAAAADGQAAAAAAGQAAAAAAGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAAGQAAAAAGGQAAAAAFGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAADGQAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAAGQAAAAAGGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: GQAAAAADGQAAAAABGQAAAAADGQAAAAADGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAGQAAAAAEGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAAGQAAAAAGGQAAAAADGQAAAAACGQAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAADGQAAAAACGQAAAAABGQAAAAABGQAAAAAFGQAAAAAFGQAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAACGQAAAAAEGQAAAAADGQAAAAAAGQAAAAAAGQAAAAAFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAEGQAAAAAAGQAAAAAGGQAAAAAFGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAAAAAAAAAGQAAAAADGQAAAAADGQAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAGQAAAAAGGQAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAABGQAAAAAAGQAAAAAAAAAAAAAAGQAAAAAEGQAAAAAAGQAAAAAGGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAFAAAAAAAAGQAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAGQAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 - type: Broadphase - type: Physics @@ -71,11 +70,33 @@ entities: chunkCollection: version: 2 nodes: + - node: + color: '#FFFFFFFF' + id: Arrows + decals: + 59: 12,8 + - node: + angle: 3.141592653589793 rad + color: '#FFFFFFFF' + id: Arrows + decals: + 60: 12,6 + - node: + color: '#FF0000FF' + id: BoxGreyscale + decals: + 58: 13,7 - node: color: '#FFFFFFFF' id: Caution decals: 0: 3.018731,5.165427 + - node: + angle: 4.71238898038469 rad + color: '#FFFFFFFF' + id: StandClear + decals: + 54: 12,7 - node: color: '#FFFFFFFF' id: WarnBox @@ -96,6 +117,12 @@ entities: id: WarnCornerSmallSW decals: 17: 4,7 + - node: + color: '#FFFFFFFF' + id: WarnLineE + decals: + 78: 12,-10 + 80: 6,-10 - node: color: '#FFFFFFFF' id: WarnLineN @@ -120,16 +147,17 @@ entities: 42: 10,-8 43: 7,-9 44: 6,-9 + 79: 6,-10 - node: color: '#FFFFFFFF' id: WoodTrimThinCornerNe decals: - 2: 12,2 + 45: 12,4 - node: color: '#FFFFFFFF' id: WoodTrimThinCornerNw decals: - 5: 8,2 + 46: 8,4 - node: color: '#FFFFFFFF' id: WoodTrimThinCornerSe @@ -169,15 +197,17 @@ entities: 22: 1,2 23: 1,1 24: 1,0 + 50: 12,3 + 51: 12,2 - node: color: '#FFFFFFFF' id: WoodTrimThinLineN decals: - 6: 9,2 - 7: 10,2 - 8: 11,2 26: 2,-1 27: 3,-1 + 47: 9,4 + 48: 10,4 + 49: 11,4 - node: color: '#FFFFFFFF' id: WoodTrimThinLineS @@ -195,11 +225,78 @@ entities: 19: 4,2 20: 4,1 21: 4,0 + 52: 8,3 + 53: 8,2 - node: color: '#FF0000FF' id: space decals: 1: 3.018731,4.884177 + - node: + color: '#FFFFFFFF' + id: syndlogo1 + decals: + 74: 8,8 + - node: + color: '#FFFFFFFF' + id: syndlogo10 + decals: + 67: 9,6 + - node: + color: '#FFFFFFFF' + id: syndlogo11 + decals: + 68: 10,6 + - node: + color: '#FFFFFFFF' + id: syndlogo12 + decals: + 69: 11,6 + - node: + color: '#FFFFFFFF' + id: syndlogo13 + decals: + 77: 10,6 + - node: + color: '#FFFFFFFF' + id: syndlogo2 + decals: + 62: 9,8 + - node: + color: '#FFFFFFFF' + id: syndlogo3 + decals: + 63: 10,8 + - node: + color: '#FFFFFFFF' + id: syndlogo4 + decals: + 73: 11,8 + - node: + color: '#FFFFFFFF' + id: syndlogo5 + decals: + 75: 8,7 + - node: + color: '#FFFFFFFF' + id: syndlogo6 + decals: + 64: 9,7 + - node: + color: '#FFFFFFFF' + id: syndlogo7 + decals: + 65: 10,7 + - node: + color: '#FFFFFFFF' + id: syndlogo8 + decals: + 72: 11,7 + - node: + color: '#FFFFFFFF' + id: syndlogo9 + decals: + 76: 8,6 - type: GridAtmosphere version: 2 data: @@ -374,8 +471,6 @@ entities: - 384 - 716 - 724 - - type: AtmosDevice - joinedGrid: 1 - proto: AirCanister entities: - uid: 3 @@ -386,8 +481,6 @@ entities: parent: 1 - type: Physics bodyType: Static - - type: AtmosDevice - joinedGrid: 1 - proto: AirlockExternalShuttleSyndicateLocked entities: - uid: 4 @@ -403,6 +496,20 @@ entities: - 806 missingComponents: - Docking + - uid: 414 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 11.5,11.5 + parent: 1 +- proto: AirlockHatchSyndicate + entities: + - uid: 405 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 11.5,9.5 + parent: 1 - proto: AirlockSyndicate entities: - uid: 5 @@ -461,6 +568,11 @@ entities: - type: Transform pos: 3.5,4.5 parent: 1 + - uid: 401 + components: + - type: Transform + pos: 11.5,5.5 + parent: 1 - uid: 950 components: - type: MetaData @@ -500,6 +612,14 @@ entities: rot: 3.141592653589793 rad pos: 4.5,7.5 parent: 1 +- proto: AlwaysPoweredLightPostSmallRed + entities: + - uid: 1006 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 13.5,11.5 + parent: 1 - proto: AlwaysPoweredSmallLightMaintenanceRed entities: - uid: 953 @@ -554,21 +674,11 @@ entities: - type: Transform pos: 4.5,8.5 parent: 1 - - uid: 30 - components: - - type: Transform - pos: 6.5,5.5 - parent: 1 - uid: 31 components: - type: Transform pos: 5.5,8.5 parent: 1 - - uid: 32 - components: - - type: Transform - pos: 6.5,8.5 - parent: 1 - uid: 33 components: - type: Transform @@ -594,11 +704,6 @@ entities: - type: Transform pos: 14.5,4.5 parent: 1 - - uid: 38 - components: - - type: Transform - pos: 15.5,4.5 - parent: 1 - uid: 39 components: - type: Transform @@ -609,26 +714,6 @@ entities: - type: Transform pos: 2.5,8.5 parent: 1 - - uid: 41 - components: - - type: Transform - pos: 7.5,5.5 - parent: 1 - - uid: 42 - components: - - type: Transform - pos: 7.5,7.5 - parent: 1 - - uid: 43 - components: - - type: Transform - pos: 8.5,5.5 - parent: 1 - - uid: 44 - components: - - type: Transform - pos: 14.5,5.5 - parent: 1 - uid: 45 components: - type: Transform @@ -729,11 +814,6 @@ entities: - type: Transform pos: 1.5,8.5 parent: 1 - - uid: 67 - components: - - type: Transform - pos: 6.5,10.5 - parent: 1 - uid: 68 components: - type: Transform @@ -1009,71 +1089,6 @@ entities: - type: Transform pos: 16.5,5.5 parent: 1 - - uid: 125 - components: - - type: Transform - pos: 15.5,5.5 - parent: 1 - - uid: 126 - components: - - type: Transform - pos: 13.5,5.5 - parent: 1 - - uid: 127 - components: - - type: Transform - pos: 11.5,8.5 - parent: 1 - - uid: 128 - components: - - type: Transform - pos: 12.5,8.5 - parent: 1 - - uid: 129 - components: - - type: Transform - pos: 9.5,6.5 - parent: 1 - - uid: 130 - components: - - type: Transform - pos: 8.5,8.5 - parent: 1 - - uid: 131 - components: - - type: Transform - pos: 7.5,8.5 - parent: 1 - - uid: 132 - components: - - type: Transform - pos: 7.5,9.5 - parent: 1 - - uid: 133 - components: - - type: Transform - pos: 9.5,8.5 - parent: 1 - - uid: 134 - components: - - type: Transform - pos: 13.5,6.5 - parent: 1 - - uid: 135 - components: - - type: Transform - pos: 12.5,6.5 - parent: 1 - - uid: 136 - components: - - type: Transform - pos: 12.5,7.5 - parent: 1 - - uid: 137 - components: - - type: Transform - pos: 14.5,6.5 - parent: 1 - uid: 138 components: - type: Transform @@ -1384,16 +1399,6 @@ entities: - type: Transform pos: 20.5,4.5 parent: 1 - - uid: 201 - components: - - type: Transform - pos: 14.5,7.5 - parent: 1 - - uid: 202 - components: - - type: Transform - pos: 15.5,7.5 - parent: 1 - uid: 203 components: - type: Transform @@ -1424,16 +1429,6 @@ entities: - type: Transform pos: 5.5,9.5 parent: 1 - - uid: 209 - components: - - type: Transform - pos: 5.5,11.5 - parent: 1 - - uid: 210 - components: - - type: Transform - pos: 6.5,9.5 - parent: 1 - uid: 211 components: - type: Transform @@ -1499,11 +1494,6 @@ entities: - type: Transform pos: -5.5,4.5 parent: 1 - - uid: 224 - components: - - type: Transform - pos: 4.5,11.5 - parent: 1 - uid: 225 components: - type: Transform @@ -1544,11 +1534,6 @@ entities: - type: Transform pos: 9.5,-14.5 parent: 1 - - uid: 233 - components: - - type: Transform - pos: 6.5,11.5 - parent: 1 - uid: 234 components: - type: Transform @@ -1824,11 +1809,6 @@ entities: - type: Transform pos: 21.5,-4.5 parent: 1 - - uid: 289 - components: - - type: Transform - pos: 6.5,-9.5 - parent: 1 - uid: 290 components: - type: Transform @@ -1909,26 +1889,6 @@ entities: - type: Transform pos: 23.5,9.5 parent: 1 - - uid: 307 - components: - - type: Transform - pos: 11.5,10.5 - parent: 1 - - uid: 308 - components: - - type: Transform - pos: 7.5,12.5 - parent: 1 - - uid: 309 - components: - - type: Transform - pos: 7.5,13.5 - parent: 1 - - uid: 310 - components: - - type: Transform - pos: 7.5,14.5 - parent: 1 - uid: 312 components: - type: Transform @@ -1979,127 +1939,201 @@ entities: - type: Transform pos: 2.5,-9.5 parent: 1 - - uid: 322 + - uid: 326 components: - type: Transform - pos: 12.5,4.5 + pos: -3.5,4.5 parent: 1 - - uid: 323 + - uid: 334 components: - type: Transform - pos: 10.5,4.5 + pos: 0.5,-9.5 parent: 1 - - uid: 324 + - uid: 337 components: - type: Transform - pos: 13.5,4.5 + pos: 1.5,7.5 parent: 1 - - uid: 325 + - uid: 339 components: - type: Transform - pos: 12.5,5.5 + pos: 1.5,-10.5 parent: 1 - - uid: 326 + - uid: 373 components: - type: Transform - pos: -3.5,4.5 + pos: 1.5,-9.5 parent: 1 - - uid: 327 + - uid: 374 components: - type: Transform - pos: 10.5,5.5 + pos: -3.5,2.5 parent: 1 - - uid: 328 + - uid: 609 components: - type: Transform - pos: 9.5,4.5 + pos: -4.5,3.5 parent: 1 - - uid: 329 + - uid: 616 components: - type: Transform - pos: 9.5,5.5 + pos: -5.5,-3.5 + parent: 1 + - uid: 818 + components: + - type: Transform + pos: -3.5,3.5 + parent: 1 + - uid: 884 + components: + - type: Transform + pos: 17.5,-1.5 + parent: 1 +- proto: AsteroidAltRockMining + entities: + - uid: 67 + components: + - type: Transform + pos: 15.5,10.5 + parent: 1 + - uid: 127 + components: + - type: Transform + pos: 16.5,10.5 + parent: 1 + - uid: 129 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 8.5,10.5 + parent: 1 + - uid: 137 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 9.5,10.5 + parent: 1 + - uid: 186 + components: + - type: Transform + pos: -8.5,-5.5 + parent: 1 + - uid: 202 + components: + - type: Transform + pos: 7.5,12.5 + parent: 1 + - uid: 210 + components: + - type: Transform + pos: 7.5,11.5 + parent: 1 + - uid: 224 + components: + - type: Transform + pos: 6.5,12.5 + parent: 1 + - uid: 306 + components: + - type: Transform + pos: 16.5,8.5 + parent: 1 + - uid: 307 + components: + - type: Transform + pos: 5.5,12.5 + parent: 1 + - uid: 310 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 9.5,11.5 + parent: 1 + - uid: 328 + components: + - type: Transform + pos: 13.5,10.5 parent: 1 - uid: 330 components: - type: Transform - pos: 11.5,5.5 + pos: 16.5,9.5 parent: 1 - uid: 331 components: - type: Transform - pos: 10.5,6.5 + rot: 1.5707963267948966 rad + pos: 8.5,11.5 parent: 1 - - uid: 333 + - uid: 383 components: - type: Transform - pos: 11.5,4.5 + pos: 16.5,7.5 parent: 1 - - uid: 334 + - uid: 412 components: - type: Transform - pos: 0.5,-9.5 + pos: 14.5,11.5 parent: 1 - - uid: 337 + - uid: 413 components: - type: Transform - pos: 1.5,7.5 + pos: 14.5,10.5 parent: 1 - - uid: 339 + - uid: 415 components: - type: Transform - pos: 1.5,-10.5 + pos: 16.5,6.5 parent: 1 - - uid: 364 + - uid: 418 components: - type: Transform - pos: 8.5,4.5 + pos: 17.5,7.5 parent: 1 - - uid: 372 + - uid: 419 components: - type: Transform - pos: 11.5,6.5 + pos: 17.5,9.5 parent: 1 - - uid: 373 + - uid: 420 components: - type: Transform - pos: 1.5,-9.5 + pos: 17.5,11.5 parent: 1 - - uid: 374 + - uid: 536 components: - type: Transform - pos: -3.5,2.5 + pos: 18.5,11.5 parent: 1 - - uid: 609 + - uid: 540 components: - type: Transform - pos: -4.5,3.5 + pos: 16.5,11.5 parent: 1 - - uid: 616 + - uid: 581 components: - type: Transform - pos: -5.5,-3.5 + pos: 15.5,9.5 parent: 1 - - uid: 818 + - uid: 582 components: - type: Transform - pos: -3.5,3.5 + pos: 15.5,8.5 parent: 1 - - uid: 884 + - uid: 583 components: - type: Transform - pos: 17.5,-1.5 + pos: 15.5,7.5 parent: 1 -- proto: AsteroidAltRockMining - entities: - - uid: 186 + - uid: 584 components: - type: Transform - pos: -8.5,-5.5 + pos: 15.5,6.5 parent: 1 - - uid: 306 + - uid: 585 components: - type: Transform - pos: 16.5,8.5 + pos: 15.5,5.5 parent: 1 - proto: AtmosDeviceFanTiny entities: @@ -2168,13 +2202,20 @@ entities: rot: -1.5707963267948966 rad pos: 8.5,-5.5 parent: 1 - - uid: 984 + - uid: 541 components: - type: Transform - pos: 11.5,2.5 + rot: -1.5707963267948966 rad + pos: 12.5,3.5 parent: 1 - proto: BenchSofaCorpRight entities: + - uid: 402 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 12.5,4.5 + parent: 1 - uid: 404 components: - type: Transform @@ -2187,13 +2228,32 @@ entities: rot: -1.5707963267948966 rad pos: 8.5,-4.5 parent: 1 - - uid: 985 +- proto: BlastDoorOpen + entities: + - uid: 324 components: - type: Transform - pos: 10.5,2.5 + pos: 12.5,7.5 parent: 1 -- proto: BlastDoorOpen - entities: + - type: DeviceLinkSink + links: + - 593 + - uid: 325 + components: + - type: Transform + pos: 12.5,6.5 + parent: 1 + - type: DeviceLinkSink + links: + - 593 + - uid: 329 + components: + - type: Transform + pos: 12.5,8.5 + parent: 1 + - type: DeviceLinkSink + links: + - 593 - uid: 350 components: - type: Transform @@ -2346,20 +2406,31 @@ entities: - type: InsideEntityStorage - proto: BriefcaseBrownFilled entities: - - uid: 401 + - uid: 403 components: - type: Transform - pos: 12.580331,-0.57071495 + pos: 12.615925,-0.21887398 parent: 1 - - uid: 402 +- proto: BriefcaseSyndieLobbyingBundleFilled + entities: + - uid: 371 components: - type: Transform - pos: 12.615925,0.015501022 + pos: 12.604369,-0.012328386 parent: 1 - - uid: 403 +- proto: BriefcaseSyndieSniperBundleFilled + entities: + - uid: 370 components: - type: Transform - pos: 12.615925,-0.21887398 + pos: 12.604369,-0.3873284 + parent: 1 +- proto: ButtonFrameCautionSecurity + entities: + - uid: 592 + components: + - type: Transform + pos: 12.5,5.5 parent: 1 - proto: CableApcExtension entities: @@ -2638,6 +2709,66 @@ entities: - type: Transform pos: 9.5,2.5 parent: 1 + - uid: 993 + components: + - type: Transform + pos: 9.5,1.5 + parent: 1 + - uid: 994 + components: + - type: Transform + pos: 9.5,2.5 + parent: 1 + - uid: 995 + components: + - type: Transform + pos: 9.5,3.5 + parent: 1 + - uid: 996 + components: + - type: Transform + pos: 9.5,4.5 + parent: 1 + - uid: 997 + components: + - type: Transform + pos: 10.5,4.5 + parent: 1 + - uid: 998 + components: + - type: Transform + pos: 11.5,4.5 + parent: 1 + - uid: 999 + components: + - type: Transform + pos: 11.5,5.5 + parent: 1 + - uid: 1000 + components: + - type: Transform + pos: 11.5,6.5 + parent: 1 + - uid: 1001 + components: + - type: Transform + pos: 11.5,7.5 + parent: 1 + - uid: 1002 + components: + - type: Transform + pos: 11.5,8.5 + parent: 1 + - uid: 1003 + components: + - type: Transform + pos: 11.5,9.5 + parent: 1 + - uid: 1004 + components: + - type: Transform + pos: 11.5,10.5 + parent: 1 - proto: CableHV entities: - uid: 493 @@ -2896,6 +3027,36 @@ entities: rot: 1.5707963267948966 rad pos: 8.5,-4.5 parent: 1 + - uid: 594 + components: + - type: Transform + pos: 10.5,2.5 + parent: 1 + - uid: 595 + components: + - type: Transform + pos: 9.5,2.5 + parent: 1 + - uid: 596 + components: + - type: Transform + pos: 9.5,3.5 + parent: 1 + - uid: 597 + components: + - type: Transform + pos: 10.5,3.5 + parent: 1 + - uid: 598 + components: + - type: Transform + pos: 11.5,3.5 + parent: 1 + - uid: 620 + components: + - type: Transform + pos: 11.5,2.5 + parent: 1 - uid: 960 components: - type: Transform @@ -3003,17 +3164,35 @@ entities: parent: 1 - proto: ChairOfficeDark entities: - - uid: 535 + - uid: 369 components: - type: Transform rot: -1.5707963267948966 rad - pos: 9.5,1.5 + pos: 9.383302,3.471712 parent: 1 - - uid: 536 + - uid: 372 components: - type: Transform rot: 1.5707963267948966 rad - pos: 11.5,1.5 + pos: 11.762909,1.1363623 + parent: 1 + - uid: 409 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 8.407137,7.5838184 + parent: 1 + - uid: 410 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 11.684784,2.2144873 + parent: 1 + - uid: 535 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 9.5,1.5 parent: 1 - proto: CigPackSyndicate entities: @@ -3027,55 +3206,6 @@ entities: - type: Transform pos: 4.7236505,1.7666011 parent: 1 -- proto: ClosetWallBlack - entities: - - uid: 405 - components: - - type: MetaData - desc: A closet packed with forged stamps - name: stamp closet - - type: Transform - pos: 10.5,3.5 - parent: 1 - - type: EntityStorage - air: - volume: 200 - immutable: False - temperature: 293.14673 - moles: - - 1.7459903 - - 6.568249 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - type: ContainerContainer - containers: - entity_storage: !type:Container - showEnts: False - occludes: True - ents: - - 411 - - 410 - - 409 - - 408 - - 407 - - 406 - - 412 - - 413 - - 414 - - 415 - - 416 - - 417 - - 418 - - 419 - - 420 - proto: ClosetWallMixed entities: - uid: 388 @@ -3172,15 +3302,17 @@ entities: - type: InsideEntityStorage - proto: ClothingMaskGasVoiceChameleon entities: - - uid: 540 + - uid: 549 components: - type: Transform - pos: 11.346417,2.446512 + rot: -1.5707963267948966 rad + pos: 12.464487,4.225065 parent: 1 - - uid: 541 + - uid: 578 components: - type: Transform - pos: 10.971417,2.462137 + rot: -1.5707963267948966 rad + pos: 12.464487,3.6313155 parent: 1 - proto: ClothingUniformJumpsuitPyjamaSyndicateBlack entities: @@ -3280,6 +3412,21 @@ entities: maxRange: 2048 - type: WorldLoader radius: 2048 +- proto: ComputerSurveillanceCameraMonitor + entities: + - uid: 991 + components: + - type: Transform + pos: 10.5,4.5 + parent: 1 +- proto: ComputerSurveillanceWirelessCameraMonitor + entities: + - uid: 406 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 12.5,2.5 + parent: 1 - proto: CrateFoodDonkpocketSavory entities: - uid: 551 @@ -3312,6 +3459,18 @@ entities: - type: Transform pos: -1.5,4.5 parent: 1 +- proto: CrateSyndicateSurplusBundle + entities: + - uid: 411 + components: + - type: Transform + pos: 9.5,6.5 + parent: 1 + - uid: 992 + components: + - type: Transform + pos: 10.5,6.5 + parent: 1 - proto: CrowbarRed entities: - uid: 378 @@ -3489,8 +3648,6 @@ entities: - 625 - 627 - 633 - - type: AtmosDevice - joinedGrid: 1 - proto: Firelock entities: - uid: 622 @@ -3604,8 +3761,6 @@ entities: rot: 1.5707963267948966 rad pos: -5.5,1.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 635 @@ -3613,8 +3768,6 @@ entities: - type: Transform pos: 6.5,6.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 636 @@ -3622,8 +3775,6 @@ entities: - type: Transform pos: 4.5,10.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 637 @@ -3632,8 +3783,6 @@ entities: rot: -1.5707963267948966 rad pos: 14.5,-0.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 638 @@ -3642,8 +3791,6 @@ entities: rot: 3.141592653589793 rad pos: 13.5,-6.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 639 @@ -3652,8 +3799,6 @@ entities: rot: 3.141592653589793 rad pos: 8.5,-7.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 640 @@ -3662,8 +3807,6 @@ entities: rot: 3.141592653589793 rad pos: 4.5,-10.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 641 @@ -3672,8 +3815,6 @@ entities: rot: 1.5707963267948966 rad pos: -4.5,-3.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 642 @@ -3682,8 +3823,6 @@ entities: rot: 1.5707963267948966 rad pos: -4.5,5.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - proto: GasPipeBend @@ -4184,8 +4323,6 @@ entities: - type: Transform pos: 1.5,5.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - proto: GasVentPump @@ -4198,8 +4335,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - uid: 707 @@ -4211,8 +4346,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - uid: 708 @@ -4223,8 +4356,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - uid: 709 @@ -4236,8 +4367,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - uid: 710 @@ -4249,8 +4378,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - uid: 711 @@ -4262,8 +4389,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - uid: 712 @@ -4275,8 +4400,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - uid: 713 @@ -4288,8 +4411,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - uid: 714 @@ -4301,8 +4422,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - uid: 716 @@ -4314,8 +4433,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#0335FCFF' - uid: 717 @@ -4324,8 +4441,6 @@ entities: rot: 1.5707963267948966 rad pos: -0.5,-3.5 parent: 1 - - type: AtmosDevice - joinedGrid: 1 - proto: GasVentScrubber entities: - uid: 718 @@ -4337,8 +4452,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 719 @@ -4350,8 +4463,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 720 @@ -4363,8 +4474,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 721 @@ -4376,8 +4485,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 722 @@ -4388,8 +4495,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 723 @@ -4400,8 +4505,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 724 @@ -4412,8 +4515,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 725 @@ -4425,8 +4526,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - uid: 726 @@ -4438,8 +4537,6 @@ entities: - type: DeviceNetwork deviceLists: - 576 - - type: AtmosDevice - joinedGrid: 1 - type: AtmosPipeColor color: '#FF1212FF' - proto: GeneratorWallmountAPU @@ -4483,6 +4580,22 @@ entities: parent: 1 - proto: Grille entities: + - uid: 130 + components: + - type: Transform + pos: 7.5,7.5 + parent: 1 + - uid: 309 + components: + - type: Transform + pos: 7.5,8.5 + parent: 1 + - uid: 588 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 7.5,6.5 + parent: 1 - uid: 735 components: - type: Transform @@ -4543,6 +4656,20 @@ entities: - type: Transform pos: 11.5,-5.5 parent: 1 +- proto: GrilleDiagonal + entities: + - uid: 589 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 8.5,6.5 + parent: 1 + - uid: 590 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 8.5,8.5 + parent: 1 - proto: HospitalCurtainsOpen entities: - uid: 747 @@ -4596,6 +4723,16 @@ entities: parent: 1 - proto: LandMineExplosive entities: + - uid: 364 + components: + - type: Transform + pos: 5.269056,11.251331 + parent: 1 + - uid: 579 + components: + - type: Transform + pos: 6.784681,11.813831 + parent: 1 - uid: 756 components: - type: Transform @@ -4874,14 +5011,6 @@ entities: rot: -1.5707963267948966 rad pos: -0.5,-4.5 parent: 1 -- proto: PosterLegitNoERP - entities: - - uid: 578 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 11.5,3.5 - parent: 1 - proto: PowerCellRecharger entities: - uid: 768 @@ -4974,6 +5103,11 @@ entities: - type: DeviceLinkSink links: - 46 + - uid: 1005 + components: + - type: Transform + pos: 10.5,8.5 + parent: 1 - proto: PoweredLightColoredRed entities: - uid: 20 @@ -4985,14 +5119,6 @@ entities: - type: DeviceLinkSink links: - 46 - - uid: 22 - components: - - type: Transform - pos: 12.5,2.5 - parent: 1 - - type: DeviceLinkSink - links: - - 46 - uid: 23 components: - type: Transform @@ -5109,10 +5235,16 @@ entities: parent: 1 - proto: RandomPosterContraband entities: + - uid: 417 + components: + - type: Transform + pos: 9.5,5.5 + parent: 1 - uid: 780 components: - type: Transform - pos: 9.5,3.5 + rot: -1.5707963267948966 rad + pos: 10.5,10.5 parent: 1 - uid: 781 components: @@ -5140,6 +5272,12 @@ entities: - type: Transform pos: 5.5,-3.5 parent: 1 + - uid: 827 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 12.5,10.5 + parent: 1 - proto: RandomVendingDrinks entities: - uid: 787 @@ -5158,6 +5296,22 @@ entities: - type: Emagged - proto: ReinforcedPlasmaWindow entities: + - uid: 38 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 7.5,6.5 + parent: 1 + - uid: 289 + components: + - type: Transform + pos: 7.5,7.5 + parent: 1 + - uid: 308 + components: + - type: Transform + pos: 7.5,8.5 + parent: 1 - uid: 789 components: - type: Transform @@ -5218,141 +5372,139 @@ entities: - type: Transform pos: 8.5,-6.5 parent: 1 +- proto: ReinforcedPlasmaWindowDiagonal + entities: + - uid: 586 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 8.5,8.5 + parent: 1 + - uid: 587 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 8.5,6.5 + parent: 1 - proto: RubberStampApproved entities: - - uid: 407 + - uid: 830 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage + pos: 8.336427,4.768587 + parent: 1 - proto: RubberStampCaptain entities: - - uid: 417 + - uid: 836 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage + pos: 8.570802,4.612337 + parent: 1 - proto: RubberStampCE entities: - - uid: 413 + - uid: 880 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage + pos: 8.883302,4.831087 + parent: 1 +- proto: RubberStampCentcom + entities: + - uid: 831 + components: + - type: Transform + pos: 8.602052,4.815462 + parent: 1 - proto: RubberStampChaplain entities: - - uid: 416 + - uid: 837 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage + pos: 8.539552,4.440462 + parent: 1 +- proto: RubberStampChiefJustice + entities: + - uid: 903 + components: + - type: Transform + pos: 8.836427,4.612337 + parent: 1 - proto: RubberStampClown entities: - - uid: 414 + - uid: 905 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage + pos: 9.164552,4.815462 + parent: 1 - proto: RubberStampCMO entities: - - uid: 412 + - uid: 904 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage + pos: 8.789552,4.502962 + parent: 1 - proto: RubberStampDenied entities: - - uid: 411 + - uid: 835 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: RubberStampHop + pos: 8.305177,4.549837 + parent: 1 +- proto: RubberStampDetective entities: - - uid: 419 + - uid: 912 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: RubberStampHos + pos: 9.133302,4.674837 + parent: 1 +- proto: RubberStampHop entities: - - uid: 409 + - uid: 919 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: RubberStampMantis + pos: 9.055177,4.518587 + parent: 1 +- proto: RubberStampHos entities: - - uid: 415 + - uid: 961 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: RubberStampMime + pos: 9.430177,4.862337 + parent: 1 +- proto: RubberStampLawyer entities: - - uid: 420 + - uid: 984 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: RubberStampQm + pos: 9.398927,4.706087 + parent: 1 +- proto: RubberStampMantis entities: - - uid: 408 + - uid: 986 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: RubberStampRd + pos: 9.633302,4.799837 + parent: 1 +- proto: RubberStampMime entities: - - uid: 418 + - uid: 987 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: RubberStampSyndicate + pos: 9.570802,4.581087 + parent: 1 +- proto: RubberStampQm entities: - - uid: 410 + - uid: 985 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage -- proto: RubberStampWarden + pos: 9.320802,4.565462 + parent: 1 +- proto: RubberStampRd entities: - - uid: 406 + - uid: 988 components: - type: Transform - parent: 405 - - type: Physics - canCollide: False - - type: InsideEntityStorage + pos: 9.773927,4.784212 + parent: 1 - proto: SalvageLorePaperGamingSpawner entities: - uid: 801 @@ -5383,6 +5535,21 @@ entities: parent: 1 - proto: SignalButton entities: + - uid: 593 + components: + - type: MetaData + name: turret shutters + - type: Transform + pos: 12.5,5.5 + parent: 1 + - type: DeviceLinkSource + linkedPorts: + 329: + - Pressed: Toggle + 324: + - Pressed: Toggle + 325: + - Pressed: Toggle - uid: 805 components: - type: MetaData @@ -5506,9 +5673,6 @@ entities: 19: - On: On - Off: Off - 22: - - On: Off - - Off: On - proto: SinkStemlessWater entities: - uid: 808 @@ -5603,6 +5767,14 @@ entities: - 0 - 0 - 0 +- proto: SyndicateComputerComms + entities: + - uid: 715 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 8.5,3.5 + parent: 1 - proto: SyndicateMonitoringServer entities: - uid: 338 @@ -5681,12 +5853,6 @@ entities: rot: 3.141592653589793 rad pos: 8.5,0.5 parent: 1 - - uid: 383 - components: - - type: Transform - rot: 3.141592653589793 rad - pos: 12.5,2.5 - parent: 1 - uid: 385 components: - type: Transform @@ -5711,6 +5877,18 @@ entities: rot: -1.5707963267948966 rad pos: 4.5,1.5 parent: 1 + - uid: 828 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 8.5,4.5 + parent: 1 + - uid: 829 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 9.5,4.5 + parent: 1 - proto: TelecomServerFilled entities: - uid: 612 @@ -5755,27 +5933,6 @@ entities: - type: Transform pos: 4.5330567,5.310578 parent: 1 -- proto: ToyFigurineNukie - entities: - - uid: 835 - components: - - type: Transform - pos: 12.208444,2.8347092 - parent: 1 -- proto: ToyFigurineNukieCommander - entities: - - uid: 836 - components: - - type: Transform - pos: 12.791773,2.838885 - parent: 1 -- proto: ToyFigurineNukieElite - entities: - - uid: 837 - components: - - type: Transform - pos: 12.489692,2.4701266 - parent: 1 - proto: ToyNuke entities: - uid: 838 @@ -5839,15 +5996,111 @@ entities: parent: 1 - proto: WallPlastitanium entities: + - uid: 22 + components: + - type: Transform + pos: 7.5,5.5 + parent: 1 + - uid: 30 + components: + - type: Transform + pos: 9.5,5.5 + parent: 1 + - uid: 41 + components: + - type: Transform + pos: 10.5,5.5 + parent: 1 + - uid: 42 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 10.5,11.5 + parent: 1 + - uid: 43 + components: + - type: Transform + pos: 12.5,9.5 + parent: 1 + - uid: 44 + components: + - type: Transform + pos: 8.5,9.5 + parent: 1 - uid: 54 components: - type: Transform pos: 5.5,-8.5 parent: 1 + - uid: 125 + components: + - type: Transform + pos: 13.5,5.5 + parent: 1 + - uid: 126 + components: + - type: Transform + pos: 13.5,9.5 + parent: 1 + - uid: 132 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 9.5,9.5 + parent: 1 + - uid: 133 + components: + - type: Transform + pos: 12.5,5.5 + parent: 1 + - uid: 134 + components: + - type: Transform + pos: 14.5,6.5 + parent: 1 + - uid: 135 + components: + - type: Transform + pos: 14.5,7.5 + parent: 1 + - uid: 136 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 10.5,9.5 + parent: 1 + - uid: 201 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 14.5,9.5 + parent: 1 + - uid: 209 + components: + - type: Transform + pos: 8.5,5.5 + parent: 1 + - uid: 322 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 12.5,11.5 + parent: 1 + - uid: 323 + components: + - type: Transform + pos: 13.5,4.5 + parent: 1 + - uid: 327 + components: + - type: Transform + pos: 12.5,10.5 + parent: 1 - uid: 332 components: - type: Transform - pos: 10.5,3.5 + rot: 1.5707963267948966 rad + pos: 10.5,10.5 parent: 1 - uid: 335 components: @@ -5864,6 +6117,17 @@ entities: - type: Transform pos: 0.5,-8.5 parent: 1 + - uid: 407 + components: + - type: Transform + pos: 14.5,8.5 + parent: 1 + - uid: 416 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 14.5,5.5 + parent: 1 - uid: 425 components: - type: Transform @@ -6049,11 +6313,6 @@ entities: - type: Transform pos: 0.5,-4.5 parent: 1 - - uid: 880 - components: - - type: Transform - pos: 12.5,3.5 - parent: 1 - uid: 881 components: - type: Transform @@ -6154,16 +6413,6 @@ entities: - type: Transform pos: 0.5,-2.5 parent: 1 - - uid: 903 - components: - - type: Transform - pos: 11.5,3.5 - parent: 1 - - uid: 905 - components: - - type: Transform - pos: 9.5,3.5 - parent: 1 - uid: 906 components: - type: Transform @@ -6194,11 +6443,6 @@ entities: - type: Transform pos: 7.5,3.5 parent: 1 - - uid: 912 - components: - - type: Transform - pos: 8.5,3.5 - parent: 1 - uid: 913 components: - type: Transform @@ -6334,6 +6578,24 @@ entities: - type: Transform pos: -3.5,-0.5 parent: 1 +- proto: WallPlastitaniumDiagonal + entities: + - uid: 128 + components: + - type: Transform + pos: 13.5,6.5 + parent: 1 + - uid: 333 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 13.5,8.5 + parent: 1 + - uid: 591 + components: + - type: Transform + pos: 7.5,9.5 + parent: 1 - proto: WarpPoint entities: - uid: 942 @@ -6381,6 +6643,49 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: WeaponTurretSyndicate + entities: + - uid: 32 + components: + - type: Transform + pos: 15.5,4.5 + parent: 1 + - uid: 131 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 18.5,-3.5 + parent: 1 + - uid: 233 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: 6.5,-9.5 + parent: 1 + - uid: 408 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: 6.5,5.5 + parent: 1 + - uid: 580 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: 13.5,7.5 + parent: 1 + - uid: 989 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,-8.5 + parent: 1 + - uid: 990 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -3.5,9.5 + parent: 1 - proto: Windoor entities: - uid: 618 diff --git a/Resources/Prototypes/DeltaV/GameRules/events.yml b/Resources/Prototypes/DeltaV/GameRules/events.yml index 84a39e4c04..db727601f8 100644 --- a/Resources/Prototypes/DeltaV/GameRules/events.yml +++ b/Resources/Prototypes/DeltaV/GameRules/events.yml @@ -83,5 +83,3 @@ duration: 1 - type: PirateRadioSpawnRule debrisCount: 6 - distanceModifier: 13 - debrisDistanceModifier: 3 From 057febb7f952754504eb9bf0872b323cfe4b1ee0 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sat, 19 Oct 2024 08:47:04 +0000 Subject: [PATCH 49/76] Automatic Changelog Update (#1065) --- Resources/Changelog/Changelog.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 4f175f014d..3c167cc895 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7378,3 +7378,13 @@ Entries: id: 6464 time: '2024-10-19T08:34:03.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1095 +- author: VMSolidus + changes: + - type: Add + message: >- + The Syndicate has significantly expanded their Listening Outpost + operations, featuring larger, better defended outposts. These new + outposts also serve as supply depots for Syndicate agents in the field. + id: 6465 + time: '2024-10-19T08:45:44.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1065 From ba171b935fb0251a2464099544d59ce5473e6df5 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 17:28:15 -0400 Subject: [PATCH 50/76] Fix Steal Hos Weapon Objective (#1084) # Description Closes #1076 This fixes the "Steal The HoS's weapon" objective by first making it a SINGLE objective that allows to stealing any of the HoS Weapons, regardless of which one was selected. It also toggles on the "VerifyMapExistence" setting for this objective, so that it will correctly not generate if the station's Head of Security either isn't present, or simply chose not to take an antique weapon to the station(I don't know why they would do this, but it's an option). # Changelog :cl: - fix: Fixed the "Steal The Head of Security's Weapon" so that it is now a single objective that counts for any anique weapon chosen by the Head of Security. The objective will also once again correctly not generate if there are no valid items for it on the station. --- .../Catalog/Fills/Items/Belts/belts.yml | 2 +- .../Weapons/Guns/Battery/battery_guns.yml | 2 +- .../DeltaV/Objectives/stealTargetGroups.yml | 49 ---------- .../Prototypes/DeltaV/Objectives/traitor.yml | 91 ------------------- .../Weapons/Guns/Battery/battery_guns.yml | 2 +- .../Objects/Weapons/Guns/SMGs/smgs.yml | 6 +- .../Weapons/Guns/Shotguns/shotguns.yml | 2 +- .../Objects/Weapons/Melee/e_sword.yml | 2 +- .../Objectives/stealTargetGroups.yml | 7 ++ Resources/Prototypes/Objectives/traitor.yml | 16 +++- 10 files changed, 30 insertions(+), 149 deletions(-) diff --git a/Resources/Prototypes/DeltaV/Catalog/Fills/Items/Belts/belts.yml b/Resources/Prototypes/DeltaV/Catalog/Fills/Items/Belts/belts.yml index 2e9d2f7ff4..141349e827 100644 --- a/Resources/Prototypes/DeltaV/Catalog/Fills/Items/Belts/belts.yml +++ b/Resources/Prototypes/DeltaV/Catalog/Fills/Items/Belts/belts.yml @@ -16,7 +16,7 @@ description: An ornate belt, wrapped in gold filigree, with a ribbon made from a stasis-field preserved swatch of linen. The history of this sheath has been lost to time. components: - type: StealTarget - stealGroup: ClothingBeltKatanaSheathFilledHoS + stealGroup: HoSAntiqueWeapon - type: entity id: ClothingBeltCorpsmanWebbingFilled diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 9ff83bfd48..20947d3bc9 100644 --- a/Resources/Prototypes/DeltaV/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -114,7 +114,7 @@ - type: StaticPrice price: 750 - type: StealTarget - stealGroup: WeaponEnergyGunMultiphase + stealGroup: HoSAntiqueWeapon - type: entity name: miniature energy gun diff --git a/Resources/Prototypes/DeltaV/Objectives/stealTargetGroups.yml b/Resources/Prototypes/DeltaV/Objectives/stealTargetGroups.yml index 1236decde9..53022ce236 100644 --- a/Resources/Prototypes/DeltaV/Objectives/stealTargetGroups.yml +++ b/Resources/Prototypes/DeltaV/Objectives/stealTargetGroups.yml @@ -12,55 +12,6 @@ sprite: DeltaV/Objects/Misc/bureaucracy.rsi state: folder-hop-ian -- type: stealTargetGroup - id: WeaponEnergyGunMultiphase - name: x-01 multiphase energy gun - sprite: - sprite: DeltaV/Objects/Weapons/Guns/Battery/multiphase_energygun.rsi - state: base - -- type: stealTargetGroup - id: WeaponPulsePistolHoS - name: antique pulse pistol - sprite: - sprite: Objects/Weapons/Guns/Battery/pulse_pistol.rsi - state: base - -- type: stealTargetGroup - id: WeaponSubMachineGunWt550HoS - name: antique Wt550 - sprite: - sprite: Objects/Weapons/Guns/SMGs/wt550.rsi - state: base - -- type: stealTargetGroup - id: WeaponSubMachineGunC20rHoS - name: antique C20r - sprite: - sprite: Objects/Weapons/Guns/SMGs/c20r.rsi - state: base - -- type: stealTargetGroup - id: WeaponShotgunBulldogHoS - name: antique C20r - sprite: - sprite: Objects/Weapons/Guns/Shotguns/bulldog.rsi - state: base - -- type: stealTargetGroup - id: EnergySwordHoS - name: antique energy sword - sprite: - sprite: Objects/Weapons/Melee/e_sword.rsi - state: e_sword - -- type: stealTargetGroup - id: ClothingBeltKatanaSheathFilledHoS - name: antique katana sheaths - sprite: - sprite: Nyanotrasen/Clothing/Belt/katanasheath.rsi - state: sheath - - type: stealTargetGroup id: RubberStampNotary name: notary stamp diff --git a/Resources/Prototypes/DeltaV/Objectives/traitor.yml b/Resources/Prototypes/DeltaV/Objectives/traitor.yml index b753aaf165..c5c5318b5d 100644 --- a/Resources/Prototypes/DeltaV/Objectives/traitor.yml +++ b/Resources/Prototypes/DeltaV/Objectives/traitor.yml @@ -20,97 +20,6 @@ stealGroup: BookIanDossier # owner: job-name-hop -- type: entity # Head of Security steal objective. - noSpawn: true - parent: BaseTraitorStealObjective - id: HoSGunStealObjective - components: - - type: Objective - difficulty: 3 # HoS will mostly be using the gun to stop you from stealing it - - type: NotJobRequirement - job: HeadOfSecurity - - type: StealCondition - stealGroup: WeaponEnergyGunMultiphase - owner: job-name-hos - -- type: entity # Head of Security steal objective. - noSpawn: true - parent: BaseTraitorStealObjective - id: HoSPulsePistolStealObjective - components: - - type: Objective - difficulty: 3 # HoS will mostly be using the gun to stop you from stealing it - - type: NotJobRequirement - job: HeadOfSecurity - - type: StealCondition - stealGroup: WeaponPulsePistolHoS - owner: job-name-hos - -- type: entity # Head of Security steal objective. - noSpawn: true - parent: BaseTraitorStealObjective - id: HoSWt550StealObjective - components: - - type: Objective - difficulty: 3 # HoS will mostly be using the gun to stop you from stealing it - - type: NotJobRequirement - job: HeadOfSecurity - - type: StealCondition - stealGroup: WeaponSubMachineGunWt550HoS - owner: job-name-hos - -- type: entity # Head of Security steal objective. - noSpawn: true - parent: BaseTraitorStealObjective - id: HoSC20rStealObjective - components: - - type: Objective - difficulty: 3 # HoS will mostly be using the gun to stop you from stealing it - - type: NotJobRequirement - job: HeadOfSecurity - - type: StealCondition - stealGroup: WeaponSubMachineGunC20rHoS - owner: job-name-hos - -- type: entity # Head of Security steal objective. - noSpawn: true - parent: BaseTraitorStealObjective - id: HoSBulldogStealObjective - components: - - type: Objective - difficulty: 3 # HoS will mostly be using the gun to stop you from stealing it - - type: NotJobRequirement - job: HeadOfSecurity - - type: StealCondition - stealGroup: WeaponShotgunBulldogHoS - owner: job-name-hos - -- type: entity # Head of Security steal objective. - noSpawn: true - parent: BaseTraitorStealObjective - id: HoSEnergySwordStealObjective - components: - - type: Objective - difficulty: 3 # HoS will mostly be using the gun to stop you from stealing it - - type: NotJobRequirement - job: HeadOfSecurity - - type: StealCondition - stealGroup: EnergySwordHoS - owner: job-name-hos - -- type: entity # Head of Security steal objective. - noSpawn: true - parent: BaseTraitorStealObjective - id: HoSKatanaSheathStealObjective - components: - - type: Objective - difficulty: 3 # HoS will mostly be using the gun to stop you from stealing it - - type: NotJobRequirement - job: HeadOfSecurity - - type: StealCondition - stealGroup: ClothingBeltKatanaSheathFilledHoS - owner: job-name-hos - - type: entity # Clerk steal objective. noSpawn: true parent: BaseTraitorStealObjective diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 7c281e1386..8f062a8620 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -275,7 +275,7 @@ description: One of the many weapons belonging to the Head of Security's private collection. This pistol is engraved with the words, "Forgive Us, Mother Sol'" components: - type: StealTarget - stealGroup: WeaponPulsePistolHoS + stealGroup: HoSAntiqueWeapon - type: entity name: pulse carbine diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml index 6abece7bc3..dd2a047dab 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml @@ -100,7 +100,7 @@ maxAngle: -16 - type: Gun minAngle: 21 - maxAngle: 32 + maxAngle: 32 shotsPerBurst: 5 availableModes: - SemiAuto @@ -124,7 +124,7 @@ description: This heavily worn submachine gun is engraved with the words, "Remember Mars". components: - type: StealTarget - stealGroup: WeaponSubMachineGunC20rHoS + stealGroup: HoSAntiqueWeapon - type: entity name: Drozd @@ -294,7 +294,7 @@ description: A prized possession of the station's Head of Security. The smell of dried blood lingers on this weapon's muzzle. A small torch with 24 stars surrounding it has been engraved on the grip. components: - type: StealTarget - stealGroup: WeaponSubMachineGunWt550HoS + stealGroup: HoSAntiqueWeapon # Rubber - type: entity diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml index 72154621e8..2ca2337bf9 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml @@ -110,7 +110,7 @@ description: This is a seemingly ordinary shotgun, no different from those issued as standard in the Republic of Biesel Navy. A close inspection reveals that this weapon's serial number is 000000013. components: - type: StealTarget - stealGroup: WeaponShotgunBulldogHoS + stealGroup: HoSAntiqueWeapon - type: entity name: double-barreled shotgun diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index c29a4ae3e7..37b2b03d9e 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -94,7 +94,7 @@ colorOptions: - "#00CCFF" - type: StealTarget - stealGroup: EnergySwordHoS + stealGroup: HoSAntiqueWeapon - type: entity name: pen diff --git a/Resources/Prototypes/Objectives/stealTargetGroups.yml b/Resources/Prototypes/Objectives/stealTargetGroups.yml index 5995278433..9c56881bd8 100644 --- a/Resources/Prototypes/Objectives/stealTargetGroups.yml +++ b/Resources/Prototypes/Objectives/stealTargetGroups.yml @@ -91,6 +91,13 @@ sprite: Objects/Misc/nukedisk.rsi state: icon +- type: stealTargetGroup + id: HoSAntiqueWeapon + name: head of security's personal weapon + sprite: + sprite: Objects/Weapons/Guns/Battery/pulse_pistol.rsi + state: base + # Thief Collection - type: stealTargetGroup diff --git a/Resources/Prototypes/Objectives/traitor.yml b/Resources/Prototypes/Objectives/traitor.yml index 554ccad264..b00d12529a 100644 --- a/Resources/Prototypes/Objectives/traitor.yml +++ b/Resources/Prototypes/Objectives/traitor.yml @@ -33,6 +33,20 @@ - type: ObjectiveLimit limit: 2 # there is usually only 1 of each steal objective, have 2 max for drama +- type: entity # Head of Security steal objective. + noSpawn: true + parent: BaseTraitorStealObjective + id: HoSAntiqueWeaponStealObjective + components: + - type: Objective + difficulty: 3 # HoS will mostly be using the gun to stop you from stealing it + - type: NotJobRequirement + job: HeadOfSecurity + - type: StealCondition + verifyMapExistence: true + stealGroup: HoSAntiqueWeapon + owner: job-name-hos + # state - type: entity @@ -323,4 +337,4 @@ - type: StealCondition stealGroup: SupermatterSliver objectiveNoOwnerText: objective-condition-steal-smsliver-title - descriptionText: objective-condition-steal-smsliver-description \ No newline at end of file + descriptionText: objective-condition-steal-smsliver-description From f3542848f8095506fa3c960f9cc424d93642770a Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sat, 19 Oct 2024 21:28:39 +0000 Subject: [PATCH 51/76] Automatic Changelog Update (#1084) --- Resources/Changelog/Changelog.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3c167cc895..5fe1c0eb10 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7388,3 +7388,14 @@ Entries: id: 6465 time: '2024-10-19T08:45:44.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1065 +- author: VMSolidus + changes: + - type: Fix + message: >- + Fixed the "Steal The Head of Security's Weapon" so that it is now a + single objective that counts for any anique weapon chosen by the Head of + Security. The objective will also once again correctly not generate if + there are no valid items for it on the station. + id: 6466 + time: '2024-10-19T21:28:16.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1084 From 15a7be0c3462a1465f05e924af8a57078fd7407b Mon Sep 17 00:00:00 2001 From: gluesniffler <159397573+gluesniffler@users.noreply.github.com> Date: Sat, 19 Oct 2024 17:28:50 -0400 Subject: [PATCH 52/76] Adds TC To Listening Post Operatives (#1062) # Description Spiritual successor to an old unintended interaction in Delta V, where LP ops would spawn with uplinks. Except instead of having objectives its just an uplink with 20TC. Why? Because it turns LP Ops from a simple nuisance into a potential threat that can send support on station for you, and makes taking a trip to the outpost to cash in some TC a very viable strategy. Salvage might be more inclined to raid them too, so thats gonna be fun. # Changelog :cl: Mocho - add: Added an uplink with 20TC for all Listening Post Operatives --- Resources/Prototypes/DeltaV/Roles/Jobs/Fun/misc_startinggear.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/Resources/Prototypes/DeltaV/Roles/Jobs/Fun/misc_startinggear.yml b/Resources/Prototypes/DeltaV/Roles/Jobs/Fun/misc_startinggear.yml index 7d7943ab69..db5e717a01 100644 --- a/Resources/Prototypes/DeltaV/Roles/Jobs/Fun/misc_startinggear.yml +++ b/Resources/Prototypes/DeltaV/Roles/Jobs/Fun/misc_startinggear.yml @@ -62,6 +62,7 @@ gloves: ClothingHandsGlovesCombat shoes: ClothingShoesSlippers id: SyndiListeningPostPDA + pocket1: BaseUplinkRadio20TC innerClothingSkirt: ClothingUniformJumpsuitPyjamaSyndicatePink # Mobsters From 1184e10d8b776defb9c8aee58cefb8948589e26f Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sat, 19 Oct 2024 21:29:15 +0000 Subject: [PATCH 53/76] Automatic Changelog Update (#1062) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 5fe1c0eb10..7a41c3f071 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7399,3 +7399,10 @@ Entries: id: 6466 time: '2024-10-19T21:28:16.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1084 +- author: Mocho + changes: + - type: Add + message: Added an uplink with 20TC for all Listening Post Operatives + id: 6467 + time: '2024-10-19T21:28:50.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1062 From 21b7a8249b8a53f0ce0a5a101c1966d7d09b7d00 Mon Sep 17 00:00:00 2001 From: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> Date: Sun, 20 Oct 2024 00:10:51 +0200 Subject: [PATCH 54/76] Job Whitelist System (#1093) # Description This PR Port the following PRs: https://github.com/space-wizards/space-station-14/pull/28085 and https://github.com/DeltaV-Station/Delta-v/pull/1678 This adds a simple job whitelist system, DB Migration correctly done, tested and working so far. Why now? i need it. --- # TODO - [x] Job WhiteList - [x] Working with current RequirementSystem - [x] DeltaV - Job Whitelist Panel Port ---

Media

![image](https://github.com/user-attachments/assets/599b9018-aabe-4e5e-be86-dd2fd462c22c) ![image](https://github.com/user-attachments/assets/3eb5cf1e-b27d-40f7-b747-53fb298b6ff6)

--- # Changelog :cl: - add: Job Whitelist! --- .../UI/DepartmentWhitelistPanel.xaml | 11 + .../UI/DepartmentWhitelistPanel.xaml.cs | 49 + .../Administration/UI/JobWhitelistsEui.cs | 40 + .../UI/JobWhitelistsWindow.xaml | 11 + .../UI/JobWhitelistsWindow.xaml.cs | 46 + Content.Client/LateJoin/LateJoinGui.cs | 19 +- Content.Client/Lobby/LobbyUIController.cs | 14 +- .../Lobby/UI/HumanoidProfileEditor.xaml.cs | 11 +- .../JobRequirementsManager.cs | 28 +- Content.IntegrationTests/PoolManager.Cvars.cs | 1 + .../20241018043329_RoleWhitelist.Designer.cs | 1896 +++++++++++++++++ .../Postgres/20241018043329_RoleWhitelist.cs | 40 + .../PostgresServerDbContextModelSnapshot.cs | 31 + .../20241018043307_RoleWhitelist.Designer.cs | 1823 ++++++++++++++++ .../Sqlite/20241018043307_RoleWhitelist.cs | 40 + .../SqliteServerDbContextModelSnapshot.cs | 31 + Content.Server.Database/Model.cs | 38 +- .../Commands/JobWhitelistCommands.cs | 214 ++ .../Commands/JobWhitelistsCommand.cs | 61 + .../Administration/JobWhitelistsEui.cs | 90 + .../Administration/Managers/BanManager.cs | 8 +- .../Administration/Managers/IBanManager.cs | 4 +- Content.Server/Database/ServerDbBase.cs | 89 +- Content.Server/Database/ServerDbManager.cs | 44 +- Content.Server/Database/UserDbDataManager.cs | 49 +- Content.Server/Entry/EntryPoint.cs | 2 + .../Events/GetDisallowedJobsEvent.cs | 8 + .../GameTicking/Events/IsJobAllowedEvent.cs | 13 + .../GameTicking/GameTicker.Spawning.cs | 20 +- Content.Server/IoC/ServerContentIoC.cs | 2 + .../JobWhitelist/JobWhitelistManager.cs | 114 + .../JobWhitelist/JobWhitelistSystem.cs | 83 + .../PlayTimeTrackingManager.cs | 9 +- .../PlayTimeTrackingSystem.cs | 30 +- .../Managers/ServerPreferencesManager.cs | 12 +- .../Events/StationJobsGetCandidatesEvent.cs | 8 + .../Systems/StationJobsSystem.Roundstart.cs | 8 +- .../Station/Systems/StationJobsSystem.cs | 2 +- .../Administration/JobWhitelistsEuiState.cs | 35 + Content.Shared/CCVar/CCVars.cs | 7 + .../CharacterRequirements.Whitelist.cs | 1 + .../Players/JobWhitelist/MsgJobWhitelist.cs | 33 + Content.Shared/Roles/JobPrototype.cs | 3 + .../en-US/administration/ui/player-panel.ftl | 1 + .../en-US/commands/job-whitelist-command.ftl | 18 + Resources/Locale/en-US/info/whitelists.ftl | 3 + Resources/Locale/en-US/job/role-whitelist.ftl | 1 + 47 files changed, 5030 insertions(+), 71 deletions(-) create mode 100644 Content.Client/Administration/UI/DepartmentWhitelistPanel.xaml create mode 100644 Content.Client/Administration/UI/DepartmentWhitelistPanel.xaml.cs create mode 100644 Content.Client/Administration/UI/JobWhitelistsEui.cs create mode 100644 Content.Client/Administration/UI/JobWhitelistsWindow.xaml create mode 100644 Content.Client/Administration/UI/JobWhitelistsWindow.xaml.cs create mode 100644 Content.Server.Database/Migrations/Postgres/20241018043329_RoleWhitelist.Designer.cs create mode 100644 Content.Server.Database/Migrations/Postgres/20241018043329_RoleWhitelist.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20241018043307_RoleWhitelist.Designer.cs create mode 100644 Content.Server.Database/Migrations/Sqlite/20241018043307_RoleWhitelist.cs create mode 100644 Content.Server/Administration/Commands/JobWhitelistCommands.cs create mode 100644 Content.Server/Administration/Commands/JobWhitelistsCommand.cs create mode 100644 Content.Server/Administration/JobWhitelistsEui.cs create mode 100644 Content.Server/GameTicking/Events/GetDisallowedJobsEvent.cs create mode 100644 Content.Server/GameTicking/Events/IsJobAllowedEvent.cs create mode 100644 Content.Server/Players/JobWhitelist/JobWhitelistManager.cs create mode 100644 Content.Server/Players/JobWhitelist/JobWhitelistSystem.cs create mode 100644 Content.Server/Station/Events/StationJobsGetCandidatesEvent.cs create mode 100644 Content.Shared/Administration/JobWhitelistsEuiState.cs create mode 100644 Content.Shared/Players/JobWhitelist/MsgJobWhitelist.cs create mode 100644 Resources/Locale/en-US/administration/ui/player-panel.ftl create mode 100644 Resources/Locale/en-US/commands/job-whitelist-command.ftl create mode 100644 Resources/Locale/en-US/info/whitelists.ftl create mode 100644 Resources/Locale/en-US/job/role-whitelist.ftl diff --git a/Content.Client/Administration/UI/DepartmentWhitelistPanel.xaml b/Content.Client/Administration/UI/DepartmentWhitelistPanel.xaml new file mode 100644 index 0000000000..d5f77aedd5 --- /dev/null +++ b/Content.Client/Administration/UI/DepartmentWhitelistPanel.xaml @@ -0,0 +1,11 @@ + + + + + + diff --git a/Content.Client/Administration/UI/DepartmentWhitelistPanel.xaml.cs b/Content.Client/Administration/UI/DepartmentWhitelistPanel.xaml.cs new file mode 100644 index 0000000000..275055daf6 --- /dev/null +++ b/Content.Client/Administration/UI/DepartmentWhitelistPanel.xaml.cs @@ -0,0 +1,49 @@ +using Content.Shared.Roles; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; + +namespace Content.Client.Administration.UI; + +[GenerateTypedNameReferences] +public sealed partial class DepartmentWhitelistPanel : PanelContainer +{ + public Action, bool>? OnSetJob; + + public DepartmentWhitelistPanel(DepartmentPrototype department, IPrototypeManager proto, HashSet> whitelists) + { + RobustXamlLoader.Load(this); + + var allWhitelisted = true; + var grey = Color.FromHex("#ccc"); + foreach (var id in department.Roles) + { + var thisJob = id; // closure capturing funny + var button = new CheckBox(); + button.Text = proto.Index(id).LocalizedName; + if (!proto.Index(id).Whitelisted) + button.Modulate = grey; // Let admins know whitelisting this job is only for futureproofing. + button.Pressed = whitelists.Contains(id); + button.OnPressed += _ => OnSetJob?.Invoke(thisJob, button.Pressed); + JobsContainer.AddChild(button); + + allWhitelisted &= button.Pressed; + } + + Department.Text = Loc.GetString(department.ID); + Department.Modulate = department.Color; + Department.Pressed = allWhitelisted; + Department.OnPressed += args => + { + foreach (var id in department.Roles) + { + // only request to whitelist roles that aren't already whitelisted, and vice versa + if (whitelists.Contains(id) != Department.Pressed) + OnSetJob?.Invoke(id, Department.Pressed); + } + }; + } +} diff --git a/Content.Client/Administration/UI/JobWhitelistsEui.cs b/Content.Client/Administration/UI/JobWhitelistsEui.cs new file mode 100644 index 0000000000..b8fe974c0a --- /dev/null +++ b/Content.Client/Administration/UI/JobWhitelistsEui.cs @@ -0,0 +1,40 @@ +using Content.Client.Eui; +using Content.Shared.Administration; +using Content.Shared.Eui; + +namespace Content.Client.Administration.UI; + +public sealed class JobWhitelistsEui : BaseEui +{ + private JobWhitelistsWindow Window; + + public JobWhitelistsEui() + { + Window = new JobWhitelistsWindow(); + Window.OnClose += () => SendMessage(new CloseEuiMessage()); + Window.OnSetJob += (id, whitelisted) => SendMessage(new SetJobWhitelistedMessage(id, whitelisted)); + } + + public override void HandleState(EuiStateBase state) + { + if (state is not JobWhitelistsEuiState cast) + return; + + Window.HandleState(cast); + } + + public override void Opened() + { + base.Opened(); + + Window.OpenCentered(); + } + + public override void Closed() + { + base.Closed(); + + Window.Close(); + Window.Dispose(); + } +} diff --git a/Content.Client/Administration/UI/JobWhitelistsWindow.xaml b/Content.Client/Administration/UI/JobWhitelistsWindow.xaml new file mode 100644 index 0000000000..165f5ac3d7 --- /dev/null +++ b/Content.Client/Administration/UI/JobWhitelistsWindow.xaml @@ -0,0 +1,11 @@ + + + + diff --git a/Content.Client/Administration/UI/JobWhitelistsWindow.xaml.cs b/Content.Client/Administration/UI/JobWhitelistsWindow.xaml.cs new file mode 100644 index 0000000000..51fb5287dc --- /dev/null +++ b/Content.Client/Administration/UI/JobWhitelistsWindow.xaml.cs @@ -0,0 +1,46 @@ +using Content.Client.UserInterface.Controls; +using Content.Shared.Database; +using Content.Shared.Administration; +using Content.Shared.Roles; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Prototypes; + +namespace Content.Client.Administration.UI; + +/// +/// An admin panel to toggle whitelists for individual jobs or entire departments. +/// This should generally be preferred to a blanket whitelist (Whitelisted: True) since +/// being good with a batong doesn't mean you know engineering and vice versa. +/// +[GenerateTypedNameReferences] +public sealed partial class JobWhitelistsWindow : FancyWindow +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + + public Action, bool>? OnSetJob; + + public JobWhitelistsWindow() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + PlayerName.Text = "???"; + } + + public void HandleState(JobWhitelistsEuiState state) + { + PlayerName.Text = state.PlayerName; + + Departments.RemoveAllChildren(); + foreach (var proto in _proto.EnumeratePrototypes()) + { + var panel = new DepartmentWhitelistPanel(proto, _proto, state.Whitelists); + panel.OnSetJob += (id, whitelisting) => OnSetJob?.Invoke(id, whitelisting); + Departments.AddChild(panel); + } + } +} diff --git a/Content.Client/LateJoin/LateJoinGui.cs b/Content.Client/LateJoin/LateJoinGui.cs index 1b2a87be72..e474f0c942 100644 --- a/Content.Client/LateJoin/LateJoinGui.cs +++ b/Content.Client/LateJoin/LateJoinGui.cs @@ -261,7 +261,24 @@ private void RebuildUI() jobButton.OnPressed += _ => SelectedId.Invoke((id, jobButton.JobId)); - if (!_characterRequirements.CheckRequirementsValid( + if (!_jobRequirements.CheckJobWhitelist(prototype, out var reason)) + { + jobButton.Disabled = true; + + var tooltip = new Tooltip(); + tooltip.SetMessage(reason); + jobButton.TooltipSupplier = _ => tooltip; + + jobSelector.AddChild(new TextureRect + { + TextureScale = new Vector2(0.4f, 0.4f), + Stretch = TextureRect.StretchMode.KeepCentered, + Texture = _sprites.Frame0(new SpriteSpecifier.Texture(new ("/Textures/Interface/Nano/lock.svg.192dpi.png"))), + HorizontalExpand = true, + HorizontalAlignment = HAlignment.Right, + }); + } + else if (!_characterRequirements.CheckRequirementsValid( prototype.Requirements ?? new(), prototype, (HumanoidCharacterProfile) (_prefs.Preferences?.SelectedCharacter diff --git a/Content.Client/Lobby/LobbyUIController.cs b/Content.Client/Lobby/LobbyUIController.cs index ccefbb0aa4..26643cb603 100644 --- a/Content.Client/Lobby/LobbyUIController.cs +++ b/Content.Client/Lobby/LobbyUIController.cs @@ -63,12 +63,8 @@ public override void Initialize() _requirements.Updated += OnRequirementsUpdated; _configurationManager.OnValueChanged(CCVars.FlavorText, _ => _profileEditor?.RefreshFlavorText()); - _configurationManager.OnValueChanged(CCVars.GameRoleTimers, - _ => - { - _profileEditor?.RefreshAntags(); - _profileEditor?.RefreshJobs(); - }); + _configurationManager.OnValueChanged(CCVars.GameRoleTimers, _ => RefreshProfileEditor()); + _configurationManager.OnValueChanged(CCVars.GameRoleWhitelist, _ => RefreshProfileEditor()); _preferencesManager.OnServerDataLoaded += PreferencesDataLoaded; } @@ -168,6 +164,12 @@ private void RefreshLobbyPreview() PreviewPanel.SetSummaryText(humanoid.Summary); } + private void RefreshProfileEditor() + { + _profileEditor?.RefreshAntags(); + _profileEditor?.RefreshJobs(); + } + private void SaveProfile() { DebugTools.Assert(EditedProfile != null); diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index 012d894aee..7b02f1591e 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -770,7 +770,9 @@ public void RefreshJobs() icon.Texture = jobIcon.Icon.Frame0(); selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon); - if (!_characterRequirementsSystem.CheckRequirementsValid( + if (!_requirements.CheckJobWhitelist(job, out var reason)) + selector.LockRequirements(reason); + else if (!_characterRequirementsSystem.CheckRequirementsValid( job.Requirements ?? new(), job, Profile ?? HumanoidCharacterProfile.DefaultWithSpecies(), @@ -916,7 +918,9 @@ private void UpdateRoleRequirements() icon.Texture = jobIcon.Icon.Frame0(); selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon); - if (!_characterRequirementsSystem.CheckRequirementsValid( + if (!_requirements.CheckJobWhitelist(job, out var reason)) + selector.LockRequirements(reason); + else if (!_characterRequirementsSystem.CheckRequirementsValid( job.Requirements ?? new(), job, Profile ?? HumanoidCharacterProfile.DefaultWithSpecies(), @@ -972,6 +976,7 @@ private void EnsureJobRequirementsValid() { var proto = _prototypeManager.Index(jobId); if ((JobPriority) selector.Selected == JobPriority.Never + || _requirements.CheckJobWhitelist(proto, out _) || _characterRequirementsSystem.CheckRequirementsValid( proto.Requirements ?? new(), proto, @@ -1437,7 +1442,7 @@ private void UpdateWeight() var avg = (Profile.Width + Profile.Height) / 2; var weight = MathF.Round(MathF.PI * MathF.Pow(radius * avg, 2) * density); WeightLabel.Text = Loc.GetString("humanoid-profile-editor-weight-label", ("weight", (int) weight)); - } + } else // Whelp, the fixture doesn't exist, guesstimate it instead WeightLabel.Text = Loc.GetString("humanoid-profile-editor-weight-label", ("weight", (int) 71)); diff --git a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs index a2f8061d05..286358b85e 100644 --- a/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs +++ b/Content.Client/Players/PlayTimeTracking/JobRequirementsManager.cs @@ -1,6 +1,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Shared.CCVar; using Content.Shared.Customization.Systems; +using Content.Shared.Players.JobWhitelist; using Content.Shared.Players; using Content.Shared.Players.PlayTimeTracking; using Content.Shared.Roles; @@ -18,13 +19,13 @@ public sealed partial class JobRequirementsManager : ISharedPlaytimeManager { [Dependency] private readonly IBaseClient _client = default!; [Dependency] private readonly IClientNetManager _net = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IPrototypeManager _prototypes = default!; private readonly Dictionary _roles = new(); private readonly List _roleBans = new(); - private ISawmill _sawmill = default!; - + private readonly List _jobWhitelists = new(); public event Action? Updated; public void Initialize() @@ -35,6 +36,7 @@ public void Initialize() _net.RegisterNetMessage(RxRoleBans); _net.RegisterNetMessage(RxPlayTime); _net.RegisterNetMessage(RxWhitelist); + _net.RegisterNetMessage(RxJobWhitelist); _client.RunLevelChanged += ClientOnRunLevelChanged; } @@ -78,6 +80,28 @@ private void RxPlayTime(MsgPlayTime message) Updated?.Invoke(); } + private void RxJobWhitelist(MsgJobWhitelist message) + { + _jobWhitelists.Clear(); + _jobWhitelists.AddRange(message.Whitelist); + Updated?.Invoke(); + } + + public bool CheckJobWhitelist(JobPrototype job, [NotNullWhen(false)] out FormattedMessage? reason) + { + reason = default; + if (!_cfg.GetCVar(CCVars.GameRoleWhitelist)) + return true; + + if (job.Whitelisted && !_jobWhitelists.Contains(job.ID)) + { + reason = FormattedMessage.FromUnformatted(Loc.GetString("role-not-whitelisted")); + return false; + } + + return true; + } + public TimeSpan FetchOverallPlaytime() { return _roles.TryGetValue("Overall", out var overallPlaytime) ? overallPlaytime : TimeSpan.Zero; diff --git a/Content.IntegrationTests/PoolManager.Cvars.cs b/Content.IntegrationTests/PoolManager.Cvars.cs index aa6b4dffdc..5acd9d502c 100644 --- a/Content.IntegrationTests/PoolManager.Cvars.cs +++ b/Content.IntegrationTests/PoolManager.Cvars.cs @@ -21,6 +21,7 @@ private static readonly (string cvar, string value)[] TestCvars = (CCVars.NPCMaxUpdates.Name, "999999"), (CVars.ThreadParallelCount.Name, "1"), (CCVars.GameRoleTimers.Name, "false"), + (CCVars.GameRoleWhitelist.Name, "false"), (CCVars.GridFill.Name, "false"), (CCVars.PreloadGrids.Name, "false"), (CCVars.ArrivalsShuttles.Name, "false"), diff --git a/Content.Server.Database/Migrations/Postgres/20241018043329_RoleWhitelist.Designer.cs b/Content.Server.Database/Migrations/Postgres/20241018043329_RoleWhitelist.Designer.cs new file mode 100644 index 0000000000..159af4d192 --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20241018043329_RoleWhitelist.Designer.cs @@ -0,0 +1,1896 @@ +// +using System; +using System.Net; +using System.Text.Json; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +#nullable disable + +namespace Content.Server.Database.Migrations.Postgres +{ + [DbContext(typeof(PostgresServerDbContext))] + [Migration("20241018043329_RoleWhitelist")] + partial class RoleWhitelist + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Title") + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminId") + .HasColumnType("uuid") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("boolean") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Id") + .HasColumnType("integer") + .HasColumnName("admin_log_id"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("smallint") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("text") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("integer") + .HasColumnName("type"); + + b.HasKey("RoundId", "Id") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Message") + .HasAnnotation("Npgsql:TsVectorConfig", "english"); + + NpgsqlIndexBuilderExtensions.HasMethod(b.HasIndex("Message"), "GIN"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("LogId") + .HasColumnType("integer") + .HasColumnName("log_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.HasKey("RoundId", "LogId", "PlayerUserId") + .HasName("PK_admin_log_player"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_log_player_player_user_id"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_messages_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("Dismissed") + .HasColumnType("boolean") + .HasColumnName("dismissed"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("boolean") + .HasColumnName("seen"); + + b.HasKey("Id") + .HasName("PK_admin_messages"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_messages_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_messages_round_id"); + + b.ToTable("admin_messages", null, t => + { + t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_notes_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("boolean") + .HasColumnName("secret"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_rank_flag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminRankId") + .HasColumnType("integer") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("admin_watchlists_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("uuid") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("boolean") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("uuid") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("character varying(4096)") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_admin_watchlists"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_watchlists_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_watchlists_round_id"); + + b.ToTable("admin_watchlists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("antag_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("assigned_user_id_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("ban_template_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("interval") + .HasColumnName("length"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.Property("Title") + .IsRequired() + .HasColumnType("text") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_ban_template"); + + b.ToTable("ban_template", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("connection_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("smallint") + .HasColumnName("denied"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("timestamp with time zone") + .HasColumnName("time"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_connection_log_server_id"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("job_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("JobName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("integer") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Loadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("loadout_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("LoadoutName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("loadout_name"); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_loadout"); + + b.HasIndex("ProfileId", "LoadoutName") + .IsUnique(); + + b.ToTable("loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("play_time_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("PlayerId") + .HasColumnType("uuid") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("interval") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("text") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("player_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FirstSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("inet") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenHWId") + .HasColumnType("bytea") + .HasColumnName("last_seen_hwid"); + + b.Property("LastSeenTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", null, t => + { + t.HasCheckConstraint("LastSeenAddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= last_seen_address"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("preference_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("integer") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("profile_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Age") + .HasColumnType("integer") + .HasColumnName("age"); + + b.Property("Backpack") + .IsRequired() + .HasColumnType("text") + .HasColumnName("backpack"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("char_name"); + + b.Property("Clothing") + .IsRequired() + .HasColumnType("text") + .HasColumnName("clothing"); + + b.Property("CustomSpecieName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("custom_specie_name"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("text") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("text") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("hair_name"); + + b.Property("Height") + .HasColumnType("real") + .HasColumnName("height"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("PreferenceId") + .HasColumnType("integer") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("integer") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("text") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("text") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("integer") + .HasColumnName("slot"); + + b.Property("SpawnPriority") + .HasColumnType("integer") + .HasColumnName("spawn_priority"); + + b.Property("Species") + .IsRequired() + .HasColumnType("text") + .HasColumnName("species"); + + b.Property("Width") + .HasColumnType("real") + .HasColumnName("width"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("text") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("round_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ServerId") + .HasColumnType("integer") + .HasColumnName("server_id"); + + b.Property("StartDate") + .HasColumnType("timestamp with time zone") + .HasColumnName("start_date"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.HasIndex("StartDate"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("boolean") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("integer") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_ban_round_id"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("integer") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_ban_hit_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("integer") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("server_role_ban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Address") + .HasColumnType("inet") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("uuid") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("bytea") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("boolean") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("timestamp with time zone") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("uuid") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("interval") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("text") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text") + .HasColumnName("role_id"); + + b.Property("RoundId") + .HasColumnType("integer") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("integer") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_role_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_role_ban_round_id"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("AddressNotIPv6MappedIPv4", "NOT inet '::ffff:0.0.0.0/96' >>= address"); + + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("role_unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("unban_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BanId") + .HasColumnType("integer") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("timestamp with time zone") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("uuid") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("trait_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ProfileId") + .HasColumnType("integer") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("text") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasColumnName("uploaded_resource_log_id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Data") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("timestamp with time zone") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("text") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("uuid") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("integer") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("integer") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("RoundId", "LogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminMessagesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminMessagesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminMessagesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminMessagesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_messages_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_messages_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminWatchlistsCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminWatchlistsDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminWatchlistsLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminWatchlistsReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_watchlists_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_watchlists_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("ConnectionLogs") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired() + .HasConstraintName("FK_connection_log_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Loadout", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Loadouts") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_loadout_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerRoleBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerRoleBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_role_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminServerRoleBansCreated"); + + b.Navigation("AdminServerRoleBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/20241018043329_RoleWhitelist.cs b/Content.Server.Database/Migrations/Postgres/20241018043329_RoleWhitelist.cs new file mode 100644 index 0000000000..13cd23a7fc --- /dev/null +++ b/Content.Server.Database/Migrations/Postgres/20241018043329_RoleWhitelist.cs @@ -0,0 +1,40 @@ +#nullable disable + +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Content.Server.Database.Migrations.Postgres +{ + /// + public partial class RoleWhitelist : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "role_whitelists", + columns: table => new + { + player_user_id = table.Column(type: "uuid", nullable: false), + role_id = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_role_whitelists", x => new { x.player_user_id, x.role_id }); + table.ForeignKey( + name: "FK_role_whitelists_player_player_user_id", + column: x => x.player_user_id, + principalTable: "player", + principalColumn: "user_id", + onDelete: ReferentialAction.Cascade); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "role_whitelists"); + } + } +} diff --git a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs index 4a13022fec..0282063649 100644 --- a/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Postgres/PostgresServerDbContextModelSnapshot.cs @@ -917,6 +917,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("profile", (string)null); }); + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("uuid") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("text") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.Round", b => { b.Property("Id") @@ -1616,6 +1632,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Preference"); }); + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + modelBuilder.Entity("Content.Server.Database.Round", b => { b.HasOne("Content.Server.Database.Server", "Server") @@ -1815,6 +1844,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("AdminWatchlistsLastEdited"); b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); }); modelBuilder.Entity("Content.Server.Database.Preference", b => diff --git a/Content.Server.Database/Migrations/Sqlite/20241018043307_RoleWhitelist.Designer.cs b/Content.Server.Database/Migrations/Sqlite/20241018043307_RoleWhitelist.Designer.cs new file mode 100644 index 0000000000..bef2d1038f --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20241018043307_RoleWhitelist.Designer.cs @@ -0,0 +1,1823 @@ +// +using System; +using Content.Server.Database; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Content.Server.Database.Migrations.Sqlite +{ + [DbContext(typeof(SqliteServerDbContext))] + [Migration("20241018043307_RoleWhitelist")] + partial class RoleWhitelist + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.0"); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Title") + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("UserId") + .HasName("PK_admin"); + + b.HasIndex("AdminRankId") + .HasDatabaseName("IX_admin_admin_rank_id"); + + b.ToTable("admin", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_flag_id"); + + b.Property("AdminId") + .HasColumnType("TEXT") + .HasColumnName("admin_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.Property("Negative") + .HasColumnType("INTEGER") + .HasColumnName("negative"); + + b.HasKey("Id") + .HasName("PK_admin_flag"); + + b.HasIndex("AdminId") + .HasDatabaseName("IX_admin_flag_admin_id"); + + b.HasIndex("Flag", "AdminId") + .IsUnique(); + + b.ToTable("admin_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Id") + .HasColumnType("INTEGER") + .HasColumnName("admin_log_id"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Impact") + .HasColumnType("INTEGER") + .HasColumnName("impact"); + + b.Property("Json") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("json"); + + b.Property("Message") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("Type") + .HasColumnType("INTEGER") + .HasColumnName("type"); + + b.HasKey("RoundId", "Id") + .HasName("PK_admin_log"); + + b.HasIndex("Date"); + + b.HasIndex("Type") + .HasDatabaseName("IX_admin_log_type"); + + b.ToTable("admin_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("LogId") + .HasColumnType("INTEGER") + .HasColumnName("log_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.HasKey("RoundId", "LogId", "PlayerUserId") + .HasName("PK_admin_log_player"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_log_player_player_user_id"); + + b.ToTable("admin_log_player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_messages_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("Dismissed") + .HasColumnType("INTEGER") + .HasColumnName("dismissed"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Seen") + .HasColumnType("INTEGER") + .HasColumnName("seen"); + + b.HasKey("Id") + .HasName("PK_admin_messages"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_messages_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_messages_round_id"); + + b.ToTable("admin_messages", null, t => + { + t.HasCheckConstraint("NotDismissedAndSeen", "NOT dismissed OR seen"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_notes_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Secret") + .HasColumnType("INTEGER") + .HasColumnName("secret"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_admin_notes"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_notes_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_notes_round_id"); + + b.ToTable("admin_notes", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_admin_rank"); + + b.ToTable("admin_rank", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_flag_id"); + + b.Property("AdminRankId") + .HasColumnType("INTEGER") + .HasColumnName("admin_rank_id"); + + b.Property("Flag") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flag"); + + b.HasKey("Id") + .HasName("PK_admin_rank_flag"); + + b.HasIndex("AdminRankId"); + + b.HasIndex("Flag", "AdminRankId") + .IsUnique(); + + b.ToTable("admin_rank_flag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("admin_watchlists_id"); + + b.Property("CreatedAt") + .HasColumnType("TEXT") + .HasColumnName("created_at"); + + b.Property("CreatedById") + .HasColumnType("TEXT") + .HasColumnName("created_by_id"); + + b.Property("Deleted") + .HasColumnType("INTEGER") + .HasColumnName("deleted"); + + b.Property("DeletedAt") + .HasColumnType("TEXT") + .HasColumnName("deleted_at"); + + b.Property("DeletedById") + .HasColumnType("TEXT") + .HasColumnName("deleted_by_id"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("LastEditedAt") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("Message") + .IsRequired() + .HasMaxLength(4096) + .HasColumnType("TEXT") + .HasColumnName("message"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.HasKey("Id") + .HasName("PK_admin_watchlists"); + + b.HasIndex("CreatedById"); + + b.HasIndex("DeletedById"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_admin_watchlists_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_admin_watchlists_round_id"); + + b.ToTable("admin_watchlists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("antag_id"); + + b.Property("AntagName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("antag_name"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_antag"); + + b.HasIndex("ProfileId", "AntagName") + .IsUnique(); + + b.ToTable("antag", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.AssignedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("assigned_user_id_id"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_assigned_user_id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.HasIndex("UserName") + .IsUnique(); + + b.ToTable("assigned_user_id", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.BanTemplate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("ban_template_id"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("Length") + .HasColumnType("TEXT") + .HasColumnName("length"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("title"); + + b.HasKey("Id") + .HasName("PK_ban_template"); + + b.ToTable("ban_template", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("connection_log_id"); + + b.Property("Address") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("Denied") + .HasColumnType("INTEGER") + .HasColumnName("denied"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("ServerId") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasDefaultValue(0) + .HasColumnName("server_id"); + + b.Property("Time") + .HasColumnType("TEXT") + .HasColumnName("time"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("UserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("user_name"); + + b.HasKey("Id") + .HasName("PK_connection_log"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_connection_log_server_id"); + + b.HasIndex("UserId"); + + b.ToTable("connection_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("job_id"); + + b.Property("JobName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("job_name"); + + b.Property("Priority") + .HasColumnType("INTEGER") + .HasColumnName("priority"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_job"); + + b.HasIndex("ProfileId"); + + b.HasIndex("ProfileId", "JobName") + .IsUnique(); + + b.HasIndex(new[] { "ProfileId" }, "IX_job_one_high_priority") + .IsUnique() + .HasFilter("priority = 3"); + + b.ToTable("job", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Loadout", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("loadout_id"); + + b.Property("LoadoutName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("loadout_name"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.HasKey("Id") + .HasName("PK_loadout"); + + b.HasIndex("ProfileId", "LoadoutName") + .IsUnique(); + + b.ToTable("loadout", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.PlayTime", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("play_time_id"); + + b.Property("PlayerId") + .HasColumnType("TEXT") + .HasColumnName("player_id"); + + b.Property("TimeSpent") + .HasColumnType("TEXT") + .HasColumnName("time_spent"); + + b.Property("Tracker") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("tracker"); + + b.HasKey("Id") + .HasName("PK_play_time"); + + b.HasIndex("PlayerId", "Tracker") + .IsUnique(); + + b.ToTable("play_time", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("player_id"); + + b.Property("FirstSeenTime") + .HasColumnType("TEXT") + .HasColumnName("first_seen_time"); + + b.Property("LastReadRules") + .HasColumnType("TEXT") + .HasColumnName("last_read_rules"); + + b.Property("LastSeenAddress") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_address"); + + b.Property("LastSeenHWId") + .HasColumnType("BLOB") + .HasColumnName("last_seen_hwid"); + + b.Property("LastSeenTime") + .HasColumnType("TEXT") + .HasColumnName("last_seen_time"); + + b.Property("LastSeenUserName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("last_seen_user_name"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_player"); + + b.HasAlternateKey("UserId") + .HasName("ak_player_user_id"); + + b.HasIndex("LastSeenUserName"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("player", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("AdminOOCColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("admin_ooc_color"); + + b.Property("SelectedCharacterSlot") + .HasColumnType("INTEGER") + .HasColumnName("selected_character_slot"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_preference"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("preference", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("Age") + .HasColumnType("INTEGER") + .HasColumnName("age"); + + b.Property("Backpack") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("backpack"); + + b.Property("CharacterName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("char_name"); + + b.Property("Clothing") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("clothing"); + + b.Property("CustomSpecieName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("custom_specie_name"); + + b.Property("EyeColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("eye_color"); + + b.Property("FacialHairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_color"); + + b.Property("FacialHairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("facial_hair_name"); + + b.Property("FlavorText") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("flavor_text"); + + b.Property("Gender") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("gender"); + + b.Property("HairColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_color"); + + b.Property("HairName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("hair_name"); + + b.Property("Height") + .HasColumnType("REAL") + .HasColumnName("height"); + + b.Property("Markings") + .HasColumnType("jsonb") + .HasColumnName("markings"); + + b.Property("PreferenceId") + .HasColumnType("INTEGER") + .HasColumnName("preference_id"); + + b.Property("PreferenceUnavailable") + .HasColumnType("INTEGER") + .HasColumnName("pref_unavailable"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("sex"); + + b.Property("SkinColor") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("skin_color"); + + b.Property("Slot") + .HasColumnType("INTEGER") + .HasColumnName("slot"); + + b.Property("SpawnPriority") + .HasColumnType("INTEGER") + .HasColumnName("spawn_priority"); + + b.Property("Species") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("species"); + + b.Property("Width") + .HasColumnType("REAL") + .HasColumnName("width"); + + b.HasKey("Id") + .HasName("PK_profile"); + + b.HasIndex("PreferenceId") + .HasDatabaseName("IX_profile_preference_id"); + + b.HasIndex("Slot", "PreferenceId") + .IsUnique(); + + b.ToTable("profile", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("ServerId") + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("StartDate") + .HasColumnType("TEXT") + .HasColumnName("start_date"); + + b.HasKey("Id") + .HasName("PK_round"); + + b.HasIndex("ServerId") + .HasDatabaseName("IX_round_server_id"); + + b.HasIndex("StartDate"); + + b.ToTable("round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_id"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("name"); + + b.HasKey("Id") + .HasName("PK_server"); + + b.ToTable("server", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("AutoDelete") + .HasColumnType("INTEGER") + .HasColumnName("auto_delete"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExemptFlags") + .HasColumnType("INTEGER") + .HasColumnName("exempt_flags"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_ban_round_id"); + + b.ToTable("server_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanExemption", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.Property("Flags") + .HasColumnType("INTEGER") + .HasColumnName("flags"); + + b.HasKey("UserId") + .HasName("PK_server_ban_exemption"); + + b.ToTable("server_ban_exemption", null, t => + { + t.HasCheckConstraint("FlagsNotZero", "flags != 0"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_ban_hit_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("ConnectionId") + .HasColumnType("INTEGER") + .HasColumnName("connection_id"); + + b.HasKey("Id") + .HasName("PK_server_ban_hit"); + + b.HasIndex("BanId") + .HasDatabaseName("IX_server_ban_hit_ban_id"); + + b.HasIndex("ConnectionId") + .HasDatabaseName("IX_server_ban_hit_connection_id"); + + b.ToTable("server_ban_hit", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("server_role_ban_id"); + + b.Property("Address") + .HasColumnType("TEXT") + .HasColumnName("address"); + + b.Property("BanTime") + .HasColumnType("TEXT") + .HasColumnName("ban_time"); + + b.Property("BanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("banning_admin"); + + b.Property("ExpirationTime") + .HasColumnType("TEXT") + .HasColumnName("expiration_time"); + + b.Property("HWId") + .HasColumnType("BLOB") + .HasColumnName("hwid"); + + b.Property("Hidden") + .HasColumnType("INTEGER") + .HasColumnName("hidden"); + + b.Property("LastEditedAt") + .HasColumnType("TEXT") + .HasColumnName("last_edited_at"); + + b.Property("LastEditedById") + .HasColumnType("TEXT") + .HasColumnName("last_edited_by_id"); + + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("PlaytimeAtNote") + .HasColumnType("TEXT") + .HasColumnName("playtime_at_note"); + + b.Property("Reason") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("reason"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.Property("RoundId") + .HasColumnType("INTEGER") + .HasColumnName("round_id"); + + b.Property("Severity") + .HasColumnType("INTEGER") + .HasColumnName("severity"); + + b.HasKey("Id") + .HasName("PK_server_role_ban"); + + b.HasIndex("Address"); + + b.HasIndex("BanningAdmin"); + + b.HasIndex("LastEditedById"); + + b.HasIndex("PlayerUserId") + .HasDatabaseName("IX_server_role_ban_player_user_id"); + + b.HasIndex("RoundId") + .HasDatabaseName("IX_server_role_ban_round_id"); + + b.ToTable("server_role_ban", null, t => + { + t.HasCheckConstraint("HaveEitherAddressOrUserIdOrHWId", "address IS NOT NULL OR player_user_id IS NOT NULL OR hwid IS NOT NULL"); + }); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("role_unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_role_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_role_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("unban_id"); + + b.Property("BanId") + .HasColumnType("INTEGER") + .HasColumnName("ban_id"); + + b.Property("UnbanTime") + .HasColumnType("TEXT") + .HasColumnName("unban_time"); + + b.Property("UnbanningAdmin") + .HasColumnType("TEXT") + .HasColumnName("unbanning_admin"); + + b.HasKey("Id") + .HasName("PK_server_unban"); + + b.HasIndex("BanId") + .IsUnique(); + + b.ToTable("server_unban", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("trait_id"); + + b.Property("ProfileId") + .HasColumnType("INTEGER") + .HasColumnName("profile_id"); + + b.Property("TraitName") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("trait_name"); + + b.HasKey("Id") + .HasName("PK_trait"); + + b.HasIndex("ProfileId", "TraitName") + .IsUnique(); + + b.ToTable("trait", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER") + .HasColumnName("uploaded_resource_log_id"); + + b.Property("Data") + .IsRequired() + .HasColumnType("BLOB") + .HasColumnName("data"); + + b.Property("Date") + .HasColumnType("TEXT") + .HasColumnName("date"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasColumnName("path"); + + b.Property("UserId") + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("Id") + .HasName("PK_uploaded_resource_log"); + + b.ToTable("uploaded_resource_log", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Whitelist", b => + { + b.Property("UserId") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT") + .HasColumnName("user_id"); + + b.HasKey("UserId") + .HasName("PK_whitelist"); + + b.ToTable("whitelist", (string)null); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.Property("PlayersId") + .HasColumnType("INTEGER") + .HasColumnName("players_id"); + + b.Property("RoundsId") + .HasColumnType("INTEGER") + .HasColumnName("rounds_id"); + + b.HasKey("PlayersId", "RoundsId") + .HasName("PK_player_round"); + + b.HasIndex("RoundsId") + .HasDatabaseName("IX_player_round_rounds_id"); + + b.ToTable("player_round", (string)null); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.HasOne("Content.Server.Database.AdminRank", "AdminRank") + .WithMany("Admins") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_admin_rank_admin_rank_id"); + + b.Navigation("AdminRank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminFlag", b => + { + b.HasOne("Content.Server.Database.Admin", "Admin") + .WithMany("Flags") + .HasForeignKey("AdminId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_flag_admin_admin_id"); + + b.Navigation("Admin"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany("AdminLogs") + .HasForeignKey("RoundId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_round_round_id"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLogPlayer", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminLogs") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_player_player_user_id"); + + b.HasOne("Content.Server.Database.AdminLog", "Log") + .WithMany("Players") + .HasForeignKey("RoundId", "LogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_log_player_admin_log_round_id_log_id"); + + b.Navigation("Log"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminMessage", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminMessagesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminMessagesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminMessagesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_messages_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminMessagesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_messages_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_messages_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminNote", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminNotesCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminNotesDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminNotesLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_notes_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminNotesReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_notes_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_notes_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRankFlag", b => + { + b.HasOne("Content.Server.Database.AdminRank", "Rank") + .WithMany("Flags") + .HasForeignKey("AdminRankId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_admin_rank_flag_admin_rank_admin_rank_id"); + + b.Navigation("Rank"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminWatchlist", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminWatchlistsCreated") + .HasForeignKey("CreatedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_created_by_id"); + + b.HasOne("Content.Server.Database.Player", "DeletedBy") + .WithMany("AdminWatchlistsDeleted") + .HasForeignKey("DeletedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_deleted_by_id"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminWatchlistsLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_admin_watchlists_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("AdminWatchlistsReceived") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_admin_watchlists_player_player_user_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_admin_watchlists_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("DeletedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Player"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.Antag", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Antags") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_antag_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("ConnectionLogs") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.SetNull) + .IsRequired() + .HasConstraintName("FK_connection_log_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.Job", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Jobs") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_job_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Loadout", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Loadouts") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_loadout_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.HasOne("Content.Server.Database.Preference", "Preference") + .WithMany("Profiles") + .HasForeignKey("PreferenceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_profile_preference_preference_id"); + + b.Navigation("Preference"); + }); + + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.HasOne("Content.Server.Database.Server", "Server") + .WithMany("Rounds") + .HasForeignKey("ServerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_round_server_server_id"); + + b.Navigation("Server"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBanHit", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithMany("BanHits") + .HasForeignKey("BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_server_ban_ban_id"); + + b.HasOne("Content.Server.Database.ConnectionLog", "Connection") + .WithMany("BanHits") + .HasForeignKey("ConnectionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_ban_hit_connection_log_connection_id"); + + b.Navigation("Ban"); + + b.Navigation("Connection"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.HasOne("Content.Server.Database.Player", "CreatedBy") + .WithMany("AdminServerRoleBansCreated") + .HasForeignKey("BanningAdmin") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_banning_admin"); + + b.HasOne("Content.Server.Database.Player", "LastEditedBy") + .WithMany("AdminServerRoleBansLastEdited") + .HasForeignKey("LastEditedById") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_server_role_ban_player_last_edited_by_id"); + + b.HasOne("Content.Server.Database.Round", "Round") + .WithMany() + .HasForeignKey("RoundId") + .HasConstraintName("FK_server_role_ban_round_round_id"); + + b.Navigation("CreatedBy"); + + b.Navigation("LastEditedBy"); + + b.Navigation("Round"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleUnban", b => + { + b.HasOne("Content.Server.Database.ServerRoleBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerRoleUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_role_unban_server_role_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerUnban", b => + { + b.HasOne("Content.Server.Database.ServerBan", "Ban") + .WithOne("Unban") + .HasForeignKey("Content.Server.Database.ServerUnban", "BanId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_server_unban_server_ban_ban_id"); + + b.Navigation("Ban"); + }); + + modelBuilder.Entity("Content.Server.Database.Trait", b => + { + b.HasOne("Content.Server.Database.Profile", "Profile") + .WithMany("Traits") + .HasForeignKey("ProfileId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_trait_profile_profile_id"); + + b.Navigation("Profile"); + }); + + modelBuilder.Entity("PlayerRound", b => + { + b.HasOne("Content.Server.Database.Player", null) + .WithMany() + .HasForeignKey("PlayersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_player_players_id"); + + b.HasOne("Content.Server.Database.Round", null) + .WithMany() + .HasForeignKey("RoundsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_player_round_round_rounds_id"); + }); + + modelBuilder.Entity("Content.Server.Database.Admin", b => + { + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminLog", b => + { + b.Navigation("Players"); + }); + + modelBuilder.Entity("Content.Server.Database.AdminRank", b => + { + b.Navigation("Admins"); + + b.Navigation("Flags"); + }); + + modelBuilder.Entity("Content.Server.Database.ConnectionLog", b => + { + b.Navigation("BanHits"); + }); + + modelBuilder.Entity("Content.Server.Database.Player", b => + { + b.Navigation("AdminLogs"); + + b.Navigation("AdminMessagesCreated"); + + b.Navigation("AdminMessagesDeleted"); + + b.Navigation("AdminMessagesLastEdited"); + + b.Navigation("AdminMessagesReceived"); + + b.Navigation("AdminNotesCreated"); + + b.Navigation("AdminNotesDeleted"); + + b.Navigation("AdminNotesLastEdited"); + + b.Navigation("AdminNotesReceived"); + + b.Navigation("AdminServerBansCreated"); + + b.Navigation("AdminServerBansLastEdited"); + + b.Navigation("AdminServerRoleBansCreated"); + + b.Navigation("AdminServerRoleBansLastEdited"); + + b.Navigation("AdminWatchlistsCreated"); + + b.Navigation("AdminWatchlistsDeleted"); + + b.Navigation("AdminWatchlistsLastEdited"); + + b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); + }); + + modelBuilder.Entity("Content.Server.Database.Preference", b => + { + b.Navigation("Profiles"); + }); + + modelBuilder.Entity("Content.Server.Database.Profile", b => + { + b.Navigation("Antags"); + + b.Navigation("Jobs"); + + b.Navigation("Loadouts"); + + b.Navigation("Traits"); + }); + + modelBuilder.Entity("Content.Server.Database.Round", b => + { + b.Navigation("AdminLogs"); + }); + + modelBuilder.Entity("Content.Server.Database.Server", b => + { + b.Navigation("ConnectionLogs"); + + b.Navigation("Rounds"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerBan", b => + { + b.Navigation("BanHits"); + + b.Navigation("Unban"); + }); + + modelBuilder.Entity("Content.Server.Database.ServerRoleBan", b => + { + b.Navigation("Unban"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/20241018043307_RoleWhitelist.cs b/Content.Server.Database/Migrations/Sqlite/20241018043307_RoleWhitelist.cs new file mode 100644 index 0000000000..9d192fc685 --- /dev/null +++ b/Content.Server.Database/Migrations/Sqlite/20241018043307_RoleWhitelist.cs @@ -0,0 +1,40 @@ +#nullable disable + +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Content.Server.Database.Migrations.Sqlite +{ + /// + public partial class RoleWhitelist : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "role_whitelists", + columns: table => new + { + player_user_id = table.Column(type: "TEXT", nullable: false), + role_id = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_role_whitelists", x => new { x.player_user_id, x.role_id }); + table.ForeignKey( + name: "FK_role_whitelists_player_player_user_id", + column: x => x.player_user_id, + principalTable: "player", + principalColumn: "user_id", + onDelete: ReferentialAction.Cascade); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "role_whitelists"); + } + } +} diff --git a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs index d2d47f60b4..c7a79b9717 100644 --- a/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs +++ b/Content.Server.Database/Migrations/Sqlite/SqliteServerDbContextModelSnapshot.cs @@ -866,6 +866,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("profile", (string)null); }); + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.Property("PlayerUserId") + .HasColumnType("TEXT") + .HasColumnName("player_user_id"); + + b.Property("RoleId") + .HasColumnType("TEXT") + .HasColumnName("role_id"); + + b.HasKey("PlayerUserId", "RoleId") + .HasName("PK_role_whitelists"); + + b.ToTable("role_whitelists", (string)null); + }); + modelBuilder.Entity("Content.Server.Database.Round", b => { b.Property("Id") @@ -1543,6 +1559,19 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("Preference"); }); + modelBuilder.Entity("Content.Server.Database.RoleWhitelist", b => + { + b.HasOne("Content.Server.Database.Player", "Player") + .WithMany("JobWhitelists") + .HasForeignKey("PlayerUserId") + .HasPrincipalKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_role_whitelists_player_player_user_id"); + + b.Navigation("Player"); + }); + modelBuilder.Entity("Content.Server.Database.Round", b => { b.HasOne("Content.Server.Database.Server", "Server") @@ -1742,6 +1771,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Navigation("AdminWatchlistsLastEdited"); b.Navigation("AdminWatchlistsReceived"); + + b.Navigation("JobWhitelists"); }); modelBuilder.Entity("Content.Server.Database.Preference", b => diff --git a/Content.Server.Database/Model.cs b/Content.Server.Database/Model.cs index 187fb81a20..e698805cfc 100644 --- a/Content.Server.Database/Model.cs +++ b/Content.Server.Database/Model.cs @@ -41,6 +41,7 @@ protected ServerDbContext(DbContextOptions options) : base(options) public DbSet AdminWatchlists { get; set; } = null!; public DbSet AdminMessages { get; set; } = null!; public DbSet BanTemplate { get; set; } = null!; + public DbSet RoleWhitelists { get; set; } = null!; protected override void OnModelCreating(ModelBuilder modelBuilder) { @@ -49,19 +50,19 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .IsUnique(); modelBuilder.Entity() - .HasIndex(p => new {p.Slot, PrefsId = p.PreferenceId}) + .HasIndex(p => new { p.Slot, PrefsId = p.PreferenceId }) .IsUnique(); modelBuilder.Entity() - .HasIndex(p => new {HumanoidProfileId = p.ProfileId, p.AntagName}) + .HasIndex(p => new { HumanoidProfileId = p.ProfileId, p.AntagName }) .IsUnique(); modelBuilder.Entity() - .HasIndex(p => new {HumanoidProfileId = p.ProfileId, p.TraitName}) + .HasIndex(p => new { HumanoidProfileId = p.ProfileId, p.TraitName }) .IsUnique(); modelBuilder.Entity() - .HasIndex(p => new {HumanoidProfileId = p.ProfileId, p.LoadoutName}) + .HasIndex(p => new { HumanoidProfileId = p.ProfileId, p.LoadoutName }) .IsUnique(); modelBuilder.Entity() @@ -91,15 +92,15 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .OnDelete(DeleteBehavior.SetNull); modelBuilder.Entity() - .HasIndex(f => new {f.Flag, f.AdminId}) + .HasIndex(f => new { f.Flag, f.AdminId }) .IsUnique(); modelBuilder.Entity() - .HasIndex(f => new {f.Flag, f.AdminRankId}) + .HasIndex(f => new { f.Flag, f.AdminRankId }) .IsUnique(); modelBuilder.Entity() - .HasKey(log => new {log.RoundId, log.Id}); + .HasKey(log => new { log.RoundId, log.Id }); modelBuilder.Entity() .Property(log => log.Id); @@ -124,7 +125,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasIndex(round => round.StartDate); modelBuilder.Entity() - .HasKey(logPlayer => new {logPlayer.RoundId, logPlayer.LogId, logPlayer.PlayerUserId}); + .HasKey(logPlayer => new { logPlayer.RoundId, logPlayer.LogId, logPlayer.PlayerUserId }); modelBuilder.Entity() .HasIndex(p => p.PlayerUserId); @@ -301,6 +302,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasForeignKey(ban => ban.LastEditedById) .HasPrincipalKey(author => author.UserId) .OnDelete(DeleteBehavior.SetNull); + + modelBuilder.Entity() + .HasOne(w => w.Player) + .WithMany(p => p.JobWhitelists) + .HasForeignKey(w => w.PlayerUserId) + .HasPrincipalKey(p => p.UserId) + .OnDelete(DeleteBehavior.Cascade); } public virtual IQueryable SearchLogs(IQueryable query, string searchText) @@ -457,6 +465,7 @@ public class Player public List AdminServerBansLastEdited { get; set; } = null!; public List AdminServerRoleBansCreated { get; set; } = null!; public List AdminServerRoleBansLastEdited { get; set; } = null!; + public List JobWhitelists { get; set; } = null!; } [Table("whitelist")] @@ -597,7 +606,7 @@ public interface IUnbanCommon public enum ServerBanExemptFlags { // @formatter:off - None = 0, + None = 0, /// /// Ban is a datacenter range, connections usually imply usage of a VPN service. @@ -1027,6 +1036,17 @@ public class AdminMessage : IAdminRemarksCommon public bool Dismissed { get; set; } } + [PrimaryKey(nameof(PlayerUserId), nameof(RoleId))] + public class RoleWhitelist + { + [Required, ForeignKey("Player")] + public Guid PlayerUserId { get; set; } + public Player Player { get; set; } = default!; + + [Required] + public string RoleId { get; set; } = default!; + } + /// /// Defines a template that admins can use to quickly fill out ban information. /// diff --git a/Content.Server/Administration/Commands/JobWhitelistCommands.cs b/Content.Server/Administration/Commands/JobWhitelistCommands.cs new file mode 100644 index 0000000000..f06cecabd7 --- /dev/null +++ b/Content.Server/Administration/Commands/JobWhitelistCommands.cs @@ -0,0 +1,214 @@ +using System.Linq; +using Content.Server.Database; +using Content.Server.Players.JobWhitelist; +using Content.Shared.Administration; +using Content.Shared.Roles; +using Robust.Server.Player; +using Robust.Shared.Console; +using Robust.Shared.Prototypes; + +namespace Content.Server.Administration.Commands; + +[AdminCommand(AdminFlags.Ban)] +public sealed class JobWhitelistAddCommand : LocalizedCommands +{ + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly JobWhitelistManager _jobWhitelist = default!; + [Dependency] private readonly IPlayerLocator _playerLocator = default!; + [Dependency] private readonly IPlayerManager _players = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + + public override string Command => "jobwhitelistadd"; + + public override async void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 2) + { + shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", + ("properAmount", 2), + ("currentAmount", args.Length))); + shell.WriteLine(Help); + return; + } + + var player = args[0].Trim(); + var job = new ProtoId(args[1].Trim()); + if (!_prototypes.TryIndex(job, out var jobPrototype)) + { + shell.WriteError(Loc.GetString("cmd-jobwhitelist-job-does-not-exist", ("job", job.Id))); + shell.WriteLine(Help); + return; + } + + var data = await _playerLocator.LookupIdByNameAsync(player); + if (data != null) + { + var guid = data.UserId; + var isWhitelisted = await _db.IsJobWhitelisted(guid, job); + if (isWhitelisted) + { + shell.WriteLine(Loc.GetString("cmd-jobwhitelist-already-whitelisted", + ("player", player), + ("jobId", job.Id), + ("jobName", jobPrototype.LocalizedName))); + return; + } + + _jobWhitelist.AddWhitelist(guid, job); + shell.WriteLine(Loc.GetString("cmd-jobwhitelistadd-added", + ("player", player), + ("jobId", job.Id), + ("jobName", jobPrototype.LocalizedName))); + return; + } + + shell.WriteError(Loc.GetString("cmd-jobwhitelist-player-not-found", ("player", player))); + } + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + if (args.Length == 1) + { + return CompletionResult.FromHintOptions( + _players.Sessions.Select(s => s.Name), + Loc.GetString("cmd-jobwhitelist-hint-player")); + } + + if (args.Length == 2) + { + return CompletionResult.FromHintOptions( + _prototypes.EnumeratePrototypes().Select(p => p.ID), + Loc.GetString("cmd-jobwhitelist-hint-job")); + } + + return CompletionResult.Empty; + } +} + +[AdminCommand(AdminFlags.Ban)] +public sealed class GetJobWhitelistCommand : LocalizedCommands +{ + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly IPlayerLocator _playerLocator = default!; + [Dependency] private readonly IPlayerManager _players = default!; + + public override string Command => "jobwhitelistget"; + + public override async void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length == 0) + { + shell.WriteError("This command needs at least one argument."); + shell.WriteLine(Help); + return; + } + + var player = string.Join(' ', args).Trim(); + var data = await _playerLocator.LookupIdByNameAsync(player); + if (data != null) + { + var guid = data.UserId; + var whitelists = await _db.GetJobWhitelists(guid); + if (whitelists.Count == 0) + { + shell.WriteLine(Loc.GetString("cmd-jobwhitelistget-whitelisted-none", ("player", player))); + return; + } + + shell.WriteLine(Loc.GetString("cmd-jobwhitelistget-whitelisted-for", + ("player", player), + ("jobs", string.Join(", ", whitelists)))); + return; + } + + shell.WriteError(Loc.GetString("cmd-jobwhitelist-player-not-found", ("player", player))); + } + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + if (args.Length == 1) + { + return CompletionResult.FromHintOptions( + _players.Sessions.Select(s => s.Name), + Loc.GetString("cmd-jobwhitelist-hint-player")); + } + + return CompletionResult.Empty; + } +} + +[AdminCommand(AdminFlags.Ban)] +public sealed class RemoveJobWhitelistCommand : LocalizedCommands +{ + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly JobWhitelistManager _jobWhitelist = default!; + [Dependency] private readonly IPlayerLocator _playerLocator = default!; + [Dependency] private readonly IPlayerManager _players = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + + public override string Command => "jobwhitelistremove"; + + public override async void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 2) + { + shell.WriteError(Loc.GetString("shell-wrong-arguments-number-need-specific", + ("properAmount", 2), + ("currentAmount", args.Length))); + shell.WriteLine(Help); + return; + } + + var player = args[0].Trim(); + var job = new ProtoId(args[1].Trim()); + if (!_prototypes.TryIndex(job, out var jobPrototype)) + { + shell.WriteError(Loc.GetString("cmd-jobwhitelist-job-does-not-exist", ("job", job))); + shell.WriteLine(Help); + return; + } + + var data = await _playerLocator.LookupIdByNameAsync(player); + if (data != null) + { + var guid = data.UserId; + var isWhitelisted = await _db.IsJobWhitelisted(guid, job); + if (!isWhitelisted) + { + shell.WriteError(Loc.GetString("cmd-jobwhitelistremove-was-not-whitelisted", + ("player", player), + ("jobId", job.Id), + ("jobName", jobPrototype.LocalizedName))); + return; + } + + _jobWhitelist.RemoveWhitelist(guid, job); + shell.WriteLine(Loc.GetString("cmd-jobwhitelistremove-removed", + ("player", player), + ("jobId", job.Id), + ("jobName", jobPrototype.LocalizedName))); + return; + } + + shell.WriteError(Loc.GetString("cmd-jobwhitelist-player-not-found", ("player", player))); + } + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + if (args.Length == 1) + { + return CompletionResult.FromHintOptions( + _players.Sessions.Select(s => s.Name), + Loc.GetString("cmd-jobwhitelist-hint-player")); + } + + if (args.Length == 2) + { + return CompletionResult.FromHintOptions( + _prototypes.EnumeratePrototypes().Select(p => p.ID), + Loc.GetString("cmd-jobwhitelist-hint-job")); + } + + return CompletionResult.Empty; + } +} diff --git a/Content.Server/Administration/Commands/JobWhitelistsCommand.cs b/Content.Server/Administration/Commands/JobWhitelistsCommand.cs new file mode 100644 index 0000000000..6ad44a0c73 --- /dev/null +++ b/Content.Server/Administration/Commands/JobWhitelistsCommand.cs @@ -0,0 +1,61 @@ +using System.Linq; +using Content.Server.Administration; +using Content.Server.EUI; +using Content.Shared.Administration; +using Robust.Shared.Console; +using Robust.Server.Player; + +namespace Content.Server.Administration.Commands; + +/// +/// Opens the job whitelists panel for editing player whitelists. +/// To use this ingame it's easiest to first open the player panel, then hit Job Whitelists. +/// +[AdminCommand(AdminFlags.Whitelist)] +public sealed class JobWhitelistsCommand : LocalizedCommands +{ + [Dependency] private readonly EuiManager _eui = default!; + [Dependency] private readonly IPlayerLocator _locator = default!; + [Dependency] private readonly IPlayerManager _players = default!; + + public override string Command => "jobwhitelists"; + + public override async void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (shell.Player is not {} player) + { + shell.WriteError(Loc.GetString("shell-cannot-run-command-from-server")); + return; + } + + if (args.Length != 1) + { + shell.WriteLine(Loc.GetString("cmd-ban-invalid-arguments")); + shell.WriteLine(Help); + } + + var playerarg = string.Join(' ', args).Trim(); + var located = await _locator.LookupIdByNameAsync(playerarg); + if (located is null) + { + shell.WriteError(Loc.GetString("cmd-jobwhitelists-player-err")); + return; + } + + var ui = new JobWhitelistsEui(located.UserId, located.Username); + ui.LoadWhitelists(); + _eui.OpenEui(ui, player); + } + + public override CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + if (args.Length == 1) + { + return CompletionResult.FromHintOptions( + _players.Sessions.Select(s => s.Name), + Loc.GetString("cmd-jobwhitelist-hint-player")); + } + + return CompletionResult.Empty; + } +} diff --git a/Content.Server/Administration/JobWhitelistsEui.cs b/Content.Server/Administration/JobWhitelistsEui.cs new file mode 100644 index 0000000000..9d50a72183 --- /dev/null +++ b/Content.Server/Administration/JobWhitelistsEui.cs @@ -0,0 +1,90 @@ +using System.Threading.Tasks; +using Content.Server.Administration.Managers; +using Content.Server.Database; +using Content.Server.EUI; +using Content.Server.Players.JobWhitelist; +using Content.Shared.Administration; +using Content.Shared.Administration; +using Content.Shared.Eui; +using Content.Shared.Roles; +using Robust.Shared.Log; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; + +namespace Content.Server.Administration; + +public sealed class JobWhitelistsEui : BaseEui +{ + [Dependency] private readonly IAdminManager _admin = default!; + [Dependency] private readonly ILogManager _log = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly JobWhitelistManager _jobWhitelist = default!; + + private readonly ISawmill _sawmill; + + public NetUserId PlayerId; + public string PlayerName; + + public HashSet> Whitelists = new(); + + public JobWhitelistsEui(NetUserId playerId, string playerName) + { + IoCManager.InjectDependencies(this); + + _sawmill = _log.GetSawmill("admin.job_whitelists_eui"); + + PlayerId = playerId; + PlayerName = playerName; + } + + public async void LoadWhitelists() + { + var jobs = await _db.GetJobWhitelists(PlayerId.UserId); + foreach (var id in jobs) + { + if (_proto.HasIndex(id)) + Whitelists.Add(id); + } + + StateDirty(); + } + + public override EuiStateBase GetNewState() + { + return new JobWhitelistsEuiState(PlayerName, Whitelists); + } + + public override void HandleMessage(EuiMessageBase msg) + { + base.HandleMessage(msg); + + if (msg is not SetJobWhitelistedMessage args) + return; + + if (!_admin.HasAdminFlag(Player, AdminFlags.Whitelist)) + { + _sawmill.Warning($"{Player.Name} ({Player.UserId}) tried to change role whitelists for {PlayerName} without whitelists flag"); + return; + } + + if (!_proto.HasIndex(args.Job)) + return; + + if (args.Whitelisting) + { + _jobWhitelist.AddWhitelist(PlayerId, args.Job); + Whitelists.Add(args.Job); + } + else + { + _jobWhitelist.RemoveWhitelist(PlayerId, args.Job); + Whitelists.Remove(args.Job); + } + + var verb = args.Whitelisting ? "added" : "removed"; + _sawmill.Info($"{Player.Name} ({Player.UserId}) {verb} whitelist for {args.Job} to player {PlayerName} ({PlayerId.UserId})"); + + StateDirty(); + } +} diff --git a/Content.Server/Administration/Managers/BanManager.cs b/Content.Server/Administration/Managers/BanManager.cs index 3a05b934b2..68bd817026 100644 --- a/Content.Server/Administration/Managers/BanManager.cs +++ b/Content.Server/Administration/Managers/BanManager.cs @@ -73,7 +73,9 @@ private async Task AddRoleBan(ServerRoleBanDef banDef) public HashSet? GetRoleBans(NetUserId playerUserId) { - return _cachedRoleBans.TryGetValue(playerUserId, out var roleBans) ? roleBans.Select(banDef => banDef.Role).ToHashSet() : null; + return _cachedRoleBans.TryGetValue(playerUserId, out var roleBans) + ? roleBans.Select(banDef => banDef.Role).ToHashSet() + : null; } private async Task CacheDbRoleBans(NetUserId userId, IPAddress? address = null, ImmutableArray? hwId = null) @@ -263,13 +265,13 @@ public async Task PardonRoleBan(int banId, NetUserId? unbanningAdmin, Da return $"Pardoned ban with id {banId}"; } - public HashSet? GetJobBans(NetUserId playerUserId) + public HashSet>? GetJobBans(NetUserId playerUserId) { if (!_cachedRoleBans.TryGetValue(playerUserId, out var roleBans)) return null; return roleBans .Where(ban => ban.Role.StartsWith(JobPrefix, StringComparison.Ordinal)) - .Select(ban => ban.Role[JobPrefix.Length..]) + .Select(ban => new ProtoId(ban.Role[JobPrefix.Length..])) .ToHashSet(); } #endregion diff --git a/Content.Server/Administration/Managers/IBanManager.cs b/Content.Server/Administration/Managers/IBanManager.cs index dafe3d35bd..b60e0a2535 100644 --- a/Content.Server/Administration/Managers/IBanManager.cs +++ b/Content.Server/Administration/Managers/IBanManager.cs @@ -2,8 +2,10 @@ using System.Net; using System.Threading.Tasks; using Content.Shared.Database; +using Content.Shared.Roles; using Robust.Shared.Network; using Robust.Shared.Player; +using Robust.Shared.Prototypes; namespace Content.Server.Administration.Managers; @@ -24,7 +26,7 @@ public interface IBanManager /// Reason for the ban public void CreateServerBan(NetUserId? target, string? targetUsername, NetUserId? banningAdmin, (IPAddress, int)? addressRange, ImmutableArray? hwid, uint? minutes, NoteSeverity severity, string reason); public HashSet? GetRoleBans(NetUserId playerUserId); - public HashSet? GetJobBans(NetUserId playerUserId); + public HashSet>? GetJobBans(NetUserId playerUserId); /// /// Creates a job ban for the specified target, username or GUID diff --git a/Content.Server/Database/ServerDbBase.cs b/Content.Server/Database/ServerDbBase.cs index 3ac755facf..8286defd11 100644 --- a/Content.Server/Database/ServerDbBase.cs +++ b/Content.Server/Database/ServerDbBase.cs @@ -17,6 +17,8 @@ using Robust.Shared.Enums; using Robust.Shared.Network; using Robust.Shared.Utility; +using Content.Shared.Roles; +using Robust.Shared.Prototypes; namespace Content.Server.Database { @@ -141,7 +143,7 @@ public async Task InitPrefsAsync(NetUserId userId, ICharacter await db.DbContext.SaveChangesAsync(); - return new PlayerPreferences(new[] {new KeyValuePair(0, defaultProfile)}, 0, Color.FromHex(prefs.AdminOOCColor)); + return new PlayerPreferences(new[] { new KeyValuePair(0, defaultProfile) }, 0, Color.FromHex(prefs.AdminOOCColor)); } public async Task DeleteSlotAndSetSelectedIndex(NetUserId userId, int deleteSlot, int newSlot) @@ -281,25 +283,25 @@ private static Profile ConvertProfiles(HumanoidCharacterProfile humanoid, int sl profile.Jobs.AddRange( humanoid.JobPriorities .Where(j => j.Value != JobPriority.Never) - .Select(j => new Job {JobName = j.Key, Priority = (DbJobPriority) j.Value}) + .Select(j => new Job { JobName = j.Key, Priority = (DbJobPriority) j.Value }) ); profile.Antags.Clear(); profile.Antags.AddRange( humanoid.AntagPreferences - .Select(a => new Antag {AntagName = a}) + .Select(a => new Antag { AntagName = a }) ); profile.Traits.Clear(); profile.Traits.AddRange( humanoid.TraitPreferences - .Select(t => new Trait {TraitName = t}) + .Select(t => new Trait { TraitName = t }) ); profile.Loadouts.Clear(); profile.Loadouts.AddRange( humanoid.LoadoutPreferences - .Select(t => new Loadout {LoadoutName = t}) + .Select(t => new Loadout { LoadoutName = t }) ); return profile; @@ -1236,7 +1238,7 @@ ban.Unban is null ban.LastEditedAt, ban.ExpirationTime, ban.Hidden, - new [] { ban.RoleId.Replace(BanManager.JobPrefix, null) }, + new[] { ban.RoleId.Replace(BanManager.JobPrefix, null) }, MakePlayerRecord(unbanningAdmin), ban.Unban?.UnbanTime); } @@ -1400,10 +1402,10 @@ public async Task> GetActiveWatchlists(Guid player) protected async Task> GetActiveWatchlistsImpl(DbGuard db, Guid player) { var entities = await (from watchlist in db.DbContext.AdminWatchlists - where watchlist.PlayerUserId == player && - !watchlist.Deleted && - (watchlist.ExpirationTime == null || DateTime.UtcNow < watchlist.ExpirationTime) - select watchlist) + where watchlist.PlayerUserId == player && + !watchlist.Deleted && + (watchlist.ExpirationTime == null || DateTime.UtcNow < watchlist.ExpirationTime) + select watchlist) .Include(note => note.Round) .ThenInclude(r => r!.Server) .Include(note => note.CreatedBy) @@ -1428,9 +1430,9 @@ public async Task> GetMessages(Guid player) protected async Task> GetMessagesImpl(DbGuard db, Guid player) { var entities = await (from message in db.DbContext.AdminMessages - where message.PlayerUserId == player && !message.Deleted && - (message.ExpirationTime == null || DateTime.UtcNow < message.ExpirationTime) - select message).Include(note => note.Round) + where message.PlayerUserId == player && !message.Deleted && + (message.ExpirationTime == null || DateTime.UtcNow < message.ExpirationTime) + select message).Include(note => note.Round) .ThenInclude(r => r!.Server) .Include(note => note.CreatedBy) .Include(note => note.LastEditedBy) @@ -1509,7 +1511,7 @@ protected async Task> GetGroupedServerRoleBansAsNo // Client side query, as EF can't do groups yet var bansEnumerable = bansQuery - .GroupBy(ban => new { ban.BanTime, CreatedBy = (Player?)ban.CreatedBy, ban.Reason, Unbanned = ban.Unban == null }) + .GroupBy(ban => new { ban.BanTime, CreatedBy = (Player?) ban.CreatedBy, ban.Reason, Unbanned = ban.Unban == null }) .Select(banGroup => banGroup) .ToArray(); @@ -1577,5 +1579,64 @@ protected abstract class DbGuard : IAsyncDisposable public abstract ValueTask DisposeAsync(); } + + #region Job Whitelists + + public async Task AddJobWhitelist(Guid player, ProtoId job) + { + await using var db = await GetDb(); + var exists = await db.DbContext.RoleWhitelists + .Where(w => w.PlayerUserId == player) + .Where(w => w.RoleId == job.Id) + .AnyAsync(); + + if (exists) + return false; + + var whitelist = new RoleWhitelist + { + PlayerUserId = player, + RoleId = job + }; + db.DbContext.RoleWhitelists.Add(whitelist); + await db.DbContext.SaveChangesAsync(); + return true; + } + + public async Task> GetJobWhitelists(Guid player, CancellationToken cancel) + { + await using var db = await GetDb(cancel); + return await db.DbContext.RoleWhitelists + .Where(w => w.PlayerUserId == player) + .Select(w => w.RoleId) + .ToListAsync(cancellationToken: cancel); + } + + public async Task IsJobWhitelisted(Guid player, ProtoId job) + { + await using var db = await GetDb(); + return await db.DbContext.RoleWhitelists + .Where(w => w.PlayerUserId == player) + .Where(w => w.RoleId == job.Id) + .AnyAsync(); + } + + public async Task RemoveJobWhitelist(Guid player, ProtoId job) + { + await using var db = await GetDb(); + var entry = await db.DbContext.RoleWhitelists + .Where(w => w.PlayerUserId == player) + .Where(w => w.RoleId == job.Id) + .SingleOrDefaultAsync(); + + if (entry == null) + return false; + + db.DbContext.RoleWhitelists.Remove(entry); + await db.DbContext.SaveChangesAsync(); + return true; + } + + #endregion } } diff --git a/Content.Server/Database/ServerDbManager.cs b/Content.Server/Database/ServerDbManager.cs index 01d1526727..a9c5e8c43a 100644 --- a/Content.Server/Database/ServerDbManager.cs +++ b/Content.Server/Database/ServerDbManager.cs @@ -19,6 +19,8 @@ using Robust.Shared.Network; using LogLevel = Robust.Shared.Log.LogLevel; using MSLogLevel = Microsoft.Extensions.Logging.LogLevel; +using Content.Shared.Roles; +using Robust.Shared.Prototypes; namespace Content.Server.Database { @@ -87,7 +89,7 @@ Task> GetServerBansAsync( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, - bool includeUnbanned=true); + bool includeUnbanned = true); Task AddServerBanAsync(ServerBanDef serverBan); Task AddServerUnbanAsync(ServerUnbanDef serverBan); @@ -290,6 +292,18 @@ Task AddConnectionLogAsync( Task MarkMessageAsSeen(int id, bool dismissedToo); #endregion + + #region Job Whitelists + + Task AddJobWhitelist(Guid player, ProtoId job); + + + Task> GetJobWhitelists(Guid player, CancellationToken cancel = default); + Task IsJobWhitelisted(Guid player, ProtoId job); + + Task RemoveJobWhitelist(Guid player, ProtoId job); + + #endregion } public sealed class ServerDbManager : IServerDbManager @@ -421,7 +435,7 @@ public Task> GetServerBansAsync( IPAddress? address, NetUserId? userId, ImmutableArray? hwId, - bool includeUnbanned=true) + bool includeUnbanned = true) { DbReadOpsMetric.Inc(); return RunDbCommand(() => _db.GetServerBansAsync(address, userId, hwId, includeUnbanned)); @@ -792,7 +806,7 @@ public Task AddAdminMessage(int? roundId, Guid player, TimeSpan playtimeAtN return RunDbCommand(() => _db.GetServerRoleBanAsNoteAsync(id)); } - public Task> GetAllAdminRemarks(Guid player) + public Task> GetAllAdminRemarks(Guid player) { DbReadOpsMetric.Inc(); return RunDbCommand(() => _db.GetAllAdminRemarks(player)); @@ -869,6 +883,30 @@ public Task MarkMessageAsSeen(int id, bool dismissedToo) return RunDbCommand(() => _db.MarkMessageAsSeen(id, dismissedToo)); } + public Task AddJobWhitelist(Guid player, ProtoId job) + { + DbWriteOpsMetric.Inc(); + return RunDbCommand(() => _db.AddJobWhitelist(player, job)); + } + + public Task> GetJobWhitelists(Guid player, CancellationToken cancel = default) + { + DbReadOpsMetric.Inc(); + return RunDbCommand(() => _db.GetJobWhitelists(player, cancel)); + } + + public Task IsJobWhitelisted(Guid player, ProtoId job) + { + DbReadOpsMetric.Inc(); + return RunDbCommand(() => _db.IsJobWhitelisted(player, job)); + } + + public Task RemoveJobWhitelist(Guid player, ProtoId job) + { + DbWriteOpsMetric.Inc(); + return RunDbCommand(() => _db.RemoveJobWhitelist(player, job)); + } + // Wrapper functions to run DB commands from the thread pool. // This will avoid SynchronizationContext capturing and avoid running CPU work on the main thread. // For SQLite, this will also enable read parallelization (within limits). diff --git a/Content.Server/Database/UserDbDataManager.cs b/Content.Server/Database/UserDbDataManager.cs index c58c594dba..fde610a6fd 100644 --- a/Content.Server/Database/UserDbDataManager.cs +++ b/Content.Server/Database/UserDbDataManager.cs @@ -1,8 +1,6 @@ using System.Threading; using System.Threading.Tasks; -using Content.Server.Players.PlayTimeTracking; using Content.Server.Preferences.Managers; -using Robust.Server.Player; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Utility; @@ -19,11 +17,12 @@ namespace Content.Server.Database; /// public sealed class UserDbDataManager : IPostInjectInit { - [Dependency] private readonly IServerPreferencesManager _prefs = default!; [Dependency] private readonly ILogManager _logManager = default!; - [Dependency] private readonly PlayTimeTrackingManager _playTimeTracking = default!; private readonly Dictionary _users = new(); + private readonly List _onLoadPlayer = []; + private readonly List _onFinishLoad = []; + private readonly List _onPlayerDisconnect = []; private ISawmill _sawmill = default!; @@ -51,8 +50,10 @@ public void ClientDisconnected(ICommonSession session) data.Cancel.Cancel(); data.Cancel.Dispose(); - _prefs.OnClientDisconnected(session); - _playTimeTracking.ClientDisconnected(session); + foreach (var onDisconnect in _onPlayerDisconnect) + { + onDisconnect(session); + } } private async Task Load(ICommonSession session, CancellationToken cancel) @@ -62,12 +63,19 @@ private async Task Load(ICommonSession session, CancellationToken cancel) // As such, this task must NOT throw a non-cancellation error! try { - await Task.WhenAll( - _prefs.LoadData(session, cancel), - _playTimeTracking.LoadData(session, cancel)); + var tasks = new List(); + foreach (var action in _onLoadPlayer) + { + tasks.Add(action(session, cancel)); + } + + await Task.WhenAll(tasks); cancel.ThrowIfCancellationRequested(); - _prefs.FinishLoad(session); + foreach (var action in _onFinishLoad) + { + action(session); + } _sawmill.Verbose($"Load complete for user {session}"); } @@ -123,5 +131,26 @@ void IPostInjectInit.PostInject() _sawmill = _logManager.GetSawmill("userdb"); } + public void AddOnLoadPlayer(OnLoadPlayer action) + { + _onLoadPlayer.Add(action); + } + + public void AddOnFinishLoad(OnFinishLoad action) + { + _onFinishLoad.Add(action); + } + + public void AddOnPlayerDisconnect(OnPlayerDisconnect action) + { + _onPlayerDisconnect.Add(action); + } + private sealed record UserData(CancellationTokenSource Cancel, Task Task); + + public delegate Task OnLoadPlayer(ICommonSession player, CancellationToken cancel); + + public delegate void OnFinishLoad(ICommonSession player); + + public delegate void OnPlayerDisconnect(ICommonSession player); } diff --git a/Content.Server/Entry/EntryPoint.cs b/Content.Server/Entry/EntryPoint.cs index 48a6597349..dda783c432 100644 --- a/Content.Server/Entry/EntryPoint.cs +++ b/Content.Server/Entry/EntryPoint.cs @@ -14,6 +14,7 @@ using Content.Server.GuideGenerator; using Content.Server.Info; using Content.Server.IoC; +using Content.Server.Players.JobWhitelist; using Content.Server.Maps; using Content.Server.NodeContainer.NodeGroups; using Content.Server.Players.PlayTimeTracking; @@ -111,6 +112,7 @@ public override void Init() _voteManager.Initialize(); _updateManager.Initialize(); _playTimeTracking.Initialize(); + IoCManager.Resolve().Initialize(); } } diff --git a/Content.Server/GameTicking/Events/GetDisallowedJobsEvent.cs b/Content.Server/GameTicking/Events/GetDisallowedJobsEvent.cs new file mode 100644 index 0000000000..cd15cfb8f8 --- /dev/null +++ b/Content.Server/GameTicking/Events/GetDisallowedJobsEvent.cs @@ -0,0 +1,8 @@ +using Content.Shared.Roles; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.GameTicking.Events; + +[ByRefEvent] +public readonly record struct GetDisallowedJobsEvent(ICommonSession Player, HashSet> Jobs); diff --git a/Content.Server/GameTicking/Events/IsJobAllowedEvent.cs b/Content.Server/GameTicking/Events/IsJobAllowedEvent.cs new file mode 100644 index 0000000000..51969d61ea --- /dev/null +++ b/Content.Server/GameTicking/Events/IsJobAllowedEvent.cs @@ -0,0 +1,13 @@ +using Content.Shared.Roles; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Server.GameTicking.Events; + +[ByRefEvent] +public struct IsJobAllowedEvent(ICommonSession player, ProtoId jobId, bool cancelled = false) +{ + public readonly ICommonSession Player = player; + public readonly ProtoId JobId = jobId; + public bool Cancelled = cancelled; +} diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 074c2d6a69..2e526b4d13 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Numerics; using Content.Server.Administration.Managers; +using Content.Server.GameTicking.Events; using Content.Server.Ghost; using Content.Server.Spawners.Components; using Content.Server.Speech.Components; @@ -138,8 +139,14 @@ private void SpawnPlayer(ICommonSession player, if (jobBans == null || jobId != null && jobBans.Contains(jobId)) return; - if (jobId != null && !_playTimeTrackings.IsAllowed(player, jobId)) - return; + if (jobId != null) + { + var ev = new IsJobAllowedEvent(player, new ProtoId(jobId)); + RaiseLocalEvent(ref ev); + if (ev.Cancelled) + return; + } + SpawnPlayer(player, character, station, jobId, lateJoin, silent); } @@ -196,10 +203,9 @@ private void SpawnPlayer(ICommonSession player, } // Figure out job restrictions - var restrictedRoles = new HashSet(); - - var getDisallowed = _playTimeTrackings.GetDisallowedJobs(player); - restrictedRoles.UnionWith(getDisallowed); + var restrictedRoles = new HashSet>(); + var ev = new GetDisallowedJobsEvent(player, restrictedRoles); + RaiseLocalEvent(ref ev); var jobBans = _banManager.GetJobBans(player.UserId); if (jobBans != null) @@ -233,7 +239,7 @@ private void SpawnPlayer(ICommonSession player, _mind.SetUserId(newMind, data.UserId); var jobPrototype = _prototypeManager.Index(jobId); - var job = new JobComponent {Prototype = jobId}; + var job = new JobComponent { Prototype = jobId }; _roles.MindAddRole(newMind, job, silent: silent); var jobName = _jobs.MindTryGetJobName(newMind); diff --git a/Content.Server/IoC/ServerContentIoC.cs b/Content.Server/IoC/ServerContentIoC.cs index bc39997735..7c15014133 100644 --- a/Content.Server/IoC/ServerContentIoC.cs +++ b/Content.Server/IoC/ServerContentIoC.cs @@ -13,6 +13,7 @@ using Content.Server.GhostKick; using Content.Server.Info; using Content.Server.Maps; +using Content.Server.Players.JobWhitelist; using Content.Server.MoMMI; using Content.Server.NodeContainer.NodeGroups; using Content.Server.Players.PlayTimeTracking; @@ -61,6 +62,7 @@ public static void Register() IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); + IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); IoCManager.Register(); diff --git a/Content.Server/Players/JobWhitelist/JobWhitelistManager.cs b/Content.Server/Players/JobWhitelist/JobWhitelistManager.cs new file mode 100644 index 0000000000..04289a4098 --- /dev/null +++ b/Content.Server/Players/JobWhitelist/JobWhitelistManager.cs @@ -0,0 +1,114 @@ +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Content.Server.Database; +using Content.Shared.CCVar; +using Content.Shared.Players.JobWhitelist; +using Content.Shared.Roles; +using Robust.Server.Player; +using Robust.Shared.Configuration; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Serilog; + +namespace Content.Server.Players.JobWhitelist; + +public sealed class JobWhitelistManager : IPostInjectInit +{ + [Dependency] private readonly IConfigurationManager _config = default!; + [Dependency] private readonly IServerDbManager _db = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + [Dependency] private readonly UserDbDataManager _userDb = default!; + + private readonly Dictionary> _whitelists = new(); + + public void Initialize() + { + _net.RegisterNetMessage(); + } + + private async Task LoadData(ICommonSession session, CancellationToken cancel) + { + var whitelists = await _db.GetJobWhitelists(session.UserId, cancel); + cancel.ThrowIfCancellationRequested(); + _whitelists[session.UserId] = whitelists.ToHashSet(); + } + + private void FinishLoad(ICommonSession session) + { + SendJobWhitelist(session); + } + + private void ClientDisconnected(ICommonSession session) + { + _whitelists.Remove(session.UserId); + } + + public async void AddWhitelist(NetUserId player, ProtoId job) + { + if (_whitelists.TryGetValue(player, out var whitelists)) + whitelists.Add(job); + + await _db.AddJobWhitelist(player, job); + + if (_player.TryGetSessionById(player, out var session)) + SendJobWhitelist(session); + } + + public bool IsAllowed(ICommonSession session, ProtoId job) + { + if (!_config.GetCVar(CCVars.GameRoleWhitelist)) + return true; + + if (!_prototypes.TryIndex(job, out var jobPrototype) || + !jobPrototype.Whitelisted) + { + return true; + } + + return IsWhitelisted(session.UserId, job); + } + + public bool IsWhitelisted(NetUserId player, ProtoId job) + { + if (!_whitelists.TryGetValue(player, out var whitelists)) + { + Log.Error("Unable to check if player {Player} is whitelisted for {Job}. Stack trace:\\n{StackTrace}", + player, + job, + Environment.StackTrace); + return false; + } + + return whitelists.Contains(job); + } + + public async void RemoveWhitelist(NetUserId player, ProtoId job) + { + _whitelists.GetValueOrDefault(player)?.Remove(job); + await _db.RemoveJobWhitelist(player, job); + + if (_player.TryGetSessionById(new NetUserId(player), out var session)) + SendJobWhitelist(session); + } + + public void SendJobWhitelist(ICommonSession player) + { + var msg = new MsgJobWhitelist + { + Whitelist = _whitelists.GetValueOrDefault(player.UserId) ?? new HashSet() + }; + + _net.ServerSendMessage(msg, player.Channel); + } + + void IPostInjectInit.PostInject() + { + _userDb.AddOnLoadPlayer(LoadData); + _userDb.AddOnFinishLoad(FinishLoad); + _userDb.AddOnPlayerDisconnect(ClientDisconnected); + } +} diff --git a/Content.Server/Players/JobWhitelist/JobWhitelistSystem.cs b/Content.Server/Players/JobWhitelist/JobWhitelistSystem.cs new file mode 100644 index 0000000000..aaada99dea --- /dev/null +++ b/Content.Server/Players/JobWhitelist/JobWhitelistSystem.cs @@ -0,0 +1,83 @@ +using System.Collections.Immutable; +using Content.Server.GameTicking.Events; +using Content.Server.Station.Events; +using Content.Shared.CCVar; +using Content.Shared.Roles; +using Robust.Server.Player; +using Robust.Shared.Configuration; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; + +namespace Content.Server.Players.JobWhitelist; + +public sealed class JobWhitelistSystem : EntitySystem +{ + [Dependency] private readonly IConfigurationManager _config = default!; + [Dependency] private readonly JobWhitelistManager _manager = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _prototypes = default!; + + private ImmutableArray> _whitelistedJobs = []; + + public override void Initialize() + { + SubscribeLocalEvent(OnPrototypesReloaded); + SubscribeLocalEvent(OnStationJobsGetCandidates); + SubscribeLocalEvent(OnIsJobAllowed); + SubscribeLocalEvent(OnGetDisallowedJobs); + + CacheJobs(); + } + + private void OnPrototypesReloaded(PrototypesReloadedEventArgs ev) + { + if (ev.WasModified()) + CacheJobs(); + } + + private void OnStationJobsGetCandidates(ref StationJobsGetCandidatesEvent ev) + { + if (!_config.GetCVar(CCVars.GameRoleWhitelist)) + return; + + for (var i = ev.Jobs.Count - 1; i >= 0; i--) + { + var jobId = ev.Jobs[i]; + if (_player.TryGetSessionById(ev.Player, out var player) && + !_manager.IsAllowed(player, jobId)) + { + ev.Jobs.RemoveSwap(i); + } + } + } + + private void OnIsJobAllowed(ref IsJobAllowedEvent ev) + { + if (!_manager.IsAllowed(ev.Player, ev.JobId)) + ev.Cancelled = true; + } + + private void OnGetDisallowedJobs(ref GetDisallowedJobsEvent ev) + { + if (!_config.GetCVar(CCVars.GameRoleWhitelist)) + return; + + foreach (var job in _whitelistedJobs) + { + if (!_manager.IsAllowed(ev.Player, job)) + ev.Jobs.Add(job); + } + } + + private void CacheJobs() + { + var builder = ImmutableArray.CreateBuilder>(); + foreach (var job in _prototypes.EnumeratePrototypes()) + { + if (job.Whitelisted) + builder.Add(job.ID); + } + + _whitelistedJobs = builder.ToImmutable(); + } +} diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs index 943b4cf786..295d11f898 100644 --- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs +++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingManager.cs @@ -55,7 +55,7 @@ namespace Content.Server.Players.PlayTimeTracking; /// Operations like refreshing and sending play time info to clients are deferred until the next frame (note: not tick). /// /// -public sealed partial class PlayTimeTrackingManager : ISharedPlaytimeManager +public sealed partial class PlayTimeTrackingManager : ISharedPlaytimeManager, IPostInjectInit { [Dependency] private readonly IServerDbManager _db = default!; [Dependency] private readonly IServerNetManager _net = default!; @@ -63,6 +63,7 @@ public sealed partial class PlayTimeTrackingManager : ISharedPlaytimeManager [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ITaskManager _task = default!; [Dependency] private readonly IRuntimeLog _runtimeLog = default!; + [Dependency] private readonly UserDbDataManager _userDb = default!; private ISawmill _sawmill = default!; @@ -456,4 +457,10 @@ private sealed class PlayTimeData /// public readonly HashSet DbTrackersDirty = new(); } + + void IPostInjectInit.PostInject() + { + _userDb.AddOnLoadPlayer(LoadData); + _userDb.AddOnPlayerDisconnect(ClientDisconnected); + } } diff --git a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs index 14eefbd6b8..ba7b9f3c37 100644 --- a/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs +++ b/Content.Server/Players/PlayTimeTracking/PlayTimeTrackingSystem.cs @@ -4,7 +4,9 @@ using Content.Server.Afk; using Content.Server.Afk.Events; using Content.Server.GameTicking; +using Content.Server.GameTicking.Events; using Content.Server.Mind; +using Content.Server.Station.Events; using Content.Server.Preferences.Managers; using Content.Shared.CCVar; using Content.Shared.Customization.Systems; @@ -15,7 +17,6 @@ using Content.Shared.Players.PlayTimeTracking; using Content.Shared.Preferences; using Content.Shared.Roles; -using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Network; @@ -57,6 +58,9 @@ public override void Initialize() SubscribeLocalEvent(OnUnAFK); SubscribeLocalEvent(OnMobStateChanged); SubscribeLocalEvent(OnPlayerJoinedLobby); + SubscribeLocalEvent(OnStationJobsGetCandidates); + SubscribeLocalEvent(OnIsJobAllowed); + SubscribeLocalEvent(OnGetDisallowedJobs); _adminManager.OnPermsChanged += AdminPermsChanged; } @@ -174,6 +178,22 @@ private void OnMobStateChanged(MobStateChangedEvent ev) _tracking.QueueRefreshTrackers(actor.PlayerSession); } + private void OnStationJobsGetCandidates(ref StationJobsGetCandidatesEvent ev) + { + RemoveDisallowedJobs(ev.Player, ev.Jobs); + } + + private void OnIsJobAllowed(ref IsJobAllowedEvent ev) + { + if (!IsAllowed(ev.Player, ev.JobId)) + ev.Cancelled = true; + } + + private void OnGetDisallowedJobs(ref GetDisallowedJobsEvent ev) + { + ev.Jobs.UnionWith(GetDisallowedJobs(ev.Player)); + } + private void OnPlayerJoinedLobby(PlayerJoinedLobbyEvent ev) { _tracking.QueueRefreshTrackers(ev.PlayerSession); @@ -210,9 +230,9 @@ public bool IsAllowed(ICommonSession player, string role) out _); } - public HashSet GetDisallowedJobs(ICommonSession player) + public HashSet> GetDisallowedJobs(ICommonSession player) { - var roles = new HashSet(); + var roles = new HashSet>(); if (!_cfg.GetCVar(CCVars.GameRoleTimers)) return roles; @@ -251,7 +271,7 @@ public HashSet GetDisallowedJobs(ICommonSession player) return roles; } - public void RemoveDisallowedJobs(NetUserId userId, ref List jobs) + public void RemoveDisallowedJobs(NetUserId userId, List> jobs) { if (!_cfg.GetCVar(CCVars.GameRoleTimers)) return; @@ -270,7 +290,7 @@ public void RemoveDisallowedJobs(NetUserId userId, ref List jobs) { var job = jobs[i]; - if (!_prototypes.TryIndex(job, out var jobber) || + if (!_prototypes.TryIndex(job, out var jobber) || jobber.Requirements == null || jobber.Requirements.Count == 0) continue; diff --git a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs index 052eccb280..df7bac72e9 100644 --- a/Content.Server/Preferences/Managers/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/Managers/ServerPreferencesManager.cs @@ -18,7 +18,7 @@ namespace Content.Server.Preferences.Managers /// Sends before the client joins the lobby. /// Receives and at any time. /// - public sealed class ServerPreferencesManager : IServerPreferencesManager + public sealed class ServerPreferencesManager : IServerPreferencesManager, IPostInjectInit { [Dependency] private readonly IServerNetManager _netManager = default!; [Dependency] private readonly IConfigurationManager _cfg = default!; @@ -26,6 +26,7 @@ public sealed class ServerPreferencesManager : IServerPreferencesManager [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IDependencyCollection _dependencies = default!; [Dependency] private readonly ILogManager _log = default!; + [Dependency] private readonly UserDbDataManager _userDb = default!; // Cache player prefs on the server so we don't need as much async hell related to them. private readonly Dictionary _cachedPlayerPrefs = @@ -178,7 +179,7 @@ public async Task LoadData(ICommonSession session, CancellationToken cancel) { PrefsLoaded = true, Prefs = new PlayerPreferences( - new[] {new KeyValuePair(0, HumanoidCharacterProfile.Random())}, + new[] { new KeyValuePair(0, HumanoidCharacterProfile.Random()) }, 0, Color.Transparent) }; @@ -321,5 +322,12 @@ private sealed class PlayerPrefData public bool PrefsLoaded; public PlayerPreferences? Prefs; } + + void IPostInjectInit.PostInject() + { + _userDb.AddOnLoadPlayer(LoadData); + _userDb.AddOnFinishLoad(FinishLoad); + _userDb.AddOnPlayerDisconnect(OnClientDisconnected); + } } } diff --git a/Content.Server/Station/Events/StationJobsGetCandidatesEvent.cs b/Content.Server/Station/Events/StationJobsGetCandidatesEvent.cs new file mode 100644 index 0000000000..58d860b1e1 --- /dev/null +++ b/Content.Server/Station/Events/StationJobsGetCandidatesEvent.cs @@ -0,0 +1,8 @@ +using Content.Shared.Roles; +using Robust.Shared.Network; +using Robust.Shared.Prototypes; + +namespace Content.Server.Station.Events; + +[ByRefEvent] +public readonly record struct StationJobsGetCandidatesEvent(NetUserId Player, List> Jobs); diff --git a/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs b/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs index 4b7cd64961..c3c3865c7b 100644 --- a/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs +++ b/Content.Server/Station/Systems/StationJobsSystem.Roundstart.cs @@ -2,6 +2,7 @@ using Content.Server.Administration.Managers; using Content.Server.Players.PlayTimeTracking; using Content.Server.Station.Components; +using Content.Server.Station.Events; using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Shared.Network; @@ -342,8 +343,9 @@ private Dictionary> GetPlayersJobCandidates(int? weight, foreach (var (player, profile) in profiles) { var roleBans = _banManager.GetJobBans(player); - var profileJobs = profile.JobPriorities.Keys.ToList(); - _playTime.RemoveDisallowedJobs(player, ref profileJobs); + var profileJobs = profile.JobPriorities.Keys.Select(k => new ProtoId(k)).ToList(); + var ev = new StationJobsGetCandidatesEvent(player, profileJobs); + RaiseLocalEvent(ref ev); List? availableJobs = null; @@ -354,7 +356,7 @@ private Dictionary> GetPlayersJobCandidates(int? weight, if (!(priority == selectedPriority || selectedPriority is null)) continue; - if (!_prototypeManager.TryIndex(jobId, out JobPrototype? job)) + if (!_prototypeManager.TryIndex(jobId, out var job)) continue; if (weight is not null && job.Weight != weight.Value) diff --git a/Content.Server/Station/Systems/StationJobsSystem.cs b/Content.Server/Station/Systems/StationJobsSystem.cs index debac8902e..3bfa815af1 100644 --- a/Content.Server/Station/Systems/StationJobsSystem.cs +++ b/Content.Server/Station/Systems/StationJobsSystem.cs @@ -428,7 +428,7 @@ public IReadOnlySet GetOverflowJobs(EntityUid station, StationJobsCompon /// Whether or not to pick from the overflow list. /// A set of disallowed jobs, if any. /// The selected job, if any. - public string? PickBestAvailableJobWithPriority(EntityUid station, IReadOnlyDictionary jobPriorities, bool pickOverflows, IReadOnlySet? disallowedJobs = null) + public string? PickBestAvailableJobWithPriority(EntityUid station, IReadOnlyDictionary jobPriorities, bool pickOverflows, IReadOnlySet>? disallowedJobs = null) { if (station == EntityUid.Invalid) return null; diff --git a/Content.Shared/Administration/JobWhitelistsEuiState.cs b/Content.Shared/Administration/JobWhitelistsEuiState.cs new file mode 100644 index 0000000000..e993ab3aa5 --- /dev/null +++ b/Content.Shared/Administration/JobWhitelistsEuiState.cs @@ -0,0 +1,35 @@ +using Content.Shared.Eui; +using Content.Shared.Roles; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Administration; + +[Serializable, NetSerializable] +public sealed class JobWhitelistsEuiState : EuiStateBase +{ + public string PlayerName; + public HashSet> Whitelists; + + public JobWhitelistsEuiState(string playerName, HashSet> whitelists) + { + PlayerName = playerName; + Whitelists = whitelists; + } +} + +/// +/// Tries to add or remove a whitelist of a job for a player. +/// +[Serializable, NetSerializable] +public sealed class SetJobWhitelistedMessage : EuiMessageBase +{ + public ProtoId Job; + public bool Whitelisting; + + public SetJobWhitelistedMessage(ProtoId job, bool whitelisting) + { + Job = job; + Whitelisting = whitelisting; + } +} diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 679b99524e..bacb2d2361 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -224,6 +224,13 @@ public static readonly CVarDef public static readonly CVarDef GameMap = CVarDef.Create("game.map", string.Empty, CVar.SERVERONLY); + /// + /// If roles should be restricted based on whether or not they are whitelisted. + /// + public static readonly CVarDef + GameRoleWhitelist = CVarDef.Create("game.role_whitelist", true, CVar.SERVER | CVar.REPLICATED); + + /// /// Controls whether to use world persistence or not. /// diff --git a/Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs b/Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs index d23d472e06..78a520ca8d 100644 --- a/Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs +++ b/Content.Shared/Customization/Systems/CharacterRequirements.Whitelist.cs @@ -6,6 +6,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; +using Robust.Shared.Network; namespace Content.Shared.Customization.Systems; diff --git a/Content.Shared/Players/JobWhitelist/MsgJobWhitelist.cs b/Content.Shared/Players/JobWhitelist/MsgJobWhitelist.cs new file mode 100644 index 0000000000..27b0b6c614 --- /dev/null +++ b/Content.Shared/Players/JobWhitelist/MsgJobWhitelist.cs @@ -0,0 +1,33 @@ +using Lidgren.Network; +using Robust.Shared.Network; +using Robust.Shared.Serialization; + +namespace Content.Shared.Players.JobWhitelist; + +public sealed class MsgJobWhitelist : NetMessage +{ + public override MsgGroups MsgGroup => MsgGroups.EntityEvent; + + public HashSet Whitelist = new(); + + public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) + { + var count = buffer.ReadVariableInt32(); + Whitelist.EnsureCapacity(count); + + for (var i = 0; i < count; i++) + { + Whitelist.Add(buffer.ReadString()); + } + } + + public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) + { + buffer.WriteVariableInt32(Whitelist.Count); + + foreach (var ban in Whitelist) + { + buffer.Write(ban); + } + } +} diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index 15f8233aab..5cf8cf38fb 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -123,6 +123,9 @@ public sealed partial class JobPrototype : IPrototype [DataField("extendedAccessGroups")] public IReadOnlyCollection> ExtendedAccessGroups { get; private set; } = Array.Empty>(); + + [DataField] + public bool Whitelisted; } /// diff --git a/Resources/Locale/en-US/administration/ui/player-panel.ftl b/Resources/Locale/en-US/administration/ui/player-panel.ftl new file mode 100644 index 0000000000..208268d589 --- /dev/null +++ b/Resources/Locale/en-US/administration/ui/player-panel.ftl @@ -0,0 +1 @@ +player-panel-job-whitelists = Job Whitelists diff --git a/Resources/Locale/en-US/commands/job-whitelist-command.ftl b/Resources/Locale/en-US/commands/job-whitelist-command.ftl new file mode 100644 index 0000000000..add6bca760 --- /dev/null +++ b/Resources/Locale/en-US/commands/job-whitelist-command.ftl @@ -0,0 +1,18 @@ +cmd-jobwhitelist-job-does-not-exist = Job {$job} does not exist. +cmd-jobwhitelist-player-not-found = Player {$player} not found. +cmd-jobwhitelist-hint-player = [player] +cmd-jobwhitelist-hint-job = [job] +cmd-jobwhitelistadd-desc = Lets a player play a whitelisted job. +cmd-jobwhitelistadd-help = Usage: jobwhitelistadd +cmd-jobwhitelistadd-already-whitelisted = {$player} is already whitelisted to play as {$jobId} .({$jobName}). +cmd-jobwhitelistadd-added = Added {$player} to the {$jobId} ({$jobName}) whitelist. +cmd-jobwhitelistget-desc = Gets all the jobs that a player has been whitelisted for. +cmd-jobwhitelistget-help = Usage: jobwhitelistadd +cmd-jobwhitelistget-whitelisted-none = Player {$player} is not whitelisted for any jobs. +cmd-jobwhitelistget-whitelisted-for = "Player {$player} is whitelisted for: +{$jobs}" + +cmd-jobwhitelistremove-desc = Removes a player's ability to play a whitelisted job. +cmd-jobwhitelistremove-help = Usage: jobwhitelistadd +cmd-jobwhitelistremove-was-not-whitelisted = {$player} was not whitelisted to play as {$jobId} ({$jobName}). +cmd-jobwhitelistremove-removed = Removed {$player} from the whitelist for {$jobId} ({$jobName}). diff --git a/Resources/Locale/en-US/info/whitelists.ftl b/Resources/Locale/en-US/info/whitelists.ftl new file mode 100644 index 0000000000..b67fc3e0ad --- /dev/null +++ b/Resources/Locale/en-US/info/whitelists.ftl @@ -0,0 +1,3 @@ +cmd-jobwhitelists-desc = Opens the job whitelists window +cmd-jobwhitelists-help = Usage: jobwhitelists [name or user guid] +cmd-jobwhitelists-player-err = The specified player could not be found diff --git a/Resources/Locale/en-US/job/role-whitelist.ftl b/Resources/Locale/en-US/job/role-whitelist.ftl new file mode 100644 index 0000000000..70031a650d --- /dev/null +++ b/Resources/Locale/en-US/job/role-whitelist.ftl @@ -0,0 +1 @@ +role-not-whitelisted = You are not whitelisted to play this role. From d7de8082d3b3c77832a2c190db1e0c03752cb930 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sat, 19 Oct 2024 22:11:17 +0000 Subject: [PATCH 55/76] Automatic Changelog Update (#1093) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7a41c3f071..51feb49bfe 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7406,3 +7406,10 @@ Entries: id: 6467 time: '2024-10-19T21:28:50.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1062 +- author: FoxxoTrystan + changes: + - type: Add + message: Job Whitelist! + id: 6468 + time: '2024-10-19T22:10:51.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1093 From 0383090d67bfdf777134647aed48060d424db416 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sat, 19 Oct 2024 21:45:50 -0400 Subject: [PATCH 56/76] Various Wielding Fixes (#1078) Unhand me, fiend # Description Literally all of Wizden's "Wielding Quality Of Life" improvements. Fixes #1074 Fixes #1073 Cherry-picks the following PRs: https://github.com/space-wizards/space-station-14/pull/26970 https://github.com/space-wizards/space-station-14/pull/27973 https://github.com/space-wizards/space-station-14/pull/27975 https://github.com/space-wizards/space-station-14/pull/28438 https://github.com/space-wizards/space-station-14/pull/27975 https://github.com/space-wizards/space-station-14/pull/28002 https://github.com/space-wizards/space-station-14/pull/28161 # Changelog :cl: - fix: Ported literally all of Wizden's Wielding Quality Of Life improvements. --------- Signed-off-by: VMSolidus Co-authored-by: RiceMar1244 <138547931+RiceMar1244@users.noreply.github.com> Co-authored-by: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com> Co-authored-by: WarMechanic <69510347+WarMechanic@users.noreply.github.com> Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com> Co-authored-by: DEATHB4DEFEAT <77995199+DEATHB4DEFEAT@users.noreply.github.com> --- Content.Server/Execution/ExecutionSystem.cs | 2 +- .../Hands/Components/HandHelpers.cs | 10 +++++ .../Hands/EntitySystems/SharedHandsSystem.cs | 11 ++++- .../VirtualItem/SharedVirtualItemSystem.cs | 43 ++++++++++++++++--- .../Components/GunRequiresWieldComponent.cs | 7 ++- .../Ranged/Events/ShotAttemptedEvent.cs | 4 +- .../Weapons/Ranged/Systems/SharedGunSystem.cs | 2 +- Content.Shared/Wieldable/WieldableSystem.cs | 43 +++++++++++++++---- .../Locale/en-US/virtual/virtual-item.ftl | 1 + .../Weapons/Guns/Basic/base_wieldable.yml | 1 + .../Objects/Weapons/Guns/SMGs/smgs.yml | 4 +- .../Weapons/Guns/Shotguns/shotguns.yml | 2 +- 12 files changed, 108 insertions(+), 22 deletions(-) create mode 100644 Resources/Locale/en-US/virtual/virtual-item.ftl diff --git a/Content.Server/Execution/ExecutionSystem.cs b/Content.Server/Execution/ExecutionSystem.cs index 326aa1d6a4..c1080b84ad 100644 --- a/Content.Server/Execution/ExecutionSystem.cs +++ b/Content.Server/Execution/ExecutionSystem.cs @@ -281,7 +281,7 @@ private void OnDoafterGun(EntityUid uid, GunComponent component, DoAfterEvent ar var prevention = new ShotAttemptedEvent { User = attacker, - Used = weapon + Used = new Entity(uid, component) }; RaiseLocalEvent(weapon, ref prevention); diff --git a/Content.Shared/Hands/Components/HandHelpers.cs b/Content.Shared/Hands/Components/HandHelpers.cs index 11fff6d9c8..aecf3a6936 100644 --- a/Content.Shared/Hands/Components/HandHelpers.cs +++ b/Content.Shared/Hands/Components/HandHelpers.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared.Hands.EntitySystems; namespace Content.Shared.Hands.Components; @@ -20,6 +21,15 @@ public static class HandHelpers /// public static int CountFreeHands(this HandsComponent component) => component.Hands.Values.Count(hand => hand.IsEmpty); + /// + /// Get the number of hands that are not currently holding anything. This is a LinQ method, not a property, so + /// cache it instead of accessing this multiple times. + /// + public static int CountFreeableHands(this Entity component, SharedHandsSystem system) + { + return system.CountFreeableHands(component); + } + /// /// Get a list of hands that are currently holding nothing. This is a LinQ method, not a property, so cache /// it instead of accessing this multiple times. diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs index fd732009e9..40f2c5bbd5 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs @@ -5,7 +5,6 @@ using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Inventory.VirtualItem; -using Content.Shared.Item; using Content.Shared.Storage.EntitySystems; using Robust.Shared.Containers; using Robust.Shared.Input.Binding; @@ -299,4 +298,14 @@ public bool TryGetHand(EntityUid handsUid, string handId, [NotNullWhen(true)] ou return hands.Hands.TryGetValue(handId, out hand); } + + public int CountFreeableHands(Entity hands) + { + var freeable = 0; + foreach (var hand in hands.Comp.Hands.Values) + if (hand.IsEmpty || CanDropHeld(hands, hand)) + freeable++; + + return freeable; + } } diff --git a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs index e45530e458..60beb54652 100644 --- a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs +++ b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Interaction; using Content.Shared.Inventory.Events; using Content.Shared.Item; +using Content.Shared.Popups; using Robust.Shared.Containers; using Robust.Shared.Network; using Robust.Shared.Prototypes; @@ -29,6 +30,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem [Dependency] private readonly SharedItemSystem _itemSystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; [ValidatePrototypeId] private const string VirtualItem = "VirtualItem"; @@ -71,23 +73,48 @@ private void OnBeforeRangedInteract(Entity ent, ref Before } #region Hands + /// /// Spawns a virtual item in a empty hand /// /// The entity we will make a virtual entity copy of /// The entity that we want to insert the virtual entity - public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user) + /// Whether or not to try and drop other items to make space + public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, bool dropOthers = false) { - return TrySpawnVirtualItemInHand(blockingEnt, user, out _); + return TrySpawnVirtualItemInHand(blockingEnt, user, out _, dropOthers); } - /// - public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem) + /// + public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem, bool dropOthers = false) { - if (!TrySpawnVirtualItem(blockingEnt, user, out virtualItem) || !_handsSystem.TryGetEmptyHand(user, out var hand)) + virtualItem = null; + if (!_handsSystem.TryGetEmptyHand(user, out var empty)) + { + if (!dropOthers) + return false; + + foreach (var hand in _handsSystem.EnumerateHands(user)) + { + if (hand.HeldEntity is not { } held + || held == blockingEnt + || HasComp(held) + || !_handsSystem.TryDrop(user, hand)) + continue; + + if (!TerminatingOrDeleted(held)) + _popup.PopupClient(Loc.GetString("virtual-item-dropped-other", ("dropped", held)), user, user); + + empty = hand; + break; + } + } + + if (empty == null + || !TrySpawnVirtualItem(blockingEnt, user, out virtualItem)) return false; - _handsSystem.DoPickup(user, hand, virtualItem.Value); + _handsSystem.DoPickup(user, empty, virtualItem.Value); return true; } @@ -120,6 +147,7 @@ public void DeleteInHandsMatching(EntityUid user, EntityUid matching) /// The entity we will make a virtual entity copy of /// The entity that we want to insert the virtual entity /// The slot to which we will insert the virtual entity (could be the "shoes" slot, for example) + /// Whether or not to force an equip public bool TrySpawnVirtualItemInInventory(EntityUid blockingEnt, EntityUid user, string slot, bool force = false) { return TrySpawnVirtualItemInInventory(blockingEnt, user, slot, force, out _); @@ -140,6 +168,8 @@ public bool TrySpawnVirtualItemInInventory(EntityUid blockingEnt, EntityUid user /// that's done check if the found virtual entity is a copy of our matching entity, /// if it is, delete it /// + /// The entity that we want to delete the virtual entity from + /// The entity that made the virtual entity /// Set this param if you have the name of the slot, it avoids unnecessary queries public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slotName = null) { @@ -178,6 +208,7 @@ public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slo ///
/// The entity we will make a virtual entity copy of /// The entity that we want to insert the virtual entity + /// The virtual item, if spawned public bool TrySpawnVirtualItem(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem) { if (_netManager.IsClient) diff --git a/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs index 2ae71334b4..fa3732209f 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs @@ -6,8 +6,13 @@ namespace Content.Shared.Weapons.Ranged.Components; /// /// Indicates that this gun requires wielding to be useable. /// -[RegisterComponent, NetworkedComponent, Access(typeof(WieldableSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(WieldableSystem))] public sealed partial class GunRequiresWieldComponent : Component { + [DataField, AutoNetworkedField] + public TimeSpan LastPopup; + [DataField, AutoNetworkedField] + public TimeSpan PopupCooldown = TimeSpan.FromSeconds(1); } diff --git a/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs b/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs index 40925ad614..d61862bf1a 100644 --- a/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs +++ b/Content.Shared/Weapons/Ranged/Events/ShotAttemptedEvent.cs @@ -1,3 +1,5 @@ +using Content.Shared.Weapons.Ranged.Components; + namespace Content.Shared.Weapons.Ranged.Events; /// @@ -15,7 +17,7 @@ public record struct ShotAttemptedEvent /// /// The gun being shot. /// - public EntityUid Used; + public Entity Used; public bool Cancelled { get; private set; } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index 989fad160c..b714acefbd 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -249,7 +249,7 @@ private void AttemptShoot(EntityUid user, EntityUid gunUid, GunComponent gun) var prevention = new ShotAttemptedEvent { User = user, - Used = gunUid + Used = (gunUid, gun) }; RaiseLocalEvent(gunUid, ref prevention); if (prevention.Cancelled) diff --git a/Content.Shared/Wieldable/WieldableSystem.cs b/Content.Shared/Wieldable/WieldableSystem.cs index 778a664e2c..95fc69e061 100644 --- a/Content.Shared/Wieldable/WieldableSystem.cs +++ b/Content.Shared/Wieldable/WieldableSystem.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Shared.Examine; using Content.Shared.Hands; using Content.Shared.Hands.Components; @@ -16,7 +17,7 @@ using Content.Shared.Weapons.Ranged.Systems; using Content.Shared.Wieldable.Components; using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; +using Robust.Shared.Timing; namespace Content.Shared.Wieldable; @@ -30,6 +31,7 @@ public sealed class WieldableSystem : EntitySystem [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly UseDelaySystem _delay = default!; [Dependency] private readonly SharedGunSystem _gun = default!; + [Dependency] private readonly IGameTiming _timing = default!; public override void Initialize() { @@ -40,9 +42,10 @@ public override void Initialize() SubscribeLocalEvent(OnItemLeaveHand); SubscribeLocalEvent(OnVirtualItemDeleted); SubscribeLocalEvent>(AddToggleWieldVerb); + SubscribeLocalEvent(OnDeselectWieldable); SubscribeLocalEvent(OnMeleeAttempt); - SubscribeLocalEvent(OnShootAttempt); + SubscribeLocalEvent(OnShootAttempt); SubscribeLocalEvent(OnGunWielded); SubscribeLocalEvent(OnGunUnwielded); SubscribeLocalEvent(OnGunRefreshModifiers); @@ -61,16 +64,21 @@ private void OnMeleeAttempt(EntityUid uid, MeleeRequiresWieldComponent component } } - private void OnShootAttempt(EntityUid uid, GunRequiresWieldComponent component, ref AttemptShootEvent args) + private void OnShootAttempt(EntityUid uid, GunRequiresWieldComponent component, ref ShotAttemptedEvent args) { if (TryComp(uid, out var wieldable) && !wieldable.Wielded) { - args.Cancelled = true; + args.Cancel(); - if (!HasComp(uid) && !HasComp(uid)) + var time = _timing.CurTime; + if (time > component.LastPopup + component.PopupCooldown && + !HasComp(uid) && + !HasComp(uid)) { - args.Message = Loc.GetString("wieldable-component-requires", ("item", uid)); + component.LastPopup = time; + var message = Loc.GetString("wieldable-component-requires", ("item", uid)); + _popupSystem.PopupClient(message, args.Used, args.User); } } } @@ -85,6 +93,15 @@ private void OnGunWielded(EntityUid uid, GunWieldBonusComponent component, ref I _gun.RefreshModifiers(uid); } + private void OnDeselectWieldable(EntityUid uid, WieldableComponent component, HandDeselectedEvent args) + { + if (!component.Wielded || + _handsSystem.EnumerateHands(args.User).Count() > 2) + return; + + TryUnwield(uid, component, args.User); + } + private void OnGunRefreshModifiers(Entity bonus, ref GunRefreshModifiersEvent args) { if (TryComp(bonus, out WieldableComponent? wield) && @@ -155,7 +172,7 @@ public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user return false; } - if (hands.CountFreeHands() < component.FreeHandsRequired) + if (_handsSystem.CountFreeableHands((user, hands)) < component.FreeHandsRequired) { if (!quiet) { @@ -196,9 +213,19 @@ public bool TryWield(EntityUid used, WieldableComponent component, EntityUid use if (component.WieldSound != null) _audioSystem.PlayPredicted(component.WieldSound, used, user); + var virtuals = new List(); for (var i = 0; i < component.FreeHandsRequired; i++) { - _virtualItemSystem.TrySpawnVirtualItemInHand(used, user); + if (_virtualItemSystem.TrySpawnVirtualItemInHand(used, user, out var virtualItem, true)) + { + virtuals.Add(virtualItem.Value); + continue; + } + + foreach (var existingVirtual in virtuals) + QueueDel(existingVirtual); + + return false; } if (TryComp(used, out UseDelayComponent? useDelay) diff --git a/Resources/Locale/en-US/virtual/virtual-item.ftl b/Resources/Locale/en-US/virtual/virtual-item.ftl new file mode 100644 index 0000000000..cb91f24cf7 --- /dev/null +++ b/Resources/Locale/en-US/virtual/virtual-item.ftl @@ -0,0 +1 @@ +virtual-item-dropped-other = You dropped {THE($dropped)}! diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml index cf858e1737..690658d0c7 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_wieldable.yml @@ -4,6 +4,7 @@ abstract: true components: - type: Wieldable + unwieldOnUse: false - type: GunWieldBonus minAngle: -20 maxAngle: -30 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml index dd2a047dab..b448ddea3e 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml @@ -81,7 +81,7 @@ - type: entity name: C-20r sub machine gun - parent: BaseWeaponSubMachineGun + parent: [BaseWeaponSubMachineGun, BaseGunWieldable] id: WeaponSubMachineGunC20r description: Manufactured by Cybersun-Armaments, the C-20r is one of the most popular personal small-arms in the Coalition of Colonies. Uses .35 auto ammo. components: @@ -128,7 +128,7 @@ - type: entity name: Drozd - parent: BaseWeaponSubMachineGun + parent: [BaseWeaponSubMachineGun, BaseGunWieldable] id: WeaponSubMachineGunDrozd description: An excellent fully automatic Heavy SMG. components: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml index 2ca2337bf9..44ee4a08c1 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml @@ -185,7 +185,7 @@ - type: Sprite sprite: DeltaV/Objects/Weapons/Guns/Shotguns/pump.rsi - type: Clothing - sprite: DeltaV/Objects/Weapons/Guns/Shotguns/pump.rsi + sprite: Objects/Weapons/Guns/Shotguns/pump.rsi - type: GunRequiresWield #remove when inaccuracy on spreads is fixed - type: BallisticAmmoProvider capacity: 4 From f7519470ae3acfe45fa53024f0d7ef972d67467c Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 20 Oct 2024 01:46:16 +0000 Subject: [PATCH 57/76] Automatic Changelog Update (#1078) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 51feb49bfe..8fb6a9b10c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7413,3 +7413,10 @@ Entries: id: 6468 time: '2024-10-19T22:10:51.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1093 +- author: VMSolidus + changes: + - type: Fix + message: Ported literally all of Wizden's Wielding Quality Of Life improvements. + id: 6469 + time: '2024-10-20T01:45:50.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1078 From 9ddd9679eccce166e8fe99fab01377c1bf950d58 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 20 Oct 2024 01:40:19 -0400 Subject: [PATCH 58/76] Allow Talking Across Atmosphere (#1089) # Description Fixes #1085 by making it so that being in a MapAtmosphere outright bypasses the check for "No talking in space!" # TODO Don't merge this without testing it, and I haven't tested it yet. # Changelog :cl: - add: Being in an Atmosphere(such as Planets like Glacier & Nukieworld) bypasses the limitation of "NO TALKING IN SPACE" --- Content.Server/Chat/Systems/ChatSystem.cs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index b4641928e4..7ea98c2fe1 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -39,6 +39,7 @@ using Content.Server.Shuttles.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics.Joints; +using Content.Shared.Atmos.Components; namespace Content.Server.Chat.Systems; @@ -506,7 +507,8 @@ private void SendEntityWhisper( if (session.AttachedEntity is not { Valid: true } listener) continue; - if (Transform(session.AttachedEntity.Value).GridUid != Transform(source).GridUid + if (!HasComp(Transform(session.AttachedEntity.Value).MapUid) + && Transform(session.AttachedEntity.Value).GridUid != Transform(source).GridUid && !CheckAttachedGrids(source, session.AttachedEntity.Value)) continue; @@ -712,7 +714,10 @@ private void SendInVoiceRange(ChatChannel channel, string name, string message, var language = languageOverride ?? _language.GetLanguage(source); foreach (var (session, data) in GetRecipients(source, Transform(source).GridUid == null ? 0.3f : VoiceRange)) { - if (session.AttachedEntity != null + if (session.AttachedEntity is not { Valid: true } playerEntity) + continue; + + if (!HasComp(Transform(session.AttachedEntity.Value).MapUid) && Transform(session.AttachedEntity.Value).GridUid != Transform(source).GridUid && !CheckAttachedGrids(source, session.AttachedEntity.Value)) continue; @@ -721,11 +726,8 @@ private void SendInVoiceRange(ChatChannel channel, string name, string message, if (entRange == MessageRangeCheckResult.Disallowed) continue; var entHideChat = entRange == MessageRangeCheckResult.HideChat; - if (session.AttachedEntity is not { Valid: true } playerEntity) - continue; EntityUid listener = session.AttachedEntity.Value; - // If the channel does not support languages, or the entity can understand the message, send the original message, otherwise send the obfuscated version if (channel == ChatChannel.LOOC || channel == ChatChannel.Emotes || _language.CanUnderstand(listener, language.ID)) { From 6d376a717b0b80c80d5c6cd3ec0d2e71107a0d5c Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 20 Oct 2024 05:40:46 +0000 Subject: [PATCH 59/76] Automatic Changelog Update (#1089) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 8fb6a9b10c..b2a85e66be 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7420,3 +7420,12 @@ Entries: id: 6469 time: '2024-10-20T01:45:50.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1078 +- author: VMSolidus + changes: + - type: Add + message: >- + Being in an Atmosphere(such as Planets like Glacier & Nukieworld) + bypasses the limitation of "NO TALKING IN SPACE" + id: 6470 + time: '2024-10-20T05:40:19.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1089 From 86da406041111e6ae460eaa6e16b04b153ba22d4 Mon Sep 17 00:00:00 2001 From: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com> Date: Sun, 20 Oct 2024 18:57:26 +0300 Subject: [PATCH 60/76] Fix a Single Petting Interaction Locale (#1106) # Description Resolves #1104 # Changelog N/A Signed-off-by: Mnemotechnican <69920617+Mnemotechnician@users.noreply.github.com> --- Resources/Locale/en-US/interaction/verbs/noop.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/interaction/verbs/noop.ftl b/Resources/Locale/en-US/interaction/verbs/noop.ftl index 37e8508c8d..953b2808ec 100644 --- a/Resources/Locale/en-US/interaction/verbs/noop.ftl +++ b/Resources/Locale/en-US/interaction/verbs/noop.ftl @@ -13,7 +13,7 @@ interaction-Hug-success-others-popup = {THE($user)} hugs {THE($target)}. interaction-Pet-name = Pet interaction-Pet-description = Pet your co-worker to ease their stress. interaction-Pet-success-self-popup = You pet {THE($target)} on {POSS-ADJ($target)} head. -interaction-Pet-success-target-popup = {THE($user)} pets you on {POSS-ADJ($target)} head. +interaction-Pet-success-target-popup = {THE($user)} pets you on your head. interaction-Pet-success-others-popup = {THE($user)} pets {THE($target)}. interaction-PetAnimal-name = {interaction-Pet-name} From b8ed999834bc01b18539fde538ca4349155f598c Mon Sep 17 00:00:00 2001 From: Remuchi <72476615+Remuchi@users.noreply.github.com> Date: Sun, 20 Oct 2024 23:01:43 +0700 Subject: [PATCH 61/76] [Feature] Generic Radial Selector Menu (#1071) # Description Introduced a generic radial selector menu. Switched shortconstructionsystem from it's own BUI to radial selector. # Changelog no cl no fun (no changelog for players) --------- Signed-off-by: Remuchi --- .../RadialSelector/RadialSelectorMenuBUI.cs | 168 ++++++++++++++++++ .../ShortConstructionSystem.cs | 46 +++++ .../UI/ShortConstructionMenuBUI.cs | 137 -------------- .../ShortConstructionSystem.cs | 24 +++ Content.Shared/RadialSelector/Ui.cs | 52 ++++++ .../ShortConstructionComponent.cs | 36 +--- .../Objects/Materials/Sheets/glass.yml | 48 +++-- .../Objects/Materials/Sheets/metal.yml | 16 +- .../Objects/Materials/Sheets/other.yml | 24 ++- .../Entities/Objects/Materials/parts.yml | 8 +- 10 files changed, 352 insertions(+), 207 deletions(-) create mode 100644 Content.Client/RadialSelector/RadialSelectorMenuBUI.cs create mode 100644 Content.Client/ShortConstruction/ShortConstructionSystem.cs delete mode 100644 Content.Client/ShortConstruction/UI/ShortConstructionMenuBUI.cs create mode 100644 Content.Server/ShortConstruction/ShortConstructionSystem.cs create mode 100644 Content.Shared/RadialSelector/Ui.cs diff --git a/Content.Client/RadialSelector/RadialSelectorMenuBUI.cs b/Content.Client/RadialSelector/RadialSelectorMenuBUI.cs new file mode 100644 index 0000000000..3e72acbfdd --- /dev/null +++ b/Content.Client/RadialSelector/RadialSelectorMenuBUI.cs @@ -0,0 +1,168 @@ +using System.Numerics; +using Content.Client.UserInterface.Controls; +using Content.Shared.Construction.Prototypes; +using Content.Shared.RadialSelector; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.Input; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Shared.Prototypes; + +// ReSharper disable InconsistentNaming + +namespace Content.Client.RadialSelector; + +[UsedImplicitly] +public sealed class RadialSelectorMenuBUI : BoundUserInterface +{ + [Dependency] private readonly IClyde _displayManager = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IPrototypeManager _protoManager = default!; + + private readonly SpriteSystem _spriteSystem; + + private readonly RadialMenu _menu; + + // Used to clearing on state changing + private readonly HashSet _cachedContainers = new(); + + private bool _openCentered; + + public RadialSelectorMenuBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + _spriteSystem = _entManager.System(); + _menu = new RadialMenu + { + HorizontalExpand = true, + VerticalExpand = true, + BackButtonStyleClass = "RadialMenuBackButton", + CloseButtonStyleClass = "RadialMenuCloseButton" + }; + } + + protected override void Open() + { + _menu.OnClose += Close; + + if (_openCentered) + _menu.OpenCentered(); + else + _menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / _displayManager.ScreenSize); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not RadialSelectorState radialSelectorState) + return; + + ClearExistingContainers(); + CreateMenu(radialSelectorState.Entries); + _openCentered = radialSelectorState.OpenCentered; + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + _menu.Dispose(); + } + + private void CreateMenu(List entries, string parentCategory = "") + { + var container = new RadialContainer + { + Name = !string.IsNullOrEmpty(parentCategory) ? parentCategory : "Main", + Radius = 48f + 24f * MathF.Log(entries.Count), + }; + + _menu.AddChild(container); + _cachedContainers.Add(container); + + foreach (var entry in entries) + { + if (entry.Category != null) + { + var button = CreateButton(entry.Category.Name, _spriteSystem.Frame0(entry.Category.Icon)); + button.TargetLayer = entry.Category.Name; + CreateMenu(entry.Category.Entries, entry.Category.Name); + container.AddChild(button); + } + else if (entry.Prototype != null) + { + var name = GetName(entry.Prototype); + var icon = GetIcon(entry); + if (icon is null) + return; + + var button = CreateButton(name, icon); + button.OnButtonUp += _ => + { + var msg = new RadialSelectorSelectedMessage(entry.Prototype); + SendPredictedMessage(msg); + }; + + container.AddChild(button); + } + } + } + + private string GetName(string proto) + { + if (_protoManager.TryIndex(proto, out var prototype)) + return prototype.Name; + if (_protoManager.TryIndex(proto, out ConstructionPrototype? constructionPrototype)) + return constructionPrototype.Name; + return proto; + } + + private Texture? GetIcon(RadialSelectorEntry entry) + { + if (_protoManager.TryIndex(entry.Prototype!, out var prototype)) + return _spriteSystem.Frame0(prototype); + + if (_protoManager.TryIndex(entry.Prototype!, out ConstructionPrototype? constructionProto)) + return _spriteSystem.Frame0(constructionProto.Icon); + + if (entry.Icon is not null) + return _spriteSystem.Frame0(entry.Icon); + + // No icons provided and no icons found in prototypes. There's nothing we can do. + return null; + } + + private RadialMenuTextureButton CreateButton(string name, Texture icon) + { + var itemSize = new Vector2(64f, 64f); + var button = new RadialMenuTextureButton + { + ToolTip = Loc.GetString(name), + StyleClasses = { "RadialMenuButton" }, + SetSize = itemSize + }; + + var iconScale = itemSize / icon.Size; + var texture = new TextureRect + { + VerticalAlignment = Control.VAlignment.Center, + HorizontalAlignment = Control.HAlignment.Center, + Texture = icon, + TextureScale = iconScale + }; + + button.AddChild(texture); + return button; + } + + private void ClearExistingContainers() + { + foreach (var container in _cachedContainers) + _menu.RemoveChild(container); + + _cachedContainers.Clear(); + } +} diff --git a/Content.Client/ShortConstruction/ShortConstructionSystem.cs b/Content.Client/ShortConstruction/ShortConstructionSystem.cs new file mode 100644 index 0000000000..492411977b --- /dev/null +++ b/Content.Client/ShortConstruction/ShortConstructionSystem.cs @@ -0,0 +1,46 @@ +using Content.Client.Construction; +using Content.Shared.Construction.Prototypes; +using Content.Shared.RadialSelector; +using Content.Shared.ShortConstruction; +using Robust.Client.Placement; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Client.ShortConstruction; + +public sealed class ShortConstructionSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IPlacementManager _placement = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; + + [Dependency] private readonly ConstructionSystem _construction = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnItemRecieved); + } + + private void OnItemRecieved(Entity ent, ref RadialSelectorSelectedMessage args) + { + if (!_proto.TryIndex(args.SelectedItem, out ConstructionPrototype? prototype) || + !_gameTiming.IsFirstTimePredicted) + return; + + if (prototype.Type == ConstructionType.Item) + { + _construction.TryStartItemConstruction(prototype.ID); + return; + } + + _placement.BeginPlacing(new PlacementInformation + { + IsTile = false, + PlacementOption = prototype.PlacementMode + }, + new ConstructionPlacementHijack(_construction, prototype)); + } +} diff --git a/Content.Client/ShortConstruction/UI/ShortConstructionMenuBUI.cs b/Content.Client/ShortConstruction/UI/ShortConstructionMenuBUI.cs deleted file mode 100644 index 95da2e0a33..0000000000 --- a/Content.Client/ShortConstruction/UI/ShortConstructionMenuBUI.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System.Numerics; -using Content.Client.Construction; -using Content.Client.UserInterface.Controls; -using Content.Shared.Construction.Prototypes; -using Content.Shared.ShortConstruction; -using JetBrains.Annotations; -using Robust.Client.GameObjects; -using Robust.Client.Graphics; -using Robust.Client.Input; -using Robust.Client.Placement; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; -using Robust.Shared.Enums; -using Robust.Shared.Prototypes; -using Robust.Shared.Utility; - -// ReSharper disable InconsistentNaming - -namespace Content.Client.ShortConstruction.UI; - -[UsedImplicitly] -public sealed class ShortConstructionMenuBUI : BoundUserInterface -{ - [Dependency] private readonly IClyde _displayManager = default!; - [Dependency] private readonly IInputManager _inputManager = default!; - [Dependency] private readonly EntityManager _entManager = default!; - [Dependency] private readonly IPrototypeManager _protoManager = default!; - [Dependency] private readonly IPlacementManager _placementManager = default!; - - private readonly ConstructionSystem _construction; - private readonly SpriteSystem _spriteSystem; - - private RadialMenu? _menu; - public ShortConstructionMenuBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey) - { - _construction = _entManager.System(); - _spriteSystem = _entManager.System(); - } - - protected override void Open() - { - _menu = new RadialMenu - { - HorizontalExpand = true, - VerticalExpand = true, - BackButtonStyleClass = "RadialMenuBackButton", - CloseButtonStyleClass = "RadialMenuCloseButton" - }; - - if (_entManager.TryGetComponent(Owner, out var crafting)) - CreateMenu(crafting.Entries); - - _menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / _displayManager.ScreenSize); - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (disposing) - _menu?.Dispose(); - } - - private void CreateMenu(List entries, string? parentCategory = null) - { - if (_menu == null) - return; - - var container = new RadialContainer - { - Name = parentCategory ?? "Main", - Radius = 48f + 24f * MathF.Log(entries.Count), - }; - - _menu.AddChild(container); - - foreach (var entry in entries) - { - if (entry.Category != null) - { - var button = CreateButton(entry.Category.Name, entry.Category.Icon); - button.TargetLayer = entry.Category.Name; - CreateMenu(entry.Category.Entries, entry.Category.Name); - container.AddChild(button); - } - else if (entry.Prototype != null - && _protoManager.TryIndex(entry.Prototype, out var proto)) - { - var button = CreateButton(proto.Name, proto.Icon); - button.OnButtonUp += _ => ConstructItem(proto); - container.AddChild(button); - } - } - - } - - private RadialMenuTextureButton CreateButton(string name, SpriteSpecifier icon) - { - var button = new RadialMenuTextureButton - { - ToolTip = Loc.GetString(name), - StyleClasses = { "RadialMenuButton" }, - SetSize = new Vector2(64f, 64f), - }; - - var texture = new TextureRect - { - VerticalAlignment = Control.VAlignment.Center, - HorizontalAlignment = Control.HAlignment.Center, - Texture = _spriteSystem.Frame0(icon), - TextureScale = new Vector2(2f, 2f) - }; - - button.AddChild(texture); - return button; - } - - /// - /// Makes an item or places a schematic based on the type of construction recipe. - /// - private void ConstructItem(ConstructionPrototype prototype) - { - if (prototype.Type == ConstructionType.Item) - { - _construction.TryStartItemConstruction(prototype.ID); - return; - } - - _placementManager.BeginPlacing(new PlacementInformation - { - IsTile = false, - PlacementOption = prototype.PlacementMode - }, new ConstructionPlacementHijack(_construction, prototype)); - - // Should only close the menu if we're placing a construction hijack. - // Theres not much point to closing it though. _menu!.Close(); - } -} diff --git a/Content.Server/ShortConstruction/ShortConstructionSystem.cs b/Content.Server/ShortConstruction/ShortConstructionSystem.cs new file mode 100644 index 0000000000..b274b6e9bf --- /dev/null +++ b/Content.Server/ShortConstruction/ShortConstructionSystem.cs @@ -0,0 +1,24 @@ +using Content.Shared.RadialSelector; +using Content.Shared.ShortConstruction; +using Content.Shared.UserInterface; +using Robust.Server.GameObjects; + +namespace Content.Server.ShortConstruction; + +public sealed class ShortConstructionSystem : EntitySystem +{ + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(BeforeUiOpen); + } + + private void BeforeUiOpen(Entity ent, ref BeforeActivatableUIOpenEvent args) + { + var state = new RadialSelectorState(ent.Comp.Entries); + _ui.SetUiState(ent.Owner, RadialSelectorUiKey.Key, state); + } +} diff --git a/Content.Shared/RadialSelector/Ui.cs b/Content.Shared/RadialSelector/Ui.cs new file mode 100644 index 0000000000..656e194dfe --- /dev/null +++ b/Content.Shared/RadialSelector/Ui.cs @@ -0,0 +1,52 @@ +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.RadialSelector; + +[NetSerializable, Serializable] +public enum RadialSelectorUiKey : byte +{ + Key, +} + +[Serializable, NetSerializable] +public sealed class RadialSelectorState(List entries, bool openCentered = false) + : BoundUserInterfaceState +{ + [DataField(required: true)] + public List Entries = entries; + + public bool OpenCentered { get; } = openCentered; +} + +[Serializable, NetSerializable] +public sealed class RadialSelectorSelectedMessage(string selectedItem) : BoundUserInterfaceMessage +{ + public string SelectedItem { get; private set; } = selectedItem; +} + +[DataDefinition, Serializable, NetSerializable] +public sealed partial class RadialSelectorEntry +{ + [DataField] + public string? Prototype { get; set; } + + [DataField] + public SpriteSpecifier? Icon { get; set; } + + [DataField] + public RadialSelectorCategory? Category { get; set; } +} + +[DataDefinition, Serializable, NetSerializable] +public sealed partial class RadialSelectorCategory +{ + [DataField(required: true)] + public string Name { get; set; } = string.Empty; + + [DataField(required: true)] + public SpriteSpecifier Icon { get; set; } = default!; + + [DataField(required: true)] + public List Entries { get; set; } = new(); +} diff --git a/Content.Shared/ShortConstruction/ShortConstructionComponent.cs b/Content.Shared/ShortConstruction/ShortConstructionComponent.cs index dedf8605bd..579110f52f 100644 --- a/Content.Shared/ShortConstruction/ShortConstructionComponent.cs +++ b/Content.Shared/ShortConstruction/ShortConstructionComponent.cs @@ -1,8 +1,5 @@ -using Content.Shared.Construction.Prototypes; +using Content.Shared.RadialSelector; using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Utility; namespace Content.Shared.ShortConstruction; @@ -10,34 +7,5 @@ namespace Content.Shared.ShortConstruction; public sealed partial class ShortConstructionComponent : Component { [DataField(required: true)] - public List Entries = new(); -} - -[DataDefinition] -public sealed partial class ShortConstructionEntry -{ - [DataField] - public ProtoId? Prototype { get; set; } - - [DataField] - public ShortConstructionCategory? Category { get; set; } -} - -[DataDefinition] -public sealed partial class ShortConstructionCategory -{ - [DataField] - public string Name { get; set; } = string.Empty; - - [DataField] - public SpriteSpecifier Icon { get; set; } = default!; - - [DataField] - public List Entries { get; set; } = new(); -} - -[NetSerializable, Serializable] -public enum ShortConstructionUiKey : byte -{ - Key, + public List Entries = new(); } diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml index 95100b340b..e318d7b188 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/glass.yml @@ -93,10 +93,12 @@ Quantity: 10 - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: Window @@ -193,10 +195,12 @@ acts: [ "Destruction" ] - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: SheetRPGlass1 @@ -283,10 +287,12 @@ acts: [ "Destruction" ] - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: SheetRPGlass0 @@ -361,10 +367,12 @@ canReact: false - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: ReinforcedPlasmaWindow @@ -447,10 +455,12 @@ canReact: false - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: SheetRUGlass0 @@ -513,10 +523,12 @@ canReact: false - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: ReinforcedUraniumWindow diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml index 040c6c81b1..f486125ff6 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/metal.yml @@ -71,10 +71,12 @@ Quantity: 1 - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: Girder @@ -283,10 +285,12 @@ canReact: false - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: SecureWindoor diff --git a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml index 781f35417d..ae6770d631 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml @@ -112,10 +112,12 @@ - Sheet - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: SheetPGlass @@ -175,10 +177,12 @@ canReact: false - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: CratePlastic @@ -246,10 +250,12 @@ canReact: false - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: SheetUGlass diff --git a/Resources/Prototypes/Entities/Objects/Materials/parts.yml b/Resources/Prototypes/Entities/Objects/Materials/parts.yml index 95640dbbb5..cfbb3ff386 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/parts.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/parts.yml @@ -70,10 +70,12 @@ Quantity: 0.5 - type: UserInterface interfaces: - enum.ShortConstructionUiKey.Key: - type: ShortConstructionMenuBUI + enum.RadialSelectorUiKey.Key: + type: RadialSelectorMenuBUI - type: ActivatableUI - key: enum.ShortConstructionUiKey.Key + key: enum.RadialSelectorUiKey.Key + inHandsOnly: true + requireActiveHand: false - type: ShortConstruction entries: - prototype: Grille From d1ec52cba97f72e1c6ed40a82b0aeabe15a1b0dd Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 20 Oct 2024 12:03:28 -0400 Subject: [PATCH 62/76] Fix Breath Of Life Locale (#1109) # Description Fixes #1107 A pretty simple single-line fix that makes Breath of Life have its correct text popup. # Changelog :cl: - fix: Breath of Life now correctly displays its esoteric text popup if the right conditions are met. --- Resources/Locale/en-US/psionics/psionic-powers.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Locale/en-US/psionics/psionic-powers.ftl b/Resources/Locale/en-US/psionics/psionic-powers.ftl index 37482a41b6..fec124c7db 100644 --- a/Resources/Locale/en-US/psionics/psionic-powers.ftl +++ b/Resources/Locale/en-US/psionics/psionic-powers.ftl @@ -69,7 +69,7 @@ revivify-power-initialization-feedback = The Secret of Life in its fullness. I feel my entire existence burning out from within, merely by knowing it. Power flows through me as a mighty river, begging to be released with a simple spoken word. revivify-power-metapsionic-feedback = {CAPITALIZE($entity)} bears the Greater Secret of Life. -revivify-word-begin = {CAPITALIZE($entity)} enunciates a word of such divine power, that those who hear it weep from joy. +revivify-start = {CAPITALIZE($entity)} enunciates a word of such divine power, that those who hear it weep from joy. # Telegnosis telegnosis-power-description = Create a telegnostic projection to remotely observe things. From aec15f80da13546039d3c8025c56b5fdd42e773a Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 20 Oct 2024 16:03:54 +0000 Subject: [PATCH 63/76] Automatic Changelog Update (#1109) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index b2a85e66be..30705e57a2 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7429,3 +7429,12 @@ Entries: id: 6470 time: '2024-10-20T05:40:19.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1089 +- author: VMSolidus + changes: + - type: Fix + message: >- + Breath of Life now correctly displays its esoteric text popup if the + right conditions are met. + id: 6471 + time: '2024-10-20T16:03:28.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1109 From 2059f593ee7427d5ad69f54fed4903792130c7aa Mon Sep 17 00:00:00 2001 From: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> Date: Sun, 20 Oct 2024 18:31:20 +0200 Subject: [PATCH 64/76] Shadowkin (#960) # Description Port Shadowkin from Parkstation: https://github.com/Simple-Station/Parkstation/pull/42 --- # TODO - [x] Specie Base - [x] Mar Language / Empathy - [x] Shadowkin Powers - [x] Tiny TODO Application - [x] Specie .yml fix - [x] Sounds Mono Fix - [x] EtherealLight - [x] ethereal goggles - [x] Shadowkin restraints - [x] Bluespace Crystal (Kicks Ethernal Ent back to realspace) ---

Media

![image](https://github.com/user-attachments/assets/47b45499-4c5d-4513-a48d-a291f27c76a6) ![image](https://github.com/user-attachments/assets/40ff7a72-a0a6-498b-9391-731603c8a3ef) ![image](https://github.com/user-attachments/assets/a1fed9d4-5406-4a57-9cb6-419795eead17)

--- # Changelog :cl: - add: Added a new species, Shadowkin! --------- Signed-off-by: FoxxoTrystan <45297731+FoxxoTrystan@users.noreply.github.com> Signed-off-by: VMSolidus Co-authored-by: VMSolidus --- Content.Client/Overlays/ColorTintOverlay.cs | 63 +++ Content.Client/Overlays/EtherealOverlay.cs | 48 +++ Content.Client/Shadowkin/EtherealSystem.cs | 52 +++ Content.Client/Shadowkin/ShadowkinSystem.cs | 114 ++++++ .../Psionics/Abilities/DarkSwapSystem.cs | 58 +++ .../Psionics/Abilities/DispelPowerSystem.cs | 3 + .../Abilities/HealOtherPowerSystem.cs | 2 +- .../Abilities/MetapsionicPowerSystem.cs | 3 + .../Psionics/Abilities/MindSwapPowerSystem.cs | 13 +- .../Abilities/NoosphericZapPowerSystem.cs | 3 + .../PsionicInvisibilityPowerSystem.cs | 8 +- .../PsionicRegenerationPowerSystem.cs | 3 + .../Abilities/PyrokinesisPowerSystem.cs | 3 + .../Abilities/TelegnosisPowerSystem.cs | 3 + .../Abilities/Psionics/AnomalyPowerSystem.cs | 3 +- .../Psionics/PsionicAbilitiesSystem.cs | 4 + .../Components/RespiratorImmuneComponent.cs | 4 + .../Body/Systems/RespiratorSystem.cs | 3 + Content.Server/Chat/EmpathyChatSystem.cs | 83 ++++ .../Shadowkin/EtherealLightComponent.cs | 15 + .../Shadowkin/EtherealStunItemSystem.cs | 44 +++ Content.Server/Shadowkin/EtherealSystem.cs | 216 ++++++++++ .../Shadowkin/ShadowkinCuffSystem.cs | 29 ++ Content.Server/Shadowkin/ShadowkinSystem.cs | 181 +++++++++ .../Shadowkin/ShowEtherealSystem.cs | 88 +++++ .../Actions/Events/AnomalyPowerActionEvent.cs | 4 + .../Actions/Events/DarkSwapActionEvent.cs | 9 + Content.Shared/Alert/AlertType.cs | 1 + Content.Shared/CCVar/CCVars.cs | 6 + Content.Shared/Eye/VisibilityFlags.cs | 7 +- Content.Shared/Humanoid/EyeColor.cs | 4 + .../SharedHumanoidAppearanceSystem.cs | 9 + Content.Shared/Language/LanguagePrototype.cs | 8 + Content.Shared/Psionics/PsionicComponent.cs | 39 +- Content.Shared/Psionics/PsionicEvents.cs | 8 +- .../Psionics/SharedPsionicAbilitiesSystem.cs | 98 +++++ Content.Shared/Shadowkin/EtherealComponent.cs | 36 ++ .../Shadowkin/EtherealStunItemComponent.cs | 11 + .../Shadowkin/ShadowkinComponent.cs | 42 ++ .../Shadowkin/ShadowkinCuffComponent.cs | 4 + .../Shadowkin/SharedEtherealSystem.cs | 141 +++++++ .../Shadowkin/ShowEtherealComponent.cs | 6 + .../Shadowkin/{Powers => }/darkswapoff.ogg | Bin .../Shadowkin/{Powers => }/darkswapon.ogg | Bin .../{Powers => }/futuristic-teleport.ogg | Bin .../Shadowkin/{Powers => }/license.txt | 2 +- .../{Powers/teleport.ogg => shadeskip.ogg} | Bin .../Audio/Voice/Shadowkin/attributions.yml | 4 + Resources/Audio/Voice/Shadowkin/mar.ogg | Bin 0 -> 10161 bytes Resources/Audio/Voice/Shadowkin/wurble.ogg | Bin 0 -> 21569 bytes Resources/Fonts/Lymphatic.ttf | Bin 0 -> 43672 bytes .../en-US/actions/actions/shadowkin.ftl | 2 + Resources/Locale/en-US/alerts/shadowkin.ftl | 2 + Resources/Locale/en-US/chat/emotes.ftl | 2 + .../en-US/chat/managers/chat-manager.ftl | 4 + Resources/Locale/en-US/chat/ui/chat-box.ftl | 2 +- Resources/Locale/en-US/language/languages.ftl | 3 + Resources/Locale/en-US/markings/shadowkin.ftl | 7 + .../en-US/metabolism/metabolizer-types.ftl | 1 + .../Locale/en-US/psionics/psionic-powers.ftl | 10 + .../Locale/en-US/research/technologies.ftl | 1 + Resources/Locale/en-US/species/shadowkin.ftl | 15 + Resources/Locale/en-US/species/species.ftl | 1 + Resources/Locale/en-US/traits/traits.ftl | 3 + Resources/Prototypes/Actions/psionics.yml | 368 ++++++++++-------- Resources/Prototypes/Actions/types.yml | 12 + Resources/Prototypes/Alerts/alerts.yml | 1 + Resources/Prototypes/Alerts/shadowkin.yml | 23 ++ .../Prototypes/Body/Organs/shadowkin.yml | 113 ++++++ Resources/Prototypes/Body/Parts/shadowkin.yml | 155 ++++++++ .../Prototypes/Body/Prototypes/shadowkin.yml | 48 +++ .../Chemistry/metabolizer_types.yml | 4 + Resources/Prototypes/Damage/modifier_sets.yml | 14 + .../Prototypes/Datasets/Names/shadowkin.yml | 70 ++++ .../DeltaV/Entities/Mobs/Player/humanoid.yml | 6 + .../Prototypes/DeltaV/Traits/altvision.yml | 2 + .../DeltaV/Voice/speech_emote_sounds.yml | 4 + .../Entities/Clothing/Back/specific.yml | 22 ++ .../Entities/Clothing/Eyes/glasses.yml | 12 + .../Entities/Clothing/OuterClothing/misc.yml | 17 + .../Entities/Effects/bluespace_flash.yml | 44 ++- .../Mobs/Customization/Markings/reptilian.yml | 2 +- .../Mobs/Customization/Markings/shadowkin.yml | 83 ++++ .../Mobs/Customization/Markings/slime.yml | 16 +- .../Mobs/Customization/Markings/tattoos.yml | 16 +- .../Entities/Mobs/Player/admin_ghost.yml | 1 + .../Entities/Mobs/Player/humanoid.yml | 14 + .../Entities/Mobs/Player/observer.yml | 1 + .../Entities/Mobs/Player/shadowkin.yml | 5 + .../Prototypes/Entities/Mobs/Species/base.yml | 1 - .../Entities/Mobs/Species/shadowkin.yml | 304 +++++++++++++++ .../Entities/Objects/Devices/pda.yml | 1 + .../Entities/Objects/Fun/prizeticket.yml | 3 + .../Prototypes/Entities/Objects/Fun/toys.yml | 12 +- .../Entities/Objects/Materials/bluespace.yml | 4 + .../Entities/Objects/Misc/fluff_lights.yml | 1 + .../Entities/Objects/Misc/kudzu.yml | 139 +++---- .../Entities/Objects/Tools/flashlights.yml | 1 + .../Structures/Lighting/base_lighting.yml | 1 + .../Entities/Structures/Machines/lathe.yml | 3 + .../Structures/Machines/vending_machines.yml | 1 + Resources/Prototypes/Guidebook/species.yml | 8 +- .../Language/Species-Specific/marish.yml | 20 + .../Roles/Jobs/Epistemics/forensicmantis.yml | 6 + .../Roles/Jobs/Wildcards/prisoner.yml | 9 + Resources/Prototypes/Psionics/psionics.yml | 41 +- Resources/Prototypes/Reagents/toxins.yml | 6 + .../Graphs/improvised/makeshifthandcuffs.yml | 1 - .../Recipes/Lathes/prizecounter.yml | 8 + .../Prototypes/Recipes/Lathes/security.yml | 14 +- Resources/Prototypes/Roles/Antags/Thief.yml | 2 +- Resources/Prototypes/Roles/Antags/traitor.yml | 2 +- .../Roles/Jobs/Civilian/chaplain.yml | 6 + .../Roles/Jobs/Civilian/librarian.yml | 6 + .../Jobs/Medical/chief_medical_officer.yml | 9 + .../Roles/Jobs/Science/research_director.yml | 6 + .../Roles/Jobs/Security/security_officer.yml | 1 - Resources/Prototypes/Shaders/shaders.yml | 14 +- .../Prototypes/SoundCollections/emotes.yml | 10 + Resources/Prototypes/Species/shadowkin.yml | 179 +++++++++ .../Prototypes/Species/species_weights.yml | 5 +- Resources/Prototypes/Traits/disabilities.yml | 1 + Resources/Prototypes/Traits/mental.yml | 32 ++ Resources/Prototypes/Traits/physical.yml | 2 + Resources/Prototypes/Traits/skills.yml | 52 ++- Resources/Prototypes/Traits/species.yml | 14 + .../Prototypes/Voice/speech_emote_sounds.yml | 84 ++++ Resources/Prototypes/Voice/speech_emotes.yml | 16 + Resources/Prototypes/fonts.yml | 4 + .../ServerInfo/Guidebook/Mobs/Shadowkin.xml | 36 ++ .../Guidebook/Mobs/shadowkin.Lore.txt | 178 +++++++++ .../equipped-BACKPACK.png | Bin 0 -> 3515 bytes .../Back/etherealteleporter.rsi/icon.png | Bin 0 -> 765 bytes .../Back/etherealteleporter.rsi/meta.json | 56 +++ .../etherealgoogles.rsi/equipped-EYES.png | Bin 0 -> 314 bytes .../Eyes/Glasses/etherealgoogles.rsi/icon.png | Bin 0 -> 415 bytes .../etherealgoogles.rsi/inhand-left.png | Bin 0 -> 386 bytes .../etherealgoogles.rsi/inhand-right.png | Bin 0 -> 397 bytes .../Glasses/etherealgoogles.rsi/meta.json | 26 ++ .../equipped-OUTERCLOTHING.png | Bin 0 -> 1142 bytes .../Misc/shadowkinrestraints.rsi/icon.png | Bin 0 -> 3072 bytes .../shadowkinrestraints.rsi/inhand-left.png | Bin 0 -> 234 bytes .../shadowkinrestraints.rsi/inhand-right.png | Bin 0 -> 242 bytes .../Misc/shadowkinrestraints.rsi/meta.json | 26 ++ .../Actions/shadowkin_icons.rsi/darkswap.png | Bin 0 -> 5595 bytes .../Actions/shadowkin_icons.rsi/meta.json | 23 ++ .../Actions/shadowkin_icons.rsi/rest.png | Bin 0 -> 5615 bytes .../Actions/shadowkin_icons.rsi/shadeskip.png | Bin 0 -> 4114 bytes .../Alerts/shadowkin_power.rsi/meta.json | 43 ++ .../Alerts/shadowkin_power.rsi/power0.png | Bin 0 -> 853 bytes .../Alerts/shadowkin_power.rsi/power1.png | Bin 0 -> 938 bytes .../Alerts/shadowkin_power.rsi/power2.png | Bin 0 -> 1000 bytes .../Alerts/shadowkin_power.rsi/power3.png | Bin 0 -> 1020 bytes .../Alerts/shadowkin_power.rsi/power4.png | Bin 0 -> 1064 bytes .../Alerts/shadowkin_power.rsi/power5.png | Bin 0 -> 1064 bytes .../Alerts/shadowkin_power.rsi/power6.png | Bin 0 -> 1063 bytes .../Alerts/shadowkin_power.rsi/power7.png | Bin 0 -> 1087 bytes .../Shadowkin/ears.rsi/meta.json | 19 + .../Shadowkin/ears.rsi/shadowkin.png | Bin 0 -> 9242 bytes .../Shadowkin/ears.rsi/shadowkin_stripes.png | Bin 0 -> 789 bytes .../Shadowkin/tails32x32.rsi/meta.json | 19 + .../tails32x32.rsi/shadowkin_medium.png | Bin 0 -> 807 bytes .../tails32x32.rsi/shadowkin_shorter.png | Bin 0 -> 630 bytes .../Shadowkin/tails64x32.rsi/meta.json | 23 ++ .../Shadowkin/tails64x32.rsi/shadowkin.png | Bin 0 -> 4674 bytes .../tails64x32.rsi/shadowkin_big.png | Bin 0 -> 5277 bytes .../tails64x32.rsi/shadowkin_big_fluff.png | Bin 0 -> 1785 bytes .../Species/Shadowkin/organs.rsi/appendix.png | Bin 0 -> 1609 bytes .../Species/Shadowkin/organs.rsi/brain.png | Bin 0 -> 823 bytes .../Species/Shadowkin/organs.rsi/core.png | Bin 0 -> 927 bytes .../Species/Shadowkin/organs.rsi/ears.png | Bin 0 -> 1677 bytes .../Species/Shadowkin/organs.rsi/eyes.png | Bin 0 -> 1014 bytes .../Species/Shadowkin/organs.rsi/heart.png | Bin 0 -> 670 bytes .../Species/Shadowkin/organs.rsi/kidneys.png | Bin 0 -> 759 bytes .../Species/Shadowkin/organs.rsi/liver.png | Bin 0 -> 710 bytes .../Species/Shadowkin/organs.rsi/lungs.png | Bin 0 -> 735 bytes .../Species/Shadowkin/organs.rsi/meta.json | 44 +++ .../Species/Shadowkin/organs.rsi/stomach.png | Bin 0 -> 770 bytes .../Species/Shadowkin/organs.rsi/tongue.png | Bin 0 -> 2303 bytes .../Mobs/Species/Shadowkin/parts.rsi/eyes.png | Bin 0 -> 1643 bytes .../Shadowkin/parts.rsi/full-nomarkings.png | Bin 0 -> 3355 bytes .../Mobs/Species/Shadowkin/parts.rsi/full.png | Bin 0 -> 4851 bytes .../Species/Shadowkin/parts.rsi/head_f.png | Bin 0 -> 849 bytes .../Species/Shadowkin/parts.rsi/head_m.png | Bin 0 -> 849 bytes .../Species/Shadowkin/parts.rsi/l_arm.png | Bin 0 -> 614 bytes .../Species/Shadowkin/parts.rsi/l_foot.png | Bin 0 -> 1867 bytes .../Species/Shadowkin/parts.rsi/l_hand.png | Bin 0 -> 450 bytes .../Species/Shadowkin/parts.rsi/l_leg.png | Bin 0 -> 3086 bytes .../Species/Shadowkin/parts.rsi/meta.json | 71 ++++ .../Species/Shadowkin/parts.rsi/r_arm.png | Bin 0 -> 605 bytes .../Species/Shadowkin/parts.rsi/r_foot.png | Bin 0 -> 1876 bytes .../Species/Shadowkin/parts.rsi/r_hand.png | Bin 0 -> 460 bytes .../Species/Shadowkin/parts.rsi/r_leg.png | Bin 0 -> 3090 bytes .../Species/Shadowkin/parts.rsi/torso_f.png | Bin 0 -> 1442 bytes .../Species/Shadowkin/parts.rsi/torso_m.png | Bin 0 -> 1432 bytes .../Textures/Objects/Fun/toys.rsi/meta.json | 3 + .../Objects/Fun/toys.rsi/shadowkin.png | Bin 0 -> 2063 bytes Resources/Textures/Shaders/color_tint.swsl | 56 +++ Resources/Textures/Shaders/ethereal.swsl | 75 ++++ 199 files changed, 4020 insertions(+), 298 deletions(-) create mode 100644 Content.Client/Overlays/ColorTintOverlay.cs create mode 100644 Content.Client/Overlays/EtherealOverlay.cs create mode 100644 Content.Client/Shadowkin/EtherealSystem.cs create mode 100644 Content.Client/Shadowkin/ShadowkinSystem.cs create mode 100644 Content.Server/Abilities/Psionics/Abilities/DarkSwapSystem.cs create mode 100644 Content.Server/Body/Components/RespiratorImmuneComponent.cs create mode 100644 Content.Server/Chat/EmpathyChatSystem.cs create mode 100644 Content.Server/Shadowkin/EtherealLightComponent.cs create mode 100644 Content.Server/Shadowkin/EtherealStunItemSystem.cs create mode 100644 Content.Server/Shadowkin/EtherealSystem.cs create mode 100644 Content.Server/Shadowkin/ShadowkinCuffSystem.cs create mode 100644 Content.Server/Shadowkin/ShadowkinSystem.cs create mode 100644 Content.Server/Shadowkin/ShowEtherealSystem.cs create mode 100644 Content.Shared/Actions/Events/DarkSwapActionEvent.cs create mode 100644 Content.Shared/Humanoid/EyeColor.cs create mode 100644 Content.Shared/Shadowkin/EtherealComponent.cs create mode 100644 Content.Shared/Shadowkin/EtherealStunItemComponent.cs create mode 100644 Content.Shared/Shadowkin/ShadowkinComponent.cs create mode 100644 Content.Shared/Shadowkin/ShadowkinCuffComponent.cs create mode 100644 Content.Shared/Shadowkin/SharedEtherealSystem.cs create mode 100644 Content.Shared/Shadowkin/ShowEtherealComponent.cs rename Resources/Audio/Effects/Shadowkin/{Powers => }/darkswapoff.ogg (100%) rename Resources/Audio/Effects/Shadowkin/{Powers => }/darkswapon.ogg (100%) rename Resources/Audio/Effects/Shadowkin/{Powers => }/futuristic-teleport.ogg (100%) rename Resources/Audio/Effects/Shadowkin/{Powers => }/license.txt (75%) rename Resources/Audio/Effects/Shadowkin/{Powers/teleport.ogg => shadeskip.ogg} (100%) create mode 100644 Resources/Audio/Voice/Shadowkin/attributions.yml create mode 100644 Resources/Audio/Voice/Shadowkin/mar.ogg create mode 100644 Resources/Audio/Voice/Shadowkin/wurble.ogg create mode 100644 Resources/Fonts/Lymphatic.ttf create mode 100644 Resources/Locale/en-US/actions/actions/shadowkin.ftl create mode 100644 Resources/Locale/en-US/alerts/shadowkin.ftl create mode 100644 Resources/Locale/en-US/markings/shadowkin.ftl create mode 100644 Resources/Locale/en-US/species/shadowkin.ftl create mode 100644 Resources/Prototypes/Alerts/shadowkin.yml create mode 100644 Resources/Prototypes/Body/Organs/shadowkin.yml create mode 100644 Resources/Prototypes/Body/Parts/shadowkin.yml create mode 100644 Resources/Prototypes/Body/Prototypes/shadowkin.yml create mode 100644 Resources/Prototypes/Datasets/Names/shadowkin.yml create mode 100644 Resources/Prototypes/Entities/Mobs/Customization/Markings/shadowkin.yml create mode 100644 Resources/Prototypes/Entities/Mobs/Player/shadowkin.yml create mode 100644 Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml create mode 100644 Resources/Prototypes/Language/Species-Specific/marish.yml create mode 100644 Resources/Prototypes/Species/shadowkin.yml create mode 100644 Resources/ServerInfo/Guidebook/Mobs/Shadowkin.xml create mode 100644 Resources/ServerInfo/Guidebook/Mobs/shadowkin.Lore.txt create mode 100644 Resources/Textures/Clothing/Back/etherealteleporter.rsi/equipped-BACKPACK.png create mode 100644 Resources/Textures/Clothing/Back/etherealteleporter.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Back/etherealteleporter.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/meta.json create mode 100644 Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/equipped-OUTERCLOTHING.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/icon.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/meta.json create mode 100644 Resources/Textures/Interface/Actions/shadowkin_icons.rsi/darkswap.png create mode 100644 Resources/Textures/Interface/Actions/shadowkin_icons.rsi/meta.json create mode 100644 Resources/Textures/Interface/Actions/shadowkin_icons.rsi/rest.png create mode 100644 Resources/Textures/Interface/Actions/shadowkin_icons.rsi/shadeskip.png create mode 100644 Resources/Textures/Interface/Alerts/shadowkin_power.rsi/meta.json create mode 100644 Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power0.png create mode 100644 Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power1.png create mode 100644 Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power2.png create mode 100644 Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power3.png create mode 100644 Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power4.png create mode 100644 Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power5.png create mode 100644 Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power6.png create mode 100644 Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power7.png create mode 100644 Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/shadowkin.png create mode 100644 Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/shadowkin_stripes.png create mode 100644 Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_medium.png create mode 100644 Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_shorter.png create mode 100644 Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin.png create mode 100644 Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin_big.png create mode 100644 Resources/Textures/Mobs/Customization/Shadowkin/tails64x32.rsi/shadowkin_big_fluff.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/appendix.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/brain.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/core.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/ears.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/eyes.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/heart.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/kidneys.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/liver.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/lungs.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/stomach.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/tongue.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/eyes.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/full-nomarkings.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/full.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/head_f.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/head_m.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_arm.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_foot.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_hand.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_leg.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_arm.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_foot.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_hand.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_leg.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/torso_f.png create mode 100644 Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/torso_m.png create mode 100644 Resources/Textures/Objects/Fun/toys.rsi/shadowkin.png create mode 100644 Resources/Textures/Shaders/color_tint.swsl create mode 100644 Resources/Textures/Shaders/ethereal.swsl diff --git a/Content.Client/Overlays/ColorTintOverlay.cs b/Content.Client/Overlays/ColorTintOverlay.cs new file mode 100644 index 0000000000..f40a8d7342 --- /dev/null +++ b/Content.Client/Overlays/ColorTintOverlay.cs @@ -0,0 +1,63 @@ +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; +using Content.Shared.Shadowkin; + +namespace Content.Client.Overlays; + +/// +/// A simple overlay that applies a colored tint to the screen. +/// +public sealed class ColorTintOverlay : Overlay +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] IEntityManager _entityManager = default!; + + public override bool RequestScreenTexture => true; + public override OverlaySpace Space => OverlaySpace.WorldSpace; + private readonly ShaderInstance _shader; + + /// + /// The color to tint the screen to as RGB on a scale of 0-1. + /// + public Vector3? TintColor = null; + /// + /// The percent to tint the screen by on a scale of 0-1. + /// + public float? TintAmount = null; + + public ColorTintOverlay() + { + IoCManager.InjectDependencies(this); + _shader = _prototype.Index("ColorTint").InstanceUnique(); + } + + protected override bool BeforeDraw(in OverlayDrawArgs args) + { + if (_player.LocalEntity is not { Valid: true } player + || !_entityManager.HasComponent(player)) + return false; + + return base.BeforeDraw(in args); + } + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenTexture is null) + return; + + _shader.SetParameter("SCREEN_TEXTURE", ScreenTexture); + if (TintColor != null) + _shader.SetParameter("tint_color", (Vector3) TintColor); + if (TintAmount != null) + _shader.SetParameter("tint_amount", (float) TintAmount); + + var worldHandle = args.WorldHandle; + var viewport = args.WorldBounds; + worldHandle.SetTransform(Matrix3.Identity); + worldHandle.UseShader(_shader); + worldHandle.DrawRect(viewport, Color.White); + worldHandle.UseShader(null); + } +} \ No newline at end of file diff --git a/Content.Client/Overlays/EtherealOverlay.cs b/Content.Client/Overlays/EtherealOverlay.cs new file mode 100644 index 0000000000..3d771de8ce --- /dev/null +++ b/Content.Client/Overlays/EtherealOverlay.cs @@ -0,0 +1,48 @@ +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; +using Content.Shared.Shadowkin; + +namespace Content.Client.Overlays; + +public sealed class EtherealOverlay : Overlay +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] IEntityManager _entityManager = default!; + + public override bool RequestScreenTexture => true; + public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV; + private readonly ShaderInstance _shader; + + public EtherealOverlay() + { + IoCManager.InjectDependencies(this); + _shader = _prototype.Index("Ethereal").InstanceUnique(); + } + + protected override bool BeforeDraw(in OverlayDrawArgs args) + { + if (_player.LocalEntity is not { Valid: true } player + || !_entityManager.HasComponent(player)) + return false; + + return base.BeforeDraw(in args); + } + + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenTexture is null) + return; + + _shader.SetParameter("SCREEN_TEXTURE", ScreenTexture); + + var worldHandle = args.WorldHandle; + var viewport = args.WorldBounds; + worldHandle.SetTransform(Matrix3.Identity); + worldHandle.UseShader(_shader); + worldHandle.DrawRect(viewport, Color.White); + worldHandle.UseShader(null); + } +} \ No newline at end of file diff --git a/Content.Client/Shadowkin/EtherealSystem.cs b/Content.Client/Shadowkin/EtherealSystem.cs new file mode 100644 index 0000000000..cb289a87f1 --- /dev/null +++ b/Content.Client/Shadowkin/EtherealSystem.cs @@ -0,0 +1,52 @@ +using Content.Shared.Shadowkin; +using Robust.Client.Graphics; +using Robust.Shared.Player; +using Content.Client.Overlays; + +namespace Content.Client.Shadowkin; + +public sealed partial class EtherealSystem : EntitySystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly ISharedPlayerManager _playerMan = default!; + + private EtherealOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(Onhutdown); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + + _overlay = new(); + } + + private void OnInit(EntityUid uid, EtherealComponent component, ComponentInit args) + { + if (uid != _playerMan.LocalEntity) + return; + + _overlayMan.AddOverlay(_overlay); + } + + private void Onhutdown(EntityUid uid, EtherealComponent component, ComponentShutdown args) + { + if (uid != _playerMan.LocalEntity) + return; + + _overlayMan.RemoveOverlay(_overlay); + } + + private void OnPlayerAttached(EntityUid uid, EtherealComponent component, LocalPlayerAttachedEvent args) + { + _overlayMan.AddOverlay(_overlay); + } + + private void OnPlayerDetached(EntityUid uid, EtherealComponent component, LocalPlayerDetachedEvent args) + { + _overlayMan.RemoveOverlay(_overlay); + } +} diff --git a/Content.Client/Shadowkin/ShadowkinSystem.cs b/Content.Client/Shadowkin/ShadowkinSystem.cs new file mode 100644 index 0000000000..d8e1b69fc7 --- /dev/null +++ b/Content.Client/Shadowkin/ShadowkinSystem.cs @@ -0,0 +1,114 @@ +using Content.Shared.Shadowkin; +using Content.Shared.CCVar; +using Robust.Client.Graphics; +using Robust.Shared.Configuration; +using Robust.Shared.Player; +using Content.Shared.Humanoid; +using Content.Shared.Abilities.Psionics; +using Content.Client.Overlays; + +namespace Content.Client.Shadowkin; + +public sealed partial class ShadowkinSystem : EntitySystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly ISharedPlayerManager _playerMan = default!; + + private ColorTintOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(Onhutdown); + SubscribeLocalEvent(OnPlayerAttached); + SubscribeLocalEvent(OnPlayerDetached); + + Subs.CVar(_cfg, CCVars.NoVisionFilters, OnNoVisionFiltersChanged); + + _overlay = new(); + } + + private void OnInit(EntityUid uid, ShadowkinComponent component, ComponentInit args) + { + if (uid != _playerMan.LocalEntity + || _cfg.GetCVar(CCVars.NoVisionFilters)) + return; + + _overlayMan.AddOverlay(_overlay); + } + + private void Onhutdown(EntityUid uid, ShadowkinComponent component, ComponentShutdown args) + { + if (uid != _playerMan.LocalEntity) + return; + + _overlayMan.RemoveOverlay(_overlay); + } + + private void OnPlayerAttached(EntityUid uid, ShadowkinComponent component, LocalPlayerAttachedEvent args) + { + if (_cfg.GetCVar(CCVars.NoVisionFilters)) + return; + + _overlayMan.AddOverlay(_overlay); + } + + private void OnPlayerDetached(EntityUid uid, ShadowkinComponent component, LocalPlayerDetachedEvent args) + { + _overlayMan.RemoveOverlay(_overlay); + } + + private void OnNoVisionFiltersChanged(bool enabled) + { + if (enabled) + _overlayMan.RemoveOverlay(_overlay); + else + _overlayMan.AddOverlay(_overlay); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + if (_cfg.GetCVar(CCVars.NoVisionFilters)) + return; + + var uid = _playerMan.LocalEntity; + if (uid == null + || !TryComp(uid, out var comp) + || !TryComp(uid, out var humanoid)) + return; + + // 1/3 = 0.333... + // intensity = min + (power / max) + // intensity = intensity / 0.333 + // intensity = clamp intensity min, max + + var tintIntensity = 0.65f; + if (TryComp(uid, out var magic)) + { + var min = 0.45f; + var max = 0.75f; + tintIntensity = Math.Clamp(min + (magic.Mana / magic.MaxMana) * 0.333f, min, max); + } + + UpdateShader(new Vector3(humanoid.EyeColor.R, humanoid.EyeColor.G, humanoid.EyeColor.B), tintIntensity); + } + + private void UpdateShader(Vector3? color, float? intensity) + { + while (_overlayMan.HasOverlay()) + _overlayMan.RemoveOverlay(_overlay); + + if (color != null) + _overlay.TintColor = color; + if (intensity != null) + _overlay.TintAmount = intensity; + + if (!_cfg.GetCVar(CCVars.NoVisionFilters)) + _overlayMan.AddOverlay(_overlay); + } +} diff --git a/Content.Server/Abilities/Psionics/Abilities/DarkSwapSystem.cs b/Content.Server/Abilities/Psionics/Abilities/DarkSwapSystem.cs new file mode 100644 index 0000000000..fd394e0a22 --- /dev/null +++ b/Content.Server/Abilities/Psionics/Abilities/DarkSwapSystem.cs @@ -0,0 +1,58 @@ +using Content.Shared.Abilities.Psionics; +using Content.Shared.Actions.Events; +using Content.Shared.Shadowkin; +using Content.Shared.Physics; +using Content.Shared.Popups; +using Content.Shared.Maps; +using Robust.Server.GameObjects; + +namespace Content.Server.Abilities.Psionics +{ + public sealed class DarkSwapSystem : EntitySystem + { + [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly PhysicsSystem _physics = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnPowerUsed); + } + + private void OnPowerUsed(DarkSwapActionEvent args) + { + if (TryComp(args.Performer, out var ethereal)) + { + var tileref = Transform(args.Performer).Coordinates.GetTileRef(); + if (tileref != null + && _physics.GetEntitiesIntersectingBody(args.Performer, (int) CollisionGroup.Impassable).Count > 0) + { + _popup.PopupEntity(Loc.GetString("revenant-in-solid"), args.Performer, args.Performer); + return; + } + + if (_psionics.OnAttemptPowerUse(args.Performer, "DarkSwap", args.ManaCost / 2, args.CheckInsulation)) + { + RemComp(args.Performer, ethereal); + args.Handled = true; + } + } + else if (_psionics.OnAttemptPowerUse(args.Performer, "DarkSwap", args.ManaCost, args.CheckInsulation)) + { + var newethereal = EnsureComp(args.Performer); + newethereal.Darken = true; + + SpawnAtPosition("ShadowkinShadow", Transform(args.Performer).Coordinates); + SpawnAtPosition("EffectFlashShadowkinDarkSwapOn", Transform(args.Performer).Coordinates); + + args.Handled = true; + } + + if (args.Handled) + _psionics.LogPowerUsed(args.Performer, "DarkSwap", 0, 0); + } + } +} + + diff --git a/Content.Server/Abilities/Psionics/Abilities/DispelPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/DispelPowerSystem.cs index cdfda7c801..ecffc86c76 100644 --- a/Content.Server/Abilities/Psionics/Abilities/DispelPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/DispelPowerSystem.cs @@ -38,6 +38,9 @@ public override void Initialize() private void OnPowerUsed(DispelPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "dispel")) + return; + var ev = new DispelledEvent(); RaiseLocalEvent(args.Target, ev, false); diff --git a/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs index 138a341ce8..6a2e90dd88 100644 --- a/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/HealOtherPowerSystem.cs @@ -44,7 +44,7 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, PsionicComponent component, PsionicHealOtherPowerActionEvent args) { - if (component.DoAfter is not null) + if (!_psionics.OnAttemptPowerUse(args.Performer, args.PowerName)) return; args.ModifiedAmplification = _psionics.ModifiedAmplification(uid, component); diff --git a/Content.Server/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs index 58d7d804da..24ef344f63 100644 --- a/Content.Server/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs @@ -19,6 +19,9 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, MetapsionicPowerComponent component, MetapsionicPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "metapsionic pulse")) + return; + foreach (var entity in _lookup.GetEntitiesInRange(uid, component.Range)) { if (HasComp(entity) && entity != uid && !HasComp(entity) && diff --git a/Content.Server/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs index 2d106706c6..869bf269ab 100644 --- a/Content.Server/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs @@ -38,7 +38,8 @@ public override void Initialize() private void OnPowerUsed(MindSwapPowerActionEvent args) { - if (!(TryComp(args.Target, out var damageable) && damageable.DamageContainerID == "Biological")) + if (!_psionics.OnAttemptPowerUse(args.Performer, "mind swap") + || !(TryComp(args.Target, out var damageable) && damageable.DamageContainerID == "Biological")) return; Swap(args.Performer, args.Target); @@ -116,8 +117,8 @@ private void OnGhostAttempt(GhostAttemptHandleEvent args) private void OnSwapInit(EntityUid uid, MindSwappedComponent component, ComponentInit args) { - _actions.AddAction(uid, ref component.MindSwapReturnActionEntity, component.MindSwapReturnActionId ); - _actions.TryGetActionData( component.MindSwapReturnActionEntity, out var actionData ); + _actions.AddAction(uid, ref component.MindSwapReturnActionEntity, component.MindSwapReturnActionId); + _actions.TryGetActionData(component.MindSwapReturnActionEntity, out var actionData); if (actionData is { UseDelay: not null }) _actions.StartUseDelay(component.MindSwapReturnActionEntity); } @@ -132,11 +133,13 @@ public void Swap(EntityUid performer, EntityUid target, bool end = false) MindComponent? targetMind = null; // This is here to prevent missing MindContainerComponent Resolve errors. - if(!_mindSystem.TryGetMind(performer, out var performerMindId, out performerMind)){ + if (!_mindSystem.TryGetMind(performer, out var performerMindId, out performerMind)) + { performerMind = null; }; - if(!_mindSystem.TryGetMind(target, out var targetMindId, out targetMind)){ + if (!_mindSystem.TryGetMind(target, out var targetMindId, out targetMind)) + { targetMind = null; }; //This is a terrible way to 'unattach' minds. I wanted to use UnVisit but in TransferTo's code they say diff --git a/Content.Server/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs index c2f5920639..22c4f2e500 100644 --- a/Content.Server/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs @@ -22,6 +22,9 @@ public override void Initialize() private void OnPowerUsed(NoosphericZapPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "noospheric zap")) + return; + _beam.TryCreateBeam(args.Performer, args.Target, "LightningNoospheric"); _stunSystem.TryParalyze(args.Target, TimeSpan.FromSeconds(5), false); diff --git a/Content.Server/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs index 1965862954..c6a01912a0 100644 --- a/Content.Server/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs @@ -31,13 +31,14 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, PsionicInvisibilityPowerComponent component, PsionicInvisibilityPowerActionEvent args) { - if (HasComp(uid)) + if (!_psionics.OnAttemptPowerUse(args.Performer, "psionic invisibility") + || HasComp(uid)) return; ToggleInvisibility(args.Performer); var action = Spawn(PsionicInvisibilityUsedComponent.PsionicInvisibilityUsedActionPrototype); _actions.AddAction(uid, action, action); - _actions.TryGetActionData( action, out var actionData ); + _actions.TryGetActionData(action, out var actionData); if (actionData is { UseDelay: not null }) _actions.StartUseDelay(action); @@ -93,7 +94,8 @@ public void ToggleInvisibility(EntityUid uid) if (!HasComp(uid)) { EnsureComp(uid); - } else + } + else { RemComp(uid); } diff --git a/Content.Server/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs index 17e9249e65..d7ad2d49ab 100644 --- a/Content.Server/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs @@ -40,6 +40,9 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, PsionicRegenerationPowerComponent component, PsionicRegenerationPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "psionic regeneration")) + return; + var ev = new PsionicRegenerationDoAfterEvent(_gameTiming.CurTime); var doAfterArgs = new DoAfterArgs(EntityManager, uid, component.UseDelay, ev, uid); diff --git a/Content.Server/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs index 3740822667..4a75083602 100644 --- a/Content.Server/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs @@ -19,6 +19,9 @@ public override void Initialize() } private void OnPowerUsed(PyrokinesisPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "pyrokinesis")) + return; + if (!TryComp(args.Target, out var flammableComponent)) return; diff --git a/Content.Server/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs index 7a3f663a43..abbbdfacc5 100644 --- a/Content.Server/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs @@ -18,6 +18,9 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, TelegnosisPowerComponent component, TelegnosisPowerActionEvent args) { + if (!_psionics.OnAttemptPowerUse(args.Performer, "telegnosis")) + return; + var projection = Spawn(component.Prototype, Transform(uid).Coordinates); Transform(projection).AttachToGridOrMap(); _mindSwap.Swap(uid, projection); diff --git a/Content.Server/Abilities/Psionics/AnomalyPowerSystem.cs b/Content.Server/Abilities/Psionics/AnomalyPowerSystem.cs index 73d5aae210..ff9910c400 100644 --- a/Content.Server/Abilities/Psionics/AnomalyPowerSystem.cs +++ b/Content.Server/Abilities/Psionics/AnomalyPowerSystem.cs @@ -49,8 +49,7 @@ public override void Initialize() private void OnPowerUsed(EntityUid uid, PsionicComponent component, AnomalyPowerActionEvent args) { - if (HasComp(uid) - || HasComp(uid)) + if (!_psionics.OnAttemptPowerUse(args.Performer, args.Settings.PowerName, args.Settings.ManaCost, args.Settings.CheckInsulation)) return; var overcharged = args.Settings.DoSupercritical ? _glimmerSystem.Glimmer * component.CurrentAmplification diff --git a/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs b/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs index 536235b6d6..841d923a24 100644 --- a/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs +++ b/Content.Server/Abilities/Psionics/PsionicAbilitiesSystem.cs @@ -217,6 +217,10 @@ public void RemoveAllPsionicPowers(EntityUid uid, bool mindbreak = false) RemComp(uid); RemComp(uid); + + var ev = new OnMindbreakEvent(); + RaiseLocalEvent(uid, ref ev); + return; } RefreshPsionicModifiers(uid, psionic); diff --git a/Content.Server/Body/Components/RespiratorImmuneComponent.cs b/Content.Server/Body/Components/RespiratorImmuneComponent.cs new file mode 100644 index 0000000000..afc261eff2 --- /dev/null +++ b/Content.Server/Body/Components/RespiratorImmuneComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Server.Body.Components; + +[RegisterComponent] +public sealed partial class RespiratorImmuneComponent : Component { } \ No newline at end of file diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index 389e5fbab7..f6a17d32b6 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -66,6 +66,9 @@ public override void Update(float frameTime) if (_mobState.IsDead(uid)) continue; + if (HasComp(uid)) + continue; + UpdateSaturation(uid, -(float) respirator.UpdateInterval.TotalSeconds, respirator); if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit. diff --git a/Content.Server/Chat/EmpathyChatSystem.cs b/Content.Server/Chat/EmpathyChatSystem.cs new file mode 100644 index 0000000000..b46bbdc34e --- /dev/null +++ b/Content.Server/Chat/EmpathyChatSystem.cs @@ -0,0 +1,83 @@ +using System.Linq; +using Robust.Shared.Utility; +using Content.Server.Chat.Managers; +using Content.Server.Language; +using Content.Server.Chat.Systems; +using Content.Server.Administration.Managers; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Content.Shared.Chat; +using Content.Shared.Language; +using Robust.Shared.Prototypes; +using Content.Shared.Language.Components; + +namespace Content.Server.Chat; + +public sealed partial class EmpathyChatSystem : EntitySystem +{ + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly LanguageSystem _language = default!; + [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnSpeak); + } + + private void OnSpeak(EntityUid uid, LanguageSpeakerComponent component, EntitySpokeEvent args) + { + if (args.Source != uid + || !args.Language.SpeechOverride.EmpathySpeech + || args.IsWhisper) + return; + + SendEmpathyChat(args.Source, args.Message, false); + } + + /// + /// Send a Message in the Shadowkin Empathy Chat. + /// + /// The entity making the message + /// The contents of the message + /// Set the ChatTransmitRange + public void SendEmpathyChat(EntityUid source, string message, bool hideChat) + { + var clients = GetEmpathChatClients(); + string wrappedMessage; + + wrappedMessage = Loc.GetString("chat-manager-send-empathy-chat-wrap-message", + ("source", source), + ("message", FormattedMessage.EscapeText(message))); + + _chatManager.ChatMessageToMany(ChatChannel.Telepathic, message, wrappedMessage, source, hideChat, true, clients.ToList(), Color.FromHex("#be3cc5")); + } + + private IEnumerable GetEmpathChatClients() + { + return Filter.Empty() + .AddWhereAttachedEntity(entity => + CanHearEmpathy(entity)) + .Recipients + .Union(_adminManager.ActiveAdmins) + .Select(p => p.Channel); + } + + /// + /// Check if an entity can hear Empathy. + /// (Admins will always be able to hear Empathy) + /// + /// The entity to check + public bool CanHearEmpathy(EntityUid entity) + { + var understood = _language.GetUnderstoodLanguages(entity); + for (int i = 0; i < understood.Count; i++) + { + var language = _prototype.Index(understood[i]); + if (language.SpeechOverride.EmpathySpeech) + return true; + } + return false; + } +} \ No newline at end of file diff --git a/Content.Server/Shadowkin/EtherealLightComponent.cs b/Content.Server/Shadowkin/EtherealLightComponent.cs new file mode 100644 index 0000000000..8f47d86200 --- /dev/null +++ b/Content.Server/Shadowkin/EtherealLightComponent.cs @@ -0,0 +1,15 @@ +namespace Content.Server.Shadowkin; + +[RegisterComponent] +public sealed partial class EtherealLightComponent : Component +{ + public EntityUid AttachedEntity = EntityUid.Invalid; + + public float OldRadius = 0f; + + public bool OldRadiusEdited = false; + + public float OldEnergy = 0f; + + public bool OldEnergyEdited = false; +} \ No newline at end of file diff --git a/Content.Server/Shadowkin/EtherealStunItemSystem.cs b/Content.Server/Shadowkin/EtherealStunItemSystem.cs new file mode 100644 index 0000000000..b48b4d4fec --- /dev/null +++ b/Content.Server/Shadowkin/EtherealStunItemSystem.cs @@ -0,0 +1,44 @@ +using Content.Shared.Interaction.Events; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; +using Content.Shared.Shadowkin; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Stacks; + +namespace Content.Server.Shadowkin; + +public sealed class EtherealStunItemSystem : EntitySystem +{ + [Dependency] private readonly StaminaSystem _stamina = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedStackSystem _sharedStackSystem = default!; + public override void Initialize() + { + SubscribeLocalEvent(OnUseInHand); + } + + private void OnUseInHand(EntityUid uid, EtherealStunItemComponent component, UseInHandEvent args) + { + foreach (var ent in _lookup.GetEntitiesInRange(uid, component.Radius)) + { + if (!TryComp(ent, out var ethereal)) + continue; + + RemComp(ent, ethereal); + + if (TryComp(ent, out var stamina)) + _stamina.TakeStaminaDamage(ent, stamina.CritThreshold, stamina, ent); + + if (TryComp(ent, out var magic)) + magic.Mana = 0; + } + + if (!component.DeleteOnUse) + return; + + if (TryComp(uid, out var stack)) + _sharedStackSystem.Use(uid, 1, stack); + else + QueueDel(uid); + } +} diff --git a/Content.Server/Shadowkin/EtherealSystem.cs b/Content.Server/Shadowkin/EtherealSystem.cs new file mode 100644 index 0000000000..2622547a3f --- /dev/null +++ b/Content.Server/Shadowkin/EtherealSystem.cs @@ -0,0 +1,216 @@ +using Content.Shared.Eye; +using Content.Shared.Shadowkin; +using Robust.Server.GameObjects; +using Content.Server.Atmos.Components; +using Content.Server.Temperature.Components; +using Content.Shared.Movement.Components; +using Content.Shared.Stealth; +using Content.Shared.Stealth.Components; +using Content.Server.Body.Components; +using Content.Server.NPC.Components; +using Content.Server.NPC.Systems; +using System.Linq; +using Content.Shared.Abilities.Psionics; +using Robust.Shared.Random; +using Content.Server.Light.Components; + +namespace Content.Server.Shadowkin; + +public sealed class EtherealSystem : SharedEtherealSystem +{ + [Dependency] private readonly VisibilitySystem _visibilitySystem = default!; + [Dependency] private readonly SharedStealthSystem _stealth = default!; + [Dependency] private readonly EyeSystem _eye = default!; + [Dependency] private readonly NpcFactionSystem _factions = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedPointLightSystem _light = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + public override void OnStartup(EntityUid uid, EtherealComponent component, MapInitEvent args) + { + base.OnStartup(uid, component, args); + + var visibility = EnsureComp(uid); + _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Normal, false); + _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Ethereal, false); + _visibilitySystem.RefreshVisibility(uid, visibility); + + if (TryComp(uid, out var eye)) + _eye.SetVisibilityMask(uid, eye.VisibilityMask | (int) (VisibilityFlags.Ethereal), eye); + + if (TryComp(uid, out var temp)) + temp.AtmosTemperatureTransferEfficiency = 0; + + var stealth = EnsureComp(uid); + _stealth.SetVisibility(uid, 0.8f, stealth); + + SuppressFactions(uid, component, true); + + EnsureComp(uid); + EnsureComp(uid); + EnsureComp(uid); + + if (HasComp(uid)) + RemComp(uid, component); + } + + public override void OnShutdown(EntityUid uid, EtherealComponent component, ComponentShutdown args) + { + base.OnShutdown(uid, component, args); + + if (TryComp(uid, out var visibility)) + { + _visibilitySystem.AddLayer((uid, visibility), (int) VisibilityFlags.Normal, false); + _visibilitySystem.RemoveLayer((uid, visibility), (int) VisibilityFlags.Ethereal, false); + _visibilitySystem.RefreshVisibility(uid, visibility); + } + + if (TryComp(uid, out var eye)) + _eye.SetVisibilityMask(uid, (int) VisibilityFlags.Normal, eye); + + if (TryComp(uid, out var temp)) + temp.AtmosTemperatureTransferEfficiency = 0.1f; + + SuppressFactions(uid, component, false); + + RemComp(uid); + RemComp(uid); + RemComp(uid); + RemComp(uid); + + SpawnAtPosition("ShadowkinShadow", Transform(uid).Coordinates); + SpawnAtPosition("EffectFlashShadowkinDarkSwapOff", Transform(uid).Coordinates); + + foreach (var light in component.DarkenedLights.ToArray()) + { + if (!TryComp(light, out var pointLight) + || !TryComp(light, out var etherealLight)) + continue; + + ResetLight(light, pointLight, etherealLight); + } + } + + public void SuppressFactions(EntityUid uid, EtherealComponent component, bool set) + { + if (set) + { + if (!TryComp(uid, out var factions)) + return; + + component.SuppressedFactions = factions.Factions.ToList(); + + foreach (var faction in factions.Factions) + _factions.RemoveFaction(uid, faction); + } + else + { + foreach (var faction in component.SuppressedFactions) + _factions.AddFaction(uid, faction); + + component.SuppressedFactions.Clear(); + } + } + + public void ResetLight(EntityUid uid, PointLightComponent light, EtherealLightComponent etherealLight) + { + etherealLight.AttachedEntity = EntityUid.Invalid; + + if (etherealLight.OldRadiusEdited) + _light.SetRadius(uid, etherealLight.OldRadius); + etherealLight.OldRadiusEdited = false; + + if (etherealLight.OldEnergyEdited) + _light.SetEnergy(uid, etherealLight.OldEnergy); + etherealLight.OldEnergyEdited = false; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var component)) + { + if (!component.Darken) + continue; + + component.DarkenAccumulator += frameTime; + + if (component.DarkenAccumulator <= 1) + continue; + + component.DarkenAccumulator -= component.DarkenRate; + + var darkened = new List(); + var lightQuery = _lookup.GetEntitiesInRange(uid, component.DarkenRange, flags: LookupFlags.StaticSundries) + .Where(x => HasComp(x) && HasComp(x)); + + foreach (var entity in lightQuery) + if (!darkened.Contains(entity)) + darkened.Add(entity); + + _random.Shuffle(darkened); + component.DarkenedLights = darkened; + + var playerPos = _transform.GetWorldPosition(uid); + + foreach (var light in component.DarkenedLights.ToArray()) + { + var lightPos = _transform.GetWorldPosition(light); + if (!TryComp(light, out var pointLight) + || !TryComp(light, out var etherealLight)) + continue; + + if (TryComp(light, out var powered) && !powered.On) + { + ResetLight(light, pointLight, etherealLight); + continue; + } + + if (etherealLight.AttachedEntity == EntityUid.Invalid) + etherealLight.AttachedEntity = uid; + + if (etherealLight.AttachedEntity != EntityUid.Invalid + && etherealLight.AttachedEntity != uid) + { + component.DarkenedLights.Remove(light); + continue; + } + + if (etherealLight.AttachedEntity == uid + && _random.Prob(0.03f)) + etherealLight.AttachedEntity = EntityUid.Invalid; + + if (!etherealLight.OldRadiusEdited) + { + etherealLight.OldRadius = pointLight.Radius; + etherealLight.OldRadiusEdited = true; + } + if (!etherealLight.OldEnergyEdited) + { + etherealLight.OldEnergy = pointLight.Energy; + etherealLight.OldEnergyEdited = true; + } + + var distance = (lightPos - playerPos).Length(); + var radius = distance * 2f; + var energy = distance * 0.8f; + + if (etherealLight.OldRadiusEdited && radius > etherealLight.OldRadius) + radius = etherealLight.OldRadius; + if (etherealLight.OldRadiusEdited && radius < etherealLight.OldRadius * 0.20f) + radius = etherealLight.OldRadius * 0.20f; + + if (etherealLight.OldEnergyEdited && energy > etherealLight.OldEnergy) + energy = etherealLight.OldEnergy; + if (etherealLight.OldEnergyEdited && energy < etherealLight.OldEnergy * 0.20f) + energy = etherealLight.OldEnergy * 0.20f; + + _light.SetRadius(light, radius); + _light.SetEnergy(light, energy); + } + } + } +} \ No newline at end of file diff --git a/Content.Server/Shadowkin/ShadowkinCuffSystem.cs b/Content.Server/Shadowkin/ShadowkinCuffSystem.cs new file mode 100644 index 0000000000..ce2b258817 --- /dev/null +++ b/Content.Server/Shadowkin/ShadowkinCuffSystem.cs @@ -0,0 +1,29 @@ +using Content.Shared.Inventory.Events; +using Content.Shared.Clothing.Components; +using Content.Shared.Shadowkin; + +namespace Content.Server.Shadowkin; + +public sealed class ShadowkinCuffSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnEquipped); + SubscribeLocalEvent(OnUnequipped); + } + + private void OnEquipped(EntityUid uid, ShadowkinCuffComponent component, GotEquippedEvent args) + { + if (!TryComp(uid, out var clothing) + || !clothing.Slots.HasFlag(args.SlotFlags)) + return; + + EnsureComp(args.Equipee); + } + + private void OnUnequipped(EntityUid uid, ShadowkinCuffComponent component, GotUnequippedEvent args) + { + RemComp(args.Equipee); + } +} \ No newline at end of file diff --git a/Content.Server/Shadowkin/ShadowkinSystem.cs b/Content.Server/Shadowkin/ShadowkinSystem.cs new file mode 100644 index 0000000000..83461e7a7f --- /dev/null +++ b/Content.Server/Shadowkin/ShadowkinSystem.cs @@ -0,0 +1,181 @@ +using Content.Shared.Examine; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Humanoid; +using Content.Shared.Psionics; +using Content.Shared.Bed.Sleep; +using Content.Shared.Damage.Components; +using Content.Shared.Damage.Systems; +using Content.Shared.Shadowkin; +using Content.Shared.Rejuvenate; +using Content.Shared.Alert; +using Content.Shared.Rounding; +using Content.Shared.Actions; +using Robust.Shared.Prototypes; +using Content.Server.Abilities.Psionics; + +namespace Content.Server.Shadowkin; + +public sealed class ShadowkinSystem : EntitySystem +{ + [Dependency] private readonly StaminaSystem _stamina = default!; + [Dependency] private readonly PsionicAbilitiesSystem _psionicAbilitiesSystem = default!; + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public const string ShadowkinSleepActionId = "ShadowkinActionSleep"; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnMindbreak); + SubscribeLocalEvent(OnAttemptPowerUse); + SubscribeLocalEvent(OnManaUpdate); + SubscribeLocalEvent(OnRejuvenate); + SubscribeLocalEvent(OnEyeColorChange); + } + + private void OnInit(EntityUid uid, ShadowkinComponent component, ComponentStartup args) + { + if (component.BlackeyeSpawn) + ApplyBlackEye(uid); + + _actionsSystem.AddAction(uid, ref component.ShadowkinSleepAction, ShadowkinSleepActionId, uid); + + UpdateShadowkinAlert(uid, component); + } + + private void OnEyeColorChange(EntityUid uid, ShadowkinComponent component, EyeColorInitEvent args) + { + if (!TryComp(uid, out var humanoid) + || !component.BlackeyeSpawn + || humanoid.EyeColor == component.OldEyeColor) + return; + + component.OldEyeColor = humanoid.EyeColor; + humanoid.EyeColor = component.BlackEyeColor; + Dirty(humanoid); + } + + private void OnExamined(EntityUid uid, ShadowkinComponent component, ExaminedEvent args) + { + if (!args.IsInDetailsRange + || !TryComp(uid, out var magic) + || HasComp(uid)) + return; + + var severity = "shadowkin-power-" + ContentHelpers.RoundToLevels(magic.Mana, magic.MaxMana, 6); + var powerType = Loc.GetString(severity); + + if (args.Examined == args.Examiner) + args.PushMarkup(Loc.GetString("shadowkin-power-examined-self", + ("power", Math.Floor(magic.Mana)), + ("powerMax", Math.Floor(magic.MaxMana)), + ("powerType", powerType) + )); + else + args.PushMarkup(Loc.GetString("shadowkin-power-examined-other", + ("target", uid), + ("powerType", powerType) + )); + } + + /// + /// Update the Shadowkin Alert, if Blackeye will remove the Alert, if not will update to its current power status. + /// + public void UpdateShadowkinAlert(EntityUid uid, ShadowkinComponent component) + { + if (TryComp(uid, out var magic)) + { + var severity = (short) ContentHelpers.RoundToLevels(magic.Mana, magic.MaxMana, 8); + _alerts.ShowAlert(uid, AlertType.ShadowkinPower, severity); + } + else + _alerts.ClearAlert(uid, AlertType.ShadowkinPower); + } + + private void OnAttemptPowerUse(EntityUid uid, ShadowkinComponent component, OnAttemptPowerUseEvent args) + { + if (HasComp(uid)) + args.Cancel(); + } + + private void OnManaUpdate(EntityUid uid, ShadowkinComponent component, ref OnManaUpdateEvent args) + { + if (!TryComp(uid, out var magic)) + return; + + if (component.SleepManaRegen + && TryComp(uid, out var sleep)) + magic.ManaGainMultiplier = component.SleepManaRegenMultiplier; + else + magic.ManaGainMultiplier = 1; + + if (magic.Mana <= component.BlackEyeMana) + ApplyBlackEye(uid); + + Dirty(magic); // Update Shadowkin Overlay. + UpdateShadowkinAlert(uid, component); + } + + /// + /// Blackeye the Shadowkin, its just a function to mindbreak the shadowkin but making sure "Removable" is checked true during it. + /// + /// + public void ApplyBlackEye(EntityUid uid) + { + if (!TryComp(uid, out var magic)) + return; + + magic.Removable = true; + _psionicAbilitiesSystem.MindBreak(uid); + } + + private void OnMindbreak(EntityUid uid, ShadowkinComponent component, ref OnMindbreakEvent args) + { + if (TryComp(uid, out var mindbreak)) + mindbreak.MindbrokenExaminationText = "examine-mindbroken-shadowkin-message"; + + if (TryComp(uid, out var humanoid)) + { + component.OldEyeColor = humanoid.EyeColor; + humanoid.EyeColor = component.BlackEyeColor; + Dirty(humanoid); + } + + if (component.BlackeyeSpawn) + return; + + if (TryComp(uid, out var stamina)) + _stamina.TakeStaminaDamage(uid, stamina.CritThreshold, stamina, uid); + } + + private void OnRejuvenate(EntityUid uid, ShadowkinComponent component, RejuvenateEvent args) + { + if (component.BlackeyeSpawn + || !HasComp(uid)) + return; + + RemComp(uid); + + if (TryComp(uid, out var humanoid)) + { + humanoid.EyeColor = component.OldEyeColor; + Dirty(humanoid); + } + + EnsureComp(uid, out var magic); + magic.Mana = 250; + magic.MaxMana = 250; + magic.ManaGain = 0.25f; + magic.BypassManaCheck = true; + magic.Removable = false; + magic.MindbreakingFeedback = "shadowkin-blackeye"; + + if (_prototypeManager.TryIndex("ShadowkinPowers", out var shadowkinPowers)) + _psionicAbilitiesSystem.InitializePsionicPower(uid, shadowkinPowers); + + UpdateShadowkinAlert(uid, component); + } +} diff --git a/Content.Server/Shadowkin/ShowEtherealSystem.cs b/Content.Server/Shadowkin/ShowEtherealSystem.cs new file mode 100644 index 0000000000..151c379afb --- /dev/null +++ b/Content.Server/Shadowkin/ShowEtherealSystem.cs @@ -0,0 +1,88 @@ +using Content.Shared.Shadowkin; +using Content.Shared.Eye; +using Robust.Server.GameObjects; +using Content.Shared.Inventory.Events; +using Content.Shared.Interaction.Events; +using Robust.Shared.Timing; +using Content.Shared.Popups; +using Content.Shared.Clothing.Components; + +namespace Content.Server.Shadowkin; +public sealed class ShowEtherealSystem : EntitySystem +{ + [Dependency] private readonly EyeSystem _eye = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnEquipped); + SubscribeLocalEvent(OnUnequipped); + SubscribeLocalEvent(OnInteractionAttempt); + SubscribeLocalEvent(OnAttackAttempt); + } + + private void OnInit(EntityUid uid, ShowEtherealComponent component, MapInitEvent args) + { + Toggle(uid, true); + } + + public void OnShutdown(EntityUid uid, ShowEtherealComponent component, ComponentShutdown args) + { + Toggle(uid, false); + } + + private void OnEquipped(EntityUid uid, ShowEtherealComponent component, GotEquippedEvent args) + { + if (!TryComp(uid, out var clothing) + || !clothing.Slots.HasFlag(args.SlotFlags)) + return; + + EnsureComp(args.Equipee); + } + + private void OnUnequipped(EntityUid uid, ShowEtherealComponent component, GotUnequippedEvent args) + { + RemComp(args.Equipee); + } + + private void Toggle(EntityUid uid, bool toggle) + { + if (!TryComp(uid, out var eye)) + return; + + if (toggle) + { + _eye.SetVisibilityMask(uid, eye.VisibilityMask | (int) (VisibilityFlags.Ethereal), eye); + return; + } + else if (HasComp(uid)) + return; + + _eye.SetVisibilityMask(uid, (int) VisibilityFlags.Normal, eye); + } + + private void OnInteractionAttempt(EntityUid uid, ShowEtherealComponent component, InteractionAttemptEvent args) + { + if (HasComp(uid) + || !HasComp(args.Target)) + return; + + args.Cancel(); + if (_gameTiming.InPrediction) + return; + + _popup.PopupEntity(Loc.GetString("ethereal-pickup-fail"), args.Target.Value, uid); + } + + private void OnAttackAttempt(EntityUid uid, ShowEtherealComponent component, AttackAttemptEvent args) + { + if (HasComp(uid) + || !HasComp(args.Target)) + return; + + args.Cancel(); + } +} \ No newline at end of file diff --git a/Content.Shared/Actions/Events/AnomalyPowerActionEvent.cs b/Content.Shared/Actions/Events/AnomalyPowerActionEvent.cs index 7ef98a152c..9184460153 100644 --- a/Content.Shared/Actions/Events/AnomalyPowerActionEvent.cs +++ b/Content.Shared/Actions/Events/AnomalyPowerActionEvent.cs @@ -76,6 +76,10 @@ public partial record struct AnomalyPowerSettings() { public string PowerName; + public float ManaCost; + + public bool CheckInsulation; + /// /// When casting above the Supercritical Threshold, if not 0, this will cause all powers to enter cooldown for the given duration. /// diff --git a/Content.Shared/Actions/Events/DarkSwapActionEvent.cs b/Content.Shared/Actions/Events/DarkSwapActionEvent.cs new file mode 100644 index 0000000000..ad60d0ede4 --- /dev/null +++ b/Content.Shared/Actions/Events/DarkSwapActionEvent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared.Actions.Events; +public sealed partial class DarkSwapActionEvent : InstantActionEvent +{ + [DataField] + public float ManaCost; + + [DataField] + public bool CheckInsulation; +} \ No newline at end of file diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs index 0808114b9d..bd8c1dbe25 100644 --- a/Content.Shared/Alert/AlertType.cs +++ b/Content.Shared/Alert/AlertType.cs @@ -71,6 +71,7 @@ public enum AlertType : byte BorgCrit, BorgDead, Offer, + ShadowkinPower, Deflecting, } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index bacb2d2361..208d6d911d 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -2001,6 +2001,12 @@ public static readonly CVarDef public static readonly CVarDef ICShowSSDIndicator = CVarDef.Create("ic.show_ssd_indicator", true, CVar.CLIENTONLY); + /// + /// Allow Ethereal Ent to PassThrough Walls/Objects while in Ethereal. + /// + public static readonly CVarDef EtherealPassThrough = + CVarDef.Create("ic.EtherealPassThrough", false, CVar.SERVER); + /* * Salvage */ diff --git a/Content.Shared/Eye/VisibilityFlags.cs b/Content.Shared/Eye/VisibilityFlags.cs index 7e2dd33d7d..2e20b1de4f 100644 --- a/Content.Shared/Eye/VisibilityFlags.cs +++ b/Content.Shared/Eye/VisibilityFlags.cs @@ -6,10 +6,11 @@ namespace Content.Shared.Eye [FlagsFor(typeof(VisibilityMaskLayer))] public enum VisibilityFlags : int { - None = 0, + None = 0, Normal = 1 << 0, - Ghost = 1 << 1, + Ghost = 1 << 1, PsionicInvisibility = 1 << 2, //Nyano - Summary: adds Psionic Invisibility as a visibility layer. Currently does nothing. - TelegnosticProjection = 5, + TelegnosticProjection = 5, + Ethereal = 1 << 3, } } diff --git a/Content.Shared/Humanoid/EyeColor.cs b/Content.Shared/Humanoid/EyeColor.cs new file mode 100644 index 0000000000..a39e090a86 --- /dev/null +++ b/Content.Shared/Humanoid/EyeColor.cs @@ -0,0 +1,4 @@ +namespace Content.Shared.Humanoid; + +[ByRefEvent] +public record struct EyeColorInitEvent(); \ No newline at end of file diff --git a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs index dc1e6b736c..a1e8bec2cd 100644 --- a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs +++ b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs @@ -15,6 +15,7 @@ using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Prototypes; +using Content.Shared.Shadowkin; using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Markdown; using Robust.Shared.Utility; @@ -104,6 +105,12 @@ private void OnExamined(EntityUid uid, HumanoidAppearanceComponent component, Ex var identity = Identity.Entity(uid, EntityManager); var species = GetSpeciesRepresentation(component.Species, component.CustomSpecieName).ToLower(); var age = GetAgeRepresentation(component.Species, component.Age); + if (HasComp(uid)) + { + var color = component.EyeColor.Name(); + if (color != null) + age = Loc.GetString("identity-eye-shadowkin", ("color", color)); + } args.PushText(Loc.GetString("humanoid-appearance-component-examine", ("user", identity), ("age", age), ("species", species))); } @@ -362,6 +369,8 @@ public virtual void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, SetSpecies(uid, profile.Species, false, humanoid); SetSex(uid, profile.Sex, false, humanoid); humanoid.EyeColor = profile.Appearance.EyeColor; + var ev = new EyeColorInitEvent(); + RaiseLocalEvent(uid, ref ev); SetSkinColor(uid, profile.Appearance.SkinColor, false); diff --git a/Content.Shared/Language/LanguagePrototype.cs b/Content.Shared/Language/LanguagePrototype.cs index 2137e4e838..3b2b24c4b2 100644 --- a/Content.Shared/Language/LanguagePrototype.cs +++ b/Content.Shared/Language/LanguagePrototype.cs @@ -53,6 +53,14 @@ public sealed partial class SpeechOverrideInfo [DataField] public bool AllowRadio = true; + /// + /// If true, the message will be relayed to the Empathy Chat and + /// anyone with that language will also hear Empathy Chat. (Unless user has ShadowkinBlackeyeComponent) + /// This is mostly only use for "Marish" but... fuckit modularity :p + /// + [DataField] + public bool EmpathySpeech = false; + /// /// If false, the entity can use this language even when it's unable to speak (i.e. muffled or muted), /// and accents are not applied to messages in this language. diff --git a/Content.Shared/Psionics/PsionicComponent.cs b/Content.Shared/Psionics/PsionicComponent.cs index 37d0a9a7ef..5a3cce19ad 100644 --- a/Content.Shared/Psionics/PsionicComponent.cs +++ b/Content.Shared/Psionics/PsionicComponent.cs @@ -5,9 +5,38 @@ namespace Content.Shared.Abilities.Psionics { - [RegisterComponent, NetworkedComponent] + [RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] public sealed partial class PsionicComponent : Component { + /// + /// Current Mana. + /// + [DataField, AutoNetworkedField] + public float Mana; + + /// + /// Max Mana Possible. + /// + [DataField, AutoNetworkedField] + public float MaxMana = 100; + + /// + /// How much energy is gained per second. + /// + [DataField] + public float ManaGain = 1; + + /// + /// ManaGain Multiplier + /// + [DataField] + public float ManaGainMultiplier = 1; + + public float ManaAccumulator; + + [DataField] + public bool BypassManaCheck; + /// /// How close a Psion is to generating a new power. When Potentia reaches the NextPowerCost, it is "Spent" in order to "Buy" a random new power. /// TODO: Psi-Potentiometry should be able to read how much Potentia a person has. @@ -45,6 +74,10 @@ public sealed partial class PsionicComponent : Component public string MindbreakingStutterAccent = "StutteringAccent"; + /// + /// The message feedback given on mindbreak. + /// + [DataField] public string MindbreakingFeedback = "mindbreaking-feedback"; /// @@ -163,6 +196,7 @@ private set [ViewVariables(VVAccess.ReadWrite)] public int PowerSlotsTaken; + /// /// List of descriptors this entity will bring up for psychognomy. Used to remove /// unneccesary subs for unique psionic entities like e.g. Oracle. /// @@ -176,5 +210,8 @@ private set /// Popup to play if a Psion attempts to start casting a power while already casting one [DataField] public string AlreadyCasting = "already-casting"; + + /// Popup to play if there no Mana left for a power to execute. + public string NoMana = "no-mana"; } } diff --git a/Content.Shared/Psionics/PsionicEvents.cs b/Content.Shared/Psionics/PsionicEvents.cs index be3bf03af6..4d13441798 100644 --- a/Content.Shared/Psionics/PsionicEvents.cs +++ b/Content.Shared/Psionics/PsionicEvents.cs @@ -8,4 +8,10 @@ namespace Content.Shared.Psionics; /// /// [ByRefEvent] -public record struct OnSetPsionicStatsEvent(float AmplificationChangedAmount, float DampeningChangedAmount); \ No newline at end of file +public record struct OnSetPsionicStatsEvent(float AmplificationChangedAmount, float DampeningChangedAmount); + +[ByRefEvent] +public record struct OnMindbreakEvent(); + +[ByRefEvent] +public record struct OnManaUpdateEvent(); \ No newline at end of file diff --git a/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs b/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs index 2792864568..b79dabbc41 100644 --- a/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs +++ b/Content.Shared/Psionics/SharedPsionicAbilitiesSystem.cs @@ -1,9 +1,13 @@ using Content.Shared.Administration.Logs; using Content.Shared.Contests; using Content.Shared.Popups; +using Content.Shared.Psionics; using Content.Shared.Psionics.Glimmer; using Robust.Shared.Random; using Robust.Shared.Serialization; +using Content.Shared.Mobs.Systems; +using Content.Shared.FixedPoint; +using Content.Shared.Rejuvenate; namespace Content.Shared.Abilities.Psionics { @@ -15,11 +19,54 @@ public sealed class SharedPsionicAbilitiesSystem : EntitySystem [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly ContestsSystem _contests = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnPowerUsed); + SubscribeLocalEvent(OnRejuvenate); + } + + public bool OnAttemptPowerUse(EntityUid uid, string power, float? manacost = null, bool checkInsulation = true) + { + if (!TryComp(uid, out var component) + || HasComp(uid) + || checkInsulation + && HasComp(uid)) + return false; + + var tev = new OnAttemptPowerUseEvent(uid, power); + RaiseLocalEvent(uid, tev); + + if (tev.Cancelled) + return false; + + if (component.DoAfter is not null) + { + _popups.PopupEntity(Loc.GetString(component.AlreadyCasting), uid, uid, PopupType.LargeCaution); + return false; + } + + if (manacost is null) + return true; + + if (component.Mana >= manacost + || component.BypassManaCheck) + { + var newmana = component.Mana - manacost; + component.Mana = newmana ?? component.Mana; + + var ev = new OnManaUpdateEvent(); + RaiseLocalEvent(uid, ref ev); + } + else + { + _popups.PopupEntity(Loc.GetString(component.NoMana), uid, uid, PopupType.LargeCaution); + return false; + } + + return true; } private void OnPowerUsed(EntityUid uid, PsionicComponent component, PsionicPowerUsedEvent args) @@ -85,6 +132,45 @@ public float ModifiedDampening(EntityUid uid, PsionicComponent component) { return component.CurrentDampening / _contests.MoodContest(uid, true); } + + public void OnRejuvenate(EntityUid uid, PsionicComponent component, RejuvenateEvent args) + { + component.Mana = component.MaxMana; + var ev = new OnManaUpdateEvent(); + RaiseLocalEvent(uid, ref ev); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var component)) + { + if (_mobState.IsDead(uid)) + continue; + + component.ManaAccumulator += frameTime; + + if (component.ManaAccumulator <= 1) + continue; + + component.ManaAccumulator -= 1; + + if (component.Mana > component.MaxMana) + component.Mana = component.MaxMana; + + if (component.Mana < component.MaxMana) + { + var gainedmana = component.ManaGain * component.ManaGainMultiplier; + component.Mana += gainedmana; + FixedPoint2.Min(component.Mana, component.MaxMana); + + var ev = new OnManaUpdateEvent(); + RaiseLocalEvent(uid, ref ev); + } + } + } } public sealed class PsionicPowerUsedEvent : HandledEntityEventArgs @@ -99,6 +185,18 @@ public PsionicPowerUsedEvent(EntityUid user, string power) } } + public sealed class OnAttemptPowerUseEvent : CancellableEntityEventArgs + { + public EntityUid User { get; } + public string Power = string.Empty; + + public OnAttemptPowerUseEvent(EntityUid user, string power) + { + User = user; + Power = power; + } + } + [Serializable] [NetSerializable] public sealed class PsionicsChangedEvent : EntityEventArgs diff --git a/Content.Shared/Shadowkin/EtherealComponent.cs b/Content.Shared/Shadowkin/EtherealComponent.cs new file mode 100644 index 0000000000..0fc50c0f12 --- /dev/null +++ b/Content.Shared/Shadowkin/EtherealComponent.cs @@ -0,0 +1,36 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Shadowkin; + +[RegisterComponent, NetworkedComponent] +public sealed partial class EtherealComponent : Component +{ + /// + /// Does the Ent, Dark lights around it? + /// + [DataField] + public bool Darken = false; + + /// + /// Range of the Darken Effect. + /// + [DataField] + public float DarkenRange = 5; + + /// + /// Darken Effect Rate. + /// + [DataField] + public float DarkenRate = 0.084f; + + public List DarkenedLights = new(); + + public float DarkenAccumulator; + + public int OldMobMask; + + public int OldMobLayer; + + public List SuppressedFactions = new(); + public bool HasDoorBumpTag; +} \ No newline at end of file diff --git a/Content.Shared/Shadowkin/EtherealStunItemComponent.cs b/Content.Shared/Shadowkin/EtherealStunItemComponent.cs new file mode 100644 index 0000000000..053b5c11f6 --- /dev/null +++ b/Content.Shared/Shadowkin/EtherealStunItemComponent.cs @@ -0,0 +1,11 @@ +namespace Content.Shared.Shadowkin; + +[RegisterComponent] +public sealed partial class EtherealStunItemComponent : Component +{ + [DataField] + public float Radius = 10; + + [DataField] + public bool DeleteOnUse = true; +} \ No newline at end of file diff --git a/Content.Shared/Shadowkin/ShadowkinComponent.cs b/Content.Shared/Shadowkin/ShadowkinComponent.cs new file mode 100644 index 0000000000..b382f3112b --- /dev/null +++ b/Content.Shared/Shadowkin/ShadowkinComponent.cs @@ -0,0 +1,42 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Shadowkin; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ShadowkinComponent : Component +{ + /// + /// Apply the SleepManaRegenMultiplier on SleepComponent if true. + /// + [DataField] + public bool SleepManaRegen = true; + + /// + /// What do edit the ManaRegenMultiplier when on Sleep. + /// + [DataField] + public float SleepManaRegenMultiplier = 4; + + /// + /// On MapInitEvent, will Blackeye the Shadowkin. + /// + [DataField] + public bool BlackeyeSpawn; + + /// + /// If mana is equal or lower then this value, blackeye the shadowkin. + /// + [DataField] + public float BlackEyeMana; + + /// + /// Set the Black-Eye Color. + /// + [DataField] + public Color BlackEyeColor = Color.Black; + + public Color OldEyeColor = Color.LimeGreen; + + [DataField] + public EntityUid? ShadowkinSleepAction; +} \ No newline at end of file diff --git a/Content.Shared/Shadowkin/ShadowkinCuffComponent.cs b/Content.Shared/Shadowkin/ShadowkinCuffComponent.cs new file mode 100644 index 0000000000..b4c62d6664 --- /dev/null +++ b/Content.Shared/Shadowkin/ShadowkinCuffComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Shared.Shadowkin; + +[RegisterComponent] +public sealed partial class ShadowkinCuffComponent : Component { } \ No newline at end of file diff --git a/Content.Shared/Shadowkin/SharedEtherealSystem.cs b/Content.Shared/Shadowkin/SharedEtherealSystem.cs new file mode 100644 index 0000000000..66196faf0a --- /dev/null +++ b/Content.Shared/Shadowkin/SharedEtherealSystem.cs @@ -0,0 +1,141 @@ +using Content.Shared.Physics; +using Robust.Shared.Physics; +using System.Linq; +using Robust.Shared.Physics.Systems; +using Content.Shared.Interaction.Events; +using Robust.Shared.Timing; +using Content.Shared.Popups; +using Content.Shared.Throwing; +using Content.Shared.Weapons.Ranged.Events; +using Content.Shared.CombatMode.Pacification; +using Content.Shared.Psionics; +using Content.Shared.Mobs; +using Content.Shared.CCVar; +using Robust.Shared.Configuration; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Tag; + +namespace Content.Shared.Shadowkin; + +public abstract class SharedEtherealSystem : EntitySystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly TagSystem _tag = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnInteractionAttempt); + SubscribeLocalEvent(OnBeforeThrow); + SubscribeLocalEvent(OnAttemptPowerUse); + SubscribeLocalEvent(OnAttackAttempt); + SubscribeLocalEvent(OnShootAttempt); + SubscribeLocalEvent(OnMindbreak); + SubscribeLocalEvent(OnMobStateChanged); + } + + public virtual void OnStartup(EntityUid uid, EtherealComponent component, MapInitEvent args) + { + if (!TryComp(uid, out var fixtures)) + return; + + var fixture = fixtures.Fixtures.First(); + + component.OldMobMask = fixture.Value.CollisionMask; + component.OldMobLayer = fixture.Value.CollisionLayer; + + if (_cfg.GetCVar(CCVars.EtherealPassThrough)) + { + _physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) CollisionGroup.GhostImpassable, fixtures); + _physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, 0, fixtures); + + if (_tag.RemoveTag(uid, "DoorBumpOpener")) + component.HasDoorBumpTag = true; + + return; + } + + _physics.SetCollisionMask(uid, fixture.Key, fixture.Value, (int) CollisionGroup.FlyingMobMask, fixtures); + _physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, (int) CollisionGroup.FlyingMobLayer, fixtures); + } + + public virtual void OnShutdown(EntityUid uid, EtherealComponent component, ComponentShutdown args) + { + if (!TryComp(uid, out var fixtures)) + return; + + var fixture = fixtures.Fixtures.First(); + + _physics.SetCollisionMask(uid, fixture.Key, fixture.Value, component.OldMobMask, fixtures); + _physics.SetCollisionLayer(uid, fixture.Key, fixture.Value, component.OldMobLayer, fixtures); + + if (component.HasDoorBumpTag) + _tag.AddTag(uid, "DoorBumpOpener"); + } + + private void OnMindbreak(EntityUid uid, EtherealComponent component, ref OnMindbreakEvent args) + { + RemComp(uid, component); + } + + private void OnMobStateChanged(EntityUid uid, EtherealComponent component, MobStateChangedEvent args) + { + if (args.NewMobState == MobState.Critical + || args.NewMobState == MobState.Dead) + RemComp(uid, component); + } + + private void OnShootAttempt(Entity ent, ref ShotAttemptedEvent args) + { + args.Cancel(); + } + + private void OnAttackAttempt(EntityUid uid, EtherealComponent component, AttackAttemptEvent args) + { + if (HasComp(args.Target)) + return; + + args.Cancel(); + } + + private void OnBeforeThrow(Entity ent, ref BeforeThrowEvent args) + { + var thrownItem = args.ItemUid; + + // Raise an AttemptPacifiedThrow event and rely on other systems to check + // whether the candidate item is OK to throw: + var ev = new AttemptPacifiedThrowEvent(thrownItem, ent); + RaiseLocalEvent(thrownItem, ref ev); + if (!ev.Cancelled) + return; + + args.Cancelled = true; + } + + private void OnInteractionAttempt(EntityUid uid, EtherealComponent component, InteractionAttemptEvent args) + { + if (!HasComp(args.Target) + || HasComp(args.Target)) + return; + + args.Cancel(); + if (_gameTiming.InPrediction) + return; + + _popup.PopupEntity(Loc.GetString("ethereal-pickup-fail"), args.Target.Value, uid); + } + + private void OnAttemptPowerUse(EntityUid uid, EtherealComponent component, OnAttemptPowerUseEvent args) + { + if (args.Power == "DarkSwap") + return; + + args.Cancel(); + } +} diff --git a/Content.Shared/Shadowkin/ShowEtherealComponent.cs b/Content.Shared/Shadowkin/ShowEtherealComponent.cs new file mode 100644 index 0000000000..45fa78fa0c --- /dev/null +++ b/Content.Shared/Shadowkin/ShowEtherealComponent.cs @@ -0,0 +1,6 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Shadowkin; + +[RegisterComponent] +public sealed partial class ShowEtherealComponent : Component { } \ No newline at end of file diff --git a/Resources/Audio/Effects/Shadowkin/Powers/darkswapoff.ogg b/Resources/Audio/Effects/Shadowkin/darkswapoff.ogg similarity index 100% rename from Resources/Audio/Effects/Shadowkin/Powers/darkswapoff.ogg rename to Resources/Audio/Effects/Shadowkin/darkswapoff.ogg diff --git a/Resources/Audio/Effects/Shadowkin/Powers/darkswapon.ogg b/Resources/Audio/Effects/Shadowkin/darkswapon.ogg similarity index 100% rename from Resources/Audio/Effects/Shadowkin/Powers/darkswapon.ogg rename to Resources/Audio/Effects/Shadowkin/darkswapon.ogg diff --git a/Resources/Audio/Effects/Shadowkin/Powers/futuristic-teleport.ogg b/Resources/Audio/Effects/Shadowkin/futuristic-teleport.ogg similarity index 100% rename from Resources/Audio/Effects/Shadowkin/Powers/futuristic-teleport.ogg rename to Resources/Audio/Effects/Shadowkin/futuristic-teleport.ogg diff --git a/Resources/Audio/Effects/Shadowkin/Powers/license.txt b/Resources/Audio/Effects/Shadowkin/license.txt similarity index 75% rename from Resources/Audio/Effects/Shadowkin/Powers/license.txt rename to Resources/Audio/Effects/Shadowkin/license.txt index c77ea8eb09..d87bd10983 100644 --- a/Resources/Audio/Effects/Shadowkin/Powers/license.txt +++ b/Resources/Audio/Effects/Shadowkin/license.txt @@ -1,4 +1,4 @@ darkswapon.ogg licensed under Pixabay Licence taken from https://pixabay.com/users/cristian_changing-30278997/ darkswapoff.ogg licensed under Pixabay Licence taken from https://pixabay.com/users/cristian_changing-30278997/ futuristic-teleport.ogg licensed under Pixabay Licence taken from https://pixabay.com/users/cristian_changing-30278997/ -teleport.ogg licensed under Pixabay Licence taken from https://pixabay.com/users/cristian_changing-30278997/ +shadeskip.ogg licensed under Pixabay Licence taken from https://pixabay.com/users/cristian_changing-30278997/ diff --git a/Resources/Audio/Effects/Shadowkin/Powers/teleport.ogg b/Resources/Audio/Effects/Shadowkin/shadeskip.ogg similarity index 100% rename from Resources/Audio/Effects/Shadowkin/Powers/teleport.ogg rename to Resources/Audio/Effects/Shadowkin/shadeskip.ogg diff --git a/Resources/Audio/Voice/Shadowkin/attributions.yml b/Resources/Audio/Voice/Shadowkin/attributions.yml new file mode 100644 index 0000000000..4ab746f465 --- /dev/null +++ b/Resources/Audio/Voice/Shadowkin/attributions.yml @@ -0,0 +1,4 @@ +- files: ["wurble.ogg", "mar.ogg"] + license: "CC-BY-SA-3.0" + copyright: "Taken from CHOMPStation" + source: "https://github.com/CHOMPStation2/CHOMPStation2" \ No newline at end of file diff --git a/Resources/Audio/Voice/Shadowkin/mar.ogg b/Resources/Audio/Voice/Shadowkin/mar.ogg new file mode 100644 index 0000000000000000000000000000000000000000..b13d2df83774dd6bdc545157e1054ae3c877a180 GIT binary patch literal 10161 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 Gi;PI&d literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/actions/actions/shadowkin.ftl b/Resources/Locale/en-US/actions/actions/shadowkin.ftl new file mode 100644 index 0000000000..063e1eafb7 --- /dev/null +++ b/Resources/Locale/en-US/actions/actions/shadowkin.ftl @@ -0,0 +1,2 @@ +action-name-shadowkin-rest = Rest +action-description-shadowkin-rest = Rama diff --git a/Resources/Locale/en-US/alerts/shadowkin.ftl b/Resources/Locale/en-US/alerts/shadowkin.ftl new file mode 100644 index 0000000000..10f8438b76 --- /dev/null +++ b/Resources/Locale/en-US/alerts/shadowkin.ftl @@ -0,0 +1,2 @@ +alerts-shadowkin-power-name = Power Level +alerts-shadowkin-power-desc = How much energy is available to spend on Shadowkin powers. \ No newline at end of file diff --git a/Resources/Locale/en-US/chat/emotes.ftl b/Resources/Locale/en-US/chat/emotes.ftl index e95cb2795d..4e26752c4b 100644 --- a/Resources/Locale/en-US/chat/emotes.ftl +++ b/Resources/Locale/en-US/chat/emotes.ftl @@ -28,6 +28,8 @@ chat-emote-name-monkeyscreeches = Monkey Screeches chat-emote-name-robotbeep = Robot chat-emote-name-yawn = Yawn chat-emote-name-snore = Snore +chat-emote-name-mars = Mars +chat-emote-name-wurble = Wurble # Message chat-emote-msg-scream = screams! diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index 034f38f854..e25522ac1d 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -50,6 +50,8 @@ chat-manager-admin-channel-name = ADMIN chat-manager-rate-limited = You are sending messages too quickly! chat-manager-rate-limit-admin-announcement = Player { $player } breached chat rate limits. Watch them if this is a regular occurence. +chat-manager-send-empathy-chat-wrap-message = {$source}: {$message} + ## Speech verbs for chat chat-speech-verb-suffix-exclamation = ! @@ -156,3 +158,5 @@ chat-speech-verb-name-electricity = Electricity chat-speech-verb-electricity-1 = crackles chat-speech-verb-electricity-2 = buzzes chat-speech-verb-electricity-3 = screeches + +chat-speech-verb-marish = Mars \ No newline at end of file diff --git a/Resources/Locale/en-US/chat/ui/chat-box.ftl b/Resources/Locale/en-US/chat/ui/chat-box.ftl index 720f0d15ab..0dbfc0a27b 100644 --- a/Resources/Locale/en-US/chat/ui/chat-box.ftl +++ b/Resources/Locale/en-US/chat/ui/chat-box.ftl @@ -30,4 +30,4 @@ hud-chatbox-channel-Notifications = Notifications hud-chatbox-channel-Server = Server hud-chatbox-channel-Visual = Actions hud-chatbox-channel-Damage = Damage -hud-chatbox-channel-Unspecified = Unspecified +hud-chatbox-channel-Unspecified = Unspecified \ No newline at end of file diff --git a/Resources/Locale/en-US/language/languages.ftl b/Resources/Locale/en-US/language/languages.ftl index 02527498db..76d172658b 100644 --- a/Resources/Locale/en-US/language/languages.ftl +++ b/Resources/Locale/en-US/language/languages.ftl @@ -61,6 +61,9 @@ language-RobotTalk-description = A language consisting of harsh binary chirps, w language-Sign-name = Tau-Ceti Basic Sign Language language-Sign-description = TCB-SL for short, this sign language is prevalent among mute and deaf people. +language-Marish-name = Marish +language-Marish-description = An Language that can be used to speak in Empathy, Sharing eachother emotions with only one word, Shadowkins speaks this language with ease, tho its is nearly impossible to replicate it or learn it. + language-ValyrianStandard-name = Valyrian Standard language-ValyrianStandard-description = A language descended from eastern european languages of old earth - Valyrian Standard is the commonly spoken tongue of Harpies brought up on their homeworld of Valyrian 4b diff --git a/Resources/Locale/en-US/markings/shadowkin.ftl b/Resources/Locale/en-US/markings/shadowkin.ftl new file mode 100644 index 0000000000..5ad1f09963 --- /dev/null +++ b/Resources/Locale/en-US/markings/shadowkin.ftl @@ -0,0 +1,7 @@ +marking-EyesShadowkin = Shadowkin + +marking-TailShadowkin = Shadowkin +marking-TailShadowkinBig = Shadowkin (Big) +marking-TailShadowkinShorter = Shadowkin (Short) +marking-TailShadowkinMedium = Shadowkin (Medium) +marking-TailShadowkinBigFluff = Shadowkin (Big and Fluffy) \ No newline at end of file diff --git a/Resources/Locale/en-US/metabolism/metabolizer-types.ftl b/Resources/Locale/en-US/metabolism/metabolizer-types.ftl index 30ab6c050e..d0f57e2bc0 100644 --- a/Resources/Locale/en-US/metabolism/metabolizer-types.ftl +++ b/Resources/Locale/en-US/metabolism/metabolizer-types.ftl @@ -11,3 +11,4 @@ metabolizer-type-moth = Moth metabolizer-type-arachnid = Arachnid metabolizer-type-vampiric = Vampiric metabolizer-type-liquorlifeline = Liquor Lifeline +metabolizer-type-shadowkin = Shadowkin diff --git a/Resources/Locale/en-US/psionics/psionic-powers.ftl b/Resources/Locale/en-US/psionics/psionic-powers.ftl index fec124c7db..f6e5445ece 100644 --- a/Resources/Locale/en-US/psionics/psionic-powers.ftl +++ b/Resources/Locale/en-US/psionics/psionic-powers.ftl @@ -1,5 +1,6 @@ generic-power-initialization-feedback = I Awaken. already-casting = I cannot channel more than one power at a time. +no-mana = I cannot channel enough power. # Dispel dispel-power-description = Dispel summoned entities such as familiars or forcewalls. @@ -139,3 +140,12 @@ examine-mindbroken-message = psionic-roll-failed = For a moment, my consciousness expands, yet I feel that it is not enough. entity-anomaly-no-grid = There is nowhere for me to conjure beings. power-overwhelming-power-feedback = {CAPITALIZE($entity)} wields a vast connection to the noösphere + +# Shadowkin ShadeSkip +action-description-shadowkin-shadeskip = Aaramrra! + +# DarkSwap +action-name-darkswap = DarkSwap +action-description-darkswap = Mmra Mamm! + +ethereal-pickup-fail = My hand sizzles as it passes through... \ No newline at end of file diff --git a/Resources/Locale/en-US/research/technologies.ftl b/Resources/Locale/en-US/research/technologies.ftl index 684a08dd9a..fe7293d848 100644 --- a/Resources/Locale/en-US/research/technologies.ftl +++ b/Resources/Locale/en-US/research/technologies.ftl @@ -43,6 +43,7 @@ research-technology-basic-xenoarcheology = Basic XenoArcheology research-technology-alternative-research = Alternative Research research-technology-magnets-tech = Localized Magnetism research-technology-advanced-parts = Advanced Parts +research-technology-advanced-bluespace = Advanced Bluespace Research research-technology-anomaly-harnessing = Anomaly Core Harnessing research-technology-grappling = Grappling research-technology-abnormal-artifact-manipulation = Artifact Recycling diff --git a/Resources/Locale/en-US/species/shadowkin.ftl b/Resources/Locale/en-US/species/shadowkin.ftl new file mode 100644 index 0000000000..ebc56487b7 --- /dev/null +++ b/Resources/Locale/en-US/species/shadowkin.ftl @@ -0,0 +1,15 @@ +shadowkin-power-examined-other = {CAPITALIZE(SUBJECT($target))} seems to be {$powerType}. +shadowkin-power-examined-self = I have {$power}/{$powerMax} energy, I am {$powerType}. + +shadowkin-power-5 = energetic +shadowkin-power-4 = great +shadowkin-power-3 = good +shadowkin-power-2 = okay +shadowkin-power-1 = exhausted +shadowkin-power-0 = drained + +examine-mindbroken-shadowkin-message = {CAPITALIZE($entity)} seems to be a blackeye. + +identity-eye-shadowkin = {$color}-eye + +shadowkin-blackeye = I feel my power draining away... \ No newline at end of file diff --git a/Resources/Locale/en-US/species/species.ftl b/Resources/Locale/en-US/species/species.ftl index 6c40c45404..9278267cc4 100644 --- a/Resources/Locale/en-US/species/species.ftl +++ b/Resources/Locale/en-US/species/species.ftl @@ -11,3 +11,4 @@ species-name-moth = Moth Person species-name-skeleton = Skeleton species-name-vox = Vox species-name-ipc = IPC +species-name-shadowkin = Shadowkin \ No newline at end of file diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index cfd53bbde3..d5c993e299 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -400,3 +400,6 @@ trait-description-CyberEyesOmni = This expensive implant provides the combined benefits of a SecHud, MedHud, and a DiagHud. Note that this augmentation is considered Contraband for anyone not under the employ of station Security personel, and may be disabled by your employer before dispatch to the station. + +trait-name-ShadowkinBlackeye = Blackeye +trait-description-ShadowkinBlackeye = You lose your special Shadowkin powers, in return for some points. diff --git a/Resources/Prototypes/Actions/psionics.yml b/Resources/Prototypes/Actions/psionics.yml index 138823313a..70f7846088 100644 --- a/Resources/Prototypes/Actions/psionics.yml +++ b/Resources/Prototypes/Actions/psionics.yml @@ -4,18 +4,18 @@ description: action-description-dispel noSpawn: true components: - - type: EntityTargetAction - icon: Interface/VerbIcons/dispel.png - useDelay: 45 - checkCanAccess: false - range: 6 - itemIconStyle: BigAction - canTargetSelf: false - blacklist: - components: - - PsionicInsulation - - Mindbroken - event: !type:DispelPowerActionEvent + - type: EntityTargetAction + icon: Interface/VerbIcons/dispel.png + useDelay: 45 + checkCanAccess: false + range: 6 + itemIconStyle: BigAction + canTargetSelf: false + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:DispelPowerActionEvent - type: entity id: ActionMassSleep @@ -23,13 +23,13 @@ description: action-description-mass-sleep noSpawn: true components: - - type: WorldTargetAction - icon: Interface/VerbIcons/mass_sleep.png - useDelay: 60 - checkCanAccess: false - range: 8 - itemIconStyle: BigAction - event: !type:MassSleepPowerActionEvent + - type: WorldTargetAction + icon: Interface/VerbIcons/mass_sleep.png + useDelay: 60 + checkCanAccess: false + range: 8 + itemIconStyle: BigAction + event: !type:MassSleepPowerActionEvent - type: entity id: ActionMindSwap @@ -37,17 +37,17 @@ description: action-description-mind-swap noSpawn: true components: - - type: EntityTargetAction - icon: Interface/VerbIcons/mind_swap.png - useDelay: 240 - checkCanAccess: false - range: 8 - itemIconStyle: BigAction - blacklist: - components: - - PsionicInsulation - - Mindbroken - event: !type:MindSwapPowerActionEvent + - type: EntityTargetAction + icon: Interface/VerbIcons/mind_swap.png + useDelay: 240 + checkCanAccess: false + range: 8 + itemIconStyle: BigAction + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:MindSwapPowerActionEvent - type: entity id: ActionMindSwapReturn @@ -55,11 +55,11 @@ description: action-description-mind-swap-return noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/mind_swap_return.png - useDelay: 20 - checkCanInteract: false - event: !type:MindSwapPowerReturnActionEvent + - type: InstantAction + icon: Interface/VerbIcons/mind_swap_return.png + useDelay: 20 + checkCanInteract: false + event: !type:MindSwapPowerReturnActionEvent - type: entity id: ActionNoosphericZap @@ -67,16 +67,16 @@ description: action-description-noospheric-zap noSpawn: true components: - - type: EntityTargetAction - icon: Interface/VerbIcons/noospheric_zap.png - useDelay: 100 - range: 5 - itemIconStyle: BigAction - blacklist: - components: - - PsionicInsulation - - Mindbroken - event: !type:NoosphericZapPowerActionEvent + - type: EntityTargetAction + icon: Interface/VerbIcons/noospheric_zap.png + useDelay: 100 + range: 5 + itemIconStyle: BigAction + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:NoosphericZapPowerActionEvent - type: entity id: ActionPyrokinesis @@ -84,13 +84,13 @@ description: action-description-pyrokinesis noSpawn: true components: - - type: EntityTargetAction - icon: Interface/VerbIcons/pyrokinesis.png - useDelay: 50 - range: 6 - checkCanAccess: false - itemIconStyle: BigAction - event: !type:PyrokinesisPowerActionEvent + - type: EntityTargetAction + icon: Interface/VerbIcons/pyrokinesis.png + useDelay: 50 + range: 6 + checkCanAccess: false + itemIconStyle: BigAction + event: !type:PyrokinesisPowerActionEvent - type: entity id: ActionMetapsionic @@ -98,10 +98,10 @@ description: action-description-metapsionic noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/metapsionic.png - useDelay: 45 - event: !type:MetapsionicPowerActionEvent + - type: InstantAction + icon: Interface/VerbIcons/metapsionic.png + useDelay: 45 + event: !type:MetapsionicPowerActionEvent - type: entity id: ActionPsionicRegeneration @@ -109,10 +109,10 @@ description: action-description-psionic-regeneration noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/psionic_regeneration.png - useDelay: 120 - event: !type:PsionicRegenerationPowerActionEvent + - type: InstantAction + icon: Interface/VerbIcons/psionic_regeneration.png + useDelay: 120 + event: !type:PsionicRegenerationPowerActionEvent - type: entity id: ActionTelegnosis @@ -120,10 +120,10 @@ description: action-description-telegnosis noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/telegnosis.png - useDelay: 150 - event: !type:TelegnosisPowerActionEvent + - type: InstantAction + icon: Interface/VerbIcons/telegnosis.png + useDelay: 150 + event: !type:TelegnosisPowerActionEvent - type: entity id: ActionPsionicInvisibility @@ -131,10 +131,10 @@ description: action-description-psionic-invisibility noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/psionic_invisibility.png - useDelay: 120 - event: !type:PsionicInvisibilityPowerActionEvent + - type: InstantAction + icon: Interface/VerbIcons/psionic_invisibility.png + useDelay: 120 + event: !type:PsionicInvisibilityPowerActionEvent - type: entity id: ActionPsionicInvisibilityUsed @@ -142,9 +142,9 @@ description: action-description-psionic-invisibility-off noSpawn: true components: - - type: InstantAction - icon: Interface/VerbIcons/psionic_invisibility_off.png - event: !type:RemovePsionicInvisibilityOffPowerActionEvent + - type: InstantAction + icon: Interface/VerbIcons/psionic_invisibility_off.png + event: !type:RemovePsionicInvisibilityOffPowerActionEvent - type: entity id: ActionHealingWord @@ -152,36 +152,36 @@ description: action-description-healing-word noSpawn: true components: - - type: EntityTargetAction - icon: { sprite : Interface/Actions/psionics.rsi, state: healing_word } - useDelay: 10 - checkCanAccess: false - range: 6 - itemIconStyle: BigAction - canTargetSelf: true - blacklist: - components: - - PsionicInsulation - - Mindbroken - event: !type:PsionicHealOtherPowerActionEvent - healingAmount: - groups: # These all get divided by the number of damage types in the group. So they're all -2.5. - Genetic: -2.5 - Toxin: -5 - Airloss: -5 - Brute: -7.5 - Burn: -10 - rotReduction: 10 - useDelay: 1 - doRevive: true - powerName: Healing Word - popupText: healing-word-begin - playSound: true - minGlimmer: 2 - maxGlimmer: 4 - glimmerSoundThreshold: 100 - glimmerPopupThreshold: 200 - glimmerDoAfterVisibilityThreshold: 70 + - type: EntityTargetAction + icon: { sprite: Interface/Actions/psionics.rsi, state: healing_word } + useDelay: 10 + checkCanAccess: false + range: 6 + itemIconStyle: BigAction + canTargetSelf: true + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:PsionicHealOtherPowerActionEvent + healingAmount: + groups: # These all get divided by the number of damage types in the group. So they're all -2.5. + Genetic: -2.5 + Toxin: -5 + Airloss: -5 + Brute: -7.5 + Burn: -10 + rotReduction: 10 + useDelay: 1 + doRevive: true + powerName: Healing Word + popupText: healing-word-begin + playSound: true + minGlimmer: 2 + maxGlimmer: 4 + glimmerSoundThreshold: 100 + glimmerPopupThreshold: 200 + glimmerDoAfterVisibilityThreshold: 70 - type: entity id: ActionRevivify @@ -189,38 +189,38 @@ description: action-description-revivify noSpawn: true components: - - type: EntityTargetAction - icon: { sprite : Interface/Actions/psionics.rsi, state: revivify } - useDelay: 120 - checkCanAccess: false - range: 2 - itemIconStyle: BigAction - canTargetSelf: false - blacklist: - components: - - PsionicInsulation - - Mindbroken - event: !type:PsionicHealOtherPowerActionEvent - healingAmount: - # These all get divided by the number of damage types in the group. So they're all -15 - # Additionally, they're multiplied by the caster's Amplification, which, - # assuming this is the only power they have, the multiplier is between 2.9-3.9 - groups: - Genetic: -15 - Toxin: -30 - Airloss: -60 # Except airloss, which heals 30 per type - Brute: -45 - Burn: -60 - rotReduction: 60 - doRevive: true - powerName: Revivify - popupText: revivify-begin - playSound: true - minGlimmer: 10 # These also get multiplied by caster stats. So, - maxGlimmer: 15 # keeping in mind the ~3.5x multiplier, this spikes glimmer by as much as 60 points. - glimmerSoundThreshold: 50 - glimmerPopupThreshold: 100 - glimmerDoAfterVisibilityThreshold: 35 + - type: EntityTargetAction + icon: { sprite: Interface/Actions/psionics.rsi, state: revivify } + useDelay: 120 + checkCanAccess: false + range: 2 + itemIconStyle: BigAction + canTargetSelf: false + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:PsionicHealOtherPowerActionEvent + healingAmount: + # These all get divided by the number of damage types in the group. So they're all -15 + # Additionally, they're multiplied by the caster's Amplification, which, + # assuming this is the only power they have, the multiplier is between 2.9-3.9 + groups: + Genetic: -15 + Toxin: -30 + Airloss: -60 # Except airloss, which heals 30 per type + Brute: -45 + Burn: -60 + rotReduction: 60 + doRevive: true + powerName: Revivify + popupText: revivify-begin + playSound: true + minGlimmer: 10 # These also get multiplied by caster stats. So, + maxGlimmer: 15 # keeping in mind the ~3.5x multiplier, this spikes glimmer by as much as 60 points. + glimmerSoundThreshold: 50 + glimmerPopupThreshold: 100 + glimmerDoAfterVisibilityThreshold: 35 - type: entity id: ActionShadeskip @@ -263,30 +263,78 @@ description: action-description-telekinetic-pulse noSpawn: true components: - - type: InstantAction - icon: { sprite : Interface/Actions/psionics.rsi, state: telekinetic_pulse } - useDelay: 45 - checkCanInteract: false - event: !type:AnomalyPowerActionEvent - settings: - powerName: "Telekinetic Pulse" - overchargeFeedback: "shadeskip-overcharge-feedback" # The text behind this is fine. - overchargeCooldown: 120 - overchargeRecoil: - groups: - Burn: -100 #This will be divided by the caster's Dampening. - minGlimmer: 6 - maxGlimmer: 8 - doSupercritical: false - entitySpawnEntries: - - settings: - spawnOnPulse: true - minAmount: 1 - maxAmount: 1 - maxRange: 0.5 - spawns: - - EffectFlashTelekineticPulse - gravity: - maxThrowRange: 3 - maxThrowStrength: 5 - spaceRange: 3 + - type: InstantAction + icon: { sprite: Interface/Actions/psionics.rsi, state: telekinetic_pulse } + useDelay: 45 + checkCanInteract: false + event: !type:AnomalyPowerActionEvent + settings: + powerName: "Telekinetic Pulse" + overchargeFeedback: "shadeskip-overcharge-feedback" # The text behind this is fine. + overchargeCooldown: 120 + overchargeRecoil: + groups: + Burn: -100 #This will be divided by the caster's Dampening. + minGlimmer: 6 + maxGlimmer: 8 + doSupercritical: false + entitySpawnEntries: + - settings: + spawnOnPulse: true + minAmount: 1 + maxAmount: 1 + maxRange: 0.5 + spawns: + - EffectFlashTelekineticPulse + gravity: + maxThrowRange: 3 + maxThrowStrength: 5 + spaceRange: 3 + +- type: entity + id: ActionShadowkinShadeskip + name: action-name-shadeskip + description: action-description-shadowkin-shadeskip + noSpawn: true + components: + - type: InstantAction + icon: { sprite: Interface/Actions/shadowkin_icons.rsi, state: shadeskip } + useDelay: 10 + checkCanInteract: false + event: !type:AnomalyPowerActionEvent + settings: + powerName: "Shadowkin-Shadeskip" + manaCost: 25 + checkInsulation: false + minGlimmer: 0 + maxGlimmer: 0 + doSupercritical: false + entitySpawnEntries: + - settings: + spawnOnPulse: true + minAmount: 5 + maxAmount: 10 + maxRange: 2.5 + spawns: + - ShadowkinShadow + - settings: + spawnOnPulse: true + minAmount: 1 + maxAmount: 1 + maxRange: 0.5 + spawns: + - EffectFlashShadowkinShadeskip + +- type: entity + id: ActionDarkSwap + name: action-name-darkswap + description: action-description-darkswap + noSpawn: true + components: + - type: InstantAction + icon: { sprite: Interface/Actions/shadowkin_icons.rsi, state: darkswap } + useDelay: 10 + checkCanInteract: false + event: !type:DarkSwapActionEvent + manaCost: 100 + checkInsulation: false diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index f81def7f7c..a7144cdda5 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -304,6 +304,18 @@ icon: { sprite: Clothing/Head/Hats/pyjamasyndicatered.rsi, state: icon } event: !type:SleepActionEvent +- type: entity + id: ShadowkinActionSleep + name: action-name-shadowkin-rest + description: action-description-shadowkin-rest + noSpawn: true + components: + - type: InstantAction + checkCanInteract: false + checkConsciousness: false + icon: { sprite: Interface/Actions/shadowkin_icons.rsi, state: rest } + event: !type:SleepActionEvent + - type: entity id: ActionWake name: Wake up diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index 1e0a8a8c62..bb0c47f48f 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -7,6 +7,7 @@ - category: Health - category: Mood - category: Stamina + - alertType: ShadowkinPower - alertType: SuitPower - category: Internals - alertType: Fire diff --git a/Resources/Prototypes/Alerts/shadowkin.yml b/Resources/Prototypes/Alerts/shadowkin.yml new file mode 100644 index 0000000000..66d41351ba --- /dev/null +++ b/Resources/Prototypes/Alerts/shadowkin.yml @@ -0,0 +1,23 @@ +- type: alert + id: ShadowkinPower + icons: + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power0 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power1 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power2 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power3 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power4 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power5 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power6 + - sprite: /Textures/Interface/Alerts/shadowkin_power.rsi + state: power7 + name: alerts-shadowkin-power-name + description: alerts-shadowkin-power-desc + minSeverity: 0 + maxSeverity: 7 \ No newline at end of file diff --git a/Resources/Prototypes/Body/Organs/shadowkin.yml b/Resources/Prototypes/Body/Organs/shadowkin.yml new file mode 100644 index 0000000000..695ddec1ab --- /dev/null +++ b/Resources/Prototypes/Body/Organs/shadowkin.yml @@ -0,0 +1,113 @@ +- type: entity + id: OrganShadowkinBrain + parent: OrganHumanBrain + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: brain + +- type: entity + id: OrganShadowkinEyes + parent: OrganHumanEyes + description: I see beyond anything you ever will! + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + layers: + - state: eyes + +- type: entity + id: OrganShadowkinEars + parent: OrganHumanEars + description: Hey, listen! + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: ears + +- type: entity + id: OrganShadowkinTongue + parent: OrganHumanTongue + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: tongue + + +- type: entity + id: OrganShadowkinAppendix + parent: OrganHumanAppendix + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + layers: + - state: appendix + + +- type: entity + id: OrganShadowkinHeart + parent: OrganHumanHeart + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: heart + - type: Metabolizer + maxReagents: 2 + metabolizerTypes: [Shadowkin] + groups: + - id: Medicine + - id: Poison + - id: Narcotic + +- type: entity + id: OrganShadowkinStomach + parent: OrganHumanStomach + description: '"Yummy!", says the stomach, although you are unable to hear it.' + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: stomach + - type: SolutionContainerManager + solutions: + stomach: + maxVol: 40 + food: + maxVol: 5 + reagents: + - ReagentId: UncookedAnimalProteins + Quantity: 5 + - type: Metabolizer + maxReagents: 3 + metabolizerTypes: [Shadowkin] + groups: + - id: Food + - id: Drink + +- type: entity + id: OrganShadowkinLiver + parent: OrganHumanLiver + description: "Live 'er? I hardly know 'er!" + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + state: liver + - type: Metabolizer + maxReagents: 1 + metabolizerTypes: [Shadowkin] + groups: + - id: Alcohol + rateModifier: 0.1 + +- type: entity + id: OrganShadowkinKidneys + parent: OrganHumanKidneys + description: Give the kid their knees back, please, this is the third time this week. + components: + - type: Sprite + sprite: Mobs/Species/Shadowkin/organs.rsi + layers: + - state: kidneys + - type: Metabolizer + maxReagents: 5 + metabolizerTypes: [Shadowkin] + removeEmpty: true \ No newline at end of file diff --git a/Resources/Prototypes/Body/Parts/shadowkin.yml b/Resources/Prototypes/Body/Parts/shadowkin.yml new file mode 100644 index 0000000000..0ddff93443 --- /dev/null +++ b/Resources/Prototypes/Body/Parts/shadowkin.yml @@ -0,0 +1,155 @@ +- type: entity + id: PartShadowkin + parent: BaseItem + name: "Shadowkin body part" + abstract: true + components: + - type: Sprite + netsync: false + sprite: Mobs/Species/Shadowkin/parts.rsi + - type: Icon + sprite: Mobs/Species/Shadowkin/parts.rsi + - type: Damageable + damageContainer: Biological + - type: BodyPart + - type: ContainerContainer + containers: + bodypart: !type:Container + ents: [] + +- type: entity + id: TorsoShadowkin + name: "Shadowkin torso" + parent: PartShadowkin + components: + - type: Sprite + state: "torso_m" + - type: Icon + state: "torso_m" + - type: BodyPart + partType: Torso + +- type: entity + id: HeadShadowkin + name: "Shadowkin head" + parent: PartShadowkin + components: + - type: Sprite + state: "head_m" + - type: Icon + state: "head_m" + - type: BodyPart + partType: Head + - type: Input + context: "ghost" + - type: MovementSpeedModifier + baseWalkSpeed: 0 + baseSprintSpeed: 0 + - type: InputMover + - type: GhostOnMove + +- type: entity + id: LeftArmShadowkin + name: "left Shadowkin arm" + parent: PartShadowkin + components: + - type: Sprite + state: "l_arm" + - type: Icon + state: "l_arm" + - type: BodyPart + partType: Arm + symmetry: Left + +- type: entity + id: RightArmShadowkin + name: "right Shadowkin arm" + parent: PartShadowkin + components: + - type: Sprite + state: "r_arm" + - type: Icon + state: "r_arm" + - type: BodyPart + partType: Arm + symmetry: Right + +- type: entity + id: LeftHandShadowkin + name: "left Shadowkin hand" + parent: PartShadowkin + components: + - type: Sprite + state: "l_hand" + - type: Icon + state: "l_hand" + - type: BodyPart + partType: Hand + symmetry: Left + +- type: entity + id: RightHandShadowkin + name: "right Shadowkin hand" + parent: PartShadowkin + components: + - type: Sprite + state: "r_hand" + - type: Icon + state: "r_hand" + - type: BodyPart + partType: Hand + symmetry: Right + +- type: entity + id: LeftLegShadowkin + name: "left Shadowkin leg" + parent: PartShadowkin + components: + - type: Sprite + state: "l_leg" + - type: Icon + state: "l_leg" + - type: BodyPart + partType: Leg + symmetry: Left + - type: MovementBodyPart + +- type: entity + id: RightLegShadowkin + name: "right Shadowkin leg" + parent: PartShadowkin + components: + - type: Sprite + state: "r_leg" + - type: Icon + state: "r_leg" + - type: BodyPart + partType: Leg + symmetry: Right + - type: MovementBodyPart + +- type: entity + id: LeftFootShadowkin + name: "left Shadowkin foot" + parent: PartShadowkin + components: + - type: Sprite + state: "l_foot" + - type: Icon + state: "l_foot" + - type: BodyPart + partType: Foot + symmetry: Left + +- type: entity + id: RightFootShadowkin + name: "right Shadowkin foot" + parent: PartShadowkin + components: + - type: Sprite + state: "r_foot" + - type: Icon + state: "r_foot" + - type: BodyPart + partType: Foot + symmetry: Right diff --git a/Resources/Prototypes/Body/Prototypes/shadowkin.yml b/Resources/Prototypes/Body/Prototypes/shadowkin.yml new file mode 100644 index 0000000000..dddad7bdb5 --- /dev/null +++ b/Resources/Prototypes/Body/Prototypes/shadowkin.yml @@ -0,0 +1,48 @@ +- type: body + id: Shadowkin + name: "Shadowkin" + root: torso + slots: + head: + part: HeadShadowkin + connections: + - torso + organs: + brain: OrganShadowkinBrain + eyes: OrganShadowkinEyes + torso: + part: TorsoShadowkin + connections: + - left arm + - right arm + - left leg + - right leg + organs: + heart: OrganShadowkinHeart + stomach: OrganShadowkinStomach + liver: OrganShadowkinLiver + kidneys: OrganShadowkinKidneys + right arm: + part: RightArmShadowkin + connections: + - right hand + left arm: + part: LeftArmShadowkin + connections: + - left hand + right hand: + part: RightHandShadowkin + left hand: + part: LeftHandShadowkin + right leg: + part: RightLegShadowkin + connections: + - right foot + left leg: + part: LeftLegShadowkin + connections: + - left foot + right foot: + part: RightFootShadowkin + left foot: + part: LeftFootShadowkin \ No newline at end of file diff --git a/Resources/Prototypes/Chemistry/metabolizer_types.yml b/Resources/Prototypes/Chemistry/metabolizer_types.yml index b49fad6c27..80f69893c6 100644 --- a/Resources/Prototypes/Chemistry/metabolizer_types.yml +++ b/Resources/Prototypes/Chemistry/metabolizer_types.yml @@ -52,3 +52,7 @@ - type: metabolizerType id: LiquorLifeline name: metabolizer-type-liquorlifeline + +- type: metabolizerType + id: Shadowkin + name: metabolizer-type-shadowkin diff --git a/Resources/Prototypes/Damage/modifier_sets.yml b/Resources/Prototypes/Damage/modifier_sets.yml index 39c956df62..8cca467f69 100644 --- a/Resources/Prototypes/Damage/modifier_sets.yml +++ b/Resources/Prototypes/Damage/modifier_sets.yml @@ -358,6 +358,20 @@ Piercing: 0.6 Holy: 1.5 +- type: damageModifierSet + id: Shadowkin + coefficients: + Blunt: 0.95 + Slash: 1.2 + Piercing: 1.1 + Asphyxiation: 0 + Cold: 0.75 + Heat: 1.2 + Cellular: 0.25 + Bloodloss: 1.35 + Shock: 1.25 + Radiation: 1.3 + - type: damageModifierSet id: DermalArmor coefficients: diff --git a/Resources/Prototypes/Datasets/Names/shadowkin.yml b/Resources/Prototypes/Datasets/Names/shadowkin.yml new file mode 100644 index 0000000000..4dbf4c5dc5 --- /dev/null +++ b/Resources/Prototypes/Datasets/Names/shadowkin.yml @@ -0,0 +1,70 @@ +# Names for the shadowkin, +# Shadowkin names are descriptive of +# Their Primary Emotion, +# A State of Being, +# Or past Memories. + +- type: dataset + id: names_shadowkin + values: + # Mar + # - Mar + + # Sad + - Fragile + - Heartbreak + - Inferior + - Lone + - Lonesome + - Loss + - Solitary + - Solitude + - Sorrow + - Shade + + # Angry + - Fear + - Fearful + - Fury + - Pain + - Rage + - Rush + - Wrath + + # Happy + - Calm + - Content + - Contented + - Happy + - Hope + - Joyous + - Lovely + - Peace + - Peaceful + - Quiet + - Serene + - Serenity + - Tranquil + - Tranquility + + # Memory + - Dillusioned + - Forgotten + - Focusless + - Lost + - Memory + - Recollection + - Remembrance + - Reminisce + - Reminiscence + + # Other + - Apathy + - Collected + - Curiosity + - Free + - Interest + - Jax # White eye (jack of all trades) :) + - Still + - Unbound + - Shadows \ No newline at end of file diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml index 2c8b01553a..4db83e9ab1 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Player/humanoid.yml @@ -10,6 +10,8 @@ - type: randomHumanoidSettings id: SyndicateListener + speciesBlacklist: + - Shadowkin components: - type: Loadout prototypes: [SyndicateListenerGear] @@ -34,6 +36,8 @@ - type: randomHumanoidSettings id: Mobster randomizeName: false + speciesBlacklist: + - Shadowkin components: - type: GhostRole name: Mobster @@ -65,6 +69,8 @@ - type: randomHumanoidSettings id: MobsterAlt randomizeName: false + speciesBlacklist: + - Shadowkin components: - type: GhostRole name: Mobster diff --git a/Resources/Prototypes/DeltaV/Traits/altvision.yml b/Resources/Prototypes/DeltaV/Traits/altvision.yml index 390e14d4ad..54fec7df8b 100644 --- a/Resources/Prototypes/DeltaV/Traits/altvision.yml +++ b/Resources/Prototypes/DeltaV/Traits/altvision.yml @@ -7,6 +7,7 @@ species: - Vulpkanin - Harpy + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -23,6 +24,7 @@ species: - Vulpkanin - Harpy + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: diff --git a/Resources/Prototypes/DeltaV/Voice/speech_emote_sounds.yml b/Resources/Prototypes/DeltaV/Voice/speech_emote_sounds.yml index 119134dcf1..ad91d9c2d2 100644 --- a/Resources/Prototypes/DeltaV/Voice/speech_emote_sounds.yml +++ b/Resources/Prototypes/DeltaV/Voice/speech_emote_sounds.yml @@ -60,6 +60,10 @@ collection: VulpkaninHowls Weh: collection: Weh + Mars: + collection: Mars + Wurble: + collection: Wurble - type: emoteSounds id: MaleVulpkanin diff --git a/Resources/Prototypes/Entities/Clothing/Back/specific.yml b/Resources/Prototypes/Entities/Clothing/Back/specific.yml index fcdecffc8f..e2932537e9 100644 --- a/Resources/Prototypes/Entities/Clothing/Back/specific.yml +++ b/Resources/Prototypes/Entities/Clothing/Back/specific.yml @@ -61,3 +61,25 @@ solution: tank - type: ExaminableSolution solution: tank + +- type: entity + parent: Clothing + id: ClothingBackpackEtherealTeleporter + name: ethereal teleporter + description: Originally created while several research facilities were experimenting on Shadowkin, this backpack allows the wearer to jump the gap between the "normal" dimension and The Dark. + components: + - type: Tag + tags: + - WhitelistChameleon + - type: Sprite + sprite: Clothing/Back/etherealteleporter.rsi + state: icon + - type: Item + size: Ginormous + - type: Clothing + slots: BACK + sprite: Clothing/Back/etherealteleporter.rsi + # TODO: Uncomment this when ClothingGrantPsionicPower is fixed and back working. + # - type: ClothingGrantPsionicPower + # power: DarkSwapPower + # - type: Psionic diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml index 9e47a685f0..f50b0dbca5 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml @@ -265,3 +265,15 @@ sprite: Clothing/Eyes/Glasses/meson.rsi - type: Clothing sprite: Clothing/Eyes/Glasses/meson.rsi + +- type: entity + parent: ClothingEyesBase + id: ClothingEyesGlassesEthereal + name: ethereal goggles + description: An unusual pair of goggles developed during a time of inhumane experimentation involving Shadowkin. They are designed to allow the wearer to peer into The Dark. + components: + - type: Sprite + sprite: Clothing/Eyes/Glasses/etherealgoogles.rsi + - type: Clothing + sprite: Clothing/Eyes/Glasses/etherealgoogles.rsi + - type: ShowEthereal \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml index f908465f7a..b5169a9cf6 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml @@ -428,3 +428,20 @@ sprite: Clothing/OuterClothing/Misc/unathirobe.rsi - type: Clothing sprite: Clothing/OuterClothing/Misc/unathirobe.rsi + +- type: entity + parent: ClothingOuterBase + id: ClothingOuterShadowkinRestraints + name: shadowkin restraints + description: One of the first creations after finding Shadowkin, these were used to contain the Shadowkin during research so they didn't teleport away. + components: + - type: Sprite + sprite: Clothing/OuterClothing/Misc/shadowkinrestraints.rsi + - type: Clothing + sprite: Clothing/OuterClothing/Misc/shadowkinrestraints.rsi + equipDelay: 0.5 + unequipDelay: 10 + - type: ShadowkinCuff + - type: GuideHelp + guides: + - Shadowkin \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Effects/bluespace_flash.yml b/Resources/Prototypes/Entities/Effects/bluespace_flash.yml index a0b3f81abc..e9ad08f015 100644 --- a/Resources/Prototypes/Entities/Effects/bluespace_flash.yml +++ b/Resources/Prototypes/Entities/Effects/bluespace_flash.yml @@ -38,4 +38,46 @@ lifetime: 1 - type: EmitSoundOnSpawn sound: - path: /Audio/Effects/Lightning/lightningbolt.ogg \ No newline at end of file + path: /Audio/Effects/Lightning/lightningbolt.ogg + +- type: entity + id: EffectFlashShadowkinShadeskip + noSpawn: true + components: + - type: PointLight + radius: 5 + energy: 3.5 + color: "#7100bd" + - type: TimedDespawn + lifetime: 3 + - type: EmitSoundOnSpawn + sound: + path: /Audio/Effects/Shadowkin/shadeskip.ogg + +- type: entity + id: EffectFlashShadowkinDarkSwapOn + noSpawn: true + components: + - type: PointLight + radius: 5 + energy: 3.5 + color: "#7100bd" + - type: TimedDespawn + lifetime: 3 + - type: EmitSoundOnSpawn + sound: + path: /Audio/Effects/Shadowkin/darkswapon.ogg + +- type: entity + id: EffectFlashShadowkinDarkSwapOff + noSpawn: true + components: + - type: PointLight + radius: 5 + energy: 3.5 + color: "#7100bd" + - type: TimedDespawn + lifetime: 3 + - type: EmitSoundOnSpawn + sound: + path: /Audio/Effects/Shadowkin/darkswapoff.ogg \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml index cad3e77962..6197f82030 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/reptilian.yml @@ -208,7 +208,7 @@ id: LizardChestTiger bodyPart: Chest markingCategory: Chest - speciesRestriction: [Reptilian] + speciesRestriction: [Reptilian, Shadowkin] sprites: - sprite: Mobs/Customization/reptilian_parts.rsi state: body_tiger diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/shadowkin.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/shadowkin.yml new file mode 100644 index 0000000000..f86852a987 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/shadowkin.yml @@ -0,0 +1,83 @@ +# Ears + +- type: marking + id: EarsShadowkin + bodyPart: HeadTop + markingCategory: HeadTop + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/ears.rsi + state: shadowkin + +- type: marking + id: EarsShadowkinStriped + bodyPart: HeadTop + markingCategory: HeadTop + speciesRestriction: [Shadowkin] + coloring: + default: + type: + !type:SkinColoring + layers: + shadowkin_stripes: + type: + !type:SimpleColoring + color: "#FFFFFF" + sprites: + - sprite: Mobs/Customization/Shadowkin/ears.rsi + state: shadowkin + - sprite: Mobs/Customization/Shadowkin/ears.rsi + state: shadowkin_stripes + +# Tails + +- type: marking + id: TailShadowkin + bodyPart: Tail + markingCategory: Tail + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/tails64x32.rsi + state: shadowkin + +- type: marking + id: TailShadowkinBig + bodyPart: Tail + markingCategory: Tail + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/tails64x32.rsi + state: shadowkin_big + +- type: marking + id: TailShadowkinBigFluff + bodyPart: Tail + markingCategory: Tail + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/tails64x32.rsi + state: shadowkin_big_fluff + +- type: marking + id: TailShadowkinShorter + bodyPart: Tail + markingCategory: Tail + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/tails32x32.rsi + state: shadowkin_shorter + +- type: marking + id: TailShadowkinMedium + bodyPart: Tail + markingCategory: Tail + speciesRestriction: [Shadowkin] + forcedColoring: true + sprites: + - sprite: Mobs/Customization/Shadowkin/tails32x32.rsi + state: shadowkin_medium \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/slime.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/slime.yml index 57b25798e7..0bff234747 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/slime.yml @@ -2,7 +2,7 @@ id: SlimeGradientLeftArm bodyPart: LArm markingCategory: LeftArm - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_l_arm @@ -11,7 +11,7 @@ id: SlimeGradientRightArm bodyPart: RArm markingCategory: RightArm - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_r_arm @@ -20,7 +20,7 @@ id: SlimeGradientLeftLeg bodyPart: LLeg markingCategory: LeftLeg - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_l_leg @@ -29,7 +29,7 @@ id: SlimeGradientRightLeg bodyPart: RLeg markingCategory: RightLeg - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_r_leg @@ -38,7 +38,7 @@ id: SlimeGradientLeftFoot bodyPart: LFoot markingCategory: LeftFoot - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_l_foot @@ -47,7 +47,7 @@ id: SlimeGradientRightFoot bodyPart: RFoot markingCategory: RightFoot - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_r_foot @@ -56,7 +56,7 @@ id: SlimeGradientLeftHand bodyPart: LHand markingCategory: LeftHand - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_l_hand @@ -65,7 +65,7 @@ id: SlimeGradientRightHand bodyPart: RHand markingCategory: RightHand - speciesRestriction: [SlimePerson] + speciesRestriction: [SlimePerson, Shadowkin] sprites: - sprite: Mobs/Customization/slime_parts.rsi state: gradient_r_hand diff --git a/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml b/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml index 8830708539..93a16fcfd3 100644 --- a/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml +++ b/Resources/Prototypes/Entities/Mobs/Customization/Markings/tattoos.yml @@ -2,7 +2,7 @@ id: TattooHiveChest bodyPart: Chest markingCategory: Chest - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -16,7 +16,7 @@ id: TattooNightlingChest bodyPart: Chest markingCategory: Chest - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -30,7 +30,7 @@ id: TattooSilverburghLeftLeg bodyPart: LLeg markingCategory: LeftLeg - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -44,7 +44,7 @@ id: TattooSilverburghRightLeg bodyPart: RLeg markingCategory: RightLeg - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -58,7 +58,7 @@ id: TattooCampbellLeftArm bodyPart: LArm markingCategory: LeftArm - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -72,7 +72,7 @@ id: TattooCampbellRightArm bodyPart: RArm markingCategory: RightArm - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -86,7 +86,7 @@ id: TattooCampbellLeftLeg bodyPart: LLeg markingCategory: LeftLeg - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: @@ -100,7 +100,7 @@ id: TattooCampbellRightLeg bodyPart: RLeg markingCategory: RightLeg - speciesRestriction: [Human, Dwarf, Felinid, Oni] # Delta V - Felinid, Oni + speciesRestriction: [Human, Dwarf, Felinid, Oni, Shadowkin] # Delta V - Felinid, Oni coloring: default: type: diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index c690bbb311..dce408ed82 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -10,6 +10,7 @@ - PsionicInvisibility - Ghost - Normal + - Ethereal - type: ContentEye maxZoom: 8.916104, 8.916104 - type: Tag diff --git a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml index e96633dde8..a4bfe2289b 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml @@ -21,6 +21,8 @@ - type: randomHumanoidSettings id: DeathSquad randomizeName: false + speciesBlacklist: + - Shadowkin components: - type: MindShield - type: GhostRole @@ -59,6 +61,8 @@ - type: randomHumanoidSettings id: ERTLeader randomizeName: false + speciesBlacklist: + - Shadowkin components: - type: MindShield - type: GhostRole @@ -486,6 +490,8 @@ - type: randomHumanoidSettings id: CBURNAgent + speciesBlacklist: + - Shadowkin components: - type: MindShield - type: Loadout @@ -516,6 +522,8 @@ - type: randomHumanoidSettings id: CentcomOfficial + speciesBlacklist: + - Shadowkin components: - type: MindShield - type: GhostRole @@ -544,6 +552,8 @@ - type: randomHumanoidSettings id: SyndicateAgent + speciesBlacklist: + - Shadowkin components: - type: Loadout prototypes: [SyndicateOperativeGearExtremelyBasic] @@ -560,6 +570,8 @@ - type: randomHumanoidSettings id: NukeOp + speciesBlacklist: + - Shadowkin components: - type: NukeOperative - type: Psionic @@ -582,6 +594,8 @@ - type: randomHumanoidSettings id: Cluwne + speciesBlacklist: + - Shadowkin randomizeName: false components: - type: GhostRole diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml index 0086be81d9..c92595ffc9 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/observer.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/observer.yml @@ -32,6 +32,7 @@ - PsionicInvisibility - Ghost - Normal + - Ethereal - type: Input context: "ghost" - type: Examiner diff --git a/Resources/Prototypes/Entities/Mobs/Player/shadowkin.yml b/Resources/Prototypes/Entities/Mobs/Player/shadowkin.yml new file mode 100644 index 0000000000..2a58fe5c1f --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Player/shadowkin.yml @@ -0,0 +1,5 @@ +- type: entity + save: false + name: Urist McShadow + parent: MobShadowkinBase + id: MobShadowkin \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index c66b00fab4..2e5a1aea52 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -152,7 +152,6 @@ - type: Identity - type: IdExaminable - type: Hands - - type: Internals - type: Inventory - type: InventorySlots - type: FloatingVisuals diff --git a/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml b/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml new file mode 100644 index 0000000000..393cb0b871 --- /dev/null +++ b/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml @@ -0,0 +1,304 @@ +- type: entity + save: false + parent: + - MobBloodstream + - MobAtmosStandard + - MobFlammable + - BaseMobSpecies + id: MobShadowkinBase + name: Urist McShadow + abstract: true + components: + - type: Destructible + thresholds: + - trigger: + !type:DamageTypeTrigger + damageType: Blunt + damage: 400 + behaviors: + - !type:GibBehavior {} + - !type:SpawnEntitiesBehavior + spawn: + ShadowkinShadow: + min: 1 + max: 1 + EffectFlashShadowkinShadeskip: + min: 1 + max: 1 + - trigger: + !type:DamageTypeTrigger + damageType: Heat + damage: 1500 + behaviors: + - !type:SpawnEntitiesBehavior + spawnInContainer: true + spawn: + Ash: + min: 1 + max: 1 + - !type:BurnBodyBehavior {} + - !type:PlaySoundBehavior + sound: + collection: MeatLaserImpact + - type: PassiveDamage # Slight passive regen. Assuming one damage type, comes out to about 4 damage a minute. + allowedStates: + - Alive + damageCap: 20 + damage: + types: + Heat: -0.07 + groups: + Brute: -0.07 + - type: StatusEffects + allowed: + - Stun + - KnockedDown + - SlowedDown + - Stutter + - SeeingRainbows + - Electrocution + - ForcedSleep + - TemporaryBlindness + - Drunk + - SlurredSpeech + - RatvarianLanguage + - PressureImmunity + - Muted + - Pacified + - StaminaModifier + - type: Blindable + - type: ThermalRegulator + metabolismHeat: 800 + radiatedHeat: 100 + implicitHeatRegulation: 500 + sweatHeatRegulation: 2000 + shiveringHeatRegulation: 2000 + normalBodyTemperature: 310.15 + thermalRegulationTemperatureThreshold: 25 + - type: Perishable + - type: FireVisuals + alternateState: Standing + - type: OfferItem + - type: LayingDown + - type: Shoving + - type: BloodstreamAffectedByMass + power: 0.6 + - type: Hunger + - type: Thirst + - type: Carriable + - type: HumanoidAppearance + species: Shadowkin + - type: Icon + sprite: Mobs/Species/Shadowkin/parts.rsi + state: full + - type: Body + prototype: Shadowkin + - type: Flammable + damage: + types: + Heat: 1.5 # burn more + - type: MobThresholds + thresholds: # Weak + 0: Alive + 80: Critical + 180: Dead + - type: SlowOnDamage + speedModifierThresholds: + 48: 0.85 + 64: 0.65 + - type: Damageable + damageContainer: Biological # Shadowkin + damageModifierSet: Shadowkin + - type: Barotrauma + damage: + types: + Blunt: 0.35 # per second, scales with pressure and other constants. + - type: Bloodstream + bloodlossDamage: + types: + Bloodloss: 1 + bloodlossHealDamage: + types: + Bloodloss: -0.25 + - type: Temperature + heatDamageThreshold: 330 + coldDamageThreshold: 195 + currentTemperature: 310.15 + specificHeat: 46 + coldDamage: + types: + Cold: 0.05 #per second, scales with temperature & other constants + heatDamage: + types: + Heat: 0.25 #per second, scales with temperature & other constants + - type: Fixtures + fixtures: + fix1: + shape: !type:PhysShapeCircle + radius: 0.35 + density: 130 #lower density + restitution: 0.0 + mask: + - MobMask + layer: + - MobLayer + - type: Sprite + netsync: false + noRot: true + drawdepth: Mobs + scale: 0.85, 0.85 + layers: + - map: ["enum.HumanoidVisualLayers.Chest"] + - map: ["enum.HumanoidVisualLayers.Head"] + - map: ["enum.HumanoidVisualLayers.Snout"] + - map: ["enum.HumanoidVisualLayers.Eyes"] + shader: unshaded + - map: ["enum.HumanoidVisualLayers.RArm"] + - map: ["enum.HumanoidVisualLayers.LArm"] + - map: ["enum.HumanoidVisualLayers.RLeg"] + - map: ["enum.HumanoidVisualLayers.LLeg"] + - shader: StencilClear + sprite: Mobs/Species/Human/parts.rsi + state: l_leg + - shader: StencilMask + map: ["enum.HumanoidVisualLayers.StencilMask"] + sprite: Mobs/Customization/masking_helpers.rsi + state: full + visible: false + - map: ["enum.HumanoidVisualLayers.LFoot"] + - map: ["enum.HumanoidVisualLayers.RFoot"] + - map: ["socks"] + - map: ["underpants"] + - map: ["undershirt"] + - map: ["jumpsuit"] + - map: ["enum.HumanoidVisualLayers.LHand"] + - map: ["enum.HumanoidVisualLayers.RHand"] + - map: ["enum.HumanoidVisualLayers.Handcuffs"] + color: "#ffffff" + sprite: Objects/Misc/handcuffs.rsi + state: body-overlay-2 + visible: false + - map: ["id"] + - map: ["gloves"] + - map: ["shoes"] + - map: ["ears"] + - map: ["outerClothing"] + - map: ["eyes"] + - map: ["belt"] + - map: ["neck"] + - map: ["back"] + - map: ["enum.HumanoidVisualLayers.FacialHair"] + - map: ["enum.HumanoidVisualLayers.Hair"] + - map: ["enum.HumanoidVisualLayers.HeadSide"] + - map: ["enum.HumanoidVisualLayers.HeadTop"] + - map: ["mask"] + - map: ["head"] + - map: ["pocket1"] + - map: ["pocket2"] + - map: ["enum.HumanoidVisualLayers.Tail"] + - type: MeleeWeapon + soundHit: + collection: AlienClaw + angle: 30 + animation: WeaponArcClaw + damage: + types: + Slash: 5 + - type: Vocal + sounds: + Male: MaleShadowkin + Female: FemaleShadowkin + Unsexed: MaleShadowkin + - type: TypingIndicator + proto: alien + - type: MovementSpeedModifier + baseWalkSpeed: 2.7 + baseSprintSpeed: 4.5 + - type: Flashable + eyeDamageChance: 0.3 + eyeDamage: 1 + durationMultiplier: 1.5 + - type: Speech + allowedEmotes: ['Mars', 'Wurble'] + - type: Shadowkin + - type: Psionic + mindbreakingFeedback: shadowkin-blackeye + manaGain: 0.25 + mana: 150 + maxMana: 250 + bypassManaCheck: true + removable: false + - type: InnatePsionicPowers + powersToAdd: + - ShadowkinPowers + - type: LanguageKnowledge + speaks: + - TauCetiBasic + - Marish + understands: + - TauCetiBasic + - Marish + +- type: entity + save: false + parent: MobHumanDummy + id: MobShadowkinDummy + noSpawn: true + description: A dummy shadowkin meant to be used in character setup. + components: + - type: HumanoidAppearance + species: Shadowkin + - type: Sprite + netsync: false + noRot: true + drawdepth: Mobs + scale: 0.85, 0.85 # Small + layers: + - map: ["enum.HumanoidVisualLayers.Chest"] + - map: ["enum.HumanoidVisualLayers.Head"] + - map: ["enum.HumanoidVisualLayers.Snout"] + - map: ["enum.HumanoidVisualLayers.Eyes"] + shader: unshaded + - map: ["enum.HumanoidVisualLayers.RArm"] + - map: ["enum.HumanoidVisualLayers.LArm"] + - map: ["enum.HumanoidVisualLayers.RLeg"] + - map: ["enum.HumanoidVisualLayers.LLeg"] + - shader: StencilClear + sprite: Mobs/Species/Human/parts.rsi + state: l_leg + - shader: StencilMask + map: ["enum.HumanoidVisualLayers.StencilMask"] + sprite: Mobs/Customization/masking_helpers.rsi + state: full + visible: false + - map: ["enum.HumanoidVisualLayers.LFoot"] + - map: ["enum.HumanoidVisualLayers.RFoot"] + - map: ["socks"] + - map: ["underpants"] + - map: ["undershirt"] + - map: ["jumpsuit"] + - map: ["enum.HumanoidVisualLayers.LHand"] + - map: ["enum.HumanoidVisualLayers.RHand"] + - map: ["enum.HumanoidVisualLayers.Handcuffs"] + color: "#ffffff" + sprite: Objects/Misc/handcuffs.rsi + state: body-overlay-2 + visible: false + - map: ["id"] + - map: ["gloves"] + - map: ["shoes"] + - map: ["ears"] + - map: ["outerClothing"] + - map: ["eyes"] + - map: ["belt"] + - map: ["neck"] + - map: ["back"] + - map: ["enum.HumanoidVisualLayers.FacialHair"] + - map: ["enum.HumanoidVisualLayers.Hair"] + - map: ["enum.HumanoidVisualLayers.HeadSide"] + - map: ["enum.HumanoidVisualLayers.HeadTop"] + - map: ["mask"] + - map: ["head"] + - map: ["pocket1"] + - map: ["pocket2"] + - map: ["enum.HumanoidVisualLayers.Tail"] diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index ad8f3d5c71..3ff5a3955a 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -57,6 +57,7 @@ - idcard - Belt - type: UnpoweredFlashlight + - type: EtherealLight - type: PointLight enabled: false radius: 1.5 diff --git a/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml b/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml index d6d86c08b2..19aa6a3015 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/prizeticket.yml @@ -285,6 +285,9 @@ - id: PetRock prob: 0.80 orGroup: Prize + - id: PlushieShadowkin + prob: 0.80 + orGroup: Prize # Uncommon - id: PrizeTicket60 prob: 0.50 diff --git a/Resources/Prototypes/Entities/Objects/Fun/toys.yml b/Resources/Prototypes/Entities/Objects/Fun/toys.yml index d7a5f3542d..fc771414b4 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/toys.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/toys.yml @@ -1954,4 +1954,14 @@ size: Ginormous sprite: Objects/Weapons/Melee/Throngler-in-hand.rsi - type: DisarmMalus - malus: 0 \ No newline at end of file + malus: 0 + +- type: entity + parent: BasePlushie + id: PlushieShadowkin + name: shadowkin plushie + description: A plushie of a Shadowkin. It's very soft. + components: + - type: Sprite + sprite: Objects/Fun/toys.rsi + state: shadowkin \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml b/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml index bc0ed8f03c..7af87ca40d 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/bluespace.yml @@ -14,6 +14,10 @@ - type: PhysicalComposition materialComposition: Bluespace: 100 + - type: EmitSoundOnUse + sound: + collection: RadiationPulse + - type: EtherealStunItem - type: Tag tags: - BluespaceCrystal diff --git a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml index a02f94215b..8fc465f4fb 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml @@ -40,6 +40,7 @@ sprite: Objects/Misc/Lights/lights.rsi size: Normal heldPrefix: off + - type: EtherealLight - type: PointLight enabled: false radius: 3 diff --git a/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml b/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml index a100500494..838715f1da 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/kudzu.yml @@ -25,8 +25,7 @@ - type: MeleeSound soundGroups: Brute: - path: - "/Audio/Weapons/slash.ogg" + path: "/Audio/Weapons/slash.ogg" - type: Sprite sprite: Objects/Misc/kudzu.rsi state: kudzu_11 @@ -38,21 +37,19 @@ fix1: hard: false density: 7 - shape: - !type:PhysShapeAabb + shape: !type:PhysShapeAabb bounds: "-0.5,-0.5,0.5,0.5" layer: - - MidImpassable + - MidImpassable - type: Damageable damageModifierSet: Wood - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 10 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: !type:DamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Temperature heatDamage: types: @@ -69,14 +66,14 @@ Flammable: [Touch] Extinguish: [Touch] reactions: - - reagents: [WeedKiller, PlantBGone] - methods: [Touch] - effects: - - !type:HealthChange - scaleByQuantity: true - damage: - types: - Heat: 10 + - reagents: [WeedKiller, PlantBGone] + methods: [Touch] + effects: + - !type:HealthChange + scaleByQuantity: true + damage: + types: + Heat: 10 - type: AtmosExposed - type: Kudzu growthTickChance: 0.3 @@ -86,19 +83,19 @@ sprintSpeedModifier: 0.2 ignoreWhitelist: components: - - IgnoreKudzu + - IgnoreKudzu - type: Food requiredStomachs: 2 # ruminants have 4 stomachs but i dont care to give them literally 4 stomachs. 2 is good delay: 0.5 - type: FlavorProfile flavors: - - fiber + - fiber - type: SolutionContainerManager solutions: food: reagents: - - ReagentId: Nutriment - Quantity: 2 + - ReagentId: Nutriment + Quantity: 2 - type: entity id: WeakKudzu @@ -127,22 +124,22 @@ sprintSpeedModifier: 0.8 ignoreWhitelist: components: - - IgnoreKudzu + - IgnoreKudzu - type: RandomSpawner deleteSpawnerAfterSpawn: false rareChance: 0.15 offset: 0.2 chance: 0.05 prototypes: - - LightTree01 - - LightTree02 - - LightTree03 - - LightTree04 - - LightTree05 - - LightTree06 - - CrystalCyan + - LightTree01 + - LightTree02 + - LightTree03 + - LightTree04 + - LightTree05 + - LightTree06 + - CrystalCyan rarePrototypes: - - AnomalyFloraBulb + - AnomalyFloraBulb - type: entity id: KudzuFlowerAngry @@ -154,11 +151,11 @@ - type: RandomSpawner chance: 0.05 rarePrototypes: - - AnomalyFloraBulb - - AnomalyFloraBulb - - MobLuminousEntity - - MobLuminousObject - - MobLuminousPerson + - AnomalyFloraBulb + - AnomalyFloraBulb + - MobLuminousEntity + - MobLuminousObject + - MobLuminousPerson - type: entity id: FleshKudzu @@ -173,8 +170,7 @@ - type: MeleeSound soundGroups: Brute: - path: - "/Audio/Weapons/slash.ogg" + path: "/Audio/Weapons/slash.ogg" - type: Sprite sprite: Objects/Misc/fleshkudzu.rsi state: kudzu_11 @@ -186,20 +182,18 @@ fix1: hard: false density: 7 - shape: - !type:PhysShapeAabb + shape: !type:PhysShapeAabb bounds: "-0.5,-0.5,0.5,0.5" layer: - - MidImpassable + - MidImpassable - type: Damageable - type: Destructible thresholds: - - trigger: - !type:DamageTrigger - damage: 40 - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: !type:DamageTrigger + damage: 40 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: DamageContacts damage: types: @@ -207,7 +201,7 @@ Piercing: 1.5 ignoreWhitelist: tags: - - Flesh + - Flesh - type: Kudzu growthTickChance: 0.1 spreadChance: 0.4 @@ -232,23 +226,23 @@ - type: Flammable fireSpread: true damage: - types: - Heat: 3 + types: + Heat: 3 - type: AtmosExposed - type: SpeedModifierContacts walkSpeedModifier: 0.3 sprintSpeedModifier: 0.3 ignoreWhitelist: tags: - - Flesh + - Flesh - type: Food # delightfully devilish ! delay: 0.5 - type: SolutionContainerManager solutions: food: reagents: - - ReagentId: Protein - Quantity: 2 + - ReagentId: Protein + Quantity: 2 - type: Respirator damage: types: @@ -260,7 +254,7 @@ - type: entity name: dark haze id: ShadowKudzu - parent: [ BaseKudzu, BaseShadow ] + parent: [BaseKudzu, BaseShadow] components: - type: Physics canCollide: false @@ -269,9 +263,9 @@ drawdepth: Effects sprite: Effects/spookysmoke.rsi layers: - - state: spookysmoke - color: "#793a80dd" - map: [base] + - state: spookysmoke + color: "#793a80dd" + map: [base] - type: Kudzu growthTickChance: 0.2 spreadChance: 0.99 @@ -281,10 +275,10 @@ offset: 0.2 chance: 0.45 prototypes: - - ShadowBasaltRandom + - ShadowBasaltRandom rarePrototypes: - - ShadowPortal - - ShadowKudzuLootSpawner + - ShadowPortal + - ShadowKudzuLootSpawner - type: Tag tags: - HideContextMenu @@ -292,10 +286,10 @@ - type: OptionsVisualizer visuals: base: - - options: Default - data: { state: spookysmoke } - - options: ReducedMotion - data: { state: spookysmoke_static } + - options: Default + data: { state: spookysmoke } + - options: ReducedMotion + data: { state: spookysmoke_static } - type: entity name: Haze @@ -304,3 +298,18 @@ components: - type: Kudzu spreadChance: 0 #appears during pulsation. It shouldnt spreading. + +- type: entity + name: Shadowkin Haze + id: ShadowkinShadow + parent: ShadowKudzuWeak + components: + - type: RandomSpawner + deleteSpawnerAfterSpawn: false + rareChance: 0 + offset: 0.2 + chance: 0.45 + prototypes: + - ShadowBasaltRandom + - type: TimedDespawn + lifetime: 30 \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml index 2a982ed6f7..cbb5dfee0a 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/flashlights.yml @@ -65,6 +65,7 @@ - type: Item sprite: Objects/Tools/flashlight.rsi storedRotation: -90 + - type: EtherealLight - type: PointLight enabled: false mask: /Textures/Effects/LightMasks/cone.png diff --git a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml index 6b301a50ee..c6c55a916c 100644 --- a/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml +++ b/Resources/Prototypes/Entities/Structures/Lighting/base_lighting.yml @@ -31,6 +31,7 @@ state: glow shader: unshaded state: base + - type: EtherealLight - type: PointLight color: "#FFE4CE" # 5000K color temp energy: 0.8 diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 35434003d2..0088fc4306 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -806,6 +806,8 @@ - WeaponLaserCarbinePractice - Zipties - ShockCollar + - ShadowkinRestraints + # DeltaV - .38 special ammo - Add various .38 special ammo to security techfab - MagazineBoxSpecial - MagazineBoxSpecialPractice - SpeedLoaderSpecial @@ -1398,6 +1400,7 @@ staticRecipes: - PrizeBall - PlushieMothRandom + - PlushieShadowkin - PlushieMothMusician - PlushieMothBartender - PlushieBee diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index e98b40b9a0..c79cfa2641 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -86,6 +86,7 @@ - type: Electrified enabled: false usesApcPower: true + - type: EtherealLight - type: PointLight enabled: false castShadows: false diff --git a/Resources/Prototypes/Guidebook/species.yml b/Resources/Prototypes/Guidebook/species.yml index d9d039ea43..f7b77b7ec6 100644 --- a/Resources/Prototypes/Guidebook/species.yml +++ b/Resources/Prototypes/Guidebook/species.yml @@ -12,6 +12,7 @@ - SlimePerson - IPCs - Harpy + - Shadowkin - type: guideEntry id: Arachnid @@ -56,4 +57,9 @@ - type: guideEntry id: Harpy name: species-name-harpy - text: "/ServerInfo/Guidebook/Mobs/Harpy.xml" \ No newline at end of file + text: "/ServerInfo/Guidebook/Mobs/Harpy.xml" + +- type: guideEntry + id: Shadowkin + name: species-name-shadowkin + text: "/ServerInfo/Guidebook/Mobs/Shadowkin.xml" diff --git a/Resources/Prototypes/Language/Species-Specific/marish.yml b/Resources/Prototypes/Language/Species-Specific/marish.yml new file mode 100644 index 0000000000..872d67373d --- /dev/null +++ b/Resources/Prototypes/Language/Species-Specific/marish.yml @@ -0,0 +1,20 @@ +# Spoken by shadowkins. +- type: language + id: Marish + speech: + color: "#be3cc5" + fontId: Lymphatic + empathySpeech: true + speechVerbOverrides: + - chat-speech-verb-marish + obfuscation: + !type:SyllableObfuscation + minSyllables: 1 # Replacements are really short + maxSyllables: 2 + replacement: + - mar + - mwrrr + - maaAr + - aarrr + - wrurrl + - mmar \ No newline at end of file diff --git a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml index f7a3120570..3834c3b95c 100644 --- a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml +++ b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml @@ -18,6 +18,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin startingGear: ForensicMantisGear icon: "JobIconForensicMantis" supervisors: job-supervisors-rd diff --git a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml index 00ffdde666..0ca1794742 100644 --- a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml +++ b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Wildcards/prisoner.yml @@ -13,6 +13,15 @@ - !type:DepartmentTimeRequirement department: Security min: 21600 + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin + - !type:CharacterTraitRequirement + traits: + - ShadowkinBlackeye - type: startingGear id: PrisonerGear diff --git a/Resources/Prototypes/Psionics/psionics.yml b/Resources/Prototypes/Psionics/psionics.yml index 378d48b3fa..54275fc9c5 100644 --- a/Resources/Prototypes/Psionics/psionics.yml +++ b/Resources/Prototypes/Psionics/psionics.yml @@ -52,7 +52,7 @@ name: Pyrokinesis description: pyrokinesis-power-description actions: - - ActionPyrokinesis + - ActionPyrokinesis components: - type: PyrokinesisPower initializationFeedback: pyrokinesis-power-initialization-feedback @@ -64,7 +64,7 @@ name: Metapsionic Pulse description: metapsionic-power-description actions: - - ActionMetapsionic + - ActionMetapsionic components: - type: MetapsionicPower initializationFeedback: metapsionic-power-initialization-feedback @@ -77,7 +77,7 @@ name: Psionic Regeneration description: psionic-regeneration-power-description actions: - - ActionPsionicRegeneration + - ActionPsionicRegeneration components: - type: PsionicRegenerationPower initializationFeedback: psionic-regeneration-power-initialization-feedback @@ -90,7 +90,7 @@ name: Telegnosis description: telegnosis-power-description actions: - - ActionTelegnosis + - ActionTelegnosis components: - type: TelegnosisPower initializationFeedback: telegnosis-power-initialization-feedback @@ -103,7 +103,7 @@ name: Psionic Invisibility description: psionic-invisibility-power-description actions: - - ActionDispel + - ActionDispel components: - type: PsionicInvisibilityPower initializationFeedback: psionic-invisibility-power-initialization-feedback @@ -204,7 +204,7 @@ name: Shadeskip description: shadeskip-power-description actions: - - ActionShadeskip + - ActionShadeskip initializationFeedback: shadeskip-power-initialization-feedback metapsionicFeedback: shadeskip-power-metapsionic-feedback amplificationModifier: 1 @@ -214,7 +214,32 @@ name: Telekinetic Pulse description: telekinetic-pulse-power-description actions: - - ActionTelekineticPulse + - ActionTelekineticPulse initializationFeedback: telekinetic-pulse-power-initialization-feedback metapsionicFeedback: telekinetic-pulse-power-metapsionic-feedback - amplificationModifier: 1 \ No newline at end of file + amplificationModifier: 1 + +- type: psionicPower + id: ShadowkinPowers + name: Shadowkin Powers + description: shadowkin-powers-description + actions: + - ActionShadowkinShadeskip + - ActionDarkSwap + powerSlotCost: 1 + +- type: psionicPower + id: EtherealVisionPower + name: Ethereal Vision + description: ethereal-vision-powers-description + components: + - type: ShowEthereal + powerSlotCost: 0 + +- type: psionicPower + id: DarkSwapPower + name: DarkSwap + description: darkswap-power-description + actions: + - ActionDarkSwap + powerSlotCost: 1 diff --git a/Resources/Prototypes/Reagents/toxins.yml b/Resources/Prototypes/Reagents/toxins.yml index 5d29f024ce..abb33f832a 100644 --- a/Resources/Prototypes/Reagents/toxins.yml +++ b/Resources/Prototypes/Reagents/toxins.yml @@ -511,6 +511,12 @@ shouldHave: true reagent: Protein amount: 0.5 + - !type:AdjustReagent + conditions: + - !type:OrganType + type: Shadowkin + reagent: Protein + amount: 0.5 - type: reagent id: Allicin diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/makeshifthandcuffs.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/makeshifthandcuffs.yml index 08d6d57fb9..47eaf30e5c 100644 --- a/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/makeshifthandcuffs.yml +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/improvised/makeshifthandcuffs.yml @@ -11,4 +11,3 @@ doAfter: 5 - node: cuffscable entity: Cablecuffs - diff --git a/Resources/Prototypes/Recipes/Lathes/prizecounter.yml b/Resources/Prototypes/Recipes/Lathes/prizecounter.yml index c600702b20..9e5e239c6f 100644 --- a/Resources/Prototypes/Recipes/Lathes/prizecounter.yml +++ b/Resources/Prototypes/Recipes/Lathes/prizecounter.yml @@ -6,6 +6,14 @@ materials: PrizeTicket: 30 +- type: latheRecipe + id: PlushieShadowkin + result: PlushieShadowkin + applyMaterialDiscount: false + completetime: 0.1 + materials: + PrizeTicket: 50 + - type: latheRecipe id: PlushieMothRandom result: PlushieMothRandom diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index 04c2ad1ec1..98e8c530b8 100644 --- a/Resources/Prototypes/Recipes/Lathes/security.yml +++ b/Resources/Prototypes/Recipes/Lathes/security.yml @@ -758,9 +758,10 @@ result: GrenadeBlast completetime: 3 materials: - Steel: 450 - Plastic: 300 - Gold: 150 + Steel: 150 + Plastic: 100 + Gold: 50 + - type: latheRecipe id: GrenadeFlash result: GrenadeFlash @@ -872,3 +873,10 @@ materials: Plastic: 15 Uranium: 10 + +- type: latheRecipe + id: ShadowkinRestraints + result: ClothingOuterShadowkinRestraints + completetime: 6 + materials: + Steel: 300 diff --git a/Resources/Prototypes/Roles/Antags/Thief.yml b/Resources/Prototypes/Roles/Antags/Thief.yml index 131db8cf1d..2715e6d96d 100644 --- a/Resources/Prototypes/Roles/Antags/Thief.yml +++ b/Resources/Prototypes/Roles/Antags/Thief.yml @@ -3,4 +3,4 @@ name: roles-antag-thief-name antagonist: true setPreference: true - objective: roles-antag-thief-objective + objective: roles-antag-thief-objective \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Antags/traitor.yml b/Resources/Prototypes/Roles/Antags/traitor.yml index fec2280ddc..05b0553c78 100644 --- a/Resources/Prototypes/Roles/Antags/traitor.yml +++ b/Resources/Prototypes/Roles/Antags/traitor.yml @@ -6,4 +6,4 @@ objective: roles-antag-syndicate-agent-objective requirements: - !type:CharacterOverallTimeRequirement # DeltaV - Playtime requirement - min: 86400 # DeltaV - 24 hours + min: 86400 # DeltaV - 24 hours \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml index b4d849b92d..a33e29963d 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml @@ -16,6 +16,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin startingGear: ChaplainGear icon: "JobIconChaplain" supervisors: job-supervisors-rd diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml b/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml index 8552073dcc..3468d10f27 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/librarian.yml @@ -16,6 +16,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin startingGear: LibrarianGear icon: "JobIconLibrarian" supervisors: job-supervisors-rd diff --git a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml index b132729432..6b61fe856e 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml @@ -18,6 +18,15 @@ min: 43200 # DeltaV - 12 hours - !type:CharacterOverallTimeRequirement min: 72000 # DeltaV - 20 hours + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin + - !type:CharacterTraitRequirement + traits: + - ShadowkinBlackeye weight: 10 startingGear: CMOGear icon: "JobIconChiefMedicalOfficer" diff --git a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml index 46d91ee00e..4fc9325f39 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml @@ -19,6 +19,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin weight: 10 startingGear: ResearchDirectorGear icon: "JobIconResearchDirector" diff --git a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml index 01f14f8099..18f6d04022 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml @@ -6,7 +6,6 @@ requirements: - !type:CharacterDepartmentTimeRequirement department: Security - min: 14400 # DeltaV - 4 hours startingGear: SecurityOfficerGear icon: "JobIconSecurityOfficer" supervisors: job-supervisors-hos diff --git a/Resources/Prototypes/Shaders/shaders.yml b/Resources/Prototypes/Shaders/shaders.yml index 3f0cb5ae1f..41d04137bb 100644 --- a/Resources/Prototypes/Shaders/shaders.yml +++ b/Resources/Prototypes/Shaders/shaders.yml @@ -110,4 +110,16 @@ - type: shader id: Flap kind: source - path: "/Textures/Shaders/flap.swsl" \ No newline at end of file + path: "/Textures/Shaders/flap.swsl" + + # Shadowkin shaders + +- type: shader + id: ColorTint + kind: source + path: "/Textures/Shaders/color_tint.swsl" + +- type: shader + id: Ethereal + kind: source + path: "/Textures/Shaders/ethereal.swsl" \ No newline at end of file diff --git a/Resources/Prototypes/SoundCollections/emotes.yml b/Resources/Prototypes/SoundCollections/emotes.yml index 35693a70a4..f655a8cf6b 100644 --- a/Resources/Prototypes/SoundCollections/emotes.yml +++ b/Resources/Prototypes/SoundCollections/emotes.yml @@ -86,3 +86,13 @@ - /Audio/Voice/IPC/whirr1.ogg - /Audio/Voice/IPC/whirr2.ogg - /Audio/Voice/IPC/whirr3.ogg + +- type: soundCollection + id: Mars + files: + - /Audio/Voice/Shadowkin/mar.ogg + +- type: soundCollection + id: Wurble + files: + - /Audio/Voice/Shadowkin/wurble.ogg \ No newline at end of file diff --git a/Resources/Prototypes/Species/shadowkin.yml b/Resources/Prototypes/Species/shadowkin.yml new file mode 100644 index 0000000000..7043397d8e --- /dev/null +++ b/Resources/Prototypes/Species/shadowkin.yml @@ -0,0 +1,179 @@ +- type: species + id: Shadowkin + name: species-name-shadowkin + roundStart: true + prototype: MobShadowkin + sprites: MobShadowkinSprites + defaultSkinTone: "#FFFFFF" + markingLimits: MobShadowkinMarkingLimits + dollPrototype: MobShadowkinDummy + skinColoration: Hues + naming: First + maleFirstNames: names_shadowkin + femaleFirstNames: names_shadowkin + minAge: 18 + maxAge: 300 + youngAge: 20 + oldAge: 250 + sexes: + - Male + - Female + - Unsexed + minHeight: 0.65 + defaultHeight: 0.85 + maxHeight: 1.15 + minWidth: 0.6 + defaultWidth: 0.85 + maxWidth: 1.2 + +- type: speciesBaseSprites + id: MobShadowkinSprites + sprites: + Head: MobShadowkinHead + Snout: MobShadowkinAnyMarkingFollowSkin + HeadTop: MobShadowkinAnyMarkingFollowSkin + HeadSide: MobShadowkinAnyMarkingFollowSkin + Tail: MobShadowkinAnyMarkingFollowSkin + Chest: MobShadowkinTorso + Eyes: MobShadowkinEyes + LArm: MobShadowkinLArm + RArm: MobShadowkinRArm + LHand: MobShadowkinLHand + RHand: MobShadowkinRHand + LLeg: MobShadowkinLLeg + RLeg: MobShadowkinRLeg + LFoot: MobShadowkinLFoot + RFoot: MobShadowkinRFoot + +- type: markingPoints + id: MobShadowkinMarkingLimits + points: + Tail: + points: 1 + required: true + defaultMarkings: [TailShadowkin] + HeadTop: + points: 1 + required: true + defaultMarkings: [EarsShadowkin] + Chest: + points: 1 + required: false + RightLeg: + points: 2 + required: false + RightFoot: + points: 2 + required: false + LeftLeg: + points: 2 + required: false + LeftFoot: + points: 2 + required: false + RightArm: + points: 2 + required: false + RightHand: + points: 2 + required: false + LeftArm: + points: 2 + required: false + LeftHand: + points: 2 + required: false + +- type: humanoidBaseSprite + id: MobShadowkinAnyMarkingFollowSkin + markingsMatchSkin: true + +- type: humanoidBaseSprite + id: MobShadowkinHead + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: head_m + +- type: humanoidBaseSprite + id: MobShadowkinHeadMale + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: head_m + +- type: humanoidBaseSprite + id: MobShadowkinHeadFemale + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: head_f + +- type: humanoidBaseSprite + id: MobShadowkinTorso + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: torso_m + +- type: humanoidBaseSprite + id: MobShadowkinTorsoMale + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: torso_m + +- type: humanoidBaseSprite + id: MobShadowkinTorsoFemale + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: torso_f + +- type: humanoidBaseSprite + id: MobShadowkinLLeg + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: l_leg + +- type: humanoidBaseSprite + id: MobShadowkinLHand + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: l_hand + +- type: humanoidBaseSprite + id: MobShadowkinEyes + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: eyes + +- type: humanoidBaseSprite + id: MobShadowkinLArm + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: l_arm + +- type: humanoidBaseSprite + id: MobShadowkinLFoot + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: l_foot + +- type: humanoidBaseSprite + id: MobShadowkinRLeg + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: r_leg + +- type: humanoidBaseSprite + id: MobShadowkinRHand + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: r_hand + +- type: humanoidBaseSprite + id: MobShadowkinRArm + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: r_arm + +- type: humanoidBaseSprite + id: MobShadowkinRFoot + baseSprite: + sprite: Mobs/Species/Shadowkin/parts.rsi + state: r_foot \ No newline at end of file diff --git a/Resources/Prototypes/Species/species_weights.yml b/Resources/Prototypes/Species/species_weights.yml index 990a8ccecf..d158862d38 100644 --- a/Resources/Prototypes/Species/species_weights.yml +++ b/Resources/Prototypes/Species/species_weights.yml @@ -4,8 +4,9 @@ weights: Human: 5 Reptilian: 4 - SlimePerson: 4 + SlimePerson: 3 Oni: 3 #Nyanotrasen Oni, see Prototypes/Nyanotrasen/Entities/Mobs/Species/Oni.yml Felinid: 4 # Nyanotrasen - Felinid, see Prototypes/Nyanotrasen/Entities/Mobs/Species/felinid.yml - Vulpkanin: 3 # DeltaV - Vulpkanin, see Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml + Vulpkanin: 4 # DeltaV - Vulpkanin, see Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml Diona: 2 + Shadowkin: 0 diff --git a/Resources/Prototypes/Traits/disabilities.yml b/Resources/Prototypes/Traits/disabilities.yml index 9961c84948..5e5028035f 100644 --- a/Resources/Prototypes/Traits/disabilities.yml +++ b/Resources/Prototypes/Traits/disabilities.yml @@ -217,6 +217,7 @@ inverted: true species: - Vulpkanin # This trait functions exactly as-is for the Vulpkanin trait. + - Shadowkin components: - type: Flashable eyeDamageChance: 0.3 diff --git a/Resources/Prototypes/Traits/mental.yml b/Resources/Prototypes/Traits/mental.yml index 4a50c8c397..8fe672c221 100644 --- a/Resources/Prototypes/Traits/mental.yml +++ b/Resources/Prototypes/Traits/mental.yml @@ -27,6 +27,7 @@ inverted: true species: - Oni + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -64,6 +65,7 @@ inverted: true species: - Oni + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -97,6 +99,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -130,6 +138,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -163,6 +177,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -196,6 +216,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -228,6 +254,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: diff --git a/Resources/Prototypes/Traits/physical.yml b/Resources/Prototypes/Traits/physical.yml index 288386b8f4..afaaf0b2cb 100644 --- a/Resources/Prototypes/Traits/physical.yml +++ b/Resources/Prototypes/Traits/physical.yml @@ -305,6 +305,7 @@ species: - Felinid # Felinids already have cat claws. - Reptilian # Reptilians also have cat claws. + - Shadowkin # Shadowkins also have claws. # - Vulpkanin # Vulpkanin have "Blunt" claws. One could argue this trait "Sharpens" their claws. - !type:CharacterTraitRequirement inverted: true @@ -403,6 +404,7 @@ species: - Arachnid - Arachne + - Shadowkin - IPC components: - type: Sericulture diff --git a/Resources/Prototypes/Traits/skills.yml b/Resources/Prototypes/Traits/skills.yml index 0e4868f19b..0d6eb045ac 100644 --- a/Resources/Prototypes/Traits/skills.yml +++ b/Resources/Prototypes/Traits/skills.yml @@ -22,16 +22,16 @@ components: - type: SelfAware analyzableTypes: - - Blunt - - Slash - - Piercing - - Heat - - Shock - - Cold - - Caustic + - Blunt + - Slash + - Piercing + - Heat + - Shock + - Cold + - Caustic detectableGroups: - - Airloss - - Toxin + - Airloss + - Toxin - type: trait id: HeavyweightDrunk @@ -186,6 +186,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - !type:CharacterTraitRequirement inverted: true traits: @@ -221,6 +227,12 @@ inverted: true traits: - LatentPsychic + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - type: trait id: NaturalTelepath @@ -252,6 +264,12 @@ - !type:CharacterTraitRequirement traits: - AnomalousPositronics + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + inverted: true + species: + - Shadowkin - type: trait id: TrapAvoider @@ -261,10 +279,10 @@ - type: StepTriggerImmune whitelist: types: - - Shard - - Landmine - - Mousetrap - - SlipEntity + - Shard + - Landmine + - Mousetrap + - SlipEntity requirements: - !type:CharacterSpeciesRequirement inverted: true @@ -279,6 +297,8 @@ componentRemovals: - PsionicInsulation requirements: - - !type:CharacterSpeciesRequirement - species: - - IPC + - !type:CharacterLogicOrRequirement + requirements: + - !type:CharacterSpeciesRequirement + species: + - IPC diff --git a/Resources/Prototypes/Traits/species.yml b/Resources/Prototypes/Traits/species.yml index 504cf469d4..17be3489fe 100644 --- a/Resources/Prototypes/Traits/species.yml +++ b/Resources/Prototypes/Traits/species.yml @@ -60,3 +60,17 @@ traits: - Swashbuckler - Spearmaster + +- type: trait + id: ShadowkinBlackeye + category: Mental + points: 4 + componentRemovals: + - Shadowkin + components: + - type: Shadowkin + blackeyeSpawn: true + requirements: + - !type:CharacterSpeciesRequirement + species: + - Shadowkin \ No newline at end of file diff --git a/Resources/Prototypes/Voice/speech_emote_sounds.yml b/Resources/Prototypes/Voice/speech_emote_sounds.yml index a5d05e24b7..5c3f8ec697 100644 --- a/Resources/Prototypes/Voice/speech_emote_sounds.yml +++ b/Resources/Prototypes/Voice/speech_emote_sounds.yml @@ -183,6 +183,90 @@ params: variation: 0.125 +- type: emoteSounds + id: MaleShadowkin + params: + variation: 0.125 + sounds: + Scream: + collection: SlimeMaleScreams + Laugh: + collection: MaleLaugh + Sneeze: + collection: MaleSneezes + Cough: + collection: MaleCoughs + Yawn: + collection: MaleYawn + Snore: + collection: Snores + Honk: + collection: BikeHorn + Sigh: + collection: MaleSigh + Crying: + collection: MaleCry + Whistle: + collection: Whistles + Weh: + collection: Weh + Hiss: + collection: FelinidHisses + Meow: + collection: FelinidMeows + Mew: + collection: FelinidMews + Growl: + collection: FelinidGrowls + Purr: + collection: FelinidPurrs + Mars: + collection: Mars + Wurble: + collection: Wurble + +- type: emoteSounds + id: FemaleShadowkin + params: + variation: 0.125 + sounds: + Scream: + collection: SlimeFemaleScreams + Laugh: + collection: MaleLaugh + Sneeze: + collection: FemaleSneezes + Cough: + collection: FemaleCoughs + Yawn: + collection: FemaleYawn + Snore: + collection: Snores + Honk: + collection: BikeHorn + Sigh: + collection: FemaleSigh + Crying: + collection: FemaleCry + Whistle: + collection: Whistles + Weh: + collection: Weh + Hiss: + collection: FelinidHisses + Meow: + collection: FelinidMeows + Mew: + collection: FelinidMews + Growl: + collection: FelinidGrowls + Purr: + collection: FelinidPurrs + Mars: + collection: Mars + Wurble: + collection: Wurble + - type: emoteSounds id: UnisexVox sounds: diff --git a/Resources/Prototypes/Voice/speech_emotes.yml b/Resources/Prototypes/Voice/speech_emotes.yml index ca4aa17229..309794c1dc 100644 --- a/Resources/Prototypes/Voice/speech_emotes.yml +++ b/Resources/Prototypes/Voice/speech_emotes.yml @@ -340,3 +340,19 @@ chatMessages: [ whirrs ] chatTriggers: - whirrs + +- type: emote + id: Mars + name: chat-emote-name-mars + category: Vocal + chatMessages: [ mars ] + chatTriggers: + - mars + +- type: emote + id: Wurble + name: chat-emote-name-wurble + category: Vocal + chatMessages: [ wurble ] + chatTriggers: + - wurble diff --git a/Resources/Prototypes/fonts.yml b/Resources/Prototypes/fonts.yml index 8e49fe8c35..c61a5fb804 100644 --- a/Resources/Prototypes/fonts.yml +++ b/Resources/Prototypes/fonts.yml @@ -62,6 +62,10 @@ id: Noganas path: /Fonts/Noganas.ttf +- type: font + id: Lymphatic + path: /Fonts/Lymphatic.ttf + - type: font id: Cambria path: /Fonts/Cambria.ttf diff --git a/Resources/ServerInfo/Guidebook/Mobs/Shadowkin.xml b/Resources/ServerInfo/Guidebook/Mobs/Shadowkin.xml new file mode 100644 index 0000000000..f2f73ee666 --- /dev/null +++ b/Resources/ServerInfo/Guidebook/Mobs/Shadowkin.xml @@ -0,0 +1,36 @@ + +# Shadowkin + + + + + +Fluffy lil' guys. + +## Ability Differences + +- Need no air to survive +- Can Shadeskip +- Can travel to and from The Dark at will +- Dims nearby lights when in the The Dark +- When too low on energy, they may fall into a powerful sleep +- Can "speak" in the Empathy, which only other Shadowkin can understand +- Slightly less blunt damage +- A bit more slash damage +- Slightly more piercing damage +- Less cold damage +- Slightly more heat damage +- Near no cellular damage +- More bloodloss damage +- Slightly more shock damage +- More radiation damage + +## Physical Differences + +- Very large and brightly colored eyes with no pupils +- Sees the world through their eyes' tint +- Very large ears +- Very fluffy +- Can be Male, Female, or Unisex +- Can be 18-300 years old + \ No newline at end of file diff --git a/Resources/ServerInfo/Guidebook/Mobs/shadowkin.Lore.txt b/Resources/ServerInfo/Guidebook/Mobs/shadowkin.Lore.txt new file mode 100644 index 0000000000..e3cda6d77e --- /dev/null +++ b/Resources/ServerInfo/Guidebook/Mobs/shadowkin.Lore.txt @@ -0,0 +1,178 @@ +# Lore + +## Biology + +[color=#a88b5e]Physicality[/color] + +Shadowkin seem very similar to canine species when looked at. +While mammalian in nature, it is nearly impossible to find any sexual dimorphism between genders, if they even have any. +They seem to all share the same body shape and often voices. + +The head consists of a snout with a nose, sharp teeth in the mouth, and two big ears on top of the head. +In addition to the two big ears, Shadowkin have two smaller ears lower down, giving them an exceptionally good ability to detect where exactly sounds come from. + +Shadowkin eyes are usually very large and fully mono-colored; they have no discernible pupils, irises, or similar. +The eye of a Shadowkin is a solid orb of color and, perhaps, their most defining trait. + +All observations show Shadowkin do not seem to age after a certain point. +Some may look older or younger than others, but the aging process seems to stop when the Shadowkin is chronologically in their 20s. + +Unusually, compiled medical data on Shadowkin shows that they have no lungs, thus not needing to breathe. +The compiled medical data also shows that they have a large, more circular shape where their lungs would be. + +[color=#a88b5e]Mentality[/color] + +Shadowkin core personalities are mirrored by their eye color. +Some common colors and their meanings are: + +- Red: Determined to reach a goal, even if it means sacrificing others. +- Green: Eager to learn and fit in with others. +- Blue: Very calm and collected, or shy. + +Other colors also exist in addition to these but are not as common. +The other colors are just mixes of the three main colors. +For example, here are a couple of mixes: + +- Purple: Determined, yet probably won't harm, in some cases shy. +- Yellow: Quite eager to be number one, especially if related to knowledge. +- Orange: Shy and calm, yet will fight if provoked. +- White: Very robust, exceeds expectations in most things they do, but is quite boring to interact with generally. + +There are rumors other colors exist as well. +Shadowkin that lose their ability to travel between dimensions have [color=#000000]black eyes[/color] no matter their core personality. +These black-eyed Shadowkin make up the largest number of them living in Realspace. + +## History + +Shadowkin are creatures native to their so-called Pocket Dimension, a part of space that is unable to be accurately pinpointed and/or visited by most people. +From there, Shadowkin visit our worlds, and sometimes a large number of them are forced into our world in mass migrations and then live among us. +What exactly causes these mass migrations is unknown, but may be related to them losing their ability to travel between dimensions. + +[color=#a88b5e]Homeworld[/color] + +While no home system or world has been found, some Shadowkin talk about dreaming of their homeworld, describing a lush and green land with abundant resources. + +[color=#a88b5e]Racial/Government Status[/color] + +There currently exists no Shadowkin government, and with Shadowkin being as rare as they are, they tend to fit into currently established societies and civilizations, with varying degrees of success in the past. +In recent years, however, many cultures have taken to accepting Shadowkin as another galactic constant, going as far as allowing them lives similar to those that any of the other species would lead. + +Some systems early on took and experimented on Shadowkin, in an attempt to utilize their powers in various technology, and even recreating them with very experimental technology. +The tests failed and this was met with a lot of resistance from Shadowkin, many of them being killed or becoming Blackeyes in the process. +After this, those systems realized that Shadowkin were a sentient species, and deeply apologized for their actions. + +## Culture + +[color=#a88b5e]The Typical Experience[/color] + +Most times, life is pretty normal. A Shadowkin can have a job, go to the job to work, then spend time off with friends. +The fact is most times of the day, the small abilities one might have are nice, but not overly useful. Sure, a Shadowkin could dim the nearby lights, but at the same time, the Shadowkin could also just flick the light switch. +However, it can sometimes happen that when a Shadowkin slept in and needs to get to work quickly, they might just teleport to work. +Some people might be overly wary of Shadowkin, some might be drawn towards Shadowkin. +Many Shadowkin are used to not wearing clothes, which can lead to some awkward situations, but those are generally the same situations any other furred species would have. + +[color=#a88b5e]Language[/color] + +The only recorded spoken language of the Shadowkin is an unusual language named "Mar", named after the fact that every single word in this language is the word "mar". +Spoken in different tones, with more or less emphasis on the various parts, or by drawing out parts of this word, a multitude of different "mar"s can be created, but they follow no apparent conventions. +Shadowkin can perfectly understand each other though and discuss complex topics using only this one word. +The Shadowkin can hear the Mar language from anywhere and understand it via Empathy, yet do not know who it is from unless they are close enough to see the sender. +Other humanoid species are incapable of understanding or learning Mar, as they are unable to accentuate their speech in the same way or hear some of the silent, Empathic tones they use. +Shadowkin tend to learn one or more languages of the Realspace beings. +The capability in such learned languages depends fully on each single Shadowkin, where some have only very broken capabilities, while others are fluent in several languages. + +[color=#a88b5e]Naming Conventions[/color] + +Shadowkin tend to name themselves with a singular word, ranging from states of being, such as Lone and Collected, to words that describe their memories connected to their supposed homeworld, like Dreamer. +Name schemes of the humans are usually frowned upon, as they do not reflect upon the Shadowkin, resulting in reactions ranging anywhere from an actual frown to being entirely ignored. +Shadowkin following another species naming scheme are often Blackeyes, not fitting in with other Shadowkin as well. +Names hold a great deal for Shadowkin because of how closely they are often tied to describing who they are. +Following life-changing events, Shadowkin often change their names to reflect the new person they have become. + +[color=#a88b5e]Rumors and Speculation[/color] + +Shadowkin are beings from another dimension, capable of performing feats that others could only describe as "magic", with no scientific explanation. +It is speculated that Shadowkin actually have a whole nation in their dimension, and those that come to Realspace just simply refuse to talk about it. +Sometimes Shadowkin mention a "Hub", acting as evidence for this theory. + +Some believe Shadowkin are the result of experimentation, though them being taken and experimented on is a large piece of evidence denying this theory. + +Some believe Shadowkin are the result of an expedition crew that disappeared hundreds of years ago, even though the first Shadowkin sightings were long before that. +Obviously, this expedition ship performed time travel. + +## OOC Notes + +Extra information, should be read if playing Shadowkin, if not you should try to learn this in gameplay. + +[color=#a88b5e]History[/color] + +Shadowkin used to live on a very lush forest planet, using primitive technology to work with metal, create tools, and build stone structures. +According to the dreams, Shadowkin back then had forged tools and blades, but had no electricity, presumably putting them at a medieval technology level. +In a sudden event, everyone and everything organic suddenly found themselves in an alternate dimension, sucked straight out of their previous home. +The Shadowkin being ripped from their homes happened in three total "dimensional ripples". + +Now stuck in this alternate dimension, Shadowkin changed a lot. +They stopped aging and with time developed the ability to use the energy of this dimension to power their unusual "magic". +And even later, they learned how to leave the dimension and travel to Realspace and back again. +However, as the eons passes, memories became shady of their home. +Many Shadowkin forgot more and more about the thousands of years they had lived, reducing memory to focus on the more important closer time. +However in dreams, Shadowkin, even those born in Realspace, often have visions of their home planet. +Some events can trigger certain memories to return like meeting someone they knew from the past makes them remember in great detail who they are. +As such, it can happen that two Shadowkin can randomly meet and remember that they met hundreds of years ago, often confusing nearby unknowing humanoids. + +[color=#a88b5e]Biology[/color] + +If a Shadowkin personality changes drastically, their eye color will change as an effect, reflecting upon their new personality. +Shadowkin, being ageless entities, can look older or younger, but that is merely a visual representation of how they feel about their age. +22 years after birth a Shadowkin stops aging, and in all of their history no instance of them dying of old age has been recorded. + +[color=#a88b5e]Shadowkin energy[/color] + +Shadowkin have an odd organ in them, their "Core". +This organ is the source and storage place for their power. +Without it, they would be unable to use their abilities. +The Core is very sensitive to physical trauma, yet is very resilient to electrical or magical damage. +The Core can get irreversibly destroyed from overloading it, using too much power too quickly, which is what happens to the Blackeyes. +The Core can not be replaced, using a new one would require a new body, and trying to use another Shadowkin Core will result in death. + +The Shadowkin's ability to fall into a deep sleep is a method to recharge their energy at a significantly higher rate than idling. +Though while in this deep sleep, it is difficult to wake up, and cannot be woken up by anything other than themself. + +Shadowkin can know exactly how much energy they have, and feel differently based on how much they have. +They can also Empathically sense the energy of others nearby and can estimate how much energy they have based on their feelings. + +[color=#a88b5e]Shadowkin Abilities[/color] + +Shadowkin have a few abilities. +They can teleport, requiring a small amount of energy, but they can do it quickly. +They can teleport to and from The Dark, requiring much more energy than teleportation. +They can immediately fall into a deep sleep at any time. + +[color=#a88b5e]The Ritual[/color] + +When a Shadowkin reaches adulthood, they undergo a "Ritual". +During this Ritual they intentionally overload their energy reserves, forcing their capacity to expand rapidly in order to gain enough energy to travel between dimensions. +Most Shadowkin pass the Ritual and that's the end of it, but some Shadowkin perish or become Blackeyes during the ritual. + +[color=#a88b5e]Important Terminology[/color] + +[color=#a88b5e]Shadowkin:[/color] +The name given for your race. +While your race doesn't have a name for itself, this is how most of the Galaxy refers to you, so it's how you refer to yourself too. + +[color=#a88b5e]The Dark:[/color] +The other dimension where you are from. +There are theories that The Dark and Bluespace are connected, but you don't know the details about that. +The Dark is a dimension where strange rules apply. +For outsiders who somehow enter it, whatever they imagine being in there, they see there, if not too complex. +If an outsider comes with a willing Shadowkin they may see what the Shadowkin sees, being anything they imagine, or a reflection of the station. + +[color=#a88b5e]Blackeyes:[/color] +A Shadowkin who has undergone and failed the Ritual, or lost their ability via other methods. +The Blackeyes have completely black eyes, no matter their actual personality. +They choose a new name, which follows a favorite species' naming scheme. +They are often ignored or left alone more by other Shadowkin. +[color=#a88b5e]Note:[/color] Blackeyes correlation with black eyes is done entirely via roleplay, the game will not handle this for you. + +[color=#a88b5e]Mar:[/color] +A word used to verbally communicate feelings, emotions, wishes, hopes, ideas, and whatever, in addition to your Empathy. \ No newline at end of file diff --git a/Resources/Textures/Clothing/Back/etherealteleporter.rsi/equipped-BACKPACK.png b/Resources/Textures/Clothing/Back/etherealteleporter.rsi/equipped-BACKPACK.png new file mode 100644 index 0000000000000000000000000000000000000000..7496c91c55095bc0fed06c6d27196b73ba5099d7 GIT binary patch literal 3515 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Back/etherealteleporter.rsi/icon.png b/Resources/Textures/Clothing/Back/etherealteleporter.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..265318a96f5078f46c8ba9f3f64defeae8c97db3 GIT binary patch literal 765 zcmVmVlN=oC2zp%};_29DCDgG2v?E~NztD24n1H{6`zHbJBXtAm9O@egp# zkj>;e8B{`CP9dC!)qDpN!CvcaD03`(fU`z25sAxxm*r_<>h7NsW~$!;%839@Pfs88bGaM`2M3Qfy&nR{ab&C2qSb0~ zeSOW&&dx}PfgvzWQ;Bk*+wEeSrlP8y9)|1ORteA8CJof9TwDxvX4VT;TgYzVB11RGzGy`x;t? z0NrjEfNHfWQ>hfTZOc-r1VE$FK+`n#_Vy$|yCJe)*zR3W>FpN)q z-+YjS5S*W%j|DI^1k=;gaeMber}-3M7>0PB7oT1TF&2Q5*Uy5#eDdc7X=^YiGsP7nkXi$#jXBKdqisdsV! z0A1G+LSPsM&1MtBxJ!w{!$X1~pxJE3{bvCCu zB!nQ!he(EDkWQz^PC!WbC5#jV0hvq&Aq3@eIZm{VjSUIoU;kJD(YM{QEExpBgR#hX veSJM~_D4!ohR>;qU&Su~F90t9|2@DT&+aor|Fh*b00000NkvXXu0mjfpayKh literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Back/etherealteleporter.rsi/meta.json b/Resources/Textures/Clothing/Back/etherealteleporter.rsi/meta.json new file mode 100644 index 0000000000..e4f5aa0f92 --- /dev/null +++ b/Resources/Textures/Clothing/Back/etherealteleporter.rsi/meta.json @@ -0,0 +1,56 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/ParadiseSS13/Paradise/blob/master/icons/mob/clothing/back.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-BACKPACK", + "directions": 4, + "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 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/equipped-EYES.png new file mode 100644 index 0000000000000000000000000000000000000000..39fafeb1449fe4e7c941553177275a6764af8e1c GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|Zh5*mhE&XX zdut=-AqSCTAN3Q|UFIH`ptbe^i{wGWAPzsJB_|~MCU7!sTR-UqgB$DXh>k^UD(NCR z|MtIox1;?1y1o8P3|pqFc)m6I|GsLS^ckPaC)X548vpil&)0eOxhi^^)x7h)TOIeW zFBHg_xon=d?{oGEOKP`HR>?heh`Tvc*Yn?r|4XI_Uq8(VbPW^~JpFU`k5r3+@!VAp zYh%`g+y6aOcSh~U;^svfRZ-_Roq74^+}UG=G1F&HUXmTRAbYP_-pm$-FH@ge&V(Cs t;S}q2Q?u7ykFCA|4dqLX@J#ddWzYh$IiQwY3Z4vNdAjk1@5Yiy`?t)*pFY&zJ)JZy|?rD;)PEug}uGP&tKIsEKllg ziaw&cIwYHAR@mz~A2Tu-w^+x{{UV)Yec?6#m*txnZ|Hnl6UNO@Q{ODWw=_w#;pSqd z1!X~xpMJHSGr87ylW$IG(5_ukcD%jJ#SFL4z0GL;5$M`-Y@WI2n}SC?I@9GJZWiG4 z@;&@LXyu;8qK6o-tvsuElm}?X-pHEkE3b&;{C|F7d*Pg9u2v<6osXLn`LH zy|vNnaDc?okHL-^f=p~oGhMVIc5#<~V%(H^+V~mgF9tvH8m5Cqp3|KkvN0tcd}!q{ zM@DC-p!5IzmfyYi6`Yu)%m_3Y2+~u=Uup_~*8ZiTeX0rzM`)vq@+F^!T;LlF4jme|vs0&+6g){B!ZWU29cd zzWWzc8auyc{;E}nUY~APd8rlos-kAn)^GLd4wKew-E4E;b5hOi6PD^Pvlrf<^L~Bt zxs%&_RW#3+FV@lTozM^$C~~lRy@~Hsm6M*DD^_Wh#4i6=)058$aVdzLv(@u|Y41h3 z&U^env^U;Tp6ugr=G%!SLI!TtDMg$MY?&B3o(uoBZMR#qUH2SNJzr{sXPU1sgBFm@ V0WtsxE(K2pQJ$`TF6*2UngG@6p5y=k literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..26fd8e57a42b5079a1d9454b1601a85f3f7e71b3 GIT binary patch literal 397 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D%zbv<1iLn`LH zz2)e2*g@jx$8g6CK_)h)$j*=rCHiMhF{ezwJI7G{1!D}WP2&ZlC9|CpnHV=dNQzmx zVB5xx39J6!H``nh|LnAuKNHYoAow@c@phxm=|feflh$kN-l={4^S;@>J)9qZwodxu zx%uWA)AuLWwO7vD&uc$Z&FEOmVOLw5WS<#6!asLisxl3l_k3;T_I--kxijo-yeFMo zw9#y~_0-gxzj+OgwLJU~)_>M%Qb}p;szn;xp8eaxc(K-bWVyu5G_!9yzV``kz3Nw|Ahf1-bagXRH5) zcei(547Zk6x?Pf)^dCmKEqiO3=NOJGUY6*wY%xL-3_RXFEzq5 c&DWPf3&`dGnF9ouf+vG0Pgg&ebxsLQ0Bh{1tN;K2 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/meta.json new file mode 100644 index 0000000000..555efbc7da --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Glasses/etherealgoogles.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "JustAnOrange", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-EYES", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/equipped-OUTERCLOTHING.png new file mode 100644 index 0000000000000000000000000000000000000000..e8618fc6b7df9fcb25976d4d01ee12c7db38d413 GIT binary patch literal 1142 zcmV-+1d02JP)EX>4Tx04R}tkvmAkP!xv$riu?Lf_4yb$WWbH5EXGORV;#q(pG5I!Q`edXws0R zxHt-~1qUCCRRf)s6A|;-i6k5c1;Br6yd;Xt$&jo}=g{fv&6i_wG zNX27fHn%GFyrK_7@L>{RiJ5vLy_kV#ece+h)m?;VdH4NU14`axfKMcjGTpFiCo7lF3yD zBgX=&P$4;f@IUxHTeC1d$WEE0hc?#$dfJ^k|X(P355dien#Jv1BP#b{xx@Qt#h0{02!Lq@(pls z2uu_yd)?#Ry`6LWx2H9~ALiw9)mX_M_5c6?24YJ`L;(K){{a7>y{D4^000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j>YA11c0pV%qZn000?uMObu0Z*6U5Zgc=ca%Ew3 zWn>_CX>@2HM@dakSAh-}0006_NklZ&eO{^M^Ka+(6yh{bDPO10E2Vi#(vFbg zhfCy$z&(PM25>Alf_bue`d;^ZN0_Z-s5Dw+9RrXAU>-)Fn-+FjG?RM8? z1|u~1;mm*>Pc1Klztf`!8vMu0AdaUTV;iDW2om;u?9@U9{?q@+Q!ta)#9M~c-{DAt zM)B{*-aMuJH=-68!he)^b1MJ0@|T2*G4*G63bU+|{kAh|<-% zVByw-(fkI3!C){L3ic}rYyH@bN=(&wcT3Ydq3&$G;cQDA7kc$Vp}*@(a#OBkq&FjipB=|~B?-j&Ex zM}GI<(t!29rx!^Ifx0z4LBkwK!yu`10at609t3LaZ6+RyI6%2LJ#7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/icon.png b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..def3161bb0aa157191591f4a2c2d4600ff9d1708 GIT binary patch literal 3072 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 YKsE=+5Foe|JQ+lJy85}Sb4q9e03B>vRsaA1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..99c96d368ad662720c069d13f2110606a75f60db GIT binary patch literal 242 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|CV9FzhE&XX zdut(s@4Z{Qzbx}!;Jtc}lh$4z6SwREX=7m6d$;OW zkfDB#`QkrM_c^?J^S|d*ZKK$3uBq{h_Hb+6Pn!Mzz*=jD2lHzf*Kw5`XO6vG57fk$ f8sVAd>&u`8WOIN_0fI}xlR=cHtDnm{r-UW|L>OEz literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/meta.json new file mode 100644 index 0000000000..57a0b994f9 --- /dev/null +++ b/Resources/Textures/Clothing/OuterClothing/Misc/shadowkinrestraints.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "JustAnOrange", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-OUTERCLOTHING", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/darkswap.png b/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/darkswap.png new file mode 100644 index 0000000000000000000000000000000000000000..3c2815e8c2466ef7dbdc0f585f2c55671b6f1aaa GIT binary patch literal 5595 zcmV<16(s73P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=TGk|eoyME|i0E+L?SA;9I>%uLOAKzg%+a~w{5e;AcGV64cXx})?&}R z^-MQxVd81I(^&?yJaIne{pElA`Ej7VhfHMI{o7Wo%gNv@L!Q%b?!qB!3EU+!7dF_QeuwDMe#VwJ$I+e}Bgb$b&hs?@CSvZ$VywplPqIGRWS{I@wiY`U z{L@&u^Cq~C3v7&TvPu`cPdKMI&CN5}o_k$%_cSSlBce|}D!G(WOEc9~Pp-zAYObZ$+8UEK zMs7UaO5;R#-S^OAPd)e2Yj1<_8DYecMjmC<(adzy&oJXmGtV;XBp()`w8Dxjt-Q*r zt8GGU`yF=NY3E&b-R;9`$E)U>&wlsZUwh5IyoSTf@mb)ex7RqG^Lq#@IEnHZ9&^Fs z@hlGz&`v&kiVeZZb4EUUsv|3+W0g_PnN}XdgMqY<+kW`&({sQ1&5-y1Q@^>do-=aY zKjAqe*ZttR-~IN3*QW4&C=VZiO=&2jKA!aBhE*CgS7#h6lAk-S!A(9d5`L7vTS@k=V{_|D ztc*YF8PrVA=@rf&ESywY4}s1(>&ssA*>e-_eT{sUq0R2R)+UihntP@`TI$nOJ;>U| zV6wEP2JA5vQXo0MdFsJxaA+lOdTfwcfX_Iw9y z|IX3;Z~-_ZdN5=npyuvtyFU8FGPZZm7JAvCVYOXC#O4)*+D5v7BPQKXV`KgJ4Ya~a z2^4H=g;u6-{sE3{PHWHDCZ=`s?6)Zn)-W~p=5lX7D|Bo)CeMsE%JIg`%@4WRF|(A% zamuWzKdn}mQ!6z&SIuUz!C7KwF~i1&c7VHV%hs`S?)V}0Z0j|mvol!&f$*~I3R%5( z&*^M!HU{xAE~2sfa)NUzIB$f-RvtfNYPPNB>JE<`7!ZoJUPH++V7<}JDG!~oiW z1c&5;5l@z|PIrTgC#L(`0jUR5Z`ihP0%nD+;Gf$oL@b$=yaiPNkb^cCD^1ISg>wEN zz}9LNyd;O>=nnhu<~`Jp6M77VRN_0tThQYtuH3aZ#-6niTRp!AKO+;{8k>=^awk@J zmZOMA5dP+~JI-i8hI&o*QZ=jOu~9N$wq>onFKF z`ilFI=7T^nWY;+U2>#5*%?vg0DK4+5n%^)Lm&J>gr7+;^%yxYSPZ+hDT@KB zZDx@OSf~}XnibFah8bYk!M(9sgrLYzFxP=6b#^2ehKnkvpvh~dKG;T!^W@q9zJTH! z2pLpWKOjvwEiFl`2PL7ZR%bC4h&YIED^4Z7v_GKBkV_+rS~5e=6VOy{cRQ~vM2?=^ zbDAzFZU|RcB*Q@BN^w;;O;35ktN#pNC_@-lGliy^#^qYXE_I<}rVeQQ zun2bWY~TkANPO-As!ps73Zi=-+>twR=Gh;+!9gSS^)z!Kgc-%7Drk8YAF*#(i@U*1 z0+UmqK14lPxp$pxm=8z}Yhx@R0XhmDc}uRMTcwkc*0%ddT<1BiYIwMI&WyNGERCsT!eWs~ZawvXPq zxqU92({RhyBhg9$NK15SiOl_rfhv)GtZk13I|2-w9nU}&5=w7*_R}?6PTJ&BGm33f z^_1tF$Yo<>58+ILpZZh>tTu@pd8w8qx9pSV;c3S})A_LgwqvzNLT_g>x}IPp+0Xnv zD_+D`k~RFIAUU}(M!m^;a+PU}B$e}1!9cce6))#YAT|{Wjv(B=pADhru*oApCJ8I* z=6;9jwh_$eJRq8dV2S5o_z?I>slq5I9+$=cWRN)9OJC#rIWDX`$#^PEQr&hL9k3<pT4mH5gQLnVuXTYS2A)=cv8HwUfXMDuk^4AM#mh>v zXc`wmGqZNt5ab`!_DWPg**rq)0jc8*N{q6tqPELlCvqXR_sV~~*6=Pqiz;_0At2&C zmt*{+E`ZdV)fK*dUOD}Fgjs&kAxDN(GyZgm9WdXvR=t2Q`0o-Z+MNZ|_H9D93~7XP zndqIdrH?0tCvp?hR5cx1KdJJldIWN>+z65lF*&J<;VlSnExA@81R{tkBzuIJqxL+a z`g(DTU^19Wv%H<;x|0ZF9~-}^hccXpWZ^ojv7>iqR%iMKM3 z5~!mFb6M&wV7^~`T~oS|lJE=bMI9w7Kr0%JN;3gpnWPB9K#Vntvi;)$TK3(yP-08NWL{`PJ#Ex6`B2|7e-0 zcIBz0PT|ki6$NEv6@JBih#Ya-Pu-jFbGwIqjeLp1ZzH0E5|dQVAP32v@ghytdY_fv ztIn%Yh3Np(C`?&!gp4zy#rR0Y0xK)?BFUuoM2RTe{&X-Gk)9|B`vxQ+pl-QM9Sh~^ zl(a-`BXii=qG;xXI2qq)_WF4*t4vKzvMVMEs^jaOh3Q-#Pm*G*($FZ^lZHf8nZzP2 zG5|(m7Hb?;yC(7A!j6ywAz7uoFuyv)Xnpo$s7Kf{T>E_oo3JN-LXM!w7F{#esY$Pr zIm(R~-$y|rJPfskNX|B@Gt!@bRpC1HTJGM-Qo`~hmIlYl*5bZitGMX7MA zENV|%l`QHvA(*!+h%x%lCF<(s=qAdxlZGZr&F^ku&Vh#Bsk#!J%{}%uI}uu}O!9N3 z@@hROObm3)4eo>$c}Fa$t$8H;@$8ZC3xGZn-p?WlPkD;mv+5WC37KkZnluJOQi`{E z8bs=o%*bQ5_S-vn>Ky;7sg;BIy;=Iw>8FLDplSIf`P5}Uk`J(>gjmuJ{@rylzqOqp zsnds>(W$i*B?rl=norM;%+;GB_3SHwDzuQk+r|9az9CP93+g-s2gGA<6~>FY6`3-l z^hclNO1u%?-_$+sKr8e&-o^MU&`+2npnE(;!+d$@>ve3?)AUZowZThpjQnJ`E}_AS zO08(cXL@(4mYn)oIN>NF;G3bU6ap;YF*Ql!jI^U(!`>#HK(*9Y!^OWYoYM-$dP0$J zNgx{4*HXMz6`yJ!da=4c=ylr#;Z$Ju_SmfQj8&=woQ2;M@h$@)Oo4VUCpg_>eo71A z6%a%7BOFv7V;&WI1ON(k- zv+48dcVrYNqF@2})rt}vZ;X_=BqRmE&L%HzrEBj-!4(H z9OhLOFsMW7uSN5hhV3u;Z~oox_ib`FHK((h+;yW|hnn2z7F&iR4hDBPU$2r}^YR?ZE z@Q0)M%Nx+I@a3!JRX)rD5r{#qh32N1=y{2B*ZLc+W6GShI91d!?$8#a^r>MRD zsr?~d`|O2%Z;{;>e|gQD(3)Au zto@ByI|TPG2+Vd3dwhFg@x0zwyx&%o)BjYfE8b4_s8vzr&Rf5vLM%9=*M8}iy{v`Q zEGkrPov|Pb*b50F4p9++ZbXz_@s)1pLQ7Uao>3SBj3v-dG()FCDC)DU0`$<8-okDv z7)DJVPZjA}0L|)k!chOnOAy3_G=M=MRZC42A(&}j&9tHaN8;5>`}5KKs~i5^HV%7{(vRPGYrHRniHe2$V9zR#jqRgg>DR5ktj4K*iV* zGO;jI1|kM_Mi5g)6tN^I63Ns#C5aNrv74A7ubsorhn?KnF1bgt>L$MP-Q)ND-A4>a zNl8gbNl8gbNl9&@6k#vXoBm%58wfS9DodUeBKka=`aCY360JO4cG&)Pe%qoYKZ}3GC-lS zH{O6906KsyW56Pfsq;FOy&(q72VRakqXAF@gtbD~>Qr`i-5kNlEdny1PfjW4_vq>6 zOdt|oRq0K8bis3in=v4K%r1$dYRyiC?#)*LRZ+On)A(@TGy`jgi_F#dS+ z)Cb1Hx4%lfQs5v$;PC>Z&65GU6lPHYZ(T6j)Mjo5q{-n-guq)Bj5f6~&C>vdxlt-X zo%e!qrS`2h3^KDhX2b|xO}ov^V{YFIROaW)hrZA1_4>;0r2<@IGuu^T=KrK2@nL+@oPGqc3M0Q=wZ4`>m3Hw7)`JNjHtr{8UhV!YmcV^ElhW5cW6k= z)A*Z>DH7NnB z%w|LsV4MfMHnk;k1DeccglI4>170(F1CGy2bHM0CKspE$*yV7y+x2~4tJR#GzosXD z`Qp2N#51(Q8xS2BxdV@93Wx@y5a5Y6fEO5r0Z+66JisUvcw!9*4n|jl!C<;9O{E|j zc;XBQ4#praNC*R-=oDB6MjOFjkr4vCI1F&W= zaB^>EX>4U6ba`-PAZ2)IW&i+q+U=TIa%?xQMgKX98p2@|R4`ZR2Sjg1Y^X8zy5m-!bzG5X7dC^?msE`CA@#Z|uK>+@&cpXIuieh>WF z`Om$yC`AAGROi7$Tm?49p!de2q&u@<}M zEql6dGZPQXlg=`j<&FJo9$)_H@7saa>S`j(?jN>dUCyZ+tXk3Qzj+FW*!93s+y;L9 ze*M9*3=()s&79d_z1kU~Mf-^@Z?>a74@N%2d0ECi048GY$YQL=16Q&>+GHarSS{Il7@ZG29zWL1%_kY)K?%8ujUHAXt zIis$7p(d!jA`Lt}gS0`)i-(ufFiVf%LOgzg7;>uUYWW zQd;CA&W3~MSTPKzR{=w>Yb-E@dkwyX`1|w&Z?@Jh2?>yUm!|K1#LC0TXR$yu0tj+f z?6r(J=MZJ(3B#>bjSL;V=Ljr>wVdnMRrSWcOn#KQSEhMLKLaRPMT3{;Rs>TunIdbH zHlxhddSArc>OS}6?Ob!kI#_|NVrMGR4sNpGFj;2}t+4jCr@D%wRqjj*JSC7YHm~H`I7Lc~t2$SJW9=>8BI~obx4#|Pgs|6aaQHcj4P&e?# zEaxi;|IMd4Uoc#ZgBhFmy{yp}+f9kx`O$QvD1|zN96^f6gxO>f%*+y-8v;ie_KFWI z%gA$8nMtn6ih8P%$hRepx@J%tmt%O3RcF}WCnR|+6qi|_ceoA`*pv)u)~-HQ-iUE- z&1y4`MGU1^3UePn9=nBUxL)l34;&=x+yO89alKJT>@(Ugqq}R>oc3U>gXau;3G&;8 zzgf2h>z!Af_!6@<0zjt7oYIx;(n3bfPKfx~{ctVU4HXAQ+=(z% zYpk(=Gk=xb7|S(BHUvj*b|JU3k&u{L)cu3;N1t+nb|Ki}bwZP{ZC>Y9G!)Q4TsFJG z(8h$qi6^J470_uk&H&y>HOb%E(ql)4+0vMd6KjbYITc|z?P_)puXgXX&N zeNLH)!USwE%kw#4`_bTmKCFW%vzIqpZ zArzW8UWS&WZt!&yjStDy79z}&QeC~9x}=fNh%eH1((tULTYVvi)Xt;_#cYQ~E#KFe zYx}uGH(6-nfoK}mub6a(<^oof=H|TWCao(IkE;bn(+GuF(wb6M8>_~vkH<5KXUyA& zk|yuE*Od}Puz3pR?XoT?C-=prk)lFZkr!4o+tJFJaONbV`&iMPvvi4=Vz$9PCOhgE zDCB1D6@&$5BE{>xV9F7Hrf5VeR_Y|C2EfA&up|)W^ZV;(2HlJ&@{2JTov?+LM%OU%V^yyEo60;^Mu>O#i~qa#*P-4 zMKDBY=%?+Ym@pA0UomTN`4~lmGYy1004#?>5L&lnfJSc3prNxh!`SjIYviEc!}yV< zUrE|LbdKKADDD&s1sKHCW=YHWiVrDGNUjNh3vLxHBY5OlQa1CVC|?C-R`^pv#qAza zW2LMiqp&|qI;_^IhT$q@N;EQy=EtJh&gP>Y`?GrV_lm{ zzTv(Ts>D6GMJb>Vgfs|$AVm8P=4_EuT68lAW5z{=;aHl|h*e^z=0KDrByg}d3-0uz zO7+6+52)0-A&=&o(3 zYcWRgU~AK!77V&Ir5I0m`wgw8rYhjPM}-s#flwgUteU?iFh{+M?OIR`Omtyu0Qu10 zd4=$cO51?$e+q6W84dX;m)=X7_eq*};3Mu*ez5|i3<(m^aEhh2u7esSzRv;?fk27q zRXWJ~J5GMOHn0WG-suL)D zS7K=d$f&uBx=L+;K~fs@8n8ScW%xJ3!n{)!AHssd{el5LDPAGqv@>yH9@Aw4jrK2yWR?PlxelDbEEa^pj3(aG z>G@$WuUw#bJzM}@Z!WlQF6i1?Ktl$oARUl8omUtQ1!)h8u??>QUHnbiE+NP5dgge1 zQl(EDPAx*vtq(yq?_1yQU~gP2cx)$cm&B!`Qy^h2{D|A#BG5UrAc$O{!fl$waIJJj zl1lKJx@?fJaw=LS_2`hxEHevUa|K;{Uy0=?p`evr#4uEY=hdA-VQi&cu(_@l9d&oq z7G_RUj6zugmPse&fV3|D2BdAOiJ))P986ob4r!8It%1Bx0XdVN2f;k%5a;Uvp@So3 z*8cf=*3J^VNHrWCjHY9#wO4U|l&qbIQd86*rV92RMbOkSE34W<$=eK<+t+coZW@@x zB0q^)3x-Wa2`uZq1J$GQ#(G@|nIWj(wRqMcY(LabMb$0=qex`X;tVwh`5Q<&i?Vwi zL=}-*sN=sU%SDV=GyWjR5KCnejKdU7=aqCo`R4A2AMRxxT9fpoSsMt~1ZZoI9e+}j z0aeZ21JjLQ0wUib#neW)&pw)Hs&ED$_nQ%i*6Uv<_k)HhzzY% zHhY;jk#XO8jg0HMW8)DS8W&GtVeT1=AoI~DZ5M1rPeQn5m77A&Y}x`<(R?c0xh<(; zkW58MfhJC-h8T;75MeuUunSr*tAy3Ee=3ePr5*A`f2fXzI4>m(6-iSEB!3%#N3I5a zqU`Mv)ai;C=@J4}RAB8&IH#;#+=z39nrtb>JJuM92{IdbO_h}9{(DCJ0xU_00@Je` z(TsxTo7x?zYimF5QEK|Q-)Vbnw&tVTkh4$21^92A7*w<7qn=|$7qK`7)yfiBy4uER zcml|Nbt-H0@uhWD*Q8i1<`Iylsa4AXjLNfed& z$Y^Yam;6-Gb^wV<3xGkD?NT7%?lBt)m0Y=5J7L=4FKtz>OleB?@>iBK7M4w?{4%OS z_G(<}f%sdyi=jgI_KCtX)y#rqm+q(ge~K5}jQ#=;zMB*g*C7cKji8TOr~_*(n2I*6 zsy65pKay2`$P=?Jil~-UTA~K9>ZX@p@+3vTE{Ln&^{@xr zQ4EP!F@*t-E=<7Sn^OFPPji2H&yGKDYlv0R1ROP~p^=PsR9RkeF=xsFf)<&inp3C- z<`d(;9I1RiCoy*vebg!7>GK%uCt!R*|7(`aVTIfmFnSCq^9d7ZQ&FsFZ`Pw*D^P*Z()^jne9aZWNjDuxJ(m}A*$(y;f&vdEof61TU zK5xW-uaQ1Vr&Cjzr-Ffu^rdl9Cq(#gKd#G0eQ(cO{wRd-a6gBq;owdcBAbm#|wF1iL}dT)*P8A6oa{ zxYPUzvFbe(oJuFBwR^}jbWGhEs)3d1{N}H&<^8b&e6*{c`OyIxj}C~O4oH6OfV@!2 z&46K9iPaF+1(_05>W%322u}}}`HSoO+2`xg=QD?zKNOoV4B}&-Ao$jxRrT4N6xdaX zOgc8w&IU2GPdbUiqH^x%viw_Be%~?QZbeHMN%hgw>ED~fzjJg)kq`r!zX;A2DyK3$gqyXg_c(Vl*WX6aQ#OZC&F>ji zY*a2pJ}>R`?3NtRPqU?wr*;ta+`+_sM-L>CnXUXGjXk$L6qYj0XeXvn3(IQuwkLb* z|IA?j!m#FP+#qNGlF88kS-y?=tA>?%*RgsYm6v}S_hqf-^-SsyNS8n<+QU)U`H;v? zvv&Re*m6Am^5)RRM8{2YIp}fip98`CGX(wD4}|k@0-pH~>dzts5deU~0004mX+uL$ zNkc;*aB^>EX>4Tx0C=2zkv&MmKpe$iQ%glE4t5Z6h)`X$AS&W0RV;#q(pG5I!Q|2} zXws0RxHt-~1qVMCs}3&Cx;nTDg5U>;tBaGOi}v_(bA4rW+RV z2Jy_MrE}gV4zrS^5T6r|8+1Y9N3P2*zi}=)Ebz>*kx9)Hhl#~v2g@DIN`^{2O&n2F zjq-)8%L?Z$&T6H`TKD8H4CS?zG}mbkA&w;^kc0>sHIz|-g($5WDJD|1AM@}JI{qZN zWO9|k$gzMbR7j2={11M2YZj&^-K05bWxZD8-o^;8O9LY~pC=`JAGy0|+(0>c`thv3l_Hp_EWT>m< z8{ps&7%fuvy2rb_JA3>0Osl^ibjWh3|1yVW00006VoOIv0RI600RN!9r;`8x010qN zS#tmY4#WTe4#WYKD-Ig~000McNliru=K}=@6&>wM&p~(-Kq>@FeJR*ORz*|9_&jWg(>KnTiM)uMIMGam7SYlxu~= z*yoGJ!)bMG*PTCo1aup6oPphA{h>1zS->%_`@N%@EKruFWJU)OdJ?cwi`1%(1Hhbqh ze*|s5k{H4PuM9(&zj9=7cb#f1d1T^opWnmpjl-}qCZ3Q6m{?pf z-G^VFV0}koHfV_}_uShU2H4xq&$YJ=8}H;kO`x6Fxvh?L-~ZhLlQc{OO#**@jQ;;* z7rwliNEVp+G^lle%Mo>0&EA9$D0SD|&1z$uSyTg*02RTKM5iuctX1%uD2ye4q)`n} z0#pPm6sK{8v5|sTr!dy=Ra!Mb2~ZK7wW>{_P$(1%g+ieK_z$Q!)dLD)vdsVh002ov JPDHLkV1j{j)Y||6 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/shadeskip.png b/Resources/Textures/Interface/Actions/shadowkin_icons.rsi/shadeskip.png new file mode 100644 index 0000000000000000000000000000000000000000..b0f1bd8dc8c5fafeba9540552a433b1a6e81ab7c GIT binary patch literal 4114 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 QHUIzs07*qoM6N<$g8C}qDF6Tf literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/meta.json b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/meta.json new file mode 100644 index 0000000000..f642565f9a --- /dev/null +++ b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/meta.json @@ -0,0 +1,43 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "DEATHB4DEFEAT#4404 (801294818839756811)", + "states": [ + { + "name": "power0", + "delays": [[0.8, 0.8, 0.8, 0.8, 0.8, 0.8]] + }, + { + "name": "power1", + "delays": [[0.7, 0.7, 0.7, 0.7, 0.7, 0.7]] + }, + { + "name": "power2", + "delays": [[0.6, 0.6, 0.6, 0.6, 0.6, 0.6]] + }, + { + "name": "power3", + "delays": [[0.5, 0.5, 0.5, 0.5, 0.5, 0.5]] + }, + { + "name": "power4", + "delays": [[0.4, 0.4, 0.4, 0.4, 0.4, 0.4]] + }, + { + "name": "power5", + "delays": [[0.3, 0.3, 0.3, 0.3, 0.3, 0.3]] + }, + { + "name": "power6", + "delays": [[0.2, 0.2, 0.2, 0.2, 0.2, 0.2]] + }, + { + "name": "power7", + "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.1]] + } + ] +} diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power0.png b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power0.png new file mode 100644 index 0000000000000000000000000000000000000000..ab370f753ed3a801484b81c74e044775d9038a62 GIT binary patch literal 853 zcmV-b1FHOqP)EX>4Tx04R}tkv&MmKp2MKrY$W}aj=7kLx$>PK~%(1s#pXIrLEAagUO|T(4-+r zad8w}3l9D)RvlcNb#-tR1i>E=R~IKm7b)?(q|hS9JC1vJ?|WbFz5|3-jj3i}0#G%} z$RrbDzOX8WUg1X=0h&RHnR+U_n1ko|x`&VNcX6KOUH9kcSBfSBd?N82(+!JwgLr1s z(mC%FM_ENuh|h_~4Z0xlBiCh@-#C{Y7Img}@eki-&FNJE5-2CAsRLY#Ju6cZUbPk8u;9ey9{yw(t_6gvB2ClTOzup37KS^(P zwb&6bunk;XcQttrxZDATo^;8O9LY~hD3yTsGy0|iFn9~}t+{>coa6KX$WgDBZ-9eC zV605p>mKh8_xA1Inoj?I0L{*FWpp6pMgRZ+24YJ`L;(K){{a7>y{D4^000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j>F_1S_CX>@2HM@dakSAh-}0003jNkl+YMq9_y~gey>0JClu>%C5=`7$jj^d=5u~i zQD7KKrvL$Et6)c2*!C zFsJA5)wF!TA|D_h&`4iCz&D@s%cj6EluiKx%2vUqXgUS*0Vv4TSIYArsxKb^e^wX) zgO9`rNT&b+O^k?LVrK>N0dso(UQNpfEb;;J0gd$K1AOy2zibK&L+KPCpllUvil$Q_ fAAo{f{gLYfsQt*t#251+00000NkvXXu0mjfSYdHc literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power1.png b/Resources/Textures/Interface/Alerts/shadowkin_power.rsi/power1.png new file mode 100644 index 0000000000000000000000000000000000000000..d72965eeec8bd0b17a7e77d13550f41e43b06233 GIT binary patch literal 938 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/meta.json b/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/meta.json new file mode 100644 index 0000000000..b67f4f3489 --- /dev/null +++ b/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/meta.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from S.P.L.U.R.T ears.dmi at commit https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13/commit/8b4abffbe538fbded19b44b989ebc6808748f31f", + "states": [ + { + "name": "shadowkin", + "directions": 4 + }, + { + "name": "shadowkin_stripes", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/shadowkin.png b/Resources/Textures/Mobs/Customization/Shadowkin/ears.rsi/shadowkin.png new file mode 100644 index 0000000000000000000000000000000000000000..3aec926c5e273471968944a48d6306c1cbf5d59d GIT binary patch literal 9242 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 N@^tlcS?83{1OSASL8brz literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/meta.json b/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/meta.json new file mode 100644 index 0000000000..4731b9de3d --- /dev/null +++ b/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/meta.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13/commit/96703f76bccd8fe6a96b78524efb97a9af661767 Shadowkin big fluff made by DSC@Cabbage#9633 (561159087765848084)", + "states": [ + { + "name": "shadowkin_shorter", + "directions": 4 + }, + { + "name": "shadowkin_medium", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_medium.png b/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_medium.png new file mode 100644 index 0000000000000000000000000000000000000000..ca162bb1b05a50446fb54dbae2e84e4cea5876b5 GIT binary patch literal 807 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`0N3h)VWy>{)|yLa#2zI}V>(4p6_U%z_wYXAQImo8m;_Uzg1+qXAu+VuSS z^GA;!-MxEv`}XZmo;R0Bytse={)Gz{HgDd% zd-v{LyLLT({P^L+hnFv3K5*c``t|Gg?Afzp$Br{+&TQDQVcoiQYu2nec<|u9efu_U z+_-k_+VkhnKYjZ2i-&i-WB){j6Arsu!!~tre8~=DNMdc9)VxJiU7O9RGE)1FL#v>+ zMqPY=;X#$()AQc1sn#%f#PahD^O9IOUe(36Q{O)45|JomddzyrZvD2YUZG33OMQ_n zY%0&-UwQRv|M|avh3`2E^&b28Zpvns!U9I|fE^9o8ZjG`H?*iN5zgQeThe6WG*H+)pLa^n-8;}dtQPCDgsL3yp_ zvfsZFUaW8Lf5gvTWv_e5{hgrWG&z~3z*3WWGXK5{t?)UPQq}OnODUjh(e%0J{#5R0 z;B=2{DXup=Ta^^3$h7=%);FV|!dlkH?vYnFO6lgFeQV)jVA>QYGHLU4mc!}}%T^_t zaP?>kKIl~ORKNds8f&E4&(r=tx5sE4_`&U!qg}dtcB}(1vKTyF{an^LB{Ts52cyJN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_shorter.png b/Resources/Textures/Mobs/Customization/Shadowkin/tails32x32.rsi/shadowkin_shorter.png new file mode 100644 index 0000000000000000000000000000000000000000..979e4e6b26c4f0f65d39fb7b1789af3e7c6cc35a GIT binary patch literal 630 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qt-3{2dfE{-7;ac^(=p4Lp1VE>Tqa>OZc+o|OT z{JhHcG2QRUPgJ%xi`Y1M&wkO)JyUkb|KI)b%bar->~oDS|NQ?d^GwCEb+3!0yZ`ID zz`&Py!!wH?f2p#4`uXRR5-U#~vFhFY(<@7+MXQi#(ZfX@-xQkqlqz_S-KV6HI^nt()n$Jj+00k4eXJwh8B-_hxOaGW5@7 zdDecAZqi}8X0jU8vF%a_~DSDor5x%sA#d0q5BBe{N6 zxqjvmY|;S1vc?JP5Y*1ZeVKYtOQ7ATb0=b~g) z>-wkXfneTs8A02-lXF;hbzBsfr{wITBDDW%{!HFS^3Ol-?3?@ktn>2A8~^>yotSWy!5++6#kLYOJ|YVDTr`_?yt1@6{*M zHcvCP_h~O=&T|v%=j?M+wv_Atl@V^A?Q(X7tgTI!+&_u_=S7yvlTS*VxI6KH*@C3>FcUdQ!-Hk003$Yb!9{R9FFHVXA4oN9Qa6p(5rDhBP zFRx!4(P$XZ^)$4deR39?sLcNeNH@=-ApM7VEpQWM%2pdQ=Xf$3yk_j27l;(aea>ui zcTW6sovs%olOaE>xl+Qucuseq*Y|oq=au11hD|aE)9`0~ev^%OcOd zF?q^Z=<}Wa6Kj)wg zelBqOh}&5sPWt|#d7^Bl{xNCfwishQY3A};cNF<|IWa4~T4?CPoec19btw~j zIOjU11{n_l@7H4wOW3!+^3*=wKCX*`U6>tX@YW}J)rPm8BmexFZx5BKrp^YTPudTD z&*ZOM)s^Zdl)Ff`HDkz4bGGWHKs*X=$$LiigLD7#Ir%0S!D&RLpZUOm=j+>=a_azp z=sg?%h7#C@v0+Mi6AhQSMDj+9sTa7vx*;p;WM_Uj{k7)8x?sE1-2gdttvSFmDHud81f5J?WYf#? z4I6)~l7Z$du}Y}_wFhcB$Jrt3E2it%qN+~Wm`o2Aj0Kh4y49q2m!mcxV8UQW>-2jJ zq^46c2enaDd!cSdD^K4{m>wb(^4crnKDtWmL5Ti;!D zYiQRwtyI)cPE;oc0QaG1-b_>ZG4VCwrQx9U+3aK4sdg>>=Yhxh&(>X#{*u{sRhJzI zPFBkh8Krl`^zT~8xlKs*<|r57*p+t(RreUsH@*@!aI&q}dA5E1v{-OWB=ysHmdftp ze#mKBczl+oYFb3@Cz3MqGcEGh_Rc1g5K-Ca6eY6f1FGL@QS*>OU=Blhl81xKX9}hO~8Ep%>~~ z*8*tMV?%zZI{#W7C7tAXqWi9M`EF9T(wIE8IijPZbJC%Rf$hIBB=hE3@$!G5XK&mzK5f|3MQQ<&r$hLhwaZ(CG&58T41uL#KOMcWuEm=^MGGyF<86Q zoJ0gk1+j^_a<^|XGjw9a&v4aPQ2pP4JbnG>;gRzX8ro+!xnei)nvXJDE!twAKf7O? zb}TL2vM-Pa(O7U>TJBZ(QR3vSlC%M;)W~L9$AgA!loC98b@l$^Xrf*a9LSw{X6NS< zRS@Ii1yxj=Z#>w)X9t{IW_bXtsO-0PFy~a4i2RRs5t{Q%$ZV6f2~IZWcdFG=mcBgI zymKsK@V;b@_p3K|%460Of37TwVTc1OF*D+Pg(+#@4AzJaW|Mfq=CY5B_KgPNlC#NP zCfFtL6zH?=aQqE&=~&H7x8nx4a3@+Hx30A=KAF95 z_nqy-YXoopY_Ah3k^Qu2{aUe3Mgo6Rx_XjVJ>+TV*muoJp}{5WsPR|Suq0L<0*%zP zN{sQAnBv&Fu{6etD^}`heo%_Dxb>qVwW^cm zvJO&v(g7n|G`VB+B$~C9M&C$}lOzJITh_4ecqz>|>`k?u5H6EKtzJoTYemCg=EY%+ z-rNGp5X1>T0j-wb@0lerzvg-5)r3hC@1h8hEPM)boULs*YH-$+Vj}-ekyZ?0AhFdk zOqp3%%jC*UBH`vJvFD2c67_wz-ptVuizRjh{p`J~?k+?-WdMa&FpvrLh?umGbl}@w zP}5x~Sr2P*K_}21Tx8NWBR8@vE`}ST7UXKJP|!cJ8W#U+5Y3(=uGS^I!I|3i(+b&0 z+VPB`@WbxF1Lm*l6=04py@83y>Sf1V2>D6GEN*MoMXv&3iu%THw-*`OG4

Bf?!# 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 uhn<6_LDDV~o{hlc|I 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 bVY2fd@2#J`H)&^900000NkvXXu0mjfPZv=2 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/appendix.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/appendix.png new file mode 100644 index 0000000000000000000000000000000000000000..0d2ad309c74d012db5d5dab59a953f02654d7a5e GIT binary patch literal 1609 zcmV-P2DbT$P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U-_Zk|ZY#{pS=u0$PFNutxX>KE5BBl~uh|bWcx@ ze;l1erYMz=#Cr$`^Pj(0_zMqZ%Rwz=FYXbK7A;)S$?@^bn=CP|-B-MH)6w0E!mB^7 z9-%$cGo(Pusl3ON?MPk>@VlgEda^fqxrgCb8Rg3nqrK2KyN7tBa(mYrd120BTWeJZ zK7O0jHI<_W!v+u?NyPVvh~2Qz0gai=arU0Mk6EJ|YalEfoGFYh)}T)5 zazT4LvCSD6V zf0s9Y$z0gf{R!s6rtS%I?|FN|+SvPh-*p(VvF$S4phiLY!C>af85aCC8NNE;$I*|Y z|He_bqo~pE?n$D z+oTr5QCK@|D}1x#tlHUW{$QTrJHWJNipxkf8y3v5f0{QOe>mHa&qQt=N(FcW8r&M} z1Wd6jp7Yw1IdXi+@K*6fNp0wk<6+r8M0t*#L&i>+30rHe*mfzMfC8uDJ)Vf|NfpP< zC;Bl>_}U(A-`I}McjXI5z5rGhH9=Nw99>)#eE+dQGR(r~USKzcvhTs?uYmj4FOd7W z48LM{qH}%$7GL1lEpO2t1HXdo_mK*p8omtsLDcXE2>WsLKX;UHg~1*a{soGNSj#pX zO4R@W0flKpLr_UWLm+T+Z)Rz1WdHzpoPCi!NW(xJ#a~mUDisGSh&W`ZPF6%k97`38 zV4<`XT6HkF^h0RUkfgXc3a$kQKNhPFF3!3-xC(;c2Z*bSlcI~1_`jskBF2N`e!RQ) zxO)c(^)geYYVzG_o zHfAM5C7vb@E2>8MLe^!4^A=~dTxHFB@)rj4+H#ufG=~w#A`(bKgp4XSP=w3Vu9j|qgF|4nNZD&1@9t{v?cX!a{(b=U>2lk$kG59;000JJOGiWi{{a60 z|De66lK=n!32;bRa{vGf6951U69E94oEQKA00(qQO+^Ri2^9i1CxXzlHUIzs8FWQh zbVF}#ZDnqB07G(RVRU6=Aa`kWXdp*PO;A^X4i^9b0G>%iK~z}7?Ub<%!ypty&&Z`+ zcmQZ<>;b(0B2JUgQ$UX^OJ6Ax!52=7djs(94g^35Aw+?7zF6mr<_x2euiK7kUZ4qK zH0eV)06-POXxd-o6q`$owPFBRw;drI$SGo)7u7o&YUENT-6SE>X3>`{Av#clnUMt#ph0Cn0Wc{I?+pA*7WPP4YcQ=~tct00000NkvXX Hu0mjf8{*v2 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/brain.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/brain.png new file mode 100644 index 0000000000000000000000000000000000000000..ac2806b79cefb50b0dafdbb93575cdde187cc7e8 GIT binary patch literal 823 zcmV-71IYY|P)EX>4Tx04R}tkv&MmKpe$iQ^gM|3RV$u$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|>f)s6A|?JWDYS_3;J6>}?mh0_0Ya_BRI_aYP&La) zL?dE4vnqDHq6-5MFo-^hnR+a_n1bi{x`&UicVV98eeTcEqhw76_(bA4rW+RV2Jy_M zrE}gV4zhxz5T6r|8+1Y9N3P2*zj4kxEbz>rkxI-H2Z_ae3(GCc3WiENO&n5Gjq-)G z%L?Z$&T6T`8u#Qc3}m&HB-d$nDK!8MxA#{&EeN{Up8G z)Ivu2C8-PGhg;Bp5TdeS9BawI=ZA(sQ*&*+;nK>sb!z2^4T*vIJukfN@ZZ-9eC zU@TAB>mKj!Z0+seGmZX!06^_>lj{7Tu>b%724YJ`L;wH)0002_L%V+f000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j>YD0v#@meDV7L00A>eL_t(o!|jz_Pr@)1hMx^) zwt+J!aX8RKK>z>mQ7;8<{9pp@*w)q?UBGAp3&fi~H*HSRzHi>}#M?-Va1j@1`6Oypqs%m~1 zkO_$&26&zaA!I^Qtk;xf$-xaQ(iCG1VY`J~35s=2^TZ<)a!Wi-)59B>K2ID57-LLj zL~BhkFNmUuJj(#czSg6)VKV4%y{+&2D5YpL8Yra*!w?|^d7d+yOr4|5flaRZi3x(B zmiXRX@Efzq)R9sGRN_aUgnuQmY07>w7K_EQE1!JgsDt#P++qL#002ovPDHLkV1nd; BZnppc literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/core.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/core.png new file mode 100644 index 0000000000000000000000000000000000000000..ac2d7893fdb13dd286886878c3ad64ea8ca4f934 GIT binary patch literal 927 zcmV;Q17Q4#P)EX>4Tx04R}tkv&MmKpe$iQ^gM|3RV$u$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|>f)s6A|?JWDYS_3;J6>}?mh0_0Ya_BRI_aYP&La) zL?dE4vnqDHq6-5MFo-^hnR+a_n1bi{x`&UicVV98eeTcEqhw76_(bA4rW+RV2Jy_M zrE}gV4zhxz5T6r|8+1Y9N3P2*zj4kxEbz>rkxI-H2Z_ae3(GCc3WiENO&n5Gjq-)G z%L?Z$&T6T`8u#Qc3}m&HB-d$nDK!8MxA#{&EeN{Up8G z)Ivu2C8-PGhg;Bp5TdeS9BawI=ZA(sQ*&*+;nK>sb!z2^4T*vIJukfN@ZZ-9eC zU@TAB>mKj!Z0+seGmZX!06^_>lj{7Tu>b%724YJ`L;wH)0002_L%V+f000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j>YD0v$8%dYvKw00EmxL_t(o!|hc)Yr;?zJx`=V zZJ>n=Ej)#yODVV{yL+c@4*7i<-24mfW)VskMTh}22nM2A)4|@yO^j(0Q>UEi<%67) zbI-jOXwaZRMRa@pxQ_3LI>^J_5daWGzPO110C>EAjsZZ^I{<(O?Fj&|U3;a3w5yPE z5HSEqIRF5>K*rx+b5WZEZ|`P;c)RvaM6Xian|+LdyVH3zov3$$$QRvSKi;@2QAq$N zmvR8lTb{i`e{P!wM$-uZ;1KTB^GoCa&?;N|+2{D|OrnUKhm!(YSpZo{u;z{;r!xT9 zLnAXAe+9CT17s(+--RQ5V<|InW0qmgxYvCkB7HLUQw(WTG z(?PUNLzTtzmW2f5mO#@x2qItbkEAo9GVn`on?`1*Tr~%>oJc!D4)8V%cSmS?C$|m7 zHB*q2^GtA;XjESk(Jo3(WgsDGa1eS0UyCP+lDml=v-zS zwyFvUB41E_>3ZX?68CCf$63fr_`f>Pph1JW@B`15%vja@;{E^t002ovPDHLkV1j#; Bn(F`n literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/ears.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/ears.png new file mode 100644 index 0000000000000000000000000000000000000000..6ff3ac86b7642a95b27e60e079439eb127376880 GIT binary patch literal 1677 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 Ef{!*J8~^|S literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/kidneys.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/kidneys.png new file mode 100644 index 0000000000000000000000000000000000000000..482bb2410227df442ae3e7ee251ef6fcd27fa473 GIT binary patch literal 759 zcmVEX>4Tx04R}tkv&MmKpe$iQ^gM|3RV$u$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|>f)s6A|?JWDYS_3;J6>}?mh0_0Ya_BRI_aYP&La) zL?dE4vnqDHq6-5MFo-^hnR+a_n1bi{x`&UicVV98eeTcEqhw76_(bA4rW+RV2Jy_M zrE}gV4zhxz5T6r|8+1Y9N3P2*zj4kxEbz>rkxI-H2Z_ae3(GCc3WiENO&n5Gjq-)G z%L?Z$&T6T`8u#Qc3}m&HB-d$nDK!8MxA#{&EeN{Up8G z)Ivu2C8-PGhg;Bp5TdeS9BawI=ZA(sQ*&*+;nK>sb!z2^4T*vIJukfN@ZZ-9eC zU@TAB>mKj!Z0+seGmZX!06^_>lj{7Tu>b%724YJ`L;wH)0002_L%V+f000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j>YD0v#~`bo>qg008qzL_t(o!|jx@3WG2dMQ=(c zTfssH2X)awKfwS006##7y4Vg5fd*2urc**96sne%!aIwQ7hmpuUIP#WK@b~=f>^uH zHSB(EV!vc_n&sB%b?DjB`X&Ksmg{49LXs3_NlQL?^|Gn~0Jqx(X_o7EMC{%@+-+_U zYGhFmYoSJakg1nd?M-BJ22A55DbTeIB%i$SU5B1^ZDS|o3jJ6HT#V}|i1k#yEh{RxB`8Mr^@d2`7pgy(~GF8KsE poSQQ+7vVa8Ri~?%APC|wcmnhbg@c}Xw&(x=002ovPDHLkV1mhNOmF}I literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/liver.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/liver.png new file mode 100644 index 0000000000000000000000000000000000000000..0a2e6ab25ae4c51e237c78ed7f9e592422c6fe16 GIT binary patch literal 710 zcmV;%0y+JOP)EX>4Tx04R}tkv&MmKpe$iQ^gM|3RV$u$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|>f)s6A|?JWDYS_3;J6>}?mh0_0Ya_BRI_aYP&La) zL?dE4vnqDHq6-5MFo-^hnR+a_n1bi{x`&UicVV98eeTcEqhw76_(bA4rW+RV2Jy_M zrE}gV4zhxz5T6r|8+1Y9N3P2*zj4kxEbz>rkxI-H2Z_ae3(GCc3WiENO&n5Gjq-)G z%L?Z$&T6T`8u#Qc3}m&HB-d$nDK!8MxA#{&EeN{Up8G z)Ivu2C8-PGhg;Bp5TdeS9BawI=ZA(sQ*&*+;nK>sb!z2^4T*vIJukfN@ZZ-9eC zU@TAB>mKj!Z0+seGmZX!06^_>lj{7Tu>b%724YJ`L;wH)0002_L%V+f000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j>YD0v#@meDV7L006>CL_t(o!|jv54uUWchkqC* z0tsv^?ZN^N!23Ud51<1s+QGr3Catb@Y66PdYieBX8!y*>_ooFIhGCdLlf@hovUA^Z zPRB!*yZ|rmhj9^CYJxQy{V+nQ2>@V?4k{w2$i3K0Jaf4<8USD}x1f7->iE~>0I=U} z7vk&voNfg|cCM_Nm$;WdcN{8@n!qP9RZc3v8jZ4Q-tL$xj+%fqdi8O|%1Pf)$j+te sdd?;^@fUJ3bEX>4Tx04R}tkv&MmKpe$iQ^gM|3RV$u$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|>f)s6A|?JWDYS_3;J6>}?mh0_0Ya_BRI_aYP&La) zL?dE4vnqDHq6-5MFo-^hnR+a_n1bi{x`&UicVV98eeTcEqhw76_(bA4rW+RV2Jy_M zrE}gV4zhxz5T6r|8+1Y9N3P2*zj4kxEbz>rkxI-H2Z_ae3(GCc3WiENO&n5Gjq-)G z%L?Z$&T6T`8u#Qc3}m&HB-d$nDK!8MxA#{&EeN{Up8G z)Ivu2C8-PGhg;Bp5TdeS9BawI=ZA(sQ*&*+;nK>sb!z2^4T*vIJukfN@ZZ-9eC zU@TAB>mKj!Z0+seGmZX!06^_>lj{7Tu>b%724YJ`L;wH)0002_L%V+f000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j>YD0v$4$eXXL z1Bq-7)kOw<0N?)scmNpcq8%8TKvK$z6Qng7h0>_WeM^)7+T7iLmjVPq5dY3@f9QSQ zN509Ar?XcP(YGCpd~KIfc#UH<*LDE?EB$u8upkatNiduPwm!v)**hSmK%As#b&WVl zolykY0lRxC1vt;p>KXvh>KdG9V-Ztah~{)|C)3J2)faa_E8`^LJcCvSO;<6kjH62_ z#=?R-@J?T}0DIQM{Owp-N002ovPDHLkV1j7)Ls|d; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/meta.json b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/meta.json new file mode 100644 index 0000000000..1c9aebfb6d --- /dev/null +++ b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/meta.json @@ -0,0 +1,44 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/tgstation/tgstation/commit/f309886bf3e29808206693e9142304260df134e9", + "states": [ + { + "name": "appendix" + }, + { + "name": "brain" + }, + { + "name": "core" + }, + { + "name": "ears" + }, + { + "name": "eyes" + }, + { + "name": "heart" + }, + { + "name": "kidneys" + }, + { + "name": "liver" + }, + { + "name": "lungs" + }, + { + "name": "stomach" + }, + { + "name": "tongue" + } + ] +} diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/stomach.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/stomach.png new file mode 100644 index 0000000000000000000000000000000000000000..a0341750d3247e864d1def1b4c272b696493d930 GIT binary patch literal 770 zcmV+d1O5DoP)EX>4Tx04R}tkv&MmKpe$iQ^gM|3RV$u$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|>f)s6A|?JWDYS_3;J6>}?mh0_0Ya_BRI_aYP&La) zL?dE4vnqDHq6-5MFo-^hnR+a_n1bi{x`&UicVV98eeTcEqhw76_(bA4rW+RV2Jy_M zrE}gV4zhxz5T6r|8+1Y9N3P2*zj4kxEbz>rkxI-H2Z_ae3(GCc3WiENO&n5Gjq-)G z%L?Z$&T6T`8u#Qc3}m&HB-d$nDK!8MxA#{&EeN{Up8G z)Ivu2C8-PGhg;Bp5TdeS9BawI=ZA(sQ*&*+;nK>sb!z2^4T*vIJukfN@ZZ-9eC zU@TAB>mKj!Z0+seGmZX!06^_>lj{7Tu>b%724YJ`L;wH)0002_L%V+f000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j>YD0v#{UMq;_4>Y36V0N{E#qszo78Q>)&MgGzX=I@VTbs_1Dr=tS{faG~64Z%|?@>k=O z7iHo8Hxiy7DtNVG3FjpnZwe&*d^!RE{KXueAAVIW_d_3e?@v&LtBbQpv@NE- z@Ie`HgeCLN&BwzI!qR#tzh556^Wrzv_Bf&c&j07*qoM6N<$f=-=I AiU0rr literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/tongue.png b/Resources/Textures/Mobs/Species/Shadowkin/organs.rsi/tongue.png new file mode 100644 index 0000000000000000000000000000000000000000..64306900f57f8f8214509ed2fe4ee22edb47debf GIT binary patch literal 2303 zcmV zaB^>EX>4U6ba`-PAZ2)IW&i+q+U-|aavV1d{pTri1Y#q|$QU6_!#c}8V#f7$Rm;FnMdvzgBmeO-lQ}sXw$mnpn)wa zFLp@g!E()2ac(>$3yCICQQGX%WJ(p5!Ak_Q{M-v&>$YpTK@|&6f+iD;5#G2)dffc# z_|(wejUZYvUcK zOP7sr(mix@?u@tFE?UBh|KS-L}j2U3c5@MD0Q~y~FGya^FUc?x+E(m@Oe>LRvHPwd{1 z`+!?Q-Ty3Z{EA#U(ESeN(t++18?4jdTPLOfH0Y*|ZKq_rtYX2<=}w=`hDF>q1)n*#u8tRp`Cp&^^qL?s666 z9AYnhHAl)-F<(f*b+l?jMkjcSX3@t+FSEw#fshdQ5}|9Cjx$Eo%9>7H2bz*j^E?<1 z9XP{mA#i1L!ue2Y3_R3NjCc`UCjCmxL5R275#4qWZ1q z!tpyRx>=UCb`TQ829Df09W}vqtrLU=e@<+cPx#tTD(zgV`7M_cl4+vIakOrd*h;t9 zozQi1_z264WwCT9WH-|c3h+JKbqd~#JGJFM>)Sh-z9<4g5!j;$;wl2a6#;gHXS0~V zW^5$M0H&MiD%QhCGrC^YO7E-n;R?D}bM&QXZ@^VM?1ih=lWNj`EX>4Tx0C=2zkv&MmKpe$iQ>7{u2P=p;WT;M7 zL`57+6^me@v=v%)FuC+YXws0RxHt-~1qVMCs}3&Cx;nTDg5U>;tBaGOi}v_(b9;(+!Jwop@%`(mC%FhgeBch|h_~47wokBiCh@-#8Z?7Isb!v+DNN+{ftykfE-YZh(VBV6;ftYaZ|JYVYmeGtK^f0QKo|+p>?gR{#J224YJ` zL;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2j>YD0yY}jxcDak z000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0005vNkl8xhd~l5T^Rp?;tPn-oi6eOcByn>DHL3|@C|$fcOn`Wv6YHw zHH|1;#1>N2j#`?G$9uG#D;+2e9OzZ@ZEEM=YofahsXbp)NSGXRkn>+fl8+e}@VVxzHzRjqOH@+D?-r$ygF2v9Ig z5g~{X9YPx(=3pR5`h>~C{St>2mGM(qiua0tm5F`LtzeiU?jK+*q@a(a88UUM&nf_n zWskD-;yFN9B0a59!7xQm86}fAL{uGQq4E;d7v}iMNvvv(>h`9qgs&HYJF9P{l}3$s z)<=R$lmnp%)5}HaZs{R!Ha9S}G&9+}==mRTlel^;A~NeAXgaN*!3CK9_Ejiyl)iP~ z2aS$H-L_HX$ZzM09A)m+qSUtx{L1xQUYM~IYxPguC@=PR3;q;JJ*=@S;ren94-b!j ZhVN3>q1ffHIM)CG002ovPDHLkV1iKRO}YR8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/eyes.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/eyes.png new file mode 100644 index 0000000000000000000000000000000000000000..20fd326f17fa91b0b6c33b7fea29a3d693ec11c3 GIT binary patch literal 1643 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_foot.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_foot.png new file mode 100644 index 0000000000000000000000000000000000000000..30ad69dc3f0463bd7436d5c0dd40d2762822e7cf GIT binary patch literal 1867 zcmV-R2ekN!P) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U-|ck|ZY#{O1%t0zycF;~+p0zJZVLr1~+lJ8gT{ z>+n|>(*vy*dL<-O+JF2W={G!uYDrbBX;;m7YOlQv3|jtp_Py^p#~D|A=q{tD7Nu`~ zc#H_`mAs&5^n4RO{qZrxpC;(Dr&n^4@8tY+Q@)9)Jq#7JPx4i=<s?W>_@S}bI09Y8Q&^l#qNN{AonQ)us{Fy8)C4z6HS$1A(l>LQ2_6F(TBl0iS`$3#VEf5Rgb&h_X~>1vsr7-!N2m9E`->%ETwrvsJ;W{)-?@Z#*%#nsK5cOMhWXVxiGr_DC|9BGxs zt1el(Y_-+b*tD^fcHOdd+itt>5m;)$QA0w*qD7DKMD0Q~eS_Io{UlpTO8m_FV=hbSmAIprA=ktOB^IA6XfleqiG3DK32tHKp=D3-h(E>!5!!* zD2|Wtx_!2dTh+M2f7z$oV7t$A*R+UMCxVJ&2AqG=U7e?25i4!gSj!!xMu=|3QU{xd zr>sl2@3}m7Ju0+t(aN-?A|2lQgr{&Ydp^eY>axu@?ktEmCQg%3++T3@s|a4;!6$O= z${^Td&>%V$-m!}Bj;g@9Ds-w{bw@d2cH2s4>#Y~8@E)j z$;{gDa=h6F)NwiNOJd@6FT5Q>I+TMO9EglTc70p$jc`C&%NxEqWZU}6*-{s|0?+x! zd)dRmkHrGHxd!a6)bYg+pV|=fyc~?Tt0JkX$e_h$1Ygg2)r&T;b160G@F(beH+IwO zI9e?C0}B-1n@u&m)1IGNb#S>rnL;ZJM86KLvOvnGl9vI;n=%R!X`BR${{>8-Wz;?W z33HeOX>Q;w_y7O_hG|1XP)S2WAaHVTW@&6?004NLeUUv#!%!53PgA8L6%jj#IAo|! z7DPn^mr}(dSSW3URvk=U`Ug!Kk`xz5!L{Jv&tlcV#aUMeS3wZ`0daM4Qgo3L?@J0T zV!ZHpALre3c<(+yXjGVLc1-}PW*Mn?Ow8t1#lR~B1mVK~<|Jn7iS%Lyp7nK4om6)b zp5@*5XZ0z0lL0=FILdUxBHkdL*|c=d`@|tuk`&@|;&Fp6Nc_lk+2uFRMTZ5R88R}d zdEyYUSnOcAgIUQ?iKmIfimFk*kabz%yv13q)L84D{Dr~1wvy&Ltr5hrganchA)|&e zDzFftT_eRriq2ym{$a-)kq~_kr8`?LJ#?;(srom*uNKFj{yP@U?yEx-U z|961*ps3190ssI2000000002|u4x*3KA$#@qj~S`$}aD{jpJyK$HSVY`AXN%rvWJ? z#TX??RaNP#A$aetswzp+aU4o1eI+;lG+^I%4a1yvZ(KS-S2nRb$vUN^#&W}e8YTy%ZdO1002ovPDHLk FV1g6-alrrp literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_hand.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/l_hand.png new file mode 100644 index 0000000000000000000000000000000000000000..0cbc7371d14bd73a3bb5d33ed1f9e467fc12f881 GIT binary patch literal 450 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R4; zr;B4q#hkad7OrawU^w=%zxR=_1+$;@Tz3PD`v+1s%((N!lc#J`?;D;lxr1kKnn+z) z(6_UgUHa*}yz)Z_4}Aa2C35cE&%+8bF%yCraG~4Gd1lg=Uw^%jwe`Z=vbkExM+~N+cvVXJ=9!zL)}HzO_gK=#4gY?m zXH{~xC1&Jq3RV8+9?yu&acJTu2lBQX&wKu`z#_p!s&>ot4ATR3`_1>gzrJhc&)a3Y zP3QWtpMSpj%GWUFIp@=BZa*z@+$K6-^D}?rRfgt+4_24k8_V_2{aTgV_1S-Y{t@d1 mXAW`qas;$8HOzsbJNyn45~Pj< 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/meta.json b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/meta.json new file mode 100644 index 0000000000..a259ab696b --- /dev/null +++ b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/meta.json @@ -0,0 +1,71 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13/commit/5bc3ea02ce03a551c85017f1ddd411315a19a5ca#diff-519788fa2ca74299d1686a44d3ab2098b49ed5ab65d293ec742bead7d49f0b8d", + "states": [ + { + "name": "full", + "directions": 4 + }, + { + "name": "full-nomarkings", + "directions": 4 + }, + { + "name": "head_m", + "directions": 4 + }, + { + "name": "head_f", + "directions": 4 + }, + { + "name": "torso_m", + "directions": 4 + }, + { + "name": "torso_f", + "directions": 4 + }, + { + "name": "r_arm", + "directions": 4 + }, + { + "name": "l_arm", + "directions": 4 + }, + { + "name": "r_hand", + "directions": 4 + }, + { + "name": "l_hand", + "directions": 4 + }, + { + "name": "r_leg", + "directions": 4 + }, + { + "name": "r_foot", + "directions": 4 + }, + { + "name": "l_leg", + "directions": 4 + }, + { + "name": "l_foot", + "directions": 4 + }, + { + "name": "eyes", + "directions": 4 + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_arm.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_arm.png new file mode 100644 index 0000000000000000000000000000000000000000..c294120942cf5eed679cf4306018857a95c18f09 GIT binary patch literal 605 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|OJk zr;B4q#hkadd|w*|N*w=q|B0WbbmD2Jh=>zWUY?)$_Apv|om#!&i<{1r6z{OER^Ej< zwoff57XLr5dAj<{oxkE~>t06(n;FM`o|!yr)=%a~h729Y85HE08!C7hK%8`mguQXx z`|q!RczU+qawVZoiFua=9M)eK=4)pTJMsGKqWkZ=Q;ae>4hlFJNE~@saA3Rn?6XHc zR=6k#80Jwhj8>61O3E>36vYsNO5e%chYvgKq7lXmFZFv0GlJZIN(f3Ukyv~$am zPxb20|IUn-m>DhZAon1>Su}fV)c)@>3C|@MjwCY(^fNlxurYkZ$~p7SK&to1%MvH8 zshh6sZmLh^O0baWs<2siPa@Y$`r+rFI{gV;2c8x^{92{Su#@ZA=bap_PSe~BSPS;w zzuGQT6umaANc6y~nM@PH|5fdMRAZOC{r18Djfh{bPWv#P(06SSc*V`L{4%F?3fF_O z-7Bjb&b%!<%r}$i#PiQlyYGrUERZ;wBv|%hzg&yfRIWFZGJ$p^EdCKFf$DB${tx0+ X*88+ucF0=;(+h*AtDnm{r-UW|r#lO; literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_foot.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/r_foot.png new file mode 100644 index 0000000000000000000000000000000000000000..390e0a27ee3c31b36740b4d14c49fc5461cf57cb GIT binary patch literal 1876 zcmV-a2dnsrP) zaB^>EX>4U6ba`-PAZ2)IW&i+q+U-|cmMbR={AU$e0+JAdRw3JP`FfBhNh4?IkTQ&DTR*P8K^UOEqST7G}_P4=AQ^b0<8)6v6<(x)Hp zJwkh?XGnpRH|1kKIgaGRfclX1Oi%hnFOO;XQ$+3Vh|xai7iY^KpZ5NVSS_&TbgauL z75S;RMctQj^Bsr+iqCr9Xl9MJvEWFtu znJ3GoYhv7aN*0omq)4G)lP*h|uuR?}(DEY|y5{ZIbdwq;UIa@fI3tX>TDo`sZ+q$} zVxANcR)5bG;$l89(8;2l|F8le6!(3kSAy^D>jPlnq`>N=xnY4L`RQ?b&phlYw4Rjq2;wCku+OVwJd)pHhyi4{|;X4b6RXwpj4 zR-3ifdK;;87o1h?wOen!_c1W(faihHqYWsWUA(w@b@S%k$HexTW$LWcW}AJEw0OxX zOIKaC+Ujd;+Sp3FY~6L+ZoBUh*lHnBLZgO7iyq^F+J$QRgxPoG{uwp8p$5Rxg;{~o z9W|)U?k?!ii5|>=7#9NKb`ZdZ=7U*oR0<#D(!nfu#;H(7lzPBfbPxlAWfN=A1G{(R zzTuW|?*A)p{1Lfyp!*%jr32j)t;+Gw~kH;IH>;Mjr~Y)^|?aNhBKE6AsNSltmICL!4A*rocO1X>y4 zQX^2-bb-+{o0_wA75E8n#|fAzjs#amI2f<*XwxzNXme|*jXajbUY4ScKscB$Dt!j-ltVc>d}1Vr?<=A`>lSL@t;G(!%h3(cYdieeb+;I{hn|#U z-KHnm63wa2YHPTaI=o+bgdWt39N~+OmSsyF?90$;2n6LBa=7KTTP3Vlr*H*x_yl|Qx=^DXP62=RLp?hdJLRH@+i2}VtaKMee_Q5^QAG4Z+w%7!W-c&9x-wc@zC6vlKY*E#w{uDpEClgZ10<4qZf zh&0Xu*?$2O?o8x8{S6*e{0(5$8@&Jk0fuQqLr_UWLm+T+Z)Rz1WdHzpoPCi!NW)MR zg-=tZA{7xkh&W`ZP8LK(1ea39B3LMGg;pI*Uit@38j=(jN5Qq=;Ll>!!Nplu2UkH5 z`~h)waZ+@V67Ne2En>XzcpvB8b9nDQKxkB$YIaQks%9CfcudUZR>i<81O(y30OllS z>WTDX2A=hGPn}eE5uWAU_hE=c^y zb=l=N&P9g>o*6PSsd?fMu~_V2xr15BP>H9B!-}d=zL0fU;k?CJt<+fSp8SQuytb0& zI;|1Jv4jMY5Fw+6GAghTp$?=2#!SC6cg{et5 zDHsE~Uu^qh6bS4B&AM%WAKP~G1n@rtS6bU&Z2+^Mq}SV8_z39V1}?7Kn!E>G?f^qi zx@1U>4?5<5S>o`L`X0O?6YK~#9!?b@M^g+L62;d2)Z0*mLZcthq52*Kh}K!PDb z0Tw~ROw!dWA)6b-|LK`dr%hcY0000000000006*WbzR#uP1g6lm1Svf$f+z#8-~HA zX|lSmZ|C%L=ks}HY zbIv__KYntlD2k7B&W__yk|Z*-uP#ii>#8`8wQZZ)wpE&@DvILsB-StK1bo9B1R=!$ O0000XhvZ!BQeyxZ7OypHo7SH)?>RDJ^{CTqbMEwcD3^-838phL4FU|2&Kl8l!%Ii|@{5%t> znMoTtju#rt^tq5>5_ElzlmZ7+v5nls(@z7mrhedjWZ}21{V-#qL|UIeZ^7Igv)PLC z&nIub`9Szh+3u9HPm3gmG{eTzrD97t(MinkC7Pk&Njh9M(TR$#;COcD?{ww?SGihf0r>(WMP0t zf{onlsI}iNaa?|RrDERqEyed_6ZYRfyzOmD{qDPuu3mfnHTd~%YlqD@x3D!g{=9j- yJ9PEc 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/torso_m.png b/Resources/Textures/Mobs/Species/Shadowkin/parts.rsi/torso_m.png new file mode 100644 index 0000000000000000000000000000000000000000..dafc83b65eb84c863c1f67ff31c90ef59b130ced GIT binary patch literal 1432 zcmV;J1!ww+P)afqZvo(H z7%XPqX0uUhYAQd+&oD%aqE|@Zy_oIC$43@DI5-%d$NUiE{r#OVIja1A34oUt78cUO z!vhn82&#vLg)uP({`B<3f5+!M2)GD%FWfvNBm^$c*X!#m-=CkKIUF1g2Y=4P?e}~A z5&(noyjWCMSJTwg6vfBK)BXKD#~+ON`ubweXm4+q1_X5Q$oAXY+XVjU>1oQ(&u76g z^Wct-j;5276aFkdr?NWu9Rl)&`}_MMJw2Tc4-d(1x6{tf4te5^PESvH;412?as#`& zyCNeagFZh$IUHhRV|fzx_V&EO2iyaE5f>LnDJdzex+f`7QBe_x1l->U$gq_dRo)>hIRNWN`tZTz!mN621)$#6QI{A^@oMERY{A%JDB zzP_I3=H@v5;Q>ueP5fN%e~diKpiS;cundF0wzihJ|6eBc-3rV70PZCvB{VTHAv!xd znH0~-0LZ~MHa61W;2_P<&wD38%Mz^Y0lu>=2M@p~qafFNh#;YL0s?j*@*DpW2Lk`x ziO`?&jMn_~&j}D{SOSpAVF^Gcha~`+9F_oNa##Y8$zcgVCWj@!Prm%y{Q-fnL6ZP8 zGc%&6r-%L);G7T#w$07W`ko0&fXMB3i|+33Uu%#6z>TB0p`jrh)EajnvbMG+a&vQO zb#;}G$5FR;I2?wGu)e-7WMv8`)3B+cqC$TUfa)%ni%(%kM@P9do|cwIo12@WtgOrs z5x`YkT+Hj(BT}1!~xKNBm zQAQ3P6Vlh$N2R5uO7Bw^Ko%qc5W^6ZsL{NDBB|a0$Hm13p-%|hzQ^&tw)qQX0XjN5 zxHNWte$MfS;v{N>D3v>%PTfNQ9G+!Qpf(8Z;o)Ja0Ag%wYfFF-7pk=} zF){1~Mgd&lL`35mOyZ0;Aqw;C@4UmkT^R# zqw@0dz_!UrWM^l4yFz4Z9A*O9*N7EB0$kt(H*E0rM%btVB>^z&fXx1wiH3#-Ze2s8 zq)`ADyso9Cg%%eVz3p7nq!! zWC2v%A5cz$AXr#f@CH1{joJcB)Ya87pX?R^&?ls2AEId$kVJYkMXJEJF;n&lslqxD mB9p@sfJ_cc05UmDFTj5?`Q@6?Q zaB^>EX>4U6ba`-PAZ2)IW&i+q+U-|amgFc5{O1&N1SBDrvag+VynrC0UUv?kxqtiyW7rB># z>yFSqvNL)?FNg77Pb`o;O^A0-AK6KNvCF*-zaYw2AVzz#$KsQJzVhR@*2oJvJM#`T zMDP>Oq^?x<9UL2okv%PCn+){3rpuI0xREGXs=uLa-A|39t2M&1S8DY&0RO&Z65|& zs~bgx>@Ti>i`ir#V^QocydVgztB!ONe6?RM2#X*AUPg09g4MSpL{oaymbfjnz){hM zV{#g208oUr6<|o9fR7|fk$hrKMBp()p9Pk|0fy3avuW5M4+iqlT)A233ukG;7fsC5EUm#uRNY)+8w;O);gEQ%N;T4q0=| zDd${rb%l#yuWE@Um0U`x6^m9xu9#hOg`shi7Mix$Qp>Hh+EG3|bnUUHo_p!l4IVPW z&=E%(d6ZEnO_b6MQ)irM=2>Q4D77UkEM0M>l~-ByR&A^5UNn1G?)R$EsTu@xTeASC zOEs8NxE^KS|5P`AR?eApKcSp6={`{IUAGTZ8+N{T+lL+pn{7iQ)eE*C9A>VZ;lW?)-B&04 zV4yZe%yogr>;*5Vx-G7CGx)mwzR|n?>bR2D| z1&xiNxgo7mz|mMnTH-d%!zeVY*~qmy=5JW<0H4Uw(3!lhRFg0zuWsePeu`i9&OFIoj^pI=3ZYLjGf4#`@i( z*1+B{RtUC;w3jYiRQ*^H$nf0@k5=4;w4n5nyQe*C&5Qd8tbGBVQ2z2l~lC_+G^S``y1zy!^_1 z0z`8F00D(*LqkwWLqi~Na&Km7Y-Iodc$|HaJxIeq9K~N#OI2DN>>#3$p?0z$D&i2R|084ld5RI=Bjg;0K7Si<6>@l=#22&?3fz<9@um z_qclp2#pF;&7KKB)hr_wkBQmbs_1`(4?*-{6cLG;dLq4;f$R9Xhlj6sQJ&>~?#~fa z@+Jd3BJmv44U2e#cxKbmIqwsPSV>Zd&xywkx*+i**X0(!aV|P6@XU~rNzD_7h{a+T z%U#S$hDtn599C3~@`Y@R70z3n)k=-E@5x^n$ZIQUuG1Vr97{+b2@x`CD5C-kQCc-p zOr&T(=HefA{7G`j0V}O4bXx44}``EUd zCxG`ExY9cQY6F=4B)#6z!biZsHgIv>(d0egat9cC(j`N3Bri>&PypV~=$mrD;4RR< z*6OXjkJASrLtQQ300)Oaq)6H8F7NK`?(N?*?f!lMe1CGQE>iJB00006VoOIv0D1t+ z0Op>uefjf#Lmt%{@Z!BYdPfv`F?k^&gQcOgwl=>2)N#~GHMj(XX$K!FZBEY8?kmvbOOzTuG zgkU0KA%ru6p^@`-Ve&D0KA*_*yjT4Yq?7>g)tEm=Foh&Z008&002ovPDHLkV1j}+*XaNN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Shaders/color_tint.swsl b/Resources/Textures/Shaders/color_tint.swsl new file mode 100644 index 0000000000..a5449b2d4d --- /dev/null +++ b/Resources/Textures/Shaders/color_tint.swsl @@ -0,0 +1,56 @@ +light_mode unshaded; + +uniform sampler2D SCREEN_TEXTURE; +uniform lowp vec3 tint_color; // RGB color between 0 and 1 +uniform lowp float tint_amount; // Number between 0 and 1 + +// Function to convert RGB to HSV. +highp vec3 rgb2hsv(highp vec3 c) +{ + highp vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + highp vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + highp vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + highp float d = q.x - min(q.w, q.y); + /* float e = 1.0e-10; */ + highp float e = 0.0000000001; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +// Function to convert HSV to RGB. +highp vec3 hsv2rgb(highp vec3 c) +{ + highp vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + highp vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +void fragment() { + highp vec4 color = zTextureSpec(SCREEN_TEXTURE, FRAGCOORD.xy * SCREEN_PIXEL_SIZE); + + // Convert color to HSV. + highp vec3 hsvTint = rgb2hsv(tint_color); + highp vec3 hsvColor = rgb2hsv(color.rgb); + + // Set the original hue to the tint hue as long as it's not greyscale. + if (hsvTint.y > 0.05 && hsvTint.z != 0.0) + { + hsvColor.x = hsvTint.x; + } + // Modify saturation based on tint color saturation, + // Halving it if it's higher and capping it at the original. + hsvColor.y = (hsvColor.y < hsvTint.y) ? + mix(hsvColor.y, hsvTint.y, 0.75) : mix(hsvColor.y, hsvTint.y, 0.35); + + // Modify value based on tint color value, but only if it's darker. + hsvColor.z = (mix(hsvColor.z, hsvTint.z, 0.85) <= hsvColor.z) ? + mix(hsvColor.z, hsvTint.z, 0.85) : hsvColor.z; + + // Convert back to RGB. + highp vec3 rgbColorMod = hsv2rgb(hsvColor); + + // Mix the final RGB product with the original color to the intensity of the tint. + color.rgb = mix(color.rgb, rgbColorMod, tint_amount); + + COLOR = color; +} \ No newline at end of file diff --git a/Resources/Textures/Shaders/ethereal.swsl b/Resources/Textures/Shaders/ethereal.swsl new file mode 100644 index 0000000000..dc9d971e1c --- /dev/null +++ b/Resources/Textures/Shaders/ethereal.swsl @@ -0,0 +1,75 @@ +light_mode unshaded; + +uniform sampler2D SCREEN_TEXTURE; + +// Function to convert RGB to HSV. +highp vec3 rgb2hsv(highp vec3 c) +{ + highp vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + highp vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); + highp vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); + + highp float d = q.x - min(q.w, q.y); + /* float e = 1.0e-10; */ + highp float e = 0.0000000001; + return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +// Function to convert HSV to RGB. +highp vec3 hsv2rgb(highp vec3 c) +{ + highp vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + highp vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} + +// Random number function with potential negative values. +highp float rand(highp vec2 n) { + highp float r = 2.0 * (0.5 + 0.5 * fract (sin (dot (n.xy, vec2(12.9898, 78.233)))* 43758.5453)) - 1.0; + return r * (r < 0.0 ? 0.8 : 1.3); +} + +void fragment() { + highp vec4 color = zTextureSpec(SCREEN_TEXTURE, FRAGCOORD.xy * SCREEN_PIXEL_SIZE); + + // Increase the contrast of the image if the luminance is low enough. + highp float luminance = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); + if (luminance < 0.06) { + color.rgb *= 0.5; + } + + // Convert to HSV. + highp vec3 hsvColor = rgb2hsv(color.rgb); + + // Apply a breathing effect to the value of the image. + hsvColor.z *= mix(0.35, 0.7, (sin(TIME) * 0.65)); + + // Increase the saturation of the color, incorperating a random value, as long as the value is above 0.1. + if (hsvColor.z > 0.065) { + hsvColor.y *= (rand(FRAGCOORD.xy * (TIME * 0.15)) * 1.5) + 1.0; + } + + // Convert back to RGB. + color.rgb = hsv2rgb(hsvColor); + + + + // get distortion magnitude. hand crafted from a random jumble of trig functions + highp float w = sin(TIME + (FRAGCOORD.x + FRAGCOORD.y + 2.0*sin(TIME*0.3) * sin(TIME*0.3 + FRAGCOORD.x - FRAGCOORD.y)) ); + + // visualize distortion via: + // COLOR = vec4(w,w,w,1.0); + + w *= 5.0; + + highp vec4 background = zTextureSpec(SCREEN_TEXTURE, ( FRAGCOORD.xy + vec2(w) ) * SCREEN_PIXEL_SIZE ); + highp vec3 hsvBg = rgb2hsv(background.rgb); + hsvBg.x *= -1.0; + background.rgb = hsv2rgb(hsvBg); + + color.xyz = mix(background.xyz, color.xyz, 0.75); + + + + COLOR = color; +} From d2da70ed401f47e329082f5bebd423f81aee1353 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 20 Oct 2024 16:31:45 +0000 Subject: [PATCH 65/76] Automatic Changelog Update (#960) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 30705e57a2..896117ce8f 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7438,3 +7438,10 @@ Entries: id: 6471 time: '2024-10-20T16:03:28.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1109 +- author: FoxxoTrystan + changes: + - type: Add + message: Added a new species, Shadowkin! + id: 6472 + time: '2024-10-20T16:31:21.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/960 From b7dd76503b89ba81e68d5eaec31786a9ff84ce9b Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 20 Oct 2024 12:32:43 -0400 Subject: [PATCH 66/76] Remove Pathfinder Latejoin Spawns (#1105) # Description Fixes #1099 # Changelog :cl: - fix: Removed latejoin spawners from the Salvage Pathfinder. --- Resources/Maps/Shuttles/pathfinder.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Resources/Maps/Shuttles/pathfinder.yml b/Resources/Maps/Shuttles/pathfinder.yml index cc43ba831c..d28c1fc820 100644 --- a/Resources/Maps/Shuttles/pathfinder.yml +++ b/Resources/Maps/Shuttles/pathfinder.yml @@ -2594,18 +2594,6 @@ entities: - type: Transform pos: 4.5,-2.5 parent: 1 -- proto: SpawnPointLatejoin - entities: - - uid: 357 - components: - - type: Transform - pos: -2.5,-4.5 - parent: 1 - - uid: 358 - components: - - type: Transform - pos: -2.5,-2.5 - parent: 1 - proto: SubstationWallBasic entities: - uid: 52 From e56f6a9f75c9a2c8cf966fbdf9c834d7d0386c3b Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 20 Oct 2024 12:32:53 -0400 Subject: [PATCH 67/76] Musician Loadouts (#1108) # Description Fixes #1103 This PR removes the default saxophone and acoustic guitar that the Musician spawns with. Instead the Musician now has a selection of 3 free picks of *almost* any instrument in the game. The only instruments not included in his list are the Supersynth, DAW, and admeme items such as the "Musical Lung" and "Fingerless Frets". There's a few instruments leftover for the Clown that will be given to the clown in his own separate PR. To make sure the musicians can also take large instruments, this PR also takes a feature from Frontier, that of "InstrumentFlatpacks", and adds them to the musician loadouts. So Musicians can take for instance a Minimoog flatpack if they so desire.

Media

![image](https://github.com/user-attachments/assets/8b79e960-440f-4f7e-9b9a-bc56d30c104a)

# Changelog :cl: - add: Musicians can now select for free, any 3 instruments in the game with their loadout. This now includes things like Piano flatpacks. The only instruments not included are the Sypersynth, DAW, and "Admeme Instruments". - remove: Musicians no longer spawn with a Saxophone and Acoustic guitar by default. You pick which instruments you spawn with in your loadouts now. --- .../Locale/en-US/loadouts/categories.ftl | 1 + .../Fills/Backpacks/StarterGear/backpack.yml | 5 - .../musicianInstrumentsGroups.yml | 71 ++- .../Objects/Devices/instrumentFlatpacks.yml | 121 ++++ .../Fun/Instruments/instruments_wind.yml | 2 +- .../Loadouts/Categories/categories.yml | 4 + .../Loadouts/Jobs/Service/musician.yml | 554 ++++++++++++++++++ .../Prototypes/Loadouts/Jobs/service.yml | 172 +----- .../Objects/Devices/flatpack.rsi/meta.json | 5 +- .../Devices/flatpack.rsi/service_music.png | Bin 0 -> 6361 bytes 10 files changed, 753 insertions(+), 182 deletions(-) create mode 100644 Resources/Prototypes/Entities/Objects/Devices/instrumentFlatpacks.yml create mode 100644 Resources/Prototypes/Loadouts/Jobs/Service/musician.yml create mode 100644 Resources/Textures/Objects/Devices/flatpack.rsi/service_music.png diff --git a/Resources/Locale/en-US/loadouts/categories.ftl b/Resources/Locale/en-US/loadouts/categories.ftl index 193760005e..778d0869b7 100644 --- a/Resources/Locale/en-US/loadouts/categories.ftl +++ b/Resources/Locale/en-US/loadouts/categories.ftl @@ -29,6 +29,7 @@ loadout-category-JobsServiceBartender = Bartender loadout-category-JobsServiceBotanist = Botanist loadout-category-JobsServiceChef = Chef loadout-category-JobsServiceJanitor = Janitor +loadout-category-JobsServiceMusician = Musician loadout-category-Mask = Mask loadout-category-Neck = Neck loadout-category-Outer = Outer diff --git a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml index 6713ed1e22..da46ae7597 100644 --- a/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml +++ b/Resources/Prototypes/Catalog/Fills/Backpacks/StarterGear/backpack.yml @@ -183,11 +183,6 @@ noSpawn: true parent: ClothingBackpack id: ClothingBackpackMusicianFilled - components: - - type: StorageFill - contents: - - id: AcousticGuitarInstrument - - id: SaxophoneInstrument - type: entity noSpawn: true diff --git a/Resources/Prototypes/CharacterItemGroups/musicianInstrumentsGroups.yml b/Resources/Prototypes/CharacterItemGroups/musicianInstrumentsGroups.yml index ccad399e74..eac816b8db 100644 --- a/Resources/Prototypes/CharacterItemGroups/musicianInstrumentsGroups.yml +++ b/Resources/Prototypes/CharacterItemGroups/musicianInstrumentsGroups.yml @@ -2,14 +2,36 @@ id: LoadoutMusicianInstruments maxItems: 3 items: + # Brass - type: loadout - id: LoadoutItemSynthesizerInstrumentMusician + id: LoadoutItemTrumpetInstrumentMusician + - type: loadout + id: LoadoutItemTromboneInstrumentMusician + - type: loadout + id: LoadoutItemFrenchHornInstrumentMusician + - type: loadout + id: LoadoutItemEuphoniumInstrumentMusician + # Misc + - type: loadout + id: LoadoutItemSeashellInstrumentMusician + - type: loadout + id: LoadoutItemBirdToyInstrumentMusician + # Percussion + - type: loadout + id: LoadoutItemGlockenspielInstrumentMusician + - type: loadout + id: LoadoutItemMusicBoxInstrumentMusician + - type: loadout + id: LoadoutItemXylophoneInstrumentMusician - type: loadout id: LoadoutItemMicrophoneInstrumentMusician + - type: loadout + id: LoadoutItemSynthesizerInstrumentMusician - type: loadout id: LoadoutItemKalimbaInstrumentMusician - type: loadout - id: LoadoutItemTrumpetInstrumentMusician + id: LoadoutItemWoodblockInstrumentMusician + # String - type: loadout id: LoadoutItemElectricGuitarInstrumentMusician - type: loadout @@ -18,14 +40,55 @@ id: LoadoutItemRockGuitarInstrumentMusician - type: loadout id: LoadoutItemAcousticGuitarInstrumentMusician + - type: loadout + id: LoadoutItemBanjoInstrumentMusician - type: loadout id: LoadoutItemViolinInstrumentMusician - type: loadout - id: LoadoutItemHarmonicaInstrumentMusician + id: LoadoutItemViolaInstrumentMusician + - type: loadout + id: LoadoutItemCelloInstrumentMusician + # Structure + - type: loadout + id: LoadoutItemPianoInstrumentMusician + - type: loadout + id: LoadoutItemUprightPianoInstrumentMusician + - type: loadout + id: LoadoutItemVibraphoneInstrumentMusician + - type: loadout + id: LoadoutItemMarimbaInstrumentMusician + - type: loadout + id: LoadoutItemChurchOrganInstrumentMusician + - type: loadout + id: LoadoutItemTubaInstrumentMusician + - type: loadout + id: LoadoutItemHarpInstrumentMusician + - type: loadout + id: LoadoutItemTimpaniInstrumentMusician + - type: loadout + id: LoadoutItemTaikoInstrumentMusician + - type: loadout + id: LoadoutItemContrabassInstrumentMusician + - type: loadout + id: LoadoutItemMinimoogInstrumentMusician + - type: loadout + id: LoadoutItemTomDrumsInstrumentMusician + # Wind + - type: loadout + id: LoadoutItemSaxophoneInstrumentMusician - type: loadout id: LoadoutItemAccordionInstrumentMusician + - type: loadout + id: LoadoutItemHarmonicaInstrumentMusician + - type: loadout + id: LoadoutItemClarinetInstrumentMusician - type: loadout id: LoadoutItemFluteInstrumentMusician + - type: loadout + id: LoadoutItemRecorderInstrumentMusician + - type: loadout + id: LoadoutItemPanFluteInstrumentMusician - type: loadout id: LoadoutItemOcarinaInstrumentMusician - + - type: loadout + id: LoadoutItemBagpipeInstrumentMusician diff --git a/Resources/Prototypes/Entities/Objects/Devices/instrumentFlatpacks.yml b/Resources/Prototypes/Entities/Objects/Devices/instrumentFlatpacks.yml new file mode 100644 index 0000000000..feb0e2ab5f --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/instrumentFlatpacks.yml @@ -0,0 +1,121 @@ +- type: entity + parent: BaseFlatpack + id: InstrumentFlatpack + name: instrument flatpack + description: A flatpack used for constructing something. + categories: + - hideSpawnMenu + components: + - type: Item + size: Normal + - type: Sprite + layers: + - state: service_music + +- type: entity + parent: InstrumentFlatpack + id: PianoFlatpack + name: piano flatpack + description: A flatpack used for constructing a piano. + components: + - type: Flatpack + entity: PianoInstrument + +- type: entity + parent: InstrumentFlatpack + id: UprightPianoFlatpack + name: upright piano flatpack + description: A flatpack used for constructing an upright piano. + components: + - type: Flatpack + entity: UprightPianoInstrument + +- type: entity + parent: InstrumentFlatpack + id: VibraphoneFlatpack + name: vibraphone flatpack + description: A flatpack used for constructing a vibraphone. + components: + - type: Flatpack + entity: VibraphoneInstrument + +- type: entity + parent: InstrumentFlatpack + id: MarimbaFlatpack + name: marimba flatpack + description: A flatpack used for constructing a marimba. + components: + - type: Flatpack + entity: MarimbaInstrument + +- type: entity + parent: InstrumentFlatpack + id: ChurchOrganFlatpack + name: church organ flatpack + description: A flatpack used for constructing a church organ. + components: + - type: Flatpack + entity: ChurchOrganInstrument + +- type: entity + parent: InstrumentFlatpack + id: TubaFlatpack + name: tuba flatpack + description: A flatpack used for constructing a tuba. + components: + - type: Flatpack + entity: TubaInstrument + +- type: entity + parent: InstrumentFlatpack + id: HarpFlatpack + name: harp flatpack + description: A flatpack used for constructing a harp. + components: + - type: Flatpack + entity: HarpInstrument + +- type: entity + parent: InstrumentFlatpack + id: TimpaniFlatpack + name: timpani flatpack + description: A flatpack used for constructing a timpani. + components: + - type: Flatpack + entity: TimpaniInstrument + +- type: entity + parent: InstrumentFlatpack + id: TaikoFlatpack + name: taiko flatpack + description: A flatpack used for constructing a taiko. + components: + - type: Flatpack + entity: TaikoInstrument + +- type: entity + parent: InstrumentFlatpack + id: ContrabassFlatpack + name: contrabass flatpack + description: A flatpack used for constructing a contrabass. + components: + - type: Flatpack + entity: ContrabassInstrument + +- type: entity + parent: InstrumentFlatpack + id: MinimoogFlatpack + name: minimoog flatpack + description: A flatpack used for constructing a minimoog. + components: + - type: Flatpack + entity: MinimoogInstrument + +- type: entity + parent: InstrumentFlatpack + id: TomDrumsFlatpack + name: tom drums flatpack + description: A flatpack used for constructing a tom drums set. + components: + - type: Flatpack + entity: TomDrumsInstrument diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_wind.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_wind.yml index e99f825d48..e37fbaa361 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_wind.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_wind.yml @@ -70,7 +70,7 @@ slots: - neck - type: ActivatableUI - inHandsOnly: false + inHandsOnly: false - type: Tag tags: - WoodwindInstrument diff --git a/Resources/Prototypes/Loadouts/Categories/categories.yml b/Resources/Prototypes/Loadouts/Categories/categories.yml index c47d6ddef8..bfda095b19 100644 --- a/Resources/Prototypes/Loadouts/Categories/categories.yml +++ b/Resources/Prototypes/Loadouts/Categories/categories.yml @@ -68,6 +68,7 @@ - JobsServiceBotanist - JobsServiceChef - JobsServiceJanitor + - JobsServiceMusician - type: loadoutCategory id: JobsServiceUncategorized @@ -84,6 +85,9 @@ - type: loadoutCategory id: JobsServiceJanitor +- type: loadoutCategory + id: JobsServiceMusician + - type: loadoutCategory id: Mask root: true diff --git a/Resources/Prototypes/Loadouts/Jobs/Service/musician.yml b/Resources/Prototypes/Loadouts/Jobs/Service/musician.yml new file mode 100644 index 0000000000..5bf5471697 --- /dev/null +++ b/Resources/Prototypes/Loadouts/Jobs/Service/musician.yml @@ -0,0 +1,554 @@ +# Musician +# Musician Instruments +# Brass Instruments +- type: loadout + id: LoadoutItemTrumpetInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - TrumpetInstrument + +- type: loadout + id: LoadoutItemTromboneInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - TromboneInstrument + +- type: loadout + id: LoadoutItemFrenchHornInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - FrenchHornInstrument + +- type: loadout + id: LoadoutItemEuphoniumInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - EuphoniumInstrument + +# Misc Instruments +- type: loadout + id: LoadoutItemSeashellInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - SeashellInstrument + +- type: loadout + id: LoadoutItemBirdToyInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - BirdToyInstrument + # After this are some instruments like "Phone, Helicopter, and Canned Aplause. I leave those to the Clown + +# Percussion +- type: loadout + id: LoadoutItemGlockenspielInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - GlockenspielInstrument + +- type: loadout + id: LoadoutItemMusicBoxInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - MusicBoxInstrument + +- type: loadout + id: LoadoutItemXylophoneInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - XylophoneInstrument + +- type: loadout + id: LoadoutItemMicrophoneInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - MicrophoneInstrument + +- type: loadout + id: LoadoutItemSynthesizerInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - SynthesizerInstrument + +- type: loadout + id: LoadoutItemKalimbaInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - KalimbaInstrument + +- type: loadout + id: LoadoutItemWoodblockInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - WoodblockInstrument + +# Stringed Instruments +- type: loadout + id: LoadoutItemElectricGuitarInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - ElectricGuitarInstrument + +- type: loadout + id: LoadoutItemBassGuitarInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - BassGuitarInstrument + +- type: loadout + id: LoadoutItemRockGuitarInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - RockGuitarInstrument + +- type: loadout + id: LoadoutItemAcousticGuitarInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - AcousticGuitarInstrument + +- type: loadout + id: LoadoutItemBanjoInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - BanjoInstrument + +- type: loadout + id: LoadoutItemViolinInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - ViolinInstrument + +- type: loadout + id: LoadoutItemViolaInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - ViolaInstrument + +- type: loadout + id: LoadoutItemCelloInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - CelloInstrument + +# Structure Instruments +- type: loadout + id: LoadoutItemPianoInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - PianoFlatpack + +- type: loadout + id: LoadoutItemUprightPianoInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - UprightPianoFlatpack + +- type: loadout + id: LoadoutItemVibraphoneInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - VibraphoneFlatpack + +- type: loadout + id: LoadoutItemMarimbaInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - MarimbaFlatpack + +- type: loadout + id: LoadoutItemChurchOrganInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - ChurchOrganFlatpack + +- type: loadout + id: LoadoutItemTubaInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - TubaFlatpack + +- type: loadout + id: LoadoutItemHarpInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - HarpFlatpack + +- type: loadout + id: LoadoutItemTimpaniInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - TimpaniFlatpack + +- type: loadout + id: LoadoutItemTaikoInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - TaikoFlatpack + +- type: loadout + id: LoadoutItemContrabassInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - ContrabassFlatpack + +- type: loadout + id: LoadoutItemMinimoogInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - MinimoogFlatpack + +- type: loadout + id: LoadoutItemTomDrumsInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - TomDrumsFlatpack + +# Wind Instruments +- type: loadout + id: LoadoutItemSaxophoneInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - SaxophoneInstrument + +- type: loadout + id: LoadoutItemAccordionInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - AccordionInstrument + +- type: loadout + id: LoadoutItemHarmonicaInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - HarmonicaInstrument + +- type: loadout + id: LoadoutItemClarinetInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - ClarinetInstrument + +- type: loadout + id: LoadoutItemFluteInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - FluteInstrument + +- type: loadout + id: LoadoutItemRecorderInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - RecorderInstrument + +- type: loadout + id: LoadoutItemPanFluteInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - PanFluteInstrument + +- type: loadout + id: LoadoutItemOcarinaInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - OcarinaInstrument + +- type: loadout + id: LoadoutItemBagpipeInstrumentMusician + category: JobsServiceMusician + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutMusicianInstruments + - !type:CharacterJobRequirement + jobs: + - Musician + items: + - BagpipeInstrument diff --git a/Resources/Prototypes/Loadouts/Jobs/service.yml b/Resources/Prototypes/Loadouts/Jobs/service.yml index b346bc3182..c0f04ef151 100644 --- a/Resources/Prototypes/Loadouts/Jobs/service.yml +++ b/Resources/Prototypes/Loadouts/Jobs/service.yml @@ -658,176 +658,6 @@ items: - ClothingUniformJumpskirtDetective -# Musician -- type: loadout - id: LoadoutItemSynthesizerInstrumentMusician - category: JobsServiceUncategorized - cost: 2 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - SynthesizerInstrument - -- type: loadout - id: LoadoutItemMicrophoneInstrumentMusician - category: JobsServiceUncategorized - cost: 0 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - MicrophoneInstrument - -- type: loadout - id: LoadoutItemKalimbaInstrumentMusician - category: Items - cost: 0 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - KalimbaInstrument - -- type: loadout - id: LoadoutItemTrumpetInstrumentMusician - category: Items - cost: 2 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - TrumpetInstrument - -- type: loadout - id: LoadoutItemElectricGuitarInstrumentMusician - category: Items - cost: 2 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - ElectricGuitarInstrument - -- type: loadout - id: LoadoutItemBassGuitarInstrumentMusician - category: Items - cost: 2 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - BassGuitarInstrument - -- type: loadout - id: LoadoutItemRockGuitarInstrumentMusician - category: Items - cost: 2 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - RockGuitarInstrument - -- type: loadout - id: LoadoutItemAcousticGuitarInstrumentMusician - category: Items - cost: 2 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - AcousticGuitarInstrument - -- type: loadout - id: LoadoutItemViolinInstrumentMusician - category: Items - cost: 2 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - ViolinInstrument - -- type: loadout - id: LoadoutItemHarmonicaInstrumentMusician - category: Items - cost: 0 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - HarmonicaInstrument - -- type: loadout - id: LoadoutItemAccordionInstrumentMusician - category: Items - cost: 0 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - AccordionInstrument - -- type: loadout - id: LoadoutItemFluteInstrumentMusician - category: Items - cost: 0 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - FluteInstrument - -- type: loadout - id: LoadoutItemOcarinaInstrumentMusician - category: Items - cost: 0 - requirements: - - !type:CharacterItemGroupRequirement - group: LoadoutMusicianInstruments - - !type:CharacterJobRequirement - jobs: - - Musician - items: - - OcarinaInstrument - # Chef - type: loadout id: LoadoutServiceJumpsuitChefNt @@ -996,4 +826,4 @@ jobs: - Janitor items: - - ClothingUniformJumpsuitJanitorOrion \ No newline at end of file + - ClothingUniformJumpsuitJanitorOrion diff --git a/Resources/Textures/Objects/Devices/flatpack.rsi/meta.json b/Resources/Textures/Objects/Devices/flatpack.rsi/meta.json index 8f46a0ca53..11f5d24e11 100644 --- a/Resources/Textures/Objects/Devices/flatpack.rsi/meta.json +++ b/Resources/Textures/Objects/Devices/flatpack.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC0-1.0", - "copyright": "Created by EmoGarbage404 (github) for SS14, solar-assembly-part taken from tgstation and modified at https://tgstation13.org/wiki/Guide_to_construction#Solar_Panels_and_Trackers, ame-part taken from vgstation at https://github.com/vgstation-coders/vgstation13/commit/1b7952787c06c21ef1623e494dcfe7cb1f46e041; singularity-generator, tesla-generator, radiation-collector, containment-field-generator, tesla-coil, grounding-rod inner icons made by lzk228; emitter made by pigeonpeas", + "copyright": "service_music by erhardsteinhauer, Created by EmoGarbage404 (github) for SS14, solar-assembly-part taken from tgstation and modified at https://tgstation13.org/wiki/Guide_to_construction#Solar_Panels_and_Trackers, ame-part taken from vgstation at https://github.com/vgstation-coders/vgstation13/commit/1b7952787c06c21ef1623e494dcfe7cb1f46e041; singularity-generator, tesla-generator, radiation-collector, containment-field-generator, tesla-coil, grounding-rod inner icons made by lzk228; emitter made by pigeonpeas", "size": { "x": 32, "y": 32 @@ -42,6 +42,9 @@ }, { "name": "emitter" + }, + { + "name": "service_music" } ] } diff --git a/Resources/Textures/Objects/Devices/flatpack.rsi/service_music.png b/Resources/Textures/Objects/Devices/flatpack.rsi/service_music.png new file mode 100644 index 0000000000000000000000000000000000000000..db1a9a8aec83bd88ed68bcc03469925cd709d3cc GIT binary patch literal 6361 zcmeHLXH-+!77i*!R6wbUXo!wdOixHLkXR z6zPaaQ$bKr!43k#C|I7T$e_NPfQWDA&3mr(-alC@Bm&C*jz+kF*G2Z@CPe2YAiG^HVI0%bb)%08= zhbB^gU7Sbj-uiLAa7ehhqiC?Efkz8ob9-s`)w6!??<6mIz)M*`t9ZRjJ^xl!Qp@E_ zS}9p?mRBAntMchiHGoQ=$-33G(--fuNj94EIEm+(?)LnO%|w@mGJn(k>RBzp%;JAI zV_{i+>ij$2Ev{-9^>(-3?w;kn4S`M4jZ<}(UUpt!HZRNeRsA{g5}*7+uR~_{TKxPC z7f;Ta>eP77NR<&{9W}qM^0&_ZrU*BN$F2j@)C$_r`oHu(-TTJOn4u|KpA ze3l;&9GK6@NKQGOE4_OvoA1HTI#}43E4zQTl)fxoR5|owkQzF`*s1Ng#N+UZ4xigw zHUA++?3$_5>B?APqN0;(k{6PMSKr!*-H|+9|FVAunY?E<7QW9gCp?Dreqym-(dM+z zhJs-RiY%>|P~3hacEf{J50)_aLjpWYQ{7bCP9Ey2VLjFVPFt!lG-z)}DSCL>K%m*P zhn5>RBQD>YRg)UxT5wi9{9(w6-Rlk{fz@wk*I8P)>Bam69_?}IXeq=P7p#rHKBEF& z^_x~}r-SYN=B%XK9)_<6)B|Td1W9J{U@@r?V?)dvYh=%RoRB_x4)_LD{T!LtS$Uj9_U4V9GEhTZ;q9rqQ^ z8g5Q-F$$PB&pdqoPe$>n@2|(k&7Bqkud3!8u)L;a^E%-keCnxebdHrlhL68E5Oikd z#fvs&5S}J)nzcWzyKWFXiFCPfFN8kYu~;T zJi0RbaM)tTP<>Xy+BV}QghA^z?tw`s~_TLt=HYtcdfCx%zH$7xJvZtcoi7IwVzshM2E)H%f<3H35q`~ASV zG8;48#rlC)d!M2A?;2izE28fTH)VP$u8`tY7*W%spKb098_ez8^T);9g)zHTZtG=o zl8mEc)Ew+L;;%UAOwqd7Tu`bLG)JAa+B!Di#ca$4N&14MHS}!*1=ZQ6ha+Pqck(>V z_GfBLN}!g0_F;&B3Qr0#i)dU}{iycP>4UoN?k`pjGVjFeE|sNY9$Y`!u9u3C4>>K1 z6Ar6BJBtLO+rvM5>^oHB>sZ!OwWi40pog{X&H20|I~p53ACcj!>vXO6tkwI--QMg- z`L(K|?`$$(9(Xqq22zPd4`RJ@?J?GZQVX?TJDd!!+r%>)ZgrllA<*ht>8n>)?0B{4 zWr?-ZxkUWTcHM0~`vm^g#?|-(dmEz$xI}7;m&!$7uqB9?D#(C=IC2h)?I_TU5QtHCd{jwVBuj(S@X(z@L?d{n)v$y)pSPB4*uGB*!o|N zEX(VE$-Xa0gMA8a|2gb~rEKN9Jp;>Krp#4|y??1j=CbGgx_Xn9?aY*^R}+TwH?5u% zWt}d(HsNC-qbc=%;ZutH+;xPvp&!)9s5eB?8!CpcKvmgLwiv;$>+C0?y;m5q?BB zjGM>~4Bxc0arq# zVRVGQn+M!ZCxm=Et<55C! zFd9ptP|z408izwd2&5!hAO)h30?9lD#R!KzC}E3vA}LQOfGaowmQW_8BM{I!e3Tqo zWjjPclcNq4Vi2UmBFD)eA(AOHYCnyoh#B9(Z5)?@1jdv;vmr2I^lu1Cvlrrvc4i^mt zrSv@B#);|XF=nHXF_^~}DJ>M}@kkDP3@4I_!<85g8x4kod?*kJ#Ekt0FXeH+9?&=Y zQLOwa5Xjva|2OC_dMRU3>Pod2vSkXVOnW*)5igY^Wb-&wo+O5D8)_L?nTL z;~*(476nPf<8fpxKmb4%XB-t%AdvzBHmIP2$Wc6q2M@3@cmN@0#et|;1TuxqCKHfsj1>+^u;NmXWE_!%q~Ne1 z#tPtaSzNM`N^uTU8xJNOfkR=w&Ul0aQm#3fW3SB^cx=DsHsqY=L03a!5n} z9r#a7ULiuc;J@Sf3LRsy5liJlaoAGvQdTI)mi|4@UxCM%JfUtUk&2_4|6x=Af}@Qj ztTSXQ6i1Kq?*>j7C~gI8P}R9Ik8wRDeAqzXXT`N3s>tab$@d0tkXZsNaqj+n0IX zUnDY)YemG904|aQTCtG?41tZLSP=mvo`PjlC~OLr%pFPOF?NZNE0qIc&?Xql0qC5e z;!>V7+-x-e%#{f-hL?wcisZuJ2uKVbiLvs=VyJiw6+=RdoHm?>R&>}eyQL|5keeHI zydE?~kD@XaiRdL0iNblH_*<4>H9;j zA98({0^bGxkzGIJ`Yr{&3;ZLy{%3M&etqNy1g)gH6#@yi{PRX-3ebx|HeWpx!%CO?{5KG3!(b^plH#X>a50U)7(C z4Z95e$`Vv#zAfyGUBoVrB!l6JCy$#ut4CPs-M2|A?R}78_p`Z){dw|Rb*V+hfVM`m z^U+3A+xW1isi;=w^!#G{bn3vN-?bh+_@n#cZ|`C28Sp_ii*{_da>=x?m2a&l(r3_~ zp1jpqTYHG4(j=d}H~OeZgXxPIp7rGQ2Ls__l4S`icYS?}<%t0OPf15w|6rgFSGCE~ zFEB`i)T@1!1twVD)H+ZL*6&Lin7QqIX5wi^qW**Z+6iL#oZ1Hk zCa}2hIj2+$`We?x1-jUBRcbiw96@2plsAt*tqG_|o{2eNqVoj(&ZgBpPCM`J-A_8F z0`%(^I;{un<0_hGH65@=UphR)Id=lAXl;pB{FCB2Ftt1DKIlZ0Z=vxDb~#U9q@(dk a-}>^=3~_W}g$Z;RFs6gM{aM?U8~zEhVYPDr literal 0 HcmV?d00001 From ef5bb6877f2889d7dc3cb998757fc739d1982f2a Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 20 Oct 2024 16:33:08 +0000 Subject: [PATCH 68/76] Automatic Changelog Update (#1105) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 896117ce8f..8c98fd3486 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7445,3 +7445,10 @@ Entries: id: 6472 time: '2024-10-20T16:31:21.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/960 +- author: VMSolidus + changes: + - type: Fix + message: Removed latejoin spawners from the Salvage Pathfinder. + id: 6473 + time: '2024-10-20T16:32:43.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1105 From 5f0f272192b0557627008ed6f7fd06f3016079d2 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 20 Oct 2024 16:33:33 +0000 Subject: [PATCH 69/76] Automatic Changelog Update (#1108) --- Resources/Changelog/Changelog.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 8c98fd3486..b08ba37381 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7452,3 +7452,18 @@ Entries: id: 6473 time: '2024-10-20T16:32:43.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1105 +- author: VMSolidus + changes: + - type: Add + message: >- + Musicians can now select for free, any 3 instruments in the game with + their loadout. This now includes things like Piano flatpacks. The only + instruments not included are the Sypersynth, DAW, and "Admeme + Instruments". + - type: Remove + message: >- + Musicians no longer spawn with a Saxophone and Acoustic guitar by + default. You pick which instruments you spawn with in your loadouts now. + id: 6474 + time: '2024-10-20T16:32:53.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1108 From b7215ac0d0407a42d465b976ca44de7677a1bb23 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 20 Oct 2024 12:35:00 -0400 Subject: [PATCH 70/76] Epistemics Job Localizations (#1094) # Description Part of our Maintainer meeting where we decided we wished to go "Completely all-out" with the "Science Cult" side of Epistemics. Next up, extremely fancy white robes. This PR localizes the various jobs in Epistemics to follow the "Science Cult" theme. Research Assistant became Novice, Scientist became Acolyte, Senior Researcher became Mystic, and Roboticist became Golemancer. So now the Epistemics roles are: Novice, Acolyte, Mystic, Golemancer, Chaplain, Psionic Mantis, Cataloguer, and Mystagogue. ![image](https://github.com/user-attachments/assets/4b2e0a26-5e6a-4dab-ae22-2d2144da8013) # Changelog :cl: - add: Added localizations for all Epistemics jobs. --- Resources/Locale/en-US/job/job-names.ftl | 15 ++++++++------- .../Roles/Jobs/Science/senior_researcher.yml | 2 +- .../Interface/Misc/job_icons.rsi/Librarian.png | Bin 119 -> 185 bytes 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Resources/Locale/en-US/job/job-names.ftl b/Resources/Locale/en-US/job/job-names.ftl index 534620f0f2..60ab9b0543 100644 --- a/Resources/Locale/en-US/job/job-names.ftl +++ b/Resources/Locale/en-US/job/job-names.ftl @@ -5,10 +5,11 @@ job-name-hos = Head of Security job-name-detective = Detective job-name-brigmedic = Corpsman job-name-borg = Cyborg -job-name-scientist = Scientist -job-name-research-assistant = Research Assistant +job-name-senior-researcher = Mystic +job-name-scientist = Acolyte +job-name-research-assistant = Noviciate job-name-rd = Mystagogue -job-name-roboticist = Roboticist +job-name-roboticist = Golemancer job-name-psychologist = Psychologist job-name-intern = Medical Intern job-name-doctor = Medical Doctor @@ -86,17 +87,17 @@ JobPassenger = Passenger JobPsychologist = Psychologist JobQuartermaster = Logistics Officer JobReporter = Reporter -JobResearchAssistant = Research Assistant +JobResearchAssistant = Noviciate JobResearchDirector = Mystagogue -JobRoboticist = Roboticist +JobRoboticist = Golemancer JobSalvageSpecialist = Salvage Specialist -JobScientist = Scientist +JobScientist = Acolyte JobSecurityCadet = Security Cadet JobSecurityOfficer = Security Officer JobSeniorEngineer = Senior Engineer JobSeniorOfficer = Senior Officer JobSeniorPhysician = Senior Physician -JobSeniorResearcher = Senior Researcher +JobSeniorResearcher = Mystic JobServiceWorker = Service Worker JobStationEngineer = Station Engineer JobTechnicalAssistant = Technical Assistant diff --git a/Resources/Prototypes/Roles/Jobs/Science/senior_researcher.yml b/Resources/Prototypes/Roles/Jobs/Science/senior_researcher.yml index 9023425030..25b170a46e 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/senior_researcher.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/senior_researcher.yml @@ -3,7 +3,7 @@ name: job-name-senior-researcher description: job-description-senior-researcher playTimeTracker: JobSeniorResearcher - setPreference: false # DeltaV - Disable Senior Roles round start selection + setPreference: true requirements: - !type:CharacterDepartmentTimeRequirement department: Epistemics # DeltaV - Epistemics Department replacing Science diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/Librarian.png b/Resources/Textures/Interface/Misc/job_icons.rsi/Librarian.png index b10b1ed71e05149a301041e695392de6b1d51b72..114283c053fdd4f724cb7f419fb5126cb38288ec 100644 GIT binary patch delta 168 zcmXTl$v8oxo`aczf#LH-znwseu{g-xiDBJ2nU_EgOS+@4BLl<6e(pbstUx|bfKP~P zw?Y5O-2MOm{|5>ro!XEIQdbh>7Yq_{&|3Kg$mcBZh%9Dc;5!V$jK}j=qyPogJY5_^ zB*K#u(ioT(j-EJiE#W93KHd$nA`k8mhXEvvudEca@5Pn6D!Q%y+x$yCmaX>{3p00i_>zopr04z5o AtN;K2 From 5e575d8ae4b28a46aaf84a5f8c2509c58d41cf0b Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 20 Oct 2024 16:35:26 +0000 Subject: [PATCH 71/76] Automatic Changelog Update (#1094) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index b08ba37381..3facddca67 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7467,3 +7467,10 @@ Entries: id: 6474 time: '2024-10-20T16:32:53.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1108 +- author: VMSolidus + changes: + - type: Add + message: 'Added localizations for all Epistemics jobs. ' + id: 6475 + time: '2024-10-20T16:35:00.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1094 From 5c91d23fed04e5e549627ccc9e9773dc6473b355 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 20 Oct 2024 13:29:15 -0400 Subject: [PATCH 72/76] Revert "Allow Talking Across Atmosphere (#1089)" (#1111) This reverts commit 9ddd9679eccce166e8fe99fab01377c1bf950d58. Introduced a game crashing bug. --- Content.Server/Chat/Systems/ChatSystem.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index 7ea98c2fe1..b4641928e4 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -39,7 +39,6 @@ using Content.Server.Shuttles.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Dynamics.Joints; -using Content.Shared.Atmos.Components; namespace Content.Server.Chat.Systems; @@ -507,8 +506,7 @@ private void SendEntityWhisper( if (session.AttachedEntity is not { Valid: true } listener) continue; - if (!HasComp(Transform(session.AttachedEntity.Value).MapUid) - && Transform(session.AttachedEntity.Value).GridUid != Transform(source).GridUid + if (Transform(session.AttachedEntity.Value).GridUid != Transform(source).GridUid && !CheckAttachedGrids(source, session.AttachedEntity.Value)) continue; @@ -714,10 +712,7 @@ private void SendInVoiceRange(ChatChannel channel, string name, string message, var language = languageOverride ?? _language.GetLanguage(source); foreach (var (session, data) in GetRecipients(source, Transform(source).GridUid == null ? 0.3f : VoiceRange)) { - if (session.AttachedEntity is not { Valid: true } playerEntity) - continue; - - if (!HasComp(Transform(session.AttachedEntity.Value).MapUid) + if (session.AttachedEntity != null && Transform(session.AttachedEntity.Value).GridUid != Transform(source).GridUid && !CheckAttachedGrids(source, session.AttachedEntity.Value)) continue; @@ -726,8 +721,11 @@ private void SendInVoiceRange(ChatChannel channel, string name, string message, if (entRange == MessageRangeCheckResult.Disallowed) continue; var entHideChat = entRange == MessageRangeCheckResult.HideChat; + if (session.AttachedEntity is not { Valid: true } playerEntity) + continue; EntityUid listener = session.AttachedEntity.Value; + // If the channel does not support languages, or the entity can understand the message, send the original message, otherwise send the obfuscated version if (channel == ChatChannel.LOOC || channel == ChatChannel.Emotes || _language.CanUnderstand(listener, language.ID)) { From 62fb1d9e96d99c06d1e0841245cbfa8d8145bf98 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 20 Oct 2024 14:18:35 -0400 Subject: [PATCH 73/76] Fix Gax Missing LV Cable (#1112) # Description Gax was missing a single LV cable, making it so that the Captain's office could be crowbarred into. ![image](https://github.com/user-attachments/assets/0718e64a-ff74-4e8e-9596-61636fd4bc70) # Changelog :cl: - fix: Fixed the Captain's Office on Gax Station missing an LV cable that would power its doors. --- Resources/Maps/gaxstation.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Resources/Maps/gaxstation.yml b/Resources/Maps/gaxstation.yml index 1e2d999ab2..3de887a3b3 100644 --- a/Resources/Maps/gaxstation.yml +++ b/Resources/Maps/gaxstation.yml @@ -29289,6 +29289,11 @@ entities: - type: Transform pos: 72.5,24.5 parent: 2 + - uid: 18587 + components: + - type: Transform + pos: 69.5,22.5 + parent: 2 - uid: 18794 components: - type: Transform @@ -124673,7 +124678,7 @@ entities: lastSignals: DoorStatus: True - type: Door - secondsUntilStateChange: -79958.195 + secondsUntilStateChange: -80064.195 state: Opening - uid: 18112 components: From eda5cfb4b7aa5e3d9e383a2cbbc1972a66bd6580 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 20 Oct 2024 18:19:02 +0000 Subject: [PATCH 74/76] Automatic Changelog Update (#1112) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3facddca67..0bf3fab5af 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7474,3 +7474,12 @@ Entries: id: 6475 time: '2024-10-20T16:35:00.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1094 +- author: VMSolidus + changes: + - type: Fix + message: >- + Fixed the Captain's Office on Gax Station missing an LV cable that would + power its doors. + id: 6476 + time: '2024-10-20T18:18:35.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1112 From d1a2d8e05afafa555a26d19e1ff5f7b9d3737336 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Sun, 20 Oct 2024 15:28:35 -0400 Subject: [PATCH 75/76] Fix Arrivals Not Respecting Force Spawn (#1113) # Description I HAVE VERIFIED WITH INGAME TESTING THAT THIS WORKS. Fixes #684 # Changelog :cl: - fix: Fixed prisoner not spawning in the Prison if the Arrivals station is enabled. --- Content.Server/Shuttles/Systems/ArrivalsSystem.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 4a2dc5df53..fef671fc30 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -18,6 +18,7 @@ using Content.Shared.Mobs.Components; using Content.Shared.Movement.Components; using Content.Shared.Parallax.Biomes; +using Content.Shared.Roles; using Content.Shared.Salvage; using Content.Shared.Shuttles.Components; using Content.Shared.Tiles; @@ -311,6 +312,12 @@ public void HandlePlayerSpawning(PlayerSpawningEvent ev) if (!Enabled || _ticker.RunLevel != GameRunLevel.InRound) return; + if (ev.Job is not null + && ev.Job.Prototype is not null + && _protoManager.Index(ev.Job.Prototype.Value.Id).AlwaysUseSpawner) + return; + + if (!HasComp(ev.Station)) return; From 54893bd2b0b493d55949d898e7d48a94883f50d4 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs Date: Sun, 20 Oct 2024 19:29:02 +0000 Subject: [PATCH 76/76] Automatic Changelog Update (#1113) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0bf3fab5af..2238e15475 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -7483,3 +7483,12 @@ Entries: id: 6476 time: '2024-10-20T18:18:35.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1112 +- author: VMSolidus + changes: + - type: Fix + message: >- + Fixed prisoner not spawning in the Prison if the Arrivals station is + enabled. + id: 6477 + time: '2024-10-20T19:28:35.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1113