From a2bf2207a2f9ceec4e19fa351f579e40ab197883 Mon Sep 17 00:00:00 2001 From: Artjom Date: Sat, 6 May 2023 08:06:42 +0300 Subject: [PATCH] New indicators for combat mode (#15427) Co-authored-by: metalgearsloth --- .../CombatMode/CombatModeIndicatorsOverlay.cs | 87 +++++++++ Content.Client/CombatMode/CombatModeSystem.cs | 143 ++++++++------ .../Options/UI/Tabs/GraphicsTab.xaml | 1 + .../Options/UI/Tabs/GraphicsTab.xaml.cs | 5 + Content.Server/CombatMode/CombatModeSystem.cs | 22 +-- Content.Shared/CCVar/CCVars.cs | 7 +- .../CombatMode/SharedCombatModeSystem.cs | 183 +++++++++--------- .../en-US/escape-menu/ui/options-menu.ftl | 1 + .../Misc/crosshair_pointers.rsi/gun_sight.png | Bin 0 -> 16635 bytes .../crosshair_pointers.rsi/melee_sight.png | Bin 0 -> 16635 bytes .../Misc/crosshair_pointers.rsi/meta.json | 17 ++ 11 files changed, 301 insertions(+), 165 deletions(-) create mode 100644 Content.Client/CombatMode/CombatModeIndicatorsOverlay.cs create mode 100644 Resources/Textures/Interface/Misc/crosshair_pointers.rsi/gun_sight.png create mode 100644 Resources/Textures/Interface/Misc/crosshair_pointers.rsi/melee_sight.png create mode 100644 Resources/Textures/Interface/Misc/crosshair_pointers.rsi/meta.json diff --git a/Content.Client/CombatMode/CombatModeIndicatorsOverlay.cs b/Content.Client/CombatMode/CombatModeIndicatorsOverlay.cs new file mode 100644 index 0000000000..8d4eedba9f --- /dev/null +++ b/Content.Client/CombatMode/CombatModeIndicatorsOverlay.cs @@ -0,0 +1,87 @@ +using Content.Client.Hands.Systems; +using Content.Shared.Weapons.Ranged.Components; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.Input; +using Robust.Client.UserInterface; +using Robust.Shared.Enums; +using Robust.Shared.Utility; + +namespace Content.Client.CombatMode; + +/// +/// This shows something like crosshairs for the combat mode next to the mouse cursor. +/// For weapons with the gun class, a crosshair of one type is displayed, +/// while for all other types of weapons and items in hand, as well as for an empty hand, +/// a crosshair of a different type is displayed. These crosshairs simply show the state of combat mode (on|off). +/// +public sealed class CombatModeIndicatorsOverlay : Overlay +{ + private readonly IInputManager _inputManager; + private readonly IEntityManager _entMan; + private readonly IEyeManager _eye; + private readonly CombatModeSystem _combat; + private readonly HandsSystem _hands = default!; + + private readonly Texture _gunSight; + private readonly Texture _meleeSight; + + public override OverlaySpace Space => OverlaySpace.ScreenSpace; + + public Color MainColor = Color.White.WithAlpha(0.3f); + public Color StrokeColor = Color.Black.WithAlpha(0.5f); + public float Scale = 0.8f; // 1 is a little big + + public CombatModeIndicatorsOverlay(IInputManager input, IEntityManager entMan, + IEyeManager eye, CombatModeSystem combatSys, HandsSystem hands) + { + _inputManager = input; + _entMan = entMan; + _eye = eye; + _combat = combatSys; + _hands = hands; + + var spriteSys = _entMan.EntitySysManager.GetEntitySystem(); + _gunSight = spriteSys.Frame0(new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Misc/crosshair_pointers.rsi"), + "gun_sight")); + _meleeSight = spriteSys.Frame0(new SpriteSpecifier.Rsi(new ResPath("/Textures/Interface/Misc/crosshair_pointers.rsi"), + "melee_sight")); + } + + protected override bool BeforeDraw(in OverlayDrawArgs args) + { + if (!_combat.IsInCombatMode()) + return false; + + return base.BeforeDraw(in args); + } + + protected override void Draw(in OverlayDrawArgs args) + { + var mouseScreenPosition = _inputManager.MouseScreenPosition; + var mousePosMap = _eye.ScreenToMap(mouseScreenPosition); + if (mousePosMap.MapId != args.MapId) + return; + + var handEntity = _hands.GetActiveHandEntity(); + var isHandGunItem = _entMan.HasComponent(handEntity); + + var mousePos = mouseScreenPosition.Position; + var uiScale = (args.ViewportControl as Control)?.UIScale ?? 1f; + var limitedScale = uiScale > 1.25f ? 1.25f : uiScale; + + var sight = isHandGunItem ? _gunSight : _meleeSight; + DrawSight(sight, args.ScreenHandle, mousePos, limitedScale * Scale); + } + + private void DrawSight(Texture sight, DrawingHandleScreen screen, Vector2 centerPos, float scale) + { + var sightSize = sight.Size * scale; + var expandedSize = sightSize + 7f; + + screen.DrawTextureRect(sight, + UIBox2.FromDimensions(centerPos - sightSize * 0.5f, sightSize), StrokeColor); + screen.DrawTextureRect(sight, + UIBox2.FromDimensions(centerPos - expandedSize * 0.5f, expandedSize), MainColor); + } +} diff --git a/Content.Client/CombatMode/CombatModeSystem.cs b/Content.Client/CombatMode/CombatModeSystem.cs index 63792385c1..d89afba214 100644 --- a/Content.Client/CombatMode/CombatModeSystem.cs +++ b/Content.Client/CombatMode/CombatModeSystem.cs @@ -1,77 +1,102 @@ +using Content.Client.Hands.Systems; using Content.Shared.CombatMode; using Content.Shared.Targeting; -using JetBrains.Annotations; +using Content.Shared.CCVar; using Robust.Client.Player; +using Robust.Client.Input; +using Robust.Client.Graphics; using Robust.Shared.GameStates; -using Robust.Shared.Input.Binding; +using Robust.Shared.Configuration; -namespace Content.Client.CombatMode +namespace Content.Client.CombatMode; + +public sealed class CombatModeSystem : SharedCombatModeSystem { - [UsedImplicitly] - public sealed class CombatModeSystem : SharedCombatModeSystem + [Dependency] private readonly IOverlayManager _overlayManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + [Dependency] private readonly IEyeManager _eye = default!; + public event Action? LocalPlayerCombatModeUpdated; + + public override void Initialize() { - [Dependency] private readonly IPlayerManager _playerManager = default!; + base.Initialize(); - public event Action? LocalPlayerCombatModeUpdated; + SubscribeLocalEvent(OnHandleState); - public override void Initialize() + _cfg.OnValueChanged(CCVars.CombatModeIndicatorsPointShow, OnShowCombatIndicatorsChanged, true); + } + + private void OnHandleState(EntityUid uid, CombatModeComponent component, ref ComponentHandleState args) + { + if (args.Current is not CombatModeComponentState state) + return; + + component.IsInCombatMode = state.IsInCombatMode; + component.ActiveZone = state.TargetingZone; + UpdateHud(uid); + } + + public override void Shutdown() + { + _cfg.OnValueChanged(CCVars.CombatModeIndicatorsPointShow, OnShowCombatIndicatorsChanged); + _overlayManager.RemoveOverlay(); + + base.Shutdown(); + } + + private void OnTargetingZoneChanged(TargetingZone obj) + { + EntityManager.RaisePredictiveEvent(new CombatModeSystemMessages.SetTargetZoneMessage(obj)); + } + + public bool IsInCombatMode() + { + var entity = _playerManager.LocalPlayer?.ControlledEntity; + + if (entity == null) + return false; + + return IsInCombatMode(entity.Value); + } + + public override void SetInCombatMode(EntityUid entity, bool inCombatMode, CombatModeComponent? component = null) + { + base.SetInCombatMode(entity, inCombatMode, component); + UpdateHud(entity); + } + + public override void SetActiveZone(EntityUid entity, TargetingZone zone, CombatModeComponent? component = null) + { + base.SetActiveZone(entity, zone, component); + UpdateHud(entity); + } + + private void UpdateHud(EntityUid entity) + { + if (entity != _playerManager.LocalPlayer?.ControlledEntity) { - base.Initialize(); - - SubscribeLocalEvent(OnHandleState); + return; } - private void OnHandleState(EntityUid uid, CombatModeComponent component, ref ComponentHandleState args) - { - if (args.Current is not CombatModeComponentState state) - return; + LocalPlayerCombatModeUpdated?.Invoke(); + } - component.IsInCombatMode = state.IsInCombatMode; - component.ActiveZone = state.TargetingZone; - UpdateHud(uid); + private void OnShowCombatIndicatorsChanged(bool isShow) + { + if (isShow) + { + _overlayManager.AddOverlay(new CombatModeIndicatorsOverlay( + _inputManager, + EntityManager, + _eye, + this, + EntityManager.System())); } - - public override void Shutdown() + else { - CommandBinds.Unregister(); - base.Shutdown(); - } - - private void OnTargetingZoneChanged(TargetingZone obj) - { - EntityManager.RaisePredictiveEvent(new CombatModeSystemMessages.SetTargetZoneMessage(obj)); - } - - public bool IsInCombatMode() - { - var entity = _playerManager.LocalPlayer?.ControlledEntity; - - if (entity == null) - return false; - - return IsInCombatMode(entity.Value); - } - - public override void SetInCombatMode(EntityUid entity, bool inCombatMode, CombatModeComponent? component = null) - { - base.SetInCombatMode(entity, inCombatMode, component); - UpdateHud(entity); - } - - public override void SetActiveZone(EntityUid entity, TargetingZone zone, CombatModeComponent? component = null) - { - base.SetActiveZone(entity, zone, component); - UpdateHud(entity); - } - - private void UpdateHud(EntityUid entity) - { - if (entity != _playerManager.LocalPlayer?.ControlledEntity) - { - return; - } - - LocalPlayerCombatModeUpdated?.Invoke(); + _overlayManager.RemoveOverlay(); } } } diff --git a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml index caf4dca99a..41b304417a 100644 --- a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml +++ b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml @@ -21,6 +21,7 @@ + diff --git a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs index b45b5db477..87adbd1ca8 100644 --- a/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs +++ b/Content.Client/Options/UI/Tabs/GraphicsTab.xaml.cs @@ -99,6 +99,7 @@ namespace Content.Client.Options.UI.Tabs }; ShowHeldItemCheckBox.OnToggled += OnCheckBoxToggled; + ShowCombatModeIndicatorsCheckBox.OnToggled += OnCheckBoxToggled; IntegerScalingCheckBox.OnToggled += OnCheckBoxToggled; ViewportLowResCheckBox.OnToggled += OnCheckBoxToggled; ParallaxLowQualityCheckBox.OnToggled += OnCheckBoxToggled; @@ -116,6 +117,7 @@ namespace Content.Client.Options.UI.Tabs ParallaxLowQualityCheckBox.Pressed = _cfg.GetCVar(CCVars.ParallaxLowQuality); FpsCounterCheckBox.Pressed = _cfg.GetCVar(CCVars.HudFpsCounterVisible); ShowHeldItemCheckBox.Pressed = _cfg.GetCVar(CCVars.HudHeldItemShow); + ShowCombatModeIndicatorsCheckBox.Pressed = _cfg.GetCVar(CCVars.CombatModeIndicatorsPointShow); ViewportWidthSlider.Value = _cfg.GetCVar(CCVars.ViewportWidth); _cfg.OnValueChanged(CCVars.ViewportMinimumWidth, _ => UpdateViewportWidthRange()); @@ -158,6 +160,7 @@ namespace Content.Client.Options.UI.Tabs _cfg.SetCVar(CCVars.ViewportScaleRender, !ViewportLowResCheckBox.Pressed); _cfg.SetCVar(CCVars.ParallaxLowQuality, ParallaxLowQualityCheckBox.Pressed); _cfg.SetCVar(CCVars.HudHeldItemShow, ShowHeldItemCheckBox.Pressed); + _cfg.SetCVar(CCVars.CombatModeIndicatorsPointShow, ShowCombatModeIndicatorsCheckBox.Pressed); _cfg.SetCVar(CCVars.HudFpsCounterVisible, FpsCounterCheckBox.Pressed); _cfg.SetCVar(CCVars.ViewportWidth, (int) ViewportWidthSlider.Value); @@ -194,6 +197,7 @@ namespace Content.Client.Options.UI.Tabs var isVPResSame = ViewportLowResCheckBox.Pressed == !_cfg.GetCVar(CCVars.ViewportScaleRender); var isPLQSame = ParallaxLowQualityCheckBox.Pressed == _cfg.GetCVar(CCVars.ParallaxLowQuality); var isShowHeldItemSame = ShowHeldItemCheckBox.Pressed == _cfg.GetCVar(CCVars.HudHeldItemShow); + var isCombatModeIndicatorsSame = ShowCombatModeIndicatorsCheckBox.Pressed == _cfg.GetCVar(CCVars.CombatModeIndicatorsPointShow); var isFpsCounterVisibleSame = FpsCounterCheckBox.Pressed == _cfg.GetCVar(CCVars.HudFpsCounterVisible); var isWidthSame = (int) ViewportWidthSlider.Value == _cfg.GetCVar(CCVars.ViewportWidth); var isLayoutSame = HudLayoutOption.SelectedMetadata is string opt && opt == _cfg.GetCVar(CCVars.UILayout); @@ -209,6 +213,7 @@ namespace Content.Client.Options.UI.Tabs isPLQSame && isHudThemeSame && isShowHeldItemSame && + isCombatModeIndicatorsSame && isFpsCounterVisibleSame && isWidthSame && isLayoutSame; diff --git a/Content.Server/CombatMode/CombatModeSystem.cs b/Content.Server/CombatMode/CombatModeSystem.cs index 3ad2e5aa19..945a7f67ce 100644 --- a/Content.Server/CombatMode/CombatModeSystem.cs +++ b/Content.Server/CombatMode/CombatModeSystem.cs @@ -2,21 +2,19 @@ using Content.Shared.CombatMode; using JetBrains.Annotations; using Robust.Shared.GameStates; -namespace Content.Server.CombatMode +namespace Content.Server.CombatMode; + +public sealed class CombatModeSystem : SharedCombatModeSystem { - [UsedImplicitly] - public sealed class CombatModeSystem : SharedCombatModeSystem + public override void Initialize() { - public override void Initialize() - { - base.Initialize(); + base.Initialize(); - SubscribeLocalEvent(OnGetState); - } + SubscribeLocalEvent(OnGetState); + } - private void OnGetState(EntityUid uid, CombatModeComponent component, ref ComponentGetState args) - { - args.State = new CombatModeComponentState(component.IsInCombatMode, component.ActiveZone); - } + private void OnGetState(EntityUid uid, CombatModeComponent component, ref ComponentGetState args) + { + args.State = new CombatModeComponentState(component.IsInCombatMode, component.ActiveZone); } } diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 4aad6a4440..29e3c6072c 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -572,6 +572,9 @@ namespace Content.Shared.CCVar public static readonly CVarDef HudHeldItemShow = CVarDef.Create("hud.held_item_show", true, CVar.ARCHIVE | CVar.CLIENTONLY); + public static readonly CVarDef CombatModeIndicatorsPointShow = + CVarDef.Create("hud.combat_mode_indicators_point_show", true, CVar.ARCHIVE | CVar.CLIENTONLY); + public static readonly CVarDef HudHeldItemOffset = CVarDef.Create("hud.held_item_offset", 28f, CVar.ARCHIVE | CVar.CLIENTONLY); @@ -919,7 +922,7 @@ namespace Content.Shared.CCVar /// Whether or not OOC chat should be enabled during a round. /// public static readonly CVarDef OocEnableDuringRound = - CVarDef.Create("ooc.enable_during_round", false, CVar.NOTIFY | CVar.REPLICATED |CVar.SERVER); + CVarDef.Create("ooc.enable_during_round", false, CVar.NOTIFY | CVar.REPLICATED | CVar.SERVER); /* * LOOC @@ -1101,7 +1104,7 @@ namespace Content.Shared.CCVar /// public static readonly CVarDef CargoShuttles = CVarDef.Create("shuttle.cargo", true, CVar.SERVERONLY); - + /// /// Whether to automatically spawn escape shuttles. /// diff --git a/Content.Shared/CombatMode/SharedCombatModeSystem.cs b/Content.Shared/CombatMode/SharedCombatModeSystem.cs index 57c7b5c8d8..244752e322 100644 --- a/Content.Shared/CombatMode/SharedCombatModeSystem.cs +++ b/Content.Shared/CombatMode/SharedCombatModeSystem.cs @@ -6,101 +6,100 @@ using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Timing; -namespace Content.Shared.CombatMode +namespace Content.Shared.CombatMode; + +public abstract class SharedCombatModeSystem : EntitySystem { - public abstract class SharedCombatModeSystem : EntitySystem + [Dependency] private readonly IPrototypeManager _protoMan = default!; + [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() { - [Dependency] private readonly IPrototypeManager _protoMan = default!; - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly IGameTiming _timing = default!; + base.Initialize(); - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnActionPerform); - } - - private void OnStartup(EntityUid uid, CombatModeComponent component, ComponentStartup args) - { - if (component.CombatToggleAction == null - && _protoMan.TryIndex(component.CombatToggleActionId, out InstantActionPrototype? toggleProto)) - { - component.CombatToggleAction = new(toggleProto); - } - - if (component.CombatToggleAction != null) - _actionsSystem.AddAction(uid, component.CombatToggleAction, null); - } - - private void OnShutdown(EntityUid uid, CombatModeComponent component, ComponentShutdown args) - { - if (component.CombatToggleAction != null) - _actionsSystem.RemoveAction(uid, component.CombatToggleAction); - } - - private void OnActionPerform(EntityUid uid, CombatModeComponent component, ToggleCombatActionEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - SetInCombatMode(uid, !component.IsInCombatMode, component); - - if (!_timing.IsFirstTimePredicted) - return; - - var msg = component.IsInCombatMode ? "action-popup-combat-enabled" : "action-popup-combat-disabled"; - _popup.PopupEntity(Loc.GetString(msg), args.Performer, args.Performer); - } - - public void SetCanDisarm(EntityUid entity, bool canDisarm, CombatModeComponent? component = null) - { - if (!Resolve(entity, ref component)) - return; - - component.CanDisarm = canDisarm; - } - - public bool IsInCombatMode(EntityUid? entity, CombatModeComponent? component = null) - { - return entity != null && Resolve(entity.Value, ref component, false) && component.IsInCombatMode; - } - - public virtual void SetInCombatMode(EntityUid entity, bool inCombatMode, - CombatModeComponent? component = null) - { - if (!Resolve(entity, ref component)) - return; - - component.IsInCombatMode = inCombatMode; - } - - public virtual void SetActiveZone(EntityUid entity, TargetingZone zone, - CombatModeComponent? component = null) - { - if (!Resolve(entity, ref component)) - return; - - component.ActiveZone = zone; - } - - [Serializable, NetSerializable] - protected sealed class CombatModeComponentState : ComponentState - { - public bool IsInCombatMode { get; } - public TargetingZone TargetingZone { get; } - - public CombatModeComponentState(bool isInCombatMode, TargetingZone targetingZone) - { - IsInCombatMode = isInCombatMode; - TargetingZone = targetingZone; - } - } + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnActionPerform); } - public sealed class ToggleCombatActionEvent : InstantActionEvent { } + private void OnStartup(EntityUid uid, CombatModeComponent component, ComponentStartup args) + { + if (component.CombatToggleAction == null + && _protoMan.TryIndex(component.CombatToggleActionId, out InstantActionPrototype? toggleProto)) + { + component.CombatToggleAction = new(toggleProto); + } + + if (component.CombatToggleAction != null) + _actionsSystem.AddAction(uid, component.CombatToggleAction, null); + } + + private void OnShutdown(EntityUid uid, CombatModeComponent component, ComponentShutdown args) + { + if (component.CombatToggleAction != null) + _actionsSystem.RemoveAction(uid, component.CombatToggleAction); + } + + private void OnActionPerform(EntityUid uid, CombatModeComponent component, ToggleCombatActionEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + SetInCombatMode(uid, !component.IsInCombatMode, component); + + if (!_timing.IsFirstTimePredicted) + return; + + var msg = component.IsInCombatMode ? "action-popup-combat-enabled" : "action-popup-combat-disabled"; + _popup.PopupEntity(Loc.GetString(msg), args.Performer, args.Performer); + } + + public void SetCanDisarm(EntityUid entity, bool canDisarm, CombatModeComponent? component = null) + { + if (!Resolve(entity, ref component)) + return; + + component.CanDisarm = canDisarm; + } + + public bool IsInCombatMode(EntityUid? entity, CombatModeComponent? component = null) + { + return entity != null && Resolve(entity.Value, ref component, false) && component.IsInCombatMode; + } + + public virtual void SetInCombatMode(EntityUid entity, bool inCombatMode, + CombatModeComponent? component = null) + { + if (!Resolve(entity, ref component)) + return; + + component.IsInCombatMode = inCombatMode; + } + + public virtual void SetActiveZone(EntityUid entity, TargetingZone zone, + CombatModeComponent? component = null) + { + if (!Resolve(entity, ref component)) + return; + + component.ActiveZone = zone; + } + + [Serializable, NetSerializable] + protected sealed class CombatModeComponentState : ComponentState + { + public bool IsInCombatMode { get; } + public TargetingZone TargetingZone { get; } + + public CombatModeComponentState(bool isInCombatMode, TargetingZone targetingZone) + { + IsInCombatMode = isInCombatMode; + TargetingZone = targetingZone; + } + } } + +public sealed class ToggleCombatActionEvent : InstantActionEvent { } diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl index 9359b7ca21..0caa547301 100644 --- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl +++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl @@ -29,6 +29,7 @@ ui-options-volume-percent = { TOSTRING($volume, "P0") } ## Graphics menu ui-options-show-held-item = Show held item next to cursor? +ui-options-show-combat-mode-indicators = Show combat mode indicators with cursor? ui-options-vsync = VSync ui-options-fullscreen = Fullscreen ui-options-lighting-label = Lighting Quality: diff --git a/Resources/Textures/Interface/Misc/crosshair_pointers.rsi/gun_sight.png b/Resources/Textures/Interface/Misc/crosshair_pointers.rsi/gun_sight.png new file mode 100644 index 0000000000000000000000000000000000000000..4b50b40d639ea315dfafd20b30bd0c76e7d997b2 GIT binary patch literal 16635 zcmeI2eP~r>9LLX1-HWwR3z=`_@@=ANB`6q%OZVaw*AgieAxniuX;v5W5C5q@453J< z{wVyzf{rur^KPE=?77>yclX?#yX`kVJLf*< zdHMZ*=Q+>&j_Rt4ypdx^I*yZ9xwv#W*AkJF#s4pAUOmGl>xHEirB1v4cK@*FeL`|K zFJAS!NILep}N*T(NUr~-X%;S&_w1M#Gp z2N&Ta)I$?ofSZtrTqE*MP(z!PxT{uD8N^G3QtH+;u+=Bg1P9?yI09<*{{W4DuY&Y= z5Jp3Bj9FY|m>SMu5Kq*&{tk~r3!lZUcoEYcjuRe(O^^fX_k9cTr@>121X|$+*yJjK zA3-q$_Cg&p&jR(^X%))MhjRD;B<{~dj`oG5=HJO``cuEcXlFa?E-0=2YiaXUo~)!+~;R$uEjUQVbR z6xL(t92qN%EwX(eIH9+1r~)H3sj-P>cIq91>w0XZrYj7?=0l_9&8r*w67ElzE`2En zJ=J|{hH90bYCs7oCRkD_&3;B)7cI}i7&INFp1}znHP)AcM4kbfeww1umOpz?-!>vI?EPQv?tl)=b(nrmQb@mePfv-vO_i76xpDf-6duXP*<{vB^-C>XXx*= z-(cD%rL`NaF+-P_k@WU$J%TnbZ0$r>Mj^YAzP9Z%^$0C)T}=mQIC;z+!FB(H>ptwa z#VBanrpB)k)DCosClVtnMbgAaLr7CbGos3f6(JOt15DA|KMC<25KB?ZRYOQq+HjDq zYcb>X>u+AtG!%HmqJ(O9BQ|)B;dp95ERj5P)Rloogj?V}GPlKdMGeJmAzF;JXrjjb zlo@U_=roZ!yNW~HuY=uqt^qVU47+G#Yiq|8R_{xB-t-c92Ob66z{poV{S^tFpMrhh zmbJe1AyqEex)I-b^k+nL9}Y9&0B9@ySD4{jCnB3uW8Z1R6cIbdz{l|NK(Q%d|6+In zs74QnmzuBxh7ttr0jkyr5p+307Y_$~{>HS1ae+L7r~UcL(rLqv=n2|7pcE=aV4 zvD!2|d={(M+-DDke3-z92Qhvjz z6`-x>o5~#TLWeJ=!xSh2^%}a$5!#T| zUV|FHHqe@#!pm`h7BJHyfjYo#LChI4R;cUL+MTwqWW4myu9Q}uwXZiMLmn9NKrbG+ m+%Q3n%hKCt0eXq-@A0AUYxi#2p53vlTv=9Cx_9B)xBds?3gp26 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Interface/Misc/crosshair_pointers.rsi/melee_sight.png b/Resources/Textures/Interface/Misc/crosshair_pointers.rsi/melee_sight.png new file mode 100644 index 0000000000000000000000000000000000000000..8ab45b0b8b0acc3c49a97ab648184afbbe80663d GIT binary patch literal 16635 zcmeI4NoW*76oz{=CNUUMLBxO%a6<$Sf}kg(nGgq;L{XwB5|@|+F>d3E7rluWQBhAW zhzOz{#FL-_K|xUTD7al*P;ifkAQ``>*3fCXdM4B3bk}sh@K05}uI2szbXQj|lf$h` znrmz7YAnmDO)W?+BW(~=SMqted;1HL%JmDIlU7Fl&fU6n9MAA=3s&v4tQivpiM|uTdsK}=S23kbD)nGRGPWcMyN3a}R0zqA|brZ`R#=i>cz$DNIUV}%8 zMB*N)KVtx|>v`Yjd4JJa7V+6vj1&i$zb}TaLQP(YN1VkM!4&fUMFi8x7gYpP$rnY0 z9OUn)LBgKGV?WTD`@~3XuL)0XTn+Qjp zJ=@BG?Fqz?UB{5G0w=+ye`r?m7bT_BHQ*_5d-~g0ltFxULTp(<^6hF+_x2b-9#+b2 zsHvbDJOOXH4fT~Ywpf8!fmnf9fmi{D0-QOfJ6xeE4*I-VK=dt$5ZGd=fOw1B1oFp# zSTsd|_^vqj%W=0$@*ggB&3Ae1 z3HBqPIJdFFDpw8>%A36UQZyn&gS