_consoleMenu.OnClose += Close;
_consoleMenu.OpenCentered();
- _consoleMenu.OnServerSelectionButtonPressed += _ =>
+ _consoleMenu.OnServerSelectionButtonPressed += () =>
{
SendMessage(new AnalysisConsoleServerSelectionMessage());
};
- _consoleMenu.OnScanButtonPressed += _ =>
+ _consoleMenu.OnScanButtonPressed += () =>
{
SendMessage(new AnalysisConsoleScanButtonPressedMessage());
};
- _consoleMenu.OnPrintButtonPressed += _ =>
+ _consoleMenu.OnPrintButtonPressed += () =>
{
SendMessage(new AnalysisConsolePrintButtonPressedMessage());
};
- _consoleMenu.OnDestroyButtonPressed += _ =>
+ _consoleMenu.OnDestroyButtonPressed += () =>
{
SendMessage(new AnalysisConsoleDestroyButtonPressedMessage());
};
if (!disposing)
return;
- _consoleMenu?.AnalysisDestroyWindow?.Close();
_consoleMenu?.Dispose();
}
}
using Content.Shared.Xenoarchaeology.Equipment;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
-using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
public sealed partial class AnalysisConsoleMenu : FancyWindow
{
[Dependency] private readonly IEntityManager _ent = default!;
-
- public AnalysisDestroyWindow? AnalysisDestroyWindow;
-
- public event Action<BaseButton.ButtonEventArgs>? OnServerSelectionButtonPressed;
- public event Action<BaseButton.ButtonEventArgs>? OnScanButtonPressed;
- public event Action<BaseButton.ButtonEventArgs>? OnPrintButtonPressed;
- public event Action<BaseButton.ButtonEventArgs>? OnDestroyButtonPressed;
+ public event Action? OnServerSelectionButtonPressed;
+ public event Action? OnScanButtonPressed;
+ public event Action? OnPrintButtonPressed;
+ public event Action? OnDestroyButtonPressed;
public AnalysisConsoleMenu()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
- ServerSelectionButton.OnPressed += a => OnServerSelectionButtonPressed?.Invoke(a);
- ScanButton.OnPressed += a => OnScanButtonPressed?.Invoke(a);
- PrintButton.OnPressed += a => OnPrintButtonPressed?.Invoke(a);
- DestroyButton.OnPressed += _ => OnDestroyButton();
- }
-
- private void OnDestroyButton()
- {
- // check if window is already open
- if (AnalysisDestroyWindow is { IsOpen: true })
- {
- AnalysisDestroyWindow.MoveToFront();
- return;
- }
-
- // open a new one
- AnalysisDestroyWindow = new ();
- AnalysisDestroyWindow.OpenCentered();
-
- AnalysisDestroyWindow.OnYesButton += a =>
- {
- OnDestroyButtonPressed?.Invoke(a);
- };
+ ServerSelectionButton.OnPressed += _ => OnServerSelectionButtonPressed?.Invoke();
+ ScanButton.OnPressed += _ => OnScanButtonPressed?.Invoke();
+ PrintButton.OnPressed += _ => OnPrintButtonPressed?.Invoke();
+ DestroyButton.OnPressed += _ => OnDestroyButtonPressed?.Invoke();
}
public void SetButtonsDisabled(AnalysisConsoleScanUpdateState state)
ScanButton.Disabled = !state.CanScan;
PrintButton.Disabled = !state.CanPrint;
- var disabled = !state.ServerConnected || !state.CanScan;
+ var disabled = !state.ServerConnected || !state.CanScan || state.PointAmount <= 0;
DestroyButton.Disabled = disabled;
("seconds", (int) state.TotalTime.TotalSeconds - (int) state.TimeRemaining.TotalSeconds));
ProgressBar.Value = (float) state.TimeRemaining.Divide(state.TotalTime);
}
-
- public override void Close()
- {
- base.Close();
-
- AnalysisDestroyWindow?.Close();
- }
}
+++ /dev/null
-<controls:FancyWindow
- xmlns="https://spacestation14.io"
- xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
- Title="{Loc 'analysis-destroy-window-title'}"
- MinSize="256 100">
- <BoxContainer
- Margin="10 10"
- HorizontalExpand="True"
- Orientation="Vertical"
- VerticalExpand="True">
- <Label Text="{Loc 'analysis-destroy-window-text'}" />
- <BoxContainer
- Margin="10 10 10 10"
- VerticalAlignment="Bottom"
- Orientation="Horizontal"
- HorizontalExpand="True">
- <Button Name="YesButton" Text="{Loc 'analysis-destroy-window-yes'}" HorizontalExpand="True"></Button>
- <BoxContainer SetSize="10 10"></BoxContainer>
- <Button Name="NoButton" Text="{Loc 'analysis-destroy-window-no'}" HorizontalExpand="True"></Button>
- </BoxContainer>
- </BoxContainer>
-</controls:FancyWindow>
+++ /dev/null
-using Content.Client.Stylesheets;
-using Content.Client.UserInterface.Controls;
-using Robust.Client.AutoGenerated;
-using Robust.Client.UserInterface.Controls;
-using Robust.Client.UserInterface.XAML;
-
-namespace Content.Client.Xenoarchaeology.Ui;
-
-[GenerateTypedNameReferences]
-public sealed partial class AnalysisDestroyWindow : FancyWindow
-{
- public event Action<BaseButton.ButtonEventArgs>? OnYesButton;
-
- public AnalysisDestroyWindow()
- {
- RobustXamlLoader.Load(this);
-
- YesButton.AddStyleClass(StyleBase.ButtonCaution);
- YesButton.OnPressed += a =>
- {
- OnYesButton?.Invoke(a);
- Close();
- };
- NoButton.OnPressed += _ => Close();
- }
-}
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly SharedAmbientSoundSystem _ambienntSound = default!;
+ [Dependency] private readonly SharedAmbientSoundSystem _ambientSound = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly ArtifactSystem _artifact = default!;
var totalTime = TimeSpan.Zero;
var canScan = false;
var canPrint = false;
+ var points = 0;
if (component.AnalyzerEntity != null && TryComp<ArtifactAnalyzerComponent>(component.AnalyzerEntity, out var analyzer))
{
artifact = analyzer.LastAnalyzedArtifact;
totalTime = analyzer.AnalysisDuration * analyzer.AnalysisDurationMulitplier;
canScan = analyzer.Contacts.Any();
canPrint = analyzer.ReadyToPrint;
+
+ // the artifact that's actually on the scanner right now.
+ if (GetArtifactForAnalysis(component.AnalyzerEntity, analyzer) is { } current)
+ points = _artifact.GetResearchPointValue(current);
}
var analyzerConnected = component.AnalyzerEntity != null;
var serverConnected = TryComp<ResearchClientComponent>(uid, out var client) && client.ConnectedToServer;
var remaining = active != null ? _timing.CurTime - active.StartTime : TimeSpan.Zero;
var state = new AnalysisConsoleScanUpdateState(artifact, analyzerConnected, serverConnected,
- canScan, canPrint, msg, scanning, remaining, totalTime);
+ canScan, canPrint, msg, scanning, remaining, totalTime, points);
var bui = _ui.GetUi(uid, ArtifactAnalzyerUiKey.Key);
_ui.SetUiState(bui, state);
if (!_research.TryGetClientServer(uid, out var server, out var serverComponent))
return;
- var entToDestroy = GetArtifactForAnalysis(component.AnalyzerEntity);
- if (entToDestroy == null)
+ var artifact = GetArtifactForAnalysis(component.AnalyzerEntity);
+ if (artifact == null)
return;
- if (TryComp<ArtifactAnalyzerComponent>(component.AnalyzerEntity.Value, out var analyzer) &&
- analyzer.LastAnalyzedArtifact == entToDestroy)
- {
- ResetAnalyzer(component.AnalyzerEntity.Value);
- }
+ var pointValue = _artifact.GetResearchPointValue(artifact.Value);
- _research.AddPointsToServer(server.Value, _artifact.GetResearchPointValue(entToDestroy.Value), serverComponent);
- EntityManager.DeleteEntity(entToDestroy.Value);
+ if (pointValue == 0)
+ return;
+
+ _research.AddPointsToServer(server.Value, pointValue, serverComponent);
+ _artifact.AdjustConsumedPoints(artifact.Value, pointValue);
_audio.PlayPvs(component.DestroySound, component.AnalyzerEntity.Value, AudioParams.Default.WithVolume(2f));
- _popup.PopupEntity(Loc.GetString("analyzer-artifact-destroy-popup"),
+ _popup.PopupEntity(Loc.GetString("analyzer-artifact-extract-popup"),
component.AnalyzerEntity.Value, PopupType.Large);
UpdateUserInterface(uid, component);
/// <summary>
/// Cancels scans if the artifact changes nodes (is activated) during the scan.
/// </summary>
- /// <param name="uid"></param>
- /// <param name="component"></param>
- /// <param name="args"></param>
private void OnArtifactActivated(EntityUid uid, ActiveScannedArtifactComponent component, ArtifactActivatedEvent args)
{
CancelScan(uid);
/// <summary>
/// Checks to make sure that the currently scanned artifact isn't moved off of the scanner
/// </summary>
- /// <param name="uid"></param>
- /// <param name="component"></param>
- /// <param name="args"></param>
private void OnScannedMoved(EntityUid uid, ActiveScannedArtifactComponent component, ref MoveEvent args)
{
if (!TryComp<ArtifactAnalyzerComponent>(component.Scanner, out var analyzer))
/// <summary>
/// Stops the current scan
/// </summary>
- /// <param name="artifact">The artifact being scanned</param>
- /// <param name="component"></param>
- /// <param name="analyzer">The artifact analyzer component</param>
[PublicAPI]
public void CancelScan(EntityUid artifact, ActiveScannedArtifactComponent? component = null, ArtifactAnalyzerComponent? analyzer = null)
{
/// <summary>
/// Finishes the current scan.
/// </summary>
- /// <param name="uid">The analyzer that is scanning</param>
- /// <param name="component"></param>
- /// <param name="active"></param>
[PublicAPI]
public void FinishScan(EntityUid uid, ArtifactAnalyzerComponent? component = null, ActiveArtifactAnalyzerComponent? active = null)
{
if (TryComp<ApcPowerReceiverComponent>(uid, out var powa))
powa.NeedsPower = true;
- _ambienntSound.SetAmbience(uid, true);
+ _ambientSound.SetAmbience(uid, true);
}
private void OnAnalyzeEnd(EntityUid uid, ActiveArtifactAnalyzerComponent component, ComponentShutdown args)
if (TryComp<ApcPowerReceiverComponent>(uid, out var powa))
powa.NeedsPower = false;
- _ambienntSound.SetAmbience(uid, false);
+ _ambientSound.SetAmbience(uid, false);
}
private void OnPowerChanged(EntityUid uid, ActiveArtifactAnalyzerComponent component, ref PowerChangedEvent args)
public sealed class SuppressArtifactContainerSystem : EntitySystem
{
+ [Dependency] private readonly ArtifactSystem _artifact = default!;
+
public override void Initialize()
{
base.Initialize();
private void OnInserted(EntityUid uid, SuppressArtifactContainerComponent component, EntInsertedIntoContainerMessage args)
{
- if (!TryComp(args.Entity, out ArtifactComponent? artifact))
+ if (!TryComp<ArtifactComponent>(args.Entity, out var artifact))
return;
- artifact.IsSuppressed = true;
+ _artifact.SetIsSuppressed(args.Entity, true, artifact);
}
private void OnRemoved(EntityUid uid, SuppressArtifactContainerComponent component, EntRemovedFromContainerMessage args)
{
- if (!TryComp(args.Entity, out ArtifactComponent? artifact))
+ if (!TryComp<ArtifactComponent>(args.Entity, out var artifact))
return;
- artifact.IsSuppressed = false;
+ _artifact.SetIsSuppressed(args.Entity, false, artifact);
}
}
namespace Content.Server.Xenoarchaeology.XenoArtifacts;
-[RegisterComponent]
+[RegisterComponent, Access(typeof(ArtifactSystem))]
public sealed class ArtifactComponent : Component
{
/// <summary>
public TimeSpan LastActivationTime;
/// <summary>
- /// The base price of each node for an artifact
+ /// A multiplier applied to the calculated point value
+ /// to determine the monetary value of the artifact
/// </summary>
- [DataField("pricePerNode")]
- public int PricePerNode = 500;
+ [DataField("priceMultiplier"), ViewVariables(VVAccess.ReadWrite)]
+ public float PriceMultiplier = 0.05f;
/// <summary>
/// The base amount of research points for each artifact node.
/// </summary>
- [DataField("pointsPerNode")]
+ [DataField("pointsPerNode"), ViewVariables(VVAccess.ReadWrite)]
public int PointsPerNode = 5000;
+ /// <summary>
+ /// Research points which have been "consumed" from the theoretical max value of the artifact.
+ /// </summary>
+ [DataField("consumedPoints"), ViewVariables(VVAccess.ReadWrite)]
+ public int ConsumedPoints;
+
/// <summary>
/// A multiplier that is raised to the power of the average depth of a node.
/// Used for calculating the research point value of an artifact node.
/// </summary>
- [DataField("pointDangerMultiplier")]
+ [DataField("pointDangerMultiplier"), ViewVariables(VVAccess.ReadWrite)]
public float PointDangerMultiplier = 1.35f;
}
/// </remarks>
private void GetPrice(EntityUid uid, ArtifactComponent component, ref PriceCalculationEvent args)
{
- var price = component.NodeTree.Sum(x => GetNodePrice(x, component));
-
- // 25% bonus for fully exploring every node.
- var fullyExploredBonus = component.NodeTree.Any(x => !x.Triggered) ? 1 : 1.25f;
-
- args.Price =+ price * fullyExploredBonus;
- }
-
- private float GetNodePrice(ArtifactNode node, ArtifactComponent component)
- {
- if (!node.Discovered) //no money for undiscovered nodes.
- return 0;
-
- var triggerProto = _prototype.Index<ArtifactTriggerPrototype>(node.Trigger);
- var effectProto = _prototype.Index<ArtifactEffectPrototype>(node.Effect);
-
- //quarter price if not triggered
- var priceMultiplier = node.Triggered ? 1f : 0.25f;
- //the danger is the average of node depth, effect danger, and trigger danger.
- var nodeDanger = (node.Depth + effectProto.TargetDepth + triggerProto.TargetDepth) / 3;
-
- var price = MathF.Pow(2f, nodeDanger) * component.PricePerNode * priceMultiplier;
- return price;
+ args.Price =+ GetResearchPointValue(uid, component) * component.PriceMultiplier;
}
/// <summary>
var sumValue = component.NodeTree.Sum(n => GetNodePointValue(n, component, getMaxPrice));
var fullyExploredBonus = component.NodeTree.All(x => x.Triggered) || getMaxPrice ? 1.25f : 1;
+ sumValue -= component.ConsumedPoints;
+
+ return (int) (sumValue * fullyExploredBonus);
+ }
+
+ /// <summary>
+ /// Adjusts how many points on the artifact have been consumed
+ /// </summary>
+ public void AdjustConsumedPoints(EntityUid uid, int amount, ArtifactComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return;
+
+ component.ConsumedPoints += amount;
+ }
+
+ /// <summary>
+ /// Sets whether or not the artifact is suppressed,
+ /// preventing it from activating
+ /// </summary>
+ public void SetIsSuppressed(EntityUid uid, bool suppressed, ArtifactComponent? component = null)
+ {
+ if (!Resolve(uid, ref component))
+ return;
- var pointValue = (int) (sumValue * fullyExploredBonus);
- return pointValue;
+ component.IsSuppressed = suppressed;
}
/// <summary>
var currentNode = GetNodeFromId(component.CurrentNodeId.Value, component);
var allNodes = currentNode.Edges;
- _sawmill.Debug("artifact", $"our node: {currentNode.Id}");
- _sawmill.Debug("artifact", $"other nodes: {string.Join(", ", allNodes)}");
+ _sawmill.Debug($"our node: {currentNode.Id}");
+ _sawmill.Debug($"other nodes: {string.Join(", ", allNodes)}");
if (TryComp<BiasedArtifactComponent>(uid, out var bias) &&
TryComp<TraversalDistorterComponent>(bias.Provider, out var trav) &&
}
var undiscoveredNodes = allNodes.Where(x => !GetNodeFromId(x, component).Discovered).ToList();
- _sawmill.Debug("artifact", $"Undiscovered nodes: {string.Join(", ", undiscoveredNodes)}");
+ _sawmill.Debug($"Undiscovered nodes: {string.Join(", ", undiscoveredNodes)}");
var newNode = _random.Pick(allNodes);
if (undiscoveredNodes.Any() && _random.Prob(0.75f))
{
newNode = _random.Pick(undiscoveredNodes);
}
- _sawmill.Debug("artifact", $"Going to node {newNode}");
+ _sawmill.Debug($"Going to node {newNode}");
return GetNodeFromId(newNode, component);
}
public TimeSpan TotalTime;
+ public int PointAmount;
+
public AnalysisConsoleScanUpdateState(EntityUid? artifact, bool analyzerConnected, bool serverConnected, bool canScan, bool canPrint,
- FormattedMessage? scanReport, bool scanning, TimeSpan timeRemaining, TimeSpan totalTime)
+ FormattedMessage? scanReport, bool scanning, TimeSpan timeRemaining, TimeSpan totalTime, int pointAmount)
{
Artifact = artifact;
AnalyzerConnected = analyzerConnected;
Scanning = scanning;
TimeRemaining = timeRemaining;
TotalTime = totalTime;
+
+ PointAmount = pointAmount;
}
}
analysis-console-scan-tooltip-info = Scan artifacts to learn information about their structure.
analysis-console-print-button = Print
analysis-console-print-tooltip-info = Print out the current information about the artifact.
-analysis-console-destroy-button = Destroy
-analysis-console-destroy-button-info = Destroy artifacts to generate points based on how much has been unlocked.
+analysis-console-destroy-button = Extract
+analysis-console-destroy-button-info = Extract points from an artifact based on the explored nodes.
analysis-console-info-no-scanner = No analyzer connected! Please connect one using a multitool.
analysis-console-info-no-artifact = No artifact present! Place one on the pad then scan for information.
*[other] T-{$seconds} seconds
}
-analysis-destroy-window-title = Confirm Destruction
-analysis-destroy-window-text = Destroy the artifact, converting it into research points?
-analysis-destroy-window-yes = Yes
-analysis-destroy-window-no = No
-
analyzer-artifact-component-upgrade-analysis = analysis duration
analysis-console-print-popup = The console printed out a report.
-analyzer-artifact-destroy-popup = The artifact disintegrated into energy!
+analyzer-artifact-extract-popup = Energy shimmers on the artifact's surface!
analysis-report-title = Artifact Report: Node {$id}
\ No newline at end of file
To set them up, simply link them with a multitool, set an artifact on top of the analyzer, and press the [color=#a4885c]Scan[/color] button.
-Using the console, you can permanently destroy an artifact in exchange for points. This is irreversible, so be sure to confirm with your department that all research on it has concluded.
+Using the console, you can extract points from the artifact using the [color=#a4885c]Extract[/color] button. The amount of points you extract is based on how many of the nodes of the artifact have been activated.
</Document>