Files
tbd-station-14/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs

186 lines
6.9 KiB
C#

using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Xenoarchaeology.Artifact.Components;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Collections;
using Robust.Shared.Random;
namespace Content.Shared.Xenoarchaeology.Artifact;
public abstract partial class SharedXenoArtifactSystem
{
[Dependency] private readonly SharedAudioSystem _audio = default!;
private EntityQuery<XenoArtifactUnlockingComponent> _unlockingQuery;
private void InitializeUnlock()
{
_unlockingQuery = GetEntityQuery<XenoArtifactUnlockingComponent>();
SubscribeLocalEvent<XenoArtifactUnlockingComponent, MapInitEvent>(OnUnlockingStarted);
}
/// <summary> Finish unlocking phase when the time is up. </summary>
private void UpdateUnlock(float _)
{
var query = EntityQueryEnumerator<XenoArtifactUnlockingComponent, XenoArtifactComponent>();
while (query.MoveNext(out var uid, out var unlock, out var comp))
{
if (_timing.CurTime < unlock.EndTime)
continue;
FinishUnlockingState((uid, unlock, comp));
}
}
/// <summary>
/// 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.
/// </summary>
public bool CanUnlockNode(Entity<XenoArtifactNodeComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp))
return false;
var artifact = ent.Comp.Attached;
if (!TryComp<XenoArtifactComponent>(artifact, out var artiComp))
return false;
if (artiComp.Suppressed)
return false;
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;
}
/// <summary>
/// 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.
/// </summary>
public void FinishUnlockingState(Entity<XenoArtifactUnlockingComponent, XenoArtifactComponent> ent)
{
string unlockAttemptResultMsg;
XenoArtifactComponent artifactComponent = ent;
XenoArtifactUnlockingComponent unlockingComponent = ent;
SoundSpecifier? soundEffect;
if (TryGetNodeFromUnlockState(ent, out var node))
{
SetNodeUnlocked((ent, artifactComponent), node.Value);
ActivateNode((ent, ent), (node.Value, node.Value), null, null, Transform(ent).Coordinates, false);
unlockAttemptResultMsg = "artifact-unlock-state-end-success";
// 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
{
unlockAttemptResultMsg = "artifact-unlock-state-end-failure";
soundEffect = unlockingComponent.UnlockActivationFailedSound;
}
if (_net.IsServer)
{
_popup.PopupEntity(Loc.GetString(unlockAttemptResultMsg), ent);
_audio.PlayPvs(soundEffect, ent.Owner);
}
RemComp(ent, unlockingComponent);
RaiseUnlockingFinished(ent, node);
artifactComponent.NextUnlockTime = _timing.CurTime + artifactComponent.UnlockStateRefractory;
}
public void CancelUnlockingState(Entity<XenoArtifactUnlockingComponent, XenoArtifactComponent> ent)
{
RemComp(ent, ent.Comp1);
RaiseUnlockingFinished(ent, null);
}
/// <summary>
/// Gets first locked node that can be unlocked (it is locked and all predecessor are unlocked).
/// </summary>
public bool TryGetNodeFromUnlockState(
Entity<XenoArtifactUnlockingComponent, XenoArtifactComponent> ent,
[NotNullWhen(true)] out Entity<XenoArtifactNodeComponent>? node
)
{
node = null;
var potentialNodes = new ValueList<Entity<XenoArtifactNodeComponent>>();
var artifactUnlockingComponent = ent.Comp1;
foreach (var nodeIndex in GetAllNodeIndices((ent, ent)))
{
var artifactComponent = ent.Comp2;
var curNode = GetNode((ent, artifactComponent), nodeIndex);
if (!curNode.Comp.Locked || !CanUnlockNode((curNode, curNode)))
continue;
var requiredIndices = GetPredecessorNodes((ent, artifactComponent), nodeIndex);
requiredIndices.Add(nodeIndex);
if (!ent.Comp1.ArtifexiumApplied)
{
// Make sure the two sets are identical
if (requiredIndices.Count != artifactUnlockingComponent.TriggeredNodeIndexes.Count
|| !artifactUnlockingComponent.TriggeredNodeIndexes.All(requiredIndices.Contains))
continue;
node = curNode;
return true; // exit early
}
// If we apply artifexium, check that the sets are identical EXCEPT for one extra node.
// This node is a "wildcard" and we'll make a pool so we can pick one to actually unlock.
if (!artifactUnlockingComponent.TriggeredNodeIndexes.All(requiredIndices.Contains) ||
requiredIndices.Count - 1 != artifactUnlockingComponent.TriggeredNodeIndexes.Count)
continue;
potentialNodes.Add(curNode);
}
if (potentialNodes.Count != 0)
node = RobustRandom.Pick(potentialNodes);
return node != null;
}
private void OnUnlockingStarted(Entity<XenoArtifactUnlockingComponent> ent, ref MapInitEvent args)
{
var unlockingStartedEvent = new ArtifactUnlockingStartedEvent();
RaiseLocalEvent(ent.Owner, ref unlockingStartedEvent);
}
private void RaiseUnlockingFinished(
Entity<XenoArtifactUnlockingComponent, XenoArtifactComponent> ent,
Entity<XenoArtifactNodeComponent>? node
)
{
var unlockingFinishedEvent = new ArtifactUnlockingFinishedEvent(node);
RaiseLocalEvent(ent.Owner, ref unlockingFinishedEvent);
}
}
/// <summary>
/// Event for starting artifact unlocking stage.
/// </summary>
[ByRefEvent]
public record struct ArtifactUnlockingStartedEvent;
/// <summary>
/// Event for finishing artifact unlocking stage.
/// </summary>
/// <param name="UnlockedNode">Node which were unlocked. Null if stage was finished without new unlocks.</param>
[ByRefEvent]
public record struct ArtifactUnlockingFinishedEvent(EntityUid? UnlockedNode);