using System.Globalization; using System.Linq; using Content.Client.UserInterface.Systems.Guidebook; using Content.Shared._DV.Species; using Content.Shared.Guidebook; using Content.Shared.Humanoid; using Content.Shared.Humanoid.Prototypes; using Content.Shared.Preferences; using Robust.Client.UserInterface.Controls; using Robust.Shared.Enums; using Robust.Shared.Prototypes; using Content.Client._CD.Records.UI; // DeltaV using Content.Shared._DV.Body.Systems; // DeltaV namespace Content.Client.Lobby.UI; public sealed partial class HumanoidProfileEditor { public event Action>>? OnOpenGuidebook; private ColorSelectorSliders _rgbSkinColorSelector; private List _species = new(); private static readonly ProtoId DefaultSpeciesGuidebook = "Species"; public void UpdateSpeciesGuidebookIcon() { SpeciesInfoButton.StyleClasses.Clear(); var species = Profile?.Species; if (species is null) return; if (!_prototypeManager.Resolve(species, out var speciesProto)) return; // Don't display the info button if no guide entry is found if (!_prototypeManager.HasIndex(species)) return; const string style = "SpeciesInfoDefault"; SpeciesInfoButton.StyleIdentifier = style; } private void UpdateGenderControls() { if (Profile == null) { return; } PronounsButton.SelectId((int)Profile.Gender); } private void UpdateAgeEdit() { AgeEdit.Text = Profile?.Age.ToString() ?? ""; } private void UpdateSexControls() { if (Profile == null) return; SexButton.Clear(); var sexes = new List(); // add species sex options, default to just none if we are in bizzaro world and have no species if (_prototypeManager.Resolve(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 UpdateEyePickers() { if (Profile == null) { return; } _markingsModel.SetOrganEyeColor(Profile.Appearance.EyeColor); EyeColorPicker.SetData(Profile.Appearance.EyeColor); } private void UpdateSkinColor() { if (Profile == null) return; var skin = _prototypeManager.Index(Profile.Species).SkinColoration; var strategy = _prototypeManager.Index(skin).Strategy; switch (strategy.InputType) { case SkinColorationStrategyInput.Unary: { if (!Skin.Visible) { Skin.Visible = true; RgbSkinColorContainer.Visible = false; } Skin.Value = strategy.ToUnary(Profile.Appearance.SkinColor); break; } case SkinColorationStrategyInput.Color: { if (!RgbSkinColorContainer.Visible) { Skin.Visible = false; RgbSkinColorContainer.Visible = true; } _rgbSkinColorSelector.Color = strategy.ClosestSkinColor(Profile.Appearance.SkinColor); break; } } } private void UpdateSpawnPriorityControls() { if (Profile == null) { return; } SpawnPriorityButton.SelectId((int)Profile.SpawnPriority); } /// /// Refreshes the species selector. /// public void RefreshSpecies() { SpeciesButton.Clear(); _species.Clear(); _species.AddRange(_prototypeManager.EnumeratePrototypes().Where(o => o.RoundStart)); _species.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.CurrentCultureIgnoreCase)); var speciesIds = _species.Select(o => o.ID).ToList(); for (var i = 0; i < _species.Count; i++) { if (SpeciesHiderSystem.IsHidden(_species[i].ID)) // Delta V - Don't add hidden species continue; 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(HumanoidCharacterProfile.DefaultSpecies); } } } private void SetSpecies(string newSpecies) { Profile = Profile?.WithSpecies(newSpecies); OnSkinColorOnValueChanged(); // Species may have special color prefs, make sure to update it. _markingsModel.OrganData = _markingManager.GetMarkingData(newSpecies); _markingsModel.ValidateMarkings(); // 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(); UpdateHeightControls(); // DeltaV ReloadPreview(); } // Begin CD - Character Records private void SetProfileHeight(float height) { Profile = Profile?.WithHeight(height); SetDirty(); ReloadProfilePreview(); } private void UpdateHeightControls() { if (Profile == null) return; var prototype = _prototypeManager.Index(Profile.Species); _defaultHeight = prototype.DefaultHeight; var sliderPercent = (Profile.Height - prototype.MinHeight) / (prototype.MaxHeight - prototype.MinHeight); CDHeightSlider.Value = sliderPercent; var scaleReference = _defaultHeight * prototype.BaseScale.Y; var newHeight = MathF.Round(MathHelper.Lerp(prototype.MinHeight, prototype.MaxHeight, sliderPercent), 2); CDHeightLabel.Text = UnitConversion.GetMetricAndImperialDisplayFromScale(scaleReference * newHeight); CDPullSpeedReductionLabel.Text = SmallCharacterSystem.GetPullSpeedPenaltyDisplayFromScale(newHeight); } // End CD - Character Records 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(); _markingsModel.SetOrganSexes(newSex); ReloadPreview(); } private void SetGender(Gender newGender) { Profile = Profile?.WithGender(newGender); ReloadPreview(); } private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority) { Profile = Profile?.WithSpawnPriorityPreference(newSpawnPriority); 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(); var species = Profile?.Species ?? HumanoidCharacterProfile.DefaultSpecies; var page = DefaultSpeciesGuidebook; if (_prototypeManager.HasIndex(species)) page = new ProtoId(species.Id); // Gross. See above todo comment. if (_prototypeManager.Resolve(DefaultSpeciesGuidebook, out var guideRoot)) { var dict = new Dictionary, 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); } } private void OnSkinColorOnValueChanged() { if (Profile is null) return; var skin = _prototypeManager.Index(Profile.Species).SkinColoration; var strategy = _prototypeManager.Index(skin).Strategy; switch (strategy.InputType) { case SkinColorationStrategyInput.Unary: { if (!Skin.Visible) { Skin.Visible = true; RgbSkinColorContainer.Visible = false; } var color = strategy.FromUnary(Skin.Value); _markingsModel.SetOrganSkinColor(color); Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color)); break; } case SkinColorationStrategyInput.Color: { if (!RgbSkinColorContainer.Visible) { Skin.Visible = false; RgbSkinColorContainer.Visible = true; } var color = strategy.ClosestSkinColor(_rgbSkinColorSelector.Color); _markingsModel.SetOrganSkinColor(color); Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color)); break; } } ReloadProfilePreview(); } }