From 8bf0961e5a1db822da9cc88bc221e6ca93e73ff7 Mon Sep 17 00:00:00 2001 From: Fildrance Date: Mon, 31 Aug 2026 20:58:52 +0000 Subject: [PATCH] Feature/refactor xenoarch trigger to be entity table ed (#45608) * refactor: rough strokes for initial implementation * refactor: fix ada remarks * refactor: fix XenoArtifactNodeParser not displaying trigger and effect properly in autocompletion * refactor: more changes for IsNotRepeatingCondition * refactor: update triggers declarations * refactor: remove IsNotRepeatingCondition - duplicate * refactor: fixup after merge with master * refactor: properly handle creation of improper trigger set, all selectors gt ToString override! * refactor: fix missing weight for some triggers, XenoArtifactCommand.UnlockAllNodes now accepts multiple entries * refactor: fix trigger hints loc being name explicitly and failing tests * yaml changes * rename * refactor: fix xml-doc copy-paste for XenoArtifactTriggerParser --------- Co-authored-by: iaada --- .../XenoArtifacts/XenoArtifactCommands.cs | 30 +- .../Artifact/XenoArtifactSystem.ProcGen.cs | 110 +++--- .../ExcludeEntitiesFromContextCondition.cs | 1 + .../EntitySelectors/AllSelector.cs | 6 + .../EntitySelectors/EntSelector.cs | 6 + .../EntitySelectors/GroupSelector.cs | 6 + .../EntitySelectors/NestedSelector.cs | 6 + .../EntitySelectors/NoneSelector.cs | 6 + .../EntityTable/EntityTableSystem.cs | 25 ++ ...tParser.cs => XenoArtifactEffectParser.cs} | 3 +- .../XenoArtifact/XenoArtifactNodeParser.cs | 8 +- .../XenoArtifact/XenoArtifactTriggerParser.cs | 26 ++ .../Components/XenoArtifactComponent.cs | 6 +- .../Prototypes/XenoArchTriggerPrototype.cs | 45 --- .../Artifact/SharedXenoArtifactSystem.Node.cs | 10 +- .../toolshed/xenoartifact-command.ftl | 2 +- Resources/Locale/en-US/entity-categories.ftl | 1 + .../en-US/xenoarchaeology/artifact-hints.ftl | 59 --- .../Xenoarchaeology/item_xenoartifacts.yml | 2 + Resources/Prototypes/Entities/categories.yml | 5 + Resources/Prototypes/XenoArch/triggers.yml | 350 +++++++++++------- 21 files changed, 399 insertions(+), 314 deletions(-) rename Content.Shared/Toolshed/TypeParsers/XenoArtifact/{XenoEffectParser.cs => XenoArtifactEffectParser.cs} (88%) create mode 100644 Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactTriggerParser.cs delete mode 100644 Content.Shared/Xenoarchaeology/Artifact/Prototypes/XenoArchTriggerPrototype.cs diff --git a/Content.Server/Toolshed/Commands/XenoArtifacts/XenoArtifactCommands.cs b/Content.Server/Toolshed/Commands/XenoArtifacts/XenoArtifactCommands.cs index 169147de0c..3d7de10c32 100644 --- a/Content.Server/Toolshed/Commands/XenoArtifacts/XenoArtifactCommands.cs +++ b/Content.Server/Toolshed/Commands/XenoArtifacts/XenoArtifactCommands.cs @@ -4,7 +4,6 @@ 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; @@ -116,15 +115,18 @@ public sealed partial class XenoArtifactCommand : ToolshedCommand /// Unlocks all nodes of artifact. [CommandImplementation("unlockallnodes")] - public void UnlockAllNodes([PipedArgument] EntityUid artifactEntityUid) + public void UnlockAllNodes([PipedArgument] IEnumerable artifactUids) { _artifact ??= Sys(); - var comp = Comp(artifactEntityUid); - - var nodes = _artifact.GetAllNodes((artifactEntityUid, comp)); - foreach (var node in nodes) + foreach (var artifactUid in artifactUids) { - _artifact.SetNodeUnlocked((node, node.Comp)); + var comp = Comp(artifactUid); + + var nodes = _artifact.GetAllNodes((artifactUid, comp)); + foreach (var node in nodes) + { + _artifact.SetNodeUnlocked((node, node.Comp)); + } } } @@ -134,8 +136,8 @@ public sealed partial class XenoArtifactCommand : ToolshedCommand [CommandImplementation("createnode")] public void CreateNodeNew( [CommandArgument] Entity artifact, - [CommandArgument(typeof(XenoEffectParser))] ProtoId effect, - [CommandArgument] ProtoId trigger + [CommandArgument(typeof(XenoArtifactEffectParser))] ProtoId effect, + [CommandArgument(typeof(XenoArtifactTriggerParser))] ProtoId trigger ) { CreateNode(artifact, effect, trigger); @@ -145,8 +147,8 @@ public sealed partial class XenoArtifactCommand : ToolshedCommand [CommandImplementation("createnodeatdepth")] public void CreateNodeAtDepth( [CommandArgument(typeof(XenoArtifactNodeParser))] (Entity Artifact, Entity Node) tuple, - [CommandArgument(typeof(XenoEffectParser))] ProtoId effect, - [CommandArgument] ProtoId trigger + [CommandArgument(typeof(XenoArtifactEffectParser))] ProtoId effect, + [CommandArgument(typeof(XenoArtifactTriggerParser))] ProtoId trigger ) { CreateNode(tuple.Artifact, effect, trigger, tuple.Node); @@ -157,8 +159,8 @@ public sealed partial class XenoArtifactCommand : ToolshedCommand public void SpawnArtifactWithNode( [CommandArgument] ICommonSession target, [CommandArgument(typeof(XenoArtifactTypeParser))] ProtoId artifactType, - [CommandArgument(typeof(XenoEffectParser))] ProtoId effect, - [CommandArgument] ProtoId trigger + [CommandArgument(typeof(XenoArtifactEffectParser))] ProtoId effect, + [CommandArgument(typeof(XenoArtifactTriggerParser))] ProtoId trigger ) { if (target.AttachedEntity == null) @@ -215,7 +217,7 @@ public sealed partial class XenoArtifactCommand : ToolshedCommand private void CreateNode( Entity artifact, ProtoId effect, - ProtoId trigger, + ProtoId trigger, Entity? node = null ) { diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 971c19c50f..f112512944 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -1,80 +1,53 @@ -using System.Linq; -using Content.Shared.Random.Helpers; -using Content.Shared.Whitelist; +using Content.Shared.EntityTable; +using Content.Shared.EntityTable.Conditions; using Content.Shared.Xenoarchaeology.Artifact.Components; -using Content.Shared.Xenoarchaeology.Artifact.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using System.Linq; namespace Content.Server.Xenoarchaeology.Artifact; public sealed partial class XenoArtifactSystem { - [Dependency] private EntityWhitelistSystem _entityWhitelist = default!; + [Dependency] private EntityTableSystem _entityTable = default!; + + /// + /// Trigger for fallback scenario, when artifact acquired no trigger when generating artifact. + /// + private static readonly EntProtoId DummyTrigger = "TriggerExamine"; private void GenerateArtifactStructure(Entity ent) { var nodeCount = ent.Comp.NodeCount.Next(RobustRandom); - var triggerPool = CreateTriggerPool(ent, nodeCount); + // trigger pool could be smaller, then requested node count - nodeCount = triggerPool.Count; + var totalTriggers = _entityTable.ListSpawns(ent.Comp.TriggersTable) + .Count(); + nodeCount = int.Min(nodeCount, totalTriggers); + var triggerPoolData = new TriggerPoolData(nodeCount); + ResizeNodeGraph(ent, nodeCount); while (nodeCount > 0) { - GenerateArtifactSegment(ent, triggerPool, ref nodeCount); + GenerateArtifactSegment(ent, triggerPoolData, ref nodeCount); } RebuildXenoArtifactMetaData((ent, ent)); } - /// - /// Creates pool from all node triggers that current artifact can support. - /// As artifact cannot re-use triggers, pool will be growing smaller - /// and smaller with each node generated. - /// - /// Artifact for which pool should be created. - /// - /// Max size of pool. Resulting pool is not guaranteed to be exactly as large, but it will 100% won't be bigger. - /// - private List CreateTriggerPool(Entity ent, int size) - { - var triggerPool = new List(size); - var weightsProto = ProtoMan.Index(ent.Comp.TriggerWeights); - var weightsByTriggersLeft = new Dictionary, float>(weightsProto.Weights); - - while (triggerPool.Count < size) - { - // OOPS! We ran out of triggers. - if (weightsByTriggersLeft.Count == 0) - { - Log.Error($"Insufficient triggers for generating {ToPrettyString(ent)}! Needed {size} but had {triggerPool.Count}"); - return triggerPool; - } - - var triggerId = RobustRandom.PickAndTake(weightsByTriggersLeft); - var trigger = ProtoMan.Index(triggerId); - if (_entityWhitelist.IsWhitelistFail(trigger.Whitelist, ent)) - continue; - - triggerPool.Add(trigger); - } - - return triggerPool; - } - /// /// Generates segment of artifact - isolated graph, nodes inside which are interconnected. /// As size of segment is randomized - it is subtracted from node count. /// private void GenerateArtifactSegment( Entity ent, - List triggerPool, + TriggerPoolData triggerPoolData, ref int nodeCount ) { var segmentSize = GetArtifactSegmentSize(ent, nodeCount); nodeCount -= segmentSize; - var populatedNodes = PopulateArtifactSegmentRecursive(ent, triggerPool, ref segmentSize); + var populatedNodes = PopulateArtifactSegmentRecursive(ent, triggerPoolData, ref segmentSize); var segments = GetSegmentsFromNodes(ent, populatedNodes).ToList(); @@ -139,7 +112,7 @@ public sealed partial class XenoArtifactSystem /// private List> PopulateArtifactSegmentRecursive( Entity ent, - List triggerPool, + TriggerPoolData triggerPoolData, ref int segmentSize, int iteration = 0 ) @@ -162,13 +135,25 @@ public sealed partial class XenoArtifactSystem var nodes = new List>(); for (var i = 0; i < nodeCount; i++) { - var trigger = RobustRandom.PickAndTake(triggerPool); - nodes.Add(CreateNode(ent, trigger, iteration)); + var trigger = _entityTable.GetFirstOrDefault(ent.Comp.TriggersTable, ctx: triggerPoolData.Context); + if (trigger == null) + { + trigger = DummyTrigger; + Log.Error( + "Failed to generate proper artifact - selector {selector} with excepted entities {excepted} " + + "provided zero triggers upon requesting new one", + ent.Comp.TriggersTable, + string.Join(", ", triggerPoolData.UsedTriggers.Select(x => x.Id)) + ); + } + + triggerPoolData.AddTriggerAsUsed(trigger.Value); + nodes.Add(CreateNode(ent, trigger.Value, iteration)); } var successors = PopulateArtifactSegmentRecursive( ent, - triggerPool, + triggerPoolData, ref segmentSize, iteration: iteration + 1 ); @@ -216,4 +201,31 @@ public sealed partial class XenoArtifactSystem return segmentSize; } + + /// + /// Container that represents pool of XenoArtifact triggers. + /// + private sealed class TriggerPoolData + { + private readonly HashSet _usedTriggers; + + public TriggerPoolData(int requestedSize) + { + _usedTriggers = new(requestedSize); + Context = new EntityTableContext(new Dictionary + { + [ExcludeEntitiesFromContextCondition.EntitiesToExclude] = _usedTriggers + }); + } + + public readonly EntityTableContext Context; + + public void AddTriggerAsUsed(EntProtoId trigger) + { + if (!_usedTriggers.Add(trigger)) + throw new ArgumentException(); + } + + public IReadOnlyCollection UsedTriggers => _usedTriggers; + } } diff --git a/Content.Shared/EntityTable/Conditions/ExcludeEntitiesFromContextCondition.cs b/Content.Shared/EntityTable/Conditions/ExcludeEntitiesFromContextCondition.cs index d7bdc9e59b..573293ac7a 100644 --- a/Content.Shared/EntityTable/Conditions/ExcludeEntitiesFromContextCondition.cs +++ b/Content.Shared/EntityTable/Conditions/ExcludeEntitiesFromContextCondition.cs @@ -11,6 +11,7 @@ public sealed partial class ExcludeEntitiesFromContextCondition : EntityTableCon { /// /// Context key used to track which entity prototypes should not be spawned. + /// Value should be . /// public const string EntitiesToExclude = "EntitiesToExclude"; diff --git a/Content.Shared/EntityTable/EntitySelectors/AllSelector.cs b/Content.Shared/EntityTable/EntitySelectors/AllSelector.cs index 66c52155cc..f209ef956f 100644 --- a/Content.Shared/EntityTable/EntitySelectors/AllSelector.cs +++ b/Content.Shared/EntityTable/EntitySelectors/AllSelector.cs @@ -45,4 +45,10 @@ public sealed partial class AllSelector : EntityTableSelectorWithChildrenBase } } } + + /// + public override string ToString() + { + return $"All({string.Join(", ", Children)})"; + } } diff --git a/Content.Shared/EntityTable/EntitySelectors/EntSelector.cs b/Content.Shared/EntityTable/EntitySelectors/EntSelector.cs index bc99f2cf83..4eec6320d2 100644 --- a/Content.Shared/EntityTable/EntitySelectors/EntSelector.cs +++ b/Content.Shared/EntityTable/EntitySelectors/EntSelector.cs @@ -44,4 +44,10 @@ public sealed partial class EntSelector : EntityTableSelector { yield return (Id, Amount.Average()); } + + /// + public override string ToString() + { + return $"{Id} x{Amount.Average()}"; + } } diff --git a/Content.Shared/EntityTable/EntitySelectors/GroupSelector.cs b/Content.Shared/EntityTable/EntitySelectors/GroupSelector.cs index 33cca7bd90..4aa6206095 100644 --- a/Content.Shared/EntityTable/EntitySelectors/GroupSelector.cs +++ b/Content.Shared/EntityTable/EntitySelectors/GroupSelector.cs @@ -65,4 +65,10 @@ public sealed partial class GroupSelector : EntityTableSelectorWithChildrenBase } } } + + /// + public override string ToString() + { + return $"Group({string.Join(", ", Children.Select(x => $"{x}: {x.Weight}"))})"; + } } diff --git a/Content.Shared/EntityTable/EntitySelectors/NestedSelector.cs b/Content.Shared/EntityTable/EntitySelectors/NestedSelector.cs index 1ca91051af..5c1a4952cf 100644 --- a/Content.Shared/EntityTable/EntitySelectors/NestedSelector.cs +++ b/Content.Shared/EntityTable/EntitySelectors/NestedSelector.cs @@ -47,4 +47,10 @@ public sealed partial class NestedSelector : EntityTableSelectorWithNestedBase { return proto.Index(TableId).Table.AverageSpawns(entMan, proto, ctx); } + + /// + public override string ToString() + { + return $"Nested({TableId})"; + } } diff --git a/Content.Shared/EntityTable/EntitySelectors/NoneSelector.cs b/Content.Shared/EntityTable/EntitySelectors/NoneSelector.cs index 72ef181cbe..a24f74c816 100644 --- a/Content.Shared/EntityTable/EntitySelectors/NoneSelector.cs +++ b/Content.Shared/EntityTable/EntitySelectors/NoneSelector.cs @@ -25,4 +25,10 @@ public sealed partial class NoneSelector : EntityTableSelector { yield break; } + + /// + public override string ToString() + { + return "None"; + } } diff --git a/Content.Shared/EntityTable/EntityTableSystem.cs b/Content.Shared/EntityTable/EntityTableSystem.cs index e75748cb66..8241b33359 100644 --- a/Content.Shared/EntityTable/EntityTableSystem.cs +++ b/Content.Shared/EntityTable/EntityTableSystem.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using System.Linq; using Content.Shared.EntityTable.EntitySelectors; using JetBrains.Annotations; using Robust.Shared.Prototypes; @@ -10,6 +11,30 @@ public sealed partial class EntityTableSystem : EntitySystem { [Dependency] private IRobustRandom _random = default!; + /// + /// Iterates once through Entities that EntitySelector in EntityTableProto provides and picks first one. + /// + public EntProtoId? GetFirstOrDefault( + EntityTablePrototype entTableProto, + IRobustRandom? rand = null, + EntityTableContext? ctx = null + ) + { + return GetSpawns(entTableProto, rand, ctx).FirstOrDefault(); + } + + /// + /// Iterates once through Entities that EntitySelector provides and picks first one. + /// + public EntProtoId? GetFirstOrDefault( + EntityTableSelector? entTableProto, + IRobustRandom? rand = null, + EntityTableContext? ctx = null + ) + { + return GetSpawns(entTableProto, rand, ctx).FirstOrDefault(); + } + /// /// Compiles a random list of entity prototypes using constraints. /// diff --git a/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoEffectParser.cs b/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactEffectParser.cs similarity index 88% rename from Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoEffectParser.cs rename to Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactEffectParser.cs index b01747e979..77f67b3e84 100644 --- a/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoEffectParser.cs +++ b/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactEffectParser.cs @@ -1,4 +1,3 @@ -using System.Linq; using Robust.Shared.Console; using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; @@ -11,7 +10,7 @@ namespace Content.Shared.Toolshed.TypeParsers.XenoArtifact; /// Custom type parser for toolshed commands /// that lets choose entity prototype of XenoArtifact effect. /// -public sealed partial class XenoEffectParser : CustomCompletionParser> +public sealed partial class XenoArtifactEffectParser : CustomCompletionParser> { private static readonly ProtoId EffectCategoryId = "XenoArtifactEffects"; diff --git a/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactNodeParser.cs b/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactNodeParser.cs index 23266f7a5d..e8f15cb165 100644 --- a/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactNodeParser.cs +++ b/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactNodeParser.cs @@ -31,14 +31,18 @@ public sealed partial class XenoArtifactNodeParser : CustomCompletionParser<(Ent node => { var metadata = _entityManager.GetComponent(node); - var entDescription = Loc.GetString(metadata.EntityDescription); + var effect = Loc.GetString(metadata.EntityDescription); + var trigger = node.Comp.TriggerTip.HasValue + ? Loc.GetString(node.Comp.TriggerTip) + : "Unknown"; return new CompletionOption( node.Owner.ToString(), Loc.GetString( "command-xenoartifact-common-node-hint", ("depth", node.Comp.Depth), ("nodeId", artifact.GetNodeId(node.Owner)), - ("nodeDetail", entDescription) + ("trigger", trigger), + ("effect", effect) ) ); }); diff --git a/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactTriggerParser.cs b/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactTriggerParser.cs new file mode 100644 index 0000000000..1ebae9118c --- /dev/null +++ b/Content.Shared/Toolshed/TypeParsers/XenoArtifact/XenoArtifactTriggerParser.cs @@ -0,0 +1,26 @@ +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; + +/// +/// Custom type parser for toolshed commands +/// that lets choose entity prototype of XenoArtifact trigger. +/// +public sealed partial class XenoArtifactTriggerParser : CustomCompletionParser> +{ + private static readonly ProtoId TriggerCategoryId = "XenoArtifactTriggers"; + + [Dependency] private IPrototypeManager _prototype = default!; + + public override CompletionResult TryAutocomplete(ParserContext ctx, CommandArgument? arg) + { + var hint = ToolshedCommand.GetArgHint(arg, typeof(ProtoId)); + var completionOptions = CompletionHelper.EntityPrototypes(ctx.GetWord(), TriggerCategoryId, _prototype); + return CompletionResult.FromHintOptions(completionOptions, hint); + + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index 144dccc546..9c87b80a2a 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -2,7 +2,6 @@ using Content.Shared.Actions; using Content.Shared.Actions.Components; using Content.Shared.Destructible.Thresholds; using Content.Shared.EntityTable.EntitySelectors; -using Content.Shared.Xenoarchaeology.Artifact.Prototypes; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -146,7 +145,10 @@ public sealed partial class XenoArtifactComponent : Component /// Triggers that can be used during this artefact generation. /// [DataField] - public ProtoId TriggerWeights = "DefaultTriggers"; + public EntityTableSelector TriggersTable = new NestedSelector + { + TableId = "XenoArtifactTriggerDefaultTable" + }; #endregion /// diff --git a/Content.Shared/Xenoarchaeology/Artifact/Prototypes/XenoArchTriggerPrototype.cs b/Content.Shared/Xenoarchaeology/Artifact/Prototypes/XenoArchTriggerPrototype.cs deleted file mode 100644 index 05a33ea037..0000000000 --- a/Content.Shared/Xenoarchaeology/Artifact/Prototypes/XenoArchTriggerPrototype.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Content.Shared.Random; -using Content.Shared.Whitelist; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Xenoarchaeology.Artifact.Prototypes; - -/// Proto for xeno artifact triggers - markers, which event could trigger node to unlock it. -[Prototype] -public sealed partial class XenoArchTriggerPrototype : IPrototype -{ - /// - [IdDataField] - public string ID { get; private set; } = default!; - - /// - /// Tip for user on how to activate this trigger. - /// - [DataField] - public LocId Tip; - - /// - /// Whitelist, describing for which subtype of artifacts this trigger could be used. - /// - [DataField] - public EntityWhitelist? Whitelist; - - /// - /// List of components that represent ways to trigger node. - /// - [DataField] - public ComponentRegistry Components = new(); -} - -/// -/// Container for list of xeno artifact triggers and their respective weights to be used in case randomly rolling trigger is required. -/// -[Prototype] -public sealed partial class WeightedRandomXenoArchTriggerPrototype : IWeightedRandomPrototype -{ - [IdDataField] - public string ID { get; private set; } = default!; - - [DataField] - public Dictionary, float> Weights { get; private set; } = new(); -} diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index cf63b0ec84..7a182599e6 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -2,7 +2,6 @@ using System.Linq; using Content.Shared.EntityTable; using Content.Shared.NameIdentifier; using Content.Shared.Xenoarchaeology.Artifact.Components; -using Content.Shared.Xenoarchaeology.Artifact.Prototypes; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -81,7 +80,7 @@ public abstract partial class SharedXenoArtifactSystem /// /// Creates artifact node entity, attaching trigger and marking depth level for future use. /// - public Entity CreateNode(Entity ent, ProtoId trigger, int depth = 0) + public Entity CreateNode(Entity ent, EntProtoId trigger, int depth = 0) { var triggerProto = ProtoMan.Index(trigger); return CreateNode(ent, triggerProto, depth); @@ -90,7 +89,7 @@ public abstract partial class SharedXenoArtifactSystem /// /// Creates artifact node entity, attaching trigger and marking depth level for future use. /// - public Entity CreateNode(Entity ent, XenoArchTriggerPrototype trigger, int depth = 0) + public Entity CreateNode(Entity ent, EntityPrototype trigger, int depth = 0) { var entProtoId = _entityTable.GetSpawns(ent.Comp.EffectsTable) .First(); @@ -100,14 +99,15 @@ public abstract partial class SharedXenoArtifactSystem /// /// Creates artifact node entity, attaching trigger and marking depth level for future use. /// - public Entity CreateNode(Entity ent, EntProtoId effect, XenoArchTriggerPrototype trigger, int depth = 0) + public Entity CreateNode(Entity ent, EntProtoId effect, EntityPrototype 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; nodeComponent.Depth = depth; - nodeComponent.TriggerTip = trigger.Tip; + nodeComponent.TriggerTip = trigger.Name; + EntityManager.AddComponents(nodeEnt.Value, trigger.Components); Dirty(nodeEnt.Value); diff --git a/Resources/Locale/en-US/commands/toolshed/xenoartifact-command.ftl b/Resources/Locale/en-US/commands/toolshed/xenoartifact-command.ftl index e65443defd..2aa21f8efd 100644 --- a/Resources/Locale/en-US/commands/toolshed/xenoartifact-command.ftl +++ b/Resources/Locale/en-US/commands/toolshed/xenoartifact-command.ftl @@ -26,4 +26,4 @@ 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 = -command-xenoartifact-common-node-hint = depth {$depth} node {$nodeId} trigger {$nodeDetail} +command-xenoartifact-common-node-hint = depth {$depth} node {$nodeId} trigger: '{$trigger}', effect: '{$effect}' diff --git a/Resources/Locale/en-US/entity-categories.ftl b/Resources/Locale/en-US/entity-categories.ftl index 3ed481333e..157115ac6d 100644 --- a/Resources/Locale/en-US/entity-categories.ftl +++ b/Resources/Locale/en-US/entity-categories.ftl @@ -6,5 +6,6 @@ entity-category-name-mapping = Mapping entity-category-name-donotmap = Do not map entity-category-name-status-effects = Status Effects entity-category-name-xeno-artifact-effects = Xeno Artifact Effects +entity-category-name-xeno-artifact-triggers = Xeno Artifact triggers entity-category-suffix-donotmap = DO NOT MAP diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl index bfffcde8cb..bc9df24f4d 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl @@ -22,65 +22,6 @@ artifact-effect-hint-polymorph = Transmogrificational activity artifact-effect-hint-magnet = Magnetic waves artifact-effect-hint-visual = Visual distortions -# the triggers should be more obvious than the effects -# gives people an idea of what to do: don't be too specific (i.e. no "welders") - -artifact-trigger-hint-electricity = Electricity -artifact-trigger-hint-heat = High temperatures -artifact-trigger-hint-physical = Physical trauma -artifact-trigger-hint-tool = Tool usage -artifact-trigger-hint-music = Sonic vibrations -artifact-trigger-hint-water = Hydro-reactive -artifact-trigger-hint-blood = Reaction with hematological fluid -artifact-trigger-hint-magnet = Magnetic waves -artifact-trigger-hint-death = Life essence -artifact-trigger-hint-radiation = Radiation -artifact-trigger-hint-pressure = Extreme pressure -artifact-trigger-hint-regular-gases = Standard atmospheric gases -artifact-trigger-hint-plasma = Gaseous plasma -artifact-trigger-hint-land = Active deceleration -artifact-trigger-hint-examine = Examination -artifact-trigger-hint-medical = Therapeutic chemicals - -xenoarch-trigger-tip-music = Harmonical sound vibrations -xenoarch-trigger-tip-heat = High temperature gas -xenoarch-trigger-tip-cold = Low temperature gas -xenoarch-trigger-tip-no-oxygen = Oxygen-free environment -xenoarch-trigger-tip-water = Water -xenoarch-trigger-tip-co2 = Carbon dioxide -xenoarch-trigger-tip-plasma = Non-solid plasma -xenoarch-trigger-tip-tritium = Tritium -xenoarch-trigger-tip-ammonia = Ammonia -xenoarch-trigger-tip-n2o = Nitrous oxide -xenoarch-trigger-tip-frezon = Frezon -xenoarch-trigger-tip-radiation = Heavy-ion radiation -xenoarch-trigger-tip-microwave = Microwave radiation -xenoarch-trigger-tip-brute-damage = Physical damage -xenoarch-trigger-tip-interaction = Physical interaction -xenoarch-trigger-tip-wrenching = Tightening -xenoarch-trigger-tip-prying = Prying -xenoarch-trigger-tip-screwing = Screwing -xenoarch-trigger-tip-pulsing = Pulsing -xenoarch-trigger-tip-pressure-low = Low pressure -xenoarch-trigger-tip-pressure-high = High pressure -xenoarch-trigger-tip-examine = Close inspection -xenoarch-trigger-tip-timer = Regular self-activation -xenoarch-trigger-tip-blood = Blood -xenoarch-trigger-tip-throw = Being thrown -xenoarch-trigger-tip-death = Death -xenoarch-trigger-tip-magnet = Magnetic waves -xenoarch-trigger-tip-money = Bribery -xenoarch-trigger-tip-knowledge = Knowledge Intake -xenoarch-trigger-tip-carbs = Carbohydrate Intake -xenoarch-trigger-tip-meat = Protein Intake -xenoarch-trigger-tip-produce = Healthy Produce Intake -xenoarch-trigger-tip-stamp = Bureaucratic Approval -xenoarch-trigger-tip-shock = Electrical Shock -xenoarch-trigger-tip-particle = Singularity-Effecting Particles -xenoarch-trigger-tip-anomparticle = Anomaly-Effecting Particles -xenoarch-trigger-tip-stamina-damage = Organic Exhaustion -xenoarch-trigger-tip-laser = High-Intensity photon impacts - ### Description hints xenoarch-trigger-examine-wrenching = There's a loose bit spinning around. xenoarch-trigger-examine-prying = There's a panel coming up from the surface. diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml index ea6a16482c..446dc3302f 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml @@ -52,6 +52,8 @@ graph: Artifact node: done - type: XenoArtifact + triggersTable: !type:NestedSelector + tableId: XenoArtifactHandheldOnlyTriggerTable effectsTable: !type:GroupSelector children: - !type:NestedSelector diff --git a/Resources/Prototypes/Entities/categories.yml b/Resources/Prototypes/Entities/categories.yml index 4fb30f9ef0..e2928e2185 100644 --- a/Resources/Prototypes/Entities/categories.yml +++ b/Resources/Prototypes/Entities/categories.yml @@ -37,3 +37,8 @@ id: XenoArtifactEffects name: entity-category-name-xeno-artifact-effects hideSpawnMenu: true + +- type: entityCategory + id: XenoArtifactTriggers + name: entity-category-name-xeno-artifact-triggers + hideSpawnMenu: true diff --git a/Resources/Prototypes/XenoArch/triggers.yml b/Resources/Prototypes/XenoArch/triggers.yml index 75fcbba673..7cc71669dd 100644 --- a/Resources/Prototypes/XenoArch/triggers.yml +++ b/Resources/Prototypes/XenoArch/triggers.yml @@ -1,55 +1,100 @@ -- type: weightedRandomXenoArchTrigger - id: DefaultTriggers - weights: - TriggerMusic: 1 - TriggerHeat: 1 - TriggerCold: 0.5 - TriggerNoOxygen: 1 - TriggerWater: 1 - TriggerCO2: 0.5 - TriggerPlasma: 0.5 - TriggerN2O: 0.5 - TriggerTritium: 0.1 - TriggerAmmonia: 0.1 - TriggerFrezon: 0.1 - TriggerRadiation: 1 - TriggerPressureHigh: 0.5 - TriggerPressureLow: 1 - TriggerExamine: 1 - TriggerBruteDamage: 1 - #TriggerInteraction: 1 #this one interferes with activating artifact AND brute dmg! - TriggerWrenching: 0.5 - TriggerPrying: 0.5 - TriggerScrewing: 0.5 - TriggerPulsing: 0.5 - TriggerTimer: 0.25 - TriggerBlood: 1 - TriggerThrow: 1 - TriggerDeath: 1 - TriggerMagnet: 1 - TriggerConsumeMoney: 1 - TriggerConsumeKnowledge: 0.25 - TriggerConsumeCarbs: 1 - TriggerConsumeMeat: 0.5 - TriggerConsumeProduce: 0.5 - TriggerInteractStamp: 1 - TriggerInteractShock: 1 - TriggerAttackParticleEngine: 0.1 - TriggerAttackParticleAnomaly: 1 - TriggerAttackStaminaDamage: 1 - TriggerAttackLaser: 0.5 +- type: entityTable + id: XenoArtifactTriggerDefaultTable + table: !type:GroupSelector + conditionsForChildren: + - !type:ExcludeEntitiesFromContextCondition + children: + - id: TriggerMusic + - id: TriggerHeat + - id: TriggerCold + weight: 0.5 + - id: TriggerNoOxygen + - id: TriggerWater + - id: TriggerCO2 + weight: 0.5 + - id: TriggerPlasma + weight: 0.5 + - id: TriggerN2O + weight: 0.5 + - id: TriggerTritium + weight: 0.1 + - id: TriggerAmmonia + weight: 0.1 + - id: TriggerFrezon + weight: 0.1 + - id: TriggerRadiation + - id: TriggerPressureHigh + weight: 0.5 + - id: TriggerPressureLow + - id: TriggerExamine + - id: TriggerBruteDamage + #- id: TriggerInteraction #this one interferes with activating artifact AND brute dmg! + - id: TriggerWrenching + weight: 0.5 + - id: TriggerPrying + weight: 0.5 + - id: TriggerScrewing + weight: 0.5 + - id: TriggerPulsing + weight: 0.5 + - id: TriggerTimer + weight: 0.25 + - id: TriggerBlood + - id: TriggerDeath + - id: TriggerMagnet + - id: TriggerConsumeMoney + - id: TriggerConsumeKnowledge + weight: 0.25 + - id: TriggerConsumeCarbs + - id: TriggerConsumeMeat + weight: 0.5 + - id: TriggerConsumeProduce + weight: 0.5 + - id: TriggerInteractStamp + - id: TriggerInteractShock + - id: TriggerAttackParticleAnomaly + - id: TriggerAttackStaminaDamage + - id: TriggerAttackLaser + weight: 0.5 + - id: TriggerAttackParticleEngine + weight: 0.1 -- type: xenoArchTrigger +- type: entityTable + id: XenoArtifactHandheldOnlyTriggerTable + table: !type:GroupSelector + conditionsForChildren: + - !type:ExcludeEntitiesFromContextCondition + children: + - !type:NestedSelector + tableId: XenoArtifactTriggerDefaultTable + weight: 35 + - !type:GroupSelector # Handheld exclusive + weight: 2 + children: + - id: TriggerThrow + - id: TriggerMicrowave + +- type: entity + abstract: true + id: BaseXenoArtifactTrigger + name: Unknown + categories: [XenoArtifactTriggers] + +#region Standard triggers + +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerMusic - tip: xenoarch-trigger-tip-music + name: Harmonical sound vibrations components: - type: XATCompNearby requireComponentWithName: ActiveInstrument radius: 2 -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerHeat - tip: xenoarch-trigger-tip-heat + name: High temperature gas components: - type: XATTemperature targetTemperature: 373 @@ -57,15 +102,16 @@ - type: XATDamageThresholdReached typesNeeded: Heat: 20 -# This kinda trivializes the difficulty. -# - type: XATToolUse -# requiredTool: Welding -# delay: 5 -# fuel: 10 + # This kinda trivializes the difficulty. + # - type: XATToolUse + # requiredTool: Welding + # delay: 5 + # fuel: 10 -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerCold - tip: xenoarch-trigger-tip-cold + name: Low temperature gas components: - type: XATTemperature targetTemperature: 255 @@ -74,18 +120,20 @@ typesNeeded: Cold: 20 -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerNoOxygen - tip: xenoarch-trigger-tip-no-oxygen + name: Oxygen-free environment components: - type: XATGas targetGas: Oxygen moles: 10 shouldBePresent: false -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerWater - tip: xenoarch-trigger-tip-water + name: Water components: - type: XATGas targetGas: WaterVapor @@ -93,9 +141,10 @@ reagents: - Water -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerCO2 - tip: xenoarch-trigger-tip-co2 + name: Carbon dioxide components: - type: XATGas targetGas: CarbonDioxide @@ -103,9 +152,10 @@ reagents: - CarbonDioxide -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerPlasma - tip: xenoarch-trigger-tip-plasma + name: Non-solid plasma components: - type: XATGas targetGas: Plasma @@ -113,9 +163,10 @@ reagents: - Plasma -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerTritium - tip: xenoarch-trigger-tip-tritium + name: Tritium components: - type: XATGas targetGas: Tritium @@ -123,9 +174,10 @@ reagents: - Tritium -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerAmmonia - tip: xenoarch-trigger-tip-ammonia + name: Ammonia components: - type: XATGas targetGas: Ammonia @@ -133,9 +185,10 @@ reagents: - Ammonia -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerN2O - tip: xenoarch-trigger-tip-n2o + name: Nitrous oxide components: - type: XATGas targetGas: NitrousOxide @@ -143,9 +196,10 @@ reagents: - NitrousOxide -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerFrezon - tip: xenoarch-trigger-tip-frezon + name: Frezon components: - type: XATGas targetGas: Frezon @@ -153,105 +207,108 @@ reagents: - Frezon -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerRadiation - tip: xenoarch-trigger-tip-radiation + name: Heavy-ion radiation components: - type: XATDamageThresholdReached typesNeeded: Radiation: 20 -- type: xenoArchTrigger - id: TriggerMicrowave - tip: xenoarch-trigger-tip-microwave - whitelist: - components: - - Item - components: - - type: XATMicrowave - -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerPressureHigh - tip: xenoarch-trigger-tip-pressure-high + name: High pressure components: - type: XATPressure maxPressureThreshold: 385 -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerPressureLow - tip: xenoarch-trigger-tip-pressure-low + name: Low pressure components: - type: XATPressure minPressureThreshold: 50 -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerExamine - tip: xenoarch-trigger-tip-examine + name: Close inspection components: - type: XATExamine -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerBruteDamage - tip: xenoarch-trigger-tip-brute-damage + name: Physical damage components: - type: XATDamageThresholdReached groupsNeeded: Brute: 20 -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerInteraction - tip: xenoarch-trigger-tip-interaction + name: Physical interaction components: - type: XATInteraction -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerWrenching - tip: xenoarch-trigger-tip-wrenching + name: Tightening components: - type: XATToolUse requiredTool: Anchoring - type: XATExaminableText examineText: xenoarch-trigger-examine-wrenching -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerPrying - tip: xenoarch-trigger-tip-prying + name: Prying components: - type: XATToolUse requiredTool: Prying - type: XATExaminableText examineText: xenoarch-trigger-examine-prying -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerScrewing - tip: xenoarch-trigger-tip-screwing + name: Screwing components: - type: XATToolUse requiredTool: Screwing - type: XATExaminableText examineText: xenoarch-trigger-examine-screwing -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerPulsing - tip: xenoarch-trigger-tip-pulsing + name: Pulsing components: - type: XATToolUse requiredTool: Pulsing - type: XATExaminableText examineText: xenoarch-trigger-examine-pulsing -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerTimer - tip: xenoarch-trigger-tip-timer + name: Regular self-activation components: - type: XATTimer possibleDelayInSeconds: min: 80 max: 120 -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerBlood - tip: xenoarch-trigger-tip-blood + name: Blood components: - type: XATReactive reagents: @@ -264,30 +321,24 @@ - Sap - SulfurBlood -- type: xenoArchTrigger - id: TriggerThrow - tip: xenoarch-trigger-tip-throw - whitelist: - components: - - Item - components: - - type: XATItemLand - -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerDeath - tip: xenoarch-trigger-tip-death + name: Death components: - type: XATDeath -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerMagnet - tip: xenoarch-trigger-tip-magnet + name: Magnetic waves components: - type: XATMagnet -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerConsumeMoney - tip: xenoarch-trigger-tip-money + name: Bribery components: - type: XATInteractWith interactionCount: @@ -300,9 +351,10 @@ components: - Cash -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerConsumeKnowledge - tip: xenoarch-trigger-tip-knowledge + name: Knowledge intake components: - type: XATInteractWith destroyAfter: true @@ -314,9 +366,10 @@ tags: - Book -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerConsumeCarbs - tip: xenoarch-trigger-tip-carbs + name: Carbohydrate intake components: - type: XATInteractWith destroyAfter: true @@ -334,9 +387,10 @@ - Pie - Pizza -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerConsumeMeat - tip: xenoarch-trigger-tip-meat + name: Protein intake components: - type: XATInteractWith destroyAfter: true @@ -353,9 +407,10 @@ - Protein - UncookedAnimalProteins -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerConsumeProduce - tip: xenoarch-trigger-tip-produce + name: Healthy produce intake components: - type: XATInteractWith destroyAfter: true @@ -372,9 +427,10 @@ - Ambrosia - Mushroom -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerInteractStamp - tip: xenoarch-trigger-tip-stamp + name: Bureaucratic approval components: - type: XATInteractWith interactionTime: 0.5 @@ -387,43 +443,48 @@ components: - Stamp -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerInteractShock - tip: xenoarch-trigger-tip-shock + name: Electrical shock components: - type: XATShock - type: XATDamageThresholdReached typesNeeded: Shock: 5 -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerAttackParticleEngine - tip: xenoarch-trigger-tip-particle + name: Singularity-Effecting particles components: - type: XATInteractAttack whitelist: components: - ParticleProjectile -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerAttackParticleAnomaly - tip: xenoarch-trigger-tip-anomparticle + name: Anomaly-Effecting particles components: - type: XATInteractAttack whitelist: components: - AnomalousParticle -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerAttackStaminaDamage - tip: xenoarch-trigger-tip-stamina-damage + name: Organic exhaustion components: - type: XATStaminaDamageThresholdReached damageNeeded: 8 # half a practice disabler capacity -- type: xenoArchTrigger +- type: entity + parent: BaseXenoArtifactTrigger id: TriggerAttackLaser - tip: xenoarch-trigger-tip-laser + name: High-Intensity photon impacts components: - type: XATInteractAttack interactionCount: @@ -433,3 +494,22 @@ - LaserProjectile tags: - EmitterBolt + +#endregion +#region Handheld triggers + +- type: entity + parent: BaseXenoArtifactTrigger + id: TriggerMicrowave + name: Microwave radiation + components: + - type: XATMicrowave + +- type: entity + parent: BaseXenoArtifactTrigger + id: TriggerThrow + name: Being thrown + components: + - type: XATItemLand + +#endregion