Species info in Guidebook and at chargen (#25844)

* guidebook pages defined

* species info button in character profile editor

* if current species has no guidebook page then open the parent page

* skrek

* destroying evidence of secret vox plot

* icon size adjustment, no icon if guidebook page for species does not exist

* finished pages

* additional info

* weh
This commit is contained in:
Errant
2024-03-11 04:01:32 +01:00
committed by GitHub
parent 8de4b4196e
commit 84a07315dd
16 changed files with 279 additions and 17 deletions

View File

@@ -58,6 +58,7 @@
<BoxContainer HorizontalExpand="True"> <BoxContainer HorizontalExpand="True">
<Label Text="{Loc 'humanoid-profile-editor-species-label'}" /> <Label Text="{Loc 'humanoid-profile-editor-species-label'}" />
<Control HorizontalExpand="True"/> <Control HorizontalExpand="True"/>
<TextureButton Name="SpeciesInfoButton" Scale="0.3 0.3" VerticalAlignment="Center"></TextureButton>
<OptionButton Name="CSpeciesButton" HorizontalAlignment="Right" /> <OptionButton Name="CSpeciesButton" HorizontalAlignment="Right" />
</BoxContainer> </BoxContainer>
<!-- Age --> <!-- Age -->

View File

@@ -1,11 +1,13 @@
using System.Linq; using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Client.Guidebook;
using Content.Client.Humanoid; using Content.Client.Humanoid;
using Content.Client.Lobby.UI; using Content.Client.Lobby.UI;
using Content.Client.Message; using Content.Client.Message;
using Content.Client.Players.PlayTimeTracking; using Content.Client.Players.PlayTimeTracking;
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Controls;
using Content.Client.UserInterface.Systems.Guidebook;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Humanoid; using Content.Shared.Humanoid;
@@ -116,6 +118,8 @@ namespace Content.Client.Preferences.UI
_configurationManager = configurationManager; _configurationManager = configurationManager;
_markingManager = IoCManager.Resolve<MarkingManager>(); _markingManager = IoCManager.Resolve<MarkingManager>();
SpeciesInfoButton.ToolTip = Loc.GetString("humanoid-profile-editor-guidebook-button-tooltip");
#region Left #region Left
#region Randomize #region Randomize
@@ -523,10 +527,30 @@ namespace Content.Client.Preferences.UI
preferencesManager.OnServerDataLoaded += LoadServerData; preferencesManager.OnServerDataLoaded += LoadServerData;
SpeciesInfoButton.OnPressed += OnSpeciesInfoButtonPressed;
UpdateSpeciesGuidebookIcon();
IsDirty = false; IsDirty = false;
} }
private void OnSpeciesInfoButtonPressed(BaseButton.ButtonEventArgs args)
{
var guidebookController = UserInterfaceManager.GetUIController<GuidebookUIController>();
var species = Profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies;
var page = "Species";
if (_prototypeManager.HasIndex<GuideEntryPrototype>(species))
page = species;
if (_prototypeManager.TryIndex<GuideEntryPrototype>("Species", out var guideRoot))
{
var dict = new Dictionary<string, GuideEntry>();
dict.Add("Species", guideRoot);
//TODO: Don't close the guidebook if its already open, just go to the correct page
guidebookController.ToggleGuidebook(dict, includeChildren:true, selected: page);
}
}
private void ToggleClothes(BaseButton.ButtonEventArgs obj) private void ToggleClothes(BaseButton.ButtonEventArgs obj)
{ {
RebuildSpriteView(); RebuildSpriteView();
@@ -790,6 +814,7 @@ namespace Content.Client.Preferences.UI
CMarkings.SetSpecies(newSpecies); // Repopulate the markings tab as well. CMarkings.SetSpecies(newSpecies); // Repopulate the markings tab as well.
UpdateSexControls(); // update sex for new species UpdateSexControls(); // update sex for new species
RebuildSpriteView(); // they might have different inv so we need a new dummy RebuildSpriteView(); // they might have different inv so we need a new dummy
UpdateSpeciesGuidebookIcon();
IsDirty = true; IsDirty = true;
_needUpdatePreview = true; _needUpdatePreview = true;
} }
@@ -941,6 +966,25 @@ namespace Content.Client.Preferences.UI
} }
public void UpdateSpeciesGuidebookIcon()
{
SpeciesInfoButton.StyleClasses.Clear();
var species = Profile?.Species;
if (species is null)
return;
if (!_prototypeManager.TryIndex<SpeciesPrototype>(species, out var speciesProto))
return;
// Don't display the info button if no guide entry is found
if (!_prototypeManager.HasIndex<GuideEntryPrototype>(species))
return;
var style = speciesProto.GuideBookIcon;
SpeciesInfoButton.StyleClasses.Add(style);
}
private void UpdateMarkings() private void UpdateMarkings()
{ {
if (Profile == null) if (Profile == null)

View File

@@ -1392,6 +1392,14 @@ namespace Content.Client.Stylesheets
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#753131")), .Prop(Control.StylePropertyModulateSelf, Color.FromHex("#753131")),
// --- // ---
// Profile Editor
Element<TextureButton>().Class("SpeciesInfoDefault")
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/Interface/VerbIcons/information.svg.192dpi.png")),
Element<TextureButton>().Class("SpeciesInfoWarning")
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/Interface/info.svg.192dpi.png"))
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#eeee11")),
// The default look of paper in UIs. Pages can have components which override this // The default look of paper in UIs. Pages can have components which override this
Element<PanelContainer>().Class("PaperDefaultBorder") Element<PanelContainer>().Class("PaperDefaultBorder")
.Prop(PanelContainer.StylePropertyPanel, paperBackground), .Prop(PanelContainer.StylePropertyPanel, paperBackground),

View File

@@ -15,7 +15,7 @@ public sealed partial class SpeciesPrototype : IPrototype
/// <summary> /// <summary>
/// User visible name of the species. /// User visible name of the species.
/// </summary> /// </summary>
[DataField("name", required: true)] [DataField(required: true)]
public string Name { get; private set; } = default!; public string Name { get; private set; } = default!;
/// <summary> /// <summary>
@@ -23,13 +23,13 @@ public sealed partial class SpeciesPrototype : IPrototype
/// for an eventual integration into IdentitySystem /// for an eventual integration into IdentitySystem
/// (i.e., young human person, young lizard person, etc.) /// (i.e., young human person, young lizard person, etc.)
/// </summary> /// </summary>
[DataField("descriptor")] [DataField]
public string Descriptor { get; private set; } = "humanoid"; public string Descriptor { get; private set; } = "humanoid";
/// <summary> /// <summary>
/// Whether the species is available "at round start" (In the character editor) /// Whether the species is available "at round start" (In the character editor)
/// </summary> /// </summary>
[DataField("roundStart", required: true)] [DataField(required: true)]
public bool RoundStart { get; private set; } = false; public bool RoundStart { get; private set; } = false;
// The below two are to avoid fetching information about the species from the entity // The below two are to avoid fetching information about the species from the entity
@@ -47,14 +47,14 @@ public sealed partial class SpeciesPrototype : IPrototype
/// <summary> /// <summary>
/// Default skin tone for this species. This applies for non-human skin tones. /// Default skin tone for this species. This applies for non-human skin tones.
/// </summary> /// </summary>
[DataField("defaultSkinTone")] [DataField]
public Color DefaultSkinTone { get; private set; } = Color.White; public Color DefaultSkinTone { get; private set; } = Color.White;
/// <summary> /// <summary>
/// Default human skin tone for this species. This applies for human skin tones. /// Default human skin tone for this species. This applies for human skin tones.
/// See <see cref="SkinColor.HumanSkinTone"/> for the valid range of skin tones. /// See <see cref="SkinColor.HumanSkinTone"/> for the valid range of skin tones.
/// </summary> /// </summary>
[DataField("defaultHumanSkinTone")] [DataField]
public int DefaultHumanSkinTone { get; private set; } = 20; public int DefaultHumanSkinTone { get; private set; } = 20;
/// <summary> /// <summary>
@@ -66,60 +66,66 @@ public sealed partial class SpeciesPrototype : IPrototype
/// <summary> /// <summary>
/// Humanoid species variant used by this entity. /// Humanoid species variant used by this entity.
/// </summary> /// </summary>
[DataField("prototype", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))] [DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string Prototype { get; private set; } = default!; public string Prototype { get; private set; } = default!;
/// <summary> /// <summary>
/// Prototype used by the species for the dress-up doll in various menus. /// Prototype used by the species for the dress-up doll in various menus.
/// </summary> /// </summary>
[DataField("dollPrototype", required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))] [DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string DollPrototype { get; private set; } = default!; public string DollPrototype { get; private set; } = default!;
/// <summary> /// <summary>
/// Method of skin coloration used by the species. /// Method of skin coloration used by the species.
/// </summary> /// </summary>
[DataField("skinColoration", required: true)] [DataField(required: true)]
public HumanoidSkinColor SkinColoration { get; private set; } public HumanoidSkinColor SkinColoration { get; private set; }
[DataField("maleFirstNames")] [DataField]
public string MaleFirstNames { get; private set; } = "names_first_male"; public string MaleFirstNames { get; private set; } = "names_first_male";
[DataField("femaleFirstNames")] [DataField]
public string FemaleFirstNames { get; private set; } = "names_first_female"; public string FemaleFirstNames { get; private set; } = "names_first_female";
[DataField("lastNames")] [DataField]
public string LastNames { get; private set; } = "names_last"; public string LastNames { get; private set; } = "names_last";
[DataField("naming")] [DataField]
public SpeciesNaming Naming { get; private set; } = SpeciesNaming.FirstLast; public SpeciesNaming Naming { get; private set; } = SpeciesNaming.FirstLast;
[DataField("sexes")] [DataField]
public List<Sex> Sexes { get; private set; } = new() { Sex.Male, Sex.Female }; public List<Sex> Sexes { get; private set; } = new() { Sex.Male, Sex.Female };
/// <summary> /// <summary>
/// Characters younger than this are too young to be hired by Nanotrasen. /// Characters younger than this are too young to be hired by Nanotrasen.
/// </summary> /// </summary>
[DataField("minAge")] [DataField]
public int MinAge = 18; public int MinAge = 18;
/// <summary> /// <summary>
/// Characters younger than this appear young. /// Characters younger than this appear young.
/// </summary> /// </summary>
[DataField("youngAge")] [DataField]
public int YoungAge = 30; public int YoungAge = 30;
/// <summary> /// <summary>
/// Characters older than this appear old. Characters in between young and old age appear middle aged. /// Characters older than this appear old. Characters in between young and old age appear middle aged.
/// </summary> /// </summary>
[DataField("oldAge")] [DataField]
public int OldAge = 60; public int OldAge = 60;
/// <summary> /// <summary>
/// Characters cannot be older than this. Only used for restrictions... /// Characters cannot be older than this. Only used for restrictions...
/// although imagine if ghosts could age people WYCI... /// although imagine if ghosts could age people WYCI...
/// </summary> /// </summary>
[DataField("maxAge")] [DataField]
public int MaxAge = 120; public int MaxAge = 120;
/// <summary>
/// The Style used for the guidebook info link in the character profile editor
/// </summary>
[DataField]
public string GuideBookIcon = "SpeciesInfoDefault";
} }
public enum SpeciesNaming : byte public enum SpeciesNaming : byte

View File

@@ -50,6 +50,7 @@ guide-entry-security = Security
guide-entry-forensics = Forensics guide-entry-forensics = Forensics
guide-entry-defusal = Large Bomb Defusal guide-entry-defusal = Large Bomb Defusal
guide-entry-criminal-records = Criminal Records guide-entry-criminal-records = Criminal Records
guide-entry-species = Species
guide-entry-antagonists = Antagonists guide-entry-antagonists = Antagonists
guide-entry-nuclear-operatives = Nuclear Operatives guide-entry-nuclear-operatives = Nuclear Operatives

View File

@@ -31,6 +31,7 @@ humanoid-profile-editor-preference-jumpskirt = Jumpskirt
humanoid-profile-editor-preference-backpack = Backpack humanoid-profile-editor-preference-backpack = Backpack
humanoid-profile-editor-preference-satchel = Satchel humanoid-profile-editor-preference-satchel = Satchel
humanoid-profile-editor-preference-duffelbag = Duffelbag humanoid-profile-editor-preference-duffelbag = Duffelbag
humanoid-profile-editor-guidebook-button-tooltip = Click for more info
# Spawn priority # Spawn priority
humanoid-profile-editor-preference-spawn-priority-none = None humanoid-profile-editor-preference-spawn-priority-none = None

View File

@@ -0,0 +1,47 @@
- type: guideEntry
id: Species
name: guide-entry-species
text: "/ServerInfo/Guidebook/Mobs/Species.xml"
children:
- Arachnid
- Diona
- Dwarf
- Human
- Moth
- Reptilian
- SlimePerson
- type: guideEntry
id: Arachnid
name: species-name-arachnid
text: "/ServerInfo/Guidebook/Mobs/Arachnid.xml"
- type: guideEntry
id: Diona
name: species-name-diona
text: "/ServerInfo/Guidebook/Mobs/Diona.xml"
- type: guideEntry
id: Dwarf
name: species-name-dwarf
text: "/ServerInfo/Guidebook/Mobs/Dwarf.xml"
- type: guideEntry
id: Human
name: species-name-human
text: "/ServerInfo/Guidebook/Mobs/Human.xml"
- type: guideEntry
id: Moth
name: species-name-moth
text: "/ServerInfo/Guidebook/Mobs/Moth.xml"
- type: guideEntry
id: Reptilian
name: species-name-reptilian
text: "/ServerInfo/Guidebook/Mobs/Reptilian.xml"
- type: guideEntry
id: SlimePerson
name: species-name-slime
text: "/ServerInfo/Guidebook/Mobs/SlimePerson.xml"

View File

@@ -8,6 +8,7 @@
- Survival - Survival
- Chemicals - Chemicals
- Antagonists - Antagonists
- Species
- Writing - Writing
- Glossary - Glossary

View File

@@ -0,0 +1,25 @@
<Document>
# Arachnids
<Box>
<GuideEntityEmbed Entity="MobArachnid" Caption=""/>
</Box>
They have two additional Pocket slots in their inventory. They can eat raw meat without any ill effects, but some foods like chocolate and onion poisons them.
They suffocate 50% faster, and their Blue Blood can't be metabolised from Iron, being based on Copper instead.
Their unarmed attacks deal Piercing damage instead of Blunt.
## Sericulture
<Box>
<GuideEntityEmbed Entity="MaterialWebSilk" Caption=""/>
<GuideEntityEmbed Entity="ClothingUniformJumpskirtWeb" Caption=""/>
<GuideEntityEmbed Entity="WebShield" Caption=""/>
<GuideEntityEmbed Entity="WallWeb" Caption=""/>
</Box>
Arachnids can create Websilk at the cost of getting more hungry. They (and only they) can craft bundles of websilk into various items from clothing and shields to entire walls.
</Document>

View File

@@ -0,0 +1,38 @@
<Document>
# Diona
<Box>
<GuideEntityEmbed Entity="MobDiona" Caption=""/>
</Box>
They can't wear shoes, but are not slowed by Kudzu.
They get hungry and thirsty slower.
Their "blood" is normal water and can't be metabolised from Iron.
Being plants, Weed Killer poisons them, while Robust Harvest heals them (but not without risk when overused!)
They take [color=#1e90ff]30% less Blunt damage and 20% less Slash damage[/color];
but [color=#ffa500]50% more Heat damage, 20% more Shock damage, and they can easily
catch on fire when receiving enough Heat damage from *any* source.[/color]
## Make Like A Tree And Leave
<Box>
<GuideEntityEmbed Entity="FloraTree06" Caption=""/>
</Box>
Being exposed to too much Robust Harvest will cause a Diona to grow out of control, turning into an immobile tree (dropping all their equipment).
Cutting down the tree will "restore" the Diona to their mobile state.
## Diona Nymphs
<Box>
<GuideEntityEmbed Entity="MobDionaNymph" Caption=""/>
<GuideEntityEmbed Entity="MobDionaNymph" Caption=""/>
<GuideEntityEmbed Entity="MobDionaNymph" Caption=""/>
</Box>
After death, a Diona can voluntarily destroy their own body, releasing their "internal organs" as three Nymphs,
with the player taking control of the Brain Nymph.
It can talk but has no hands or inventory, and can't do much.
After 10 minutes, a Nymph can reform into a whole Diona. This will be a new randomised body with a random name,
and there will be little to no evidence beyond their word about who they were before.
</Document>

View File

@@ -0,0 +1,11 @@
<Document>
# Dwarves
<Box>
<GuideEntityEmbed Entity="MobDwarf" Caption=""/>
</Box>
Dwarves are similar to humans in most respect, but tolerate alcohol better and are healed by it.
</Document>

View File

@@ -0,0 +1,11 @@
<Document>
# Humans
<Box>
<GuideEntityEmbed Entity="MobHumanDummy" Caption=""/>
</Box>
Depending on who you ask, humans are either unremarkable or the universal standard to which everything else is compared.
They have no special mechanics or notable attributes.
</Document>

View File

@@ -0,0 +1,15 @@
<Document>
# Moth People
<Box>
<GuideEntityEmbed Entity="MobMoth" Caption=""/>
</Box>
They can eat cotton, fabrics and clothing, but virtually none of the food that others would consider "edible". They prefer a somewhat lower temperature range than humans.
Their Insect Blood can't be metabolised from Iron like normal blood.
Their wings give them better acceleration if there is no gravity on the station, but they still can't move without equipment when floating out in space.
They take [color=#1e90ff]30% less Cold damage[/color] but [color=#ffa500]30% more Heat damage, and catch on fire more easily[/color].
</Document>

View File

@@ -0,0 +1,16 @@
<Document>
# Reptilians
<Box>
<GuideEntityEmbed Entity="MobReptilian" Caption=""/>
</Box>
They can ONLY eat fruits and meat, but can eat raw meat and drink blood without any ill effects.
They prefer a somewhat higher temperature range than humans.
They can drag objects with their tail, keeping both their hands free.
Their unarmed claw attacks deal Slash damage instead of Blunt.
They take [color=#ffa500]30% more Cold damage.[/color]
</Document>

View File

@@ -0,0 +1,19 @@
<Document>
# Slime People
<Box>
<GuideEntityEmbed Entity="MobSlimePerson" Caption=""/>
</Box>
They breathe nitrogen instead of oxygen, which is abundant in the station air, but harder to find compressed into gas tanks. They take significant damage if they are sprayed or splashed with water, but can
(and like other species, need to) drink it safely to stay hydrated.
They exhale nitrous oxide and are unaffected by it.
Their body can process 6 reagents at the same time instead of just 2.
Their Slime "blood" can not be regenerated from Iron. Slime Blood is technically a source of
moderately filling food for other species, although drinking the blood of your coworkers is usually frowned upon.
They suffocate 80% slower, but take pressure damage 9% faster. This makes them by far the species most capable to survive in hard vacuum. For a while.
They take [color=#1e90ff]80% less Cellular damage, 40% less Blunt damage and 20% less Poison damage[/color], but [color=#ffa500]50% more Cold damage, 20% more Slash damage and 20% more Piercing damage[/color].
</Document>

View File

@@ -0,0 +1,18 @@
<Document>
# Species
Nanotrasen employs a variety of sapient species.
<Box>
<GuideEntityEmbed Entity="MobArachnid" Caption="Arachnid"/>
<GuideEntityEmbed Entity="MobDiona" Caption="Diona"/>
<GuideEntityEmbed Entity="MobDwarf" Caption="Dwarf"/>
<GuideEntityEmbed Entity="MobHuman" Caption="Human"/>
</Box>
<Box>
<GuideEntityEmbed Entity="MobMoth" Caption="Moth Person"/>
<GuideEntityEmbed Entity="MobReptilian" Caption="Reptilian"/>
<GuideEntityEmbed Entity="MobSlimePerson" Caption="Slime Person"/>
</Box>
</Document>