using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Audio.Jukebox; using Content.Shared.Power; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Components; using Robust.Shared.Audio.Systems; using Robust.Shared.Player; using Robust.Shared.Prototypes; using JukeboxComponent = Content.Shared.Audio.Jukebox.JukeboxComponent; namespace Content.Server.Audio.Jukebox; public sealed class JukeboxSystem : SharedJukeboxSystem { [Dependency] private readonly IPrototypeManager _protoManager = default!; [Dependency] private readonly AppearanceSystem _appearanceSystem = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnJukeboxSelected); SubscribeLocalEvent(OnJukeboxPlay); SubscribeLocalEvent(OnJukeboxPause); SubscribeLocalEvent(OnJukeboxStop); SubscribeLocalEvent(OnJukeboxSetTime); SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnComponentShutdown); SubscribeLocalEvent(OnPowerChanged); } private void OnComponentInit(EntityUid uid, JukeboxComponent component, ComponentInit args) { if (HasComp(uid)) { TryUpdateVisualState(uid, component); } } private void OnJukeboxPlay(EntityUid uid, JukeboxComponent component, ref JukeboxPlayingMessage args) { if (Exists(component.AudioStream)) { Audio.SetState(component.AudioStream, AudioState.Playing); } else { component.AudioStream = Audio.Stop(component.AudioStream); if (string.IsNullOrEmpty(component.SelectedSongId) || !_protoManager.Resolve(component.SelectedSongId, out var jukeboxProto)) { return; } component.AudioStream = Audio.PlayPvs(jukeboxProto.Path, uid, AudioParams.Default.WithMaxDistance(10f))?.Entity; Dirty(uid, component); } } private void OnJukeboxPause(Entity ent, ref JukeboxPauseMessage args) { Audio.SetState(ent.Comp.AudioStream, AudioState.Paused); } private void OnJukeboxSetTime(EntityUid uid, JukeboxComponent component, JukeboxSetTimeMessage args) { if (TryComp(args.Actor, out ActorComponent? actorComp)) { var offset = actorComp.PlayerSession.Channel.Ping * 1.5f / 1000f; Audio.SetPlaybackPosition(component.AudioStream, args.SongTime + offset); } } private void OnPowerChanged(Entity entity, ref PowerChangedEvent args) { TryUpdateVisualState(entity); if (!this.IsPowered(entity.Owner, EntityManager)) { Stop(entity); } } private void OnJukeboxStop(Entity entity, ref JukeboxStopMessage args) { Stop(entity); } private void Stop(Entity entity) { Audio.SetState(entity.Comp.AudioStream, AudioState.Stopped); Dirty(entity); } private void OnJukeboxSelected(EntityUid uid, JukeboxComponent component, JukeboxSelectedMessage args) { if (!Audio.IsPlaying(component.AudioStream)) { component.SelectedSongId = args.SongId; DirectSetVisualState(uid, JukeboxVisualState.Select); component.Selecting = true; component.AudioStream = Audio.Stop(component.AudioStream); } Dirty(uid, component); } public override void Update(float frameTime) { base.Update(frameTime); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp)) { if (comp.Selecting) { comp.SelectAccumulator += frameTime; if (comp.SelectAccumulator >= 0.5f) { comp.SelectAccumulator = 0f; comp.Selecting = false; TryUpdateVisualState(uid, comp); } } } } private void OnComponentShutdown(EntityUid uid, JukeboxComponent component, ComponentShutdown args) { component.AudioStream = Audio.Stop(component.AudioStream); } private void DirectSetVisualState(EntityUid uid, JukeboxVisualState state) { _appearanceSystem.SetData(uid, JukeboxVisuals.VisualState, state); } private void TryUpdateVisualState(EntityUid uid, JukeboxComponent? jukeboxComponent = null) { if (!Resolve(uid, ref jukeboxComponent)) return; var finalState = JukeboxVisualState.On; if (!this.IsPowered(uid, EntityManager)) { finalState = JukeboxVisualState.Off; } _appearanceSystem.SetData(uid, JukeboxVisuals.VisualState, finalState); } }