diff --git a/Content.Client/GameObjects/Components/Pointing/PointingArrowComponent.cs b/Content.Client/GameObjects/Components/Pointing/PointingArrowComponent.cs new file mode 100644 index 0000000000..c12776b01c --- /dev/null +++ b/Content.Client/GameObjects/Components/Pointing/PointingArrowComponent.cs @@ -0,0 +1,21 @@ +using Content.Shared.GameObjects.Components.Pointing; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using DrawDepth = Content.Shared.GameObjects.DrawDepth; + +namespace Content.Client.GameObjects.Components.Pointing +{ + [RegisterComponent] + public class PointingArrowComponent : SharedPointingArrowComponent + { + protected override void Startup() + { + base.Startup(); + + if (Owner.TryGetComponent(out SpriteComponent sprite)) + { + sprite.DrawDepth = (int) DrawDepth.Overlays; + } + } + } +} diff --git a/Content.Client/GameObjects/Components/Pointing/RoguePointingArrowComponent.cs b/Content.Client/GameObjects/Components/Pointing/RoguePointingArrowComponent.cs new file mode 100644 index 0000000000..4b7cfe3681 --- /dev/null +++ b/Content.Client/GameObjects/Components/Pointing/RoguePointingArrowComponent.cs @@ -0,0 +1,21 @@ +using Content.Shared.GameObjects.Components.Pointing; +using Robust.Client.GameObjects; +using Robust.Shared.GameObjects; +using DrawDepth = Content.Shared.GameObjects.DrawDepth; + +namespace Content.Client.GameObjects.Components.Pointing +{ + [RegisterComponent] + public class RoguePointingArrowComponent : SharedRoguePointingArrowComponent + { + protected override void Startup() + { + base.Startup(); + + if (Owner.TryGetComponent(out SpriteComponent sprite)) + { + sprite.DrawDepth = (int) DrawDepth.Overlays; + } + } + } +} diff --git a/Content.Client/GameObjects/Components/Pointing/RoguePointingArrowVisualizer.cs b/Content.Client/GameObjects/Components/Pointing/RoguePointingArrowVisualizer.cs new file mode 100644 index 0000000000..97de9d0b9f --- /dev/null +++ b/Content.Client/GameObjects/Components/Pointing/RoguePointingArrowVisualizer.cs @@ -0,0 +1,67 @@ +using System; +using Content.Shared.GameObjects.Components.Mobs; +using Content.Shared.GameObjects.Components.Pointing; +using JetBrains.Annotations; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Client.GameObjects.Components.Animations; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.Animations; +using Robust.Shared.Maths; + +namespace Content.Client.GameObjects.Components.Pointing +{ + [UsedImplicitly] + public class RoguePointingArrowVisualizer : AppearanceVisualizer + { + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + if (component.Deleted) + { + return; + } + + if (component.TryGetData(RoguePointingArrowVisuals.Rotation, out var degrees)) + { + SetRotation(component, Angle.FromDegrees(degrees)); + } + } + + private void SetRotation(AppearanceComponent component, Angle rotation) + { + var sprite = component.Owner.GetComponent(); + + if (!sprite.Owner.TryGetComponent(out AnimationPlayerComponent animation)) + { + sprite.Rotation = rotation; + return; + } + + if (animation.HasRunningAnimation("rotate")) + { + animation.Stop("rotate"); + } + + animation.Play(new Animation + { + Length = TimeSpan.FromSeconds(0.125), + AnimationTracks = + { + new AnimationTrackComponentProperty + { + ComponentType = typeof(ISpriteComponent), + Property = nameof(ISpriteComponent.Rotation), + InterpolationMode = AnimationInterpolationMode.Linear, + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(sprite.Rotation, 0), + new AnimationTrackProperty.KeyFrame(rotation, 0.125f) + } + } + } + }, "rotate"); + } + } +} diff --git a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs index 9ec0ada247..69ceade1f0 100644 --- a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs @@ -440,7 +440,8 @@ namespace Content.Client.GameObjects.EntitySystems return; } - if (args.Function == EngineKeyFunctions.Use) + if (args.Function == EngineKeyFunctions.Use || + args.Function == ContentKeyFunctions.Point) { var inputSys = _master.EntitySystemManager.GetEntitySystem(); diff --git a/Content.Client/Input/ContentContexts.cs b/Content.Client/Input/ContentContexts.cs index 197774ed33..79a4cfe0f0 100644 --- a/Content.Client/Input/ContentContexts.cs +++ b/Content.Client/Input/ContentContexts.cs @@ -35,6 +35,7 @@ namespace Content.Client.Input human.AddFunction(ContentKeyFunctions.MouseMiddle); human.AddFunction(ContentKeyFunctions.ToggleCombatMode); human.AddFunction(ContentKeyFunctions.WideAttack); + human.AddFunction(ContentKeyFunctions.Point); var ghost = contexts.New("ghost", "common"); ghost.AddFunction(EngineKeyFunctions.MoveUp); diff --git a/Content.Client/UserInterface/TutorialWindow.cs b/Content.Client/UserInterface/TutorialWindow.cs index bf6796e789..a91dcc01e5 100644 --- a/Content.Client/UserInterface/TutorialWindow.cs +++ b/Content.Client/UserInterface/TutorialWindow.cs @@ -82,6 +82,7 @@ Do wide attack: [color=#a4885c]{23}[/color] Use targeted entity: [color=#a4885c]{11}[/color] Throw held item: [color=#a4885c]{12}[/color] Examine entity: [color=#a4885c]{13}[/color] +Point somewhere: [color=#a4885c]{28}[/color] Open entity context menu: [color=#a4885c]{14}[/color] Toggle combat mode: [color=#a4885c]{15}[/color] Toggle console: [color=#a4885c]{16}[/color] @@ -114,7 +115,8 @@ Toggle sandbox window: [color=#a4885c]{21}[/color]", Key(SmartEquipBackpack), Key(SmartEquipBelt), Key(FocusOOC), - Key(FocusAdminChat))); + Key(FocusAdminChat), + Key(Point))); //Gameplay VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nGameplay" }); diff --git a/Content.Server/GameObjects/Components/Pointing/PointingArrowComponent.cs b/Content.Server/GameObjects/Components/Pointing/PointingArrowComponent.cs new file mode 100644 index 0000000000..0c95a3d69f --- /dev/null +++ b/Content.Server/GameObjects/Components/Pointing/PointingArrowComponent.cs @@ -0,0 +1,101 @@ +#nullable enable +using Content.Shared.GameObjects.Components.Pointing; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using DrawDepth = Content.Shared.GameObjects.DrawDepth; + +namespace Content.Server.GameObjects.Components.Pointing +{ + [RegisterComponent] + public class PointingArrowComponent : SharedPointingArrowComponent + { + /// + /// The current amount of seconds left on this arrow. + /// + [ViewVariables(VVAccess.ReadWrite)] + private float _duration; + + /// + /// The amount of seconds before the arrow changes movement direction. + /// + [ViewVariables(VVAccess.ReadWrite)] + private float _step; + + /// + /// The amount of units that this arrow will move by when multiplied + /// by the frame time. + /// + [ViewVariables(VVAccess.ReadWrite)] + private float _speed; + + /// + /// The current amount of seconds left before the arrow changes + /// movement direction. + /// + [ViewVariables(VVAccess.ReadWrite)] + private float _currentStep; + + /// + /// Whether or not this arrow is currently going up. + /// + [ViewVariables(VVAccess.ReadWrite)] + private bool _up; + + /// + /// Whether or not this arrow will convert into a + /// when its duration runs out. + /// + [ViewVariables(VVAccess.ReadWrite)] + private bool _rogue; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _duration, "duration", 4); + serializer.DataField(ref _step, "step", 0.5f); + serializer.DataField(ref _speed, "speed", 1); + serializer.DataField(ref _rogue, "rogue", false); + } + + protected override void Startup() + { + base.Startup(); + + if (Owner.TryGetComponent(out SpriteComponent sprite)) + { + sprite.DrawDepth = (int) DrawDepth.Overlays; + } + } + + public void Update(float frameTime) + { + var movement = _speed * frameTime * (_up ? 1 : -1); + Owner.Transform.LocalPosition += (0, movement); + + _duration -= frameTime; + _currentStep -= frameTime; + + if (_duration <= 0) + { + if (_rogue) + { + Owner.RemoveComponent(); + Owner.AddComponent(); + return; + } + + Owner.Delete(); + return; + } + + if (_currentStep <= 0) + { + _currentStep = _step; + _up ^= true; + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Pointing/RoguePointingArrowComponent.cs b/Content.Server/GameObjects/Components/Pointing/RoguePointingArrowComponent.cs new file mode 100644 index 0000000000..5865bb1f82 --- /dev/null +++ b/Content.Server/GameObjects/Components/Pointing/RoguePointingArrowComponent.cs @@ -0,0 +1,137 @@ +#nullable enable +using System; +using System.Linq; +using Content.Server.Explosions; +using Content.Shared.GameObjects.Components.Pointing; +using Robust.Server.GameObjects; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.Player; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Maths; +using Robust.Shared.Random; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using DrawDepth = Content.Shared.GameObjects.DrawDepth; + +namespace Content.Server.GameObjects.Components.Pointing +{ + [RegisterComponent] + public class RoguePointingArrowComponent : SharedRoguePointingArrowComponent + { + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + [ViewVariables] + private IEntity? _chasing; + + [ViewVariables(VVAccess.ReadWrite)] + private float _turningDelay; + + [ViewVariables(VVAccess.ReadWrite)] + private float _chasingDelay; + + [ViewVariables(VVAccess.ReadWrite)] + private float _chasingSpeed; + + [ViewVariables(VVAccess.ReadWrite)] + private float _chasingTime; + + private IEntity? RandomNearbyPlayer() + { + var players = _playerManager + .GetPlayersInRange(Owner.Transform.GridPosition, 15) + .Where(player => player.AttachedEntity != null) + .ToArray(); + + if (players.Length == 0) + { + return null; + } + + return _random.Pick(players).AttachedEntity; + } + + private void UpdateAppearance() + { + if (_chasing == null || + !Owner.TryGetComponent(out AppearanceComponent appearance)) + { + return; + } + + appearance.SetData(RoguePointingArrowVisuals.Rotation, Owner.Transform.LocalRotation.Degrees); + } + + protected override void Startup() + { + base.Startup(); + + if (Owner.TryGetComponent(out SpriteComponent sprite)) + { + sprite.DrawDepth = (int) DrawDepth.Overlays; + } + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _turningDelay, "turningDelay", 2); + serializer.DataField(ref _chasingDelay, "chasingDelay", 1); + serializer.DataField(ref _chasingSpeed, "chasingSpeed", 5); + serializer.DataField(ref _chasingTime, "chasingTime", 1f); + } + + public void Update(float frameTime) + { + _chasing ??= RandomNearbyPlayer(); + + if (_chasing == null) + { + Owner.Delete(); + return; + } + + _turningDelay -= frameTime; + + if (_turningDelay > 0) + { + var difference = _chasing.Transform.WorldPosition - Owner.Transform.WorldPosition; + var angle = difference.ToAngle(); + var adjusted = angle.Degrees + 90; + var newAngle = Angle.FromDegrees(adjusted); + + Owner.Transform.LocalRotation = newAngle; + + UpdateAppearance(); + return; + } + + _chasingDelay -= frameTime; + + Owner.Transform.WorldRotation += Angle.FromDegrees(20); + + UpdateAppearance(); + + var toChased = _chasing.Transform.WorldPosition - Owner.Transform.WorldPosition; + + Owner.Transform.WorldPosition += toChased * frameTime * _chasingSpeed; + + _chasingTime -= frameTime; + + if (_chasingTime > 0) + { + return; + } + + ExplosionHelper.SpawnExplosion(Owner.Transform.GridPosition, 0, 2, 1, 1); + EntitySystem.Get().PlayFromEntity("/Audio/Effects/explosion.ogg", Owner); + + Owner.Delete(); + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/HandsSystem.cs b/Content.Server/GameObjects/EntitySystems/HandsSystem.cs index 754f098c60..85b96fdf39 100644 --- a/Content.Server/GameObjects/EntitySystems/HandsSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/HandsSystem.cs @@ -20,6 +20,8 @@ using Content.Server.GameObjects; using Content.Server.GameObjects.Components; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.EntitySystems.Click; +using Content.Shared.Interfaces; +using Robust.Shared.Maths; namespace Content.Server.Interfaces.GameObjects.Components.Interaction { @@ -47,8 +49,7 @@ namespace Content.Server.Interfaces.GameObjects.Components.Interaction .Bind(ContentKeyFunctions.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem)) .Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem)) .Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack)) - .Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt)) - .Register(); + .Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt)).Register(); } /// diff --git a/Content.Server/GameObjects/EntitySystems/PointingSystem.cs b/Content.Server/GameObjects/EntitySystems/PointingSystem.cs new file mode 100644 index 0000000000..67933dd25f --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/PointingSystem.cs @@ -0,0 +1,172 @@ +#nullable enable +using System; +using System.Collections.Generic; +using Content.Server.GameObjects.Components.Pointing; +using Content.Shared.Input; +using Content.Shared.Interfaces; +using JetBrains.Annotations; +using Robust.Server.Interfaces.Player; +using Robust.Server.Player; +using Robust.Shared.Enums; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Input.Binding; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.Interfaces.Timing; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Players; + +namespace Content.Server.GameObjects.EntitySystems +{ + [UsedImplicitly] + public class PointingSystem : EntitySystem + { +#pragma warning disable 649 + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; +#pragma warning restore 649 + + private static readonly TimeSpan PointDelay = TimeSpan.FromSeconds(0.5f); + + /// + /// A dictionary of players to the last time that they + /// pointed at something. + /// + private readonly Dictionary _pointers = new Dictionary(); + + private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) + { + if (e.NewStatus != SessionStatus.Disconnected) + { + return; + } + + _pointers.Remove(e.Session); + } + + // TODO: FOV + private void SendMessage(IEntity source, IList viewers, IEntity? pointed, string selfMessage, + string viewerMessage, string? viewerPointedAtMessage = null) + { + foreach (var viewer in viewers) + { + var viewerEntity = viewer.AttachedEntity; + if (viewerEntity == null) + { + continue; + } + + var message = viewerEntity == source + ? selfMessage + : viewerEntity == pointed && viewerPointedAtMessage != null + ? viewerPointedAtMessage + : viewerMessage; + + source.PopupMessage(viewer.AttachedEntity, message); + } + } + + public bool InRange(GridCoordinates from, GridCoordinates to) + { + return from.InRange(_mapManager, to, 15); + } + + public bool TryPoint(ICommonSession? session, GridCoordinates coords, EntityUid uid) + { + var player = session?.AttachedEntity; + if (player == null) + { + return false; + } + + if (_pointers.TryGetValue(session!, out var lastTime) && + _gameTiming.CurTime < lastTime + PointDelay) + { + return false; + } + + if (!InRange(coords, player.Transform.GridPosition)) + { + player.PopupMessage(player, Loc.GetString("You can't reach there!")); + return false; + } + + var diff = coords.ToMapPos(_mapManager) - player.Transform.MapPosition.Position; + if (diff.LengthSquared > 0.01f) + { + player.Transform.LocalRotation = new Angle(diff); + } + + var viewers = _playerManager.GetPlayersInRange(player.Transform.GridPosition, 15); + + EntityManager.SpawnEntity("pointingarrow", coords); + + string selfMessage; + string viewerMessage; + string? viewerPointedAtMessage = null; + + if (EntityManager.TryGetEntity(uid, out var pointed)) + { + selfMessage = player == pointed + ? Loc.GetString("You point at yourself.") + : Loc.GetString("You point at {0:theName}.", pointed); + + viewerMessage = player == pointed + ? $"{player.Name} {Loc.GetString("points at {0:themself}.", player)}" + : $"{player.Name} {Loc.GetString("points at {0:theName}.", pointed)}"; + + viewerPointedAtMessage = $"{player.Name} {Loc.GetString("points at you.")}"; + } + else + { + var tileRef = _mapManager.GetGrid(coords.GridID).GetTileRef(coords); + var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId]; + + selfMessage = Loc.GetString("You point at {0}.", tileDef.DisplayName); + + viewerMessage = $"{player.Name} {Loc.GetString("points at {0}.", tileDef.DisplayName)}"; + } + + _pointers[session!] = _gameTiming.CurTime; + + SendMessage(player, viewers, pointed, selfMessage, viewerMessage, viewerPointedAtMessage); + + return true; + } + + public override void Initialize() + { + base.Initialize(); + + _playerManager.PlayerStatusChanged += OnPlayerStatusChanged; + + EntityQuery = new TypeEntityQuery(typeof(PointingArrowComponent)); + + CommandBinds.Builder + .Bind(ContentKeyFunctions.Point, new PointerInputCmdHandler(TryPoint)) + .Register(); + } + + public override void Shutdown() + { + base.Shutdown(); + + _playerManager.PlayerStatusChanged -= OnPlayerStatusChanged; + _pointers.Clear(); + } + + public override void Update(float frameTime) + { + foreach (var entity in RelevantEntities) + { + entity.GetComponent().Update(frameTime); + } + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/RoguePointingSystem.cs b/Content.Server/GameObjects/EntitySystems/RoguePointingSystem.cs new file mode 100644 index 0000000000..7fcb5a06e7 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/RoguePointingSystem.cs @@ -0,0 +1,26 @@ +using Content.Server.GameObjects.Components.Pointing; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; + +namespace Content.Server.GameObjects.EntitySystems +{ + [UsedImplicitly] + public class RoguePointingSystem : EntitySystem + { + public override void Initialize() + { + base.Initialize(); + + EntityQuery = new TypeEntityQuery(typeof(RoguePointingArrowComponent)); + } + + public override void Update(float frameTime) + { + foreach (var entity in RelevantEntities) + { + entity.GetComponent().Update(frameTime); + } + } + } +} diff --git a/Content.Server/GlobalVerbs/PointingVerb.cs b/Content.Server/GlobalVerbs/PointingVerb.cs new file mode 100644 index 0000000000..b15ef785ad --- /dev/null +++ b/Content.Server/GlobalVerbs/PointingVerb.cs @@ -0,0 +1,53 @@ +using Content.Server.GameObjects.Components.Pointing; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Localization; + +namespace Content.Server.GlobalVerbs +{ + /// + /// Global verb that points at an entity. + /// + [GlobalVerb] + public class PointingVerb : GlobalVerb + { + public override bool RequireInteractionRange => false; + + public override void GetData(IEntity user, IEntity target, VerbData data) + { + data.Visibility = VerbVisibility.Invisible; + + if (!user.HasComponent()) + { + return; + } + + if (!EntitySystem.Get().InRange(user.Transform.GridPosition, target.Transform.GridPosition)) + { + return; + } + + if (target.HasComponent()) + { + return; + } + + data.Visibility = VerbVisibility.Visible; + + data.Text = Loc.GetString("Point at"); + } + + public override void Activate(IEntity user, IEntity target) + { + if (!user.TryGetComponent(out IActorComponent actor)) + { + return; + } + + EntitySystem.Get().TryPoint(actor.playerSession, target.Transform.GridPosition, target.Uid); + } + } +} diff --git a/Content.Shared/GameObjects/Components/Pointing/SharedPointingArrowComponent.cs b/Content.Shared/GameObjects/Components/Pointing/SharedPointingArrowComponent.cs new file mode 100644 index 0000000000..05e14bb666 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Pointing/SharedPointingArrowComponent.cs @@ -0,0 +1,11 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Pointing +{ + public class SharedPointingArrowComponent : Component + { + public sealed override string Name => "PointingArrow"; + } +} diff --git a/Content.Shared/GameObjects/Components/Pointing/SharedRoguePointingArrowComponent.cs b/Content.Shared/GameObjects/Components/Pointing/SharedRoguePointingArrowComponent.cs new file mode 100644 index 0000000000..a18ec2101a --- /dev/null +++ b/Content.Shared/GameObjects/Components/Pointing/SharedRoguePointingArrowComponent.cs @@ -0,0 +1,17 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Pointing +{ + public class SharedRoguePointingArrowComponent : Component + { + public sealed override string Name => "RoguePointingArrow"; + } + + [Serializable, NetSerializable] + public enum RoguePointingArrowVisuals + { + Rotation + } +} diff --git a/Content.Shared/Input/ContentKeyFunctions.cs b/Content.Shared/Input/ContentKeyFunctions.cs index 90b5820089..9d0ef01344 100644 --- a/Content.Shared/Input/ContentKeyFunctions.cs +++ b/Content.Shared/Input/ContentKeyFunctions.cs @@ -29,5 +29,6 @@ namespace Content.Shared.Input public static readonly BoundKeyFunction OpenTileSpawnWindow = "OpenTileSpawnWindow"; public static readonly BoundKeyFunction TakeScreenshot = "TakeScreenshot"; public static readonly BoundKeyFunction TakeScreenshotNoUI = "TakeScreenshotNoUI"; + public static readonly BoundKeyFunction Point = "Point"; } } diff --git a/Resources/Prototypes/Entities/Effects/Markers/pointing.yml b/Resources/Prototypes/Entities/Effects/Markers/pointing.yml new file mode 100644 index 0000000000..afceefcbc4 --- /dev/null +++ b/Resources/Prototypes/Entities/Effects/Markers/pointing.yml @@ -0,0 +1,15 @@ +- type: entity + name: pointing arrow + id: pointingarrow + components: + - type: Sprite + netsync: false + sprite: Interface/Misc/pointing.rsi + state: pointing + - type: PointingArrow + duration: 4 + step: 0.5 + speed: 1 + - type: Appearance + visuals: + - type: RoguePointingArrowVisualizer diff --git a/Resources/Textures/Interface/Misc/pointing.rsi/meta.json b/Resources/Textures/Interface/Misc/pointing.rsi/meta.json new file mode 100644 index 0000000000..87ab2ae0e7 --- /dev/null +++ b/Resources/Textures/Interface/Misc/pointing.rsi/meta.json @@ -0,0 +1,16 @@ +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/tgstation/tgstation/tree/96ac5662eb4977aedbff7ddc061eee100b3bf365", + "states": [ + { + "name": "pointing", + "directions": 1, + "delays": [[1]] + } + ] +} diff --git a/Resources/Textures/Interface/Misc/pointing.rsi/pointing.png b/Resources/Textures/Interface/Misc/pointing.rsi/pointing.png new file mode 100644 index 0000000000..c940418f08 Binary files /dev/null and b/Resources/Textures/Interface/Misc/pointing.rsi/pointing.png differ diff --git a/Resources/keybinds.yml b/Resources/keybinds.yml index 8a715222da..66d9113fb3 100644 --- a/Resources/keybinds.yml +++ b/Resources/keybinds.yml @@ -256,3 +256,7 @@ binds: type: state key: Tab mod1: Shift +- function: Point + type: State + key: MouseMiddle + mod1: Shift