Instrument band support, submodule update to 138.0.0 (#17995)
This commit is contained in:
committed by
GitHub
parent
69ff0ae2e6
commit
a2893dd6c3
@@ -1,20 +1,44 @@
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Instruments;
|
||||
using Content.Shared.Instruments.UI;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Popups;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Audio.Midi;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
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 _stunSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = 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 EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly InteractionSystem _interactions = 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()
|
||||
{
|
||||
@@ -25,30 +49,62 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
SubscribeNetworkEvent<InstrumentMidiEventEvent>(OnMidiEventRx);
|
||||
SubscribeNetworkEvent<InstrumentStartMidiEvent>(OnMidiStart);
|
||||
SubscribeNetworkEvent<InstrumentStopMidiEvent>(OnMidiStop);
|
||||
SubscribeNetworkEvent<InstrumentSetMasterEvent>(OnMidiSetMaster);
|
||||
SubscribeNetworkEvent<InstrumentSetFilteredChannelEvent>(OnMidiSetFilteredChannel);
|
||||
|
||||
SubscribeLocalEvent<InstrumentComponent, BoundUIClosedEvent>(OnBoundUIClosed);
|
||||
SubscribeLocalEvent<InstrumentComponent, BoundUIOpenedEvent>(OnBoundUIOpened);
|
||||
SubscribeLocalEvent<InstrumentComponent, InstrumentBandRequestBuiMessage>(OnBoundUIRequestBands);
|
||||
|
||||
_conHost.RegisterCommand("addtoband", AddToBandCommand);
|
||||
}
|
||||
|
||||
[AdminCommand(AdminFlags.Fun)]
|
||||
private void AddToBandCommand(IConsoleShell shell, string _, string[] args)
|
||||
{
|
||||
if (!EntityUid.TryParse(args[0], out var firstUid))
|
||||
{
|
||||
shell.WriteError($"Cannot parse first Uid");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(args[1], 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);
|
||||
otherInstrument.Playing = true;
|
||||
otherInstrument.Master = firstUid;
|
||||
Dirty(secondUid, otherInstrument);
|
||||
}
|
||||
|
||||
private void OnMidiStart(InstrumentStartMidiEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var uid = msg.Uid;
|
||||
|
||||
if (!EntityManager.TryGetComponent(uid, out InstrumentComponent? instrument))
|
||||
if (!TryComp(uid, out InstrumentComponent? instrument))
|
||||
return;
|
||||
|
||||
if (args.SenderSession != instrument.InstrumentPlayer)
|
||||
return;
|
||||
|
||||
instrument.Playing = true;
|
||||
instrument.Dirty();
|
||||
Dirty(uid, instrument);
|
||||
}
|
||||
|
||||
private void OnMidiStop(InstrumentStopMidiEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var uid = msg.Uid;
|
||||
|
||||
if (!EntityManager.TryGetComponent(uid, out InstrumentComponent? instrument))
|
||||
if (!TryComp(uid, out InstrumentComponent? instrument))
|
||||
return;
|
||||
|
||||
if (args.SenderSession != instrument.InstrumentPlayer)
|
||||
@@ -57,6 +113,66 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
Clean(uid, instrument);
|
||||
}
|
||||
|
||||
private void OnMidiSetMaster(InstrumentSetMasterEvent msg, EntitySessionEventArgs args)
|
||||
{
|
||||
var uid = msg.Uid;
|
||||
var master = msg.Master;
|
||||
|
||||
if (!HasComp<ActiveInstrumentComponent>(uid))
|
||||
return;
|
||||
|
||||
if (!TryComp(uid, out InstrumentComponent? instrument))
|
||||
return;
|
||||
|
||||
if (args.SenderSession != 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 = msg.Uid;
|
||||
|
||||
if (!TryComp(uid, out InstrumentComponent? instrument))
|
||||
return;
|
||||
|
||||
if (args.SenderSession != 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(uid, new []{RobustMidiEvent.AllNotesOff((byte)msg.Channel, 0)}));
|
||||
}
|
||||
|
||||
Dirty(uid, instrument);
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
@@ -70,7 +186,7 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
return;
|
||||
|
||||
if (HasComp<ActiveInstrumentComponent>(uid)
|
||||
&& _userInterfaceSystem.TryGetUi(uid, args.UiKey, out var bui)
|
||||
&& _bui.TryGetUi(uid, args.UiKey, out var bui)
|
||||
&& bui.SubscribedSessions.Count == 0)
|
||||
{
|
||||
RemComp<ActiveInstrumentComponent>(uid);
|
||||
@@ -88,6 +204,63 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
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 (EntityUid, string)[] GetBands(EntityUid uid)
|
||||
{
|
||||
var metadataQuery = EntityManager.GetEntityQuery<MetaDataComponent>();
|
||||
|
||||
if (Deleted(uid, metadataQuery))
|
||||
return Array.Empty<(EntityUid, string)>();
|
||||
|
||||
var list = new ValueList<(EntityUid, string)>();
|
||||
var instrumentQuery = EntityManager.GetEntityQuery<InstrumentComponent>();
|
||||
|
||||
if (!TryComp(uid, out InstrumentComponent? originInstrument)
|
||||
|| originInstrument.InstrumentPlayer?.AttachedEntity is not {} originPlayer)
|
||||
return Array.Empty<(EntityUid, 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?.AttachedEntity 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, uses the Opaque collision group so this works across windows etc.
|
||||
if (!_interactions.InRangeUnobstructed(uid, entity, MaxInstrumentBandRange,
|
||||
CollisionGroup.Opaque, e => e == playerUid || e == originPlayer))
|
||||
continue;
|
||||
|
||||
if (!metadataQuery.TryGetComponent(playerUid, out var playerMetadata)
|
||||
|| !metadataQuery.TryGetComponent(entity, out var metadata))
|
||||
continue;
|
||||
|
||||
list.Add((entity, $"{playerMetadata.EntityName} - {metadata.EntityName}"));
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
|
||||
public void Clean(EntityUid uid, InstrumentComponent? instrument = null)
|
||||
{
|
||||
if (!Resolve(uid, ref instrument))
|
||||
@@ -95,14 +268,19 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
|
||||
if (instrument.Playing)
|
||||
{
|
||||
// Reset puppet instruments too.
|
||||
RaiseNetworkEvent(new InstrumentMidiEventEvent(uid, new[]{RobustMidiEvent.SystemReset(0)}));
|
||||
|
||||
RaiseNetworkEvent(new InstrumentStopMidiEvent(uid));
|
||||
}
|
||||
|
||||
instrument.Playing = false;
|
||||
instrument.Master = null;
|
||||
instrument.FilteredChannels.SetAll(false);
|
||||
instrument.LastSequencerTick = 0;
|
||||
instrument.BatchesDropped = 0;
|
||||
instrument.LaggedBatches = 0;
|
||||
instrument.Dirty();
|
||||
Dirty(uid, instrument);
|
||||
}
|
||||
|
||||
private void OnMidiEventRx(InstrumentMidiEventEvent msg, EntitySessionEventArgs args)
|
||||
@@ -142,12 +320,13 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
{
|
||||
if (instrument.LaggedBatches == (int) (MaxMidiLaggedBatches * (1 / 3d) + 1))
|
||||
{
|
||||
attached.PopupMessage(
|
||||
Loc.GetString("instrument-component-finger-cramps-light-message"));
|
||||
} else if (instrument.LaggedBatches == (int) (MaxMidiLaggedBatches * (2 / 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))
|
||||
{
|
||||
attached.PopupMessage(
|
||||
Loc.GetString("instrument-component-finger-cramps-serious-message"));
|
||||
_popup.PopupEntity(Loc.GetString("instrument-component-finger-cramps-serious-message"),
|
||||
uid, attached, PopupType.MediumCaution);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,24 +344,58 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
send = false;
|
||||
}
|
||||
|
||||
instrument.LastSequencerTick = Math.Max(maxTick, minTick);
|
||||
|
||||
if (send || !instrument.RespectMidiLimits)
|
||||
{
|
||||
RaiseNetworkEvent(msg);
|
||||
}
|
||||
|
||||
instrument.LastSequencerTick = Math.Max(maxTick, minTick);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
foreach (var (_, instrument) in EntityManager.EntityQuery<ActiveInstrumentComponent, InstrumentComponent>(true))
|
||||
if (_bandRequestQueue.Count > 0 && _bandRequestTimer < _timing.RealTime)
|
||||
{
|
||||
if (instrument.DirtyRenderer)
|
||||
_bandRequestTimer = _timing.RealTime.Add(TimeSpan.FromSeconds(BandRequestDelay));
|
||||
|
||||
foreach (var request in _bandRequestQueue)
|
||||
{
|
||||
Dirty(instrument);
|
||||
instrument.DirtyRenderer = false;
|
||||
var nearby = GetBands(request.Entity);
|
||||
_bui.TrySendUiMessage(request.Entity, request.UiKey, new InstrumentBandResponseBuiMessage(nearby),
|
||||
(IPlayerSession)request.Session);
|
||||
}
|
||||
|
||||
_bandRequestQueue.Clear();
|
||||
}
|
||||
|
||||
var activeQuery = EntityManager.GetEntityQuery<ActiveInstrumentComponent>();
|
||||
var metadataQuery = EntityManager.GetEntityQuery<MetaDataComponent>();
|
||||
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, metadataQuery))
|
||||
{
|
||||
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 (!masterTrans.Coordinates.InRange(EntityManager, _transform, trans.Coordinates, 10f))
|
||||
{
|
||||
Clean(uid, instrument);
|
||||
}
|
||||
}
|
||||
|
||||
if (instrument.RespectMidiLimits &&
|
||||
@@ -191,16 +404,17 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
{
|
||||
if (instrument.InstrumentPlayer?.AttachedEntity is {Valid: true} mob)
|
||||
{
|
||||
_stunSystem.TryParalyze(mob, TimeSpan.FromSeconds(1), true);
|
||||
_stuns.TryParalyze(mob, TimeSpan.FromSeconds(1), true);
|
||||
|
||||
instrument.Owner.PopupMessage(mob, Loc.GetString("instrument-component-finger-cramps-max-message"));
|
||||
_popup.PopupEntity(Loc.GetString("instrument-component-finger-cramps-max-message"),
|
||||
uid, mob, PopupType.LargeCaution);
|
||||
}
|
||||
|
||||
// Just in case
|
||||
Clean((instrument).Owner);
|
||||
Clean(uid);
|
||||
|
||||
if (instrument.UserInterface is not null)
|
||||
_userInterfaceSystem.CloseAll(instrument.UserInterface);
|
||||
_bui.CloseAll(instrument.UserInterface);
|
||||
}
|
||||
|
||||
instrument.Timer += frameTime;
|
||||
@@ -219,7 +433,7 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (_userInterfaceSystem.TryGetUi(uid, InstrumentUiKey.Key, out var bui))
|
||||
_userInterfaceSystem.ToggleUi(bui, session);
|
||||
if (_bui.TryGetUi(uid, InstrumentUiKey.Key, out var bui))
|
||||
_bui.ToggleUi(bui, session);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user