Upgradeable machines. (#2675)
* Start work on upgradeable machines. * Upgradeable machines work * Component requirements for upgradeable machines * Better container handling * Remember to not push submodule updates in your PRs, kids! * Refresh parts after building a machine. * NetSync false * Address some reviews, fix some bugs * Nullable stackhelpers dependencies * Use container helper method to delete all entities in containers * Nullable string in AddContainer * Better examine for machine frame and construction in general * Machine breakage * Nullable node * nullable GraphPrototype * Re-save saltern for autolathe parts * Fix SaveLoadSave
This commit is contained in:
committed by
GitHub
parent
ba2bdec13b
commit
c3341132c5
@@ -48,10 +48,10 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
private ConstructionGraphNode? _target = null;
|
||||
|
||||
[ViewVariables]
|
||||
public ConstructionGraphPrototype GraphPrototype { get; private set; } = null!;
|
||||
public ConstructionGraphPrototype? GraphPrototype { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public ConstructionGraphNode Node { get; private set; } = null!;
|
||||
public ConstructionGraphNode? Node { get; private set; } = null;
|
||||
|
||||
[ViewVariables]
|
||||
public ConstructionGraphEdge? Edge { get; private set; } = null;
|
||||
@@ -88,6 +88,17 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
serializer.DataField(ref _startingNodeIdentifier, "node", string.Empty);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
@@ -97,8 +108,8 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
|
||||
public void UpdateTarget()
|
||||
{
|
||||
// Can't pathfind without a target.
|
||||
if (Target == null) return;
|
||||
// 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)
|
||||
@@ -133,7 +144,7 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
}
|
||||
|
||||
// Let's set the next target edge.
|
||||
if (Edge == null && TargetNextEdge == null)
|
||||
if (Edge == null && TargetNextEdge == null && TargetPathfinding != null)
|
||||
TargetNextEdge = Node.GetEdge(TargetPathfinding.Peek().Name);
|
||||
}
|
||||
|
||||
@@ -161,6 +172,8 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
{
|
||||
EdgeStep = 0;
|
||||
|
||||
if (Node == null || GraphPrototype == null) return false;
|
||||
|
||||
foreach (var edge in Node.Edges)
|
||||
{
|
||||
if(edge.Steps.Count == 0)
|
||||
@@ -344,7 +357,7 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
|
||||
private async Task<bool> HandleCompletion(ConstructionGraphEdge edge, IEntity user)
|
||||
{
|
||||
if (edge.Steps.Count != EdgeStep)
|
||||
if (edge.Steps.Count != EdgeStep || GraphPrototype == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -380,6 +393,16 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
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;
|
||||
@@ -389,7 +412,8 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
|
||||
private async Task<bool> HandleEntityChange(ConstructionGraphNode node, IEntity? user = null)
|
||||
{
|
||||
if (node.Entity == Owner.Prototype?.ID || string.IsNullOrEmpty(node.Entity)) return false;
|
||||
if (node.Entity == Owner.Prototype?.ID || string.IsNullOrEmpty(node.Entity)
|
||||
|| GraphPrototype == null) return false;
|
||||
|
||||
var entity = Owner.EntityManager.SpawnEntity(node.Entity, Owner.Transform.Coordinates);
|
||||
|
||||
@@ -398,7 +422,7 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
if (entity.TryGetComponent(out ConstructionComponent? construction))
|
||||
{
|
||||
if(construction.GraphPrototype != GraphPrototype)
|
||||
throw new Exception($"New entity {node.Entity}'s graph {construction.GraphPrototype.ID} isn't the same as our graph {GraphPrototype.ID} on node {node.Name}!");
|
||||
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;
|
||||
@@ -461,14 +485,6 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
if (GraphPrototype.Nodes.TryGetValue(_startingNodeIdentifier, out var node))
|
||||
{
|
||||
Node = node;
|
||||
|
||||
foreach (var action in Node.Actions)
|
||||
{
|
||||
action.PerformAction(Owner, null);
|
||||
|
||||
if (Owner.Deleted)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -481,8 +497,25 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -508,21 +541,28 @@ namespace Content.Server.GameObjects.Components.Construction
|
||||
|
||||
if (Edge == null && TargetNextEdge != null)
|
||||
{
|
||||
var preventStepExamine = false;
|
||||
|
||||
foreach (var condition in TargetNextEdge.Conditions)
|
||||
{
|
||||
condition.DoExamine(Owner, message, inDetailsRange);
|
||||
preventStepExamine |= condition.DoExamine(Owner, message, inDetailsRange);
|
||||
}
|
||||
|
||||
TargetNextEdge.Steps[0].DoExamine(message, inDetailsRange);
|
||||
if(!preventStepExamine)
|
||||
TargetNextEdge.Steps[0].DoExamine(message, inDetailsRange);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Edge != null)
|
||||
{
|
||||
var preventStepExamine = false;
|
||||
|
||||
foreach (var condition in Edge.Conditions)
|
||||
{
|
||||
condition.DoExamine(Owner, message, inDetailsRange);
|
||||
preventStepExamine |= condition.DoExamine(Owner, message, inDetailsRange);
|
||||
}
|
||||
|
||||
if (preventStepExamine) return;
|
||||
}
|
||||
|
||||
if (_edgeNestedStepProgress == null)
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Construction;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Construction
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MachineBoardComponent : Component, IExamine
|
||||
{
|
||||
public override string Name => "MachineBoard";
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<MachinePart, int> _requirements;
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<StackType, int> _materialRequirements;
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<string, ComponentPartInfo> _componentRequirements;
|
||||
|
||||
/// <summary>
|
||||
/// So, what happens if you spawn a machine from the entity spawning menu?
|
||||
/// It should probably have all parts, including the component parts...
|
||||
/// This is where this fancy little dictionary comes in!
|
||||
/// This maps component name types to entity prototype IDs to be used as defaults.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
private Dictionary<string, string> _componentDefaults;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Prototype { get; private set; }
|
||||
public IReadOnlyDictionary<MachinePart, int> Requirements => _requirements;
|
||||
public IReadOnlyDictionary<StackType, int> MaterialRequirements => _materialRequirements;
|
||||
public IReadOnlyDictionary<string, ComponentPartInfo> ComponentRequirements => _componentRequirements;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(this, x => x.Prototype, "prototype", null);
|
||||
serializer.DataField(ref _requirements, "requirements", new Dictionary<MachinePart, int>());
|
||||
serializer.DataField(ref _materialRequirements, "materialRequirements", new Dictionary<StackType, int>());
|
||||
serializer.DataField(ref _componentRequirements, "componentRequirements", new Dictionary<string, ComponentPartInfo>());
|
||||
}
|
||||
|
||||
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.ToString())));
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public struct ComponentPartInfo
|
||||
{
|
||||
public int Amount;
|
||||
public string ExamineName;
|
||||
public string DefaultPrototype;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Construction;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Construction
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MachineComponent : Component, IMapInit
|
||||
{
|
||||
public override string Name => "Machine";
|
||||
|
||||
public string BoardPrototype { get; private set; }
|
||||
|
||||
private Container _boardContainer;
|
||||
private Container _partContainer;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(this, x => x.BoardPrototype, "board", null);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_boardContainer = ContainerManagerComponent.Ensure<Container>(MachineFrameComponent.BoardContainer, Owner);
|
||||
_partContainer = ContainerManagerComponent.Ensure<Container>(MachineFrameComponent.PartContainer, Owner);
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
CreateBoardAndStockParts();
|
||||
}
|
||||
|
||||
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 = ContainerManagerComponent.Ensure<Container>(MachineFrameComponent.BoardContainer, Owner, out var existedBoard);
|
||||
var partContainer = ContainerManagerComponent.Ensure<Container>(MachineFrameComponent.PartContainer, Owner, 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 s = StackHelpers.SpawnStack(stackType, amount, Owner.Transform.Coordinates);
|
||||
|
||||
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"}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void MapInit()
|
||||
{
|
||||
CreateBoardAndStockParts();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,301 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Construction;
|
||||
using Content.Server.GameObjects.Components.Stack;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Content.Shared.GameObjects.Components.Construction;
|
||||
using Content.Shared.GameObjects.Components.Power;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Construction
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MachineFrameComponent : Component, IInteractUsing
|
||||
{
|
||||
[Dependency] private 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;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public bool HasBoard => _boardContainer?.ContainedEntities.Count != 0;
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<MachinePart, int> _progress;
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<StackType, int> _materialProgress;
|
||||
|
||||
[ViewVariables]
|
||||
private Dictionary<string, int> _componentProgress;
|
||||
|
||||
[ViewVariables]
|
||||
private Container _boardContainer;
|
||||
|
||||
[ViewVariables]
|
||||
private Container _partContainer;
|
||||
|
||||
[ViewVariables]
|
||||
public IReadOnlyDictionary<MachinePart, int> Requirements { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public IReadOnlyDictionary<StackType, int> MaterialRequirements { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public IReadOnlyDictionary<string, ComponentPartInfo> ComponentRequirements { get; private set; }
|
||||
|
||||
public IReadOnlyDictionary<MachinePart, int> Progress => _progress;
|
||||
public IReadOnlyDictionary<StackType, int> MaterialProgress => _materialProgress;
|
||||
public IReadOnlyDictionary<string, int> ComponentProgress => _componentProgress;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_boardContainer = ContainerManagerComponent.Ensure<Container>(BoardContainer, Owner);
|
||||
_partContainer = ContainerManagerComponent.Ensure<Container>(PartContainer, Owner);
|
||||
}
|
||||
|
||||
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.MaterialRequirements;
|
||||
ComponentRequirements = machineBoard.ComponentRequirements;
|
||||
_progress = new Dictionary<MachinePart, int>();
|
||||
_materialProgress = new Dictionary<StackType, int>();
|
||||
_componentProgress = new Dictionary<string, int>();
|
||||
|
||||
foreach (var (machinePart, _) in Requirements)
|
||||
{
|
||||
_progress[machinePart] = 0;
|
||||
}
|
||||
|
||||
foreach (var (stackType, _) in MaterialRequirements)
|
||||
{
|
||||
_materialProgress[stackType] = 0;
|
||||
}
|
||||
|
||||
foreach (var (compName, _) in ComponentRequirements)
|
||||
{
|
||||
_componentProgress[compName] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void RegenerateProgress()
|
||||
{
|
||||
AppearanceComponent appearance;
|
||||
|
||||
if (!HasBoard)
|
||||
{
|
||||
if (Owner.TryGetComponent(out appearance))
|
||||
{
|
||||
appearance.SetData(MachineFrameVisuals.State, 1);
|
||||
}
|
||||
|
||||
Requirements = null;
|
||||
MaterialRequirements = null;
|
||||
ComponentRequirements = null;
|
||||
_progress = null;
|
||||
_materialProgress = null;
|
||||
_componentProgress = null;
|
||||
|
||||
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 = (StackType) stack.StackType;
|
||||
// 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, amount) in ComponentRequirements)
|
||||
{
|
||||
var registration = _componentFactory.GetRegistration(compName);
|
||||
|
||||
if (!part.HasComponent(registration.Type))
|
||||
continue;
|
||||
|
||||
if (!_componentProgress.ContainsKey(compName))
|
||||
_componentProgress[compName] = 1;
|
||||
else
|
||||
_componentProgress[compName]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> 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 = (StackType) stack.StackType;
|
||||
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 && stack.Split(count, Owner.Transform.Coordinates, out var newStack))
|
||||
{
|
||||
_materialProgress[type] += count;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!stack.Split(needed, Owner.Transform.Coordinates, out newStack))
|
||||
return false;
|
||||
|
||||
if(!_partContainer.Insert(newStack))
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Construction;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Construction
|
||||
{
|
||||
[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]
|
||||
public MachinePart PartType { get; private set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int Rating { get; private set; }
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(this, x => x.PartType, "part", MachinePart.Capacitor);
|
||||
serializer.DataField(this, x => x.Rating, "rating", 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user