using Content.Shared._WL.Languages; using Content.Shared._WL.Languages.Components; using Content.Shared.IdentityManagement; using Content.Shared.Popups; using Content.Shared.Timing; using Content.Shared.Radio; using Content.Shared.Speech; using Content.Shared.Trigger.Systems; using Content.Server.Atmos.EntitySystems; using Robust.Shared.Network; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; using Robust.Shared.Timing; namespace Content.Server._WL.Languages; public sealed class LanguagesSystem : SharedLanguagesSystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IEntityManager _ent = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; /// /// Потому что . /// private static readonly Color DefaultChatTextColor = Color.LightGray; private static readonly string DefaultChatTextFontId = "Default"; private static readonly int DefaultChatTextFontSize = 12; private static readonly float FullTalkPressure = 50f; private static readonly float MinTalkPressure = 5f; private static readonly float ForceWhisperProb = .3f; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnComponentInit); SubscribeLocalEvent(OnPressureLanguageCheck); SubscribeLocalEvent(OnModifyInit); SubscribeNetworkEvent(OnGlobalLanguageChange); SubscribeNetworkEvent(OnLanguagesSync); } public void AddLanguage(EntityUid ent, string language) { if (!TryComp(ent, out var comp)) return; comp.Speaking.Add(language); comp.Understood.Add(language); Dirty(ent, comp); var net_ent = GetNetEntity(ent); SyncLanguages(net_ent, comp); } public void OnModifyInit(EntityUid ent, ModifyLanguagesComponent component, ref ComponentInit args) { var langs = component.Languages; if (!TryComp(ent, out var out_comp)) { RemComp(ent); return; } if (!component.SpecieLanguage) { foreach (ProtoId protoid in langs) { var proto = GetLanguagePrototype(protoid); if (proto != null) { if (component.ToSpeaking) out_comp.Speaking.Add(protoid); if (component.ToUnderstood) out_comp.Understood.Add(protoid); } } } else { var protoid = out_comp.SpecieLanguage; var proto = GetLanguagePrototype(protoid); if (proto != null && protoid != null) { if (component.ToSpeaking) out_comp.Speaking.Remove(protoid.Value); if (component.ToUnderstood) out_comp.Understood.Remove(protoid.Value); } } RemComp(ent); Dirty(ent, out_comp); var net_ent = GetNetEntity(ent); SyncLanguages(net_ent, out_comp); } public void OnComponentInit(EntityUid ent, LanguagesComponent component, ref ComponentInit args) { var langs = component.Speaking; if (langs.Count == 0) return; if (component.SpecieLanguage != null) AddLanguage(ent, component.SpecieLanguage); foreach (ProtoId protoid in langs) { var proto = GetLanguagePrototype(protoid); if (proto != null) { if (TryChangeLanguage(_ent.GetNetEntity(ent), protoid)) return; } } } public void OnLanguagesSync(LanguagesSyncEvent msg, EntitySessionEventArgs args) { var entity = _ent.GetEntity(msg.Entity); if (!TryComp(entity, out var component)) return; component.Speaking = msg.Speaking; component.Understood = msg.Understood; Dirty(entity, component); } public void OnGlobalLanguageChange(LanguageChangeEvent msg, EntitySessionEventArgs args) { var entity = _ent.GetEntity(msg.Entity); if (!TryComp(entity, out var component)) return; OnLanguageChange(entity, (string)msg.Language); } public void OnPressureLanguageCheck(EntityUid source, LanguagesComponent comp, ref PressureLanguageCheckEvent args) { var passability = CheckPressurePass(source, args.Message); if (passability == 0) { args.Cancelled = true; var time = _timing.CurTime; if (true || time > comp.LastPopup + comp.PopupCooldown) { comp.LastPopup = time; var message = Loc.GetString("languages-vacuum-block"); _popup.PopupEntity(message, source, source); } } else if (passability < 1) { args.Message = ObfuscateMessageReadability(args.Message, passability); if (passability < ForceWhisperProb) args.ForceWhisper = true; var time = _timing.CurTime; if (true || time > comp.LastPopup + comp.PopupCooldown) { comp.LastPopup = time; var message = Loc.GetString("languages-vacuum-part-pass"); _popup.PopupEntity(message, source); } } } public string ObfuscateMessageFromSource(string message, EntityUid source) { LanguagePrototype? proto = null; var innerMsg = message; if (TryProcessLanguageMessage(source, message, out var new_message)) { proto = GetLanguagePrototype(source, message); innerMsg = new_message; } else if (TryComp(source, out var comp)) proto = GetLanguagePrototype(comp.CurrentLanguage); if (proto == null) return innerMsg; return ObfuscateMessage(source, innerMsg, proto.ID); } public bool CanUnderstand(EntityUid source, EntityUid listener, string? message = null, ProtoId? overrideLang = null) { if (source == listener) return true; if (!TryComp(source, out var source_lang)) { return true; } if (!TryComp(listener, out var listen_lang)) { return true; } var message_language = GetLanguagePrototype(source, message) ?? GetLanguagePrototype(overrideLang) ?? source_lang.CurrentLanguage; return listen_lang.IsUnderstanding && source_lang.IsSpeaking && message_language != null && listen_lang.Understood.Contains(message_language.Value); } public bool NeedTTS(EntityUid source) { if (!TryComp(source, out var source_lang)) return true; else { var message_language = source_lang.CurrentLanguage; var proto = GetLanguagePrototype(message_language); if (proto == null) return true; else { return proto.NeedTTS; } } } public bool IsObfusEmoting(EntityUid source) { if (!TryComp(source, out var source_lang)) return false; else { var message_language = source_lang.CurrentLanguage; var proto = GetLanguagePrototype(message_language); if (proto == null) return false; else { return proto.Emoting; } } } public bool IsObfusEmoting(EntityUid source, string message) { var proto = GetLanguagePrototype(source, message); if (proto != null) return proto.Emoting; return IsObfusEmoting(source); } /* Функция не используется нигде в коде, но может быть полезна. Закоментированно. public string GetObfusWrappedMessage(string message, EntityUid source, string name, SpeechVerbPrototype? speech = null) { var obfusMessage = ObfuscateMessageFromSource(message, source); var wrappedMessage = GetWrappedMessage(obfusMessage, source, name, speech); return wrappedMessage; } */ public string GetRadioWrappedMessageFor( string msg, EntityUid source, EntityUid listener, string name, SpeechVerbPrototype speech, RadioChannelPrototype channel) { var isSelf = listener == source; var canUnderstand = CanUnderstand(source, listener, msg); var language = GetLanguagePrototype(source, msg); var color = GetColor(language, channel.Color); var (fontSize, fontId) = GetFontParams(language, speech.FontSize, speech.FontId); string message; if (isSelf || canUnderstand) { if (TryProcessLanguageMessage(source, msg, out var parsed)) message = parsed; else message = msg; } else message = ObfuscateMessageFromSource(msg, source); var locId = speech.Bold ? "chat-radio-message-wrap-bold-lang" : "chat-radio-message-wrap-lang"; if (!isSelf && !canUnderstand && IsObfusEmoting(source, msg)) locId = "chat-radio-message-wrap-emote-lang"; var wrappedMessage = Loc.GetString(locId, ("color", channel.Color), ("fontType", fontId), ("fontSize", fontSize), ("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))), ("channel", $"\\[{channel.LocalizedName}\\]"), ("name", name), ("message", message), ("langColor", color)); return SanitizeWrappedMessage(source, wrappedMessage); } public (int, string) GetFontParams(LanguagePrototype? language, int? fallbackSize = null, string? fallbackId = null) { int size; string id; if (language == null || language.FontSize == DefaultChatTextFontSize) size = fallbackSize ?? DefaultChatTextFontSize; else size = language.FontSize; if (language == null || language.FontId == DefaultChatTextFontId) id = fallbackId ?? DefaultChatTextFontId; else id = language.FontId; return (size, id); } public Color GetColor(LanguagePrototype? language, Color? fallback = null) { if (language == null || language.Color == DefaultChatTextColor) return fallback ?? DefaultChatTextColor; return language.Color; } public string GetWhisperWrappedMessage(string message, EntityUid source, string name) { if (string.IsNullOrEmpty(message)) return string.Empty; TryProcessLanguageMessage(source, message, out string new_message); var language = GetLanguagePrototype(source, message); var color = GetColor(language); var escapedMessage = FormattedMessage.EscapeText(new_message); var wrappedMessage = Loc.GetString("chat-manager-entity-whisper-wrap-message-lang", ("entityName", name), ("message", escapedMessage), ("langColor", color)); return SanitizeWrappedMessage(source, wrappedMessage); } public string GetEmoteWrappedMessage(string message, EntityUid source, string name) { var ent = Identity.Entity(source, EntityManager); var wrappedMessage = Loc.GetString("chat-manager-entity-me-wrap-message", ("entityName", name), ("entity", ent), ("message", FormattedMessage.RemoveMarkupOrThrow(message)) ); return SanitizeWrappedMessage(source, wrappedMessage); } public string GetWrappedMessage(string message, EntityUid source, string name, SpeechVerbPrototype speech) { if (string.IsNullOrEmpty(message)) return string.Empty; TryProcessLanguageMessage(source, message, out string new_message); var language = GetLanguagePrototype(source, message); var color = GetColor(language); var (fontSize, fontId) = GetFontParams(language, speech.FontSize, speech.FontId); var locId = speech.Bold ? "chat-manager-entity-say-bold-wrap-message-lang" : "chat-manager-entity-say-wrap-message-lang"; Logger.Debug(fontId); var wrappedMessage = Loc.GetString(locId, ("entityName", name), ("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))), ("fontType", fontId), //Оно не работает, даже если захардкодить. Не понимаю почему. (tau) ("fontSize", fontSize), ("message", FormattedMessage.EscapeText(new_message)), ("langColor", color)); return SanitizeWrappedMessage(source, wrappedMessage); } private float CheckPressurePass(EntityUid source, string msg) { var language = GetLanguagePrototype(source, msg); if (language == null) return 1f; if (_atmosphereSystem.GetContainingMixture(source) is { } mixture) { var fixed_pressure = MathF.Max(mixture.Pressure - MinTalkPressure, 0f); var pressure_prob = MathF.Min(fixed_pressure/(FullTalkPressure - MinTalkPressure), 1f); var full_prob = MathF.Min(pressure_prob + language.PressurePass, 1f); return full_prob; } else return 1f; } }