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:
|
contents:
|
||||||
- id: NukeDisk
|
- id: NukeDisk
|
||||||
prob: 1
|
prob: 1
|
||||||
|
- id: PinpointerNuclear
|
||||||
|
prob: 1
|
||||||
- id: CaptainIDCard
|
- id: CaptainIDCard
|
||||||
prob: 1
|
prob: 1
|
||||||
- id: ClothingHeadHatCaptain
|
- 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
|
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!"
|
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:
|
components:
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- NukeDisk
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
netsync: false
|
netsync: false
|
||||||
sprite: Objects/Misc/nukedisk.rsi
|
sprite: Objects/Misc/nukedisk.rsi
|
||||||
@@ -14,6 +17,17 @@
|
|||||||
state: icon
|
state: icon
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: NukeDisk
|
name: nuclear authentication disk
|
||||||
|
parent: BaseItem
|
||||||
id: NukeDiskFake
|
id: NukeDiskFake
|
||||||
|
suffix: Fake
|
||||||
description: A nuclear auth disk, capable of.. WAIT THIS IS JUST PAINTED PLASTIC, FUCK-
|
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
|
- type: Tag
|
||||||
id: HideContextMenu
|
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 |