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
30
Content.Server/Construction/Completions/AddContainer.cs
Normal file
30
Content.Server/Construction/Completions/AddContainer.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Construction;
|
||||
using Content.Shared.Construction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Construction.Completions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class AddContainer : IGraphAction
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.Container, "container", null);
|
||||
}
|
||||
|
||||
public string? Container { get; private set; } = null;
|
||||
|
||||
public async Task PerformAction(IEntity entity, IEntity? user)
|
||||
{
|
||||
if (entity.Deleted || string.IsNullOrEmpty(Container))
|
||||
return;
|
||||
|
||||
var construction = entity.GetComponent<ConstructionComponent>();
|
||||
construction.AddContainer(Container);
|
||||
}
|
||||
}
|
||||
}
|
||||
115
Content.Server/Construction/Completions/BuildMachine.cs
Normal file
115
Content.Server/Construction/Completions/BuildMachine.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects;
|
||||
using Content.Server.GameObjects.Components.Construction;
|
||||
using Content.Shared.Construction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Construction.Completions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class BuildMachine : IGraphAction
|
||||
{
|
||||
public async Task PerformAction(IEntity entity, IEntity? user)
|
||||
{
|
||||
if (!entity.TryGetComponent(out ContainerManagerComponent? containerManager))
|
||||
{
|
||||
Logger.Warning($"Machine frame entity {entity} did not have a container manager! Aborting build machine action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity.TryGetComponent(out MachineFrameComponent? machineFrame))
|
||||
{
|
||||
Logger.Warning($"Machine frame entity {entity} did not have a machine frame component! Aborting build machine action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!machineFrame.IsComplete)
|
||||
{
|
||||
Logger.Warning($"Machine frame entity {entity} doesn't have all required parts to be built! Aborting build machine action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!containerManager.TryGetContainer(MachineFrameComponent.BoardContainer, out var entBoardContainer))
|
||||
{
|
||||
Logger.Warning($"Machine frame entity {entity} did not have the '{MachineFrameComponent.BoardContainer}' container! Aborting build machine action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!containerManager.TryGetContainer(MachineFrameComponent.PartContainer, out var entPartContainer))
|
||||
{
|
||||
Logger.Warning($"Machine frame entity {entity} did not have the '{MachineFrameComponent.PartContainer}' container! Aborting build machine action.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entBoardContainer.ContainedEntities.Count != 1)
|
||||
{
|
||||
Logger.Warning($"Machine frame entity {entity} did not have exactly one item in the '{MachineFrameComponent.BoardContainer}' container! Aborting build machine action.");
|
||||
}
|
||||
|
||||
var board = entBoardContainer.ContainedEntities[0];
|
||||
|
||||
if (!board.TryGetComponent(out MachineBoardComponent? boardComponent))
|
||||
{
|
||||
Logger.Warning($"Machine frame entity {entity} had an invalid entity in container \"{MachineFrameComponent.BoardContainer}\"! Aborting build machine action.");
|
||||
return;
|
||||
}
|
||||
|
||||
var entityManager = entity.EntityManager;
|
||||
entBoardContainer.Remove(board);
|
||||
|
||||
var machine = entityManager.SpawnEntity(boardComponent.Prototype, entity.Transform.Coordinates);
|
||||
machine.Transform.LocalRotation = entity.Transform.LocalRotation;
|
||||
|
||||
var boardContainer = ContainerManagerComponent.Ensure<Container>(MachineFrameComponent.BoardContainer, machine, out var existed);
|
||||
|
||||
if (existed)
|
||||
{
|
||||
// Clean that up...
|
||||
boardContainer.CleanContainer();
|
||||
}
|
||||
|
||||
var partContainer = ContainerManagerComponent.Ensure<Container>(MachineFrameComponent.PartContainer, machine, out existed);
|
||||
|
||||
if (existed)
|
||||
{
|
||||
// Clean that up, too...
|
||||
partContainer.CleanContainer();
|
||||
}
|
||||
|
||||
boardContainer.Insert(board);
|
||||
|
||||
// Now we insert all parts.
|
||||
foreach (var part in entPartContainer.ContainedEntities.ToArray())
|
||||
{
|
||||
entPartContainer.ForceRemove(part);
|
||||
partContainer.Insert(part);
|
||||
}
|
||||
|
||||
if (machine.TryGetComponent(out ConstructionComponent? construction))
|
||||
{
|
||||
// We only add these two container. If some construction needs to take other containers into account, fix this.
|
||||
construction.AddContainer(MachineFrameComponent.BoardContainer);
|
||||
construction.AddContainer(MachineFrameComponent.PartContainer);
|
||||
}
|
||||
|
||||
if (machine.TryGetComponent(out MachineComponent? machineComp))
|
||||
{
|
||||
machineComp.RefreshParts();
|
||||
}
|
||||
|
||||
entity.Delete();
|
||||
}
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using Content.Shared.Construction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Construction.Completions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class EmptyAllContainers : IGraphAction
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task PerformAction(IEntity entity, IEntity? user)
|
||||
{
|
||||
if (entity.Deleted || !entity.TryGetComponent<ContainerManagerComponent>(out var containerManager))
|
||||
return;
|
||||
|
||||
foreach (var container in containerManager.GetAllContainers())
|
||||
{
|
||||
container.EmptyContainer(true, entity.Transform.Coordinates);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Threading.Tasks;
|
||||
using Content.Shared.Construction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects.Components.Container;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
@@ -26,12 +27,7 @@ namespace Content.Server.Construction.Completions
|
||||
if (!entity.TryGetComponent(out ContainerManagerComponent? containerManager) ||
|
||||
!containerManager.TryGetContainer(Container, out var container)) return;
|
||||
|
||||
foreach (var ent in container.ContainedEntities.ToArray())
|
||||
{
|
||||
if (ent == null || ent.Deleted) continue;
|
||||
container.ForceRemove(ent);
|
||||
ent.Transform.Coordinates = entity.Transform.Coordinates;
|
||||
}
|
||||
container.EmptyContainer(true, entity.Transform.Coordinates);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Construction;
|
||||
using Content.Shared.Construction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Construction.Completions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class MachineFrameRegenerateProgress : IGraphAction
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{ }
|
||||
|
||||
public async Task PerformAction(IEntity entity, IEntity? user)
|
||||
{
|
||||
if (entity.Deleted)
|
||||
return;
|
||||
|
||||
if (entity.TryGetComponent<MachineFrameComponent>(out var machineFrame))
|
||||
{
|
||||
machineFrame.RegenerateProgress();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
Content.Server/Construction/Conditions/AllWiresCut.cs
Normal file
46
Content.Server/Construction/Conditions/AllWiresCut.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components;
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Content.Shared.Construction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Construction.Conditions
|
||||
{
|
||||
/// <summary>
|
||||
/// A condition that requires all wires to be cut (or intact)
|
||||
/// Returns true if the entity doesn't have a wires component.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class AllWiresCut : IEdgeCondition
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.Value, "value", true);
|
||||
}
|
||||
|
||||
public bool Value { get; private set; } = true;
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
{
|
||||
if (entity.Deleted)
|
||||
return false;
|
||||
|
||||
if (!entity.TryGetComponent<WiresComponent>(out var wires))
|
||||
return true;
|
||||
|
||||
foreach (var wire in wires.WiresList)
|
||||
{
|
||||
switch (Value)
|
||||
{
|
||||
case true when !wire.IsCut:
|
||||
case false when wire.IsCut:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,13 +29,17 @@ namespace Content.Server.Construction.Conditions
|
||||
return container.ContainedEntities.Count == 0;
|
||||
}
|
||||
|
||||
public void DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange)
|
||||
public bool DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
if (!entity.TryGetComponent(out ContainerManagerComponent? containerManager) ||
|
||||
!containerManager.TryGetContainer(Container, out var container)) return;
|
||||
!containerManager.TryGetContainer(Container, out var container)) return false;
|
||||
|
||||
if (container.ContainedEntities.Count == 0)
|
||||
return false;
|
||||
|
||||
message.AddMarkup(Text);
|
||||
return true;
|
||||
|
||||
if (container.ContainedEntities.Count != 0)
|
||||
message.AddMarkup(Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,16 +27,21 @@ namespace Content.Server.Construction.Conditions
|
||||
return doorComponent.IsWeldedShut == Welded;
|
||||
}
|
||||
|
||||
public void DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange)
|
||||
public bool DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
if (!entity.TryGetComponent(out ServerDoorComponent doorComponent)) return;
|
||||
if (!entity.TryGetComponent(out ServerDoorComponent doorComponent)) return false;
|
||||
|
||||
if (doorComponent.State == ServerDoorComponent.DoorState.Closed && Welded)
|
||||
message.AddMarkup(Loc.GetString("First, weld the door.\n"));
|
||||
else if (doorComponent.IsWeldedShut && !Welded)
|
||||
{
|
||||
message.AddMarkup(Loc.GetString("First, unweld the door.\n"));
|
||||
message.AddMarkup(Loc.GetString("First, weld the door.\n"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!doorComponent.IsWeldedShut || Welded) return false;
|
||||
|
||||
message.AddMarkup(Loc.GetString("First, unweld the door.\n"));
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,15 +25,21 @@ namespace Content.Server.Construction.Conditions
|
||||
return physics.Anchored == Anchored;
|
||||
}
|
||||
|
||||
public void DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange)
|
||||
public bool DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
if (!entity.TryGetComponent(out IPhysicsComponent physics)) return;
|
||||
if (!entity.TryGetComponent(out IPhysicsComponent physics)) return false;
|
||||
|
||||
if(Anchored && !physics.Anchored)
|
||||
message.AddMarkup("First, anchor it.\n");
|
||||
switch (Anchored)
|
||||
{
|
||||
case true when !physics.Anchored:
|
||||
message.AddMarkup("First, anchor it.\n");
|
||||
return true;
|
||||
case false when physics.Anchored:
|
||||
message.AddMarkup("First, unanchor it.\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!Anchored && physics.Anchored)
|
||||
message.AddMarkup("First, unanchor it.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Construction;
|
||||
using Content.Shared.Construction;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Construction.Conditions
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks that the entity has all parts needed in the machine frame component.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class MachineFrameComplete : IEdgeCondition
|
||||
{
|
||||
public void ExposeData(ObjectSerializer serializer) { }
|
||||
|
||||
public async Task<bool> Condition(IEntity entity)
|
||||
{
|
||||
if (entity.Deleted || !entity.TryGetComponent<MachineFrameComponent>(out var machineFrame))
|
||||
return false;
|
||||
|
||||
return machineFrame.IsComplete;
|
||||
}
|
||||
|
||||
public bool DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
if (!entity.TryGetComponent<MachineFrameComponent>(out var machineFrame))
|
||||
return false;
|
||||
|
||||
if (!machineFrame.HasBoard)
|
||||
{
|
||||
message.AddMarkup(Loc.GetString("Insert [color=cyan]any machine circuit board[/color]."));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (machineFrame.IsComplete) return false;
|
||||
|
||||
message.AddMarkup(Loc.GetString("Requires:\n"));
|
||||
foreach (var (part, required) in machineFrame.Requirements)
|
||||
{
|
||||
var amount = required - machineFrame.Progress[part];
|
||||
|
||||
if(amount == 0) continue;
|
||||
|
||||
message.AddMarkup(Loc.GetString("[color=yellow]{0}x[/color] [color=green]{1}[/color]\n", amount, Loc.GetString(part.ToString())));
|
||||
}
|
||||
|
||||
foreach (var (material, required) in machineFrame.MaterialRequirements)
|
||||
{
|
||||
var amount = required - machineFrame.MaterialProgress[material];
|
||||
|
||||
if(amount == 0) continue;
|
||||
|
||||
message.AddMarkup(Loc.GetString("[color=yellow]{0}x[/color] [color=green]{1}[/color]\n", amount, Loc.GetString(material.ToString())));
|
||||
}
|
||||
|
||||
foreach (var (compName, info) in machineFrame.ComponentRequirements)
|
||||
{
|
||||
var amount = info.Amount - machineFrame.ComponentProgress[compName];
|
||||
|
||||
if(amount == 0) continue;
|
||||
|
||||
message.AddMarkup(Loc.GetString("[color=yellow]{0}x[/color] [color=green]{1}[/color]\n", info.Amount, Loc.GetString(info.ExamineName)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,15 +26,21 @@ namespace Content.Server.Construction.Conditions
|
||||
return wires.IsPanelOpen == Open;
|
||||
}
|
||||
|
||||
public void DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange)
|
||||
public bool DoExamine(IEntity entity, FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
if (!entity.TryGetComponent(out WiresComponent wires)) return;
|
||||
if (!entity.TryGetComponent(out WiresComponent wires)) return false;
|
||||
|
||||
if(Open && !wires.IsPanelOpen)
|
||||
message.AddMarkup(Loc.GetString("First, open the maintenance panel.\n"));
|
||||
switch (Open)
|
||||
{
|
||||
case true when !wires.IsPanelOpen:
|
||||
message.AddMarkup(Loc.GetString("First, open the maintenance panel.\n"));
|
||||
return true;
|
||||
case false when wires.IsPanelOpen:
|
||||
message.AddMarkup(Loc.GetString("First, close the maintenance panel.\n"));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!Open && wires.IsPanelOpen)
|
||||
message.AddMarkup(Loc.GetString("First, close the maintenance panel.\n"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
20
Content.Server/Construction/MachinePart.cs
Normal file
20
Content.Server/Construction/MachinePart.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace Content.Server.Construction
|
||||
{
|
||||
public enum MachinePart : byte
|
||||
{
|
||||
Capacitor,
|
||||
ScanningModule,
|
||||
Manipulator,
|
||||
Laser,
|
||||
MatterBin,
|
||||
|
||||
// Subspace parts.
|
||||
Ansible,
|
||||
Filter,
|
||||
Amplifier,
|
||||
Treatment,
|
||||
Analyzer,
|
||||
Crystal,
|
||||
Transmitter,
|
||||
}
|
||||
}
|
||||
63
Content.Server/Construction/StackHelpers.cs
Normal file
63
Content.Server/Construction/StackHelpers.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.GameObjects.Components.Stack;
|
||||
using Content.Shared.GameObjects.Components;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Construction
|
||||
{
|
||||
public static class StackHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Spawns a stack of a specified type given an amount.
|
||||
/// </summary>
|
||||
public static IEntity SpawnStack(StackType stack, int amount, EntityCoordinates coordinates, IEntityManager? entityManager = null)
|
||||
{
|
||||
entityManager ??= IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
string prototype;
|
||||
|
||||
switch (stack)
|
||||
{
|
||||
case StackType.Metal:
|
||||
prototype = "SteelSheet1";
|
||||
break;
|
||||
|
||||
case StackType.Glass:
|
||||
prototype = "GlassSheet1";
|
||||
break;
|
||||
|
||||
case StackType.MetalRod:
|
||||
prototype = "MetalRodStack1";
|
||||
break;
|
||||
|
||||
case StackType.Phoron:
|
||||
prototype = "PhoronStack1";
|
||||
break;
|
||||
|
||||
case StackType.Plasteel:
|
||||
prototype = "PlasteelSheet1";
|
||||
break;
|
||||
|
||||
case StackType.Cable:
|
||||
prototype = "ApcExtensionCableStack1";
|
||||
break;
|
||||
|
||||
// TODO: Add more.
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(stack),"Stack type doesn't have a prototype specified yet!");
|
||||
}
|
||||
|
||||
var ent = entityManager.SpawnEntity(prototype, coordinates);
|
||||
|
||||
var stackComponent = ent.GetComponent<StackComponent>();
|
||||
|
||||
stackComponent.Count = Math.Min(amount, stackComponent.MaxCount);
|
||||
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user