]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add a system for modifying entity names without causing conflicts (#27863)
authorTayrtahn <tayrtahn@gmail.com>
Sun, 16 Jun 2024 19:38:53 +0000 (15:38 -0400)
committerGitHub <noreply@github.com>
Sun, 16 Jun 2024 19:38:53 +0000 (15:38 -0400)
30 files changed:
Content.Server/Chemistry/Components/TransformableContainerComponent.cs
Content.Server/Chemistry/EntitySystems/TransformableContainerSystem.cs
Content.Server/Cluwne/CluwneSystem.cs
Content.Server/Fax/FaxSystem.cs
Content.Server/Glue/GlueSystem.cs
Content.Server/Labels/Label/LabelSystem.cs
Content.Server/Lube/LubedSystem.cs
Content.Server/Nutrition/EntitySystems/AnimalHusbandrySystem.cs
Content.Server/Zombies/ZombieSystem.Transform.cs
Content.Server/Zombies/ZombieSystem.cs
Content.Shared/Glue/GluedComponent.cs
Content.Shared/Inventory/InventorySystem.Relay.cs
Content.Shared/Labels/Components/LabelComponent.cs
Content.Shared/Labels/EntitySystems/SharedLabelSystem.cs
Content.Shared/Lube/LubedComponent.cs
Content.Shared/NameModifier/Components/ModifyWearerNameComponent.cs [new file with mode: 0644]
Content.Shared/NameModifier/Components/NameModifierComponent.cs [new file with mode: 0644]
Content.Shared/NameModifier/EntitySystems/ModifyWearerNameSystem.cs [new file with mode: 0644]
Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs [new file with mode: 0644]
Content.Shared/Nutrition/AnimalHusbandry/InfantComponent.cs
Content.Shared/Zombies/SharedZombieSystem.cs
Content.Shared/Zombies/ZombieComponent.cs
Resources/Locale/en-US/chemistry/components/transformable-container-component.ftl
Resources/Locale/en-US/cluwne/cluwne.ftl
Resources/Locale/en-US/glue/glue.ftl
Resources/Locale/en-US/label/label-component.ftl [new file with mode: 0644]
Resources/Locale/en-US/lube/lube.ftl
Resources/Locale/en-US/nutrition/components/animal-husbandry.ftl
Resources/Locale/en-US/zombies/zombie.ftl
Resources/Prototypes/Entities/Objects/Specific/chemical-containers.yml

index 5ea9a244878ff2903d9ff81e3ff40b380e0fea6b..db6c9c5397659de901c7cb42439895d21bd143b7 100644 (file)
@@ -14,14 +14,6 @@ namespace Content.Server.Chemistry.Components;
 [RegisterComponent, Access(typeof(TransformableContainerSystem))]
 public sealed partial class TransformableContainerComponent : Component
 {
-    /// <summary>
-    /// This is the initial metadata name for the container.
-    /// It will revert to this when emptied.
-    /// It defaults to the name of the parent entity unless overwritten.
-    /// </summary>
-    [DataField("initialName")]
-    public string? InitialName;
-
     /// <summary>
     /// This is the initial metadata description for the container.
     /// It will revert to this when emptied.
index c375d97b8c3f7ed65d9ef34bf9ae8fba0e8a56ec..32bd912b2205af7e54dc500da39ab23248ae7f1b 100644 (file)
@@ -2,6 +2,7 @@ using Content.Server.Chemistry.Components;
 using Content.Server.Chemistry.Containers.EntitySystems;
 using Content.Shared.Chemistry.EntitySystems;
 using Content.Shared.Chemistry.Reagent;
+using Content.Shared.NameModifier.EntitySystems;
 using Robust.Shared.Prototypes;
 
 namespace Content.Server.Chemistry.EntitySystems;
@@ -11,6 +12,7 @@ public sealed class TransformableContainerSystem : EntitySystem
     [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
     [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!;
     [Dependency] private readonly MetaDataSystem _metadataSystem = default!;
+    [Dependency] private readonly NameModifierSystem _nameMod = default!;
 
     public override void Initialize()
     {
@@ -18,15 +20,12 @@ public sealed class TransformableContainerSystem : EntitySystem
 
         SubscribeLocalEvent<TransformableContainerComponent, MapInitEvent>(OnMapInit);
         SubscribeLocalEvent<TransformableContainerComponent, SolutionContainerChangedEvent>(OnSolutionChange);
+        SubscribeLocalEvent<TransformableContainerComponent, RefreshNameModifiersEvent>(OnRefreshNameModifiers);
     }
 
-    private void OnMapInit(Entity<TransformableContainerComponent> entity, ref MapInitEvent args) 
+    private void OnMapInit(Entity<TransformableContainerComponent> entity, ref MapInitEvent args)
     {
         var meta = MetaData(entity.Owner);
-        if (string.IsNullOrEmpty(entity.Comp.InitialName))
-        {
-            entity.Comp.InitialName = meta.EntityName;
-        }
         if (string.IsNullOrEmpty(entity.Comp.InitialDescription))
         {
             entity.Comp.InitialDescription = meta.EntityDescription;
@@ -58,12 +57,20 @@ public sealed class TransformableContainerSystem : EntitySystem
             && _prototypeManager.TryIndex(reagentId.Value.Prototype, out ReagentPrototype? proto))
         {
             var metadata = MetaData(entity.Owner);
-            var val = Loc.GetString("transformable-container-component-glass", ("name", proto.LocalizedName));
-            _metadataSystem.SetEntityName(entity.Owner, val, metadata);
             _metadataSystem.SetEntityDescription(entity.Owner, proto.LocalizedDescription, metadata);
             entity.Comp.CurrentReagent = proto;
             entity.Comp.Transformed = true;
         }
+
+        _nameMod.RefreshNameModifiers(entity.Owner);
+    }
+
+    private void OnRefreshNameModifiers(Entity<TransformableContainerComponent> entity, ref RefreshNameModifiersEvent args)
+    {
+        if (entity.Comp.CurrentReagent is { } currentReagent)
+        {
+            args.AddModifier("transformable-container-component-glass", priority: -1, ("reagent", currentReagent.LocalizedName));
+        }
     }
 
     private void CancelTransformation(Entity<TransformableContainerComponent> entity)
@@ -73,10 +80,8 @@ public sealed class TransformableContainerSystem : EntitySystem
 
         var metadata = MetaData(entity);
 
-        if (!string.IsNullOrEmpty(entity.Comp.InitialName))
-        {
-            _metadataSystem.SetEntityName(entity.Owner, entity.Comp.InitialName, metadata);
-        }
+        _nameMod.RefreshNameModifiers(entity.Owner);
+
         if (!string.IsNullOrEmpty(entity.Comp.InitialDescription))
         {
             _metadataSystem.SetEntityDescription(entity.Owner, entity.Comp.InitialDescription, metadata);
index c170886a803a0425e7bd2e801f41f25fe8688381..18d82659debe29edcf9ec03d20279bacf440d19d 100644 (file)
@@ -14,8 +14,8 @@ using Content.Server.Emoting.Systems;
 using Content.Server.Speech.EntitySystems;
 using Content.Shared.Cluwne;
 using Content.Shared.Interaction.Components;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
+using Content.Shared.NameModifier.EntitySystems;
 
 namespace Content.Server.Cluwne;
 
@@ -30,6 +30,7 @@ public sealed class CluwneSystem : EntitySystem
     [Dependency] private readonly ChatSystem _chat = default!;
     [Dependency] private readonly AutoEmoteSystem _autoEmote = default!;
     [Dependency] private readonly MetaDataSystem _metaData = default!;
+    [Dependency] private readonly NameModifierSystem _nameMod = default!;
 
     public override void Initialize()
     {
@@ -39,6 +40,7 @@ public sealed class CluwneSystem : EntitySystem
         SubscribeLocalEvent<CluwneComponent, MobStateChangedEvent>(OnMobState);
         SubscribeLocalEvent<CluwneComponent, EmoteEvent>(OnEmote, before:
         new[] { typeof(VocalSystem), typeof(BodyEmotesSystem) });
+        SubscribeLocalEvent<CluwneComponent, RefreshNameModifiersEvent>(OnRefreshNameModifiers);
     }
 
     /// <summary>
@@ -47,19 +49,19 @@ public sealed class CluwneSystem : EntitySystem
     private void OnMobState(EntityUid uid, CluwneComponent component, MobStateChangedEvent args)
     {
         if (args.NewMobState == MobState.Dead)
-               {
+        {
             RemComp<CluwneComponent>(uid);
             RemComp<ClumsyComponent>(uid);
             RemComp<AutoEmoteComponent>(uid);
             var damageSpec = new DamageSpecifier(_prototypeManager.Index<DamageGroupPrototype>("Genetic"), 300);
             _damageableSystem.TryChangeDamage(uid, damageSpec);
-               }
+        }
     }
 
     public EmoteSoundsPrototype? EmoteSounds;
 
     /// <summary>
-    /// OnStartup gives the cluwne outfit, ensures clumsy, gives name prefix and makes sure emote sounds are laugh.
+    /// OnStartup gives the cluwne outfit, ensures clumsy, and makes sure emote sounds are laugh.
     /// </summary>
     private void OnComponentStartup(EntityUid uid, CluwneComponent component, ComponentStartup args)
     {
@@ -67,9 +69,6 @@ public sealed class CluwneSystem : EntitySystem
             return;
         _prototypeManager.TryIndex(component.EmoteSoundsId, out EmoteSounds);
 
-        var meta = MetaData(uid);
-        var name = meta.EntityName;
-
         EnsureComp<AutoEmoteComponent>(uid);
         _autoEmote.AddEmote(uid, "CluwneGiggle");
         EnsureComp<ClumsyComponent>(uid);
@@ -77,7 +76,7 @@ public sealed class CluwneSystem : EntitySystem
         _popupSystem.PopupEntity(Loc.GetString("cluwne-transform", ("target", uid)), uid, PopupType.LargeCaution);
         _audio.PlayPvs(component.SpawnSound, uid);
 
-        _metaData.SetEntityName(uid, Loc.GetString("cluwne-name-prefix", ("target", name)), meta);
+        _nameMod.RefreshNameModifiers(uid);
 
         SetOutfitCommand.SetOutfit(uid, "CluwneGear", EntityManager);
     }
@@ -104,4 +103,12 @@ public sealed class CluwneSystem : EntitySystem
             _chat.TrySendInGameICMessage(uid, "spasms", InGameICChatType.Emote, ChatTransmitRange.Normal);
         }
     }
+
+    /// <summary>
+    /// Applies "Cluwnified" prefix
+    /// </summary>
+    private void OnRefreshNameModifiers(Entity<CluwneComponent> entity, ref RefreshNameModifiersEvent args)
+    {
+        args.AddModifier("cluwne-name-prefix");
+    }
 }
index 16d2d391f65a430daabfd7b7671c91b2cfdacdb6..82acb3c60c36ea6bb32abab56788fc8928b6f56e 100644 (file)
@@ -29,6 +29,7 @@ using Robust.Shared.Audio.Systems;
 using Robust.Shared.Containers;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
+using Content.Shared.NameModifier.Components;
 
 namespace Content.Server.Fax;
 
@@ -464,10 +465,11 @@ public sealed class FaxSystem : EntitySystem
             return;
 
         TryComp<LabelComponent>(sendEntity, out var labelComponent);
+        TryComp<NameModifierComponent>(sendEntity, out var nameMod);
 
         // TODO: See comment in 'Send()' about not being able to copy whole entities
         var printout = new FaxPrintout(paper.Content,
-                                       labelComponent?.OriginalName ?? metadata.EntityName,
+                                       nameMod?.BaseName ?? metadata.EntityName,
                                        labelComponent?.CurrentLabel,
                                        metadata.EntityPrototype?.ID ?? DefaultPaperPrototypeId,
                                        paper.StampState,
@@ -510,12 +512,14 @@ public sealed class FaxSystem : EntitySystem
            !TryComp<PaperComponent>(sendEntity, out var paper))
             return;
 
+        TryComp<NameModifierComponent>(sendEntity, out var nameMod);
+
         TryComp<LabelComponent>(sendEntity, out var labelComponent);
 
         var payload = new NetworkPayload()
         {
             { DeviceNetworkConstants.Command, FaxConstants.FaxPrintCommand },
-            { FaxConstants.FaxPaperNameData, labelComponent?.OriginalName ?? metadata.EntityName },
+            { FaxConstants.FaxPaperNameData, nameMod?.BaseName ?? metadata.EntityName },
             { FaxConstants.FaxPaperLabelData, labelComponent?.CurrentLabel },
             { FaxConstants.FaxPaperContentData, paper.Content },
         };
index ff53ef91cac6cc845d35b91b7581ae3e8a7e7e29..79249f5bd965f28c59e370952f6f7b25d74e1950 100644 (file)
@@ -6,6 +6,7 @@ using Content.Shared.Hands;
 using Content.Shared.Interaction;
 using Content.Shared.Interaction.Components;
 using Content.Shared.Item;
+using Content.Shared.NameModifier.EntitySystems;
 using Content.Shared.Nutrition.EntitySystems;
 using Content.Shared.Popups;
 using Content.Shared.Verbs;
@@ -20,9 +21,9 @@ public sealed class GlueSystem : SharedGlueSystem
     [Dependency] private readonly SharedPopupSystem _popup = default!;
     [Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
     [Dependency] private readonly IGameTiming _timing = default!;
-    [Dependency] private readonly MetaDataSystem _metaData = default!;
     [Dependency] private readonly IAdminLogManager _adminLogger = default!;
     [Dependency] private readonly OpenableSystem _openable = default!;
+    [Dependency] private readonly NameModifierSystem _nameMod = default!;
 
     public override void Initialize()
     {
@@ -32,6 +33,7 @@ public sealed class GlueSystem : SharedGlueSystem
         SubscribeLocalEvent<GluedComponent, ComponentInit>(OnGluedInit);
         SubscribeLocalEvent<GlueComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb);
         SubscribeLocalEvent<GluedComponent, GotEquippedHandEvent>(OnHandPickUp);
+        SubscribeLocalEvent<GluedComponent, RefreshNameModifiersEvent>(OnRefreshNameModifiers);
     }
 
     // When glue bottle is used on item it will apply the glued and unremoveable components.
@@ -95,27 +97,22 @@ public sealed class GlueSystem : SharedGlueSystem
     {
         base.Update(frameTime);
 
-        var query = EntityQueryEnumerator<GluedComponent, UnremoveableComponent, MetaDataComponent>();
-        while (query.MoveNext(out var uid, out var glue, out var _, out var meta))
+        var query = EntityQueryEnumerator<GluedComponent, UnremoveableComponent>();
+        while (query.MoveNext(out var uid, out var glue, out var _))
         {
             if (_timing.CurTime < glue.Until)
                 continue;
 
-            // Instead of string matching, just reconstruct the expected name and compare
-            if (meta.EntityName == Loc.GetString("glued-name-prefix", ("target", glue.BeforeGluedEntityName)))
-                _metaData.SetEntityName(uid, glue.BeforeGluedEntityName);
-
             RemComp<UnremoveableComponent>(uid);
             RemComp<GluedComponent>(uid);
+
+            _nameMod.RefreshNameModifiers(uid);
         }
     }
 
     private void OnGluedInit(Entity<GluedComponent> entity, ref ComponentInit args)
     {
-        var meta = MetaData(entity);
-        var name = meta.EntityName;
-        entity.Comp.BeforeGluedEntityName = meta.EntityName;
-        _metaData.SetEntityName(entity.Owner, Loc.GetString("glued-name-prefix", ("target", name)));
+        _nameMod.RefreshNameModifiers(entity.Owner);
     }
 
     private void OnHandPickUp(Entity<GluedComponent> entity, ref GotEquippedHandEvent args)
@@ -124,4 +121,9 @@ public sealed class GlueSystem : SharedGlueSystem
         comp.DeleteOnDrop = false;
         entity.Comp.Until = _timing.CurTime + entity.Comp.Duration;
     }
+
+    private void OnRefreshNameModifiers(Entity<GluedComponent> entity, ref RefreshNameModifiersEvent args)
+    {
+        args.AddModifier("glued-name-prefix");
+    }
 }
index aee2abe7ab9f7dba0dc23d022cd7a5a582a6986d..17d18918fea239019285c0c42142088b66fd1df9 100644 (file)
@@ -5,6 +5,7 @@ using Content.Shared.Examine;
 using Content.Shared.Labels;
 using Content.Shared.Labels.Components;
 using Content.Shared.Labels.EntitySystems;
+using Content.Shared.NameModifier.EntitySystems;
 using JetBrains.Annotations;
 using Robust.Shared.Containers;
 
@@ -18,7 +19,7 @@ namespace Content.Server.Labels
     {
         [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
         [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
-        [Dependency] private readonly MetaDataSystem _metaData = default!;
+        [Dependency] private readonly NameModifierSystem _nameMod = default!;
 
         public const string ContainerName = "paper_label";
 
@@ -41,6 +42,8 @@ namespace Content.Server.Labels
                 component.CurrentLabel = Loc.GetString(component.CurrentLabel);
                 Dirty(uid, component);
             }
+
+            _nameMod.RefreshNameModifiers(uid);
         }
 
         /// <summary>
@@ -52,30 +55,11 @@ namespace Content.Server.Labels
         /// <param name="metadata">metadata component for resolve</param>
         public override void Label(EntityUid uid, string? text, MetaDataComponent? metadata = null, LabelComponent? label = null)
         {
-            if (!Resolve(uid, ref metadata))
-                return;
             if (!Resolve(uid, ref label, false))
                 label = EnsureComp<LabelComponent>(uid);
 
-            if (string.IsNullOrEmpty(text))
-            {
-                if (label.OriginalName is null)
-                    return;
-
-                // Remove label
-                _metaData.SetEntityName(uid, label.OriginalName, metadata);
-                label.CurrentLabel = null;
-                label.OriginalName = null;
-
-                Dirty(uid, label);
-
-                return;
-            }
-
-            // Update label
-            label.OriginalName ??= metadata.EntityName;
             label.CurrentLabel = text;
-            _metaData.SetEntityName(uid, $"{label.OriginalName} ({text})", metadata);
+            _nameMod.RefreshNameModifiers(uid);
 
             Dirty(uid, label);
         }
index f786c5f91af27e924b899691706f7b5ad6d2d0a2..c2d15c8a284063e955a32f535e56cc5fd4bbe688 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Shared.IdentityManagement;
 using Content.Shared.Lube;
+using Content.Shared.NameModifier.EntitySystems;
 using Content.Shared.Popups;
 using Content.Shared.Throwing;
 using Robust.Shared.Containers;
@@ -9,11 +10,11 @@ namespace Content.Server.Lube;
 
 public sealed class LubedSystem : EntitySystem
 {
-    [Dependency] private readonly MetaDataSystem _metaData = default!;
     [Dependency] private readonly ThrowingSystem _throwing = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
     [Dependency] private readonly SharedTransformSystem _transform = default!;
     [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly NameModifierSystem _nameMod = default!;
 
     public override void Initialize()
     {
@@ -21,14 +22,12 @@ public sealed class LubedSystem : EntitySystem
 
         SubscribeLocalEvent<LubedComponent, ComponentInit>(OnInit);
         SubscribeLocalEvent<LubedComponent, ContainerGettingInsertedAttemptEvent>(OnHandPickUp);
+        SubscribeLocalEvent<LubedComponent, RefreshNameModifiersEvent>(OnRefreshNameModifiers);
     }
 
     private void OnInit(EntityUid uid, LubedComponent component, ComponentInit args)
     {
-        var meta = MetaData(uid);
-        var name = meta.EntityName;
-        component.BeforeLubedEntityName = meta.EntityName;
-        _metaData.SetEntityName(uid, Loc.GetString("lubed-name-prefix", ("target", name)));
+        _nameMod.RefreshNameModifiers(uid);
     }
 
     private void OnHandPickUp(EntityUid uid, LubedComponent component, ContainerGettingInsertedAttemptEvent args)
@@ -36,7 +35,7 @@ public sealed class LubedSystem : EntitySystem
         if (component.SlipsLeft <= 0)
         {
             RemComp<LubedComponent>(uid);
-            _metaData.SetEntityName(uid, component.BeforeLubedEntityName);
+            _nameMod.RefreshNameModifiers(uid);
             return;
         }
         component.SlipsLeft--;
@@ -47,4 +46,9 @@ public sealed class LubedSystem : EntitySystem
         _throwing.TryThrow(uid, _random.NextVector2(), strength: component.SlipStrength);
         _popup.PopupEntity(Loc.GetString("lube-slip", ("target", Identity.Entity(uid, EntityManager))), user, user, PopupType.MediumCaution);
     }
+
+    private void OnRefreshNameModifiers(Entity<LubedComponent> entity, ref RefreshNameModifiersEvent args)
+    {
+        args.AddModifier("lubed-name-prefix");
+    }
 }
index e224c7c4792796e25e6863659300312ba7eccf97..e5f590a3626c6d9c0ea2227591d7481be49b70c7 100644 (file)
@@ -5,13 +5,12 @@ using Content.Shared.IdentityManagement;
 using Content.Shared.Interaction.Components;
 using Content.Shared.Mind.Components;
 using Content.Shared.Mobs.Systems;
+using Content.Shared.NameModifier.EntitySystems;
 using Content.Shared.Nutrition.AnimalHusbandry;
 using Content.Shared.Nutrition.Components;
 using Content.Shared.Nutrition.EntitySystems;
 using Content.Shared.Storage;
 using Content.Shared.Whitelist;
-using Robust.Server.GameObjects;
-using Robust.Shared.Audio;
 using Robust.Shared.Audio.Systems;
 using Robust.Shared.Player;
 using Robust.Shared.Random;
@@ -29,12 +28,12 @@ public sealed class AnimalHusbandrySystem : EntitySystem
     [Dependency] private readonly IAdminLogManager _adminLog = default!;
     [Dependency] private readonly IGameTiming _timing = default!;
     [Dependency] private readonly IRobustRandom _random = default!;
-    [Dependency] private readonly MetaDataSystem _metaData = default!;
     [Dependency] private readonly MobStateSystem _mobState = default!;
     [Dependency] private readonly PopupSystem _popup = default!;
     [Dependency] private readonly SharedAudioSystem _audio = default!;
     [Dependency] private readonly SharedTransformSystem _transform = default!;
     [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
+    [Dependency] private readonly NameModifierSystem _nameMod = default!;
 
     private readonly HashSet<EntityUid> _failedAttempts = new();
     private readonly HashSet<EntityUid> _birthQueue = new();
@@ -43,8 +42,7 @@ public sealed class AnimalHusbandrySystem : EntitySystem
     public override void Initialize()
     {
         SubscribeLocalEvent<ReproductiveComponent, MindAddedMessage>(OnMindAdded);
-        SubscribeLocalEvent<InfantComponent, ComponentStartup>(OnInfantStartup);
-        SubscribeLocalEvent<InfantComponent, ComponentShutdown>(OnInfantShutdown);
+        SubscribeLocalEvent<InfantComponent, RefreshNameModifiersEvent>(OnRefreshNameModifiers);
     }
 
     // we express EZ-pass terminate the pregnancy if a player takes the role
@@ -54,16 +52,11 @@ public sealed class AnimalHusbandrySystem : EntitySystem
         component.GestationEndTime = null;
     }
 
-    private void OnInfantStartup(EntityUid uid, InfantComponent component, ComponentStartup args)
+    private void OnRefreshNameModifiers(Entity<InfantComponent> entity, ref RefreshNameModifiersEvent args)
     {
-        var meta = MetaData(uid);
-        component.OriginalName = meta.EntityName;
-        _metaData.SetEntityName(uid, Loc.GetString("infant-name-prefix", ("name", meta.EntityName)), meta);
-    }
-
-    private void OnInfantShutdown(EntityUid uid, InfantComponent component, ComponentShutdown args)
-    {
-        _metaData.SetEntityName(uid, component.OriginalName);
+        // This check may seem redundant, but it makes sure that the prefix is removed before the component is removed
+        if (_timing.CurTime < entity.Comp.InfantEndTime)
+            args.AddModifier("infant-name-prefix");
     }
 
     /// <summary>
@@ -202,6 +195,8 @@ public sealed class AnimalHusbandrySystem : EntitySystem
             {
                 var infant = AddComp<InfantComponent>(offspring);
                 infant.InfantEndTime = _timing.CurTime + infant.InfantDuration;
+                // Make sure the name prefix is applied
+                _nameMod.RefreshNameModifiers(offspring);
             }
             _adminLog.Add(LogType.Action, $"{ToPrettyString(uid)} gave birth to {ToPrettyString(offspring)}.");
         }
@@ -249,6 +244,8 @@ public sealed class AnimalHusbandrySystem : EntitySystem
             if (_timing.CurTime < infant.InfantEndTime)
                 continue;
             RemCompDeferred(uid, infant);
+            // Make sure the name prefix gets removed
+            _nameMod.RefreshNameModifiers(uid);
         }
     }
 }
index 0a745d5fc7da4af9a196713e5bfb0f18b3c6bbd7..a8952009e66eac3bba63248a68cfac5ce2494ce8 100644 (file)
@@ -222,9 +222,7 @@ namespace Content.Server.Zombies
             _faction.AddFaction(target, "Zombie");
 
             //gives it the funny "Zombie ___" name.
-            var meta = MetaData(target);
-            zombiecomp.BeforeZombifiedEntityName = meta.EntityName;
-            _metaData.SetEntityName(target, Loc.GetString("zombie-name-prefix", ("target", meta.EntityName)), meta);
+            _nameMod.RefreshNameModifiers(target);
 
             _identity.QueueIdentityUpdate(target);
 
index 552fd2781c0929266b1ce020c701a2b67e82a2da..371c6f1222aeaadbd4fe3d1d0bd16c2749fd2867 100644 (file)
@@ -14,6 +14,7 @@ using Content.Shared.Mind;
 using Content.Shared.Mobs;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Mobs.Systems;
+using Content.Shared.NameModifier.EntitySystems;
 using Content.Shared.Popups;
 using Content.Shared.Weapons.Melee.Events;
 using Content.Shared.Zombies;
@@ -34,9 +35,9 @@ namespace Content.Server.Zombies
         [Dependency] private readonly ActionsSystem _actions = default!;
         [Dependency] private readonly AutoEmoteSystem _autoEmote = default!;
         [Dependency] private readonly EmoteOnDamageSystem _emoteOnDamage = default!;
-        [Dependency] private readonly MetaDataSystem _metaData = default!;
         [Dependency] private readonly MobStateSystem _mobState = default!;
         [Dependency] private readonly SharedPopupSystem _popup = default!;
+        [Dependency] private readonly NameModifierSystem _nameMod = default!;
 
         public const SlotFlags ProtectiveSlots =
             SlotFlags.FEET |
@@ -281,7 +282,7 @@ namespace Content.Server.Zombies
             _humanoidAppearance.SetSkinColor(target, zombiecomp.BeforeZombifiedSkinColor, false);
             _bloodstream.ChangeBloodReagent(target, zombiecomp.BeforeZombifiedBloodReagent);
 
-            _metaData.SetEntityName(target, zombiecomp.BeforeZombifiedEntityName);
+            _nameMod.RefreshNameModifiers(target);
             return true;
         }
 
index fd7a52fdb139dc8cd8b501c7cca2d1d044c587f7..4b46f0aa5b0ef4ea7a10fcb424785d7119e14b3c 100644 (file)
@@ -6,11 +6,6 @@ namespace Content.Shared.Glue;
 [Access(typeof(SharedGlueSystem))]
 public sealed partial class GluedComponent : Component
 {
-    /// <summary>
-    /// Reverts name to before prefix event (essentially removes prefix).
-    /// </summary>
-    [DataField("beforeGluedEntityName"), ViewVariables(VVAccess.ReadOnly)]
-    public string BeforeGluedEntityName = string.Empty;
 
     [DataField("until", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
     public TimeSpan Until;
index ea368884e0506a260604b59dc346d46413357045..bca9eb6cfa23c13e8a3b06ad9a0f40ea5312a8e3 100644 (file)
@@ -7,6 +7,7 @@ using Content.Shared.Gravity;
 using Content.Shared.IdentityManagement.Components;
 using Content.Shared.Inventory.Events;
 using Content.Shared.Movement.Systems;
+using Content.Shared.NameModifier.EntitySystems;
 using Content.Shared.Overlays;
 using Content.Shared.Radio;
 using Content.Shared.Slippery;
@@ -28,6 +29,7 @@ public partial class InventorySystem
         SubscribeLocalEvent<InventoryComponent, SeeIdentityAttemptEvent>(RelayInventoryEvent);
         SubscribeLocalEvent<InventoryComponent, ModifyChangedTemperatureEvent>(RelayInventoryEvent);
         SubscribeLocalEvent<InventoryComponent, GetDefaultRadioChannelEvent>(RelayInventoryEvent);
+        SubscribeLocalEvent<InventoryComponent, RefreshNameModifiersEvent>(RelayInventoryEvent);
 
         // by-ref events
         SubscribeLocalEvent<InventoryComponent, GetExplosionResistanceEvent>(RefRelayInventoryEvent);
index c0dccd348154e1f99ba77f78c67ac2573d7e2610..d57023c8ab152837d0f9e7664207e2cbc5a15b82 100644 (file)
@@ -14,11 +14,4 @@ public sealed partial class LabelComponent : Component
     /// </summary>
     [DataField, AutoNetworkedField]
     public string? CurrentLabel { get; set; }
-
-    /// <summary>
-    ///  The original name of the entity
-    ///  Used for reverting the modified entity name when the label is removed
-    /// </summary>
-    [DataField, AutoNetworkedField]
-    public string? OriginalName { get; set; }
 }
index 1189bb46d043ba3c347c0e441f1a1517ce338ba4..f1998e524d90965ecde2b4b3dfc0cbb4b21960be 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Shared.Examine;
 using Content.Shared.Labels.Components;
+using Content.Shared.NameModifier.EntitySystems;
 using Robust.Shared.Utility;
 
 namespace Content.Shared.Labels.EntitySystems;
@@ -11,6 +12,7 @@ public abstract partial class SharedLabelSystem : EntitySystem
         base.Initialize();
 
         SubscribeLocalEvent<LabelComponent, ExaminedEvent>(OnExamine);
+        SubscribeLocalEvent<LabelComponent, RefreshNameModifiersEvent>(OnRefreshNameModifiers);
     }
 
     public virtual void Label(EntityUid uid, string? text, MetaDataComponent? metadata = null, LabelComponent? label = null){}
@@ -27,4 +29,10 @@ public abstract partial class SharedLabelSystem : EntitySystem
         message.AddText(Loc.GetString("hand-labeler-has-label", ("label", label.CurrentLabel)));
         args.PushMessage(message);
     }
+
+    private void OnRefreshNameModifiers(Entity<LabelComponent> entity, ref RefreshNameModifiersEvent args)
+    {
+        if (!string.IsNullOrEmpty(entity.Comp.CurrentLabel))
+            args.AddModifier("comp-label-format", extraArgs: ("label", entity.Comp.CurrentLabel));
+    }
 }
index fe1946ddb15424dc6a19666d6436947f33b91ce5..9d032a077ecd8dcb9ed38347396cfa85c16fd4f7 100644 (file)
@@ -3,12 +3,6 @@ namespace Content.Shared.Lube;
 [RegisterComponent]
 public sealed partial class LubedComponent : Component
 {
-    /// <summary>
-    /// Reverts name to before prefix event (essentially removes prefix).
-    /// </summary>
-    [DataField("beforeLubedEntityName")]
-    public string BeforeLubedEntityName = string.Empty;
-
     [DataField("slipsLeft"), ViewVariables(VVAccess.ReadWrite)]
     public int SlipsLeft;
 
diff --git a/Content.Shared/NameModifier/Components/ModifyWearerNameComponent.cs b/Content.Shared/NameModifier/Components/ModifyWearerNameComponent.cs
new file mode 100644 (file)
index 0000000..781ed3d
--- /dev/null
@@ -0,0 +1,25 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.NameModifier.Components;
+
+/// <summary>
+/// Adds a modifier to the wearer's name when this item is equipped,
+/// and removes it when it is unequipped.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentState]
+public sealed partial class ModifyWearerNameComponent : Component
+{
+    /// <summary>
+    /// The localization ID of the text to be used as the modifier.
+    /// The base name will be passed in as <c>$baseName</c>
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public LocId LocId = string.Empty;
+
+    /// <summary>
+    /// Priority of the modifier. See <see cref="EntitySystems.RefreshNameModifiersEvent"/> for more information.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public int Priority;
+}
diff --git a/Content.Shared/NameModifier/Components/NameModifierComponent.cs b/Content.Shared/NameModifier/Components/NameModifierComponent.cs
new file mode 100644 (file)
index 0000000..3a9dd97
--- /dev/null
@@ -0,0 +1,20 @@
+using Content.Shared.NameModifier.EntitySystems;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.NameModifier.Components;
+
+/// <summary>
+/// Used to manage modifiers on an entity's name and handle renaming in a way
+/// that survives being renamed by multiple systems.
+/// </summary>
+[RegisterComponent]
+[NetworkedComponent, AutoGenerateComponentState]
+[Access(typeof(NameModifierSystem))]
+public sealed partial class NameModifierComponent : Component
+{
+    /// <summary>
+    /// The entity's name without any modifiers applied.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public string BaseName = string.Empty;
+}
diff --git a/Content.Shared/NameModifier/EntitySystems/ModifyWearerNameSystem.cs b/Content.Shared/NameModifier/EntitySystems/ModifyWearerNameSystem.cs
new file mode 100644 (file)
index 0000000..e728e6c
--- /dev/null
@@ -0,0 +1,34 @@
+using Content.Shared.Clothing;
+using Content.Shared.Inventory;
+using Content.Shared.NameModifier.Components;
+
+namespace Content.Shared.NameModifier.EntitySystems;
+
+public sealed partial class ModifyWearerNameSystem : EntitySystem
+{
+    [Dependency] private readonly NameModifierSystem _nameMod = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ModifyWearerNameComponent, InventoryRelayedEvent<RefreshNameModifiersEvent>>(OnRefreshNameModifiers);
+        SubscribeLocalEvent<ModifyWearerNameComponent, ClothingGotEquippedEvent>(OnGotEquipped);
+        SubscribeLocalEvent<ModifyWearerNameComponent, ClothingGotUnequippedEvent>(OnGotUnequipped);
+    }
+
+    private void OnGotEquipped(Entity<ModifyWearerNameComponent> entity, ref ClothingGotEquippedEvent args)
+    {
+        _nameMod.RefreshNameModifiers(args.Wearer);
+    }
+
+    private void OnGotUnequipped(Entity<ModifyWearerNameComponent> entity, ref ClothingGotUnequippedEvent args)
+    {
+        _nameMod.RefreshNameModifiers(args.Wearer);
+    }
+
+    private void OnRefreshNameModifiers(Entity<ModifyWearerNameComponent> entity, ref InventoryRelayedEvent<RefreshNameModifiersEvent> args)
+    {
+        args.Args.AddModifier(entity.Comp.LocId, entity.Comp.Priority);
+    }
+}
diff --git a/Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs b/Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs
new file mode 100644 (file)
index 0000000..4dffb51
--- /dev/null
@@ -0,0 +1,143 @@
+using System.Linq;
+using Content.Shared.Inventory;
+using Content.Shared.NameModifier.Components;
+
+namespace Content.Shared.NameModifier.EntitySystems;
+
+/// <inheritdoc cref="NameModifierComponent"/>
+public sealed partial class NameModifierSystem : EntitySystem
+{
+    [Dependency] private readonly MetaDataSystem _metaData = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<NameModifierComponent, EntityRenamedEvent>(OnEntityRenamed);
+    }
+
+    private void OnEntityRenamed(Entity<NameModifierComponent> entity, ref EntityRenamedEvent args)
+    {
+        SetBaseName((entity, entity.Comp), args.NewName);
+        RefreshNameModifiers((entity, entity.Comp));
+    }
+
+    private void SetBaseName(Entity<NameModifierComponent> entity, string name)
+    {
+        if (name == entity.Comp.BaseName)
+            return;
+
+        // Set the base name to the new name
+        entity.Comp.BaseName = name;
+        Dirty(entity);
+    }
+
+    /// <summary>
+    /// Raises a <see cref="RefreshNameModifiersEvent"/> to gather modifiers and
+    /// updates the entity's name to its base name with modifiers applied.
+    /// This will add a <see cref="NameModifierComponent"/> if any modifiers are added.
+    /// </summary>
+    /// <remarks>
+    /// Call this to update the entity's name when adding or removing a modifier.
+    /// </remarks>
+    public void RefreshNameModifiers(Entity<NameModifierComponent?> entity)
+    {
+        var meta = MetaData(entity);
+        var baseName = meta.EntityName;
+        if (Resolve(entity, ref entity.Comp, logMissing: false))
+            baseName = entity.Comp.BaseName;
+
+        // Raise an event to get any modifiers
+        // If the entity already has the component, use its BaseName, otherwise use the entity's name from metadata
+        var modifierEvent = new RefreshNameModifiersEvent(baseName);
+        RaiseLocalEvent(entity, ref modifierEvent);
+
+        // Nothing added a modifier, so we can just use the base name
+        if (modifierEvent.ModifierCount == 0)
+        {
+            // If the entity doesn't have the component, we're done
+            if (entity.Comp == null)
+                return;
+
+            // Restore the base name
+            _metaData.SetEntityName(entity, entity.Comp.BaseName, meta, raiseEvents: false);
+            // The component isn't doing anything anymore, so remove it
+            RemComp<NameModifierComponent>(entity);
+            return;
+        }
+        // We have at least one modifier, so we need to apply it to the entity.
+
+        // Get the final name with modifiers applied
+        var modifiedName = modifierEvent.GetModifiedName();
+
+        // Add the component if needed, and initialize it with the base name
+        if (!EnsureComp<NameModifierComponent>(entity, out var comp))
+            SetBaseName((entity, comp), meta.EntityName);
+
+        // Set the entity's name with modifiers applied
+        _metaData.SetEntityName(entity, modifiedName, meta, raiseEvents: false);
+    }
+}
+
+/// <summary>
+/// Raised on an entity when <see cref="NameModifierSystem.RefreshNameModifiers"/> is called.
+/// Subscribe to this event and use its methods to add modifiers to the entity's name.
+/// </summary>
+[ByRefEvent]
+public sealed class RefreshNameModifiersEvent : IInventoryRelayEvent
+{
+    /// <summary>
+    /// The entity's name without any modifiers applied.
+    /// If you want to base a modifier on the entity's name, use
+    /// this so you don't include other modifiers.
+    /// </summary>
+    public readonly string BaseName;
+
+    private readonly List<(LocId LocId, int Priority, (string, object)[] ExtraArgs)> _modifiers = [];
+
+    /// <inheritdoc/>
+    public SlotFlags TargetSlots => ~SlotFlags.POCKET;
+
+    /// <summary>
+    /// How many modifiers have been added to this event.
+    /// </summary>
+    public int ModifierCount => _modifiers.Count;
+
+    public RefreshNameModifiersEvent(string baseName)
+    {
+        BaseName = baseName;
+    }
+
+    /// <summary>
+    /// Adds a modifier to the entity's name.
+    /// The original name will be passed to Fluent as <c>$baseName</c> along with any <paramref name="extraArgs"/>.
+    /// Modifiers with a higher <paramref name="priority"/> will be applied later.
+    /// </summary>
+    public void AddModifier(LocId locId, int priority = 0, params (string, object)[] extraArgs)
+    {
+        _modifiers.Add((locId, priority, extraArgs));
+    }
+
+    /// <summary>
+    /// Returns the final name with all modifiers applied.
+    /// </summary>
+    public string GetModifiedName()
+    {
+        // Start out with the entity's name name
+        var name = BaseName;
+
+        // Iterate through all the modifiers in priority order
+        foreach (var modifier in _modifiers.OrderBy(n => n.Priority))
+        {
+            // Grab any extra args needed by the Loc string
+            var args = modifier.ExtraArgs;
+            // Add the current version of the entity name as an arg
+            Array.Resize(ref args, args.Length + 1);
+            args[^1] = ("baseName", name);
+            // Resolve the Loc string and use the result as the base in the next iteration.
+            name = Loc.GetString(modifier.LocId, args);
+        }
+
+        return name;
+    }
+}
index 2708c823d2c83a1fea9312e5bfc94bd144d0e95c..06c533e6460f3ac45bc204db577e328524f79144 100644 (file)
@@ -35,10 +35,4 @@ public sealed partial class InfantComponent : Component
     [DataField("infantEndTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
     [AutoPausedField]
     public TimeSpan InfantEndTime;
-
-    /// <summary>
-    /// The entity's name before the "baby" prefix is added.
-    /// </summary>
-    [DataField("originalName")]
-    public string OriginalName = string.Empty;
 }
index 6d9103639f6cdf2d5ab1b315da79b74c13be01fd..0388450a8c41cc601569bcadee69c52664d0b28c 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Shared.Movement.Systems;
+using Content.Shared.NameModifier.EntitySystems;
 
 namespace Content.Shared.Zombies;
 
@@ -10,6 +11,7 @@ public abstract class SharedZombieSystem : EntitySystem
         base.Initialize();
 
         SubscribeLocalEvent<ZombieComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshSpeed);
+        SubscribeLocalEvent<ZombieComponent, RefreshNameModifiersEvent>(OnRefreshNameModifiers);
     }
 
     private void OnRefreshSpeed(EntityUid uid, ZombieComponent component, RefreshMovementSpeedModifiersEvent args)
@@ -17,4 +19,9 @@ public abstract class SharedZombieSystem : EntitySystem
         var mod = component.ZombieMovementSpeedDebuff;
         args.ModifySpeed(mod, mod);
     }
+
+    private void OnRefreshNameModifiers(Entity<ZombieComponent> entity, ref RefreshNameModifiersEvent args)
+    {
+        args.AddModifier("zombie-name-prefix");
+    }
 }
index 2cd0cdb96d77615226b6401cad4bebfdad5faf61..f510d65d6dcd4bb7cfd6c8bd7b3f4366a0e11fd2 100644 (file)
@@ -62,12 +62,6 @@ public sealed partial class ZombieComponent : Component
     [DataField("zombieRoleId", customTypeSerializer: typeof(PrototypeIdSerializer<AntagPrototype>))]
     public string ZombieRoleId = "Zombie";
 
-    /// <summary>
-    /// The EntityName of the humanoid to restore in case of cloning
-    /// </summary>
-    [DataField("beforeZombifiedEntityName"), ViewVariables(VVAccess.ReadOnly)]
-    public string BeforeZombifiedEntityName = string.Empty;
-
     /// <summary>
     /// The CustomBaseLayers of the humanoid to restore in case of cloning
     /// </summary>
index 21f096273d1b6a5c4e6a7d71688b7c73ee436c3a..ce43bd714adb0db9099ad61997b4aa3e0d588ae2 100644 (file)
@@ -1 +1 @@
-transformable-container-component-glass = {$name} glass
+transformable-container-component-glass = {$reagent} glass
index 206df8657dd376db8a51c2d2ac13fe03aa3859a8..0ffd3f32dfb2f6ae8ac79505718d01c64c140cf0 100644 (file)
@@ -1,2 +1,2 @@
 cluwne-transform = {CAPITALIZE(THE($target))} turned into a cluwne!
-cluwne-name-prefix = Cluwnified {$target}
+cluwne-name-prefix = cluwnified {$baseName}
index 1a711d51c21d5d3f7217e2218edbc19b210895b0..158ebc9ed85b7e829e31f8333dbddaeb21f78405 100644 (file)
@@ -1,5 +1,5 @@
 glue-success = {THE($target)} has been covered in glue!
-glued-name-prefix = Glued {$target}
+glued-name-prefix = glued {$baseName}
 glue-failure = Can't cover {THE($target)} in glue!
 glue-verb-text = Apply Glue
 glue-verb-message = Glue an object
diff --git a/Resources/Locale/en-US/label/label-component.ftl b/Resources/Locale/en-US/label/label-component.ftl
new file mode 100644 (file)
index 0000000..ff3a250
--- /dev/null
@@ -0,0 +1 @@
+comp-label-format = {$baseName} ({$label})
index 92dd2802ec77a860708a7e4059041ca0908bf76b..1b5b66e069f15ce7629727e021a8702a2f190521 100644 (file)
@@ -1,5 +1,5 @@
 lube-success = {THE($target)} has been covered in lube!
-lubed-name-prefix = Lubed {$target}
+lubed-name-prefix = lubed {$baseName}
 lube-failure = Can't cover {THE($target)} in lube!
 lube-slip = {THE($target)} slips out of your hands!
 lube-verb-text = Apply Lube
index 6ca108b653a35260c25876f2348ceb360fb98067..cf7bf2d03acbeab69a970ea940fff066ea7d5001 100644 (file)
@@ -1,3 +1,3 @@
-infant-name-prefix = baby {$name}
+infant-name-prefix = baby {$baseName}
 reproductive-birth-popup = {CAPITALIZE(THE($parent))} gave birth!
 reproductive-laid-egg-popup = {CAPITALIZE(THE($parent))} lays an egg!
index a391a95b0dae6f2f5383a5ff29090767471ae98c..d45943e825de3b0fe0d769dbf55e35c7905ac38b 100644 (file)
@@ -2,7 +2,7 @@ zombie-transform = {CAPITALIZE(THE($target))} turned into a zombie!
 zombie-infection-greeting = You have become a zombie. Your goal is to seek out the living and to try to infect them.  Work together with the other zombies to overtake the station.
 
 zombie-generic = zombie
-zombie-name-prefix = Zombified {$target}
+zombie-name-prefix = zombified {$baseName}
 zombie-role-desc =  A malevolent creature of the dead.
 zombie-role-rules = You are an antagonist. Search out the living and bite them in order to infect them and turn them into zombies. Work together with the other zombies to overtake the station.
 
index 4bd71f898db17fdf461d85d8dc7e02f46ecd5c3e..68d96fcd3dede85ef0ad253264723aec6667dc61 100644 (file)
@@ -79,7 +79,6 @@
         - !type:DoActsBehavior
           acts: [ "Destruction" ]
     - type: Label
-      originalName: jug
     - type: Tag
       tags:
       - ChemDispensable