| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500 |
- using System.Linq;
- using Content.Client.Guidebook;
- using Content.Client.Humanoid;
- using Content.Client.Inventory;
- using Content.Client.Lobby.UI;
- using Content.Client.Players.PlayTimeTracking;
- using Content.Client.Station;
- using Content.Shared.CCVar;
- using Content.Shared.Clothing;
- using Content.Shared.GameTicking;
- using Content.Shared.Humanoid;
- using Content.Shared.Humanoid.Markings;
- using Content.Shared.Humanoid.Prototypes;
- using Content.Shared.Preferences;
- using Content.Shared.Preferences.Loadouts;
- using Content.Shared.Roles;
- using Content.Shared.Traits;
- using Robust.Client.Player;
- using Robust.Client.ResourceManagement;
- using Robust.Client.State;
- using Robust.Client.UserInterface;
- using Robust.Client.UserInterface.Controllers;
- using Robust.Shared.Configuration;
- using Robust.Shared.Map;
- using Robust.Shared.Prototypes;
- using Robust.Shared.Utility;
- namespace Content.Client.Lobby;
- public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState>, IOnStateExited<LobbyState>
- {
- [Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
- [Dependency] private readonly IConfigurationManager _configurationManager = default!;
- [Dependency] private readonly IFileDialogManager _dialogManager = default!;
- [Dependency] private readonly ILogManager _logManager = default!;
- [Dependency] private readonly IPlayerManager _playerManager = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
- [Dependency] private readonly IResourceCache _resourceCache = default!;
- [Dependency] private readonly IStateManager _stateManager = default!;
- [Dependency] private readonly JobRequirementsManager _requirements = default!;
- [Dependency] private readonly MarkingManager _markings = default!;
- [UISystemDependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
- [UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
- [UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
- [UISystemDependency] private readonly GuidebookSystem _guide = default!;
- private CharacterSetupGui? _characterSetup;
- private HumanoidProfileEditor? _profileEditor;
- private CharacterSetupGuiSavePanel? _savePanel;
- /// <summary>
- /// This is the characher preview panel in the chat. This should only update if their character updates.
- /// </summary>
- private LobbyCharacterPreviewPanel? PreviewPanel => GetLobbyPreview();
- /// <summary>
- /// This is the modified profile currently being edited.
- /// </summary>
- private HumanoidCharacterProfile? EditedProfile => _profileEditor?.Profile;
- private int? EditedSlot => _profileEditor?.CharacterSlot;
- public override void Initialize()
- {
- base.Initialize();
- _prototypeManager.PrototypesReloaded += OnProtoReload;
- _preferencesManager.OnServerDataLoaded += PreferencesDataLoaded;
- _requirements.Updated += OnRequirementsUpdated;
- _configurationManager.OnValueChanged(CCVars.FlavorText, args =>
- {
- _profileEditor?.RefreshFlavorText();
- });
- _configurationManager.OnValueChanged(CCVars.GameRoleTimers, _ => RefreshProfileEditor());
- _configurationManager.OnValueChanged(CCVars.GameRoleWhitelist, _ => RefreshProfileEditor());
- }
- private LobbyCharacterPreviewPanel? GetLobbyPreview()
- {
- if (_stateManager.CurrentState is LobbyState lobby)
- {
- return lobby.Lobby?.CharacterPreview;
- }
- return null;
- }
- private void OnRequirementsUpdated()
- {
- if (_profileEditor != null)
- {
- _profileEditor.RefreshAntags();
- _profileEditor.RefreshJobs();
- }
- }
- private void OnProtoReload(PrototypesReloadedEventArgs obj)
- {
- if (_profileEditor != null)
- {
- if (obj.WasModified<AntagPrototype>())
- {
- _profileEditor.RefreshAntags();
- }
- if (obj.WasModified<JobPrototype>() ||
- obj.WasModified<DepartmentPrototype>())
- {
- _profileEditor.RefreshJobs();
- }
- if (obj.WasModified<LoadoutPrototype>() ||
- obj.WasModified<LoadoutGroupPrototype>() ||
- obj.WasModified<RoleLoadoutPrototype>())
- {
- _profileEditor.RefreshLoadouts();
- }
- if (obj.WasModified<SpeciesPrototype>())
- {
- _profileEditor.RefreshSpecies();
- }
- if (obj.WasModified<TraitPrototype>())
- {
- _profileEditor.RefreshTraits();
- }
- }
- }
- private void PreferencesDataLoaded()
- {
- PreviewPanel?.SetLoaded(true);
- if (_stateManager.CurrentState is not LobbyState)
- return;
- ReloadCharacterSetup();
- }
- public void OnStateEntered(LobbyState state)
- {
- PreviewPanel?.SetLoaded(_preferencesManager.ServerDataLoaded);
- ReloadCharacterSetup();
- }
- public void OnStateExited(LobbyState state)
- {
- PreviewPanel?.SetLoaded(false);
- _profileEditor?.Dispose();
- _characterSetup?.Dispose();
- _characterSetup = null;
- _profileEditor = null;
- }
- /// <summary>
- /// Reloads every single character setup control.
- /// </summary>
- public void ReloadCharacterSetup()
- {
- RefreshLobbyPreview();
- var (characterGui, profileEditor) = EnsureGui();
- characterGui.ReloadCharacterPickers();
- profileEditor.SetProfile(
- (HumanoidCharacterProfile?) _preferencesManager.Preferences?.SelectedCharacter,
- _preferencesManager.Preferences?.SelectedCharacterIndex);
- }
- /// <summary>
- /// Refreshes the character preview in the lobby chat.
- /// </summary>
- private void RefreshLobbyPreview()
- {
- if (PreviewPanel == null)
- return;
- // Get selected character, load it, then set it
- var character = _preferencesManager.Preferences?.SelectedCharacter;
- if (character is not HumanoidCharacterProfile humanoid)
- {
- PreviewPanel.SetSprite(EntityUid.Invalid);
- PreviewPanel.SetSummaryText(string.Empty);
- return;
- }
- var dummy = LoadProfileEntity(humanoid, null, true);
- PreviewPanel.SetSprite(dummy);
- PreviewPanel.SetSummaryText(humanoid.Summary);
- }
- private void RefreshProfileEditor()
- {
- _profileEditor?.RefreshAntags();
- _profileEditor?.RefreshJobs();
- _profileEditor?.RefreshLoadouts();
- }
- private void SaveProfile()
- {
- DebugTools.Assert(EditedProfile != null);
- if (EditedProfile == null || EditedSlot == null)
- return;
- var selected = _preferencesManager.Preferences?.SelectedCharacterIndex;
- if (selected == null)
- return;
- _preferencesManager.UpdateCharacter(EditedProfile, EditedSlot.Value);
- ReloadCharacterSetup();
- }
- private void CloseProfileEditor()
- {
- if (_profileEditor == null)
- return;
- _profileEditor.SetProfile(null, null);
- _profileEditor.Visible = false;
- if (_stateManager.CurrentState is LobbyState lobbyGui)
- {
- lobbyGui.SwitchState(LobbyGui.LobbyGuiState.Default);
- }
- }
- private void OpenSavePanel()
- {
- if (_savePanel is { IsOpen: true })
- return;
- _savePanel = new CharacterSetupGuiSavePanel();
- _savePanel.SaveButton.OnPressed += _ =>
- {
- SaveProfile();
- _savePanel.Close();
- CloseProfileEditor();
- };
- _savePanel.NoSaveButton.OnPressed += _ =>
- {
- _savePanel.Close();
- CloseProfileEditor();
- };
- _savePanel.OpenCentered();
- }
- private (CharacterSetupGui, HumanoidProfileEditor) EnsureGui()
- {
- if (_characterSetup != null && _profileEditor != null)
- {
- _characterSetup.Visible = true;
- _profileEditor.Visible = true;
- return (_characterSetup, _profileEditor);
- }
- _profileEditor = new HumanoidProfileEditor(
- _preferencesManager,
- _configurationManager,
- EntityManager,
- _dialogManager,
- _logManager,
- _playerManager,
- _prototypeManager,
- _resourceCache,
- _requirements,
- _markings);
- _profileEditor.OnOpenGuidebook += _guide.OpenHelp;
- _characterSetup = new CharacterSetupGui(_profileEditor);
- _characterSetup.CloseButton.OnPressed += _ =>
- {
- // Open the save panel if we have unsaved changes.
- if (_profileEditor.Profile != null && _profileEditor.IsDirty)
- {
- OpenSavePanel();
- return;
- }
- // Reset sliders etc.
- CloseProfileEditor();
- };
- _profileEditor.Save += SaveProfile;
- _characterSetup.SelectCharacter += args =>
- {
- _preferencesManager.SelectCharacter(args);
- ReloadCharacterSetup();
- };
- _characterSetup.DeleteCharacter += args =>
- {
- _preferencesManager.DeleteCharacter(args);
- // Reload everything
- if (EditedSlot == args)
- {
- ReloadCharacterSetup();
- }
- else
- {
- // Only need to reload character pickers
- _characterSetup?.ReloadCharacterPickers();
- }
- };
- if (_stateManager.CurrentState is LobbyState lobby)
- {
- lobby.Lobby?.CharacterSetupState.AddChild(_characterSetup);
- }
- return (_characterSetup, _profileEditor);
- }
- #region Helpers
- /// <summary>
- /// Applies the highest priority job's clothes to the dummy.
- /// </summary>
- public void GiveDummyJobClothesLoadout(EntityUid dummy, JobPrototype? jobProto, HumanoidCharacterProfile profile)
- {
- var job = jobProto ?? GetPreferredJob(profile);
- GiveDummyJobClothes(dummy, profile, job);
- if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
- {
- var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), _playerManager.LocalSession, profile.Species, EntityManager, _prototypeManager);
- GiveDummyLoadout(dummy, loadout);
- }
- }
- /// <summary>
- /// Gets the highest priority job for the profile.
- /// </summary>
- public JobPrototype GetPreferredJob(HumanoidCharacterProfile profile)
- {
- var highPriorityJob = profile.JobPriorities.FirstOrDefault(p => p.Value == JobPriority.High).Key;
- // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?)
- return _prototypeManager.Index<JobPrototype>(highPriorityJob.Id ?? SharedGameTicker.FallbackOverflowJob);
- }
- public void GiveDummyLoadout(EntityUid uid, RoleLoadout? roleLoadout)
- {
- if (roleLoadout == null)
- return;
- foreach (var group in roleLoadout.SelectedLoadouts.Values)
- {
- foreach (var loadout in group)
- {
- if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
- continue;
- _spawn.EquipStartingGear(uid, loadoutProto);
- }
- }
- }
- /// <summary>
- /// Applies the specified job's clothes to the dummy.
- /// </summary>
- public void GiveDummyJobClothes(EntityUid dummy, HumanoidCharacterProfile profile, JobPrototype job)
- {
- if (!_inventory.TryGetSlots(dummy, out var slots))
- return;
- // Apply loadout
- if (profile.Loadouts.TryGetValue(job.ID, out var jobLoadout))
- {
- foreach (var loadouts in jobLoadout.SelectedLoadouts.Values)
- {
- foreach (var loadout in loadouts)
- {
- if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
- continue;
- // TODO: Need some way to apply starting gear to an entity and replace existing stuff coz holy fucking shit dude.
- foreach (var slot in slots)
- {
- // Try startinggear first
- if (_prototypeManager.TryIndex(loadoutProto.StartingGear, out var loadoutGear))
- {
- var itemType = ((IEquipmentLoadout) loadoutGear).GetGear(slot.Name);
- if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
- {
- EntityManager.DeleteEntity(unequippedItem.Value);
- }
- if (itemType != string.Empty)
- {
- var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
- _inventory.TryEquip(dummy, item, slot.Name, true, true);
- }
- }
- else
- {
- var itemType = ((IEquipmentLoadout) loadoutProto).GetGear(slot.Name);
- if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
- {
- EntityManager.DeleteEntity(unequippedItem.Value);
- }
- if (itemType != string.Empty)
- {
- var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
- _inventory.TryEquip(dummy, item, slot.Name, true, true);
- }
- }
- }
- }
- }
- }
- if (!_prototypeManager.TryIndex(job.StartingGear, out var gear))
- return;
- foreach (var slot in slots)
- {
- var itemType = ((IEquipmentLoadout) gear).GetGear(slot.Name);
- if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
- {
- EntityManager.DeleteEntity(unequippedItem.Value);
- }
- if (itemType != string.Empty)
- {
- var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
- _inventory.TryEquip(dummy, item, slot.Name, true, true);
- }
- }
- }
- /// <summary>
- /// Loads the profile onto a dummy entity.
- /// </summary>
- public EntityUid LoadProfileEntity(HumanoidCharacterProfile? humanoid, JobPrototype? job, bool jobClothes)
- {
- EntityUid dummyEnt;
- EntProtoId? previewEntity = null;
- if (humanoid != null && jobClothes)
- {
- job ??= GetPreferredJob(humanoid);
- previewEntity = job.JobPreviewEntity ?? (EntProtoId?)job?.JobEntity;
- }
- if (previewEntity != null)
- {
- // Special type like borg or AI, do not spawn a human just spawn the entity.
- dummyEnt = EntityManager.SpawnEntity(previewEntity, MapCoordinates.Nullspace);
- return dummyEnt;
- }
- else if (humanoid is not null)
- {
- var dummy = _prototypeManager.Index<SpeciesPrototype>(humanoid.Species).DollPrototype;
- dummyEnt = EntityManager.SpawnEntity(dummy, MapCoordinates.Nullspace);
- }
- else
- {
- dummyEnt = EntityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(SharedHumanoidAppearanceSystem.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
- }
- _humanoid.LoadProfile(dummyEnt, humanoid);
- if (humanoid != null && jobClothes)
- {
- DebugTools.Assert(job != null);
- GiveDummyJobClothes(dummyEnt, humanoid, job);
- if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
- {
- var loadout = humanoid.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), _playerManager.LocalSession, humanoid.Species, EntityManager, _prototypeManager);
- GiveDummyLoadout(dummyEnt, loadout);
- }
- }
- return dummyEnt;
- }
- #endregion
- }
|