2025-06-11 20:32:48 +02:00
using System.Linq ;
2023-07-16 21:12:53 +02:00
using Content.Server.Administration ;
2025-06-11 20:32:48 +02:00
using Content.Server.Administration.Logs ;
2023-07-16 21:12:53 +02:00
using Content.Server.Interaction ;
using Content.Server.Popups ;
2021-11-28 01:47:36 +01:00
using Content.Server.Stunnable ;
2023-07-16 21:12:53 +02:00
using Content.Shared.Administration ;
2025-06-11 20:32:48 +02:00
using Content.Shared.CCVar ;
using Content.Shared.Database ;
2024-06-07 17:57:07 -07:00
using Content.Shared.Examine ;
2021-11-28 01:47:36 +01:00
using Content.Shared.Instruments ;
2023-07-16 21:12:53 +02:00
using Content.Shared.Instruments.UI ;
using Content.Shared.Physics ;
2021-11-28 01:47:36 +01:00
using Content.Shared.Popups ;
2020-08-13 22:17:12 +10:00
using JetBrains.Annotations ;
2022-04-04 23:08:36 -07:00
using Robust.Server.GameObjects ;
2023-07-16 21:12:53 +02:00
using Robust.Shared.Audio.Midi ;
using Robust.Shared.Collections ;
2021-02-11 01:13:03 -08:00
using Robust.Shared.Configuration ;
2023-07-16 21:12:53 +02:00
using Robust.Shared.Console ;
2023-09-12 14:43:06 +10:00
using Robust.Shared.GameStates ;
2023-10-28 09:59:53 +11:00
using Robust.Shared.Player ;
2023-07-16 21:12:53 +02:00
using Robust.Shared.Timing ;
2025-06-11 20:32:48 +02:00
using Robust.Shared.Utility ;
2020-05-18 13:29:31 +02:00
2021-11-28 01:47:36 +01:00
namespace Content.Server.Instruments ;
[UsedImplicitly]
public sealed partial class InstrumentSystem : SharedInstrumentSystem
2020-05-18 13:29:31 +02:00
{
2023-07-16 21:12:53 +02:00
[Dependency] private readonly IGameTiming _timing = default ! ;
[Dependency] private readonly IConsoleHost _conHost = default ! ;
2021-11-28 01:47:36 +01:00
[Dependency] private readonly IConfigurationManager _cfg = default ! ;
2023-07-16 21:12:53 +02:00
[Dependency] private readonly StunSystem _stuns = default ! ;
[Dependency] private readonly UserInterfaceSystem _bui = default ! ;
[Dependency] private readonly PopupSystem _popup = default ! ;
[Dependency] private readonly TransformSystem _transform = default ! ;
2024-06-07 17:57:07 -07:00
[Dependency] private readonly ExamineSystemShared _examineSystem = default ! ;
2025-06-11 20:32:48 +02:00
[Dependency] private readonly IAdminLogManager _admingLogSystem = default ! ;
2023-07-16 21:12:53 +02:00
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 ( ) ;
2021-11-28 01:47:36 +01:00
public override void Initialize ( )
2020-05-18 13:29:31 +02:00
{
2021-11-28 01:47:36 +01:00
base . Initialize ( ) ;
2020-11-27 17:12:45 +01:00
2021-11-28 01:47:36 +01:00
InitializeCVars ( ) ;
2020-11-27 17:12:45 +01:00
2021-11-28 01:47:36 +01:00
SubscribeNetworkEvent < InstrumentMidiEventEvent > ( OnMidiEventRx ) ;
SubscribeNetworkEvent < InstrumentStartMidiEvent > ( OnMidiStart ) ;
SubscribeNetworkEvent < InstrumentStopMidiEvent > ( OnMidiStop ) ;
2023-07-16 21:12:53 +02:00
SubscribeNetworkEvent < InstrumentSetMasterEvent > ( OnMidiSetMaster ) ;
SubscribeNetworkEvent < InstrumentSetFilteredChannelEvent > ( OnMidiSetFilteredChannel ) ;
2025-06-11 20:32:48 +02:00
SubscribeNetworkEvent < InstrumentSetChannelsEvent > ( OnMidiSetChannels ) ;
2021-11-23 18:19:08 +00:00
2024-01-14 08:18:39 +01:00
Subs . BuiEvents < InstrumentComponent > ( InstrumentUiKey . Key , subs = >
{
subs . Event < BoundUIClosedEvent > ( OnBoundUIClosed ) ;
subs . Event < BoundUIOpenedEvent > ( OnBoundUIOpened ) ;
subs . Event < InstrumentBandRequestBuiMessage > ( OnBoundUIRequestBands ) ;
} ) ;
2023-07-16 21:12:53 +02:00
2023-09-12 14:43:06 +10:00
SubscribeLocalEvent < InstrumentComponent , ComponentGetState > ( OnStrumentGetState ) ;
2023-07-16 21:12:53 +02:00
_conHost . RegisterCommand ( "addtoband" , AddToBandCommand ) ;
}
2023-09-12 14:43:06 +10:00
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
} ;
}
2023-07-16 21:12:53 +02:00
[AdminCommand(AdminFlags.Fun)]
private void AddToBandCommand ( IConsoleShell shell , string _ , string [ ] args )
{
2023-09-11 09:42:41 +10:00
if ( ! NetEntity . TryParse ( args [ 0 ] , out var firstUidNet ) | | ! TryGetEntity ( firstUidNet , out var firstUid ) )
2023-07-16 21:12:53 +02:00
{
shell . WriteError ( $"Cannot parse first Uid" ) ;
return ;
}
2023-09-11 09:42:41 +10:00
if ( ! NetEntity . TryParse ( args [ 1 ] , out var secondUidNet ) | | ! TryGetEntity ( secondUidNet , out var secondUid ) )
2023-07-16 21:12:53 +02:00
{
shell . WriteError ( $"Cannot parse second Uid" ) ;
return ;
}
if ( ! HasComp < ActiveInstrumentComponent > ( secondUid ) )
{
shell . WriteError ( $"Puppet instrument is not active!" ) ;
return ;
}
2023-09-11 09:42:41 +10:00
var otherInstrument = Comp < InstrumentComponent > ( secondUid . Value ) ;
2023-07-16 21:12:53 +02:00
otherInstrument . Playing = true ;
otherInstrument . Master = firstUid ;
2023-09-11 09:42:41 +10:00
Dirty ( secondUid . Value , otherInstrument ) ;
2021-11-28 01:47:36 +01:00
}
2020-11-27 17:12:45 +01:00
2021-11-28 01:47:36 +01:00
private void OnMidiStart ( InstrumentStartMidiEvent msg , EntitySessionEventArgs args )
{
2023-09-11 09:42:41 +10:00
var uid = GetEntity ( msg . Uid ) ;
2020-11-27 17:12:45 +01:00
2023-07-16 21:12:53 +02:00
if ( ! TryComp ( uid , out InstrumentComponent ? instrument ) )
2021-11-28 01:47:36 +01:00
return ;
2024-04-26 18:16:24 +10:00
if ( args . SenderSession . AttachedEntity ! = instrument . InstrumentPlayer )
2021-11-28 01:47:36 +01:00
return ;
instrument . Playing = true ;
2023-07-16 21:12:53 +02:00
Dirty ( uid , instrument ) ;
2021-11-28 01:47:36 +01:00
}
private void OnMidiStop ( InstrumentStopMidiEvent msg , EntitySessionEventArgs args )
{
2023-09-11 09:42:41 +10:00
var uid = GetEntity ( msg . Uid ) ;
2020-11-27 17:12:45 +01:00
2023-07-16 21:12:53 +02:00
if ( ! TryComp ( uid , out InstrumentComponent ? instrument ) )
2021-11-28 01:47:36 +01:00
return ;
2024-04-26 18:16:24 +10:00
if ( args . SenderSession . AttachedEntity ! = instrument . InstrumentPlayer )
2021-11-28 01:47:36 +01:00
return ;
Clean ( uid , instrument ) ;
}
2025-06-11 20:32:48 +02:00
private void OnMidiSetChannels ( InstrumentSetChannelsEvent msg , EntitySessionEventArgs args )
{
var uid = GetEntity ( msg . Uid ) ;
if ( ! TryComp ( uid , out InstrumentComponent ? instrument ) | | ! TryComp ( uid , out ActiveInstrumentComponent ? activeInstrument ) )
return ;
if ( args . SenderSession . AttachedEntity ! = instrument . InstrumentPlayer )
return ;
if ( msg . Tracks . Length > RobustMidiEvent . MaxChannels )
{
Log . Warning ( $"{args.SenderSession.UserId.ToString()} - Tried to send tracks over the limit! Received: {msg.Tracks.Length}; Limit: {RobustMidiEvent.MaxChannels}" ) ;
return ;
}
2025-07-07 14:23:45 +02:00
foreach ( var t in msg . Tracks )
{
// Remove any control characters that may be part of the midi file so they don't end up in the admin logs.
t ? . SanitizeFields ( ) ;
// Truncate any track names too long.
t ? . TruncateFields ( _cfg . GetCVar ( CCVars . MidiMaxChannelNameLength ) ) ;
}
2025-06-11 20:32:48 +02:00
var tracksString = string . Join ( "\n" ,
msg . Tracks
. Where ( t = > t ! = null )
. Select ( t = > t ! . ToString ( ) ) ) ;
_admingLogSystem . Add (
LogType . Instrument ,
LogImpact . Low ,
$"{ToPrettyString(args.SenderSession.AttachedEntity)} set the midi channels for {ToPrettyString(uid)} to {tracksString}" ) ;
activeInstrument . Tracks = msg . Tracks ;
Dirty ( uid , activeInstrument ) ;
}
2023-07-16 21:12:53 +02:00
private void OnMidiSetMaster ( InstrumentSetMasterEvent msg , EntitySessionEventArgs args )
{
2023-09-11 09:42:41 +10:00
var uid = GetEntity ( msg . Uid ) ;
var master = GetEntity ( msg . Master ) ;
2023-07-16 21:12:53 +02:00
if ( ! HasComp < ActiveInstrumentComponent > ( uid ) )
return ;
if ( ! TryComp ( uid , out InstrumentComponent ? instrument ) )
return ;
2024-04-26 18:16:24 +10:00
if ( args . SenderSession . AttachedEntity ! = instrument . InstrumentPlayer )
2023-07-16 21:12:53 +02:00
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 )
{
2023-09-11 09:42:41 +10:00
var uid = GetEntity ( msg . Uid ) ;
2023-07-16 21:12:53 +02:00
if ( ! TryComp ( uid , out InstrumentComponent ? instrument ) )
return ;
2024-04-26 18:16:24 +10:00
if ( args . SenderSession . AttachedEntity ! = instrument . InstrumentPlayer )
2023-07-16 21:12:53 +02:00
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.
2023-09-11 09:42:41 +10:00
RaiseNetworkEvent ( new InstrumentMidiEventEvent ( msg . Uid , new [ ] { RobustMidiEvent . AllNotesOff ( ( byte ) msg . Channel , 0 ) } ) ) ;
2023-07-16 21:12:53 +02:00
}
Dirty ( uid , instrument ) ;
}
2022-04-04 23:08:36 -07:00
private void OnBoundUIClosed ( EntityUid uid , InstrumentComponent component , BoundUIClosedEvent args )
{
if ( HasComp < ActiveInstrumentComponent > ( uid )
2024-04-26 18:16:24 +10:00
& & ! _bui . IsUiOpen ( uid , args . UiKey ) )
2022-04-04 23:08:36 -07:00
{
RemComp < ActiveInstrumentComponent > ( uid ) ;
}
Clean ( uid , component ) ;
}
private void OnBoundUIOpened ( EntityUid uid , InstrumentComponent component , BoundUIOpenedEvent args )
{
EnsureComp < ActiveInstrumentComponent > ( uid ) ;
Clean ( uid , component ) ;
}
2023-07-16 21:12:53 +02:00
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 ) ;
}
2023-09-11 09:42:41 +10:00
public ( NetEntity , string ) [ ] GetBands ( EntityUid uid )
2023-07-16 21:12:53 +02:00
{
2025-06-26 19:50:49 -04:00
var metadataQuery = GetEntityQuery < MetaDataComponent > ( ) ;
2023-07-16 21:12:53 +02:00
2025-04-16 11:08:22 +00:00
if ( Deleted ( uid ) )
2023-09-11 09:42:41 +10:00
return Array . Empty < ( NetEntity , string ) > ( ) ;
2023-07-16 21:12:53 +02:00
2023-09-11 09:42:41 +10:00
var list = new ValueList < ( NetEntity , string ) > ( ) ;
2025-06-26 19:50:49 -04:00
var instrumentQuery = GetEntityQuery < InstrumentComponent > ( ) ;
2023-07-16 21:12:53 +02:00
if ( ! TryComp ( uid , out InstrumentComponent ? originInstrument )
2024-04-26 18:16:24 +10:00
| | originInstrument . InstrumentPlayer is not { } originPlayer )
2023-09-11 09:42:41 +10:00
return Array . Empty < ( NetEntity , string ) > ( ) ;
2023-07-16 21:12:53 +02:00
// It's probably faster to get all possible active instruments than all entities in range
2025-06-26 19:50:49 -04:00
var activeEnumerator = EntityQueryEnumerator < ActiveInstrumentComponent > ( ) ;
2023-07-16 21:12:53 +02:00
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.
2024-04-26 18:16:24 +10:00
if ( instrument . InstrumentPlayer is not { } playerUid )
2023-07-16 21:12:53 +02:00
continue ;
// Maybe a bit expensive but oh well GetBands is queued and has a timer anyway.
2024-06-07 17:57:07 -07:00
// Make sure the instrument is visible
if ( ! _examineSystem . InRangeUnOccluded ( uid , entity , MaxInstrumentBandRange , e = > e = = playerUid | | e = = originPlayer ) )
2023-07-16 21:12:53 +02:00
continue ;
if ( ! metadataQuery . TryGetComponent ( playerUid , out var playerMetadata )
| | ! metadataQuery . TryGetComponent ( entity , out var metadata ) )
continue ;
2023-09-11 09:42:41 +10:00
list . Add ( ( GetNetEntity ( entity ) , $"{playerMetadata.EntityName} - {metadata.EntityName}" ) ) ;
2023-07-16 21:12:53 +02:00
}
return list . ToArray ( ) ;
}
2021-11-28 01:47:36 +01:00
public void Clean ( EntityUid uid , InstrumentComponent ? instrument = null )
{
if ( ! Resolve ( uid , ref instrument ) )
return ;
if ( instrument . Playing )
2020-11-27 17:12:45 +01:00
{
2023-09-11 09:42:41 +10:00
var netUid = GetNetEntity ( uid ) ;
2023-07-16 21:12:53 +02:00
// Reset puppet instruments too.
2023-09-11 09:42:41 +10:00
RaiseNetworkEvent ( new InstrumentMidiEventEvent ( netUid , new [ ] { RobustMidiEvent . SystemReset ( 0 ) } ) ) ;
2023-07-16 21:12:53 +02:00
2023-09-11 09:42:41 +10:00
RaiseNetworkEvent ( new InstrumentStopMidiEvent ( netUid ) ) ;
2020-11-27 17:12:45 +01:00
}
2021-11-28 01:47:36 +01:00
instrument . Playing = false ;
2023-07-16 21:12:53 +02:00
instrument . Master = null ;
instrument . FilteredChannels . SetAll ( false ) ;
2021-11-28 01:47:36 +01:00
instrument . LastSequencerTick = 0 ;
instrument . BatchesDropped = 0 ;
instrument . LaggedBatches = 0 ;
2023-07-16 21:12:53 +02:00
Dirty ( uid , instrument ) ;
2021-11-28 01:47:36 +01:00
}
private void OnMidiEventRx ( InstrumentMidiEventEvent msg , EntitySessionEventArgs args )
{
2023-09-11 09:42:41 +10:00
var uid = GetEntity ( msg . Uid ) ;
2021-11-28 01:47:36 +01:00
2022-04-08 16:22:05 +02:00
if ( ! TryComp ( uid , out InstrumentComponent ? instrument ) )
2021-11-28 01:47:36 +01:00
return ;
2021-12-06 15:34:46 +01:00
if ( ! instrument . Playing
2024-04-26 18:16:24 +10:00
| | args . SenderSession . AttachedEntity ! = instrument . InstrumentPlayer
2021-12-06 15:34:46 +01:00
| | instrument . InstrumentPlayer = = null
2023-09-11 09:42:41 +10:00
| | args . SenderSession . AttachedEntity is not { } attached )
{
2021-11-28 01:47:36 +01:00
return ;
2023-09-11 09:42:41 +10:00
}
2021-11-28 01:47:36 +01:00
var send = true ;
2022-04-08 16:22:05 +02:00
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 ;
}
2021-11-28 01:47:36 +01:00
if ( instrument . LastSequencerTick > minTick )
2020-11-27 17:12:45 +01:00
{
2021-11-28 01:47:36 +01:00
instrument . LaggedBatches + + ;
if ( instrument . RespectMidiLimits )
{
if ( instrument . LaggedBatches = = ( int ) ( MaxMidiLaggedBatches * ( 1 / 3d ) + 1 ) )
{
2023-07-16 21:12:53 +02:00
_popup . PopupEntity ( Loc . GetString ( "instrument-component-finger-cramps-light-message" ) ,
uid , attached , PopupType . SmallCaution ) ;
}
else if ( instrument . LaggedBatches = = ( int ) ( MaxMidiLaggedBatches * ( 2 / 3d ) + 1 ) )
2021-11-28 01:47:36 +01:00
{
2023-07-16 21:12:53 +02:00
_popup . PopupEntity ( Loc . GetString ( "instrument-component-finger-cramps-serious-message" ) ,
uid , attached , PopupType . MediumCaution ) ;
2021-11-28 01:47:36 +01:00
}
}
if ( instrument . LaggedBatches > MaxMidiLaggedBatches )
{
send = false ;
}
2020-11-27 17:12:45 +01:00
}
2021-11-28 01:47:36 +01:00
if ( + + instrument . MidiEventCount > MaxMidiEventsPerSecond
| | msg . MidiEvent . Length > MaxMidiEventsPerBatch )
2020-11-27 17:12:45 +01:00
{
2021-11-28 01:47:36 +01:00
instrument . BatchesDropped + + ;
send = false ;
2020-11-27 17:12:45 +01:00
}
2023-07-16 21:12:53 +02:00
instrument . LastSequencerTick = Math . Max ( maxTick , minTick ) ;
2021-11-28 01:47:36 +01:00
if ( send | | ! instrument . RespectMidiLimits )
2021-11-23 18:19:08 +00:00
{
2021-11-28 01:47:36 +01:00
RaiseNetworkEvent ( msg ) ;
2021-11-23 18:19:08 +00:00
}
2021-11-28 01:47:36 +01:00
}
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
2020-05-18 13:29:31 +02:00
2023-07-16 21:12:53 +02:00
if ( _bandRequestQueue . Count > 0 & & _bandRequestTimer < _timing . RealTime )
{
_bandRequestTimer = _timing . RealTime . Add ( TimeSpan . FromSeconds ( BandRequestDelay ) ) ;
foreach ( var request in _bandRequestQueue )
{
2023-09-11 09:42:41 +10:00
var entity = GetEntity ( request . Entity ) ;
var nearby = GetBands ( entity ) ;
2024-04-26 18:16:24 +10:00
_bui . ServerSendUiMessage ( entity , request . UiKey , new InstrumentBandResponseBuiMessage ( nearby ) , request . Actor ) ;
2023-07-16 21:12:53 +02:00
}
_bandRequestQueue . Clear ( ) ;
}
2025-06-26 19:50:49 -04:00
var activeQuery = GetEntityQuery < ActiveInstrumentComponent > ( ) ;
var transformQuery = GetEntityQuery < TransformComponent > ( ) ;
2023-07-16 21:12:53 +02:00
var query = AllEntityQuery < ActiveInstrumentComponent , InstrumentComponent > ( ) ;
while ( query . MoveNext ( out var uid , out _ , out var instrument ) )
2021-11-28 01:47:36 +01:00
{
2023-07-16 21:12:53 +02:00
if ( instrument . Master is { } master )
2021-12-25 02:02:14 +01:00
{
2025-04-16 11:08:22 +00:00
if ( Deleted ( master ) )
2023-07-16 21:12:53 +02:00
{
Clean ( uid , instrument ) ;
}
var masterActive = activeQuery . CompOrNull ( master ) ;
if ( masterActive = = null )
{
Clean ( uid , instrument ) ;
}
var trans = transformQuery . GetComponent ( uid ) ;
var masterTrans = transformQuery . GetComponent ( master ) ;
2024-07-13 14:25:51 -07:00
if ( ! _transform . InRange ( masterTrans . Coordinates , trans . Coordinates , 10f )
)
2023-07-16 21:12:53 +02:00
{
Clean ( uid , instrument ) ;
}
2021-12-25 02:02:14 +01:00
}
2022-04-04 23:08:36 -07:00
if ( instrument . RespectMidiLimits & &
2022-01-27 13:07:35 +01:00
( instrument . BatchesDropped > = MaxMidiBatchesDropped
| | instrument . LaggedBatches > = MaxMidiLaggedBatches ) )
2020-05-18 13:29:31 +02:00
{
2024-04-26 18:16:24 +10:00
if ( instrument . InstrumentPlayer is { Valid : true } mob )
2021-11-28 01:47:36 +01:00
{
2025-07-21 10:22:11 -07:00
_stuns . TryUpdateParalyzeDuration ( mob , TimeSpan . FromSeconds ( 1 ) ) ;
2021-11-28 01:47:36 +01:00
2023-07-16 21:12:53 +02:00
_popup . PopupEntity ( Loc . GetString ( "instrument-component-finger-cramps-max-message" ) ,
uid , mob , PopupType . LargeCaution ) ;
2021-11-28 01:47:36 +01:00
}
2022-01-27 13:07:35 +01:00
// Just in case
2023-07-16 21:12:53 +02:00
Clean ( uid ) ;
2024-04-26 18:16:24 +10:00
_bui . CloseUi ( uid , InstrumentUiKey . Key ) ;
2020-05-18 13:29:31 +02:00
}
2021-11-28 01:47:36 +01:00
instrument . Timer + = frameTime ;
if ( instrument . Timer < 1 )
2021-12-01 14:00:10 +00:00
continue ;
2021-11-28 01:47:36 +01:00
instrument . Timer = 0f ;
instrument . MidiEventCount = 0 ;
instrument . LaggedBatches = 0 ;
instrument . BatchesDropped = 0 ;
2020-05-18 13:29:31 +02:00
}
}
2022-03-07 14:41:50 +03:00
2024-04-26 18:16:24 +10:00
public void ToggleInstrumentUi ( EntityUid uid , EntityUid actor , InstrumentComponent ? component = null )
2022-03-07 14:41:50 +03:00
{
if ( ! Resolve ( uid , ref component ) )
return ;
2024-04-26 18:16:24 +10:00
_bui . TryToggleUi ( uid , InstrumentUiKey . Key , actor ) ;
2022-03-07 14:41:50 +03:00
}
2023-09-12 14:43:06 +10:00
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 ;
}
2020-05-18 13:29:31 +02:00
}