Additional console commands for debugging Xeno Artifact (#41455)

* feat: xeno artifact debug commands

* refactor:  minor renaming + whitespaces

* refactor: do not allocate unless needed

* refactor: rename common xeno-artifact commands localization string

* refactor: extract reusable parts of code, change command to work with EntityUid and not with NetEntity

* refactor: renaming

* refactor: make all commands toolshed

* refactor: localization fix, command naming reverted to camelCase, xml-doc for new commands

* refactor: make toolshed tuple actually work

* refactor: remove unused code

* refactor: cache for EffectPrototypes

* refactor: fix unlcokNode command, remove engine copy-paste for TryParseEntity

* refactor: fix localization for toolshed

* refactor: remove unused dependency

* refactor: caching systems for XenoArtifactCommand where possible, comments clarification, whitespacing

* refactor: added readonly to SharedXenoArtifactSystem.EffectPrototypeIds

* refactor: use component to detect which proto is xenoarch effect

* refactor: fix missing partial for XenoArtifactCommand

* refactor: fix readonly on dependencies for XenoArtifactCommand

* refactor: fix missing partial for nested XenoArtifact related parsers

* refactor: cleanup after merge

* refactor: remove obsolete IPrototype.HasComponent call and unused using

* refactor: change xenoarch commands to lowercase (as convention dictates)

* refactor: XenoArtifactNodeParser and XenoArtifactTypeParser are now CustomCompletionParser (removed duplicate code)

* refactor: XenoArtifactNodeParser is now CustomCompletionParser, moved custom parsers to shared/toolshed/parsers

* refactor: todo for ent proto caching, moved XenoArtifactCommand to toolshed namespace

* refactor: fix xenoartifact:totalresearch command

* refactor: remove system caching, clean whitespaces

* fix: fix localization for xenoarch commands

* refactor: moved StatusEffectCompletionParser to shared, added same todo remark for cache of status effect entity prototypes.

* refactor: use proxy methods in command and  _entityManager.System in all parsers

* refactor: mved commands and parsers into respective folders

* refactor: access restriction for cached EntityPrototypeIds

* refactor: todo link to issue, fixed DI in StatusEffectCompletionParser

---------

Co-authored-by: pa.pecherskij <pa.pecherskij@interfax.ru>
This commit is contained in:
Fildrance 2026-07-31 20:48:45 +03:00 • committed by GitHub
parent d2472f82ed
commit bb5f8d696a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 469 additions and 222 deletions

View file

@ -1,4 +1,4 @@
using Content.Shared.Station.Components;
using Content.Shared.Station.Components;
using Robust.Shared.Console;
namespace Content.Server.Commands;

View file

@ -1,7 +1,7 @@
using Content.Server.Administration;
using Content.Server.Toolshed.TypeParsers.StatusEffects;
using Content.Shared.Administration;
using Content.Shared.StatusEffectNew;
using Content.Shared.Toolshed.TypeParsers;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed;

View file

@ -0,0 +1,242 @@
using System.Text;
using Content.Server.Administration;
using Content.Server.Xenoarchaeology.Artifact;
using Content.Shared.Administration;
using Content.Shared.Toolshed.TypeParsers.XenoArtifact;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Content.Shared.Xenoarchaeology.Artifact.Prototypes;
using Robust.Shared.Map;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed;
namespace Content.Server.Toolshed.Commands.XenoArtifacts;
/// <summary>
/// Toolshed commands for manipulating xeno artifact.
/// </summary>
[ToolshedCommand, AdminCommand(AdminFlags.Debug)]
public sealed partial class XenoArtifactCommand : ToolshedCommand
{
[Dependency] private IPrototypeManager _prototypeManager = default!;
private XenoArtifactSystem? _artifact;
public static readonly EntProtoId ArtifactPrototype = "ComplexXenoArtifactItem";
/// <summary> List existing artifacts. </summary>
[CommandImplementation("list")]
public IEnumerable<EntityUid> List()
{
var query = EntityManager.EntityQueryEnumerator<XenoArtifactComponent>();
while (query.MoveNext(out var uid, out _))
{
yield return uid;
}
}
/// <summary>
/// Output matrix of artifact nodes and how they are connected.
/// </summary>
[CommandImplementation("printmatrix")]
public string PrintMatrix([CommandArgument] Entity<XenoArtifactComponent> artifactEnt)
{
var nodeCount = artifactEnt.Comp.NodeVertices.Length;
var sb = new StringBuilder("\n |");
for (var i = 0; i < nodeCount; i++)
{
sb.Append($" {i:D2}|");
}
AddHorizontalFiller(sb);
for (var i = 0; i < nodeCount; i++)
{
sb.Append($"\n{i:D2}|");
for (var j = 0; j < nodeCount; j++)
{
var value = artifactEnt.Comp.NodeAdjacencyMatrix[i][j]
? "X"
: " ";
sb.Append($" {value} |");
}
AddHorizontalFiller(sb);
}
return sb.ToString();
void AddHorizontalFiller(StringBuilder builder)
{
builder.AppendLine();
builder.Append("--+");
for (var i = 0; i < nodeCount; i++)
{
builder.Append("---+");
}
}
}
/// <summary> Output total research points artifact contains. </summary>
[CommandImplementation("totalresearch")]
public int TotalResearch([PipedArgument] EntityUid artifactEntityUid)
{
_artifact ??= Sys<XenoArtifactSystem>();
var comp = Comp<XenoArtifactComponent>(artifactEntityUid);
var sum = 0;
var nodes = _artifact.GetAllNodes((artifactEntityUid, comp));
foreach (var node in nodes)
{
sum += node.Comp.ResearchValue;
}
return sum;
}
/// <summary>
/// Spawns a bunch of artifacts and gets average total research points they can yield.
/// </summary>
[CommandImplementation("averageresearch")]
public float AverageResearch()
{
const int n = 100;
var sum = 0;
for (var i = 0; i < n; i++)
{
var ent = Spawn(ArtifactPrototype, MapCoordinates.Nullspace);
sum += TotalResearch(ent);
Del(ent);
}
return (float)sum / n;
}
/// <summary> Unlocks all nodes of artifact. </summary>
[CommandImplementation("unlockallnodes")]
public void UnlockAllNodes([PipedArgument] EntityUid artifactEntityUid)
{
_artifact ??= Sys<XenoArtifactSystem>();
var comp = Comp<XenoArtifactComponent>(artifactEntityUid);
var nodes = _artifact.GetAllNodes((artifactEntityUid, comp));
foreach (var node in nodes)
{
_artifact.SetNodeUnlocked((node, node.Comp));
}
}
/// <summary>
/// Create node in artifact (new on depth 0 or attach next to existing one).
/// </summary>
[CommandImplementation("createnode")]
public void CreateNodeNew(
[CommandArgument] Entity<XenoArtifactComponent> artifact,
[CommandArgument(typeof(XenoEffectParser))] ProtoId<EntityPrototype> effect,
[CommandArgument] ProtoId<XenoArchTriggerPrototype> trigger
)
{
CreateNode(artifact, effect, trigger);
}
/// <summary> Add a new node to the given artifact. </summary>
[CommandImplementation("createnodeatdepth")]
public void CreateNodeAtDepth(
[CommandArgument(typeof(XenoArtifactNodeParser))] (Entity<XenoArtifactComponent> Artifact, Entity<XenoArtifactNodeComponent> Node) tuple,
[CommandArgument(typeof(XenoEffectParser))] ProtoId<EntityPrototype> effect,
[CommandArgument] ProtoId<XenoArchTriggerPrototype> trigger
)
{
CreateNode(tuple.Artifact, effect, trigger, tuple.Node);
}
/// <summary> Spawns a new xeno artifact with single node with the given trigger and effect. </summary>
[CommandImplementation("spawnartwithnode")]
public void SpawnArtifactWithNode(
[CommandArgument] ICommonSession target,
[CommandArgument(typeof(XenoArtifactTypeParser))] ProtoId<EntityPrototype> artifactType,
[CommandArgument(typeof(XenoEffectParser))] ProtoId<EntityPrototype> effect,
[CommandArgument] ProtoId<XenoArchTriggerPrototype> trigger
)
{
if (target.AttachedEntity == null)
return;
var entity = EntityManager.SpawnNextToOrDrop(artifactType, target.AttachedEntity.Value);
if (!TryComp(entity, out XenoArtifactComponent? artifactComp))
{
return;
}
CreateNode((entity, artifactComp), effect, trigger);
}
/// <summary> Marks a node as unlocked. </summary>
[CommandImplementation("unlocknode")]
public void UnlockNode(
[CommandArgument(typeof(XenoArtifactNodeParser))]
(Entity<XenoArtifactComponent> Artifact, Entity<XenoArtifactNodeComponent> Node) tuple
)
{
_artifact ??= Sys<XenoArtifactSystem>();
_artifact.SetNodeUnlocked(tuple.Node.AsNullable());
}
/// <summary> Removes a node from a xeno artifact. </summary>
[CommandImplementation("removenode")]
public void RemoveNode(
[CommandArgument(typeof(XenoArtifactNodeParser))]
(Entity<XenoArtifactComponent> Artifact, Entity<XenoArtifactNodeComponent> Node) tuple
)
{
_artifact ??= Sys<XenoArtifactSystem>();
_artifact.RemoveNode(tuple.Artifact.AsNullable(), tuple.Node.AsNullable());
}
/// <summary> Adds an edge between two nodes of a xeno artifact. </summary>
[CommandImplementation("addedge")]
public void AddEdge(
[CommandArgument(typeof(XenoArtifactNodeParser))]
(Entity<XenoArtifactComponent> Artifact, Entity<XenoArtifactNodeComponent> Node) from,
[CommandArgument(typeof(XenoArtifactNodeParser))]
(Entity<XenoArtifactComponent> Artifact, Entity<XenoArtifactNodeComponent> Node) to
)
{
// no inter-artifact edges or self-connects allowed
if (from.Artifact.Owner != to.Artifact.Owner || from.Node.Owner == to.Node.Owner)
return;
_artifact = Sys<XenoArtifactSystem>();
_artifact.AddEdge(from.Artifact.AsNullable(), from.Node, to.Node);
}
private void CreateNode(
Entity<XenoArtifactComponent> artifact,
ProtoId<EntityPrototype> effect,
ProtoId<XenoArchTriggerPrototype> trigger,
Entity<XenoArtifactNodeComponent>? node = null
)
{
var depth = 0;
if (node.HasValue)
{
depth = node.Value.Comp.Depth + 1;
}
_artifact ??= Sys<XenoArtifactSystem>();
if (!_prototypeManager.Resolve(trigger, out var triggerPrototype))
return;
var createdNode = _artifact.CreateNode(artifact, effect.Id, triggerPrototype, depth);
if (node.HasValue)
{
_artifact.AddEdge(artifact.AsNullable(), node.Value, createdNode);
}
else
{
_artifact.RebuildXenoArtifactMetaData(artifact.AsNullable());
}
}
}

View file

@ -1,16 +0,0 @@
using Content.Shared.StatusEffectNew;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
namespace Content.Server.Toolshed.TypeParsers.StatusEffects;
public sealed class StatusEffectCompletionParser : CustomCompletionParser<EntProtoId>
{
public override CompletionResult? TryAutocomplete(ParserContext ctx, CommandArgument? arg)
{
return CompletionResult.FromHintOptions(IoCManager.Resolve<IEntityManager>().System<StatusEffectsSystem>().StatusEffectPrototypes, GetArgHint(arg));
}
}

View file

@ -1,68 +0,0 @@
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Robust.Shared.Console;
namespace Content.Server.Xenoarchaeology.Artifact;
/// <summary> Command for unlocking a specific node of a xeno artifact. </summary>
[AdminCommand(AdminFlags.Debug)]
public sealed partial class UnlockNodeCommand : LocalizedEntityCommands
{
[Dependency] private XenoArtifactSystem _artiSystem = default!;
public override string Command => "unlocknode";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 2)
{
shell.WriteError(Loc.GetString("shell-wrong-arguments-number"));
return;
}
if (!NetEntity.TryParse(args[1], out var netNode) || !EntityManager.TryGetEntity(netNode, out var entityUid))
{
shell.WriteError(Loc.GetString("shell-could-not-find-entity-with-uid", ("uid", args[1])));
return;
}
_artiSystem.SetNodeUnlocked(entityUid.Value);
}
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
switch (args.Length)
{
case 1:
{
var query = EntityManager.EntityQueryEnumerator<XenoArtifactComponent>();
var completionOptions = new List<CompletionOption>();
while (query.MoveNext(out var uid, out _))
{
completionOptions.Add(new CompletionOption(uid.ToString()));
}
return CompletionResult.FromHintOptions(completionOptions, Loc.GetString("cmd-unlocknode-artifact-hint"));
}
case 2 when
NetEntity.TryParse(args[0], out var netEnt) &&
EntityManager.TryGetEntity(netEnt, out var artifactUid) &&
EntityManager.TryGetComponent<XenoArtifactComponent>(artifactUid, out var comp):
{
var result = new List<CompletionOption>();
foreach (var node in _artiSystem.GetAllNodes((artifactUid.Value, comp)))
{
var metaData = EntityManager.MetaQuery.Comp(artifactUid.Value);
var entityUidStr = EntityManager.GetNetEntity(node).ToString();
var completionOption = new CompletionOption(entityUidStr, metaData.EntityName);
result.Add(completionOption);
}
return CompletionResult.FromHintOptions(result, Loc.GetString("cmd-unlocknode-node-hint"));
}
default:
return CompletionResult.Empty;
}
}
}

View file

@ -1,124 +0,0 @@
using System.Text;
using Content.Server.Administration;
using Content.Shared.Administration;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed;
namespace Content.Server.Xenoarchaeology.Artifact;
/// <summary>
/// Toolshed commands for manipulating xeno artifact.
/// </summary>
[ToolshedCommand, AdminCommand(AdminFlags.Debug)]
public sealed class XenoArtifactCommand : ToolshedCommand
{
public static readonly EntProtoId ArtifactPrototype = "BaseXenoArtifact";
/// <summary> List existing artifacts. </summary>
[CommandImplementation("list")]
public IEnumerable<EntityUid> List()
{
var query = EntityManager.EntityQueryEnumerator<XenoArtifactComponent>();
while (query.MoveNext(out var uid, out _))
{
yield return uid;
}
}
/// <summary>
/// Output matrix of artifact nodes and how they are connected.
/// </summary>
[CommandImplementation("printMatrix")]
public string PrintMatrix([PipedArgument] EntityUid artifactEntitUid)
{
var comp = EntityManager.GetComponent<XenoArtifactComponent>(artifactEntitUid);
var nodeCount = comp.NodeVertices.Length;
var sb = new StringBuilder("\n |");
for (var i = 0; i < nodeCount; i++)
{
sb.Append($" {i:D2}|");
}
AddHorizontalFiller(sb);
for (var i = 0; i < nodeCount; i++)
{
sb.Append($"\n{i:D2}|");
for (var j = 0; j < nodeCount; j++)
{
var value = comp.NodeAdjacencyMatrix[i][j]
? "X"
: " ";
sb.Append($" {value} |");
}
AddHorizontalFiller(sb);
}
return sb.ToString();
void AddHorizontalFiller(StringBuilder builder)
{
builder.AppendLine();
builder.Append("--+");
for (var i = 0; i < nodeCount; i++)
{
builder.Append($"---+");
}
}
}
/// <summary> Output total research points artifact contains. </summary>
[CommandImplementation("totalResearch")]
public int TotalResearch([PipedArgument] EntityUid artifactEntityUid)
{
var artiSys = EntityManager.System<XenoArtifactSystem>();
var comp = EntityManager.GetComponent<XenoArtifactComponent>(artifactEntityUid);
var sum = 0;
var nodes = artiSys.GetAllNodes((artifactEntityUid, comp));
foreach (var node in nodes)
{
sum += node.Comp.ResearchValue;
}
return sum;
}
/// <summary>
/// Spawns a bunch of artifacts and gets average total research points they can yield.
/// </summary>
[CommandImplementation("averageResearch")]
public float AverageResearch()
{
const int n = 100;
var sum = 0;
for (var i = 0; i < n; i++)
{
var ent = Spawn(ArtifactPrototype, MapCoordinates.Nullspace);
sum += TotalResearch(ent);
Del(ent);
}
return (float) sum / n;
}
/// <summary> Unlocks all nodes of artifact. </summary>
[CommandImplementation("unlockAllNodes")]
public void UnlockAllNodes([PipedArgument] EntityUid artifactEntityUid)
{
var artiSys = EntityManager.System<XenoArtifactSystem>();
var comp = EntityManager.GetComponent<XenoArtifactComponent>(artifactEntityUid);
var nodes = artiSys.GetAllNodes((artifactEntityUid, comp));
foreach (var node in nodes)
{
artiSys.SetNodeUnlocked((node, node.Comp));
}
}
}

View file

@ -1,10 +1,11 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared.Rejuvenate;
using Content.Shared.StatusEffectNew.Components;
using Content.Shared.Toolshed.TypeParsers;
using Content.Shared.Whitelist;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using System.Diagnostics.CodeAnalysis;
namespace Content.Shared.StatusEffectNew;
@ -21,6 +22,8 @@ public sealed partial class StatusEffectsSystem : EntitySystem
[Dependency] private EntityQuery<StatusEffectContainerComponent> _containerQuery = default!;
[Dependency] private EntityQuery<StatusEffectComponent> _effectQuery = default!;
// TODO: https://github.com/space-wizards/space-station-14/issues/45060
[Access(typeof(StatusEffectCompletionParser), Other = AccessPermissions.None)]
public readonly HashSet<string> StatusEffectPrototypes = [];
public override void Initialize()

View file

@ -0,0 +1,19 @@
using Content.Shared.StatusEffectNew;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
namespace Content.Shared.Toolshed.TypeParsers;
public sealed partial class StatusEffectCompletionParser : CustomCompletionParser<EntProtoId>
{
[Dependency] private IEntityManager _entityManager = default!;
public override CompletionResult? TryAutocomplete(ParserContext ctx, CommandArgument? arg)
{
var statusEffects = _entityManager.System<StatusEffectsSystem>();
return CompletionResult.FromHintOptions(statusEffects.StatusEffectPrototypes, GetArgHint(arg));
}
}

View file

@ -0,0 +1,66 @@
using System.Linq;
using Content.Shared.Xenoarchaeology.Artifact;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Robust.Shared.Console;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
namespace Content.Shared.Toolshed.TypeParsers.XenoArtifact;
/// <summary>
/// Argument parser for toolshed commands, which should autocomplete artifact nodes that exists on artifact.
/// </summary>
public sealed partial class XenoArtifactNodeParser : CustomCompletionParser<(Entity<XenoArtifactComponent>, Entity<XenoArtifactNodeComponent>)>
{
[Dependency] private IEntityManager _entityManager = default!;
/// <inheritdoc />
public override CompletionResult? TryAutocomplete(ParserContext ctx, CommandArgument? arg)
{
if (!Toolshed.TryParse<Entity<XenoArtifactComponent>>(ctx, out var artifactEnt))
{
return GetHintedEntities<XenoArtifactComponent>(arg);
}
var hint = ToolshedCommand.GetArgHint(arg, typeof(Entity<XenoArtifactNodeComponent>));
var artifact = _entityManager.System<SharedXenoArtifactSystem>();
var list = artifact.GetAllNodes(artifactEnt)
.Select(
node =>
{
var metadata = _entityManager.GetComponent<MetaDataComponent>(node);
var entDescription = Loc.GetString(metadata.EntityDescription);
return new CompletionOption(
node.Owner.ToString(),
Loc.GetString(
"command-xenoartifact-common-node-hint",
("depth", node.Comp.Depth),
("nodeId", artifact.GetNodeId(node.Owner)),
("nodeDetail", entDescription)
)
);
});
return CompletionResult.FromHintOptions(list, hint);
}
private CompletionResult? GetHintedEntities<T>(CommandArgument? arg) where T : IComponent
{
var hint = ToolshedCommand.GetArgHint(arg, typeof(NetEntity));
// Avoid dumping too many entities
if (_entityManager.Count<T>() > 128)
return CompletionResult.FromHint(hint);
var query = _entityManager.AllEntityQueryEnumerator<T, MetaDataComponent>();
var list = new List<CompletionOption>();
while (query.MoveNext(out _, out var metadata))
{
list.Add(new CompletionOption(metadata.NetEntity.ToString(), metadata.EntityName));
}
return CompletionResult.FromHintOptions(list, hint);
}
}

View file

@ -0,0 +1,28 @@
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
namespace Content.Shared.Toolshed.TypeParsers.XenoArtifact;
/// <summary>
/// Custom type parser for toolshed commands that will enable choosing between hand-held and
/// stationary artifact types.
/// </summary>
public sealed partial class XenoArtifactTypeParser : CustomCompletionParser<ProtoId<EntityPrototype>>
{
private static readonly EntProtoId ArtifactDummyItem = "DummyArtifactItem";
private static readonly EntProtoId ArtifactDummyStructure = "DummyArtifactStructure";
public override CompletionResult TryAutocomplete(ParserContext ctx, CommandArgument? arg)
{
return CompletionResult.FromHintOptions(
[
new CompletionOption(ArtifactDummyItem, Loc.GetString("command-spawnartifactwithnode-spawn-artifact-item-hint")),
new CompletionOption(ArtifactDummyStructure, Loc.GetString("command-spawnartifactwithnode-spawn-artifact-structure-hint")),
],
Loc.GetString("command-spawnartifactwithnode-spawn-artifact-type-hint")
);
}
}

View file

@ -0,0 +1,26 @@
using Content.Shared.Xenoarchaeology.Artifact;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
using Robust.Shared.Toolshed;
using Robust.Shared.Toolshed.Syntax;
using Robust.Shared.Toolshed.TypeParsers;
namespace Content.Shared.Toolshed.TypeParsers.XenoArtifact;
/// <summary>
/// Custom type parser for toolshed commands
/// that lets choose entity prototype of XenoArtifact effect.
/// </summary>
public sealed partial class XenoEffectParser : CustomCompletionParser<ProtoId<EntityPrototype>>
{
[Dependency] private IEntityManager _entityManager = default!;
public override CompletionResult TryAutocomplete(ParserContext ctx, CommandArgument? arg)
{
var hint = ToolshedCommand.GetArgHint(arg, typeof(ProtoId<EntityPrototype>));
var artifact = _entityManager.System<SharedXenoArtifactSystem>();
return CompletionResult.FromHintOptions(artifact.EffectPrototypeIds, hint);
}
}

View file

@ -94,8 +94,15 @@ public abstract partial class SharedXenoArtifactSystem
{
var entProtoId = _entityTable.GetSpawns(ent.Comp.EffectsTable)
.First();
return CreateNode(ent, entProtoId, trigger, depth);
}
AddNode((ent, ent), entProtoId, out var nodeEnt, dirty: false);
/// <summary>
/// Creates artifact node entity, attaching trigger and marking depth level for future use.
/// </summary>
public Entity<XenoArtifactNodeComponent> CreateNode(Entity<XenoArtifactComponent> ent, EntProtoId effect, XenoArchTriggerPrototype trigger, int depth = 0)
{
AddNode((ent, ent), effect, out var nodeEnt, dirty: false);
DebugTools.Assert(nodeEnt.HasValue, "Failed to create node on artifact.");
var nodeComponent = nodeEnt.Value.Comp;

View file

@ -1,8 +1,10 @@
using Content.Shared.Actions;
using Content.Shared.Popups;
using Content.Shared.Toolshed.TypeParsers.XenoArtifact;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
@ -20,9 +22,16 @@ public abstract partial class SharedXenoArtifactSystem : EntitySystem
[Dependency] private SharedContainerSystem _container = default!;
[Dependency] private SharedPopupSystem _popup = default!;
// TODO: https://github.com/space-wizards/space-station-14/issues/45060
/// <summary> Cached EntProtoIds of all XenoArtifactEffect prototypes. Used for text hints. </summary>
[Access(typeof(XenoEffectParser), Other = AccessPermissions.None)]
public readonly HashSet<string> EffectPrototypeIds = [];
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
SubscribeLocalEvent<XenoArtifactComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<XenoArtifactComponent, ArtifactSelfActivateEvent>(OnSelfActivate);
@ -30,6 +39,28 @@ public abstract partial class SharedXenoArtifactSystem : EntitySystem
InitializeUnlock();
InitializeXAT();
InitializeXAE();
ReloadEffectCache();
}
private void OnPrototypesReloaded(PrototypesReloadedEventArgs args)
{
if (!args.WasModified<EntityPrototype>())
return;
ReloadEffectCache();
}
private void ReloadEffectCache()
{
EffectPrototypeIds.Clear();
foreach (var entityPrototype in ProtoMan.EnumeratePrototypes<EntityPrototype>())
{
// all effect prototypes are marked as nodes, as nodes are born from those prototypes
if (entityPrototype.HasComp<XenoArtifactNodeComponent>(Factory) && !entityPrototype.Abstract)
EffectPrototypeIds.Add(entityPrototype.ID);
}
}
/// <inheritdoc />

View file

@ -1,10 +1,29 @@
command-description-xenoartifact-list =
List all EntityUids of spawned artifacts.
command-description-xenoartifact-printMatrix =
command-description-xenoartifact-printmatrix =
Prints out matrix that displays all edges between nodes.
command-description-xenoartifact-totalResearch =
command-description-xenoartifact-totalresearch =
Gets all research points that can be extracted from artifact currently.
command-description-xenoartifact-averageResearch =
command-description-xenoartifact-averageresearch =
Calculates amount of research points average generated xeno artifact will output when fully activated.
command-description-xenoartifact-unlockAllNodes =
command-description-xenoartifact-unlockallnodes =
Unlocks all nodes of artifact.
command-description-xenoartifact-createnode =
Create node in artifact (new on depth 0 or attach next to existing one).
command-description-xenoartifact-createnodeatdepth =
Add a new node to the given artifact.
command-description-xenoartifact-spawnartwithnode =
Spawns a new xeno artifact with single node with the given trigger and effect.
command-description-xenoartifact-unlocknode =
Marks a node as unlocked.
command-description-xenoartifact-removenode =
Removes a node from a xeno artifact.
command-description-xenoartifact-addedge =
Adds an edge between two nodes of a xeno artifact.
command-spawnartifactwithnode-spawn-artifact-item-hint = use hand-held artifact
command-spawnartifactwithnode-spawn-artifact-structure-hint = use structure-like stationary artifact
command-spawnartifactwithnode-spawn-artifact-type-hint = <artifact entity proto id>
command-xenoartifact-common-node-hint = depth {$depth} node {$nodeId} trigger {$nodeDetail}

View file

@ -1,4 +0,0 @@
cmd-unlocknode-desc = Unlocks a node on a given artifact
cmd-unlocknode-help = unlocknode <artifact uid> <node uid>
cmd-unlocknode-artifact-hint = <artifact uid>
cmd-unlocknode-node-hint = <node uid>

View file

@ -1,4 +1,4 @@
- type: entity
- type: entity
parent: [BaseItem, BaseXenoArtifact]
id: BaseXenoArtifactItem
name: artifact
@ -61,6 +61,15 @@
tableId: XenoArtifactEffectsHandheldOnlyTable
weight: 2
- type: entity
id: DummyArtifactItem
parent: BaseXenoArtifactItem
name: artifact
suffix: Debug
components:
- type: XenoArtifact
isGenerationRequired: false
- type: entity
parent: BaseXenoArtifactItem
id: ComplexXenoArtifactItem

View file

@ -1,4 +1,4 @@
- type: entity
- type: entity
parent: [BaseStructureDynamic, BaseXenoArtifact]
id: BaseXenoArtifactStructure
name: artifact
@ -40,6 +40,15 @@
mask:
- MachineMask
- type: entity
id: DummyArtifactStructure
parent: BaseXenoArtifactStructure
name: artifact
suffix: Debug
components:
- type: XenoArtifact
isGenerationRequired: false
- type: entity
parent: BaseXenoArtifactStructure
id: ComplexXenoArtifact