Enhance school creation functionality by introducing support for name sets in the API and UI. Update the catalog to include skills, traits, body attributes, needs, and name sets, improving character generation capabilities. Revise localization strings for better user guidance and update tests to validate the new name set functionality and ensure robustness in school creation processes.
This commit is contained in:
@@ -14,6 +14,7 @@ internal abstract record GameCommand
|
||||
DateTime StartDate,
|
||||
IReadOnlyList<string>? ExtraModIds,
|
||||
MapLayout? Map,
|
||||
string? NameSetId,
|
||||
TaskCompletionSource<SchoolCreationOutcome> Result) : GameCommand;
|
||||
|
||||
internal sealed record DeleteSchool(int SchoolId, TaskCompletionSource<bool> Result) : GameCommand;
|
||||
|
||||
@@ -216,10 +216,28 @@ internal sealed class GameLoopService(
|
||||
return;
|
||||
}
|
||||
|
||||
DefCatalog catalog;
|
||||
try
|
||||
{
|
||||
catalog = mods.LoadCatalog(packIds);
|
||||
}
|
||||
catch (Exception ex) when (ex is ContentLoadException or SchoolContentUnavailableException)
|
||||
{
|
||||
command.Result.TrySetResult(new SchoolCreationOutcome(null, SchoolCreationError.InvalidCatalog));
|
||||
return;
|
||||
}
|
||||
|
||||
var nameSetId = ResolveNameSetId(catalog, command.NameSetId);
|
||||
if (nameSetId is null)
|
||||
{
|
||||
command.Result.TrySetResult(new SchoolCreationOutcome(null, SchoolCreationError.UnknownNameSet));
|
||||
return;
|
||||
}
|
||||
|
||||
var id = _nextId++;
|
||||
store.WriteNextId(_nextId);
|
||||
|
||||
var worker = SpawnWorker(id, normalized, command.StartDate, running: true, ClockSpeed.DefaultIndex, isNew: true, packIds, command.Map);
|
||||
var worker = SpawnWorker(id, normalized, command.StartDate, running: true, ClockSpeed.DefaultIndex, isNew: true, packIds, command.Map, nameSetId);
|
||||
Track(worker);
|
||||
worker.Start();
|
||||
|
||||
@@ -385,7 +403,8 @@ internal sealed class GameLoopService(
|
||||
save.SpeedIndex,
|
||||
isNew: false,
|
||||
save.ModIds,
|
||||
save.Map);
|
||||
save.Map,
|
||||
save.NameSetId);
|
||||
worker.Start();
|
||||
|
||||
try
|
||||
@@ -431,7 +450,8 @@ internal sealed class GameLoopService(
|
||||
int speedIndex,
|
||||
bool isNew,
|
||||
IReadOnlyList<string>? modIds,
|
||||
MapLayout? map) =>
|
||||
MapLayout? map,
|
||||
string? nameSetId) =>
|
||||
new(
|
||||
id,
|
||||
name,
|
||||
@@ -441,6 +461,7 @@ internal sealed class GameLoopService(
|
||||
isNew,
|
||||
modIds,
|
||||
map,
|
||||
nameSetId,
|
||||
_options,
|
||||
clients,
|
||||
metrics,
|
||||
@@ -449,6 +470,30 @@ internal sealed class GameLoopService(
|
||||
onFailed: schoolId => commands.Enqueue(new GameCommand.WorkerFailed(schoolId)),
|
||||
loggerFactory.CreateLogger($"HSchool.Server.Game.SchoolWorker.{id}"));
|
||||
|
||||
/// <summary>
|
||||
/// Empty request uses the first placeable set (core's Slavic). A named id must exist in the
|
||||
/// catalog already loaded for this pack list — unknown extras were rejected above.
|
||||
/// </summary>
|
||||
internal static string? ResolveNameSetId(DefCatalog catalog, string? requested)
|
||||
{
|
||||
var available = catalog.NameSets.Values
|
||||
.Where(def => !def.Abstract)
|
||||
.Select(def => def.DefName)
|
||||
.OrderBy(name => name, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
if (available.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(requested))
|
||||
{
|
||||
return available[0];
|
||||
}
|
||||
|
||||
return available.Contains(requested, StringComparer.Ordinal) ? requested : null;
|
||||
}
|
||||
|
||||
private void Track(SchoolWorker worker)
|
||||
{
|
||||
_workers[worker.Id] = worker;
|
||||
|
||||
@@ -23,6 +23,8 @@ internal sealed class SchoolSave
|
||||
public IReadOnlyList<string>? ModIds { get; init; }
|
||||
|
||||
public MapLayout? Map { get; init; }
|
||||
|
||||
public string? NameSetId { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>Allocates school ids that survive a process restart.</summary>
|
||||
@@ -152,6 +154,7 @@ internal sealed class SchoolStore
|
||||
SpeedIndex = save.SpeedIndex,
|
||||
ModIds = save.ModIds,
|
||||
Map = save.Map,
|
||||
NameSetId = save.NameSetId,
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -29,6 +29,7 @@ internal sealed class SchoolWorker
|
||||
private readonly bool _isNew;
|
||||
private readonly IReadOnlyList<string>? _modIds;
|
||||
private readonly MapLayout? _savedMap;
|
||||
private readonly string? _nameSetId;
|
||||
private readonly Action<int> _onFailed;
|
||||
|
||||
private readonly int _id;
|
||||
@@ -53,6 +54,7 @@ internal sealed class SchoolWorker
|
||||
bool isNew,
|
||||
IReadOnlyList<string>? modIds,
|
||||
MapLayout? savedMap,
|
||||
string? nameSetId,
|
||||
SimulationOptions options,
|
||||
ClientRegistry clients,
|
||||
GameMetrics metrics,
|
||||
@@ -69,6 +71,7 @@ internal sealed class SchoolWorker
|
||||
_isNew = isNew;
|
||||
_modIds = modIds;
|
||||
_savedMap = savedMap;
|
||||
_nameSetId = nameSetId;
|
||||
_options = options;
|
||||
_clients = clients;
|
||||
_metrics = metrics;
|
||||
@@ -411,6 +414,7 @@ internal sealed class SchoolWorker
|
||||
SpeedIndex = school.Clock.SpeedIndex,
|
||||
ModIds = school.Catalog?.PackIds,
|
||||
Map = school.Map,
|
||||
NameSetId = _nameSetId,
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
Reference in New Issue
Block a user