| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642 |
- using System.IO;
- using System.Linq;
- using System.Numerics;
- using Content.Client.Humanoid;
- using Content.Client.Lobby.UI.Loadouts;
- using Content.Client.Lobby.UI.Roles;
- using Content.Client.Message;
- using Content.Client.Players.PlayTimeTracking;
- using Content.Client.Sprite;
- using Content.Client.Stylesheets;
- using Content.Client.UserInterface.Systems.Guidebook;
- using Content.Shared.CCVar;
- using Content.Shared.Clothing;
- using Content.Shared.GameTicking;
- using Content.Shared.Guidebook;
- using Content.Shared.Humanoid;
- using Content.Shared.Humanoid.Markings;
- using Content.Shared.Humanoid.Prototypes;
- using Content.Shared.Preferences;
- using Content.Shared.Preferences.Loadouts;
- using Content.Shared.Roles;
- using Content.Shared.Traits;
- using Robust.Client.AutoGenerated;
- using Robust.Client.Graphics;
- using Robust.Client.Player;
- using Robust.Client.UserInterface;
- using Robust.Client.UserInterface.Controls;
- using Robust.Client.UserInterface.XAML;
- using Robust.Client.Utility;
- using Robust.Shared.Configuration;
- using Robust.Shared.ContentPack;
- using Robust.Shared.Enums;
- using Robust.Shared.Prototypes;
- using Robust.Shared.Utility;
- using Direction = Robust.Shared.Maths.Direction;
- namespace Content.Client.Lobby.UI
- {
- [GenerateTypedNameReferences]
- public sealed partial class HumanoidProfileEditor : BoxContainer
- {
- private readonly IClientPreferencesManager _preferencesManager;
- private readonly IConfigurationManager _cfgManager;
- private readonly IEntityManager _entManager;
- private readonly IFileDialogManager _dialogManager;
- private readonly IPlayerManager _playerManager;
- private readonly IPrototypeManager _prototypeManager;
- private readonly IResourceManager _resManager;
- private readonly MarkingManager _markingManager;
- private readonly JobRequirementsManager _requirements;
- private readonly LobbyUIController _controller;
- private FlavorText.FlavorText? _flavorText;
- private TextEdit? _flavorTextEdit;
- // One at a time.
- private LoadoutWindow? _loadoutWindow;
- private bool _exporting;
- private bool _imaging;
- /// <summary>
- /// If we're attempting to save.
- /// </summary>
- public event Action? Save;
- /// <summary>
- /// Entity used for the profile editor preview
- /// </summary>
- public EntityUid PreviewDummy;
- /// <summary>
- /// Temporary override of their selected job, used to preview roles.
- /// </summary>
- public JobPrototype? JobOverride;
- /// <summary>
- /// The character slot for the current profile.
- /// </summary>
- public int? CharacterSlot;
- /// <summary>
- /// The work in progress profile being edited.
- /// </summary>
- public HumanoidCharacterProfile? Profile;
- private List<SpeciesPrototype> _species = new();
- private List<(string, RequirementsSelector)> _jobPriorities = new();
- private readonly Dictionary<string, BoxContainer> _jobCategories;
- private Direction _previewRotation = Direction.North;
- private ColorSelectorSliders _rgbSkinColorSelector;
- private bool _isDirty;
- [ValidatePrototypeId<GuideEntryPrototype>]
- private const string DefaultSpeciesGuidebook = "Species";
- public event Action<List<ProtoId<GuideEntryPrototype>>>? OnOpenGuidebook;
- private ISawmill _sawmill;
- public HumanoidProfileEditor(
- IClientPreferencesManager preferencesManager,
- IConfigurationManager configurationManager,
- IEntityManager entManager,
- IFileDialogManager dialogManager,
- ILogManager logManager,
- IPlayerManager playerManager,
- IPrototypeManager prototypeManager,
- IResourceManager resManager,
- JobRequirementsManager requirements,
- MarkingManager markings)
- {
- RobustXamlLoader.Load(this);
- _sawmill = logManager.GetSawmill("profile.editor");
- _cfgManager = configurationManager;
- _entManager = entManager;
- _dialogManager = dialogManager;
- _playerManager = playerManager;
- _prototypeManager = prototypeManager;
- _markingManager = markings;
- _preferencesManager = preferencesManager;
- _resManager = resManager;
- _requirements = requirements;
- _controller = UserInterfaceManager.GetUIController<LobbyUIController>();
- ImportButton.OnPressed += args =>
- {
- ImportProfile();
- };
- ExportButton.OnPressed += args =>
- {
- ExportProfile();
- };
- ExportImageButton.OnPressed += args =>
- {
- ExportImage();
- };
- OpenImagesButton.OnPressed += args =>
- {
- _resManager.UserData.OpenOsWindow(ContentSpriteSystem.Exports);
- };
- ResetButton.OnPressed += args =>
- {
- SetProfile((HumanoidCharacterProfile?) _preferencesManager.Preferences?.SelectedCharacter, _preferencesManager.Preferences?.SelectedCharacterIndex);
- };
- SaveButton.OnPressed += args =>
- {
- Save?.Invoke();
- };
- #region Left
- #region Name
- NameEdit.OnTextChanged += args => { SetName(args.Text); };
- NameRandomize.OnPressed += args => RandomizeName();
- RandomizeEverythingButton.OnPressed += args => { RandomizeEverything(); };
- WarningLabel.SetMarkup($"[color=red]{Loc.GetString("humanoid-profile-editor-naming-rules-warning")}[/color]");
- #endregion Name
- #region Appearance
- TabContainer.SetTabTitle(0, Loc.GetString("humanoid-profile-editor-appearance-tab"));
- #region Sex
- SexButton.OnItemSelected += args =>
- {
- SexButton.SelectId(args.Id);
- SetSex((Sex) args.Id);
- };
- #endregion Sex
- #region Age
- AgeEdit.OnTextChanged += args =>
- {
- if (!int.TryParse(args.Text, out var newAge))
- return;
- SetAge(newAge);
- };
- #endregion Age
- #region Gender
- PronounsButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-male-text"), (int) Gender.Male);
- PronounsButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-female-text"), (int) Gender.Female);
- PronounsButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-epicene-text"), (int) Gender.Epicene);
- PronounsButton.AddItem(Loc.GetString("humanoid-profile-editor-pronouns-neuter-text"), (int) Gender.Neuter);
- PronounsButton.OnItemSelected += args =>
- {
- PronounsButton.SelectId(args.Id);
- SetGender((Gender) args.Id);
- };
- #endregion Gender
- RefreshSpecies();
- SpeciesButton.OnItemSelected += args =>
- {
- SpeciesButton.SelectId(args.Id);
- SetSpecies(_species[args.Id].ID);
- UpdateHairPickers();
- OnSkinColorOnValueChanged();
- };
- #region Skin
- Skin.OnValueChanged += _ =>
- {
- OnSkinColorOnValueChanged();
- };
- RgbSkinColorContainer.AddChild(_rgbSkinColorSelector = new ColorSelectorSliders());
- _rgbSkinColorSelector.OnColorChanged += _ =>
- {
- OnSkinColorOnValueChanged();
- };
- #endregion
- #region Hair
- HairStylePicker.OnMarkingSelect += newStyle =>
- {
- if (Profile is null)
- return;
- Profile = Profile.WithCharacterAppearance(
- Profile.Appearance.WithHairStyleName(newStyle.id));
- ReloadPreview();
- };
- HairStylePicker.OnColorChanged += newColor =>
- {
- if (Profile is null)
- return;
- Profile = Profile.WithCharacterAppearance(
- Profile.Appearance.WithHairColor(newColor.marking.MarkingColors[0]));
- UpdateCMarkingsHair();
- ReloadPreview();
- };
- FacialHairPicker.OnMarkingSelect += newStyle =>
- {
- if (Profile is null)
- return;
- Profile = Profile.WithCharacterAppearance(
- Profile.Appearance.WithFacialHairStyleName(newStyle.id));
- ReloadPreview();
- };
- FacialHairPicker.OnColorChanged += newColor =>
- {
- if (Profile is null)
- return;
- Profile = Profile.WithCharacterAppearance(
- Profile.Appearance.WithFacialHairColor(newColor.marking.MarkingColors[0]));
- UpdateCMarkingsFacialHair();
- ReloadPreview();
- };
- HairStylePicker.OnSlotRemove += _ =>
- {
- if (Profile is null)
- return;
- Profile = Profile.WithCharacterAppearance(
- Profile.Appearance.WithHairStyleName(HairStyles.DefaultHairStyle)
- );
- UpdateHairPickers();
- UpdateCMarkingsHair();
- ReloadPreview();
- };
- FacialHairPicker.OnSlotRemove += _ =>
- {
- if (Profile is null)
- return;
- Profile = Profile.WithCharacterAppearance(
- Profile.Appearance.WithFacialHairStyleName(HairStyles.DefaultFacialHairStyle)
- );
- UpdateHairPickers();
- UpdateCMarkingsFacialHair();
- ReloadPreview();
- };
- HairStylePicker.OnSlotAdd += delegate()
- {
- if (Profile is null)
- return;
- var hair = _markingManager.MarkingsByCategoryAndSpecies(MarkingCategories.Hair, Profile.Species).Keys
- .FirstOrDefault();
- if (string.IsNullOrEmpty(hair))
- return;
- Profile = Profile.WithCharacterAppearance(
- Profile.Appearance.WithHairStyleName(hair)
- );
- UpdateHairPickers();
- UpdateCMarkingsHair();
- ReloadPreview();
- };
- FacialHairPicker.OnSlotAdd += delegate()
- {
- if (Profile is null)
- return;
- var hair = _markingManager.MarkingsByCategoryAndSpecies(MarkingCategories.FacialHair, Profile.Species).Keys
- .FirstOrDefault();
- if (string.IsNullOrEmpty(hair))
- return;
- Profile = Profile.WithCharacterAppearance(
- Profile.Appearance.WithFacialHairStyleName(hair)
- );
- UpdateHairPickers();
- UpdateCMarkingsFacialHair();
- ReloadPreview();
- };
- #endregion Hair
- #region SpawnPriority
- foreach (var value in Enum.GetValues<SpawnPriorityPreference>())
- {
- SpawnPriorityButton.AddItem(Loc.GetString($"humanoid-profile-editor-preference-spawn-priority-{value.ToString().ToLower()}"), (int) value);
- }
- SpawnPriorityButton.OnItemSelected += args =>
- {
- SpawnPriorityButton.SelectId(args.Id);
- SetSpawnPriority((SpawnPriorityPreference) args.Id);
- };
- #endregion SpawnPriority
- #region Eyes
- EyeColorPicker.OnEyeColorPicked += newColor =>
- {
- if (Profile is null)
- return;
- Profile = Profile.WithCharacterAppearance(
- Profile.Appearance.WithEyeColor(newColor));
- Markings.CurrentEyeColor = Profile.Appearance.EyeColor;
- ReloadProfilePreview();
- };
- #endregion Eyes
- #endregion Appearance
- #region Jobs
- TabContainer.SetTabTitle(1, Loc.GetString("humanoid-profile-editor-jobs-tab"));
- PreferenceUnavailableButton.AddItem(
- Loc.GetString("humanoid-profile-editor-preference-unavailable-stay-in-lobby-button"),
- (int) PreferenceUnavailableMode.StayInLobby);
- PreferenceUnavailableButton.AddItem(
- Loc.GetString("humanoid-profile-editor-preference-unavailable-spawn-as-overflow-button",
- ("overflowJob", Loc.GetString(SharedGameTicker.FallbackOverflowJobName))),
- (int) PreferenceUnavailableMode.SpawnAsOverflow);
- PreferenceUnavailableButton.OnItemSelected += args =>
- {
- PreferenceUnavailableButton.SelectId(args.Id);
- Profile = Profile?.WithPreferenceUnavailable((PreferenceUnavailableMode) args.Id);
- SetDirty();
- };
- _jobCategories = new Dictionary<string, BoxContainer>();
- RefreshAntags();
- RefreshJobs();
- #endregion Jobs
- TabContainer.SetTabTitle(2, Loc.GetString("humanoid-profile-editor-antags-tab"));
- RefreshTraits();
- #region Markings
- TabContainer.SetTabTitle(4, Loc.GetString("humanoid-profile-editor-markings-tab"));
- Markings.OnMarkingAdded += OnMarkingChange;
- Markings.OnMarkingRemoved += OnMarkingChange;
- Markings.OnMarkingColorChange += OnMarkingChange;
- Markings.OnMarkingRankChange += OnMarkingChange;
- #endregion Markings
- RefreshFlavorText();
- #region Dummy
- SpriteRotateLeft.OnPressed += _ =>
- {
- _previewRotation = _previewRotation.TurnCw();
- SetPreviewRotation(_previewRotation);
- };
- SpriteRotateRight.OnPressed += _ =>
- {
- _previewRotation = _previewRotation.TurnCcw();
- SetPreviewRotation(_previewRotation);
- };
- #endregion Dummy
- #endregion Left
- ShowClothes.OnToggled += args =>
- {
- ReloadPreview();
- };
- SpeciesInfoButton.OnPressed += OnSpeciesInfoButtonPressed;
- UpdateSpeciesGuidebookIcon();
- IsDirty = false;
- }
- /// <summary>
- /// Refreshes the flavor text editor status.
- /// </summary>
- public void RefreshFlavorText()
- {
- if (_cfgManager.GetCVar(CCVars.FlavorText))
- {
- if (_flavorText != null)
- return;
- _flavorText = new FlavorText.FlavorText();
- TabContainer.AddChild(_flavorText);
- TabContainer.SetTabTitle(TabContainer.ChildCount - 1, Loc.GetString("humanoid-profile-editor-flavortext-tab"));
- _flavorTextEdit = _flavorText.CFlavorTextInput;
- _flavorText.OnFlavorTextChanged += OnFlavorTextChange;
- }
- else
- {
- if (_flavorText == null)
- return;
- TabContainer.RemoveChild(_flavorText);
- _flavorText.OnFlavorTextChanged -= OnFlavorTextChange;
- _flavorText.Dispose();
- _flavorTextEdit?.Dispose();
- _flavorTextEdit = null;
- _flavorText = null;
- }
- }
- /// <summary>
- /// Refreshes traits selector
- /// </summary>
- public void RefreshTraits()
- {
- TraitsList.DisposeAllChildren();
- 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 < 1)
- {
- TraitsList.AddChild(new Label
- {
- Text = Loc.GetString("humanoid-profile-editor-no-traits"),
- FontColorOverride = Color.Gray,
- });
- return;
- }
- // Setup model
- Dictionary<string, List<string>> traitGroups = new();
- List<string> defaultTraits = new();
- traitGroups.Add(TraitCategoryPrototype.Default, defaultTraits);
- foreach (var trait in traits)
- {
- if (trait.Category == null)
- {
- defaultTraits.Add(trait.ID);
- continue;
- }
- if (!_prototypeManager.HasIndex(trait.Category))
- continue;
- var group = traitGroups.GetOrNew(trait.Category);
- group.Add(trait.ID);
- }
- // Create UI view from model
- foreach (var (categoryId, categoryTraits) in traitGroups)
- {
- TraitCategoryPrototype? category = null;
- if (categoryId != TraitCategoryPrototype.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 categoryTraits)
- {
- 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 =>
- {
- if (preference)
- {
- Profile = Profile?.WithTraitPreference(trait.ID, _prototypeManager);
- }
- else
- {
- Profile = Profile?.WithoutTraitPreference(trait.ID, _prototypeManager);
- }
- 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);
- }
- }
- }
- /// <summary>
- /// Refreshes the species selector.
- /// </summary>
- public void RefreshSpecies()
- {
- SpeciesButton.Clear();
- _species.Clear();
- _species.AddRange(_prototypeManager.EnumeratePrototypes<SpeciesPrototype>().Where(o => o.RoundStart));
- var speciesIds = _species.Select(o => o.ID).ToList();
- for (var i = 0; i < _species.Count; i++)
- {
- var name = Loc.GetString(_species[i].Name);
- SpeciesButton.AddItem(name, i);
- if (Profile?.Species.Equals(_species[i].ID) == true)
- {
- SpeciesButton.SelectId(i);
- }
- }
- // If our species isn't available then reset it to default.
- if (Profile != null)
- {
- if (!speciesIds.Contains(Profile.Species))
- {
- SetSpecies(SharedHumanoidAppearanceSystem.DefaultSpecies);
- }
- }
- }
- public void RefreshAntags()
- {
- AntagList.DisposeAllChildren();
- var items = new[]
- {
- ("humanoid-profile-editor-antag-preference-yes-button", 0),
- ("humanoid-profile-editor-antag-preference-no-button", 1)
- };
- foreach (var antag in _prototypeManager.EnumeratePrototypes<AntagPrototype>().OrderBy(a => Loc.GetString(a.Name)))
- {
- if (!antag.SetPreference)
- continue;
- var antagContainer = new BoxContainer()
- {
- Orientation = LayoutOrientation.Horizontal,
- };
- var selector = new RequirementsSelector()
- {
- Margin = new Thickness(3f, 3f, 3f, 0f),
- };
- selector.OnOpenGuidebook += OnOpenGuidebook;
- var title = Loc.GetString(antag.Name);
- var description = Loc.GetString(antag.Objective);
- selector.Setup(items, title, 250, description, guides: antag.Guides);
- selector.Select(Profile?.AntagPreferences.Contains(antag.ID) == true ? 0 : 1);
- var requirements = _entManager.System<SharedRoleSystem>().GetAntagRequirement(antag);
- if (!_requirements.CheckRoleRequirements(requirements, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason))
- {
- selector.LockRequirements(reason);
- Profile = Profile?.WithAntagPreference(antag.ID, false);
- SetDirty();
- }
- else
- {
- selector.UnlockRequirements();
- }
- selector.OnSelected += preference =>
- {
- Profile = Profile?.WithAntagPreference(antag.ID, preference == 0);
- SetDirty();
- };
- antagContainer.AddChild(selector);
- antagContainer.AddChild(new Button()
- {
- Disabled = true,
- Text = Loc.GetString("loadout-window"),
- HorizontalAlignment = HAlignment.Right,
- Margin = new Thickness(3f, 0f, 0f, 0f),
- });
- AntagList.AddChild(antagContainer);
- }
- }
- private void SetDirty()
- {
- // If it equals default then reset the button.
- if (Profile == null || _preferencesManager.Preferences?.SelectedCharacter.MemberwiseEquals(Profile) == true)
- {
- IsDirty = false;
- return;
- }
- // TODO: Check if profile matches default.
- IsDirty = true;
- }
- /// <summary>
- /// Refresh all loadouts.
- /// </summary>
- public void RefreshLoadouts()
- {
- _loadoutWindow?.Dispose();
- }
- /// <summary>
- /// Reloads the entire dummy entity for preview.
- /// </summary>
- /// <remarks>
- /// This is expensive so not recommended to run if you have a slider.
- /// </remarks>
- private void ReloadPreview()
- {
- _entManager.DeleteEntity(PreviewDummy);
- PreviewDummy = EntityUid.Invalid;
- if (Profile == null || !_prototypeManager.HasIndex(Profile.Species))
- return;
- PreviewDummy = _controller.LoadProfileEntity(Profile, JobOverride, ShowClothes.Pressed);
- SpriteView.SetEntity(PreviewDummy);
- _entManager.System<MetaDataSystem>().SetEntityName(PreviewDummy, Profile.Name);
- // Check and set the dirty flag to enable the save/reset buttons as appropriate.
- SetDirty();
- }
- /// <summary>
- /// Resets the profile to the defaults.
- /// </summary>
- public void ResetToDefault()
- {
- SetProfile(
- (HumanoidCharacterProfile?) _preferencesManager.Preferences?.SelectedCharacter,
- _preferencesManager.Preferences?.SelectedCharacterIndex);
- }
- /// <summary>
- /// Sets the editor to the specified profile with the specified slot.
- /// </summary>
- public void SetProfile(HumanoidCharacterProfile? profile, int? slot)
- {
- Profile = profile?.Clone();
- CharacterSlot = slot;
- IsDirty = false;
- JobOverride = null;
- UpdateNameEdit();
- UpdateFlavorTextEdit();
- UpdateSexControls();
- UpdateGenderControls();
- UpdateSkinColor();
- UpdateSpawnPriorityControls();
- UpdateAgeEdit();
- UpdateEyePickers();
- UpdateSaveButton();
- UpdateMarkings();
- UpdateHairPickers();
- UpdateCMarkingsHair();
- UpdateCMarkingsFacialHair();
- RefreshAntags();
- RefreshJobs();
- RefreshLoadouts();
- RefreshSpecies();
- RefreshTraits();
- RefreshFlavorText();
- ReloadPreview();
- if (Profile != null)
- {
- PreferenceUnavailableButton.SelectId((int) Profile.PreferenceUnavailable);
- }
- }
- /// <summary>
- /// A slim reload that only updates the entity itself and not any of the job entities, etc.
- /// </summary>
- private void ReloadProfilePreview()
- {
- if (Profile == null || !_entManager.EntityExists(PreviewDummy))
- return;
- _entManager.System<HumanoidAppearanceSystem>().LoadProfile(PreviewDummy, Profile);
- // Check and set the dirty flag to enable the save/reset buttons as appropriate.
- SetDirty();
- }
- private void OnSpeciesInfoButtonPressed(BaseButton.ButtonEventArgs args)
- {
- // TODO GUIDEBOOK
- // make the species guide book a field on the species prototype.
- // I.e., do what jobs/antags do.
- var guidebookController = UserInterfaceManager.GetUIController<GuidebookUIController>();
- var species = Profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies;
- var page = DefaultSpeciesGuidebook;
- if (_prototypeManager.HasIndex<GuideEntryPrototype>(species))
- page = species;
- if (_prototypeManager.TryIndex<GuideEntryPrototype>(DefaultSpeciesGuidebook, out var guideRoot))
- {
- var dict = new Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry>();
- dict.Add(DefaultSpeciesGuidebook, guideRoot);
- //TODO: Don't close the guidebook if its already open, just go to the correct page
- guidebookController.OpenGuidebook(dict, includeChildren:true, selected: page);
- }
- }
- /// <summary>
- /// Refreshes all job selectors.
- /// </summary>
- public void RefreshJobs()
- {
- JobList.DisposeAllChildren();
- _jobCategories.Clear();
- _jobPriorities.Clear();
- var firstCategory = true;
- // Get all displayed departments
- var departments = new List<DepartmentPrototype>();
- foreach (var department in _prototypeManager.EnumeratePrototypes<DepartmentPrototype>())
- {
- if (department.EditorHidden)
- continue;
- departments.Add(department);
- }
- departments.Sort(DepartmentUIComparer.Instance);
- var items = new[]
- {
- ("humanoid-profile-editor-job-priority-never-button", (int) JobPriority.Never),
- ("humanoid-profile-editor-job-priority-low-button", (int) JobPriority.Low),
- ("humanoid-profile-editor-job-priority-medium-button", (int) JobPriority.Medium),
- ("humanoid-profile-editor-job-priority-high-button", (int) JobPriority.High),
- };
- foreach (var department in departments)
- {
- var departmentName = Loc.GetString(department.Name);
- if (!_jobCategories.TryGetValue(department.ID, out var category))
- {
- category = new BoxContainer
- {
- Orientation = LayoutOrientation.Vertical,
- Name = department.ID,
- ToolTip = Loc.GetString("humanoid-profile-editor-jobs-amount-in-department-tooltip",
- ("departmentName", departmentName))
- };
- if (firstCategory)
- {
- firstCategory = false;
- }
- else
- {
- category.AddChild(new Control
- {
- MinSize = new Vector2(0, 23),
- });
- }
- category.AddChild(new PanelContainer
- {
- PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#464966")},
- Children =
- {
- new Label
- {
- Text = Loc.GetString("humanoid-profile-editor-department-jobs-label",
- ("departmentName", departmentName)),
- Margin = new Thickness(5f, 0, 0, 0)
- }
- }
- });
- _jobCategories[department.ID] = category;
- JobList.AddChild(category);
- }
- var jobs = department.Roles.Select(jobId => _prototypeManager.Index(jobId))
- .Where(job => job.SetPreference)
- .ToArray();
- Array.Sort(jobs, JobUIComparer.Instance);
- foreach (var job in jobs)
- {
- var jobContainer = new BoxContainer()
- {
- Orientation = LayoutOrientation.Horizontal,
- };
- var selector = new RequirementsSelector()
- {
- Margin = new Thickness(3f, 3f, 3f, 0f),
- };
- selector.OnOpenGuidebook += OnOpenGuidebook;
- var icon = new TextureRect
- {
- TextureScale = new Vector2(2, 2),
- VerticalAlignment = VAlignment.Center
- };
- var jobIcon = _prototypeManager.Index(job.Icon);
- icon.Texture = jobIcon.Icon.Frame0();
- selector.Setup(items, job.LocalizedName, 200, job.LocalizedDescription, icon, job.Guides);
- if (!_requirements.IsAllowed(job, (HumanoidCharacterProfile?)_preferencesManager.Preferences?.SelectedCharacter, out var reason))
- {
- selector.LockRequirements(reason);
- }
- else
- {
- selector.UnlockRequirements();
- }
- selector.OnSelected += selectedPrio =>
- {
- var selectedJobPrio = (JobPriority) selectedPrio;
- Profile = Profile?.WithJobPriority(job.ID, selectedJobPrio);
- foreach (var (jobId, other) in _jobPriorities)
- {
- // Sync other selectors with the same job in case of multiple department jobs
- if (jobId == job.ID)
- {
- other.Select(selectedPrio);
- continue;
- }
- if (selectedJobPrio != JobPriority.High || (JobPriority) other.Selected != JobPriority.High)
- continue;
- // Lower any other high priorities to medium.
- other.Select((int)JobPriority.Medium);
- Profile = Profile?.WithJobPriority(jobId, JobPriority.Medium);
- }
- // TODO: Only reload on high change (either to or from).
- ReloadPreview();
- UpdateJobPriorities();
- SetDirty();
- };
- var loadoutWindowBtn = new Button()
- {
- Text = Loc.GetString("loadout-window"),
- HorizontalAlignment = HAlignment.Right,
- VerticalAlignment = VAlignment.Center,
- Margin = new Thickness(3f, 3f, 0f, 0f),
- };
- var collection = IoCManager.Instance!;
- var protoManager = collection.Resolve<IPrototypeManager>();
- // If no loadout found then disabled button
- if (!protoManager.TryIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID), out var roleLoadoutProto))
- {
- loadoutWindowBtn.Disabled = true;
- }
- // else
- else
- {
- loadoutWindowBtn.OnPressed += args =>
- {
- RoleLoadout? loadout = null;
- // Clone so we don't modify the underlying loadout.
- Profile?.Loadouts.TryGetValue(LoadoutSystem.GetJobPrototype(job.ID), out loadout);
- loadout = loadout?.Clone();
- if (loadout == null)
- {
- loadout = new RoleLoadout(roleLoadoutProto.ID);
- loadout.SetDefault(Profile, _playerManager.LocalSession, _prototypeManager);
- }
- OpenLoadout(job, loadout, roleLoadoutProto);
- };
- }
- _jobPriorities.Add((job.ID, selector));
- jobContainer.AddChild(selector);
- jobContainer.AddChild(loadoutWindowBtn);
- category.AddChild(jobContainer);
- }
- }
- UpdateJobPriorities();
- }
- private void OpenLoadout(JobPrototype? jobProto, RoleLoadout roleLoadout, RoleLoadoutPrototype roleLoadoutProto)
- {
- _loadoutWindow?.Dispose();
- _loadoutWindow = null;
- var collection = IoCManager.Instance;
- if (collection == null || _playerManager.LocalSession == null || Profile == null)
- return;
- JobOverride = jobProto;
- var session = _playerManager.LocalSession;
- _loadoutWindow = new LoadoutWindow(Profile, roleLoadout, roleLoadoutProto, _playerManager.LocalSession, collection)
- {
- Title = jobProto?.ID + "-loadout",
- };
- // Refresh the buttons etc.
- _loadoutWindow.RefreshLoadouts(roleLoadout, session, collection);
- _loadoutWindow.OpenCenteredLeft();
- _loadoutWindow.OnNameChanged += name =>
- {
- roleLoadout.EntityName = name;
- Profile = Profile.WithLoadout(roleLoadout);
- SetDirty();
- };
- _loadoutWindow.OnLoadoutPressed += (loadoutGroup, loadoutProto) =>
- {
- roleLoadout.AddLoadout(loadoutGroup, loadoutProto, _prototypeManager);
- _loadoutWindow.RefreshLoadouts(roleLoadout, session, collection);
- Profile = Profile?.WithLoadout(roleLoadout);
- ReloadPreview();
- };
- _loadoutWindow.OnLoadoutUnpressed += (loadoutGroup, loadoutProto) =>
- {
- roleLoadout.RemoveLoadout(loadoutGroup, loadoutProto, _prototypeManager);
- _loadoutWindow.RefreshLoadouts(roleLoadout, session, collection);
- Profile = Profile?.WithLoadout(roleLoadout);
- ReloadPreview();
- };
- JobOverride = jobProto;
- ReloadPreview();
- _loadoutWindow.OnClose += () =>
- {
- JobOverride = null;
- ReloadPreview();
- };
- if (Profile is null)
- return;
- UpdateJobPriorities();
- }
- private void OnFlavorTextChange(string content)
- {
- if (Profile is null)
- return;
- Profile = Profile.WithFlavorText(content);
- SetDirty();
- }
- private void OnMarkingChange(MarkingSet markings)
- {
- if (Profile is null)
- return;
- Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithMarkings(markings.GetForwardEnumerator().ToList()));
- ReloadProfilePreview();
- }
- private void OnSkinColorOnValueChanged()
- {
- if (Profile is null) return;
- var skin = _prototypeManager.Index<SpeciesPrototype>(Profile.Species).SkinColoration;
- switch (skin)
- {
- case HumanoidSkinColor.HumanToned:
- {
- if (!Skin.Visible)
- {
- Skin.Visible = true;
- RgbSkinColorContainer.Visible = false;
- }
- var color = SkinColor.HumanSkinTone((int) Skin.Value);
- Markings.CurrentSkinColor = color;
- Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));//
- break;
- }
- case HumanoidSkinColor.Hues:
- {
- if (!RgbSkinColorContainer.Visible)
- {
- Skin.Visible = false;
- RgbSkinColorContainer.Visible = true;
- }
- Markings.CurrentSkinColor = _rgbSkinColorSelector.Color;
- Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(_rgbSkinColorSelector.Color));
- break;
- }
- case HumanoidSkinColor.TintedHues:
- {
- if (!RgbSkinColorContainer.Visible)
- {
- Skin.Visible = false;
- RgbSkinColorContainer.Visible = true;
- }
- var color = SkinColor.TintedHues(_rgbSkinColorSelector.Color);
- Markings.CurrentSkinColor = color;
- Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
- break;
- }
- case HumanoidSkinColor.VoxFeathers:
- {
- if (!RgbSkinColorContainer.Visible)
- {
- Skin.Visible = false;
- RgbSkinColorContainer.Visible = true;
- }
- var color = SkinColor.ClosestVoxColor(_rgbSkinColorSelector.Color);
- Markings.CurrentSkinColor = color;
- Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
- break;
- }
- }
- ReloadProfilePreview();
- }
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (!disposing)
- return;
- _loadoutWindow?.Dispose();
- _loadoutWindow = null;
- }
- protected override void EnteredTree()
- {
- base.EnteredTree();
- ReloadPreview();
- }
- protected override void ExitedTree()
- {
- base.ExitedTree();
- _entManager.DeleteEntity(PreviewDummy);
- PreviewDummy = EntityUid.Invalid;
- }
- private void SetAge(int newAge)
- {
- Profile = Profile?.WithAge(newAge);
- ReloadPreview();
- }
- private void SetSex(Sex newSex)
- {
- Profile = Profile?.WithSex(newSex);
- // for convenience, default to most common gender when new sex is selected
- switch (newSex)
- {
- case Sex.Male:
- Profile = Profile?.WithGender(Gender.Male);
- break;
- case Sex.Female:
- Profile = Profile?.WithGender(Gender.Female);
- break;
- default:
- Profile = Profile?.WithGender(Gender.Epicene);
- break;
- }
- UpdateGenderControls();
- Markings.SetSex(newSex);
- ReloadPreview();
- }
- private void SetGender(Gender newGender)
- {
- Profile = Profile?.WithGender(newGender);
- ReloadPreview();
- }
- private void SetSpecies(string newSpecies)
- {
- Profile = Profile?.WithSpecies(newSpecies);
- OnSkinColorOnValueChanged(); // Species may have special color prefs, make sure to update it.
- Markings.SetSpecies(newSpecies); // Repopulate the markings tab as well.
- // In case there's job restrictions for the species
- RefreshJobs();
- // In case there's species restrictions for loadouts
- RefreshLoadouts();
- UpdateSexControls(); // update sex for new species
- UpdateSpeciesGuidebookIcon();
- ReloadPreview();
- }
- private void SetName(string newName)
- {
- Profile = Profile?.WithName(newName);
- SetDirty();
- if (!IsDirty)
- return;
- _entManager.System<MetaDataSystem>().SetEntityName(PreviewDummy, newName);
- }
- private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority)
- {
- Profile = Profile?.WithSpawnPriorityPreference(newSpawnPriority);
- SetDirty();
- }
- public bool IsDirty
- {
- get => _isDirty;
- set
- {
- if (_isDirty == value)
- return;
- _isDirty = value;
- UpdateSaveButton();
- }
- }
- private void UpdateNameEdit()
- {
- NameEdit.Text = Profile?.Name ?? "";
- }
- private void UpdateFlavorTextEdit()
- {
- if (_flavorTextEdit != null)
- {
- _flavorTextEdit.TextRope = new Rope.Leaf(Profile?.FlavorText ?? "");
- }
- }
- private void UpdateAgeEdit()
- {
- AgeEdit.Text = Profile?.Age.ToString() ?? "";
- }
- /// <summary>
- /// Updates selected job priorities to the profile's.
- /// </summary>
- private void UpdateJobPriorities()
- {
- foreach (var (jobId, prioritySelector) in _jobPriorities)
- {
- var priority = Profile?.JobPriorities.GetValueOrDefault(jobId, JobPriority.Never) ?? JobPriority.Never;
- prioritySelector.Select((int) priority);
- }
- }
- private void UpdateSexControls()
- {
- if (Profile == null)
- return;
- SexButton.Clear();
- var sexes = new List<Sex>();
- // add species sex options, default to just none if we are in bizzaro world and have no species
- if (_prototypeManager.TryIndex<SpeciesPrototype>(Profile.Species, out var speciesProto))
- {
- foreach (var sex in speciesProto.Sexes)
- {
- sexes.Add(sex);
- }
- }
- else
- {
- sexes.Add(Sex.Unsexed);
- }
- // add button for each sex
- foreach (var sex in sexes)
- {
- SexButton.AddItem(Loc.GetString($"humanoid-profile-editor-sex-{sex.ToString().ToLower()}-text"), (int) sex);
- }
- if (sexes.Contains(Profile.Sex))
- SexButton.SelectId((int) Profile.Sex);
- else
- SexButton.SelectId((int) sexes[0]);
- }
- private void UpdateSkinColor()
- {
- if (Profile == null)
- return;
- var skin = _prototypeManager.Index<SpeciesPrototype>(Profile.Species).SkinColoration;
- switch (skin)
- {
- case HumanoidSkinColor.HumanToned:
- {
- if (!Skin.Visible)
- {
- Skin.Visible = true;
- RgbSkinColorContainer.Visible = false;
- }
- Skin.Value = SkinColor.HumanSkinToneFromColor(Profile.Appearance.SkinColor);
- break;
- }
- case HumanoidSkinColor.Hues:
- {
- if (!RgbSkinColorContainer.Visible)
- {
- Skin.Visible = false;
- RgbSkinColorContainer.Visible = true;
- }
- // set the RGB values to the direct values otherwise
- _rgbSkinColorSelector.Color = Profile.Appearance.SkinColor;
- break;
- }
- case HumanoidSkinColor.TintedHues:
- {
- if (!RgbSkinColorContainer.Visible)
- {
- Skin.Visible = false;
- RgbSkinColorContainer.Visible = true;
- }
- // set the RGB values to the direct values otherwise
- _rgbSkinColorSelector.Color = Profile.Appearance.SkinColor;
- break;
- }
- case HumanoidSkinColor.VoxFeathers:
- {
- if (!RgbSkinColorContainer.Visible)
- {
- Skin.Visible = false;
- RgbSkinColorContainer.Visible = true;
- }
- _rgbSkinColorSelector.Color = SkinColor.ClosestVoxColor(Profile.Appearance.SkinColor);
- break;
- }
- }
- }
- 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;
- const string style = "SpeciesInfoDefault";
- SpeciesInfoButton.StyleClasses.Add(style);
- }
- private void UpdateMarkings()
- {
- if (Profile == null)
- {
- return;
- }
- Markings.SetData(Profile.Appearance.Markings, Profile.Species,
- Profile.Sex, Profile.Appearance.SkinColor, Profile.Appearance.EyeColor
- );
- }
- private void UpdateGenderControls()
- {
- if (Profile == null)
- {
- return;
- }
- PronounsButton.SelectId((int) Profile.Gender);
- }
- private void UpdateSpawnPriorityControls()
- {
- if (Profile == null)
- {
- return;
- }
- SpawnPriorityButton.SelectId((int) Profile.SpawnPriority);
- }
- private void UpdateHairPickers()
- {
- if (Profile == null)
- {
- return;
- }
- var hairMarking = Profile.Appearance.HairStyleId switch
- {
- HairStyles.DefaultHairStyle => new List<Marking>(),
- _ => new() { new(Profile.Appearance.HairStyleId, new List<Color>() { Profile.Appearance.HairColor }) },
- };
- var facialHairMarking = Profile.Appearance.FacialHairStyleId switch
- {
- HairStyles.DefaultFacialHairStyle => new List<Marking>(),
- _ => new() { new(Profile.Appearance.FacialHairStyleId, new List<Color>() { Profile.Appearance.FacialHairColor }) },
- };
- HairStylePicker.UpdateData(
- hairMarking,
- Profile.Species,
- 1);
- FacialHairPicker.UpdateData(
- facialHairMarking,
- Profile.Species,
- 1);
- }
- private void UpdateCMarkingsHair()
- {
- if (Profile == null)
- {
- return;
- }
- // hair color
- Color? hairColor = null;
- if ( Profile.Appearance.HairStyleId != HairStyles.DefaultHairStyle &&
- _markingManager.Markings.TryGetValue(Profile.Appearance.HairStyleId, out var hairProto)
- )
- {
- if (_markingManager.CanBeApplied(Profile.Species, Profile.Sex, hairProto, _prototypeManager))
- {
- if (_markingManager.MustMatchSkin(Profile.Species, HumanoidVisualLayers.Hair, out var _, _prototypeManager))
- {
- hairColor = Profile.Appearance.SkinColor;
- }
- else
- {
- hairColor = Profile.Appearance.HairColor;
- }
- }
- }
- if (hairColor != null)
- {
- Markings.HairMarking = new (Profile.Appearance.HairStyleId, new List<Color>() { hairColor.Value });
- }
- else
- {
- Markings.HairMarking = null;
- }
- }
- private void UpdateCMarkingsFacialHair()
- {
- if (Profile == null)
- {
- return;
- }
- // facial hair color
- Color? facialHairColor = null;
- if ( Profile.Appearance.FacialHairStyleId != HairStyles.DefaultFacialHairStyle &&
- _markingManager.Markings.TryGetValue(Profile.Appearance.FacialHairStyleId, out var facialHairProto))
- {
- if (_markingManager.CanBeApplied(Profile.Species, Profile.Sex, facialHairProto, _prototypeManager))
- {
- if (_markingManager.MustMatchSkin(Profile.Species, HumanoidVisualLayers.Hair, out var _, _prototypeManager))
- {
- facialHairColor = Profile.Appearance.SkinColor;
- }
- else
- {
- facialHairColor = Profile.Appearance.FacialHairColor;
- }
- }
- }
- if (facialHairColor != null)
- {
- Markings.FacialHairMarking = new (Profile.Appearance.FacialHairStyleId, new List<Color>() { facialHairColor.Value });
- }
- else
- {
- Markings.FacialHairMarking = null;
- }
- }
- private void UpdateEyePickers()
- {
- if (Profile == null)
- {
- return;
- }
- Markings.CurrentEyeColor = Profile.Appearance.EyeColor;
- EyeColorPicker.SetData(Profile.Appearance.EyeColor);
- }
- private void UpdateSaveButton()
- {
- SaveButton.Disabled = Profile is null || !IsDirty;
- ResetButton.Disabled = Profile is null || !IsDirty;
- }
- private void SetPreviewRotation(Direction direction)
- {
- SpriteView.OverrideDirection = (Direction) ((int) direction % 4 * 2);
- }
- private void RandomizeEverything()
- {
- Profile = HumanoidCharacterProfile.Random();
- SetProfile(Profile, CharacterSlot);
- SetDirty();
- }
- private void RandomizeName()
- {
- if (Profile == null) return;
- var name = HumanoidCharacterProfile.GetName(Profile.Species, Profile.Gender);
- SetName(name);
- UpdateNameEdit();
- }
- private async void ExportImage()
- {
- if (_imaging)
- return;
- var dir = SpriteView.OverrideDirection ?? Direction.South;
- // I tried disabling the button but it looks sorta goofy as it only takes a frame or two to save
- _imaging = true;
- await _entManager.System<ContentSpriteSystem>().Export(PreviewDummy, dir, includeId: false);
- _imaging = false;
- }
- private async void ImportProfile()
- {
- if (_exporting || CharacterSlot == null || Profile == null)
- return;
- StartExport();
- await using var file = await _dialogManager.OpenFile(new FileDialogFilters(new FileDialogFilters.Group("yml")));
- if (file == null)
- {
- EndExport();
- return;
- }
- try
- {
- var profile = _entManager.System<HumanoidAppearanceSystem>().FromStream(file, _playerManager.LocalSession!);
- var oldProfile = Profile;
- SetProfile(profile, CharacterSlot);
- IsDirty = !profile.MemberwiseEquals(oldProfile);
- }
- catch (Exception exc)
- {
- _sawmill.Error($"Error when importing profile\n{exc.StackTrace}");
- }
- finally
- {
- EndExport();
- }
- }
- private async void ExportProfile()
- {
- if (Profile == null || _exporting)
- return;
- StartExport();
- var file = await _dialogManager.SaveFile(new FileDialogFilters(new FileDialogFilters.Group("yml")));
- if (file == null)
- {
- EndExport();
- return;
- }
- try
- {
- var dataNode = _entManager.System<HumanoidAppearanceSystem>().ToDataNode(Profile);
- await using var writer = new StreamWriter(file.Value.fileStream);
- dataNode.Write(writer);
- }
- catch (Exception exc)
- {
- _sawmill.Error($"Error when exporting profile\n{exc.StackTrace}");
- }
- finally
- {
- EndExport();
- await file.Value.fileStream.DisposeAsync();
- }
- }
- private void StartExport()
- {
- _exporting = true;
- ImportButton.Disabled = true;
- ExportButton.Disabled = true;
- }
- private void EndExport()
- {
- _exporting = false;
- ImportButton.Disabled = false;
- ExportButton.Disabled = false;
- }
- }
- }
|