Merge remote-tracking branch 'upstream-origin/master' into fork/iaada/more-trash

This commit is contained in:
SlamBamActionman
2025-09-11 17:08:39 +02:00
329 changed files with 53462 additions and 4397 deletions

View File

@@ -29,7 +29,7 @@ namespace Content.Client.Access.UI
foreach (var access in accessLevels)
{
if (!protoManager.TryIndex(access, out var accessLevel))
if (!protoManager.Resolve(access, out var accessLevel))
{
continue;
}

View File

@@ -57,7 +57,7 @@ public sealed partial class GroupedAccessLevelChecklist : BoxContainer
foreach (var accessGroup in _accessGroups)
{
if (!_protoManager.TryIndex(accessGroup, out var accessGroupProto))
if (!_protoManager.Resolve(accessGroup, out var accessGroupProto))
continue;
_groupedAccessLevels.Add(accessGroupProto, new());
@@ -65,13 +65,13 @@ public sealed partial class GroupedAccessLevelChecklist : BoxContainer
// Ensure that the 'general' access group is added to handle
// misc. access levels that aren't associated with any group
if (_protoManager.TryIndex(GeneralAccessGroup, out var generalAccessProto))
if (_protoManager.Resolve(GeneralAccessGroup, out var generalAccessProto))
_groupedAccessLevels.TryAdd(generalAccessProto, new());
// Assign known access levels with their associated groups
foreach (var accessLevel in _accessLevels)
{
if (!_protoManager.TryIndex(accessLevel, out var accessLevelProto))
if (!_protoManager.Resolve(accessLevel, out var accessLevelProto))
continue;
var assigned = false;

View File

@@ -4,6 +4,7 @@ using Content.Shared.Access.Systems;
using Content.Shared.CCVar;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.CrewManifest;
using Content.Shared.Roles;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using static Content.Shared.Access.Components.IdCardConsoleComponent;
@@ -74,7 +75,7 @@ namespace Content.Client.Access.UI
_window?.UpdateState(castState);
}
public void SubmitData(string newFullName, string newJobTitle, List<ProtoId<AccessLevelPrototype>> newAccessList, string newJobPrototype)
public void SubmitData(string newFullName, string newJobTitle, List<ProtoId<AccessLevelPrototype>> newAccessList, ProtoId<JobPrototype> newJobPrototype)
{
if (newFullName.Length > _maxNameLength)
newFullName = newFullName[.._maxNameLength];

View File

@@ -123,7 +123,7 @@ namespace Content.Client.Access.UI
foreach (var group in job.AccessGroups)
{
if (!_prototypeManager.TryIndex(group, out AccessGroupPrototype? groupPrototype))
if (!_prototypeManager.Resolve(group, out AccessGroupPrototype? groupPrototype))
{
continue;
}

View File

@@ -316,8 +316,9 @@ public sealed partial class BanPanel : DefaultWindow
};
// This is adding the icon before the role name
// Yeah, this is sus, but having to split the functions up and stuff is worse imo.
if (_protoMan.TryIndex<JobPrototype>(role, out var jobPrototype) && _protoMan.TryIndex(jobPrototype.Icon, out var iconProto))
// TODO: This should not be using raw strings for prototypes as it means it won't be validated at all.
// I know the ban manager is doing the same thing, but that should not leak into UI code.
if (_protoMan.TryIndex<JobPrototype>(role, out var jobPrototype) && _protoMan.Resolve(jobPrototype.Icon, out var iconProto))
{
var jobIconTexture = new TextureRect
{

View File

@@ -134,7 +134,7 @@ public sealed class AlignAtmosPipeLayers : SnapgridCenter
var newProtoId = altPrototypes[(int)layer];
if (!_protoManager.TryIndex(newProtoId, out var newProto))
if (!_protoManager.Resolve(newProtoId, out var newProto))
return;
if (newProto.Type != ConstructionType.Structure)

View File

@@ -58,7 +58,7 @@ public sealed class JukeboxBoundUserInterface : BoundUserInterface
_menu.SetAudioStream(jukebox.AudioStream);
if (_protoManager.TryIndex(jukebox.SelectedSongId, out var songProto))
if (_protoManager.Resolve(jukebox.SelectedSongId, out var songProto))
{
var length = EntMan.System<AudioSystem>().GetAudioLength(songProto.Path.Path.ToString());
_menu.SetSelectedSong(songProto.Name, (float) length.TotalSeconds);

View File

@@ -39,7 +39,7 @@ public sealed class BarSignSystem : VisualizerSystem<BarSignComponent>
if (powered
&& sign.Current != null
&& _prototypeManager.TryIndex(sign.Current, out var proto))
&& _prototypeManager.Resolve(sign.Current, out var proto))
{
SpriteSystem.LayerSetSprite((id, sprite), 0, proto.Icon);
sprite.LayerSetShader(0, "unshaded");

View File

@@ -35,7 +35,7 @@ public sealed class BarSignBoundUserInterface(EntityUid owner, Enum uiKey) : Bou
public void Update(ProtoId<BarSignPrototype>? sign)
{
if (_prototype.TryIndex(sign, out var signPrototype))
if (_prototype.Resolve(sign, out var signPrototype))
_menu?.UpdateState(signPrototype);
}

View File

@@ -29,7 +29,7 @@ public sealed partial class BountyEntry : BoxContainer
UntilNextSkip = untilNextSkip;
if (!_prototype.TryIndex<CargoBountyPrototype>(bounty.Bounty, out var bountyPrototype))
if (!_prototype.Resolve<CargoBountyPrototype>(bounty.Bounty, out var bountyPrototype))
return;
var items = new List<string>();

View File

@@ -19,7 +19,7 @@ public sealed partial class BountyHistoryEntry : BoxContainer
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
if (!_prototype.TryIndex(bounty.Bounty, out var bountyPrototype))
if (!_prototype.Resolve(bounty.Bounty, out var bountyPrototype))
return;
var items = new List<string>();

View File

@@ -1,4 +1,7 @@
using Content.Shared.Changeling.Systems;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
using Content.Shared.Changeling.Components;
using Content.Shared.Changeling.Systems;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
@@ -7,28 +10,58 @@ namespace Content.Client.Changeling.UI;
[UsedImplicitly]
public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
private ChangelingTransformMenu? _window;
private SimpleRadialMenu? _menu;
private static readonly Color SelectedOptionBackground = StyleNano.ButtonColorGoodDefault.WithAlpha(128);
private static readonly Color SelectedOptionHoverBackground = StyleNano.ButtonColorGoodHovered.WithAlpha(128);
protected override void Open()
{
base.Open();
_window = this.CreateWindow<ChangelingTransformMenu>();
_window.OnIdentitySelect += SendIdentitySelect;
_window.Update(Owner);
_menu = this.CreateWindow<SimpleRadialMenu>();
Update();
_menu.OpenOverMouseScreenPosition();
}
public override void Update()
{
if (_window == null)
if (_menu == null)
return;
_window.Update(Owner);
if (!EntMan.TryGetComponent<ChangelingIdentityComponent>(Owner, out var lingIdentity))
return;
var models = ConvertToButtons(lingIdentity.ConsumedIdentities, lingIdentity?.CurrentIdentity);
_menu.SetButtons(models);
}
public void SendIdentitySelect(NetEntity identityId)
private IEnumerable<RadialMenuOptionBase> ConvertToButtons(
IEnumerable<EntityUid> identities,
EntityUid? currentIdentity
)
{
var buttons = new List<RadialMenuOptionBase>();
foreach (var identity in identities)
{
if (!EntMan.TryGetComponent<MetaDataComponent>(identity, out var metadata))
continue;
var option = new RadialMenuActionOption<NetEntity>(SendIdentitySelect, EntMan.GetNetEntity(identity))
{
IconSpecifier = RadialMenuIconSpecifier.With(identity),
ToolTip = metadata.EntityName,
BackgroundColor = (currentIdentity == identity) ? SelectedOptionBackground : null,
HoverBackgroundColor = (currentIdentity == identity) ? SelectedOptionHoverBackground : null
};
buttons.Add(option);
}
return buttons;
}
private void SendIdentitySelect(NetEntity identityId)
{
SendPredictedMessage(new ChangelingTransformIdentitySelectMessage(identityId));
}

View File

@@ -1,8 +0,0 @@
<ui:RadialMenu
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True">
<ui:RadialContainer Name="Main">
</ui:RadialContainer>
</ui:RadialMenu>

View File

@@ -1,62 +0,0 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Changeling.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Changeling.UI;
[GenerateTypedNameReferences]
public sealed partial class ChangelingTransformMenu : RadialMenu
{
[Dependency] private readonly IEntityManager _entity = default!;
public event Action<NetEntity>? OnIdentitySelect;
public ChangelingTransformMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}
public void Update(EntityUid uid)
{
Main.DisposeAllChildren();
if (!_entity.TryGetComponent<ChangelingIdentityComponent>(uid, out var identityComp))
return;
foreach (var identityUid in identityComp.ConsumedIdentities)
{
if (!_entity.TryGetComponent<MetaDataComponent>(identityUid, out var metadata))
continue;
var identityName = metadata.EntityName;
var button = new ChangelingTransformMenuButton()
{
StyleClasses = { "RadialMenuButton" },
SetSize = new Vector2(64, 64),
ToolTip = identityName,
};
var entView = new SpriteView()
{
SetSize = new Vector2(48, 48),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Stretch = SpriteView.StretchMode.Fill,
};
entView.SetEntity(identityUid);
button.OnButtonUp += _ =>
{
OnIdentitySelect?.Invoke(_entity.GetNetEntity(identityUid));
Close();
};
button.AddChild(entView);
Main.AddChild(button);
}
}
}
public sealed class ChangelingTransformMenuButton : RadialMenuTextureButtonWithSector;

View File

@@ -27,7 +27,7 @@ public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingInd
if (overrideIndicator != null)
currentTypingIndicator = overrideIndicator.Value;
if (!_prototypeManager.TryIndex(currentTypingIndicator, out var proto))
if (!_prototypeManager.Resolve(currentTypingIndicator, out var proto))
{
Log.Error($"Unknown typing indicator id: {component.TypingIndicatorPrototype}");
return;

View File

@@ -45,7 +45,7 @@ public sealed class ChameleonBoundUserInterface : BoundUserInterface
var newTargets = new List<EntProtoId>();
foreach (var target in targets)
{
if (string.IsNullOrEmpty(target) || !_proto.TryIndex(target, out EntityPrototype? proto))
if (string.IsNullOrEmpty(target) || !_proto.Resolve(target, out EntityPrototype? proto))
continue;
if (!proto.TryGetComponent(out TagComponent? tag, EntMan.ComponentFactory) || !_tag.HasTag(tag, st.RequiredTag))

View File

@@ -54,7 +54,7 @@ public sealed partial class ChameleonMenu : DefaultWindow
foreach (var id in _possibleIds)
{
if (!_prototypeManager.TryIndex(id, out EntityPrototype? proto))
if (!_prototypeManager.Resolve(id, out EntityPrototype? proto))
continue;
var lowId = id.Id.ToLowerInvariant();

View File

@@ -80,7 +80,7 @@ namespace Content.Client.Construction
{
foreach (var constructionProto in PrototypeManager.EnumeratePrototypes<ConstructionPrototype>())
{
if (!PrototypeManager.TryIndex(constructionProto.Graph, out var graphProto))
if (!PrototypeManager.Resolve(constructionProto.Graph, out var graphProto))
continue;
if (constructionProto.TargetNode is not { } targetNodeId)
@@ -121,17 +121,14 @@ namespace Content.Client.Construction
// If we got the id of the prototype, we exit the “recursion” by clearing the stack.
stack.Clear();
if (!PrototypeManager.TryIndex(constructionProto.ID, out ConstructionPrototype? recipe))
if (!PrototypeManager.Resolve(entityId, out var proto))
continue;
if (!PrototypeManager.TryIndex(entityId, out var proto))
continue;
var name = constructionProto.SetName.HasValue ? Loc.GetString(constructionProto.SetName) : proto.Name;
var desc = constructionProto.SetDescription.HasValue ? Loc.GetString(constructionProto.SetDescription) : proto.Description;
var name = recipe.SetName.HasValue ? Loc.GetString(recipe.SetName) : proto.Name;
var desc = recipe.SetDescription.HasValue ? Loc.GetString(recipe.SetDescription) : proto.Description;
recipe.Name = name;
recipe.Description = desc;
constructionProto.Name = name;
constructionProto.Description = desc;
_recipesMetadataCache.Add(constructionProto.ID, entityId);
} while (stack.Count > 0);
@@ -172,7 +169,7 @@ namespace Content.Client.Construction
"construction-ghost-examine-message",
("name", component.Prototype.Name)));
if (!PrototypeManager.TryIndex(component.Prototype.Graph, out var graph))
if (!PrototypeManager.Resolve(component.Prototype.Graph, out var graph))
return;
var startNode = graph.Nodes[component.Prototype.StartNode];

View File

@@ -510,7 +510,7 @@ namespace Content.Client.Construction.UI
foreach (var id in favorites)
{
if (_prototypeManager.TryIndex(id, out ConstructionPrototype? recipe, logError: false))
if (_prototypeManager.TryIndex(id, out ConstructionPrototype? recipe))
_favoritedRecipes.Add(recipe);
}

View File

@@ -150,7 +150,7 @@ public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponen
// If the damage container on our entity's DamageableComponent
// is not null, we can try to check through its groups.
if (damageComponent.DamageContainerID != null
&& _prototypeManager.TryIndex<DamageContainerPrototype>(damageComponent.DamageContainerID, out var damageContainer))
&& _prototypeManager.Resolve<DamageContainerPrototype>(damageComponent.DamageContainerID, out var damageContainer))
{
// Are we using damage overlay sprites by group?
// Check if the container matches the supported groups,

View File

@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared.DisplacementMap;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -10,6 +11,11 @@ public sealed class DisplacementMapSystem : EntitySystem
[Dependency] private readonly ISerializationManager _serialization = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
private static string? BuildDisplacementLayerKey(object key)
{
return key.ToString() is null ? null : $"{key}-displacement";
}
/// <summary>
/// Attempting to apply a displacement map to a specific layer of SpriteComponent
/// </summary>
@@ -19,21 +25,22 @@ public sealed class DisplacementMapSystem : EntitySystem
/// <param name="key">Unique layer key, which will determine which layer to apply displacement map to</param>
/// <param name="displacementKey">The key of the new displacement map layer added by this function.</param>
/// <returns></returns>
public bool TryAddDisplacement(DisplacementData data,
public bool TryAddDisplacement(
DisplacementData data,
Entity<SpriteComponent> sprite,
int index,
object key,
out string displacementKey)
[NotNullWhen(true)] out string? displacementKey
)
{
displacementKey = $"{key}-displacement";
if (key.ToString() is null)
displacementKey = BuildDisplacementLayerKey(key);
if (displacementKey is null)
return false;
if (data.ShaderOverride != null)
sprite.Comp.LayerSetShader(index, data.ShaderOverride);
EnsureDisplacementIsNotOnSprite(sprite, key);
_sprite.RemoveLayer(sprite.AsNullable(), displacementKey, false);
if (data.ShaderOverride is not null)
sprite.Comp.LayerSetShader(index, data.ShaderOverride);
//allows you not to write it every time in the YML
foreach (var pair in data.SizeMaps)
@@ -70,7 +77,11 @@ public sealed class DisplacementMapSystem : EntitySystem
}
var displacementLayer = _serialization.CreateCopy(displacementDataLayer, notNullableOverride: true);
displacementLayer.CopyToShaderParameters!.LayerKey = key.ToString() ?? "this is impossible";
// This previously assigned a string reading "this is impossible" if key.ToString eval'd to false.
// However, for the sake of sanity, we've changed this to assert non-null - !.
// If this throws an error, we're not sorry. Nanotrasen thanks you for your service fixing this bug.
displacementLayer.CopyToShaderParameters!.LayerKey = key.ToString()!;
_sprite.AddLayer(sprite.AsNullable(), displacementLayer, index);
_sprite.LayerMapSet(sprite.AsNullable(), displacementKey, index);
@@ -78,14 +89,18 @@ public sealed class DisplacementMapSystem : EntitySystem
return true;
}
/// <inheritdoc cref="TryAddDisplacement"/>
[Obsolete("Use the Entity<SpriteComponent> overload")]
public bool TryAddDisplacement(DisplacementData data,
SpriteComponent sprite,
int index,
object key,
out string displacementKey)
/// <summary>
/// Ensures that the displacement map associated with the given layer key is not in the Sprite's LayerMap.
/// </summary>
/// <param name="sprite">The sprite to remove the displacement layer from.</param>
/// <param name="key">The key of the layer that is referenced by the displacement layer we want to remove.</param>
/// <param name="logMissing">Whether to report an error if the displacement map isn't on the sprite.</param>
public void EnsureDisplacementIsNotOnSprite(Entity<SpriteComponent> sprite, object key)
{
return TryAddDisplacement(data, (sprite.Owner, sprite), index, key, out displacementKey);
var displacementLayerKey = BuildDisplacementLayerKey(key);
if (displacementLayerKey is null)
return;
_sprite.RemoveLayer(sprite.AsNullable(), displacementLayerKey, false);
}
}

View File

@@ -142,7 +142,7 @@ public sealed class DoorSystem : SharedDoorSystem
private void UpdateSpriteLayers(Entity<SpriteComponent> sprite, string targetProto)
{
if (!_prototypeManager.TryIndex(targetProto, out var target))
if (!_prototypeManager.Resolve(targetProto, out var target))
return;
if (!target.TryGetComponent(out SpriteComponent? targetSprite, _componentFactory))

View File

@@ -1,25 +1,58 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Ghost.Roles;
using Content.Shared.Ghost.Roles.Components;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client.Ghost;
public sealed class GhostRoleRadioBoundUserInterface : BoundUserInterface
public sealed class GhostRoleRadioBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
private GhostRoleRadioMenu? _ghostRoleRadioMenu;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public GhostRoleRadioBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
}
private SimpleRadialMenu? _ghostRoleRadioMenu;
protected override void Open()
{
base.Open();
_ghostRoleRadioMenu = this.CreateWindow<GhostRoleRadioMenu>();
_ghostRoleRadioMenu.SetEntity(Owner);
_ghostRoleRadioMenu.SendGhostRoleRadioMessageAction += SendGhostRoleRadioMessage;
_ghostRoleRadioMenu = this.CreateWindow<SimpleRadialMenu>();
// The purpose of this radial UI is for ghost role radios that allow you to select
// more than one potential option, such as with kobolds/lizards.
// This means that it won't show anything if SelectablePrototypes is empty.
if (!EntMan.TryGetComponent<GhostRoleMobSpawnerComponent>(Owner, out var comp))
return;
var list = ConvertToButtons(comp.SelectablePrototypes);
_ghostRoleRadioMenu.SetButtons(list);
}
private IEnumerable<RadialMenuOptionBase> ConvertToButtons(List<ProtoId<GhostRolePrototype>> protoIds)
{
var list = new List<RadialMenuOptionBase>();
foreach (var ghostRoleProtoId in protoIds)
{
// For each prototype we find we want to create a button that uses the name of the ghost role
// as the hover tooltip, and the icon is taken from either the ghost role entityprototype
// or the indicated icon entityprototype.
if (!_prototypeManager.Resolve(ghostRoleProtoId, out var ghostRoleProto))
continue;
var option = new RadialMenuActionOption<ProtoId<GhostRolePrototype>>(SendGhostRoleRadioMessage, ghostRoleProtoId)
{
ToolTip = Loc.GetString(ghostRoleProto.Name),
// pick the icon if it exists, otherwise fallback to the ghost role's entity
IconSpecifier = ghostRoleProto.IconPrototype != null
&& _prototypeManager.Resolve(ghostRoleProto.IconPrototype, out var iconProto)
? RadialMenuIconSpecifier.With(iconProto)
: RadialMenuIconSpecifier.With(ghostRoleProto.EntityPrototype)
};
list.Add(option);
}
return list;
}
private void SendGhostRoleRadioMessage(ProtoId<GhostRolePrototype> protoId)

View File

@@ -1,8 +0,0 @@
<ui:RadialMenu
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True">
<ui:RadialContainer Name="Main">
</ui:RadialContainer>
</ui:RadialMenu>

View File

@@ -1,105 +0,0 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Ghost.Roles;
using Content.Shared.Ghost.Roles.Components;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using System.Numerics;
namespace Content.Client.Ghost;
public sealed partial class GhostRoleRadioMenu : RadialMenu
{
[Dependency] private readonly EntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public event Action<ProtoId<GhostRolePrototype>>? SendGhostRoleRadioMessageAction;
public EntityUid Entity { get; set; }
public GhostRoleRadioMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
}
public void SetEntity(EntityUid uid)
{
Entity = uid;
RefreshUI();
}
private void RefreshUI()
{
// The main control that will contain all the clickable options
var main = FindControl<RadialContainer>("Main");
// The purpose of this radial UI is for ghost role radios that allow you to select
// more than one potential option, such as with kobolds/lizards.
// This means that it won't show anything if SelectablePrototypes is empty.
if (!_entityManager.TryGetComponent<GhostRoleMobSpawnerComponent>(Entity, out var comp))
return;
foreach (var ghostRoleProtoString in comp.SelectablePrototypes)
{
// For each prototype we find we want to create a button that uses the name of the ghost role
// as the hover tooltip, and the icon is taken from either the ghost role entityprototype
// or the indicated icon entityprototype.
if (!_prototypeManager.TryIndex<GhostRolePrototype>(ghostRoleProtoString, out var ghostRoleProto))
continue;
var button = new GhostRoleRadioMenuButton()
{
SetSize = new Vector2(64, 64),
ToolTip = Loc.GetString(ghostRoleProto.Name),
ProtoId = ghostRoleProto.ID,
};
var entProtoView = new EntityPrototypeView()
{
SetSize = new Vector2(48, 48),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Stretch = SpriteView.StretchMode.Fill
};
// pick the icon if it exists, otherwise fallback to the ghost role's entity
if (_prototypeManager.TryIndex(ghostRoleProto.IconPrototype, out var iconProto))
entProtoView.SetPrototype(iconProto);
else
entProtoView.SetPrototype(ghostRoleProto.EntityPrototype);
button.AddChild(entProtoView);
main.AddChild(button);
AddGhostRoleRadioMenuButtonOnClickActions(main);
}
}
private void AddGhostRoleRadioMenuButtonOnClickActions(Control control)
{
var mainControl = control as RadialContainer;
if (mainControl == null)
return;
foreach (var child in mainControl.Children)
{
var castChild = child as GhostRoleRadioMenuButton;
if (castChild == null)
continue;
castChild.OnButtonUp += _ =>
{
SendGhostRoleRadioMessageAction?.Invoke(castChild.ProtoId);
Close();
};
}
}
}
public sealed class GhostRoleRadioMenuButton : RadialMenuTextureButtonWithSector
{
public ProtoId<GhostRolePrototype> ProtoId { get; set; }
}

View File

@@ -5,14 +5,17 @@ using Content.Client.Guidebook.Richtext;
using Content.Client.Message;
using Content.Client.UserInterface.ControlExtensions;
using Content.Shared.Body.Prototypes;
using Content.Shared.CCVar;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Contraband;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -27,8 +30,10 @@ public sealed partial class GuideReagentEmbed : BoxContainer, IDocumentTag, ISea
[Dependency] private readonly IEntitySystemManager _systemManager = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
private readonly ChemistryGuideDataSystem _chemistryGuideData;
private readonly ContrabandSystem _contraband;
private readonly ISawmill _sawmill;
public IPrototype? RepresentedPrototype { get; private set; }
@@ -39,6 +44,7 @@ public sealed partial class GuideReagentEmbed : BoxContainer, IDocumentTag, ISea
IoCManager.InjectDependencies(this);
_sawmill = _logManager.GetSawmill("guidebook.reagent");
_chemistryGuideData = _systemManager.GetEntitySystem<ChemistryGuideDataSystem>();
_contraband = _systemManager.GetEntitySystem<ContrabandSystem>();
MouseFilter = MouseFilterMode.Stop;
}
@@ -204,6 +210,25 @@ public sealed partial class GuideReagentEmbed : BoxContainer, IDocumentTag, ISea
description.PushNewline();
description.AddMarkupOrThrow(Loc.GetString("guidebook-reagent-physical-description",
("description", reagent.LocalizedPhysicalDescription)));
if (_config.GetCVar(CCVars.ContrabandExamine))
{
// Department-restricted text
if (reagent.AllowedJobs.Count > 0 || reagent.AllowedDepartments.Count > 0)
{
description.PushNewline();
description.AddMarkupPermissive(
_contraband.GenerateDepartmentExamineMessage(reagent.AllowedDepartments, reagent.AllowedJobs, ContrabandItemType.Reagent));
}
// Other contraband text
else if (reagent.ContrabandSeverity != null &&
_prototype.Resolve(reagent.ContrabandSeverity.Value, out var severity))
{
description.PushNewline();
description.AddMarkupPermissive(Loc.GetString(severity.ExamineText, ("type", ContrabandItemType.Reagent)));
}
}
ReagentDescription.SetMessage(description);
}

View File

@@ -53,7 +53,7 @@ public sealed partial class DocumentParsingManager
public bool TryAddMarkup(Control control, ProtoId<GuideEntryPrototype> entryId, bool log = true)
{
if (!_prototype.TryIndex(entryId, out var entry))
if (!_prototype.Resolve(entryId, out var entry))
return false;
using var file = _resourceManager.ContentFileReadText(entry.Text);

View File

@@ -289,25 +289,26 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
private void RemoveMarking(Marking marking, Entity<SpriteComponent> spriteEnt)
{
if (!_markingManager.TryGetMarking(marking, out var prototype))
{
return;
}
foreach (var sprite in prototype.Sprites)
{
if (sprite is not SpriteSpecifier.Rsi rsi)
{
continue;
}
var layerId = $"{marking.MarkingId}-{rsi.RsiState}";
if (!_sprite.LayerMapTryGet(spriteEnt.AsNullable(), layerId, out var index, false))
{
continue;
}
_sprite.LayerMapRemove(spriteEnt.AsNullable(), layerId);
_sprite.RemoveLayer(spriteEnt.AsNullable(), index);
// If this marking is one that can be displaced, we need to remove the displacement as well; otherwise
// altering a marking at runtime can lead to the renderer falling over.
// The Vulps must be shaved.
// (https://github.com/space-wizards/space-station-14/issues/40135).
if (prototype.CanBeDisplaced)
_displacement.EnsureDisplacementIsNotOnSprite(spriteEnt, layerId);
}
}
@@ -346,9 +347,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
var sprite = entity.Comp2;
if (!_sprite.LayerMapTryGet((entity.Owner, sprite), markingPrototype.BodyPart, out var targetLayer, false))
{
return;
}
visible &= !IsHidden(humanoid, markingPrototype.BodyPart);
visible &= humanoid.BaseLayers.TryGetValue(markingPrototype.BodyPart, out var setting)
@@ -359,9 +358,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
var markingSprite = markingPrototype.Sprites[j];
if (markingSprite is not SpriteSpecifier.Rsi rsi)
{
continue;
}
return;
var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
@@ -375,26 +372,18 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
_sprite.LayerSetVisible((entity.Owner, sprite), layerId, visible);
if (!visible || setting == null) // this is kinda implied
{
continue;
}
// Okay so if the marking prototype is modified but we load old marking data this may no longer be valid
// and we need to check the index is correct.
// So if that happens just default to white?
if (colors != null && j < colors.Count)
{
_sprite.LayerSetColor((entity.Owner, sprite), layerId, colors[j]);
}
else
{
_sprite.LayerSetColor((entity.Owner, sprite), layerId, Color.White);
}
if (humanoid.MarkingsDisplacement.TryGetValue(markingPrototype.BodyPart, out var displacementData) && markingPrototype.CanBeDisplaced)
{
_displacement.TryAddDisplacement(displacementData, (entity.Owner, sprite), targetLayer + j + 1, layerId, out _);
}
}
}

View File

@@ -28,7 +28,7 @@ public sealed class ImplanterSystem : SharedImplanterSystem
Dictionary<string, string> implants = new();
foreach (var implant in component.DeimplantWhitelist)
{
if (_proto.TryIndex(implant, out var proto))
if (_proto.Resolve(implant, out var proto))
implants.Add(proto.ID, proto.Name);
}

View File

@@ -62,7 +62,7 @@ public sealed partial class ChameleonControllerMenu : FancyWindow
// Go through every outfit and add them to the correct department.
foreach (var outfit in _outfits)
{
_prototypeManager.TryIndex(outfit.Job, out var jobProto);
_prototypeManager.Resolve(outfit.Job, out var jobProto);
var name = outfit.LoadoutName ?? outfit.Name ?? jobProto?.Name ?? "Prototype has no name or job.";

View File

@@ -49,7 +49,7 @@ public sealed class ImplanterStatusControl : Control
if (_parent.CurrentMode == ImplanterToggleMode.Draw)
{
string implantName = _parent.DeimplantChosen != null
? (_prototype.TryIndex(_parent.DeimplantChosen.Value, out EntityPrototype? implantProto) ? implantProto.Name : Loc.GetString("implanter-empty-text"))
? (_prototype.Resolve(_parent.DeimplantChosen.Value, out EntityPrototype? implantProto) ? implantProto.Name : Loc.GetString("implanter-empty-text"))
: Loc.GetString("implanter-empty-text");
_label.SetMarkup(Loc.GetString("implanter-label-draw",

View File

@@ -97,7 +97,7 @@ public sealed partial class LatheMenu : DefaultWindow
var recipesToShow = new List<LatheRecipePrototype>();
foreach (var recipe in Recipes)
{
if (!_prototypeManager.TryIndex(recipe, out var proto))
if (!_prototypeManager.Resolve(recipe, out var proto))
continue;
// Category filtering
@@ -183,7 +183,7 @@ public sealed partial class LatheMenu : DefaultWindow
foreach (var (id, amount) in prototype.Materials)
{
if (!_prototypeManager.TryIndex(id, out var proto))
if (!_prototypeManager.Resolve(id, out var proto))
continue;
var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, multiplier);

View File

@@ -362,7 +362,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
{
foreach (var loadout in group)
{
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
if (!_prototypeManager.Resolve(loadout.Prototype, out var loadoutProto))
continue;
_spawn.EquipStartingGear(uid, loadoutProto);
@@ -385,14 +385,14 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
{
foreach (var loadout in loadouts)
{
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
if (!_prototypeManager.Resolve(loadout.Prototype, out var loadoutProto))
continue;
// TODO: Need some way to apply starting gear to an entity and replace existing stuff coz holy fucking shit dude.
foreach (var slot in slots)
{
// Try startinggear first
if (_prototypeManager.TryIndex(loadoutProto.StartingGear, out var loadoutGear))
if (_prototypeManager.Resolve(loadoutProto.StartingGear, out var loadoutGear))
{
var itemType = ((IEquipmentLoadout) loadoutGear).GetGear(slot.Name);
@@ -427,7 +427,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
}
}
if (!_prototypeManager.TryIndex(job.StartingGear, out var gear))
if (!_prototypeManager.Resolve(job.StartingGear, out var gear))
return;
foreach (var slot in slots)

View File

@@ -810,7 +810,7 @@ namespace Content.Client.Lobby.UI
if (_prototypeManager.HasIndex<GuideEntryPrototype>(species))
page = new ProtoId<GuideEntryPrototype>(species.Id); // Gross. See above todo comment.
if (_prototypeManager.TryIndex(DefaultSpeciesGuidebook, out var guideRoot))
if (_prototypeManager.Resolve(DefaultSpeciesGuidebook, out var guideRoot))
{
var dict = new Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry>();
dict.Add(DefaultSpeciesGuidebook, guideRoot);
@@ -1291,7 +1291,7 @@ namespace Content.Client.Lobby.UI
var sexes = new List<Sex>();
// add species sex options, default to just none if we are in bizzaro world and have no species
if (_prototypeManager.TryIndex<SpeciesPrototype>(Profile.Species, out var speciesProto))
if (_prototypeManager.Resolve<SpeciesPrototype>(Profile.Species, out var speciesProto))
{
foreach (var sex in speciesProto.Sexes)
{
@@ -1384,7 +1384,7 @@ namespace Content.Client.Lobby.UI
if (species is null)
return;
if (!_prototypeManager.TryIndex<SpeciesPrototype>(species, out var speciesProto))
if (!_prototypeManager.Resolve<SpeciesPrototype>(species, out var speciesProto))
return;
// Don't display the info button if no guide entry is found

View File

@@ -40,7 +40,7 @@ public sealed partial class LoadoutContainer : BoxContainer
SelectButton.TooltipSupplier = _ => tooltip;
}
if (_protoManager.TryIndex(proto, out var loadProto))
if (_protoManager.Resolve(proto, out var loadProto))
{
var ent = loadProto.DummyEntity ?? _entManager.System<LoadoutSystem>().GetFirstOrNull(loadProto);

View File

@@ -62,7 +62,7 @@ public sealed partial class LoadoutGroupContainer : BoxContainer
});
}
if (protoMan.TryIndex(loadout.Role, out var roleProto) && roleProto.Points != null && loadout.Points != null)
if (protoMan.Resolve(loadout.Role, out var roleProto) && roleProto.Points != null && loadout.Points != null)
{
RestrictionsContainer.AddChild(new Label()
{
@@ -112,14 +112,14 @@ public sealed partial class LoadoutGroupContainer : BoxContainer
})
.ToList();
/*
* Determine which element should be displayed first:
* - If any element is currently selected (its button is pressed), use it.
* - Otherwise, fallback to the first element in the list.
*
* This moves the selected item outside of the sublist for better usability,
* making it easier for players to quickly toggle loadout options (e.g. clothing, accessories)
* without having to search inside expanded subgroups.
/*
* Determine which element should be displayed first:
* - If any element is currently selected (its button is pressed), use it.
* - Otherwise, fallback to the first element in the list.
*
* This moves the selected item outside of the sublist for better usability,
* making it easier for players to quickly toggle loadout options (e.g. clothing, accessories)
* without having to search inside expanded subgroups.
*/
var firstElement = uiElements.FirstOrDefault(e => e.Select.Pressed) ?? uiElements[0];
@@ -195,8 +195,8 @@ public sealed partial class LoadoutGroupContainer : BoxContainer
/// <summary>
/// Creates a UI container for a single Loadout item.
///
/// This method was extracted from RefreshLoadouts because the logic for creating
/// individual loadout items is used multiple times inside that method, and duplicating
/// This method was extracted from RefreshLoadouts because the logic for creating
/// individual loadout items is used multiple times inside that method, and duplicating
/// the code made it harder to maintain.
///
/// Logic:

View File

@@ -68,7 +68,7 @@ public sealed partial class LoadoutWindow : FancyWindow
{
foreach (var group in proto.Groups)
{
if (!protoManager.TryIndex(group, out var groupProto))
if (!protoManager.Resolve(group, out var groupProto))
continue;
if (groupProto.Hidden)

View File

@@ -64,7 +64,9 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
if (!LocalizedNames.TryGetValue(netEntity, out var name))
name = "Unknown";
var message = name + "\nLocation: [x = " + MathF.Round(blip.Coordinates.X) + ", y = " + MathF.Round(blip.Coordinates.Y) + "]";
var message = name + "\n" + Loc.GetString("navmap-location",
("x", MathF.Round(blip.Coordinates.X)),
("y", MathF.Round(blip.Coordinates.Y)));
_trackedEntityLabel.Text = message;
_trackedEntityPanel.Visible = true;

View File

@@ -57,7 +57,7 @@ public sealed class EntityHealthBarOverlay : Overlay
const float scale = 1f;
var scaleMatrix = Matrix3Helpers.CreateScale(new Vector2(scale, scale));
var rotationMatrix = Matrix3Helpers.CreateRotation(-rotation);
_prototype.TryIndex(StatusIcon, out var statusIcon);
_prototype.Resolve(StatusIcon, out var statusIcon);
var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>();
while (query.MoveNext(out var uid,

View File

@@ -22,7 +22,7 @@ public sealed class ShowCriminalRecordIconsSystem : EquipmentHudSystem<ShowCrimi
if (!IsActive)
return;
if (_prototype.TryIndex(component.StatusIcon, out var iconPrototype))
if (_prototype.Resolve(component.StatusIcon, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype);
}
}

View File

@@ -78,9 +78,9 @@ public sealed class ShowHealthIconsSystem : EquipmentHudSystem<ShowHealthIconsCo
if (TryComp<MobStateComponent>(entity, out var state))
{
// Since there is no MobState for a rotting mob, we have to deal with this case first.
if (HasComp<RottingComponent>(entity) && _prototypeMan.TryIndex(damageableComponent.RottingIcon, out var rottingIcon))
if (HasComp<RottingComponent>(entity) && _prototypeMan.Resolve(damageableComponent.RottingIcon, out var rottingIcon))
result.Add(rottingIcon);
else if (damageableComponent.HealthIcons.TryGetValue(state.CurrentState, out var value) && _prototypeMan.TryIndex(value, out var icon))
else if (damageableComponent.HealthIcons.TryGetValue(state.CurrentState, out var value) && _prototypeMan.Resolve(value, out var icon))
result.Add(icon);
}
}

View File

@@ -51,7 +51,7 @@ public sealed class ShowJobIconsSystem : EquipmentHudSystem<ShowJobIconsComponen
}
}
if (_prototype.TryIndex(iconId, out var iconPrototype))
if (_prototype.Resolve(iconId, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype);
else
Log.Error($"Invalid job icon prototype: {iconPrototype}");

View File

@@ -23,7 +23,7 @@ public sealed class ShowMindShieldIconsSystem : EquipmentHudSystem<ShowMindShiel
{
if(!IsActive)
return;
if (component.IsEnabled && _prototype.TryIndex(component.MindShieldStatusIcon, out var fakeStatusIconPrototype))
if (component.IsEnabled && _prototype.Resolve(component.MindShieldStatusIcon, out var fakeStatusIconPrototype))
ev.StatusIcons.Add(fakeStatusIconPrototype);
}
@@ -32,7 +32,7 @@ public sealed class ShowMindShieldIconsSystem : EquipmentHudSystem<ShowMindShiel
if (!IsActive)
return;
if (_prototype.TryIndex(component.MindShieldStatusIcon, out var iconPrototype))
if (_prototype.Resolve(component.MindShieldStatusIcon, out var iconPrototype))
ev.StatusIcons.Add(iconPrototype);
}
}

View File

@@ -65,7 +65,7 @@ public sealed partial class StencilOverlay : Overlay
{
foreach (var (proto, weather) in comp.Weather)
{
if (!_protoManager.TryIndex<WeatherPrototype>(proto, out var weatherProto))
if (!_protoManager.Resolve<WeatherPrototype>(proto, out var weatherProto))
continue;
var alpha = _weather.GetPercent(weather, mapUid);

View File

@@ -20,7 +20,7 @@
<Label Text="{Loc 'apc-menu-breaker-label'}" HorizontalExpand="True"
StyleClasses="StatusFieldTitle" MinWidth="120"/>
<BoxContainer Orientation="Horizontal" MinWidth="90">
<Button Name="BreakerButton" Text="{Loc 'apc-menu-breaker-button'}" HorizontalExpand="True"/>
<Button Name="BreakerButton" Text="{Loc 'apc-menu-breaker-button'}" HorizontalExpand="True" ToggleMode="True"/>
</BoxContainer>
<!--Charging Status-->
<Label Text="{Loc 'apc-menu-external-label'}" StyleClasses="StatusFieldTitle" MinWidth="120" />

View File

@@ -51,10 +51,10 @@ public sealed class RCDMenuBoundUserInterface : BoundUserInterface
_menu.OpenOverMouseScreenPosition();
}
private IEnumerable<RadialMenuOption> ConvertToButtons(HashSet<ProtoId<RCDPrototype>> prototypes)
private IEnumerable<RadialMenuOptionBase> ConvertToButtons(HashSet<ProtoId<RCDPrototype>> prototypes)
{
Dictionary<string, List<RadialMenuActionOption>> buttonsByCategory = new();
ValueList<RadialMenuActionOption> topLevelActions = new();
Dictionary<string, List<RadialMenuActionOptionBase>> buttonsByCategory = new();
ValueList<RadialMenuActionOptionBase> topLevelActions = new();
foreach (var protoId in prototypes)
{
var prototype = _prototypeManager.Index(protoId);
@@ -62,7 +62,7 @@ public sealed class RCDMenuBoundUserInterface : BoundUserInterface
{
var topLevelActionOption = new RadialMenuActionOption<RCDPrototype>(HandleMenuOptionClick, prototype)
{
Sprite = prototype.Sprite,
IconSpecifier = RadialMenuIconSpecifier.With(prototype.Sprite),
ToolTip = GetTooltip(prototype)
};
topLevelActions.Add(topLevelActionOption);
@@ -74,26 +74,26 @@ public sealed class RCDMenuBoundUserInterface : BoundUserInterface
if (!buttonsByCategory.TryGetValue(prototype.Category, out var list))
{
list = new List<RadialMenuActionOption>();
list = new List<RadialMenuActionOptionBase>();
buttonsByCategory.Add(prototype.Category, list);
}
var actionOption = new RadialMenuActionOption<RCDPrototype>(HandleMenuOptionClick, prototype)
{
Sprite = prototype.Sprite,
IconSpecifier = RadialMenuIconSpecifier.With(prototype.Sprite),
ToolTip = GetTooltip(prototype)
};
list.Add(actionOption);
}
var models = new RadialMenuOption[buttonsByCategory.Count + topLevelActions.Count];
var models = new RadialMenuOptionBase[buttonsByCategory.Count + topLevelActions.Count];
var i = 0;
foreach (var (key, list) in buttonsByCategory)
{
var groupInfo = PrototypesGroupingInfo[key];
models[i] = new RadialMenuNestedLayerOption(list)
{
Sprite = groupInfo.Sprite,
IconSpecifier = RadialMenuIconSpecifier.With(groupInfo.Sprite),
ToolTip = Loc.GetString(groupInfo.Tooltip)
};
i++;
@@ -125,8 +125,10 @@ public sealed class RCDMenuBoundUserInterface : BoundUserInterface
var name = Loc.GetString(proto.SetName);
if (proto.Prototype != null &&
_prototypeManager.TryIndex(proto.Prototype, out var entProto, logError: false))
_prototypeManager.Resolve(proto.Prototype, out var entProto))
{
name = entProto.Name;
}
msg = Loc.GetString("rcd-component-change-build-mode", ("name", name));
}
@@ -142,7 +144,7 @@ public sealed class RCDMenuBoundUserInterface : BoundUserInterface
if (proto.Mode is RcdMode.ConstructTile or RcdMode.ConstructObject
&& proto.Prototype != null
&& _prototypeManager.TryIndex(proto.Prototype, out var entProto, logError: false))
&& _prototypeManager.Resolve(proto.Prototype, out var entProto))
{
tooltip = Loc.GetString(entProto.Name);
}

View File

@@ -42,7 +42,7 @@ public sealed partial class IntercomMenu : FancyWindow
for (var i = 0; i < entity.Comp.SupportedChannels.Count; i++)
{
var channel = entity.Comp.SupportedChannels[i];
if (!_prototype.TryIndex(channel, out var prototype))
if (!_prototype.Resolve(channel, out var prototype))
continue;
_channels.Add(channel);

View File

@@ -25,13 +25,13 @@ public sealed class RevolutionarySystem : SharedRevolutionarySystem
if (HasComp<HeadRevolutionaryComponent>(ent))
return;
if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
if (_prototype.Resolve(ent.Comp.StatusIcon, out var iconPrototype))
args.StatusIcons.Add(iconPrototype);
}
private void GetHeadRevIcon(Entity<HeadRevolutionaryComponent> ent, ref GetStatusIconsEvent args)
{
if (_prototype.TryIndex(ent.Comp.StatusIcon, out var iconPrototype))
if (_prototype.Resolve(ent.Comp.StatusIcon, out var iconPrototype))
args.StatusIcons.Add(iconPrototype);
}
}

View File

@@ -0,0 +1,5 @@
using Content.Shared.Shuttles.Systems;
namespace Content.Client.Shuttles.Systems;
public sealed partial class EmergencyShuttleSystem : SharedEmergencyShuttleSystem;

View File

@@ -23,15 +23,15 @@ public sealed class StationAiBoundUserInterface(EntityUid owner, Enum uiKey) : B
_menu.Open();
}
private IEnumerable<RadialMenuActionOption> ConvertToButtons(IReadOnlyList<StationAiRadial> actions)
private IEnumerable<RadialMenuActionOptionBase> ConvertToButtons(IReadOnlyList<StationAiRadial> actions)
{
var models = new RadialMenuActionOption[actions.Count];
var models = new RadialMenuActionOptionBase[actions.Count];
for (int i = 0; i < actions.Count; i++)
{
var action = actions[i];
models[i] = new RadialMenuActionOption<BaseStationAiAction>(HandleRadialMenuClick, action.Event)
{
Sprite = action.Sprite,
IconSpecifier = RadialMenuIconSpecifier.With(action.Sprite),
ToolTip = action.Tooltip
};
}

View File

@@ -44,7 +44,7 @@ public sealed partial class StationAiCustomizationMenu : FancyWindow
StationAiCustomizationPrototype? selectedPrototype = null;
if (stationAiCustomization?.ProtoIds.TryGetValue(groupPrototype, out var selectedProtoId) == true)
_protoManager.TryIndex(selectedProtoId, out selectedPrototype);
_protoManager.Resolve(selectedProtoId, out selectedPrototype);
_buttonGroups[groupPrototype] = new ButtonGroup();
_groupContainers[groupPrototype] = new StationAiCustomizationGroupContainer(groupPrototype, selectedPrototype, _buttonGroups[groupPrototype], this, _protoManager);
@@ -76,7 +76,7 @@ public sealed partial class StationAiCustomizationMenu : FancyWindow
// Create UI entries for all customization in the group
foreach (var protoId in groupPrototype.ProtoIds)
{
if (!protoManager.TryIndex(protoId, out var prototype))
if (!protoManager.Resolve(protoId, out var prototype))
continue;
var entry = new StationAiCustomizationEntryContainer(groupPrototype, prototype, buttonGroup, menu);

View File

@@ -65,7 +65,7 @@ public sealed class SprayPainterSystem : SharedSprayPainterSystem
var groupList = new List<string>();
foreach (var groupId in category.Groups)
{
if (!Proto.TryIndex(groupId, out var group))
if (!Proto.Resolve(groupId, out var group))
continue;
groupList.Add(groupId);

View File

@@ -42,7 +42,7 @@ public sealed class EntityStorageVisualizerSystem : VisualizerSystem<EntityStora
var forceRedrawBase = false;
if (AppearanceSystem.TryGetData<string>(uid, PaintableVisuals.Prototype, out var prototype, args.Component))
{
if (_prototypeManager.TryIndex(prototype, out var proto))
if (_prototypeManager.Resolve(prototype, out var proto))
{
if (proto.TryGetComponent(out SpriteComponent? sprite, _componentFactory))
{

View File

@@ -33,7 +33,7 @@ public sealed partial class StoreWithdrawWindow : DefaultWindow
_validCurrencies.Clear();
foreach (var currency in balance)
{
if (!_prototypeManager.TryIndex(currency.Key, out var proto))
if (!_prototypeManager.Resolve(currency.Key, out var proto))
continue;
_validCurrencies.Add(proto, currency.Value);

View File

@@ -10,7 +10,7 @@
<PanelContainer StyleClasses="WindowHeadingBackground" />
<BoxContainer Margin="4 2 8 0" Orientation="Horizontal">
<Label Name="WindowTitle"
HorizontalExpand="True" VAlign="Center" StyleClasses="FancyWindowTitle" />
HorizontalExpand="True" VAlign="Center" StyleClasses="FancyWindowTitle" ClipText="true" />
<TextureButton Name="HelpButton" StyleClasses="windowHelpButton" VerticalAlignment="Center" Disabled="True" Visible="False" Access="Public" />
<TextureButton Name="CloseButton" StyleClasses="windowCloseButton"
VerticalAlignment="Center" />

View File

@@ -229,10 +229,10 @@ public class RadialMenu : BaseWindow
/// from interactions.
/// </summary>
[Virtual]
public class RadialMenuTextureButtonBase : TextureButton
public abstract class RadialMenuButtonBase : BaseButton
{
/// <inheritdoc />
protected RadialMenuTextureButtonBase()
protected RadialMenuButtonBase()
{
EnableAllKeybinds = true;
}
@@ -242,7 +242,9 @@ public class RadialMenuTextureButtonBase : TextureButton
{
if (args.Function == EngineKeyFunctions.UIClick
|| args.Function == ContentKeyFunctions.AltActivateItemInWorld)
{
base.KeyBindUp(args);
}
}
}
@@ -253,8 +255,14 @@ public class RadialMenuTextureButtonBase : TextureButton
/// works only if control have parent, and ActiveContainer property is set.
/// Also considers all space outside of radial menu buttons as itself for clicking.
/// </summary>
public sealed class RadialMenuContextualCentralTextureButton : RadialMenuTextureButtonBase
public sealed class RadialMenuContextualCentralTextureButton : TextureButton
{
/// <inheritdoc />
public RadialMenuContextualCentralTextureButton()
{
EnableAllKeybinds = true;
}
public float InnerRadius { get; set; }
public Vector2? ParentCenter { get; set; }
@@ -271,15 +279,25 @@ public sealed class RadialMenuContextualCentralTextureButton : RadialMenuTexture
var innerRadiusSquared = InnerRadius * InnerRadius;
// comparing to squared values is faster then making sqrt
// comparing to squared values is faster, then making sqrt
return distSquared < innerRadiusSquared;
}
/// <inheritdoc />
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
{
if (args.Function == EngineKeyFunctions.UIClick
|| args.Function == ContentKeyFunctions.AltActivateItemInWorld)
{
base.KeyBindUp(args);
}
}
}
/// <summary>
/// Menu button for outer area of radial menu (covers everything 'outside').
/// </summary>
public sealed class RadialMenuOuterAreaButton : RadialMenuTextureButtonBase
public sealed class RadialMenuOuterAreaButton : RadialMenuButtonBase
{
public float OuterRadius { get; set; }
@@ -303,7 +321,7 @@ public sealed class RadialMenuOuterAreaButton : RadialMenuTextureButtonBase
}
[Virtual]
public class RadialMenuTextureButton : RadialMenuTextureButtonBase
public class RadialMenuButton : RadialMenuButtonBase
{
/// <summary>
/// Upon clicking this button the radial menu will be moved to the layer of this control.
@@ -319,9 +337,8 @@ public class RadialMenuTextureButton : RadialMenuTextureButtonBase
/// <summary>
/// A simple texture button that can move the user to a different layer within a radial menu
/// </summary>
public RadialMenuTextureButton()
public RadialMenuButton()
{
EnableAllKeybinds = true;
OnButtonUp += OnClicked;
}
@@ -391,7 +408,7 @@ public interface IRadialMenuItemWithSector
}
[Virtual]
public class RadialMenuTextureButtonWithSector : RadialMenuTextureButton, IRadialMenuItemWithSector
public class RadialMenuButtonWithSector : RadialMenuButton, IRadialMenuItemWithSector
{
private Vector2[]? _sectorPointsForDrawing;
@@ -500,7 +517,7 @@ public class RadialMenuTextureButtonWithSector : RadialMenuTextureButton, IRadia
/// <summary>
/// A simple texture button that can move the user to a different layer within a radial menu
/// </summary>
public RadialMenuTextureButtonWithSector()
public RadialMenuButtonWithSector()
{
}

View File

@@ -7,6 +7,8 @@ using Robust.Client.GameObjects;
using Robust.Shared.Timing;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Input;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Prototypes;
namespace Content.Client.UserInterface.Controls;
@@ -30,7 +32,7 @@ public sealed partial class SimpleRadialMenu : RadialMenu
_attachMenuToEntity = owner;
}
public void SetButtons(IEnumerable<RadialMenuOption> models, SimpleRadialMenuSettings? settings = null)
public void SetButtons(IEnumerable<RadialMenuOptionBase> models, SimpleRadialMenuSettings? settings = null)
{
ClearExistingChildrenRadialButtons();
@@ -45,7 +47,7 @@ public sealed partial class SimpleRadialMenu : RadialMenu
}
private void Fill(
IEnumerable<RadialMenuOption> models,
IEnumerable<RadialMenuOptionBase> models,
SpriteSystem sprites,
ICollection<Control> rootControlChildren,
SimpleRadialMenuSettings settings
@@ -77,7 +79,7 @@ public sealed partial class SimpleRadialMenu : RadialMenu
}
}
private RadialMenuTextureButton RecursiveContainerExtraction(
private RadialMenuButton RecursiveContainerExtraction(
SpriteSystem sprites,
ICollection<Control> rootControlChildren,
RadialMenuNestedLayerOption model,
@@ -112,8 +114,8 @@ public sealed partial class SimpleRadialMenu : RadialMenu
return thisLayerLinkButton;
}
private RadialMenuTextureButton ConvertToButton(
RadialMenuOption model,
private RadialMenuButton ConvertToButton(
RadialMenuOptionBase model,
SpriteSystem sprites,
SimpleRadialMenuSettings settings,
bool haveNested
@@ -121,29 +123,26 @@ public sealed partial class SimpleRadialMenu : RadialMenu
{
var button = settings.UseSectors
? ConvertToButtonWithSector(model, settings)
: new RadialMenuTextureButton();
: new RadialMenuButton();
button.SetSize = new Vector2(64f, 64f);
button.ToolTip = model.ToolTip;
if (model.Sprite != null)
var imageControl = model.IconSpecifier switch
{
var scale = Vector2.One;
RadialMenuTextureIconSpecifier textureSpecifier => CreateTexture(textureSpecifier.Sprite, sprites),
RadialMenuEntityIconSpecifier entitySpecifier => CreateSpriteView(entitySpecifier.Entity),
RadialMenuEntityPrototypeIconSpecifier entProtoSpecifier => CreateEntityPrototypeView(entProtoSpecifier.ProtoId),
_ => null
};
var texture = sprites.Frame0(model.Sprite);
if (texture.Width <= 32)
{
scale *= 2;
}
if(imageControl != null)
button.AddChild(imageControl);
button.TextureNormal = texture;
button.Scale = scale;
}
if (model is RadialMenuActionOption actionOption)
if (model is RadialMenuActionOptionBase actionOption)
{
button.OnPressed += _ =>
{
actionOption.OnPressed?.Invoke();
if(!haveNested)
if (!haveNested)
Close();
};
}
@@ -151,9 +150,53 @@ public sealed partial class SimpleRadialMenu : RadialMenu
return button;
}
private static RadialMenuTextureButtonWithSector ConvertToButtonWithSector(RadialMenuOption model, SimpleRadialMenuSettings settings)
private Control CreateEntityPrototypeView(EntProtoId protoId)
{
var button = new RadialMenuTextureButtonWithSector
var entProtoView = new EntityPrototypeView
{
SetSize = new Vector2(48, 48),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Stretch = SpriteView.StretchMode.Fill,
};
entProtoView.SetPrototype(protoId);
return entProtoView;
}
private static Control CreateSpriteView(EntityUid entityForSpriteView)
{
var entView = new SpriteView
{
SetSize = new Vector2(48, 48),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Stretch = SpriteView.StretchMode.Fill,
};
entView.SetEntity(entityForSpriteView);
return entView;
}
private static Control CreateTexture(SpriteSpecifier spriteSpecifier, SpriteSystem sprites)
{
var scale = Vector2.One;
var texture = sprites.Frame0(spriteSpecifier);
if (texture.Width <= 32)
{
scale *= 2;
}
var imageControl = new TextureRect()
{
Texture = texture,
TextureScale = scale
};
return imageControl;
}
private static RadialMenuButtonWithSector ConvertToButtonWithSector(RadialMenuOptionBase model, SimpleRadialMenuSettings settings)
{
var button = new RadialMenuButtonWithSector
{
DrawBorder = settings.DisplayBorders,
DrawBackground = !settings.NoBackground
@@ -228,32 +271,99 @@ public sealed partial class SimpleRadialMenu : RadialMenu
}
public abstract class RadialMenuOption
/// <summary>
/// Abstract representation of a way to specify icon in radial menu.
/// </summary>
public abstract record RadialMenuIconSpecifier
{
public string? ToolTip { get; init; }
/// <summary> Use entity prototype viewer. </summary>
public static RadialMenuIconSpecifier? With(EntProtoId? protoId)
{
if (protoId is null)
return null;
public SpriteSpecifier? Sprite { get; init; }
public Color? BackgroundColor { get; set; }
public Color? HoverBackgroundColor { get; set; }
return new RadialMenuEntityPrototypeIconSpecifier(protoId.Value);
}
/// <summary> Use simple texture icon. </summary>
public static RadialMenuIconSpecifier? With(SpriteSpecifier? sprite)
{
if (sprite == null)
return null;
return new RadialMenuTextureIconSpecifier(sprite);
}
/// <summary> Use entity sprite viewer. </summary>
public static RadialMenuIconSpecifier? With(EntityUid? entity)
{
if (entity == null)
return null;
return new RadialMenuEntityIconSpecifier(entity.Value);
}
}
public abstract class RadialMenuActionOption(Action onPressed) : RadialMenuOption
/// <summary> Marker that <see cref="SpriteView"/> should be used to display radial menu icon. </summary>
public sealed record RadialMenuEntityIconSpecifier(EntityUid Entity) : RadialMenuIconSpecifier;
/// <summary> Marker that <see cref="TextureRect"/> should be used to display radial menu icon. </summary>
public sealed record RadialMenuTextureIconSpecifier(SpriteSpecifier Sprite) : RadialMenuIconSpecifier;
/// <summary> Marker that <see cref="EntityPrototypeView"/> should be used to display radial menu icon. </summary>
public sealed record RadialMenuEntityPrototypeIconSpecifier(EntProtoId ProtoId) : RadialMenuIconSpecifier;
/// <summary> Container for common options for radial menu button. </summary>
public abstract class RadialMenuOptionBase
{
/// <summary> Tooltip to be displayed when button is hovered. </summary>
public string? ToolTip { get; init; }
/// <summary>
/// Color for button background.
/// Is used only with sector radial (<see cref="SimpleRadialMenuSettings.UseSectors"/>).
/// </summary>
public Color? BackgroundColor { get; set; }
/// <summary>
/// Color for button background when it is hovered.
/// Is used only with sector radial (<see cref="SimpleRadialMenuSettings.UseSectors"/>).
/// </summary>
public Color? HoverBackgroundColor { get; set; }
/// <summary>
/// Specifier that describes icon to be used for radial menu button.
/// </summary>
public RadialMenuIconSpecifier? IconSpecifier { get; set; }
}
/// <summary> Base type for model of radial menu button with some action on button pressed. </summary>
/// <param name="onPressed"></param>
public abstract class RadialMenuActionOptionBase(Action onPressed) : RadialMenuOptionBase
{
/// <summary> Action to be executed on button press. </summary>
public Action OnPressed { get; } = onPressed;
}
public sealed class RadialMenuActionOption<T>(Action<T> onPressed, T data)
: RadialMenuActionOption(onPressed: () => onPressed(data));
/// <summary> Strong-typed model for radial menu button with action, stores provided data to be used upon button press. </summary>
public sealed class RadialMenuActionOption<T>(Action<T> onPressed, T data) : RadialMenuActionOptionBase(onPressed: () => onPressed(data));
public sealed class RadialMenuNestedLayerOption(IReadOnlyCollection<RadialMenuOption> nested, float containerRadius = 100)
: RadialMenuOption
/// <summary>
/// Model for radial menu button that represents reference for next layer of radial buttons.
/// </summary>
/// <param name="nested">List of button models for next layer of menu.</param>
/// <param name="containerRadius">Radius for radial menu buttons of next layer.</param>
public sealed class RadialMenuNestedLayerOption(IReadOnlyCollection<RadialMenuOptionBase> nested, float containerRadius = 100) : RadialMenuOptionBase
{
/// <summary> Radius for radial menu buttons of next layer. </summary>
public float? ContainerRadius { get; } = containerRadius;
public IReadOnlyCollection<RadialMenuOption> Nested { get; } = nested;
/// <summary> List of button models for next layer of menu. </summary>
public IReadOnlyCollection<RadialMenuOptionBase> Nested { get; } = nested;
}
/// <summary>
/// Additional settings for radial menu render.
/// </summary>
public sealed class SimpleRadialMenuSettings
{
/// <summary>

View File

@@ -18,7 +18,6 @@ namespace Content.Client.UserInterface.Systems.Emotes;
[UsedImplicitly]
public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayState>
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
@@ -133,12 +132,12 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
_menu = null;
}
private IEnumerable<RadialMenuOption> ConvertToButtons(IEnumerable<EmotePrototype> emotePrototypes)
private IEnumerable<RadialMenuOptionBase> ConvertToButtons(IEnumerable<EmotePrototype> emotePrototypes)
{
var whitelistSystem = EntitySystemManager.GetEntitySystem<EntityWhitelistSystem>();
var player = _playerManager.LocalSession?.AttachedEntity;
Dictionary<EmoteCategory, List<RadialMenuOption>> emotesByCategory = new();
Dictionary<EmoteCategory, List<RadialMenuOptionBase>> emotesByCategory = new();
foreach (var emote in emotePrototypes)
{
if(emote.Category == EmoteCategory.Invalid)
@@ -158,19 +157,19 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
if (!emotesByCategory.TryGetValue(emote.Category, out var list))
{
list = new List<RadialMenuOption>();
list = new List<RadialMenuOptionBase>();
emotesByCategory.Add(emote.Category, list);
}
var actionOption = new RadialMenuActionOption<EmotePrototype>(HandleRadialButtonClick, emote)
{
Sprite = emote.Icon,
IconSpecifier = RadialMenuIconSpecifier.With(emote.Icon),
ToolTip = Loc.GetString(emote.Name)
};
list.Add(actionOption);
}
var models = new RadialMenuOption[emotesByCategory.Count];
var models = new RadialMenuOptionBase[emotesByCategory.Count];
var i = 0;
foreach (var (key, list) in emotesByCategory)
{
@@ -178,7 +177,7 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
models[i] = new RadialMenuNestedLayerOption(list)
{
Sprite = tuple.Sprite,
IconSpecifier = RadialMenuIconSpecifier.With(tuple.Sprite),
ToolTip = Loc.GetString(tuple.Tooltip)
};
i++;
@@ -189,6 +188,6 @@ public sealed class EmotesUIController : UIController, IOnStateChanged<GameplayS
private void HandleRadialButtonClick(EmotePrototype prototype)
{
_entityManager.RaisePredictiveEvent(new PlayEmoteMessage(prototype.ID));
EntityManager.RaisePredictiveEvent(new PlayEmoteMessage(prototype.ID));
}
}

View File

@@ -120,7 +120,7 @@ namespace Content.Client.VendingMachines.UI
{
var entry = inventory[i];
if (!_prototypeManager.TryIndex(entry.ID, out var prototype))
if (!_prototypeManager.Resolve(entry.ID, out var prototype))
{
_amounts[entry.ID] = 0;
continue;

View File

@@ -273,7 +273,7 @@ namespace Content.Client.Verbs.UI
if (verbElement.SubMenu == null)
{
var popupElement = new ConfirmationMenuElement(verb, "Confirm");
var popupElement = new ConfirmationMenuElement(verb, Loc.GetString("generic-confirm"));
verbElement.SubMenu = new ContextMenuPopup(_context, verbElement);
_context.AddElement(verbElement.SubMenu, popupElement);
}

View File

@@ -146,8 +146,8 @@ public sealed class SuicideCommandTests
mobThresholdsComp = entManager.GetComponent<MobThresholdsComponent>(player);
damageableComp = entManager.GetComponent<DamageableComponent>(player);
if (protoMan.TryIndex(DamageType, out var slashProto))
damageableSystem.TryChangeDamage(player, new DamageSpecifier(slashProto, FixedPoint2.New(46.5)));
var slashProto = protoMan.Index(DamageType);
damageableSystem.TryChangeDamage(player, new DamageSpecifier(slashProto, FixedPoint2.New(46.5)));
});
// Check that running the suicide command kills the player

View File

@@ -27,8 +27,11 @@ public sealed class ContrabandTest
if (!proto.TryGetComponent<ContrabandComponent>(out var contraband, componentFactory))
continue;
Assert.That(protoMan.TryIndex(contraband.Severity, out var severity, false),
@$"{proto.ID} has a ContrabandComponent with a unknown severity.");
if (!protoMan.TryIndex(contraband.Severity, out var severity))
{
Assert.Fail($"{proto.ID} has a ContrabandComponent with a unknown severity.");
continue;
}
if (!severity.ShowDepartmentsAndJobs)
continue;

View File

@@ -88,14 +88,18 @@ public sealed class LatheTest
// Check each recipe assigned to this lathe
foreach (var recipeId in recipes)
{
Assert.That(protoMan.TryIndex(recipeId, out var recipeProto));
if (!protoMan.TryIndex(recipeId, out var recipeProto))
{
Assert.Fail($"Lathe recipe '{recipeId}' does not exist");
continue;
}
// Track the total material volume of the recipe
var totalQuantity = 0;
// Check each material called for by the recipe
foreach (var (materialId, quantity) in recipeProto.Materials)
{
Assert.That(protoMan.TryIndex(materialId, out var materialProto));
Assert.That(protoMan.HasIndex(materialId), $"Material '{materialId}' does not exist");
// Make sure the material is accepted by the lathe
Assert.That(acceptedMaterials, Does.Contain(materialId), $"Lathe {latheProto.ID} has recipe {recipeId} but does not accept any materials containing {materialId}");
totalQuantity += quantity;

View File

@@ -145,10 +145,7 @@ public sealed partial class MindTests
await server.WaitAssertion(() =>
{
var damageable = entMan.GetComponent<DamageableComponent>(entity);
if (!protoMan.TryIndex(BluntDamageType, out var prototype))
{
return;
}
var prototype = protoMan.Index(BluntDamageType);
damageableSystem.SetDamage(entity, damageable, new DamageSpecifier(prototype, FixedPoint2.New(401)));
Assert.That(mindSystem.GetMind(entity, mindContainerComp), Is.EqualTo(mindId));

View File

@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Content.Server.Administration.Systems;
using Content.Server.GameTicking;
using Content.Server.Maps;
@@ -44,17 +45,44 @@ namespace Content.IntegrationTests.Tests
AdminTestArenaSystem.ArenaMapPath
};
/// <summary>
/// A dictionary linking maps to collections of entity prototype ids that should be exempt from "DoNotMap" restrictions.
/// </summary>
/// <remarks>
/// This declares that the listed entity prototypes are allowed to be present on the map
/// despite being categorized as "DoNotMap", while any unlisted prototypes will still
/// cause the test to fail.
/// </remarks>
private static readonly Dictionary<string, HashSet<EntProtoId>> DoNotMapWhitelistSpecific = new()
{
{"/Maps/bagel.yml", ["RubberStampMime"]},
{"/Maps/reach.yml", ["HandheldCrewMonitor"]},
{"/Maps/Shuttles/ShuttleEvent/honki.yml", ["GoldenBikeHorn", "RubberStampClown"]},
{"/Maps/Shuttles/ShuttleEvent/syndie_evacpod.yml", ["RubberStampSyndicate"]},
{"/Maps/Shuttles/ShuttleEvent/cruiser.yml", ["ShuttleGunPerforator"]},
{"/Maps/Shuttles/ShuttleEvent/instigator.yml", ["ShuttleGunFriendship"]},
};
/// <summary>
/// Maps listed here are given blanket freedom to contain "DoNotMap" entities. Use sparingly.
/// </summary>
/// <remarks>
/// It is also possible to whitelist entire directories here. For example, adding
/// "/Maps/Shuttles/**" will whitelist all shuttle maps.
/// </remarks>
private static readonly string[] DoNotMapWhitelist =
{
"/Maps/centcomm.yml",
"/Maps/bagel.yml", // Contains mime's rubber stamp --> Either fix this, remove the category, or remove this comment if intentional.
"/Maps/reach.yml", // Contains handheld crew monitor
"/Maps/Shuttles/ShuttleEvent/cruiser.yml", // Contains LSE-1200c "Perforator"
"/Maps/Shuttles/ShuttleEvent/honki.yml", // Contains golden honker, clown's rubber stamp
"/Maps/Shuttles/ShuttleEvent/instigator.yml", // Contains EXP-320g "Friendship"
"/Maps/Shuttles/ShuttleEvent/syndie_evacpod.yml", // Contains syndicate rubber stamp
"/Maps/Shuttles/AdminSpawn/**" // admin gaming
};
/// <summary>
/// Converts the above globs into regex so your eyes dont bleed trying to add filepaths.
/// </summary>
private static readonly Regex[] DoNotMapWhiteListRegexes = DoNotMapWhitelist
.Select(glob => new Regex(GlobToRegex(glob), RegexOptions.IgnoreCase | RegexOptions.Compiled))
.ToArray();
private static readonly string[] GameMaps =
{
"Dev",
@@ -247,18 +275,30 @@ namespace Content.IntegrationTests.Tests
await pair.CleanReturnAsync();
}
private bool IsWhitelistedForMap(EntProtoId protoId, ResPath map)
{
if (!DoNotMapWhitelistSpecific.TryGetValue(map.ToString(), out var allowedProtos))
return false;
return allowedProtos.Contains(protoId);
}
/// <summary>
/// Check that maps do not have any entities that belong to the DoNotMap entity category
/// </summary>
private void CheckDoNotMap(ResPath map, YamlNode node, IPrototypeManager protoManager)
{
if (DoNotMapWhitelist.Contains(map.ToString()))
return;
foreach (var regex in DoNotMapWhiteListRegexes)
{
if (regex.IsMatch(map.ToString()))
return;
}
var yamlEntities = node["entities"];
if (!protoManager.TryIndex(DoNotMapCategory, out var dnmCategory))
return;
var dnmCategory = protoManager.Index(DoNotMapCategory);
// Make a set containing all the specific whitelisted proto ids for this map
HashSet<EntProtoId> unusedExemptions = DoNotMapWhitelistSpecific.TryGetValue(map.ToString(), out var exemptions) ? new(exemptions) : [];
Assert.Multiple(() =>
{
foreach (var yamlEntity in (YamlSequenceNode)yamlEntities)
@@ -266,13 +306,20 @@ namespace Content.IntegrationTests.Tests
var protoId = yamlEntity["proto"].AsString();
// This doesn't properly handle prototype migrations, but thats not a significant issue.
if (!protoManager.TryIndex(protoId, out var proto, false))
if (!protoManager.TryIndex(protoId, out var proto))
continue;
Assert.That(!proto.Categories.Contains(dnmCategory),
Assert.That(!proto.Categories.Contains(dnmCategory) || IsWhitelistedForMap(protoId, map),
$"\nMap {map} contains entities in the DO NOT MAP category ({proto.Name})");
// The proto id is used on this map, so remove it from the set
unusedExemptions.Remove(protoId);
}
});
// If there are any proto ids left, they must not have been used in the map!
Assert.That(unusedExemptions, Is.Empty,
$"Map {map} has DO NOT MAP entities whitelisted that are not present in the map: {string.Join(", ", unusedExemptions)}");
}
private bool IsPreInit(ResPath map,
@@ -333,7 +380,7 @@ namespace Content.IntegrationTests.Tests
MapId mapId;
try
{
var opts = DeserializationOptions.Default with {InitializeMaps = true};
var opts = DeserializationOptions.Default with { InitializeMaps = true };
ticker.LoadGameMap(protoManager.Index<GameMapPrototype>(mapProto), out mapId, opts);
}
catch (Exception ex)
@@ -440,7 +487,7 @@ namespace Content.IntegrationTests.Tests
#nullable enable
while (queryPoint.MoveNext(out T? comp, out var xform))
{
var spawner = (ISpawnPoint) comp;
var spawner = (ISpawnPoint)comp;
if (spawner.SpawnType is not SpawnPointType.LateJoin
|| xform.GridUid == null
@@ -554,5 +601,20 @@ namespace Content.IntegrationTests.Tests
await server.WaitRunTicks(1);
await pair.CleanReturnAsync();
}
/// <summary>
/// Lets us the convert the filepaths to regex without eyeglaze trying to add new paths.
/// </summary>
private static string GlobToRegex(string glob)
{
var regex = Regex.Escape(glob)
.Replace(@"\*\*", "**") // replace **
.Replace(@"\*", "*") // replace *
.Replace("**", ".*") // ** → match across folders
.Replace("*", @"[^/]*") // * → match within a single folder
.Replace(@"\?", "."); // ? → any single character
return $"^{regex}$";
}
}
}

View File

@@ -25,6 +25,11 @@ public sealed class AdminTest : ToolshedTest
if (ignored.Contains(cmd.Cmd.GetType().Assembly))
continue;
// Only care about content commands.
var assemblyName = cmd.Cmd.GetType().Assembly.FullName;
if (assemblyName == null || !assemblyName.StartsWith("Content."))
continue;
Assert.That(admin.TryGetCommandFlags(cmd, out _), $"Command does not have admin permissions set up: {cmd.FullName()}");
}
});

View File

@@ -45,7 +45,7 @@ namespace Content.Server.Access.Systems
if (!TryComp<IdCardComponent>(ent, out var idCardComp))
return;
_prototypeManager.TryIndex(args.Args.ChameleonOutfit.Job, out var jobProto);
_prototypeManager.Resolve(args.Args.ChameleonOutfit.Job, out var jobProto);
var jobIcon = args.Args.ChameleonOutfit.Icon ?? jobProto?.Icon;
var jobName = args.Args.ChameleonOutfit.Name ?? jobProto?.Name ?? "";
@@ -130,7 +130,7 @@ namespace Content.Server.Access.Systems
if (!TryComp<IdCardComponent>(uid, out var idCard))
return;
if (!_prototypeManager.TryIndex(args.JobIconId, out var jobIcon))
if (!_prototypeManager.Resolve(args.JobIconId, out var jobIcon))
return;
_cardSystem.TryChangeJobIcon(uid, jobIcon, idCard);

View File

@@ -98,7 +98,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
var targetIdComponent = Comp<IdCardComponent>(targetId);
var targetAccessComponent = Comp<AccessComponent>(targetId);
var jobProto = targetIdComponent.JobPrototype ?? new ProtoId<AccessLevelPrototype>(string.Empty);
var jobProto = targetIdComponent.JobPrototype ?? new ProtoId<JobPrototype>(string.Empty);
if (TryComp<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
&& keyStorage.Key is { } key
&& _record.TryGetRecord<GeneralStationRecord>(key, out var record))
@@ -130,7 +130,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
string newFullName,
string newJobTitle,
List<ProtoId<AccessLevelPrototype>> newAccessList,
ProtoId<AccessLevelPrototype> newJobProto,
ProtoId<JobPrototype> newJobProto,
EntityUid player,
IdCardConsoleComponent? component = null)
{
@@ -144,7 +144,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
_idCard.TryChangeJobTitle(targetId, newJobTitle, player: player);
if (_prototype.TryIndex<JobPrototype>(newJobProto, out var job)
&& _prototype.TryIndex(job.Icon, out var jobIcon))
&& _prototype.Resolve(job.Icon, out var jobIcon))
{
_idCard.TryChangeJobIcon(targetId, jobIcon, player: player);
_idCard.TryChangeJobDepartment(targetId, job);

View File

@@ -82,7 +82,7 @@ public sealed class PresetIdCardSystem : EntitySystem
_cardSystem.TryChangeJobTitle(uid, job.LocalizedName);
_cardSystem.TryChangeJobDepartment(uid, job);
if (_prototypeManager.TryIndex(job.Icon, out var jobIcon))
if (_prototypeManager.Resolve(job.Icon, out var jobIcon))
_cardSystem.TryChangeJobIcon(uid, jobIcon);
}
}

View File

@@ -118,7 +118,7 @@ public sealed class ExplosionCommand : LocalizedEntityCommands
return;
}
}
else if (!_prototypeManager.TryIndex(ExplosionSystem.DefaultExplosionPrototypeId, out type))
else if (!_prototypeManager.Resolve(ExplosionSystem.DefaultExplosionPrototypeId, out type))
{
// no prototype was specified, so lets default to whichever one was defined first
type = _prototypeManager.EnumeratePrototypes<ExplosionPrototype>().FirstOrDefault();

View File

@@ -38,7 +38,7 @@ public sealed partial class SpeakOnUIClosedSystem : SharedSpeakOnUIClosedSystem
if (!entity.Comp.Enabled)
return false;
if (!_prototypeManager.TryIndex(entity.Comp.Pack, out var messagePack))
if (!_prototypeManager.Resolve(entity.Comp.Pack, out var messagePack))
return false;
var message = Loc.GetString(_random.Pick(messagePack.Values), ("name", Name(entity)));

View File

@@ -86,7 +86,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
private void AddAnomalyToBody(Entity<InnerBodyAnomalyComponent> ent)
{
if (!_proto.TryIndex(ent.Comp.InjectionProto, out var injectedAnom))
if (!_proto.Resolve(ent.Comp.InjectionProto, out var injectedAnom))
return;
if (ent.Comp.Injected)
@@ -210,7 +210,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
if (!ent.Comp.Injected)
return;
if (_proto.TryIndex(ent.Comp.InjectionProto, out var injectedAnom))
if (_proto.Resolve(ent.Comp.InjectionProto, out var injectedAnom))
EntityManager.RemoveComponents(ent, injectedAnom.Components);
_stun.TryUpdateParalyzeDuration(ent, TimeSpan.FromSeconds(ent.Comp.StunDuration));

View File

@@ -1,6 +1,7 @@
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Damage;
using Content.Shared.FixedPoint;
using Content.Shared.Guidebook;
namespace Content.Server.Atmos.Components;
@@ -87,6 +88,7 @@ public sealed partial class DeltaPressureComponent : Component
/// The minimum difference in pressure between any side required for the entity to start taking damage.
/// </summary>
[DataField]
[GuidebookData]
public float MinPressureDelta = 7500;
/// <summary>

View File

@@ -51,7 +51,7 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
component.AudioStream = Audio.Stop(component.AudioStream);
if (string.IsNullOrEmpty(component.SelectedSongId) ||
!_protoManager.TryIndex(component.SelectedSongId, out var jukeboxProto))
!_protoManager.Resolve(component.SelectedSongId, out var jukeboxProto))
{
return;
}

View File

@@ -113,7 +113,7 @@ public sealed partial class CargoSystem
public void SetupBountyLabel(EntityUid uid, EntityUid stationId, CargoBountyData bounty, PaperComponent? paper = null, CargoBountyLabelComponent? label = null)
{
if (!Resolve(uid, ref paper, ref label) || !_protoMan.TryIndex<CargoBountyPrototype>(bounty.Bounty, out var prototype))
if (!Resolve(uid, ref paper, ref label) || !_protoMan.Resolve<CargoBountyPrototype>(bounty.Bounty, out var prototype))
return;
label.Id = bounty.Id;
@@ -156,7 +156,7 @@ public sealed partial class CargoSystem
if (!TryGetBountyFromId(station, component.Id, out var bounty, database))
return;
if (!_protoMan.TryIndex(bounty.Value.Bounty, out var bountyPrototype) ||
if (!_protoMan.Resolve(bounty.Value.Bounty, out var bountyPrototype) ||
!IsBountyComplete(container.Owner, bountyPrototype))
return;
@@ -275,7 +275,7 @@ public sealed partial class CargoSystem
public bool IsBountyComplete(EntityUid container, CargoBountyData data, out HashSet<EntityUid> bountyEntities)
{
if (!_protoMan.TryIndex(data.Bounty, out var proto))
if (!_protoMan.Resolve(data.Bounty, out var proto))
{
bountyEntities = new();
return false;

View File

@@ -167,7 +167,7 @@ namespace Content.Server.Cargo.Systems
// Find our order again. It might have been dispatched or approved already
var order = orderDatabase.Orders[component.Account].Find(order => args.OrderId == order.OrderId && !order.Approved);
if (order == null || !_protoMan.TryIndex(order.Account, out var account))
if (order == null || !_protoMan.Resolve(order.Account, out var account))
{
return;
}
@@ -322,7 +322,7 @@ namespace Content.Server.Cargo.Systems
private void OnAddOrderMessageSlipPrinter(EntityUid uid, CargoOrderConsoleComponent component, CargoConsoleAddOrderMessage args, CargoProductPrototype product)
{
if (!_protoMan.TryIndex(component.Account, out var account))
if (!_protoMan.Resolve(component.Account, out var account))
return;
if (Timing.CurTime < component.NextPrintTime)

View File

@@ -78,7 +78,6 @@ public sealed class ChatSanitizationManager : IChatSanitizationManager
Entry("rofl", "chatsan-laughs"),
Entry("o7", "chatsan-salutes"),
Entry(";_;7", "chatsan-tearfully-salutes"),
Entry("idk", "chatsan-shrugs"),
Entry(";)", "chatsan-winks"),
Entry(";]", "chatsan-winks"),
Entry("(;", "chatsan-winks"),

View File

@@ -433,7 +433,7 @@ public sealed partial class ChatSystem : SharedChatSystem
RaiseLocalEvent(source, nameEv);
name = nameEv.VoiceName;
// Check for a speech verb override
if (nameEv.SpeechVerb != null && _prototypeManager.TryIndex(nameEv.SpeechVerb, out var proto))
if (nameEv.SpeechVerb != null && _prototypeManager.Resolve(nameEv.SpeechVerb, out var proto))
speech = proto;
}

View File

@@ -66,7 +66,7 @@ public sealed class TransformableContainerSystem : EntitySystem
private void OnRefreshNameModifiers(Entity<TransformableContainerComponent> entity, ref RefreshNameModifiersEvent args)
{
if (_prototypeManager.TryIndex(entity.Comp.CurrentReagent, out var currentReagent))
if (_prototypeManager.Resolve(entity.Comp.CurrentReagent, out var currentReagent))
{
args.AddModifier("transformable-container-component-glass", priority: -1, ("reagent", currentReagent.LocalizedName));
}

View File

@@ -43,13 +43,13 @@ public sealed partial class CloningSystem : SharedCloningSystem
public bool TryCloning(EntityUid original, MapCoordinates? coords, ProtoId<CloningSettingsPrototype> settingsId, [NotNullWhen(true)] out EntityUid? clone)
{
clone = null;
if (!_prototype.TryIndex(settingsId, out var settings))
if (!_prototype.Resolve(settingsId, out var settings))
return false; // invalid settings
if (!TryComp<HumanoidAppearanceComponent>(original, out var humanoid))
return false; // whatever body was to be cloned, was not a humanoid
if (!_prototype.TryIndex(humanoid.Species, out var speciesPrototype))
if (!_prototype.Resolve(humanoid.Species, out var speciesPrototype))
return false; // invalid species
var attemptEv = new CloningAttemptEvent(settings);

View File

@@ -145,7 +145,7 @@ namespace Content.Server.Construction
return guide;
// If the graph doesn't actually exist, do nothing.
if (!PrototypeManager.TryIndex(construction.Graph, out ConstructionGraphPrototype? graph))
if (!PrototypeManager.Resolve(construction.Graph, out ConstructionGraphPrototype? graph))
return null;
// If either the start node or the target node are missing, do nothing.

View File

@@ -61,7 +61,7 @@ public sealed class DamageForceSaySystem : EntitySystem
var ev = new BeforeForceSayEvent(component.ForceSayStringDataset);
RaiseLocalEvent(uid, ev);
if (!_prototype.TryIndex(ev.Prefix, out var prefixList))
if (!_prototype.Resolve(ev.Prefix, out var prefixList))
return;
var suffix = Loc.GetString(_random.Pick(prefixList.Values));

View File

@@ -20,7 +20,7 @@ public sealed class ExaminableDamageSystem : EntitySystem
private void OnExamine(Entity<ExaminableDamageComponent> ent, ref ExaminedEvent args)
{
if (!_prototype.TryIndex(ent.Comp.Messages, out var proto) || proto.Values.Count == 0)
if (!_prototype.Resolve(ent.Comp.Messages, out var proto) || proto.Values.Count == 0)
return;
var percent = GetDamagePercent(ent);

View File

@@ -102,7 +102,7 @@ public sealed partial class DeliverySystem : SharedDeliverySystem
if (ent.Comp.WasPenalized)
return;
if (!_protoMan.TryIndex(ent.Comp.PenaltyBankAccount, out var accountInfo))
if (!_protoMan.Resolve(ent.Comp.PenaltyBankAccount, out var accountInfo))
return;
var multiplier = GetDeliveryMultiplier(ent);

View File

@@ -66,7 +66,7 @@ public sealed partial class WeightedSpawnEntityBehavior : IThresholdBehavior
if (SpawnAfter != 0)
{
// if it fails to get the spawner, this won't ever work so just return
if (!system.PrototypeManager.TryIndex(TempEntityProtoId, out var tempSpawnerProto))
if (!system.PrototypeManager.Resolve(TempEntityProtoId, out var tempSpawnerProto))
return;
// spawn the spawner, assign it a lifetime, and assign the entity that it will spawn when despawned

View File

@@ -949,9 +949,7 @@ public sealed class EntityEffectSystem : EntitySystem
return;
var targetProto = _random.Pick(plantholder.Seed.MutationPrototypes);
_protoManager.TryIndex(targetProto, out SeedPrototype? protoSeed);
if (protoSeed == null)
if (!_protoManager.TryIndex(targetProto, out SeedPrototype? protoSeed))
{
Log.Error($"Seed prototype could not be found: {targetProto}!");
return;

View File

@@ -32,7 +32,7 @@ public sealed class AntagLoadProfileRuleSystem : GameRuleSystem<AntagLoadProfile
: HumanoidCharacterProfile.RandomWithSpecies();
if (profile?.Species is not { } speciesId || !_proto.TryIndex(speciesId, out var species))
if (profile?.Species is not { } speciesId || !_proto.Resolve(speciesId, out var species))
{
species = _proto.Index<SpeciesPrototype>(SharedHumanoidAppearanceSystem.DefaultSpecies);
}

View File

@@ -17,7 +17,7 @@ public sealed class PuddleMessVariationPassSystem : VariationPassSystem<PuddleMe
{
var totalTiles = Stations.GetTileCount(args.Station.AsNullable());
if (!_proto.TryIndex(ent.Comp.RandomPuddleSolutionFill, out var proto))
if (!_proto.Resolve(ent.Comp.RandomPuddleSolutionFill, out var proto))
return;
var puddleMod = Random.NextGaussian(ent.Comp.TilesPerSpillAverage, ent.Comp.TilesPerSpillStdDev);

View File

@@ -808,7 +808,7 @@ public sealed class GhostRoleSystem : EntitySystem
public void OnGhostRoleRadioMessage(Entity<GhostRoleMobSpawnerComponent> entity, ref GhostRoleRadioMessage args)
{
if (!_prototype.TryIndex(args.ProtoId, out var ghostRoleProto))
if (!_prototype.Resolve(args.ProtoId, out var ghostRoleProto))
return;
// if the prototype chosen isn't actually part of the selectable options, ignore it

View File

@@ -32,7 +32,7 @@ public sealed class SpookySpeakerSystem : EntitySystem
if (curTime < entity.Comp.NextSpeakTime)
return;
if (!_proto.TryIndex(entity.Comp.MessageSet, out var messages))
if (!_proto.Resolve(entity.Comp.MessageSet, out var messages))
return;
// Grab a random localized message from the set

View File

@@ -49,8 +49,8 @@ public sealed class ChameleonControllerSystem : SharedChameleonControllerSystem
{
var outfitPrototype = _proto.Index(outfit);
_proto.TryIndex(outfitPrototype.Job, out var jobPrototype);
_proto.TryIndex(outfitPrototype.StartingGear, out var startingGearPrototype);
_proto.Resolve(outfitPrototype.Job, out var jobPrototype);
_proto.Resolve(outfitPrototype.StartingGear, out var startingGearPrototype);
GetJobEquipmentInformation(jobPrototype, user, out var customRoleLoadout, out var defaultRoleLoadout, out var jobStartingGearPrototype);
@@ -81,7 +81,7 @@ public sealed class ChameleonControllerSystem : SharedChameleonControllerSystem
if (jobPrototype == null)
return;
_proto.TryIndex(jobPrototype.StartingGear, out jobStartingGearPrototype);
_proto.Resolve(jobPrototype.StartingGear, out jobStartingGearPrototype);
if (!TryComp<ActorComponent>(user, out var actorComponent))
return;

View File

@@ -725,7 +725,7 @@ namespace Content.Server.Kitchen.EntitySystems
{
foreach (ProtoId<FoodRecipePrototype> recipeId in ent.Comp.ProvidedRecipes)
{
if (_prototype.TryIndex(recipeId, out var recipeProto))
if (_prototype.Resolve(recipeId, out var recipeProto))
{
args.Recipes.Add(recipeProto);
}

View File

@@ -137,7 +137,7 @@ namespace Content.Server.Lathe
var recipes = GetAvailableRecipes(uid, component, true);
foreach (var id in recipes)
{
if (!_proto.TryIndex(id, out var proto))
if (!_proto.Resolve(id, out var proto))
continue;
foreach (var (mat, _) in proto.Materials)
{

View File

@@ -92,7 +92,7 @@ public sealed class NameIdentifierSystem : EntitySystem
if (ent.Comp.Group is null)
return;
if (!_prototypeManager.TryIndex(ent.Comp.Group, out var group))
if (!_prototypeManager.Resolve(ent.Comp.Group, out var group))
return;
int id;
@@ -131,7 +131,7 @@ public sealed class NameIdentifierSystem : EntitySystem
if (ent.Comp.LifeStage > ComponentLifeStage.Running)
return;
if (!_prototypeManager.TryIndex(ent.Comp.Group, out var group))
if (!_prototypeManager.Resolve(ent.Comp.Group, out var group))
return;
var format = group.FullName ? "name-identifier-format-full" : "name-identifier-format-append";

View File

@@ -21,12 +21,13 @@ namespace Content.Server.Nutrition.EntitySystems
[UsedImplicitly]
public sealed class CreamPieSystem : SharedCreamPieSystem
{
[Dependency] private readonly SharedSolutionContainerSystem _solutions = default!;
[Dependency] private readonly PuddleSystem _puddle = default!;
[Dependency] private readonly IngestionSystem _ingestion = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
[Dependency] private readonly TriggerSystem _trigger = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly PuddleSystem _puddle = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solutions = default!;
[Dependency] private readonly TriggerSystem _trigger = default!;
public override void Initialize()
{
@@ -39,26 +40,23 @@ namespace Content.Server.Nutrition.EntitySystems
SubscribeLocalEvent<CreamPiedComponent, RejuvenateEvent>(OnRejuvenate);
}
protected override void SplattedCreamPie(EntityUid uid, CreamPieComponent creamPie)
protected override void SplattedCreamPie(Entity<CreamPieComponent, EdibleComponent?> entity)
{
// The entity is deleted, so play the sound at its position rather than parenting
var coordinates = Transform(uid).Coordinates;
_audio.PlayPvs(_audio.ResolveSound(creamPie.Sound), coordinates, AudioParams.Default.WithVariation(0.125f));
var coordinates = Transform(entity).Coordinates;
_audio.PlayPvs(_audio.ResolveSound(entity.Comp1.Sound), coordinates, AudioParams.Default.WithVariation(0.125f));
if (TryComp(uid, out FoodComponent? foodComp))
if (Resolve(entity, ref entity.Comp2, false))
{
if (_solutions.TryGetSolution(uid, foodComp.Solution, out _, out var solution))
{
_puddle.TrySpillAt(uid, solution, out _, false);
}
foreach (var trash in foodComp.Trash)
{
Spawn(trash, Transform(uid).Coordinates);
}
}
ActivatePayload(uid);
if (_solutions.TryGetSolution(entity.Owner, entity.Comp2.Solution, out _, out var solution))
_puddle.TrySpillAt(entity.Owner, solution, out _, false);
QueueDel(uid);
_ingestion.SpawnTrash((entity, entity.Comp2));
}
ActivatePayload(entity);
QueueDel(entity);
}
private void OnConsume(Entity<CreamPieComponent> entity, ref ConsumeDoAfterEvent args)

View File

@@ -127,7 +127,7 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
SetSeed(uid, component, _random.Next());
}
if (_proto.TryIndex(component.Template, out var biome))
if (_proto.Resolve(component.Template, out var biome))
SetTemplate(uid, component, biome);
var xform = Transform(uid);

View File

@@ -63,7 +63,7 @@ public sealed class JobWhitelistManager : IPostInjectInit
if (!_config.GetCVar(CCVars.GameRoleWhitelist))
return true;
if (!_prototypes.TryIndex(job, out var jobPrototype) ||
if (!_prototypes.Resolve(job, out var jobPrototype) ||
!jobPrototype.Whitelisted)
{
return true;

View File

@@ -238,7 +238,7 @@ public sealed class PlayTimeTrackingSystem : EntitySystem
for (var i = 0; i < jobs.Count; i++)
{
if (_prototypes.TryIndex(jobs[i], out var job)
if (_prototypes.Resolve(jobs[i], out var job)
&& JobRequirements.TryRequirementsMet(job, playTimes, out _, EntityManager, _prototypes, (HumanoidCharacterProfile?) _preferencesManager.GetPreferences(userId).SelectedCharacter))
{
continue;

View File

@@ -20,6 +20,12 @@ public sealed partial class PolymorphedEntityComponent : Component
[DataField(required: true)]
public EntityUid? Parent;
/// <summary>
/// Whether this polymorph has been reverted.
/// </summary>
[DataField]
public bool Reverted;
/// <summary>
/// The amount of time that has passed since the entity was created
/// used for tracking the duration

View File

@@ -112,7 +112,7 @@ public sealed partial class PolymorphSystem : EntitySystem
private void OnPolymorphActionEvent(Entity<PolymorphableComponent> ent, ref PolymorphActionEvent args)
{
if (!_proto.TryIndex(args.ProtoId, out var prototype) || args.Handled)
if (!_proto.Resolve(args.ProtoId, out var prototype) || args.Handled)
return;
PolymorphEntity(ent, prototype.Configuration);
@@ -128,12 +128,11 @@ public sealed partial class PolymorphSystem : EntitySystem
private void OnBeforeFullySliced(Entity<PolymorphedEntityComponent> ent, ref BeforeFullySlicedEvent args)
{
var (_, comp) = ent;
if (comp.Configuration.RevertOnEat)
{
args.Cancel();
Revert((ent, ent));
}
if (ent.Comp.Reverted || !ent.Comp.Configuration.RevertOnEat)
return;
args.Cancel();
Revert((ent, ent));
}
/// <summary>
@@ -142,14 +141,17 @@ public sealed partial class PolymorphSystem : EntitySystem
/// </summary>
private void OnDestruction(Entity<PolymorphedEntityComponent> ent, ref DestructionEventArgs args)
{
if (ent.Comp.Configuration.RevertOnDeath)
{
Revert((ent, ent));
}
if (ent.Comp.Reverted || !ent.Comp.Configuration.RevertOnDeath)
return;
Revert((ent, ent));
}
private void OnPolymorphedTerminating(Entity<PolymorphedEntityComponent> ent, ref EntityTerminatingEvent args)
{
if (ent.Comp.Reverted)
return;
if (ent.Comp.Configuration.RevertOnDelete)
Revert(ent.AsNullable());
@@ -298,8 +300,6 @@ public sealed partial class PolymorphSystem : EntitySystem
if (component.Parent is not { } parent)
return null;
// Clear our reference to the original entity
component.Parent = null;
if (Deleted(parent))
return null;
@@ -316,6 +316,8 @@ public sealed partial class PolymorphSystem : EntitySystem
_transform.SetParent(parent, parentXform, uidXform.ParentUid);
_transform.SetCoordinates(parent, parentXform, uidXform.Coordinates, uidXform.LocalRotation);
component.Reverted = true;
if (component.Configuration.TransferDamage &&
TryComp<DamageableComponent>(parent, out var damageParent) &&
_mobThreshold.GetScaledDamage(uid, parent, out var damage) &&
@@ -387,7 +389,7 @@ public sealed partial class PolymorphSystem : EntitySystem
if (target.Comp.PolymorphActions.ContainsKey(id))
return;
if (!_proto.TryIndex(id, out var polyProto))
if (!_proto.Resolve(id, out var polyProto))
return;
var entProto = _proto.Index(polyProto.Configuration.Entity);

Some files were not shown because too many files have changed in this diff Show More