Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Client.Xenoarchaeology.Artifact;
using Content.Shared.Random;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
Expand All @@ -7,6 +8,7 @@
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Input;
using Robust.Shared.Random;
using System.Linq;
using System.Numerics;

Expand All @@ -16,6 +18,7 @@ namespace Content.Client.Xenoarchaeology.Ui;
public sealed partial class XenoArtifactGraphControl : BoxContainer
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IRobustRandom _rand = default!;

private readonly XenoArtifactSystem _artifactSystem;

Expand Down Expand Up @@ -154,6 +157,11 @@ protected override void Draw(DrawingHandleScreen handle)
var text = _artifactSystem.GetNodeId(node);
var dimensions = handle.GetDimensions(_font, text, 1);
handle.DrawString(_font, pos - new Vector2(dimensions.X / 2, dimensions.Y / 2), text, color);

if (node.Comp.Shattered)
{
DrawShatteredNode(handle, pos, color, node.Comp.ShatterPattern);
}
}
}

Expand Down Expand Up @@ -204,5 +212,91 @@ private float GetBiggestWidth(List<Entity<XenoArtifactNodeComponent>> nodes)
.Max(p => p.Value.Count);
return (NodeDiameter * num) + MinXSpacing * (num - 1);
}
}

private void DrawShatteredNode(DrawingHandleScreen handle, Vector2 pos, Color color, ShatterPatternTypes type)
{
var shatteredColor = Color.ToSrgb(color.WithAlpha(0.9f));

switch (type)
{
case ShatterPatternTypes.Strike:
DrawShatterStrike(handle, pos, shatteredColor);
break;
case ShatterPatternTypes.Bolt:
DrawShatterBolt(handle, pos, shatteredColor);
break;
case ShatterPatternTypes.Spider:
DrawShatterSpider(handle, pos, shatteredColor);
break;
case ShatterPatternTypes.Fracture:
DrawShatterFracture(handle, pos, shatteredColor);
break;
case ShatterPatternTypes.Split:
DrawShatterSplit(handle, pos, shatteredColor);
break;
case ShatterPatternTypes.Fragment:
DrawShatterFragment(handle, pos, shatteredColor);
break;
default:
DrawShatterStrike(handle, pos, shatteredColor);
break;
}
}

private void DrawShatterStrike(DrawingHandleScreen handle, Vector2 pos, Color color)
{
handle.DrawLine(pos + new Vector2(-12, -18), pos + new Vector2(-3, -6), color);
handle.DrawLine(pos + new Vector2(-3, -6), pos + new Vector2(-9, 4), color);
handle.DrawLine(pos + new Vector2(-3, -6), pos + new Vector2(7, 1), color);
handle.DrawLine(pos + new Vector2(7, 1), pos + new Vector2(14, 15), color);
}

private void DrawShatterBolt(DrawingHandleScreen handle, Vector2 pos, Color color)
{
handle.DrawLine(pos + new Vector2(-10, -18), pos + new Vector2(-2, -8), color);
handle.DrawLine(pos + new Vector2(-2, -8), pos + new Vector2(-8, 2), color);
handle.DrawLine(pos + new Vector2(-8, 2), pos + new Vector2(4, 10), color);
handle.DrawLine(pos + new Vector2(4, 10), pos + new Vector2(-2, 18), color);
handle.DrawLine(pos + new Vector2(-2, -8), pos + new Vector2(8, -4), color);
handle.DrawLine(pos + new Vector2(4, 10), pos + new Vector2(12, 6), color);
}

private void DrawShatterSpider(DrawingHandleScreen handle, Vector2 pos, Color color)
{
handle.DrawLine(pos + new Vector2(0, -18), pos + new Vector2(0, 18), color);
handle.DrawLine(pos + new Vector2(-15, 0), pos + new Vector2(15, 0), color);
handle.DrawLine(pos + new Vector2(-10, -10), pos + new Vector2(10, 10), color);
handle.DrawLine(pos + new Vector2(10, -10), pos + new Vector2(-10, 10), color);
handle.DrawLine(pos + new Vector2(0, 0), pos + new Vector2(6, -15), color);
handle.DrawLine(pos + new Vector2(0, 0), pos + new Vector2(-12, 6), color);
}

private void DrawShatterFracture(DrawingHandleScreen handle, Vector2 pos, Color color)
{
handle.DrawLine(pos + new Vector2(-18, -2), pos + new Vector2(-6, -2), color);
handle.DrawLine(pos + new Vector2(-6, -2), pos + new Vector2(4, -12), color);
handle.DrawLine(pos + new Vector2(-6, -2), pos + new Vector2(2, 6), color);
handle.DrawLine(pos + new Vector2(2, 6), pos + new Vector2(12, 4), color);
handle.DrawLine(pos + new Vector2(2, 6), pos + new Vector2(6, 16), color);
}

private void DrawShatterSplit(DrawingHandleScreen handle, Vector2 pos, Color color)
{
handle.DrawLine(pos + new Vector2(0, 18), pos + new Vector2(0, 2), color);
handle.DrawLine(pos + new Vector2(0, 2), pos + new Vector2(-12, -10), color);
handle.DrawLine(pos + new Vector2(0, 2), pos + new Vector2(12, -10), color);
handle.DrawLine(pos + new Vector2(-12, -10), pos + new Vector2(-8, -18), color);
handle.DrawLine(pos + new Vector2(12, -10), pos + new Vector2(8, -18), color);
}

private void DrawShatterFragment(DrawingHandleScreen handle, Vector2 pos, Color color)
{
handle.DrawLine(pos + new Vector2(-14, -14), pos + new Vector2(-2, -2), color);
handle.DrawLine(pos + new Vector2(-2, -2), pos + new Vector2(8, -12), color);
handle.DrawLine(pos + new Vector2(-2, -2), pos + new Vector2(-10, 8), color);
handle.DrawLine(pos + new Vector2(-2, -2), pos + new Vector2(10, 6), color);
handle.DrawLine(pos + new Vector2(10, 6), pos + new Vector2(4, 16), color);
}

public delegate void NodeHandler(DrawingHandleScreen handle, Vector2 pos, Color color);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ public sealed partial class XAEChargeBatteryComponent : Component
/// </summary>
[DataField("radius")]
public float Radius = 15f;

/// <summary>
/// The percent increase in charge for nearby batteries
/// </summary>
public float ChargePercent = 100.0f;
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,15 @@ protected override void OnActivated(Entity<XAEChargeBatteryComponent> ent, ref X
_lookup.GetEntitiesInRange(args.Coordinates, ent.Comp.Radius, _batteryEntities);
foreach (var battery in _batteryEntities)
{
_battery.SetCharge(battery.AsNullable(), battery.Comp.MaxCharge);
if (ent.Comp.ChargePercent >= 100f) // Skip calculation if charge to full
{
_battery.SetCharge(battery.AsNullable(), battery.Comp.MaxCharge);
continue;
}

// Calculate charge on the battery based on charge percent.
var charge = MathF.Min(battery.Comp.MaxCharge, battery.Comp.LastCharge + ent.Comp.ChargePercent / 100f * battery.Comp.MaxCharge);
_battery.SetCharge(battery.AsNullable(), charge);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public sealed partial class XenoArtifactComponent : Component
/// The total number of nodes that make up this artifact.
/// </summary>
[DataField]
public MinMax NodeCount = new(10, 16);
public MinMax NodeCount = new(12, 18);

/// <summary>
/// The amount of nodes that go in each segment.
Expand All @@ -133,13 +133,46 @@ public sealed partial class XenoArtifactComponent : Component
[DataField]
public MinMax ScatterPerLayer = new(0, 2);

/// <summary>
/// Lower threshold in which nodes will be considered "root"
/// </summary>
[DataField]
public int RootNodeThreshold = 0;

/// <summary>
/// Threshold at which nodes will switch from main to deep.
/// </summary>
[DataField]
public int DeepNodeThreshold = 3;

/// <summary>
/// Effects that can be used during this artifact generation.
/// Used on nodes of depth at most <see cref="RootNodeThreshold"/>
/// </summary>
[DataField]
public EntityTableSelector RootEffectsTable = new NestedSelector
{
TableId = "XenoArtifactDefaultEffectsRoot"
};

/// <summary>
/// Effects that can be used during this artifact generation.
/// Used on nodes between depths <see cref="RootNodeThreshold"/> and <see cref="DeepNodeThreshold"/>
/// </summary>
[DataField]
public EntityTableSelector MainEffectsTable = new NestedSelector
{
TableId = "XenoArtifactDefaultEffectsMain"
};

/// <summary>
/// Effects that can be used during this artifact generation.
/// Used on nodes of depth <see cref="DeepNodeThreshold"/> or deeper
/// </summary>
[DataField]
public EntityTableSelector EffectsTable = new NestedSelector
public EntityTableSelector DeepEffectsTable = new NestedSelector
{
TableId = "XenoArtifactEffectsDefaultTable"
TableId = "XenoArtifactDefaultEffectsDeep"
};

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Shared.Destructible.Thresholds;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;

namespace Content.Shared.Xenoarchaeology.Artifact.Components;
Expand Down Expand Up @@ -55,14 +56,50 @@ public sealed partial class XenoArtifactNodeComponent : Component
/// <summary>
/// The maximum factor by which using the durability of an artifact will scale it's Research Value.
/// </summary>
[DataField]
[DataField, AutoNetworkedField]
public float DurabilityResearchMultiplier = 2f;

/// <summary>
/// The variance from MaxDurability present when a node is created.
/// </summary>
[DataField]
[DataField, AutoNetworkedField]
public MinMax MaxDurabilityCanDecreaseBy = new(0, 2);

/// <summary>
/// The lifetime consumed durability of the node.
/// </summary>
[DataField, AutoNetworkedField]
public int TotalConsumedDurability = 0;

/// <summary>
/// The threshold at which the node has a 50% chance of shattering on activation.
/// </summary>
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
public int AverageShatterDurabilityThreshold = 10;

/// <summary>
/// The threshold at which the node has a 100% chance of shattering on activation.
/// </summary>
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
public int MaxShatterDurabilityThreshold = 15;

/// <summary>
/// Shattered nodes cannot be have their durability increased.
/// </summary>
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
public bool Shattered = false;

/// <summary>
/// Determines the pattern shown on the UI for the analysis console.
/// </summary>
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
public ShatterPatternTypes ShatterPattern = ShatterPatternTypes.Strike;

/// <summary>
/// Sound to play when a node is shattered.
/// </summary>
[DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadOnly)]
public SoundSpecifier ShatterSound = new SoundPathSpecifier("/Audio/Effects/glass_break1.ogg");
#endregion

#region Research
Expand All @@ -85,3 +122,8 @@ public sealed partial class XenoArtifactNodeComponent : Component
public int ConsumedResearchValue;
#endregion
}

public enum ShatterPatternTypes
{
Strike, Bolt, Spider, Fracture, Split, Fragment
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Linq;
using Content.Shared.Database;
using Content.Shared.EntityTable;
using Content.Shared.NameIdentifier;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Content.Shared.Xenoarchaeology.Artifact.Prototypes;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Utility;

namespace Content.Shared.Xenoarchaeology.Artifact;
Expand Down Expand Up @@ -64,17 +67,18 @@ public void AdjustNodeDurability(Entity<XenoArtifactNodeComponent?> ent, int dur
{
if (!Resolve(ent, ref ent.Comp))
return;

SetNodeDurability(ent, ent.Comp.Durability + durabilityDelta);
}

/// <summary>
/// Sets a node's durability to the specified value. HIGHLY recommended to not be less than 0.
/// </summary>
public void SetNodeDurability(Entity<XenoArtifactNodeComponent?> ent, int durability)
public void SetNodeDurability(Entity<XenoArtifactNodeComponent?> ent, int durability, bool ignoreShatter = false)
{
if (!Resolve(ent, ref ent.Comp))
return;
if (ent.Comp.Shattered && !ignoreShatter)
return;

ent.Comp.Durability = Math.Clamp(durability, 0, ent.Comp.MaxDurability);
UpdateNodeResearchValue((ent, ent.Comp));
Expand All @@ -95,8 +99,12 @@ public Entity<XenoArtifactNodeComponent> CreateNode(Entity<XenoArtifactComponent
/// </summary>
public Entity<XenoArtifactNodeComponent> CreateNode(Entity<XenoArtifactComponent> ent, XenoArchTriggerPrototype trigger, int depth = 0)
{
var entProtoId = _entityTable.GetSpawns(ent.Comp.EffectsTable)
.First();
// Get the correct table based on the depth of the node. Deeper nodes have more valuable options.
var tableByDepth = depth <= ent.Comp.RootNodeThreshold ? ent.Comp.RootEffectsTable :
depth >= ent.Comp.DeepNodeThreshold ? ent.Comp.DeepEffectsTable :
ent.Comp.MainEffectsTable;

var entProtoId = _entityTable.GetSpawns(tableByDepth).First();

AddNode((ent, ent), entProtoId, out var nodeEnt, dirty: false);
DebugTools.Assert(nodeEnt.HasValue, "Failed to create node on artifact.");
Expand Down Expand Up @@ -383,12 +391,31 @@ public void UpdateNodeResearchValue(Entity<XenoArtifactNodeComponent> node)

var artifact = _xenoArtifactQuery.Get(nodeComponent.Attached.Value);

var nonactiveNodes = GetActiveNodes(artifact); // This seems like its... wrong...
var durabilityEffect = MathF.Pow((float)nodeComponent.Durability / nodeComponent.MaxDurability, 2);
var maxDurability = nodeComponent.MaxDurability <= 0 ? 1 : nodeComponent.MaxDurability;
var durabilityEffect = MathF.Pow((float)nodeComponent.Durability / maxDurability, 2);
var durabilityMultiplier = nodeComponent.DurabilityResearchMultiplier - (nodeComponent.DurabilityResearchMultiplier - 1) * durabilityEffect;

var predecessorNodes = GetPredecessorNodes((artifact, artifact), node);
var predecessorMultiplier = Math.Pow(1.25, Math.Pow(predecessorNodes.Count, 1.5f));
nodeComponent.ResearchValue = (int)(nodeComponent.BasePointValue * predecessorMultiplier * durabilityMultiplier);
}
}

public void Shatter(Entity<XenoArtifactNodeComponent?> node)
{
if (!_net.IsServer)
return;

if (!Resolve(node, ref node.Comp))
return;

if (node.Comp.Shattered)
return;

node.Comp.ShatterPattern = RobustRandom.Pick(Enum.GetValues<ShatterPatternTypes>());
node.Comp.Durability = 0;
node.Comp.Shattered = true;

_audio.PlayPvs(node.Comp.ShatterSound, node.Owner);
Dirty(node);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ namespace Content.Shared.Xenoarchaeology.Artifact;

public abstract partial class SharedXenoArtifactSystem
{
[Dependency] private readonly SharedAudioSystem _audio = default!;

private EntityQuery<XenoArtifactUnlockingComponent> _unlockingQuery;

private void InitializeUnlock()
Expand Down Expand Up @@ -77,10 +75,8 @@ public void FinishUnlockingState(Entity<XenoArtifactUnlockingComponent, XenoArti
SetNodeUnlocked((ent, artifactComponent), node.Value);
ActivateNode((ent, ent), (node.Value, node.Value), null, null, Transform(ent).Coordinates, false);
unlockAttemptResultMsg = "artifact-unlock-state-end-success";
UpdateNodeResearchValue(node.Value);

// as an experiment - unlocking node doesn't activate it, activation is left for player to decide.
// var activated = ActivateNode((ent, artifactComponent), node.Value, null, null, Transform(ent).Coordinates, false);
// if (activated)
soundEffect = unlockingComponent.UnlockActivationSuccessfulSound;
}
else
Expand Down
Loading
Loading