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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.Function == EngineKeyFunctions.Use)
|
if (args.Function == EngineKeyFunctions.Use ||
|
||||||
|
args.Function == ContentKeyFunctions.Point)
|
||||||
{
|
{
|
||||||
var inputSys = _master.EntitySystemManager.GetEntitySystem<InputSystem>();
|
var inputSys = _master.EntitySystemManager.GetEntitySystem<InputSystem>();
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ namespace Content.Client.Input
|
|||||||
human.AddFunction(ContentKeyFunctions.MouseMiddle);
|
human.AddFunction(ContentKeyFunctions.MouseMiddle);
|
||||||
human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
|
human.AddFunction(ContentKeyFunctions.ToggleCombatMode);
|
||||||
human.AddFunction(ContentKeyFunctions.WideAttack);
|
human.AddFunction(ContentKeyFunctions.WideAttack);
|
||||||
|
human.AddFunction(ContentKeyFunctions.Point);
|
||||||
|
|
||||||
var ghost = contexts.New("ghost", "common");
|
var ghost = contexts.New("ghost", "common");
|
||||||
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
ghost.AddFunction(EngineKeyFunctions.MoveUp);
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ Do wide attack: [color=#a4885c]{23}[/color]
|
|||||||
Use targeted entity: [color=#a4885c]{11}[/color]
|
Use targeted entity: [color=#a4885c]{11}[/color]
|
||||||
Throw held item: [color=#a4885c]{12}[/color]
|
Throw held item: [color=#a4885c]{12}[/color]
|
||||||
Examine entity: [color=#a4885c]{13}[/color]
|
Examine entity: [color=#a4885c]{13}[/color]
|
||||||
|
Point somewhere: [color=#a4885c]{28}[/color]
|
||||||
Open entity context menu: [color=#a4885c]{14}[/color]
|
Open entity context menu: [color=#a4885c]{14}[/color]
|
||||||
Toggle combat mode: [color=#a4885c]{15}[/color]
|
Toggle combat mode: [color=#a4885c]{15}[/color]
|
||||||
Toggle console: [color=#a4885c]{16}[/color]
|
Toggle console: [color=#a4885c]{16}[/color]
|
||||||
@@ -114,7 +115,8 @@ Toggle sandbox window: [color=#a4885c]{21}[/color]",
|
|||||||
Key(SmartEquipBackpack),
|
Key(SmartEquipBackpack),
|
||||||
Key(SmartEquipBelt),
|
Key(SmartEquipBelt),
|
||||||
Key(FocusOOC),
|
Key(FocusOOC),
|
||||||
Key(FocusAdminChat)));
|
Key(FocusAdminChat),
|
||||||
|
Key(Point)));
|
||||||
|
|
||||||
//Gameplay
|
//Gameplay
|
||||||
VBox.AddChild(new Label { FontOverride = headerFont, Text = "\nGameplay" });
|
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;
|
||||||
using Content.Server.GameObjects.Components.Items.Storage;
|
using Content.Server.GameObjects.Components.Items.Storage;
|
||||||
using Content.Server.GameObjects.EntitySystems.Click;
|
using Content.Server.GameObjects.EntitySystems.Click;
|
||||||
|
using Content.Shared.Interfaces;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
|
||||||
namespace Content.Server.Interfaces.GameObjects.Components.Interaction
|
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.ActivateItemInHand, InputCmdHandler.FromDelegate(HandleActivateItem))
|
||||||
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
|
.Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
|
||||||
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
|
.Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
|
||||||
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt))
|
.Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt)).Register<HandsSystem>();
|
||||||
.Register<HandsSystem>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <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 OpenTileSpawnWindow = "OpenTileSpawnWindow";
|
||||||
public static readonly BoundKeyFunction TakeScreenshot = "TakeScreenshot";
|
public static readonly BoundKeyFunction TakeScreenshot = "TakeScreenshot";
|
||||||
public static readonly BoundKeyFunction TakeScreenshotNoUI = "TakeScreenshotNoUI";
|
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
|
type: state
|
||||||
key: Tab
|
key: Tab
|
||||||
mod1: Shift
|
mod1: Shift
|
||||||
|
- function: Point
|
||||||
|
type: State
|
||||||
|
key: MouseMiddle
|
||||||
|
mod1: Shift
|
||||||
|
|||||||
Reference in New Issue
Block a user