Add player onboarding and tactical viewer updates
This commit is contained in:
@@ -10,6 +10,7 @@ namespace SpaceGame.Api.Universe.Simulation;
|
||||
public sealed class WorldService
|
||||
{
|
||||
private const int DeltaHistoryLimit = 256;
|
||||
private const string StarterPlayerShipId = "ship_arg_s_scout_01_a";
|
||||
|
||||
private readonly Lock _sync = new();
|
||||
private readonly OrbitalSimulationSnapshot _orbitalSimulation;
|
||||
@@ -148,11 +149,6 @@ public sealed class WorldService
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
if (_world.Factions.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var playerKey = GetCurrentPlayerKey();
|
||||
var player = _playerFaction.TryGetDomain(_playerStateStore, playerKey)
|
||||
?? _playerFaction.EnsureDomain(_world, _playerStateStore, playerKey);
|
||||
@@ -160,6 +156,26 @@ public sealed class WorldService
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerFactionSnapshot? CompletePlayerOnboarding(CompletePlayerOnboardingRequest request)
|
||||
{
|
||||
lock (_sync)
|
||||
{
|
||||
if (!_staticData.RaceDefinitions.TryGetValue(request.RaceId.Trim(), out var race))
|
||||
{
|
||||
throw new InvalidOperationException($"Race '{request.RaceId}' is not defined in static data.");
|
||||
}
|
||||
|
||||
var playerKey = GetCurrentPlayerKey();
|
||||
var player = _playerFaction.CompleteOnboarding(_world, _playerStateStore, playerKey, request);
|
||||
var playerFaction = CreatePlayerOwnedFactionUnsafe(player, race);
|
||||
var starterSystemId = ResolveStarterSystemIdUnsafe();
|
||||
SpawnPlayerStarterShipUnsafe(playerFaction, starterSystemId);
|
||||
_playerFaction.EnsureInitializedDomain(_world, _playerStateStore, playerKey);
|
||||
PublishSnapshotRefreshUnsafe("player-onboarding", $"Initialized player {player.PersonaName}", "faction", playerFaction.Id);
|
||||
return GetPlayerFactionSnapshotUnsafe();
|
||||
}
|
||||
}
|
||||
|
||||
public PlayerFactionSnapshot? CreatePlayerOrganization(PlayerOrganizationCommandRequest request)
|
||||
{
|
||||
lock (_sync)
|
||||
@@ -530,7 +546,102 @@ public sealed class WorldService
|
||||
private PlayerFactionSnapshot? GetPlayerFactionSnapshotUnsafe() =>
|
||||
_playerFactionProjection.ToSnapshot(_playerFaction.TryGetDomain(_playerStateStore, GetCurrentPlayerKey()));
|
||||
|
||||
private string GetCurrentPlayerKey() => _playerIdentityResolver.GetRequiredPlayerId().ToString("N");
|
||||
private FactionRuntime CreatePlayerOwnedFactionUnsafe(PlayerFactionRuntime player, RaceDefinition race)
|
||||
{
|
||||
var playerFaction = new FactionRuntime
|
||||
{
|
||||
Id = player.SovereignFactionId,
|
||||
Label = player.PersonaName ?? player.Label,
|
||||
Color = ResolvePlayerFactionColor(race.Id),
|
||||
Credits = 25000f,
|
||||
};
|
||||
|
||||
_world.Factions.Add(playerFaction);
|
||||
|
||||
var policy = _worldSeedingService.CreatePolicies([playerFaction]).Single();
|
||||
var templateFaction = _staticData.FactionDefinitions.Values
|
||||
.Where(candidate => string.Equals(candidate.RaceId, race.Id, StringComparison.Ordinal))
|
||||
.OrderBy(candidate => candidate.Id, StringComparer.Ordinal)
|
||||
.Select(candidate => _world.Factions.FirstOrDefault(worldFaction => string.Equals(worldFaction.Id, candidate.Id, StringComparison.Ordinal)))
|
||||
.FirstOrDefault(candidate => candidate is not null);
|
||||
if (templateFaction?.DefaultPolicySetId is { } racePolicyId
|
||||
&& _world.Policies.FirstOrDefault(candidate => candidate.Id == racePolicyId) is { } racePolicy)
|
||||
{
|
||||
policy.TradeAccessPolicy = racePolicy.TradeAccessPolicy;
|
||||
policy.DockingAccessPolicy = racePolicy.DockingAccessPolicy;
|
||||
policy.ConstructionAccessPolicy = racePolicy.ConstructionAccessPolicy;
|
||||
policy.OperationalRangePolicy = racePolicy.OperationalRangePolicy;
|
||||
policy.CombatEngagementPolicy = racePolicy.CombatEngagementPolicy;
|
||||
policy.FleeHullRatio = racePolicy.FleeHullRatio;
|
||||
policy.AvoidHostileSystems = racePolicy.AvoidHostileSystems;
|
||||
foreach (var systemId in racePolicy.BlacklistedSystemIds)
|
||||
{
|
||||
policy.BlacklistedSystemIds.Add(systemId);
|
||||
}
|
||||
}
|
||||
_world.Policies.Add(policy);
|
||||
|
||||
var factionCommander = CreateFactionCommander(playerFaction);
|
||||
_world.Commanders.Add(factionCommander);
|
||||
playerFaction.CommanderIds.Add(factionCommander.Id);
|
||||
return playerFaction;
|
||||
}
|
||||
|
||||
private string ResolveStarterSystemIdUnsafe()
|
||||
{
|
||||
return _world.Systems
|
||||
.Select(system => system.Definition.Id)
|
||||
.OrderBy(systemId => systemId, StringComparer.Ordinal)
|
||||
.FirstOrDefault()
|
||||
?? throw new InvalidOperationException("No systems are available for player onboarding.");
|
||||
}
|
||||
|
||||
private void SpawnPlayerStarterShipUnsafe(FactionRuntime playerFaction, string systemId)
|
||||
{
|
||||
var request = new SpawnShipCommandRequest(
|
||||
playerFaction.Id,
|
||||
systemId,
|
||||
StarterPlayerShipId,
|
||||
Idle);
|
||||
var system = _world.Systems.First(candidate => string.Equals(candidate.Definition.Id, request.SystemId, StringComparison.Ordinal));
|
||||
var definition = ResolveShipDefinition(request, playerFaction.Id);
|
||||
var shipId = $"ship-{playerFaction.Id}-{definition.Id}-{Guid.NewGuid():N}".ToLowerInvariant();
|
||||
var spawnPosition = ResolveSpawnPosition(system.Definition.Id);
|
||||
var defaultBehavior = CreateSpawnBehavior(request, definition, system.Definition.Id, null);
|
||||
|
||||
var ship = new ShipRuntime
|
||||
{
|
||||
Id = shipId,
|
||||
SystemId = system.Definition.Id,
|
||||
Definition = definition,
|
||||
FactionId = playerFaction.Id,
|
||||
Position = spawnPosition,
|
||||
TargetPosition = spawnPosition,
|
||||
SpatialState = SpatialBuilder.CreateInitialShipSpatialState(system.Definition.Id, spawnPosition, _world.Celestials),
|
||||
DefaultBehavior = defaultBehavior,
|
||||
Skills = ShipBootstrapPolicy.CreateSkills(definition),
|
||||
Health = definition.Hull,
|
||||
};
|
||||
|
||||
_world.Ships.Add(ship);
|
||||
EnsureShipCommander(playerFaction, ship);
|
||||
new GeopoliticalSimulationService().Update(_world, 0f, []);
|
||||
}
|
||||
|
||||
private string ResolvePlayerFactionColor(string raceId) =>
|
||||
raceId switch
|
||||
{
|
||||
"argon" => "#3b82f6",
|
||||
"boron" => "#14b8a6",
|
||||
"paranid" => "#eab308",
|
||||
"split" => "#b91c1c",
|
||||
"teladi" => "#22c55e",
|
||||
"terran" => "#38bdf8",
|
||||
"xenon" => "#9ca3af",
|
||||
_ => "#94a3b8",
|
||||
};
|
||||
|
||||
private string GetCurrentPlayerKey() => _playerIdentityResolver.GetRequiredEffectivePlayerId().ToString("N");
|
||||
|
||||
private bool CanCurrentActorAccessGm() => _playerIdentityResolver.CanAccessGm();
|
||||
|
||||
@@ -807,7 +918,8 @@ public sealed class WorldService
|
||||
powerModuleId,
|
||||
factionId,
|
||||
_staticData.ModuleDefinitions,
|
||||
_staticData.ItemDefinitions)
|
||||
_staticData.ItemDefinitions,
|
||||
_staticData.Recipes)
|
||||
.FirstOrDefault(moduleId =>
|
||||
{
|
||||
return _staticData.ModuleDefinitions.TryGetValue(moduleId, out var definition)
|
||||
@@ -820,6 +932,24 @@ public sealed class WorldService
|
||||
EnsureStationModule(modules, defaultContainerStorageModuleId);
|
||||
}
|
||||
|
||||
var defaultSolidStorageModuleId = StarterStationLayoutResolver.ResolveRequiredStorageModuleIds(
|
||||
powerModuleId,
|
||||
factionId,
|
||||
_staticData.ModuleDefinitions,
|
||||
_staticData.ItemDefinitions,
|
||||
_staticData.Recipes)
|
||||
.FirstOrDefault(moduleId =>
|
||||
{
|
||||
return _staticData.ModuleDefinitions.TryGetValue(moduleId, out var definition)
|
||||
&& definition is StorageModuleDefinition storageDefinition
|
||||
&& storageDefinition.StorageKind == StorageKind.Solid;
|
||||
});
|
||||
|
||||
if (defaultSolidStorageModuleId is not null)
|
||||
{
|
||||
EnsureStationModule(modules, defaultSolidStorageModuleId);
|
||||
}
|
||||
|
||||
var objectiveModuleId = StarterStationLayoutResolver.ResolveObjectiveModuleId(objective, factionId, _staticData.ModuleDefinitions);
|
||||
if (!string.IsNullOrWhiteSpace(objectiveModuleId))
|
||||
{
|
||||
@@ -828,7 +958,8 @@ public sealed class WorldService
|
||||
objectiveModuleId,
|
||||
factionId,
|
||||
_staticData.ModuleDefinitions,
|
||||
_staticData.ItemDefinitions))
|
||||
_staticData.ItemDefinitions,
|
||||
_staticData.Recipes))
|
||||
{
|
||||
EnsureStationModule(modules, storageModuleId);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user