XenoArch [Science Overhaul] (#12204)
* multi-node xeno artifacts * refactor existing artifact effects * more tweaks to generation * more shit plus fix tests * more generation stuff plus threat levels * doink * now make it build * defer the artifact activation to not cause errors also pricing * some changes * all of the yaml + ui stuff for artifact analyzer * machine linking and starting to make the ui functional * artifact analyzer display * a shit ton of artifact analyzer stuff * more changes; making destroy work properly; progress bar tweaks * getting shit going! ALL RIGHT * small tweaks that didn't help much * Komm susser todd: the end of analysis * recipes and hints and ui, oh my! * add some in-game sources gotta prepare for day 1 launch * node data + ditch random seed in place of id * bunch of triggers * finish off the last few triggers * implement machine examine verb * knock, flicker, blink, throw * shatter, foam, shuffle, heat * fix all the shit i broke * *some* of these have to be good, no? 25 effects * callin' it there for effects * comments + reword some trigger hints * don't mind this little commit here * byref event * fix brokey node entry * fix low pressure trigger * mirror review plus fixing 0x40's bug also the throw artifact threw incorrectly * randomize the event message a teeny bit
This commit is contained in:
@@ -1,50 +1,122 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.Cargo.Systems;
|
||||
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Xenoarchaeology.XenoArtifacts;
|
||||
|
||||
public sealed class ArtifactSystem : EntitySystem
|
||||
public sealed partial class ArtifactSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
|
||||
private const int PricePerNode = 500;
|
||||
private const int PointsPerNode = 5000;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ArtifactComponent, MapInitEvent>(OnInit);
|
||||
SubscribeLocalEvent<ArtifactComponent, PriceCalculationEvent>(GetPrice);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, ArtifactComponent component, MapInitEvent args)
|
||||
{
|
||||
if (component.RandomTrigger)
|
||||
{
|
||||
AddRandomTrigger(uid, component);
|
||||
}
|
||||
RandomizeArtifact(component);
|
||||
}
|
||||
|
||||
private void AddRandomTrigger(EntityUid uid, ArtifactComponent? component = null)
|
||||
/// <summary>
|
||||
/// Calculates the price of an artifact based on
|
||||
/// how many nodes have been unlocked/triggered
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// General balancing (for fully unlocked artifacts):
|
||||
/// Simple (1-2 Nodes): 1-2K
|
||||
/// Medium (5-8 Nodes): 6-7K
|
||||
/// Complex (7-12 Nodes): 10-11K
|
||||
/// </remarks>
|
||||
private void GetPrice(EntityUid uid, ArtifactComponent component, ref PriceCalculationEvent args)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
if (component.NodeTree == null)
|
||||
return;
|
||||
|
||||
var triggerName = _random.Pick(component.PossibleTriggers);
|
||||
var trigger = (Component) _componentFactory.GetComponent(triggerName);
|
||||
trigger.Owner = uid;
|
||||
var price = component.NodeTree.AllNodes.Sum(GetNodePrice);
|
||||
|
||||
if (EntityManager.HasComponent(uid, trigger.GetType()))
|
||||
{
|
||||
Logger.Error($"Attempted to add a random artifact trigger ({triggerName}) to an entity ({ToPrettyString(uid)}), but it already has the trigger");
|
||||
return;
|
||||
}
|
||||
// 25% bonus for fully exploring every node.
|
||||
var fullyExploredBonus = component.NodeTree.AllNodes.Any(x => !x.Triggered) ? 1 : 1.25f;
|
||||
|
||||
EntityManager.AddComponent(uid, trigger);
|
||||
RaiseLocalEvent(uid, new RandomizeTriggerEvent(), true);
|
||||
args.Price =+ price * fullyExploredBonus;
|
||||
}
|
||||
|
||||
public bool TryActivateArtifact(EntityUid uid, EntityUid? user = null,
|
||||
ArtifactComponent? component = null)
|
||||
private float GetNodePrice(ArtifactNode node)
|
||||
{
|
||||
if (!node.Discovered) //no money for undiscovered nodes.
|
||||
return 0;
|
||||
|
||||
//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 + node.Effect.TargetDepth + node.Trigger.TargetDepth) / 3;
|
||||
|
||||
var price = MathF.Pow(2f, nodeDanger) * PricePerNode * priceMultiplier;
|
||||
return price;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates how many research points the artifact is worht
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Rebalance this shit at some point. Definitely OP.
|
||||
/// </remarks>
|
||||
public int GetResearchPointValue(EntityUid uid, ArtifactComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component) || component.NodeTree == null)
|
||||
return 0;
|
||||
|
||||
var sumValue = component.NodeTree.AllNodes.Sum(GetNodePointValue);
|
||||
var fullyExploredBonus = component.NodeTree.AllNodes.Any(x => !x.Triggered) ? 1 : 1.25f;
|
||||
|
||||
var pointValue = (int) (sumValue * fullyExploredBonus);
|
||||
return pointValue;
|
||||
}
|
||||
|
||||
private float GetNodePointValue(ArtifactNode node)
|
||||
{
|
||||
if (!node.Discovered)
|
||||
return 0;
|
||||
|
||||
var valueDeduction = !node.Triggered ? 0.5f : 1;
|
||||
var nodeDanger = (node.Depth + node.Effect.TargetDepth + node.Trigger.TargetDepth) / 3;
|
||||
|
||||
return (nodeDanger+1) * PointsPerNode * valueDeduction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Randomize a given artifact.
|
||||
/// </summary>
|
||||
[PublicAPI]
|
||||
public void RandomizeArtifact(ArtifactComponent component)
|
||||
{
|
||||
var nodeAmount = _random.Next(component.NodesMin, component.NodesMax);
|
||||
|
||||
component.NodeTree = new ArtifactTree();
|
||||
|
||||
GenerateArtifactNodeTree(ref component.NodeTree, nodeAmount);
|
||||
EnterNode(component.Owner, ref component.NodeTree.StartNode, component);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to activate the artifact
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="component"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryActivateArtifact(EntityUid uid, EntityUid? user = null, ArtifactComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
@@ -55,25 +127,85 @@ public sealed class ArtifactSystem : EntitySystem
|
||||
|
||||
// check if artifact isn't under cooldown
|
||||
var timeDif = _gameTiming.CurTime - component.LastActivationTime;
|
||||
if (timeDif.TotalSeconds < component.CooldownTime)
|
||||
if (timeDif < component.CooldownTime)
|
||||
return false;
|
||||
|
||||
ForceActivateArtifact(uid, user, component);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ForceActivateArtifact(EntityUid uid, EntityUid? user = null,
|
||||
ArtifactComponent? component = null)
|
||||
/// <summary>
|
||||
/// Forces an artifact to activate
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="component"></param>
|
||||
public void ForceActivateArtifact(EntityUid uid, EntityUid? user = null, ArtifactComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
if (component.CurrentNode == null)
|
||||
return;
|
||||
|
||||
component.LastActivationTime = _gameTiming.CurTime;
|
||||
|
||||
var ev = new ArtifactActivatedEvent()
|
||||
var ev = new ArtifactActivatedEvent
|
||||
{
|
||||
Activator = user
|
||||
};
|
||||
RaiseLocalEvent(uid, ev, true);
|
||||
|
||||
component.CurrentNode.Triggered = true;
|
||||
if (component.CurrentNode.Edges.Any())
|
||||
{
|
||||
var newNode = _random.Pick(component.CurrentNode.Edges);
|
||||
EnterNode(uid, ref newNode, component);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try and get a data object from a node
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity you're getting the data from</param>
|
||||
/// <param name="key">The data's key</param>
|
||||
/// <param name="data">The data you are trying to get.</param>
|
||||
/// <param name="component"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
public bool TryGetNodeData<T>(EntityUid uid, string key, [NotNullWhen(true)] out T data, ArtifactComponent? component = null)
|
||||
{
|
||||
data = default!;
|
||||
|
||||
if (!Resolve(uid, ref component))
|
||||
return false;
|
||||
|
||||
if (component.CurrentNode == null)
|
||||
return false;
|
||||
|
||||
if (component.CurrentNode.NodeData.TryGetValue(key, out var dat) && dat is T value)
|
||||
{
|
||||
data = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the node data to a certain value
|
||||
/// </summary>
|
||||
/// <param name="uid">The artifact</param>
|
||||
/// <param name="key">The key being set</param>
|
||||
/// <param name="value">The value it's being set to</param>
|
||||
/// <param name="component"></param>
|
||||
public void SetNodeData(EntityUid uid, string key, object value, ArtifactComponent? component = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component))
|
||||
return;
|
||||
|
||||
if (component.CurrentNode == null)
|
||||
return;
|
||||
|
||||
component.CurrentNode.NodeData[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user