diff --git a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs index 7c05b879ad..983ea16ed8 100644 --- a/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs +++ b/Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using System.Numerics; using Content.Client._WL.Records; // WL-Records +using Content.Client._WL.Skills.Ui; // WL-Skills using Content.Client.Electrocution; using Content.Client.Guidebook; using Content.Client.Humanoid; @@ -12,6 +13,7 @@ using Content.Client.Players.PlayTimeTracking; using Content.Client.Sprite; using Content.Client.Stylesheets; using Content.Client.UserInterface.Systems.Guidebook; +using Content.Shared._WL.Skills; // WL-Skills using Content.Shared.CCVar; using Content.Shared.Clothing; using Content.Shared.Corvax.CCCVars; @@ -132,6 +134,7 @@ namespace Content.Client.Lobby.UI // One at a time. private LoadoutWindow? _loadoutWindow; + private SkillsWindow? _skillsWindow; // WL-Skills private TTSTab? _ttsTab; // Corvax-TTS @@ -1135,6 +1138,13 @@ namespace Content.Client.Lobby.UI _loadoutWindow?.Dispose(); } + // WL-Skills-start + public void RefreshSkills() + { + _skillsWindow?.Dispose(); + } + // WL-Skills-end + /// /// Reloads the entire dummy entity for preview. /// @@ -1199,6 +1209,7 @@ namespace Content.Client.Lobby.UI RefreshAntags(); RefreshJobs(); RefreshLoadouts(); + RefreshSkills(); // WL-Skills RefreshSpecies(); RefreshTraits(); RefreshFlavorText(); @@ -1423,6 +1434,17 @@ namespace Content.Client.Lobby.UI Margin = new Thickness(3f, 3f, 0f, 0f), }; + // WL-Skills-start + var skillsWindowBtn = new Button() + { + Text = Loc.GetString("skill-window"), + HorizontalAlignment = HAlignment.Right, + VerticalAlignment = VAlignment.Center, + Margin = new Thickness(3f, 3f, 0f, 0f), + ToolTip = Loc.GetString("skill-window-tooltip") + }; + // WL-Skills-end + var collection = IoCManager.Instance!; var protoManager = collection.Resolve(); @@ -1452,10 +1474,28 @@ namespace Content.Client.Lobby.UI }; } + // WL-Skills-start + skillsWindowBtn.OnPressed += args => + { + OpenSkills(job); + }; + // WL-Skills-end + _jobPriorities.Add((job.ID, subnameSelector, selector)); jobContainer.AddChild(selector); - jobContainer.AddChild(loadoutWindowBtn); + + // WL-Skills-Edit-start + var buttonsContainer = new BoxContainer + { + Orientation = LayoutOrientation.Horizontal, + HorizontalAlignment = HAlignment.Right + }; + buttonsContainer.AddChild(loadoutWindowBtn); + buttonsContainer.AddChild(skillsWindowBtn); + + jobContainer.AddChild(buttonsContainer); category.AddChild(jobContainer); + // WL-Skills-Edit-end } //WL-Changes-end } @@ -1523,6 +1563,61 @@ namespace Content.Client.Lobby.UI UpdateJobPriorities(); } + // WL-Skills-start + private void OpenSkills(JobPrototype? jobProto) + { + _skillsWindow?.Dispose(); + _skillsWindow = null; + + if (jobProto == null || Profile == null) + return; + + JobOverride = jobProto; + + var currentSkills = Profile.Skills.GetValueOrDefault(jobProto.ID, new Dictionary()); + var defaultSkills = jobProto.DefaultSkills.ToDictionary( + kvp => (byte)kvp.Key, + kvp => kvp.Value + ); + + var bonusPoints = jobProto.BonusSkillPoints; + var racialBonus = CalculateRacialBonus(Profile.Species, Profile.Age); + var totalPoints = bonusPoints + racialBonus; + + _skillsWindow = new SkillsWindow(jobProto.ID, currentSkills, defaultSkills, totalPoints); + _skillsWindow.OnSkillChanged += (jobId, skillKey, newLevel) => + { + Profile = Profile.WithSkill(jobId, skillKey, newLevel); + SetDirty(); + }; + + _skillsWindow.OnClose += () => + { + JobOverride = null; + ReloadPreview(); + }; + + _skillsWindow.OpenCenteredLeft(); + JobOverride = jobProto; + ReloadPreview(); + } + + private int CalculateRacialBonus(string species, int age) + { + var bonus = 0; + foreach (var racialBonusProto in _prototypeManager.EnumeratePrototypes()) + { + if (racialBonusProto.Species != species) + continue; + + bonus = racialBonusProto.GetBonusForAge(age); + break; + } + + return bonus; + } + // WL-Skills-end + private void OnFlavorTextChange(string content) { if (Profile is null) @@ -1604,6 +1699,8 @@ namespace Content.Client.Lobby.UI _loadoutWindow?.Dispose(); _loadoutWindow = null; + _skillsWindow?.Dispose(); // WL-Skills + _skillsWindow = null; // WL-Skills } protected override void EnteredTree() @@ -1623,6 +1720,7 @@ namespace Content.Client.Lobby.UI { Profile = Profile?.WithAge(newAge); ReloadPreview(); + RefreshSkills(); // WL-Skills } // WL-Height-Start @@ -1679,6 +1777,7 @@ namespace Content.Client.Lobby.UI RefreshJobs(); // In case there's species restrictions for loadouts RefreshLoadouts(); + RefreshSkills(); // WL-Skills UpdateSexControls(); // update sex for new species UpdateSpeciesGuidebookIcon(); ReloadPreview(); diff --git a/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs b/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs index 6fe0243c63..cbba74e20f 100644 --- a/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs +++ b/Content.Client/UserInterface/Systems/Character/CharacterUIController.cs @@ -1,3 +1,5 @@ +using System.Linq; +using Content.Client._WL.Skills.Ui; // WL-Skills using Content.Client._WL.DynamicText.UI; // WL-Chages using Content.Client.CharacterInfo; using Content.Client.Gameplay; @@ -6,6 +8,8 @@ using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Systems.Character.Controls; using Content.Client.UserInterface.Systems.Character.Windows; using Content.Client.UserInterface.Systems.Objectives.Controls; +using Content.Shared._WL.Skills; // WL-Skills +using Content.Shared._WL.Skills.Components; // WL-Skills using Content.Shared.Input; using Content.Shared.Mind; using Content.Shared.Mind.Components; @@ -19,7 +23,6 @@ using Robust.Client.UserInterface.Controls; using Robust.Shared.Input.Binding; using Robust.Shared.Prototypes; using Robust.Shared.Utility; -using System.Linq; using static Content.Client.CharacterInfo.CharacterInfoSystem; using static Robust.Client.UserInterface.Controls.BaseButton; @@ -31,6 +34,7 @@ public sealed class CharacterUIController : UIController, IOnStateEntered UIManager.GetActiveUIWidgetOrNull()?.CharacterButton; public void OnStateEntered(GameplayState state) @@ -60,6 +65,10 @@ public sealed class CharacterUIController : UIController, IOnStateEntered(_player.LocalEntity)) + _window.SkillsButton.Disabled = false; + _window.DynamicTextButton.OnPressed += _ => { _dynamicText.OpenWindow(); @@ -267,4 +276,57 @@ public sealed class CharacterUIController : UIController, IOnStateEntered(); + if (!_ent.TryGetComponent(entity, out var skillsComp)) + return; + + var jobId = skillsComp.CurrentJob; + var defaultSkills = skillsSystem.GetDefaultSkillsForJob(jobId); + var totalPoints = skillsSystem.GetTotalPoints(entity, jobId, skillsComp); + var currentSkills = skillsComp.Skills.ToDictionary( + kvp => (byte)kvp.Key, + kvp => kvp.Value + ); + + var skillsWindow = new SkillsWindow( + jobId ?? "unknown", + currentSkills, + defaultSkills, + totalPoints, + true + ); + + _skillsWindow = skillsWindow; + + skillsWindow.OnSkillChanged += (changedJobId, skillKey, newLevel) => + { + if (_player.LocalEntity is not { } localEntity) + return; + + var skillType = (SkillType)skillKey; + var ev = new SelectSkillPressedEvent(_ent.GetNetEntity(localEntity), skillType, newLevel, changedJobId); + _entityNetworkManager.SendSystemNetworkMessage(ev); + }; + + skillsWindow.OpenCentered(); + } + // WL-Skills-end } diff --git a/Content.Client/UserInterface/Systems/Character/Windows/CharacterWindow.xaml b/Content.Client/UserInterface/Systems/Character/Windows/CharacterWindow.xaml index 0c75999846..a67de47395 100644 --- a/Content.Client/UserInterface/Systems/Character/Windows/CharacterWindow.xaml +++ b/Content.Client/UserInterface/Systems/Character/Windows/CharacterWindow.xaml @@ -18,6 +18,9 @@ + +