using System.Linq; using Content.Client._NF.LateJoin.Interfaces; using Content.Client._NF.LateJoin.ListItems; using Content.Client.Lobby; using Content.Client.Players.PlayTimeTracking; using Content.Shared._NF.Shipyard.Prototypes; using Content.Shared.GameTicking; using Content.Shared.Preferences; using Robust.Client.AutoGenerated; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.UserInterface.XAML; using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Client._NF.LateJoin.Controls; [GenerateTypedNameReferences] public sealed partial class CrewPickerControl : PickerControl { [Dependency] private readonly ILocalizationManager _loc = default!; [Dependency] private readonly IEntitySystemManager _entitySystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly JobRequirementsManager _jobReqs = default!; [Dependency] private readonly IClientPreferencesManager _preferencesManager = default!; private readonly SpriteSystem _spriteSystem; private Dictionary _lobbyJobs = new(); private CrewListItem.ViewState? _lastSelectedStation; public Action? OnJobJoined; private bool _hideJoblessShips = true; public CrewPickerControl() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); _spriteSystem = _entitySystem.GetEntitySystem(); HideJoblessShipsCheckbox.OnPressed += _ => ToggleHideJoblessShips(); HideJoblessShipsCheckbox.Pressed = _hideJoblessShips; } public override void UpdateUi(IReadOnlyDictionary obj) { _lobbyJobs = new Dictionary(obj); StationItemList.RemoveAllChildren(); foreach (var stationViewState in BuildStationViewStateList(_lobbyJobs)) { var item = new CrewListItem(stationViewState); item.StationButton.OnPressed += _ => OnStationPressed(stationViewState); StationItemList.AddChild(item); // Update displayed information if we get a new state update. if (_lastSelectedStation != null && stationViewState.StationEntity == _lastSelectedStation.StationEntity) _lastSelectedStation = stationViewState; } // Build station jobs, the right section of the screen. StationJobItemList.RemoveAllChildren(); if (_lastSelectedStation != null && obj.TryGetValue(_lastSelectedStation.StationEntity, out var stationInfo)) { foreach (var jobViewState in BuildJobViewStateList(stationInfo)) { var item = new JobListItem(jobViewState); item.OnPressed += _ => { OnJobJoined?.Invoke(_lastSelectedStation.StationEntity, jobViewState.JobId); }; StationJobItemList.AddChild(item); } } StationName.Text = _lastSelectedStation?.StationName ?? ""; StationDescription.SetMessage(BuildDescription(_lastSelectedStation)); } private void ToggleHideJoblessShips() { _hideJoblessShips = HideJoblessShipsCheckbox.Pressed; UpdateUi(_lobbyJobs); } private void OnStationPressed(CrewListItem.ViewState stationItemViewState) { _lastSelectedStation = stationItemViewState; UpdateUi(_lobbyJobs); } private List BuildJobViewStateList(StationJobInformation jobInformation) { var viewStateList = new List(); foreach (var (jobPrototype, jobCount) in jobInformation.JobsAvailable) { if (_preferencesManager.Preferences?.SelectedCharacter is not HumanoidCharacterProfile profile) { continue; } var prototype = _prototypeManager.Index(jobPrototype); var jobName = prototype.LocalizedName + jobCount.WrapJobCountInParentheses(); Texture? texture = null; if (_prototypeManager.TryIndex(prototype.Icon, out var jobIcon)) { texture = _spriteSystem.Frame0(jobIcon.Icon); } var buttonTooltip = ""; if (!_jobReqs.IsAllowed(prototype, profile, out var denyReason)) { buttonTooltip = denyReason.ToString(); } var isButtonDisabled = jobCount == 0 || !_jobReqs.IsAllowed(prototype, profile, out _); var viewState = new JobListItem.ViewState( jobId: jobPrototype, jobName: jobName, toolTip: buttonTooltip, disabled: isButtonDisabled, jobIcon: texture ); viewStateList.Add(viewState); } return viewStateList; } /** * Convert some raw dictionary data to a view state model that is more readable. * * @param obj Dictionary of station entities to job prototypes. * @param stationNames Dictionary of station entities to station names. * @return List of view states for each station. */ private List BuildStationViewStateList( IReadOnlyDictionary obj) { var stationList = obj.Where(kvp => !kvp.Value.IsLateJoinStation).ToDictionary(kvp => kvp.Key, kvp => kvp.Value); var viewStateList = new List(); foreach (var (stationEntity, stationJobInformation) in stationList) { if (_hideJoblessShips && !stationJobInformation.JobsAvailable.Any(x => x.Value != 0)) { continue; } var viewState = new CrewListItem.ViewState( stationEntity, stationJobInformation.GetStationNameWithJobCount(), "", // No subtext currently. stationJobInformation.VesselDisplayInformation?.VesselAdvertisement != null ? _loc.GetString(stationJobInformation.VesselDisplayInformation.VesselAdvertisement) : "", _lastSelectedStation?.StationEntity == stationEntity, "", // No icons currently. stationJobInformation?.VesselDisplayInformation?.Vessel ); // Always select the first station in the list if none is selected yet. // This is because otherwise the right side of the screen would then be a blank space. if (_lastSelectedStation == null) { _lastSelectedStation = viewState; viewState.Selected = true; } viewStateList.Add(viewState); } // Sort 0 to the end of the list in the order it is in the dictionary. // Sort 1 first, 2 second, etc. return viewStateList.ToList(); } private FormattedMessage BuildDescription(CrewListItem.ViewState? state) { FormattedMessage msg = new(); if (state == null) return msg; if (_prototypeManager.TryIndex(state.Vessel, out var proto)) { msg.AddMarkupPermissive(Loc.GetString("frontier-lobby-crew-vessel", ("name", proto.Name))); msg.PushNewline(); if (proto.Classes.Count > 0) { msg.AddMarkupPermissive(Loc.GetString("frontier-lobby-crew-class-prefix")); // Assuming a comma-separated string is fine var comma = false; foreach (var protoClass in proto.Classes) { if (comma) msg.AddMarkupPermissive(", "); else comma = true; msg.AddMarkupOrThrow(Loc.GetString($"shipyard-console-class-{protoClass}")); } msg.PushNewline(); } msg.AddMarkupPermissive(Loc.GetString("frontier-lobby-crew-size", ("size", Loc.GetString($"shipyard-console-category-{proto.Category}")))); msg.PushNewline(); msg.PushNewline(); } if (state.StationDescription != null) { msg.AddMarkupPermissive(Loc.GetString("frontier-lobby-crew-class-advertisement")); msg.PushNewline(); msg.AddMarkupPermissive(state.StationDescription); } return msg; } }