Implement traits system (#10693)
This commit is contained in:
@@ -2,8 +2,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:magicmirror="clr-namespace:Content.Client.CharacterAppearance"
|
||||
xmlns:prefUi="clr-namespace:Content.Client.Preferences.UI"
|
||||
xmlns:markings="clr-namespace:Content.Client.Markings"
|
||||
xmlns:flavorText="clr-namespace:Content.Client.FlavorText">
|
||||
xmlns:markings="clr-namespace:Content.Client.Markings">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<!-- Left side -->
|
||||
<BoxContainer Orientation="Vertical" Margin="10 10 10 10">
|
||||
@@ -47,6 +46,7 @@
|
||||
</prefUi:HighlightedContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<Control MinHeight="10" />
|
||||
<!-- tabContainer -->
|
||||
<TabContainer Name="CTabContainer" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
@@ -133,13 +133,19 @@
|
||||
<BoxContainer Name="CJobList" Orientation="Vertical" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Vertical" Margin="10">
|
||||
<!-- Antags -->
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<BoxContainer Name="CAntagList" Orientation="Vertical" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Name="CMarkingsTab" Orientation="Vertical">
|
||||
<BoxContainer Orientation="Vertical" Margin="10">
|
||||
<!-- Traits -->
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<BoxContainer Name="CTraitsList" Orientation="Vertical" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Name="CMarkingsTab" Orientation="Vertical" Margin="10">
|
||||
<!-- Markings -->
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<markings:MarkingPicker Name="CMarkings" />
|
||||
|
||||
@@ -15,6 +15,7 @@ using Content.Shared.Markings;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Species;
|
||||
using Content.Shared.Traits;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
@@ -80,12 +81,14 @@ namespace Content.Client.Preferences.UI
|
||||
private TabContainer _tabContainer => CTabContainer;
|
||||
private BoxContainer _jobList => CJobList;
|
||||
private BoxContainer _antagList => CAntagList;
|
||||
private BoxContainer _traitsList => CTraitsList;
|
||||
private readonly List<JobPrioritySelector> _jobPriorities;
|
||||
private OptionButton _preferenceUnavailableButton => CPreferenceUnavailableButton;
|
||||
private readonly Dictionary<string, BoxContainer> _jobCategories;
|
||||
// Mildly hacky, as I don't trust prototype order to stay consistent and don't want the UI to break should a new one get added mid-edit. --moony
|
||||
private readonly List<SpeciesPrototype> _speciesList;
|
||||
private readonly List<AntagPreferenceSelector> _antagPreferences;
|
||||
private readonly List<TraitPreferenceSelector> _traitPreferences;
|
||||
|
||||
private Control _previewSpriteControl => CSpriteViewFront;
|
||||
private Control _previewSpriteSideControl => CSpriteViewSide;
|
||||
@@ -461,14 +464,46 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
#endregion Antags
|
||||
|
||||
#region Traits
|
||||
|
||||
var traits = prototypeManager.EnumeratePrototypes<TraitPrototype>().OrderBy(t => t.Name).ToList();
|
||||
_traitPreferences = new List<TraitPreferenceSelector>();
|
||||
_tabContainer.SetTabTitle(3, Loc.GetString("humanoid-profile-editor-traits-tab"));
|
||||
|
||||
if (traits.Count > 0)
|
||||
{
|
||||
foreach (var trait in traits)
|
||||
{
|
||||
var selector = new TraitPreferenceSelector(trait);
|
||||
_traitsList.AddChild(selector);
|
||||
_traitPreferences.Add(selector);
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithTraitPreference(trait.ID, preference);
|
||||
IsDirty = true;
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_traitsList.AddChild(new Label
|
||||
{
|
||||
Text = "No traits available :(",
|
||||
FontColorOverride = Color.Gray,
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Save
|
||||
|
||||
_saveButton.OnPressed += args => { Save(); };
|
||||
_saveButton.OnPressed += _ => { Save(); };
|
||||
|
||||
#endregion Save
|
||||
|
||||
#region Markings
|
||||
_tabContainer.SetTabTitle(3, Loc.GetString("humanoid-profile-editor-markings-tab"));
|
||||
_tabContainer.SetTabTitle(4, Loc.GetString("humanoid-profile-editor-markings-tab"));
|
||||
|
||||
CMarkings.OnMarkingAdded += OnMarkingChange;
|
||||
CMarkings.OnMarkingRemoved += OnMarkingChange;
|
||||
@@ -483,7 +518,7 @@ namespace Content.Client.Preferences.UI
|
||||
{
|
||||
var flavorText = new FlavorText.FlavorText();
|
||||
_tabContainer.AddChild(flavorText);
|
||||
_tabContainer.SetTabTitle(_tabContainer.ChildCount-1, Loc.GetString("humanoid-profile-editor-flavortext-tab"));
|
||||
_tabContainer.SetTabTitle(_tabContainer.ChildCount - 1, Loc.GetString("humanoid-profile-editor-flavortext-tab"));
|
||||
_flavorTextEdit = flavorText.CFlavorTextInput;
|
||||
|
||||
flavorText.OnFlavorTextChanged += OnFlavorTextChange;
|
||||
@@ -960,6 +995,7 @@ namespace Content.Client.Preferences.UI
|
||||
UpdateSaveButton();
|
||||
UpdateJobPriorities();
|
||||
UpdateAntagPreferences();
|
||||
UpdateTraitPreferences();
|
||||
UpdateMarkings();
|
||||
|
||||
NeedsDummyRebuild = true;
|
||||
@@ -1103,6 +1139,17 @@ namespace Content.Client.Preferences.UI
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateTraitPreferences()
|
||||
{
|
||||
foreach (var preferenceSelector in _traitPreferences)
|
||||
{
|
||||
var traitId = preferenceSelector.Trait.ID;
|
||||
var preference = Profile?.TraitPreferences.Contains(traitId) ?? false;
|
||||
|
||||
preferenceSelector.Preference = preference;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class AntagPreferenceSelector : Control
|
||||
{
|
||||
public AntagPrototype Antag { get; }
|
||||
@@ -1138,5 +1185,38 @@ namespace Content.Client.Preferences.UI
|
||||
PreferenceChanged?.Invoke(Preference);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TraitPreferenceSelector : Control
|
||||
{
|
||||
public TraitPrototype Trait { get; }
|
||||
private readonly CheckBox _checkBox;
|
||||
|
||||
public bool Preference
|
||||
{
|
||||
get => _checkBox.Pressed;
|
||||
set => _checkBox.Pressed = value;
|
||||
}
|
||||
|
||||
public event Action<bool>? PreferenceChanged;
|
||||
|
||||
public TraitPreferenceSelector(TraitPrototype trait)
|
||||
{
|
||||
Trait = trait;
|
||||
|
||||
_checkBox = new CheckBox {Text = $"{trait.Name}"};
|
||||
_checkBox.OnToggled += OnCheckBoxToggled;
|
||||
|
||||
AddChild(new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
Children = { _checkBox },
|
||||
});
|
||||
}
|
||||
|
||||
private void OnCheckBoxToggled(BaseButton.ButtonToggledEventArgs args)
|
||||
{
|
||||
PreferenceChanged?.Invoke(Preference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1333
Content.Server.Database/Migrations/Postgres/20220816163319_Traits.Designer.cs
generated
Normal file
1333
Content.Server.Database/Migrations/Postgres/20220816163319_Traits.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,44 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
public partial class Traits : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "trait",
|
||||
columns: table => new
|
||||
{
|
||||
trait_id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
profile_id = table.Column<int>(type: "integer", nullable: false),
|
||||
trait_name = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_trait", x => x.trait_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_trait_profile_profile_id",
|
||||
column: x => x.profile_id,
|
||||
principalTable: "profile",
|
||||
principalColumn: "profile_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_trait_profile_id",
|
||||
table: "trait",
|
||||
column: "profile_id");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "trait");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -914,6 +914,33 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.ToTable("server_unban", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Trait", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("trait_id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("ProfileId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("profile_id");
|
||||
|
||||
b.Property<string>("TraitName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("trait_name");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_trait");
|
||||
|
||||
b.HasIndex("ProfileId")
|
||||
.HasDatabaseName("IX_trait_profile_id");
|
||||
|
||||
b.ToTable("trait", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1197,6 +1224,18 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("Ban");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Trait", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Profile", "Profile")
|
||||
.WithMany("Traits")
|
||||
.HasForeignKey("ProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_trait_profile_profile_id");
|
||||
|
||||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("PlayerRound", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", null)
|
||||
@@ -1261,6 +1300,8 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("Antags");
|
||||
|
||||
b.Navigation("Jobs");
|
||||
|
||||
b.Navigation("Traits");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
|
||||
1267
Content.Server.Database/Migrations/Sqlite/20220816163313_Traits.Designer.cs
generated
Normal file
1267
Content.Server.Database/Migrations/Sqlite/20220816163313_Traits.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
public partial class Traits : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "trait",
|
||||
columns: table => new
|
||||
{
|
||||
trait_id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
profile_id = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
trait_name = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_trait", x => x.trait_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_trait_profile_profile_id",
|
||||
column: x => x.profile_id,
|
||||
principalTable: "profile",
|
||||
principalColumn: "profile_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_trait_profile_id",
|
||||
table: "trait",
|
||||
column: "profile_id");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "trait");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -852,6 +852,31 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.ToTable("server_unban", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Trait", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("trait_id");
|
||||
|
||||
b.Property<int>("ProfileId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("profile_id");
|
||||
|
||||
b.Property<string>("TraitName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("trait_name");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_trait");
|
||||
|
||||
b.HasIndex("ProfileId")
|
||||
.HasDatabaseName("IX_trait_profile_id");
|
||||
|
||||
b.ToTable("trait", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.UploadedResourceLog", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1133,6 +1158,18 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("Ban");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Trait", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Profile", "Profile")
|
||||
.WithMany("Traits")
|
||||
.HasForeignKey("ProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_trait_profile_profile_id");
|
||||
|
||||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("PlayerRound", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Player", null)
|
||||
@@ -1197,6 +1234,8 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("Antags");
|
||||
|
||||
b.Navigation("Jobs");
|
||||
|
||||
b.Navigation("Traits");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
|
||||
@@ -51,6 +51,10 @@ namespace Content.Server.Database
|
||||
.HasIndex(p => new {HumanoidProfileId = p.ProfileId, p.AntagName})
|
||||
.IsUnique();
|
||||
|
||||
modelBuilder.Entity<Trait>()
|
||||
.HasIndex(p => new {HumanoidProfileId = p.ProfileId, p.TraitName})
|
||||
.IsUnique();
|
||||
|
||||
modelBuilder.Entity<Job>()
|
||||
.HasIndex(j => j.ProfileId);
|
||||
|
||||
@@ -218,6 +222,7 @@ namespace Content.Server.Database
|
||||
public string Backpack { get; set; } = null!;
|
||||
public List<Job> Jobs { get; } = new();
|
||||
public List<Antag> Antags { get; } = new();
|
||||
public List<Trait> Traits { get; } = new();
|
||||
|
||||
[Column("pref_unavailable")] public DbPreferenceUnavailableMode PreferenceUnavailable { get; set; }
|
||||
|
||||
@@ -253,6 +258,15 @@ namespace Content.Server.Database
|
||||
public string AntagName { get; set; } = null!;
|
||||
}
|
||||
|
||||
public class Trait
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public Profile Profile { get; set; } = null!;
|
||||
public int ProfileId { get; set; }
|
||||
|
||||
public string TraitName { get; set; } = null!;
|
||||
}
|
||||
|
||||
public enum DbPreferenceUnavailableMode
|
||||
{
|
||||
// These enum values HAVE to match the ones in PreferenceUnavailableMode in Shared.
|
||||
|
||||
@@ -27,6 +27,7 @@ namespace Content.Server.Database
|
||||
.Preference
|
||||
.Include(p => p.Profiles).ThenInclude(h => h.Jobs)
|
||||
.Include(p => p.Profiles).ThenInclude(h => h.Antags)
|
||||
.Include(p => p.Profiles).ThenInclude(h => h.Traits)
|
||||
.AsSingleQuery()
|
||||
.SingleOrDefaultAsync(p => p.UserId == userId.UserId);
|
||||
|
||||
@@ -156,6 +157,7 @@ namespace Content.Server.Database
|
||||
{
|
||||
var jobs = profile.Jobs.ToDictionary(j => j.JobName, j => (JobPriority) j.Priority);
|
||||
var antags = profile.Antags.Select(a => a.AntagName);
|
||||
var traits = profile.Traits.Select(t => t.TraitName);
|
||||
|
||||
var sex = Sex.Male;
|
||||
if (Enum.TryParse<Sex>(profile.Sex, true, out var sexVal))
|
||||
@@ -211,7 +213,8 @@ namespace Content.Server.Database
|
||||
backpack,
|
||||
jobs,
|
||||
(PreferenceUnavailableMode) profile.PreferenceUnavailable,
|
||||
antags.ToList()
|
||||
antags.ToList(),
|
||||
traits.ToList()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -254,6 +257,10 @@ namespace Content.Server.Database
|
||||
humanoid.AntagPreferences
|
||||
.Select(a => new Antag {AntagName = a})
|
||||
);
|
||||
entity.Traits.AddRange(
|
||||
humanoid.TraitPreferences
|
||||
.Select(t => new Trait {TraitName = t})
|
||||
);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -160,9 +160,6 @@ namespace Content.Server.Nutrition.Components
|
||||
{
|
||||
_currentHunger -= frametime * ActualDecayRate;
|
||||
UpdateCurrentThreshold();
|
||||
|
||||
if (_currentHungerThreshold != HungerThreshold.Dead)
|
||||
return;
|
||||
}
|
||||
|
||||
private void UpdateCurrentThreshold()
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
using Content.Server.Nutrition.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Random;
|
||||
using Content.Shared.MobState.Components;
|
||||
using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Movement.Systems;
|
||||
|
||||
namespace Content.Server.Nutrition.EntitySystems
|
||||
@@ -43,6 +39,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
|
||||
private void OnRefreshMovespeed(EntityUid uid, ThirstComponent component, RefreshMovementSpeedModifiersEvent args)
|
||||
{
|
||||
// TODO: This should really be taken care of somewhere else
|
||||
if (_jetpack.IsUserFlying(component.Owner))
|
||||
return;
|
||||
|
||||
|
||||
40
Content.Server/Traits/TraitSystem.cs
Normal file
40
Content.Server/Traits/TraitSystem.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.Traits;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
|
||||
namespace Content.Server.Traits;
|
||||
|
||||
public sealed class TraitSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly ISerializationManager _serializationManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PlayerSpawnCompleteEvent>(OnPlayerSpawnComplete);
|
||||
}
|
||||
|
||||
// When the player is spawned in, add all trait components selected during character creation
|
||||
private void OnPlayerSpawnComplete(PlayerSpawnCompleteEvent args)
|
||||
{
|
||||
foreach (var traitId in args.Profile.TraitPreferences)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex<TraitPrototype>(traitId, out var traitPrototype))
|
||||
{
|
||||
Logger.Warning($"No trait found with ID {traitId}!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Add all components required by the prototype
|
||||
foreach (var entry in traitPrototype.Components.Values)
|
||||
{
|
||||
var comp = (Component) _serializationManager.Copy(entry.Component);
|
||||
comp.Owner = args.Mob;
|
||||
EntityManager.AddComponent(args.Mob, comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using Content.Shared.GameTicking;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Species;
|
||||
using Content.Shared.Traits;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -28,6 +29,7 @@ namespace Content.Shared.Preferences
|
||||
|
||||
private readonly Dictionary<string, JobPriority> _jobPriorities;
|
||||
private readonly List<string> _antagPreferences;
|
||||
private readonly List<string> _traitPreferences;
|
||||
|
||||
private HumanoidCharacterProfile(
|
||||
string name,
|
||||
@@ -41,7 +43,8 @@ namespace Content.Shared.Preferences
|
||||
BackpackPreference backpack,
|
||||
Dictionary<string, JobPriority> jobPriorities,
|
||||
PreferenceUnavailableMode preferenceUnavailable,
|
||||
List<string> antagPreferences)
|
||||
List<string> antagPreferences,
|
||||
List<string> traitPreferences)
|
||||
{
|
||||
Name = name;
|
||||
FlavorText = flavortext;
|
||||
@@ -55,21 +58,23 @@ namespace Content.Shared.Preferences
|
||||
_jobPriorities = jobPriorities;
|
||||
PreferenceUnavailable = preferenceUnavailable;
|
||||
_antagPreferences = antagPreferences;
|
||||
_traitPreferences = traitPreferences;
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor but with overridable references (to prevent useless copies)</summary>
|
||||
private HumanoidCharacterProfile(
|
||||
HumanoidCharacterProfile other,
|
||||
Dictionary<string, JobPriority> jobPriorities,
|
||||
List<string> antagPreferences)
|
||||
List<string> antagPreferences,
|
||||
List<string> traitPreferences)
|
||||
: this(other.Name, other.FlavorText, other.Species, other.Age, other.Sex, other.Gender, other.Appearance, other.Clothing, other.Backpack,
|
||||
jobPriorities, other.PreferenceUnavailable, antagPreferences)
|
||||
jobPriorities, other.PreferenceUnavailable, antagPreferences, traitPreferences)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor</summary>
|
||||
private HumanoidCharacterProfile(HumanoidCharacterProfile other)
|
||||
: this(other, new Dictionary<string, JobPriority>(other.JobPriorities), new List<string>(other.AntagPreferences))
|
||||
: this(other, new Dictionary<string, JobPriority>(other.JobPriorities), new List<string>(other.AntagPreferences), new List<string>(other.TraitPreferences))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -85,9 +90,10 @@ namespace Content.Shared.Preferences
|
||||
BackpackPreference backpack,
|
||||
IReadOnlyDictionary<string, JobPriority> jobPriorities,
|
||||
PreferenceUnavailableMode preferenceUnavailable,
|
||||
IReadOnlyList<string> antagPreferences)
|
||||
IReadOnlyList<string> antagPreferences,
|
||||
IReadOnlyList<string> traitPreferences)
|
||||
: this(name, flavortext, species, age, sex, gender, appearance, clothing, backpack, new Dictionary<string, JobPriority>(jobPriorities),
|
||||
preferenceUnavailable, new List<string>(antagPreferences))
|
||||
preferenceUnavailable, new List<string>(antagPreferences), new List<string>(traitPreferences))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -108,6 +114,7 @@ namespace Content.Shared.Preferences
|
||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High}
|
||||
},
|
||||
PreferenceUnavailableMode.SpawnAsOverflow,
|
||||
new List<string>(),
|
||||
new List<string>());
|
||||
}
|
||||
|
||||
@@ -127,8 +134,8 @@ namespace Content.Shared.Preferences
|
||||
return new HumanoidCharacterProfile(name, "", species, age, sex, gender, HumanoidCharacterAppearance.Random(sex), ClothingPreference.Jumpsuit, BackpackPreference.Backpack,
|
||||
new Dictionary<string, JobPriority>
|
||||
{
|
||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High}
|
||||
}, PreferenceUnavailableMode.StayInLobby, new List<string>());
|
||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High},
|
||||
}, PreferenceUnavailableMode.StayInLobby, new List<string>(), new List<string>());
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
@@ -143,6 +150,7 @@ namespace Content.Shared.Preferences
|
||||
public BackpackPreference Backpack { get; private set; }
|
||||
public IReadOnlyDictionary<string, JobPriority> JobPriorities => _jobPriorities;
|
||||
public IReadOnlyList<string> AntagPreferences => _antagPreferences;
|
||||
public IReadOnlyList<string> TraitPreferences => _traitPreferences;
|
||||
public PreferenceUnavailableMode PreferenceUnavailable { get; private set; }
|
||||
|
||||
public HumanoidCharacterProfile WithName(string name)
|
||||
@@ -191,7 +199,7 @@ namespace Content.Shared.Preferences
|
||||
}
|
||||
public HumanoidCharacterProfile WithJobPriorities(IEnumerable<KeyValuePair<string, JobPriority>> jobPriorities)
|
||||
{
|
||||
return new(this, new Dictionary<string, JobPriority>(jobPriorities), _antagPreferences);
|
||||
return new(this, new Dictionary<string, JobPriority>(jobPriorities), _antagPreferences, _traitPreferences);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priority)
|
||||
@@ -205,7 +213,7 @@ namespace Content.Shared.Preferences
|
||||
{
|
||||
dictionary[jobId] = priority;
|
||||
}
|
||||
return new(this, dictionary, _antagPreferences);
|
||||
return new(this, dictionary, _antagPreferences, _traitPreferences);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableMode mode)
|
||||
@@ -215,7 +223,7 @@ namespace Content.Shared.Preferences
|
||||
|
||||
public HumanoidCharacterProfile WithAntagPreferences(IEnumerable<string> antagPreferences)
|
||||
{
|
||||
return new(this, _jobPriorities, new List<string>(antagPreferences));
|
||||
return new(this, _jobPriorities, new List<string>(antagPreferences), _traitPreferences);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref)
|
||||
@@ -235,7 +243,29 @@ namespace Content.Shared.Preferences
|
||||
list.Remove(antagId);
|
||||
}
|
||||
}
|
||||
return new(this, _jobPriorities, list);
|
||||
return new(this, _jobPriorities, list, _traitPreferences);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref)
|
||||
{
|
||||
var list = new List<string>(_traitPreferences);
|
||||
|
||||
// TODO: Maybe just refactor this to HashSet? Same with _antagPreferences
|
||||
if(pref)
|
||||
{
|
||||
if(!list.Contains(traitId))
|
||||
{
|
||||
list.Add(traitId);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(list.Contains(traitId))
|
||||
{
|
||||
list.Remove(traitId);
|
||||
}
|
||||
}
|
||||
return new(this, _jobPriorities, _antagPreferences, list);
|
||||
}
|
||||
|
||||
public string Summary =>
|
||||
@@ -258,6 +288,7 @@ namespace Content.Shared.Preferences
|
||||
if (Backpack != other.Backpack) return false;
|
||||
if (!_jobPriorities.SequenceEqual(other._jobPriorities)) return false;
|
||||
if (!_antagPreferences.SequenceEqual(other._antagPreferences)) return false;
|
||||
if (!_traitPreferences.SequenceEqual(other._traitPreferences)) return false;
|
||||
return Appearance.MemberwiseEquals(other.Appearance);
|
||||
}
|
||||
|
||||
@@ -357,6 +388,10 @@ namespace Content.Shared.Preferences
|
||||
.Where(prototypeManager.HasIndex<AntagPrototype>)
|
||||
.ToList();
|
||||
|
||||
var traits = TraitPreferences
|
||||
.Where(prototypeManager.HasIndex<TraitPrototype>)
|
||||
.ToList();
|
||||
|
||||
Name = name;
|
||||
FlavorText = flavortext;
|
||||
Age = age;
|
||||
@@ -377,6 +412,9 @@ namespace Content.Shared.Preferences
|
||||
|
||||
_antagPreferences.Clear();
|
||||
_antagPreferences.AddRange(antags);
|
||||
|
||||
_traitPreferences.Clear();
|
||||
_traitPreferences.AddRange(traits);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
@@ -399,7 +437,8 @@ namespace Content.Shared.Preferences
|
||||
),
|
||||
PreferenceUnavailable,
|
||||
_jobPriorities,
|
||||
_antagPreferences
|
||||
_antagPreferences,
|
||||
_traitPreferences
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
28
Content.Shared/Traits/TraitPrototype.cs
Normal file
28
Content.Shared/Traits/TraitPrototype.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Shared.Prototypes.EntityPrototype; // don't worry about it
|
||||
|
||||
namespace Content.Shared.Traits
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes a trait.
|
||||
/// </summary>
|
||||
[Prototype("trait")]
|
||||
public sealed class TraitPrototype : IPrototype
|
||||
{
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The name of this trait.
|
||||
/// </summary>
|
||||
[DataField("name")]
|
||||
public string Name { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The components that get added to the player, when they pick this trait.
|
||||
/// </summary>
|
||||
[DataField("components")]
|
||||
public ComponentRegistry Components { get; } = default!;
|
||||
}
|
||||
}
|
||||
@@ -64,7 +64,8 @@ namespace Content.Tests.Server.Preferences
|
||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High}
|
||||
},
|
||||
PreferenceUnavailableMode.StayInLobby,
|
||||
new List<string> ()
|
||||
new List<string> (),
|
||||
new List<string>()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ humanoid-profile-editor-preference-duffelbag = Duffelbag
|
||||
humanoid-profile-editor-jobs-amount-in-department-tooltip = Jobs in the {$departmentName} department
|
||||
humanoid-profile-editor-department-jobs-label = {$departmentName} jobs
|
||||
humanoid-profile-editor-antags-tab = Antags
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user