Adds Research, unlockable technologies, Protolathes... (#264)
* Work on Research so far More work on UI... Fix ResearchClient and Protolathe UI stuff. Fix infinite select -> request state -> select -> ... loop Add UI to ResearchClient, etc. Technology Database states, and a bit of work on the research console ui A bit of work on Research Console UI Protolathe sync Stuff that actually does things Protolathe databases yay Alright got my motivation back Yeah, no. It's almost 3 AM already Fix serialization bug again More work on stuff Stuff Adds files for most new components/systems. * Protolathes actually work now * Research. Just Research. * Adds icons from Eris. * Address reviews * Change LatheMenu resize behaviour * Update Content.Client/GameObjects/Components/Research/ResearchConsoleBoundUserInterface.cs Co-Authored-By: Pieter-Jan Briers <pieterjan.briers@gmail.com> * Update Content.Client/Research/ResearchConsoleMenu.cs Co-Authored-By: Pieter-Jan Briers <pieterjan.briers@gmail.com> * Move IoC Resolve out of for loop * Address review * Localize stuff
@@ -100,6 +100,9 @@ namespace Content.Client
|
||||
"PlayerInputMover",
|
||||
"Computer",
|
||||
"AsteroidRock",
|
||||
"ResearchServer",
|
||||
"ResearchPointSource",
|
||||
"ResearchClient",
|
||||
"IdCard",
|
||||
"Access",
|
||||
"AccessReader",
|
||||
@@ -110,8 +113,10 @@ namespace Content.Client
|
||||
factory.RegisterIgnore(ignoreName);
|
||||
}
|
||||
|
||||
factory.Register<SharedResearchConsoleComponent>();
|
||||
factory.Register<SharedLatheComponent>();
|
||||
factory.Register<SharedSpawnPointComponent>();
|
||||
|
||||
factory.Register<SolutionComponent>();
|
||||
|
||||
factory.Register<SharedVendingMachineComponent>();
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Content.Client.GameObjects.Components.Research
|
||||
|
||||
public MaterialStorageComponent Storage { get; private set; }
|
||||
public SharedLatheComponent Lathe { get; private set; }
|
||||
public LatheDatabaseComponent Database { get; private set; }
|
||||
public SharedLatheDatabaseComponent Database { get; private set; }
|
||||
|
||||
[ViewVariables]
|
||||
public Queue<LatheRecipePrototype> QueuedRecipes => _queuedRecipes;
|
||||
@@ -41,13 +41,15 @@ namespace Content.Client.GameObjects.Components.Research
|
||||
|
||||
if (!Owner.Owner.TryGetComponent(out MaterialStorageComponent storage)
|
||||
|| !Owner.Owner.TryGetComponent(out SharedLatheComponent lathe)
|
||||
|| !Owner.Owner.TryGetComponent(out LatheDatabaseComponent database)) return;
|
||||
|| !Owner.Owner.TryGetComponent(out SharedLatheDatabaseComponent database)) return;
|
||||
|
||||
|
||||
|
||||
Storage = storage;
|
||||
Lathe = lathe;
|
||||
Database = database;
|
||||
|
||||
menu = new LatheMenu {Owner = this};
|
||||
menu = new LatheMenu(this);
|
||||
queueMenu = new LatheQueueMenu { Owner = this };
|
||||
|
||||
menu.OnClose += Close;
|
||||
@@ -57,6 +59,16 @@ namespace Content.Client.GameObjects.Components.Research
|
||||
|
||||
menu.QueueButton.OnPressed += (args) => { queueMenu.OpenCentered(); };
|
||||
|
||||
menu.ServerConnectButton.OnPressed += (args) =>
|
||||
{
|
||||
SendMessage(new SharedLatheComponent.LatheServerSelectionMessage());
|
||||
};
|
||||
|
||||
menu.ServerSyncButton.OnPressed += (args) =>
|
||||
{
|
||||
SendMessage(new SharedLatheComponent.LatheServerSyncMessage());
|
||||
};
|
||||
|
||||
storage.OnMaterialStorageChanged += menu.PopulateDisabled;
|
||||
storage.OnMaterialStorageChanged += menu.PopulateMaterials;
|
||||
|
||||
@@ -74,10 +86,10 @@ namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
case SharedLatheComponent.LatheProducingRecipeMessage msg:
|
||||
if (!_prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype recipe)) break;
|
||||
queueMenu.SetInfo(recipe);
|
||||
queueMenu?.SetInfo(recipe);
|
||||
break;
|
||||
case SharedLatheComponent.LatheStoppedProducingRecipeMessage msg:
|
||||
queueMenu.ClearInfo();
|
||||
queueMenu?.ClearInfo();
|
||||
break;
|
||||
case SharedLatheComponent.LatheFullQueueMessage msg:
|
||||
_queuedRecipes.Clear();
|
||||
@@ -86,7 +98,7 @@ namespace Content.Client.GameObjects.Components.Research
|
||||
if (!_prototypeManager.TryIndex(id, out LatheRecipePrototype recipePrototype)) break;
|
||||
_queuedRecipes.Enqueue(recipePrototype);
|
||||
}
|
||||
queueMenu.PopulateList();
|
||||
queueMenu?.PopulateList();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedLatheDatabaseComponent))]
|
||||
public class ProtolatheDatabaseComponent : SharedProtolatheDatabaseComponent
|
||||
{
|
||||
#pragma warning disable CS0649
|
||||
[Dependency]
|
||||
private IPrototypeManager _prototypeManager;
|
||||
#pragma warning restore
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the database gets updated.
|
||||
/// </summary>
|
||||
public event Action OnDatabaseUpdated;
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
if (!(curState is ProtolatheDatabaseState state)) return;
|
||||
Clear();
|
||||
foreach (var ID in state.Recipes)
|
||||
{
|
||||
if(!_prototypeManager.TryIndex(ID, out LatheRecipePrototype recipe)) continue;
|
||||
AddRecipe(recipe);
|
||||
}
|
||||
|
||||
OnDatabaseUpdated?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
public class ResearchClientBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
private ResearchClientServerSelectionMenu _menu;
|
||||
|
||||
public ResearchClientBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
SendMessage(new SharedResearchClientComponent.ResearchClientSyncMessage());
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
_menu = new ResearchClientServerSelectionMenu() { Owner = this };
|
||||
|
||||
_menu.OnClose += Close;
|
||||
|
||||
_menu.OpenCentered();
|
||||
}
|
||||
|
||||
public void SelectServer(int serverId)
|
||||
{
|
||||
SendMessage(new SharedResearchClientComponent.ResearchClientServerSelectedMessage(serverId));
|
||||
}
|
||||
|
||||
public void DeselectServer()
|
||||
{
|
||||
SendMessage(new SharedResearchClientComponent.ResearchClientServerDeselectedMessage());
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
if (!(state is SharedResearchClientComponent.ResearchClientBoundInterfaceState rstate)) return;
|
||||
_menu.Populate(rstate.ServerCount, rstate.ServerNames, rstate.ServerIds, rstate.SelectedServerId);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
public class ResearchClientServerSelectionMenu : SS14Window
|
||||
{
|
||||
private ItemList _servers;
|
||||
private int _serverCount = 0;
|
||||
private string[] _serverNames = new string[]{};
|
||||
private int[] _serverIds = new int[]{};
|
||||
private int _selectedServerId = -1;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
protected override Vector2? CustomSize => (300, 300);
|
||||
public ResearchClientBoundUserInterface Owner { get; set; }
|
||||
|
||||
public ResearchClientServerSelectionMenu()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Title = _localizationManager.GetString("Research Server Selection");
|
||||
|
||||
_servers = new ItemList() {SelectMode = ItemList.ItemListSelectMode.Single};
|
||||
|
||||
_servers.OnItemSelected += OnItemSelected;
|
||||
_servers.OnItemDeselected += OnItemDeselected;
|
||||
|
||||
var margin = new MarginContainer()
|
||||
{
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
MarginTop = 5f,
|
||||
MarginLeft = 5f,
|
||||
MarginRight = -5f,
|
||||
MarginBottom = -5f,
|
||||
};
|
||||
|
||||
margin.AddChild(_servers);
|
||||
|
||||
Contents.AddChild(margin);
|
||||
}
|
||||
|
||||
public void OnItemSelected(ItemList.ItemListSelectedEventArgs itemListSelectedEventArgs)
|
||||
{
|
||||
Owner.SelectServer(_serverIds[itemListSelectedEventArgs.ItemIndex]);
|
||||
}
|
||||
|
||||
public void OnItemDeselected(ItemList.ItemListDeselectedEventArgs itemListDeselectedEventArgs)
|
||||
{
|
||||
Owner.DeselectServer();
|
||||
}
|
||||
|
||||
public void Populate(int serverCount, string[] serverNames, int[] serverIds, int selectedServerId)
|
||||
{
|
||||
_serverCount = serverCount;
|
||||
_serverNames = serverNames;
|
||||
_serverIds = serverIds;
|
||||
_selectedServerId = selectedServerId;
|
||||
|
||||
// Disable so we can select the new selected server without triggering a new sync request.
|
||||
_servers.OnItemSelected -= OnItemSelected;
|
||||
_servers.OnItemDeselected -= OnItemDeselected;
|
||||
|
||||
_servers.Clear();
|
||||
for (var i = 0; i < _serverCount; i++)
|
||||
{
|
||||
var id = _serverIds[i];
|
||||
_servers.AddItem($"ID: {id} || {_serverNames[i]}");
|
||||
if(id == _selectedServerId)
|
||||
_servers.Select(i);
|
||||
}
|
||||
|
||||
_servers.OnItemSelected += OnItemSelected;
|
||||
_servers.OnItemDeselected += OnItemDeselected;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
using Content.Client.Research;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Client.GameObjects.Components.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Log;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
public class ResearchConsoleBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
public int Points { get; private set; } = 0;
|
||||
public int PointsPerSecond { get; private set; } = 0;
|
||||
private ResearchConsoleMenu _consoleMenu;
|
||||
private TechnologyDatabaseComponent TechnologyDatabase;
|
||||
|
||||
|
||||
public ResearchConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
||||
{
|
||||
SendMessage(new SharedResearchConsoleComponent.ConsoleServerSyncMessage());
|
||||
}
|
||||
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
|
||||
if (!Owner.Owner.TryGetComponent(out TechnologyDatabase)) return;
|
||||
|
||||
_consoleMenu = new ResearchConsoleMenu(this);
|
||||
|
||||
_consoleMenu.ServerSyncButton.OnPressed += (args) =>
|
||||
{
|
||||
SendMessage(new SharedResearchConsoleComponent.ConsoleServerSyncMessage());
|
||||
};
|
||||
|
||||
_consoleMenu.ServerSelectionButton.OnPressed += (args) =>
|
||||
{
|
||||
SendMessage(new SharedResearchConsoleComponent.ConsoleServerSelectionMessage());
|
||||
};
|
||||
|
||||
_consoleMenu.UnlockButton.OnPressed += (args) =>
|
||||
{
|
||||
SendMessage(new SharedResearchConsoleComponent.ConsoleUnlockTechnologyMessage(_consoleMenu.TechnologySelected.ID));
|
||||
};
|
||||
|
||||
_consoleMenu.OpenCentered();
|
||||
|
||||
TechnologyDatabase.OnDatabaseUpdated += _consoleMenu.Populate;
|
||||
}
|
||||
|
||||
public bool IsTechnologyUnlocked(TechnologyPrototype technology)
|
||||
{
|
||||
return TechnologyDatabase.IsTechnologyUnlocked(technology);
|
||||
}
|
||||
|
||||
public bool CanUnlockTechnology(TechnologyPrototype technology)
|
||||
{
|
||||
return TechnologyDatabase.CanUnlockTechnology(technology);
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
var castState = (SharedResearchConsoleComponent.ResearchConsoleBoundInterfaceState)state;
|
||||
Points = castState.Points;
|
||||
PointsPerSecond = castState.PointsPerSecond;
|
||||
// We update the user interface here.
|
||||
_consoleMenu?.PopulatePoints();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing) return;
|
||||
_consoleMenu?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using SixLabors.ImageSharp.Processing;
|
||||
|
||||
namespace Content.Client.GameObjects.Components.Research
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class TechnologyDatabaseComponent : SharedTechnologyDatabaseComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Event called when the database is updated.
|
||||
/// </summary>
|
||||
public event Action OnDatabaseUpdated;
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
if (!(curState is TechnologyDatabaseState state)) return;
|
||||
_technologies.Clear();
|
||||
var protoManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
foreach (var techID in state.Technologies)
|
||||
{
|
||||
if (!protoManager.TryIndex(techID, out TechnologyPrototype technology)) continue;
|
||||
_technologies.Add(technology);
|
||||
}
|
||||
|
||||
OnDatabaseUpdated?.Invoke();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.GameObjects.Components.Research;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Materials;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Client.UserInterface;
|
||||
@@ -25,6 +26,8 @@ namespace Content.Client.Research
|
||||
private LineEdit AmountLineEdit;
|
||||
private LineEdit SearchBar;
|
||||
public Button QueueButton;
|
||||
public Button ServerConnectButton;
|
||||
public Button ServerSyncButton;
|
||||
protected override Vector2? CustomSize => (300, 450);
|
||||
|
||||
public LatheBoundUserInterface Owner { get; set; }
|
||||
@@ -32,10 +35,12 @@ namespace Content.Client.Research
|
||||
private List<LatheRecipePrototype> _recipes = new List<LatheRecipePrototype>();
|
||||
private List<LatheRecipePrototype> _shownRecipes = new List<LatheRecipePrototype>();
|
||||
|
||||
public LatheMenu()
|
||||
public LatheMenu(LatheBoundUserInterface owner = null)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Owner = owner;
|
||||
|
||||
Title = "Lathe Menu";
|
||||
|
||||
var margin = new MarginContainer()
|
||||
@@ -69,7 +74,23 @@ namespace Content.Client.Research
|
||||
{
|
||||
Text = "Queue",
|
||||
TextAlign = Button.AlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsHorizontal = SizeFlags.Fill,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
};
|
||||
|
||||
ServerConnectButton = new Button()
|
||||
{
|
||||
Text = "Server list",
|
||||
TextAlign = Button.AlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.Fill,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
};
|
||||
|
||||
ServerSyncButton = new Button()
|
||||
{
|
||||
Text = "Sync",
|
||||
TextAlign = Button.AlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.Fill,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
};
|
||||
|
||||
@@ -101,7 +122,7 @@ namespace Content.Client.Research
|
||||
{
|
||||
Text = "Filter",
|
||||
TextAlign = Button.AlignMode.Center,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsHorizontal = SizeFlags.Fill,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
Disabled = true,
|
||||
};
|
||||
@@ -130,6 +151,12 @@ namespace Content.Client.Research
|
||||
};
|
||||
|
||||
hBoxButtons.AddChild(spacer);
|
||||
if (Owner?.Database is ProtolatheDatabaseComponent database)
|
||||
{
|
||||
hBoxButtons.AddChild(ServerConnectButton);
|
||||
hBoxButtons.AddChild(ServerSyncButton);
|
||||
database.OnDatabaseUpdated += Populate;
|
||||
}
|
||||
hBoxButtons.AddChild(QueueButton);
|
||||
|
||||
hBoxFilter.AddChild(SearchBar);
|
||||
|
||||
316
Content.Client/Research/ResearchConsoleMenu.cs
Normal file
@@ -0,0 +1,316 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Client.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Research
|
||||
{
|
||||
public class ResearchConsoleMenu : SS14Window
|
||||
{
|
||||
public ResearchConsoleBoundUserInterface Owner { get; set; }
|
||||
|
||||
protected override Vector2? CustomSize => (800, 400);
|
||||
|
||||
private List<TechnologyPrototype> _unlockedTechnologyPrototypes = new List<TechnologyPrototype>();
|
||||
private List<TechnologyPrototype> _unlockableTechnologyPrototypes = new List<TechnologyPrototype>();
|
||||
private List<TechnologyPrototype> _futureTechnologyPrototypes = new List<TechnologyPrototype>();
|
||||
|
||||
private Label _pointLabel;
|
||||
private Label _pointsPerSecondLabel;
|
||||
private Label _technologyName;
|
||||
private Label _technologyDescription;
|
||||
private Label _technologyRequirements;
|
||||
private TextureRect _technologyIcon;
|
||||
private ItemList _unlockedTechnologies;
|
||||
private ItemList _unlockableTechnologies;
|
||||
private ItemList _futureTechnologies;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly ILocalizationManager _localizationManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public Button UnlockButton { get; private set; }
|
||||
public Button ServerSelectionButton { get; private set; }
|
||||
public Button ServerSyncButton { get; private set; }
|
||||
|
||||
public TechnologyPrototype TechnologySelected;
|
||||
|
||||
public ResearchConsoleMenu(ResearchConsoleBoundUserInterface owner = null)
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
Title = _localizationManager.GetString("R&D Console");
|
||||
|
||||
Owner = owner;
|
||||
|
||||
_unlockedTechnologies = new ItemList()
|
||||
{
|
||||
SelectMode = ItemList.ItemListSelectMode.Single,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
};
|
||||
|
||||
_unlockedTechnologies.OnItemSelected += UnlockedTechnologySelected;
|
||||
|
||||
_unlockableTechnologies = new ItemList()
|
||||
{
|
||||
SelectMode = ItemList.ItemListSelectMode.Single,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
};
|
||||
|
||||
_unlockableTechnologies.OnItemSelected += UnlockableTechnologySelected;
|
||||
|
||||
_futureTechnologies = new ItemList()
|
||||
{
|
||||
SelectMode = ItemList.ItemListSelectMode.Single,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
};
|
||||
|
||||
_futureTechnologies.OnItemSelected += FutureTechnologySelected;
|
||||
|
||||
var vbox = new VBoxContainer()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
};
|
||||
|
||||
var hboxTechnologies = new HBoxContainer()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 2,
|
||||
SeparationOverride = 10,
|
||||
};
|
||||
|
||||
var hboxSelected = new HBoxContainer()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1
|
||||
};
|
||||
|
||||
var vboxPoints = new VBoxContainer()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
};
|
||||
|
||||
var vboxTechInfo = new VBoxContainer()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 3,
|
||||
};
|
||||
|
||||
_pointLabel = new Label() { Text = _localizationManager.GetString("Research Points") + ": 0" };
|
||||
_pointsPerSecondLabel = new Label() { Text = _localizationManager.GetString("Points per Second") + ": 0" };
|
||||
|
||||
var vboxPointsButtons = new VBoxContainer()
|
||||
{
|
||||
Align = BoxContainer.AlignMode.End,
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
};
|
||||
|
||||
ServerSelectionButton = new Button() { Text = _localizationManager.GetString("Server list") };
|
||||
ServerSyncButton = new Button() { Text = _localizationManager.GetString("Sync")};
|
||||
UnlockButton = new Button() { Text = _localizationManager.GetString("Unlock"), Disabled = true };
|
||||
|
||||
|
||||
vboxPointsButtons.AddChild(ServerSelectionButton);
|
||||
vboxPointsButtons.AddChild(ServerSyncButton);
|
||||
vboxPointsButtons.AddChild(UnlockButton);
|
||||
|
||||
vboxPoints.AddChild(_pointLabel);
|
||||
vboxPoints.AddChild(_pointsPerSecondLabel);
|
||||
vboxPoints.AddChild(vboxPointsButtons);
|
||||
|
||||
_technologyIcon = new TextureRect()
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
SizeFlagsVertical = SizeFlags.FillExpand,
|
||||
SizeFlagsStretchRatio = 1,
|
||||
Stretch = TextureRect.StretchMode.KeepAspectCentered,
|
||||
};
|
||||
_technologyName = new Label();
|
||||
_technologyDescription = new Label();
|
||||
_technologyRequirements = new Label();
|
||||
|
||||
vboxTechInfo.AddChild(_technologyName);
|
||||
vboxTechInfo.AddChild(_technologyDescription);
|
||||
vboxTechInfo.AddChild(_technologyRequirements);
|
||||
|
||||
hboxSelected.AddChild(_technologyIcon);
|
||||
hboxSelected.AddChild(vboxTechInfo);
|
||||
hboxSelected.AddChild(vboxPoints);
|
||||
|
||||
hboxTechnologies.AddChild(_unlockedTechnologies);
|
||||
hboxTechnologies.AddChild(_unlockableTechnologies);
|
||||
hboxTechnologies.AddChild(_futureTechnologies);
|
||||
|
||||
vbox.AddChild(hboxTechnologies);
|
||||
vbox.AddChild(hboxSelected);
|
||||
|
||||
Contents.AddChild(vbox);
|
||||
|
||||
UnlockButton.OnPressed += (args) =>
|
||||
{
|
||||
CleanSelectedTechnology();
|
||||
};
|
||||
|
||||
Populate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleans the selected technology controls to blank.
|
||||
/// </summary>
|
||||
private void CleanSelectedTechnology()
|
||||
{
|
||||
UnlockButton.Disabled = true;
|
||||
_technologyIcon.Texture = Texture.Transparent;
|
||||
_technologyName.Text = "";
|
||||
_technologyDescription.Text = "";
|
||||
_technologyRequirements.Text = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when an unlocked technology is selected.
|
||||
/// </summary>
|
||||
private void UnlockedTechnologySelected(ItemList.ItemListSelectedEventArgs obj)
|
||||
{
|
||||
TechnologySelected = _unlockedTechnologyPrototypes[obj.ItemIndex];
|
||||
|
||||
_unlockedTechnologies.Unselect(obj.ItemIndex);
|
||||
|
||||
UnlockButton.Disabled = true;
|
||||
|
||||
PopulateSelectedTechnology();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when an unlockable technology is selected.
|
||||
/// </summary>
|
||||
private void UnlockableTechnologySelected(ItemList.ItemListSelectedEventArgs obj)
|
||||
{
|
||||
TechnologySelected = _unlockableTechnologyPrototypes[obj.ItemIndex];
|
||||
|
||||
_unlockableTechnologies.Unselect(obj.ItemIndex);
|
||||
|
||||
UnlockButton.Disabled = Owner.Points < TechnologySelected.RequiredPoints;
|
||||
|
||||
PopulateSelectedTechnology();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when a future technology is selected
|
||||
/// </summary>
|
||||
private void FutureTechnologySelected(ItemList.ItemListSelectedEventArgs obj)
|
||||
{
|
||||
TechnologySelected = _futureTechnologyPrototypes[obj.ItemIndex];
|
||||
|
||||
_futureTechnologies.Unselect(obj.ItemIndex);
|
||||
|
||||
UnlockButton.Disabled = true;
|
||||
|
||||
PopulateSelectedTechnology();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populate all technologies in the ItemLists.
|
||||
/// </summary>
|
||||
public void PopulateItemLists()
|
||||
{
|
||||
_unlockedTechnologies.Clear();
|
||||
_unlockableTechnologies.Clear();
|
||||
_futureTechnologies.Clear();
|
||||
|
||||
_unlockedTechnologyPrototypes.Clear();
|
||||
_unlockableTechnologyPrototypes.Clear();
|
||||
_futureTechnologyPrototypes.Clear();
|
||||
|
||||
var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
|
||||
|
||||
// For now, we retrieve all technologies. In the future, this should be changed.
|
||||
foreach (var tech in prototypeMan.EnumeratePrototypes<TechnologyPrototype>())
|
||||
{
|
||||
if (Owner.IsTechnologyUnlocked(tech))
|
||||
{
|
||||
_unlockedTechnologies.AddItem(tech.Name, tech.Icon.Frame0());
|
||||
_unlockedTechnologyPrototypes.Add(tech);
|
||||
}
|
||||
else if (Owner.CanUnlockTechnology(tech))
|
||||
{
|
||||
_unlockableTechnologies.AddItem(tech.Name, tech.Icon.Frame0());
|
||||
_unlockableTechnologyPrototypes.Add(tech);
|
||||
}
|
||||
else
|
||||
{
|
||||
_futureTechnologies.AddItem(tech.Name, tech.Icon.Frame0());;
|
||||
_futureTechnologyPrototypes.Add(tech);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills the selected technology controls with details.
|
||||
/// </summary>
|
||||
public void PopulateSelectedTechnology()
|
||||
{
|
||||
if (TechnologySelected == null)
|
||||
{
|
||||
_technologyName.Text = "";
|
||||
_technologyDescription.Text = "";
|
||||
_technologyRequirements.Text = "";
|
||||
return;
|
||||
}
|
||||
|
||||
_technologyIcon.Texture = TechnologySelected.Icon.Frame0();
|
||||
_technologyName.Text = TechnologySelected.Name;
|
||||
_technologyDescription.Text = TechnologySelected.Description+$"\n{TechnologySelected.RequiredPoints} " + _localizationManager.GetString("research points");
|
||||
_technologyRequirements.Text = _localizationManager.GetString("No technology requirements.");
|
||||
|
||||
var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
|
||||
|
||||
for (var i = 0; i < TechnologySelected.RequiredTechnologies.Count; i++)
|
||||
{
|
||||
var requiredId = TechnologySelected.RequiredTechnologies[i];
|
||||
if (!prototypeMan.TryIndex(requiredId, out TechnologyPrototype prototype)) continue;
|
||||
if (i == 0)
|
||||
_technologyRequirements.Text = _localizationManager.GetString("Requires") + $": {prototype.Name}";
|
||||
else
|
||||
_technologyRequirements.Text += $", {prototype.Name}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the research point labels.
|
||||
/// </summary>
|
||||
public void PopulatePoints()
|
||||
{
|
||||
_pointLabel.Text = _localizationManager.GetString("Research Points") + $": {Owner.Points}";
|
||||
_pointsPerSecondLabel.Text = _localizationManager.GetString("Points per second") + $": {Owner.PointsPerSecond}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the whole user interface.
|
||||
/// </summary>
|
||||
public void Populate()
|
||||
{
|
||||
PopulatePoints();
|
||||
PopulateSelectedTechnology();
|
||||
PopulateItemLists();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ namespace Content.Server
|
||||
"SubFloorHide",
|
||||
"LowWall",
|
||||
"Window",
|
||||
"CharacterInfo"
|
||||
"CharacterInfo",
|
||||
};
|
||||
|
||||
foreach (var ignoreName in registerIgnore)
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Timers;
|
||||
@@ -37,10 +38,9 @@ namespace Content.Server.GameObjects.Components.Research
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg)
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
{
|
||||
var message = serverMsg.Message;
|
||||
switch (message)
|
||||
switch (message.Message)
|
||||
{
|
||||
case LatheQueueRecipeMessage msg:
|
||||
_prototypeManager.TryIndex(msg.ID, out LatheRecipePrototype recipe);
|
||||
@@ -57,6 +57,20 @@ namespace Content.Server.GameObjects.Components.Research
|
||||
if (_producingRecipe != null)
|
||||
_userInterface.SendMessage(new LatheProducingRecipeMessage(_producingRecipe.ID));
|
||||
break;
|
||||
|
||||
case LatheServerSelectionMessage msg:
|
||||
if (!Owner.TryGetComponent(out ResearchClientComponent researchClient)) return;
|
||||
researchClient.OpenUserInterface(message.Session);
|
||||
break;
|
||||
|
||||
case LatheServerSyncMessage msg:
|
||||
if (!Owner.TryGetComponent(out TechnologyDatabaseComponent database)
|
||||
|| !Owner.TryGetComponent(out ProtolatheDatabaseComponent protoDatabase)) return;
|
||||
|
||||
if(database.SyncWithServer())
|
||||
protoDatabase.Sync();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,12 +102,17 @@ namespace Content.Server.GameObjects.Components.Research
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OpenUserInterface(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Open(session);
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
return;
|
||||
|
||||
_userInterface.Open(actor.playerSession);
|
||||
OpenUserInterface(actor.playerSession);
|
||||
return;
|
||||
}
|
||||
bool IAttackBy.AttackBy(AttackByEventArgs eventArgs)
|
||||
|
||||
@@ -30,12 +30,14 @@ namespace Content.Server.GameObjects.Components.Research
|
||||
public override void Clear()
|
||||
{
|
||||
if (Static) return;
|
||||
base.Clear();
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public override void AddRecipe(LatheRecipePrototype recipe)
|
||||
{
|
||||
if (Static) return;
|
||||
base.AddRecipe(recipe);
|
||||
Dirty();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Research
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedLatheDatabaseComponent))]
|
||||
public class ProtolatheDatabaseComponent : SharedProtolatheDatabaseComponent
|
||||
{
|
||||
public override string Name => "ProtolatheDatabase";
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new ProtolatheDatabaseState(GetRecipeIdList());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds unlocked recipes from technologies to the database.
|
||||
/// </summary>
|
||||
public void Sync()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out TechnologyDatabaseComponent database)) return;
|
||||
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
|
||||
foreach (var technology in database.Technologies)
|
||||
{
|
||||
foreach (var id in technology.UnlockedRecipes)
|
||||
{
|
||||
var recipe = (LatheRecipePrototype)prototypeManager.Index(typeof(LatheRecipePrototype), id);
|
||||
UnlockRecipe(recipe);
|
||||
}
|
||||
}
|
||||
|
||||
Dirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks a recipe but only if it's one of the allowed recipes on this protolathe.
|
||||
/// </summary>
|
||||
/// <param name="recipe">The recipe</param>
|
||||
/// <returns>Whether it could add it or not.</returns>
|
||||
public bool UnlockRecipe(LatheRecipePrototype recipe)
|
||||
{
|
||||
if (!ProtolatheRecipes.Contains(recipe)) return false;
|
||||
|
||||
AddRecipe(recipe);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects.Systems;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Research
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ResearchClientComponent : SharedResearchClientComponent, IActivate
|
||||
{
|
||||
// TODO: Create GUI for changing RD server.
|
||||
|
||||
private BoundUserInterface _userInterface;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public bool ConnectedToServer => Server != null;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public ResearchServerComponent Server { get; set; }
|
||||
|
||||
public bool RegisterServer(ResearchServerComponent server)
|
||||
{
|
||||
var result = server != null && server.RegisterClient(this);
|
||||
return result;
|
||||
}
|
||||
|
||||
public void UnregisterFromServer()
|
||||
{
|
||||
Server?.UnregisterClient(this);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
// For now it just registers on the first server it can find.
|
||||
var servers = _entitySystemManager.GetEntitySystem<ResearchSystem>().Servers;
|
||||
if(servers.Count > 0)
|
||||
RegisterServer(servers[0]);
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>().GetBoundUserInterface(ResearchClientUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
}
|
||||
|
||||
public void OpenUserInterface(IPlayerSession session)
|
||||
{
|
||||
UpdateUserInterface();
|
||||
_userInterface.Open(session);
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
return;
|
||||
|
||||
OpenUserInterface(actor.playerSession);
|
||||
return;
|
||||
}
|
||||
|
||||
public void UpdateUserInterface()
|
||||
{
|
||||
_userInterface?.SetState(GetNewUiState());
|
||||
}
|
||||
|
||||
private ResearchClientBoundInterfaceState GetNewUiState()
|
||||
{
|
||||
var rd = _entitySystemManager.GetEntitySystem<ResearchSystem>();
|
||||
|
||||
return new ResearchClientBoundInterfaceState(rd.Servers.Count, rd.GetServerNames(),
|
||||
rd.GetServerIds(), ConnectedToServer ? Server.Id : -1);
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage msg)
|
||||
{
|
||||
switch (msg.Message)
|
||||
{
|
||||
case ResearchClientSyncMessage _:
|
||||
UpdateUserInterface();
|
||||
break;
|
||||
|
||||
case ResearchClientServerSelectedMessage selectedMessage:
|
||||
UnregisterFromServer();
|
||||
RegisterServer(_entitySystemManager.GetEntitySystem<ResearchSystem>().GetServerById(selectedMessage.ServerId));
|
||||
UpdateUserInterface();
|
||||
break;
|
||||
|
||||
case ResearchClientServerDeselectedMessage _:
|
||||
UnregisterFromServer();
|
||||
UpdateUserInterface();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
UnregisterFromServer();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Server.GameObjects.Components.UserInterface;
|
||||
using Robust.Server.Interfaces.GameObjects;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Research
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class ResearchConsoleComponent : SharedResearchConsoleComponent, IActivate
|
||||
{
|
||||
private BoundUserInterface _userInterface;
|
||||
private ResearchClientComponent _client;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_userInterface = Owner.GetComponent<ServerUserInterfaceComponent>().GetBoundUserInterface(ResearchConsoleUiKey.Key);
|
||||
_userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage;
|
||||
_client = Owner.GetComponent<ResearchClientComponent>();
|
||||
}
|
||||
|
||||
private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out TechnologyDatabaseComponent database)) return;
|
||||
|
||||
switch (message.Message)
|
||||
{
|
||||
case ConsoleUnlockTechnologyMessage msg:
|
||||
var protoMan = IoCManager.Resolve<IPrototypeManager>();
|
||||
if (!protoMan.TryIndex(msg.Id, out TechnologyPrototype tech)) break;
|
||||
if(!_client.Server.CanUnlockTechnology(tech)) break;
|
||||
if (_client.Server.UnlockTechnology(tech))
|
||||
{
|
||||
database.SyncWithServer();
|
||||
database.Dirty();
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ConsoleServerSyncMessage msg:
|
||||
database.SyncWithServer();
|
||||
UpdateUserInterface();
|
||||
break;
|
||||
|
||||
case ConsoleServerSelectionMessage msg:
|
||||
if (!Owner.TryGetComponent(out ResearchClientComponent client)) break;
|
||||
client.OpenUserInterface(message.Session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method to update the user interface on the clients.
|
||||
/// </summary>
|
||||
public void UpdateUserInterface()
|
||||
{
|
||||
_userInterface.SetState(GetNewUiState());
|
||||
}
|
||||
|
||||
private ResearchConsoleBoundInterfaceState GetNewUiState()
|
||||
{
|
||||
var points = _client.ConnectedToServer ? _client.Server.Point : 0;
|
||||
var pointsPerSecond = _client.ConnectedToServer ? _client.Server.PointsPerSecond : 0;
|
||||
|
||||
return new ResearchConsoleBoundInterfaceState(points, pointsPerSecond);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the user interface on a certain player session.
|
||||
/// </summary>
|
||||
/// <param name="session">Session where the UI will be shown</param>
|
||||
public void OpenUserInterface(IPlayerSession session)
|
||||
{
|
||||
_userInterface.Open(session);
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IActorComponent actor))
|
||||
return;
|
||||
|
||||
OpenUserInterface(actor.playerSession);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Research
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class ResearchPointSourceComponent : ResearchClientComponent
|
||||
{
|
||||
public override string Name => "ResearchPointSource";
|
||||
|
||||
private int _pointsPerSecond;
|
||||
private bool _active;
|
||||
|
||||
[ViewVariables]
|
||||
public int PointsPerSecond
|
||||
{
|
||||
get => _pointsPerSecond;
|
||||
set => value = _pointsPerSecond;
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public bool Active
|
||||
{
|
||||
get => _active;
|
||||
set => value = _active;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _pointsPerSecond, "pointspersecond", 0);
|
||||
serializer.DataField(ref _active, "active", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Research
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ResearchServerComponent : Component
|
||||
{
|
||||
public static int ServerCount = 0;
|
||||
|
||||
public override string Name => "ResearchServer";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string ServerName => _serverName;
|
||||
|
||||
private string _serverName = "RDSERVER";
|
||||
private float _timer = 0f;
|
||||
public TechnologyDatabaseComponent Database { get; private set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private int _points = 0;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public int Id { get; private set; }
|
||||
|
||||
// You could optimize research by keeping a list of unlocked recipes too.
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public IReadOnlyList<TechnologyPrototype> UnlockedTechnologies => Database.Technologies;
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public List<ResearchPointSourceComponent> PointSources { get; } = new List<ResearchPointSourceComponent>();
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public List<ResearchClientComponent> Clients { get; } = new List<ResearchClientComponent>();
|
||||
|
||||
public int Point => _points;
|
||||
|
||||
/// <summary>
|
||||
/// How many points per second this R&D server gets.
|
||||
/// The value is calculated from all point sources connected to it.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public int PointsPerSecond
|
||||
{
|
||||
// This could be changed to PointsPerMinute quite easily for optimization.
|
||||
get
|
||||
{
|
||||
var points = 0;
|
||||
|
||||
foreach (var source in PointSources)
|
||||
{
|
||||
if (source.Active) points += source.PointsPerSecond;
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Id = ServerCount++;
|
||||
IoCManager.Resolve<IEntitySystemManager>()?.GetEntitySystem<ResearchSystem>()?.RegisterServer(this);
|
||||
Database = Owner.GetComponent<TechnologyDatabaseComponent>();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
IoCManager.Resolve<IEntitySystemManager>()?.GetEntitySystem<ResearchSystem>()?.UnregisterServer(this);
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
serializer.DataField(ref _serverName, "servername", "RDSERVER");
|
||||
serializer.DataField(ref _points, "points", 0);
|
||||
}
|
||||
|
||||
public bool CanUnlockTechnology(TechnologyPrototype technology)
|
||||
{
|
||||
if (!Database.CanUnlockTechnology(technology) || _points < technology.RequiredPoints || Database.IsTechnologyUnlocked(technology)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks a technology, but only if there are enough research points for it.
|
||||
/// If there are, it subtracts the amount of points from the total.
|
||||
/// </summary>
|
||||
/// <param name="technology"></param>
|
||||
/// <returns></returns>
|
||||
public bool UnlockTechnology(TechnologyPrototype technology)
|
||||
{
|
||||
if (!CanUnlockTechnology(technology)) return false;
|
||||
var result = Database.UnlockTechnology(technology);
|
||||
if(result)
|
||||
_points -= technology.RequiredPoints;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check whether a technology is unlocked or not.
|
||||
/// </summary>
|
||||
/// <param name="technology"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsTechnologyUnlocked(TechnologyPrototype technology)
|
||||
{
|
||||
return Database.IsTechnologyUnlocked(technology);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a remote client on this research server.
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
/// <returns></returns>
|
||||
public bool RegisterClient(ResearchClientComponent client)
|
||||
{
|
||||
if (client is ResearchPointSourceComponent source)
|
||||
{
|
||||
if (PointSources.Contains(source)) return false;
|
||||
PointSources.Add(source);
|
||||
source.Server = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Clients.Contains(client)) return false;
|
||||
Clients.Add(client);
|
||||
client.Server = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unregisters a remote client from this server.
|
||||
/// </summary>
|
||||
/// <param name="client"></param>
|
||||
public void UnregisterClient(ResearchClientComponent client)
|
||||
{
|
||||
if (client is ResearchPointSourceComponent source)
|
||||
{
|
||||
PointSources.Remove(source);
|
||||
return;
|
||||
}
|
||||
|
||||
Clients.Remove(client);
|
||||
}
|
||||
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
_timer += frameTime;
|
||||
if (_timer < 1f) return;
|
||||
_timer = 0f;
|
||||
_points += PointsPerSecond;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using Content.Shared.GameObjects.Components.Research;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Research
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class TechnologyDatabaseComponent : SharedTechnologyDatabaseComponent
|
||||
{
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new TechnologyDatabaseState(_technologies);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes this database against other,
|
||||
/// adding all technologies from the other that
|
||||
/// this one doesn't have.
|
||||
/// </summary>
|
||||
/// <param name="otherDatabase">The other database</param>
|
||||
/// <param name="twoway">Whether the other database should be synced against this one too or not.</param>
|
||||
public void Sync(TechnologyDatabaseComponent otherDatabase, bool twoway = true)
|
||||
{
|
||||
foreach (var tech in otherDatabase.Technologies)
|
||||
{
|
||||
if (!IsTechnologyUnlocked(tech)) AddTechnology(tech);
|
||||
}
|
||||
|
||||
if(twoway)
|
||||
otherDatabase.Sync(this, false);
|
||||
|
||||
Dirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If there's a research client component attached to the owner entity,
|
||||
/// and the research client is connected to a research server, this method
|
||||
/// syncs against the research server, and the server against the local database.
|
||||
/// </summary>
|
||||
/// <returns>Whether it could sync or not</returns>
|
||||
public bool SyncWithServer()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out ResearchClientComponent client)) return false;
|
||||
if (!client.ConnectedToServer) return false;
|
||||
|
||||
Sync(client.Server.Database);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If possible, unlocks a technology on this database.
|
||||
/// </summary>
|
||||
/// <param name="technology"></param>
|
||||
/// <returns></returns>
|
||||
public bool UnlockTechnology(TechnologyPrototype technology)
|
||||
{
|
||||
if (!CanUnlockTechnology(technology)) return false;
|
||||
|
||||
AddTechnology(technology);
|
||||
Dirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a technology to the database without checking if it could be unlocked.
|
||||
/// </summary>
|
||||
/// <param name="technology"></param>
|
||||
public void AddTechnology(TechnologyPrototype technology)
|
||||
{
|
||||
_technologies.Add(technology);
|
||||
}
|
||||
}
|
||||
}
|
||||
89
Content.Server/GameObjects/EntitySystems/ResearchSystem.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Research;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
public class ResearchSystem : EntitySystem
|
||||
{
|
||||
public const float ResearchConsoleUIUpdateTime = 30f;
|
||||
|
||||
private float _timer = ResearchConsoleUIUpdateTime;
|
||||
private readonly List<ResearchServerComponent> _servers = new List<ResearchServerComponent>();
|
||||
private readonly IEntityQuery ConsoleQuery;
|
||||
public IReadOnlyList<ResearchServerComponent> Servers => _servers;
|
||||
|
||||
public ResearchSystem()
|
||||
{
|
||||
ConsoleQuery = new TypeEntityQuery(typeof(ResearchConsoleComponent));
|
||||
}
|
||||
|
||||
public bool RegisterServer(ResearchServerComponent server)
|
||||
{
|
||||
if (_servers.Contains(server)) return false;
|
||||
_servers.Add(server);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UnregisterServer(ResearchServerComponent server)
|
||||
{
|
||||
_servers.Remove(server);
|
||||
}
|
||||
|
||||
public ResearchServerComponent GetServerById(int id)
|
||||
{
|
||||
foreach (var server in Servers)
|
||||
{
|
||||
if (server.Id == id) return server;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public string[] GetServerNames()
|
||||
{
|
||||
var list = new string[Servers.Count];
|
||||
|
||||
for (var i = 0; i < Servers.Count; i++)
|
||||
{
|
||||
list[i] = Servers[i].ServerName;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public int[] GetServerIds()
|
||||
{
|
||||
var list = new int[Servers.Count];
|
||||
|
||||
for (var i = 0; i < Servers.Count; i++)
|
||||
{
|
||||
list[i] = Servers[i].Id;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
_timer += frameTime;
|
||||
|
||||
foreach (var server in _servers)
|
||||
{
|
||||
server.Update(frameTime);
|
||||
}
|
||||
|
||||
if (_timer >= ResearchConsoleUIUpdateTime)
|
||||
{
|
||||
foreach (var console in EntityManager.GetEntities(ConsoleQuery))
|
||||
{
|
||||
console.GetComponent<ResearchConsoleComponent>().UpdateUserInterface();
|
||||
}
|
||||
|
||||
_timer = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,6 +54,30 @@ namespace Content.Shared.GameObjects.Components.Research
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Sent to the server to sync the lathe's technology database with the research server.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class LatheServerSyncMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public LatheServerSyncMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sent to the server to open the ResearchClient UI.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class LatheServerSelectionMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public LatheServerSelectionMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sent to the client when the lathe is producing a recipe.
|
||||
/// </summary>
|
||||
|
||||
@@ -13,8 +13,8 @@ namespace Content.Shared.GameObjects.Components.Research
|
||||
public class SharedLatheDatabaseComponent : Component, IEnumerable<LatheRecipePrototype>
|
||||
{
|
||||
public override string Name => "LatheDatabase";
|
||||
public sealed override uint? NetID => ContentNetIDs.LATHE_DATABASE;
|
||||
public sealed override Type StateType => typeof(LatheDatabaseState);
|
||||
public override uint? NetID => ContentNetIDs.LATHE_DATABASE;
|
||||
public override Type StateType => typeof(LatheDatabaseState);
|
||||
|
||||
private List<LatheRecipePrototype> _recipes = new List<LatheRecipePrototype>();
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace Content.Shared.GameObjects.Components.Research
|
||||
/// <returns>Whether it could be added or not</returns>
|
||||
public virtual void AddRecipe(LatheRecipePrototype recipe)
|
||||
{
|
||||
if(!Contains(recipe))
|
||||
_recipes.Add(recipe);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Research
|
||||
{
|
||||
|
||||
[ComponentReference(typeof(SharedLatheDatabaseComponent))]
|
||||
public class SharedProtolatheDatabaseComponent : SharedLatheDatabaseComponent
|
||||
{
|
||||
public override string Name => "ProtolatheDatabase";
|
||||
public sealed override uint? NetID => ContentNetIDs.PROTOLATHE_DATABASE;
|
||||
public sealed override Type StateType => typeof(ProtolatheDatabaseState);
|
||||
|
||||
private List<LatheRecipePrototype> _protolatheRecipes = new List<LatheRecipePrototype>();
|
||||
|
||||
/// <summary>
|
||||
/// A full list of recipes this protolathe can print.
|
||||
/// </summary>
|
||||
public List<LatheRecipePrototype> ProtolatheRecipes => _protolatheRecipes;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
if (serializer.Reading)
|
||||
{
|
||||
var recipes = serializer.ReadDataField("protolatherecipes", new List<string>());
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
foreach (var id in recipes)
|
||||
{
|
||||
if (!prototypeManager.TryIndex(id, out LatheRecipePrototype recipe)) continue;
|
||||
_protolatheRecipes.Add(recipe);
|
||||
}
|
||||
} else if (serializer.Writing)
|
||||
{
|
||||
var recipes = GetProtolatheRecipeIdList();
|
||||
serializer.DataField(ref recipes, "protolatherecipes", new List<string>());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of the allowed protolathe recipe IDs.
|
||||
/// </summary>
|
||||
/// <returns>A list of recipe IDs allowed</returns>
|
||||
public List<string> GetProtolatheRecipeIdList()
|
||||
{
|
||||
var list = new List<string>();
|
||||
|
||||
foreach (var recipe in ProtolatheRecipes)
|
||||
{
|
||||
list.Add(recipe.ID);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public class ProtolatheDatabaseState : ComponentState
|
||||
{
|
||||
public readonly List<string> Recipes;
|
||||
public ProtolatheDatabaseState(List<string> recipes) : base(ContentNetIDs.PROTOLATHE_DATABASE)
|
||||
{
|
||||
Recipes = recipes;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Research
|
||||
{
|
||||
public class SharedResearchClientComponent : Component
|
||||
{
|
||||
public override string Name => "ResearchClient";
|
||||
|
||||
/// <summary>
|
||||
/// Request that the server updates the client.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class ResearchClientSyncMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
|
||||
public ResearchClientSyncMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sent to the server when the client chooses a research server.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class ResearchClientServerSelectedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public int ServerId;
|
||||
|
||||
public ResearchClientServerSelectedMessage(int serverId)
|
||||
{
|
||||
ServerId = serverId;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sent to the server when the client deselects a research server.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class ResearchClientServerDeselectedMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public ResearchClientServerDeselectedMessage()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public enum ResearchClientUiKey
|
||||
{
|
||||
Key,
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ResearchClientBoundInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public int ServerCount;
|
||||
public string[] ServerNames;
|
||||
public int[] ServerIds;
|
||||
public int SelectedServerId;
|
||||
|
||||
public ResearchClientBoundInterfaceState(int serverCount, string[] serverNames, int[] serverIds, int selectedServerId = -1)
|
||||
{
|
||||
ServerCount = serverCount;
|
||||
ServerNames = serverNames;
|
||||
ServerIds = serverIds;
|
||||
SelectedServerId = selectedServerId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Components.UserInterface;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Research
|
||||
{
|
||||
public class SharedResearchConsoleComponent : Component
|
||||
{
|
||||
public override string Name => "ResearchConsole";
|
||||
public override uint? NetID => ContentNetIDs.RESEARCH_CONSOLE;
|
||||
|
||||
[NetSerializable, Serializable]
|
||||
public enum ResearchConsoleUiKey
|
||||
{
|
||||
Key,
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class ConsoleUnlockTechnologyMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public string Id;
|
||||
public ConsoleUnlockTechnologyMessage(string id)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class ConsoleServerSyncMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public ConsoleServerSyncMessage()
|
||||
{}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class ConsoleServerSelectionMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public ConsoleServerSelectionMessage()
|
||||
{}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class ResearchConsoleBoundInterfaceState : BoundUserInterfaceState
|
||||
{
|
||||
public int Points;
|
||||
public int PointsPerSecond;
|
||||
public ResearchConsoleBoundInterfaceState(int points, int pointsPerSecond)
|
||||
{
|
||||
Points = points;
|
||||
PointsPerSecond = pointsPerSecond;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Research;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Research
|
||||
{
|
||||
public class SharedTechnologyDatabaseComponent : Component, IEnumerable<TechnologyPrototype>
|
||||
{
|
||||
public override string Name => "TechnologyDatabase";
|
||||
public override uint? NetID => ContentNetIDs.TECHNOLOGY_DATABASE;
|
||||
public override Type StateType => typeof(TechnologyDatabaseState);
|
||||
|
||||
protected List<TechnologyPrototype> _technologies = new List<TechnologyPrototype>();
|
||||
|
||||
/// <summary>
|
||||
/// A read-only list of unlocked technologies.
|
||||
/// </summary>
|
||||
public IReadOnlyList<TechnologyPrototype> Technologies => _technologies;
|
||||
|
||||
public IEnumerator<TechnologyPrototype> GetEnumerator()
|
||||
{
|
||||
return Technologies.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list with the IDs of all unlocked technologies.
|
||||
/// </summary>
|
||||
/// <returns>A list of technology IDs</returns>
|
||||
public List<string> GetTechnologyIdList()
|
||||
{
|
||||
List<string> techIds = new List<string>();
|
||||
|
||||
foreach (var tech in _technologies)
|
||||
{
|
||||
techIds.Add(tech.ID);
|
||||
}
|
||||
|
||||
return techIds;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a technology is unlocked on this database or not.
|
||||
/// </summary>
|
||||
/// <param name="technology">The technology to be checked</param>
|
||||
/// <returns>Whether it is unlocked or not</returns>
|
||||
public bool IsTechnologyUnlocked(TechnologyPrototype technology)
|
||||
{
|
||||
return _technologies.Contains(technology);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a technology can be unlocked on this database,
|
||||
/// taking parent technologies into account.
|
||||
/// </summary>
|
||||
/// <param name="technology">The technology to be checked</param>
|
||||
/// <returns>Whether it could be unlocked or not</returns>
|
||||
public bool CanUnlockTechnology(TechnologyPrototype technology)
|
||||
{
|
||||
if (technology == null || IsTechnologyUnlocked(technology)) return false;
|
||||
var protoMan = IoCManager.Resolve<IPrototypeManager>();
|
||||
foreach (var technologyId in technology.RequiredTechnologies)
|
||||
{
|
||||
protoMan.TryIndex(technologyId, out TechnologyPrototype requiredTechnology);
|
||||
if (requiredTechnology == null)
|
||||
return false;
|
||||
|
||||
if (!IsTechnologyUnlocked(requiredTechnology))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
if (serializer.Reading)
|
||||
{
|
||||
var techs = serializer.ReadDataField("technologies", new List<string>());
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
foreach (var id in techs)
|
||||
{
|
||||
if (!prototypeManager.TryIndex(id, out TechnologyPrototype tech)) continue;
|
||||
_technologies.Add(tech);
|
||||
}
|
||||
} else if (serializer.Writing)
|
||||
{
|
||||
var techs = GetTechnologyIdList();
|
||||
serializer.DataField(ref techs, "technologies", new List<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public class TechnologyDatabaseState : ComponentState
|
||||
{
|
||||
public List<string> Technologies;
|
||||
public TechnologyDatabaseState(List<string> technologies) : base(ContentNetIDs.TECHNOLOGY_DATABASE)
|
||||
{
|
||||
technologies = technologies;
|
||||
}
|
||||
|
||||
public TechnologyDatabaseState(List<TechnologyPrototype> technologies) : base(ContentNetIDs.TECHNOLOGY_DATABASE)
|
||||
{
|
||||
Technologies = new List<string>();
|
||||
foreach (var technology in technologies)
|
||||
{
|
||||
Technologies.Add(technology.ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,9 @@
|
||||
public const uint MATERIAL_STORAGE = 1018;
|
||||
public const uint HAND_TELEPORTER = 1019;
|
||||
public const uint VENDING_MACHINE = 1020;
|
||||
public const uint WIRES = 1021;
|
||||
public const uint PROTOLATHE_DATABASE = 1021;
|
||||
public const uint TECHNOLOGY_DATABASE = 1022;
|
||||
public const uint RESEARCH_CONSOLE = 1023;
|
||||
public const uint WIRES = 1024;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Shared.Research
|
||||
@@ -21,11 +22,13 @@ namespace Content.Shared.Research
|
||||
private int _completeTime;
|
||||
private Dictionary<string, int> _requiredMaterials;
|
||||
|
||||
[ViewVariables]
|
||||
public string ID => _id;
|
||||
|
||||
/// <summary>
|
||||
/// Name displayed in the lathe GUI.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string Name
|
||||
{
|
||||
get
|
||||
@@ -43,6 +46,7 @@ namespace Content.Shared.Research
|
||||
/// <summary>
|
||||
/// Short description displayed in the lathe GUI.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string Description
|
||||
{
|
||||
get
|
||||
@@ -60,17 +64,20 @@ namespace Content.Shared.Research
|
||||
/// <summary>
|
||||
/// Texture path used in the lathe GUI.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public SpriteSpecifier Icon => _icon;
|
||||
|
||||
/// <summary>
|
||||
/// The prototype name of the resulting entity when the recipe is printed.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string Result => _result;
|
||||
|
||||
/// <summary>
|
||||
/// The materials required to produce this recipe.
|
||||
/// Takes a material ID as string.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public Dictionary<string, int> RequiredMaterials
|
||||
{
|
||||
get => _requiredMaterials;
|
||||
@@ -82,6 +89,7 @@ namespace Content.Shared.Research
|
||||
/// How many milliseconds it'll take for the lathe to finish this recipe.
|
||||
/// Might lower depending on the lathe's upgrade level.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public int CompleteTime => _completeTime;
|
||||
|
||||
public void LoadFrom(YamlMappingNode mapping)
|
||||
|
||||
76
Content.Shared/Research/TechnologyPrototype.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Shared.Research
|
||||
{
|
||||
[NetSerializable, Serializable, Prototype("technology")]
|
||||
public class TechnologyPrototype : IPrototype, IIndexedPrototype
|
||||
{
|
||||
private string _name;
|
||||
private string _id;
|
||||
private SpriteSpecifier _icon;
|
||||
private string _description;
|
||||
private int _requiredPoints;
|
||||
private List<string> _requiredTechnologies;
|
||||
private List<string> _unlockedRecipes;
|
||||
|
||||
/// <summary>
|
||||
/// The ID of this technology prototype.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string ID => _id;
|
||||
|
||||
/// <summary>
|
||||
/// The name this technology will have on user interfaces.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string Name => _name;
|
||||
|
||||
/// <summary>
|
||||
/// An icon that represent this technology.
|
||||
/// </summary>
|
||||
public SpriteSpecifier Icon => _icon;
|
||||
|
||||
/// <summary>
|
||||
/// A short description of the technology.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string Description => _description;
|
||||
|
||||
/// <summary>
|
||||
/// The required research points to unlock this technology.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public int RequiredPoints => _requiredPoints;
|
||||
|
||||
/// <summary>
|
||||
/// A list of technology IDs required to unlock this technology.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public List<string> RequiredTechnologies => _requiredTechnologies;
|
||||
|
||||
/// <summary>
|
||||
/// A list of recipe IDs this technology unlocks.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public List<string> UnlockedRecipes => _unlockedRecipes;
|
||||
|
||||
public void LoadFrom(YamlMappingNode mapping)
|
||||
{
|
||||
var serializer = YamlObjectSerializer.NewReader(mapping);
|
||||
|
||||
serializer.DataField(ref _name, "name", string.Empty);
|
||||
serializer.DataField(ref _id, "id", string.Empty);
|
||||
serializer.DataField(ref _description, "description", string.Empty);
|
||||
serializer.DataField(ref _icon, "icon", SpriteSpecifier.Invalid);
|
||||
serializer.DataField(ref _requiredPoints, "requiredpoints", 0);
|
||||
serializer.DataField(ref _requiredTechnologies, "requiredtechnologies", new List<string>());
|
||||
serializer.DataField(ref _unlockedRecipes, "unlockedrecipes", new List<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
40
Resources/Prototypes/Entities/Research.yml
Normal file
@@ -0,0 +1,40 @@
|
||||
- type: entity
|
||||
id: researchAndDevelopmentServer
|
||||
name: "R&D Server"
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Buildings/research.rsi
|
||||
state: server
|
||||
- type: Icon
|
||||
sprite: Buildings/research.rsi
|
||||
state: server
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Collidable
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
- type: ResearchServer
|
||||
- type: TechnologyDatabase
|
||||
|
||||
- type: entity
|
||||
id: baseResearchAndDevelopmentPointSource
|
||||
name: "Base R&D Point Source"
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Buildings/research.rsi
|
||||
state: tdoppler
|
||||
- type: Icon
|
||||
sprite: Buildings/research.rsi
|
||||
state: tdoppler
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Collidable
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
- type: ResearchPointSource
|
||||
pointspersecond: 1000
|
||||
active: true
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.ResearchClientUiKey.Key
|
||||
type: ResearchClientBoundUserInterface
|
||||
@@ -10,7 +10,6 @@
|
||||
- type: Icon
|
||||
sprite: Buildings/computer.rsi
|
||||
state: computer
|
||||
|
||||
- type: Computer
|
||||
- type: PowerDevice
|
||||
priority: High
|
||||
@@ -94,6 +93,15 @@
|
||||
- type: ComputerVisualizer2D
|
||||
key: rd_key
|
||||
screen: rdcomp
|
||||
- type: ResearchClient
|
||||
- type: ResearchConsole
|
||||
- type: TechnologyDatabase
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.ResearchConsoleUiKey.Key
|
||||
type: ResearchConsoleBoundUserInterface
|
||||
- key: enum.ResearchClientUiKey.Key
|
||||
type: ResearchClientBoundUserInterface
|
||||
|
||||
|
||||
- type: entity
|
||||
|
||||
@@ -1,8 +1,25 @@
|
||||
- type: entity
|
||||
id: BaseLathe
|
||||
name: "Lathe"
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Collidable
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
- type: Lathe
|
||||
- type: MaterialStorage
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.LatheUiKey.Key
|
||||
type: LatheBoundUserInterface
|
||||
|
||||
|
||||
- type: entity
|
||||
parent: BaseLathe
|
||||
id: autolathe
|
||||
name: "Autolathe"
|
||||
components:
|
||||
- type: Clickable
|
||||
- type: Sprite
|
||||
sprite: Buildings/autolathe.rsi
|
||||
state: idle
|
||||
@@ -33,8 +50,45 @@
|
||||
- CableStack
|
||||
- Crowbar
|
||||
- Multitool
|
||||
- type: MaterialStorage
|
||||
|
||||
- type: entity
|
||||
parent: BaseLathe
|
||||
id: protolathe
|
||||
name: "Protolathe"
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: Buildings/research.rsi
|
||||
state: protolathe
|
||||
- type: Icon
|
||||
sprite: Buildings/research.rsi
|
||||
state: protolathe
|
||||
- type: Collidable
|
||||
shapes:
|
||||
- !type:PhysShapeAabb
|
||||
mask: 19
|
||||
layer: 16
|
||||
- type: SnapGrid
|
||||
offset: Center
|
||||
- type: ResearchClient
|
||||
- type: TechnologyDatabase
|
||||
- type: ProtolatheDatabase
|
||||
protolatherecipes:
|
||||
- Brutepack
|
||||
- Ointment
|
||||
- LightTube
|
||||
- LightBulb
|
||||
- MetalStack
|
||||
- GlassStack
|
||||
- Wirecutter
|
||||
- Screwdriver
|
||||
- Welder
|
||||
- Wrench
|
||||
- CableStack
|
||||
- Crowbar
|
||||
- Multitool
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.LatheUiKey.Key
|
||||
type: LatheBoundUserInterface
|
||||
- key: enum.ResearchClientUiKey.Key
|
||||
type: ResearchClientBoundUserInterface
|
||||
26
Resources/Prototypes/Technologies/sheet.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
- type: technology
|
||||
name: "Test Technology 1"
|
||||
id: Parent1
|
||||
description: Parent technology needed for sheets
|
||||
icon: Objects/sheet_glass.png
|
||||
requiredpoints: 5000
|
||||
|
||||
- type: technology
|
||||
name: "Test Technology 2"
|
||||
id: Parent2
|
||||
description: Parent technology 2 needed for sheets
|
||||
icon: Objects/sheet_glass.png
|
||||
requiredpoints: 5000
|
||||
|
||||
- type: technology
|
||||
name: Material sheet printing
|
||||
id: Sheets
|
||||
description: Print those sheets!
|
||||
icon: Objects/sheet_metal.png
|
||||
requiredpoints: 500
|
||||
requiredtechnologies:
|
||||
- Parent1
|
||||
- Parent2
|
||||
unlockedrecipes:
|
||||
- MetalStack
|
||||
- GlassStack
|
||||
BIN
Resources/Textures/Buildings/research.rsi/circuit_imprinter.png
Normal file
|
After Width: | Height: | Size: 603 B |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 664 B |
BIN
Resources/Textures/Buildings/research.rsi/d_analyzer.png
Normal file
|
After Width: | Height: | Size: 592 B |
BIN
Resources/Textures/Buildings/research.rsi/d_analyzer_l.png
Normal file
|
After Width: | Height: | Size: 623 B |
BIN
Resources/Textures/Buildings/research.rsi/d_analyzer_la.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Resources/Textures/Buildings/research.rsi/d_analyzer_process.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
Resources/Textures/Buildings/research.rsi/d_analyzer_t.png
Normal file
|
After Width: | Height: | Size: 631 B |
1
Resources/Textures/Buildings/research.rsi/meta.json
Normal file
@@ -0,0 +1 @@
|
||||
{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/discordia-space/CEV-Eris/blob/master/icons/obj/machines/excelsior/autolathe.dmi at 40b254106b46981b8ad95ccd5589deb8fa56e765", "states": [{"name": "circuit_imprinter", "directions": 1, "delays": [[1.0]]}, {"name": "circuit_imprinter_ani", "directions": 1, "delays": [[0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08, 0.08]]}, {"name": "circuit_imprinter_t", "directions": 1, "delays": [[1.0]]}, {"name": "d_analyzer", "directions": 1, "delays": [[1.0]]}, {"name": "d_analyzer_l", "directions": 1, "delays": [[1.0]]}, {"name": "d_analyzer_la", "directions": 1, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "d_analyzer_process", "directions": 1, "delays": [[0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09, 0.09]]}, {"name": "d_analyzer_t", "directions": 1, "delays": [[1.0]]}, {"name": "protolathe", "directions": 1, "delays": [[1.0]]}, {"name": "protolathe_adamantine", "directions": 1, "delays": [[0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088]]}, {"name": "protolathe_bananium", "directions": 1, "delays": [[0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088]]}, {"name": "protolathe_diamond", "directions": 1, "delays": [[0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088]]}, {"name": "protolathe_glass", "directions": 1, "delays": [[0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088]]}, {"name": "protolathe_gold", "directions": 1, "delays": [[0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088]]}, {"name": "protolathe_metal", "directions": 1, "delays": [[0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088]]}, {"name": "protolathe_n", "directions": 1, "delays": [[0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05]]}, {"name": "protolathe_silver", "directions": 1, "delays": [[0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088]]}, {"name": "protolathe_solid plasma", "directions": 1, "delays": [[0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088]]}, {"name": "protolathe_t", "directions": 1, "delays": [[1.0]]}, {"name": "protolathe_uranium", "directions": 1, "delays": [[0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088, 0.088]]}, {"name": "server", "directions": 1, "delays": [[1.0]]}, {"name": "server-nopower", "directions": 1, "delays": [[1.0]]}, {"name": "server-off", "directions": 1, "delays": [[1.0]]}, {"name": "server-on", "directions": 1, "delays": [[1.0]]}, {"name": "server_o", "directions": 1, "delays": [[1.0]]}, {"name": "tdoppler", "directions": 4, "delays": [[0.8, 0.2], [0.8, 0.2], [0.8, 0.2], [0.8, 0.2]]}, {"name": "tdoppler-broken", "directions": 1, "delays": [[0.1, 0.1, 0.1]]}, {"name": "tdoppler-off", "directions": 1, "delays": [[1.0]]}]}
|
||||
BIN
Resources/Textures/Buildings/research.rsi/protolathe.png
Normal file
|
After Width: | Height: | Size: 879 B |
|
After Width: | Height: | Size: 526 B |
|
After Width: | Height: | Size: 521 B |
BIN
Resources/Textures/Buildings/research.rsi/protolathe_diamond.png
Normal file
|
After Width: | Height: | Size: 532 B |
BIN
Resources/Textures/Buildings/research.rsi/protolathe_glass.png
Normal file
|
After Width: | Height: | Size: 521 B |
BIN
Resources/Textures/Buildings/research.rsi/protolathe_gold.png
Normal file
|
After Width: | Height: | Size: 567 B |
BIN
Resources/Textures/Buildings/research.rsi/protolathe_metal.png
Normal file
|
After Width: | Height: | Size: 524 B |
BIN
Resources/Textures/Buildings/research.rsi/protolathe_n.png
Normal file
|
After Width: | Height: | Size: 3.0 KiB |
BIN
Resources/Textures/Buildings/research.rsi/protolathe_silver.png
Normal file
|
After Width: | Height: | Size: 534 B |
|
After Width: | Height: | Size: 538 B |
BIN
Resources/Textures/Buildings/research.rsi/protolathe_t.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
Resources/Textures/Buildings/research.rsi/protolathe_uranium.png
Normal file
|
After Width: | Height: | Size: 509 B |
BIN
Resources/Textures/Buildings/research.rsi/server-nopower.png
Normal file
|
After Width: | Height: | Size: 487 B |
BIN
Resources/Textures/Buildings/research.rsi/server-off.png
Normal file
|
After Width: | Height: | Size: 528 B |
BIN
Resources/Textures/Buildings/research.rsi/server-on.png
Normal file
|
After Width: | Height: | Size: 529 B |
BIN
Resources/Textures/Buildings/research.rsi/server.png
Normal file
|
After Width: | Height: | Size: 448 B |
BIN
Resources/Textures/Buildings/research.rsi/server_o.png
Normal file
|
After Width: | Height: | Size: 559 B |
BIN
Resources/Textures/Buildings/research.rsi/tdoppler-broken.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Resources/Textures/Buildings/research.rsi/tdoppler-off.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
Resources/Textures/Buildings/research.rsi/tdoppler.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |