Add pointing (#1435)
* Add pointing keybind and simple message * Add turning the player when pointing * Add pointing arrow * Make the popup message appear on the pointing entity * Add pointing to tiles and space and proper grammar * Move pointing bind from HandsSystem to PointingSystem * Add more messages for pointing depending on the viewer perspective * Fix non nullable reference type * Serialize pointing arrow duration * Serialize pointing arrow step and add summaries * Make arrow speed serializable and make it depend on frame time * Add 0.2 second delay between pointings * Add pointing arrow yaml examples * Add the ability for pointing arrows to be rogue * Remove rogue package reference * Add point to verb https://cdn.discordapp.com/attachments/313107470031650816/735268651636228197/unknown.png * Add shift middle clicking an entity in the verb menu to point at it * Add VV to PointingArrowComponent * Increase pointing delay from 0.2 to 0.5 seconds * Address reviews * Fix nullability errors * Separate pointing and rogue pointing code * Fix rogue pointing arrow visuals * Made rogue pointing arrow rotation adjustment readable for mortals * Make rogue pointing arrows less destructive * Cleanup more of the rogue pointing arrow code
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<double>(RoguePointingArrowVisuals.Rotation, out var degrees))
|
||||
{
|
||||
SetRotation(component, Angle.FromDegrees(degrees));
|
||||
}
|
||||
}
|
||||
|
||||
private void SetRotation(AppearanceComponent component, Angle rotation)
|
||||
{
|
||||
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<InputSystem>();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" });
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The current amount of seconds left on this arrow.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private float _duration;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of seconds before the arrow changes movement direction.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private float _step;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of units that this arrow will move by when multiplied
|
||||
/// by the frame time.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private float _speed;
|
||||
|
||||
/// <summary>
|
||||
/// The current amount of seconds left before the arrow changes
|
||||
/// movement direction.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private float _currentStep;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this arrow is currently going up.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private bool _up;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this arrow will convert into a
|
||||
/// <see cref="RoguePointingArrowComponent"/> when its duration runs out.
|
||||
/// </summary>
|
||||
[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<PointingArrowComponent>();
|
||||
Owner.AddComponent<RoguePointingArrowComponent>();
|
||||
return;
|
||||
}
|
||||
|
||||
Owner.Delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_currentStep <= 0)
|
||||
{
|
||||
_currentStep = _step;
|
||||
_up ^= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<AudioSystem>().PlayFromEntity("/Audio/Effects/explosion.ogg", Owner);
|
||||
|
||||
Owner.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<HandsSystem>();
|
||||
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt)).Register<HandsSystem>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
172
Content.Server/GameObjects/EntitySystems/PointingSystem.cs
Normal file
172
Content.Server/GameObjects/EntitySystems/PointingSystem.cs
Normal file
@@ -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);
|
||||
|
||||
/// <summary>
|
||||
/// A dictionary of players to the last time that they
|
||||
/// pointed at something.
|
||||
/// </summary>
|
||||
private readonly Dictionary<ICommonSession, TimeSpan> _pointers = new Dictionary<ICommonSession, TimeSpan>();
|
||||
|
||||
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<IPlayerSession> 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<PointingSystem>();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
_playerManager.PlayerStatusChanged -= OnPlayerStatusChanged;
|
||||
_pointers.Clear();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
foreach (var entity in RelevantEntities)
|
||||
{
|
||||
entity.GetComponent<PointingArrowComponent>().Update(frameTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<RoguePointingArrowComponent>().Update(frameTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
53
Content.Server/GlobalVerbs/PointingVerb.cs
Normal file
53
Content.Server/GlobalVerbs/PointingVerb.cs
Normal file
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Global verb that points at an entity.
|
||||
/// </summary>
|
||||
[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<IActorComponent>())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntitySystem.Get<PointingSystem>().InRange(user.Transform.GridPosition, target.Transform.GridPosition))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (target.HasComponent<PointingArrowComponent>())
|
||||
{
|
||||
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<PointingSystem>().TryPoint(actor.playerSession, target.Transform.GridPosition, target.Uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
15
Resources/Prototypes/Entities/Effects/Markers/pointing.yml
Normal file
15
Resources/Prototypes/Entities/Effects/Markers/pointing.yml
Normal file
@@ -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
|
||||
16
Resources/Textures/Interface/Misc/pointing.rsi/meta.json
Normal file
16
Resources/Textures/Interface/Misc/pointing.rsi/meta.json
Normal file
@@ -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]]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Interface/Misc/pointing.rsi/pointing.png
Normal file
BIN
Resources/Textures/Interface/Misc/pointing.rsi/pointing.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 631 B |
@@ -256,3 +256,7 @@ binds:
|
||||
type: state
|
||||
key: Tab
|
||||
mod1: Shift
|
||||
- function: Point
|
||||
type: State
|
||||
key: MouseMiddle
|
||||
mod1: Shift
|
||||
|
||||
Reference in New Issue
Block a user