Data-oriented Construction System (#2152)

- Powerful
- Data-oriented
- Approved by PJB
- Powered by node graphs and AI pathfinding
- Coded by the same nerd who brought you atmos

Co-authored-by: Exp <theexp111@gmail.com>
This commit is contained in:
Víctor Aguilera Puerto
2020-10-08 17:41:23 +02:00
committed by GitHub
parent a6647e8de1
commit 745401a41e
261 changed files with 3886 additions and 11986 deletions

View File

@@ -0,0 +1,68 @@
#nullable enable
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Construction;
using Content.Shared.Construction;
using JetBrains.Annotations;
using Robust.Server.GameObjects.Components.Container;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class BuildComputer : IGraphAction
{
public string Container { get; private set; } = string.Empty;
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.Container, "container", string.Empty);
}
public async Task PerformAction(IEntity entity, IEntity? user)
{
if (!entity.TryGetComponent(out ContainerManagerComponent? containerManager))
{
Logger.Warning($"Computer entity {entity} did not have a container manager! Aborting build computer action.");
return;
}
if (!containerManager.TryGetContainer(Container, out var container))
{
Logger.Warning($"Computer entity {entity} did not have the specified '{Container}' container! Aborting build computer action.");
return;
}
if (container.ContainedEntities.Count != 1)
{
Logger.Warning($"Computer entity {entity} did not have exactly one item in the specified '{Container}' container! Aborting build computer action.");
}
var board = container.ContainedEntities[0];
if (!board.TryGetComponent(out ComputerBoardComponent? boardComponent))
{
Logger.Warning($"Computer entity {entity} had an invalid entity in container \"{Container}\"! Aborting build computer action.");
return;
}
var entityManager = entity.EntityManager;
container.Remove(board);
var computer = entityManager.SpawnEntity(boardComponent.Prototype, entity.Transform.Coordinates);
computer.Transform.LocalRotation = entity.Transform.LocalRotation;
var computerContainer = ContainerManagerComponent.Ensure<Container>(Container, computer);
computerContainer.Insert(board);
if (computer.TryGetComponent(out ConstructionComponent? construction))
{
// We only add this container. If some construction needs to take other containers into account, fix this.
construction.AddContainer(Container);
}
entity.Delete();
}
}
}

View File

@@ -0,0 +1,24 @@
#nullable enable
using System.Threading.Tasks;
using Content.Shared.Construction;
using JetBrains.Annotations;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class DeleteEntity : IGraphAction
{
public void ExposeData(ObjectSerializer serializer)
{
}
public async Task PerformAction(IEntity entity, IEntity? user)
{
if (entity.Deleted) return;
entity.Delete();
}
}
}

View File

@@ -0,0 +1,37 @@
#nullable enable
using System.Linq;
using System.Threading.Tasks;
using Content.Shared.Construction;
using JetBrains.Annotations;
using Robust.Server.GameObjects.Components.Container;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class EmptyContainer : IGraphAction
{
public string Container { get; private set; } = string.Empty;
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.Container, "container", string.Empty);
}
public async Task PerformAction(IEntity entity, IEntity? user)
{
if (entity.Deleted) return;
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;
}
}
}
}

View File

@@ -0,0 +1,39 @@
#nullable enable
using System.Threading.Tasks;
using Content.Shared.Audio;
using Content.Shared.Construction;
using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class PlaySound : IGraphAction
{
public string SoundCollection { get; private set; } = string.Empty;
public string Sound { get; private set; } = string.Empty;
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.Sound, "sound", string.Empty);
serializer.DataField(this, x => x.SoundCollection, "soundCollection", string.Empty);
}
public async Task PerformAction(IEntity entity, IEntity? user)
{
var sound = GetSound();
if (string.IsNullOrEmpty(sound)) return;
EntitySystem.Get<AudioSystem>().PlayFromEntity(sound, entity, AudioHelpers.WithVariation(0.125f));
}
private string GetSound()
{
return !string.IsNullOrEmpty(SoundCollection) ? AudioHelpers.GetRandomFileFromSoundCollection(SoundCollection) : Sound;
}
}
}

View File

@@ -0,0 +1,33 @@
#nullable enable
using System.Threading.Tasks;
using Content.Shared.Construction;
using Content.Shared.Interfaces;
using JetBrains.Annotations;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class PopupUser : IGraphAction
{
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.Text, "text", string.Empty);
serializer.DataField(this, x => x.Cursor, "cursor", false);
}
public bool Cursor { get; private set; } = false;
public string Text { get; private set; } = string.Empty;
public async Task PerformAction(IEntity entity, IEntity? user)
{
if (user == null) return;
if(Cursor)
user.PopupMessageCursor(Text);
else
entity.PopupMessage(user, Text);
}
}
}

View File

@@ -0,0 +1,28 @@
#nullable enable
using System.Threading.Tasks;
using Content.Shared.Construction;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Components;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class SetAnchor : IGraphAction
{
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.Value, "value", true);
}
public bool Value { get; private set; } = true;
public async Task PerformAction(IEntity entity, IEntity? user)
{
if (!entity.TryGetComponent(out CollidableComponent? collidable)) return;
collidable.Anchored = Value;
}
}
}

View File

@@ -0,0 +1,29 @@
#nullable enable
using System.Threading.Tasks;
using Content.Server.Utility;
using Content.Shared.Construction;
using JetBrains.Annotations;
using Robust.Shared.GameObjects.Components.Transform;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class SnapToGrid : IGraphAction
{
public SnapGridOffset Offset { get; private set; } = SnapGridOffset.Center;
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.Offset, "offset", SnapGridOffset.Center);
}
public async Task PerformAction(IEntity entity, IEntity? user)
{
if (entity.Deleted) return;
entity.SnapToGrid(Offset);
}
}
}

View File

@@ -0,0 +1,36 @@
#nullable enable
using System.Threading.Tasks;
using Content.Shared.Construction;
using JetBrains.Annotations;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class SpawnPrototype : IGraphAction
{
public string Prototype { get; private set; } = string.Empty;
public int Amount { get; private set; } = 1;
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.Prototype, "prototype", string.Empty);
serializer.DataField(this, x => x.Amount, "amount", 1);
}
public async Task PerformAction(IEntity entity, IEntity? user)
{
if (entity.Deleted || string.IsNullOrEmpty(Prototype)) return;
var entityManager = IoCManager.Resolve<IEntityManager>();
var coordinates = entity.Transform.Coordinates;
for (var i = 0; i < Amount; i++)
{
entityManager.SpawnEntity(Prototype, coordinates);
}
}
}
}

View File

@@ -0,0 +1,33 @@
#nullable enable
using System.Threading.Tasks;
using Content.Shared.Construction;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class SpriteChange : IGraphAction
{
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.SpriteSpecifier, "specifier", SpriteSpecifier.Invalid);
serializer.DataField(this, x => x.Layer, "layer", 0);
}
public int Layer { get; private set; } = 0;
public SpriteSpecifier? SpriteSpecifier { get; private set; } = SpriteSpecifier.Invalid;
public async Task PerformAction(IEntity entity, IEntity? user)
{
if (entity.Deleted || SpriteSpecifier == null || SpriteSpecifier == SpriteSpecifier.Invalid) return;
if (!entity.TryGetComponent(out SpriteComponent? sprite)) return;
sprite.LayerSetSprite(Layer, SpriteSpecifier);
}
}
}

View File

@@ -0,0 +1,39 @@
#nullable enable
using System.Threading.Tasks;
using Content.Shared.Construction;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class SpriteStateChange : IGraphAction
{
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.State, "state", string.Empty);
serializer.DataField(this, x => x.Layer, "layer", 0);
}
public int Layer { get; private set; } = 0;
public string? State { get; private set; } = string.Empty;
public async Task StepCompleted(IEntity entity, IEntity user)
{
await PerformAction(entity, user);
}
public async Task PerformAction(IEntity entity, IEntity? user)
{
if (entity.Deleted || string.IsNullOrEmpty(State)) return;
if (!entity.TryGetComponent(out SpriteComponent? sprite)) return;
sprite.LayerSetState(Layer, State);
}
}
}

View File

@@ -0,0 +1,49 @@
#nullable enable
using System.Threading.Tasks;
using Content.Shared.Construction;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Reflection;
using Robust.Shared.IoC;
using Robust.Shared.Serialization;
namespace Content.Server.Construction.Completions
{
[UsedImplicitly]
public class VisualizerDataInt : IGraphAction
{
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
public VisualizerDataInt()
{
IoCManager.InjectDependencies(this);
}
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => x.Key, "key", string.Empty);
serializer.DataField(this, x => x.Data, "data", 0);
}
public string? Key { get; private set; } = string.Empty;
public int Data { get; private set; } = 0;
public async Task PerformAction(IEntity entity, IEntity? user)
{
if (string.IsNullOrEmpty(Key)) return;
if (entity.TryGetComponent(out AppearanceComponent? appearance))
{
if(_reflectionManager.TryParseEnumReference(Key, out var @enum))
{
appearance.SetData(@enum, Data);
}
else
{
appearance.SetData(Key, Data);
}
}
}
}
}