251 lines
9.9 KiB
C#
251 lines
9.9 KiB
C#
using System.Linq;
|
|
using System.Numerics;
|
|
using Content.Client.UserInterface.Controls;
|
|
using Content.Shared.NameIdentifier;
|
|
using Content.Shared.Xenoarchaeology.Artifact;
|
|
using Content.Shared.Xenoarchaeology.Artifact.Components;
|
|
using Content.Shared.Xenoarchaeology.Equipment.Components;
|
|
using Robust.Client.AutoGenerated;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Client.UserInterface.XAML;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Client._DV.Xenoarchaeology.Ui;
|
|
|
|
/// <summary>
|
|
/// DeltaV replacement for NodeScannerDisplay, the UI of the artifact node scanner.
|
|
/// Displays extra information about the artifact unlocking phase.
|
|
/// </summary>
|
|
[GenerateTypedNameReferences]
|
|
public sealed partial class DVNodeScannerDisplay : FancyWindow
|
|
{
|
|
[Dependency] private readonly IEntityManager _ent = default!;
|
|
[Dependency] private readonly IGameTiming _timing= default!;
|
|
|
|
private readonly SharedXenoArtifactSystem _artifact;
|
|
private readonly SpriteSystem _spriteSystem;
|
|
|
|
private Texture? _blipTexture;
|
|
private EntityUid _owner;
|
|
|
|
private float animatedTimeRemaining = 0;
|
|
private float animatedTimeRemainingVelocity = 0;
|
|
|
|
public DVNodeScannerDisplay()
|
|
{
|
|
RobustXamlLoader.Load(this);
|
|
|
|
IoCManager.InjectDependencies(this);
|
|
|
|
_artifact = _ent.System<SharedXenoArtifactSystem>();
|
|
_spriteSystem = _ent.System<SpriteSystem>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets entity that represents hand-held xeno artifact node scanner for which window is opened.
|
|
/// Closes window if <see cref="NodeScannerComponent"/> is not present on entity.
|
|
/// </summary>
|
|
public void SetOwner(EntityUid scannerEntityUid)
|
|
{
|
|
if (!_ent.TryGetComponent<NodeScannerComponent>(scannerEntityUid, out var scannerComponent))
|
|
{
|
|
Close();
|
|
return;
|
|
}
|
|
|
|
_owner = scannerEntityUid;
|
|
|
|
_blipTexture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override void FrameUpdate(FrameEventArgs args)
|
|
{
|
|
base.FrameUpdate(args);
|
|
|
|
|
|
// reset
|
|
NoActiveNodeDataLabel.Visible = true;
|
|
NodeScannerState.Text = Loc.GetString("node-scanner-artifact-non-connected");
|
|
ProgressBarContainer.Visible = false;
|
|
ActiveNodesList.Visible = false;
|
|
SystemWarningPanel.Visible = false;
|
|
|
|
|
|
if (!_ent.TryGetComponent(_owner, out NodeScannerConnectedComponent? connectedScanner))
|
|
return;
|
|
|
|
var attachedArtifactEnt = connectedScanner.AttachedTo;
|
|
if (!_ent.TryGetComponent(attachedArtifactEnt, out XenoArtifactComponent? artifactComponent))
|
|
return;
|
|
|
|
_ent.TryGetComponent(attachedArtifactEnt, out XenoArtifactUnlockingComponent? unlockingComponent);
|
|
|
|
|
|
// header banner
|
|
NodeScannerState.Text =
|
|
unlockingComponent != null
|
|
? Loc.GetString("node-scanner-artifact-state-unlocking")
|
|
: _timing.CurTime < artifactComponent.NextUnlockTime
|
|
? Loc.GetString("node-scanner-artifact-state-cooldown")
|
|
: Loc.GetString("node-scanner-artifact-state-ready");
|
|
|
|
|
|
// time remaining progress bar
|
|
var unlockTimeRemaining =
|
|
unlockingComponent != null
|
|
? MathF.Max(0f, (float)(unlockingComponent.EndTime - _timing.CurTime).TotalSeconds)
|
|
: 0f;
|
|
|
|
// damped-oscillator-style system for animating sudden jumps in time remaining
|
|
animatedTimeRemainingVelocity += 0.001f * (unlockTimeRemaining - animatedTimeRemaining) - 0.1f * animatedTimeRemainingVelocity;
|
|
animatedTimeRemaining = MathF.Max(0f, animatedTimeRemaining + animatedTimeRemainingVelocity);
|
|
|
|
// sigmoid-style function to make sure progress bar never goes over 100%
|
|
ProgressBar.Value = 2f / (1f + MathF.Exp(-0.08f * animatedTimeRemaining)) - 1f;
|
|
|
|
|
|
// node data and warning panel
|
|
(List<int> triggeredIndexesOrdered, HashSet<int> triggeredIndexesRelated)? unlockingDataToShow = null;
|
|
if (unlockingComponent != null)
|
|
{
|
|
unlockingDataToShow = (
|
|
unlockingComponent.TriggeredNodeIndexesOrdered,
|
|
unlockingComponent.TriggeredNodeIndexesRelated
|
|
);
|
|
}
|
|
else if (
|
|
artifactComponent.LastUnlockingEndTime != null
|
|
&& _timing.CurTime - artifactComponent.LastUnlockingEndTime < TimeSpan.FromSeconds(60)
|
|
)
|
|
{
|
|
unlockingDataToShow = (
|
|
artifactComponent.LastUnlockingTriggeredNodeIndexesOrdered,
|
|
artifactComponent.LastUnlockingTriggeredNodeIndexesRelated
|
|
);
|
|
}
|
|
|
|
if (
|
|
unlockingDataToShow is (List<int> triggeredIndexesOrdered, HashSet<int> triggeredIndexesRelated)
|
|
&& triggeredIndexesOrdered.Count > 0
|
|
)
|
|
{
|
|
// show triggered node data instead of 'no data' placeholder
|
|
NoActiveNodeDataLabel.Visible = false;
|
|
ProgressBarContainer.Visible = true;
|
|
ActiveNodesList.Visible = true;
|
|
|
|
|
|
// node list
|
|
ActiveNodesList.Children.Clear();
|
|
foreach (var triggeredIndex in triggeredIndexesOrdered)
|
|
{
|
|
var node = _artifact.GetNode((attachedArtifactEnt, artifactComponent), triggeredIndex);
|
|
|
|
var nodeControl = new Button
|
|
{
|
|
Margin = new Thickness(15, 5, 15, 0),
|
|
MaxHeight = 40,
|
|
Disabled = true,
|
|
HorizontalExpand = true
|
|
};
|
|
ActiveNodesList.AddChild(nodeControl);
|
|
|
|
var mainContainer = new BoxContainer()
|
|
{
|
|
Orientation = BoxContainer.LayoutOrientation.Horizontal,
|
|
HorizontalExpand = true,
|
|
};
|
|
nodeControl.AddChild(mainContainer);
|
|
|
|
// mimics suitCoordsIndicator in CrewMonitoringWindow
|
|
var relatednessIndicator = new TextureRect()
|
|
{
|
|
Texture = _blipTexture,
|
|
TextureScale = new Vector2(0.25f, 0.25f),
|
|
Modulate = triggeredIndexesRelated.Contains(triggeredIndex) ? Color.LimeGreen : Color.DarkRed,
|
|
HorizontalAlignment = HAlignment.Center,
|
|
VerticalAlignment = VAlignment.Center,
|
|
};
|
|
mainContainer.AddChild(relatednessIndicator);
|
|
|
|
var triggerTip = Loc.GetString(node.Comp.TriggerTip!);
|
|
var triggeredNodeName = (_ent.GetComponentOrNull<NameIdentifierComponent>(node)?.Identifier ?? 0).ToString("D3");
|
|
var descriptionLabel = new Label()
|
|
{
|
|
Text = $"{triggerTip} ({triggeredNodeName})",
|
|
HorizontalExpand = true,
|
|
ClipText = true,
|
|
Margin = new Thickness(8, 0, 0, 0),
|
|
};
|
|
mainContainer.AddChild(descriptionLabel);
|
|
}
|
|
|
|
|
|
// warning panel (mimics that of PowerMonitoringWindow)
|
|
(Color background, Color border, string message, string subtext)? warningDataToShow = null;
|
|
if (unlockingComponent == null)
|
|
{
|
|
if (artifactComponent.LastUnlockingSuccessful)
|
|
{
|
|
warningDataToShow = (
|
|
background: Color.Green,
|
|
border: Color.DarkGreen,
|
|
message: Loc.GetString("node-scan-warning-unlock-complete"),
|
|
subtext: Loc.GetString("node-scan-warning-unlock-complete-subtext")
|
|
);
|
|
}
|
|
else if (triggeredIndexesOrdered.Count == triggeredIndexesRelated.Count)
|
|
{
|
|
warningDataToShow = (
|
|
background: Color.Orange,
|
|
border: Color.DarkOrange,
|
|
message: Loc.GetString("node-scan-warning-partial-complete"),
|
|
subtext: Loc.GetString("node-scan-warning-partial-complete-subtext")
|
|
);
|
|
}
|
|
else
|
|
{
|
|
warningDataToShow = (
|
|
background: Color.Red,
|
|
border: Color.DarkRed,
|
|
message: Loc.GetString("node-scan-warning-failure-complete"),
|
|
subtext: Loc.GetString("node-scan-warning-failure-complete-subtext")
|
|
);
|
|
}
|
|
}
|
|
else if (triggeredIndexesOrdered.Count != triggeredIndexesRelated.Count)
|
|
{
|
|
warningDataToShow = (
|
|
background: Color.Red,
|
|
border: Color.DarkRed,
|
|
message: Loc.GetString("node-scan-warning-failure-imminent"),
|
|
subtext: Loc.GetString("node-scan-warning-failure-imminent-subtext")
|
|
);
|
|
}
|
|
|
|
if (warningDataToShow is (Color background, Color border, string message, string subtext))
|
|
{
|
|
SystemWarningPanel.Visible = true;
|
|
|
|
SystemWarningPanel.PanelOverride = new StyleBoxFlat
|
|
{
|
|
BackgroundColor = background,
|
|
BorderColor = border,
|
|
BorderThickness = new Thickness(2),
|
|
};
|
|
|
|
// warning panel pulses during unlocking phase
|
|
var lit = unlockingComponent != null && _timing.RealTime.TotalSeconds % 1f > 0.5f;
|
|
SystemWarningPanel.Modulate = lit ? Color.White : new Color(178, 178, 178);
|
|
|
|
SystemWarningLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(message));
|
|
SystemWarningSublabel.SetMessage(FormattedMessage.FromMarkupOrThrow(subtext));
|
|
}
|
|
}
|
|
}
|
|
}
|