Re-organize all projects (#4166)
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Construction.Prototypes
|
||||
{
|
||||
[Prototype("constructionGraph")]
|
||||
public class ConstructionGraphPrototype : IPrototype, ISerializationHooks
|
||||
{
|
||||
private readonly Dictionary<string, ConstructionGraphNode> _nodes = new();
|
||||
private readonly Dictionary<ValueTuple<string, string>, ConstructionGraphNode[]?> _paths = new();
|
||||
private readonly Dictionary<string, Dictionary<ConstructionGraphNode, ConstructionGraphNode?>> _pathfinding = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("id", required: true)]
|
||||
public string ID { get; } = default!;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("start")]
|
||||
public string? Start { get; }
|
||||
|
||||
[DataField("graph", priority: 0)]
|
||||
private List<ConstructionGraphNode> _graph = new();
|
||||
|
||||
[ViewVariables]
|
||||
public IReadOnlyDictionary<string, ConstructionGraphNode> Nodes => _nodes;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
_nodes.Clear();
|
||||
|
||||
foreach (var graphNode in _graph)
|
||||
{
|
||||
if (string.IsNullOrEmpty(graphNode.Name))
|
||||
{
|
||||
throw new InvalidDataException($"Name of graph node is null in construction graph {ID}!");
|
||||
}
|
||||
|
||||
_nodes[graphNode.Name] = graphNode;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(Start) || !_nodes.ContainsKey(Start))
|
||||
throw new InvalidDataException($"Starting node for construction graph {ID} is null, empty or invalid!");
|
||||
}
|
||||
|
||||
public ConstructionGraphEdge? Edge(string startNode, string nextNode)
|
||||
{
|
||||
var start = _nodes[startNode];
|
||||
return start.GetEdge(nextNode);
|
||||
}
|
||||
|
||||
public bool TryPath(string startNode, string finishNode, [NotNullWhen(true)] out ConstructionGraphNode[]? path)
|
||||
{
|
||||
return (path = Path(startNode, finishNode)) != null;
|
||||
}
|
||||
|
||||
public ConstructionGraphNode[]? Path(string startNode, string finishNode)
|
||||
{
|
||||
var tuple = new ValueTuple<string, string>(startNode, finishNode);
|
||||
|
||||
if (_paths.ContainsKey(tuple))
|
||||
return _paths[tuple];
|
||||
|
||||
// Get graph given the current start.
|
||||
|
||||
Dictionary<ConstructionGraphNode, ConstructionGraphNode?> pathfindingForStart;
|
||||
if (_pathfinding.ContainsKey(startNode))
|
||||
{
|
||||
pathfindingForStart = _pathfinding[startNode];
|
||||
}
|
||||
else
|
||||
{
|
||||
pathfindingForStart = _pathfinding[startNode] = PathsForStart(startNode);
|
||||
}
|
||||
|
||||
// Follow the chain backwards.
|
||||
|
||||
var start = _nodes[startNode];
|
||||
var finish = _nodes[finishNode];
|
||||
|
||||
var current = finish;
|
||||
var path = new List<ConstructionGraphNode>();
|
||||
while (current != start)
|
||||
{
|
||||
// No path.
|
||||
if (current == null || !pathfindingForStart.ContainsKey(current))
|
||||
{
|
||||
// We remember this for next time.
|
||||
_paths[tuple] = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
path.Add(current);
|
||||
|
||||
current = pathfindingForStart[current];
|
||||
}
|
||||
|
||||
path.Reverse();
|
||||
return _paths[tuple] = path.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses breadth first search for pathfinding.
|
||||
/// </summary>
|
||||
/// <param name="start"></param>
|
||||
private Dictionary<ConstructionGraphNode, ConstructionGraphNode?> PathsForStart(string start)
|
||||
{
|
||||
// TODO: Make this use A* or something, although it's not that important.
|
||||
var startNode = _nodes[start];
|
||||
|
||||
var frontier = new Queue<ConstructionGraphNode>();
|
||||
var cameFrom = new Dictionary<ConstructionGraphNode, ConstructionGraphNode?>();
|
||||
|
||||
frontier.Enqueue(startNode);
|
||||
cameFrom[startNode] = null;
|
||||
|
||||
while (frontier.Count != 0)
|
||||
{
|
||||
var current = frontier.Dequeue();
|
||||
foreach (var edge in current.Edges)
|
||||
{
|
||||
var edgeNode = _nodes[edge.Target];
|
||||
if(cameFrom.ContainsKey(edgeNode)) continue;
|
||||
frontier.Enqueue(edgeNode);
|
||||
cameFrom[edgeNode] = current;
|
||||
}
|
||||
}
|
||||
|
||||
return cameFrom;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Construction.Conditions;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.Construction.Prototypes
|
||||
{
|
||||
[Prototype("construction")]
|
||||
public class ConstructionPrototype : IPrototype
|
||||
{
|
||||
[DataField("conditions")] private List<IConstructionCondition> _conditions = new();
|
||||
|
||||
/// <summary>
|
||||
/// Friendly name displayed in the construction GUI.
|
||||
/// </summary>
|
||||
[DataField("name")]
|
||||
public string Name { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// "Useful" description displayed in the construction GUI.
|
||||
/// </summary>
|
||||
[DataField("description")]
|
||||
public string Description { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ConstructionGraphPrototype"/> this construction will be using.
|
||||
/// </summary>
|
||||
[DataField("graph")]
|
||||
public string Graph { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The target <see cref="ConstructionGraphNode"/> this construction will guide the user to.
|
||||
/// </summary>
|
||||
[DataField("targetNode")]
|
||||
public string TargetNode { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The starting <see cref="ConstructionGraphNode"/> this construction will start at.
|
||||
/// </summary>
|
||||
[DataField("startNode")]
|
||||
public string StartNode { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Texture path inside the construction GUI.
|
||||
/// </summary>
|
||||
[DataField("icon")]
|
||||
public SpriteSpecifier Icon { get; } = SpriteSpecifier.Invalid;
|
||||
|
||||
/// <summary>
|
||||
/// If you can start building or complete steps on impassable terrain.
|
||||
/// </summary>
|
||||
[DataField("canBuildInImpassable")]
|
||||
public bool CanBuildInImpassable { get; private set; }
|
||||
|
||||
[DataField("category")] public string Category { get; private set; } = string.Empty;
|
||||
|
||||
[DataField("objectType")] public ConstructionType Type { get; private set; } = ConstructionType.Structure;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("id", required: true)]
|
||||
public string ID { get; } = default!;
|
||||
|
||||
[DataField("placementMode")]
|
||||
public string PlacementMode { get; } = "PlaceFree";
|
||||
|
||||
/// <summary>
|
||||
/// Whether this construction can be constructed rotated or not.
|
||||
/// </summary>
|
||||
[DataField("canRotate")]
|
||||
public bool CanRotate { get; } = true;
|
||||
|
||||
public IReadOnlyList<IConstructionCondition> Conditions => _conditions;
|
||||
}
|
||||
|
||||
public enum ConstructionType
|
||||
{
|
||||
Structure,
|
||||
Item,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user