using System.Linq; using System.Numerics; using System.Text; using Content.Client.Guidebook; using Content.Client.Paint; using Content.Client.Players.PlayTimeTracking; using Content.Client.Stylesheets; using Content.Client.UserInterface.Systems.Guidebook; using Content.Shared.Clothing.Loadouts.Prototypes; using Content.Shared.Clothing.Loadouts.Systems; using Content.Shared.Customization.Systems; using Content.Shared.Guidebook; using Content.Shared.Labels.Components; using Content.Shared.Paint; using Content.Shared.Preferences; using Content.Shared.Roles; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Configuration; using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Client.Lobby.UI; [GenerateTypedNameReferences] public sealed partial class LoadoutPreferenceSelector : Control { public const string DefaultLoadoutInfoGuidebook = "LoadoutInfo"; public EntityUid DummyEntityUid; private readonly IEntityManager _entityManager; public LoadoutPrototype Loadout { get; } private Loadout _preference = null!; public Loadout Preference { get => _preference; set { _preference = value; NameEdit.Text = value.CustomName ?? ""; DescriptionEdit.TextRope = new Rope.Leaf(value.CustomDescription ?? ""); BookTextEdit.TextRope = new Rope.Leaf(value.CustomContent ?? ""); // WD EDIT ColorEdit.Color = Color.FromHex(value.CustomColorTint, Color.White); if (value.CustomColorTint != null) UpdatePaint(new(DummyEntityUid, _entityManager.GetComponent(DummyEntityUid)), _entityManager); HeirloomButton.Pressed = value.CustomHeirloom ?? false; } } public bool Valid; private bool _showUnusable; public bool ShowUnusable { get => _showUnusable; set { _showUnusable = value; Visible = Valid && _wearable || _showUnusable; PreferenceButton.RemoveStyleClass(StyleBase.ButtonDanger); PreferenceButton.AddStyleClass(Valid ? "" : StyleBase.ButtonDanger); } } private bool _wearable; public bool Wearable { get => _wearable; set { _wearable = value; Visible = Valid && _wearable || _showUnusable; PreferenceButton.RemoveStyleClass(StyleBase.ButtonCaution); PreferenceButton.AddStyleClass(_wearable ? "" : StyleBase.ButtonCaution); } } public event Action? PreferenceChanged; public LoadoutPreferenceSelector(LoadoutPrototype loadout, JobPrototype highJob, HumanoidCharacterProfile profile, ref Dictionary entities, IEntityManager entityManager, IPrototypeManager prototypeManager, IConfigurationManager configManager, CharacterRequirementsSystem characterRequirementsSystem, JobRequirementsManager jobRequirementsManager) { RobustXamlLoader.Load(this); _entityManager = entityManager; Loadout = loadout; // Show/hide the special menu and items depending on what's allowed HeirloomButton.Visible = loadout.CanBeHeirloom; SpecialMenu.Visible = Loadout.CustomName || Loadout.CustomDescription || Loadout.CustomContent || Loadout.CustomColorTint; // WD EDIT SpecialName.Visible = Loadout.CustomName; SpecialDescription.Visible = Loadout.CustomDescription; SpecialBookText.Visible = Loadout.CustomContent; // WD EDIT SpecialColorTintToggle.Visible = Loadout.CustomColorTint; SpriteView previewLoadout; if (!entities.TryGetValue(loadout.ID + 0, out var dummyLoadoutItem)) { // Get the first item in the loadout to be the preview dummyLoadoutItem = entityManager.SpawnEntity(loadout.Items.First(), MapCoordinates.Nullspace); entities.Add(loadout.ID + 0, dummyLoadoutItem); // Create a sprite preview of the loadout item previewLoadout = new SpriteView { Scale = new Vector2(1, 1), OverrideDirection = Direction.South, VerticalAlignment = VAlignment.Center, SizeFlagsStretchRatio = 1, }; previewLoadout.SetEntity(dummyLoadoutItem); } else { // Create a sprite preview of the loadout item previewLoadout = new SpriteView { Scale = new Vector2(1, 1), OverrideDirection = Direction.South, VerticalAlignment = VAlignment.Center, SizeFlagsStretchRatio = 1, }; previewLoadout.SetEntity(dummyLoadoutItem); } DummyEntityUid = dummyLoadoutItem; entityManager.EnsureComponent(dummyLoadoutItem); entityManager.EnsureComponent(dummyLoadoutItem, out var paint); var loadoutName = Loc.GetString($"loadout-name-{loadout.ID}") == $"loadout-name-{loadout.ID}" ? entityManager.GetComponent(dummyLoadoutItem).EntityName : Loc.GetString($"loadout-name-{loadout.ID}"); // Display the item's label if it's present if (entityManager.TryGetComponent(dummyLoadoutItem, out LabelComponent? labelComponent)) { var itemLabel = labelComponent.CurrentLabel; if (!string.IsNullOrEmpty(itemLabel)) loadoutName += $" ({Loc.GetString(itemLabel)})"; } var loadoutDesc = !Loc.TryGetString($"loadout-description-{loadout.ID}", out var description) ? entityManager.GetComponent(dummyLoadoutItem).EntityDescription : description; // Manage the info button void UpdateGuidebook() => GuidebookButton.Visible = prototypeManager.HasIndex(loadout.GuideEntry); UpdateGuidebook(); prototypeManager.PrototypesReloaded += _ => UpdateGuidebook(); GuidebookButton.OnPressed += _ => { if (!prototypeManager.TryIndex(loadout.GuideEntry, out var guideRoot)) return; var guidebookController = UserInterfaceManager.GetUIController(); //TODO: Don't close the guidebook if its already open, just go to the correct page guidebookController.OpenGuidebook( new Dictionary, GuideEntry> { { loadout.GuideEntry, guideRoot } }, includeChildren: true, selected: loadout.GuideEntry); }; // Create a checkbox to get the loadout PreferenceButton.AddChild(new BoxContainer { Children = { new Label { Text = loadout.Cost.ToString(), StyleClasses = { StyleBase.StyleClassLabelHeading }, MinWidth = 32, MaxWidth = 32, ClipText = true, Margin = new(0, 0, 8, 0), }, new PanelContainer { PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#2f2f2f") }, Children = { previewLoadout, }, }, new Label { Text = loadoutName, Margin = new(8, 0, 0, 0), }, }, }); // Update prefs cache when something changes NameEdit.OnTextChanged += _ => _preference.CustomName = string.IsNullOrEmpty(NameEdit.Text) ? null : NameEdit.Text; DescriptionEdit.OnTextChanged += _ => _preference.CustomDescription = string.IsNullOrEmpty(Rope.Collapse(DescriptionEdit.TextRope)) ? null : Rope.Collapse(DescriptionEdit.TextRope); // WD EDIT START BookTextEdit.OnTextChanged += _ => _preference.CustomContent = string.IsNullOrEmpty(Rope.Collapse(BookTextEdit.TextRope)) ? null : Rope.Collapse(BookTextEdit.TextRope); // WD EDIT END SpecialColorTintToggle.OnToggled += args => ColorEdit.Visible = args.Pressed; ColorEdit.OnColorChanged += _ => { _preference.CustomColorTint = SpecialColorTintToggle.Pressed ? ColorEdit.Color.ToHex() : null; UpdatePaint(new(dummyLoadoutItem, paint), entityManager); }; var desc = Loc.GetString(loadoutDesc); NameEdit.PlaceHolder = loadoutName; DescriptionEdit.Placeholder = new Rope.Leaf(desc); BookTextEdit.Placeholder = new Rope.Leaf(Loc.GetString("humanoid-profile-editor-loadouts-customize-book-text-placeholder")); // WD EDIT var tooltip = new Tooltip(); PreferenceButton.TooltipSupplier = _ => tooltip; var toolBox = (BoxContainer) tooltip.Children.First(); if (!string.IsNullOrEmpty(desc)) tooltip.SetMessage(FormattedMessage.FromMarkupPermissive(desc)); if (loadout.Requirements.Any()) { toolBox.AddChild( new Label { Text = Loc.GetString("character-requirement-desc"), StyleClasses = { StyleBase.StyleClassLabelHeading, }, Margin = new(0, 8, 0, 4), }); MakeTooltipTree(toolBox, loadout.Requirements); toolBox.AddChild(new() { Margin = new(0, 2), }); } return; void MakeTooltipTree(BoxContainer box, List requirements) { foreach (var requirement in requirements) { if (requirement is CharacterLogicRequirement logicRequirement) { requirement.IsValid( highJob, profile, new Dictionary(), jobRequirementsManager.IsWhitelisted(), loadout, entityManager, prototypeManager, configManager, out var reason); box.AddChild(new RichTextLabel { Text = reason?.Split("\n")[0], Margin = new(8, 2), }); var newBox = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Vertical, }; box.AddChild(new PanelContainer { PanelOverride = new StyleBoxFlat { BackgroundColor = Color.FromHex("#1B1B1C"), BorderColor = Color.FromHex("#3A3A3D"), BorderThickness = new(1), }, Margin = new(8, 2), Children = { newBox, }, }); MakeTooltipTree(newBox, logicRequirement.Requirements); } else { requirement.IsValid( highJob, profile, new Dictionary(), jobRequirementsManager.IsWhitelisted(), loadout, entityManager, prototypeManager, configManager, out var reason); box.AddChild(new RichTextLabel { Text = reason, Margin = new(8, 2), }); } } } } private bool _initialized; protected override void FrameUpdate(FrameEventArgs args) { if (_initialized || SpecialMenu.Heading == null) return; // Move the special editor var heading = SpecialMenu.Heading; heading.Orphan(); ButtonGroup.AddChild(heading); GuidebookButton.Orphan(); ButtonGroup.AddChild(GuidebookButton); // These guys are here too for reasons HeadingButton.SetHeight = HeirloomButton.SetHeight = GuidebookButton.SetHeight = PreferenceButton.Size.Y; SpecialColorTintToggle.Pressed = ColorEdit.Visible = _preference.CustomColorTint != null; _initialized = true; } private void UpdatePaint(Entity entity, IEntityManager entityManager) { if (_preference.CustomColorTint != null) { entity.Comp.Color = Color.FromHex(_preference.CustomColorTint); entity.Comp.Enabled = true; } else entity.Comp.Enabled = false; var app = entityManager.System(); app.TryGetData(entity, PaintVisuals.Painted, out bool value); app.SetData(entity, PaintVisuals.Painted, !value); } }