Upstream species (#6066)

* Step 1 of porting; grabbed most of the files via patches.

* Add species field to the DB

* Appearance patches for slimes.

* Fix the db test.

* Add slime's biocompat.

* slimby

* Fixes, allow specifying if a species is playable or not.

* Update Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs

Co-authored-by: Javier Guardia Fernández <DrSmugleaf@users.noreply.github.com>

* Update Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs

Co-authored-by: Javier Guardia Fernández <DrSmugleaf@users.noreply.github.com>

* Update Content.Client/Preferences/UI/HumanoidProfileEditor.xaml.cs

Co-authored-by: Javier Guardia Fernández <DrSmugleaf@users.noreply.github.com>

* Address reviews.

* Address reviews.

* make an if-case.

* Fix a goof where species wouldn't get shown in the editor correctly (it'd always default to human)

Co-authored-by: Javier Guardia Fernández <DrSmugleaf@users.noreply.github.com>
This commit is contained in:
Moony
2022-01-08 19:53:14 -06:00
committed by GitHub
parent a3e73889b2
commit ca984036d6
31 changed files with 2436 additions and 108 deletions

View File

@@ -10,6 +10,7 @@ using Content.Shared.CharacterAppearance.Systems;
using Content.Shared.GameTicking;
using Content.Shared.Preferences;
using Content.Shared.Roles;
using Content.Shared.Species;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -28,6 +29,7 @@ using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BoxContainer;
using Range = Robust.Client.UserInterface.Controls.Range;
namespace Content.Client.Preferences.UI
{
@@ -55,6 +57,7 @@ namespace Content.Client.Preferences.UI
private Button _randomizeEverythingButton => CRandomizeEverything;
private RichTextLabel _warningLabel => CWarningLabel;
private readonly IClientPreferencesManager _preferencesManager;
private readonly IEntityManager _entMan;
private Button _saveButton => CSaveButton;
private Button _sexFemaleButton => CSexFemale;
private Button _sexMaleButton => CSexMale;
@@ -72,14 +75,15 @@ namespace Content.Client.Preferences.UI
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 EntityUid _previewDummy;
private EntityUid _previewDummy;
private Control _previewSpriteControl => CSpriteViewFront;
private Control _previewSpriteSideControl => CSpriteViewSide;
private readonly SpriteView _previewSprite;
private readonly SpriteView _previewSpriteSide;
private SpriteView? _previewSprite;
private SpriteView? _previewSpriteSide;
private bool _isDirty;
private bool _needUpdatePreview;
@@ -94,7 +98,7 @@ namespace Content.Client.Preferences.UI
RobustXamlLoader.Load(this);
_random = IoCManager.Resolve<IRobustRandom>();
_prototypeManager = prototypeManager;
_entMan = entityManager;
_preferencesManager = preferencesManager;
#region Left
@@ -171,6 +175,23 @@ namespace Content.Client.Preferences.UI
#endregion Gender
#region Species
_speciesList = prototypeManager.EnumeratePrototypes<SpeciesPrototype>().ToList();
for (var i = 0; i < _speciesList.Count; i++)
{
CSpeciesButton.AddItem(_speciesList[i].Name, i);
}
CSpeciesButton.OnItemSelected += args =>
{
CSpeciesButton.SelectId(args.Id);
SetSpecies(_speciesList[args.Id].ID);
OnSkinColorOnValueChanged(CSkin);
};
#endregion Species
#region Skin
// 0 - 100, 0 being gold/yellowish and 100 being dark
@@ -181,33 +202,7 @@ namespace Content.Client.Preferences.UI
// 0 is 45 - 20 - 100
// 20 is 25 - 20 - 100
// 100 is 25 - 100 - 20
_skinColor.OnValueChanged += range =>
{
if (Profile is null)
return;
int rangeOffset = (int) range.Value - 20;
float hue = 25;
float sat = 20;
float val = 100;
if (rangeOffset < 0)
{
hue += Math.Abs(rangeOffset);
}
else if (rangeOffset > 0)
{
sat += rangeOffset;
val -= rangeOffset;
}
var color = Color.FromHsv(new Vector4(hue / 360, sat / 100, val / 100, 1.0f));
Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithSkinColor(color));
IsDirty = true;
};
_skinColor.OnValueChanged += OnSkinColorOnValueChanged;
#endregion
@@ -434,12 +429,76 @@ namespace Content.Client.Preferences.UI
#endregion Left
#region Right
if (preferencesManager.ServerDataLoaded)
{
LoadServerData();
}
#region Preview
preferencesManager.OnServerDataLoaded += LoadServerData;
_previewDummy = entityManager.SpawnEntity("MobHumanDummy", MapCoordinates.Nullspace);
var sprite = IoCManager.Resolve<IEntityManager>().GetComponent<SpriteComponent>(_previewDummy);
IsDirty = false;
}
private void OnSkinColorOnValueChanged(Range range)
{
if (Profile is null) return;
var skin = _prototypeManager.Index<SpeciesPrototype>(Profile.Species).SkinColoration;
switch (skin)
{
case SpeciesSkinColor.HumanToned:
{
var rangeOffset = (int) range.Value - 20;
float hue = 25;
float sat = 20;
float val = 100;
if (rangeOffset <= 0)
{
hue += Math.Abs(rangeOffset);
}
else
{
sat += rangeOffset;
val -= rangeOffset;
}
var color = Color.FromHsv(new Vector4(hue / 360, sat / 100, val / 100, 1.0f));
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
break;
}
case SpeciesSkinColor.Hues:
{
var color = Color.FromHsv(new Vector4(range.Value / 100.0f, 1.0f, 1.0f, 1.0f));
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
break;
}
}
IsDirty = true;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_entMan.DeleteEntity(_previewDummy);
_preferencesManager.OnServerDataLoaded -= LoadServerData;
}
private void RebuildSpriteView()
{
var dollProto = _prototypeManager.Index<SpeciesPrototype>(Profile?.Species ?? SpeciesManager.DefaultSpecies).DollPrototype;
_previewDummy = _entMan.SpawnEntity(dollProto, MapCoordinates.Nullspace);
var sprite = _entMan.GetComponent<SpriteComponent>(_previewDummy);
_previewSpriteControl.DisposeAllChildren();
// Front
_previewSprite = new SpriteView
@@ -452,6 +511,8 @@ namespace Content.Client.Preferences.UI
};
_previewSpriteControl.AddChild(_previewSprite);
_previewSpriteSideControl.DisposeAllChildren();
// Side
_previewSpriteSide = new SpriteView
{
@@ -462,29 +523,6 @@ namespace Content.Client.Preferences.UI
SizeFlagsStretchRatio = 1
};
_previewSpriteSideControl.AddChild(_previewSpriteSide);
#endregion Right
#endregion
if (preferencesManager.ServerDataLoaded)
{
LoadServerData();
}
preferencesManager.OnServerDataLoaded += LoadServerData;
IsDirty = false;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
IoCManager.Resolve<IEntityManager>().DeleteEntity((EntityUid) _previewDummy);
_preferencesManager.OnServerDataLoaded -= LoadServerData;
}
private void LoadServerData()
@@ -512,6 +550,13 @@ namespace Content.Client.Preferences.UI
IsDirty = true;
}
private void SetSpecies(string newSpecies)
{
Profile = Profile?.WithSpecies(newSpecies);
OnSkinColorOnValueChanged(CSkin); // Species may have special color prefs, make sure to update it.
IsDirty = true;
}
private void SetName(string newName)
{
Profile = Profile?.WithName(newName);
@@ -576,20 +621,45 @@ namespace Content.Client.Preferences.UI
if (Profile == null)
return;
var skin = _prototypeManager.Index<SpeciesPrototype>(Profile.Species).SkinColoration;
var color = Color.ToHsv(Profile.Appearance.SkinColor);
// check for hue/value first, if hue is lower than this percentage
// and value is 1.0
// then it'll be hue
if (Math.Clamp(color.X, 25f / 360f, 1) > 25f / 360f
&& color.Z == 1.0)
switch (skin)
{
_skinColor.Value = Math.Abs(45 - (color.X * 360));
case SpeciesSkinColor.HumanToned:
{
// check for hue/value first, if hue is lower than this percentage
// and value is 1.0
// then it'll be hue
if (Math.Clamp(color.X, 25f / 360f, 1) > 25f / 360f
&& color.Z == 1.0)
{
_skinColor.Value = Math.Abs(45 - (color.X * 360));
}
// otherwise it'll directly be the saturation
else
{
_skinColor.Value = color.Y * 100;
}
break;
}
case SpeciesSkinColor.Hues:
{
_skinColor.Value = color.X * 100;
break;
}
}
// otherwise it'll directly be the saturation
else
}
private void UpdateSpecies()
{
if (Profile == null)
{
_skinColor.Value = color.Y * 100;
return;
}
CSpeciesButton.Select(_speciesList.FindIndex(x => x.ID == Profile.Species));
}
private void UpdateGenderControls()
@@ -660,6 +730,7 @@ namespace Content.Client.Preferences.UI
{
if (Profile is null)
return;
RebuildSpriteView();
EntitySystem.Get<SharedHumanoidAppearanceSystem>().UpdateFromProfile(_previewDummy, Profile);
LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy, Profile);
@@ -672,6 +743,7 @@ namespace Content.Client.Preferences.UI
UpdateSexControls();
UpdateGenderControls();
UpdateSkinColor();
UpdateSpecies();
UpdateClothingControls();
UpdateBackpackControls();
UpdateAgeEdit();