mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 10:21:18 +03:00
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 <chuffine@gmail.com> * Typo and [SubscribeLocalEvent] Signed-off-by: Connor Huffine <chuffine@gmail.com> * Refactor GenerateUniqueName to GenerateUniqueNameModifier Signed-off-by: Connor Huffine <chuffine@gmail.com> * Fix returning name when empty Signed-off-by: Connor Huffine <chuffine@gmail.com> * Docs Signed-off-by: Connor Huffine <chuffine@gmail.com> * Tests Signed-off-by: Connor Huffine <chuffine@gmail.com> * Fix comment Signed-off-by: Connor Huffine <chuffine@gmail.com> * Fix FTL test Signed-off-by: Connor Huffine <chuffine@gmail.com> * Add adjectives Signed-off-by: Connor Huffine <chuffine@gmail.com> * Amateur hour Signed-off-by: Connor Huffine <chuffine@gmail.com> * Simplify spawning Signed-off-by: Connor Huffine <chuffine@gmail.com> * Even more streamlined Signed-off-by: Connor Huffine <chuffine@gmail.com> * Improve comment Signed-off-by: Connor Huffine <chuffine@gmail.com> * 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 <chuffine@gmail.com> Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com> Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com>
This commit is contained in:
parent
1807635fa0
commit
8b48748048
13 changed files with 416 additions and 57 deletions
|
|
@ -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!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
/// <remarks>
|
||||
/// We set <see cref="PoolSettings.Connected"/> false because the client has no unique behavior.
|
||||
/// Skipping client sim saves several seconds.
|
||||
/// </remarks>
|
||||
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<EntityUid>();
|
||||
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<EntityUid>();
|
||||
|
||||
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<MetaDataComponent>(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<MetaDataComponent>(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<MetaDataComponent>(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<MetaDataComponent>(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<MetaDataComponent>(single).EntityName,
|
||||
Does.Match(@"^\d+"),
|
||||
"Did not create a valid name with a prefix."
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Robust.Shared.Random;
|
|||
|
||||
namespace Content.Server.NameIdentifier;
|
||||
|
||||
/// <inheritdoc cref="SharedNameIdentifierSystem"/>
|
||||
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<string, List<int>> CurrentIds = [];
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a new unique name/suffix for a given entity and adds it to <see cref="CurrentIds"/>
|
||||
/// but does not set the entity's name.
|
||||
/// <inheritdoc cref="GenerateUniqueNameModifier(Content.Shared.NameIdentifier.NameIdentifierGroupPrototype,out int)" path="/summary"/>
|
||||
/// </summary>
|
||||
public string GenerateUniqueName(EntityUid uid, ProtoId<NameIdentifierGroupPrototype> proto, out int randomVal)
|
||||
/// <remarks>
|
||||
/// This overload resolves the ProtoId of the NameIdentifierGroupPrototype first.
|
||||
/// </remarks>
|
||||
/// <param name="proto">A ProtoId that will be resolved and passed on.</param>
|
||||
/// <param name="randomVal">The index value of the randomly selected modifier.</param>
|
||||
public string GenerateUniqueNameModifier(ProtoId<NameIdentifierGroupPrototype> proto, out int randomVal)
|
||||
{
|
||||
return GenerateUniqueName(uid, ProtoMan.Index(proto), out randomVal);
|
||||
return GenerateUniqueNameModifier(ProtoMan.Index(proto), out randomVal);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a new unique name/suffix for a given entity and adds it to <see cref="CurrentIds"/>
|
||||
/// but does not set the entity's name.
|
||||
/// Generates a new unique name modifier for a given entity and removes it from <see cref="CurrentIds"/>
|
||||
/// but does not set the entity's name.
|
||||
/// </summary>
|
||||
public string GenerateUniqueName(EntityUid uid, NameIdentifierGroupPrototype proto, out int randomVal)
|
||||
/// <param name="proto">The <see cref="NameIdentifierGroupPrototype"/> prototype to retrieve from.</param>
|
||||
/// <param name="randomVal">The index value of the randomly selected modifier.</param>
|
||||
/// <returns>A formatted and/or localized modifier. Empty string if invalid.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format and localize the provided integer against the prototype.
|
||||
/// </summary>
|
||||
/// <param name="value">The selected value to process.</param>
|
||||
/// <param name="proto">The prototype that defines optional localization and formatting.</param>
|
||||
/// <returns>A formatted and/or localized string.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="ent">The entity component tuple being initialized.</param>
|
||||
/// <param name="args">The arguments for the event. Unused.</param>
|
||||
private void OnMapInit(Entity<NameIdentifierComponent> 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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fill a provided list with a range of numbers corresponding to a prototype's defined range.
|
||||
/// </summary>
|
||||
/// <param name="proto">The <see cref="NameIdentifierGroupPrototype"/> prototype to retrieve from.</param>
|
||||
/// <param name="values">Reference to the list where values should be placed.</param>
|
||||
private void FillGroup(NameIdentifierGroupPrototype proto, List<int> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the appropriate list or create a new one if one does not already exist.
|
||||
/// </summary>
|
||||
/// <param name="proto">The <see cref="NameIdentifierGroupPrototype"/> prototype to retrieve from.</param>
|
||||
/// <returns>The list corresponding to the prototype, a new empty list if not already extant.</returns>
|
||||
private List<int> GetOrCreateIdList(NameIdentifierGroupPrototype proto)
|
||||
{
|
||||
if (!CurrentIds.TryGetValue(proto.ID, out var ids))
|
||||
{
|
||||
ids = new List<int>(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<int>(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();
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ namespace Content.Shared.NameIdentifier;
|
|||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||
public sealed partial class NameIdentifierComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The group prototype that defines the behavior of the identifier.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<NameIdentifierGroupPrototype>? Group;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Should the identifier be a prefix, or a suffix?
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Prefix = false;
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
|||
/// </summary>
|
||||
[DataField]
|
||||
public int MinValue = 0;
|
||||
|
||||
/// <summary>
|
||||
/// An optional field that will provide a list of name elements to pull from.
|
||||
/// If non-null, <see cref="MinValue"/> and <see cref="MaxValue"/> will be ignored.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ProtoId<LocalizedDatasetPrototype>? IdentifierDataset;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,18 +7,16 @@ namespace Content.Shared.NameIdentifier;
|
|||
/// </summary>
|
||||
public abstract partial class SharedNameIdentifierSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NameIdentifierComponent, RefreshNameModifiersEvent>(OnRefreshNameModifiers);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnRefreshNameModifiers(Entity<NameIdentifierComponent> 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));
|
||||
|
|
|
|||
|
|
@ -9,13 +9,7 @@ public sealed partial class NameModifierSystem : EntitySystem
|
|||
{
|
||||
[Dependency] private MetaDataSystem _metaData = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<NameModifierComponent, EntityRenamedEvent>(OnEntityRenamed);
|
||||
}
|
||||
|
||||
[SubscribeLocalEvent]
|
||||
private void OnEntityRenamed(Entity<NameModifierComponent> ent, ref EntityRenamedEvent args)
|
||||
{
|
||||
SetBaseName(ent, args.NewName);
|
||||
|
|
@ -135,7 +129,7 @@ public sealed class RefreshNameModifiersEvent : IInventoryRelayEvent
|
|||
/// </summary>
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@
|
|||
id: Adjectives
|
||||
values:
|
||||
prefix: adjectives-dataset-
|
||||
count: 396
|
||||
count: 422
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue