From fd994511a74bf58bf6f09bda61662b88a4f38f2d Mon Sep 17 00:00:00 2001
From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
Date: Mon, 16 Oct 2023 17:51:58 -0400
Subject: [PATCH] technology auto guidebook (#21029)
* technology auto guidebook
* boo-womp
* boo-womp II
---
.../Controls/GuideTechDisciplineEmbed.xaml | 4 +
.../Controls/GuideTechDisciplineEmbed.xaml.cs | 60 ++++++++++++
.../Controls/GuideTechnologyEmbed.xaml | 22 +++++
.../Controls/GuideTechnologyEmbed.xaml.cs | 93 +++++++++++++++++++
.../UI/MiniTechnologyCardControl.xaml.cs | 8 +-
.../Research/UI/ResearchConsoleMenu.xaml.cs | 34 +------
.../Systems/TechnologyDiskSystem.cs | 2 +-
.../Prototypes/TechnologyPrototype.cs | 32 +++----
.../Research/Systems/SharedResearchSystem.cs | 56 ++++++++++-
Resources/Locale/en-US/guidebook/guides.ftl | 1 +
.../components/research-console-component.ftl | 2 +
Resources/Prototypes/Guidebook/science.yml | 7 ++
.../ServerInfo/Guidebook/Science/Science.xml | 4 +-
.../Guidebook/Science/Technologies.xml | 23 +++++
14 files changed, 296 insertions(+), 52 deletions(-)
create mode 100644 Content.Client/Guidebook/Controls/GuideTechDisciplineEmbed.xaml
create mode 100644 Content.Client/Guidebook/Controls/GuideTechDisciplineEmbed.xaml.cs
create mode 100644 Content.Client/Guidebook/Controls/GuideTechnologyEmbed.xaml
create mode 100644 Content.Client/Guidebook/Controls/GuideTechnologyEmbed.xaml.cs
create mode 100644 Resources/ServerInfo/Guidebook/Science/Technologies.xml
diff --git a/Content.Client/Guidebook/Controls/GuideTechDisciplineEmbed.xaml b/Content.Client/Guidebook/Controls/GuideTechDisciplineEmbed.xaml
new file mode 100644
index 0000000000..0878951af8
--- /dev/null
+++ b/Content.Client/Guidebook/Controls/GuideTechDisciplineEmbed.xaml
@@ -0,0 +1,4 @@
+
+
+
diff --git a/Content.Client/Guidebook/Controls/GuideTechDisciplineEmbed.xaml.cs b/Content.Client/Guidebook/Controls/GuideTechDisciplineEmbed.xaml.cs
new file mode 100644
index 0000000000..88d264cb05
--- /dev/null
+++ b/Content.Client/Guidebook/Controls/GuideTechDisciplineEmbed.xaml.cs
@@ -0,0 +1,60 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Content.Client.Guidebook.Richtext;
+using Content.Shared.Research.Prototypes;
+using JetBrains.Annotations;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Guidebook.Controls;
+
+///
+/// Control for embedding all the technologies in a discipline into a guidebook.
+///
+[UsedImplicitly, GenerateTypedNameReferences]
+public sealed partial class GuideTechDisciplineEmbed : BoxContainer, IDocumentTag
+{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+
+ public GuideTechDisciplineEmbed()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+ MouseFilter = MouseFilterMode.Stop;
+ }
+
+ public GuideTechDisciplineEmbed(string group) : this()
+ {
+ var prototypes = _prototype.EnumeratePrototypes()
+ .Where(p => p.Discipline.Equals(group)).OrderBy(p => p.Tier).ThenBy(p => Loc.GetString(p.Name));
+ foreach (var tech in prototypes)
+ {
+ var embed = new GuideTechnologyEmbed(tech);
+ DisciplineContainer.AddChild(embed);
+ }
+ }
+
+ public bool TryParseTag(Dictionary args, [NotNullWhen(true)] out Control? control)
+ {
+ control = null;
+ if (!args.TryGetValue("Discipline", out var group))
+ {
+ Logger.Error("Technology discipline embed tag is missing discipline argument");
+ return false;
+ }
+
+ var prototypes = _prototype.EnumeratePrototypes()
+ .Where(p => p.Discipline.Equals(group)).OrderBy(p => p.Tier).ThenBy(p => Loc.GetString(p.Name));
+ foreach (var tech in prototypes)
+ {
+ var embed = new GuideTechnologyEmbed(tech);
+ DisciplineContainer.AddChild(embed);
+ }
+
+ control = this;
+ return true;
+ }
+}
diff --git a/Content.Client/Guidebook/Controls/GuideTechnologyEmbed.xaml b/Content.Client/Guidebook/Controls/GuideTechnologyEmbed.xaml
new file mode 100644
index 0000000000..6d96f19972
--- /dev/null
+++ b/Content.Client/Guidebook/Controls/GuideTechnologyEmbed.xaml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Guidebook/Controls/GuideTechnologyEmbed.xaml.cs b/Content.Client/Guidebook/Controls/GuideTechnologyEmbed.xaml.cs
new file mode 100644
index 0000000000..d61cc2d961
--- /dev/null
+++ b/Content.Client/Guidebook/Controls/GuideTechnologyEmbed.xaml.cs
@@ -0,0 +1,93 @@
+using System.Diagnostics.CodeAnalysis;
+using Content.Client.Guidebook.Richtext;
+using Content.Client.Message;
+using Content.Client.Research;
+using Content.Client.UserInterface.ControlExtensions;
+using Content.Shared.Research.Prototypes;
+using JetBrains.Annotations;
+using Robust.Client.AutoGenerated;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+
+namespace Content.Client.Guidebook.Controls;
+
+///
+/// Control for embedding a research technology into a guidebook.
+///
+[UsedImplicitly, GenerateTypedNameReferences]
+public sealed partial class GuideTechnologyEmbed : BoxContainer, IDocumentTag, ISearchableControl
+{
+ [Dependency] private readonly IEntitySystemManager _systemManager = default!;
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+
+ private readonly ResearchSystem _research;
+ private readonly SpriteSystem _sprite;
+
+ public GuideTechnologyEmbed()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+ _research = _systemManager.GetEntitySystem();
+ _sprite = _systemManager.GetEntitySystem();
+ MouseFilter = MouseFilterMode.Stop;
+ }
+
+ public GuideTechnologyEmbed(string technology) : this()
+ {
+ GenerateControl(_prototype.Index(technology));
+ }
+
+ public GuideTechnologyEmbed(TechnologyPrototype technology) : this()
+ {
+ GenerateControl(technology);
+ }
+
+ public bool CheckMatchesSearch(string query)
+ {
+ return this.ChildrenContainText(query);
+ }
+
+ public void SetHiddenState(bool state, string query)
+ {
+ Visible = CheckMatchesSearch(query) ? state : !state;
+ }
+
+ public bool TryParseTag(Dictionary args, [NotNullWhen(true)] out Control? control)
+ {
+ control = null;
+ if (!args.TryGetValue("Technology", out var id))
+ {
+ Logger.Error("Technology embed tag is missing technology prototype argument");
+ return false;
+ }
+
+ if (!_prototype.TryIndex(id, out var technology))
+ {
+ Logger.Error($"Specified technology prototype \"{id}\" is not a valid technology prototype");
+ return false;
+ }
+
+ GenerateControl(technology);
+
+ control = this;
+ return true;
+ }
+
+ private void GenerateControl(TechnologyPrototype technology)
+ {
+ var discipline = _prototype.Index(technology.Discipline);
+
+ NameLabel.SetMarkup($"[bold]{Loc.GetString(technology.Name)}[/bold]");
+ DescriptionLabel.SetMessage(_research.GetTechnologyDescription(technology, includePrereqs: true, disciplinePrototype: discipline));
+ TechTexture.Texture = _sprite.Frame0(technology.Icon);
+
+ DisciplineColorBackground.PanelOverride = new StyleBoxFlat
+ {
+ BackgroundColor = discipline.Color
+ };
+ }
+}
diff --git a/Content.Client/Research/UI/MiniTechnologyCardControl.xaml.cs b/Content.Client/Research/UI/MiniTechnologyCardControl.xaml.cs
index 8b1a583c24..5af1159c93 100644
--- a/Content.Client/Research/UI/MiniTechnologyCardControl.xaml.cs
+++ b/Content.Client/Research/UI/MiniTechnologyCardControl.xaml.cs
@@ -2,6 +2,7 @@
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -15,10 +16,13 @@ public sealed partial class MiniTechnologyCardControl : Control
{
RobustXamlLoader.Load(this);
- var discipline = prototypeManager.Index(technology.Discipline);
+ var discipline = prototypeManager.Index(technology.Discipline);
Background.ModulateSelfOverride = discipline.Color;
Texture.Texture = spriteSys.Frame0(technology.Icon);
NameLabel.SetMessage(Loc.GetString(technology.Name));
- Main.ToolTip = description.ToString();
+
+ var tooltip = new Tooltip();
+ tooltip.SetMessage(description);
+ Main.TooltipSupplier = _ => tooltip;
}
}
diff --git a/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs b/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs
index c2b23f7341..a20509202f 100644
--- a/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs
+++ b/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs
@@ -27,7 +27,7 @@ public sealed partial class ResearchConsoleMenu : FancyWindow
private readonly TechnologyDatabaseComponent? _technologyDatabase;
private readonly ResearchSystem _research;
private readonly SpriteSystem _sprite;
- private readonly AccessReaderSystem _accessReader = default!;
+ private readonly AccessReaderSystem _accessReader;
public readonly EntityUid Entity;
@@ -55,7 +55,7 @@ public sealed partial class ResearchConsoleMenu : FancyWindow
foreach (var tech in allTech)
{
- var mini = new MiniTechnologyCardControl(tech, _prototype, _sprite, GetTechnologyDescription(tech, false));
+ var mini = new MiniTechnologyCardControl(tech, _prototype, _sprite, _research.GetTechnologyDescription(tech));
AvailableCardsContainer.AddChild(mini);
}
@@ -74,7 +74,7 @@ public sealed partial class ResearchConsoleMenu : FancyWindow
foreach (var techId in _technologyDatabase.CurrentTechnologyCards)
{
var tech = _prototype.Index(techId);
- var cardControl = new TechnologyCardControl(tech, _prototype, _sprite, GetTechnologyDescription(tech), state.Points, hasAccess);
+ var cardControl = new TechnologyCardControl(tech, _prototype, _sprite, _research.GetTechnologyDescription(tech, includeTier: false), state.Points, hasAccess);
cardControl.OnPressed += () => OnTechnologyCardPressed?.Invoke(techId);
TechnologyCardsContainer.AddChild(cardControl);
}
@@ -82,37 +82,11 @@ public sealed partial class ResearchConsoleMenu : FancyWindow
foreach (var unlocked in _technologyDatabase.UnlockedTechnologies)
{
var tech = _prototype.Index(unlocked);
- var cardControl = new MiniTechnologyCardControl(tech, _prototype, _sprite, GetTechnologyDescription(tech, false));
+ var cardControl = new MiniTechnologyCardControl(tech, _prototype, _sprite, _research.GetTechnologyDescription(tech, false));
UnlockedCardsContainer.AddChild(cardControl);
}
}
- public FormattedMessage GetTechnologyDescription(TechnologyPrototype technology, bool includeCost = true)
- {
- 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(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))));
- }
-
- return description;
- }
-
public void UpdateInformationPanel(ResearchConsoleBoundInterfaceState state)
{
var amountMsg = new FormattedMessage();
diff --git a/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs b/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs
index d886493e3f..176b2b68bc 100644
--- a/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs
+++ b/Content.Server/Research/TechnologyDisk/Systems/TechnologyDiskSystem.cs
@@ -72,7 +72,7 @@ public sealed class TechnologyDiskSystem : EntitySystem
var tier = int.Parse(weightedRandom.Pick(_random));
//get a list of every distinct recipe in all the technologies.
- var techs = new List();
+ var techs = new List>();
foreach (var tech in _prototype.EnumeratePrototypes())
{
if (tech.Tier != tier)
diff --git a/Content.Shared/Research/Prototypes/TechnologyPrototype.cs b/Content.Shared/Research/Prototypes/TechnologyPrototype.cs
index ec1ca029c7..38000677a1 100644
--- a/Content.Shared/Research/Prototypes/TechnologyPrototype.cs
+++ b/Content.Shared/Research/Prototypes/TechnologyPrototype.cs
@@ -1,6 +1,4 @@
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;
@@ -19,57 +17,57 @@ public sealed class TechnologyPrototype : IPrototype
/// The name of the technology.
/// Supports locale strings
///
- [DataField("name", required: true)]
- public string Name = string.Empty;
+ [DataField(required: true)]
+ public LocId Name = string.Empty;
///
/// An icon used to visually represent the technology in UI.
///
- [DataField("icon", required: true)]
+ [DataField(required: true)]
public SpriteSpecifier Icon = default!;
///
/// What research discipline this technology belongs to.
///
- [DataField("discipline", required: true, customTypeSerializer: typeof(PrototypeIdSerializer))]
- public string Discipline = default!;
+ [DataField(required: true)]
+ public ProtoId Discipline;
///
/// What tier research is this?
/// The tier governs how much lower-tier technology
/// needs to be unlocked before this one.
///
- [DataField("tier", required: true)]
+ [DataField(required: true)]
public int Tier;
///
/// Hidden tech is not ever available at the research console.
///
- [DataField("hidden")]
+ [DataField]
public bool Hidden;
///
/// How much research is needed to unlock.
///
- [DataField("cost")]
+ [DataField]
public int Cost = 10000;
///
/// A list of s that need to be unlocked in order to unlock this technology.
///
- [DataField("technologyPrerequisites", customTypeSerializer: typeof(PrototypeIdListSerializer))]
- public IReadOnlyList TechnologyPrerequisites = new List();
+ [DataField]
+ public List> TechnologyPrerequisites = new();
///
/// A list of s that are unlocked by this technology
///
- [DataField("recipeUnlocks", customTypeSerializer: typeof(PrototypeIdListSerializer))]
- public IReadOnlyList RecipeUnlocks = new List();
+ [DataField]
+ public List> RecipeUnlocks = new();
///
/// A list of non-standard effects that are done when this technology is unlocked.
///
- [DataField("genericUnlocks")]
+ [DataField]
public IReadOnlyList GenericUnlocks = new List();
}
@@ -80,13 +78,13 @@ public partial record struct GenericUnlock()
/// What event is raised when this is unlocked?
/// Used for doing non-standard logic.
///
- [DataField("purchaseEvent")]
+ [DataField]
public object? PurchaseEvent = null;
///
/// A player facing tooltip for what the unlock does.
/// Supports locale strings.
///
- [DataField("unlockDescription")]
+ [DataField]
public string UnlockDescription = string.Empty;
}
diff --git a/Content.Shared/Research/Systems/SharedResearchSystem.cs b/Content.Shared/Research/Systems/SharedResearchSystem.cs
index e0cc937b00..12f27d0b9c 100644
--- a/Content.Shared/Research/Systems/SharedResearchSystem.cs
+++ b/Content.Shared/Research/Systems/SharedResearchSystem.cs
@@ -3,6 +3,7 @@ using Content.Shared.Research.Components;
using Content.Shared.Research.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
+using Robust.Shared.Utility;
namespace Content.Shared.Research.Systems;
@@ -40,7 +41,7 @@ public abstract class SharedResearchSystem : EntitySystem
component.CurrentTechnologyCards.Add(selected.ID);
}
- Dirty(component);
+ Dirty(uid, component);
}
public List GetAvailableTechnologies(EntityUid uid, TechnologyDatabaseComponent? component = null)
@@ -142,6 +143,59 @@ public abstract class SharedResearchSystem : EntitySystem
return tier - 1;
}
+ public FormattedMessage GetTechnologyDescription(
+ TechnologyPrototype technology,
+ bool includeCost = true,
+ bool includeTier = true,
+ bool includePrereqs = false,
+ TechDisciplinePrototype? disciplinePrototype = null)
+ {
+ var description = new FormattedMessage();
+ if (includeTier)
+ {
+ disciplinePrototype ??= PrototypeManager.Index(technology.Discipline);
+ description.AddMarkup(Loc.GetString("research-console-tier-discipline-info",
+ ("tier", technology.Tier), ("color", disciplinePrototype.Color), ("discipline", Loc.GetString(disciplinePrototype.Name))));
+ description.PushNewline();
+ }
+
+ if (includeCost)
+ {
+ description.AddMarkup(Loc.GetString("research-console-cost", ("amount", technology.Cost)));
+ description.PushNewline();
+ }
+
+ if (includePrereqs && technology.TechnologyPrerequisites.Any())
+ {
+ description.AddMarkup(Loc.GetString("research-console-prereqs-list-start"));
+ foreach (var recipe in technology.TechnologyPrerequisites)
+ {
+ var techProto = PrototypeManager.Index(recipe);
+ description.PushNewline();
+ description.AddMarkup(Loc.GetString("research-console-prereqs-list-entry",
+ ("text", Loc.GetString(techProto.Name))));
+ }
+ description.PushNewline();
+ }
+
+ description.AddMarkup(Loc.GetString("research-console-unlocks-list-start"));
+ foreach (var recipe in technology.RecipeUnlocks)
+ {
+ var recipeProto = PrototypeManager.Index(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",
+ ("text", Loc.GetString(generic.UnlockDescription))));
+ }
+
+ return description;
+ }
+
///
/// Returns whether a technology is unlocked on this database or not.
///
diff --git a/Resources/Locale/en-US/guidebook/guides.ftl b/Resources/Locale/en-US/guidebook/guides.ftl
index 9fc69839c0..8893db9b23 100644
--- a/Resources/Locale/en-US/guidebook/guides.ftl
+++ b/Resources/Locale/en-US/guidebook/guides.ftl
@@ -34,6 +34,7 @@ guide-entry-botanicals = Botanicals
guide-entry-cloning = Cloning
guide-entry-cryogenics = Cryogenics
guide-entry-science = Science
+guide-entry-technologies = Technologies
guide-entry-anomalous-research = Anomalous Research
guide-entry-scanners-and-vessels = Scanners and Vessels
guide-entry-ape = A.P.E.
diff --git a/Resources/Locale/en-US/research/components/research-console-component.ftl b/Resources/Locale/en-US/research/components/research-console-component.ftl
index 1063e6ebcb..196983efcd 100644
--- a/Resources/Locale/en-US/research/components/research-console-component.ftl
+++ b/Resources/Locale/en-US/research/components/research-console-component.ftl
@@ -14,5 +14,7 @@ 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]
+research-console-prereqs-list-start = Requires:
+research-console-prereqs-list-entry = - [color=orchid]{$text}[/color]
research-console-no-access-popup = No access!
diff --git a/Resources/Prototypes/Guidebook/science.yml b/Resources/Prototypes/Guidebook/science.yml
index d9a3d442b4..03840ec15e 100644
--- a/Resources/Prototypes/Guidebook/science.yml
+++ b/Resources/Prototypes/Guidebook/science.yml
@@ -3,11 +3,18 @@
name: guide-entry-science
text: "/ServerInfo/Guidebook/Science/Science.xml"
children:
+ - Technologies
- AnomalousResearch
- Xenoarchaeology
- Robotics
- MachineUpgrading
+- type: guideEntry
+ id: Technologies
+ name: guide-entry-technologies
+ text: "/ServerInfo/Guidebook/Science/Technologies.xml"
+ filterEnabled: True
+
- type: guideEntry
id: AnomalousResearch
name: guide-entry-anomalous-research
diff --git a/Resources/ServerInfo/Guidebook/Science/Science.xml b/Resources/ServerInfo/Guidebook/Science/Science.xml
index a32112877f..c886daf4d5 100644
--- a/Resources/ServerInfo/Guidebook/Science/Science.xml
+++ b/Resources/ServerInfo/Guidebook/Science/Science.xml
@@ -10,7 +10,9 @@ Science, often called Research and Development, is a job made up of both generat
The most important thing inside your department is the R&D server, which stores unlocked technologies, and the R&D computer, which allows you to unlock technologies.
-Each technology costs [color=#a4885c]Research Points[/color] and unlocks recipes at lathes. Some technologies will also have prerequesites you have to unlock before you can research them.
+Each technology costs [color=#a4885c]Research Points[/color] and unlocks recipes at lathes. Some technologies will also have prerequisites you have to unlock before you can research them.
+
+Information about the different technologies can be viewed [textlink="on the technology guidebook page" link="Technologies"].
## Disciplines
Technologies are spread over 5 different Disciplines:
diff --git a/Resources/ServerInfo/Guidebook/Science/Technologies.xml b/Resources/ServerInfo/Guidebook/Science/Technologies.xml
new file mode 100644
index 0000000000..7f0feaca42
--- /dev/null
+++ b/Resources/ServerInfo/Guidebook/Science/Technologies.xml
@@ -0,0 +1,23 @@
+
+# Technologies
+
+All technologies have a cost and a tier requirement in order to be researched. Unlocking them adds a variety of recipes that can be printed at various lathes.
+
+The different technologies and their respective discipline are listed below.
+
+## Industrial
+
+
+## Biochemical
+
+
+## Arsenal
+
+
+## Experimental
+
+
+## Civilian Services
+
+
+
--
2.51.2