Add loadout names (#31303)

* Add loadout names

Did it for AI, breaking change for pgsql + migrations in general. Nothing atm uses it.

* the box

* Spawning cherry pick

* Fix nit

* revert

* Final cleanup

* Real

* Name UI fix

* Migrations

* a

* Review

* Re-run migrations

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
This commit is contained in:
metalgearsloth
2025-02-12 04:30:24 +11:00
committed by GitHub
parent c197d3db37
commit 15b28936df
17 changed files with 4314 additions and 5 deletions

View File

@@ -1015,6 +1015,13 @@ namespace Content.Client.Lobby.UI
_loadoutWindow.RefreshLoadouts(roleLoadout, session, collection);
_loadoutWindow.OpenCenteredLeft();
_loadoutWindow.OnNameChanged += name =>
{
roleLoadout.EntityName = name;
Profile = Profile.WithLoadout(roleLoadout);
SetDirty();
};
_loadoutWindow.OnLoadoutPressed += (loadoutGroup, loadoutProto) =>
{
roleLoadout.AddLoadout(loadoutGroup, loadoutProto, _prototypeManager);

View File

@@ -5,17 +5,15 @@
SetSize="800 800"
MinSize="800 128">
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<!--
<BoxContainer Name="RoleNameBox" Orientation="Vertical" Margin="10">
<Label Name="LoadoutNameLabel" Text="{Loc 'loadout-name-edit-label'}"/>
<PanelContainer HorizontalExpand="True" SetHeight="24">
<PanelContainer.PanelOverride>
<graphics:StyleBoxFlat BackgroundColor="#1B1B1E" />
</PanelContainer.PanelOverride>
<LineEdit Name="RoleNameEdit" ToolTip="{Loc 'loadout-name-edit-tooltip'}" VerticalExpand="True" HorizontalExpand="True"/>
<LineEdit Name="RoleNameEdit" VerticalExpand="True" HorizontalExpand="True"/>
</PanelContainer>
</BoxContainer>
-->
<VerticalTabContainer Name="LoadoutGroupsContainer"
VerticalExpand="True"
HorizontalExpand="True">

View File

@@ -1,7 +1,9 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Dataset;
using Content.Shared.Preferences;
using Content.Shared.Preferences.Loadouts;
using Content.Shared.Random.Helpers;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Player;
@@ -13,6 +15,7 @@ namespace Content.Client.Lobby.UI.Loadouts;
[GenerateTypedNameReferences]
public sealed partial class LoadoutWindow : FancyWindow
{
public event Action<string>? OnNameChanged;
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutUnpressed;
@@ -25,6 +28,23 @@ public sealed partial class LoadoutWindow : FancyWindow
RobustXamlLoader.Load(this);
Profile = profile;
var protoManager = collection.Resolve<IPrototypeManager>();
RoleNameEdit.IsValid = text => text.Length <= HumanoidCharacterProfile.MaxLoadoutNameLength;
// Hide if we can't edit the name.
if (!proto.CanCustomizeName)
{
RoleNameBox.Visible = false;
}
else
{
var name = loadout.EntityName;
RoleNameEdit.ToolTip = Loc.GetString(
"loadout-name-edit-tooltip",
("max", HumanoidCharacterProfile.MaxLoadoutNameLength));
RoleNameEdit.Text = name ?? string.Empty;
RoleNameEdit.OnTextChanged += args => OnNameChanged?.Invoke(args.Text);
}
// Hide if no groups
if (proto.Groups.Count == 0)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Postgres
{
/// <inheritdoc />
public partial class LoadoutNames : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "entity_name",
table: "profile_role_loadout",
type: "character varying(256)",
maxLength: 256,
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "entity_name",
table: "profile_role_loadout");
}
}
}

View File

@@ -975,6 +975,11 @@ namespace Content.Server.Database.Migrations.Postgres
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("EntityName")
.HasMaxLength(256)
.HasColumnType("character varying(256)")
.HasColumnName("entity_name");
b.Property<int>("ProfileId")
.HasColumnType("integer")
.HasColumnName("profile_id");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,29 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Sqlite
{
/// <inheritdoc />
public partial class LoadoutNames : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "entity_name",
table: "profile_role_loadout",
type: "TEXT",
maxLength: 256,
nullable: true);
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "entity_name",
table: "profile_role_loadout");
}
}
}

View File

@@ -921,6 +921,11 @@ namespace Content.Server.Database.Migrations.Sqlite
.HasColumnType("INTEGER")
.HasColumnName("profile_role_loadout_id");
b.Property<string>("EntityName")
.HasMaxLength(256)
.HasColumnType("TEXT")
.HasColumnName("entity_name");
b.Property<int>("ProfileId")
.HasColumnType("INTEGER")
.HasColumnName("profile_id");

View File

@@ -480,6 +480,12 @@ namespace Content.Server.Database
/// </summary>
public string RoleName { get; set; } = string.Empty;
/// <summary>
/// Custom name of the role loadout if it supports it.
/// </summary>
[MaxLength(256)]
public string? EntityName { get; set; }
/// <summary>
/// Store the saved loadout groups. These may get validated and removed when loaded at runtime.
/// </summary>

View File

@@ -222,6 +222,7 @@ namespace Content.Server.Database
{
var loadout = new RoleLoadout(role.RoleName)
{
EntityName = role.EntityName,
};
foreach (var group in role.Groups)
@@ -319,6 +320,7 @@ namespace Content.Server.Database
var dz = new ProfileRoleLoadout()
{
RoleName = role,
EntityName = loadouts.EntityName ?? string.Empty,
};
foreach (var (group, groupLoadouts) in loadouts.SelectedLoadouts)

View File

@@ -29,6 +29,7 @@ namespace Content.Shared.Preferences
private static readonly Regex ICNameCaseRegex = new(@"^(?<word>\w)|\b(?<word>\w)(?=\w*$)");
public const int MaxNameLength = 32;
public const int MaxLoadoutNameLength = 32;
public const int MaxDescLength = 512;
/// <summary>

View File

@@ -22,6 +22,11 @@ public sealed partial class RoleLoadout : IEquatable<RoleLoadout>
[DataField]
public Dictionary<ProtoId<LoadoutGroupPrototype>, List<Loadout>> SelectedLoadouts = new();
/// <summary>
/// Loadout specific name.
/// </summary>
public string? EntityName;
/*
* Loadout-specific data used for validation.
*/
@@ -42,6 +47,8 @@ public sealed partial class RoleLoadout : IEquatable<RoleLoadout>
weh.SelectedLoadouts.Add(selected.Key, new List<Loadout>(selected.Value));
}
weh.EntityName = EntityName;
return weh;
}
@@ -55,10 +62,34 @@ public sealed partial class RoleLoadout : IEquatable<RoleLoadout>
if (!protoManager.TryIndex(Role, out var roleProto))
{
EntityName = null;
SelectedLoadouts.Clear();
return;
}
// Remove name not allowed.
if (!roleProto.CanCustomizeName)
{
EntityName = null;
}
// Validate name length
// TODO: Probably allow regex to be supplied?
if (EntityName != null)
{
var name = EntityName.Trim();
if (name.Length > HumanoidCharacterProfile.MaxNameLength)
{
EntityName = name[..HumanoidCharacterProfile.MaxNameLength];
}
if (name.Length == 0)
{
EntityName = null;
}
}
// In some instances we might not have picked up a new group for existing data.
foreach (var groupProto in roleProto.Groups)
{
@@ -322,7 +353,8 @@ public sealed partial class RoleLoadout : IEquatable<RoleLoadout>
if (!Role.Equals(other.Role) ||
SelectedLoadouts.Count != other.SelectedLoadouts.Count ||
Points != other.Points)
Points != other.Points ||
EntityName != other.EntityName)
{
return false;
}

View File

@@ -16,6 +16,12 @@ public sealed partial class RoleLoadoutPrototype : IPrototype
[IdDataField]
public string ID { get; } = string.Empty;
/// <summary>
/// Can the user edit their entity name for this role loadout?
/// </summary>
[DataField]
public bool CanCustomizeName;
/// <summary>
/// Should we use a random name for this loadout?
/// </summary>

View File

@@ -68,6 +68,11 @@ public abstract class SharedStationSpawningSystem : EntitySystem
{
string? name = null;
if (roleProto.CanCustomizeName)
{
name = loadout.EntityName;
}
if (string.IsNullOrEmpty(name) && PrototypeManager.TryIndex(roleProto.NameDataset, out var nameData))
{
name = Loc.GetString(_random.Pick(nameData.Values));

View File

@@ -1,6 +1,6 @@
# Name
loadout-name-edit-label = Custom name
loadout-name-edit-tooltip = 32 characters max. If no name is specified a random one may be chosen for you.
loadout-name-edit-tooltip = {$max} characters max. If no name is specified a random one may be chosen for you.
# Restrictions
loadout-restrictions = Restrictions

View File

@@ -26,9 +26,15 @@
- GroupSpeciesBreathTool
# Silicons
#- type: roleLoadout
# id: JobBorg
# nameDataset: roleloadout doesn't support both so need to update that first.
# canCustomizeName: true
- type: roleLoadout
id: JobStationAi
nameDataset: NamesAI
canCustomizeName: true
# Civilian
- type: roleLoadout