mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-10 10:51:14 +03:00
Signed-off-by: Prole <172158352+Prole0@users.noreply.github.com> Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com> Co-authored-by: ScarKy0 <106310278+ScarKy0@users.noreply.github.com> Co-authored-by: Samuka-C <47865393+Samuka-C@users.noreply.github.com> Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Co-authored-by: Partmedia <kevinz5000@gmail.com> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Co-authored-by: themias <89101928+themias@users.noreply.github.com> Co-authored-by: Victor Shen <71985089+Vexerot@users.noreply.github.com> Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com> Co-authored-by: Milon <milonpl.git@proton.me> Co-authored-by: Kirus59 <145689588+Kirus59@users.noreply.github.com> Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Co-authored-by: Stomf <5dorkydorks@gmail.com> Co-authored-by: drakewill-CRL <46307022+drakewill-CRL@users.noreply.github.com> Co-authored-by: PraxisMapper <praxismapper@gmail.com> Co-authored-by: EmoGarbage404 <retron404@gmail.com> Co-authored-by: lzk <124214523+lzk228@users.noreply.github.com> Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Co-authored-by: IProduceWidgets <107586145+IProduceWidgets@users.noreply.github.com> Co-authored-by: TytosB <54259736+TytosB@users.noreply.github.com> Co-authored-by: abadaba695 <spacestation13thingy@gmail.com> Co-authored-by: kosticia <kosticia46@gmail.com> Co-authored-by: Thinbug <101073555+Thinbug0@users.noreply.github.com> Co-authored-by: pathetic meowmeow <uhhadd@gmail.com> Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Co-authored-by: Boaz1111 <149967078+Boaz1111@users.noreply.github.com> Co-authored-by: ActiveMammmoth <140334666+ActiveMammmoth@users.noreply.github.com> Co-authored-by: Myra <vasilis@pikachu.systems> Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com> Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com> Co-authored-by: K-Dynamic <20566341+K-Dynamic@users.noreply.github.com> Co-authored-by: Gentleman-Bird <dcgreen406@gmail.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: BIGZi0348 <svalker0348@gmail.com> Co-authored-by: LaCumbiaDelCoronavirus <90893484+LaCumbiaDelCoronavirus@users.noreply.github.com> Co-authored-by: imatsoup <93290208+imatsoup@users.noreply.github.com> Co-authored-by: Matthew Herber <32679887+happyrobot33@users.noreply.github.com> Co-authored-by: Ertanic <36124833+Ertanic@users.noreply.github.com> Co-authored-by: MissKay1994 <15877268+MissKay1994@users.noreply.github.com> Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com> Co-authored-by: eoineoineoin <helloworld@eoinrul.es> Co-authored-by: Tiniest Shark <head.rebel@yahoo.com> Co-authored-by: nikitosych <boriszyn@gmail.com> Co-authored-by: Tayrtahn <tayrtahn@gmail.com> Co-authored-by: Perry Fraser <perryprog@users.noreply.github.com> Co-authored-by: YoungThug <ramialanbagy@gmail.com> Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> Co-authored-by: Southbridge <7013162+southbridge-fur@users.noreply.github.com> Co-authored-by: Vladislav Suchkov <20380250+murolem@users.noreply.github.com> Co-authored-by: Prole <172158352+Prole0@users.noreply.github.com> Co-authored-by: Unkn0wn_Gh0st <shadowstalkermll@gmail.com> Co-authored-by: 3nderall <101940324+3nderall@users.noreply.github.com> Co-authored-by: Radezolid <snappednexus@gmail.com> Co-authored-by: J <billsmith116@gmail.com> Co-authored-by: Ghagliiarghii <68826635+Ghagliiarghii@users.noreply.github.com> Co-authored-by: chromiumboy <50505512+chromiumboy@users.noreply.github.com> Co-authored-by: youtissoum <51883137+youtissoum@users.noreply.github.com> Co-authored-by: Minemoder5000 <minemoder50000@gmail.com> Co-authored-by: Spanky <scott@wearejacob.com> Co-authored-by: Spessmann <156740760+Spessmann@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: brainfood1183 <113240905+brainfood1183@users.noreply.github.com> Co-authored-by: Deerstop <edainturner@gmail.com> Co-authored-by: B_Kirill <153602297+B-Kirill@users.noreply.github.com> Co-authored-by: archee1 <archee3@hotmail.co.uk> Co-authored-by: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Co-authored-by: Quantum-cross <7065792+Quantum-cross@users.noreply.github.com> Co-authored-by: poklj <compgeek223@gmail.com> Co-authored-by: Krunklehorn <42424291+Krunklehorn@users.noreply.github.com> Co-authored-by: OnyxTheBrave <131422822+OnyxTheBrave@users.noreply.github.com> Co-authored-by: UpAndLeaves <92269094+Alpha-Two@users.noreply.github.com> Co-authored-by: Flareguy <78941145+Flareguy@users.noreply.github.com> Co-authored-by: Zalycon <84675130+Zalycon@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: Verm <32827189+Vermidia@users.noreply.github.com> Co-authored-by: nikthechampiongr <32041239+nikthechampiongr@users.noreply.github.com> Co-authored-by: ScarKy0 <scarky0@onet.eu> Co-authored-by: Dmitry <57028746+dimm00n@users.noreply.github.com>
455 lines
15 KiB
C#
455 lines
15 KiB
C#
using Content.Server.Administration;
|
|
using Content.Server.Interaction;
|
|
using Content.Server.Popups;
|
|
using Content.Server.Stunnable;
|
|
using Content.Shared.Administration;
|
|
using Content.Shared.Examine;
|
|
using Content.Shared.Instruments;
|
|
using Content.Shared.Instruments.UI;
|
|
using Content.Shared.Physics;
|
|
using Content.Shared.Popups;
|
|
using JetBrains.Annotations;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Shared.Audio.Midi;
|
|
using Robust.Shared.Collections;
|
|
using Robust.Shared.Configuration;
|
|
using Robust.Shared.Console;
|
|
using Robust.Shared.GameStates;
|
|
using Robust.Shared.Player;
|
|
using Robust.Shared.Timing;
|
|
|
|
namespace Content.Server.Instruments;
|
|
|
|
[UsedImplicitly]
|
|
public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
|
{
|
|
[Dependency] private readonly IGameTiming _timing = default!;
|
|
[Dependency] private readonly IConsoleHost _conHost = default!;
|
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
|
[Dependency] private readonly StunSystem _stuns = default!;
|
|
[Dependency] private readonly UserInterfaceSystem _bui = default!;
|
|
[Dependency] private readonly PopupSystem _popup = default!;
|
|
[Dependency] private readonly TransformSystem _transform = default!;
|
|
[Dependency] private readonly ExamineSystemShared _examineSystem = default!;
|
|
|
|
private const float MaxInstrumentBandRange = 10f;
|
|
|
|
// Band Requests are queued and delayed both to avoid metagaming and to prevent spamming it, since it's expensive.
|
|
private const float BandRequestDelay = 1.0f;
|
|
private TimeSpan _bandRequestTimer = TimeSpan.Zero;
|
|
private readonly List<InstrumentBandRequestBuiMessage> _bandRequestQueue = new();
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
InitializeCVars();
|
|
|
|
SubscribeNetworkEvent<InstrumentMidiEventEvent>(OnMidiEventRx);
|
|
SubscribeNetworkEvent<InstrumentStartMidiEvent>(OnMidiStart);
|
|
SubscribeNetworkEvent<InstrumentStopMidiEvent>(OnMidiStop);
|
|
SubscribeNetworkEvent<InstrumentSetMasterEvent>(OnMidiSetMaster);
|
|
SubscribeNetworkEvent<InstrumentSetFilteredChannelEvent>(OnMidiSetFilteredChannel);
|
|
|
|
Subs.BuiEvents<InstrumentComponent>(InstrumentUiKey.Key, subs =>
|
|
{
|
|
subs.Event<BoundUIClosedEvent>(OnBoundUIClosed);
|
|
subs.Event<BoundUIOpenedEvent>(OnBoundUIOpened);
|
|
subs.Event<InstrumentBandRequestBuiMessage>(OnBoundUIRequestBands);
|
|
});
|
|
|
|
SubscribeLocalEvent<InstrumentComponent, ComponentGetState>(OnStrumentGetState);
|
|
|
|
_conHost.RegisterCommand("addtoband", AddToBandCommand);
|
|
}
|
|
|
|
private void OnStrumentGetState(EntityUid uid, InstrumentComponent component, ref ComponentGetState args)
|
|
{
|
|
args.State = new InstrumentComponentState()
|
|
{
|
|
Playing = component.Playing,
|
|
InstrumentProgram = component.InstrumentProgram,
|
|
InstrumentBank = component.InstrumentBank,
|
|
AllowPercussion = component.AllowPercussion,
|
|
AllowProgramChange = component.AllowProgramChange,
|
|
RespectMidiLimits = component.RespectMidiLimits,
|
|
Master = GetNetEntity(component.Master),
|
|
FilteredChannels = component.FilteredChannels
|
|
};
|
|
}
|
|
|
|
[AdminCommand(AdminFlags.Fun)]
|
|
private void AddToBandCommand(IConsoleShell shell, string _, string[] args)
|
|
{
|
|
if (!NetEntity.TryParse(args[0], out var firstUidNet) || !TryGetEntity(firstUidNet, out var firstUid))
|
|
{
|
|
shell.WriteError($"Cannot parse first Uid");
|
|
return;
|
|
}
|
|
|
|
if (!NetEntity.TryParse(args[1], out var secondUidNet) || !TryGetEntity(secondUidNet, out var secondUid))
|
|
{
|
|
shell.WriteError($"Cannot parse second Uid");
|
|
return;
|
|
}
|
|
|
|
if (!HasComp<ActiveInstrumentComponent>(secondUid))
|
|
{
|
|
shell.WriteError($"Puppet instrument is not active!");
|
|
return;
|
|
}
|
|
|
|
var otherInstrument = Comp<InstrumentComponent>(secondUid.Value);
|
|
otherInstrument.Playing = true;
|
|
otherInstrument.Master = firstUid;
|
|
Dirty(secondUid.Value, otherInstrument);
|
|
}
|
|
|
|
private void OnMidiStart(InstrumentStartMidiEvent msg, EntitySessionEventArgs args)
|
|
{
|
|
var uid = GetEntity(msg.Uid);
|
|
|
|
if (!TryComp(uid, out InstrumentComponent? instrument))
|
|
return;
|
|
|
|
if (args.SenderSession.AttachedEntity != instrument.InstrumentPlayer)
|
|
return;
|
|
|
|
instrument.Playing = true;
|
|
Dirty(uid, instrument);
|
|
}
|
|
|
|
private void OnMidiStop(InstrumentStopMidiEvent msg, EntitySessionEventArgs args)
|
|
{
|
|
var uid = GetEntity(msg.Uid);
|
|
|
|
if (!TryComp(uid, out InstrumentComponent? instrument))
|
|
return;
|
|
|
|
if (args.SenderSession.AttachedEntity != instrument.InstrumentPlayer)
|
|
return;
|
|
|
|
Clean(uid, instrument);
|
|
}
|
|
|
|
private void OnMidiSetMaster(InstrumentSetMasterEvent msg, EntitySessionEventArgs args)
|
|
{
|
|
var uid = GetEntity(msg.Uid);
|
|
var master = GetEntity(msg.Master);
|
|
|
|
if (!HasComp<ActiveInstrumentComponent>(uid))
|
|
return;
|
|
|
|
if (!TryComp(uid, out InstrumentComponent? instrument))
|
|
return;
|
|
|
|
if (args.SenderSession.AttachedEntity != instrument.InstrumentPlayer)
|
|
return;
|
|
|
|
if (master != null)
|
|
{
|
|
if (!HasComp<ActiveInstrumentComponent>(master))
|
|
return;
|
|
|
|
if (!TryComp<InstrumentComponent>(master, out var masterInstrument) || masterInstrument.Master != null)
|
|
return;
|
|
|
|
instrument.Master = master;
|
|
instrument.FilteredChannels.SetAll(false);
|
|
instrument.Playing = true;
|
|
Dirty(uid, instrument);
|
|
return;
|
|
}
|
|
|
|
// Cleanup when disabling master...
|
|
if (master == null && instrument.Master != null)
|
|
{
|
|
Clean(uid, instrument);
|
|
}
|
|
}
|
|
|
|
private void OnMidiSetFilteredChannel(InstrumentSetFilteredChannelEvent msg, EntitySessionEventArgs args)
|
|
{
|
|
var uid = GetEntity(msg.Uid);
|
|
|
|
if (!TryComp(uid, out InstrumentComponent? instrument))
|
|
return;
|
|
|
|
if (args.SenderSession.AttachedEntity != instrument.InstrumentPlayer)
|
|
return;
|
|
|
|
if (msg.Channel == RobustMidiEvent.PercussionChannel && !instrument.AllowPercussion)
|
|
return;
|
|
|
|
instrument.FilteredChannels[msg.Channel] = msg.Value;
|
|
|
|
if (msg.Value)
|
|
{
|
|
// Prevent stuck notes when turning off a channel... Shrimple.
|
|
RaiseNetworkEvent(new InstrumentMidiEventEvent(msg.Uid, new []{RobustMidiEvent.AllNotesOff((byte)msg.Channel, 0)}));
|
|
}
|
|
|
|
Dirty(uid, instrument);
|
|
}
|
|
|
|
private void OnBoundUIClosed(EntityUid uid, InstrumentComponent component, BoundUIClosedEvent args)
|
|
{
|
|
if (HasComp<ActiveInstrumentComponent>(uid)
|
|
&& !_bui.IsUiOpen(uid, args.UiKey))
|
|
{
|
|
RemComp<ActiveInstrumentComponent>(uid);
|
|
}
|
|
|
|
Clean(uid, component);
|
|
}
|
|
|
|
private void OnBoundUIOpened(EntityUid uid, InstrumentComponent component, BoundUIOpenedEvent args)
|
|
{
|
|
EnsureComp<ActiveInstrumentComponent>(uid);
|
|
Clean(uid, component);
|
|
}
|
|
|
|
private void OnBoundUIRequestBands(EntityUid uid, InstrumentComponent component, InstrumentBandRequestBuiMessage args)
|
|
{
|
|
foreach (var request in _bandRequestQueue)
|
|
{
|
|
// Prevent spamming requests for the same entity.
|
|
if (request.Entity == args.Entity)
|
|
return;
|
|
}
|
|
|
|
_bandRequestQueue.Add(args);
|
|
}
|
|
|
|
public (NetEntity, string)[] GetBands(EntityUid uid)
|
|
{
|
|
var metadataQuery = EntityManager.GetEntityQuery<MetaDataComponent>();
|
|
|
|
if (Deleted(uid))
|
|
return Array.Empty<(NetEntity, string)>();
|
|
|
|
var list = new ValueList<(NetEntity, string)>();
|
|
var instrumentQuery = EntityManager.GetEntityQuery<InstrumentComponent>();
|
|
|
|
if (!TryComp(uid, out InstrumentComponent? originInstrument)
|
|
|| originInstrument.InstrumentPlayer is not {} originPlayer)
|
|
return Array.Empty<(NetEntity, string)>();
|
|
|
|
// It's probably faster to get all possible active instruments than all entities in range
|
|
var activeEnumerator = EntityManager.EntityQueryEnumerator<ActiveInstrumentComponent>();
|
|
while (activeEnumerator.MoveNext(out var entity, out _))
|
|
{
|
|
if (entity == uid)
|
|
continue;
|
|
|
|
// Don't grab puppet instruments.
|
|
if (!instrumentQuery.TryGetComponent(entity, out var instrument) || instrument.Master != null)
|
|
continue;
|
|
|
|
// We want to use the instrument player's name.
|
|
if (instrument.InstrumentPlayer is not {} playerUid)
|
|
continue;
|
|
|
|
// Maybe a bit expensive but oh well GetBands is queued and has a timer anyway.
|
|
// Make sure the instrument is visible
|
|
if (!_examineSystem.InRangeUnOccluded(uid, entity, MaxInstrumentBandRange, e => e == playerUid || e == originPlayer))
|
|
continue;
|
|
|
|
if (!metadataQuery.TryGetComponent(playerUid, out var playerMetadata)
|
|
|| !metadataQuery.TryGetComponent(entity, out var metadata))
|
|
continue;
|
|
|
|
list.Add((GetNetEntity(entity), $"{playerMetadata.EntityName} - {metadata.EntityName}"));
|
|
}
|
|
|
|
return list.ToArray();
|
|
}
|
|
|
|
public void Clean(EntityUid uid, InstrumentComponent? instrument = null)
|
|
{
|
|
if (!Resolve(uid, ref instrument))
|
|
return;
|
|
|
|
if (instrument.Playing)
|
|
{
|
|
var netUid = GetNetEntity(uid);
|
|
|
|
// Reset puppet instruments too.
|
|
RaiseNetworkEvent(new InstrumentMidiEventEvent(netUid, new[]{RobustMidiEvent.SystemReset(0)}));
|
|
|
|
RaiseNetworkEvent(new InstrumentStopMidiEvent(netUid));
|
|
}
|
|
|
|
instrument.Playing = false;
|
|
instrument.Master = null;
|
|
instrument.FilteredChannels.SetAll(false);
|
|
instrument.LastSequencerTick = 0;
|
|
instrument.BatchesDropped = 0;
|
|
instrument.LaggedBatches = 0;
|
|
Dirty(uid, instrument);
|
|
}
|
|
|
|
private void OnMidiEventRx(InstrumentMidiEventEvent msg, EntitySessionEventArgs args)
|
|
{
|
|
var uid = GetEntity(msg.Uid);
|
|
|
|
if (!TryComp(uid, out InstrumentComponent? instrument))
|
|
return;
|
|
|
|
if (!instrument.Playing
|
|
|| args.SenderSession.AttachedEntity != instrument.InstrumentPlayer
|
|
|| instrument.InstrumentPlayer == null
|
|
|| args.SenderSession.AttachedEntity is not { } attached)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var send = true;
|
|
|
|
var minTick = uint.MaxValue;
|
|
var maxTick = uint.MinValue;
|
|
|
|
for (var i = 0; i < msg.MidiEvent.Length; i++)
|
|
{
|
|
var tick = msg.MidiEvent[i].Tick;
|
|
|
|
if (tick < minTick)
|
|
minTick = tick;
|
|
|
|
if (tick > maxTick)
|
|
maxTick = tick;
|
|
}
|
|
|
|
if (instrument.LastSequencerTick > minTick)
|
|
{
|
|
instrument.LaggedBatches++;
|
|
|
|
if (instrument.RespectMidiLimits)
|
|
{
|
|
if (instrument.LaggedBatches == (int) (MaxMidiLaggedBatches * (1 / 3d) + 1))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("instrument-component-finger-cramps-light-message"),
|
|
uid, attached, PopupType.SmallCaution);
|
|
}
|
|
else if (instrument.LaggedBatches == (int) (MaxMidiLaggedBatches * (2 / 3d) + 1))
|
|
{
|
|
_popup.PopupEntity(Loc.GetString("instrument-component-finger-cramps-serious-message"),
|
|
uid, attached, PopupType.MediumCaution);
|
|
}
|
|
}
|
|
|
|
if (instrument.LaggedBatches > MaxMidiLaggedBatches)
|
|
{
|
|
send = false;
|
|
}
|
|
}
|
|
|
|
if (++instrument.MidiEventCount > MaxMidiEventsPerSecond
|
|
|| msg.MidiEvent.Length > MaxMidiEventsPerBatch)
|
|
{
|
|
instrument.BatchesDropped++;
|
|
|
|
send = false;
|
|
}
|
|
|
|
instrument.LastSequencerTick = Math.Max(maxTick, minTick);
|
|
|
|
if (send || !instrument.RespectMidiLimits)
|
|
{
|
|
RaiseNetworkEvent(msg);
|
|
}
|
|
}
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
|
|
if (_bandRequestQueue.Count > 0 && _bandRequestTimer < _timing.RealTime)
|
|
{
|
|
_bandRequestTimer = _timing.RealTime.Add(TimeSpan.FromSeconds(BandRequestDelay));
|
|
|
|
foreach (var request in _bandRequestQueue)
|
|
{
|
|
var entity = GetEntity(request.Entity);
|
|
|
|
var nearby = GetBands(entity);
|
|
_bui.ServerSendUiMessage(entity, request.UiKey, new InstrumentBandResponseBuiMessage(nearby), request.Actor);
|
|
}
|
|
|
|
_bandRequestQueue.Clear();
|
|
}
|
|
|
|
var activeQuery = EntityManager.GetEntityQuery<ActiveInstrumentComponent>();
|
|
var transformQuery = EntityManager.GetEntityQuery<TransformComponent>();
|
|
|
|
var query = AllEntityQuery<ActiveInstrumentComponent, InstrumentComponent>();
|
|
while (query.MoveNext(out var uid, out _, out var instrument))
|
|
{
|
|
if (instrument.Master is {} master)
|
|
{
|
|
if (Deleted(master))
|
|
{
|
|
Clean(uid, instrument);
|
|
}
|
|
|
|
var masterActive = activeQuery.CompOrNull(master);
|
|
if (masterActive == null)
|
|
{
|
|
Clean(uid, instrument);
|
|
}
|
|
|
|
var trans = transformQuery.GetComponent(uid);
|
|
var masterTrans = transformQuery.GetComponent(master);
|
|
if (!_transform.InRange(masterTrans.Coordinates, trans.Coordinates, 10f)
|
|
)
|
|
{
|
|
Clean(uid, instrument);
|
|
}
|
|
}
|
|
|
|
if (instrument.RespectMidiLimits &&
|
|
(instrument.BatchesDropped >= MaxMidiBatchesDropped
|
|
|| instrument.LaggedBatches >= MaxMidiLaggedBatches))
|
|
{
|
|
if (instrument.InstrumentPlayer is {Valid: true} mob)
|
|
{
|
|
_stuns.TryParalyze(mob, TimeSpan.FromSeconds(1), true);
|
|
|
|
_popup.PopupEntity(Loc.GetString("instrument-component-finger-cramps-max-message"),
|
|
uid, mob, PopupType.LargeCaution);
|
|
}
|
|
|
|
// Just in case
|
|
Clean(uid);
|
|
_bui.CloseUi(uid, InstrumentUiKey.Key);
|
|
}
|
|
|
|
instrument.Timer += frameTime;
|
|
if (instrument.Timer < 1)
|
|
continue;
|
|
|
|
instrument.Timer = 0f;
|
|
instrument.MidiEventCount = 0;
|
|
instrument.LaggedBatches = 0;
|
|
instrument.BatchesDropped = 0;
|
|
}
|
|
}
|
|
|
|
public void ToggleInstrumentUi(EntityUid uid, EntityUid actor, InstrumentComponent? component = null)
|
|
{
|
|
if (!Resolve(uid, ref component))
|
|
return;
|
|
|
|
_bui.TryToggleUi(uid, InstrumentUiKey.Key, actor);
|
|
}
|
|
|
|
public override bool ResolveInstrument(EntityUid uid, ref SharedInstrumentComponent? component)
|
|
{
|
|
if (component is not null)
|
|
return true;
|
|
|
|
TryComp<InstrumentComponent>(uid, out var localComp);
|
|
component = localComp;
|
|
return component != null;
|
|
}
|
|
}
|