Re-organize all projects (#4166)
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Construction.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ComputerBoardComponent : Component
|
||||
{
|
||||
public override string Name => "ComputerBoard";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("prototype")]
|
||||
public string? Prototype { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Notification;
|
||||
using Content.Shared.Verbs;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Construction.Components
|
||||
{
|
||||
public partial class ConstructionComponent
|
||||
{
|
||||
[Verb]
|
||||
public sealed class DeconstructibleVerb : Verb<ConstructionComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, ConstructionComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
if (((component.Target != null) && (component.Target.Name == component.DeconstructionNodeIdentifier)) ||
|
||||
((component.Node != null) && (component.Node.Name == component.DeconstructionNodeIdentifier)))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.CategoryData = VerbCategories.Construction;
|
||||
data.Text = Loc.GetString("Begin deconstructing");
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png";
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, ConstructionComponent component)
|
||||
{
|
||||
component.SetNewTarget(component.DeconstructionNodeIdentifier);
|
||||
if (component.Target == null)
|
||||
{
|
||||
// Maybe check, but on the flip-side a better solution might be to not make it undeconstructible in the first place, no?
|
||||
component.Owner.PopupMessage(user, Loc.GetString("There is no way to deconstruct this."));
|
||||
}
|
||||
else
|
||||
{
|
||||
component.Owner.PopupMessage(user, Loc.GetString("Examine to see instructions."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
583
Content.Server/Construction/Components/ConstructionComponent.cs
Normal file
583
Content.Server/Construction/Components/ConstructionComponent.cs
Normal file
@@ -0,0 +1,583 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Tools.Components;
|
||||
using Content.Shared.Construction;
|
||||
using Content.Shared.Construction.Prototypes;
|
||||
using Content.Shared.Construction.Steps;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Tool;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Construction.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public partial class ConstructionComponent : Component, IExamine, IInteractUsing
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override string Name => "Construction";
|
||||
|
||||
private bool _handling = false;
|
||||
|
||||
private TaskCompletionSource<object>? _handlingTask = null;
|
||||
[DataField("graph")]
|
||||
private string _graphIdentifier = string.Empty;
|
||||
[DataField("node")]
|
||||
private string _startingNodeIdentifier = string.Empty;
|
||||
[DataField("defaultTarget")]
|
||||
private string _startingTargetNodeIdentifier = string.Empty;
|
||||
|
||||
[ViewVariables]
|
||||
private HashSet<string> _containers = new();
|
||||
[ViewVariables]
|
||||
private List<List<ConstructionGraphStep>>? _edgeNestedStepProgress = null;
|
||||
|
||||
private ConstructionGraphNode? _target = null;
|
||||
|
||||
[ViewVariables]
|
||||
public ConstructionGraphPrototype? GraphPrototype { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public ConstructionGraphNode? Node { get; private set; } = null;
|
||||
|
||||
[ViewVariables]
|
||||
public ConstructionGraphEdge? Edge { get; private set; } = null;
|
||||
|
||||
public IReadOnlyCollection<string> Containers => _containers;
|
||||
|
||||
[ViewVariables]
|
||||
int IInteractUsing.Priority => 2;
|
||||
|
||||
[ViewVariables]
|
||||
public ConstructionGraphNode? Target
|
||||
{
|
||||
get => _target;
|
||||
set
|
||||
{
|
||||
ClearTarget();
|
||||
_target = value;
|
||||
UpdateTarget();
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public ConstructionGraphEdge? TargetNextEdge { get; private set; } = null;
|
||||
|
||||
[ViewVariables]
|
||||
public Queue<ConstructionGraphNode>? TargetPathfinding { get; private set; } = null;
|
||||
|
||||
[ViewVariables]
|
||||
public int EdgeStep { get; private set; } = 0;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("deconstructionTarget")]
|
||||
public string DeconstructionNodeIdentifier { get; private set; } = "start";
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to set a new pathfinding target.
|
||||
/// </summary>
|
||||
public void SetNewTarget(string node)
|
||||
{
|
||||
if (GraphPrototype != null && GraphPrototype.Nodes.TryGetValue(node, out var target))
|
||||
{
|
||||
Target = target;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearTarget()
|
||||
{
|
||||
_target = null;
|
||||
TargetNextEdge = null;
|
||||
TargetPathfinding = null;
|
||||
}
|
||||
|
||||
public void UpdateTarget()
|
||||
{
|
||||
// Can't pathfind without a target or no node.
|
||||
if (Target == null || Node == null || GraphPrototype == null) return;
|
||||
|
||||
// If we're at our target, stop pathfinding.
|
||||
if (Target == Node)
|
||||
{
|
||||
ClearTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we don't have the path, set it!
|
||||
if (TargetPathfinding == null)
|
||||
{
|
||||
var path = GraphPrototype.Path(Node.Name, Target.Name);
|
||||
|
||||
if (path == null)
|
||||
{
|
||||
ClearTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
TargetPathfinding = new Queue<ConstructionGraphNode>(path);
|
||||
}
|
||||
|
||||
// Dequeue the pathfinding queue if the next is the node we're at.
|
||||
if (TargetPathfinding.Peek() == Node)
|
||||
TargetPathfinding.Dequeue();
|
||||
|
||||
// If we went the wrong way, we stop pathfinding.
|
||||
if (Edge != null && TargetNextEdge != Edge)
|
||||
{
|
||||
ClearTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
// Let's set the next target edge.
|
||||
if (Edge == null && TargetNextEdge == null && TargetPathfinding != null)
|
||||
TargetNextEdge = Node.GetEdge(TargetPathfinding.Peek().Name);
|
||||
}
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (_handling)
|
||||
return true;
|
||||
|
||||
_handlingTask = new TaskCompletionSource<object>();
|
||||
_handling = true;
|
||||
bool result;
|
||||
|
||||
if (Edge == null)
|
||||
result = await HandleNode(eventArgs);
|
||||
else
|
||||
result = await HandleEdge(eventArgs);
|
||||
|
||||
_handling = false;
|
||||
_handlingTask.SetResult(null!);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task<bool> HandleNode(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
EdgeStep = 0;
|
||||
|
||||
if (Node == null || GraphPrototype == null) return false;
|
||||
|
||||
foreach (var edge in Node.Edges)
|
||||
{
|
||||
if(edge.Steps.Count == 0)
|
||||
throw new InvalidDataException($"Edge to \"{edge.Target}\" in node \"{Node.Name}\" of graph \"{GraphPrototype.ID}\" doesn't have any steps!");
|
||||
|
||||
var firstStep = edge.Steps[0];
|
||||
switch (firstStep)
|
||||
{
|
||||
case MaterialConstructionGraphStep _:
|
||||
case ToolConstructionGraphStep _:
|
||||
case ArbitraryInsertConstructionGraphStep _:
|
||||
if (await HandleStep(eventArgs, edge, firstStep))
|
||||
{
|
||||
if(edge.Steps.Count > 1)
|
||||
Edge = edge;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case NestedConstructionGraphStep nestedStep:
|
||||
throw new IndexOutOfRangeException($"Nested construction step not supported as the first step in an edge! Graph: {GraphPrototype.ID} Node: {Node.Name} Edge: {edge.Target}");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private async Task<bool> HandleStep(InteractUsingEventArgs eventArgs, ConstructionGraphEdge? edge = null, ConstructionGraphStep? step = null, bool nested = false)
|
||||
{
|
||||
edge ??= Edge;
|
||||
step ??= edge?.Steps[EdgeStep];
|
||||
|
||||
if (edge == null || step == null)
|
||||
return false;
|
||||
|
||||
foreach (var condition in edge.Conditions)
|
||||
{
|
||||
if (!await condition.Condition(Owner)) return false;
|
||||
}
|
||||
|
||||
var handled = false;
|
||||
|
||||
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
|
||||
|
||||
var doAfterArgs = new DoAfterEventArgs(eventArgs.User, step.DoAfter, default, eventArgs.Target)
|
||||
{
|
||||
BreakOnDamage = false,
|
||||
BreakOnStun = true,
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
NeedHand = true,
|
||||
};
|
||||
|
||||
switch (step)
|
||||
{
|
||||
case ToolConstructionGraphStep toolStep:
|
||||
// Gotta take welder fuel into consideration.
|
||||
if (toolStep.Tool == ToolQuality.Welding)
|
||||
{
|
||||
if (eventArgs.Using.TryGetComponent(out WelderComponent? welder) &&
|
||||
await welder.UseTool(eventArgs.User, Owner, step.DoAfter, toolStep.Tool, toolStep.Fuel))
|
||||
{
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (eventArgs.Using.TryGetComponent(out ToolComponent? tool) &&
|
||||
await tool.UseTool(eventArgs.User, Owner, step.DoAfter, toolStep.Tool))
|
||||
{
|
||||
handled = true;
|
||||
}
|
||||
break;
|
||||
|
||||
// To prevent too much code duplication.
|
||||
case EntityInsertConstructionGraphStep insertStep:
|
||||
var valid = false;
|
||||
var entityUsing = eventArgs.Using;
|
||||
|
||||
switch (insertStep)
|
||||
{
|
||||
case ArbitraryInsertConstructionGraphStep arbitraryStep:
|
||||
if (arbitraryStep.EntityValid(eventArgs.Using)
|
||||
&& await doAfterSystem.DoAfter(doAfterArgs) == DoAfterStatus.Finished)
|
||||
{
|
||||
valid = true;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case MaterialConstructionGraphStep materialStep:
|
||||
if (materialStep.EntityValid(eventArgs.Using, out var stack)
|
||||
&& await doAfterSystem.DoAfter(doAfterArgs) == DoAfterStatus.Finished)
|
||||
{
|
||||
var splitStack = new StackSplitEvent() {Amount = materialStep.Amount, SpawnPosition = eventArgs.User.Transform.Coordinates};
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(stack.Owner.Uid, splitStack);
|
||||
|
||||
if (splitStack.Result != null)
|
||||
{
|
||||
entityUsing = splitStack.Result;
|
||||
valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!valid || entityUsing == null) break;
|
||||
|
||||
if(string.IsNullOrEmpty(insertStep.Store))
|
||||
{
|
||||
entityUsing.Delete();
|
||||
}
|
||||
else
|
||||
{
|
||||
_containers.Add(insertStep.Store);
|
||||
var container = Owner.EnsureContainer<Container>(insertStep.Store);
|
||||
container.Insert(entityUsing);
|
||||
}
|
||||
|
||||
handled = true;
|
||||
|
||||
break;
|
||||
|
||||
case NestedConstructionGraphStep nestedStep:
|
||||
if(_edgeNestedStepProgress == null)
|
||||
_edgeNestedStepProgress = new List<List<ConstructionGraphStep>>(nestedStep.Steps);
|
||||
|
||||
foreach (var list in _edgeNestedStepProgress.ToArray())
|
||||
{
|
||||
if (list.Count == 0)
|
||||
{
|
||||
_edgeNestedStepProgress.Remove(list);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!await HandleStep(eventArgs, edge, list[0], true)) continue;
|
||||
|
||||
list.RemoveAt(0);
|
||||
|
||||
// We check again...
|
||||
if (list.Count == 0)
|
||||
_edgeNestedStepProgress.Remove(list);
|
||||
}
|
||||
|
||||
if (_edgeNestedStepProgress.Count == 0)
|
||||
handled = true;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled)
|
||||
{
|
||||
foreach (var completed in step.Completed)
|
||||
{
|
||||
await completed.PerformAction(Owner, eventArgs.User);
|
||||
|
||||
if (Owner.Deleted)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (nested && handled) return true;
|
||||
if (!handled) return false;
|
||||
|
||||
EdgeStep++;
|
||||
|
||||
if (edge.Steps.Count == EdgeStep)
|
||||
{
|
||||
await HandleCompletion(edge, eventArgs.User);
|
||||
}
|
||||
|
||||
UpdateTarget();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> HandleCompletion(ConstructionGraphEdge edge, IEntity user)
|
||||
{
|
||||
if (edge.Steps.Count != EdgeStep || GraphPrototype == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Edge = edge;
|
||||
|
||||
UpdateTarget();
|
||||
|
||||
TargetNextEdge = null;
|
||||
Edge = null;
|
||||
Node = GraphPrototype.Nodes[edge.Target];
|
||||
|
||||
foreach (var completed in edge.Completed)
|
||||
{
|
||||
await completed.PerformAction(Owner, user);
|
||||
if (Owner.Deleted) return true;
|
||||
}
|
||||
|
||||
// Perform node actions!
|
||||
foreach (var action in Node.Actions)
|
||||
{
|
||||
await action.PerformAction(Owner, user);
|
||||
|
||||
if (Owner.Deleted)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Target == Node)
|
||||
ClearTarget();
|
||||
|
||||
await HandleEntityChange(Node, user);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ResetEdge()
|
||||
{
|
||||
_edgeNestedStepProgress = null;
|
||||
TargetNextEdge = null;
|
||||
Edge = null;
|
||||
EdgeStep = 0;
|
||||
|
||||
UpdateTarget();
|
||||
}
|
||||
|
||||
private async Task<bool> HandleEdge(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (Edge == null || EdgeStep >= Edge.Steps.Count) return false;
|
||||
|
||||
return await HandleStep(eventArgs, Edge, Edge.Steps[EdgeStep]);
|
||||
}
|
||||
|
||||
private async Task<bool> HandleEntityChange(ConstructionGraphNode node, IEntity? user = null)
|
||||
{
|
||||
if (node.Entity == Owner.Prototype?.ID || string.IsNullOrEmpty(node.Entity)
|
||||
|| GraphPrototype == null) return false;
|
||||
|
||||
var entity = Owner.EntityManager.SpawnEntity(node.Entity, Owner.Transform.Coordinates);
|
||||
|
||||
entity.Transform.LocalRotation = Owner.Transform.LocalRotation;
|
||||
|
||||
if (entity.TryGetComponent(out ConstructionComponent? construction))
|
||||
{
|
||||
if(construction.GraphPrototype != GraphPrototype)
|
||||
throw new Exception($"New entity {node.Entity}'s graph {construction.GraphPrototype?.ID ?? null} isn't the same as our graph {GraphPrototype.ID} on node {node.Name}!");
|
||||
|
||||
construction.Node = node;
|
||||
construction.Target = Target;
|
||||
construction._containers = new HashSet<string>(_containers);
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ContainerManagerComponent? containerComp))
|
||||
{
|
||||
foreach (var container in _containers)
|
||||
{
|
||||
var otherContainer = entity.EnsureContainer<Container>(container);
|
||||
var ourContainer = containerComp.GetContainer(container);
|
||||
|
||||
foreach (var ent in ourContainer.ContainedEntities.ToArray())
|
||||
{
|
||||
ourContainer.ForceRemove(ent);
|
||||
otherContainer.Insert(ent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out IPhysBody? physics) &&
|
||||
entity.TryGetComponent(out IPhysBody? otherPhysics))
|
||||
{
|
||||
otherPhysics.BodyType = physics.BodyType;
|
||||
}
|
||||
|
||||
Owner.Delete();
|
||||
|
||||
foreach (var action in node.Actions)
|
||||
{
|
||||
await action.PerformAction(entity, user);
|
||||
|
||||
if (entity.Deleted)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool AddContainer(string id)
|
||||
{
|
||||
return _containers.Add(id);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (string.IsNullOrEmpty(_graphIdentifier))
|
||||
{
|
||||
Logger.Warning($"Prototype {Owner.Prototype?.ID}'s construction component didn't have a graph identifier!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_prototypeManager.TryIndex(_graphIdentifier, out ConstructionGraphPrototype? graph))
|
||||
{
|
||||
GraphPrototype = graph;
|
||||
|
||||
if (GraphPrototype.Nodes.TryGetValue(_startingNodeIdentifier, out var node))
|
||||
{
|
||||
Node = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error($"Couldn't find node {_startingNodeIdentifier} in graph {_graphIdentifier} in construction component!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error($"Couldn't find prototype {_graphIdentifier} in construction component!");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_startingTargetNodeIdentifier))
|
||||
SetNewTarget(_startingTargetNodeIdentifier);
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (Node == null) return;
|
||||
|
||||
foreach (var action in Node.Actions)
|
||||
{
|
||||
action.PerformAction(Owner, null);
|
||||
|
||||
if (Owner.Deleted)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ChangeNode(string node)
|
||||
{
|
||||
if (GraphPrototype == null) return;
|
||||
|
||||
var graphNode = GraphPrototype.Nodes[node];
|
||||
|
||||
if (_handling && _handlingTask?.Task != null)
|
||||
await _handlingTask.Task;
|
||||
|
||||
Edge = null;
|
||||
Node = graphNode;
|
||||
|
||||
foreach (var action in Node.Actions)
|
||||
{
|
||||
await action.PerformAction(Owner, null);
|
||||
if (Owner.Deleted)
|
||||
return;
|
||||
}
|
||||
|
||||
await HandleEntityChange(graphNode);
|
||||
}
|
||||
|
||||
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
if(Target != null)
|
||||
message.AddMarkup(Loc.GetString("To create {0}...\n", Target.Name));
|
||||
|
||||
if (Edge == null && TargetNextEdge != null)
|
||||
{
|
||||
var preventStepExamine = false;
|
||||
|
||||
foreach (var condition in TargetNextEdge.Conditions)
|
||||
{
|
||||
preventStepExamine |= condition.DoExamine(Owner, message, inDetailsRange);
|
||||
}
|
||||
|
||||
if(!preventStepExamine)
|
||||
TargetNextEdge.Steps[0].DoExamine(message, inDetailsRange);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Edge != null)
|
||||
{
|
||||
var preventStepExamine = false;
|
||||
|
||||
foreach (var condition in Edge.Conditions)
|
||||
{
|
||||
preventStepExamine |= condition.DoExamine(Owner, message, inDetailsRange);
|
||||
}
|
||||
|
||||
if (preventStepExamine) return;
|
||||
}
|
||||
|
||||
if (_edgeNestedStepProgress == null)
|
||||
{
|
||||
if(EdgeStep < Edge?.Steps.Count)
|
||||
Edge.Steps[EdgeStep].DoExamine(message, inDetailsRange);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var list in _edgeNestedStepProgress)
|
||||
{
|
||||
if(list.Count == 0) continue;
|
||||
|
||||
list[0].DoExamine(message, inDetailsRange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Content.Server/Construction/Components/IRefreshParts.cs
Normal file
11
Content.Server/Construction/Components/IRefreshParts.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Analyzers;
|
||||
|
||||
namespace Content.Server.Construction.Components
|
||||
{
|
||||
[RequiresExplicitImplementation]
|
||||
public interface IRefreshParts
|
||||
{
|
||||
void RefreshParts(IEnumerable<MachinePartComponent> parts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Stacks;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Construction.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MachineBoardComponent : Component, IExamine
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override string Name => "MachineBoard";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("requirements")]
|
||||
public readonly Dictionary<MachinePart, int> Requirements = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("materialRequirements")]
|
||||
public readonly Dictionary<string, int> MaterialIdRequirements = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("tagRequirements")]
|
||||
public readonly Dictionary<string, GenericPartInfo> TagRequirements = new();
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("componentRequirements")]
|
||||
public readonly Dictionary<string, GenericPartInfo> ComponentRequirements = new();
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("prototype")]
|
||||
public string? Prototype { get; private set; }
|
||||
|
||||
public IEnumerable<KeyValuePair<StackPrototype, int>> MaterialRequirements
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var (materialId, amount) in MaterialIdRequirements)
|
||||
{
|
||||
var material = _prototypeManager.Index<StackPrototype>(materialId);
|
||||
yield return new KeyValuePair<StackPrototype, int>(material, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Examine(FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
message.AddMarkup(Loc.GetString("Requires:\n"));
|
||||
foreach (var (part, amount) in Requirements)
|
||||
{
|
||||
message.AddMarkup(Loc.GetString("[color=yellow]{0}x[/color] [color=green]{1}[/color]\n", amount, Loc.GetString(part.ToString())));
|
||||
}
|
||||
|
||||
foreach (var (material, amount) in MaterialRequirements)
|
||||
{
|
||||
message.AddMarkup(Loc.GetString("[color=yellow]{0}x[/color] [color=green]{1}[/color]\n", amount, Loc.GetString(material.Name)));
|
||||
}
|
||||
|
||||
foreach (var (_, info) in ComponentRequirements)
|
||||
{
|
||||
message.AddMarkup(Loc.GetString("[color=yellow]{0}x[/color] [color=green]{1}[/color]\n", info.Amount, Loc.GetString(info.ExamineName)));
|
||||
}
|
||||
|
||||
foreach (var (_, info) in TagRequirements)
|
||||
{
|
||||
message.AddMarkup(Loc.GetString("[color=yellow]{0}x[/color] [color=green]{1}[/color]\n", info.Amount, Loc.GetString(info.ExamineName)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public struct GenericPartInfo
|
||||
{
|
||||
[DataField("Amount")]
|
||||
public int Amount;
|
||||
[DataField("ExamineName")]
|
||||
public string ExamineName;
|
||||
[DataField("DefaultPrototype")]
|
||||
public string DefaultPrototype;
|
||||
}
|
||||
}
|
||||
130
Content.Server/Construction/Components/MachineComponent.cs
Normal file
130
Content.Server/Construction/Components/MachineComponent.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Stack;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Construction.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MachineComponent : Component, IMapInit
|
||||
{
|
||||
public override string Name => "Machine";
|
||||
|
||||
[DataField("board")]
|
||||
public string? BoardPrototype { get; private set; }
|
||||
|
||||
private Container _boardContainer = default!;
|
||||
private Container _partContainer = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_boardContainer = Owner.EnsureContainer<Container>(MachineFrameComponent.BoardContainer);
|
||||
_partContainer = Owner.EnsureContainer<Container>(MachineFrameComponent.PartContainer);
|
||||
}
|
||||
|
||||
public IEnumerable<MachinePartComponent> GetAllParts()
|
||||
{
|
||||
foreach (var entity in _partContainer.ContainedEntities)
|
||||
{
|
||||
if (entity.TryGetComponent<MachinePartComponent>(out var machinePart))
|
||||
yield return machinePart;
|
||||
}
|
||||
}
|
||||
|
||||
public void RefreshParts()
|
||||
{
|
||||
foreach (var refreshable in Owner.GetAllComponents<IRefreshParts>())
|
||||
{
|
||||
refreshable.RefreshParts(GetAllParts());
|
||||
}
|
||||
}
|
||||
|
||||
public void CreateBoardAndStockParts()
|
||||
{
|
||||
// Entity might not be initialized yet.
|
||||
var boardContainer = Owner.EnsureContainer<Container>(MachineFrameComponent.BoardContainer, out var existedBoard);
|
||||
var partContainer = Owner.EnsureContainer<Container>(MachineFrameComponent.PartContainer, out var existedParts);
|
||||
|
||||
if (string.IsNullOrEmpty(BoardPrototype))
|
||||
return;
|
||||
|
||||
var entityManager = Owner.EntityManager;
|
||||
|
||||
if (existedBoard || existedParts)
|
||||
{
|
||||
// We're done here, let's suppose all containers are correct just so we don't screw SaveLoadSave.
|
||||
if (boardContainer.ContainedEntities.Count > 0)
|
||||
return;
|
||||
}
|
||||
|
||||
var board = entityManager.SpawnEntity(BoardPrototype, Owner.Transform.Coordinates);
|
||||
|
||||
if (!_boardContainer.Insert(board))
|
||||
{
|
||||
throw new Exception($"Couldn't insert board with prototype {BoardPrototype} to machine with prototype {Owner.Prototype?.ID ?? "N/A"}!");
|
||||
}
|
||||
|
||||
if (!board.TryGetComponent<MachineBoardComponent>(out var machineBoard))
|
||||
{
|
||||
throw new Exception($"Entity with prototype {BoardPrototype} doesn't have a {nameof(MachineBoardComponent)}!");
|
||||
}
|
||||
|
||||
foreach (var (part, amount) in machineBoard.Requirements)
|
||||
{
|
||||
for (var i = 0; i < amount; i++)
|
||||
{
|
||||
var p = entityManager.SpawnEntity(MachinePartComponent.Prototypes[part], Owner.Transform.Coordinates);
|
||||
|
||||
if (!partContainer.Insert(p))
|
||||
throw new Exception($"Couldn't insert machine part of type {part} to machine with prototype {Owner.Prototype?.ID ?? "N/A"}!");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (stackType, amount) in machineBoard.MaterialRequirements)
|
||||
{
|
||||
var stackSpawn = new StackTypeSpawnEvent()
|
||||
{Amount = amount, StackType = stackType, SpawnPosition = Owner.Transform.Coordinates};
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, stackSpawn);
|
||||
|
||||
var s = stackSpawn.Result;
|
||||
|
||||
if (s == null)
|
||||
throw new Exception($"Couldn't spawn stack of type {stackType}!");
|
||||
|
||||
if (!partContainer.Insert(s))
|
||||
throw new Exception($"Couldn't insert machine material of type {stackType} to machine with prototype {Owner.Prototype?.ID ?? "N/A"}");
|
||||
}
|
||||
|
||||
foreach (var (compName, info) in machineBoard.ComponentRequirements)
|
||||
{
|
||||
for (var i = 0; i < info.Amount; i++)
|
||||
{
|
||||
var c = entityManager.SpawnEntity(info.DefaultPrototype, Owner.Transform.Coordinates);
|
||||
|
||||
if(!partContainer.Insert(c))
|
||||
throw new Exception($"Couldn't insert machine component part with default prototype '{compName}' to machine with prototype {Owner.Prototype?.ID ?? "N/A"}");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var (tagName, info) in machineBoard.TagRequirements)
|
||||
{
|
||||
for (var i = 0; i < info.Amount; i++)
|
||||
{
|
||||
var c = entityManager.SpawnEntity(info.DefaultPrototype, Owner.Transform.Coordinates);
|
||||
|
||||
if(!partContainer.Insert(c))
|
||||
throw new Exception($"Couldn't insert machine component part with default prototype '{tagName}' to machine with prototype {Owner.Prototype?.ID ?? "N/A"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MapInit()
|
||||
{
|
||||
CreateBoardAndStockParts();
|
||||
}
|
||||
}
|
||||
}
|
||||
362
Content.Server/Construction/Components/MachineFrameComponent.cs
Normal file
362
Content.Server/Construction/Components/MachineFrameComponent.cs
Normal file
@@ -0,0 +1,362 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Stack;
|
||||
using Content.Shared.Construction;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Construction.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MachineFrameComponent : Component, IInteractUsing
|
||||
{
|
||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||
|
||||
public const string PartContainer = "machine_parts";
|
||||
public const string BoardContainer = "machine_board";
|
||||
|
||||
public override string Name => "MachineFrame";
|
||||
|
||||
[ViewVariables]
|
||||
public bool IsComplete
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!HasBoard || Requirements == null || MaterialRequirements == null)
|
||||
return false;
|
||||
|
||||
foreach (var (part, amount) in Requirements)
|
||||
{
|
||||
if (_progress[part] < amount)
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var (type, amount) in MaterialRequirements)
|
||||
{
|
||||
if (_materialProgress[type] < amount)
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var (compName, info) in ComponentRequirements)
|
||||
{
|
||||
if (_componentProgress[compName] < info.Amount)
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var (tagName, info) in TagRequirements)
|
||||
{
|
||||
if (_tagProgress[tagName] < info.Amount)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public bool HasBoard => _boardContainer?.ContainedEntities.Count != 0;
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<MachinePart, int> _progress = new();
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<string, int> _materialProgress = new();
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<string, int> _componentProgress = new();
|
||||
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<string, int> _tagProgress = new();
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<MachinePart, int> _requirements = new();
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<string, int> _materialRequirements = new();
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<string, GenericPartInfo> _componentRequirements = new();
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<string, GenericPartInfo> _tagRequirements = new();
|
||||
|
||||
[ViewVariables]
|
||||
private Container _boardContainer = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private Container _partContainer = default!;
|
||||
|
||||
public IReadOnlyDictionary<MachinePart, int> Progress => _progress;
|
||||
|
||||
public IReadOnlyDictionary<string, int> MaterialProgress => _materialProgress;
|
||||
|
||||
public IReadOnlyDictionary<string, int> ComponentProgress => _componentProgress;
|
||||
|
||||
public IReadOnlyDictionary<string, int> TagProgress => _tagProgress;
|
||||
|
||||
public IReadOnlyDictionary<MachinePart, int> Requirements => _requirements;
|
||||
|
||||
public IReadOnlyDictionary<string, int> MaterialRequirements => _materialRequirements;
|
||||
|
||||
public IReadOnlyDictionary<string, GenericPartInfo> ComponentRequirements => _componentRequirements;
|
||||
|
||||
public IReadOnlyDictionary<string, GenericPartInfo> TagRequirements => _tagRequirements;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_boardContainer = ContainerHelpers.EnsureContainer<Container>(Owner, BoardContainer);
|
||||
_partContainer = ContainerHelpers.EnsureContainer<Container>(Owner, PartContainer);
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
RegenerateProgress();
|
||||
|
||||
if (Owner.TryGetComponent<ConstructionComponent>(out var construction))
|
||||
{
|
||||
// Attempt to set pathfinding to the machine node...
|
||||
construction.SetNewTarget("machine");
|
||||
}
|
||||
}
|
||||
|
||||
private void ResetProgressAndRequirements(MachineBoardComponent machineBoard)
|
||||
{
|
||||
_requirements = machineBoard.Requirements;
|
||||
_materialRequirements = machineBoard.MaterialIdRequirements;
|
||||
_componentRequirements = machineBoard.ComponentRequirements;
|
||||
_tagRequirements = machineBoard.TagRequirements;
|
||||
|
||||
_progress.Clear();
|
||||
_materialProgress.Clear();
|
||||
_componentProgress.Clear();
|
||||
_tagProgress.Clear();
|
||||
|
||||
foreach (var (machinePart, _) in Requirements)
|
||||
{
|
||||
_progress[machinePart] = 0;
|
||||
}
|
||||
|
||||
foreach (var (stackType, _) in MaterialRequirements)
|
||||
{
|
||||
_materialProgress[stackType] = 0;
|
||||
}
|
||||
|
||||
foreach (var (compName, _) in ComponentRequirements)
|
||||
{
|
||||
_componentProgress[compName] = 0;
|
||||
}
|
||||
|
||||
foreach (var (compName, _) in TagRequirements)
|
||||
{
|
||||
_tagProgress[compName] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void RegenerateProgress()
|
||||
{
|
||||
AppearanceComponent? appearance;
|
||||
|
||||
if (!HasBoard)
|
||||
{
|
||||
if (Owner.TryGetComponent(out appearance))
|
||||
{
|
||||
appearance.SetData(MachineFrameVisuals.State, 1);
|
||||
}
|
||||
|
||||
_requirements.Clear();
|
||||
_materialRequirements.Clear();
|
||||
_componentRequirements.Clear();
|
||||
_tagRequirements.Clear();
|
||||
_progress.Clear();
|
||||
_materialProgress.Clear();
|
||||
_componentProgress.Clear();
|
||||
_tagProgress.Clear();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var board = _boardContainer.ContainedEntities[0];
|
||||
|
||||
if (!board.TryGetComponent<MachineBoardComponent>(out var machineBoard))
|
||||
return;
|
||||
|
||||
if (Owner.TryGetComponent(out appearance))
|
||||
{
|
||||
appearance.SetData(MachineFrameVisuals.State, 2);
|
||||
}
|
||||
|
||||
ResetProgressAndRequirements(machineBoard);
|
||||
|
||||
foreach (var part in _partContainer.ContainedEntities)
|
||||
{
|
||||
if (part.TryGetComponent<MachinePartComponent>(out var machinePart))
|
||||
{
|
||||
// Check this is part of the requirements...
|
||||
if (!Requirements.ContainsKey(machinePart.PartType))
|
||||
continue;
|
||||
|
||||
if (!_progress.ContainsKey(machinePart.PartType))
|
||||
_progress[machinePart.PartType] = 1;
|
||||
else
|
||||
_progress[machinePart.PartType]++;
|
||||
}
|
||||
|
||||
if (part.TryGetComponent<StackComponent>(out var stack))
|
||||
{
|
||||
var type = stack.StackTypeId;
|
||||
// Check this is part of the requirements...
|
||||
if (!MaterialRequirements.ContainsKey(type))
|
||||
continue;
|
||||
|
||||
if (!_materialProgress.ContainsKey(type))
|
||||
_materialProgress[type] = 1;
|
||||
else
|
||||
_materialProgress[type]++;
|
||||
}
|
||||
|
||||
// I have many regrets.
|
||||
foreach (var (compName, _) in ComponentRequirements)
|
||||
{
|
||||
var registration = _componentFactory.GetRegistration(compName);
|
||||
|
||||
if (!part.HasComponent(registration.Type))
|
||||
continue;
|
||||
|
||||
if (!_componentProgress.ContainsKey(compName))
|
||||
_componentProgress[compName] = 1;
|
||||
else
|
||||
_componentProgress[compName]++;
|
||||
}
|
||||
|
||||
// I have MANY regrets.
|
||||
foreach (var (tagName, _) in TagRequirements)
|
||||
{
|
||||
if (!part.HasTag(tagName))
|
||||
continue;
|
||||
|
||||
if (!_tagProgress.ContainsKey(tagName))
|
||||
_tagProgress[tagName] = 1;
|
||||
else
|
||||
_tagProgress[tagName]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (!HasBoard && eventArgs.Using.TryGetComponent<MachineBoardComponent>(out var machineBoard))
|
||||
{
|
||||
if (eventArgs.Using.TryRemoveFromContainer())
|
||||
{
|
||||
// Valid board!
|
||||
_boardContainer.Insert(eventArgs.Using);
|
||||
|
||||
// Setup requirements and progress...
|
||||
ResetProgressAndRequirements(machineBoard);
|
||||
|
||||
if (Owner.TryGetComponent<AppearanceComponent>(out var appearance))
|
||||
{
|
||||
appearance.SetData(MachineFrameVisuals.State, 2);
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ConstructionComponent? construction))
|
||||
{
|
||||
// So prying the components off works correctly.
|
||||
construction.ResetEdge();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (HasBoard)
|
||||
{
|
||||
if (eventArgs.Using.TryGetComponent<MachinePartComponent>(out var machinePart))
|
||||
{
|
||||
if (!Requirements.ContainsKey(machinePart.PartType))
|
||||
return false;
|
||||
|
||||
if (_progress[machinePart.PartType] != Requirements[machinePart.PartType]
|
||||
&& eventArgs.Using.TryRemoveFromContainer() && _partContainer.Insert(eventArgs.Using))
|
||||
{
|
||||
_progress[machinePart.PartType]++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (eventArgs.Using.TryGetComponent<StackComponent>(out var stack))
|
||||
{
|
||||
var type = stack.StackTypeId;
|
||||
if (!MaterialRequirements.ContainsKey(type))
|
||||
return false;
|
||||
|
||||
if (_materialProgress[type] == MaterialRequirements[type])
|
||||
return false;
|
||||
|
||||
var needed = MaterialRequirements[type] - _materialProgress[type];
|
||||
var count = stack.Count;
|
||||
|
||||
if (count < needed)
|
||||
{
|
||||
if(!_partContainer.Insert(stack.Owner))
|
||||
return false;
|
||||
|
||||
_materialProgress[type] += count;
|
||||
return true;
|
||||
}
|
||||
|
||||
var splitStack = new StackSplitEvent()
|
||||
{Amount = needed, SpawnPosition = Owner.Transform.Coordinates};
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(stack.Owner.Uid, splitStack);
|
||||
|
||||
if (splitStack.Result == null)
|
||||
return false;
|
||||
|
||||
if(!_partContainer.Insert(splitStack.Result))
|
||||
return false;
|
||||
|
||||
_materialProgress[type] += needed;
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var (compName, info) in ComponentRequirements)
|
||||
{
|
||||
if (_componentProgress[compName] >= info.Amount)
|
||||
continue;
|
||||
|
||||
var registration = _componentFactory.GetRegistration(compName);
|
||||
|
||||
if (!eventArgs.Using.HasComponent(registration.Type))
|
||||
continue;
|
||||
|
||||
if (!eventArgs.Using.TryRemoveFromContainer() || !_partContainer.Insert(eventArgs.Using)) continue;
|
||||
_componentProgress[compName]++;
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var (tagName, info) in TagRequirements)
|
||||
{
|
||||
if (_tagProgress[tagName] >= info.Amount)
|
||||
continue;
|
||||
|
||||
if (!eventArgs.Using.HasTag(tagName))
|
||||
continue;
|
||||
|
||||
if (!eventArgs.Using.TryRemoveFromContainer() || !_partContainer.Insert(eventArgs.Using)) continue;
|
||||
_tagProgress[tagName]++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Examine;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Construction.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MachinePartComponent : Component, IExamine
|
||||
{
|
||||
// I'm so sorry for hard-coding this. But trust me, it should make things less painful.
|
||||
public static IReadOnlyDictionary<MachinePart, string> Prototypes { get; } = new Dictionary<MachinePart, string>()
|
||||
{
|
||||
{MachinePart.Capacitor, "CapacitorStockPart"},
|
||||
{MachinePart.ScanningModule, "ScanningModuleStockPart"},
|
||||
{MachinePart.Manipulator, "MicroManipulatorStockPart"},
|
||||
{MachinePart.Laser, "MicroLaserStockPart"},
|
||||
{MachinePart.MatterBin, "MatterBinStockPart"},
|
||||
{MachinePart.Ansible, "AnsibleSubspaceStockPart"},
|
||||
{MachinePart.Filter, "FilterSubspaceStockPart"},
|
||||
{MachinePart.Amplifier, "AmplifierSubspaceStockPart"},
|
||||
{MachinePart.Treatment, "TreatmentSubspaceStockPart"},
|
||||
{MachinePart.Analyzer, "AnalyzerSubspaceStockPart"},
|
||||
{MachinePart.Crystal, "CrystalSubspaceStockPart"},
|
||||
{MachinePart.Transmitter, "TransmitterSubspaceStockPart"}
|
||||
};
|
||||
|
||||
public override string Name => "MachinePart";
|
||||
|
||||
[ViewVariables] [DataField("part")] public MachinePart PartType { get; private set; } = MachinePart.Capacitor;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("rating")]
|
||||
public int Rating { get; private set; } = 1;
|
||||
|
||||
public void Examine(FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
message.AddMarkup(Loc.GetString("[color=white]Rating:[/color] [color=cyan]{0}[/color]\n", Rating));
|
||||
message.AddMarkup(Loc.GetString("[color=white]Type:[/color] [color=cyan]{0}[/color]\n", PartType));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Tools.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Stacks;
|
||||
using Content.Shared.Tool;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Construction.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for something that can be refined by welder.
|
||||
/// For example, glass shard can be refined to glass sheet.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class WelderRefinableComponent : Component, IInteractUsing
|
||||
{
|
||||
[ViewVariables]
|
||||
[DataField("refineResult")]
|
||||
private HashSet<string>? _refineResult = new() { };
|
||||
[ViewVariables]
|
||||
[DataField("refineTime")]
|
||||
private float _refineTime = 2f;
|
||||
|
||||
private bool _beingWelded;
|
||||
|
||||
public override string Name => "WelderRefinable";
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
// check if object is welder
|
||||
if (!eventArgs.Using.TryGetComponent(out ToolComponent? tool))
|
||||
return false;
|
||||
|
||||
// check if someone is already welding object
|
||||
if (_beingWelded)
|
||||
return false;
|
||||
_beingWelded = true;
|
||||
|
||||
if (!await tool.UseTool(eventArgs.User, Owner, _refineTime, ToolQuality.Welding))
|
||||
{
|
||||
// failed to veld - abort refine
|
||||
_beingWelded = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// get last owner coordinates and delete it
|
||||
var resultPosition = Owner.Transform.Coordinates;
|
||||
Owner.Delete();
|
||||
|
||||
// spawn each result afrer refine
|
||||
foreach (var result in _refineResult!)
|
||||
{
|
||||
var droppedEnt = Owner.EntityManager.SpawnEntity(result, resultPosition);
|
||||
|
||||
// TODO: If something has a stack... Just use a prototype with a single thing in the stack.
|
||||
// This is not a good way to do it.
|
||||
if (droppedEnt.HasComponent<StackComponent>())
|
||||
Owner.EntityManager.EventBus.RaiseLocalEvent(droppedEnt.Uid, new StackChangeCountEvent(1), false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user