From 01088086dc15f1b402e4faeeb619148734b9dae9 Mon Sep 17 00:00:00 2001 From: William Lemon Date: Mon, 1 Sep 2025 18:46:23 +1000 Subject: [PATCH] =?UTF-8?q?Adds=20Skia=20(=CF=83=CE=BA=CE=B9=CE=AC,=20shad?= =?UTF-8?q?e)=20as=20a=20Midround=20Antagonist=20(#4152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * AAAAAAAAadds skia * Added to the spawn options table * Fix EOR, Added shatter ability, Fix Reroll popup * Fixed the death of the Skia * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed taco's silly mistake * Skia need pry-ability * Adds Goob Nightvision * Added some missing Protos * Appease the Yaml gods * Did the Delta Fixes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Lathes fix * Update attributions.yml * More yaml fixes * The Yaml Linter shall never be sated * More fixes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed objective reroll * Bring up to date * aaaaaaaaaaa * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed damageset, removed unneeded point light * I'm so sorry Deltandas * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: William Lemon Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- Content.Client/Overlays/EquipmentHudSystem.cs | 4 +- .../_DV/Body/LightLevelHealthSystem.cs | 52 +++++ .../_DV/Light/LightReactiveSystem.cs | 24 +++ .../Overlays/BaseSwitchableOverlay.cs | 56 +++++ .../Overlays/NightVisionSystem.cs | 111 ++++++++++ .../Overlays/ThermalVisionOverlay.cs | 168 +++++++++++++++ .../Overlays/ThermalVisionSystem.cs | 120 +++++++++++ Content.Server/Flash/FlashSystem.cs | 11 +- .../PsychokineticScreamPowerSystem.cs | 56 +++++ .../Abilities/ShatterLightsAbilitySystem.cs | 82 +++++++ .../TechnokineticPulseAbilitySystem.cs | 43 ++++ .../_DV/Body/LightLevelHealthSystem.cs | 45 ++++ .../Rules/Components/SkiaRuleComponent.cs | 4 + .../_DV/Light/BreakLightsOnSpawnSystem.cs | 20 ++ .../_DV/Light/LightReactiveSystem.cs | 24 +++ .../RerollAfterCompletionComponent.cs | 39 ++++ .../Systems/RerollAfterCompletionSystem.cs | 95 ++++++++ Content.Server/_DV/Roles/SkiaRoleComponent.cs | 9 + .../PsychokineticScreamPowerComponent.cs | 42 ++++ .../ShatterLightsAbilityComponent.cs | 42 ++++ .../TechnokineticPulseAbilityComponent.cs | 42 ++++ .../_DV/Body/LightLevelDamageMultComponent.cs | 30 +++ .../_DV/Body/LightLevelHealthComponent.cs | 58 +++++ .../_DV/Body/SharedLightLevelHealthSystem.cs | 102 +++++++++ .../_DV/Light/BreakLightsOnSpawnComponent.cs | 19 ++ .../_DV/Light/LightReactiveComponent.cs | 44 ++++ .../_DV/Light/SharedLightReactiveSystem.cs | 109 ++++++++++ Content.Shared/_Goobstation/FlashEvents.cs | 18 ++ .../Inventory/GoobInventorySystem.Relays.cs | 39 ++++ .../Inventory/GoobInventorySystem.cs | 17 ++ .../Overlays/BaseVisionOverlayComponent.cs | 24 +++ .../Overlays/NightVisionComponent.cs | 23 ++ .../Overlays/SharedNightVisionSystem.cs | 11 + .../Overlays/SharedThermalVisionSystem.cs | 11 + .../Overlays/SwitchableOverlaySystem.cs | 202 ++++++++++++++++++ .../SwitchableVisionOverlayComponent.cs | 64 ++++++ .../Overlays/ThermalVisionComponent.cs | 27 +++ Resources/Audio/_DV/Effects/attributions.yml | 4 + Resources/Audio/_DV/Effects/creepyshriek.ogg | Bin 0 -> 45042 bytes .../Audio/_White/Items/Goggles/activate.ogg | Bin 0 -> 12048 bytes .../_White/Items/Goggles/attributions.yml | 15 ++ .../Audio/_White/Items/Goggles/deactivate.ogg | Bin 0 -> 12424 bytes .../Locale/en-US/_DV/abilities/psionic.ftl | 1 + .../Locale/en-US/_DV/ghost/roles/skia.ftl | 11 + .../en-US/_DV/objectives/conditions/skia.ftl | 3 + .../en-US/_DV/prototypes/roles/antags.ftl | 3 + Resources/Locale/en-US/_DV/recipes/tags.ftl | 3 + .../en-US/_white/research/technologies.ftl | 7 + .../Entities/Clothing/Eyes/glasses.yml | 19 -- .../Entities/Clothing/OuterClothing/misc.yml | 12 ++ .../Entities/Structures/Machines/lathe.yml | 2 + .../Prototypes/Nyanotrasen/psionicPowers.yml | 1 + Resources/Prototypes/Roles/Antags/nukeops.yml | 4 +- Resources/Prototypes/_DV/Actions/psionic.yml | 11 + Resources/Prototypes/_DV/Actions/skia.yml | 21 ++ .../Prototypes/_DV/Alerts/categories.yml | 2 + Resources/Prototypes/_DV/Alerts/light.yml | 26 +++ .../_DV/Entities/Clothing/Head/hoods.yml | 18 ++ .../Entities/Clothing/OuterClothing/misc.yml | 20 ++ .../_DV/Entities/Effects/weapon_arc.yml | 29 +++ .../Entities/Markers/Spawners/ghost_roles.yml | 16 ++ .../_DV/Entities/Mobs/NPCs/skia.yml | 109 ++++++++++ .../Entities/Structures/Machines/lathe.yml | 3 + Resources/Prototypes/_DV/GameRules/events.yml | 31 +++ Resources/Prototypes/_DV/Objectives/skia.yml | 25 +++ .../Graphs/clothing/skia_hoodie.yml | 21 ++ .../_DV/Recipes/Construction/clothing.yml | 8 + .../Prototypes/_DV/Roles/Antags/nukeops.yml | 2 +- .../Prototypes/_DV/Roles/Antags/skia.yml | 8 + .../_DV/Roles/MindRoles/mind_roles.yml | 13 ++ Resources/Prototypes/_DV/tags.yml | 3 + .../Recipes/Lathes/Packs/misc.yml | 15 ++ Resources/Prototypes/_White/Actions/types.yml | 48 +++++ .../_White/Entities/Clothing/Eyes/goggles.yml | 94 ++++++++ .../_White/Recipes/Lathes/devices.yml | 25 +++ .../_White/Research/experimental.yml | 39 ++++ .../Prototypes/_White/Shaders/shaders.yml | 13 ++ .../_DV/Actions/shatterlights.rsi/meta.json | 14 ++ .../shatterlights.rsi/shatter-lights.png | Bin 0 -> 760 bytes .../Actions/technokineticpulse.rsi/meta.json | 14 ++ .../technokinetic-pulse.png | Bin 0 -> 1119 bytes .../Hoods/skia_hoodie.rsi/equipped-HELMET.png | Bin 0 -> 934 bytes .../Head/Hoods/skia_hoodie.rsi/icon.png | Bin 0 -> 482 bytes .../Hoods/skia_hoodie.rsi/inhand-left.png | Bin 0 -> 820 bytes .../Hoods/skia_hoodie.rsi/inhand-right.png | Bin 0 -> 819 bytes .../Head/Hoods/skia_hoodie.rsi/meta.json | 26 +++ .../equipped-OUTERCLOTHING.png | Bin 0 -> 1604 bytes .../Misc/skia_hoodie.rsi/icon.png | Bin 0 -> 729 bytes .../Misc/skia_hoodie.rsi/inhand-left.png | Bin 0 -> 667 bytes .../Misc/skia_hoodie.rsi/inhand-right.png | Bin 0 -> 678 bytes .../Misc/skia_hoodie.rsi/meta.json | 26 +++ .../Textures/_DV/Effects/arcs.rsi/meta.json | 17 ++ .../_DV/Effects/arcs.rsi/skialunge.png | Bin 0 -> 609 bytes .../_DV/Effects/arcs.rsi/skiaswipe.png | Bin 0 -> 661 bytes .../Alerts/light_level.rsi/bright.png | Bin 0 -> 898 bytes .../Interface/Alerts/light_level.rsi/dark.png | Bin 0 -> 859 bytes .../Alerts/light_level.rsi/meta.json | 20 ++ .../Alerts/light_level.rsi/neutral.png | Bin 0 -> 746 bytes .../_DV/Mobs/Animals/skia.rsi/dead.png | Bin 0 -> 613 bytes .../_DV/Mobs/Animals/skia.rsi/meta.json | 18 ++ .../_DV/Mobs/Animals/skia.rsi/skia.png | Bin 0 -> 1970 bytes .../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 +++++ .../snightvision.rsi/equipped-EYES-off.png | Bin 0 -> 462 bytes .../snightvision.rsi/equipped-EYES.png | Bin 0 -> 675 bytes .../Eyes/Goggles/snightvision.rsi/icon.png | Bin 0 -> 512 bytes .../Goggles/snightvision.rsi/inhand-left.png | Bin 0 -> 385 bytes .../Goggles/snightvision.rsi/inhand-right.png | Bin 0 -> 410 bytes .../Eyes/Goggles/snightvision.rsi/meta.json | 48 +++++ .../Goggles/sthermals.rsi/equipped-EYES.png | Bin 0 -> 446 bytes .../Eyes/Goggles/sthermals.rsi/icon.png | Bin 0 -> 514 bytes .../Goggles/sthermals.rsi/inhand-left.png | Bin 0 -> 327 bytes .../Goggles/sthermals.rsi/inhand-right.png | Bin 0 -> 325 bytes .../Eyes/Goggles/sthermals.rsi/meta.json | 26 +++ .../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 +++ .../Textures/_White/Shaders/nightvision.swsl | 38 ++++ 124 files changed, 2868 insertions(+), 25 deletions(-) create mode 100644 Content.Client/_DV/Body/LightLevelHealthSystem.cs create mode 100644 Content.Client/_DV/Light/LightReactiveSystem.cs create mode 100644 Content.Client/_Goobstation/Overlays/BaseSwitchableOverlay.cs create mode 100644 Content.Client/_Goobstation/Overlays/NightVisionSystem.cs create mode 100644 Content.Client/_Goobstation/Overlays/ThermalVisionOverlay.cs create mode 100644 Content.Client/_Goobstation/Overlays/ThermalVisionSystem.cs create mode 100644 Content.Server/_DV/Abilities/Psionics/PsychokineticScreamPowerSystem.cs create mode 100644 Content.Server/_DV/Abilities/ShatterLightsAbilitySystem.cs create mode 100644 Content.Server/_DV/Abilities/TechnokineticPulseAbilitySystem.cs create mode 100644 Content.Server/_DV/Body/LightLevelHealthSystem.cs create mode 100644 Content.Server/_DV/GameTicking/Rules/Components/SkiaRuleComponent.cs create mode 100644 Content.Server/_DV/Light/BreakLightsOnSpawnSystem.cs create mode 100644 Content.Server/_DV/Light/LightReactiveSystem.cs create mode 100644 Content.Server/_DV/Objectives/Components/RerollAfterCompletionComponent.cs create mode 100644 Content.Server/_DV/Objectives/Systems/RerollAfterCompletionSystem.cs create mode 100644 Content.Server/_DV/Roles/SkiaRoleComponent.cs create mode 100644 Content.Shared/_DV/Abilities/Psionics/PsychokineticScreamPowerComponent.cs create mode 100644 Content.Shared/_DV/Abilities/ShatterLightsAbilityComponent.cs create mode 100644 Content.Shared/_DV/Abilities/TechnokineticPulseAbilityComponent.cs create mode 100644 Content.Shared/_DV/Body/LightLevelDamageMultComponent.cs create mode 100644 Content.Shared/_DV/Body/LightLevelHealthComponent.cs create mode 100644 Content.Shared/_DV/Body/SharedLightLevelHealthSystem.cs create mode 100644 Content.Shared/_DV/Light/BreakLightsOnSpawnComponent.cs create mode 100644 Content.Shared/_DV/Light/LightReactiveComponent.cs create mode 100644 Content.Shared/_DV/Light/SharedLightReactiveSystem.cs create mode 100644 Content.Shared/_Goobstation/FlashEvents.cs create mode 100644 Content.Shared/_Goobstation/Inventory/GoobInventorySystem.Relays.cs create mode 100644 Content.Shared/_Goobstation/Inventory/GoobInventorySystem.cs create mode 100644 Content.Shared/_Goobstation/Overlays/BaseVisionOverlayComponent.cs create mode 100644 Content.Shared/_Goobstation/Overlays/NightVisionComponent.cs create mode 100644 Content.Shared/_Goobstation/Overlays/SharedNightVisionSystem.cs create mode 100644 Content.Shared/_Goobstation/Overlays/SharedThermalVisionSystem.cs create mode 100644 Content.Shared/_Goobstation/Overlays/SwitchableOverlaySystem.cs create mode 100644 Content.Shared/_Goobstation/Overlays/SwitchableVisionOverlayComponent.cs create mode 100644 Content.Shared/_Goobstation/Overlays/ThermalVisionComponent.cs create mode 100644 Resources/Audio/_DV/Effects/creepyshriek.ogg create mode 100644 Resources/Audio/_White/Items/Goggles/activate.ogg create mode 100644 Resources/Audio/_White/Items/Goggles/attributions.yml create mode 100644 Resources/Audio/_White/Items/Goggles/deactivate.ogg create mode 100644 Resources/Locale/en-US/_DV/ghost/roles/skia.ftl create mode 100644 Resources/Locale/en-US/_DV/objectives/conditions/skia.ftl create mode 100644 Resources/Locale/en-US/_white/research/technologies.ftl create mode 100644 Resources/Prototypes/_DV/Actions/skia.yml create mode 100644 Resources/Prototypes/_DV/Alerts/categories.yml create mode 100644 Resources/Prototypes/_DV/Alerts/light.yml create mode 100644 Resources/Prototypes/_DV/Entities/Effects/weapon_arc.yml create mode 100644 Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml create mode 100644 Resources/Prototypes/_DV/Objectives/skia.yml create mode 100644 Resources/Prototypes/_DV/Recipes/Construction/Graphs/clothing/skia_hoodie.yml create mode 100644 Resources/Prototypes/_DV/Roles/Antags/skia.yml create mode 100644 Resources/Prototypes/_Goobstation/Recipes/Lathes/Packs/misc.yml create mode 100644 Resources/Prototypes/_White/Actions/types.yml create mode 100644 Resources/Prototypes/_White/Entities/Clothing/Eyes/goggles.yml create mode 100644 Resources/Prototypes/_White/Recipes/Lathes/devices.yml create mode 100644 Resources/Prototypes/_White/Research/experimental.yml create mode 100644 Resources/Prototypes/_White/Shaders/shaders.yml create mode 100644 Resources/Textures/_DV/Actions/shatterlights.rsi/meta.json create mode 100644 Resources/Textures/_DV/Actions/shatterlights.rsi/shatter-lights.png create mode 100644 Resources/Textures/_DV/Actions/technokineticpulse.rsi/meta.json create mode 100644 Resources/Textures/_DV/Actions/technokineticpulse.rsi/technokinetic-pulse.png create mode 100644 Resources/Textures/_DV/Clothing/Head/Hoods/skia_hoodie.rsi/equipped-HELMET.png create mode 100644 Resources/Textures/_DV/Clothing/Head/Hoods/skia_hoodie.rsi/icon.png create mode 100644 Resources/Textures/_DV/Clothing/Head/Hoods/skia_hoodie.rsi/inhand-left.png create mode 100644 Resources/Textures/_DV/Clothing/Head/Hoods/skia_hoodie.rsi/inhand-right.png create mode 100644 Resources/Textures/_DV/Clothing/Head/Hoods/skia_hoodie.rsi/meta.json create mode 100644 Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/equipped-OUTERCLOTHING.png create mode 100644 Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/icon.png create mode 100644 Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/inhand-left.png create mode 100644 Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/inhand-right.png create mode 100644 Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/meta.json create mode 100644 Resources/Textures/_DV/Effects/arcs.rsi/meta.json create mode 100644 Resources/Textures/_DV/Effects/arcs.rsi/skialunge.png create mode 100644 Resources/Textures/_DV/Effects/arcs.rsi/skiaswipe.png create mode 100644 Resources/Textures/_DV/Interface/Alerts/light_level.rsi/bright.png create mode 100644 Resources/Textures/_DV/Interface/Alerts/light_level.rsi/dark.png create mode 100644 Resources/Textures/_DV/Interface/Alerts/light_level.rsi/meta.json create mode 100644 Resources/Textures/_DV/Interface/Alerts/light_level.rsi/neutral.png create mode 100644 Resources/Textures/_DV/Mobs/Animals/skia.rsi/dead.png create mode 100644 Resources/Textures/_DV/Mobs/Animals/skia.rsi/meta.json create mode 100644 Resources/Textures/_DV/Mobs/Animals/skia.rsi/skia.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/nightvision.rsi/icon.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/nightvision.rsi/meta.json create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/equipped-EYES-off.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/equipped-EYES.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/icon.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/inhand-left.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/inhand-right.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/meta.json create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/sthermals.rsi/equipped-EYES.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/sthermals.rsi/icon.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/sthermals.rsi/inhand-left.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/sthermals.rsi/inhand-right.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/sthermals.rsi/meta.json create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/thermal.rsi/icon.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/thermal.rsi/inhand-left.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png create mode 100644 Resources/Textures/_White/Clothing/Eyes/Goggles/thermal.rsi/meta.json create mode 100644 Resources/Textures/_White/Shaders/nightvision.swsl diff --git a/Content.Client/Overlays/EquipmentHudSystem.cs b/Content.Client/Overlays/EquipmentHudSystem.cs index f3c556961a..ad915264ec 100644 --- a/Content.Client/Overlays/EquipmentHudSystem.cs +++ b/Content.Client/Overlays/EquipmentHudSystem.cs @@ -94,7 +94,9 @@ public abstract class EquipmentHudSystem : EntitySystem where T : IComponent protected virtual void OnRefreshEquipmentHud(Entity ent, ref InventoryRelayedEvent> args) { - OnRefreshComponentHud(ent, ref args.Args); + // Goobstation edit + args.Args.Active = true; + args.Args.Components.Add(ent.Comp); } protected virtual void OnRefreshComponentHud(Entity ent, ref RefreshEquipmentHudEvent args) diff --git a/Content.Client/_DV/Body/LightLevelHealthSystem.cs b/Content.Client/_DV/Body/LightLevelHealthSystem.cs new file mode 100644 index 0000000000..28021396c1 --- /dev/null +++ b/Content.Client/_DV/Body/LightLevelHealthSystem.cs @@ -0,0 +1,52 @@ +using Content.Client._DV.Light; +using Content.Shared._DV.Body; +using Content.Shared.Alert; +using Robust.Client.Player; +using Robust.Shared.Prototypes; + +namespace Content.Client._DV.Body; + +public sealed class LightLevelHealthSystem : SharedLightLevelHealthSystem +{ + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly LightReactiveSystem _lightReactive = default!; + + private ProtoId _lightLevelDarkIcon = "LightLevelDarkIcon"; + private ProtoId _lightLevelNeutralIcon = "LightLevelNeutralIcon"; + private ProtoId _lightLevelBrightIcon = "LightLevelBrightIcon"; + private ProtoId _lightAlertCategory = "Light"; + + private int _lastThreshold = 0; + + public override void Update(float frameTime) + { + base.Update(frameTime); + + // If we're a LightLevelHealthComponent client, update our alerts. + if (_player.LocalSession?.AttachedEntity is not { } ent) + return; + + if (!TryComp(ent, out var lightLevelHealth)) + return; + + var currentThreshold = CurrentThreshold(_lightReactive.GetLightLevelForPoint(ent), lightLevelHealth); + var alertIcon = currentThreshold switch + { + -1 => _lightLevelDarkIcon, + 1 => _lightLevelBrightIcon, + _ => _lightLevelNeutralIcon, + }; + + if (currentThreshold != _lastThreshold) + { + var alertCategory = _prototype.Index(_lightAlertCategory); + _alerts.ClearAlertCategory(ent, alertCategory); + } + _lastThreshold = currentThreshold; + + var alertProto = _prototype.Index(alertIcon); + _alerts.ShowAlert(ent, alertProto); + } +} diff --git a/Content.Client/_DV/Light/LightReactiveSystem.cs b/Content.Client/_DV/Light/LightReactiveSystem.cs new file mode 100644 index 0000000000..420b3d027f --- /dev/null +++ b/Content.Client/_DV/Light/LightReactiveSystem.cs @@ -0,0 +1,24 @@ +using Content.Shared._DV.Light; +using Robust.Client.GameObjects; + +namespace Content.Client._DV.Light; + +public sealed partial class LightReactiveSystem : SharedLightReactiveSystem +{ + [Dependency] private readonly EntityLookupSystem _lookup = default!; + + private readonly HashSet> _lightsInRange = new(); + private readonly HashSet> _validLightsInRange = new(); + public override HashSet> GetLights(EntityUid targetEntity) + { + _lightsInRange.Clear(); + _lookup.GetEntitiesInRange(Transform(targetEntity).Coordinates, 10f, _lightsInRange); + _validLightsInRange.Clear(); + foreach (var light in _lightsInRange) + { + if(light.Comp.Enabled && !light.Comp.Deleted && light.Comp.NetSyncEnabled) + _validLightsInRange.Add(new(light.Owner, light.Comp)); + } + return _validLightsInRange; + } +} diff --git a/Content.Client/_Goobstation/Overlays/BaseSwitchableOverlay.cs b/Content.Client/_Goobstation/Overlays/BaseSwitchableOverlay.cs new file mode 100644 index 0000000000..e6070794ad --- /dev/null +++ b/Content.Client/_Goobstation/Overlays/BaseSwitchableOverlay.cs @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using System.Numerics; +using Content.Shared._Goobstation.Overlays; +using Robust.Client.Graphics; +using Robust.Shared.Enums; +using Robust.Shared.Prototypes; + +namespace Content.Client._Goobstation.Overlays; + +public sealed class BaseSwitchableOverlay : Overlay where TComp : SwitchableVisionOverlayComponent +{ + [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("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/_Goobstation/Overlays/NightVisionSystem.cs b/Content.Client/_Goobstation/Overlays/NightVisionSystem.cs new file mode 100644 index 0000000000..e71da09cfe --- /dev/null +++ b/Content.Client/_Goobstation/Overlays/NightVisionSystem.cs @@ -0,0 +1,111 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Client.Overlays; +using Content.Shared._Goobstation.Overlays; +using Content.Shared.Inventory; +using Content.Shared.Inventory.Events; +using Robust.Client.Graphics; + +namespace Content.Client._Goobstation.Overlays; + +public sealed class NightVisionSystem : EquipmentHudSystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + [Dependency] private readonly ILightManager _lightManager = default!; + + private BaseSwitchableOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggle); + + _overlay = new BaseSwitchableOverlay(); + } + + protected override void OnRefreshComponentHud(Entity ent, + ref RefreshEquipmentHudEvent args) + { + if (!ent.Comp.IsEquipment) + base.OnRefreshComponentHud(ent, ref args); + } + + protected override void OnRefreshEquipmentHud(Entity ent, + ref InventoryRelayedEvent> args) + { + if (ent.Comp.IsEquipment) + base.OnRefreshEquipmentHud(ent, ref args); + } + + private void OnToggle(Entity ent, ref SwitchableOverlayToggledEvent args) + { + RefreshOverlay(); + } + + protected override void UpdateInternal(RefreshEquipmentHudEvent 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>(): + _overlayMan.AddOverlay(_overlay); + break; + case null: + _overlayMan.RemoveOverlay(_overlay); + break; + } + + if (_overlayMan.TryGetOverlay>(out var overlay)) + overlay.IsActive = nvComp == null; + } +} diff --git a/Content.Client/_Goobstation/Overlays/ThermalVisionOverlay.cs b/Content.Client/_Goobstation/Overlays/ThermalVisionOverlay.cs new file mode 100644 index 0000000000..d99741bd55 --- /dev/null +++ b/Content.Client/_Goobstation/Overlays/ThermalVisionOverlay.cs @@ -0,0 +1,168 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using System.Linq; +using System.Numerics; +using Content.Client.Stealth; +using Content.Shared._Goobstation.Overlays; +using Content.Shared.Body.Components; +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._Goobstation.Overlays; + +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 _entries = []; + + private EntityUid? _lightEntity; + + public float LightRadius; + + public ThermalVisionComponent? Comp; + + public ThermalVisionOverlay() + { + IoCManager.InjectDependencies(this); + + _container = _entity.System(); + _transform = _entity.System(); + _stealth = _entity.System(); + _light = _entity.System(); + + 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(_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(); + while (entities.MoveNext(out var uid, out var body, out var sprite, out var xform)) + { + if (!CanSee(uid, sprite)) + continue; + + var entity = uid; + + if (_container.TryGetOuterContainer(uid, xform, out var container)) + { + var owner = container.Owner; + if (_entity.TryGetComponent(owner, out var ownerSprite) + && _entity.TryGetComponent(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 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(bool checkFirstTimePredicted = true) + { + if (_lightEntity == null || checkFirstTimePredicted && !_timing.IsFirstTimePredicted) + return; + + _entity.DeleteEntity(_lightEntity); + _lightEntity = null; + } +} + +public record struct ThermalVisionRenderEntry( + Entity Ent, + MapId? Map, + Angle EyeRot); diff --git a/Content.Client/_Goobstation/Overlays/ThermalVisionSystem.cs b/Content.Client/_Goobstation/Overlays/ThermalVisionSystem.cs new file mode 100644 index 0000000000..873db395e2 --- /dev/null +++ b/Content.Client/_Goobstation/Overlays/ThermalVisionSystem.cs @@ -0,0 +1,120 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Client.Overlays; +using Content.Shared._Goobstation.Overlays; +using Content.Shared.Inventory; +using Content.Shared.Inventory.Events; +using Robust.Client.Graphics; + +namespace Content.Client._Goobstation.Overlays; + +public sealed class ThermalVisionSystem : EquipmentHudSystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + + private ThermalVisionOverlay _thermalOverlay = default!; + private BaseSwitchableOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggle); + + _thermalOverlay = new ThermalVisionOverlay(); + _overlay = new BaseSwitchableOverlay(); + } + + protected override void OnRefreshComponentHud(Entity ent, + ref RefreshEquipmentHudEvent args) + { + if (!ent.Comp.IsEquipment) + base.OnRefreshComponentHud(ent, ref args); + } + + protected override void OnRefreshEquipmentHud(Entity ent, + ref InventoryRelayedEvent> args) + { + if (ent.Comp.IsEquipment) + base.OnRefreshEquipmentHud(ent, ref args); + } + + private void OnToggle(Entity ent, ref SwitchableOverlayToggledEvent args) + { + RefreshOverlay(); + } + + protected override void UpdateInternal(RefreshEquipmentHudEvent 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(); + + _thermalOverlay.ResetLight(false); + 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(): + _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>(): + _overlayMan.AddOverlay(_overlay); + break; + case null or { DrawOverlay: false }: + _overlayMan.RemoveOverlay(_overlay); + break; + } + + // Night vision overlay is prioritized + _overlay.IsActive = !_overlayMan.HasOverlay>(); + } +} diff --git a/Content.Server/Flash/FlashSystem.cs b/Content.Server/Flash/FlashSystem.cs index f8819bbdc8..200647aaa9 100644 --- a/Content.Server/Flash/FlashSystem.cs +++ b/Content.Server/Flash/FlashSystem.cs @@ -22,6 +22,7 @@ using Robust.Shared.Audio; using Robust.Shared.Random; using InventoryComponent = Content.Shared.Inventory.InventoryComponent; using Robust.Shared.Prototypes; +using Content.Shared._Goobstation.Flashbang; namespace Content.Server.Flash { @@ -132,8 +133,14 @@ namespace Content.Server.Flash if (attempt.Cancelled) return; + // Goobstation start + var multiplierEv = new FlashDurationMultiplierEvent(); + RaiseLocalEvent(target, multiplierEv); + var multiplier = multiplierEv.Multiplier; + // Goobstation end + // don't paralyze, slowdown or convert to rev if the target is immune to flashes - if (!_statusEffectsSystem.TryAddStatusEffect(target, FlashedKey, TimeSpan.FromSeconds(flashDuration / 1000f), true) && !ignoreProtection) //DeltaV: allow flashing to ignore flash protection + if (!_statusEffectsSystem.TryAddStatusEffect(target, FlashedKey, TimeSpan.FromSeconds(flashDuration * multiplier / 1000f), true) && !ignoreProtection) //DeltaV: allow flashing to ignore flash protection return; if (stunDuration != null) @@ -142,7 +149,7 @@ namespace Content.Server.Flash } else { - _stun.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration / 1000f), true, + _stun.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration * multiplier / 1000f), true, slowTo, slowTo); } diff --git a/Content.Server/_DV/Abilities/Psionics/PsychokineticScreamPowerSystem.cs b/Content.Server/_DV/Abilities/Psionics/PsychokineticScreamPowerSystem.cs new file mode 100644 index 0000000000..134aa1fb26 --- /dev/null +++ b/Content.Server/_DV/Abilities/Psionics/PsychokineticScreamPowerSystem.cs @@ -0,0 +1,56 @@ +using Content.Shared._DV.Abilities; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Actions; +using Robust.Server.Audio; + +namespace Content.Server._DV.Abilities; + +public sealed partial class PsychokineticScreamPowerSystem : EntitySystem +{ + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly ShatterLightsAbilitySystem _shatterLights = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnShatterLightsAction); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + _actions.AddAction(ent, ref ent.Comp.PsychokineticScreamActionEntity, ent.Comp.ShatterLightsActionId); + _actions.StartUseDelay(ent.Comp.PsychokineticScreamActionEntity); + + if (TryComp(ent, out var psionic) && psionic.PsionicAbility == null) + { + psionic.PsionicAbility = ent.Comp.PsychokineticScreamActionEntity; + psionic.ActivePowers.Add(ent.Comp); + } + } + + private void OnShutdown(Entity entity, ref ComponentShutdown args) + { + _actions.RemoveAction(entity.Owner, entity.Comp.PsychokineticScreamActionEntity); + if (TryComp(entity, out var psionic)) + { + psionic.ActivePowers.Remove(entity.Comp); + } + } + + private void OnShatterLightsAction(Entity entity, ref ShatterLightsActionEvent args) + { + if (args.Handled) + return; + + if (entity.Comp.AbilitySound != null) + _audio.PlayPvs(entity.Comp.AbilitySound, entity); + + _shatterLights.ShatterLightsAround(entity.Owner, entity.Comp.Radius, entity.Comp.LineOfSight); + args.Handled = true; + } + +} diff --git a/Content.Server/_DV/Abilities/ShatterLightsAbilitySystem.cs b/Content.Server/_DV/Abilities/ShatterLightsAbilitySystem.cs new file mode 100644 index 0000000000..1e3b310af1 --- /dev/null +++ b/Content.Server/_DV/Abilities/ShatterLightsAbilitySystem.cs @@ -0,0 +1,82 @@ +using Content.Server.Light.Components; +using Content.Server.Light.EntitySystems; +using Content.Shared._DV.Abilities; +using Content.Shared.Actions; +using Content.Shared.Physics; +using Robust.Server.Audio; +using Robust.Shared.Map; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Systems; +using System.Linq; +using System.Numerics; + +namespace Content.Server._DV.Abilities; + +public sealed partial class ShatterLightsAbilitySystem : EntitySystem +{ + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly PoweredLightSystem _light = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + private readonly HashSet> _lightsInRange = new(); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnShatterLightsAction); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + _actions.AddAction(ent, ref ent.Comp.ShatterLightsActionEntity, ent.Comp.ShatterLightsActionId); + _actions.StartUseDelay(ent.Comp.ShatterLightsActionEntity); + } + + private void OnShutdown(Entity entity, ref ComponentShutdown args) + { + _actions.RemoveAction(entity.Owner, entity.Comp.ShatterLightsActionEntity); + } + + private void OnShatterLightsAction(Entity entity, ref ShatterLightsActionEvent args) + { + if (args.Handled) + return; + + if (entity.Comp.AbilitySound != null) + _audio.PlayPvs(entity.Comp.AbilitySound, entity); + + ShatterLightsAround(entity.Owner, entity.Comp.Radius, entity.Comp.LineOfSight); + args.Handled = true; + } + + public void ShatterLightsAround(EntityUid center, float range, bool lineOfSight) + { + var pos = _transform.GetWorldPosition(center); + + // Get all light entities within the specified radius + _lightsInRange.Clear(); + _lookup.GetEntitiesInRange(Transform(center).Coordinates, range, _lightsInRange); + foreach (var light in _lightsInRange) + { + if (lineOfSight) // If LoS is required, test it. + { + var lightPos = _transform.GetWorldPosition(light); + var sqrDistance = Vector2.DistanceSquared(pos, lightPos); + var ray = new CollisionRay(pos, (lightPos - pos).Normalized(), (int)CollisionGroup.Opaque); + var hit = _physics.IntersectRay(_transform.GetMapId(center), ray, MathF.Sqrt(sqrDistance) - 0.5f, returnOnFirstHit: true); + if (hit.Any() && hit.First().Distance != 0) + continue; + } + + // If we reach here, the light is unobstructed and within range, break it. + _light.TryDestroyBulb(light, light.Comp); + } + } + +} diff --git a/Content.Server/_DV/Abilities/TechnokineticPulseAbilitySystem.cs b/Content.Server/_DV/Abilities/TechnokineticPulseAbilitySystem.cs new file mode 100644 index 0000000000..89767d3c14 --- /dev/null +++ b/Content.Server/_DV/Abilities/TechnokineticPulseAbilitySystem.cs @@ -0,0 +1,43 @@ +using Content.Server.Emp; +using Content.Shared._DV.Abilities; +using Content.Shared.Actions; + +namespace Content.Server._DV.Abilities; + +public sealed partial class TechnokineticPulseAbilitySystem : EntitySystem +{ + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly EmpSystem _emp = default!; + + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnTechnokineticPulseAction); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + _actions.AddAction(ent, ref ent.Comp.TechnokineticPulseActionEntity, ent.Comp.TechnokineticPulseActionId); + _actions.StartUseDelay(ent.Comp.TechnokineticPulseActionEntity); + } + + private void OnShutdown(Entity entity, ref ComponentShutdown args) + { + _actions.RemoveAction(entity.Owner, entity.Comp.TechnokineticPulseActionEntity); + } + + private void OnTechnokineticPulseAction(Entity entity, ref TechnokineticPulseActionEvent args) + { + if (args.Handled) + return; + + _emp.EmpPulse(_transform.GetMapCoordinates(entity), entity.Comp.Range, entity.Comp.EnergyConsumption, entity.Comp.DisableDuration); + + args.Handled = true; + } +} diff --git a/Content.Server/_DV/Body/LightLevelHealthSystem.cs b/Content.Server/_DV/Body/LightLevelHealthSystem.cs new file mode 100644 index 0000000000..1f7895a505 --- /dev/null +++ b/Content.Server/_DV/Body/LightLevelHealthSystem.cs @@ -0,0 +1,45 @@ +using Content.Shared._DV.Body; +using Content.Shared._DV.Light; +using Content.Shared.Mobs.Systems; +using Content.Shared.Movement.Systems; +using Content.Shared.Popups; +using Robust.Shared.Timing; + +namespace Content.Server._DV.Body; + +public sealed class LightLevelHealthSystem : SharedLightLevelHealthSystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!; + [Dependency] private readonly SharedLightReactiveSystem _lightReactive = default!; + + private TimeSpan _nextUpdate = TimeSpan.MinValue; + public override void Update(float frameTime) + { + if (_timing.CurTime < _nextUpdate) + return; + _nextUpdate = _timing.CurTime + TimeSpan.FromSeconds(1); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (_mobState.IsDead(uid)) + continue; // Don't apply damage if the mob is dead + // Get the light level at the entity's position + var lightLevel = _lightReactive.GetLightLevel(uid, true); + + int currentThreshold = CurrentThreshold(lightLevel, comp); + if (currentThreshold != comp.CurrentThreshold) + { + comp.CurrentThreshold = currentThreshold; + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); + } + + if (currentThreshold == -1) + TryDealDamage(new(uid, comp), comp.DarkDamage); + if (currentThreshold == 1) + TryDealDamage(new(uid, comp), comp.LightDamage); + } + } +} diff --git a/Content.Server/_DV/GameTicking/Rules/Components/SkiaRuleComponent.cs b/Content.Server/_DV/GameTicking/Rules/Components/SkiaRuleComponent.cs new file mode 100644 index 0000000000..420705a942 --- /dev/null +++ b/Content.Server/_DV/GameTicking/Rules/Components/SkiaRuleComponent.cs @@ -0,0 +1,4 @@ +namespace Content.Server._DV.GameTicking.Rules.Components; + +[RegisterComponent] +public sealed partial class SkiaRuleComponent : Component; diff --git a/Content.Server/_DV/Light/BreakLightsOnSpawnSystem.cs b/Content.Server/_DV/Light/BreakLightsOnSpawnSystem.cs new file mode 100644 index 0000000000..b240782443 --- /dev/null +++ b/Content.Server/_DV/Light/BreakLightsOnSpawnSystem.cs @@ -0,0 +1,20 @@ +using Content.Server._DV.Abilities; +using Content.Shared._DV.Light; + +namespace Content.Server._DV.Light; + +public sealed partial class BreakLightsOnSpawnSystem : EntitySystem +{ + [Dependency] private readonly ShatterLightsAbilitySystem _shatterLights = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMapInit); + } + + private void OnMapInit(Entity entity, ref MapInitEvent args) + { + _shatterLights.ShatterLightsAround(entity.Owner, entity.Comp.Radius, entity.Comp.LineOfSight); + } +} diff --git a/Content.Server/_DV/Light/LightReactiveSystem.cs b/Content.Server/_DV/Light/LightReactiveSystem.cs new file mode 100644 index 0000000000..f2a062c054 --- /dev/null +++ b/Content.Server/_DV/Light/LightReactiveSystem.cs @@ -0,0 +1,24 @@ +using Content.Shared._DV.Light; +using Robust.Server.GameObjects; + +namespace Content.Server._DV.Light; + +public sealed partial class LightReactiveSystem : SharedLightReactiveSystem +{ + [Dependency] private readonly EntityLookupSystem _lookup = default!; + + private readonly HashSet> _lightsInRange = new(); + private readonly HashSet> _validLightsInRange = new(); + public override HashSet> GetLights(EntityUid targetEntity) + { + _lightsInRange.Clear(); + _lookup.GetEntitiesInRange(Transform(targetEntity).Coordinates, 10f, _lightsInRange); + _validLightsInRange.Clear(); + foreach (var light in _lightsInRange) + { + if(light.Comp.Enabled && !light.Comp.Deleted && light.Comp.NetSyncEnabled) + _validLightsInRange.Add(new(light.Owner, light.Comp)); + } + return _validLightsInRange; + } +} diff --git a/Content.Server/_DV/Objectives/Components/RerollAfterCompletionComponent.cs b/Content.Server/_DV/Objectives/Components/RerollAfterCompletionComponent.cs new file mode 100644 index 0000000000..a8ca7fac19 --- /dev/null +++ b/Content.Server/_DV/Objectives/Components/RerollAfterCompletionComponent.cs @@ -0,0 +1,39 @@ +using Robust.Shared.Prototypes; + +namespace Content.Server._DV.Objectives.Components; + +/// +/// When this objective is completed, duplicate it with a new target. +/// +[RegisterComponent] +public sealed partial class RerollAfterCompletionComponent : Component +{ + /// + /// If true, the objective has already been rerolled. + /// + /// + /// Ideally this shouldn't matter, as we delete the component once its rolled + /// + public bool Rerolled; + + /// + /// Tracks a reference of the owner of this objective. + /// From what I can see, there is no normaly way to get a mind from an objective, as they're usually passed together. + /// + [DataField] + public EntityUid MindUid = default!; + + /// + /// Prototype of the objective to use for rerolling. + /// Probably the same as this entity (If you want a potentially infinite number of objectives), but could be different if you want it to be a different objective. + /// + [DataField(required: true)] + public EntProtoId RerollObjectivePrototype = default!; + + /// + /// Message to display when the objective is rerolled. + /// If null, no message will be displayed. + /// + [DataField] + public LocId? RerollObjectiveMessage; +} diff --git a/Content.Server/_DV/Objectives/Systems/RerollAfterCompletionSystem.cs b/Content.Server/_DV/Objectives/Systems/RerollAfterCompletionSystem.cs new file mode 100644 index 0000000000..534e739989 --- /dev/null +++ b/Content.Server/_DV/Objectives/Systems/RerollAfterCompletionSystem.cs @@ -0,0 +1,95 @@ +using Content.Server._DV.Objectives.Components; +using Content.Server.Objectives.Components; +using Content.Server.Roles.Jobs; +using Content.Shared.Mind; +using Content.Shared.Objectives.Components; +using Content.Shared.Objectives.Systems; +using Content.Shared.Popups; +using Robust.Shared.Prototypes; + +namespace Content.Server._DV.Objectives.Systems; + +public sealed class RerollAfterCompletionSystem : EntitySystem +{ + [Dependency] private readonly SharedObjectivesSystem _objectives = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly JobSystem _job = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + + private readonly HashSet _objectivesToAdd = new(); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnObjectiveAfterAssign); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + _objectivesToAdd.Clear(); + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var component)) + { + if (component.Rerolled) // If already rerolled, skip. + continue; + + if (!HasComp(uid)) + continue; // If the entity doesn't have an ObjectiveComponent, skip. + + if (!TryComp(component.MindUid, out var mind)) + continue; // If the mind component is missing, skip. + + // Check that this objective has been completed. + if (!_objectives.IsCompleted(uid, new(component.MindUid, mind))) + continue; + + // Destroy this commponent as it is no longer needed, and this will speed up the next check. + RemCompDeferred(uid); + + component.Rerolled = true; + + // I'd be a lot happier if I could do all the rerolling here + // But creating the new objective causes the Query to freak out + // And I need the objective to do everything else. + _objectivesToAdd.Add(component); + } + + foreach (var component in _objectivesToAdd) + { + var mind = component.MindUid; + if (!TryComp(mind, out var mindComponent)) + continue; + // Create a new objective with the specified prototype. + if (_objectives.TryCreateObjective(mind, mindComponent, component.RerollObjectivePrototype) is not { } newObjUid) + continue; + if (component.RerollObjectiveMessage is null) + continue; + + var bodyUid = mindComponent.CurrentEntity ?? component.MindUid; + + // Check if this has a target component, and if so, get it's name for Localization. + if (TryComp(newObjUid, out var targetComp) && TryComp(targetComp.Target, out var targetMindComp)) + { + var newTarget = targetMindComp.CharacterName ?? "Unknown"; + var targetJob = _job.MindTryGetJobName(targetComp.Target); + _popup.PopupEntity(Loc.GetString(component.RerollObjectiveMessage, ("targetName", newTarget), ("job", targetJob)), bodyUid, bodyUid, PopupType.Large); + } + else + { + _popup.PopupEntity(Loc.GetString(component.RerollObjectiveMessage), bodyUid, bodyUid, PopupType.Large); + } + _mind.AddObjective(mind, mindComponent, newObjUid); + } + } + + private void OnObjectiveAfterAssign(EntityUid uid, RerollAfterCompletionComponent comp, ref ObjectiveAfterAssignEvent args) + { + // If the objective is assigned, we can set the mind UID. + if (args.Mind != null) + comp.MindUid = args.MindId; + } +} diff --git a/Content.Server/_DV/Roles/SkiaRoleComponent.cs b/Content.Server/_DV/Roles/SkiaRoleComponent.cs new file mode 100644 index 0000000000..0280a7a7ad --- /dev/null +++ b/Content.Server/_DV/Roles/SkiaRoleComponent.cs @@ -0,0 +1,9 @@ +using Content.Shared.Roles; + +namespace Content.Shared._DV.Roles; + +/// +/// Added to mind role entities to tag that they are a Skia. +/// +[RegisterComponent] +public sealed partial class SkiaRoleComponent : BaseMindRoleComponent; diff --git a/Content.Shared/_DV/Abilities/Psionics/PsychokineticScreamPowerComponent.cs b/Content.Shared/_DV/Abilities/Psionics/PsychokineticScreamPowerComponent.cs new file mode 100644 index 0000000000..48f7ebc07c --- /dev/null +++ b/Content.Shared/_DV/Abilities/Psionics/PsychokineticScreamPowerComponent.cs @@ -0,0 +1,42 @@ +using Content.Shared.Actions; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Abilities; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PsychokineticScreamPowerComponent : Component +{ + /// + /// The radius in which lights will be broken. + /// + [DataField] + public float Radius = 10f; + + /// + /// If true, lights will only be broken if the entity has line of sight to them. + /// + [DataField] + public bool LineOfSight = false; + + /// + /// The action that triggers the psychokinetic scream ability. + /// + [DataField] + public EntProtoId ShatterLightsActionId = "ActionPsychokineticScream"; + + /// + /// Standing reference to the action entity, if it exists. + /// + [DataField, AutoNetworkedField] + public EntityUid? PsychokineticScreamActionEntity; + + /// + /// The sound to play when the ability is used. + /// + [DataField] + public SoundSpecifier AbilitySound = new SoundPathSpecifier("/Audio/_DV/Effects/creepyshriek.ogg"); +} + +public sealed partial class ShatterLightsActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Abilities/ShatterLightsAbilityComponent.cs b/Content.Shared/_DV/Abilities/ShatterLightsAbilityComponent.cs new file mode 100644 index 0000000000..fb5412ef52 --- /dev/null +++ b/Content.Shared/_DV/Abilities/ShatterLightsAbilityComponent.cs @@ -0,0 +1,42 @@ +using Content.Shared.Actions; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Abilities; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class ShatterLightsAbilityComponent : Component +{ + /// + /// The radius in which lights will be broken. + /// + [DataField] + public float Radius = 10f; + + /// + /// If true, lights will only be broken if the entity has line of sight to them. + /// + [DataField] + public bool LineOfSight = false; + + /// + /// The action that triggers the shatter lights ability. + /// + [DataField] + public EntProtoId ShatterLightsActionId = "ActionShatterLights"; + + /// + /// Standing reference to the action entity, if it exists. + /// + [DataField, AutoNetworkedField] + public EntityUid? ShatterLightsActionEntity; + + /// + /// The sound to play when the ability is used. + /// + [DataField] + public SoundSpecifier AbilitySound = new SoundPathSpecifier("/Audio/_DV/Effects/creepyshriek.ogg"); +} + +public sealed partial class ShatterLightsActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Abilities/TechnokineticPulseAbilityComponent.cs b/Content.Shared/_DV/Abilities/TechnokineticPulseAbilityComponent.cs new file mode 100644 index 0000000000..e41e089f8b --- /dev/null +++ b/Content.Shared/_DV/Abilities/TechnokineticPulseAbilityComponent.cs @@ -0,0 +1,42 @@ +using Content.Shared.Actions; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Abilities; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class TechnokineticPulseAbilityComponent : Component +{ + /// + /// The radius in which to EMP + /// + [DataField] + public float Range = 1.0f; + + /// + /// The amount of power to drain from batteries + /// + [DataField] + public float EnergyConsumption = 20000; + + /// + /// The duration for which devices are disabled. + /// + [DataField] + public float DisableDuration = 20f; + + /// + /// The action that triggers the technokinetic pulse ability. + /// + [DataField] + public EntProtoId TechnokineticPulseActionId = "ActionTechnokineticPulse"; + + /// + /// Standing reference to the action entity, if it exists. + /// + [DataField, AutoNetworkedField] + public EntityUid? TechnokineticPulseActionEntity; +} + +public sealed partial class TechnokineticPulseActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Body/LightLevelDamageMultComponent.cs b/Content.Shared/_DV/Body/LightLevelDamageMultComponent.cs new file mode 100644 index 0000000000..63655e4b5a --- /dev/null +++ b/Content.Shared/_DV/Body/LightLevelDamageMultComponent.cs @@ -0,0 +1,30 @@ +using Content.Shared.Damage; +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Body; + +/// +/// Component that allows a body to deal or receive modified damage amounts based on their light level. +/// Requires to function. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class LightLevelDamageMultComponent : Component +{ + [DataField] + public float DarkReceivedMultiplier = 1.0f; + [DataField] + public float LightReceivedMultiplier = 1.0f; + [DataField] + public float LightDealtMultiplier = 1.0f; + [DataField] + public float DarkDealtMultiplier = 1.0f; + + [DataField] + public DamageModifierSet? DarkReceivedModifiers = default!; + [DataField] + public DamageModifierSet? LightReceivedModifiers = default!; + [DataField] + public DamageModifierSet? DarkDealtModifiers = default!; + [DataField] + public DamageModifierSet? LightDealtModifiers = default!; +} diff --git a/Content.Shared/_DV/Body/LightLevelHealthComponent.cs b/Content.Shared/_DV/Body/LightLevelHealthComponent.cs new file mode 100644 index 0000000000..d9d766c45a --- /dev/null +++ b/Content.Shared/_DV/Body/LightLevelHealthComponent.cs @@ -0,0 +1,58 @@ +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Body; + +/// +/// Component that allows a body to have health that is affected by light levels. +/// Either damaged or healed by certain light levels. +/// This is used for the Skia, which is a creature that is harmed by light. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class LightLevelHealthComponent : Component +{ + /// + /// Level of light that, when below, we are considered in darkness. + /// + [DataField] + public float DarkThreshold = 0.2f; + /// + /// Level of light that, when above, we are considered in light. + /// + [DataField] + public float LightThreshold = 0.8f; + /// + /// Amount of health or damage per second when in darkness. Positive values harm, negative values heal. + /// + [DataField(required: true)] + public DamageSpecifier DarkDamage = default!; + /// + /// Amount of health or damage per second when in light. Positive values harm, negative values heal. + /// + [DataField(required: true)] + public DamageSpecifier LightDamage = default!; + /// + /// Movement speed multiplier when in darkness. + /// + [DataField] + public float DarkMovementSpeedMultiplier = 1.0f; + /// + /// Movement speed multiplier when in light. + /// + [DataField] + public float LightMovementSpeedMultiplier = 1.0f; + /// + /// Sound to play when the entity is damaged by light or darkness. + /// + [DataField] + public SoundSpecifier SizzleSoundPath = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); + + /// + /// The current light threshhold for this component. + /// -1 for darkness, 1 for light. + /// 0 for neither. + /// + [DataField] + public int CurrentThreshold = 0; +} diff --git a/Content.Shared/_DV/Body/SharedLightLevelHealthSystem.cs b/Content.Shared/_DV/Body/SharedLightLevelHealthSystem.cs new file mode 100644 index 0000000000..415b101d97 --- /dev/null +++ b/Content.Shared/_DV/Body/SharedLightLevelHealthSystem.cs @@ -0,0 +1,102 @@ +using Content.Shared._DV.Body; +using Content.Shared._DV.Light; +using Content.Shared.Damage; +using Content.Shared.Mobs.Systems; +using Content.Shared.Movement.Systems; +using Content.Shared.Popups; +using Content.Shared.Weapons.Melee.Events; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Timing; + +namespace Content.Shared._DV.Body; + +public abstract class SharedLightLevelHealthSystem : EntitySystem +{ + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnGetMoveSpeedModifiers); + SubscribeLocalEvent(OnDamageModify); + SubscribeLocalEvent(OnGetMeleeDamage); + } + + public int CurrentThreshold(float lightLevel, LightLevelHealthComponent comp) + { + bool lowLight = lightLevel < comp.DarkThreshold; + bool highLight = lightLevel > comp.LightThreshold; + return lowLight && !highLight ? -1 + : !lowLight && highLight ? 1 + : 0; + } + + public void TryDealDamage(Entity target, DamageSpecifier damage) + { + if (damage.AnyPositive()) + { + _audio.PlayPvs(target.Comp.SizzleSoundPath, target); + } + _damageable.TryChangeDamage(target, damage, true, false); + } + + private void OnGetMoveSpeedModifiers(Entity ent, ref RefreshMovementSpeedModifiersEvent args) + { + if (!TryComp(ent, out var lightReactive)) + return; + + if (lightReactive.CurrentLightLevel < ent.Comp.DarkThreshold) + args.ModifySpeed(ent.Comp.DarkMovementSpeedMultiplier); + else if (lightReactive.CurrentLightLevel > ent.Comp.LightThreshold) + args.ModifySpeed(ent.Comp.LightMovementSpeedMultiplier); + } + + private void OnDamageModify(Entity ent, ref DamageModifyEvent args) + { + if (!TryComp(ent, out var lightHealth)) + return; + + // On the receiving end. + args.Damage *= lightHealth.CurrentThreshold switch + { + -1 => ent.Comp.DarkReceivedMultiplier, + 1 => ent.Comp.LightReceivedMultiplier, + _ => 1.0f + }; + + var modifiers = lightHealth.CurrentThreshold switch + { + -1 => ent.Comp.DarkReceivedModifiers, + 1 => ent.Comp.LightReceivedModifiers, + _ => null + }; + + if (modifiers != null) + args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, modifiers); + } + + private void OnGetMeleeDamage(Entity ent, ref GetMeleeDamageEvent args) + { + if (!TryComp(ent, out var lightHealth)) + return; + + args.Damage *= lightHealth.CurrentThreshold switch + { + -1 => ent.Comp.DarkDealtMultiplier, + 1 => ent.Comp.LightDealtMultiplier, + _ => 1.0f + }; + + var modifiers = lightHealth.CurrentThreshold switch + { + -1 => ent.Comp.DarkDealtModifiers, + 1 => ent.Comp.LightDealtModifiers, + _ => null + }; + + if (modifiers != null) + args.Damage = DamageSpecifier.ApplyModifierSet(args.Damage, modifiers); + } + +} diff --git a/Content.Shared/_DV/Light/BreakLightsOnSpawnComponent.cs b/Content.Shared/_DV/Light/BreakLightsOnSpawnComponent.cs new file mode 100644 index 0000000000..ae15820281 --- /dev/null +++ b/Content.Shared/_DV/Light/BreakLightsOnSpawnComponent.cs @@ -0,0 +1,19 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Light; + +[RegisterComponent, NetworkedComponent] +public sealed partial class BreakLightsOnSpawnComponent : Component +{ + /// + /// The radius in which lights will be broken. + /// + [DataField] + public float Radius = 10f; + + /// + /// If true, lights will only be broken if the entity has line of sight to them. + /// + [DataField] + public bool LineOfSight = false; +} diff --git a/Content.Shared/_DV/Light/LightReactiveComponent.cs b/Content.Shared/_DV/Light/LightReactiveComponent.cs new file mode 100644 index 0000000000..10838ce6cc --- /dev/null +++ b/Content.Shared/_DV/Light/LightReactiveComponent.cs @@ -0,0 +1,44 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Light; + +/// +/// A component that reacts to changes in light levels. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedLightReactiveSystem))] +public sealed partial class LightReactiveComponent : Component +{ + /// + /// The frequency at which the component checks for light level changes. + /// There should be very little reason it should be higher than this. + /// + [DataField] + public TimeSpan UpdateFrequency = TimeSpan.FromSeconds(1); + + /// + /// Whether the component should only update while the entity is alive. + /// If false, it will update even if the entity is dead. + /// + [DataField] + public bool OnlyWhileAlive = true; + + /// + /// Should this update its light level automatically, or only when asked to by another system? + /// If true, it will update its light level automatically. + /// If false, it will only update when explicitly requested. + /// + [DataField] + public bool Manual = false; + + /// + /// The next time the component should update. + /// + [AutoNetworkedField] + public TimeSpan NextUpdate = TimeSpan.Zero; + + /// + /// The current light level of this entity. + /// + [DataField] + public float CurrentLightLevel = 0f; +} diff --git a/Content.Shared/_DV/Light/SharedLightReactiveSystem.cs b/Content.Shared/_DV/Light/SharedLightReactiveSystem.cs new file mode 100644 index 0000000000..468c6f203c --- /dev/null +++ b/Content.Shared/_DV/Light/SharedLightReactiveSystem.cs @@ -0,0 +1,109 @@ +using Content.Shared.Mobs.Systems; +using Content.Shared.Physics; +using Robust.Shared.Map.Components; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Timing; +using System.Linq; +using System.Numerics; + +namespace Content.Shared._DV.Light; + +public abstract class SharedLightReactiveSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + + private EntityQuery _lightReactive; + + public override void Initialize() + { + base.Initialize(); + _lightReactive = GetEntityQuery(); + } + + public override void Update(float frameTime) + { + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (_timing.CurTime < comp.NextUpdate) + return; + comp.NextUpdate = _timing.CurTime + TimeSpan.FromSeconds(1); + if (_mobState.IsDead(uid) && comp.OnlyWhileAlive) + continue; // Don't apply damage / healing if the mob is dead + // Get the light level at the entity's position + comp.CurrentLightLevel = GetLightLevelForPoint(uid); + } + } + + public abstract HashSet> GetLights(EntityUid targetEntity); + + /// + /// Gets the current light level of an entity. + /// + /// + /// This is a cached value that is updated periodically. + /// I could add arguments to either: Force check if it's not a ReactiveComponent, or force update, + /// but I don't need them so you don't get them. Add it if you want it. + /// + public float GetLightLevel(EntityUid uid, bool forceUpdate = false) + { + if (_lightReactive.TryComp(uid, out LightReactiveComponent? comp)) + { + if (forceUpdate) + comp.CurrentLightLevel = GetLightLevelForPoint(uid); + return comp.CurrentLightLevel; + } + return 0.0f; + } + + /// + /// Gets the light level at a specific point in the world. + /// Avoid calling this too often, as it can be expensive. + /// + public float GetLightLevelForPoint(EntityUid uid) + { + float val = 0.0f; + // Get the current map entity so we can get a MapLightComponent from it if it has one + var map = _transform.GetMap(uid); + if (TryComp(map, out MapLightComponent? mapLight)) + val += (mapLight.AmbientLightColor.R + mapLight.AmbientLightColor.G + mapLight.AmbientLightColor.B) / 3f; + var pos = _transform.GetWorldPosition(uid); + + foreach (var (lightUid, lightComp) in GetLights(uid)) + { + // Ensure we're on the same grid as the light source + if (_transform.GetMap(lightUid) != map) + continue; + + // Ensure we're within the light's radius. + var lightPos = _transform.GetWorldPosition(lightUid); + var sqrDistance = Vector2.DistanceSquared(pos, lightPos); + if (sqrDistance > lightComp.Radius * lightComp.Radius) + continue; + + if (sqrDistance < 0.01f) + { + // If we're right on top of the light, just add its full energy value. + val += lightComp.Energy; + continue; + } + + // Collision ray check from the entity to the light source + var ray = new CollisionRay(pos, (lightPos - pos).Normalized(), (int)CollisionGroup.Opaque); + var hit = _physics.IntersectRay(_transform.GetMapId(uid), ray, MathF.Sqrt(sqrDistance) - 0.5f, returnOnFirstHit: true); + if (hit.Any() && hit.First().Distance != 0) + continue; + // If we reach here, the light is unobstructed and within range, calculate a light value to add. + val += lightComp.Energy * (1.0f - sqrDistance / (lightComp.Radius * lightComp.Radius)); + } + + + return val; + } +} diff --git a/Content.Shared/_Goobstation/FlashEvents.cs b/Content.Shared/_Goobstation/FlashEvents.cs new file mode 100644 index 0000000000..2d4ce49035 --- /dev/null +++ b/Content.Shared/_Goobstation/FlashEvents.cs @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared.Inventory; + +namespace Content.Shared._Goobstation.Flashbang; + +public sealed class FlashDurationMultiplierEvent : EntityEventArgs, IInventoryRelayEvent +{ + public float Multiplier = 1f; + + public SlotFlags TargetSlots => SlotFlags.EYES | SlotFlags.HEAD | SlotFlags.MASK; +} diff --git a/Content.Shared/_Goobstation/Inventory/GoobInventorySystem.Relays.cs b/Content.Shared/_Goobstation/Inventory/GoobInventorySystem.Relays.cs new file mode 100644 index 0000000000..6a7a2fa602 --- /dev/null +++ b/Content.Shared/_Goobstation/Inventory/GoobInventorySystem.Relays.cs @@ -0,0 +1,39 @@ + +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 GoobBot +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Solstice +// SPDX-FileCopyrightText: 2025 SolsticeOfTheWinter +// SPDX-FileCopyrightText: 2025 gus +// SPDX-FileCopyrightText: 2025 pheenty +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared._Goobstation.Flashbang; +using Content.Shared.Inventory; +using Content.Shared.Inventory.Events; + +namespace Content.Shared._Goobstation.Inventory; + +public partial class GoobInventorySystem +{ + [Dependency] private readonly InventorySystem _inventorySystem = default!; + + public void InitializeRelays() + { + base.Initialize(); + SubscribeLocalEvent(RelayInventoryEvent); + SubscribeLocalEvent>(RefRelayInventoryEvent); + SubscribeLocalEvent>(RefRelayInventoryEvent); + } + + private void RefRelayInventoryEvent(EntityUid uid, InventoryComponent component, ref T args) where T : IInventoryRelayEvent + { + _inventorySystem.RelayEvent((uid, component), ref args); + } + + private void RelayInventoryEvent(EntityUid uid, InventoryComponent component, T args) where T : IInventoryRelayEvent + { + _inventorySystem.RelayEvent((uid, component), args); + } +} diff --git a/Content.Shared/_Goobstation/Inventory/GoobInventorySystem.cs b/Content.Shared/_Goobstation/Inventory/GoobInventorySystem.cs new file mode 100644 index 0000000000..981c28bf2d --- /dev/null +++ b/Content.Shared/_Goobstation/Inventory/GoobInventorySystem.cs @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +namespace Content.Shared._Goobstation.Inventory; + +public sealed partial class GoobInventorySystem : EntitySystem +{ + + public override void Initialize() + { + base.Initialize(); + InitializeRelays(); + } +} diff --git a/Content.Shared/_Goobstation/Overlays/BaseVisionOverlayComponent.cs b/Content.Shared/_Goobstation/Overlays/BaseVisionOverlayComponent.cs new file mode 100644 index 0000000000..5469c8f225 --- /dev/null +++ b/Content.Shared/_Goobstation/Overlays/BaseVisionOverlayComponent.cs @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +namespace Content.Shared._Goobstation.Overlays; + +public abstract partial class BaseVisionOverlayComponent : 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/_Goobstation/Overlays/NightVisionComponent.cs b/Content.Shared/_Goobstation/Overlays/NightVisionComponent.cs new file mode 100644 index 0000000000..feeeb1109c --- /dev/null +++ b/Content.Shared/_Goobstation/Overlays/NightVisionComponent.cs @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared.Actions; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Goobstation.Overlays; + +[RegisterComponent, NetworkedComponent] +public sealed partial class NightVisionComponent : SwitchableVisionOverlayComponent +{ + public override EntProtoId? ToggleAction { get; set; } = "ToggleNightVision"; + + public override Color Color { get; set; } = Color.FromHex("#98FB98"); +} + +public sealed partial class ToggleNightVisionEvent : InstantActionEvent; diff --git a/Content.Shared/_Goobstation/Overlays/SharedNightVisionSystem.cs b/Content.Shared/_Goobstation/Overlays/SharedNightVisionSystem.cs new file mode 100644 index 0000000000..ac0ba6b9e9 --- /dev/null +++ b/Content.Shared/_Goobstation/Overlays/SharedNightVisionSystem.cs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +namespace Content.Shared._Goobstation.Overlays; + +public sealed class SharedNightVisionSystem : SwitchableOverlaySystem; diff --git a/Content.Shared/_Goobstation/Overlays/SharedThermalVisionSystem.cs b/Content.Shared/_Goobstation/Overlays/SharedThermalVisionSystem.cs new file mode 100644 index 0000000000..ae5be95211 --- /dev/null +++ b/Content.Shared/_Goobstation/Overlays/SharedThermalVisionSystem.cs @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +namespace Content.Shared._Goobstation.Overlays; + +public sealed class SharedThermalVisionSystem : SwitchableOverlaySystem; diff --git a/Content.Shared/_Goobstation/Overlays/SwitchableOverlaySystem.cs b/Content.Shared/_Goobstation/Overlays/SwitchableOverlaySystem.cs new file mode 100644 index 0000000000..31f5406d45 --- /dev/null +++ b/Content.Shared/_Goobstation/Overlays/SwitchableOverlaySystem.cs @@ -0,0 +1,202 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared._Goobstation.Flashbang; +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._Goobstation.Overlays; + +public abstract class SwitchableOverlaySystem : EntitySystem // this should get move to a white module if we ever do anything with forks.. + where TComp : SwitchableVisionOverlayComponent + 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(OnToggle); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnGetItemActions); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnHandleState); + SubscribeLocalEvent(OnGetFlashMultiplier); + SubscribeLocalEvent>(OnGetInventoryFlashMultiplier); + } + + private void OnGetFlashMultiplier(Entity ent, ref FlashDurationMultiplierEvent args) + { + if (!ent.Comp.IsEquipment) + args.Multiplier *= GetFlashMultiplier(ent); + } + + private void OnGetInventoryFlashMultiplier(Entity ent, + ref InventoryRelayedEvent args) + { + if (ent.Comp.IsEquipment) + args.Args.Multiplier *= GetFlashMultiplier(ent); + } + + private float GetFlashMultiplier(TComp comp) + { + if (!comp.IsActive && (comp.PulseTime <= 0f || comp.PulseAccumulator >= comp.PulseTime)) + return 1f; + + return comp.FlashDurationMultiplier; + } + + 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(); + + 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, + FlashDurationMultiplier = component.FlashDurationMultiplier, + 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.FlashDurationMultiplier = state.FlashDurationMultiplier; + 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, + component.IsEquipment ? Transform(uid).ParentUid : uid, + component.IsActive); + } + + private void OnGetItemActions(Entity ent, ref GetItemActionsEvent args) + { + if (ent.Comp.IsEquipment && 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) + { + if (!component.IsEquipment) + _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 is { IsEquipment: false, ToggleActionEntity: null, ToggleAction: not 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/_Goobstation/Overlays/SwitchableVisionOverlayComponent.cs b/Content.Shared/_Goobstation/Overlays/SwitchableVisionOverlayComponent.cs new file mode 100644 index 0000000000..76adf293fc --- /dev/null +++ b/Content.Shared/_Goobstation/Overlays/SwitchableVisionOverlayComponent.cs @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared._Goobstation.Overlays; + +public abstract partial class SwitchableVisionOverlayComponent : BaseVisionOverlayComponent +{ + [DataField] + public bool IsActive; + + [DataField] + public bool DrawOverlay = true; + + /// + /// Whether it should grant equipment enhanced vision or is it mob vision + /// + [DataField] + public bool IsEquipment; + + /// + /// If it is greater than 0, overlay isn't toggled but pulsed instead + /// + [DataField] + public float PulseTime; + + [ViewVariables(VVAccess.ReadOnly)] + public float PulseAccumulator; + + [DataField] + public float FlashDurationMultiplier = 1f; + + [DataField] + public SoundSpecifier? ActivateSound = new SoundPathSpecifier("/Audio/_White/Items/Goggles/activate.ogg"); + + [DataField] + public SoundSpecifier? DeactivateSound = new SoundPathSpecifier("/Audio/_White/Items/Goggles/deactivate.ogg"); + + [DataField] + public virtual EntProtoId? ToggleAction { get; set; } + + [ViewVariables] + public EntityUid? ToggleActionEntity; +} + +[Serializable, NetSerializable] +public sealed class SwitchableVisionOverlayComponentState : IComponentState +{ + public Color Color; + public bool IsActive; + public float FlashDurationMultiplier; + public SoundSpecifier? ActivateSound; + public SoundSpecifier? DeactivateSound; + public EntProtoId? ToggleAction; + public float LightRadius; +} diff --git a/Content.Shared/_Goobstation/Overlays/ThermalVisionComponent.cs b/Content.Shared/_Goobstation/Overlays/ThermalVisionComponent.cs new file mode 100644 index 0000000000..136a701d95 --- /dev/null +++ b/Content.Shared/_Goobstation/Overlays/ThermalVisionComponent.cs @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Armok <155400926+ARMOKS@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 Misandry +// SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +// SPDX-FileCopyrightText: 2025 gus +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +using Content.Shared.Actions; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._Goobstation.Overlays; + +[RegisterComponent, NetworkedComponent] +public sealed partial class ThermalVisionComponent : SwitchableVisionOverlayComponent +{ + public override EntProtoId? ToggleAction { get; set; } = "ToggleThermalVision"; + + public override Color Color { get; set; } = Color.FromHex("#d06764"); + + [DataField] + public float LightRadius = 2f; +} + +public sealed partial class ToggleThermalVisionEvent : InstantActionEvent; diff --git a/Resources/Audio/_DV/Effects/attributions.yml b/Resources/Audio/_DV/Effects/attributions.yml index 6a4a98a7ee..090bb2ba8f 100644 --- a/Resources/Audio/_DV/Effects/attributions.yml +++ b/Resources/Audio/_DV/Effects/attributions.yml @@ -2,3 +2,7 @@ license: "CC-BY-NC-3.0" copyright: "Freesound user BristolStories" source: "https://freesound.org/people/BristolStories/sounds/65915/" +- files: ["creepyshriek.ogg"] + license: "CC-BY-NC-3.0" + copyright: "Aurorastation" + source: "https://github.com/Aurorastation/Aurora.3/blob/master/sound/effects/creepyshriek.ogg" diff --git a/Resources/Audio/_DV/Effects/creepyshriek.ogg b/Resources/Audio/_DV/Effects/creepyshriek.ogg new file mode 100644 index 0000000000000000000000000000000000000000..86933130d5df41dcf1ce527fc3e844c0902e5d00 GIT binary patch literal 45042 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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/_White/Items/Goggles/attributions.yml b/Resources/Audio/_White/Items/Goggles/attributions.yml new file mode 100644 index 0000000000..35a9a7b38b --- /dev/null +++ b/Resources/Audio/_White/Items/Goggles/attributions.yml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- 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/_White/Items/Goggles/deactivate.ogg b/Resources/Audio/_White/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/_DV/abilities/psionic.ftl b/Resources/Locale/en-US/_DV/abilities/psionic.ftl index b8854b545e..3cfe306558 100644 --- a/Resources/Locale/en-US/_DV/abilities/psionic.ftl +++ b/Resources/Locale/en-US/_DV/abilities/psionic.ftl @@ -24,6 +24,7 @@ psionic-power-precognition-breaker-flip-result-message = You see torches snuff a psionic-power-precognition-bureaucratic-error-result-message = You see a vision of yourself trapped in a room, trying to solve a puzzle with both missing and duplicate pieces. psionic-power-precognition-clerical-error-result-message = You see faces you once knew being obscured in a fog of static, identities lost. psionic-power-precognition-closet-skeleton-result-message = You hear a crackling laugh echo and clinking bones in the dusty recesses of the station. +psionic-power-precognition-skia-result-message = The shadows around you gnash and scratch at you, a great beast of the noösphere is stalking you. You feel its breath on your neck. psionic-power-precognition-dragon-spawn-result-message = Reality around you bulges and breaks as a great beast cries for war. The smell of salty sea and blood fills the air. psionic-power-precognition-colossus-spawn-result-message = You see the vast shadow of a monstrosity so large that it casts all beneath it into darkness. The noösphere shifts precariously in its wake. psionic-power-precognition-ninja-spawn-result-message = You see a vision of shadows brought to life, hounds of war howling their cries as they chase it through dark corners of the station. diff --git a/Resources/Locale/en-US/_DV/ghost/roles/skia.ftl b/Resources/Locale/en-US/_DV/ghost/roles/skia.ftl new file mode 100644 index 0000000000..ee00929f66 --- /dev/null +++ b/Resources/Locale/en-US/_DV/ghost/roles/skia.ftl @@ -0,0 +1,11 @@ +ghost-role-information-skia-name = Skia +ghost-role-information-skia-description = The fates have cut the thread of a soul, stalk the shadows to find them and reap what is owed. +ghost-role-information-skia-rules = You are a [color=red][bold]Solo Antagonist[/bold][/color]. + You may kill your target, you do not need to ensure they stay dead. + You should avoid attacking those other than your target, except to protect yourself or to ensure your target dies. + +skia-role-briefing = + You are a Skia, a Shade of the Noösphere. + You are bound to darkness and shadows, use the darkness to your advantage. + Find your prey and reap their soul (kill them). Avoid ending the lives of innocents. +skia-round-end-name = Skia diff --git a/Resources/Locale/en-US/_DV/objectives/conditions/skia.ftl b/Resources/Locale/en-US/_DV/objectives/conditions/skia.ftl new file mode 100644 index 0000000000..5ba9091414 --- /dev/null +++ b/Resources/Locale/en-US/_DV/objectives/conditions/skia.ftl @@ -0,0 +1,3 @@ +objective-issuer-skia = [color=#c7e1eb]The Fates[/color] +objective-condition-reap-soul-title = Sever the soul of {$targetName}, {CAPITALIZE($job)}. +objective-condition-reap-soul-reroll-message = The Fates have severed another soul. Your new target is {$targetName}, {CAPITALIZE($job)}. diff --git a/Resources/Locale/en-US/_DV/prototypes/roles/antags.ftl b/Resources/Locale/en-US/_DV/prototypes/roles/antags.ftl index b7c83ee4b7..be47429361 100644 --- a/Resources/Locale/en-US/_DV/prototypes/roles/antags.ftl +++ b/Resources/Locale/en-US/_DV/prototypes/roles/antags.ftl @@ -22,5 +22,8 @@ roles-antag-nuclear-operative-corpsman-objective = The team's combat medic taske roles-antag-menace-skeleton-name = Menace Skeleton roles-antag-menace-skeleton-objective = Rattle 'Em +roles-antag-skia-name = Skia +roles-antag-skia-objective = Rend souls from the living. + roles-antag-syndicate-armsdealer-name = Arms Dealer roles-antag-syndicate-armsdealer-objective = Sell your firearms and stay away from the law. diff --git a/Resources/Locale/en-US/_DV/recipes/tags.ftl b/Resources/Locale/en-US/_DV/recipes/tags.ftl index 0f7295a3b2..835d8d6895 100644 --- a/Resources/Locale/en-US/_DV/recipes/tags.ftl +++ b/Resources/Locale/en-US/_DV/recipes/tags.ftl @@ -34,3 +34,6 @@ construction-graph-tag-carp-plushie = carp plushie # Moth plushie construction-graph-tag-mothroach-hide = mothroach hide + +# Hoodies +construction-graph-tag-hoodie = hoodie diff --git a/Resources/Locale/en-US/_white/research/technologies.ftl b/Resources/Locale/en-US/_white/research/technologies.ftl new file mode 100644 index 0000000000..b8108d7727 --- /dev/null +++ b/Resources/Locale/en-US/_white/research/technologies.ftl @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +research-technology-night-vision = Night Vision Technology +research-technology-thermal-vision = Thermal Vision Technology diff --git a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml index 0dd1dd6888..79c807f9c9 100644 --- a/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml +++ b/Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml @@ -229,25 +229,6 @@ - type: IdentityBlocker coverage: EYES -#Make a scanner category when these actually function and we get the trayson -- type: entity - parent: ClothingEyesBase - id: ClothingEyesGlassesThermal - name: optical thermal scanner - description: Thermals in the shape of glasses. - components: - - type: Sprite - sprite: Clothing/Eyes/Glasses/thermal.rsi - - type: Clothing - sprite: Clothing/Eyes/Glasses/thermal.rsi - - type: Armor - modifiers: - coefficients: - Heat: 0.95 - - type: GroupExamine - - type: IdentityBlocker - coverage: EYES - - type: entity parent: ClothingEyesBase id: ClothingEyesGlassesChemical diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml index a29eb93dbc..2cd0e82d39 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/misc.yml @@ -82,6 +82,10 @@ sprite: Clothing/OuterClothing/Misc/black_hoodie.rsi - type: Clothing sprite: Clothing/OuterClothing/Misc/black_hoodie.rsi + - type: Tag # DeltaV - It's a hoodie + tags: + - WhitelistChameleon + - Hoodie - type: entity parent: ClothingOuterBase @@ -93,6 +97,10 @@ sprite: Clothing/OuterClothing/Misc/grey_hoodie.rsi - type: Clothing sprite: Clothing/OuterClothing/Misc/grey_hoodie.rsi + - type: Tag # DeltaV - It's a hoodie + tags: + - WhitelistChameleon + - Hoodie - type: entity parent: ClothingOuterBase @@ -122,6 +130,10 @@ sprite: Clothing/OuterClothing/Misc/chaplain_hoodie.rsi - type: ToggleableClothing clothingPrototype: ClothingHeadHatHoodChaplainHood + - type: Tag # DeltaV - It's a hoodie + tags: + - WhitelistChameleon + - Hoodie - type: entity parent: ClothingOuterBase diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 7ddde13783..0b6cb2199f 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -228,6 +228,7 @@ - Equipment - FauxTiles - Botany # DeltaV + - SpecOpsGoogles # DeltaV - Surgery # Shitmed change - type: EmagLatheRecipes emagDynamicPacks: @@ -442,6 +443,7 @@ - SecurityRubberAmmoStatic - PrisonerSoftsuits - EVASuit + - SpecOpsGoogles # End DeltaV Additions dynamicPacks: - SalvageSecurityBoards diff --git a/Resources/Prototypes/Nyanotrasen/psionicPowers.yml b/Resources/Prototypes/Nyanotrasen/psionicPowers.yml index 9504d259bf..2a3c4646cc 100644 --- a/Resources/Prototypes/Nyanotrasen/psionicPowers.yml +++ b/Resources/Prototypes/Nyanotrasen/psionicPowers.yml @@ -6,6 +6,7 @@ TelegnosisPower: 1 PsionicRegenerationPower: 1 PrecognitionPower: 1 # DeltaV + PsychokineticScreamPower: 1 MassSleepPower: 0.3 NoosphericZapPower: 0.3 # PsionicInvisibilityPower: 0.15 diff --git a/Resources/Prototypes/Roles/Antags/nukeops.yml b/Resources/Prototypes/Roles/Antags/nukeops.yml index 7a3f8c1958..61cf08f141 100644 --- a/Resources/Prototypes/Roles/Antags/nukeops.yml +++ b/Resources/Prototypes/Roles/Antags/nukeops.yml @@ -57,7 +57,7 @@ jumpsuit: ClothingUniformJumpsuitOperative back: ClothingBackpackDuffelSyndicate mask: ClothingMaskGasSyndicate - eyes: ClothingEyesHudSyndicate + eyes: ClothingEyesNightVisionGogglesNukie # Goobstation ears: ClothingHeadsetAltSyndicate gloves: ClothingHandsGlovesCombat outerClothing: ClothingOuterHardsuitSyndie @@ -115,7 +115,7 @@ parent: SyndicateOperativeGearFull equipment: pocket2: AgentUplinkRadio45TC # DeltaV - allows them to buy Agent armor - eyes: ClothingEyesHudSyndicateAgent + eyes: ClothingEyesNightVisionGogglesNukie # Goobstation outerClothing: ClothingOuterCoatCybersunWindbreaker # DeltaV - removal of armor # shoes: ClothingShoesBootsMagSyndie # DeltaV - removal of armor id: SyndiAgentPDA diff --git a/Resources/Prototypes/_DV/Actions/psionic.yml b/Resources/Prototypes/_DV/Actions/psionic.yml index 1b29fc921e..1d19b0fdc9 100644 --- a/Resources/Prototypes/_DV/Actions/psionic.yml +++ b/Resources/Prototypes/_DV/Actions/psionic.yml @@ -211,3 +211,14 @@ checkConsciousness: false - type: InstantAction event: !type:PrecognitionPowerActionEvent + +- type: entity + id: ActionPsychokineticScream + name: Psychokinetic Scream + description: Emit a blood-curdling scream that shatters all lights in the area. + components: + - type: Action + icon: { sprite: _DV/Actions/shatterlights.rsi, state: shatter-lights } + useDelay: 60 + - type: InstantAction + event: !type:ShatterLightsActionEvent diff --git a/Resources/Prototypes/_DV/Actions/skia.yml b/Resources/Prototypes/_DV/Actions/skia.yml new file mode 100644 index 0000000000..06fdd429fe --- /dev/null +++ b/Resources/Prototypes/_DV/Actions/skia.yml @@ -0,0 +1,21 @@ +- type: entity + id: ActionShatterLights + name: Shatter Lights + description: Emit a blood-curdling scream that shatters all lights in the area. + components: + - type: Action + icon: { sprite: _DV/Actions/shatterlights.rsi, state: shatter-lights } + useDelay: 30 + - type: InstantAction + event: !type:ShatterLightsActionEvent + +- type: entity + id: ActionTechnokineticPulse + name: Technokinetic Pulse + description: Unleash a burst of Technokinetic energy, disabling and destroying nearby electronics. + components: + - type: Action + icon: { sprite: _DV/Actions/technokineticpulse.rsi, state: technokinetic-pulse } + useDelay: 60 + - type: InstantAction + event: !type:TechnokineticPulseActionEvent diff --git a/Resources/Prototypes/_DV/Alerts/categories.yml b/Resources/Prototypes/_DV/Alerts/categories.yml new file mode 100644 index 0000000000..99b3615bfb --- /dev/null +++ b/Resources/Prototypes/_DV/Alerts/categories.yml @@ -0,0 +1,2 @@ +- type: alertCategory + id: Light diff --git a/Resources/Prototypes/_DV/Alerts/light.yml b/Resources/Prototypes/_DV/Alerts/light.yml new file mode 100644 index 0000000000..ad5c5212bd --- /dev/null +++ b/Resources/Prototypes/_DV/Alerts/light.yml @@ -0,0 +1,26 @@ +- type: alert + id: LightLevelDarkIcon + category: Light + icons: + - sprite: /Textures/_DV/Interface/Alerts/light_level.rsi + state: dark + name: alerts-light-level-dark-name + description: alerts-light-level-dark-desc + +- type: alert + id: LightLevelNeutralIcon + category: Light + icons: + - sprite: /Textures/_DV/Interface/Alerts/light_level.rsi + state: neutral + name: alerts-light-level-neutral-name + description: alerts-light-level-neutral-desc + +- type: alert + id: LightLevelBrightIcon + category: Light + icons: + - sprite: /Textures/_DV/Interface/Alerts/light_level.rsi + state: bright + name: alerts-light-level-bright-name + description: alerts-light-level-bright-desc diff --git a/Resources/Prototypes/_DV/Entities/Clothing/Head/hoods.yml b/Resources/Prototypes/_DV/Entities/Clothing/Head/hoods.yml index 5e992d97c6..95d40780b1 100644 --- a/Resources/Prototypes/_DV/Entities/Clothing/Head/hoods.yml +++ b/Resources/Prototypes/_DV/Entities/Clothing/Head/hoods.yml @@ -9,3 +9,21 @@ sprite: _DV/Clothing/Head/Hoods/interdynechemhood.rsi - type: Clothing sprite: _DV/Clothing/Head/Hoods/interdynechemhood.rsi + +- type: entity + parent: ClothingHeadBase + id: ClothingHeadHatHoodSkiaHood + categories: [ HideSpawnMenu ] + name: skia hood + description: Smells like Tartarus in here. + components: + - type: Sprite + sprite: _DV/Clothing/Head/Hoods/skia_hoodie.rsi + - type: Clothing + sprite: _DV/Clothing/Head/Hoods/skia_hoodie.rsi + - type: Tag + tags: + - WhitelistChameleon + - type: HideLayerClothing + slots: + - Hair diff --git a/Resources/Prototypes/_DV/Entities/Clothing/OuterClothing/misc.yml b/Resources/Prototypes/_DV/Entities/Clothing/OuterClothing/misc.yml index 3f8c44f9f2..23612a85bf 100644 --- a/Resources/Prototypes/_DV/Entities/Clothing/OuterClothing/misc.yml +++ b/Resources/Prototypes/_DV/Entities/Clothing/OuterClothing/misc.yml @@ -30,3 +30,23 @@ Caustic: 0.40 - type: ToggleableClothing clothingPrototype: ClothingHeadHatInterdyneChemistryHood + +- type: entity + parent: ClothingOuterBaseToggleable + id: ClothingOuterHoodieSkia + name: skia hoodie + description: The robes worn by a shade. + components: + - type: Sprite + sprite: _DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi + - type: Clothing + sprite: _DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi + - type: ToggleableClothing + clothingPrototype: ClothingHeadHatHoodSkiaHood + - type: Construction + graph: SkiaHoodie + node: skiaHoodie + - type: Tag + tags: + - Hoodie + - WhitelistChameleon diff --git a/Resources/Prototypes/_DV/Entities/Effects/weapon_arc.yml b/Resources/Prototypes/_DV/Entities/Effects/weapon_arc.yml new file mode 100644 index 0000000000..4c6a05fb8d --- /dev/null +++ b/Resources/Prototypes/_DV/Entities/Effects/weapon_arc.yml @@ -0,0 +1,29 @@ +- type: entity + id: WeaponArcSkiaLunge + parent: WeaponArcStatic + categories: [ HideSpawnMenu ] + components: + - type: WeaponArcVisuals + - type: Sprite + scale: .5, .5 + layers: + - sprite: _DV/Effects/arcs.rsi + state: skialunge + shader: unshaded + - type: TimedDespawn + lifetime: 0.399 + +- type: entity + id: WeaponArcSkiaSwipe + parent: WeaponArcSlash + categories: [ HideSpawnMenu ] + components: + - type: WeaponArcVisuals + - type: Sprite + scale: .5, .5 + layers: + - sprite: _DV/Effects/arcs.rsi + state: skiaswipe + shader: unshaded + - type: TimedDespawn + lifetime: 0.399 diff --git a/Resources/Prototypes/_DV/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/_DV/Entities/Markers/Spawners/ghost_roles.yml index a5982ba671..b94e12e8e6 100644 --- a/Resources/Prototypes/_DV/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/_DV/Entities/Markers/Spawners/ghost_roles.yml @@ -155,6 +155,22 @@ - !type:OverallPlaytimeRequirement time: 172800 # 48h +- type: entity + categories: [ HideSpawnMenu, Spawner ] + parent: BaseAntagSpawner + id: SpawnPointSkia + name: skia spawn point + components: + - type: GhostRole + name: ghost-role-information-skia-name + description: ghost-role-information-skia-description + rules: ghost-role-information-skia-rules + mindRoles: + - MindRoleSkia + requirements: # keep in sync with the antag prototype + - !type:OverallPlaytimeRequirement + time: 172800 # 24h + - type: entity categories: [ HideSpawnMenu, Spawner ] parent: BaseAntagSpawner diff --git a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml new file mode 100644 index 0000000000..b5e35b1054 --- /dev/null +++ b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml @@ -0,0 +1,109 @@ +- type: entity + parent: [ SimpleSpaceMobBase, MobCombat ] + id: MobSkia + name: skia + description: A shadow given form, lashing out at anything that comes too close. + components: + - type: Sprite + drawdepth: Mobs + sprite: _DV/Mobs/Animals/skia.rsi + layers: + - map: ["enum.DamageStateVisualLayers.Base"] + state: skia + - type: DamageStateVisuals + states: + Alive: + Base: skia + Dead: + Base: dead + - type: MobState + allowedStates: + - Alive + - Dead + - type: MobThresholds + thresholds: + 0: Alive + 120: Dead + - type: Damageable + damageModifierSet: ManifestedSpirit + damageContainer: BiologicalMetaphysical + - type: Armor + modifiers: + coefficients: + Blunt: 0.4 + Slash: 0.4 + Piercing: 0.4 + Heat: 0.8 + Radiation: 0.2 + Caustic: 0.2 + - type: MovementSpeedModifier + baseWalkSpeed: 2.25 + baseSprintSpeed: 3.75 + - type: CombatMode + - type: MeleeWeapon + soundHit: + path: /Audio/Weapons/Xeno/alien_claw_flesh3.ogg + angle: 90 + animation: WeaponArcSkiaLunge + wideAnimation: WeaponArcSkiaSwipe + wideAnimationRotation: -90 + swingLeft: true + attackRate: 1.0 + range: 2.0 + altDisarm: false + damage: + types: + Slash: 19 + Pierce: 8 + Cold: 8 + - type: NpcFactionMember + factions: + - SimpleHostile + - type: Tag + tags: + - DoorBumpOpener + - type: Speech + speechVerb: Ghost + - type: Insulated + - type: LightReactive + manual: true + - type: LightLevelHealth + darkThreshold: 0.3 + lightThreshold: 0.7 + darkDamage: + groups: + Brute: -5 + Burn: -5 + Toxin: -5 + Airloss: -5 + Genetic: -5 + Metaphysical: -1 + lightDamage: + groups: + Brute: 5 + darkMovementSpeedMultiplier: 1.25 + lightMovementSpeedMultiplier: 1.0 + - type: LightLevelDamageMult + lightReceivedMultiplier: 2.0 + lightDealtMultiplier: 0.5 + - type: Bloodstream + maxBleedAmount: 0 + - type: ShatterLightsAbility + lineOfSight: true + - type: TechnokineticPulseAbility + range: 5.0 + energyConsumption: 20000 + disableDuration: 20.0 + - type: Butcherable + spawned: + - id: Ectoplasm + amount: 1 + - type: Prying + pryPowered: true + force: true + speedModifier: 2.5 # needs to be fast because they'll get ganked otherwise + useSound: + path: /Audio/Items/crowbar.ogg + - type: NightVision + drawOverlay: false + - type: Targeting diff --git a/Resources/Prototypes/_DV/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/_DV/Entities/Structures/Machines/lathe.yml index 93bcc9b58f..721aa65fe6 100644 --- a/Resources/Prototypes/_DV/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/_DV/Entities/Structures/Machines/lathe.yml @@ -89,6 +89,7 @@ - EngineeringWeapons - FauxTiles - Equipment + - SpecOpsGoogles - UpgradeKits - UpgradeKits_Goob - EngineeringHardsuits @@ -130,6 +131,7 @@ dynamicPacks: - Equipment - Mining + - SpecOpsGoogles - SalvageWeapons - SalvageHardsuits - type: Machine @@ -216,6 +218,7 @@ - ScienceEquipment - ScienceClothing - ScienceWeapons + - SpecOpsGoogles - Chemistry - PowerCells - type: Machine diff --git a/Resources/Prototypes/_DV/GameRules/events.yml b/Resources/Prototypes/_DV/GameRules/events.yml index 920e994d74..7c4c3547e2 100644 --- a/Resources/Prototypes/_DV/GameRules/events.yml +++ b/Resources/Prototypes/_DV/GameRules/events.yml @@ -14,6 +14,7 @@ - id: ListeningPost - id: RoboNeuroticist - id: MenaceSkeleton + - id: SkiaSpawn # Replaces upstream meteor events until they're good - type: entity @@ -300,3 +301,33 @@ sound: /Audio/Effects/clang.ogg mindRoles: - MindRoleMenaceSkeleton + +- type: entity + parent: BaseMidRoundAntag + id: SkiaSpawn + components: + - type: StationEvent + weight: 6 + duration: null + minimumPlayers: 30 + - type: PrecognitionResult + message: psionic-power-precognition-skia-result-message + - type: AntagSpawner + prototype: MobSkia + - type: AntagObjectives + objectives: + - SkiaReapObjective + - type: AntagSelection + agentName: skia-round-end-name + definitions: + - spawnerPrototype: SpawnPointSkia + min: 1 + max: 1 + pickPlayer: false + mindRoles: + - MindRoleSkia + components: + - type: EmitSoundOnSpawn + sound: /Audio/_DV/Effects/creepyshriek.ogg + - type: BreakLightsOnSpawn + radius: 10 diff --git a/Resources/Prototypes/_DV/Objectives/skia.yml b/Resources/Prototypes/_DV/Objectives/skia.yml new file mode 100644 index 0000000000..4c31fb0a02 --- /dev/null +++ b/Resources/Prototypes/_DV/Objectives/skia.yml @@ -0,0 +1,25 @@ +- type: entity + parent: BaseObjective + id: SkiaReapObjective + name: Reap the damned soul. + description: Ensure this soul is released from its body at least once. + components: + - type: Objective + difficulty: 1.5 + issuer: objective-issuer-skia + unique: false + icon: + sprite: Mobs/Ghosts/ghost_human.rsi + state: icon + - type: RoleRequirement + roles: + - SkiaRole + - type: KillPersonCondition + requireDead: true + - type: TargetObjective + title: objective-condition-reap-soul-title + - type: PickRandomPerson + onlyChoosableJobs: true + - type: RerollAfterCompletion + rerollObjectivePrototype: SkiaReapObjective + rerollObjectiveMessage: objective-condition-reap-soul-reroll-message diff --git a/Resources/Prototypes/_DV/Recipes/Construction/Graphs/clothing/skia_hoodie.yml b/Resources/Prototypes/_DV/Recipes/Construction/Graphs/clothing/skia_hoodie.yml new file mode 100644 index 0000000000..3f1ada85bd --- /dev/null +++ b/Resources/Prototypes/_DV/Recipes/Construction/Graphs/clothing/skia_hoodie.yml @@ -0,0 +1,21 @@ +- type: constructionGraph + id: SkiaHoodie + start: start + graph: + - node: start + edges: + - to: skiaHoodie + steps: + - tag: Hoodie + name: construction-graph-tag-hoodie + icon: + sprite: Clothing/OuterClothing/Misc/chaplain_hoodie.rsi + state: icon + - tag: Ectoplasm + name: construction-graph-tag-ectoplasm + icon: + sprite: _DV/Mobs/Ghosts/revenant.rsi + state: ectoplasm + doAfter: 10 + - node: skiaHoodie + entity: ClothingOuterHoodieSkia diff --git a/Resources/Prototypes/_DV/Recipes/Construction/clothing.yml b/Resources/Prototypes/_DV/Recipes/Construction/clothing.yml index ac7ecf0673..a1f00039c2 100644 --- a/Resources/Prototypes/_DV/Recipes/Construction/clothing.yml +++ b/Resources/Prototypes/_DV/Recipes/Construction/clothing.yml @@ -45,3 +45,11 @@ targetNode: sharkminnowShoes category: construction-category-clothing objectType: Item + +- type: construction + id: SkiaHoodie + graph: SkiaHoodie + startNode: start + targetNode: skiaHoodie + category: construction-category-clothing + objectType: Item diff --git a/Resources/Prototypes/_DV/Roles/Antags/nukeops.yml b/Resources/Prototypes/_DV/Roles/Antags/nukeops.yml index 6a03bfafa4..914045b067 100644 --- a/Resources/Prototypes/_DV/Roles/Antags/nukeops.yml +++ b/Resources/Prototypes/_DV/Roles/Antags/nukeops.yml @@ -5,7 +5,7 @@ jumpsuit: ClothingUniformJumpsuitOperative back: ClothingBackpackDuffelSyndicate mask: ClothingMaskGasSyndicate - eyes: ClothingEyesHudSyndicate + eyes: ClothingEyesNightVisionGogglesNukie # Goobstation ears: ClothingHeadsetAltSyndicate gloves: ClothingHandsGlovesCombat outerClothing: ClothingOuterVestWeb diff --git a/Resources/Prototypes/_DV/Roles/Antags/skia.yml b/Resources/Prototypes/_DV/Roles/Antags/skia.yml new file mode 100644 index 0000000000..c848d0b659 --- /dev/null +++ b/Resources/Prototypes/_DV/Roles/Antags/skia.yml @@ -0,0 +1,8 @@ +- type: antag + id: Skia + name: roles-antag-skia-name + objective: roles-antag-skia-objective + antagonist: true + requirements: + - !type:OverallPlaytimeRequirement + time: 172800 # 48 hours overall diff --git a/Resources/Prototypes/_DV/Roles/MindRoles/mind_roles.yml b/Resources/Prototypes/_DV/Roles/MindRoles/mind_roles.yml index 6bfb750e59..6d928246e3 100644 --- a/Resources/Prototypes/_DV/Roles/MindRoles/mind_roles.yml +++ b/Resources/Prototypes/_DV/Roles/MindRoles/mind_roles.yml @@ -74,6 +74,19 @@ - type: RoleBriefing briefing: skeleton-role-briefing +- type: entity + parent: BaseMindRoleAntag + id: MindRoleSkia + name: Skia Role + components: + - type: MindRole + roleType: TeamAntagonist + antagPrototype: Skia + exclusiveAntag: true + - type: SkiaRole + - type: RoleBriefing + briefing: skia-role-briefing + - type: entity parent: BaseMindRoleAntag id: MindRoleArmsdealer diff --git a/Resources/Prototypes/_DV/tags.yml b/Resources/Prototypes/_DV/tags.yml index 2635f8e95c..82560e73d3 100644 --- a/Resources/Prototypes/_DV/tags.yml +++ b/Resources/Prototypes/_DV/tags.yml @@ -173,3 +173,6 @@ - type: Tag id: CleaningGrenade + +- type: Tag + id: Hoodie diff --git a/Resources/Prototypes/_Goobstation/Recipes/Lathes/Packs/misc.yml b/Resources/Prototypes/_Goobstation/Recipes/Lathes/Packs/misc.yml new file mode 100644 index 0000000000..5e308ff40e --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Recipes/Lathes/Packs/misc.yml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 PunishedJoe +# SPDX-FileCopyrightText: 2025 Roudenn +# SPDX-FileCopyrightText: 2025 SX_7 +# SPDX-FileCopyrightText: 2025 Ted Lukin <66275205+pheenty@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 pheenty +# +# SPDX-License-Identifier: AGPL-3.0-or-later +## Dynamic +- type: latheRecipePack # WD + id: SpecOpsGoogles + recipes: + - ClothingEyesNightVisionGoggles + - ClothingEyesGlassesThermal \ No newline at end of file diff --git a/Resources/Prototypes/_White/Actions/types.yml b/Resources/Prototypes/_White/Actions/types.yml new file mode 100644 index 0000000000..124829a6c6 --- /dev/null +++ b/Resources/Prototypes/_White/Actions/types.yml @@ -0,0 +1,48 @@ +# SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- type: entity + id: ToggleNightVision + name: Toggle Night Vision + description: Toggles night vision. + categories: [ HideSpawnMenu ] + components: + - type: Action + itemIconStyle: BigAction + priority: -20 + useDelay: 1 + icon: + sprite: _White/Clothing/Eyes/Goggles/nightvision.rsi + state: icon + - type: InstantAction + event: !type:ToggleNightVisionEvent + +- type: entity + id: ToggleThermalVision + name: Toggle Thermal Vision + description: Toggles thermal vision. + categories: [ HideSpawnMenu ] + components: + - type: Action + itemIconStyle: BigAction + priority: -20 + useDelay: 1 + icon: + sprite: _White/Clothing/Eyes/Goggles/thermal.rsi + state: icon + - type: InstantAction + event: !type:ToggleThermalVisionEvent + +- type: entity + id: PulseThermalVision + parent: ToggleThermalVision + name: Pulse Thermal Vision + description: Activate thermal vision temporarily. + categories: [ HideSpawnMenu ] + components: + - type: Action + useDelay: 4 + - type: InstantAction diff --git a/Resources/Prototypes/_White/Entities/Clothing/Eyes/goggles.yml b/Resources/Prototypes/_White/Entities/Clothing/Eyes/goggles.yml new file mode 100644 index 0000000000..964cf293d0 --- /dev/null +++ b/Resources/Prototypes/_White/Entities/Clothing/Eyes/goggles.yml @@ -0,0 +1,94 @@ +# SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Armok <155400926+ARMOKS@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# Night Vision Goggles + +- type: entity + parent: ClothingEyesBase + id: ClothingEyesNightVisionGoggles + name: night vision goggles + description: Now you can see in the dark! + components: + - type: Sprite + sprite: _White/Clothing/Eyes/Goggles/nightvision.rsi + - type: Clothing + sprite: _White/Clothing/Eyes/Goggles/nightvision.rsi + - type: NightVision + flashDurationMultiplier: 2 + isEquipment: true + - type: IdentityBlocker + +- type: entity + parent: [ClothingEyesBase,BaseSyndicateContraband] + id: ClothingEyesNightVisionGogglesSyndie + name: syndicate night vision goggles + description: A high-tech pair of night vision goggles. Has medical analysis technology. + components: + - type: Sprite + sprite: _White/Clothing/Eyes/Goggles/snightvision.rsi + - type: Clothing + sprite: _White/Clothing/Eyes/Goggles/snightvision.rsi + - type: NightVision + flashDurationMultiplier: 2 + isEquipment: true + - type: IdentityBlocker + coverage: EYES + - type: ShowHealthBars + damageContainers: + - Biological + +- type: entity + parent: ClothingEyesNightVisionGogglesSyndie + id: ClothingEyesNightVisionGogglesNukie + suffix: "NukeOps" + components: + - type: ShowSyndicateIcons + +# Thermal Vision Goggles + +- type: entity + parent: ClothingEyesBase + id: ClothingEyesGlassesThermal + name: thermal vision goggles + description: Now you can see everyone! + components: + - type: Sprite + sprite: _White/Clothing/Eyes/Goggles/thermal.rsi + - type: Clothing + sprite: _White/Clothing/Eyes/Goggles/thermal.rsi + - type: ThermalVision + flashDurationMultiplier: 2 + pulseTime: 2 + isEquipment: true + toggleAction: PulseThermalVision + - type: IdentityBlocker + coverage: EYES + +- type: entity + parent: [ClothingEyesBase, BaseSyndicateContraband] + id: ClothingEyesThermalVisionGogglesSyndie + name: thermal vision goggles + description: A high-tech pair of thermal goggles. + components: + - type: Sprite + sprite: _White/Clothing/Eyes/Goggles/sthermals.rsi + - type: Clothing + sprite: _White/Clothing/Eyes/Goggles/sthermals.rsi + - type: ThermalVision + flashDurationMultiplier: 2 + isEquipment: true + toggleAction: ToggleThermalVision + - type: IdentityBlocker + coverage: EYES + +- type: entity + parent: ClothingEyesThermalVisionGogglesSyndie + id: ClothingEyesThermalVisionGogglesNukie + suffix: "NukeOps" + components: + - type: ShowSyndicateIcons + - type: ShowJobIcons + - type: ShowMindShieldIcons diff --git a/Resources/Prototypes/_White/Recipes/Lathes/devices.yml b/Resources/Prototypes/_White/Recipes/Lathes/devices.yml new file mode 100644 index 0000000000..428b892d62 --- /dev/null +++ b/Resources/Prototypes/_White/Recipes/Lathes/devices.yml @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- type: latheRecipe + id: ClothingEyesNightVisionGoggles + result: ClothingEyesNightVisionGoggles + completetime: 2 + materials: + Steel: 200 + Glass: 100 + Silver: 100 + Gold: 100 + +- type: latheRecipe + id: ClothingEyesGlassesThermal + result: ClothingEyesGlassesThermal + completetime: 2 + materials: + Steel: 200 + Glass: 100 + Silver: 100 + Gold: 100 \ No newline at end of file diff --git a/Resources/Prototypes/_White/Research/experimental.yml b/Resources/Prototypes/_White/Research/experimental.yml new file mode 100644 index 0000000000..34b80578da --- /dev/null +++ b/Resources/Prototypes/_White/Research/experimental.yml @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Aiden +# SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 FaDeOkno <143940725+FaDeOkno@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 FaDeOkno +# SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 gluesniffler <159397573+gluesniffler@users.noreply.github.com> +# +# SPDX-License-Identifier: AGPL-3.0-or-later +# Tier 2 + +- type: technology + id: NightVisionTech + name: research-technology-night-vision + icon: + sprite: _White/Clothing/Eyes/Goggles/nightvision.rsi + state: icon + discipline: Experimental + tier: 2 + cost: 10000 + recipeUnlocks: + - ClothingEyesNightVisionGoggles + technologyPrerequisites: + - MagnetsTech + +- type: technology + id: ThermalVisionTech + name: research-technology-thermal-vision + icon: + sprite: _White/Clothing/Eyes/Goggles/thermal.rsi + state: icon + discipline: Experimental + tier: 2 + cost: 10000 + recipeUnlocks: + - ClothingEyesGlassesThermal + technologyPrerequisites: + - MagnetsTech diff --git a/Resources/Prototypes/_White/Shaders/shaders.yml b/Resources/Prototypes/_White/Shaders/shaders.yml new file mode 100644 index 0000000000..fb669441a5 --- /dev/null +++ b/Resources/Prototypes/_White/Shaders/shaders.yml @@ -0,0 +1,13 @@ +# SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Aviu00 <93730715+Aviu00@users.noreply.github.com> +# SPDX-FileCopyrightText: 2025 Eris +# SPDX-FileCopyrightText: 2025 GoobBot +# SPDX-FileCopyrightText: 2025 SX-7 +# SPDX-FileCopyrightText: 2025 Spatison <137375981+Spatison@users.noreply.github.com> +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +- type: shader + id: NightVision + kind: source + path: "/Textures/_White/Shaders/nightvision.swsl" diff --git a/Resources/Textures/_DV/Actions/shatterlights.rsi/meta.json b/Resources/Textures/_DV/Actions/shatterlights.rsi/meta.json new file mode 100644 index 0000000000..86da737b92 --- /dev/null +++ b/Resources/Textures/_DV/Actions/shatterlights.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by TehFlaminTaco (github) for SS14", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "shatter-lights" + } + ] +} diff --git a/Resources/Textures/_DV/Actions/shatterlights.rsi/shatter-lights.png b/Resources/Textures/_DV/Actions/shatterlights.rsi/shatter-lights.png new file mode 100644 index 0000000000000000000000000000000000000000..df34b71dca9e3a09e7defd4fdde325373ba5f899 GIT binary patch literal 760 zcmVz@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2nY!S2~~SjEC2uk zPDw;TR9M69RzYjjKotHGO4+RFs@p>jf#AZnd+;iXf%YQgBrTNEKj5i{Djr(47xA*C zYY`Pe3jG5?i}mQChb&ks{R6uVSOOMuXr)EmEWJDrx?wh*NhVRThkg(yd3iJ6dvD&% zd%y+GBO0&X(tlr1Fo`6dOyo)io5-~-)W&WA0NS1X=m#sC`1mQ)x=J4MBxE&0VMDbv zKck4q0U+>vxJRLIv5B$DUogC0>`#yJ^E0xKq806ZP zbUvu;$I|?a5>tx2caK6$H|qcZ{oYOPINhvA5&Auk(mYpW_CD}@j4VC^04zRyB2->^ z`4j+fxbgye)s)HwRVGnBANNwp+5dfg8K|;~Y2;b;G8@px-f4)v(=b)`PYhs5`tLkh z77X!@2%D`Rb~|Ww!&HMUHu7qtQJMu)XC9H0000z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2oeb!8ykHM#sB~V zwMj%lR9M5+S21YYKoorn-lQ$=tic3CJY>kADIPp#v?04fA?aco2!W8f6igw-OA`pr z?oy9g3W6k)1`i$_Oo|83bvAp=b@=*sUne=G!O-?VV4ZdUz5D-9hTsMiw_HVv0}Q$vql9u5XhgtS*DQ)|Y0)GB?oOOnfo|^u(}eDi zF3f-Dhrow>;QUZwHbP0W0?f{P2Nd8nJLTP0Lb`ef!235&?m0gM5g2so!EzN?j17^y z6Z&0iD@+qAjXlcM?0mTj9lUo?TuOdsngENjQ!+vff!Ro5F?QevN-rLCX^{+G3G-wo zi3u!fymX|*OIVB@#2uyUba#?n%)s?l7L4m_ld6P3+#ylhB-}uu-U5aXq6+|E`prQ< zRH%f&AjmG1!5P>t$81FN@>wFDjm5f(JE9ACOcUycSZKiw6dDoGzoxX>S&;puo6-*9{p?5$RrgY$7w{Y6{S7E(uGsl&wG~?tPxQUv$3SW-tKyC+5F~- zvK4oTe7Q=c;5k1mG_^Gi8r>@RO{y1zOIIrZf4AY_-3G<B|k&noc|GsW{`M2~KYN2bVfmCTG$J=pnR>+?Lq?K-5Z5Hhf`7a> zC1vL;Iv19{8%DR;Uahs25l>U0)>ioZldf7@-+PbGO1Brs;kAB~<}44$*PhKt4i3a7 z!T<~!-4Yol?grU=jxBmW?}V~d?4x#)kdUMwqzBIY=84=-mSTI~*e|DiF?KNM(tm|n zVAhyrmm`i2#d^clM%?1Yw&j zcM6L&a~%6k<$eEZ`j=la*ifd4IjH{RHJ#0j^}+8EXFJw=0jrejD$}@%FC!V-(uY&{ lVPSu$tnw3W)p7s(@e3PZeTZaGW@P{X002ovPDHLkV1huM>+S#m literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Clothing/Head/Hoods/skia_hoodie.rsi/equipped-HELMET.png b/Resources/Textures/_DV/Clothing/Head/Hoods/skia_hoodie.rsi/equipped-HELMET.png new file mode 100644 index 0000000000000000000000000000000000000000..7945becf5e28f8f29cd025f48dfb21158fb90b3f GIT binary patch literal 934 zcmV;X16lluP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2nYlz4y>%1^Z)&P(;9lX(5!!2evx ztI?JA(Qo!gSH710ovbeu58H|Hs^@LIZxW~8iUGhCA}Q(nMgvehY{#YQ!HV;AEmaSU z2f?Yg;{8&2W8HqKtQCZvEY91mNSq!90RUha1W6EVR~QY#Tqyv6iX_IvyQHW{uGD8? z@1+mUltQU05J`z)5Ma>jUJiO)sy>dSgewG|#-S1Cty(kQwe93Lbp{0l0CsH~s5Rr+ z{Y~N36+qE-g}|WKy#Vk)Wzg$h&{)*zg_m}KwG4pmE&@%3f??S9nNeLB6I6`1{Z)ONgrTnFclp06@>H1e!z=l8no+^KO?sa&)El}@HrNm6Bq>#;- zQo!SFrsXix!!*n8m&zCg{Zjy60eq-XXB2(Y&Oa9j9mluY2^J6AF-=Nzo&31U&H2B+6;3N5@1AEEhG7_nVHk#C7=~dO zhGDi~tFOXSxk!bjuURuQnR)XrUbpE8q5WmLpxcOw*MKV)*O|WpZSOkB_gTU zRV48=4q@50KUd0kgfOOlM+nT7@-5r;pT=R56{fZr%?_sNSwSk3J%W507^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10g0sLQ zvY3HEPZ@+6E0)@q0R`DhJbhi+UvhA;=nCCA$gm%1pM|H3V@Sl|w^Po09Wf9%w!cey zn?Y*g^rj1`{I34B3?JW|e#87jV&B<$w%zLfVqHo)DjT*0Y0SQ)>iFtl?#k8d(Y@lI zn1$~|6k4cLWL*9rRFk2>n`Pb+Z3cbc$DVtNI_-Y^-NlrwByyAOcd|hH=c!WyrXHEW zVPzU{z;B=ay@xA}?cV;9v}BsQPIj%xDW&5&5jx+WRWr2`SdG_%a?k4l* zUsXQFz;N4U|EFb_Uq74l?#JTK>h=r8-L{5tDIdG=rS|^+ey$feK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@pr;JNj1^1m%YcIHC7!;n>@PVuShSVaI8C3*z`)ey>EaktaqI0Y$LvQABFF#V zk`j{f44fjiwCO?O$1T&R&ik(YHTjG1o5cMt=c``nAc#y`0vvE|}l8Qb;6Rrmk>jGOBBef_`5 zuj90e&#K!dth{=0^7?D9vtMsdG`l6p5aIMfX#M?cMz6JgDdGL$J*CvY z?zu8l9v5Odx1gT$L1UNki}*`X_nsUL&=i@*Byr72LrcZ&pn-sz3~Q%rgC@(e$JMje z{N8hSy3ncV2CWVjQTA>MPv>`ZxH7vs?5TaPlyLS*-LX^K{_9rkIX$tQSyNDJDfd^- zsZ4V3g=%HgtR&uEa^2n2X>G&)f%(93ufJcKuGKxble0H{;k7$EJk@r73$7M4l78E9 zsQg*w^QA>UoA!L4^l^5J$Kekl_gTEx`nk-OicR0YF4#EfJxhoCq5oC$;6V;%sQGUC zyz$cu6{A)M1@W7zwRb-@XREOoNWXn?c)9W1!euV2^;Om06)evQ)XToN{)&J2EFHxU z5BTC`Ie+@NGziSlzN#(Z_CE8~%M{JjIpv6dL1BD{eY^ z_R<5r4IZ<#A;zCNJL@%@%q%*ag>QOC$<1~7c-C;j wCs`Y|=>fN7BW#_7J!Za=WNJ&ir?;QcgUL$tZU)b3U_xc^boFyt=akR{0Q`nrfdBvi literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Clothing/Head/Hoods/skia_hoodie.rsi/inhand-right.png b/Resources/Textures/_DV/Clothing/Head/Hoods/skia_hoodie.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..47c9aca3eced6a1518adc524c9fadad384203f12 GIT binary patch literal 819 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@pr;JNj1^1m%YcIHC7!;n>@PVuShV$)e!0Dmfq|*h)5S5Q;?~<)j@`E$M2`R8 zmF7{P;<`GgQ-fok&DSko?yTP;pKR~Q|5vE`-OBc(aaB>>Iht=KZAjXccr;Q;SjBdS z{@JWA8(u1Ie9!Lu$fxP^zt7LkPB3RUY`_CYpX&opu6i@CwrBh6J0GWeMfPO%o-6zH zzrgm~oBGn-iwq@H^|#BaZ{Bxe#@$37hG|ngJ+I4)EHIQfTK4-(>&KF@MrMF_&Ua1#RSTi8@>%<`6NvagkG!gp6Q3?k4olBhh4SDnT@3CmvQmgg!9O~*EZRuy;LA`uE?w6 zC8i%L;&1NeU!W1WeRjo%cjp*u*gx!h`b96}-TFJTo!+ze_|@l6@H_u^ue9Q3>z|G?i-@ilWF#kIi>E=Km$JTYH+=4^OiS_7?Pu5d zBiZ0z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2nYlu4EGF%l>h(* znn^@KRCwC$Tia^mNDw`~sF83d4!$HYG7bqb1LF_K|3A(Ln`KuALa?N`E?srHRMi1$)TmLTMvWRZYWzRZIO?g3QRGSq00@+duf6da_0Jciu_xw3FGP&<1#;=zP2v6?rS75v=C4?ujS+CIP_5c8$l-Md|egiP~^;RKJ z>L@2PKQn&(?YsF%f;=hF_b132Pxk>}{O!AmjIS7fo;aNww^^@@tgVVa>#ahk+rwtP zGEw4O-h3**z3;}^-7FGbY?$6EgjxgGIlz+=S?BQQ$Vs}_i{73di$?-WyHINr{;^K6 zEaNC4K)UXpi&HYxqv%j1?u~#@YqPSc+)s>MFBj&AuRMRwlQMBgiPcYxqHt~rVZM`$ zB?wO%U0XsRNESnQ5>E1Wg&g3Hr_sQlVCehC83gjY@TB?S*ZK29-^ak8z#UHurGh)2 zMpw7@sqdsT;LhftRN&BCg{#~9gYw&l6j{#aSSRf22!T**I6|Zdk%*5BCy&#$va3&q zk~816HcXDg{Rf1VtR+2!WyRV>zEgcoLyrA=DZ?CyDJT?;RmxK}on$ zWDI>D>*Zpv0*AnJpGQF!dDl1>6ehv9r^m*#C!^QpB3^-nL;@iaR*sDp-p-P>wqbPd@KbV&Z* z6xgg+ANJ8u>y$vAqnq_AO?ZjY8zD+*3355WQUvfQ%j1BeXqO2=02ug_4|PD95G<2|!jtF@(;N_L zT`C0|MF@ztARhLd1F~CDrK_J1FoE6*vz$dz(*@ciG$0cCjLXa~4b z=GNL-Hg=^vN&e@_FT(@PG918nf^1~if-9wQ?mqeanMyg}>~8jYjTSX8|o+LZsP1Kja6qE-~KEaf6y3zRZ%U(}i^+5`Bv%CAwQMvWRZ zYSgHa?G5}_bZVBBcg*DVRQQT6jWWWhGed(5(}=Lp3EH>X{B<~`PrlX&1$HI*3uxrgxgEp@(P^kl2 ztmn2>%Dk4+wU*_%0GB~B1Eo@vKr&9dGf z`o4*Pfj{|hYQrXkCr#J~N@3{xrK7N2#`}&CSkC9BQ=B)Sw+SxOl@iPO+(gAmA^-q> z&7aM1f`qq(uv;WujLa5HGCSQKe$AhAb*vnD-PyCdS>#;aMDBPRouop%7)9=Q8abCY z(LQ~wML^!{jkB?Hf?a?QNbwy03A>!WdbbGkEy^jx0o(O$^f~(tmbh^FN zigVrUl)~(U1}8o$NUA;~d`O4v9FTM>szb9(2+{#1M+kJfy;Mbbl9!*vc&-CKyJRf| zsNNoA!ky}XQ*l6*Iut%1K%UP_0Y?aH=L51j>`Q^7I<$+19Tg(n)7i5Eq2ki)cJtyOg?W$U*-7#n>rLeACM<# zw+eR72iVl%Qx9e5^LZs_Hs|R7dxF3Is3Zz(>hKQ|6FhoG?_U@I00005P)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2nYl%HF?Xm)Bpei zFG)l}R9M69l~HTkKn%zK?q*?=%QDisq+H0L5DM-0|0sPb6gCzT=FZs71)DSB_7LAy zc1&ZZ>C>VYW9e+^q@N_AC07bMSMJ;>nmFD1`O)iLf>LXYqBlo{Qt-#)nlmdD zwx0`yz$Q!jGZ0MY=1R^GNr`x|K&iE}>0E(AQFChbJa_o`xOfF zYJXm}Q$b|DMH2u!*eFB|&ewPaq)V?zfkNFFp9r}+62eWEp#1aGYl6AS5_cnB_f3}6 zSV6krSU3ZggP>(op0o@2O?fGrjGUB#;qAlTG_&=$&$e-XvJC9zzNoF* zrgOf8`n2Q00D|e<3`S$;XsflGcve>^g)IwgFd8G6&JE6TmJad{;#por%zk@L00000 LNkvXXu0mjf;5$6J literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/inhand-left.png b/Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..098354f8c03a07960b841e26e1c9eb5a08df25f8 GIT binary patch literal 667 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jPK-BC>eK@{Ea{HEjtmSN z`?>!lvI6-E$sR$z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8X6a z5n0T@pr;JNj1^1m%YcIHC7!;n>@PVuSo9SG)tR+{_I>npaSW-r^>$Xg_iF=*WAjVD zrKW05?)vW_^7Mq>H^x8zraxz{(5_gZ;n#lH!~4{Wodun1FFX#rZk#<|eu_?``s?57 zo{IVo9EvRhP7vay&|Y8v9p*bvF)y8v(YyTh{k{Ev&wqJ%RONin!2*$;8?XQ5*~bx= zx32pBEyn7}tM8~tF&NykFM9m>r7eR4(^r3Wk>2f}BVvR7KA4$aeDK7`UbcUtBr}7@ zmNI^Zlw%J{Y>oj1*I#0GSY;ab+}LA**RkcND_4DqE^p9p%9NRYY1<8^115bByF*^B z{k-O!RQ}1Hl~?L#+cGe0sGie)@wZgn%(D&8cTfF)+TP+cyVuUTRi>NTZ@b;Q>g=3y z%z-Ji>(&1&`F9!jari9CUj8QhYjmlDgzHicvFV4kzD)n!c=fw<-K84vatF8 z^w&48wZW1fB>orffBlYqEyz{xUe#K3U0dgv@wdF8_eX)qRIZiRmR>*gLO+{BCBUGw wOm{;aucP+#hP9#woo=hP8qYIjG5x`PJ?7JtyEc8@!1Tc2>FVdQ&MBb@0DsXN%m4rY literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/inhand-right.png b/Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..105b063887ed031c3c9e7e2068afca3e8cabaab7 GIT binary patch literal 678 zcmV;X0$KfuP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2nYl)1lM`l@&Et< z{7FPXRCwC$no&#RKoo^f(#??2DV2y_1+na+^r1h%|NrO@@KsOC265DC%C*^h*9lV^3?;4P)rAM`h;^TCN3rSCQLl;^;|KY=eVwrDanv6Sw9 zr4-8h+eZCi+nNL^kFF2KQerKp20&}y%?H9R4@HxyiG;vF2*gq%noP}J8BoR7=IOT? z;5%X0`Ul|pZa!y0Cj)G|2>^cnI`_7P-r0b%GV2f7-Tr03C`~aOr>>3e+xdYn?*m>l zAjfc=)(#>0$fq|jw*M{?30)|61^hTa@Z}P)eRf?atdw%Wv;XcXFTMWvPx3X94FCWD M07*qoM6N<$f)~vxI{*Lx literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/meta.json b/Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/meta.json new file mode 100644 index 0000000000..3fd46b294a --- /dev/null +++ b/Resources/Textures/_DV/Clothing/OuterClothing/Misc/skia_hoodie.rsi/meta.json @@ -0,0 +1,26 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by TehFlaminTaco (github) for SS14", + "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/_DV/Effects/arcs.rsi/meta.json b/Resources/Textures/_DV/Effects/arcs.rsi/meta.json new file mode 100644 index 0000000000..07f75ed4c3 --- /dev/null +++ b/Resources/Textures/_DV/Effects/arcs.rsi/meta.json @@ -0,0 +1,17 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Created by TehFlaminTaco (github) for SS14", + "states": [ + { + "name": "skialunge" + }, + { + "name": "skiaswipe" + } + ] +} diff --git a/Resources/Textures/_DV/Effects/arcs.rsi/skialunge.png b/Resources/Textures/_DV/Effects/arcs.rsi/skialunge.png new file mode 100644 index 0000000000000000000000000000000000000000..2f273b805960fda455bb7af7b563a84e539afb92 GIT binary patch literal 609 zcmV-n0-pVeP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2oeD(Atwpo`~Uy} zw@E}nR9M5sS51q;Fc6&v4TcQ}3x$Px_5c4G4@K}G78ZJ13{=vCdzv~;(`1s`-MMJf zFz?MrUZw>wZFe|6>GM`f008K%9X$g8+%Kns>vxj^jv!L$;hj|Dy|y!uVjJ#8AGp%5 z|D7?(-~a#uGL3>Su_OXC`nmVm6RT$6UC}Qp0a{sAi%5z8jGj$^o^dzwwrM7NUCmfT zV9pr=BN1++x>m%%w^D*sI+*&hs-SHe=!^lS^`HqHMHH}-Nu^n(5FAHt^lsXzb>^jp zAN$WJi6{Y-S05+^qwlFC$4H1+{rBb9P~%YjL&r8mLgWe${$A^PR*bMALk%oaw4bKj z7qwdkx<82kreA42IK`uNt?NmxePr&JQ^Dzl=sbYVsk0Ok*{6V}|EK+q-UoB(e%#JQ zXiyQcpxAy0xa(w*)%{U5Ci=6o300000NkvXXu0mjfT{-@x literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Effects/arcs.rsi/skiaswipe.png b/Resources/Textures/_DV/Effects/arcs.rsi/skiaswipe.png new file mode 100644 index 0000000000000000000000000000000000000000..6a7d1f35a89b81ad8865ed4f486442ad1ebc0671 GIT binary patch literal 661 zcmV;G0&4wz@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2oeb|6RwPQ0{{R4 z>q$gGR9M69R>5k*KoEUf%+lZ%!73CY9t82=2lW4s{QxPU;2{_gVuj#JBp%Y!gmJP- zO-N65PU24X&Axg2W`RdM;=hM#ll(ke7ESAy-Ir=xz@QEAa5M%0_{IPLra^F%_X!kj zi%|Ch&sQq|KoZ9QfXv!5@0=bxGogM1_Khh|O5le$Y7O)}TRPcMF5pQpcW%-JnD6ar zjnj6coEo46?VHNSnYB2l$4U(}hIB}BM+rU&=17x$ zsfW8}fxHm}#u);DG})K^mKu3ktPAa%%3g{mNMLSCb^btkE`jnr$zu+2bW_~GrBb>T z+&DNG6+{2=VdQBIZ?P_}d0sI#V(9K+#6w??!q@G|i6Q9~e>}fANTUM6dA!YFkE?i9^v`00000NkvXXu0mjf&}b87 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Interface/Alerts/light_level.rsi/bright.png b/Resources/Textures/_DV/Interface/Alerts/light_level.rsi/bright.png new file mode 100644 index 0000000000000000000000000000000000000000..0285258884442b5de63190b73697d7338c0a79c3 GIT binary patch literal 898 zcmV-|1AY97P)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2n7Zo7$AzVV*mgG z*hxe|R7l6QmQ8CDQ51%sNg8Gx8Zv==P!k#w8X+qeVg&^YLg{Z1T)OZ#bZ^)01lL; zS}3F~tfPVT{b2GL^`!4`(ARK$uT4S?va${-)6#*W-VC_#?MFTBzm#ds_!=bb zv7L4#gF%0QX_@3}YXqk!6De8;b0sQN8UQ$@GM?L%u|k0;3|Im%EmJZW^aq**0{r;x zU7XdN1kY~ma?v`VP-#eCfgv!XP-(KAcI1Izf6+S7fbEsy1mv&E~Ls6OAPP-$pY z07PLhZL*R;G_`YnN)(()aC@als=mqa=%cRraQ{pQ7c7u&f_ZlxrfE)vKlo0f@F zDiiu0qTr0Z?V4nss&CHi!SGg{ttV;rwrfPe8KK|7DV3$<013ffXy^Yr1|VNe1CV+A zYyuWzFHDel{j@d`u_@ESPCJma#|f3a=Qd?SRn4yh7nZN4xmP6#;JHn$io|t6g4s>r z_DWHkuDB%@f!}1Q`XHE}F8> z*VYghC1qMTrLwF9m`Sw0ug%hQOvmbjJqm+~hVk*7f(-}#t41oOCeWMwA75Mk Y0B|gWV8FGTxBvhE07*qoM6N<$f_@i}ng9R* literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Interface/Alerts/light_level.rsi/dark.png b/Resources/Textures/_DV/Interface/Alerts/light_level.rsi/dark.png new file mode 100644 index 0000000000000000000000000000000000000000..b9efba368643b4bb74df78d510f8a0ec652e4bfe GIT binary patch literal 859 zcmV-h1Elz@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2n7ZmF;EUmjQ{`w zu}MThR7l6Qmd|S%Q4q&J>yPg166*@lkZ2J?2oXvTxfI&oO6jHlhkuhj_SznEE4ihm zz2p!YQi52d(Ns6ED=X`6B(Vp5&wXk(P{>?dotf|V`(|e6X&^0aP1N&EU=X*sFK$V3 zGoK+HyDwfT-}#TPT3)GAU5VenXlvh>Fm3|FAZ<+w#SLmcldYVs98!J1ea4>MVE4&8O}RVjaN>VtG&$uuI-*pqX{P`Pd~ep| z7=di+)n$jk>niX!@1H90&!4^n;EaC)a2Xym2pd@T7VDd3)pSXak%^`-m5RXYk{5M~ z*=L$^r+cfObzhx<*TA2hejJ4a{?wC_>SgWRzrR;D)al-Ga@--wWNy|JSUDTg)+Dv4 z9w0C3Y>IsfkIOvYF9UFL+`%2)O^_G$sgPC^yIfI&lj0sLZlyRp_>%~pWSPVxk2)M4 z@ZaF~5=fGjJ&8?GlBIZH%WxAIuG5F>ef zl1n=NePxMbDS~kn;30AF(l|6{Uh$xKI1}#-z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2n7Zn1TB!6wfFc5uCVkAUNWQY@r4S^7PXg{Ebet!@Bg%Sb*hvGOO2M1wcf_vD^ zRCc`Ez3ioPNp@%E&CDC=DFMi)s^jl>8^AZ;=i6Ix4_`oz&)>eY@7K>Cy}VVn8O1;6 z$KKyh709kH4Z`wB-*}#& zuK+Fe^7{l^S6F2mNHu3({Z5BPRHU)|l=9N4054fc0i9;3&1Dq)HIr(NRknexE7ax! zon}lq08FTR;?9)CbC;$X?cO&PY0eFWgo-pURo%qzzC7#!0CBQr&i@5WHeW0N0Oeud zn__|ww*Rz1C?slgc^3f5#qD|>_0Mp(fhmg)0Fvzvp^!M|N5skc-QaYw{0nEwqJIyD zB1eii6tpq$F$Vrl@UYf3C=Yui+Z}5SP4N~O#roa%)pVdmbe<`ZBxKB5*T9rTPY7uj zBo#AyaH#Y0utz8)beeGyTUSt#=F8(6_X6-xS)FF6PB~iFAp7!_aZFis{Gt(PV=6vD zQM_a=8a|`+JdVXvF;f=k74XL-T3XlebVH+QhdDldd`x{FZCx=1W2d8a&8RT?1?ZRt cAN{lC59N1lH2wJD(EtDd07*qoM6N<$f_1+(<^TWy literal 0 HcmV?d00001 diff --git a/Resources/Textures/_DV/Mobs/Animals/skia.rsi/dead.png b/Resources/Textures/_DV/Mobs/Animals/skia.rsi/dead.png new file mode 100644 index 0000000000000000000000000000000000000000..d9c9aa0f82f1640ee5030e030440d0711f0d172a GIT binary patch literal 613 zcmV-r0-F7aP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2nY%@9g?9B`v3p| zyGcYrR9M69R84E+Fc5ty^Gvs#|LunDW?`nq0o}BrY2^cb#NFT zw1-x1lO}E*y6na8A~YK5z4_1pEn2i_(V|6*7A^iWI;ULf{kSqEGaMq=_0Pl2NpGDu zz2Jsp;|0SH_LA9b7x#@toHg$o1|#!tGVn_5N{K_!1E}mw&{93*6|t3B@vi4|bJBI4{b@^ErlZH1I5^8Bc~kZ%IR$>o{O?SxWvnl%Tn$rpnw8n*w;D%#!QWGZ2C9)(&$qZjd*kmb6 zPT}ha^VziWvu9mT!PgR-ECm<&>2ts#LZCGkcefxR(e)Ho%O`X_RrN$qDL6#vDYZjp zGetsTad(SAYaE*$wWHX-`f1j$(Yx|Rgl$GHa_o;{YbWuNfo-0{AwoYGVs&%fya_j1 zit_of^PYUa#)tM?z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy32;bRa{vG?BLDy{BLR4&KXw2B00(qQO+^Rk2nPijAoiWusQ>^6 z2uVaiRCwC$THkNtMi3qcytT`6jtu0291&e~QPj&*UmEqbRsZT!@3p$V+*7VXl_DJq z3W|GHd@MCxD{>!Tma#DeFD$LvtYjrw6TaQqZ+`5|06dT9@xLCH&Oe=8{g@T!Zr}fG z?Q7Ak%c~{)&)4s@@DHl~L-XuXfp>zSbl&7}PR=s`0GD(1(0QW*Xr5hWUgtQsdHm^h zj*Sb!IXTZ7o?rT|;rUuZs8`uMbGh2|hj(w)yKjH^3ButJn9HGgcA1TDu8pX}qm%#u zAQJ&H5xIBFq`*QIwm1G$epjO0eewU@nJA9H|F|U^7v)@4`d?L0vevLC=US83VJY!BXu&&<+Jw> zch;^pkF;N>Vl~E~;rSpFF%#1LCu=(f9Covli#It}gOqFfKG=*w)Atnxsf#+fc(XMV z?4#2RnKg-{nXBU*s4>KiJDfwq^VLDT=p1jvg!(3KZ*lrUNf_yd#)-}Y%sfIX=z;Wo zJ-2w+&766VlS-*hezqo&vjK~FAfJQz`nuUVCLTQH_R0(pbv?zu>F2g_i>OLSXFPS6 z^HO|VYmG6Oidme%T)rfzjRfGFoM-Kzr=mQ%2}*b7@|;>w;bFkk4tkX%H0R_zd->}1 zQm`|LBS=25 zJ8Sv?Fcsa*W()@XK2$Y=RIj=B@BPHN*6sfhQ!~?Cv zU#S9o*Ovy4itYnr+QiuztYLzg~VoT$p#=@ zHwiwp$J{kGJU{25Pz|O+R1UuCRdo`5e;kgKn?g0viN&iLM`TBlEy@O&Oo*VaQ)5SM zfUdEtdLy(MQ=3MW)VT>(7696SVoXqK0}L^MvaHMo7|RG1ub}Oqw-g8HP*6t&najcJ zj%k3Y-8*uss z`bRHCNIwcrFG0I@hbHl#6aZFZ5a^M(8=8%!MGM_3&A$=ELHx;aG)jl${ix;f?Sf8!Vvj!p1(6f|A#vZUEL&(lncal=q6(- z3jjk+WWYF@rJ|a?Z|ojGv75dRQz2Ag7T2MRt;K*i9H|pPU|K0QIS_{?M&od_e+FP^ zYB~@2lq%AT(B2q8!%$~M{m|s5iwxMC4X}?+Girh9Ev)JLW>ew-kluf?Fe_EWZw0-| z{XvxlP@C5UQ&M)a=e7A&*VbJQ{C87N&NsZCp zF+uxiIxSu!m0v84V}+>U`NoBiN~w~eUgubq=rJo@Go2QHB+{q)=XH*u69h2m_o3lk zWnbpqV!Ox+HP0@ysSv6Ot9Nk$;cy6vSZ^!q9zOr-?akw?Ie zF#PU8zpoB}uES;wY8rKky3V4g&KKQ}sYXd`kf3g_MmDcI4s8B(oN2Oit6-W)8 zepydK-XK@Uuhbtv4H3=86aynufshIOy3Q$33ykgS+lHk76uwq}K(QuYR--rBAFxaO z%KZUlVALI%?hn{C{*zKF*5}tHDCvnz;%Fx&GSm2f1I%QWut39CSO5S307*qoM6N<$ Ef{NLSZ2$lO literal 0 HcmV?d00001 diff --git a/Resources/Textures/_White/Clothing/Eyes/Goggles/nightvision.rsi/equipped-EYES-off.png b/Resources/Textures/_White/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%7wZ z^T!=~_Ut$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?mzopr E058M1eE(56?F=2Egz13RyoeI)BL=X2J-)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~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*VS)*8>L**f|I28rn!mD1}BQ zi!9KSsclyt1T!`qZMy~(> literal 0 HcmV?d00001 diff --git a/Resources/Textures/_White/Clothing/Eyes/Goggles/nightvision.rsi/inhand-left.png b/Resources/Textures/_White/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!_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/_White/Clothing/Eyes/Goggles/nightvision.rsi/inhand-right.png b/Resources/Textures/_White/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~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#;mG;OXk;vd$@?2>@%NsSE%B literal 0 HcmV?d00001 diff --git a/Resources/Textures/_White/Clothing/Eyes/Goggles/nightvision.rsi/meta.json b/Resources/Textures/_White/Clothing/Eyes/Goggles/nightvision.rsi/meta.json new file mode 100644 index 0000000000..987b20b9af --- /dev/null +++ b/Resources/Textures/_White/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/_White/Clothing/Eyes/Goggles/snightvision.rsi/equipped-EYES-off.png b/Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/equipped-EYES-off.png new file mode 100644 index 0000000000000000000000000000000000000000..4d8f2fdb66c96933032ced96540cfdfa0d28dfcf GIT binary patch literal 462 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9GG!XV7ZFl&wk0|R4& zr;B4q#hkaZe7z4lh%m&vL~Pi!J;JQK`dh*RTh=c7Xs*A~FGHiV{33fUT>h*g5>IaAZJU{0mlZjY^*aA!YCyQ*(m&eKYUQRjwy2gGlvxs7b1bG&K*`Rizq>g0GKGXLRZ@!0d7AiB zr|g^FoXM*n2E0q(9QQoEy@u_+T;sCLO>axzOaEoySamd*LA)$`xXQDN*7xMHdPv|`eu zviU)M>W}73R%%fZtUAxG^Xk{(zk1u;xoeMpll-j|)9-dE-A(c4`@;8o?e!X3BdZjxK`lu4Y=S^qN5vu173WXQSk z%r!MKZ;nM$Pjmf$i{FO(y>H%cIQ6@v$Z%WboLd_-a%!x$B?d-3{p)w`+0^fua?8t# zK3bI5?qB^g_B2<(^-?2a?t?j^Vrgk*pC8`3H-G=Xmp`_xxnD0TCU)p?@%;O-w^#S? zo@t*wtHfmHl8YHz3aw}PTw0U4={f(cn6`EGOh9Xw_iw(xLba-HdKBw~cQ3R!mN6KB zT;{`=z{L>EFoWrU2m=X1U)36l_5SAFKk(mfzc{1T`{0kY>lrNT<(A)EfA*H5arUe) zUtgV?_Wir`@jDL}?zObr=AWJ(9jz93z4TX6<S@n0XwY&cJnqtJb`;SED-q}p?tZ40K%yT3K;ego6L{dK?) z*u>o9U!T*oVvqm+d)xlX|MNFb6!^S*+FqNzpX}Dk$>r>fQF{I1<bP0l+XkKI{Xps literal 0 HcmV?d00001 diff --git a/Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/icon.png b/Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2576acf8a449c43c024d90b880cb51d2182a39fb GIT binary patch literal 512 zcmV+b0{{JqP)cA92ZIWc2v(Cl%rN0$I>@YlT6e{uz3g*Z=AD`M_ss13 zj*oK=H#O&Kb`QV<@Blmj00FyTrBay~%jWWyEtC=icemHR;jwr;J{y3Q5{V>VkYoS= zqhunS7090f5{V=S;JC4vm(At*)#bVET%X-zB(5Np&TvUqMq3YIIWJ}nh4?JP*X?7oA0k+lwd)Uu@+kYwXUsqAe^Vt89p<>YG{j7Vn8?)-(UI%!jUN2dI!Vt zpr)E*0+3MxLb?+HY|Bt=&I$qa_7zC70!j>oBT;M?8CW3zMhSwNilC;VQLlm$!(7iO zLAiWy`wuwwfmlh#+3pI8$BT%S`i|Oq2YVY!@YiG$rgdPLj)k0(PX+Mw_6-1deCgu( zp)025|d(Sm_=LYEWBz8~QrvN)Ud7?|pf8M{f|DGp zz^d^GccR|MHSXT76WRj&hg^S*JOB^C1MmP`2lxSeM%~+*6XH++0000~=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!_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/_White/Clothing/Eyes/Goggles/snightvision.rsi/inhand-right.png b/Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.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~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#;mG;OXk;vd$@?2>@%NsSE%B literal 0 HcmV?d00001 diff --git a/Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/meta.json b/Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.rsi/meta.json new file mode 100644 index 0000000000..987b20b9af --- /dev/null +++ b/Resources/Textures/_White/Clothing/Eyes/Goggles/snightvision.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/_White/Clothing/Eyes/Goggles/sthermals.rsi/equipped-EYES.png b/Resources/Textures/_White/Clothing/Eyes/Goggles/sthermals.rsi/equipped-EYES.png new file mode 100644 index 0000000000000000000000000000000000000000..d60650a5c5e6ac643f80fa001be25d81aff62910 GIT binary patch literal 446 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9GG!XV7ZFl&wk0|R4$ zr;B4q#hkaZ{Qa68B;5C(_nBP8Vx~6fz_M<+gT@=an#?;IQ4BvjP= zXP(Wy)NL73@n8S0yyJF7@kr{lo3ZoyzuhgFd-r|WwpZ2uGm9_jzP{ydY6cJmd=&L2K@d(zMCm)`xIY+(Ira!XiyCb71_9QKSgU>L`L@p`sqP)Q)RsRc_qwJM$nH}u5W$)$s{ z%WZ<6zz^VXc--PAxWz#yX9WjAi{R-XiX5f3ih?@SRyiMsn4IM_Nrgj{yzs)C=lPTW zkLP{zL=h2QbG5yydk7E$gaGdnU}Q^aX|hFLYLw38{xO>QeMTj2@x9t3)3+x|koPF9 zj$&OmC8nwX)N3_2UQLe?KUyWGs~|0Har37?gop&*nwAhvOUP5X`G_GRg4t_{ zrX^%PVo0BreNy{)2dY4}f7nyBFuu*#l_3BwFRv(<4isHCWofF)=-i!W6?_4lyS`~O zp4GHRqd1zbu1@xT6%~MFDkJ66fhT}x6Q(mcIhs5J;9}>L(xDySs6rtR-2=u$&Q5&V z&2n9DxP9`tN1j`r70`844!=}fW5*oebG%vW#AiD!*ZBPW*tZFNKf}fKzvn_8mdo~e zxeW0S&Dug9W{U(ihX6g0Ol4$u*e=13HTc$+aC0+v6(*99vh3$v(ZcV}TLu6y4MQ4# zBE(d+$w1dl0dVF*tLA|L{^xpsKcf&J1PB2_fHwzl4v@xb1_FVDPUrWmc)9n>XTSEY{`*wGJ@BP4Y`g2W{ z@4IJdS4$$NUAC-T6)(0v=IQU!-9o0C?{~LXu3q)Z-*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/_White/Clothing/Eyes/Goggles/sthermals.rsi/inhand-right.png b/Resources/Textures/_White/Clothing/Eyes/Goggles/sthermals.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+*gzr=`xV)W7BD z7WgIbUuIddqrert>EU)yL*kx>{X1yh{dh-RsxpJvySiOlcf;n zfXIf6JpYn!l?xpXT`Q~zopr06im!bN~PV literal 0 HcmV?d00001 diff --git a/Resources/Textures/_White/Clothing/Eyes/Goggles/sthermals.rsi/meta.json b/Resources/Textures/_White/Clothing/Eyes/Goggles/sthermals.rsi/meta.json new file mode 100644 index 0000000000..205508acfa --- /dev/null +++ b/Resources/Textures/_White/Clothing/Eyes/Goggles/sthermals.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/_White/Clothing/Eyes/Goggles/thermal.rsi/equipped-EYES.png b/Resources/Textures/_White/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=> zRF7M8H5dr69j}nF@vcztmTUGcLZM@bZhhV_L6f^To+GC)ukuUkvCh+_+)$V*aYbQomVd z3Im?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_{4n40aY>v4(UjYnXdl9N2msQpJGL%i!ti=d#Wzp$Py!<<~I) literal 0 HcmV?d00001 diff --git a/Resources/Textures/_White/Clothing/Eyes/Goggles/thermal.rsi/icon.png b/Resources/Textures/_White/Clothing/Eyes/Goggles/thermal.rsi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3d5f8ef9b65ccfe2ed64fbc2d10c725c5fad51d2 GIT binary patch literal 510 zcmV~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^O2@kk}_Q@h%{p%Af$|gDeMy z0!0o0fy>j@!r35SKRIv_%m&diTpTEBLGldK&Vt#*SWeIZAVX4t@pvN37|h?^vy>s{ zBOhFT3d1J`R|kCt784yX-vVekwj4sX13&;|pfWW3iAriPteAv@xb1_FVDPUrWmc)9n>XTSEY{`*wGJ@BP4Y`g2W{ z@4IJdS4$$NUAC-T6)(0v=IQU!-9o0C?{~LXu3q)Z-*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/_White/Clothing/Eyes/Goggles/thermal.rsi/inhand-right.png b/Resources/Textures/_White/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+*gzr=`xV)W7BD z7WgIbUuIddqrert>EU)yL*kx>{X1yh{dh-RsxpJvySiOlcf;n zfXIf6JpYn!l?xpXT`Q~zopr06im!bN~PV literal 0 HcmV?d00001 diff --git a/Resources/Textures/_White/Clothing/Eyes/Goggles/thermal.rsi/meta.json b/Resources/Textures/_White/Clothing/Eyes/Goggles/thermal.rsi/meta.json new file mode 100644 index 0000000000..205508acfa --- /dev/null +++ b/Resources/Textures/_White/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/_White/Shaders/nightvision.swsl b/Resources/Textures/_White/Shaders/nightvision.swsl new file mode 100644 index 0000000000..8a3e7706ad --- /dev/null +++ b/Resources/Textures/_White/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; +}