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
@@ -0,0 +1,23 @@
|
|||||||
|
using Content.Shared.GameObjects.Components.Construction;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Client.Interfaces.GameObjects.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.GameObjects.Components.Construction
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public class MachineFrameVisualizer : AppearanceVisualizer
|
||||||
|
{
|
||||||
|
public override void OnChangeData(AppearanceComponent component)
|
||||||
|
{
|
||||||
|
base.OnChangeData(component);
|
||||||
|
|
||||||
|
if (component.TryGetData<int>(MachineFrameVisuals.State, out var data))
|
||||||
|
{
|
||||||
|
var sprite = component.Owner.GetComponent<ISpriteComponent>();
|
||||||
|
|
||||||
|
sprite.LayerSetState(0, $"box_{data}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -216,6 +216,10 @@
|
|||||||
"ConveyorAssembly",
|
"ConveyorAssembly",
|
||||||
"TwoWayLever",
|
"TwoWayLever",
|
||||||
"FirelockElectronics",
|
"FirelockElectronics",
|
||||||
|
"Machine",
|
||||||
|
"MachinePart",
|
||||||
|
"MachineFrame",
|
||||||
|
"MachineBoard"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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 Content.Shared.Construction;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects.Components.Container;
|
using Robust.Server.GameObjects.Components.Container;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
@@ -26,12 +27,7 @@ namespace Content.Server.Construction.Completions
|
|||||||
if (!entity.TryGetComponent(out ContainerManagerComponent? containerManager) ||
|
if (!entity.TryGetComponent(out ContainerManagerComponent? containerManager) ||
|
||||||
!containerManager.TryGetContainer(Container, out var container)) return;
|
!containerManager.TryGetContainer(Container, out var container)) return;
|
||||||
|
|
||||||
foreach (var ent in container.ContainedEntities.ToArray())
|
container.EmptyContainer(true, entity.Transform.Coordinates);
|
||||||
{
|
|
||||||
if (ent == null || ent.Deleted) continue;
|
|
||||||
container.ForceRemove(ent);
|
|
||||||
ent.Transform.Coordinates = 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;
|
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) ||
|
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;
|
||||||
|
|
||||||
if (container.ContainedEntities.Count != 0)
|
|
||||||
message.AddMarkup(Text);
|
message.AddMarkup(Text);
|
||||||
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,16 +27,21 @@ namespace Content.Server.Construction.Conditions
|
|||||||
return doorComponent.IsWeldedShut == Welded;
|
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)
|
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;
|
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)
|
switch (Anchored)
|
||||||
|
{
|
||||||
|
case true when !physics.Anchored:
|
||||||
message.AddMarkup("First, anchor it.\n");
|
message.AddMarkup("First, anchor it.\n");
|
||||||
|
return true;
|
||||||
if(!Anchored && physics.Anchored)
|
case false when physics.Anchored:
|
||||||
message.AddMarkup("First, unanchor it.\n");
|
message.AddMarkup("First, unanchor it.\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
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)
|
switch (Open)
|
||||||
|
{
|
||||||
|
case true when !wires.IsPanelOpen:
|
||||||
message.AddMarkup(Loc.GetString("First, open the maintenance panel.\n"));
|
message.AddMarkup(Loc.GetString("First, open the maintenance panel.\n"));
|
||||||
|
return true;
|
||||||
if(!Open && wires.IsPanelOpen)
|
case false when wires.IsPanelOpen:
|
||||||
message.AddMarkup(Loc.GetString("First, close the maintenance panel.\n"));
|
message.AddMarkup(Loc.GetString("First, close the maintenance panel.\n"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,10 +48,10 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
private ConstructionGraphNode? _target = null;
|
private ConstructionGraphNode? _target = null;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public ConstructionGraphPrototype GraphPrototype { get; private set; } = null!;
|
public ConstructionGraphPrototype? GraphPrototype { get; private set; }
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public ConstructionGraphNode Node { get; private set; } = null!;
|
public ConstructionGraphNode? Node { get; private set; } = null;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public ConstructionGraphEdge? Edge { get; private set; } = null;
|
public ConstructionGraphEdge? Edge { get; private set; } = null;
|
||||||
@@ -88,6 +88,17 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
serializer.DataField(ref _startingNodeIdentifier, "node", string.Empty);
|
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()
|
public void ClearTarget()
|
||||||
{
|
{
|
||||||
_target = null;
|
_target = null;
|
||||||
@@ -97,8 +108,8 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
|
|
||||||
public void UpdateTarget()
|
public void UpdateTarget()
|
||||||
{
|
{
|
||||||
// Can't pathfind without a target.
|
// Can't pathfind without a target or no node.
|
||||||
if (Target == null) return;
|
if (Target == null || Node == null || GraphPrototype == null) return;
|
||||||
|
|
||||||
// If we're at our target, stop pathfinding.
|
// If we're at our target, stop pathfinding.
|
||||||
if (Target == Node)
|
if (Target == Node)
|
||||||
@@ -133,7 +144,7 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let's set the next target edge.
|
// 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);
|
TargetNextEdge = Node.GetEdge(TargetPathfinding.Peek().Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +172,8 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
{
|
{
|
||||||
EdgeStep = 0;
|
EdgeStep = 0;
|
||||||
|
|
||||||
|
if (Node == null || GraphPrototype == null) return false;
|
||||||
|
|
||||||
foreach (var edge in Node.Edges)
|
foreach (var edge in Node.Edges)
|
||||||
{
|
{
|
||||||
if(edge.Steps.Count == 0)
|
if(edge.Steps.Count == 0)
|
||||||
@@ -344,7 +357,7 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
|
|
||||||
private async Task<bool> HandleCompletion(ConstructionGraphEdge edge, IEntity user)
|
private async Task<bool> HandleCompletion(ConstructionGraphEdge edge, IEntity user)
|
||||||
{
|
{
|
||||||
if (edge.Steps.Count != EdgeStep)
|
if (edge.Steps.Count != EdgeStep || GraphPrototype == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -380,6 +393,16 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ResetEdge()
|
||||||
|
{
|
||||||
|
_edgeNestedStepProgress = null;
|
||||||
|
TargetNextEdge = null;
|
||||||
|
Edge = null;
|
||||||
|
EdgeStep = 0;
|
||||||
|
|
||||||
|
UpdateTarget();
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<bool> HandleEdge(InteractUsingEventArgs eventArgs)
|
private async Task<bool> HandleEdge(InteractUsingEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (Edge == null || EdgeStep >= Edge.Steps.Count) return false;
|
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)
|
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);
|
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 (entity.TryGetComponent(out ConstructionComponent? construction))
|
||||||
{
|
{
|
||||||
if(construction.GraphPrototype != GraphPrototype)
|
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.Node = node;
|
||||||
construction.Target = Target;
|
construction.Target = Target;
|
||||||
@@ -461,14 +485,6 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
if (GraphPrototype.Nodes.TryGetValue(_startingNodeIdentifier, out var node))
|
if (GraphPrototype.Nodes.TryGetValue(_startingNodeIdentifier, out var node))
|
||||||
{
|
{
|
||||||
Node = node;
|
Node = node;
|
||||||
|
|
||||||
foreach (var action in Node.Actions)
|
|
||||||
{
|
|
||||||
action.PerformAction(Owner, null);
|
|
||||||
|
|
||||||
if (Owner.Deleted)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
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)
|
public async Task ChangeNode(string node)
|
||||||
{
|
{
|
||||||
|
if (GraphPrototype == null) return;
|
||||||
|
|
||||||
var graphNode = GraphPrototype.Nodes[node];
|
var graphNode = GraphPrototype.Nodes[node];
|
||||||
|
|
||||||
if (_handling && _handlingTask?.Task != null)
|
if (_handling && _handlingTask?.Task != null)
|
||||||
@@ -508,21 +541,28 @@ namespace Content.Server.GameObjects.Components.Construction
|
|||||||
|
|
||||||
if (Edge == null && TargetNextEdge != null)
|
if (Edge == null && TargetNextEdge != null)
|
||||||
{
|
{
|
||||||
|
var preventStepExamine = false;
|
||||||
|
|
||||||
foreach (var condition in TargetNextEdge.Conditions)
|
foreach (var condition in TargetNextEdge.Conditions)
|
||||||
{
|
{
|
||||||
condition.DoExamine(Owner, message, inDetailsRange);
|
preventStepExamine |= condition.DoExamine(Owner, message, inDetailsRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!preventStepExamine)
|
||||||
TargetNextEdge.Steps[0].DoExamine(message, inDetailsRange);
|
TargetNextEdge.Steps[0].DoExamine(message, inDetailsRange);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Edge != null)
|
if (Edge != null)
|
||||||
{
|
{
|
||||||
|
var preventStepExamine = false;
|
||||||
|
|
||||||
foreach (var condition in Edge.Conditions)
|
foreach (var condition in Edge.Conditions)
|
||||||
{
|
{
|
||||||
condition.DoExamine(Owner, message, inDetailsRange);
|
preventStepExamine |= condition.DoExamine(Owner, message, inDetailsRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (preventStepExamine) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_edgeNestedStepProgress == null)
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Content.Server/Interfaces/GameObjects/IRefreshParts.cs
Normal file
11
Content.Server/Interfaces/GameObjects/IRefreshParts.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Server.GameObjects.Components.Construction;
|
||||||
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.Interfaces.GameObjects
|
||||||
|
{
|
||||||
|
public interface IRefreshParts
|
||||||
|
{
|
||||||
|
void RefreshParts(IEnumerable<MachinePartComponent> parts);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
using Robust.Shared.Interfaces.Serialization;
|
using Robust.Shared.Interfaces.Serialization;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -8,6 +9,6 @@ namespace Content.Shared.Construction
|
|||||||
public interface IEdgeCondition : IExposeData
|
public interface IEdgeCondition : IExposeData
|
||||||
{
|
{
|
||||||
Task<bool> Condition(IEntity entity);
|
Task<bool> Condition(IEntity entity);
|
||||||
void DoExamine(IEntity entity, FormattedMessage message, bool inExamineRange) { }
|
bool DoExamine(IEntity entity, FormattedMessage message, bool inExamineRange) { return false; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.GameObjects.Components.Construction
|
||||||
|
{
|
||||||
|
[NetSerializable]
|
||||||
|
public enum MachineFrameVisuals
|
||||||
|
{
|
||||||
|
State,
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -19,8 +19,8 @@
|
|||||||
- type: latheRecipe
|
- type: latheRecipe
|
||||||
id: Welder
|
id: Welder
|
||||||
icon:
|
icon:
|
||||||
sprite: Objects/Tools/welder.rsi
|
sprite: /Textures/Objects/Tools/welder.rsi
|
||||||
state: welder
|
state: icon
|
||||||
result: Welder
|
result: Welder
|
||||||
completetime: 500
|
completetime: 500
|
||||||
materials:
|
materials:
|
||||||
|
|||||||
@@ -49,6 +49,18 @@
|
|||||||
- state: autolathe_unlit
|
- state: autolathe_unlit
|
||||||
shader: unshaded
|
shader: unshaded
|
||||||
map: ["enum.AutolatheVisualLayers.BaseUnlit"]
|
map: ["enum.AutolatheVisualLayers.BaseUnlit"]
|
||||||
|
- state: autolathe_panel
|
||||||
|
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
|
||||||
|
- type: Construction
|
||||||
|
graph: machine
|
||||||
|
node: machine
|
||||||
|
- type: BreakableConstruction
|
||||||
|
node: machineFrame
|
||||||
|
- type: Machine
|
||||||
|
board: AutolatheMachineCircuitboard
|
||||||
|
- type: Wires
|
||||||
|
BoardName: "Autolathe"
|
||||||
|
LayoutId: Autolathe
|
||||||
- type: LatheDatabase
|
- type: LatheDatabase
|
||||||
static: true
|
static: true
|
||||||
recipes:
|
recipes:
|
||||||
@@ -68,6 +80,7 @@
|
|||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: AutolatheVisualizer
|
- type: AutolatheVisualizer
|
||||||
|
- type: WiresVisualizer
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: BaseLathe
|
parent: BaseLathe
|
||||||
@@ -85,7 +98,12 @@
|
|||||||
map: ["enum.ProtolatheVisualLayers.BaseUnlit"]
|
map: ["enum.ProtolatheVisualLayers.BaseUnlit"]
|
||||||
- state: protolathe
|
- state: protolathe
|
||||||
map: ["enum.ProtolatheVisualLayers.AnimationLayer"]
|
map: ["enum.ProtolatheVisualLayers.AnimationLayer"]
|
||||||
|
- state: protolathe_panel
|
||||||
|
map: [ "enum.WiresVisualLayers.MaintenancePanel" ]
|
||||||
- type: ResearchClient
|
- type: ResearchClient
|
||||||
|
- type: Wires
|
||||||
|
BoardName: "Protolathe"
|
||||||
|
LayoutId: Protolathe
|
||||||
- type: TechnologyDatabase
|
- type: TechnologyDatabase
|
||||||
- type: ProtolatheDatabase
|
- type: ProtolatheDatabase
|
||||||
protolatherecipes:
|
protolatherecipes:
|
||||||
@@ -112,3 +130,4 @@
|
|||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: ProtolatheVisualizer
|
- type: ProtolatheVisualizer
|
||||||
|
- type: WiresVisualizer
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
- type: entity
|
||||||
|
id: UnfinishedMachineFrame
|
||||||
|
name: machine frame
|
||||||
|
suffix: Unfinished
|
||||||
|
placement:
|
||||||
|
mode: SnapgridCenter
|
||||||
|
components:
|
||||||
|
- type: Physics
|
||||||
|
mass: 25
|
||||||
|
anchored: true
|
||||||
|
shapes:
|
||||||
|
- !type:PhysShapeAabb
|
||||||
|
bounds: "-0.5,-0.25,0.5,0.25"
|
||||||
|
layer:
|
||||||
|
- Impassable
|
||||||
|
- MobImpassable
|
||||||
|
- VaultImpassable
|
||||||
|
- Opaque
|
||||||
|
mask:
|
||||||
|
- Impassable
|
||||||
|
- MobImpassable
|
||||||
|
- VaultImpassable
|
||||||
|
- type: Clickable
|
||||||
|
- type: InteractionOutline
|
||||||
|
- type: Anchorable
|
||||||
|
- type: Rotatable
|
||||||
|
- type: Pullable
|
||||||
|
- type: Construction
|
||||||
|
graph: machine
|
||||||
|
node: missingWires
|
||||||
|
- type: BreakableConstruction
|
||||||
|
node: start
|
||||||
|
- type: Sprite
|
||||||
|
sprite: "Constructible/Misc/stock_parts.rsi"
|
||||||
|
state: "box_0"
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: MachineFrame
|
||||||
|
name: machine frame
|
||||||
|
suffix: Ready
|
||||||
|
placement:
|
||||||
|
mode: SnapgridCenter
|
||||||
|
components:
|
||||||
|
- type: Physics
|
||||||
|
mass: 25
|
||||||
|
anchored: true
|
||||||
|
shapes:
|
||||||
|
- !type:PhysShapeAabb
|
||||||
|
bounds: "-0.5,-0.25,0.5,0.25"
|
||||||
|
layer:
|
||||||
|
- Impassable
|
||||||
|
- MobImpassable
|
||||||
|
- VaultImpassable
|
||||||
|
- Opaque
|
||||||
|
mask:
|
||||||
|
- Impassable
|
||||||
|
- MobImpassable
|
||||||
|
- VaultImpassable
|
||||||
|
- type: Clickable
|
||||||
|
- type: InteractionOutline
|
||||||
|
- type: Anchorable
|
||||||
|
- type: Rotatable
|
||||||
|
- type: Pullable
|
||||||
|
- type: Construction
|
||||||
|
graph: machine
|
||||||
|
node: machineFrame
|
||||||
|
- type: BreakableConstruction
|
||||||
|
node: missingWires
|
||||||
|
- type: MachineFrame
|
||||||
|
- type: Sprite
|
||||||
|
netsync: false
|
||||||
|
sprite: "Constructible/Misc/stock_parts.rsi"
|
||||||
|
state: "box_1"
|
||||||
|
- type: Appearance
|
||||||
|
visuals:
|
||||||
|
- type: MachineFrameVisualizer
|
||||||
@@ -0,0 +1,324 @@
|
|||||||
|
- type: entity
|
||||||
|
id: BaseStockPart
|
||||||
|
name: stock part
|
||||||
|
parent: BaseItem
|
||||||
|
description: What?
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
netsync: false
|
||||||
|
sprite: Constructible/Misc/stock_parts.rsi
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 1
|
||||||
|
|
||||||
|
# Rating 1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: CapacitorStockPart
|
||||||
|
name: capacitor
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A basic capacitor used in the construction of a variety of devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: capacitor
|
||||||
|
- type: MachinePart
|
||||||
|
part: Capacitor
|
||||||
|
rating: 1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ScanningModuleStockPart
|
||||||
|
name: scanning module
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A compact, high resolution scanning module used in the construction of certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: scan_module
|
||||||
|
- type: MachinePart
|
||||||
|
part: ScanningModule
|
||||||
|
rating: 1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: MicroManipulatorStockPart
|
||||||
|
name: micro-manipulator
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A tiny little manipulator used in the construction of certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: micro_mani
|
||||||
|
- type: MachinePart
|
||||||
|
part: Manipulator
|
||||||
|
rating: 1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: MicroLaserStockPart
|
||||||
|
name: micro-laser
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A tiny laser used in certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: micro_laser
|
||||||
|
- type: MachinePart
|
||||||
|
part: Laser
|
||||||
|
rating: 1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: MatterBinStockPart
|
||||||
|
name: matter bin
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A container designed to hold compressed matter awaiting reconstruction.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: matter_bin
|
||||||
|
- type: MachinePart
|
||||||
|
part: MatterBin
|
||||||
|
rating: 1
|
||||||
|
|
||||||
|
# Rating 2
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: AdvancedCapacitorStockPart
|
||||||
|
name: advanced capacitor
|
||||||
|
parent: CapacitorStockPart
|
||||||
|
description: An advanced capacitor used in the construction of a variety of devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: adv_capacitor
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 2
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: AdvancedScanningModuleStockPart
|
||||||
|
name: advanced scanning module
|
||||||
|
parent: ScanningModuleStockPart
|
||||||
|
description: A compact, high resolution scanning module used in the construction of certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: adv_scan_module
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 2
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: NanoManipulatorStockPart
|
||||||
|
name: nano-manipulator
|
||||||
|
parent: MicroManipulatorStockPart
|
||||||
|
description: A tiny little manipulator used in the construction of certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: nano_mani
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 2
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: HighPowerMicroLaserStockPart
|
||||||
|
name: high-power micro-laser
|
||||||
|
parent: MicroLaserStockPart
|
||||||
|
description: A tiny laser used in certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: high_micro_laser
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 2
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: AdvancedMatterBinStockPart
|
||||||
|
name: advanced matter bin
|
||||||
|
parent: MatterBinStockPart
|
||||||
|
description: A container designed to hold compressed matter awaiting reconstruction.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: advanced_matter_bin
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 2
|
||||||
|
|
||||||
|
# Rating 3
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: SuperCapacitorStockPart
|
||||||
|
name: super capacitor
|
||||||
|
parent: CapacitorStockPart
|
||||||
|
description: A super-high capacity capacitor used in the construction of a variety of devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: super_capacitor
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 3
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: PhasicScanningModuleStockPart
|
||||||
|
name: phasic scanning module
|
||||||
|
parent: ScanningModuleStockPart
|
||||||
|
description: A compact, high resolution phasic scanning module used in the construction of certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: super_scan_module
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 3
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: PicoManipulatorStockPart
|
||||||
|
name: pico-manipulator
|
||||||
|
parent: MicroManipulatorStockPart
|
||||||
|
description: A tiny little manipulator used in the construction of certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: pico_mani
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 3
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: UltraHighPowerMicroLaserStockPart
|
||||||
|
name: ultra-high-power micro-laser
|
||||||
|
parent: MicroLaserStockPart
|
||||||
|
description: A tiny laser used in certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: ultra_high_micro_laser
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 3
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: SuperMatterBinStockPart
|
||||||
|
name: super matter bin
|
||||||
|
parent: MatterBinStockPart
|
||||||
|
description: A container designed to hold compressed matter awaiting reconstruction.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: super_matter_bin
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 3
|
||||||
|
|
||||||
|
# Rating 4
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: QuadraticCapacitorStockPart
|
||||||
|
name: quadratic capacitor
|
||||||
|
parent: CapacitorStockPart
|
||||||
|
description: A quadratic capacity capacitor used in the construction of a variety of devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: quadratic_capacitor
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 4
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: TriphasicScanningModuleStockPart
|
||||||
|
name: triphasic scanning module
|
||||||
|
parent: ScanningModuleStockPart
|
||||||
|
description: A compact, ultra resolution triphasic scanning module used in the construction of certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: triphasic_scan_module
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 4
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: FemtoManipulatorStockPart
|
||||||
|
name: femto-manipulator
|
||||||
|
parent: MicroManipulatorStockPart
|
||||||
|
description: A tiny little manipulator used in the construction of certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: femto_mani
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 4
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: QuadUltraMicroLaserStockPart
|
||||||
|
name: quad-ultra micro-laser
|
||||||
|
parent: MicroLaserStockPart
|
||||||
|
description: A tiny laser used in certain devices.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: quadultra_micro_laser
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 4
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: BluespaceMatterBinStockPart
|
||||||
|
name: bluespace matter bin
|
||||||
|
parent: MatterBinStockPart
|
||||||
|
description: A container designed to hold compressed matter in bluespace awaiting reconstruction.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: bluespace_matter_bin
|
||||||
|
- type: MachinePart
|
||||||
|
rating: 4
|
||||||
|
|
||||||
|
# Subspace stock parts
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: AnsibleSubspaceStockPart
|
||||||
|
name: subspace ansible
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A compact module capable of sensing extradimensional activity.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: subspace_ansible
|
||||||
|
- type: MachinePart
|
||||||
|
part: Ansible
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: FilterSubspaceStockPart
|
||||||
|
name: hyperwave filter
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A tiny device capable of filtering and converting super-intense radiowaves.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: hyperwave_filter
|
||||||
|
- type: MachinePart
|
||||||
|
part: Filter
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: AmplifierSubspaceStockPart
|
||||||
|
name: subspace amplifier
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A compact micro-machine capable of amplifying weak subspace transmissions.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: subspace_amplifier
|
||||||
|
- type: MachinePart
|
||||||
|
part: Amplifier
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: TreatmentSubspaceStockPart
|
||||||
|
name: subspace treatment disk
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A compact micro-machine capable of stretching out hyper-compressed radio waves.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: treatment_disk
|
||||||
|
- type: MachinePart
|
||||||
|
part: Treatment
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: AnalyzerSubspaceStockPart
|
||||||
|
name: subspace wavelength analyzer
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A sophisticated analyzer capable of analyzing cryptic subspace wavelengths.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: wavelength_analyzer
|
||||||
|
- type: MachinePart
|
||||||
|
part: Analyzer
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: CrystalSubspaceStockPart
|
||||||
|
name: ansible crystal
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A crystal made from pure glass used to transmit laser databursts to subspace.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: ansible_crystal
|
||||||
|
- type: MachinePart
|
||||||
|
part: Crystal
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: TransmitterSubspaceStockPart
|
||||||
|
name: subspace transmitter
|
||||||
|
parent: BaseStockPart
|
||||||
|
description: A large piece of equipment used to open a window into the subspace dimension.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: subspace_transmitter
|
||||||
|
- type: MachinePart
|
||||||
|
part: Transmitter
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
- type: entity
|
||||||
|
id: BaseMachineCircuitboard
|
||||||
|
parent: BaseItem
|
||||||
|
name: machine board
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: MachineBoard
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Constructible/Misc/module.rsi
|
||||||
|
state: id_mod
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: AutolatheMachineCircuitboard
|
||||||
|
parent: BaseMachineCircuitboard
|
||||||
|
name: Autolathe (Machine Board)
|
||||||
|
components:
|
||||||
|
- type: MachineBoard
|
||||||
|
prototype: Autolathe
|
||||||
|
requirements:
|
||||||
|
MatterBin: 3
|
||||||
|
Manipulator: 1
|
||||||
|
materialRequirements:
|
||||||
|
Glass: 1
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
steps:
|
steps:
|
||||||
- component: ComputerBoard
|
- component: ComputerBoard
|
||||||
store: board
|
store: board
|
||||||
name: a computer circuit board
|
name: any computer circuit board
|
||||||
icon:
|
icon:
|
||||||
sprite: "Constructible/Misc/module.rsi"
|
sprite: "Constructible/Misc/module.rsi"
|
||||||
state: "id_mod"
|
state: "id_mod"
|
||||||
@@ -57,8 +57,7 @@
|
|||||||
conditions:
|
conditions:
|
||||||
- !type:EntityAnchored { }
|
- !type:EntityAnchored { }
|
||||||
completed:
|
completed:
|
||||||
- !type:EmptyContainer
|
- !type:EmptyAllContainers {}
|
||||||
container: board
|
|
||||||
- !type:SpriteStateChange
|
- !type:SpriteStateChange
|
||||||
state: 0
|
state: 0
|
||||||
steps:
|
steps:
|
||||||
@@ -99,6 +98,10 @@
|
|||||||
- to: missingWires
|
- to: missingWires
|
||||||
conditions:
|
conditions:
|
||||||
- !type:EntityAnchored { }
|
- !type:EntityAnchored { }
|
||||||
|
completed:
|
||||||
|
- !type:SpawnPrototype
|
||||||
|
prototype: ApcExtensionCableStack1
|
||||||
|
amount: 5
|
||||||
steps:
|
steps:
|
||||||
- tool: Cutting
|
- tool: Cutting
|
||||||
|
|
||||||
|
|||||||
89
Resources/Prototypes/Recipes/Construction/Graphs/machine.yml
Normal file
89
Resources/Prototypes/Recipes/Construction/Graphs/machine.yml
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
- type: constructionGraph
|
||||||
|
id: machine
|
||||||
|
start: start
|
||||||
|
graph:
|
||||||
|
- node: start
|
||||||
|
actions:
|
||||||
|
- !type:SpawnPrototype
|
||||||
|
prototype: SteelSheet1
|
||||||
|
- !type:DeleteEntity {}
|
||||||
|
edges:
|
||||||
|
- to: missingWires
|
||||||
|
completed:
|
||||||
|
- !type:SetAnchor
|
||||||
|
value: false
|
||||||
|
steps:
|
||||||
|
- material: Metal
|
||||||
|
amount: 5
|
||||||
|
doAfter: 2.5
|
||||||
|
|
||||||
|
- node: missingWires
|
||||||
|
entity: UnfinishedMachineFrame
|
||||||
|
actions:
|
||||||
|
- !type:SpriteStateChange
|
||||||
|
state: "box_0"
|
||||||
|
- !type:EmptyAllContainers {}
|
||||||
|
edges:
|
||||||
|
- to: machineFrame
|
||||||
|
conditions:
|
||||||
|
- !type:EntityAnchored {}
|
||||||
|
steps:
|
||||||
|
- material: Cable
|
||||||
|
- to: start
|
||||||
|
conditions:
|
||||||
|
- !type:EntityAnchored
|
||||||
|
anchored: false
|
||||||
|
completed:
|
||||||
|
- !type:SpawnPrototype
|
||||||
|
prototype: SteelSheet1
|
||||||
|
amount: 5
|
||||||
|
- !type:DeleteEntity {}
|
||||||
|
steps:
|
||||||
|
- tool: Screwing
|
||||||
|
doAfter: 2
|
||||||
|
|
||||||
|
- node: machineFrame
|
||||||
|
entity: MachineFrame
|
||||||
|
actions:
|
||||||
|
- !type:MachineFrameRegenerateProgress {}
|
||||||
|
edges:
|
||||||
|
- to: machine
|
||||||
|
conditions:
|
||||||
|
- !type:EntityAnchored {}
|
||||||
|
- !type:MachineFrameComplete {}
|
||||||
|
completed:
|
||||||
|
# Yes, this is snowflaked so we don't have to make an unique graph per machine. You're welcome.
|
||||||
|
- !type:BuildMachine {}
|
||||||
|
steps:
|
||||||
|
- tool: Screwing
|
||||||
|
doAfter: 0.5
|
||||||
|
|
||||||
|
- to: missingWires
|
||||||
|
conditions:
|
||||||
|
- !type:EntityAnchored {}
|
||||||
|
completed:
|
||||||
|
- !type:SpawnPrototype
|
||||||
|
prototype: ApcExtensionCableStack1
|
||||||
|
steps:
|
||||||
|
- tool: Prying
|
||||||
|
doAfter: 2
|
||||||
|
completed:
|
||||||
|
- !type:EmptyAllContainers {}
|
||||||
|
- !type:MachineFrameRegenerateProgress {}
|
||||||
|
- tool: Cutting
|
||||||
|
doAfter: 0.25
|
||||||
|
|
||||||
|
- node: machine
|
||||||
|
actions:
|
||||||
|
- !type:AddContainer
|
||||||
|
container: machine_parts
|
||||||
|
- !type:AddContainer
|
||||||
|
container: machine_board
|
||||||
|
edges:
|
||||||
|
- to: machineFrame
|
||||||
|
conditions:
|
||||||
|
- !type:EntityAnchored {}
|
||||||
|
- !type:WirePanel {}
|
||||||
|
steps:
|
||||||
|
- tool: Prying
|
||||||
|
doAfter: 0.25
|
||||||
@@ -10,3 +10,16 @@
|
|||||||
icon:
|
icon:
|
||||||
sprite: Constructible/Misc/stock_parts.rsi
|
sprite: Constructible/Misc/stock_parts.rsi
|
||||||
state: 4
|
state: 4
|
||||||
|
|
||||||
|
- type: construction
|
||||||
|
name: machine frame
|
||||||
|
id: MachineFrame
|
||||||
|
graph: machine
|
||||||
|
startNode: start
|
||||||
|
targetNode: machine
|
||||||
|
category: Machines
|
||||||
|
placementMode: SnapgridCenter
|
||||||
|
canBuildInImpassable: false
|
||||||
|
icon:
|
||||||
|
sprite: Constructible/Misc/stock_parts.rsi
|
||||||
|
state: "box_0"
|
||||||
|
|||||||
@@ -200,7 +200,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "protolathe_open",
|
"name": "protolathe_panel",
|
||||||
"directions": 1,
|
"directions": 1,
|
||||||
"delays": [
|
"delays": [
|
||||||
[
|
[
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
@@ -162,6 +162,7 @@
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unequip/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unequip/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=unlockable/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=unlockable/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unoccluded/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Unoccluded/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=unweld/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=uplink/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=uplink/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Wirecutter/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Wirecutter/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Xeno/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Xeno/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|||||||
Reference in New Issue
Block a user