From eba8da7fc69cc10f56eabd38299ed760328521f9 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Sun, 16 Jun 2024 20:55:41 -0400 Subject: [PATCH 01/81] DAG Adjacency Matrix & Tests --- .../Tests/XenoArtifactTest.cs | 312 +++++++++++++ .../Artifact/XenoArtifactSystem.cs | 10 + .../Components/XenoArtifactComponent.cs | 47 ++ .../Components/XenoArtifactNodeComponent.cs | 38 ++ .../SharedXenoArtifactSystem.Graph.cs | 442 ++++++++++++++++++ .../Artifact/SharedXenoArtifactSystem.Node.cs | 120 +++++ .../Artifact/SharedXenoArtifactSystem.cs | 22 + 7 files changed, 991 insertions(+) create mode 100644 Content.IntegrationTests/Tests/XenoArtifactTest.cs create mode 100644 Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs diff --git a/Content.IntegrationTests/Tests/XenoArtifactTest.cs b/Content.IntegrationTests/Tests/XenoArtifactTest.cs new file mode 100644 index 000000000000..13c65508980c --- /dev/null +++ b/Content.IntegrationTests/Tests/XenoArtifactTest.cs @@ -0,0 +1,312 @@ +using System.Linq; +using Content.Shared.Xenoarchaeology.Artifact; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.Tests; + +[TestFixture] +public sealed class XenoArtifactTest +{ + [TestPrototypes] + private const string Prototypes = @" +- type: entity + id: TestArtifact + name: artifact + components: + - type: XenoArtifact + +- type: entity + id: TestArtifactNode + name: artifact node + components: + - type: XenoArtifactNode + maxDurability: 3 +"; + + /// + /// Checks that adding nodes and edges properly adds them into the adjacency matrix + /// + [Test] + public async Task XenoArtifactAddNodeTest() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entManager = server.ResolveDependency(); + var artifactSystem = entManager.System(); + + await server.WaitPost(() => + { + var artifactUid = entManager.Spawn("TestArtifact"); + var artifactEnt = (artifactUid, entManager.GetComponent(artifactUid)); + + // Create 3 nodes + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3)); + + Assert.That(artifactSystem.GetAllNodeIndices(artifactEnt).Count(), Is.EqualTo(3)); + + // Add connection from 1 -> 2 and 2-> 3 + artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value); + + // Assert that successors and direct successors are counted correctly for node 1. + Assert.That(artifactSystem.GetDirectSuccessorNodes(artifactEnt, node1!.Value).Count, Is.EqualTo(1)); + Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node1!.Value).Count, Is.EqualTo(2)); + // Assert that we didn't somehow get predecessors on node 1. + Assert.That(artifactSystem.GetDirectPredecessorNodes(artifactEnt, node1!.Value), Is.Empty); + Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node1!.Value), Is.Empty); + + // Assert that successors and direct successors are counted correctly for node 2. + Assert.That(artifactSystem.GetDirectSuccessorNodes(artifactEnt, node2!.Value), Has.Count.EqualTo(1)); + Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node2!.Value), Has.Count.EqualTo(1)); + // Assert that predecessors and direct predecessors are counted correctly for node 2. + Assert.That(artifactSystem.GetDirectPredecessorNodes(artifactEnt, node2!.Value), Has.Count.EqualTo(1)); + Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node2!.Value), Has.Count.EqualTo(1)); + + // Assert that successors and direct successors are counted correctly for node 3. + Assert.That(artifactSystem.GetDirectSuccessorNodes(artifactEnt, node3!.Value), Is.Empty); + Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node3!.Value), Is.Empty); + // Assert that predecessors and direct predecessors are counted correctly for node 3. + Assert.That(artifactSystem.GetDirectPredecessorNodes(artifactEnt, node3!.Value), Has.Count.EqualTo(1)); + Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node3!.Value), Has.Count.EqualTo(2)); + }); + await server.WaitRunTicks(1); + + await pair.CleanReturnAsync(); + } + + /// + /// Checks to make sure that removing nodes properly cleans up all connections. + /// + [Test] + public async Task XenoArtifactRemoveNodeTest() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entManager = server.ResolveDependency(); + var artifactSystem = entManager.System(); + + await server.WaitPost(() => + { + var artifactUid = entManager.Spawn("TestArtifact"); + var artifactEnt = (artifactUid, entManager.GetComponent(artifactUid)); + + // Create 3 nodes + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node4)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node5)); + + Assert.That(artifactSystem.GetAllNodeIndices(artifactEnt).Count(), Is.EqualTo(5)); + + // Add connection: 1 -> 2 -> 3 -> 4 -> 5 + artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value); + artifactSystem.AddEdge(artifactEnt, node3!.Value, node4!.Value); + artifactSystem.AddEdge(artifactEnt, node4!.Value, node5!.Value); + + // Make sure we have a continuous connection between the two ends of the graph. + Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node1.Value), Has.Count.EqualTo(4)); + Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node5.Value), Has.Count.EqualTo(4)); + + // Remove the node and make sure it's no longer in the artifact. + Assert.That(artifactSystem.RemoveNode(artifactEnt, node3!.Value)); + Assert.That(artifactSystem.TryGetIndex(artifactEnt, node3!.Value, out _), Is.False, "Node 3 still present in artifact."); + + // Check to make sure that we got rid of all the connections. + Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node2!.Value), Is.Empty); + Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node4!.Value), Is.Empty); + }); + await server.WaitRunTicks(1); + + await pair.CleanReturnAsync(); + } + + /// + /// Sets up series of linked nodes and ensures that resizing the adjacency matrix doesn't disturb the connections + /// + [Test] + public async Task XenoArtifactResizeTest() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entManager = server.ResolveDependency(); + var artifactSystem = entManager.System(); + + await server.WaitPost(() => + { + var artifactUid = entManager.Spawn("TestArtifact"); + var artifactEnt = (artifactUid, entManager.GetComponent(artifactUid)); + + // Create 3 nodes + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3)); + + // Add connection: 1 -> 2 -> 3 + artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value); + + // Make sure our connection is set up + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node1.Value, node2.Value)); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node2.Value, node3.Value)); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node2.Value, node1.Value), Is.False); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node3.Value, node2.Value), Is.False); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node1.Value, node3.Value), Is.False); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node3.Value, node1.Value), Is.False); + + Assert.That(artifactSystem.GetIndex(artifactEnt, node1!.Value), Is.EqualTo(0)); + Assert.That(artifactSystem.GetIndex(artifactEnt, node2!.Value), Is.EqualTo(1)); + Assert.That(artifactSystem.GetIndex(artifactEnt, node3!.Value), Is.EqualTo(2)); + + // Add a new node, resizing the original adjacency matrix and array. + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node4)); + + // Check that our connections haven't changed. + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node1.Value, node2.Value)); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node2.Value, node3.Value)); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node2.Value, node1.Value), Is.False); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node3.Value, node2.Value), Is.False); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node1.Value, node3.Value), Is.False); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node3.Value, node1.Value), Is.False); + + // Has our array shifted any when we resized? + Assert.That(artifactSystem.GetIndex(artifactEnt, node1!.Value), Is.EqualTo(0)); + Assert.That(artifactSystem.GetIndex(artifactEnt, node2!.Value), Is.EqualTo(1)); + Assert.That(artifactSystem.GetIndex(artifactEnt, node3!.Value), Is.EqualTo(2)); + + // Check that 4 didn't somehow end up with connections + Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node4!.Value), Is.Empty); + Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node4!.Value), Is.Empty); + }); + await server.WaitRunTicks(1); + + await pair.CleanReturnAsync(); + } + + /// + /// Checks if removing a node and adding a new node into its place in the adjacency matrix doesn't accidentally retain extra data. + /// + [Test] + public async Task XenoArtifactReplaceTest() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entManager = server.ResolveDependency(); + var artifactSystem = entManager.System(); + + await server.WaitPost(() => + { + var artifactUid = entManager.Spawn("TestArtifact"); + var artifactEnt = (artifactUid, entManager.GetComponent(artifactUid)); + + // Create 3 nodes + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3)); + + // Add connection: 1 -> 2 -> 3 + artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value); + + // Make sure our connection is set up + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node1.Value, node2.Value)); + Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node2.Value, node3.Value)); + + // Remove middle node, severing connections + artifactSystem.RemoveNode(artifactEnt, node2!.Value); + + // Make sure our connection are properly severed. + Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node1.Value), Is.Empty); + Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node3.Value), Is.Empty); + + // Make sure our matrix is 3x3 + Assert.That(artifactEnt.Item2.NodeAdjacencyMatrixRows, Is.EqualTo(3)); + Assert.That(artifactEnt.Item2.NodeAdjacencyMatrixColumns, Is.EqualTo(3)); + + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node4)); + + // Make sure that adding in a new node didn't add a new slot but instead re-used the middle slot. + Assert.That(artifactEnt.Item2.NodeAdjacencyMatrixRows, Is.EqualTo(3)); + Assert.That(artifactEnt.Item2.NodeAdjacencyMatrixColumns, Is.EqualTo(3)); + + // Ensure that all connections are still severed + Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node1.Value), Is.Empty); + Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node3.Value), Is.Empty); + Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node4!.Value), Is.Empty); + Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node4!.Value), Is.Empty); + + }); + await server.WaitRunTicks(1); + + await pair.CleanReturnAsync(); + } + + /// + /// Checks if the active nodes are properly detected. + /// + [Test] + public async Task XenoArtifactBuildActiveNodesTest() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entManager = server.ResolveDependency(); + var artifactSystem = entManager.System(); + + await server.WaitPost(() => + { + var artifactUid = entManager.Spawn("TestArtifact"); + var artifactEnt = (artifactUid, entManager.GetComponent(artifactUid)); + + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node4)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node5)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node6)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node7)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node8)); + + // /----( 6 ) + // /----[*3 ]-/----( 7 )----( 8 ) + // / + // / /----[*5 ] + // [ 1 ]--/----[ 2 ]--/----( 4 ) + // Diagram of the example generation. Nodes in [brackets] are unlocked, nodes in (braces) are locked + // and nodes with an *asterisk are supposed to be active. + artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value); + artifactSystem.AddEdge(artifactEnt, node1!.Value, node3!.Value); + + artifactSystem.AddEdge(artifactEnt, node2!.Value, node4!.Value); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node5!.Value); + + artifactSystem.AddEdge(artifactEnt, node3!.Value, node6!.Value); + artifactSystem.AddEdge(artifactEnt, node3!.Value, node7!.Value); + + artifactSystem.AddEdge(artifactEnt, node7!.Value, node8!.Value); + + artifactSystem.SetNodeUnlocked(node1!.Value); + artifactSystem.SetNodeUnlocked(node2!.Value); + artifactSystem.SetNodeUnlocked(node3!.Value); + artifactSystem.SetNodeUnlocked(node5!.Value); + + artifactSystem.RebuildCachedActiveNodes(artifactEnt); + + var activeNodes = new[] { node3!.Value.Owner, node5!.Value.Owner }; + Assert.That(artifactEnt.Item2.CachedActiveNodes, Is.SupersetOf(activeNodes)); + Assert.That(artifactEnt.Item2.CachedActiveNodes, Has.Count.EqualTo(activeNodes.Length)); + + }); + await server.WaitRunTicks(1); + + await pair.CleanReturnAsync(); + } +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs new file mode 100644 index 000000000000..6bfa8d8a66b5 --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs @@ -0,0 +1,10 @@ +namespace Content.Server.Xenoarchaeology.Artifact; + +public sealed partial class XenoArtifactSystem : EntitySystem +{ + /// + public override void Initialize() + { + base.Initialize(); + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs new file mode 100644 index 000000000000..8978367593be --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -0,0 +1,47 @@ +using Robust.Shared.Containers; +using Robust.Shared.GameStates; + +namespace Content.Shared.Xenoarchaeology.Artifact.Components; + +/// +/// This is used for handling interactions with artifacts as well as +/// storing data about artifact node graphs. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem)), AutoGenerateComponentState] +public sealed partial class XenoArtifactComponent : Component +{ + public static string NodeContainerId = "node-container"; + + [ViewVariables] + public Container NodeContainer = default!; + + /// + /// The nodes in this artifact that are currently "active." + /// This is cached and updated when nodes are removed, added, or unlocked. + /// + [DataField, AutoNetworkedField] + public List CachedActiveNodes = new(); + + //TODO: can't serialize entityuid arrays. Well fuck. + + // NOTE: you should not be accessing any of these values directly. Use the methods in SharedXenoArtifactSystem.Graph + #region Graph + /// + /// List of all of the nodes currently on this artifact. + /// Indexes are used as a lookup table for . + /// + [DataField] //AutoNetworkedField + public EntityUid?[] NodeVertices = []; + + /// + /// Adjacency matrix that stores connections between this artifact's nodes. + /// A value of "true" denotes an directed edge from node1 to node2, where the location of the vertex is (node1, node2) + /// A value of "false" denotes no edge. + /// + //[DataField, AutoNetworkedField] + public bool[,] NodeAdjacencyMatrix = { }; + + public int NodeAdjacencyMatrixRows => NodeAdjacencyMatrix.GetLength(0); + public int NodeAdjacencyMatrixColumns => NodeAdjacencyMatrix.GetLength(1); + #endregion +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs new file mode 100644 index 000000000000..3716ac421ba8 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs @@ -0,0 +1,38 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Xenoarchaeology.Artifact.Components; + +/// +/// Stores metadata about a particular artifact node +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem)), AutoGenerateComponentState] +public sealed partial class XenoArtifactNodeComponent : Component +{ + /// + /// Denotes whether or not an artifact node has been activated through the required triggers. + /// + [DataField, AutoNetworkedField] + public bool Locked = true; + + /// + /// The entity whose graph this node is a part of. + /// + [DataField] + public EntityUid? Attached; + + #region Durability + public bool Degraded => Durability <= 0; + + /// + /// The amount of generic activations a node has left before becoming fully degraded and useless. + /// + [DataField, AutoNetworkedField] + public int Durability; + + /// + /// The maximum amount of times a node can be generically activated before becoming useless + /// + [DataField] + public int MaxDurability; + #endregion +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs new file mode 100644 index 000000000000..0dfec2282a23 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs @@ -0,0 +1,442 @@ +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Xenoarchaeology.Artifact; + +/// +/// User-friendly API for viewing and modifying the complex graph relationship in XenoArtifacts +/// +public sealed partial class SharedXenoArtifactSystem +{ + public int GetIndex(Entity ent, EntityUid node) + { + for (var i = 0; i < ent.Comp.NodeVertices.Length; i++) + { + if (!TryGetNode((ent, ent), i, out var iNode)) + continue; + + if (node != iNode.Value.Owner) + continue; + + return i; + } + + throw new ArgumentException($"node {ToPrettyString(node)} is not present in {ToPrettyString(ent)}"); + } + + public bool TryGetIndex(Entity ent, EntityUid node, [NotNullWhen(true)] out int? index) + { + index = null; + if (!Resolve(ent, ref ent.Comp)) + return false; + + for (var i = 0; i < ent.Comp.NodeVertices.Length; i++) + { + if (!TryGetNode(ent, i, out var iNode)) + continue; + + if (node != iNode.Value.Owner) + continue; + + index = i; + return true; + } + + return false; + } + + public Entity GetNode(Entity ent, int index) + { + if (ent.Comp.NodeVertices[index] is { } uid) + return (uid, XenoArtifactNode(uid)); + + throw new ArgumentException($"index {index} does not correspond to an existing node in {ToPrettyString(ent)}"); + } + + public bool TryGetNode(Entity ent, int index, [NotNullWhen(true)] out Entity? node) + { + node = null; + if (!Resolve(ent, ref ent.Comp)) + return false; + + if (index < 0 || index >= ent.Comp.NodeVertices.Length) + return false; + + if (ent.Comp.NodeVertices[index] is { } uid) + node = (uid, XenoArtifactNode(uid)); + + return node != null; + } + + /// + /// Gets the index of the first empty spot in the NodeVertices array. + /// If there is none, resizes both arrays and returns the new index. + /// + public int GetFreeNodeIndex(Entity ent) + { + var length = ent.Comp.NodeVertices.Length; + for (var i = 0; i < length; i++) + { + if (TryGetNode((ent, ent), i, out _)) + continue; + + return i; + } + + ResizeNodeGraph(ent, length + 1); + return length; + } + + public IEnumerable> GetAllNodes(Entity ent) + { + foreach (var node in ent.Comp.NodeVertices) + { + if (node is not null) + yield return (node.Value, XenoArtifactNode(node.Value)); + } + } + + public IEnumerable GetAllNodeIndices(Entity ent) + { + for (var i = 0; i < ent.Comp.NodeVertices.Length; i++) + { + if (ent.Comp.NodeVertices[i] is not null) + yield return i; + } + } + + public bool AddEdge(Entity ent, EntityUid from, EntityUid to, bool dirty = true) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + + if (!TryGetIndex(ent, from, out var fromIdx) || + !TryGetIndex(ent, to, out var toIdx)) + return false; + + return AddEdge(ent, fromIdx.Value, toIdx.Value, dirty: dirty); + } + + public bool AddEdge(Entity ent, int fromIdx, int toIdx, bool dirty = true) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + + Debug.Assert(fromIdx >= 0 && fromIdx < ent.Comp.NodeVertices.Length, $"fromIdx \"{fromIdx}\" is out of bounds!"); + Debug.Assert(toIdx >= 0 && toIdx < ent.Comp.NodeVertices.Length, $"toIdx \"{toIdx}\" is out of bounds!"); + + if (ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx]) + return false; //Edge is already present + + // TODO: add a safety check to prohibit cyclic paths. + + ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx] = true; + if (dirty) + RebuildCachedActiveNodes(ent); + return true; + } + + public bool RemoveEdge(Entity ent, EntityUid from, EntityUid to, bool dirty = true) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + + if (!TryGetIndex(ent, from, out var fromIdx) || + !TryGetIndex(ent, to, out var toIdx)) + return false; + + return RemoveEdge(ent, fromIdx.Value, toIdx.Value, dirty); + } + + public bool RemoveEdge(Entity ent, int fromIdx, int toIdx, bool dirty = true) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + + Debug.Assert(fromIdx >= 0 && fromIdx < ent.Comp.NodeVertices.Length, $"fromIdx \"{fromIdx}\" is out of bounds!"); + Debug.Assert(toIdx >= 0 && toIdx < ent.Comp.NodeVertices.Length, $"toIdx \"{toIdx}\" is out of bounds!"); + + if (!ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx]) + return false; //Edge doesn't exist + + ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx] = false; + + if (dirty) + RebuildCachedActiveNodes(ent); + return true; + } + + public bool AddNode(Entity ent, + EntProtoId prototype, + [NotNullWhen(true)] out Entity? node, + bool dirty = true) + { + node = null; + if (!Resolve(ent, ref ent.Comp)) + return false; + + var uid = Spawn(prototype.Id); + node = (uid, XenoArtifactNode(uid)); + return AddNode(ent, (node.Value, node.Value.Comp), dirty: dirty); + } + + public bool AddNode(Entity ent, Entity node, bool dirty = true) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + node.Comp ??= XenoArtifactNode(node); + node.Comp.Attached = ent; + + var nodeIdx = GetFreeNodeIndex((ent, ent.Comp)); + _container.Insert(node.Owner, ent.Comp.NodeContainer); + ent.Comp.NodeVertices[nodeIdx] = node; + + Dirty(node); + if (dirty) + RebuildCachedActiveNodes(ent); + return true; + } + + public bool RemoveNode(Entity ent, Entity node, bool dirty = true) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + node.Comp ??= XenoArtifactNode(node); + + if (!TryGetIndex(ent, node, out var idx)) + return false; // node isn't attached to this entity. + + RemoveAllNodeEdges(ent, idx.Value, dirty: false); + + _container.Remove(node.Owner, ent.Comp.NodeContainer); + node.Comp.Attached = null; + ent.Comp.NodeVertices[idx.Value] = null; + if (dirty) + { + RebuildCachedActiveNodes(ent); + Dirty(ent); + } + Dirty(node); + return true; + } + + public void RemoveAllNodeEdges(Entity ent, int nodeIdx, bool dirty = true) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + var predecessors = GetDirectPredecessorNodes(ent, nodeIdx); + foreach (var p in predecessors) + { + RemoveEdge(ent, p, nodeIdx, dirty: false); + } + + var successors = GetDirectSuccessorNodes(ent, nodeIdx); + foreach (var s in successors) + { + RemoveEdge(ent, nodeIdx, s, dirty: false); + } + + if (dirty) + { + RebuildCachedActiveNodes(ent); + Dirty(ent); + } + } + + public HashSet> GetDirectPredecessorNodes(Entity ent, EntityUid node) + { + if (!Resolve(ent, ref ent.Comp)) + return new(); + + if (!TryGetIndex(ent, node, out var index)) + return new(); + + var indices = GetDirectPredecessorNodes(ent, index.Value); + var output = new HashSet>(); + foreach (var i in indices) + { + if (TryGetNode(ent, i, out var predecessor)) + output.Add(predecessor.Value); + } + + return output; + } + + public HashSet GetDirectPredecessorNodes(Entity ent, int nodeIdx) + { + if (!Resolve(ent, ref ent.Comp)) + return new(); + Debug.Assert(nodeIdx >= 0 && nodeIdx < ent.Comp.NodeVertices.Length, $"node index \"{nodeIdx}\" is out of bounds!"); + + var indices = new HashSet(); + for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixRows; i++) + { + if (ent.Comp.NodeAdjacencyMatrix[i, nodeIdx]) + indices.Add(i); + } + + return indices; + } + + public HashSet> GetDirectSuccessorNodes(Entity ent, EntityUid node) + { + if (!Resolve(ent, ref ent.Comp)) + return new(); + + if (!TryGetIndex(ent, node, out var index)) + return new(); + + var indices = GetDirectSuccessorNodes(ent, index.Value); + var output = new HashSet>(); + foreach (var i in indices) + { + if (TryGetNode(ent, i, out var successor)) + output.Add(successor.Value); + } + + return output; + } + + public HashSet GetDirectSuccessorNodes(Entity ent, int nodeIdx) + { + if (!Resolve(ent, ref ent.Comp)) + return new(); + Debug.Assert(nodeIdx >= 0 && nodeIdx < ent.Comp.NodeVertices.Length, $"node index \"{nodeIdx}\" is out of bounds!"); + + var indices = new HashSet(); + for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixColumns; i++) + { + if (ent.Comp.NodeAdjacencyMatrix[nodeIdx, i]) + indices.Add(i); + } + + return indices; + } + + public HashSet> GetPredecessorNodes(Entity ent, Entity node) + { + if (!Resolve(ent, ref ent.Comp)) + return new(); + + var predecessors = GetPredecessorNodes(ent, GetIndex((ent, ent.Comp), node)); + var output = new HashSet>(); + foreach (var p in predecessors) + { + output.Add(GetNode((ent, ent.Comp), p)); + } + + return output; + } + + public HashSet GetPredecessorNodes(Entity ent, int nodeIdx) + { + if (!Resolve(ent, ref ent.Comp)) + return new(); + + var predecessors = GetDirectPredecessorNodes(ent, nodeIdx); + if (predecessors.Count == 0) + return new(); + + var output = new HashSet(); + foreach (var p in predecessors) + { + output.Add(p); + var recursivePredecessors = GetPredecessorNodes(ent, p); + foreach (var rp in recursivePredecessors) + { + output.Add(rp); + } + } + + return output; + } + + public HashSet> GetSuccessorNodes(Entity ent, Entity node) + { + if (!Resolve(ent, ref ent.Comp)) + return new(); + + var successors = GetSuccessorNodes(ent, GetIndex((ent, ent.Comp), node)); + var output = new HashSet>(); + foreach (var s in successors) + { + output.Add(GetNode((ent, ent.Comp), s)); + } + + return output; + } + + public HashSet GetSuccessorNodes(Entity ent, int nodeIdx) + { + if (!Resolve(ent, ref ent.Comp)) + return new(); + + var successors = GetDirectSuccessorNodes(ent, nodeIdx); + if (successors.Count == 0) + return new(); + + var output = new HashSet(); + foreach (var s in successors) + { + output.Add(s); + var recursiveSuccessors = GetSuccessorNodes(ent, s); + foreach (var rs in recursiveSuccessors) + { + output.Add(rs); + } + } + + return output; + } + + public bool NodeHasEdge(Entity ent, + Entity from, + Entity to) + { + if (!Resolve(ent, ref ent.Comp)) + return new(); + + var fromIdx = GetIndex((ent, ent.Comp), from); + var toIdx = GetIndex((ent, ent.Comp), to); + + return ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx]; + } + + /// + /// Resizes the adjacency matrix and vertices array to newLength + /// + protected void ResizeNodeGraph(Entity ent, int newSize) + { + Array.Resize(ref ent.Comp.NodeVertices, newSize); + ent.Comp.NodeAdjacencyMatrix = XenoArtifactGraphHelpers.ResizeArray(ent.Comp.NodeAdjacencyMatrix, newSize, newSize); + Dirty(ent); + } +} + +public static class XenoArtifactGraphHelpers +{ + /// + /// Resizes a 2d array to the specified dimensions, retaining the positions of existing values in the array. + /// Taken from https://stackoverflow.com/a/9059866. + /// + public static T[,] ResizeArray(T[,] original, int x, int y) + { + var newArray = new T[x, y]; + var minX = Math.Min(original.GetLength(0), newArray.GetLength(0)); + var minY = Math.Min(original.GetLength(1), newArray.GetLength(1)); + + for (var i = 0; i < minY; ++i) + { + Array.Copy(original, + i * original.GetLength(0), + newArray, + i * newArray.GetLength(0), + minX); + } + + return newArray; + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs new file mode 100644 index 000000000000..70ee914d5d4a --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -0,0 +1,120 @@ +using Content.Shared.Xenoarchaeology.Artifact.Components; + +namespace Content.Shared.Xenoarchaeology.Artifact; + +public sealed partial class SharedXenoArtifactSystem +{ + private EntityQuery _nodeQuery; + + public void InitializeNode() + { + SubscribeLocalEvent(OnNodeMapInit); + + _nodeQuery = GetEntityQuery(); + } + + private void OnNodeMapInit(Entity ent, ref MapInitEvent args) + { + ReplenishNodeDurability((ent, ent)); + } + + public XenoArtifactNodeComponent XenoArtifactNode(EntityUid uid) + { + return _nodeQuery.Get(uid); + } + + public void SetNodeUnlocked(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + if (!ent.Comp.Locked) + return; + + ent.Comp.Locked = false; + // TODO: update the cached active node data. + Dirty(ent); + } + + /// + /// Resets a node's durability back to max. + /// + public void ReplenishNodeDurability(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return; + SetNodeDurability(ent, ent.Comp.MaxDurability); + } + + /// + /// Adds to the nodes durability by the specified value. + /// + /// + /// + public void AdjustNodeDurability(Entity ent, int delta) + { + if (!Resolve(ent, ref ent.Comp)) + return; + SetNodeDurability(ent, ent.Comp.Durability + delta); + } + + /// + /// Sets a node's durability to the specified value. + /// + public void SetNodeDurability(Entity ent, int durability) + { + if (!Resolve(ent, ref ent.Comp)) + return; + ent.Comp.Durability = Math.Clamp(durability, 0, ent.Comp.MaxDurability); + Dirty(ent); + } + + /// + /// Clears all cached active nodes and rebuilds the list using the current node state. + /// Active nodes have the following property: + /// - Are unlocked + /// - Have no successors which are also locked + /// + /// + /// You could technically modify this to have a per-node method that only checks direct predecessors + /// and then does recursive updates for all successors, but I don't think the optimization is necessary right now. + /// + public void RebuildCachedActiveNodes(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + ent.Comp.CachedActiveNodes.Clear(); + var allNodes = GetAllNodes((ent, ent.Comp)); + foreach (var node in allNodes) + { + // Locked nodes cannot be active. + if (node.Comp.Locked) + continue; + + var successors = GetDirectSuccessorNodes(ent, node); + + // If this node has no successors, then we don't need to bother with this extra logic. + if (successors.Count != 0) + { + // Checks for any of the direct successors being unlocked. + var successorIsUnlocked = false; + foreach (var sNode in successors) + { + if (sNode.Comp.Locked) + continue; + successorIsUnlocked = true; + break; + } + + // Active nodes must be at the end of the path. + if (successorIsUnlocked) + continue; + } + + ent.Comp.CachedActiveNodes.Add(node); + } + + Dirty(ent); + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs new file mode 100644 index 000000000000..d42f03dde73b --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs @@ -0,0 +1,22 @@ +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Shared.Containers; + +namespace Content.Shared.Xenoarchaeology.Artifact; + +public sealed partial class SharedXenoArtifactSystem : EntitySystem +{ + [Dependency] private readonly SharedContainerSystem _container = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnStartup); + + InitializeNode(); + } + + private void OnStartup(Entity ent, ref ComponentStartup args) + { + ent.Comp.NodeContainer = _container.EnsureContainer(ent, XenoArtifactComponent.NodeContainerId); + } +} From c7e209a8c168029adc98ccf4ac46ab667a5229f9 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Mon, 17 Jun 2024 19:22:05 -0400 Subject: [PATCH 02/81] Fix sandbox type errors --- .../Artifact/Components/XenoArtifactComponent.cs | 10 ++++++---- .../Artifact/SharedXenoArtifactSystem.Graph.cs | 14 +++++++------- .../Artifact/SharedXenoArtifactSystem.Node.cs | 3 ++- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index 8978367593be..f176b412207d 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Xenoarchaeology.Artifact.Components; /// This is used for handling interactions with artifacts as well as /// storing data about artifact node graphs. /// -[RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem)), AutoGenerateComponentState] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem))] public sealed partial class XenoArtifactComponent : Component { public static string NodeContainerId = "node-container"; @@ -19,7 +19,7 @@ public sealed partial class XenoArtifactComponent : Component /// The nodes in this artifact that are currently "active." /// This is cached and updated when nodes are removed, added, or unlocked. /// - [DataField, AutoNetworkedField] + [DataField] public List CachedActiveNodes = new(); //TODO: can't serialize entityuid arrays. Well fuck. @@ -30,7 +30,7 @@ public sealed partial class XenoArtifactComponent : Component /// List of all of the nodes currently on this artifact. /// Indexes are used as a lookup table for . /// - [DataField] //AutoNetworkedField + [DataField] public EntityUid?[] NodeVertices = []; /// @@ -38,10 +38,12 @@ public sealed partial class XenoArtifactComponent : Component /// A value of "true" denotes an directed edge from node1 to node2, where the location of the vertex is (node1, node2) /// A value of "false" denotes no edge. /// - //[DataField, AutoNetworkedField] + [DataField] public bool[,] NodeAdjacencyMatrix = { }; public int NodeAdjacencyMatrixRows => NodeAdjacencyMatrix.GetLength(0); public int NodeAdjacencyMatrixColumns => NodeAdjacencyMatrix.GetLength(1); #endregion } + +// TODO: manually implement component state. yeesh. diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs index 0dfec2282a23..2ddedda35132 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs @@ -1,7 +1,7 @@ -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Content.Shared.Xenoarchaeology.Artifact.Components; using Robust.Shared.Prototypes; +using Robust.Shared.Utility; namespace Content.Shared.Xenoarchaeology.Artifact; @@ -124,8 +124,8 @@ public bool AddEdge(Entity ent, int fromIdx, int toIdx, if (!Resolve(ent, ref ent.Comp)) return false; - Debug.Assert(fromIdx >= 0 && fromIdx < ent.Comp.NodeVertices.Length, $"fromIdx \"{fromIdx}\" is out of bounds!"); - Debug.Assert(toIdx >= 0 && toIdx < ent.Comp.NodeVertices.Length, $"toIdx \"{toIdx}\" is out of bounds!"); + DebugTools.Assert(fromIdx >= 0 && fromIdx < ent.Comp.NodeVertices.Length, $"fromIdx is out of bounds!"); + DebugTools.Assert(toIdx >= 0 && toIdx < ent.Comp.NodeVertices.Length, $"toIdx is out of bounds!"); if (ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx]) return false; //Edge is already present @@ -155,8 +155,8 @@ public bool RemoveEdge(Entity ent, int fromIdx, int toId if (!Resolve(ent, ref ent.Comp)) return false; - Debug.Assert(fromIdx >= 0 && fromIdx < ent.Comp.NodeVertices.Length, $"fromIdx \"{fromIdx}\" is out of bounds!"); - Debug.Assert(toIdx >= 0 && toIdx < ent.Comp.NodeVertices.Length, $"toIdx \"{toIdx}\" is out of bounds!"); + DebugTools.Assert(fromIdx >= 0 && fromIdx < ent.Comp.NodeVertices.Length, $"fromIdx is out of bounds!"); + DebugTools.Assert(toIdx >= 0 && toIdx < ent.Comp.NodeVertices.Length, $"toIdx is out of bounds!"); if (!ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx]) return false; //Edge doesn't exist @@ -269,7 +269,7 @@ public HashSet GetDirectPredecessorNodes(Entity ent { if (!Resolve(ent, ref ent.Comp)) return new(); - Debug.Assert(nodeIdx >= 0 && nodeIdx < ent.Comp.NodeVertices.Length, $"node index \"{nodeIdx}\" is out of bounds!"); + DebugTools.Assert(nodeIdx >= 0 && nodeIdx < ent.Comp.NodeVertices.Length, $"node index is out of bounds!"); var indices = new HashSet(); for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixRows; i++) @@ -304,7 +304,7 @@ public HashSet GetDirectSuccessorNodes(Entity ent, { if (!Resolve(ent, ref ent.Comp)) return new(); - Debug.Assert(nodeIdx >= 0 && nodeIdx < ent.Comp.NodeVertices.Length, $"node index \"{nodeIdx}\" is out of bounds!"); + DebugTools.Assert(nodeIdx >= 0 && nodeIdx < ent.Comp.NodeVertices.Length, "node index is out of bounds!"); var indices = new HashSet(); for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixColumns; i++) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 70ee914d5d4a..2ccf9e186383 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -32,7 +32,8 @@ public void SetNodeUnlocked(Entity ent) return; ent.Comp.Locked = false; - // TODO: update the cached active node data. + if (ent.Comp.Attached is { } artifact) + RebuildCachedActiveNodes(artifact); Dirty(ent); } From d2e8d72d5cf3fec1c4af511e509fb2b8b54b0b34 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Tue, 18 Jun 2024 01:25:25 -0400 Subject: [PATCH 03/81] First pass on procgen --- .../Artifact/XenoArtifactCommands.cs | 62 +++++++++++ .../Artifact/XenoArtifactSystem.ProcGen.cs | 105 ++++++++++++++++++ .../Artifact/XenoArtifactSystem.cs | 12 +- .../Components/XenoArtifactComponent.cs | 27 +++++ .../SharedXenoArtifactSystem.Graph.cs | 5 +- .../Artifact/SharedXenoArtifactSystem.Node.cs | 2 +- .../Artifact/SharedXenoArtifactSystem.cs | 6 +- .../Prototypes/XenoArch/effect_weights.yml | 4 + Resources/Prototypes/XenoArch/effects.yml | 6 + 9 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 Content.Server/Xenoarchaeology/Artifact/XenoArtifactCommands.cs create mode 100644 Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs create mode 100644 Resources/Prototypes/XenoArch/effect_weights.yml create mode 100644 Resources/Prototypes/XenoArch/effects.yml diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactCommands.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactCommands.cs new file mode 100644 index 000000000000..a422cc2b26b8 --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactCommands.cs @@ -0,0 +1,62 @@ +using System.Text; +using Content.Server.Administration; +using Content.Shared.Administration; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Shared.Toolshed; + +namespace Content.Server.Xenoarchaeology.Artifact; + +[ToolshedCommand, AdminCommand(AdminFlags.Debug)] +public sealed class XenoArtifactCommand : ToolshedCommand +{ + [CommandImplementation("list")] + public IEnumerable List() + { + var query = EntityManager.EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _)) + { + yield return uid; + } + } + + [CommandImplementation("printMatrix")] + public string PrintMatrix([PipedArgument] EntityUid ent) + { + var comp = EntityManager.GetComponent(ent); + + 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($"---+"); + } + } + } +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs new file mode 100644 index 000000000000..04358cd7e22d --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -0,0 +1,105 @@ +using Content.Shared.Random.Helpers; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Shared.Random; +using Robust.Shared.Utility; + +namespace Content.Server.Xenoarchaeology.Artifact; + +public sealed partial class XenoArtifactSystem +{ + private void GenerateArtifactStructure(Entity ent) + { + var nodeCount = ent.Comp.NodeCount.Next(RobustRandom); + ResizeNodeGraph(ent, nodeCount); + while (nodeCount > 0) + { + GenerateArtifactSegment(ent, ref nodeCount); + } + + RebuildCachedActiveNodes((ent, ent)); + } + + private void GenerateArtifactSegment(Entity ent, ref int nodeCount) + { + var segmentSize = GetArtifactSegmentSize(ent, nodeCount); + nodeCount -= segmentSize; + PopulateArtifactSegmentRecursive(ent, ref segmentSize); + + // TODO: store the segments in a list somewhere so we don't have to rebuild them constantly. + // Or maybe just rebuild them manually like we do active nodes??? hard to say. + } + + private List> PopulateArtifactSegmentRecursive(Entity ent, ref int segmentSize, int layerMaxMod = 0) + { + if (segmentSize == 0) + return new(); + + var layerMin = ent.Comp.NodesPerSegmentLayer.Min; + var layerMax = Math.Min(ent.Comp.NodesPerSegmentLayer.Max + layerMaxMod, segmentSize); + + // Default to one node if we had shenanigans and ended up with weird layer counts. + var nodeCount = 1; + if (layerMax >= layerMin) + nodeCount = RobustRandom.Next(layerMin, layerMax + 1); // account for non-inclusive max + + segmentSize -= nodeCount; + var nodes = new List>(); + for (var i = 0; i < nodeCount; i++) + { + nodes.Add(CreateRandomNode(ent)); + } + + var layerMod = nodes.Count / 2; // cumulative modifier to enable slight growth for something like 3 -> 4 + var successors = PopulateArtifactSegmentRecursive(ent, ref segmentSize, layerMod); + if (successors.Count == 0) + return nodes; + + // We do the picks from node -> successor and from successor -> node to ensure that no nodes get orphaned without connections. + foreach (var node in nodes) + { + var successor = RobustRandom.Pick(successors); + AddEdge((ent, ent), node, successor); + } + foreach (var successor in successors) + { + var node = RobustRandom.Pick(nodes); + AddEdge((ent, ent), node, successor); + } + + // TODO: if gen is bad, consider implementing random scattering + + return nodes; + } + + private int GetArtifactSegmentSize(Entity ent, int nodeCount) + { + // Make sure we can't generate a single segment artifact. + // We always want to have at least 2 segments. For variety. + var segmentMin = ent.Comp.SegmentSize.Min; + var segmentMax = Math.Min(ent.Comp.SegmentSize.Max, Math.Max(nodeCount / 2, segmentMin)); + + var segmentSize = RobustRandom.Next(segmentMin, segmentMax + 1); // account for non-inclusive max + var remainder = nodeCount - segmentSize; + + // If our next segment is going to be undersized, then we just absorb it into this segment. + if (remainder < ent.Comp.SegmentSize.Min) + segmentSize += remainder; + + // Sanity check to make sure we don't exceed the node count. (it shouldn't happen prior anyway but oh well) + segmentSize = Math.Min(nodeCount, segmentSize); + + return segmentSize; + } + + private Entity CreateRandomNode(Entity ent) + { + var proto = PrototypeManager.Index(ent.Comp.EffectWeights).Pick(RobustRandom); + + AddNode((ent, ent), proto, out var nodeEnt, dirty: false); + DebugTools.Assert(nodeEnt.HasValue, "Failed to create node on artifact."); + + // TODO: setup trigger or effect or smth. idk quite how we're gonna do this. + + return nodeEnt.Value; + } +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs index 6bfa8d8a66b5..56a7d3178b5b 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs @@ -1,10 +1,20 @@ +using Content.Shared.Xenoarchaeology.Artifact; +using Content.Shared.Xenoarchaeology.Artifact.Components; + namespace Content.Server.Xenoarchaeology.Artifact; -public sealed partial class XenoArtifactSystem : EntitySystem +public sealed partial class XenoArtifactSystem : SharedXenoArtifactSystem { /// public override void Initialize() { base.Initialize(); + + SubscribeLocalEvent(OnArtifactMapInit); + } + + private void OnArtifactMapInit(Entity ent, ref MapInitEvent args) + { + GenerateArtifactStructure(ent); } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index f176b412207d..0bc416b8f3a8 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -1,5 +1,8 @@ +using Content.Shared.Destructible.Thresholds; +using Content.Shared.Random; using Robust.Shared.Containers; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; namespace Content.Shared.Xenoarchaeology.Artifact.Components; @@ -44,6 +47,30 @@ public sealed partial class XenoArtifactComponent : Component public int NodeAdjacencyMatrixRows => NodeAdjacencyMatrix.GetLength(0); public int NodeAdjacencyMatrixColumns => NodeAdjacencyMatrix.GetLength(1); #endregion + + #region GenerationInfo + /// + /// The total number of nodes that make up this artifact. + /// + [DataField] + public MinMax NodeCount = new(10, 20); + + /// + /// The amount of nodes that go in each segment. + /// A segment is an interconnected series of nodes. + /// + [DataField] + public MinMax SegmentSize = new(5, 8); + + /// + /// For each "layer" in a segment (set of nodes with equal depth), how many will we generate? + /// + [DataField] + public MinMax NodesPerSegmentLayer = new(1, 3); + + [DataField] + public ProtoId EffectWeights = "XenoArtifactEffectsDefault"; + #endregion } // TODO: manually implement component state. yeesh. diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs index 2ddedda35132..5972b383feac 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs @@ -8,8 +8,11 @@ namespace Content.Shared.Xenoarchaeology.Artifact; /// /// User-friendly API for viewing and modifying the complex graph relationship in XenoArtifacts /// -public sealed partial class SharedXenoArtifactSystem +public abstract partial class SharedXenoArtifactSystem { + /// + /// Gets the index corresponding to a given node, throwing if the node is not present. + /// public int GetIndex(Entity ent, EntityUid node) { for (var i = 0; i < ent.Comp.NodeVertices.Length; i++) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 2ccf9e186383..eca37e998048 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -2,7 +2,7 @@ namespace Content.Shared.Xenoarchaeology.Artifact; -public sealed partial class SharedXenoArtifactSystem +public abstract partial class SharedXenoArtifactSystem { private EntityQuery _nodeQuery; diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs index d42f03dde73b..184ef7fcbe5c 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs @@ -1,10 +1,14 @@ using Content.Shared.Xenoarchaeology.Artifact.Components; using Robust.Shared.Containers; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; namespace Content.Shared.Xenoarchaeology.Artifact; -public sealed partial class SharedXenoArtifactSystem : EntitySystem +public abstract partial class SharedXenoArtifactSystem : EntitySystem { + [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; + [Dependency] protected readonly IRobustRandom RobustRandom = default!; [Dependency] private readonly SharedContainerSystem _container = default!; /// diff --git a/Resources/Prototypes/XenoArch/effect_weights.yml b/Resources/Prototypes/XenoArch/effect_weights.yml new file mode 100644 index 000000000000..0b615286f4e7 --- /dev/null +++ b/Resources/Prototypes/XenoArch/effect_weights.yml @@ -0,0 +1,4 @@ +- type: weightedRandomEntity + id: XenoArtifactEffectsDefault + weights: + XenoArtifactEffectTest: 1.0 diff --git a/Resources/Prototypes/XenoArch/effects.yml b/Resources/Prototypes/XenoArch/effects.yml new file mode 100644 index 000000000000..149bf4ac3f09 --- /dev/null +++ b/Resources/Prototypes/XenoArch/effects.yml @@ -0,0 +1,6 @@ +- type: entity + id: XenoArtifactEffectTest + name: test effect + noSpawn: true + components: + - type: XenoArtifactNode From 5aa453b8f53de1483fe0e5d84ca38037f88bc367 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Tue, 18 Jun 2024 22:26:00 -0400 Subject: [PATCH 04/81] Procgen adjustments --- .../Artifact/XenoArtifactSystem.ProcGen.cs | 44 ++++++++++++++----- .../Components/XenoArtifactComponent.cs | 5 ++- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 04358cd7e22d..98432f342720 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -23,18 +23,23 @@ private void GenerateArtifactSegment(Entity ent, ref int { var segmentSize = GetArtifactSegmentSize(ent, nodeCount); nodeCount -= segmentSize; - PopulateArtifactSegmentRecursive(ent, ref segmentSize); + PopulateArtifactSegmentRecursive(ent, ref segmentSize, ensureLayerConnected: true); // TODO: store the segments in a list somewhere so we don't have to rebuild them constantly. // Or maybe just rebuild them manually like we do active nodes??? hard to say. } - private List> PopulateArtifactSegmentRecursive(Entity ent, ref int segmentSize, int layerMaxMod = 0) + private List> PopulateArtifactSegmentRecursive( + Entity ent, + ref int segmentSize, + int layerMinMod = 0, + int layerMaxMod = 0, + bool ensureLayerConnected = false) { if (segmentSize == 0) return new(); - var layerMin = ent.Comp.NodesPerSegmentLayer.Min; + var layerMin = Math.Min(ent.Comp.NodesPerSegmentLayer.Min + layerMinMod, segmentSize); var layerMax = Math.Min(ent.Comp.NodesPerSegmentLayer.Max + layerMaxMod, segmentSize); // Default to one node if we had shenanigans and ended up with weird layer counts. @@ -49,24 +54,39 @@ private List> PopulateArtifactSegmentRecursive nodes.Add(CreateRandomNode(ent)); } - var layerMod = nodes.Count / 2; // cumulative modifier to enable slight growth for something like 3 -> 4 - var successors = PopulateArtifactSegmentRecursive(ent, ref segmentSize, layerMod); + var minMod = ent.Comp.NodeContainer.Count < 3 ? 0 : 1; // Try to stop boring linear generation. + var maxMod = nodes.Count / 2; // cumulative modifier to enable slight growth for something like 3 -> 4 + var successors = PopulateArtifactSegmentRecursive( + ent, + ref segmentSize, + layerMinMod: minMod, + layerMaxMod: maxMod); if (successors.Count == 0) return nodes; // We do the picks from node -> successor and from successor -> node to ensure that no nodes get orphaned without connections. - foreach (var node in nodes) - { - var successor = RobustRandom.Pick(successors); - AddEdge((ent, ent), node, successor); - } foreach (var successor in successors) { var node = RobustRandom.Pick(nodes); - AddEdge((ent, ent), node, successor); + AddEdge((ent, ent), node, successor, dirty: false); + } + + if (ensureLayerConnected) + { + foreach (var node in nodes) + { + var successor = RobustRandom.Pick(successors); + AddEdge((ent, ent), node, successor, dirty: false); + } } - // TODO: if gen is bad, consider implementing random scattering + var reverseScatterCount = ent.Comp.ReverseScatterPerLayer.Next(RobustRandom); + for (var i = 0; i < reverseScatterCount; i++) + { + var node = RobustRandom.Pick(nodes); + var successor = RobustRandom.Pick(successors); + AddEdge((ent, ent), node, successor, dirty: false); + } return nodes; } diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index 0bc416b8f3a8..48b181f1fc65 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -53,7 +53,7 @@ public sealed partial class XenoArtifactComponent : Component /// The total number of nodes that make up this artifact. /// [DataField] - public MinMax NodeCount = new(10, 20); + public MinMax NodeCount = new(10, 24); /// /// The amount of nodes that go in each segment. @@ -68,6 +68,9 @@ public sealed partial class XenoArtifactComponent : Component [DataField] public MinMax NodesPerSegmentLayer = new(1, 3); + [DataField] + public MinMax ReverseScatterPerLayer = new(0, 2); + [DataField] public ProtoId EffectWeights = "XenoArtifactEffectsDefault"; #endregion From 2befbd0cda4ca16b7584dafbbc9305261bddae45 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Wed, 19 Jun 2024 14:17:07 -0400 Subject: [PATCH 05/81] Networking --- .../Artifact/XenoArtifactSystem.cs | 39 +++++++++++++++++ .../Artifact/XenoArtifactSystem.cs | 27 +++++++++++- .../Components/XenoArtifactComponent.cs | 18 ++++++-- .../Xenoarchaeology/item_artifacts.yml | 1 + .../Xenoarchaeology/structure_artifacts.yml | 1 + .../Xenoarchaeology/xenoartifacts.yml | 43 +++++++++++++++++++ 6 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml diff --git a/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs b/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs new file mode 100644 index 000000000000..31c2f5e78951 --- /dev/null +++ b/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs @@ -0,0 +1,39 @@ +using Content.Shared.Xenoarchaeology.Artifact; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Shared.GameStates; + +namespace Content.Client.Xenoarchaeology.Artifact; + +/// +public sealed class XenoArtifactSystem : SharedXenoArtifactSystem +{ + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHandleState); + } + + private void OnHandleState(Entity ent, ref ComponentHandleState args) + { + if (args.Current is not XenoArtifactComponentState state) + return; + + ResizeNodeGraph(ent, state.NodeVertices.Count); + + // Copy over node vertices + for (var i = 0; i < state.NodeVertices.Count; i++) + { + ent.Comp.NodeVertices[i] = GetEntity(state.NodeVertices[i]); + } + + for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixRows; i++) + { + for (var j = 0; j < ent.Comp.NodeAdjacencyMatrixColumns; j++) + { + ent.Comp.NodeAdjacencyMatrix[i, j] = state.NodeAdjacencyMatrix[i][j]; + } + } + } +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs index 56a7d3178b5b..de536469113f 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Xenoarchaeology.Artifact; using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Shared.GameStates; namespace Content.Server.Xenoarchaeology.Artifact; @@ -9,10 +10,34 @@ public sealed partial class XenoArtifactSystem : SharedXenoArtifactSystem public override void Initialize() { base.Initialize(); - + + SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnArtifactMapInit); } + private void OnGetState(Entity ent, ref ComponentGetState args) + { + var nodeVertices = new List(); + foreach (var vertex in ent.Comp.NodeVertices) + { + nodeVertices.Add(GetNetEntity(vertex)); + } + + var nodeAdjacencyMatrix = new List>(); + for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixRows; i++) + { + var row = new List(); + for (var j = 0; j < ent.Comp.NodeAdjacencyMatrixColumns; j++) + { + row.Add(ent.Comp.NodeAdjacencyMatrix[i, j]); + } + + nodeAdjacencyMatrix.Add(row); + } + + args.State = new XenoArtifactComponentState(nodeVertices, nodeAdjacencyMatrix); + } + private void OnArtifactMapInit(Entity ent, ref MapInitEvent args) { GenerateArtifactStructure(ent); diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index 48b181f1fc65..ae13f3744a41 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -3,6 +3,7 @@ using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; namespace Content.Shared.Xenoarchaeology.Artifact.Components; @@ -18,6 +19,7 @@ public sealed partial class XenoArtifactComponent : Component [ViewVariables] public Container NodeContainer = default!; + // todo: instead of networking this what if we just reconstructed on the client... hm... /// /// The nodes in this artifact that are currently "active." /// This is cached and updated when nodes are removed, added, or unlocked. @@ -25,8 +27,6 @@ public sealed partial class XenoArtifactComponent : Component [DataField] public List CachedActiveNodes = new(); - //TODO: can't serialize entityuid arrays. Well fuck. - // NOTE: you should not be accessing any of these values directly. Use the methods in SharedXenoArtifactSystem.Graph #region Graph /// @@ -76,4 +76,16 @@ public sealed partial class XenoArtifactComponent : Component #endregion } -// TODO: manually implement component state. yeesh. +[Serializable, NetSerializable] +public sealed class XenoArtifactComponentState : ComponentState +{ + public List NodeVertices; + + public List> NodeAdjacencyMatrix; + + public XenoArtifactComponentState(List nodeVertices, List> nodeAdjacencyMatrix) + { + NodeVertices = nodeVertices; + NodeAdjacencyMatrix = nodeAdjacencyMatrix; + } +} diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_artifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_artifacts.yml index 2241cdd4aa09..b8d21ecc7834 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_artifacts.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_artifacts.yml @@ -4,6 +4,7 @@ name: alien artifact description: A strange handheld alien device. abstract: true + noSpawn: true components: - type: Sprite sprite: Objects/Specific/Xenoarchaeology/item_artifacts.rsi diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/structure_artifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/structure_artifacts.yml index 4c3dac181608..c0641f2ec4b4 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/structure_artifacts.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/structure_artifacts.yml @@ -4,6 +4,7 @@ name: alien artifact description: A strange alien device. abstract: true + noSpawn: true components: - type: Sprite drawdepth: SmallObjects diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml new file mode 100644 index 000000000000..b63f8891aa16 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml @@ -0,0 +1,43 @@ +- type: entity + parent: BaseStructureDynamic + id: XenoArtifact + name: artifact + description: A strange artifact from time unknown. Looks like a good time. + components: + - type: Sprite + drawdepth: SmallObjects + sprite: Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi + noRot: true + layers: + - state: ano30 + map: [ "enum.ArtifactsVisualLayers.Base" ] + - state: ano30_on + map: [ "enum.ArtifactsVisualLayers.Effect" ] + visible: false + - type: Appearance + - type: Actions + - type: Damageable + - type: Physics + bodyType: Dynamic + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.45 + density: 75 + layer: # doesn't collide with artifact storage + - Opaque + mask: + - MachineMask + - type: InteractionOutline + - type: RandomSprite + available: + - enum.ArtifactsVisualLayers.Effect: + ano01_on: Rainbow + - type: XenoArtifact + - type: StealTarget + stealGroup: XenoArtifact + - type: GuideHelp + guides: + - Xenoarchaeology From 33c57757a7833f0a2167bbc00f47628f8eb61ee9 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Wed, 19 Jun 2024 17:51:54 -0400 Subject: [PATCH 06/81] Cruft and god and beauty and analysis console --- .../Equipment/ArtifactAnalyzerSystem.cs | 38 +++++ .../Ui/AnalysisConsoleBoundUserInterface.cs | 21 +-- .../Ui/AnalysisConsoleMenu.xaml.cs | 156 ++---------------- .../Artifact/XenoArtifactSystem.ProcGen.cs | 13 +- .../{Systems => }/ArtifactAnalyzerSystem.cs | 130 +-------------- .../Components/AnalysisConsoleComponent.cs | 38 ----- .../Components/ArtifactAnalyzerComponent.cs | 45 ----- .../Components/XenoArtifactNodeComponent.cs | 7 + .../Components/AnalysisConsoleComponent.cs | 39 +++++ .../Components/ArtifactAnalyzerComponent.cs | 38 +++++ .../Equipment/SharedArtifactAnalyzer.cs | 6 - .../Equipment/SharedArtifactAnalyzerSystem.cs | 67 ++++++++ .../Structures/Machines/artifact_analyzer.yml | 2 +- Resources/Prototypes/XenoArch/effects.yml | 2 + .../Prototypes/name_identifier_groups.yml | 5 + 15 files changed, 228 insertions(+), 379 deletions(-) create mode 100644 Content.Client/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs rename Content.Server/Xenoarchaeology/Equipment/{Systems => }/ArtifactAnalyzerSystem.cs (75%) delete mode 100644 Content.Server/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs delete mode 100644 Content.Server/Xenoarchaeology/Equipment/Components/ArtifactAnalyzerComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Equipment/Components/ArtifactAnalyzerComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs diff --git a/Content.Client/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs b/Content.Client/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs new file mode 100644 index 000000000000..398a9902b6f9 --- /dev/null +++ b/Content.Client/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs @@ -0,0 +1,38 @@ +using Content.Client.Xenoarchaeology.Ui; +using Content.Shared.Xenoarchaeology.Equipment; +using Content.Shared.Xenoarchaeology.Equipment.Components; +using Robust.Client.GameObjects; + +namespace Content.Client.Xenoarchaeology.Equipment; + +/// +/// This handles... +/// +public sealed class ArtifactAnalyzerSystem : SharedArtifactAnalyzerSystem +{ + [Dependency] private readonly UserInterfaceSystem _ui = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAnalysisConsoleAfterAutoHandleState); + SubscribeLocalEvent(OnAnalyzerAfterAutoHandleState); + } + + private void OnAnalysisConsoleAfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (_ui.TryGetOpenUi(ent.Owner, ArtifactAnalzyerUiKey.Key, out var bui)) + bui.Update(ent); + } + + private void OnAnalyzerAfterAutoHandleState(Entity ent, ref AfterAutoHandleStateEvent args) + { + if (!TryGetAnalysisConsole(ent, out var analysisConsole)) + return; + + if (_ui.TryGetOpenUi(analysisConsole.Value.Owner, ArtifactAnalzyerUiKey.Key, out var bui)) + bui.Update(analysisConsole.Value); + } +} diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs index 2538caf6eb8d..477ad2b04d31 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs @@ -1,19 +1,15 @@ using Content.Shared.Xenoarchaeology.Equipment; +using Content.Shared.Xenoarchaeology.Equipment.Components; using JetBrains.Annotations; -using Robust.Client.GameObjects; namespace Content.Client.Xenoarchaeology.Ui; [UsedImplicitly] -public sealed class AnalysisConsoleBoundUserInterface : BoundUserInterface +public sealed class AnalysisConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey) { [ViewVariables] private AnalysisConsoleMenu? _consoleMenu; - public AnalysisConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) - { - } - protected override void Open() { base.Open(); @@ -49,18 +45,9 @@ protected override void Open() }; } - protected override void UpdateState(BoundUserInterfaceState state) + public void Update(Entity ent) { - base.UpdateState(state); - - switch (state) - { - case AnalysisConsoleUpdateState msg: - _consoleMenu?.SetButtonsDisabled(msg); - _consoleMenu?.UpdateInformationDisplay(msg); - _consoleMenu?.UpdateProgressBar(msg); - break; - } + _consoleMenu?.Update(ent); } protected override void Dispose(bool disposing) diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs index 2890bb3dbf7f..f1a9c8d36aa7 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs @@ -1,6 +1,9 @@ using Content.Client.Stylesheets; using Content.Client.UserInterface.Controls; +using Content.Client.Xenoarchaeology.Artifact; +using Content.Client.Xenoarchaeology.Equipment; using Content.Shared.Xenoarchaeology.Equipment; +using Content.Shared.Xenoarchaeology.Equipment.Components; using Robust.Client.AutoGenerated; using Robust.Client.GameObjects; using Robust.Client.UserInterface.Controls; @@ -15,6 +18,8 @@ public sealed partial class AnalysisConsoleMenu : FancyWindow { [Dependency] private readonly IEntityManager _ent = default!; [Dependency] private readonly IGameTiming _timing = default!; + private readonly ArtifactAnalyzerSystem _artifactAnalyzer; + private readonly XenoArtifactSystem _xenoArtifact; public event Action? OnServerSelectionButtonPressed; public event Action? OnScanButtonPressed; @@ -23,160 +28,19 @@ public sealed partial class AnalysisConsoleMenu : FancyWindow public event Action? OnUpBiasButtonPressed; public event Action? OnDownBiasButtonPressed; - // For rendering the progress bar, updated from BUI state - private TimeSpan? _startTime; - private TimeSpan? _totalTime; - private TimeSpan? _accumulatedRunTime; - - private bool _paused; - public AnalysisConsoleMenu() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); - ServerSelectionButton.OnPressed += _ => OnServerSelectionButtonPressed?.Invoke(); - ScanButton.OnPressed += _ => OnScanButtonPressed?.Invoke(); - PrintButton.OnPressed += _ => OnPrintButtonPressed?.Invoke(); - ExtractButton.OnPressed += _ => OnExtractButtonPressed?.Invoke(); - UpBiasButton.OnPressed += _ => OnUpBiasButtonPressed?.Invoke(); - DownBiasButton.OnPressed += _ => OnDownBiasButtonPressed?.Invoke(); - - var buttonGroup = new ButtonGroup(false); - UpBiasButton.Group = buttonGroup; - DownBiasButton.Group = buttonGroup; - } - - protected override void FrameUpdate(FrameEventArgs args) - { - base.FrameUpdate(args); - - if (_startTime is not { } start || _totalTime is not { } total || _accumulatedRunTime is not { } accumulated) - return; - - var remaining = total - accumulated; - if (!_paused) - { - // If the analyzer is running, its remaining time is further discounted by the time it's been running for. - remaining += start - _timing.CurTime; - } - var secsText = Math.Max((int) remaining.TotalSeconds, 0); - - ProgressLabel.Text = Loc.GetString("analysis-console-progress-text", - ("seconds", secsText)); - - // 1.0 - div because we want it to tick up not down - ProgressBar.Value = Math.Clamp(1.0f - (float) remaining.Divide(total), 0.0f, 1.0f); + _xenoArtifact = _ent.System(); + _artifactAnalyzer = _ent.System(); } - public void SetButtonsDisabled(AnalysisConsoleUpdateState state) + public void Update(Entity ent) { - ScanButton.Disabled = !state.CanScan; - PrintButton.Disabled = !state.CanPrint; - if (state.IsTraversalDown) - DownBiasButton.Pressed = true; - else - UpBiasButton.Pressed = true; - - ExtractButton.Disabled = false; - if (!state.ServerConnected) - { - ExtractButton.Disabled = true; - ExtractButton.ToolTip = Loc.GetString("analysis-console-no-server-connected"); - } - else if (!state.CanScan) - { - ExtractButton.Disabled = true; - - // CanScan can be false if either there's no analyzer connected or if there's - // no entity on the scanner. The `Information` text will always tell the user - // of the former case, but in the latter, it'll only show a message if a scan - // has never been performed, so add a tooltip to indicate that the artifact - // is gone. - if (state.AnalyzerConnected) - { - ExtractButton.ToolTip = Loc.GetString("analysis-console-no-artifact-placed"); - } - else - { - ExtractButton.ToolTip = null; - } - } - else if (state.PointAmount <= 0) - { - ExtractButton.Disabled = true; - ExtractButton.ToolTip = Loc.GetString("analysis-console-no-points-to-extract"); - } - - if (ExtractButton.Disabled) - { - ExtractButton.RemoveStyleClass("ButtonColorGreen"); - } - else - { - ExtractButton.AddStyleClass("ButtonColorGreen"); - ExtractButton.ToolTip = null; - } - } - private void UpdateArtifactIcon(EntityUid? uid) - { - if (uid == null) - { - ArtifactDisplay.Visible = false; - return; - } - - ArtifactDisplay.Visible = true; - ArtifactDisplay.SetEntity(uid); - } - - public void UpdateInformationDisplay(AnalysisConsoleUpdateState state) - { - var message = new FormattedMessage(); - - if (state.Scanning) - { - if (state.Paused) - { - message.AddMarkup(Loc.GetString("analysis-console-info-scanner-paused")); - } - else - { - message.AddMarkup(Loc.GetString("analysis-console-info-scanner")); - } - Information.SetMessage(message); - UpdateArtifactIcon(null); //set it to blank - return; - } - - UpdateArtifactIcon(_ent.GetEntity(state.Artifact)); - - if (state.ScanReport == null) - { - if (!state.AnalyzerConnected) //no analyzer connected - message.AddMarkup(Loc.GetString("analysis-console-info-no-scanner")); - else if (!state.CanScan) //no artifact - message.AddMarkup(Loc.GetString("analysis-console-info-no-artifact")); - else if (state.Artifact == null) //ready to go - message.AddMarkup(Loc.GetString("analysis-console-info-ready")); - } - else - { - message.AddMessage(state.ScanReport); - } - - Information.SetMessage(message); - } - - public void UpdateProgressBar(AnalysisConsoleUpdateState state) - { - ProgressBar.Visible = state.Scanning; - ProgressLabel.Visible = state.Scanning; - - _startTime = state.StartTime; - _totalTime = state.TotalTime; - _accumulatedRunTime = state.AccumulatedRunTime; - _paused = state.Paused; + Logger.Debug("Ok we got a message here:"); + Logger.Debug($"arti not null : {_artifactAnalyzer.TryGetArtifactFromConsole(ent, out _)}"); } } diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 98432f342720..1a502325ecd6 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -34,7 +34,8 @@ private List> PopulateArtifactSegmentRecursive ref int segmentSize, int layerMinMod = 0, int layerMaxMod = 0, - bool ensureLayerConnected = false) + bool ensureLayerConnected = false, + int iteration = 0) { if (segmentSize == 0) return new(); @@ -51,7 +52,7 @@ private List> PopulateArtifactSegmentRecursive var nodes = new List>(); for (var i = 0; i < nodeCount; i++) { - nodes.Add(CreateRandomNode(ent)); + nodes.Add(CreateRandomNode(ent, iteration)); } var minMod = ent.Comp.NodeContainer.Count < 3 ? 0 : 1; // Try to stop boring linear generation. @@ -60,7 +61,9 @@ private List> PopulateArtifactSegmentRecursive ent, ref segmentSize, layerMinMod: minMod, - layerMaxMod: maxMod); + layerMaxMod: maxMod, + iteration: iteration + 1); + if (successors.Count == 0) return nodes; @@ -111,13 +114,15 @@ private int GetArtifactSegmentSize(Entity ent, int nodeCo return segmentSize; } - private Entity CreateRandomNode(Entity ent) + private Entity CreateRandomNode(Entity ent, int depth = 0) { var proto = PrototypeManager.Index(ent.Comp.EffectWeights).Pick(RobustRandom); AddNode((ent, ent), proto, out var nodeEnt, dirty: false); DebugTools.Assert(nodeEnt.HasValue, "Failed to create node on artifact."); + nodeEnt.Value.Comp.Depth = depth; + // TODO: setup trigger or effect or smth. idk quite how we're gonna do this. return nodeEnt.Value; diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs b/Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs similarity index 75% rename from Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs rename to Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs index 59ca913a3984..d526d95e5b8f 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/ArtifactAnalyzerSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Research.Systems; using Content.Shared.UserInterface; using Content.Server.Xenoarchaeology.Equipment.Components; +using Content.Server.Xenoarchaeology.Equipment.Systems; using Content.Server.Xenoarchaeology.XenoArtifacts; using Content.Server.Xenoarchaeology.XenoArtifacts.Events; using Content.Shared.Audio; @@ -13,6 +14,7 @@ using Content.Shared.Popups; using Content.Shared.Research.Components; using Content.Shared.Xenoarchaeology.Equipment; +using Content.Shared.Xenoarchaeology.Equipment.Components; using Content.Shared.Xenoarchaeology.XenoArtifacts; using JetBrains.Annotations; using Robust.Server.GameObjects; @@ -22,13 +24,13 @@ using Robust.Shared.Timing; using Robust.Shared.Utility; -namespace Content.Server.Xenoarchaeology.Equipment.Systems; +namespace Content.Server.Xenoarchaeology.Equipment; /// /// This system is used for managing the artifact analyzer as well as the analysis console. /// It also hanadles scanning and ui updates for both systems. /// -public sealed class ArtifactAnalyzerSystem : EntitySystem +public sealed class ArtifactAnalyzerSystem : SharedArtifactAnalyzerSystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; @@ -45,15 +47,14 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem /// public override void Initialize() { + base.Initialize(); + SubscribeLocalEvent(OnArtifactActivated); SubscribeLocalEvent(OnAnalyzeStart); SubscribeLocalEvent(OnAnalyzeEnd); SubscribeLocalEvent(OnPowerChanged); - SubscribeLocalEvent(OnItemPlaced); - SubscribeLocalEvent(OnItemRemoved); - SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(OnNewLink); SubscribeLocalEvent(OnPortDisconnected); @@ -65,9 +66,9 @@ public override void Initialize() SubscribeLocalEvent(OnBiasButton); SubscribeLocalEvent((e, c, _) => UpdateUserInterface(e, c), - after: new[] { typeof(ResearchSystem) }); + after: [typeof(ResearchSystem)]); SubscribeLocalEvent((e, c, _) => UpdateUserInterface(e, c), - after: new[] { typeof(ResearchSystem) }); + after: [typeof(ResearchSystem)]); SubscribeLocalEvent((e, c, _) => UpdateUserInterface(e, c)); } @@ -88,22 +89,6 @@ public override void Update(float frameTime) } } - /// - /// Resets the current scan on the artifact analyzer - /// - /// The analyzer being reset - /// - [PublicAPI] - public void ResetAnalyzer(EntityUid uid, ArtifactAnalyzerComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - component.LastAnalyzedArtifact = null; - component.ReadyToPrint = false; - UpdateAnalyzerInformation(uid, component); - } - /// /// Goes through the current entities on /// the analyzer and returns a valid artifact @@ -129,20 +114,6 @@ private void UpdateAnalyzerInformation(EntityUid uid, ArtifactAnalyzerComponent? { if (!Resolve(uid, ref component)) return; - - if (component.LastAnalyzedArtifact == null) - { - component.LastAnalyzerPointValue = null; - component.LastAnalyzedNode = null; - } - else if (TryComp(component.LastAnalyzedArtifact, out var artifact)) - { - var lastNode = artifact.CurrentNodeId == null - ? null - : (ArtifactNode?) _artifact.GetNodeFromId(artifact.CurrentNodeId.Value, artifact).Clone(); - component.LastAnalyzedNode = lastNode; - component.LastAnalyzerPointValue = _artifact.GetResearchPointValue(component.LastAnalyzedArtifact.Value, artifact); - } } private void OnMapInit(EntityUid uid, ArtifactAnalyzerComponent component, MapInitEvent args) @@ -197,7 +168,6 @@ private void UpdateUserInterface(EntityUid uid, AnalysisConsoleComponent? compon if (TryComp(component.AnalyzerEntity, out var analyzer)) { - artifact = analyzer.LastAnalyzedArtifact; msg = GetArtifactScanMessage(analyzer); totalTime = analyzer.AnalysisDuration; if (TryComp(component.AnalyzerEntity, out var placer)) @@ -270,76 +240,12 @@ private void OnScanButton(EntityUid uid, AnalysisConsoleComponent component, Ana private void OnPrintButton(EntityUid uid, AnalysisConsoleComponent component, AnalysisConsolePrintButtonPressedMessage args) { - if (component.AnalyzerEntity == null) - return; - - if (!TryComp(component.AnalyzerEntity, out var analyzer) || - analyzer.LastAnalyzedNode == null || - analyzer.LastAnalyzerPointValue == null || - !analyzer.ReadyToPrint) - { - return; - } - analyzer.ReadyToPrint = false; - - var report = Spawn(component.ReportEntityId, Transform(uid).Coordinates); - _metaSystem.SetEntityName(report, Loc.GetString("analysis-report-title", ("id", analyzer.LastAnalyzedNode.Id))); - - var msg = GetArtifactScanMessage(analyzer); - if (msg == null) - return; - _popup.PopupEntity(Loc.GetString("analysis-console-print-popup"), uid); - _paper.SetContent(report, msg.ToMarkup()); - UpdateUserInterface(uid, component); } private FormattedMessage? GetArtifactScanMessage(ArtifactAnalyzerComponent component) { var msg = new FormattedMessage(); - if (component.LastAnalyzedNode == null) - return null; - - var n = component.LastAnalyzedNode; - - msg.AddMarkup(Loc.GetString("analysis-console-info-id", ("id", n.Id))); - msg.PushNewline(); - msg.AddMarkup(Loc.GetString("analysis-console-info-depth", ("depth", n.Depth))); - msg.PushNewline(); - - var activated = n.Triggered - ? "analysis-console-info-triggered-true" - : "analysis-console-info-triggered-false"; - msg.AddMarkup(Loc.GetString(activated)); - msg.PushNewline(); - - msg.PushNewline(); - var needSecondNewline = false; - - var triggerProto = _prototype.Index(n.Trigger); - if (triggerProto.TriggerHint != null) - { - msg.AddMarkup(Loc.GetString("analysis-console-info-trigger", - ("trigger", Loc.GetString(triggerProto.TriggerHint))) + "\n"); - needSecondNewline = true; - } - - var effectproto = _prototype.Index(n.Effect); - if (effectproto.EffectHint != null) - { - msg.AddMarkup(Loc.GetString("analysis-console-info-effect", - ("effect", Loc.GetString(effectproto.EffectHint))) + "\n"); - needSecondNewline = true; - } - - if (needSecondNewline) - msg.PushNewline(); - - msg.AddMarkup(Loc.GetString("analysis-console-info-edges", ("edges", n.Edges.Count))); - msg.PushNewline(); - - if (component.LastAnalyzerPointValue != null) - msg.AddMarkup(Loc.GetString("analysis-console-info-value", ("value", component.LastAnalyzerPointValue))); return msg; } @@ -433,7 +339,6 @@ public void FinishScan(EntityUid uid, ArtifactAnalyzerComponent? component = nul component.ReadyToPrint = true; _audio.PlayPvs(component.ScanFinishedSound, uid); - component.LastAnalyzedArtifact = active.Artifact; UpdateAnalyzerInformation(uid, component); RemComp(active.Artifact); @@ -469,25 +374,6 @@ public void ResumeScan(EntityUid uid, ArtifactAnalyzerComponent? component = nul UpdateUserInterface(component.Console.Value); } - private void OnItemPlaced(EntityUid uid, ArtifactAnalyzerComponent component, ref ItemPlacedEvent args) - { - if (component.Console != null && Exists(component.Console)) - UpdateUserInterface(component.Console.Value); - } - - private void OnItemRemoved(EntityUid uid, ArtifactAnalyzerComponent component, ref ItemRemovedEvent args) - { - // Scanners shouldn't give permanent remove vision to an artifact, and the scanned artifact doesn't have any - // component to track analyzers that have scanned it for removal if the artifact gets deleted. - // So we always clear this on removal. - component.LastAnalyzedArtifact = null; - - // cancel the scan if the artifact moves off the analyzer - CancelScan(args.OtherEntity); - if (Exists(component.Console)) - UpdateUserInterface(component.Console.Value); - } - private void OnAnalyzeStart(EntityUid uid, ActiveArtifactAnalyzerComponent component, ComponentStartup args) { if (TryComp(uid, out var powa)) diff --git a/Content.Server/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs b/Content.Server/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs deleted file mode 100644 index 892e24495bb4..000000000000 --- a/Content.Server/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs +++ /dev/null @@ -1,38 +0,0 @@ -using Content.Shared.DeviceLinking; -using Robust.Shared.Audio; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Xenoarchaeology.Equipment.Components; - -/// -/// The console that is used for artifact analysis -/// -[RegisterComponent] -public sealed partial class AnalysisConsoleComponent : Component -{ - /// - /// The analyzer entity the console is linked. - /// Can be null if not linked. - /// - [ViewVariables(VVAccess.ReadWrite)] - public EntityUid? AnalyzerEntity; - - /// - /// The machine linking port for the analyzer - /// - [DataField("linkingPort", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string LinkingPort = "ArtifactAnalyzerSender"; - - /// - /// The sound played when an artifact has points extracted. - /// - [DataField("extractSound")] - public SoundSpecifier ExtractSound = new SoundPathSpecifier("/Audio/Effects/radpulse11.ogg"); - - /// - /// The entity spawned by a report. - /// - [DataField("reportEntityId", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ReportEntityId = "Paper"; -} diff --git a/Content.Server/Xenoarchaeology/Equipment/Components/ArtifactAnalyzerComponent.cs b/Content.Server/Xenoarchaeology/Equipment/Components/ArtifactAnalyzerComponent.cs deleted file mode 100644 index 949717676fca..000000000000 --- a/Content.Server/Xenoarchaeology/Equipment/Components/ArtifactAnalyzerComponent.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Content.Server.Xenoarchaeology.XenoArtifacts; -using Content.Shared.Construction.Prototypes; -using Robust.Shared.Audio; -using Robust.Shared.Serialization.TypeSerializers.Implementations; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Server.Xenoarchaeology.Equipment.Components; - -/// -/// A machine that is combined and linked to the -/// in order to analyze artifacts and extract points. -/// -[RegisterComponent] -public sealed partial class ArtifactAnalyzerComponent : Component -{ - /// - /// How long it takes to analyze an artifact - /// - [DataField("analysisDuration", customTypeSerializer: typeof(TimespanSerializer))] - public TimeSpan AnalysisDuration = TimeSpan.FromSeconds(30); - - /// - /// The corresponding console entity. - /// Can be null if not linked. - /// - [ViewVariables] - public EntityUid? Console; - - [ViewVariables(VVAccess.ReadWrite)] - public bool ReadyToPrint = false; - - [DataField("scanFinishedSound")] - public SoundSpecifier ScanFinishedSound = new SoundPathSpecifier("/Audio/Machines/scan_finish.ogg"); - - #region Analysis Data - [DataField] - public EntityUid? LastAnalyzedArtifact; - - [ViewVariables] - public ArtifactNode? LastAnalyzedNode; - - [ViewVariables(VVAccess.ReadWrite)] - public int? LastAnalyzerPointValue; - #endregion -} diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs index 3716ac421ba8..740a8c1ddf91 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs @@ -8,6 +8,13 @@ namespace Content.Shared.Xenoarchaeology.Artifact.Components; [RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem)), AutoGenerateComponentState] public sealed partial class XenoArtifactNodeComponent : Component { + /// + /// Depth within the graph generation. + /// Used for sorting. + /// + [DataField, AutoNetworkedField] + public int Depth; + /// /// Denotes whether or not an artifact node has been activated through the required triggers. /// diff --git a/Content.Shared/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs b/Content.Shared/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs new file mode 100644 index 000000000000..d2cf19bd5008 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs @@ -0,0 +1,39 @@ +using Content.Shared.DeviceLinking; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Xenoarchaeology.Equipment.Components; + +/// +/// The console that is used for artifact analysis +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +public sealed partial class AnalysisConsoleComponent : Component +{ + /// + /// The analyzer entity the console is linked. + /// Can be null if not linked. + /// + [DataField, AutoNetworkedField] + public EntityUid? AnalyzerEntity; + + /// + /// The sound played when an artifact has points extracted. + /// + [DataField] + public SoundSpecifier? ExtractSound = new SoundPathSpecifier("/Audio/Effects/radpulse11.ogg"); + + /// + /// The machine linking port for the analyzer + /// + [DataField] + public ProtoId LinkingPort = "ArtifactAnalyzerSender"; +} + +[Serializable, NetSerializable] +public enum ArtifactAnalzyerUiKey : byte +{ + Key +} diff --git a/Content.Shared/Xenoarchaeology/Equipment/Components/ArtifactAnalyzerComponent.cs b/Content.Shared/Xenoarchaeology/Equipment/Components/ArtifactAnalyzerComponent.cs new file mode 100644 index 000000000000..661b43539024 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Equipment/Components/ArtifactAnalyzerComponent.cs @@ -0,0 +1,38 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Xenoarchaeology.Equipment.Components; + +/// +/// A machine that is combined and linked to the +/// in order to analyze artifacts and extract points. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +public sealed partial class ArtifactAnalyzerComponent : Component +{ + /// + /// How long it takes to analyze an artifact + /// + [DataField] + public TimeSpan AnalysisDuration = TimeSpan.FromSeconds(30); + + /// + /// The current artifact placed on this analyzer. + /// Can be null if none are present. + /// + [DataField, AutoNetworkedField] + public EntityUid? CurrentArtifact; + + /// + /// The corresponding console entity. + /// Can be null if not linked. + /// + [ViewVariables, AutoNetworkedField] + public EntityUid? Console; + + [ViewVariables(VVAccess.ReadWrite)] + public bool ReadyToPrint = false; + + [DataField] + public SoundSpecifier? ScanFinishedSound = new SoundPathSpecifier("/Audio/Machines/scan_finish.ogg"); +} diff --git a/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzer.cs b/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzer.cs index 07f2a60c8484..6799cca64441 100644 --- a/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzer.cs +++ b/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzer.cs @@ -3,12 +3,6 @@ namespace Content.Shared.Xenoarchaeology.Equipment; -[Serializable, NetSerializable] -public enum ArtifactAnalzyerUiKey : byte -{ - Key -} - [Serializable, NetSerializable] public sealed class AnalysisConsoleServerSelectionMessage : BoundUserInterfaceMessage { diff --git a/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs b/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs new file mode 100644 index 000000000000..64f0b53b6432 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs @@ -0,0 +1,67 @@ +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Placeable; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Equipment.Components; + +namespace Content.Shared.Xenoarchaeology.Equipment; + +public abstract class SharedArtifactAnalyzerSystem : EntitySystem +{ + /// + public override void Initialize() + { + SubscribeLocalEvent(OnItemPlaced); + SubscribeLocalEvent(OnItemRemoved); + } + + private void OnItemPlaced(Entity ent, ref ItemPlacedEvent args) + { + ent.Comp.CurrentArtifact = args.OtherEntity; + Dirty(ent); + } + + private void OnItemRemoved(Entity ent, ref ItemRemovedEvent args) + { + if (args.OtherEntity != ent.Comp.CurrentArtifact) + return; + + ent.Comp.CurrentArtifact = null; + Dirty(ent); + } + + public bool TryGetAnalyzer(Entity ent, [NotNullWhen(true)] out Entity? analyzer) + { + analyzer = null; + + if (!TryComp(ent.Comp.AnalyzerEntity, out var comp)) + return false; + + analyzer = (ent.Comp.AnalyzerEntity.Value, comp); + return true; + } + + public bool TryGetArtifactFromConsole(Entity ent, [NotNullWhen(true)] out Entity? artifact) + { + artifact = null; + + if (!TryGetAnalyzer(ent, out var analyzer)) + return false; + + if (!TryComp(analyzer.Value.Comp.CurrentArtifact, out var comp)) + return false; + + artifact = (analyzer.Value.Comp.CurrentArtifact.Value, comp); + return true; + } + + public bool TryGetAnalysisConsole(Entity ent, [NotNullWhen(true)] out Entity? analysisConsole) + { + analysisConsole = null; + + if (!TryComp(ent.Comp.Console, out var comp)) + return false; + + analysisConsole = (ent.Comp.Console.Value, comp); + return true; + } +} diff --git a/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml b/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml index 9c878c7e7c55..cd538ad76fc5 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/artifact_analyzer.yml @@ -48,7 +48,7 @@ - type: ItemPlacer whitelist: components: - - Artifact + - XenoArtifact - type: DeviceNetwork deviceNetId: Wired receiveFrequencyId: BasicDevice diff --git a/Resources/Prototypes/XenoArch/effects.yml b/Resources/Prototypes/XenoArch/effects.yml index 149bf4ac3f09..9f5d296ccf55 100644 --- a/Resources/Prototypes/XenoArch/effects.yml +++ b/Resources/Prototypes/XenoArch/effects.yml @@ -4,3 +4,5 @@ noSpawn: true components: - type: XenoArtifactNode + - type: NameIdentifier + group: XenoArtifactNode diff --git a/Resources/Prototypes/name_identifier_groups.yml b/Resources/Prototypes/name_identifier_groups.yml index 4823e31f55d0..c0a40137ac4f 100644 --- a/Resources/Prototypes/name_identifier_groups.yml +++ b/Resources/Prototypes/name_identifier_groups.yml @@ -29,6 +29,11 @@ minValue: 1000 maxValue: 9999 +- type: nameIdentifierGroup + id: XenoArtifactNode + minValue: 0 + maxValue: 9999 + # Used to suffix a number to any mob to identify player controlled mob griefing. - type: nameIdentifierGroup id: GenericNumber From ad6e6868732442dbb2dd4817757d2bb45af7a376 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Wed, 19 Jun 2024 20:19:15 -0400 Subject: [PATCH 07/81] convert to data types that dont make me want to kill myself --- .../Artifact/XenoArtifactSystem.cs | 25 -------- .../Tests/XenoArtifactTest.cs | 3 +- .../Artifact/XenoArtifactCommands.cs | 2 +- .../Artifact/XenoArtifactSystem.cs | 28 +-------- .../Components/XenoArtifactComponent.cs | 37 ++++------- .../SharedXenoArtifactSystem.Graph.cs | 63 ++++++++----------- .../Artifact/SharedXenoArtifactSystem.Node.cs | 2 +- 7 files changed, 45 insertions(+), 115 deletions(-) diff --git a/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs b/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs index 31c2f5e78951..436ed1dc7aea 100644 --- a/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs +++ b/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs @@ -1,6 +1,4 @@ using Content.Shared.Xenoarchaeology.Artifact; -using Content.Shared.Xenoarchaeology.Artifact.Components; -using Robust.Shared.GameStates; namespace Content.Client.Xenoarchaeology.Artifact; @@ -12,28 +10,5 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnHandleState); - } - - private void OnHandleState(Entity ent, ref ComponentHandleState args) - { - if (args.Current is not XenoArtifactComponentState state) - return; - - ResizeNodeGraph(ent, state.NodeVertices.Count); - - // Copy over node vertices - for (var i = 0; i < state.NodeVertices.Count; i++) - { - ent.Comp.NodeVertices[i] = GetEntity(state.NodeVertices[i]); - } - - for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixRows; i++) - { - for (var j = 0; j < ent.Comp.NodeAdjacencyMatrixColumns; j++) - { - ent.Comp.NodeAdjacencyMatrix[i, j] = state.NodeAdjacencyMatrix[i][j]; - } - } } } diff --git a/Content.IntegrationTests/Tests/XenoArtifactTest.cs b/Content.IntegrationTests/Tests/XenoArtifactTest.cs index 13c65508980c..d7d93c98e6d9 100644 --- a/Content.IntegrationTests/Tests/XenoArtifactTest.cs +++ b/Content.IntegrationTests/Tests/XenoArtifactTest.cs @@ -15,6 +15,7 @@ public sealed class XenoArtifactTest name: artifact components: - type: XenoArtifact + doGeneration: false - type: entity id: TestArtifactNode @@ -300,7 +301,7 @@ await server.WaitPost(() => artifactSystem.RebuildCachedActiveNodes(artifactEnt); - var activeNodes = new[] { node3!.Value.Owner, node5!.Value.Owner }; + var activeNodes = new[] { entManager.GetNetEntity(node3!.Value.Owner), entManager.GetNetEntity(node5!.Value.Owner) }; Assert.That(artifactEnt.Item2.CachedActiveNodes, Is.SupersetOf(activeNodes)); Assert.That(artifactEnt.Item2.CachedActiveNodes, Has.Count.EqualTo(activeNodes.Length)); diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactCommands.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactCommands.cs index a422cc2b26b8..091dbf09a026 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactCommands.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactCommands.cs @@ -39,7 +39,7 @@ public string PrintMatrix([PipedArgument] EntityUid ent) sb.Append($"\n{i:D2}|"); for (var j = 0; j < nodeCount; j++) { - var value = comp.NodeAdjacencyMatrix[i, j] + var value = comp.NodeAdjacencyMatrix[i][j] ? "X" : " "; sb.Append($" {value} |"); diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs index de536469113f..9bc4b44ed623 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs @@ -1,6 +1,5 @@ using Content.Shared.Xenoarchaeology.Artifact; using Content.Shared.Xenoarchaeology.Artifact.Components; -using Robust.Shared.GameStates; namespace Content.Server.Xenoarchaeology.Artifact; @@ -11,35 +10,12 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnArtifactMapInit); } - private void OnGetState(Entity ent, ref ComponentGetState args) - { - var nodeVertices = new List(); - foreach (var vertex in ent.Comp.NodeVertices) - { - nodeVertices.Add(GetNetEntity(vertex)); - } - - var nodeAdjacencyMatrix = new List>(); - for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixRows; i++) - { - var row = new List(); - for (var j = 0; j < ent.Comp.NodeAdjacencyMatrixColumns; j++) - { - row.Add(ent.Comp.NodeAdjacencyMatrix[i, j]); - } - - nodeAdjacencyMatrix.Add(row); - } - - args.State = new XenoArtifactComponentState(nodeVertices, nodeAdjacencyMatrix); - } - private void OnArtifactMapInit(Entity ent, ref MapInitEvent args) { - GenerateArtifactStructure(ent); + if (ent.Comp.DoGeneration) + GenerateArtifactStructure(ent); } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index ae13f3744a41..783506e6fd4d 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -3,7 +3,7 @@ using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; +using Robust.Shared.Utility; namespace Content.Shared.Xenoarchaeology.Artifact.Components; @@ -11,11 +11,14 @@ namespace Content.Shared.Xenoarchaeology.Artifact.Components; /// This is used for handling interactions with artifacts as well as /// storing data about artifact node graphs. /// -[RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem))] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem)), AutoGenerateComponentState] public sealed partial class XenoArtifactComponent : Component { public static string NodeContainerId = "node-container"; + [DataField] + public bool DoGeneration = true; + [ViewVariables] public Container NodeContainer = default!; @@ -24,8 +27,8 @@ public sealed partial class XenoArtifactComponent : Component /// The nodes in this artifact that are currently "active." /// This is cached and updated when nodes are removed, added, or unlocked. /// - [DataField] - public List CachedActiveNodes = new(); + [DataField, AutoNetworkedField] + public List CachedActiveNodes = new(); // NOTE: you should not be accessing any of these values directly. Use the methods in SharedXenoArtifactSystem.Graph #region Graph @@ -33,19 +36,19 @@ public sealed partial class XenoArtifactComponent : Component /// List of all of the nodes currently on this artifact. /// Indexes are used as a lookup table for . /// - [DataField] - public EntityUid?[] NodeVertices = []; + [DataField, AutoNetworkedField] + public NetEntity?[] NodeVertices = []; /// /// Adjacency matrix that stores connections between this artifact's nodes. /// A value of "true" denotes an directed edge from node1 to node2, where the location of the vertex is (node1, node2) /// A value of "false" denotes no edge. /// - [DataField] - public bool[,] NodeAdjacencyMatrix = { }; + [DataField, AutoNetworkedField] + public List> NodeAdjacencyMatrix = new(); - public int NodeAdjacencyMatrixRows => NodeAdjacencyMatrix.GetLength(0); - public int NodeAdjacencyMatrixColumns => NodeAdjacencyMatrix.GetLength(1); + public int NodeAdjacencyMatrixRows => NodeAdjacencyMatrix.Count; + public int NodeAdjacencyMatrixColumns => NodeAdjacencyMatrix.TryGetValue(0, out var value) ? value.Count : 0; #endregion #region GenerationInfo @@ -75,17 +78,3 @@ public sealed partial class XenoArtifactComponent : Component public ProtoId EffectWeights = "XenoArtifactEffectsDefault"; #endregion } - -[Serializable, NetSerializable] -public sealed class XenoArtifactComponentState : ComponentState -{ - public List NodeVertices; - - public List> NodeAdjacencyMatrix; - - public XenoArtifactComponentState(List nodeVertices, List> nodeAdjacencyMatrix) - { - NodeVertices = nodeVertices; - NodeAdjacencyMatrix = nodeAdjacencyMatrix; - } -} diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs index 5972b383feac..bfa403eb412b 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using System.Linq; using Content.Shared.Xenoarchaeology.Artifact.Components; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -52,7 +53,7 @@ public bool TryGetIndex(Entity ent, EntityUid node, [Not public Entity GetNode(Entity ent, int index) { - if (ent.Comp.NodeVertices[index] is { } uid) + if (ent.Comp.NodeVertices[index] is { } netUid && GetEntity(netUid) is var uid) return (uid, XenoArtifactNode(uid)); throw new ArgumentException($"index {index} does not correspond to an existing node in {ToPrettyString(ent)}"); @@ -67,7 +68,7 @@ public bool TryGetNode(Entity ent, int index, [NotNullWh if (index < 0 || index >= ent.Comp.NodeVertices.Length) return false; - if (ent.Comp.NodeVertices[index] is { } uid) + if (ent.Comp.NodeVertices[index] is { } netUid && GetEntity(netUid) is var uid) node = (uid, XenoArtifactNode(uid)); return node != null; @@ -82,7 +83,7 @@ public int GetFreeNodeIndex(Entity ent) var length = ent.Comp.NodeVertices.Length; for (var i = 0; i < length; i++) { - if (TryGetNode((ent, ent), i, out _)) + if (ent.Comp.NodeVertices[i] != null) continue; return i; @@ -94,10 +95,10 @@ public int GetFreeNodeIndex(Entity ent) public IEnumerable> GetAllNodes(Entity ent) { - foreach (var node in ent.Comp.NodeVertices) + foreach (var netNode in ent.Comp.NodeVertices) { - if (node is not null) - yield return (node.Value, XenoArtifactNode(node.Value)); + if (GetEntity(netNode) is { } node) + yield return (node, XenoArtifactNode(node)); } } @@ -130,12 +131,12 @@ public bool AddEdge(Entity ent, int fromIdx, int toIdx, DebugTools.Assert(fromIdx >= 0 && fromIdx < ent.Comp.NodeVertices.Length, $"fromIdx is out of bounds!"); DebugTools.Assert(toIdx >= 0 && toIdx < ent.Comp.NodeVertices.Length, $"toIdx is out of bounds!"); - if (ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx]) + if (ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx]) return false; //Edge is already present // TODO: add a safety check to prohibit cyclic paths. - ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx] = true; + ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx] = true; if (dirty) RebuildCachedActiveNodes(ent); return true; @@ -161,10 +162,10 @@ public bool RemoveEdge(Entity ent, int fromIdx, int toId DebugTools.Assert(fromIdx >= 0 && fromIdx < ent.Comp.NodeVertices.Length, $"fromIdx is out of bounds!"); DebugTools.Assert(toIdx >= 0 && toIdx < ent.Comp.NodeVertices.Length, $"toIdx is out of bounds!"); - if (!ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx]) + if (!ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx]) return false; //Edge doesn't exist - ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx] = false; + ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx] = false; if (dirty) RebuildCachedActiveNodes(ent); @@ -194,7 +195,7 @@ public bool AddNode(Entity ent, Entity GetDirectPredecessorNodes(Entity ent var indices = new HashSet(); for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixRows; i++) { - if (ent.Comp.NodeAdjacencyMatrix[i, nodeIdx]) + if (ent.Comp.NodeAdjacencyMatrix[i][nodeIdx]) indices.Add(i); } @@ -312,7 +313,7 @@ public HashSet GetDirectSuccessorNodes(Entity ent, var indices = new HashSet(); for (var i = 0; i < ent.Comp.NodeAdjacencyMatrixColumns; i++) { - if (ent.Comp.NodeAdjacencyMatrix[nodeIdx, i]) + if (ent.Comp.NodeAdjacencyMatrix[nodeIdx][i]) indices.Add(i); } @@ -405,41 +406,29 @@ public bool NodeHasEdge(Entity ent, var fromIdx = GetIndex((ent, ent.Comp), from); var toIdx = GetIndex((ent, ent.Comp), to); - return ent.Comp.NodeAdjacencyMatrix[fromIdx, toIdx]; + return ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx]; } /// /// Resizes the adjacency matrix and vertices array to newLength + /// or at least what it WOULD do if i wasn't forced to used shitty lists. /// protected void ResizeNodeGraph(Entity ent, int newSize) { Array.Resize(ref ent.Comp.NodeVertices, newSize); - ent.Comp.NodeAdjacencyMatrix = XenoArtifactGraphHelpers.ResizeArray(ent.Comp.NodeAdjacencyMatrix, newSize, newSize); - Dirty(ent); - } -} -public static class XenoArtifactGraphHelpers -{ - /// - /// Resizes a 2d array to the specified dimensions, retaining the positions of existing values in the array. - /// Taken from https://stackoverflow.com/a/9059866. - /// - public static T[,] ResizeArray(T[,] original, int x, int y) - { - var newArray = new T[x, y]; - var minX = Math.Min(original.GetLength(0), newArray.GetLength(0)); - var minY = Math.Min(original.GetLength(1), newArray.GetLength(1)); - - for (var i = 0; i < minY; ++i) + while (ent.Comp.NodeAdjacencyMatrix.Count < newSize) { - Array.Copy(original, - i * original.GetLength(0), - newArray, - i * newArray.GetLength(0), - minX); + ent.Comp.NodeAdjacencyMatrix.Add(new()); } - return newArray; + foreach (var row in ent.Comp.NodeAdjacencyMatrix) + { + while (row.Count < newSize) + { + row.Add(false); + } + } + Dirty(ent); } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index eca37e998048..09e77ee33ed4 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -113,7 +113,7 @@ public void RebuildCachedActiveNodes(Entity ent) continue; } - ent.Comp.CachedActiveNodes.Add(node); + ent.Comp.CachedActiveNodes.Add(GetNetEntity(node)); } Dirty(ent); From d6d9965cc0cd0ac2a555f7e0efbdcd6dfba63ed7 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Fri, 21 Jun 2024 16:16:52 -0400 Subject: [PATCH 08/81] starting work on console UI --- .../Ui/AnalysisConsoleBoundUserInterface.cs | 2 +- .../Ui/AnalysisConsoleMenu.xaml | 18 +-- .../Ui/AnalysisConsoleMenu.xaml.cs | 10 +- .../Ui/XenoArtifactGraphControl.xaml | 5 + .../Ui/XenoArtifactGraphControl.xaml.cs | 55 +++++++++ .../Equipment/ArtifactAnalyzerSystem.cs | 108 +++++------------- .../Components/XenoArtifactComponent.cs | 3 + .../Components/AnalysisConsoleComponent.cs | 2 +- .../Equipment/SharedArtifactAnalyzerSystem.cs | 7 +- 9 files changed, 108 insertions(+), 102 deletions(-) create mode 100644 Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml create mode 100644 Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs index 477ad2b04d31..fff82180fb8e 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs @@ -14,7 +14,7 @@ protected override void Open() { base.Open(); - _consoleMenu = new AnalysisConsoleMenu(); + _consoleMenu = new AnalysisConsoleMenu(Owner); _consoleMenu.OnClose += Close; _consoleMenu.OpenCentered(); diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml index 29f4a5484790..f449dcfd2f01 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml @@ -2,6 +2,7 @@ xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls" + xmlns:ui="clr-namespace:Content.Client.Xenoarchaeology.Ui" Title="{Loc 'analysis-console-menu-title'}" MinSize="620 280" SetSize="620 280"> @@ -58,22 +59,7 @@ - - - - - - - - - - + diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs index f1a9c8d36aa7..d191f8a1e52b 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs @@ -28,19 +28,23 @@ public sealed partial class AnalysisConsoleMenu : FancyWindow public event Action? OnUpBiasButtonPressed; public event Action? OnDownBiasButtonPressed; - public AnalysisConsoleMenu() + public AnalysisConsoleMenu(EntityUid owner) { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); _xenoArtifact = _ent.System(); _artifactAnalyzer = _ent.System(); + + var comp = _ent.GetComponent(owner); + _artifactAnalyzer.TryGetArtifactFromConsole((owner, comp), out var arti); + GraphControl.SetArtifact(arti); } public void Update(Entity ent) { - Logger.Debug("Ok we got a message here:"); - Logger.Debug($"arti not null : {_artifactAnalyzer.TryGetArtifactFromConsole(ent, out _)}"); + _artifactAnalyzer.TryGetArtifactFromConsole(ent, out var arti); + GraphControl.SetArtifact(arti); } } diff --git a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml new file mode 100644 index 000000000000..b59f8d7c4686 --- /dev/null +++ b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml @@ -0,0 +1,5 @@ + diff --git a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs new file mode 100644 index 000000000000..9e69095ef21b --- /dev/null +++ b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs @@ -0,0 +1,55 @@ +using System.Linq; +using System.Numerics; +using Content.Client.Xenoarchaeology.Artifact; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.Xenoarchaeology.Ui; + +[GenerateTypedNameReferences] +public sealed partial class XenoArtifactGraphControl : BoxContainer +{ + [Dependency] private readonly IEntityManager _entityManager = default!; + + private Entity? _artifact; + + private float NodeRadius => 25 * UIScale; + + private float NodeDiameter => NodeRadius * 2; + + public XenoArtifactGraphControl() + { + IoCManager.InjectDependencies(this); + RobustXamlLoader.Load(this); + } + + public void SetArtifact(Entity? artifact) + { + _artifact = artifact; + } + + protected override void Draw(DrawingHandleScreen handle) + { + base.Draw(handle); + + if (_artifact == null) + return; + + var artiSys = _entityManager.System(); + + var bottomLeft = Position + Size with { X = 0 }; + + var tiers = artiSys.GetAllNodes(_artifact.Value).OrderBy(e => e.Comp.Depth).ToList(); + + for (var i = 0; i < tiers.Count; i++) + { + var node = tiers[i]; + var pos = bottomLeft + new Vector2(NodeDiameter * i + NodeRadius, -NodeRadius + NodeDiameter * node.Comp.Depth); + handle.DrawCircle(pos, NodeRadius, Color.White, false); + } + } +} + diff --git a/Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs b/Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs index d526d95e5b8f..b04060d6774b 100644 --- a/Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs @@ -126,7 +126,7 @@ private void OnMapInit(EntityUid uid, ArtifactAnalyzerComponent component, MapIn if (!TryComp(source, out var analysis)) continue; component.Console = source; - analysis.AnalyzerEntity = uid; + analysis.AnalyzerEntity = GetNetEntity(uid); return; } } @@ -136,8 +136,10 @@ private void OnNewLink(EntityUid uid, AnalysisConsoleComponent component, NewLin if (!TryComp(args.Sink, out var analyzer)) return; - component.AnalyzerEntity = args.Sink; + component.AnalyzerEntity = GetNetEntity(args.Sink); analyzer.Console = uid; + Dirty(uid, component); + Dirty(args.Sink, analyzer); UpdateUserInterface(uid, component); } @@ -146,8 +148,8 @@ private void OnPortDisconnected(EntityUid uid, AnalysisConsoleComponent componen { if (args.Port == component.LinkingPort && component.AnalyzerEntity != null) { - if (TryComp(component.AnalyzerEntity, out var analyzezr)) - analyzezr.Console = null; + // if (TryComp(component.AnalyzerEntity, out var analyzezr)) + // analyzezr.Console = null; component.AnalyzerEntity = null; } @@ -159,41 +161,8 @@ private void UpdateUserInterface(EntityUid uid, AnalysisConsoleComponent? compon if (!Resolve(uid, ref component, false)) return; - EntityUid? artifact = null; - FormattedMessage? msg = null; - TimeSpan? totalTime = null; - var canScan = false; - var canPrint = false; - var points = 0; + return; - if (TryComp(component.AnalyzerEntity, out var analyzer)) - { - msg = GetArtifactScanMessage(analyzer); - totalTime = analyzer.AnalysisDuration; - if (TryComp(component.AnalyzerEntity, out var placer)) - canScan = placer.PlacedEntities.Any(); - canPrint = analyzer.ReadyToPrint; - - // the artifact that's actually on the scanner right now. - if (GetArtifactForAnalysis(component.AnalyzerEntity, placer) is { } current) - points = _artifact.GetResearchPointValue(current); - } - - var analyzerConnected = component.AnalyzerEntity != null; - var serverConnected = TryComp(uid, out var client) && client.ConnectedToServer; - - var scanning = TryComp(component.AnalyzerEntity, out var active); - var paused = active != null ? active.AnalysisPaused : false; - - var biasDirection = BiasDirection.Up; - - if (TryComp(component.AnalyzerEntity, out var trav)) - biasDirection = trav.BiasDirection; - - var state = new AnalysisConsoleUpdateState(GetNetEntity(artifact), analyzerConnected, serverConnected, - canScan, canPrint, msg, scanning, paused, active?.StartTime, active?.AccumulatedRunTime, totalTime, points, biasDirection == BiasDirection.Down); - - _ui.SetUiState(uid, ArtifactAnalzyerUiKey.Key, state); } /// @@ -217,25 +186,6 @@ private void OnScanButton(EntityUid uid, AnalysisConsoleComponent component, Ana { if (component.AnalyzerEntity == null) return; - - if (HasComp(component.AnalyzerEntity)) - return; - - var ent = GetArtifactForAnalysis(component.AnalyzerEntity); - if (ent == null) - return; - - var activeComp = EnsureComp(component.AnalyzerEntity.Value); - activeComp.StartTime = _timing.CurTime; - activeComp.AccumulatedRunTime = TimeSpan.Zero; - activeComp.Artifact = ent.Value; - - if (TryComp(component.AnalyzerEntity.Value, out var powa)) - activeComp.AnalysisPaused = !powa.Powered; - - var activeArtifact = EnsureComp(ent.Value); - activeArtifact.Scanner = component.AnalyzerEntity.Value; - UpdateUserInterface(uid, component); } private void OnPrintButton(EntityUid uid, AnalysisConsoleComponent component, AnalysisConsolePrintButtonPressedMessage args) @@ -264,23 +214,23 @@ private void OnExtractButton(EntityUid uid, AnalysisConsoleComponent component, if (!_research.TryGetClientServer(uid, out var server, out var serverComponent)) return; - var artifact = GetArtifactForAnalysis(component.AnalyzerEntity); - if (artifact == null) - return; - - var pointValue = _artifact.GetResearchPointValue(artifact.Value); - - // no new nodes triggered so nothing to add - if (pointValue == 0) - return; - - _research.ModifyServerPoints(server.Value, pointValue, serverComponent); - _artifact.AdjustConsumedPoints(artifact.Value, pointValue); - - _audio.PlayPvs(component.ExtractSound, component.AnalyzerEntity.Value, AudioParams.Default.WithVolume(2f)); - - _popup.PopupEntity(Loc.GetString("analyzer-artifact-extract-popup"), - component.AnalyzerEntity.Value, PopupType.Large); + // var artifact = GetArtifactForAnalysis(component.AnalyzerEntity); + // if (artifact == null) + // return; + // + // var pointValue = _artifact.GetResearchPointValue(artifact.Value); + // + // // no new nodes triggered so nothing to add + // if (pointValue == 0) + // return; + // + // _research.ModifyServerPoints(server.Value, pointValue, serverComponent); + // _artifact.AdjustConsumedPoints(artifact.Value, pointValue); + // + // _audio.PlayPvs(component.ExtractSound, component.AnalyzerEntity.Value, AudioParams.Default.WithVolume(2f)); + // + // _popup.PopupEntity(Loc.GetString("analyzer-artifact-extract-popup"), + // component.AnalyzerEntity.Value, PopupType.Large); UpdateUserInterface(uid, component); } @@ -290,11 +240,11 @@ private void OnBiasButton(EntityUid uid, AnalysisConsoleComponent component, Ana if (component.AnalyzerEntity == null) return; - if (!TryComp(component.AnalyzerEntity, out var trav)) - return; - - if (!_traversalDistorter.SetState(component.AnalyzerEntity.Value, trav, args.IsDown)) - return; + // if (!TryComp(component.AnalyzerEntity, out var trav)) + // return; + // + // if (!_traversalDistorter.SetState(component.AnalyzerEntity.Value, trav, args.IsDown)) + // return; UpdateUserInterface(uid, component); } diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index 783506e6fd4d..18178169e1b5 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -30,6 +30,9 @@ public sealed partial class XenoArtifactComponent : Component [DataField, AutoNetworkedField] public List CachedActiveNodes = new(); + [DataField, AutoNetworkedField] + public List> CachedSegments = new(); + // NOTE: you should not be accessing any of these values directly. Use the methods in SharedXenoArtifactSystem.Graph #region Graph /// diff --git a/Content.Shared/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs b/Content.Shared/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs index d2cf19bd5008..052d0c85aabb 100644 --- a/Content.Shared/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs +++ b/Content.Shared/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs @@ -17,7 +17,7 @@ public sealed partial class AnalysisConsoleComponent : Component /// Can be null if not linked. /// [DataField, AutoNetworkedField] - public EntityUid? AnalyzerEntity; + public NetEntity? AnalyzerEntity; /// /// The sound played when an artifact has points extracted. diff --git a/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs b/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs index 64f0b53b6432..abb739b8a8b1 100644 --- a/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs +++ b/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs @@ -10,6 +10,8 @@ public abstract class SharedArtifactAnalyzerSystem : EntitySystem /// public override void Initialize() { + base.Initialize(); + SubscribeLocalEvent(OnItemPlaced); SubscribeLocalEvent(OnItemRemoved); } @@ -33,10 +35,11 @@ public bool TryGetAnalyzer(Entity ent, [NotNullWhen(tr { analyzer = null; - if (!TryComp(ent.Comp.AnalyzerEntity, out var comp)) + var analyzerUid = GetEntity(ent.Comp.AnalyzerEntity); + if (!TryComp(analyzerUid, out var comp)) return false; - analyzer = (ent.Comp.AnalyzerEntity.Value, comp); + analyzer = (analyzerUid.Value, comp); return true; } From 13716133bc3ffdfd73f0fdc49158c88538a08a2d Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Sat, 22 Jun 2024 17:08:11 -0400 Subject: [PATCH 09/81] drawing nodes n shit --- .../Ui/AnalysisConsoleMenu.xaml | 10 +- .../Ui/XenoArtifactGraphControl.xaml.cs | 84 +++++++++++++++-- .../Tests/XenoArtifactTest.cs | 88 +++++++++--------- .../Artifact/XenoArtifactSystem.ProcGen.cs | 8 +- .../Equipment/ArtifactAnalyzerSystem.cs | 46 ---------- .../Systems/TraversalDistorterSystem.cs | 21 ----- .../SharedXenoArtifactSystem.Graph.cs | 13 +-- .../Artifact/SharedXenoArtifactSystem.Node.cs | 92 +++++++++++++++++++ .../Equipment/SharedArtifactAnalyzerSystem.cs | 48 ++++++++++ 9 files changed, 275 insertions(+), 135 deletions(-) diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml index f449dcfd2f01..3e57fad189ba 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml @@ -4,8 +4,8 @@ xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls" xmlns:ui="clr-namespace:Content.Client.Xenoarchaeology.Ui" Title="{Loc 'analysis-console-menu-title'}" - MinSize="620 280" - SetSize="620 280"> + MinSize="700 350" + SetSize="950 500"> @@ -54,12 +54,12 @@ - + - - + + diff --git a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs index 9e69095ef21b..580210b2e209 100644 --- a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs @@ -17,8 +17,13 @@ public sealed partial class XenoArtifactGraphControl : BoxContainer private Entity? _artifact; private float NodeRadius => 25 * UIScale; - private float NodeDiameter => NodeRadius * 2; + private float MinYSpacing => NodeDiameter * 0.75f; + private float MaxYSpacing => NodeDiameter * 1.5f; + private float MinXSpacing => NodeDiameter * 0.33f; + private float MaxXSpacing => NodeDiameter * 1f; + private float MinXSegmentSpacing => NodeDiameter * 0.5f; + private float MaxXSegmentSpacing => NodeDiameter * 3f; public XenoArtifactGraphControl() { @@ -40,16 +45,81 @@ protected override void Draw(DrawingHandleScreen handle) var artiSys = _entityManager.System(); - var bottomLeft = Position + Size with { X = 0 }; + var maxDepth = artiSys.GetAllNodes(_artifact.Value).Max(s => s.Comp.Depth); + var segments = artiSys.GetSegments(_artifact.Value); + + var bottomLeft = Position // the position + + new Vector2(0, Size.Y * UIScale) // the scaled height of the control + + new Vector2(NodeRadius, -NodeRadius); // offset half a node so we don't render off screen + + var controlHeight = bottomLeft.Y; + var controlWidth = Size.X * UIScale - NodeRadius; + + var ySpacing = 0f; + if (maxDepth != 0) + ySpacing = Math.Clamp((controlHeight - ((maxDepth + 1) * NodeDiameter)) / maxDepth, MinYSpacing, MaxYSpacing); + + var segmentWidths = segments.Sum(GetBiggestWidth); + var segmentSpacing = Math.Clamp((controlWidth - segmentWidths) / (segments.Count - 1), MinXSegmentSpacing, MaxXSegmentSpacing); + var segmentOffset = Math.Max((controlWidth - (segmentWidths) - (segmentSpacing * (segments.Count - 1))) / 2, 0); + + bottomLeft.X += segmentOffset; + bottomLeft.Y -= (controlHeight - (ySpacing * maxDepth) - (NodeDiameter * (maxDepth + 1))) / 2; + + foreach (var segment in segments) + { + var orderedNodes = artiSys.GetDepthOrderedNodes(segment); + + foreach (var (_, nodes) in orderedNodes) + { + for (var i = 0; i < nodes.Count; i++) + { + var node = nodes[i]; + var pos = GetNodePos(node); + handle.DrawCircle(pos, NodeRadius, Color.White, false); + } + } + + foreach (var node in segment) + { + var from = GetNodePos(node) + new Vector2(0, -NodeRadius); + var successors = artiSys.GetDirectSuccessorNodes((_artifact.Value, _artifact.Value.Comp), node); + foreach (var s in successors) + { + var to = GetNodePos(s) + new Vector2(0, NodeRadius); + handle.DrawLine(from, to, Color.White); + } + } - var tiers = artiSys.GetAllNodes(_artifact.Value).OrderBy(e => e.Comp.Depth).ToList(); + bottomLeft.X += GetBiggestWidth(segment) + segmentSpacing; + } - for (var i = 0; i < tiers.Count; i++) + Vector2 GetNodePos(Entity node) { - var node = tiers[i]; - var pos = bottomLeft + new Vector2(NodeDiameter * i + NodeRadius, -NodeRadius + NodeDiameter * node.Comp.Depth); - handle.DrawCircle(pos, NodeRadius, Color.White, false); + var yPos = -(NodeDiameter + ySpacing) * node.Comp.Depth; + + var segment = segments!.First(s => s.Contains(node)); + var depthOrderedNodes = artiSys!.GetDepthOrderedNodes(segment); + var biggestTier = depthOrderedNodes.Max(s => s.Value.Count); + var nodesInLayer = depthOrderedNodes.GetValueOrDefault(node.Comp.Depth)!.Count; + var biggestWidth = (NodeDiameter + MinXSpacing) * biggestTier; + + var xSpacing = Math.Clamp((biggestWidth - (NodeDiameter * nodesInLayer)) / (nodesInLayer - 1), MinXSpacing, MaxXSpacing); + var layerXOffset = (biggestWidth - (xSpacing * (nodesInLayer - 1)) - (NodeDiameter * nodesInLayer)) / 2; + + var index = depthOrderedNodes.GetValueOrDefault(node.Comp.Depth)!.IndexOf(node); + + var xPos = NodeDiameter * index + (xSpacing * index) + layerXOffset; + + return bottomLeft + new Vector2(xPos, yPos); } } + + private float GetBiggestWidth(List> nodes) + { + var artiSys = _entityManager.System(); + var num = artiSys.GetDepthOrderedNodes(nodes).Max(p => p.Value.Count); + return (NodeDiameter * num) + MinXSpacing * (num - 1); + } } diff --git a/Content.IntegrationTests/Tests/XenoArtifactTest.cs b/Content.IntegrationTests/Tests/XenoArtifactTest.cs index d7d93c98e6d9..880ac1aea1f7 100644 --- a/Content.IntegrationTests/Tests/XenoArtifactTest.cs +++ b/Content.IntegrationTests/Tests/XenoArtifactTest.cs @@ -43,15 +43,15 @@ await server.WaitPost(() => var artifactEnt = (artifactUid, entManager.GetComponent(artifactUid)); // Create 3 nodes - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3, false)); Assert.That(artifactSystem.GetAllNodeIndices(artifactEnt).Count(), Is.EqualTo(3)); // Add connection from 1 -> 2 and 2-> 3 - artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value); - artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value); + artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value, false); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value, false); // Assert that successors and direct successors are counted correctly for node 1. Assert.That(artifactSystem.GetDirectSuccessorNodes(artifactEnt, node1!.Value).Count, Is.EqualTo(1)); @@ -97,26 +97,26 @@ await server.WaitPost(() => var artifactEnt = (artifactUid, entManager.GetComponent(artifactUid)); // Create 3 nodes - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node4)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node5)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node4, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node5, false)); Assert.That(artifactSystem.GetAllNodeIndices(artifactEnt).Count(), Is.EqualTo(5)); // Add connection: 1 -> 2 -> 3 -> 4 -> 5 - artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value); - artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value); - artifactSystem.AddEdge(artifactEnt, node3!.Value, node4!.Value); - artifactSystem.AddEdge(artifactEnt, node4!.Value, node5!.Value); + artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value, false); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value, false); + artifactSystem.AddEdge(artifactEnt, node3!.Value, node4!.Value, false); + artifactSystem.AddEdge(artifactEnt, node4!.Value, node5!.Value, false); // Make sure we have a continuous connection between the two ends of the graph. Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node1.Value), Has.Count.EqualTo(4)); Assert.That(artifactSystem.GetPredecessorNodes(artifactEnt, node5.Value), Has.Count.EqualTo(4)); // Remove the node and make sure it's no longer in the artifact. - Assert.That(artifactSystem.RemoveNode(artifactEnt, node3!.Value)); + Assert.That(artifactSystem.RemoveNode(artifactEnt, node3!.Value, false)); Assert.That(artifactSystem.TryGetIndex(artifactEnt, node3!.Value, out _), Is.False, "Node 3 still present in artifact."); // Check to make sure that we got rid of all the connections. @@ -146,13 +146,13 @@ await server.WaitPost(() => var artifactEnt = (artifactUid, entManager.GetComponent(artifactUid)); // Create 3 nodes - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3, false)); // Add connection: 1 -> 2 -> 3 - artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value); - artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value); + artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value, false); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value, false); // Make sure our connection is set up Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node1.Value, node2.Value)); @@ -209,20 +209,20 @@ await server.WaitPost(() => var artifactEnt = (artifactUid, entManager.GetComponent(artifactUid)); // Create 3 nodes - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3, false)); // Add connection: 1 -> 2 -> 3 - artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value); - artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value); + artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value, false); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node3!.Value, false); // Make sure our connection is set up Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node1.Value, node2.Value)); Assert.That(artifactSystem.NodeHasEdge(artifactEnt, node2.Value, node3.Value)); // Remove middle node, severing connections - artifactSystem.RemoveNode(artifactEnt, node2!.Value); + artifactSystem.RemoveNode(artifactEnt, node2!.Value, false); // Make sure our connection are properly severed. Assert.That(artifactSystem.GetSuccessorNodes(artifactEnt, node1.Value), Is.Empty); @@ -232,7 +232,7 @@ await server.WaitPost(() => Assert.That(artifactEnt.Item2.NodeAdjacencyMatrixRows, Is.EqualTo(3)); Assert.That(artifactEnt.Item2.NodeAdjacencyMatrixColumns, Is.EqualTo(3)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node4)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node4, false)); // Make sure that adding in a new node didn't add a new slot but instead re-used the middle slot. Assert.That(artifactEnt.Item2.NodeAdjacencyMatrixRows, Is.EqualTo(3)); @@ -267,14 +267,14 @@ await server.WaitPost(() => var artifactUid = entManager.Spawn("TestArtifact"); var artifactEnt = (artifactUid, entManager.GetComponent(artifactUid)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node4)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node5)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node6)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node7)); - Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node8)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node1, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node2, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node3, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node4, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node5, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node6, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node7, false)); + Assert.That(artifactSystem.AddNode(artifactEnt, "TestArtifactNode", out var node8, false)); // /----( 6 ) // /----[*3 ]-/----( 7 )----( 8 ) @@ -283,24 +283,22 @@ await server.WaitPost(() => // [ 1 ]--/----[ 2 ]--/----( 4 ) // Diagram of the example generation. Nodes in [brackets] are unlocked, nodes in (braces) are locked // and nodes with an *asterisk are supposed to be active. - artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value); - artifactSystem.AddEdge(artifactEnt, node1!.Value, node3!.Value); + artifactSystem.AddEdge(artifactEnt, node1!.Value, node2!.Value, false); + artifactSystem.AddEdge(artifactEnt, node1!.Value, node3!.Value, false); - artifactSystem.AddEdge(artifactEnt, node2!.Value, node4!.Value); - artifactSystem.AddEdge(artifactEnt, node2!.Value, node5!.Value); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node4!.Value, false); + artifactSystem.AddEdge(artifactEnt, node2!.Value, node5!.Value, false); - artifactSystem.AddEdge(artifactEnt, node3!.Value, node6!.Value); - artifactSystem.AddEdge(artifactEnt, node3!.Value, node7!.Value); + artifactSystem.AddEdge(artifactEnt, node3!.Value, node6!.Value, false); + artifactSystem.AddEdge(artifactEnt, node3!.Value, node7!.Value, false); - artifactSystem.AddEdge(artifactEnt, node7!.Value, node8!.Value); + artifactSystem.AddEdge(artifactEnt, node7!.Value, node8!.Value, false); artifactSystem.SetNodeUnlocked(node1!.Value); artifactSystem.SetNodeUnlocked(node2!.Value); artifactSystem.SetNodeUnlocked(node3!.Value); artifactSystem.SetNodeUnlocked(node5!.Value); - artifactSystem.RebuildCachedActiveNodes(artifactEnt); - var activeNodes = new[] { entManager.GetNetEntity(node3!.Value.Owner), entManager.GetNetEntity(node5!.Value.Owner) }; Assert.That(artifactEnt.Item2.CachedActiveNodes, Is.SupersetOf(activeNodes)); Assert.That(artifactEnt.Item2.CachedActiveNodes, Has.Count.EqualTo(activeNodes.Length)); @@ -310,4 +308,6 @@ await server.WaitPost(() => await pair.CleanReturnAsync(); } + + // TODO: write test for segments because we'll probably need that. } diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 1a502325ecd6..94813778c015 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -16,7 +16,7 @@ private void GenerateArtifactStructure(Entity ent) GenerateArtifactSegment(ent, ref nodeCount); } - RebuildCachedActiveNodes((ent, ent)); + RebuildNodeData((ent, ent)); } private void GenerateArtifactSegment(Entity ent, ref int nodeCount) @@ -24,9 +24,6 @@ private void GenerateArtifactSegment(Entity ent, ref int var segmentSize = GetArtifactSegmentSize(ent, nodeCount); nodeCount -= segmentSize; PopulateArtifactSegmentRecursive(ent, ref segmentSize, ensureLayerConnected: true); - - // TODO: store the segments in a list somewhere so we don't have to rebuild them constantly. - // Or maybe just rebuild them manually like we do active nodes??? hard to say. } private List> PopulateArtifactSegmentRecursive( @@ -67,6 +64,9 @@ private List> PopulateArtifactSegmentRecursive if (successors.Count == 0) return nodes; + // TODO: this doesn't actually make sure that the segment is interconnected. + // You can still occasionally get orphaned segments. + // We do the picks from node -> successor and from successor -> node to ensure that no nodes get orphaned without connections. foreach (var successor in successors) { diff --git a/Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs b/Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs index b04060d6774b..8b66aaeeddf8 100644 --- a/Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs @@ -38,8 +38,6 @@ public sealed class ArtifactAnalyzerSystem : SharedArtifactAnalyzerSystem [Dependency] private readonly SharedAmbientSoundSystem _ambientSound = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; - [Dependency] private readonly ArtifactSystem _artifact = default!; - [Dependency] private readonly PaperSystem _paper = default!; [Dependency] private readonly ResearchSystem _research = default!; [Dependency] private readonly MetaDataSystem _metaSystem = default!; [Dependency] private readonly TraversalDistorterSystem _traversalDistorter = default!; @@ -55,10 +53,6 @@ public override void Initialize() SubscribeLocalEvent(OnAnalyzeEnd); SubscribeLocalEvent(OnPowerChanged); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnNewLink); - SubscribeLocalEvent(OnPortDisconnected); - SubscribeLocalEvent(OnServerSelectionMessage); SubscribeLocalEvent(OnScanButton); SubscribeLocalEvent(OnPrintButton); @@ -116,46 +110,6 @@ private void UpdateAnalyzerInformation(EntityUid uid, ArtifactAnalyzerComponent? return; } - private void OnMapInit(EntityUid uid, ArtifactAnalyzerComponent component, MapInitEvent args) - { - if (!TryComp(uid, out var sink)) - return; - - foreach (var source in sink.LinkedSources) - { - if (!TryComp(source, out var analysis)) - continue; - component.Console = source; - analysis.AnalyzerEntity = GetNetEntity(uid); - return; - } - } - - private void OnNewLink(EntityUid uid, AnalysisConsoleComponent component, NewLinkEvent args) - { - if (!TryComp(args.Sink, out var analyzer)) - return; - - component.AnalyzerEntity = GetNetEntity(args.Sink); - analyzer.Console = uid; - Dirty(uid, component); - Dirty(args.Sink, analyzer); - - UpdateUserInterface(uid, component); - } - - private void OnPortDisconnected(EntityUid uid, AnalysisConsoleComponent component, PortDisconnectedEvent args) - { - if (args.Port == component.LinkingPort && component.AnalyzerEntity != null) - { - // if (TryComp(component.AnalyzerEntity, out var analyzezr)) - // analyzezr.Console = null; - component.AnalyzerEntity = null; - } - - UpdateUserInterface(uid, component); - } - private void UpdateUserInterface(EntityUid uid, AnalysisConsoleComponent? component = null) { if (!Resolve(uid, ref component, false)) diff --git a/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs b/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs index d277792243d8..3a3ba2609bc6 100644 --- a/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs +++ b/Content.Server/Xenoarchaeology/Equipment/Systems/TraversalDistorterSystem.cs @@ -27,27 +27,6 @@ private void OnInit(EntityUid uid, TraversalDistorterComponent component, MapIni component.NextActivation = _timing.CurTime; } - /// - /// Switches the state of the traversal distorter between up and down. - /// - /// The distorter's entity - /// The component on the entity - /// If the distorter changed state - public bool SetState(EntityUid uid, TraversalDistorterComponent component, bool isDown) - { - if (!this.IsPowered(uid, EntityManager)) - return false; - - if (_timing.CurTime < component.NextActivation) - return false; - - component.NextActivation = _timing.CurTime + component.ActivationDelay; - - component.BiasDirection = isDown ? BiasDirection.Down : BiasDirection.Up; - - return true; - } - private void OnExamine(EntityUid uid, TraversalDistorterComponent component, ExaminedEvent args) { string examine = string.Empty; diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs index bfa403eb412b..35e60450fb17 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using System.Linq; using Content.Shared.Xenoarchaeology.Artifact.Components; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -138,7 +137,7 @@ public bool AddEdge(Entity ent, int fromIdx, int toIdx, ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx] = true; if (dirty) - RebuildCachedActiveNodes(ent); + RebuildNodeData(ent); return true; } @@ -168,7 +167,7 @@ public bool RemoveEdge(Entity ent, int fromIdx, int toId ent.Comp.NodeAdjacencyMatrix[fromIdx][toIdx] = false; if (dirty) - RebuildCachedActiveNodes(ent); + RebuildNodeData(ent); return true; } @@ -199,7 +198,7 @@ public bool AddNode(Entity ent, Entity ent, Entity ent, int nodeIdx, if (dirty) { - RebuildCachedActiveNodes(ent); - Dirty(ent); + RebuildNodeData(ent); } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 09e77ee33ed4..12e2fdcd27a5 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -1,4 +1,6 @@ +using System.Linq; using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Shared.Utility; namespace Content.Shared.Xenoarchaeology.Artifact; @@ -70,6 +72,49 @@ public void SetNodeDurability(Entity ent, int durabi Dirty(ent); } + public List>> GetSegments(Entity ent) + { + var output = new List>>(); + + foreach (var segment in ent.Comp.CachedSegments) + { + var outSegment = new List>(); + foreach (var netNode in segment) + { + var node = GetEntity(netNode); + outSegment.Add((node, XenoArtifactNode(node))); + } + + output.Add(outSegment); + } + + return output; + } + + public Dictionary>> GetDepthOrderedNodes(IEnumerable> nodes) + { + var output = new Dictionary>>(); + + foreach (var node in nodes) + { + var depthList = output.GetOrNew(node.Comp.Depth); + depthList.Add(node); + } + + return output; + } + + /// + /// Rebuilds all the data associated with nodes in an artifact. + /// + public void RebuildNodeData(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return; + RebuildCachedActiveNodes(ent); + RebuildCachedSegments(ent); + } + /// /// Clears all cached active nodes and rebuilds the list using the current node state. /// Active nodes have the following property: @@ -118,4 +163,51 @@ public void RebuildCachedActiveNodes(Entity ent) Dirty(ent); } + + public void RebuildCachedSegments(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return; + + ent.Comp.CachedSegments.Clear(); + foreach (var node in GetAllNodes((ent, ent.Comp))) + { + var segment = new List>(); + GetSegmentNodesRecursive((ent, ent.Comp), node, ref segment); + + + if (segment.Count == 0) + continue; + + ent.Comp.CachedSegments.Add(segment.Select(n => GetNetEntity(n.Owner)).ToList()); + } + + Dirty(ent); + } + + private void GetSegmentNodesRecursive( + Entity ent, + Entity node, + ref List> segment) + { + if (ent.Comp.CachedSegments.Any(s => s.Contains(GetNetEntity(node)))) + return; + + if (segment.Contains(node)) + return; + + segment.Add(node); + + var predecessors = GetDirectPredecessorNodes((ent, ent), node); + foreach (var p in predecessors) + { + GetSegmentNodesRecursive(ent, p, ref segment); + } + + var successors = GetDirectSuccessorNodes((ent, ent), node); + foreach (var s in successors) + { + GetSegmentNodesRecursive(ent, s, ref segment); + } + } } diff --git a/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs b/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs index abb739b8a8b1..a90fd25545c1 100644 --- a/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs +++ b/Content.Shared/Xenoarchaeology/Equipment/SharedArtifactAnalyzerSystem.cs @@ -1,4 +1,6 @@ using System.Diagnostics.CodeAnalysis; +using Content.Shared.DeviceLinking; +using Content.Shared.DeviceLinking.Events; using Content.Shared.Placeable; using Content.Shared.Xenoarchaeology.Artifact.Components; using Content.Shared.Xenoarchaeology.Equipment.Components; @@ -14,6 +16,10 @@ public override void Initialize() SubscribeLocalEvent(OnItemPlaced); SubscribeLocalEvent(OnItemRemoved); + SubscribeLocalEvent(OnMapInit); + + SubscribeLocalEvent(OnNewLink); + SubscribeLocalEvent(OnPortDisconnected); } private void OnItemPlaced(Entity ent, ref ItemPlacedEvent args) @@ -31,6 +37,48 @@ private void OnItemRemoved(Entity ent, ref ItemRemove Dirty(ent); } + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + if (!TryComp(ent, out var sink)) + return; + + foreach (var source in sink.LinkedSources) + { + if (!TryComp(source, out var analysis)) + continue; + analysis.AnalyzerEntity = GetNetEntity(ent); + ent.Comp.Console = source; + Dirty(source, analysis); + Dirty(ent); + break; + } + } + + private void OnNewLink(Entity ent, ref NewLinkEvent args) + { + if (!TryComp(args.Sink, out var analyzer)) + return; + + ent.Comp.AnalyzerEntity = GetNetEntity(args.Sink); + analyzer.Console = ent; + Dirty(args.Sink, analyzer); + Dirty(ent); + } + + private void OnPortDisconnected(Entity ent, ref PortDisconnectedEvent args) + { + if (args.Port != ent.Comp.LinkingPort || ent.Comp.AnalyzerEntity == null) + return; + + if (TryComp(GetEntity(ent.Comp.AnalyzerEntity), out var analyzer)) + { + analyzer.Console = null; + Dirty(GetEntity(ent.Comp.AnalyzerEntity.Value), analyzer); + } + ent.Comp.AnalyzerEntity = null; + Dirty(ent); + } + public bool TryGetAnalyzer(Entity ent, [NotNullWhen(true)] out Entity? analyzer) { analyzer = null; From 5dae996aa640a0c294e1df3c252bdb8587c834ef Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Sat, 22 Jun 2024 21:48:22 -0400 Subject: [PATCH 10/81] damn that ui FUCKS --- .../Ui/XenoArtifactGraphControl.xaml | 3 +- .../Ui/XenoArtifactGraphControl.xaml.cs | 61 +++++++++++++++++-- .../Artifact/XenoArtifactSystem.cs | 55 +++++++++++++++++ .../Artifact/SharedXenoArtifactSystem.Node.cs | 5 ++ .../xenoarchaeology/artifact-analyzer.ftl | 2 +- .../xenoarchaeology/artifact-component.ftl | 6 +- .../Prototypes/name_identifier_groups.yml | 2 +- 7 files changed, 126 insertions(+), 8 deletions(-) diff --git a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml index b59f8d7c4686..54133875bdf2 100644 --- a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml +++ b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml @@ -2,4 +2,5 @@ xmlns="https://spacestation14.io" xmlns:controls="clr-namespace:Content.Client.Xenoarchaeology.Ui" HorizontalExpand="True" - VerticalExpand="True"/> + VerticalExpand="True" + MouseFilter="Stop"/> diff --git a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs index 580210b2e209..118d545ece47 100644 --- a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs @@ -1,11 +1,15 @@ using System.Linq; using System.Numerics; using Content.Client.Xenoarchaeology.Artifact; +using Content.Shared.NameIdentifier; using Content.Shared.Xenoarchaeology.Artifact.Components; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; +using Robust.Shared.Input; namespace Content.Client.Xenoarchaeology.Ui; @@ -16,6 +20,12 @@ public sealed partial class XenoArtifactGraphControl : BoxContainer private Entity? _artifact; + private Entity? _hoveredNode; + + private readonly Font _font; + + public event Action>? OnNodeSelected; + private float NodeRadius => 25 * UIScale; private float NodeDiameter => NodeRadius * 2; private float MinYSpacing => NodeDiameter * 0.75f; @@ -29,6 +39,8 @@ public XenoArtifactGraphControl() { IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); + + _font = new VectorFont(IoCManager.Resolve().GetResource("/EngineFonts/NotoSans/NotoSansMono-Regular.ttf"), 16); } public void SetArtifact(Entity? artifact) @@ -36,13 +48,28 @@ public void SetArtifact(Entity? artifact) _artifact = artifact; } + protected override void KeyBindDown(GUIBoundKeyEventArgs args) + { + base.KeyBindDown(args); + + if (args.Handled || args.Function != EngineKeyFunctions.UIClick) + return; + + if (_hoveredNode == null) + return; + OnNodeSelected?.Invoke(_hoveredNode.Value); + } + protected override void Draw(DrawingHandleScreen handle) { base.Draw(handle); + _hoveredNode = null; if (_artifact == null) return; + var cursor = (UserInterfaceManager.MousePositionScaled.Position * UIScale) - GlobalPixelPosition; + var artiSys = _entityManager.System(); var maxDepth = artiSys.GetAllNodes(_artifact.Value).Max(s => s.Comp.Depth); @@ -76,7 +103,29 @@ protected override void Draw(DrawingHandleScreen handle) { var node = nodes[i]; var pos = GetNodePos(node); - handle.DrawCircle(pos, NodeRadius, Color.White, false); + var hovered = (cursor - pos).LengthSquared() <= NodeRadius * NodeRadius; + + var color = Color.FromHex("#888888"); + if (artiSys.IsNodeActive(_artifact.Value, node)) + { + color = Color.Red; + } + else if (!node.Comp.Locked) + { + color = Color.White; + } + + if (hovered) + { + _hoveredNode = node; + handle.DrawCircle(pos, NodeRadius, Color.DimGray); + } + + handle.DrawCircle(pos, NodeRadius, Color.ToSrgb(color), false); + + var text = (_entityManager.GetComponentOrNull(node)?.Identifier ?? 0).ToString("D3"); + var dimensions = handle.GetDimensions(_font, text, 1); + handle.DrawString(_font, pos - new Vector2(dimensions.X / 2, dimensions.Y / 2), text, color); } } @@ -86,8 +135,12 @@ protected override void Draw(DrawingHandleScreen handle) var successors = artiSys.GetDirectSuccessorNodes((_artifact.Value, _artifact.Value.Comp), node); foreach (var s in successors) { + var color = Color.FromHex("#888888"); + if (!node.Comp.Locked && !s.Comp.Locked) + color = Color.White; + var to = GetNodePos(s) + new Vector2(0, NodeRadius); - handle.DrawLine(from, to, Color.White); + handle.DrawLine(from, to, color); } } @@ -98,8 +151,8 @@ Vector2 GetNodePos(Entity node) { var yPos = -(NodeDiameter + ySpacing) * node.Comp.Depth; - var segment = segments!.First(s => s.Contains(node)); - var depthOrderedNodes = artiSys!.GetDepthOrderedNodes(segment); + var segment = segments.First(s => s.Contains(node)); + var depthOrderedNodes = artiSys.GetDepthOrderedNodes(segment); var biggestTier = depthOrderedNodes.Max(s => s.Value.Count); var nodesInLayer = depthOrderedNodes.GetValueOrDefault(node.Comp.Depth)!.Count; var biggestWidth = (NodeDiameter + MinXSpacing) * biggestTier; diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs index 9bc4b44ed623..c872a74d8520 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs @@ -1,16 +1,71 @@ +using Content.Server.Administration; +using Content.Shared.Administration; using Content.Shared.Xenoarchaeology.Artifact; using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Shared.Console; namespace Content.Server.Xenoarchaeology.Artifact; public sealed partial class XenoArtifactSystem : SharedXenoArtifactSystem { + [Dependency] private readonly IConsoleHost _consoleHost = default!; + /// public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnArtifactMapInit); + + // this isn't a toolshed command because apparently it doesn't support autocompletion. + // and no, i'm not writing some complicated bullshit for a one-off testing command. + _consoleHost.RegisterCommand("unlocknode", + Loc.GetString("cmd-unlocknode-desc"), + Loc.GetString("cmd-unlocknode-help"), + UnlockNodeCommand, + UnlockNodeCompletion); + } + + + [AdminCommand(AdminFlags.Debug)] + private void UnlockNodeCommand(IConsoleShell shell, string argstr, string[] args) + { + if (args.Length != 2) + { + Log.Error("incorrect number of args!"); + return; + } + + if (!NetEntity.TryParse(args[1], out var netNode)) + { + Log.Error("invalid node entity"); + return; + } + + SetNodeUnlocked(GetEntity(netNode)); + } + + private CompletionResult UnlockNodeCompletion(IConsoleShell shell, string[] args) + { + if (args.Length == 1) + { + return CompletionResult.FromHintOptions(CompletionHelper.Components(args[0]), ""); + } + + if (args.Length == 2 && + NetEntity.TryParse(args[0], out var netEnt) && + GetEntity(netEnt) is var ent && + TryComp(ent, out var comp)) + { + var result = new List(); + foreach (var node in GetAllNodes((ent, comp))) + { + result.Add(new CompletionOption(GetNetEntity(node).ToString(), Name(node))); + } + + return CompletionResult.FromHintOptions(result, ""); + } + return CompletionResult.Empty; } private void OnArtifactMapInit(Entity ent, ref MapInitEvent args) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 12e2fdcd27a5..6364efac5d93 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -115,6 +115,11 @@ public void RebuildNodeData(Entity ent) RebuildCachedSegments(ent); } + public bool IsNodeActive(Entity ent, EntityUid node) + { + return ent.Comp.CachedActiveNodes.Contains(GetNetEntity(node)); + } + /// /// Clears all cached active nodes and rebuilds the list using the current node state. /// Active nodes have the following property: diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl index 35dd42167f63..2789e690f959 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl @@ -1,4 +1,4 @@ -analysis-console-menu-title = analysis console +analysis-console-menu-title = Broad-Spectrum Mark 3 Analysis Console analysis-console-server-list-button = Server List analysis-console-scan-button = Scan analysis-console-scan-tooltip-info = Scan artifacts to learn information about their structure. diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl index debb36530421..bd5b129bb8a5 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl @@ -1,4 +1,8 @@ -### Verbs +### Commands +cmd-unlocknode-desc = Unlocks a node on a given artifact +cmd-unlocknode-help = unlocknode + +### Verbs artifact-verb-make-always-active = Make artifact always active artifact-verb-activate = Activate artifact diff --git a/Resources/Prototypes/name_identifier_groups.yml b/Resources/Prototypes/name_identifier_groups.yml index c0a40137ac4f..f175058e9ac2 100644 --- a/Resources/Prototypes/name_identifier_groups.yml +++ b/Resources/Prototypes/name_identifier_groups.yml @@ -32,7 +32,7 @@ - type: nameIdentifierGroup id: XenoArtifactNode minValue: 0 - maxValue: 9999 + maxValue: 999 # Used to suffix a number to any mob to identify player controlled mob griefing. - type: nameIdentifierGroup From e3cea19a147d25c93ebd49fbd4bad82ae83c7c4a Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Sun, 23 Jun 2024 00:33:58 -0400 Subject: [PATCH 11/81] XAT --- .../Artifact/XenoArtifactSystem.ProcGen.cs | 11 +++- .../Components/XenoArtifactComponent.cs | 20 ++++++- .../Components/XenoArtifactNodeComponent.cs | 6 ++ .../XenoArtifactUnlockingComponent.cs | 22 ++++++++ .../SharedXenoArtifactSystem.Unlock.cs | 50 +++++++++++++++++ .../Artifact/SharedXenoArtifactSystem.cs | 13 +++++ .../Artifact/XAT/BaseXATSystem.cs | 55 +++++++++++++++++++ .../XAT/Components/XATCompNearbyComponent.cs | 20 +++++++ .../Artifact/XAT/XATCompNearbySystem.cs | 26 +++++++++ .../Artifact/XAT/XenoArchTriggerPrototype.cs | 17 ++++++ 10 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactUnlockingComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XATCompNearbySystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 94813778c015..50058ab153a2 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -1,5 +1,6 @@ using Content.Shared.Random.Helpers; using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -7,6 +8,8 @@ namespace Content.Server.Xenoarchaeology.Artifact; public sealed partial class XenoArtifactSystem { + private List _triggerPool = new(); + private void GenerateArtifactStructure(Entity ent) { var nodeCount = ent.Comp.NodeCount.Next(RobustRandom); @@ -23,6 +26,7 @@ private void GenerateArtifactSegment(Entity ent, ref int { var segmentSize = GetArtifactSegmentSize(ent, nodeCount); nodeCount -= segmentSize; + // TODO: generate a pool of triggers here based on segmentsize PopulateArtifactSegmentRecursive(ent, ref segmentSize, ensureLayerConnected: true); } @@ -121,10 +125,13 @@ private Entity CreateRandomNode(Entity -[RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem)), AutoGenerateComponentState] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem)), AutoGenerateComponentState, AutoGenerateComponentPause] public sealed partial class XenoArtifactComponent : Component { public static string NodeContainerId = "node-container"; @@ -22,7 +22,6 @@ public sealed partial class XenoArtifactComponent : Component [ViewVariables] public Container NodeContainer = default!; - // todo: instead of networking this what if we just reconstructed on the client... hm... /// /// The nodes in this artifact that are currently "active." /// This is cached and updated when nodes are removed, added, or unlocked. @@ -33,6 +32,23 @@ public sealed partial class XenoArtifactComponent : Component [DataField, AutoNetworkedField] public List> CachedSegments = new(); + #region Unlocking + /// + /// How long does the unlocking state last. + /// + [DataField] + public TimeSpan UnlockStateDuration = TimeSpan.FromSeconds(10); + + /// + /// Minimum waiting time between unlock states. + /// + [DataField] + public TimeSpan UnlockStateRefractory = TimeSpan.FromSeconds(10); + + [DataField, AutoPausedField] + public TimeSpan NextUnlockTime; + #endregion + // NOTE: you should not be accessing any of these values directly. Use the methods in SharedXenoArtifactSystem.Graph #region Graph /// diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs index 740a8c1ddf91..969f47adc87e 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs @@ -21,6 +21,12 @@ public sealed partial class XenoArtifactNodeComponent : Component [DataField, AutoNetworkedField] public bool Locked = true; + /// + /// Strings that denote the triggers that this node has. + /// + [DataField, AutoNetworkedField] + public List TriggerHints = new(); + /// /// The entity whose graph this node is a part of. /// diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactUnlockingComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactUnlockingComponent.cs new file mode 100644 index 000000000000..a760f50df361 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactUnlockingComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Xenoarchaeology.Artifact.Components; + +/// +/// This is used for tracking the nodes which have been triggered during a particular unlocking state. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] +public sealed partial class XenoArtifactUnlockingComponent : Component +{ + /// + /// Indexes corresponding to all of the nodes that have been triggered + /// + [DataField, AutoNetworkedField] + public HashSet TriggeredNodeIndexes = new(); + + /// + /// The time at which the unlocking state ends. + /// + [DataField, AutoNetworkedField, AutoPausedField] + public TimeSpan EndTime; +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs new file mode 100644 index 000000000000..26058a452a97 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -0,0 +1,50 @@ +using System.Linq; +using Content.Shared.Xenoarchaeology.Artifact.Components; + +namespace Content.Shared.Xenoarchaeology.Artifact; + +public abstract partial class SharedXenoArtifactSystem +{ + private EntityQuery _unlockingQuery; + + private void InitializeUnlock() + { + _unlockingQuery = GetEntityQuery(); + } + + private void UpdateUnlock(float frameTime) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var unlock, out var comp)) + { + if (_timing.CurTime < unlock.EndTime) + continue; + } + } + + public void TriggerXenoArtifact(Entity ent, Entity node) + { + if (_timing.CurTime < ent.Comp.NextUnlockTime) + return; + + if (!_unlockingQuery.TryGetComponent(ent, out var unlockingComp)) + { + unlockingComp = EnsureComp(ent); + unlockingComp.EndTime = _timing.CurTime + ent.Comp.UnlockStateDuration; + } + var index = GetIndex(ent, node); + unlockingComp.TriggeredNodeIndexes.Add(index); + } + + public bool CanUnlockNode(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + + if (!TryComp(ent.Comp.Attached, out var artiComp)) + return false; + + var predecessors = GetDirectPredecessorNodes((ent.Comp.Attached.Value, artiComp), ent); + return predecessors.Any(p => !p.Comp.Locked); + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs index 184ef7fcbe5c..a6737543ec69 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs @@ -2,11 +2,16 @@ using Robust.Shared.Containers; using Robust.Shared.Prototypes; using Robust.Shared.Random; +using Robust.Shared.Timing; namespace Content.Shared.Xenoarchaeology.Artifact; +/// +/// Handles all logic for generating and facilitating interactions with XenoArtifacts +/// public abstract partial class SharedXenoArtifactSystem : EntitySystem { + [Dependency] private readonly IGameTiming _timing = default!; [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] protected readonly IRobustRandom RobustRandom = default!; [Dependency] private readonly SharedContainerSystem _container = default!; @@ -17,6 +22,14 @@ public override void Initialize() SubscribeLocalEvent(OnStartup); InitializeNode(); + InitializeUnlock(); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + UpdateUnlock(frameTime); } private void OnStartup(Entity ent, ref ComponentStartup args) diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs new file mode 100644 index 000000000000..266494df145d --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs @@ -0,0 +1,55 @@ +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Robust.Shared.Timing; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT; + +public abstract class BaseXATSystem : EntitySystem where T : Component +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] protected readonly SharedXenoArtifactSystem XenoArtifact = default!; + + private EntityQuery _xenoArtifactQuery; + + /// + public override void Initialize() + { + _xenoArtifactQuery = GetEntityQuery(); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var node)) + { + if (node.Attached == null) + continue; + + var artifact = _xenoArtifactQuery.Get(node.Attached.Value); + + if (!CanTriggerCrude(artifact)) + continue; + + if (!XenoArtifact.CanUnlockNode((uid, node))) + continue; + + UpdateXAT(artifact, (uid, comp, node), frameTime); + } + } + + private bool CanTriggerCrude(Entity artifact) + { + return _timing.CurTime > artifact.Comp.NextUnlockTime; + } + + protected virtual void UpdateXAT(Entity artifact, Entity node, float frameTime) + { + + } + + protected void Trigger(Entity artifact, Entity node) + { + XenoArtifact.TriggerXenoArtifact(artifact, (node.Owner, node.Comp2)); + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs new file mode 100644 index 000000000000..e2a876a99b5d --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs @@ -0,0 +1,20 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used a XAT that activates when an entity fulfilling the given whitelist is nearby the artifact. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class XATCompNearbyComponent : Component +{ + [DataField(customTypeSerializer: typeof(ComponentNameSerializer))] + public string Component; + + [DataField] + public float Radius = 5; + + [DataField] + public int Count = 1; +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATCompNearbySystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATCompNearbySystem.cs new file mode 100644 index 000000000000..a71bd1755ab3 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATCompNearbySystem.cs @@ -0,0 +1,26 @@ +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT; + +public sealed class XATCompNearbySystem : BaseXATSystem +{ + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + private readonly HashSet> _entities = new(); + + protected override void UpdateXAT( + Entity artifact, + Entity node, + float frameTime) + { + var pos = _transform.GetMapCoordinates(artifact); + var comp = EntityManager.ComponentFactory.GetRegistration(node.Comp1.Component); + _entities.Clear(); + _entityLookup.GetEntitiesInRange(comp.Type, pos, node.Comp1.Radius, _entities); + + if (_entities.Count >= node.Comp1.Count) + Trigger(artifact, node); + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs new file mode 100644 index 000000000000..1a5353653483 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs @@ -0,0 +1,17 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT; + +[Prototype] +public sealed partial class XenoArchTriggerPrototype : IPrototype +{ + /// + [IdDataField] + public string ID { get; } = default!; + + [DataField] + public LocId Hint; + + [DataField] + public ComponentRegistry Components = new(); +} From e833ba9c3b2b36b53c4adbdf6e7a86858b855180 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Fri, 28 Jun 2024 15:58:34 -0400 Subject: [PATCH 12/81] Add a bunch of basic triggers --- .../XAT/Components/XATGasComponent.cs | 28 +++++ .../XAT/Components/XATPressureComponent.cs | 20 +++ .../XAT/Components/XATTemperatureComponent.cs | 20 +++ .../Artifact/XAT/XATGasSystem.cs | 34 +++++ .../Artifact/XAT/XATPressureSystem.cs | 27 ++++ .../Artifact/XAT/XATTemperatureSystem.cs | 34 +++++ .../Artifact/XenoArtifactSystem.ProcGen.cs | 2 +- .../Components/XenoArtifactNodeComponent.cs | 2 +- .../SharedXenoArtifactSystem.Unlock.cs | 15 +-- .../Artifact/SharedXenoArtifactSystem.XAT.cs | 66 ++++++++++ .../Artifact/SharedXenoArtifactSystem.cs | 1 + .../Artifact/XAT/BaseXATSystem.cs | 42 ++++++- .../XAT/Components/XATCompNearbyComponent.cs | 2 +- .../XAT/Components/XATDamageComponent.cs | 23 ++++ .../XAT/Components/XATExamineComponent.cs | 9 ++ .../Artifact/XAT/XATDamageSystem.cs | 52 ++++++++ .../Artifact/XAT/XATExamineSystem.cs | 27 ++++ .../Artifact/XAT/XenoArchTriggerPrototype.cs | 2 +- .../xenoarchaeology/artifact-component.ftl | 3 + .../en-US/xenoarchaeology/artifact-hints.ftl | 16 +++ .../Xenoarchaeology/xenoartifacts.yml | 2 + Resources/Prototypes/XenoArch/triggers.yml | 119 ++++++++++++++++++ 22 files changed, 522 insertions(+), 24 deletions(-) create mode 100644 Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATGasComponent.cs create mode 100644 Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATPressureComponent.cs create mode 100644 Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATTemperatureComponent.cs create mode 100644 Content.Server/Xenoarchaeology/Artifact/XAT/XATGasSystem.cs create mode 100644 Content.Server/Xenoarchaeology/Artifact/XAT/XATPressureSystem.cs create mode 100644 Content.Server/Xenoarchaeology/Artifact/XAT/XATTemperatureSystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATExamineComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs create mode 100644 Resources/Prototypes/XenoArch/triggers.yml diff --git a/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATGasComponent.cs b/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATGasComponent.cs new file mode 100644 index 000000000000..439581317f6b --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATGasComponent.cs @@ -0,0 +1,28 @@ +using Content.Shared.Atmos; + +namespace Content.Server.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for an artifact that is activated by having a certain amount of gas around it. +/// +[RegisterComponent, Access(typeof(XATGasSystem))] +public sealed partial class XATGasComponent : Component +{ + /// + /// The gas that needs to be supplied + /// + [DataField] + public Gas TargetGas; + + /// + /// The amount of gas needed. + /// + [DataField] + public float Moles = Atmospherics.MolesCellStandard * 0.1f; + + /// + /// Whether or not the gas should be present or not + /// + [DataField] + public bool ShouldBePresent = true; +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATPressureComponent.cs b/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATPressureComponent.cs new file mode 100644 index 000000000000..35c7999521ba --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATPressureComponent.cs @@ -0,0 +1,20 @@ +namespace Content.Server.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for an artifact that activates when above or below a certain pressure. +/// +[RegisterComponent, Access(typeof(XATPressureSystem))] +public sealed partial class XATPressureComponent : Component +{ + /// + /// The lower-end pressure threshold + /// + [DataField] + public float? MinPressureThreshold; + + /// + /// The higher-end pressure threshold + /// + [DataField] + public float? MaxPressureThreshold; +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATTemperatureComponent.cs b/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATTemperatureComponent.cs new file mode 100644 index 000000000000..b82e01c65f18 --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATTemperatureComponent.cs @@ -0,0 +1,20 @@ +namespace Content.Server.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for an artifact that is activated by having a certain temperature near it. +/// +[RegisterComponent, Access(typeof(XATTemperatureSystem))] +public sealed partial class XATTemperatureComponent : Component +{ + /// + /// The temperature that needs to be reached for the trigger + /// + [DataField] + public float TargetTemperature; + + /// + /// Whether or not the temp needs to be above or below the target. + /// + [DataField] + public bool TriggerOnHigherTemp = true; +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XAT/XATGasSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XAT/XATGasSystem.cs new file mode 100644 index 000000000000..5f8392e4c3de --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XAT/XATGasSystem.cs @@ -0,0 +1,34 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Server.Xenoarchaeology.Artifact.XAT.Components; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT; + +namespace Content.Server.Xenoarchaeology.Artifact.XAT; + +public sealed class XATGasSystem : BaseXATSystem +{ + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + + protected override void UpdateXAT(Entity artifact, Entity node, float frameTime) + { + base.UpdateXAT(artifact, node, frameTime); + + var xform = Transform(artifact); + + if (_atmosphere.GetTileMixture((artifact, xform)) is not { } mixture) + return; + + var moles = mixture.GetMoles(node.Comp1.TargetGas); + + if (node.Comp1.ShouldBePresent) + { + if (moles >= node.Comp1.Moles) + Trigger(artifact, node); + } + else + { + if (moles <= node.Comp1.Moles) + Trigger(artifact, node); + } + } +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XAT/XATPressureSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XAT/XATPressureSystem.cs new file mode 100644 index 000000000000..2d2be797692f --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XAT/XATPressureSystem.cs @@ -0,0 +1,27 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Server.Xenoarchaeology.Artifact.XAT.Components; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT; + +namespace Content.Server.Xenoarchaeology.Artifact.XAT; + +public sealed class XATPressureSystem : BaseXATSystem +{ + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + + protected override void UpdateXAT(Entity artifact, Entity node, float frameTime) + { + base.UpdateXAT(artifact, node, frameTime); + + var xform = Transform(artifact); + + if (_atmosphere.GetTileMixture((artifact, xform)) is not { } mixture) + return; + + var pressure = mixture.Pressure; + if (pressure >= node.Comp1.MaxPressureThreshold || pressure <= node.Comp1.MinPressureThreshold) + { + Trigger(artifact, node); + } + } +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XAT/XATTemperatureSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XAT/XATTemperatureSystem.cs new file mode 100644 index 000000000000..b85be507a36e --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XAT/XATTemperatureSystem.cs @@ -0,0 +1,34 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Server.Xenoarchaeology.Artifact.XAT.Components; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT; + +namespace Content.Server.Xenoarchaeology.Artifact.XAT; + +public sealed class XATTemperatureSystem : BaseXATSystem +{ + [Dependency] private readonly AtmosphereSystem _atmosphere = default!; + + protected override void UpdateXAT(Entity artifact, Entity node, float frameTime) + { + base.UpdateXAT(artifact, node, frameTime); + + var xform = Transform(artifact); + + if (_atmosphere.GetTileMixture((artifact, xform)) is not { } mixture) + return; + + var curTemp = mixture.Temperature; + + if (node.Comp1.TriggerOnHigherTemp) + { + if (curTemp >= node.Comp1.TargetTemperature) + Trigger(artifact, node); + } + else + { + if (curTemp <= node.Comp1.TargetTemperature) + Trigger(artifact, node); + } + } +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 50058ab153a2..29b8d4267b24 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -128,7 +128,7 @@ private Entity CreateRandomNode(Entity [DataField, AutoNetworkedField] - public List TriggerHints = new(); + public List TriggerTips = new(); /// /// The entity whose graph this node is a part of. diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs index 26058a452a97..362637fe1887 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -19,23 +19,10 @@ private void UpdateUnlock(float frameTime) { if (_timing.CurTime < unlock.EndTime) continue; + //todo: trigger unlock } } - public void TriggerXenoArtifact(Entity ent, Entity node) - { - if (_timing.CurTime < ent.Comp.NextUnlockTime) - return; - - if (!_unlockingQuery.TryGetComponent(ent, out var unlockingComp)) - { - unlockingComp = EnsureComp(ent); - unlockingComp.EndTime = _timing.CurTime + ent.Comp.UnlockStateDuration; - } - var index = GetIndex(ent, node); - unlockingComp.TriggeredNodeIndexes.Add(index); - } - public bool CanUnlockNode(Entity ent) { if (!Resolve(ent, ref ent.Comp)) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs new file mode 100644 index 000000000000..acbbbf5d6f66 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs @@ -0,0 +1,66 @@ +using Content.Shared.Damage; +using Content.Shared.Interaction; +using Content.Shared.Xenoarchaeology.Artifact.Components; + +namespace Content.Shared.Xenoarchaeology.Artifact; + +public abstract partial class SharedXenoArtifactSystem +{ + private void InitializeXAT() + { + XATRelayLocalEvent(); + XATRelayLocalEvent(); + } + + protected void XATRelayLocalEvent() where T : notnull + { + SubscribeLocalEvent(RelayEventToNodes); + } + + protected void RelayEventToNodes(Entity ent, ref T args) where T : notnull + { + var ev = new XATRelayedEvent(ent, args); + + var nodes = GetAllNodes(ent); + foreach (var node in nodes) + { + RaiseLocalEvent(node, ref ev); + } + } + + public void TriggerXenoArtifact(Entity ent, Entity node) + { + if (_timing.CurTime < ent.Comp.NextUnlockTime) + return; + + if (!_unlockingQuery.TryGetComponent(ent, out var unlockingComp)) + { + unlockingComp = EnsureComp(ent); + unlockingComp.EndTime = _timing.CurTime + ent.Comp.UnlockStateDuration; + Log.Debug($"{ToPrettyString(ent)} entered unlocking state"); + } + var index = GetIndex(ent, node); + + if (unlockingComp.TriggeredNodeIndexes.Add(index)) + { + Dirty(ent, unlockingComp); + } + } +} + +/// +/// Event wrapper for XenoArch Trigger events. +/// +[ByRefEvent] +public sealed class XATRelayedEvent : EntityEventArgs +{ + public Entity Artifact; + + public TEvent Args; + + public XATRelayedEvent(Entity artifact, TEvent args) + { + Artifact = artifact; + Args = args; + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs index a6737543ec69..745df51d4c1b 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs @@ -23,6 +23,7 @@ public override void Initialize() InitializeNode(); InitializeUnlock(); + InitializeXAT(); } public override void Update(float frameTime) diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs index 266494df145d..5577c786bee9 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs @@ -9,17 +9,39 @@ public abstract class BaseXATSystem : EntitySystem where T : Component [Dependency] protected readonly SharedXenoArtifactSystem XenoArtifact = default!; private EntityQuery _xenoArtifactQuery; + private EntityQuery _unlockingQuery; /// public override void Initialize() { _xenoArtifactQuery = GetEntityQuery(); + _unlockingQuery = GetEntityQuery(); } + protected void XATSubscribeLocalEvent(XATEventHandler eventHandler) where TEvent : notnull + { + SubscribeLocalEvent>((uid, component, args) => + { + var nodeComp = Comp(uid); + + if (!CanTrigger(args.Artifact, (uid, nodeComp))) + return; + + var node = new Entity(uid, component, nodeComp); + eventHandler.Invoke(args.Artifact, node, ref args.Args); + }); + } + + protected delegate void XATEventHandler(Entity artifact, Entity node, ref TEvent args) + where TEvent : notnull; + public override void Update(float frameTime) { base.Update(frameTime); + // TODO: need a pre-function so we can initialize queries + // TODO: add a way to defer triggering artifacts to the end of the Update loop + var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var comp, out var node)) { @@ -28,19 +50,26 @@ public override void Update(float frameTime) var artifact = _xenoArtifactQuery.Get(node.Attached.Value); - if (!CanTriggerCrude(artifact)) - continue; - - if (!XenoArtifact.CanUnlockNode((uid, node))) + if (!CanTrigger(artifact, (uid, node))) continue; UpdateXAT(artifact, (uid, comp, node), frameTime); } } - private bool CanTriggerCrude(Entity artifact) + private bool CanTrigger(Entity artifact, Entity node) { - return _timing.CurTime > artifact.Comp.NextUnlockTime; + if (_timing.CurTime < artifact.Comp.NextUnlockTime) + return false; + + if (_unlockingQuery.TryComp(artifact, out var unlocking) && + unlocking.TriggeredNodeIndexes.Contains(XenoArtifact.GetIndex(artifact, node))) + return false; + + if (!XenoArtifact.CanUnlockNode((node, node))) + return false; + + return true; } protected virtual void UpdateXAT(Entity artifact, Entity node, float frameTime) @@ -50,6 +79,7 @@ protected virtual void UpdateXAT(Entity artifact, Entity< protected void Trigger(Entity artifact, Entity node) { + Log.Debug($"Activated trigger {nameof(T)} on node {ToPrettyString(node)} for {ToPrettyString(artifact)}"); XenoArtifact.TriggerXenoArtifact(artifact, (node.Owner, node.Comp2)); } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs index e2a876a99b5d..1eb716a466dc 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs @@ -6,7 +6,7 @@ namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; /// /// This is used a XAT that activates when an entity fulfilling the given whitelist is nearby the artifact. /// -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, Access(typeof(XATCompNearbyComponent))] public sealed partial class XATCompNearbyComponent : Component { [DataField(customTypeSerializer: typeof(ComponentNameSerializer))] diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs new file mode 100644 index 000000000000..8c1683b5d790 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for an artifact that is activated after a certain amount of damage is dealt. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(XATDamageSystem))] +public sealed partial class XATDamageComponent : Component +{ + [DataField, AutoNetworkedField] + public DamageSpecifier AccumulatedDamage = new(); + + [DataField] + public Dictionary, FixedPoint2> TypesNeeded; + + [DataField] + public Dictionary, FixedPoint2> GroupsNeeded; +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATExamineComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATExamineComponent.cs new file mode 100644 index 000000000000..1b001414841e --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATExamineComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for an artifact that is activated when someone examines it. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(XATExamineSystem))] +public sealed partial class XATExamineComponent : Component; diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs new file mode 100644 index 000000000000..3bd9a61885a3 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs @@ -0,0 +1,52 @@ +using Content.Shared.Damage; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT; + +public sealed class XATDamageSystem : BaseXATSystem +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + XATSubscribeLocalEvent(OnDamageChanged); + } + + private void OnDamageChanged(Entity artifact, Entity node, ref DamageChangedEvent args) + { + if (!args.DamageIncreased || args.DamageDelta == null) + return; + + node.Comp1.AccumulatedDamage += args.DamageDelta; + + foreach (var (type, needed) in node.Comp1.TypesNeeded) + { + if (node.Comp1.AccumulatedDamage.DamageDict.GetValueOrDefault(type) >= needed) + { + node.Comp1.AccumulatedDamage.DamageDict.Clear(); + Dirty(node, node.Comp1); + Trigger(artifact, node); + return; // intentional. Do not continue checks + } + } + + foreach (var (group, needed) in node.Comp1.GroupsNeeded) + { + if (!node.Comp1.AccumulatedDamage.TryGetDamageInGroup(_prototype.Index(group), out var damage)) + continue; + + if (damage >= needed) + { + node.Comp1.AccumulatedDamage.DamageDict.Clear(); + Dirty(node, node.Comp1); + Trigger(artifact, node); + return; // intentional. Do not continue checks + } + } + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs new file mode 100644 index 000000000000..1d4c6404f887 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs @@ -0,0 +1,27 @@ +using Content.Shared.Examine; +using Content.Shared.Ghost; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT; + +public sealed class XATExamineSystem : BaseXATSystem +{ + /// + public override void Initialize() + { + XATSubscribeLocalEvent(OnExamine); + } + + private void OnExamine(Entity artifact, Entity node, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + if (TryComp(args.Examiner, out var ghost) && !ghost.CanGhostInteract) + return; + + Trigger(artifact, node); + args.AddMarkup(Loc.GetString("artifact-examine-trigger-desc")); + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs index 1a5353653483..2c45fe7f9b58 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs @@ -10,7 +10,7 @@ public sealed partial class XenoArchTriggerPrototype : IPrototype public string ID { get; } = default!; [DataField] - public LocId Hint; + public LocId Tip; [DataField] public ComponentRegistry Components = new(); diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl index bd5b129bb8a5..d40c14da8ce9 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl @@ -6,3 +6,6 @@ cmd-unlocknode-help = unlocknode artifact-verb-make-always-active = Make artifact always active artifact-verb-activate = Activate artifact + +### Misc. +artifact-examine-trigger-desc = [color=gray][italic]Am I on your mind?[/italic][/color] diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl index 98dfa89fa9fc..9803590e580c 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl @@ -40,3 +40,19 @@ 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 + +xenoarch-trigger-tip-music = Musical vibrations +xenoarch-trigger-tip-heat = High temperatures +xenoarch-trigger-tip-cold = Low temperatures +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 = Radiation +xenoarch-trigger-tip-pressure-low = Low environmental pressure +xenoarch-trigger-tip-pressure-high = High environmental pressure +xenoarch-trigger-tip-examine = Close inspection diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml index b63f8891aa16..dcec8bc0d63d 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml @@ -41,3 +41,5 @@ - type: GuideHelp guides: - Xenoarchaeology + # These components are needed for certain triggers to work. + - type: RadiationReceiver diff --git a/Resources/Prototypes/XenoArch/triggers.yml b/Resources/Prototypes/XenoArch/triggers.yml new file mode 100644 index 000000000000..ef1cfd47e554 --- /dev/null +++ b/Resources/Prototypes/XenoArch/triggers.yml @@ -0,0 +1,119 @@ +- type: xenoArchTrigger + id: TriggerMusic + tip: xenoarch-trigger-tip-music + components: + - type: XATCompNearby + component: ActiveInstrumentComponent + +- type: xenoArchTrigger + id: TriggerHeat + tip: xenoarch-trigger-tip-heat + components: + - type: XATTemperature + targetTemperature: 373 + triggerOnHigherTemp: true + # TODO: add interaction for welders, etc. + - type: XATDamage + typesNeeded: + Heat: 50 + +- type: xenoArchTrigger + id: TriggerCold + tip: xenoarch-trigger-tip-cold + components: + - type: XATTemperature + targetTemperature: 255 + triggerOnHigherTemp: false + - type: XATDamage + typesNeeded: + Cold: 50 + +- type: xenoArchTrigger + id: TriggerNoOxygen + tip: xenoarch-trigger-tip-no-oxygen + components: + - type: XATGas + targetGas: Oxygen + moles: 10 + shouldBePresent: false + +# TODO: all the gas triggers need to support liquid versions as well +- type: xenoArchTrigger + id: TriggerWater + tip: xenoarch-trigger-tip-water + components: + - type: XATGas + targetGas: WaterVapor + +- type: xenoArchTrigger + id: TriggerCO2 + tip: xenoarch-trigger-tip-co2 + components: + - type: XATGas + targetGas: CarbonDioxide + +- type: xenoArchTrigger + id: TriggerPlasma + tip: xenoarch-trigger-tip-plasma + components: + - type: XATGas + targetGas: Plasma + +- type: xenoArchTrigger + id: TriggerTritium + tip: xenoarch-trigger-tip-tritium + components: + - type: XATGas + targetGas: Tritium + +- type: xenoArchTrigger + id: TriggerAmmonia + tip: xenoarch-trigger-tip-ammonia + components: + - type: XATGas + targetGas: Ammonia + +- type: xenoArchTrigger + id: TriggerN2O + tip: xenoarch-trigger-tip-n2o + components: + - type: XATGas + targetGas: NitrousOxide + +- type: xenoArchTrigger + id: TriggerFrezon + tip: xenoarch-trigger-tip-frezon + components: + - type: XATGas + targetGas: Frezon +# END GAS TRIGGERS + +- type: xenoArchTrigger + id: TriggerRadiation + tip: xenoarch-trigger-tip-radiation + components: + - type: XATDamage + typesNeeded: + Radiation: 100 + # TODO: legendary microwave trigger + # TODO: add rad receiver to artifact + +- type: xenoArchTrigger + id: TriggerPressureHigh + tip: xenoarch-trigger-tip-pressure-high + components: + - type: XATPressure + maxPressureThreshold: 385 + +- type: xenoArchTrigger + id: TriggerPressureLow + tip: xenoarch-trigger-tip-pressure-low + components: + - type: XATPressure + minPressureThreshold: 50 + +- type: xenoArchTrigger + id: TriggerExamine + tip: xenoarch-trigger-tip-examine + components: + - type: XATExamine From 95de7f5e05e9300a75f3fb124df0c696c9d9f3da Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Fri, 28 Jun 2024 17:10:14 -0400 Subject: [PATCH 13/81] Fix trigger gen --- .../Artifact/XenoArtifactSystem.cs | 1 - .../Instruments/InstrumentComponent.cs | 6 ------ Content.Server/PAI/PAISystem.cs | 1 + .../Artifact/XenoArtifactSystem.ProcGen.cs | 21 ++++++++++++++++++- .../Systems/ArtifactMusicTriggerSystem.cs | 1 + .../Instruments/SharedInstrumentComponent.cs | 4 ++++ .../Components/XenoArtifactComponent.cs | 7 ++++++- .../Components/XenoArtifactNodeComponent.cs | 4 ++-- .../SharedXenoArtifactSystem.Graph.cs | 2 +- .../Artifact/SharedXenoArtifactSystem.Node.cs | 2 +- .../SharedXenoArtifactSystem.Unlock.cs | 10 ++++++--- .../Artifact/SharedXenoArtifactSystem.XAT.cs | 2 ++ .../Artifact/XAT/BaseXATSystem.cs | 2 +- .../XAT/Components/XATCompNearbyComponent.cs | 10 ++++----- .../XAT/Components/XATDamageComponent.cs | 8 +++---- .../Artifact/XAT/XATCompNearbySystem.cs | 2 +- .../Artifact/XAT/XATExamineSystem.cs | 4 +++- .../Artifact/XAT/XenoArchTriggerPrototype.cs | 12 +++++++++++ Resources/Prototypes/XenoArch/triggers.yml | 21 ++++++++++++++++++- 19 files changed, 91 insertions(+), 29 deletions(-) diff --git a/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs b/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs index 436ed1dc7aea..a45585a71e4c 100644 --- a/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs +++ b/Content.Client/Xenoarchaeology/Artifact/XenoArtifactSystem.cs @@ -9,6 +9,5 @@ public sealed class XenoArtifactSystem : SharedXenoArtifactSystem public override void Initialize() { base.Initialize(); - } } diff --git a/Content.Server/Instruments/InstrumentComponent.cs b/Content.Server/Instruments/InstrumentComponent.cs index db9dbb375bca..ab1fd2ce0469 100644 --- a/Content.Server/Instruments/InstrumentComponent.cs +++ b/Content.Server/Instruments/InstrumentComponent.cs @@ -1,4 +1,3 @@ -using Content.Server.UserInterface; using Content.Shared.Instruments; using Robust.Shared.Player; using ActivatableUIComponent = Content.Shared.UserInterface.ActivatableUIComponent; @@ -21,8 +20,3 @@ public sealed partial class InstrumentComponent : SharedInstrumentComponent _entMan.GetComponentOrNull(Owner)?.CurrentSingleUser ?? _entMan.GetComponentOrNull(Owner)?.PlayerSession.AttachedEntity; } - -[RegisterComponent] -public sealed partial class ActiveInstrumentComponent : Component -{ -} diff --git a/Content.Server/PAI/PAISystem.cs b/Content.Server/PAI/PAISystem.cs index 0cdb0bc29a2d..17fea50b72f2 100644 --- a/Content.Server/PAI/PAISystem.cs +++ b/Content.Server/PAI/PAISystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Popups; using Robust.Shared.Random; using System.Text; +using Content.Shared.Instruments; using Robust.Shared.Player; namespace Content.Server.PAI; diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 29b8d4267b24..742b8baaf566 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -8,12 +8,13 @@ namespace Content.Server.Xenoarchaeology.Artifact; public sealed partial class XenoArtifactSystem { - private List _triggerPool = new(); + private readonly List _triggerPool = new(); private void GenerateArtifactStructure(Entity ent) { var nodeCount = ent.Comp.NodeCount.Next(RobustRandom); ResizeNodeGraph(ent, nodeCount); + CreateTriggerPool(ent, nodeCount); while (nodeCount > 0) { GenerateArtifactSegment(ent, ref nodeCount); @@ -22,6 +23,23 @@ private void GenerateArtifactStructure(Entity ent) RebuildNodeData((ent, ent)); } + private void CreateTriggerPool(Entity ent, int size) + { + _triggerPool.Clear(); + var weightsProto = PrototypeManager.Index(ent.Comp.TriggerWeights); + var weights = new Dictionary(weightsProto.Weights); + + while (_triggerPool.Count < size) + { + var triggerId = RobustRandom.Pick(weights); + + var trigger = PrototypeManager.Index(triggerId); + + _triggerPool.Add(trigger); + weights.Remove(triggerId); + } + } + private void GenerateArtifactSegment(Entity ent, ref int nodeCount) { var segmentSize = GetArtifactSegmentSize(ent, nodeCount); @@ -118,6 +136,7 @@ private int GetArtifactSegmentSize(Entity ent, int nodeCo return segmentSize; } + //todo: move this into system.node or something. private Entity CreateRandomNode(Entity ent, int depth = 0) { var proto = PrototypeManager.Index(ent.Comp.EffectWeights).Pick(RobustRandom); diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMusicTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMusicTriggerSystem.cs index c62ed587527e..8a0ea261bd07 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMusicTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMusicTriggerSystem.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Server.Instruments; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; +using Content.Shared.Instruments; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; diff --git a/Content.Shared/Instruments/SharedInstrumentComponent.cs b/Content.Shared/Instruments/SharedInstrumentComponent.cs index 73500f3869d2..75d52b859941 100644 --- a/Content.Shared/Instruments/SharedInstrumentComponent.cs +++ b/Content.Shared/Instruments/SharedInstrumentComponent.cs @@ -34,6 +34,10 @@ public abstract partial class SharedInstrumentComponent : Component public BitArray FilteredChannels { get; set; } = new(RobustMidiEvent.MaxChannels, true); } + +[RegisterComponent, NetworkedComponent] +public sealed partial class ActiveInstrumentComponent : Component; + [Serializable, NetSerializable] public sealed class InstrumentComponentState : ComponentState { diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index 67547e8b974d..35022e2e3569 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Destructible.Thresholds; using Content.Shared.Random; +using Content.Shared.Xenoarchaeology.Artifact.XAT; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -71,11 +72,12 @@ public sealed partial class XenoArtifactComponent : Component #endregion #region GenerationInfo + /// /// The total number of nodes that make up this artifact. /// [DataField] - public MinMax NodeCount = new(10, 24); + public MinMax NodeCount = new(10, 12);// TODO: add back in. 24); /// /// The amount of nodes that go in each segment. @@ -95,5 +97,8 @@ public sealed partial class XenoArtifactComponent : Component [DataField] public ProtoId EffectWeights = "XenoArtifactEffectsDefault"; + + [DataField] + public ProtoId TriggerWeights = "DefaultTriggers"; #endregion } diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs index 88671db43eaa..23affa641683 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactNodeComponent.cs @@ -30,8 +30,8 @@ public sealed partial class XenoArtifactNodeComponent : Component /// /// The entity whose graph this node is a part of. /// - [DataField] - public EntityUid? Attached; + [DataField, AutoNetworkedField] + public NetEntity? Attached; #region Durability public bool Degraded => Durability <= 0; diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs index 35e60450fb17..c533ce114d1f 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs @@ -190,7 +190,7 @@ public bool AddNode(Entity ent, Entity ent) ent.Comp.Locked = false; if (ent.Comp.Attached is { } artifact) - RebuildCachedActiveNodes(artifact); + RebuildCachedActiveNodes(GetEntity(artifact)); Dirty(ent); } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs index 362637fe1887..9f7774087203 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -28,10 +28,14 @@ public bool CanUnlockNode(Entity ent) if (!Resolve(ent, ref ent.Comp)) return false; - if (!TryComp(ent.Comp.Attached, out var artiComp)) + var artifact = GetEntity(ent.Comp.Attached); + if (!TryComp(artifact, out var artiComp)) return false; - var predecessors = GetDirectPredecessorNodes((ent.Comp.Attached.Value, artiComp), ent); - return predecessors.Any(p => !p.Comp.Locked); + var predecessors = GetDirectPredecessorNodes((artifact.Value, artiComp), ent); + if (predecessors.Count != 0 && predecessors.All(p => p.Comp.Locked)) + return false; + + return true; } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs index acbbbf5d6f66..91f973287c4f 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs @@ -1,4 +1,5 @@ using Content.Shared.Damage; +using Content.Shared.Examine; using Content.Shared.Interaction; using Content.Shared.Xenoarchaeology.Artifact.Components; @@ -9,6 +10,7 @@ public abstract partial class SharedXenoArtifactSystem private void InitializeXAT() { XATRelayLocalEvent(); + XATRelayLocalEvent(); XATRelayLocalEvent(); } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs index 5577c786bee9..4e7baebfa6a9 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs @@ -48,7 +48,7 @@ public override void Update(float frameTime) if (node.Attached == null) continue; - var artifact = _xenoArtifactQuery.Get(node.Attached.Value); + var artifact = _xenoArtifactQuery.Get(GetEntity(node.Attached.Value)); if (!CanTrigger(artifact, (uid, node))) continue; diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs index 1eb716a466dc..2cb4f2161044 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATCompNearbyComponent.cs @@ -6,15 +6,15 @@ namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; /// /// This is used a XAT that activates when an entity fulfilling the given whitelist is nearby the artifact. /// -[RegisterComponent, NetworkedComponent, Access(typeof(XATCompNearbyComponent))] +[RegisterComponent, NetworkedComponent, Access(typeof(XATCompNearbyComponent)), AutoGenerateComponentState] public sealed partial class XATCompNearbyComponent : Component { - [DataField(customTypeSerializer: typeof(ComponentNameSerializer))] - public string Component; + [DataField(customTypeSerializer: typeof(ComponentNameSerializer)), AutoNetworkedField] + public string Comp = "Item"; - [DataField] + [DataField, AutoNetworkedField] public float Radius = 5; - [DataField] + [DataField, AutoNetworkedField] public int Count = 1; } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs index 8c1683b5d790..79eb7c757996 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs @@ -15,9 +15,9 @@ public sealed partial class XATDamageComponent : Component [DataField, AutoNetworkedField] public DamageSpecifier AccumulatedDamage = new(); - [DataField] - public Dictionary, FixedPoint2> TypesNeeded; + [DataField, AutoNetworkedField] + public Dictionary, FixedPoint2> TypesNeeded = new(); - [DataField] - public Dictionary, FixedPoint2> GroupsNeeded; + [DataField, AutoNetworkedField] + public Dictionary, FixedPoint2> GroupsNeeded = new(); } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATCompNearbySystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATCompNearbySystem.cs index a71bd1755ab3..07862aceb83c 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATCompNearbySystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATCompNearbySystem.cs @@ -16,7 +16,7 @@ protected override void UpdateXAT( float frameTime) { var pos = _transform.GetMapCoordinates(artifact); - var comp = EntityManager.ComponentFactory.GetRegistration(node.Comp1.Component); + var comp = EntityManager.ComponentFactory.GetRegistration(node.Comp1.Comp); _entities.Clear(); _entityLookup.GetEntitiesInRange(comp.Type, pos, node.Comp1.Radius, _entities); diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs index 1d4c6404f887..23f0d0e29874 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs @@ -10,6 +10,8 @@ public sealed class XATExamineSystem : BaseXATSystem /// public override void Initialize() { + base.Initialize(); + XATSubscribeLocalEvent(OnExamine); } @@ -22,6 +24,6 @@ private void OnExamine(Entity artifact, Entity))] + public Dictionary Weights { get; private set; } = new(); +} diff --git a/Resources/Prototypes/XenoArch/triggers.yml b/Resources/Prototypes/XenoArch/triggers.yml index ef1cfd47e554..3e5c700329f9 100644 --- a/Resources/Prototypes/XenoArch/triggers.yml +++ b/Resources/Prototypes/XenoArch/triggers.yml @@ -1,9 +1,28 @@ +- type: weightedRandomXenoArchTrigger + id: DefaultTriggers + weights: + TriggerMusic: 1 + TriggerHeat: 0.5 + TriggerCold: 0.5 + TriggerNoOxygen: 1 + TriggerWater: 1 + TriggerCO2: 0.16 + TriggerPlasma: 0.16 + TriggerTritium: 0.16 + TriggerAmmonia: 0.16 + TriggerN2O: 0.16 + TriggerFrezon: 0.16 + TriggerRadiation: 1 + TriggerPressureHigh: 0.5 + TriggerPressureLow: 0.5 + TriggerExamine: 1 + - type: xenoArchTrigger id: TriggerMusic tip: xenoarch-trigger-tip-music components: - type: XATCompNearby - component: ActiveInstrumentComponent + comp: ActiveInstrument - type: xenoArchTrigger id: TriggerHeat From 99b97c08df2a5c26fda962803631576d6b076f59 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Fri, 28 Jun 2024 21:58:04 -0400 Subject: [PATCH 14/81] Add node info into the analysis console UI --- .../Ui/AnalysisConsoleMenu.xaml | 100 +++++++++--------- .../Ui/AnalysisConsoleMenu.xaml.cs | 97 +++++++++++++++-- .../Ui/XenoArtifactGraphControl.xaml.cs | 11 +- .../Artifact/XenoArtifactSystem.ProcGen.cs | 2 +- .../Components/XenoArtifactNodeComponent.cs | 4 +- .../Artifact/SharedXenoArtifactSystem.Node.cs | 8 ++ .../SharedXenoArtifactSystem.Unlock.cs | 3 +- .../xenoarchaeology/artifact-analyzer.ftl | 36 ++++--- 8 files changed, 179 insertions(+), 82 deletions(-) diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml index 3e57fad189ba..736fc0729667 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml @@ -5,62 +5,58 @@ xmlns:ui="clr-namespace:Content.Client.Xenoarchaeology.Ui" Title="{Loc 'analysis-console-menu-title'}" MinSize="700 350" - SetSize="950 500"> + SetSize="980 500"> - - - - - - - - - - - + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - + + + + + + + + + + diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs index d191f8a1e52b..a6c74f0ffd19 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs @@ -1,15 +1,17 @@ -using Content.Client.Stylesheets; +using System.Text; +using Content.Client.Message; +using Content.Client.Resources; using Content.Client.UserInterface.Controls; using Content.Client.Xenoarchaeology.Artifact; using Content.Client.Xenoarchaeology.Equipment; -using Content.Shared.Xenoarchaeology.Equipment; +using Content.Shared.NameIdentifier; +using Content.Shared.Xenoarchaeology.Artifact.Components; using Content.Shared.Xenoarchaeology.Equipment.Components; using Robust.Client.AutoGenerated; -using Robust.Client.GameObjects; -using Robust.Client.UserInterface.Controls; +using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; using Robust.Client.UserInterface.XAML; using Robust.Shared.Timing; -using Robust.Shared.Utility; namespace Content.Client.Xenoarchaeology.Ui; @@ -17,10 +19,14 @@ namespace Content.Client.Xenoarchaeology.Ui; public sealed partial class AnalysisConsoleMenu : FancyWindow { [Dependency] private readonly IEntityManager _ent = default!; + [Dependency] private readonly IResourceCache _resCache = default!; [Dependency] private readonly IGameTiming _timing = default!; private readonly ArtifactAnalyzerSystem _artifactAnalyzer; private readonly XenoArtifactSystem _xenoArtifact; + private Entity _owner; + private Entity? _currentNode; + public event Action? OnServerSelectionButtonPressed; public event Action? OnScanButtonPressed; public event Action? OnPrintButtonPressed; @@ -36,15 +42,92 @@ public AnalysisConsoleMenu(EntityUid owner) _xenoArtifact = _ent.System(); _artifactAnalyzer = _ent.System(); + if (BackPanel.PanelOverride is StyleBoxTexture tex) + tex.Texture = _resCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); + + LockedLabel.SetMarkup(Loc.GetString("analysis-console-info-locked")); + ActiveLabel.SetMarkup(Loc.GetString("analysis-console-info-active")); + EffectLabel.SetMarkup(Loc.GetString("analysis-console-info-effect")); + TriggerLabel.SetMarkup(Loc.GetString("analysis-console-info-trigger")); + DurabilityLabel.SetMarkup(Loc.GetString("analysis-console-info-durability")); + + GraphControl.OnNodeSelected += node => + { + _currentNode = node; + SetSelectedNode(node); + }; + var comp = _ent.GetComponent(owner); - _artifactAnalyzer.TryGetArtifactFromConsole((owner, comp), out var arti); - GraphControl.SetArtifact(arti); + _owner = (owner, comp); + Update((owner, comp)); + SetSelectedNode(null); } public void Update(Entity ent) { _artifactAnalyzer.TryGetArtifactFromConsole(ent, out var arti); + ArtifactView.SetEntity(arti); GraphControl.SetArtifact(arti); + + if (_currentNode == null || arti == null || !_xenoArtifact.TryGetIndex((arti.Value, arti.Value), _currentNode.Value, out _)) + { + SetSelectedNode(null); + } + } + + public void SetSelectedNode(Entity? node) + { + var idLabelMsg = node != null + ? Loc.GetString("analysis-console-info-id") + : Loc.GetString("analysis-console-no-node"); + IDLabel.SetMarkup(idLabelMsg); + + IDValueLabel.Visible = node != null; + InfoContainer.Visible = node != null; + if (node == null || !_artifactAnalyzer.TryGetArtifactFromConsole(_owner, out var artifact)) + return; + + IDValueLabel.SetMarkup(Loc.GetString("analysis-console-info-id-value", + ("id", (_ent.GetComponentOrNull(node.Value)?.Identifier ?? 0).ToString("D3")))); + LockedValueLabel.SetMarkup(Loc.GetString("analysis-console-info-locked-value", + ("state", node.Value.Comp.Locked))); + ActiveValueLabel.SetMarkup(Loc.GetString("analysis-console-info-active-value", + ("state", _xenoArtifact.IsNodeActive(artifact.Value, node.Value)))); + + var percent = (float) node.Value.Comp.Durability / node.Value.Comp.MaxDurability; + var color = percent switch + { + >= 0.75f => Color.Lime, + >= 0.50f => Color.Yellow, + _ => Color.Red + }; + DurabilityValueLabel.SetMarkup(Loc.GetString("analysis-console-info-durability-value", + ("color", color), + ("current", node.Value.Comp.Durability), + ("max", node.Value.Comp.MaxDurability))); + + var hasInfo = _xenoArtifact.HasUnlockedPredecessor(artifact.Value, node.Value); + + EffectValueLabel.SetMarkup(Loc.GetString("analysis-console-info-effect-value", + ("state", hasInfo))); + + if (!hasInfo) + { + TriggerValueLabel.SetMarkup(Loc.GetString("analysis-console-info-effect-value", ("state", false))); + } + else + { + var triggerStr = new StringBuilder(); + triggerStr.Append("- "); + triggerStr.AppendLine(Loc.GetString(node.Value.Comp.TriggerTip)); + + foreach (var predecessor in _xenoArtifact.GetPredecessorNodes(artifact.Value.Owner, node.Value)) + { + triggerStr.Append("- "); + triggerStr.AppendLine(Loc.GetString(predecessor.Comp.TriggerTip)); + } + TriggerValueLabel.SetMarkup(Loc.GetString("analysis-console-info-triggered-value", ("triggers", triggerStr.ToString()))); + } } } diff --git a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs index 118d545ece47..c041d2ca877d 100644 --- a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs @@ -26,6 +26,8 @@ public sealed partial class XenoArtifactGraphControl : BoxContainer public event Action>? OnNodeSelected; + private static Color _lockedNodeColor = Color.FromHex("#777777"); + private float NodeRadius => 25 * UIScale; private float NodeDiameter => NodeRadius * 2; private float MinYSpacing => NodeDiameter * 0.75f; @@ -58,6 +60,7 @@ protected override void KeyBindDown(GUIBoundKeyEventArgs args) if (_hoveredNode == null) return; OnNodeSelected?.Invoke(_hoveredNode.Value); + UserInterfaceManager.ClickSound(); } protected override void Draw(DrawingHandleScreen handle) @@ -105,10 +108,10 @@ protected override void Draw(DrawingHandleScreen handle) var pos = GetNodePos(node); var hovered = (cursor - pos).LengthSquared() <= NodeRadius * NodeRadius; - var color = Color.FromHex("#888888"); + var color = _lockedNodeColor; if (artiSys.IsNodeActive(_artifact.Value, node)) { - color = Color.Red; + color = Color.Orange; } else if (!node.Comp.Locked) { @@ -135,8 +138,8 @@ protected override void Draw(DrawingHandleScreen handle) var successors = artiSys.GetDirectSuccessorNodes((_artifact.Value, _artifact.Value.Comp), node); foreach (var s in successors) { - var color = Color.FromHex("#888888"); - if (!node.Comp.Locked && !s.Comp.Locked) + var color = _lockedNodeColor; + if (!node.Comp.Locked) color = Color.White; var to = GetNodePos(s) + new Vector2(0, NodeRadius); diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 742b8baaf566..4fe6cdeedc51 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -147,7 +147,7 @@ private Entity CreateRandomNode(Entity [DataField, AutoNetworkedField] - public List TriggerTips = new(); + public LocId TriggerTip; /// /// The entity whose graph this node is a part of. @@ -46,6 +46,6 @@ public sealed partial class XenoArtifactNodeComponent : Component /// The maximum amount of times a node can be generically activated before becoming useless /// [DataField] - public int MaxDurability; + public int MaxDurability = 5; #endregion } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 90fe667e8df6..4bace4ab3e32 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -72,6 +72,14 @@ public void SetNodeDurability(Entity ent, int durabi Dirty(ent); } + public bool HasUnlockedPredecessor(Entity ent, EntityUid node) + { + var predecessors = GetDirectPredecessorNodes((ent, ent), node); + if (predecessors.Count != 0 && predecessors.All(p => p.Comp.Locked)) + return false; + return true; + } + public List>> GetSegments(Entity ent) { var output = new List>>(); diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs index 9f7774087203..374ab68cb721 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -32,8 +32,7 @@ public bool CanUnlockNode(Entity ent) if (!TryComp(artifact, out var artiComp)) return false; - var predecessors = GetDirectPredecessorNodes((artifact.Value, artiComp), ent); - if (predecessors.Count != 0 && predecessors.All(p => p.Comp.Locked)) + if (!HasUnlockedPredecessor((artifact.Value, artiComp), ent)) return false; return true; diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl index 2789e690f959..dde2e552a449 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl @@ -15,15 +15,28 @@ analysis-console-info-no-scanner = No analyzer connected! Please connect one usi analysis-console-info-no-artifact = No artifact present! Place one on the pad then scan for information. analysis-console-info-ready = Systems operational. Ready to scan. -analysis-console-info-id = NODE_ID: {$id} -analysis-console-info-depth = DEPTH: {$depth} -analysis-console-info-triggered-true = ACTIVATED: TRUE -analysis-console-info-triggered-false = ACTIVATED: FALSE -analysis-console-info-effect = REACTION: {$effect} -analysis-console-info-trigger = STIMULUS: {$trigger} -analysis-console-info-edges = EDGES: {$edges} -analysis-console-info-value = UNEXTRACTED_VALUE: {$value} - +analysis-console-no-node = [font="Monospace" size=11]Select node to view[/font] +analysis-console-info-id = [font="Monospace" size=11]Node ID:[/font] +analysis-console-info-id-value = [font="Monospace" size=11][color=yellow]{$id}[/color][/font] +analysis-console-info-locked = [font="Monospace" size=11]Status:[/font] +analysis-console-info-locked-value = [font="Monospace" size=11][color={ $state -> + [true] red]Locked + *[false] lime]Unlocked +}[/color][/font] +analysis-console-info-active = [font="Monospace" size=11]Activity:[/font] +analysis-console-info-active-value = [font="Monospace" size=11][color={ $state -> + [true] orange]Active + *[false] gray]Inactive +}[/color][/font] +analysis-console-info-durability = [font="Monospace" size=11]Durability:[/font] +analysis-console-info-durability-value = [font="Monospace" size=11][color={$color}]{$current}/{$max}[/color][/font] +analysis-console-info-effect = [font="Monospace" size=11]Effect:[/font] +analysis-console-info-effect-value = [font="Monospace" size=11][color=gray]{ $state -> + [true] WIP + *[false] Unlock nodes to gain info +}[/color][/font] +analysis-console-info-trigger = [font="Monospace" size=11]Triggers:[/font] +analysis-console-info-triggered-value = [font="Monospace" size=11][color=gray]{$triggers}[/color][/font] analysis-console-info-scanner = Scanning... analysis-console-info-scanner-paused = Paused. analysis-console-progress-text = {$seconds -> @@ -34,9 +47,4 @@ analysis-console-no-server-connected = Cannot extract. No server connected. analysis-console-no-artifact-placed = No artifact on scanner. analysis-console-no-points-to-extract = No points to extract. -analyzer-artifact-component-upgrade-analysis = analysis duration - -analysis-console-print-popup = The console printed out a report. analyzer-artifact-extract-popup = Energy shimmers on the artifact's surface! - -analysis-report-title = Artifact Report: Node {$id} From dd52fbe3d0a03c310c2d6452501f69101cb4d49d Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Sun, 30 Jun 2024 11:12:30 -0400 Subject: [PATCH 15/81] Add node unlocking --- .../Artifact/SharedXenoArtifactSystem.Node.cs | 21 +++++-- .../SharedXenoArtifactSystem.Unlock.cs | 55 ++++++++++++++++++- .../Artifact/SharedXenoArtifactSystem.XAT.cs | 3 + .../Artifact/SharedXenoArtifactSystem.cs | 4 ++ .../Artifact/XAT/BaseXATSystem.cs | 2 +- .../xenoarchaeology/artifact-component.ftl | 5 ++ 6 files changed, 82 insertions(+), 8 deletions(-) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 4bace4ab3e32..522f295f25aa 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -30,13 +30,24 @@ public void SetNodeUnlocked(Entity ent) if (!Resolve(ent, ref ent.Comp)) return; - if (!ent.Comp.Locked) + if (ent.Comp.Attached is not { } netArtifact) return; + var artifact = GetEntity(netArtifact); - ent.Comp.Locked = false; - if (ent.Comp.Attached is { } artifact) - RebuildCachedActiveNodes(GetEntity(artifact)); - Dirty(ent); + if (!TryComp(artifact, out var artifactComponent)) + return; + + SetNodeUnlocked((artifact, artifactComponent), (ent, ent.Comp)); + } + + public void SetNodeUnlocked(Entity artifact, Entity node) + { + if (!node.Comp.Locked) + return; + + node.Comp.Locked = false; + RebuildCachedActiveNodes((artifact, artifact)); + Dirty(node); } /// diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs index 374ab68cb721..28d742431e40 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Xenoarchaeology.Artifact.Components; @@ -7,6 +8,7 @@ public abstract partial class SharedXenoArtifactSystem { private EntityQuery _unlockingQuery; + // TODO: we should cancel the unlock state if you remove/add nodes private void InitializeUnlock() { _unlockingQuery = GetEntityQuery(); @@ -15,11 +17,12 @@ private void InitializeUnlock() private void UpdateUnlock(float frameTime) { var query = EntityQueryEnumerator(); - while (query.MoveNext(out var unlock, out var comp)) + while (query.MoveNext(out var uid, out var unlock, out var comp)) { if (_timing.CurTime < unlock.EndTime) continue; - //todo: trigger unlock + + FinishUnlockingState((uid, unlock, comp)); } } @@ -37,4 +40,52 @@ public bool CanUnlockNode(Entity ent) return true; } + + public void FinishUnlockingState(Entity ent) + { + string unlockMsg; + if (TryGetNodeFromUnlockState(ent, out var node)) + { + // TODO: animation + SetNodeUnlocked((ent, ent.Comp2), node.Value); + unlockMsg = "artifact-unlock-state-end-success"; + } + else + { + unlockMsg = "artifact-unlock-state-end-failure"; + } + + if (_net.IsServer) + _popup.PopupEntity(Loc.GetString(unlockMsg), ent); + + RemComp(ent, ent.Comp1); + ent.Comp2.NextUnlockTime = _timing.CurTime + ent.Comp2.UnlockStateRefractory; + } + + public bool TryGetNodeFromUnlockState( + Entity ent, + [NotNullWhen(true)] out Entity? node) + { + node = null; + + foreach (var nodeIndex in ent.Comp1.TriggeredNodeIndexes) + { + var curNode = GetNode((ent, ent.Comp2), nodeIndex); + if (!curNode.Comp.Locked || !CanUnlockNode((curNode, curNode))) + continue; + + var neededIndices = GetPredecessorNodes((ent, ent.Comp2), nodeIndex); + neededIndices.Add(nodeIndex); + + // Make sure the two sets are identical + if (neededIndices.Count != ent.Comp1.TriggeredNodeIndexes.Count || + !ent.Comp1.TriggeredNodeIndexes.All(neededIndices.Contains)) + continue; + + node = curNode; + return true; + } + + return node != null; + } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs index 91f973287c4f..3fe9f5d605ae 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs @@ -40,6 +40,9 @@ public void TriggerXenoArtifact(Entity ent, Entity(ent); unlockingComp.EndTime = _timing.CurTime + ent.Comp.UnlockStateDuration; Log.Debug($"{ToPrettyString(ent)} entered unlocking state"); + + if (_net.IsServer) + _popup.PopupEntity(Loc.GetString("artifact-unlock-state-begin"), ent); } var index = GetIndex(ent, node); diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs index 745df51d4c1b..647271ba0d7a 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs @@ -1,5 +1,7 @@ +using Content.Shared.Popups; 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; @@ -12,9 +14,11 @@ namespace Content.Shared.Xenoarchaeology.Artifact; public abstract partial class SharedXenoArtifactSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly INetManager _net = default!; [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] protected readonly IRobustRandom RobustRandom = default!; [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; /// public override void Initialize() diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs index 4e7baebfa6a9..9730e7058d32 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs @@ -79,7 +79,7 @@ protected virtual void UpdateXAT(Entity artifact, Entity< protected void Trigger(Entity artifact, Entity node) { - Log.Debug($"Activated trigger {nameof(T)} on node {ToPrettyString(node)} for {ToPrettyString(artifact)}"); + Log.Debug($"Activated trigger {typeof(T).Name} on node {ToPrettyString(node)} for {ToPrettyString(artifact)}"); XenoArtifact.TriggerXenoArtifact(artifact, (node.Owner, node.Comp2)); } } diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl index d40c14da8ce9..27844a5766e5 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl @@ -7,5 +7,10 @@ cmd-unlocknode-help = unlocknode artifact-verb-make-always-active = Make artifact always active artifact-verb-activate = Activate artifact +### Unlocking +artifact-unlock-state-begin = It begins to shift in strange ways... +artifact-unlock-state-end-success = It slows down, visibly changed. +artifact-unlock-state-end-failure = It slows down before uneventfully stopping. + ### Misc. artifact-examine-trigger-desc = [color=gray][italic]Am I on your mind?[/italic][/color] From 3c01fdbd668d3dafc06d87fa86ae2317b370f399 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Fri, 5 Jul 2024 10:13:40 -0400 Subject: [PATCH 16/81] more trigger cuz thats pretty cool --- .../Artifact/XenoArtifactSystem.ProcGen.cs | 18 +++++-- .../Artifact/XenoArtifactSystem.cs | 2 + .../Components/XenoArtifactComponent.cs | 3 +- .../XenoArchTriggerPrototype.cs | 6 ++- .../Artifact/SharedXenoArtifactSystem.XAT.cs | 14 +++-- .../Artifact/XAT/BaseXATSystem.cs | 2 +- .../Components/XATExaminableTextComponent.cs | 13 +++++ .../XAT/Components/XATInteractionComponent.cs | 9 ++++ .../XAT/Components/XATToolUseComponent.cs | 39 ++++++++++++++ .../Artifact/XAT/XATDamageSystem.cs | 5 +- .../Artifact/XAT/XATExaminableTextSystem.cs | 27 ++++++++++ .../Artifact/XAT/XATInteractionSystem.cs | 39 ++++++++++++++ .../Artifact/XAT/XATToolUseSystem.cs | 51 ++++++++++++++++++ .../en-US/xenoarchaeology/artifact-hints.ftl | 14 ++++- Resources/Prototypes/XenoArch/triggers.yml | 52 ++++++++++++++++++- 15 files changed, 278 insertions(+), 16 deletions(-) rename Content.Shared/Xenoarchaeology/Artifact/{XAT => Prototypes}/XenoArchTriggerPrototype.cs (84%) create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATExaminableTextComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATInteractionComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATToolUseComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XATExaminableTextSystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XATInteractionSystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XATToolUseSystem.cs diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 4fe6cdeedc51..5f0c4430fd90 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -1,6 +1,6 @@ using Content.Shared.Random.Helpers; using Content.Shared.Xenoarchaeology.Artifact.Components; -using Content.Shared.Xenoarchaeology.Artifact.XAT; +using Content.Shared.Xenoarchaeology.Artifact.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -13,8 +13,8 @@ public sealed partial class XenoArtifactSystem private void GenerateArtifactStructure(Entity ent) { var nodeCount = ent.Comp.NodeCount.Next(RobustRandom); + CreateTriggerPool(ent, ref nodeCount); ResizeNodeGraph(ent, nodeCount); - CreateTriggerPool(ent, nodeCount); while (nodeCount > 0) { GenerateArtifactSegment(ent, ref nodeCount); @@ -23,7 +23,7 @@ private void GenerateArtifactStructure(Entity ent) RebuildNodeData((ent, ent)); } - private void CreateTriggerPool(Entity ent, int size) + private void CreateTriggerPool(Entity ent, ref int size) { _triggerPool.Clear(); var weightsProto = PrototypeManager.Index(ent.Comp.TriggerWeights); @@ -31,9 +31,18 @@ private void CreateTriggerPool(Entity ent, int size) while (_triggerPool.Count < size) { - var triggerId = RobustRandom.Pick(weights); + // OOPS! We ran out of triggers. + if (weights.Count == 0) + { + Log.Error($"Insufficient triggers for generating {ToPrettyString(ent)}! Needed {size} but had {_triggerPool.Count}"); + size = _triggerPool.Count; + return; + } + var triggerId = RobustRandom.Pick(weights); var trigger = PrototypeManager.Index(triggerId); + if (_entityWhitelist.IsWhitelistFail(trigger.Whitelist, ent)) + continue; _triggerPool.Add(trigger); weights.Remove(triggerId); @@ -44,7 +53,6 @@ private void GenerateArtifactSegment(Entity ent, ref int { var segmentSize = GetArtifactSegmentSize(ent, nodeCount); nodeCount -= segmentSize; - // TODO: generate a pool of triggers here based on segmentsize PopulateArtifactSegmentRecursive(ent, ref segmentSize, ensureLayerConnected: true); } diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs index c872a74d8520..232d16114c39 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Administration; using Content.Shared.Administration; +using Content.Shared.Whitelist; using Content.Shared.Xenoarchaeology.Artifact; using Content.Shared.Xenoarchaeology.Artifact.Components; using Robust.Shared.Console; @@ -9,6 +10,7 @@ namespace Content.Server.Xenoarchaeology.Artifact; public sealed partial class XenoArtifactSystem : SharedXenoArtifactSystem { [Dependency] private readonly IConsoleHost _consoleHost = default!; + [Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!; /// public override void Initialize() diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index 35022e2e3569..d715fae2d416 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Destructible.Thresholds; using Content.Shared.Random; +using Content.Shared.Xenoarchaeology.Artifact.Prototypes; using Content.Shared.Xenoarchaeology.Artifact.XAT; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -77,7 +78,7 @@ public sealed partial class XenoArtifactComponent : Component /// The total number of nodes that make up this artifact. /// [DataField] - public MinMax NodeCount = new(10, 12);// TODO: add back in. 24); + public MinMax NodeCount = new(10, 16); /// /// The amount of nodes that go in each segment. diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs b/Content.Shared/Xenoarchaeology/Artifact/Prototypes/XenoArchTriggerPrototype.cs similarity index 84% rename from Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs rename to Content.Shared/Xenoarchaeology/Artifact/Prototypes/XenoArchTriggerPrototype.cs index 42f30d8126e5..fe1b83878a3f 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XenoArchTriggerPrototype.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Prototypes/XenoArchTriggerPrototype.cs @@ -1,8 +1,9 @@ using Content.Shared.Random; +using Content.Shared.Whitelist; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -namespace Content.Shared.Xenoarchaeology.Artifact.XAT; +namespace Content.Shared.Xenoarchaeology.Artifact.Prototypes; [Prototype] public sealed partial class XenoArchTriggerPrototype : IPrototype @@ -14,6 +15,9 @@ public sealed partial class XenoArchTriggerPrototype : IPrototype [DataField] public LocId Tip; + [DataField] + public EntityWhitelist? Whitelist; + [DataField] public ComponentRegistry Components = new(); } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs index 3fe9f5d605ae..6415a6b6587c 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs @@ -1,17 +1,25 @@ using Content.Shared.Damage; using Content.Shared.Examine; using Content.Shared.Interaction; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Weapons.Melee.Events; using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; namespace Content.Shared.Xenoarchaeology.Artifact; public abstract partial class SharedXenoArtifactSystem { + // todo this is kinda a misnomer since it handles generic relays. private void InitializeXAT() { XATRelayLocalEvent(); XATRelayLocalEvent(); XATRelayLocalEvent(); + XATRelayLocalEvent(); + XATRelayLocalEvent(); + XATRelayLocalEvent(); + XATRelayLocalEvent(); } protected void XATRelayLocalEvent() where T : notnull @@ -21,7 +29,7 @@ protected void XATRelayLocalEvent() where T : notnull protected void RelayEventToNodes(Entity ent, ref T args) where T : notnull { - var ev = new XATRelayedEvent(ent, args); + var ev = new XenoArchNodeRelayedEvent(ent, args); var nodes = GetAllNodes(ent); foreach (var node in nodes) @@ -57,13 +65,13 @@ public void TriggerXenoArtifact(Entity ent, Entity [ByRefEvent] -public sealed class XATRelayedEvent : EntityEventArgs +public sealed class XenoArchNodeRelayedEvent : EntityEventArgs { public Entity Artifact; public TEvent Args; - public XATRelayedEvent(Entity artifact, TEvent args) + public XenoArchNodeRelayedEvent(Entity artifact, TEvent args) { Artifact = artifact; Args = args; diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs index 9730e7058d32..cf0cea7fb62f 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs @@ -20,7 +20,7 @@ public override void Initialize() protected void XATSubscribeLocalEvent(XATEventHandler eventHandler) where TEvent : notnull { - SubscribeLocalEvent>((uid, component, args) => + SubscribeLocalEvent>((uid, component, args) => { var nodeComp = Comp(uid); diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATExaminableTextComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATExaminableTextComponent.cs new file mode 100644 index 000000000000..92def3776741 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATExaminableTextComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for an artifact node that puts examine text on the artifact itself. Useful for flavor +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedXenoArtifactSystem)), AutoGenerateComponentState] +public sealed partial class XATExaminableTextComponent : Component +{ + [DataField(required: true), AutoNetworkedField] + public LocId ExamineText; +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATInteractionComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATInteractionComponent.cs new file mode 100644 index 000000000000..988738fbb5c0 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATInteractionComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for a xenoarch trigger that activates after any type of physical interaction. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(XATInteractionSystem))] +public sealed partial class XATInteractionComponent : Component; diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATToolUseComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATToolUseComponent.cs new file mode 100644 index 000000000000..e28eca1f185c --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATToolUseComponent.cs @@ -0,0 +1,39 @@ +using Content.Shared.DoAfter; +using Content.Shared.Tools; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for a xenoarch trigger that is activated by a tool being used on it. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(XATToolUseSystem)), AutoGenerateComponentState] +public sealed partial class XATToolUseComponent : Component +{ + [DataField, AutoNetworkedField] + public ProtoId NeededTool; + + [DataField, AutoNetworkedField] + public float Delay = 3; + + [DataField, AutoNetworkedField] + public float Fuel; +} + +[Serializable, NetSerializable] +public sealed partial class XATToolUseDoAfterEvent : DoAfterEvent +{ + public NetEntity Node; + + public XATToolUseDoAfterEvent(NetEntity node) + { + Node = node; + } + + public override DoAfterEvent Clone() + { + return this; + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs index 3bd9a61885a3..394d510ee7da 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs @@ -2,12 +2,14 @@ using Content.Shared.Xenoarchaeology.Artifact.Components; using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; using Robust.Shared.Prototypes; +using Robust.Shared.Timing; namespace Content.Shared.Xenoarchaeology.Artifact.XAT; public sealed class XATDamageSystem : BaseXATSystem { [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IGameTiming _timing = default!; /// public override void Initialize() @@ -22,7 +24,8 @@ private void OnDamageChanged(Entity artifact, Entity +/// This isn't an actual trigger but this framework is so fucking nice +/// +public sealed class XATExaminableTextSystem : BaseXATSystem +{ + /// + public override void Initialize() + { + base.Initialize(); + + XATSubscribeLocalEvent(OnExamined); + } + + private void OnExamined(Entity artifact, Entity node, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + args.PushMarkup(Loc.GetString(node.Comp1.ExamineText)); + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATInteractionSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATInteractionSystem.cs new file mode 100644 index 000000000000..8ede143bfdf7 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATInteractionSystem.cs @@ -0,0 +1,39 @@ +using Content.Shared.Interaction; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Weapons.Melee.Events; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT; + +public sealed class XATInteractionSystem : BaseXATSystem +{ + /// + public override void Initialize() + { + base.Initialize(); + + XATSubscribeLocalEvent(OnPullStart); + XATSubscribeLocalEvent(OnAttacked); + XATSubscribeLocalEvent(OnInteractHand); + } + + private void OnPullStart(Entity artifact, Entity node, ref PullStartedMessage args) + { + Trigger(artifact, node); + } + + private void OnAttacked(Entity artifact, Entity node, ref AttackedEvent args) + { + Trigger(artifact, node); + } + + private void OnInteractHand(Entity artifact, Entity node, ref InteractHandEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + Trigger(artifact, node); + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATToolUseSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATToolUseSystem.cs new file mode 100644 index 000000000000..2a2708080ac5 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATToolUseSystem.cs @@ -0,0 +1,51 @@ +using Content.Shared.Interaction; +using Content.Shared.Tools.Components; +using Content.Shared.Tools.Systems; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT; + +/// +/// This handles +/// +public sealed class XATToolUseSystem : BaseXATSystem +{ + [Dependency] private readonly SharedToolSystem _tool = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + XATSubscribeLocalEvent(OnInteractUsing); + XATSubscribeLocalEvent(OnToolUseComplete); + } + + private void OnToolUseComplete(Entity artifact, Entity node, ref XATToolUseDoAfterEvent args) + { + if (args.Cancelled) + return; + + if (GetEntity(args.Node) != node.Owner) + return; + + Trigger(artifact, node); + args.Handled = true; + } + + private void OnInteractUsing(Entity artifact, Entity node, ref InteractUsingEvent args) + { + if (!TryComp(args.Used, out var tool)) + return; + + args.Handled = _tool.UseTool(args.Used, + args.User, + artifact, + node.Comp1.Delay, + node.Comp1.NeededTool, + new XATToolUseDoAfterEvent(GetNetEntity(node)), + fuel: node.Comp1.Fuel, + tool); + } +} diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl index 9803590e580c..57a96e9c6a39 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl @@ -53,6 +53,16 @@ xenoarch-trigger-tip-ammonia = Ammonia xenoarch-trigger-tip-n2o = Nitrous oxide xenoarch-trigger-tip-frezon = Frezon xenoarch-trigger-tip-radiation = Radiation -xenoarch-trigger-tip-pressure-low = Low environmental pressure -xenoarch-trigger-tip-pressure-high = High environmental pressure +xenoarch-trigger-tip-brute-damage = Physical damage +xenoarch-trigger-tip-interaction = Phyiscal interaction +xenoarch-trigger-tip-wrenching = Tightening +xenoarch-trigger-tip-prying = Prying +xenoarch-trigger-tip-screwing = Screwing +xenoarch-trigger-tip-pressure-low = Low pressure +xenoarch-trigger-tip-pressure-high = High pressure xenoarch-trigger-tip-examine = Close inspection + +### Description hints +xenoarch-trigger-examine-wrenching = [italic]There's a loose bit spinning around.[/italic] +xenoarch-trigger-examine-prying = [italic]There's a panel coming up from the surface.[/italic] +xenoarch-trigger-examine-screwing = [italic]There's a raised section with a small inset on it.[/italic] diff --git a/Resources/Prototypes/XenoArch/triggers.yml b/Resources/Prototypes/XenoArch/triggers.yml index 3e5c700329f9..9900477ae969 100644 --- a/Resources/Prototypes/XenoArch/triggers.yml +++ b/Resources/Prototypes/XenoArch/triggers.yml @@ -16,6 +16,11 @@ TriggerPressureHigh: 0.5 TriggerPressureLow: 0.5 TriggerExamine: 1 + TriggerBruteDamage: 1 + TriggerInteraction: 1 + TriggerWrenching: 0.33 + TriggerPrying: 0.33 + TriggerScrewing: 0.33 - type: xenoArchTrigger id: TriggerMusic @@ -31,10 +36,13 @@ - type: XATTemperature targetTemperature: 373 triggerOnHigherTemp: true - # TODO: add interaction for welders, etc. - type: XATDamage typesNeeded: Heat: 50 + - type: XATToolUse + neededTool: Welding + delay: 5 + fuel: 10 - type: xenoArchTrigger id: TriggerCold @@ -115,7 +123,6 @@ typesNeeded: Radiation: 100 # TODO: legendary microwave trigger - # TODO: add rad receiver to artifact - type: xenoArchTrigger id: TriggerPressureHigh @@ -136,3 +143,44 @@ tip: xenoarch-trigger-tip-examine components: - type: XATExamine + +- type: xenoArchTrigger + id: TriggerBruteDamage + tip: xenoarch-trigger-tip-brute-damage + components: + - type: XATDamage + groupsNeeded: + Brute: 150 + +- type: xenoArchTrigger + id: TriggerInteraction + tip: xenoarch-trigger-tip-interaction + components: + - type: XATInteraction + +- type: xenoArchTrigger + id: TriggerWrenching + tip: xenoarch-trigger-tip-wrenching + components: + - type: XATToolUse + neededTool: Anchoring + - type: XATExaminableText + examineText: xenoarch-trigger-examine-wrenching + +- type: xenoArchTrigger + id: TriggerPrying + tip: xenoarch-trigger-tip-prying + components: + - type: XATToolUse + neededTool: Prying + - type: XATExaminableText + examineText: xenoarch-trigger-examine-prying + +- type: xenoArchTrigger + id: TriggerScrewing + tip: xenoarch-trigger-tip-screwing + components: + - type: XATToolUse + neededTool: Screwing + - type: XATExaminableText + examineText: xenoarch-trigger-examine-screwing From 34a703c15745914df0ade1cdc2d8ea28dbd1d9fd Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Fri, 5 Jul 2024 20:04:39 -0400 Subject: [PATCH 17/81] final triggers + incorporate gnostic faith --- .../Ui/AnalysisConsoleMenu.xaml | 4 + .../Ui/AnalysisConsoleMenu.xaml.cs | 7 +- .../XAT/Components/XATMagnetComponent.cs | 20 ++++ .../Artifact/XAT/XATMagnetSystem.cs | 59 ++++++++++ .../Artifact/XenoArtifactSystem.ProcGen.cs | 4 +- .../Systems/ArtifactMagnetTriggerSystem.cs | 79 -------------- Content.Shared/Chemistry/ReactiveSystem.cs | 17 +++ .../Artifact/SharedXenoArtifactSystem.XAT.cs | 16 ++- .../Artifact/XAT/BaseXATSystem.cs | 13 +-- .../XAT/Components/XATDeathComponent.cs | 13 +++ .../XAT/Components/XATItemLandComponent.cs | 9 ++ .../XAT/Components/XATReactiveComponent.cs | 23 ++++ .../XAT/Components/XATTimerComponent.cs | 16 +++ .../Artifact/XAT/XATDamageSystem.cs | 6 +- .../Artifact/XAT/XATDeathSystem.cs | 41 +++++++ .../Artifact/XAT/XATExaminableTextSystem.cs | 2 +- .../Artifact/XAT/XATExamineSystem.cs | 2 +- .../Artifact/XAT/XATInteractionSystem.cs | 6 +- .../Artifact/XAT/XATItemLandSystem.cs | 20 ++++ .../Artifact/XAT/XATReactiveSystem.cs | 30 ++++++ .../Artifact/XAT/XATTimerSystem.cs | 54 ++++++++++ .../Artifact/XAT/XATToolUseSystem.cs | 4 +- .../xenoarchaeology/artifact-analyzer.ftl | 4 +- .../xenoarchaeology/artifact-component.ftl | 7 ++ .../en-US/xenoarchaeology/artifact-hints.ftl | 16 ++- .../Xenoarchaeology/xenoartifacts.yml | 9 +- Resources/Prototypes/XenoArch/triggers.yml | 101 +++++++++++++++--- 27 files changed, 460 insertions(+), 122 deletions(-) create mode 100644 Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATMagnetComponent.cs create mode 100644 Content.Server/Xenoarchaeology/Artifact/XAT/XATMagnetSystem.cs delete mode 100644 Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMagnetTriggerSystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDeathComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATItemLandComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATReactiveComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATTimerComponent.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XATDeathSystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XATItemLandSystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XATReactiveSystem.cs create mode 100644 Content.Shared/Xenoarchaeology/Artifact/XAT/XATTimerSystem.cs diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml index 736fc0729667..bcb364f1736e 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml @@ -25,6 +25,10 @@ + + + + diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs index a6c74f0ffd19..dfd2a24f4099 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs @@ -45,6 +45,7 @@ public AnalysisConsoleMenu(EntityUid owner) if (BackPanel.PanelOverride is StyleBoxTexture tex) tex.Texture = _resCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png"); + ClassLabel.SetMarkup(Loc.GetString("analysis-console-info-class")); LockedLabel.SetMarkup(Loc.GetString("analysis-console-info-locked")); ActiveLabel.SetMarkup(Loc.GetString("analysis-console-info-active")); EffectLabel.SetMarkup(Loc.GetString("analysis-console-info-effect")); @@ -111,6 +112,7 @@ public void SetSelectedNode(Entity? node) EffectValueLabel.SetMarkup(Loc.GetString("analysis-console-info-effect-value", ("state", hasInfo))); + var predecessorNodes = _xenoArtifact.GetPredecessorNodes(artifact.Value.Owner, node.Value); if (!hasInfo) { TriggerValueLabel.SetMarkup(Loc.GetString("analysis-console-info-effect-value", ("state", false))); @@ -121,13 +123,16 @@ public void SetSelectedNode(Entity? node) triggerStr.Append("- "); triggerStr.AppendLine(Loc.GetString(node.Value.Comp.TriggerTip)); - foreach (var predecessor in _xenoArtifact.GetPredecessorNodes(artifact.Value.Owner, node.Value)) + foreach (var predecessor in predecessorNodes) { triggerStr.Append("- "); triggerStr.AppendLine(Loc.GetString(predecessor.Comp.TriggerTip)); } TriggerValueLabel.SetMarkup(Loc.GetString("analysis-console-info-triggered-value", ("triggers", triggerStr.ToString()))); } + + ClassValueLabel.SetMarkup(Loc.GetString("analysis-console-info-class-value", + ("class", Loc.GetString($"artifact-node-class-{Math.Min(6, predecessorNodes.Count + 1)}")))); } } diff --git a/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATMagnetComponent.cs b/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATMagnetComponent.cs new file mode 100644 index 000000000000..9cf4e13f1230 --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XAT/Components/XATMagnetComponent.cs @@ -0,0 +1,20 @@ +using Robust.Shared.GameStates; + +namespace Content.Server.Xenoarchaeology.Artifact.XAT.Components; + +[RegisterComponent, Access(typeof(XATMagnetSystem))] +public sealed partial class XATMagnetComponent : Component +{ + /// + /// how close to the magnet do you have to be? + /// + [DataField] + public float MagnetRange = 40f; + + /// + /// How close do active magboots have to be? + /// This is smaller because they are weaker magnets + /// + [DataField] + public float MagbootsRange = 2f; +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XAT/XATMagnetSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XAT/XATMagnetSystem.cs new file mode 100644 index 000000000000..e13af978668c --- /dev/null +++ b/Content.Server/Xenoarchaeology/Artifact/XAT/XATMagnetSystem.cs @@ -0,0 +1,59 @@ +using Content.Server.Salvage; +using Content.Server.Xenoarchaeology.Artifact.XAT.Components; +using Content.Shared.Clothing; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT; + +namespace Content.Server.Xenoarchaeology.Artifact.XAT; + +public sealed class XATMagnetSystem : BaseXATSystem +{ + [Dependency] private readonly SharedTransformSystem _transform = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMagnetActivated); + } + + private void OnMagnetActivated(ref SalvageMagnetActivatedEvent args) + { + var coords1 = Transform(args.Magnet).Coordinates; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var node)) + { + if (node.Attached == null) + continue; + + var artifact = _xenoArtifactQuery.Get(GetEntity(node.Attached.Value)); + + if (!CanTrigger(artifact, (uid, node))) + continue; + + var coords2 = Transform(artifact).Coordinates; + if (_transform.InRange(coords1, coords2, comp.MagnetRange)) + Trigger(artifact, (uid, comp, node)); + } + } + + protected override void UpdateXAT(Entity artifact, Entity node, float frameTime) + { + base.UpdateXAT(artifact, node, frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out var magboots, out var xform)) + { + if (!magboots.On) + continue; + + if (!_transform.InRange(xform.Coordinates, Transform(artifact).Coordinates, node.Comp1.MagnetRange)) + continue; + + Trigger(artifact, node); + break; + } + } +} diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 5f0c4430fd90..b97ac2c8b1bf 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -40,12 +40,12 @@ private void CreateTriggerPool(Entity ent, ref int size) } var triggerId = RobustRandom.Pick(weights); + weights.Remove(triggerId); var trigger = PrototypeManager.Index(triggerId); if (_entityWhitelist.IsWhitelistFail(trigger.Whitelist, ent)) continue; _triggerPool.Add(trigger); - weights.Remove(triggerId); } } @@ -158,7 +158,7 @@ private Entity CreateRandomNode(Entity -/// This handles artifacts that are activated by magnets, both salvage and magboots. -/// -public sealed class ArtifactMagnetTriggerSystem : EntitySystem -{ - [Dependency] private readonly ArtifactSystem _artifact = default!; - - private readonly List _toActivate = new(); - - /// - public override void Initialize() - { - SubscribeLocalEvent(OnMagnetActivated); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - if (!EntityQuery().Any()) - return; - - _toActivate.Clear(); - - //assume that there's more instruments than artifacts - var query = EntityQueryEnumerator(); - while (query.MoveNext(out _, out var magboot, out var magXform)) - { - if (!magboot.On) - continue; - - var artiQuery = EntityQueryEnumerator(); - while (artiQuery.MoveNext(out var artifactUid, out var trigger, out var xform)) - { - if (!magXform.Coordinates.TryDistance(EntityManager, xform.Coordinates, out var distance)) - continue; - - if (distance > trigger.MagbootRange) - continue; - - _toActivate.Add(artifactUid); - } - } - - foreach (var a in _toActivate) - { - _artifact.TryActivateArtifact(a); - } - } - - private void OnMagnetActivated(ref SalvageMagnetActivatedEvent ev) - { - var magXform = Transform(ev.Magnet); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var artifact, out var xform)) - { - if (!magXform.Coordinates.TryDistance(EntityManager, xform.Coordinates, out var distance)) - continue; - - if (distance > artifact.Range) - continue; - - _toActivate.Add(uid); - } - - foreach (var a in _toActivate) - { - _artifact.TryActivateArtifact(a); - } - } -} diff --git a/Content.Shared/Chemistry/ReactiveSystem.cs b/Content.Shared/Chemistry/ReactiveSystem.cs index edd75bb0b4c5..9c3f75bf6413 100644 --- a/Content.Shared/Chemistry/ReactiveSystem.cs +++ b/Content.Shared/Chemistry/ReactiveSystem.cs @@ -38,6 +38,10 @@ public void ReactionEntity(EntityUid uid, ReactionMethod method, ReagentPrototyp if (!TryComp(uid, out ReactiveComponent? reactive)) return; + // custom event for bypassing reactivecomponent stuff + var ev = new ReactionEntityEvent(method, proto, reagentQuantity, source); + RaiseLocalEvent(uid, ref ev); + // If we have a source solution, use the reagent quantity we have left. Otherwise, use the reaction volume specified. var args = new EntityEffectReagentArgs(uid, EntityManager, null, source, source?.GetReagentQuantity(reagentQuantity.Reagent) ?? reagentQuantity.Quantity, proto, method, 1f); @@ -107,3 +111,16 @@ public enum ReactionMethod Injection, Ingestion, } + +[ByRefEvent] +public readonly record struct ReactionEntityEvent( + ReactionMethod Method, + ReagentPrototype Reagent, + ReagentQuantity ReagentQuantity, + Solution? Source) +{ + public readonly ReactionMethod Method = Method; + public readonly ReagentPrototype Reagent = Reagent; + public readonly ReagentQuantity ReagentQuantity = ReagentQuantity; + public readonly Solution? Source = Source; +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs index 6415a6b6587c..61c7f5f01fa2 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs @@ -1,3 +1,4 @@ +using Content.Shared.Chemistry; using Content.Shared.Damage; using Content.Shared.Examine; using Content.Shared.Interaction; @@ -13,13 +14,18 @@ public abstract partial class SharedXenoArtifactSystem // todo this is kinda a misnomer since it handles generic relays. private void InitializeXAT() { + base.Initialize(); + XATRelayLocalEvent(); - XATRelayLocalEvent(); XATRelayLocalEvent(); XATRelayLocalEvent(); XATRelayLocalEvent(); XATRelayLocalEvent(); XATRelayLocalEvent(); + XATRelayLocalEvent(); + + // special case this one because we need to order the messages + SubscribeLocalEvent(OnExamined); } protected void XATRelayLocalEvent() where T : notnull @@ -27,6 +33,14 @@ protected void XATRelayLocalEvent() where T : notnull SubscribeLocalEvent(RelayEventToNodes); } + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + //using (args.PushGroup(nameof(XenoArtifactComponent))) + //{ + RelayEventToNodes(ent, ref args); + //} + } + protected void RelayEventToNodes(Entity ent, ref T args) where T : notnull { var ev = new XenoArchNodeRelayedEvent(ent, args); diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs index cf0cea7fb62f..122bf86e24b2 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs @@ -5,10 +5,10 @@ namespace Content.Shared.Xenoarchaeology.Artifact.XAT; public abstract class BaseXATSystem : EntitySystem where T : Component { - [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] protected readonly SharedXenoArtifactSystem XenoArtifact = default!; - private EntityQuery _xenoArtifactQuery; + protected EntityQuery _xenoArtifactQuery; private EntityQuery _unlockingQuery; /// @@ -18,7 +18,7 @@ public override void Initialize() _unlockingQuery = GetEntityQuery(); } - protected void XATSubscribeLocalEvent(XATEventHandler eventHandler) where TEvent : notnull + protected void XATSubscribeDirectEvent(XATEventHandler eventHandler) where TEvent : notnull { SubscribeLocalEvent>((uid, component, args) => { @@ -57,9 +57,9 @@ public override void Update(float frameTime) } } - private bool CanTrigger(Entity artifact, Entity node) + protected bool CanTrigger(Entity artifact, Entity node) { - if (_timing.CurTime < artifact.Comp.NextUnlockTime) + if (Timing.CurTime < artifact.Comp.NextUnlockTime) return false; if (_unlockingQuery.TryComp(artifact, out var unlocking) && @@ -79,7 +79,8 @@ protected virtual void UpdateXAT(Entity artifact, Entity< protected void Trigger(Entity artifact, Entity node) { - Log.Debug($"Activated trigger {typeof(T).Name} on node {ToPrettyString(node)} for {ToPrettyString(artifact)}"); + if (Timing.IsFirstTimePredicted) + Log.Debug($"Activated trigger {typeof(T).Name} on node {ToPrettyString(node)} for {ToPrettyString(artifact)}"); XenoArtifact.TriggerXenoArtifact(artifact, (node.Owner, node.Comp2)); } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDeathComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDeathComponent.cs new file mode 100644 index 000000000000..f7cf0532cbf4 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDeathComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for a xenoarch trigger that activates when something dies nearby. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(XATDeathSystem)), AutoGenerateComponentState] +public sealed partial class XATDeathComponent : Component +{ + [DataField, AutoNetworkedField] + public float Range = 15; +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATItemLandComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATItemLandComponent.cs new file mode 100644 index 000000000000..153e532288d4 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATItemLandComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for an artifact trigger that activates when a thrown item lands on the ground. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(XATItemLandSystem))] +public sealed partial class XATItemLandComponent : Component; diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATReactiveComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATReactiveComponent.cs new file mode 100644 index 000000000000..ba7f8798c1f3 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATReactiveComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for a xenoarch trigger that activates when a reaction occurs on the artifact. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(XATReactiveSystem)), AutoGenerateComponentState] +public sealed partial class XATReactiveComponent : Component +{ + [DataField, AutoNetworkedField] + public List ReactionMethods = new() { ReactionMethod.Touch }; + + [DataField, AutoNetworkedField] + public HashSet> Reagents = new(); + + [DataField, AutoNetworkedField] + public FixedPoint2 MinQuantity = 5f; +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATTimerComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATTimerComponent.cs new file mode 100644 index 000000000000..10bd5109489a --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATTimerComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +/// +/// This is used for a xenoarch trigger that self-activates at a regular interval +/// +[RegisterComponent, NetworkedComponent, Access(typeof(XATTimerSystem)), AutoGenerateComponentState, AutoGenerateComponentPause] +public sealed partial class XATTimerComponent : Component +{ + [DataField, AutoNetworkedField, AutoPausedField] + public TimeSpan NextActivation; + + [DataField, AutoNetworkedField] + public TimeSpan Delay; +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs index 394d510ee7da..1087525221f1 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs @@ -2,21 +2,19 @@ using Content.Shared.Xenoarchaeology.Artifact.Components; using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; using Robust.Shared.Prototypes; -using Robust.Shared.Timing; namespace Content.Shared.Xenoarchaeology.Artifact.XAT; public sealed class XATDamageSystem : BaseXATSystem { [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IGameTiming _timing = default!; /// public override void Initialize() { base.Initialize(); - XATSubscribeLocalEvent(OnDamageChanged); + XATSubscribeDirectEvent(OnDamageChanged); } private void OnDamageChanged(Entity artifact, Entity node, ref DamageChangedEvent args) @@ -24,7 +22,7 @@ private void OnDamageChanged(Entity artifact, Entity +{ + [Dependency] private readonly SharedTransformSystem _transform = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMobStateChanged); + } + + private void OnMobStateChanged(MobStateChangedEvent args) + { + if (args.NewMobState != MobState.Dead) + return; + var coords1 = Transform(args.Target).Coordinates; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var node)) + { + if (node.Attached == null) + continue; + + var artifact = _xenoArtifactQuery.Get(GetEntity(node.Attached.Value)); + + if (!CanTrigger(artifact, (uid, node))) + continue; + + var coords2 = Transform(artifact).Coordinates; + if (_transform.InRange(coords1, coords2, comp.Range)) + Trigger(artifact, (uid, comp, node)); + } + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExaminableTextSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExaminableTextSystem.cs index a0121ee55300..e558c0f0094d 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExaminableTextSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExaminableTextSystem.cs @@ -14,7 +14,7 @@ public override void Initialize() { base.Initialize(); - XATSubscribeLocalEvent(OnExamined); + XATSubscribeDirectEvent(OnExamined); } private void OnExamined(Entity artifact, Entity node, ref ExaminedEvent args) diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs index 23f0d0e29874..2e1c7d54df6e 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs @@ -12,7 +12,7 @@ public override void Initialize() { base.Initialize(); - XATSubscribeLocalEvent(OnExamine); + XATSubscribeDirectEvent(OnExamine); } private void OnExamine(Entity artifact, Entity node, ref ExaminedEvent args) diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATInteractionSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATInteractionSystem.cs index 8ede143bfdf7..ac8c23defff6 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATInteractionSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATInteractionSystem.cs @@ -13,9 +13,9 @@ public override void Initialize() { base.Initialize(); - XATSubscribeLocalEvent(OnPullStart); - XATSubscribeLocalEvent(OnAttacked); - XATSubscribeLocalEvent(OnInteractHand); + XATSubscribeDirectEvent(OnPullStart); + XATSubscribeDirectEvent(OnAttacked); + XATSubscribeDirectEvent(OnInteractHand); } private void OnPullStart(Entity artifact, Entity node, ref PullStartedMessage args) diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATItemLandSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATItemLandSystem.cs new file mode 100644 index 000000000000..82714ffec727 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATItemLandSystem.cs @@ -0,0 +1,20 @@ +using Content.Shared.Throwing; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT; + +public sealed class XATItemLandSystem : BaseXATSystem +{ + /// + public override void Initialize() + { + base.Initialize(); + XATSubscribeDirectEvent(OnLand); + } + + private void OnLand(Entity artifact, Entity node, ref LandEvent args) + { + Trigger(artifact, node); + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATReactiveSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATReactiveSystem.cs new file mode 100644 index 000000000000..07b99bda0a85 --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATReactiveSystem.cs @@ -0,0 +1,30 @@ +using Content.Shared.Chemistry; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT; + +public sealed class XATReactiveSystem : BaseXATSystem +{ + /// + public override void Initialize() + { + base.Initialize(); + + XATSubscribeDirectEvent(OnReaction); + } + + private void OnReaction(Entity artifact, Entity node, ref ReactionEntityEvent args) + { + if (!node.Comp1.ReactionMethods.Contains(args.Method)) + return; + + if (args.ReagentQuantity.Quantity < node.Comp1.MinQuantity) + return; + + if (!node.Comp1.Reagents.Contains(args.Reagent.ID)) + return; + + Trigger(artifact, node); + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATTimerSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATTimerSystem.cs new file mode 100644 index 000000000000..aeccd880ddad --- /dev/null +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATTimerSystem.cs @@ -0,0 +1,54 @@ +using Content.Shared.Examine; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; + +namespace Content.Shared.Xenoarchaeology.Artifact.XAT; + +public sealed class XATTimerSystem : BaseXATSystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + XATSubscribeDirectEvent(OnExamine); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.NextActivation = Timing.CurTime + ent.Comp.Delay; + Dirty(ent); + } + + private void OnExamine(Entity artifact, Entity node, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + args.PushMarkup(Loc.GetString("xenoarch-trigger-examine-timer", + ("time", MathF.Ceiling((float) (node.Comp1.NextActivation - Timing.CurTime).TotalSeconds)))); + } + + protected override void UpdateXAT(Entity artifact, Entity node, float frameTime) + { + base.UpdateXAT(artifact, node, frameTime); + + if (Timing.CurTime > node.Comp1.NextActivation) + Trigger(artifact, node); + } + + // We handle the timer resetting here because we need to keep it updated even if the node isn't able to unlock. + public override void Update(float frameTime) + { + base.Update(frameTime); + + var timerQuery = EntityQueryEnumerator(); + while (timerQuery.MoveNext(out var uid, out var timer)) + { + if (Timing.CurTime < timer.NextActivation) + continue; + timer.NextActivation += timer.Delay; + Dirty(uid, timer); + } + } +} diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATToolUseSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATToolUseSystem.cs index 2a2708080ac5..ccb80c11c126 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATToolUseSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATToolUseSystem.cs @@ -18,8 +18,8 @@ public override void Initialize() { base.Initialize(); - XATSubscribeLocalEvent(OnInteractUsing); - XATSubscribeLocalEvent(OnToolUseComplete); + XATSubscribeDirectEvent(OnInteractUsing); + XATSubscribeDirectEvent(OnToolUseComplete); } private void OnToolUseComplete(Entity artifact, Entity node, ref XATToolUseDoAfterEvent args) diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl index dde2e552a449..dc81663e12fb 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl @@ -16,8 +16,10 @@ analysis-console-info-no-artifact = No artifact present! Place one on the pad th analysis-console-info-ready = Systems operational. Ready to scan. analysis-console-no-node = [font="Monospace" size=11]Select node to view[/font] -analysis-console-info-id = [font="Monospace" size=11]Node ID:[/font] +analysis-console-info-id = [font="Monospace" size=11]ID:[/font] analysis-console-info-id-value = [font="Monospace" size=11][color=yellow]{$id}[/color][/font] +analysis-console-info-class = [font="Monospace" size=11]Class:[/font] +analysis-console-info-class-value = [font="Monospace" size=11]{$class}[/font] analysis-console-info-locked = [font="Monospace" size=11]Status:[/font] analysis-console-info-locked-value = [font="Monospace" size=11][color={ $state -> [true] red]Locked diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl index 27844a5766e5..c078ed5cf086 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl @@ -14,3 +14,10 @@ artifact-unlock-state-end-failure = It slows down before uneventfully stopping. ### Misc. artifact-examine-trigger-desc = [color=gray][italic]Am I on your mind?[/italic][/color] + +artifact-node-class-1 = [color=#ff00a1]Hylic[/color] +artifact-node-class-2 = [color=#ff7300]Psychic[/color] +artifact-node-class-3 = [color=#90fe00]Pneumatic[/color] +artifact-node-class-4 = [color=#00fff7]Archon[/color] +artifact-node-class-5 = [color=#5d7af0]Luminary[/color] +artifact-node-class-6 = [color=#be78ff]Demiurge[/color] diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl index 57a96e9c6a39..e69627954ced 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl @@ -54,15 +54,23 @@ xenoarch-trigger-tip-n2o = Nitrous oxide xenoarch-trigger-tip-frezon = Frezon xenoarch-trigger-tip-radiation = Radiation xenoarch-trigger-tip-brute-damage = Physical damage -xenoarch-trigger-tip-interaction = Phyiscal interaction +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 ### Description hints -xenoarch-trigger-examine-wrenching = [italic]There's a loose bit spinning around.[/italic] -xenoarch-trigger-examine-prying = [italic]There's a panel coming up from the surface.[/italic] -xenoarch-trigger-examine-screwing = [italic]There's a raised section with a small inset on it.[/italic] +xenoarch-trigger-examine-wrenching = There's a loose bit spinning around. +xenoarch-trigger-examine-prying = There's a panel coming up from the surface. +xenoarch-trigger-examine-screwing = There's a raised section with a small inset on it. +xenoarch-trigger-examine-pulsing = An exposed diode pokes out of the artifact's surface. +xenoarch-trigger-examine-timer = Carvings and scratches cover the surface... You can just barely make out a number: [italic]{$time}[/italic] diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml index dcec8bc0d63d..2ca6997d7e36 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml @@ -31,10 +31,10 @@ mask: - MachineMask - type: InteractionOutline - - type: RandomSprite - available: - - enum.ArtifactsVisualLayers.Effect: - ano01_on: Rainbow + #- type: RandomSprite + # available: + # - enum.ArtifactsVisualLayers.Effect: + # ano01_on: Rainbow - type: XenoArtifact - type: StealTarget stealGroup: XenoArtifact @@ -43,3 +43,4 @@ - Xenoarchaeology # These components are needed for certain triggers to work. - type: RadiationReceiver + - type: Reactive diff --git a/Resources/Prototypes/XenoArch/triggers.yml b/Resources/Prototypes/XenoArch/triggers.yml index 9900477ae969..85d2ad66f335 100644 --- a/Resources/Prototypes/XenoArch/triggers.yml +++ b/Resources/Prototypes/XenoArch/triggers.yml @@ -2,25 +2,31 @@ id: DefaultTriggers weights: TriggerMusic: 1 - TriggerHeat: 0.5 + TriggerHeat: 1 TriggerCold: 0.5 TriggerNoOxygen: 1 TriggerWater: 1 - TriggerCO2: 0.16 - TriggerPlasma: 0.16 - TriggerTritium: 0.16 - TriggerAmmonia: 0.16 - TriggerN2O: 0.16 - TriggerFrezon: 0.16 + TriggerCO2: 0.5 + TriggerPlasma: 0.5 + TriggerN2O: 0.5 + TriggerTritium: 0.1 + TriggerAmmonia: 0.1 + TriggerFrezon: 0.1 TriggerRadiation: 1 TriggerPressureHigh: 0.5 - TriggerPressureLow: 0.5 + TriggerPressureLow: 1 TriggerExamine: 1 TriggerBruteDamage: 1 TriggerInteraction: 1 - TriggerWrenching: 0.33 - TriggerPrying: 0.33 - TriggerScrewing: 0.33 + TriggerWrenching: 0.5 + TriggerPrying: 0.5 + TriggerScrewing: 0.5 + TriggerPulsing: 0.5 + TriggerTimer: 0.25 + TriggerBlood: 1 + TriggerThrow: 1 + TriggerDeath: 1 + TriggerMagnet: 1 - type: xenoArchTrigger id: TriggerMusic @@ -64,13 +70,15 @@ moles: 10 shouldBePresent: false -# TODO: all the gas triggers need to support liquid versions as well - type: xenoArchTrigger id: TriggerWater tip: xenoarch-trigger-tip-water components: - type: XATGas targetGas: WaterVapor + - type: XATReactive + reagents: + - Water - type: xenoArchTrigger id: TriggerCO2 @@ -78,6 +86,9 @@ components: - type: XATGas targetGas: CarbonDioxide + - type: XATReactive + reagents: + - CarbonDioxide - type: xenoArchTrigger id: TriggerPlasma @@ -85,6 +96,9 @@ components: - type: XATGas targetGas: Plasma + - type: XATReactive + reagents: + - Plasma - type: xenoArchTrigger id: TriggerTritium @@ -92,6 +106,9 @@ components: - type: XATGas targetGas: Tritium + - type: XATReactive + reagents: + - Tritium - type: xenoArchTrigger id: TriggerAmmonia @@ -99,6 +116,9 @@ components: - type: XATGas targetGas: Ammonia + - type: XATReactive + reagents: + - Ammonia - type: xenoArchTrigger id: TriggerN2O @@ -106,6 +126,9 @@ components: - type: XATGas targetGas: NitrousOxide + - type: XATReactive + reagents: + - NitrousOxide - type: xenoArchTrigger id: TriggerFrezon @@ -113,7 +136,9 @@ components: - type: XATGas targetGas: Frezon -# END GAS TRIGGERS + - type: XATReactive + reagents: + - Frezon - type: xenoArchTrigger id: TriggerRadiation @@ -184,3 +209,53 @@ neededTool: Screwing - type: XATExaminableText examineText: xenoarch-trigger-examine-screwing + +- type: xenoArchTrigger + id: TriggerPulsing + tip: xenoarch-trigger-tip-pulsing + components: + - type: XATToolUse + neededTool: Pulsing + - type: XATExaminableText + examineText: xenoarch-trigger-examine-pulsing + +- type: xenoArchTrigger + id: TriggerTimer + tip: xenoarch-trigger-tip-timer + components: + - type: XATTimer + delay: 120 #2 minutes + +- type: xenoArchTrigger + id: TriggerBlood + tip: xenoarch-trigger-tip-blood + components: + - type: XATReactive + reagents: + - Blood + - CopperBlood + - InsectBlood + - Slime + - AmmoniaBlood + - ZombieBlood + +- type: xenoArchTrigger + id: TriggerThrow + tip: xenoarch-trigger-tip-throw + whitelist: + components: + - Item + components: + - type: XATItemLand + +- type: xenoArchTrigger + id: TriggerDeath + tip: xenoarch-trigger-tip-death + components: + - type: XATDeath + +- type: xenoArchTrigger + id: TriggerMagnet + tip: xenoarch-trigger-tip-magnet + components: + - type: XATMagnet From c42a2b274fa482f1bad68fcf2f47e301556a4ab6 Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Fri, 5 Jul 2024 20:58:21 -0400 Subject: [PATCH 18/81] some ui changes, mostly --- .../Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml | 2 +- .../Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs | 5 +++-- .../en-US/xenoarchaeology/artifact-component.ftl | 10 +++++----- Resources/Prototypes/XenoArch/triggers.yml | 9 +++++---- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml index bcb364f1736e..3cdc074b744b 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml @@ -5,7 +5,7 @@ xmlns:ui="clr-namespace:Content.Client.Xenoarchaeology.Ui" Title="{Loc 'analysis-console-menu-title'}" MinSize="700 350" - SetSize="980 500"> + SetSize="980 550"> diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs index dfd2a24f4099..2cae34524e44 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs @@ -121,12 +121,13 @@ public void SetSelectedNode(Entity? node) { var triggerStr = new StringBuilder(); triggerStr.Append("- "); - triggerStr.AppendLine(Loc.GetString(node.Value.Comp.TriggerTip)); + triggerStr.Append(Loc.GetString(node.Value.Comp.TriggerTip)); foreach (var predecessor in predecessorNodes) { + triggerStr.AppendLine(); triggerStr.Append("- "); - triggerStr.AppendLine(Loc.GetString(predecessor.Comp.TriggerTip)); + triggerStr.Append(Loc.GetString(predecessor.Comp.TriggerTip)); } TriggerValueLabel.SetMarkup(Loc.GetString("analysis-console-info-triggered-value", ("triggers", triggerStr.ToString()))); } diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl index c078ed5cf086..b1b699d0ddb9 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-component.ftl @@ -15,9 +15,9 @@ artifact-unlock-state-end-failure = It slows down before uneventfully stopping. ### Misc. artifact-examine-trigger-desc = [color=gray][italic]Am I on your mind?[/italic][/color] -artifact-node-class-1 = [color=#ff00a1]Hylic[/color] -artifact-node-class-2 = [color=#ff7300]Psychic[/color] -artifact-node-class-3 = [color=#90fe00]Pneumatic[/color] -artifact-node-class-4 = [color=#00fff7]Archon[/color] -artifact-node-class-5 = [color=#5d7af0]Luminary[/color] +artifact-node-class-1 = [color=#ff2bb1]Hylic[/color] +artifact-node-class-2 = [color=#ff8b2b]Psychic[/color] +artifact-node-class-3 = [color=#a9ff38]Pneumatic[/color] +artifact-node-class-4 = [color=#2bfff8]Archon[/color] +artifact-node-class-5 = [color=#576aff]Luminary[/color] artifact-node-class-6 = [color=#be78ff]Demiurge[/color] diff --git a/Resources/Prototypes/XenoArch/triggers.yml b/Resources/Prototypes/XenoArch/triggers.yml index 85d2ad66f335..c87b7d88f4ee 100644 --- a/Resources/Prototypes/XenoArch/triggers.yml +++ b/Resources/Prototypes/XenoArch/triggers.yml @@ -45,10 +45,11 @@ - type: XATDamage typesNeeded: Heat: 50 - - type: XATToolUse - neededTool: Welding - delay: 5 - fuel: 10 +# This kinda trivializes the difficulty. +# - type: XATToolUse +# neededTool: Welding +# delay: 5 +# fuel: 10 - type: xenoArchTrigger id: TriggerCold From 2392c5107486de2bd285b56e3207a3dc1a94095c Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Sun, 7 Jul 2024 23:59:49 -0400 Subject: [PATCH 19/81] Fix orphaned procgen segments --- .../Artifact/XenoArtifactSystem.ProcGen.cs | 102 ++++++++++-------- .../Components/XenoArtifactComponent.cs | 3 +- .../Artifact/SharedXenoArtifactSystem.Node.cs | 54 ++++++++-- .../Artifact/SharedXenoArtifactSystem.XAT.cs | 7 +- Resources/Prototypes/XenoArch/effects.yml | 13 ++- 5 files changed, 117 insertions(+), 62 deletions(-) diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index b97ac2c8b1bf..72c9240b633d 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -1,8 +1,8 @@ +using System.Linq; using Content.Shared.Random.Helpers; using Content.Shared.Xenoarchaeology.Artifact.Components; using Content.Shared.Xenoarchaeology.Artifact.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Utility; namespace Content.Server.Xenoarchaeology.Artifact; @@ -53,22 +53,67 @@ private void GenerateArtifactSegment(Entity ent, ref int { var segmentSize = GetArtifactSegmentSize(ent, nodeCount); nodeCount -= segmentSize; - PopulateArtifactSegmentRecursive(ent, ref segmentSize, ensureLayerConnected: true); + var populatedNodes = PopulateArtifactSegmentRecursive(ent, ref segmentSize); + + var segments = GetSegmentsFromNodes(ent, populatedNodes).ToList(); + + // We didn't connect all of our nodes: do extra work to make sure there's a connection. + if (segments.Count > 1) + { + var parent = segments.MaxBy(s => s.Count)!; + var minP = parent.Min(n => n.Comp.Depth); + var maxP = parent.Max(n => n.Comp.Depth); + + segments.Remove(parent); + foreach (var segment in segments) + { + // calculate the range of the depth of the nodes in the segment + var minS = segment.Min(n => n.Comp.Depth); + var maxS = segment.Max(n => n.Comp.Depth); + + // Figure out the range of depths that allows for a connection between these two. + // The range is essentially the lower values + 1 on each side. + var min = Math.Max(minS, minP) - 1; + var max = Math.Min(maxS, maxP) + 1; + + // how the fuck did you do this? you don't even deserve to get a parent. fuck you. + if (min > max || min == max) + continue; + + var node1 = RobustRandom.Pick(segment + .Where(n => n.Comp.Depth >= min && n.Comp.Depth <= max) + .ToList()); + var node1Depth = node1.Comp.Depth; + + var node2 = RobustRandom.Pick(parent + .Where(n => n.Comp.Depth >= node1Depth - 1 && n.Comp.Depth <= node1Depth + 1 && n.Comp.Depth != node1Depth) + .ToList()); + + if (node1.Comp.Depth < node2.Comp.Depth) + { + AddEdge((ent, ent.Comp), node1, node2, false); + } + else + { + AddEdge((ent, ent.Comp), node2, node1, false); + } + } + } } private List> PopulateArtifactSegmentRecursive( Entity ent, ref int segmentSize, - int layerMinMod = 0, - int layerMaxMod = 0, - bool ensureLayerConnected = false, int iteration = 0) { if (segmentSize == 0) return new(); - var layerMin = Math.Min(ent.Comp.NodesPerSegmentLayer.Min + layerMinMod, segmentSize); - var layerMax = Math.Min(ent.Comp.NodesPerSegmentLayer.Max + layerMaxMod, segmentSize); + // Try and get larger as the we create more layers. Prevents excessive layers. + var mod = RobustRandom.Next((int) (iteration / 1.5f), iteration + 1); + + var layerMin = Math.Min(ent.Comp.NodesPerSegmentLayer.Min + mod, segmentSize); + var layerMax = Math.Min(ent.Comp.NodesPerSegmentLayer.Max + mod, segmentSize); // Default to one node if we had shenanigans and ended up with weird layer counts. var nodeCount = 1; @@ -79,42 +124,27 @@ private List> PopulateArtifactSegmentRecursive var nodes = new List>(); for (var i = 0; i < nodeCount; i++) { - nodes.Add(CreateRandomNode(ent, iteration)); + var trigger = RobustRandom.PickAndTake(_triggerPool); + nodes.Add(CreateRandomNode(ent, trigger, iteration)); } - var minMod = ent.Comp.NodeContainer.Count < 3 ? 0 : 1; // Try to stop boring linear generation. - var maxMod = nodes.Count / 2; // cumulative modifier to enable slight growth for something like 3 -> 4 var successors = PopulateArtifactSegmentRecursive( ent, ref segmentSize, - layerMinMod: minMod, - layerMaxMod: maxMod, iteration: iteration + 1); if (successors.Count == 0) return nodes; - // TODO: this doesn't actually make sure that the segment is interconnected. - // You can still occasionally get orphaned segments. - - // We do the picks from node -> successor and from successor -> node to ensure that no nodes get orphaned without connections. foreach (var successor in successors) { var node = RobustRandom.Pick(nodes); AddEdge((ent, ent), node, successor, dirty: false); } - if (ensureLayerConnected) - { - foreach (var node in nodes) - { - var successor = RobustRandom.Pick(successors); - AddEdge((ent, ent), node, successor, dirty: false); - } - } - - var reverseScatterCount = ent.Comp.ReverseScatterPerLayer.Next(RobustRandom); - for (var i = 0; i < reverseScatterCount; i++) + // randomly add in some extra edges for variance. + var scatterCount = ent.Comp.ScatterPerLayer.Next(RobustRandom); + for (var i = 0; i < scatterCount; i++) { var node = RobustRandom.Pick(nodes); var successor = RobustRandom.Pick(successors); @@ -143,22 +173,4 @@ private int GetArtifactSegmentSize(Entity ent, int nodeCo return segmentSize; } - - //todo: move this into system.node or something. - private Entity CreateRandomNode(Entity ent, int depth = 0) - { - var proto = PrototypeManager.Index(ent.Comp.EffectWeights).Pick(RobustRandom); - - AddNode((ent, ent), proto, out var nodeEnt, dirty: false); - DebugTools.Assert(nodeEnt.HasValue, "Failed to create node on artifact."); - - var trigger = RobustRandom.PickAndTake(_triggerPool); - - nodeEnt.Value.Comp.Depth = depth; - nodeEnt.Value.Comp.TriggerTip = trigger.Tip; - EntityManager.AddComponents(nodeEnt.Value, trigger.Components); - - //Dirty(nodeEnt.Value); - return nodeEnt.Value; - } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index d715fae2d416..2da30d8255de 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -1,7 +1,6 @@ using Content.Shared.Destructible.Thresholds; using Content.Shared.Random; using Content.Shared.Xenoarchaeology.Artifact.Prototypes; -using Content.Shared.Xenoarchaeology.Artifact.XAT; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -94,7 +93,7 @@ public sealed partial class XenoArtifactComponent : Component public MinMax NodesPerSegmentLayer = new(1, 3); [DataField] - public MinMax ReverseScatterPerLayer = new(0, 2); + public MinMax ScatterPerLayer = new(0, 2); [DataField] public ProtoId EffectWeights = "XenoArtifactEffectsDefault"; diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 522f295f25aa..7698b52cb9a8 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -1,5 +1,8 @@ using System.Linq; +using Content.Shared.Random.Helpers; using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.Prototypes; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Shared.Xenoarchaeology.Artifact; @@ -83,6 +86,26 @@ public void SetNodeDurability(Entity ent, int durabi Dirty(ent); } + public Entity CreateRandomNode(Entity ent, ProtoId trigger, int depth = 0) + { + return CreateRandomNode(ent, PrototypeManager.Index(trigger), depth); + } + + public Entity CreateRandomNode(Entity ent, XenoArchTriggerPrototype trigger, int depth = 0) + { + var proto = PrototypeManager.Index(ent.Comp.EffectWeights).Pick(RobustRandom); + + AddNode((ent, ent), proto, out var nodeEnt, dirty: false); + DebugTools.Assert(nodeEnt.HasValue, "Failed to create node on artifact."); + + nodeEnt.Value.Comp.Depth = depth; + nodeEnt.Value.Comp.TriggerTip = trigger.Tip; + EntityManager.AddComponents(nodeEnt.Value, trigger.Components); + + Dirty(nodeEnt.Value); + return nodeEnt.Value; + } + public bool HasUnlockedPredecessor(Entity ent, EntityUid node) { var predecessors = GetDirectPredecessorNodes((ent, ent), node); @@ -194,27 +217,40 @@ public void RebuildCachedSegments(Entity ent) return; ent.Comp.CachedSegments.Clear(); - foreach (var node in GetAllNodes((ent, ent.Comp))) + + var segments = GetSegmentsFromNodes((ent, ent.Comp), GetAllNodes((ent, ent.Comp)).ToList()); + ent.Comp.CachedSegments.AddRange(segments + .Select(s => s + .Select(n => GetNetEntity(n)) + .ToList())); + + Dirty(ent); + } + + public IEnumerable>> GetSegmentsFromNodes(Entity ent, List> nodes) + { + var outSegments = new List>>(); + foreach (var node in nodes) { var segment = new List>(); - GetSegmentNodesRecursive((ent, ent.Comp), node, ref segment); - + GetSegmentNodesRecursive(ent, node, ref segment, ref outSegments); if (segment.Count == 0) continue; - ent.Comp.CachedSegments.Add(segment.Select(n => GetNetEntity(n.Owner)).ToList()); + outSegments.Add(segment); } - Dirty(ent); + return outSegments; } private void GetSegmentNodesRecursive( Entity ent, Entity node, - ref List> segment) + ref List> segment, + ref List>> otherSegments) { - if (ent.Comp.CachedSegments.Any(s => s.Contains(GetNetEntity(node)))) + if (otherSegments.Any(s => s.Contains(node))) return; if (segment.Contains(node)) @@ -225,13 +261,13 @@ private void GetSegmentNodesRecursive( var predecessors = GetDirectPredecessorNodes((ent, ent), node); foreach (var p in predecessors) { - GetSegmentNodesRecursive(ent, p, ref segment); + GetSegmentNodesRecursive(ent, p, ref segment, ref otherSegments); } var successors = GetDirectSuccessorNodes((ent, ent), node); foreach (var s in successors) { - GetSegmentNodesRecursive(ent, s, ref segment); + GetSegmentNodesRecursive(ent, s, ref segment, ref otherSegments); } } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs index 61c7f5f01fa2..589ba17a14a0 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs @@ -11,7 +11,6 @@ namespace Content.Shared.Xenoarchaeology.Artifact; public abstract partial class SharedXenoArtifactSystem { - // todo this is kinda a misnomer since it handles generic relays. private void InitializeXAT() { base.Initialize(); @@ -35,10 +34,10 @@ protected void XATRelayLocalEvent() where T : notnull private void OnExamined(Entity ent, ref ExaminedEvent args) { - //using (args.PushGroup(nameof(XenoArtifactComponent))) - //{ + using (args.PushGroup(nameof(XenoArtifactComponent))) + { RelayEventToNodes(ent, ref args); - //} + } } protected void RelayEventToNodes(Entity ent, ref T args) where T : notnull diff --git a/Resources/Prototypes/XenoArch/effects.yml b/Resources/Prototypes/XenoArch/effects.yml index 9f5d296ccf55..15ed55a570b8 100644 --- a/Resources/Prototypes/XenoArch/effects.yml +++ b/Resources/Prototypes/XenoArch/effects.yml @@ -1,7 +1,16 @@ +- type: entity + id: BaseXenoArtifactEffect + name: effect + description: Unknown + categories: [ HideSpawnMenu ] + abstract: true + components: + - type: XenoArtifactNode + - type: entity id: XenoArtifactEffectTest - name: test effect - noSpawn: true + parent: BaseXenoArtifactEffect + description: Bombs the middle east. components: - type: XenoArtifactNode - type: NameIdentifier From 05e791874d345430b0c262c716738eb47272663a Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Mon, 8 Jul 2024 00:00:24 -0400 Subject: [PATCH 20/81] its not random dipshit --- .../Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs | 2 +- .../Artifact/SharedXenoArtifactSystem.Node.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs index 72c9240b633d..6fb88ace987a 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XenoArtifactSystem.ProcGen.cs @@ -125,7 +125,7 @@ private List> PopulateArtifactSegmentRecursive for (var i = 0; i < nodeCount; i++) { var trigger = RobustRandom.PickAndTake(_triggerPool); - nodes.Add(CreateRandomNode(ent, trigger, iteration)); + nodes.Add(CreateNode(ent, trigger, iteration)); } var successors = PopulateArtifactSegmentRecursive( diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 7698b52cb9a8..241807d960d9 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -86,12 +86,12 @@ public void SetNodeDurability(Entity ent, int durabi Dirty(ent); } - public Entity CreateRandomNode(Entity ent, ProtoId trigger, int depth = 0) + public Entity CreateNode(Entity ent, ProtoId trigger, int depth = 0) { - return CreateRandomNode(ent, PrototypeManager.Index(trigger), depth); + return CreateNode(ent, PrototypeManager.Index(trigger), depth); } - public Entity CreateRandomNode(Entity ent, XenoArchTriggerPrototype trigger, int depth = 0) + public Entity CreateNode(Entity ent, XenoArchTriggerPrototype trigger, int depth = 0) { var proto = PrototypeManager.Index(ent.Comp.EffectWeights).Pick(RobustRandom); From dbc3f7054db7020abc45e56146d5a40cee3c874f Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Tue, 9 Jul 2024 19:23:58 -0400 Subject: [PATCH 21/81] yeah... this one will make pjb happy.... --- Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs index 2e1c7d54df6e..a8f764a93ff9 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATExamineSystem.cs @@ -20,7 +20,7 @@ private void OnExamine(Entity artifact, Entity(args.Examiner, out var ghost) && !ghost.CanGhostInteract) + if (HasComp(args.Examiner)) return; Trigger(artifact, node); From e22daae2f4302239f220cf89571bc779d8bcdf5e Mon Sep 17 00:00:00 2001 From: EmoGarbage404 Date: Fri, 12 Jul 2024 18:03:18 -0400 Subject: [PATCH 22/81] we call it a day for the UI --- .../Ui/AnalysisConsoleBoundUserInterface.cs | 19 +-- .../Ui/AnalysisConsoleMenu.xaml | 26 +-- .../Ui/AnalysisConsoleMenu.xaml.cs | 58 +++++-- .../Ui/XenoArtifactGraphControl.xaml.cs | 2 +- .../Equipment/ArtifactAnalyzerSystem.cs | 155 +----------------- .../Equipment/SharedArtifactAnalyzer.cs | 25 +-- .../xenoarchaeology/artifact-analyzer.ftl | 27 +-- .../xenoarchaeology/artifact-component.ftl | 2 +- .../Structures/Machines/artifact_analyzer.yml | 1 - 9 files changed, 70 insertions(+), 245 deletions(-) diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs index fff82180fb8e..29365a02588d 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs @@ -1,3 +1,4 @@ +using Content.Shared.Research.Components; using Content.Shared.Xenoarchaeology.Equipment; using Content.Shared.Xenoarchaeology.Equipment.Components; using JetBrains.Annotations; @@ -21,28 +22,12 @@ protected override void Open() _consoleMenu.OnServerSelectionButtonPressed += () => { - SendMessage(new AnalysisConsoleServerSelectionMessage()); - }; - _consoleMenu.OnScanButtonPressed += () => - { - SendMessage(new AnalysisConsoleScanButtonPressedMessage()); - }; - _consoleMenu.OnPrintButtonPressed += () => - { - SendMessage(new AnalysisConsolePrintButtonPressedMessage()); + SendMessage(new ConsoleServerSelectionMessage()); }; _consoleMenu.OnExtractButtonPressed += () => { SendMessage(new AnalysisConsoleExtractButtonPressedMessage()); }; - _consoleMenu.OnUpBiasButtonPressed += () => - { - SendMessage(new AnalysisConsoleBiasButtonPressedMessage(false)); - }; - _consoleMenu.OnDownBiasButtonPressed += () => - { - SendMessage(new AnalysisConsoleBiasButtonPressedMessage(true)); - }; } public void Update(Entity ent) diff --git a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml index 3cdc074b744b..f38314f798a8 100644 --- a/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml +++ b/Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml @@ -16,22 +16,19 @@ - - + + - - - - + + + + public int GetResearchValue(Entity ent) { @@ -156,11 +173,18 @@ public void SetConsumedResearchValue(Entity ent, int Dirty(ent); } + /// + /// Converts node entity uid to its display name (which is Identifier from . + /// public string GetNodeId(EntityUid uid) { return (CompOrNull(uid)?.Identifier ?? 0).ToString("D3"); } + /// + /// Gets two-dimensional array in a form of nested lists, which holds artifact nodes, grouped by segments. + /// Segments are groups of interconnected nodes, there might be one or more segments in non-empty artifact. + /// public List>> GetSegments(Entity ent) { var output = new List>>(); @@ -180,6 +204,10 @@ public List>> GetSegments(Entity + /// Gets list of nodes, grouped by depth level. Depth level count starts from 0. + /// Only 0 depth nodes have no incoming edges - as only they are starting nodes. + /// public Dictionary>> GetDepthOrderedNodes(IEnumerable> nodes) { var nodesByDepth = new Dictionary>>(); @@ -199,7 +227,7 @@ public Dictionary>> GetDepthOrderedN } /// - /// Rebuilds all the data associated with nodes in an artifact. + /// Rebuilds all the data, associated with nodes in an artifact, updating caches. /// public void RebuildXenoArtifactMetaData(Entity artifact) { @@ -291,6 +319,9 @@ public void RebuildCachedSegments(Entity ent) Dirty(ent); } + /// + /// Gets two-dimensional array (as lists inside enumeration) that contains artifact nodes, grouped by segment. + /// public IEnumerable>> GetSegmentsFromNodes(Entity ent, List> nodes) { var outSegments = new List>>(); @@ -308,6 +339,9 @@ public IEnumerable>> GetSegmentsFromNodes return outSegments; } + /// + /// Fills nodes into segments by recursively walking through collections of predecessors and successors. + /// private void GetSegmentNodesRecursive( Entity ent, Entity node, @@ -336,6 +370,10 @@ List>> otherSegments } } + /// + /// Sets node research point amount that can be extracted. + /// Used up durability increases amount to be extracted. + /// public void UpdateNodeResearchValue(Entity node) { if (node.Comp.Attached == null) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs index 291eb5213d96..a4d23e37076c 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -16,7 +16,8 @@ private void InitializeUnlock() _unlockingQuery = GetEntityQuery(); } - private void UpdateUnlock(float frameTime) + /// Finish unlocking phase when the time is up. + private void UpdateUnlock(float _) { var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var unlock, out var comp)) @@ -28,6 +29,12 @@ private void UpdateUnlock(float frameTime) } } + /// + /// Checks if node can be unlocked. + /// Only those nodes, that have no predecessors, or have all + /// predecessors unlocked can be unlocked themselves. + /// Artifact being suppressed also prevents unlocking. + /// public bool CanUnlockNode(Entity ent) { if (!Resolve(ent, ref ent.Comp)) @@ -46,6 +53,10 @@ public bool CanUnlockNode(Entity ent) return true; } + /// + /// Finishes unlocking phase, removing related component, and sums up what nodes were triggered, + /// that could be unlocked. Marks such nodes as unlocked, and pushes their node activation event. + /// public void FinishUnlockingState(Entity ent) { string unlockAttemptResultMsg; diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAE.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAE.cs index 5b3042d7e288..f537766f353b 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAE.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAE.cs @@ -44,11 +44,20 @@ private void OnActivateInWorld(Entity ent, ref ActivateIn args.Handled = TryActivateXenoArtifact(ent, args.User, args.Target, Transform(args.Target).Coordinates); } + /// + /// Attempts to activate artifact nodes. 'active' are nodes that are marked as 'unlocked' and have no other successors, marked as 'unlocked'. + /// + /// Artifact entity, for which attempt to activate was made. + /// Character that attempted to activate artifact. + /// Target, on which artifact activation attempt was used (for hand-held artifact - it can be 'clicked' over someone). + /// Coordinates of entity. + /// True, if activation was successful, false otherwise. public bool TryActivateXenoArtifact( Entity artifact, EntityUid? user, EntityUid? target, - EntityCoordinates coordinates) + EntityCoordinates coordinates + ) { if (artifact.Comp.Suppressed) return false; @@ -70,6 +79,16 @@ public bool TryActivateXenoArtifact( return true; } + /// + /// Pushes node activation event and updates durability for activated node. + /// + /// Artifact entity, for which attempt to activate was made. + /// Node entity, effect of which should be activated. + /// Character that attempted to activate artifact. + /// Target, on which artifact activation attempt was used (for hand-held artifact - it can be 'clicked' over someone). + /// Coordinates of entity. + /// Marker, if node durability should be adjusted as a result of activation. + /// True, if activation was successful, false otherwise. public bool ActivateNode( Entity artifact, Entity node, @@ -98,6 +117,14 @@ public bool ActivateNode( } } +/// +/// Event of node activation. Should lead to node effect being activated. +/// +/// Artifact entity, for which attempt to activate was made. +/// Node entity, effect of which should be activated. +/// Character that attempted to activate artifact. +/// Target, on which artifact activation attempt was used (for hand-held artifact - it can be 'clicked' over someone). +/// Coordinates of entity. [ByRefEvent] public readonly record struct XenoArtifactNodeActivatedEvent( Entity Artifact, diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs index a669c3586974..e8fe49c0ba11 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs @@ -27,6 +27,7 @@ private void InitializeXAT() SubscribeLocalEvent(OnExamined); } + /// Relays artifact events for artifact nodes. protected void XATRelayLocalEvent() where T : notnull { SubscribeLocalEvent(RelayEventToNodes); @@ -51,6 +52,9 @@ protected void RelayEventToNodes(Entity ent, ref T arg } } + /// + /// Attempts to shift artifact into unlocking state, in which it is going to listen to interactions, that could trigger nodes. + /// public void TriggerXenoArtifact(Entity ent, Entity node) { // limits spontaneous chain activations, also prevents spamming every triggering tool to activate nodes @@ -82,7 +86,13 @@ public void TriggerXenoArtifact(Entity ent, Entity(Entity Artifact, TEvent Args) { + /// + /// Original event. + /// public TEvent Args = Args; + /// + /// Artifact entity, that received original event. + /// public Entity Artifact = Artifact; } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs index 7372348f4b3c..dc6eb5af7a9e 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.cs @@ -38,6 +38,7 @@ public override void Update(float frameTime) UpdateUnlock(frameTime); } + /// As all artifacts have to contain nodes - we ensure that they are containers. private void OnStartup(Entity ent, ref ComponentStartup args) { ent.Comp.NodeContainer = _container.EnsureContainer(ent, XenoArtifactComponent.NodeContainerId); @@ -47,6 +48,7 @@ public void SetSuppressed(Entity ent, bool val) { if (ent.Comp.Suppressed == val) return; + ent.Comp.Suppressed = val; Dirty(ent); } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageThresholdReachedComponent.cs similarity index 92% rename from Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs rename to Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageThresholdReachedComponent.cs index 775fe4846aa1..4f5ceb1bae40 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATDamageThresholdReachedComponent.cs @@ -9,8 +9,8 @@ namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; /// /// This is used for an artifact that is activated after a certain amount of damage is dealt. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(XATDamageSystem))] -public sealed partial class XATDamageComponent : Component +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(XATDamageThresholdReachedSystem))] +public sealed partial class XATDamageThresholdReachedComponent : Component { /// /// Damage, accumulated by artifact so far. Is cleared on node activation. diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATTimerComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATTimerComponent.cs index 10bd5109489a..d23b222b149e 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATTimerComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/Components/XATTimerComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Destructible.Thresholds; using Robust.Shared.GameStates; namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; @@ -8,9 +9,15 @@ namespace Content.Shared.Xenoarchaeology.Artifact.XAT.Components; [RegisterComponent, NetworkedComponent, Access(typeof(XATTimerSystem)), AutoGenerateComponentState, AutoGenerateComponentPause] public sealed partial class XATTimerComponent : Component { + /// + /// Next time timer going to activate. + /// [DataField, AutoNetworkedField, AutoPausedField] public TimeSpan NextActivation; + /// + /// Delay between activations. + /// [DataField, AutoNetworkedField] - public TimeSpan Delay; + public MinMax PossibleDelayInSeconds; } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageThresholdReachedSystem.cs similarity index 86% rename from Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs rename to Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageThresholdReachedSystem.cs index e5ec3ed8b25d..a4d669da79a7 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageThresholdReachedSystem.cs @@ -5,7 +5,7 @@ namespace Content.Shared.Xenoarchaeology.Artifact.XAT; -public sealed class XATDamageSystem : BaseXATSystem +public sealed class XATDamageThresholdReachedSystem : BaseXATSystem { [Dependency] private readonly IPrototypeManager _prototype = default!; @@ -17,7 +17,7 @@ public override void Initialize() XATSubscribeDirectEvent(OnDamageChanged); } - private void OnDamageChanged(Entity artifact, Entity node, ref DamageChangedEvent args) + private void OnDamageChanged(Entity artifact, Entity node, ref DamageChangedEvent args) { if (!args.DamageIncreased || args.DamageDelta == null) return; @@ -51,7 +51,7 @@ private void OnDamageChanged(Entity artifact, Entity artifact, - Entity node + Entity node ) { var damageTriggerComponent = node.Comp1; diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATTimerSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATTimerSystem.cs index b7c7f752b060..b7b2873d82c4 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATTimerSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATTimerSystem.cs @@ -1,11 +1,14 @@ using Content.Shared.Examine; using Content.Shared.Xenoarchaeology.Artifact.Components; using Content.Shared.Xenoarchaeology.Artifact.XAT.Components; +using Robust.Shared.Random; namespace Content.Shared.Xenoarchaeology.Artifact.XAT; public sealed class XATTimerSystem : BaseQueryUpdateXATSystem { + [Dependency] private readonly IRobustRandom _robustRandom = default!; + public override void Initialize() { base.Initialize(); @@ -16,7 +19,8 @@ public override void Initialize() private void OnMapInit(Entity ent, ref MapInitEvent args) { - ent.Comp.NextActivation = Timing.CurTime + ent.Comp.Delay; + var delay = GetNextDelay(ent); + ent.Comp.NextActivation = Timing.CurTime + delay; Dirty(ent); } @@ -38,6 +42,7 @@ protected override void UpdateXAT(Entity artifact, Entity } // We handle the timer resetting here because we need to keep it updated even if the node isn't able to unlock. + public override void Update(float frameTime) { base.Update(frameTime); @@ -47,8 +52,13 @@ public override void Update(float frameTime) { if (Timing.CurTime < timer.NextActivation) continue; - timer.NextActivation += timer.Delay; + timer.NextActivation += GetNextDelay(timer); Dirty(uid, timer); } } + + private TimeSpan GetNextDelay(XATTimerComponent comp) + { + return TimeSpan.FromSeconds(comp.PossibleDelayInSeconds.Next(_robustRandom)); + } } diff --git a/Resources/Prototypes/XenoArch/triggers.yml b/Resources/Prototypes/XenoArch/triggers.yml index 13b9c17756de..6d61123f402f 100644 --- a/Resources/Prototypes/XenoArch/triggers.yml +++ b/Resources/Prototypes/XenoArch/triggers.yml @@ -42,7 +42,7 @@ - type: XATTemperature targetTemperature: 373 triggerOnHigherTemp: true - - type: XATDamage + - type: XATDamageThresholdReached typesNeeded: Heat: 50 # This kinda trivializes the difficulty. @@ -58,7 +58,7 @@ - type: XATTemperature targetTemperature: 255 triggerOnHigherTemp: false - - type: XATDamage + - type: XATDamageThresholdReached typesNeeded: Cold: 50 @@ -145,7 +145,7 @@ id: TriggerRadiation tip: xenoarch-trigger-tip-radiation components: - - type: XATDamage + - type: XATDamageThresholdReached typesNeeded: Radiation: 100 # TODO: legendary microwave trigger @@ -174,7 +174,7 @@ id: TriggerBruteDamage tip: xenoarch-trigger-tip-brute-damage components: - - type: XATDamage + - type: XATDamageThresholdReached groupsNeeded: Brute: 150 @@ -225,7 +225,9 @@ tip: xenoarch-trigger-tip-timer components: - type: XATTimer - delay: 120 #2 minutes + possibleDelayInSeconds: + min: 80 + max: 120 - type: xenoArchTrigger id: TriggerBlood From 0b818e29da45dd5dd1335e509f3b8a51fcb31f39 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sat, 14 Dec 2024 22:04:09 +0300 Subject: [PATCH 59/81] map for playtest, TODO REVERT THIS --- Resources/Maps/reach.yml | 757 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 710 insertions(+), 47 deletions(-) diff --git a/Resources/Maps/reach.yml b/Resources/Maps/reach.yml index fca485b2b97b..22fd6c077b7d 100644 --- a/Resources/Maps/reach.yml +++ b/Resources/Maps/reach.yml @@ -5,6 +5,8 @@ tilemap: 0: Space 1: FloorBlueCircuit 31: FloorDark + 4: FloorDarkDiagonal + 3: FloorDarkDiagonalMini 36: FloorDarkMono 47: FloorGlass 48: FloorGold @@ -57,11 +59,11 @@ entities: version: 6 -1,0: ind: -1,0 - tiles: bQAAAAAATgAAAAABLwAAAAABLwAAAAADLwAAAAAALwAAAAADLwAAAAABLwAAAAADLwAAAAADLwAAAAACLwAAAAABLwAAAAABTgAAAAACHwAAAAAAHwAAAAADHwAAAAABfgAAAAAATgAAAAABTgAAAAABTgAAAAACTgAAAAADTgAAAAADTgAAAAADTgAAAAADTgAAAAABTgAAAAAATgAAAAAATgAAAAADTgAAAAAAHwAAAAABHwAAAAABHwAAAAAAfgAAAAAAfgAAAAAAHwAAAAAAfgAAAAAAfgAAAAAAegAAAAADegAAAAACegAAAAABegAAAAADfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAHwAAAAABHwAAAAADfgAAAAAAPAAAAAAATQAAAAAAPAAAAAAAfgAAAAAAegAAAAAAegAAAAABegAAAAAAegAAAAAAfgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAfgAAAAAAHwAAAAADHwAAAAACfgAAAAAAPAAAAAAATQAAAAAATQAAAAADHwAAAAAAegAAAAAAegAAAAAAegAAAAABegAAAAACHwAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAfgAAAAAAHwAAAAABHwAAAAABfgAAAAAAPAAAAAAATQAAAAAAPAAAAAAAfgAAAAAAHwAAAAACHwAAAAABHwAAAAAAHwAAAAACfgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAfgAAAAAAHwAAAAACHwAAAAACfgAAAAAAPAAAAAAAPAAAAAAAPAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAHwAAAAADfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfgAAAAAAfgAAAAAAHwAAAAABHwAAAAAAHwAAAAADHwAAAAABHwAAAAAAJAAAAAABfQAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAHwAAAAAAHwAAAAAAHwAAAAACHwAAAAABHwAAAAACHwAAAAABJAAAAAABfQAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAACHwAAAAABfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAATwAAAAAATwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAABJAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAATwAAAAAATwAAAAAAHwAAAAAAHwAAAAAAHwAAAAADHwAAAAADJAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAQAAAAAAAQAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAA + tiles: bQAAAAAATgAAAAABLwAAAAABLwAAAAADLwAAAAAALwAAAAADLwAAAAABLwAAAAADLwAAAAADLwAAAAACLwAAAAABLwAAAAABTgAAAAACHwAAAAAAHwAAAAADHwAAAAABfgAAAAAATgAAAAABTgAAAAABTgAAAAACTgAAAAADTgAAAAADTgAAAAADTgAAAAADTgAAAAABTgAAAAAATgAAAAAATgAAAAADTgAAAAAAHwAAAAABHwAAAAABHwAAAAAAfgAAAAAAfgAAAAAAHwAAAAAAfgAAAAAAfgAAAAAAegAAAAADegAAAAACegAAAAABegAAAAADfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAHwAAAAABHwAAAAADfgAAAAAAPAAAAAAATQAAAAAAPAAAAAAAfgAAAAAAegAAAAAAegAAAAABegAAAAAAegAAAAAAfgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAfgAAAAAAHwAAAAADHwAAAAACfgAAAAAAPAAAAAAATQAAAAAATQAAAAADHwAAAAAAegAAAAAAegAAAAAAegAAAAABegAAAAACHwAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAfgAAAAAAHwAAAAABHwAAAAABfgAAAAAAPAAAAAAATQAAAAAAPAAAAAAAfgAAAAAAHwAAAAACHwAAAAABHwAAAAAAHwAAAAACfgAAAAAAPgAAAAAAPgAAAAAAPgAAAAAAfgAAAAAAHwAAAAACHwAAAAACfgAAAAAAPAAAAAAAPAAAAAAAPAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAHwAAAAADfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfgAAAAAAfgAAAAAAHwAAAAABHwAAAAAAHwAAAAADHwAAAAABHwAAAAAAJAAAAAABfQAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAHwAAAAAAHwAAAAAAHwAAAAACHwAAAAABHwAAAAACHwAAAAABJAAAAAABfQAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfQAAAAAAfgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAACHwAAAAABfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAATwAAAAAATwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAABJAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAATwAAAAAATwAAAAAAHwAAAAAAHwAAAAAAHwAAAAADHwAAAAADJAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAABAAAAAAAfgAAAAAAAQAAAAAAAQAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAABAAAAAAABAAAAAAABAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAA version: 6 0,-1: ind: 0,-1 - tiles: AAAAAAAAAAAAAAAAfQAAAAAAfgAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAUAAAAAAAUAAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAAAfgAAAAAAAgAAAAAAAgAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAUAAAAAAAUAAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAAAXQAAAAABAgAAAAAAAgAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAADfgAAAAAAAgAAAAAAfgAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAABfgAAAAAAfgAAAAAAfgAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAACfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAABfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAADfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAACfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAACfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAHwAAAAACfgAAAAAAcAAAAAAAcAAAAAABcAAAAAACcAAAAAADcAAAAAAAcAAAAAAAcAAAAAABcAAAAAADcAAAAAACcAAAAAADcAAAAAACfgAAAAAAHwAAAAADegAAAAADHwAAAAACfgAAAAAAcAAAAAABcAAAAAADcAAAAAAAcAAAAAACcAAAAAADcAAAAAACcAAAAAADcAAAAAAAcAAAAAACcAAAAAACcAAAAAAAfgAAAAAAHwAAAAADegAAAAABHwAAAAABfgAAAAAAcAAAAAAAcAAAAAADcAAAAAABcAAAAAAAcAAAAAACcAAAAAABcAAAAAACcAAAAAACcAAAAAAAcAAAAAACcAAAAAABfgAAAAAAHwAAAAAAegAAAAADHwAAAAACfgAAAAAAfgAAAAAAcAAAAAADcAAAAAADfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAcAAAAAADcAAAAAADcAAAAAADfgAAAAAAfgAAAAAAfgAAAAAAHwAAAAAAfgAAAAAAcAAAAAABcAAAAAACcAAAAAACfgAAAAAAcAAAAAAAcAAAAAAAcAAAAAAAfgAAAAAAcAAAAAABcAAAAAABcAAAAAADfgAAAAAAHwAAAAABHwAAAAAC + tiles: AAAAAAAAAAAAAAAAfQAAAAAAfgAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAUAAAAAAAUAAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAAAfgAAAAAAAgAAAAAAAgAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAUAAAAAAAUAAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAAAXQAAAAABAgAAAAAAAgAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAADfgAAAAAAAgAAAAAAfgAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAABfgAAAAAAfgAAAAAAfgAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAACfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAABfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAADfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAbAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAACfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAHwAAAAACfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAHwAAAAACfgAAAAAAcAAAAAAAcAAAAAABcAAAAAACcAAAAAADcAAAAAAAcAAAAAAAcAAAAAABcAAAAAADcAAAAAACcAAAAAADcAAAAAACfgAAAAAAHwAAAAADegAAAAADHwAAAAACfgAAAAAAcAAAAAABcAAAAAADcAAAAAAAcAAAAAACcAAAAAADcAAAAAACcAAAAAADcAAAAAAAcAAAAAACcAAAAAACcAAAAAAAfgAAAAAAHwAAAAADegAAAAABHwAAAAABfgAAAAAAcAAAAAAAcAAAAAADcAAAAAABcAAAAAAAcAAAAAACcAAAAAABcAAAAAACcAAAAAACcAAAAAAAcAAAAAACcAAAAAABfgAAAAAAHwAAAAAAegAAAAADHwAAAAACfgAAAAAAfgAAAAAAcAAAAAADcAAAAAADfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAcAAAAAADcAAAAAADcAAAAAADfgAAAAAAfgAAAAAAfgAAAAAAHwAAAAAAfgAAAAAAcAAAAAABcAAAAAACcAAAAAACfgAAAAAAcAAAAAAAcAAAAAAAcAAAAAAAfgAAAAAAcAAAAAABcAAAAAABcAAAAAADfgAAAAAAHwAAAAABHwAAAAAC version: 6 -2,0: ind: -2,0 @@ -73,11 +75,11 @@ entities: version: 6 0,-2: ind: 0,-2 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfQAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 0,1: ind: 0,1 - tiles: fgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAHwAAAAAAHwAAAAADegAAAAABegAAAAADegAAAAADHwAAAAACHwAAAAACfgAAAAAAfQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAegAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAegAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAATwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: fgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAHwAAAAAAHwAAAAADegAAAAABegAAAAADegAAAAADHwAAAAACHwAAAAACfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAegAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAegAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAATwAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 1,0: ind: 1,0 @@ -89,7 +91,7 @@ entities: version: 6 -1,1: ind: -1,1 - tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfgAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAABAAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAAwAAAAAAfgAAAAAAfgAAAAAAfgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA version: 6 - type: Broadphase - type: Physics @@ -1031,7 +1033,7 @@ entities: -4,-2: 0: 57344 -5,-2: - 0: 57344 + 0: 58368 -4,-1: 0: 61166 -5,-1: @@ -1064,10 +1066,10 @@ entities: -1,-4: 0: 32768 0,-4: - 0: 62720 + 0: 64768 1: 4 0,-3: - 0: 21781 + 0: 21783 0,-2: 0: 55805 0,-1: @@ -1123,12 +1125,11 @@ entities: 3,-1: 0: 53725 3,4: - 1: 1 + 0: 1 4,0: 0: 54783 4,1: - 0: 157 - 2: 64 + 0: 221 -5,0: 0: 61183 -4,1: @@ -1145,10 +1146,18 @@ entities: 1: 57344 -3,2: 1: 241 + -3,3: + 0: 52224 + -3,4: + 0: 3276 -2,1: 0: 57567 + -2,3: + 0: 65512 -2,2: 0: 61166 + -2,4: + 0: 1919 -1,4: 0: 174 1,-4: @@ -1164,11 +1173,11 @@ entities: 2,-2: 0: 61687 2,-4: - 3: 6 - 4: 1536 + 2: 6 + 3: 1536 3,-4: - 1: 8737 - 0: 256 + 0: 257 + 1: 8736 3,-3: 0: 4097 1: 8738 @@ -1206,10 +1215,9 @@ entities: -6,-2: 1: 31744 0,-5: - 1: 2048 + 0: 2048 2,-5: - 0: 512 - 1: 2048 + 0: 2560 5,0: 0: 61167 5,1: @@ -1254,21 +1262,6 @@ entities: - 0 - 0 - 0 - - volume: 2500 - temperature: 293.14975 - moles: - - 20.078888 - - 75.53487 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - - 0 - volume: 2500 temperature: 293.15 moles: @@ -1751,6 +1744,13 @@ entities: - type: Transform pos: -6.5,4.5 parent: 2 +- proto: AmmoniaCanister + entities: + - uid: 2603 + components: + - type: Transform + pos: 6.5,-14.5 + parent: 2 - proto: APCBasic entities: - uid: 56 @@ -1908,6 +1908,14 @@ entities: - type: Transform pos: -9.5,6.5 parent: 2 +- proto: BaseGasCondenser + entities: + - uid: 2562 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,13.5 + parent: 2 - proto: BaseResearchAndDevelopmentPointSource entities: - uid: 82 @@ -1979,6 +1987,17 @@ entities: parent: 2 - type: DeviceLinkSink invokeCounter: 1 + - uid: 2522 + components: + - type: Transform + pos: -10.5,14.5 + parent: 2 + - uid: 2553 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -10.5,18.5 + parent: 2 - proto: BluespaceBeaker entities: - uid: 93 @@ -1993,6 +2012,14 @@ entities: - type: Transform pos: -8.5,5.5 parent: 2 +- proto: BoxBeaker + entities: + - uid: 2591 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -4.3516417,13.478178 + parent: 2 - proto: BoxBodyBag entities: - uid: 95 @@ -3859,6 +3886,111 @@ entities: - type: Transform pos: -4.5,10.5 parent: 2 + - uid: 2523 + components: + - type: Transform + pos: -4.5,12.5 + parent: 2 + - uid: 2524 + components: + - type: Transform + pos: -4.5,13.5 + parent: 2 + - uid: 2525 + components: + - type: Transform + pos: -4.5,14.5 + parent: 2 + - uid: 2526 + components: + - type: Transform + pos: -5.5,14.5 + parent: 2 + - uid: 2527 + components: + - type: Transform + pos: -6.5,14.5 + parent: 2 + - uid: 2528 + components: + - type: Transform + pos: -7.5,14.5 + parent: 2 + - uid: 2529 + components: + - type: Transform + pos: -8.5,14.5 + parent: 2 + - uid: 2530 + components: + - type: Transform + pos: -9.5,14.5 + parent: 2 + - uid: 2531 + components: + - type: Transform + pos: -4.5,15.5 + parent: 2 + - uid: 2532 + components: + - type: Transform + pos: -4.5,16.5 + parent: 2 + - uid: 2533 + components: + - type: Transform + pos: -4.5,16.5 + parent: 2 + - uid: 2534 + components: + - type: Transform + pos: -4.5,17.5 + parent: 2 + - uid: 2535 + components: + - type: Transform + pos: -4.5,17.5 + parent: 2 + - uid: 2536 + components: + - type: Transform + pos: -4.5,18.5 + parent: 2 + - uid: 2537 + components: + - type: Transform + pos: -5.5,18.5 + parent: 2 + - uid: 2538 + components: + - type: Transform + pos: -6.5,18.5 + parent: 2 + - uid: 2539 + components: + - type: Transform + pos: -6.5,18.5 + parent: 2 + - uid: 2540 + components: + - type: Transform + pos: -7.5,18.5 + parent: 2 + - uid: 2541 + components: + - type: Transform + pos: -7.5,18.5 + parent: 2 + - uid: 2542 + components: + - type: Transform + pos: -8.5,18.5 + parent: 2 + - uid: 2543 + components: + - type: Transform + pos: -9.5,18.5 + parent: 2 - proto: CableHV entities: - uid: 461 @@ -5321,6 +5453,13 @@ entities: rot: 3.141592653589793 rad pos: -22.5,2.5 parent: 2 +- proto: CarbonDioxideCanister + entities: + - uid: 2605 + components: + - type: Transform + pos: 6.5,-13.5 + parent: 2 - proto: Carpet entities: - uid: 753 @@ -6183,6 +6322,38 @@ entities: - type: Physics canCollide: False - type: InsideEntityStorage +- proto: ClothingBeltUtilityFilled + entities: + - uid: 2584 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.719757,13.601252 + parent: 2 + - uid: 2585 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.063507,13.788882 + parent: 2 + - uid: 2586 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.136424,16.947327 + parent: 2 + - uid: 2587 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.3030906,16.947327 + parent: 2 + - uid: 2588 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.323924,9.212788 + parent: 2 - proto: ClothingEyesGlassesMeson entities: - uid: 900 @@ -6274,6 +6445,13 @@ entities: - type: Transform pos: 4.5333743,-11.7164135 parent: 2 +- proto: ClothingShoesBootsMagSci + entities: + - uid: 2595 + components: + - type: Transform + pos: -1.591155,12.411863 + parent: 2 - proto: ClothingUniformJumpsuitERTJanitor entities: - uid: 889 @@ -6399,16 +6577,24 @@ entities: parent: 2 - proto: ComputerAnalysisConsole entities: - - uid: 1801 + - uid: 1862 components: - type: Transform rot: 1.5707963267948966 rad - pos: -4.5,11.5 + pos: -4.5,10.5 + parent: 2 + - uid: 2612 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -7.5,15.5 + parent: 2 + - uid: 2613 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -7.5,17.5 parent: 2 - - type: DeviceLinkSource - linkedPorts: - 1070: - - ArtifactAnalyzerSender: ArtifactAnalyzerReceiver - proto: ComputerCargoBounty entities: - uid: 936 @@ -6502,11 +6688,11 @@ entities: parent: 2 - proto: ComputerResearchAndDevelopment entities: - - uid: 1862 + - uid: 2614 components: - type: Transform - rot: 1.5707963267948966 rad - pos: -4.5,10.5 + rot: -1.5707963267948966 rad + pos: -1.5,9.5 parent: 2 - proto: ComputerSalvageExpedition entities: @@ -7520,6 +7706,13 @@ entities: parent: 2 - type: Fixtures fixtures: {} +- proto: FrezonCanister + entities: + - uid: 2604 + components: + - type: Transform + pos: 7.5,-15.5 + parent: 2 - proto: FuelDispenser entities: - uid: 2453 @@ -7577,6 +7770,41 @@ entities: parent: 2 - type: AtmosPipeColor color: '#990000FF' + - uid: 2504 + components: + - type: Transform + pos: -6.5,10.5 + parent: 2 + - uid: 2505 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,10.5 + parent: 2 + - uid: 2568 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,14.5 + parent: 2 + - uid: 2569 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,15.5 + parent: 2 + - uid: 2570 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,17.5 + parent: 2 + - uid: 2571 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -8.5,18.5 + parent: 2 - proto: GasPipeBend entities: - uid: 1115 @@ -7989,6 +8217,12 @@ entities: parent: 2 - type: AtmosPipeColor color: '#990000FF' + - uid: 2499 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -6.5,9.5 + parent: 2 - proto: GasPipeFourway entities: - uid: 1168 @@ -9770,6 +10004,36 @@ entities: parent: 2 - type: AtmosPipeColor color: '#990000FF' + - uid: 2081 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.5,9.5 + parent: 2 + - uid: 2500 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,10.5 + parent: 2 + - uid: 2501 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -4.5,9.5 + parent: 2 + - uid: 2572 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -7.5,14.5 + parent: 2 + - uid: 2575 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -7.5,18.5 + parent: 2 - proto: GasPipeTJunction entities: - uid: 1397 @@ -10157,6 +10421,17 @@ entities: parent: 2 - type: AtmosPipeColor color: '#990000FF' + - uid: 2563 + components: + - type: Transform + pos: -7.5,17.5 + parent: 2 + - uid: 2574 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -7.5,15.5 + parent: 2 - proto: GasPort entities: - uid: 1448 @@ -10180,6 +10455,54 @@ entities: parent: 2 - type: AtmosPipeColor color: '#990000FF' + - uid: 2506 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,9.5 + parent: 2 + - uid: 2507 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -2.5,10.5 + parent: 2 + - uid: 2580 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,14.5 + parent: 2 + - uid: 2581 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,15.5 + parent: 2 + - uid: 2582 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,17.5 + parent: 2 + - uid: 2583 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,18.5 + parent: 2 + - uid: 2589 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,13.5 + parent: 2 + - uid: 2590 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -4.5,15.5 + parent: 2 - proto: GasPressurePump entities: - uid: 1451 @@ -10189,6 +10512,49 @@ entities: parent: 2 - type: AtmosPipeColor color: '#0055CCFF' + - uid: 2502 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -3.5,9.5 + parent: 2 + - uid: 2503 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.5,10.5 + parent: 2 + - uid: 2576 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,14.5 + parent: 2 + - uid: 2577 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,15.5 + parent: 2 + - uid: 2578 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,17.5 + parent: 2 + - uid: 2579 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.5,18.5 + parent: 2 +- proto: GasThermoMachineHeater + entities: + - uid: 2573 + components: + - type: Transform + pos: -4.5,16.5 + parent: 2 - proto: GasValve entities: - uid: 1452 @@ -11583,6 +11949,8 @@ entities: - type: Transform pos: -10.5,5.5 parent: 2 + - type: Lock + locked: False - proto: LockerBotanistFilled entities: - uid: 1646 @@ -11679,6 +12047,16 @@ entities: - type: Transform pos: -5.5,11.5 parent: 2 + - uid: 2564 + components: + - type: Transform + pos: -9.5,15.5 + parent: 2 + - uid: 2565 + components: + - type: Transform + pos: -9.5,17.5 + parent: 2 - proto: MachineCentrifuge entities: - uid: 1659 @@ -11764,6 +12142,31 @@ entities: - type: Transform pos: -16.322409,-2.5030437 parent: 2 + - uid: 1801 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -5.684905,14.188459 + parent: 2 + - uid: 2596 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -6.2682385,15.9501 + parent: 2 + - uid: 2597 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -3.7786553,9.476853 + parent: 2 +- proto: MusicBoxInstrument + entities: + - uid: 2594 + components: + - type: Transform + pos: -2.507822,12.370947 + parent: 2 - proto: NitrogenCanister entities: - uid: 1673 @@ -11771,6 +12174,38 @@ entities: - type: Transform pos: 11.5,-10.5 parent: 2 +- proto: NodeScanner + entities: + - uid: 2606 + components: + - type: Transform + pos: -6.543989,17.499607 + parent: 2 + - uid: 2607 + components: + - type: Transform + pos: -6.9710727,16.769932 + parent: 2 + - uid: 2608 + components: + - type: Transform + pos: -4.5335727,14.330738 + parent: 2 + - uid: 2609 + components: + - type: Transform + pos: -6.116906,14.070141 + parent: 2 + - uid: 2610 + components: + - type: Transform + pos: -2.741906,10.724067 + parent: 2 + - uid: 2611 + components: + - type: Transform + pos: -2.804406,9.82761 + parent: 2 - proto: OreProcessor entities: - uid: 1674 @@ -11854,6 +12289,40 @@ entities: rot: -1.5707963267948966 rad pos: -4.5,11.5 parent: 2 + - uid: 2554 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,15.5 + parent: 2 + - uid: 2555 + components: + - type: Transform + pos: -9.5,16.5 + parent: 2 + - uid: 2556 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,16.5 + parent: 2 + - uid: 2557 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -8.5,16.5 + parent: 2 + - uid: 2558 + components: + - type: Transform + pos: -8.5,16.5 + parent: 2 + - uid: 2559 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,17.5 + parent: 2 - proto: PlasmaWindoorSecureScienceLocked entities: - uid: 1591 @@ -11862,6 +12331,18 @@ entities: rot: 3.141592653589793 rad pos: -5.5,9.5 parent: 2 + - uid: 2560 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,14.5 + parent: 2 + - uid: 2561 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -7.5,18.5 + parent: 2 - proto: PlasticFlapsAirtightClear entities: - uid: 1687 @@ -12443,6 +12924,29 @@ entities: rot: -1.5707963267948966 rad pos: -16.5,-3.5 parent: 2 + - uid: 2544 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -9.5,14.5 + parent: 2 + - uid: 2545 + components: + - type: Transform + pos: -9.5,18.5 + parent: 2 + - uid: 2546 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,14.5 + parent: 2 + - uid: 2547 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,18.5 + parent: 2 - proto: PoweredlightLED entities: - uid: 1771 @@ -12730,6 +13234,18 @@ entities: - type: Transform pos: -6.5,11.5 parent: 2 + - uid: 2566 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -9.5,14.5 + parent: 2 + - uid: 2567 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -9.5,18.5 + parent: 2 - proto: RandomBoard entities: - uid: 1820 @@ -12939,6 +13455,30 @@ entities: - type: Transform pos: -20.53674,3.6443589 parent: 2 + - uid: 2598 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -20.501162,3.39234 + parent: 2 + - uid: 2599 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -20.501162,3.39234 + parent: 2 + - uid: 2600 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -20.501162,3.39234 + parent: 2 + - uid: 2601 + components: + - type: Transform + rot: 1.5707963267948966 rad + pos: -20.501162,3.39234 + parent: 2 - proto: ShuttersNormalOpen entities: - uid: 1849 @@ -13397,11 +13937,71 @@ entities: - type: Transform pos: -7.5,8.5 parent: 2 + - uid: 2215 + components: + - type: Transform + pos: -7.5,13.5 + parent: 2 - uid: 2269 components: - type: Transform pos: -7.5,10.5 parent: 2 + - uid: 2508 + components: + - type: Transform + pos: -8.5,13.5 + parent: 2 + - uid: 2509 + components: + - type: Transform + pos: -9.5,13.5 + parent: 2 + - uid: 2510 + components: + - type: Transform + pos: -10.5,13.5 + parent: 2 + - uid: 2514 + components: + - type: Transform + pos: -7.5,19.5 + parent: 2 + - uid: 2515 + components: + - type: Transform + pos: -9.5,19.5 + parent: 2 + - uid: 2516 + components: + - type: Transform + pos: -9.5,19.5 + parent: 2 + - uid: 2517 + components: + - type: Transform + pos: -10.5,19.5 + parent: 2 + - uid: 2518 + components: + - type: Transform + pos: -8.5,19.5 + parent: 2 + - uid: 2519 + components: + - type: Transform + pos: -7.5,19.5 + parent: 2 + - uid: 2520 + components: + - type: Transform + pos: -7.5,19.5 + parent: 2 + - uid: 2521 + components: + - type: Transform + pos: -8.5,19.5 + parent: 2 - proto: SignalButtonDirectional entities: - uid: 1946 @@ -13440,6 +14040,19 @@ entities: - Pressed: Toggle 1852: - Pressed: Toggle +- proto: SignalSwitch + entities: + - uid: 2592 + components: + - type: Transform + rot: 3.141592653589793 rad + pos: -7.5,13.5 + parent: 2 + - uid: 2593 + components: + - type: Transform + pos: -7.5,19.5 + parent: 2 - proto: SignalSwitchDirectional entities: - uid: 1802 @@ -14501,6 +15114,13 @@ entities: - type: Transform pos: -4.498433,-6.6936054 parent: 2 +- proto: TritiumCanister + entities: + - uid: 2602 + components: + - type: Transform + pos: 5.5,-15.5 + parent: 2 - proto: TwoWayLever entities: - uid: 2105 @@ -15157,11 +15777,6 @@ entities: - type: Transform pos: -16.5,-5.5 parent: 2 - - uid: 2215 - components: - - type: Transform - pos: -4.5,12.5 - parent: 2 - uid: 2216 components: - type: Transform @@ -16096,6 +16711,54 @@ entities: - type: Transform pos: 1.5,17.5 parent: 2 + - uid: 2511 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,19.5 + parent: 2 + - uid: 2512 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,18.5 + parent: 2 + - uid: 2513 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -4.5,17.5 + parent: 2 + - uid: 2548 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -6.5,19.5 + parent: 2 + - uid: 2549 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -5.5,19.5 + parent: 2 + - uid: 2550 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,15.5 + parent: 2 + - uid: 2551 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,16.5 + parent: 2 + - uid: 2552 + components: + - type: Transform + rot: -1.5707963267948966 rad + pos: -10.5,17.5 + parent: 2 - proto: WallShuttleDiagonal entities: - uid: 2409 From 5114279ef27f3241821828215e25346f8b7f24b1 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 19 Dec 2024 15:25:11 +0300 Subject: [PATCH 60/81] fix: magboots trigger art from a mile --- Content.Server/Xenoarchaeology/Artifact/XAT/XATMagnetSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/Xenoarchaeology/Artifact/XAT/XATMagnetSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XAT/XATMagnetSystem.cs index e67f0e0fc707..9a2697dc9058 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XAT/XATMagnetSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XAT/XATMagnetSystem.cs @@ -48,7 +48,7 @@ protected override void UpdateXAT(Entity artifact, Entity if (!itemToggle.Activated) continue; - if (!_transform.InRange(xform.Coordinates, Transform(artifact).Coordinates, node.Comp1.MagnetRange)) + if (!_transform.InRange(xform.Coordinates, Transform(artifact).Coordinates, node.Comp1.MagbootsRange)) continue; Trigger(artifact, node); From 5f6b3bf0dc9054ffdf1e6e2ec2570a9fbea03c1f Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 19 Dec 2024 15:26:27 +0300 Subject: [PATCH 61/81] refactor: bind artifact animation to unlocking state --- .../Artifact/RandomArtifactSpriteSystem.cs | 32 +++++------------ .../SharedXenoArtifactSystem.Unlock.cs | 34 ++++++++++++++++--- .../RandomArtifactSpriteComponent.cs | 4 +-- 3 files changed, 39 insertions(+), 31 deletions(-) diff --git a/Content.Server/Xenoarchaeology/Artifact/RandomArtifactSpriteSystem.cs b/Content.Server/Xenoarchaeology/Artifact/RandomArtifactSpriteSystem.cs index 89e72f5a5084..555f9828ea72 100644 --- a/Content.Server/Xenoarchaeology/Artifact/RandomArtifactSpriteSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/RandomArtifactSpriteSystem.cs @@ -19,26 +19,8 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnActivated); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var component, out var appearance)) - { - if (component.ActivationStart == null) - continue; - - var timeDif = _time.CurTime - component.ActivationStart.Value; - if (timeDif.Seconds >= component.ActivationTime) - { - _appearance.SetData(uid, SharedArtifactsVisuals.IsActivated, false, appearance); - component.ActivationStart = null; - } - } + SubscribeLocalEvent(UnlockingStageStarted); + SubscribeLocalEvent(UnlockingStageFinished); } private void OnMapInit(EntityUid uid, RandomArtifactSpriteComponent component, MapInitEvent args) @@ -48,9 +30,13 @@ private void OnMapInit(EntityUid uid, RandomArtifactSpriteComponent component, M _item.SetHeldPrefix(uid, "ano" + randomSprite.ToString("D2")); //set item artifact inhands } - private void OnActivated(EntityUid uid, RandomArtifactSpriteComponent component, ref ArtifactActivatedEvent args) + private void UnlockingStageStarted(Entity ent, ref ArtifactUnlockingStartedEvent args) + { + _appearance.SetData(ent, SharedArtifactsVisuals.IsActivated, true); + } + + private void UnlockingStageFinished(Entity ent, ref ArtifactUnlockingFinishedEvent args) { - _appearance.SetData(uid, SharedArtifactsVisuals.IsActivated, true); - component.ActivationStart = _time.CurTime; + _appearance.SetData(ent, SharedArtifactsVisuals.IsActivated, false); } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs index a4d23e37076c..8ae5c3a38a04 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -14,6 +14,8 @@ public abstract partial class SharedXenoArtifactSystem private void InitializeUnlock() { _unlockingQuery = GetEntityQuery(); + + SubscribeLocalEvent(OnUnlockingStarted); } /// Finish unlocking phase when the time is up. @@ -70,9 +72,6 @@ public void FinishUnlockingState(Entity ent) { RemComp(ent, ent.Comp1); + RiseUnlockingFinished(ent, null); } /// @@ -125,10 +126,33 @@ public bool TryGetNodeFromUnlockState( return node != null; } + + private void OnUnlockingStarted(Entity ent, ref MapInitEvent args) + { + var unlockingStartedEvent = new ArtifactUnlockingStartedEvent(); + RaiseLocalEvent(ent.Owner, ref unlockingStartedEvent); + } + + private void RiseUnlockingFinished( + Entity ent, + Entity? node + ) + { + var unlockingFinishedEvent = new ArtifactUnlockingFinishedEvent(node); + RaiseLocalEvent(ent.Owner, ref unlockingFinishedEvent); + } + } /// -/// Event of artifact node finishing unlocking and getting 1 or more node activated. +/// Event for starting artifact unlocking stage. +/// +[ByRefEvent] +public record struct ArtifactUnlockingStartedEvent; + +/// +/// Event for finishing artifact unlocking stage. /// +/// Node which were unlocked. Null if stage was finished without new unlocks. [ByRefEvent] -public record struct ArtifactActivatedEvent(EntityUid ArtifactUid); +public record struct ArtifactUnlockingFinishedEvent(EntityUid? UnlockedNode); diff --git a/Content.Shared/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteComponent.cs b/Content.Shared/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteComponent.cs index 77b381b0b51f..763bfa67181f 100644 --- a/Content.Shared/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteComponent.cs +++ b/Content.Shared/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Shared.Xenoarchaeology.XenoArtifacts; +namespace Content.Shared.Xenoarchaeology.XenoArtifacts; [RegisterComponent] public sealed partial class RandomArtifactSpriteComponent : Component @@ -11,6 +11,4 @@ public sealed partial class RandomArtifactSpriteComponent : Component [DataField("activationTime")] public double ActivationTime = 2.0; - - public TimeSpan? ActivationStart; } From cd3a79835a3687e3080a3f07dbe1a47fc3cb2374 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 19 Dec 2024 15:27:23 +0300 Subject: [PATCH 62/81] feat: radiation dmg now have reference to source (and artifacts won't irradiate themselves) --- Content.Server/Radiation/Systems/RadiationSystem.cs | 4 ++-- Content.Shared/Damage/Systems/DamageableSystem.cs | 2 +- Content.Shared/Radiation/Events/OnIrradiatedEvent.cs | 7 +++++-- .../Artifact/XAT/XATDamageThresholdReachedSystem.cs | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Content.Server/Radiation/Systems/RadiationSystem.cs b/Content.Server/Radiation/Systems/RadiationSystem.cs index e184c022f2f4..c54bdb2290a5 100644 --- a/Content.Server/Radiation/Systems/RadiationSystem.cs +++ b/Content.Server/Radiation/Systems/RadiationSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.Radiation.Components; +using Content.Server.Radiation.Components; using Content.Shared.Radiation.Components; using Content.Shared.Radiation.Events; using Robust.Shared.Configuration; @@ -36,7 +36,7 @@ public override void Update(float frameTime) public void IrradiateEntity(EntityUid uid, float radsPerSecond, float time) { - var msg = new OnIrradiatedEvent(time, radsPerSecond); + var msg = new OnIrradiatedEvent(time, radsPerSecond, uid); RaiseLocalEvent(uid, msg); } diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 3c3e1b736df3..b997f432ae00 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -248,7 +248,7 @@ private void OnIrradiated(EntityUid uid, DamageableComponent component, OnIrradi damage.DamageDict.Add(typeId, damageValue); } - TryChangeDamage(uid, damage, interruptsDoAfters: false); + TryChangeDamage(uid, damage, interruptsDoAfters: false, origin: args.Origin); } private void OnRejuvenate(EntityUid uid, DamageableComponent component, RejuvenateEvent args) diff --git a/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs b/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs index ee35304e2af7..00f6ed21c5dd 100644 --- a/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs +++ b/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs @@ -1,4 +1,4 @@ -namespace Content.Shared.Radiation.Events; +namespace Content.Shared.Radiation.Events; /// /// Raised on entity when it was irradiated @@ -10,11 +10,14 @@ public sealed class OnIrradiatedEvent : EntityEventArgs public readonly float RadsPerSecond; + public readonly EntityUid Origin; + public float TotalRads => RadsPerSecond * FrameTime; - public OnIrradiatedEvent(float frameTime, float radsPerSecond) + public OnIrradiatedEvent(float frameTime, float radsPerSecond, EntityUid origin) { FrameTime = frameTime; RadsPerSecond = radsPerSecond; + Origin = origin; } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageThresholdReachedSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageThresholdReachedSystem.cs index a4d669da79a7..9ba5bfe04da2 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageThresholdReachedSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/XATDamageThresholdReachedSystem.cs @@ -19,7 +19,7 @@ public override void Initialize() private void OnDamageChanged(Entity artifact, Entity node, ref DamageChangedEvent args) { - if (!args.DamageIncreased || args.DamageDelta == null) + if (!args.DamageIncreased || args.DamageDelta == null || args.Origin == artifact.Owner) return; var damageTriggerComponent = node.Comp1; From 058c0e6d565e6eb918dc05f31cce8a3354e7425c Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 19 Dec 2024 15:28:03 +0300 Subject: [PATCH 63/81] fix: random artifact node durability now is rolled for max and not current value --- .../Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index 979b79e53b5d..bf80a54f19db 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -28,7 +28,9 @@ private void InitializeNode() /// private void OnNodeMapInit(Entity ent, ref MapInitEvent args) { - SetNodeDurability((ent, ent), ent.Comp.MaxDurability - ent.Comp.MaxDurabilityCanDecreaseBy.Next(RobustRandom)); + XenoArtifactNodeComponent nodeComponent = ent; + nodeComponent.MaxDurability -= nodeComponent.MaxDurabilityCanDecreaseBy.Next(RobustRandom); + SetNodeDurability((ent, ent), nodeComponent.MaxDurability); } /// Gets node component by node entity uid. From 4b4b276344ff2c693912031aac14314b2ca6efda Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Thu, 19 Dec 2024 15:30:52 +0300 Subject: [PATCH 64/81] refactor: gas effects are more rare, hand-held artifact effects are filtered properly now, rad dmg trigger now requires only 20 dmg for activation --- .../Xenoarchaeology/item_xenoartifacts.yml | 4 +-- Resources/Prototypes/XenoArch/effects.yml | 31 +++++++------------ Resources/Prototypes/XenoArch/triggers.yml | 3 +- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml index 6724784adcb5..83cbb6f4fa7e 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml @@ -53,10 +53,10 @@ children: - !type:NestedSelector tableId: XenoArtifactEffectsDefaultTable - weight: 58 + weight: 54 - !type:NestedSelector tableId: XenoArtifactEffectsHandheldOnlyTable - weight: 3 + weight: 2 - type: entity parent: BaseXenoArtifactItem diff --git a/Resources/Prototypes/XenoArch/effects.yml b/Resources/Prototypes/XenoArch/effects.yml index 5ddd16234a61..8476d2a14ad9 100644 --- a/Resources/Prototypes/XenoArch/effects.yml +++ b/Resources/Prototypes/XenoArch/effects.yml @@ -2,10 +2,9 @@ id: XenoArtifactEffectsDefaultTable table: !type:GroupSelector children: - - id: XenoArtifactEffectUniversalIntercom - weight: 10.0 - - id: XenoArtifactStorage - weight: 10.0 + # hijacks use key, prevents from using artifact, sadly + #- id: XenoArtifactEffectUniversalIntercom + # weight: 10.0 - id: XenoArtifactSolutionStorage weight: 10.0 - id: XenoArtifactPhasing @@ -14,12 +13,8 @@ weight: 4.0 - id: XenoArtifactSpeedUp weight: 4.0 - - id: XenoArtifactDrill - weight: 6.0 - id: XenoArtifactGhost weight: 2.0 - - id: XenoArtifactMultitool - weight: 10.0 - id: XenoArtifactEffectBadFeeling weight: 10.0 - id: XenoArtifactEffectGoodFeeling @@ -29,7 +24,7 @@ - id: XenoArtifactEffectLightFlicker weight: 10.0 - id: XenoArtifactPotassiumWave - weight: 10.0 + weight: 7.0 - id: XenoArtifactFloraSpawn weight: 10.0 - id: XenoArtifactChemicalPuddle @@ -109,17 +104,17 @@ - id: XenoArtifactBoom weight: 5.0 - id: XenoArtifactEffectCreationGasPlasma - weight: 5.0 + weight: 2.0 - id: XenoArtifactEffectCreationGasTritium - weight: 5.0 + weight: 2.0 - id: XenoArtifactEffectCreationGasAmmonia - weight: 10.0 + weight: 3.0 - id: XenoArtifactEffectCreationGasFrezon weight: 1.0 - id: XenoArtifactEffectCreationGasNitrousOxide - weight: 10.0 + weight: 4.0 - id: XenoArtifactEffectCreationGasCarbonDioxide - weight: 10.0 + weight: 4.0 - type: entityTable id: XenoArtifactEffectsHandheldOnlyTable @@ -129,12 +124,10 @@ # weight 10.0 # removed until we have value-based system #- id: XenoArtifactGun # weight 4.0 #it conflicts with default interaction - it should activate artifact nodes - - id: XenoArtifactDrill - weight: 10 - id: XenoArtifactMultitool - weight: 10 - - id: XenoArtifactStorage - weight: 10 + weight: 10.0 + - id: XenoArtifactDrill + weight: 10.0 - type: entity id: BaseXenoArtifactEffect diff --git a/Resources/Prototypes/XenoArch/triggers.yml b/Resources/Prototypes/XenoArch/triggers.yml index 6d61123f402f..39bc19263bb9 100644 --- a/Resources/Prototypes/XenoArch/triggers.yml +++ b/Resources/Prototypes/XenoArch/triggers.yml @@ -34,6 +34,7 @@ components: - type: XATCompNearby requireComponentWithName: ActiveInstrument + radius: 2 - type: xenoArchTrigger id: TriggerHeat @@ -147,7 +148,7 @@ components: - type: XATDamageThresholdReached typesNeeded: - Radiation: 100 + Radiation: 20 # TODO: legendary microwave trigger - type: xenoArchTrigger From e551dc93ca0869dfddcbfb549540f32f1d235842 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Fri, 20 Dec 2024 20:52:44 +0300 Subject: [PATCH 65/81] feat: animations and sound effects for artifact force-use and failed finish of unlocking phase --- .../RandomArtifactSpriteSystem.cs | 21 ++++++++---- .../Artifact/RandomArtifactSpriteSystem.cs | 30 ++++++++++++++++-- .../Components/XenoArtifactComponent.cs | 13 ++++++++ .../XenoArtifactUnlockingComponent.cs | 16 ++++++++-- .../SharedXenoArtifactSystem.Unlock.cs | 10 +++--- .../Artifact/SharedXenoArtifactSystem.XAE.cs | 24 +++++++++++++- .../RandomArtifactSpriteComponent.cs | 4 ++- .../XenoArtifacts/SharedArtifact.cs | 3 +- .../Artifact/artifact-activation-fail1.ogg | Bin 0 -> 16443 bytes .../Artifact/artifact-force-activated1.ogg | Bin 0 -> 9845 bytes .../Audio/Items/Artifact/attributions.yml | 8 +++++ .../Xenoarchaeology/item_xenoartifacts.yml | 7 ++-- .../structure_xenoartifacts.yml | 7 ++-- .../Prototypes/SoundCollections/artifact.yml | 12 ++++++- Resources/Prototypes/XenoArch/effects.yml | 2 ++ .../artifact-activation.png | Bin 0 -> 5993 bytes .../item_artifacts.rsi/meta.json | 15 +++++++++ .../artifact-activation.png | Bin 0 -> 5993 bytes .../xeno_artifacts.rsi/meta.json | 15 +++++++++ 19 files changed, 164 insertions(+), 23 deletions(-) create mode 100644 Resources/Audio/Items/Artifact/artifact-activation-fail1.ogg create mode 100644 Resources/Audio/Items/Artifact/artifact-force-activated1.ogg create mode 100644 Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/artifact-activation.png create mode 100644 Resources/Textures/Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi/artifact-activation.png diff --git a/Content.Client/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteSystem.cs b/Content.Client/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteSystem.cs index 3be9b027484b..b02c44f920c7 100644 --- a/Content.Client/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteSystem.cs +++ b/Content.Client/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Xenoarchaeology.XenoArtifacts; +using Content.Shared.Xenoarchaeology.XenoArtifacts; using Robust.Client.GameObjects; namespace Content.Client.Xenoarchaeology.XenoArtifacts; @@ -13,19 +13,28 @@ protected override void OnAppearanceChange(EntityUid uid, RandomArtifactSpriteCo if (!AppearanceSystem.TryGetData(uid, SharedArtifactsVisuals.SpriteIndex, out var spriteIndex, args.Component)) return; + if (!AppearanceSystem.TryGetData(uid, SharedArtifactsVisuals.IsUnlocking, out var isUnlocking, args.Component)) + isUnlocking = false; + if (!AppearanceSystem.TryGetData(uid, SharedArtifactsVisuals.IsActivated, out var isActivated, args.Component)) isActivated = false; var spriteIndexStr = spriteIndex.ToString("D2"); - var spritePrefix = isActivated ? "_on" : ""; + var spritePrefix = isUnlocking ? "_on" : ""; // layered artifact sprite - if (args.Sprite.LayerMapTryGet(ArtifactsVisualLayers.Effect, out var layer)) + if (args.Sprite.LayerMapTryGet(ArtifactsVisualLayers.UnlockingEffect, out var layer)) { var spriteState = "ano" + spriteIndexStr; args.Sprite.LayerSetState(ArtifactsVisualLayers.Base, spriteState); args.Sprite.LayerSetState(layer, spriteState + "_on"); - args.Sprite.LayerSetVisible(layer, isActivated); + args.Sprite.LayerSetVisible(layer, isUnlocking); + + if (args.Sprite.LayerMapTryGet(ArtifactsVisualLayers.ActivationEffect, out var activationEffectLayer)) + { + args.Sprite.LayerSetState(activationEffectLayer, "artifact-activation"); + args.Sprite.LayerSetVisible(activationEffectLayer, isActivated); + } } // non-layered else @@ -33,12 +42,12 @@ protected override void OnAppearanceChange(EntityUid uid, RandomArtifactSpriteCo var spriteState = "ano" + spriteIndexStr + spritePrefix; args.Sprite.LayerSetState(ArtifactsVisualLayers.Base, spriteState); } - } } public enum ArtifactsVisualLayers : byte { Base, - Effect // doesn't have to use this + UnlockingEffect, // doesn't have to use this + ActivationEffect } diff --git a/Content.Server/Xenoarchaeology/Artifact/RandomArtifactSpriteSystem.cs b/Content.Server/Xenoarchaeology/Artifact/RandomArtifactSpriteSystem.cs index 555f9828ea72..645dac24e207 100644 --- a/Content.Server/Xenoarchaeology/Artifact/RandomArtifactSpriteSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/RandomArtifactSpriteSystem.cs @@ -21,6 +21,26 @@ public override void Initialize() SubscribeLocalEvent(OnMapInit); SubscribeLocalEvent(UnlockingStageStarted); SubscribeLocalEvent(UnlockingStageFinished); + SubscribeLocalEvent(ArtifactActivated); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var component, out var appearance)) + { + if (component.ActivationStart == null) + continue; + + var timeDif = _time.CurTime - component.ActivationStart.Value; + if (timeDif.Seconds >= component.ActivationTime) + { + _appearance.SetData(uid, SharedArtifactsVisuals.IsActivated, false, appearance); + component.ActivationStart = null; + } + } } private void OnMapInit(EntityUid uid, RandomArtifactSpriteComponent component, MapInitEvent args) @@ -32,11 +52,17 @@ private void OnMapInit(EntityUid uid, RandomArtifactSpriteComponent component, M private void UnlockingStageStarted(Entity ent, ref ArtifactUnlockingStartedEvent args) { - _appearance.SetData(ent, SharedArtifactsVisuals.IsActivated, true); + _appearance.SetData(ent, SharedArtifactsVisuals.IsUnlocking, true); } private void UnlockingStageFinished(Entity ent, ref ArtifactUnlockingFinishedEvent args) { - _appearance.SetData(ent, SharedArtifactsVisuals.IsActivated, false); + _appearance.SetData(ent, SharedArtifactsVisuals.IsUnlocking, false); + } + + private void ArtifactActivated(Entity ent, ref XenoArtifactActivatedEvent args) + { + _appearance.SetData(ent, SharedArtifactsVisuals.IsActivated, true); + ent.Comp.ActivationStart = _time.CurTime; } } diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index 9a084364d152..9dfc0ebd48fd 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -1,6 +1,7 @@ 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; using Robust.Shared.Prototypes; @@ -139,4 +140,16 @@ public sealed partial class XenoArtifactComponent : Component [DataField] public ProtoId TriggerWeights = "DefaultTriggers"; #endregion + + /// + /// Sound effect to be played when artifact node is force-activated. + /// + [DataField] + public SoundSpecifier? ForceActivationSoundSpecifier = new SoundCollectionSpecifier("ArtifactForceActivation") + { + Params = new() + { + Variation = 0.1f + } + }; } diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactUnlockingComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactUnlockingComponent.cs index 94e3df98c214..13b07f447418 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactUnlockingComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactUnlockingComponent.cs @@ -22,10 +22,10 @@ public sealed partial class XenoArtifactUnlockingComponent : Component public TimeSpan EndTime; /// - /// The sound that plays when an artifact is activated + /// The sound that plays when an artifact finishes unlocking successfully (with node unlocked). /// [DataField] - public SoundSpecifier ActivationSound = new SoundCollectionSpecifier("ArtifactActivation") + public SoundSpecifier UnlockActivationSuccessfulSound = new SoundCollectionSpecifier("ArtifactUnlockingActivationSuccess") { Params = new() { @@ -33,4 +33,16 @@ public sealed partial class XenoArtifactUnlockingComponent : Component Volume = 3f } }; + + /// + /// The sound that plays when artifact finishes unlocking non-successfully. + /// + [DataField] + public SoundSpecifier? UnlockActivationFailedSound = new SoundCollectionSpecifier("ArtifactUnlockActivationFailure") + { + Params = new() + { + Variation = 0.1f + } + }; } diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs index 8ae5c3a38a04..0d316536b21a 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -62,7 +62,9 @@ public bool CanUnlockNode(Entity ent) public void FinishUnlockingState(Entity ent) { string unlockAttemptResultMsg; - var artifactComponent = ent.Comp2; + XenoArtifactComponent artifactComponent = ent; + XenoArtifactUnlockingComponent unlockingComponent = ent; + if (TryGetNodeFromUnlockState(ent, out var node)) { SetNodeUnlocked((ent, artifactComponent), node.Value); @@ -70,19 +72,17 @@ public void FinishUnlockingState(Entity(artifact, out var delay) && !_useDelay.TryResetDelay((artifact, delay), true)) @@ -74,8 +75,21 @@ EntityCoordinates coordinates if (!success) { _popup.PopupClient(Loc.GetString("artifact-activation-fail"), artifact, user); + return false; } + // we raised event for each node activation, + // now we raise event for artifact itself. For animations and stuff. + var ev = new XenoArtifactActivatedEvent( + artifact, + user, + target, + coordinates + ); + RaiseLocalEvent(artifact, ref ev); + + _audio.PlayPvs(xenoArtifactComponent.ForceActivationSoundSpecifier, artifact); + return true; } @@ -133,3 +147,11 @@ public readonly record struct XenoArtifactNodeActivatedEvent( EntityUid? Target, EntityCoordinates Coordinates ); + +[ByRefEvent] +public readonly record struct XenoArtifactActivatedEvent( + Entity Artifact, + EntityUid? User, + EntityUid? Target, + EntityCoordinates Coordinates +); diff --git a/Content.Shared/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteComponent.cs b/Content.Shared/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteComponent.cs index 763bfa67181f..ff10ec628b95 100644 --- a/Content.Shared/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteComponent.cs +++ b/Content.Shared/Xenoarchaeology/XenoArtifacts/RandomArtifactSpriteComponent.cs @@ -10,5 +10,7 @@ public sealed partial class RandomArtifactSpriteComponent : Component public int MaxSprite = 14; [DataField("activationTime")] - public double ActivationTime = 2.0; + public double ActivationTime = 0.4; + + public TimeSpan? ActivationStart; } diff --git a/Content.Shared/Xenoarchaeology/XenoArtifacts/SharedArtifact.cs b/Content.Shared/Xenoarchaeology/XenoArtifacts/SharedArtifact.cs index 5c09be24f577..a434f468ebb2 100644 --- a/Content.Shared/Xenoarchaeology/XenoArtifacts/SharedArtifact.cs +++ b/Content.Shared/Xenoarchaeology/XenoArtifacts/SharedArtifact.cs @@ -6,5 +6,6 @@ namespace Content.Shared.Xenoarchaeology.XenoArtifacts; public enum SharedArtifactsVisuals : byte { SpriteIndex, - IsActivated + IsActivated, + IsUnlocking } diff --git a/Resources/Audio/Items/Artifact/artifact-activation-fail1.ogg b/Resources/Audio/Items/Artifact/artifact-activation-fail1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..fb9b7e1289b40da9ef5d41440bb8434aa2cd91e1 GIT binary patch literal 16443 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 literal 0 HcmV?d00001 diff --git a/Resources/Audio/Items/Artifact/artifact-force-activated1.ogg b/Resources/Audio/Items/Artifact/artifact-force-activated1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..9ad8cb165919f48d053edb939cf064d03c06deef GIT binary patch literal 9845 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�!2{@i^bAorBeAXrIZN62>bW%dEGS4gSxJ{+1ut`XzQnf3n9y1Ib>2$}JqKL&Zne;bqJ9OYtMWNR^J2O?=Hcd35$9w=KtRYP1 z`h5UF!zucX_dU-?5QL%YdgU+-uVpjo-w1*LzVD+@C{9!=l{wGzi@qO}_wL(uN>LPB z2;TA+Y!yC$gN|eWq7WjVPN%-1sub~f>{>DzpNqvDq3b#YFomBuO;%;821qmx7iRPI z+uz)0Kl2!#_~Soidv@=vZQtI1*|M#o?N|@a%;c-X!y{LQhe!MmJ@kM=2*Jeo^dZC0 zn@933LjcGFSlGK~u;#?#qN~{%P8ZG(xZNBpF z{tc#QX5rX26GFIgv>kAkcy!l!TBd3?uG1n^*te{mK_waYOvj zp#ywya66L{LQ0`Ij`Lm5^KL<7OI$|7H`p3>YpQ!)D%xBrOf=5o1^_<{qlyqh1+XKE zSmxPhpF4WXEqnIX>h3j#5cbF;N7hrtiny)|MNzTsr&KLk@ zn&!GxZzBc~(R`Qys1U*r!%zc|v2E+1z@M|2fS1&haDZMfyv34 zxiF0C2M_L#IOkaD9KI1u)5N*+m*l0(BYdH-=>Pd&d>4g6v0f~ed^AJk*IVXBh5$!n zk1zoMK^XFKrHaYPX%_%E=b}=rZh7ZhoIQI1&5H>NrfJw0hAu|{_^r3zvY`ogqejt| z7F_=xMn@;`@sB;a!D3g3bB-ut(r+3D(1-@vvY6yHZQ8VH)8;+IhR*9NCM z4Yo2mbTkYP8XB7*AC1is18!)PC;;HvwQ)>LPJ?rSTxSM7JzY>$b=}wZZ6t3{17J!i zJDSfi03d`c5JIY0qbX{=EU8}s04{`t1QbP~LP)5p%2idp;b#FVrQ8W%2j^lx=UgX* z{1G7pnx;ZZ`SDtY0Q}Kwue}Z-MCxHtLrv4v;&GLrF$~sZAX!lW*WJ_W5P~!S zEEY?j7~H+%tZ5igXJ>X=aDICbMul3ve$DfIB$M&qJ9Oy4NDzd3(1_yLP{!C2WE4du z0CqmVz|iO*rIeaiu3j_gC^ch_gTDSAaqxY&tgGO)XaH?~P9uZ}Ri%i8`Ez7K=l8GwTk;1Yb z9)IFVJow-})V576lkPAL!{C->*3n!EHhqJ{bpp`Pu)hZYSQIfO48uME=PQ-!e~rgu zL$+-{O`0XvYHmtVh^r_{WLf5o%pg&kZS&CxqN)K9Taq)h}>v{&;95n%+D8!vvc!hG_ttr*y8&GR$!?|Yu%>t^<1vr3WSWIF&~(WF?RLps6RC| zi>c{Z4?sXEjXwLC$JWz^e^J)64``*dMrgRj6h%?EWm#1T@M^Ut0El>e^YH-xoNVMY zsNpB~i9~3u^R1!8(D2V~VH0mG8+D8X&1Zq11XTcAv+4O~W!)Q<)*<8M$ul_l`Wf=* zqYtN3$wbmLO`#}CNl{3NQhL)EM%L{cG|k4Ee)9D*1b{j*K4s6&&0Dp4T@AyKQc7>C zB#h03%ToaMTjws0zx?vCb1}yruDfpQUt8QXY&u^+N(sZzA3T5l(q1X#O*Q_3%?W^` z@t-E*K@h%KE?36@6kXTfa&y>?WHTu<5{+9XT*er}C=&I$3(K-@nhCH?n>KCQyw~~v XW^ct03O`@}00000NkvXXu0mjf`8A*h literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/meta.json b/Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/meta.json index e6a7cb553dc2..5c1c4088fc5f 100644 --- a/Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/meta.json @@ -270,6 +270,21 @@ { "name": "ano11-inhand-right", "directions": 4 + }, + { + "name": "artifact-activation", + "delays": [ + [ + 0.100, + 0.100, + 0.100, + 0.100, + 0.100, + 0.100, + 0.100, + 0.100 + ] + ] } ] } diff --git a/Resources/Textures/Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi/artifact-activation.png b/Resources/Textures/Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi/artifact-activation.png new file mode 100644 index 0000000000000000000000000000000000000000..fd45f20d9d4e5af467ba7d49f567d6b8816c4d39 GIT binary patch literal 5993 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�!2{@i^bAorBeAXrIZN62>bW%dEGS4gSxJ{+1ut`XzQnf3n9y1Ib>2$}JqKL&Zne;bqJ9OYtMWNR^J2O?=Hcd35$9w=KtRYP1 z`h5UF!zucX_dU-?5QL%YdgU+-uVpjo-w1*LzVD+@C{9!=l{wGzi@qO}_wL(uN>LPB z2;TA+Y!yC$gN|eWq7WjVPN%-1sub~f>{>DzpNqvDq3b#YFomBuO;%;821qmx7iRPI z+uz)0Kl2!#_~Soidv@=vZQtI1*|M#o?N|@a%;c-X!y{LQhe!MmJ@kM=2*Jeo^dZC0 zn@933LjcGFSlGK~u;#?#qN~{%P8ZG(xZNBpF z{tc#QX5rX26GFIgv>kAkcy!l!TBd3?uG1n^*te{mK_waYOvj zp#ywya66L{LQ0`Ij`Lm5^KL<7OI$|7H`p3>YpQ!)D%xBrOf=5o1^_<{qlyqh1+XKE zSmxPhpF4WXEqnIX>h3j#5cbF;N7hrtiny)|MNzTsr&KLk@ zn&!GxZzBc~(R`Qys1U*r!%zc|v2E+1z@M|2fS1&haDZMfyv34 zxiF0C2M_L#IOkaD9KI1u)5N*+m*l0(BYdH-=>Pd&d>4g6v0f~ed^AJk*IVXBh5$!n zk1zoMK^XFKrHaYPX%_%E=b}=rZh7ZhoIQI1&5H>NrfJw0hAu|{_^r3zvY`ogqejt| z7F_=xMn@;`@sB;a!D3g3bB-ut(r+3D(1-@vvY6yHZQ8VH)8;+IhR*9NCM z4Yo2mbTkYP8XB7*AC1is18!)PC;;HvwQ)>LPJ?rSTxSM7JzY>$b=}wZZ6t3{17J!i zJDSfi03d`c5JIY0qbX{=EU8}s04{`t1QbP~LP)5p%2idp;b#FVrQ8W%2j^lx=UgX* z{1G7pnx;ZZ`SDtY0Q}Kwue}Z-MCxHtLrv4v;&GLrF$~sZAX!lW*WJ_W5P~!S zEEY?j7~H+%tZ5igXJ>X=aDICbMul3ve$DfIB$M&qJ9Oy4NDzd3(1_yLP{!C2WE4du z0CqmVz|iO*rIeaiu3j_gC^ch_gTDSAaqxY&tgGO)XaH?~P9uZ}Ri%i8`Ez7K=l8GwTk;1Yb z9)IFVJow-})V576lkPAL!{C->*3n!EHhqJ{bpp`Pu)hZYSQIfO48uME=PQ-!e~rgu zL$+-{O`0XvYHmtVh^r_{WLf5o%pg&kZS&CxqN)K9Taq)h}>v{&;95n%+D8!vvc!hG_ttr*y8&GR$!?|Yu%>t^<1vr3WSWIF&~(WF?RLps6RC| zi>c{Z4?sXEjXwLC$JWz^e^J)64``*dMrgRj6h%?EWm#1T@M^Ut0El>e^YH-xoNVMY zsNpB~i9~3u^R1!8(D2V~VH0mG8+D8X&1Zq11XTcAv+4O~W!)Q<)*<8M$ul_l`Wf=* zqYtN3$wbmLO`#}CNl{3NQhL)EM%L{cG|k4Ee)9D*1b{j*K4s6&&0Dp4T@AyKQc7>C zB#h03%ToaMTjws0zx?vCb1}yruDfpQUt8QXY&u^+N(sZzA3T5l(q1X#O*Q_3%?W^` z@t-E*K@h%KE?36@6kXTfa&y>?WHTu<5{+9XT*er}C=&I$3(K-@nhCH?n>KCQyw~~v XW^ct03O`@}00000NkvXXu0mjf`8A*h literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi/meta.json b/Resources/Textures/Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi/meta.json index 9550a7217d85..797abd80eea6 100644 --- a/Resources/Textures/Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi/meta.json @@ -586,6 +586,21 @@ 0.10599999 ] ] + }, + { + "name": "artifact-activation", + "delays": [ + [ + 0.100, + 0.100, + 0.100, + 0.100, + 0.100, + 0.100, + 0.100, + 0.100 + ] + ] } ] } From ca6891fd15bd517d6f31063acfa339c3a45c6b36 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sat, 21 Dec 2024 16:32:58 +0300 Subject: [PATCH 66/81] use only 1 file with art use animation --- .../Xenoarchaeology/item_xenoartifacts.yml | 1 + .../item_artifacts.rsi/artifact-activation.png | Bin 5993 -> 0 bytes .../Xenoarchaeology/item_artifacts.rsi/meta.json | 15 --------------- 3 files changed, 1 insertion(+), 15 deletions(-) delete mode 100644 Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/artifact-activation.png diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml index 8ffab39358c3..00c6abf362fb 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/item_xenoartifacts.yml @@ -16,6 +16,7 @@ map: [ "enum.ArtifactsVisualLayers.UnlockingEffect" ] visible: false - state: artifact-activation + sprite: Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi map: [ "enum.ArtifactsVisualLayers.ActivationEffect" ] visible: false - type: Physics diff --git a/Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/artifact-activation.png b/Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/artifact-activation.png deleted file mode 100644 index fd45f20d9d4e5af467ba7d49f567d6b8816c4d39..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5993 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�!2{@i^bAorBeAXrIZN62>bW%dEGS4gSxJ{+1ut`XzQnf3n9y1Ib>2$}JqKL&Zne;bqJ9OYtMWNR^J2O?=Hcd35$9w=KtRYP1 z`h5UF!zucX_dU-?5QL%YdgU+-uVpjo-w1*LzVD+@C{9!=l{wGzi@qO}_wL(uN>LPB z2;TA+Y!yC$gN|eWq7WjVPN%-1sub~f>{>DzpNqvDq3b#YFomBuO;%;821qmx7iRPI z+uz)0Kl2!#_~Soidv@=vZQtI1*|M#o?N|@a%;c-X!y{LQhe!MmJ@kM=2*Jeo^dZC0 zn@933LjcGFSlGK~u;#?#qN~{%P8ZG(xZNBpF z{tc#QX5rX26GFIgv>kAkcy!l!TBd3?uG1n^*te{mK_waYOvj zp#ywya66L{LQ0`Ij`Lm5^KL<7OI$|7H`p3>YpQ!)D%xBrOf=5o1^_<{qlyqh1+XKE zSmxPhpF4WXEqnIX>h3j#5cbF;N7hrtiny)|MNzTsr&KLk@ zn&!GxZzBc~(R`Qys1U*r!%zc|v2E+1z@M|2fS1&haDZMfyv34 zxiF0C2M_L#IOkaD9KI1u)5N*+m*l0(BYdH-=>Pd&d>4g6v0f~ed^AJk*IVXBh5$!n zk1zoMK^XFKrHaYPX%_%E=b}=rZh7ZhoIQI1&5H>NrfJw0hAu|{_^r3zvY`ogqejt| z7F_=xMn@;`@sB;a!D3g3bB-ut(r+3D(1-@vvY6yHZQ8VH)8;+IhR*9NCM z4Yo2mbTkYP8XB7*AC1is18!)PC;;HvwQ)>LPJ?rSTxSM7JzY>$b=}wZZ6t3{17J!i zJDSfi03d`c5JIY0qbX{=EU8}s04{`t1QbP~LP)5p%2idp;b#FVrQ8W%2j^lx=UgX* z{1G7pnx;ZZ`SDtY0Q}Kwue}Z-MCxHtLrv4v;&GLrF$~sZAX!lW*WJ_W5P~!S zEEY?j7~H+%tZ5igXJ>X=aDICbMul3ve$DfIB$M&qJ9Oy4NDzd3(1_yLP{!C2WE4du z0CqmVz|iO*rIeaiu3j_gC^ch_gTDSAaqxY&tgGO)XaH?~P9uZ}Ri%i8`Ez7K=l8GwTk;1Yb z9)IFVJow-})V576lkPAL!{C->*3n!EHhqJ{bpp`Pu)hZYSQIfO48uME=PQ-!e~rgu zL$+-{O`0XvYHmtVh^r_{WLf5o%pg&kZS&CxqN)K9Taq)h}>v{&;95n%+D8!vvc!hG_ttr*y8&GR$!?|Yu%>t^<1vr3WSWIF&~(WF?RLps6RC| zi>c{Z4?sXEjXwLC$JWz^e^J)64``*dMrgRj6h%?EWm#1T@M^Ut0El>e^YH-xoNVMY zsNpB~i9~3u^R1!8(D2V~VH0mG8+D8X&1Zq11XTcAv+4O~W!)Q<)*<8M$ul_l`Wf=* zqYtN3$wbmLO`#}CNl{3NQhL)EM%L{cG|k4Ee)9D*1b{j*K4s6&&0Dp4T@AyKQc7>C zB#h03%ToaMTjws0zx?vCb1}yruDfpQUt8QXY&u^+N(sZzA3T5l(q1X#O*Q_3%?W^` z@t-E*K@h%KE?36@6kXTfa&y>?WHTu<5{+9XT*er}C=&I$3(K-@nhCH?n>KCQyw~~v XW^ct03O`@}00000NkvXXu0mjf`8A*h diff --git a/Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/meta.json b/Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/meta.json index 5c1c4088fc5f..e6a7cb553dc2 100644 --- a/Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Xenoarchaeology/item_artifacts.rsi/meta.json @@ -270,21 +270,6 @@ { "name": "ano11-inhand-right", "directions": 4 - }, - { - "name": "artifact-activation", - "delays": [ - [ - 0.100, - 0.100, - 0.100, - 0.100, - 0.100, - 0.100, - 0.100, - 0.100 - ] - ] } ] } From f509ed25d49bc40b36936960afddad1c7e80c808 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sat, 21 Dec 2024 22:31:36 +0300 Subject: [PATCH 67/81] refactor: minor artifact dmg triggers tuning --- Resources/Prototypes/XenoArch/triggers.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/XenoArch/triggers.yml b/Resources/Prototypes/XenoArch/triggers.yml index 39bc19263bb9..5e70843c1da6 100644 --- a/Resources/Prototypes/XenoArch/triggers.yml +++ b/Resources/Prototypes/XenoArch/triggers.yml @@ -45,7 +45,7 @@ triggerOnHigherTemp: true - type: XATDamageThresholdReached typesNeeded: - Heat: 50 + Heat: 20 # This kinda trivializes the difficulty. # - type: XATToolUse # requiredTool: Welding @@ -61,7 +61,7 @@ triggerOnHigherTemp: false - type: XATDamageThresholdReached typesNeeded: - Cold: 50 + Cold: 20 - type: xenoArchTrigger id: TriggerNoOxygen @@ -177,7 +177,7 @@ components: - type: XATDamageThresholdReached groupsNeeded: - Brute: 150 + Brute: 20 - type: xenoArchTrigger id: TriggerInteraction From 65727adab1dfd5ff7fd9f2b711cc2ca71d726be6 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sat, 21 Dec 2024 22:32:59 +0300 Subject: [PATCH 68/81] feat: now nodes that CAN be unlocked are displayed with specific color in console. --- .../Ui/XenoArtifactGraphControl.xaml.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs index 619109c04561..d5bc6770a1b0 100644 --- a/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs +++ b/Content.Client/Xenoarchaeology/Ui/XenoArtifactGraphControl.xaml.cs @@ -52,6 +52,7 @@ public XenoArtifactGraphControl() public Color ActiveNodeColor { get; set; } = Color.Plum; public Color UnlockedNodeColor { get; set; } = Color.White; public Color HoveredNodeColor { get; set; } = Color.DimGray; + public Color UnlockableNodeColor { get; set; } = Color.LightSlateGray; public void SetArtifact(Entity? artifact) { @@ -82,10 +83,11 @@ protected override void Draw(DrawingHandleScreen handle) _hoveredNode = null; if (_artifact == null) return; + var artifact = _artifact.Value; - var maxDepth = _artifactSystem.GetAllNodes(_artifact.Value) + var maxDepth = _artifactSystem.GetAllNodes(artifact) .Max(s => s.Comp.Depth); - var segments = _artifactSystem.GetSegments(_artifact.Value); + var segments = _artifactSystem.GetSegments(artifact); var bottomLeft = Position // the position + new Vector2(0, Size.Y * UIScale) // the scaled height of the control @@ -120,7 +122,7 @@ protected override void Draw(DrawingHandleScreen handle) // selecting color for node based on its state var node = nodes[i]; var color = LockedNodeColor; - if (_artifactSystem.IsNodeActive(_artifact.Value, node)) + if (_artifactSystem.IsNodeActive(artifact, node)) { color = ActiveNodeColor; } @@ -128,6 +130,14 @@ protected override void Draw(DrawingHandleScreen handle) { color = UnlockedNodeColor; } + else + { + var directPredecessorNodes = _artifactSystem.GetDirectPredecessorNodes((artifact, artifact), node); + if (directPredecessorNodes.Count == 0 || directPredecessorNodes.All(x => !x.Comp.Locked)) + { + color = UnlockableNodeColor; + } + } var pos = GetNodePos(node, ySpacing, segments, ref bottomLeft); var hovered = (cursor - pos).LengthSquared() <= NodeRadius * NodeRadius; @@ -151,7 +161,7 @@ protected override void Draw(DrawingHandleScreen handle) foreach (var node in segment) { var from = GetNodePos(node, ySpacing, segments, ref bottomLeft) + new Vector2(0, -NodeRadius); - var successorNodes = _artifactSystem.GetDirectSuccessorNodes((_artifact.Value, _artifact.Value.Comp), node); + var successorNodes = _artifactSystem.GetDirectSuccessorNodes((artifact, artifact), node); foreach (var successorNode in successorNodes) { var color = node.Comp.Locked From ec338d4a8deef995a3ded3f0ac1b212e7e9b2b8e Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sat, 21 Dec 2024 23:46:13 +0300 Subject: [PATCH 69/81] feat: now unlocking stage time is dynamic and it depends on amount of triggers player activated correctly. Failed one stops incrementing --- .../Artifact/Components/XenoArtifactComponent.cs | 10 ++++++++-- .../Artifact/SharedXenoArtifactSystem.XAT.cs | 16 +++++++++++++++- .../Artifact/XAT/BaseXATSystem.cs | 6 ++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs index 9dfc0ebd48fd..fb118602c77f 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/Components/XenoArtifactComponent.cs @@ -59,10 +59,16 @@ public sealed partial class XenoArtifactComponent : Component #region Unlocking /// - /// How long does the unlocking state last. + /// How long does the unlocking state last by default. /// [DataField] - public TimeSpan UnlockStateDuration = TimeSpan.FromSeconds(10); + public TimeSpan UnlockStateDuration = TimeSpan.FromSeconds(6); + + /// + /// By how much unlocking state should be prolonged for each node that was unlocked. + /// + [DataField] + public TimeSpan UnlockStateIncrementPerNode = TimeSpan.FromSeconds(5); /// /// Minimum waiting time between unlock states. diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs index e8fe49c0ba11..863f56134bd3 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs @@ -1,3 +1,4 @@ +using System.Linq; using Content.Shared.Chemistry; using Content.Shared.Damage; using Content.Shared.Examine; @@ -62,6 +63,8 @@ public void TriggerXenoArtifact(Entity ent, Entity(ent); @@ -71,7 +74,18 @@ public void TriggerXenoArtifact(Entity ent, Entity predecessorNodeIndices.Contains(x) || successorNodeIndices.Contains(x) + ) + ) + // we add time on each new trigger, if it is not going to fail us + unlockingComp.EndTime += ent.Comp.UnlockStateIncrementPerNode; + } if (unlockingComp.TriggeredNodeIndexes.Add(index)) { diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs index ed1a9729de4a..647efc79f328 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAT/BaseXATSystem.cs @@ -49,8 +49,10 @@ protected bool CanTrigger(Entity artifact, Entity artifact, Entity node) { - if (Timing.IsFirstTimePredicted) - Log.Debug($"Activated trigger {typeof(T).Name} on node {ToPrettyString(node)} for {ToPrettyString(artifact)}"); + if (!Timing.IsFirstTimePredicted) + return; + + Log.Debug($"Activated trigger {typeof(T).Name} on node {ToPrettyString(node)} for {ToPrettyString(artifact)}"); XenoArtifact.TriggerXenoArtifact(artifact, (node.Owner, node.Comp2)); } From eb4a02dd4ab7056b9dfcb2ed716e23dcf6bf29d5 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Mon, 23 Dec 2024 00:06:15 +0300 Subject: [PATCH 70/81] feat: now non-active unlocked nodes return more points if durability was not wasted --- .../Artifact/SharedXenoArtifactSystem.Node.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index bf80a54f19db..e4c1a1bae559 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -378,15 +378,22 @@ List>> otherSegments /// public void UpdateNodeResearchValue(Entity node) { - if (node.Comp.Attached == null) + XenoArtifactNodeComponent nodeComponent = node; + if (nodeComponent.Attached == null) { - node.Comp.ResearchValue = 0; + nodeComponent.ResearchValue = 0; return; } - var artifact = _xenoArtifactQuery.Get(GetEntity(node.Comp.Attached.Value)); + var artifact = _xenoArtifactQuery.Get(GetEntity(nodeComponent.Attached.Value)); - var durabilityPenalty = 1f - MathF.Pow((float) node.Comp.Durability / node.Comp.MaxDurability, 2); - node.Comp.ResearchValue = (int) (Math.Pow(1.25, GetPredecessorNodes((artifact, artifact), node).Count) * node.Comp.BasePointValue * durabilityPenalty); + var nonactiveNodes = GetActiveNodes(artifact); + var durabilityEffect = MathF.Pow((float)nodeComponent.Durability / nodeComponent.MaxDurability, 2); + var durabilityMultiplier = nonactiveNodes.Contains(node) + ? 1f - durabilityEffect + : 1f + durabilityEffect; + + var predecessorNodes = GetPredecessorNodes((artifact, artifact), node); + nodeComponent.ResearchValue = (int)(Math.Pow(1.25, predecessorNodes.Count) * nodeComponent.BasePointValue * durabilityMultiplier); } } From aed1d51bf3136d532aba0980f18ca277d7683604 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Tue, 24 Dec 2024 21:33:51 +0300 Subject: [PATCH 71/81] feat: now puddle/foam effects change description of node --- .../Components/XAECreatePuddleComponent.cs | 7 +++++ .../XAE/Components/XAEFoamComponent.cs | 7 +++++ .../Artifact/XAE/XAECreatePuddleSystem.cs | 31 ++++++++++++++++--- .../Artifact/XAE/XAEFoamSystem.cs | 11 +++++++ .../en-US/xenoarchaeology/artifact-hints.ftl | 4 +++ Resources/Prototypes/XenoArch/effects.yml | 5 +++ 6 files changed, 61 insertions(+), 4 deletions(-) diff --git a/Content.Server/Xenoarchaeology/Artifact/XAE/Components/XAECreatePuddleComponent.cs b/Content.Server/Xenoarchaeology/Artifact/XAE/Components/XAECreatePuddleComponent.cs index 3d66924b37c7..4b807ba0b523 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XAE/Components/XAECreatePuddleComponent.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XAE/Components/XAECreatePuddleComponent.cs @@ -34,4 +34,11 @@ public sealed partial class XAECreatePuddleComponent : Component /// and are picked from . /// public List>? SelectedChemicals; + + /// + /// Marker, if entity where this component is placed should have description replaced with selected chemicals + /// on component init. + /// + [DataField] + public bool ReplaceDescription; } diff --git a/Content.Server/Xenoarchaeology/Artifact/XAE/Components/XAEFoamComponent.cs b/Content.Server/Xenoarchaeology/Artifact/XAE/Components/XAEFoamComponent.cs index 6adb6d36853b..7e5069d3c22a 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XAE/Components/XAEFoamComponent.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XAE/Components/XAEFoamComponent.cs @@ -45,5 +45,12 @@ public sealed partial class XAEFoamComponent : Component /// [DataField, ViewVariables(VVAccess.ReadWrite)] public int MaxFoamAmount = 20; + + /// + /// Marker, if entity where this component is placed should have description replaced with selected chemicals + /// on component init. + /// + [DataField] + public bool ReplaceDescription; } diff --git a/Content.Server/Xenoarchaeology/Artifact/XAE/XAECreatePuddleSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XAE/XAECreatePuddleSystem.cs index 81472d30fc58..183eccfbfa96 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XAE/XAECreatePuddleSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XAE/XAECreatePuddleSystem.cs @@ -1,7 +1,6 @@ using Content.Server.Fluids.EntitySystems; using Content.Server.Xenoarchaeology.Artifact.XAE.Components; using Content.Shared.Chemistry.Reagent; -using Content.Shared.Random.Helpers; using Content.Shared.Xenoarchaeology.Artifact; using Content.Shared.Xenoarchaeology.Artifact.XAE; using Robust.Shared.Prototypes; @@ -13,12 +12,17 @@ public sealed class XAECreatePuddleSystem: BaseXAESystem - protected override void OnActivated(Entity ent, ref XenoArtifactNodeActivatedEvent args) + public override void Initialize() { - var component = ent.Comp; + SubscribeLocalEvent(OnInit); + } + private void OnInit(EntityUid uid, XAECreatePuddleComponent component, MapInitEvent _) + { if (component.SelectedChemicals == null) { var chemicalList = new List>(); @@ -31,8 +35,27 @@ protected override void OnActivated(Entity ent, ref Xe component.SelectedChemicals = chemicalList; } + if (component.ReplaceDescription) + { + var reagentNames = new HashSet(); + foreach (var chemProtoId in component.SelectedChemicals) + { + var reagent = _prototypeManager.Index(chemProtoId); + reagentNames.Add(reagent.LocalizedName); + } + + var reagentNamesStr = string.Join(", ", reagentNames); + var newEntityDescription = Loc.GetString("xenoarch-effect-puddle", ("reagent", reagentNamesStr)); + _metaData.SetEntityDescription(uid, newEntityDescription); + } + } + + /// + protected override void OnActivated(Entity ent, ref XenoArtifactNodeActivatedEvent args) + { + var component = ent.Comp; var amountPerChem = component.ChemicalSolution.MaxVolume / component.ChemAmount; - foreach (var reagent in component.SelectedChemicals) + foreach (var reagent in component.SelectedChemicals!) { component.ChemicalSolution.AddReagent(reagent, amountPerChem); } diff --git a/Content.Server/Xenoarchaeology/Artifact/XAE/XAEFoamSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XAE/XAEFoamSystem.cs index 932b7c879406..3ce1ad03c64e 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XAE/XAEFoamSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XAE/XAEFoamSystem.cs @@ -2,8 +2,10 @@ using Content.Server.Xenoarchaeology.Artifact.XAE.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reaction; +using Content.Shared.Chemistry.Reagent; using Content.Shared.Xenoarchaeology.Artifact; using Content.Shared.Xenoarchaeology.Artifact.XAE; +using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.Xenoarchaeology.Artifact.XAE; @@ -12,6 +14,8 @@ public sealed class XAEFoamSystem : BaseXAESystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SmokeSystem _smoke = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager= default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; /// public override void Initialize() @@ -30,6 +34,13 @@ private void OnMapInit(EntityUid uid, XAEFoamComponent component, MapInitEvent a return; component.SelectedReagent = _random.Pick(component.Reagents); + + if (component.ReplaceDescription) + { + var reagent = _prototypeManager.Index(component.SelectedReagent); + var newEntityDescription = Loc.GetString("xenoarch-effect-foam", ("reagent", reagent.LocalizedName)); + _metaData.SetEntityDescription(uid, newEntityDescription); + } } /// diff --git a/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl b/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl index affba488ecdc..23abad3383b1 100644 --- a/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl +++ b/Resources/Locale/en-US/xenoarchaeology/artifact-hints.ftl @@ -75,3 +75,7 @@ xenoarch-trigger-examine-prying = There's a panel coming up from the surface. xenoarch-trigger-examine-screwing = There's a raised section with a small inset on it. xenoarch-trigger-examine-pulsing = An exposed diode pokes out of the artifact's surface. xenoarch-trigger-examine-timer = Carvings and scratches cover the surface... You can just barely make out a number: [italic]{$time}[/italic] + +### Effects hints +xenoarch-effect-puddle = Produces puddle of following reagents: {$reagent} +xenoarch-effect-foam = Produces foam of following reagents: {$reagent} diff --git a/Resources/Prototypes/XenoArch/effects.yml b/Resources/Prototypes/XenoArch/effects.yml index f11f16273dc8..e21020068d84 100644 --- a/Resources/Prototypes/XenoArch/effects.yml +++ b/Resources/Prototypes/XenoArch/effects.yml @@ -520,6 +520,7 @@ description: Produces puddle of chemical mixture # todo: make description say what exact chemical is produced, maybe add mixes into possible chemicals components: - type: XAECreatePuddle + replaceDescription: true chemicalSolution: maxVol: 500 canReact: false @@ -575,6 +576,7 @@ description: Produces chemical foam # todo: separate in 1 for each chemical for description? actually sounds like a very good idea components: - type: XAEFoam + replaceDescription: true reagents: - Oxygen - Plasma @@ -835,6 +837,7 @@ description: Creates wave of helpful foam components: - type: XAEFoam + replaceDescription: true reagents: - Dermaline - Arithrazine @@ -852,6 +855,7 @@ - type: XAEFoam minFoamAmount: 20 maxFoamAmount: 30 + replaceDescription: true reagents: - Tritium - Plasma @@ -871,6 +875,7 @@ description: Creates puddle of helpful chemicals components: - type: XAECreatePuddle + replaceDescription: true chemicalSolution: maxVol: 500 canReact: false From 24a4aaefbe2bc656f39ae1b90a82668b78da6ba3 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Wed, 25 Dec 2024 10:08:00 +0300 Subject: [PATCH 72/81] fix: fix test failure --- .../Xenoarchaeology/Artifact/XAE/XAECreatePuddleSystem.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Content.Server/Xenoarchaeology/Artifact/XAE/XAECreatePuddleSystem.cs b/Content.Server/Xenoarchaeology/Artifact/XAE/XAECreatePuddleSystem.cs index 183eccfbfa96..44c3830e05e5 100644 --- a/Content.Server/Xenoarchaeology/Artifact/XAE/XAECreatePuddleSystem.cs +++ b/Content.Server/Xenoarchaeology/Artifact/XAE/XAECreatePuddleSystem.cs @@ -23,6 +23,11 @@ public override void Initialize() private void OnInit(EntityUid uid, XAECreatePuddleComponent component, MapInitEvent _) { + if (component.PossibleChemicals == null || component.PossibleChemicals.Count == 0) + { + return; + } + if (component.SelectedChemicals == null) { var chemicalList = new List>(); From 917d66a078597e78ad35d6a95f62d6d3c555060b Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sun, 5 Jan 2025 01:07:44 +0300 Subject: [PATCH 73/81] refactor: renamed phasing effect, fixed failing test for elkridge --- ...{XAEPhasingComponent.cs => XAERemoveCollisionComponent.cs} | 4 ++-- .../XAE/{XAEPhasingSystem.cs => XAERemoveCollisionSystem.cs} | 4 ++-- Resources/Maps/elkridge.yml | 1 - Resources/Prototypes/XenoArch/effects.yml | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) rename Content.Shared/Xenoarchaeology/Artifact/XAE/Components/{XAEPhasingComponent.cs => XAERemoveCollisionComponent.cs} (64%) rename Content.Shared/Xenoarchaeology/Artifact/XAE/{XAEPhasingSystem.cs => XAERemoveCollisionSystem.cs} (71%) diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAE/Components/XAEPhasingComponent.cs b/Content.Shared/Xenoarchaeology/Artifact/XAE/Components/XAERemoveCollisionComponent.cs similarity index 64% rename from Content.Shared/Xenoarchaeology/Artifact/XAE/Components/XAEPhasingComponent.cs rename to Content.Shared/Xenoarchaeology/Artifact/XAE/Components/XAERemoveCollisionComponent.cs index 72a2d666ab6e..4a548860af9e 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAE/Components/XAEPhasingComponent.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAE/Components/XAERemoveCollisionComponent.cs @@ -4,7 +4,7 @@ namespace Content.Shared.Xenoarchaeology.Artifact.XAE.Components; /// Removes the masks/layers of hard fixtures from the artifact when added, allowing it to pass through walls /// and such. /// -[RegisterComponent, Access(typeof(XAEPhasingSystem))] -public sealed partial class XAEPhasingComponent : Component +[RegisterComponent, Access(typeof(XAERemoveCollisionSystem))] +public sealed partial class XAERemoveCollisionComponent : Component { } diff --git a/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEPhasingSystem.cs b/Content.Shared/Xenoarchaeology/Artifact/XAE/XAERemoveCollisionSystem.cs similarity index 71% rename from Content.Shared/Xenoarchaeology/Artifact/XAE/XAEPhasingSystem.cs rename to Content.Shared/Xenoarchaeology/Artifact/XAE/XAERemoveCollisionSystem.cs index 8960279383c0..e11e39c28b72 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/XAE/XAEPhasingSystem.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/XAE/XAERemoveCollisionSystem.cs @@ -4,12 +4,12 @@ namespace Content.Shared.Xenoarchaeology.Artifact.XAE; -public sealed class XAEPhasingSystem : BaseXAESystem +public sealed class XAERemoveCollisionSystem : BaseXAESystem { [Dependency] private readonly SharedPhysicsSystem _physics = default!; /// - protected override void OnActivated(Entity ent, ref XenoArtifactNodeActivatedEvent args) + protected override void OnActivated(Entity ent, ref XenoArtifactNodeActivatedEvent args) { if (!TryComp(ent.Owner, out var fixtures)) return; diff --git a/Resources/Maps/elkridge.yml b/Resources/Maps/elkridge.yml index 8bdfc5925f70..c42e25ec722b 100644 --- a/Resources/Maps/elkridge.yml +++ b/Resources/Maps/elkridge.yml @@ -87417,7 +87417,6 @@ entities: - type: Transform pos: -23.5,25.5 parent: 2 - - type: BiasedArtifact - proto: SimpleXenoArtifactItem entities: - uid: 15286 diff --git a/Resources/Prototypes/XenoArch/effects.yml b/Resources/Prototypes/XenoArch/effects.yml index e21020068d84..c0aeacca312d 100644 --- a/Resources/Prototypes/XenoArch/effects.yml +++ b/Resources/Prototypes/XenoArch/effects.yml @@ -201,7 +201,7 @@ parent: BaseXenoArtifactEffect description: Becomes phased components: - - type: XAEPhasing + - type: XAERemoveCollision - type: entity id: XenoArtifactWandering From e2fbb6190e11289c26ddae50b5f30d869fca59aa Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sun, 12 Jan 2025 17:09:26 +0300 Subject: [PATCH 74/81] minor balance changes --- Resources/Prototypes/XenoArch/effects.yml | 167 +++++++++++++++++----- 1 file changed, 132 insertions(+), 35 deletions(-) diff --git a/Resources/Prototypes/XenoArch/effects.yml b/Resources/Prototypes/XenoArch/effects.yml index d260cedbf8f4..6be9dd65151c 100644 --- a/Resources/Prototypes/XenoArch/effects.yml +++ b/Resources/Prototypes/XenoArch/effects.yml @@ -29,7 +29,7 @@ weight: 10.0 - id: XenoArtifactChemicalPuddle weight: 10.0 - - id: XenoArtifactThrow + - id: XenoArtifactThrowThingsAround weight: 10.0 - id: XenoArtifactColdWave weight: 10.0 @@ -78,15 +78,19 @@ - id: XenoArtifactEmp weight: 2.0 - id: XenoArtifactPolyMonkey - weight: 6.0 + weight: 2.0 - id: XenoArtifactPolyLuminous - weight: 6.0 + weight: 2.0 - id: XenoArtifactPolyLizard - weight: 6.0 + weight: 2.0 - id: XenoArtifactRadioactiveStrong weight: 3.0 - - id: XenoArtifactMaterialSpawn - weight: 10.0 + - id: XenoArtifactMaterialSpawnGlass + weight: 3.3 + - id: XenoArtifactMaterialSpawnSteel + weight: 3.3 + - id: XenoArtifactMaterialSpawnPlastic + weight: 3.3 - id: XenoArtifactPortal weight: 2.0 - id: XenoArtifactArtifactSpawn @@ -124,7 +128,7 @@ # weight 10.0 # removed until we have value-based system #- id: XenoArtifactGun # weight 4.0 #it conflicts with default interaction - it should activate artifact nodes - - id: XenoArtifactMultitool + - id: XenoArtifactOmnitool weight: 10.0 - id: XenoArtifactDrill weight: 10.0 @@ -141,8 +145,24 @@ group: XenoArtifactNode - type: entity - id: XenoArtifactEffectUniversalIntercom + id: BaseOneTimeXenoArtifactEffect parent: BaseXenoArtifactEffect + name: one-time-effect + description: Unknown + categories: [ HideSpawnMenu ] + abstract: true + components: + - type: XenoArtifactNode + maxDurability: 1 + maxDurabilityCanDecreaseBy: + min: 0 + max: 0 + - type: NameIdentifier + group: XenoArtifactNode + +- type: entity + id: XenoArtifactEffectUniversalIntercom + parent: BaseOneTimeXenoArtifactEffect description: Obtains ability of long-distance communication device components: - type: XAEApplyComponents @@ -171,7 +191,7 @@ - type: entity id: XenoArtifactBecomeRandomInstrument - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Obtains ability of musical instrument components: - type: XAEApplyComponents @@ -184,7 +204,7 @@ - type: entity id: XenoArtifactStorage - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Obtains ability of hidden storage components: - type: XAEApplyComponents @@ -198,14 +218,14 @@ - type: entity id: XenoArtifactPhasing - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Becomes phased components: - type: XAERemoveCollision - type: entity id: XenoArtifactWandering - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Starts to move sporadically components: - type: XAEApplyComponents @@ -218,7 +238,7 @@ - type: entity id: XenoArtifactSolutionStorage - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Obtains ability of container for chemical solutions components: - type: XAEApplyComponents @@ -246,7 +266,7 @@ - type: entity id: XenoArtifactSpeedUp - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Improves holder movement speed components: - type: XAEApplyComponents @@ -257,7 +277,7 @@ - type: entity id: XenoArtifactDrill - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Obtains ability of drill components: - type: XAEApplyComponents @@ -273,7 +293,7 @@ - type: entity id: XenoArtifactGenerateEnergy - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect # todo - increment power, but only once per node description: Produces power components: - type: XAEApplyComponents @@ -289,7 +309,7 @@ - type: entity id: XenoArtifactGun - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Obtains ability of firearm components: - type: XAEApplyComponents @@ -320,7 +340,7 @@ - type: entity id: XenoArtifactGhost - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Becomes sentient components: - type: XAEApplyComponents @@ -342,8 +362,8 @@ baseSprintSpeed: 0.5 - type: entity - id: XenoArtifactMultitool - parent: BaseXenoArtifactEffect + id: XenoArtifactOmnitool + parent: BaseOneTimeXenoArtifactEffect description: Obtains ability of omnitool components: - type: XAEApplyComponents @@ -550,7 +570,7 @@ - Sulfur - type: entity - id: XenoArtifactThrow + id: XenoArtifactThrowThingsAround parent: BaseXenoArtifactEffect description: Minor implosion components: @@ -597,6 +617,11 @@ parent: BaseXenoArtifactEffect description: Creates musical instrument components: + - type: XenoArtifactNode + maxDurability: 2 + maxDurabilityCanDecreaseBy: + min: 0 + max: 1 - type: XAEApplyComponents applyIfAlreadyHave: true refreshOnReactivate: true @@ -612,6 +637,11 @@ parent: BaseXenoArtifactEffect description: Creates primate components: + - type: XenoArtifactNode + maxDurability: 3 + maxDurabilityCanDecreaseBy: + min: 0 + max: 2 - type: XAEApplyComponents applyIfAlreadyHave: true refreshOnReactivate: true @@ -627,7 +657,7 @@ - type: entity id: XenoArtifactRadioactive - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Becomes mildly radioactive components: - type: XAEApplyComponents @@ -658,7 +688,7 @@ - type: entity id: XenoArtifactMagnet - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Create small gravity well components: - type: XAEApplyComponents @@ -672,7 +702,7 @@ - type: entity id: XenoArtifactMagnetNegative - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Create small gravity well components: - type: XAEApplyComponents @@ -686,7 +716,7 @@ - type: entity id: XenoArtifactStealth - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Create light interference components: - type: XAEApplyComponents @@ -699,7 +729,7 @@ - type: entity id: XenoArtifactRareMaterialSpawn - parent: BaseXenoArtifactEffect + parent: BaseXenoArtifactEffect # todo: splice into different well-named effects, amounts should reflect how rare material is description: Create rare materials components: - type: XAEApplyComponents @@ -732,6 +762,11 @@ parent: BaseXenoArtifactEffect description: Create hostile fish components: + - type: XenoArtifactNode + maxDurability: 3 + maxDurabilityCanDecreaseBy: + min: 0 + max: 2 - type: XAEApplyComponents applyIfAlreadyHave: true refreshOnReactivate: true @@ -750,6 +785,11 @@ parent: BaseXenoArtifactEffect description: Create friendly fauna components: + - type: XenoArtifactNode + maxDurability: 4 + maxDurabilityCanDecreaseBy: + min: 0 + max: 3 - type: XAEApplyComponents applyIfAlreadyHave: true refreshOnReactivate: true @@ -800,6 +840,11 @@ parent: BaseXenoArtifactEffect description: Create money components: + - type: XenoArtifactNode + maxDurability: 2 + maxDurabilityCanDecreaseBy: + min: 0 + max: 1 - type: XAEApplyComponents applyIfAlreadyHave: true refreshOnReactivate: true @@ -824,6 +869,11 @@ parent: BaseXenoArtifactEffect description: Break windows components: + - type: XenoArtifactNode + maxDurability: 3 + maxDurabilityCanDecreaseBy: + min: 0 + max: 2 - type: XAEDamageInArea damageChance: 0.75 whitelist: @@ -838,6 +888,11 @@ parent: BaseXenoArtifactEffect description: Creates wave of helpful foam components: + - type: XenoArtifactNode + maxDurability: 7 + maxDurabilityCanDecreaseBy: + min: 0 + max: 5 - type: XAEFoam replaceDescription: true reagents: @@ -903,6 +958,11 @@ parent: BaseXenoArtifactEffect description: Creates anomaly components: + - type: XenoArtifactNode + maxDurability: 2 + maxDurabilityCanDecreaseBy: + min: 0 + max: 1 - type: XAEApplyComponents applyIfAlreadyHave: true refreshOnReactivate: true @@ -913,7 +973,6 @@ children: - id: RandomAnomalySpawner - - type: entity id: XenoArtifactIgnite parent: BaseXenoArtifactEffect @@ -936,6 +995,11 @@ parent: BaseXenoArtifactEffect description: Dangerous electromagnetic interference components: + - type: XenoArtifactNode + maxDurability: 5 + maxDurabilityCanDecreaseBy: + min: 0 + max: 3 - type: XAEEmpInArea - type: entity @@ -963,7 +1027,7 @@ - type: entity id: XenoArtifactRadioactiveStrong - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Becomes highly radioactive components: - type: XAEApplyComponents @@ -975,9 +1039,9 @@ slope: 0.3 - type: entity - id: XenoArtifactMaterialSpawn + id: XenoArtifactMaterialSpawnGlass parent: BaseXenoArtifactEffect - description: Create basic material + description: Create glass components: - type: XAEApplyComponents applyIfAlreadyHave: true @@ -987,8 +1051,36 @@ deleteSpawnerAfterSpawn: false table: !type:GroupSelector children: - - id: SheetSteel - id: SheetGlass + +- type: entity + id: XenoArtifactMaterialSpawnSteel + parent: BaseXenoArtifactEffect + description: Create steel + components: + - type: XAEApplyComponents + applyIfAlreadyHave: true + refreshOnReactivate: true + components: + - type: EntityTableSpawner + deleteSpawnerAfterSpawn: false + table: !type:GroupSelector + children: + - id: SheetSteel + +- type: entity + id: XenoArtifactMaterialSpawnPlastic + parent: BaseXenoArtifactEffect + description: Create plastic + components: + - type: XAEApplyComponents + applyIfAlreadyHave: true + refreshOnReactivate: true + components: + - type: EntityTableSpawner + deleteSpawnerAfterSpawn: false + table: !type:GroupSelector + children: - id: SheetPlastic - type: entity @@ -1003,6 +1095,11 @@ parent: BaseXenoArtifactEffect description: Create artifact components: + - type: XenoArtifactNode + maxDurability: 2 + maxDurabilityCanDecreaseBy: + min: 0 + max: 1 - type: XAEApplyComponents applyIfAlreadyHave: true refreshOnReactivate: true @@ -1042,7 +1139,7 @@ - type: entity id: XenoArtifactTesla - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Mass destruction components: - type: XAEApplyComponents @@ -1057,7 +1154,7 @@ - type: entity id: XenoArtifactSingularity - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Imminent doom components: - type: XAEApplyComponents @@ -1072,7 +1169,7 @@ - type: entity id: XenoArtifactExplosionScary - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Small scale high-speed nuclear reaction components: - type: XAETriggerExplosives @@ -1087,7 +1184,7 @@ - type: entity id: XenoArtifactBoom - parent: BaseXenoArtifactEffect + parent: BaseOneTimeXenoArtifactEffect description: Explosion components: - type: XAETriggerExplosives From 3c88e6913326fdffe27d4432303abe8d0aa47475 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sun, 12 Jan 2025 17:50:39 +0300 Subject: [PATCH 75/81] refactor: split rare materials into separate effects --- Resources/Prototypes/XenoArch/effects.yml | 102 +++++++++++++++++++++- 1 file changed, 100 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/XenoArch/effects.yml b/Resources/Prototypes/XenoArch/effects.yml index 6be9dd65151c..be13fac83d17 100644 --- a/Resources/Prototypes/XenoArch/effects.yml +++ b/Resources/Prototypes/XenoArch/effects.yml @@ -53,8 +53,14 @@ weight: 2.0 - id: XenoArtifactStealth weight: 1.0 - - id: XenoArtifactRareMaterialSpawn - weight: 8.0 # amount is laughable + - id: XenoArtifactRareMaterialSpawnSilver + weight: 1.8 # amount is laughable + - id: XenoArtifactRareMaterialSpawnPlasma + weight: 2.0 # amount is laughable + - id: XenoArtifactRareMaterialSpawnGold + weight: 1.8 # amount is laughable + - id: XenoArtifactRareMaterialSpawnUranium + weight: 1.0 # amount is laughable - id: XenoArtifactAngryCarpSpawn weight: 4.0 - id: XenoArtifactFaunaSpawn @@ -757,6 +763,98 @@ value: 6 prob: 0.3 +- type: entity + id: XenoArtifactRareMaterialSpawnSilver + parent: BaseXenoArtifactEffect + description: Create rare materials + components: + - type: XenoArtifactNode + maxDurability: 4 + maxDurabilityCanDecreaseBy: + min: 0 + max: 2 + - type: XAEApplyComponents + applyIfAlreadyHave: true + refreshOnReactivate: true + components: + - type: EntityTableSpawner + deleteSpawnerAfterSpawn: false + table: !type:AllSelector + children: + - id: SilverOre1 + rolls: !type:ConstantNumberSelector + value: 6 + prob: 0.3 + +- type: entity + id: XenoArtifactRareMaterialSpawnPlasma + parent: BaseXenoArtifactEffect + description: Create plasma + components: + - type: XenoArtifactNode + maxDurability: 4 + maxDurabilityCanDecreaseBy: + min: 0 + max: 2 + - type: XAEApplyComponents + applyIfAlreadyHave: true + refreshOnReactivate: true + components: + - type: EntityTableSpawner + deleteSpawnerAfterSpawn: false + table: !type:AllSelector + children: + - id: PlasmaOre1 + rolls: !type:ConstantNumberSelector + value: 6 + prob: 0.3 + +- type: entity + id: XenoArtifactRareMaterialSpawnGold + parent: BaseXenoArtifactEffect + description: Create gold + components: + - type: XenoArtifactNode + maxDurability: 3 + maxDurabilityCanDecreaseBy: + min: 0 + max: 1 + - type: XAEApplyComponents + applyIfAlreadyHave: true + refreshOnReactivate: true + components: + - type: EntityTableSpawner + deleteSpawnerAfterSpawn: false + table: !type:AllSelector + children: + - id: GoldOre1 + rolls: !type:ConstantNumberSelector + value: 6 + prob: 0.3 + +- type: entity + id: XenoArtifactRareMaterialSpawnUranium + parent: BaseXenoArtifactEffect + description: Create uranium + components: + - type: XenoArtifactNode + maxDurability: 4 + maxDurabilityCanDecreaseBy: + min: 0 + max: 2 + - type: XAEApplyComponents + applyIfAlreadyHave: true + refreshOnReactivate: true + components: + - type: EntityTableSpawner + deleteSpawnerAfterSpawn: false + table: !type:AllSelector + children: + - id: UraniumOre1 + rolls: !type:ConstantNumberSelector + value: 3 + prob: 0.3 + - type: entity id: XenoArtifactAngryCarpSpawn parent: BaseXenoArtifactEffect From db0de70064cf08c42859dce7bb1f0c2e824d8962 Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Mon, 13 Jan 2025 23:20:53 +0300 Subject: [PATCH 76/81] feat: unlocked nodes without successor wont listen to unlocks, node unlock is not activating node --- .../Artifact/SharedXenoArtifactSystem.Unlock.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs index 0d316536b21a..01b97a530021 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs @@ -49,7 +49,10 @@ public bool CanUnlockNode(Entity ent) if (artiComp.Suppressed) return false; - if (!HasUnlockedPredecessor((artifact.Value, artiComp), ent)) + if (!HasUnlockedPredecessor((artifact.Value, artiComp), ent) + // unlocked final nodes should not listen for unlocking + || (!ent.Comp.Locked && GetSuccessorNodes((artifact.Value, artiComp), (ent.Owner, ent.Comp)).Count == 0) + ) return false; return true; @@ -69,10 +72,11 @@ public void FinishUnlockingState(Entity Date: Sun, 19 Jan 2025 16:51:10 +0300 Subject: [PATCH 77/81] fix: removed OnIrradiatedEvent duplicate c-tor --- Content.Shared/Radiation/Events/OnIrradiatedEvent.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs b/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs index 9aafcb455d55..ec9988fffbfd 100644 --- a/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs +++ b/Content.Shared/Radiation/Events/OnIrradiatedEvent.cs @@ -4,20 +4,13 @@ namespace Content.Shared.Radiation.Events; /// Raised on entity when it was irradiated /// by some radiation source. /// -public readonly record struct OnIrradiatedEvent(float FrameTime, float RadsPerSecond) +public readonly record struct OnIrradiatedEvent(float FrameTime, float RadsPerSecond, EntityUid Origin) { public readonly float FrameTime = FrameTime; public readonly float RadsPerSecond = RadsPerSecond; - public readonly EntityUid Origin; + public readonly EntityUid Origin = Origin; public float TotalRads => RadsPerSecond * FrameTime; - - public OnIrradiatedEvent(float frameTime, float radsPerSecond, EntityUid origin) - { - FrameTime = frameTime; - RadsPerSecond = radsPerSecond; - Origin = origin; - } } From 368ed9ff0fc48fcad7953ea4ea49f15edea7421f Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sun, 19 Jan 2025 17:00:36 +0300 Subject: [PATCH 78/81] revert changes of reach for playtest --- Resources/Maps/reach.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Maps/reach.yml b/Resources/Maps/reach.yml index 971468410de8..d1ddf3eb8e1e 100644 --- a/Resources/Maps/reach.yml +++ b/Resources/Maps/reach.yml @@ -16659,4 +16659,4 @@ entities: - type: Transform pos: -22.499155,-1.51554 parent: 2 -... +... \ No newline at end of file From b1a0dcf266a436e9e2d6df169fa33c3cde1bebbc Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sun, 19 Jan 2025 17:36:02 +0300 Subject: [PATCH 79/81] revert last row empty line removal on reach.yml --- Resources/Maps/reach.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Maps/reach.yml b/Resources/Maps/reach.yml index d1ddf3eb8e1e..971468410de8 100644 --- a/Resources/Maps/reach.yml +++ b/Resources/Maps/reach.yml @@ -16659,4 +16659,4 @@ entities: - type: Transform pos: -22.499155,-1.51554 parent: 2 -... \ No newline at end of file +... From d9a58271e05b4c216a7bb4933e1cca5d4fa7bcab Mon Sep 17 00:00:00 2001 From: "pa.pecherskij" Date: Sun, 2 Feb 2025 14:30:06 +0300 Subject: [PATCH 80/81] fix: fix PVS bug, born from attempt to relay event to art nodes that were not synced yet to the client --- .../Artifact/SharedXenoArtifactSystem.Graph.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs index 94c7310a5119..fca5baf10ffc 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Graph.cs @@ -102,8 +102,8 @@ public IEnumerable> GetAllNodes(Entity Date: Tue, 25 Feb 2025 11:04:28 +0300 Subject: [PATCH 81/81] fix: fix elkridge for tests (again) --- Resources/Maps/elkridge.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/Resources/Maps/elkridge.yml b/Resources/Maps/elkridge.yml index db070434f21a..50b128aac0b1 100644 --- a/Resources/Maps/elkridge.yml +++ b/Resources/Maps/elkridge.yml @@ -81542,7 +81542,6 @@ entities: - type: Transform pos: -28.5,12.5 parent: 2 - - type: BiasedArtifact - proto: MedkitAdvancedFilled entities: - uid: 4057