+using System.Collections.Frozen;
using System.Linq;
using System.Numerics;
using Content.Client.Administration.Systems;
private readonly EntityLookupSystem _entityLookup;
private readonly IUserInterfaceManager _userInterfaceManager;
private readonly SharedRoleSystem _roles;
+ private readonly IPrototypeManager _prototypeManager;
private readonly Font _font;
private readonly Font _fontBold;
private AdminOverlayAntagFormat _overlayFormat;
private float _overlayMergeDistance;
//TODO make this adjustable via GUI?
- private readonly ProtoId<RoleTypePrototype>[] _filter =
- ["SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"];
+ private static readonly FrozenSet<ProtoId<RoleTypePrototype>> Filter =
+ new ProtoId<RoleTypePrototype>[] {"SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"}
+ .ToFrozenSet();
private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic");
EntityLookupSystem entityLookup,
IUserInterfaceManager userInterfaceManager,
IConfigurationManager config,
- SharedRoleSystem roles)
+ SharedRoleSystem roles,
+ IPrototypeManager prototypeManager)
{
_system = system;
_entityManager = entityManager;
_entityLookup = entityLookup;
_userInterfaceManager = userInterfaceManager;
_roles = roles;
+ _prototypeManager = prototypeManager;
ZIndex = 200;
// Setting these to a specific ttf would break the antag symbols
_font = resourceCache.NotoStack();
foreach (var info in sortable.OrderBy(s => s.Item4.Y).ToList())
{
var playerInfo = info.Item1;
+ var rolePrototype = playerInfo.RoleProto == null
+ ? null
+ : _prototypeManager.Index(playerInfo.RoleProto.Value);
+
+ var roleName = Loc.GetString(rolePrototype?.Name ?? RoleTypePrototype.FallbackName);
+ var roleColor = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor;
+ var roleSymbol = rolePrototype?.Symbol ?? RoleTypePrototype.FallbackSymbol;
+
var aabb = info.Item2;
var entity = info.Item3;
var screenCoordinatesCenter = info.Item4;
switch (_overlaySymbolStyle)
{
case AdminOverlayAntagSymbolStyle.Specific:
- symbol = playerInfo.RoleProto.Symbol;
+ symbol = roleSymbol;
break;
case AdminOverlayAntagSymbolStyle.Basic:
symbol = Loc.GetString("player-tab-antag-prefix");
switch (_overlayFormat)
{
case AdminOverlayAntagFormat.Roletype:
- color = playerInfo.RoleProto.Color;
- symbol = _filter.Contains(playerInfo.RoleProto) ? symbol : string.Empty;
- text = _filter.Contains(playerInfo.RoleProto)
- ? Loc.GetString(playerInfo.RoleProto.Name).ToUpper()
+ color = roleColor;
+ symbol = IsFiltered(playerInfo.RoleProto) ? symbol : string.Empty;
+ text = IsFiltered(playerInfo.RoleProto)
+ ? roleName.ToUpper()
: string.Empty;
break;
case AdminOverlayAntagFormat.Subtype:
- color = playerInfo.RoleProto.Color;
- symbol = _filter.Contains(playerInfo.RoleProto) ? symbol : string.Empty;
- text = _filter.Contains(playerInfo.RoleProto)
- ? _roles.GetRoleSubtypeLabel(playerInfo.RoleProto.Name, playerInfo.Subtype).ToUpper()
+ color = roleColor;
+ symbol = IsFiltered(playerInfo.RoleProto) ? symbol : string.Empty;
+ text = IsFiltered(playerInfo.RoleProto)
+ ? _roles.GetRoleSubtypeLabel(roleName, playerInfo.Subtype).ToUpper()
: string.Empty;
break;
default:
drawnOverlays.Add((screenCoordinatesCenter, currentOffset));
}
}
+
+ private static bool IsFiltered(ProtoId<RoleTypePrototype>? roleProtoId)
+ {
+ if (roleProtoId == null)
+ return false;
+
+ return Filter.Contains(roleProtoId.Value);
+ }
}
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Shared.Configuration;
+using Robust.Shared.Prototypes;
namespace Content.Client.Administration.Systems
{
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
private AdminNameOverlay _adminNameOverlay = default!;
_entityLookup,
_userInterfaceManager,
_configurationManager,
- _roles);
+ _roles,
+ _proto);
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
}
using Content.Shared.Administration;
+using Content.Shared.Mind;
using Content.Shared.Roles;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
public sealed partial class PlayerTabEntry : PanelContainer
{
[Dependency] private readonly IEntityManager _entMan = default!;
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
public PlayerTabEntry(
PlayerInfo player,
RobustXamlLoader.Load(this);
var roles = _entMan.System<SharedRoleSystem>();
+ var rolePrototype = player.RoleProto == null ? null : _prototype.Index(player.RoleProto.Value);
+
UsernameLabel.Text = player.Username;
if (!player.Connected)
UsernameLabel.StyleClasses.Add("Disabled");
break;
default:
case AdminPlayerTabSymbolOption.Specific:
- symbol = player.Antag ? player.RoleProto.Symbol : string.Empty;
+ symbol = player.Antag ? rolePrototype?.Symbol ?? RoleTypePrototype.FallbackSymbol : string.Empty;
break;
}
CharacterLabel.Text = Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", player.CharacterName));
if (player.Antag && colorAntags)
- CharacterLabel.FontColorOverride = player.RoleProto.Color;
+ CharacterLabel.FontColorOverride = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor;
if (player.IdentityName != player.CharacterName)
CharacterLabel.Text += $" [{player.IdentityName}]";
- var roletype = RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name);
- var subtype = roles.GetRoleSubtypeLabel(player.RoleProto.Name, player.Subtype);
+ var roletype = RoleTypeLabel.Text = Loc.GetString(rolePrototype?.Name ?? RoleTypePrototype.FallbackName);
+ var subtype = roles.GetRoleSubtypeLabel(rolePrototype?.Name ?? RoleTypePrototype.FallbackName, player.Subtype);
switch (roleSetting)
{
case AdminPlayerTabRoleTypeOption.RoleTypeSubtype:
}
if (colorRoles)
- RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
+ RoleTypeLabel.FontColorOverride = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor;
BackgroundColorPanel.PanelOverride = styleBoxFlat;
OverallPlaytimeLabel.Text = player.PlaytimeString;
}
-using System.Numerics;
+using System.Linq;
+using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.DeviceLinking;
using Content.Shared.DeviceNetwork;
[GenerateTypedNameReferences]
public sealed partial class NetworkConfiguratorLinkMenu : FancyWindow
{
+ private readonly IPrototypeManager _prototypeManager = null!;
+
private const string PanelBgColor = "#202023";
private readonly LinksRender _links;
public NetworkConfiguratorLinkMenu()
{
RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
var footerStyleBox = new StyleBoxFlat()
{
ButtonContainerRight.RemoveAllChildren();
_sources.Clear();
- _sources.AddRange(linkState.Sources);
+ _sources.AddRange(linkState.Sources.Select(s => _prototypeManager.Index(s)));
_links.SourceButtons.Clear();
var i = 0;
foreach (var source in _sources)
}
_sinks.Clear();
- _sinks.AddRange(linkState.Sinks);
+ _sinks.AddRange(linkState.Sinks.Select(s => _prototypeManager.Index(s)));
_links.SinkButtons.Clear();
i = 0;
foreach (var sink in _sinks)
using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.Database;
+using Content.Shared.Mind;
using Content.Shared.Players.PlayTimeTracking;
using Prometheus;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
using Robust.Shared.Player;
+using Robust.Shared.Prototypes;
using Robust.Shared.Reflection;
using Robust.Shared.Timing;
[Dependency] private readonly ISharedPlayerManager _player = default!;
[Dependency] private readonly ISharedPlaytimeManager _playtime = default!;
[Dependency] private readonly ISharedChatManager _chat = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
public const string SawmillId = "admin.logs";
var cachedInfo = adminSys.GetCachedPlayerInfo(new NetUserId(id));
if (cachedInfo != null && cachedInfo.Antag)
{
- var subtype = Loc.GetString(cachedInfo.Subtype ?? cachedInfo.RoleProto.Name);
+ var proto = cachedInfo.RoleProto == null ? null : _proto.Index(cachedInfo.RoleProto.Value);
+ var subtype = Loc.GetString(cachedInfo.Subtype ?? proto?.Name ?? RoleTypePrototype.FallbackName);
logMessage = Loc.GetString(
"admin-alert-antag-label",
("message", logMessage),
var antag = false;
// Starting role, antagonist status and role type
- RoleTypePrototype roleType = new();
+ RoleTypePrototype? roleType = null;
var startingRole = string.Empty;
LocId? subtype = null;
if (_minds.TryGetMind(session, out var mindId, out var mindComp) && mindComp is not null)
subtype = mindComp.Subtype;
}
else
- Log.Error($"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(roleType.Name)}' instead");
+ Log.Error($"{ToPrettyString(mindId)} has invalid Role Type '{mindComp.RoleType}'. Displaying '{Loc.GetString(RoleTypePrototype.FallbackName)}' instead");
antag = _role.MindIsAntagonist(mindId);
startingRole = _jobs.MindTryGetJobName(mindId);
identityName,
startingRole,
antag,
- roleType,
+ roleType?.ID,
subtype,
sortWeight,
GetNetEntity(session?.AttachedEntity),
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Map.Events;
+using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
return;
var sources = _deviceLinkSystem.GetSourcePorts(sourceUid, sourceComponent);
- var sinks = _deviceLinkSystem.GetSinkPorts(sinkUid, sinkComponent);
+ var sinks = _deviceLinkSystem.GetSinkPortIds((sinkUid, sinkComponent));
var links = _deviceLinkSystem.GetLinks(sourceUid, sinkUid, sourceComponent);
var defaults = _deviceLinkSystem.GetDefaults(sources);
+ var sourceIds = sources.Select(s => (ProtoId<SourcePortPrototype>)s.ID).ToArray();
var sourceAddress = Resolve(sourceUid, ref sourceNetworkComponent, false) ? sourceNetworkComponent.Address : "";
var sinkAddress = Resolve(sinkUid, ref sinkNetworkComponent, false) ? sinkNetworkComponent.Address : "";
- var state = new DeviceLinkUserInterfaceState(sources, sinks, links, sourceAddress, sinkAddress, defaults);
+ var state = new DeviceLinkUserInterfaceState(sourceIds, sinks, links, sourceAddress, sinkAddress, defaults);
_uiSystem.SetUiState(configuratorUid, NetworkConfiguratorUiKey.Link, state);
}
using Content.Server.Speech.Components;
using Content.Shared.Chat.Prototypes;
using Content.Shared.Speech.Components;
+using Robust.Shared.Prototypes;
namespace Content.Server.Speech.EntitySystems;
{
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly ReplacementAccentSystem _replacement = default!;
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
public override void Initialize()
{
if (args.Handled || !args.Emote.Category.HasFlag(EmoteCategory.Vocal))
return;
- if (TryComp<VocalComponent>(ent.Owner, out var vocalComp))
+ if (TryComp<VocalComponent>(ent.Owner, out var vocalComp) && vocalComp.EmoteSounds is { } sounds)
{
// play a muffled version of the vocal emote
- args.Handled = _chat.TryPlayEmoteSound(ent.Owner, vocalComp.EmoteSounds, args.Emote, ent.Comp.EmoteAudioParams);
+ args.Handled = _chat.TryPlayEmoteSound(
+ ent.Owner,
+ _prototype.Index(sounds),
+ args.Emote,
+ ent.Comp.EmoteAudioParams);
}
}
return;
}
+ if (component.EmoteSounds is not { } sounds)
+ return;
+
// just play regular sound based on emote proto
- args.Handled = _chat.TryPlayEmoteSound(uid, component.EmoteSounds, args.Emote);
+ args.Handled = _chat.TryPlayEmoteSound(uid, _proto.Index(sounds), args.Emote);
}
private void OnScreamAction(EntityUid uid, VocalComponent component, ScreamActionEvent args)
return true;
}
- return _chat.TryPlayEmoteSound(uid, component.EmoteSounds, component.ScreamId);
+ if (component.EmoteSounds is not { } sounds)
+ return false;
+
+ return _chat.TryPlayEmoteSound(uid, _proto.Index(sounds), component.ScreamId);
}
private void LoadSounds(EntityUid uid, VocalComponent component, Sex? sex = null)
if (!component.Sounds.TryGetValue(sex.Value, out var protoId))
return;
- _proto.TryIndex(protoId, out component.EmoteSounds);
+
+ if (!_proto.HasIndex(protoId))
+ return;
+
+ component.EmoteSounds = protoId;
}
}
using Content.Shared.Mind;
using Robust.Shared.Network;
+using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Shared.Administration;
string IdentityName,
string StartingJob,
bool Antag,
- RoleTypePrototype RoleProto,
+ ProtoId<RoleTypePrototype>? RoleProto,
LocId? Subtype,
int SortWeight,
NetEntity? NetEntity,
/// that must be sold together in a labeled container in order
/// to receive a monetary reward.
/// </summary>
-[Prototype, Serializable, NetSerializable]
+[Prototype]
public sealed partial class CargoBountyPrototype : IPrototype
{
/// <inheritdoc/>
/// Sounds collection for each <see cref="EmotePrototype"/>.
/// Different entities may use different sounds collections.
/// </summary>
-[Prototype, Serializable, NetSerializable]
+[Prototype]
public sealed partial class EmoteSoundsPrototype : IPrototype
{
[IdDataField]
/// cref="DamageableComponent"/> should support.
/// </remarks>
[Prototype]
- [Serializable, NetSerializable]
public sealed partial class DamageContainerPrototype : IPrototype
{
[ViewVariables]
/// to change/get/set damage in a <see cref="DamageableComponent"/>.
/// </remarks>
[Prototype(2)]
- [Serializable, NetSerializable]
public sealed partial class DamageGroupPrototype : IPrototype
{
[IdDataField] public string ID { get; private set; } = default!;
/// <summary>
/// A prototype for a device port, for use with device linking.
/// </summary>
-[Serializable, NetSerializable]
public abstract class DevicePortPrototype
{
[IdDataField]
}
[Prototype]
-[Serializable, NetSerializable]
public sealed partial class SinkPortPrototype : DevicePortPrototype, IPrototype
{
}
[Prototype]
-[Serializable, NetSerializable]
public sealed partial class SourcePortPrototype : DevicePortPrototype, IPrototype
{
/// <summary>
+using System.Linq;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.DeviceLinking.Events;
}
}
+ public ProtoId<SourcePortPrototype>[] GetSourcePortIds(Entity<DeviceLinkSourceComponent> source)
+ {
+ return source.Comp.Ports.ToArray();
+ }
+
/// <summary>
/// Retrieves the available ports from a source
/// </summary>
return sourcePorts;
}
+ public ProtoId<SinkPortPrototype>[] GetSinkPortIds(Entity<DeviceLinkSinkComponent> source)
+ {
+ return source.Comp.Ports.ToArray();
+ }
+
/// <summary>
/// Retrieves the available ports from a sink
/// </summary>
/// A named device network frequency. Useful for ensuring entity prototypes can communicate with each other.
/// </summary>
[Prototype]
-[Serializable, NetSerializable]
public sealed partial class DeviceFrequencyPrototype : IPrototype
{
[IdDataField]
[Serializable, NetSerializable]
public sealed class DeviceLinkUserInterfaceState : BoundUserInterfaceState
{
- public readonly List<SourcePortPrototype> Sources;
- public readonly List<SinkPortPrototype> Sinks;
+ public readonly ProtoId<SourcePortPrototype>[] Sources;
+ public readonly ProtoId<SinkPortPrototype>[] Sinks;
public readonly HashSet<(ProtoId<SourcePortPrototype> source, ProtoId<SinkPortPrototype> sink)> Links;
public readonly List<(string source, string sink)>? Defaults;
public readonly string SourceAddress;
public readonly string SinkAddress;
public DeviceLinkUserInterfaceState(
- List<SourcePortPrototype> sources,
- List<SinkPortPrototype> sinks,
+ ProtoId<SourcePortPrototype>[] sources,
+ ProtoId<SinkPortPrototype>[] sinks,
HashSet<(ProtoId<SourcePortPrototype> source, ProtoId<SinkPortPrototype> sink)> links,
string sourceAddress,
string sinkAddress,
/// <summary>
/// The core properties of Role Types
/// </summary>
-[Prototype, Serializable]
+[Prototype]
public sealed partial class RoleTypePrototype : IPrototype
{
[IdDataField]
public string ID { get; private set; } = default!;
+ public static readonly LocId FallbackName = "role-type-crew-aligned-name";
+ public const string FallbackSymbol = "";
+ public static readonly Color FallbackColor = Color.FromHex("#eeeeee");
+
/// <summary>
/// The role's name as displayed on the UI.
/// </summary>
[DataField]
- public LocId Name = "role-type-crew-aligned-name";
+ public LocId Name = FallbackName;
/// <summary>
/// The role's displayed color.
/// </summary>
[DataField]
- public Color Color = Color.FromHex("#eeeeee");
+ public Color Color = FallbackColor;
/// <summary>
/// A symbol used to represent the role type.
/// </summary>
[DataField]
- public string Symbol = string.Empty;
+ public string Symbol = FallbackSymbol;
}
/// Describes information for a single antag.
/// </summary>
[Prototype]
-[Serializable, NetSerializable]
public sealed partial class AntagPrototype : IPrototype
{
[ViewVariables]
/// This is a prototype for a law governing the behavior of silicons.
/// </summary>
[Prototype]
-[Serializable, NetSerializable]
public sealed partial class SiliconLawPrototype : SiliconLaw, IPrototype
{
/// <inheritdoc/>
/// This is a prototype for a <see cref="SiliconLawPrototype"/> list.
/// Cannot be used directly since it is a list of prototype ids rather than List<Siliconlaw>.
/// </summary>
-[Prototype, Serializable, NetSerializable]
+[Prototype]
public sealed partial class SiliconLawsetPrototype : IPrototype
{
/// <inheritdoc/>
/// </summary>
[ViewVariables]
[AutoNetworkedField]
- public EmoteSoundsPrototype? EmoteSounds = null;
+ public ProtoId<EmoteSoundsPrototype>? EmoteSounds = null;
}
/// This is separate to the cargo ordering system.
/// </summary>
[Prototype]
-[DataDefinition, Serializable, NetSerializable]
+[DataDefinition]
public sealed partial class CurrencyPrototype : IPrototype
{
[ViewVariables]
/// Defines a set item listing that is available in a store
/// </summary>
[Prototype]
-[Serializable, NetSerializable]
[DataDefinition]
public sealed partial class ListingPrototype : ListingData, IPrototype
{
/// how <see cref="StoreDiscountComponent"/> will be filled by respective system.
/// </summary>
[Prototype]
-[DataDefinition, Serializable, NetSerializable]
+[DataDefinition]
public sealed partial class DiscountCategoryPrototype : IPrototype
{
[ViewVariables]
/// Used to define different categories for a store.
/// </summary>
[Prototype]
-[Serializable, NetSerializable, DataDefinition]
public sealed partial class StoreCategoryPrototype : IPrototype
{
private string _name = string.Empty;
/// <summary>
/// Prototype for a story template that can be filled in with words chosen from <see cref="DatasetPrototype"/>s.
/// </summary>
-[Serializable, Prototype]
+[Prototype]
public sealed partial class StoryTemplatePrototype : IPrototype
{
/// <summary>
namespace Content.Shared.VendingMachines
{
- [Serializable, NetSerializable, Prototype]
+ [Prototype]
public sealed partial class VendingMachineInventoryPrototype : IPrototype
{
[ViewVariables]