diff --git a/Content.Client/UserInterface/HumanoidProfileEditor.cs b/Content.Client/UserInterface/HumanoidProfileEditor.cs index baacc607a9..3ab41fa719 100644 --- a/Content.Client/UserInterface/HumanoidProfileEditor.cs +++ b/Content.Client/UserInterface/HumanoidProfileEditor.cs @@ -43,8 +43,11 @@ namespace Content.Client.UserInterface private readonly Button _sexClassifiedButton; private readonly HairStylePicker _hairPicker; private readonly FacialHairStylePicker _facialHairPicker; + private readonly List _jobPriorities; private readonly OptionButton _preferenceUnavailableButton; + private readonly Dictionary _jobCategories; + private readonly List _antagPreferences; private readonly IEntity _previewDummy; @@ -313,31 +316,79 @@ namespace Content.Client.UserInterface }; _jobPriorities = new List(); + _jobCategories = new Dictionary(); + + var firstCategory = true; foreach (var job in prototypeManager.EnumeratePrototypes().OrderBy(j => j.Name)) { - var selector = new JobPrioritySelector(job); - jobList.AddChild(selector); - _jobPriorities.Add(selector); - - selector.PriorityChanged += priority => + foreach (var department in job.Departments) { - Profile = Profile.WithJobPriority(job.ID, priority); - IsDirty = true; - - if (priority == JobPriority.High) + if (!_jobCategories.TryGetValue(department, out var category)) { - // Lower any other high priorities to medium. + category = new VBoxContainer + { + Name = department, + ToolTip = Loc.GetString("Jobs in the {0} department", department) + }; + + if (firstCategory) + { + firstCategory = false; + } + else + { + category.AddChild(new Control + { + CustomMinimumSize = new Vector2(0, 23), + }); + } + + category.AddChild(new PanelContainer + { + PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#464966")}, + Children = + { + new Label + { + Text = Loc.GetString("{0} jobs", department) + } + } + }); + + _jobCategories[department] = category; + jobList.AddChild(category); + } + + var selector = new JobPrioritySelector(job); + category.AddChild(selector); + _jobPriorities.Add(selector); + + selector.PriorityChanged += priority => + { + Profile = Profile.WithJobPriority(job.ID, priority); + IsDirty = true; + foreach (var jobSelector in _jobPriorities) { - if (jobSelector != selector && jobSelector.Priority == JobPriority.High) + // Sync other selectors with the same job in case of multiple department jobs + if (jobSelector.Job == selector.Job) { - jobSelector.Priority = JobPriority.Medium; - Profile = Profile.WithJobPriority(jobSelector.Job.ID, JobPriority.Medium); + jobSelector.Priority = priority; + } + + // Lower any other high priorities to medium. + if (priority == JobPriority.High) + { + if (jobSelector.Job != selector.Job && jobSelector.Priority == JobPriority.High) + { + jobSelector.Priority = JobPriority.Medium; + Profile = Profile.WithJobPriority(jobSelector.Job.ID, JobPriority.Medium); + } } } - } - }; + }; + } } } @@ -452,7 +503,7 @@ namespace Content.Client.UserInterface SizeFlagsHorizontal = SizeFlags.FillExpand, }; hbox.AddChild(vBox); - + #region Preview _previewDummy = entityManager.SpawnEntity("HumanMob_Dummy", MapCoordinates.Nullspace); @@ -495,7 +546,7 @@ namespace Content.Client.UserInterface box.AddChild(_previewSpriteSide); #endregion - + #endregion if (preferencesManager.ServerDataLoaded) diff --git a/Content.Client/UserInterface/LateJoinGui.cs b/Content.Client/UserInterface/LateJoinGui.cs index 13eed2a6ab..c1449ceb11 100644 --- a/Content.Client/UserInterface/LateJoinGui.cs +++ b/Content.Client/UserInterface/LateJoinGui.cs @@ -4,6 +4,8 @@ using System.Linq; using Content.Client.Interfaces; using Content.Shared.Roles; using Robust.Client.Console; +using Robust.Client.Graphics.Drawing; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.Utility; @@ -27,6 +29,7 @@ namespace Content.Client.UserInterface public event Action SelectedId; private readonly Dictionary _jobButtons = new(); + private readonly Dictionary _jobCategories = new(); public LateJoinGui() { @@ -37,65 +40,108 @@ namespace Content.Client.UserInterface var jobList = new VBoxContainer(); var vBox = new VBoxContainer { - Children = + Children = + { + new ScrollContainer { - new ScrollContainer + SizeFlagsVertical = SizeFlags.FillExpand, + Children = { - SizeFlagsVertical = SizeFlags.FillExpand, - Children = - { - jobList - } + jobList } } - }; + } + }; + Contents.AddChild(vBox); + var firstCategory = true; + foreach (var job in _prototypeManager.EnumeratePrototypes().OrderBy(j => j.Name)) { - var jobButton = new JobButton + foreach (var department in job.Departments) { - JobId = job.ID - }; + if (!_jobCategories.TryGetValue(department, out var category)) + { + category = new VBoxContainer + { + Name = department, + ToolTip = Loc.GetString("Jobs in the {0} department", department) + }; - var jobSelector = new HBoxContainer - { - SizeFlagsHorizontal = SizeFlags.FillExpand - }; + if (firstCategory) + { + firstCategory = false; + } + else + { + category.AddChild(new Control + { + CustomMinimumSize = new Vector2(0, 23), + }); + } - var icon = new TextureRect - { - TextureScale = (2, 2), - Stretch = TextureRect.StretchMode.KeepCentered - }; + category.AddChild(new PanelContainer + { + PanelOverride = new StyleBoxFlat {BackgroundColor = Color.FromHex("#464966")}, + Children = + { + new Label + { + Text = Loc.GetString("{0} jobs", department) + } + } + }); - if (job.Icon != null) - { - var specifier = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Interface/Misc/job_icons.rsi"), job.Icon); - icon.Texture = specifier.Frame0(); + _jobCategories[department] = category; + jobList.AddChild(category); + } + + var jobButton = new JobButton + { + JobId = job.ID + }; + + var jobSelector = new HBoxContainer + { + SizeFlagsHorizontal = SizeFlags.FillExpand + }; + + var icon = new TextureRect + { + TextureScale = (2, 2), + Stretch = TextureRect.StretchMode.KeepCentered + }; + + if (job.Icon != null) + { + var specifier = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Interface/Misc/job_icons.rsi"), job.Icon); + icon.Texture = specifier.Frame0(); + } + + jobSelector.AddChild(icon); + + var jobLabel = new Label + { + Text = job.Name + }; + + jobSelector.AddChild(jobLabel); + jobButton.AddChild(jobSelector); + category.AddChild(jobButton); + + jobButton.OnPressed += _ => + { + SelectedId?.Invoke(jobButton.JobId); + }; + + if (!_gameTicker.JobsAvailable.Contains(job.ID)) + { + jobButton.Disabled = true; + } + + _jobButtons[job.ID] = jobButton; } - jobSelector.AddChild(icon); - - var jobLabel = new Label - { - Text = job.Name - }; - - jobSelector.AddChild(jobLabel); - - jobButton.AddChild(jobSelector); - jobList.AddChild(jobButton); - jobButton.OnPressed += args => - { - SelectedId?.Invoke(jobButton.JobId); - }; - - if (!_gameTicker.JobsAvailable.Contains(job.ID)) - { - jobButton.Disabled = true; - } - - _jobButtons[job.ID] = jobButton; } SelectedId += jobId => @@ -108,11 +154,6 @@ namespace Content.Client.UserInterface _gameTicker.LobbyJobsAvailableUpdated += JobsAvailableUpdated; } - public string ReturnId() - { - return SelectedId.ToString(); - } - private void JobsAvailableUpdated(IReadOnlyList jobs) { foreach (var (id, button) in _jobButtons) @@ -129,6 +170,7 @@ namespace Content.Client.UserInterface { _gameTicker.LobbyJobsAvailableUpdated -= JobsAvailableUpdated; _jobButtons.Clear(); + _jobCategories.Clear(); } } } diff --git a/Content.Shared/Roles/JobPrototype.cs b/Content.Shared/Roles/JobPrototype.cs index 63aea01f21..08bf12e340 100644 --- a/Content.Shared/Roles/JobPrototype.cs +++ b/Content.Shared/Roles/JobPrototype.cs @@ -42,7 +42,7 @@ namespace Content.Shared.Roles public JobSpecial Special { get; private set; } - public IReadOnlyCollection Department { get; private set; } + public IReadOnlyCollection Departments { get; private set; } public IReadOnlyCollection Access { get; private set; } public void LoadFrom(YamlMappingNode mapping) @@ -51,7 +51,7 @@ namespace Content.Shared.Roles ID = srz.ReadDataField("id"); Name = Loc.GetString(srz.ReadDataField("name")); StartingGear = srz.ReadDataField("startingGear"); - Department = srz.ReadDataField>("department"); + Departments = srz.ReadDataField>("departments"); TotalPositions = srz.ReadDataField("positions"); srz.DataField(this, p => p.SpawnPositions, "spawnPositions", TotalPositions); diff --git a/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml b/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml index 80b764c620..5f9d492902 100644 --- a/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml +++ b/Resources/Prototypes/Roles/Jobs/Cargo/cargo_technician.yml @@ -4,7 +4,7 @@ positions: 2 spawnPositions: 1 startingGear: CargoTechGear - department: + departments: - Cargo icon: "CargoTechnician" access: diff --git a/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml b/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml index c733b02d29..9c2ac826f0 100644 --- a/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml +++ b/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml @@ -4,7 +4,7 @@ positions: 1 spawnPositions: 1 startingGear: QuartermasterGear - department: + departments: - Cargo icon: "QuarterMaster" access: diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/assistant.yml b/Resources/Prototypes/Roles/Jobs/Civilian/assistant.yml index 3df6e98b9d..07a5162534 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/assistant.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/assistant.yml @@ -3,7 +3,7 @@ name: "assistant" positions: -1 # Treated as infinite. startingGear: AssistantGear - department: + departments: - Civilian icon: "Assistant" access: diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/bartender.yml b/Resources/Prototypes/Roles/Jobs/Civilian/bartender.yml index c40b5162c0..04d367ae25 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/bartender.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/bartender.yml @@ -3,7 +3,7 @@ name: "bartender" positions: 1 startingGear: BartenderGear - department: + departments: - Civilian icon: "Bartender" access: diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/botanist.yml b/Resources/Prototypes/Roles/Jobs/Civilian/botanist.yml index 7d20c703bc..94c8d0ab37 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/botanist.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/botanist.yml @@ -4,7 +4,7 @@ positions: 2 spawnPositions: 2 startingGear: BotanistGear - department: + departments: - Civilian icon: "Botanist" access: diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml index bb67525c43..3f4736aa89 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml @@ -3,7 +3,7 @@ # name: "chaplain" # positions: 1 # startingGear: ChaplainGear -# department: +# departments: # - Civilian # icon: "Chaplain" # access: diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/chef.yml b/Resources/Prototypes/Roles/Jobs/Civilian/chef.yml index 0c06658301..bc62b37288 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/chef.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/chef.yml @@ -3,7 +3,7 @@ name: "chef" positions: 1 startingGear: ChefGear - department: + departments: - Civilian icon: "Chef" access: diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml b/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml index c709f8ff76..15bdd058e8 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/clown.yml @@ -3,7 +3,7 @@ name: "clown" positions: 1 startingGear: ClownGear - department: + departments: - Civilian icon: "Clown" access: diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/janitor.yml b/Resources/Prototypes/Roles/Jobs/Civilian/janitor.yml index 8701597c8a..5b5f6824d5 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/janitor.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/janitor.yml @@ -3,7 +3,7 @@ name: "janitor" positions: 1 startingGear: JanitorGear - department: + departments: - Civilian icon: "Janitor" access: diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml b/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml index 4e0dd184d6..c2053ca915 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/mime.yml @@ -3,7 +3,7 @@ name: "mime" positions: 1 startingGear: MimeGear - department: + departments: - Civilian icon: "Mime" access: diff --git a/Resources/Prototypes/Roles/Jobs/Command/captain.yml b/Resources/Prototypes/Roles/Jobs/Command/captain.yml index 849e517d00..2000efb720 100644 --- a/Resources/Prototypes/Roles/Jobs/Command/captain.yml +++ b/Resources/Prototypes/Roles/Jobs/Command/captain.yml @@ -4,7 +4,7 @@ head: true positions: 1 startingGear: CaptainGear - department: + departments: - Command icon: "Captain" access: diff --git a/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml b/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml index 716e0ded13..ec51cd6a54 100644 --- a/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml +++ b/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml @@ -4,7 +4,7 @@ head: true positions: 1 startingGear: HoPGear - department: + departments: - Command - Civilian icon: "HeadOfPersonnel" diff --git a/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml b/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml index 7ebf03b75b..fea846c5a4 100644 --- a/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml +++ b/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml @@ -4,7 +4,7 @@ head: true positions: 1 startingGear: ChiefEngineerGear - department: + departments: - Command - Engineering icon: "ChiefEngineer" diff --git a/Resources/Prototypes/Roles/Jobs/Engineering/station_engineer.yml b/Resources/Prototypes/Roles/Jobs/Engineering/station_engineer.yml index 0e3e49807c..d99ed5d635 100644 --- a/Resources/Prototypes/Roles/Jobs/Engineering/station_engineer.yml +++ b/Resources/Prototypes/Roles/Jobs/Engineering/station_engineer.yml @@ -4,7 +4,7 @@ positions: 3 spawnPositions: 2 startingGear: StationEngineerGear - department: + departments: - Engineering icon: "StationEngineer" access: diff --git a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml index 7eb6037b6c..570a0c0c88 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml @@ -6,7 +6,7 @@ head: true positions: 1 startingGear: CMOGear - department: + departments: - Command - Medical icon: "ChiefMedicalOfficer" diff --git a/Resources/Prototypes/Roles/Jobs/Medical/medical_doctor.yml b/Resources/Prototypes/Roles/Jobs/Medical/medical_doctor.yml index 8e4e3b6080..55b09224aa 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/medical_doctor.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/medical_doctor.yml @@ -4,7 +4,7 @@ positions: 3 spawnPositions: 2 startingGear: DoctorGear - department: + departments: - Medical icon: "MedicalDoctor" access: diff --git a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml index 9034e9a6b5..791d95ab78 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml @@ -4,7 +4,7 @@ head: true positions: 1 startingGear: ResearchDirectorGear - department: + departments: - Command - Science icon: "ResearchDirector" diff --git a/Resources/Prototypes/Roles/Jobs/Science/scientist.yml b/Resources/Prototypes/Roles/Jobs/Science/scientist.yml index 72ae62d7c8..dd32adf4fb 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/scientist.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/scientist.yml @@ -4,7 +4,7 @@ positions: 3 spawnPositions: 2 startingGear: ScientistGear - department: + departments: - Science icon: "Scientist" access: diff --git a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml index 0e24f9ebdb..bc170284fa 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml @@ -4,7 +4,7 @@ head: true positions: 1 startingGear: HoSGear - department: + departments: - Command - Security icon: "HeadOfSecurity" diff --git a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml index 3bd87c2371..2cb0c2efdf 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/security_officer.yml @@ -4,7 +4,7 @@ positions: 3 spawnPositions: 2 startingGear: SecurityOfficerGear - department: + departments: - Security icon: "SecurityOfficer" access: diff --git a/Resources/Prototypes/Roles/Jobs/Security/warden.yml b/Resources/Prototypes/Roles/Jobs/Security/warden.yml index d659f558ea..5780ffd4d7 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/warden.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/warden.yml @@ -4,7 +4,7 @@ positions: 1 spawnPositions: 1 startingGear: WardenGear - department: + departments: - Security icon: "Warden" access: