Refactor stations to properly use entity prototypes. (stationsv3) (#16570)
* Update StationSpawningSystem.cs Web-edit to allow feeding in an existing entity. * Update StationSpawningSystem.cs value type moment * Update StationSpawningSystem.cs * Oh goddamnit this is a refactor now. * awawawa * aaaaaaaaaaa * ee * forgot records. * no records? no records. * What's in a name? * Sloth forcing me to do the refactor properly smh. * e * optional evac in test. * tests pls work * awa --------- Co-authored-by: moonheart08 <moonheart08@users.noreply.github.com>
This commit is contained in:
@@ -1,15 +0,0 @@
|
||||
using Content.Server.GameTicking;
|
||||
|
||||
namespace Content.Server.Station.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Added to grids saved in maps to designate them as 'part of a station' and not main grids. I.e. ancillary
|
||||
/// shuttles for multi-grid stations.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(GameTicker)), Obsolete("Performs the exact same function as BecomesStationComponent.")]
|
||||
public sealed class PartOfStationComponent : Component
|
||||
{
|
||||
[DataField("id", required: true)]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Id = default!;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using Content.Server.Shuttles.Systems;
|
||||
using Content.Server.Station.Systems;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Station.Components;
|
||||
|
||||
@@ -19,15 +21,6 @@ public sealed class StationDataComponent : Component
|
||||
/// <summary>
|
||||
/// List of all grids this station is part of.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// You should not mutate this yourself, go through StationSystem so the appropriate events get fired.
|
||||
/// </remarks>
|
||||
[DataField("grids")]
|
||||
public readonly HashSet<EntityUid> Grids = new();
|
||||
|
||||
/// <summary>
|
||||
/// The emergency shuttle assigned to this station.
|
||||
/// </summary>
|
||||
[ViewVariables, Access(typeof(ShuttleSystem), typeof(EmergencyShuttleSystem), Friend = AccessPermissions.ReadWrite)]
|
||||
public EntityUid? EmergencyShuttle;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.Station.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for event eligibility.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class StationEventEligibleComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
@@ -32,6 +32,16 @@ public sealed class StationJobsComponent : Component
|
||||
/// </summary>
|
||||
[DataField("extendedAccess")] public bool ExtendedAccess;
|
||||
|
||||
/// <summary>
|
||||
/// If there are less than or equal this amount of players in the game at round start,
|
||||
/// people get extended access levels from job prototypes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Set to -1 to disable extended access.
|
||||
/// </remarks>
|
||||
[DataField("extendedAccessThreshold")]
|
||||
public int ExtendedAccessThreshold { get; set; } = 15;
|
||||
|
||||
/// <summary>
|
||||
/// The percentage of jobs remaining.
|
||||
/// </summary>
|
||||
@@ -62,5 +72,10 @@ public sealed class StationJobsComponent : Component
|
||||
/// <summary>
|
||||
/// Overflow jobs that round-start can spawn infinitely many of.
|
||||
/// </summary>
|
||||
[DataField("overflowJobs", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<JobPrototype>))] public HashSet<string> OverflowJobs = new();
|
||||
[DataField("overflowJobs", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<JobPrototype>))]
|
||||
public HashSet<string> OverflowJobs = new();
|
||||
|
||||
[DataField("availableJobs", required: true,
|
||||
customTypeSerializer: typeof(PrototypeIdDictionarySerializer<List<int?>, JobPrototype>))]
|
||||
public readonly Dictionary<string, List<int?>> SetupAvailableJobs = default!;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using Content.Server.Maps.NameGenerators;
|
||||
|
||||
namespace Content.Server.Station.Components;
|
||||
|
||||
/// <summary>
|
||||
/// This is used for setting up a station's name.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed class StationNameSetupComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The name template to use for the station.
|
||||
/// If there's a name generator this should follow it's required format.
|
||||
/// </summary>
|
||||
[DataField("mapNameTemplate", required: true)]
|
||||
public string StationNameTemplate { get; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Name generator to use for the station, if any.
|
||||
/// </summary>
|
||||
[DataField("nameGenerator")]
|
||||
public StationNameGenerator? NameGenerator { get; }
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||
|
||||
namespace Content.Server.Station;
|
||||
|
||||
public sealed partial class StationConfig
|
||||
{
|
||||
[DataField("overflowJobs", required: true, customTypeSerializer: typeof(PrototypeIdListSerializer<JobPrototype>))]
|
||||
private readonly List<string> _overflowJobs = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Jobs used at round start should the station run out of job slots.
|
||||
/// Doesn't necessarily mean the station has infinite slots for the given jobs mid-round!
|
||||
/// </summary>
|
||||
public IReadOnlyList<string> OverflowJobs => _overflowJobs;
|
||||
|
||||
|
||||
[DataField("availableJobs", required: true,
|
||||
customTypeSerializer: typeof(PrototypeIdDictionarySerializer<List<int?>, JobPrototype>))]
|
||||
private readonly Dictionary<string, List<int?>> _availableJobs = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Index of all jobs available on the station, of form
|
||||
/// job name: [round-start, mid-round]
|
||||
/// </summary>
|
||||
public IReadOnlyDictionary<string, List<int?>> AvailableJobs => _availableJobs;
|
||||
|
||||
/// <summary>
|
||||
/// If there are less than or equal this amount of players in the game at round start,
|
||||
/// people get extended access levels from job prototypes.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Set to -1 to disable extended access.
|
||||
/// </remarks>
|
||||
[DataField("extendedAccessThreshold")]
|
||||
public int ExtendedAccessThreshold { get; set; } = 15;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations;
|
||||
|
||||
namespace Content.Server.Station;
|
||||
|
||||
public sealed partial class StationConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Emergency shuttle map path for this station.
|
||||
/// </summary>
|
||||
[DataField("emergencyShuttlePath", customTypeSerializer: typeof(ResPathSerializer))]
|
||||
public ResPath EmergencyShuttlePath { get; set; } = new("/Maps/Shuttles/emergency.yml");
|
||||
}
|
||||
@@ -1,30 +1,19 @@
|
||||
using Content.Server.Maps.NameGenerators;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Station;
|
||||
|
||||
/// <summary>
|
||||
/// A config for a station. Specifies name and job slots.
|
||||
/// This is the only part of stations a downstream should ideally need to modify directly.
|
||||
/// A config for a station. Specifies name and component modifications.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Forks should not directly edit existing parts of this class.
|
||||
/// Make a new partial for your fancy new feature, it'll save you time later.
|
||||
/// </remarks>
|
||||
[DataDefinition, PublicAPI]
|
||||
public sealed partial class StationConfig
|
||||
public sealed class StationConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The name template to use for the station.
|
||||
/// If there's a name generator this should follow it's required format.
|
||||
/// </summary>
|
||||
[DataField("mapNameTemplate", required: true)]
|
||||
public string StationNameTemplate { get; } = default!;
|
||||
[DataField("stationProto", required: true)]
|
||||
public string StationPrototype = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Name generator to use for the station, if any.
|
||||
/// </summary>
|
||||
[DataField("nameGenerator")]
|
||||
public StationNameGenerator? NameGenerator { get; }
|
||||
[DataField("components", required: true)]
|
||||
public ComponentRegistry StationComponentOverrides = default!;
|
||||
}
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ public sealed partial class StationJobsSystem
|
||||
assignedJobs.Add(player, (null, EntityUid.Invalid));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
_random.Shuffle(givenStations);
|
||||
|
||||
foreach (var station in givenStations)
|
||||
@@ -318,9 +318,8 @@ public sealed partial class StationJobsSystem
|
||||
foreach (var (station, count) in jobsCount)
|
||||
{
|
||||
var jobs = Comp<StationJobsComponent>(station);
|
||||
var data = Comp<StationDataComponent>(station);
|
||||
|
||||
var thresh = data.StationConfig?.ExtendedAccessThreshold ?? -1;
|
||||
var thresh = jobs.ExtendedAccessThreshold;
|
||||
|
||||
jobs.ExtendedAccess = count <= thresh;
|
||||
|
||||
|
||||
@@ -53,30 +53,32 @@ public sealed partial class StationJobsSystem : EntitySystem
|
||||
|
||||
private void OnStationInitialized(StationInitializedEvent msg)
|
||||
{
|
||||
var stationJobs = AddComp<StationJobsComponent>(msg.Station);
|
||||
var stationData = Comp<StationDataComponent>(msg.Station);
|
||||
|
||||
if (stationData.StationConfig == null)
|
||||
if (!TryComp<StationJobsComponent>(msg.Station, out var stationJobs))
|
||||
return;
|
||||
|
||||
var mapJobList = stationData.StationConfig.AvailableJobs;
|
||||
var mapJobList = stationJobs.SetupAvailableJobs;
|
||||
|
||||
stationJobs.RoundStartTotalJobs = mapJobList.Values.Where(x => x[0] is not null && x[0] > 0).Sum(x => x[0]!.Value);
|
||||
stationJobs.MidRoundTotalJobs = mapJobList.Values.Where(x => x[1] is not null && x[1] > 0).Sum(x => x[1]!.Value);
|
||||
|
||||
stationJobs.TotalJobs = stationJobs.MidRoundTotalJobs;
|
||||
|
||||
stationJobs.JobList = mapJobList.ToDictionary(x => x.Key, x =>
|
||||
{
|
||||
if (x.Value[1] <= -1)
|
||||
return null;
|
||||
return (uint?) x.Value[1];
|
||||
});
|
||||
|
||||
stationJobs.RoundStartJobList = mapJobList.ToDictionary(x => x.Key, x =>
|
||||
{
|
||||
if (x.Value[0] <= -1)
|
||||
return null;
|
||||
return (uint?) x.Value[0];
|
||||
});
|
||||
stationJobs.OverflowJobs = stationData.StationConfig.OverflowJobs.ToHashSet();
|
||||
|
||||
stationJobs.OverflowJobs = stationJobs.OverflowJobs.ToHashSet();
|
||||
|
||||
UpdateJobsAvailable();
|
||||
}
|
||||
|
||||
@@ -464,9 +466,11 @@ public sealed partial class StationJobsSystem : EntitySystem
|
||||
var jobs = new Dictionary<EntityUid, Dictionary<string, uint?>>();
|
||||
var stationNames = new Dictionary<EntityUid, string>();
|
||||
|
||||
foreach (var station in _stationSystem.Stations)
|
||||
var query = EntityQueryEnumerator<StationJobsComponent>();
|
||||
|
||||
while (query.MoveNext(out var station, out var comp))
|
||||
{
|
||||
var list = Comp<StationJobsComponent>(station).JobList.ToDictionary(x => x.Key, x => x.Value);
|
||||
var list = comp.JobList.ToDictionary(x => x.Key, x => x.Value);
|
||||
jobs.Add(station, list);
|
||||
stationNames.Add(station, Name(station));
|
||||
}
|
||||
|
||||
35
Content.Server/Station/Systems/StationNameSystem.cs
Normal file
35
Content.Server/Station/Systems/StationNameSystem.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Content.Server.Station.Components;
|
||||
|
||||
namespace Content.Server.Station.Systems;
|
||||
|
||||
/// <summary>
|
||||
/// This handles naming stations.
|
||||
/// </summary>
|
||||
public sealed class StationNameSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly StationSystem _station = default!;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<StationNameSetupComponent, ComponentInit>(OnStationNameSetupInit);
|
||||
}
|
||||
|
||||
private void OnStationNameSetupInit(EntityUid uid, StationNameSetupComponent component, ComponentInit args)
|
||||
{
|
||||
if (!HasComp<StationDataComponent>(uid))
|
||||
return;
|
||||
|
||||
_station.RenameStation(uid, GenerateStationName(component), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a station name from the given config.
|
||||
/// </summary>
|
||||
private static string GenerateStationName(StationNameSetupComponent config)
|
||||
{
|
||||
return config.NameGenerator is not null
|
||||
? config.NameGenerator.FormatName(config.StationNameTemplate)
|
||||
: config.StationNameTemplate;
|
||||
}
|
||||
}
|
||||
@@ -50,15 +50,9 @@ public sealed class StationSpawningSystem : EntitySystem
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<StationInitializedEvent>(OnStationInitialized);
|
||||
_configurationManager.OnValueChanged(CCVars.ICRandomCharacters, e => _randomizeCharacters = e, true);
|
||||
}
|
||||
|
||||
private void OnStationInitialized(StationInitializedEvent ev)
|
||||
{
|
||||
AddComp<StationSpawningComponent>(ev.Station);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to spawn a player character onto the given station.
|
||||
/// </summary>
|
||||
@@ -95,16 +89,19 @@ public sealed class StationSpawningSystem : EntitySystem
|
||||
/// <param name="job">Job to assign to the character, if any.</param>
|
||||
/// <param name="profile">Appearance profile to use for the character.</param>
|
||||
/// <param name="station">The station this player is being spawned on.</param>
|
||||
/// <param name="entity">The entity to use, if one already exists.</param>
|
||||
/// <returns>The spawned entity</returns>
|
||||
public EntityUid SpawnPlayerMob(
|
||||
EntityCoordinates coordinates,
|
||||
Job? job,
|
||||
HumanoidCharacterProfile? profile,
|
||||
EntityUid? station)
|
||||
EntityUid? station,
|
||||
EntityUid? entity = null)
|
||||
{
|
||||
// If we're not spawning a humanoid, we're gonna exit early without doing all the humanoid stuff.
|
||||
if (job?.JobEntity != null)
|
||||
{
|
||||
DebugTools.Assert(entity is null);
|
||||
var jobEntity = EntityManager.SpawnEntity(job.JobEntity, coordinates);
|
||||
MakeSentientCommand.MakeSentient(jobEntity, EntityManager);
|
||||
DoJobSpecials(job, jobEntity);
|
||||
@@ -131,7 +128,7 @@ public sealed class StationSpawningSystem : EntitySystem
|
||||
if (!_prototypeManager.TryIndex<SpeciesPrototype>(speciesId, out var species))
|
||||
throw new ArgumentException($"Invalid species prototype was used: {speciesId}");
|
||||
|
||||
var entity = Spawn(species.Prototype, coordinates);
|
||||
entity ??= Spawn(species.Prototype, coordinates);
|
||||
|
||||
if (_randomizeCharacters)
|
||||
{
|
||||
@@ -141,24 +138,24 @@ public sealed class StationSpawningSystem : EntitySystem
|
||||
if (job?.StartingGear != null)
|
||||
{
|
||||
var startingGear = _prototypeManager.Index<StartingGearPrototype>(job.StartingGear);
|
||||
EquipStartingGear(entity, startingGear, profile);
|
||||
EquipStartingGear(entity.Value, startingGear, profile);
|
||||
if (profile != null)
|
||||
EquipIdCard(entity, profile.Name, job.Prototype, station);
|
||||
EquipIdCard(entity.Value, profile.Name, job.Prototype, station);
|
||||
}
|
||||
|
||||
if (profile != null)
|
||||
{
|
||||
_humanoidSystem.LoadProfile(entity, profile);
|
||||
MetaData(entity).EntityName = profile.Name;
|
||||
_humanoidSystem.LoadProfile(entity.Value, profile);
|
||||
MetaData(entity.Value).EntityName = profile.Name;
|
||||
if (profile.FlavorText != "" && _configurationManager.GetCVar(CCVars.FlavorText))
|
||||
{
|
||||
AddComp<DetailExaminableComponent>(entity).Content = profile.FlavorText;
|
||||
AddComp<DetailExaminableComponent>(entity.Value).Content = profile.FlavorText;
|
||||
}
|
||||
}
|
||||
|
||||
DoJobSpecials(job, entity);
|
||||
_identity.QueueIdentityUpdate(entity);
|
||||
return entity;
|
||||
DoJobSpecials(job, entity.Value);
|
||||
_identity.QueueIdentityUpdate(entity.Value);
|
||||
return entity.Value;
|
||||
}
|
||||
|
||||
private void DoJobSpecials(Job? job, EntityUid entity)
|
||||
|
||||
@@ -13,6 +13,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Station.Systems;
|
||||
|
||||
@@ -35,16 +36,6 @@ public sealed class StationSystem : EntitySystem
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private readonly HashSet<EntityUid> _stations = new();
|
||||
|
||||
/// <summary>
|
||||
/// All stations that currently exist.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// I'd have this just invoke an entity query, but I want this to be a hashset for convenience and it allocating on use would be lame.
|
||||
/// </remarks>
|
||||
public IReadOnlySet<EntityUid> Stations => _stations;
|
||||
|
||||
private bool _randomStationOffset;
|
||||
private bool _randomStationRotation;
|
||||
private float _maxRandomStationOffset;
|
||||
@@ -57,9 +48,8 @@ public sealed class StationSystem : EntitySystem
|
||||
SubscribeLocalEvent<GameRunLevelChangedEvent>(OnRoundEnd);
|
||||
SubscribeLocalEvent<PreGameMapLoad>(OnPreGameMapLoad);
|
||||
SubscribeLocalEvent<PostGameMapLoad>(OnPostGameMapLoad);
|
||||
SubscribeLocalEvent<StationDataComponent, ComponentAdd>(OnStationAdd);
|
||||
SubscribeLocalEvent<StationDataComponent, ComponentStartup>(OnStationAdd);
|
||||
SubscribeLocalEvent<StationDataComponent, ComponentShutdown>(OnStationDeleted);
|
||||
SubscribeLocalEvent<StationDataComponent, EntParentChangedMessage>(OnParentChanged);
|
||||
SubscribeLocalEvent<StationMemberComponent, ComponentShutdown>(OnStationGridDeleted);
|
||||
SubscribeLocalEvent<StationMemberComponent, PostGridSplitEvent>(OnStationSplitEvent);
|
||||
|
||||
@@ -89,68 +79,34 @@ public sealed class StationSystem : EntitySystem
|
||||
_player.PlayerStatusChanged -= OnPlayerStatusChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when the server shuts down or restarts to avoid uneccesarily logging mid-round station deletion errors.
|
||||
/// </summary>
|
||||
public void OnServerDispose()
|
||||
{
|
||||
_stations.Clear();
|
||||
}
|
||||
|
||||
private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e)
|
||||
{
|
||||
if (e.NewStatus == SessionStatus.Connected)
|
||||
{
|
||||
RaiseNetworkEvent(new StationsUpdatedEvent(_stations), e.Session);
|
||||
RaiseNetworkEvent(new StationsUpdatedEvent(GetStationsSet()), e.Session);
|
||||
}
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void OnStationAdd(EntityUid uid, StationDataComponent component, ComponentAdd args)
|
||||
private void OnStationAdd(EntityUid uid, StationDataComponent component, ComponentStartup args)
|
||||
{
|
||||
_stations.Add(uid);
|
||||
RaiseNetworkEvent(new StationsUpdatedEvent(GetStationsSet()), Filter.Broadcast());
|
||||
|
||||
var metaData = MetaData(uid);
|
||||
RaiseLocalEvent(new StationInitializedEvent(uid));
|
||||
_sawmill.Info($"Set up station {metaData.EntityName} ({uid}).");
|
||||
|
||||
RaiseNetworkEvent(new StationsUpdatedEvent(_stations), Filter.Broadcast());
|
||||
}
|
||||
|
||||
private void OnStationDeleted(EntityUid uid, StationDataComponent component, ComponentShutdown args)
|
||||
{
|
||||
if (_stations.Contains(uid) && // Was not deleted via DeleteStation()
|
||||
_gameTicker.RunLevel == GameRunLevel.InRound && // And not due to a round restart
|
||||
_gameTicker.LobbyEnabled) // If there isn't a lobby, this is probably sandbox, single player, or a test
|
||||
{
|
||||
// printing a stack trace, rather than throwing an exception so that entity deletion continues as normal.
|
||||
Logger.Error($"Station entity {ToPrettyString(uid)} is getting deleted mid-round. Trace: {Environment.StackTrace}");
|
||||
}
|
||||
|
||||
foreach (var grid in component.Grids)
|
||||
{
|
||||
RemComp<StationMemberComponent>(grid);
|
||||
}
|
||||
|
||||
_stations.Remove(uid);
|
||||
RaiseNetworkEvent(new StationsUpdatedEvent(_stations), Filter.Broadcast());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If a station data entity is getting re-parented mid-round, this will log an error.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This doesn't really achieve anything, it just for debugging any future station data bugs.
|
||||
/// </remarks>
|
||||
private void OnParentChanged(EntityUid uid, StationDataComponent component, ref EntParentChangedMessage args)
|
||||
{
|
||||
if (_gameTicker.RunLevel != GameRunLevel.InRound ||
|
||||
MetaData(uid).EntityLifeStage >= EntityLifeStage.MapInitialized ||
|
||||
component.LifeStage <= ComponentLifeStage.Initializing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Yeah this doesn't actually stop the parent change..... it just ineffectually yells about it.
|
||||
// STOP RIGHT THERE CRIMINAL SCUM
|
||||
_sawmill.Error($"Station entity {ToPrettyString(uid)} is getting reparented from {ToPrettyString(args.OldParent ?? EntityUid.Invalid)} to {ToPrettyString(args.Transform.ParentUid)}");
|
||||
RaiseNetworkEvent(new StationsUpdatedEvent(GetStationsSet()), Filter.Broadcast());
|
||||
}
|
||||
|
||||
private void OnPreGameMapLoad(PreGameMapLoad ev)
|
||||
@@ -199,23 +155,18 @@ public sealed class StationSystem : EntitySystem
|
||||
_sawmill.Error($"There were no station grids for {ev.GameMap.ID}!");
|
||||
}
|
||||
|
||||
// Iterate over all PartOfStation
|
||||
// TODO: Remove this whenever pillar finally gets replaced. It's the sole user.
|
||||
foreach (var grid in ev.Grids)
|
||||
{
|
||||
if (!TryComp<PartOfStationComponent>(grid, out var partOfStation))
|
||||
continue;
|
||||
|
||||
AddGrid(partOfStation.Id, grid);
|
||||
}
|
||||
|
||||
foreach (var (id, gridIds) in dict)
|
||||
{
|
||||
StationConfig? stationConfig = null;
|
||||
StationConfig stationConfig;
|
||||
|
||||
if (ev.GameMap.Stations.ContainsKey(id))
|
||||
stationConfig = ev.GameMap.Stations[id];
|
||||
else
|
||||
{
|
||||
_sawmill.Error($"The station {id} in map {ev.GameMap.ID} does not have an associated station config!");
|
||||
continue;
|
||||
}
|
||||
|
||||
InitializeNewStation(stationConfig, gridIds, ev.StationName);
|
||||
}
|
||||
}
|
||||
@@ -225,9 +176,10 @@ public sealed class StationSystem : EntitySystem
|
||||
if (eventArgs.New != GameRunLevel.PreRoundLobby)
|
||||
return;
|
||||
|
||||
foreach (var entity in _stations)
|
||||
var query = EntityQueryEnumerator<StationDataComponent>();
|
||||
while (query.MoveNext(out var station, out _))
|
||||
{
|
||||
DeleteStation(entity);
|
||||
QueueDel(station);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,47 +278,26 @@ public sealed class StationSystem : EntitySystem
|
||||
return filter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates a station name from the given config.
|
||||
/// </summary>
|
||||
public static string GenerateStationName(StationConfig config)
|
||||
{
|
||||
return config.NameGenerator is not null
|
||||
? config.NameGenerator.FormatName(config.StationNameTemplate)
|
||||
: config.StationNameTemplate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new station with the given information.
|
||||
/// </summary>
|
||||
/// <param name="stationConfig">The game map prototype used, if any.</param>
|
||||
/// <param name="gridIds">All grids that should be added to the station.</param>
|
||||
/// <param name="name">Optional override for the station name.</param>
|
||||
/// <remarks>This is for ease of use, manually spawning the entity works just fine.</remarks>
|
||||
/// <returns>The initialized station.</returns>
|
||||
public EntityUid InitializeNewStation(StationConfig? stationConfig, IEnumerable<EntityUid>? gridIds, string? name = null)
|
||||
public EntityUid InitializeNewStation(StationConfig stationConfig, IEnumerable<EntityUid>? gridIds, string? name = null)
|
||||
{
|
||||
var station = Spawn(null, MapCoordinates.Nullspace);
|
||||
// Use overrides for setup.
|
||||
var station = EntityManager.SpawnEntity(stationConfig.StationPrototype, MapCoordinates.Nullspace, stationConfig.StationComponentOverrides);
|
||||
|
||||
// TODO SERIALIZATION The station data needs to be saveable somehow, but when a map gets saved, this entity
|
||||
// won't be included because its in null-space. Also, what happens to shuttles on other maps?
|
||||
if (name is not null)
|
||||
RenameStation(station, name, false);
|
||||
|
||||
var data = AddComp<StationDataComponent>(station);
|
||||
var metaData = MetaData(station);
|
||||
data.StationConfig = stationConfig;
|
||||
DebugTools.Assert(HasComp<StationDataComponent>(station), "Stations should have StationData in their prototype.");
|
||||
|
||||
if (stationConfig is not null && name is null)
|
||||
{
|
||||
name = GenerateStationName(stationConfig);
|
||||
}
|
||||
else if (name is null)
|
||||
{
|
||||
_sawmill.Error($"When setting up station {station}, was unable to find a valid name in the config and no name was provided.");
|
||||
name = "unnamed station";
|
||||
}
|
||||
|
||||
metaData.EntityName = name;
|
||||
RaiseLocalEvent(new StationInitializedEvent(station));
|
||||
_sawmill.Info($"Set up station {metaData.EntityName} ({station}).");
|
||||
var data = Comp<StationDataComponent>(station);
|
||||
name ??= MetaData(station).EntityName;
|
||||
|
||||
foreach (var grid in gridIds ?? Array.Empty<EntityUid>())
|
||||
{
|
||||
@@ -397,11 +328,11 @@ public sealed class StationSystem : EntitySystem
|
||||
|
||||
var stationMember = AddComp<StationMemberComponent>(mapGrid);
|
||||
stationMember.Station = station;
|
||||
stationData.Grids.Add(gridComponent.Owner);
|
||||
stationData.Grids.Add(mapGrid);
|
||||
|
||||
RaiseLocalEvent(station, new StationGridAddedEvent(gridComponent.Owner, false), true);
|
||||
RaiseLocalEvent(station, new StationGridAddedEvent(mapGrid, false), true);
|
||||
|
||||
_sawmill.Info($"Adding grid {mapGrid}:{gridComponent.Owner} to station {Name(station)} ({station})");
|
||||
_sawmill.Info($"Adding grid {mapGrid} to station {Name(station)} ({station})");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -420,10 +351,10 @@ public sealed class StationSystem : EntitySystem
|
||||
throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station));
|
||||
|
||||
RemComp<StationMemberComponent>(mapGrid);
|
||||
stationData.Grids.Remove(gridComponent.Owner);
|
||||
stationData.Grids.Remove(mapGrid);
|
||||
|
||||
RaiseLocalEvent(station, new StationGridRemovedEvent(gridComponent.Owner), true);
|
||||
_sawmill.Info($"Removing grid {mapGrid}:{gridComponent.Owner} from station {Name(station)} ({station})");
|
||||
RaiseLocalEvent(station, new StationGridRemovedEvent(mapGrid), true);
|
||||
_sawmill.Info($"Removing grid {mapGrid} from station {Name(station)} ({station})");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -462,9 +393,7 @@ public sealed class StationSystem : EntitySystem
|
||||
if (!Resolve(station, ref stationData))
|
||||
throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station));
|
||||
|
||||
// component shutdown will error if the station was not removed from _stations prior to deletion.
|
||||
_stations.Remove(station);
|
||||
Del(station);
|
||||
QueueDel(station);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -501,6 +430,16 @@ public sealed class StationSystem : EntitySystem
|
||||
|
||||
return CompOrNull<StationMemberComponent>(xform.GridUid)?.Station;
|
||||
}
|
||||
|
||||
public List<EntityUid> GetStations()
|
||||
{
|
||||
return EntityQuery<StationDataComponent>().Select(x => x.Owner).ToList();
|
||||
}
|
||||
|
||||
public HashSet<EntityUid> GetStationsSet()
|
||||
{
|
||||
return EntityQuery<StationDataComponent>().Select(x => x.Owner).ToHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user