From f3a878439d3321c42acd32b498e22484b74614e8 Mon Sep 17 00:00:00 2001 From: XingYeFish <154997195+XingYeNotFish@users.noreply.github.com> Date: Thu, 14 Aug 2025 16:12:28 +0800 Subject: [PATCH 1/2] refactor: CustomScpTermination --- EXILED/Exiled.API/Features/Cassie.cs | 44 +++++++++++++++++----------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/EXILED/Exiled.API/Features/Cassie.cs b/EXILED/Exiled.API/Features/Cassie.cs index 0730f9a5a9..88707e4365 100644 --- a/EXILED/Exiled.API/Features/Cassie.cs +++ b/EXILED/Exiled.API/Features/Cassie.cs @@ -21,8 +21,7 @@ namespace Exiled.API.Features using Respawning; - using CustomFirearmHandler = DamageHandlers.FirearmDamageHandler; - using CustomHandlerBase = DamageHandlers.DamageHandlerBase; + using CustomHandler = DamageHandlers.CustomDamageHandler; /// /// A set of tools to use in-game C.A.S.S.I.E. @@ -140,25 +139,36 @@ public static void ScpTermination(Player scp, DamageHandlerBase info) => NineTailedFoxAnnouncer.AnnounceScpTermination(scp.ReferenceHub, info); /// - /// Announces the termination of a custom SCP name. + /// Announces the termination of a custom SCP Number. /// - /// SCP Name. Note that for larger numbers, C.A.S.S.I.E will pronounce the place (eg. "457" -> "four hundred fifty seven"). Spaces can be used to prevent this behavior. + /// SCP Number. Note that for larger numbers, C.A.S.S.I.E will pronounce the place (eg. "457" -> "four hundred fifty seven"). Spaces can be used to prevent this behavior. /// Hit Information. - public static void CustomScpTermination(string scpName, CustomHandlerBase info) + public static void CustomScpTermination(string scpNumber, CustomHandler info) { - string result = scpName; - if (info.Is(out MicroHidDamageHandler _)) - result += " SUCCESSFULLY TERMINATED BY AUTOMATIC SECURITY SYSTEM"; - else if (info.Is(out WarheadDamageHandler _)) - result += " SUCCESSFULLY TERMINATED BY ALPHA WARHEAD"; - else if (info.Is(out UniversalDamageHandler _)) - result += " LOST IN DECONTAMINATION SEQUENCE"; - else if (info.BaseIs(out CustomFirearmHandler firearmDamageHandler) && firearmDamageHandler.Attacker is Player attacker) - result += " CONTAINEDSUCCESSFULLY " + ConvertTeam(attacker.Role.Team, attacker.UnitName); - - // result += "To be changed"; + if (info is null) + throw new System.ArgumentNullException(nameof(info)); + + string result = "SCP " + scpNumber; + if (info.Attacker is null) + { + result += info.Type switch + { + Enums.DamageType.Warhead => " SUCCESSFULLY TERMINATED BY ALPHA WARHEAD", + Enums.DamageType.Decontamination => " LOST IN DECONTAMINATION SEQUENCE", + Enums.DamageType.Tesla => " SUCCESSFULLY TERMINATED BY AUTOMATIC SECURITY SYSTEM", + _ => " SUCCESSFULLY TERMINATED . TERMINATION CAUSE UNSPECIFIED", + }; + } else - result += " SUCCESSFULLY TERMINATED . TERMINATION CAUSE UNSPECIFIED"; + { + result += info.Attacker.Role.Team switch + { + Team.SCPs => " TERMINATED BY SCP " + string.Join(" ", info.Attacker.Role.Name.Remove(0, 4).ToCharArray()), + Team.Flamingos => " TERMINATED BY SCP 1 5 0 7", + Team.OtherAlive or Team.Dead => " SUCCESSFULLY TERMINATED . TERMINATION CAUSE UNSPECIFIED", + _ => " CONTAINEDSUCCESSFULLY " + ConvertTeam(info.Attacker.Role.Team, info.Attacker.UnitName), + }; + } float num = AlphaWarheadController.TimeUntilDetonation <= 0f ? 3.5f : 1f; GlitchyMessage(result, UnityEngine.Random.Range(0.1f, 0.14f) * num, UnityEngine.Random.Range(0.07f, 0.08f) * num); From f9860f6f27a49d5a3969a23e62917867dcee2d9b Mon Sep 17 00:00:00 2001 From: XingYeFish <154997195+XingYeNotFish@users.noreply.github.com> Date: Sat, 20 Dec 2025 07:06:12 +0800 Subject: [PATCH 2/2] for new cassie update --- EXILED/Exiled.API/Features/Cassie.cs | 116 +++++++++++++++++++-------- 1 file changed, 82 insertions(+), 34 deletions(-) diff --git a/EXILED/Exiled.API/Features/Cassie.cs b/EXILED/Exiled.API/Features/Cassie.cs index 88707e4365..21c54516c4 100644 --- a/EXILED/Exiled.API/Features/Cassie.cs +++ b/EXILED/Exiled.API/Features/Cassie.cs @@ -7,19 +7,19 @@ namespace Exiled.API.Features { + using System; using System.Collections.Generic; using System.Linq; using System.Text; using Exiled.API.Features.Pools; - + using global::Cassie; + using global::Cassie.Interpreters; using MEC; - using PlayerRoles; - using PlayerStatsSystem; - using Respawning; + using Respawning.NamingRules; using CustomHandler = DamageHandlers.CustomDamageHandler; @@ -28,20 +28,15 @@ namespace Exiled.API.Features /// public static class Cassie { - /// - /// Gets the singleton. - /// - public static NineTailedFoxAnnouncer Announcer => NineTailedFoxAnnouncer.singleton; - /// /// Gets a value indicating whether C.A.S.S.I.E is currently announcing. Does not include decontamination or Alpha Warhead Messages. /// - public static bool IsSpeaking => Announcer.queue.Count != 0; + public static bool IsSpeaking => CassieAnnouncementDispatcher.AllAnnouncements.Count != 0; /// - /// Gets a of objects that C.A.S.S.I.E recognizes. + /// Gets a of objects that C.A.S.S.I.E recognizes. /// - public static IReadOnlyCollection VoiceLines => Announcer.voiceLines; + public static IReadOnlyCollection VoiceLines => CassieAnnouncementDispatcher.AllAnnouncements; /// /// Reproduce a non-glitched C.A.S.S.I.E message. @@ -51,7 +46,7 @@ public static class Cassie /// Indicates whether C.A.S.S.I.E has to make noises during the message. /// Indicates whether C.A.S.S.I.E has to make subtitles. public static void Message(string message, bool isHeld = false, bool isNoisy = true, bool isSubtitles = false) => - RespawnEffectsController.PlayCassieAnnouncement(message, isHeld, isNoisy, isSubtitles); + new CassieAnnouncement(new CassieTtsPayload(message, isSubtitles, isHeld), 0f, isNoisy ? 1 : 0).AddToQueue(); /// /// Reproduce a non-glitched C.A.S.S.I.E message with a possibility to custom the subtitles. @@ -63,14 +58,7 @@ public static void Message(string message, bool isHeld = false, bool isNoisy = t /// Indicates whether C.A.S.S.I.E has to make subtitles. public static void MessageTranslated(string message, string translation, bool isHeld = false, bool isNoisy = true, bool isSubtitles = true) { - StringBuilder announcement = StringBuilderPool.Pool.Get(); - string[] cassies = message.Split('\n'); - string[] translations = translation.Split('\n'); - for (int i = 0; i < cassies.Length; i++) - announcement.Append($"{translations[i].Replace(' ', ' ')} {cassies[i]} "); - - RespawnEffectsController.PlayCassieAnnouncement(announcement.ToString(), isHeld, isNoisy, isSubtitles); - StringBuilderPool.Pool.Return(announcement); + new CassieAnnouncement(new CassieTtsPayload(message, translation, isHeld), 0f, isNoisy ? 1 : 0).AddToQueue(); } /// @@ -80,7 +68,7 @@ public static void MessageTranslated(string message, string translation, bool is /// The chance of placing a glitch between each word. /// The chance of jamming each word. public static void GlitchyMessage(string message, float glitchChance, float jamChance) => - Announcer.ServerOnlyAddGlitchyPhrase(message, glitchChance, jamChance); + new CassieAnnouncement(new CassieTtsPayload(CassieGlitchifier.Glitchify(message, glitchChance, jamChance), true, true), 0f, 0f).AddToQueue(); /// /// Reproduce a non-glitched C.A.S.S.I.E message after a certain amount of seconds. @@ -91,7 +79,7 @@ public static void GlitchyMessage(string message, float glitchChance, float jamC /// Indicates whether C.A.S.S.I.E has to make noises during the message. /// Indicates whether C.A.S.S.I.E has to make subtitles. public static void DelayedMessage(string message, float delay, bool isHeld = false, bool isNoisy = true, bool isSubtitles = false) => - Timing.CallDelayed(delay, () => RespawnEffectsController.PlayCassieAnnouncement(message, isHeld, isNoisy, isSubtitles)); + Timing.CallDelayed(delay, () => new CassieAnnouncement(new CassieTtsPayload(message, isSubtitles, isHeld), 0f, isNoisy ? 1 : 0).AddToQueue()); /// /// Reproduce a glitchy C.A.S.S.I.E announcement after a certain period of seconds. @@ -101,17 +89,53 @@ public static void DelayedMessage(string message, float delay, bool isHeld = fal /// The chance of placing a glitch between each word. /// The chance of jamming each word. public static void DelayedGlitchyMessage(string message, float delay, float glitchChance, float jamChance) => - Timing.CallDelayed(delay, () => Announcer.ServerOnlyAddGlitchyPhrase(message, glitchChance, jamChance)); + Timing.CallDelayed(delay, () => new CassieAnnouncement(new CassieTtsPayload(CassieGlitchifier.Glitchify(message, glitchChance, jamChance), true, true), 0f, 0f).AddToQueue()); /// /// Calculates the duration of a C.A.S.S.I.E message. /// /// The message, which duration will be calculated. - /// Determines if a number won't be converted to its full pronunciation. - /// The speed of the message. + /// An obsolete parameter. + /// Another obsolete parameter. /// Duration (in seconds) of specified message. - public static float CalculateDuration(string message, bool rawNumber = false, float speed = 1f) - => Announcer.CalculateDuration(message, rawNumber, speed); + [Obsolete("Please use CalculateDuration(string)", true)] + public static float CalculateDuration(string message, bool obsolete1, float obsolete2) => CalculateDuration(message); + + /// + /// Calculates the duration of a C.A.S.S.I.E message. + /// + /// The message, which duration will be calculated. + /// Duration (in seconds) of specified message. + public static float CalculateDuration(string message) + { + if (!CassieTtsAnnouncer.TryGetDatabase(out CassieLineDatabase cassieLineDatabase)) + { + return 0; + } + + float value = 0; + string[] lines = message.Split(' ', StringSplitOptions.RemoveEmptyEntries); + + CassiePlaybackModifiers modifiers = new(); + StringBuilder builder = StringBuilderPool.Pool.Get(); + + for (int i = 0; i < lines.Length; i++) + { + foreach (CassieInterpreter interpreter in CassieTtsAnnouncer.Interpreters) + { + bool halt; + foreach (CassieInterpreter.Result result in interpreter.GetResults(cassieLineDatabase, ref modifiers, lines[i], builder, out halt)) + { + value += (float)result.Modifiers.GetTimeUntilNextWord(result.Line); + } + + if (halt) + break; + } + } + + return value; + } /// /// Converts a into a Cassie-Readable CONTAINMENTUNIT. @@ -119,8 +143,16 @@ public static float CalculateDuration(string message, bool rawNumber = false, fl /// . /// Unit Name. /// Containment Unit text. - public static string ConvertTeam(Team team, string unitName) - => NineTailedFoxAnnouncer.ConvertTeam(team, unitName); + public static string ConvertTeam(Team team, string unitName) => team switch + { + Team.FoundationForces when NamingRulesManager.TryGetNamingRule(team, out UnitNamingRule unitNamingRule) => "CONTAINMENTUNIT " + unitNamingRule.TranslateToCassie(unitName), + Team.FoundationForces => "CONTAINMENTUNIT UNKNOWN", + Team.ChaosInsurgency => "BY CHAOSINSURGENCY", + Team.Scientists => "BY SCIENCE PERSONNEL", + Team.ClassD => "BY CLASSD PERSONNEL", + Team.Flamingos => "BY FLAMINGOS", + _ => "UNKNOWN", + }; /// /// Converts a number into a Cassie-Readable String. @@ -128,7 +160,23 @@ public static string ConvertTeam(Team team, string unitName) /// Number to convert. /// A CASSIE-readable representing the number. public static string ConvertNumber(int num) - => NineTailedFoxAnnouncer.ConvertNumber(num); + { + if (!CassieTtsAnnouncer.TryGetDatabase(out CassieLineDatabase cassieLineDatabase)) + { + return string.Empty; + } + + NumberInterpreter numberInterpreter = (NumberInterpreter)CassieTtsAnnouncer.Interpreters.FirstOrDefault((CassieInterpreter x) => x is NumberInterpreter); + if (numberInterpreter == null) + { + return string.Empty; + } + + CassiePlaybackModifiers cassiePlaybackModifiers = default; + StringBuilder stringBuilder = new(); + numberInterpreter.GetResults(cassieLineDatabase, ref cassiePlaybackModifiers, num.ToString(), stringBuilder, out bool flag); + return stringBuilder.ToString(); + } /// /// Announce a SCP Termination. @@ -136,7 +184,7 @@ public static string ConvertNumber(int num) /// SCP to announce termination of. /// HitInformation. public static void ScpTermination(Player scp, DamageHandlerBase info) - => NineTailedFoxAnnouncer.AnnounceScpTermination(scp.ReferenceHub, info); + => CassieScpTerminationAnnouncement.AnnounceScpTermination(scp.ReferenceHub, info); /// /// Announces the termination of a custom SCP Number. @@ -177,14 +225,14 @@ public static void CustomScpTermination(string scpNumber, CustomHandler info) /// /// Clears the C.A.S.S.I.E queue. /// - public static void Clear() => RespawnEffectsController.ClearQueue(); + public static void Clear() => CassieAnnouncementDispatcher.ClearAll(); /// /// Gets a value indicating whether the given word is a valid C.A.S.S.I.E word. /// /// The word to check. /// if the word is valid; otherwise, . - public static bool IsValid(string word) => Announcer.voiceLines.Any(line => line.apiName.ToUpper() == word.ToUpper()); + public static bool IsValid(string word) => CassieTtsAnnouncer.TryGetDatabase(out CassieLineDatabase cassieLineDatabase) ? cassieLineDatabase.AllLines.Any(line => line.ApiName.ToUpper() == word.ToUpper()) : false; /// /// Gets a value indicating whether the given sentence is all valid C.A.S.S.I.E word.