--- /dev/null
+<BoxContainer xmlns="https://spacestation14.io"
+ Orientation="Vertical">
+ <BoxContainer Name="DisciplineContainer" Orientation="Vertical"/>
+</BoxContainer>
--- /dev/null
+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;
+
+/// <summary>
+/// Control for embedding all the technologies in a discipline into a guidebook.
+/// </summary>
+[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<TechnologyPrototype>()
+ .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<string, string> 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<TechnologyPrototype>()
+ .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;
+ }
+}
--- /dev/null
+<BoxContainer xmlns="https://spacestation14.io"
+ xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
+ xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
+ Orientation="Vertical"
+ Margin="5 5 5 5">
+ <PanelContainer HorizontalExpand="True">
+ <PanelContainer.PanelOverride>
+ <gfx:StyleBoxFlat BorderThickness="1" BorderColor="#777777"/>
+ </PanelContainer.PanelOverride>
+ <BoxContainer Orientation="Vertical">
+ <PanelContainer Name="DisciplineColorBackground" HorizontalExpand="True" VerticalExpand="False" MinHeight="15"/>
+ <BoxContainer Orientation="Vertical" Margin="5 5 5 5">
+ <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
+ <RichTextLabel Name="NameLabel" VerticalAlignment="Bottom" HorizontalExpand="True"/>
+ <TextureRect Name="TechTexture" VerticalAlignment="Center" HorizontalAlignment="Right"/>
+ </BoxContainer>
+ <customControls:HSeparator Margin="10 5 10 10"/>
+ <RichTextLabel Name="DescriptionLabel"/>
+ </BoxContainer>
+ </BoxContainer>
+ </PanelContainer>
+</BoxContainer>
--- /dev/null
+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;
+
+/// <summary>
+/// Control for embedding a research technology into a guidebook.
+/// </summary>
+[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<ResearchSystem>();
+ _sprite = _systemManager.GetEntitySystem<SpriteSystem>();
+ MouseFilter = MouseFilterMode.Stop;
+ }
+
+ public GuideTechnologyEmbed(string technology) : this()
+ {
+ GenerateControl(_prototype.Index<TechnologyPrototype>(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<string, string> 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<TechnologyPrototype>(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
+ };
+ }
+}
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;
{
RobustXamlLoader.Load(this);
- var discipline = prototypeManager.Index<TechDisciplinePrototype>(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;
}
}
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;
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);
}
foreach (var techId in _technologyDatabase.CurrentTechnologyCards)
{
var tech = _prototype.Index<TechnologyPrototype>(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);
}
foreach (var unlocked in _technologyDatabase.UnlockedTechnologies)
{
var tech = _prototype.Index<TechnologyPrototype>(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<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))));
- }
-
- return description;
- }
-
public void UpdateInformationPanel(ResearchConsoleBoundInterfaceState state)
{
var amountMsg = new FormattedMessage();
var tier = int.Parse(weightedRandom.Pick(_random));
//get a list of every distinct recipe in all the technologies.
- var techs = new List<string>();
+ var techs = new List<ProtoId<LatheRecipePrototype>>();
foreach (var tech in _prototype.EnumeratePrototypes<TechnologyPrototype>())
{
if (tech.Tier != tier)
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;
/// The name of the technology.
/// Supports locale strings
/// </summary>
- [DataField("name", required: true)]
- public string Name = string.Empty;
+ [DataField(required: true)]
+ public LocId Name = string.Empty;
/// <summary>
/// An icon used to visually represent the technology in UI.
/// </summary>
- [DataField("icon", required: true)]
+ [DataField(required: true)]
public SpriteSpecifier Icon = default!;
/// <summary>
/// What research discipline this technology belongs to.
/// </summary>
- [DataField("discipline", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<TechDisciplinePrototype>))]
- public string Discipline = default!;
+ [DataField(required: true)]
+ public ProtoId<TechDisciplinePrototype> Discipline;
/// <summary>
/// What tier research is this?
/// The tier governs how much lower-tier technology
/// needs to be unlocked before this one.
/// </summary>
- [DataField("tier", required: true)]
+ [DataField(required: true)]
public int Tier;
/// <summary>
/// Hidden tech is not ever available at the research console.
/// </summary>
- [DataField("hidden")]
+ [DataField]
public bool Hidden;
/// <summary>
/// How much research is needed to unlock.
/// </summary>
- [DataField("cost")]
+ [DataField]
public 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 IReadOnlyList<string> TechnologyPrerequisites = new List<string>();
+ [DataField]
+ public List<ProtoId<TechnologyPrototype>> TechnologyPrerequisites = new();
/// <summary>
/// A list of <see cref="LatheRecipePrototype"/>s that are unlocked by this technology
/// </summary>
- [DataField("recipeUnlocks", customTypeSerializer: typeof(PrototypeIdListSerializer<LatheRecipePrototype>))]
- public IReadOnlyList<string> RecipeUnlocks = new List<string>();
+ [DataField]
+ public List<ProtoId<LatheRecipePrototype>> RecipeUnlocks = new();
/// <summary>
/// A list of non-standard effects that are done when this technology is unlocked.
/// </summary>
- [DataField("genericUnlocks")]
+ [DataField]
public IReadOnlyList<GenericUnlock> GenericUnlocks = new List<GenericUnlock>();
}
/// What event is raised when this is unlocked?
/// Used for doing non-standard logic.
/// </summary>
- [DataField("purchaseEvent")]
+ [DataField]
public object? PurchaseEvent = null;
/// <summary>
/// A player facing tooltip for what the unlock does.
/// Supports locale strings.
/// </summary>
- [DataField("unlockDescription")]
+ [DataField]
public string UnlockDescription = string.Empty;
}
using Content.Shared.Research.Prototypes;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
+using Robust.Shared.Utility;
namespace Content.Shared.Research.Systems;
component.CurrentTechnologyCards.Add(selected.ID);
}
- Dirty(component);
+ Dirty(uid, component);
}
public List<TechnologyPrototype> GetAvailableTechnologies(EntityUid uid, TechnologyDatabaseComponent? component = null)
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;
+ }
+
/// <summary>
/// Returns whether a technology is unlocked on this database or not.
/// </summary>
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.
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!
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
</Box>
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:
--- /dev/null
+<Document>
+# 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
+<GuideTechDisciplineEmbed Discipline="Industrial"/>
+
+## Biochemical
+<GuideTechDisciplineEmbed Discipline="Biochemical"/>
+
+## Arsenal
+<GuideTechDisciplineEmbed Discipline="Arsenal"/>
+
+## Experimental
+<GuideTechDisciplineEmbed Discipline="Experimental"/>
+
+## Civilian Services
+<GuideTechDisciplineEmbed Discipline="CivilianServices"/>
+
+</Document>