Removes the ConstructorComponents and moves the construction blueprint feature into a new ECS system. (#1114)

This commit is contained in:
Acruid
2020-06-15 12:30:11 -07:00
committed by GitHub
parent 548b91df61
commit 189ed9309f
11 changed files with 302 additions and 330 deletions

View File

@@ -1,8 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Client.GameObjects.Components.Construction; using Content.Client.GameObjects.EntitySystems;
using Content.Client.Interfaces.GameObjects;
using Content.Shared.Construction; using Content.Shared.Construction;
using Content.Shared.GameObjects.Components.Interactable; using Content.Shared.GameObjects.Components.Interactable;
using Robust.Client.Graphics; using Robust.Client.Graphics;
@@ -14,7 +13,7 @@ using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.Utility; using Robust.Client.Utility;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Interfaces.GameObjects.Components; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -24,11 +23,11 @@ namespace Content.Client.Construction
public class ConstructionMenu : SS14Window public class ConstructionMenu : SS14Window
{ {
#pragma warning disable CS0649 #pragma warning disable CS0649
[Dependency] readonly IPrototypeManager PrototypeManager; [Dependency] private readonly IPrototypeManager _prototypeManager;
[Dependency] readonly IResourceCache ResourceCache; [Dependency] private readonly IResourceCache _resourceCache;
[Dependency] private readonly IEntitySystemManager _systemManager;
#pragma warning restore #pragma warning restore
public ConstructorComponent Owner { get; set; }
private readonly Button BuildButton; private readonly Button BuildButton;
private readonly Button EraseButton; private readonly Button EraseButton;
private readonly LineEdit SearchBar; private readonly LineEdit SearchBar;
@@ -118,6 +117,7 @@ namespace Content.Client.Construction
PopulateTree(); PopulateTree();
} }
/// <inheritdoc />
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
base.Dispose(disposing); base.Dispose(disposing);
@@ -128,7 +128,7 @@ namespace Content.Client.Construction
} }
} }
void OnItemSelected() private void OnItemSelected()
{ {
var prototype = (ConstructionPrototype) RecipeList.Selected.Metadata; var prototype = (ConstructionPrototype) RecipeList.Selected.Metadata;
@@ -162,17 +162,17 @@ namespace Content.Client.Construction
switch (mat.Material) switch (mat.Material)
{ {
case ConstructionStepMaterial.MaterialType.Metal: case ConstructionStepMaterial.MaterialType.Metal:
icon = ResourceCache.GetResource<TextureResource>( icon = _resourceCache.GetResource<TextureResource>(
"/Textures/Objects/Materials/sheet_metal.png"); "/Textures/Objects/Materials/sheet_metal.png");
text = $"Metal x{mat.Amount}"; text = $"Metal x{mat.Amount}";
break; break;
case ConstructionStepMaterial.MaterialType.Glass: case ConstructionStepMaterial.MaterialType.Glass:
icon = ResourceCache.GetResource<TextureResource>( icon = _resourceCache.GetResource<TextureResource>(
"/Textures/Objects/Materials/sheet_glass.png"); "/Textures/Objects/Materials/sheet_glass.png");
text = $"Glass x{mat.Amount}"; text = $"Glass x{mat.Amount}";
break; break;
case ConstructionStepMaterial.MaterialType.Cable: case ConstructionStepMaterial.MaterialType.Cable:
icon = ResourceCache.GetResource<TextureResource>( icon = _resourceCache.GetResource<TextureResource>(
"/Textures/Objects/Tools/cable_coil.png"); "/Textures/Objects/Tools/cable_coil.png");
text = $"Cable Coil x{mat.Amount}"; text = $"Cable Coil x{mat.Amount}";
break; break;
@@ -185,25 +185,25 @@ namespace Content.Client.Construction
switch (tool.ToolQuality) switch (tool.ToolQuality)
{ {
case ToolQuality.Anchoring: case ToolQuality.Anchoring:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/Tools/wrench.png"); icon = _resourceCache.GetResource<TextureResource>("/Textures/Objects/Tools/wrench.png");
text = "Wrench"; text = "Wrench";
break; break;
case ToolQuality.Prying: case ToolQuality.Prying:
icon = ResourceCache.GetResource<TextureResource>("/Textures/Objects/Tools/crowbar.png"); icon = _resourceCache.GetResource<TextureResource>("/Textures/Objects/Tools/crowbar.png");
text = "Crowbar"; text = "Crowbar";
break; break;
case ToolQuality.Screwing: case ToolQuality.Screwing:
icon = ResourceCache.GetResource<TextureResource>( icon = _resourceCache.GetResource<TextureResource>(
"/Textures/Objects/Tools/screwdriver.png"); "/Textures/Objects/Tools/screwdriver.png");
text = "Screwdriver"; text = "Screwdriver";
break; break;
case ToolQuality.Welding: case ToolQuality.Welding:
icon = ResourceCache.GetResource<RSIResource>("/Textures/Objects/tools.rsi") icon = _resourceCache.GetResource<RSIResource>("/Textures/Objects/tools.rsi")
.RSI["welder"].Frame0; .RSI["welder"].Frame0;
text = $"Welding tool ({tool.Amount} fuel)"; text = $"Welding tool ({tool.Amount} fuel)";
break; break;
case ToolQuality.Cutting: case ToolQuality.Cutting:
icon = ResourceCache.GetResource<TextureResource>( icon = _resourceCache.GetResource<TextureResource>(
"/Textures/Objects/Tools/wirecutter.png"); "/Textures/Objects/Tools/wirecutter.png");
text = "Wirecutters"; text = "Wirecutters";
break; break;
@@ -221,13 +221,13 @@ namespace Content.Client.Construction
} }
} }
void OnTextEntered(LineEdit.LineEditEventArgs args) private void OnTextEntered(LineEdit.LineEditEventArgs args)
{ {
var str = args.Text; var str = args.Text;
PopulateTree(string.IsNullOrWhiteSpace(str) ? null : str.ToLowerInvariant()); PopulateTree(string.IsNullOrWhiteSpace(str) ? null : str.ToLowerInvariant());
} }
void OnBuildToggled(BaseButton.ButtonToggledEventArgs args) private void OnBuildToggled(BaseButton.ButtonToggledEventArgs args)
{ {
if (args.Pressed) if (args.Pressed)
{ {
@@ -239,7 +239,8 @@ namespace Content.Client.Construction
if (prototype.Type == ConstructionType.Item) if (prototype.Type == ConstructionType.Item)
{ {
Owner.TryStartItemConstruction(prototype.ID); var constructSystem = _systemManager.GetEntitySystem<ConstructionSystem>();
constructSystem.TryStartItemConstruction(prototype.ID);
BuildButton.Pressed = false; BuildButton.Pressed = false;
return; return;
} }
@@ -250,7 +251,7 @@ namespace Content.Client.Construction
IsTile = false, IsTile = false,
PlacementOption = prototype.PlacementMode PlacementOption = prototype.PlacementMode
}, },
new ConstructionPlacementHijack(prototype, Owner)); new ConstructionPlacementHijack(_systemManager.GetEntitySystem<ConstructionSystem>(), prototype));
} }
else else
{ {
@@ -262,7 +263,7 @@ namespace Content.Client.Construction
private void OnEraseToggled(BaseButton.ButtonToggledEventArgs args) private void OnEraseToggled(BaseButton.ButtonToggledEventArgs args)
{ {
if (args.Pressed) Placement.Clear(); if (args.Pressed) Placement.Clear();
Placement.ToggleEraserHijacked(new ConstructionPlacementHijack(null, Owner)); Placement.ToggleEraserHijacked(new ConstructionPlacementHijack(_systemManager.GetEntitySystem<ConstructionSystem>(), null));
EraseButton.Pressed = args.Pressed; EraseButton.Pressed = args.Pressed;
} }
@@ -272,12 +273,12 @@ namespace Content.Client.Construction
EraseButton.Pressed = false; EraseButton.Pressed = false;
} }
void PopulatePrototypeList() private void PopulatePrototypeList()
{ {
RootCategory = new CategoryNode("", null); RootCategory = new CategoryNode("", null);
int count = 1; var count = 1;
foreach (var prototype in PrototypeManager.EnumeratePrototypes<ConstructionPrototype>()) foreach (var prototype in _prototypeManager.EnumeratePrototypes<ConstructionPrototype>())
{ {
var currentNode = RootCategory; var currentNode = RootCategory;
@@ -316,7 +317,7 @@ namespace Content.Client.Construction
Recurse(RootCategory); Recurse(RootCategory);
} }
void PopulateTree(string searchTerm = null) private void PopulateTree(string searchTerm = null)
{ {
RecipeList.Clear(); RecipeList.Clear();
@@ -378,18 +379,18 @@ namespace Content.Client.Construction
private static int ComparePrototype(ConstructionPrototype x, ConstructionPrototype y) private static int ComparePrototype(ConstructionPrototype x, ConstructionPrototype y)
{ {
return String.Compare(x.Name, y.Name, StringComparison.Ordinal); return string.Compare(x.Name, y.Name, StringComparison.Ordinal);
} }
class CategoryNode private class CategoryNode
{ {
public readonly string Name; public readonly string Name;
public readonly CategoryNode Parent; public readonly CategoryNode Parent;
public SortedDictionary<string, CategoryNode> public readonly SortedDictionary<string, CategoryNode>
ChildCategories = new SortedDictionary<string, CategoryNode>(); ChildCategories = new SortedDictionary<string, CategoryNode>();
public List<ConstructionPrototype> Prototypes = new List<ConstructionPrototype>(); public readonly List<ConstructionPrototype> Prototypes = new List<ConstructionPrototype>();
public int FlattenedIndex = -1; public int FlattenedIndex = -1;
public CategoryNode(string name, CategoryNode parent) public CategoryNode(string name, CategoryNode parent)

View File

@@ -1,49 +1,51 @@
using Content.Client.GameObjects.Components.Construction; using Content.Client.GameObjects.Components.Construction;
using Content.Client.GameObjects.EntitySystems;
using Content.Shared.Construction; using Content.Shared.Construction;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Placement; using Robust.Client.Placement;
using Robust.Client.Utility; using Robust.Client.Utility;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
namespace Content.Client.Construction namespace Content.Client.Construction
{ {
public class ConstructionPlacementHijack : PlacementHijack public class ConstructionPlacementHijack : PlacementHijack
{ {
private readonly ConstructionPrototype Prototype; private readonly ConstructionSystem _constructionSystem;
private readonly ConstructorComponent Owner; private readonly ConstructionPrototype _prototype;
public ConstructionPlacementHijack(ConstructionPrototype prototype, ConstructorComponent owner) public ConstructionPlacementHijack(ConstructionSystem constructionSystem, ConstructionPrototype prototype)
{ {
Prototype = prototype; _constructionSystem = constructionSystem;
Owner = owner; _prototype = prototype;
} }
/// <inheritdoc />
public override bool HijackPlacementRequest(GridCoordinates coords) public override bool HijackPlacementRequest(GridCoordinates coords)
{ {
if (Prototype != null) if (_prototype != null)
{ {
var dir = Manager.Direction; var dir = Manager.Direction;
Owner.SpawnGhost(Prototype, coords, dir); _constructionSystem.SpawnGhost(_prototype, coords, dir);
} }
return true; return true;
} }
/// <inheritdoc />
public override bool HijackDeletion(IEntity entity) public override bool HijackDeletion(IEntity entity)
{ {
if (entity.TryGetComponent(out ConstructionGhostComponent ghost)) if (entity.TryGetComponent(out ConstructionGhostComponent ghost))
{ {
Owner.ClearGhost(ghost.GhostID); _constructionSystem.ClearGhost(ghost.GhostID);
} }
return true; return true;
} }
/// <inheritdoc />
public override void StartHijack(PlacementManager manager) public override void StartHijack(PlacementManager manager)
{ {
base.StartHijack(manager); base.StartHijack(manager);
manager.CurrentBaseSprite = Prototype.Icon.DirFrame0(); manager.CurrentBaseSprite = _prototype.Icon.DirFrame0();
} }
} }
} }

View File

@@ -10,7 +10,6 @@ namespace Content.Client.GameObjects.Components.Construction
public override string Name => "ConstructionGhost"; public override string Name => "ConstructionGhost";
[ViewVariables] public ConstructionPrototype Prototype { get; set; } [ViewVariables] public ConstructionPrototype Prototype { get; set; }
[ViewVariables] public ConstructorComponent Master { get; set; }
[ViewVariables] public int GhostID { get; set; } [ViewVariables] public int GhostID { get; set; }
} }
} }

View File

@@ -1,132 +0,0 @@
using System.Collections.Generic;
using Content.Client.Construction;
using Content.Client.UserInterface;
using Content.Shared.Construction;
using Content.Shared.GameObjects.Components.Construction;
using Robust.Client.GameObjects;
using Robust.Client.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Players;
namespace Content.Client.GameObjects.Components.Construction
{
[RegisterComponent]
public class ConstructorComponent : SharedConstructorComponent
{
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
#pragma warning restore 649
private int nextId;
private readonly Dictionary<int, ConstructionGhostComponent> Ghosts = new Dictionary<int, ConstructionGhostComponent>();
public ConstructionMenu ConstructionMenu { get; private set; }
public override void Initialize()
{
base.Initialize();
Owner.GetComponent<ITransformComponent>();
}
public override void HandleMessage(ComponentMessage message, IComponent component)
{
base.HandleMessage(message, component);
switch (message)
{
case PlayerAttachedMsg _:
if (ConstructionMenu == null)
{
ConstructionMenu = new ConstructionMenu {Owner = this};
ConstructionMenu.OnClose += () => _gameHud.CraftingButtonDown = false;
}
_gameHud.CraftingButtonVisible = true;
_gameHud.CraftingButtonToggled = b =>
{
if (b)
{
ConstructionMenu.Open();
}
else
{
ConstructionMenu.Close();
}
};
break;
case PlayerDetachedMsg _:
_gameHud.CraftingButtonVisible = false;
break;
default:
break;
}
}
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null)
{
base.HandleNetworkMessage(message, channel, session);
switch (message)
{
case AckStructureConstructionMessage ackMsg:
ClearGhost(ackMsg.Ack);
break;
default:
break;
}
}
public override void OnRemove()
{
ConstructionMenu?.Dispose();
}
public void SpawnGhost(ConstructionPrototype prototype, GridCoordinates loc, Direction dir)
{
var entMgr = IoCManager.Resolve<IClientEntityManager>();
var ghost = entMgr.SpawnEntity("constructionghost", loc);
var comp = ghost.GetComponent<ConstructionGhostComponent>();
comp.Prototype = prototype;
comp.Master = this;
comp.GhostID = nextId++;
ghost.GetComponent<ITransformComponent>().LocalRotation = dir.ToAngle();
var sprite = ghost.GetComponent<SpriteComponent>();
sprite.LayerSetSprite(0, prototype.Icon);
sprite.LayerSetVisible(0, true);
Ghosts.Add(comp.GhostID, comp);
}
public void TryStartConstruction(int ghostId)
{
var ghost = Ghosts[ghostId];
var transform = ghost.Owner.GetComponent<ITransformComponent>();
var msg = new TryStartStructureConstructionMessage(transform.GridPosition, ghost.Prototype.ID, transform.LocalRotation, ghostId);
SendNetworkMessage(msg);
}
public void TryStartItemConstruction(string prototypeName)
{
var msg = new TryStartItemConstructionMessage(prototypeName);
SendNetworkMessage(msg);
}
public void ClearGhost(int ghostId)
{
if (Ghosts.TryGetValue(ghostId, out var ghost))
{
ghost.Owner.Delete();
Ghosts.Remove(ghostId);
}
}
}
}

View File

@@ -0,0 +1,196 @@
using System.Collections.Generic;
using Content.Client.Construction;
using Content.Client.GameObjects.Components.Construction;
using Content.Client.UserInterface;
using Content.Shared.Construction;
using Content.Shared.Input;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Player;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.EntitySystems
{
/// <summary>
/// The client-side implementation of the construction system, which is used for constructing entities in game.
/// </summary>
[UsedImplicitly]
public class ConstructionSystem : Shared.GameObjects.EntitySystems.ConstructionSystem
{
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IEntityManager _entityManager;
#pragma warning restore 649
private int _nextId;
private readonly Dictionary<int, ConstructionGhostComponent> _ghosts = new Dictionary<int, ConstructionGhostComponent>();
private ConstructionMenu _constructionMenu;
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PlayerAttachSysMessage>(HandlePlayerAttached);
SubscribeNetworkEvent<AckStructureConstructionMessage>(HandleAckStructure);
CommandBinds.Builder
.Bind(ContentKeyFunctions.OpenCraftingMenu,
new PointerInputCmdHandler(HandleOpenCraftingMenu))
.Bind(EngineKeyFunctions.Use,
new PointerInputCmdHandler(HandleUse))
.Register<ConstructionSystem>();
}
private void HandleAckStructure(AckStructureConstructionMessage msg)
{
ClearGhost(msg.GhostId);
}
private void HandlePlayerAttached(PlayerAttachSysMessage msg)
{
if (msg.AttachedEntity == null)
{
_gameHud.CraftingButtonVisible = false;
return;
}
if (_constructionMenu == null)
{
_constructionMenu = new ConstructionMenu();
_constructionMenu.OnClose += () => _gameHud.CraftingButtonDown = false;
}
_gameHud.CraftingButtonVisible = true;
_gameHud.CraftingButtonToggled = b =>
{
if (b)
{
_constructionMenu.Open();
}
else
{
_constructionMenu.Close();
}
};
}
/// <inheritdoc />
public override void Shutdown()
{
_constructionMenu?.Dispose();
CommandBinds.Unregister<ConstructionSystem>();
base.Shutdown();
}
private bool HandleOpenCraftingMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
{
if (_playerManager.LocalPlayer.ControlledEntity == null)
{
return false;
}
var menu = _constructionMenu;
if (menu.IsOpen)
{
if (menu.IsAtFront())
{
SetOpenValue(menu, false);
}
else
{
menu.MoveToFront();
}
}
else
{
SetOpenValue(menu, true);
}
return true;
}
private bool HandleUse(in PointerInputCmdHandler.PointerInputCmdArgs args)
{
if (!args.EntityUid.IsValid() || !args.EntityUid.IsClientSide())
return false;
var entity = _entityManager.GetEntity(args.EntityUid);
if (!entity.TryGetComponent(out ConstructionGhostComponent ghostComp))
return false;
TryStartConstruction(ghostComp.GhostID);
return true;
}
private void SetOpenValue(ConstructionMenu menu, bool value)
{
if (value)
{
_gameHud.CraftingButtonDown = true;
menu.OpenCentered();
}
else
{
_gameHud.CraftingButtonDown = false;
menu.Close();
}
}
/// <summary>
/// Creates a construction ghost at the given location.
/// </summary>
public void SpawnGhost(ConstructionPrototype prototype, GridCoordinates loc, Direction dir)
{
var ghost = _entityManager.SpawnEntity("constructionghost", loc);
var comp = ghost.GetComponent<ConstructionGhostComponent>();
comp.Prototype = prototype;
comp.GhostID = _nextId++;
ghost.Transform.LocalRotation = dir.ToAngle();
var sprite = ghost.GetComponent<SpriteComponent>();
sprite.LayerSetSprite(0, prototype.Icon);
sprite.LayerSetVisible(0, true);
_ghosts.Add(comp.GhostID, comp);
}
private void TryStartConstruction(int ghostId)
{
var ghost = _ghosts[ghostId];
var transform = ghost.Owner.Transform;
var msg = new TryStartStructureConstructionMessage(transform.GridPosition, ghost.Prototype.ID, transform.LocalRotation, ghostId);
RaiseNetworkEvent(msg);
}
/// <summary>
/// Starts constructing an item underneath the attached entity.
/// </summary>
public void TryStartItemConstruction(string prototypeName)
{
RaiseNetworkEvent(new TryStartItemConstructionMessage(prototypeName));
}
/// <summary>
/// Removes a construction ghost entity with the given ID.
/// </summary>
public void ClearGhost(int ghostId)
{
if (_ghosts.TryGetValue(ghostId, out var ghost))
{
ghost.Owner.Delete();
_ghosts.Remove(ghostId);
}
}
}
}

View File

@@ -1,101 +0,0 @@
using Content.Client.Construction;
using Content.Client.GameObjects.Components.Construction;
using Content.Client.UserInterface;
using Content.Shared.Input;
using Robust.Client.GameObjects.EntitySystems;
using Robust.Client.Player;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Input;
using Robust.Shared.Input.Binding;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.IoC;
namespace Content.Client.GameObjects.EntitySystems
{
public sealed class ConstructorSystem : EntitySystem
{
#pragma warning disable 649
[Dependency] private readonly IGameHud _gameHud;
[Dependency] private readonly IPlayerManager _playerManager;
[Dependency] private readonly IEntityManager _entityManager;
#pragma warning restore 649
public override void Initialize()
{
base.Initialize();
var inputSys = EntitySystemManager.GetEntitySystem<InputSystem>();
CommandBinds.Builder
.Bind(ContentKeyFunctions.OpenCraftingMenu,
new PointerInputCmdHandler(HandleOpenCraftingMenu))
.Bind(EngineKeyFunctions.Use,
new PointerInputCmdHandler(HandleUse))
.Register<ConstructorSystem>();
}
public override void Shutdown()
{
CommandBinds.Unregister<ConstructorSystem>();
base.Shutdown();
}
private bool HandleOpenCraftingMenu(in PointerInputCmdHandler.PointerInputCmdArgs args)
{
if (_playerManager.LocalPlayer.ControlledEntity == null
|| !_playerManager.LocalPlayer.ControlledEntity.TryGetComponent(out ConstructorComponent constructor))
{
return false;
}
var menu = constructor.ConstructionMenu;
if (menu.IsOpen)
{
if (menu.IsAtFront())
{
_setOpenValue(menu, false);
}
else
{
menu.MoveToFront();
}
}
else
{
_setOpenValue(menu, true);
}
return true;
}
private bool HandleUse(in PointerInputCmdHandler.PointerInputCmdArgs args)
{
if (!args.EntityUid.IsValid() || !args.EntityUid.IsClientSide())
return false;
var entity = _entityManager.GetEntity(args.EntityUid);
if (!entity.TryGetComponent(out ConstructionGhostComponent ghostComp))
return false;
ghostComp.Master.TryStartConstruction(ghostComp.GhostID);
return true;
}
private void _setOpenValue(ConstructionMenu menu, bool value)
{
if (value)
{
_gameHud.CraftingButtonDown = true;
menu.OpenCentered();
}
else
{
_gameHud.CraftingButtonDown = false;
menu.Close();
}
}
}
}

View File

@@ -1,26 +1,28 @@
using System; using System;
using Content.Server.GameObjects.Components.Construction;
using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.Components.Stack;
using Content.Server.Utility; using Content.Server.Utility;
using Content.Shared.Construction; using Content.Shared.Construction;
using Content.Shared.GameObjects.Components.Construction; using JetBrains.Annotations;
using Robust.Server.GameObjects.EntitySystems; using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects; using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems; using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Players;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Construction namespace Content.Server.GameObjects.EntitySystems
{ {
[RegisterComponent] /// <summary>
public class ConstructorComponent : SharedConstructorComponent /// The server-side implementation of the construction system, which is used for constructing entities in game.
/// </summary>
[UsedImplicitly]
internal class ConstructionSystem : Shared.GameObjects.EntitySystems.ConstructionSystem
{ {
#pragma warning disable 649 #pragma warning disable 649
[Dependency] private readonly IPrototypeManager _prototypeManager; [Dependency] private readonly IPrototypeManager _prototypeManager;
@@ -28,32 +30,41 @@ namespace Content.Server.GameObjects.Components.Construction
[Dependency] private readonly IServerEntityManager _serverEntityManager; [Dependency] private readonly IServerEntityManager _serverEntityManager;
#pragma warning restore 649 #pragma warning restore 649
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null) /// <inheritdoc />
public override void Initialize()
{ {
base.HandleNetworkMessage(message, channel, session); base.Initialize();
switch (message) SubscribeNetworkEvent<TryStartStructureConstructionMessage>(HandleStartStructureConstruction);
{ SubscribeNetworkEvent<TryStartItemConstructionMessage>(HandleStartItemConstruction);
case TryStartStructureConstructionMessage tryStart:
TryStartStructureConstruction(tryStart.Location, tryStart.PrototypeName, tryStart.Angle, tryStart.Ack);
break;
case TryStartItemConstructionMessage tryStart:
TryStartItemConstruction(tryStart.PrototypeName);
break;
}
} }
void TryStartStructureConstruction(GridCoordinates loc, string prototypeName, Angle angle, int ack) private void HandleStartStructureConstruction(TryStartStructureConstructionMessage msg, EntitySessionEventArgs args)
{
var placingEnt = args.SenderSession.AttachedEntity;
var result = TryStartStructureConstruction(placingEnt, msg.Location, msg.PrototypeName, msg.Angle);
if (!result) return;
var responseMsg = new AckStructureConstructionMessage(msg.Ack);
var channel = ((IPlayerSession) args.SenderSession).ConnectedClient;
RaiseNetworkEvent(responseMsg, channel);
}
private void HandleStartItemConstruction(TryStartItemConstructionMessage msg, EntitySessionEventArgs args)
{
var placingEnt = args.SenderSession.AttachedEntity;
TryStartItemConstruction(placingEnt, msg.PrototypeName);
}
private bool TryStartStructureConstruction(IEntity placingEnt, GridCoordinates loc, string prototypeName, Angle angle)
{ {
var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName); var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName);
if (!InteractionChecks.InRangeUnobstructed(Owner, loc.ToMap(_mapManager), if (!InteractionChecks.InRangeUnobstructed(placingEnt, loc.ToMap(_mapManager),
ignoredEnt: Owner, insideBlockerValid: prototype.CanBuildInImpassable)) ignoredEnt: placingEnt, insideBlockerValid: prototype.CanBuildInImpassable))
{ {
return; return false;
} }
if (prototype.Stages.Count < 2) if (prototype.Stages.Count < 2)
{ {
throw new InvalidOperationException($"Prototype '{prototypeName}' does not have enough stages."); throw new InvalidOperationException($"Prototype '{prototypeName}' does not have enough stages.");
@@ -66,25 +77,25 @@ namespace Content.Server.GameObjects.Components.Construction
} }
// Try to find the stack with the material in the user's hand. // Try to find the stack with the material in the user's hand.
var hands = Owner.GetComponent<HandsComponent>(); var hands = placingEnt.GetComponent<HandsComponent>();
var activeHand = hands.GetActiveHand?.Owner; var activeHand = hands.GetActiveHand?.Owner;
if (activeHand == null) if (activeHand == null)
{ {
return; return false;
} }
if (!activeHand.TryGetComponent(out StackComponent stack) || !ConstructionComponent.MaterialStackValidFor(matStep, stack)) if (!activeHand.TryGetComponent(out StackComponent stack) || !ConstructionComponent.MaterialStackValidFor(matStep, stack))
{ {
return; return false;
} }
if (!stack.Use(matStep.Amount)) if (!stack.Use(matStep.Amount))
{ {
return; return false;
} }
// OK WE'RE GOOD CONSTRUCTION STARTED. // OK WE'RE GOOD CONSTRUCTION STARTED.
EntitySystem.Get<AudioSystem>().PlayAtCoords("/Audio/items/deconstruct.ogg", loc); Get<AudioSystem>().PlayAtCoords("/Audio/items/deconstruct.ogg", loc);
if (prototype.Stages.Count == 2) if (prototype.Stages.Count == 2)
{ {
// Exactly 2 stages, so don't make an intermediate frame. // Exactly 2 stages, so don't make an intermediate frame.
@@ -99,11 +110,10 @@ namespace Content.Server.GameObjects.Components.Construction
frame.Transform.LocalRotation = angle; frame.Transform.LocalRotation = angle;
} }
var msg = new AckStructureConstructionMessage(ack); return true;
SendNetworkMessage(msg);
} }
void TryStartItemConstruction(string prototypeName) private void TryStartItemConstruction(IEntity placingEnt, string prototypeName)
{ {
var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName); var prototype = _prototypeManager.Index<ConstructionPrototype>(prototypeName);
@@ -119,7 +129,7 @@ namespace Content.Server.GameObjects.Components.Construction
} }
// Try to find the stack with the material in the user's hand. // Try to find the stack with the material in the user's hand.
var hands = Owner.GetComponent<HandsComponent>(); var hands = placingEnt.GetComponent<HandsComponent>();
var activeHand = hands.GetActiveHand?.Owner; var activeHand = hands.GetActiveHand?.Owner;
if (activeHand == null) if (activeHand == null)
{ {
@@ -137,17 +147,17 @@ namespace Content.Server.GameObjects.Components.Construction
} }
// OK WE'RE GOOD CONSTRUCTION STARTED. // OK WE'RE GOOD CONSTRUCTION STARTED.
EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/items/deconstruct.ogg", Owner); EntitySystem.Get<AudioSystem>().PlayFromEntity("/Audio/items/deconstruct.ogg", placingEnt);
if (prototype.Stages.Count == 2) if (prototype.Stages.Count == 2)
{ {
// Exactly 2 stages, so don't make an intermediate frame. // Exactly 2 stages, so don't make an intermediate frame.
var ent = _serverEntityManager.SpawnEntity(prototype.Result, Owner.Transform.GridPosition); var ent = _serverEntityManager.SpawnEntity(prototype.Result, placingEnt.Transform.GridPosition);
hands.PutInHandOrDrop(ent.GetComponent<ItemComponent>()); hands.PutInHandOrDrop(ent.GetComponent<ItemComponent>());
} }
else else
{ {
//TODO: Make these viable as an item and try putting them in the players hands //TODO: Make these viable as an item and try putting them in the players hands
var frame = _serverEntityManager.SpawnEntity("structureconstructionframe", Owner.Transform.GridPosition); var frame = _serverEntityManager.SpawnEntity("structureconstructionframe", placingEnt.Transform.GridPosition);
var construction = frame.GetComponent<ConstructionComponent>(); var construction = frame.GetComponent<ConstructionComponent>();
construction.Init(prototype); construction.Init(prototype);
} }

View File

@@ -26,6 +26,7 @@
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="GameObjects\Components\Construction\" />
<Folder Include="GameObjects\Components\Trigger\" /> <Folder Include="GameObjects\Components\Trigger\" />
<EmbeddedResource Include="Text\Names\*.txt" /> <EmbeddedResource Include="Text\Names\*.txt" />
</ItemGroup> </ItemGroup>

View File

@@ -11,7 +11,8 @@
public const uint STORAGE = 1005; public const uint STORAGE = 1005;
public const uint INVENTORY = 1006; public const uint INVENTORY = 1006;
public const uint POWER_DEBUG_TOOL = 1007; public const uint POWER_DEBUG_TOOL = 1007;
public const uint CONSTRUCTOR = 1008; // 1008
// 1009
public const uint RANGED_WEAPON = 1010; public const uint RANGED_WEAPON = 1010;
public const uint CAMERA_RECOIL = 1011; public const uint CAMERA_RECOIL = 1011;
public const uint SOUND = 1012; public const uint SOUND = 1012;

View File

@@ -1,25 +1,22 @@
using System; using System;
using JetBrains.Annotations;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths; using Robust.Shared.Maths;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
namespace Content.Shared.GameObjects.Components.Construction namespace Content.Shared.GameObjects.EntitySystems
{ {
/// <summary> [UsedImplicitly]
/// Basically handles the logic of "this mob can do construction". public class ConstructionSystem : EntitySystem
/// </summary>
public abstract class SharedConstructorComponent : Component
{ {
public override string Name => "Constructor";
public override uint? NetID => ContentNetIDs.CONSTRUCTOR;
/// <summary> /// <summary>
/// Sent client -> server to to tell the server that we started building /// Sent client -> server to to tell the server that we started building
/// a structure-construction. /// a structure-construction.
/// </summary> /// </summary>
[Serializable, NetSerializable] [Serializable, NetSerializable]
protected class TryStartStructureConstructionMessage : ComponentMessage public class TryStartStructureConstructionMessage : EntitySystemMessage
{ {
/// <summary> /// <summary>
/// Position to start building. /// Position to start building.
@@ -40,7 +37,6 @@ namespace Content.Shared.GameObjects.Components.Construction
public TryStartStructureConstructionMessage(GridCoordinates loc, string prototypeName, Angle angle, int ack) public TryStartStructureConstructionMessage(GridCoordinates loc, string prototypeName, Angle angle, int ack)
{ {
Directed = true;
Location = loc; Location = loc;
PrototypeName = prototypeName; PrototypeName = prototypeName;
Angle = angle; Angle = angle;
@@ -53,7 +49,7 @@ namespace Content.Shared.GameObjects.Components.Construction
/// an item-construction. /// an item-construction.
/// </summary> /// </summary>
[Serializable, NetSerializable] [Serializable, NetSerializable]
protected class TryStartItemConstructionMessage : ComponentMessage public class TryStartItemConstructionMessage : EntitySystemMessage
{ {
/// <summary> /// <summary>
/// The construction prototype to start building. /// The construction prototype to start building.
@@ -62,21 +58,21 @@ namespace Content.Shared.GameObjects.Components.Construction
public TryStartItemConstructionMessage(string prototypeName) public TryStartItemConstructionMessage(string prototypeName)
{ {
Directed = true;
PrototypeName = prototypeName; PrototypeName = prototypeName;
} }
} }
/// <summary>
/// Send server -> client to tell the client that a ghost has started to be constructed.
/// </summary>
[Serializable, NetSerializable] [Serializable, NetSerializable]
protected class AckStructureConstructionMessage : ComponentMessage public class AckStructureConstructionMessage : EntitySystemMessage
{ {
public readonly int Ack; public readonly int GhostId;
public AckStructureConstructionMessage(int ack) public AckStructureConstructionMessage(int ghostId)
{ {
Directed = true; GhostId = ghostId;
Ack = ack;
} }
} }
} }

View File

@@ -24,7 +24,6 @@
max_volume: 250 max_volume: 250
digestionDelay: 20 digestionDelay: 20
- type: Inventory - type: Inventory
- type: Constructor
- type: Clickable - type: Clickable
- type: InteractionOutline - type: InteractionOutline
- type: Sprite - type: Sprite