Make artifacts support saving (#14784)

This commit is contained in:
Nemanja
2023-03-23 22:50:24 -04:00
committed by GitHub
parent a0332c2f2e
commit 65acae15c0
7 changed files with 170 additions and 117 deletions

View File

@@ -14,11 +14,13 @@ using Content.Shared.MachineLinking.Events;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Research.Components; using Content.Shared.Research.Components;
using Content.Shared.Xenoarchaeology.Equipment; using Content.Shared.Xenoarchaeology.Equipment;
using Content.Shared.Xenoarchaeology.XenoArtifacts;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Events;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -31,6 +33,7 @@ namespace Content.Server.Xenoarchaeology.Equipment.Systems;
public sealed class ArtifactAnalyzerSystem : EntitySystem public sealed class ArtifactAnalyzerSystem : EntitySystem
{ {
[Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambienntSound = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambienntSound = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPopupSystem _popup = default!;
@@ -74,7 +77,8 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem
{ {
base.Update(frameTime); base.Update(frameTime);
foreach (var (active, scan) in EntityQuery<ActiveArtifactAnalyzerComponent, ArtifactAnalyzerComponent>()) var query = EntityQueryEnumerator<ActiveArtifactAnalyzerComponent, ArtifactAnalyzerComponent>();
while (query.MoveNext(out var uid, out var active, out var scan))
{ {
if (scan.Console != null) if (scan.Console != null)
UpdateUserInterface(scan.Console.Value); UpdateUserInterface(scan.Console.Value);
@@ -82,7 +86,7 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem
if (_timing.CurTime - active.StartTime < (scan.AnalysisDuration * scan.AnalysisDurationMulitplier)) if (_timing.CurTime - active.StartTime < (scan.AnalysisDuration * scan.AnalysisDurationMulitplier))
continue; continue;
FinishScan(scan.Owner, scan, active); FinishScan(uid, scan, active);
} }
} }
@@ -139,7 +143,9 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem
} }
else if (TryComp<ArtifactComponent>(component.LastAnalyzedArtifact, out var artifact)) else if (TryComp<ArtifactComponent>(component.LastAnalyzedArtifact, out var artifact))
{ {
var lastNode = (ArtifactNode?) artifact.CurrentNode?.Clone(); var lastNode = artifact.CurrentNodeId == null
? null
: (ArtifactNode?) _artifact.GetNodeFromId(artifact.CurrentNodeId.Value, artifact).Clone();
component.LastAnalyzedNode = lastNode; component.LastAnalyzedNode = lastNode;
component.LastAnalyzerPointValue = _artifact.GetResearchPointValue(component.LastAnalyzedArtifact.Value, artifact); component.LastAnalyzerPointValue = _artifact.GetResearchPointValue(component.LastAnalyzedArtifact.Value, artifact);
} }
@@ -298,17 +304,20 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem
msg.PushNewline(); msg.PushNewline();
var needSecondNewline = false; var needSecondNewline = false;
if (n.Trigger.TriggerHint != null)
var triggerProto = _prototype.Index<ArtifactTriggerPrototype>(n.Trigger);
if (triggerProto.TriggerHint != null)
{ {
msg.AddMarkup(Loc.GetString("analysis-console-info-trigger", msg.AddMarkup(Loc.GetString("analysis-console-info-trigger",
("trigger", Loc.GetString(n.Trigger.TriggerHint))) + "\n"); ("trigger", Loc.GetString(triggerProto.TriggerHint))) + "\n");
needSecondNewline = true; needSecondNewline = true;
} }
if (n.Effect.EffectHint != null) var effectproto = _prototype.Index<ArtifactEffectPrototype>(n.Effect);
if (effectproto.EffectHint != null)
{ {
msg.AddMarkup(Loc.GetString("analysis-console-info-effect", msg.AddMarkup(Loc.GetString("analysis-console-info-effect",
("effect", Loc.GetString(n.Effect.EffectHint))) + "\n"); ("effect", Loc.GetString(effectproto.EffectHint))) + "\n");
needSecondNewline = true; needSecondNewline = true;
} }

View File

@@ -23,7 +23,7 @@ public sealed class NodeScannerSystem : EntitySystem
if (!args.CanReach || args.Target == null) if (!args.CanReach || args.Target == null)
return; return;
if (!TryComp<ArtifactComponent>(args.Target, out var artifact) || artifact.CurrentNode == null) if (!TryComp<ArtifactComponent>(args.Target, out var artifact) || artifact.CurrentNodeId == null)
return; return;
if (args.Handled) if (args.Handled)
@@ -33,6 +33,6 @@ public sealed class NodeScannerSystem : EntitySystem
var target = args.Target.Value; var target = args.Target.Value;
_useDelay.BeginDelay(uid); _useDelay.BeginDelay(uid);
_popupSystem.PopupEntity(Loc.GetString("node-scan-popup", _popupSystem.PopupEntity(Loc.GetString("node-scan-popup",
("id", $"{artifact.CurrentNode.Id}")), target); ("id", $"{artifact.CurrentNodeId}")), target);
} }
} }

View File

@@ -1,5 +1,6 @@
using Content.Shared.Xenoarchaeology.XenoArtifacts; using Content.Shared.Xenoarchaeology.XenoArtifacts;
using Robust.Shared.Serialization.TypeSerializers.Implementations; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Xenoarchaeology.XenoArtifacts; namespace Content.Server.Xenoarchaeology.XenoArtifacts;
@@ -7,16 +8,16 @@ namespace Content.Server.Xenoarchaeology.XenoArtifacts;
public sealed class ArtifactComponent : Component public sealed class ArtifactComponent : Component
{ {
/// <summary> /// <summary>
/// The artifact's node tree. /// Every node contained in the tree
/// </summary> /// </summary>
[ViewVariables] [DataField("nodeTree"), ViewVariables]
public ArtifactTree? NodeTree; public List<ArtifactNode> NodeTree = new();
/// <summary> /// <summary>
/// The current node the artifact is on. /// The current node the artifact is on.
/// </summary> /// </summary>
[ViewVariables] [DataField("currentNodeId"), ViewVariables]
public ArtifactNode? CurrentNode; public int? CurrentNodeId;
#region Node Tree Gen #region Node Tree Gen
/// <summary> /// <summary>
@@ -35,21 +36,20 @@ public sealed class ArtifactComponent : Component
/// <summary> /// <summary>
/// Cooldown time between artifact activations (in seconds). /// Cooldown time between artifact activations (in seconds).
/// </summary> /// </summary>
[DataField("timer", customTypeSerializer: typeof(TimespanSerializer))] [DataField("timer"), ViewVariables(VVAccess.ReadWrite)]
[ViewVariables(VVAccess.ReadWrite)]
public TimeSpan CooldownTime = TimeSpan.FromSeconds(5); public TimeSpan CooldownTime = TimeSpan.FromSeconds(5);
/// <summary> /// <summary>
/// Is this artifact under some suppression device? /// Is this artifact under some suppression device?
/// f true, will ignore all trigger activations attempts. /// f true, will ignore all trigger activations attempts.
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [DataField("isSuppressed"), ViewVariables(VVAccess.ReadWrite)]
public bool IsSuppressed; public bool IsSuppressed;
/// <summary> /// <summary>
/// The last time the artifact was activated. /// The last time the artifact was activated.
/// </summary> /// </summary>
[DataField("lastActivationTime", customTypeSerializer: typeof(TimespanSerializer))] [DataField("lastActivationTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
public TimeSpan LastActivationTime; public TimeSpan LastActivationTime;
/// <summary> /// <summary>
@@ -72,25 +72,6 @@ public sealed class ArtifactComponent : Component
public float PointDangerMultiplier = 1.35f; public float PointDangerMultiplier = 1.35f;
} }
/// <summary>
/// A tree of nodes.
/// </summary>
[DataDefinition]
public sealed class ArtifactTree
{
/// <summary>
/// The first node of the tree
/// </summary>
[ViewVariables]
public ArtifactNode StartNode = default!;
/// <summary>
/// Every node contained in the tree
/// </summary>
[ViewVariables]
public readonly List<ArtifactNode> AllNodes = new();
}
/// <summary> /// <summary>
/// A single "node" of an artifact that contains various data about it. /// A single "node" of an artifact that contains various data about it.
/// </summary> /// </summary>
@@ -98,51 +79,51 @@ public sealed class ArtifactTree
public sealed class ArtifactNode : ICloneable public sealed class ArtifactNode : ICloneable
{ {
/// <summary> /// <summary>
/// A numeric id corresponding to each node. used for display purposes /// A numeric id corresponding to each node.
/// </summary> /// </summary>
[ViewVariables] [DataField("id"), ViewVariables]
public int Id; public int Id;
/// <summary> /// <summary>
/// how "deep" into the node tree. used for generation and price/value calculations /// how "deep" into the node tree. used for generation and price/value calculations
/// </summary> /// </summary>
[ViewVariables] [DataField("depth"), ViewVariables]
public int Depth = 0; public int Depth;
/// <summary> /// <summary>
/// A list of surrounding nodes. Used for tree traversal /// A list of surrounding nodes. Used for tree traversal
/// </summary> /// </summary>
[ViewVariables] [DataField("edges"), ViewVariables]
public List<ArtifactNode> Edges = new(); public HashSet<int> Edges = new();
/// <summary> /// <summary>
/// Whether or not the node has been entered /// Whether or not the node has been entered
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [DataField("discovered"), ViewVariables(VVAccess.ReadWrite)]
public bool Discovered = false; public bool Discovered;
/// <summary> /// <summary>
/// The trigger for the node /// The trigger for the node
/// </summary> /// </summary>
[ViewVariables] [DataField("trigger", customTypeSerializer: typeof(PrototypeIdSerializer<ArtifactTriggerPrototype>), required: true), ViewVariables]
public ArtifactTriggerPrototype Trigger = default!; public string Trigger = default!;
/// <summary> /// <summary>
/// Whether or not the node has been triggered /// Whether or not the node has been triggered
/// </summary> /// </summary>
[ViewVariables(VVAccess.ReadWrite)] [DataField("triggered"), ViewVariables(VVAccess.ReadWrite)]
public bool Triggered = false; public bool Triggered;
/// <summary> /// <summary>
/// The effect when the node is activated /// The effect when the node is activated
/// </summary> /// </summary>
[ViewVariables] [DataField("effect", customTypeSerializer: typeof(PrototypeIdSerializer<ArtifactEffectPrototype>), required: true), ViewVariables]
public ArtifactEffectPrototype Effect = default!; public string Effect = default!;
/// <summary> /// <summary>
/// Used for storing cumulative information about nodes /// Used for storing cumulative information about nodes
/// </summary> /// </summary>
[ViewVariables] [DataField("nodeData"), ViewVariables]
public Dictionary<string, object> NodeData = new(); public Dictionary<string, object> NodeData = new();
public object Clone() public object Clone()

View File

@@ -42,10 +42,10 @@ public partial class ArtifactSystem
private void OnSelfActivate(EntityUid uid, ArtifactComponent component, ArtifactSelfActivateEvent args) private void OnSelfActivate(EntityUid uid, ArtifactComponent component, ArtifactSelfActivateEvent args)
{ {
if (component.CurrentNode == null) if (component.CurrentNodeId == null)
return; return;
var curNode = component.CurrentNode.Id; var curNode = GetNodeFromId(component.CurrentNodeId.Value, component).Id;
_popup.PopupEntity(Loc.GetString("activate-artifact-popup-self", ("node", curNode)), uid, uid); _popup.PopupEntity(Loc.GetString("activate-artifact-popup-self", ("node", curNode)), uid, uid);
TryActivateArtifact(uid, uid, component); TryActivateArtifact(uid, uid, component);

View File

@@ -28,10 +28,10 @@ public partial class ArtifactSystem
if (!EntityUid.TryParse(args[0], out var uid) || ! int.TryParse(args[1], out var id)) if (!EntityUid.TryParse(args[0], out var uid) || ! int.TryParse(args[1], out var id))
return; return;
if (!TryComp<ArtifactComponent>(uid, out var artifact) || artifact.NodeTree == null) if (!TryComp<ArtifactComponent>(uid, out var artifact))
return; return;
if (artifact.NodeTree.AllNodes.FirstOrDefault(n => n.Id == id) is { } node) if (artifact.NodeTree.FirstOrDefault(n => n.Id == id) is { } node)
{ {
EnterNode(uid, ref node); EnterNode(uid, ref node);
} }
@@ -41,9 +41,9 @@ public partial class ArtifactSystem
{ {
if (args.Length == 2 && EntityUid.TryParse(args[0], out var uid)) if (args.Length == 2 && EntityUid.TryParse(args[0], out var uid))
{ {
if (TryComp<ArtifactComponent>(uid, out var artifact) && artifact.NodeTree != null) if (TryComp<ArtifactComponent>(uid, out var artifact))
{ {
return CompletionResult.FromHintOptions(artifact.NodeTree.AllNodes.Select(s => s.Id.ToString()), "<node id>"); return CompletionResult.FromHintOptions(artifact.NodeTree.Select(s => s.Id.ToString()), "<node id>");
} }
} }
@@ -59,10 +59,10 @@ public partial class ArtifactSystem
if (!EntityUid.TryParse(args[0], out var uid)) if (!EntityUid.TryParse(args[0], out var uid))
return; return;
if (!TryComp<ArtifactComponent>(uid, out var artifact) || artifact.NodeTree == null) if (!TryComp<ArtifactComponent>(uid, out var artifact))
return; return;
var pointSum = GetResearchPointValue(uid, artifact, true); var pointSum = GetResearchPointValue(uid, artifact, true);
shell.WriteLine($"Max point value for {ToPrettyString(uid)} with {artifact.NodeTree.AllNodes.Count} nodes: {pointSum}"); shell.WriteLine($"Max point value for {ToPrettyString(uid)} with {artifact.NodeTree.Count} nodes: {pointSum}");
} }
} }

View File

@@ -1,6 +1,7 @@
using System.Linq; using System.Linq;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events; using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Shared.Xenoarchaeology.XenoArtifacts; using Content.Shared.Xenoarchaeology.XenoArtifacts;
using JetBrains.Annotations;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager; using Robust.Shared.Serialization.Manager;
@@ -15,13 +16,15 @@ public sealed partial class ArtifactSystem
private const int MaxEdgesPerNode = 4; private const int MaxEdgesPerNode = 4;
private readonly HashSet<int> _usedNodeIds = new();
/// <summary> /// <summary>
/// Generate an Artifact tree with fully developed nodes. /// Generate an Artifact tree with fully developed nodes.
/// </summary> /// </summary>
/// <param name="artifact"></param> /// <param name="artifact"></param>
/// <param name="tree">The tree being generated.</param> /// <param name="allNodes"></param>
/// <param name="nodeAmount">The amount of nodes it has.</param> /// <param name="nodeAmount">The amount of nodes it has.</param>
private void GenerateArtifactNodeTree(EntityUid artifact, ref ArtifactTree tree, int nodeAmount) private void GenerateArtifactNodeTree(EntityUid artifact, ref List<ArtifactNode> allNodes, int nodeAmount)
{ {
if (nodeAmount < 1) if (nodeAmount < 1)
{ {
@@ -29,19 +32,36 @@ public sealed partial class ArtifactSystem
return; return;
} }
var uninitializedNodes = new List<ArtifactNode> { new() }; _usedNodeIds.Clear();
tree.StartNode = uninitializedNodes.First(); //the first node
var rootNode = new ArtifactNode
{
Id = GetValidNodeId()
};
var uninitializedNodes = new List<ArtifactNode> { rootNode };
while (uninitializedNodes.Any()) while (uninitializedNodes.Any())
{ {
GenerateNode(artifact, ref uninitializedNodes, ref tree, nodeAmount); GenerateNode(artifact, ref uninitializedNodes, ref allNodes, nodeAmount);
} }
} }
private int GetValidNodeId()
{
var id = _random.Next(10000, 100000);
while (_usedNodeIds.Contains(id))
{
id = _random.Next(10000, 100000);
}
_usedNodeIds.Add(id);
return id;
}
/// <summary> /// <summary>
/// Generate an individual node on the tree. /// Generate an individual node on the tree.
/// </summary> /// </summary>
private void GenerateNode(EntityUid artifact, ref List<ArtifactNode> uninitializedNodes, ref ArtifactTree tree, int targetNodeAmount) private void GenerateNode(EntityUid artifact, ref List<ArtifactNode> uninitializedNodes, ref List<ArtifactNode> allNodes, int targetNodeAmount)
{ {
if (!uninitializedNodes.Any()) if (!uninitializedNodes.Any())
return; return;
@@ -49,13 +69,10 @@ public sealed partial class ArtifactSystem
var node = uninitializedNodes.First(); var node = uninitializedNodes.First();
uninitializedNodes.Remove(node); uninitializedNodes.Remove(node);
//random 5-digit number
node.Id = _random.Next(10000, 100000);
//Generate the connected nodes //Generate the connected nodes
var maxEdges = Math.Max(1, targetNodeAmount - tree.AllNodes.Count - uninitializedNodes.Count - 1); var maxEdges = Math.Max(1, targetNodeAmount - allNodes.Count - uninitializedNodes.Count - 1);
maxEdges = Math.Min(maxEdges, MaxEdgesPerNode); maxEdges = Math.Min(maxEdges, MaxEdgesPerNode);
var minEdges = Math.Clamp(targetNodeAmount - tree.AllNodes.Count - uninitializedNodes.Count - 1, 0, 1); var minEdges = Math.Clamp(targetNodeAmount - allNodes.Count - uninitializedNodes.Count - 1, 0, 1);
var edgeAmount = _random.Next(minEdges, maxEdges); var edgeAmount = _random.Next(minEdges, maxEdges);
@@ -63,10 +80,11 @@ public sealed partial class ArtifactSystem
{ {
var neighbor = new ArtifactNode var neighbor = new ArtifactNode
{ {
Depth = node.Depth + 1 Depth = node.Depth + 1,
Id = GetValidNodeId()
}; };
node.Edges.Add(neighbor); node.Edges.Add(neighbor.Id);
neighbor.Edges.Add(node); neighbor.Edges.Add(node.Id);
uninitializedNodes.Add(neighbor); uninitializedNodes.Add(neighbor);
} }
@@ -74,13 +92,13 @@ public sealed partial class ArtifactSystem
node.Trigger = GetRandomTrigger(artifact, ref node); node.Trigger = GetRandomTrigger(artifact, ref node);
node.Effect = GetRandomEffect(artifact, ref node); node.Effect = GetRandomEffect(artifact, ref node);
tree.AllNodes.Add(node); allNodes.Add(node);
} }
//yeah these two functions are near duplicates but i don't //yeah these two functions are near duplicates but i don't
//want to implement an interface or abstract parent //want to implement an interface or abstract parent
private ArtifactTriggerPrototype GetRandomTrigger(EntityUid artifact, ref ArtifactNode node) private string GetRandomTrigger(EntityUid artifact, ref ArtifactNode node)
{ {
var allTriggers = _prototype.EnumeratePrototypes<ArtifactTriggerPrototype>() var allTriggers = _prototype.EnumeratePrototypes<ArtifactTriggerPrototype>()
.Where(x => (x.Whitelist?.IsValid(artifact, EntityManager) ?? true) && (!x.Blacklist?.IsValid(artifact, EntityManager) ?? true)).ToList(); .Where(x => (x.Whitelist?.IsValid(artifact, EntityManager) ?? true) && (!x.Blacklist?.IsValid(artifact, EntityManager) ?? true)).ToList();
@@ -91,10 +109,10 @@ public sealed partial class ArtifactSystem
var targetTriggers = allTriggers var targetTriggers = allTriggers
.Where(x => x.TargetDepth == selectedRandomTargetDepth).ToList(); .Where(x => x.TargetDepth == selectedRandomTargetDepth).ToList();
return _random.Pick(targetTriggers); return _random.Pick(targetTriggers).ID;
} }
private ArtifactEffectPrototype GetRandomEffect(EntityUid artifact, ref ArtifactNode node) private string GetRandomEffect(EntityUid artifact, ref ArtifactNode node)
{ {
var allEffects = _prototype.EnumeratePrototypes<ArtifactEffectPrototype>() var allEffects = _prototype.EnumeratePrototypes<ArtifactEffectPrototype>()
.Where(x => (x.Whitelist?.IsValid(artifact, EntityManager) ?? true) && (!x.Blacklist?.IsValid(artifact, EntityManager) ?? true)).ToList(); .Where(x => (x.Whitelist?.IsValid(artifact, EntityManager) ?? true) && (!x.Blacklist?.IsValid(artifact, EntityManager) ?? true)).ToList();
@@ -105,7 +123,7 @@ public sealed partial class ArtifactSystem
var targetEffects = allEffects var targetEffects = allEffects
.Where(x => x.TargetDepth == selectedRandomTargetDepth).ToList(); .Where(x => x.TargetDepth == selectedRandomTargetDepth).ToList();
return _random.Pick(targetEffects); return _random.Pick(targetEffects).ID;
} }
/// <remarks> /// <remarks>
@@ -156,14 +174,17 @@ public sealed partial class ArtifactSystem
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return; return;
if (component.CurrentNode != null) if (component.CurrentNodeId != null)
{ {
ExitNode(uid, component); ExitNode(uid, component);
} }
component.CurrentNode = node; component.CurrentNodeId = node.Id;
var allComponents = node.Effect.Components.Concat(node.Effect.PermanentComponents).Concat(node.Trigger.Components); var trigger = _prototype.Index<ArtifactTriggerPrototype>(node.Trigger);
var effect = _prototype.Index<ArtifactEffectPrototype>(node.Effect);
var allComponents = effect.Components.Concat(effect.PermanentComponents).Concat(trigger.Components);
foreach (var (name, entry) in allComponents) foreach (var (name, entry) in allComponents)
{ {
var reg = _componentFactory.GetRegistration(name); var reg = _componentFactory.GetRegistration(name);
@@ -171,7 +192,7 @@ public sealed partial class ArtifactSystem
if (node.Discovered && EntityManager.HasComponent(uid, reg.Type)) if (node.Discovered && EntityManager.HasComponent(uid, reg.Type))
{ {
// Don't re-add permanent components unless this is the first time you've entered this node // Don't re-add permanent components unless this is the first time you've entered this node
if (node.Effect.PermanentComponents.ContainsKey(name)) if (effect.PermanentComponents.ContainsKey(name))
continue; continue;
EntityManager.RemoveComponent(uid, reg.Type); EntityManager.RemoveComponent(uid, reg.Type);
@@ -187,7 +208,7 @@ public sealed partial class ArtifactSystem
} }
node.Discovered = true; node.Discovered = true;
RaiseLocalEvent(uid, new ArtifactNodeEnteredEvent(component.CurrentNode.Id)); RaiseLocalEvent(uid, new ArtifactNodeEnteredEvent(component.CurrentNodeId.Value));
} }
/// <summary> /// <summary>
@@ -198,16 +219,31 @@ public sealed partial class ArtifactSystem
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return; return;
var node = component.CurrentNode; if (component.CurrentNodeId == null)
if (node == null)
return; return;
var currentNode = GetNodeFromId(component.CurrentNodeId.Value, component);
foreach (var name in node.Effect.Components.Keys.Concat(node.Trigger.Components.Keys)) var trigger = _prototype.Index<ArtifactTriggerPrototype>(currentNode.Trigger);
var effect = _prototype.Index<ArtifactEffectPrototype>(currentNode.Effect);
foreach (var name in effect.Components.Keys.Concat(trigger.Components.Keys))
{ {
var comp = _componentFactory.GetRegistration(name); var comp = _componentFactory.GetRegistration(name);
EntityManager.RemoveComponentDeferred(uid, comp.Type); EntityManager.RemoveComponentDeferred(uid, comp.Type);
} }
component.CurrentNode = null; component.CurrentNodeId = null;
}
[PublicAPI]
public ArtifactNode GetNodeFromId(int id, ArtifactComponent component)
{
return component.NodeTree.First(x => x.Id == id);
}
[PublicAPI]
public ArtifactNode GetNodeFromId(int id, IEnumerable<ArtifactNode> nodes)
{
return nodes.First(x => x.Id == id);
} }
} }

View File

@@ -6,6 +6,7 @@ using Content.Server.Power.EntitySystems;
using Content.Server.Xenoarchaeology.Equipment.Components; using Content.Server.Xenoarchaeology.Equipment.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events; using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
using Content.Shared.Xenoarchaeology.XenoArtifacts;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -46,13 +47,10 @@ public sealed partial class ArtifactSystem : EntitySystem
/// </remarks> /// </remarks>
private void GetPrice(EntityUid uid, ArtifactComponent component, ref PriceCalculationEvent args) private void GetPrice(EntityUid uid, ArtifactComponent component, ref PriceCalculationEvent args)
{ {
if (component.NodeTree == null) var price = component.NodeTree.Sum(x => GetNodePrice(x, component));
return;
var price = component.NodeTree.AllNodes.Sum(x => GetNodePrice(x, component));
// 25% bonus for fully exploring every node. // 25% bonus for fully exploring every node.
var fullyExploredBonus = component.NodeTree.AllNodes.Any(x => !x.Triggered) ? 1 : 1.25f; var fullyExploredBonus = component.NodeTree.Any(x => !x.Triggered) ? 1 : 1.25f;
args.Price =+ price * fullyExploredBonus; args.Price =+ price * fullyExploredBonus;
} }
@@ -62,10 +60,13 @@ public sealed partial class ArtifactSystem : EntitySystem
if (!node.Discovered) //no money for undiscovered nodes. if (!node.Discovered) //no money for undiscovered nodes.
return 0; return 0;
var triggerProto = _prototype.Index<ArtifactTriggerPrototype>(node.Trigger);
var effectProto = _prototype.Index<ArtifactEffectPrototype>(node.Effect);
//quarter price if not triggered //quarter price if not triggered
var priceMultiplier = node.Triggered ? 1f : 0.25f; var priceMultiplier = node.Triggered ? 1f : 0.25f;
//the danger is the average of node depth, effect danger, and trigger danger. //the danger is the average of node depth, effect danger, and trigger danger.
var nodeDanger = (node.Depth + node.Effect.TargetDepth + node.Trigger.TargetDepth) / 3; var nodeDanger = (node.Depth + effectProto.TargetDepth + triggerProto.TargetDepth) / 3;
var price = MathF.Pow(2f, nodeDanger) * component.PricePerNode * priceMultiplier; var price = MathF.Pow(2f, nodeDanger) * component.PricePerNode * priceMultiplier;
return price; return price;
@@ -86,11 +87,11 @@ public sealed partial class ArtifactSystem : EntitySystem
/// </remarks> /// </remarks>
public int GetResearchPointValue(EntityUid uid, ArtifactComponent? component = null, bool getMaxPrice = false) public int GetResearchPointValue(EntityUid uid, ArtifactComponent? component = null, bool getMaxPrice = false)
{ {
if (!Resolve(uid, ref component) || component.NodeTree == null) if (!Resolve(uid, ref component))
return 0; return 0;
var sumValue = component.NodeTree.AllNodes.Sum(n => GetNodePointValue(n, component, getMaxPrice)); var sumValue = component.NodeTree.Sum(n => GetNodePointValue(n, component, getMaxPrice));
var fullyExploredBonus = component.NodeTree.AllNodes.All(x => x.Triggered) || getMaxPrice ? 1.25f : 1; var fullyExploredBonus = component.NodeTree.All(x => x.Triggered) || getMaxPrice ? 1.25f : 1;
var pointValue = (int) (sumValue * fullyExploredBonus); var pointValue = (int) (sumValue * fullyExploredBonus);
return pointValue; return pointValue;
@@ -109,7 +110,11 @@ public sealed partial class ArtifactSystem : EntitySystem
valueDeduction = !node.Triggered ? 0.25f : 1; valueDeduction = !node.Triggered ? 0.25f : 1;
} }
var nodeDanger = (node.Depth + node.Effect.TargetDepth + node.Trigger.TargetDepth) / 3;
var triggerProto = _prototype.Index<ArtifactTriggerPrototype>(node.Trigger);
var effectProto = _prototype.Index<ArtifactEffectPrototype>(node.Effect);
var nodeDanger = (node.Depth + effectProto.TargetDepth + triggerProto.TargetDepth) / 3;
return component.PointsPerNode * MathF.Pow(component.PointDangerMultiplier, nodeDanger) * valueDeduction; return component.PointsPerNode * MathF.Pow(component.PointDangerMultiplier, nodeDanger) * valueDeduction;
} }
@@ -121,10 +126,9 @@ public sealed partial class ArtifactSystem : EntitySystem
{ {
var nodeAmount = _random.Next(component.NodesMin, component.NodesMax); var nodeAmount = _random.Next(component.NodesMin, component.NodesMax);
component.NodeTree = new ArtifactTree();
GenerateArtifactNodeTree(uid, ref component.NodeTree, nodeAmount); GenerateArtifactNodeTree(uid, ref component.NodeTree, nodeAmount);
EnterNode(uid, ref component.NodeTree.StartNode, component); var firstNode = GetRootNode(component.NodeTree);
EnterNode(uid, ref firstNode, component);
} }
/// <summary> /// <summary>
@@ -162,7 +166,7 @@ public sealed partial class ArtifactSystem : EntitySystem
{ {
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return; return;
if (component.CurrentNode == null) if (component.CurrentNodeId == null)
return; return;
component.LastActivationTime = _gameTiming.CurTime; component.LastActivationTime = _gameTiming.CurTime;
@@ -173,8 +177,10 @@ public sealed partial class ArtifactSystem : EntitySystem
}; };
RaiseLocalEvent(uid, ev, true); RaiseLocalEvent(uid, ev, true);
component.CurrentNode.Triggered = true; var currentNode = GetNodeFromId(component.CurrentNodeId.Value, component);
if (component.CurrentNode.Edges.Any())
currentNode.Triggered = true;
if (currentNode.Edges.Any())
{ {
var newNode = GetNewNode(uid, component); var newNode = GetNewNode(uid, component);
if (newNode == null) if (newNode == null)
@@ -185,10 +191,18 @@ public sealed partial class ArtifactSystem : EntitySystem
private ArtifactNode? GetNewNode(EntityUid uid, ArtifactComponent component) private ArtifactNode? GetNewNode(EntityUid uid, ArtifactComponent component)
{ {
if (component.CurrentNode == null) if (component.CurrentNodeId == null)
return null; return null;
var allNodes = component.CurrentNode.Edges; var currentNode = GetNodeFromId(component.CurrentNodeId.Value, component);
var allNodes = currentNode.Edges;
Logger.Debug($"our node: {currentNode.Id}");
Logger.Debug("other nodes:");
foreach (var other in allNodes)
{
Logger.Debug($"{other}");
}
if (TryComp<BiasedArtifactComponent>(uid, out var bias) && if (TryComp<BiasedArtifactComponent>(uid, out var bias) &&
TryComp<TraversalDistorterComponent>(bias.Provider, out var trav) && TryComp<TraversalDistorterComponent>(bias.Provider, out var trav) &&
@@ -198,26 +212,26 @@ public sealed partial class ArtifactSystem : EntitySystem
switch (trav.BiasDirection) switch (trav.BiasDirection)
{ {
case BiasDirection.In: case BiasDirection.In:
var foo = allNodes.Where(x => x.Depth < component.CurrentNode.Depth).ToList(); var foo = allNodes.Where(x => GetNodeFromId(x, component).Depth < currentNode.Depth).ToHashSet();
if (foo.Any()) if (foo.Any())
allNodes = foo; allNodes = foo;
break; break;
case BiasDirection.Out: case BiasDirection.Out:
var bar = allNodes.Where(x => x.Depth > component.CurrentNode.Depth).ToList(); var bar = allNodes.Where(x => GetNodeFromId(x, component).Depth > currentNode.Depth).ToHashSet();
if (bar.Any()) if (bar.Any())
allNodes = bar; allNodes = bar;
break; break;
} }
} }
var undiscoveredNodes = allNodes.Where(x => !x.Discovered).ToList(); var undiscoveredNodes = allNodes.Where(x => GetNodeFromId(x, component).Discovered).ToList();
var newNode = _random.Pick(allNodes); var newNode = _random.Pick(allNodes);
if (undiscoveredNodes.Any() && _random.Prob(0.75f)) if (undiscoveredNodes.Any() && _random.Prob(0.75f))
{ {
newNode = _random.Pick(undiscoveredNodes); newNode = _random.Pick(undiscoveredNodes);
} }
return newNode; return GetNodeFromId(newNode, component);
} }
/// <summary> /// <summary>
@@ -236,10 +250,11 @@ public sealed partial class ArtifactSystem : EntitySystem
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return false; return false;
if (component.CurrentNode == null) if (component.CurrentNodeId == null)
return false; return false;
var currentNode = GetNodeFromId(component.CurrentNodeId.Value, component);
if (component.CurrentNode.NodeData.TryGetValue(key, out var dat) && dat is T value) if (currentNode.NodeData.TryGetValue(key, out var dat) && dat is T value)
{ {
data = value; data = value;
return true; return true;
@@ -260,10 +275,21 @@ public sealed partial class ArtifactSystem : EntitySystem
if (!Resolve(uid, ref component)) if (!Resolve(uid, ref component))
return; return;
if (component.CurrentNode == null) if (component.CurrentNodeId == null)
return; return;
var currentNode = GetNodeFromId(component.CurrentNodeId.Value, component);
component.CurrentNode.NodeData[key] = value; currentNode.NodeData[key] = value;
}
/// <summary>
/// Gets the base node (depth 0) of an artifact's node graph
/// </summary>
/// <param name="allNodes"></param>
/// <returns></returns>
public ArtifactNode GetRootNode(List<ArtifactNode> allNodes)
{
return allNodes.First(n => n.Depth == 0);
} }
/// <summary> /// <summary>
@@ -271,10 +297,11 @@ public sealed partial class ArtifactSystem : EntitySystem
/// </summary> /// </summary>
private void OnRoundEnd(RoundEndTextAppendEvent ev) private void OnRoundEnd(RoundEndTextAppendEvent ev)
{ {
foreach (var artifactComp in EntityQuery<ArtifactComponent>()) var query = EntityQueryEnumerator<ArtifactComponent>();
while (query.MoveNext(out var ent, out var artifactComp))
{ {
artifactComp.CooldownTime = TimeSpan.Zero; artifactComp.CooldownTime = TimeSpan.Zero;
var timerTrigger = EnsureComp<ArtifactTimerTriggerComponent>(artifactComp.Owner); var timerTrigger = EnsureComp<ArtifactTimerTriggerComponent>(ent);
timerTrigger.ActivationRate = TimeSpan.FromSeconds(0.5); //HAHAHAHAHAHAHAHAHAH -emo timerTrigger.ActivationRate = TimeSpan.FromSeconds(0.5); //HAHAHAHAHAHAHAHAHAH -emo
} }
} }