Accent trait limit (#28046)

This commit is contained in:
Ed
2024-06-03 21:47:06 +03:00
committed by GitHub
parent ee8224bce2
commit a4d1601758
14 changed files with 365 additions and 171 deletions

View File

@@ -7,6 +7,7 @@ using Content.Client.Lobby.UI.Loadouts;
using Content.Client.Lobby.UI.Roles;
using Content.Client.Message;
using Content.Client.Players.PlayTimeTracking;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Systems.Guidebook;
using Content.Shared.CCVar;
using Content.Shared.Clothing;
@@ -466,38 +467,96 @@ namespace Content.Client.Lobby.UI
var traits = _prototypeManager.EnumeratePrototypes<TraitPrototype>().OrderBy(t => Loc.GetString(t.Name)).ToList();
TabContainer.SetTabTitle(3, Loc.GetString("humanoid-profile-editor-traits-tab"));
if (traits.Count > 0)
{
foreach (var trait in traits)
{
var selector = new TraitPreferenceSelector(trait);
if (Profile?.TraitPreferences.Contains(trait.ID) == true)
{
selector.Preference = true;
}
else
{
selector.Preference = false;
}
selector.PreferenceChanged += preference =>
{
Profile = Profile?.WithTraitPreference(trait.ID, preference);
SetDirty();
};
TraitsList.AddChild(selector);
}
}
else
if (traits.Count < 1)
{
TraitsList.AddChild(new Label
{
// TODO: Localise
Text = "No traits available :(",
Text = Loc.GetString("humanoid-profile-editor-no-traits"),
FontColorOverride = Color.Gray,
});
return;
}
//Setup model
Dictionary<string, List<string>> model = new();
List<string> defaultTraits = new();
model.Add("default", defaultTraits);
foreach (var trait in traits)
{
if (trait.Category == null)
{
defaultTraits.Add(trait.ID);
continue;
}
if (!model.ContainsKey(trait.Category))
{
model.Add(trait.Category, new());
}
model[trait.Category].Add(trait.ID);
}
//Create UI view from model
foreach (var (categoryId, traitId) in model)
{
TraitCategoryPrototype? category = null;
if (categoryId != "default")
{
category = _prototypeManager.Index<TraitCategoryPrototype>(categoryId);
// Label
TraitsList.AddChild(new Label
{
Text = Loc.GetString(category.Name),
Margin = new Thickness(0, 10, 0, 0),
StyleClasses = { StyleBase.StyleClassLabelHeading },
});
}
List<TraitPreferenceSelector?> selectors = new();
var selectionCount = 0;
foreach (var traitProto in traitId)
{
var trait = _prototypeManager.Index<TraitPrototype>(traitProto);
var selector = new TraitPreferenceSelector(trait);
selector.Preference = Profile?.TraitPreferences.Contains(trait.ID) == true;
if (selector.Preference)
selectionCount += trait.Cost;
selector.PreferenceChanged += preference =>
{
Profile = Profile?.WithTraitPreference(trait.ID, categoryId, preference);
SetDirty();
RefreshTraits(); // If too many traits are selected, they will be reset to the real value.
};
selectors.Add(selector);
}
// Selection counter
if (category is { MaxTraitPoints: >= 0 })
{
TraitsList.AddChild(new Label
{
Text = Loc.GetString("humanoid-profile-editor-trait-count-hint", ("current", selectionCount) ,("max", category.MaxTraitPoints)),
FontColorOverride = Color.Gray
});
}
foreach (var selector in selectors)
{
if (selector == null)
continue;
if (category is { MaxTraitPoints: >= 0 } &&
selector.Cost + selectionCount > category.MaxTraitPoints)
{
selector.Checkbox.Label.FontColorOverride = Color.Red;
}
TraitsList.AddChild(selector);
}
}
}

View File

@@ -2,6 +2,6 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<BoxContainer Name="Container"
Orientation="Horizontal">
<CheckBox Name="Checkbox"/>
<CheckBox Name="Checkbox" Access="Public"/>
</BoxContainer>
</Control>

View File

@@ -9,6 +9,8 @@ namespace Content.Client.Lobby.UI.Roles;
[GenerateTypedNameReferences]
public sealed partial class TraitPreferenceSelector : Control
{
public int Cost;
public bool Preference
{
get => Checkbox.Pressed;
@@ -20,7 +22,12 @@ public sealed partial class TraitPreferenceSelector : Control
public TraitPreferenceSelector(TraitPrototype trait)
{
RobustXamlLoader.Load(this);
Checkbox.Text = Loc.GetString(trait.Name);
var text = trait.Cost != 0 ? $"[{trait.Cost}] " : "";
text += Loc.GetString(trait.Name);
Cost = trait.Cost;
Checkbox.Text = text;
Checkbox.OnToggled += OnCheckBoxToggled;
if (trait.Description is { } desc)

View File

@@ -38,27 +38,21 @@ public sealed class TraitSystem : EntitySystem
continue;
// Add all components required by the prototype
foreach (var entry in traitPrototype.Components.Values)
{
if (HasComp(args.Mob, entry.Component.GetType()))
continue;
var comp = (Component) _serializationManager.CreateCopy(entry.Component, notNullableOverride: true);
comp.Owner = args.Mob;
EntityManager.AddComponent(args.Mob, comp);
}
EntityManager.AddComponents(args.Mob, traitPrototype.Components, false);
// Add item required by the trait
if (traitPrototype.TraitGear != null)
{
if (traitPrototype.TraitGear == null)
continue;
if (!TryComp(args.Mob, out HandsComponent? handsComponent))
continue;
var coords = Transform(args.Mob).Coordinates;
var inhandEntity = EntityManager.SpawnEntity(traitPrototype.TraitGear, coords);
_sharedHandsSystem.TryPickup(args.Mob, inhandEntity, checkActionBlocker: false,
_sharedHandsSystem.TryPickup(args.Mob,
inhandEntity,
checkActionBlocker: false,
handsComp: handsComponent);
}
}
}
}

View File

@@ -346,13 +346,43 @@ namespace Content.Shared.Preferences
};
}
public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref)
public HumanoidCharacterProfile WithTraitPreference(string traitId, string? categoryId, bool pref)
{
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
var traitProto = prototypeManager.Index<TraitPrototype>(traitId);
TraitCategoryPrototype? categoryProto = null;
if (categoryId != null && categoryId != "default")
categoryProto = prototypeManager.Index<TraitCategoryPrototype>(categoryId);
var list = new HashSet<string>(_traitPreferences);
if (pref)
{
list.Add(traitId);
if (categoryProto == null || categoryProto.MaxTraitPoints < 0)
{
return new(this)
{
_traitPreferences = list,
};
}
var count = 0;
foreach (var trait in list)
{
var traitProtoTemp = prototypeManager.Index<TraitPrototype>(trait);
count += traitProtoTemp.Cost;
}
if (count > categoryProto.MaxTraitPoints && traitProto.Cost != 0)
{
return new(this)
{
_traitPreferences = _traitPreferences,
};
}
}
else
{

View File

@@ -0,0 +1,26 @@
using Robust.Shared.Prototypes;
namespace Content.Shared.Traits;
/// <summary>
/// Traits category with general settings. Allows you to limit the number of taken traits in one category
/// </summary>
[Prototype]
public sealed partial class TraitCategoryPrototype : IPrototype
{
[ViewVariables]
[IdDataField]
public string ID { get; private set; } = default!;
/// <summary>
/// Name of the trait category displayed in the UI
/// </summary>
[DataField]
public LocId Name { get; private set; } = string.Empty;
/// <summary>
/// The maximum number of traits that can be taken in this category. If -1, you can take as many traits as you like.
/// </summary>
[DataField]
public int MaxTraitPoints = -1;
}

View File

@@ -1,17 +1,14 @@
using Content.Shared.Whitelist;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
// don't worry about it
namespace Content.Shared.Traits;
namespace Content.Shared.Traits
/// <summary>
/// Describes a trait.
/// </summary>
[Prototype]
public sealed partial class TraitPrototype : IPrototype
{
/// <summary>
/// Describes a trait.
/// </summary>
[Prototype("trait")]
public sealed partial class TraitPrototype : IPrototype
{
[ViewVariables]
[IdDataField]
public string ID { get; private set; } = default!;
@@ -19,37 +16,48 @@ namespace Content.Shared.Traits
/// <summary>
/// The name of this trait.
/// </summary>
[DataField("name")]
public string Name { get; private set; } = "";
[DataField]
public LocId Name { get; private set; } = string.Empty;
/// <summary>
/// The description of this trait.
/// </summary>
[DataField("description")]
public string? Description { get; private set; }
[DataField]
public LocId? Description { get; private set; }
/// <summary>
/// Don't apply this trait to entities this whitelist IS NOT valid for.
/// </summary>
[DataField("whitelist")]
[DataField]
public EntityWhitelist? Whitelist;
/// <summary>
/// Don't apply this trait to entities this whitelist IS valid for. (hence, a blacklist)
/// </summary>
[DataField("blacklist")]
[DataField]
public EntityWhitelist? Blacklist;
/// <summary>
/// The components that get added to the player, when they pick this trait.
/// </summary>
[DataField("components")]
[DataField]
public ComponentRegistry Components { get; private set; } = default!;
/// <summary>
/// Gear that is given to the player, when they pick this trait.
/// </summary>
[DataField("traitGear", required: false, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? TraitGear;
}
[DataField]
public EntProtoId? TraitGear;
/// <summary>
/// Trait Price. If negative number, points will be added.
/// </summary>
[DataField]
public int Cost = 0;
/// <summary>
/// Adds a trait to a category, allowing you to limit the selection of some traits to the settings of that category.
/// </summary>
[DataField]
public ProtoId<TraitCategoryPrototype>? Category;
}

View File

@@ -42,7 +42,7 @@ humanoid-profile-editor-department-jobs-label = {$departmentName} jobs
humanoid-profile-editor-antags-tab = Antags
humanoid-profile-editor-antag-preference-yes-button = Yes
humanoid-profile-editor-antag-preference-no-button = No
humanoid-profile-editor-traits-tab = Traits
humanoid-profile-editor-job-priority-high-button = High
humanoid-profile-editor-job-priority-medium-button = Medium
humanoid-profile-editor-job-priority-low-button = Low
@@ -50,3 +50,12 @@ humanoid-profile-editor-job-priority-never-button = Never
humanoid-profile-editor-naming-rules-warning = Warning: Offensive or LRP IC names and descriptions will lead to admin intervention on this server. Read our \[Rules\] for more.
humanoid-profile-editor-markings-tab = Markings
humanoid-profile-editor-flavortext-tab = Description
# Traits
humanoid-profile-editor-traits-tab = Traits
humanoid-profile-editor-no-traits = No traits available
humanoid-profile-editor-trait-count-hint = Points available: [{$current}/{$max}]
trait-category-disabilities = Disabilities
trait-category-speech = Speech traits

View File

@@ -12,7 +12,7 @@ trait-pacifist-desc = You cannot attack or hurt any living beings.
permanent-blindness-trait-examined = [color=lightblue]{CAPITALIZE(POSS-ADJ($target))} eyes are glassy and unfocused. It doesn't seem like {SUBJECT($target)} can see you well, if at all.[/color]
trait-lightweight-name = Lightweight Drunk
trait-lightweight-name = Lightweight drunk
trait-lightweight-desc = Alcohol has a stronger effect on you
trait-muted-name = Muted
@@ -24,19 +24,19 @@ trait-paracusia-desc = You hear sounds that aren't really there
trait-unrevivable-name = Unrevivable
trait-unrevivable-desc = You are unable to be revived by defibrillators.
trait-pirate-accent-name = Pirate Accent
trait-pirate-accent-name = Pirate accent
trait-pirate-accent-desc = You can't stop speaking like a pirate!
trait-accentless-name = Accentless
trait-accentless-desc = You don't have the accent that your species would usually have
trait-frontal-lisp-name = Frontal Lisp
trait-frontal-lisp-name = Frontal lisp
trait-frontal-lisp-desc = You thpeak with a lithp
trait-socialanxiety-name = Social Anxiety
trait-socialanxiety-name = Social anxiety
trait-socialanxiety-desc = You are anxious when you speak and stutter.
trait-southern-name = Southern Drawl
trait-southern-name = Southern drawl
trait-southern-desc = You have a different way of speakin'.
trait-snoring-name = Snoring
@@ -44,3 +44,9 @@ trait-snoring-desc = You will snore while sleeping.
trait-liar-name = Pathological liar
trait-liar-desc = You can hardly bring yourself to tell the truth. Sometimes you lie anyway.
trait-cowboy-name = Cowboy accent
trait-cowboy-desc = You speak with a distinct cowboy accent!
trait-italian-name = Italian accent
trait-italian-desc = Mamma mia! You seem to have lived in space italy!

View File

@@ -0,0 +1,8 @@
- type: traitCategory
id: Disabilities
name: trait-category-disabilities
- type: traitCategory
id: SpeechTraits
name: trait-category-speech
maxTraitPoints: 2

View File

@@ -3,6 +3,7 @@
name: trait-blindness-name
description: trait-blindness-desc
traitGear: WhiteCane
category: Disabilities
whitelist:
components:
- Blindable
@@ -14,6 +15,7 @@
name: trait-poor-vision-name
description: trait-poor-vision-desc
traitGear: ClothingEyesGlasses
category: Disabilities
whitelist:
components:
- Blindable
@@ -25,6 +27,7 @@
id: Narcolepsy
name: trait-narcolepsy-name
description: trait-narcolepsy-desc
category: Disabilities
components:
- type: Narcolepsy
timeBetweenIncidents: 300, 600
@@ -34,13 +37,43 @@
id: Pacifist
name: trait-pacifist-name
description: trait-pacifist-desc
category: Disabilities
components:
- type: Pacified
- type: trait
id: Unrevivable
name: trait-unrevivable-name
description: trait-unrevivable-desc
category: Disabilities
components:
- type: Unrevivable
- type: trait
id: Muted
name: trait-muted-name
description: trait-muted-desc
category: Disabilities
blacklist:
components:
- BorgChassis
components:
- type: Muted
- type: trait
id: LightweightDrunk
name: trait-lightweight-name
description: trait-lightweight-desc
category: Disabilities
components:
- type: LightweightDrunk
boozeStrengthMultiplier: 2
- type: trait
id: Paracusia
name: trait-paracusia-name
description: trait-paracusia-desc
category: Disabilities
components:
- type: Paracusia
minTimeBetweenIncidents: 0.1
@@ -49,33 +82,10 @@
sounds:
collection: Paracusia
- type: trait
id: Unrevivable
name: trait-unrevivable-name
description: trait-unrevivable-desc
components:
- type: Unrevivable
- type: trait
id: Muted
name: trait-muted-name
description: trait-muted-desc
blacklist:
components:
- BorgChassis
components:
- type: Muted
- type: trait
id: FrontalLisp
name: trait-frontal-lisp-name
description: trait-frontal-lisp-desc
components:
- type: FrontalLisp
- type: trait
id: Snoring
name: trait-snoring-name
description: trait-snoring-desc
category: Disabilities
components:
- type: Snoring

View File

@@ -1,18 +0,0 @@
- type: trait
id: LightweightDrunk
name: trait-lightweight-name
description: trait-lightweight-desc
components:
- type: LightweightDrunk
boozeStrengthMultiplier: 2
- type: trait
id: SocialAnxiety
name: trait-socialanxiety-name
description: trait-socialanxiety-desc
components:
- type: StutteringAccent
matchRandomProb: 0.1
fourRandomProb: 0
threeRandomProb: 0
cutRandomProb: 0

View File

@@ -1,33 +0,0 @@
- type: trait
id: PirateAccent
name: trait-pirate-accent-name
description: trait-pirate-accent-desc
components:
- type: PirateAccent
- type: trait
id: Accentless
name: trait-accentless-name
description: trait-accentless-desc
components:
- type: Accentless
removes:
- type: LizardAccent
- type: MothAccent
- type: ReplacementAccent
accent: dwarf
- type: trait
id: Southern
name: trait-southern-name
description: trait-southern-desc
components:
- type: SouthernAccent
- type: trait
id: Liar
name: trait-liar-name
description: trait-liar-desc
components:
- type: ReplacementAccent
accent: liar

View File

@@ -0,0 +1,88 @@
# Free
- type: trait
id: Accentless
name: trait-accentless-name
description: trait-accentless-desc
category: SpeechTraits
components:
- type: Accentless
removes:
- type: LizardAccent
- type: MothAccent
- type: ReplacementAccent
accent: dwarf
# 1 Cost
- type: trait
id: SouthernAccent
name: trait-southern-name
description: trait-southern-desc
category: SpeechTraits
cost: 1
components:
- type: SouthernAccent
- type: trait
id: PirateAccent
name: trait-pirate-accent-name
description: trait-pirate-accent-desc
category: SpeechTraits
cost: 1
components:
- type: PirateAccent
- type: trait
id: CowboyAccent
name: trait-cowboy-name
description: trait-cowboy-desc
category: SpeechTraits
cost: 1
components:
- type: ReplacementAccent
accent: cowboy
- type: trait
id: ItalianAccent
name: trait-italian-name
description: trait-italian-desc
category: SpeechTraits
cost: 1
components:
- type: ReplacementAccent
accent: italian
- type: trait
id: Liar
name: trait-liar-name
description: trait-liar-desc
category: SpeechTraits
cost: 1
components:
- type: ReplacementAccent
accent: liar
# 2 Cost
- type: trait
id: SocialAnxiety
name: trait-socialanxiety-name
description: trait-socialanxiety-desc
category: SpeechTraits
cost: 2
components:
- type: StutteringAccent
matchRandomProb: 0.1
fourRandomProb: 0
threeRandomProb: 0
cutRandomProb: 0
- type: trait
id: FrontalLisp
name: trait-frontal-lisp-name
description: trait-frontal-lisp-desc
category: SpeechTraits
cost: 2
components:
- type: FrontalLisp