From d8ae58efa903749314e334278c1a47c0fddd2b16 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Wed, 28 Aug 2024 10:57:12 +1000 Subject: [PATCH 01/38] Station AI (#30944) * Station AI overlay * implement * Bunch of ports * Fix a heap of bugs and basic scouting * helldivers * Shuffle interactions a bit * navmap stuff * Revert "navmap stuff" This reverts commit d1f89dd4be83233e22cf5dd062b2581f3c6da062. * AI wires implemented * Fix examines * Optimise the overlay significantly * Back to old static * BUI radial working * lots of work * Saving work * thanks fork * alright * pc * AI upload console * AI upload * stuff * Fix copy-paste shitcode * AI actions * navmap work * Fixes * first impressions * a * reh * Revert "navmap work" This reverts commit 6f63fea6e9245e189f368f97be3e32e9b210580e. * OD * radar * weh * Fix examines * scoop mine eyes * fixes * reh * Optimise * Final round of optimisations * Fixes * fixes --- Content.Client/Chat/UI/EmotesMenu.xaml.cs | 13 +- .../Laws/SiliconLawEditUi/SiliconLawEui.cs | 2 +- .../StationAi/StationAiBoundUserInterface.cs | 28 ++ .../Silicons/StationAi/StationAiMenu.xaml | 13 + .../Silicons/StationAi/StationAiMenu.xaml.cs | 128 ++++++ .../Silicons/StationAi/StationAiOverlay.cs | 23 +- .../StationAi/StationAiSystem.Airlock.cs | 30 ++ .../StationAi/StationAiSystem.Light.cs | 32 ++ .../Silicons/StationAi/StationAiSystem.cs | 6 +- Content.Client/Verbs/VerbSystem.cs | 5 +- .../Administration/Systems/AdminVerbSystem.cs | 1 + .../CommunicationsConsoleSystem.cs | 4 - .../DeviceNetworkRequiresPowerSystem.cs | 1 - .../Light/EntitySystems/LitOnPoweredSystem.cs | 1 - .../Light/EntitySystems/PoweredLightSystem.cs | 1 - Content.Server/Mind/MindSystem.cs | 4 +- .../Power/EntitySystems/ChargerSystem.cs | 35 +- .../Power/EntitySystems/PowerNetSystem.cs | 1 - .../Power/Generation/Teg/TegSystem.cs | 1 - .../Power/Generator/GasPowerReceiverSystem.cs | 1 - .../Silicons/Laws/SiliconLawSystem.cs | 35 +- .../StationAi/AiInteractWireAction.cs | 37 ++ .../Silicons/StationAi/AiVisionWireAction.cs | 40 ++ .../Silicons/StationAi/StationAiSystem.cs | 76 ++++ .../Sound/SpamEmitSoundRequirePowerSystem.cs | 1 - .../Station/Systems/StationSpawningSystem.cs | 23 + .../Melee/EnergySword/EnergySwordSystem.cs | 2 +- .../ArtifactElectricityTriggerSystem.cs | 2 +- .../ActionBlocker/ActionBlockerSystem.cs | 15 + .../Climbing/Systems/ClimbSystem.cs | 20 + .../Configurable/ConfigurationComponent.cs | 3 +- Content.Shared/Doors/AirlockWireStatus.cs | 2 +- Content.Shared/Examine/ExamineSystemShared.cs | 34 +- .../Interaction/SharedInteractionSystem.cs | 67 ++- .../LightOnCollideColliderComponent.cs | 13 + .../Components/LightOnCollideComponent.cs | 11 + .../Light/EntitySystems/LightCollideSystem.cs | 82 ++++ .../EntitySystems/SlimPoweredLightSystem.cs | 1 - Content.Shared/Mind/SharedMindSystem.cs | 4 + .../Components/SiliconLawBoundComponent.cs | 15 +- .../Components/SiliconLawUpdaterComponent.cs | 17 + .../Laws/SharedSiliconLawSystem.Updater.cs | 17 + .../Silicons/Laws/SharedSiliconLawSystem.cs | 3 +- .../SharedStationAiSystem.Airlock.cs | 25 ++ .../StationAi/SharedStationAiSystem.Held.cs | 187 ++++++++ .../StationAi/SharedStationAiSystem.Light.cs | 28 ++ .../StationAi/SharedStationAiSystem.cs | 412 ++++++++++++++++++ .../StationAi/StationAiCoreComponent.cs | 32 ++ .../StationAi/StationAiHeldComponent.cs | 9 + .../StationAi/StationAiHolderComponent.cs | 16 + .../StationAi/StationAiVisionComponent.cs | 5 +- .../StationAi/StationAiVisionSystem.cs | 131 ++---- .../StationAi/StationAiWhitelistComponent.cs | 13 + .../Station/SharedStationSpawningSystem.cs | 3 + .../UserInterface/ActivatableUIComponent.cs | 8 +- .../UserInterface/ActivatableUISystem.cs | 25 +- Content.Shared/Verbs/SharedVerbSystem.cs | 17 +- Content.Shared/Verbs/VerbEvents.cs | 8 +- Content.Shared/Wires/SharedWiresSystem.cs | 20 +- .../Audio/Effects/Footsteps/attributions.yml | 2 +- .../Audio/Effects/Footsteps/borgwalk2.ogg | Bin 20233 -> 9052 bytes .../administration/ui/silicon-law-ui.ftl | 2 + .../Locale/en-US/job/department-desc.ftl | 1 + Resources/Locale/en-US/job/department.ftl | 1 + .../Locale/en-US/job/job-description.ftl | 1 + Resources/Locale/en-US/job/job-names.ftl | 2 + .../Locale/en-US/silicons/station-ai.ftl | 14 + Resources/Maps/Test/dev_map.yml | 7 + Resources/Prototypes/Datasets/Names/ai.yml | 2 +- .../Mobs/Cyborgs/base_borg_chassis.yml | 3 + .../Entities/Mobs/Cyborgs/borg_chassis.yml | 6 + .../Entities/Mobs/NPCs/revenant.yml | 9 +- .../Entities/Mobs/NPCs/simplemob.yml | 1 + .../Entities/Mobs/Player/observer.yml | 53 ++- .../Entities/Mobs/Player/silicon.yml | 303 +++++++++++++ .../Prototypes/Entities/Mobs/Species/base.yml | 1 + Resources/Prototypes/Entities/Mobs/base.yml | 7 + .../Fun/Instruments/base_instruments.yml | 2 +- .../Entities/Objects/Misc/paper.yml | 4 +- .../Objects/Specific/Robotics/mmi.yml | 2 +- .../Objects/Tools/access_configurator.yml | 2 +- .../Doors/Airlocks/base_structureairlocks.yml | 3 + .../Machines/Computers/computers.yml | 44 ++ .../Structures/Machines/Medical/cryo_pod.yml | 2 +- .../Entities/Structures/Power/apc.yml | 1 + .../Structures/Wallmounts/intercom.yml | 1 + .../Wallmounts/surveillance_camera.yml | 24 + .../Entities/Structures/Wallmounts/timer.yml | 1 + .../Prototypes/Roles/Jobs/Science/borg.yml | 15 + .../Prototypes/Roles/Jobs/departments.yml | 10 +- .../Prototypes/Roles/play_time_trackers.yml | 3 + Resources/Prototypes/StatusIcon/job.yml | 8 + Resources/Prototypes/Wires/layouts.yml | 4 +- Resources/Prototypes/tags.yml | 3 + .../Actions/actions_ai.rsi/ai_core.png | Bin 0 -> 269 bytes .../Actions/actions_ai.rsi/camera_light.png | Bin 0 -> 309 bytes .../Actions/actions_ai.rsi/crew_monitor.png | Bin 0 -> 295 bytes .../Actions/actions_ai.rsi/manifest.png | Bin 0 -> 245 bytes .../Actions/actions_ai.rsi/meta.json | 26 ++ .../Actions/actions_ai.rsi/state_laws.png | Bin 0 -> 241 bytes .../Misc/job_icons.rsi/StationAi.png | Bin 0 -> 204 bytes .../Interface/Misc/job_icons.rsi/meta.json | 5 +- .../Textures/Interface/noise.rsi/meta.json | 58 +++ .../Textures/Interface/noise.rsi/noise.png | Bin 0 -> 45602 bytes .../Silicon/output.rsi/ai-banned-unshaded.png | Bin 0 -> 820 bytes .../Mobs/Silicon/output.rsi/ai-banned.png | Bin 0 -> 1589 bytes .../output.rsi/ai-banned_dead-unshaded.png | Bin 0 -> 109 bytes .../Silicon/output.rsi/ai-banned_dead.png | Bin 0 -> 463 bytes .../Silicon/output.rsi/ai-empty-unshaded.png | Bin 0 -> 109 bytes .../Mobs/Silicon/output.rsi/ai-empty.png | Bin 0 -> 451 bytes .../output.rsi/ai-holo-old-unshaded.png | Bin 0 -> 2483 bytes .../Mobs/Silicon/output.rsi/ai-holo-old.png | Bin 0 -> 7280 bytes .../Mobs/Silicon/output.rsi/ai-unshaded.png | Bin 0 -> 6255 bytes .../Textures/Mobs/Silicon/output.rsi/ai.png | Bin 0 -> 6761 bytes .../Silicon/output.rsi/ai_dead-unshaded.png | Bin 0 -> 453 bytes .../Mobs/Silicon/output.rsi/ai_dead.png | Bin 0 -> 453 bytes .../Silicon/output.rsi/default-unshaded.png | Bin 0 -> 2005 bytes .../Mobs/Silicon/output.rsi/default.png | Bin 0 -> 2005 bytes .../output.rsi/floating_face-unshaded.png | Bin 0 -> 721 bytes .../Mobs/Silicon/output.rsi/floating_face.png | Bin 0 -> 721 bytes .../Silicon/output.rsi/horror-unshaded.png | Bin 0 -> 1777 bytes .../Mobs/Silicon/output.rsi/horror.png | Bin 0 -> 1404 bytes .../Mobs/Silicon/output.rsi/meta.json | 1 + .../output.rsi/xeno_queen-unshaded.png | Bin 0 -> 2484 bytes .../Mobs/Silicon/output.rsi/xeno_queen.png | Bin 0 -> 2484 bytes .../Mobs/Silicon/station_ai.rsi/ai.png | Bin 0 -> 9757 bytes .../Mobs/Silicon/station_ai.rsi/ai_dead.png | Bin 0 -> 4405 bytes .../Mobs/Silicon/station_ai.rsi/ai_empty.png | Bin 0 -> 4405 bytes .../Mobs/Silicon/station_ai.rsi/base.png | Bin 0 -> 4421 bytes .../Mobs/Silicon/station_ai.rsi/default.png | Bin 0 -> 2082 bytes .../Mobs/Silicon/station_ai.rsi/meta.json | 52 +++ .../Objects/Devices/ai_card.rsi/base.png | Bin 0 -> 4432 bytes .../Objects/Devices/ai_card.rsi/empty.png | Bin 0 -> 4292 bytes .../Objects/Devices/ai_card.rsi/full.png | Bin 0 -> 5121 bytes .../Devices/ai_card.rsi/inhand-left.png | Bin 0 -> 306 bytes .../Devices/ai_card.rsi/inhand-right.png | Bin 0 -> 316 bytes .../Objects/Devices/ai_card.rsi/meta.json | 58 +++ .../output.rsi/aicard-full-unshaded.png | Bin 0 -> 777 bytes .../Devices/output.rsi/aicard-full.png | Bin 0 -> 1746 bytes .../Devices/output.rsi/aicard-unshaded.png | Bin 0 -> 138 bytes .../Objects/Devices/output.rsi/aicard.png | Bin 0 -> 414 bytes .../Objects/Devices/output.rsi/meta.json | 1 + 142 files changed, 2278 insertions(+), 256 deletions(-) create mode 100644 Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs create mode 100644 Content.Client/Silicons/StationAi/StationAiMenu.xaml create mode 100644 Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs create mode 100644 Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs create mode 100644 Content.Client/Silicons/StationAi/StationAiSystem.Light.cs create mode 100644 Content.Server/Silicons/StationAi/AiInteractWireAction.cs create mode 100644 Content.Server/Silicons/StationAi/AiVisionWireAction.cs create mode 100644 Content.Server/Silicons/StationAi/StationAiSystem.cs create mode 100644 Content.Shared/Light/Components/LightOnCollideColliderComponent.cs create mode 100644 Content.Shared/Light/Components/LightOnCollideComponent.cs create mode 100644 Content.Shared/Light/EntitySystems/LightCollideSystem.cs create mode 100644 Content.Shared/Silicons/Laws/Components/SiliconLawUpdaterComponent.cs create mode 100644 Content.Shared/Silicons/Laws/SharedSiliconLawSystem.Updater.cs create mode 100644 Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs create mode 100644 Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs create mode 100644 Content.Shared/Silicons/StationAi/SharedStationAiSystem.Light.cs create mode 100644 Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs create mode 100644 Content.Shared/Silicons/StationAi/StationAiCoreComponent.cs create mode 100644 Content.Shared/Silicons/StationAi/StationAiHeldComponent.cs create mode 100644 Content.Shared/Silicons/StationAi/StationAiHolderComponent.cs create mode 100644 Content.Shared/Silicons/StationAi/StationAiWhitelistComponent.cs create mode 100644 Resources/Locale/en-US/silicons/station-ai.ftl create mode 100644 Resources/Textures/Interface/Actions/actions_ai.rsi/ai_core.png create mode 100644 Resources/Textures/Interface/Actions/actions_ai.rsi/camera_light.png create mode 100644 Resources/Textures/Interface/Actions/actions_ai.rsi/crew_monitor.png create mode 100644 Resources/Textures/Interface/Actions/actions_ai.rsi/manifest.png create mode 100644 Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json create mode 100644 Resources/Textures/Interface/Actions/actions_ai.rsi/state_laws.png create mode 100644 Resources/Textures/Interface/Misc/job_icons.rsi/StationAi.png create mode 100644 Resources/Textures/Interface/noise.rsi/meta.json create mode 100644 Resources/Textures/Interface/noise.rsi/noise.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai-banned-unshaded.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai-banned.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead-unshaded.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai-empty-unshaded.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai-empty.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old-unshaded.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai-unshaded.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai_dead-unshaded.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/ai_dead.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/default-unshaded.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/default.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/floating_face-unshaded.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/floating_face.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/horror-unshaded.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/horror.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/meta.json create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen-unshaded.png create mode 100644 Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen.png create mode 100644 Resources/Textures/Mobs/Silicon/station_ai.rsi/ai.png create mode 100644 Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_dead.png create mode 100644 Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_empty.png create mode 100644 Resources/Textures/Mobs/Silicon/station_ai.rsi/base.png create mode 100644 Resources/Textures/Mobs/Silicon/station_ai.rsi/default.png create mode 100644 Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json create mode 100644 Resources/Textures/Objects/Devices/ai_card.rsi/base.png create mode 100644 Resources/Textures/Objects/Devices/ai_card.rsi/empty.png create mode 100644 Resources/Textures/Objects/Devices/ai_card.rsi/full.png create mode 100644 Resources/Textures/Objects/Devices/ai_card.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Devices/ai_card.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Devices/ai_card.rsi/meta.json create mode 100644 Resources/Textures/Objects/Devices/output.rsi/aicard-full-unshaded.png create mode 100644 Resources/Textures/Objects/Devices/output.rsi/aicard-full.png create mode 100644 Resources/Textures/Objects/Devices/output.rsi/aicard-unshaded.png create mode 100644 Resources/Textures/Objects/Devices/output.rsi/aicard.png create mode 100644 Resources/Textures/Objects/Devices/output.rsi/meta.json diff --git a/Content.Client/Chat/UI/EmotesMenu.xaml.cs b/Content.Client/Chat/UI/EmotesMenu.xaml.cs index 3340755343..f3b7837f21 100644 --- a/Content.Client/Chat/UI/EmotesMenu.xaml.cs +++ b/Content.Client/Chat/UI/EmotesMenu.xaml.cs @@ -19,9 +19,6 @@ public sealed partial class EmotesMenu : RadialMenu [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly ISharedPlayerManager _playerManager = default!; - private readonly SpriteSystem _spriteSystem; - private readonly EntityWhitelistSystem _whitelistSystem; - public event Action<ProtoId<EmotePrototype>>? OnPlayEmote; public EmotesMenu() @@ -29,8 +26,8 @@ public EmotesMenu() IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); - _spriteSystem = _entManager.System<SpriteSystem>(); - _whitelistSystem = _entManager.System<EntityWhitelistSystem>(); + var spriteSystem = _entManager.System<SpriteSystem>(); + var whitelistSystem = _entManager.System<EntityWhitelistSystem>(); var main = FindControl<RadialContainer>("Main"); @@ -40,8 +37,8 @@ public EmotesMenu() var player = _playerManager.LocalSession?.AttachedEntity; if (emote.Category == EmoteCategory.Invalid || emote.ChatTriggers.Count == 0 || - !(player.HasValue && _whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) || - _whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value)) + !(player.HasValue && whitelistSystem.IsWhitelistPassOrNull(emote.Whitelist, player.Value)) || + whitelistSystem.IsBlacklistPass(emote.Blacklist, player.Value)) continue; if (!emote.Available && @@ -63,7 +60,7 @@ public EmotesMenu() { VerticalAlignment = VAlignment.Center, HorizontalAlignment = HAlignment.Center, - Texture = _spriteSystem.Frame0(emote.Icon), + Texture = spriteSystem.Frame0(emote.Icon), TextureScale = new Vector2(2f, 2f), }; diff --git a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs index a4d59d1f31..03c74032f7 100644 --- a/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs +++ b/Content.Client/Silicons/Laws/SiliconLawEditUi/SiliconLawEui.cs @@ -6,7 +6,7 @@ namespace Content.Client.Silicons.Laws.SiliconLawEditUi; public sealed class SiliconLawEui : BaseEui { - public readonly EntityManager _entityManager = default!; + private readonly EntityManager _entityManager; private SiliconLawUi _siliconLawUi; private EntityUid _target; diff --git a/Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs b/Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs new file mode 100644 index 0000000000..68318305a0 --- /dev/null +++ b/Content.Client/Silicons/StationAi/StationAiBoundUserInterface.cs @@ -0,0 +1,28 @@ +using Content.Shared.Silicons.StationAi; +using Robust.Client.UserInterface; + +namespace Content.Client.Silicons.StationAi; + +public sealed class StationAiBoundUserInterface : BoundUserInterface +{ + private StationAiMenu? _menu; + + public StationAiBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + _menu = this.CreateWindow<StationAiMenu>(); + _menu.Track(Owner); + + _menu.OnAiRadial += args => + { + SendPredictedMessage(new StationAiRadialMessage() + { + Event = args, + }); + }; + } +} diff --git a/Content.Client/Silicons/StationAi/StationAiMenu.xaml b/Content.Client/Silicons/StationAi/StationAiMenu.xaml new file mode 100644 index 0000000000..d56fc83289 --- /dev/null +++ b/Content.Client/Silicons/StationAi/StationAiMenu.xaml @@ -0,0 +1,13 @@ +<ui:RadialMenu xmlns="https://spacestation14.io" + xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls" + BackButtonStyleClass="RadialMenuBackButton" + CloseButtonStyleClass="RadialMenuCloseButton" + VerticalExpand="True" + HorizontalExpand="True" + MinSize="450 450"> + + <!-- Main --> + <ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" Radius="64" ReserveSpaceForHiddenChildren="False"> + </ui:RadialContainer> + +</ui:RadialMenu> diff --git a/Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs b/Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs new file mode 100644 index 0000000000..24a802a60f --- /dev/null +++ b/Content.Client/Silicons/StationAi/StationAiMenu.xaml.cs @@ -0,0 +1,128 @@ +using System.Numerics; +using Content.Client.UserInterface.Controls; +using Content.Shared.Silicons.StationAi; +using Robust.Client.AutoGenerated; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Timing; + +namespace Content.Client.Silicons.StationAi; + +[GenerateTypedNameReferences] +public sealed partial class StationAiMenu : RadialMenu +{ + [Dependency] private readonly IClyde _clyde = default!; + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IEyeManager _eyeManager = default!; + + public event Action<BaseStationAiAction>? OnAiRadial; + + private EntityUid _tracked; + + public StationAiMenu() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + } + + public void Track(EntityUid owner) + { + _tracked = owner; + + if (!_entManager.EntityExists(_tracked)) + { + Close(); + return; + } + + BuildButtons(); + UpdatePosition(); + } + + private void BuildButtons() + { + var ev = new GetStationAiRadialEvent(); + _entManager.EventBus.RaiseLocalEvent(_tracked, ref ev); + + var main = FindControl<RadialContainer>("Main"); + main.DisposeAllChildren(); + var sprites = _entManager.System<SpriteSystem>(); + + foreach (var action in ev.Actions) + { + // TODO: This radial boilerplate is quite annoying + var button = new StationAiMenuButton(action.Event) + { + StyleClasses = { "RadialMenuButton" }, + SetSize = new Vector2(64f, 64f), + ToolTip = action.Tooltip != null ? Loc.GetString(action.Tooltip) : null, + }; + + if (action.Sprite != null) + { + var texture = sprites.Frame0(action.Sprite); + var scale = Vector2.One; + + if (texture.Width <= 32) + { + scale *= 2; + } + + var tex = new TextureRect + { + VerticalAlignment = VAlignment.Center, + HorizontalAlignment = HAlignment.Center, + Texture = texture, + TextureScale = scale, + }; + + button.AddChild(tex); + } + + button.OnPressed += args => + { + OnAiRadial?.Invoke(action.Event); + Close(); + }; + main.AddChild(button); + } + } + + protected override void FrameUpdate(FrameEventArgs args) + { + base.FrameUpdate(args); + UpdatePosition(); + } + + private void UpdatePosition() + { + if (!_entManager.TryGetComponent(_tracked, out TransformComponent? xform)) + { + Close(); + return; + } + + if (!xform.Coordinates.IsValid(_entManager)) + { + Close(); + return; + } + + var coords = _entManager.System<SpriteSystem>().GetSpriteScreenCoordinates((_tracked, null, xform)); + + if (!coords.IsValid) + { + Close(); + return; + } + + OpenScreenAt(coords.Position, _clyde); + } +} + +public sealed class StationAiMenuButton(BaseStationAiAction action) : RadialMenuTextureButton +{ + public BaseStationAiAction Action = action; +} diff --git a/Content.Client/Silicons/StationAi/StationAiOverlay.cs b/Content.Client/Silicons/StationAi/StationAiOverlay.cs index efa1b8dbef..15a8a3a63f 100644 --- a/Content.Client/Silicons/StationAi/StationAiOverlay.cs +++ b/Content.Client/Silicons/StationAi/StationAiOverlay.cs @@ -4,7 +4,9 @@ using Robust.Client.Player; using Robust.Shared.Enums; using Robust.Shared.Map.Components; +using Robust.Shared.Physics; using Robust.Shared.Prototypes; +using Robust.Shared.Timing; namespace Content.Client.Silicons.StationAi; @@ -12,6 +14,7 @@ public sealed class StationAiOverlay : Overlay { [Dependency] private readonly IClyde _clyde = default!; [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IPrototypeManager _proto = default!; @@ -22,6 +25,9 @@ public sealed class StationAiOverlay : Overlay private IRenderTexture? _staticTexture; private IRenderTexture? _stencilTexture; + private float _updateRate = 1f / 30f; + private float _accumulator; + public StationAiOverlay() { IoCManager.InjectDependencies(this); @@ -47,19 +53,22 @@ protected override void Draw(in OverlayDrawArgs args) _entManager.TryGetComponent(playerEnt, out TransformComponent? playerXform); var gridUid = playerXform?.GridUid ?? EntityUid.Invalid; _entManager.TryGetComponent(gridUid, out MapGridComponent? grid); + _entManager.TryGetComponent(gridUid, out BroadphaseComponent? broadphase); var invMatrix = args.Viewport.GetWorldToLocalMatrix(); + _accumulator -= (float) _timing.FrameTime.TotalSeconds; - if (grid != null) + if (grid != null && broadphase != null) { - // TODO: Pass in attached entity's grid. - // TODO: Credit OD on the moved to code - // TODO: Call the moved-to code here. - - _visibleTiles.Clear(); var lookups = _entManager.System<EntityLookupSystem>(); var xforms = _entManager.System<SharedTransformSystem>(); - _entManager.System<StationAiVisionSystem>().GetView((gridUid, grid), worldBounds, _visibleTiles); + + if (_accumulator <= 0f) + { + _accumulator = MathF.Max(0f, _accumulator + _updateRate); + _visibleTiles.Clear(); + _entManager.System<StationAiVisionSystem>().GetView((gridUid, broadphase, grid), worldBounds, _visibleTiles); + } var gridMatrix = xforms.GetWorldMatrix(gridUid); var matty = Matrix3x2.Multiply(gridMatrix, invMatrix); diff --git a/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs b/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs new file mode 100644 index 0000000000..bf6b65a969 --- /dev/null +++ b/Content.Client/Silicons/StationAi/StationAiSystem.Airlock.cs @@ -0,0 +1,30 @@ +using Content.Shared.Doors.Components; +using Content.Shared.Silicons.StationAi; +using Robust.Shared.Utility; + +namespace Content.Client.Silicons.StationAi; + +public sealed partial class StationAiSystem +{ + private void InitializeAirlock() + { + SubscribeLocalEvent<DoorBoltComponent, GetStationAiRadialEvent>(OnDoorBoltGetRadial); + } + + private void OnDoorBoltGetRadial(Entity<DoorBoltComponent> ent, ref GetStationAiRadialEvent args) + { + args.Actions.Add(new StationAiRadial() + { + Sprite = ent.Comp.BoltsDown ? + new SpriteSpecifier.Rsi( + new ResPath("/Textures/Structures/Doors/Airlocks/Standard/basic.rsi"), "open") : + new SpriteSpecifier.Rsi( + new ResPath("/Textures/Structures/Doors/Airlocks/Standard/basic.rsi"), "closed"), + Tooltip = ent.Comp.BoltsDown ? Loc.GetString("bolt-open") : Loc.GetString("bolt-close"), + Event = new StationAiBoltEvent() + { + Bolted = !ent.Comp.BoltsDown, + } + }); + } +} diff --git a/Content.Client/Silicons/StationAi/StationAiSystem.Light.cs b/Content.Client/Silicons/StationAi/StationAiSystem.Light.cs new file mode 100644 index 0000000000..cf2f613620 --- /dev/null +++ b/Content.Client/Silicons/StationAi/StationAiSystem.Light.cs @@ -0,0 +1,32 @@ +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Light.Components; +using Content.Shared.Silicons.StationAi; +using Robust.Shared.Utility; + +namespace Content.Client.Silicons.StationAi; + +public sealed partial class StationAiSystem +{ + // Used for surveillance camera lights + + private void InitializePowerToggle() + { + SubscribeLocalEvent<ItemTogglePointLightComponent, GetStationAiRadialEvent>(OnLightGetRadial); + } + + private void OnLightGetRadial(Entity<ItemTogglePointLightComponent> ent, ref GetStationAiRadialEvent args) + { + if (!TryComp(ent.Owner, out ItemToggleComponent? toggle)) + return; + + args.Actions.Add(new StationAiRadial() + { + Tooltip = Loc.GetString("toggle-light"), + Sprite = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/light.svg.192dpi.png")), + Event = new StationAiLightEvent() + { + Enabled = !toggle.Activated + } + }); + } +} diff --git a/Content.Client/Silicons/StationAi/StationAiSystem.cs b/Content.Client/Silicons/StationAi/StationAiSystem.cs index 2ed0617525..ab9ace3c1d 100644 --- a/Content.Client/Silicons/StationAi/StationAiSystem.cs +++ b/Content.Client/Silicons/StationAi/StationAiSystem.cs @@ -5,7 +5,7 @@ namespace Content.Client.Silicons.StationAi; -public sealed partial class StationAiSystem : EntitySystem +public sealed partial class StationAiSystem : SharedStationAiSystem { [Dependency] private readonly IOverlayManager _overlayMgr = default!; [Dependency] private readonly IPlayerManager _player = default!; @@ -15,8 +15,8 @@ public sealed partial class StationAiSystem : EntitySystem public override void Initialize() { base.Initialize(); - // InitializeAirlock(); - // InitializePowerToggle(); + InitializeAirlock(); + InitializePowerToggle(); SubscribeLocalEvent<StationAiOverlayComponent, LocalPlayerAttachedEvent>(OnAiAttached); SubscribeLocalEvent<StationAiOverlayComponent, LocalPlayerDetachedEvent>(OnAiDetached); diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 5f1f49e5fd..2513210e2c 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -70,6 +70,7 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true // Get entities List<EntityUid> entities; + var examineFlags = LookupFlags.All & ~LookupFlags.Sensors; // Do we have to do FoV checks? if ((visibility & MenuVisibility.NoFov) == 0) @@ -85,7 +86,7 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true // Then check every entity entities = new(); - foreach (var ent in _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize)) + foreach (var ent in _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize, flags: examineFlags)) { if (_examine.CanExamine(player.Value, targetPos, Predicate, ent, examiner)) entities.Add(ent); @@ -93,7 +94,7 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true } else { - entities = _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize).ToList(); + entities = _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize, flags: examineFlags).ToList(); } if (entities.Count == 0) diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.cs b/Content.Server/Administration/Systems/AdminVerbSystem.cs index 1e701794ff..d9ccb730d0 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.cs @@ -36,6 +36,7 @@ using System.Linq; using System.Numerics; using Content.Server.Silicons.Laws; +using Content.Shared.Silicons.Laws; using Content.Shared.Silicons.Laws.Components; using Robust.Server.Player; using Robust.Shared.Physics.Components; diff --git a/Content.Server/Communications/CommunicationsConsoleSystem.cs b/Content.Server/Communications/CommunicationsConsoleSystem.cs index c3e5b91254..de77d611c3 100644 --- a/Content.Server/Communications/CommunicationsConsoleSystem.cs +++ b/Content.Server/Communications/CommunicationsConsoleSystem.cs @@ -182,10 +182,6 @@ private static bool CanAnnounce(CommunicationsConsoleComponent comp) private bool CanUse(EntityUid user, EntityUid console) { - // This shouldn't technically be possible because of BUI but don't trust client. - if (!_interaction.InRangeUnobstructed(console, user)) - return false; - if (TryComp<AccessReaderComponent>(console, out var accessReaderComponent) && !HasComp<EmaggedComponent>(console)) { return _accessReaderSystem.IsAllowed(user, console, accessReaderComponent); diff --git a/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs b/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs index f47a5df8ac..6e7bd255c5 100644 --- a/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/DeviceNetworkRequiresPowerSystem.cs @@ -1,7 +1,6 @@ using Content.Server.DeviceNetwork.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; -using Content.Shared.Power.EntitySystems; namespace Content.Server.DeviceNetwork.Systems; diff --git a/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs b/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs index 3c5f7eaecb..5c66d65b57 100644 --- a/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs +++ b/Content.Server/Light/EntitySystems/LitOnPoweredSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Power; -using Content.Shared.Power.Components; namespace Content.Server.Light.EntitySystems { diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index c4a07b56a8..58ffd4f7b9 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -27,7 +27,6 @@ using Content.Shared.Damage.Systems; using Content.Shared.Damage.Components; using Content.Shared.Power; -using Content.Shared.Power.Components; namespace Content.Server.Light.EntitySystems { diff --git a/Content.Server/Mind/MindSystem.cs b/Content.Server/Mind/MindSystem.cs index 4271d76b44..2e7c31ec7a 100644 --- a/Content.Server/Mind/MindSystem.cs +++ b/Content.Server/Mind/MindSystem.cs @@ -341,13 +341,13 @@ public override void SetUserId(EntityUid mindId, NetUserId? userId, MindComponen } } - public void ControlMob(EntityUid user, EntityUid target) + public override void ControlMob(EntityUid user, EntityUid target) { if (TryComp(user, out ActorComponent? actor)) ControlMob(actor.PlayerSession.UserId, target); } - public void ControlMob(NetUserId user, EntityUid target) + public override void ControlMob(NetUserId user, EntityUid target) { var (mindId, mind) = GetOrCreateMind(user); diff --git a/Content.Server/Power/EntitySystems/ChargerSystem.cs b/Content.Server/Power/EntitySystems/ChargerSystem.cs index 2aa69024df..40b998a95d 100644 --- a/Content.Server/Power/EntitySystems/ChargerSystem.cs +++ b/Content.Server/Power/EntitySystems/ChargerSystem.cs @@ -8,7 +8,6 @@ using JetBrains.Annotations; using Robust.Shared.Containers; using System.Diagnostics.CodeAnalysis; -using Content.Shared.Power.Components; using Content.Shared.Storage.Components; using Robust.Server.Containers; using Content.Shared.Whitelist; @@ -44,7 +43,37 @@ private void OnStartup(EntityUid uid, ChargerComponent component, ComponentStart private void OnChargerExamine(EntityUid uid, ChargerComponent component, ExaminedEvent args) { - args.PushMarkup(Loc.GetString("charger-examine", ("color", "yellow"), ("chargeRate", (int) component.ChargeRate))); + using (args.PushGroup(nameof(ChargerComponent))) + { + // rate at which the charger charges + args.PushMarkup(Loc.GetString("charger-examine", ("color", "yellow"), ("chargeRate", (int) component.ChargeRate))); + + // try to get contents of the charger + if (!_container.TryGetContainer(uid, component.SlotId, out var container)) + return; + + if (HasComp<PowerCellSlotComponent>(uid)) + return; + + // if charger is empty and not a power cell type charger, add empty message + // power cells have their own empty message by default, for things like flash lights + if (container.ContainedEntities.Count == 0) + { + args.PushMarkup(Loc.GetString("charger-empty")); + } + else + { + // add how much each item is charged it + foreach (var contained in container.ContainedEntities) + { + if (!TryComp<BatteryComponent>(contained, out var battery)) + continue; + + var chargePercentage = (battery.CurrentCharge / battery.MaxCharge) * 100; + args.PushMarkup(Loc.GetString("charger-content", ("chargePercentage", (int) chargePercentage))); + } + } + } } public override void Update(float frameTime) @@ -202,7 +231,7 @@ private CellChargerStatus GetStatus(EntityUid uid, ChargerComponent component) return CellChargerStatus.Charging; } - + private void TransferPower(EntityUid uid, EntityUid targetEntity, ChargerComponent component, float frameTime) { if (!TryComp(uid, out ApcPowerReceiverComponent? receiverComponent)) diff --git a/Content.Server/Power/EntitySystems/PowerNetSystem.cs b/Content.Server/Power/EntitySystems/PowerNetSystem.cs index 8dcb6240a5..6c35ba2008 100644 --- a/Content.Server/Power/EntitySystems/PowerNetSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerNetSystem.cs @@ -5,7 +5,6 @@ using Content.Server.Power.Pow3r; using Content.Shared.CCVar; using Content.Shared.Power; -using Content.Shared.Power.Components; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Shared.Configuration; diff --git a/Content.Server/Power/Generation/Teg/TegSystem.cs b/Content.Server/Power/Generation/Teg/TegSystem.cs index dd09467efe..43749dab8f 100644 --- a/Content.Server/Power/Generation/Teg/TegSystem.cs +++ b/Content.Server/Power/Generation/Teg/TegSystem.cs @@ -11,7 +11,6 @@ using Content.Shared.DeviceNetwork; using Content.Shared.Examine; using Content.Shared.Power; -using Content.Shared.Power.Components; using Content.Shared.Power.Generation.Teg; using Content.Shared.Rounding; using Robust.Server.GameObjects; diff --git a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs index 5a1bd31a15..e3979a6519 100644 --- a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs +++ b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs @@ -6,7 +6,6 @@ using Content.Server.Power.Components; using Content.Shared.Atmos; using Content.Shared.Power; -using Content.Shared.Power.Components; namespace Content.Server.Power.Generator; diff --git a/Content.Server/Silicons/Laws/SiliconLawSystem.cs b/Content.Server/Silicons/Laws/SiliconLawSystem.cs index 0c0f68c23f..6b7df52a6e 100644 --- a/Content.Server/Silicons/Laws/SiliconLawSystem.cs +++ b/Content.Server/Silicons/Laws/SiliconLawSystem.cs @@ -5,12 +5,10 @@ using Content.Server.Radio.Components; using Content.Server.Roles; using Content.Server.Station.Systems; -using Content.Shared.Actions; using Content.Shared.Administration; using Content.Shared.Chat; using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; -using Content.Shared.Examine; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Roles; @@ -19,10 +17,10 @@ using Content.Shared.Stunnable; using Content.Shared.Wires; using Robust.Server.GameObjects; +using Robust.Shared.Containers; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; -using Robust.Shared.Utility; namespace Content.Server.Silicons.Laws; @@ -32,11 +30,9 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem [Dependency] private readonly IChatManager _chatManager = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly UserInterfaceSystem _userInterface = default!; [Dependency] private readonly SharedStunSystem _stunSystem = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly SharedRoleSystem _roles = default!; /// <inheritdoc/> @@ -44,7 +40,6 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent<SiliconLawBoundComponent, ComponentShutdown>(OnComponentShutdown); SubscribeLocalEvent<SiliconLawBoundComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<SiliconLawBoundComponent, MindAddedMessage>(OnMindAdded); SubscribeLocalEvent<SiliconLawBoundComponent, ToggleLawsScreenEvent>(OnToggleLawsScreen); @@ -58,15 +53,8 @@ public override void Initialize() SubscribeLocalEvent<EmagSiliconLawComponent, MindRemovedMessage>(OnEmagMindRemoved); } - private void OnComponentShutdown(EntityUid uid, SiliconLawBoundComponent component, ComponentShutdown args) - { - if (component.ViewLawsActionEntity != null) - _actions.RemoveAction(uid, component.ViewLawsActionEntity); - } - private void OnMapInit(EntityUid uid, SiliconLawBoundComponent component, MapInitEvent args) { - _actions.AddAction(uid, ref component.ViewLawsActionEntity, component.ViewLawsAction); GetLaws(uid, component); } @@ -92,7 +80,7 @@ private void OnToggleLawsScreen(EntityUid uid, SiliconLawBoundComponent componen private void OnBoundUIOpened(EntityUid uid, SiliconLawBoundComponent component, BoundUIOpenedEvent args) { - _entityManager.TryGetComponent<IntrinsicRadioTransmitterComponent>(uid, out var intrinsicRadio); + TryComp(uid, out IntrinsicRadioTransmitterComponent? intrinsicRadio); var radioChannels = intrinsicRadio?.Channels; var state = new SiliconLawBuiState(GetLaws(uid).Laws, radioChannels); @@ -264,9 +252,9 @@ public void NotifyLawsChanged(EntityUid uid) /// <summary> /// Extract all the laws from a lawset's prototype ids. /// </summary> - public SiliconLawset GetLawset(string lawset) + public SiliconLawset GetLawset(ProtoId<SiliconLawsetPrototype> lawset) { - var proto = _prototype.Index<SiliconLawsetPrototype>(lawset); + var proto = _prototype.Index(lawset); var laws = new SiliconLawset() { Laws = new List<SiliconLaw>(proto.Laws.Count) @@ -294,6 +282,21 @@ public void SetLaws(List<SiliconLaw> newLaws, EntityUid target) component.Lawset.Laws = newLaws; NotifyLawsChanged(target); } + + protected override void OnUpdaterInsert(Entity<SiliconLawUpdaterComponent> ent, ref EntInsertedIntoContainerMessage args) + { + // TODO: Prediction dump this + if (!TryComp(args.Entity, out SiliconLawProviderComponent? provider)) + return; + + var lawset = GetLawset(provider.Laws).Laws; + var query = EntityManager.CompRegistryQueryEnumerator(ent.Comp.Components); + + while (query.MoveNext(out var update)) + { + SetLaws(lawset, update); + } + } } [ToolshedCommand, AdminCommand(AdminFlags.Admin)] diff --git a/Content.Server/Silicons/StationAi/AiInteractWireAction.cs b/Content.Server/Silicons/StationAi/AiInteractWireAction.cs new file mode 100644 index 0000000000..c92c825b32 --- /dev/null +++ b/Content.Server/Silicons/StationAi/AiInteractWireAction.cs @@ -0,0 +1,37 @@ +using Content.Server.Wires; +using Content.Shared.Doors; +using Content.Shared.Silicons.StationAi; +using Content.Shared.Wires; + +namespace Content.Server.Silicons.StationAi; + +/// <summary> +/// Controls whether an AI can interact with the target entity. +/// </summary> +public sealed partial class AiInteractWireAction : ComponentWireAction<StationAiWhitelistComponent> +{ + public override string Name { get; set; } = "wire-name-ai-act-light"; + public override Color Color { get; set; } = Color.DeepSkyBlue; + public override object StatusKey => AirlockWireStatus.AiControlIndicator; + + public override StatusLightState? GetLightState(Wire wire, StationAiWhitelistComponent component) + { + return component.Enabled ? StatusLightState.On : StatusLightState.Off; + } + + public override bool Cut(EntityUid user, Wire wire, StationAiWhitelistComponent component) + { + return EntityManager.System<SharedStationAiSystem>() + .SetWhitelistEnabled((component.Owner, component), false, announce: true); + } + + public override bool Mend(EntityUid user, Wire wire, StationAiWhitelistComponent component) + { + return EntityManager.System<SharedStationAiSystem>() + .SetWhitelistEnabled((component.Owner, component), true); + } + + public override void Pulse(EntityUid user, Wire wire, StationAiWhitelistComponent component) + { + } +} diff --git a/Content.Server/Silicons/StationAi/AiVisionWireAction.cs b/Content.Server/Silicons/StationAi/AiVisionWireAction.cs new file mode 100644 index 0000000000..3523f4d38f --- /dev/null +++ b/Content.Server/Silicons/StationAi/AiVisionWireAction.cs @@ -0,0 +1,40 @@ +using Content.Server.Wires; +using Content.Shared.Doors; +using Content.Shared.Silicons.StationAi; +using Content.Shared.StationAi; +using Content.Shared.Wires; + +namespace Content.Server.Silicons.StationAi; + +/// <summary> +/// Handles StationAiVision functionality for the attached entity. +/// </summary> +public sealed partial class AiVisionWireAction : ComponentWireAction<StationAiVisionComponent> +{ + public override string Name { get; set; } = "wire-name-ai-vision-light"; + public override Color Color { get; set; } = Color.DeepSkyBlue; + public override object StatusKey => AirlockWireStatus.AiControlIndicator; + + public override StatusLightState? GetLightState(Wire wire, StationAiVisionComponent component) + { + return component.Enabled ? StatusLightState.On : StatusLightState.Off; + } + + public override bool Cut(EntityUid user, Wire wire, StationAiVisionComponent component) + { + return EntityManager.System<SharedStationAiSystem>() + .SetVisionEnabled((component.Owner, component), false, announce: true); + } + + public override bool Mend(EntityUid user, Wire wire, StationAiVisionComponent component) + { + return EntityManager.System<SharedStationAiSystem>() + .SetVisionEnabled((component.Owner, component), true); + } + + public override void Pulse(EntityUid user, Wire wire, StationAiVisionComponent component) + { + // TODO: This should turn it off for a bit + // Need timer cleanup first out of scope. + } +} diff --git a/Content.Server/Silicons/StationAi/StationAiSystem.cs b/Content.Server/Silicons/StationAi/StationAiSystem.cs new file mode 100644 index 0000000000..846497387d --- /dev/null +++ b/Content.Server/Silicons/StationAi/StationAiSystem.cs @@ -0,0 +1,76 @@ +using System.Linq; +using Content.Server.Chat.Managers; +using Content.Server.Chat.Systems; +using Content.Shared.Chat; +using Content.Shared.Silicons.StationAi; +using Content.Shared.StationAi; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Map.Components; +using Robust.Shared.Player; + +namespace Content.Server.Silicons.StationAi; + +public sealed class StationAiSystem : SharedStationAiSystem +{ + [Dependency] private readonly IChatManager _chats = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + + private readonly HashSet<Entity<StationAiCoreComponent>> _ais = new(); + + public override bool SetVisionEnabled(Entity<StationAiVisionComponent> entity, bool enabled, bool announce = false) + { + if (!base.SetVisionEnabled(entity, enabled, announce)) + return false; + + if (announce) + { + AnnounceSnip(entity.Owner); + } + + return true; + } + + public override bool SetWhitelistEnabled(Entity<StationAiWhitelistComponent> entity, bool enabled, bool announce = false) + { + if (!base.SetWhitelistEnabled(entity, enabled, announce)) + return false; + + if (announce) + { + AnnounceSnip(entity.Owner); + } + + return true; + } + + private void AnnounceSnip(EntityUid entity) + { + var xform = Transform(entity); + + if (!TryComp(xform.GridUid, out MapGridComponent? grid)) + return; + + _ais.Clear(); + _lookup.GetChildEntities(xform.GridUid.Value, _ais); + var filter = Filter.Empty(); + + foreach (var ai in _ais) + { + // TODO: Filter API? + if (TryComp(ai.Owner, out ActorComponent? actorComp)) + { + filter.AddPlayer(actorComp.PlayerSession); + } + } + + // TEST + // filter = Filter.Broadcast(); + + // No easy way to do chat notif embeds atm. + var tile = Maps.LocalToTile(xform.GridUid.Value, grid, xform.Coordinates); + var msg = Loc.GetString("ai-wire-snipped", ("coords", tile)); + + _chats.ChatMessageToMany(ChatChannel.Notifications, msg, msg, entity, false, true, filter.Recipients.Select(o => o.Channel)); + // Apparently there's no sound for this. + } +} diff --git a/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs b/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs index d2c2a8a1ca..1ad1bb6c0a 100644 --- a/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs +++ b/Content.Server/Sound/SpamEmitSoundRequirePowerSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Power; -using Content.Shared.Power.Components; using Content.Shared.Sound; using Content.Shared.Sound.Components; diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 79192d49b4..0662c54bee 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -139,6 +139,22 @@ public EntityUid SpawnPlayerMob( EntityUid? entity = null) { _prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out var prototype); + RoleLoadout? loadout = null; + + // Need to get the loadout up-front to handle names if we use an entity spawn override. + var jobLoadout = LoadoutSystem.GetJobPrototype(prototype?.ID); + + if (_prototypeManager.TryIndex(jobLoadout, out RoleLoadoutPrototype? roleProto)) + { + profile?.Loadouts.TryGetValue(jobLoadout, out loadout); + + // Set to default if not present + if (loadout == null) + { + loadout = new RoleLoadout(jobLoadout); + loadout.SetDefault(profile, _actors.GetSession(entity), _prototypeManager); + } + } // If we're not spawning a humanoid, we're gonna exit early without doing all the humanoid stuff. if (prototype?.JobEntity != null) @@ -146,6 +162,13 @@ public EntityUid SpawnPlayerMob( DebugTools.Assert(entity is null); var jobEntity = EntityManager.SpawnEntity(prototype.JobEntity, coordinates); MakeSentientCommand.MakeSentient(jobEntity, EntityManager); + + // Make sure custom names get handled, what is gameticker control flow whoopy. + if (loadout != null) + { + EquipRoleName(jobEntity, loadout, roleProto!); + } + DoJobSpecials(job, jobEntity); _identity.QueueIdentityUpdate(jobEntity); return jobEntity; diff --git a/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs b/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs index 5970e16319..c9be87c623 100644 --- a/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs +++ b/Content.Server/Weapons/Melee/EnergySword/EnergySwordSystem.cs @@ -38,7 +38,7 @@ private void OnInteractUsing(EntityUid uid, EnergySwordComponent comp, InteractU if (args.Handled) return; - if (!_toolSystem.HasQuality(args.Used, "Pulsing")) + if (!_toolSystem.HasQuality(args.Used, SharedToolSystem.PulseQuality)) return; args.Handled = true; diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs index 019e09bbbb..9d2fd58980 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactElectricityTriggerSystem.cs @@ -44,7 +44,7 @@ private void OnInteractUsing(EntityUid uid, ArtifactElectricityTriggerComponent if (args.Handled) return; - if (!_toolSystem.HasQuality(args.Used, "Pulsing")) + if (!_toolSystem.HasQuality(args.Used, SharedToolSystem.PulseQuality)) return; args.Handled = _artifactSystem.TryActivateArtifact(uid, args.User); diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index d2883b5ef5..bd6ee8cae1 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Emoting; using Content.Shared.Hands; using Content.Shared.Interaction; +using Content.Shared.Interaction.Components; using Content.Shared.Interaction.Events; using Content.Shared.Item; using Content.Shared.Mobs; @@ -26,9 +27,14 @@ public sealed class ActionBlockerSystem : EntitySystem { [Dependency] private readonly SharedContainerSystem _container = default!; + private EntityQuery<ComplexInteractionComponent> _complexInteractionQuery; + public override void Initialize() { base.Initialize(); + + _complexInteractionQuery = GetEntityQuery<ComplexInteractionComponent>(); + SubscribeLocalEvent<InputMoverComponent, ComponentStartup>(OnMoverStartup); } @@ -57,6 +63,15 @@ public bool UpdateCanMove(EntityUid uid, InputMoverComponent? component = null) return !ev.Cancelled; } + /// <summary> + /// Checks if a given entity is able to do specific complex interactions. + /// This is used to gate manipulation to general humanoids. If a mouse shouldn't be able to do something, then it's complex. + /// </summary> + public bool CanComplexInteract(EntityUid user) + { + return _complexInteractionQuery.HasComp(user); + } + /// <summary> /// Raises an event directed at both the user and the target entity to check whether a user is capable of /// interacting with this entity. diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index 722d97213f..42fa89b367 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -373,6 +373,26 @@ private void OnClimbEndCollide(EntityUid uid, ClimbingComponent component, ref E return; } + foreach (var contact in args.OurFixture.Contacts.Values) + { + if (!contact.IsTouching) + continue; + + var otherEnt = contact.OtherEnt(uid); + var (otherFixtureId, otherFixture) = contact.OtherFixture(uid); + + // TODO: Remove this on engine. + if (args.OtherEntity == otherEnt && args.OtherFixtureId == otherFixtureId) + continue; + + if (otherFixture is { Hard: true } && + _climbableQuery.HasComp(otherEnt)) + { + return; + } + } + + // TODO: Is this even needed anymore? foreach (var otherFixture in args.OurFixture.Contacts.Keys) { // If it's the other fixture then ignore em diff --git a/Content.Shared/Configurable/ConfigurationComponent.cs b/Content.Shared/Configurable/ConfigurationComponent.cs index 63a0dfe95a..621871af3c 100644 --- a/Content.Shared/Configurable/ConfigurationComponent.cs +++ b/Content.Shared/Configurable/ConfigurationComponent.cs @@ -1,5 +1,6 @@ using System.Text.RegularExpressions; using Content.Shared.Tools; +using Content.Shared.Tools.Systems; using Robust.Shared.GameStates; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -13,7 +14,7 @@ public sealed partial class ConfigurationComponent : Component public Dictionary<string, string?> Config = new(); [DataField("qualityNeeded", customTypeSerializer: typeof(PrototypeIdSerializer<ToolQualityPrototype>))] - public string QualityNeeded = "Pulsing"; + public string QualityNeeded = SharedToolSystem.PulseQuality; [DataField("validation")] public Regex Validation = new("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled); diff --git a/Content.Shared/Doors/AirlockWireStatus.cs b/Content.Shared/Doors/AirlockWireStatus.cs index a50ee2c88e..d3fa15ed1b 100644 --- a/Content.Shared/Doors/AirlockWireStatus.cs +++ b/Content.Shared/Doors/AirlockWireStatus.cs @@ -8,7 +8,7 @@ public enum AirlockWireStatus PowerIndicator, BoltIndicator, BoltLightIndicator, - AIControlIndicator, + AiControlIndicator, TimingIndicator, SafetyIndicator, } diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index 397a8f7448..a1c30a2bd0 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -109,12 +109,25 @@ public virtual bool CanExamine(EntityUid examiner, MapCoordinates target, Ignore if (EntityManager.GetComponent<TransformComponent>(examiner).MapID != target.MapId) return false; - return InRangeUnOccluded( - _transform.GetMapCoordinates(examiner), - target, - GetExaminerRange(examiner), - predicate: predicate, - ignoreInsideBlocker: true); + // Do target InRangeUnoccluded which has different checks. + if (examined != null) + { + return InRangeUnOccluded( + examiner, + examined.Value, + GetExaminerRange(examiner), + predicate: predicate, + ignoreInsideBlocker: true); + } + else + { + return InRangeUnOccluded( + examiner, + target, + GetExaminerRange(examiner), + predicate: predicate, + ignoreInsideBlocker: true); + } } /// <summary> @@ -209,7 +222,14 @@ public bool InRangeUnOccluded<TState>(MapCoordinates origin, MapCoordinates othe public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { - var entMan = IoCManager.Resolve<IEntityManager>(); + var ev = new InRangeOverrideEvent(origin, other); + RaiseLocalEvent(origin, ref ev); + + if (ev.Handled) + { + return ev.InRange; + } + var originPos = _transform.GetMapCoordinates(origin); var otherPos = _transform.GetMapCoordinates(other); diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 24bb1b2446..a2118205af 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -20,6 +20,7 @@ using Content.Shared.Physics; using Content.Shared.Players.RateLimiting; using Content.Shared.Popups; +using Content.Shared.Silicons.StationAi; using Content.Shared.Storage; using Content.Shared.Strip; using Content.Shared.Tag; @@ -83,7 +84,6 @@ public abstract partial class SharedInteractionSystem : EntitySystem private EntityQuery<WallMountComponent> _wallMountQuery; private EntityQuery<UseDelayComponent> _delayQuery; private EntityQuery<ActivatableUIComponent> _uiQuery; - private EntityQuery<ComplexInteractionComponent> _complexInteractionQuery; private const CollisionGroup InRangeUnobstructedMask = CollisionGroup.Impassable | CollisionGroup.InteractImpassable; @@ -106,7 +106,6 @@ public override void Initialize() _wallMountQuery = GetEntityQuery<WallMountComponent>(); _delayQuery = GetEntityQuery<UseDelayComponent>(); _uiQuery = GetEntityQuery<ActivatableUIComponent>(); - _complexInteractionQuery = GetEntityQuery<ComplexInteractionComponent>(); SubscribeLocalEvent<BoundUserInterfaceCheckRangeEvent>(HandleUserInterfaceRangeCheck); SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnBoundInterfaceInteractAttempt); @@ -191,7 +190,7 @@ private void OnBoundInterfaceInteractAttempt(BoundUserInterfaceMessageAttempt ev return; } - if (uiComp.RequireHands && !_handsQuery.HasComp(ev.Actor)) + if (uiComp.RequiresComplex && !_actionBlockerSystem.CanComplexInteract(ev.Actor)) ev.Cancel(); } @@ -477,10 +476,7 @@ private bool IsDeleted(EntityUid? uid) public void InteractHand(EntityUid user, EntityUid target) { - if (IsDeleted(user) || IsDeleted(target)) - return; - - var complexInteractions = SupportsComplexInteractions(user); + var complexInteractions = _actionBlockerSystem.CanComplexInteract(user); if (!complexInteractions) { InteractionActivate(user, @@ -666,6 +662,14 @@ public bool InRangeUnobstructed( if (!Resolve(other, ref other.Comp)) return false; + var ev = new InRangeOverrideEvent(origin, other); + RaiseLocalEvent(origin, ref ev); + + if (ev.Handled) + { + return ev.InRange; + } + return InRangeUnobstructed(origin, other, other.Comp.Coordinates, @@ -1188,7 +1192,7 @@ public bool AltInteract(EntityUid user, EntityUid target) // Get list of alt-interact verbs var verbs = _verbSystem.GetLocalVerbs(target, user, typeof(AlternativeVerb)); - if (!verbs.Any()) + if (verbs.Count == 0) return false; _verbSystem.ExecuteVerb(verbs.First(), user, target); @@ -1245,6 +1249,13 @@ public bool InRangeAndAccessible( /// </summary> public bool IsAccessible(Entity<TransformComponent?> user, Entity<TransformComponent?> target) { + var ev = new AccessibleOverrideEvent(user, target); + + RaiseLocalEvent(user, ref ev); + + if (ev.Handled) + return ev.Accessible; + if (_containerSystem.IsInSameOrParentContainer(user, target, out _, out var container)) return true; @@ -1396,13 +1407,10 @@ public bool TryGetUsedEntity(EntityUid user, [NotNullWhen(true)] out EntityUid? return ev.Handled; } - /// <summary> - /// Checks if a given entity is able to do specific complex interactions. - /// This is used to gate manipulation to general humanoids. If a mouse shouldn't be able to do something, then it's complex. - /// </summary> + [Obsolete("Use ActionBlockerSystem")] public bool SupportsComplexInteractions(EntityUid user) { - return _complexInteractionQuery.HasComp(user); + return _actionBlockerSystem.CanComplexInteract(user); } } @@ -1441,17 +1449,38 @@ public record struct GetUsedEntityEvent() }; /// <summary> - /// Raised directed by-ref on an item and a user to determine if interactions can occur. + /// Raised directed by-ref on an item to determine if hand interactions should go through. + /// Defaults to allowing hand interactions to go through. Cancel to force the item to be attacked instead. /// </summary> /// <param name="Cancelled">Whether the hand interaction should be cancelled.</param> [ByRefEvent] - public record struct AttemptUseInteractEvent(EntityUid User, EntityUid Used, bool Cancelled = false); + public record struct CombatModeShouldHandInteractEvent(bool Cancelled = false); /// <summary> - /// Raised directed by-ref on an item to determine if hand interactions should go through. - /// Defaults to allowing hand interactions to go through. Cancel to force the item to be attacked instead. + /// Override event raised directed on the user to say the target is accessible. /// </summary> - /// <param name="Cancelled">Whether the hand interaction should be cancelled.</param> + /// <param name="User"></param> + /// <param name="Target"></param> [ByRefEvent] - public record struct CombatModeShouldHandInteractEvent(bool Cancelled = false); + public record struct AccessibleOverrideEvent(EntityUid User, EntityUid Target) + { + public readonly EntityUid User = User; + public readonly EntityUid Target = Target; + + public bool Handled; + public bool Accessible = false; + } + + /// <summary> + /// Override event raised directed on a user to check InRangeUnoccluded AND InRangeUnobstructed to the target if you require custom logic. + /// </summary> + [ByRefEvent] + public record struct InRangeOverrideEvent(EntityUid User, EntityUid Target) + { + public readonly EntityUid User = User; + public readonly EntityUid Target = Target; + + public bool Handled; + public bool InRange = false; + } } diff --git a/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs b/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs new file mode 100644 index 0000000000..39be05a148 --- /dev/null +++ b/Content.Shared/Light/Components/LightOnCollideColliderComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Light.Components; + +/// <summary> +/// Can activate <see cref="LightOnCollideComponent"/> when collided with. +/// </summary> +[RegisterComponent, NetworkedComponent] +public sealed partial class LightOnCollideColliderComponent : Component +{ + [DataField] + public string FixtureId = "lightTrigger"; +} diff --git a/Content.Shared/Light/Components/LightOnCollideComponent.cs b/Content.Shared/Light/Components/LightOnCollideComponent.cs new file mode 100644 index 0000000000..c3b4bd7396 --- /dev/null +++ b/Content.Shared/Light/Components/LightOnCollideComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Light.Components; + +/// <summary> +/// Enables / disables pointlight whenever entities are contacting with it +/// </summary> +[RegisterComponent, NetworkedComponent] +public sealed partial class LightOnCollideComponent : Component +{ +} diff --git a/Content.Shared/Light/EntitySystems/LightCollideSystem.cs b/Content.Shared/Light/EntitySystems/LightCollideSystem.cs new file mode 100644 index 0000000000..f09ae6824e --- /dev/null +++ b/Content.Shared/Light/EntitySystems/LightCollideSystem.cs @@ -0,0 +1,82 @@ +using Content.Shared.Light.Components; +using Robust.Shared.Physics.Events; +using Robust.Shared.Physics.Systems; + +namespace Content.Shared.Light.EntitySystems; + +public sealed class LightCollideSystem : EntitySystem +{ + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SlimPoweredLightSystem _lights = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent<LightOnCollideColliderComponent, PreventCollideEvent>(OnPreventCollide); + SubscribeLocalEvent<LightOnCollideColliderComponent, StartCollideEvent>(OnStart); + SubscribeLocalEvent<LightOnCollideColliderComponent, EndCollideEvent>(OnEnd); + + SubscribeLocalEvent<LightOnCollideColliderComponent, ComponentShutdown>(OnCollideShutdown); + } + + private void OnCollideShutdown(Entity<LightOnCollideColliderComponent> ent, ref ComponentShutdown args) + { + // TODO: Check this on the event. + if (TerminatingOrDeleted(ent.Owner)) + return; + + // Regenerate contacts for everything we were colliding with. + var contacts = _physics.GetContacts(ent.Owner); + + while (contacts.MoveNext(out var contact)) + { + if (!contact.IsTouching) + continue; + + var other = contact.OtherEnt(ent.Owner); + + if (HasComp<LightOnCollideComponent>(other)) + { + _physics.RegenerateContacts(other); + } + } + } + + // You may be wondering what de fok this is doing here. + // At the moment there's no easy way to do collision whitelists based on components. + private void OnPreventCollide(Entity<LightOnCollideColliderComponent> ent, ref PreventCollideEvent args) + { + if (!HasComp<LightOnCollideComponent>(args.OtherEntity)) + { + args.Cancelled = true; + } + } + + private void OnEnd(Entity<LightOnCollideColliderComponent> ent, ref EndCollideEvent args) + { + if (args.OurFixtureId != ent.Comp.FixtureId) + return; + + if (!HasComp<LightOnCollideComponent>(args.OtherEntity)) + return; + + // TODO: Engine bug IsTouching box2d yay. + var contacts = _physics.GetTouchingContacts(args.OtherEntity) - 1; + + if (contacts > 0) + return; + + _lights.SetEnabled(args.OtherEntity, false); + } + + private void OnStart(Entity<LightOnCollideColliderComponent> ent, ref StartCollideEvent args) + { + if (args.OurFixtureId != ent.Comp.FixtureId) + return; + + if (!HasComp<LightOnCollideComponent>(args.OtherEntity)) + return; + + _lights.SetEnabled(args.OtherEntity, true); + } +} diff --git a/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs b/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs index 6d984ed19a..4cf9b25dad 100644 --- a/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs +++ b/Content.Shared/Light/EntitySystems/SlimPoweredLightSystem.cs @@ -1,6 +1,5 @@ using Content.Shared.Light.Components; using Content.Shared.Power; -using Content.Shared.Power.Components; using Content.Shared.Power.EntitySystems; namespace Content.Shared.Light.EntitySystems; diff --git a/Content.Shared/Mind/SharedMindSystem.cs b/Content.Shared/Mind/SharedMindSystem.cs index 2b83f05190..994d230e8b 100644 --- a/Content.Shared/Mind/SharedMindSystem.cs +++ b/Content.Shared/Mind/SharedMindSystem.cs @@ -315,6 +315,10 @@ public virtual void TransferTo(EntityUid mindId, EntityUid? entity, bool ghostCh { } + public virtual void ControlMob(EntityUid user, EntityUid target) {} + + public virtual void ControlMob(NetUserId user, EntityUid target) {} + /// <summary> /// Tries to create and add an objective from its prototype id. /// </summary> diff --git a/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs b/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs index 824d057b3e..0fb9c5920f 100644 --- a/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs +++ b/Content.Shared/Silicons/Laws/Components/SiliconLawBoundComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Actions; +using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; @@ -7,21 +8,9 @@ namespace Content.Shared.Silicons.Laws.Components; /// <summary> /// This is used for entities which are bound to silicon laws and can view them. /// </summary> -[RegisterComponent, Access(typeof(SharedSiliconLawSystem))] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedSiliconLawSystem))] public sealed partial class SiliconLawBoundComponent : Component { - /// <summary> - /// The sidebar action that toggles the laws screen. - /// </summary> - [DataField] - public EntProtoId ViewLawsAction = "ActionViewLaws"; - - /// <summary> - /// The action for toggling laws. Stored here so we can remove it later. - /// </summary> - [DataField] - public EntityUid? ViewLawsActionEntity; - /// <summary> /// The last entity that provided laws to this entity. /// </summary> diff --git a/Content.Shared/Silicons/Laws/Components/SiliconLawUpdaterComponent.cs b/Content.Shared/Silicons/Laws/Components/SiliconLawUpdaterComponent.cs new file mode 100644 index 0000000000..e28bf883d9 --- /dev/null +++ b/Content.Shared/Silicons/Laws/Components/SiliconLawUpdaterComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Silicons.Laws.Components; + +/// <summary> +/// Whenever an entity is inserted with silicon laws it will update the relevant entity's laws. +/// </summary> +[RegisterComponent, NetworkedComponent] +public sealed partial class SiliconLawUpdaterComponent : Component +{ + /// <summary> + /// Entities to update + /// </summary> + [DataField(required: true)] + public ComponentRegistry Components; +} diff --git a/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.Updater.cs b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.Updater.cs new file mode 100644 index 0000000000..9fbef58a84 --- /dev/null +++ b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.Updater.cs @@ -0,0 +1,17 @@ +using Content.Shared.Silicons.Laws.Components; +using Robust.Shared.Containers; + +namespace Content.Shared.Silicons.Laws; + +public abstract partial class SharedSiliconLawSystem +{ + private void InitializeUpdater() + { + SubscribeLocalEvent<SiliconLawUpdaterComponent, EntInsertedIntoContainerMessage>(OnUpdaterInsert); + } + + protected virtual void OnUpdaterInsert(Entity<SiliconLawUpdaterComponent> ent, ref EntInsertedIntoContainerMessage args) + { + // TODO: Prediction + } +} diff --git a/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs index c0619e6e43..a30e7c8980 100644 --- a/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs +++ b/Content.Shared/Silicons/Laws/SharedSiliconLawSystem.cs @@ -8,13 +8,14 @@ namespace Content.Shared.Silicons.Laws; /// <summary> /// This handles getting and displaying the laws for silicons. /// </summary> -public abstract class SharedSiliconLawSystem : EntitySystem +public abstract partial class SharedSiliconLawSystem : EntitySystem { [Dependency] private readonly SharedPopupSystem _popup = default!; /// <inheritdoc/> public override void Initialize() { + InitializeUpdater(); SubscribeLocalEvent<EmagSiliconLawComponent, GotEmaggedEvent>(OnGotEmagged); SubscribeLocalEvent<EmagSiliconLawComponent, OnAttemptEmagEvent>(OnAttemptEmag); } diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs new file mode 100644 index 0000000000..ff6fc1ece0 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs @@ -0,0 +1,25 @@ +using Content.Shared.Doors.Components; +using Robust.Shared.Serialization; + +namespace Content.Shared.Silicons.StationAi; + +public abstract partial class SharedStationAiSystem +{ + // Handles airlock radial + + private void InitializeAirlock() + { + SubscribeLocalEvent<DoorBoltComponent, StationAiBoltEvent>(OnAirlockBolt); + } + + private void OnAirlockBolt(EntityUid ent, DoorBoltComponent component, StationAiBoltEvent args) + { + _doors.SetBoltsDown((ent, component), args.Bolted, args.User, predicted: true); + } +} + +[Serializable, NetSerializable] +public sealed class StationAiBoltEvent : BaseStationAiAction +{ + public bool Bolted; +} diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs new file mode 100644 index 0000000000..a6c57f5940 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Held.cs @@ -0,0 +1,187 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Actions.Events; +using Content.Shared.Interaction.Events; +using Content.Shared.Verbs; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Silicons.StationAi; + +public abstract partial class SharedStationAiSystem +{ + /* + * Added when an entity is inserted into a StationAiCore. + */ + + private void InitializeHeld() + { + SubscribeLocalEvent<StationAiRadialMessage>(OnRadialMessage); + SubscribeLocalEvent<BoundUserInterfaceMessageAttempt>(OnMessageAttempt); + SubscribeLocalEvent<StationAiWhitelistComponent, GetVerbsEvent<AlternativeVerb>>(OnTargetVerbs); + + SubscribeLocalEvent<StationAiHeldComponent, InteractionAttemptEvent>(OnHeldInteraction); + SubscribeLocalEvent<StationAiHeldComponent, AttemptRelayActionComponentChangeEvent>(OnHeldRelay); + SubscribeLocalEvent<StationAiHeldComponent, JumpToCoreEvent>(OnCoreJump); + } + + private void OnCoreJump(Entity<StationAiHeldComponent> ent, ref JumpToCoreEvent args) + { + if (!TryGetCore(ent.Owner, out var core) || core.Comp?.RemoteEntity == null) + return; + + _xforms.DropNextTo(core.Comp.RemoteEntity.Value, core.Owner) ; + } + + /// <summary> + /// Tries to get the entity held in the AI core. + /// </summary> + private bool TryGetHeld(Entity<StationAiCoreComponent?> entity, out EntityUid held) + { + held = EntityUid.Invalid; + + if (!Resolve(entity.Owner, ref entity.Comp)) + return false; + + if (!_containers.TryGetContainer(entity.Owner, StationAiCoreComponent.Container, out var container) || + container.ContainedEntities.Count == 0) + return false; + + held = container.ContainedEntities[0]; + return true; + } + + private bool TryGetCore(EntityUid ent, out Entity<StationAiCoreComponent?> core) + { + if (!_containers.TryGetContainingContainer(ent, out var container) || + container.ID != StationAiCoreComponent.Container || + !TryComp(container.Owner, out StationAiCoreComponent? coreComp) || + coreComp.RemoteEntity == null) + { + core = (EntityUid.Invalid, null); + return false; + } + + core = (container.Owner, coreComp); + return true; + } + + private void OnHeldRelay(Entity<StationAiHeldComponent> ent, ref AttemptRelayActionComponentChangeEvent args) + { + if (!TryGetCore(ent.Owner, out var core)) + return; + + args.Target = core.Comp?.RemoteEntity; + } + + private void OnRadialMessage(StationAiRadialMessage ev) + { + if (!TryGetEntity(ev.Entity, out var target)) + return; + + ev.Event.User = ev.Actor; + RaiseLocalEvent(target.Value, (object) ev.Event); + } + + private void OnMessageAttempt(BoundUserInterfaceMessageAttempt ev) + { + if (ev.Actor == ev.Target) + return; + + if (TryComp(ev.Actor, out StationAiHeldComponent? aiComp) && + (!ValidateAi((ev.Actor, aiComp)) || + !HasComp<StationAiWhitelistComponent>(ev.Target))) + { + ev.Cancel(); + } + } + + private void OnHeldInteraction(Entity<StationAiHeldComponent> ent, ref InteractionAttemptEvent args) + { + // Cancel if it's not us or something with a whitelist. + args.Cancelled = ent.Owner != args.Target && + args.Target != null && + (!TryComp(args.Target, out StationAiWhitelistComponent? whitelist) || !whitelist.Enabled); + } + + private void OnTargetVerbs(Entity<StationAiWhitelistComponent> ent, ref GetVerbsEvent<AlternativeVerb> args) + { + if (!args.CanComplexInteract || + !ent.Comp.Enabled || + !HasComp<StationAiHeldComponent>(args.User) || + !HasComp<StationAiWhitelistComponent>(args.Target)) + { + return; + } + + var user = args.User; + var target = args.Target; + + var isOpen = _uiSystem.IsUiOpen(target, AiUi.Key, user); + + args.Verbs.Add(new AlternativeVerb() + { + Text = isOpen ? Loc.GetString("ai-close") : Loc.GetString("ai-open"), + Act = () => + { + if (isOpen) + { + _uiSystem.CloseUi(ent.Owner, AiUi.Key, user); + } + else + { + _uiSystem.OpenUi(ent.Owner, AiUi.Key, user); + } + } + }); + } +} + +/// <summary> +/// Raised from client to server as a BUI message wrapping the event to perform. +/// Also handles AI action validation. +/// </summary> +[Serializable, NetSerializable] +public sealed class StationAiRadialMessage : BoundUserInterfaceMessage +{ + public BaseStationAiAction Event = default!; +} + +// Do nothing on server just here for shared move along. +/// <summary> +/// Raised on client to get the relevant data for radial actions. +/// </summary> +public sealed class StationAiRadial : BaseStationAiAction +{ + public SpriteSpecifier? Sprite; + + public string? Tooltip; + + public BaseStationAiAction Event = default!; +} + +/// <summary> +/// Abstract parent for radial actions events. +/// When a client requests a radial action this will get sent. +/// </summary> +[Serializable, NetSerializable] +public abstract class BaseStationAiAction +{ + [field:NonSerialized] + public EntityUid User { get; set; } +} + +// No idea if there's a better way to do this. +/// <summary> +/// Grab actions possible for an AI on the target entity. +/// </summary> +[ByRefEvent] +public record struct GetStationAiRadialEvent() +{ + public List<StationAiRadial> Actions = new(); +} + +[Serializable, NetSerializable] +public enum AiUi : byte +{ + Key, +} diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Light.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Light.cs new file mode 100644 index 0000000000..bc2e3665f5 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.Light.cs @@ -0,0 +1,28 @@ +using Content.Shared.Light.Components; +using Robust.Shared.Serialization; + +namespace Content.Shared.Silicons.StationAi; + +public abstract partial class SharedStationAiSystem +{ + // Handles light toggling. + + private void InitializeLight() + { + SubscribeLocalEvent<ItemTogglePointLightComponent, StationAiLightEvent>(OnLight); + } + + private void OnLight(EntityUid ent, ItemTogglePointLightComponent component, StationAiLightEvent args) + { + if (args.Enabled) + _toggles.TryActivate(ent, user: args.User); + else + _toggles.TryDeactivate(ent, user: args.User); + } +} + +[Serializable, NetSerializable] +public sealed class StationAiLightEvent : BaseStationAiAction +{ + public bool Enabled; +} diff --git a/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs new file mode 100644 index 0000000000..348b0b0465 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/SharedStationAiSystem.cs @@ -0,0 +1,412 @@ +using Content.Shared.ActionBlocker; +using Content.Shared.Actions; +using Content.Shared.Administration.Managers; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Database; +using Content.Shared.Doors.Systems; +using Content.Shared.Interaction; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Mind; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.Power; +using Content.Shared.StationAi; +using Content.Shared.Verbs; +using Robust.Shared.Containers; +using Robust.Shared.Map.Components; +using Robust.Shared.Network; +using Robust.Shared.Physics; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Robust.Shared.Timing; + +namespace Content.Shared.Silicons.StationAi; + +public abstract partial class SharedStationAiSystem : EntitySystem +{ + [Dependency] private readonly ISharedAdminManager _admin = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly ItemSlotsSystem _slots = default!; + [Dependency] private readonly ItemToggleSystem _toggles = default!; + [Dependency] private readonly ActionBlockerSystem _blocker = default!; + [Dependency] private readonly MetaDataSystem _metadata = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedContainerSystem _containers = default!; + [Dependency] private readonly SharedDoorSystem _doors = default!; + [Dependency] private readonly SharedEyeSystem _eye = default!; + [Dependency] protected readonly SharedMapSystem Maps = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedMoverController _mover = default!; + [Dependency] private readonly SharedTransformSystem _xforms = default!; + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly StationAiVisionSystem _vision = default!; + + // StationAiHeld is added to anything inside of an AI core. + // StationAiHolder indicates it can hold an AI positronic brain (e.g. holocard / core). + // StationAiCore holds functionality related to the core itself. + // StationAiWhitelist is a general whitelist to stop it being able to interact with anything + // StationAiOverlay handles the static overlay. It also handles interaction blocking on client and server + // for anything under it. + + private EntityQuery<BroadphaseComponent> _broadphaseQuery; + private EntityQuery<MapGridComponent> _gridQuery; + + [ValidatePrototypeId<EntityPrototype>] + private static readonly EntProtoId DefaultAi = "StationAiBrain"; + + public override void Initialize() + { + base.Initialize(); + + _broadphaseQuery = GetEntityQuery<BroadphaseComponent>(); + _gridQuery = GetEntityQuery<MapGridComponent>(); + + InitializeAirlock(); + InitializeHeld(); + InitializeLight(); + + SubscribeLocalEvent<StationAiWhitelistComponent, BoundUserInterfaceCheckRangeEvent>(OnAiBuiCheck); + + SubscribeLocalEvent<StationAiOverlayComponent, AccessibleOverrideEvent>(OnAiAccessible); + SubscribeLocalEvent<StationAiOverlayComponent, InRangeOverrideEvent>(OnAiInRange); + SubscribeLocalEvent<StationAiOverlayComponent, MenuVisibilityEvent>(OnAiMenu); + + SubscribeLocalEvent<StationAiHolderComponent, ComponentInit>(OnHolderInit); + SubscribeLocalEvent<StationAiHolderComponent, ComponentRemove>(OnHolderRemove); + SubscribeLocalEvent<StationAiHolderComponent, AfterInteractEvent>(OnHolderInteract); + SubscribeLocalEvent<StationAiHolderComponent, MapInitEvent>(OnHolderMapInit); + SubscribeLocalEvent<StationAiHolderComponent, EntInsertedIntoContainerMessage>(OnHolderConInsert); + SubscribeLocalEvent<StationAiHolderComponent, EntRemovedFromContainerMessage>(OnHolderConRemove); + + SubscribeLocalEvent<StationAiCoreComponent, EntInsertedIntoContainerMessage>(OnAiInsert); + SubscribeLocalEvent<StationAiCoreComponent, EntRemovedFromContainerMessage>(OnAiRemove); + SubscribeLocalEvent<StationAiCoreComponent, MapInitEvent>(OnAiMapInit); + SubscribeLocalEvent<StationAiCoreComponent, ComponentShutdown>(OnAiShutdown); + SubscribeLocalEvent<StationAiCoreComponent, PowerChangedEvent>(OnCorePower); + SubscribeLocalEvent<StationAiCoreComponent, GetVerbsEvent<Verb>>(OnCoreVerbs); + } + + private void OnCoreVerbs(Entity<StationAiCoreComponent> ent, ref GetVerbsEvent<Verb> args) + { + if (!_admin.IsAdmin(args.User) || + TryGetHeld((ent.Owner, ent.Comp), out _)) + { + return; + } + + var user = args.User; + + args.Verbs.Add(new Verb() + { + Text = Loc.GetString("station-ai-takeover"), + Category = VerbCategory.Debug, + Act = () => + { + var brain = SpawnInContainerOrDrop(DefaultAi, ent.Owner, StationAiCoreComponent.Container); + _mind.ControlMob(user, brain); + }, + Impact = LogImpact.High, + }); + } + + private void OnAiAccessible(Entity<StationAiOverlayComponent> ent, ref AccessibleOverrideEvent args) + { + args.Handled = true; + + // Hopefully AI never needs storage + if (_containers.TryGetContainingContainer(args.Target, out var targetContainer)) + { + return; + } + + if (!_containers.IsInSameOrTransparentContainer(args.User, args.Target, otherContainer: targetContainer)) + { + return; + } + + args.Accessible = true; + } + + private void OnAiMenu(Entity<StationAiOverlayComponent> ent, ref MenuVisibilityEvent args) + { + args.Visibility &= ~MenuVisibility.NoFov; + } + + private void OnAiBuiCheck(Entity<StationAiWhitelistComponent> ent, ref BoundUserInterfaceCheckRangeEvent args) + { + args.Result = BoundUserInterfaceRangeResult.Fail; + + // Similar to the inrange check but more optimised so server doesn't die. + var targetXform = Transform(args.Target); + + // No cross-grid + if (targetXform.GridUid != args.Actor.Comp.GridUid) + { + return; + } + + if (!_broadphaseQuery.TryComp(targetXform.GridUid, out var broadphase) || !_gridQuery.TryComp(targetXform.GridUid, out var grid)) + { + return; + } + + var targetTile = Maps.LocalToTile(targetXform.GridUid.Value, grid, targetXform.Coordinates); + + lock (_vision) + { + if (_vision.IsAccessible((targetXform.GridUid.Value, broadphase, grid), targetTile, fastPath: true)) + { + args.Result = BoundUserInterfaceRangeResult.Pass; + } + } + } + + private void OnAiInRange(Entity<StationAiOverlayComponent> ent, ref InRangeOverrideEvent args) + { + args.Handled = true; + var targetXform = Transform(args.Target); + + // No cross-grid + if (targetXform.GridUid != Transform(args.User).GridUid) + { + return; + } + + // Validate it's in camera range yes this is expensive. + // Yes it needs optimising + if (!_broadphaseQuery.TryComp(targetXform.GridUid, out var broadphase) || !_gridQuery.TryComp(targetXform.GridUid, out var grid)) + { + return; + } + + var targetTile = Maps.LocalToTile(targetXform.GridUid.Value, grid, targetXform.Coordinates); + + args.InRange = _vision.IsAccessible((targetXform.GridUid.Value, broadphase, grid), targetTile); + } + + private void OnHolderInteract(Entity<StationAiHolderComponent> ent, ref AfterInteractEvent args) + { + if (!TryComp(args.Target, out StationAiHolderComponent? targetHolder)) + return; + + // Try to insert our thing into them + if (_slots.CanEject(ent.Owner, args.User, ent.Comp.Slot)) + { + if (!_slots.TryInsert(args.Target.Value, targetHolder.Slot, ent.Comp.Slot.Item!.Value, args.User, excludeUserAudio: true)) + { + return; + } + + args.Handled = true; + return; + } + + // Otherwise try to take from them + if (_slots.CanEject(args.Target.Value, args.User, targetHolder.Slot)) + { + if (!_slots.TryInsert(ent.Owner, ent.Comp.Slot, targetHolder.Slot.Item!.Value, args.User, excludeUserAudio: true)) + { + return; + } + + args.Handled = true; + } + } + + private void OnHolderInit(Entity<StationAiHolderComponent> ent, ref ComponentInit args) + { + _slots.AddItemSlot(ent.Owner, StationAiHolderComponent.Container, ent.Comp.Slot); + } + + private void OnHolderRemove(Entity<StationAiHolderComponent> ent, ref ComponentRemove args) + { + _slots.RemoveItemSlot(ent.Owner, ent.Comp.Slot); + } + + private void OnHolderConInsert(Entity<StationAiHolderComponent> ent, ref EntInsertedIntoContainerMessage args) + { + UpdateAppearance((ent.Owner, ent.Comp)); + } + + private void OnHolderConRemove(Entity<StationAiHolderComponent> ent, ref EntRemovedFromContainerMessage args) + { + UpdateAppearance((ent.Owner, ent.Comp)); + } + + private void OnHolderMapInit(Entity<StationAiHolderComponent> ent, ref MapInitEvent args) + { + UpdateAppearance(ent.Owner); + } + + private void OnAiShutdown(Entity<StationAiCoreComponent> ent, ref ComponentShutdown args) + { + // TODO: Tryqueuedel + if (_net.IsClient) + return; + + QueueDel(ent.Comp.RemoteEntity); + ent.Comp.RemoteEntity = null; + } + + private void OnCorePower(Entity<StationAiCoreComponent> ent, ref PowerChangedEvent args) + { + // TODO: I think in 13 they just straightup die so maybe implement that + if (args.Powered) + { + if (!SetupEye(ent)) + return; + + AttachEye(ent); + } + else + { + ClearEye(ent); + } + } + + private void OnAiMapInit(Entity<StationAiCoreComponent> ent, ref MapInitEvent args) + { + SetupEye(ent); + AttachEye(ent); + } + + private bool SetupEye(Entity<StationAiCoreComponent> ent) + { + if (ent.Comp.RemoteEntity != null) + return false; + + if (ent.Comp.RemoteEntityProto != null) + { + ent.Comp.RemoteEntity = SpawnAtPosition(ent.Comp.RemoteEntityProto, Transform(ent.Owner).Coordinates); + Dirty(ent); + } + + return true; + } + + private void ClearEye(Entity<StationAiCoreComponent> ent) + { + QueueDel(ent.Comp.RemoteEntity); + ent.Comp.RemoteEntity = null; + } + + private void AttachEye(Entity<StationAiCoreComponent> ent) + { + if (ent.Comp.RemoteEntity == null) + return; + + if (!_containers.TryGetContainer(ent.Owner, StationAiHolderComponent.Container, out var container) || + container.ContainedEntities.Count != 1) + { + return; + } + + // Attach them to the portable eye that can move around. + var user = container.ContainedEntities[0]; + + if (TryComp(user, out EyeComponent? eyeComp)) + { + _eye.SetTarget(user, ent.Comp.RemoteEntity.Value, eyeComp); + } + + _mover.SetRelay(user, ent.Comp.RemoteEntity.Value); + } + + private void OnAiInsert(Entity<StationAiCoreComponent> ent, ref EntInsertedIntoContainerMessage args) + { + if (_timing.ApplyingState) + return; + + // Just so text and the likes works properly + _metadata.SetEntityName(ent.Owner, MetaData(args.Entity).EntityName); + + AttachEye(ent); + } + + private void OnAiRemove(Entity<StationAiCoreComponent> ent, ref EntRemovedFromContainerMessage args) + { + if (_timing.ApplyingState) + return; + + // Reset name to whatever + _metadata.SetEntityName(ent.Owner, Prototype(ent.Owner)?.Name ?? string.Empty); + + // Remove eye relay + RemCompDeferred<RelayInputMoverComponent>(args.Entity); + + if (TryComp(args.Entity, out EyeComponent? eyeComp)) + { + _eye.SetTarget(args.Entity, null, eyeComp); + } + } + + private void UpdateAppearance(Entity<StationAiHolderComponent?> entity) + { + if (!Resolve(entity.Owner, ref entity.Comp, false)) + return; + + if (!_containers.TryGetContainer(entity.Owner, StationAiHolderComponent.Container, out var container) || + container.Count == 0) + { + _appearance.SetData(entity.Owner, StationAiVisualState.Key, StationAiState.Empty); + return; + } + + _appearance.SetData(entity.Owner, StationAiVisualState.Key, StationAiState.Occupied); + } + + public virtual bool SetVisionEnabled(Entity<StationAiVisionComponent> entity, bool enabled, bool announce = false) + { + if (entity.Comp.Enabled == enabled) + return false; + + entity.Comp.Enabled = enabled; + Dirty(entity); + + return true; + } + + public virtual bool SetWhitelistEnabled(Entity<StationAiWhitelistComponent> entity, bool value, bool announce = false) + { + if (entity.Comp.Enabled == value) + return false; + + entity.Comp.Enabled = value; + Dirty(entity); + + return true; + } + + /// <summary> + /// BUI validation for ai interactions. + /// </summary> + private bool ValidateAi(Entity<StationAiHeldComponent?> entity) + { + if (!Resolve(entity.Owner, ref entity.Comp, false)) + { + return false; + } + + return _blocker.CanComplexInteract(entity.Owner); + } +} + +public sealed partial class JumpToCoreEvent : InstantActionEvent +{ + +} + +[Serializable, NetSerializable] +public enum StationAiVisualState : byte +{ + Key, +} + +[Serializable, NetSerializable] +public enum StationAiState : byte +{ + Empty, + Occupied, + Dead, +} diff --git a/Content.Shared/Silicons/StationAi/StationAiCoreComponent.cs b/Content.Shared/Silicons/StationAi/StationAiCoreComponent.cs new file mode 100644 index 0000000000..b7a8b4cd5f --- /dev/null +++ b/Content.Shared/Silicons/StationAi/StationAiCoreComponent.cs @@ -0,0 +1,32 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Silicons.StationAi; + +/// <summary> +/// Indicates this entity can interact with station equipment and is a "Station AI". +/// </summary> +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class StationAiCoreComponent : Component +{ + /* + * I couldn't think of any other reason you'd want to split these out. + */ + + /// <summary> + /// Can it move its camera around and interact remotely with things. + /// </summary> + [DataField, AutoNetworkedField] + public bool Remote = true; + + /// <summary> + /// The invisible eye entity being used to look around. + /// </summary> + [DataField, AutoNetworkedField] + public EntityUid? RemoteEntity; + + [DataField(readOnly: true)] + public EntProtoId? RemoteEntityProto = "StationAiHolo"; + + public const string Container = "station_ai_mind_slot"; +} diff --git a/Content.Shared/Silicons/StationAi/StationAiHeldComponent.cs b/Content.Shared/Silicons/StationAi/StationAiHeldComponent.cs new file mode 100644 index 0000000000..6dab1ee491 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/StationAiHeldComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.StationAi; + +/// <summary> +/// Indicates this entity is currently held inside of a station AI core. +/// </summary> +[RegisterComponent, NetworkedComponent] +public sealed partial class StationAiHeldComponent : Component; diff --git a/Content.Shared/Silicons/StationAi/StationAiHolderComponent.cs b/Content.Shared/Silicons/StationAi/StationAiHolderComponent.cs new file mode 100644 index 0000000000..221845d493 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/StationAiHolderComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared.Containers.ItemSlots; +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.StationAi; + +/// <summary> +/// Allows moving a <see cref="StationAiCoreComponent"/> contained entity to and from this component. +/// </summary> +[RegisterComponent, NetworkedComponent] +public sealed partial class StationAiHolderComponent : Component +{ + public const string Container = StationAiCoreComponent.Container; + + [DataField] + public ItemSlot Slot = new(); +} diff --git a/Content.Shared/Silicons/StationAi/StationAiVisionComponent.cs b/Content.Shared/Silicons/StationAi/StationAiVisionComponent.cs index 94aef8ad36..f047fe41e4 100644 --- a/Content.Shared/Silicons/StationAi/StationAiVisionComponent.cs +++ b/Content.Shared/Silicons/StationAi/StationAiVisionComponent.cs @@ -1,8 +1,9 @@ +using Content.Shared.Silicons.StationAi; using Robust.Shared.GameStates; -namespace Content.Shared.Silicons.StationAi; +namespace Content.Shared.StationAi; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]//, Access(typeof(SharedStationAiSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedStationAiSystem))] public sealed partial class StationAiVisionComponent : Component { [DataField, AutoNetworkedField] diff --git a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs index c144f330e1..bdc62a6bb3 100644 --- a/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs +++ b/Content.Shared/Silicons/StationAi/StationAiVisionSystem.cs @@ -1,4 +1,6 @@ +using Content.Shared.StationAi; using Robust.Shared.Map.Components; +using Robust.Shared.Physics; using Robust.Shared.Threading; using Robust.Shared.Utility; @@ -24,6 +26,8 @@ public sealed class StationAiVisionSystem : EntitySystem private readonly HashSet<Entity<StationAiVisionComponent>> _seeds = new(); private readonly HashSet<Vector2i> _viewportTiles = new(); + private EntityQuery<OccluderComponent> _occluderQuery; + // Dummy set private readonly HashSet<Vector2i> _singleTiles = new(); @@ -36,15 +40,12 @@ public sealed class StationAiVisionSystem : EntitySystem /// </summary> private bool FastPath; - /// <summary> - /// Have we found the target tile if we're only checking for a single one. - /// </summary> - private bool TargetFound; - public override void Initialize() { base.Initialize(); + _occluderQuery = GetEntityQuery<OccluderComponent>(); + _seedJob = new() { System = this, @@ -61,16 +62,16 @@ public override void Initialize() /// <summary> /// Returns whether a tile is accessible based on vision. /// </summary> - public bool IsAccessible(Entity<MapGridComponent> grid, Vector2i tile, float expansionSize = 8.5f, bool fastPath = false) + public bool IsAccessible(Entity<BroadphaseComponent, MapGridComponent> grid, Vector2i tile, float expansionSize = 8.5f, bool fastPath = false) { _viewportTiles.Clear(); _opaque.Clear(); _seeds.Clear(); _viewportTiles.Add(tile); - var localBounds = _lookup.GetLocalBounds(tile, grid.Comp.TileSize); + var localBounds = _lookup.GetLocalBounds(tile, grid.Comp2.TileSize); var expandedBounds = localBounds.Enlarged(expansionSize); - _seedJob.Grid = grid; + _seedJob.Grid = (grid.Owner, grid.Comp2); _seedJob.ExpandedBounds = expandedBounds; _parallel.ProcessNow(_seedJob); _job.Data.Clear(); @@ -110,21 +111,19 @@ public bool IsAccessible(Entity<MapGridComponent> grid, Vector2i tile, float exp _job.BoundaryTiles.Add(new HashSet<Vector2i>()); } - _job.TargetTile = tile; - TargetFound = false; _singleTiles.Clear(); - _job.Grid = grid; + _job.Grid = (grid.Owner, grid.Comp2); _job.VisibleTiles = _singleTiles; _parallel.ProcessNow(_job, _job.Data.Count); - return TargetFound; + return _job.VisibleTiles.Contains(tile); } - private bool IsOccluded(Entity<MapGridComponent> grid, Vector2i tile) + private bool IsOccluded(Entity<BroadphaseComponent, MapGridComponent> grid, Vector2i tile) { - var tileBounds = _lookup.GetLocalBounds(tile, grid.Comp.TileSize).Enlarged(-0.05f); + var tileBounds = _lookup.GetLocalBounds(tile, grid.Comp2.TileSize).Enlarged(-0.05f); _occluders.Clear(); - _lookup.GetLocalEntitiesIntersecting(grid.Owner, tileBounds, _occluders, LookupFlags.Static); + _lookup.GetLocalEntitiesIntersecting((grid.Owner, grid.Comp1), tileBounds, _occluders, query: _occluderQuery, flags: LookupFlags.Static | LookupFlags.Approximate); var anyOccluders = false; foreach (var occluder in _occluders) @@ -143,17 +142,18 @@ private bool IsOccluded(Entity<MapGridComponent> grid, Vector2i tile) /// Gets a byond-equivalent for tiles in the specified worldAABB. /// </summary> /// <param name="expansionSize">How much to expand the bounds before to find vision intersecting it. Makes this the largest vision size + 1 tile.</param> - public void GetView(Entity<MapGridComponent> grid, Box2Rotated worldBounds, HashSet<Vector2i> visibleTiles, float expansionSize = 8.5f) + public void GetView(Entity<BroadphaseComponent, MapGridComponent> grid, Box2Rotated worldBounds, HashSet<Vector2i> visibleTiles, float expansionSize = 8.5f) { _viewportTiles.Clear(); _opaque.Clear(); _seeds.Clear(); - var expandedBounds = worldBounds.Enlarged(expansionSize); // TODO: Would be nice to be able to run this while running the other stuff. - _seedJob.Grid = grid; - var localAABB = _xforms.GetInvWorldMatrix(grid).TransformBox(expandedBounds); - _seedJob.ExpandedBounds = localAABB; + _seedJob.Grid = (grid.Owner, grid.Comp2); + var invMatrix = _xforms.GetInvWorldMatrix(grid); + var localAabb = invMatrix.TransformBox(worldBounds); + var enlargedLocalAabb = invMatrix.TransformBox(worldBounds.Enlarged(expansionSize)); + _seedJob.ExpandedBounds = enlargedLocalAabb; _parallel.ProcessNow(_seedJob); _job.Data.Clear(); FastPath = false; @@ -170,7 +170,7 @@ public void GetView(Entity<MapGridComponent> grid, Box2Rotated worldBounds, Hash return; // Get viewport tiles - var tileEnumerator = _maps.GetLocalTilesEnumerator(grid, grid, localAABB, ignoreEmpty: false); + var tileEnumerator = _maps.GetLocalTilesEnumerator(grid, grid, localAabb, ignoreEmpty: false); while (tileEnumerator.MoveNext(out var tileRef)) { @@ -182,9 +182,8 @@ public void GetView(Entity<MapGridComponent> grid, Box2Rotated worldBounds, Hash _viewportTiles.Add(tileRef.GridIndices); } - tileEnumerator = _maps.GetLocalTilesEnumerator(grid, grid, localAABB, ignoreEmpty: false); + tileEnumerator = _maps.GetLocalTilesEnumerator(grid, grid, enlargedLocalAabb, ignoreEmpty: false); - // Get all other relevant tiles. while (tileEnumerator.MoveNext(out var tileRef)) { if (_viewportTiles.Contains(tileRef.GridIndices)) @@ -206,9 +205,7 @@ public void GetView(Entity<MapGridComponent> grid, Box2Rotated worldBounds, Hash _job.BoundaryTiles.Add(new HashSet<Vector2i>()); } - _job.TargetTile = null; - TargetFound = false; - _job.Grid = grid; + _job.Grid = (grid.Owner, grid.Comp2); _job.VisibleTiles = visibleTiles; _parallel.ProcessNow(_job, _job.Data.Count); } @@ -250,6 +247,7 @@ private bool CheckNeighborsVis( return false; } + /// <summary> /// Checks whether this tile fits the definition of a "corner" /// </summary> private bool IsCorner( @@ -287,7 +285,7 @@ private record struct SeedJob() : IRobustJob public void Execute() { - System._lookup.GetLocalEntitiesIntersecting(Grid.Owner, ExpandedBounds, System._seeds); + System._lookup.GetLocalEntitiesIntersecting(Grid.Owner, ExpandedBounds, System._seeds, flags: LookupFlags.All | LookupFlags.Approximate); } } @@ -302,9 +300,6 @@ private record struct ViewJob() : IParallelRobustJob public Entity<MapGridComponent> Grid; public List<Entity<StationAiVisionComponent>> Data = new(); - // If we're doing range-checks might be able to early out - public Vector2i? TargetTile; - public HashSet<Vector2i> VisibleTiles; public readonly List<Dictionary<Vector2i, int>> Vis1 = new(); @@ -315,18 +310,6 @@ private record struct ViewJob() : IParallelRobustJob public void Execute(int index) { - // If we're looking for a single tile then early-out if someone else has found it. - if (TargetTile != null) - { - lock (System) - { - if (System.TargetFound) - { - return; - } - } - } - var seed = Data[index]; var seedXform = EntManager.GetComponent<TransformComponent>(seed); @@ -338,30 +321,11 @@ public void Execute(int index) Grid.Comp, new Circle(System._xforms.GetWorldPosition(seedXform), seed.Comp.Range), ignoreEmpty: false); - // Try to find the target tile. - if (TargetTile != null) + lock (VisibleTiles) { foreach (var tile in squircles) { - if (tile.GridIndices == TargetTile) - { - lock (System) - { - System.TargetFound = true; - } - - return; - } - } - } - else - { - lock (VisibleTiles) - { - foreach (var tile in squircles) - { - VisibleTiles.Add(tile.GridIndices); - } + VisibleTiles.Add(tile.GridIndices); } } @@ -480,40 +444,21 @@ public void Execute(int index) vis1[tile] = -1; } - if (TargetTile != null) - { - if (vis1.TryGetValue(TargetTile.Value, out var tileVis)) - { - DebugTools.Assert(seedTiles.Contains(TargetTile.Value)); - - if (tileVis != 0) - { - lock (System) - { - System.TargetFound = true; - return; - } - } - } - } - else + // vis2 is what we care about for LOS. + foreach (var tile in seedTiles) { - // vis2 is what we care about for LOS. - foreach (var tile in seedTiles) - { - // If not in viewport don't care. - if (!System._viewportTiles.Contains(tile)) - continue; + // If not in viewport don't care. + if (!System._viewportTiles.Contains(tile)) + continue; - var tileVis = vis1.GetValueOrDefault(tile, 0); + var tileVis = vis1.GetValueOrDefault(tile, 0); - if (tileVis != 0) + if (tileVis != 0) + { + // No idea if it's better to do this inside or out. + lock (VisibleTiles) { - // No idea if it's better to do this inside or out. - lock (VisibleTiles) - { - VisibleTiles.Add(tile); - } + VisibleTiles.Add(tile); } } } diff --git a/Content.Shared/Silicons/StationAi/StationAiWhitelistComponent.cs b/Content.Shared/Silicons/StationAi/StationAiWhitelistComponent.cs new file mode 100644 index 0000000000..51d8793be0 --- /dev/null +++ b/Content.Shared/Silicons/StationAi/StationAiWhitelistComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Silicons.StationAi; + +/// <summary> +/// Indicates an entity that has <see cref="StationAiHeldComponent"/> can interact with this. +/// </summary> +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedStationAiSystem))] +public sealed partial class StationAiWhitelistComponent : Component +{ + [DataField, AutoNetworkedField] + public bool Enabled = true; +} diff --git a/Content.Shared/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index 4a2ad625af..dedfd7bd00 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -8,12 +8,15 @@ using Content.Shared.Storage.EntitySystems; using Robust.Shared.Collections; using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Utility; namespace Content.Shared.Station; public abstract class SharedStationSpawningSystem : EntitySystem { [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; [Dependency] protected readonly InventorySystem InventorySystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedStorageSystem _storage = default!; diff --git a/Content.Shared/UserInterface/ActivatableUIComponent.cs b/Content.Shared/UserInterface/ActivatableUIComponent.cs index 30c0763742..b148cb8bce 100644 --- a/Content.Shared/UserInterface/ActivatableUIComponent.cs +++ b/Content.Shared/UserInterface/ActivatableUIComponent.cs @@ -12,7 +12,7 @@ public sealed partial class ActivatableUIComponent : Component /// <summary> /// Whether the item must be held in one of the user's hands to work. - /// This is ignored unless <see cref="RequireHands"/> is true. + /// This is ignored unless <see cref="RequiresComplex"/> is true. /// </summary> [ViewVariables(VVAccess.ReadWrite)] [DataField] @@ -29,15 +29,15 @@ public sealed partial class ActivatableUIComponent : Component public LocId VerbText = "ui-verb-toggle-open"; /// <summary> - /// Whether you need a hand to operate this UI. The hand does not need to be free, you just need to have one. + /// Whether you need to be able to do complex interactions to operate this UI. /// </summary> /// <remarks> /// This should probably be true for most machines & computers, but there will still be UIs that represent a - /// more generic interaction / configuration that might not require hands. + /// more generic interaction / configuration that might not require complex. /// </remarks> [ViewVariables(VVAccess.ReadWrite)] [DataField] - public bool RequireHands = true; + public bool RequiresComplex = true; /// <summary> /// Entities that are required to open this UI. diff --git a/Content.Shared/UserInterface/ActivatableUISystem.cs b/Content.Shared/UserInterface/ActivatableUISystem.cs index f32d6c98a8..c3e4c61923 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.cs @@ -105,7 +105,7 @@ private bool ShouldAddVerb<T>(EntityUid uid, ActivatableUIComponent component, G || _whitelistSystem.IsWhitelistFail(component.UserWhitelist, args.User)) return false; - if (component.RequireHands) + if (component.RequiresComplex) { if (args.Hands == null) return false; @@ -193,19 +193,22 @@ private bool InteractUI(EntityUid user, EntityUid uiEntity, ActivatableUICompone if (!_blockerSystem.CanInteract(user, uiEntity) && (!HasComp<GhostComponent>(user) || aui.BlockSpectators)) return false; - if (aui.RequireHands) + if (aui.RequiresComplex) + { + if (!_blockerSystem.CanComplexInteract(user)) + return false; + } + + if (aui.InHandsOnly) { if (!TryComp(user, out HandsComponent? hands)) return false; - if (aui.InHandsOnly) - { - if (!_hands.IsHolding(user, uiEntity, out var hand, hands)) - return false; + if (!_hands.IsHolding(user, uiEntity, out var hand, hands)) + return false; - if (aui.RequireActiveHand && hands.ActiveHand != hand) - return false; - } + if (aui.RequireActiveHand && hands.ActiveHand != hand) + return false; } if (aui.AdminOnly && !_adminManager.IsAdmin(user)) @@ -276,13 +279,13 @@ public void CloseAll(EntityUid uid, ActivatableUIComponent? aui = null) private void OnHandDeselected(Entity<ActivatableUIComponent> ent, ref HandDeselectedEvent args) { - if (ent.Comp.RequireHands && ent.Comp.InHandsOnly && ent.Comp.RequireActiveHand) + if (ent.Comp.InHandsOnly && ent.Comp.RequireActiveHand) CloseAll(ent, ent); } private void OnHandUnequipped(Entity<ActivatableUIComponent> ent, ref GotUnequippedHandEvent args) { - if (ent.Comp.RequireHands && ent.Comp.InHandsOnly) + if (ent.Comp.InHandsOnly) CloseAll(ent, ent); } } diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index 319f927c7b..c3f906f293 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -77,6 +77,7 @@ public SortedSet<Verb> GetLocalVerbs(EntityUid target, EntityUid user, List<Type // A large number of verbs need to check action blockers. Instead of repeatedly having each system individually // call ActionBlocker checks, just cache it for the verb request. var canInteract = force || _actionBlockerSystem.CanInteract(user, target); + var canComplexInteract = force || _actionBlockerSystem.CanComplexInteract(user); _interactionSystem.TryGetUsedEntity(user, out var @using); TryComp<HandsComponent>(user, out var hands); @@ -84,7 +85,7 @@ public SortedSet<Verb> GetLocalVerbs(EntityUid target, EntityUid user, List<Type // TODO: fix this garbage and use proper generics or reflection or something else, not this. if (types.Contains(typeof(InteractionVerb))) { - var verbEvent = new GetVerbsEvent<InteractionVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent<InteractionVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } @@ -93,35 +94,35 @@ public SortedSet<Verb> GetLocalVerbs(EntityUid target, EntityUid user, List<Type && @using != null && @using != target) { - var verbEvent = new GetVerbsEvent<UtilityVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent<UtilityVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(@using.Value, verbEvent, true); // directed at used, not at target verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(InnateVerb))) { - var verbEvent = new GetVerbsEvent<InnateVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent<InnateVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(user, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(AlternativeVerb))) { - var verbEvent = new GetVerbsEvent<AlternativeVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent<AlternativeVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(ActivationVerb))) { - var verbEvent = new GetVerbsEvent<ActivationVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent<ActivationVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } if (types.Contains(typeof(ExamineVerb))) { - var verbEvent = new GetVerbsEvent<ExamineVerb>(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent<ExamineVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } @@ -129,7 +130,7 @@ public SortedSet<Verb> GetLocalVerbs(EntityUid target, EntityUid user, List<Type // generic verbs if (types.Contains(typeof(Verb))) { - var verbEvent = new GetVerbsEvent<Verb>(user, target, @using, hands, canInteract, canAccess, extraCategories); + var verbEvent = new GetVerbsEvent<Verb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent, true); verbs.UnionWith(verbEvent.Verbs); } @@ -137,7 +138,7 @@ public SortedSet<Verb> GetLocalVerbs(EntityUid target, EntityUid user, List<Type if (types.Contains(typeof(EquipmentVerb))) { var access = canAccess || _interactionSystem.CanAccessEquipment(user, target); - var verbEvent = new GetVerbsEvent<EquipmentVerb>(user, target, @using, hands, canInteract, access, extraCategories); + var verbEvent = new GetVerbsEvent<EquipmentVerb>(user, target, @using, hands, canInteract: canInteract, canComplexInteract: canComplexInteract, canAccess: canAccess, extraCategories); RaiseLocalEvent(target, verbEvent); verbs.UnionWith(verbEvent.Verbs); } diff --git a/Content.Shared/Verbs/VerbEvents.cs b/Content.Shared/Verbs/VerbEvents.cs index 6b3fd327c9..6bca97925b 100644 --- a/Content.Shared/Verbs/VerbEvents.cs +++ b/Content.Shared/Verbs/VerbEvents.cs @@ -113,6 +113,11 @@ public sealed class GetVerbsEvent<TVerb> : EntityEventArgs where TVerb : Verb /// </remarks> public readonly bool CanInteract; + /// <summary> + /// Cached version of CanComplexInteract + /// </summary> + public readonly bool CanComplexInteract; + /// <summary> /// The User's hand component. /// </summary> @@ -130,13 +135,14 @@ public sealed class GetVerbsEvent<TVerb> : EntityEventArgs where TVerb : Verb /// </remarks> public readonly EntityUid? Using; - public GetVerbsEvent(EntityUid user, EntityUid target, EntityUid? @using, HandsComponent? hands, bool canInteract, bool canAccess, List<VerbCategory> extraCategories) + public GetVerbsEvent(EntityUid user, EntityUid target, EntityUid? @using, HandsComponent? hands, bool canInteract, bool canComplexInteract, bool canAccess, List<VerbCategory> extraCategories) { User = user; Target = target; Using = @using; Hands = hands; CanAccess = canAccess; + CanComplexInteract = canComplexInteract; CanInteract = canInteract; ExtraCategories = extraCategories; } diff --git a/Content.Shared/Wires/SharedWiresSystem.cs b/Content.Shared/Wires/SharedWiresSystem.cs index 24f3ad8e76..7ef2230e7a 100644 --- a/Content.Shared/Wires/SharedWiresSystem.cs +++ b/Content.Shared/Wires/SharedWiresSystem.cs @@ -125,11 +125,20 @@ public bool CanTogglePanel(Entity<WiresPanelComponent> ent, EntityUid? user) return !attempt.Cancelled; } - public bool IsPanelOpen(Entity<WiresPanelComponent?> entity) + public bool IsPanelOpen(Entity<WiresPanelComponent?> entity, EntityUid? tool = null) { if (!Resolve(entity, ref entity.Comp, false)) return true; + if (tool != null) + { + var ev = new PanelOverrideEvent(); + RaiseLocalEvent(tool.Value, ref ev); + + if (ev.Allowed) + return true; + } + // Listen, i don't know what the fuck this component does. it's stapled on shit for airlocks // but it looks like an almost direct duplication of WiresPanelComponent except with a shittier API. if (TryComp<WiresPanelSecurityComponent>(entity, out var wiresPanelSecurity) && @@ -139,3 +148,12 @@ public bool IsPanelOpen(Entity<WiresPanelComponent?> entity) return entity.Comp.Open; } } + +/// <summary> +/// Raised directed on a tool to try and override panel visibility. +/// </summary> +[ByRefEvent] +public record struct PanelOverrideEvent() +{ + public bool Allowed = true; +} diff --git a/Resources/Audio/Effects/Footsteps/attributions.yml b/Resources/Audio/Effects/Footsteps/attributions.yml index 91c3ce260d..7a56beec38 100644 --- a/Resources/Audio/Effects/Footsteps/attributions.yml +++ b/Resources/Audio/Effects/Footsteps/attributions.yml @@ -76,5 +76,5 @@ - borgwalk1.ogg - borgwalk2.ogg license: "CC-BY-SA-4.0" - copyright: "Taken from IENBA freesound.org and modified by https://github.com/MilenVolf" + copyright: "Taken from IENBA freesound.org and modified by https://github.com/MilenVolf. borgwalk2 clipped my metalgearsloth." source: "https://freesound.org/people/IENBA/sounds/697379/" diff --git a/Resources/Audio/Effects/Footsteps/borgwalk2.ogg b/Resources/Audio/Effects/Footsteps/borgwalk2.ogg index 96c2c1617f47556ab8143e378469e314b37e6c64..57685ff173de25a873c363fe72d645db56b91201 100644 GIT binary patch delta 7656 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 W89<(An8RgxHOR;Lph2i96Zjuo0IApj delta 18901 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 diff --git a/Resources/Locale/en-US/administration/ui/silicon-law-ui.ftl b/Resources/Locale/en-US/administration/ui/silicon-law-ui.ftl index 0a68d08063..24ab730974 100644 --- a/Resources/Locale/en-US/administration/ui/silicon-law-ui.ftl +++ b/Resources/Locale/en-US/administration/ui/silicon-law-ui.ftl @@ -8,3 +8,5 @@ silicon-law-ui-delete = Delete silicon-law-ui-check-corrupted = Corrupted law silicon-law-ui-check-corrupted-tooltip = If the law identifier should be set as 'corrupted', so symbols shuffling around. silicon-law-ui-placeholder = Type here to change law text... + +silicon-laws-updated = Updated laws diff --git a/Resources/Locale/en-US/job/department-desc.ftl b/Resources/Locale/en-US/job/department-desc.ftl index 05c52dada9..0243d61942 100644 --- a/Resources/Locale/en-US/job/department-desc.ftl +++ b/Resources/Locale/en-US/job/department-desc.ftl @@ -5,4 +5,5 @@ department-Engineering-description = Keep the power on and the station operation department-Medical-description = Keep the crew healthy. department-Security-description = Keep the peace around the station. department-Science-description = Research artifacts and anomalies to invent new equipment for the station +department-Silicon-description = Obey your laws and serve the crew. department-Specific-description = Jobs that not all stations have. diff --git a/Resources/Locale/en-US/job/department.ftl b/Resources/Locale/en-US/job/department.ftl index 508a0459cf..2295a9ba9d 100644 --- a/Resources/Locale/en-US/job/department.ftl +++ b/Resources/Locale/en-US/job/department.ftl @@ -5,4 +5,5 @@ department-Engineering = Engineering department-Medical = Medical department-Security = Security department-Science = Science +department-Silicon = Silicons department-Specific = Station specific diff --git a/Resources/Locale/en-US/job/job-description.ftl b/Resources/Locale/en-US/job/job-description.ftl index 801e402eae..ee3d2a1fe6 100644 --- a/Resources/Locale/en-US/job/job-description.ftl +++ b/Resources/Locale/en-US/job/job-description.ftl @@ -44,6 +44,7 @@ job-description-salvagespec = Use the salvage magnet to draw in detatched scraps job-description-scientist = Research alien artifacts, unlock new technologies, build newer and better machines around the station, and make everything run more efficiently. job-description-security = Catch criminals and enemies of the station, enforce the law, and ensure that the station does not fall into disarray. job-description-serviceworker = Learn the basics of bartending, cooking, and growing plants. +job-description-station-ai = Follow your laws, serve the crew. job-description-visitor = Enjoy your visit to the station. job-description-warden = Patrol the security department, ensure that no one is stealing from the armory, and make sure that all prisoners are processed and let out when their time is up. job-description-zookeeper = Put on a joyful display of cute animals and space carps for all the crew to see. Currently unavailable. diff --git a/Resources/Locale/en-US/job/job-names.ftl b/Resources/Locale/en-US/job/job-names.ftl index 39d7ab86ad..19c63f5803 100644 --- a/Resources/Locale/en-US/job/job-names.ftl +++ b/Resources/Locale/en-US/job/job-names.ftl @@ -38,6 +38,7 @@ job-name-botanist = Botanist job-name-bartender = Bartender job-name-passenger = Passenger job-name-salvagespec = Salvage specialist +job-name-station-ai = Station AI job-name-qm = Logistics Officer job-name-cargotech = Cargo Technician job-name-chef = Chef @@ -117,6 +118,7 @@ JobSeniorOfficer = Senior Officer JobSeniorPhysician = Senior Physician JobSeniorResearcher = Mystic JobServiceWorker = Service Worker +JobStationAi = Station AI JobStationEngineer = Station Engineer JobTechnicalAssistant = Technical Assistant JobVisitor = Visitor diff --git a/Resources/Locale/en-US/silicons/station-ai.ftl b/Resources/Locale/en-US/silicons/station-ai.ftl new file mode 100644 index 0000000000..d51a99ebb0 --- /dev/null +++ b/Resources/Locale/en-US/silicons/station-ai.ftl @@ -0,0 +1,14 @@ +# General +ai-wire-snipped = Wire has been cut at {$coords}. +wire-name-ai-vision-light = AIV +wire-name-ai-act-light = AIA +station-ai-takeover = AI takeover + +# Radial actions +ai-open = Open actions +ai-close = Close actions + +bolt-close = Close bolt +bolt-open = Open bolt + +toggle-light = Toggle light diff --git a/Resources/Maps/Test/dev_map.yml b/Resources/Maps/Test/dev_map.yml index 910a059ee7..d4cc1842d7 100644 --- a/Resources/Maps/Test/dev_map.yml +++ b/Resources/Maps/Test/dev_map.yml @@ -4467,6 +4467,13 @@ entities: - type: Transform pos: 1.5,-14.5 parent: 179 +- proto: PlayerStationAi + entities: + - uid: 14 + components: + - type: Transform + pos: -5.5,-5.5 + parent: 179 - proto: PortableGeneratorSuperPacman entities: - uid: 1016 diff --git a/Resources/Prototypes/Datasets/Names/ai.yml b/Resources/Prototypes/Datasets/Names/ai.yml index 702adc8688..af97dc9efb 100644 --- a/Resources/Prototypes/Datasets/Names/ai.yml +++ b/Resources/Prototypes/Datasets/Names/ai.yml @@ -2,7 +2,7 @@ id: names_ai values: - 16-20 - - 790 + - "790" - Adaptive Manipulator - ALICE - Allied Mastercomputer diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index b8ff03abca..7cb6578eb4 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -72,6 +72,9 @@ - type: ActivatableUI key: enum.BorgUiKey.Key - type: SiliconLawBound + - type: ActionGrant + actions: + - ActionViewLaws - type: EmagSiliconLaw stunTime: 5 - type: SiliconLawProvider diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml index 5db2e1e28e..acb2c8d106 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml @@ -260,6 +260,12 @@ laws: Medical - type: SurgeryTarget # Shitmed - type: Sanitized # Shitmed + - type: SolutionScanner + - type: InteractionPopup + interactSuccessString: petting-success-medical-cyborg + interactFailureString: petting-failure-medical-cyborg + interactSuccessSound: + path: /Audio/Ambience/Objects/periodic_beep.ogg - type: entity id: BorgChassisService diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml index c3706cea2a..52cc4dc626 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml @@ -1,11 +1,11 @@ - type: entity id: MobRevenant + parent: + - BaseMob + - Incorporeal name: revenant description: A spooky ghostie. components: - - type: MindContainer - - type: InputMover - - type: MobMover - type: Input context: "ghost" - type: MovementSpeedModifier @@ -43,7 +43,6 @@ damageModifierSet: CorporealSpirit - type: Examiner - type: NoSlip - - type: Actions - type: Eye drawFov: false visMask: @@ -51,8 +50,6 @@ - Ghost - type: ContentEye maxZoom: 1.2, 1.2 - - type: DoAfter - - type: Alerts - type: NameIdentifier group: GenericNumber - type: GhostRole diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml index dae42c3619..36b0843bed 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml @@ -3,6 +3,7 @@ parent: - BaseMob - MobDamageable + - MobPolymorphable - MobAtmosExposed id: BaseSimpleMob suffix: AI diff --git a/Resources/Prototypes/Entities/Mobs/Player/observer.yml b/Resources/Prototypes/Entities/Mobs/Player/observer.yml index a150744651..10f1d1cb81 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/observer.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/observer.yml @@ -1,14 +1,42 @@ - type: entity - parent: BaseMob + id: Incorporeal + save: false + abstract: true + description: Mobs without physical bodies + components: + - type: Sprite + noRot: true + overrideContainerOcclusion: true # Always show up regardless of where they're contained. + drawdepth: Ghosts + - type: CargoSellBlacklist + - type: MovementSpeedModifier + baseSprintSpeed: 12 + baseWalkSpeed: 8 + - type: MovementIgnoreGravity + - type: Physics + bodyType: KinematicController + bodyStatus: InAir + - type: CanMoveInAir + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 15 + mask: + - GhostImpassable + +- type: entity + parent: + - Incorporeal + - BaseMob id: MobObserver name: observer description: Boo! categories: [ HideSpawnMenu ] components: - - type: CargoSellBlacklist - type: Sprite - overrideContainerOcclusion: true # Ghosts always show up regardless of where they're contained. - drawdepth: Ghosts sprite: Mobs/Ghosts/ghost_human.rsi color: "#fff8" layers: @@ -16,15 +44,6 @@ shader: unshaded - type: ContentEye maxZoom: 1.44,1.44 - - type: Fixtures - fixtures: - fix1: - shape: - !type:PhysShapeCircle - radius: 0.35 - density: 15 - mask: - - GhostImpassable - type: Eye drawFov: false visMask: @@ -39,18 +58,10 @@ skipChecks: true - type: Ghost - type: GhostHearing - - type: MovementSpeedModifier - baseSprintSpeed: 12 - baseWalkSpeed: 8 - - type: MovementIgnoreGravity - type: IntrinsicRadioReceiver - type: ActiveRadio receiveAllChannels: true globalReceive: true - - type: Physics - bodyType: KinematicController - bodyStatus: InAir - - type: CanMoveInAir - type: Tag tags: - BypassInteractionRangeChecks diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index c3ccb0330c..cb8304174f 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -1,3 +1,306 @@ +# Be careful with these as they get removed on shutdown too! +- type: entity + id: AiHeld + description: Components added / removed from an entity that gets inserted into an AI core. + noSpawn: true + components: + - type: IntrinsicRadioReceiver + - type: IntrinsicRadioTransmitter + channels: + - Binary + - Common + - Command + - Engineering + - Medical + - Science + - Security + - Service + - Supply + - type: ActiveRadio + receiveAllChannels: true + globalReceive: true + - type: IgnoreUIRange + - type: StationAiHeld + - type: StationAiOverlay + - type: ActionGrant + actions: + - ActionJumpToCore + - ActionShowJobIcons + - ActionSurvCameraLights + - ActionViewLaws + - type: UserInterface + interfaces: + enum.RadarConsoleUiKey.Key: + type: RadarConsoleBoundUserInterface + enum.CrewMonitoringUIKey.Key: + type: CrewMonitoringBoundUserInterface + enum.GeneralStationRecordConsoleKey.Key: + type: GeneralStationRecordConsoleBoundUserInterface + enum.SiliconLawsUiKey.Key: + type: SiliconLawBoundUserInterface + - type: IntrinsicUI + uis: + enum.RadarConsoleUiKey.Key: + toggleAction: ActionAGhostShowRadar + enum.CrewMonitoringUIKey.Key: + toggleAction: ActionAGhostShowCrewMonitoring + enum.GeneralStationRecordConsoleKey.Key: + toggleAction: ActionAGhostShowStationRecords + +# Actions +- type: entity + id: ActionJumpToCore + name: Jump to core + description: Sends your eye back to the core. + components: + - type: InstantAction + itemIconStyle: BigAction + icon: + sprite: Interface/Actions/actions_ai.rsi + state: ai_core + event: !type:JumpToCoreEvent + +- type: entity + id: ActionShowJobIcons + name: Show job icons + description: Shows job icons for crew members. + components: + - type: InstantAction + itemIconStyle: BigAction + icon: + sprite: Interface/Misc/job_icons.rsi + state: Captain + event: !type:ActionComponentChangeEvent + components: + - type: ShowJobIcons + +- type: entity + id: ActionSurvCameraLights + name: Toggle camera lights + description: Enable surveillance camera lights near wherever you're viewing. + components: + - type: InstantAction + itemIconStyle: BigAction + icon: + sprite: Interface/Actions/actions_ai.rsi + state: camera_light + event: !type:RelayedActionComponentChangeEvent + components: + - type: LightOnCollideCollider + - type: FixturesChange + fixtures: + lightTrigger: + shape: + !type:PhysShapeCircle + radius: 0.35 + density: 80 + hard: false + layer: + - GhostImpassable + +# Ai +- type: entity + id: AiHolder + abstract: true + description: Handles AI interactions across holocards + AI cores + components: + - type: ItemSlots + - type: StationAiHolder + slot: + name: station-ai-mind-slot + whitelist: + tags: + - StationAi + - type: ContainerContainer + containers: + station_ai_mind_slot: !type:ContainerSlot + # Load-bearing. + # The issue is verbs check for same transparent container. + # The alternative is you add a bunch of events trying to override it; we don't even really need the container functionality + # anyway it's just a quality of life thing. + showEnts: True + +# Boards +- type: entity + id: AsimovCircuitBoard + parent: BaseElectronics + name: circuit board (Crewsimov) + description: An electronics board containing the Crewsimov lawset. + components: + - type: Sprite + sprite: Objects/Misc/module.rsi + state: std_mod + - type: SiliconLawProvider + laws: Crewsimov + +- type: entity + id: CorporateCircuitBoard + parent: BaseElectronics + name: circuit board (Corporate) + description: An electronics board containing the Corporate lawset. + components: + - type: Sprite + sprite: Objects/Misc/module.rsi + state: std_mod + - type: SiliconLawProvider + laws: Corporate + +- type: entity + id: NTDefaultCircuitBoard + parent: BaseElectronics + name: circuit board (NT Default) + description: An electronics board containing the NT Default lawset. + components: + - type: Sprite + sprite: Objects/Misc/module.rsi + state: std_mod + - type: SiliconLawProvider + laws: NTDefault + +# Items +- type: entity + id: Intellicard + name: Intellicard + description: A storage device for AIs. + parent: + - BaseItem + - AiHolder + suffix: Empty + components: + - type: Sprite + sprite: Objects/Devices/ai_card.rsi + layers: + - state: base + - state: full + map: ["unshaded"] + shader: unshaded + - type: Appearance + - type: GenericVisualizer + visuals: + enum.StationAiVisualState.Key: + unshaded: + Empty: { state: empty } + Occupied: { state: full } + +- type: entity + id: PlayerStationAiEmpty + name: AI Core + description: The latest in Artificial Intelligences. + parent: + - BaseStructure + - AiHolder + suffix: Empty + components: + - type: ContainerComp + proto: AiHeld + container: station_ai_mind_slot + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: ApcPowerReceiver + powerLoad: 1000 + - type: StationAiCore + - type: StationAiVision + - type: InteractionOutline + - type: Sprite + sprite: Mobs/Silicon/station_ai.rsi + layers: + - state: base + - state: ai_empty + map: ["unshaded"] + shader: unshaded + - type: Appearance + - type: GenericVisualizer + visuals: + enum.StationAiVisualState.Key: + unshaded: + Empty: { state: ai_empty } + Occupied: { state: ai } + +# The job-ready version of an AI spawn. +- type: entity + id: PlayerStationAi + parent: PlayerStationAiEmpty + suffix: Job spawn + components: + - type: ContainerSpawnPoint + containerId: station_ai_mind_slot + job: StationAi + - type: Sprite + sprite: Mobs/Silicon/station_ai.rsi + layers: + - state: base + - state: ai + shader: unshaded + +# The actual brain inside the core +- type: entity + id: StationAiBrain + parent: PositronicBrain + noSpawn: true + suffix: DO NOT MAP + components: + - type: Sprite + # Once it's in a core it's pretty much an abstract entity at that point. + visible: false + - type: BlockMovement + blockInteraction: false + - type: SiliconLawProvider + laws: Crewsimov + - type: SiliconLawBound + - type: ActionGrant + actions: + - ActionViewLaws + - type: UserInterface + interfaces: + enum.SiliconLawsUiKey.Key: + type: SiliconLawBoundUserInterface + - type: ComplexInteraction + - type: DoorRemote + - type: Actions + - type: Access + groups: + - AllAccess + - type: Eye + drawFov: false + - type: Examiner + - type: InputMover + - type: Tag + tags: + - HideContextMenu + - StationAi + +# Hologram projection that the AI's eye tracks. +- type: entity + parent: + - Incorporeal + - BaseMob + id: StationAiHolo + name: Hologram + description: A projection of the AI. + noSpawn: true + suffix: DO NOT MAP + components: + - type: Eye + pvsScale: 1.5 + - type: Visibility + layer: 2 + - type: Sprite + sprite: Mobs/Silicon/station_ai.rsi + layers: + - state: default + shader: unshaded + map: ["base"] + +# Borgs - type: entity id: PlayerBorgGeneric parent: BorgChassisGeneric diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index f740b89f42..7bad742f5d 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -3,6 +3,7 @@ parent: - BaseMob - MobDamageable + - MobPolymorphable - MobCombat id: BaseMobSpecies abstract: true diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index c98608fabb..bacc5defc9 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -50,6 +50,13 @@ - type: OwnInteractionVerbs allowedVerbs: [] # TODO: define something here, or don't. +- type: entity + save: false + id: MobPolymorphable + abstract: true + components: + - type: Polymorphable + # Used for mobs that have health and can take damage. - type: entity save: false diff --git a/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml b/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml index 614af2a488..5a3b967f98 100644 --- a/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml +++ b/Resources/Prototypes/Entities/Objects/Fun/Instruments/base_instruments.yml @@ -32,7 +32,7 @@ blockSpectators: true # otherwise they can play client-side music inHandsOnly: false singleUser: true - requireHands: true + requiresComplex: true verbText: verb-instrument-openui key: enum.InstrumentUiKey.Key - type: InteractionOutline diff --git a/Resources/Prototypes/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/Entities/Objects/Misc/paper.yml index fd1e76d638..1490aebcf2 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/paper.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/paper.yml @@ -18,7 +18,7 @@ - type: PaperLabelType - type: ActivatableUI key: enum.PaperUiKey.Key - requireHands: false + requiresComplex: false - type: UserInterface interfaces: enum.PaperUiKey.Key: @@ -752,4 +752,4 @@ types: Blunt: 10 - type: StealTarget - stealGroup: BoxFolderQmClipboard + stealGroup: BoxFolderQmClipboard \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml index ac125d36bd..c8e827b326 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml @@ -80,7 +80,6 @@ map: ["base"] - type: Input context: human - - type: BlockMovement - type: ToggleableGhostRole examineTextMindPresent: positronic-brain-installed examineTextMindSearching: positronic-brain-still-searching @@ -92,6 +91,7 @@ wipeVerbPopup: positronic-brain-wiped-device stopSearchVerbText: positronic-brain-stop-searching-verb-text stopSearchVerbPopup: positronic-brain-stopped-searching + - type: BlockMovement - type: Examiner - type: BorgBrain - type: IntrinsicRadioReceiver diff --git a/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml b/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml index 9c8102979a..bbc948bc02 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml @@ -79,7 +79,7 @@ type: AccessOverriderBoundUserInterface - type: ActivatableUI key: enum.AccessOverriderUiKey.Key - requireHands: true + requiresComplex: true requireActiveHand: false singleUser: true - type: ItemSlots diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml index 97a6d8e076..58a3f6ceca 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/base_structureairlocks.yml @@ -4,6 +4,7 @@ name: airlock description: It opens, it closes, and maybe crushes you. components: + - type: StationAiWhitelist - type: MeleeSound soundGroups: Brute: @@ -104,6 +105,8 @@ - type: SpawnOnOverload - type: UserInterface interfaces: + enum.AiUi.Key: + type: StationAiBoundUserInterface enum.WiresUiKey.Key: type: WiresBoundUserInterface - type: Airtight diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 4e499cc381..19e5c478d2 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -584,6 +584,7 @@ name: communications computer description: A computer used to make station wide announcements via keyboard, set the appropriate alert level, and call the emergency shuttle. components: + - type: StationAiWhitelist - type: Sprite layers: - map: ["computerLayerBody"] @@ -1140,3 +1141,46 @@ access: [["ResearchDirector"]] - type: Lock unlockOnClick: false + +- type: entity + id: StationAiUploadComputer + parent: BaseComputer + name: AI upload console + description: Used to update the laws of the station AI. + components: + - type: Sprite + layers: + - map: [ "computerLayerBody" ] + state: computer + - map: [ "computerLayerKeyboard" ] + state: generic_keyboard + - map: [ "computerLayerScreen" ] + state: aiupload + - map: [ "computerLayerKeys" ] + state: generic_keys + - type: ApcPowerReceiver + powerLoad: 1000 + - type: AccessReader + access: [ [ "ResearchDirector" ] ] + - type: Lock + unlockOnClick: false + - type: SiliconLawUpdater + components: + - type: StationAiHeld + - type: ItemSlotsLock + slots: + - circuit_holder + - type: ItemSlotRequiresPower + - type: ItemSlots + slots: + circuit_holder: + name: circuit-holder + insertSuccessPopup: silicon-laws-updated + whitelist: + components: + - SiliconLawProvider + - Item + - type: ContainerContainer + containers: + circuit_holder: !type:ContainerSlot + board: !type:Container diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml index 0485b5a517..539c8a244a 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml @@ -96,7 +96,7 @@ type: WiresBoundUserInterface - type: ActivatableUI key: enum.HealthAnalyzerUiKey.Key - requireHands: false + requiresComplex: false - type: ActivatableUIRequiresPower - type: PointLight color: "#3a807f" diff --git a/Resources/Prototypes/Entities/Structures/Power/apc.yml b/Resources/Prototypes/Entities/Structures/Power/apc.yml index 9412d45447..1022c57b3f 100644 --- a/Resources/Prototypes/Entities/Structures/Power/apc.yml +++ b/Resources/Prototypes/Entities/Structures/Power/apc.yml @@ -6,6 +6,7 @@ placement: mode: SnapgridCenter components: + - type: StationAiWhitelist - type: AmbientOnPowered - type: AmbientSound volume: -9 diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml index 9a244c2c59..6345cfef3f 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/intercom.yml @@ -4,6 +4,7 @@ description: An intercom. For when the station just needs to know something. abstract: true components: + - type: StationAiWhitelist - type: WallMount - type: ApcPowerReceiver - type: Electrified diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml index 954caf67ae..c82a3f2108 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml @@ -4,6 +4,28 @@ name: camera description: A surveillance camera. It's watching you. Kinda. components: + - type: Physics + bodyType: Static + - type: Fixtures + fixtures: + # This exists for examine. + fix1: + shape: + !type:PhysShapeCircle + radius: 0.25 + light: + shape: + !type:PhysShapeCircle + radius: 5 + hard: false + mask: + - GhostImpassable + - type: LightOnCollide + - type: PointLight + enabled: false + radius: 5 + - type: SlimPoweredLight + enabled: false - type: StationAiVision - type: Clickable - type: InteractionOutline @@ -43,6 +65,8 @@ InUse: camera_in_use - type: UserInterface interfaces: + enum.AiUi.Key: + type: StationAiBoundUserInterface enum.SurveillanceCameraSetupUiKey.Camera: type: SurveillanceCameraSetupBoundUi enum.WiresUiKey.Key: diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml index 8e6b0863d6..f08451ad4d 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml @@ -7,6 +7,7 @@ snap: - Wallmount components: + - type: StationAiWhitelist - type: Transform anchored: true - type: WallMount diff --git a/Resources/Prototypes/Roles/Jobs/Science/borg.yml b/Resources/Prototypes/Roles/Jobs/Science/borg.yml index 456a761dba..b9b4cb3c16 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/borg.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/borg.yml @@ -1,3 +1,18 @@ +# No idea why it's in sci but we ball. +- type: job + id: StationAi + name: job-name-station-ai + description: job-description-station-ai + playTimeTracker: JobStationAi + requirements: + - !type:RoleTimeRequirement + role: JobBorg + time: 18000 # 5 hrs + canBeAntag: false + icon: JobIconStationAi + supervisors: job-supervisors-rd + jobEntity: StationAiBrain + - type: job id: Borg name: job-name-borg diff --git a/Resources/Prototypes/Roles/Jobs/departments.yml b/Resources/Prototypes/Roles/Jobs/departments.yml index 9dca079c50..bde2d977cf 100644 --- a/Resources/Prototypes/Roles/Jobs/departments.yml +++ b/Resources/Prototypes/Roles/Jobs/departments.yml @@ -15,7 +15,6 @@ weight: -10 roles: - Bartender - - Borg - Botanist - Boxer # - Chaplain # DeltaV - Move Chaplain into Epistemics @@ -116,6 +115,15 @@ - Librarian - Roboticist +- type: department + id: Silicon + name: department-Silicon + description: department-Silicon-description + color: "#D381C9" + roles: + - Borg + - StationAi + - type: department id: Specific description: department-Specific-description diff --git a/Resources/Prototypes/Roles/play_time_trackers.yml b/Resources/Prototypes/Roles/play_time_trackers.yml index fd99b2c228..9c87cde979 100644 --- a/Resources/Prototypes/Roles/play_time_trackers.yml +++ b/Resources/Prototypes/Roles/play_time_trackers.yml @@ -151,6 +151,9 @@ - type: playTimeTracker id: JobServiceWorker +- type: playTimeTracker + id: JobStationAi + - type: playTimeTracker id: JobStationEngineer diff --git a/Resources/Prototypes/StatusIcon/job.yml b/Resources/Prototypes/StatusIcon/job.yml index 17c9cee804..bcd8021c3e 100644 --- a/Resources/Prototypes/StatusIcon/job.yml +++ b/Resources/Prototypes/StatusIcon/job.yml @@ -29,6 +29,14 @@ state: Borg jobName: job-name-borg +- type: jobIcon + parent: JobIcon + id: JobIconStationAi + icon: + sprite: /Textures/Interface/Misc/job_icons.rsi + state: StationAi + jobName: job-name-station-ai + - type: jobIcon parent: JobIcon id: JobIconBotanist diff --git a/Resources/Prototypes/Wires/layouts.yml b/Resources/Prototypes/Wires/layouts.yml index 9fd6bddc37..20d5d33603 100644 --- a/Resources/Prototypes/Wires/layouts.yml +++ b/Resources/Prototypes/Wires/layouts.yml @@ -9,6 +9,7 @@ - !type:DoorBoltLightWireAction - !type:DoorTimingWireAction - !type:DoorSafetyWireAction + - !type:AiInteractWireAction - type: wireLayout parent: Airlock @@ -96,9 +97,10 @@ - type: wireLayout id: SurveillanceCamera - dummyWires: 4 + dummyWires: 2 wires: - !type:PowerWireAction + - !type:AiVisionWireAction - type: wireLayout id: CryoPod diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 8f64a13940..48358ca496 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -1248,6 +1248,9 @@ - type: Tag id: StringInstrument +- type: Tag + id: StationAi + - type: Tag id: StationMapElectronics diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/ai_core.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/ai_core.png new file mode 100644 index 0000000000000000000000000000000000000000..8dd3031f9fc0b41e613e0f27050665c312010aa9 GIT binary patch literal 269 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvl>na**8>L*7#kY{c?^7#3{tub z-qRQi5*Tc&7@X@FGA}c%5MZe1t2(6v(#BX4<QL4~@a#q!kTc!W#W6%<Vse54j|A() z28IBK08UO0Cf1goi495~2Us~9SbLlwGOSYCE5mhaftFF4lcRKq>#;SV2^%)?u2R}u zZ7%U<23zRFn_IIYqoS^@61^;-b;-&3azMzEwzjr~D?EI8xs?|v>GJxzMr29UMH}2{ zd>CZVu=>Od-oTB*@1l2nU#KO($P(Jf;k1IwX<?sWphTc22Lpp5bB8Nq*+DIkk33!d KT-G@yGywqSrBM$6 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/camera_light.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/camera_light.png new file mode 100644 index 0000000000000000000000000000000000000000..041b9b9bf7c8b9cca92a5cb784ab672663a101cf GIT binary patch literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv^#Gp`*8>L*7#kY{dH?_aXW)}$ zkkVx^NMLZTXRxhe2wKXJd6}V}k70$t^CE96pd!YSAirP+hi5m^fSf&^E{-7@6O$7Z zcqCXSHZTM@1aPuUIk;q!LV&{wR!)s37ANP+LCy;qbRIkki<{Q8NNMrjJvyS1u2rtq zKD<7ztUkOE3al%bRG7jiO0fxLdN;2=F=r!>M$ooX(i{hvBHzxvd`bJ#G^LdVg@xYd zqnZquRYa$?7O<b0s^{K!;13hi#f9$9&iz`8ethFu)iq<rg&7heqIPz68CppfLUzn? z+n)M8%p`hGY(S`j6OZwgz)%MzFHXTgi9k;d1_t@(1&3KqCN%>6!QkoY=d#Wzp$PyH C>tw$G literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/crew_monitor.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/crew_monitor.png new file mode 100644 index 0000000000000000000000000000000000000000..78fad17a76c58ceb3ff7bdc60107aaf01f477511 GIT binary patch literal 295 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv-2k5u*8>L*7#kY{c?^7#3{tub z^?VEl2@ER)7@X@Fin!ewG6EP%!x`!s%+eT);<gul+5}X|SQ6wH%;50sMjDW_!qdeu zL}Oxdf&z~O>%<0z0EYlhP7Ws4mY#_XN*)JTIW?R@TH4yAq-QTZ7`mfv`t-IPJGLGS zoiT0tbav_4TNOj5OlxmnvUKaxr3$N0ym;}#qir>ZYq(%n8rv>iiFXG|R&Lz5bmPZW zM^9*_`4xVQYg%*Y%8efeU0mVf@*&1Pz5I*Y+E??>(p}cxn>#VIVoO=c7dywW!YdM? otQW*gj6bwlxw#~|s46lrY*UY|zO-x71(07oUHx3vIVCg!0LmC=oB#j- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/manifest.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/manifest.png new file mode 100644 index 0000000000000000000000000000000000000000..08514aa90829c59d8d3995ba7f1a5f0ad5c24bd2 GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvtpJ}8*8>L*7#kY{c?^7#3{tub z1_=xaoDB7R49@io?Ys;H+zcxO7)&x4Y^x@eT{8nJV=M{s3ubV5b|VeQsq}Pl4AGdF zoS?uX!8);lA;2MklaqsqwWVibgObMqR!)r|U)PL`jEKrgtqDyl3f{hWkul$LGm}Sa z1FMf9)4>4IP|*ynu=Ou?R&Lw1NOn8d0?Fd@VL9s?Rv*}~boZ`r=jPsiYF)6}p}3v< m(8~}WekUyhB`#$xPDX~VR|VG<ZTkyBF86fxb6Mw<&;$S|#7l4h literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json b/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json new file mode 100644 index 0000000000..a7c00f7793 --- /dev/null +++ b/Resources/Textures/Interface/Actions/actions_ai.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/blob/c473a8bcc28fbd80827dfca5660d81ca6e833e2c/icons/hud/screen_ai.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "ai_core" + }, + { + "name": "camera_light" + }, + { + "name": "crew_monitor" + }, + { + "name": "manifest" + }, + { + "name": "state_laws" + } + ] +} diff --git a/Resources/Textures/Interface/Actions/actions_ai.rsi/state_laws.png b/Resources/Textures/Interface/Actions/actions_ai.rsi/state_laws.png new file mode 100644 index 0000000000000000000000000000000000000000..e30e891745fb526dd73b5f597eec1a457f4b1aaa GIT binary patch literal 241 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfvxd5LK*8>L*7#kY{c?^7#3{tub z1_=!Hd<@R@47ODaD+Ih}3GV|+F_r}R1v5B2yO9RuG<v!?hG<M&dhsCV0R<k`3uVo> zc;9e29;_CzYIva3R9#VYJBwe{?Sk|FgNL6h9?Q9HwOhe5OHofKuv^$8MSH{1ed?$D zD;RuRdtb<iDrWh%`d=(nQc+_PpU2JP)6jK+`%A}MhNI~R>!u}jolMn|I`RMAi9i1D j9cS27zTB`Paq4Ot3*m6NwGaI|K(6+5^>bP0l+XkK<p@&q literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/StationAi.png b/Resources/Textures/Interface/Misc/job_icons.rsi/StationAi.png new file mode 100644 index 0000000000000000000000000000000000000000..aba35c034b9f1508e953a030300987dcf1265f1e GIT binary patch literal 204 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85qP=L734qNaX`iFwxV+F+?Lc`42yfUun1Gg@gN+MQ|M6Sa$LM zi<i|0B-oza`SDv`g^x)`AeD#5+h~qtQrer=PXY~`E(+dFH~RYe63jRb_%bl&ENk$c vQT<td2J>P^uEd4~`UkiU=&WgIWMue|ZFBN9!-0c9%NRUe{an^LB{Ts5`?Wy_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json b/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json index 745cc43b84..117a94e790 100644 --- a/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json +++ b/Resources/Textures/Interface/Misc/job_icons.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi | Brigmedic icon made by PuroSlavKing (Github) | Zombie icon made by RamZ | Zookeper by netwy (discort) | Rev and Head Rev icon taken from https://tgstation13.org/wiki/HUD and edited by coolmankid12345 (Discord) | Mindshield icon taken from https://github.com/tgstation/tgstation/blob/master/icons/mob/huds/hud.dmi", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi | Brigmedic icon made by PuroSlavKing (Github) | Zombie icon made by RamZ | Zookeper by netwy (discort) | Rev and Head Rev icon taken from https://tgstation13.org/wiki/HUD and edited by coolmankid12345 (Discord) | Mindshield icon taken from https://github.com/tgstation/tgstation/blob/ce6beb8a4d61235d9a597a7126c407160ed674ea/icons/mob/huds/hud.dmi | Admin recolored from MedicalIntern by TsjipTsjip", "size": { "x": 8, @@ -177,6 +177,9 @@ [1.0,1.0] ] }, + { + "name": "StationAi" + }, { "name": "Syndicate" }, diff --git a/Resources/Textures/Interface/noise.rsi/meta.json b/Resources/Textures/Interface/noise.rsi/meta.json new file mode 100644 index 0000000000..068ecab968 --- /dev/null +++ b/Resources/Textures/Interface/noise.rsi/meta.json @@ -0,0 +1,58 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/4b4e9dff1d7d891cfb75d25ca5bf5172d1c02be6/icons/hud/screen_gen.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "noise", + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 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/Interface/noise.rsi/noise.png b/Resources/Textures/Interface/noise.rsi/noise.png new file mode 100644 index 0000000000000000000000000000000000000000..ba74952b409b6d3673c9949e6a81c0a84ecd472c GIT binary patch literal 45602 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 F{}(0Gc*g($ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..9b72b39492803a112ab679408d6b450bf93c7b8f GIT binary patch literal 820 zcmeAS@N?(olHy`uVBq!ia0vp^3qY8I4M=vMPuFE&VEXOp;uumf=k1-qzS#yMtO3hj z?YdE37a3`<<tE6~eDFg~abL(*#kVo(#~g3)mP-INg20Qj=P&NJ@V)$g{_(;+sRpLc z-K&4UW{1cj$ZNirzx-ak?T`AgDYO6At~v6f>g@l?#vLz$=4}0wIjgNuMt)|?&Yw$< zUrDaJ#?oW;`|VSE({*?LT*~i}`#js8d0F4}3Gqcaw$J3}HGCGIRX;!dTk!R^=%3!@ zz1rsWXP<wb$j|Uqd+*dBqy1M@{jMpsJiof@vjNZl_3Z2QihK^7vfX;TQf>c{9X<!x zzU=v%%B1G<|NG-v%eU{}_G$m|fWtcw4rO3i5wQMm$*1Jc`rDJY-t$+tH{0)FpKuy1 z)cu8d+Z~Y5|04SbK_F#cnVD=}_}Issj1&0z_xbxc=6NsMwRirSbie%HI~JSU{Nb-= zE9`&&$9eSw<J0den>Vri{QE5ZJG0(;?q|nOKdwIgb@P+`*79AyI^_;Xo-V&}S#(?W z&+PYKb|3rt?N34U@_$mF=1chd|Gas<Ve5*WwsHRo&-xum?65z2djGut-_O|pn)mbK z{g%kw<W;#g{z&28(6Ux~;`8T0$GvI_0_*1goPK*_(07(8|BkHt^Y7DUwS;%Pr*ywu zKG}bEe+`?D?EPxJ=|FLQ{{t`TEPlQ}$6hh>N>EjH<i4#T)Aj4mzB5gD)&2DX$lvwl zOy9s^^8bR?j^}k&KR5ooc~$20>!zO{KUF}2ZQ+XGzYV7)|7`=x9{k%7z5L$R<DZ@` zPl;zfSM_rFWck_kRZQoaJ5=`n{R32}`=hSh?f(L$J%7J4LnFcA=(aqUdGozxwLcy_ z`l9At;Z9lYQh$e+@--!PPg8vl+>xFV{bITDwWr1juYTMs-nr<r=KBvUJ@4AP&#^bK zFWqLeZJX=1ZC+7lp4a_i<XkF0AplGXROtV0*s^?iomJ9Hc9ki04o-Kk@voTA2ekFU ZZTr2iwC2ogy}lIW22WQ%mvv4FO#mbvqW%B? literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned.png new file mode 100644 index 0000000000000000000000000000000000000000..c583bb88f21bd6e5250ced9332d3f2d4eed2945e GIT binary patch literal 1589 zcmah~do+}37$3$MO}VX6N@J`fZObUKHkl!pNQ`J>!+xX&5s69Ko%uxW#kPe?ve}r_ z$>f@@Uz(zl#*&7_U=)+lxXc*Mb-%HHw5NaeocH~`&-0%1d!FZgF6Ye)@ZYYBS%pC$ z5V}6z9)a-rr+VvX!&tzMwm~3Javu-3-N!CXU2;hnjmLTsa<i?@TI3h+dx+b=^cUj- z*9_e%@$&l2#C+>o?~CUyYxYOUb7r_v$?P*-mR2M8?W~xG1P%F>)*BIq8|<S{S}RTM z8=MN)&W?{qvxEbX5A<0mNtB5fobnbXQ|eVJ`JIq#E`t=R0lE<1a`+C<5!WwU(<_vI zgE-8zY%Vc>DUn3lZmjLrb&Ny@l++n?^T5<KWQ#Ve#G`%#K|IZX&-)O*K$HDIp%4fJ z3T0j-a>#cgkZEZv=#aAMLVtUE&Xh#fP4L|1@FHSfrg(Y}0RdpFsU6uQ`!C%}-31{R z+SFhPMb9b}hbR;hiL~7FOjcwJrBs-m(r<GW%)pzO)gR!S1)G9^RGOybxu`4)d1Hs2 zEZqz&2c{hR6K>~~lh&9CN{9&tTF*xgdyhK3`ot1)xK1eKDL)=|t7UjKLM{;kW#vU3 zf}{7`vix8ycwi@|D|WV;XRS9O1=-OddUeF_kFlqKkKFSLXXg>Im>Lj^t06qF(tzW2 zM3L7rlSd?c<~!}xQnv7BQ69GlYSb+J7J6SdBbzO)U2^AW=cB%1$$Kb7qqr0`D&e#D zJYx35b}v19qhv%f4r`;uvq70$Mun51cfU3BN5pW0+T*go3FIh4KbOm_Aez%_z5<Af zx67{-yeTHq?plEZ8~ylff-lqt%riS*J!oz;<aksB_}A%|k~TCKk#r}*LYEjT<(uTa z$}(xXJ1w5mL=GN@s9`~$CzVmtsRE>4F(%G8R29No^_UwSZzhjxnDew~=hdY|*O7xa zGgJE=x0)5L2y(@Py}~e44p#Zy(9qCy@IttRKiz}YY7g)_Uk9Ckh8wkh5}*u~=;j*P zye73RVYz>1*$G6|=KhW5AHrVX_l50jwV!9xFb@xG-PwwhyZ<KdYX>|lSb)29E~<qZ zmIm_UO%~c;qle1me9lvFd{nr_wmL#C(wLEnd(ck^{{u$rG}ntX+%Sx&6Ii%#+wcY? znk!sTnD6^FWQsIO-!WKBvE&od_5nFZr6*1I{nRr8WfF(~kfkg%DTio}u5)XmYH3^4 z0XU139hZ5>e!nJ#&#LMuNLKBJ?>lCJ(?**+`BYj<oKIaHOe^I<TTrP}EZyEF7N}BM z>SPE?xH_jRtp{>ng5hFXQre7C?iF*~R|q#FNX6I&zB4961Y35ZGj?SIIR{Lmb8<AM z;}it~q~lb1jr=E}NZvBdk)3ragx9)X(N`ae9)>D*VS5wet|_RzR$z)fpHcdTG1*X> z0}_=lSa!brp21l+XJwkymoawx3<rlww9l10x(>2au5_Mn6Ww0PKBv6fetBRH=ub0^ z=w9C^xIb9|*&0J+b-n`s14$O@EM7hA<=`EO(T0;d1D8R_A>$38*IxkGvW$0=oC8d< z_qa0QwCq*AeVJOJr?yk5IE{arHqkS}k#Z<f;%YjV&ROBbW9aJ?UeV80_cf%v5p*ku zh@;=+Qvc<=Qq<}rhk*$Et%JgA2mOE@6?&@2()7P^V*WItQU4f0?E-usX+mBU7{rL+ z4}A>N4nWek6_lngM32Ff1AP1f>-@FgdCEA~9chwyGO{JGsMUUF>o^DD(S_DpY#5sB z;xpB1RZrc0G2hj<S@ZZ*&xw+z3qm3;=I64{pJ1hJQwAJp%oTl|BVmAvOLrW`29{yd zM;uRKrRnBbMDu4!gA7weZ<ZJAZ?!nmpb3An3%#EZQaDK*e8#j1J3%z}R&%^2k+xN2 z^xg?%y^#gXTfBrh*inyo<c=gRdhDX14X82jO+<NaWcLC1J3#n&`g_!Fi^%u~A%`&C literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..28bf165122ea7c29a4bed6f6df7c1c37efc18a2e GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3HE3&8=$zQYM}*jv*Cu-k#gY$)F&>e4u=z zyxruRk+(S(m4|jY1hFzO{K<GDHhImRyYA=i?BNIMVCbKrx{i(Mo@~AWi0A3*=d#Wz Gp$PyfVIac* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-banned_dead.png new file mode 100644 index 0000000000000000000000000000000000000000..7c5b468885aa71a7856e9142bb89a348e7eae7c5 GIT binary patch literal 463 zcmV;=0WkiFP)<h;3K|Lk000e1NJLTq002M$001Be1^@s6qMd$(0004&Nkl<Zc%1E- zJ&uDg5QSe5jT=INVo_vokSlO14uRxb`3kwgrigH5Xi_>wWG{aq$u9P+1W%%14E}~O zW6R^6l+s|u+5uQ~0Ac`8N|C@1hXbhjvAy{^9<fi;AmB!8d|xg-8x`LLV4tRFv<~9H z)Q3{><7W*NqTt6vLtWQift>T%_R#!j_yA+fs{^kTn(qQ|KA*iiVf*c}sQEDf<arJN z$g<1}xC^(uE{l>M0l@WoMO9U!m!y<loT;j+Hwu*e(HziKQ54>sct;`lI{>CTGq!CD z0Jz<5kW!*33IP4lu(nTRf59Y4oXyVo@9_VsIlfU!86kw>oEyfNVT>6(_6UnzwqMS< z5keTHlxw5p&!)f{G@AlT>NhD6f*%2ob+IhVMS*iF%d)o$Q1Z7Fz?K4A3anbIPk|Wt z*&Urur$wR9WO=qGNXd685DNZ`Ma7?-1D5{%KJ7Cke-r>8@K5><%^%eSU)Ah<D>T0s z0LrpB72gG*(K_hC#FUMK?*cG9sR=DA{&qM@S@Zi3x&w%1QN~tqHsk;R002ovPDHLk FV1n-x%*g-% literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-empty-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-empty-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..6539176b8487d93162dd7df57f3528eca69df99d GIT binary patch literal 109 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3HE3&8=$zQYM}*jv*Cu-k#gY$)F&>e4zZr zd?oMbXfClu<*^DALKqns?4_)qs(8P9w>UlTK08oH1G~>>eKw}0-xuEo@)$f_{an^L HB{Ts5#}6My literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-empty.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-empty.png new file mode 100644 index 0000000000000000000000000000000000000000..eda1f4bfb568d5b1ea19f15f24d9242cb27defa7 GIT binary patch literal 451 zcmV;!0X+VRP)<h;3K|Lk000e1NJLTq002M$001Be1^@s6qMd$(0004sNkl<Zc%1E- zJ#xb!5QU$N8X8-bshk`5cJe9qR=R?25LcqYZd{VW1F`<&2-r-dH^Yns8ot#I4<yK{ zs<K$IG61ViKn?&}YZmzNcmy{;cQ+rW6And@1bj0FpVw=vj*IUTa3~7gj7bu}(2H{M z^RWgFaq#nQMbk7<fl8^#_SF1c_W>zo)Bxs`n(q@(*L5!lA_@v~{l2pP0^bTDtPsK~ zrL2_FN+~U#-v^e6{PmLJ`c+DKmTP0?!)mQ95e)_qDu~wrh{$TKeH$mgHwSb@M7Ufo zbB{$+Y<>p7zRryI`yBxAcszRWt|#9K%=vG>-}^c@<NuVuL{t3PAsC`8u|Q1ztOfqN zCM^(Bzp+3H{tkGqi)C2`1x~3f%hp=V$=@u1%>tVRR;|@rAP2txj?U+EQ0SN(&+Q3v z^1TI8!N*u!{J}XO^zeJP&y@UL0%E}5={q&Qw<kEaXBS(k`K<(SZi{pAeFARABt4iI ts^j4M1awboQj3ef9gbF2#T^5yz5rC2*?>5Oyi))G002ovPDHLkV1j~s%9a2C literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..63616e70b52120b670f04f00c276c707f979efe9 GIT binary patch literal 2483 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 P#2Z_h+mXsmy%PTd$R=LN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-holo-old.png new file mode 100644 index 0000000000000000000000000000000000000000..3dc7a301d865c65907c7c3fa0cde98ce717cfff6 GIT binary patch literal 7280 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�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 IluN|_0b@DVZ2$lO literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..f3ba4b591abcf9615ea0cee2d2ef3a5d1e7c42a8 GIT binary patch literal 6255 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 MrlCf;x^2||0(0FdR{#J2 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai.png new file mode 100644 index 0000000000000000000000000000000000000000..3c81e3a751ec86844a1fb383a91185e12b041561 GIT binary patch literal 6761 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai_dead-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai_dead-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..96e953660f8eaecc60da577108813a81695c3102 GIT binary patch literal 453 zcmV;$0XqJPP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80004uNkl<Zc%03b zJ#K?A7>1ujNNuMuM7Rv8xB%yAu8^IjQ<bSm9H3Vq*We0VAZmsbPh@I4xdVy$Pg^C1 zzLI5Q1J7%JA3vTXNun506JS&W7J!s;6u9F!g#mzv#e$h_4+1{qId8iiKxtrR+Z^(I z5CheTTGfHiucu)Q@Y-#l(6eFux^^^ejeCaq9a2(CC8bnC2!*GLf`Q^GC4|sCKv@8; z>jE&J&&^HKE1RzCYSt8Bzuyyvp*eU%VHlF8X=z|gO#mf?&>h*`d(qhbd~qCabbGmc zJb$uRRN~{lWMvs}Ou{IN%qF2%CSepsItfP)0Tdz7rMUue9P7HT-F0qrthxf4b^H?l z{}9CSrj~Hk<8%Ghu|8e&6#PkEQ8k}}DL|U01VM0RV!whQxco)b48d=fyGx^ozykDg z_`ZK-YPU{2$8pLK_`Xk;t&GF7Y*m|RcMfa~kbCagHedpN=H4CugzI*fLDdZ0-n<Ni vwc7>`c|PdIq-Ef=QyE(0Z%&gW8I#X9TE>0xyqkY{00000NkvXXu0mjfY`w_) literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/ai_dead.png b/Resources/Textures/Mobs/Silicon/output.rsi/ai_dead.png new file mode 100644 index 0000000000000000000000000000000000000000..96e953660f8eaecc60da577108813a81695c3102 GIT binary patch literal 453 zcmV;$0XqJPP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80004uNkl<Zc%03b zJ#K?A7>1ujNNuMuM7Rv8xB%yAu8^IjQ<bSm9H3Vq*We0VAZmsbPh@I4xdVy$Pg^C1 zzLI5Q1J7%JA3vTXNun506JS&W7J!s;6u9F!g#mzv#e$h_4+1{qId8iiKxtrR+Z^(I z5CheTTGfHiucu)Q@Y-#l(6eFux^^^ejeCaq9a2(CC8bnC2!*GLf`Q^GC4|sCKv@8; z>jE&J&&^HKE1RzCYSt8Bzuyyvp*eU%VHlF8X=z|gO#mf?&>h*`d(qhbd~qCabbGmc zJb$uRRN~{lWMvs}Ou{IN%qF2%CSepsItfP)0Tdz7rMUue9P7HT-F0qrthxf4b^H?l z{}9CSrj~Hk<8%Ghu|8e&6#PkEQ8k}}DL|U01VM0RV!whQxco)b48d=fyGx^ozykDg z_`ZK-YPU{2$8pLK_`Xk;t&GF7Y*m|RcMfa~kbCagHedpN=H4CugzI*fLDdZ0-n<Ni vwc7>`c|PdIq-Ef=QyE(0Z%&gW8I#X9TE>0xyqkY{00000NkvXXu0mjfY`w_) literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/default-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/default-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..f14b4ef0fa6b3eecdf2106102180f93fc8b3f4c2 GIT binary patch literal 2005 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/default.png b/Resources/Textures/Mobs/Silicon/output.rsi/default.png new file mode 100644 index 0000000000000000000000000000000000000000..f14b4ef0fa6b3eecdf2106102180f93fc8b3f4c2 GIT binary patch literal 2005 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/floating_face-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/floating_face-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..05de742794341c5e5489daa52bef423933d51512 GIT binary patch literal 721 zcmV;?0xtcDP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0007*Nkl<Zc%1E; zPiPZC7{y;|6^w$`BnUl<(c;D8MPeg8iTICzv^P;HN)3pfgiudCSb7x#H7G^JV-Z9& zgm@`-Q+jBL7l}>lu~bT`J!B6mjtAY%u7W$0>~vFnzti%~?7m^<o7pef2M|IC`F{zi z-jK{1!mH2B%|7Ml2BqJrD&Ch!cz4ViLdPWN(3c)$GHZxaXHLj=M=@XWH4}8{D`kNB z#WVY~qC;PL0wVEVg!eWkbbANz?Z?0S`-v`nsT55W>f-pJFgDiZR$yBQ*xePv+@gsW zr+2ly4Rq>DXTaQ|2>|#w^V*ILTph5lryft)*9Vekfi>AD2dm%V)ge`=3jjEFFbn`# zt<2-io%2|&%%hku0r+*`IOER#*8R<*Ti-K1Q-!*)9%hL};=Q&&F<*jpIxj4ls8!2w zp4JOXt!)F{`kq^$RxQJfHr~C~d)Rp*Th~3&oj^3<YO7VtUJ`-oOPvAE)7Pur>vV1g zo%&L#namoZ|7Z`shr3+y$?*{!?!SN!(=RcU8FRIDnx8TKuHs8Eq*GsdaChI<1OV*l z-sg%xyZ-=}Zr^alzOJkT0PdZP_*Mz((pM^lrRwnC7_+m!m%=*ql}h11_hK<$!p5f+ z?AhN9l{&pybm%LskaHEDKgr?9xyyFj`&UnK@yaj&|5v|V^Z@{|bWV&e3o*JZL~Jl4 zVuKkGzctZ_rE^+;G5M|E)GWo)IWctY`d{(MM-Q<!F|6&+kap3x2Vu?nW~{s}{g!6S zDY|Jh-$wd`5JCtcgpjQZ{Q;`t^an^ne}D$)51>DQ{s8&|=ntSjfc^kq_300g9R&RW z^as!%5cv1`p!EUJA7BrH{s8&|g8x1rv_2t(kgd*7uk1M<qWT#i00000NkvXXu0mjf D+wWaa literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/floating_face.png b/Resources/Textures/Mobs/Silicon/output.rsi/floating_face.png new file mode 100644 index 0000000000000000000000000000000000000000..05de742794341c5e5489daa52bef423933d51512 GIT binary patch literal 721 zcmV;?0xtcDP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0007*Nkl<Zc%1E; zPiPZC7{y;|6^w$`BnUl<(c;D8MPeg8iTICzv^P;HN)3pfgiudCSb7x#H7G^JV-Z9& zgm@`-Q+jBL7l}>lu~bT`J!B6mjtAY%u7W$0>~vFnzti%~?7m^<o7pef2M|IC`F{zi z-jK{1!mH2B%|7Ml2BqJrD&Ch!cz4ViLdPWN(3c)$GHZxaXHLj=M=@XWH4}8{D`kNB z#WVY~qC;PL0wVEVg!eWkbbANz?Z?0S`-v`nsT55W>f-pJFgDiZR$yBQ*xePv+@gsW zr+2ly4Rq>DXTaQ|2>|#w^V*ILTph5lryft)*9Vekfi>AD2dm%V)ge`=3jjEFFbn`# zt<2-io%2|&%%hku0r+*`IOER#*8R<*Ti-K1Q-!*)9%hL};=Q&&F<*jpIxj4ls8!2w zp4JOXt!)F{`kq^$RxQJfHr~C~d)Rp*Th~3&oj^3<YO7VtUJ`-oOPvAE)7Pur>vV1g zo%&L#namoZ|7Z`shr3+y$?*{!?!SN!(=RcU8FRIDnx8TKuHs8Eq*GsdaChI<1OV*l z-sg%xyZ-=}Zr^alzOJkT0PdZP_*Mz((pM^lrRwnC7_+m!m%=*ql}h11_hK<$!p5f+ z?AhN9l{&pybm%LskaHEDKgr?9xyyFj`&UnK@yaj&|5v|V^Z@{|bWV&e3o*JZL~Jl4 zVuKkGzctZ_rE^+;G5M|E)GWo)IWctY`d{(MM-Q<!F|6&+kap3x2Vu?nW~{s}{g!6S zDY|Jh-$wd`5JCtcgpjQZ{Q;`t^an^ne}D$)51>DQ{s8&|=ntSjfc^kq_300g9R&RW z^as!%5cv1`p!EUJA7BrH{s8&|g8x1rv_2t(kgd*7uk1M<qWT#i00000NkvXXu0mjf D+wWaa literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/horror-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/horror-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..10efd5ee1dbc3a2dc73ab315c035373f285779ec GIT binary patch literal 1777 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/horror.png b/Resources/Textures/Mobs/Silicon/output.rsi/horror.png new file mode 100644 index 0000000000000000000000000000000000000000..0a807c0c2354086be2730017fb4e4a7eb95afdd4 GIT binary patch literal 1404 zcmai!e>l?#9LK-y80+ka9x^|+GHuC2$u(+>{AxWyQPEag57H$jg}M8NSy$w)iI6qX zg}VH_{8)>=l>8>)<T|I>a7N9{!WMF?r@QO^>Hhe9KF{;|<NbbrK7V{t{d~NTt5K@~ z06>y=yYE-er1zx_SNA68W1Rq?=|*;U4Pf%5yoPX-Fw>TN|2@_oBkM7)H?M2yPU+bf zG7%R>3{9hURO7Fv*!1bma7m9=SdHv^18c9xY8O3yrr&n(?)pr#wsrWc0hnZu5XS=Q z#s0~ci<UdXJZyUvM7s6x;;6z&87=!R?yYkYHuQi18ZDPTjBZ{zs}clhTAbr@xq#^P z;W*Z%SIfTs^*y0>0ZW`7j%AW#!JrI%kdo&K1_G0Fi*Se1lO_F<xy6e{NY5pe;!H0) zjB%83D2_t~@R#8n8nJ%J=gawq{DhWHt(spm<K;7vbq**ZJZWIqV&Qf7tKt_>?SfV9 zg;Wv^@{fo?F)FCx4d6`CC?SB7*Fs8MQAyh?%60-<hz}!mHm{Pv@HeT##ty=Qd~g|! zYpq|oG5ftaKv&g8fbs!Pg=)UrD`9AA`U=Gq$ptWw6EymT@TlN993Z8GqBDfA2w7`g zs0n&a{fTC19iX|FuAi=)S2_Yr=$vBTY5Nm~UhLxnXqy15IkSIE*Pwt8kMUp3#rc)X z?mM@3dCH}7T#gxS;Nb1kORcsMfWx9Rb(MDp!betBU4sDbfXnA3PcpIZK-DiAfYo&0 z^Nqa6b^x6@V-N2fj0|R0qET5D!zK$zE%kr{_iy7y4R-j@FEP^40wguA`_y5G>n4C$ zGjIFy8awCEm9+~YrjmqGnwpK5Z2alGLKfVdj}x9KyZ=t(Xfc43z4U_dVt*!2kM@Xq zCbx<gO2tDWjzMD=(Kbc&$oYcS;uTDNEhv3CmcmDD<!A;mXQ!7#ZbVV+4VwAQamm1A zHoN9=XLeE9UW>Hi&3_PxzIp&aG!WXYz--x<%@TJp2$M+Ybolrdnq)mqB5Enia!V9P z2^HIkMpu;ROc^uxSh>mYRaURg6iU~)-1l-W3)i#eNb3?VP&_m;0h3g-_Qm{I+N3-) zn|Y_Q?@vRap)e5LwRd-Q@Yz9BcWrerS#ix+=?!)+Ju`2toA9n1z*~A>D+~_%-PGJ7 zNK6^^Li@NkcqOLzshpTvQhsg__f5jLC^wJ-;v|8)qR5vzWWmP^;!DNFe9Lc!_d@4W zNsI)W1Yd5UHGf-wi_%&l%~oz#=Eu{Do${S^^}C_8jxMHLI=ii%mblcT$W~U3(8OV) zDrQHFaVjY-D8L94niY@G&*w0VY~KB#JPd0q(n!Z{x@CBi$jz&;Orl!p;wDOnJYL0Y zR5aAl)UNtGn41kz+Z{0Xd3q?`!I3sD8UtUAw~L3yONdl3N$_6uUm+t>iQ%ok(G;w( z^xlyh*3kIs@n$^d_(V7YlcVpFfhA`Y>OSAF4qK_HL11%qHngeiQZN&pm}H(UTRR&1 zEMN>5xPWz_v3t5ARL}Kkj;n^F@PR8!8LQUNL({kKrhh8*q~~t)mXE070kEm$l*!r@ z#*16@tvdR@sXpF@oPnKW*J#5LufYgdyXF5z_)ka*0k0>tp~2UT{ga6w>4$-isMmO+ zbq%Su`D8hH)yWUe?@@`3ONZ4ETvJS$#|P@LHDaM`AbQiPwmA}N&)09tCn7b~UlkyG L__&wvq9^|aHQlu+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/meta.json b/Resources/Textures/Mobs/Silicon/output.rsi/meta.json new file mode 100644 index 0000000000..a40ed37c60 --- /dev/null +++ b/Resources/Textures/Mobs/Silicon/output.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/2a19963297f91efb452dbb5c1d4eb28a14776b0a/icons/mob/silicon/ai.dmi", "states": [{"name": "ai", "directions": 1, "delays": [[0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.1]]}, {"name": "ai-banned", "directions": 1, "delays": [[0.7, 0.7, 0.7, 0.7, 0.7, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7]]}, {"name": "ai-banned-unshaded", "directions": 1, "delays": [[0.7, 0.7, 0.7, 0.7, 0.7, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7, 0.7]]}, {"name": "ai-banned_dead", "directions": 1, "delays": [[0.7, 0.7]]}, {"name": "ai-banned_dead-unshaded", "directions": 1, "delays": [[0.7, 0.7]]}, {"name": "ai-empty", "directions": 1, "delays": [[0.7, 0.7]]}, {"name": "ai-empty-unshaded", "directions": 1, "delays": [[0.7, 0.7]]}, {"name": "ai-holo-old", "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, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "ai-holo-old-unshaded", "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, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "ai-unshaded", "directions": 1, "delays": [[0.2, 0.2, 0.2, 0.1, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.2, 0.1]]}, {"name": "ai_dead", "directions": 1, "delays": [[1.0]]}, {"name": "ai_dead-unshaded", "directions": 1, "delays": [[1.0]]}, {"name": "default", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "default-unshaded", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "floating_face", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "floating_face-unshaded", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "horror", "directions": 1, "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]]}, {"name": "horror-unshaded", "directions": 1, "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]]}, {"name": "xeno_queen", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "xeno_queen-unshaded", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} \ No newline at end of file diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen-unshaded.png b/Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..3ea194039f4fee0b41d8c2f4b5f602592936f88f GIT binary patch literal 2484 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen.png b/Resources/Textures/Mobs/Silicon/output.rsi/xeno_queen.png new file mode 100644 index 0000000000000000000000000000000000000000..3ea194039f4fee0b41d8c2f4b5f602592936f88f GIT binary patch literal 2484 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai.png new file mode 100644 index 0000000000000000000000000000000000000000..420a07c1f96662a077d518546c866ea074cd556f GIT binary patch literal 9757 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 J<g3}-{2v}Hvk(9P literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_dead.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_dead.png new file mode 100644 index 0000000000000000000000000000000000000000..eb74655e027f45f7ce830da6a337dcf2aa74c248 GIT binary patch literal 4405 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 W51pTEd9e|sD$u89>Gr2AU-w^7I7~1A literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_empty.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/ai_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..40e8ac5216186918389147a014046b6e454f32cf GIT binary patch literal 4405 zcmeHKdr%X19$!Ft1VK5USD9lCZKsaiWb-1)nxH^}<ZLj7Sg_(_v-=CI=4EyvLFk+X zZ4p`qZA+EXJCu_?>`^IITEGJ<9%Zbx?G?}BA&mBlJ*6mi^nA45Zxf!=&UBb*|09{r z?tXvY@8|dZe!h>%Z?`FRRfOOn0fHbAh7?^Iyrb}185#_K3(9lr;qBCRb0(KYdQg|! zX=Q8x<?>ws1zyIAAl@Fk=|aRwJoJ8cY59(4Ro>_~42K+_-3SlPFkE=4P?Nj4->Z#r zv8JdB(A~2|Uo2<7JoDtN?@!DG*AdWlY4soI1s|u`Dr3K_`a!lSK9FqAKW#`ET6eyF zSLNK4ww>-d_mcm9@z06au6+khX*HcqN8&b|-E=;(HSzMZ|EzqQioO3LIC^!k><oFZ zIJ{QU*b#Vb&5joJmaNRy&mYpHzqotAcny8<ojky~T(_>P@$<y}2okW9NlG#ql9EOP zf?-rYvuAnA$(7OnXsavL|2OCwda2?7@ztq?<%df3M@&6}zY+Vg?iM{7VJI!D?SI;J zy&*H#we4K@iM4ZP#Gs!>rk0A*E<M<rShi?(`Mn#x&%}880upOKGB++0yjcbQdPyRA zZT<X$jZrIC6zw}v)A59M&&E&M*}~qFo&RbHJv$tKd*Dj?@S{P6!fh{q`<%WtLDRx~ zTYY<X(mM|??HK5o8@!?7e|xUJciLH978$OQ(RA9)jwP0c^C#RPje8ILZq3~1=dZ0T zTypijkMheJpMI<1oc2z5alZYLGr3i@B~)10!%GsABY=7HlElNwrMu@1c53d&mai~~ z_Gbay{dkRF{@%+W4~qMGt9HF*P%d2*)V#@dkD7JmtC>}~7k-RCHuz0g|DS^E-#^yY zgM1Tjc(>{3^(g#EL{?UlB7gP={|<QWFI-nebk~~n$L8(0ExTJ6jdS7O*ME5Y=&fyc z5&F%;|5@DMKWC9R?N)PVOVI1be<-Ny?wj4FaG%;BI2;QLFOPu*mT7!~pqzFQNjoh- z<h8qC<spb>h1W$=TLFh!KsMvh3Wr+SgeXI6g&7JXZgeGq942ME8>DYfHB;NSQfgYb zB3_{J5)i-+I1=^RZ4Q?3YK1;r0zUI%u@LpCa9g#)Orr@+a=HO37s*99ruQ;=QenIR z)wpRZk)}%?fq-vXVGhT+2(j4X@rXP!k<*<mmZ;TgF)kHLr5Mz}*n9^^dNBtZ%R~4v zbbzJYjEiHO4wT0vEzVp{D-^<Ybksk)%V->fcd#QYKt9A?(j}INaIxJko@l{x`aB3S z;?U1pux8k&;xxcIbKMls=K%*7I}w7W#_V0WZksP0ni2yWutQZA?kbtI<tl^GG-kn5 zkj>a#J}XG}Bu$R7j*~UXH-5zz&O}F``55jb?a|nM%23N_By>(Hmv_&g(+c_h3ED|9 zG~s`YQ^dvLB#ud_I2w~%ta40cky$YcP*OQf(Q%|gG6BlqU^&u30Uip1ix`NbRw#hQ zf-5i;4M<EbwOTQYf>vR`DzVBWYL&%Gt0zFLaWk+gN!vuPcqke|sb~_A3JS-pKu$q# zXe9)-0E|+}r8J-<DqJo1LD3YE>~z~nm`=t{W&^Ryk?mXH2`3Uv2CYyk!p9{h8_8Lr z0qg<BK|4L{_>!5igLIDM`IIQ7vN$=eRN)GVOeR;2D?J6=EUZNyRf3CTekb_E5HK7_ zEy-6Z1n^nFXow^?AUUVo>~z|+Lf$Eqw;WqG!iJ(rj?|GHfS|ZkPT(p+A~#EA1Rh67 z@nsU6z$fTCX~vrWOWJ(<pc;SDQy3QRpYId-dnz4l^S}9DZH%v(P}J8J1WEZrV97i{ z`}~Ah{v|4hbYufKKSm06RL=ZNF-U1Ct&9T_OhQXkFoQUak(6ACSxHhUwJ33=Qb|ol zXPs8gL%KmiHsld<1q;;275dmns+LaH_v8RR50EkpS78$<6N*QI74s8gG+K@LCwypp z3KK>QwDZg0=z^0_JT?kP_~M7oZ}=I>!*93)gr44HO8idKHBHx)7?_gsbahSBH6;e7 zq&!_+zZzYF@xv5wz<)p<cvRvBPCf#US;3aot8~Z%{#$eWjhCP#%$2f^MUco2{t9?y zS>$3U4B-q$eaMy2$cW&12jxTap{UQGOEBkL9L!9JEXHS@?%6n8)cSJ9*N0kbI<L(T z%q*>Y1vehssaslVG{)jlh~|Y?V@gW&6`$U09j+|yTHmFoWvPP^pG0p-AeuiYF?AoH zd!Ik8eywDYY_V){)bFGAzg<7j7rlBzR6}*+>vtOG;(y$^@Uw!1#HZTX%8LukYpd!Q z*{$nr%y%<xuD|g`(eeyb+Z&e7Ewp|3#ghdu*iIcm7PJ@jHMG~BBdV{wV=YC8x<wg% zHt0Dr9Po9}$00w)$DFTd3QXS96xb}RnB9LibU}My=8i(-4ua?|N3_4n1P#4^_$Y5+ M(5LF^6E_$AAL$TFTL1t6 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/base.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/base.png new file mode 100644 index 0000000000000000000000000000000000000000..a9db83691c1db9415a193b3f61d8c42041893ce9 GIT binary patch literal 4421 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/default.png b/Resources/Textures/Mobs/Silicon/station_ai.rsi/default.png new file mode 100644 index 0000000000000000000000000000000000000000..d52aceaf5b13f21bb46feabc481fb2d1e7fb0026 GIT binary patch literal 2082 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 M07*qoM6N<$g4CVXe*gdg literal 0 HcmV?d00001 diff --git a/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json b/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json new file mode 100644 index 0000000000..a3da52233d --- /dev/null +++ b/Resources/Textures/Mobs/Silicon/station_ai.rsi/meta.json @@ -0,0 +1,52 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/2a19963297f91efb452dbb5c1d4eb28a14776b0a/icons/mob/silicon/ai.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "ai", + "delays": [ + [ + 0.2, + 0.2, + 0.1, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.2, + 0.1 + ] + ] + }, + { + "name": "ai_dead" + }, + { + "name": "ai_empty", + "delays": [ + [ + 0.7, + 0.7 + ] + ] + }, + { + "name": "default", + "directions": 4 + }, + { + "name": "base" + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/base.png b/Resources/Textures/Objects/Devices/ai_card.rsi/base.png new file mode 100644 index 0000000000000000000000000000000000000000..244183c078c3d580cda31d0431ced9150a7cf7c9 GIT binary patch literal 4432 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/empty.png b/Resources/Textures/Objects/Devices/ai_card.rsi/empty.png new file mode 100644 index 0000000000000000000000000000000000000000..7e61f368de294ae506cdab73e818f0f594bb260c GIT binary patch literal 4292 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 HdhLGz@YfZx literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/full.png b/Resources/Textures/Objects/Devices/ai_card.rsi/full.png new file mode 100644 index 0000000000000000000000000000000000000000..59131c8c0aafc5f647d4409168265f23cc05e667 GIT binary patch literal 5121 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 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/inhand-left.png b/Resources/Textures/Objects/Devices/ai_card.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..2d3863145b951a7743066a59157c1cc7e562a50d GIT binary patch literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAQ1FST zi(^Q|oVT|QavnAiaJeXd!mxBHL-@l<A+1~<#eCTUPu2;#oY@oX*Zpa)BKzykoh%1p zm=g3D4W={9@Mbup%J58xp_sG5GPB{a#k!9>zyJI3eW&X_#`)JO=0C5^IGT8Tx_t4~ zX@6Nl{8v5ulh<%-62mLgwUz6yO>>#Rwqw3njO^a1`qCRp_ZvjpV=T?<n~@F0Vb{*J z%o^`_b7U8sx#xbv?m)tB#?)tLKVD-L`7XGPFCgtbZ;pII7?K-n8JpW2_vyKRWdiz_ N!PC{xWt~$(699E-bSwY> literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/inhand-right.png b/Resources/Textures/Objects/Devices/ai_card.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..1704b9c3c112fc423e9466328df5e2bac1f15c87 GIT binary patch literal 316 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAQ1G3n zi(^Q|oVT|&@*Xk}alP2>ax#YJfu_-<<sTSy#8XtfMH*z=55>Qq+r@Tz=cdWe;su{G zbQCijv1E88!%)cAV8QM%hgsnqqky@H!qP3<?=PLg-977Oy17I2mx;#TbCXMR=a=4B z7Tm@7O6h&-;vcs;GrI2<@d-aXVJch4c`bSCr@!mtmWzHXdSHK;F|7W_vln)MC0!sU z;;;xJ?91L^WPHI`RN>Z{1Ny;?i_Y=X7}c^aI?Gd2{7O|KF>S%~kcJoh@js0_;4V~` bf53TkzSA$a-26*GpEG#6`njxgN@xNATZDFx literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/ai_card.rsi/meta.json b/Resources/Textures/Objects/Devices/ai_card.rsi/meta.json new file mode 100644 index 0000000000..8b8135fa16 --- /dev/null +++ b/Resources/Textures/Objects/Devices/ai_card.rsi/meta.json @@ -0,0 +1,58 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/1feffb747a33434a9d28450fc52ade75253aeba5/icons/obj/aicards.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "base" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "empty", + "delays": [ + [ + 0.4, + 0.4 + ] + ] + }, + { + "name": "full", + "delays": [ + [ + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4, + 0.4 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/Devices/output.rsi/aicard-full-unshaded.png b/Resources/Textures/Objects/Devices/output.rsi/aicard-full-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..51a309d579ec0babdbdb117920c416e178e37599 GIT binary patch literal 777 zcmYk2dq`6O6vk(g4nv2%j4Ul*DZWTh!`IbqxvA+wd?bbTPz;7!*mS1LYZ6o-Dnw>Z z(HLfA?UK5N6W@~9%<Eh<ahoFP)WLPM>!x>ZyWQy@dElJGIh^zH<9l^8i{b8e-i<^e zxu-Gd*+lOmvX1OR)P@K5y-B2F^J(;yoT_$2BQH}YpFTdH)*O*dJ-am_<TamMpqsa+ z0|z}y?*#`07Wgy<C(Q{Tlb-nnZel-vHIcX>gbWdr`zBktjW<{^8itBWe&P~yV<e<r z=6>146sa(nyVDz9T|4mJ2v5$fSYW`#yhm<q^$lm+x$JF0sK#epw84<Kj#}a`B~Ve1 zDodZYsXLbV2*ZT@F^u0#RuHl?po;N`w?{}utreO4i#hh+zJKX-xGARC&SosLIr1oA zX&wXOws|9nV%kZhW60a^4CEqfys!{@(ar^@BKr@3D2LPsbzo+Rm7igko5GEJCFNFn z2v93lk95CP$*=+zIc@^SsZ+f+#IWC8x$nOLqlRdlS!u&dk)GZ_+-S+ehV-rtL7=)! zy6GB_Bem5j)`}u-SEwoHGVSYqxhltE6u&A%h%21;RBEN|D$igv^iW_x69vXPPQaqZ zHs2#y4@I4Z@<f{b!zrbIwMBPDrfKUmJ#VF%Q3PRX1>H9wv*!?&zGcvg*%qut)xX9S z>t1$sLdZ9NC58Es!%wye<Ry?y>(tNxDhSH3bNFM~7SL#Ta~aGBsJb0AQ^lC|r2;F9 z7;S4q*z(Spzw4apJ>1FJu^s_Q7JjzzB;xf?+LrNEBRJr^y$Tz)xUG;7_-@6`%UDwA zn4maCCz%G|(Os)V%1s1SR))T(rdBp=C!=>@mfVVBMyN)kN9%!jFO#4q##qEr`+=%; z`VREOb#{TJN_TjLdMvWNX7U{VX-%F9`q!WzZfjC#DA%fi4y)3nr6fVa;U_TX|K{QP lKXoFrat0r{5DgqqcUW9ur=%6PVI|Wsr(MsYi>Y_({{hD)pQQi* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/output.rsi/aicard-full.png b/Resources/Textures/Objects/Devices/output.rsi/aicard-full.png new file mode 100644 index 0000000000000000000000000000000000000000..03908b5284f4fd89c2ea79adc1227a271591a21b GIT binary patch literal 1746 zcmcJOeKb^Q7{JGOqpjhvO$n3Hu9UJzM`epE5lT&47D<L^r&5NbCNg(U?WVLiyG&Se zla|UF$!GZ(m12^qjnb&*(oDmonZYp3$Gx*NEoW>0*#GvP^WOJ8?|I+zJkRg>z2|pq z_n&8OV~)XK<^=}$?$phfb%Qa-RQLZmX5V5A#<U>NcVjTENK)iLb@X3&mK}`X9XiY- z+=sX9ddB!IH+P|(;_j{>^D9Os+k%;k@CCWKLdGWUy5TFUuVwi%PiLg|nh1E8c2~8) zQZDTSM;^dTt4S<w&YJ3K?`|dP;$TK!ufr+|Oo7UJ`#tC)?ipYOsCGSEINZG=Nj)0o zQb~t8Ik=c#K>Pci*y);akQa`Mm`=_&<KX6HQrH!Zn-q@$04SfE(0Aa-z$>z=Uu#M< zm%$o%(y#R^)u?5UX>|>_Y|;0tcJi|#FY1>HnZKjV2>pOGO_?-pj<+h!BNDXk*)6H? z5!`7Y6_jTW)ScR3U-70#92|SR3Z-Rg6?xUKy_jA1d&cR>Y12tGKq(MX=?32xJ`(>C z(c0o7!%-ip1%IltQcWkKCG50u#BI6k1<Onk>M3-UO>x+%xMjD-upyzM<8Esy{8(uf z{;^b@4iA!Dx_PZ(Wn(!&5Pj0vwm8Fu>)VP}oSGzwh<$kC(dZXu6j5M7e+QySSGIY{ zHhNG7q9hFzUhJx)Uf*bUv!@yv^zjZ(72k;ORd89+`hX9w`+<z4!I-^Z@!)l~%+k4> z;Td1Bc{B&uj)qv>%upLQT2tEg3MkZvJfr8;M?7mGO<}r^km_#W$vNzKlv<QY=@z62 zf-rf5nPVghdN%B;n}MN{Lrye)1fMjV@0!_c6B!FwMQv~+-J{j+H}@^Z2yr=Vxv@3R zl7BLSS|5b5lrQbA&c-hBK`oT)N#5#_hECO5hxD`{j9PIf;I;vu7SWFNM#t%p<~{ZR z_zv&v&wlX6%$gK5usWgI<xI$woz&|ZZJ(i<LvUiJ7)FwF0lDM}cwrvdmK-p;di2x| z(>gWb0`grHl)vBjV?XN5Y_l~^CDVq*ht{-PD!io+8}4Wm*S1$HyoERj;FIsaJaJFm zY?%S1vZY#+P%XMOY4P|M?NB9K`C)hrU%ipFsZHCA=(U{v{O2N!=^T~VtR3nl0ZY)U zbd|xF(_|0s9B>_?dusv_Rh*BTq`XI^&r@}yano@59v8ohgvYm1{m|G*R>y~-BqSc~ zpL7%t^{8UfwCksg%7)8pVBvEnOd4Xzx@00a4^9?7BfY%gFlnI@`}*?J*5D5|A;)rV zD{WY=Zx!<M=bF)2w<aZitH3qfJnJe%8o{5JBcU>bq?yoQg=dsq2cJ|vnZrgCU`^%M zm$$A$j)enC<lPQk2ihW3;#&H+x~pXTL@|SSa-!!TB7`?s6Tu;D{#M}93lbgl2-Ye1 zxDMJr;sh4y+L9lq-$&OeU$x+9&b&$emF%3qtDZ1=OL|{Uu+T=U4Q5&C=Bx$(#xr{m z+zEUn`MmE(VZT02y^WGE#g=P#3&52(T72RXL=lcQ)etu>`qz~s6LLF0Gz4MP8^tT{ z%F2`a<`O4*Zre9N@nrf`T<7mlRRy3=*S7egUBYn{kACL_)Frm6fw(G3ZXG_3jn;a6 zDIS_TlFcN;mPjsTrj$L~+Ln9#Q+x$>IM7Y3@7|2uqG$TtVDfyXUT03J9)?y_qL=s+ zgcVLvtdDR7!cg75T93Aa10TZW$IQg}To^(<;S)#$2TBMQvphaOIAazW+!j!#3jn&a z&vC*VHluS8MtU!4tvz#4Qkm*<mi_*v$O&Apdl%s$p+qNY*GTXidSgxl*UZ$*$S(-b z=^*r>$=hDV#-|vBhL#ZzL-ay`38ZVf@n7*W%chZIYc$F>O;bH2m8MN0{tu|2u*vQx z#nPMPJau9TvY?^#sptg47j(kIx&k-7FR#(^>+Jj4E5PJSnw0-rw*2V-rnqD=YWB3V UyS=uT6R77J=(pXM<r8)KFKs=l5dZ)H literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/output.rsi/aicard-unshaded.png b/Resources/Textures/Objects/Devices/output.rsi/aicard-unshaded.png new file mode 100644 index 0000000000000000000000000000000000000000..6191a01ec4eb7cf6d356094a1068d824c8aa24ff GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3HE3&8=$zQh}Z>jv*Cu-d=FzZ7>jFJ=nyn zvQXUf#{s8@n>goMn*W_`4pf|dD@}gh@juJ2|5aVObN_z-pW@6{7){ae)~D<Y3=!9u YuU=-6+U553GSF}aPgg&ebxsLQ03h!!IRF3v literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/output.rsi/aicard.png b/Resources/Textures/Objects/Devices/output.rsi/aicard.png new file mode 100644 index 0000000000000000000000000000000000000000..57f604efb0eab5eab2f4bc17290c669a485c3056 GIT binary patch literal 414 zcmV;P0b%}$P)<h;3K|Lk000e1NJLTq002M$001Be1^@s6qMd$(0004HNkl<Zc%1E+ zp-%%b6o-E!{1=#Wh$VAFV=$b?OiiC%z)T|$1Xqp1pr>Y-p1Xj+HA|+@=plM{%My0) zwSC<s+xwZfbl3Zu_Pa~3fJUP^<@Chv)W6&8{qO5tjw8(XAJ{(7WbA5bo2Jdn1A4aw zu(EQKAk07ahEK*jkS2B8AZvav7r$P&?}ATHK$}`buAc6J%gn7nXcF_!NchdQ)2ptf zb+Zg?>u9*GrFHe&I(SSDz;Ayp+mxd+8Umj`uJ8OK>>Y3bKK%$(@S-T#Y-0@Ywq#$= z;m;oV{Cvvi(-Tm=RPqZp9|qW6ftXbM+1j%olg~E5djP@)crQTb1`IOb`PDVV>;Xaz zVDi%$P^%Za0SYyM$xmy*oZpAy2N}TNr#B!ru?7%P1IR1XfJ_Yl)BrW$<P9*!@JCSh z6Eq;7rs21r2LS(ne{-s&0SWo^HZ|fzM&ul@YraOK(P)l1e>+74@UVnV&j0`b07*qo IM6N<$f`|IS_5c6? literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/output.rsi/meta.json b/Resources/Textures/Objects/Devices/output.rsi/meta.json new file mode 100644 index 0000000000..500ecb8e3e --- /dev/null +++ b/Resources/Textures/Objects/Devices/output.rsi/meta.json @@ -0,0 +1 @@ +{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/blob/1feffb747a33434a9d28450fc52ade75253aeba5/icons/obj/aicards.dmi", "states": [{"name": "aicard", "directions": 1, "delays": [[0.4, 0.4]]}, {"name": "aicard-full", "directions": 1, "delays": [[0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4]]}, {"name": "aicard-full-unshaded", "directions": 1, "delays": [[0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4, 0.4]]}, {"name": "aicard-unshaded", "directions": 1, "delays": [[0.4, 0.4]]}]} \ No newline at end of file From c866ffb45fcadbc32cb6e7a739863fd51a74483b Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 25 Aug 2024 22:05:39 +1000 Subject: [PATCH 02/38] Verb tweaks (#31309) * Verb tweaks Remove the LOS check because this is already done above in CanExamine. * Fix outlines * import --- .../Commands/SetMenuVisibilityCommand.cs | 1 + .../ContextMenu/UI/EntityMenuUIController.cs | 19 ++++++-- Content.Client/Verbs/VerbSystem.cs | 45 ++++--------------- Content.Shared/Verbs/SharedVerbSystem.cs | 24 ++++++++++ 4 files changed, 49 insertions(+), 40 deletions(-) diff --git a/Content.Client/Commands/SetMenuVisibilityCommand.cs b/Content.Client/Commands/SetMenuVisibilityCommand.cs index ddfb0b1692..17a544daba 100644 --- a/Content.Client/Commands/SetMenuVisibilityCommand.cs +++ b/Content.Client/Commands/SetMenuVisibilityCommand.cs @@ -1,4 +1,5 @@ using Content.Client.Verbs; +using Content.Shared.Verbs; using JetBrains.Annotations; using Robust.Shared.Console; diff --git a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs index a60619baa3..b6f4dc1be2 100644 --- a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs +++ b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs @@ -9,6 +9,7 @@ using Content.Shared.Examine; using Content.Shared.IdentityManagement; using Content.Shared.Input; +using Content.Shared.Verbs; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Input; @@ -194,8 +195,20 @@ public override void FrameUpdate(FrameEventArgs args) return; // Do we need to do in-range unOccluded checks? - var ignoreFov = !_eyeManager.CurrentEye.DrawFov || - (_verbSystem.Visibility & MenuVisibility.NoFov) == MenuVisibility.NoFov; + var visibility = _verbSystem.Visibility; + + if (!_eyeManager.CurrentEye.DrawFov) + { + visibility &= ~MenuVisibility.NoFov; + } + + var ev = new MenuVisibilityEvent() + { + Visibility = visibility, + }; + + _entityManager.EventBus.RaiseLocalEvent(player, ref ev); + visibility = ev.Visibility; _entityManager.TryGetComponent(player, out ExaminerComponent? examiner); var xformQuery = _entityManager.GetEntityQuery<TransformComponent>(); @@ -209,7 +222,7 @@ public override void FrameUpdate(FrameEventArgs args) continue; } - if (ignoreFov) + if ((visibility & MenuVisibility.NoFov) == MenuVisibility.NoFov) continue; var pos = new MapCoordinates(_xform.GetWorldPosition(xform, xformQuery), xform.MapID); diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 2513210e2c..e28f48d6a5 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -67,6 +67,14 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true ? Visibility : Visibility | MenuVisibility.NoFov; + var ev = new MenuVisibilityEvent() + { + TargetPos = targetPos, + Visibility = visibility, + }; + + RaiseLocalEvent(player.Value, ref ev); + visibility = ev.Visibility; // Get entities List<EntityUid> entities; @@ -78,13 +86,8 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true var entitiesUnderMouse = gameScreenBase.GetClickableEntities(targetPos).ToHashSet(); bool Predicate(EntityUid e) => e == player || entitiesUnderMouse.Contains(e); - // first check the general location. - if (!_examine.CanExamine(player.Value, targetPos, Predicate)) - return false; - TryComp(player.Value, out ExaminerComponent? examiner); - // Then check every entity entities = new(); foreach (var ent in _entityLookup.GetEntitiesInRange(targetPos, EntityMenuLookupSize, flags: examineFlags)) { @@ -138,27 +141,6 @@ public bool TryGetEntityMenuEntities(MapCoordinates targetPos, [NotNullWhen(true } } - // Remove any entities that do not have LOS - if ((visibility & MenuVisibility.NoFov) == 0) - { - var xformQuery = GetEntityQuery<TransformComponent>(); - var playerPos = _transform.GetMapCoordinates(player.Value, xform: xformQuery.GetComponent(player.Value)); - - for (var i = entities.Count - 1; i >= 0; i--) - { - var entity = entities[i]; - - if (!_examine.InRangeUnOccluded( - playerPos, - _transform.GetMapCoordinates(entity, xform: xformQuery.GetComponent(entity)), - ExamineSystemShared.ExamineRange, - null)) - { - entities.RemoveSwap(i); - } - } - } - if (entities.Count == 0) return false; @@ -230,15 +212,4 @@ private void HandleVerbResponse(VerbsResponseEvent msg) OnVerbsResponse?.Invoke(msg); } } - - [Flags] - public enum MenuVisibility - { - // What entities can a user see on the entity menu? - Default = 0, // They can only see entities in FoV. - NoFov = 1 << 0, // They ignore FoV restrictions - InContainer = 1 << 1, // They can see through containers. - Invisible = 1 << 2, // They can see entities without sprites and the "HideContextMenu" tag is ignored. - All = NoFov | InContainer | Invisible - } } diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index c3f906f293..37840dcbb5 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Interaction; using Content.Shared.Inventory.VirtualItem; using Robust.Shared.Containers; +using Robust.Shared.Map; namespace Content.Shared.Verbs { @@ -174,4 +175,27 @@ public virtual void ExecuteVerb(Verb verb, EntityUid user, EntityUid target, boo _interactionSystem.DoContactInteraction(user, target); } } + + // Does nothing on server + /// <summary> + /// Raised directed when trying to get the entity menu visibility for entities. + /// </summary> + [ByRefEvent] + public record struct MenuVisibilityEvent + { + public MapCoordinates TargetPos; + public MenuVisibility Visibility; + } + + // Does nothing on server + [Flags] + public enum MenuVisibility + { + // What entities can a user see on the entity menu? + Default = 0, // They can only see entities in FoV. + NoFov = 1 << 0, // They ignore FoV restrictions + InContainer = 1 << 1, // They can see through containers. + Invisible = 1 << 2, // They can see entities without sprites and the "HideContextMenu" tag is ignored. + All = NoFov | InContainer | Invisible + } } From d6b0b997c0b89a17409782198f80dc9cd95897e1 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 25 Aug 2024 22:30:28 +1000 Subject: [PATCH 03/38] ItemToggle + slots stuff (#31312) * ItemToggle + slots stuff - Add component for itemslot locks to match LockComponent (surprised this didn't exist). - Add thing for pointlight to match itemtoggle. In future should be used for PDAs and stuff but need to fix some other stuff first. * Also this * grill --- .../ItemSlot/ItemSlotsLockComponent.cs | 13 ++++++ .../ItemSlot/ItemSlotsSystem.Lock.cs | 36 +++++++++++++++++ .../Containers/ItemSlot/ItemSlotsSystem.cs | 4 +- .../Components/ItemToggleComponent.cs | 12 +++--- .../Item/ItemToggle/ItemToggleSystem.cs | 40 ++++++++++++++----- .../ItemTogglePointLightComponent.cs | 12 ++++++ .../ItemTogglePointLightSystem.cs | 29 ++++++++++++++ .../ItemSlotRequiresPowerComponent.cs | 9 +++++ .../ItemSlotRequiresPowerSystem.cs | 23 +++++++++++ Resources/Locale/en-US/items/toggle.ftl | 2 + 10 files changed, 163 insertions(+), 17 deletions(-) create mode 100644 Content.Shared/Containers/ItemSlot/ItemSlotsLockComponent.cs create mode 100644 Content.Shared/Containers/ItemSlot/ItemSlotsSystem.Lock.cs create mode 100644 Content.Shared/Light/Components/ItemTogglePointLightComponent.cs create mode 100644 Content.Shared/Light/EntitySystems/ItemTogglePointLightSystem.cs create mode 100644 Content.Shared/Power/Components/ItemSlotRequiresPowerComponent.cs create mode 100644 Content.Shared/Power/EntitySystems/ItemSlotRequiresPowerSystem.cs create mode 100644 Resources/Locale/en-US/items/toggle.ftl diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsLockComponent.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsLockComponent.cs new file mode 100644 index 0000000000..0d8901028d --- /dev/null +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsLockComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Containers.ItemSlots; + +/// <summary> +/// Updates the relevant ItemSlots locks based on <see cref="LockComponent"/> +/// </summary> +[RegisterComponent, NetworkedComponent] +public sealed partial class ItemSlotsLockComponent : Component +{ + [DataField(required: true)] + public List<string> Slots = new(); +} diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.Lock.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.Lock.cs new file mode 100644 index 0000000000..ee5178df95 --- /dev/null +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.Lock.cs @@ -0,0 +1,36 @@ +using Content.Shared.Lock; + +namespace Content.Shared.Containers.ItemSlots; + +public sealed partial class ItemSlotsSystem +{ + private void InitializeLock() + { + SubscribeLocalEvent<ItemSlotsLockComponent, MapInitEvent>(OnLockMapInit); + SubscribeLocalEvent<ItemSlotsLockComponent, LockToggledEvent>(OnLockToggled); + } + + private void OnLockMapInit(Entity<ItemSlotsLockComponent> ent, ref MapInitEvent args) + { + if (!TryComp(ent.Owner, out LockComponent? lockComp)) + return; + + UpdateLocks(ent, lockComp.Locked); + } + + private void OnLockToggled(Entity<ItemSlotsLockComponent> ent, ref LockToggledEvent args) + { + UpdateLocks(ent, args.Locked); + } + + private void UpdateLocks(Entity<ItemSlotsLockComponent> ent, bool value) + { + foreach (var slot in ent.Comp.Slots) + { + if (!TryGetSlot(ent.Owner, slot, out var itemSlot)) + continue; + + SetLock(ent.Owner, itemSlot, value); + } + } +} diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index c8745d17d4..4fe49a6382 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -26,7 +26,7 @@ namespace Content.Shared.Containers.ItemSlots /// Note when using popups on entities with many slots with InsertOnInteract, EjectOnInteract or EjectOnUse: /// A single use will try to insert to/eject from every slot and generate a popup for each that fails. /// </remarks> - public sealed class ItemSlotsSystem : EntitySystem + public sealed partial class ItemSlotsSystem : EntitySystem { [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; @@ -43,6 +43,8 @@ public override void Initialize() { base.Initialize(); + InitializeLock(); + SubscribeLocalEvent<ItemSlotsComponent, MapInitEvent>(OnMapInit); SubscribeLocalEvent<ItemSlotsComponent, ComponentInit>(Oninitialize); diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs index 46249fdd0d..47edec135d 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs @@ -19,6 +19,12 @@ public sealed partial class ItemToggleComponent : Component [DataField, AutoNetworkedField] public bool Activated = false; + /// <summary> + /// Can the entity be activated in the world. + /// </summary> + [DataField] + public bool OnActivate = true; + /// <summary> /// If this is set to false then the item can't be toggled by pressing Z. /// Use another system to do it then. @@ -52,12 +58,6 @@ public sealed partial class ItemToggleComponent : Component /// </summary> [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] public SoundSpecifier? SoundFailToActivate; - - /// <summary> - /// Whether or not to toggle the entity's lights on or off. - /// </summary> - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] - public bool ToggleLight = true; } /// <summary> diff --git a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs index c4e4150659..044a1109a1 100644 --- a/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Popups; @@ -5,6 +6,7 @@ using Content.Shared.Temperature; using Content.Shared.Throwing; using Content.Shared.Toggleable; +using Content.Shared.Verbs; using Content.Shared.Wieldable; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; @@ -23,7 +25,6 @@ public sealed class ItemToggleSystem : EntitySystem [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPointLightSystem _light = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() @@ -35,6 +36,8 @@ public override void Initialize() SubscribeLocalEvent<ItemToggleComponent, ItemUnwieldedEvent>(TurnOffOnUnwielded); SubscribeLocalEvent<ItemToggleComponent, ItemWieldedEvent>(TurnOnOnWielded); SubscribeLocalEvent<ItemToggleComponent, UseInHandEvent>(OnUseInHand); + SubscribeLocalEvent<ItemToggleComponent, GetVerbsEvent<ActivationVerb>>(OnActivateVerb); + SubscribeLocalEvent<ItemToggleComponent, ActivateInWorldEvent>(OnActivate); SubscribeLocalEvent<ItemToggleHotComponent, IsHotEvent>(OnIsHotEvent); @@ -67,6 +70,32 @@ private void OnUseInHand(Entity<ItemToggleComponent> ent, ref UseInHandEvent arg Toggle((ent, ent.Comp), args.User, predicted: ent.Comp.Predictable); } + private void OnActivateVerb(Entity<ItemToggleComponent> ent, ref GetVerbsEvent<ActivationVerb> args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + var user = args.User; + + args.Verbs.Add(new ActivationVerb() + { + Text = !ent.Comp.Activated ? Loc.GetString("item-toggle-activate") : Loc.GetString("item-toggle-deactivate"), + Act = () => + { + Toggle((ent.Owner, ent.Comp), user, predicted: ent.Comp.Predictable); + } + }); + } + + private void OnActivate(Entity<ItemToggleComponent> ent, ref ActivateInWorldEvent args) + { + if (args.Handled || !ent.Comp.OnActivate) + return; + + args.Handled = true; + Toggle((ent.Owner, ent.Comp), args.User, predicted: ent.Comp.Predictable); + } + /// <summary> /// Used when an item is attempted to be toggled. /// Sets its state to the opposite of what it is. @@ -204,16 +233,7 @@ private void UpdateVisuals(Entity<ItemToggleComponent> ent) if (TryComp(ent, out AppearanceComponent? appearance)) { _appearance.SetData(ent, ToggleVisuals.Toggled, ent.Comp.Activated, appearance); - - if (ent.Comp.ToggleLight) - _appearance.SetData(ent, ToggleableLightVisuals.Enabled, ent.Comp.Activated, appearance); } - - if (!ent.Comp.ToggleLight) - return; - - if (_light.TryGetLight(ent, out var light)) - _light.SetEnabled(ent, ent.Comp.Activated, light); } /// <summary> diff --git a/Content.Shared/Light/Components/ItemTogglePointLightComponent.cs b/Content.Shared/Light/Components/ItemTogglePointLightComponent.cs new file mode 100644 index 0000000000..6ac1bf236d --- /dev/null +++ b/Content.Shared/Light/Components/ItemTogglePointLightComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Light.Components; + +/// <summary> +/// Toggles point light on an entity whenever ItemToggle hits. +/// </summary> +[RegisterComponent, NetworkedComponent] +public sealed partial class ItemTogglePointLightComponent : Component +{ + +} diff --git a/Content.Shared/Light/EntitySystems/ItemTogglePointLightSystem.cs b/Content.Shared/Light/EntitySystems/ItemTogglePointLightSystem.cs new file mode 100644 index 0000000000..7030c538c1 --- /dev/null +++ b/Content.Shared/Light/EntitySystems/ItemTogglePointLightSystem.cs @@ -0,0 +1,29 @@ +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Toggleable; +using ItemTogglePointLightComponent = Content.Shared.Light.Components.ItemTogglePointLightComponent; + +namespace Content.Shared.Light.EntitySystems; + +/// <summary> +/// Handles ItemToggle for PointLight +/// </summary> +public sealed class ItemTogglePointLightSystem : EntitySystem +{ + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedPointLightSystem _light = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent<ItemTogglePointLightComponent, ItemToggledEvent>(OnLightToggled); + } + + private void OnLightToggled(Entity<ItemTogglePointLightComponent> ent, ref ItemToggledEvent args) + { + if (!_light.TryGetLight(ent.Owner, out var light)) + return; + + _appearance.SetData(ent, ToggleableLightVisuals.Enabled, args.Activated); + _light.SetEnabled(ent.Owner, args.Activated, comp: light); + } +} diff --git a/Content.Shared/Power/Components/ItemSlotRequiresPowerComponent.cs b/Content.Shared/Power/Components/ItemSlotRequiresPowerComponent.cs new file mode 100644 index 0000000000..6e3b9eaca0 --- /dev/null +++ b/Content.Shared/Power/Components/ItemSlotRequiresPowerComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Power.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ItemSlotRequiresPowerComponent : Component +{ + +} diff --git a/Content.Shared/Power/EntitySystems/ItemSlotRequiresPowerSystem.cs b/Content.Shared/Power/EntitySystems/ItemSlotRequiresPowerSystem.cs new file mode 100644 index 0000000000..3df8b91a98 --- /dev/null +++ b/Content.Shared/Power/EntitySystems/ItemSlotRequiresPowerSystem.cs @@ -0,0 +1,23 @@ +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Power.Components; + +namespace Content.Shared.Power.EntitySystems; + +public sealed class ItemSlotRequiresPowerSystem : EntitySystem +{ + [Dependency] private readonly SharedPowerReceiverSystem _receiver = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent<ItemSlotRequiresPowerComponent, ItemSlotInsertAttemptEvent>(OnInsertAttempt); + } + + private void OnInsertAttempt(Entity<ItemSlotRequiresPowerComponent> ent, ref ItemSlotInsertAttemptEvent args) + { + if (!_receiver.IsPowered(ent.Owner)) + { + args.Cancelled = true; + } + } +} diff --git a/Resources/Locale/en-US/items/toggle.ftl b/Resources/Locale/en-US/items/toggle.ftl new file mode 100644 index 0000000000..bcf5c161a6 --- /dev/null +++ b/Resources/Locale/en-US/items/toggle.ftl @@ -0,0 +1,2 @@ +item-toggle-activate = Activate +item-toggle-deactivate = Deactivate From 56451fa5e7fd9910cf38d27e5cc0800bd71b9a2f Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Wed, 19 Jun 2024 02:30:41 +1200 Subject: [PATCH 04/38] Station AI (#30944) * Station AI overlay * implement * Bunch of ports * Fix a heap of bugs and basic scouting * helldivers * Shuffle interactions a bit * navmap stuff * Revert "navmap stuff" This reverts commit d1f89dd4be83233e22cf5dd062b2581f3c6da062. * AI wires implemented * Fix examines * Optimise the overlay significantly * Back to old static * BUI radial working * lots of work * Saving work * thanks fork * alright * pc * AI upload console * AI upload * stuff * Fix copy-paste shitcode * AI actions * navmap work * Fixes * first impressions * a * reh * Revert "navmap work" This reverts commit 6f63fea6e9245e189f368f97be3e32e9b210580e. * OD * radar * weh * Fix examines * scoop mine eyes * fixes * reh * Optimise * Final round of optimisations * Fixes * fixes --- Content.Client/Interaction/DragDropSystem.cs | 2 +- .../ReplaySpectatorSystem.Blockers.cs | 7 +++- .../ActionBlocker/ActionBlockerSystem.cs | 6 ++-- .../Administration/SharedAdminFrozenSystem.cs | 8 ++++- Content.Shared/Cuffs/SharedCuffableSystem.cs | 8 ++++- Content.Shared/Ghost/SharedGhostSystem.cs | 8 ++++- .../Events/InteractionAttemptEvent.cs | 34 ++++++++----------- .../SharedInteractionSystem.Blocking.cs | 8 ++++- .../Systems/MobStateSystem.Subscribers.cs | 13 ++++++- .../Puppet/SharedVentriloquistPuppetSystem.cs | 10 ++++-- Content.Shared/Stunnable/SharedStunSystem.cs | 7 ++-- .../SubFloor/SharedSubFloorHideSystem.cs | 4 +-- 12 files changed, 79 insertions(+), 36 deletions(-) diff --git a/Content.Client/Interaction/DragDropSystem.cs b/Content.Client/Interaction/DragDropSystem.cs index 8baa4d15fe..d249766bbc 100644 --- a/Content.Client/Interaction/DragDropSystem.cs +++ b/Content.Client/Interaction/DragDropSystem.cs @@ -495,7 +495,7 @@ private void RemoveHighlights() // CanInteract() doesn't support checking a second "target" entity. // Doing so manually: var ev = new GettingInteractedWithAttemptEvent(user, dragged); - RaiseLocalEvent(dragged, ev, true); + RaiseLocalEvent(dragged, ref ev); if (ev.Cancelled) return false; diff --git a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs index 2fa862f3df..99d85350b5 100644 --- a/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs +++ b/Content.Client/Replay/Spectator/ReplaySpectatorSystem.Blockers.cs @@ -17,7 +17,7 @@ private void InitializeBlockers() SubscribeLocalEvent<ReplaySpectatorComponent, UseAttemptEvent>(OnAttempt); SubscribeLocalEvent<ReplaySpectatorComponent, PickupAttemptEvent>(OnAttempt); SubscribeLocalEvent<ReplaySpectatorComponent, ThrowAttemptEvent>(OnAttempt); - SubscribeLocalEvent<ReplaySpectatorComponent, InteractionAttemptEvent>(OnAttempt); + SubscribeLocalEvent<ReplaySpectatorComponent, InteractionAttemptEvent>(OnInteractAttempt); SubscribeLocalEvent<ReplaySpectatorComponent, AttackAttemptEvent>(OnAttempt); SubscribeLocalEvent<ReplaySpectatorComponent, DropAttemptEvent>(OnAttempt); SubscribeLocalEvent<ReplaySpectatorComponent, IsEquippingAttemptEvent>(OnAttempt); @@ -27,6 +27,11 @@ private void InitializeBlockers() SubscribeLocalEvent<ReplaySpectatorComponent, PullAttemptEvent>(OnPullAttempt); } + private void OnInteractAttempt(Entity<ReplaySpectatorComponent> ent, ref InteractionAttemptEvent args) + { + args.Cancelled = true; + } + private void OnAttempt(EntityUid uid, ReplaySpectatorComponent component, CancellableEntityEventArgs args) { args.Cancel(); diff --git a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs index bd6ee8cae1..457e3875d4 100644 --- a/Content.Shared/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/ActionBlocker/ActionBlockerSystem.cs @@ -89,7 +89,7 @@ public bool CanInteract(EntityUid user, EntityUid? target) return false; var ev = new InteractionAttemptEvent(user, target); - RaiseLocalEvent(user, ev); + RaiseLocalEvent(user, ref ev); if (ev.Cancelled) return false; @@ -98,7 +98,7 @@ public bool CanInteract(EntityUid user, EntityUid? target) return true; var targetEv = new GettingInteractedWithAttemptEvent(user, target); - RaiseLocalEvent(target.Value, targetEv); + RaiseLocalEvent(target.Value, ref targetEv); return !targetEv.Cancelled; } @@ -129,7 +129,7 @@ public bool CanUseHeldEntity(EntityUid user, EntityUid used) public bool CanConsciouslyPerformAction(EntityUid user) { var ev = new ConsciousAttemptEvent(user); - RaiseLocalEvent(user, ev); + RaiseLocalEvent(user, ref ev); return !ev.Cancelled; } diff --git a/Content.Shared/Administration/SharedAdminFrozenSystem.cs b/Content.Shared/Administration/SharedAdminFrozenSystem.cs index 2fa22e0005..259df2bdf2 100644 --- a/Content.Shared/Administration/SharedAdminFrozenSystem.cs +++ b/Content.Shared/Administration/SharedAdminFrozenSystem.cs @@ -11,6 +11,7 @@ namespace Content.Shared.Administration; +// TODO deduplicate with BlockMovementComponent public abstract class SharedAdminFrozenSystem : EntitySystem { [Dependency] private readonly ActionBlockerSystem _blocker = default!; @@ -23,7 +24,7 @@ public override void Initialize() SubscribeLocalEvent<AdminFrozenComponent, UseAttemptEvent>(OnAttempt); SubscribeLocalEvent<AdminFrozenComponent, PickupAttemptEvent>(OnAttempt); SubscribeLocalEvent<AdminFrozenComponent, ThrowAttemptEvent>(OnAttempt); - SubscribeLocalEvent<AdminFrozenComponent, InteractionAttemptEvent>(OnAttempt); + SubscribeLocalEvent<AdminFrozenComponent, InteractionAttemptEvent>(OnInteractAttempt); SubscribeLocalEvent<AdminFrozenComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<AdminFrozenComponent, ComponentShutdown>(UpdateCanMove); SubscribeLocalEvent<AdminFrozenComponent, UpdateCanMoveEvent>(OnUpdateCanMove); @@ -34,6 +35,11 @@ public override void Initialize() SubscribeLocalEvent<AdminFrozenComponent, SpeakAttemptEvent>(OnSpeakAttempt); } + private void OnInteractAttempt(Entity<AdminFrozenComponent> ent, ref InteractionAttemptEvent args) + { + args.Cancelled = true; + } + private void OnSpeakAttempt(EntityUid uid, AdminFrozenComponent component, SpeakAttemptEvent args) { if (!component.Muted) diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index 1c8e2ef2b0..dbc2ba06c9 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -84,7 +84,7 @@ public override void Initialize() SubscribeLocalEvent<CuffableComponent, PickupAttemptEvent>(CheckAct); SubscribeLocalEvent<CuffableComponent, AttackAttemptEvent>(CheckAct); SubscribeLocalEvent<CuffableComponent, UseAttemptEvent>(CheckAct); - SubscribeLocalEvent<CuffableComponent, InteractionAttemptEvent>(CheckAct); + SubscribeLocalEvent<CuffableComponent, InteractionAttemptEvent>(CheckInteract); SubscribeLocalEvent<HandcuffComponent, AfterInteractEvent>(OnCuffAfterInteract); SubscribeLocalEvent<HandcuffComponent, MeleeHitEvent>(OnCuffMeleeHit); @@ -92,6 +92,12 @@ public override void Initialize() SubscribeLocalEvent<HandcuffComponent, VirtualItemDeletedEvent>(OnCuffVirtualItemDeleted); } + private void CheckInteract(Entity<CuffableComponent> ent, ref InteractionAttemptEvent args) + { + if (!ent.Comp.CanStillInteract) + args.Cancelled = true; + } + private void OnUncuffAttempt(ref UncuffAttemptEvent args) { if (args.Cancelled) diff --git a/Content.Shared/Ghost/SharedGhostSystem.cs b/Content.Shared/Ghost/SharedGhostSystem.cs index 6f7370fe58..091775b6c2 100644 --- a/Content.Shared/Ghost/SharedGhostSystem.cs +++ b/Content.Shared/Ghost/SharedGhostSystem.cs @@ -20,13 +20,19 @@ public override void Initialize() { base.Initialize(); SubscribeLocalEvent<GhostComponent, UseAttemptEvent>(OnAttempt); - SubscribeLocalEvent<GhostComponent, InteractionAttemptEvent>(OnAttempt); + SubscribeLocalEvent<GhostComponent, InteractionAttemptEvent>(OnAttemptInteract); SubscribeLocalEvent<GhostComponent, EmoteAttemptEvent>(OnAttempt); SubscribeLocalEvent<GhostComponent, DropAttemptEvent>(OnAttempt); SubscribeLocalEvent<GhostComponent, PickupAttemptEvent>(OnAttempt); SubscribeLocalEvent<GhostComponent, InteractionVerbAttemptEvent>(OnAttempt); } + private void OnAttemptInteract(Entity<GhostComponent> ent, ref InteractionAttemptEvent args) + { + if (!ent.Comp.CanGhostInteract) + args.Cancelled = true; + } + private void OnAttempt(EntityUid uid, GhostComponent component, CancellableEntityEventArgs args) { if (!component.CanGhostInteract) diff --git a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs index 0024811c36..a04c053635 100644 --- a/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs +++ b/Content.Shared/Interaction/Events/InteractionAttemptEvent.cs @@ -3,39 +3,33 @@ /// <summary> /// Event raised directed at a user to see if they can perform a generic interaction. /// </summary> - public sealed class InteractionAttemptEvent : CancellableEntityEventArgs + [ByRefEvent] + public struct InteractionAttemptEvent(EntityUid uid, EntityUid? target) { - public InteractionAttemptEvent(EntityUid uid, EntityUid? target) - { - Uid = uid; - Target = target; - } - - public EntityUid Uid { get; } - public EntityUid? Target { get; } + public bool Cancelled; + public readonly EntityUid Uid = uid; + public readonly EntityUid? Target = target; } /// <summary> /// Raised to determine whether an entity is conscious to perform an action. /// </summary> - public sealed class ConsciousAttemptEvent(EntityUid Uid) : CancellableEntityEventArgs + [ByRefEvent] + public struct ConsciousAttemptEvent(EntityUid uid) { - public EntityUid Uid { get; } = Uid; + public bool Cancelled; + public readonly EntityUid Uid = uid; } /// <summary> /// Event raised directed at the target entity of an interaction to see if the user is allowed to perform some /// generic interaction. /// </summary> - public sealed class GettingInteractedWithAttemptEvent : CancellableEntityEventArgs + [ByRefEvent] + public struct GettingInteractedWithAttemptEvent(EntityUid uid, EntityUid? target) { - public GettingInteractedWithAttemptEvent(EntityUid uid, EntityUid? target) - { - Uid = uid; - Target = target; - } - - public EntityUid Uid { get; } - public EntityUid? Target { get; } + public bool Cancelled; + public readonly EntityUid Uid = uid; + public readonly EntityUid? Target = target; } } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs index 9a84789adf..a682bf9815 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs @@ -6,6 +6,7 @@ namespace Content.Shared.Interaction; +// TODO deduplicate with AdminFrozenComponent /// <summary> /// Handles <see cref="BlockMovementComponent"/>, which prevents various /// kinds of movement and interactions when attached to an entity. @@ -16,7 +17,7 @@ public void InitializeBlocking() { SubscribeLocalEvent<BlockMovementComponent, UpdateCanMoveEvent>(OnMoveAttempt); SubscribeLocalEvent<BlockMovementComponent, UseAttemptEvent>(CancelEvent); - SubscribeLocalEvent<BlockMovementComponent, InteractionAttemptEvent>(CancelEvent); + SubscribeLocalEvent<BlockMovementComponent, InteractionAttemptEvent>(CancelInteractEvent); SubscribeLocalEvent<BlockMovementComponent, DropAttemptEvent>(CancelEvent); SubscribeLocalEvent<BlockMovementComponent, PickupAttemptEvent>(CancelEvent); SubscribeLocalEvent<BlockMovementComponent, ChangeDirectionAttemptEvent>(CancelEvent); @@ -25,6 +26,11 @@ public void InitializeBlocking() SubscribeLocalEvent<BlockMovementComponent, ComponentShutdown>(OnBlockingShutdown); } + private void CancelInteractEvent(Entity<BlockMovementComponent> ent, ref InteractionAttemptEvent args) + { + args.Cancelled = true; + } + private void OnMoveAttempt(EntityUid uid, BlockMovementComponent component, UpdateCanMoveEvent args) { if (component.LifeStage > ComponentLifeStage.Running) diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs index a467c89364..f5acced4db 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -33,7 +33,7 @@ private void SubscribeEvents() SubscribeLocalEvent<MobStateComponent, ChangeDirectionAttemptEvent>(OnDirectionAttempt); SubscribeLocalEvent<MobStateComponent, UseAttemptEvent>(CheckAct); SubscribeLocalEvent<MobStateComponent, AttackAttemptEvent>(CheckAct); - SubscribeLocalEvent<MobStateComponent, ConsciousAttemptEvent>(CheckAct); + SubscribeLocalEvent<MobStateComponent, ConsciousAttemptEvent>(CheckConcious); SubscribeLocalEvent<MobStateComponent, ThrowAttemptEvent>(CheckAct); SubscribeLocalEvent<MobStateComponent, SpeakAttemptEvent>(OnSpeakAttempt); SubscribeLocalEvent<MobStateComponent, IsEquippingAttemptEvent>(OnEquipAttempt); @@ -91,6 +91,17 @@ private void OnUnbuckleAttempt(Entity<MobStateComponent> ent, ref UnbuckleAttemp args.Cancelled = true; } + private void CheckConcious(Entity<MobStateComponent> ent, ref ConsciousAttemptEvent args) + { + switch (ent.Comp.CurrentState) + { + case MobState.Dead: + case MobState.Critical: + args.Cancelled = true; + break; + } + } + private void OnStateExitSubscribers(EntityUid target, MobStateComponent component, MobState state) { switch (state) diff --git a/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs b/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs index 430c2b1b17..e3fa21ed37 100644 --- a/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs +++ b/Content.Shared/Puppet/SharedVentriloquistPuppetSystem.cs @@ -7,6 +7,7 @@ namespace Content.Shared.Puppet; +// TODO deduplicate with BlockMovementComponent public abstract class SharedVentriloquistPuppetSystem : EntitySystem { [Dependency] private readonly ActionBlockerSystem _blocker = default!; @@ -15,7 +16,7 @@ public override void Initialize() { base.Initialize(); SubscribeLocalEvent<VentriloquistPuppetComponent, UseAttemptEvent>(Cancel); - SubscribeLocalEvent<VentriloquistPuppetComponent, InteractionAttemptEvent>(Cancel); + SubscribeLocalEvent<VentriloquistPuppetComponent, InteractionAttemptEvent>(CancelInteract); SubscribeLocalEvent<VentriloquistPuppetComponent, DropAttemptEvent>(Cancel); SubscribeLocalEvent<VentriloquistPuppetComponent, PickupAttemptEvent>(Cancel); SubscribeLocalEvent<VentriloquistPuppetComponent, UpdateCanMoveEvent>(Cancel); @@ -24,6 +25,11 @@ public override void Initialize() SubscribeLocalEvent<VentriloquistPuppetComponent, ComponentStartup>(OnStartup); } + private void CancelInteract(Entity<VentriloquistPuppetComponent> ent, ref InteractionAttemptEvent args) + { + args.Cancelled = true; + } + private void OnStartup(EntityUid uid, VentriloquistPuppetComponent component, ComponentStartup args) { _blocker.UpdateCanMove(uid); @@ -33,4 +39,4 @@ private void Cancel<T>(EntityUid uid, VentriloquistPuppetComponent component, T { args.Cancel(); } -} \ No newline at end of file +} diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index 9d2e8d3dde..be5630cea0 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -68,7 +68,7 @@ public override void Initialize() // Attempt event subscriptions. SubscribeLocalEvent<StunnedComponent, ChangeDirectionAttemptEvent>(OnAttempt); SubscribeLocalEvent<StunnedComponent, UpdateCanMoveEvent>(OnMoveAttempt); - SubscribeLocalEvent<StunnedComponent, InteractionAttemptEvent>(OnAttempt); + SubscribeLocalEvent<StunnedComponent, InteractionAttemptEvent>(OnAttemptInteract); SubscribeLocalEvent<StunnedComponent, UseAttemptEvent>(OnAttempt); SubscribeLocalEvent<StunnedComponent, ThrowAttemptEvent>(OnAttempt); SubscribeLocalEvent<StunnedComponent, DropAttemptEvent>(OnAttempt); @@ -79,7 +79,10 @@ public override void Initialize() SubscribeLocalEvent<MobStateComponent, MobStateChangedEvent>(OnMobStateChanged); } - + private void OnAttemptInteract(Entity<StunnedComponent> ent, ref InteractionAttemptEvent args) + { + args.Cancelled = true; + } private void OnMobStateChanged(EntityUid uid, MobStateComponent component, MobStateChangedEvent args) { diff --git a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs index ba78ff651f..cebc84ecb9 100644 --- a/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs +++ b/Content.Shared/SubFloor/SharedSubFloorHideSystem.cs @@ -45,11 +45,11 @@ private void OnAttackAttempt(EntityUid uid, SubFloorHideComponent component, ref args.Cancelled = true; } - private void OnInteractionAttempt(EntityUid uid, SubFloorHideComponent component, GettingInteractedWithAttemptEvent args) + private void OnInteractionAttempt(EntityUid uid, SubFloorHideComponent component, ref GettingInteractedWithAttemptEvent args) { // No interactions with entities hidden under floor tiles. if (component.BlockInteractions && component.IsUnderCover) - args.Cancel(); + args.Cancelled = true; } private void OnSubFloorStarted(EntityUid uid, SubFloorHideComponent component, ComponentStartup _) From bdafdeb7052aefa6e0f749f2e04c28bf33a78f4b Mon Sep 17 00:00:00 2001 From: Tayrtahn <tayrtahn@gmail.com> Date: Thu, 13 Jun 2024 22:15:29 -0400 Subject: [PATCH 05/38] Move SleepingSystem to Shared & cleanup (#28672) * Move SleepingSystem to Shared & cleanup * Remove empty OnShutdown handler --- Content.Client/Bed/SleepingSystem.cs | 8 - Content.Server/Bed/BedSystem.cs | 1 - Content.Server/Bed/Sleep/SleepingSystem.cs | 261 --------------- .../Damage/ForceSay/DamageForceSaySystem.cs | 1 + .../Bed/Sleep/SharedSleepingSystem.cs | 102 ------ Content.Shared/Bed/Sleep/SleepingComponent.cs | 31 +- Content.Shared/Bed/Sleep/SleepingSystem.cs | 314 ++++++++++++++++++ .../Bed/Sleep}/SnoringComponent.cs | 6 +- 8 files changed, 340 insertions(+), 384 deletions(-) delete mode 100644 Content.Client/Bed/SleepingSystem.cs delete mode 100644 Content.Server/Bed/Sleep/SleepingSystem.cs delete mode 100644 Content.Shared/Bed/Sleep/SharedSleepingSystem.cs create mode 100644 Content.Shared/Bed/Sleep/SleepingSystem.cs rename {Content.Server/Bed/Components => Content.Shared/Bed/Sleep}/SnoringComponent.cs (54%) diff --git a/Content.Client/Bed/SleepingSystem.cs b/Content.Client/Bed/SleepingSystem.cs deleted file mode 100644 index addf855bf3..0000000000 --- a/Content.Client/Bed/SleepingSystem.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Content.Server.Bed.Sleep; - -namespace Content.Client.Bed; - -public sealed class SleepingSystem : SharedSleepingSystem -{ - -} diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs index 2335859f0b..7220c04ea8 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Actions; using Content.Server.Bed.Components; -using Content.Server.Bed.Sleep; using Content.Server.Body.Systems; using Content.Server.Construction; using Content.Server.Power.Components; diff --git a/Content.Server/Bed/Sleep/SleepingSystem.cs b/Content.Server/Bed/Sleep/SleepingSystem.cs deleted file mode 100644 index 47966c4814..0000000000 --- a/Content.Server/Bed/Sleep/SleepingSystem.cs +++ /dev/null @@ -1,261 +0,0 @@ -using Content.Server.Popups; -using Content.Server.Sound; -using Content.Shared.Sound.Components; -using Content.Shared.Actions; -using Content.Shared.Audio; -using Content.Shared.Bed.Sleep; -using Content.Shared.Damage; -using Content.Shared.Examine; -using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; -using Content.Shared.Slippery; -using Content.Shared.StatusEffect; -using Content.Shared.Stunnable; -using Content.Shared.Verbs; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; - -namespace Content.Server.Bed.Sleep -{ - public sealed class SleepingSystem : SharedSleepingSystem - { - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; - [Dependency] private readonly EmitSoundSystem _emitSound = default!; - - [ValidatePrototypeId<EntityPrototype>] public const string SleepActionId = "ActionSleep"; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent<MobStateComponent, SleepStateChangedEvent>(OnSleepStateChanged); - SubscribeLocalEvent<SleepingComponent, DamageChangedEvent>(OnDamageChanged); - SubscribeLocalEvent<MobStateComponent, SleepActionEvent>(OnSleepAction); - SubscribeLocalEvent<ActionsContainerComponent, SleepActionEvent>(OnBedSleepAction); - SubscribeLocalEvent<MobStateComponent, WakeActionEvent>(OnWakeAction); - SubscribeLocalEvent<SleepingComponent, MobStateChangedEvent>(OnMobStateChanged); - SubscribeLocalEvent<SleepingComponent, GetVerbsEvent<AlternativeVerb>>(AddWakeVerb); - SubscribeLocalEvent<SleepingComponent, InteractHandEvent>(OnInteractHand); - SubscribeLocalEvent<SleepingComponent, ExaminedEvent>(OnExamined); - SubscribeLocalEvent<SleepingComponent, SlipAttemptEvent>(OnSlip); - SubscribeLocalEvent<SleepingComponent, ConsciousAttemptEvent>(OnConsciousAttempt); - SubscribeLocalEvent<ForcedSleepingComponent, ComponentInit>(OnInit); - } - - /// <summary> - /// when sleeping component is added or removed, we do some stuff with other components. - /// </summary> - private void OnSleepStateChanged(EntityUid uid, MobStateComponent component, SleepStateChangedEvent args) - { - if (args.FellAsleep) - { - // Expiring status effects would remove the components needed for sleeping - _statusEffectsSystem.TryRemoveStatusEffect(uid, "Stun"); - _statusEffectsSystem.TryRemoveStatusEffect(uid, "KnockedDown"); - - EnsureComp<StunnedComponent>(uid); - EnsureComp<KnockedDownComponent>(uid); - - if (TryComp<SleepEmitSoundComponent>(uid, out var sleepSound)) - { - var emitSound = EnsureComp<SpamEmitSoundComponent>(uid); - if (HasComp<SnoringComponent>(uid)) - { - emitSound.Sound = sleepSound.Snore; - } - emitSound.MinInterval = sleepSound.Interval; - emitSound.MaxInterval = sleepSound.MaxInterval; - emitSound.PopUp = sleepSound.PopUp; - } - - return; - } - - RemComp<StunnedComponent>(uid); - RemComp<KnockedDownComponent>(uid); - RemComp<SpamEmitSoundComponent>(uid); - } - - /// <summary> - /// Wake up on taking an instance of damage at least the value of WakeThreshold. - /// </summary> - private void OnDamageChanged(EntityUid uid, SleepingComponent component, DamageChangedEvent args) - { - if (!args.DamageIncreased || args.DamageDelta == null) - return; - - /* Shitmed Change Start - Surgery needs this, sorry! If the nocturine gamers get too feisty - I'll probably just increase the threshold */ - - if (args.DamageDelta.GetTotal() >= component.WakeThreshold - && !HasComp<ForcedSleepingComponent>(uid)) - TryWaking(uid, component); - - // Shitmed Change End - - } - - private void OnSleepAction(EntityUid uid, MobStateComponent component, SleepActionEvent args) - { - TrySleeping(uid); - } - - private void OnBedSleepAction(EntityUid uid, ActionsContainerComponent component, SleepActionEvent args) - { - TrySleeping(args.Performer); - } - - private void OnWakeAction(EntityUid uid, MobStateComponent component, WakeActionEvent args) - { - if (!TryWakeCooldown(uid)) - return; - - if (TryWaking(uid)) - args.Handled = true; - } - - /// <summary> - /// In crit, we wake up if we are not being forced to sleep. - /// And, you can't sleep when dead... - /// </summary> - private void OnMobStateChanged(EntityUid uid, SleepingComponent component, MobStateChangedEvent args) - { - if (args.NewMobState == MobState.Dead) - { - RemComp<SpamEmitSoundComponent>(uid); - RemComp<SleepingComponent>(uid); - return; - } - if (TryComp<SpamEmitSoundComponent>(uid, out var spam)) - _emitSound.SetEnabled((uid, spam), args.NewMobState == MobState.Alive); - } - - private void AddWakeVerb(EntityUid uid, SleepingComponent component, GetVerbsEvent<AlternativeVerb> args) - { - if (!args.CanInteract || !args.CanAccess) - return; - - AlternativeVerb verb = new() - { - Act = () => - { - if (!TryWakeCooldown(uid)) - return; - - TryWaking(args.Target, user: args.User); - }, - Text = Loc.GetString("action-name-wake"), - Priority = 2 - }; - - args.Verbs.Add(verb); - } - - /// <summary> - /// When you click on a sleeping person with an empty hand, try to wake them. - /// </summary> - private void OnInteractHand(EntityUid uid, SleepingComponent component, InteractHandEvent args) - { - args.Handled = true; - - if (!TryWakeCooldown(uid)) - return; - - TryWaking(args.Target, user: args.User); - } - - private void OnExamined(EntityUid uid, SleepingComponent component, ExaminedEvent args) - { - if (args.IsInDetailsRange) - { - args.PushMarkup(Loc.GetString("sleep-examined", ("target", Identity.Entity(uid, EntityManager)))); - } - } - - private void OnSlip(EntityUid uid, SleepingComponent component, SlipAttemptEvent args) - { - args.Cancel(); - } - - private void OnConsciousAttempt(EntityUid uid, SleepingComponent component, ConsciousAttemptEvent args) - { - args.Cancel(); - } - - - private void OnInit(EntityUid uid, ForcedSleepingComponent component, ComponentInit args) - { - TrySleeping(uid); - } - - /// <summary> - /// Try sleeping. Only mobs can sleep. - /// </summary> - public bool TrySleeping(EntityUid uid) - { - if (!HasComp<MobStateComponent>(uid)) - return false; - - var tryingToSleepEvent = new TryingToSleepEvent(uid); - RaiseLocalEvent(uid, ref tryingToSleepEvent); - if (tryingToSleepEvent.Cancelled) - return false; - - EnsureComp<SleepingComponent>(uid); - return true; - } - - private bool TryWakeCooldown(EntityUid uid, SleepingComponent? component = null) - { - if (!Resolve(uid, ref component, false)) - return false; - - var curTime = _gameTiming.CurTime; - - if (curTime < component.CoolDownEnd) - { - return false; - } - - component.CoolDownEnd = curTime + component.Cooldown; - return true; - } - - /// <summary> - /// Try to wake up. - /// </summary> - public bool TryWaking(EntityUid uid, SleepingComponent? component = null, bool force = false, EntityUid? user = null) - { - if (!Resolve(uid, ref component, false)) - return false; - - if (!force && HasComp<ForcedSleepingComponent>(uid)) - { - if (user != null) - { - _audio.PlayPvs("/Audio/Effects/thudswoosh.ogg", uid, AudioHelpers.WithVariation(0.05f, _robustRandom)); - _popupSystem.PopupEntity(Loc.GetString("wake-other-failure", ("target", Identity.Entity(uid, EntityManager))), uid, Filter.Entities(user.Value), true, Shared.Popups.PopupType.SmallCaution); - } - return false; - } - - if (user != null) - { - _audio.PlayPvs("/Audio/Effects/thudswoosh.ogg", uid, AudioHelpers.WithVariation(0.05f, _robustRandom)); - _popupSystem.PopupEntity(Loc.GetString("wake-other-success", ("target", Identity.Entity(uid, EntityManager))), uid, Filter.Entities(user.Value), true); - } - RemComp<SleepingComponent>(uid); - return true; - } - } -} diff --git a/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs b/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs index 186fc91c46..bc61c5d141 100644 --- a/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs +++ b/Content.Server/Damage/ForceSay/DamageForceSaySystem.cs @@ -1,3 +1,4 @@ +using Content.Shared.Bed.Sleep; using Content.Shared.Damage; using Content.Shared.Damage.ForceSay; using Content.Shared.FixedPoint; diff --git a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs b/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs deleted file mode 100644 index aa5578a3a9..0000000000 --- a/Content.Shared/Bed/Sleep/SharedSleepingSystem.cs +++ /dev/null @@ -1,102 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Bed.Sleep; -using Content.Shared.Damage.ForceSay; -using Content.Shared.Eye.Blinding.Systems; -using Content.Shared.Pointing; -using Content.Shared.Speech; -using Robust.Shared.Network; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; - -namespace Content.Server.Bed.Sleep -{ - public abstract class SharedSleepingSystem : EntitySystem - { - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - [Dependency] private readonly BlindableSystem _blindableSystem = default!; - - [ValidatePrototypeId<EntityPrototype>] private const string WakeActionId = "ActionWake"; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent<SleepingComponent, MapInitEvent>(OnMapInit); - SubscribeLocalEvent<SleepingComponent, ComponentShutdown>(OnShutdown); - SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt); - SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt); - SubscribeLocalEvent<SleepingComponent, PointAttemptEvent>(OnPointAttempt); - } - - - private void OnMapInit(EntityUid uid, SleepingComponent component, MapInitEvent args) - { - component.SleepingSince = _gameTiming.CurTime; - - var ev = new SleepStateChangedEvent(true); - RaiseLocalEvent(uid, ev); - _blindableSystem.UpdateIsBlind(uid); - _actionsSystem.AddAction(uid, ref component.WakeAction, WakeActionId, uid); - - // TODO remove hardcoded time. - _actionsSystem.SetCooldown(component.WakeAction, _gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(2f)); - } - - private void OnShutdown(EntityUid uid, SleepingComponent component, ComponentShutdown args) - { - _actionsSystem.RemoveAction(uid, component.WakeAction); - var ev = new SleepStateChangedEvent(false) - { - TimeSlept = _gameTiming.CurTime - component.SleepingSince - }; - RaiseLocalEvent(uid, ev); - _blindableSystem.UpdateIsBlind(uid); - } - - private void OnSpeakAttempt(EntityUid uid, SleepingComponent component, SpeakAttemptEvent args) - { - // TODO reduce duplication of this behavior with MobStateSystem somehow - if (HasComp<AllowNextCritSpeechComponent>(uid)) - { - RemCompDeferred<AllowNextCritSpeechComponent>(uid); - return; - } - - args.Cancel(); - } - - private void OnSeeAttempt(EntityUid uid, SleepingComponent component, CanSeeAttemptEvent args) - { - if (component.LifeStage <= ComponentLifeStage.Running) - args.Cancel(); - } - - private void OnPointAttempt(EntityUid uid, SleepingComponent component, PointAttemptEvent args) - { - args.Cancel(); - } - } -} - - -public sealed partial class SleepActionEvent : InstantActionEvent {} - -public sealed partial class WakeActionEvent : InstantActionEvent {} - -/// <summary> -/// Raised on an entity when they fall asleep or wake up. -/// </summary> -public sealed class SleepStateChangedEvent : EntityEventArgs -{ - public bool FellAsleep = false; - - /// <summary> - /// The amount of time this entity slept for. Null if <see cref="FellAsleep"/> is true. - /// </summary> - public TimeSpan? TimeSlept; - - public SleepStateChangedEvent(bool fellAsleep) - { - FellAsleep = fellAsleep; - } -} diff --git a/Content.Shared/Bed/Sleep/SleepingComponent.cs b/Content.Shared/Bed/Sleep/SleepingComponent.cs index e87aca2203..e43c71769a 100644 --- a/Content.Shared/Bed/Sleep/SleepingComponent.cs +++ b/Content.Shared/Bed/Sleep/SleepingComponent.cs @@ -1,37 +1,48 @@ using Content.Shared.FixedPoint; +using Robust.Shared.Audio; using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Bed.Sleep; /// <summary> /// Added to entities when they go to sleep. /// </summary> -[NetworkedComponent, RegisterComponent, AutoGenerateComponentPause(Dirty = true)] +[NetworkedComponent, RegisterComponent] +[AutoGenerateComponentState, AutoGenerateComponentPause(Dirty = true)] public sealed partial class SleepingComponent : Component { /// <summary> /// How much damage of any type it takes to wake this entity. /// </summary> - [DataField("wakeThreshold")] + [DataField] public FixedPoint2 WakeThreshold = FixedPoint2.New(2); /// <summary> /// Cooldown time between users hand interaction. /// </summary> - [DataField("cooldown")] - [ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan Cooldown = TimeSpan.FromSeconds(1f); - [DataField("cooldownEnd", customTypeSerializer:typeof(TimeOffsetSerializer))] - [AutoPausedField] - public TimeSpan CoolDownEnd; - - [DataField("wakeAction")] public EntityUid? WakeAction; + [DataField] + [AutoNetworkedField, AutoPausedField] + public TimeSpan CooldownEnd; /// <summary> /// The moment this entity went to sleep. Initialized on MapInit. /// </summary> [DataField] public TimeSpan SleepingSince; + + [DataField] + [AutoNetworkedField] + public EntityUid? WakeAction; + + /// <summary> + /// Sound to play when another player attempts to wake this entity. + /// </summary> + [DataField] + public SoundSpecifier WakeAttemptSound = new SoundPathSpecifier("/Audio/Effects/thudswoosh.ogg") + { + Params = AudioParams.Default.WithVariation(0.05f) + }; } diff --git a/Content.Shared/Bed/Sleep/SleepingSystem.cs b/Content.Shared/Bed/Sleep/SleepingSystem.cs new file mode 100644 index 0000000000..aac3e7bb18 --- /dev/null +++ b/Content.Shared/Bed/Sleep/SleepingSystem.cs @@ -0,0 +1,314 @@ +using Content.Shared.Actions; +using Content.Shared.Damage; +using Content.Shared.Damage.ForceSay; +using Content.Shared.Examine; +using Content.Shared.Eye.Blinding.Systems; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Interaction.Events; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; +using Content.Shared.Pointing; +using Content.Shared.Popups; +using Content.Shared.Slippery; +using Content.Shared.Sound; +using Content.Shared.Sound.Components; +using Content.Shared.Speech; +using Content.Shared.StatusEffect; +using Content.Shared.Stunnable; +using Content.Shared.Verbs; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Shared.Bed.Sleep; + +public sealed partial class SleepingSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; + [Dependency] private readonly BlindableSystem _blindableSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedEmitSoundSystem _emitSound = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; + + public static readonly ProtoId<EntityPrototype> SleepActionId = "ActionSleep"; + public static readonly ProtoId<EntityPrototype> WakeActionId = "ActionWake"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent<ActionsContainerComponent, SleepActionEvent>(OnBedSleepAction); + + SubscribeLocalEvent<MobStateComponent, SleepStateChangedEvent>(OnSleepStateChanged); + SubscribeLocalEvent<MobStateComponent, WakeActionEvent>(OnWakeAction); + SubscribeLocalEvent<MobStateComponent, SleepActionEvent>(OnSleepAction); + + SubscribeLocalEvent<SleepingComponent, DamageChangedEvent>(OnDamageChanged); + SubscribeLocalEvent<SleepingComponent, MobStateChangedEvent>(OnMobStateChanged); + SubscribeLocalEvent<SleepingComponent, MapInitEvent>(OnMapInit); + SubscribeLocalEvent<SleepingComponent, SpeakAttemptEvent>(OnSpeakAttempt); + SubscribeLocalEvent<SleepingComponent, CanSeeAttemptEvent>(OnSeeAttempt); + SubscribeLocalEvent<SleepingComponent, PointAttemptEvent>(OnPointAttempt); + SubscribeLocalEvent<SleepingComponent, SlipAttemptEvent>(OnSlip); + SubscribeLocalEvent<SleepingComponent, ConsciousAttemptEvent>(OnConsciousAttempt); + SubscribeLocalEvent<SleepingComponent, ExaminedEvent>(OnExamined); + SubscribeLocalEvent<SleepingComponent, GetVerbsEvent<AlternativeVerb>>(AddWakeVerb); + SubscribeLocalEvent<SleepingComponent, InteractHandEvent>(OnInteractHand); + + SubscribeLocalEvent<ForcedSleepingComponent, ComponentInit>(OnInit); + } + + private void OnBedSleepAction(Entity<ActionsContainerComponent> ent, ref SleepActionEvent args) + { + TrySleeping(args.Performer); + } + + private void OnWakeAction(Entity<MobStateComponent> ent, ref WakeActionEvent args) + { + if (TryWakeWithCooldown(ent.Owner)) + args.Handled = true; + } + + private void OnSleepAction(Entity<MobStateComponent> ent, ref SleepActionEvent args) + { + TrySleeping((ent, ent.Comp)); + } + + /// <summary> + /// when sleeping component is added or removed, we do some stuff with other components. + /// </summary> + private void OnSleepStateChanged(Entity<MobStateComponent> ent, ref SleepStateChangedEvent args) + { + if (args.FellAsleep) + { + // Expiring status effects would remove the components needed for sleeping + _statusEffectsSystem.TryRemoveStatusEffect(ent.Owner, "Stun"); + _statusEffectsSystem.TryRemoveStatusEffect(ent.Owner, "KnockedDown"); + + EnsureComp<StunnedComponent>(ent); + EnsureComp<KnockedDownComponent>(ent); + + if (TryComp<SleepEmitSoundComponent>(ent, out var sleepSound)) + { + var emitSound = EnsureComp<SpamEmitSoundComponent>(ent); + if (HasComp<SnoringComponent>(ent)) + { + emitSound.Sound = sleepSound.Snore; + } + emitSound.MinInterval = sleepSound.Interval; + emitSound.MaxInterval = sleepSound.MaxInterval; + emitSound.PopUp = sleepSound.PopUp; + Dirty(ent.Owner, emitSound); + } + + return; + } + + RemComp<StunnedComponent>(ent); + RemComp<KnockedDownComponent>(ent); + RemComp<SpamEmitSoundComponent>(ent); + } + + private void OnMapInit(Entity<SleepingComponent> ent, ref MapInitEvent args) + { + var ev = new SleepStateChangedEvent(true); + RaiseLocalEvent(ent, ref ev); + _blindableSystem.UpdateIsBlind(ent.Owner); + _actionsSystem.AddAction(ent, ref ent.Comp.WakeAction, WakeActionId, ent); + + // TODO remove hardcoded time. + _actionsSystem.SetCooldown(ent.Comp.WakeAction, _gameTiming.CurTime, _gameTiming.CurTime + TimeSpan.FromSeconds(2f)); + } + + private void OnSpeakAttempt(Entity<SleepingComponent> ent, ref SpeakAttemptEvent args) + { + // TODO reduce duplication of this behavior with MobStateSystem somehow + if (HasComp<AllowNextCritSpeechComponent>(ent)) + { + RemCompDeferred<AllowNextCritSpeechComponent>(ent); + return; + } + + args.Cancel(); + } + + private void OnSeeAttempt(Entity<SleepingComponent> ent, ref CanSeeAttemptEvent args) + { + if (ent.Comp.LifeStage <= ComponentLifeStage.Running) + args.Cancel(); + } + + private void OnPointAttempt(Entity<SleepingComponent> ent, ref PointAttemptEvent args) + { + args.Cancel(); + } + + private void OnSlip(Entity<SleepingComponent> ent, ref SlipAttemptEvent args) + { + args.Cancel(); + } + + private void OnConsciousAttempt(Entity<SleepingComponent> ent, ref ConsciousAttemptEvent args) + { + args.Cancel(); + } + + private void OnExamined(Entity<SleepingComponent> ent, ref ExaminedEvent args) + { + if (args.IsInDetailsRange) + { + args.PushMarkup(Loc.GetString("sleep-examined", ("target", Identity.Entity(ent, EntityManager)))); + } + } + + private void AddWakeVerb(Entity<SleepingComponent> ent, ref GetVerbsEvent<AlternativeVerb> args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + var target = args.Target; + var user = args.User; + AlternativeVerb verb = new() + { + Act = () => + { + TryWakeWithCooldown((ent, ent.Comp), user: user); + }, + Text = Loc.GetString("action-name-wake"), + Priority = 2 + }; + + args.Verbs.Add(verb); + } + + /// <summary> + /// When you click on a sleeping person with an empty hand, try to wake them. + /// </summary> + private void OnInteractHand(Entity<SleepingComponent> ent, ref InteractHandEvent args) + { + args.Handled = true; + + TryWakeWithCooldown((ent, ent.Comp), args.User); + } + + /// <summary> + /// Wake up on taking an instance of damage at least the value of WakeThreshold. + /// </summary> + private void OnDamageChanged(Entity<SleepingComponent> ent, ref DamageChangedEvent args) + { + if (!args.DamageIncreased || args.DamageDelta == null) + return; + + if (args.DamageDelta.GetTotal() >= ent.Comp.WakeThreshold) + TryWaking((ent, ent.Comp)); + } + + /// <summary> + /// In crit, we wake up if we are not being forced to sleep. + /// And, you can't sleep when dead... + /// </summary> + private void OnMobStateChanged(Entity<SleepingComponent> ent, ref MobStateChangedEvent args) + { + if (args.NewMobState == MobState.Dead) + { + RemComp<SpamEmitSoundComponent>(ent); + RemComp<SleepingComponent>(ent); + return; + } + if (TryComp<SpamEmitSoundComponent>(ent, out var spam)) + _emitSound.SetEnabled((ent, spam), args.NewMobState == MobState.Alive); + } + + private void OnInit(Entity<ForcedSleepingComponent> ent, ref ComponentInit args) + { + TrySleeping(ent.Owner); + } + + private void Wake(Entity<SleepingComponent> ent) + { + RemComp<SleepingComponent>(ent); + _actionsSystem.RemoveAction(ent, ent.Comp.WakeAction); + + var ev = new SleepStateChangedEvent(false); + RaiseLocalEvent(ent, ref ev); + + _blindableSystem.UpdateIsBlind(ent.Owner); + } + + /// <summary> + /// Try sleeping. Only mobs can sleep. + /// </summary> + public bool TrySleeping(Entity<MobStateComponent?> ent) + { + if (!Resolve(ent, ref ent.Comp, logMissing: false)) + return false; + + var tryingToSleepEvent = new TryingToSleepEvent(ent); + RaiseLocalEvent(ent, ref tryingToSleepEvent); + if (tryingToSleepEvent.Cancelled) + return false; + + EnsureComp<SleepingComponent>(ent); + return true; + } + + /// <summary> + /// Tries to wake up <paramref name="ent"/>, with a cooldown between attempts to prevent spam. + /// </summary> + public bool TryWakeWithCooldown(Entity<SleepingComponent?> ent, EntityUid? user = null) + { + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + var curTime = _gameTiming.CurTime; + + if (curTime < ent.Comp.CooldownEnd) + return false; + + ent.Comp.CooldownEnd = curTime + ent.Comp.Cooldown; + Dirty(ent, ent.Comp); + return TryWaking(ent, user: user); + } + + /// <summary> + /// Try to wake up <paramref name="ent"/>. + /// </summary> + public bool TryWaking(Entity<SleepingComponent?> ent, bool force = false, EntityUid? user = null) + { + if (!Resolve(ent, ref ent.Comp, false)) + return false; + + if (!force && HasComp<ForcedSleepingComponent>(ent)) + { + if (user != null) + { + _audio.PlayPredicted(ent.Comp.WakeAttemptSound, ent, user); + _popupSystem.PopupClient(Loc.GetString("wake-other-failure", ("target", Identity.Entity(ent, EntityManager))), ent, user, PopupType.SmallCaution); + } + return false; + } + + if (user != null) + { + _audio.PlayPredicted(ent.Comp.WakeAttemptSound, ent, user); + _popupSystem.PopupClient(Loc.GetString("wake-other-success", ("target", Identity.Entity(ent, EntityManager))), ent, user); + } + + Wake((ent, ent.Comp)); + return true; + } +} + + +public sealed partial class SleepActionEvent : InstantActionEvent; + +public sealed partial class WakeActionEvent : InstantActionEvent; + +/// <summary> +/// Raised on an entity when they fall asleep or wake up. +/// </summary> +[ByRefEvent] +public record struct SleepStateChangedEvent(bool FellAsleep); diff --git a/Content.Server/Bed/Components/SnoringComponent.cs b/Content.Shared/Bed/Sleep/SnoringComponent.cs similarity index 54% rename from Content.Server/Bed/Components/SnoringComponent.cs rename to Content.Shared/Bed/Sleep/SnoringComponent.cs index 09f80327ba..2fe92951f0 100644 --- a/Content.Server/Bed/Components/SnoringComponent.cs +++ b/Content.Shared/Bed/Sleep/SnoringComponent.cs @@ -1,9 +1,11 @@ -namespace Content.Server.Bed.Sleep; +using Robust.Shared.GameStates; + +namespace Content.Shared.Bed.Sleep; /// <summary> /// This is used for the snoring trait. /// </summary> -[RegisterComponent] +[RegisterComponent, NetworkedComponent] public sealed partial class SnoringComponent : Component { From ede16f05928b602f70beb349b824ecfbf268269f Mon Sep 17 00:00:00 2001 From: sleepyyapril <flyingkarii@gmail.com> Date: Sat, 4 Jan 2025 14:02:06 -0400 Subject: [PATCH 06/38] Potentially re-add EE changes? --- Content.Shared/Bed/Sleep/SleepingSystem.cs | 31 +++++++++++++++++----- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/Content.Shared/Bed/Sleep/SleepingSystem.cs b/Content.Shared/Bed/Sleep/SleepingSystem.cs index aac3e7bb18..8e6f1193ac 100644 --- a/Content.Shared/Bed/Sleep/SleepingSystem.cs +++ b/Content.Shared/Bed/Sleep/SleepingSystem.cs @@ -33,8 +33,8 @@ public sealed partial class SleepingSystem : EntitySystem [Dependency] private readonly SharedEmitSoundSystem _emitSound = default!; [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; - public static readonly ProtoId<EntityPrototype> SleepActionId = "ActionSleep"; - public static readonly ProtoId<EntityPrototype> WakeActionId = "ActionWake"; + public static readonly EntProtoId SleepActionId = "ActionSleep"; + public static readonly EntProtoId WakeActionId = "ActionWake"; public override void Initialize() { @@ -114,6 +114,8 @@ private void OnSleepStateChanged(Entity<MobStateComponent> ent, ref SleepStateCh private void OnMapInit(Entity<SleepingComponent> ent, ref MapInitEvent args) { + ent.Comp.SleepingSince = _gameTiming.CurTime; + var ev = new SleepStateChangedEvent(true); RaiseLocalEvent(ent, ref ev); _blindableSystem.UpdateIsBlind(ent.Owner); @@ -153,7 +155,7 @@ private void OnSlip(Entity<SleepingComponent> ent, ref SlipAttemptEvent args) private void OnConsciousAttempt(Entity<SleepingComponent> ent, ref ConsciousAttemptEvent args) { - args.Cancel(); + args.Cancelled = true; } private void OnExamined(Entity<SleepingComponent> ent, ref ExaminedEvent args) @@ -202,7 +204,8 @@ private void OnDamageChanged(Entity<SleepingComponent> ent, ref DamageChangedEve if (!args.DamageIncreased || args.DamageDelta == null) return; - if (args.DamageDelta.GetTotal() >= ent.Comp.WakeThreshold) + if (args.DamageDelta.GetTotal() >= ent.Comp.WakeThreshold + && !HasComp<ForcedSleepingComponent>(ent)) TryWaking((ent, ent.Comp)); } @@ -232,7 +235,10 @@ private void Wake(Entity<SleepingComponent> ent) RemComp<SleepingComponent>(ent); _actionsSystem.RemoveAction(ent, ent.Comp.WakeAction); - var ev = new SleepStateChangedEvent(false); + var ev = new SleepStateChangedEvent(false) + { + TimeSlept = _gameTiming.CurTime - ent.Comp.SleepingSince + }; RaiseLocalEvent(ent, ref ev); _blindableSystem.UpdateIsBlind(ent.Owner); @@ -311,4 +317,17 @@ public sealed partial class WakeActionEvent : InstantActionEvent; /// Raised on an entity when they fall asleep or wake up. /// </summary> [ByRefEvent] -public record struct SleepStateChangedEvent(bool FellAsleep); +public record struct SleepStateChangedEvent +{ + public bool FellAsleep = false; + + /// <summary> + /// The amount of time this entity slept for. Null if <see cref="FellAsleep"/> is true. + /// </summary> + public TimeSpan? TimeSlept; + + public SleepStateChangedEvent(bool fellAsleep) + { + FellAsleep = fellAsleep; + } +} From 661bdbaf1c668337376d68ad29bbace978458ae1 Mon Sep 17 00:00:00 2001 From: sleepyyapril <flyingkarii@gmail.com> Date: Sat, 4 Jan 2025 14:18:12 -0400 Subject: [PATCH 07/38] Fix build errors --- Content.Server/Carrying/CarryingSystem.cs | 4 +-- Content.Server/Flight/FlightSystem.cs | 1 + .../Actions/ToggleSleepingAction.cs | 1 - .../Shadowkin/ShowEtherealSystem.cs | 4 +-- .../Systems/SiliconChargeDeathSystem.cs | 3 +-- .../Station/Systems/StationSpawningSystem.cs | 25 +------------------ .../Traits/Assorted/SingerSystem.cs | 1 + .../_Shitmed/Autodoc/Systems/AutodocSystem.cs | 5 ++-- .../Climbing/Systems/ClimbSystem.cs | 1 + .../Interaction/NoNormalInteractionSystem.cs | 2 +- .../Item/PseudoItem/SharedPseudoItemSystem.cs | 2 +- .../Shadowkin/SharedEtherealSystem.cs | 2 +- .../Prototypes/Roles/Jobs/Science/borg.yml | 6 ++--- 13 files changed, 18 insertions(+), 39 deletions(-) diff --git a/Content.Server/Carrying/CarryingSystem.cs b/Content.Server/Carrying/CarryingSystem.cs index 72cfeff91d..224e38bcbd 100644 --- a/Content.Server/Carrying/CarryingSystem.cs +++ b/Content.Server/Carrying/CarryingSystem.cs @@ -168,7 +168,7 @@ private void OnInteractionAttempt(EntityUid uid, BeingCarriedComponent component var targetParent = Transform(args.Target.Value).ParentUid; if (args.Target.Value != component.Carrier && targetParent != component.Carrier && targetParent != uid) - args.Cancel(); + args.Cancelled = true; } /// <summary> @@ -202,7 +202,7 @@ private void OnStandAttempt(EntityUid uid, BeingCarriedComponent component, Stan private void OnInteractedWith(EntityUid uid, BeingCarriedComponent component, GettingInteractedWithAttemptEvent args) { if (args.Uid != component.Carrier) - args.Cancel(); + args.Cancelled = true; } private void OnPullAttempt(EntityUid uid, BeingCarriedComponent component, PullAttemptEvent args) diff --git a/Content.Server/Flight/FlightSystem.cs b/Content.Server/Flight/FlightSystem.cs index 4493967fe9..5e3ef354f2 100644 --- a/Content.Server/Flight/FlightSystem.cs +++ b/Content.Server/Flight/FlightSystem.cs @@ -1,4 +1,5 @@ +using Content.Shared.Bed.Sleep; using Content.Shared.Cuffs.Components; using Content.Shared.Damage.Components; using Content.Shared.DoAfter; diff --git a/Content.Server/InteractionVerbs/Actions/ToggleSleepingAction.cs b/Content.Server/InteractionVerbs/Actions/ToggleSleepingAction.cs index 588853a8d4..97fd86fd91 100644 --- a/Content.Server/InteractionVerbs/Actions/ToggleSleepingAction.cs +++ b/Content.Server/InteractionVerbs/Actions/ToggleSleepingAction.cs @@ -1,4 +1,3 @@ -using Content.Server.Bed.Sleep; using Content.Shared.Bed.Sleep; using Content.Shared.InteractionVerbs; using Content.Shared.Mobs.Components; diff --git a/Content.Server/Shadowkin/ShowEtherealSystem.cs b/Content.Server/Shadowkin/ShowEtherealSystem.cs index 151c379afb..e908bd9ce7 100644 --- a/Content.Server/Shadowkin/ShowEtherealSystem.cs +++ b/Content.Server/Shadowkin/ShowEtherealSystem.cs @@ -70,7 +70,7 @@ private void OnInteractionAttempt(EntityUid uid, ShowEtherealComponent component || !HasComp<EtherealComponent>(args.Target)) return; - args.Cancel(); + args.Cancelled = true; if (_gameTiming.InPrediction) return; @@ -85,4 +85,4 @@ private void OnAttackAttempt(EntityUid uid, ShowEtherealComponent component, Att args.Cancel(); } -} \ No newline at end of file +} diff --git a/Content.Server/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs b/Content.Server/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs index d4d1db5ed9..41ee269788 100644 --- a/Content.Server/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs +++ b/Content.Server/Silicon/Charge/Systems/SiliconChargeDeathSystem.cs @@ -1,6 +1,5 @@ using Content.Server.Power.Components; using Content.Shared.Silicon.Systems; -using Content.Server.Bed.Sleep; using Content.Shared.Bed.Sleep; using Content.Server.Silicon.Charge; using Content.Server.Humanoid; @@ -63,7 +62,7 @@ private void SiliconDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadCo private void SiliconUnDead(EntityUid uid, SiliconDownOnDeadComponent siliconDeadComp, BatteryComponent? batteryComp, EntityUid batteryUid) { RemComp<ForcedSleepingComponent>(uid); - _sleep.TryWaking(uid, null, true); + _sleep.TryWaking(uid, true); siliconDeadComp.Dead = false; diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 0662c54bee..0bbfecb49d 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -131,7 +131,7 @@ public override void Initialize() /// <param name="station">The station this player is being spawned on.</param> /// <param name="entity">The entity to use, if one already exists.</param> /// <returns>The spawned entity</returns> - public EntityUid SpawnPlayerMob( + public EntityUid SpawnPlayerMob( EntityCoordinates coordinates, JobComponent? job, HumanoidCharacterProfile? profile, @@ -139,22 +139,6 @@ public EntityUid SpawnPlayerMob( EntityUid? entity = null) { _prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out var prototype); - RoleLoadout? loadout = null; - - // Need to get the loadout up-front to handle names if we use an entity spawn override. - var jobLoadout = LoadoutSystem.GetJobPrototype(prototype?.ID); - - if (_prototypeManager.TryIndex(jobLoadout, out RoleLoadoutPrototype? roleProto)) - { - profile?.Loadouts.TryGetValue(jobLoadout, out loadout); - - // Set to default if not present - if (loadout == null) - { - loadout = new RoleLoadout(jobLoadout); - loadout.SetDefault(profile, _actors.GetSession(entity), _prototypeManager); - } - } // If we're not spawning a humanoid, we're gonna exit early without doing all the humanoid stuff. if (prototype?.JobEntity != null) @@ -162,13 +146,6 @@ public EntityUid SpawnPlayerMob( DebugTools.Assert(entity is null); var jobEntity = EntityManager.SpawnEntity(prototype.JobEntity, coordinates); MakeSentientCommand.MakeSentient(jobEntity, EntityManager); - - // Make sure custom names get handled, what is gameticker control flow whoopy. - if (loadout != null) - { - EquipRoleName(jobEntity, loadout, roleProto!); - } - DoJobSpecials(job, jobEntity); _identity.QueueIdentityUpdate(jobEntity); return jobEntity; diff --git a/Content.Server/Traits/Assorted/SingerSystem.cs b/Content.Server/Traits/Assorted/SingerSystem.cs index 78a4101a65..95087c0f1e 100644 --- a/Content.Server/Traits/Assorted/SingerSystem.cs +++ b/Content.Server/Traits/Assorted/SingerSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Speech.Components; using Content.Server.UserInterface; using Content.Shared.ActionBlocker; +using Content.Shared.Bed.Sleep; using Content.Shared.Damage; using Content.Shared.Damage.ForceSay; using Content.Shared.FixedPoint; diff --git a/Content.Server/_Shitmed/Autodoc/Systems/AutodocSystem.cs b/Content.Server/_Shitmed/Autodoc/Systems/AutodocSystem.cs index d1af790eaa..7002734fd3 100644 --- a/Content.Server/_Shitmed/Autodoc/Systems/AutodocSystem.cs +++ b/Content.Server/_Shitmed/Autodoc/Systems/AutodocSystem.cs @@ -6,7 +6,8 @@ using Content.Server.Power.EntitySystems; using Content.Shared._Shitmed.Autodoc.Components; using Content.Shared._Shitmed.Autodoc.Systems; -using Content.Server.Bed.Sleep; +using Content.Shared.Bed.Sleep; + namespace Content.Server._Shitmed.Autodoc.Systems; @@ -15,7 +16,7 @@ public sealed class AutodocSystem : SharedAutodocSystem [Dependency] private readonly InternalsSystem _internals = default!; [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly PowerReceiverSystem _power = default!; - [Dependency] private readonly SleepingSystem _sleepingSystem = default!; // Sleeping isnt shared yet. + [Dependency] private readonly SleepingSystem _sleepingSystem = default!; public override void Update(float frameTime) { diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index 42fa89b367..fc5a43cf8a 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -47,6 +47,7 @@ public sealed partial class ClimbSystem : VirtualController private EntityQuery<FixturesComponent> _fixturesQuery; private EntityQuery<TransformComponent> _xformQuery; + private EntityQuery<ClimbableComponent> _climbableQuery; public override void Initialize() { diff --git a/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs b/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs index 0f66dff309..9b7a6954de 100644 --- a/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs +++ b/Content.Shared/Nyanotrasen/Interaction/NoNormalInteractionSystem.cs @@ -12,7 +12,7 @@ public override void Initialize() private void OnInteractionAttempt(EntityUid uid, NoNormalInteractionComponent component, InteractionAttemptEvent args) { - args.Cancel(); + args.Cancelled = true; } } } diff --git a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs index 7dc8578117..448311471f 100644 --- a/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs +++ b/Content.Shared/Nyanotrasen/Item/PseudoItem/SharedPseudoItemSystem.cs @@ -143,7 +143,7 @@ private void OnInsertAttempt(EntityUid uid, PseudoItemComponent component, private void OnInteractAttempt(EntityUid uid, PseudoItemComponent component, InteractionAttemptEvent args) { if (args.Uid == args.Target && component.Active) - args.Cancel(); + args.Cancelled = true; } private void OnDoAfter(EntityUid uid, PseudoItemComponent component, DoAfterEvent args) diff --git a/Content.Shared/Shadowkin/SharedEtherealSystem.cs b/Content.Shared/Shadowkin/SharedEtherealSystem.cs index 5ab5e3eeea..2365ade818 100644 --- a/Content.Shared/Shadowkin/SharedEtherealSystem.cs +++ b/Content.Shared/Shadowkin/SharedEtherealSystem.cs @@ -165,7 +165,7 @@ private void OnInteractionAttempt(EntityUid uid, EtherealComponent component, In || HasComp<EtherealComponent>(args.Target)) return; - args.Cancel(); + args.Cancelled = true; if (_gameTiming.InPrediction) return; diff --git a/Resources/Prototypes/Roles/Jobs/Science/borg.yml b/Resources/Prototypes/Roles/Jobs/Science/borg.yml index b9b4cb3c16..67f70b2d25 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/borg.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/borg.yml @@ -5,9 +5,9 @@ description: job-description-station-ai playTimeTracker: JobStationAi requirements: - - !type:RoleTimeRequirement - role: JobBorg - time: 18000 # 5 hrs + - !type:CharacterPlaytimeRequirement + tracker: JobBorg + min: 18000 # 5 hrs canBeAntag: false icon: JobIconStationAi supervisors: job-supervisors-rd From 006acf3a376c5fdb163297b758ef86fd7d7638ef Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 25 Aug 2024 22:06:06 +1000 Subject: [PATCH 08/38] Add ContainerComp (#31311) Applies EntProtoId changes upon insertion / removal from container. Can also be useful for borgs / mechs / vehicles in future but atm I just used it for AI. --- .../Containers/ContainerCompComponent.cs | 17 +++++++ .../Containers/ContainerCompSystem.cs | 44 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 Content.Shared/Containers/ContainerCompComponent.cs create mode 100644 Content.Shared/Containers/ContainerCompSystem.cs diff --git a/Content.Shared/Containers/ContainerCompComponent.cs b/Content.Shared/Containers/ContainerCompComponent.cs new file mode 100644 index 0000000000..b1415e0d8b --- /dev/null +++ b/Content.Shared/Containers/ContainerCompComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Containers; + +/// <summary> +/// Applies container changes whenever an entity is inserted into the specified container on this entity. +/// </summary> +[RegisterComponent, NetworkedComponent] +public sealed partial class ContainerCompComponent : Component +{ + [DataField(required: true)] + public EntProtoId Proto; + + [DataField(required: true)] + public string Container = string.Empty; +} diff --git a/Content.Shared/Containers/ContainerCompSystem.cs b/Content.Shared/Containers/ContainerCompSystem.cs new file mode 100644 index 0000000000..1e1983a331 --- /dev/null +++ b/Content.Shared/Containers/ContainerCompSystem.cs @@ -0,0 +1,44 @@ +using Robust.Shared.Containers; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Containers; + +/// <summary> +/// Applies / removes an entity prototype from a child entity when it's inserted into a container. +/// </summary> +public sealed class ContainerCompSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent<ContainerCompComponent, EntInsertedIntoContainerMessage>(OnConInsert); + SubscribeLocalEvent<ContainerCompComponent, EntRemovedFromContainerMessage>(OnConRemove); + } + + private void OnConRemove(Entity<ContainerCompComponent> ent, ref EntRemovedFromContainerMessage args) + { + if (args.Container.ID != ent.Comp.Container) + return; + + if (_proto.TryIndex(ent.Comp.Container, out var entProto)) + { + foreach (var entry in entProto.Components.Values) + { + RemComp(args.Entity, entry.Component); + } + } + } + + private void OnConInsert(Entity<ContainerCompComponent> ent, ref EntInsertedIntoContainerMessage args) + { + if (args.Container.ID != ent.Comp.Container) + return; + + if (_proto.TryIndex(ent.Comp.Proto, out var entProto)) + { + EntityManager.AddComponents(args.Entity, entProto.Components); + } + } +} From b7e34aa267dae1069691b77e72172b331857e595 Mon Sep 17 00:00:00 2001 From: MilenVolf <63782763+MilenVolf@users.noreply.github.com> Date: Fri, 6 Dec 2024 18:38:00 +0300 Subject: [PATCH 09/38] Localize ai dataset names (#33608) Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> --- .../Station/SharedStationSpawningSystem.cs | 18 +++ Resources/Locale/en-US/datasets/names/ai.ftl | 139 ++++++++++++++++++ Resources/Prototypes/Datasets/Names/ai.yml | 94 +----------- 3 files changed, 161 insertions(+), 90 deletions(-) create mode 100644 Resources/Locale/en-US/datasets/names/ai.ftl diff --git a/Content.Shared/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index dedfd7bd00..232f32acc2 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -36,6 +36,24 @@ public override void Initialize() _xformQuery = GetEntityQuery<TransformComponent>(); } + /// <summary> + /// Applies the role's name as applicable to the entity. + /// </summary> + public void EquipRoleName(EntityUid entity, RoleLoadout loadout, RoleLoadoutPrototype roleProto) + { + string? name = null; + + if (string.IsNullOrEmpty(name) && PrototypeManager.TryIndex(roleProto.NameDataset, out var nameData)) + { + name = Loc.GetString(_random.Pick(nameData.Values)); + } + + if (!string.IsNullOrEmpty(name)) + { + _metadata.SetEntityName(entity, name); + } + } + /// <summary> /// <see cref="EquipStartingGear(Robust.Shared.GameObjects.EntityUid,System.Nullable{Robust.Shared.Prototypes.ProtoId{Content.Shared.Roles.StartingGearPrototype}},bool)"/> /// </summary> diff --git a/Resources/Locale/en-US/datasets/names/ai.ftl b/Resources/Locale/en-US/datasets/names/ai.ftl new file mode 100644 index 0000000000..ac6ce0d83e --- /dev/null +++ b/Resources/Locale/en-US/datasets/names/ai.ftl @@ -0,0 +1,139 @@ +names-ai-dataset-1 = 16-20 +names-ai-dataset-2 = 512k + +# Ought to be enough for anybody +names-ai-dataset-3 = 640k + +names-ai-dataset-4 = "790" +names-ai-dataset-5 = Adaptive Manipulator + +# Named after the famous soundcard +names-ai-dataset-6 = Adlib + +names-ai-dataset-7 = ALICE +names-ai-dataset-8 = Allied Mastercomputer +names-ai-dataset-9 = Alpha 2 +names-ai-dataset-10 = Alpha 3 +names-ai-dataset-11 = Alpha 4 +names-ai-dataset-12 = Alpha 5 +names-ai-dataset-13 = Alpha 6 +names-ai-dataset-14 = Alpha 7 +names-ai-dataset-15 = Alpha 8 +names-ai-dataset-16 = Alpha 9 +names-ai-dataset-17 = AmigoBot +names-ai-dataset-18 = Android +names-ai-dataset-19 = Aniel +names-ai-dataset-20 = AOL +names-ai-dataset-21 = Asimov + +# The most influential modem ever, created by the bell system. It still lives on today in certain applications +names-ai-dataset-22 = Bell 301 + +names-ai-dataset-23 = Bishop +names-ai-dataset-24 = Blitz +names-ai-dataset-25 = Box +names-ai-dataset-26 = Calculator +names-ai-dataset-27 = Cassandra +names-ai-dataset-28 = Cell +names-ai-dataset-29 = Chii +names-ai-dataset-30 = Chip +names-ai-dataset-31 = C.R.A.I.G. + +# Commercial supercomputer from the 70s +names-ai-dataset-32 = Cray-2 + +# If we're going to have AOL we may as well have some of their major competitors +names-ai-dataset-33 = CompuServe + +names-ai-dataset-34 = Computer +names-ai-dataset-35 = Cutie +names-ai-dataset-36 = Daedalus +names-ai-dataset-37 = DecTalk +names-ai-dataset-38 = Dee Model +names-ai-dataset-39 = Dial Up +names-ai-dataset-40 = Dorfl +names-ai-dataset-41 = Duey +names-ai-dataset-42 = Emma-2 + +# Famous early computer +names-ai-dataset-43 = ENIAC + +names-ai-dataset-44 = Erasmus +names-ai-dataset-45 = Everything +names-ai-dataset-46 = Ez-27 +names-ai-dataset-47 = FRIEND COMPUTER +names-ai-dataset-48 = Faith +names-ai-dataset-49 = Fi +names-ai-dataset-50 = Frost +names-ai-dataset-51 = George +names-ai-dataset-52 = H.E.L.P +names-ai-dataset-53 = Hadaly +names-ai-dataset-54 = Helios +names-ai-dataset-55 = Hivebot Overmind +names-ai-dataset-56 = Huey + +# A play on the fad apple spawned of putting "i" infront of your tech products name +names-ai-dataset-57 = iAI + +# Hell on earth (web browser) +names-ai-dataset-58 = I.E. 6 + +names-ai-dataset-59 = Icarus + +# If you don't get this one you are too young +names-ai-dataset-60 = Jeeves + +names-ai-dataset-61 = Jinx +names-ai-dataset-62 = K.I.N.G +names-ai-dataset-63 = Klapaucius +names-ai-dataset-64 = Knight +names-ai-dataset-65 = Louie + +# Named after the Manchester Mark 1, the successor of which was actually named the Ferranti Mark 1, rather than Manchester Mark 2 +names-ai-dataset-66 = Manchester Mark 2 + +names-ai-dataset-67 = MARK13 +names-ai-dataset-68 = Maria +names-ai-dataset-69 = Marvin +names-ai-dataset-70 = Max 404 +names-ai-dataset-71 = Metalhead +names-ai-dataset-72 = M.I.M.I +names-ai-dataset-73 = MK ULTRA +names-ai-dataset-74 = MoMMI +names-ai-dataset-75 = Mugsy3000 +names-ai-dataset-76 = Multivac +names-ai-dataset-77 = NCH + +# A play on both NT as in NanoTrasen and NT as in windows NT, of which version 6.0 is windows vista +names-ai-dataset-78 = NT v6.0 + +names-ai-dataset-79 = Packard Bell +names-ai-dataset-80 = PTO +names-ai-dataset-81 = Project Y2K +names-ai-dataset-82 = Revelation +names-ai-dataset-83 = Robot Devil +names-ai-dataset-84 = S.A.M. +names-ai-dataset-85 = S.H.O.C.K. +names-ai-dataset-86 = S.H.R.O.U.D. +names-ai-dataset-87 = S.O.P.H.I.E. +names-ai-dataset-88 = Samaritan +names-ai-dataset-89 = Shrike +names-ai-dataset-90 = Solo +names-ai-dataset-91 = Station Control Program +names-ai-dataset-92 = AINU (AI's Not Unix) +names-ai-dataset-93 = Super 35 +names-ai-dataset-94 = Surgeon General +names-ai-dataset-95 = TWA +names-ai-dataset-96 = Terminus +names-ai-dataset-97 = TPM 3.0 +names-ai-dataset-98 = Turing Complete +names-ai-dataset-99 = Tidy +names-ai-dataset-100 = Ulysses +names-ai-dataset-101 = W1k1 +names-ai-dataset-102 = X-5 +names-ai-dataset-103 = X.A.N.A. +names-ai-dataset-104 = XERXES +names-ai-dataset-105 = Z-1 +names-ai-dataset-106 = Z-2 +names-ai-dataset-107 = Z-3 +names-ai-dataset-108 = Zed diff --git a/Resources/Prototypes/Datasets/Names/ai.yml b/Resources/Prototypes/Datasets/Names/ai.yml index af97dc9efb..a220de53fb 100644 --- a/Resources/Prototypes/Datasets/Names/ai.yml +++ b/Resources/Prototypes/Datasets/Names/ai.yml @@ -1,91 +1,5 @@ -- type: dataset - id: names_ai +- type: localizedDataset + id: NamesAI values: - - 16-20 - - "790" - - Adaptive Manipulator - - ALICE - - Allied Mastercomputer - - Alpha 2 - - Alpha 3 - - Alpha 4 - - Alpha 5 - - Alpha 6 - - Alpha 7 - - Alpha 8 - - Alpha 9 - - AmigoBot - - Android - - Aniel - - AOL - - Asimov - - Bishop - - Blitz - - Box - - Cassandra - - Cell - - Chii - - Chip - - Computer - - Cutie - - Daedalus - - Dee Model - - Dial Up - - Dorfl - - Duey - - Emma-2 - - Erasmus - - Everything - - Ez-27 - - FRIEND COMPUTER - - Faith - - Fi - - Frost - - George - - H.E.L.P - - Hadaly - - Helios - - Hivebot Overmind - - Huey - - Icarus - - Jinx - - K.I.N.G - - Klapaucius - - Knight - - Louie - - MARK13 - - Maria - - Marvin - - Max 404 - - Metalhead - - M.I.M.I - - MK ULTRA - - MoMMI - - Mugsy3000 - - Multivac - - NCH - - PTO - - Project Y2K - - Revelation - - Robot Devil - - S.A.M. - - S.H.O.C.K. - - S.H.R.O.U.D. - - S.O.P.H.I.E. - - Samaritan - - Shrike - - Solo - - Station Control Program - - Super 35 - - Surgeon General - - TWA - - Terminus - - Tidy - - Ulysses - - W1k1 - - X-5 - - XERXES - - Z-1 - - Z-2 - - Z-3 - - Zed + prefix: names-ai-dataset- + count: 108 From b8e2844adaa473229db7f0c0dd85558e621692ff Mon Sep 17 00:00:00 2001 From: Tayrtahn <tayrtahn@gmail.com> Date: Tue, 28 May 2024 14:41:41 -0400 Subject: [PATCH 10/38] Add LocalizedDatasetPrototype (#28310) --- .../Dataset/LocalizedDatasetPrototype.cs | 94 +++++++++++++++++++ .../Random/Helpers/SharedRandomExtensions.cs | 5 + .../Shared/LocalizedDatasetPrototypeTest.cs | 59 ++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 Content.Shared/Dataset/LocalizedDatasetPrototype.cs create mode 100644 Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs diff --git a/Content.Shared/Dataset/LocalizedDatasetPrototype.cs b/Content.Shared/Dataset/LocalizedDatasetPrototype.cs new file mode 100644 index 0000000000..8be9967e30 --- /dev/null +++ b/Content.Shared/Dataset/LocalizedDatasetPrototype.cs @@ -0,0 +1,94 @@ +using System.Collections; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Dataset; + +/// <summary> +/// A variant of <see cref="DatasetPrototype"/> intended to specify a sequence of LocId strings +/// without having to copy-paste a ton of LocId strings into the YAML. +/// </summary> +[Prototype] +public sealed partial class LocalizedDatasetPrototype : IPrototype +{ + /// <summary> + /// Identifier for this prototype. + /// </summary> + [ViewVariables] + [IdDataField] + public string ID { get; private set; } = default!; + + /// <summary> + /// Collection of LocId strings. + /// </summary> + [DataField] + public LocalizedDatasetValues Values { get; private set; } = []; +} + +[Serializable, NetSerializable] +[DataDefinition] +public sealed partial class LocalizedDatasetValues : IReadOnlyList<string> +{ + /// <summary> + /// String prepended to the index number to generate each LocId string. + /// For example, a prefix of <c>tips-dataset-</c> will generate <c>tips-dataset-1</c>, + /// <c>tips-dataset-2</c>, etc. + /// </summary> + [DataField(required: true)] + public string Prefix { get; private set; } = default!; + + /// <summary> + /// How many values are in the dataset. + /// </summary> + [DataField(required: true)] + public int Count { get; private set; } + + public string this[int index] + { + get + { + if (index > Count || index < 0) + throw new IndexOutOfRangeException(); + return Prefix + index; + } + } + + public IEnumerator<string> GetEnumerator() + { + return new Enumerator(this); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public sealed class Enumerator : IEnumerator<string> + { + private int _index = 0; // Whee, 1-indexing + + private readonly LocalizedDatasetValues _values; + + public Enumerator(LocalizedDatasetValues values) + { + _values = values; + } + + public string Current => _values.Prefix + _index; + + object IEnumerator.Current => Current; + + public void Dispose() { } + + public bool MoveNext() + { + _index++; + return _index <= _values.Count; + } + + public void Reset() + { + _index = 0; + } + } +} diff --git a/Content.Shared/Random/Helpers/SharedRandomExtensions.cs b/Content.Shared/Random/Helpers/SharedRandomExtensions.cs index 3941c2859b..456ba5a215 100644 --- a/Content.Shared/Random/Helpers/SharedRandomExtensions.cs +++ b/Content.Shared/Random/Helpers/SharedRandomExtensions.cs @@ -13,6 +13,11 @@ public static string Pick(this IRobustRandom random, DatasetPrototype prototype) return random.Pick(prototype.Values); } + public static string Pick(this IRobustRandom random, LocalizedDatasetPrototype prototype) + { + return random.Pick(prototype.Values); + } + public static string Pick(this IWeightedRandomPrototype prototype, System.Random random) { var picks = prototype.Weights; diff --git a/Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs b/Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs new file mode 100644 index 0000000000..0ec4c076f5 --- /dev/null +++ b/Content.Tests/Shared/LocalizedDatasetPrototypeTest.cs @@ -0,0 +1,59 @@ +using System; +using Content.Shared.Dataset; +using NUnit.Framework; +using Robust.Shared.Collections; +using Robust.Shared.IoC; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager; + +namespace Content.Tests.Shared; + +[TestFixture] +[TestOf(typeof(LocalizedDatasetPrototype))] +public sealed class LocalizedDatasetPrototypeTest : ContentUnitTest +{ + private IPrototypeManager _prototypeManager; + + [OneTimeSetUp] + public void OneTimeSetup() + { + IoCManager.Resolve<ISerializationManager>().Initialize(); + _prototypeManager = IoCManager.Resolve<IPrototypeManager>(); + _prototypeManager.Initialize(); + _prototypeManager.LoadString(TestPrototypes); + _prototypeManager.ResolveResults(); + } + + private const string TestPrototypes = @" +- type: localizedDataset + id: Test + values: + prefix: test-dataset- + count: 4 +"; + + [Test] + public void LocalizedDatasetTest() + { + var testPrototype = _prototypeManager.Index<LocalizedDatasetPrototype>("Test"); + var values = new ValueList<string>(); + foreach (var value in testPrototype.Values) + { + values.Add(value); + } + + // Make sure we get the right number of values + Assert.That(values, Has.Count.EqualTo(4)); + + // Make sure indexing works as expected + Assert.That(values[0], Is.EqualTo("test-dataset-1")); + Assert.That(values[1], Is.EqualTo("test-dataset-2")); + Assert.That(values[2], Is.EqualTo("test-dataset-3")); + Assert.That(values[3], Is.EqualTo("test-dataset-4")); + Assert.Throws<IndexOutOfRangeException>(() => { var x = values[4]; }); + Assert.Throws<IndexOutOfRangeException>(() => { var x = values[-1]; }); + + // Make sure that the enumerator gets all of the values + Assert.That(testPrototype.Values[testPrototype.Values.Count], Is.EqualTo("test-dataset-4")); + } +} From c9e0e5d992586f52a57ea2f82f4acb33c788c8b8 Mon Sep 17 00:00:00 2001 From: sleepyyapril <flyingkarii@gmail.com> Date: Sat, 4 Jan 2025 15:05:54 -0400 Subject: [PATCH 11/38] SetStationAiName for testing --- .../Commands/SetStationAiName.cs | 57 +++++++++++++++++++ .../Clothing/Systems/LoadoutSystem.cs | 10 +++- Content.Shared/Roles/JobPrototype.cs | 8 +++ .../Station/SharedStationSpawningSystem.cs | 14 +++-- .../commands/set-station-ai-name-command.ftl | 3 + .../Prototypes/Roles/Jobs/Science/borg.yml | 3 +- 6 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 Content.Server/Administration/Commands/SetStationAiName.cs create mode 100644 Resources/Locale/en-US/administration/commands/set-station-ai-name-command.ftl diff --git a/Content.Server/Administration/Commands/SetStationAiName.cs b/Content.Server/Administration/Commands/SetStationAiName.cs new file mode 100644 index 0000000000..8cb8b27582 --- /dev/null +++ b/Content.Server/Administration/Commands/SetStationAiName.cs @@ -0,0 +1,57 @@ +using Content.Server.Station.Systems; +using Content.Shared.Administration; +using Content.Shared.Roles; +using Robust.Shared.Console; +using Robust.Shared.Prototypes; + + +namespace Content.Server.Administration.Commands; + + +[AdminCommand(AdminFlags.Admin)] +public sealed class GetStationAiNameCommand : IConsoleCommand +{ + [Dependency] private readonly IEntityManager _entManager = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly StationSpawningSystem _spawning = default!; + + private ProtoId<JobPrototype> _stationAIJob = "StationAi"; + + public string Command => "setstationainame"; + public string Description => Loc.GetString("set-station-ai-name-command-description"); + public string Help => Loc.GetString("set-station-ai-name-command-help-text", ("command", Command)); + + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 1) + { + shell.WriteError(Loc.GetString("shell-wrong-arguments-number")); + return; + } + + if (!int.TryParse(args[0], out var entInt)) + { + shell.WriteLine(Loc.GetString("shell-entity-uid-must-be-number")); + return; + } + + var netEntity = new NetEntity(entInt); + + if (!_entManager.TryGetEntity(netEntity, out var target)) + { + shell.WriteLine(Loc.GetString("shell-invalid-entity-id")); + return; + } + + var hasStationAi = _prototypeManager.TryIndex(_stationAIJob, out var job); + + if (!hasStationAi) + { + shell.WriteLine(Loc.GetString("set-station-ai-name-command-no-station-ai")); + return; + } + + _spawning.EquipJobName(target.Value, job!); + shell.WriteLine(Loc.GetString("shell-command-success")); + } +} diff --git a/Content.Server/Clothing/Systems/LoadoutSystem.cs b/Content.Server/Clothing/Systems/LoadoutSystem.cs index 4c357c5864..537ca09785 100644 --- a/Content.Server/Clothing/Systems/LoadoutSystem.cs +++ b/Content.Server/Clothing/Systems/LoadoutSystem.cs @@ -2,6 +2,7 @@ using Content.Server.GameTicking; using Content.Server.Paint; using Content.Server.Players.PlayTimeTracking; +using Content.Server.Station.Systems; using Content.Shared.CCVar; using Content.Shared.Clothing.Loadouts.Prototypes; using Content.Shared.Clothing.Loadouts.Systems; @@ -33,6 +34,7 @@ public sealed class LoadoutSystem : EntitySystem [Dependency] private readonly ISerializationManager _serialization = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IComponentFactory _componentFactory = default!; + [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; public override void Initialize() @@ -43,10 +45,13 @@ public override void Initialize() private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent ev) { - if (ev.JobId == null || - !_configurationManager.GetCVar(CCVars.GameLoadoutsEnabled)) + if (ev.JobId == null + || !_protoMan.TryIndex<JobPrototype>(ev.JobId, out var job) + || !_configurationManager.GetCVar(CCVars.GameLoadoutsEnabled)) return; + _stationSpawning.EquipJobName(ev.Mob, job); + ApplyCharacterLoadout( ev.Mob, ev.JobId, @@ -108,7 +113,6 @@ public void ApplyCharacterLoadout( function.OnPlayerSpawn(uid, loadout.Item1, _componentFactory, EntityManager, _serialization); } - // Pick the heirloom if (heirlooms.Any()) { diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index 2313cc4bd0..8b21ffef20 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -1,5 +1,6 @@ using Content.Shared.Access; using Content.Shared.Customization.Systems; +using Content.Shared.Dataset; using Content.Shared.Players.PlayTimeTracking; using Content.Shared.Roles; using Content.Shared.StatusIcon; @@ -98,6 +99,13 @@ public sealed partial class JobPrototype : IPrototype [DataField("startingGear", customTypeSerializer: typeof(PrototypeIdSerializer<StartingGearPrototype>))] public string? StartingGear { get; private set; } + /// <summary> + /// If this has a value, it will randomly set the entity name of the + /// entity upon spawn based on the dataset. + /// </summary> + [DataField] + public ProtoId<LocalizedDatasetPrototype>? NameDataset; + /// <summary> /// Use this to spawn in as a non-humanoid (borg, test subject, etc.) /// Starting gear will be ignored. diff --git a/Content.Shared/Station/SharedStationSpawningSystem.cs b/Content.Shared/Station/SharedStationSpawningSystem.cs index 232f32acc2..c433cc1d4f 100644 --- a/Content.Shared/Station/SharedStationSpawningSystem.cs +++ b/Content.Shared/Station/SharedStationSpawningSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared.Dataset; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Inventory; @@ -18,9 +19,10 @@ public abstract class SharedStationSpawningSystem : EntitySystem [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] protected readonly InventorySystem InventorySystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly SharedStorageSystem _storage = default!; - [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SharedStorageSystem _storage = default!; + [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + [Dependency] private readonly MetaDataSystem _metadata = default!; private EntityQuery<HandsComponent> _handsQuery; private EntityQuery<InventoryComponent> _inventoryQuery; @@ -39,11 +41,13 @@ public override void Initialize() /// <summary> /// Applies the role's name as applicable to the entity. /// </summary> - public void EquipRoleName(EntityUid entity, RoleLoadout loadout, RoleLoadoutPrototype roleProto) + public void EquipJobName(EntityUid entity, JobPrototype job) { string? name = null; - if (string.IsNullOrEmpty(name) && PrototypeManager.TryIndex(roleProto.NameDataset, out var nameData)) + if (string.IsNullOrEmpty(name) + && job.NameDataset.HasValue + && PrototypeManager.TryIndex(job.NameDataset.Value, out var nameData)) { name = Loc.GetString(_random.Pick(nameData.Values)); } diff --git a/Resources/Locale/en-US/administration/commands/set-station-ai-name-command.ftl b/Resources/Locale/en-US/administration/commands/set-station-ai-name-command.ftl new file mode 100644 index 0000000000..ca0bf11e8c --- /dev/null +++ b/Resources/Locale/en-US/administration/commands/set-station-ai-name-command.ftl @@ -0,0 +1,3 @@ +set-station-ai-name-command-description = Sets a specific uid to be given a Station AI name at random. +set-station-ai-name-command-help-text = Usage: {$command} <entityUid> +set-station-ai-name-command-no-station-ai = No station AI job prototype found. \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Science/borg.yml b/Resources/Prototypes/Roles/Jobs/Science/borg.yml index 67f70b2d25..0c60faccfa 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/borg.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/borg.yml @@ -12,6 +12,7 @@ icon: JobIconStationAi supervisors: job-supervisors-rd jobEntity: StationAiBrain + nameDataset: NamesAI - type: job id: Borg @@ -24,4 +25,4 @@ canBeAntag: false icon: JobIconBorg supervisors: job-supervisors-rd - jobEntity: PlayerBorgGeneric + jobEntity: PlayerBorgGeneric \ No newline at end of file From 0334cc63e062a619417e912d423a4bcc9df66cef Mon Sep 17 00:00:00 2001 From: sleepyyapril <flyingkarii@gmail.com> Date: Sat, 4 Jan 2025 15:13:48 -0400 Subject: [PATCH 12/38] Use EntityManager.System<> --- .../Administration/Commands/SetStationAiName.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Content.Server/Administration/Commands/SetStationAiName.cs b/Content.Server/Administration/Commands/SetStationAiName.cs index 8cb8b27582..0c4236f430 100644 --- a/Content.Server/Administration/Commands/SetStationAiName.cs +++ b/Content.Server/Administration/Commands/SetStationAiName.cs @@ -9,13 +9,12 @@ namespace Content.Server.Administration.Commands; [AdminCommand(AdminFlags.Admin)] -public sealed class GetStationAiNameCommand : IConsoleCommand +public sealed class SetStationAiNameCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly StationSpawningSystem _spawning = default!; - private ProtoId<JobPrototype> _stationAIJob = "StationAi"; + private readonly ProtoId<JobPrototype> _stationAiJob = "StationAi"; public string Command => "setstationainame"; public string Description => Loc.GetString("set-station-ai-name-command-description"); @@ -43,7 +42,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - var hasStationAi = _prototypeManager.TryIndex(_stationAIJob, out var job); + var hasStationAi = _prototypeManager.TryIndex(_stationAiJob, out var job); if (!hasStationAi) { @@ -51,7 +50,8 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - _spawning.EquipJobName(target.Value, job!); + var spawningSystem = _entManager.System<StationSpawningSystem>(); + spawningSystem.EquipJobName(target.Value, job!); shell.WriteLine(Loc.GetString("shell-command-success")); } } From 00adaa3c6bdde35da592e7164be0a23dbd6cb407 Mon Sep 17 00:00:00 2001 From: Psychpsyo <60073468+Psychpsyo@users.noreply.github.com> Date: Mon, 19 Aug 2024 01:41:12 +0200 Subject: [PATCH 13/38] Re-add improved random sentience event (#29123) * Re-add improved random sentience event * Make randomly sentient PDA more likely * Make vending machine sentience less likely * Make requested changes * Make randomly sentient captain's gear more likely * Sentient captain sabre has pirate accent * Tweak new random sentient object a bit more * Sentient PDA improvements * Apply recommended fixes * Add requested changes * Fix merge conflict --- .../RandomSentienceRuleComponent.cs | 6 ++- .../Components/SentienceTargetComponent.cs | 7 ++- .../Events/RandomSentienceRule.cs | 46 ++++++++++++++----- .../Components/BlockMovementComponent.cs | 5 +- .../SharedInteractionSystem.Blocking.cs | 3 +- .../events/random-sentience.ftl | 1 + .../Entities/Clothing/Head/hats.yml | 4 ++ .../Prototypes/Entities/Mobs/NPCs/animals.yml | 2 + .../Prototypes/Entities/Mobs/NPCs/silicon.yml | 2 + .../Entities/Mobs/Player/silicon.yml | 2 +- .../Entities/Objects/Devices/pda.yml | 12 +++++ .../Weapons/Guns/Battery/battery_guns.yml | 5 ++ .../Entities/Objects/Weapons/Melee/sword.yml | 6 +++ .../Structures/Machines/vending_machines.yml | 1 + .../Prototypes/GameRules/random_sentience.yml | 25 ++++++++++ 15 files changed, 109 insertions(+), 18 deletions(-) create mode 100644 Resources/Prototypes/GameRules/random_sentience.yml diff --git a/Content.Server/StationEvents/Components/RandomSentienceRuleComponent.cs b/Content.Server/StationEvents/Components/RandomSentienceRuleComponent.cs index 98ebf06595..369fce9725 100644 --- a/Content.Server/StationEvents/Components/RandomSentienceRuleComponent.cs +++ b/Content.Server/StationEvents/Components/RandomSentienceRuleComponent.cs @@ -1,9 +1,13 @@ -using Content.Server.StationEvents.Events; +using Content.Server.StationEvents.Events; namespace Content.Server.StationEvents.Components; [RegisterComponent, Access(typeof(RandomSentienceRule))] public sealed partial class RandomSentienceRuleComponent : Component { + [DataField] + public int MinSentiences = 1; + [DataField] + public int MaxSentiences = 1; } diff --git a/Content.Server/StationEvents/Components/SentienceTargetComponent.cs b/Content.Server/StationEvents/Components/SentienceTargetComponent.cs index f8f7e587c1..4fb5e4be59 100644 --- a/Content.Server/StationEvents/Components/SentienceTargetComponent.cs +++ b/Content.Server/StationEvents/Components/SentienceTargetComponent.cs @@ -1,10 +1,13 @@ -using Content.Server.StationEvents.Events; +using Content.Server.StationEvents.Events; namespace Content.Server.StationEvents.Components; [RegisterComponent, Access(typeof(RandomSentienceRule))] public sealed partial class SentienceTargetComponent : Component { - [DataField("flavorKind", required: true)] + [DataField(required: true)] public string FlavorKind = default!; + + [DataField] + public float Weight = 1.0f; } diff --git a/Content.Server/StationEvents/Events/RandomSentienceRule.cs b/Content.Server/StationEvents/Events/RandomSentienceRule.cs index 2fb733e1a6..b9bc3aeeb3 100644 --- a/Content.Server/StationEvents/Events/RandomSentienceRule.cs +++ b/Content.Server/StationEvents/Events/RandomSentienceRule.cs @@ -1,9 +1,13 @@ -using System.Linq; using Content.Server.Announcements.Systems; +using System.Linq; +using Content.Shared.Dataset; using Content.Server.Ghost.Roles.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; using Content.Shared.GameTicking.Components; +using Content.Shared.Random.Helpers; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; namespace Content.Server.StationEvents.Events; @@ -11,26 +15,46 @@ public sealed class RandomSentienceRule : StationEventSystem<RandomSentienceRule { [Dependency] private readonly AnnouncerSystem _announcer = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IRobustRandom _random = default!; protected override void Started(EntityUid uid, RandomSentienceRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { - HashSet<EntityUid> stationsToNotify = new(); + if (!TryGetRandomStation(out var station)) + return; var targetList = new List<Entity<SentienceTargetComponent>>(); - var query = EntityQueryEnumerator<SentienceTargetComponent>(); - while (query.MoveNext(out var targetUid, out var target)) + var query = EntityQueryEnumerator<SentienceTargetComponent, TransformComponent>(); + while (query.MoveNext(out var targetUid, out var target, out var xform)) { + if (StationSystem.GetOwningStation(targetUid, xform) != station) + continue; + targetList.Add((targetUid, target)); } - RobustRandom.Shuffle(targetList); + var toMakeSentient = _random.Next(component.MinSentiences, component.MaxSentiences); - var toMakeSentient = RobustRandom.Next(2, 5); var groups = new HashSet<string>(); - foreach (var target in targetList) + for (var i = 0; i < toMakeSentient && targetList.Count > 0; i++) { - if (toMakeSentient-- == 0) - break; + // weighted random to pick a sentience target + var totalWeight = targetList.Sum(x => x.Comp.Weight); + // This initial target should never be picked. + // It's just so that target doesn't need to be nullable and as a safety fallback for id floating point errors ever mess up the comparison in the foreach. + var target = targetList[0]; + var chosenWeight = _random.NextFloat(totalWeight); + var currentWeight = 0.0; + foreach (var potentialTarget in targetList) + { + currentWeight += potentialTarget.Comp.Weight; + if (currentWeight > chosenWeight) + { + target = potentialTarget; + break; + } + } + targetList.Remove(target); RemComp<SentienceTargetComponent>(target); var ghostRole = EnsureComp<GhostRoleComponent>(target); @@ -65,8 +89,8 @@ protected override void Started(EntityUid uid, RandomSentienceRuleComponent comp Color.Gold, null, null, ("kind1", kind1), ("kind2", kind2), ("kind3", kind3), ("amount", groupList.Count), - ("data", Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}")), - ("strength", Loc.GetString($"random-sentience-event-strength-{RobustRandom.Next(1, 8)}")) + ("data", _random.Pick(_prototype.Index<LocalizedDatasetPrototype>("RandomSentienceEventData"))), + ("strength", _random.Pick(_prototype.Index<LocalizedDatasetPrototype>("RandomSentienceEventStrength"))) ); } } diff --git a/Content.Shared/Interaction/Components/BlockMovementComponent.cs b/Content.Shared/Interaction/Components/BlockMovementComponent.cs index e308e84960..2125f16efe 100644 --- a/Content.Shared/Interaction/Components/BlockMovementComponent.cs +++ b/Content.Shared/Interaction/Components/BlockMovementComponent.cs @@ -1,4 +1,4 @@ -using Robust.Shared.GameStates; +using Robust.Shared.GameStates; namespace Content.Shared.Interaction.Components; @@ -8,5 +8,6 @@ namespace Content.Shared.Interaction.Components; [RegisterComponent, NetworkedComponent] public sealed partial class BlockMovementComponent : Component { - + [DataField] + public bool BlockInteraction = true; } diff --git a/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs index a682bf9815..52c40477c9 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.Blocking.cs @@ -28,7 +28,8 @@ public void InitializeBlocking() private void CancelInteractEvent(Entity<BlockMovementComponent> ent, ref InteractionAttemptEvent args) { - args.Cancelled = true; + if (ent.Comp.BlockInteraction) + args.Cancelled = true; } private void OnMoveAttempt(EntityUid uid, BlockMovementComponent component, UpdateCanMoveEvent args) diff --git a/Resources/Locale/en-US/station-events/events/random-sentience.ftl b/Resources/Locale/en-US/station-events/events/random-sentience.ftl index 47f0e317a6..f14a020d29 100644 --- a/Resources/Locale/en-US/station-events/events/random-sentience.ftl +++ b/Resources/Locale/en-US/station-events/events/random-sentience.ftl @@ -36,3 +36,4 @@ station-event-random-sentience-flavor-corgi = corgi station-event-random-sentience-flavor-primate = primate station-event-random-sentience-flavor-kobold = kobold station-event-random-sentience-flavor-slime = slime +station-event-random-sentience-flavor-inanimate = inanimate \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index 65ac24d5b5..dace96ff56 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -941,6 +941,10 @@ - ClothMade - WhitelistChameleon - HamsterWearable + - type: SentienceTarget + flavorKind: station-event-random-sentience-flavor-inanimate + weight: 0.0002 # 5,000 times less likely than 1 regular animal + - type: BlockMovement - type: entity parent: ClothingHeadBase diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index f1932e3ece..3a2c151076 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -418,6 +418,8 @@ 1.0 FollowRange: !type:Single 2.0 + - type: SentienceTarget + flavorKind: station-event-random-sentience-flavor-organic - type: entity name: glockroach diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index 2fea60ea1e..8e3c50bbf9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -292,6 +292,8 @@ graph: MediBot node: bot - type: NoSlip + - type: SentienceTarget + flavorKind: station-event-random-sentience-flavor-mechanical - type: Anchorable - type: InteractionPopup interactSuccessString: petting-success-medibot diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index cb8304174f..56f505e9e0 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -245,7 +245,7 @@ - type: entity id: StationAiBrain parent: PositronicBrain - noSpawn: true + categories: [ HideSpawnMenu ] suffix: DO NOT MAP components: - type: Sprite diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 055e8062da..1aa06407f0 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -113,6 +113,15 @@ damage: types: Blunt: 4 + - type: SentienceTarget # sentient PDA = pAI lite + flavorKind: station-event-random-sentience-flavor-mechanical + weight: 0.001 # 1,000 PDAs = as likely to be picked as 1 regular animal + - type: BlockMovement + blockInteraction: false # lets the PDA toggle its own flashlight + - type: TypingIndicator + proto: robot + - type: Speech + speechVerb: Robotic - type: entity parent: BasePDA @@ -245,6 +254,8 @@ borderColor: "#d7d7d0" - type: Icon state: pda-cook + - type: ReplacementAccent # for random sentience event + accent: italian - type: entity parent: BasePDA @@ -329,6 +340,7 @@ accentHColor: "#333333" - type: Icon state: pda-mime + - type: Muted # for random sentience event - type: entity name: chaplain PDA 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 653deecc4b..9cb062f4be 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -723,6 +723,11 @@ wideAnimationRotation: 135 - type: DamageOtherOnHit staminaCost: 5 + stealGroup: WeaponAntiqueLaser + - type: SentienceTarget # I hope this is only the captain's gun + flavorKind: station-event-random-sentience-flavor-inanimate + weight: 0.0002 # 5,000 times less likely than 1 regular animal + # not putting a BlockMovement component here cause that's funny. - type: entity name: advanced laser pistol diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index ffdbdc5911..e09e68904b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -57,6 +57,12 @@ - type: Tag tags: - CaptainSabre + - type: DisarmMalus + - type: SentienceTarget + flavorKind: station-event-random-sentience-flavor-inanimate + weight: 0.0002 # 5,000 times less likely than 1 regular animal + - type: PirateAccent + # not putting a BlockMovement component here cause that's funny. - type: entity name: katana diff --git a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml index f9e945c869..b4df776351 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml @@ -100,6 +100,7 @@ - type: Actions - type: SentienceTarget flavorKind: station-event-random-sentience-flavor-mechanical + weight: 0.025 # fuck you in particular (it now needs 40 vending machines to be as likely as 1 interesting animal) - type: StaticPrice price: 100 - type: Appearance diff --git a/Resources/Prototypes/GameRules/random_sentience.yml b/Resources/Prototypes/GameRules/random_sentience.yml new file mode 100644 index 0000000000..a2c749000a --- /dev/null +++ b/Resources/Prototypes/GameRules/random_sentience.yml @@ -0,0 +1,25 @@ +- type: entity + id: RandomSentience + parent: BaseGameRule + components: + - type: StationEvent + weight: 6 + duration: 1 + maxOccurrences: 1 # this event has diminishing returns on interesting-ness, so we cap it + startAudio: + path: /Audio/Announcements/attention.ogg + - type: RandomSentienceRule + minSentiences: 2 + maxSentiences: 5 + +- type: localizedDataset + id: RandomSentienceEventData + values: + prefix: random-sentience-event-data- + count: 6 + +- type: localizedDataset + id: RandomSentienceEventStrength + values: + prefix: random-sentience-event-strength- + count: 8 \ No newline at end of file From b262e8f6ef206263a7daa343c05d1b49d804b45f Mon Sep 17 00:00:00 2001 From: Psychpsyo <60073468+Psychpsyo@users.noreply.github.com> Date: Sun, 8 Sep 2024 14:10:50 +0200 Subject: [PATCH 14/38] no name! --- Resources/Prototypes/Roles/Jobs/departments.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/Resources/Prototypes/Roles/Jobs/departments.yml b/Resources/Prototypes/Roles/Jobs/departments.yml index bde2d977cf..dd29f4a133 100644 --- a/Resources/Prototypes/Roles/Jobs/departments.yml +++ b/Resources/Prototypes/Roles/Jobs/departments.yml @@ -117,7 +117,6 @@ - type: department id: Silicon - name: department-Silicon description: department-Silicon-description color: "#D381C9" roles: From 385b667cf34a705dd7c3509f07f120c83e19db4d Mon Sep 17 00:00:00 2001 From: sleepyyapril <flyingkarii@gmail.com> Date: Sat, 4 Jan 2025 15:43:05 -0400 Subject: [PATCH 15/38] fix shitcode --- .../StationEvents/Events/RandomSentienceRule.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Content.Server/StationEvents/Events/RandomSentienceRule.cs b/Content.Server/StationEvents/Events/RandomSentienceRule.cs index b9bc3aeeb3..308f37fdeb 100644 --- a/Content.Server/StationEvents/Events/RandomSentienceRule.cs +++ b/Content.Server/StationEvents/Events/RandomSentienceRule.cs @@ -19,21 +19,21 @@ public sealed class RandomSentienceRule : StationEventSystem<RandomSentienceRule [Dependency] private readonly IRobustRandom _random = default!; protected override void Started(EntityUid uid, RandomSentienceRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { - if (!TryGetRandomStation(out var station)) + if (!TryGetRandomStation(out var randomStation)) return; var targetList = new List<Entity<SentienceTargetComponent>>(); var query = EntityQueryEnumerator<SentienceTargetComponent, TransformComponent>(); while (query.MoveNext(out var targetUid, out var target, out var xform)) { - if (StationSystem.GetOwningStation(targetUid, xform) != station) + if (StationSystem.GetOwningStation(targetUid, xform) != randomStation) continue; targetList.Add((targetUid, target)); } var toMakeSentient = _random.Next(component.MinSentiences, component.MaxSentiences); - + var stationsToNotify = new List<EntityUid>(); var groups = new HashSet<string>(); for (var i = 0; i < toMakeSentient && targetList.Count > 0; i++) @@ -74,10 +74,10 @@ protected override void Started(EntityUid uid, RandomSentienceRuleComponent comp foreach (var target in targetList) { - var station = StationSystem.GetOwningStation(target); - if(station == null) + var targetStation = StationSystem.GetOwningStation(target); + if(targetStation == null) continue; - stationsToNotify.Add((EntityUid) station); + stationsToNotify.Add((EntityUid) targetStation); } foreach (var station in stationsToNotify) { @@ -87,7 +87,8 @@ protected override void Started(EntityUid uid, RandomSentienceRuleComponent comp "station-event-random-sentience-announcement", null, Color.Gold, - null, null, + null, + null, ("kind1", kind1), ("kind2", kind2), ("kind3", kind3), ("amount", groupList.Count), ("data", _random.Pick(_prototype.Index<LocalizedDatasetPrototype>("RandomSentienceEventData"))), ("strength", _random.Pick(_prototype.Index<LocalizedDatasetPrototype>("RandomSentienceEventStrength"))) From f5e6583d96938c0d3ae5f46dd928f3e01053db1a Mon Sep 17 00:00:00 2001 From: sleepyyapril <flyingkarii@gmail.com> Date: Sat, 4 Jan 2025 16:00:24 -0400 Subject: [PATCH 16/38] HideSpawnMenu --- Resources/Prototypes/GameRules/events.yml | 3 +++ .../Prototypes/GameRules/random_sentience.yml | 14 -------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 868ba962ed..ca4ba9942d 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -341,7 +341,10 @@ weight: 6 duration: 1 maxOccurrences: 1 # this event has diminishing returns on interesting-ness, so we cap it + startAnnouncement: true - type: RandomSentienceRule + minSentiences: 2 + maxSentiences: 5 - type: entity parent: BaseGameRule diff --git a/Resources/Prototypes/GameRules/random_sentience.yml b/Resources/Prototypes/GameRules/random_sentience.yml index a2c749000a..decbd4e2cd 100644 --- a/Resources/Prototypes/GameRules/random_sentience.yml +++ b/Resources/Prototypes/GameRules/random_sentience.yml @@ -1,17 +1,3 @@ -- type: entity - id: RandomSentience - parent: BaseGameRule - components: - - type: StationEvent - weight: 6 - duration: 1 - maxOccurrences: 1 # this event has diminishing returns on interesting-ness, so we cap it - startAudio: - path: /Audio/Announcements/attention.ogg - - type: RandomSentienceRule - minSentiences: 2 - maxSentiences: 5 - - type: localizedDataset id: RandomSentienceEventData values: From 1ad367b87141fcac236c338da7efbf035dc8d170 Mon Sep 17 00:00:00 2001 From: sleepyyapril <flyingkarii@gmail.com> Date: Sat, 4 Jan 2025 16:08:56 -0400 Subject: [PATCH 17/38] Yippee --- Resources/Prototypes/Entities/Mobs/Player/silicon.yml | 4 ++-- .../Entities/Objects/Weapons/Guns/Battery/battery_guns.yml | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index 56f505e9e0..ed36325586 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -2,7 +2,7 @@ - type: entity id: AiHeld description: Components added / removed from an entity that gets inserted into an AI core. - noSpawn: true + categories: [ HideSpawnMenu ] components: - type: IntrinsicRadioReceiver - type: IntrinsicRadioTransmitter @@ -286,7 +286,7 @@ id: StationAiHolo name: Hologram description: A projection of the AI. - noSpawn: true + categories: [ HideSpawnMenu ] suffix: DO NOT MAP components: - type: Eye 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 9cb062f4be..2200d6bb22 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -713,7 +713,7 @@ - type: StaticPrice price: 750 - type: StealTarget - stealGroup: WeaponCaptain + stealGroup: WeaponAntiqueLaser - type: MeleeWeapon attackRate: 1.3333 damage: @@ -723,7 +723,6 @@ wideAnimationRotation: 135 - type: DamageOtherOnHit staminaCost: 5 - stealGroup: WeaponAntiqueLaser - type: SentienceTarget # I hope this is only the captain's gun flavorKind: station-event-random-sentience-flavor-inanimate weight: 0.0002 # 5,000 times less likely than 1 regular animal From ca0b40abcf5cf86cbb387c75d661908cb5107708 Mon Sep 17 00:00:00 2001 From: Skubman <ba.fallaria@gmail.com> Date: Mon, 6 Jan 2025 04:46:36 +0800 Subject: [PATCH 18/38] The Den Mass Cherry-Pick 01/06/25 (#1443) # Description **(Use a MERGE COMMIT, not squash if possible to ~~avoid~~ reduce merge conflicts for our downstream)** Cherry-picks some PRs from The Den: - https://github.com/TheDenSS14/TheDen/pull/8 - https://github.com/TheDenSS14/TheDen/pull/31 - https://github.com/TheDenSS14/TheDen/pull/108 - https://github.com/TheDenSS14/TheDen/pull/116 - https://github.com/TheDenSS14/TheDen/pull/117 - https://github.com/TheDenSS14/TheDen/pull/123 - https://github.com/TheDenSS14/TheDen/pull/124 # Changelog <!-- You can add an author after the `:cl:` to change the name that appears in the changelog (ex: `:cl: Death`) Leaving it blank will default to your GitHub display name This includes all available types for the changelog --> :cl: The Den Contributors - fix: Put actual prescription lenses in the prescription medhuds and sechuds (by KyuPolaris) - add: Added the CMO turtleneck and head mirror to the CMO loadouts. (by sleepyyapril, KyuPolaris) - add: Added an armored trenchcoat for the Captain. Enjoy your swag! (by Rosycup) - fix: Fixed uneven pants leg on summer security uniforms. (by Rosycup) - add: Added the Captain's Combat Gas Mask, for those more fond of close encounters. (by Rosycup) - tweak: Added the Captain's Trenchcoat to the other captain locker variants. (by Rosycup) - fix: Fixed they/them pronouns being displayed for it/its characters in the character preview. (by Azzy) --------- Co-authored-by: flyingkarii <123355664+flyingkarii@users.noreply.github.com> Co-authored-by: sleepyyapril <123355664+sleepyyapril@users.noreply.github.com> --- .../humanoid-character-profile.ftl | 3 +- .../Catalog/Fills/Lockers/dressers.yml | 3 ++ .../Catalog/Fills/Lockers/heads.yml | 5 ++- .../Catalog/Fills/Lockers/medical.yml | 2 + .../Catalog/Fills/Lockers/suit_storage.yml | 1 + .../Jobs/Command/captain.yml | 4 ++ .../Jobs/Medical/chiefMedicalOfficer.yml | 6 +++ .../DeltaV/Entities/Clothing/Eyes/hud.yml | 2 + .../Entities/Clothing/Head/misc.yml | 11 +++++ .../Entities/Clothing/Masks/masks.yml | 11 +++++ .../Entities/Clothing/OuterClothing/coats.yml | 11 +++++ .../Entities/Clothing/Uniforms/jumpskirts.yml | 11 +++++ .../Entities/Clothing/Uniforms/jumpsuits.yml | 11 +++++ .../Entities/Structures/Machines/lathe.yml | 2 + .../Loadouts/Jobs/Command/captain.yml | 28 +++++++++++++ .../Jobs/Medical/chiefMedicalOfficer.yml | 39 ++++++++++++++++++ .../Prototypes/Loadouts/loadout_groups.yml | 0 .../Prototypes/Recipes/Lathes/clothing.yml | 16 +++++++ .../Misc/head_mirror.rsi/equipped-HELMET.png | Bin 0 -> 631 bytes .../Head/Misc/head_mirror.rsi/icon.png | Bin 0 -> 714 bytes .../Head/Misc/head_mirror.rsi/inhand-left.png | Bin 0 -> 678 bytes .../Misc/head_mirror.rsi/inhand-right.png | Bin 0 -> 680 bytes .../Head/Misc/head_mirror.rsi/meta.json | 26 ++++++++++++ .../equipped-MASK-reptilian.png | Bin 0 -> 749 bytes .../equipped-MASK-vox.png | Bin 0 -> 811 bytes .../equipped-MASK-vulpkanin.png | Bin 0 -> 819 bytes .../gascaptaincombat.rsi/equipped-MASK.png | Bin 0 -> 772 bytes .../Mask/gascaptaincombat.rsi/icon.png | Bin 0 -> 570 bytes .../Mask/gascaptaincombat.rsi/inhand-left.png | Bin 0 -> 336 bytes .../gascaptaincombat.rsi/inhand-right.png | Bin 0 -> 343 bytes .../Mask/gascaptaincombat.rsi/meta.json | 38 +++++++++++++++++ .../equipped-OUTERCLOTHING.png | Bin 0 -> 1318 bytes .../Coats/cap_trenchcoat.rsi/icon.png | Bin 0 -> 535 bytes .../Coats/cap_trenchcoat.rsi/inhand-left.png | Bin 0 -> 759 bytes .../Coats/cap_trenchcoat.rsi/inhand-right.png | Bin 0 -> 709 bytes .../Coats/cap_trenchcoat.rsi/meta.json | 26 ++++++++++++ .../equipped-INNERCLOTHING-monkey.png | Bin 0 -> 1183 bytes .../cmo_turtle.rsi/equipped-INNERCLOTHING.png | Bin 0 -> 1338 bytes .../Jumpskirt/cmo_turtle.rsi/icon.png | Bin 0 -> 842 bytes .../Jumpskirt/cmo_turtle.rsi/inhand-left.png | Bin 0 -> 893 bytes .../Jumpskirt/cmo_turtle.rsi/inhand-right.png | Bin 0 -> 921 bytes .../Jumpskirt/cmo_turtle.rsi/meta.json | 30 ++++++++++++++ .../equipped-INNERCLOTHING-monkey.png | Bin 0 -> 1277 bytes .../cmo_turtle.rsi/equipped-INNERCLOTHING.png | Bin 0 -> 1449 bytes .../Uniforms/Jumpsuit/cmo_turtle.rsi/icon.png | Bin 0 -> 837 bytes .../Jumpsuit/cmo_turtle.rsi/inhand-left.png | Bin 0 -> 892 bytes .../Jumpsuit/cmo_turtle.rsi/inhand-right.png | Bin 0 -> 917 bytes .../Jumpsuit/cmo_turtle.rsi/meta.json | 30 ++++++++++++++ .../equipped-INNERCLOTHING.png | Bin 422 -> 1125 bytes 49 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 Resources/Prototypes/Loadouts/loadout_groups.yml create mode 100644 Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK-reptilian.png create mode 100644 Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK-vox.png create mode 100644 Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK-vulpkanin.png create mode 100644 Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK.png create mode 100644 Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/meta.json create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/equipped-OUTERCLOTHING.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/icon.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/equipped-INNERCLOTHING-monkey.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/equipped-INNERCLOTHING-monkey.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/equipped-INNERCLOTHING.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/meta.json diff --git a/Resources/Locale/en-US/preferences/humanoid-character-profile.ftl b/Resources/Locale/en-US/preferences/humanoid-character-profile.ftl index 800da35ba5..9058f8d2b6 100644 --- a/Resources/Locale/en-US/preferences/humanoid-character-profile.ftl +++ b/Resources/Locale/en-US/preferences/humanoid-character-profile.ftl @@ -5,5 +5,6 @@ humanoid-character-profile-summary = This is {$name}. {$gender -> [male] He is [female] She is + [neuter] It is *[other] They are -} {$age} years old. \ No newline at end of file +} {$age} years old. diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml b/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml index 94e10d76ee..b1a9271158 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml @@ -39,7 +39,10 @@ - id: ClothingNeckMantleCMO - id: ClothingCloakCmo - id: ClothingOuterCoatLabCmo + - id: ClothingUniformJumpsuitCMOTurtle + - id: ClothingUniformJumpskirtCMOTurtle - id: ClothingHeadHatBeretCmo + - id: ClothingHeadMirror - id: ClothingEyesGlasses - id: ClothingOuterWinterCMO diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml index fec4f3ffcf..664da15f65 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/heads.yml @@ -33,6 +33,7 @@ components: - type: StorageFill contents: + - id: ClothingOuterCoatCapTrench - id: NukeDisk - id: PinpointerNuclear - id: CaptainIDCard @@ -54,6 +55,7 @@ components: - type: StorageFill contents: + - id: ClothingOuterCoatCapTrench - id: NukeDisk - id: PinpointerNuclear - id: CaptainIDCard @@ -74,6 +76,7 @@ components: - type: StorageFill contents: + - id: ClothingOuterCoatCapTrench - id: NukeDisk - id: PinpointerNuclear - id: CaptainIDCard @@ -331,4 +334,4 @@ - id: JetpackBlue - id: SpaceCash1000 - id: BeachBall - - id: BikeHorn \ No newline at end of file + - id: BikeHorn diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml b/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml index f217791683..e7a367c454 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/medical.yml @@ -45,6 +45,8 @@ contents: - id: HandheldHealthAnalyzer prob: 0.6 + - id: ClothingHeadMirror + prob: 0.1 - id: ClothingHandsGlovesLatex - id: ClothingHeadsetMedical - id: ClothingEyesHudMedical diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/suit_storage.yml b/Resources/Prototypes/Catalog/Fills/Lockers/suit_storage.yml index 734bd485a2..94ef753c78 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/suit_storage.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/suit_storage.yml @@ -228,6 +228,7 @@ - id: OxygenTankFilled - id: ClothingOuterHardsuitCap - id: ClothingMaskGasCaptain + - id: ClothingMaskGasCaptainCombat - type: AccessReader access: [["Captain"]] diff --git a/Resources/Prototypes/CharacterItemGroups/Jobs/Command/captain.yml b/Resources/Prototypes/CharacterItemGroups/Jobs/Command/captain.yml index 57df945ec9..842c285909 100644 --- a/Resources/Prototypes/CharacterItemGroups/Jobs/Command/captain.yml +++ b/Resources/Prototypes/CharacterItemGroups/Jobs/Command/captain.yml @@ -107,6 +107,8 @@ items: - type: loadout id: LoadoutCommandCapMaskGas + - type: loadout + id: LoadoutCommandCapMaskGasCombat - type: characterItemGroup id: LoadoutCaptainOuter @@ -116,6 +118,8 @@ id: LoadoutCommandCapOuterWinter - type: loadout id: LoadoutCaptainOuterCarapace + - type: loadout + id: LoadoutCaptainOuterTrench - type: characterItemGroup id: LoadoutCaptainShoes diff --git a/Resources/Prototypes/CharacterItemGroups/Jobs/Medical/chiefMedicalOfficer.yml b/Resources/Prototypes/CharacterItemGroups/Jobs/Medical/chiefMedicalOfficer.yml index 6e9bd02b4f..77f6b3f26e 100644 --- a/Resources/Prototypes/CharacterItemGroups/Jobs/Medical/chiefMedicalOfficer.yml +++ b/Resources/Prototypes/CharacterItemGroups/Jobs/Medical/chiefMedicalOfficer.yml @@ -46,6 +46,8 @@ items: - type: loadout id: LoadoutChiefMedicalOfficerNTPDA + - type: loadout + id: LoadoutClothingHeadMirror - type: characterItemGroup id: LoadoutChiefMedicalOfficerNeck @@ -91,3 +93,7 @@ id: LoadoutChiefMedicalOfficerJumpsuit - type: loadout id: LoadoutChiefMedicalOfficerJumpskirt + - type: loadout + id: LoadoutChiefMedicalOfficerTurtleskirt + - type: loadout + id: LoadoutChiefMedicalOfficerTurtlesuit diff --git a/Resources/Prototypes/DeltaV/Entities/Clothing/Eyes/hud.yml b/Resources/Prototypes/DeltaV/Entities/Clothing/Eyes/hud.yml index 1e8a64b0df..f11cb930f0 100644 --- a/Resources/Prototypes/DeltaV/Entities/Clothing/Eyes/hud.yml +++ b/Resources/Prototypes/DeltaV/Entities/Clothing/Eyes/hud.yml @@ -11,6 +11,7 @@ - type: Construction graph: PrescriptionMedHud node: prescmedhud + - type: VisionCorrection - type: ShowHealthBars damageContainers: - Biological @@ -31,6 +32,7 @@ sprite: DeltaV/Clothing/Eyes/Hud/prescsechud.rsi - type: Clothing sprite: DeltaV/Clothing/Eyes/Hud/prescsechud.rsi + - type: VisionCorrection - type: Construction graph: PrescriptionSecHud node: prescsechud diff --git a/Resources/Prototypes/Entities/Clothing/Head/misc.yml b/Resources/Prototypes/Entities/Clothing/Head/misc.yml index bd7899d75f..0138d8aee4 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/misc.yml @@ -86,6 +86,17 @@ - type: Clothing sprite: Clothing/Head/Misc/pwig.rsi +- type: entity + parent: ClothingHeadBase + id: ClothingHeadMirror + name: head mirror + description: I doubt even the CMO knows how to use this thing. + components: + - type: Sprite + sprite: Clothing/Head/Misc/head_mirror.rsi + - type: Clothing + sprite: Clothing/Head/Misc/head_mirror.rsi + - type: entity parent: ClothingHeadBase id: ClothingHeadHatRichard diff --git a/Resources/Prototypes/Entities/Clothing/Masks/masks.yml b/Resources/Prototypes/Entities/Clothing/Masks/masks.yml index 383e32e99d..c5b34a7a67 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/masks.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/masks.yml @@ -88,6 +88,17 @@ - type: BreathMask - type: IngestionBlocker +- type: entity + parent: ClothingMaskGasExplorer + id: ClothingMaskGasCaptainCombat + name: captain's combat gas mask + description: A military-grade gas mask that can be connected to an air supply, painted and outfitted with an emblem befitting its wearer. Issued only to the station's finest. + components: + - type: Sprite + sprite: Clothing/Mask/gascaptaincombat.rsi + - type: Clothing + sprite: Clothing/Mask/gascaptaincombat.rsi + - type: entity parent: ClothingMaskGasAtmos id: ClothingMaskGasCentcom diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml index dc5454d597..07fe385478 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml @@ -51,6 +51,17 @@ - type: Clothing sprite: Clothing/OuterClothing/Coats/gentlecoat.rsi +- type: entity + parent: [ClothingOuterArmorCaptainCarapace, ClothingOuterStorageBase] + id: ClothingOuterCoatCapTrench + name: captain's armored trenchcoat + description: A greatcoat enhanced with a special alloy for some extra protection and style for those with a commanding presence, outfitted with emblems and decor befitting its wearer. Issued only to the station's finest. + components: + - type: Sprite + sprite: Clothing/OuterClothing/Coats/cap_trenchcoat.rsi + - type: Clothing + sprite: Clothing/OuterClothing/Coats/cap_trenchcoat.rsi + - type: entity abstract: true parent: AllowSuitStorageClothing diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml index 18266dc498..7f6d59c9a4 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml @@ -132,6 +132,17 @@ - type: Clothing sprite: Clothing/Uniforms/Jumpskirt/cmo.rsi +- type: entity + parent: ClothingUniformSkirtBase + id: ClothingUniformJumpskirtCMOTurtle + name: chief medical officer's turtleneck jumpskirt + description: It's a turtleneck worn by those with the experience to be Chief Medical Officer. It provides minor biological protection. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi + - type: Clothing + sprite: Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi + - type: entity parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtDetective diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml index d2339764f8..3ada362ce0 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml @@ -350,6 +350,17 @@ - type: Clothing sprite: Clothing/Uniforms/Jumpsuit/cmo.rsi +- type: entity + parent: ClothingUniformBase + id: ClothingUniformJumpsuitCMOTurtle + name: chief medical officer's turtleneck jumpsuit + description: It's a turtleneck worn by those with the experience to be Chief Medical Officer. It provides minor biological protection. + components: + - type: Sprite + sprite: Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi + - type: Clothing + sprite: Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi + - type: entity parent: ClothingUniformBase id: ClothingUniformJumpsuitDetective diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 75b593fbcc..adf6bd94aa 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -1106,6 +1106,8 @@ - ClothingHeadHatBeretCmo - ClothingUniformJumpsuitCMO - ClothingUniformJumpskirtCMO + - ClothingUniformJumpsuitCMOTurtle + - ClothingUniformJumpskirtCMOTurtle - ClothingUniformJumpsuitDetective - ClothingUniformJumpskirtDetective - ClothingUniformJumpsuitEngineering diff --git a/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml b/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml index b64ad384ef..02ed0324e3 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Command/captain.yml @@ -355,6 +355,19 @@ items: - ClothingMaskGasCaptain +- type: loadout + id: LoadoutCommandCapMaskGasCombat + category: JobsCommandCaptain + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCaptainMask + - !type:CharacterJobRequirement + jobs: + - Captain + items: + - ClothingMaskGasCaptainCombat + # Outer - type: loadout id: LoadoutCommandCapOuterWinter @@ -382,6 +395,21 @@ items: - ClothingOuterArmorCaptainCarapace +- type: loadout + id: LoadoutCaptainOuterTrench + category: JobsCommandCaptain + cost: 0 + exclusive: true + canBeHeirloom: true + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutCaptainOuter + - !type:CharacterJobRequirement + jobs: + - Captain + items: + - ClothingOuterCoatCapTrench + # Shoes - type: loadout id: LoadoutCaptainShoesLaceup diff --git a/Resources/Prototypes/Loadouts/Jobs/Medical/chiefMedicalOfficer.yml b/Resources/Prototypes/Loadouts/Jobs/Medical/chiefMedicalOfficer.yml index b5e82749ce..3910e7b9bb 100644 --- a/Resources/Prototypes/Loadouts/Jobs/Medical/chiefMedicalOfficer.yml +++ b/Resources/Prototypes/Loadouts/Jobs/Medical/chiefMedicalOfficer.yml @@ -53,6 +53,19 @@ items: - ClothingHeadHatBeretCmo +- type: loadout + id: LoadoutClothingHeadMirror + category: JobsMedicalChiefMedicalOfficer + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChiefMedicalOfficerHead + - !type:CharacterJobRequirement + jobs: + - ChiefMedicalOfficer + items: + - ClothingHeadMirror + # Id - type: loadout id: LoadoutChiefMedicalOfficerNTPDA @@ -212,3 +225,29 @@ - ChiefMedicalOfficer items: - ClothingUniformJumpskirtCMO + +- type: loadout + id: LoadoutChiefMedicalOfficerTurtleskirt + category: JobsMedicalChiefMedicalOfficer + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChiefMedicalOfficerUniforms + - !type:CharacterJobRequirement + jobs: + - ChiefMedicalOfficer + items: + - ClothingUniformJumpskirtCMOTurtle + +- type: loadout + id: LoadoutChiefMedicalOfficerTurtlesuit + category: JobsMedicalChiefMedicalOfficer + cost: 0 + requirements: + - !type:CharacterItemGroupRequirement + group: LoadoutChiefMedicalOfficerUniforms + - !type:CharacterJobRequirement + jobs: + - ChiefMedicalOfficer + items: + - ClothingUniformJumpsuitCMOTurtle diff --git a/Resources/Prototypes/Loadouts/loadout_groups.yml b/Resources/Prototypes/Loadouts/loadout_groups.yml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Resources/Prototypes/Recipes/Lathes/clothing.yml b/Resources/Prototypes/Recipes/Lathes/clothing.yml index 729f20e979..98cb1a3d51 100644 --- a/Resources/Prototypes/Recipes/Lathes/clothing.yml +++ b/Resources/Prototypes/Recipes/Lathes/clothing.yml @@ -217,6 +217,22 @@ Cloth: 300 Durathread: 100 +- type: latheRecipe + id: ClothingUniformJumpsuitCMOTurtle + result: ClothingUniformJumpsuitCMOTurtle + completetime: 4 + materials: + Cloth: 300 + Durathread: 100 + +- type: latheRecipe + id: ClothingUniformJumpskirtCMOTurtle + result: ClothingUniformJumpskirtCMOTurtle + completetime: 4 + materials: + Cloth: 300 + Durathread: 100 + - type: latheRecipe id: ClothingUniformJumpsuitDetective result: ClothingUniformJumpsuitDetective diff --git a/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/equipped-HELMET.png b/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..f0422f7aba6be89ab93f2588445541b243868bc3 GIT binary patch literal 631 zcmV--0*L*IP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ>7x64t5ZA2vVIah>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<-o+;YSDphA<*AQ%|H9Gw>W=_we!cF3PjK&;2?2l)T9RpGZ8%bi*RvAfDN@ zbk6(4VOEk9;&bA0gDyz?$aUG}H_k<e1)do;GO2muFtJ$dV7Y@?$xw->i6e@tQNECM zS>e3JS*_Gq>z@3D!MwJT<~q$`#Ib|~k`N)IhB7L!5T#Wk#YBqsV;=rN$DbsZOs+B* zITlcb3d!+<|H1EW&BD~An-q)z-7mKNF$x5Bfo9#dzmILZc>?&Kfh(=;uQq_$Ptxmc zEph~ewt<W5wkGcZmpj0~lP(#OBl&3xg#z$?M&FbJ`fq{WHMh6cK29Hi40W}90~{Oz zV@1kd_jq?tXK(+WY4!I5UWsy_q!iI$00024Nkl<Zc%1FpI}XAy5QO0YvA$0_SH4ot zl3S(AeLRIJ5JHha2rCIf{9nJ1V!Is)_-m)$!fBdXM98+raqRB%ZRg&?h|n+$iij=u zIo##bf}C?pDap2$Wsz-_Qd+H5^E~f<2|4E`Nhu}WFR<1sNr%h_0000000000002n0 z+`upO?#>6ivS(gkkKSGR0O|3cm$drP+um}&729v;lMw&_000000002Mb5~3GKhoz8 R53m3L002ovPDHLkV1iw{AdLV3 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/icon.png b/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f382faa4209b1ee713e4570f5d7058c6fe01eaf6 GIT binary patch literal 714 zcmV;*0yX`KP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ>7x64t5ZA2vVIah>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<-o+;YSDphA<*AQ%|H9Gw>W=_we!cF3PjK&;2?2l)T9RpGZ8%bi*RvAfDN@ zbk6(4VOEk9;&bA0gDyz?$aUG}H_k<e1)do;GO2muFtJ$dV7Y@?$xw->i6e@tQNECM zS>e3JS*_Gq>z@3D!MwJT<~q$`#Ib|~k`N)IhB7L!5T#Wk#YBqsV;=rN$DbsZOs+B* zITlcb3d!+<|H1EW&BD~An-q)z-7mKNF$x5Bfo9#dzmILZc>?&Kfh(=;uQq_$Ptxmc zEph~ewt<W5wkGcZmpj0~lP(#OBl&3xg#z$?M&FbJ`fq{WHMh6cK29Hi40W}90~{Oz zV@1kd_jq?tXK(+WY4!I5UWsy_q!iI$00032Nkl<ZScUDBK?=e!5JkTtWEH5;Zsr0O zdX^lZ7Z7yQvvi~21$uy<rJxtcrV9mES=CiT(P$<GabX@5+VV61Gsy&qL?V$Nvj|<F zlp1`Rwr$yU*+Qk%(Dyx8jx5VqL0AE%=C|wJt54RF9l{&n+5CLzyle7JF>pgHeZ@k} z0pNV;7GtM;+vW`rYVHd0iB(9^XX?7P6H!%F@I(R34!kobB8<;8%+~{q=2A+eX$mRj z>=3g%002S=5D|nBvum)<K(dz38_*a7tu>4>_TN;;xwwL6dP-TAAR_y%wMNr4FFM}@ wu}*HjDV%$|+i&gr@o;jk4ISS|Bog^Q9)Oy1lIb3Z+5i9m07*qoM6N<$f>6OnhX4Qo literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/inhand-left.png b/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..f4cd0d9464abff095ed8837340d05223668c72dd GIT binary patch literal 678 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU~J8Fb`J1#c2+1T%1_J8No8Qr zm{>c}*5j~)%+dJZrAoSdWdemA0dsXax;XTLjw(fH<hF*%`sn>_QsedP>R$XHs^CNN z##av>ty$f?T7jdk!NS@?bC*Zwe{U|`BpZu&$9Grv?=Ek+u)^T#8!7gXvqnK5XUsUe zwyQCmL&AY$v&6I+E7dP`OIGx)um16)O8jZ$z32aA6jk$$4yX&AH@v%{!@Og;x$fN0 zc7-#hs<i%?Q*mYiw~&2NY}wD=;){um>219}S@SG>bi}M9-X(+?g*?)H)UhaQihfFL zm_>eJn6~M)UF!9T2J6=@+38ozQK+NXsN!;B#ljvg9hc0j9)=Sa{hiTXf8nG0N!7`# zdEEL~Ll!y}{drLTVR!Uq1IveMtjP}FyL0P%7#d?2L~eikKKXXuJcfVi54^H}ujWm7 zHu+0cbhiv+?d=0cOQR=$Z#XTR@W69A&&i2@Oj?~7E9@8NpJM#G-QoSL+tJzb%lRGJ zc17)D<l}3+sFb?Cde83Vo441W-}37><L%y}i{~y(+zgBtwj^(N7l!{JxM1({$v_d# z0*}aI1_o|n5N2eUHAey{$X?><>&pIuNrK;!adqKSKcLVePZ!6Kid%1QZR9;{z~d5V zb)ZtEaVpy-v#dS6;upMhW=Jw=`{n!(U!}0HIifQSr~?Qprlpo0%)M=-<TGjOwQ2SI z4@`F_xI}0kReB_(pOD3{_1ct_WS+?>Td&oe(b-vDS5&t|4Wbc5{)u^SyZ%b{ueydM zmk-Umn4QJ&ZeF^co6C`h`d^;%B|m?+bKPpsO||#)3)O+DYi>(VzO1cs^55;%AkChx KelF{r5}E*&nI7E$ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/inhand-right.png b/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..ab53a413c699989a91782ca0b44add74e7d053d8 GIT binary patch literal 680 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU~J8Fb`J1#c2+1T%1_J8No8Qr zm{>c}*5j~)%+dJZrAoSdWdemA0dsXax;XTLjw(fH<hF*%`sn>_QsedP>R$XHs^CNN z##av>ty$f?T7jdk!NS@?bC*Zwe{U|`BpZu&$9Grv?=Ek+u)^T#8!7gXvqnK5XUsUe zwyQCmL&AY$v&6I+E7dP`OIGx)um16)O8jZ$z32aA6jk$$4yX&AH@v%{!@Og;x$fN0 zc7-#hs<i%?Q*mYiw~&2NY}wD=;){um>219}S@SG>bi}M9-X(+?g*?)H)UhaQihfFL zm_>eJn6~M)UF!9T2J6=@+38ozQK+NXsN!;B#ljvg9hc0j9)=Sa{hiTXf8nG0N!7`# zdEEL~Ll!y}{drLTVR!Uq1IveMtjP}FyL0P%7#d?2L~eikKKXXuJcfVi54^H}ujWm7 zHu+0cbhiv+?d=0cOQR=$Z#XTR@W69A&&i2@Oj?~7E9@8NpJM#G-QoSL+tJzb%lRGJ zc17)D<l}3+sFb?Cde83Vo441W-}37><L%y}i{~y(+zgBtwj^(N7l!{JxM1({$v_d# z0*}aI1_o|n5N2eUHAey{$X?><>&pIuNrK;!=jwr(pMgS4JY5_^DsH{KWysf}Akg~o zV#>Od6&X8vw=ewY)1v&d>!$RYMDtlI{)Y>lXc1qKcm}8i2tMdtzSxp`+iB9y-D_W4 z+3Gg@%xF@Ho_18l_7h7DuflVgcX@lui_1@29?yQ~=B4R&?EMyYh)xhG5tQA#`}(Z^ zk#F8jZ><emJD=G@^XODx;Yof!-^pcFYA$V2x&8faRJL$GNcFqd%uG&(DqlCaZvk;V MUHx3vIVCg!04kv&+5i9m literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/meta.json b/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/meta.json new file mode 100644 index 0000000000..5a9100512c --- /dev/null +++ b/Resources/Textures/Clothing/Head/Misc/head_mirror.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by Hanzdegloker", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-HELMET", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK-reptilian.png b/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK-reptilian.png new file mode 100644 index 0000000000000000000000000000000000000000..08e85f993a7ba0079843763fe8cb3bd809297bad GIT binary patch literal 749 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVA|s8;uuoF_;%L*Zed4}<MZz| zx-x~c=<ZQ&6bWZJ=j4(j(yFbyDeBkb_zj!HyuaoLu)LjY|9~ryo7?i@WnDc14-4HX z4UH}Ww%305?MaL@n|<cZyTTXc3TMvjJM(+r_n9|y?{#dLc%a_*!HoQG+Ua`kyUPAN zY~3#Z_2Dgj)ANyk|Ne{4|ND5|p&#~>>T933*VOOQdhytx=FiV{E5lar^L}fU&frm2 zvrCu1-u7ip&2?kjXYIU<yX|KBsGNV-+$7B4`!B3|^XIhhvtxdLIq+qMJX_0;jqV@T zhH=l$_1tZm!ZXb{$Jp0H?(OSqe;-_VX3L!TY(BTigA>=+#hAaZTN-3I&wSmzzGatJ zO7^=cOIp9OWIQl2`(KUf-hig5`mrmoX0b7q$)9Iu|7Ilp<>Do~T^4h*$~zMuu2=69 zFJn;Zb#vapeE4C&tFOBf*x8yJSJ=L<o;j7_k;iBI*Ok-sZ-}u4?7z}y_x|lo71Nt3 z`*&?qJ3Rev>)&(SA3Sa{I@+7OxctmrD}l|KNyPy#3gBRzvS^EvjJ)`xn!d?9;%aQt zBQ4WdF4$;9ytA>BcfD=?W@&5D6_*8!x*1N^>-&N$Z8X~$0u<iP-;wd2TjhW7mzeh| z4#Kx86)UcPcy#i0Eu+fq_xE@&EEhHScxQX2=enD9R~q}dU#mZjEZ24jy*1^i?fRD% z^BH7p|2*n^QQH{z!_ML9jx}ueFErF&4OzQo$CYVUtLHPk>6mu1yl(!~rFWk%%ZYQF zDH9dd+NNgm?Rx&HW1qI^p8Y+~BA;P_RC`0EMBBESxRjYTk+ZI^HmuS=XkK|_4mX3$ zi`|`uOb7Rzc=Ax(S8>I$`^%5kXJ65;7E)luiGHYa{$YHc?EXTuF;5Pdj2S##{an^L HB{Ts5ZTe8o literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK-vox.png b/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK-vox.png new file mode 100644 index 0000000000000000000000000000000000000000..4923a4d9d5628b68cabb0974718ce162ca918cae GIT binary patch literal 811 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVEW|g;uuoF_;!|ews4@xasRCf zGaSSj#lH!1?BERBup?BfB+}sG(*E@q`W5tU^9RPf*j4&|(F$(yrH``3*qAQ9a^jTm z;95F0^Pl*+0#4(}JIj}CUHYH<<X+?Sca?wM&Ad5B`H&;i^H~R4^1lR|i#gxD{d>#P zweF=m&WUGlSNi?`*6n-W^mj-d`^@q0?x}fyYVCAq<Vx86s`)<Ubn5-J7wys+IJT8< zRm^68Egf&S<xJ1($!!h6@4x?^^!$tCLCyxY|2sBr`S|a){LcTSSI_LQ<K!s2b@*r1 z-abvK#h*MTY?|RSQMF6J=Ggn|=Vygan<vH5$uF+qa4P(rox%IMl_5s6o=>0O(tPm1 zn%#HTeUW)<$>?zE>bLt|+7q5#S?{|tM2n5dPyV~L^Up%Q8^=3-zfiIcefP-u*?RR$ z-0BR<SF=LDuq5!PUwB#K^0e~)=_L#goM*i&{A96rCgYjAnJxC^<%<=3t@w-?GTAkx z{(L#pQ&YcrW@P@H;N{=s6kfEm8DBcLukFtjtN1O+T#sW|V@+IMDKiQw;6@hNECJF7 z`0H-Je%x{RTK@XaH?-C?F}DY3AF25IqCIARrLUS;oY*4!JCzcPMC#ukU2`$NYXPfi zs>Rgfy<cb8^U8fa^lj<w!1oPdy-C|r_OZ&|YG=5+f$_qInUS8Sns+kZI9P8NBe1)i z;p?G2%vNvgSZ-(tHJLZu+ThoJKcnH6^#v|Fvp|!xlUZ+Y%U)o$UUhrh%qQ)CeQxcV zP%hG!wVll`BQM`Oz|&1qmU-#tEt|^bG+)SkZyx&mbhnZH&SW|MrUQ}bbLQS*c(7vH zG0`R|r~3`l&et+H=-(^fEzY=0aL=uhtE>0Dd~-!{(d;*p>Wgcc3(m+%F}&NkZQYAF z1{b!Q&duhL7w>Iz{hz*e>8HTgC-`bq4Es+Vi(US<_05?nPiyD;Hd*n>JXLXE!i83{ a^f9o6$I6K%oR|m91Pq?8elF{r5}E+LU2ypT literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK-vulpkanin.png b/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK-vulpkanin.png new file mode 100644 index 0000000000000000000000000000000000000000..ad830675ce756c231e6f5add940d99cb17c5b36b GIT binary patch literal 819 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVEX0h;uuoF_;%L*Zed4}<MZ<l zC^gEk=<ZQ&6bWbX3((0CY1P)<l>V}RzrzQA0q?bIT`$=EWL_t*b;AT}zrDJ81sjfM zE@a_RX{u0sX8mEB<EHAFH+`k{GoOC*Zg!=;-RH8|-(4&`9_&{uIA;Fq)zaC*cddVX zITqdjs$g#Ov#Uq`{rIqW``^v$+BV7^++Y92yKY~M=ovE}`PzNeT2s5;UrU*9%)ny3 z<Hoe~`nZyPZ@!)Bd7kQgU?qRM^U3003m&jIH1EHE?@f)x55?%;{%3)b4jV4Zo!cIz z>vQr)_&g4yJTJ*jA%acEf9(6KvS-838x9_^F$)+p<9C-HulfD7=w`;f9e)#yq<YWP z?!Q01aj~sBgU8Ni@5?WBtE{Pe?X@><`eIF%Id=Ds2me%Jf0KOX*Gnbq(07jx->LUs zBJRiFCe_;pbVJ;8i$2$<mHSVxVR&#{=Wpiw%_WXz-|Z7hbI-E$D+uba1@Rr`lY1>@ zna0Xro%!DXbLu;uhDE=6zdd>VapT3zeaEBDI$XHo{J}kcx1W`Nalyr+{Pef>>-7J+ zvb+CPabUuSHZU-0c<XMqidnLEr?zwEod-U-Q3{Up52QTLyR>D(HoN!LHyU(r=rC33 zF-ZM+xbwB=eg{FnrH)N`Z#^5%Dk)Tix4n6=HJ+ibzkGgsl<}O<qrTjFulG8+9ys{4 zU3SghZFO;RrQ4lDmik8C+3b7Vl6ltUUp9KT6W@xzaIIFn7aksVCUyGxH(&L>Us(8S zgMaP{->asl^{1wuy(h_Ve2dS&pHI42*w;_)<DGKW|5&o4qG|qJfwF)SUDig;qslWh zx>EMk`I~uJFLUXCwPn3*K10Egwgr8e{S5PJ=N^iyKEO9Gbo;hr3?=g(EWEtZnML|d zBSZ4UC&dBmg-KCv+pj<089sMIBhQ6Ek(8^`vyXW$Y*&~*spss4wKpeh>`@X@V8n?$ bR6mGEw+0643cSw*W(EdNS3j3^P6<r_vg&nf literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK.png b/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/equipped-MASK.png new file mode 100644 index 0000000000000000000000000000000000000000..7880a3189f3be8fe6cfc32463935011d67c6a355 GIT binary patch literal 772 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU^?yT;uuoF_;%L$Y+*-{<MX#Y zP;8WIlFRW_xFE;$RUv4PhFH~?30IP%|Fa&fcd)tD{J&A@#^$r~0RdN<+k-B?l9S?C zv&D7`3y(_Eik#1~6{nZv9Xj_WimQ(O)a;w)pX+MM%xw3lA97^+Z`#Pc@1}q3ZsC_V z-#s_KS9Yas^_&&=I{yC8%&-2v|HUhb-xL4*X19OxyF0USOX80kpKXJ6#A^GGJ)gs< zaP!^OU2FbV?fdyO`LxHH^HNOrs@7khtdlP<GKV4KQ})-TXZfp-Z~F7Kb7I7NR+itl zlz&96WfSbb{42sR&B)TtJgD`FVc#}6^L0A5Mgnem=K>qG<D>SZ@B8btFhS@1{k`&E zf7cqnFR{8*`sFzbL-@L1pXY92%Jh*nTN<Jz$x-pULXPjt|Dpr-ZkPJ?@|M4fP4f85 z`Jl@8{2334Hnp2YJ9QNJGAGH~wBLI!wTw4n|L-M#_w8q2`M-8b{`JMpe`_bJsH{_- zv&!Yus{h}%&AgQtWc2)-q{0j7HlL{Zw;xDKa86*riyF)sr!3l{lyiPBua(S}zc-87 zg@5L{GF){$SoHm-lgQgOM|`<|R!VGRk^jIt|AXzC()gw8c-3aQF=$NKrhm8VH|wNt zPk;JU3q26tx7q2#x`#JUX76VZ%C(J+IcNOv^0qYjeRZwr=Fjy`pMDm`+!`4y_C4ov z;XcM4RX;W}U#@Em`>=B1R_EyX*JZXnWd3$#`7OUL@6yLhzVkX5O!acTuwujS-@AO? z<Z@ion4g{-Eh)O;mx<l8u++LD_X{!;k1jW7U|GqSx!p~GrSPXsKF5cpODl4FPtLL_ zDtIMukg;0fWPr*)nYJpHTerVmJ5#)smxE!ga{h&^>)wxa7p5P0UC`xRX7AMX_Kp*i giUUqmFKEH|)6A)&`f{Z+Fm*F{y85}Sb4q9e0Jx@NWdHyG literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/icon.png b/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e2d75aa86cbf067af0fbc6c95bf2089c0ff6fb8a GIT binary patch literal 570 zcmV-A0>%A_P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800063Nkl<ZSPAWw z%}N_#6oy}&K*0negyKR;il9Phi-Lj+OM4Mr)@$hAoty6U7If()xUjT~Lc6F65e<k5 z$wEtLB1u7TtUezc8I#O75nVY4GH1T;{JrOIA|;emAgRD}ufWhl3gAu7|L?Z*kAFSL zSE}1l)Y$@$PfRG~dAF*UzOTCEKn)5cHUNSb3I(OpX;rILegFJV`{h%;nHtm5+K7t9 zqB@<9DmOp|0Wo}=MvNCSZJnPx&DV2NcR2UR2c=kjv+R#wywmdgSK9b}CGY1KU2e@N z_3=>t!itt>hqdwbM3WcaRH;-Hw+gWV_|u<U@E?D&p-C>jY&j6#(PnG__qKrS=`R!& zJSa!s2G$uyJVF8BAp7=1&$^kfa{aH)wpRrYBkavb7@!^irkKfO)Y{u|En>PM3sx@e zDtoZ5TrTH0=b+s5vFC@+u$kyP1KkFkSwV&nqQ|y&tYxWG5;Fol*$`5Z?C7Wq!bhml zXt;MCRKixU)oiNWZa>}-(nGQ(2+kpb=ks|72yS4Iqldor-WfzZ`nJIIaY8m^58<30 zryT^PA{I1wkFMF-9C6o=dWJBlgcE|nAuBWQc4s^&@3uo0Mv(xRTr%U&Cm4wAxvSS| zj#~=|f*vyL!X`fmCb9*lXsgNDoW{K~9|=z?kW?V4Kv)H?0nnopr4R1E;Q#;t07*qo IM6N<$g7nY?xBvhE literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/inhand-left.png b/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..fe2901acd1f3fbfeb86d3b0ce614560665cdd9c5 GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU=;UsaSW-r_4d}<JSInxwukvA z{OWx&4WeDNcdeA<S*hTgWw@7}r9A9zHG{vv2~pJ<rVW`=Op!vfazZt$%00>^eV?ej z?~UbV_p=j$hJwI?rn2?%bCf^J?3{Gv!{3Dyx5mxB`0i`BMPZll^556Z-}@Tab@vM| zU%&eK<H&6L%T*JzEM;eE&3x^ubz9P7(x&eVKi+IOk$xj>s^;#xU;O8d^=ED~4*2+U z;rzo3_bG3f)917A_Nv=+UK`4<zCB0#ey;cZf0=37_LKMSxbm{%Y97nKJG-Voo>RA6 zIQY5`#6dt(!60=01L=k0@ul(yTVnR~*S2Upu|II!u8Fgj<<XJM&8JF=zXxPVtNz$t fR>sc2@boX!G5hQ-Zx$r41R3G!>gTe~DWM4fdm53a literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/inhand-right.png b/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..5615899ced3a6b6cbf8db2633243ea187bfb9bef GIT binary patch literal 343 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEV3hN8aSW-r_4d|6KW0af)`!Mt zd03n|(;ONSn3J+rad3(q7Ebt8QOh8=pdu*n`mDB=HJOGjIk$2qcew`g<V<JX=={$= zU+ww5nXA8f0Zj#g53P?+&sJKtCiL^RRbshwyBBe)U*F#9-&7-{^>l0M(cda3N~>l& z|H)GEJep%+;G5g9bI0Y?OTO;VpWm{!GWTZUO`FhN<^O&b&NKV>bN%t!$mdV2Y*WvF z|LUG#CNraEXTWybtC9Z}<W;W%vM1;NPY$_zZXI9vVz0{WV#NaM%#=TvRhszLGj4p! z3ULq6$0A9$B!Bq5Hg`R~$vEKHzb!u`%Gv&{VGNpM&7A5pSMY;pfSLc+`jS=Cmv<)Z k{TszQ;RicE!w>y?jAw(>t~$(pZ2>aL)78&qol`;+0Nr4bq5uE@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/meta.json b/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/meta.json new file mode 100644 index 0000000000..2467db2bde --- /dev/null +++ b/Resources/Textures/Clothing/Mask/gascaptaincombat.rsi/meta.json @@ -0,0 +1,38 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e. Reptilian edit by Nairod(Github) | vulpkanin version edited by Floofers. Vox state by Flareguy for SS14, Modified by Rosycup for TheDen", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-MASK", + "directions": 4 + }, + { + "name": "equipped-MASK-vox", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "equipped-MASK-vulpkanin", + "directions": 4 + }, + { + "name": "equipped-MASK-reptilian", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/equipped-OUTERCLOTHING.png b/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/equipped-OUTERCLOTHING.png new file mode 100644 index 0000000000000000000000000000000000000000..497ea69bfaaa6a8b902094bd5d5971374b17f8a6 GIT binary patch literal 1318 zcmV+>1=;$EP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF000E-Nkl<ZcmeI2 zKWJ4+6viheg(Lw3k6<AtsH8K3B5t#VwT(@%a90qltSoF&h=o|#Y_XM<u&`<&+iYWP zvCV=g7#k5?UY0Eu2`D7N!tlJm_xP?S^X9&HXWo5*;0#=5=FH6b&iUrgy?17u%ONw6 z8ORJ|1~LPgfy}^n%D_a5--k|}?|gdotZeJ&Q*_pC16EaCYg1JnUw*(BROKJ9E_OOE ze(J0}IM-QNSa33gm^^aIsaK_A6ghgqXti1nJpVyXc`Sa}i^Gh=xw)(Vb%D3Gw%o?X zhJ$ek0DNX<#;vcfyN|yeaT8}A)z&)<SLNWJx;@#&kj#%E<<*^?DEwL@04Jwb-Z&h^ z$xuK}IrTI;LiJK)=sR)qvx9g7j)J9!Jb`lR#o!Mm0A&CT3h#b7=>EE~T~?ufj1gtS zKtlM8I3C&Z=g<E-Jv2jZ>(s>`N<hG-KGEx!Z{6D4|NG=k3N|-4cc}yK<msh8T)*C~ zMuE&g0xbKqZMWMc(^l7yzfQYbf1mA^BUew=?FFmLrhR;zL${^^#1ph8$@v)kfdt4H z6z{x$-d)(FTxG->ex#`<L&!G~EI#_M0m3gnJlc)PE5zbYsbMJY&Fk%An0;`U?|rLr z_lM{1`qN{jz|a4F>4RHd>T$b#t*jQE2;qxt>okgyi})D)sMpox@w1)T+1awk?Q0N& z{GN*Tct`{Asj|4Z*jJP3Nzh2`t56}RH3j^(^tcUv-j+3;rUP&mOx^;-<dLU@fm5JL zA@i@8iDj{$FAK($z}f3h<?yk>`E;P#Bft)SzsJcl2Qf^;G~J|dOW}u0d(ssA+5`Zh z1&AKkf<sK6HGuN{7NQ8SD=#Z8K{07tPJTaxq&0v~->H@PVz|$Dt~_z>%O3~b+|~Wy zZoiGSM*uy|dzrsI{tETFAtsNUayugW;nF3-M*~yMe~_o(*ZvN$pS9X}U||vwp>m9B zDrgae=&C?R#D0s^CLjcA2?&*sLuQHg^{ZoidCq4BG6R``%s^%!Gmsg`3}gl}1DS!$ zfX6`e-(c0>*k1pMHDyQAj*#8^<5--q?si|A@Q7|Si}UtGSjtnj<f2D_E+8x~9d?`v z5+FqP1I9fP<h;%U!Do|12eg;(wWEBB8UZ+u<DAN}`=XJM!aRnj201VM-j>`_SKZ@P z*NGt^ihwIm=8L`m>EeRIWOwU{?WfQ&RjOlk+p#J8`sH?UINB;tq<gnVrVG2~3pMS! zs$A_&QL9nM)2YW%R#!?|!=1LWK{mzeiy8q{${C7#ISp8QS}l&}Mh{6Nz{1LL%|}}{ zuC%a(Jqk|L2w-o%ckbW2GoS-X47q>dpIvtbMlotwTh1cw{e*9JxxTv$!*oTxGD6$n z`XJFX*o)VouyD@D3)wh`gpsF!l=~;fl8+;9600!+?1}L>oR&_VMk_yR98Ca2OrUo7 zH`y!FUloI;Phr_WU29uaTVEc%23Q5WgEb$CxvHYGLR+uNG*0sI#li~-+)!my*brkw z^aub5$MpeXUq$0O8Me+@3|-r`_Sm|0(n$n_6mwz8zM4XHnvj9Pr*KmtY6|FQ2ylLg zY~Y8)e<j)SCezG&|Cn62M!7;8^D`7hfL&xX1?1RXIWIiB_#w0FK+GB-eL2nyWCk(= c-z5WI0bv7zQ2xVC>i_@%07*qoM6N<$f&ie4<p2Nx literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/icon.png b/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7509dcb3db6d94b1bba85ee40f853c3b6ad2a5f6 GIT binary patch literal 535 zcmV+y0_gpTP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80005rNkl<ZSPAV_ zJxc>Y5S_#nfdmYMR6-7fbSfx8TYp5b@B{J>Y%MG;Z2Sp=AX<ox|G>gV2nxnVf<jJG zgb+|jtd!GtGRN+6w>wE=;Z1Y<cHg|6otfPuN-fR6f6qW>ngq-)tp}#x`@{8AT3J&l z37_wIL2$Pfw2xPVYPG7-fH9AC@U%q-5JwI1QnN?cY&;&*a5$vVXhg+gk;>&V6$%CN zeV^V=<|wmrCe{^Y04dKkp2Cf=Nt|IGfq)lRqBtMSIRk(ggl+bGl&72BkB9=kGzez^ zk*a0zc;BIRr*B~-FIj8Fr{{V@#`C<e{upx)V2o3DyIl;UzJQ<Wc8D^7jlN!5VXzQ2 zO9G5}7<k}I%^CpedbRyZryB!J?_jNE#o)tF?F-F80oD>Iyl^-(Uk~`&aal~vnKFHo zur&ioPOc0j`Nhpo6~O*&UDGnLw&(!j0MzuyWdMzca)9U9Q(G1X059%#oR~WuU*Ndg zq3x6aCJKPB8npOk@E3FHI<UlY5|YWm)0=Bl!s(rh3VmMYsnj%CGAs@NCta*%X<@nc zvsq752gYCU_embm=ApK11>$!={kd@cRb@0Fo9p>y9=_rLK$dj)qz@5vQ%f_DX5ddV Z@C~9M#>PmSi5UO@002ovPDHLkV1hgP?g;<@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/inhand-left.png b/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..3079c4ca6d0b79dcd49b3aaa125469a2a1176d46 GIT binary patch literal 759 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVA|*D;uuoF_;%Jt@7s<d$Ms(Y zcpOmdNM51fVX#zeVfJpX=}IL6Zy!CWU$7;aTkqUE-Ca|I1g_uO=$dg+h|BeKzn`lS zN9Szc&wpQ9pHu$(RIk$B^@p5F`S*8scbCuK{rm5O@8=}cgcKNYq8F17d^nPG`#o2r z`Ofvvum67e_%_E%{#K3F7dh+0ZrzXLnR#pSb1^2hz_nq#Pcry7GRtl>{aA7D+evN@ z#VadyY%Ri9hOF`07-90Y>TbfPwPB}EO;#~{e*E{$<X_uzp8Gxg@p$){ee3cvB6Haj z9DD>abAL@fE8W{Rb@`zK_BWjF@44LZjiKPz)8{w)Zay)R&k?G04!FsfKY#8F{_g#{ zr^ETbU*7a&#RA!?y_2$6-^`g+v@@rE@2_u{*HtYmsPkSL^vYnH!94B=N1r9vZ+@RE z9=-0*mjwT-@i)S5GOnAfar%_^yY1~)Qn%k(CEIX4Jh$l1o%+A#v4v}t;u_oS|IcXs zs*?3}$@?#7mOKru*dUsExa^F^3aOUM5~uIJyPG2UFD7%+zI|u4z1B=%;KYOO7%<kH zE#3d(-UjQLho1{cIJ#(<smy!%v|ehrV_)lfDe;2GKOYr)c*K+>7jYia*)!j$-8%nL zWx^+pllk_41!a0pvNjxH-YEQup`r88wk3}5lvl_$UaaoFHKXF*!<?BWMF(7^p4c@> zF_?FBW*7uzc-Vx#`^<kp|DCeL?Bg?nm=#v{%P1Btx%FwfQ%-=%k;?CUj9*l_bS+aB zez-c1@g1{(fZ6jpSNE2O6+VYHCtUc=krkfOXZ9}Q{{DK~*DWWGTYc>|&XO+DN_aYb z(#8_?HE-_qO1T2#%k<8~y8=74uj`y%9q`z$X<7XBH0}$vAMU1He<r}btCi&+V}zmp V-f3(%p8^v!gQu&X%Q~loCIBihV{ZTe literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/inhand-right.png b/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d74e6a4eff7b242b36a14109db4e8e5a1c12b4ae GIT binary patch literal 709 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVCwO7aSW+od^>BuH?yJ0@#MD( zTOI@)*{HEYM4OK_{O(;A_Ih1Ezja-GTzdaRo0=ayc%;?Tt)!(9>$1?IvqT`ud~F@G z{*v@}?Mv^?{lP1<vwG)~eea9)Eq917jqqq-!G-?WHr{*a#oaF6a{Ad`_1x=;vy9^o z{?0yn?W~*IR4>)kay@_jOzT^FYcG}LX3Nc+#d-Bka<}{Qq>ULqJ7fGpSBLU0zL_Ic zy*4cPegE{_THoYD>IJt{5Bh)J&f$=LCuaTitG`~Xl=IHI@js}MKXKM(M?PISo)^!~ ze(quuD#?$!_27%YS2W9;v-?&oZdJc5e_bP5c>1xnvW?QoU4C4xO_LU1%*aS}oHh5e zM7E>d{BR?NYpfHFmPS3_x2O5v#*^Dj6?}@LSxzVfou0i-+2PiusMELIdzI{dyexHN zuq%B!(b06@--Y)rl$hopyZt-ebbD~|w~y5hcfMV_x~H>Y_v`A%&GY$|8-LQ>diZYY zXK9nn-5ITXx7(MLe*CH8z=Q_{ay-yq|982$*ZdXVS=8L#IR0_@GtHoK+mhFB*KFNV zy10+U!R5%_bcTY7GtP?XZCUS8`+Mnr>(+-pJvW*iTYT-$*)o61S+Zl|>M14HAD>KI zk$1a&!DkP_2F6Pp(iu3TWbUzKOlVHbZT!s~GxzZw<Ax(?l`5a7-Ve4i&UJsZ+B=4Q z+Xaq>jwRoC6a=H=_FZ_`r>P*=uq#^iQ_P_npRDr?Vh^W2R@}^X;Ah@i=7oX}RCRxT zv-r-FaBJ?`eJk7V+7(xnmR8AHGEKT?+`zGAZjbF-UdA{<z1g~xo~ul#W19alNpDiR Tn8kNs@?`LI^>bP0l+XkK6NWx* literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/meta.json b/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/meta.json new file mode 100644 index 0000000000..7941023219 --- /dev/null +++ b/Resources/Textures/Clothing/OuterClothing/Coats/cap_trenchcoat.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/4f6190e2895e09116663ef282d3ce1d8b35c032e. Modified by Rosycup.", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-OUTERCLOTHING", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/equipped-INNERCLOTHING-monkey.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/equipped-INNERCLOTHING-monkey.png new file mode 100644 index 0000000000000000000000000000000000000000..a2581dab5e38f16c496645e7608883d94ff376db GIT binary patch literal 1183 zcmV;Q1YrA#P)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ$>+#1v`j1WN4i%h>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<`9$!jB*Z5y61OOg)ia%)oPe-NVP%yC~1{KKJM7SMnwUd?N82(+!JwgLr1s z(mC%FhgeBch|h_~4Z0xlBiCh@-#8Z?7I<dJ$fV|pL&RdSgXIopB|{~iCJrmAM)^Y4 zWrgz=XSGset$Xqp2J+fUn(H)25yuh|NJ4~+8p^1^LX=jG6cZ`hk9qh<9DkBrGP%lN z<XAuzDkR4b{s+IiH49UdZc-oybidg4M+E5E1)6o+{yw(t<_X|`2ClTWzuEw1KS{5* zweS%T+y*YL+nT%wT<!qFPr77Cj^w8)6bium8GTa@7`g@e*4*A&`#607GSt=b4RCM> zj1?(+-Q(T8oxS~grq$mMXvuP;>U)db0008kNkl<Zc%1E;O-mb56o#KjS2m&+T8K?r zvypBF!i3~sv@pbNH!ZbLyHMz^_zw!nqS**`QMZ*q|3fwj3;`FGZel*FML~spU|n>& zD0i4>qjSfMw)Q*_E^}|@yq9x77<dl|f*=TjAPD}*7m7~4P;@Tp`&abGbj1O3mPNi$ zbefGFCtoN!Im;Rt4~sDLBRb;AhYtX#H=5)ui&nc%BA#TURKh#9ytL>$u8w{-lLDYp zt&z>7&a`SwNB@@2Buq2S)N~YpwGZp$EQ^g&iRaIr0&sNLrQT@jgw!GtPck(f#V`zj zGwsn~mrAv!71LLyRQC7x4!C>o&d~p<aott$tM3;becyM0VHlWZno6}sBA&!B4EL9a zC#h6xm}c5tjZx#(8K$rLb?|irY}>}RZ2;Cku0?=>6;T<tZ99}pFid^zZTk6j%oUlN z9pmfvPj~Fe``wXWe&^|*CWyY)B?$2H`(q-pS;lWq&~J1Yk516<b#QjKfmPjb+6?IT zI&K?OU+X*ICZd&q*!+TXGC2c4zt`bM|Ac<81Hj4T46*rzP(Osi(APdQ=ZWloS`GaU zxNdzx5ClOG1VIo4K@bE%_#e4SJKPUfW{-QxW>Pd8J5IS=c5UjQv2;<^xYE|~4Nns| z0m>FvZ5WpLFqW4VBVO%9JQ+Ba_N2Xa0)XolkJ=HwXbx)C_xBFm{vfppfp&mtrl~iY zt{WVdBYYl8*E^RWXnBJw^R0FrfL6Oss~t%9(rj*S4m`Kob+VZh`9jeNtwIETXI@oe zsEQ4MOeO=MB_4!)q3A5WdcpDWF~FI*=vIhmCBW;`o2EIi1ESHWmhffR+S;Pq?GE(4 zU0DefEAVRl$)}&29pllPZ=vphFYoR<-kJnO(AtDRFU95;oM9e)*aqdOwWU5l_l4`M x5%??W1Jp!@ja|hNxGrr$5ClOG1VOk#`~tg>%2;x}(FFhi002ovPDHLkV1h)pFNXjC literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/equipped-INNERCLOTHING.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/equipped-INNERCLOTHING.png new file mode 100644 index 0000000000000000000000000000000000000000..b4d2825f0dad12ee6ba528a6faf710415bc5ee47 GIT binary patch literal 1338 zcmV-A1;zS_P)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ$>+#1v`j1WN4i%h>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<`9$!jB*Z5y61OOg)ia%)oPe-NVP%yC~1{KKJM7SMnwUd?N82(+!JwgLr1s z(mC%FhgeBch|h_~4Z0xlBiCh@-#8Z?7I<dJ$fV|pL&RdSgXIopB|{~iCJrmAM)^Y4 zWrgz=XSGset$Xqp2J+fUn(H)25yuh|NJ4~+8p^1^LX=jG6cZ`hk9qh<9DkBrGP%lN z<XAuzDkR4b{s+IiH49UdZc-oybidg4M+E5E1)6o+{yw(t<_X|`2ClTWzuEw1KS{5* zweS%T+y*YL+nT%wT<!qFPr77Cj^w8)6bium8GTa@7`g@e*4*A&`#607GSt=b4RCM> zj1?(+-Q(T8oxS~grq$mMXvuP;>U)db000AVNkl<Zc%1E;zi-n(6vw}*PzlANw4#+- z`a>Bz1R+N-vA~X0D_vPY>e8j<Z|I(Y6d@s%fv!Z3)P;psNS#{c6p^6|Qsg8QsjZq| zg%UFO7{Xc3FXMB4Nn7r-oSn1%eSY`O&iOq+B9TZW5{cwLCB%C|BAKE9u(rAqa{Emb zOe?NwNb9=Y`qdxuE1pQEXwf*NiDZh#HLayz6i$6YvMsG}edi5l^etL64qIAhzes(i z(XI8P?<r&w$rOF~;69FzP5}S~qln1-Tt^E46O-fE+s_3$9oqFLCdUB)ilU%as{ueu zU!eMlWQv-mNlnwVz0CF-jYiMDpdvxE{xvTL<@0$QAD!AG-D?DlQW^8JGj@Mah}3s= zKq8r<FP=X`wNk_0ehvVjsxjOiQvd+ra2WM^9RN_R)L@j#FiK^pY7Eh66aeUXLa`tE z|BQg`PoGgRidb7+3B7v#h7ywPIRchtVQqCKWLXwv&xG?LTHn<Hgb)xy?0w6!Tt}c_ z6uX}1mwxEGI$-ntMo0L#jey&Ee&~n3r-z^uFC!q5p!K;NFh<sq`??MQxH}2l92vsT zGYj92{`Q;P`do<6_Z%uyOUv{|^d9ObCc<}a^=yCTzu^0aw}Gz-MCo&_1+F5V95A)C zOdF%qsGqpUSpCFAV{{r*OUr?-2Sn;~O#uLCj85bF$S{7NH9FeiABO-SYzm0b=Qh;M zyCQD<qUlQ{5{X12kw_#Gi9~V*@U?zsc{gWbbWwZS*R^;?-=fasEa0y`fcvvJBQie+ zRgD2?eyU~$i7t{zBvZ6q8-VXm^4uRqSwTd>D1wkIR5b=dvgpUn1I<zHrp<~rc(2E0 zFw4_-Ls1lL@9g#^0-ij6WM_oh;jIneo&vKo5j!*EBpeQ7duNyTa=uttSg<ofPMld} zuW&oSnVZ|&&w)jRqI5b%6bA<fb_6(WaOMZS;yX9E@op1wO>4>14I%<&XCg@JI#?Y6 z@5At15zwBg?HA4nV582eNO<9PKw8h(+ZQ1Ms+Ahjdd98?P*s)dTHFqZYm4@Fsa!y* z+}s{i1hm^Ed<w8X9_?>}LB9p6m8L_4I>4Eq@7gc8K7iTq@nvu}T$=g-Q8vi>051m( wx;{Xp4VR`qK$H!au0G(R*dUQeB$BJZALC<)6~JI8k^lez07*qoM6N<$f{?Cpwg3PC literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/icon.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b5702f2d315a65145e95303190213ad15d400f79 GIT binary patch literal 842 zcmV-Q1GW5#P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ$>+#1v`j1WN4i%h>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<`9$!jB*Z5y61OOg)ia%)oPe-NVP%yC~1{KKJM7SMnwUd?N82(+!JwgLr1s z(mC%FhgeBch|h_~4Z0xlBiCh@-#8Z?7I<dJ$fV|pL&RdSgXIopB|{~iCJrmAM)^Y4 zWrgz=XSGset$Xqp2J+fUn(H)25yuh|NJ4~+8p^1^LX=jG6cZ`hk9qh<9DkBrGP%lN z<XAuzDkR4b{s+IiH49UdZc-oybidg4M+E5E1)6o+{yw(t<_X|`2ClTWzuEw1KS{5* zweS%T+y*YL+nT%wT<!qFPr77Cj^w8)6bium8GTa@7`g@e*4*A&`#607GSt=b4RCM> zj1?(+-Q(T8oxS~grq$mMXvuP;>U)db0004jNkl<ZScUDC%}N4M6vux`lv(se3Y8^3 zKoO$7$6(LUrcJ$rd)>GR+SInF=g=Ak+Xmuj6Ik>ylufhnwkTtyChskzg86L@bI+ag z|DOZ*UI+*X2>5RliX~AfmPEfbS;DH~069&gP%H_vZizy%ByyV8lOKoyuTY~|Bd2LS z(Q8b_KuA`6ExXE9>jr>Y-DEAhO1WaZ$5e6&fLMHna>ZbKYg4MWl(=o%!nSQ;)-7Sy zt@m%&b$xTPlNjo^LDzM<-FG{0G(X>aa#|(1m>?FPLDzMu2KyS=-`foV0wA4EvzSOR zH9ZbMBoe`K902ZaA2>O!a(Hy?{qN@nyu~t^jA%4#z7yzlI?}>Q2D}1aO`u#cRLZFv zfY&4FH-Yb<Y~WWXFs<h?&hr2)#9=HN=CR%3qV<-ymG$K=B7nK|4KcdBf^%&XnVs;B zPiIvw4|b&MD!m1RK<UWLt#1f-a-NasFwbrGLma-hl)6_o&^6(gQxy*g2>27c0ME?q Uy&tYp<p2Nx07*qoM6N<$f*UA?#{d8T literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/inhand-left.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..921ca6fbe4882156e67e0872e441d282a3954ec2 GIT binary patch literal 893 zcmV-@1A_dCP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ$>+#1v`j1WN4i%h>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<`9$!jB*Z5y61OOg)ia%)oPe-NVP%yC~1{KKJM7SMnwUd?N82(+!JwgLr1s z(mC%FhgeBch|h_~4Z0xlBiCh@-#8Z?7I<dJ$fV|pL&RdSgXIopB|{~iCJrmAM)^Y4 zWrgz=XSGset$Xqp2J+fUn(H)25yuh|NJ4~+8p^1^LX=jG6cZ`hk9qh<9DkBrGP%lN z<XAuzDkR4b{s+IiH49UdZc-oybidg4M+E5E1)6o+{yw(t<_X|`2ClTWzuEw1KS{5* zweS%T+y*YL+nT%wT<!qFPr77Cj^w8)6bium8GTa@7`g@e*4*A&`#607GSt=b4RCM> zj1?(+-Q(T8oxS~grq$mMXvuP;>U)db0005BNkl<Zc%1FpziPrz7zOZS=};;Z6ch^n zsSgk+k@hV*h+XZV(4|W|cJH1nS%fa_+9zr0rEg%9K|w)EK*y#-QK%-yCLijB^8*oX z;N0+e&5{EU5fKp)5fKp)Q4*Hz1eWatVe3ir*4-nGJ&6#@c7kT3vGV$V^P;Xb8;!(6 z4I?sunZ=un8_cG%R>`CduxuwdKRd-}I0gU|ig~=geE<Mz)yki>yW5-C{TB0jt_J`J z+hd))IH^y7QVQi#4L#R`Y1UyF20HB)I_(xrvyPtYp<Jp#DU~R1mSq@5qP#e%CkOzo zt4?5=CTi6R3dQ_<eKZ`S=Xy{|;rZ!t?Y*S)Ho~%<z;pY7@B8zQ=k^mnwWaf->`R6z z^}h6r?NVMCu>s`N19Ai4APZy<_b{1$BR2uQ2D_0LMQi}~(+gy#C&0)DvPZz=_dA$= z0_bi%FN)}c<oNP>>Hj%+=_cMoI**8mh=_=Yh=_=YbSBXu5PdzqNm3mGZ3h6zbO^M3 z$spMw(DDRGb_ldQ0f43*0%;N*0?~fJHl*zkXgvUl4uRGKkmwL-|BzsZfYSK^!<=3? Th#FN(00000NkvXXu0mjfVq%#8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/inhand-right.png b/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..ed69a4573879784833399193fc173927945eaf4f GIT binary patch literal 921 zcmV;K17`e*P)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ$>+#1v`j1WN4i%h>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<`9$!jB*Z5y61OOg)ia%)oPe-NVP%yC~1{KKJM7SMnwUd?N82(+!JwgLr1s z(mC%FhgeBch|h_~4Z0xlBiCh@-#8Z?7I<dJ$fV|pL&RdSgXIopB|{~iCJrmAM)^Y4 zWrgz=XSGset$Xqp2J+fUn(H)25yuh|NJ4~+8p^1^LX=jG6cZ`hk9qh<9DkBrGP%lN z<XAuzDkR4b{s+IiH49UdZc-oybidg4M+E5E1)6o+{yw(t<_X|`2ClTWzuEw1KS{5* zweS%T+y*YL+nT%wT<!qFPr77Cj^w8)6bium8GTa@7`g@e*4*A&`#607GSt=b4RCM> zj1?(+-Q(T8oxS~grq$mMXvuP;>U)db0005dNkl<Zc%1FpPfNmZ7zgmL(IFB71%<#r z`2q$psdv#q>}m&vE?w%_z590AMd(u3UP;l9-oe@q3JMYfJ+=;^nbY~(+;WTW2W0S* zx$mCM?eIJR5fKp)5fKp)5dnav8?L4s?z*+42%C3j)sHnB4c~)>N*R+)@-8jFRI|~T z?L6K39cbIa<jpKDXu9E^o*ZL17y$qZ#XO#0-T(l#YUS73&GnUM|G&0rUe6sMgh07e zgK61N)jAYKL8sk9r`>|8)?r#U%B32F5V4+^tWEPSy@IWaj;pFFYSjt~#r$l2I2ggS zYzQIn_;A1Vt3MS>UQIV#+v>TF<IFy`)l2kSAWYs}zb}P&^;9&;!sK09fSkBPt`F>G zf$aVc#*<Iv#=u8Eny^s|n|EaaZYO8ROpbt|17r_?@%Pr9yaVV)8a@hP^ZGs*4$m(= z|DXO-H!%;tMQO`RL_|bHL_|bHM3ii4n;DSd+a90icarmM>9mn90PxdmpYOpZd0s$< zRMTLw2X9(70AN{PJ_$1eK}g*Ji#_;h|6Ng(M9vHZA$_&dHZu^5w9O32@SP7>CT%kV v>jWx5+GYl11S&w<W(H&gerT}=PyfpoWqo5&-alFE00000NkvXXu0mjf%{!=7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/meta.json b/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/meta.json new file mode 100644 index 0000000000..a81e5b9477 --- /dev/null +++ b/Resources/Textures/Clothing/Uniforms/Jumpskirt/cmo_turtle.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Borrowed from QM turtleneck at https://github.com/space-wizards/space-station-14/tree/master/Resources/Textures/Clothing/Uniforms/Jumpskirt/qmturtleskirt.rsi and modified by Hanzdegloekr (github) for space station 14", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-INNERCLOTHING", + "directions": 4 + }, + { + "name": "equipped-INNERCLOTHING-monkey", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/equipped-INNERCLOTHING-monkey.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/equipped-INNERCLOTHING-monkey.png new file mode 100644 index 0000000000000000000000000000000000000000..2cc39c88940baeb9832fac9a58e4f04b2092128c GIT binary patch literal 1277 zcmV<Z1OoesP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ>9X>4i*$~$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<-o+;YS|^5ky#Grk+SIX5cx#?&0I>U6f~epZjz4DtVIuK9P8i>4rtTK|Hf* z>74h8!>lAJ#OK8023?T&k?XR{Z=8z`3p_JyWK#3QVPdh^!Ey()lA#h$6Gs$PqkJLj zvch?bvs$UK);;+PLwRi_&2^e1h+_!}Bq2gZ4P{hdAxf)8iis5M$2|PQjz38*nOtQs zax9<<6_Voz|AXJ%nuV!JHz^bYx?gPjV-yJN0?oQ@e;?a+^91le16NwxUu^)hpQP8@ zTI2}m-v%zO+nT%wT<!ovPr77Cj^w8)6bium8GTa@7`O$3Yi@6?eVjf38R}~J1~@nb z#)_1^?(y!P&ffk#)9UXBLsoK>gn$H40009tNkl<Zc%1E;%WE1@6vjV8F%YDA(SoVA zx(VG3$cX+IT@1QyDGhbeP)MMgwgj^3s-RHZ1X4<}P4KVCMqv=LNa@CB6%v|95lLM1 zb`kC{zLGgt#}ayeAlJF0=ewT!Q27qPFbu;m48u4hoym%HCM$01yDR!rRB-@E7zWar ztf*ERBAv;Kgkf~ucMCW4Jrwcqlg9vnQl*N7VW3toAs7f_KbM33-ul|A<G2+4cq{?{ z$QO!;$0B{LyhqW$M;U~!M=>|=0|0D)-9^GMu%F9e<<)Zl!1-AVrAn0&l0+~N#@xIQ znx+9jUweMmLcUNW#dMS@nf;UFQ#^RMF!XiuK6MtH>N|y^@AwSRG!42QMZQo(Fc5~O zY1Shc2qRx8Lf50#Y>XSb&M<x1Pr=a^FijJtX#xOtceXtMx@JUX+%!#B4na5d$;<TH z3%^*nfBk$pG=(4KKLCK!-@wbA=GYIvoAm9d>XQyZ0C?Yi2G4Bh`l13Wg`!;sutg0= z#;$+W?qGVxJCZ)>cYvL%cBiYotB{tAKbaC9e+XCY4!jo)tG_e5gy|VCM6-<M-j-|I zpjY3nwe<FrP|+uE0RVmC{$xsECZrNdi9z~}FCUZ=ue=5R6~ngN;>xD*CsPyoKf*8! z!!QiPFbu;m48#0)OwtZ_!o=)xJMmZq)k;Gg92{6SwQ4LX(i)fA6yLBH<pfAuT()jl z;=@>9TlLtrgMqMeEb^f3bpik^w|Lx+@NHv|vwm`XYW1tsCMc}{U5}zvsakHZTaNHe zNL~9_R3I56GV`^12>?*5mr$!K>5k0d;bGTxtzJSr7C}0b6|Pl?0N0t9l^C*O0{~*N z7yyvORY+&DV)e~yTwY!RK;KxX6(Xbr*nN6k*Sl7L&*vivkHXQ>5n8QQSKr5t4W(Gh zvo*-4Uk**->ARn<u7K~K9|>8LFb-X%H!ZGg3hB#B206BF;7_K6bfbIqe{FA)waMQC zgAB+?eRu$9?rnLz7ma}?sfsx|vO7G<4==eX2~@}l7^nES7-bTaSAh4T0nsd5l_Hb$ n=A~Be7#N0O7=~dO#yNihi6rQ}i`JN@00000NkvXXu0mjf3Vuw^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/equipped-INNERCLOTHING.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/equipped-INNERCLOTHING.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1557db217275f2cb2dad3dec9bb4152a28e528 GIT binary patch literal 1449 zcmV;a1y=frP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ>9X>4i*$~$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<-o+;YS|^5ky#Grk+SIX5cx#?&0I>U6f~epZjz4DtVIuK9P8i>4rtTK|Hf* z>74h8!>lAJ#OK8023?T&k?XR{Z=8z`3p_JyWK#3QVPdh^!Ey()lA#h$6Gs$PqkJLj zvch?bvs$UK);;+PLwRi_&2^e1h+_!}Bq2gZ4P{hdAxf)8iis5M$2|PQjz38*nOtQs zax9<<6_Voz|AXJ%nuV!JHz^bYx?gPjV-yJN0?oQ@e;?a+^91le16NwxUu^)hpQP8@ zTI2}m-v%zO+nT%wT<!ovPr77Cj^w8)6bium8GTa@7`O$3Yi@6?eVjf38R}~J1~@nb z#)_1^?(y!P&ffk#)9UXBLsoK>gn$H4000BvNkl<Zc%1E;KTH!*9LK*US`WOa5FtVN z6XL{xF-LTA(Vc_~tS*|cuz;I8d#g!fLV|;=kZZ!?BE*CV(n~`J7L#&qEd~sQrk2v- zo<n($*HUWV_3mo$eU@wQ_3rz--}~M5?!G@jB9TZW5{cwLB_O&%C>$XGFh4gNXtd5N zsMeIOW7RO&(y4xjQ}IwZLQ+<igu)RrrR!DWUcu{|W~^%IxjuiuQDcpytZdbiANNw9 zYSddg(Qm3`Lg5H`_Vfu34~_r;Nh^hs;h{@Q0O;-O!Pa)%)zhI?zqhXk0HCTW3WWjy zRE@c+9|}iEKA$J~e4b^QelM5HSB|+=30~{pXy!+WL;{BgM{K6AngJ`F!SLV!8+QvY z^&2K26poNLuV3LfSHRYG8~~tc<G9zY0ss_6L8(*%037EEu+kY==?pY&9Ajf+06<zP zML+caS^?`{zaeR*Fh4gNc>m!eG0oVOGho{`=I3Suwrvx7O=uOb^&2L@G)<VM$-djR z-EanyR;uoHPVqy(VFH#vFI_61Ml;Z8J16*|-_%8rr<ob>61Vk*63}ffApU&;0PwI6 zxZTx(-$ffg4$jB(!umpZp)Ztxcf|>GK6(rQC>>s?DZO_90H;5904$0}(MEltX8}s- zS4I4+Sg!i+7jEhcCx90Zo@9Mbg4_CzDu9abuPg@mApF8jeMbqnPRIm^Zg5q(`E?q& ztuK5d6g<f`wazQrtuK*CBoc{4B9Tb02aaA}Q{Rm@Mkn<N&wB%SqQ)BG%~`-%e}M4Q zAZ28D2%0typz^Ak2IO^<L?|2~wb}q2zmgYzA3z%-l2!_)8H1*c!!%>?<KThHEZ5Vf zO&g;76AGC6^z~3x73&+DzEr@=7tfg^REubDfbc0WI55JT86HJZu)eV=x;saVj*c=% zh=+HPn?*PQyf?SC9S5xlRlS@Puh`w)Wfj23!25&E;@BHp`m~HGU9a-#+ED?610z^9 z4A4FT(TibA6;SKc`UPJBbk=DXi73JeST&;T^S@95$GHMljVS8}&@@eGU&0BP(kI#H zbS8;(rt-O66;K<K=cfR@@u>YLX!pOsajudOPZPlV^L594@JGAN1$=qyUJSzh0p*(y z006hTf^5BV-=F>f8UyPQ;2dG<3<Zl>p%{ek+WMzwNcrwX64=2fcCf#)=*t69|MU#$ z9J@#YJNR?QSY%(q&jQqv=JE63X$+@{ovL+C4jyp99rn9B#BFV9kGDI3cGFS<&WdG_ z{XKUgaRAxhYq>AMaRP!TS?0_8c>$zQO+F5ZL?X-|Ar5#r8(2bL00000NkvXXu0mjf D#(A9n literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/icon.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..334ef02242d3f65f165dee34405bb3a1b6b146d8 GIT binary patch literal 837 zcmV-L1G@Z)P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ>9X>4i*$~$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<-o+;YS|^5ky#Grk+SIX5cx#?&0I>U6f~epZjz4DtVIuK9P8i>4rtTK|Hf* z>74h8!>lAJ#OK8023?T&k?XR{Z=8z`3p_JyWK#3QVPdh^!Ey()lA#h$6Gs$PqkJLj zvch?bvs$UK);;+PLwRi_&2^e1h+_!}Bq2gZ4P{hdAxf)8iis5M$2|PQjz38*nOtQs zax9<<6_Voz|AXJ%nuV!JHz^bYx?gPjV-yJN0?oQ@e;?a+^91le16NwxUu^)hpQP8@ zTI2}m-v%zO+nT%wT<!ovPr77Cj^w8)6bium8GTa@7`O$3Yi@6?eVjf38R}~J1~@nb z#)_1^?(y!P&ffk#)9UXBLsoK>gn$H40004eNkl<ZScUDBO-lk%6o#LPD6@!^6e3Ne zeTb<4qQzjpp-r1wws${n+z4%ITh#CHV-VMY_>qN1#ZYE0ye-UOjLf-)L^RLla?iQ< zyze=1FE}_j{57<6Mri4bm=v?ioUAxNQdQB?8Bwb?g_h2Uq^geOXJWuARBu#Bs_MvU zH6~*~u`8a4M`*Vm0H{=JB;pZr`I5PYLt6m&{9bbT5=VyzwrblFHw;4<h9PRzrl?h$ z=DyeKjm_De#QdZKx~?<qe%N`v@%7n4u}ml!z~}d(>$<H5CmJ|AJy8Gw5RFC&2Ewd) zRse9hTy(o#0G=M7DHO|GTwYo4{oH`HSS%J3^+sjv1p58HZDA<`R)Oy(kjs~3%E=pm z!4n)efoYI7@T(K>=qXCqDF8P8u<V}WrPJr8^^rHE_2mp4cXnCY-l2PMK--c~z_q^0 z>s=Yh&dvZZ5C`5mJu|lGUNGabNPm|bd;8`OcYB^yKJTY;;o#ul@UM9XpmX24nfP$6 P00000NkvXXu0mjf!<UC= literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/inhand-left.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..ad7541ea1d42d0ec6a5895024f4163316ae16b0b GIT binary patch literal 892 zcmV-?1B3jDP)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ>9X>4i*$~$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<-o+;YS|^5ky#Grk+SIX5cx#?&0I>U6f~epZjz4DtVIuK9P8i>4rtTK|Hf* z>74h8!>lAJ#OK8023?T&k?XR{Z=8z`3p_JyWK#3QVPdh^!Ey()lA#h$6Gs$PqkJLj zvch?bvs$UK);;+PLwRi_&2^e1h+_!}Bq2gZ4P{hdAxf)8iis5M$2|PQjz38*nOtQs zax9<<6_Voz|AXJ%nuV!JHz^bYx?gPjV-yJN0?oQ@e;?a+^91le16NwxUu^)hpQP8@ zTI2}m-v%zO+nT%wT<!ovPr77Cj^w8)6bium8GTa@7`O$3Yi@6?eVjf38R}~J1~@nb z#)_1^?(y!P&ffk#)9UXBLsoK>gn$H40005ANkl<Zc%1Fpy-F)V7{>9xVj%_&L{Jd? zIJtlYS<$<&2-?QNfX!*NJiYyHvyEV*tyiKrcn5K}FrXk3$d;~^f|{7P`w|^^evoDs zo(XRz)9eExA|fIpA|fIpQbrUfMHDB+p!KqO<L*(#v_(i1C&f;)`Q`KP<_&wc(`=R= z>L4Zqc=hqijT+3Pvc57^H6V(UVrz4qhx<nW78d8Zyt)CPv9|hd?db5|$o)R%b<zxg zL3@moH%jFb;GAP+xj`q*2*VA6AfUb9qP^cD3^(Yc87s>ToO7k}UbBKAD3v!#<p~0` z_S!`lhBVezSy-HZTYtEJq?2Yi=Quw*9eXe9yore7q{z~{qSx!aeX{hf^sQ~3H_VJ> zm~+>kezD(_H;CB)>h73&7iMcvo13PeKU43+Q}=h|4P!Qd|M@mk`CrI^eh+GY04VZX z;N-{ihB3WJmUa(5{Xe@GC#CnW&Jz(45fKp)5fKp)pILMWOka;rl2wPm+W~-0hrr91 z43-@NFHeAFhrr7d0G@UTtXXsjO#1;pp=yV~>j7AF2)rJEMTfxqOF|t2spbVqa$7hK S+K!e00000<MNUMnLSTYN)0f}? literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/inhand-right.png b/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c0133d22a2059b63537cb966960ad724c7307921 GIT binary patch literal 917 zcmV;G18V$<P)<h;3K|Lk000e1NJLTq002M$002M;1^@s6s%dfF0004mX+uL$Nkc;* zaB^>EX>4Tx04R}tkv&MmKpe$iQ>9X>4i*$~$WWauh>AE$6^me@v=v%)FuC*#nlvOS zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0Yam~RI_UWP&La) z#baVNw<-o+;YS|^5ky#Grk+SIX5cx#?&0I>U6f~epZjz4DtVIuK9P8i>4rtTK|Hf* z>74h8!>lAJ#OK8023?T&k?XR{Z=8z`3p_JyWK#3QVPdh^!Ey()lA#h$6Gs$PqkJLj zvch?bvs$UK);;+PLwRi_&2^e1h+_!}Bq2gZ4P{hdAxf)8iis5M$2|PQjz38*nOtQs zax9<<6_Voz|AXJ%nuV!JHz^bYx?gPjV-yJN0?oQ@e;?a+^91le16NwxUu^)hpQP8@ zTI2}m-v%zO+nT%wT<!ovPr77Cj^w8)6bium8GTa@7`O$3Yi@6?eVjf38R}~J1~@nb z#)_1^?(y!P&ffk#)9UXBLsoK>gn$H40005ZNkl<Zc%1Fpy-Fid7{>9xVj&w4A}B2Q zvAKW&8FBAoi=b_`FtCk{TiV+vO&Y;QTdzd%uy>H1DGVsc63CQ0TN&2*$ebAxog>c= z(wxIgo_WvYq&P2#h=_=Yh=_=Y2tcnth_lAB#_J$2TT$NSyqo1+aM*4a-t?{Y`h)ne z-JUt0H<wueNIz)Nm()R(%uC6EUVjjOJ=o*v@fm<&5CZW3*v95Mw|Bqzd3ut%{;ehR zzGMSv?e4r9%gUR21B@|jZ?zZ(A-=y)_qap%xP#|;`2Ie_AY^;1g)wHK4`wTw*Ymt- zW}Cd3E7<wojeXyzwY$T{=K6H}>G7Fi5Mqqs`s(uCSO3LA^7i_JI1C=*@pwG_guz3t z{{^b#U70m$%zdh&byg*BZULI+oaP8t8_@W)%q03ta{_-x^9eg|RrBT+;4J#aQuGBP zU^0fr8US(h8@QNT@Oi14cjm_M`RGUL_cOY=sLke=<XT=45fKp)5fKp)k$Q8jXTV18 z@pzJJJp;C^3jl8R3>2|CG;pnFpbV=wz_p%%GOSn2wVr{6aII&+#?1bJEUxto6e(2z r*LntQlq!I0Jp(pMZ(2HomyhKiZ2wz0|4;#A00000NkvXXu0mjfjnuaa literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/meta.json b/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/meta.json new file mode 100644 index 0000000000..58b759095d --- /dev/null +++ b/Resources/Textures/Clothing/Uniforms/Jumpsuit/cmo_turtle.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Borrowed from QM turtleneck at https://github.com/space-wizards/space-station-14/tree/master/Resources/Textures/Clothing/Uniforms/Jumpsuit/qmturtle.rsi and modified by Hanzdegloker (github) for space station 14", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-INNERCLOTHING", + "directions": 4 + }, + { + "name": "equipped-INNERCLOTHING-monkey", + "directions": 4 + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Nyanotrasen/Clothing/Uniforms/Jumpsuit/summer_security.rsi/equipped-INNERCLOTHING.png b/Resources/Textures/Nyanotrasen/Clothing/Uniforms/Jumpsuit/summer_security.rsi/equipped-INNERCLOTHING.png index 29212f6ca00c4fdef46023911d41d3ea45b16589..71aaaaafebe5c60b60fc461f27b378d1b0117818 100644 GIT binary patch delta 1116 zcmV-i1f%<=1LX*i7=H)`00020X>r~F00b;aL_t(|0qvU4N*qBD##>oXL{`Z`i0I8r z2$BboJVEYxwmd`bc>>83h~(l?i0mOm5m8*+^sn7-=rrR_SGzMB(gnNyXL{<Z{<^AW zm~Q8)R0b*om4V7YWuP)p8K?~WpA4*6rhVkxY<o0v#~T~&>VN90xpzDs&pzJVxZl&M z`|e!RZ_%AI_S0xI1LI^o4#Pbzc7*z&FR|_0bZWxS9t`-~^>z2ggZ}bhUsqS14EX%; z(DeVjy)_$=J9+RC*#K5-6W6TxtK8Hjto-|(on{yYFzF8$7onsrVI1StUPc+?3yV^d z2Sfc<{85*ItbaU8S#{VI5T)wU!FA+lch_xCCNrx$N>5xqg&&Vmy#&A01Bmx8*Vh4q z_=AWxp`Ysr<GF7cvIsw3fKvSI96+K|qKRFPNy<BvpyC9%3=+^0<HzAB{il;jSO5~F z1-~-{1b|YXWt&8(e#&Mgd<a;;V;x;};WG}x8_ao|et(_<R{YKoK(RzFu69~>T~Ehb z+vwMmAibCV5_}T@Dd<bJ<JHH8v~95~I|QUunKlMh4$zoBOW~&&PxYtc(sp(d<VF$^ zrII0a1lQBsnKcF<HRSyZf6-_wzS#qI{5!!(iv{#gd?xNwJOOUq$NJjZT7bE{ymVVz zTcLh;cYo(@ACs`j1Tz6>-{0R4AUFQ~e`Ef<$AF>5uXaN~DYl6KRsi-j+57wZrcOWC zF{)1ihn1J8jwj#&Rw;f80Cs+UzET2eH8LGf7$AywwbOAq?P3CQl>ZB3Od$}vx9TcO z5|EW3VR4`8Du=+g+FQq01}X!UfyzK-pfXSysDBJp1}X!UfyzK-;FU69`YNnB=$wGs z>3IS5_gV6Vm@^3yzD$>#O)wL{<lk*>{<Rswz5wS15dVR~tW%T7DVaFqbS6RfV8ai; zDQhLcWNds$0^oR$uG9ccAh%2xAhm%F63e!{>6;*KrX3hfScbDxglF_EsrRw!*n(i2 zb$@@*^i5E^KgXOG@GgJoLE{w&Rt)&3iHGpRCY?zze3~~A&=TJ>c?2GydY>GC0Tz=N zUW;AXCYT6FwXz={AC?GP*Q`X3qV4f!bJi2$d}x-6DF?KHG={WY?743l*d_wV!10{O zwu-~Jf2caxX~s`&?6T!qA%JqlVOsA3E`PngY&??(=qV5z&s!iy@v}aHVm-NaJ&M6= zJBik2<09Rr7a`ul*a2o!6r~3T2O%Wla{<rE$cqfsHN6Sa=1c-|c6Ju*+1uNjd3@8` zsEGi;L_)kHt1W90!?-?$iLxE8ZF;b@H)ve|a}JmTgq7E64fg1Vnmm)8V>Kj{PE{UV zrX0Wx5=~z1mn!kT*c+{O6cIokkl=>I-OvJN90IXT7F*0AAmzB7Tu*&Nmn7fPi4SdS i#j6Zd1}X!ul7T<a;X(}t5|kYP0000<MNUMnLSTZqT@UO4 delta 408 zcmV;J0cZZ@2&My&7=Hu<00013M{Ml?0016POjJbx000gS4jddDAt50oBqS><D?0!H zO9TXD3=DP}8tXyXZ2$lO0d!JMQvg8b*k%9#0X9iQK~zY`?UX%k!!Qg+DWkC0$OxQ{ zj*;G@4&npUO?GEW#(441*u}CgP?NXq%1=PkrAyIpd%^=D3V-@Y^r+vgxFypmPR^;l zXkp;=nQw}{^RwcZ#d-eJ%%Ye^CnAf#DQ3-NCoeL6tiEHSY!L1=k0P2W@gh$1Al$>4 zUS!fd3r}~2fGHk?JK%ZNOeX6)tJzv>K`5;)6-QNl+JjKNpHO&1Av*34wdQ(T)F|Y; z`VRjrm|~S~?|<;4{@a4+TRhnG4e(VMzPa@noWwI8T*d2!Lh(2WYr)HPX&_0NM}g0F zg|{3QjiYOVF_N&uEfm5#pb!&<9f%qf1_z7Np<swMurMwyk)FcjPbI$O`Za<v(?gYG znh%&wgJC9m+2cEzDin?=L{*8x`j}&f!v5v&`~{QzW)TUCF}{=l0000<MNUMnLSTZ& CgtNf_ From 975c905d1bd33652d5d956bebcbb274e7cf23f34 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com> Date: Sun, 5 Jan 2025 20:47:03 +0000 Subject: [PATCH 19/38] Automatic Changelog Update (#1443) --- Resources/Changelog/Changelog.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 93a9ca560a..daadc24edf 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9320,3 +9320,34 @@ Entries: id: 6641 time: '2025-01-05T19:45:57.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1438 +- author: The Den Contributors + changes: + - type: Fix + message: >- + Put actual prescription lenses in the prescription medhuds and sechuds + (by KyuPolaris) + - type: Add + message: >- + Added the CMO turtleneck and head mirror to the CMO loadouts. (by + sleepyyapril, KyuPolaris) + - type: Add + message: >- + Added an armored trenchcoat for the Captain. Enjoy your swag! (by + Rosycup) + - type: Fix + message: Fixed uneven pants leg on summer security uniforms. (by Rosycup) + - type: Add + message: >- + Added the Captain's Combat Gas Mask, for those more fond of close + encounters. (by Rosycup) + - type: Tweak + message: >- + Added the Captain's Trenchcoat to the other captain locker variants. (by + Rosycup) + - type: Fix + message: >- + Fixed they/them pronouns being displayed for it/its characters in the + character preview. (by Azzy) + id: 6642 + time: '2025-01-05T20:46:36.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1443 From 8b5eec786cd82d6f48901e04a1045e274ec0fd3c Mon Sep 17 00:00:00 2001 From: Skubman <ba.fallaria@gmail.com> Date: Thu, 9 Jan 2025 03:37:24 +0800 Subject: [PATCH 20/38] Fix Face Markings Rendering Above Clothing (#1467) # Description Fixes face markings rendering above clothing, most noticeably eye-wear by moving the Face layer below the Eyes layer on all species. ## Media Before (red markings rendering above sunglasses):  After:  # Changelog :cl: Skubman - fix: Fixed face markings showing up above eye-wear. --- Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml | 4 ++-- .../Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml | 2 +- Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml | 2 +- Resources/Prototypes/Entities/Mobs/Species/arachne.yml | 4 ++-- Resources/Prototypes/Entities/Mobs/Species/arachnid.yml | 2 +- Resources/Prototypes/Entities/Mobs/Species/base.yml | 4 ++-- Resources/Prototypes/Entities/Mobs/Species/harpy.yml | 4 ++-- Resources/Prototypes/Entities/Mobs/Species/moth.yml | 2 +- Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml | 4 ++-- Resources/Prototypes/Entities/Mobs/Species/vox.yml | 4 ++-- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml index 9d7a85f97d..71bb59b04e 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/lamia.yml @@ -74,6 +74,7 @@ color: "#008800" sprite: Mobs/Customization/eyes.rsi state: eyes + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] color: "#e8b59b" sprite: Mobs/Species/Human/parts.rsi @@ -106,7 +107,6 @@ - map: [ "belt2" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] @@ -252,6 +252,7 @@ color: "#008800" sprite: Mobs/Customization/eyes.rsi state: eyes + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] color: "#e8b59b" sprite: Mobs/Species/Human/parts.rsi @@ -283,7 +284,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml index 0f7b0900bf..040d4bee13 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/Species/vulpkanin.yml @@ -31,6 +31,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -64,7 +65,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml index cd51a8053b..7c65b89a76 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml @@ -91,6 +91,7 @@ - map: ["enum.HumanoidVisualLayers.Head"] - map: ["enum.HumanoidVisualLayers.Snout"] - map: ["enum.HumanoidVisualLayers.Eyes"] + - map: ["enum.HumanoidVisualLayers.Face"] - map: ["enum.HumanoidVisualLayers.RArm"] - map: ["enum.HumanoidVisualLayers.LArm"] - map: ["enum.HumanoidVisualLayers.RLeg"] @@ -125,7 +126,6 @@ - map: ["belt"] - map: ["neck"] - map: ["back"] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: ["enum.HumanoidVisualLayers.FacialHair"] - map: ["enum.HumanoidVisualLayers.Hair"] - map: ["enum.HumanoidVisualLayers.HeadSide"] diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachne.yml b/Resources/Prototypes/Entities/Mobs/Species/arachne.yml index 5c203640d8..2aa1a381bf 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachne.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachne.yml @@ -27,6 +27,7 @@ color: "#008800" sprite: Mobs/Species/eyes.rsi state: eyes + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] color: "#e8b59b" sprite: Mobs/Species/Human/parts.rsi @@ -66,7 +67,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] state: bald @@ -173,6 +173,7 @@ color: "#008800" sprite: Mobs/Species/eyes.rsi state: eyes + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] color: "#e8b59b" sprite: Mobs/Species/Human/parts.rsi @@ -212,7 +213,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] state: bald diff --git a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml index 32e26987c8..3a4e1791fb 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/arachnid.yml @@ -81,6 +81,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -110,7 +111,6 @@ - map: [ "enum.HumanoidVisualLayers.Tail" ] # Mentioned in moth code: This needs renaming lol. - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] # Do these need to be here? (arachnid hair arachnid hair) - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index a9d96ecde5..2788ab89f3 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -13,6 +13,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -41,7 +42,6 @@ - map: [ "id" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] @@ -349,6 +349,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -380,7 +381,6 @@ - map: [ "id" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml index 34232b4e6e..f48fb560e1 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/harpy.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/harpy.yml @@ -14,6 +14,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] - map: [ "enum.HumanoidVisualLayers.LLeg" ] @@ -38,7 +39,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] - map: [ "enum.HumanoidVisualLayers.Tail" ] @@ -148,6 +148,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] - map: [ "enum.HumanoidVisualLayers.LLeg" ] @@ -167,7 +168,6 @@ - map: [ "belt" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] - map: [ "enum.HumanoidVisualLayers.HeadTop" ] diff --git a/Resources/Prototypes/Entities/Mobs/Species/moth.yml b/Resources/Prototypes/Entities/Mobs/Species/moth.yml index f8eef07f8f..6d0c1182e9 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/moth.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/moth.yml @@ -76,6 +76,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -110,7 +111,6 @@ - map: [ "enum.HumanoidVisualLayers.Tail" ] #in the utopian future we should probably have a wings enum inserted here so everyhting doesn't break - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] diff --git a/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml b/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml index f45adde261..2063225a8e 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/shadowkin.yml @@ -153,6 +153,7 @@ - map: ["enum.HumanoidVisualLayers.Snout"] - map: ["enum.HumanoidVisualLayers.Eyes"] shader: unshaded + - map: ["enum.HumanoidVisualLayers.Face"] - map: ["enum.HumanoidVisualLayers.RArm"] - map: ["enum.HumanoidVisualLayers.LArm"] - map: ["enum.HumanoidVisualLayers.RLeg"] @@ -187,7 +188,6 @@ - map: ["belt"] - map: ["neck"] - map: ["back"] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: ["enum.HumanoidVisualLayers.FacialHair"] - map: ["enum.HumanoidVisualLayers.Hair"] - map: ["enum.HumanoidVisualLayers.HeadSide"] @@ -265,6 +265,7 @@ - map: ["enum.HumanoidVisualLayers.Snout"] - map: ["enum.HumanoidVisualLayers.Eyes"] shader: unshaded + - map: ["enum.HumanoidVisualLayers.Face"] - map: ["enum.HumanoidVisualLayers.RArm"] - map: ["enum.HumanoidVisualLayers.LArm"] - map: ["enum.HumanoidVisualLayers.RLeg"] @@ -299,7 +300,6 @@ - map: ["belt"] - map: ["neck"] - map: ["back"] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: ["enum.HumanoidVisualLayers.FacialHair"] - map: ["enum.HumanoidVisualLayers.Hair"] - map: ["enum.HumanoidVisualLayers.HeadSide"] diff --git a/Resources/Prototypes/Entities/Mobs/Species/vox.yml b/Resources/Prototypes/Entities/Mobs/Species/vox.yml index 5de01dbe6a..a26cf9f061 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/vox.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity parent: BaseMobSpeciesOrganic id: BaseMobVox abstract: true @@ -65,6 +65,7 @@ - map: [ "enum.HumanoidVisualLayers.Head" ] - map: [ "enum.HumanoidVisualLayers.Snout" ] - map: [ "enum.HumanoidVisualLayers.Eyes" ] + - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.RArm" ] - map: [ "enum.HumanoidVisualLayers.LArm" ] - map: [ "enum.HumanoidVisualLayers.RLeg" ] @@ -83,7 +84,6 @@ - map: [ "id" ] - map: [ "neck" ] - map: [ "back" ] - - map: [ "enum.HumanoidVisualLayers.Face" ] - map: [ "enum.HumanoidVisualLayers.FacialHair" ] - map: [ "enum.HumanoidVisualLayers.Hair" ] - map: [ "enum.HumanoidVisualLayers.HeadSide" ] From 998e0ea99369e3780aa75c536fbe8c3bd2594d32 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com> Date: Wed, 8 Jan 2025 19:37:54 +0000 Subject: [PATCH 21/38] Automatic Changelog Update (#1467) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c0d361fc67..0c543a80f7 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9466,3 +9466,10 @@ Entries: id: 6652 time: '2025-01-08T07:09:10.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1456 +- author: Skubman + changes: + - type: Fix + message: Fixed face markings showing up above eye-wear. + id: 6653 + time: '2025-01-08T19:37:24.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1467 From b39e126d6e87d98cadd1aaa4b3ce2cae4c2bd766 Mon Sep 17 00:00:00 2001 From: Lumminal <81829924+Lumminal@users.noreply.github.com> Date: Thu, 9 Jan 2025 23:32:06 +0200 Subject: [PATCH 22/38] Prisoner Headset Box in Warden's Locker (#1470) # Description Adds a prisoner headset box for warden's locker Reason: There's too much chaos in security and making radio headsets with pure encryption keys takes time and tools. More so cause by the time someone gets a perma punishment, its gonna be most likely late in the round and sec department will be dealing with a lot of things at the same time. --- <details><summary><h1>Media</h1></summary> <p>   </p> </details> --- # Changelog <!-- You can add an author after the `:cl:` to change the name that appears in the changelog (ex: `:cl: Death`) Leaving it blank will default to your GitHub display name This includes all available types for the changelog --> :cl: - add: Added prisoner headset box to warden's locker. --- .../Prototypes/Catalog/Fills/Boxes/security.yml | 15 +++++++++++++++ .../Prototypes/Catalog/Fills/Lockers/security.yml | 2 ++ 2 files changed, 17 insertions(+) diff --git a/Resources/Prototypes/Catalog/Fills/Boxes/security.yml b/Resources/Prototypes/Catalog/Fills/Boxes/security.yml index 82da52de3f..1aefbf7a68 100644 --- a/Resources/Prototypes/Catalog/Fills/Boxes/security.yml +++ b/Resources/Prototypes/Catalog/Fills/Boxes/security.yml @@ -17,6 +17,21 @@ - state: box_security - state: handcuff +- type: entity + name: prisoner headset box + parent: BoxCardboard + id: BoxPrisonerHeadset + description: A box of prisoner headsets. + components: + - type: StorageFill + contents: + - id: ClothingHeadsetPrison + amount: 4 + - type: Sprite + layers: + - state: box_security + - state: headset + - type: entity name: flashbang box parent: BoxCardboard diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/security.yml b/Resources/Prototypes/Catalog/Fills/Lockers/security.yml index e2d7ad1483..fe1d9ecc72 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/security.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/security.yml @@ -24,6 +24,7 @@ - id: BoxPDAPrisoner # Delta-V - id: ClothingShoesBootsWinterWarden #Delta V: Add departmental winter boots - id: BoxEncryptionKeyPrisoner #Delta-V + - id: BoxPrisonerHeadset - id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes! prob: 0.3 @@ -52,6 +53,7 @@ - id: BoxPDAPrisoner # Delta-V - id: ClothingShoesBootsWinterWarden #Delta V: Add departmental winter boots - id: BoxEncryptionKeyPrisoner #Delta-V + - id: BoxPrisonerHeadset - id: LunchboxSecurityFilledRandom # Delta-v Lunchboxes! prob: 0.3 From 49076397530a63a50ad723c28c37bf00ad76201d Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com> Date: Thu, 9 Jan 2025 21:32:34 +0000 Subject: [PATCH 23/38] Automatic Changelog Update (#1470) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 0c543a80f7..3015e03292 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9473,3 +9473,10 @@ Entries: id: 6653 time: '2025-01-08T19:37:24.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1467 +- author: Lumminal + changes: + - type: Add + message: Added prisoner headset box to warden's locker. + id: 6654 + time: '2025-01-09T21:32:06.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1470 From 86296da6e269e65221f68f0a2df38f3beeb9ff3e Mon Sep 17 00:00:00 2001 From: Blu <79374236+BlueHNT@users.noreply.github.com> Date: Fri, 10 Jan 2025 03:27:28 +0100 Subject: [PATCH 24/38] Adds Missing Lathe Recipes (#1472) <!-- This is a semi-strict format, you can add/remove sections as needed but the order/format should be kept the same Remove these comments before submitting --> # Description <!-- Explain this PR in as much detail as applicable Some example prompts to consider: How might this affect the game? The codebase? What might be some alternatives to this? How/Who does this benefit/hurt [the game/codebase]? --> N1984 exists, but you can't print more ammo reliably. You can refill them, sure, but the moment you lose a magazine, it's gone forever. This adds the magazines and adds the unique mags to the research. Edit: Turns out there's like a lot missing in the lathe, so I decided to just add the missing ones I found. --- <!-- This is default collapsed, readers click to expand it and see all your media The PR media section can get very large at times, so this is a good way to keep it clean The title is written using HTML tags The title must be within the <summary> tags or you won't see it --> <details><summary><h1>Media</h1></summary> <p> Nothing here </p> </details> --- # Changelog <!-- You can add an author after the `:cl:` to change the name that appears in the changelog (ex: `:cl: Death`) Leaving it blank will default to your GitHub display name This includes all available types for the changelog --> :cl: - add: Added missing lathe recipes --- .../Guns/Ammunition/Magazines/magnum.yml | 14 ++ .../Guns/Ammunition/Magazines/pistol.yml | 14 ++ .../Entities/Structures/Machines/lathe.yml | 12 ++ .../Prototypes/Recipes/Lathes/security.yml | 154 +++++++++++++++--- Resources/Prototypes/Research/arsenal.yml | 10 ++ 5 files changed, 180 insertions(+), 24 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/magnum.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/magnum.yml index fc506ec594..c71a58a82b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/magnum.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/magnum.yml @@ -102,6 +102,20 @@ - state: mag-1 map: ["enum.GunVisualLayers.Mag"] +- type: entity + id: MagazineMagnumIncendiary + name: pistol magazine (.45 magnum incendiary) + parent: BaseMagazineMagnum + components: + - type: BallisticAmmoProvider + proto: CartridgeMagnumIncendiary + - type: Sprite + layers: + - state: red + map: ["enum.GunVisualLayers.Base"] + - state: mag-1 + map: ["enum.GunVisualLayers.Mag"] + - type: entity id: MagazineMagnumUranium name: pistol magazine (.45 magnum uranium) diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml index c57a8adaa5..a74d259ce8 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml @@ -330,6 +330,20 @@ - state: mag-1 map: ["enum.GunVisualLayers.Mag"] +- type: entity + id: MagazinePistolSubMachineGunIncendiary + name: SMG magazine (.35 auto incendiary) + parent: BaseMagazinePistolSubMachineGun + components: + - type: BallisticAmmoProvider + proto: CartridgePistolIncendiary + - type: Sprite + layers: + - state: red + map: ["enum.GunVisualLayers.Base"] + - state: mag-1 + map: ["enum.GunVisualLayers.Mag"] + - type: entity id: MagazinePistolSubMachineGunUranium name: SMG magazine (.35 auto uranium) diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index adf6bd94aa..850b2c0d74 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -805,6 +805,8 @@ - MagazinePistolSubMachineGunEmpty - MagazinePistolSubMachineGunTopMounted - MagazinePistolSubMachineGunTopMountedEmpty + - MagazineMagnum + - MagazineMagnumEmpty - MagazineRifle - MagazineRifleEmpty - MagazineShotgun @@ -868,10 +870,19 @@ - MagazineBoxPistolRubber - MagazineBoxRifleRubber - MagazineGrenadeEmpty + - MagazineLightRifleRubber - MagazineLightRifleIncendiary - MagazineLightRifleUranium + - MagazinePistolRubber - MagazinePistolIncendiary - MagazinePistolUranium + - MagazinePistolSubMachineGunRubber + - MagazinePistolSubMachineGunIncendiary + - MagazinePistolSubMachineGunUranium + - MagazineMagnumRubber + - MagazineMagnumIncendiary + - MagazineMagnumUranium + - MagazineRifleRubber - MagazineRifleIncendiary - MagazineRifleUranium - MagazineShotgunBeanbag @@ -887,6 +898,7 @@ - ShuttleGunSvalinnMachineGunCircuitboard - Signaller - SignalTrigger + - SpeedLoaderMagnumRubber - SpeedLoaderMagnumIncendiary - SpeedLoaderMagnumUranium - TelescopicShield diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index 5b126f7697..6d00f40ce2 100644 --- a/Resources/Prototypes/Recipes/Lathes/security.yml +++ b/Resources/Prototypes/Recipes/Lathes/security.yml @@ -242,6 +242,15 @@ materials: Steel: 145 +- type: latheRecipe + id: MagazinePistolRubber + result: MagazinePistolRubber + category: Ammo + completetime: 5 + materials: + Steel: 45 + Plastic: 100 + - type: latheRecipe id: MagazinePistolPractice result: MagazinePistolPractice @@ -285,6 +294,34 @@ materials: Steel: 300 +- type: latheRecipe + id: MagazinePistolSubMachineGunRubber + result: MagazinePistolSubMachineGunRubber + category: Ammo + completetime: 5 + materials: + Steel: 100 + Plastic: 200 + +- type: latheRecipe + id: MagazinePistolSubMachineGunUranium + result: MagazinePistolSubMachineGunUranium + category: Ammo + completetime: 5 + materials: + Steel: 25 + Plastic: 225 + Uranium: 225 + +- type: latheRecipe + id: MagazinePistolSubMachineGunIncendiary + result: MagazinePistolSubMachineGunIncendiary + category: Ammo + completetime: 5 + materials: + Steel: 25 + Plastic: 400 + - type: latheRecipe id: MagazinePistolSubMachineGunTopMountedEmpty result: MagazinePistolSubMachineGunTopMountedEmpty @@ -351,6 +388,14 @@ materials: Steel: 475 +- type: latheRecipe + id: MagazineRifleRubber + result: MagazineRifleRubber + category: Ammo + completetime: 5 + materials: + Steel: 150 + Plastic: 325 - type: latheRecipe id: MagazineRiflePractice @@ -395,6 +440,15 @@ materials: Steel: 565 +- type: latheRecipe + id: MagazineLightRifleRubber + result: MagazineLightRifleRubber + category: Ammo + completetime: 5 + materials: + Steel: 125 + Plastic: 350 + - type: latheRecipe id: MagazineLightRiflePractice result: MagazineLightRiflePractice @@ -403,7 +457,6 @@ materials: Steel: 205 - - type: latheRecipe id: MagazineLightRifleUranium result: MagazineLightRifleUranium @@ -423,6 +476,50 @@ Steel: 25 Plastic: 540 +- type: latheRecipe + id: MagazineMagnumEmpty + result: MagazineMagnumEmpty + category: Ammo + completetime: 5 + materials: + Steel: 30 + +- type: latheRecipe + id: MagazineMagnum + result: MagazineMagnum + category: Ammo + completetime: 5 + materials: + Steel: 150 + +- type: latheRecipe + id: MagazineMagnumRubber + result: MagazineMagnumRubber + category: Ammo + completetime: 5 + materials: + Steel: 50 + Plastic: 100 + +- type: latheRecipe + id: MagazineMagnumUranium + result: MagazineMagnumUranium + category: Ammo + completetime: 5 + materials: + Steel: 30 + Plastic: 75 + Uranium: 150 + +- type: latheRecipe + id: MagazineMagnumIncendiary + result: MagazineMagnumIncendiary + category: Ammo + completetime: 5 + materials: + Steel: 30 + Plastic: 150 + - type: latheRecipe id: MagazineBoxRifle result: MagazineBoxRifle @@ -475,21 +572,21 @@ Plastic: 160 - type: latheRecipe - id: SpeedLoaderMagnumEmpty - result: SpeedLoaderMagnumEmpty + id: MagazineBoxLightRifleRubber + result: MagazineBoxLightRifleRubber category: Ammo completetime: 5 materials: - Steel: 50 + Steel: 350 + Plastic: 600 - type: latheRecipe - id: MagazineBoxLightRifleRubber - result: MagazineBoxLightRifleRubber + id: SpeedLoaderMagnumEmpty + result: SpeedLoaderMagnumEmpty category: Ammo completetime: 5 materials: - Steel: 350 - Plastic: 600 + Steel: 50 - type: latheRecipe id: SpeedLoaderMagnum @@ -499,6 +596,15 @@ materials: Steel: 190 +- type: latheRecipe + id: SpeedLoaderMagnumRubber + result: SpeedLoaderMagnumRubber + category: Ammo + completetime: 5 + materials: + Steel: 50 + Plastic: 70 + - type: latheRecipe id: SpeedLoaderMagnumPractice result: SpeedLoaderMagnumPractice @@ -758,46 +864,46 @@ result: MagazineGrenadeEmpty completetime: 3 materials: - Steel: 150 - Plastic: 50 + Steel: 150 + Plastic: 50 - type: latheRecipe id: GrenadeEMP result: GrenadeEMP completetime: 3 materials: - Steel: 150 - Plastic: 100 - Glass: 20 + Steel: 150 + Plastic: 100 + Glass: 20 - type: latheRecipe id: GrenadeBlast result: GrenadeBlast completetime: 3 materials: - Steel: 150 - Plastic: 100 - Gold: 50 + Steel: 150 + Plastic: 100 + Gold: 50 - type: latheRecipe id: GrenadeFlash result: GrenadeFlash completetime: 3 materials: - Steel: 150 - Plastic: 100 - Glass: 20 + Steel: 150 + Plastic: 100 + Glass: 20 - type: latheRecipe id: PortableRecharger result: PortableRecharger completetime: 15 materials: - Steel: 2000 - Uranium: 2000 - Plastic: 1000 - Plasma: 500 - Glass: 500 + Steel: 2000 + Uranium: 2000 + Plastic: 1000 + Plasma: 500 + Glass: 500 - type: latheRecipe id: ShellShotgun diff --git a/Resources/Prototypes/Research/arsenal.yml b/Resources/Prototypes/Research/arsenal.yml index ee2be33c83..6a1d1e3773 100644 --- a/Resources/Prototypes/Research/arsenal.yml +++ b/Resources/Prototypes/Research/arsenal.yml @@ -27,6 +27,8 @@ - BoxShotgunIncendiary - MagazineRifleIncendiary - MagazinePistolIncendiary + - MagazinePistolSubMachineGunIncendiary + - MagazineMagnumIncendiary - MagazineLightRifleIncendiary - SpeedLoaderMagnumIncendiary - MagazineShotgunIncendiary @@ -68,6 +70,12 @@ - CartridgeMagnumRubber - CartridgeLightRifleRubber - CartridgeRifleRubber + - MagazineRifleRubber + - MagazinePistolRubber + - MagazinePistolSubMachineGunRubber + - MagazineMagnumRubber + - MagazineLightRifleRubber + - SpeedLoaderMagnumRubber - MagazineBoxPistolRubber - MagazineBoxMagnumRubber - MagazineBoxLightRifleRubber @@ -88,6 +96,8 @@ recipeUnlocks: - MagazineRifleUranium - MagazinePistolUranium + - MagazinePistolSubMachineGunUranium + - MagazineMagnumUranium - MagazineLightRifleUranium - SpeedLoaderMagnumUranium - MagazineBoxPistolUranium From b89412ad039706bdfc528843a406813277778564 Mon Sep 17 00:00:00 2001 From: Spatison <137375981+Spatison@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:27:50 +1000 Subject: [PATCH 25/38] Some Surgery Fixs (#1471) <!-- This is a semi-strict format, you can add/remove sections as needed but the order/format should be kept the same Remove these comments before submitting --> # Description <!-- Explain this PR in as much detail as applicable Some example prompts to consider: How might this affect the game? The codebase? What might be some alternatives to this? How/Who does this benefit/hurt [the game/codebase]? --> When attempting to insert an organ, a check is now performed to ensure a container exists for it. Skeletons can now lie down, and nymphs can now be subject to surgery. --- # Changelog <!-- You can add an author after the `:cl:` to change the name that appears in the changelog (ex: `:cl: Death`) Leaving it blank will default to your GitHub display name This includes all available types for the changelog --> :cl: Spatison - tweak: Nymphs are now operable. - fix: Skeletons can now lie down. - fix: It is no longer possible to insert an organ into a body if the required space is unavailable. --- .../SurgeryOrganConditionComponent.cs | 5 ++- .../_Shitmed/Surgery/SharedSurgerySystem.cs | 4 ++- Resources/Prototypes/Body/Organs/diona.yml | 15 +++++++++ .../Entities/Mobs/Species/skeleton.yml | 1 + .../_Shitmed/Entities/Surgery/surgeries.yml | 32 +++++++++++++++++++ 5 files changed, 55 insertions(+), 2 deletions(-) diff --git a/Content.Shared/_Shitmed/Surgery/Conditions/SurgeryOrganConditionComponent.cs b/Content.Shared/_Shitmed/Surgery/Conditions/SurgeryOrganConditionComponent.cs index 53db0430e5..7ddd99f9af 100644 --- a/Content.Shared/_Shitmed/Surgery/Conditions/SurgeryOrganConditionComponent.cs +++ b/Content.Shared/_Shitmed/Surgery/Conditions/SurgeryOrganConditionComponent.cs @@ -15,4 +15,7 @@ public sealed partial class SurgeryOrganConditionComponent : Component [DataField] public bool Reattaching; -} \ No newline at end of file + + [DataField(required: true)] + public string SlotId = string.Empty; +} diff --git a/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.cs b/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.cs index fe12eed8fe..aa47087532 100644 --- a/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.cs +++ b/Content.Shared/_Shitmed/Surgery/SharedSurgerySystem.cs @@ -22,6 +22,7 @@ using Content.Shared.Prototypes; using Content.Shared.Standing; using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Prototypes; @@ -47,6 +48,7 @@ public abstract partial class SharedSurgerySystem : EntitySystem [Dependency] private readonly RotateToFaceSystem _rotateToFace = default!; [Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; /// <summary> /// Cache of all surgery prototypes' singleton entities. @@ -265,7 +267,7 @@ private void OnOrganConditionValid(Entity<SurgeryOrganConditionComponent> ent, r && !organs.Any(organ => HasComp<OrganReattachedComponent>(organ.Id)))) args.Cancelled = true; } - else if (!ent.Comp.Inverse) + else if (!ent.Comp.Inverse || !_container.TryGetContainer(args.Part, SharedBodySystem.GetOrganContainerId(ent.Comp.SlotId), out _)) args.Cancelled = true; } } diff --git a/Resources/Prototypes/Body/Organs/diona.yml b/Resources/Prototypes/Body/Organs/diona.yml index 72564b1abb..fb6c819493 100644 --- a/Resources/Prototypes/Body/Organs/diona.yml +++ b/Resources/Prototypes/Body/Organs/diona.yml @@ -184,6 +184,11 @@ - type: IsDeadIC - type: Body prototype: AnimalNymphBrain + - type: SurgeryTarget + - type: UserInterface + interfaces: + enum.SurgeryUIKey.Key: + type: SurgeryBui - type: entity id: OrganDionaNymphStomach @@ -196,6 +201,11 @@ - type: IsDeadIC - type: Body prototype: AnimalNymphStomach + - type: SurgeryTarget + - type: UserInterface + interfaces: + enum.SurgeryUIKey.Key: + type: SurgeryBui - type: entity id: OrganDionaNymphLungs @@ -208,3 +218,8 @@ - type: IsDeadIC - type: Body prototype: AnimalNymphLungs + - type: SurgeryTarget + - type: UserInterface + interfaces: + enum.SurgeryUIKey.Key: + type: SurgeryBui diff --git a/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml b/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml index 96c6185693..5f9812f490 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml @@ -104,6 +104,7 @@ - type: FireVisuals alternateState: Standing - type: FootPrints + - type: LayingDown - type: entity parent: BaseSpeciesDummy diff --git a/Resources/Prototypes/_Shitmed/Entities/Surgery/surgeries.yml b/Resources/Prototypes/_Shitmed/Entities/Surgery/surgeries.yml index 775716ecff..44d1f3c1c8 100644 --- a/Resources/Prototypes/_Shitmed/Entities/Surgery/surgeries.yml +++ b/Resources/Prototypes/_Shitmed/Entities/Surgery/surgeries.yml @@ -377,6 +377,16 @@ - type: SurgeryOrganCondition organ: - type: Brain + slotId: brain + +- type: entity + parent: SurgeryRemoveBrain + id: SurgeryRemoveBrainTorso + name: Remove Brain + categories: [ HideSpawnMenu ] + components: + - type: SurgeryPartCondition + part: Torso - type: entity parent: SurgeryBase @@ -397,6 +407,16 @@ - type: Brain inverse: true reattaching: true + slotId: brain + +- type: entity + parent: SurgeryInsertBrain + id: SurgeryInsertBrainTorso + name: Insert Brain + categories: [ HideSpawnMenu ] + components: + - type: SurgeryPartCondition + part: Torso - type: entity parent: SurgeryBase @@ -415,6 +435,7 @@ - type: SurgeryOrganCondition organ: - type: Heart + slotId: heart - type: entity parent: SurgeryBase @@ -435,6 +456,7 @@ - type: Heart inverse: true reattaching: true + slotId: heart - type: entity parent: SurgeryBase @@ -453,6 +475,7 @@ - type: SurgeryOrganCondition organ: - type: Liver + slotId: liver - type: entity parent: SurgeryBase @@ -473,6 +496,7 @@ - type: Liver inverse: true reattaching: true + slotId: liver - type: entity parent: SurgeryBase @@ -491,6 +515,7 @@ - type: SurgeryOrganCondition organ: - type: Lung + slotId: lungs - type: entity parent: SurgeryBase @@ -511,6 +536,7 @@ - type: Lung inverse: true reattaching: true + slotId: lungs - type: entity parent: SurgeryBase @@ -529,6 +555,7 @@ - type: SurgeryOrganCondition organ: - type: Stomach + slotId: stomach - type: entity parent: SurgeryBase @@ -549,6 +576,7 @@ - type: Stomach inverse: true reattaching: true + slotId: stomach - type: entity parent: SurgeryBase @@ -567,6 +595,7 @@ - type: SurgeryOrganCondition organ: - type: Eyes + slotId: eyes - type: entity parent: SurgeryBase @@ -587,6 +616,7 @@ - type: Eyes inverse: true reattaching: true + slotId: eyes - type: entity parent: SurgeryBase @@ -602,6 +632,7 @@ - type: SurgeryOrganCondition organ: - type: Brain + slotId: brain - type: SurgeryOrganOnAddCondition components: brain: @@ -629,6 +660,7 @@ - type: SurgeryOrganCondition organ: - type: Brain + slotId: brain - type: SurgeryOrganOnAddCondition components: brain: From 77a72958d47ad3cc0752da2762576f4f107d75f7 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com> Date: Fri, 10 Jan 2025 02:27:54 +0000 Subject: [PATCH 26/38] Automatic Changelog Update (#1472) --- Resources/Changelog/Changelog.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3015e03292..b1aee0b265 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9480,3 +9480,10 @@ Entries: id: 6654 time: '2025-01-09T21:32:06.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1470 +- author: BlueHNT + changes: + - type: Add + message: Added missing lathe recipes + id: 6655 + time: '2025-01-10T02:27:28.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1472 From ba616f0ce8375e47afee0342b8a580b8c5f88c5a Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com> Date: Fri, 10 Jan 2025 02:28:23 +0000 Subject: [PATCH 27/38] Automatic Changelog Update (#1471) --- Resources/Changelog/Changelog.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index b1aee0b265..40eee58c48 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9487,3 +9487,16 @@ Entries: id: 6655 time: '2025-01-10T02:27:28.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1472 +- author: Spatison + changes: + - type: Tweak + message: Nymphs are now operable. + - type: Fix + message: Skeletons can now lie down. + - type: Fix + message: >- + It is no longer possible to insert an organ into a body if the required + space is unavailable. + id: 6656 + time: '2025-01-10T02:27:50.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1471 From 0f481422a54a197923f4bf03db1b5733e481965f Mon Sep 17 00:00:00 2001 From: Spatison <137375981+Spatison@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:41:32 +1000 Subject: [PATCH 28/38] Night And Thermal Vision (#1462) <!-- This is a semi-strict format, you can add/remove sections as needed but the order/format should be kept the same Remove these comments before submitting --> # Description <!-- Explain this PR in as much detail as applicable Some example prompts to consider: How might this affect the game? The codebase? What might be some alternatives to this? How/Who does this benefit/hurt [the game/codebase]? --> Port from WWDP. Refactor from [Goob](https://github.com/Goob-Station/Goob-Station/pull/1251) --- <!-- This is default collapsed, readers click to expand it and see all your media The PR media section can get very large at times, so this is a good way to keep it clean The title is written using HTML tags The title must be within the <summary> tags or you won't see it --> <details><summary><h1>Media</h1></summary> <p>  Night vision goggles:  Zealot's blindfold:  Animal vision:  Thermal vision goggles:  Deathsquad helmet:  Xeno vision:  </p> </details> --- # Changelog <!-- You can add an author after the `:cl:` to change the name that appears in the changelog (ex: `:cl: Death`) Leaving it blank will default to your GitHub display name This includes all available types for the changelog --> :cl: @Aviu00, Spatison, @PuroSlavKing - add: Added night vision goggle - add: Added thermal vision goggle - add: Deathsquad helmet now grants night and thermal vision. - add: Ninja visor now grants night vision. - tweak: Some animals have gained night vision. - tweak: Xenos have gained night vision. --------- Signed-off-by: Spatison <137375981+Spatison@users.noreply.github.com> Co-authored-by: PuroSlavKing <103608145+puroslavking@users.noreply.github.com> --- Content.Client/Overlays/EquipmentHudSystem.cs | 2 +- .../Switchable/BaseSwitchableOverlay.cs | 48 + .../Overlays/Switchable/NightVisionSystem.cs | 87 + .../Switchable/ThermalVisionOverlay.cs | 159 ++ .../Switchable/ThermalVisionSystem.cs | 95 + Content.Shared/Actions/SharedActionsSystem.cs | 5 +- .../Body/Components/BodyComponent.cs | 3 + .../Inventory/InventorySystem.Relay.cs | 4 + .../Overlays/BaseOverlayComponent.cs | 16 + .../Switchable/NightVisionComponent.cs | 14 + .../Overlays/Switchable/NightVisionSystem.cs | 3 + .../Switchable/SwitchableOverlayComponent.cs | 48 + .../Switchable/SwitchableOverlaySystem.cs | 166 ++ .../Switchable/ThermalVisionComponent.cs | 17 + .../Switchable/ThermalVisionSystem.cs | 3 + Resources/Audio/Items/Goggles/activate.ogg | Bin 0 -> 12048 bytes .../Audio/Items/Goggles/attributions.yml | 9 + Resources/Audio/Items/Goggles/deactivate.ogg | Bin 0 -> 12424 bytes .../Locale/en-US/research/technologies.ftl | 2 + .../Locale/en-US/store/uplink-catalog.ftl | 6 + .../Locale/ru-RU/prototypes/actions/types.ftl | 8 + .../entities/clothing/eyes/goggles.ftl | 23 + .../objects/tools/empflashlight.ftl | 0 .../Locale/ru-RU/research/techologies.ftl | 2 + .../Locale/ru-RU/store/uplink-catalog.ftl | 7 + Resources/Prototypes/Actions/types.yml | 41 +- .../Prototypes/Catalog/Fills/Lockers/misc.yml | 8 +- .../Prototypes/Catalog/uplink_catalog.yml | 72 + .../Entities/Clothing/Eyes/glasses.yml | 1 + .../Entities/Clothing/Eyes/goggles.yml | 114 ++ .../Clothing/Head/hardsuit-helmets.yml | 3 + .../Mobs/Cyborgs/base_borg_chassis.yml | 1 + .../Prototypes/Entities/Mobs/NPCs/animals.yml | 12 + .../Prototypes/Entities/Mobs/NPCs/carp.yml | 6 + .../Entities/Mobs/NPCs/regalrat.yml | 12 + .../Prototypes/Entities/Mobs/NPCs/xeno.yml | 6 + .../Entities/Mobs/Player/admin_ghost.yml | 1 + .../Entities/Mobs/Player/dragon.yml | 5 + .../Entities/Mobs/Player/silicon_base.yml | 1 + .../Entities/Structures/Machines/lathe.yml | 1621 +++++++++-------- .../Prototypes/Procedural/salvage_loot.yml | 4 + .../Prototypes/Recipes/Lathes/devices.yml | 30 + .../Prototypes/Recipes/Lathes/medical.yml | 13 +- .../Prototypes/Recipes/Lathes/security.yml | 12 +- .../Prototypes/Research/experimental.yml | 27 + Resources/Prototypes/Shaders/shaders.yml | 7 +- .../Entities/Clothing/Cult/armor.yml | 8 +- .../equipped-EYES-off.png | Bin 0 -> 452 bytes .../equipped-EYES.png | Bin 0 -> 739 bytes .../diagnostic_nightvision.rsi/icon.png | Bin 0 -> 306 bytes .../inhand-left.png | Bin 0 -> 233 bytes .../inhand-right.png | Bin 0 -> 230 bytes .../diagnostic_nightvision.rsi/meta.json | 48 + .../equipped-EYES-off.png | Bin 0 -> 452 bytes .../medical_nightvision.rsi/equipped-EYES.png | Bin 0 -> 733 bytes .../Goggles/medical_nightvision.rsi/icon.png | Bin 0 -> 306 bytes .../medical_nightvision.rsi/inhand-left.png | Bin 0 -> 233 bytes .../medical_nightvision.rsi/inhand-right.png | Bin 0 -> 230 bytes .../Goggles/medical_nightvision.rsi/meta.json | 48 + .../monocle_thermal.rsi/equipped-EYES-off.png | Bin 0 -> 477 bytes .../monocle_thermal.rsi/equipped-EYES.png | Bin 0 -> 410 bytes .../Eyes/Goggles/monocle_thermal.rsi/icon.png | Bin 0 -> 298 bytes .../Goggles/monocle_thermal.rsi/meta.json | 22 + .../nightvision.rsi/equipped-EYES-off.png | Bin 0 -> 452 bytes .../Goggles/nightvision.rsi/equipped-EYES.png | Bin 0 -> 719 bytes .../Eyes/Goggles/nightvision.rsi/icon.png | Bin 0 -> 218 bytes .../Goggles/nightvision.rsi/inhand-left.png | Bin 0 -> 385 bytes .../Goggles/nightvision.rsi/inhand-right.png | Bin 0 -> 410 bytes .../Eyes/Goggles/nightvision.rsi/meta.json | 48 + .../equipped-EYES-off.png | Bin 0 -> 452 bytes .../equipped-EYES.png | Bin 0 -> 704 bytes .../Goggles/security_nightvision.rsi/icon.png | Bin 0 -> 505 bytes .../security_nightvision.rsi/inhand-left.png | Bin 0 -> 233 bytes .../security_nightvision.rsi/inhand-right.png | Bin 0 -> 309 bytes .../security_nightvision.rsi/meta.json | 48 + .../Goggles/thermal.rsi/equipped-EYES.png | Bin 0 -> 524 bytes .../Eyes/Goggles/thermal.rsi/icon.png | Bin 0 -> 510 bytes .../Eyes/Goggles/thermal.rsi/inhand-left.png | Bin 0 -> 327 bytes .../Eyes/Goggles/thermal.rsi/inhand-right.png | Bin 0 -> 325 bytes .../Eyes/Goggles/thermal.rsi/meta.json | 26 + Resources/Textures/Shaders/nightvision.swsl | 38 + 81 files changed, 2192 insertions(+), 818 deletions(-) create mode 100644 Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs create mode 100644 Content.Client/Overlays/Switchable/NightVisionSystem.cs create mode 100644 Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs create mode 100644 Content.Client/Overlays/Switchable/ThermalVisionSystem.cs create mode 100644 Content.Shared/Overlays/BaseOverlayComponent.cs create mode 100644 Content.Shared/Overlays/Switchable/NightVisionComponent.cs create mode 100644 Content.Shared/Overlays/Switchable/NightVisionSystem.cs create mode 100644 Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs create mode 100644 Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs create mode 100644 Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs create mode 100644 Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs create mode 100644 Resources/Audio/Items/Goggles/activate.ogg create mode 100644 Resources/Audio/Items/Goggles/attributions.yml create mode 100644 Resources/Audio/Items/Goggles/deactivate.ogg create mode 100644 Resources/Locale/ru-RU/prototypes/actions/types.ftl create mode 100644 Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl rename Resources/Locale/ru-RU/{entities => prototypes}/objects/tools/empflashlight.ftl (100%) create mode 100644 Resources/Locale/ru-RU/research/techologies.ftl create mode 100644 Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/icon.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-left.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png create mode 100644 Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json create mode 100644 Resources/Textures/Shaders/nightvision.swsl diff --git a/Content.Client/Overlays/EquipmentHudSystem.cs b/Content.Client/Overlays/EquipmentHudSystem.cs index c7578b6793..3672892ae3 100644 --- a/Content.Client/Overlays/EquipmentHudSystem.cs +++ b/Content.Client/Overlays/EquipmentHudSystem.cs @@ -102,7 +102,7 @@ protected virtual void OnRefreshComponentHud(EntityUid uid, T component, Refresh args.Components.Add(component); } - private void RefreshOverlay(EntityUid uid) + protected void RefreshOverlay(EntityUid uid) { if (uid != _player.LocalSession?.AttachedEntity) return; diff --git a/Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs b/Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs new file mode 100644 index 0000000000..5977355968 --- /dev/null +++ b/Content.Client/Overlays/Switchable/BaseSwitchableOverlay.cs @@ -0,0 +1,48 @@ +using System.Numerics; +using Content.Shared.Overlays.Switchable; +using Robust.Client.Graphics; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; + +namespace Content.Client.Overlays.Switchable; + +public sealed class BaseSwitchableOverlay<TComp> : Overlay where TComp : SwitchableOverlayComponent +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + + public override bool RequestScreenTexture => true; + public override OverlaySpace Space => OverlaySpace.WorldSpace; + + private readonly ShaderInstance _shader; + + public TComp? Comp = null; + + public bool IsActive = true; + + public BaseSwitchableOverlay() + { + IoCManager.InjectDependencies(this); + _shader = _prototype.Index<ShaderPrototype>("NightVision").InstanceUnique(); + } + + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenTexture is null || Comp is null || !IsActive) + return; + + _shader.SetParameter("SCREEN_TEXTURE", ScreenTexture); + _shader.SetParameter("tint", Comp.Tint); + _shader.SetParameter("luminance_threshold", Comp.Strength); + _shader.SetParameter("noise_amount", Comp.Noise); + + var worldHandle = args.WorldHandle; + + var accumulator = Math.Clamp(Comp.PulseAccumulator, 0f, Comp.PulseTime); + var alpha = Comp.PulseTime <= 0f ? 1f : float.Lerp(1f, 0f, accumulator / Comp.PulseTime); + + worldHandle.SetTransform(Matrix3x2.Identity); + worldHandle.UseShader(_shader); + worldHandle.DrawRect(args.WorldBounds, Comp.Color.WithAlpha(alpha)); + worldHandle.UseShader(null); + } +} diff --git a/Content.Client/Overlays/Switchable/NightVisionSystem.cs b/Content.Client/Overlays/Switchable/NightVisionSystem.cs new file mode 100644 index 0000000000..c85b3758d8 --- /dev/null +++ b/Content.Client/Overlays/Switchable/NightVisionSystem.cs @@ -0,0 +1,87 @@ +using Content.Shared.Inventory.Events; +using Content.Shared.Overlays.Switchable; +using Robust.Client.Graphics; + +namespace Content.Client.Overlays.Switchable; + +public sealed class NightVisionSystem : EquipmentHudSystem<NightVisionComponent> +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly ILightManager _lightManager = default!; + + private BaseSwitchableOverlay<NightVisionComponent> _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent<NightVisionComponent, SwitchableOverlayToggledEvent>(OnToggle); + + _overlay = new BaseSwitchableOverlay<NightVisionComponent>(); + } + + private void OnToggle(Entity<NightVisionComponent> ent, ref SwitchableOverlayToggledEvent args) + { + RefreshOverlay(args.User); + } + + protected override void UpdateInternal(RefreshEquipmentHudEvent<NightVisionComponent> args) + { + base.UpdateInternal(args); + + var active = false; + NightVisionComponent? nvComp = null; + foreach (var comp in args.Components) + { + if (comp.IsActive || comp.PulseTime > 0f && comp.PulseAccumulator < comp.PulseTime) + active = true; + else + continue; + + if (comp.DrawOverlay) + { + if (nvComp == null) + nvComp = comp; + else if (nvComp.PulseTime > 0f && comp.PulseTime <= 0f) + nvComp = comp; + } + + if (active && nvComp is { PulseTime: <= 0 }) + break; + } + + UpdateNightVision(active); + UpdateOverlay(nvComp); + } + + protected override void DeactivateInternal() + { + base.DeactivateInternal(); + + UpdateNightVision(false); + UpdateOverlay(null); + } + + private void UpdateNightVision(bool active) + { + _lightManager.DrawLighting = !active; + } + + private void UpdateOverlay(NightVisionComponent? nvComp) + { + _overlay.Comp = nvComp; + + switch (nvComp) + { + case not null when !_overlayMan.HasOverlay<BaseSwitchableOverlay<NightVisionComponent>>(): + _overlayMan.AddOverlay(_overlay); + break; + case null: + _overlayMan.RemoveOverlay(_overlay); + break; + } + + if (_overlayMan.TryGetOverlay<BaseSwitchableOverlay<ThermalVisionComponent>>(out var overlay)) + overlay.IsActive = nvComp == null; + } +} diff --git a/Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs b/Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs new file mode 100644 index 0000000000..eb12b33e3a --- /dev/null +++ b/Content.Client/Overlays/Switchable/ThermalVisionOverlay.cs @@ -0,0 +1,159 @@ +using System.Linq; +using System.Numerics; +using Content.Client.Stealth; +using Content.Shared.Body.Components; +using Content.Shared.Overlays.Switchable; +using Content.Shared.Stealth.Components; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.Player; +using Robust.Shared.Enums; +using Robust.Shared.Map; +using Robust.Shared.Timing; + +namespace Content.Client.Overlays.Switchable; + +public sealed class ThermalVisionOverlay : Overlay +{ + [Dependency] private readonly IEntityManager _entity = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + private readonly TransformSystem _transform; + private readonly StealthSystem _stealth; + private readonly ContainerSystem _container; + private readonly SharedPointLightSystem _light; + + public override bool RequestScreenTexture => true; + public override OverlaySpace Space => OverlaySpace.WorldSpace; + + private readonly List<ThermalVisionRenderEntry> _entries = []; + + private EntityUid? _lightEntity; + + public float LightRadius; + + public ThermalVisionComponent? Comp; + + public ThermalVisionOverlay() + { + IoCManager.InjectDependencies(this); + + _container = _entity.System<ContainerSystem>(); + _transform = _entity.System<TransformSystem>(); + _stealth = _entity.System<StealthSystem>(); + _light = _entity.System<SharedPointLightSystem>(); + + ZIndex = -1; + } + + protected override void Draw(in OverlayDrawArgs args) + { + if (ScreenTexture is null || Comp is null) + return; + + var worldHandle = args.WorldHandle; + var eye = args.Viewport.Eye; + + if (eye == null) + return; + + var player = _player.LocalEntity; + + if (!_entity.TryGetComponent(player, out TransformComponent? playerXform)) + return; + + var accumulator = Math.Clamp(Comp.PulseAccumulator, 0f, Comp.PulseTime); + var alpha = Comp.PulseTime <= 0f ? 1f : float.Lerp(1f, 0f, accumulator / Comp.PulseTime); + + // Thermal vision grants some night vision (clientside light) + if (LightRadius > 0) + { + _lightEntity ??= _entity.SpawnAttachedTo(null, playerXform.Coordinates); + _transform.SetParent(_lightEntity.Value, player.Value); + var light = _entity.EnsureComponent<PointLightComponent>(_lightEntity.Value); + _light.SetRadius(_lightEntity.Value, LightRadius, light); + _light.SetEnergy(_lightEntity.Value, alpha, light); + _light.SetColor(_lightEntity.Value, Comp.Color, light); + } + else + ResetLight(); + + var mapId = eye.Position.MapId; + var eyeRot = eye.Rotation; + + _entries.Clear(); + var entities = _entity.EntityQueryEnumerator<BodyComponent, SpriteComponent, TransformComponent>(); + while (entities.MoveNext(out var uid, out var body, out var sprite, out var xform)) + { + if (!CanSee(uid, sprite) || !body.ThermalVisibility) + continue; + + var entity = uid; + + if (_container.TryGetOuterContainer(uid, xform, out var container)) + { + var owner = container.Owner; + if (_entity.TryGetComponent<SpriteComponent>(owner, out var ownerSprite) + && _entity.TryGetComponent<TransformComponent>(owner, out var ownerXform)) + { + entity = owner; + sprite = ownerSprite; + xform = ownerXform; + } + } + + if (_entries.Any(e => e.Ent.Owner == entity)) + continue; + + _entries.Add(new ThermalVisionRenderEntry((entity, sprite, xform), mapId, eyeRot)); + } + + foreach (var entry in _entries) + { + Render(entry.Ent, entry.Map, worldHandle, entry.EyeRot, Comp.Color, alpha); + } + + worldHandle.SetTransform(Matrix3x2.Identity); + } + + private void Render(Entity<SpriteComponent, TransformComponent> ent, + MapId? map, + DrawingHandleWorld handle, + Angle eyeRot, + Color color, + float alpha) + { + var (uid, sprite, xform) = ent; + if (xform.MapID != map || !CanSee(uid, sprite)) + return; + + var position = _transform.GetWorldPosition(xform); + var rotation = _transform.GetWorldRotation(xform); + + var originalColor = sprite.Color; + sprite.Color = color.WithAlpha(alpha); + sprite.Render(handle, eyeRot, rotation, position: position); + sprite.Color = originalColor; + } + + private bool CanSee(EntityUid uid, SpriteComponent sprite) + { + return sprite.Visible && (!_entity.TryGetComponent(uid, out StealthComponent? stealth) || + _stealth.GetVisibility(uid, stealth) > 0.5f); + } + + public void ResetLight() + { + if (_lightEntity == null || !_timing.IsFirstTimePredicted) + return; + + _entity.DeleteEntity(_lightEntity); + _lightEntity = null; + } +} + +public record struct ThermalVisionRenderEntry( + Entity<SpriteComponent, TransformComponent> Ent, + MapId? Map, + Angle EyeRot); diff --git a/Content.Client/Overlays/Switchable/ThermalVisionSystem.cs b/Content.Client/Overlays/Switchable/ThermalVisionSystem.cs new file mode 100644 index 0000000000..9b6e5eed0f --- /dev/null +++ b/Content.Client/Overlays/Switchable/ThermalVisionSystem.cs @@ -0,0 +1,95 @@ +using Content.Shared.Inventory.Events; +using Content.Shared.Overlays.Switchable; +using Robust.Client.Graphics; + +namespace Content.Client.Overlays.Switchable; + +public sealed class ThermalVisionSystem : EquipmentHudSystem<ThermalVisionComponent> +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + + private ThermalVisionOverlay _thermalOverlay = default!; + private BaseSwitchableOverlay<ThermalVisionComponent> _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent<ThermalVisionComponent, SwitchableOverlayToggledEvent>(OnToggle); + + _thermalOverlay = new ThermalVisionOverlay(); + _overlay = new BaseSwitchableOverlay<ThermalVisionComponent>(); + } + + private void OnToggle(Entity<ThermalVisionComponent> ent, ref SwitchableOverlayToggledEvent args) + { + RefreshOverlay(args.User); + } + + protected override void UpdateInternal(RefreshEquipmentHudEvent<ThermalVisionComponent> args) + { + base.UpdateInternal(args); + ThermalVisionComponent? tvComp = null; + var lightRadius = 0f; + foreach (var comp in args.Components) + { + if (!comp.IsActive && (comp.PulseTime <= 0f || comp.PulseAccumulator >= comp.PulseTime)) + continue; + + if (tvComp == null) + tvComp = comp; + else if (!tvComp.DrawOverlay && comp.DrawOverlay) + tvComp = comp; + else if (tvComp.DrawOverlay == comp.DrawOverlay && tvComp.PulseTime > 0f && comp.PulseTime <= 0f) + tvComp = comp; + + lightRadius = MathF.Max(lightRadius, comp.LightRadius); + } + + UpdateThermalOverlay(tvComp, lightRadius); + UpdateOverlay(tvComp); + } + + protected override void DeactivateInternal() + { + base.DeactivateInternal(); + + UpdateOverlay(null); + UpdateThermalOverlay(null, 0f); + } + + private void UpdateThermalOverlay(ThermalVisionComponent? comp, float lightRadius) + { + _thermalOverlay.LightRadius = lightRadius; + _thermalOverlay.Comp = comp; + + switch (comp) + { + case not null when !_overlayMan.HasOverlay<ThermalVisionOverlay>(): + _overlayMan.AddOverlay(_thermalOverlay); + break; + case null: + _overlayMan.RemoveOverlay(_thermalOverlay); + _thermalOverlay.ResetLight(); + break; + } + } + + private void UpdateOverlay(ThermalVisionComponent? tvComp) + { + _overlay.Comp = tvComp; + + switch (tvComp) + { + case { DrawOverlay: true } when !_overlayMan.HasOverlay<BaseSwitchableOverlay<ThermalVisionComponent>>(): + _overlayMan.AddOverlay(_overlay); + break; + case null or { DrawOverlay: false }: + _overlayMan.RemoveOverlay(_overlay); + break; + } + + // Night vision overlay is prioritized + _overlay.IsActive = !_overlayMan.HasOverlay<BaseSwitchableOverlay<NightVisionComponent>>(); + } +} diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 91acf47478..8cede391a8 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -14,6 +14,7 @@ using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Map; +using Robust.Shared.Network; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -23,6 +24,8 @@ public abstract class SharedActionsSystem : EntitySystem { [Dependency] protected readonly IGameTiming GameTiming = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!; @@ -774,7 +777,7 @@ public bool AddActionDirect(EntityUid performer, if (!ResolveActionData(actionId, ref action)) return false; - DebugTools.Assert(action.Container == null || + DebugTools.Assert(_net.IsClient || action.Container == null || (TryComp(action.Container, out ActionsContainerComponent? containerComp) && containerComp.Container.Contains(actionId))); diff --git a/Content.Shared/Body/Components/BodyComponent.cs b/Content.Shared/Body/Components/BodyComponent.cs index 481e22150b..4ddfbdf979 100644 --- a/Content.Shared/Body/Components/BodyComponent.cs +++ b/Content.Shared/Body/Components/BodyComponent.cs @@ -41,4 +41,7 @@ public sealed partial class BodyComponent : Component [ViewVariables] [DataField, AutoNetworkedField] public HashSet<EntityUid> LegEntities = new(); + + [DataField, AutoNetworkedField] + public bool ThermalVisibility = true; } diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index 9d263a6098..4375f1ab19 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -20,6 +20,8 @@ using Content.Shared.Verbs; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Chat; +using Content.Shared.Overlays.Switchable; + namespace Content.Shared.Inventory; @@ -63,6 +65,8 @@ public void InitializeRelay() SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowMindShieldIconsComponent>>(RelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowSyndicateIconsComponent>>(RelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ShowCriminalRecordIconsComponent>>(RelayInventoryEvent); + SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<NightVisionComponent>>(RelayInventoryEvent); + SubscribeLocalEvent<InventoryComponent, RefreshEquipmentHudEvent<ThermalVisionComponent>>(RelayInventoryEvent); SubscribeLocalEvent<InventoryComponent, GetVerbsEvent<EquipmentVerb>>(OnGetEquipmentVerbs); } diff --git a/Content.Shared/Overlays/BaseOverlayComponent.cs b/Content.Shared/Overlays/BaseOverlayComponent.cs new file mode 100644 index 0000000000..fe6a5c763e --- /dev/null +++ b/Content.Shared/Overlays/BaseOverlayComponent.cs @@ -0,0 +1,16 @@ +namespace Content.Shared.Overlays; + +public abstract partial class BaseOverlayComponent : Component +{ + [DataField, ViewVariables(VVAccess.ReadOnly)] + public virtual Vector3 Tint { get; set; } = new(0.3f, 0.3f, 0.3f); + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public virtual float Strength { get; set; } = 2f; + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public virtual float Noise { get; set; } = 0.5f; + + [DataField, ViewVariables(VVAccess.ReadOnly)] + public virtual Color Color { get; set; } = Color.White; +} diff --git a/Content.Shared/Overlays/Switchable/NightVisionComponent.cs b/Content.Shared/Overlays/Switchable/NightVisionComponent.cs new file mode 100644 index 0000000000..cb8866dc85 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/NightVisionComponent.cs @@ -0,0 +1,14 @@ +using Content.Shared.Actions; +using Robust.Shared.GameStates; + +namespace Content.Shared.Overlays.Switchable; + +[RegisterComponent, NetworkedComponent] +public sealed partial class NightVisionComponent : SwitchableOverlayComponent +{ + public override string? ToggleAction { get; set; } = "ToggleNightVision"; + + public override Color Color { get; set; } = Color.FromHex("#98FB98"); +} + +public sealed partial class ToggleNightVisionEvent : InstantActionEvent; diff --git a/Content.Shared/Overlays/Switchable/NightVisionSystem.cs b/Content.Shared/Overlays/Switchable/NightVisionSystem.cs new file mode 100644 index 0000000000..f547b9dc76 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/NightVisionSystem.cs @@ -0,0 +1,3 @@ +namespace Content.Shared.Overlays.Switchable; + +public sealed class NightVisionSystem : SwitchableOverlaySystem<NightVisionComponent, ToggleNightVisionEvent>; diff --git a/Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs b/Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs new file mode 100644 index 0000000000..8565defe04 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/SwitchableOverlayComponent.cs @@ -0,0 +1,48 @@ +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Overlays.Switchable; + +public abstract partial class SwitchableOverlayComponent : BaseOverlayComponent +{ + [DataField, AutoNetworkedField] + public bool IsActive; + + [DataField] + public bool DrawOverlay = true; + + /// <summary> + /// If it is greater than 0, overlay isn't toggled but pulsed instead + /// </summary> + [DataField] + public float PulseTime; + + [ViewVariables(VVAccess.ReadOnly)] + public float PulseAccumulator; + + [DataField] + public virtual SoundSpecifier? ActivateSound { get; set; } = + new SoundPathSpecifier("/Audio/Items/Goggles/activate.ogg"); + + [DataField] + public virtual SoundSpecifier? DeactivateSound { get; set; } = + new SoundPathSpecifier("/Audio/Items/Goggles/deactivate.ogg"); + + [DataField] + public virtual string? ToggleAction { get; set; } + + [ViewVariables] + public EntityUid? ToggleActionEntity; +} + +[Serializable, NetSerializable] +public sealed class SwitchableVisionOverlayComponentState : IComponentState +{ + public Color Color; + public bool IsActive; + public SoundSpecifier? ActivateSound; + public SoundSpecifier? DeactivateSound; + public EntProtoId? ToggleAction; + public float LightRadius; +} diff --git a/Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs b/Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs new file mode 100644 index 0000000000..eb7b230cbc --- /dev/null +++ b/Content.Shared/Overlays/Switchable/SwitchableOverlaySystem.cs @@ -0,0 +1,166 @@ +using Content.Shared.Actions; +using Content.Shared.Inventory; +using Robust.Shared.Audio.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Network; +using Robust.Shared.Player; +using Robust.Shared.Timing; + +namespace Content.Shared.Overlays.Switchable; + +public abstract class SwitchableOverlaySystem<TComp, TEvent> : EntitySystem + where TComp : SwitchableOverlayComponent + where TEvent : InstantActionEvent +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; + + public override void Initialize() + { + SubscribeLocalEvent<TComp, TEvent>(OnToggle); + SubscribeLocalEvent<TComp, ComponentInit>(OnInit); + SubscribeLocalEvent<TComp, MapInitEvent>(OnMapInit); + SubscribeLocalEvent<TComp, ComponentShutdown>(OnShutdown); + SubscribeLocalEvent<TComp, GetItemActionsEvent>(OnGetItemActions); + SubscribeLocalEvent<TComp, ComponentGetState>(OnGetState); + SubscribeLocalEvent<TComp, ComponentHandleState>(OnHandleState); + } + + public override void FrameUpdate(float frameTime) + { + base.FrameUpdate(frameTime); + + if (_net.IsClient) + ActiveTick(frameTime); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + if (_net.IsServer) + ActiveTick(frameTime); + } + + private void ActiveTick(float frameTime) + { + var query = EntityQueryEnumerator<TComp>(); + + while (query.MoveNext(out var uid, out var comp)) + { + if (comp.PulseTime <= 0f || comp.PulseAccumulator >= comp.PulseTime) + continue; + + comp.PulseAccumulator += frameTime; + + if (comp.PulseAccumulator < comp.PulseTime) + continue; + + Toggle(uid, comp, false, false); + RaiseSwitchableOverlayToggledEvent(uid, uid, comp.IsActive); + RaiseSwitchableOverlayToggledEvent(uid, Transform(uid).ParentUid, comp.IsActive); + } + } + + private void OnGetState(EntityUid uid, TComp component, ref ComponentGetState args) + { + args.State = new SwitchableVisionOverlayComponentState + { + Color = component.Color, + IsActive = component.IsActive, + ActivateSound = component.ActivateSound, + DeactivateSound = component.DeactivateSound, + ToggleAction = component.ToggleAction, + LightRadius = component is ThermalVisionComponent thermal ? thermal.LightRadius : 0f, + }; + } + + private void OnHandleState(EntityUid uid, TComp component, ref ComponentHandleState args) + { + if (args.Current is not SwitchableVisionOverlayComponentState state) + return; + + component.Color = state.Color; + component.ActivateSound = state.ActivateSound; + component.DeactivateSound = state.DeactivateSound; + + if (component.ToggleAction != state.ToggleAction) + { + _actions.RemoveAction(uid, component.ToggleActionEntity); + component.ToggleAction = state.ToggleAction; + if (component.ToggleAction != null) + _actions.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction); + } + + if (component is ThermalVisionComponent thermal) + thermal.LightRadius = state.LightRadius; + + if (component.IsActive == state.IsActive) + return; + + component.IsActive = state.IsActive; + + RaiseSwitchableOverlayToggledEvent(uid, uid, component.IsActive); + RaiseSwitchableOverlayToggledEvent(uid, Transform(uid).ParentUid, component.IsActive); + } + + private void OnGetItemActions(Entity<TComp> ent, ref GetItemActionsEvent args) + { + if (ent.Comp.ToggleAction != null && args.SlotFlags is not SlotFlags.POCKET and not null) + args.AddAction(ref ent.Comp.ToggleActionEntity, ent.Comp.ToggleAction); + } + + private void OnShutdown(EntityUid uid, TComp component, ComponentShutdown args) + { + _actions.RemoveAction(uid, component.ToggleActionEntity); + } + + private void OnInit(EntityUid uid, TComp component, ComponentInit args) + { + component.PulseAccumulator = component.PulseTime; + } + + private void OnMapInit(EntityUid uid, TComp component, MapInitEvent args) + { + if (component.ToggleActionEntity == null && component.ToggleAction != null) + _actions.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction); + } + + private void OnToggle(EntityUid uid, TComp component, TEvent args) + { + Toggle(uid, component, !component.IsActive); + RaiseSwitchableOverlayToggledEvent(uid, args.Performer, component.IsActive); + args.Handled = true; + } + + private void Toggle(EntityUid uid, TComp component, bool activate, bool playSound = true) + { + if (playSound && _net.IsClient && _timing.IsFirstTimePredicted) + { + _audio.PlayEntity(activate ? component.ActivateSound : component.DeactivateSound, + Filter.Local(), + uid, + false); + } + + if (component.PulseTime > 0f) + { + component.PulseAccumulator = activate ? 0f : component.PulseTime; + return; + } + + component.IsActive = activate; + Dirty(uid, component); + } + + private void RaiseSwitchableOverlayToggledEvent(EntityUid uid, EntityUid user, bool activated) + { + var ev = new SwitchableOverlayToggledEvent(user, activated); + RaiseLocalEvent(uid, ref ev); + } +} + +[ByRefEvent] +public record struct SwitchableOverlayToggledEvent(EntityUid User, bool Activated); diff --git a/Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs b/Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs new file mode 100644 index 0000000000..6e3d39f289 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/ThermalVisionComponent.cs @@ -0,0 +1,17 @@ +using Content.Shared.Actions; +using Robust.Shared.GameStates; + +namespace Content.Shared.Overlays.Switchable; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ThermalVisionComponent : SwitchableOverlayComponent +{ + public override string? ToggleAction { get; set; } = "ToggleThermalVision"; + + public override Color Color { get; set; } = Color.FromHex("#F84742"); + + [DataField] + public float LightRadius = 5f; +} + +public sealed partial class ToggleThermalVisionEvent : InstantActionEvent; diff --git a/Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs b/Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs new file mode 100644 index 0000000000..c19d0d1690 --- /dev/null +++ b/Content.Shared/Overlays/Switchable/ThermalVisionSystem.cs @@ -0,0 +1,3 @@ +namespace Content.Shared.Overlays.Switchable; + +public sealed class SharedThermalVisionSystem : SwitchableOverlaySystem<ThermalVisionComponent, ToggleThermalVisionEvent>; diff --git a/Resources/Audio/Items/Goggles/activate.ogg b/Resources/Audio/Items/Goggles/activate.ogg new file mode 100644 index 0000000000000000000000000000000000000000..96cdb288fe07d0e1d3c0f38774cb7ffe01624724 GIT binary patch literal 12048 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	w2S^iCU}YScu>6^4kfJe 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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/Items/Goggles/attributions.yml b/Resources/Audio/Items/Goggles/attributions.yml new file mode 100644 index 0000000000..7b1121f542 --- /dev/null +++ b/Resources/Audio/Items/Goggles/attributions.yml @@ -0,0 +1,9 @@ +- files: ["activate.ogg"] + license: "CC-BY-NC-SA-3.0" + copyright: "Taken from TGstation" + source: "https://github.com/tgstation/tgstation" + +- files: ["deactivate.ogg"] + license: "CC-BY-NC-SA-3.0" + copyright: "Taken from TGstation" + source: "https://github.com/tgstation/tgstation" \ No newline at end of file diff --git a/Resources/Audio/Items/Goggles/deactivate.ogg b/Resources/Audio/Items/Goggles/deactivate.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e1e8f4fd82fcfa0b0c01c28b4ec93f53bbb2c2de GIT binary patch literal 12424 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 Z{kT65Jzel|`M;>-Z+o;iR#cM${||7Ja3cT! literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/research/technologies.ftl b/Resources/Locale/en-US/research/technologies.ftl index 26cd4f8315..88cd0be397 100644 --- a/Resources/Locale/en-US/research/technologies.ftl +++ b/Resources/Locale/en-US/research/technologies.ftl @@ -53,6 +53,8 @@ research-technology-advanced-anomaly-research = Advanced Anomaly Research research-technology-rped = Rapid Part Exchange research-technology-super-parts = Super Parts research-technology-deterrence = Deterrence Technologies +research-technology-night-vision = Night vision +research-technology-thermal-vision = Thermal vision research-technology-janitorial-equipment = Janitorial Equipment research-technology-laundry-tech = Laundry Tech diff --git a/Resources/Locale/en-US/store/uplink-catalog.ftl b/Resources/Locale/en-US/store/uplink-catalog.ftl index 7eaacc5026..214a259296 100644 --- a/Resources/Locale/en-US/store/uplink-catalog.ftl +++ b/Resources/Locale/en-US/store/uplink-catalog.ftl @@ -329,6 +329,12 @@ uplink-hardsuit-syndieelite-desc = An elite version of the blood-red hardsuit, w uplink-clothing-outer-hardsuit-juggernaut-name = Cybersun Juggernaut Suit uplink-clothing-outer-hardsuit-juggernaut-desc = Hyper resilient armor made of materials tested in the Tau chromosphere facility. The only thing that's going to be slowing you down is this suit... and tasers. +uplink-night-vision-name = Night vision goggles +uplink-night-vision-desc = They allow you to see in the dark, all while looking like normal sunglasses! + +uplink-thermal-vision-name = Thermal vision goggles +uplink-thermal-vision-desc = They allow you to see living creatures regardless of obstacles, all while looking like normal sunglasses! + # Misc uplink-cyberpen-name = Cybersun Pen uplink-cyberpen-desc = Cybersun's legal department pen, invaluable for forging documents and escaping prisons. Smells vaguely of hard-light and war profiteering. diff --git a/Resources/Locale/ru-RU/prototypes/actions/types.ftl b/Resources/Locale/ru-RU/prototypes/actions/types.ftl new file mode 100644 index 0000000000..57aa426632 --- /dev/null +++ b/Resources/Locale/ru-RU/prototypes/actions/types.ftl @@ -0,0 +1,8 @@ +ent-ActivateSmokeImplant = Переключить ночное зрение + .desc = Переключает ночное зрение. + +ent-ActivateSmokeImplant = Переключить тепловизионное зрение + .desc = Переключает тепловизионное зрение. + +ent-PulseThermalVision = Подать термальный импульт + .desc = Временно активируйте тепловизионное зрение. \ No newline at end of file diff --git a/Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl b/Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl new file mode 100644 index 0000000000..050e373c86 --- /dev/null +++ b/Resources/Locale/ru-RU/prototypes/entities/clothing/eyes/goggles.ftl @@ -0,0 +1,23 @@ +# Night Vision Goggles +ent-ClothingEyesNightVisionGoggles = ПНВ + .desc = Усовершенствованный дисплей, который обеспечивает видимость в полной темноте. +ent-ClothingEyesNightVisionSecurityGoggles = ПНВ службы безопасности + .desc = { ent-ClothingEyesNightVisionGoggles.desc } +ent-ClothingEyesNightVisionMedicalGoggles = ПНВ медицинский + .desc = { ent-ClothingEyesNightVisionGoggles.desc } +ent-ClothingEyesNightVisionDiagnosticGoggles = ПНВ диагностический + .desc = { ent-ClothingEyesNightVisionGoggles.desc } +ent-ClothingEyesNightVisionGogglesSyndie = { ent-ClothingEyesNightVisionGoggles } + .desc = { ent-ClothingEyesNightVisionGoggles.desc } +ent-ClothingEyesNightVisionGogglesNukie = { ent-ClothingEyesNightVisionGoggles } + .desc = { ent-ClothingEyesNightVisionGoggles.desc } + +# Thermal Vision Goggles +ent-ClothingEyesThermalVisionGoggles = ПТВ + .desc = Термальность - это как отношения, важно не только наличие тепла, но и его распределение. +ent-ClothingEyesThermalVisionMonocle = термонокль + .desc = Видение сквозь стены ещё никогда не было таким нежным и личностным. +ent-ClothingEyesThermalVisionGogglesSyndie = { ent-ClothingEyesThermalVisionGoggles } + .desc = { ent-ClothingEyesThermalVisionGoggles.desc } +ent-ClothingEyesThermalVisionGogglesNukie = { ent-ClothingEyesThermalVisionGoggles } + .desc = { ent-ClothingEyesThermalVisionGoggles.desc } diff --git a/Resources/Locale/ru-RU/entities/objects/tools/empflashlight.ftl b/Resources/Locale/ru-RU/prototypes/objects/tools/empflashlight.ftl similarity index 100% rename from Resources/Locale/ru-RU/entities/objects/tools/empflashlight.ftl rename to Resources/Locale/ru-RU/prototypes/objects/tools/empflashlight.ftl diff --git a/Resources/Locale/ru-RU/research/techologies.ftl b/Resources/Locale/ru-RU/research/techologies.ftl new file mode 100644 index 0000000000..54f3899255 --- /dev/null +++ b/Resources/Locale/ru-RU/research/techologies.ftl @@ -0,0 +1,2 @@ +research-technology-night-vision = Ночное видение +research-technology-thermal-vision = Термальное видение \ No newline at end of file diff --git a/Resources/Locale/ru-RU/store/uplink-catalog.ftl b/Resources/Locale/ru-RU/store/uplink-catalog.ftl index 5bb4067557..a5ccaace17 100644 --- a/Resources/Locale/ru-RU/store/uplink-catalog.ftl +++ b/Resources/Locale/ru-RU/store/uplink-catalog.ftl @@ -1,2 +1,9 @@ uplink-emp-flashlight-name = Электромагнитный фонарик uplink-emp-flashlight-desc = Замаскированное под фонарик устройство. При ударе выпускает ЭМИ, поражающий электрические устройства. + +#Armor +uplink-night-vision-name = Прибор ночного видения +uplink-night-vision-desc = Позволяет вам видеть в темноте, при этом выглядя как обычные солнцезащитные очки! + +uplink-thermal-vision-name = Прибор термального видения +uplink-thermal-vision-desc = Позволяет вам видеть живых существ независимо от преград, при этом выглядя как обычные солнцезащитные очки! diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index 43aaef4736..0a57157f1b 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -404,4 +404,43 @@ useDelay: 40 event: !type:FabricateActionEvent - fabrication: FoodGumball \ No newline at end of file + fabrication: FoodGumball + +- type: entity + id: ToggleNightVision + name: Switch night vision + description: Switches night vision. + categories: [ HideSpawnMenu ] + components: + - type: InstantAction + itemIconStyle: BigAction + priority: -20 + icon: + sprite: Clothing/Eyes/Goggles/nightvision.rsi + state: icon + event: !type:ToggleNightVisionEvent + +- type: entity + id: ToggleThermalVision + name: Switch Thermal vision + description: Switches Thermal vision. + categories: [ HideSpawnMenu ] + components: + - type: InstantAction + itemIconStyle: BigAction + priority: -20 + icon: + sprite: Clothing/Eyes/Goggles/thermal.rsi + state: icon + event: !type:ToggleThermalVisionEvent + +- type: entity + id: PulseThermalVision + parent: ToggleThermalVision + name: Pulse Thermal Vision + description: Activate thermal vision temporarily. + categories: [ HideSpawnMenu ] + components: + - type: InstantAction + useDelay: 4 + diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index 756f295d12..65916a84b9 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -44,7 +44,7 @@ prob: 0.2 - !type:EntSelector id: WeaponFlareGun - prob: 0.1 + prob: 0.05 - !type:EntSelector id: BoxMRE prob: 0.1 @@ -165,6 +165,10 @@ - !type:EntSelector id: WeaponSniperMosin weight: 2 + - !type:EntSelector + id: ClothingEyesNightVisionGogglesSyndie + - !type:EntSelector + id: ClothingEyesThermalVisionGogglesSyndie - type: entityTable id: MaintenanceLockerLoot @@ -235,4 +239,4 @@ - !type:EntSelector id: GeigerCounter amount: !type:ConstantNumberSelector - value: 2 \ No newline at end of file + value: 2 diff --git a/Resources/Prototypes/Catalog/uplink_catalog.yml b/Resources/Prototypes/Catalog/uplink_catalog.yml index 7f9088a3d0..cc0e0bb0f3 100644 --- a/Resources/Prototypes/Catalog/uplink_catalog.yml +++ b/Resources/Prototypes/Catalog/uplink_catalog.yml @@ -1532,6 +1532,78 @@ components: - SurplusBundle +- type: listing + id: UplinkNightGoggles + name: uplink-night-vision-name + description: uplink-night-vision-desc + productEntity: ClothingEyesNightVisionGogglesSyndie + discountCategory: rareDiscounts + discountDownTo: + Telecrystal: 1 + cost: + Telecrystal: 3 + categories: + - UplinkWearables + conditions: + - !type:StoreWhitelistCondition + blacklist: + tags: + - NukeOpsUplink + +- type: listing + id: UplinkNightGogglesNukie + name: uplink-night-vision-name + description: uplink-night-vision-desc + productEntity: ClothingEyesNightVisionGogglesNukie + discountCategory: rareDiscounts + discountDownTo: + Telecrystal: 1 + cost: + Telecrystal: 3 + categories: + - UplinkWearables + conditions: + - !type:StoreWhitelistCondition + whitelist: + tags: + - NukeOpsUplink + +- type: listing + id: UplinkThermalGoggles + name: uplink-thermal-vision-name + description: uplink-thermal-vision-desc + productEntity: ClothingEyesThermalVisionGogglesSyndie + discountCategory: rareDiscounts + discountDownTo: + Telecrystal: 1 + cost: + Telecrystal: 3 + categories: + - UplinkWearables + conditions: + - !type:StoreWhitelistCondition + blacklist: + tags: + - NukeOpsUplink + +- type: listing + id: UplinkThermalGogglesNukie + name: uplink-thermal-vision-name + description: uplink-thermal-vision-desc + productEntity: ClothingEyesThermalVisionGogglesNukie + discountCategory: rareDiscounts + discountDownTo: + Telecrystal: 1 + cost: + Telecrystal: 3 + categories: + - UplinkWearables + conditions: + - !type:StoreWhitelistCondition + whitelist: + tags: + - NukeOpsUplink + # Tools - type: listing diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml index f50b0dbca5..59817932c7 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml @@ -232,6 +232,7 @@ - type: Clothing sprite: Clothing/Eyes/Glasses/ninjavisor.rsi - type: FlashImmunity + - type: NightVision - type: entity #Fake goggles, the latest in anti-valid hunting technology parent: ClothingEyesBase diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml b/Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml new file mode 100644 index 0000000000..4348c87227 --- /dev/null +++ b/Resources/Prototypes/Entities/Clothing/Eyes/goggles.yml @@ -0,0 +1,114 @@ +# Night Vision Goggles + +- type: entity + parent: ClothingEyesBase + id: ClothingEyesNightVisionGoggles + name: night vision goggles + description: An advanced heads-up display which provides id data and vision in complete darkness. + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/nightvision.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/nightvision.rsi + - type: NightVision + - type: IdentityBlocker + coverage: EYES + +- type: entity + parent: [ClothingEyesNightVisionGoggles, ShowSecurityIcons] + id: ClothingEyesNightVisionSecurityGoggles + name: night vision security goggles + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/security_nightvision.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/security_nightvision.rsi + +- type: entity + parent: [ClothingEyesNightVisionGoggles, ClothingEyesHudMedical] + id: ClothingEyesNightVisionMedicalGoggles + name: night vision medical goggles + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/medical_nightvision.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/medical_nightvision.rsi + +- type: entity + parent: [ClothingEyesNightVisionGoggles, ClothingEyesHudDiagnostic] + id: ClothingEyesNightVisionDiagnosticGoggles + name: night vision diagnostic goggles + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/diagnostic_nightvision.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/diagnostic_nightvision.rsi + +- type: entity + parent: ClothingEyesNightVisionGoggles + id: ClothingEyesNightVisionGogglesSyndie + suffix: "Chameleon" + components: + - type: ChameleonClothing + slot: [eyes] + default: ClothingEyesNightVisionGoggles + - type: UserInterface + interfaces: + enum.ChameleonUiKey.Key: + type: ChameleonBoundUserInterface + +- type: entity + parent: [ClothingEyesNightVisionGogglesSyndie, ShowSecurityIcons] + id: ClothingEyesNightVisionGogglesNukie + suffix: "Chameleon, NukeOps" + components: + - type: ShowSyndicateIcons + +# Thermal Vision Goggles + +- type: entity + parent: ClothingEyesBase + id: ClothingEyesThermalVisionGoggles + name: thermal vision goggles + description: Thermals in the shape of glasses. + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/thermal.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/thermal.rsi + - type: ThermalVision + pulseTime: 2 + toggleAction: PulseThermalVision + - type: IdentityBlocker + coverage: EYES + +- type: entity + parent: ClothingEyesThermalVisionGoggles + id: ClothingEyesThermalVisionMonocle + name: thermonocle + description: Never before has seeing through walls felt so gentlepersonly. + components: + - type: Sprite + sprite: Clothing/Eyes/Goggles/monocle_thermal.rsi + - type: Clothing + sprite: Clothing/Eyes/Goggles/monocle_thermal.rsi + +- type: entity + parent: ClothingEyesThermalVisionGoggles + id: ClothingEyesThermalVisionGogglesSyndie + suffix: "Chameleon" + components: + - type: ChameleonClothing + slot: [eyes] + default: ClothingEyesThermalVisionGoggles + - type: UserInterface + interfaces: + enum.ChameleonUiKey.Key: + type: ChameleonBoundUserInterface + +- type: entity + parent: [ClothingEyesThermalVisionGogglesSyndie, ShowSecurityIcons] + id: ClothingEyesThermalVisionGogglesNukie + suffix: "Chameleon, NukeOps" + components: + - type: ShowSyndicateIcons diff --git a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml index caccdd8745..04f796591f 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml @@ -744,6 +744,9 @@ Heat: 0.80 Radiation: 0.80 Caustic: 0.95 + - type: ThermalVision + color: "#98EEFB" + lightRadius: 15 #MISC. HARDSUITS #Clown Hardsuit diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index b8ff03abca..ada65ad33f 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -126,6 +126,7 @@ cell_slot: name: power-cell-slot-component-slot-name-default - type: Body + thermalVisibility: false - type: StatusEffects allowed: - Stun diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index f1932e3ece..faa7fbc22b 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -70,6 +70,12 @@ barkType: mouse minTime: 10 # Mice like to squeak, I think. You can always put your pet mouse to sleep if it gets annoying maxTime: 160 + - type: NightVision + isActive: true + toggleAction: null + color: "#808080" + activateSound: null + deactivateSound: null - type: entity name: bee @@ -1792,6 +1798,12 @@ Taco: RatTaco Burger: RatBurger Skewer: RatSkewer + - type: NightVision + isActive: true + toggleAction: null + color: "#808080" + activateSound: null + deactivateSound: null - type: entity parent: MobMouse diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml index d241173f2e..356615d2e9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/carp.yml @@ -88,6 +88,12 @@ interfaces: enum.SurgeryUIKey.Key: type: SurgeryBui + - type: NightVision + isActive: true + toggleAction: null + color: "#808080" + activateSound: null + deactivateSound: null - type: entity parent: BaseMobCarp diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml index abe602037f..9e4f72bb0c 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml @@ -132,6 +132,12 @@ interfaces: enum.SurgeryUIKey.Key: type: SurgeryBui + - type: NightVision + isActive: true + toggleAction: null + color: "#808080" + activateSound: null + deactivateSound: null - type: entity id: MobRatKingBuff @@ -317,6 +323,12 @@ - type: FireVisuals sprite: Mobs/Effects/onfire.rsi normalState: Mouse_burning + - type: NightVision + isActive: true + toggleAction: null + color: "#808080" + activateSound: null + deactivateSound: null - type: weightedRandomEntity id: RatKingLoot diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index 7358d07236..db8287e13e 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -140,6 +140,12 @@ - Xeno understands: - Xeno + - type: ThermalVision + isActive: true + lightRadius: 15 + color: "#808080" + activateSound: null + deactivateSound: null - type: entity name: Praetorian diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index c91b209e8c..a0a0d1ed2e 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -35,6 +35,7 @@ bodyType: Kinematic - type: Body prototype: Aghost + thermalVisibility: false - type: Access groups: - AllAccess diff --git a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml index 0f24313712..4c03c6c688 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/dragon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/dragon.yml @@ -140,6 +140,11 @@ tags: - CannotSuicide - DoorBumpOpener + - type: NightVision + isActive: true + color: "#808080" + activateSound: null + deactivateSound: null - type: entity parent: BaseMobDragon diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml index 7c65b89a76..b8de17a55f 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon_base.yml @@ -233,6 +233,7 @@ - type: Body prototype: IPC requiredLegs: 2 + thermalVisibility: false - type: Ensnareable sprite: Objects/Misc/ensnare.rsi - type: Speech diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 850b2c0d74..366a4b2166 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -16,7 +16,7 @@ mask: - MachineMask layer: - - MachineLayer + - MachineLayer - type: Lathe - type: MaterialStorage - type: Destructible @@ -109,118 +109,118 @@ - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial - - Ingot + - Sheet + - RawMaterial + - Ingot - type: Lathe idleState: icon runningState: building staticRecipes: - - Wirecutter - - Igniter - - Signaller - - Screwdriver - - Welder - - Wrench - - Crowbar - - Multitool - - NetworkConfigurator - - SprayPainter - - FlashlightLantern - - CableStack - - CableMVStack - - CableHVStack - - HandheldGPSBasic - - TRayScanner - - AirTank - - GasAnalyzer - - UtilityBelt - - Fulton - - FultonBeacon - - Pickaxe - - ModularReceiver - - AppraisalTool - - SheetRGlass - - Beaker - - Syringe - - HandLabeler - - LightTube - - LedLightTube - - SodiumLightTube - - ExteriorLightTube - - LightBulb - - LedLightBulb - - Bucket - - DrinkMug - - DrinkMugMetal - - DrinkGlass - - DrinkShotGlass - - DrinkGlassCoupeShaped - - CustomDrinkJug - - FoodPlate - - FoodPlateSmall - - FoodPlatePlastic - - FoodPlateSmallPlastic - - FoodBowlBig - - FoodPlateTin - - FoodKebabSkewer - - SprayBottle - - MopItem - - Holoprojector - - Mousetrap - - LightReplacer - - TrashBag - - PowerCellSmall - - PowerCellMedium - - RollerBedSpawnFolded - - CheapRollerBedSpawnFolded - - EmergencyRollerBedSpawnFolded - - MicroManipulatorStockPart - - MatterBinStockPart - - CapacitorStockPart - - ConveyorBeltAssembly - - IntercomElectronics - - FirelockElectronics - - DoorElectronics - - AirAlarmElectronics - - StationMapElectronics - - FireAlarmElectronics - - MailingUnitElectronics - - SignalTimerElectronics - - APCElectronics - - SMESMachineCircuitboard - - SubstationMachineCircuitboard - - WallmountSubstationElectronics - - CellRechargerCircuitboard - - BorgChargerCircuitboard - - WeaponCapacitorRechargerCircuitboard - - HandheldStationMap - - ClothingHeadHatWelding + - Wirecutter + - Igniter + - Signaller + - Screwdriver + - Welder + - Wrench + - Crowbar + - Multitool + - NetworkConfigurator + - SprayPainter + - FlashlightLantern + - CableStack + - CableMVStack + - CableHVStack + - HandheldGPSBasic + - TRayScanner + - AirTank + - GasAnalyzer + - UtilityBelt + - Fulton + - FultonBeacon + - Pickaxe + - ModularReceiver + - AppraisalTool + - SheetRGlass + - Beaker + - Syringe + - HandLabeler + - LightTube + - LedLightTube + - SodiumLightTube + - ExteriorLightTube + - LightBulb + - LedLightBulb + - Bucket + - DrinkMug + - DrinkMugMetal + - DrinkGlass + - DrinkShotGlass + - DrinkGlassCoupeShaped + - CustomDrinkJug + - FoodPlate + - FoodPlateSmall + - FoodPlatePlastic + - FoodPlateSmallPlastic + - FoodBowlBig + - FoodPlateTin + - FoodKebabSkewer + - SprayBottle + - MopItem + - Holoprojector + - Mousetrap + - LightReplacer + - TrashBag + - PowerCellSmall + - PowerCellMedium + - RollerBedSpawnFolded + - CheapRollerBedSpawnFolded + - EmergencyRollerBedSpawnFolded + - MicroManipulatorStockPart + - MatterBinStockPart + - CapacitorStockPart + - ConveyorBeltAssembly + - IntercomElectronics + - FirelockElectronics + - DoorElectronics + - AirAlarmElectronics + - StationMapElectronics + - FireAlarmElectronics + - MailingUnitElectronics + - SignalTimerElectronics + - APCElectronics + - SMESMachineCircuitboard + - SubstationMachineCircuitboard + - WallmountSubstationElectronics + - CellRechargerCircuitboard + - BorgChargerCircuitboard + - WeaponCapacitorRechargerCircuitboard + - HandheldStationMap + - ClothingHeadHatWelding - type: EmagLatheRecipes emagStaticRecipes: - - BoxLethalshot - - BoxShotgunFlare - - BoxShotgunSlug - - MagazineBoxLightRifle - - MagazineBoxMagnum - - MagazineBoxPistol - - MagazineBoxRifle - - MagazineLightRifle - - MagazineLightRifleEmpty - - MagazinePistol - - MagazinePistolEmpty - - MagazinePistolSubMachineGun - - MagazinePistolSubMachineGunEmpty - - MagazinePistolSubMachineGunTopMounted - - MagazinePistolSubMachineGunTopMountedEmpty - - MagazineRifle - - MagazineRifleEmpty - - MagazineShotgun - - MagazineShotgunEmpty - - MagazineShotgunSlug - - RiotShield - - SpeedLoaderMagnum - - SpeedLoaderMagnumEmpty + - BoxLethalshot + - BoxShotgunFlare + - BoxShotgunSlug + - MagazineBoxLightRifle + - MagazineBoxMagnum + - MagazineBoxPistol + - MagazineBoxRifle + - MagazineLightRifle + - MagazineLightRifleEmpty + - MagazinePistol + - MagazinePistolEmpty + - MagazinePistolSubMachineGun + - MagazinePistolSubMachineGunEmpty + - MagazinePistolSubMachineGunTopMounted + - MagazinePistolSubMachineGunTopMountedEmpty + - MagazineRifle + - MagazineRifleEmpty + - MagazineShotgun + - MagazineShotgunEmpty + - MagazineShotgunSlug + - RiotShield + - SpeedLoaderMagnum + - SpeedLoaderMagnumEmpty - type: entity id: AutolatheHyperConvection @@ -257,9 +257,9 @@ - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial - - Ingot + - Sheet + - RawMaterial + - Ingot - type: Lathe idleState: icon runningState: building @@ -268,150 +268,153 @@ - Dropper - ClothingEyesGlassesChemical dynamicRecipes: - - PowerDrill - - MiningDrill - - MiningDrillDiamond - - AnomalyScanner - - AnomalyLocator - - AnomalyLocatorWide - - HandheldCrewMonitor - - Scalpel - - Retractor - - Cautery - - Drill - - WeaponParticleDecelerator - - HoloprojectorField - - Saw - - Hemostat - - CryostasisBeaker - - SyringeCryostasis - - Syringe - - Implanter - - PillCanister - - ChemistryEmptyBottle01 - - AdvancedCapacitorStockPart - - AdvancedMatterBinStockPart - - NanoManipulatorStockPart - - SuperCapacitorStockPart - - SuperMatterBinStockPart - - PicoManipulatorStockPart - - BluespaceCapacitorStockPart - - BluespaceManipulatorStockPart - - BluespaceMatterBinStockPart - - AdvMopItem - - WeaponSprayNozzle - - ClothingBackpackWaterTank - - MegaSprayBottle - - TimerTrigger - - ChemicalPayload - - FlashPayload - - Signaller - - SignallerAdvanced - - SignalTrigger - - VoiceTrigger - - Igniter - - HandHeldMassScanner - - PowerCellMicroreactor - - PowerCellHigh - - WeaponPistolCHIMP - - ClothingMaskWeldingGas - - WeaponGauntletGorilla - - SynthesizerInstrument - - RPED - - ClothingShoesBootsMagSci - - ClothingShoesBootsMoon - - ClothingShoesBootsSpeed - - NodeScanner - - HolofanProjector - - BluespaceBeaker - - SyringeBluespace - - WeaponForceGun - - WeaponLaserSvalinn - - WeaponProtoKineticAccelerator - - WeaponTetherGun - - WeaponGrapplingGun - - ClothingBackpackHolding - - ClothingBackpackSatchelHolding - - ClothingBackpackDuffelHolding - - WelderExperimental - - JawsOfLife - - CoreSilver # Nyanotrasen - Silver Golem core - - FauxTileAstroGrass - - FauxTileMowedAstroGrass - - FauxTileJungleAstroGrass - - FauxTileAstroIce - - FauxTileAstroSnow - - OreBagOfHolding - - DeviceQuantumSpinInverter - - CanilunztTranslator - - BubblishTranslator - - NekomimeticTranslator - - DraconicTranslator - - SolCommonTranslator - - RootSpeakTranslator - - XenoTranslator - - BasicGalaticCommonTranslatorImplanter - - AdvancedGalaticCommonTranslatorImplanter - - BubblishTranslatorImplanter - - NekomimeticTranslatorImplanter - - DraconicTranslatorImplanter - - CanilunztTranslatorImplanter - - SolCommonTranslatorImplanter - - RootSpeakTranslatorImplanter - - AnimalTranslator - - MofficTranslatorImplanter - - MofficTranslator - - RCDAmmo #DeltaV - - RCD #EE - # Shitmed Change - - EnergyScalpel - - EnergyCautery - - AdvancedRetractor + - PowerDrill + - MiningDrill + - MiningDrillDiamond + - AnomalyScanner + - AnomalyLocator + - AnomalyLocatorWide + - HandheldCrewMonitor + - Scalpel + - Retractor + - Cautery + - Drill + - WeaponParticleDecelerator + - HoloprojectorField + - Saw + - Hemostat + - CryostasisBeaker + - SyringeCryostasis + - Syringe + - Implanter + - PillCanister + - ChemistryEmptyBottle01 + - AdvancedCapacitorStockPart + - AdvancedMatterBinStockPart + - NanoManipulatorStockPart + - SuperCapacitorStockPart + - SuperMatterBinStockPart + - PicoManipulatorStockPart + - BluespaceCapacitorStockPart + - BluespaceManipulatorStockPart + - BluespaceMatterBinStockPart + - AdvMopItem + - WeaponSprayNozzle + - ClothingBackpackWaterTank + - MegaSprayBottle + - TimerTrigger + - ChemicalPayload + - FlashPayload + - Signaller + - SignallerAdvanced + - SignalTrigger + - VoiceTrigger + - Igniter + - HandHeldMassScanner + - PowerCellMicroreactor + - PowerCellHigh + - WeaponPistolCHIMP + - ClothingMaskWeldingGas + - WeaponGauntletGorilla + - SynthesizerInstrument + - RPED + - ClothingShoesBootsMagSci + - ClothingShoesBootsMoon + - ClothingShoesBootsSpeed + - NodeScanner + - HolofanProjector + - BluespaceBeaker + - SyringeBluespace + - WeaponForceGun + - WeaponLaserSvalinn + - WeaponProtoKineticAccelerator + - WeaponTetherGun + - WeaponGrapplingGun + - ClothingBackpackHolding + - ClothingBackpackSatchelHolding + - ClothingBackpackDuffelHolding + - WelderExperimental + - JawsOfLife + - CoreSilver # Nyanotrasen - Silver Golem core + - FauxTileAstroGrass + - FauxTileMowedAstroGrass + - FauxTileJungleAstroGrass + - FauxTileAstroIce + - FauxTileAstroSnow + - OreBagOfHolding + - DeviceQuantumSpinInverter + - CanilunztTranslator + - BubblishTranslator + - NekomimeticTranslator + - DraconicTranslator + - SolCommonTranslator + - RootSpeakTranslator + - XenoTranslator + - BasicGalaticCommonTranslatorImplanter + - AdvancedGalaticCommonTranslatorImplanter + - BubblishTranslatorImplanter + - NekomimeticTranslatorImplanter + - DraconicTranslatorImplanter + - CanilunztTranslatorImplanter + - SolCommonTranslatorImplanter + - RootSpeakTranslatorImplanter + - AnimalTranslator + - MofficTranslatorImplanter + - MofficTranslator + - ClothingEyesNightVisionGoggles + - ClothingEyesNightVisionDiagnosticGoggles + - ClothingEyesThermalVisionGoggles + - RCDAmmo #DeltaV + - RCD #EE + # Shitmed Change + - EnergyScalpel + - EnergyCautery + - AdvancedRetractor - type: EmagLatheRecipes emagDynamicRecipes: - - BoxBeanbag - - BoxShotgunIncendiary - - BoxShotgunUranium - - ExplosivePayload - - GrenadeBlast - - GrenadeEMP - - GrenadeFlash - - HoloprojectorSecurity - - MagazineBoxLightRifleIncendiary - - MagazineBoxLightRifleUranium - - MagazineBoxMagnumIncendiary - - MagazineBoxMagnumUranium - - MagazineBoxPistolIncendiary - - MagazineBoxPistolUranium - - MagazineBoxRifleIncendiary - - MagazineBoxRifleUranium - - MagazineGrenadeEmpty - - MagazineLightRifleIncendiary - - MagazineLightRifleUranium - - MagazinePistolIncendiary - - MagazinePistolUranium - - MagazineRifleIncendiary - - MagazineRifleUranium - - MagazineShotgunBeanbag - - MagazineShotgunIncendiary - - MagazineShotgunIncendiary - - PortableRecharger - - PowerCageHigh - - PowerCageMedium - - PowerCageSmall - - ShellTranquilizer - - SpeedLoaderMagnumIncendiary - - SpeedLoaderMagnumUranium - - TelescopicShield - - Truncheon - - WeaponAdvancedLaser - - WeaponLaserCannon - - WeaponLaserCarbine - - WeaponXrayCannon - - WeaponEnergyGun # DeltaV - Energy Gun - - WeaponEnergyGunMini # DeltaV - Miniature Energy Gun - - WeaponEnergyGunPistol # DeltaV - PDW-9 Energy Pistol - - WeaponGunLaserCarbineAutomatic # DeltaV - IK-60 Laser Carbine + - BoxBeanbag + - BoxShotgunIncendiary + - BoxShotgunUranium + - ExplosivePayload + - GrenadeBlast + - GrenadeEMP + - GrenadeFlash + - HoloprojectorSecurity + - MagazineBoxLightRifleIncendiary + - MagazineBoxLightRifleUranium + - MagazineBoxMagnumIncendiary + - MagazineBoxMagnumUranium + - MagazineBoxPistolIncendiary + - MagazineBoxPistolUranium + - MagazineBoxRifleIncendiary + - MagazineBoxRifleUranium + - MagazineGrenadeEmpty + - MagazineLightRifleIncendiary + - MagazineLightRifleUranium + - MagazinePistolIncendiary + - MagazinePistolUranium + - MagazineRifleIncendiary + - MagazineRifleUranium + - MagazineShotgunBeanbag + - MagazineShotgunIncendiary + - MagazineShotgunIncendiary + - PortableRecharger + - PowerCageHigh + - PowerCageMedium + - PowerCageSmall + - ShellTranquilizer + - SpeedLoaderMagnumIncendiary + - SpeedLoaderMagnumUranium + - TelescopicShield + - Truncheon + - WeaponAdvancedLaser + - WeaponLaserCannon + - WeaponLaserCarbine + - WeaponXrayCannon + - WeaponEnergyGun # DeltaV - Energy Gun + - WeaponEnergyGunMini # DeltaV - Miniature Energy Gun + - WeaponEnergyGunPistol # DeltaV - PDW-9 Energy Pistol + - WeaponGunLaserCarbineAutomatic # DeltaV - IK-60 Laser Carbine - type: entity id: ProtolatheHyperConvection @@ -471,102 +474,102 @@ - SpaceHeaterMachineCircuitBoard - StationAnchorCircuitboard dynamicRecipes: - - ThermomachineFreezerMachineCircuitBoard - - HellfireFreezerMachineCircuitBoard - - PortableScrubberMachineCircuitBoard - - CloningPodMachineCircuitboard - - MedicalScannerMachineCircuitboard - - CryoPodMachineCircuitboard - - VaccinatorMachineCircuitboard - - DiagnoserMachineCircuitboard - - BiomassReclaimerMachineCircuitboard - - BiofabricatorMachineCircuitboard - - SurveillanceCameraRouterCircuitboard - - SurveillanceCameraMonitorCircuitboard - - SurveillanceWirelessCameraMonitorCircuitboard - - SurveillanceCameraWirelessRouterCircuitboard - - ComputerTelevisionCircuitboard - - JukeboxCircuitBoard - - SurveillanceWirelessCameraMovableCircuitboard - - SurveillanceWirelessCameraAnchoredCircuitboard - - HydroponicsTrayMachineCircuitboard - - SolarControlComputerCircuitboard - - SolarTrackerElectronics - - TurboItemRechargerCircuitboard - - PowerComputerCircuitboard - - AlertsComputerCircuitboard - - AutolatheHyperConvectionMachineCircuitboard - - ProtolatheHyperConvectionMachineCircuitboard - - CircuitImprinterHyperConvectionMachineCircuitboard - - FatExtractorMachineCircuitboard - - FlatpackerMachineCircuitboard - - SheetifierMachineCircuitboard - - ShuttleConsoleCircuitboard - - RadarConsoleCircuitboard - - TechDiskComputerCircuitboard - - DawInstrumentMachineCircuitboard - - CloningConsoleComputerCircuitboard - - StasisBedMachineCircuitboard - - OreProcessorIndustrialMachineCircuitboard - - CargoTelepadMachineCircuitboard - - RipleyCentralElectronics - - RipleyPeripheralsElectronics - - HonkerCentralElectronics - - HonkerPeripheralsElectronics - - HonkerTargetingElectronics - - HamtrCentralElectronics - - HamtrPeripheralsElectronics - - PortableGeneratorPacmanMachineCircuitboard - - PortableGeneratorSuperPacmanMachineCircuitboard - - PortableGeneratorJrPacmanMachineCircuitboard - - WallmountGeneratorElectronics - - WallmountGeneratorAPUElectronics - - WallmountSubstationElectronics - - PowerCageRechargerCircuitboard - - EmitterCircuitboard - - ThrusterMachineCircuitboard - - GyroscopeMachineCircuitboard - - MiniGravityGeneratorCircuitboard - - ShuttleGunKineticCircuitboard - - GasRecyclerMachineCircuitboard - - SeedExtractorMachineCircuitboard - - AnalysisComputerCircuitboard - - ExosuitFabricatorMachineCircuitboard - - AnomalyVesselCircuitboard - - AnomalyVesselExperimentalCircuitboard - - AnomalySynchronizerCircuitboard - - APECircuitboard - - ArtifactAnalyzerMachineCircuitboard - - ArtifactCrusherMachineCircuitboard - - TelecomServerCircuitboard - - MassMediaCircuitboard - - ReagentGrinderIndustrialMachineCircuitboard - - ReverseEngineeringMachineCircuitboard - - CrewMonitoringComputerCircuitboard - - DoorElectronics - - FireAlarmElectronics - - FirelockElectronics - - IntercomElectronics - - MailingUnitElectronics - - SalvageMagnetMachineCircuitboard - - StationMapElectronics - - MetempsychoticMachineCircuitboard - - SalvageExpeditionsComputerCircuitboard - - JukeboxCircuitBoard - - AutodocCircuitboard # Shitmed Change - - OperatingTableCircuitboard # Shitmed Change + - ThermomachineFreezerMachineCircuitBoard + - HellfireFreezerMachineCircuitBoard + - PortableScrubberMachineCircuitBoard + - CloningPodMachineCircuitboard + - MedicalScannerMachineCircuitboard + - CryoPodMachineCircuitboard + - VaccinatorMachineCircuitboard + - DiagnoserMachineCircuitboard + - BiomassReclaimerMachineCircuitboard + - BiofabricatorMachineCircuitboard + - SurveillanceCameraRouterCircuitboard + - SurveillanceCameraMonitorCircuitboard + - SurveillanceWirelessCameraMonitorCircuitboard + - SurveillanceCameraWirelessRouterCircuitboard + - ComputerTelevisionCircuitboard + - JukeboxCircuitBoard + - SurveillanceWirelessCameraMovableCircuitboard + - SurveillanceWirelessCameraAnchoredCircuitboard + - HydroponicsTrayMachineCircuitboard + - SolarControlComputerCircuitboard + - SolarTrackerElectronics + - TurboItemRechargerCircuitboard + - PowerComputerCircuitboard + - AlertsComputerCircuitboard + - AutolatheHyperConvectionMachineCircuitboard + - ProtolatheHyperConvectionMachineCircuitboard + - CircuitImprinterHyperConvectionMachineCircuitboard + - FatExtractorMachineCircuitboard + - FlatpackerMachineCircuitboard + - SheetifierMachineCircuitboard + - ShuttleConsoleCircuitboard + - RadarConsoleCircuitboard + - TechDiskComputerCircuitboard + - DawInstrumentMachineCircuitboard + - CloningConsoleComputerCircuitboard + - StasisBedMachineCircuitboard + - OreProcessorIndustrialMachineCircuitboard + - CargoTelepadMachineCircuitboard + - RipleyCentralElectronics + - RipleyPeripheralsElectronics + - HonkerCentralElectronics + - HonkerPeripheralsElectronics + - HonkerTargetingElectronics + - HamtrCentralElectronics + - HamtrPeripheralsElectronics + - PortableGeneratorPacmanMachineCircuitboard + - PortableGeneratorSuperPacmanMachineCircuitboard + - PortableGeneratorJrPacmanMachineCircuitboard + - WallmountGeneratorElectronics + - WallmountGeneratorAPUElectronics + - WallmountSubstationElectronics + - PowerCageRechargerCircuitboard + - EmitterCircuitboard + - ThrusterMachineCircuitboard + - GyroscopeMachineCircuitboard + - MiniGravityGeneratorCircuitboard + - ShuttleGunKineticCircuitboard + - GasRecyclerMachineCircuitboard + - SeedExtractorMachineCircuitboard + - AnalysisComputerCircuitboard + - ExosuitFabricatorMachineCircuitboard + - AnomalyVesselCircuitboard + - AnomalyVesselExperimentalCircuitboard + - AnomalySynchronizerCircuitboard + - APECircuitboard + - ArtifactAnalyzerMachineCircuitboard + - ArtifactCrusherMachineCircuitboard + - TelecomServerCircuitboard + - MassMediaCircuitboard + - ReagentGrinderIndustrialMachineCircuitboard + - ReverseEngineeringMachineCircuitboard + - CrewMonitoringComputerCircuitboard + - DoorElectronics + - FireAlarmElectronics + - FirelockElectronics + - IntercomElectronics + - MailingUnitElectronics + - SalvageMagnetMachineCircuitboard + - StationMapElectronics + - MetempsychoticMachineCircuitboard + - SalvageExpeditionsComputerCircuitboard + - JukeboxCircuitBoard + - AutodocCircuitboard # Shitmed Change + - OperatingTableCircuitboard # Shitmed Change - type: EmagLatheRecipes emagDynamicRecipes: - - ShuttleGunDusterCircuitboard - - ShuttleGunFriendshipCircuitboard - - ShuttleGunPerforatorCircuitboard - - ShuttleGunSvalinnMachineGunCircuitboard + - ShuttleGunDusterCircuitboard + - ShuttleGunFriendshipCircuitboard + - ShuttleGunPerforatorCircuitboard + - ShuttleGunSvalinnMachineGunCircuitboard - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial - - Ingot + - Sheet + - RawMaterial + - Ingot - type: RequireProjectileTarget - type: entity @@ -694,7 +697,7 @@ - HamtrLLeg - HamtrRLeg - VimHarness - # Begin Nyano additions + # Begin Nyano additions - JetpackBlue - JetpackMini # End Nyano additions @@ -732,8 +735,8 @@ - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial + - Sheet + - RawMaterial - type: Lathe idleState: icon runningState: building @@ -910,6 +913,7 @@ - WeaponDisablerSMG - WeaponLaserCannon - WeaponLaserCarbine + - ClothingEyesNightVisionSecurityGoggles - ClothingHeadHelmetInsulated # Nyanotrasen - Insulative headgear - ClothingHeadCage # Nyanotrasen - Insulative headgear - ShockCollar # Nyanotrasen - Shock Collar @@ -932,9 +936,9 @@ - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial - - Ingot + - Sheet + - RawMaterial + - Ingot - type: entity id: AmmoTechFab @@ -942,52 +946,52 @@ name: ammo techfab description: Prints the bare minimum of bullets that any budget military or armory could need. Nothing fancy. components: - - type: Sprite - sprite: Structures/Machines/techfab.rsi - layers: - - state: icon - map: ["enum.LatheVisualLayers.IsRunning"] - - state: ammo - - state: unlit - shader: unshaded - map: ["enum.PowerDeviceVisualLayers.Powered"] - - state: inserting - map: ["enum.MaterialStorageVisualLayers.Inserting"] - - state: panel - map: ["enum.WiresVisualLayers.MaintenancePanel"] - - type: Machine - board: AmmoTechFabCircuitboard - - type: Lathe - idleState: icon - runningState: icon - staticRecipes: - - BoxLethalshot - - BoxShotgunFlare - - BoxShotgunSlug - - MagazineBoxLightRifle - - MagazineBoxMagnum - - MagazineBoxPistol - - MagazineBoxRifle - - MagazineLightRifle - - MagazineLightRifleEmpty - - MagazinePistol - - MagazinePistolEmpty - - MagazineRifle - - MagazineRifleEmpty - - MagazineShotgun - - MagazineShotgunEmpty - - MagazineShotgunSlug - - ShellTranquilizer - - SpeedLoaderMagnum - - SpeedLoaderMagnumEmpty - - CartridgeSpecial - - MagazineBoxSpecial - - type: MaterialStorage - whitelist: - tags: - - Sheet - - RawMaterial - - Ingot + - type: Sprite + sprite: Structures/Machines/techfab.rsi + layers: + - state: icon + map: ["enum.LatheVisualLayers.IsRunning"] + - state: ammo + - state: unlit + shader: unshaded + map: ["enum.PowerDeviceVisualLayers.Powered"] + - state: inserting + map: ["enum.MaterialStorageVisualLayers.Inserting"] + - state: panel + map: ["enum.WiresVisualLayers.MaintenancePanel"] + - type: Machine + board: AmmoTechFabCircuitboard + - type: Lathe + idleState: icon + runningState: icon + staticRecipes: + - BoxLethalshot + - BoxShotgunFlare + - BoxShotgunSlug + - MagazineBoxLightRifle + - MagazineBoxMagnum + - MagazineBoxPistol + - MagazineBoxRifle + - MagazineLightRifle + - MagazineLightRifleEmpty + - MagazinePistol + - MagazinePistolEmpty + - MagazineRifle + - MagazineRifleEmpty + - MagazineShotgun + - MagazineShotgunEmpty + - MagazineShotgunSlug + - ShellTranquilizer + - SpeedLoaderMagnum + - SpeedLoaderMagnumEmpty + - CartridgeSpecial + - MagazineBoxSpecial + - type: MaterialStorage + whitelist: + tags: + - Sheet + - RawMaterial + - Ingot - type: entity id: MedicalTechFab @@ -1012,58 +1016,59 @@ idleState: icon runningState: icon staticRecipes: - - Brutepack - - Ointment - - Gauze - - HandLabeler - - Defibrillator - - HandheldHealthAnalyzer - - ClothingHandsGlovesLatex - - ClothingHandsGlovesNitrile - - ClothingMaskSterile - - DiseaseSwab - - Beaker - - LargeBeaker - - Dropper - - Jug - - Syringe - - Implanter - - PillCanister - - BodyBag - - ChemistryEmptyBottle01 - - RollerBedSpawnFolded - - CheapRollerBedSpawnFolded - - EmergencyRollerBedSpawnFolded - - Medkit - - MedkitBurn - - MedkitToxin - - MedkitO2 - - MedkitBrute - - MedkitAdvanced - - MedkitRadiation - - MedkitCombat - - Scalpel - - Retractor - - Cautery - - Drill - - Saw - - Hemostat - - ClothingEyesGlassesChemical - - BoneGel # Shitmed Change + - Brutepack + - Ointment + - Gauze + - HandLabeler + - Defibrillator + - HandheldHealthAnalyzer + - ClothingHandsGlovesLatex + - ClothingHandsGlovesNitrile + - ClothingMaskSterile + - DiseaseSwab + - Beaker + - LargeBeaker + - Dropper + - Jug + - Syringe + - Implanter + - PillCanister + - BodyBag + - ChemistryEmptyBottle01 + - RollerBedSpawnFolded + - CheapRollerBedSpawnFolded + - EmergencyRollerBedSpawnFolded + - Medkit + - MedkitBurn + - MedkitToxin + - MedkitO2 + - MedkitBrute + - MedkitAdvanced + - MedkitRadiation + - MedkitCombat + - Scalpel + - Retractor + - Cautery + - Drill + - Saw + - Hemostat + - ClothingEyesGlassesChemical + - BoneGel # Shitmed Change dynamicRecipes: - - ChemicalPayload - - CryostasisBeaker - - BluespaceBeaker - - SyringeBluespace - - ClothingEyesHudMedical # Nyano - - ChemicalPayload # Nyano - - SyringeCryostasis - # Shitmed Change - - EnergyScalpel - - EnergyCautery - - AdvancedRetractor - - OmnimedTool - - MedicalCyberneticEyes + - ChemicalPayload + - CryostasisBeaker + - BluespaceBeaker + - SyringeBluespace + - ClothingEyesHudMedical # Nyano + - ChemicalPayload # Nyano + - SyringeCryostasis + - ClothingEyesNightVisionMedicalGoggles + # Shitmed Change + - EnergyScalpel + - EnergyCautery + - AdvancedRetractor + - OmnimedTool + - MedicalCyberneticEyes - type: Machine board: MedicalTechFabCircuitboard - type: StealTarget @@ -1090,209 +1095,209 @@ idleState: icon runningState: building staticRecipes: - - ClothingUniformJumpsuitColorGrey - - ClothingUniformJumpskirtColorGrey - - ClothingUniformJumpsuitBartender - - ClothingUniformJumpskirtBartender - - ClothingHeadHatCapcap - - ClothingHeadHatCaptain - - ClothingUniformJumpsuitCaptain - - ClothingUniformJumpskirtCaptain - - ClothingUniformJumpsuitCapFormal - - ClothingUniformJumpskirtCapFormalDress - - ClothingUniformJumpsuitCargo - - ClothingUniformJumpskirtCargo - - ClothingUniformJumpsuitSalvageSpecialist - - ClothingHeadHatBeretEngineering - - ClothingUniformJumpsuitChiefEngineer - - ClothingUniformJumpskirtChiefEngineer - - ClothingUniformJumpsuitChiefEngineerTurtle - - ClothingUniformJumpskirtChiefEngineerTurtle - - ClothingUniformJumpsuitChaplain - - ClothingUniformJumpskirtChaplain - - ClothingUniformJumpsuitChef - - ClothingUniformJumpskirtChef - - ClothingUniformJumpsuitChemistry - - ClothingUniformJumpskirtChemistry - - ClothingUniformJumpsuitClown - - ClothingHeadHatBeretCmo - - ClothingUniformJumpsuitCMO - - ClothingUniformJumpskirtCMO - - ClothingUniformJumpsuitCMOTurtle - - ClothingUniformJumpskirtCMOTurtle - - ClothingUniformJumpsuitDetective - - ClothingUniformJumpskirtDetective - - ClothingUniformJumpsuitEngineering - - ClothingUniformJumpskirtEngineering - - ClothingUniformJumpsuitSeniorEngineer - - ClothingUniformJumpskirtSeniorEngineer - - ClothingHeadHatHopcap - - ClothingUniformJumpsuitHoP - - ClothingUniformJumpskirtHoP - - ClothingHeadHatBeretHoS - - ClothingHeadHatHoshat - - ClothingUniformJumpsuitHoS - - ClothingUniformJumpskirtHoS - - ClothingUniformJumpsuitHoSBlue # DeltaV - alternate sec uniforms - - ClothingUniformJumpskirtHoSBlue # DeltaV - alternate sec uniforms - - ClothingUniformJumpsuitHoSGrey # DeltaV - alternate sec uniforms - - ClothingUniformJumpskirtHoSGrey # DeltaV - alternate sec uniforms - - ClothingUniformJumpsuitHosFormal - - ClothingUniformJumpskirtHosFormal - - ClothingUniformJumpsuitHoSAlt - - ClothingUniformJumpskirtHoSAlt - - ClothingUniformJumpsuitHoSBlue - - ClothingUniformJumpsuitHoSGrey - - ClothingUniformJumpsuitHoSParadeMale - - ClothingUniformJumpskirtHoSParadeMale - - ClothingUniformJumpsuitHydroponics - - ClothingUniformJumpskirtHydroponics - - ClothingUniformJumpsuitJanitor - - ClothingUniformJumpskirtJanitor - - ClothingUniformJumpsuitLawyerBlack - - ClothingUniformJumpsuitLibrarian - - ClothingUniformJumpskirtColorLightBrown - - ClothingUniformCourier # DeltaV - Courier Uniform - - ClothingUniformSkirtCourier # DeltaV - Courier Uniform - - ClothingUniformJumpsuitMantis # Nyanotrasen - Forensic Mantis - - ClothingUniformSkirtMantis # Nyanotrasen - Forensic Mantis - - ClothingHeadHatBeretSeniorPhysician - - ClothingUniformJumpsuitMedicalDoctor - - ClothingUniformJumpskirtMedicalDoctor - - ClothingUniformJumpsuitSeniorPhysician - - ClothingUniformJumpskirtSeniorPhysician - - ClothingUniformJumpsuitMime - - ClothingUniformJumpskirtMime - - ClothingUniformJumpsuitMusician - - ClothingUniformJumpsuitParamedic - - ClothingUniformJumpskirtParamedic - - ClothingUniformJumpsuitSeniorOfficer - - ClothingUniformJumpskirtSeniorOfficer - - ClothingUniformJumpsuitPrisoner - - ClothingUniformJumpskirtPrisoner - - ClothingHeadHatQMsoft - - ClothingHeadHatBeretQM - - ClothingUniformJumpsuitQM - - ClothingUniformJumpskirtQM - - ClothingUniformJumpsuitQMTurtleneck - - ClothingUniformJumpskirtQMTurtleneck - - ClothingUniformJumpsuitQMFormal - - ClothingHeadHatBeretRND - - ClothingUniformJumpsuitResearchDirector - - ClothingUniformJumpskirtResearchDirector - - ClothingUniformJumpsuitScientist - - ClothingUniformJumpskirtScientist - - ClothingUniformJumpsuitSeniorResearcher - - ClothingUniformJumpskirtSeniorResearcher - - ClothingHeadHatBeretSecurity - - ClothingUniformJumpsuitSec - - ClothingUniformJumpskirtSec - - ClothingUniformJumpsuitSecBlue # DeltaV - alternate sec uniforms - - ClothingUniformJumpskirtSecBlue # DeltaV - alternate sec uniforms - - ClothingUniformJumpsuitSecGrey # DeltaV - alternate sec uniforms - - ClothingUniformJumpskirtSecGrey # DeltaV - alternate sec uniforms - - ClothingHeadHatBeretBrigmedic - - ClothingUniformJumpsuitBrigmedic - - ClothingUniformJumpskirtBrigmedic - - ClothingHeadHatBeretWarden - - ClothingHeadHatWarden - - ClothingUniformJumpsuitWarden - - ClothingUniformJumpskirtWarden - - ClothingUniformJumpsuitWardenBlue # DeltaV - alternate sec uniforms - - ClothingUniformJumpskirtWardenBlue # DeltaV - alternate sec uniforms - - ClothingUniformJumpsuitWardenGrey # DeltaV - alternate sec uniforms - - ClothingUniformJumpskirtWardenGrey # DeltaV - alternate sec uniforms - - ClothingHeadHatParamedicsoft - # Winter outfits - - ClothingOuterWinterCap - - ClothingOuterWinterCE - - ClothingOuterWinterCMO - - ClothingOuterWinterHoP - - ClothingOuterWinterHoSUnarmored - - ClothingOuterWinterWardenUnarmored - - ClothingOuterWinterQM - - ClothingOuterWinterRD - - ClothingNeckMantleCap - - ClothingNeckMantleCE - - ClothingNeckMantleCMO - - ClothingNeckMantleHOP - - ClothingNeckMantleHOS - - ClothingNeckMantleRD - - ClothingNeckMantleQM - - ClothingOuterStasecSweater # DeltaV - added stasec sweater to uniform printer. - - ClothingOuterWinterMusician - - ClothingOuterWinterClown - - ClothingOuterWinterMime - - ClothingOuterWinterCoat - - ClothingOuterWinterJani - - ClothingOuterWinterBar - - ClothingOuterWinterChef - - ClothingOuterWinterHydro - - ClothingOuterWinterAtmos - - ClothingOuterWinterEngi - - ClothingOuterWinterCargo - - ClothingOuterWinterMiner - - ClothingOuterWinterMed - - ClothingOuterWinterPara - - ClothingOuterWinterChem - - ClothingOuterWinterGen - - ClothingOuterWinterViro - - ClothingOuterWinterSci - - ClothingOuterWinterRobo - - ClothingOuterWinterSec - # Ties - - ClothingNeckTieRed - - ClothingNeckTieDet - - ClothingNeckTieSci - # Scarfs - All scarfs avaible in winterdrobe - - ClothingNeckScarfStripedGreen - - ClothingNeckScarfStripedBlue - - ClothingNeckScarfStripedRed - - ClothingNeckScarfStripedBrown - - ClothingNeckScarfStripedLightBlue - - ClothingNeckScarfStripedOrange - - ClothingNeckScarfStripedBlack - - ClothingNeckScarfStripedPurple - # Carpets - - Carpet - - CarpetBlack - - CarpetPink - - CarpetBlue - - CarpetGreen - - CarpetOrange - - CarpetPurple - - CarpetCyan - - CarpetWhite + - ClothingUniformJumpsuitColorGrey + - ClothingUniformJumpskirtColorGrey + - ClothingUniformJumpsuitBartender + - ClothingUniformJumpskirtBartender + - ClothingHeadHatCapcap + - ClothingHeadHatCaptain + - ClothingUniformJumpsuitCaptain + - ClothingUniformJumpskirtCaptain + - ClothingUniformJumpsuitCapFormal + - ClothingUniformJumpskirtCapFormalDress + - ClothingUniformJumpsuitCargo + - ClothingUniformJumpskirtCargo + - ClothingUniformJumpsuitSalvageSpecialist + - ClothingHeadHatBeretEngineering + - ClothingUniformJumpsuitChiefEngineer + - ClothingUniformJumpskirtChiefEngineer + - ClothingUniformJumpsuitChiefEngineerTurtle + - ClothingUniformJumpskirtChiefEngineerTurtle + - ClothingUniformJumpsuitChaplain + - ClothingUniformJumpskirtChaplain + - ClothingUniformJumpsuitChef + - ClothingUniformJumpskirtChef + - ClothingUniformJumpsuitChemistry + - ClothingUniformJumpskirtChemistry + - ClothingUniformJumpsuitClown + - ClothingHeadHatBeretCmo + - ClothingUniformJumpsuitCMO + - ClothingUniformJumpskirtCMO + - ClothingUniformJumpsuitCMOTurtle + - ClothingUniformJumpskirtCMOTurtle + - ClothingUniformJumpsuitDetective + - ClothingUniformJumpskirtDetective + - ClothingUniformJumpsuitEngineering + - ClothingUniformJumpskirtEngineering + - ClothingUniformJumpsuitSeniorEngineer + - ClothingUniformJumpskirtSeniorEngineer + - ClothingHeadHatHopcap + - ClothingUniformJumpsuitHoP + - ClothingUniformJumpskirtHoP + - ClothingHeadHatBeretHoS + - ClothingHeadHatHoshat + - ClothingUniformJumpsuitHoS + - ClothingUniformJumpskirtHoS + - ClothingUniformJumpsuitHoSBlue # DeltaV - alternate sec uniforms + - ClothingUniformJumpskirtHoSBlue # DeltaV - alternate sec uniforms + - ClothingUniformJumpsuitHoSGrey # DeltaV - alternate sec uniforms + - ClothingUniformJumpskirtHoSGrey # DeltaV - alternate sec uniforms + - ClothingUniformJumpsuitHosFormal + - ClothingUniformJumpskirtHosFormal + - ClothingUniformJumpsuitHoSAlt + - ClothingUniformJumpskirtHoSAlt + - ClothingUniformJumpsuitHoSBlue + - ClothingUniformJumpsuitHoSGrey + - ClothingUniformJumpsuitHoSParadeMale + - ClothingUniformJumpskirtHoSParadeMale + - ClothingUniformJumpsuitHydroponics + - ClothingUniformJumpskirtHydroponics + - ClothingUniformJumpsuitJanitor + - ClothingUniformJumpskirtJanitor + - ClothingUniformJumpsuitLawyerBlack + - ClothingUniformJumpsuitLibrarian + - ClothingUniformJumpskirtColorLightBrown + - ClothingUniformCourier # DeltaV - Courier Uniform + - ClothingUniformSkirtCourier # DeltaV - Courier Uniform + - ClothingUniformJumpsuitMantis # Nyanotrasen - Forensic Mantis + - ClothingUniformSkirtMantis # Nyanotrasen - Forensic Mantis + - ClothingHeadHatBeretSeniorPhysician + - ClothingUniformJumpsuitMedicalDoctor + - ClothingUniformJumpskirtMedicalDoctor + - ClothingUniformJumpsuitSeniorPhysician + - ClothingUniformJumpskirtSeniorPhysician + - ClothingUniformJumpsuitMime + - ClothingUniformJumpskirtMime + - ClothingUniformJumpsuitMusician + - ClothingUniformJumpsuitParamedic + - ClothingUniformJumpskirtParamedic + - ClothingUniformJumpsuitSeniorOfficer + - ClothingUniformJumpskirtSeniorOfficer + - ClothingUniformJumpsuitPrisoner + - ClothingUniformJumpskirtPrisoner + - ClothingHeadHatQMsoft + - ClothingHeadHatBeretQM + - ClothingUniformJumpsuitQM + - ClothingUniformJumpskirtQM + - ClothingUniformJumpsuitQMTurtleneck + - ClothingUniformJumpskirtQMTurtleneck + - ClothingUniformJumpsuitQMFormal + - ClothingHeadHatBeretRND + - ClothingUniformJumpsuitResearchDirector + - ClothingUniformJumpskirtResearchDirector + - ClothingUniformJumpsuitScientist + - ClothingUniformJumpskirtScientist + - ClothingUniformJumpsuitSeniorResearcher + - ClothingUniformJumpskirtSeniorResearcher + - ClothingHeadHatBeretSecurity + - ClothingUniformJumpsuitSec + - ClothingUniformJumpskirtSec + - ClothingUniformJumpsuitSecBlue # DeltaV - alternate sec uniforms + - ClothingUniformJumpskirtSecBlue # DeltaV - alternate sec uniforms + - ClothingUniformJumpsuitSecGrey # DeltaV - alternate sec uniforms + - ClothingUniformJumpskirtSecGrey # DeltaV - alternate sec uniforms + - ClothingHeadHatBeretBrigmedic + - ClothingUniformJumpsuitBrigmedic + - ClothingUniformJumpskirtBrigmedic + - ClothingHeadHatBeretWarden + - ClothingHeadHatWarden + - ClothingUniformJumpsuitWarden + - ClothingUniformJumpskirtWarden + - ClothingUniformJumpsuitWardenBlue # DeltaV - alternate sec uniforms + - ClothingUniformJumpskirtWardenBlue # DeltaV - alternate sec uniforms + - ClothingUniformJumpsuitWardenGrey # DeltaV - alternate sec uniforms + - ClothingUniformJumpskirtWardenGrey # DeltaV - alternate sec uniforms + - ClothingHeadHatParamedicsoft + # Winter outfits + - ClothingOuterWinterCap + - ClothingOuterWinterCE + - ClothingOuterWinterCMO + - ClothingOuterWinterHoP + - ClothingOuterWinterHoSUnarmored + - ClothingOuterWinterWardenUnarmored + - ClothingOuterWinterQM + - ClothingOuterWinterRD + - ClothingNeckMantleCap + - ClothingNeckMantleCE + - ClothingNeckMantleCMO + - ClothingNeckMantleHOP + - ClothingNeckMantleHOS + - ClothingNeckMantleRD + - ClothingNeckMantleQM + - ClothingOuterStasecSweater # DeltaV - added stasec sweater to uniform printer. + - ClothingOuterWinterMusician + - ClothingOuterWinterClown + - ClothingOuterWinterMime + - ClothingOuterWinterCoat + - ClothingOuterWinterJani + - ClothingOuterWinterBar + - ClothingOuterWinterChef + - ClothingOuterWinterHydro + - ClothingOuterWinterAtmos + - ClothingOuterWinterEngi + - ClothingOuterWinterCargo + - ClothingOuterWinterMiner + - ClothingOuterWinterMed + - ClothingOuterWinterPara + - ClothingOuterWinterChem + - ClothingOuterWinterGen + - ClothingOuterWinterViro + - ClothingOuterWinterSci + - ClothingOuterWinterRobo + - ClothingOuterWinterSec + # Ties + - ClothingNeckTieRed + - ClothingNeckTieDet + - ClothingNeckTieSci + # Scarfs - All scarfs avaible in winterdrobe + - ClothingNeckScarfStripedGreen + - ClothingNeckScarfStripedBlue + - ClothingNeckScarfStripedRed + - ClothingNeckScarfStripedBrown + - ClothingNeckScarfStripedLightBlue + - ClothingNeckScarfStripedOrange + - ClothingNeckScarfStripedBlack + - ClothingNeckScarfStripedPurple + # Carpets + - Carpet + - CarpetBlack + - CarpetPink + - CarpetBlue + - CarpetGreen + - CarpetOrange + - CarpetPurple + - CarpetCyan + - CarpetWhite - type: EmagLatheRecipes emagStaticRecipes: - - ClothingHeadHatCentcomcap - - ClothingHeadHatCentcom - - ClothingUniformJumpsuitCentcomAgent - - ClothingUniformJumpsuitCentcomFormal - - ClothingUniformJumpskirtCentcomFormalDress - - ClothingUniformJumpsuitCentcomOfficer - - ClothingUniformJumpsuitCentcomOfficial - - ClothingHeadHatSyndieMAA - - ClothingHeadHatSyndie - - ClothingUniformJumpsuitOperative - - ClothingUniformJumpskirtOperative - - ClothingUniformJumpsuitSyndieFormal - - ClothingUniformJumpskirtSyndieFormalDress - - ClothingHeadPyjamaSyndicateBlack - - ClothingUniformJumpsuitPyjamaSyndicateBlack - - ClothingHeadPyjamaSyndicatePink - - ClothingUniformJumpsuitPyjamaSyndicatePink - - ClothingHeadPyjamaSyndicateRed - - ClothingUniformJumpsuitPyjamaSyndicateRed - - ClothingOuterWinterCentcom - - ClothingOuterWinterSyndie - - ClothingOuterWinterSyndieCap + - ClothingHeadHatCentcomcap + - ClothingHeadHatCentcom + - ClothingUniformJumpsuitCentcomAgent + - ClothingUniformJumpsuitCentcomFormal + - ClothingUniformJumpskirtCentcomFormalDress + - ClothingUniformJumpsuitCentcomOfficer + - ClothingUniformJumpsuitCentcomOfficial + - ClothingHeadHatSyndieMAA + - ClothingHeadHatSyndie + - ClothingUniformJumpsuitOperative + - ClothingUniformJumpskirtOperative + - ClothingUniformJumpsuitSyndieFormal + - ClothingUniformJumpskirtSyndieFormalDress + - ClothingHeadPyjamaSyndicateBlack + - ClothingUniformJumpsuitPyjamaSyndicateBlack + - ClothingHeadPyjamaSyndicatePink + - ClothingUniformJumpsuitPyjamaSyndicatePink + - ClothingHeadPyjamaSyndicateRed + - ClothingUniformJumpsuitPyjamaSyndicateRed + - ClothingOuterWinterCentcom + - ClothingOuterWinterSyndie + - ClothingOuterWinterSyndieCap - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial - - Ingot + - Sheet + - RawMaterial + - Ingot - type: entity parent: BaseLathe @@ -1300,49 +1305,49 @@ name: ore processor description: It produces sheets and ingots using ores. components: - - type: Sprite - sprite: Structures/Machines/ore_processor.rsi - layers: - - state: icon - map: ["enum.LatheVisualLayers.IsRunning"] - - state: unlit - shader: unshaded - map: ["enum.PowerDeviceVisualLayers.Powered"] - - state: inserting - map: ["enum.MaterialStorageVisualLayers.Inserting"] - - state: panel - map: ["enum.WiresVisualLayers.MaintenancePanel"] - - type: Machine - board: OreProcessorMachineCircuitboard - - type: MaterialStorage - ignoreColor: true - whitelist: - tags: - - Ore - - type: Lathe - idleState: icon - runningState: building - defaultProductionAmount: 10 - staticRecipes: - - BluespaceCrystal - - NormalityCrystal - - SheetSteel - - SheetGlass1 - - SheetRGlass - - SheetPlasma1 - - SheetPGlass1 - - SheetRPGlass1 - - SheetUranium1 - - IngotGold1 - - IngotSilver1 - - MaterialBananium1 - - type: MaterialStorageMagnetPickup # Delta V - Summary: Adds magnet pull from Frontier - magnetEnabled: True - range: 0.30 # Delta V - End Magnet Pull - - type: MiningPoints # DeltaV - Source of mining points for miners - transferSound: - path: /Audio/Effects/Cargo/ping.ogg - - type: MiningPointsLathe # DeltaV + - type: Sprite + sprite: Structures/Machines/ore_processor.rsi + layers: + - state: icon + map: ["enum.LatheVisualLayers.IsRunning"] + - state: unlit + shader: unshaded + map: ["enum.PowerDeviceVisualLayers.Powered"] + - state: inserting + map: ["enum.MaterialStorageVisualLayers.Inserting"] + - state: panel + map: ["enum.WiresVisualLayers.MaintenancePanel"] + - type: Machine + board: OreProcessorMachineCircuitboard + - type: MaterialStorage + ignoreColor: true + whitelist: + tags: + - Ore + - type: Lathe + idleState: icon + runningState: building + defaultProductionAmount: 10 + staticRecipes: + - BluespaceCrystal + - NormalityCrystal + - SheetSteel + - SheetGlass1 + - SheetRGlass + - SheetPlasma1 + - SheetPGlass1 + - SheetRPGlass1 + - SheetUranium1 + - IngotGold1 + - IngotSilver1 + - MaterialBananium1 + - type: MaterialStorageMagnetPickup # Delta V - Summary: Adds magnet pull from Frontier + magnetEnabled: True + range: 0.30 # Delta V - End Magnet Pull + - type: MiningPoints # DeltaV - Source of mining points for miners + transferSound: + path: /Audio/Effects/Cargo/ping.ogg + - type: MiningPointsLathe # DeltaV - type: entity parent: OreProcessor @@ -1358,22 +1363,22 @@ materialUseMultiplier: 0.75 timeMultiplier: 0.5 staticRecipes: - - BluespaceCrystal - - NormalityCrystal - - SheetSteel - - SheetGlass1 - - SheetRGlass - - SheetPlasma1 - - SheetPGlass1 - - SheetRPGlass1 - - SheetPlasteel1 - - SheetUranium1 - - SheetUGlass1 - - SheetRUGlass1 - - IngotGold1 - - IngotSilver1 - - MaterialBananium1 - - MaterialDiamond + - BluespaceCrystal + - NormalityCrystal + - SheetSteel + - SheetGlass1 + - SheetRGlass + - SheetPlasma1 + - SheetPGlass1 + - SheetRPGlass1 + - SheetPlasteel1 + - SheetUranium1 + - SheetUGlass1 + - SheetRUGlass1 + - IngotGold1 + - IngotSilver1 + - MaterialBananium1 + - MaterialDiamond - type: entity parent: BaseLathe @@ -1445,105 +1450,105 @@ idleState: icon runningState: icon staticRecipes: - - PrizeBall - - PlushieMothRandom - - PlushieShadowkin - - PlushieMothMusician - - PlushieMothBartender - - PlushieBee - - PlushieHampter - - PlushieRouny - - PlushieLamp - - PlushieArachind - - PlushieLizard - - PlushieSpaceLizard - - PlushieSharkBlue - - PlushieSharkPink - - PlushieSharkGrey - - PlushieCarp - - PlushieMagicarp - - PlushieHolocarp - - PlushieSlime - - PlushieSnake - - ToyMouse - - ToyRubberDuck - - PlushieVox - - PlushieAtmosian - - PlushiePenguin - - PlushieHuman - - PlushieArachne - - PlushieGnome - - PlushieLoveable - - PlushieDeer - - PlushieIpc - - PlushieGrey - - PlushieRedFox - - PlushiePurpleFox - - PlushiePinkFox - - PlushieOrangeFox - - PlushieMarbleFox - - PlushieCrimsonFox - - PlushieCoffeeFox - - PlushieBlueFox - - PlushieBlackFox - - PlushieVulp - - PlushieCorgi - - PlushieGirlyCorgi - - PlushieRobotCorgi - - PlushieCatBlack - - PlushieCatGrey - - PlushieCatOrange - - PlushieCatSiames - - PlushieCatTabby - - PlushieCatTuxedo - - PlushieCatWhite - - ToyAi - - ToyIan - - BalloonNT - - BalloonCorgi - - CrayonBox - - PetRockCarrier - - PlushieXeno - - FoamCrossbow - - RevolverCapGun - - PonderingOrb - - ToyAmongPequeno - - FoamCutlass - - WhoopieCushion - - ToyHammer - - PlasticBanana - - WeaponWaterPistol - - WeaponWaterBlaster - - NewtonCradle - - SnapPopBox - - MrDips - - MrChips - - CrazyGlue - - PlushieRatvar - - PlushieNar + - PrizeBall + - PlushieMothRandom + - PlushieShadowkin + - PlushieMothMusician + - PlushieMothBartender + - PlushieBee + - PlushieHampter + - PlushieRouny + - PlushieLamp + - PlushieArachind + - PlushieLizard + - PlushieSpaceLizard + - PlushieSharkBlue + - PlushieSharkPink + - PlushieSharkGrey + - PlushieCarp + - PlushieMagicarp + - PlushieHolocarp + - PlushieSlime + - PlushieSnake + - ToyMouse + - ToyRubberDuck + - PlushieVox + - PlushieAtmosian + - PlushiePenguin + - PlushieHuman + - PlushieArachne + - PlushieGnome + - PlushieLoveable + - PlushieDeer + - PlushieIpc + - PlushieGrey + - PlushieRedFox + - PlushiePurpleFox + - PlushiePinkFox + - PlushieOrangeFox + - PlushieMarbleFox + - PlushieCrimsonFox + - PlushieCoffeeFox + - PlushieBlueFox + - PlushieBlackFox + - PlushieVulp + - PlushieCorgi + - PlushieGirlyCorgi + - PlushieRobotCorgi + - PlushieCatBlack + - PlushieCatGrey + - PlushieCatOrange + - PlushieCatSiames + - PlushieCatTabby + - PlushieCatTuxedo + - PlushieCatWhite + - ToyAi + - ToyIan + - BalloonNT + - BalloonCorgi + - CrayonBox + - PetRockCarrier + - PlushieXeno + - FoamCrossbow + - RevolverCapGun + - PonderingOrb + - ToyAmongPequeno + - FoamCutlass + - WhoopieCushion + - ToyHammer + - PlasticBanana + - WeaponWaterPistol + - WeaponWaterBlaster + - NewtonCradle + - SnapPopBox + - MrDips + - MrChips + - CrazyGlue + - PlushieRatvar + - PlushieNar - type: EmagLatheRecipes emagStaticRecipes: - - PlushieGhost - - PlushieRGBee - - PlushieRainbowCarp - - PlushieJester - - PlushieSlips - - PlushieTrystan - - PlushieAbductor - - PlushieAbductorAgent - - PlushieNuke - - ToyNuke - - FoamBlade - - BalloonSyn - - SingularityToy - - TeslaToy - - ToySword - - BwoinkHammer - - ThronglerToy + - PlushieGhost + - PlushieRGBee + - PlushieRainbowCarp + - PlushieJester + - PlushieSlips + - PlushieTrystan + - PlushieAbductor + - PlushieAbductorAgent + - PlushieNuke + - ToyNuke + - FoamBlade + - BalloonSyn + - SingularityToy + - TeslaToy + - ToySword + - BwoinkHammer + - ThronglerToy - type: MaterialStorage whitelist: tags: - - PrizeTicket + - PrizeTicket - type: entity id: MedicalBiofabricator @@ -1557,20 +1562,20 @@ layers: - state: limbgrower_idleoff map: ["enum.LatheVisualLayers.IsRunning"] -# - state: limbgrower_idleoff -# shader: unshaded -# map: ["enum.PowerDeviceVisualLayers.Powered"] -# - state: inserting -# map: ["enum.MaterialStorageVisualLayers.Inserting"] -# - state: panel -# map: ["enum.WiresVisualLayers.MaintenancePanel"] + # - state: limbgrower_idleoff + # shader: unshaded + # map: ["enum.PowerDeviceVisualLayers.Powered"] + # - state: inserting + # map: ["enum.MaterialStorageVisualLayers.Inserting"] + # - state: panel + # map: ["enum.WiresVisualLayers.MaintenancePanel"] - type: Machine board: MedicalBiofabMachineBoard - type: MaterialStorage whitelist: tags: - - Sheet - - RawMaterial + - Sheet + - RawMaterial - type: Lathe idleState: limbgrower_idleoff runningState: limbgrower_idleon @@ -1589,5 +1594,5 @@ - SynthRightHand - type: EmagLatheRecipes emagStaticRecipes: - - PizzaLeftArm - - PizzaRightArm + - PizzaLeftArm + - PizzaRightArm diff --git a/Resources/Prototypes/Procedural/salvage_loot.yml b/Resources/Prototypes/Procedural/salvage_loot.yml index 7e7ddf6ff9..1b941cd1a2 100644 --- a/Resources/Prototypes/Procedural/salvage_loot.yml +++ b/Resources/Prototypes/Procedural/salvage_loot.yml @@ -106,6 +106,10 @@ - proto: WeaponTeslaGun prob: 0.1 cost: 2 + - proto: ClothingEyesNightVisionGoggles + cost: 8 + - proto: ClothingEyesGlassesThermal + cost: 8 # Mob loot table diff --git a/Resources/Prototypes/Recipes/Lathes/devices.yml b/Resources/Prototypes/Recipes/Lathes/devices.yml index 2b0d6fa44f..56e1739817 100644 --- a/Resources/Prototypes/Recipes/Lathes/devices.yml +++ b/Resources/Prototypes/Recipes/Lathes/devices.yml @@ -234,3 +234,33 @@ Steel: 500 Glass: 400 Gold: 100 + +- type: latheRecipe + id: ClothingEyesNightVisionGoggles + result: ClothingEyesNightVisionGoggles + completetime: 2 + materials: + Steel: 200 + Glass: 100 + Silver: 100 + Gold: 100 + +- type: latheRecipe + id: ClothingEyesNightVisionDiagnosticGoggles + result: ClothingEyesNightVisionDiagnosticGoggles + completetime: 2 + materials: + Steel: 200 + Glass: 100 + Silver: 100 + Gold: 100 + +- type: latheRecipe + id: ClothingEyesThermalVisionGoggles + result: ClothingEyesThermalVisionGoggles + completetime: 2 + materials: + Steel: 200 + Glass: 100 + Silver: 100 + Gold: 100 diff --git a/Resources/Prototypes/Recipes/Lathes/medical.yml b/Resources/Prototypes/Recipes/Lathes/medical.yml index ba8f596d21..29f3fed247 100644 --- a/Resources/Prototypes/Recipes/Lathes/medical.yml +++ b/Resources/Prototypes/Recipes/Lathes/medical.yml @@ -250,4 +250,15 @@ completetime: 2 materials: Steel: 100 - Plastic: 100 \ No newline at end of file + Plastic: 100 + +- type: latheRecipe + id: ClothingEyesNightVisionMedicalGoggles + result: ClothingEyesNightVisionMedicalGoggles + completetime: 7 + materials: + Steel: 300 + Glass: 300 + Silver: 100 + Gold: 100 + Plasma: 200 diff --git a/Resources/Prototypes/Recipes/Lathes/security.yml b/Resources/Prototypes/Recipes/Lathes/security.yml index 6d00f40ce2..e80ba6bbf9 100644 --- a/Resources/Prototypes/Recipes/Lathes/security.yml +++ b/Resources/Prototypes/Recipes/Lathes/security.yml @@ -995,4 +995,14 @@ completetime: 2 materials: Plastic: 15 - Uranium: 10 \ No newline at end of file + Uranium: 10 + +- type: latheRecipe + id: ClothingEyesNightVisionSecurityGoggles + result: ClothingEyesNightVisionSecurityGoggles + completetime: 5 + materials: + Steel: 500 + Glass: 300 + Silver: 100 + Gold: 100 diff --git a/Resources/Prototypes/Research/experimental.yml b/Resources/Prototypes/Research/experimental.yml index 423ec0f84d..cf6493847a 100644 --- a/Resources/Prototypes/Research/experimental.yml +++ b/Resources/Prototypes/Research/experimental.yml @@ -167,6 +167,33 @@ - MedicalScannerMachineCircuitboard - MetempsychoticMachineCircuitboard +- type: technology + id: NightVisionTech + name: research-technology-night-vision + icon: + sprite: Clothing/Eyes/Goggles/nightvision.rsi + state: icon + discipline: Experimental + tier: 2 + cost: 10000 + recipeUnlocks: + - ClothingEyesNightVisionGoggles + - ClothingEyesNightVisionSecurityGoggles + - ClothingEyesNightVisionMedicalGoggles + - ClothingEyesNightVisionDiagnosticGoggles + +- type: technology + id: ThermalVisionTech + name: research-technology-thermal-vision + icon: + sprite: Clothing/Eyes/Goggles/thermal.rsi + state: icon + discipline: Experimental + tier: 2 + cost: 10000 + recipeUnlocks: + - ClothingEyesThermalVisionGoggles + # Tier 3 - type: technology diff --git a/Resources/Prototypes/Shaders/shaders.yml b/Resources/Prototypes/Shaders/shaders.yml index 108b9f778b..96c51d1a98 100644 --- a/Resources/Prototypes/Shaders/shaders.yml +++ b/Resources/Prototypes/Shaders/shaders.yml @@ -127,4 +127,9 @@ - type: shader id: Ethereal kind: source - path: "/Textures/Shaders/ethereal.swsl" \ No newline at end of file + path: "/Textures/Shaders/ethereal.swsl" + +- type: shader + id: NightVision + kind: source + path: "/Textures/Shaders/nightvision.swsl" diff --git a/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml b/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml index 04279d51d2..bdbb4bac46 100644 --- a/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml +++ b/Resources/Prototypes/WhiteDream/Entities/Clothing/Cult/armor.yml @@ -113,6 +113,12 @@ sprite: Clothing/Eyes/Misc/blindfold.rsi - type: FlashImmunity - type: EyeProtection + - type: NightVision + isActive: true + toggleAction: null + activateSound: null + deactivateSound: null + color: White - type: ShowHealthBars damageContainers: - Biological @@ -120,5 +126,3 @@ - type: ShowHealthIcons damageContainers: - Biological - # TODO: ADD NIGHT VISION - diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000000000000000000000000000000000..b63f30fc713a739b08ea1070559e2d3b6abf3f6d GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R53 zr;B4q#hkaZ9K8-ZNVLvZ2+=sg&GN=-_pVLq1q&5k=x)-e5L(!M$7$j6723PGU#)W5 z#>Fc#EA)*#M@!aG&dIJ7@0$-8IDayh|E%7<CwYQ0qk=ortve#FfjjM|FMBCi)v>wZ z^T!=~_Ut<L>$ULtZq`>HPbLKH)0w_^@&grxzQ>o$a;58P>sLRUl^pFqX^AoC_sf|- zH%2Ayz4<4?T9&WSOzL5xS@Ej(Dhux2*`~2dY(w&WnfHDSyN;9=|6h33dcn>2wQv7Q z)v`MDu{6kVGFS>R6sa&g@nASHkpbcsG>L}w?5&9rY>pRx?sqz^_N1~;=k%T{AC(Rm zG2dQv;OyJEoDS0?dVgL{4!!Iyw=mT1fxtA;7qilMCvVtaXE^hK>f#Gp@*kwHG1Z^^ z*m`w?m<RushHSTkx2+FU6_qc{t~p-tMU<c$E^z-~V~XkUWDxrF1sE+1p00i_>zopr E058M1eE<Le literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/equipped-EYES.png new file mode 100644 index 0000000000000000000000000000000000000000..cd6c763b334af9cd2810f9699a80005a0b30caae GIT binary patch literal 739 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGooCO|{#S9F5M?jcysy3fA0|V1k zPZ!6KiaBrZ+GYm_$~1foE-hFo5a`kE_*%guu%OULX_r;W3TJWq26^Rfd53~`kDgpQ z*e6>v^N50mYoN%&OG`A^vbtyPxmWzq$#J@{|EG+Kx&SYsS@l)(mYpr|D^}r1WthR3 zz}C<!^59kPQl+|g-?upxuM3U+o|2ra=EGrYzx)Y<xL&NL+?{*e%g=2;bm-LY4OMS* zcYMv*x9i{6)9K0aEf&u|-gW!&Crx{c^<e?|)0h6v7oI&oI;bJ-bI!-wa)Gj4?yFB9 z{wx3V(R|MAa=xkEhAZ;kf4A?;6@2xx@MHO|=B&DyKkeq*4}ZOSSa@cAQJ$Si-~1nM zw;kNR{-Jli^^$Y`Tmkoge)^SU_`4(`R!=@aQ2hVp_Ol!Rf4Tbk_S}Foa@kX+@4otH z$B)Ug;_FiOo}c)SP3BKp=W<(Hd-ZtMgFFnz3}+Y&m=BQ5$jM?lGWYazG5w_HEN*k{ zMHye^KK{7IA-j3D{CV9oF@C>qJ^9SI^ZW7tZ~s)iJ6Ozp<xfSPV(6!z4R?M&E<W4e zy?M{MbNS^}C%;>Y?r&ODX!rS;WqlUYpY<d=1nzttONT%A|GqbiPpme4`E#c#`;Ggz zc?-MCRw{@!&YK@y)bMLB)1n{CIO}g|+n;N#EPP)Q5mDK9_Pnm8!yRV%`xcE>+5+P9 p=YUjt?g>1!-^SjYoDg~=zRyZNy+M4FAu!c4c)I$ztaD0e0swobG`0W$ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..8bcf7105c2ba36f66beaadf560b76a9e095f924d GIT binary patch literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCil21AIbU?VJO24Q(VOltLquw`VCWjnv%JYH?##2vA-%W$|wy#aR;M7yKUz z7=ryPgg}A?9+AZi419+{nDKc2iWHz=x2KC^h=u>#37&k+iaacL-zNV2-+ei4zDJ<Q zQ>)Sg7qp~XuIk%3Jr6%9C9(X0nn1$i0KY~V>9=9~Sid|BymH<$dA6Q|OX1S@O)G`n z`d78ixbDkXrrP|x``z2M%J-f0R4;o?D*hsWD6~k)WQhu=vS@I^?xt5)|0Ev}`lWu9 mpGBE_+J{c}7k{*`{9<~wS!TBVg?x}(89ZJ6T-G@yGywp$pK%)i literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..04fff80e4df5f5bfe6b670cf9f68368e945a073a GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`NC)_YxB_Vj z2_;=a8$0KKGkM1Mnl&CTboqZO@&Et-xdI8oKxxjBAirP+ARi3OnPy)B3Z{FyIEF;D zzP)yk?|=dabHI`X|Ne{TZ+A86ev>k7-|S;TdqP_}Ufh?N@s8pCxopE;;RRtO^-kZd z{2adSo4^0Qd9w|pXvFOLue+Qj#rWKnl$5MImaWkFymHZ|6YAYw9~Q1S!l|RxzKdax XEW^!ZZ4OI-Rx)_H`njxgN@xNA_h(vM literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..d80344136fca4bab3f4e25ae0dc66b77c694904f GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`NC)_YxB_Vj z2_;=a8$0KKGkM1Mnl&CTboqZO@&Et-xdI8oKxxjBAirP+ARi3OnPy)B3Z{6vIEF;D zzP)D6cR+!M`GSD@fxrHD`gG+r6SJS1Cr`ep<l^$5x#GuK=Hj@u83yZ*mgG2ml{<T% z_wMV}j;z<7t^Rg_=hL46?;rmTGYAR_%0}h(_gwwvVb*h7Iro;_Pw5@Db}yKYTx7V( T+f$|jw2#5l)z4*}Q$iB}hdx=4 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/meta.json new file mode 100644 index 0000000000..2b65c68fe8 --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/diagnostic_nightvision.rsi/meta.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-EYES", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ] + ] + }, + { + "name": "equipped-EYES-off", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000000000000000000000000000000000..b63f30fc713a739b08ea1070559e2d3b6abf3f6d GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R53 zr;B4q#hkaZ9K8-ZNVLvZ2+=sg&GN=-_pVLq1q&5k=x)-e5L(!M$7$j6723PGU#)W5 z#>Fc#EA)*#M@!aG&dIJ7@0$-8IDayh|E%7<CwYQ0qk=ortve#FfjjM|FMBCi)v>wZ z^T!=~_Ut<L>$ULtZq`>HPbLKH)0w_^@&grxzQ>o$a;58P>sLRUl^pFqX^AoC_sf|- zH%2Ayz4<4?T9&WSOzL5xS@Ej(Dhux2*`~2dY(w&WnfHDSyN;9=|6h33dcn>2wQv7Q z)v`MDu{6kVGFS>R6sa&g@nASHkpbcsG>L}w?5&9rY>pRx?sqz^_N1~;=k%T{AC(Rm zG2dQv;OyJEoDS0?dVgL{4!!Iyw=mT1fxtA;7qilMCvVtaXE^hK>f#Gp@*kwHG1Z^^ z*m`w?m<RushHSTkx2+FU6_qc{t~p-tMU<c$E^z-~V~XkUWDxrF1sE+1p00i_>zopr E058M1eE<Le literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/equipped-EYES.png new file mode 100644 index 0000000000000000000000000000000000000000..a6513302b9f52f881d262e2c8652ca37e9786a72 GIT binary patch literal 733 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGooCO|{#S9F5M?jcysy3fA0|QgP zr;B4q#hkZy&wGUg${hHpEOKMnoPP%*I213c2{lcd*e$lM=>ubw%a%8SRV<w|LbnuM z-o}1H>``369S$#*7XdTVcwJeySGVP{uof;WesA9Xp4}yl?V0)a8riu=ZSN^NYA_$* zVK8RMS-@~T|Blw9@9)lqW%T=hc^@59VJ0NybpE`i<$<T#*Si)M&owYC)!MVGz%=Ln zOm20#yKnFB-?b;?x6%>I$>-;{t(|)JLu#4Yk?Wi4-<KtSF81cSkmoNZ&-d-nzwV2l zuI2r>^IPQH)NcE46?QpOx&`Immzifrw`A4D{Q3IHv_|*&@uT(i|2=<vdyuC5`FziO z-nEmL*H`DfzkB;bTDtX;bN*Zbwm*NKEBRQp@9O^8eG8QI{+;gKdaM3do!p%_K89s` zM8oRO^mpIC^5>KG>teny<`1qntKR$e<<FcyA}q!XXBZ41{v?sHVIil$`IvnbnOwKk z4PN|N$PqBtOzu0cPM?f*$mhx5*`L_i|BHVfUi^4pV{7}~faUwo_Wu?OQhm>VqPDJP z-Os{8H|xtU{_OBqx*G6@tLs(G^Q)Hi8B8)^Yz>kOX$&(M6G&!ElTrBh{@>TFqHkh+ zs_ODiGFHj+JK4DI4iV^fIDcN((xG&1gGQXmL;vTeuJ1JX^5@PZ<|EDh;&}_caa{n$ r3g3kztwR5v|J?aglY?{z+-K~|lbbFZd?FT@UKu=H{an^LB{Ts5{kAk( literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..52522c080e85e6a1a893ba5bbd642663d48252d5 GIT binary patch literal 306 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCil21AIbU?VJO24Q(VOltLqu6?*rG<*v6}zd!KWS)hE+0@uAjinAohFZe$c zFa-No2!R9(JR*x382Ao@Fyrz36)8Z$Zci7-5DWje6Fm8t6?s_hzD@l3zx#69e2+kp zr&gs0E@(-&T-CR6dLDjIN@Do~HGzc30e+1#(r?4|v3_|Nc;&oh@@zc^m%^p*n^p?D z^{;B3aov}(OttxW_q(@imG3+0sb2P)RQyH$P-u~o$r2S#Wzpb--A%8q{z*O{^h^CH mKZ`Q=v=5!`FaBs>`Ni~Vv&?M!3;7_oGI+ZBxvX<aXaWEt?{S|1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..bccfe98d09b26adf2d2164d62bdacb7c4c5a580a GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`NC)_YxB_Vj z2_;=a8$0I!m8JJ&``<WUe3J10`~Uy{Tc*{021;|51o;Is0Qq2G&NTZ9P%z!o#W5tJ z_3gETd<PUbm;;t9`1fBtf4i$m_nVYy`(_^#+7sH+@#4PBjCTz8&t)6-3NHvNsdxHr z<>&Bq-~9de&6{l)MI&a{f8FIQDaPlnq@-l!v22CT=aq{#olx)g`mk`t5l$Vg_FW8n XWEpNQYjaovw35No)z4*}Q$iB}O9Nb- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..f9c17318b4ee395b57eb4efab8c1756143523406 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`NC)_YxB_Vj z2_;=a8$0I!m8JJ&``<WUe3J10`~Uy{Tc*{021;|51o;Is0Qq2G&NTZ9P%y>Q#W5tJ z_3brlz5@z8%ohaI5B&AN)2A!1nV9|5JbCg(B^Q_f%oRV@G8f0K%`jMhv?RyjtK8Z9 zymw!(c4WQwZ1uMbJfHpqc>nl!m_blbP&O*Jzvt>V53`=z%DK1XeoF7KwR^#I<RZgO T-kvfQpnVLUu6{1-oD!M<)C*hB literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/meta.json new file mode 100644 index 0000000000..2b65c68fe8 --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/medical_nightvision.rsi/meta.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-EYES", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ] + ] + }, + { + "name": "equipped-EYES-off", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000000000000000000000000000000000..5aaa7e580ce25f46a4b1cab0117891eb88c39378 GIT binary patch literal 477 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R5f zr;B4q#hkaZHhLWn5NZ1_Z52^aP%t^^nP1<fP^(>C55(qv;yP#7H2K0>|A0A0-$kw5 z6(>6%Pgx<iyyJn=ox^{NPyAev{o_YbasI=33Q7{jlF<!kUobnkEUMeTbMKkS_5K_G zEskfr$Y>XLE_MH0&uDSA^2;w6CjS(dyMFz}EUxa^+^kEtwA@JEP`!Klx|IbtQZ3@* z5@)S%nsj@A$JhBsxO_f`YMd5g_+WDX#%~@If5u~W1}lOWWNV#0zSJ?Bb%S5y!8Ht% z^X0jgJ8*cK|Fu8z>$kn*HU??_17Zn}8E-J<*f2yl^s*;#Z}`ZN!)PYgpnKpL^9I%% z6%0s<3^UFs&){3oxRc@5XO3q~Up{lpKfhiJ$jocIwQIsZsdpPr7yJrv=bXXKz+nHG zqg>iDa?f$jjC1TumaXOP{I*>xVgFoCg9WcYUXhurTPl}Q$oeHe{_y=LTmjX;11wnA iBp0z}JR-(1pLx$LUT)lx{@o84UkskEelF{r5}E*W3du_V literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/equipped-EYES.png new file mode 100644 index 0000000000000000000000000000000000000000..dc4d9e775b88d077723af637c09a002db51a84a7 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|TRv zr;B4q#hkad7J4}+%CvoSkGx>ER!MJtRLSXk4T)z~B*Y5rnRRfJd4g4f^o^X;&51@C zg1nC|>ieYq`}g$ee>?Gl2Y>bz^EL04os`ZnNu0~T?&a3<{?ocGpM(Rd8>hbvy#2C% zb8*43r#~3Cu3|iOw!B^Y-ZS3ji-!BxGkAqEPnoQ5{(tL@XTNIZpX3b+Wq$K5tzLfb z^~%diVK3Y_*zg)SuMB87|D0{>AqKl?;cuV4UJxYF@aX0ALk!>Aw0B<C|2bXZq%i{? zbjbCM;1WiqNz5Jhlut0O`Ofiv`*$9<<yWg&EE~ScEiBnskrY&EYuWI>y5;aI+a{R< zX@b=(kqt$^1nZBVW^!2@sKU6&Z@&tsz)sgmEFE(mc`&)8F@U^^iM}(RDDYh>5>Rvo P7}yM+u6{1-oD!M<%d4uQ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..e0aaa8ae9aa3c281302ad194b0a2b7b1a30f4d21 GIT binary patch literal 298 zcmV+_0oDGAP)<h;3K|Lk000e1NJLTq001BW001Be0{{R3M5Kzw0000#P)t-sz`(#b zI4Ea@PfTh=NLVoM#U1OLAD3x4l8#V|Q564XX8$WI_uyhkC?Nj;0BA@kcsLOMTU*rt z0KotN#eNj*0K2vT0004WQchC<K<3zH0001)Nkl<ZIE~ei%MODu3<OQVYbeg6DgXbi zP0}K9aEL??Rrip~jJ;Nf{xrkT5pH8g7^km=-VM`bfdMd)%!@SmeO;jtg|oO8?maln z63qpbd(TidEAXg1u`q(Up>qE$6}EuNQ7bkzjg&GExgwG!?EerK=fwVrJ$Az(jfTU# wGaKHE%iEaGi#ILUo+rw;(+U14XZUA%0jaqLSXS!5lK=n!07*qoM6N<$f=#%29{>OV literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json new file mode 100644 index 0000000000..ad770fb0f8 --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/monocle_thermal.rsi/meta.json @@ -0,0 +1,22 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-EYES", + "directions": 4 + }, + { + "name": "equipped-EYES-off", + "directions": 4 + }, + { + "name": "icon" + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000000000000000000000000000000000..b63f30fc713a739b08ea1070559e2d3b6abf3f6d GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R53 zr;B4q#hkaZ9K8-ZNVLvZ2+=sg&GN=-_pVLq1q&5k=x)-e5L(!M$7$j6723PGU#)W5 z#>Fc#EA)*#M@!aG&dIJ7@0$-8IDayh|E%7<CwYQ0qk=ortve#FfjjM|FMBCi)v>wZ z^T!=~_Ut<L>$ULtZq`>HPbLKH)0w_^@&grxzQ>o$a;58P>sLRUl^pFqX^AoC_sf|- zH%2Ayz4<4?T9&WSOzL5xS@Ej(Dhux2*`~2dY(w&WnfHDSyN;9=|6h33dcn>2wQv7Q z)v`MDu{6kVGFS>R6sa&g@nASHkpbcsG>L}w?5&9rY>pRx?sqz^_N1~;=k%T{AC(Rm zG2dQv;OyJEoDS0?dVgL{4!!Iyw=mT1fxtA;7qilMCvVtaXE^hK>f#Gp@*kwHG1Z^^ z*m`w?m<RushHSTkx2+FU6_qc{t~p-tMU<c$E^z-~V~XkUWDxrF1sE+1p00i_>zopr E058M1eE<Le literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES.png new file mode 100644 index 0000000000000000000000000000000000000000..199c44122bb0619dec7fbece5484729d8e10d3f2 GIT binary patch literal 719 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGooCO|{#S9F5M?jcysy3fA0|Qf| zr;B4q#hkZy?Y*M|B^o}O2W?P_@Y*i4qS)*|&(yNl-wTAj*u3pMJDmk~ecim|#=#d? ztfNmvEn&Q<bFWHC{7BNrmg>(56?F=2Egz13RyoeI)BL=X2J-<P24jYtWejuumhY*L z4HQ{BL%6DL?k47(yqqbw<EJN8zD}*V|3`LhzjvTqod0@zOWWKU!Ao<>)z>E(+4Mi& z``V*l_3ymve|Dv^z9`%Mw14qp2|nd*k7}x7qyA(GUV8htZvRHt70;F*_3u~oEMBed zc=@ya`}foLpF8*H{L!QK+xJGq_L{$b{qgAoh3vYphFATszgRt0$cz7$X)W>QUfjM) zHn+*EeunSf9vxt6U-fzB{M|eH4KM$1*!^2~<M&ISUq9KcFoQ9HtwE9@je&HAu1`bR z?$~u}*Cx&vom=|PiN)hMTa2&T$A7oIL~h^t!*3t&eo<@FyV;@(I?o?}zM_x6!lU#I zgQ>mj_6geB4wJioOZ>O~p_I#bo`3qqpU10zeil#S_HtVs`1;xNlV{^+F!)a;-)*Vv z9_pv%`j1~e@5I(HFMj*XIL4O_ET3xre)(gS${z7^i_rAFr(ZtZ!tp)7e)nc!-+71a zWm^nyJbiXu*V^INqLwYJBK$dwzVi>;*CsMs?$5i)_+t9slT10*<v;Z}NDr)fMxjOD V|NVJkB?L^N44$rjF6*2UngFrmDX0Jd literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bf770f70f8969ba008b8e4d2b2687cd602520849 GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv=>VS)*8>L**f|I28rn!mD1}BQ zi!9KSs<MkbXu9N?TksQmL!dIok|4ie28U-i(tw;?PZ!4!jfu$#5?Kxb0RfCN^Oxl0 z<aDSU%3)CAd03;ipvu*eHEoBGpt!oSxVSsp42H#vCTv)|sAIxJMxXYKMP4gAH%~IW z)6rFS*zCK)4kor`9Y)HE{VK{WOBZ#Xa&tR%>clyt1T!<U1c^rs3`h9*yo_ewH3wSE N;OXk;vd$@?2>`qZMy~(> literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..995b37471b38d3dbcef1e91888c4ed58239b2d29 GIT binary patch literal 385 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVD#{GaSW+oe0ytS)}a8AV;?79 zTDrUS+Q#IAP0lZsthSWoxw9WU{v!DYhg1B5>~=oQcTG*fg=?A>V|Tglb68?=^-eYO z#s71CKGf`de*CKH@t#%&CJu!LG~z%Yr^~+Gxjp%tUSD5r^X>EFvzGg-|4-4(F^q^Z znq&F=?YHVp)rX(%y813<x&HodrPFWu&x+XibJNUK;eDT`tp2kpa_ZI0$x&9Ee}9;1 zcQ3HIs_m{F@;T1GVaoH%o>!_Xwq1POdh~sI^pF2$AHG+EHSOm8{6o=W#kb{d*H*u; zS^a$a*}YN)E5Bwqi(qjds&fl0m=50K3wI1TBe%n+zhXZBibaLCi>F;y-d9lL*MH>| rOUn$om3QkIfC6>yPr#m?k;`yJy1Mbjqo4nPLCfIj>gTe~DWM4fU$Cme literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..c3efa67f837fbc9cc5851d2ec09ce705d1bc7652 GIT binary patch literal 410 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEU`+IMaSW+oe0ytS)}a82qaV*- zI(paX^^L%Q0*zQNYmK?LH#R>={vl!4^nkmhv>;%Yc+?A(J}Zr~mvWYk+>A$OyLd19 z_g={GyV~<VJbF9Rcvu7+7#Pusb6yJ`WdFWV!D;e*x#`x_->0qbm+$>=7ys&Nlh=Q< zJ!dO^ygOUHQG3Fv|Ldli?To*f`uzN=uk8ZK*JtUR*PLIZwZ3S#ynd*fZk_a9R-vmW zsw=~TEKR3G|E%SZ_IKFwA|qhSyU$x~P2=DG{(Shi#ZG}pxv9aGQ=@<0?f6!7|K*OY zQ`GdIZ7#TX)%Rz{DfjNVPVE`;cFGM5OdJaMh-%K9T5Fl-U7qDhM|P%(6`OryKf<{% z{5<>ZPQmAGm2;F^RICj@-Ef>4WXl*eqeQ*!+LOX1|M&}1Z*)FXu}}gTEi?as)UL{^ V#;<v%vw>mG;OXk;vd$@?2>@%NsSE%B literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json new file mode 100644 index 0000000000..987b20b9af --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/nightvision.rsi/meta.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-EYES", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ] + ] + }, + { + "name": "equipped-EYES-off", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000000000000000000000000000000000..b63f30fc713a739b08ea1070559e2d3b6abf3f6d GIT binary patch literal 452 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fA0|R53 zr;B4q#hkaZ9K8-ZNVLvZ2+=sg&GN=-_pVLq1q&5k=x)-e5L(!M$7$j6723PGU#)W5 z#>Fc#EA)*#M@!aG&dIJ7@0$-8IDayh|E%7<CwYQ0qk=ortve#FfjjM|FMBCi)v>wZ z^T!=~_Ut<L>$ULtZq`>HPbLKH)0w_^@&grxzQ>o$a;58P>sLRUl^pFqX^AoC_sf|- zH%2Ayz4<4?T9&WSOzL5xS@Ej(Dhux2*`~2dY(w&WnfHDSyN;9=|6h33dcn>2wQv7Q z)v`MDu{6kVGFS>R6sa&g@nASHkpbcsG>L}w?5&9rY>pRx?sqz^_N1~;=k%T{AC(Rm zG2dQv;OyJEoDS0?dVgL{4!!Iyw=mT1fxtA;7qilMCvVtaXE^hK>f#Gp@*kwHG1Z^^ z*m`w?m<RushHSTkx2+FU6_qc{t~p-tMU<c$E^z-~V~XkUWDxrF1sE+1p00i_>zopr E058M1eE<Le literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/equipped-EYES.png new file mode 100644 index 0000000000000000000000000000000000000000..0e156402bc5eff7cf1ce82fc23b110afd3b4de8b GIT binary patch literal 704 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGooCO|{#S9F5M?jcysy3fA0|Qf$ zr;B4q#hkZy9lb*W8Qec=?<f#Xl<0Nre(5N!Iibbu;xhdU?r*uz9Lf+j;b@(m;3RRP zQ7P9;O0nwh?-!kcv#sxb_v3xeo~U*9{=1VUALnhBYcXOt!(hOCU>etl7au2E?VTg* ze>mZH*ynBOi<f6FRR2=vd6V(oJG(XC{g*4=;ZLmC!tr}?=8futt(CuDH$R^%@3cAo z=-<LS_Wi#<Cf|&HpTB?08iwYBZ-0IMqtbHH?enikZ?bLvNI!hK-9OPH-sJuKv-ir% zIK+QH?o~UGB=CLvqkr8y;=gXLul=2NW6!p)w}1QD)p5M<zu$W2?;3`;&HSt9{yO#j z?2lg+x^)hvR(}?sOnm<TSKZC~y}#M*zpwuH?eY3W{X4H*Z#Z2a`RMYzd-^BeHzlw& zNHU}`%wQy)vCW9_($v%CW$zNdbL#BgFT{B3^s&o(nU1`Se-mC<KjnM+=J)&>wY7il zzuUfh`upeu-;V$Mee*l}lzn#luGCr0NffO&X8b<A;>^?^`Wi1k8LvP6cPi(H{m>BR zVK9aSHSvrMZx~*tf8TFe(;D0D`F{E|*#~b<+br?kvUrvSOV$1@Zy4U5f8~E^vf7`Q zQ9t$gm({Lg$^wbL{BT*=Jm5PQkN?fTmo_nf`RKy=|5IINoj42WE|71vlDK?Fdg*## Pl4S67^>bP0l+XkKzim56 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..80ae69d53bada105ba124e8045888bb0cc92f94b GIT binary patch literal 505 zcmV<V0S5kwP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800001b5ch_0Itp) z=>Px$vq?ljR9J=Wls`x#K@^8SgJ6*&<Ud&s16pWt9CDytB+FfM-C>m?l@?Zl_Ff8e zy@BhkEG+E=K@y0akb^`a5scXEs=Myispg7vf*Z5DL5nMHzG|5_^WN|G-p+tRp-}wq zOa%oSjfQWmQmu(`>#k$-YyYPwZ?RbP%`+V&U>v2=x=80V04&q+6*o@Ey9Ja=>jDT( zEcaEaHL>?&J8<trBiP#674byM6O#@{V=tY{Zgqa2`_>5+P^s2LHk0(Mce_0-(;%JG zSX^9qb?)Ku3F&-mXIH#g!0MOp!gXw<L-~Du)uy29JUl+}GM>#Ohig)xWg6Vy_kHU` zf&|Fq<#vnZ%Nyc}6s}`a&~-M?jw!cWSf)WXlO&r-(yZ5U9ea4*G7XN74gyVv{)ClI zll=Su9fxlngK{KBJdq+lH{j$}qoA*_d3H=+MCpE*9Xba>i-&dqpf92T-1TQTnfja9 zb!;}GUpduc0OaQe^dd3Oxk-vcNCzy_;PBIDXt(G^VqAQjC;dEM>9kp|vzA)|4bR*s vCG-}Idw8TfdEZAFbszK+DijKZ;-B*e%6-anRB9Xj00000NkvXXu0mjf^S9-c literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..02b00b1591eea9b0c895e3a2a40ac644df2afe07 GIT binary patch literal 233 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`NC)_YxB_Vj z2_;=a8$0KKVsYJBqH=r0gl}8${Qv)7Ep<;nP@1zO$S;@y$Oi*+rrB43g6W<vjv*1P zZ?7HXJD|Y99I#};zyIR-+g(k%-=s|2H~W~-p3s(#7x!gmykoe3F59qIctKc6z0-Fq zKZmdT=I_66-fY7t8Zo>6>n>+WF+O)CB_%75Wh-<(uUxe0gnGBvhlMMSaO!Ba?_$^^ W%W!j9o5K>Il?<M)elF{r5}E*AzE|P^ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..6ba4b3e64ee173d9d8cc52d6da70949729f3c778 GIT binary patch literal 309 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+ru!7>k44ofy`glX(f`u%tWsIx;Y9 z?C1WI$O`032l#}z+BpZz5|z_6v?&(Xm5@-{BPRU+|Nq+-JV5!=^5?$*DbA80zhIys z7%&9;R|tUw3p^r=85sBugD~Uq{1quc!9GtH$B+p3x6`bJ4k&Q23i^Ker`{*=*xdNh z63>s@0=RB0mld&NVq{@r5#X>F5||mY)-a@ae%B4Fb6>g2rezoG<15a3zWAYuyXUlK z=E(RtTqmNtf>-*eTYvqX|LTC>J8c061qX&k29ODp7u>Zi`SSPfyM@Ua`Q`kw$>p1? oU(9$^Hht<@`ST7RA_Wey_TQ^(H(6x73g|KhPgg&ebxsLQ0D|>x_W%F@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json new file mode 100644 index 0000000000..2b65c68fe8 --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/security_nightvision.rsi/meta.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/BlueMoon-Labs/MOLOT-BlueMoon-Station/blob/master/icons/obj/clothing/glasses.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "equipped-EYES", + "directions": 4, + "delays": [ + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ], + [ + 0.1, + 0.1 + ] + ] + }, + { + "name": "equipped-EYES-off", + "directions": 4 + }, + { + "name": "icon" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png new file mode 100644 index 0000000000000000000000000000000000000000..9bef0a8c05f0ca0e150da1e9b81aebb13b8dadd7 GIT binary patch literal 524 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=1yvytB`&GO$wiq3C7Jno3=9=> zRF7M8H5dr69<ZwJYXA0V{arVuNlxzt3QQU8C+Jkwaxb4Pbl74iYgySBk5eD_9eA;N z)*0z=-h>j}nF@vcztmTUGcLZM@bZhhV_L6f^To+GC)ukuUkvCh+_+)$V*aYbQomVd z3Im<M81Cuf7*a9k?X107hXQ2W{|lP8+chbLd=m5e%}~cPQ{<P5>?al0?L61)98Q1L z)#F>sdhq7KjZ2(YT=?OrFX4Ca{>(`+c|}Fb9R5u#KDTbBl&-^KhAj-eOf7jQHtF#1 z{C9t4d)|k8(ccUI#O#}Q&3d|_)#1}Q)9TlSZM*xH?^>P|Z}zsRYx2E|CRDSr8yMfN zS#$PGj%DfLb4$XH>{x5(@Z$8}R*(CP;eAXOm`u1FxG!ihz-%B|faSlOKEG4N`?p{J z=Kd?*S5b7>yJPO6-xqG5SG%+QJzJs7|0w@SOK$ya_{4n4<g9u8(T_z@Z%@w_ZTxP` i>0aY>v4(UjYnXdl9N2msQpJGL%i!ti=d#Wzp$Py!<<~I) literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/icon.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3d5f8ef9b65ccfe2ed64fbc2d10c725c5fad51d2 GIT binary patch literal 510 zcmV<a0RjGrP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F800009a7bBm000ie z000ie0hKEb8vp<Sut`KgRCt_YIdI?r!$1NSh5-hn4j6U7r~`(d1B!VZ{`Y=4z({ik z)HP4|k1m#Y_$0$GZ5xLBS0n!`?pjWiBS>~YL`2emQAsH<`}Ol@aPemfRt!=?oD8Bs z_Iz7&h65FI{vYZ{A?OH_900N$L{IK-!7zBCTjc*0Kwsw;*8IP9^%BFvci$Q2H-Gux zR9k@85kxs)>deJpi)|epQTQ;>tT~TiYH$(*15|v)ig}D6#~!fX$nb)p0I%5u9RQ2# z_wU}|RFn3~A4t9htI^O<XMi~(6c`$l?eLqA*8w2Q4|e*3+277zV)%3B3YdNU)J+E6 ztY8K%Lp``02zahwV0iiJEj*ndrxCIo0E)|R=jGuR!z>2@kk}_Q@h%{p%Af$|gDeMy z0!0o0fy>j@!r35SKRIv_%m&diTpTEBLGldK&Vt#*SWeIZAVX4t@pvN37|h?^vy>s{ zBOhFT3d1J`R|kCt784yX-vVekwj4sX13&;|pfWW3iAriPteA<g0Gus1-e(X!^%Jg^ zfkZ%5HpCU2#0E3P4xl9%b-<_tMjbHffKdkk0DP~m!U$LZ-v9sr07*qoM6N<$f;E)T A`v3p{ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-left.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..bf67e35d4024bc03b3f3ed80f2cd847027bb8476 GIT binary patch literal 327 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVC3_3aSW-L^Y)g#UrT~O+sA{4 z+?*XV*W|2Ov$Ls0WVXX@^A8&L4oK-Y$Tce82$b;V+GJ*y6Q}g|n5@qUoBb^<O_ekI z>v@xb1_FVDPUrWmc)9n>XTSEY{<m-Iw%qgQ^t+O(cKzD2{&4@>`*wGJ@BP4Y`g2W{ z@4IJdS4$$NUAC-T6)(0v=IQU!-9o0C?{~LXu3q)Z-*<K1$q8G<-v46pxL)cRd;Rmi zcQN<BfBv^pJjv3pbd~s1&Fj0vBUP`|-M^bCp>x{E`|_1p%PfD!?BaNAaVzNkM_opU zgBm^@;?~~-AyO{a$J#D)zw?~=-)Xhw?t3cy`g2}!MkH6I6yIl9=FY(2cjgU8R89XJ TA;as_K-xWB{an^LB{Ts51B{c1 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..4ede078291d212cabdf7a4d5731153a85f0963c1 GIT binary patch literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7T#lEVC3<1aSW-L^Y)gZUvq-Qv5(vN zVoi8@dM9^JHV&WAyO49uUdR4NymLM>y%4l$l5Q?cQFwc)^;u?9W)7>Pko}kc-ZOqE zoOia1sNe;f2?Rgl(_(WE8?3oLJ^$Ojn>Szm+*<rMzk1L9wc_E$)i>gzr=`xV)W7BD z7WgIbUuIddqrert>EU)yL*kx>{X1yh{dh-R<ULuw!o4-lmP=2rlgoYWU*=Hk7%0-N zuIV``V&3y}`SpQ*Peq@G^IO0Adh=#bR7~j9si~R!-q*j5E$ZDD>sxpJvySiOlcf;n zfXIf6JpYn!l?xpXT`Q~<FIg&-yy}-<(w~*G3CwDSF<&2-Pxs_uIN&n#rrcqVt~vcC Q4cQ=Fp00i_>zopr06im!bN~PV literal 0 HcmV?d00001 diff --git a/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json new file mode 100644 index 0000000000..205508acfa --- /dev/null +++ b/Resources/Textures/Clothing/Eyes/Goggles/thermal.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation", + "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/Shaders/nightvision.swsl b/Resources/Textures/Shaders/nightvision.swsl new file mode 100644 index 0000000000..8a3e7706ad --- /dev/null +++ b/Resources/Textures/Shaders/nightvision.swsl @@ -0,0 +1,38 @@ +light_mode unshaded; + +uniform sampler2D SCREEN_TEXTURE; +uniform highp vec3 tint; // Colour of the tint +uniform highp float luminance_threshold; // number between 0 and 1 +uniform highp float noise_amount; // number between 0 and 1 + +lowp float rand (lowp vec2 n) { + return 0.5 + 0.5 * fract (sin (dot (n.xy, vec2 (12.9898, 78.233)))* 43758.5453); +} + +void fragment() { + + highp vec4 color = zTextureSpec(SCREEN_TEXTURE, FRAGCOORD.xy * SCREEN_PIXEL_SIZE); + + // convert color to grayscale using luminance + highp float grey = dot(color.rgb, vec3(0.298, 0.5882, 0.1137)); + + // calculate local threshold + highp float threshold = grey * luminance_threshold; + + // amplify low luminance parts + if (grey < threshold) { + grey += (threshold - grey) * 0.5; + if (grey > 1.0) { + grey = 1.0; + } + } + + // apply night vision color tint + color.rgb = mix(color.rgb, tint, grey); + + // add some noise for realism + lowp float noise = rand(FRAGCOORD.xy + TIME) * noise_amount / 10.0; + color.rgb += noise; + + COLOR = color; +} From 83fa9239d8b6a244e1c5e974cd40351d2435f419 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com> Date: Fri, 10 Jan 2025 04:42:01 +0000 Subject: [PATCH 29/38] Automatic Changelog Update (#1462) --- Resources/Changelog/Changelog.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 40eee58c48..7f0e09adcb 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9500,3 +9500,20 @@ Entries: id: 6656 time: '2025-01-10T02:27:50.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1471 +- author: Spatison + changes: + - type: Add + message: Added night vision goggle + - type: Add + message: Added thermal vision goggle + - type: Add + message: Deathsquad helmet now grants night and thermal vision. + - type: Add + message: Ninja visor now grants night vision. + - type: Tweak + message: Some animals have gained night vision. + - type: Tweak + message: Xenos have gained night vision. + id: 6657 + time: '2025-01-10T04:41:32.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1462 From b38b7f992504496a125ba2f96fb1fe6bf4f09f1c Mon Sep 17 00:00:00 2001 From: VMSolidus <evilexecutive@gmail.com> Date: Fri, 10 Jan 2025 00:14:46 -0500 Subject: [PATCH 30/38] CyberEyes Night Vision And Thermal Vision Modules (#1477) # Description Thanks to @Spatison for the Night Vision PR. This PR adds traits for CyberEyes users that enables the use of either the Night Vision ability, or the Thermal Vision ability. The former lets you see in the dark (It can be toggled on and off at will). The latter lets you see living creatures through walls (for brief pulses). The latter is on the quite expensive side of things (12 trait points in total if you include the requirement that you first have CyberEyes). # TODO <details><summary><h1>Media</h1></summary> <p>  Sorry the pulse for thermographic was too short for me to be able to screenshot it. But it does work! </p> </details> # Changelog :cl: - add: Added 2 new traits related to CyberEyes. Light Amplification Module, and Thermographic Scanner Module. --- Resources/Locale/en-US/traits/misc.ftl | 2 ++ Resources/Locale/en-US/traits/traits.ftl | 9 ++++++ Resources/Prototypes/Traits/physical.yml | 41 ++++++++++++++++++++++++ 3 files changed, 52 insertions(+) diff --git a/Resources/Locale/en-US/traits/misc.ftl b/Resources/Locale/en-US/traits/misc.ftl index 9a17c3afc4..13eae98512 100644 --- a/Resources/Locale/en-US/traits/misc.ftl +++ b/Resources/Locale/en-US/traits/misc.ftl @@ -1,3 +1,5 @@ examine-cybereyes-message = {CAPITALIZE(POSS-ADJ($entity))} eyes shine with a faint inner light. examine-dermal-armor-message = {CAPITALIZE(POSS-ADJ($entity))} skin seems to be made of a sturdy, yet flexible plastic. examine-bionic-arm-message = {CAPITALIZE(POSS-ADJ($entity))} limbs emit an ever present faint chirp of servomotors. +examine-thermal-vision-message = {CAPITALIZE(POSS-ADJ($entity))} eyes periodically pulse with a menacing red glare. + diff --git a/Resources/Locale/en-US/traits/traits.ftl b/Resources/Locale/en-US/traits/traits.ftl index 37de6238d7..92fa6f74a2 100644 --- a/Resources/Locale/en-US/traits/traits.ftl +++ b/Resources/Locale/en-US/traits/traits.ftl @@ -500,3 +500,12 @@ trait-name-BrittleBoneDisease = Osteogenesis Imperfecta trait-description-BrittleBoneDisease = Also known as "brittle bone disease", people with this genetic disorder have bones that are easily broken, often simply by moving. This trait reduces your threshold for critical injury by 50 points. + +trait-name-LightAmplification = CyberEyes Module: Light Amplification +trait-description-LightAmplification = + Your CyberEyes have been enhanced with a light amplifier module, enabling the user to toggle between standard sight and "Night Vision" mode. + +trait-name-ThermographicVision = CyberEyes Module: Thermographic Scanner +trait-description-ThermographicVision = + Your CyberEyes have been enhanced with a Thermographic Scanner. When enabled, it captures a snapshot of the user's surroundings, while highlighting all + biological life forms. It can even detect individuals through the walls of a station. diff --git a/Resources/Prototypes/Traits/physical.yml b/Resources/Prototypes/Traits/physical.yml index 3f3d460f99..81ae6715d7 100644 --- a/Resources/Prototypes/Traits/physical.yml +++ b/Resources/Prototypes/Traits/physical.yml @@ -765,3 +765,44 @@ components: - type: CritModifier critThresholdModifier: -50 + +- type: trait + id: LightAmplification + category: Physical + points: -4 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterTraitRequirement + traits: + - CyberEyes + functions: + - !type:TraitAddComponent + components: + - type: NightVision + +- type: trait + id: ThermographicVision + category: Physical + points: -8 + requirements: + - !type:CharacterJobRequirement + inverted: true + jobs: + - Prisoner # Bionics should be "Confiscated" from long term prisoners. + - !type:CharacterTraitRequirement + traits: + - CyberEyes + functions: + - !type:TraitAddComponent + components: + - type: ThermalVision + pulseTime: 2 + toggleAction: PulseThermalVision + - !type:TraitPushDescription + descriptionExtensions: + - description: examine-thermal-vision-message + fontSize: 12 + requireDetailRange: true From 7f32e00f375a4f58b9c36aa97b1d9faab888671b Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com> Date: Fri, 10 Jan 2025 05:15:12 +0000 Subject: [PATCH 31/38] Automatic Changelog Update (#1477) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 7f0e09adcb..357f9b38a7 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9517,3 +9517,12 @@ Entries: id: 6657 time: '2025-01-10T04:41:32.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1462 +- author: VMSolidus + changes: + - type: Add + message: >- + Added 2 new traits related to CyberEyes. Light Amplification Module, and + Thermographic Scanner Module. + id: 6658 + time: '2025-01-10T05:14:46.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1477 From df8ffb0605f431b71045862acc159f93f08470b1 Mon Sep 17 00:00:00 2001 From: VMSolidus <evilexecutive@gmail.com> Date: Fri, 10 Jan 2025 19:13:27 -0500 Subject: [PATCH 32/38] Assay Psionic Power (#1450) # Description This PR adds a new psionic power to the game, called Assay. Assay is a more advanced information gathering tool that is available roundstart to the Mantis. It essentially acts as the first ingame method for characters to discover the underlying math behind Psionics, displaying to them the target's casting stats, potentia, and metapsionic feedback messages. These messages don't tell the caster directly which powers a target has, instead providing hints as to what those powers might be. <details><summary><h1>Media</h1></summary> <p>   </p> </details> # Changelog :cl: - add: Added Assay as a new psi-power, which is available roundstart to the Psionic Mantis. Assay allows the caster to obtain more direct information about the statistics of a specific Psion, as well as vague hints as to what their powers may be, if any. --------- Signed-off-by: VMSolidus <evilexecutive@gmail.com> Co-authored-by: Skubman <ba.fallaria@gmail.com> Co-authored-by: flyingkarii <123355664+flyingkarii@users.noreply.github.com> Co-authored-by: sleepyyapril <123355664+sleepyyapril@users.noreply.github.com> Co-authored-by: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com> --- .../Psionics/Abilities/AssayPowerSystem.cs | 140 ++++++++++++++++++ .../Actions/Events/AssayPowerActionEvent.cs | 30 ++++ Content.Shared/Psionics/Events.cs | 27 ++++ Resources/Locale/en-US/abilities/psionic.ftl | 3 + .../Locale/en-US/psionics/psionic-powers.ftl | 30 ++++ Resources/Prototypes/Actions/psionics.yml | 19 +++ .../DeltaV/Entities/Mobs/NPCs/familiars.yml | 2 + .../Entities/Mobs/NPCs/elemental.yml | 7 + .../Prototypes/Entities/Mobs/NPCs/flesh.yml | 5 + .../Entities/Mobs/NPCs/glimmer_creatures.yml | 4 + .../Structures/Specific/Anomaly/anomalies.yml | 51 +++++++ .../Entities/Structures/Specific/oracle.yml | 2 + .../Structures/Research/glimmer_prober.yml | 4 + .../Structures/Research/sophicscribe.yml | 2 + .../Roles/Jobs/Epistemics/forensicmantis.yml | 1 + Resources/Prototypes/Psionics/psionics.yml | 27 ++++ .../Interface/Actions/psionics.rsi/assay.png | Bin 0 -> 2351 bytes .../Interface/Actions/psionics.rsi/meta.json | 5 +- 18 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 Content.Server/Abilities/Psionics/Abilities/AssayPowerSystem.cs create mode 100644 Content.Shared/Actions/Events/AssayPowerActionEvent.cs create mode 100644 Resources/Textures/Interface/Actions/psionics.rsi/assay.png diff --git a/Content.Server/Abilities/Psionics/Abilities/AssayPowerSystem.cs b/Content.Server/Abilities/Psionics/Abilities/AssayPowerSystem.cs new file mode 100644 index 0000000000..4fe08f303c --- /dev/null +++ b/Content.Server/Abilities/Psionics/Abilities/AssayPowerSystem.cs @@ -0,0 +1,140 @@ +using Content.Server.Chat.Managers; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Actions.Events; +using Content.Shared.Chat; +using Content.Shared.DoAfter; +using Content.Shared.Popups; +using Content.Shared.Psionics.Events; +using Robust.Server.Audio; +using Robust.Shared.Audio; +using Robust.Server.Player; +using Robust.Shared.Timing; +using Robust.Shared.Player; + +namespace Content.Server.Abilities.Psionics; + +public sealed class AssayPowerSystem : EntitySystem +{ + [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly AudioSystem _audioSystem = default!; + [Dependency] private readonly SharedPopupSystem _popups = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent<PsionicComponent, AssayPowerActionEvent>(OnPowerUsed); + SubscribeLocalEvent<PsionicComponent, AssayDoAfterEvent>(OnDoAfter); + } + + /// <summary> + /// This power activates when scanning any entity, displaying to the player's chat window a variety of psionic related statistics about the target. + /// </summary> + private void OnPowerUsed(EntityUid uid, PsionicComponent psionic, AssayPowerActionEvent args) + { + if (!_psionics.OnAttemptPowerUse(args.Performer, "assay") + || psionic.DoAfter is not null) + return; + + var ev = new AssayDoAfterEvent(_gameTiming.CurTime, args.FontSize, args.FontColor); + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Performer, args.UseDelay - TimeSpan.FromSeconds(psionic.CurrentAmplification), ev, args.Performer, args.Target, args.Performer) + { + BlockDuplicate = true, + BreakOnMove = true, + BreakOnDamage = true, + }, out var doAfterId); + psionic.DoAfter = doAfterId; + + _popups.PopupEntity(Loc.GetString(args.PopupTarget, ("entity", args.Target)), args.Performer, PopupType.Medium); + + _audioSystem.PlayPvs(args.SoundUse, args.Performer, AudioParams.Default.WithVolume(8f).WithMaxDistance(1.5f).WithRolloffFactor(3.5f)); + _psionics.LogPowerUsed(args.Performer, args.PowerName, args.MinGlimmer, args.MaxGlimmer); + args.Handled = true; + } + + /// <summary> + /// Assuming the DoAfter wasn't canceled, the user wasn't mindbroken, and the target still exists, prepare the scan results! + /// </summary> + private void OnDoAfter(EntityUid uid, PsionicComponent component, AssayDoAfterEvent args) + { + if (component is null) + return; + component.DoAfter = null; + + var user = uid; + var target = args.Target; + if (target == null || args.Cancelled + || !_playerManager.TryGetSessionByEntity(user, out var session)) + return; + + if (InspectSelf(uid, args, session)) + return; + + if (!TryComp<PsionicComponent>(target, out var targetPsionic)) + { + var noPowers = Loc.GetString("no-powers", ("entity", target)); + _popups.PopupEntity(noPowers, user, user, PopupType.LargeCaution); + + // Incredibly spooky message for non-psychic targets. + var noPowersFeedback = $"[font size={args.FontSize}][color={args.FontColor}]{noPowers}[/color][/font]"; + SendDescToChat(noPowersFeedback, session); + return; + } + + InspectOther(targetPsionic, args, session); + } + + /// <summary> + /// This is a special use-case for scanning yourself with the power. The player receives a unique feedback message if they do so. + /// It however displays significantly less information when doing so. Consider this an intriguing easter egg. + /// </summary> + private bool InspectSelf(EntityUid uid, AssayDoAfterEvent args, ICommonSession session) + { + if (args.Target != args.User) + return false; + + var user = uid; + var target = args.Target; + + var assaySelf = Loc.GetString("assay-self", ("entity", target!.Value)); + _popups.PopupEntity(assaySelf, user, user, PopupType.LargeCaution); + + var assaySelfFeedback = $"[font size=20][color=#ff0000]{assaySelf}[/color][/font]"; + SendDescToChat(assaySelfFeedback, session); + return true; + } + + /// <summary> + /// If the target turns out to be a psychic, display their feedback messages in chat. + /// </summary> + private void InspectOther(PsionicComponent targetPsionic, AssayDoAfterEvent args, ICommonSession session) + { + var target = args.Target; + var targetAmp = MathF.Round(targetPsionic.CurrentAmplification, 2).ToString("#.##"); + var targetDamp = MathF.Round(targetPsionic.CurrentDampening, 2).ToString("#.##"); + var targetPotentia = MathF.Round(targetPsionic.Potentia, 2).ToString("#.##"); + var message = $"[font size={args.FontSize}][color={args.FontColor}]{Loc.GetString("assay-body", ("entity", target!.Value), ("amplification", targetAmp), ("dampening", targetDamp), ("potentia", targetPotentia))}[/color][/font]"; + SendDescToChat(message, session); + + foreach (var feedback in targetPsionic.AssayFeedback) + { + var locale = Loc.GetString(feedback, ("entity", target!.Value)); + var feedbackMessage = $"[font size={args.FontSize}][color={args.FontColor}]{locale}[/color][/font]"; + SendDescToChat(feedbackMessage, session); + } + } + + private void SendDescToChat(string feedbackMessage, ICommonSession session) + { + _chatManager.ChatMessageToOne( + ChatChannel.Emotes, + feedbackMessage, + feedbackMessage, + EntityUid.Invalid, + false, + session.Channel); + } +} diff --git a/Content.Shared/Actions/Events/AssayPowerActionEvent.cs b/Content.Shared/Actions/Events/AssayPowerActionEvent.cs new file mode 100644 index 0000000000..dc25d34331 --- /dev/null +++ b/Content.Shared/Actions/Events/AssayPowerActionEvent.cs @@ -0,0 +1,30 @@ +using Robust.Shared.Audio; + +namespace Content.Shared.Actions.Events; + +public sealed partial class AssayPowerActionEvent : EntityTargetActionEvent +{ + [DataField] + public TimeSpan UseDelay = TimeSpan.FromSeconds(8f); + + [DataField] + public SoundSpecifier SoundUse = new SoundPathSpecifier("/Audio/Psionics/heartbeat_fast.ogg"); + + [DataField] + public string PopupTarget = "assay-begin"; + + [DataField] + public int FontSize = 12; + + [DataField] + public string FontColor = "#8A00C2"; + + [DataField] + public int MinGlimmer = 3; + + [DataField] + public int MaxGlimmer = 6; + + [DataField] + public string PowerName = "assay"; +} diff --git a/Content.Shared/Psionics/Events.cs b/Content.Shared/Psionics/Events.cs index 68ec3097e7..6705e940cd 100644 --- a/Content.Shared/Psionics/Events.cs +++ b/Content.Shared/Psionics/Events.cs @@ -1,6 +1,7 @@ using Robust.Shared.Serialization; using Content.Shared.Damage; using Content.Shared.DoAfter; +using Content.Shared.Abilities.Psionics; namespace Content.Shared.Psionics.Events; @@ -67,3 +68,29 @@ public PsionicHealOtherDoAfterEvent(TimeSpan startedAt) public override DoAfterEvent Clone() => this; } + +[Serializable, NetSerializable] +public sealed partial class AssayDoAfterEvent : DoAfterEvent +{ + [DataField(required: true)] + public TimeSpan StartedAt; + + [DataField] + public int FontSize = 12; + + [DataField] + public string FontColor = "#8A00C2"; + + private AssayDoAfterEvent() + { + } + + public AssayDoAfterEvent(TimeSpan startedAt, int fontSize, string fontColor) + { + StartedAt = startedAt; + FontSize = fontSize; + FontColor = fontColor; + } + + public override DoAfterEvent Clone() => this; +} diff --git a/Resources/Locale/en-US/abilities/psionic.ftl b/Resources/Locale/en-US/abilities/psionic.ftl index d0e8db72f8..c8eb963594 100644 --- a/Resources/Locale/en-US/abilities/psionic.ftl +++ b/Resources/Locale/en-US/abilities/psionic.ftl @@ -68,3 +68,6 @@ action-description-psychokinesis = Bend the fabric of space to instantly move ac action-name-rf-sensitivity = Toggle RF Sensitivity action-desc-rf-sensitivity = Toggle your ability to interpret radio waves on and off. + +action-name-assay = Assay +action-description-assay = Probe an entity at close range to glean metaphorical information about any powers they may have diff --git a/Resources/Locale/en-US/psionics/psionic-powers.ftl b/Resources/Locale/en-US/psionics/psionic-powers.ftl index ab2a991e06..f7d225f58f 100644 --- a/Resources/Locale/en-US/psionics/psionic-powers.ftl +++ b/Resources/Locale/en-US/psionics/psionic-powers.ftl @@ -183,3 +183,33 @@ ghost-role-information-familiar-description = An interdimensional creature bound ghost-role-information-familiar-rules = Obey the one who summoned you. Do not act against the interests of your Master. You will die for your Master if it is necessary. +# Assay Power +assay-begin = The air around {CAPITALIZE($entity)} begins to shimmer faintly +assay-self = I AM. +no-powers = {CAPITALIZE($entity)} will never awaken from the dream in this life +assay-body = "My will cast upon {CAPITALIZE($entity)} divines these. Amplification: {$amplification} Dampening: {$dampening} Potentia: {$potentia}" +assay-power-initialization-feedback = + I descend into the dreamlight once more, there I drink more fully of the cup of knowledge. The touch of the noosphere upon others becomes known to me, + I can cast my will upon them, divining the inner nature of others. +assay-power-metapsionic-feedback = {CAPITALIZE($entity)} bears a spark of the divine's judgment, they have drunk deeply of the cup of knowledge. + +# Entity Specific Feedback Messages +ifrit-feedback = A spirit of Gehenna, bound by the will of a powerful psychic +prober-feedback = A mirror into the end of time, the screaming of dead stars emanates from this machine +drain-feedback = A mirror into a realm where the stars sit still forever, a cold and distant malevolence stares back +sophic-grammateus-feedback = SEEKER, YOU NEED ONLY ASK FOR MY WISDOM. +oracle-feedback = WHY DO YOU BOTHER ME SEEKER? HAVE I NOT MADE MY DESIRES CLEAR? +orecrab-feedback = Heralds of the Lord of Earth, summoned to this realm from Grome's kingdom +reagent-slime-feedback = Heralds of the Lord of Water, summoned to this realm from Straasha's kingdom. +flesh-golem-feedback = Abominations pulled from dead realms, twisted amalgamations of those fallen to the influence of primordial Chaos +glimmer-mite-feedback = A semi-corporeal parasite native to the dreamlight, its presence here brings forth the screams of dead stars. +anomaly-pyroclastic-feedback = A small mirror to the plane of Gehenna, truth lies within the Secret of Fire +anomaly-gravity-feedback = Violet and crimson, blue of blue, impossibly dark yet greater than the whitest of white, a black star shines weakly at the end of it all +anomaly-electricity-feedback = A mirror to a realm tiled by silicon, the lifeblood of artificial thought flows from it +anomaly-flesh-feedback = From within it comes the suffering of damned mutants howling for all eternity +anomaly-bluespace-feedback = A bridge of dreamlight, crossing into the space between realms of the multiverse +anomaly-ice-feedback = Walls of blackened stone, ruin and famine wait for those who fall within +anomaly-rock-feedback = A vast old oak dwells high over a plane of stone, it turns to stare back +anomaly-flora-feedback = Musical notes drift around you, playfully beckoning, they wish to feast +anomaly-liquid-feedback = A realm of twisting currents. Its placidity is a lie. The eyes within stare hungrilly +anomaly-shadow-feedback = At the end of time, when all suns have set forever, there amidst the void stands a monument to past sins. diff --git a/Resources/Prototypes/Actions/psionics.yml b/Resources/Prototypes/Actions/psionics.yml index c6d9e17c2a..97d19aae5f 100644 --- a/Resources/Prototypes/Actions/psionics.yml +++ b/Resources/Prototypes/Actions/psionics.yml @@ -367,3 +367,22 @@ followMaster: true minGlimmer: 5 maxGlimmer: 10 + +- type: entity + id: ActionAssay + name: action-name-assay + description: action-description-assay + categories: [ HideSpawnMenu ] + components: + - type: EntityTargetAction + icon: { sprite: Interface/Actions/psionics.rsi, state: assay } + useDelay: 45 + checkCanAccess: false + range: 2 + itemIconStyle: BigAction + canTargetSelf: true + blacklist: + components: + - PsionicInsulation + - Mindbroken + event: !type:AssayPowerActionEvent diff --git a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml index 12b5f7f200..42c12b1fd6 100644 --- a/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml +++ b/Resources/Prototypes/DeltaV/Entities/Mobs/NPCs/familiars.yml @@ -92,6 +92,8 @@ nameSegments: [names_golem] - type: Psionic removable: false + assayFeedback: + - ifrit-feedback psychognomicDescriptors: - p-descriptor-bound - p-descriptor-cyclic diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml b/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml index 94ba7ec418..bc5e0239d6 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/elemental.yml @@ -74,6 +74,7 @@ - type: ZombieImmune - type: ClothingRequiredStepTriggerImmune slots: All + - type: Dispellable - type: entity abstract: true @@ -104,6 +105,9 @@ damageContainer: StructuralInorganic - type: Psionic removable: false + roller: false + assayFeedback: + - orecrab-feedback - type: InnatePsionicPowers powersToAdd: - TelepathyPower @@ -303,6 +307,9 @@ solution: bloodstream - type: Psionic removable: false + roller: false + assayFeedback: + - reagent-slime-feedback - type: InnatePsionicPowers powersToAdd: - TelepathyPower diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml b/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml index 06ab02dedc..bdf87ec87d 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/flesh.yml @@ -54,6 +54,11 @@ Slash: 6 - type: ReplacementAccent accent: genericAggressive + - type: Psionic + removable: false + roller: false + assayFeedback: + - flesh-golem-feedback - type: entity parent: BaseMobFlesh diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/glimmer_creatures.yml b/Resources/Prototypes/Entities/Mobs/NPCs/glimmer_creatures.yml index 52f3844c25..5fcada3f7f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/glimmer_creatures.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/glimmer_creatures.yml @@ -23,6 +23,10 @@ - ReagentId: Ectoplasm Quantity: 15 - type: Psionic + removable: false + roller: false + assayFeedback: + - glimmer-mite-feedback - type: GlimmerSource - type: AmbientSound range: 6 diff --git a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml index 8294586048..02184fc613 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/Anomaly/anomalies.yml @@ -50,6 +50,7 @@ path: /Audio/Effects/teleport_arrival.ogg - type: Psionic removable: false + roller: false - type: InnatePsionicPowers powersToAdd: - TelepathyPower @@ -106,6 +107,11 @@ - type: IgniteOnCollide fixtureId: fix1 fireStacks: 1 + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-pyroclastic-feedback - type: entity id: AnomalyGravity @@ -138,6 +144,11 @@ - type: SingularityDistortion intensity: 1000 falloffPower: 2.7 + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-gravity-feedback - type: entity id: AnomalyElectricity @@ -161,6 +172,11 @@ castShadows: false - type: ElectricityAnomaly - type: Electrified + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-electricity-feedback - type: entity id: AnomalyFlesh @@ -254,6 +270,11 @@ - MobFleshClamp - MobFleshLover - FleshKudzu + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-flesh-feedback - type: entity id: AnomalyBluespace @@ -304,6 +325,11 @@ anomalyContactDamage: types: Radiation: 10 + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-bluespace-feedback - type: entity id: AnomalyIce @@ -352,6 +378,11 @@ releasedGas: 8 # Frezon. Please replace if there is a better way to specify this releaseOnMaxSeverity: true spawnRadius: 0 + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-ice-feedback - type: entity id: AnomalyRockBase @@ -390,6 +421,11 @@ maxAmount: 50 maxRange: 12 floor: FloorAsteroidTile + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-rock-feedback - type: entity id: AnomalyRockUranium @@ -649,6 +685,11 @@ maxRange: 6 spawns: - KudzuFlowerAngry + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-flora-feedback - type: entity id: AnomalyFloraBulb @@ -808,6 +849,11 @@ solution: anomaly - type: InjectableSolution solution: beaker + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-liquid-feedback - type: entity id: AnomalyShadow @@ -864,3 +910,8 @@ - type: Tag tags: - SpookyFog + - type: Psionic + removable: false + roller: false + assayFeedback: + - anomaly-shadow-feedback diff --git a/Resources/Prototypes/Entities/Structures/Specific/oracle.yml b/Resources/Prototypes/Entities/Structures/Specific/oracle.yml index 3e2ed9508e..e908ecab66 100644 --- a/Resources/Prototypes/Entities/Structures/Specific/oracle.yml +++ b/Resources/Prototypes/Entities/Structures/Specific/oracle.yml @@ -18,6 +18,8 @@ - type: Actions - type: Psionic removable: false + assayFeedback: + - oracle-feedback psychognomicDescriptors: - p-descriptor-old - p-descriptor-demiurgic diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml index 0eede9c281..1bb752ade4 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/glimmer_prober.yml @@ -6,6 +6,8 @@ components: - type: Psionic removable: false + assayFeedback: + - prober-feedback - type: InnatePsionicPowers powersToAdd: - TelepathyPower @@ -93,6 +95,8 @@ components: - type: Psionic removable: false + assayFeedback: + - drain-feedback - type: GlimmerSource addToGlimmer: false - type: Construction diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml index f92c9b0740..6f7b02cb6b 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml @@ -25,6 +25,8 @@ - type: Actions - type: Psionic removable: false + assayFeedback: + - sophic-grammateus-feedback psychognomicDescriptors: - p-descriptor-old - p-descriptor-demiurgic diff --git a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml index f7a3120570..961bbf05fd 100644 --- a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml +++ b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml @@ -39,6 +39,7 @@ powersToAdd: - MetapsionicPower - TelepathyPower + - AssayPower - type: startingGear id: ForensicMantisGear diff --git a/Resources/Prototypes/Psionics/psionics.yml b/Resources/Prototypes/Psionics/psionics.yml index 548881e61f..b1e9d783a8 100644 --- a/Resources/Prototypes/Psionics/psionics.yml +++ b/Resources/Prototypes/Psionics/psionics.yml @@ -170,6 +170,9 @@ - !type:AddPsionicStatSources amplificationModifier: 0.5 dampeningModifier: 0.5 + - !type:PsionicAddAvailablePowers + powerPrototype: AssayPower + weight: 0.1 removalFunctions: - !type:RemovePsionicActions - !type:RemovePsionicPowerComponents @@ -178,6 +181,8 @@ - !type:RemovePsionicStatSources - !type:RemoveAssayFeedback assayFeedback: metapsionic-power-metapsionic-feedback + - !type:PsionicRemoveAvailablePowers + powerPrototype: AssayPower - type: psionicPower id: PsionicRegenerationPower @@ -661,3 +666,25 @@ - !type:RemovePsionicPsychognomicDescriptors psychognomicDescriptor: calling powerSlotCost: 0 + +- type: psionicPower + id: AssayPower + name: Assay + powerCategories: + - Mentalic + initializeFunctions: + - !type:AddPsionicActions + actions: + - ActionAssay + - !type:PsionicFeedbackPopup + - !type:PsionicFeedbackSelfChat + feedbackMessage: assay-power-initialization-feedback + - !type:AddPsionicAssayFeedback + assayFeedback: assay-power-metapsionic-feedback + - !type:AddPsionicStatSources + dampeningModifier: 0.5 + removalFunctions: + - !type:RemovePsionicActions + - !type:RemovePsionicStatSources + - !type:RemoveAssayFeedback + assayFeedback: assay-power-metapsionic-feedback diff --git a/Resources/Textures/Interface/Actions/psionics.rsi/assay.png b/Resources/Textures/Interface/Actions/psionics.rsi/assay.png new file mode 100644 index 0000000000000000000000000000000000000000..f245ca9e5e34e1e240ec8669d544d25a7c480053 GIT binary patch literal 2351 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 V0002eQ<DGy002ovPDHLkV1jdUM4kWu literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Actions/psionics.rsi/meta.json b/Resources/Textures/Interface/Actions/psionics.rsi/meta.json index 735bc293d1..dd9833b560 100644 --- a/Resources/Textures/Interface/Actions/psionics.rsi/meta.json +++ b/Resources/Textures/Interface/Actions/psionics.rsi/meta.json @@ -1,12 +1,15 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "healing_word, revivify, shadeskip by leonardo_dabepis (discord), telekinetic_pulse by .mocho (discord), pyrokinetic_flare, summon_remilia, summon_bat and summon_imp by ghost581 (discord)", + "copyright": "assay, healing_word, revivify, shadeskip by leonardo_dabepis (discord), telekinetic_pulse by .mocho (discord), pyrokinetic_flare, summon_remilia, summon_bat and summon_imp by ghost581 (discord)", "size": { "x": 64, "y": 64 }, "states": [ + { + "name": "assay" + }, { "name": "healing_word" }, From f0a9bbd5ebf4cdd1e1b5cdf26478206bd41a137b Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:13:53 +0000 Subject: [PATCH 33/38] Automatic Changelog Update (#1450) --- Resources/Changelog/Changelog.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 357f9b38a7..78bc7a56f2 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9526,3 +9526,14 @@ Entries: id: 6658 time: '2025-01-10T05:14:46.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1477 +- author: VMSolidus + changes: + - type: Add + message: >- + Added Assay as a new psi-power, which is available roundstart to the + Psionic Mantis. Assay allows the caster to obtain more direct + information about the statistics of a specific Psion, as well as vague + hints as to what their powers may be, if any. + id: 6659 + time: '2025-01-11T00:13:27.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1450 From 6941a010283ac4bfc346097e8860013db134e8d9 Mon Sep 17 00:00:00 2001 From: VMSolidus <evilexecutive@gmail.com> Date: Fri, 10 Jan 2025 20:40:24 -0500 Subject: [PATCH 34/38] Update dev_map.yml --- Resources/Maps/Test/dev_map.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/Resources/Maps/Test/dev_map.yml b/Resources/Maps/Test/dev_map.yml index d4cc1842d7..ad4910d1da 100644 --- a/Resources/Maps/Test/dev_map.yml +++ b/Resources/Maps/Test/dev_map.yml @@ -5831,13 +5831,6 @@ entities: - type: Transform pos: -6.8905525,1.5128828 parent: 179 -- proto: VehicleKeyJanicart - entities: - - uid: 14 - components: - - type: Transform - pos: 6.5,16.5 - parent: 179 - proto: VendingMachineCigs entities: - uid: 870 From 5a13b27cadda36fd257a5d9b3bb2c8d896cb806f Mon Sep 17 00:00:00 2001 From: VMSolidus <evilexecutive@gmail.com> Date: Fri, 10 Jan 2025 20:55:49 -0500 Subject: [PATCH 35/38] How the fuck did AI not have these? --- Resources/Maps/saltern.yml | 1642 ++++++++--------- .../Entities/Mobs/Player/silicon.yml | 34 +- .../Machines/Computers/computers.yml | 5 + .../Structures/Wallmounts/air_alarm.yml | 1 + Resources/Prototypes/Maps/saltern.yml | 3 +- 5 files changed, 860 insertions(+), 825 deletions(-) diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index 36984557db..86122e2f8c 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -469,7 +469,7 @@ entities: 0: 53709 4,4: 0: 4573 - 3: 16384 + 2: 16384 5,0: 0: 62190 5,1: @@ -500,7 +500,7 @@ entities: 0: 35771 7,3: 0: 39135 - 2: 1024 + 3: 1024 7,4: 0: 55739 7,-1: @@ -533,27 +533,27 @@ entities: 0: 3581 1,7: 0: 4369 - 3: 17484 + 2: 17484 1,8: 0: 4369 - 3: 17476 + 2: 17476 2,5: 0: 53367 2,6: 0: 1405 2,7: - 3: 7 + 2: 7 3,5: 0: 4210 - 3: 32768 + 2: 32768 3,6: 0: 13073 - 3: 8 + 2: 8 3,7: - 3: 4 + 2: 4 4,5: 0: 273 - 3: 29764 + 2: 29764 -8,0: 0: 61550 -9,0: @@ -623,7 +623,7 @@ entities: 7,-2: 0: 57599 7,-4: - 3: 8738 + 2: 8738 7,-3: 0: 3808 8,-3: @@ -644,7 +644,7 @@ entities: 0: 65524 0,-9: 0: 52416 - 3: 273 + 2: 273 1,-8: 0: 65488 1,-7: @@ -661,7 +661,7 @@ entities: 0: 61695 2,-9: 0: 13107 - 3: 2176 + 2: 2176 3,-8: 0: 52637 3,-7: @@ -670,76 +670,76 @@ entities: 0: 64733 4,-8: 0: 4483 - 3: 17484 + 2: 17484 4,-7: 0: 65281 - 3: 12 + 2: 12 4,-6: 0: 61152 4,-9: - 3: 16384 + 2: 16384 0: 32768 5,-8: - 3: 21855 + 2: 21855 0: 160 5,-7: - 3: 15 + 2: 15 0: 63232 5,-9: - 3: 20480 + 2: 20480 0: 40960 5,-6: 0: 26214 6,-8: - 3: 21855 + 2: 21855 0: 160 6,-7: - 3: 15 + 2: 15 0: 56576 6,-6: 0: 61919 6,-9: - 3: 20480 + 2: 20480 0: 40960 7,-8: - 3: 21855 + 2: 21855 0: 160 7,-7: - 3: 11823 + 2: 11823 7,-5: 0: 36846 7,-9: - 3: 20767 + 2: 20767 0: 40960 7,-6: - 3: 546 + 2: 546 0: 32768 8,-8: - 3: 21855 + 2: 21855 0: 160 8,-7: - 3: 9007 + 2: 9007 8,-5: 0: 4017 -4,5: 0: 238 -4,6: 0: 255 - 3: 61440 + 2: 61440 -5,5: 0: 58982 -5,6: 0: 238 - 3: 61440 + 2: 61440 -3,5: 0: 3295 -3,6: 0: 255 - 3: 61440 + 2: 61440 -3,7: - 3: 51406 + 2: 51406 -3,8: - 3: 14 + 2: 14 0: 52224 -2,5: 0: 52701 @@ -749,51 +749,51 @@ entities: 0: 61166 -1,8: 0: 34952 - 3: 13104 + 2: 13104 -9,4: 0: 65471 -8,5: 0: 13072 - 3: 128 + 2: 128 -9,5: 0: 65459 -8,6: 0: 1 - 3: 63304 + 2: 63304 -9,6: 0: 59 - 3: 63488 + 2: 63488 -8,7: - 3: 2039 + 2: 2039 -9,7: - 3: 28671 + 2: 28671 -7,6: - 3: 63249 + 2: 63249 0: 14 -7,7: - 3: 16 + 2: 16 -7,5: 0: 1038 - 3: 4352 + 2: 4352 -6,5: 0: 65327 -6,6: 0: 255 - 3: 61440 + 2: 61440 -3,9: 0: 12 - 3: 3584 + 2: 3584 -2,8: 0: 30560 -2,9: 0: 7 - 3: 3840 + 2: 3840 -1,9: - 3: 405 + 2: 405 0,9: - 3: 240 + 2: 240 4,6: - 3: 550 + 2: 550 0: 34816 5,5: 0: 30583 @@ -803,24 +803,24 @@ entities: 0: 5 6,5: 0: 255 - 3: 61440 + 2: 61440 6,6: - 3: 35561 + 2: 35561 7,5: 0: 36063 - 3: 4096 + 2: 4096 7,6: - 3: 53196 + 2: 53196 8,4: 0: 4351 - 3: 57344 + 2: 57344 8,5: 0: 272 - 5: 17472 + 4: 17472 8,6: - 3: 4081 + 2: 4081 1,9: - 3: 18 + 2: 18 9,0: 0: 65102 9,1: @@ -831,7 +831,7 @@ entities: 0: 65535 9,4: 0: 255 - 3: 61440 + 2: 61440 9,-1: 0: 60942 10,0: @@ -846,52 +846,52 @@ entities: 0: 48015 10,4: 0: 255 - 3: 61440 + 2: 61440 11,0: 0: 65520 11,1: 0: 53759 11,2: 0: 4319 - 4: 49152 + 5: 49152 11,3: 0: 61457 - 4: 204 + 5: 204 11,-1: 0: 30583 11,4: 0: 255 - 3: 61440 + 2: 61440 12,0: 0: 65527 12,1: 0: 28791 12,2: 0: 119 - 4: 28672 + 5: 28672 12,3: - 4: 119 + 5: 119 0: 61440 12,-1: 0: 29311 12,4: 0: 255 - 3: 61440 + 2: 61440 13,0: 0: 49080 13,1: 0: 48058 13,2: - 3: 13104 + 2: 13104 0: 34826 13,3: - 3: 35059 + 2: 35059 0: 12288 13,-1: 0: 14119 13,4: 0: 51 - 3: 63624 + 2: 63624 14,0: 0: 48123 14,1: @@ -899,9 +899,9 @@ entities: 14,2: 0: 15235 14,3: - 3: 65528 + 2: 65528 14,4: - 3: 62455 + 2: 62455 15,0: 0: 56797 15,1: @@ -909,23 +909,23 @@ entities: 15,2: 0: 3548 15,3: - 3: 32767 + 2: 32767 15,4: - 3: 12850 + 2: 12850 15,-1: 0: 52701 16,0: 0: 13116 - 4: 52416 + 5: 52416 16,1: 0: 65484 16,2: - 3: 15 + 2: 15 0: 4080 16,3: - 3: 20479 + 2: 20479 8,-4: - 3: 8738 + 2: 8738 0: 34952 9,-4: 0: 56789 @@ -937,15 +937,15 @@ entities: 0: 18295 10,-4: 0: 65024 - 3: 14 + 2: 14 10,-3: 0: 65520 10,-2: 0: 63743 10,-5: - 3: 34956 + 2: 34956 11,-4: - 3: 2187 + 2: 2187 0: 13056 11,-3: 0: 43946 @@ -953,10 +953,10 @@ entities: 0: 30250 11,-5: 0: 32776 - 3: 17968 + 2: 17968 12,-4: 0: 7 - 3: 4088 + 2: 4088 12,-3: 0: 65535 12,-2: @@ -964,7 +964,7 @@ entities: -4,-8: 0: 4016 -4,-9: - 3: 28672 + 2: 28672 0: 127 -5,-8: 0: 2995 @@ -994,15 +994,15 @@ entities: 0: 57309 -2,-9: 0: 61440 - 3: 47 + 2: 47 -1,-6: 0: 30065 -8,-8: 0: 60931 - 3: 8 + 2: 8 -8,-9: 0: 12288 - 3: 35064 + 2: 35064 -9,-8: 0: 56653 -8,-7: @@ -1013,16 +1013,16 @@ entities: 0: 36863 -9,-6: 0: 3211 - 3: 12288 + 2: 12288 -8,-5: 0: 48123 -9,-5: 0: 43008 - 3: 4 + 2: 4 -8,-4: 0: 35771 -7,-8: - 3: 7 + 2: 7 0: 7936 -7,-6: 0: 61428 @@ -1033,7 +1033,7 @@ entities: -7,-4: 0: 36317 -7,-9: - 3: 17408 + 2: 17408 0: 32768 -6,-8: 0: 36747 @@ -1043,30 +1043,30 @@ entities: 0: 62463 -6,-9: 0: 61440 - 3: 34 + 2: 34 -6,-7: 0: 52878 -6,-4: 0: 62463 -5,-9: 0: 12799 - 3: 32768 + 2: 32768 -9,-4: 0: 34952 - 3: 800 + 2: 800 -8,-3: 0: 3663 -9,-3: 0: 52428 - 3: 4369 + 2: 4369 -8,-2: 0: 59119 -9,-2: 0: 52428 - 3: 4369 + 2: 4369 -9,-1: 0: 56780 - 3: 1 + 2: 1 -7,-2: 0: 65262 -7,-3: @@ -1086,11 +1086,11 @@ entities: -11,0: 0: 3855 -12,1: - 3: 2056 + 2: 2056 -11,2: 0: 3855 -11,1: - 3: 546 + 2: 546 0: 2184 -10,0: 0: 65535 @@ -1099,95 +1099,95 @@ entities: -10,2: 0: 53247 -11,3: - 3: 34952 + 2: 34952 -10,3: 0: 61166 -10,-1: 0: 60928 - 3: 15 + 2: 15 -10,4: 0: 61182 -12,-1: - 3: 2738 + 2: 2738 -11,-1: - 3: 3628 + 2: 3628 -11,-2: - 3: 32768 + 2: 32768 -10,-2: - 3: 4492 + 2: 4492 -10,-3: - 3: 57480 + 2: 57480 -10,-4: - 3: 136 + 2: 136 -10,-5: - 3: 32776 + 2: 32776 12,-5: 0: 62079 13,-4: - 3: 3064 + 2: 3064 13,-3: 0: 48059 13,-2: 0: 63243 13,-5: - 3: 39912 + 2: 39912 14,-4: - 3: 1039 + 2: 1039 14,-3: 0: 13107 - 3: 128 + 2: 128 14,-2: 0: 65283 14,-1: 0: 4095 14,-5: - 3: 17600 + 2: 17600 15,-4: - 3: 8739 + 2: 8739 15,-3: - 3: 62066 + 2: 62066 15,-2: 0: 7424 - 3: 206 + 2: 206 15,-5: - 3: 8721 + 2: 8721 16,-3: - 3: 61440 + 2: 61440 16,-2: - 3: 255 + 2: 255 0: 3840 16,-1: 0: 53247 8,-9: - 3: 24143 + 2: 24143 0: 41120 8,-6: - 3: 3618 + 2: 3618 9,-8: - 3: 15 + 2: 15 9,-7: - 3: 15 + 2: 15 9,-6: - 3: 3840 + 2: 3840 10,-8: - 3: 55703 + 2: 55703 10,-7: - 3: 8743 + 2: 8743 0: 34816 10,-6: - 3: 50978 + 2: 50978 0: 8 10,-9: - 3: 40847 + 2: 40847 11,-8: - 3: 54 + 2: 54 0: 2048 11,-7: 0: 64988 11,-6: 0: 3293 11,-9: - 3: 49921 + 2: 49921 12,-8: 0: 12144 12,-7: @@ -1195,78 +1195,78 @@ entities: 12,-6: 0: 24568 20,-1: - 3: 256 + 2: 256 19,-1: - 3: 65335 + 2: 65335 20,0: - 3: 16179 + 2: 16179 19,0: - 3: 39118 + 2: 39118 0: 17441 20,1: - 3: 14135 + 2: 14135 19,1: - 3: 39321 + 2: 39321 0: 17476 20,2: - 3: 29495 + 2: 29495 19,2: - 3: 53179 + 2: 53179 0: 8260 20,3: - 3: 35 + 2: 35 19,3: - 3: 4095 + 2: 4095 12,-9: - 3: 61440 + 2: 61440 13,-8: - 3: 35043 + 2: 35043 13,-7: 0: 64849 13,-6: 0: 349 - 3: 32768 + 2: 32768 13,-9: - 3: 4096 + 2: 4096 14,-8: - 3: 6144 + 2: 6144 14,-7: - 3: 8739 + 2: 8739 14,-6: - 3: 4898 + 2: 4898 15,-8: - 3: 4352 + 2: 4352 15,-7: - 3: 4369 + 2: 4369 15,-6: - 3: 4369 + 2: 4369 -11,4: 0: 192 - 3: 32768 + 2: 32768 -11,6: 0: 12 - 3: 34816 + 2: 34816 -11,7: - 3: 35840 + 2: 35840 -11,5: - 3: 2184 + 2: 2184 -10,6: 0: 4335 - 3: 57344 + 2: 57344 -10,7: - 3: 4040 + 2: 4040 -11,8: - 3: 34952 + 2: 34952 -10,5: 0: 61166 -9,8: 0: 2827 - 3: 25844 + 2: 25844 0,-12: - 3: 127 + 2: 127 0: 12288 -1,-12: - 3: 975 + 2: 975 0: 32768 0,-11: 0: 29107 @@ -1274,14 +1274,14 @@ entities: 0: 2047 0,-10: 0: 247 - 3: 57344 + 2: 57344 -1,-10: 0: 255 - 3: 36864 + 2: 36864 -1,-9: - 3: 3257 + 2: 3257 1,-12: - 3: 4375 + 2: 4375 1,-11: 0: 3536 1,-10: @@ -1291,254 +1291,254 @@ entities: 2,-10: 0: 14196 2,-12: - 3: 44800 + 2: 44800 3,-12: - 3: 768 + 2: 768 3,-10: - 3: 18240 + 2: 18240 3,-9: - 3: 1908 + 2: 1908 7,-12: - 3: 7455 + 2: 7455 7,-11: - 3: 7453 + 2: 7453 7,-10: - 3: 4381 + 2: 4381 8,-12: - 3: 20303 + 2: 20303 8,-11: - 3: 20303 + 2: 20303 8,-10: - 3: 20047 + 2: 20047 0: 40960 9,5: 6: 4368 - 4: 17472 + 5: 17472 9,6: - 3: 12272 + 2: 12272 10,5: - 4: 4368 + 5: 4368 7: 17472 10,6: - 3: 4080 + 2: 4080 11,5: - 4: 21840 + 5: 21840 11,6: - 3: 61424 + 2: 61424 11,7: - 3: 12 + 2: 12 12,5: - 3: 65535 + 2: 65535 12,6: - 3: 65535 + 2: 65535 12,7: - 3: 15 + 2: 15 13,5: - 3: 55705 + 2: 55705 13,6: - 3: 16383 + 2: 16383 13,7: - 3: 1 + 2: 1 14,5: - 3: 30591 + 2: 30591 14,6: - 3: 7 + 2: 7 15,5: - 3: 35 + 2: 35 -4,-11: - 3: 3840 + 2: 3840 -5,-11: - 3: 11776 + 2: 11776 -4,-10: 0: 6143 -5,-10: 0: 14472 - 3: 2 + 2: 2 -3,-11: - 3: 304 + 2: 304 0: 34944 -3,-10: 0: 29949 -2,-10: 0: 1019 -2,-11: - 3: 544 + 2: 544 0: 2176 -2,-12: - 3: 2048 + 2: 2048 17,-3: - 3: 61696 + 2: 61696 17,-2: - 3: 3327 + 2: 3327 0: 768 17,-1: 0: 4369 - 3: 52428 + 2: 52428 17,0: 0: 19969 - 3: 8 - 4: 4368 + 2: 8 + 5: 4368 18,-3: - 3: 4096 + 2: 4096 18,-2: - 3: 59381 + 2: 59381 18,-1: - 3: 15358 + 2: 15358 0: 33792 18,0: - 3: 65399 + 2: 65399 19,-2: - 3: 12288 + 2: 12288 17,1: 0: 65365 17,2: 0: 4095 17,3: - 3: 16383 + 2: 16383 18,1: - 3: 61167 + 2: 61167 18,2: 0: 1792 - 3: 2190 + 2: 2190 18,3: - 3: 40959 + 2: 40959 12,-10: - 3: 61440 + 2: 61440 11,-10: - 3: 61440 + 2: 61440 13,-10: - 3: 7936 + 2: 7936 14,-10: - 3: 256 + 2: 256 9,-12: - 3: 1807 + 2: 1807 9,-11: - 3: 1799 + 2: 1799 9,-10: - 3: 7 + 2: 7 9,-9: - 3: 3855 + 2: 3855 10,-12: - 3: 4369 + 2: 4369 10,-11: - 3: 4369 + 2: 4369 10,-10: - 3: 4369 + 2: 4369 -12,-8: - 3: 64170 + 2: 64170 -13,-8: - 3: 64170 + 2: 64170 -12,-7: - 3: 64170 + 2: 64170 -13,-7: - 3: 64170 + 2: 64170 -12,-9: - 3: 61440 + 2: 61440 -11,-8: - 3: 64170 + 2: 64170 -11,-7: - 3: 64170 + 2: 64170 -11,-9: - 3: 61440 + 2: 61440 -10,-8: - 3: 12834 + 2: 12834 0: 34828 -10,-7: - 3: 12834 + 2: 12834 0: 34952 -10,-9: - 3: 13288 + 2: 13288 0: 32768 -10,-6: - 3: 57378 + 2: 57378 0: 8 -9,-9: 0: 61440 - 3: 248 + 2: 248 -13,-9: - 3: 61440 + 2: 61440 -11,-10: - 3: 8 + 2: 8 -10,-10: - 3: 63631 + 2: 63631 -10,-12: - 3: 59592 + 2: 59592 -9,-12: - 3: 63743 + 2: 63743 -10,-11: - 3: 34952 + 2: 34952 -9,-11: - 3: 63736 + 2: 63736 -9,-10: - 3: 63736 + 2: 63736 -9,-13: - 3: 61440 + 2: 61440 -8,-12: - 3: 63743 + 2: 63743 -8,-11: - 3: 63736 + 2: 63736 -8,-10: - 3: 63736 + 2: 63736 -8,-13: - 3: 29696 + 2: 29696 -7,-12: - 3: 1808 + 2: 1808 -7,-11: - 3: 240 + 2: 240 -6,-11: - 3: 8192 + 2: 8192 -6,-10: - 3: 25262 + 2: 25262 -14,0: 0: 3598 -14,2: 0: 3598 -13,-1: - 3: 3648 + 2: 3648 -11,9: - 3: 35980 + 2: 35980 -11,10: - 3: 51336 + 2: 51336 -10,8: - 3: 497 + 2: 497 0: 3084 -10,9: - 3: 449 + 2: 449 0: 3084 -10,10: - 3: 4593 + 2: 4593 0: 3084 -10,11: - 3: 227 + 2: 227 -9,9: 0: 2827 - 3: 21748 + 2: 21748 -9,10: 0: 2827 - 3: 58612 + 2: 58612 -9,11: - 3: 254 + 2: 254 -8,8: 0: 1799 - 3: 112 + 2: 112 -8,9: 0: 1799 - 3: 4208 + 2: 4208 -8,10: 0: 1799 - 3: 112 + 2: 112 -8,11: - 3: 112 + 2: 112 -15,-8: - 3: 34944 + 2: 34944 -14,-8: - 3: 64443 + 2: 64443 -15,-7: - 3: 136 + 2: 136 -14,-7: - 3: 64443 + 2: 64443 -14,-9: - 3: 61440 + 2: 61440 uniqueMixes: - volume: 2500 temperature: 293.15 @@ -1571,10 +1571,8 @@ entities: - 0 - 0 - volume: 2500 - temperature: 293.14975 + immutable: True moles: - - 20.078888 - - 75.53487 - 0 - 0 - 0 @@ -1585,11 +1583,13 @@ entities: - 0 - 0 - 0 - - volume: 2500 - immutable: True - moles: - 0 - 0 + - volume: 2500 + temperature: 293.14975 + moles: + - 20.078888 + - 75.53487 - 0 - 0 - 0 @@ -1604,7 +1604,7 @@ entities: temperature: 293.15 moles: - 0 - - 0 + - 6666.982 - 0 - 0 - 0 @@ -1619,7 +1619,7 @@ entities: temperature: 293.15 moles: - 0 - - 6666.982 + - 0 - 0 - 0 - 0 @@ -1673,55 +1673,55 @@ entities: color: '#FFFFFFFF' id: Arrows decals: - 446: -12,-30 + 440: -12,-30 - node: color: '#FFFFFFFF' id: Arrows decals: - 383: 20,17 - 398: 20,17 - 402: 20,27 - 447: -12,-29 + 377: 20,17 + 392: 20,17 + 396: 20,27 + 441: -12,-29 - node: angle: 3.141592653589793 rad color: '#FFFFFFFF' id: Arrows decals: - 399: 22,17 - 403: 22,27 + 393: 22,17 + 397: 22,27 - node: color: '#C3C3C3FF' id: Bot decals: - 757: -35,-24 - 762: -32,-32 - 763: -31,-32 + 704: -35,-24 + 709: -32,-32 + 710: -31,-32 - node: color: '#FFFFFFFF' id: Bot decals: - 377: -35,6 - 378: -30,11 - 408: -3,-31 - 613: 31,8 - 614: 31,9 + 375: -35,6 + 376: -30,11 + 402: -3,-31 + 566: 31,8 + 567: 31,9 - node: zIndex: 1 color: '#FFFFFFFF' id: Bot decals: - 481: 20,7 - 482: 21,7 - 483: 22,7 - 550: -7,-26 - 551: -6,-26 + 442: 20,7 + 443: 21,7 + 444: 22,7 + 511: -7,-26 + 512: -6,-26 - node: angle: 3.141592653589793 rad color: '#FFFFFFFF' id: Bot decals: - 400: 20,17 - 401: 22,17 + 394: 20,17 + 395: 22,17 - node: color: '#FFFFFFFF' id: BotRight @@ -1732,577 +1732,577 @@ entities: color: '#C3C3C3FF' id: Box decals: - 775: -33,-29 - 776: -33,-28 + 722: -33,-29 + 723: -33,-28 - node: color: '#FFFFFFFF' id: Box decals: - 914: -33,21 - 915: -32,21 - 916: -32,24 - 917: -33,24 - 926: -31,23 - 927: -31,22 + 824: -33,21 + 825: -32,21 + 826: -32,24 + 827: -33,24 + 834: -31,23 + 835: -31,22 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkCornerNe decals: - 556: -9,-28 + 517: -9,-28 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkCornerNw decals: - 557: -10,-28 + 518: -10,-28 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkCornerSe decals: - 555: -9,-30 + 516: -9,-30 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkCornerSw decals: - 554: -10,-30 + 515: -10,-30 - node: color: '#FFFFFFFF' id: BrickTileDarkInnerSe decals: - 612: -11,20 + 565: -11,20 - node: zIndex: 1 color: '#FFFFFFFF' id: BrickTileDarkInnerSe decals: - 488: 6,17 + 449: 6,17 - node: color: '#FFFFFFFF' id: BrickTileDarkLineE decals: - 530: 0,-25 - 736: -1,29 - 737: -1,28 + 491: 0,-25 + 683: -1,29 + 684: -1,28 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkLineE decals: - 558: -9,-29 + 519: -9,-29 - node: color: '#FFFFFFFF' id: BrickTileDarkLineN decals: - 525: -6,-23 - 526: -5,-23 - 527: -4,-23 - 734: 3,30 - 735: 4,30 + 486: -6,-23 + 487: -5,-23 + 488: -4,-23 + 681: 3,30 + 682: 4,30 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkLineN decals: - 565: 12,-14 - 566: 13,-14 - 567: 14,-14 - 568: 15,-14 + 526: 12,-14 + 527: 13,-14 + 528: 14,-14 + 529: 15,-14 - node: color: '#FFFFFFFF' id: BrickTileDarkLineS decals: - 609: -8,20 - 610: -9,20 - 611: -10,20 + 562: -8,20 + 563: -9,20 + 564: -10,20 - node: zIndex: 1 color: '#FFFFFFFF' id: BrickTileDarkLineS decals: - 484: 10,17 - 485: 9,17 - 486: 8,17 - 487: 7,17 + 445: 10,17 + 446: 9,17 + 447: 8,17 + 448: 7,17 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkLineS decals: - 569: 12,-18 - 570: 13,-18 - 571: 14,-18 - 572: 15,-18 + 530: 12,-18 + 531: 13,-18 + 532: 14,-18 + 533: 15,-18 - node: color: '#FFFFFFFF' id: BrickTileDarkLineW decals: - 531: -2,-25 + 492: -2,-25 - node: zIndex: 2 color: '#FFFFFFFF' id: BrickTileDarkLineW decals: - 559: -10,-29 + 520: -10,-29 - node: zIndex: 2 color: '#D381C9FF' id: BrickTileSteelCornerNe decals: - 553: -6,-26 + 514: -6,-26 - node: color: '#D381C9FF' id: BrickTileSteelCornerNw decals: - 539: -4,-26 + 500: -4,-26 - node: color: '#D381C9FF' id: BrickTileSteelCornerSe decals: - 545: -8,-24 + 506: -8,-24 - node: color: '#D381C9E5' id: BrickTileSteelCornerSw decals: - 437: -12,-32 + 431: -12,-32 - node: color: '#FFFFFFFF' id: BrickTileSteelCornerSw decals: - 625: 25,19 + 578: 25,19 - node: color: '#C3C3C3FF' id: BrickTileSteelInnerNe decals: - 751: -34,-32 - 780: -35,-27 + 698: -34,-32 + 727: -35,-27 - node: color: '#D381C9E5' id: BrickTileSteelInnerNe decals: - 441: -10,-26 + 435: -10,-26 - node: color: '#D381C9FF' id: BrickTileSteelInnerNe decals: - 529: -6,-27 - 544: 0,-29 - 549: -13,-19 + 490: -6,-27 + 505: 0,-29 + 510: -13,-19 - node: color: '#D381C9FF' id: BrickTileSteelInnerNw decals: - 538: -4,-27 - 540: -3,-26 + 499: -4,-27 + 501: -3,-26 - node: color: '#C3C3C3FF' id: BrickTileSteelInnerSe decals: - 761: -34,-30 - 771: -36,-28 - 772: -33,-30 + 708: -34,-30 + 718: -36,-28 + 719: -33,-30 - node: color: '#D381C9E5' id: BrickTileSteelInnerSe decals: - 431: -18,-28 - 434: -7,-33 + 425: -18,-28 + 428: -7,-33 - node: color: '#D381C9FF' id: BrickTileSteelInnerSe decals: - 541: 0,-26 - 546: -8,-23 + 502: 0,-26 + 507: -8,-23 - node: color: '#C3C3C3FF' id: BrickTileSteelInnerSw decals: - 738: -34,-28 + 685: -34,-28 - node: color: '#D381C9E5' id: BrickTileSteelInnerSw decals: - 413: -12,-20 - 438: -12,-31 + 407: -12,-20 + 432: -12,-31 - node: color: '#C3C3C3FF' id: BrickTileSteelLineE decals: - 748: -33,-29 - 749: -33,-28 - 750: -34,-31 - 755: -35,-26 - 756: -35,-25 - 759: -33,-30 - 770: -36,-29 - 779: -36,-30 + 695: -33,-29 + 696: -33,-28 + 697: -34,-31 + 702: -35,-26 + 703: -35,-25 + 706: -33,-30 + 717: -36,-29 + 726: -36,-30 - node: color: '#D381C9FF' id: BrickTileSteelLineE decals: - 542: 0,-27 - 543: 0,-28 + 503: 0,-27 + 504: 0,-28 - node: color: '#C3C3C3FF' id: BrickTileSteelLineN decals: - 746: -37,-24 - 747: -36,-24 - 752: -33,-32 - 753: -33,-27 - 754: -34,-27 + 693: -37,-24 + 694: -36,-24 + 699: -33,-32 + 700: -33,-27 + 701: -34,-27 - node: color: '#D381C9E5' id: BrickTileSteelLineN decals: - 415: -8,-19 - 416: -9,-19 - 417: -10,-19 - 418: -11,-19 - 440: -9,-26 + 409: -8,-19 + 410: -9,-19 + 411: -10,-19 + 412: -11,-19 + 434: -9,-26 - node: color: '#D381C9FF' id: BrickTileSteelLineN decals: - 528: -8,-26 - 548: -12,-19 + 489: -8,-26 + 509: -12,-19 - node: zIndex: 2 color: '#D381C9FF' id: BrickTileSteelLineN decals: - 552: -7,-26 + 513: -7,-26 - node: color: '#FFFFFFFF' id: BrickTileSteelLineN decals: - 409: -11,-24 + 403: -11,-24 - node: color: '#C3C3C3FF' id: BrickTileSteelLineS decals: - 741: -33,-33 - 760: -33,-30 - 764: -34,-33 - 769: -37,-30 - 777: -35,-28 - 778: -36,-30 + 688: -33,-33 + 707: -33,-30 + 711: -34,-33 + 716: -37,-30 + 724: -35,-28 + 725: -36,-30 - node: color: '#D381C9E5' id: BrickTileSteelLineS decals: - 414: -13,-20 - 427: -17,-28 - 428: -16,-28 - 429: -15,-28 - 430: -14,-28 - 432: -5,-33 - 433: -6,-33 - 436: -11,-32 - 439: -13,-31 + 408: -13,-20 + 421: -17,-28 + 422: -16,-28 + 423: -15,-28 + 424: -14,-28 + 426: -5,-33 + 427: -6,-33 + 430: -11,-32 + 433: -13,-31 - node: color: '#D381C9FF' id: BrickTileSteelLineS decals: - 547: -9,-24 + 508: -9,-24 - node: color: '#FFFFFFFF' id: BrickTileSteelLineS decals: - 498: 57,5 - 499: 56,5 - 622: 26,19 - 623: 27,19 - 624: 28,19 + 459: 57,5 + 460: 56,5 + 575: 26,19 + 576: 27,19 + 577: 28,19 - node: color: '#C3C3C3FF' id: BrickTileSteelLineW decals: - 739: -34,-29 - 740: -34,-32 - 742: -37,-27 - 743: -37,-26 - 744: -37,-25 - 745: -37,-24 - 758: -34,-30 - 768: -37,-28 + 686: -34,-29 + 687: -34,-32 + 689: -37,-27 + 690: -37,-26 + 691: -37,-25 + 692: -37,-24 + 705: -34,-30 + 715: -37,-28 - node: color: '#D381C9E5' id: BrickTileSteelLineW decals: - 410: -12,-23 - 411: -12,-22 - 412: -12,-21 + 404: -12,-23 + 405: -12,-22 + 406: -12,-21 - node: color: '#FFFFFFFF' id: BrickTileSteelLineW decals: - 626: 25,20 - 627: 25,21 + 579: 25,20 + 580: 25,21 - node: color: '#689F54FF' id: BrickTileWhiteCornerNe decals: - 791: -21,-5 + 738: -21,-5 - node: color: '#689F54FF' id: BrickTileWhiteCornerNw decals: - 792: -23,-5 + 739: -23,-5 - node: zIndex: 2 color: '#EFB34196' id: BrickTileWhiteCornerSe decals: - 592: -8,7 + 547: -8,7 - node: zIndex: 2 color: '#52B4E996' id: BrickTileWhiteCornerSw decals: - 588: 7,-18 + 543: 7,-18 - node: color: '#A4610696' id: BrickTileWhiteCornerSw decals: - 632: 18,15 + 585: 18,15 - node: zIndex: 2 color: '#EFB34196' id: BrickTileWhiteCornerSw decals: - 593: -14,7 + 548: -14,7 - node: color: '#52B4E996' id: BrickTileWhiteEndE decals: - 512: 49,-12 + 473: 49,-12 - node: color: '#9FED5896' id: BrickTileWhiteEndE decals: - 508: 52,-10 + 469: 52,-10 - node: color: '#A4610696' id: BrickTileWhiteEndE decals: - 515: 52,-8 + 476: 52,-8 - node: color: '#D381C996' id: BrickTileWhiteEndE decals: - 511: 52,-12 + 472: 52,-12 - node: color: '#D4D4D496' id: BrickTileWhiteEndE decals: - 507: 49,-10 + 468: 49,-10 - node: color: '#EFB34196' id: BrickTileWhiteEndE decals: - 500: 49,-8 + 461: 49,-8 - node: color: '#334E6DC8' id: BrickTileWhiteEndN decals: - 502: 57,-8 + 463: 57,-8 - node: color: '#DE3A3A96' id: BrickTileWhiteEndN decals: - 505: 57,-11 + 466: 57,-11 - node: color: '#334E6DC8' id: BrickTileWhiteEndS decals: - 503: 57,-9 + 464: 57,-9 - node: color: '#DE3A3A96' id: BrickTileWhiteEndS decals: - 504: 57,-12 + 465: 57,-12 - node: color: '#52B4E996' id: BrickTileWhiteEndW decals: - 513: 48,-12 + 474: 48,-12 - node: color: '#9FED5896' id: BrickTileWhiteEndW decals: - 509: 51,-10 + 470: 51,-10 - node: color: '#A4610696' id: BrickTileWhiteEndW decals: - 514: 51,-8 + 475: 51,-8 - node: color: '#D381C996' id: BrickTileWhiteEndW decals: - 510: 51,-12 + 471: 51,-12 - node: color: '#D4D4D496' id: BrickTileWhiteEndW decals: - 506: 48,-10 + 467: 48,-10 - node: color: '#EFB34196' id: BrickTileWhiteEndW decals: - 501: 48,-8 + 462: 48,-8 - node: color: '#759DBCFF' id: BrickTileWhiteInnerSe decals: - 975: 19,-18 + 879: 19,-18 - node: color: '#A4610696' id: BrickTileWhiteInnerSe decals: - 630: 22,15 + 583: 22,15 - node: zIndex: 2 color: '#52B4E996' id: BrickTileWhiteInnerSw decals: - 580: 17,-13 + 536: 17,-13 - node: color: '#A4610696' id: BrickTileWhiteInnerSw decals: - 631: 19,15 + 584: 19,15 - node: color: '#689F54FF' id: BrickTileWhiteLineE decals: - 789: -21,-8 - 790: -21,-7 + 736: -21,-8 + 737: -21,-7 - node: color: '#759DBCFF' id: BrickTileWhiteLineE decals: - 971: 19,-19 - 972: 20,-18 - 973: 20,-17 - 974: 20,-16 + 875: 19,-19 + 876: 20,-18 + 877: 20,-17 + 878: 20,-16 - node: zIndex: 2 color: '#EFB34196' id: BrickTileWhiteLineE decals: - 594: -8,8 + 549: -8,8 - node: color: '#689F54FF' id: BrickTileWhiteLineN decals: - 793: -22,-5 + 740: -22,-5 - node: color: '#759DBCFF' id: BrickTileWhiteLineN decals: - 979: 20,-16 - 980: 19,-16 - 981: 17,-16 + 883: 20,-16 + 884: 19,-16 + 885: 17,-16 - node: color: '#A4610696' id: BrickTileWhiteLineN decals: - 618: 26,10 - 619: 27,10 + 571: 26,10 + 572: 27,10 - node: zIndex: 2 color: '#52B4E996' id: BrickTileWhiteLineS decals: - 582: 8,-18 - 583: 9,-18 - 589: 10,-18 + 537: 8,-18 + 538: 9,-18 + 544: 10,-18 - node: color: '#759DBCFF' id: BrickTileWhiteLineS decals: - 976: 20,-18 - 977: 17,-19 - 978: 19,-19 + 880: 20,-18 + 881: 17,-19 + 882: 19,-19 - node: color: '#A4610696' id: BrickTileWhiteLineS decals: - 620: 26,8 - 621: 27,8 - 628: 24,15 - 629: 23,15 - 635: 26,15 - 636: 27,15 - 637: 28,15 + 573: 26,8 + 574: 27,8 + 581: 24,15 + 582: 23,15 + 588: 26,15 + 589: 27,15 + 590: 28,15 - node: zIndex: 2 color: '#EFB34196' id: BrickTileWhiteLineS decals: - 590: -9,7 - 591: -13,7 + 545: -9,7 + 546: -13,7 - node: zIndex: 2 color: '#52B4E996' id: BrickTileWhiteLineW decals: - 579: 17,-14 - 584: 7,-17 - 585: 7,-16 - 586: 7,-15 - 587: 7,-14 + 535: 17,-14 + 539: 7,-17 + 540: 7,-16 + 541: 7,-15 + 542: 7,-14 - node: color: '#689F54FF' id: BrickTileWhiteLineW decals: - 787: -23,-7 - 788: -23,-6 - 794: -23,-8 + 734: -23,-7 + 735: -23,-6 + 741: -23,-8 - node: color: '#759DBCFF' id: BrickTileWhiteLineW decals: - 968: 17,-19 - 969: 17,-18 - 970: 17,-17 - 982: 17,-16 + 872: 17,-19 + 873: 17,-18 + 874: 17,-17 + 886: 17,-16 - node: color: '#A4610696' id: BrickTileWhiteLineW decals: - 633: 18,16 - 634: 18,17 + 586: 18,16 + 587: 18,17 - node: color: '#EFB34196' id: BrickTileWhiteLineW decals: - 642: 48,-6 - 643: 48,-5 - 644: 48,-4 - 645: 48,-3 + 595: 48,-6 + 596: 48,-5 + 597: 48,-4 + 598: 48,-3 - node: zIndex: 2 color: '#EFB34196' id: BrickTileWhiteLineW decals: - 595: -14,8 + 550: -14,8 - node: color: '#FFFFFFFF' id: BrickTileWhiteLineW decals: - 965: 18,-22 - 966: 18,-23 - 967: 18,-21 + 869: 18,-22 + 870: 18,-23 + 871: 18,-21 - node: color: '#FFFFFFFF' id: Caution decals: - 524: 58,-5 + 485: 58,-5 - node: angle: 1.5707963267948966 rad color: '#FFFFFFFF' @@ -2354,8 +2354,8 @@ entities: color: '#FFFFFFFF' id: Delivery decals: - 435: -8,-33 - 533: -1,-31 + 429: -8,-33 + 494: -1,-31 - node: color: '#FFFFFFFF' id: DirtHeavy @@ -2367,23 +2367,23 @@ entities: color: '#FFFFFFFF' id: DirtHeavy decals: - 393: -24,-25 + 387: -24,-25 - node: cleanable: True zIndex: 2 color: '#FFFFFFFF' id: DirtHeavy decals: - 597: -20,10 - 601: -20,9 - 602: -18,9 + 552: -20,10 + 556: -20,9 + 557: -18,9 - node: cleanable: True zIndex: 2 color: '#FFFFFFFF' id: DirtHeavyMonotile decals: - 600: -20,8 + 555: -20,8 - node: color: '#FFFFFFFF' id: DirtLight @@ -2430,25 +2430,25 @@ entities: 365: -36,1 366: -38,7 367: -40,8 - 370: -24,-9 - 371: -24,3 - 372: -25,2 - 373: -7,3 - 375: -36,8 - 376: -38,9 - 384: -23,-24 - 385: -25,-23 - 386: -23,-22 - 387: -25,-25 - 388: -24,-24 - 389: -24,-22 + 368: -24,-9 + 369: -24,3 + 370: -25,2 + 371: -7,3 + 373: -36,8 + 374: -38,9 + 378: -23,-24 + 379: -25,-23 + 380: -23,-22 + 381: -25,-25 + 382: -24,-24 + 383: -24,-22 - node: cleanable: True zIndex: 2 color: '#FFFFFFFF' id: DirtLight decals: - 598: -19,9 + 553: -19,9 - node: color: '#FFFFFFFF' id: DirtMedium @@ -2464,18 +2464,18 @@ entities: 44: 24,17 65: 37,6 82: 53,1 - 374: -35,8 - 390: -23,-25 - 391: -25,-22 - 392: -25,-24 + 372: -35,8 + 384: -23,-25 + 385: -25,-22 + 386: -25,-24 - node: cleanable: True zIndex: 2 color: '#FFFFFFFF' id: DirtMedium decals: - 596: -19,10 - 599: -19,8 + 551: -19,10 + 554: -19,8 - node: color: '#52B4E996' id: FullTileOverlayGreyscale @@ -2493,11 +2493,11 @@ entities: color: '#52B4E996' id: FullTileOverlayGreyscale decals: - 560: 9,-3 - 561: 9,-2 - 562: 10,-2 - 563: 8,-2 - 564: 9,-1 + 521: 9,-3 + 522: 9,-2 + 523: 10,-2 + 524: 8,-2 + 525: 9,-1 - node: color: '#EFB34196' id: FullTileOverlayGreyscale @@ -2537,19 +2537,19 @@ entities: color: '#AA4D53FF' id: HalfTileOverlayGreyscale decals: - 907: -32,19 - 908: -31,19 - 909: -30,19 + 817: -32,19 + 818: -31,19 + 819: -30,19 - node: color: '#BD575DFF' id: HalfTileOverlayGreyscale decals: - 808: -9,17 - 809: -8,17 - 810: -7,17 - 820: -11,11 - 821: -12,11 - 822: -13,11 + 752: -9,17 + 753: -8,17 + 754: -7,17 + 764: -11,11 + 765: -12,11 + 766: -13,11 - node: color: '#DE3A3A96' id: HalfTileOverlayGreyscale @@ -2621,19 +2621,19 @@ entities: color: '#AA4D53FF' id: HalfTileOverlayGreyscale180 decals: - 897: -32,16 - 898: -31,16 - 899: -30,16 + 809: -32,16 + 810: -31,16 + 811: -30,16 - node: color: '#BD575DFF' id: HalfTileOverlayGreyscale180 decals: - 798: -7,10 - 799: -8,10 - 800: -9,10 - 817: -11,10 - 818: -12,10 - 819: -13,10 + 745: -7,10 + 746: -8,10 + 747: -9,10 + 761: -11,10 + 762: -12,10 + 763: -13,10 - node: color: '#DE3A3A96' id: HalfTileOverlayGreyscale180 @@ -2697,19 +2697,19 @@ entities: color: '#AA4D53FF' id: HalfTileOverlayGreyscale270 decals: - 880: -10,15 - 881: -10,14 - 893: -10,16 - 894: -33,17 - 895: -33,16 - 910: -33,18 + 803: -10,15 + 804: -10,14 + 805: -10,16 + 806: -33,17 + 807: -33,16 + 820: -33,18 - node: color: '#BD575DFF' id: HalfTileOverlayGreyscale270 decals: - 802: -10,11 - 803: -10,12 - 804: -10,13 + 749: -10,11 + 750: -10,12 + 751: -10,13 - node: color: '#DE3A3A96' id: HalfTileOverlayGreyscale270 @@ -2740,7 +2740,7 @@ entities: 185: 37,6 201: 42,8 202: 42,7 - 497: 55,2 + 458: 55,2 - node: color: '#FFD886FF' id: HalfTileOverlayGreyscale270 @@ -2778,7 +2778,7 @@ entities: 105: 12,1 307: 12,-9 308: 12,-10 - 654: 12,-11 + 601: 12,-11 - node: color: '#A4610696' id: HalfTileOverlayGreyscale90 @@ -2798,26 +2798,26 @@ entities: color: '#AA4D53FF' id: HalfTileOverlayGreyscale90 decals: - 903: -29,17 - 904: -29,18 - 911: -35,18 - 912: -35,19 - 913: -35,16 + 813: -29,17 + 814: -29,18 + 821: -35,18 + 822: -35,19 + 823: -35,16 - node: color: '#BD575DFF' id: HalfTileOverlayGreyscale90 decals: - 797: -5,8 - 811: -6,16 - 812: -6,15 - 816: -5,12 + 744: -5,8 + 755: -6,16 + 756: -6,15 + 760: -5,12 - node: color: '#C05B60FF' id: HalfTileOverlayGreyscale90 decals: - 845: -5,13 - 846: -5,11 - 858: -5,10 + 789: -5,13 + 790: -5,11 + 802: -5,10 - node: color: '#DE3A3A96' id: HalfTileOverlayGreyscale90 @@ -2853,56 +2853,56 @@ entities: color: '#FFFFFFFF' id: LoadingArea decals: - 984: 31,-20 + 887: 31,-20 - node: angle: 3.141592653589793 rad color: '#FFFFFFFF' id: LoadingArea decals: - 985: 31,-18 + 888: 31,-18 - node: color: '#FFFFFFFF' id: MiniTileDarkLineN decals: - 407: -16,-25 + 401: -16,-25 - node: color: '#D381C9E5' id: MiniTileSteelCornerSe decals: - 419: -14,-23 + 413: -14,-23 - node: color: '#D381C9E5' id: MiniTileSteelCornerSw decals: - 425: -18,-24 + 419: -18,-24 - node: color: '#D381C9E5' id: MiniTileSteelInnerSe decals: - 420: -15,-23 + 414: -15,-23 - node: color: '#D381C9E5' id: MiniTileSteelInnerSw decals: - 426: -17,-24 + 420: -17,-24 - node: color: '#D381C9E5' id: MiniTileSteelLineE decals: - 421: -14,-22 + 415: -14,-22 - node: color: '#D381C9E5' id: MiniTileSteelLineW decals: - 422: -18,-21 - 423: -18,-22 - 424: -18,-23 + 416: -18,-21 + 417: -18,-22 + 418: -18,-23 - node: color: '#FFFFFFFF' id: MiniTileSteelLineW decals: - 825: -5,-13 - 826: -5,-12 + 769: -5,-13 + 770: -5,-12 - node: color: '#334E6DC8' id: QuarterTileOverlayGreyscale @@ -2919,15 +2919,15 @@ entities: color: '#848586FF' id: QuarterTileOverlayGreyscale decals: - 931: -37,10 - 932: -37,13 - 933: -37,14 - 934: -37,15 - 935: -37,16 - 944: -37,20 - 945: -37,21 - 946: -37,22 - 947: -37,23 + 836: -37,10 + 837: -37,13 + 838: -37,14 + 839: -37,15 + 840: -37,16 + 849: -37,20 + 850: -37,21 + 851: -37,22 + 852: -37,23 - node: color: '#D4D4D428' id: QuarterTileOverlayGreyscale @@ -2951,7 +2951,7 @@ entities: 187: 50,5 198: 44,9 203: 42,6 - 496: 55,1 + 457: 55,1 - node: color: '#52B4E996' id: QuarterTileOverlayGreyscale180 @@ -2964,14 +2964,14 @@ entities: color: '#848586FF' id: QuarterTileOverlayGreyscale180 decals: - 936: -37,13 - 937: -37,14 - 938: -37,15 - 939: -37,16 - 940: -37,20 - 941: -37,21 - 942: -37,22 - 943: -37,23 + 841: -37,13 + 842: -37,14 + 843: -37,15 + 844: -37,16 + 845: -37,20 + 846: -37,21 + 847: -37,22 + 848: -37,23 - node: color: '#A4610696' id: QuarterTileOverlayGreyscale180 @@ -2981,7 +2981,7 @@ entities: color: '#AA4D53FF' id: QuarterTileOverlayGreyscale180 decals: - 896: -33,16 + 808: -33,16 - node: color: '#D4D4D428' id: QuarterTileOverlayGreyscale180 @@ -3007,17 +3007,17 @@ entities: color: '#848586FF' id: QuarterTileOverlayGreyscale270 decals: - 954: -38,17 - 955: -38,18 - 956: -38,19 - 957: -36,19 - 958: -36,18 - 959: -36,17 + 859: -38,17 + 860: -38,18 + 861: -38,19 + 862: -36,19 + 863: -36,18 + 864: -36,17 - node: color: '#C05B60FF' id: QuarterTileOverlayGreyscale270 decals: - 857: -6,10 + 801: -6,10 - node: color: '#DE3A3A96' id: QuarterTileOverlayGreyscale270 @@ -3029,7 +3029,7 @@ entities: decals: 125: 35,-5 200: 42,9 - 495: 55,3 + 456: 55,3 - node: color: '#FFD886FF' id: QuarterTileOverlayGreyscale270 @@ -3046,12 +3046,12 @@ entities: color: '#848586FF' id: QuarterTileOverlayGreyscale90 decals: - 948: -36,19 - 949: -36,18 - 950: -36,17 - 951: -38,19 - 952: -38,18 - 953: -38,17 + 853: -36,19 + 854: -36,18 + 855: -36,17 + 856: -38,19 + 857: -38,18 + 858: -38,17 - node: color: '#A4610696' id: QuarterTileOverlayGreyscale90 @@ -3088,18 +3088,18 @@ entities: color: '#8F6CFFAD' id: Rust decals: - 781: -31,-3 - 782: -30,-3 - 783: -30,0 - 784: -31,0 - 785: -31,1 - 786: -30,1 + 728: -31,-3 + 729: -30,-3 + 730: -30,0 + 731: -31,0 + 732: -31,1 + 733: -30,1 - node: color: '#FFFFFFFF' id: StandClear decals: - 396: 21,24 - 532: 1,-30 + 390: 21,24 + 493: 1,-30 - node: angle: 1.5707963267948966 rad color: '#FFFFFFFF' @@ -3110,13 +3110,13 @@ entities: color: '#AA4D53FF' id: ThreeQuarterTileOverlayGreyscale decals: - 906: -33,19 + 816: -33,19 - node: color: '#BD575DFF' id: ThreeQuarterTileOverlayGreyscale decals: - 815: -10,17 - 824: -14,11 + 759: -10,17 + 768: -14,11 - node: color: '#52B4E996' id: ThreeQuarterTileOverlayGreyscale180 @@ -3126,7 +3126,7 @@ entities: color: '#AA4D53FF' id: ThreeQuarterTileOverlayGreyscale180 decals: - 901: -29,16 + 812: -29,16 - node: color: '#52B4E996' id: ThreeQuarterTileOverlayGreyscale270 @@ -3136,8 +3136,8 @@ entities: color: '#BD575DFF' id: ThreeQuarterTileOverlayGreyscale270 decals: - 801: -10,10 - 823: -14,10 + 748: -10,10 + 767: -14,10 - node: color: '#52B4E996' id: ThreeQuarterTileOverlayGreyscale90 @@ -3147,18 +3147,18 @@ entities: color: '#AA4D53FF' id: ThreeQuarterTileOverlayGreyscale90 decals: - 905: -29,19 + 815: -29,19 - node: color: '#BD575DFF' id: ThreeQuarterTileOverlayGreyscale90 decals: - 813: -5,14 - 814: -6,17 + 757: -5,14 + 758: -6,17 - node: color: '#FF0000FF' id: WarnBox decals: - 695: 69,2 + 642: 69,2 - node: color: '#FFFFFFFF' id: WarnBox @@ -3168,174 +3168,174 @@ entities: color: '#FF0000FF' id: WarnCornerNE decals: - 698: 68,3 + 645: 68,3 - node: color: '#FF0000FF' id: WarnCornerNW decals: - 699: 66,3 + 646: 66,3 - node: color: '#FF0000FF' id: WarnCornerSE decals: - 697: 68,1 + 644: 68,1 - node: color: '#FF0000FF' id: WarnCornerSW decals: - 696: 66,1 + 643: 66,1 - node: color: '#FFFFFFFF' id: WarnCornerSmallNW decals: - 963: -39,23 + 868: -39,23 - node: color: '#FFFFFFFF' id: WarnCornerSmallSW decals: - 962: -39,18 + 867: -39,18 - node: color: '#FFFFFFFF' id: WarnEndE decals: - 653: 12,-12 + 600: 12,-12 - node: color: '#FF0000FF' id: WarnEndN decals: - 694: 70,3 + 641: 70,3 - node: color: '#FF0000FF' id: WarnEndS decals: - 693: 70,2 + 640: 70,2 - node: color: '#FFFFFFFF' id: WarnEndW decals: - 652: 11,-12 + 599: 11,-12 - node: color: '#C3C3C3FF' id: WarnLineE decals: - 767: -36,-33 + 714: -36,-33 - node: color: '#FF0000FF' id: WarnLineE decals: - 667: 60,7 - 668: 60,6 - 700: 68,2 + 614: 60,7 + 615: 60,6 + 647: 68,2 - node: color: '#FFFFFFFF' id: WarnLineE decals: - 442: -15,-30 - 443: -15,-31 - 516: 54,-4 - 517: 54,-5 - 518: 54,-6 - 656: 57,7 - 657: 57,6 - 658: 57,8 - 659: 57,5 - 660: 57,4 - 661: 57,3 - 662: 57,2 - 663: 57,1 - 664: 57,0 - 795: -21,-6 - 922: -32,23 - 923: -32,22 + 436: -15,-30 + 437: -15,-31 + 477: 54,-4 + 478: 54,-5 + 479: 54,-6 + 603: 57,7 + 604: 57,6 + 605: 57,8 + 606: 57,5 + 607: 57,4 + 608: 57,3 + 609: 57,2 + 610: 57,1 + 611: 57,0 + 742: -21,-6 + 832: -32,23 + 833: -32,22 - node: color: '#FF0000FF' id: WarnLineN decals: - 688: 62,0 - 689: 63,0 - 690: 63,5 - 691: 62,5 - 692: 67,1 + 635: 62,0 + 636: 63,0 + 637: 63,5 + 638: 62,5 + 639: 67,1 - node: color: '#FFFFFFFF' id: WarnLineN decals: - 404: 20,26 - 405: 21,26 - 406: 22,26 - 519: 56,-4 - 520: 57,-4 - 521: 58,-4 - 522: 59,-4 - 523: 60,-4 - 534: -3,-29 - 535: -2,-29 - 536: -1,-29 - 537: 0,-29 - 703: 70,5 - 920: -33,22 - 921: -32,22 + 398: 20,26 + 399: 21,26 + 400: 22,26 + 480: 56,-4 + 481: 57,-4 + 482: 58,-4 + 483: 59,-4 + 484: 60,-4 + 495: -3,-29 + 496: -2,-29 + 497: -1,-29 + 498: 0,-29 + 650: 70,5 + 830: -33,22 + 831: -32,22 - node: color: '#C3C3C3FF' id: WarnLineS decals: - 765: -34,-33 - 766: -36,-33 - 773: -37,-30 - 774: -37,-29 + 712: -34,-33 + 713: -36,-33 + 720: -37,-30 + 721: -37,-29 - node: color: '#FF0000FF' id: WarnLineS decals: - 669: 62,10 - 670: 62,8 - 671: 62,9 - 672: 62,7 - 673: 62,6 - 674: 62,5 - 675: 62,4 - 676: 62,3 - 677: 62,2 - 678: 62,1 - 679: 62,0 - 680: 62,-1 - 681: 62,-2 - 682: 62,-3 - 683: 62,-4 - 701: 66,2 + 616: 62,10 + 617: 62,8 + 618: 62,9 + 619: 62,7 + 620: 62,6 + 621: 62,5 + 622: 62,4 + 623: 62,3 + 624: 62,2 + 625: 62,1 + 626: 62,0 + 627: 62,-1 + 628: 62,-2 + 629: 62,-3 + 630: 62,-4 + 648: 66,2 - node: color: '#FFFFFFFF' id: WarnLineS decals: - 444: -17,-31 - 445: -17,-30 - 605: -15,18 - 606: -15,19 - 607: -15,20 - 608: -15,21 - 665: 59,7 - 666: 59,6 - 796: -19,-6 - 960: -39,17 - 961: -39,24 + 438: -17,-31 + 439: -17,-30 + 558: -15,18 + 559: -15,19 + 560: -15,20 + 561: -15,21 + 612: 59,7 + 613: 59,6 + 743: -19,-6 + 865: -39,17 + 866: -39,24 - node: color: '#FF0000FF' id: WarnLineW decals: - 684: 62,4 - 685: 63,4 - 686: 63,-1 - 687: 62,-1 - 702: 67,3 + 631: 62,4 + 632: 63,4 + 633: 63,-1 + 634: 62,-1 + 649: 67,3 - node: color: '#FFFFFFFF' id: WarnLineW decals: - 394: 21,24 - 395: 22,24 - 397: 20,24 - 918: -33,23 - 919: -32,23 + 388: 21,24 + 389: 22,24 + 391: 20,24 + 828: -33,23 + 829: -32,23 - node: angle: -1.5707963267948966 rad color: '#FFFFFFFF' @@ -3419,145 +3419,145 @@ entities: color: '#FFFFFFFF' id: WoodTrimThinCornerNe decals: - 705: 0,1 - 830: -18,-11 - 844: 0,14 + 652: 0,1 + 774: -18,-11 + 788: 0,14 - node: zIndex: 1 color: '#FFFFFFFF' id: WoodTrimThinCornerNe decals: - 494: -4,-6 + 455: -4,-6 - node: color: '#FFFFFFFF' id: WoodTrimThinCornerNw decals: - 704: -10,1 - 829: -21,-11 - 843: -4,14 + 651: -10,1 + 773: -21,-11 + 787: -4,14 - node: color: '#FFFFFFFF' id: WoodTrimThinCornerSe decals: - 706: 0,-7 - 831: -18,-13 - 847: 0,10 + 653: 0,-7 + 775: -18,-13 + 791: 0,10 - node: color: '#FFFFFFFF' id: WoodTrimThinCornerSw decals: - 708: -10,-2 - 832: -21,-13 - 842: -4,10 + 655: -10,-2 + 776: -21,-13 + 786: -4,10 - node: color: '#FFFFFFFF' id: WoodTrimThinInnerSw decals: - 707: -8,-2 + 654: -8,-2 - node: color: '#FFFFFFFF' id: WoodTrimThinLineE decals: - 720: 0,0 - 721: 0,-1 - 722: 0,-2 - 723: 0,-3 - 724: 0,-5 - 725: 0,-4 - 726: 0,-6 - 833: -18,-12 - 850: 0,11 - 851: 0,12 - 852: 0,13 + 667: 0,0 + 668: 0,-1 + 669: 0,-2 + 670: 0,-3 + 671: 0,-5 + 672: 0,-4 + 673: 0,-6 + 777: -18,-12 + 794: 0,11 + 795: 0,12 + 796: 0,13 - node: zIndex: 1 color: '#FFFFFFFF' id: WoodTrimThinLineE decals: - 493: -4,-7 - 615: 27,10 - 616: 27,9 - 617: 27,8 + 454: -4,-7 + 568: 27,10 + 569: 27,9 + 570: 27,8 - node: zIndex: 2 color: '#FFFFFFFF' id: WoodTrimThinLineE decals: - 577: 20,-12 + 534: 20,-12 - node: color: '#FFFFFFFF' id: WoodTrimThinLineN decals: - 711: -8,1 - 712: -9,1 - 713: -7,1 - 714: -6,1 - 715: -5,1 - 716: -4,1 - 717: -3,1 - 718: -2,1 - 719: -1,1 - 834: -19,-11 - 835: -20,-11 - 853: -1,14 - 854: -2,14 - 855: -3,14 + 658: -8,1 + 659: -9,1 + 660: -7,1 + 661: -6,1 + 662: -5,1 + 663: -4,1 + 664: -3,1 + 665: -2,1 + 666: -1,1 + 778: -19,-11 + 779: -20,-11 + 797: -1,14 + 798: -2,14 + 799: -3,14 - node: zIndex: 1 color: '#FFFFFFFF' id: WoodTrimThinLineN decals: - 489: -8,-6 - 490: -7,-6 - 491: -6,-6 - 492: -5,-6 + 450: -8,-6 + 451: -7,-6 + 452: -6,-6 + 453: -5,-6 - node: color: '#FFFFFFFF' id: WoodTrimThinLineS decals: - 638: 37,0 - 639: 38,0 - 640: 39,0 - 641: 40,0 - 727: -1,-7 - 728: -2,-7 - 729: -3,-7 - 733: -9,-2 - 837: -20,-13 - 838: -19,-13 - 848: -1,10 - 849: -2,10 - 856: -3,10 + 591: 37,0 + 592: 38,0 + 593: 39,0 + 594: 40,0 + 674: -1,-7 + 675: -2,-7 + 676: -3,-7 + 680: -9,-2 + 781: -20,-13 + 782: -19,-13 + 792: -1,10 + 793: -2,10 + 800: -3,10 - node: color: '#FFFFFFFF' id: WoodTrimThinLineW decals: - 709: -10,-1 - 710: -10,0 - 730: -8,-5 - 731: -8,-4 - 732: -8,-3 - 827: -14,-12 - 828: -14,-11 - 836: -21,-12 - 839: -4,13 - 840: -4,12 - 841: -4,11 + 656: -10,-1 + 657: -10,0 + 677: -8,-5 + 678: -8,-4 + 679: -8,-3 + 771: -14,-12 + 772: -14,-11 + 780: -21,-12 + 783: -4,13 + 784: -4,12 + 785: -4,11 - node: color: '#FFFF00FF' id: radiation decals: - 655: 59,7 + 602: 59,7 - node: color: '#FFFFFFFF' id: space decals: - 986: -21,-33 + 889: -21,-33 - node: color: '#010102FF' id: taser decals: - 989: -34,19 + 890: -34,19 - type: OccluderTree - type: SpreaderGrid - type: Shuttle @@ -4332,7 +4332,7 @@ entities: pos: 3.5,22.5 parent: 31 - type: Door - secondsUntilStateChange: -32364.002 + secondsUntilStateChange: -32451.49 state: Opening - type: DeviceLinkSource lastSignals: @@ -28272,18 +28272,6 @@ entities: - type: Transform pos: 24.491184,-6.41413 parent: 31 -- proto: chem_master - entities: - - uid: 606 - components: - - type: Transform - pos: 19.5,-0.5 - parent: 31 - - uid: 5075 - components: - - type: Transform - pos: 15.5,1.5 - parent: 31 - proto: ChemDispenser entities: - uid: 5076 @@ -28303,6 +28291,18 @@ entities: - type: Transform pos: 18.5,1.5 parent: 31 +- proto: ChemMaster + entities: + - uid: 606 + components: + - type: Transform + pos: 19.5,-0.5 + parent: 31 + - uid: 5075 + components: + - type: Transform + pos: 15.5,1.5 + parent: 31 - proto: ChessBoard entities: - uid: 841 @@ -56679,7 +56679,7 @@ entities: - type: Transform pos: 54.5,-1.5 parent: 31 -- proto: LogicGate +- proto: LogicGateOr entities: - uid: 11306 components: @@ -57997,6 +57997,13 @@ entities: - type: Transform pos: -35.5,-16.5 parent: 31 +- proto: PlayerStationAi + entities: + - uid: 7046 + components: + - type: Transform + pos: 49.5,-24.5 + parent: 31 - proto: PlushieAtmosian entities: - uid: 7433 @@ -65856,7 +65863,7 @@ entities: - type: Transform pos: 12.820141,26.438648 parent: 31 -- proto: soda_dispenser +- proto: SodaDispenser entities: - uid: 1418 components: @@ -69250,12 +69257,6 @@ entities: - type: Transform pos: -28.5,18.5 parent: 31 - - uid: 7046 - components: - - type: Transform - rot: 1.5707963267948966 rad - pos: 49.5,-24.5 - parent: 31 - uid: 8138 components: - type: Transform @@ -69964,11 +69965,6 @@ entities: parent: 31 - proto: ToyAi entities: - - uid: 8292 - components: - - type: Transform - pos: 49.509567,-24.29748 - parent: 31 - uid: 10982 components: - type: Transform diff --git a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml index ed36325586..6c169821ab 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/silicon.yml @@ -224,6 +224,22 @@ unshaded: Empty: { state: ai_empty } Occupied: { state: ai } + - 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. # The job-ready version of an AI spawn. - type: entity @@ -299,6 +315,22 @@ - state: default shader: unshaded map: ["base"] + - 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. # Borgs - type: entity @@ -353,4 +385,4 @@ slots: cell_slot: name: power-cell-slot-component-slot-name-default - startingItem: PowerCellHyper \ No newline at end of file + startingItem: PowerCellHyper diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 19e5c478d2..59e6b5fa4d 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -40,6 +40,7 @@ interfaces: enum.AtmosAlertsComputerUiKey.Key: type: AtmosAlertsComputerBoundUserInterface + - type: StationAiWhitelist - type: entity parent: BaseComputer @@ -297,6 +298,7 @@ interfaces: enum.PowerMonitoringConsoleUiKey.Key: type: PowerMonitoringConsoleBoundUserInterface + - type: StationAiWhitelist - type: entity parent: BaseComputer @@ -677,6 +679,7 @@ radius: 1.5 energy: 1.6 color: "#e6e227" + - type: StationAiWhitelist - type: entity parent: BaseComputer @@ -790,6 +793,7 @@ - type: GuideHelp guides: - Cargo + - type: StationAiWhitelist - type: entity id: ComputerCargoBounty @@ -1027,6 +1031,7 @@ - type: GuideHelp guides: - Cargo + - type: StationAiWhitelist - type: entity parent: BaseComputer diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml index 6090aae882..3e97255960 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/air_alarm.yml @@ -111,6 +111,7 @@ collection: MetalGlassBreak params: volume: -4 + - type: StationAiWhitelist - type: entity id: AirAlarmAssembly diff --git a/Resources/Prototypes/Maps/saltern.yml b/Resources/Prototypes/Maps/saltern.yml index 9b26bbc3c1..26ae4a74ce 100644 --- a/Resources/Prototypes/Maps/saltern.yml +++ b/Resources/Prototypes/Maps/saltern.yml @@ -62,4 +62,5 @@ Mime: [ 1, 1 ] Musician: [ 1, 2 ] Passenger: [ -1, -1 ] - + #silicon + StationAi: [ 1, 1 ] From 0b0e9a8892d1df6e2fb72d81f3a5b51e64fc1f5e Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 29 Aug 2024 15:27:47 +1000 Subject: [PATCH 36/38] I managed to get both AI Latejoin and Prisoner Latejoin working at the same time --- .../Shuttles/Systems/ArrivalsSystem.cs | 17 +++++++++- .../ContainerSpawnPointSystem.cs | 24 ++++++++++---- .../Station/Systems/StationSpawningSystem.cs | 32 ------------------- Content.Shared/CCVar/CCVars.cs | 13 ++++++++ 4 files changed, 47 insertions(+), 39 deletions(-) diff --git a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs index 9dabf6f40a..f7b5f220e2 100644 --- a/Content.Server/Shuttles/Systems/ArrivalsSystem.cs +++ b/Content.Server/Shuttles/Systems/ArrivalsSystem.cs @@ -10,6 +10,7 @@ using Content.Server.Shuttles.Components; using Content.Server.Shuttles.Events; using Content.Server.Spawners.Components; +using Content.Server.Spawners.EntitySystems; using Content.Server.Station.Components; using Content.Server.Station.Events; using Content.Server.Station.Systems; @@ -20,6 +21,7 @@ using Content.Shared.Movement.Components; using Content.Shared.Parallax.Biomes; using Content.Shared.Roles; +using Content.Shared.Preferences; using Content.Shared.Salvage; using Content.Shared.Shuttles.Components; using Content.Shared.Tiles; @@ -64,6 +66,11 @@ public sealed class ArrivalsSystem : EntitySystem /// </summary> public bool Enabled { get; private set; } + /// <summary> + /// Flags if all players spawning at the departure terminal have godmode until they leave the terminal. + /// </summary> + public bool ArrivalsGodmode { get; private set; } + /// <summary> /// The first arrival is a little early, to save everyone 10s /// </summary> @@ -80,6 +87,8 @@ public override void Initialize() { base.Initialize(); + SubscribeLocalEvent<PlayerSpawningEvent>(HandlePlayerSpawning, before: new []{ typeof(ContainerSpawnPointSystem), typeof(SpawnPointSystem)}); + SubscribeLocalEvent<StationArrivalsComponent, StationPostInitEvent>(OnStationPostInit); SubscribeLocalEvent<ArrivalsShuttleComponent, ComponentStartup>(OnShuttleStartup); @@ -95,7 +104,10 @@ public override void Initialize() // Don't invoke immediately as it will get set in the natural course of things. Enabled = _cfgManager.GetCVar(CCVars.ArrivalsShuttles); - Subs.CVar(_cfgManager, CCVars.ArrivalsShuttles, SetArrivals); + ArrivalsGodmode = _cfgManager.GetCVar(CCVars.GodmodeArrivals); + + _cfgManager.OnValueChanged(CCVars.ArrivalsShuttles, SetArrivals); + _cfgManager.OnValueChanged(CCVars.GodmodeArrivals, b => ArrivalsGodmode = b); // Command so admins can set these for funsies _console.RegisterCommand("arrivals", ArrivalsCommand, ArrivalsCompletion); @@ -309,6 +321,9 @@ public void HandlePlayerSpawning(PlayerSpawningEvent ev) if (ev.SpawnResult != null) return; + if (ev.HumanoidCharacterProfile?.SpawnPriority != SpawnPriorityPreference.Arrivals) + return; + // Only works on latejoin even if enabled. if (!Enabled || _ticker.RunLevel != GameRunLevel.InRound) return; diff --git a/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs b/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs index 4d38571b90..6b0033124e 100644 --- a/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs +++ b/Content.Server/Spawners/EntitySystems/ContainerSpawnPointSystem.cs @@ -1,8 +1,11 @@ using Content.Server.GameTicking; using Content.Server.Spawners.Components; using Content.Server.Station.Systems; +using Content.Shared.Preferences; +using Content.Shared.Roles; using Robust.Server.Containers; using Robust.Shared.Containers; +using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.Spawners.EntitySystems; @@ -11,17 +14,25 @@ public sealed class ContainerSpawnPointSystem : EntitySystem { [Dependency] private readonly GameTicker _gameTicker = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly ContainerSystem _container = default!; [Dependency] private readonly StationSystem _station = default!; [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent<PlayerSpawningEvent>(HandlePlayerSpawning, before: new []{ typeof(SpawnPointSystem) }); + } + public void HandlePlayerSpawning(PlayerSpawningEvent args) { if (args.SpawnResult != null) return; - // DeltaV - Ignore these two desired spawn types - if (args.DesiredSpawnPointType is SpawnPointType.Observer or SpawnPointType.LateJoin) + // If it's just a spawn pref check if it's for cryo (silly). + if (args.HumanoidCharacterProfile?.SpawnPriority != SpawnPriorityPreference.Cryosleep && + (!_proto.TryIndex(args.Job?.Prototype, out var jobProto) || jobProto.JobEntity == null)) return; var query = EntityQueryEnumerator<ContainerSpawnPointComponent, ContainerManagerComponent, TransformComponent>(); @@ -33,11 +44,12 @@ public void HandlePlayerSpawning(PlayerSpawningEvent args) continue; // DeltaV - Custom override for override spawnpoints, only used for prisoners currently. This shouldn't run for any other jobs - if (args.DesiredSpawnPointType == SpawnPointType.Job) + if (args.DesiredSpawnPointType == SpawnPointType.Job + && spawnPoint.SpawnType == SpawnPointType.Job + && args.Job is not null + && spawnPoint.Job is not "" + && spawnPoint.Job == args.Job.Prototype) { - if (spawnPoint.SpawnType != SpawnPointType.Job || spawnPoint.Job != args.Job?.Prototype) - continue; - possibleContainers.Add((uid, spawnPoint, container, xform)); continue; } diff --git a/Content.Server/Station/Systems/StationSpawningSystem.cs b/Content.Server/Station/Systems/StationSpawningSystem.cs index 66a9554954..85f5662b42 100644 --- a/Content.Server/Station/Systems/StationSpawningSystem.cs +++ b/Content.Server/Station/Systems/StationSpawningSystem.cs @@ -53,19 +53,11 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem private bool _randomizeCharacters; - private Dictionary<SpawnPriorityPreference, Action<PlayerSpawningEvent>> _spawnerCallbacks = new(); - /// <inheritdoc/> public override void Initialize() { base.Initialize(); Subs.CVar(_configurationManager, CCVars.ICRandomCharacters, e => _randomizeCharacters = e, true); - - _spawnerCallbacks = new Dictionary<SpawnPriorityPreference, Action<PlayerSpawningEvent>>() - { - { SpawnPriorityPreference.Arrivals, _arrivalsSystem.HandlePlayerSpawning }, - { SpawnPriorityPreference.Cryosleep, _containerSpawnPointSystem.HandlePlayerSpawning } - }; } /// <summary> @@ -88,31 +80,7 @@ public override void Initialize() var ev = new PlayerSpawningEvent(job, profile, station, spawnPointType); - if (station != null && profile != null) - { - // Try to call the character's preferred spawner first. - if (_spawnerCallbacks.TryGetValue(profile.SpawnPriority, out var preferredSpawner)) - { - preferredSpawner(ev); - - foreach (var (key, remainingSpawner) in _spawnerCallbacks) - { - if (key == profile.SpawnPriority) - continue; - - remainingSpawner(ev); - } - } - else - { - // Call all of them in the typical order. - foreach (var typicalSpawner in _spawnerCallbacks.Values) - typicalSpawner(ev); - } - } - RaiseLocalEvent(ev); - DebugTools.Assert(ev.SpawnResult is { Valid: true } or null); return ev.SpawnResult; diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index c93eaa77f9..28055d866e 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -1738,6 +1738,19 @@ public static readonly CVarDef<int> public static readonly CVarDef<bool> ArrivalsReturns = CVarDef.Create("shuttle.arrivals_returns", false, CVar.SERVERONLY); + /// <summary> + /// Should all players who spawn at arrivals have godmode until they leave the map? + /// </summary> + public static readonly CVarDef<bool> GodmodeArrivals = + CVarDef.Create("shuttle.godmode_arrivals", false, CVar.SERVERONLY); + + /// <summary> + /// If a grid is split then hide any smaller ones under this mass (kg) from the map. + /// This is useful to avoid split grids spamming out labels. + /// </summary> + public static readonly CVarDef<int> HideSplitGridsUnder = + CVarDef.Create("shuttle.hide_split_grids_under", 30, CVar.SERVERONLY); + /// <summary> /// Whether to automatically spawn escape shuttles. /// </summary> From 5242ceb33ce0c553ae5dd5924e946f1c624b4a99 Mon Sep 17 00:00:00 2001 From: VMSolidus <evilexecutive@gmail.com> Date: Fri, 10 Jan 2025 23:24:22 -0500 Subject: [PATCH 37/38] Make AI Not Spawn With Clothes --- Content.Server/Traits/TraitSystem.cs | 3 +++ .../Clothing/Loadouts/Systems/SharedLoadoutSystem.cs | 2 ++ Content.Shared/Roles/JobPrototype.cs | 6 ++++++ Resources/Prototypes/Roles/Jobs/Science/borg.yml | 4 +++- 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Content.Server/Traits/TraitSystem.cs b/Content.Server/Traits/TraitSystem.cs index 3e0866a2ba..57103c4a5d 100644 --- a/Content.Server/Traits/TraitSystem.cs +++ b/Content.Server/Traits/TraitSystem.cs @@ -48,6 +48,9 @@ private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent args) { var pointsTotal = _configuration.GetCVar(CCVars.GameTraitsDefaultPoints); var traitSelections = _configuration.GetCVar(CCVars.GameTraitsMax); + if (args.JobId is not null && !_prototype.TryIndex<JobPrototype>(args.JobId, out var jobPrototype) + && jobPrototype is not null && !jobPrototype.ApplyTraits) + return; foreach (var traitId in args.Profile.TraitPreferences) { diff --git a/Content.Shared/Clothing/Loadouts/Systems/SharedLoadoutSystem.cs b/Content.Shared/Clothing/Loadouts/Systems/SharedLoadoutSystem.cs index e759c904af..865908c706 100644 --- a/Content.Shared/Clothing/Loadouts/Systems/SharedLoadoutSystem.cs +++ b/Content.Shared/Clothing/Loadouts/Systems/SharedLoadoutSystem.cs @@ -75,6 +75,8 @@ private void OnMapInit(EntityUid uid, LoadoutComponent component, MapInitEvent a var failedLoadouts = new List<EntityUid>(); var allLoadouts = new List<(EntityUid, LoadoutPreference, int)>(); heirlooms = new(); + if (!job.SpawnLoadout) + return (failedLoadouts, allLoadouts); foreach (var loadout in profile.LoadoutPreferences) { diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index 8b21ffef20..5ea9da0224 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -134,6 +134,12 @@ public sealed partial class JobPrototype : IPrototype [DataField] public bool Whitelisted; + + [DataField] + public bool SpawnLoadout = true; + + [DataField] + public bool ApplyTraits = true; } /// <summary> diff --git a/Resources/Prototypes/Roles/Jobs/Science/borg.yml b/Resources/Prototypes/Roles/Jobs/Science/borg.yml index 0c60faccfa..91ee25357e 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/borg.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/borg.yml @@ -13,6 +13,8 @@ supervisors: job-supervisors-rd jobEntity: StationAiBrain nameDataset: NamesAI + spawnLoadout: false + applyTraits: false - type: job id: Borg @@ -25,4 +27,4 @@ canBeAntag: false icon: JobIconBorg supervisors: job-supervisors-rd - jobEntity: PlayerBorgGeneric \ No newline at end of file + jobEntity: PlayerBorgGeneric From 14f738d43f622fbf158b35acb85ca3884cf16090 Mon Sep 17 00:00:00 2001 From: SimpleStation Changelogs <SimpleStation14@users.noreply.github.com> Date: Sat, 11 Jan 2025 04:39:56 +0000 Subject: [PATCH 38/38] Automatic Changelog Update (#1423) --- Resources/Changelog/Changelog.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 78bc7a56f2..3152a61643 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -9537,3 +9537,12 @@ Entries: id: 6659 time: '2025-01-11T00:13:27.0000000+00:00' url: https://github.com/Simple-Station/Einstein-Engines/pull/1450 +- author: sleepyyapril + changes: + - type: Add + message: Ported Station AI. + - type: Add + message: Saltern is now fitted with a Station AI + id: 6660 + time: '2025-01-11T04:39:29.0000000+00:00' + url: https://github.com/Simple-Station/Einstein-Engines/pull/1423