RND Rework [Death to Techweb] (#16370)

* Techweb rework

* more ui work

* finishing ui

* Finish all the C# logic

* the techs + lathes

* remove old-tech

* mirror-review
This commit is contained in:
Nemanja
2023-05-15 16:17:30 -04:00
committed by GitHub
parent a71d9c8eff
commit 9efc727fe1
51 changed files with 1732 additions and 1401 deletions

View File

@@ -0,0 +1,29 @@
<Control xmlns="https://spacestation14.io">
<BoxContainer Orientation="Horizontal">
<PanelContainer Name="Background"
Access="Public"
StyleClasses="PDABackground"
VerticalExpand="False"
HorizontalExpand="False"
MaxWidth="10"
Margin="0 0 -5 0"/>
<Button Name="Main"
Disabled="True"
HorizontalExpand="True"
VerticalExpand="False"
StyleClasses="ButtonSquare"
Margin="0"
ToolTip="foobar"
TooltipDelay="0.25">
<BoxContainer Orientation="Horizontal" Margin="0">
<TextureRect Name="Texture"
HorizontalExpand="False"
VerticalExpand="False"
Margin="1"
TextureScale="0.5 0.5"/>
<Control MinWidth="5"/>
<RichTextLabel Name="NameLabel" StyleClasses="LabelSubText" VerticalAlignment="Center"/>
</BoxContainer>
</Button>
</BoxContainer>
</Control>

View File

@@ -0,0 +1,24 @@
using Content.Shared.Research.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Research.UI;
[GenerateTypedNameReferences]
public sealed partial class MiniTechnologyCardControl : Control
{
public MiniTechnologyCardControl(TechnologyPrototype technology, IPrototypeManager prototypeManager, SpriteSystem spriteSys, FormattedMessage description)
{
RobustXamlLoader.Load(this);
var discipline = prototypeManager.Index<TechDisciplinePrototype>(technology.Discipline);
Background.ModulateSelfOverride = discipline.Color;
Texture.Texture = spriteSys.Frame0(technology.Icon);
NameLabel.SetMessage(Loc.GetString(technology.Name));
Main.ToolTip = description.ToString();
}
}

View File

@@ -4,83 +4,56 @@ using Content.Shared.Research.Systems;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
namespace Content.Client.Research.UI
{
namespace Content.Client.Research.UI;
[UsedImplicitly]
public sealed class ResearchConsoleBoundUserInterface : BoundUserInterface
{
public int Points { get; private set; }
public int PointsPerSecond { get; private set; }
private ResearchConsoleMenu? _consoleMenu;
private TechnologyDatabaseComponent? _technologyDatabase;
private readonly IEntityManager _entityManager;
private readonly SharedResearchSystem _research;
public ResearchConsoleBoundUserInterface(ClientUserInterfaceComponent owner, Enum uiKey) : base(owner, uiKey)
{
SendMessage(new ConsoleServerSyncMessage());
_entityManager = IoCManager.Resolve<IEntityManager>();
_research = _entityManager.System<SharedResearchSystem>();
}
protected override void Open()
{
base.Open();
if (!_entityManager.TryGetComponent(Owner.Owner, out _technologyDatabase))
return;
var owner = Owner.Owner;
_consoleMenu = new ResearchConsoleMenu(this);
_consoleMenu = new ResearchConsoleMenu(owner);
_consoleMenu.OnClose += Close;
_consoleMenu.ServerSyncButton.OnPressed += (_) =>
_consoleMenu.OnTechnologyCardPressed += id =>
{
SendMessage(new ConsoleServerSyncMessage());
SendMessage(new ConsoleUnlockTechnologyMessage(id));
};
_consoleMenu.ServerSelectionButton.OnPressed += (_) =>
_consoleMenu.OnServerButtonPressed += () =>
{
SendMessage(new ConsoleServerSelectionMessage());
};
_consoleMenu.UnlockButton.OnPressed += (_) =>
_consoleMenu.OnSyncButtonPressed += () =>
{
if (_consoleMenu.TechnologySelected != null)
{
SendMessage(new ConsoleUnlockTechnologyMessage(_consoleMenu.TechnologySelected.ID));
}
SendMessage(new ConsoleServerSyncMessage());
};
_consoleMenu.OnClose += Close;
_consoleMenu.OpenCentered();
}
public bool IsTechnologyUnlocked(TechnologyPrototype technology)
{
if (_technologyDatabase == null)
return false;
return _research.IsTechnologyUnlocked(_technologyDatabase.Owner, technology, _technologyDatabase);
}
public bool CanUnlockTechnology(TechnologyPrototype technology)
{
if (_technologyDatabase == null)
return false;
return _research.ArePrerequesitesUnlocked(_technologyDatabase.Owner, technology, _technologyDatabase);
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (ResearchConsoleBoundInterfaceState)state;
Points = castState.Points;
PointsPerSecond = castState.PointsPerSecond;
// We update the user interface here.
_consoleMenu?.PopulatePoints();
_consoleMenu?.Populate();
if (state is not ResearchConsoleBoundInterfaceState castState)
return;
_consoleMenu?.UpdatePanels(castState);
_consoleMenu?.UpdateInformationPanel(castState);
}
protected override void Dispose(bool disposing)
@@ -91,4 +64,3 @@ namespace Content.Client.Research.UI
_consoleMenu?.Dispose();
}
}
}

View File

@@ -1,87 +1,101 @@
<DefaultWindow xmlns="https://spacestation14.io"
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
Title="{Loc 'research-console-menu-title'}"
MinSize="800 400"
SetSize="800 400">
MinSize="625 400"
SetSize="700 550">
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<BoxContainer Orientation="Horizontal"
HorizontalExpand="True"
VerticalExpand="False"
MinHeight="85"
Margin="10">
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<RichTextLabel Name="ResearchAmountLabel"/>
<RichTextLabel Name="MainDisciplineLabel"/>
<Control VerticalExpand="True"/>
<BoxContainer Name="TierDisplayContainer" Orientation="Horizontal" HorizontalExpand="True" VerticalAlignment="Bottom"/>
<!-- This is where we put all of the little graphics that display discipline tiers!-->
</BoxContainer>
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalAlignment="Right">
<Button Name="ServerButton" Text="{Loc 'research-console-menu-server-selection-button'}" VerticalExpand="True"/>
<Control MinHeight="5"/>
<!--todo is this button even necessary?!-->
<Button Name="SyncButton" Text="{Loc 'research-console-menu-server-sync-button'}" VerticalExpand="True"/>
</BoxContainer>
</BoxContainer>
<BoxContainer Orientation="Horizontal"
HorizontalExpand="True"
VerticalExpand="True">
<BoxContainer Orientation="Vertical"
VerticalExpand="True"
HorizontalExpand="True"
SizeFlagsStretchRatio="2"
SeparationOverride="10">
<BoxContainer Orientation="Vertical"
Margin="10 0 10 10"
MinWidth="175">
<Label Text="{Loc 'research-console-available-text'}" HorizontalAlignment="Center"/>
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 0 0 10"/>
<PanelContainer VerticalExpand="True">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
</PanelContainer.PanelOverride>
<ScrollContainer
HScrollEnabled="False"
HorizontalExpand="True"
VerticalExpand="True">
<Label Text="{Loc 'research-console-menu-unlocked-technologies-label'}" />
<ItemList Name="UnlockedTechnologies"
SelectMode="Button"
<BoxContainer
Name="AvailableCardsContainer"
Orientation="Vertical"
VerticalExpand="True">
</BoxContainer>
</ScrollContainer>
</PanelContainer>
<Control MinHeight="10"/>
<Label Text="{Loc 'research-console-unlocked-text'}" HorizontalAlignment="Center"/>
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 0 0 10"/>
<PanelContainer VerticalExpand="True">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
</PanelContainer.PanelOverride>
<ScrollContainer
HScrollEnabled="False"
HorizontalExpand="True"
VerticalExpand="True">
<!-- Unlocked technologies are added here by code -->
</ItemList>
<BoxContainer
Name="UnlockedCardsContainer"
Orientation="Vertical"
VerticalExpand="True">
</BoxContainer>
</ScrollContainer>
</PanelContainer>
</BoxContainer>
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<Label Text="{Loc 'research-console-menu-unlockable-technologies-label'}" />
<ItemList Name="UnlockableTechnologies"
SelectMode="Button"
HorizontalExpand="True"
VerticalExpand="True">
<!-- Unlockable technologies are added here by code -->
</ItemList>
</BoxContainer>
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<Label Text="{Loc 'research-console-menu-future-technologies-label'}" />
<ItemList Name="FutureTechnologies"
SelectMode="Button"
HorizontalExpand="True"
VerticalExpand="True">
<!-- Future technologies are added here by code -->
</ItemList>
</BoxContainer>
</BoxContainer>
<BoxContainer Orientation="Horizontal"
HorizontalExpand="True"
VerticalExpand="True"
SizeFlagsStretchRatio="1">
<TextureRect Name="TechnologyIcon"
HorizontalExpand="True"
VerticalExpand="True"
SizeFlagsStretchRatio="1"
Stretch="KeepAspectCentered" />
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True"
SizeFlagsStretchRatio="3">
<Label Name="TechnologyName" />
<Label Name="TechnologyDescription" />
<Label Name="TechnologyRequirements" />
</BoxContainer>
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True"
SizeFlagsStretchRatio="1">
<Label Name="PointLabel" />
<Label Name="PointsPerSecondLabel" />
<BoxContainer Orientation="Vertical"
Align="End"
SizeFlagsStretchRatio="3"
Margin="0 0 10 10">
<PanelContainer VerticalExpand="True" MinSize="0 200">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E" />
</PanelContainer.PanelOverride>
<ScrollContainer
HScrollEnabled="False"
HorizontalExpand="True"
SizeFlagsStretchRatio="2"
VerticalExpand="True">
<Button Name="ServerSelectionButton"
Access="Public"
Text="{Loc 'research-console-menu-server-selection-button'}" />
<Button Name="ServerSyncButton"
Access="Public"
Text="{Loc 'research-console-menu-server-sync-button'}" />
<Button Name="UnlockButton"
Access="Public"
Disabled="True" />
<BoxContainer
Name="TechnologyCardsContainer"
MinSize="100 256"
Orientation="Vertical"
SizeFlagsStretchRatio="2"
VerticalExpand="True">
</BoxContainer>
</ScrollContainer>
</PanelContainer>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</DefaultWindow>
</controls:FancyWindow>

View File

@@ -1,195 +1,167 @@
using System.Collections.Generic;
using Content.Client.UserInterface.Controls;
using Content.Shared.Research.Components;
using Content.Shared.Research.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Utility;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Research.UI;
namespace Content.Client.Research.UI
{
[GenerateTypedNameReferences]
public sealed partial class ResearchConsoleMenu : DefaultWindow
public sealed partial class ResearchConsoleMenu : FancyWindow
{
public ResearchConsoleBoundUserInterface Owner { get; }
public Action<string>? OnTechnologyCardPressed;
public Action? OnServerButtonPressed;
public Action? OnSyncButtonPressed;
private readonly List<TechnologyPrototype> _unlockedTechnologyPrototypes = new();
private readonly List<TechnologyPrototype> _unlockableTechnologyPrototypes = new();
private readonly List<TechnologyPrototype> _futureTechnologyPrototypes = new();
[Dependency] private readonly IEntityManager _entity = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
private readonly TechnologyDatabaseComponent? _technologyDatabase;
private readonly ResearchSystem _research;
private readonly SpriteSystem _sprite;
public TechnologyPrototype? TechnologySelected;
public readonly EntityUid Entity;
public ResearchConsoleMenu(ResearchConsoleBoundUserInterface owner)
public ResearchConsoleMenu(EntityUid entity)
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
Owner = owner;
_research = _entity.System<ResearchSystem>();
_sprite = _entity.System<SpriteSystem>();
Entity = entity;
UnlockedTechnologies.OnItemSelected += UnlockedTechnologySelected;
UnlockableTechnologies.OnItemSelected += UnlockableTechnologySelected;
FutureTechnologies.OnItemSelected += FutureTechnologySelected;
ServerButton.OnPressed += _ => OnServerButtonPressed?.Invoke();
SyncButton.OnPressed += _ => OnSyncButtonPressed?.Invoke();
PointLabel.Text = Loc.GetString("research-console-menu-research-points-text", ("points", 0));
PointsPerSecondLabel.Text = Loc.GetString("research-console-menu-points-per-second-text", ("pointsPerSecond", 0));
UnlockButton.Text = Loc.GetString("research-console-menu-server-unlock-button");
UnlockButton.OnPressed += _ =>
{
CleanSelectedTechnology();
};
Populate();
_entity.TryGetComponent(entity, out _technologyDatabase);
}
/// <summary>
/// Cleans the selected technology controls to blank.
/// </summary>
private void CleanSelectedTechnology()
public void UpdatePanels(ResearchConsoleBoundInterfaceState state)
{
UnlockButton.Disabled = true;
TechnologyIcon.Texture = Texture.Transparent;
TechnologyName.Text = string.Empty;
TechnologyDescription.Text = string.Empty;
TechnologyRequirements.Text = string.Empty;
var allTech = _research.GetAvailableTechnologies(Entity);
AvailableCardsContainer.Children.Clear();
TechnologyCardsContainer.Children.Clear();
UnlockedCardsContainer.Children.Clear();
foreach (var tech in allTech)
{
var mini = new MiniTechnologyCardControl(tech, _prototype, _sprite, GetTechnologyDescription(tech, false));
AvailableCardsContainer.AddChild(mini);
}
/// <summary>
/// Called when an unlocked technology is selected.
/// </summary>
private void UnlockedTechnologySelected(ItemList.ItemListSelectedEventArgs obj)
{
TechnologySelected = _unlockedTechnologyPrototypes[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];
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];
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>())
{
var techName = GetTechName(tech);
if (Owner.IsTechnologyUnlocked(tech))
{
UnlockedTechnologies.AddItem(techName, tech.Icon.Frame0());
_unlockedTechnologyPrototypes.Add(tech);
}
else if (Owner.CanUnlockTechnology(tech))
{
UnlockableTechnologies.AddItem(techName, tech.Icon.Frame0());
_unlockableTechnologyPrototypes.Add(tech);
}
else
{
FutureTechnologies.AddItem(techName, tech.Icon.Frame0());
_futureTechnologyPrototypes.Add(tech);
}
}
}
private string GetTechName(TechnologyPrototype prototype)
{
if (prototype.Name is { } name)
return Loc.GetString(name);
return prototype.ID;
}
/// <summary>
/// Fills the selected technology controls with details.
/// </summary>
public void PopulateSelectedTechnology()
{
if (TechnologySelected == null)
{
TechnologyName.Text = string.Empty;
TechnologyDescription.Text = string.Empty;
TechnologyRequirements.Text = string.Empty;
if (_technologyDatabase == null)
return;
}
TechnologyIcon.Texture = TechnologySelected.Icon.Frame0();
TechnologyName.Text = GetTechName(TechnologySelected);
var desc = Loc.GetString(TechnologySelected.Description);
TechnologyDescription.Text = desc + $"\n{TechnologySelected.RequiredPoints} " + Loc.GetString("research-console-menu-research-points-text" ,("points", Owner.Points)).ToLowerInvariant();
TechnologyRequirements.Text = Loc.GetString("research-console-tech-requirements-none");
var prototypeMan = IoCManager.Resolve<IPrototypeManager>();
for (var i = 0; i < TechnologySelected.RequiredTechnologies.Count; i++)
// i can't figure out the spacing so here you go
TechnologyCardsContainer.AddChild(new Control
{
var requiredId = TechnologySelected.RequiredTechnologies[i];
if (!prototypeMan.TryIndex(requiredId, out TechnologyPrototype? prototype)) continue;
var protoName = GetTechName(prototype);
if (i == 0)
TechnologyRequirements.Text = Loc.GetString("research-console-tech-requirements-prototype-name", ("prototypeName", protoName));
else
TechnologyRequirements.Text += $", {protoName}";
MinHeight = 10
});
foreach (var techId in _technologyDatabase.CurrentTechnologyCards)
{
var tech = _prototype.Index<TechnologyPrototype>(techId);
var cardControl = new TechnologyCardControl(tech, _prototype, _sprite, GetTechnologyDescription(tech), state.Points);
cardControl.OnPressed += () => OnTechnologyCardPressed?.Invoke(techId);
TechnologyCardsContainer.AddChild(cardControl);
}
foreach (var unlocked in _technologyDatabase.UnlockedTechnologies)
{
var tech = _prototype.Index<TechnologyPrototype>(unlocked);
var cardControl = new MiniTechnologyCardControl(tech, _prototype, _sprite, GetTechnologyDescription(tech, false));
UnlockedCardsContainer.AddChild(cardControl);
}
}
/// <summary>
/// Updates the research point labels.
/// </summary>
public void PopulatePoints()
public FormattedMessage GetTechnologyDescription(TechnologyPrototype technology, bool includeCost = true)
{
PointLabel.Text = Loc.GetString("research-console-menu-research-points-text", ("points", Owner.Points));
PointsPerSecondLabel.Text = Loc.GetString("research-console-menu-points-per-second-text", ("pointsPerSecond", Owner.PointsPerSecond));
var description = new FormattedMessage();
if (includeCost)
{
description.AddMarkup(Loc.GetString("research-console-cost", ("amount", technology.Cost)));
description.PushNewline();
}
description.AddMarkup(Loc.GetString("research-console-unlocks-list-start"));
foreach (var recipe in technology.RecipeUnlocks)
{
var recipeProto = _prototype.Index<LatheRecipePrototype>(recipe);
description.PushNewline();
description.AddMarkup(Loc.GetString("research-console-unlocks-list-entry",
("name",recipeProto.Name)));
}
foreach (var generic in technology.GenericUnlocks)
{
description.PushNewline();
description.AddMarkup(Loc.GetString("research-console-unlocks-list-entry-generic",
("name", Loc.GetString(generic.UnlockDescription))));
}
/// <summary>
/// Updates the whole user interface.
/// </summary>
public void Populate()
return description;
}
public void UpdateInformationPanel(ResearchConsoleBoundInterfaceState state)
{
PopulatePoints();
PopulateSelectedTechnology();
PopulateItemLists();
var amountMsg = new FormattedMessage();
amountMsg.AddMarkup(Loc.GetString("research-console-menu-research-points-text",
("points", state.Points)));
ResearchAmountLabel.SetMessage(amountMsg);
if (_technologyDatabase == null)
return;
var disciplineText = Loc.GetString("research-discipline-none");
var disciplineColor = Color.Gray;
if (_technologyDatabase.MainDiscipline != null)
{
var discipline = _prototype.Index<TechDisciplinePrototype>(_technologyDatabase.MainDiscipline);
disciplineText = Loc.GetString(discipline.Name);
disciplineColor = discipline.Color;
}
var msg = new FormattedMessage();
msg.AddMarkup(Loc.GetString("research-console-menu-main-discipline",
("name", disciplineText), ("color", disciplineColor)));
MainDisciplineLabel.SetMessage(msg);
TierDisplayContainer.Children.Clear();
foreach (var disciplineId in _technologyDatabase.SupportedDisciplines)
{
var discipline = _prototype.Index<TechDisciplinePrototype>(disciplineId);
var tier = _research.GetHighestDisciplineTier(_technologyDatabase, discipline);
// don't show tiers with no available tech
if (tier == 0)
continue;
// i'm building the small-ass control here to spare me some mild annoyance in making a new file
var texture = new TextureRect
{
TextureScale = ( 2, 2 ),
VerticalAlignment = VAlignment.Center
};
var label = new RichTextLabel();
texture.Texture = _sprite.Frame0(discipline.Icon);
label.SetMessage(Loc.GetString("research-console-tier-info-small", ("tier", tier)));
var control = new BoxContainer
{
Children =
{
texture,
label,
new Control
{
MinWidth = 10
}
}
};
TierDisplayContainer.AddChild(control);
}
}
}

View File

@@ -0,0 +1,42 @@
<Control xmlns="https://spacestation14.io"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls">
<BoxContainer Name="MainContainer" Orientation="Vertical" Margin="10 0 10 10">
<PanelContainer Name="Background"
Access="Public"
StyleClasses="PDABackground"
MinHeight="15"
VerticalExpand="False"
HorizontalExpand="True"
Margin="0 0 0 -5"/>
<Button Name="MainButton"
Disabled="True"
HorizontalExpand="True"
VerticalExpand="True"
StyleClasses="ButtonSquare"
Margin="0">
<BoxContainer Orientation="Vertical"
VerticalExpand="True"
Margin="5">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<TextureRect Name="TechnologyTexture"
TextureScale="2 2"
VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalAlignment="Center" Margin="10 0 0 0">
<BoxContainer Orientation="Horizontal">
<Label Name="TechnologyNameLabel" StyleClasses="LabelKeyText" HorizontalExpand="True"/>
<TextureRect Name="DisciplineTexture" TextureScale="2 2" VerticalAlignment="Top" HorizontalAlignment="Right"/>
</BoxContainer>
<customControls:HSeparator StyleClasses="LowDivider" Margin="0 5 0 5"/>
<BoxContainer Orientation="Horizontal">
<RichTextLabel Name="TierLabel" HorizontalAlignment="Left" StyleClasses="LabelSubText" HorizontalExpand="True"/>
<Button Name="ResearchButton" Text="{Loc 'research-console-menu-server-research-button'}"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
<Control MinHeight="5"></Control>
<RichTextLabel Name="UnlocksLabel" HorizontalExpand="True" StyleClasses="LabelSubText"></RichTextLabel>
</BoxContainer>
</Button>
</BoxContainer>
</Control>

View File

@@ -0,0 +1,36 @@
using Content.Shared.Research.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Research.UI;
[GenerateTypedNameReferences]
public sealed partial class TechnologyCardControl : Control
{
public Action? OnPressed;
public TechnologyCardControl(TechnologyPrototype technology, IPrototypeManager prototypeManager, SpriteSystem spriteSys, FormattedMessage description, int points)
{
RobustXamlLoader.Load(this);
var discipline = prototypeManager.Index<TechDisciplinePrototype>(technology.Discipline);
Background.ModulateSelfOverride = discipline.Color;
DisciplineTexture.Texture = spriteSys.Frame0(discipline.Icon);
TechnologyNameLabel.Text = Loc.GetString(technology.Name);
var message = new FormattedMessage();
message.AddMarkup(Loc.GetString("research-console-tier-discipline-info",
("tier", technology.Tier), ("color", discipline.Color), ("discipline", Loc.GetString(discipline.Name))));
TierLabel.SetMessage(message);
UnlocksLabel.SetMessage(description);
TechnologyTexture.Texture = spriteSys.Frame0(technology.Icon);
ResearchButton.Disabled = points < technology.Cost;
ResearchButton.OnPressed += _ => OnPressed?.Invoke();
}
}

View File

@@ -1351,6 +1351,10 @@ namespace Content.Client.Stylesheets
.Prop(Label.StylePropertyFont, notoSans12)
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#111111")),
Element<RichTextLabel>().Class("LabelSubText")
.Prop(Label.StylePropertyFont, notoSans10)
.Prop(Label.StylePropertyFontColor, Color.DarkGray),
Element<LineEdit>().Class("PaperLineEdit")
.Prop(LineEdit.StylePropertyStyleBox, new StyleBoxEmpty()),

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Content.Shared.Lathe;
using Content.Shared.Research.Prototypes;
@@ -10,6 +11,40 @@ namespace Content.IntegrationTests.Tests;
[TestFixture]
public sealed class ResearchTest
{
[Test]
public async Task DisciplineValidTierPrerequesitesTest()
{
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings {NoClient = true});
var server = pairTracker.Pair.Server;
var protoManager = server.ResolveDependency<IPrototypeManager>();
await server.WaitAssertion(() =>
{
var allTechs = protoManager.EnumeratePrototypes<TechnologyPrototype>().ToList();
foreach (var discipline in protoManager.EnumeratePrototypes<TechDisciplinePrototype>())
{
foreach (var tech in allTechs)
{
if (tech.Discipline != discipline.ID)
continue;
// we ignore these, anyways
if (tech.Tier == 1)
continue;
Assert.That(tech.Tier, Is.GreaterThan(0), $"Technology {tech} has invalid tier {tech.Tier}.");
Assert.That(discipline.TierPrerequisites.ContainsKey(tech.Tier),
$"Discipline {discipline.ID} does not have a TierPrerequisites definition for tier {tech.Tier}");
}
}
});
await pairTracker.CleanReturnAsync();
}
[Test]
public async Task AllTechPrintableTest()
{
@@ -40,14 +75,13 @@ public sealed class ResearchTest
foreach (var recipe in lathe.DynamicRecipes)
{
if (!latheTechs.Contains(recipe))
latheTechs.Add(recipe);
}
}
foreach (var tech in protoManager.EnumeratePrototypes<TechnologyPrototype>())
{
foreach (var recipe in tech.UnlockedRecipes)
foreach (var recipe in tech.RecipeUnlocks)
{
Assert.That(latheTechs, Does.Contain(recipe), $"Recipe \"{recipe}\" cannot be unlocked on any lathes.");
}

View File

@@ -194,7 +194,7 @@ namespace Content.Server.Lathe
if (uid != args.Lathe || !TryComp<LatheComponent>(uid, out var latheComponent) || latheComponent.DynamicRecipes == null)
return;
args.Recipes = args.Recipes.Union(component.RecipeIds.Where(r => latheComponent.DynamicRecipes.Contains(r))).ToList();
args.Recipes = args.Recipes.Union(component.UnlockedRecipes.Where(r => latheComponent.DynamicRecipes.Contains(r))).ToList();
}
private void OnMaterialAmountChanged(EntityUid uid, LatheComponent component, ref MaterialAmountChangedEvent args)

View File

@@ -28,7 +28,7 @@ namespace Content.Server.Research.Disk
if (!TryComp<ResearchServerComponent>(args.Target, out var server))
return;
_research.AddPointsToServer(server.Owner, component.Points, server);
_research.ModifyServerPoints(args.Target.Value, component.Points, server);
_popupSystem.PopupEntity(Loc.GetString("research-disk-inserted", ("points", component.Points)), args.Target.Value, args.User);
EntityManager.QueueDeleteEntity(uid);
}
@@ -39,7 +39,7 @@ namespace Content.Server.Research.Disk
return;
component.Points = _prototype.EnumeratePrototypes<TechnologyPrototype>()
.Sum(tech => tech.RequiredPoints);
.Sum(tech => tech.Cost);
}
}
}

View File

@@ -105,8 +105,10 @@ public sealed partial class ResearchSystem
/// <param name="serverComponent">The server's ResearchServerComponent. Null if false</param>
/// <param name="component">The client's Researchclient component</param>
/// <returns>If the server was successfully retrieved.</returns>
public bool TryGetClientServer(EntityUid uid, [NotNullWhen(returnValue: true)] out EntityUid? server,
[NotNullWhen(returnValue: true)] out ResearchServerComponent? serverComponent, ResearchClientComponent? component = null)
public bool TryGetClientServer(EntityUid uid,
[NotNullWhen(returnValue: true)] out EntityUid? server,
[NotNullWhen(returnValue: true)] out ResearchServerComponent? serverComponent,
ResearchClientComponent? component = null)
{
server = null;
serverComponent = null;
@@ -117,11 +119,10 @@ public sealed partial class ResearchSystem
if (component.Server == null)
return false;
if (!TryComp<ResearchServerComponent>(component.Server, out var sc))
if (!TryComp(component.Server, out serverComponent))
return false;
server = component.Server;
serverComponent = sc;
return true;
}
}

View File

@@ -33,18 +33,17 @@ public sealed partial class ResearchSystem
ResearchConsoleBoundInterfaceState state;
if (TryGetClientServer(uid, out var server, out var serverComponent, clientComponent))
if (TryGetClientServer(uid, out _, out var serverComponent, clientComponent))
{
var points = clientComponent.ConnectedToServer ? serverComponent.Points : 0;
var pointsPerSecond = clientComponent.ConnectedToServer ? PointsPerSecond(server.Value, serverComponent) : 0;
state = new ResearchConsoleBoundInterfaceState(points, pointsPerSecond);
state = new ResearchConsoleBoundInterfaceState(points);
}
else
{
state = new ResearchConsoleBoundInterfaceState(default, default);
state = new ResearchConsoleBoundInterfaceState(default);
}
_uiSystem.TrySetUiState(component.Owner, ResearchConsoleUiKey.Key, state);
_uiSystem.TrySetUiState(uid, ResearchConsoleUiKey.Key, state);
}
private void OnPointsChanged(EntityUid uid, ResearchConsoleComponent component, ref ResearchServerPointsChangedEvent args)

View File

@@ -49,7 +49,7 @@ public sealed partial class ResearchSystem
if (!CanRun(uid))
return;
AddPointsToServer(uid, PointsPerSecond(uid, component) * time, component);
ModifyServerPoints(uid, GetPointsPerSecond(uid, component) * time, component);
}
/// <summary>
@@ -60,15 +60,14 @@ public sealed partial class ResearchSystem
/// <param name="clientComponent"></param>
/// <param name="serverComponent"></param>
/// <param name="dirtyServer">Whether or not to dirty the server component after registration</param>
/// <returns>Whether or not the client was successfully registered to the server</returns>
public bool RegisterClient(EntityUid client, EntityUid server, ResearchClientComponent? clientComponent = null,
public void RegisterClient(EntityUid client, EntityUid server, ResearchClientComponent? clientComponent = null,
ResearchServerComponent? serverComponent = null, bool dirtyServer = true)
{
if (!Resolve(client, ref clientComponent) || !Resolve(server, ref serverComponent))
return false;
return;
if (serverComponent.Clients.Contains(client))
return false;
return;
serverComponent.Clients.Add(client);
clientComponent.Server = server;
@@ -78,7 +77,6 @@ public sealed partial class ResearchSystem
var ev = new ResearchRegistrationChangedEvent(server);
RaiseLocalEvent(client, ref ev);
return true;
}
/// <summary>
@@ -130,7 +128,7 @@ public sealed partial class ResearchSystem
/// <param name="uid"></param>
/// <param name="component"></param>
/// <returns></returns>
public int PointsPerSecond(EntityUid uid, ResearchServerComponent? component = null)
public int GetPointsPerSecond(EntityUid uid, ResearchServerComponent? component = null)
{
var points = 0;
@@ -154,7 +152,7 @@ public sealed partial class ResearchSystem
/// <param name="uid">The server</param>
/// <param name="points">The amount of points being added</param>
/// <param name="component"></param>
public void AddPointsToServer(EntityUid uid, int points, ResearchServerComponent? component = null)
public void ModifyServerPoints(EntityUid uid, int points, ResearchServerComponent? component = null)
{
if (points == 0)
return;

View File

@@ -1,5 +1,6 @@
using Content.Shared.Research.Components;
using Content.Shared.Research.Prototypes;
using JetBrains.Annotations;
namespace Content.Server.Research.Systems;
@@ -13,13 +14,16 @@ public sealed partial class ResearchSystem
if (!Resolve(primaryUid, ref primaryDb) || !Resolve(otherUid, ref otherDb))
return;
primaryDb.TechnologyIds = otherDb.TechnologyIds;
primaryDb.RecipeIds = otherDb.RecipeIds;
primaryDb.MainDiscipline = otherDb.MainDiscipline;
primaryDb.CurrentTechnologyCards = otherDb.CurrentTechnologyCards;
primaryDb.SupportedDisciplines = otherDb.SupportedDisciplines;
primaryDb.UnlockedTechnologies = otherDb.UnlockedTechnologies;
primaryDb.UnlockedRecipes = otherDb.UnlockedRecipes;
Dirty(primaryDb);
var ev = new TechnologyDatabaseModifiedEvent();
RaiseLocalEvent(primaryDb.Owner, ref ev);
RaiseLocalEvent(primaryUid, ref ev);
}
/// <summary>
@@ -28,16 +32,15 @@ public sealed partial class ResearchSystem
/// syncs against the research server, and the server against the local database.
/// </summary>
/// <returns>Whether it could sync or not</returns>
public bool SyncClientWithServer(EntityUid uid, TechnologyDatabaseComponent? databaseComponent = null, ResearchClientComponent? clientComponent = null)
public void SyncClientWithServer(EntityUid uid, TechnologyDatabaseComponent? databaseComponent = null, ResearchClientComponent? clientComponent = null)
{
if (!Resolve(uid, ref databaseComponent, ref clientComponent, false))
return false;
return;
if (!TryComp<TechnologyDatabaseComponent>(clientComponent.Server, out var serverDatabase))
return false;
return;
Sync(uid, clientComponent.Server.Value, databaseComponent, serverDatabase);
return true;
}
/// <summary>
@@ -45,45 +48,49 @@ public sealed partial class ResearchSystem
/// </summary>
/// <returns>If the technology was successfully added</returns>
public bool UnlockTechnology(EntityUid client, string prototypeid, ResearchClientComponent? component = null,
TechnologyDatabaseComponent? databaseComponent = null)
TechnologyDatabaseComponent? clientDatabase = null)
{
if (!_prototypeManager.TryIndex<TechnologyPrototype>(prototypeid, out var prototype))
{
Logger.Error("invalid technology prototype");
if (!PrototypeManager.TryIndex<TechnologyPrototype>(prototypeid, out var prototype))
return false;
}
return UnlockTechnology(client, prototype, component, databaseComponent);
return UnlockTechnology(client, prototype, component, clientDatabase);
}
/// <summary>
/// Tries to add a technology to a database, checking if it is able to
/// </summary>
/// <returns>If the technology was successfully added</returns>
public bool UnlockTechnology(EntityUid client, TechnologyPrototype prototype, ResearchClientComponent? component = null,
TechnologyDatabaseComponent? databaseComponent = null)
public bool UnlockTechnology(EntityUid client,
TechnologyPrototype prototype,
ResearchClientComponent? component = null,
TechnologyDatabaseComponent? clientDatabase = null)
{
if (!Resolve(client, ref component, ref databaseComponent, false))
if (!Resolve(client, ref component, ref clientDatabase, false))
return false;
if (!CanUnlockTechnology(client, prototype, databaseComponent))
if (!TryGetClientServer(client, out var serverEnt, out _, component))
return false;
if (component.Server is not { } server)
if (!CanServerUnlockTechnology(client, prototype, clientDatabase, component))
return false;
AddTechnology(server, prototype.ID);
AddPointsToServer(server, -prototype.RequiredPoints);
AddTechnology(serverEnt.Value, prototype);
TrySetMainDiscipline(prototype, serverEnt.Value);
ModifyServerPoints(serverEnt.Value, -prototype.Cost);
UpdateTechnologyCards(serverEnt.Value);
return true;
}
/// <summary>
/// Adds a technology to the database without checking if it could be unlocked.
/// </summary>
[PublicAPI]
public void AddTechnology(EntityUid uid, string technology, TechnologyDatabaseComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
if (!_prototypeManager.TryIndex<TechnologyPrototype>(technology, out var prototype))
if (!PrototypeManager.TryIndex<TechnologyPrototype>(technology, out var prototype))
return;
AddTechnology(uid, prototype, component);
}
@@ -96,12 +103,19 @@ public sealed partial class ResearchSystem
if (!Resolve(uid, ref component))
return;
component.TechnologyIds.Add(technology.ID);
foreach (var unlock in technology.UnlockedRecipes)
//todo this needs to support some other stuff, too
foreach (var generic in technology.GenericUnlocks)
{
if (component.RecipeIds.Contains(unlock))
if (generic.PurchaseEvent != null)
RaiseLocalEvent(generic.PurchaseEvent);
}
component.UnlockedTechnologies.Add(technology.ID);
foreach (var unlock in technology.RecipeUnlocks)
{
if (component.UnlockedRecipes.Contains(unlock))
continue;
component.RecipeIds.Add(unlock);
component.UnlockedRecipes.Add(unlock);
}
Dirty(component);
@@ -113,16 +127,15 @@ public sealed partial class ResearchSystem
/// Adds a lathe recipe to the specified technology database
/// without checking if it can be unlocked.
/// </summary>
public void AddLatheRecipe(EntityUid uid, string recipe, TechnologyDatabaseComponent? component = null, bool dirty = true)
public void AddLatheRecipe(EntityUid uid, string recipe, TechnologyDatabaseComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
if (component.RecipeIds.Contains(recipe))
if (component.UnlockedRecipes.Contains(recipe))
return;
component.RecipeIds.Add(recipe);
if (dirty)
component.UnlockedRecipes.Add(recipe);
Dirty(component);
var ev = new TechnologyDatabaseModifiedEvent();
@@ -134,34 +147,36 @@ public sealed partial class ResearchSystem
/// taking parent technologies into account.
/// </summary>
/// <returns>Whether it could be unlocked or not</returns>
public bool CanUnlockTechnology(EntityUid uid, string technology, TechnologyDatabaseComponent? database = null, ResearchClientComponent? client = null)
public bool CanServerUnlockTechnology(EntityUid uid,
TechnologyPrototype technology,
TechnologyDatabaseComponent? database = null,
ResearchClientComponent? client = null)
{
if (!_prototypeManager.TryIndex<TechnologyPrototype>(technology, out var prototype))
return false;
return CanUnlockTechnology(uid, prototype, database, client);
}
/// <summary>
/// Returns whether a technology can be unlocked on this database,
/// taking parent technologies into account.
/// </summary>
/// <returns>Whether it could be unlocked or not</returns>
public bool CanUnlockTechnology(EntityUid uid, TechnologyPrototype technology, TechnologyDatabaseComponent? database = null, ResearchClientComponent? client = null)
{
if (!Resolve(uid, ref database, ref client))
if (!Resolve(uid, ref client, ref database, false))
return false;
if (!TryGetClientServer(uid, out _, out var serverComponent, client))
if (!TryGetClientServer(uid, out _, out var serverComp, client))
return false;
if (serverComponent.Points < technology.RequiredPoints)
if (!IsTechnologyAvailable(database, technology))
return false;
if (IsTechnologyUnlocked(uid, technology, database))
if (technology.Cost > serverComp.Points)
return false;
if (!ArePrerequesitesUnlocked(uid, technology, database))
return false;
return true;
}
private void OnDatabaseRegistrationChanged(EntityUid uid, TechnologyDatabaseComponent component, ref ResearchRegistrationChangedEvent args)
{
if (args.Server != null)
return;
component.MainDiscipline = null;
component.CurrentTechnologyCards = new List<string>();
component.SupportedDisciplines = new List<string>();
component.UnlockedTechnologies = new List<string>();
component.UnlockedRecipes = new List<string>();
Dirty(component);
}
}

View File

@@ -4,7 +4,6 @@ using Content.Shared.Research.Components;
using Content.Shared.Research.Systems;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Server.Research.Systems
@@ -13,7 +12,6 @@ namespace Content.Server.Research.Systems
public sealed partial class ResearchSystem : SharedResearchSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
public override void Initialize()
@@ -23,6 +21,8 @@ namespace Content.Server.Research.Systems
InitializeConsole();
InitializeSource();
InitializeServer();
SubscribeLocalEvent<TechnologyDatabaseComponent, ResearchRegistrationChangedEvent>(OnDatabaseRegistrationChanged);
}
/// <summary>

View File

@@ -51,7 +51,7 @@ public sealed class DiskConsoleSystem : EntitySystem
if (serverComp.Points < component.PricePerDisk)
return;
_research.AddPointsToServer(server.Value, -component.PricePerDisk, serverComp);
_research.ModifyServerPoints(server.Value, -component.PricePerDisk, serverComp);
_audio.PlayPvs(component.PrintSound, uid);
var printing = EnsureComp<DiskConsolePrintingComponent>(uid);

View File

@@ -38,12 +38,11 @@ public sealed class TechnologyDiskSystem : EntitySystem
{
foreach (var recipe in component.Recipes)
{
_research.AddLatheRecipe(target, recipe, database, false);
_research.AddLatheRecipe(target, recipe, database);
}
Dirty(database);
}
_popup.PopupEntity(Loc.GetString("tech-disk-inserted"), target, args.User);
EntityManager.DeleteEntity(uid);
Del(uid);
args.Handled = true;
}
@@ -71,7 +70,7 @@ public sealed class TechnologyDiskSystem : EntitySystem
var allTechs = new List<string>();
foreach (var tech in _prototype.EnumeratePrototypes<TechnologyPrototype>())
{
allTechs.AddRange(tech.UnlockedRecipes);
allTechs.AddRange(tech.RecipeUnlocks);
}
allTechs = allTechs.Distinct().ToList();
@@ -79,7 +78,7 @@ public sealed class TechnologyDiskSystem : EntitySystem
var allUnlocked = new List<string>();
foreach (var database in EntityQuery<TechnologyDatabaseComponent>())
{
allUnlocked.AddRange(database.RecipeIds);
allUnlocked.AddRange(database.UnlockedRecipes);
}
allUnlocked = allUnlocked.Distinct().ToList();

View File

@@ -361,7 +361,7 @@ public sealed class ArtifactAnalyzerSystem : EntitySystem
if (pointValue == 0)
return;
_research.AddPointsToServer(server.Value, pointValue, serverComponent);
_research.ModifyServerPoints(server.Value, pointValue, serverComponent);
_artifact.AdjustConsumedPoints(artifact.Value, pointValue);
_audio.PlayPvs(component.DestroySound, component.AnalyzerEntity.Value, AudioParams.Default.WithVolume(2f));

View File

@@ -2,26 +2,29 @@ using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Research.Components
{
[RegisterComponent, NetworkedComponent]
public sealed class ResearchServerComponent : Component
namespace Content.Shared.Research.Components;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ResearchServerComponent : Component
{
/// <summary>
/// The name of the server
/// </summary>
[DataField("servername"), ViewVariables(VVAccess.ReadWrite)]
[AutoNetworkedField]
[DataField("serverName"), ViewVariables(VVAccess.ReadWrite)]
public string ServerName = "RDSERVER";
/// <summary>
/// The amount of points on the server.
/// </summary>
[AutoNetworkedField]
[DataField("points"), ViewVariables(VVAccess.ReadWrite)]
public int Points;
/// <summary>
/// A unique numeric id representing the server
/// </summary>
[AutoNetworkedField]
[ViewVariables(VVAccess.ReadOnly)]
public int Id;
@@ -41,20 +44,6 @@ namespace Content.Shared.Research.Components
public readonly TimeSpan ResearchConsoleUpdateTime = TimeSpan.FromSeconds(1);
}
[Serializable, NetSerializable]
public sealed class ResearchServerState : ComponentState
{
public string ServerName;
public int Points;
public int Id;
public ResearchServerState(string serverName, int points, int id)
{
ServerName = serverName;
Points = points;
Id = id;
}
}
/// <summary>
/// Event raised on a server's clients when the point value of the server is changed.
/// </summary>
@@ -71,4 +60,4 @@ namespace Content.Shared.Research.Components
/// <param name="Points"></param>
[ByRefEvent]
public record struct ResearchServerGetPointsPerSecondEvent(EntityUid Server, int Points);
}

View File

@@ -35,11 +35,9 @@ namespace Content.Shared.Research.Components
public sealed class ResearchConsoleBoundInterfaceState : BoundUserInterfaceState
{
public int Points;
public int PointsPerSecond;
public ResearchConsoleBoundInterfaceState(int points, int pointsPerSecond)
public ResearchConsoleBoundInterfaceState(int points)
{
Points = points;
PointsPerSecond = pointsPerSecond;
}
}
}

View File

@@ -1,25 +1,47 @@
using Content.Shared.Research.Prototypes;
using Content.Shared.Research.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Research.Components
{
[RegisterComponent, NetworkedComponent]
public sealed class TechnologyDatabaseComponent : Component
namespace Content.Shared.Research.Components;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedResearchSystem)), AutoGenerateComponentState]
public sealed partial class TechnologyDatabaseComponent : Component
{
/// <summary>
/// A main discipline that locks out other discipline technology past a certain tier.
/// </summary>
[AutoNetworkedField]
[DataField("mainDiscipline", customTypeSerializer: typeof(PrototypeIdSerializer<TechDisciplinePrototype>))]
public string? MainDiscipline;
[AutoNetworkedField(true)]
[DataField("currentTechnologyCards")]
public List<string> CurrentTechnologyCards = new();
/// <summary>
/// Which research disciplines are able to be unlocked
/// </summary>
[AutoNetworkedField(true)]
[DataField("supportedDisciplines", customTypeSerializer: typeof(PrototypeIdListSerializer<TechDisciplinePrototype>))]
public List<string> SupportedDisciplines = new();
/// <summary>
/// The ids of all the technologies which have been unlocked.
/// </summary>
[DataField("technologyIds", customTypeSerializer: typeof(PrototypeIdListSerializer<TechnologyPrototype>))]
public List<string> TechnologyIds = new();
[AutoNetworkedField(true)]
[DataField("unlockedTechnologies", customTypeSerializer: typeof(PrototypeIdListSerializer<TechnologyPrototype>))]
public List<string> UnlockedTechnologies = new();
/// <summary>
/// The ids of all the lathe recipes which have been unlocked.
/// This is maintained alongside the TechnologyIds
/// </summary>
[DataField("recipeIds", customTypeSerializer: typeof(PrototypeIdListSerializer<LatheRecipePrototype>))]
public List<string> RecipeIds = new();
/// todo: if you unlock all the recipes in a tech, it doesn't count as unlocking the tech. sadge
[AutoNetworkedField(true)]
[DataField("unlockedRecipes", customTypeSerializer: typeof(PrototypeIdListSerializer<LatheRecipePrototype>))]
public List<string> UnlockedRecipes = new();
}
/// <summary>
@@ -32,17 +54,3 @@ namespace Content.Shared.Research.Components
/// </remarks>
[ByRefEvent]
public readonly record struct TechnologyDatabaseModifiedEvent;
[Serializable, NetSerializable]
public sealed class TechnologyDatabaseState : ComponentState
{
public List<string> Technologies;
public List<string> Recipes;
public TechnologyDatabaseState(List<string> technologies, List<string> recipes)
{
Technologies = technologies;
Recipes = recipes;
}
}
}

View File

@@ -0,0 +1,48 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared.Research.Prototypes;
/// <summary>
/// This is a prototype for a research discipline, a category
/// that governs how <see cref="TechnologyPrototype"/>s are unlocked.
/// </summary>
[Prototype("techDiscipline")]
public sealed class TechDisciplinePrototype : IPrototype
{
/// <inheritdoc/>
[IdDataField]
public string ID { get; } = default!;
/// <summary>
/// Player-facing name.
/// Supports locale strings.
/// </summary>
[DataField("name", required: true)]
public readonly string Name = string.Empty;
/// <summary>
/// A color used for UI
/// </summary>
[DataField("color", required: true)]
public readonly Color Color;
/// <summary>
/// An icon used to visually represent the discipline in UI.
/// </summary>
[DataField("icon")]
public readonly SpriteSpecifier Icon = default!;
/// <summary>
/// For each tier a discipline supports, what percentage
/// of the previous tier must be unlocked for it to become available
/// </summary>
[DataField("tierPrerequisites", required: true)]
public readonly Dictionary<int, float> TierPrerequisites = new();
/// <summary>
/// Purchasing this tier of technology causes a server to become "locked" to this discipline.
/// </summary>
[DataField("lockoutTier")]
public readonly int LockoutTier = 3;
}

View File

@@ -1,54 +1,92 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
using Robust.Shared.Utility;
namespace Content.Shared.Research.Prototypes
{
[NetSerializable, Serializable, Prototype("technology")]
namespace Content.Shared.Research.Prototypes;
/// <summary>
/// This is a prototype for a technology that can be unlocked.
/// </summary>
[Prototype("technology")]
public sealed class TechnologyPrototype : IPrototype
{
/// <summary>
/// The ID of this technology prototype.
/// </summary>
[ViewVariables]
/// <inheritdoc/>
[IdDataField]
public string ID { get; } = default!;
/// <summary>
/// The name this technology will have on user interfaces.
/// The name of the technology.
/// Supports locale strings
/// </summary>
[DataField("name")]
public string? Name { get; private set; }
[DataField("name", required: true)]
public readonly string Name = string.Empty;
/// <summary>
/// An icon that represent this technology.
/// An icon used to visually represent the technology in UI.
/// </summary>
[DataField("icon")]
public SpriteSpecifier Icon { get; } = SpriteSpecifier.Invalid;
[DataField("icon", required: true)]
public readonly SpriteSpecifier Icon = default!;
/// <summary>
/// A short description of the technology.
/// What research discipline this technology belongs to.
/// </summary>
[DataField("description")]
public string Description { get; private set; } = "";
[DataField("discipline", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<TechDisciplinePrototype>))]
public readonly string Discipline = default!;
/// <summary>
/// The required research points to unlock this technology.
/// What tier research is this?
/// The tier governs how much lower-tier technology
/// needs to be unlocked before this one.
/// </summary>
[DataField("requiredPoints")]
public int RequiredPoints { get; }
[DataField("tier", required: true)]
public readonly int Tier;
/// <summary>
/// A list of technology IDs required to unlock this technology.
/// Hidden tech is not ever available at the research console.
/// </summary>
[DataField("requiredTechnologies", customTypeSerializer: typeof(PrototypeIdListSerializer<TechnologyPrototype>))]
public List<string> RequiredTechnologies { get; } = new();
[DataField("hidden")]
public readonly bool Hidden;
/// <summary>
/// A list of recipe IDs this technology unlocks.
/// How much research is needed to unlock.
/// </summary>
[DataField("unlockedRecipes", customTypeSerializer:typeof(PrototypeIdListSerializer<LatheRecipePrototype>))]
public List<string> UnlockedRecipes { get; } = new();
[DataField("cost")]
public readonly int Cost = 10000;
/// <summary>
/// A list of <see cref="TechnologyPrototype"/>s that need to be unlocked in order to unlock this technology.
/// </summary>
[DataField("technologyPrerequisites", customTypeSerializer: typeof(PrototypeIdListSerializer<TechnologyPrototype>))]
public readonly IReadOnlyList<string> TechnologyPrerequisites = new List<string>();
/// <summary>
/// A list of <see cref="LatheRecipePrototype"/>s that are unlocked by this technology
/// </summary>
[DataField("recipeUnlocks", customTypeSerializer: typeof(PrototypeIdListSerializer<LatheRecipePrototype>))]
public readonly IReadOnlyList<string> RecipeUnlocks = new List<string>();
/// <summary>
/// A list of non-standard effects that are done when this technology is unlocked.
/// </summary>
[DataField("genericUnlocks")]
public readonly IReadOnlyList<GenericUnlock> GenericUnlocks = new List<GenericUnlock>();
}
[DataDefinition]
public record struct GenericUnlock()
{
/// <summary>
/// What event is raised when this is unlocked?
/// Used for doing non-standard logic.
/// </summary>
[DataField("purchaseEvent")]
public readonly object? PurchaseEvent = null;
/// <summary>
/// A player facing tooltip for what the unlock does.
/// Supports locale strings.
/// </summary>
[DataField("unlockDescription")]
public readonly string UnlockDescription = string.Empty;
}

View File

@@ -1,46 +1,145 @@
using Content.Shared.Research.Components;
using System.Linq;
using Content.Shared.Research.Components;
using Content.Shared.Research.Prototypes;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Shared.Research.Systems;
public abstract class SharedResearchSystem : EntitySystem
{
[Dependency] protected readonly IPrototypeManager PrototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ResearchServerComponent, ComponentGetState>(OnServerGetState);
SubscribeLocalEvent<ResearchServerComponent, ComponentHandleState>(OnServerHandleState);
SubscribeLocalEvent<TechnologyDatabaseComponent, ComponentGetState>(OnTechnologyGetState);
SubscribeLocalEvent<TechnologyDatabaseComponent, ComponentHandleState>(OnTechnologyHandleState);
SubscribeLocalEvent<TechnologyDatabaseComponent, MapInitEvent>(OnMapInit);
}
private void OnServerGetState(EntityUid uid, ResearchServerComponent component, ref ComponentGetState args)
private void OnMapInit(EntityUid uid, TechnologyDatabaseComponent component, MapInitEvent args)
{
args.State = new ResearchServerState(component.ServerName, component.Points, component.Id);
UpdateTechnologyCards(uid, component);
}
private void OnServerHandleState(EntityUid uid, ResearchServerComponent component, ref ComponentHandleState args)
public void UpdateTechnologyCards(EntityUid uid, TechnologyDatabaseComponent? component = null)
{
if (args.Current is not ResearchServerState state)
if (!Resolve(uid, ref component))
return;
component.ServerName = state.ServerName;
component.Points = state.Points;
component.Id = state.Id;
var availableTechnology = GetAvailableTechnologies(uid, component);
_random.Shuffle(availableTechnology);
component.CurrentTechnologyCards.Clear();
foreach (var discipline in component.SupportedDisciplines)
{
var selected = availableTechnology.FirstOrDefault(p => p.Discipline == discipline);
if (selected == null)
continue;
component.CurrentTechnologyCards.Add(selected.ID);
}
Dirty(component);
}
private void OnTechnologyHandleState(EntityUid uid, TechnologyDatabaseComponent component, ref ComponentHandleState args)
public List<TechnologyPrototype> GetAvailableTechnologies(EntityUid uid, TechnologyDatabaseComponent? component = null)
{
if (args.Current is not TechnologyDatabaseState state)
return;
component.TechnologyIds = new (state.Technologies);
component.RecipeIds = new(state.Recipes);
if (!Resolve(uid, ref component, false))
return new List<TechnologyPrototype>();
var availableTechnologies = new List<TechnologyPrototype>();
var disciplineTiers = GetDisciplineTiers(component);
foreach (var tech in PrototypeManager.EnumeratePrototypes<TechnologyPrototype>())
{
if (IsTechnologyAvailable(component, tech, disciplineTiers))
availableTechnologies.Add(tech);
}
private void OnTechnologyGetState(EntityUid uid, TechnologyDatabaseComponent component, ref ComponentGetState args)
return availableTechnologies;
}
public bool IsTechnologyAvailable(TechnologyDatabaseComponent component, TechnologyPrototype tech, Dictionary<string, int>? disciplineTiers = null)
{
args.State = new TechnologyDatabaseState(component.TechnologyIds, component.RecipeIds);
disciplineTiers ??= GetDisciplineTiers(component);
if (tech.Hidden)
return false;
if (!component.SupportedDisciplines.Contains(tech.Discipline))
return false;
if (tech.Tier > disciplineTiers[tech.Discipline])
return false;
if (component.UnlockedTechnologies.Contains(tech.ID))
return false;
foreach (var prereq in tech.TechnologyPrerequisites)
{
if (!component.UnlockedTechnologies.Contains(prereq))
return false;
}
return true;
}
public Dictionary<string, int> GetDisciplineTiers(TechnologyDatabaseComponent component)
{
var tiers = new Dictionary<string, int>();
foreach (var discipline in component.SupportedDisciplines)
{
tiers.Add(discipline, GetHighestDisciplineTier(component, discipline));
}
return tiers;
}
public int GetHighestDisciplineTier(TechnologyDatabaseComponent component, string disciplineId)
{
return GetHighestDisciplineTier(component, PrototypeManager.Index<TechDisciplinePrototype>(disciplineId));
}
public int GetHighestDisciplineTier(TechnologyDatabaseComponent component, TechDisciplinePrototype techDiscipline)
{
var allTech = PrototypeManager.EnumeratePrototypes<TechnologyPrototype>()
.Where(p => p.Discipline == techDiscipline.ID && !p.Hidden).ToList();
var allUnlocked = new List<TechnologyPrototype>();
foreach (var recipe in component.UnlockedTechnologies)
{
var proto = PrototypeManager.Index<TechnologyPrototype>(recipe);
if (proto.Discipline != techDiscipline.ID)
continue;
allUnlocked.Add(proto);
}
var highestTier = techDiscipline.TierPrerequisites.Keys.Max();
var tier = 2; //tier 1 is always given
// todo this might break if you have hidden technologies. i'm not sure
while (tier <= highestTier)
{
// we need to get the tech for the tier 1 below because that's
// what the percentage in TierPrerequisites is referring to.
var unlockedTierTech = allUnlocked.Where(p => p.Tier == tier - 1).ToList();
var allTierTech = allTech.Where(p => p.Discipline == techDiscipline.ID && p.Tier == tier - 1).ToList();
if (allTierTech.Count == 0)
break;
var percent = (float) unlockedTierTech.Count / allTierTech.Count;
if (percent < techDiscipline.TierPrerequisites[tier])
break;
if (tier >= techDiscipline.LockoutTier &&
component.MainDiscipline != null &&
techDiscipline.ID != component.MainDiscipline)
break;
tier++;
}
return tier - 1;
}
/// <summary>
@@ -58,26 +157,18 @@ public abstract class SharedResearchSystem : EntitySystem
/// <returns>Whether it is unlocked or not</returns>
public bool IsTechnologyUnlocked(EntityUid uid, string technologyId, TechnologyDatabaseComponent? component = null)
{
return Resolve(uid, ref component, false) && component.TechnologyIds.Contains(technologyId);
return Resolve(uid, ref component, false) && component.UnlockedTechnologies.Contains(technologyId);
}
/// <summary>
/// Returns whether or not all the prerequisite
/// technologies for a technology are unlocked.
/// </summary>
/// <param name="uid"></param>
/// <param name="prototype"></param>
/// <param name="component"></param>
/// <returns>Whether or not the prerequesites are present</returns>
public bool ArePrerequesitesUnlocked(EntityUid uid, TechnologyPrototype prototype, TechnologyDatabaseComponent? component = null)
public void TrySetMainDiscipline(TechnologyPrototype prototype, EntityUid uid, TechnologyDatabaseComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
foreach (var technologyId in prototype.RequiredTechnologies)
{
if (!IsTechnologyUnlocked(uid, technologyId, component))
return false;
}
return true;
return;
var discipline = PrototypeManager.Index<TechDisciplinePrototype>(prototype.Discipline);
if (prototype.Tier < discipline.LockoutTier)
return;
component.MainDiscipline = prototype.Discipline;
Dirty(component);
}
}

View File

@@ -1,105 +0,0 @@
technologies-basic-research-technology = Basic research technology
technologies-basic-research-technology-description = Nanotrasen basic research technologies.
technologies-cleaning-technology = Cleaning technology
technologies-cleaning-technology-description = Start to a shiny clean station.
technologies-advanced-cleaning-technology = Advanced cleaning technology
technologies-advanced-cleaning-technology-description = Advanced tools won't stop people from trashing the station, sadly.
technologies-advanced-spray-technology = Advanced spray technology
technologies-advanced-spray-technology-description = The newest ways to hose down the station. Filthy animals.
technologies-foodbev-technology = Food and beverage technology
technologies-food-and-beverage-technology-description = Robust service from better technology.
technologies-biological-technology = Biological technology
technologies-biological-technology-description = Investigations into the natural world.
technologies-advanced-botany = Advanced botany
technologies-advanced-botany-description = A better understanding of botany.
technologies-virology = Virology
technologies-virology-description = The secrets of the immune system.
technologies-advanced-surgery = Advanced surgery
technologies-advanced-surgery-description = Research new surgical procedures.
technologies-chemistry-technology = Chemistry technology
technologies-chemistry-technology-description = A crash course in chemistry.
technologies-medical-machinery = Medical machinery
technologies-medical-machinery-description = Machines any self-respecting medbay would need.
technologies-advanced-life-support = Advanced life support systems
technologies-advanced-life-support-description = The cutting edge of life and death.
technologies-salvage-equipment = Salvage equipment
technologies-salvage-equipment-description = Newer and faster resource collection.
technologies-spacefaring = Spacefaring technology
technologies-spacefaring-description = Able to bring you into the stars!
technologies-surveillance = Surveillance technology
technologies-surveillance-description = Retro-styled cameras straight from the year 1984!
technologies-industrial-engineering = Industrial engineering
technologies-industrial-engineering-description = A refresher course on modern engineering technology.
technologies-rapid-upgrade = Rapid upgrade
technologies-rapid-upgrade-description = The ability to quickly improve the station like never before.
technologies-material-sheet-printing = Material sheet printing
technologies-material-sheet-printing-description = Print those sheets!
technologies-electromagnetic-theory = Electromagnetic theory
technologies-electromagnetic-theory-description = Try not to fry yourself.
technologies-electrical-engineering = Electrical engineering
technologies-electrical-engineering-description = Machinery used to keep the station stable.
technologies-advanced-atmospherics-technology = Advanced atmospherics technology
technologies-advanced-atmospherics-technology-description = As if it can get more advanced.
technologies-compact-power-technology = Compact power technology
technologies-compact-power-technology-description = Power, but smaller.
technologies-applied-musicology = Applied musicology
technologies-applied-musicology-description = Bringing you the latest in audio-audio technology.
technologies-basic-powercell-printing = Basic powercell printing
technologies-basic-powercell-printing-description = Print standard powercells.
technologies-advanced-powercell-printing = Advanced powercell printing
technologies-advanced-powercell-printing-description = Print advanced powercells.
technologies-super-powercell-printing = Super powercell printing
technologies-super-powercell-printing-description = Print super powercells.
technologies-scientific-technology = Scientific technology
technologies-scientific-technology-description = The basics of a research team's supplies.
technologies-anomaly-technology = Anomaly technology
technologies-anomaly-technology-description = Machines for advanced anomaly containment.
technologies-robotics-technology = Robotics technology
technologies-robotics-technology-description = Parts needed for constructing mechanized friends.
technologies-archaeology = Archeological equipment
technologies-archaeology-description = Advanced equipment for uncovering the secrets of artifacts.
technologies-ripley-technology = Exosuit: Ripley
technologies-ripley-technology-description = The latest and greatest in mechanized cargo construction.
technologies-clown-technology = Exosuit: H.O.N.K.
technologies-clown-technology-description = Honk?!
technologies-adv-parts-technology-description = Like the previous ones, but better!
technologies-adv-parts-technology = Advanced parts technology
technologies-super-parts-technology = Super parts technology
technologies-super-parts-technology-description = New heights of machine performance.
technologies-magboots-technology = Magboots technology
technologies-magboots-technology-description = Magboots for a space escape from the assistant.

View File

@@ -1,13 +1,16 @@
## UI
research-console-menu-title = R&D Console
research-console-menu-unlocked-technologies-label = Unlocked technologies
research-console-menu-unlockable-technologies-label = Unlockable technologies
research-console-menu-future-technologies-label = Future technologies
research-console-menu-research-points-text = Research Points: {$points}
research-console-menu-points-per-second-text = Points per Second {$pointsPerSecond}
research-console-menu-research-points-text = Research: [color=orchid]{$points}[/color]
research-console-menu-main-discipline = Main Discipline: [color={$color}]{$name}[/color]
research-console-menu-server-selection-button = Server list
research-console-menu-server-sync-button = Sync
research-console-menu-server-unlock-button = Unlock
research-console-tech-requirements-none = No technology requirements.
research-console-tech-requirements-prototype-name = Requires: {$prototypeName}
research-console-menu-server-research-button = Research
research-console-available-text = Researchable Technologies
research-console-unlocked-text = Unlocked Technologies
research-console-tier-discipline-info = Tier {$tier}, [color={$color}]{$discipline}[/color]
research-console-tier-info-small = : Tier {$tier}
research-console-cost = Cost: [color=orchid]{$amount}[/color]
research-console-unlocks-list-start = Unlocks:
research-console-unlocks-list-entry = - [color=yellow]{$name}[/color]
research-console-unlocks-list-entry-generic = - [color=green]{$text}[/color]

View File

@@ -0,0 +1,51 @@
research-discipline-none = None
research-discipline-industrial = Industrial
research-discipline-biochemical = Biochemical
research-discipline-experimental = Experimental
research-discipline-civilian-services = Civilian Services
research-technology-salvage-equipment = Salvage Equipment
research-technology-advanced-powercells = Advanced Powercells
research-technology-compact-power = Compact Power
research-technology-industrial-engineering = Industrial Engineering
research-technology-power-generation = Power Generation
research-technology-atmospheric-tech = Atmospherics
research-technology-rapid-construction = Rapid Construction
research-technology-shuttlecraft = Shuttlecraft
research-technology-ripley-aplu = Ripley APLU
research-technology-advanced-atmospherics = Advanced Atmospherics
research-technology-super-powercells = Super Powercells
research-technology-bluespace-storage = Bluespace Storage
research-technology-chemistry = Chemistry
research-technology-surgical-tools = Surgical Tools
research-technology-biochemical-stasis = Biochemical Stasis
research-technology-virology = Virology
research-technology-cryogenics = Cryogenics
research-technology-chemical-dispensary = Chemical Dispensary
research-technology-crew-monitoring = Crew Monitoring
research-technology-cloning = Cloning
research-technology-basic-robotics = Basic Robotics
research-technology-signalling-tech = Signalling Tech
research-technology-basic-anomalous-research = Basic Anomalous Research
research-technology-basic-xenoarcheology = Basic XenoArcheology
research-technology-alternative-research = Alternative Research
research-technology-magnets-tech = Localized Magnetism
research-technology-advanced-parts = Advanced Parts
research-technology-abnormal-artifact-manipulation = Abnormal Artifact Manipulation
research-technology-mobile-anomaly-tech = Mobile Anomaly Tech
research-technology-rped = Rapid Part Exchange
research-technology-super-parts = Super Parts
research-technology-janitorial-equipment = Janitorial Equipment
research-technology-laundry-tech = Laundry Tech
research-technology-basic-hydroponics = Basic Hydroponics
research-technology-food-service = Food Service
research-technology-advanced-entertainment = Advanced Entertainment
research-technology-audio-visual-communication = A/V Communication
research-technology-advanced-cleaning = Advanced Cleaning
research-technology-meat-manipulation = Meat Manipulation
research-technology-honk-mech = H.O.N.K. Mech
research-technology-advanced-spray = Advanced Spray

View File

@@ -1,655 +0,0 @@
# In order to make this list somewhat organized, please place
# new technologies underneath their overarching "base" technology.
# Base Technology
- type: technology
name: technologies-basic-research-technology
id: BasicResearch
description: technologies-basic-research-technology-description
icon:
sprite: Structures/Machines/server.rsi
state: server-on
requiredPoints: 2500
# Cleaning Technology Tree
- type: technology
name: technologies-cleaning-technology
id: CleaningTechnology
description: technologies-cleaning-technology-description
icon:
sprite: Objects/Specific/Janitorial/janitorial.rsi
state: mopbucket
requiredPoints: 5000
requiredTechnologies:
- BasicResearch
unlockedRecipes:
- Bucket
- MopItem
- SprayBottle
- type: technology
name: technologies-advanced-cleaning-technology
id: AdvancedCleaningTechnology
description: technologies-advanced-cleaning-technology-description
icon:
sprite: Objects/Specific/Janitorial/advmop.rsi
state: advmop
requiredPoints: 5000
requiredTechnologies:
- CleaningTechnology
unlockedRecipes:
- AdvMopItem
- MegaSprayBottle
- type: technology
id: AdvancedSprayTechnology
name: technologies-advanced-spray-technology
description: technologies-advanced-spray-technology-description
icon:
sprite: Objects/Weapons/Guns/Basic/spraynozzle.rsi
state: icon
requiredPoints: 7500
requiredTechnologies:
- AdvancedCleaningTechnology
unlockedRecipes:
- WeaponSprayNozzle
- ClothingBackpackWaterTank
# Food/Bev Service Technology Tree
- type: technology
name: technologies-foodbev-technology
id: FoodBevTechnology
description: technologies-food-and-beverage-technology-description
icon:
sprite: Objects/Weapons/Melee/cleaver.rsi
state: butch
requiredPoints: 5000
requiredTechnologies:
- BasicResearch
unlockedRecipes:
- ButchCleaver
- KitchenKnife
- MicrowaveMachineCircuitboard
- BoozeDispenserMachineCircuitboard
- SodaDispenserMachineCircuitboard
- FatExtractorMachineCircuitboard
# Biological Technology Tree
- type: technology
name: technologies-biological-technology
id: BiologicalTechnology
description: technologies-biological-technology-description
icon:
sprite: Structures/Furniture/potted_plants.rsi
state: applebush
requiredPoints: 10000
requiredTechnologies:
- BasicResearch
unlockedRecipes:
- HydroponicsToolScythe
- HydroponicsToolHatchet
- Shovel
- MiniHoe
- Spade
- Clippers
- ButchCleaver
- KitchenKnife
- MicrowaveMachineCircuitboard
- type: technology
name: technologies-advanced-botany
id: AdvancedBotany
description: technologies-advanced-botany-description
icon:
sprite: Objects/Specific/Hydroponics/potato.rsi
state: seed
requiredPoints: 15000
requiredTechnologies:
- BiologicalTechnology
unlockedRecipes:
- SeedExtractorMachineCircuitboard
- HydroponicsTrayMachineCircuitboard
- Vape
- type: technology
name: technologies-virology
id: Virology
description: technologies-virology-description
icon:
sprite: Clothing/Mask/sterile.rsi
state: icon
requiredPoints: 10000
requiredTechnologies:
- BiologicalTechnology
unlockedRecipes:
- VaccinatorMachineCircuitboard
- DiagnoserMachineCircuitboard
# Medical Technology Tree
- type: technology
name: technologies-chemistry-technology
id: ChemistryTechnology
description: technologies-chemistry-technology-description
icon:
sprite: Objects/Specific/Chemistry/beaker_large.rsi
state: beakerlarge
requiredPoints: 10000
requiredTechnologies:
- BasicResearch
unlockedRecipes:
- Beaker
- LargeBeaker
- CryostasisBeaker
- Dropper
- Syringe
- ReagentGrinderMachineCircuitboard
- HotplateMachineCircuitboard
- PillCanister
- ChemistryEmptyBottle01
- ChemicalPayload
- type: technology
name: technologies-advanced-surgery
id: AdvancedSugery
description: technologies-advanced-surgery-description
icon:
sprite: Objects/Specific/Medical/Surgery/saw.rsi
state: saw
requiredPoints: 7500
requiredTechnologies:
- BiologicalTechnology
unlockedRecipes:
- Scalpel
- Retractor
- Cautery
- Drill
- Saw
- Hemostat
- type: technology
name: technologies-medical-machinery
id: MedicalMachinery
description: technologies-medical-machinery-description
icon:
sprite: Structures/dispensers.rsi
state: industrial-working
requiredPoints: 10000
requiredTechnologies:
- BiologicalTechnology
- ChemistryTechnology
unlockedRecipes:
- ChemMasterMachineCircuitboard
- ChemDispenserMachineCircuitboard
- HandheldCrewMonitor
- BiomassReclaimerMachineCircuitboard
- type: technology
name: technologies-advanced-life-support
id: AdvancedLifeSupport
description: technologies-advanced-life-support-description
icon:
sprite: Structures/Machines/cloning.rsi
state: pod_0
requiredPoints: 20000
requiredTechnologies:
- MedicalMachinery
- Virology
unlockedRecipes:
- CloningPodMachineCircuitboard
- MedicalScannerMachineCircuitboard
- StasisBedMachineCircuitboard
- CloningConsoleComputerCircuitboard
- CryoPodMachineCircuitboard
# Security Technology Tree
- type: technology
name: technologies-surveillance
id: SurveillanceTechnology
description: technologies-surveillance-description
icon:
sprite: Structures/Wallmounts/camera.rsi
state: cameracase
requiredPoints: 12500
requiredTechnologies:
- BasicResearch
unlockedRecipes:
- SurveillanceCameraRouterCircuitboard
- SurveillanceCameraWirelessRouterCircuitboard
- SurveillanceWirelessCameraMovableCircuitboard
- SurveillanceWirelessCameraAnchoredCircuitboard
- SurveillanceCameraMonitorCircuitboard
- SurveillanceWirelessCameraMonitorCircuitboard
- ComputerTelevisionCircuitboard
#- type: technology
# name: "ballistic technology"
# id: BallisticTechnology
# description: Just a fancy term for guns.
# icon:
# sprite: Objects/Weapons/Guns/Pistols/mk58.rsi
# state: icon
# requiredPoints: 15000
# requiredTechnologies:
# - SecurityTechnology
#
# - type: technology
# name: "direct energy technology"
# id: DirectEnergyTechnology
# description: Basically laser guns.
# icon:
# sprite: Objects/Weapons/Guns/Battery/taser.rsi
# state: icon
# requiredPoints: 15000
# requiredTechnologies:
# - SecurityTechnology
#
# - type: technology
# name: "explosives technology"
# id: ExplosivesTechnology
# description: Let's just start with grenades for now.
# icon:
# sprite: Objects/Weapons/Grenades/flashbang.rsi
# state: icon
# requiredPoints: 15000
# requiredTechnologies:
# - SecurityTechnology
#
# - type: technology
# name: "armor technology"
# id: ArmorTechnology
# description: Basic protective gear for security personnel.
# icon:
# sprite: Clothing/OuterClothing/Vests/kevlar.rsi
# state: icon
# requiredPoints: 15000
# requiredTechnologies:
# - SecurityTechnology
# Salvage Technology Tree
- type: technology
name: technologies-salvage-equipment
id: SalvageEquipment
description: technologies-salvage-equipment-description
icon:
sprite: Objects/Tools/handdrill.rsi
state: handdrill
requiredPoints: 7500
requiredTechnologies:
- BasicResearch
unlockedRecipes:
- AppraisalTool
- MiningDrill
- OreProcessorMachineCircuitboard
- type: technology
name: technologies-spacefaring
id: SpacefaringTechnology
description: technologies-spacefaring-description
icon:
sprite: Structures/Shuttles/gyroscope.rsi
state: base
requiredPoints: 10000
requiredTechnologies:
- SalvageEquipment
- IndustrialEngineering
unlockedRecipes:
- ShuttleConsoleCircuitboard
- RadarConsoleCircuitboard
- ThrusterMachineCircuitboard
- GyroscopeMachineCircuitboard
- type: technology
name: technologies-ripley-technology
id: RipleyTechnology
description: technologies-ripley-technology-description
icon:
sprite: Objects/Specific/Mech/mecha.rsi
state: ripley
requiredPoints: 30000
requiredTechnologies:
- SalvageEquipment
unlockedRecipes:
- RipleyCentralElectronics
- RipleyPeripheralsElectronics
- MechEquipmentGrabber
- RipleyHarness
- RipleyLArm
- RipleyRArm
- RipleyLLeg
- RipleyRLeg
- type: technology
name: technologies-clown-technology
id: ClownTechnology
description: technologies-clown-technology-description
icon:
sprite: Objects/Specific/Mech/mecha.rsi
state: honker
requiredPoints: 15000
requiredTechnologies:
- RipleyTechnology
unlockedRecipes:
- HonkerCentralElectronics
- HonkerPeripheralsElectronics
- HonkerTargetingElectronics
- MechEquipmentHorn
- HonkerHarness
- HonkerLArm
- HonkerRArm
- HonkerLLeg
- HonkerRLeg
# Industrial Engineering Technology Tree
- type: technology
name: technologies-industrial-engineering
id: IndustrialEngineering
description: technologies-industrial-engineering-description
icon:
sprite: Structures/Machines/protolathe.rsi
state: icon
requiredPoints: 10000
requiredTechnologies:
- BasicResearch
unlockedRecipes:
- IntercomElectronics
- ConveyorBeltAssembly
- FlashlightLantern
- FireExtinguisher
- FirelockElectronics
- DoorElectronics
- AutolatheMachineCircuitboard
- ProtolatheMachineCircuitboard
- CircuitImprinterMachineCircuitboard
- UniformPrinterMachineCircuitboard
- AirAlarmElectronics
- FireAlarmElectronics
- MailingUnitElectronics
- SignalTimerElectronics
- type: technology
name: technologies-material-sheet-printing
id: Sheets
description: technologies-material-sheet-printing-description
icon: Objects/Materials/Sheets/researchicon.png
requiredPoints: 5000
requiredTechnologies:
- IndustrialEngineering
unlockedRecipes:
- SheetSteel
- SheetPlastic
- SheetRGlass
- SheetGlass1
- MaterialReclaimerMachineCircuitboard
# Electromagnetic Theory Technology Tree
- type: technology
name: technologies-electromagnetic-theory
id: ElectromagneticTheory
description: technologies-electromagnetic-theory-description
icon:
sprite: Structures/Power/apc.rsi
state: base
requiredPoints: 10000
requiredTechnologies:
- BasicResearch
unlockedRecipes:
- CableStack
- CableMVStack
- CableHVStack
- LightBulb
- LightTube
- APCElectronics
- SubstationMachineCircuitboard
- Signaller
- SignalTrigger
- VoiceTrigger
- TimerTrigger
- TelecomServerCircuitboard
- type: technology
name: technologies-electrical-engineering
id: ElectricalEngineering
description: technologies-electrical-engineering-description
icon:
sprite: Structures/Power/Generation/Singularity/emitter.rsi
state: emitter1
requiredPoints: 15000
requiredTechnologies:
- ElectromagneticTheory
- IndustrialEngineering
unlockedRecipes:
- PowerDrill
- SMESMachineCircuitboard
- PowerComputerCircuitboard
- GeneratorPlasmaMachineCircuitboard
- GeneratorUraniumMachineCircuitboard
- SolarControlComputerCircuitboard
- EmitterCircuitboard
- type: technology
name: technologies-advanced-atmospherics-technology
id: AdvancedAtmosTechnology
description: technologies-advanced-atmospherics-technology-description
icon:
sprite: Structures/Piping/Atmospherics/thermomachine.rsi
state: freezerOff
requiredPoints: 7500
requiredTechnologies:
- ElectricalEngineering
unlockedRecipes:
- HolofanProjector
- ThermomachineFreezerMachineCircuitBoard
- PortableScrubberMachineCircuitBoard
- GasRecyclerMachineCircuitboard
- type: technology
name: technologies-rapid-upgrade
id: RapidUpgrade
description: technologies-rapid-upgrade-description
icon:
sprite: Objects/Specific/Research/rped.rsi
state: icon
requiredPoints: 10000
requiredTechnologies:
- ElectricalEngineering
unlockedRecipes:
- RCD
- RCDAmmo
- RPED
- type: technology
name: technologies-compact-power-technology
id: CompactPowerTechnology
description: technologies-compact-power-technology-description
icon:
sprite: Structures/Power/Generation/wallmount_generator.rsi
state: panel
requiredPoints: 5000
requiredTechnologies:
- ElectromagneticTheory
unlockedRecipes:
- WallmountSubstationElectronics
- WallmountGeneratorElectronics
- WallmountGeneratorAPUElectronics
- type: technology
name: technologies-basic-powercell-printing
id: PowerCellBasic
description: technologies-basic-powercell-printing-description
icon:
sprite: Objects/Power/power_cells.rsi
state: small
requiredPoints: 2500
requiredTechnologies:
- ElectromagneticTheory
unlockedRecipes:
- PowerCellSmall
- type: technology
name: technologies-advanced-powercell-printing
id: PowerCellAdvanced
description: technologies-advanced-powercell-printing-description
icon:
sprite: Objects/Power/power_cells.rsi
state: medium
requiredPoints: 5000
requiredTechnologies:
- PowerCellBasic
unlockedRecipes:
- PowerCellMedium
- type: technology
name: technologies-super-powercell-printing
id: PowerCellSuper
description: technologies-super-powercell-printing-description
icon:
sprite: Objects/Power/power_cells.rsi
state: high
requiredPoints: 7500
requiredTechnologies:
- PowerCellAdvanced
unlockedRecipes:
- PowerCellHigh
# Entertainment Technology Tree
- type: technology
name: technologies-applied-musicology
id: AppliedMusicology
description: technologies-applied-musicology-description
icon:
sprite: Objects/Fun/Instruments/h_synthesizer.rsi
state: icon
requiredPoints: 5000
requiredTechnologies:
- BasicResearch
unlockedRecipes:
- SynthesizerInstrument
- DawInstrumentMachineCircuitboard
# Science Technology Tree
- type: technology
name: technologies-scientific-technology
id: ScientificTechnology
description: technologies-scientific-technology-description
icon:
sprite: Objects/Misc/stock_parts.rsi
state: micro_mani
requiredPoints: 5000
requiredTechnologies:
- BasicResearch
unlockedRecipes:
- TechDiskComputerCircuitboard
- CapacitorStockPart
- MatterBinStockPart
- MicroManipulatorStockPart
- NodeScanner
- AnomalyScanner
- AnomalyLocator
- type: technology
name: technologies-anomaly-technology
id: AnomalyTechnology
description: technologies-anomaly-technology-description
icon:
sprite: Structures/Machines/Anomaly/ape.rsi
state: base
requiredPoints: 10000
requiredTechnologies:
- ScientificTechnology
unlockedRecipes:
- AnomalyVesselCircuitboard
- APECircuitboard
- WeaponPistolCHIMP
- CartridgeAnomalousParticleDelta
- CartridgeAnomalousParticleEpsilon
- CartridgeAnomalousParticleZeta
- type: technology
name: technologies-robotics-technology
id: RoboticsTechnology
description: technologies-robotics-technology-description
icon:
sprite: Mobs/Silicon/Bots/honkbot.rsi
state: honkbot
requiredPoints: 7500
requiredTechnologies:
- ScientificTechnology
unlockedRecipes:
- ProximitySensor
- LeftArmBorg
- LightHeadBorg
- RightArmBorg
- LeftLegBorg
- RightLegBorg
- Drone
- ExosuitFabricatorMachineCircuitboard
- type: technology
name: technologies-archaeology
id: ArchaeologicalEquipment
description: technologies-archaeology-description
icon:
sprite: Objects/Specific/Xenoarchaeology/xeno_artifacts.rsi
state: ano01
requiredPoints: 10000
requiredTechnologies:
- ScientificTechnology
unlockedRecipes:
- AnalysisComputerCircuitboard
- ArtifactAnalyzerMachineCircuitboard
- TraversalDistorterMachineCircuitboard
- type: technology
name: technologies-adv-parts-technology
id: AdvancedPartsTechnology
description: technologies-adv-parts-technology-description
icon:
sprite: Objects/Misc/stock_parts.rsi
state: adv_capacitor
requiredPoints: 15000
requiredTechnologies:
- ScientificTechnology
unlockedRecipes:
- AdvancedCapacitorStockPart
- AdvancedMatterBinStockPart
- NanoManipulatorStockPart
- type: technology
name: technologies-magboots-technology
id: MagbootsTechnology
description: technologies-magboots-technology-description
icon:
sprite: Clothing/Shoes/Boots/magboots.rsi
state: icon
requiredPoints: 8500
requiredTechnologies:
- ScientificTechnology
unlockedRecipes:
- ClothingShoesBootsMag
- type: technology
name: technologies-super-parts-technology
id: SuperPartsTechnology
description: technologies-super-parts-technology-description
icon:
sprite: Objects/Misc/stock_parts.rsi
state: super_capacitor
requiredPoints: 20000
requiredTechnologies:
- AdvancedPartsTechnology
- CompactPowerTechnology
unlockedRecipes:
- SuperCapacitorStockPart
- SuperMatterBinStockPart
- PicoManipulatorStockPart

View File

@@ -76,12 +76,37 @@
- Multitool
- NetworkConfigurator
- AirlockPainter
- FlashlightLantern
- CableStack
- CableMVStack
- CableHVStack
- HandheldGPSBasic
- TRayScanner
- GasAnalyzer
- UtilityBelt
- Pickaxe
- AppraisalTool
- SheetRGlass
- Beaker
- Syringe
- LightTube
- LightBulb
- Bucket
- SprayBottle
- PowerCellSmall
- MicroManipulatorStockPart
- MatterBinStockPart
- CapacitorStockPart
- ConveyorBeltAssembly
- IntercomElectronics
- FirelockElectronics
- DoorElectronics
- AirAlarmElectronics
- FireAlarmElectronics
- MailingUnitElectronics
- APCElectronics
- SMESMachineCircuitboard
- SubstationMachineCircuitboard
- type: StaticPrice
price: 800
@@ -159,29 +184,12 @@
idleState: icon
runningState: building
dynamicRecipes:
- LightTube
- LightBulb
- SheetSteel #these sheet recipe costs don't scale with upgrades
- SheetGlass1
- SheetRGlass
- SheetPlastic
- CableStack
- CableMVStack
- CableHVStack
- PowerDrill
- MiningDrill
- ConveyorBeltAssembly
- AppraisalTool
- AnomalyScanner
- AnomalyLocator
- RCD
- RCDAmmo
- HydroponicsToolScythe
- HydroponicsToolHatchet
- Clippers
- MiniHoe
- Shovel
- Spade
- HandheldCrewMonitor
- Scalpel
- Retractor
@@ -198,42 +206,28 @@
- PillCanister
- ChemistryEmptyBottle01
- Drone
- Flash
- MicroManipulatorStockPart
- MatterBinStockPart
- CapacitorStockPart
- AdvancedCapacitorStockPart
- AdvancedMatterBinStockPart
- NanoManipulatorStockPart
- SuperCapacitorStockPart
- SuperMatterBinStockPart
- PicoManipulatorStockPart
- FirelockElectronics
- DoorElectronics
- APCElectronics
- AirAlarmElectronics
- FireAlarmElectronics
- IntercomElectronics
- MailingUnitElectronics
- SignalTimerElectronics
- Bucket
- MopItem
- Holoprojector
- Mousetrap
- LightReplacer
- TrashBag
- AdvMopItem
- WeaponSprayNozzle
- ClothingBackpackWaterTank
- SprayBottle
- MegaSprayBottle
- FireExtinguisher
- KitchenKnife
- ButchCleaver
- FlashlightLantern
- TimerTrigger
- ChemicalPayload
- FlashPayload
- Signaller
- SignalTrigger
- VoiceTrigger
- PowerCellSmall
- PowerCellMedium
- PowerCellHigh
- WeaponPistolCHIMP
@@ -245,7 +239,9 @@
- ClothingShoesBootsMag
- NodeScanner
- HolofanProjector
- Vape
- ClothingBackpackHolding
- ClothingBackpackSatchelHolding
- ClothingBackpackDuffelHolding
- type: entity
parent: Protolathe
@@ -271,16 +267,7 @@
idleState: icon
runningState: building
dynamicRecipes:
- FirelockElectronics
- DoorElectronics
- APCElectronics
- AirAlarmElectronics
- FireAlarmElectronics
- MailingUnitElectronics
- IntercomElectronics
- SignalTimerElectronics
- SMESMachineCircuitboard
- SubstationMachineCircuitboard
- ThermomachineFreezerMachineCircuitBoard
- PortableScrubberMachineCircuitBoard
- CloningPodMachineCircuitboard
@@ -492,16 +479,7 @@
- ClothingHandsGlovesLatex
- ClothingMaskSterile
- DiseaseSwab
- Scalpel
- Retractor
- Cautery
- Drill
- Saw
- Hemostat
- Beaker
- LargeBeaker
- CryostasisBeaker
- Dropper
- Syringe
- Implanter
- PillCanister
@@ -518,6 +496,15 @@
dynamicRecipes:
- HandheldCrewMonitor
- ClothingHandsGlovesNitrile
- CryostasisBeaker
- LargeBeaker
- Dropper
- Scalpel
- Retractor
- Cautery
- Drill
- Saw
- Hemostat
- type: Machine
board: MedicalTechFabCircuitboard

View File

@@ -9,6 +9,11 @@
state: server
- type: ResearchServer
- type: TechnologyDatabase
supportedDisciplines:
- Industrial
- Biochemical
- Experimental
- CivilianServices
- type: ApcPowerReceiver
powerLoad: 200
priority: Low

View File

@@ -72,3 +72,30 @@
materials:
Steel: 500
Glass: 400
- type: latheRecipe
id: ClothingBackpackHolding
result: ClothingBackpackHolding
completetime: 5
materials:
Steel: 1500
Plastic: 750
Plasma: 1000
- type: latheRecipe
id: ClothingBackpackSatchelHolding
result: ClothingBackpackSatchelHolding
completetime: 5
materials:
Steel: 1500
Plastic: 750
Plasma: 1000
- type: latheRecipe
id: ClothingBackpackDuffelHolding
result: ClothingBackpackDuffelHolding
completetime: 5
materials:
Steel: 1500
Plastic: 750
Plasma: 1000

View File

@@ -56,6 +56,22 @@
Steel: 100
Glass: 500
- type: latheRecipe
id: Mousetrap
result: Mousetrap
completetime: 1
materials:
Wood: 100
Steel: 50
- type: latheRecipe
id: Holoprojector
result: Holoprojector
completetime: 3
materials:
Plastic: 250
Glass: 150
- type: latheRecipe
id: AdvMopItem
result: AdvMopItem

View File

@@ -0,0 +1,117 @@
# Tier 1
- type: technology
id: Chemistry
name: research-technology-chemistry
icon:
sprite: Objects/Specific/Chemistry/beaker_large.rsi
state: beakerlarge
discipline: Biochemical
tier: 1
cost: 10000
recipeUnlocks:
- LargeBeaker
- Dropper
- HotplateMachineCircuitboard
- ChemicalPayload
- type: technology
id: SurgicalTools
name: research-technology-surgical-tools
icon:
sprite: Objects/Specific/Medical/Surgery/saw.rsi
state: saw
discipline: Biochemical
tier: 1
cost: 5000
recipeUnlocks:
- Scalpel
- Retractor
- Cautery
- Drill
- Saw
- Hemostat
- type: technology
id: BiochemicalStasis
name: research-technology-biochemical-stasis
icon:
sprite: Structures/Machines/stasis_bed.rsi
state: icon
discipline: Biochemical
tier: 1
cost: 10000
recipeUnlocks:
- CryostasisBeaker
- StasisBedMachineCircuitboard
- type: technology
id: Virology
name: research-technology-virology
icon:
sprite: Structures/Machines/diagnoser.rsi
state: icon
discipline: Biochemical
tier: 1
cost: 5000
recipeUnlocks:
- VaccinatorMachineCircuitboard
- DiagnoserMachineCircuitboard
# Tier 2
- type: technology
id: Cryopod
name: research-technology-cryogenics
icon:
sprite: Structures/Machines/cryogenics.rsi
state: pod-on
discipline: Biochemical
tier: 2
cost: 7500
recipeUnlocks:
- CryoPodMachineCircuitboard
- type: technology
id: ChemicalDispensary
name: research-technology-chemical-dispensary
icon:
sprite: Structures/dispensers.rsi
state: industrial-working
discipline: Biochemical
tier: 2
cost: 10000
recipeUnlocks:
- ChemMasterMachineCircuitboard
- ChemDispenserMachineCircuitboard
technologyPrerequisites:
- Chemistry
- type: technology
id: CrewMonitoring
name: research-technology-crew-monitoring
icon:
sprite: Objects/Specific/Medical/handheldcrewmonitor.rsi
state: scanner
discipline: Biochemical
tier: 2
cost: 10000
recipeUnlocks:
- HandheldCrewMonitor
# Tier 3
- type: technology
id: Cloning
name: research-technology-cloning
icon:
sprite: Structures/Machines/cloning.rsi
state: pod_0
discipline: Biochemical
tier: 3
cost: 15000
recipeUnlocks:
- CloningPodMachineCircuitboard
- MedicalScannerMachineCircuitboard
- CloningConsoleComputerCircuitboard
- BiomassReclaimerMachineCircuitboard

View File

@@ -0,0 +1,153 @@
# Tier 1
- type: technology
id: JanitorialEquipment
name: research-technology-janitorial-equipment
icon:
sprite: Objects/Specific/Janitorial/mop.rsi
state: mop
discipline: CivilianServices
tier: 1
cost: 5000
recipeUnlocks:
- MopItem
- Holoprojector
- Mousetrap
- LightReplacer
- TrashBag
- type: technology
id: LaundryTech
name: research-technology-laundry-tech
icon:
sprite: Structures/Machines/uniform_printer.rsi
state: icon
discipline: CivilianServices
tier: 1
cost: 5000
recipeUnlocks:
- UniformPrinterMachineCircuitboard
- type: technology
id: Hydroponics
name: research-technology-basic-hydroponics
icon:
sprite: Structures/Machines/seed_extractor.rsi
state: seedextractor
discipline: CivilianServices
tier: 1
cost: 5000
recipeUnlocks:
- SeedExtractorMachineCircuitboard
- HydroponicsTrayMachineCircuitboard
- type: technology
id: FoodService
name: research-technology-food-service
icon:
sprite: Structures/Machines/juicer.rsi
state: juicer1
discipline: CivilianServices
tier: 1
cost: 7500
recipeUnlocks: #remove all of these once we have more kitchen equipment
- MicrowaveMachineCircuitboard
- ReagentGrinderMachineCircuitboard
- BoozeDispenserMachineCircuitboard
- SodaDispenserMachineCircuitboard
- type: technology
id: AudioVisualCommunication
name: research-technology-audio-visual-communication
icon:
sprite: Structures/Wallmounts/camera.rsi
state: cameracase
discipline: CivilianServices
tier: 1
cost: 7500
recipeUnlocks:
- SurveillanceCameraRouterCircuitboard
- SurveillanceCameraWirelessRouterCircuitboard
- SurveillanceWirelessCameraMovableCircuitboard
- SurveillanceWirelessCameraAnchoredCircuitboard
- SurveillanceCameraMonitorCircuitboard
- SurveillanceWirelessCameraMonitorCircuitboard
- TelecomServerCircuitboard
# Tier 2
- type: technology
id: AdvancedCleaning
name: research-technology-advanced-cleaning
icon:
sprite: Objects/Specific/Janitorial/advmop.rsi
state: advmop
discipline: CivilianServices
tier: 2
cost: 10000
recipeUnlocks:
- AdvMopItem
- MegaSprayBottle
technologyPrerequisites:
- JanitorialEquipment
- type: technology
id: MeatManipulation
name: research-technology-meat-manipulation
icon:
sprite: Structures/Machines/fat_sucker.rsi
state: display
discipline: CivilianServices
tier: 2
cost: 5000
recipeUnlocks:
- FatExtractorMachineCircuitboard
- type: technology
id: HONKMech
name: research-technology-honk-mech
icon:
sprite: Objects/Specific/Mech/mecha.rsi
state: honker
discipline: CivilianServices
tier: 2
cost: 10000
recipeUnlocks:
- HonkerHarness
- HonkerLArm
- HonkerRArm
- HonkerLLeg
- HonkerRLeg
- HonkerCentralElectronics
- HonkerPeripheralsElectronics
- HonkerTargetingElectronics
- MechEquipmentHorn
- type: technology
id: AdvancedEntertainment
name: research-technology-advanced-entertainment
icon:
sprite: Structures/Machines/computers.rsi
state: television
discipline: CivilianServices
tier: 2
cost: 7500
recipeUnlocks:
- ComputerTelevisionCircuitboard
- SynthesizerInstrument
- DawInstrumentMachineCircuitboard
# Tier 3
- type: technology
id: AdvancedSpray
name: research-technology-advanced-spray
icon:
sprite: Objects/Weapons/Guns/Basic/spraynozzle.rsi
state: icon
discipline: CivilianServices
tier: 3
cost: 15000
recipeUnlocks:
- WeaponSprayNozzle
- ClothingBackpackWaterTank

View File

@@ -0,0 +1,47 @@
- type: techDiscipline
id: Industrial
name: research-discipline-industrial
color: "#eeac34"
icon:
sprite: Interface/Misc/research_disciplines.rsi
state: industrial
tierPrerequisites:
1: 0
2: 0.75
3: 0.75
- type: techDiscipline
id: Biochemical
name: research-discipline-biochemical
color: "#449ae6"
icon:
sprite: Interface/Misc/research_disciplines.rsi
state: biochemical
tierPrerequisites:
1: 0
2: 0.75
3: 0.75
- type: techDiscipline
id: Experimental
name: research-discipline-experimental
color: "#9a6ef0"
icon:
sprite: Interface/Misc/research_disciplines.rsi
state: experimental
tierPrerequisites:
1: 0
2: 0.75
3: 0.75
- type: techDiscipline
id: CivilianServices
name: research-discipline-civilian-services
color: "#7ecd48"
icon:
sprite: Interface/Misc/research_disciplines.rsi
state: civilianservices
tierPrerequisites:
1: 0
2: 0.75
3: 0.75

View File

@@ -0,0 +1,162 @@
# Tier 1
- type: technology
id: BasicRobotics
name: research-technology-basic-robotics
icon:
sprite: Mobs/Silicon/drone.rsi
state: drone
discipline: Experimental
tier: 1
cost: 5000
recipeUnlocks:
- ProximitySensor
- LeftArmBorg
- LightHeadBorg
- RightArmBorg
- LeftLegBorg
- RightLegBorg
- Drone
- ExosuitFabricatorMachineCircuitboard
- type: technology
id: SignallingTech
name: research-technology-signalling-tech
icon:
sprite: Objects/Devices/signaller.rsi
state: signaller
discipline: Experimental
tier: 1
cost: 7500
recipeUnlocks:
- Signaller
- SignalTrigger
- VoiceTrigger
- TimerTrigger
- SignalTimerElectronics
- type: technology
id: BasicAnomalousResearch
name: research-technology-basic-anomalous-research
icon:
sprite: Objects/Specific/Research/anomalyscanner.rsi
state: icon
discipline: Experimental
tier: 1
cost: 7500
recipeUnlocks:
- AnomalyScanner
- AnomalyLocator
- APECircuitboard
- AnomalyVesselCircuitboard
- type: technology
id: BasicXenoArcheology
name: research-technology-basic-xenoarcheology
icon:
sprite: Structures/Machines/artifact_analyzer.rsi
state: display
discipline: Experimental
tier: 1
cost: 10000
recipeUnlocks:
- NodeScanner
- AnalysisComputerCircuitboard
- ArtifactAnalyzerMachineCircuitboard
- type: technology
id: AlternativeResearch
name: research-technology-alternative-research
icon:
sprite: Structures/Machines/tech_disk_printer.rsi
state: display
discipline: Experimental
tier: 1
cost: 5000
recipeUnlocks:
- TechDiskComputerCircuitboard
- type: technology
id: MagnetsTech
name: research-technology-magnets-tech
icon:
sprite: Clothing/Shoes/Boots/magboots.rsi
state: icon
discipline: Experimental
tier: 1
cost: 10000
recipeUnlocks:
- ClothingShoesBootsMag
# Tier 2
- type: technology
id: AdvancedParts
name: research-technology-advanced-parts
icon:
sprite: Objects/Misc/stock_parts.rsi
state: advanced_matter_bin
discipline: Experimental
tier: 2
cost: 15000
recipeUnlocks:
- AdvancedCapacitorStockPart
- AdvancedMatterBinStockPart
- NanoManipulatorStockPart
- type: technology
id: AbnormalArtifactManipulation
name: research-technology-abnormal-artifact-manipulation
icon:
sprite: Structures/Machines/traversal_distorter.rsi
state: display
discipline: Experimental
tier: 2
cost: 5000
recipeUnlocks:
- TraversalDistorterMachineCircuitboard
- type: technology
id: MobileAnomalyTech
name: research-technology-mobile-anomaly-tech
icon:
sprite: Objects/Weapons/Guns/Revolvers/chimp.rsi
state: base
discipline: Experimental
tier: 2
cost: 10000
recipeUnlocks:
- WeaponPistolCHIMP
- CartridgeAnomalousParticleDelta
- CartridgeAnomalousParticleEpsilon
- CartridgeAnomalousParticleZeta
technologyPrerequisites:
- BasicAnomalousResearch
- type: technology
id: RapidPartExchange
name: research-technology-rped
icon:
sprite: Objects/Specific/Research/rped.rsi
state: icon
discipline: Experimental
tier: 2
cost: 7500
recipeUnlocks:
- RPED
# Tier 3
- type: technology
id: SuperParts
name: research-technology-super-parts
icon:
sprite: Objects/Misc/stock_parts.rsi
state: super_matter_bin
discipline: Experimental
tier: 3
cost: 15000
recipeUnlocks:
- SuperCapacitorStockPart
- SuperMatterBinStockPart
- PicoManipulatorStockPart

View File

@@ -0,0 +1,177 @@
# Tier 1
- type: technology
id: SalvageEquipment
name: research-technology-salvage-equipment
icon:
sprite: Objects/Tools/handdrill.rsi
state: handdrill
discipline: Industrial
tier: 1
cost: 7500
recipeUnlocks:
- PowerDrill #todo remove this once we have advanced tools
- MiningDrill
- OreProcessorMachineCircuitboard
- type: technology
id: AdvancedPowercells
name: research-technology-advanced-powercells
icon:
sprite: Objects/Power/power_cells.rsi
state: medium
discipline: Industrial
tier: 1
cost: 5000
recipeUnlocks:
- PowerCellMedium
- type: technology
id: CompactPower
name: research-technology-compact-power
icon:
sprite: Structures/Power/Generation/wallmount_generator.rsi
state: panel
discipline: Industrial
tier: 1
cost: 10000
recipeUnlocks:
- WallmountSubstationElectronics
- WallmountGeneratorElectronics
- WallmountGeneratorAPUElectronics
- type: technology
id: IndustrialEngineering
name: research-technology-industrial-engineering
icon:
sprite: Structures/Machines/protolathe.rsi
state: icon
discipline: Industrial
tier: 1
cost: 10000
recipeUnlocks:
- ProtolatheMachineCircuitboard
- AutolatheMachineCircuitboard
- CircuitImprinterMachineCircuitboard
- MaterialReclaimerMachineCircuitboard
- type: technology
id: PowerGeneration
name: research-technology-power-generation
icon:
sprite: Structures/Power/Generation/portable_generator.rsi
state: portgen0_1
discipline: Industrial
tier: 1
cost: 10000
recipeUnlocks:
- GeneratorPlasmaMachineCircuitboard
- GeneratorUraniumMachineCircuitboard
- PowerComputerCircuitboard #the actual solar panel itself should be in here
- SolarControlComputerCircuitboard
- EmitterCircuitboard
- type: technology
id: AtmosphericTech
name: research-technology-atmospheric-tech
icon:
sprite: Structures/Piping/Atmospherics/thermomachine.rsi
state: freezerOff
discipline: Industrial
tier: 1
cost: 7500
recipeUnlocks:
- ThermomachineFreezerMachineCircuitBoard
- GasRecyclerMachineCircuitboard
# Tier 2
- type: technology
id: RapidConstruction
name: research-technology-rapid-construction
icon:
sprite: Objects/Tools/rcd.rsi
state: icon
discipline: Industrial
tier: 2
cost: 10000
recipeUnlocks:
- RCD
- RCDAmmo
- type: technology
id: Shuttlecraft
name: research-technology-shuttlecraft
icon:
sprite: Structures/Shuttles/gyroscope.rsi
state: base
discipline: Industrial
tier: 2
cost: 10000
recipeUnlocks:
- ShuttleConsoleCircuitboard
- RadarConsoleCircuitboard
- ThrusterMachineCircuitboard
- GyroscopeMachineCircuitboard
- type: technology
id: RipleyAPLU
name: research-technology-ripley-aplu
icon:
sprite: Objects/Specific/Mech/mecha.rsi
state: ripley
discipline: Industrial
tier: 2
cost: 10000
recipeUnlocks:
- RipleyHarness
- RipleyLArm
- RipleyRArm
- RipleyLLeg
- RipleyRLeg
- RipleyCentralElectronics
- RipleyPeripheralsElectronics
- MechEquipmentGrabber
- type: technology
id: AdvancedAtmospherics
name: research-technology-advanced-atmospherics
icon:
sprite: Objects/Devices/Holoprojectors/atmos.rsi
state: icon
discipline: Industrial
tier: 2
cost: 5000
recipeUnlocks:
- HolofanProjector
- PortableScrubberMachineCircuitBoard
- type: technology
id: SuperPowercells
name: research-technology-super-powercells
icon:
sprite: Objects/Power/power_cells.rsi
state: high
discipline: Industrial
tier: 2
cost: 7500
recipeUnlocks:
- PowerCellHigh
technologyPrerequisites:
- AdvancedPowercells
# Tier 3
- type: technology
id: BluespaceStorage
name: research-technology-bluespace-storage
icon:
sprite: Clothing/Back/Backpacks/holding.rsi
state: holding
discipline: Industrial
tier: 3
cost: 15000
recipeUnlocks:
- ClothingBackpackHolding
- ClothingBackpackSatchelHolding
- ClothingBackpackDuffelHolding

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

View File

@@ -0,0 +1,23 @@
{
"version": 1,
"license": "CC0-1.0",
"copyright": "Created by EmoGarbage404 (github) for Space Station 14.",
"size": {
"x": 8,
"y": 8
},
"states": [
{
"name": "biochemical"
},
{
"name": "civilianservices"
},
{
"name": "experimental"
},
{
"name": "industrial"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

View File

@@ -7,6 +7,9 @@
"y": 32
},
"states": [
{
"name": "display"
},
{
"name": "icon"
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

View File

@@ -7,6 +7,9 @@
"y": 32
},
"states": [
{
"name": "display"
},
{
"name": "fat"
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

View File

@@ -7,6 +7,9 @@
"y": 32
},
"states": [
{
"name": "display"
},
{
"name": "icon"
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 B

View File

@@ -7,6 +7,9 @@
"y": 32
},
"states": [
{
"name": "display"
},
{
"name": "icon"
},