# Conflicts: # .github/CODEOWNERS # .github/workflows/publish.yml # Content.Client/Administration/UI/BanPanel/BanPanel.xaml # Content.Client/Administration/UI/BanPanel/BanPanel.xaml.cs # Content.Client/Administration/UI/Tabs/PlayerTab/PlayerTabEntry.xaml.cs # Content.Client/Entry/EntryPoint.cs # Content.Client/Hands/Systems/HandsSystem.cs # Content.Client/Humanoid/HumanoidAppearanceSystem.cs # Content.Client/Humanoid/SingleMarkingPicker.xaml.cs # Content.Client/IoC/ClientContentIoC.cs # Content.Client/Lobby/LobbyState.cs # Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs # Content.Client/Lobby/UI/Loadouts/LoadoutGroupContainer.xaml.cs # Content.Client/Lobby/UI/LobbyGui.xaml # Content.Client/NetworkConfigurator/NetworkConfiguratorConfigurationMenu.xaml # Content.Client/Options/UI/EscapeMenu.xaml # Content.Client/Stealth/StealthSystem.cs # Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml.cs # Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml.cs # Content.Client/UserInterface/Systems/Emotes/EmotesUIController.cs # Content.Client/Weapons/Ranged/Systems/GunSystem.cs # Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs # Content.IntegrationTests/Tests/Damageable/DamageableTest.cs # Content.IntegrationTests/Tests/PostMapInitTest.cs # Content.Server/Administration/Commands/AdminWhoCommand.cs # Content.Server/Administration/Commands/PardonCommand.cs # Content.Server/Administration/Commands/RoleUnbanCommand.cs # Content.Server/Administration/Commands/SetOutfitCommand.cs # Content.Server/Administration/Systems/AdminVerbSystem.Antags.cs # Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs # Content.Server/AlertLevel/Commands/SetAlertLevelCommand.cs # Content.Server/Body/Components/BloodstreamComponent.cs # Content.Server/Body/Systems/BloodstreamSystem.cs # Content.Server/Body/Systems/MetabolizerSystem.cs # Content.Server/Body/Systems/RespiratorSystem.cs # Content.Server/Chat/Managers/ChatSanitizationManager.cs # Content.Server/Chat/Systems/ChatSystem.Emote.cs # Content.Server/Chat/Systems/ChatSystem.cs # Content.Server/CrewManifest/CrewManifestSystem.cs # Content.Server/Emp/EmpSystem.cs # Content.Server/Explosion/EntitySystems/TriggerSystem.cs # Content.Server/Fax/FaxSystem.cs # Content.Server/Fluids/EntitySystems/AbsorbentSystem.cs # Content.Server/GameTicking/Commands/ForceMapCommand.cs # Content.Server/GameTicking/Commands/ForcePresetCommand.cs # Content.Server/GameTicking/Presets/GamePresetPrototype.cs # Content.Server/GameTicking/Rules/ChangelingRuleSystem.cs # Content.Server/GameTicking/Rules/Components/ChangelingRuleComponent.cs # Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs # Content.Server/Ghost/Roles/GhostRoleSystem.cs # Content.Server/Implants/Components/ScramImplantComponent.cs # Content.Server/Implants/SubdermalImplantSystem.cs # Content.Server/Materials/ProduceMaterialExtractorSystem.cs # Content.Server/Medical/HealthAnalyzerSystem.cs # Content.Server/NPC/Systems/NPCCombatSystem.Ranged.cs # Content.Server/NPC/Systems/NPCSystem.cs # Content.Server/Polymorph/Systems/PolymorphSystem.cs # Content.Server/Radio/EntitySystems/HeadsetSystem.cs # Content.Server/Repairable/RepairableSystem.cs # Content.Server/Revenant/EntitySystems/RevenantSystem.cs # Content.Server/RoundEnd/RoundEndSystem.cs # Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs # Content.Server/Speech/EntitySystems/FrenchAccentSystem.cs # Content.Server/Speech/EntitySystems/FrontalLispSystem.cs # Content.Server/Speech/EntitySystems/LizardAccentSystem.cs # Content.Server/Speech/EntitySystems/MothAccentSystem.cs # Content.Server/Speech/EntitySystems/OwOAccentSystem.cs # Content.Server/Station/Systems/StationSpawningSystem.cs # Content.Server/StationEvents/Events/MeteorSwarmSystem.cs # Content.Server/Stunnable/Components/StunOnCollideComponent.cs # Content.Server/SurveillanceCamera/Components/SurveillanceCameraRouterComponent.cs # Content.Server/Weapons/Ranged/Systems/GunSystem.Cartridges.cs # Content.Server/Zombies/ZombieSystem.Transform.cs # Content.Server/Zombies/ZombieSystem.cs # Content.Shared/Actions/SharedActionsSystem.cs # Content.Shared/Bed/SharedBedSystem.cs # Content.Shared/Blocking/BlockingSystem.cs # Content.Shared/Body/Systems/SharedBodySystem.Parts.cs # Content.Shared/Chemistry/Components/SolutionRegenerationComponent.cs # Content.Shared/Chemistry/EntitySystems/HypospraySystem.cs # Content.Shared/Construction/Conditions/TileNotBlocked.cs # Content.Shared/Cuffs/SharedCuffableSystem.cs # Content.Shared/Doors/Systems/SharedDoorSystem.cs # Content.Shared/Emoting/EmoteAttemptEvent.cs # Content.Shared/Eye/Blinding/Systems/BlindableSystem.cs # Content.Shared/Eye/VisibilityFlags.cs # Content.Shared/Hands/EntitySystems/SharedHandsSystem.Pickup.cs # Content.Shared/Humanoid/HumanoidCharacterAppearance.cs # Content.Shared/Interaction/SmartEquipSystem.cs # Content.Shared/Inventory/InventorySystem.Relay.cs # Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs # Content.Shared/Medical/Healing/HealingSystem.cs # Content.Shared/Medical/HealingComponent.cs # Content.Shared/Nutrition/EntitySystems/FoodSystem.cs # Content.Shared/Slippery/SlipperySystem.cs # Content.Shared/SprayPainter/SharedSprayPainterSystem.cs # Content.Shared/Standing/StandingStateComponent.cs # Content.Shared/Standing/StandingStateSystem.cs # Content.Shared/Station/SharedStationSpawningSystem.cs # Content.Shared/Stunnable/SharedStunSystem.cs # Content.Shared/SurveillanceCamera/SharedSurveillanceCameraMonitorSystem.cs # Content.Shared/VendingMachines/VendingMachineComponent.cs # Content.Shared/Warps/WarpPointSystem.cs # Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs # Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs # Content.Shared/Wieldable/SharedWieldableSystem.cs # Resources/Audio/Ambience/Antag/attributions.yml # Resources/Audio/Ambience/Antag/changeling_start.ogg # Resources/Audio/Effects/attributions.yml # Resources/Audio/Lobby/attributions.yml # Resources/Audio/Machines/attributions.yml # Resources/Audio/Weapons/Guns/Gunshots/attributions.yml # Resources/Locale/en-US/_strings/administration/antag.ftl # Resources/Locale/en-US/_strings/administration/commands/camera.ftl # Resources/Locale/en-US/_strings/administration/commands/dsay-command.ftl # Resources/Locale/en-US/_strings/administration/commands/follow-command.ftl # Resources/Locale/en-US/_strings/administration/smites.ftl # Resources/Locale/en-US/_strings/administration/ui/admin-camera-window.ftl # Resources/Locale/en-US/_strings/administration/ui/role-bans.ftl # Resources/Locale/en-US/_strings/alerts/alerts.ftl # Resources/Locale/en-US/_strings/atmos/gas-pressure-regulator-system.ftl # Resources/Locale/en-US/_strings/bonk/components/bonkable-component.ftl # Resources/Locale/en-US/_strings/commands/add-body-part-command.ftl # Resources/Locale/en-US/_strings/commands/admin-logs-command.ftl # Resources/Locale/en-US/_strings/commands/admin-who-command.ftl # Resources/Locale/en-US/_strings/commands/asay-command.ftl # Resources/Locale/en-US/_strings/commands/crew-manifest-command.ftl # Resources/Locale/en-US/_strings/commands/deadmin-command.ftl # Resources/Locale/en-US/_strings/commands/delete-component-command.ftl # Resources/Locale/en-US/_strings/commands/destroy-mechanism-command.ftl # Resources/Locale/en-US/_strings/commands/dirty-command.ftl # Resources/Locale/en-US/_strings/commands/dsay-command.ftl # Resources/Locale/en-US/_strings/commands/dump-reagent-guide-text-command.ftl # Resources/Locale/en-US/_strings/commands/eye-commands.ftl # Resources/Locale/en-US/_strings/commands/follow-command.ftl # Resources/Locale/en-US/_strings/commands/force-map-command.ftl # Resources/Locale/en-US/_strings/commands/force-preset-command.ftl # Resources/Locale/en-US/_strings/commands/ghost-kick-command.ftl # Resources/Locale/en-US/_strings/commands/ghost-visibility-commands.ftl # Resources/Locale/en-US/_strings/commands/go-lobby-command.ftl # Resources/Locale/en-US/_strings/commands/grid-drag-command.ftl # Resources/Locale/en-US/_strings/commands/is-afk-command.ftl # Resources/Locale/en-US/_strings/commands/makesentient-command.ftl # Resources/Locale/en-US/_strings/commands/map-commands.ftl # Resources/Locale/en-US/_strings/commands/mapping-client-side-setup-command.ftl # Resources/Locale/en-US/_strings/commands/mappingclientsidesetup-command.ftl # Resources/Locale/en-US/_strings/commands/me-command.ftl # Resources/Locale/en-US/_strings/commands/mind-info-command.ftl # Resources/Locale/en-US/_strings/commands/node-vis-commands.ftl # Resources/Locale/en-US/_strings/commands/npc-command.ftl # Resources/Locale/en-US/_strings/commands/nukecodes-command.ftl # Resources/Locale/en-US/_strings/commands/ooc-command.ftl # Resources/Locale/en-US/_strings/commands/pardon-command.ftl # Resources/Locale/en-US/_strings/commands/powerstat-command.ftl # Resources/Locale/en-US/_strings/commands/promote-host-command.ftl # Resources/Locale/en-US/_strings/commands/radiation-command.ftl # Resources/Locale/en-US/_strings/commands/readmin-command.ftl # Resources/Locale/en-US/_strings/commands/remove-extra-components-command.ftl # Resources/Locale/en-US/_strings/commands/role-commands.ftl # Resources/Locale/en-US/_strings/commands/say-command.ftl # Resources/Locale/en-US/_strings/commands/set-battery-percent-command.ftl # Resources/Locale/en-US/_strings/commands/show-ambient-command.ftl # Resources/Locale/en-US/_strings/commands/show-gun-spread-command.ftl # Resources/Locale/en-US/_strings/commands/show-htn-command.ftl # Resources/Locale/en-US/_strings/commands/showrules-command.ftl # Resources/Locale/en-US/_strings/commands/spawn-entity-list-command.ftl # Resources/Locale/en-US/_strings/commands/start-singularity-command.ftl # Resources/Locale/en-US/_strings/commands/tile-pry-command.ftl # Resources/Locale/en-US/_strings/commands/toggle-decals-command.ftl # Resources/Locale/en-US/_strings/commands/toggle-disallow-late-join-command.ftl # Resources/Locale/en-US/_strings/commands/unlocknode-command.ftl # Resources/Locale/en-US/_strings/commands/whisper-command.ftl # Resources/Locale/en-US/_strings/components/gas-pressure-regulator-component.ftl # Resources/Locale/en-US/_strings/engineer-painter/engineer-painter.ftl # Resources/Locale/en-US/_strings/game-ticking/forcemap-command.ftl # Resources/Locale/en-US/_strings/interaction/interaction-popup-component.ftl # Resources/Locale/en-US/_strings/markings/vox_tattoos.ftl # Resources/Locale/en-US/_strings/materials/material-extractor.ftl # Resources/Locale/en-US/_strings/movement/eye.ftl # Resources/Locale/en-US/_strings/nutrition/components/food-component.ftl # Resources/Locale/en-US/_strings/nutrition/components/ingestion-system.ftl # Resources/Locale/en-US/_strings/nutrition/components/sealable-component.ftl # Resources/Locale/en-US/_strings/radiation/radiation-command.ftl # Resources/Locale/en-US/_strings/reagents/meta/physical-desc.ftl # Resources/Locale/en-US/_strings/research/technologies.ftl # Resources/Locale/en-US/_strings/seeds/seeds.ftl # Resources/Locale/en-US/_strings/weapons/grenades/timer-trigger.ftl # Resources/Locale/en-US/_strings/weapons/grenades/voice-trigger.ftl # Resources/Locale/en-US/commands/mapping-client-side-setup-command.ftl # Resources/Locale/en-US/recipes/tags.ftl # Resources/Maps/_Sunrise/Station/bagel.yml # Resources/Maps/_Sunrise/Station/barratry.yml # Resources/Maps/_Sunrise/Station/box.yml # Resources/Maps/_Sunrise/Station/fland.yml # Resources/Maps/_Sunrise/Station/marathon.yml # Resources/Maps/_Sunrise/Station/oasis.yml # Resources/Maps/_Sunrise/Station/packed.yml # Resources/Maps/_Sunrise/Station/plasma.yml # Resources/Maps/amber.yml # Resources/Maps/centcomm.yml # Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml # Resources/Prototypes/Catalog/Fills/Lockers/security.yml # Resources/Prototypes/Catalog/Fills/Lockers/service.yml # Resources/Prototypes/Catalog/Fills/Lockers/suit_storage.yml # Resources/Prototypes/Catalog/VendingMachines/Inventories/coffee.yml # Resources/Prototypes/Catalog/VendingMachines/Inventories/sustenance.yml # Resources/Prototypes/Catalog/VendingMachines/Inventories/winterdrobe.yml # Resources/Prototypes/Entities/Clothing/Belt/belts.yml # Resources/Prototypes/Entities/Clothing/Eyes/glasses.yml # Resources/Prototypes/Entities/Clothing/Eyes/hud.yml # Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml # Resources/Prototypes/Entities/Clothing/Head/hats.yml # Resources/Prototypes/Entities/Clothing/Head/helmets.yml # Resources/Prototypes/Entities/Clothing/Head/hoods.yml # Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml # Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml # Resources/Prototypes/Entities/Clothing/OuterClothing/softsuits.yml # Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml # Resources/Prototypes/Entities/Mobs/NPCs/animals.yml # Resources/Prototypes/Entities/Mobs/NPCs/carp.yml # Resources/Prototypes/Entities/Mobs/NPCs/pets.yml # Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml # Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml # Resources/Prototypes/Entities/Mobs/Player/clone.yml # Resources/Prototypes/Entities/Mobs/Species/base.yml # Resources/Prototypes/Entities/Mobs/Species/dwarf.yml # Resources/Prototypes/Entities/Mobs/base.yml # Resources/Prototypes/Entities/Objects/Fun/pai.yml # Resources/Prototypes/Entities/Objects/Fun/toys.yml # Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml # Resources/Prototypes/Entities/Objects/Shields/shields.yml # Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml # Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml # Resources/Prototypes/Entities/Objects/Tools/access_configurator.yml # Resources/Prototypes/Entities/Objects/Tools/matches.yml # Resources/Prototypes/Entities/Objects/Weapons/Bombs/plastic.yml # Resources/Prototypes/Entities/Objects/Weapons/Bombs/spider.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/caseless_rifle.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/light_rifle.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/magnum.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/grenade.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml # Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml # Resources/Prototypes/Entities/Objects/Weapons/Melee/mining.yml # Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml # Resources/Prototypes/Entities/Structures/Doors/Airlocks/airlocks.yml # Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml # Resources/Prototypes/Entities/Structures/Furniture/beds.yml # Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml # Resources/Prototypes/Entities/Structures/Power/debug_power.yml # Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml # Resources/Prototypes/Entities/Structures/Wallmounts/Misc/mirror.yml # Resources/Prototypes/GameRules/subgamemodes.yml # Resources/Prototypes/Reagents/Consumable/Drink/drinks.yml # Resources/Prototypes/Reagents/Consumable/Drink/soda.yml # Resources/Prototypes/Reagents/medicine.yml # Resources/Prototypes/Recipes/Lathes/Packs/tiles.yml # Resources/Prototypes/Recipes/Lathes/categories.yml # Resources/Prototypes/Recipes/Reactions/chemicals.yml # Resources/Prototypes/Roles/Antags/changeling.yml # Resources/Prototypes/name_identifier_groups.yml # Resources/Prototypes/tool_qualities.yml # Resources/ServerInfo/Guidebook/Antagonist/Zombies.xml # Resources/ServerInfo/Guidebook/Cargo/CargoBounties.xml # Resources/ServerInfo/Guidebook/Command.xml # Resources/ServerInfo/Guidebook/Glossary.xml # Resources/ServerInfo/Guidebook/NewPlayer/YourFirstCharacter.xml # Resources/ServerInfo/Guidebook/Science/Robotics.xml # Resources/ServerInfo/Guidebook/Science/ScannersAndVessels.xml # Resources/ServerInfo/Guidebook/Science/Science.xml # Resources/ServerInfo/Guidebook/SpaceStation14.xml # Resources/ServerInfo/Guidebook/Survival.xml # Resources/Textures/Clothing/Head/Hardsuits/atmospherics.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/capspace.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/cybersun.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/engineering-white.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/engineering-white.rsi/on-equipped-HELMET.png # Resources/Textures/Clothing/Head/Hardsuits/engineering.rsi/icon.png # Resources/Textures/Clothing/Head/Hardsuits/engineering.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/engineering.rsi/on-equipped-HELMET.png # Resources/Textures/Clothing/Head/Hardsuits/medical.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/rd.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/salvage.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/security.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/spatiohelm.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/syndicate.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/syndiecommander.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/syndieelite.rsi/meta.json # Resources/Textures/Clothing/Head/Hardsuits/wizard.rsi/meta.json # Resources/Textures/Clothing/Head/Helmets/atmos_firehelmet.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Armor/captain_carapace.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Armor/security.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/atmospherics.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/capspace.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/cybersun.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/engineering-white.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/engineering.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/medical.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/paramed.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/rd.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/salvage.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/security.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/spatio.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/syndicate.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/syndiecommander.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/syndieelite.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Hardsuits/wizard.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Suits/atmos_firesuit.rsi/meta.json # Resources/Textures/Clothing/OuterClothing/Suits/eva.rsi/meta.json # Resources/Textures/Interface/Misc/job_icons.rsi/meta.json # Resources/Textures/Objects/Misc/Lights/lampgreen.rsi/meta.json # Resources/Textures/Objects/Specific/Janitorial/janitorial_cart.rsi/meta.json # Resources/Textures/Objects/Tools/spray_painter.rsi/meta.json # Resources/Textures/Objects/Weapons/Melee/baseball_bat.rsi/meta.json # Resources/engineCommandPerms.yml # Resources/keybinds.yml # Resources/migration.yml
356 lines
13 KiB
C#
356 lines
13 KiB
C#
using System.Collections.Frozen;
|
||
using System.Text.RegularExpressions;
|
||
using Content.Shared._Sunrise.CollectiveMind;
|
||
using Content.Shared.Popups;
|
||
using Content.Shared.Radio;
|
||
using Content.Shared.Speech;
|
||
using Robust.Shared.Prototypes;
|
||
using Robust.Shared.Utility;
|
||
|
||
namespace Content.Shared.Chat;
|
||
|
||
public abstract class SharedChatSystem : EntitySystem
|
||
{
|
||
public const char RadioCommonPrefix = ';';
|
||
public const char RadioChannelPrefix = ':';
|
||
public const char RadioChannelAltPrefix = '.';
|
||
public const char LocalPrefix = '>';
|
||
public const char ConsolePrefix = '/';
|
||
public const char DeadPrefix = '\\';
|
||
public const char LOOCPrefix = '_'; // Russian-Localization
|
||
public const char OOCPrefix = '[';
|
||
public const char EmotesPrefix = '%'; // Russian-Localization
|
||
public const char EmotesAltPrefix = '*';
|
||
public const char AdminPrefix = ']';
|
||
public const char WhisperPrefix = ',';
|
||
public const char DefaultChannelKey = 'р'; // Russian-Localization
|
||
public const char CollectiveMindPrefix = '+'; // Sunrise-Edit
|
||
// Sunrise-TTS-Start
|
||
public const int VoiceRange = 10; // how far voice goes in world units
|
||
public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units
|
||
public const int WhisperMuffledRange = 5; // how far whisper goes at all, in world units
|
||
// Sunrise-TTS-End
|
||
|
||
public const int VoiceRange = 10; // how far voice goes in world units
|
||
public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units
|
||
public const int WhisperMuffledRange = 5; // how far whisper goes at all, in world units
|
||
public const string DefaultAnnouncementSound = "/Audio/Announcements/announce.ogg";
|
||
|
||
public static readonly ProtoId<RadioChannelPrototype> CommonChannel = "Common";
|
||
|
||
public static readonly string DefaultChannelPrefix = $"{RadioChannelPrefix}{DefaultChannelKey}";
|
||
public static readonly ProtoId<SpeechVerbPrototype> DefaultSpeechVerb = "Default";
|
||
|
||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||
|
||
/// <summary>
|
||
/// Cache of the keycodes for faster lookup.
|
||
/// </summary>
|
||
private FrozenDictionary<char, RadioChannelPrototype> _keyCodes = default!;
|
||
|
||
private FrozenDictionary<char, CollectiveMindPrototype> _mindKeyCodes = default!; // Sunrise-Edit
|
||
|
||
public override void Initialize()
|
||
{
|
||
base.Initialize();
|
||
DebugTools.Assert(_prototypeManager.HasIndex(CommonChannel));
|
||
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypeReload);
|
||
CacheRadios();
|
||
CacheCollectiveMinds(); // Sunrise-Edit
|
||
}
|
||
|
||
protected virtual void OnPrototypeReload(PrototypesReloadedEventArgs obj)
|
||
{
|
||
if (obj.WasModified<RadioChannelPrototype>())
|
||
CacheRadios();
|
||
// Sunrise-Start
|
||
if (obj.WasModified<CollectiveMindPrototype>())
|
||
CacheCollectiveMinds();
|
||
// Sunrise-End
|
||
}
|
||
|
||
private void CacheRadios()
|
||
{
|
||
_keyCodes = _prototypeManager.EnumeratePrototypes<RadioChannelPrototype>()
|
||
.ToFrozenDictionary(x => x.KeyCode);
|
||
}
|
||
|
||
// Sunrise-Start
|
||
private void CacheCollectiveMinds()
|
||
{
|
||
_prototypeManager.PrototypesReloaded -= OnPrototypeReload;
|
||
_mindKeyCodes = _prototypeManager.EnumeratePrototypes<CollectiveMindPrototype>()
|
||
.ToFrozenDictionary(x => x.KeyCode);
|
||
}
|
||
// Sunrise-End
|
||
|
||
/// <summary>
|
||
/// Attempts to find an applicable <see cref="SpeechVerbPrototype"/> for a speaking entity's message.
|
||
/// If one is not found, returns <see cref="DefaultSpeechVerb"/>.
|
||
/// </summary>
|
||
public SpeechVerbPrototype GetSpeechVerb(EntityUid source, string message, SpeechComponent? speech = null)
|
||
{
|
||
if (!Resolve(source, ref speech, false))
|
||
return _prototypeManager.Index(DefaultSpeechVerb);
|
||
|
||
// check for a suffix-applicable speech verb
|
||
SpeechVerbPrototype? current = null;
|
||
foreach (var (str, id) in speech.SuffixSpeechVerbs)
|
||
{
|
||
var proto = _prototypeManager.Index(id);
|
||
if (message.EndsWith(Loc.GetString(str)) && proto.Priority >= (current?.Priority ?? 0))
|
||
{
|
||
current = proto;
|
||
}
|
||
}
|
||
|
||
// if no applicable suffix verb return the normal one used by the entity
|
||
return current ?? _prototypeManager.Index(speech.SpeechVerb);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Splits the input message into a radio prefix part and the rest to preserve it during sanitization.
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// This is primarily for the chat emote sanitizer, which can match against ":b" as an emote, which is a valid radio keycode.
|
||
/// </remarks>
|
||
public void GetRadioKeycodePrefix(EntityUid source,
|
||
string input,
|
||
out string output,
|
||
out string prefix)
|
||
{
|
||
prefix = string.Empty;
|
||
output = input;
|
||
|
||
// If the string is less than 2, then it's probably supposed to be an emote.
|
||
// No one is sending empty radio messages!
|
||
if (input.Length <= 2)
|
||
return;
|
||
|
||
if (!(input.StartsWith(RadioChannelPrefix) || input.StartsWith(RadioChannelAltPrefix)))
|
||
return;
|
||
|
||
if (!_keyCodes.TryGetValue(char.ToLower(input[1]), out _))
|
||
return;
|
||
|
||
prefix = input[..2];
|
||
output = input[2..];
|
||
}
|
||
|
||
/// <summary>
|
||
/// Attempts to resolve radio prefixes in chat messages (e.g., remove a leading ":e" and resolve the requested
|
||
/// channel. Returns true if a radio message was attempted, even if the channel is invalid.
|
||
/// </summary>
|
||
/// <param name="source">Source of the message</param>
|
||
/// <param name="input">The message to be modified</param>
|
||
/// <param name="output">The modified message</param>
|
||
/// <param name="channel">The channel that was requested, if any</param>
|
||
/// <param name="quiet">Whether or not to generate an informative pop-up message.</param>
|
||
/// <returns></returns>
|
||
public bool TryProccessRadioMessage(
|
||
EntityUid source,
|
||
string input,
|
||
out string output,
|
||
out RadioChannelPrototype? channel,
|
||
bool quiet = false)
|
||
{
|
||
output = input.Trim();
|
||
channel = null;
|
||
|
||
if (input.Length == 0)
|
||
return false;
|
||
|
||
if (input.StartsWith(RadioCommonPrefix))
|
||
{
|
||
output = SanitizeMessageCapital(input[1..].TrimStart());
|
||
channel = _prototypeManager.Index<RadioChannelPrototype>(CommonChannel);
|
||
return true;
|
||
}
|
||
|
||
if (!(input.StartsWith(RadioChannelPrefix) || input.StartsWith(RadioChannelAltPrefix)))
|
||
return false;
|
||
|
||
if (input.Length < 2 || char.IsWhiteSpace(input[1]))
|
||
{
|
||
output = SanitizeMessageCapital(input[1..].TrimStart());
|
||
if (!quiet)
|
||
_popup.PopupEntity(Loc.GetString("chat-manager-no-radio-key"), source, source);
|
||
return true;
|
||
}
|
||
|
||
var channelKey = input[1];
|
||
channelKey = char.ToLower(channelKey);
|
||
output = SanitizeMessageCapital(input[2..].TrimStart());
|
||
|
||
if (channelKey == DefaultChannelKey)
|
||
{
|
||
var ev = new GetDefaultRadioChannelEvent();
|
||
RaiseLocalEvent(source, ev);
|
||
|
||
if (ev.Channel != null)
|
||
_prototypeManager.TryIndex(ev.Channel, out channel);
|
||
return true;
|
||
}
|
||
|
||
if (!_keyCodes.TryGetValue(channelKey, out channel) && !quiet)
|
||
{
|
||
var msg = Loc.GetString("chat-manager-no-such-channel", ("key", channelKey));
|
||
_popup.PopupEntity(msg, source, source);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
// Sunrise-Start
|
||
public bool TryProccessCollectiveMindMessage(
|
||
EntityUid source,
|
||
string input,
|
||
out string output,
|
||
out CollectiveMindPrototype? channel,
|
||
bool quiet = false)
|
||
{
|
||
output = input.Trim();
|
||
channel = null;
|
||
|
||
if (input.Length == 0)
|
||
return false;
|
||
|
||
if (!input.StartsWith(CollectiveMindPrefix))
|
||
return false;
|
||
|
||
if (input.Length < 2 || char.IsWhiteSpace(input[1]))
|
||
{
|
||
output = SanitizeMessageCapital(input[1..].TrimStart());
|
||
if (!quiet)
|
||
_popup.PopupEntity(Loc.GetString("chat-manager-no-radio-key"), source, source);
|
||
return true;
|
||
}
|
||
|
||
var channelKey = input[1];
|
||
channelKey = char.ToLower(channelKey);
|
||
output = SanitizeMessageCapital(input[2..].TrimStart());
|
||
|
||
if (_mindKeyCodes.TryGetValue(channelKey, out channel) || quiet)
|
||
return true;
|
||
|
||
var msg = Loc.GetString("chat-manager-no-such-channel", ("key", channelKey));
|
||
_popup.PopupEntity(msg, source, source);
|
||
|
||
return false;
|
||
}
|
||
// Sunrise-End
|
||
|
||
public string SanitizeMessageCapital(string message)
|
||
{
|
||
if (string.IsNullOrEmpty(message))
|
||
return message;
|
||
// Capitalize first letter
|
||
message = OopsConcat(char.ToUpper(message[0]).ToString(), message.Remove(0, 1));
|
||
return message;
|
||
}
|
||
|
||
private static string OopsConcat(string a, string b)
|
||
{
|
||
// This exists to prevent Roslyn being clever and compiling something that fails sandbox checks.
|
||
return a + b;
|
||
}
|
||
|
||
public string SanitizeMessageCapitalizeTheWordI(string message, string theWordI = "i")
|
||
{
|
||
if (string.IsNullOrEmpty(message))
|
||
return message;
|
||
|
||
for
|
||
(
|
||
var index = message.IndexOf(theWordI);
|
||
index != -1;
|
||
index = message.IndexOf(theWordI, index + 1)
|
||
)
|
||
{
|
||
// Stops the code If It's tryIng to capItalIze the letter I In the mIddle of words
|
||
// Repeating the code twice is the simplest option
|
||
if (index + 1 < message.Length && char.IsLetter(message[index + 1]))
|
||
continue;
|
||
if (index - 1 >= 0 && char.IsLetter(message[index - 1]))
|
||
continue;
|
||
|
||
var beforeTarget = message.Substring(0, index);
|
||
var target = message.Substring(index, theWordI.Length);
|
||
var afterTarget = message.Substring(index + theWordI.Length);
|
||
|
||
message = beforeTarget + target.ToUpper() + afterTarget;
|
||
}
|
||
|
||
return message;
|
||
}
|
||
|
||
public static string SanitizeAnnouncement(string message, int maxLength = 0, int maxNewlines = 2)
|
||
{
|
||
var trimmed = message.Trim();
|
||
if (maxLength > 0 && trimmed.Length > maxLength)
|
||
{
|
||
trimmed = $"{message[..maxLength]}...";
|
||
}
|
||
|
||
// No more than max newlines, other replaced to spaces
|
||
if (maxNewlines > 0)
|
||
{
|
||
var chars = trimmed.ToCharArray();
|
||
var newlines = 0;
|
||
for (var i = 0; i < chars.Length; i++)
|
||
{
|
||
if (chars[i] != '\n')
|
||
continue;
|
||
|
||
if (newlines >= maxNewlines)
|
||
chars[i] = ' ';
|
||
|
||
newlines++;
|
||
}
|
||
|
||
return new string(chars);
|
||
}
|
||
|
||
return trimmed;
|
||
}
|
||
|
||
public static string InjectTagInsideTag(ChatMessage message, string outerTag, string innerTag, string? tagParameter)
|
||
{
|
||
var rawmsg = message.WrappedMessage;
|
||
var tagStart = rawmsg.IndexOf($"[{outerTag}]");
|
||
var tagEnd = rawmsg.IndexOf($"[/{outerTag}]");
|
||
if (tagStart < 0 || tagEnd < 0) //If the outer tag is not found, the injection is not performed
|
||
return rawmsg;
|
||
tagStart += outerTag.Length + 2;
|
||
|
||
string innerTagProcessed = tagParameter != null ? $"[{innerTag}={tagParameter}]" : $"[{innerTag}]";
|
||
|
||
rawmsg = rawmsg.Insert(tagEnd, $"[/{innerTag}]");
|
||
rawmsg = rawmsg.Insert(tagStart, innerTagProcessed);
|
||
|
||
return rawmsg;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Injects a tag around all found instances of a specific string in a ChatMessage.
|
||
/// Excludes strings inside other tags and brackets.
|
||
/// </summary>
|
||
public static string InjectTagAroundString(ChatMessage message, string targetString, string tag, string? tagParameter)
|
||
{
|
||
var rawmsg = message.WrappedMessage;
|
||
rawmsg = Regex.Replace(rawmsg, "(?i)(" + targetString + ")(?-i)(?![^[]*])", $"[{tag}={tagParameter}]$1[/{tag}]");
|
||
return rawmsg;
|
||
}
|
||
|
||
public static string GetStringInsideTag(ChatMessage message, string tag)
|
||
{
|
||
var rawmsg = message.WrappedMessage;
|
||
var tagStart = rawmsg.IndexOf($"[{tag}]");
|
||
var tagEnd = rawmsg.IndexOf($"[/{tag}]");
|
||
if (tagStart < 0 || tagEnd < 0)
|
||
return "";
|
||
tagStart += tag.Length + 2;
|
||
return rawmsg.Substring(tagStart, tagEnd - tagStart);
|
||
}
|
||
}
|