Pinpointer (#5131)
* Add pinpointer sprites * Start working on pinpointer * Updated pinpointer * Working on visuals * Working on a pinpointer and eye rotation * Add client/server pinpointers systems * Minor cleanup * Add distance support * Add nuke tag * Remove redundant flag and add pinpointer to caps locker * Disable rotation of pinpointer * Fixed distance Co-authored-by: Alexander Evgrashin <evgrashin.adl@gmail.com>
88
Content.Client/Pinpointer/ClientPinpointerSystem.cs
Normal file
@@ -0,0 +1,88 @@
|
||||
using Content.Shared.Pinpointer;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Pinpointer
|
||||
{
|
||||
public sealed class ClientPinpointerSystem : SharedPinpointerSystem
|
||||
{
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PinpointerComponent, ComponentHandleState>(HandleCompState);
|
||||
}
|
||||
|
||||
public override void FrameUpdate(float frameTime)
|
||||
{
|
||||
base.FrameUpdate(frameTime);
|
||||
|
||||
// we want to show pinpointers arrow direction relative
|
||||
// to players eye rotation (like it was in SS13)
|
||||
|
||||
// because eye can change it rotation anytime
|
||||
// we need to update this arrow in a update loop
|
||||
foreach (var uid in ActivePinpointers)
|
||||
{
|
||||
UpdateEyeDir(uid);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCompState(EntityUid uid, PinpointerComponent pinpointer, ref ComponentHandleState args)
|
||||
{
|
||||
if (args.Current is not PinpointerComponentState state) return;
|
||||
SetActive(uid, state.IsActive, pinpointer);
|
||||
SetDirection(uid, state.DirectionToTarget, pinpointer);
|
||||
SetDistance(uid, state.DistanceToTarget, pinpointer);
|
||||
|
||||
UpdateAppearance(uid, pinpointer);
|
||||
UpdateEyeDir(uid, pinpointer);
|
||||
}
|
||||
|
||||
private void UpdateAppearance(EntityUid uid, PinpointerComponent? pinpointer = null,
|
||||
AppearanceComponent? appearance = null)
|
||||
{
|
||||
if (!Resolve(uid, ref pinpointer, ref appearance))
|
||||
return;
|
||||
|
||||
appearance.SetData(PinpointerVisuals.IsActive, pinpointer.IsActive);
|
||||
appearance.SetData(PinpointerVisuals.TargetDistance, pinpointer.DistanceToTarget);
|
||||
}
|
||||
|
||||
private void UpdateDirAppearance(EntityUid uid, Direction dir,PinpointerComponent? pinpointer = null,
|
||||
AppearanceComponent? appearance = null)
|
||||
{
|
||||
if (!Resolve(uid, ref pinpointer, ref appearance))
|
||||
return;
|
||||
|
||||
appearance.SetData(PinpointerVisuals.TargetDirection, dir);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform pinpointer arrow from world space to eye space
|
||||
/// And send it to the appearance component
|
||||
/// </summary>
|
||||
private void UpdateEyeDir(EntityUid uid, PinpointerComponent? pinpointer = null)
|
||||
{
|
||||
if (!Resolve(uid, ref pinpointer))
|
||||
return;
|
||||
|
||||
var worldDir = pinpointer.DirectionToTarget;
|
||||
if (worldDir == Direction.Invalid)
|
||||
{
|
||||
UpdateDirAppearance(uid, Direction.Invalid, pinpointer);
|
||||
return;
|
||||
}
|
||||
|
||||
var eye = _eyeManager.CurrentEye;
|
||||
var angle = worldDir.ToAngle() + eye.Rotation;
|
||||
var eyeDir = angle.GetDir();
|
||||
UpdateDirAppearance(uid, eyeDir, pinpointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
60
Content.Client/Pinpointer/PinpointerVisualizer.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Content.Shared.Pinpointer;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.Pinpointer
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class PinpointerVisualizer : AppearanceVisualizer
|
||||
{
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
base.OnChangeData(component);
|
||||
|
||||
if (!component.Owner.TryGetComponent<SpriteComponent>(out var sprite))
|
||||
return;
|
||||
|
||||
// check if pinpointer screen is active
|
||||
if (!component.TryGetData(PinpointerVisuals.IsActive, out bool isActive) || !isActive)
|
||||
{
|
||||
sprite.LayerSetVisible(PinpointerLayers.Screen, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if it has direction to target
|
||||
sprite.LayerSetVisible(PinpointerLayers.Screen, true);
|
||||
sprite.LayerSetRotation(PinpointerLayers.Screen, Angle.Zero);
|
||||
|
||||
if (!component.TryGetData(PinpointerVisuals.TargetDirection, out Direction dir) || dir == Direction.Invalid)
|
||||
{
|
||||
sprite.LayerSetState(PinpointerLayers.Screen, "pinonnull");
|
||||
return;
|
||||
}
|
||||
|
||||
// check distance to target
|
||||
if (!component.TryGetData(PinpointerVisuals.TargetDistance, out Distance dis))
|
||||
dis = Distance.UNKNOWN;
|
||||
|
||||
switch (dis)
|
||||
{
|
||||
case Distance.REACHED:
|
||||
sprite.LayerSetState(PinpointerLayers.Screen, "pinondirect");
|
||||
break;
|
||||
case Distance.CLOSE:
|
||||
sprite.LayerSetState(PinpointerLayers.Screen, "pinonclose");
|
||||
sprite.LayerSetRotation(PinpointerLayers.Screen, dir.ToAngle());
|
||||
break;
|
||||
case Distance.MEDIUM:
|
||||
sprite.LayerSetState(PinpointerLayers.Screen, "pinonmedium");
|
||||
sprite.LayerSetRotation(PinpointerLayers.Screen, dir.ToAngle());
|
||||
break;
|
||||
case Distance.FAR:
|
||||
case Distance.UNKNOWN:
|
||||
sprite.LayerSetState(PinpointerLayers.Screen, "pinonfar");
|
||||
sprite.LayerSetRotation(PinpointerLayers.Screen, dir.ToAngle());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
158
Content.Server/Pinpointer/ServerPinpointerSystem.cs
Normal file
@@ -0,0 +1,158 @@
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Pinpointer;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Pinpointer
|
||||
{
|
||||
public sealed class ServerPinpointerSystem : SharedPinpointerSystem
|
||||
{
|
||||
[Dependency] private readonly IEntityLookup _entityLookup = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PinpointerComponent, UseInHandEvent>(OnUseInHand);
|
||||
}
|
||||
|
||||
private void OnUseInHand(EntityUid uid, PinpointerComponent component, UseInHandEvent args)
|
||||
{
|
||||
TogglePinpointer(uid, component);
|
||||
|
||||
// try to find target from whitelist
|
||||
if (component.IsActive && component.Whitelist != null)
|
||||
{
|
||||
var target = FindTargetFromWhitelist(uid, component.Whitelist);
|
||||
SetTarget(uid, target, component);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
// because target or pinpointer can move
|
||||
// we need to update pinpointers arrow each frame
|
||||
foreach (var uid in ActivePinpointers)
|
||||
{
|
||||
UpdateDirectionToTarget(uid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to find the closest entity from whitelist on a current map
|
||||
/// Will return null if can't find anything
|
||||
/// </summary>
|
||||
private EntityUid? FindTargetFromWhitelist(EntityUid uid, EntityWhitelist whitelist,
|
||||
ITransformComponent? transform = null)
|
||||
{
|
||||
if (!Resolve(uid, ref transform))
|
||||
return null;
|
||||
|
||||
var mapId = transform.MapID;
|
||||
var ents = _entityLookup.GetEntitiesInMap(mapId);
|
||||
|
||||
// sort all entities in distance increasing order
|
||||
var l = new SortedList<float, EntityUid>();
|
||||
foreach (var e in ents)
|
||||
{
|
||||
if (whitelist.IsValid(e))
|
||||
{
|
||||
var dist = (e.Transform.WorldPosition - transform.WorldPosition).LengthSquared;
|
||||
l.TryAdd(dist, e.Uid);
|
||||
}
|
||||
}
|
||||
|
||||
// return uid with a smallest distacne
|
||||
return l.Count > 0 ? l.First().Value : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set pinpointers target to track
|
||||
/// </summary>
|
||||
public void SetTarget(EntityUid uid, EntityUid? target, PinpointerComponent? pinpointer = null)
|
||||
{
|
||||
if (!Resolve(uid, ref pinpointer))
|
||||
return;
|
||||
|
||||
if (pinpointer.Target == target)
|
||||
return;
|
||||
|
||||
pinpointer.Target = target;
|
||||
if (pinpointer.IsActive)
|
||||
UpdateDirectionToTarget(uid, pinpointer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update direction from pinpointer to selected target (if it was set)
|
||||
/// </summary>
|
||||
private void UpdateDirectionToTarget(EntityUid uid, PinpointerComponent? pinpointer = null)
|
||||
{
|
||||
if (!Resolve(uid, ref pinpointer))
|
||||
return;
|
||||
|
||||
var target = pinpointer.Target;
|
||||
if (target == null || !EntityManager.EntityExists(target.Value))
|
||||
{
|
||||
SetDirection(uid, Direction.Invalid, pinpointer);
|
||||
SetDistance(uid, Distance.UNKNOWN, pinpointer);
|
||||
return;
|
||||
}
|
||||
|
||||
var dirVec = CalculateDirection(uid, target.Value);
|
||||
if (dirVec != null)
|
||||
{
|
||||
var dir = dirVec.Value.GetDir();
|
||||
SetDirection(uid, dir, pinpointer);
|
||||
var dist = CalculateDistance(uid, dirVec.Value, pinpointer);
|
||||
SetDistance(uid, dist, pinpointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetDirection(uid, Direction.Invalid, pinpointer);
|
||||
SetDistance(uid, Distance.UNKNOWN, pinpointer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate direction from pinUid to trgUid
|
||||
/// </summary>
|
||||
/// <returns>Null if failed to caluclate distance between two entities</returns>
|
||||
private Vector2? CalculateDirection(EntityUid pinUid, EntityUid trgUid)
|
||||
{
|
||||
// check if entities have transform component
|
||||
if (!EntityManager.TryGetComponent(pinUid, out ITransformComponent? pin))
|
||||
return null;
|
||||
if (!EntityManager.TryGetComponent(trgUid, out ITransformComponent? trg))
|
||||
return null;
|
||||
|
||||
// check if they are on same map
|
||||
if (pin.MapID != trg.MapID)
|
||||
return null;
|
||||
|
||||
// get world direction vector
|
||||
var dir = (trg.WorldPosition - pin.WorldPosition);
|
||||
return dir;
|
||||
}
|
||||
|
||||
private Distance CalculateDistance(EntityUid uid, Vector2 vec, PinpointerComponent? pinpointer = null)
|
||||
{
|
||||
if (!Resolve(uid, ref pinpointer))
|
||||
return Distance.UNKNOWN;
|
||||
|
||||
var dist = vec.Length;
|
||||
if (dist <= pinpointer.ReachedDistance)
|
||||
return Distance.REACHED;
|
||||
else if (dist <= pinpointer.CloseDistance)
|
||||
return Distance.CLOSE;
|
||||
else if (dist <= pinpointer.MediumDistance)
|
||||
return Distance.MEDIUM;
|
||||
else
|
||||
return Distance.FAR;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
Content.Shared/Pinpointer/PinpointerComponent.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Shared.Pinpointer
|
||||
{
|
||||
[RegisterComponent]
|
||||
[NetworkedComponent]
|
||||
[Friend(typeof(SharedPinpointerSystem))]
|
||||
public class PinpointerComponent : Component
|
||||
{
|
||||
public override string Name => "Pinpointer";
|
||||
|
||||
[DataField("whitelist")]
|
||||
public EntityWhitelist? Whitelist;
|
||||
|
||||
[DataField("mediumDistance")]
|
||||
public float MediumDistance = 16f;
|
||||
|
||||
[DataField("closeDistance")]
|
||||
public float CloseDistance = 8f;
|
||||
|
||||
[DataField("reachedDistance")]
|
||||
public float ReachedDistance = 1f;
|
||||
|
||||
public EntityUid? Target = null;
|
||||
public bool IsActive = false;
|
||||
public Direction DirectionToTarget = Direction.Invalid;
|
||||
public Distance DistanceToTarget = Distance.UNKNOWN;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class PinpointerComponentState : ComponentState
|
||||
{
|
||||
public bool IsActive;
|
||||
public Direction DirectionToTarget;
|
||||
public Distance DistanceToTarget;
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum Distance : byte
|
||||
{
|
||||
UNKNOWN,
|
||||
REACHED,
|
||||
CLOSE,
|
||||
MEDIUM,
|
||||
FAR
|
||||
}
|
||||
}
|
||||
19
Content.Shared/Pinpointer/PinpointerVisuals.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Robust.Shared.Serialization;
|
||||
using System;
|
||||
|
||||
namespace Content.Shared.Pinpointer
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public enum PinpointerVisuals : byte
|
||||
{
|
||||
IsActive,
|
||||
TargetDirection,
|
||||
TargetDistance
|
||||
}
|
||||
|
||||
public enum PinpointerLayers : byte
|
||||
{
|
||||
Base,
|
||||
Screen
|
||||
}
|
||||
}
|
||||
93
Content.Shared/Pinpointer/SharedPinpointerSystem.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Shared.Pinpointer
|
||||
{
|
||||
public abstract class SharedPinpointerSystem : EntitySystem
|
||||
{
|
||||
protected readonly HashSet<EntityUid> ActivePinpointers = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<PinpointerComponent, ComponentGetState>(GetCompState);
|
||||
}
|
||||
|
||||
private void GetCompState(EntityUid uid, PinpointerComponent pinpointer, ref ComponentGetState args)
|
||||
{
|
||||
args.State = new PinpointerComponentState
|
||||
{
|
||||
IsActive = pinpointer.IsActive,
|
||||
DirectionToTarget = pinpointer.DirectionToTarget,
|
||||
DistanceToTarget = pinpointer.DistanceToTarget
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually set distance from pinpointer to target
|
||||
/// </summary>
|
||||
public void SetDistance(EntityUid uid, Distance distance, PinpointerComponent? pinpointer = null)
|
||||
{
|
||||
if (!Resolve(uid, ref pinpointer))
|
||||
return;
|
||||
|
||||
if (distance == pinpointer.DistanceToTarget)
|
||||
return;
|
||||
|
||||
pinpointer.DistanceToTarget = distance;
|
||||
pinpointer.Dirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Manually set pinpointer arrow direction
|
||||
/// </summary>
|
||||
public void SetDirection(EntityUid uid, Direction directionToTarget, PinpointerComponent? pinpointer = null)
|
||||
{
|
||||
if (!Resolve(uid, ref pinpointer))
|
||||
return;
|
||||
|
||||
if (directionToTarget == pinpointer.DirectionToTarget)
|
||||
return;
|
||||
|
||||
pinpointer.DirectionToTarget = directionToTarget;
|
||||
pinpointer.Dirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activate/deactivate pinpointer screen. If it has target it will start tracking it.
|
||||
/// </summary>
|
||||
public void SetActive(EntityUid uid, bool isActive, PinpointerComponent? pinpointer = null)
|
||||
{
|
||||
if (!Resolve(uid, ref pinpointer))
|
||||
return;
|
||||
if (isActive == pinpointer.IsActive)
|
||||
return;
|
||||
|
||||
// add-remove pinpointer from update list
|
||||
if (isActive)
|
||||
ActivePinpointers.Add(uid);
|
||||
else
|
||||
ActivePinpointers.Remove(uid);
|
||||
|
||||
pinpointer.IsActive = isActive;
|
||||
pinpointer.Dirty();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Toggle Pinpointer screen. If it has target it will start tracking it.
|
||||
/// </summary>
|
||||
/// <returns>True if pinpointer was activated, false otherwise</returns>
|
||||
public bool TogglePinpointer(EntityUid uid, PinpointerComponent? pinpointer = null)
|
||||
{
|
||||
if (!Resolve(uid, ref pinpointer))
|
||||
return false;
|
||||
|
||||
var isActive = !pinpointer.IsActive;
|
||||
SetActive(uid, isActive, pinpointer);
|
||||
return isActive;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,8 @@
|
||||
contents:
|
||||
- id: NukeDisk
|
||||
prob: 1
|
||||
- id: PinpointerNuclear
|
||||
prob: 1
|
||||
- id: CaptainIDCard
|
||||
prob: 1
|
||||
- id: ClothingHeadHatCaptain
|
||||
|
||||
33
Resources/Prototypes/Entities/Objects/Devices/pinpointer.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
- type: entity
|
||||
name: pinpointer
|
||||
description: A handheld tracking device that locks onto certain signals.
|
||||
parent: BaseItem
|
||||
id: PinpointerBase
|
||||
abstract: true
|
||||
components:
|
||||
- type: Transform
|
||||
noRot: True
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
sprite: Objects/Devices/pinpointer.rsi
|
||||
layers:
|
||||
- state: pinpointer
|
||||
map: ["enum.PinpointerLayers.Base"]
|
||||
- state: pinonnull
|
||||
map: ["enum.PinpointerLayers.Screen"]
|
||||
- type: Item
|
||||
sprite: Objects/Devices/pinpointer.rsi
|
||||
- type: Pinpointer
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: PinpointerVisualizer
|
||||
|
||||
- type: entity
|
||||
name: pinpointer
|
||||
id: PinpointerNuclear
|
||||
parent: PinpointerBase
|
||||
components:
|
||||
- type: Pinpointer
|
||||
whitelist:
|
||||
tags:
|
||||
- NukeDisk
|
||||
@@ -4,6 +4,9 @@
|
||||
id: NukeDisk
|
||||
description: A nuclear auth disk, capable of arming a nuke if used along with a code. Note from nanotrasen reads "THIS IS YOUR MOST IMPORTANT POSESSION, SECURE DAT FUKKEN DISK!"
|
||||
components:
|
||||
- type: Tag
|
||||
tags:
|
||||
- NukeDisk
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
sprite: Objects/Misc/nukedisk.rsi
|
||||
@@ -14,6 +17,17 @@
|
||||
state: icon
|
||||
|
||||
- type: entity
|
||||
parent: NukeDisk
|
||||
name: nuclear authentication disk
|
||||
parent: BaseItem
|
||||
id: NukeDiskFake
|
||||
suffix: Fake
|
||||
description: A nuclear auth disk, capable of.. WAIT THIS IS JUST PAINTED PLASTIC, FUCK-
|
||||
components:
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
sprite: Objects/Misc/nukedisk.rsi
|
||||
state: icon
|
||||
- type: Item
|
||||
size: 12
|
||||
sprite: Objects/Misc/nukedisk.rsi
|
||||
state: icon
|
||||
|
||||
@@ -223,3 +223,5 @@
|
||||
- type: Tag
|
||||
id: HideContextMenu
|
||||
|
||||
- type: Tag
|
||||
id: NukeDisk
|
||||
|
||||
|
After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 198 B |
162
Resources/Textures/Objects/Devices/pinpointer.rsi/meta.json
Normal file
@@ -0,0 +1,162 @@
|
||||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/59f2a4e10e5ba36033c9734ddebfbbdc6157472d",
|
||||
"states": [
|
||||
{
|
||||
"name": "pinonalert",
|
||||
"directions": 8,
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
],
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
],
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
],
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
],
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
],
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
],
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
],
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinonalertdirect",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinonalertnull",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinonclose",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinondirect",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinondirectlarge",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinondirectsmall",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinondirectxtrlarge",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinonfar",
|
||||
"delays": [
|
||||
[
|
||||
0.6,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinonmedium",
|
||||
"delays": [
|
||||
[
|
||||
0.4,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinonnull",
|
||||
"delays": [
|
||||
[
|
||||
0.2,
|
||||
0.2
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "pinpointer"
|
||||
},
|
||||
{
|
||||
"name": "pinpointer_crew"
|
||||
},
|
||||
{
|
||||
"name": "pinpointer_crewprox"
|
||||
},
|
||||
{
|
||||
"name": "pinpointer_syndicate"
|
||||
},
|
||||
{
|
||||
"name": "pinpointer_way"
|
||||
},
|
||||
{
|
||||
"name": "inhand-left",
|
||||
"directions": 4
|
||||
},
|
||||
{
|
||||
"name": "inhand-right",
|
||||
"directions": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
Resources/Textures/Objects/Devices/pinpointer.rsi/pinonalert.png
Normal file
|
After Width: | Height: | Size: 577 B |
|
After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 212 B |
BIN
Resources/Textures/Objects/Devices/pinpointer.rsi/pinonclose.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 166 B |
|
After Width: | Height: | Size: 173 B |
|
After Width: | Height: | Size: 152 B |
|
After Width: | Height: | Size: 159 B |
BIN
Resources/Textures/Objects/Devices/pinpointer.rsi/pinonfar.png
Normal file
|
After Width: | Height: | Size: 230 B |
|
After Width: | Height: | Size: 4.9 KiB |
BIN
Resources/Textures/Objects/Devices/pinpointer.rsi/pinonnull.png
Normal file
|
After Width: | Height: | Size: 182 B |
BIN
Resources/Textures/Objects/Devices/pinpointer.rsi/pinpointer.png
Normal file
|
After Width: | Height: | Size: 276 B |
|
After Width: | Height: | Size: 265 B |
|
After Width: | Height: | Size: 315 B |
|
After Width: | Height: | Size: 237 B |
|
After Width: | Height: | Size: 266 B |