From 8b48748048978170a19d86fd16353d415aef0251 Mon Sep 17 00:00:00 2001 From: Connor Huffine Date: Sat, 8 Aug 2026 20:22:15 -0400 Subject: [PATCH] Give mobs amusing name prefixes (#44977) * Functional product * Bugfix * Handle empty case gracefully * Fix inserting bad values * First set of tests * Don't need this * Dont add empty modifers to names Signed-off-by: Connor Huffine * Typo and [SubscribeLocalEvent] Signed-off-by: Connor Huffine * Refactor GenerateUniqueName to GenerateUniqueNameModifier Signed-off-by: Connor Huffine * Fix returning name when empty Signed-off-by: Connor Huffine * Docs Signed-off-by: Connor Huffine * Tests Signed-off-by: Connor Huffine * Fix comment Signed-off-by: Connor Huffine * Fix FTL test Signed-off-by: Connor Huffine * Add adjectives Signed-off-by: Connor Huffine * Amateur hour Signed-off-by: Connor Huffine * Simplify spawning Signed-off-by: Connor Huffine * Even more streamlined Signed-off-by: Connor Huffine * Improve comment Signed-off-by: Connor Huffine * Apply suggestions from code review Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com> Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com> --------- Signed-off-by: Connor Huffine Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com> --- .../NameIdentifier/NameIdentifierTest.cs | 248 ++++++++++++++++++ .../Cargo/Systems/CargoSystem.Bounty.cs | 2 +- .../NameIdentifier/NameIdentifierSystem.cs | 134 +++++++--- .../NameIdentifier/NameIdentifierComponent.cs | 3 + .../NameIdentifierGroupPrototype.cs | 14 + .../SharedNameIdentifierSystem.cs | 18 +- .../EntitySystems/NameModifierSystem.cs | 10 +- .../Locale/en-US/datasets/adjectives.ftl | 26 ++ Resources/Locale/en-US/name-identifier.ftl | 1 + .../en-US/name-identifier/name-identifier.ftl | 3 + Resources/Prototypes/Datasets/adjectives.yml | 2 +- .../Prototypes/Entities/Mobs/NPCs/animals.yml | 4 + .../Prototypes/name_identifier_groups.yml | 8 +- 13 files changed, 416 insertions(+), 57 deletions(-) create mode 100644 Content.IntegrationTests/Tests/NameIdentifier/NameIdentifierTest.cs diff --git a/Content.IntegrationTests/Tests/NameIdentifier/NameIdentifierTest.cs b/Content.IntegrationTests/Tests/NameIdentifier/NameIdentifierTest.cs new file mode 100644 index 0000000000..f85f93d3c9 --- /dev/null +++ b/Content.IntegrationTests/Tests/NameIdentifier/NameIdentifierTest.cs @@ -0,0 +1,248 @@ +using System.Collections.Generic; +using System.Linq; +using Content.IntegrationTests.Fixtures; +using Content.IntegrationTests.Fixtures.Attributes; +using Content.Server.NameIdentifier; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; + +namespace Content.IntegrationTests.Tests.NameIdentifier; + +[TestOf(typeof(NameIdentifierSystem))] +public sealed class NameIdentifierTest : GameTest +{ + private const string MaxIds = "5"; + private const int MaxIdsInt = 5; + + private const string NameTest = "NameTest"; + private const string NumberTestGroup = "NumberTestGroup"; + private const string PrefixTestGroup = "PrefixTestGroup"; + private const string PrefixTest = "PrefixTest"; + private const string ParenTestEnt = "ParenTestEnt"; + private const string LocTestEnt = "LocTestEnt"; + + [TestPrototypes] + private const string Prototypes = + $""" + - type: nameIdentifierGroup + id: {NumberTestGroup} + minValue: 1 + maxValue: {MaxIds} + + - type: entity + name: {NameTest} + id: {NameTest} + components: + - type: NameIdentifier + group: {NumberTestGroup} + + - type: nameIdentifierGroup + parent: {NumberTestGroup} + id: {PrefixTestGroup} + prefix: true + + - type: entity + name: {PrefixTest} + id: {PrefixTest} + components: + - type: NameIdentifier + group: {PrefixTestGroup} + + - type: entity + name: {ParenTestEnt} + id: {ParenTestEnt} + components: + - type: NameIdentifier + group: GenericNumber + + - type: localizedDataset + id: NameIdentifierTest + values: + prefix: name-identifier-test- + count: 1 + + - type: nameIdentifierGroup + id: Localized + identifierDataset: NameIdentifierTest + + - type: entity + name: {LocTestEnt} + id: {LocTestEnt} + components: + - type: NameIdentifier + group: Localized + """; + + [SidedDependency(Side.Server)] private NameIdentifierSystem _nameModifier = default!; + + /// + /// + /// We set false because the client has no unique behavior. + /// Skipping client sim saves several seconds. + /// + public override PoolSettings PoolSettings => new () + { + Connected = false, + }; + + [Test] + [Description("Tests that prototypes are loaded into the identifier system properly.")] + public async Task ValidatePoolGeneration() + { + await Server.WaitAssertion(() => + { + using (Assert.EnterMultipleScope()) + { + Assert.That(_nameModifier.CurrentIds.ContainsKey(NumberTestGroup), + "Failed to load test prototypes."); + Assert.That(_nameModifier.CurrentIds[NumberTestGroup], + Has.Count.EqualTo(MaxIdsInt), + "Failed to load test prototypes."); + } + }); + } + + [Test] + [Description("Tests that IDs are removed from the pool and properly returned when deleted.")] + public async Task TakeAndReturn() + { + await Server.WaitAssertion(() => + { + // Create an entity that will draw a modifier from the pool + var single = SSpawn(NameTest); + + Assert.That(_nameModifier.CurrentIds[NumberTestGroup], + Has.Count.EqualTo(MaxIdsInt - 1), + "CurrentIds did not decrease."); + + // Delete the entity, which should return the modifier to the pool + SDeleteNow(single); + + Assert.That(_nameModifier.CurrentIds[NumberTestGroup], + Has.Count.EqualTo(MaxIdsInt), + "CurrentIds did not return to max."); + }); + } + + [Test] + [Description("Tests that the list of IDs behaves properly when taken to zero values.")] + public async Task ExhaustAndRefillList() + { + var entList = new List(); + var original = _nameModifier.CurrentIds[NumberTestGroup]; + + await Server.WaitAssertion(() => + { + // Spawn as many entities as the pool should hold. + for (var i = 0; i <= MaxIdsInt; i++) + entList.Add(SSpawn(NameTest)); + + using (Assert.EnterMultipleScope()) + { + Assert.That(_nameModifier.CurrentIds[NumberTestGroup], + Has.Count.EqualTo(0), + "CurrentIds failed to empty."); + } + + // Delete all entities, returning them to the pool. + foreach (var ent in entList) + SDeleteNow(ent); + + using (Assert.EnterMultipleScope()) + { + Assert.That(_nameModifier.CurrentIds[NumberTestGroup], + Has.Count.EqualTo(MaxIdsInt), + "CurrentIds failed to refill."); + Assert.That(original, + Is.EqualTo(_nameModifier.CurrentIds[NumberTestGroup]), + "List of values was not returned to starting state."); + } + }); + } + + [Test] + [Description("Tests that IDs are not duplicated and correctly formatted.")] + public async Task ValidateDefaultGeneration() + { + var entList = new List(); + + await Server.WaitAssertion(() => + { + // Spawn as many entities as the pool should hold. + for (var i = 0; i < MaxIdsInt; i++) + entList.Add(SSpawn(NameTest)); + + var nameList = entList + .Select(p => SEntMan.GetComponent(p).EntityName) + .ToList(); + + using (Assert.EnterMultipleScope()) + { + Assert.That(nameList, Is.Unique, "A duplicate name was generated."); + Assert.That(nameList, Has.All.Contain(NameTest), "The base name was not preserved."); + Assert.That(nameList, Has.All.Match(@"\d+$"), "Created an invalid name."); + } + }); + } + + [Test] + [Description("Tests that the core parenthesis wrapped modifier works properly.")] + public async Task ParenthesisAppend() + { + await Server.WaitAssertion(() => + { + var single = SSpawn(ParenTestEnt); + Assert.That( + SEntMan.GetComponent(single).EntityName, + Does.Match($@"{ParenTestEnt} \(\d+\)$"), + "Did not create a valid parenthesis wrapped name." + ); + }); + } + + [Test] + [Description("Tests that nothing is attached to the name when the pool of available names is empty.")] + public async Task DrawWhenEmpty() + { + await Server.WaitAssertion(() => + { + // Spawn as many entities as the pool should hold. + for (var i = 0; i < MaxIdsInt; i++) + SSpawn(NameTest); + + var emptyDraw = SSpawn(NameTest); + var name = SEntMan.GetComponent(emptyDraw).EntityName; + Assert.That(name, Is.EqualTo(NameTest), "Created an invalid name."); + }); + } + + [Test] + [Description("Tests that a localized value is properly fetched and attached.")] + public async Task LocalizedIdentifier() + { + await Server.WaitAssertion(() => + { + var single = SSpawn(LocTestEnt); + Assert.That( + SEntMan.GetComponent(single).EntityName, + Is.EqualTo($"{LocTestEnt} TestValue"), + "Did not create a valid localized name." + ); + }); + } + + [Test] + [Description("Tests that the prefix setting properly prepends a value.")] + public async Task PrefixedIdentifier() + { + await Server.WaitAssertion(() => + { + var single = SSpawn(PrefixTest); + Assert.That( + SEntMan.GetComponent(single).EntityName, + Does.Match(@"^\d+"), + "Did not create a valid name with a prefix." + ); + }); + } +} diff --git a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs index 3a5fd34eb3..919676c126 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Bounty.cs @@ -428,7 +428,7 @@ public sealed partial class CargoSystem if (component.Bounties.Count >= component.MaxBounties) return false; - _nameIdentifier.GenerateUniqueName(uid, BountyNameIdentifierGroup, out var randomVal); + _nameIdentifier.GenerateUniqueNameModifier(BountyNameIdentifierGroup, out var randomVal); var newBounty = new CargoBountyData(bounty, randomVal); // This bounty id already exists! Probably because NameIdentifierSystem ran out of ids. if (component.Bounties.Any(b => b.Id == newBounty.Id)) diff --git a/Content.Server/NameIdentifier/NameIdentifierSystem.cs b/Content.Server/NameIdentifier/NameIdentifierSystem.cs index c8ab73fdf5..a9a6dfed7e 100644 --- a/Content.Server/NameIdentifier/NameIdentifierSystem.cs +++ b/Content.Server/NameIdentifier/NameIdentifierSystem.cs @@ -7,6 +7,7 @@ using Robust.Shared.Random; namespace Content.Server.NameIdentifier; +/// public sealed partial class NameIdentifierSystem : SharedNameIdentifierSystem { [Dependency] private IRobustRandom _robustRandom = default!; @@ -18,6 +19,7 @@ public sealed partial class NameIdentifierSystem : SharedNameIdentifierSystem [ViewVariables] public readonly Dictionary> CurrentIds = []; + /// public override void Initialize() { base.Initialize(); @@ -35,7 +37,17 @@ public sealed partial class NameIdentifierSystem : SharedNameIdentifierSystem if (ent.Comp.Group is null) return; - if (CurrentIds.TryGetValue(ent.Comp.Group, out var ids) && ids.Count > 0) + if (!CurrentIds.TryGetValue(ent.Comp.Group, out var ids)) + { + _nameModifier.RefreshNameModifiers(ent.Owner); + return; + } + + // Not a valid value + if (ent.Comp.Identifier == -1) + return; + + if (ids.Count > 0) { // Avoid inserting the value right back at the end or shuffling in place: // just pick a random spot to put it and then move that one to the end. @@ -44,44 +56,79 @@ public sealed partial class NameIdentifierSystem : SharedNameIdentifierSystem ids[randomIndex] = ent.Comp.Identifier; ids.Add(random); } + else + { + ids.Add(ent.Comp.Identifier); + } _nameModifier.RefreshNameModifiers(ent.Owner); } /// - /// Generates a new unique name/suffix for a given entity and adds it to - /// but does not set the entity's name. + /// /// - public string GenerateUniqueName(EntityUid uid, ProtoId proto, out int randomVal) + /// + /// This overload resolves the ProtoId of the NameIdentifierGroupPrototype first. + /// + /// A ProtoId that will be resolved and passed on. + /// The index value of the randomly selected modifier. + public string GenerateUniqueNameModifier(ProtoId proto, out int randomVal) { - return GenerateUniqueName(uid, ProtoMan.Index(proto), out randomVal); + return GenerateUniqueNameModifier(ProtoMan.Index(proto), out randomVal); } /// - /// Generates a new unique name/suffix for a given entity and adds it to - /// but does not set the entity's name. + /// Generates a new unique name modifier for a given entity and removes it from + /// but does not set the entity's name. /// - public string GenerateUniqueName(EntityUid uid, NameIdentifierGroupPrototype proto, out int randomVal) + /// The prototype to retrieve from. + /// The index value of the randomly selected modifier. + /// A formatted and/or localized modifier. Empty string if invalid. + public string GenerateUniqueNameModifier(NameIdentifierGroupPrototype proto, out int randomVal) { - randomVal = 0; - var entityName = Name(uid); + randomVal = -1; if (!CurrentIds.TryGetValue(proto.ID, out var set)) - return entityName; + return string.Empty; if (set.Count == 0) { // Oh jeez. We're outta numbers. - return entityName; + return string.Empty; } randomVal = set[^1]; set.RemoveAt(set.Count - 1); - return proto.Format is not null - ? Loc.GetString(proto.Format, ("number", randomVal)) - : $"{randomVal}"; + return FormatAndLocalize(randomVal, proto); } + /// + /// Format and localize the provided integer against the prototype. + /// + /// The selected value to process. + /// The prototype that defines optional localization and formatting. + /// A formatted and/or localized string. + private string FormatAndLocalize(int value, NameIdentifierGroupPrototype proto) + { + var formatted = value.ToString(); + + if (proto.IdentifierDataset is not null) + { + var identifiers = ProtoMan.Index(proto.IdentifierDataset); + formatted = Loc.GetString(identifiers.Values.Prefix+formatted); + } + + return proto.Format is not null + ? Loc.GetString(proto.Format, ("number", formatted)) + : formatted; + } + + /// + /// Initializes the component when initialized on the map. + /// This will use the existing identifier, if present, or generate a new one and update the component appropriately. + /// + /// The entity component tuple being initialized. + /// The arguments for the event. Unused. private void OnMapInit(Entity ent, ref MapInitEvent args) { if (ent.Comp.Group is null) @@ -90,29 +137,18 @@ public sealed partial class NameIdentifierSystem : SharedNameIdentifierSystem if (!ProtoMan.Resolve(ent.Comp.Group, out var group)) return; - int id; - string uniqueName; - // If it has an existing valid identifier then use that, otherwise generate a new one. if (ent.Comp.Identifier != -1 && CurrentIds.TryGetValue(ent.Comp.Group, out var ids) && ids.Remove(ent.Comp.Identifier)) { - id = ent.Comp.Identifier; - uniqueName = group.Format is not null - ? Loc.GetString(group.Format, ("number", id)) - : $"{id}"; + ent.Comp.FullIdentifier = FormatAndLocalize(ent.Comp.Identifier, group); } else { - uniqueName = GenerateUniqueName(ent, group, out id); - ent.Comp.Identifier = id; + ent.Comp.FullIdentifier = GenerateUniqueNameModifier(group, out ent.Comp.Identifier); } - ent.Comp.FullIdentifier = group.FullName - ? uniqueName - : $"({uniqueName})"; - Dirty(ent); _nameModifier.RefreshNameModifiers(ent.Owner); } @@ -122,10 +158,24 @@ public sealed partial class NameIdentifierSystem : SharedNameIdentifierSystem EnsureIds(); } + /// + /// Fill a provided list with a range of numbers corresponding to a prototype's defined range. + /// + /// The prototype to retrieve from. + /// Reference to the list where values should be placed. private void FillGroup(NameIdentifierGroupPrototype proto, List values) { values.Clear(); - for (var i = proto.MinValue; i < proto.MaxValue; i++) + + var (max, min) = (proto.MaxValue, proto.MinValue); + + if (proto.IdentifierDataset is not null) + { + max = ProtoMan.Index(proto.IdentifierDataset).Values.Count; + min = 1; + } + + for (var i = min; i <= max; i++) { values.Add(i); } @@ -133,14 +183,23 @@ public sealed partial class NameIdentifierSystem : SharedNameIdentifierSystem _robustRandom.Shuffle(values); } + /// + /// Retrieve the appropriate list or create a new one if one does not already exist. + /// + /// The prototype to retrieve from. + /// The list corresponding to the prototype, a new empty list if not already extant. private List GetOrCreateIdList(NameIdentifierGroupPrototype proto) { - if (!CurrentIds.TryGetValue(proto.ID, out var ids)) - { - ids = new List(proto.MaxValue - proto.MinValue); - CurrentIds.Add(proto.ID, ids); - } + // The ID list already exists. + if (CurrentIds.TryGetValue(proto.ID, out var ids)) + return ids; + // If we're using a dataset, grab the count. Otherwise, use (max - min). + ids = new List(proto.IdentifierDataset is null + ? proto.MaxValue - proto.MinValue + : ProtoMan.Index(proto.IdentifierDataset).Values.Count); + + CurrentIds.Add(proto.ID, ids); return ids; } @@ -176,18 +235,17 @@ public sealed partial class NameIdentifierSystem : SharedNameIdentifierSystem foreach (var proto in set.Modified.Values) { - var name_proto = (NameIdentifierGroupPrototype)proto; + var nameProto = (NameIdentifierGroupPrototype)proto; // Only bother adding new ones. if (CurrentIds.ContainsKey(proto.ID)) continue; - var ids = GetOrCreateIdList(name_proto); - FillGroup(name_proto, ids); + var ids = GetOrCreateIdList(nameProto); + FillGroup(nameProto, ids); } } - private void CleanupIds(RoundRestartCleanupEvent ev) { EnsureIds(); diff --git a/Content.Shared/NameIdentifier/NameIdentifierComponent.cs b/Content.Shared/NameIdentifier/NameIdentifierComponent.cs index f9f9eef49b..c8b42d29db 100644 --- a/Content.Shared/NameIdentifier/NameIdentifierComponent.cs +++ b/Content.Shared/NameIdentifier/NameIdentifierComponent.cs @@ -9,6 +9,9 @@ namespace Content.Shared.NameIdentifier; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class NameIdentifierComponent : Component { + /// + /// The group prototype that defines the behavior of the identifier. + /// [DataField] public ProtoId? Group; diff --git a/Content.Shared/NameIdentifier/NameIdentifierGroupPrototype.cs b/Content.Shared/NameIdentifier/NameIdentifierGroupPrototype.cs index 933902e222..98e1c7a539 100644 --- a/Content.Shared/NameIdentifier/NameIdentifierGroupPrototype.cs +++ b/Content.Shared/NameIdentifier/NameIdentifierGroupPrototype.cs @@ -1,3 +1,4 @@ +using Content.Shared.Dataset; using Robust.Shared.Prototypes; namespace Content.Shared.NameIdentifier; @@ -14,6 +15,12 @@ public sealed partial class NameIdentifierGroupPrototype : IPrototype [DataField] public bool FullName = false; + /// + /// Should the identifier be a prefix, or a suffix? + /// + [DataField] + public bool Prefix = false; + /// /// Optional format identifier. If set, the name will be formatted using it (e.g., "MK-500"). /// If not set, only the numeric part will be used (e.g., "500"). @@ -32,4 +39,11 @@ public sealed partial class NameIdentifierGroupPrototype : IPrototype /// [DataField] public int MinValue = 0; + + /// + /// An optional field that will provide a list of name elements to pull from. + /// If non-null, and will be ignored. + /// + [DataField] + public ProtoId? IdentifierDataset; } diff --git a/Content.Shared/NameIdentifier/SharedNameIdentifierSystem.cs b/Content.Shared/NameIdentifier/SharedNameIdentifierSystem.cs index 068e416d77..fa30cf0b89 100644 --- a/Content.Shared/NameIdentifier/SharedNameIdentifierSystem.cs +++ b/Content.Shared/NameIdentifier/SharedNameIdentifierSystem.cs @@ -7,18 +7,16 @@ namespace Content.Shared.NameIdentifier; /// public abstract partial class SharedNameIdentifierSystem : EntitySystem { - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnRefreshNameModifiers); - } - + [SubscribeLocalEvent] private void OnRefreshNameModifiers(Entity ent, ref RefreshNameModifiersEvent args) { if (ent.Comp.Group is null) return; + // Don't add whitespace + if (string.IsNullOrWhiteSpace(ent.Comp.FullIdentifier)) + return; + // Don't apply the modifier if the component is being removed if (ent.Comp.LifeStage > ComponentLifeStage.Running) return; @@ -26,7 +24,11 @@ public abstract partial class SharedNameIdentifierSystem : EntitySystem if (!ProtoMan.Resolve(ent.Comp.Group, out var group)) return; - var format = group.FullName ? "name-identifier-format-full" : "name-identifier-format-append"; + var format = "name-identifier-format-full"; + + if (!group.FullName) + format = group.Prefix ? "name-identifier-format-prepend" : "name-identifier-format-append"; + // We apply the modifier with a low priority to keep it near the base name // "Beep (Si-4562) the zombie" instead of "Beep the zombie (Si-4562)" args.AddModifier(format, -10, ("identifier", ent.Comp.FullIdentifier)); diff --git a/Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs b/Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs index eef5261c01..2fb413a2ea 100644 --- a/Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs +++ b/Content.Shared/NameModifier/EntitySystems/NameModifierSystem.cs @@ -9,13 +9,7 @@ public sealed partial class NameModifierSystem : EntitySystem { [Dependency] private MetaDataSystem _metaData = default!; - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnEntityRenamed); - } - + [SubscribeLocalEvent] private void OnEntityRenamed(Entity ent, ref EntityRenamedEvent args) { SetBaseName(ent, args.NewName); @@ -135,7 +129,7 @@ public sealed class RefreshNameModifiersEvent : IInventoryRelayEvent /// public string GetModifiedName() { - // Start out with the entity's name name + // Start out with the entity's base name var name = BaseName; // Iterate through all the modifiers in priority order diff --git a/Resources/Locale/en-US/datasets/adjectives.ftl b/Resources/Locale/en-US/datasets/adjectives.ftl index be7eecfc59..3dc9175a5e 100644 --- a/Resources/Locale/en-US/datasets/adjectives.ftl +++ b/Resources/Locale/en-US/datasets/adjectives.ftl @@ -394,3 +394,29 @@ adjectives-dataset-393 = many adjectives-dataset-394 = numerous adjectives-dataset-395 = substantial adjectives-dataset-396 = capitalist +adjectives-dataset-397 = zippy +adjectives-dataset-398 = zesty +adjectives-dataset-399 = ravenous +adjectives-dataset-400 = abominable +adjectives-dataset-401 = man-eating +adjectives-dataset-402 = sly +adjectives-dataset-403 = aloof +adjectives-dataset-404 = sluggish +adjectives-dataset-405 = wise +adjectives-dataset-406 = noble +adjectives-dataset-407 = stealthy +adjectives-dataset-408 = cowardly +adjectives-dataset-409 = sniveling +adjectives-dataset-410 = wriggling +adjectives-dataset-411 = skittering +adjectives-dataset-412 = wretched +adjectives-dataset-413 = creeping +adjectives-dataset-414 = cunning +adjectives-dataset-415 = reticulated +adjectives-dataset-416 = snapping +adjectives-dataset-417 = giant +adjectives-dataset-418 = suspicious +adjectives-dataset-419 = sussy +adjectives-dataset-420 = blazed +adjectives-dataset-421 = curmudgeonly +adjectives-dataset-422 = pudgy diff --git a/Resources/Locale/en-US/name-identifier.ftl b/Resources/Locale/en-US/name-identifier.ftl index 31a3986d2b..847fe84a70 100644 --- a/Resources/Locale/en-US/name-identifier.ftl +++ b/Resources/Locale/en-US/name-identifier.ftl @@ -7,3 +7,4 @@ name-identifier-format-silicon = Si-{$number} name-identifier-format-xenoborg = Xi-{$number} name-identifier-format-station-ai = AI-{$number} name-identifier-format-telepad = TELE-{$number} +name-identifier-format-parens = ({$number}) diff --git a/Resources/Locale/en-US/name-identifier/name-identifier.ftl b/Resources/Locale/en-US/name-identifier/name-identifier.ftl index e0c05e51d3..532dacd991 100644 --- a/Resources/Locale/en-US/name-identifier/name-identifier.ftl +++ b/Resources/Locale/en-US/name-identifier/name-identifier.ftl @@ -1,2 +1,5 @@ name-identifier-format-append = {$baseName} {$identifier} +name-identifier-format-prepend = {$identifier} {$baseName} name-identifier-format-full = {$identifier} + +name-identifier-test-1 = TestValue diff --git a/Resources/Prototypes/Datasets/adjectives.yml b/Resources/Prototypes/Datasets/adjectives.yml index a0b5d2fc02..7c669a29af 100644 --- a/Resources/Prototypes/Datasets/adjectives.yml +++ b/Resources/Prototypes/Datasets/adjectives.yml @@ -2,4 +2,4 @@ id: Adjectives values: prefix: adjectives-dataset- - count: 396 + count: 422 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 068713b9b2..3c25d1a74a 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -560,6 +560,8 @@ weightlessAcceleration: 1.5 weightlessFriction: 1 baseWeightlessModifier: 1 + - type: NameIdentifier + group: AdjectivePrefix - type: Damageable damageModifierSet: Moth - type: Bloodstream @@ -2000,6 +2002,8 @@ - type: MovementSpeedModifier baseWalkSpeed : 2.5 baseSprintSpeed : 5 + - type: NameIdentifier + group: AdjectivePrefix - type: DamageStateVisuals states: Alive: diff --git a/Resources/Prototypes/name_identifier_groups.yml b/Resources/Prototypes/name_identifier_groups.yml index 1928e11281..e461fa8d58 100644 --- a/Resources/Prototypes/name_identifier_groups.yml +++ b/Resources/Prototypes/name_identifier_groups.yml @@ -1,4 +1,4 @@ -# Non-fungible apes, anyone? +# Non-fungible apes, anyone? - type: nameIdentifierGroup id: Monkey format: name-identifier-format-monkey @@ -48,6 +48,7 @@ # Used to suffix a number to any mob to identify player controlled mob griefing. - type: nameIdentifierGroup + format: name-identifier-format-parens id: GenericNumber - type: nameIdentifierGroup @@ -60,3 +61,8 @@ format: name-identifier-format-telepad minValue: 0 maxValue: 999 + +- type: nameIdentifierGroup + id: AdjectivePrefix + identifierDataset: Adjectives + prefix: true