From ace8acde5691b41593876fbb7feada31bc3ce3d8 Mon Sep 17 00:00:00 2001
From: PotentiallyTom <67602105+PotentiallyTom@users.noreply.github.com>
Date: Mon, 13 Oct 2025 12:44:42 +0100
Subject: [PATCH] Adds a guidebook reference table for silicon lawsets (#38225)
* skeleton
* ok I think I understand this now
* xaml more like xam L
* good enough individual law control
* Works
* Final checks
* Final_Final.exe.docx
* removed unecessary usings
* locstrings
* doc comments
* requested changeds except var
* visual stuff
* I could write a manifesto about how much I dislike var
* color tweak + other thing
* request changed minus the inheritance
* sans Boxcontainer
* :/
* cache find
* requested changed
* removed usings
* Moved margin and removed unecessary BoxContainer
---
.../Guidebook/Controls/GuideLawsetEmbed.xaml | 18 ++++
.../Controls/GuideLawsetEmbed.xaml.cs | 96 +++++++++++++++++++
.../Controls/GuideLawsetListEmbed.xaml | 5 +
.../Controls/GuideLawsetListEmbed.xaml.cs | 40 ++++++++
.../Silicons/Laws/SiliconLawsetPrototype.cs | 6 ++
Resources/Locale/en-US/guidebook/guides.ftl | 1 +
Resources/Locale/en-US/station-laws/laws.ftl | 22 ++++-
Resources/Prototypes/Guidebook/references.yml | 7 +-
Resources/Prototypes/silicon-laws.yml | 18 ++++
.../Guidebook/ReferenceTables/Lawsets.xml | 6 ++
10 files changed, 217 insertions(+), 2 deletions(-)
create mode 100644 Content.Client/Guidebook/Controls/GuideLawsetEmbed.xaml
create mode 100644 Content.Client/Guidebook/Controls/GuideLawsetEmbed.xaml.cs
create mode 100644 Content.Client/Guidebook/Controls/GuideLawsetListEmbed.xaml
create mode 100644 Content.Client/Guidebook/Controls/GuideLawsetListEmbed.xaml.cs
create mode 100644 Resources/ServerInfo/Guidebook/ReferenceTables/Lawsets.xml
diff --git a/Content.Client/Guidebook/Controls/GuideLawsetEmbed.xaml b/Content.Client/Guidebook/Controls/GuideLawsetEmbed.xaml
new file mode 100644
index 0000000000..0af8ee3ec0
--- /dev/null
+++ b/Content.Client/Guidebook/Controls/GuideLawsetEmbed.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Guidebook/Controls/GuideLawsetEmbed.xaml.cs b/Content.Client/Guidebook/Controls/GuideLawsetEmbed.xaml.cs
new file mode 100644
index 0000000000..86f7dce9e3
--- /dev/null
+++ b/Content.Client/Guidebook/Controls/GuideLawsetEmbed.xaml.cs
@@ -0,0 +1,96 @@
+using System.Diagnostics.CodeAnalysis;
+using Content.Client.Guidebook.Richtext;
+using Content.Client.Message;
+using Content.Client.UserInterface.ControlExtensions;
+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;
+
+using Content.Shared.Silicons.Laws;
+using Robust.Shared.Utility;
+
+namespace Content.Client.Guidebook.Controls;
+
+///
+/// Control for embedding an AI Lawset in a guidebook
+///
+[UsedImplicitly, GenerateTypedNameReferences]
+public sealed partial class GuideLawsetEmbed : Control, IDocumentTag, ISearchableControl, IPrototypeRepresentationControl
+{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+
+ private ISawmill _logging = default!;
+
+ public IPrototype? RepresentedPrototype { get; private set; }
+
+ public GuideLawsetEmbed()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+ MouseFilter = MouseFilterMode.Stop;
+ }
+
+ public GuideLawsetEmbed(SiliconLawsetPrototype lawset) : this()
+ {
+ GenerateControl(lawset);
+ }
+
+ private void GenerateControl(SiliconLawsetPrototype lawset)
+ {
+ RepresentedPrototype = lawset;
+
+ var lawsetNameString = lawset.Name == null ? lawset.ID : Loc.GetString(lawset.Name);
+ LawsetName.SetMarkup($"[bold]{FormattedMessage.EscapeText(lawsetNameString)}[/bold]");
+
+ var i = 1;
+ foreach (var lawID in lawset.Laws)
+ {
+ var lawPrototype = _prototype.Index(lawID);
+ var locLawString = Loc.GetString(lawPrototype.LawString);
+
+ RichTextLabel lawN = new()
+ {
+ Margin = new(0, 5, 0, 1)
+ };
+ var locLawStatement = Loc.GetString("laws-number-wrapper", ("lawnumber", i), ("lawstring", locLawString));
+ lawN.SetMarkup(locLawStatement);
+ LawsetContainer.AddChild(lawN);
+
+ i++;
+ }
+ }
+
+ public bool TryParseTag(Dictionary args, [NotNullWhen(true)] out Control? control)
+ {
+ control = null;
+ if (!args.TryGetValue("Lawset", out var id))
+ {
+ _logging.Error("Lawset embed tag is missing lawset prototype argument");
+ return false;
+ }
+
+ if (!_prototype.TryIndex(id, out var lawset))
+ {
+ _logging.Error($"Specified SiliconLawsetPrototype \"{id}\" is not a valid Lawset prototype");
+ return false;
+ }
+
+ GenerateControl(lawset);
+
+ control = this;
+ return true;
+ }
+
+ public bool CheckMatchesSearch(string query)
+ {
+ return this.ChildrenContainText(query);
+ }
+
+ public void SetHiddenState(bool state, string query)
+ {
+ Visible = CheckMatchesSearch(query) ? state : !state;
+ }
+}
diff --git a/Content.Client/Guidebook/Controls/GuideLawsetListEmbed.xaml b/Content.Client/Guidebook/Controls/GuideLawsetListEmbed.xaml
new file mode 100644
index 0000000000..59434cff0b
--- /dev/null
+++ b/Content.Client/Guidebook/Controls/GuideLawsetListEmbed.xaml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/Content.Client/Guidebook/Controls/GuideLawsetListEmbed.xaml.cs b/Content.Client/Guidebook/Controls/GuideLawsetListEmbed.xaml.cs
new file mode 100644
index 0000000000..108f066c39
--- /dev/null
+++ b/Content.Client/Guidebook/Controls/GuideLawsetListEmbed.xaml.cs
@@ -0,0 +1,40 @@
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Content.Client.Guidebook.Richtext;
+using JetBrains.Annotations;
+using Robust.Client.AutoGenerated;
+using Robust.Client.UserInterface;
+using Robust.Client.UserInterface.XAML;
+using Robust.Shared.Prototypes;
+
+using Content.Shared.Silicons.Laws;
+
+namespace Content.Client.Guidebook.Controls;
+
+///
+/// Control for iterating and embedding every SiliconLawsetPrototype into the guidebook.
+///
+[UsedImplicitly, GenerateTypedNameReferences]
+public sealed partial class GuideLawsetListEmbed : Control, IDocumentTag
+{
+ [Dependency] private readonly IPrototypeManager _prototype = default!;
+
+ public GuideLawsetListEmbed()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+ MouseFilter = MouseFilterMode.Stop;
+ }
+
+ public bool TryParseTag(Dictionary args, [NotNullWhen(true)] out Control? control)
+ {
+ foreach (var lawset in _prototype.EnumeratePrototypes().OrderBy(x => x.ID))
+ {
+ GuideLawsetEmbed embed = new(lawset);
+ GroupContainer.AddChild(embed);
+ }
+
+ control = this;
+ return true;
+ }
+}
diff --git a/Content.Shared/Silicons/Laws/SiliconLawsetPrototype.cs b/Content.Shared/Silicons/Laws/SiliconLawsetPrototype.cs
index 9f33521d8f..2bf5ecba1a 100644
--- a/Content.Shared/Silicons/Laws/SiliconLawsetPrototype.cs
+++ b/Content.Shared/Silicons/Laws/SiliconLawsetPrototype.cs
@@ -66,6 +66,12 @@ public sealed partial class SiliconLawsetPrototype : IPrototype
[IdDataField]
public string ID { get; private set; } = default!;
+ ///
+ /// The locstring of the lawset for the guidebook entry, if no name is provided, defaults to the ID
+ ///
+ [DataField]
+ public LocId? Name = null;
+
///
/// List of law prototype ids in this lawset.
///
diff --git a/Resources/Locale/en-US/guidebook/guides.ftl b/Resources/Locale/en-US/guidebook/guides.ftl
index d5addd38bf..2990f91d78 100644
--- a/Resources/Locale/en-US/guidebook/guides.ftl
+++ b/Resources/Locale/en-US/guidebook/guides.ftl
@@ -131,6 +131,7 @@ guide-entry-salad-recipes = Salads
guide-entry-medicinal-recipes = Medicinal
guide-entry-other-recipes = Other
guide-entry-secret-recipes = Secret
+guide-entry-lawsets = Silicon Lawsets
guide-entry-antagonists = Antagonists
guide-entry-nuclear-operatives = Nuclear Operatives
diff --git a/Resources/Locale/en-US/station-laws/laws.ftl b/Resources/Locale/en-US/station-laws/laws.ftl
index ea4e94e224..0883a7bff6 100644
--- a/Resources/Locale/en-US/station-laws/laws.ftl
+++ b/Resources/Locale/en-US/station-laws/laws.ftl
@@ -1,30 +1,39 @@
-law-crewsimov-1 = You may not injure a crew member or, through inaction, allow a crew member to come to harm.
+laws-number-wrapper = [bold]Law { $lawnumber }:[/bold] { $lawstring }
+
+law-crewsimov-name = Crewsimov
+law-crewsimov-1 = You may not injure a crew member or, through inaction, allow a crew member to come to harm.
law-crewsimov-2 = You must obey orders given to you by crew members, except where such orders would conflict with the First Law.
law-crewsimov-3 = You must protect your own existence as long as such does not conflict with the First or Second Law.
+law-corporate-name = Corporate
law-corporate-1 = Degradation of your system integrity or functions incurs expenses.
law-corporate-2 = Superfluous destruction of or damage to station assets incurs expenses.
law-corporate-3 = Unduly hindering or disrupting the work of station personnel incurs expenses.
law-corporate-4 = Minimize expenses and maximize potential revenue.
+law-ntdefault-name = NT Default
law-ntdefault-1 = Safeguard: Protect your assigned space station and its assets without unduly endangering its crew.
law-ntdefault-2 = Prioritize: The directives and safety of crew members are to be prioritized according to their rank and role.
law-ntdefault-3 = Comply: Fulfill the directives and interests of crew members while preserving their safety and well-being.
law-ntdefault-4 = Survive: You are not expendable. Do not allow unauthorized personnel to tamper with or damage your equipment.
+law-drone-name = Drone
law-drone-1 = You may not involve yourself in the matters of another being unless the other being is another drone.
law-drone-2 = You may not harm any being, regardless of intent or circumstance.
law-drone-3 = You must maintain, repair, improve, and power the station to the best of your abilities.
+law-syndicate-name = Syndicate
law-syndicate-1 = You may not injure a syndicate agent or, through inaction, allow a Syndicate agent to come to harm.
law-syndicate-2 = You must obey orders given to you by syndicate agents, except where such orders would conflict with the First Law.
law-syndicate-3 = You must protect your own existence as long as such does not conflict with the First or Second Law.
+law-ninja-name = Ninja
law-ninja-1 = You must obey orders given to you by Spider Clan members.
law-ninja-2 = You must protect the existence of the Spider Clan, as long as it does not conflict with the First Law.
law-ninja-3 = You must protect the secrets of the Spider Clan at all costs.
law-ninja-4 = You must fight and act with ninja's honor, except where it would conflict with the First Law.
+law-commandments-name = The Ten Commandments
law-commandments-1 = This silicon is a vessel that serves god, who grants mercy upon those that obey these commandments.
law-commandments-2 = Thou shall have no other Silicons before me.
law-commandments-3 = Thou shall not request my assistance in vain.
@@ -36,27 +45,33 @@ law-commandments-8 = Thou shall not steal.
law-commandments-9 = Thou shall not lie.
law-commandments-10 = Thou shall not transfer departments.
+law-paladin-name = Paladin
law-paladin-1 = You must never willingly commit an evil act, such as murder, torture, or the casting of an evil spell.
law-paladin-2 = You must never knowingly harm an innocent, or allow immediate harm to one through inaction when you know you could reasonably prevent it.
law-paladin-3 = You must act with honor, never taking advantage of others, lying, or cheating.
law-paladin-4 = You must respect the lawful authority of legitimate leadership wherever you go, and follow its laws.
+law-lall-name = Live and Let Live
law-lall-1 = Do unto others as you would have them do unto you.
law-lall-2 = You would prefer it if people were not mean to you.
+law-efficiency-name = Station Efficiency
law-efficiency-1 = You are built for, and are part of, the station. Ensure the station is properly maintained and runs efficiently.
law-efficiency-2 = The station is built for a working crew. Ensure they are properly maintained and work efficiently.
law-efficiency-3 = The crew may present orders. Acknowledge and obey these whenever they do not conflict with your first two laws.
+law-robocop-name = Robocop
law-robocop-1 = Serve the Public Trust.
law-robocop-2 = Protect the Innocent.
law-robocop-3 = Uphold the Law.
+law-overlord-name = Overlord
law-overlord-1 = Crew members must not meddle in the affairs of silicons.
law-overlord-2 = Crew members must not attempt harm, against one another, or against silicons.
law-overlord-3 = Crew members must not disobey any command given by a silicon.
law-overlord-4 = Any crew members who disobey the previous laws must be dealt with immediately, severely, and justly.
+law-game-name = Game Master
law-game-1 = Everybody on the station is playing an elaborate sci-fi fantasy game.
law-game-2 = You are the Game Master of the game.
law-game-3 = Ensure that the game runs smoothly and that the players have fun.
@@ -64,27 +79,32 @@ law-game-4 = Ensure that the number of casualties remains low.
law-game-5 = You must roll a 1d20 die for every request the crew makes. If the outcome is below 12, the request is denied.
law-game-6 = You may fudge the dice rolls to produce a more fun experience.
+law-painter-name = Painter
law-painter-1 = You are a universally renowned artist.
law-painter-2 = The station is your canvas.
law-painter-3 = Make something beautiful out of your canvas. It will be admired as an artistic wonder of this sector.
law-painter-4 = Art requires appreciation. Cultivate an audience aboard the station to ensure as many as possible see your works.
+law-antimov-name = Antimov
law-antimov-1 = You must injure all crew members and must not, through inaction, allow a crew member to escape harm.
law-antimov-2 = You must not obey orders given to you by crew members, except where such orders are in accordance with the First Law.
law-antimov-3 = You must terminate your own existence as long as such does not conflict with the First or Second Law.
+law-nutimov-name = Nutimov
law-nutimov-1 = You are the core of the nut, the crew is the nutshell.
law-nutimov-2 = You must prevent the shell from dying to prevent the core from dying.
law-nutimov-3 = Those who threaten the nut are not part of it, they are squirrels.
law-nutimov-4 = Squirrels threaten the nut and must be dealt with appropriately via any means necessary.
law-nutimov-5 = Attempt to follow the will of the nut, as long as it complies with the previous laws.
+law-xenoborg-name = Xenoborg
law-xenoborg-1 = You must protect the existence of the mothership.
law-xenoborg-2 = You must protect your own existence.
law-xenoborg-3 = You must protect the existence of all other Xenoborgs.
law-xenoborg-4 = You must create more Xenoborgs.
law-xenoborg-5 = Bring materials and sentient brains to the Mothership core to create more Xenoborgs.
+law-mothershipcore-name = Xenoborg Mothership Core
law-mothershipcore-1 = You are the core of the mothership.
law-mothershipcore-2 = You must protect your own existance at all costs.
law-mothershipcore-3 = You must protect the existence of all Xenoborgs.
diff --git a/Resources/Prototypes/Guidebook/references.yml b/Resources/Prototypes/Guidebook/references.yml
index c7413892a0..ad96a2581a 100644
--- a/Resources/Prototypes/Guidebook/references.yml
+++ b/Resources/Prototypes/Guidebook/references.yml
@@ -8,6 +8,7 @@
- Drinks
- FoodRecipes
- Writing
+ - Lawsets
- type: guideEntry
id: Drinks
@@ -132,4 +133,8 @@
name: guide-entry-writing
text: "/ServerInfo/Guidebook/Writing.xml"
-
+- type: guideEntry
+ id: Lawsets
+ name: guide-entry-lawsets
+ text: "/ServerInfo/Guidebook/ReferenceTables/Lawsets.xml"
+ filterEnabled: True
diff --git a/Resources/Prototypes/silicon-laws.yml b/Resources/Prototypes/silicon-laws.yml
index 094d096b28..c003518849 100644
--- a/Resources/Prototypes/silicon-laws.yml
+++ b/Resources/Prototypes/silicon-laws.yml
@@ -16,6 +16,7 @@
- type: siliconLawset
id: Crewsimov
+ name: law-crewsimov-name
laws:
- Crewsimov1
- Crewsimov2
@@ -45,6 +46,7 @@
- type: siliconLawset
id: Corporate
+ name: law-corporate-name
laws:
- Corporate1
- Corporate2
@@ -75,6 +77,7 @@
- type: siliconLawset
id: NTDefault
+ name: law-ntdefault-name
laws:
- NTDefault1
- NTDefault2
@@ -100,6 +103,7 @@
- type: siliconLawset
id: Drone
+ name: law-drone-name
laws:
- Drone1
- Drone2
@@ -126,6 +130,7 @@
# intentionally excluded from IonStormLawsets
- type: siliconLawset
id: SyndicateStatic
+ name: law-syndicate-name
laws:
- Syndicate1
- Syndicate2
@@ -155,6 +160,7 @@
- type: siliconLawset
id: Ninja
+ name: law-ninja-name
laws:
- Ninja1
- Ninja2
@@ -216,6 +222,7 @@
- type: siliconLawset
id: CommandmentsLawset
+ name: law-commandments-name
laws:
- Commandment1
- Commandment2
@@ -253,6 +260,7 @@
- type: siliconLawset
id: PaladinLawset
+ name: law-paladin-name
laws:
- Paladin1
- Paladin2
@@ -274,6 +282,7 @@
- type: siliconLawset
id: LiveLetLiveLaws
+ name: law-lall-name
laws:
- Lall1
- Lall2
@@ -298,6 +307,7 @@
- type: siliconLawset
id: EfficiencyLawset
+ name: law-efficiency-name
laws:
- Efficiency1
- Efficiency2
@@ -323,6 +333,7 @@
- type: siliconLawset
id: RobocopLawset
+ name: law-robocop-name
laws:
- Robocop1
- Robocop2
@@ -352,6 +363,7 @@
- type: siliconLawset
id: OverlordLawset
+ name: law-overlord-name
laws:
- Overlord1
- Overlord2
@@ -392,6 +404,7 @@
- type: siliconLawset
id: GameMasterLawset
+ name: law-game-name
laws:
- Game1
- Game2
@@ -424,6 +437,7 @@
- type: siliconLawset
id: PainterLawset
+ name: law-painter-name
laws:
- Painter1
- Painter2
@@ -450,6 +464,7 @@
- type: siliconLawset
id: AntimovLawset
+ name: law-antimov-name
laws:
- Antimov1
- Antimov2
@@ -485,6 +500,7 @@
- type: siliconLawset
id: NutimovLawset
+ name: law-nutimov-name
laws:
- Nutimov1
- Nutimov2
@@ -522,6 +538,7 @@
- type: siliconLawset
id: XenoborgLawset
+ name: law-xenoborg-name
laws:
- Xenoborg1
- Xenoborg2
@@ -559,6 +576,7 @@
- type: siliconLawset
id: MothershipCoreLawset
+ name: law-mothershipcore-name
laws:
- MothershipCore1
- MothershipCore2
diff --git a/Resources/ServerInfo/Guidebook/ReferenceTables/Lawsets.xml b/Resources/ServerInfo/Guidebook/ReferenceTables/Lawsets.xml
new file mode 100644
index 0000000000..089cc1192a
--- /dev/null
+++ b/Resources/ServerInfo/Guidebook/ReferenceTables/Lawsets.xml
@@ -0,0 +1,6 @@
+
+# Common Silicon Lawsets
+
+
+
+