using Content.Server.Actions; using Content.Server.Chat.Systems; using Content.Server.Speech.Components; using Content.Shared.Chat.Prototypes; using Content.Shared.Humanoid; using Content.Shared.Speech; using Content.Shared.Speech.Components; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.Speech.EntitySystems; public sealed class VocalSystem : EntitySystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly ActionsSystem _actions = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnSexChanged); SubscribeLocalEvent(OnEmote); SubscribeLocalEvent(OnScreamAction); } private void OnMapInit(EntityUid uid, VocalComponent component, MapInitEvent args) { // try to add scream action when vocal comp added _actions.AddAction(uid, ref component.ScreamActionEntity, component.ScreamAction); LoadSounds(uid, component); } private void OnShutdown(EntityUid uid, VocalComponent component, ComponentShutdown args) { // remove scream action when component removed if (component.ScreamActionEntity != null) { _actions.RemoveAction(uid, component.ScreamActionEntity); } } private void OnSexChanged(EntityUid uid, VocalComponent component, SexChangedEvent args) { LoadSounds(uid, component); } private void OnEmote(EntityUid uid, VocalComponent component, ref EmoteEvent args) { if (args.Handled || !args.Emote.Category.HasFlag(EmoteCategory.Vocal)) return; // snowflake case for wilhelm scream easter egg if (args.Emote.ID == component.ScreamId) { args.Handled = TryPlayScreamSound(uid, component); return; } // just play regular sound based on emote proto args.Handled = _chat.TryPlayEmoteSound(uid, component.EmoteSounds, args.Emote); } private void OnScreamAction(EntityUid uid, VocalComponent component, ScreamActionEvent args) { if (args.Handled) return; _chat.TryEmoteWithChat(uid, component.ScreamId); args.Handled = true; } private bool TryPlayScreamSound(EntityUid uid, VocalComponent component) { if (_random.Prob(component.WilhelmProbability)) { _audio.PlayPvs(component.Wilhelm, uid, component.Wilhelm.Params); return true; } return _chat.TryPlayEmoteSound(uid, component.EmoteSounds, component.ScreamId); } private void LoadSounds(EntityUid uid, VocalComponent component, Sex? sex = null) { if (component.Sounds == null) return; sex ??= CompOrNull(uid)?.Sex ?? Sex.Unsexed; if (!component.Sounds.TryGetValue(sex.Value, out var protoId)) return; _proto.TryIndex(protoId, out component.EmoteSounds); } }