From 3c9328ff9930362f33e644bb4ab1ddf65d898fea Mon Sep 17 00:00:00 2001 From: ScalyChimp <72841710+scaly-chimp@users.noreply.github.com> Date: Sun, 6 Feb 2022 15:59:41 +0000 Subject: [PATCH] Adds portable flasher (#4523) Co-authored-by: Paul Ritter Co-authored-by: metalgearsloth Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- Content.Client/Entry/IgnoredComponents.cs | 1 + .../Trigger/ProximityTriggerVisualizer.cs | 96 +++++++++++ .../Components/TriggerOnProximityComponent.cs | 64 +++++++ .../EntitySystems/TriggerSystem.Proximity.cs | 158 ++++++++++++++++++ .../Explosion/EntitySystems/TriggerSystem.cs | 37 ++-- .../Components/FlashOnTriggerComponent.cs | 2 - .../SharedPullingStateManagementSystem.cs | 3 +- Content.Shared/Trigger/TriggerVisuals.cs | 27 ++- .../Entities/Objects/Weapons/security.yml | 50 ++++++ .../Objects/Weapons/pflash.rsi/flashing.png | Bin 0 -> 1094 bytes .../Objects/Weapons/pflash.rsi/meta.json | 30 ++++ .../Objects/Weapons/pflash.rsi/off.png | Bin 0 -> 489 bytes .../Objects/Weapons/pflash.rsi/on-unlit.png | Bin 0 -> 113 bytes .../Objects/Weapons/pflash.rsi/on.png | Bin 0 -> 503 bytes 14 files changed, 441 insertions(+), 27 deletions(-) create mode 100644 Content.Client/Trigger/ProximityTriggerVisualizer.cs create mode 100644 Content.Server/Explosion/Components/TriggerOnProximityComponent.cs create mode 100644 Content.Server/Explosion/EntitySystems/TriggerSystem.Proximity.cs create mode 100644 Resources/Textures/Objects/Weapons/pflash.rsi/flashing.png create mode 100644 Resources/Textures/Objects/Weapons/pflash.rsi/meta.json create mode 100644 Resources/Textures/Objects/Weapons/pflash.rsi/off.png create mode 100644 Resources/Textures/Objects/Weapons/pflash.rsi/on-unlit.png create mode 100644 Resources/Textures/Objects/Weapons/pflash.rsi/on.png diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index defc8e2649..8e6e1b2673 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -78,6 +78,7 @@ namespace Content.Client.Entry "BodyScanner", "Stunbaton", "Tool", + "TriggerOnProximity", "TilePrying", "RandomSpriteColor", "ConditionalSpawner", diff --git a/Content.Client/Trigger/ProximityTriggerVisualizer.cs b/Content.Client/Trigger/ProximityTriggerVisualizer.cs new file mode 100644 index 0000000000..b19cb06448 --- /dev/null +++ b/Content.Client/Trigger/ProximityTriggerVisualizer.cs @@ -0,0 +1,96 @@ +using System; +using Content.Shared.Trigger; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Shared.Animations; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Client.Trigger +{ + public sealed class ProximityTriggerVisualizer : AppearanceVisualizer + { + [DataField("animationState")] + private string? _animationState; + + [DataField("duration")] + private float _animationDuration = 0.3f; + + private const string AnimKey = "proximity"; + + private static Animation _animation = default!; + + public override void InitializeEntity(EntityUid entityUid) + { + + base.InitializeEntity(entityUid); + + if (_animationState == null) return; + + IoCManager.Resolve().EnsureComponent(entityUid); + + _animation = new Animation + { + Length = TimeSpan.FromSeconds(_animationDuration), + AnimationTracks = {new AnimationTrackSpriteFlick + { + LayerKey = ProximityTriggerVisualLayers.Base, + KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(_animationState, 0f)} + + }, + new AnimationTrackComponentProperty() + { + ComponentType = typeof(PointLightComponent), + InterpolationMode = AnimationInterpolationMode.Nearest, + Property = nameof(PointLightComponent.Radius), + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(0.1f, 0), + new AnimationTrackProperty.KeyFrame(3f, 0.1f), + new AnimationTrackProperty.KeyFrame(0.1f, 0.5f) + } + } + } + }; + } + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + var entityManager = IoCManager.Resolve(); + if (!entityManager.TryGetComponent(component.Owner, out SpriteComponent spriteComponent)) return; + + var animSystem = EntitySystem.Get(); + entityManager.TryGetComponent(component.Owner, out AnimationPlayerComponent? player); + component.TryGetData(ProximityTriggerVisualState.State, out ProximityTriggerVisuals state); + + switch (state) + { + case ProximityTriggerVisuals.Inactive: + if (player != null) + animSystem.Stop(player, AnimKey); + + spriteComponent.LayerSetState(ProximityTriggerVisualLayers.Base, "on"); + break; + case ProximityTriggerVisuals.Active: + /* TODO: Waiting on ECS OnChangeData so we can actually subscribe to the animation finishing properly. + if (_animationState == null || player == null || + animSystem.HasRunningAnimation(player, AnimKey)) return; + + animSystem.Play(player, _animation, AnimKey); + */ + break; + case ProximityTriggerVisuals.Off: + default: + spriteComponent.LayerSetState(ProximityTriggerVisualLayers.Base, "off"); + break; + } + } + } + + public enum ProximityTriggerVisualLayers : byte + { + Base, + } +} diff --git a/Content.Server/Explosion/Components/TriggerOnProximityComponent.cs b/Content.Server/Explosion/Components/TriggerOnProximityComponent.cs new file mode 100644 index 0000000000..f754b660d1 --- /dev/null +++ b/Content.Server/Explosion/Components/TriggerOnProximityComponent.cs @@ -0,0 +1,64 @@ +using Content.Server.Explosion.EntitySystems; +using Robust.Shared.GameObjects; +using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.ViewVariables; +using System.Collections.Generic; + +namespace Content.Server.Explosion.Components +{ + + /// + /// Raises a whenever an entity collides with a fixture attached to the owner of this component. + /// + [RegisterComponent] + public sealed class TriggerOnProximityComponent : Component + { + public const string FixtureID = "trigger-on-proximity-fixture"; + + public HashSet Colliding = new(); + + [DataField("shape", required: true)] + public IPhysShape Shape { get; set; } = new PhysShapeCircle {Radius = 2}; + + /// + /// How long the the proximity trigger animation plays for. + /// + [ViewVariables] + [DataField("animationDuration")] + public float AnimationDuration = 0.3f; + + /// + /// Whether the entity needs to be anchored for the proximity to work. + /// + [ViewVariables] + [DataField("requiresAnchored")] + public bool RequiresAnchored { get; set; } = true; + + [ViewVariables] + [DataField("enabled")] + public bool Enabled = true; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("cooldown")] + public float Cooldown { get; set; } = 5f; + + /// + /// How much cooldown has elapsed (if relevant). + /// + public float Accumulator = 0f; + + /// + /// What speed should the other object be moving at to trigger the proximity fixture? + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("triggerSpeed")] + public float TriggerSpeed { get; set; } = 3.5f; + + /// + /// If this proximity is triggered should we continually repeat it? + /// + [DataField("repeating")] + internal bool Repeating = true; + } +} diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.Proximity.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.Proximity.cs new file mode 100644 index 0000000000..0c38555d48 --- /dev/null +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.Proximity.cs @@ -0,0 +1,158 @@ +using System.Collections.Generic; +using Content.Server.Explosion.Components; +using Content.Shared.Physics; +using Content.Shared.Trigger; +using Robust.Shared.GameObjects; +using Robust.Shared.Physics.Dynamics; +using Robust.Shared.Utility; + +namespace Content.Server.Explosion.EntitySystems; + +public sealed partial class TriggerSystem +{ + /// + /// Anything that has stuff touching it (to check speed) or is on cooldown. + /// + private HashSet _activeProximities = new(); + + private void InitializeProximity() + { + SubscribeLocalEvent(OnProximityStartCollide); + SubscribeLocalEvent(OnProximityEndCollide); + SubscribeLocalEvent(OnProximityStartup); + SubscribeLocalEvent(OnProximityShutdown); + SubscribeLocalEvent(OnProximityAnchor); + } + + private void OnProximityAnchor(EntityUid uid, TriggerOnProximityComponent component, ref AnchorStateChangedEvent args) + { + component.Enabled = !component.RequiresAnchored || + args.Anchored; + + SetProximityAppearance(uid, component); + + if (!component.Enabled) + { + _activeProximities.Remove(component); + component.Colliding.Clear(); + } + // Re-check for contacts as we cleared them. + else if (TryComp(uid, out var body)) + { + _broadphase.RegenerateContacts(body); + } + } + + private void OnProximityShutdown(EntityUid uid, TriggerOnProximityComponent component, ComponentShutdown args) + { + _activeProximities.Remove(component); + component.Colliding.Clear(); + } + + private void OnProximityStartup(EntityUid uid, TriggerOnProximityComponent component, ComponentStartup args) + { + component.Enabled = !component.RequiresAnchored || + EntityManager.GetComponent(uid).Anchored; + + SetProximityAppearance(uid, component); + + if (!TryComp(uid, out var body)) return; + + _fixtures.CreateFixture(body, new Fixture(body, component.Shape) + { + // TODO: Should probably have these settable via datafield but I'm lazy and it's a pain + CollisionLayer = (int) (CollisionGroup.MobImpassable | CollisionGroup.SmallImpassable | CollisionGroup.VaultImpassable), Hard = false, ID = TriggerOnProximityComponent.FixtureID + }); + } + + private void OnProximityStartCollide(EntityUid uid, TriggerOnProximityComponent component, StartCollideEvent args) + { + if (args.OurFixture.ID != TriggerOnProximityComponent.FixtureID) return; + + _activeProximities.Add(component); + component.Colliding.Add(args.OtherFixture.Body); + } + + private void OnProximityEndCollide(EntityUid uid, TriggerOnProximityComponent component, EndCollideEvent args) + { + if (args.OurFixture.ID != TriggerOnProximityComponent.FixtureID) return; + + component.Colliding.Remove(args.OtherFixture.Body); + + if (component.Colliding.Count == 0) + _activeProximities.Remove(component); + } + + private void SetProximityAppearance(EntityUid uid, TriggerOnProximityComponent component) + { + if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent)) + { + appearanceComponent.SetData(ProximityTriggerVisualState.State, component.Enabled ? ProximityTriggerVisuals.Inactive : ProximityTriggerVisuals.Off); + } + } + + private void Activate(TriggerOnProximityComponent component) + { + DebugTools.Assert(component.Enabled); + + if (!component.Repeating) + { + component.Enabled = false; + _activeProximities.Remove(component); + component.Colliding.Clear(); + } + else + { + component.Accumulator = component.Cooldown; + } + + SetProximityAppearance(component.Owner, component); + Trigger(component.Owner); + } + + private void UpdateProximity(float frameTime) + { + var toRemove = new RemQueue(); + + foreach (var comp in _activeProximities) + { + if (!comp.Enabled) + { + toRemove.Add(comp); + continue; + } + + MetaDataComponent? metadata = null; + + if (Deleted(comp.Owner, metadata)) + { + toRemove.Add(comp); + continue; + } + + if (Paused(comp.Owner, metadata)) continue; + + comp.Accumulator -= frameTime; + + if (comp.Accumulator > 0f) continue; + + // Alright now that we have no cd check everything in range. + + foreach (var colliding in comp.Colliding) + { + if (Deleted(colliding.Owner)) continue; + + if (colliding.LinearVelocity.Length < comp.TriggerSpeed) continue; + + // Trigger! + Activate(comp); + break; + } + } + + foreach (var prox in toRemove) + { + _activeProximities.Remove(prox); + } + } +} diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index 9dfbcc3c45..fd29c503f7 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -6,25 +6,29 @@ using Content.Server.Doors.Systems; using Content.Server.Explosion.Components; using Content.Server.Flash; using Content.Server.Flash.Components; -using Content.Server.Projectiles.Components; using Content.Shared.Audio; -using Content.Shared.Database; using Content.Shared.Doors; -using Content.Shared.Throwing; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.GameObjects; using Robust.Shared.IoC; +using Robust.Shared.Physics; using Robust.Shared.Physics.Dynamics; using Robust.Shared.Player; using Robust.Shared.Timing; +using System.Threading; +using Content.Server.Construction.Components; +using Content.Shared.Trigger; +using Timer = Robust.Shared.Timing.Timer; +using Content.Shared.Physics; +using System.Collections.Generic; namespace Content.Server.Explosion.EntitySystems { /// /// Raised whenever something is Triggered on the entity. /// - public class TriggerEvent : HandledEntityEventArgs + public sealed class TriggerEvent : HandledEntityEventArgs { public EntityUid Triggered { get; } public EntityUid? User { get; } @@ -40,16 +44,19 @@ namespace Content.Server.Explosion.EntitySystems public sealed partial class TriggerSystem : EntitySystem { [Dependency] private readonly ExplosionSystem _explosions = default!; + [Dependency] private readonly FixtureSystem _fixtures = default!; [Dependency] private readonly FlashSystem _flashSystem = default!; [Dependency] private readonly DoorSystem _sharedDoorSystem = default!; + [Dependency] private readonly SharedBroadphaseSystem _broadphase = default!; public override void Initialize() { base.Initialize(); + InitializeProximity(); InitializeOnUse(); - SubscribeLocalEvent(HandleCollide); + SubscribeLocalEvent(OnTriggerCollide); SubscribeLocalEvent(HandleDeleteTrigger); SubscribeLocalEvent(HandleSoundTrigger); @@ -88,11 +95,8 @@ namespace Content.Server.Explosion.EntitySystems #region Flash private void HandleFlashTrigger(EntityUid uid, FlashOnTriggerComponent component, TriggerEvent args) { - if (component.Flashed) return; - // TODO Make flash durations sane ffs. _flashSystem.FlashArea(uid, args.User, component.Range, component.Duration * 1000f); - component.Flashed = true; } #endregion @@ -112,15 +116,9 @@ namespace Content.Server.Explosion.EntitySystems _sharedDoorSystem.TryToggleDoor(uid); } - private void HandleCollide(EntityUid uid, TriggerOnCollideComponent component, StartCollideEvent args) + private void OnTriggerCollide(EntityUid uid, TriggerOnCollideComponent component, StartCollideEvent args) { - EntityUid? user = null; - if (EntityManager.TryGetComponent(uid, out ProjectileComponent projectile)) - user = projectile.Shooter; - else if (EntityManager.TryGetComponent(uid, out ThrownItemComponent thrown)) - user = thrown.Thrower; - - Trigger(component.Owner, user); + Trigger(component.Owner); } @@ -144,5 +142,12 @@ namespace Content.Server.Explosion.EntitySystems Trigger(triggered, user); }); } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + UpdateProximity(frameTime); + } } } diff --git a/Content.Server/Flash/Components/FlashOnTriggerComponent.cs b/Content.Server/Flash/Components/FlashOnTriggerComponent.cs index 856a678f3e..40230d14b6 100644 --- a/Content.Server/Flash/Components/FlashOnTriggerComponent.cs +++ b/Content.Server/Flash/Components/FlashOnTriggerComponent.cs @@ -11,7 +11,5 @@ namespace Content.Server.Flash.Components { [DataField("range")] internal float Range = 1.0f; [DataField("duration")] internal float Duration = 8.0f; - - internal bool Flashed; } } diff --git a/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs b/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs index 1bb301d431..4ee4303662 100644 --- a/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs +++ b/Content.Shared/Pulling/Systems/SharedPullingStateManagementSystem.cs @@ -30,6 +30,7 @@ namespace Content.Shared.Pulling public class SharedPullingStateManagementSystem : EntitySystem { [Dependency] private readonly SharedJointSystem _jointSystem = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; public override void Initialize() { @@ -116,7 +117,7 @@ namespace Content.Shared.Pulling pullable.Puller = puller.Owner; // Joint startup - var union = pullerPhysics.GetWorldAABB().Union(pullablePhysics.GetWorldAABB()); + var union = _physics.GetHardAABB(pullerPhysics).Union(_physics.GetHardAABB(pullablePhysics)); var length = Math.Max(union.Size.X, union.Size.Y) * 0.75f; pullable.PullJoint = _jointSystem.CreateDistanceJoint(pullablePhysics.Owner, pullerPhysics.Owner, id:$"pull-joint-{pullablePhysics.Owner}"); diff --git a/Content.Shared/Trigger/TriggerVisuals.cs b/Content.Shared/Trigger/TriggerVisuals.cs index 193ecd5478..ed544f3590 100644 --- a/Content.Shared/Trigger/TriggerVisuals.cs +++ b/Content.Shared/Trigger/TriggerVisuals.cs @@ -1,18 +1,29 @@ -using System; -using Robust.Shared.Serialization; +using Robust.Shared.Serialization; namespace Content.Shared.Trigger { - [NetSerializable] - [Serializable] - public enum TriggerVisuals + [Serializable, NetSerializable] + public enum ProximityTriggerVisuals : byte + { + Off, + Inactive, + Active, + } + + [Serializable, NetSerializable] + public enum ProximityTriggerVisualState : byte + { + State, + } + + [Serializable, NetSerializable] + public enum TriggerVisuals : byte { VisualState, } - [NetSerializable] - [Serializable] - public enum TriggerVisualState + [Serializable, NetSerializable] + public enum TriggerVisualState : byte { Primed, Unprimed, diff --git a/Resources/Prototypes/Entities/Objects/Weapons/security.yml b/Resources/Prototypes/Entities/Objects/Weapons/security.yml index cfc7e05482..6e9fd49400 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/security.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/security.yml @@ -49,3 +49,53 @@ size: 2 sprite: Objects/Weapons/Melee/flash.rsi - type: ItemCooldown + +- type: entity + name: portable flasher + parent: BaseStructure + id: Portableflasher + description: An ultrabright flashbulb with a proximity trigger, useful for making an area security-only. + components: + - type: SoundOnTrigger + sound: + path: /Audio/Weapons/flash.ogg + - type: FlashOnTrigger + repeating: true + range: 3 + - type: TriggerOnProximity + enabled: true + cooldown: 5 + shape: + !type:PhysShapeCircle + radius: 2 + - type: Anchorable + - type: Sprite + netsync: false + sprite: Objects/Weapons/pflash.rsi + layers: + - state: "off" + map: ["enum.ProximityTriggerVisualLayers.Base"] + - type: InteractionOutline + - type: Physics + - type: Fixtures + fixtures: + - shape: + !type:PhysShapeAabb + bounds: "-0.15,-0.3,0.15,0.3" + mask: + - Impassable + - VaultImpassable + - SmallImpassable + layer: + - MobImpassable + mass: 70 + - type: Appearance + visuals: + - type: ProximityTriggerVisualizer + animationState: flashing + - type: PointLight + energy: 2.0 + radius: 0 + softness: 0 + enabled: true + diff --git a/Resources/Textures/Objects/Weapons/pflash.rsi/flashing.png b/Resources/Textures/Objects/Weapons/pflash.rsi/flashing.png new file mode 100644 index 0000000000000000000000000000000000000000..075fc5373317b6bf90d17bfb41aa2775f89c88f4 GIT binary patch literal 1094 zcmV-M1iAZ(P)V=-0C=1w$FUB=AQT1AnfDbp?b?_ku5U3khN1lhLjnj1TEinX{=I`|J0q^k z&26x&qa!vc`wNWAX(sh4p=U76nN*JDk^4W=eUD%a?^M0yd#YaYKq{0bU}#dQ`DYX` z45<*S|AL_>JsBFYY!-HP^aJ_lAcQB!fnopv1Aj?GK~#90?V2%Y6Hy$-e@V60C8VUH zgd~*?;*c%{X`o0NTm-jT=Wcd7aOsrYrDLWO1h)gNo$Vkb1R-FjdWfi$Hin|4rxKK; zML7q(kk}S|FE95zlOL2Xvn%OzIwMwnb_lT0S1 zk0pE`U>Jsb(!N5W(D%7OwZ4!6hGDqpVle>DoITsM@1WVF?l^rlp&$CftH8|640Xq$ zR;zXWCFgr$B`;Px4!hZ!LJ`$vL97~Dt%!C zn$0Evr#B;A+w1kZl7jr!m+F8>B+?!mjfRwPFR0WPo&;^%Zi{w~wr%%Rh%SBM`+!oZ zommBTkn} zB{!SRhP=O7SXfZr3H{QSdM5ZkG4PMYhX&{_KsN(#fb@3(Rm{)NYtOOK035bjjE;=~ zxLaFWf$M~x=77Uit7{A*(gy79?Ri(eJq8hB1NQg#k8SfHwm(RAK&J_#V`G6mdFUY@ zU|AO6y`P`+H6Nf7>3o1`ngGP(ah8^re9Z@_L^>Z}Kga@5kA_M5oDWcmumRrt`Ij%_ z9b3zk%VpP(e1K|%4EWRgdBZRQXF}le0jddHK0q~r%?GF=aQOh$1Tr6>lECEyR3p>@ zg+d|Zy`TU6%KMcMP)*?S0jdd1KA>M{&j$=&jcI0RfbIfxGw=rd02Th;#txrGF8}}l M07*qoM6N<$f(A4SX#fBK literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/pflash.rsi/meta.json b/Resources/Textures/Objects/Weapons/pflash.rsi/meta.json new file mode 100644 index 0000000000..c7b536cf6e --- /dev/null +++ b/Resources/Textures/Objects/Weapons/pflash.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/f349b842c84f500399bd5673e5e34a6bc45b001a", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "off" + }, + { + "name": "on" + }, + { + "name": "on-unlit" + }, + { + "name": "flashing", + "delays": [ + [ + 0.1, + 0.1, + 0.1 + ] + ] + } + ] +} diff --git a/Resources/Textures/Objects/Weapons/pflash.rsi/off.png b/Resources/Textures/Objects/Weapons/pflash.rsi/off.png new file mode 100644 index 0000000000000000000000000000000000000000..b7007484036f4605d823866ef3f03029620f843f GIT binary patch literal 489 zcmVl&H6vux=O{3J3hgww_$jrjTlF{GO}@72F6>MkSqp} zbrrH?ImzM z?0Jca0oLpFZsmnP2td2teznY!@{0iegh~kVAqWDzCp{>}%txPs$z+lPc%JuSrAn#X_x)1wPqW$l=So;_!T+|S fU&~JbM+f)?rdO%Q5*2jk00000NkvXXu0mjfb=T!- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/pflash.rsi/on-unlit.png b/Resources/Textures/Objects/Weapons/pflash.rsi/on-unlit.png new file mode 100644 index 0000000000000000000000000000000000000000..8057f65fe43c92a9ee1f1f072720a3f7604f716a GIT binary patch literal 113 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@&H$ef*8>L*1Rb<H!7r>mdK II;Vst0QV&y4gdfE literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Weapons/pflash.rsi/on.png b/Resources/Textures/Objects/Weapons/pflash.rsi/on.png new file mode 100644 index 0000000000000000000000000000000000000000..2e0449572687d99cc14bc896ed3fe71f5027eccd GIT binary patch literal 503 zcmVp%O9+jtGAQ?`V@HCs}8meeZtn-recty4^05l$nV}p8`w)fKO7#X0w@Q z@6~EGCtXLBfm*GWhG9q;hQx79y_Ig=zrgLEHd@lu