Re-organize all projects (#4166)

This commit is contained in:
DrSmugleaf
2021-06-09 22:19:39 +02:00
committed by GitHub
parent 9f50e4061b
commit ff1a2d97ea
1773 changed files with 5258 additions and 5508 deletions

View File

@@ -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; }
}
}

View File

@@ -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."));
}
}
}
}
}

View 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);
}
}
}
}

View 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);
}
}

View File

@@ -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;
}
}

View 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();
}
}
}

View 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;
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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;
}
}
}