Lobby refactor + species loadouts support (#27576)
* Vox stuff * Species loadouts and lobby refactor The control flow for lobby is all over the shop so I pulled it all up from the individual controls so now they handle the bare minimum required and LobbyUIController handles the rest. * a * Bulk changes * a * weh * Character import / export * finalise * woops this stuff too * Also datafield exporting * comments * Review
This commit is contained in:
@@ -10,10 +10,10 @@ using Content.Client.Info;
|
|||||||
using Content.Client.Input;
|
using Content.Client.Input;
|
||||||
using Content.Client.IoC;
|
using Content.Client.IoC;
|
||||||
using Content.Client.Launcher;
|
using Content.Client.Launcher;
|
||||||
|
using Content.Client.Lobby;
|
||||||
using Content.Client.MainMenu;
|
using Content.Client.MainMenu;
|
||||||
using Content.Client.Parallax.Managers;
|
using Content.Client.Parallax.Managers;
|
||||||
using Content.Client.Players.PlayTimeTracking;
|
using Content.Client.Players.PlayTimeTracking;
|
||||||
using Content.Client.Preferences;
|
|
||||||
using Content.Client.Radiation.Overlays;
|
using Content.Client.Radiation.Overlays;
|
||||||
using Content.Client.Replay;
|
using Content.Client.Replay;
|
||||||
using Content.Client.Screenshot;
|
using Content.Client.Screenshot;
|
||||||
|
|||||||
@@ -2,23 +2,20 @@ using Content.Client.Administration.Managers;
|
|||||||
using Content.Client.Changelog;
|
using Content.Client.Changelog;
|
||||||
using Content.Client.Chat.Managers;
|
using Content.Client.Chat.Managers;
|
||||||
using Content.Client.Clickable;
|
using Content.Client.Clickable;
|
||||||
using Content.Client.Options;
|
|
||||||
using Content.Client.Eui;
|
using Content.Client.Eui;
|
||||||
using Content.Client.GhostKick;
|
using Content.Client.GhostKick;
|
||||||
using Content.Client.Info;
|
using Content.Client.Info;
|
||||||
using Content.Client.Launcher;
|
using Content.Client.Launcher;
|
||||||
using Content.Client.Parallax.Managers;
|
using Content.Client.Parallax.Managers;
|
||||||
using Content.Client.Players.PlayTimeTracking;
|
using Content.Client.Players.PlayTimeTracking;
|
||||||
using Content.Client.Preferences;
|
|
||||||
using Content.Client.Screenshot;
|
using Content.Client.Screenshot;
|
||||||
using Content.Client.Fullscreen;
|
using Content.Client.Fullscreen;
|
||||||
using Content.Client.Stylesheets;
|
using Content.Client.Stylesheets;
|
||||||
using Content.Client.Viewport;
|
using Content.Client.Viewport;
|
||||||
using Content.Client.Voting;
|
using Content.Client.Voting;
|
||||||
using Content.Shared.Administration;
|
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Module;
|
|
||||||
using Content.Client.Guidebook;
|
using Content.Client.Guidebook;
|
||||||
|
using Content.Client.Lobby;
|
||||||
using Content.Client.Replay;
|
using Content.Client.Replay;
|
||||||
using Content.Shared.Administration.Managers;
|
using Content.Shared.Administration.Managers;
|
||||||
using Content.Shared.Players.PlayTimeTracking;
|
using Content.Shared.Players.PlayTimeTracking;
|
||||||
|
|||||||
@@ -2,12 +2,10 @@ using System.Linq;
|
|||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
using Robust.Client;
|
using Robust.Client;
|
||||||
using Robust.Client.Player;
|
using Robust.Client.Player;
|
||||||
using Robust.Shared.Configuration;
|
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Client.Preferences
|
namespace Content.Client.Lobby
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Receives <see cref="PlayerPreferences" /> and <see cref="GameSettings" /> from the server during the initial
|
/// Receives <see cref="PlayerPreferences" /> and <see cref="GameSettings" /> from the server during the initial
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using System;
|
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
|
|
||||||
namespace Content.Client.Preferences
|
namespace Content.Client.Lobby
|
||||||
{
|
{
|
||||||
public interface IClientPreferencesManager
|
public interface IClientPreferencesManager
|
||||||
{
|
{
|
||||||
@@ -3,8 +3,6 @@ using Content.Client.GameTicking.Managers;
|
|||||||
using Content.Client.LateJoin;
|
using Content.Client.LateJoin;
|
||||||
using Content.Client.Lobby.UI;
|
using Content.Client.Lobby.UI;
|
||||||
using Content.Client.Message;
|
using Content.Client.Message;
|
||||||
using Content.Client.Preferences;
|
|
||||||
using Content.Client.Preferences.UI;
|
|
||||||
using Content.Client.UserInterface.Systems.Chat;
|
using Content.Client.UserInterface.Systems.Chat;
|
||||||
using Content.Client.Voting;
|
using Content.Client.Voting;
|
||||||
using Robust.Client;
|
using Robust.Client;
|
||||||
@@ -12,8 +10,6 @@ using Robust.Client.Console;
|
|||||||
using Robust.Client.ResourceManagement;
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Shared.Configuration;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
|
||||||
@@ -25,20 +21,15 @@ namespace Content.Client.Lobby
|
|||||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
[Dependency] private readonly IVoteManager _voteManager = default!;
|
[Dependency] private readonly IVoteManager _voteManager = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
|
||||||
|
|
||||||
[ViewVariables] private CharacterSetupGui? _characterSetup;
|
|
||||||
|
|
||||||
private ClientGameTicker _gameTicker = default!;
|
private ClientGameTicker _gameTicker = default!;
|
||||||
private ContentAudioSystem _contentAudioSystem = default!;
|
private ContentAudioSystem _contentAudioSystem = default!;
|
||||||
|
|
||||||
protected override Type? LinkedScreenType { get; } = typeof(LobbyGui);
|
protected override Type? LinkedScreenType { get; } = typeof(LobbyGui);
|
||||||
private LobbyGui? _lobby;
|
public LobbyGui? Lobby;
|
||||||
|
|
||||||
protected override void Startup()
|
protected override void Startup()
|
||||||
{
|
{
|
||||||
@@ -47,45 +38,23 @@ namespace Content.Client.Lobby
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_lobby = (LobbyGui) _userInterfaceManager.ActiveScreen;
|
Lobby = (LobbyGui) _userInterfaceManager.ActiveScreen;
|
||||||
|
|
||||||
var chatController = _userInterfaceManager.GetUIController<ChatUIController>();
|
var chatController = _userInterfaceManager.GetUIController<ChatUIController>();
|
||||||
_gameTicker = _entityManager.System<ClientGameTicker>();
|
_gameTicker = _entityManager.System<ClientGameTicker>();
|
||||||
_contentAudioSystem = _entityManager.System<ContentAudioSystem>();
|
_contentAudioSystem = _entityManager.System<ContentAudioSystem>();
|
||||||
_contentAudioSystem.LobbySoundtrackChanged += UpdateLobbySoundtrackInfo;
|
_contentAudioSystem.LobbySoundtrackChanged += UpdateLobbySoundtrackInfo;
|
||||||
_characterSetup = new CharacterSetupGui(_entityManager, _resourceCache, _preferencesManager,
|
|
||||||
_prototypeManager, _configurationManager);
|
|
||||||
LayoutContainer.SetAnchorPreset(_characterSetup, LayoutContainer.LayoutPreset.Wide);
|
|
||||||
|
|
||||||
_lobby.CharacterSetupState.AddChild(_characterSetup);
|
|
||||||
chatController.SetMainChat(true);
|
chatController.SetMainChat(true);
|
||||||
|
|
||||||
_voteManager.SetPopupContainer(_lobby.VoteContainer);
|
_voteManager.SetPopupContainer(Lobby.VoteContainer);
|
||||||
|
LayoutContainer.SetAnchorPreset(Lobby, LayoutContainer.LayoutPreset.Wide);
|
||||||
_characterSetup.CloseButton.OnPressed += _ =>
|
Lobby.ServerName.Text = _baseClient.GameInfo?.ServerName; //The eye of refactor gazes upon you...
|
||||||
{
|
|
||||||
// Reset sliders etc.
|
|
||||||
_characterSetup?.UpdateControls();
|
|
||||||
|
|
||||||
var controller = _userInterfaceManager.GetUIController<LobbyUIController>();
|
|
||||||
controller.SetClothes(true);
|
|
||||||
controller.UpdateProfile();
|
|
||||||
_lobby.SwitchState(LobbyGui.LobbyGuiState.Default);
|
|
||||||
};
|
|
||||||
|
|
||||||
_characterSetup.SaveButton.OnPressed += _ =>
|
|
||||||
{
|
|
||||||
_characterSetup.Save();
|
|
||||||
_userInterfaceManager.GetUIController<LobbyUIController>().ReloadProfile();
|
|
||||||
};
|
|
||||||
|
|
||||||
LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide);
|
|
||||||
_lobby.ServerName.Text = _baseClient.GameInfo?.ServerName; //The eye of refactor gazes upon you...
|
|
||||||
UpdateLobbyUi();
|
UpdateLobbyUi();
|
||||||
|
|
||||||
_lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed;
|
Lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed;
|
||||||
_lobby.ReadyButton.OnPressed += OnReadyPressed;
|
Lobby.ReadyButton.OnPressed += OnReadyPressed;
|
||||||
_lobby.ReadyButton.OnToggled += OnReadyToggled;
|
Lobby.ReadyButton.OnToggled += OnReadyToggled;
|
||||||
|
|
||||||
_gameTicker.InfoBlobUpdated += UpdateLobbyUi;
|
_gameTicker.InfoBlobUpdated += UpdateLobbyUi;
|
||||||
_gameTicker.LobbyStatusUpdated += LobbyStatusUpdated;
|
_gameTicker.LobbyStatusUpdated += LobbyStatusUpdated;
|
||||||
@@ -103,20 +72,23 @@ namespace Content.Client.Lobby
|
|||||||
|
|
||||||
_voteManager.ClearPopupContainer();
|
_voteManager.ClearPopupContainer();
|
||||||
|
|
||||||
_lobby!.CharacterPreview.CharacterSetupButton.OnPressed -= OnSetupPressed;
|
Lobby!.CharacterPreview.CharacterSetupButton.OnPressed -= OnSetupPressed;
|
||||||
_lobby!.ReadyButton.OnPressed -= OnReadyPressed;
|
Lobby!.ReadyButton.OnPressed -= OnReadyPressed;
|
||||||
_lobby!.ReadyButton.OnToggled -= OnReadyToggled;
|
Lobby!.ReadyButton.OnToggled -= OnReadyToggled;
|
||||||
|
|
||||||
_lobby = null;
|
Lobby = null;
|
||||||
|
}
|
||||||
|
|
||||||
_characterSetup?.Dispose();
|
public void SwitchState(LobbyGui.LobbyGuiState state)
|
||||||
_characterSetup = null;
|
{
|
||||||
|
// Yeah I hate this but LobbyState contains all the badness for now.
|
||||||
|
Lobby?.SwitchState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSetupPressed(BaseButton.ButtonEventArgs args)
|
private void OnSetupPressed(BaseButton.ButtonEventArgs args)
|
||||||
{
|
{
|
||||||
SetReady(false);
|
SetReady(false);
|
||||||
_lobby!.SwitchState(LobbyGui.LobbyGuiState.CharacterSetup);
|
Lobby?.SwitchState(LobbyGui.LobbyGuiState.CharacterSetup);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnReadyPressed(BaseButton.ButtonEventArgs args)
|
private void OnReadyPressed(BaseButton.ButtonEventArgs args)
|
||||||
@@ -138,13 +110,13 @@ namespace Content.Client.Lobby
|
|||||||
{
|
{
|
||||||
if (_gameTicker.IsGameStarted)
|
if (_gameTicker.IsGameStarted)
|
||||||
{
|
{
|
||||||
_lobby!.StartTime.Text = string.Empty;
|
Lobby!.StartTime.Text = string.Empty;
|
||||||
var roundTime = _gameTiming.CurTime.Subtract(_gameTicker.RoundStartTimeSpan);
|
var roundTime = _gameTiming.CurTime.Subtract(_gameTicker.RoundStartTimeSpan);
|
||||||
_lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-time", ("hours", roundTime.Hours), ("minutes", roundTime.Minutes));
|
Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-time", ("hours", roundTime.Hours), ("minutes", roundTime.Minutes));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started");
|
Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started");
|
||||||
string text;
|
string text;
|
||||||
|
|
||||||
if (_gameTicker.Paused)
|
if (_gameTicker.Paused)
|
||||||
@@ -153,7 +125,7 @@ namespace Content.Client.Lobby
|
|||||||
}
|
}
|
||||||
else if (_gameTicker.StartTime < _gameTiming.CurTime)
|
else if (_gameTicker.StartTime < _gameTiming.CurTime)
|
||||||
{
|
{
|
||||||
_lobby!.StartTime.Text = Loc.GetString("lobby-state-soon");
|
Lobby!.StartTime.Text = Loc.GetString("lobby-state-soon");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -170,7 +142,7 @@ namespace Content.Client.Lobby
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_lobby!.StartTime.Text = Loc.GetString("lobby-state-round-start-countdown-text", ("timeLeft", text));
|
Lobby!.StartTime.Text = Loc.GetString("lobby-state-round-start-countdown-text", ("timeLeft", text));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LobbyStatusUpdated()
|
private void LobbyStatusUpdated()
|
||||||
@@ -181,31 +153,31 @@ namespace Content.Client.Lobby
|
|||||||
|
|
||||||
private void LobbyLateJoinStatusUpdated()
|
private void LobbyLateJoinStatusUpdated()
|
||||||
{
|
{
|
||||||
_lobby!.ReadyButton.Disabled = _gameTicker.DisallowedLateJoin;
|
Lobby!.ReadyButton.Disabled = _gameTicker.DisallowedLateJoin;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateLobbyUi()
|
private void UpdateLobbyUi()
|
||||||
{
|
{
|
||||||
if (_gameTicker.IsGameStarted)
|
if (_gameTicker.IsGameStarted)
|
||||||
{
|
{
|
||||||
_lobby!.ReadyButton.Text = Loc.GetString("lobby-state-ready-button-join-state");
|
Lobby!.ReadyButton.Text = Loc.GetString("lobby-state-ready-button-join-state");
|
||||||
_lobby!.ReadyButton.ToggleMode = false;
|
Lobby!.ReadyButton.ToggleMode = false;
|
||||||
_lobby!.ReadyButton.Pressed = false;
|
Lobby!.ReadyButton.Pressed = false;
|
||||||
_lobby!.ObserveButton.Disabled = false;
|
Lobby!.ObserveButton.Disabled = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_lobby!.StartTime.Text = string.Empty;
|
Lobby!.StartTime.Text = string.Empty;
|
||||||
_lobby!.ReadyButton.Text = Loc.GetString(_lobby!.ReadyButton.Pressed ? "lobby-state-player-status-ready": "lobby-state-player-status-not-ready");
|
Lobby!.ReadyButton.Text = Loc.GetString(Lobby!.ReadyButton.Pressed ? "lobby-state-player-status-ready": "lobby-state-player-status-not-ready");
|
||||||
_lobby!.ReadyButton.ToggleMode = true;
|
Lobby!.ReadyButton.ToggleMode = true;
|
||||||
_lobby!.ReadyButton.Disabled = false;
|
Lobby!.ReadyButton.Disabled = false;
|
||||||
_lobby!.ReadyButton.Pressed = _gameTicker.AreWeReady;
|
Lobby!.ReadyButton.Pressed = _gameTicker.AreWeReady;
|
||||||
_lobby!.ObserveButton.Disabled = true;
|
Lobby!.ObserveButton.Disabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_gameTicker.ServerInfoBlob != null)
|
if (_gameTicker.ServerInfoBlob != null)
|
||||||
{
|
{
|
||||||
_lobby!.ServerInfo.SetInfoBlob(_gameTicker.ServerInfoBlob);
|
Lobby!.ServerInfo.SetInfoBlob(_gameTicker.ServerInfoBlob);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,7 +185,7 @@ namespace Content.Client.Lobby
|
|||||||
{
|
{
|
||||||
if (ev.SoundtrackFilename == null)
|
if (ev.SoundtrackFilename == null)
|
||||||
{
|
{
|
||||||
_lobby!.LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text"));
|
Lobby!.LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text"));
|
||||||
}
|
}
|
||||||
else if (
|
else if (
|
||||||
ev.SoundtrackFilename != null
|
ev.SoundtrackFilename != null
|
||||||
@@ -234,7 +206,7 @@ namespace Content.Client.Lobby
|
|||||||
("songTitle", title),
|
("songTitle", title),
|
||||||
("songArtist", artist));
|
("songArtist", artist));
|
||||||
|
|
||||||
_lobby!.LobbySong.SetMarkup(markup);
|
Lobby!.LobbySong.SetMarkup(markup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,11 +214,11 @@ namespace Content.Client.Lobby
|
|||||||
{
|
{
|
||||||
if (_gameTicker.LobbyBackground != null)
|
if (_gameTicker.LobbyBackground != null)
|
||||||
{
|
{
|
||||||
_lobby!.Background.Texture = _resourceCache.GetResource<TextureResource>(_gameTicker.LobbyBackground );
|
Lobby!.Background.Texture = _resourceCache.GetResource<TextureResource>(_gameTicker.LobbyBackground );
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_lobby!.Background.Texture = null;
|
Lobby!.Background.Texture = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,190 +2,292 @@ using System.Linq;
|
|||||||
using Content.Client.Humanoid;
|
using Content.Client.Humanoid;
|
||||||
using Content.Client.Inventory;
|
using Content.Client.Inventory;
|
||||||
using Content.Client.Lobby.UI;
|
using Content.Client.Lobby.UI;
|
||||||
using Content.Client.Preferences;
|
using Content.Client.Players.PlayTimeTracking;
|
||||||
using Content.Client.Preferences.UI;
|
|
||||||
using Content.Client.Station;
|
using Content.Client.Station;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Clothing;
|
using Content.Shared.Clothing;
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
|
using Content.Shared.Humanoid;
|
||||||
|
using Content.Shared.Humanoid.Markings;
|
||||||
using Content.Shared.Humanoid.Prototypes;
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
using Content.Shared.Preferences.Loadouts;
|
using Content.Shared.Preferences.Loadouts;
|
||||||
using Content.Shared.Preferences.Loadouts.Effects;
|
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
|
using Content.Shared.Traits;
|
||||||
|
using Robust.Client.Player;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Client.State;
|
using Robust.Client.State;
|
||||||
using Robust.Client.UserInterface;
|
using Robust.Client.UserInterface;
|
||||||
using Robust.Client.UserInterface.Controllers;
|
using Robust.Client.UserInterface.Controllers;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.Manager;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Client.Lobby;
|
namespace Content.Client.Lobby;
|
||||||
|
|
||||||
public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState>, IOnStateExited<LobbyState>
|
public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState>, IOnStateExited<LobbyState>
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||||
[Dependency] private readonly IStateManager _stateManager = 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 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 HumanoidAppearanceSystem _humanoid = default!;
|
||||||
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
||||||
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
||||||
|
|
||||||
private LobbyCharacterPreviewPanel? _previewPanel;
|
private CharacterSetupGui? _characterSetup;
|
||||||
|
private HumanoidProfileEditor? _profileEditor;
|
||||||
private bool _showClothes = true;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Each character profile has its own dummy. There is also a dummy for the lobby screen + character editor
|
|
||||||
* that is shared too.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Preview dummy for role gear.
|
/// This is the characher preview panel in the chat. This should only update if their character updates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private EntityUid? _previewDummy;
|
private LobbyCharacterPreviewPanel? PreviewPanel => GetLobbyPreview();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If we currently have a job prototype selected.
|
/// This is the modified profile currently being edited.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private JobPrototype? _dummyJob;
|
private HumanoidCharacterProfile? EditedProfile => _profileEditor?.Profile;
|
||||||
|
|
||||||
// TODO: Load the species directly and don't update entity ever.
|
private int? EditedSlot => _profileEditor?.CharacterSlot;
|
||||||
public event Action<EntityUid>? PreviewDummyUpdated;
|
|
||||||
|
|
||||||
private HumanoidCharacterProfile? _profile;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
_prototypeManager.PrototypesReloaded += OnProtoReload;
|
||||||
_preferencesManager.OnServerDataLoaded += PreferencesDataLoaded;
|
_preferencesManager.OnServerDataLoaded += PreferencesDataLoaded;
|
||||||
|
_requirements.Updated += OnRequirementsUpdated;
|
||||||
|
|
||||||
|
_configurationManager.OnValueChanged(CCVars.FlavorText, args =>
|
||||||
|
{
|
||||||
|
_profileEditor?.RefreshFlavorText();
|
||||||
|
});
|
||||||
|
|
||||||
|
_configurationManager.OnValueChanged(CCVars.GameRoleTimers, args =>
|
||||||
|
{
|
||||||
|
_profileEditor?.RefreshAntags();
|
||||||
|
_profileEditor?.RefreshJobs();
|
||||||
|
_profileEditor?.RefreshLoadouts();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
private void PreferencesDataLoaded()
|
||||||
{
|
{
|
||||||
UpdateProfile();
|
PreviewPanel?.SetLoaded(true);
|
||||||
}
|
|
||||||
|
|
||||||
public void OnStateEntered(LobbyState state)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnStateExited(LobbyState state)
|
|
||||||
{
|
|
||||||
EntityManager.DeleteEntity(_previewDummy);
|
|
||||||
_previewDummy = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetPreviewPanel(LobbyCharacterPreviewPanel? panel)
|
|
||||||
{
|
|
||||||
_previewPanel = panel;
|
|
||||||
ReloadProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetClothes(bool value)
|
|
||||||
{
|
|
||||||
if (_showClothes == value)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_showClothes = value;
|
|
||||||
ReloadCharacterUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetDummyJob(JobPrototype? job)
|
|
||||||
{
|
|
||||||
_dummyJob = job;
|
|
||||||
ReloadCharacterUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the character only with the specified profile change.
|
|
||||||
/// </summary>
|
|
||||||
public void ReloadProfile()
|
|
||||||
{
|
|
||||||
// Test moment
|
|
||||||
if (_profile == null || _stateManager.CurrentState is not LobbyState)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Ignore job clothes and the likes so we don't spam entities out every frame of color changes.
|
|
||||||
var previewDummy = EnsurePreviewDummy(_profile);
|
|
||||||
_humanoid.LoadProfile(previewDummy, _profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates the currently selected character's preview.
|
|
||||||
/// </summary>
|
|
||||||
public void ReloadCharacterUI()
|
|
||||||
{
|
|
||||||
// Test moment
|
|
||||||
if (_profile == null || _stateManager.CurrentState is not LobbyState)
|
|
||||||
return;
|
|
||||||
|
|
||||||
EntityManager.DeleteEntity(_previewDummy);
|
|
||||||
_previewDummy = null;
|
|
||||||
_previewDummy = EnsurePreviewDummy(_profile);
|
|
||||||
_previewPanel?.SetSprite(_previewDummy.Value);
|
|
||||||
_previewPanel?.SetSummaryText(_profile.Summary);
|
|
||||||
_humanoid.LoadProfile(_previewDummy.Value, _profile);
|
|
||||||
|
|
||||||
if (_showClothes)
|
|
||||||
GiveDummyJobClothesLoadout(_previewDummy.Value, _profile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Updates character profile to the default.
|
|
||||||
/// </summary>
|
|
||||||
public void UpdateProfile()
|
|
||||||
{
|
|
||||||
if (!_preferencesManager.ServerDataLoaded)
|
|
||||||
{
|
|
||||||
_profile = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_preferencesManager.Preferences?.SelectedCharacter is HumanoidCharacterProfile selectedCharacter)
|
|
||||||
{
|
|
||||||
_profile = selectedCharacter;
|
|
||||||
_previewPanel?.SetLoaded(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_previewPanel?.SetSummaryText(string.Empty);
|
|
||||||
_previewPanel?.SetLoaded(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReloadCharacterUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateProfile(HumanoidCharacterProfile? profile)
|
|
||||||
{
|
|
||||||
if (_profile?.Equals(profile) == true)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_stateManager.CurrentState is not LobbyState)
|
if (_stateManager.CurrentState is not LobbyState)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_profile = profile;
|
ReloadCharacterSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
private EntityUid EnsurePreviewDummy(HumanoidCharacterProfile profile)
|
public void OnStateEntered(LobbyState state)
|
||||||
{
|
{
|
||||||
if (_previewDummy != null)
|
PreviewPanel?.SetLoaded(_preferencesManager.ServerDataLoaded);
|
||||||
return _previewDummy.Value;
|
ReloadCharacterSetup();
|
||||||
|
|
||||||
_previewDummy = EntityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(profile.Species).DollPrototype, MapCoordinates.Nullspace);
|
|
||||||
PreviewDummyUpdated?.Invoke(_previewDummy.Value);
|
|
||||||
return _previewDummy.Value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 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 (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,
|
||||||
|
_requirements,
|
||||||
|
_markings);
|
||||||
|
|
||||||
|
_characterSetup = new CharacterSetupGui(EntityManager, _prototypeManager, _resourceCache, _preferencesManager, _profileEditor);
|
||||||
|
|
||||||
|
_characterSetup.CloseButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
// Reset sliders etc.
|
||||||
|
_profileEditor.SetProfile(null, null);
|
||||||
|
_profileEditor.Visible = false;
|
||||||
|
|
||||||
|
if (_stateManager.CurrentState is LobbyState lobbyGui)
|
||||||
|
{
|
||||||
|
lobbyGui.SwitchState(LobbyGui.LobbyGuiState.Default);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_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>
|
/// <summary>
|
||||||
/// Applies the highest priority job's clothes to the dummy.
|
/// Applies the highest priority job's clothes to the dummy.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void GiveDummyJobClothesLoadout(EntityUid dummy, HumanoidCharacterProfile profile)
|
public void GiveDummyJobClothesLoadout(EntityUid dummy, JobPrototype? jobProto, HumanoidCharacterProfile profile)
|
||||||
{
|
{
|
||||||
var job = _dummyJob ?? GetPreferredJob(profile);
|
var job = jobProto ?? GetPreferredJob(profile);
|
||||||
GiveDummyJobClothes(dummy, profile, job);
|
GiveDummyJobClothes(dummy, profile, job);
|
||||||
|
|
||||||
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
|
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
|
||||||
{
|
{
|
||||||
var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), EntityManager, _prototypeManager);
|
var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), profile.Species, EntityManager, _prototypeManager);
|
||||||
GiveDummyLoadout(dummy, loadout);
|
GiveDummyLoadout(dummy, loadout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,8 +381,39 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityUid? GetPreviewDummy()
|
/// <summary>
|
||||||
|
/// Loads the profile onto a dummy entity.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid LoadProfileEntity(HumanoidCharacterProfile? humanoid, JobPrototype? job, bool jobClothes)
|
||||||
{
|
{
|
||||||
return _previewDummy;
|
EntityUid dummyEnt;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
job ??= GetPreferredJob(humanoid);
|
||||||
|
GiveDummyJobClothes(dummyEnt, humanoid, job);
|
||||||
|
|
||||||
|
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
|
||||||
|
{
|
||||||
|
var loadout = humanoid.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), humanoid.Species, EntityManager, _prototypeManager);
|
||||||
|
GiveDummyLoadout(dummyEnt, loadout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dummyEnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
22
Content.Client/Lobby/UI/CharacterPickerButton.xaml
Normal file
22
Content.Client/Lobby/UI/CharacterPickerButton.xaml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<ContainerButton xmlns="https://spacestation14.io"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:style="clr-namespace:Content.Client.Stylesheets">
|
||||||
|
<BoxContainer Orientation="Horizontal"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
SeparationOverride="0"
|
||||||
|
Name="InternalHBox">
|
||||||
|
<SpriteView Scale="2 2"
|
||||||
|
OverrideDirection="South"
|
||||||
|
Name="View"/>
|
||||||
|
<Label Name="DescriptionLabel"
|
||||||
|
ClipText="True"
|
||||||
|
HorizontalExpand="True"/>
|
||||||
|
<Button Name="DeleteButton"
|
||||||
|
Text="{Loc 'character-setup-gui-character-picker-button-delete-button'}"/>
|
||||||
|
<Button Name="ConfirmDeleteButton"
|
||||||
|
Text="{Loc 'character-setup-gui-character-picker-button-confirm-delete-button'}"
|
||||||
|
Visible="False"
|
||||||
|
ModulateSelfOverride="{x:Static style:StyleNano.ButtonColorCautionDefault}"/>
|
||||||
|
|
||||||
|
</BoxContainer>
|
||||||
|
</ContainerButton>
|
||||||
92
Content.Client/Lobby/UI/CharacterPickerButton.xaml.cs
Normal file
92
Content.Client/Lobby/UI/CharacterPickerButton.xaml.cs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Client.Humanoid;
|
||||||
|
using Content.Shared.Clothing;
|
||||||
|
using Content.Shared.Humanoid;
|
||||||
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
|
using Content.Shared.Preferences;
|
||||||
|
using Content.Shared.Preferences.Loadouts;
|
||||||
|
using Content.Shared.Roles;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Client.Lobby.UI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Holds character data on the side of the setup GUI.
|
||||||
|
/// </summary>
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class CharacterPickerButton : ContainerButton
|
||||||
|
{
|
||||||
|
private IEntityManager _entManager;
|
||||||
|
|
||||||
|
private EntityUid _previewDummy;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked if we should delete the attached character
|
||||||
|
/// </summary>
|
||||||
|
public event Action? OnDeletePressed;
|
||||||
|
|
||||||
|
public CharacterPickerButton(
|
||||||
|
IEntityManager entityManager,
|
||||||
|
IPrototypeManager prototypeManager,
|
||||||
|
ButtonGroup group,
|
||||||
|
ICharacterProfile profile,
|
||||||
|
bool isSelected)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
_entManager = entityManager;
|
||||||
|
AddStyleClass(StyleClassButton);
|
||||||
|
ToggleMode = true;
|
||||||
|
Group = group;
|
||||||
|
var description = profile.Name;
|
||||||
|
|
||||||
|
if (profile is not HumanoidCharacterProfile humanoid)
|
||||||
|
{
|
||||||
|
_previewDummy = entityManager.SpawnEntity(prototypeManager.Index<SpeciesPrototype>(SharedHumanoidAppearanceSystem.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_previewDummy = UserInterfaceManager.GetUIController<LobbyUIController>()
|
||||||
|
.LoadProfileEntity(humanoid, null, true);
|
||||||
|
|
||||||
|
var highPriorityJob = humanoid.JobPriorities.SingleOrDefault(p => p.Value == JobPriority.High).Key;
|
||||||
|
if (highPriorityJob != null)
|
||||||
|
{
|
||||||
|
var jobName = prototypeManager.Index<JobPrototype>(highPriorityJob).LocalizedName;
|
||||||
|
description = $"{description}\n{jobName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pressed = isSelected;
|
||||||
|
DeleteButton.Visible = !isSelected;
|
||||||
|
|
||||||
|
View.SetEntity(_previewDummy);
|
||||||
|
DescriptionLabel.Text = description;
|
||||||
|
|
||||||
|
ConfirmDeleteButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
Parent?.RemoveChild(this);
|
||||||
|
Parent?.RemoveChild(ConfirmDeleteButton);
|
||||||
|
OnDeletePressed?.Invoke();
|
||||||
|
};
|
||||||
|
|
||||||
|
DeleteButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
DeleteButton.Visible = false;
|
||||||
|
ConfirmDeleteButton.Visible = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
if (!disposing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_entManager.DeleteEntity(_previewDummy);
|
||||||
|
_previewDummy = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,10 +17,6 @@
|
|||||||
<Button Name="RulesButton"
|
<Button Name="RulesButton"
|
||||||
Text="{Loc 'character-setup-gui-character-setup-rules-button'}"
|
Text="{Loc 'character-setup-gui-character-setup-rules-button'}"
|
||||||
StyleClasses="ButtonBig"/>
|
StyleClasses="ButtonBig"/>
|
||||||
<Button Name="SaveButton"
|
|
||||||
Access="Public"
|
|
||||||
Text="{Loc 'character-setup-gui-character-setup-save-button'}"
|
|
||||||
StyleClasses="ButtonBig"/>
|
|
||||||
<Button Name="CloseButton"
|
<Button Name="CloseButton"
|
||||||
Access="Public"
|
Access="Public"
|
||||||
Text="{Loc 'character-setup-gui-character-setup-close-button'}"
|
Text="{Loc 'character-setup-gui-character-setup-close-button'}"
|
||||||
118
Content.Client/Lobby/UI/CharacterSetupGui.xaml.cs
Normal file
118
Content.Client/Lobby/UI/CharacterSetupGui.xaml.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using Content.Client.Info;
|
||||||
|
using Content.Client.Info.PlaytimeStats;
|
||||||
|
using Content.Client.Resources;
|
||||||
|
using Content.Shared.Preferences;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Client.Lobby.UI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Holds the entire character setup GUI, from character picks to individual character editing.
|
||||||
|
/// </summary>
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class CharacterSetupGui : Control
|
||||||
|
{
|
||||||
|
private readonly IClientPreferencesManager _preferencesManager;
|
||||||
|
private readonly IEntityManager _entManager;
|
||||||
|
private readonly IPrototypeManager _protomanager;
|
||||||
|
|
||||||
|
private readonly Button _createNewCharacterButton;
|
||||||
|
|
||||||
|
public event Action<int>? SelectCharacter;
|
||||||
|
public event Action<int>? DeleteCharacter;
|
||||||
|
|
||||||
|
public CharacterSetupGui(
|
||||||
|
IEntityManager entManager,
|
||||||
|
IPrototypeManager protoManager,
|
||||||
|
IResourceCache resourceCache,
|
||||||
|
IClientPreferencesManager preferencesManager,
|
||||||
|
HumanoidProfileEditor profileEditor)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
_preferencesManager = preferencesManager;
|
||||||
|
_entManager = entManager;
|
||||||
|
_protomanager = protoManager;
|
||||||
|
|
||||||
|
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||||
|
var back = new StyleBoxTexture
|
||||||
|
{
|
||||||
|
Texture = panelTex,
|
||||||
|
Modulate = new Color(37, 37, 42)
|
||||||
|
};
|
||||||
|
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||||
|
|
||||||
|
BackgroundPanel.PanelOverride = back;
|
||||||
|
|
||||||
|
_createNewCharacterButton = new Button
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("character-setup-gui-create-new-character-button"),
|
||||||
|
};
|
||||||
|
|
||||||
|
_createNewCharacterButton.OnPressed += args =>
|
||||||
|
{
|
||||||
|
preferencesManager.CreateCharacter(HumanoidCharacterProfile.Random());
|
||||||
|
ReloadCharacterPickers();
|
||||||
|
args.Event.Handle();
|
||||||
|
};
|
||||||
|
|
||||||
|
CharEditor.AddChild(profileEditor);
|
||||||
|
RulesButton.OnPressed += _ => new RulesAndInfoWindow().Open();
|
||||||
|
|
||||||
|
StatsButton.OnPressed += _ => new PlaytimeStatsWindow().OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disposes and reloads all character picker buttons from the preferences data.
|
||||||
|
/// </summary>
|
||||||
|
public void ReloadCharacterPickers()
|
||||||
|
{
|
||||||
|
_createNewCharacterButton.Orphan();
|
||||||
|
Characters.DisposeAllChildren();
|
||||||
|
|
||||||
|
var numberOfFullSlots = 0;
|
||||||
|
var characterButtonsGroup = new ButtonGroup();
|
||||||
|
|
||||||
|
if (!_preferencesManager.ServerDataLoaded)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_createNewCharacterButton.ToolTip =
|
||||||
|
Loc.GetString("character-setup-gui-create-new-character-button-tooltip",
|
||||||
|
("maxCharacters", _preferencesManager.Settings!.MaxCharacterSlots));
|
||||||
|
|
||||||
|
var selectedSlot = _preferencesManager.Preferences?.SelectedCharacterIndex;
|
||||||
|
|
||||||
|
foreach (var (slot, character) in _preferencesManager.Preferences!.Characters)
|
||||||
|
{
|
||||||
|
numberOfFullSlots++;
|
||||||
|
var characterPickerButton = new CharacterPickerButton(_entManager,
|
||||||
|
_protomanager,
|
||||||
|
characterButtonsGroup,
|
||||||
|
character,
|
||||||
|
slot == selectedSlot);
|
||||||
|
|
||||||
|
Characters.AddChild(characterPickerButton);
|
||||||
|
|
||||||
|
characterPickerButton.OnPressed += args =>
|
||||||
|
{
|
||||||
|
SelectCharacter?.Invoke(slot);
|
||||||
|
};
|
||||||
|
|
||||||
|
characterPickerButton.OnDeletePressed += () =>
|
||||||
|
{
|
||||||
|
DeleteCharacter?.Invoke(slot);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_createNewCharacterButton.Disabled = numberOfFullSlots >= _preferencesManager.Settings.MaxCharacterSlots;
|
||||||
|
Characters.AddChild(_createNewCharacterButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ using Robust.Client.AutoGenerated;
|
|||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
namespace Content.Client.Preferences.UI;
|
namespace Content.Client.Lobby.UI;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class HighlightedContainer : PanelContainer
|
public sealed partial class HighlightedContainer : PanelContainer
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<BoxContainer xmlns="https://spacestation14.io"
|
<BoxContainer xmlns="https://spacestation14.io"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:prefUi="clr-namespace:Content.Client.Preferences.UI"
|
|
||||||
xmlns:humanoid="clr-namespace:Content.Client.Humanoid"
|
xmlns:humanoid="clr-namespace:Content.Client.Humanoid"
|
||||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.Lobby.UI"
|
||||||
HorizontalExpand="True">
|
HorizontalExpand="True">
|
||||||
<!-- Left side -->
|
<!-- Left side -->
|
||||||
<BoxContainer Orientation="Vertical" Margin="10 10 10 10" HorizontalExpand="True">
|
<BoxContainer Orientation="Vertical" Margin="10 10 10 10" HorizontalExpand="True">
|
||||||
@@ -10,45 +10,41 @@
|
|||||||
<BoxContainer Orientation="Horizontal" SeparationOverride="10">
|
<BoxContainer Orientation="Horizontal" SeparationOverride="10">
|
||||||
<!-- Name box-->
|
<!-- Name box-->
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<prefUi:HighlightedContainer>
|
<ui:HighlightedContainer>
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<prefUi:HighlightedContainer>
|
<ui:HighlightedContainer>
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
|
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
|
||||||
<Label Text="{Loc 'humanoid-profile-editor-name-label'}" />
|
<Label Text="{Loc 'humanoid-profile-editor-name-label'}" />
|
||||||
<LineEdit Name="CNameEdit" MinSize="270 0" VerticalAlignment="Center" Margin="5 0 0 0" />
|
<LineEdit Name="NameEdit" MinSize="270 0" VerticalAlignment="Center" Margin="5 0 0 0" />
|
||||||
<Button Name="CNameRandomize" Text="{Loc 'humanoid-profile-editor-name-random-button'}" />
|
<Button Name="NameRandomize" Text="{Loc 'humanoid-profile-editor-name-random-button'}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Button Name="CRandomizeEverything" HorizontalAlignment="Center"
|
<Button Name="RandomizeEverythingButton" HorizontalAlignment="Center"
|
||||||
HorizontalExpand="False" MaxWidth="256"
|
HorizontalExpand="False" MaxWidth="256"
|
||||||
Text="{Loc 'humanoid-profile-editor-randomize-everything-button'}" />
|
Text="{Loc 'humanoid-profile-editor-randomize-everything-button'}" />
|
||||||
<RichTextLabel Name="CWarningLabel" HorizontalExpand="False"
|
<RichTextLabel Name="WarningLabel" HorizontalExpand="False"
|
||||||
VerticalExpand="True" MaxWidth="425"
|
VerticalExpand="True" MaxWidth="425"
|
||||||
HorizontalAlignment="Left" />
|
HorizontalAlignment="Left" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</prefUi:HighlightedContainer>
|
</ui:HighlightedContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</prefUi:HighlightedContainer>
|
</ui:HighlightedContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Import/Export -->
|
<!-- Import/Export -->
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<prefUi:HighlightedContainer>
|
<ui:HighlightedContainer>
|
||||||
<BoxContainer Orientation="Horizontal">
|
<BoxContainer Orientation="Horizontal" SeparationOverride="5">
|
||||||
<Button Text="{Loc 'humanoid-profile-editor-import-button'}" Disabled="True"
|
<Button Name="ImportButton" Text="{Loc 'humanoid-profile-editor-import-button'}"/>
|
||||||
ToolTip="{Loc 'generic-not-yet-implemented'}" />
|
<Button Name="ExportButton" Text="{Loc 'humanoid-profile-editor-export-button'}"/>
|
||||||
<Button Text="{Loc 'humanoid-profile-editor-export-button'}" Disabled="True"
|
<Button Name="SaveButton" Text="{Loc 'humanoid-profile-editor-save-button'}" />
|
||||||
ToolTip="{Loc 'generic-not-yet-implemented'}" />
|
<Button Name="ResetButton" Disabled="True" Text="{Loc 'humanoid-profile-editor-reset-button'}"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</prefUi:HighlightedContainer>
|
</ui:HighlightedContainer>
|
||||||
<!-- Save -->
|
|
||||||
<prefUi:HighlightedContainer>
|
|
||||||
<Button Name="CSaveButton" Text="{Loc 'humanoid-profile-editor-save-button'}" HorizontalAlignment="Center" />
|
|
||||||
</prefUi:HighlightedContainer>
|
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Control MinHeight="10" />
|
<Control MinHeight="10" />
|
||||||
<!-- tabContainer -->
|
<!-- tabContainer -->
|
||||||
<TabContainer Name="CTabContainer" VerticalExpand="True">
|
<TabContainer Name="TabContainer" VerticalExpand="True">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<ScrollContainer VerticalExpand="True">
|
<ScrollContainer VerticalExpand="True">
|
||||||
<!-- appearanceList -->
|
<!-- appearanceList -->
|
||||||
@@ -61,25 +57,25 @@
|
|||||||
<TextureButton Name="SpeciesInfoButton" Scale="0.3 0.3"
|
<TextureButton Name="SpeciesInfoButton" Scale="0.3 0.3"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
ToolTip="{Loc 'humanoid-profile-editor-guidebook-button-tooltip'}"/>
|
ToolTip="{Loc 'humanoid-profile-editor-guidebook-button-tooltip'}"/>
|
||||||
<OptionButton Name="CSpeciesButton" HorizontalAlignment="Right" />
|
<OptionButton Name="SpeciesButton" HorizontalAlignment="Right" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Age -->
|
<!-- Age -->
|
||||||
<BoxContainer HorizontalExpand="True">
|
<BoxContainer HorizontalExpand="True">
|
||||||
<Label Text="{Loc 'humanoid-profile-editor-age-label'}" />
|
<Label Text="{Loc 'humanoid-profile-editor-age-label'}" />
|
||||||
<Control HorizontalExpand="True"/>
|
<Control HorizontalExpand="True"/>
|
||||||
<LineEdit Name="CAgeEdit" MinSize="40 0" HorizontalAlignment="Right" />
|
<LineEdit Name="AgeEdit" MinSize="40 0" HorizontalAlignment="Right" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Sex -->
|
<!-- Sex -->
|
||||||
<BoxContainer HorizontalExpand="True">
|
<BoxContainer HorizontalExpand="True">
|
||||||
<Label Text="{Loc 'humanoid-profile-editor-sex-label'}" />
|
<Label Text="{Loc 'humanoid-profile-editor-sex-label'}" />
|
||||||
<Control HorizontalExpand="True"/>
|
<Control HorizontalExpand="True"/>
|
||||||
<OptionButton Name="CSexButton" HorizontalAlignment="Right" />
|
<OptionButton Name="SexButton" HorizontalAlignment="Right" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Pronouns -->
|
<!-- Pronouns -->
|
||||||
<BoxContainer HorizontalExpand="True">
|
<BoxContainer HorizontalExpand="True">
|
||||||
<Label Text="{Loc 'humanoid-profile-editor-pronouns-label'}" />
|
<Label Text="{Loc 'humanoid-profile-editor-pronouns-label'}" />
|
||||||
<Control HorizontalExpand="True"/>
|
<Control HorizontalExpand="True"/>
|
||||||
<OptionButton Name="CPronounsButton" HorizontalAlignment="Right" />
|
<OptionButton Name="PronounsButton" HorizontalAlignment="Right" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Show clothing -->
|
<!-- Show clothing -->
|
||||||
<BoxContainer HorizontalExpand="True">
|
<BoxContainer HorizontalExpand="True">
|
||||||
@@ -91,62 +87,62 @@
|
|||||||
<BoxContainer HorizontalExpand="True">
|
<BoxContainer HorizontalExpand="True">
|
||||||
<Label Text="{Loc 'humanoid-profile-editor-spawn-priority-label'}" />
|
<Label Text="{Loc 'humanoid-profile-editor-spawn-priority-label'}" />
|
||||||
<Control HorizontalExpand="True"/>
|
<Control HorizontalExpand="True"/>
|
||||||
<OptionButton Name="CSpawnPriorityButton" HorizontalAlignment="Right" />
|
<OptionButton Name="SpawnPriorityButton" HorizontalAlignment="Right" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Skin -->
|
<!-- Skin -->
|
||||||
<BoxContainer Margin="10" HorizontalExpand="True" Orientation="Vertical">
|
<BoxContainer Margin="10" HorizontalExpand="True" Orientation="Vertical">
|
||||||
<Label Text="{Loc 'humanoid-profile-editor-skin-color-label'}" />
|
<Label Text="{Loc 'humanoid-profile-editor-skin-color-label'}" />
|
||||||
<Slider HorizontalExpand="True" Name="CSkin" MinValue="0" MaxValue="100" Value="20" />
|
<Slider HorizontalExpand="True" Name="Skin" MinValue="0" MaxValue="100" Value="20" />
|
||||||
<BoxContainer Name="CRgbSkinColorContainer" Visible="False" Orientation="Vertical" HorizontalExpand="True"></BoxContainer>
|
<BoxContainer Name="RgbSkinColorContainer" Visible="False" Orientation="Vertical" HorizontalExpand="True"></BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Hair -->
|
<!-- Hair -->
|
||||||
<BoxContainer Margin="10" Orientation="Horizontal">
|
<BoxContainer Margin="10" Orientation="Horizontal">
|
||||||
<humanoid:SingleMarkingPicker Name="CHairStylePicker" Category="Hair" />
|
<humanoid:SingleMarkingPicker Name="HairStylePicker" Category="Hair" />
|
||||||
<humanoid:SingleMarkingPicker Name="CFacialHairPicker" Category="FacialHair" />
|
<humanoid:SingleMarkingPicker Name="FacialHairPicker" Category="FacialHair" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Eyes -->
|
<!-- Eyes -->
|
||||||
<BoxContainer Margin="10" Orientation="Vertical">
|
<BoxContainer Margin="10" Orientation="Vertical">
|
||||||
<Label Text="{Loc 'humanoid-profile-editor-eyes-label'}" />
|
<Label Text="{Loc 'humanoid-profile-editor-eyes-label'}" />
|
||||||
<humanoid:EyeColorPicker Name="CEyeColorPicker" />
|
<humanoid:EyeColorPicker Name="EyeColorPicker" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
<!-- Jobs -->
|
<!-- Jobs -->
|
||||||
<OptionButton Name="CPreferenceUnavailableButton" />
|
<OptionButton Name="PreferenceUnavailableButton" />
|
||||||
<ScrollContainer VerticalExpand="True">
|
<ScrollContainer VerticalExpand="True">
|
||||||
<BoxContainer Name="CJobList" Orientation="Vertical" />
|
<BoxContainer Name="JobList" Orientation="Vertical" />
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Vertical" Margin="10">
|
<BoxContainer Orientation="Vertical" Margin="10">
|
||||||
<!-- Antags -->
|
<!-- Antags -->
|
||||||
<ScrollContainer VerticalExpand="True">
|
<ScrollContainer VerticalExpand="True">
|
||||||
<BoxContainer Name="CAntagList" Orientation="Vertical" />
|
<BoxContainer Name="AntagList" Orientation="Vertical" />
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Orientation="Vertical" Margin="10">
|
<BoxContainer Orientation="Vertical" Margin="10">
|
||||||
<!-- Traits -->
|
<!-- Traits -->
|
||||||
<ScrollContainer VerticalExpand="True">
|
<ScrollContainer VerticalExpand="True">
|
||||||
<BoxContainer Name="CTraitsList" Orientation="Vertical" />
|
<BoxContainer Name="TraitsList" Orientation="Vertical" />
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Name="CMarkingsTab" Orientation="Vertical" Margin="10">
|
<BoxContainer Name="MarkingsTab" Orientation="Vertical" Margin="10">
|
||||||
<!-- Markings -->
|
<!-- Markings -->
|
||||||
<ScrollContainer VerticalExpand="True">
|
<ScrollContainer VerticalExpand="True">
|
||||||
<humanoid:MarkingPicker Name="CMarkings" IgnoreCategories="Hair,FacialHair" />
|
<humanoid:MarkingPicker Name="Markings" IgnoreCategories="Hair,FacialHair" />
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</TabContainer>
|
</TabContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Right side -->
|
<!-- Right side -->
|
||||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" VerticalAlignment="Center">
|
<BoxContainer Orientation="Vertical" VerticalExpand="True" VerticalAlignment="Center">
|
||||||
<SpriteView Name="CSpriteView" Scale="8 8" SizeFlagsStretchRatio="1" />
|
<SpriteView Name="SpriteView" Scale="8 8" SizeFlagsStretchRatio="1" />
|
||||||
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 5">
|
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 5">
|
||||||
<Button Name="CSpriteRotateLeft" Text="◀" StyleClasses="OpenRight" />
|
<Button Name="SpriteRotateLeft" Text="◀" StyleClasses="OpenRight" />
|
||||||
<cc:VSeparator Margin="2 0 3 0" />
|
<cc:VSeparator Margin="2 0 3 0" />
|
||||||
<Button Name="CSpriteRotateRight" Text="▶" StyleClasses="OpenLeft" />
|
<Button Name="SpriteRotateRight" Text="▶" StyleClasses="OpenLeft" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ using Robust.Shared.Map;
|
|||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Client.Preferences.UI;
|
namespace Content.Client.Lobby.UI.Loadouts;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class LoadoutContainer : BoxContainer
|
public sealed partial class LoadoutContainer : BoxContainer
|
||||||
@@ -45,7 +45,7 @@ public sealed partial class LoadoutContainer : BoxContainer
|
|||||||
|
|
||||||
var spriteTooltip = new Tooltip();
|
var spriteTooltip = new Tooltip();
|
||||||
spriteTooltip.SetMessage(FormattedMessage.FromUnformatted(_entManager.GetComponent<MetaDataComponent>(_entity.Value).EntityDescription));
|
spriteTooltip.SetMessage(FormattedMessage.FromUnformatted(_entManager.GetComponent<MetaDataComponent>(_entity.Value).EntityDescription));
|
||||||
Sprite.TooltipSupplier = _ => spriteTooltip;
|
TooltipSupplier = _ => spriteTooltip;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Clothing;
|
using Content.Shared.Clothing;
|
||||||
|
using Content.Shared.Preferences;
|
||||||
using Content.Shared.Preferences.Loadouts;
|
using Content.Shared.Preferences.Loadouts;
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
@@ -7,7 +8,7 @@ using Robust.Client.UserInterface.XAML;
|
|||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Client.Preferences.UI;
|
namespace Content.Client.Lobby.UI.Loadouts;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class LoadoutGroupContainer : BoxContainer
|
public sealed partial class LoadoutGroupContainer : BoxContainer
|
||||||
@@ -17,18 +18,18 @@ public sealed partial class LoadoutGroupContainer : BoxContainer
|
|||||||
public event Action<ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
|
public event Action<ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
|
||||||
public event Action<ProtoId<LoadoutPrototype>>? OnLoadoutUnpressed;
|
public event Action<ProtoId<LoadoutPrototype>>? OnLoadoutUnpressed;
|
||||||
|
|
||||||
public LoadoutGroupContainer(RoleLoadout loadout, LoadoutGroupPrototype groupProto, ICommonSession session, IDependencyCollection collection)
|
public LoadoutGroupContainer(HumanoidCharacterProfile profile, RoleLoadout loadout, LoadoutGroupPrototype groupProto, ICommonSession session, IDependencyCollection collection)
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
_groupProto = groupProto;
|
_groupProto = groupProto;
|
||||||
|
|
||||||
RefreshLoadouts(loadout, session, collection);
|
RefreshLoadouts(profile, loadout, session, collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates button availabilities and buttons.
|
/// Updates button availabilities and buttons.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RefreshLoadouts(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection)
|
public void RefreshLoadouts(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection)
|
||||||
{
|
{
|
||||||
var protoMan = collection.Resolve<IPrototypeManager>();
|
var protoMan = collection.Resolve<IPrototypeManager>();
|
||||||
var loadoutSystem = collection.Resolve<IEntityManager>().System<LoadoutSystem>();
|
var loadoutSystem = collection.Resolve<IEntityManager>().System<LoadoutSystem>();
|
||||||
@@ -74,7 +75,7 @@ public sealed partial class LoadoutGroupContainer : BoxContainer
|
|||||||
var matchingLoadout = selected.FirstOrDefault(e => e.Prototype == loadoutProto);
|
var matchingLoadout = selected.FirstOrDefault(e => e.Prototype == loadoutProto);
|
||||||
var pressed = matchingLoadout != null;
|
var pressed = matchingLoadout != null;
|
||||||
|
|
||||||
var enabled = loadout.IsValid(session, loadoutProto, collection, out var reason);
|
var enabled = loadout.IsValid(profile, session, loadoutProto, collection, out var reason);
|
||||||
var loadoutContainer = new LoadoutContainer(loadoutProto, !enabled, reason);
|
var loadoutContainer = new LoadoutContainer(loadoutProto, !enabled, reason);
|
||||||
loadoutContainer.Select.Pressed = pressed;
|
loadoutContainer.Select.Pressed = pressed;
|
||||||
loadoutContainer.Text = loadoutSystem.GetName(loadProto);
|
loadoutContainer.Text = loadoutSystem.GetName(loadProto);
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
using Content.Client.Lobby;
|
|
||||||
using Content.Client.UserInterface.Controls;
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Content.Shared.Preferences;
|
||||||
using Content.Shared.Preferences.Loadouts;
|
using Content.Shared.Preferences.Loadouts;
|
||||||
using Content.Shared.Preferences.Loadouts.Effects;
|
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Client.Preferences.UI;
|
namespace Content.Client.Lobby.UI.Loadouts;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class LoadoutWindow : FancyWindow
|
public sealed partial class LoadoutWindow : FancyWindow
|
||||||
@@ -17,9 +16,12 @@ public sealed partial class LoadoutWindow : FancyWindow
|
|||||||
|
|
||||||
private List<LoadoutGroupContainer> _groups = new();
|
private List<LoadoutGroupContainer> _groups = new();
|
||||||
|
|
||||||
public LoadoutWindow(RoleLoadout loadout, RoleLoadoutPrototype proto, ICommonSession session, IDependencyCollection collection)
|
public HumanoidCharacterProfile Profile;
|
||||||
|
|
||||||
|
public LoadoutWindow(HumanoidCharacterProfile profile, RoleLoadout loadout, RoleLoadoutPrototype proto, ICommonSession session, IDependencyCollection collection)
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
|
Profile = profile;
|
||||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||||
|
|
||||||
foreach (var group in proto.Groups)
|
foreach (var group in proto.Groups)
|
||||||
@@ -27,7 +29,7 @@ public sealed partial class LoadoutWindow : FancyWindow
|
|||||||
if (!protoManager.TryIndex(group, out var groupProto))
|
if (!protoManager.TryIndex(group, out var groupProto))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var container = new LoadoutGroupContainer(loadout, protoManager.Index(group), session, collection);
|
var container = new LoadoutGroupContainer(profile, loadout, protoManager.Index(group), session, collection);
|
||||||
LoadoutGroupsContainer.AddTab(container, Loc.GetString(groupProto.Name));
|
LoadoutGroupsContainer.AddTab(container, Loc.GetString(groupProto.Name));
|
||||||
_groups.Add(container);
|
_groups.Add(container);
|
||||||
|
|
||||||
@@ -43,18 +45,11 @@ public sealed partial class LoadoutWindow : FancyWindow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Close()
|
|
||||||
{
|
|
||||||
base.Close();
|
|
||||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
|
||||||
controller.SetDummyJob(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RefreshLoadouts(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection)
|
public void RefreshLoadouts(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection)
|
||||||
{
|
{
|
||||||
foreach (var group in _groups)
|
foreach (var group in _groups)
|
||||||
{
|
{
|
||||||
group.RefreshLoadouts(loadout, session, collection);
|
group.RefreshLoadouts(Profile, loadout, session, collection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,12 +10,16 @@ namespace Content.Client.Lobby.UI;
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class LobbyCharacterPreviewPanel : Control
|
public sealed partial class LobbyCharacterPreviewPanel : Control
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
|
||||||
public Button CharacterSetupButton => CharacterSetup;
|
public Button CharacterSetupButton => CharacterSetup;
|
||||||
|
|
||||||
|
private EntityUid? _previewDummy;
|
||||||
|
|
||||||
public LobbyCharacterPreviewPanel()
|
public LobbyCharacterPreviewPanel()
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
UserInterfaceManager.GetUIController<LobbyUIController>().SetPreviewPanel(this);
|
IoCManager.InjectDependencies(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLoaded(bool value)
|
public void SetLoaded(bool value)
|
||||||
@@ -26,11 +30,18 @@ public sealed partial class LobbyCharacterPreviewPanel : Control
|
|||||||
|
|
||||||
public void SetSummaryText(string value)
|
public void SetSummaryText(string value)
|
||||||
{
|
{
|
||||||
Summary.Text = string.Empty;
|
Summary.Text = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetSprite(EntityUid uid)
|
public void SetSprite(EntityUid uid)
|
||||||
{
|
{
|
||||||
|
if (_previewDummy != null)
|
||||||
|
{
|
||||||
|
_entManager.DeleteEntity(_previewDummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
_previewDummy = uid;
|
||||||
|
|
||||||
ViewBox.DisposeAllChildren();
|
ViewBox.DisposeAllChildren();
|
||||||
var spriteView = new SpriteView
|
var spriteView = new SpriteView
|
||||||
{
|
{
|
||||||
@@ -42,4 +53,11 @@ public sealed partial class LobbyCharacterPreviewPanel : Control
|
|||||||
spriteView.SetEntity(uid);
|
spriteView.SetEntity(uid);
|
||||||
ViewBox.AddChild(spriteView);
|
ViewBox.AddChild(spriteView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
_entManager.DeleteEntity(_previewDummy);
|
||||||
|
_previewDummy = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,9 @@ using Robust.Client.UserInterface.XAML;
|
|||||||
namespace Content.Client.Lobby.UI
|
namespace Content.Client.Lobby.UI
|
||||||
{
|
{
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
internal sealed partial class LobbyGui : UIScreen
|
public sealed partial class LobbyGui : UIScreen
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
|
||||||
|
|
||||||
public LobbyGui()
|
public LobbyGui()
|
||||||
{
|
{
|
||||||
@@ -23,7 +22,7 @@ namespace Content.Client.Lobby.UI
|
|||||||
LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text"));
|
LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text"));
|
||||||
|
|
||||||
LeaveButton.OnPressed += _ => _consoleHost.ExecuteCommand("disconnect");
|
LeaveButton.OnPressed += _ => _consoleHost.ExecuteCommand("disconnect");
|
||||||
OptionsButton.OnPressed += _ => _userInterfaceManager.GetUIController<OptionsUIController>().ToggleWindow();
|
OptionsButton.OnPressed += _ => UserInterfaceManager.GetUIController<OptionsUIController>().ToggleWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SwitchState(LobbyGuiState state)
|
public void SwitchState(LobbyGuiState state)
|
||||||
@@ -40,7 +39,7 @@ namespace Content.Client.Lobby.UI
|
|||||||
case LobbyGuiState.CharacterSetup:
|
case LobbyGuiState.CharacterSetup:
|
||||||
CharacterSetupState.Visible = true;
|
CharacterSetupState.Visible = true;
|
||||||
|
|
||||||
var actualWidth = (float) _userInterfaceManager.RootControl.PixelWidth;
|
var actualWidth = (float) UserInterfaceManager.RootControl.PixelWidth;
|
||||||
var setupWidth = (float) LeftSide.PixelWidth;
|
var setupWidth = (float) LeftSide.PixelWidth;
|
||||||
|
|
||||||
if (1 - (setupWidth / actualWidth) > 0.30)
|
if (1 - (setupWidth / actualWidth) > 0.30)
|
||||||
@@ -48,6 +47,8 @@ namespace Content.Client.Lobby.UI
|
|||||||
RightSide.Visible = false;
|
RightSide.Visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UserInterfaceManager.GetUIController<LobbyUIController>().ReloadCharacterSetup();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,23 +2,20 @@ using JetBrains.Annotations;
|
|||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
|
|
||||||
namespace Content.Client.Lobby.UI
|
namespace Content.Client.Lobby.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed partial class ObserveWarningWindow : DefaultWindow
|
||||||
{
|
{
|
||||||
[GenerateTypedNameReferences]
|
public ObserveWarningWindow()
|
||||||
[UsedImplicitly]
|
|
||||||
internal sealed partial class ObserveWarningWindow : DefaultWindow
|
|
||||||
{
|
{
|
||||||
public ObserveWarningWindow()
|
Title = Loc.GetString("observe-warning-window-title");
|
||||||
{
|
RobustXamlLoader.Load(this);
|
||||||
Title = Loc.GetString("observe-warning-window-title");
|
IoCManager.InjectDependencies(this);
|
||||||
RobustXamlLoader.Load(this);
|
|
||||||
IoCManager.InjectDependencies(this);
|
|
||||||
|
|
||||||
ObserveButton.OnPressed += _ => { this.Close(); };
|
ObserveButton.OnPressed += _ => { this.Close(); };
|
||||||
NevermindButton.OnPressed += _ => { this.Close(); };
|
NevermindButton.OnPressed += _ => { this.Close(); };
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml
Normal file
9
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<BoxContainer xmlns="https://spacestation14.io"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Label Name="TitleLabel"
|
||||||
|
Margin="5 0"
|
||||||
|
MouseFilter="Stop"/>
|
||||||
|
<BoxContainer Name="OptionsContainer"
|
||||||
|
SetWidth="400"/>
|
||||||
|
</BoxContainer>
|
||||||
118
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs
Normal file
118
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Lobby.UI.Roles;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A generic locking selector.
|
||||||
|
/// </summary>
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class RequirementsSelector : BoxContainer
|
||||||
|
{
|
||||||
|
private readonly RadioOptions<int> _options;
|
||||||
|
private readonly StripeBack _lockStripe;
|
||||||
|
|
||||||
|
public event Action<int>? OnSelected;
|
||||||
|
|
||||||
|
public int Selected => _options.SelectedId;
|
||||||
|
|
||||||
|
public RequirementsSelector()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
_options = new RadioOptions<int>(RadioOptionsLayout.Horizontal)
|
||||||
|
{
|
||||||
|
FirstButtonStyle = StyleBase.ButtonOpenRight,
|
||||||
|
ButtonStyle = StyleBase.ButtonOpenBoth,
|
||||||
|
LastButtonStyle = StyleBase.ButtonOpenLeft,
|
||||||
|
HorizontalExpand = true,
|
||||||
|
};
|
||||||
|
//Override default radio option button width
|
||||||
|
_options.GenerateItem = GenerateButton;
|
||||||
|
|
||||||
|
_options.OnItemSelected += args =>
|
||||||
|
{
|
||||||
|
_options.Select(args.Id);
|
||||||
|
OnSelected?.Invoke(args.Id);
|
||||||
|
};
|
||||||
|
|
||||||
|
var requirementsLabel = new Label()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("role-timer-locked"),
|
||||||
|
Visible = true,
|
||||||
|
HorizontalAlignment = HAlignment.Center,
|
||||||
|
StyleClasses = {StyleBase.StyleClassLabelSubText},
|
||||||
|
};
|
||||||
|
|
||||||
|
_lockStripe = new StripeBack()
|
||||||
|
{
|
||||||
|
Visible = false,
|
||||||
|
HorizontalExpand = true,
|
||||||
|
HasMargins = false,
|
||||||
|
MouseFilter = MouseFilterMode.Stop,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
requirementsLabel
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Actually adds the controls.
|
||||||
|
/// </summary>
|
||||||
|
public void Setup((string, int)[] items, string title, int titleSize, string? description, TextureRect? icon = null)
|
||||||
|
{
|
||||||
|
foreach (var (text, value) in items)
|
||||||
|
{
|
||||||
|
_options.AddItem(Loc.GetString(text), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleLabel.Text = title;
|
||||||
|
TitleLabel.MinSize = new Vector2(titleSize, 0f);
|
||||||
|
TitleLabel.ToolTip = description;
|
||||||
|
|
||||||
|
if (icon != null)
|
||||||
|
{
|
||||||
|
AddChild(icon);
|
||||||
|
icon.SetPositionFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
OptionsContainer.AddChild(_options);
|
||||||
|
OptionsContainer.AddChild(_lockStripe);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LockRequirements(FormattedMessage requirements)
|
||||||
|
{
|
||||||
|
var tooltip = new Tooltip();
|
||||||
|
tooltip.SetMessage(requirements);
|
||||||
|
_lockStripe.TooltipSupplier = _ => tooltip;
|
||||||
|
_lockStripe.Visible = true;
|
||||||
|
_options.Visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnlockRequirements()
|
||||||
|
{
|
||||||
|
_lockStripe.Visible = false;
|
||||||
|
_options.Visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Button GenerateButton(string text, int value)
|
||||||
|
{
|
||||||
|
return new Button
|
||||||
|
{
|
||||||
|
Text = text,
|
||||||
|
MinWidth = 90,
|
||||||
|
HorizontalExpand = true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Select(int id)
|
||||||
|
{
|
||||||
|
_options.Select(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<Control xmlns="https://spacestation14.io"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||||
|
<BoxContainer Name="Container"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<CheckBox Name="Checkbox"/>
|
||||||
|
</BoxContainer>
|
||||||
|
</Control>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using Content.Shared.Traits;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.Lobby.UI.Roles;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class TraitPreferenceSelector : Control
|
||||||
|
{
|
||||||
|
public bool Preference
|
||||||
|
{
|
||||||
|
get => Checkbox.Pressed;
|
||||||
|
set => Checkbox.Pressed = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event Action<bool>? PreferenceChanged;
|
||||||
|
|
||||||
|
public TraitPreferenceSelector(TraitPrototype trait)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
Checkbox.Text = Loc.GetString(trait.Name);
|
||||||
|
Checkbox.OnToggled += OnCheckBoxToggled;
|
||||||
|
|
||||||
|
if (trait.Description is { } desc)
|
||||||
|
{
|
||||||
|
Checkbox.ToolTip = Loc.GetString(desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCheckBoxToggled(BaseButton.ButtonToggledEventArgs args)
|
||||||
|
{
|
||||||
|
PreferenceChanged?.Invoke(Preference);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
using Content.Client.Players.PlayTimeTracking;
|
|
||||||
using Content.Shared.Roles;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
|
|
||||||
namespace Content.Client.Preferences.UI;
|
|
||||||
|
|
||||||
public sealed class AntagPreferenceSelector : RequirementsSelector<AntagPrototype>
|
|
||||||
{
|
|
||||||
// 0 is yes and 1 is no
|
|
||||||
public bool Preference
|
|
||||||
{
|
|
||||||
get => Options.SelectedValue == 0;
|
|
||||||
set => Options.Select((value && !Disabled) ? 0 : 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action<bool>? PreferenceChanged;
|
|
||||||
|
|
||||||
public AntagPreferenceSelector(AntagPrototype proto, ButtonGroup btnGroup)
|
|
||||||
: base(proto, btnGroup)
|
|
||||||
{
|
|
||||||
Options.OnItemSelected += args => PreferenceChanged?.Invoke(Preference);
|
|
||||||
|
|
||||||
var items = new[]
|
|
||||||
{
|
|
||||||
("humanoid-profile-editor-antag-preference-yes-button", 0),
|
|
||||||
("humanoid-profile-editor-antag-preference-no-button", 1)
|
|
||||||
};
|
|
||||||
var title = Loc.GetString(proto.Name);
|
|
||||||
var description = Loc.GetString(proto.Objective);
|
|
||||||
// Not supported yet get fucked.
|
|
||||||
Setup(null, items, title, 250, description);
|
|
||||||
|
|
||||||
// immediately lock requirements if they arent met.
|
|
||||||
// another function checks Disabled after creating the selector so this has to be done now
|
|
||||||
var requirements = IoCManager.Resolve<JobRequirementsManager>();
|
|
||||||
if (proto.Requirements != null && !requirements.CheckRoleTime(proto.Requirements, out var reason))
|
|
||||||
{
|
|
||||||
LockRequirements(reason);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,276 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
|
||||||
using Content.Client.Humanoid;
|
|
||||||
using Content.Client.Info;
|
|
||||||
using Content.Client.Info.PlaytimeStats;
|
|
||||||
using Content.Client.Lobby;
|
|
||||||
using Content.Client.Resources;
|
|
||||||
using Content.Client.Stylesheets;
|
|
||||||
using Content.Shared.Clothing;
|
|
||||||
using Content.Shared.Humanoid;
|
|
||||||
using Content.Shared.Humanoid.Prototypes;
|
|
||||||
using Content.Shared.Preferences;
|
|
||||||
using Content.Shared.Preferences.Loadouts;
|
|
||||||
using Content.Shared.Roles;
|
|
||||||
using Robust.Client.AutoGenerated;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.ResourceManagement;
|
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Robust.Client.UserInterface.XAML;
|
|
||||||
using Robust.Shared.Configuration;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
|
||||||
using Direction = Robust.Shared.Maths.Direction;
|
|
||||||
|
|
||||||
namespace Content.Client.Preferences.UI
|
|
||||||
{
|
|
||||||
[GenerateTypedNameReferences]
|
|
||||||
public sealed partial class CharacterSetupGui : Control
|
|
||||||
{
|
|
||||||
private readonly IClientPreferencesManager _preferencesManager;
|
|
||||||
private readonly IEntityManager _entityManager;
|
|
||||||
private readonly IPrototypeManager _prototypeManager;
|
|
||||||
private readonly Button _createNewCharacterButton;
|
|
||||||
private readonly HumanoidProfileEditor _humanoidProfileEditor;
|
|
||||||
|
|
||||||
public CharacterSetupGui(
|
|
||||||
IEntityManager entityManager,
|
|
||||||
IResourceCache resourceCache,
|
|
||||||
IClientPreferencesManager preferencesManager,
|
|
||||||
IPrototypeManager prototypeManager,
|
|
||||||
IConfigurationManager configurationManager)
|
|
||||||
{
|
|
||||||
RobustXamlLoader.Load(this);
|
|
||||||
_entityManager = entityManager;
|
|
||||||
_prototypeManager = prototypeManager;
|
|
||||||
_preferencesManager = preferencesManager;
|
|
||||||
|
|
||||||
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
|
||||||
var back = new StyleBoxTexture
|
|
||||||
{
|
|
||||||
Texture = panelTex,
|
|
||||||
Modulate = new Color(37, 37, 42)
|
|
||||||
};
|
|
||||||
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
|
||||||
|
|
||||||
BackgroundPanel.PanelOverride = back;
|
|
||||||
|
|
||||||
_createNewCharacterButton = new Button
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("character-setup-gui-create-new-character-button"),
|
|
||||||
};
|
|
||||||
_createNewCharacterButton.OnPressed += args =>
|
|
||||||
{
|
|
||||||
preferencesManager.CreateCharacter(HumanoidCharacterProfile.Random());
|
|
||||||
UpdateUI();
|
|
||||||
args.Event.Handle();
|
|
||||||
};
|
|
||||||
|
|
||||||
_humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager, configurationManager);
|
|
||||||
_humanoidProfileEditor.OnProfileChanged += ProfileChanged;
|
|
||||||
CharEditor.AddChild(_humanoidProfileEditor);
|
|
||||||
|
|
||||||
UpdateUI();
|
|
||||||
|
|
||||||
RulesButton.OnPressed += _ => new RulesAndInfoWindow().Open();
|
|
||||||
|
|
||||||
StatsButton.OnPressed += _ => new PlaytimeStatsWindow().OpenCentered();
|
|
||||||
preferencesManager.OnServerDataLoaded += UpdateUI;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
base.Dispose(disposing);
|
|
||||||
if (!disposing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_preferencesManager.OnServerDataLoaded -= UpdateUI;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Save() => _humanoidProfileEditor.Save();
|
|
||||||
|
|
||||||
private void ProfileChanged(ICharacterProfile profile, int profileSlot)
|
|
||||||
{
|
|
||||||
_humanoidProfileEditor.UpdateControls();
|
|
||||||
UpdateUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateControls()
|
|
||||||
{
|
|
||||||
// Reset sliders etc. upon going going back to GUI.
|
|
||||||
_humanoidProfileEditor.LoadServerData();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateUI()
|
|
||||||
{
|
|
||||||
var numberOfFullSlots = 0;
|
|
||||||
var characterButtonsGroup = new ButtonGroup();
|
|
||||||
Characters.RemoveAllChildren();
|
|
||||||
|
|
||||||
if (!_preferencesManager.ServerDataLoaded)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_createNewCharacterButton.ToolTip =
|
|
||||||
Loc.GetString("character-setup-gui-create-new-character-button-tooltip",
|
|
||||||
("maxCharacters", _preferencesManager.Settings!.MaxCharacterSlots));
|
|
||||||
|
|
||||||
foreach (var (slot, character) in _preferencesManager.Preferences!.Characters)
|
|
||||||
{
|
|
||||||
numberOfFullSlots++;
|
|
||||||
var characterPickerButton = new CharacterPickerButton(_entityManager,
|
|
||||||
_preferencesManager,
|
|
||||||
_prototypeManager,
|
|
||||||
characterButtonsGroup,
|
|
||||||
character);
|
|
||||||
Characters.AddChild(characterPickerButton);
|
|
||||||
|
|
||||||
var characterIndexCopy = slot;
|
|
||||||
characterPickerButton.OnPressed += args =>
|
|
||||||
{
|
|
||||||
_humanoidProfileEditor.Profile = (HumanoidCharacterProfile)character;
|
|
||||||
_humanoidProfileEditor.CharacterSlot = characterIndexCopy;
|
|
||||||
_humanoidProfileEditor.UpdateControls();
|
|
||||||
_preferencesManager.SelectCharacter(character);
|
|
||||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
|
||||||
controller.UpdateProfile(_humanoidProfileEditor.Profile);
|
|
||||||
controller.ReloadCharacterUI();
|
|
||||||
UpdateUI();
|
|
||||||
args.Event.Handle();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_createNewCharacterButton.Disabled =
|
|
||||||
numberOfFullSlots >= _preferencesManager.Settings.MaxCharacterSlots;
|
|
||||||
Characters.AddChild(_createNewCharacterButton);
|
|
||||||
// TODO: Move this shit to the Lobby UI controller
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shows individual characters on the side of the character GUI.
|
|
||||||
/// </summary>
|
|
||||||
private sealed class CharacterPickerButton : ContainerButton
|
|
||||||
{
|
|
||||||
private EntityUid _previewDummy;
|
|
||||||
|
|
||||||
public CharacterPickerButton(
|
|
||||||
IEntityManager entityManager,
|
|
||||||
IClientPreferencesManager preferencesManager,
|
|
||||||
IPrototypeManager prototypeManager,
|
|
||||||
ButtonGroup group,
|
|
||||||
ICharacterProfile profile)
|
|
||||||
{
|
|
||||||
AddStyleClass(StyleClassButton);
|
|
||||||
ToggleMode = true;
|
|
||||||
Group = group;
|
|
||||||
|
|
||||||
var humanoid = profile as HumanoidCharacterProfile;
|
|
||||||
if (humanoid is not null)
|
|
||||||
{
|
|
||||||
var dummy = prototypeManager.Index<SpeciesPrototype>(humanoid.Species).DollPrototype;
|
|
||||||
_previewDummy = entityManager.SpawnEntity(dummy, MapCoordinates.Nullspace);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_previewDummy = entityManager.SpawnEntity(prototypeManager.Index<SpeciesPrototype>(SharedHumanoidAppearanceSystem.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
|
|
||||||
}
|
|
||||||
|
|
||||||
EntitySystem.Get<HumanoidAppearanceSystem>().LoadProfile(_previewDummy, (HumanoidCharacterProfile)profile);
|
|
||||||
|
|
||||||
if (humanoid != null)
|
|
||||||
{
|
|
||||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
|
||||||
var job = controller.GetPreferredJob(humanoid);
|
|
||||||
controller.GiveDummyJobClothes(_previewDummy, humanoid, job);
|
|
||||||
|
|
||||||
if (prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
|
|
||||||
{
|
|
||||||
var loadout = humanoid.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), entityManager, prototypeManager);
|
|
||||||
controller.GiveDummyLoadout(_previewDummy, loadout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var isSelectedCharacter = profile == preferencesManager.Preferences?.SelectedCharacter;
|
|
||||||
|
|
||||||
if (isSelectedCharacter)
|
|
||||||
Pressed = true;
|
|
||||||
|
|
||||||
var view = new SpriteView
|
|
||||||
{
|
|
||||||
Scale = new Vector2(2, 2),
|
|
||||||
OverrideDirection = Direction.South
|
|
||||||
};
|
|
||||||
view.SetEntity(_previewDummy);
|
|
||||||
|
|
||||||
var description = profile.Name;
|
|
||||||
|
|
||||||
var highPriorityJob = humanoid?.JobPriorities.SingleOrDefault(p => p.Value == JobPriority.High).Key;
|
|
||||||
if (highPriorityJob != null)
|
|
||||||
{
|
|
||||||
var jobName = IoCManager.Resolve<IPrototypeManager>().Index<JobPrototype>(highPriorityJob).LocalizedName;
|
|
||||||
description = $"{description}\n{jobName}";
|
|
||||||
}
|
|
||||||
|
|
||||||
var descriptionLabel = new Label
|
|
||||||
{
|
|
||||||
Text = description,
|
|
||||||
ClipText = true,
|
|
||||||
HorizontalExpand = true
|
|
||||||
};
|
|
||||||
var deleteButton = new Button
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("character-setup-gui-character-picker-button-delete-button"),
|
|
||||||
Visible = !isSelectedCharacter,
|
|
||||||
};
|
|
||||||
var confirmDeleteButton = new Button
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("character-setup-gui-character-picker-button-confirm-delete-button"),
|
|
||||||
Visible = false,
|
|
||||||
};
|
|
||||||
confirmDeleteButton.ModulateSelfOverride = StyleNano.ButtonColorCautionDefault;
|
|
||||||
confirmDeleteButton.OnPressed += _ =>
|
|
||||||
{
|
|
||||||
Parent?.RemoveChild(this);
|
|
||||||
Parent?.RemoveChild(confirmDeleteButton);
|
|
||||||
preferencesManager.DeleteCharacter(profile);
|
|
||||||
};
|
|
||||||
deleteButton.OnPressed += _ =>
|
|
||||||
{
|
|
||||||
|
|
||||||
deleteButton.Visible = false;
|
|
||||||
confirmDeleteButton.Visible = true;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
var internalHBox = new BoxContainer
|
|
||||||
{
|
|
||||||
Orientation = LayoutOrientation.Horizontal,
|
|
||||||
HorizontalExpand = true,
|
|
||||||
SeparationOverride = 0,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
view,
|
|
||||||
descriptionLabel,
|
|
||||||
deleteButton,
|
|
||||||
confirmDeleteButton
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
AddChild(internalHBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
base.Dispose(disposing);
|
|
||||||
if (!disposing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
IoCManager.Resolve<IEntityManager>().DeleteEntity(_previewDummy);
|
|
||||||
_previewDummy = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
using Content.Shared.Preferences;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Client.Preferences.UI
|
|
||||||
{
|
|
||||||
public sealed partial class HumanoidProfileEditor
|
|
||||||
{
|
|
||||||
private void RandomizeEverything()
|
|
||||||
{
|
|
||||||
Profile = HumanoidCharacterProfile.Random();
|
|
||||||
UpdateControls();
|
|
||||||
IsDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RandomizeName()
|
|
||||||
{
|
|
||||||
if (Profile == null) return;
|
|
||||||
var name = HumanoidCharacterProfile.GetName(Profile.Species, Profile.Gender);
|
|
||||||
SetName(name);
|
|
||||||
UpdateNameEdit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Content.Shared.Preferences;
|
|
||||||
using Content.Shared.Preferences.Loadouts;
|
|
||||||
using Content.Shared.Preferences.Loadouts.Effects;
|
|
||||||
using Content.Shared.Roles;
|
|
||||||
using Content.Shared.StatusIcon;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Robust.Client.Utility;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
|
|
||||||
namespace Content.Client.Preferences.UI;
|
|
||||||
|
|
||||||
public sealed class JobPrioritySelector : RequirementsSelector<JobPrototype>
|
|
||||||
{
|
|
||||||
public JobPriority Priority
|
|
||||||
{
|
|
||||||
get => (JobPriority) Options.SelectedValue;
|
|
||||||
set => Options.SelectByValue((int) value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public event Action<JobPriority>? PriorityChanged;
|
|
||||||
|
|
||||||
public JobPrioritySelector(RoleLoadout? loadout, JobPrototype proto, ButtonGroup btnGroup, IPrototypeManager protoMan)
|
|
||||||
: base(proto, btnGroup)
|
|
||||||
{
|
|
||||||
Options.OnItemSelected += args => PriorityChanged?.Invoke(Priority);
|
|
||||||
|
|
||||||
var items = new[]
|
|
||||||
{
|
|
||||||
("humanoid-profile-editor-job-priority-high-button", (int) JobPriority.High),
|
|
||||||
("humanoid-profile-editor-job-priority-medium-button", (int) JobPriority.Medium),
|
|
||||||
("humanoid-profile-editor-job-priority-low-button", (int) JobPriority.Low),
|
|
||||||
("humanoid-profile-editor-job-priority-never-button", (int) JobPriority.Never),
|
|
||||||
};
|
|
||||||
|
|
||||||
var icon = new TextureRect
|
|
||||||
{
|
|
||||||
TextureScale = new Vector2(2, 2),
|
|
||||||
VerticalAlignment = VAlignment.Center
|
|
||||||
};
|
|
||||||
var jobIcon = protoMan.Index<StatusIconPrototype>(proto.Icon);
|
|
||||||
icon.Texture = jobIcon.Icon.Frame0();
|
|
||||||
|
|
||||||
Setup(loadout, items, proto.LocalizedName, 200, proto.LocalizedDescription, icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Content.Client.Lobby;
|
|
||||||
using Content.Client.Stylesheets;
|
|
||||||
using Content.Client.UserInterface.Controls;
|
|
||||||
using Content.Shared.Clothing;
|
|
||||||
using Content.Shared.Preferences.Loadouts;
|
|
||||||
using Content.Shared.Preferences.Loadouts.Effects;
|
|
||||||
using Content.Shared.Roles;
|
|
||||||
using Robust.Client.Player;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Client.Preferences.UI;
|
|
||||||
|
|
||||||
public abstract class RequirementsSelector<T> : BoxContainer where T : IPrototype
|
|
||||||
{
|
|
||||||
private ButtonGroup _loadoutGroup;
|
|
||||||
|
|
||||||
public T Proto { get; }
|
|
||||||
public bool Disabled => _lockStripe.Visible;
|
|
||||||
|
|
||||||
protected readonly RadioOptions<int> Options;
|
|
||||||
private readonly StripeBack _lockStripe;
|
|
||||||
private LoadoutWindow? _loadoutWindow;
|
|
||||||
|
|
||||||
private RoleLoadout? _loadout;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised if a loadout has been updated.
|
|
||||||
/// </summary>
|
|
||||||
public event Action<RoleLoadout>? LoadoutUpdated;
|
|
||||||
|
|
||||||
protected RequirementsSelector(T proto, ButtonGroup loadoutGroup)
|
|
||||||
{
|
|
||||||
_loadoutGroup = loadoutGroup;
|
|
||||||
Proto = proto;
|
|
||||||
|
|
||||||
Options = new RadioOptions<int>(RadioOptionsLayout.Horizontal)
|
|
||||||
{
|
|
||||||
FirstButtonStyle = StyleBase.ButtonOpenRight,
|
|
||||||
ButtonStyle = StyleBase.ButtonOpenBoth,
|
|
||||||
LastButtonStyle = StyleBase.ButtonOpenLeft,
|
|
||||||
HorizontalExpand = true,
|
|
||||||
};
|
|
||||||
//Override default radio option button width
|
|
||||||
Options.GenerateItem = GenerateButton;
|
|
||||||
|
|
||||||
Options.OnItemSelected += args => Options.Select(args.Id);
|
|
||||||
|
|
||||||
var requirementsLabel = new Label()
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("role-timer-locked"),
|
|
||||||
Visible = true,
|
|
||||||
HorizontalAlignment = HAlignment.Center,
|
|
||||||
StyleClasses = {StyleBase.StyleClassLabelSubText},
|
|
||||||
};
|
|
||||||
|
|
||||||
_lockStripe = new StripeBack()
|
|
||||||
{
|
|
||||||
Visible = false,
|
|
||||||
HorizontalExpand = true,
|
|
||||||
HasMargins = false,
|
|
||||||
MouseFilter = MouseFilterMode.Stop,
|
|
||||||
Children =
|
|
||||||
{
|
|
||||||
requirementsLabel
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Setup must be called after
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Actually adds the controls, must be called in the inheriting class' constructor.
|
|
||||||
/// </summary>
|
|
||||||
protected void Setup(RoleLoadout? loadout, (string, int)[] items, string title, int titleSize, string? description, TextureRect? icon = null)
|
|
||||||
{
|
|
||||||
_loadout = loadout;
|
|
||||||
|
|
||||||
foreach (var (text, value) in items)
|
|
||||||
{
|
|
||||||
Options.AddItem(Loc.GetString(text), value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var titleLabel = new Label()
|
|
||||||
{
|
|
||||||
Margin = new Thickness(5f, 0, 5f, 0),
|
|
||||||
Text = title,
|
|
||||||
MinSize = new Vector2(titleSize, 0),
|
|
||||||
MouseFilter = MouseFilterMode.Stop,
|
|
||||||
ToolTip = description
|
|
||||||
};
|
|
||||||
|
|
||||||
if (icon != null)
|
|
||||||
AddChild(icon);
|
|
||||||
|
|
||||||
AddChild(titleLabel);
|
|
||||||
AddChild(Options);
|
|
||||||
AddChild(_lockStripe);
|
|
||||||
|
|
||||||
var loadoutWindowBtn = new Button()
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("loadout-window"),
|
|
||||||
HorizontalAlignment = HAlignment.Right,
|
|
||||||
Group = _loadoutGroup,
|
|
||||||
Margin = new Thickness(3f, 0f, 0f, 0f),
|
|
||||||
};
|
|
||||||
|
|
||||||
var collection = IoCManager.Instance!;
|
|
||||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
|
||||||
|
|
||||||
// If no loadout found then disabled button
|
|
||||||
if (!protoManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(Proto.ID)))
|
|
||||||
{
|
|
||||||
loadoutWindowBtn.Disabled = true;
|
|
||||||
}
|
|
||||||
// else
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var session = collection.Resolve<IPlayerManager>().LocalSession!;
|
|
||||||
// TODO: Most of lobby state should be a uicontroller
|
|
||||||
// trying to handle all this shit is a big-ass mess.
|
|
||||||
// Every time I touch it I try to make it slightly better but it needs a howitzer dropped on it.
|
|
||||||
loadoutWindowBtn.OnPressed += args =>
|
|
||||||
{
|
|
||||||
if (args.Button.Pressed)
|
|
||||||
{
|
|
||||||
// We only create a loadout when necessary to avoid unnecessary DB entries.
|
|
||||||
_loadout ??= new RoleLoadout(LoadoutSystem.GetJobPrototype(Proto.ID));
|
|
||||||
_loadout.SetDefault(protoManager);
|
|
||||||
|
|
||||||
_loadoutWindow = new LoadoutWindow(_loadout, protoManager.Index(_loadout.Role), session, collection)
|
|
||||||
{
|
|
||||||
Title = Loc.GetString(Proto.ID + "-loadout"),
|
|
||||||
};
|
|
||||||
|
|
||||||
_loadoutWindow.RefreshLoadouts(_loadout, session, collection);
|
|
||||||
|
|
||||||
// If it's a job preview then refresh it.
|
|
||||||
if (Proto is JobPrototype jobProto)
|
|
||||||
{
|
|
||||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
|
||||||
controller.SetDummyJob(jobProto);
|
|
||||||
}
|
|
||||||
|
|
||||||
_loadoutWindow.OnLoadoutUnpressed += (selectedGroup, selectedLoadout) =>
|
|
||||||
{
|
|
||||||
if (!_loadout.RemoveLoadout(selectedGroup, selectedLoadout, protoManager))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_loadout.EnsureValid(session, collection);
|
|
||||||
_loadoutWindow.RefreshLoadouts(_loadout, session, collection);
|
|
||||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
|
||||||
controller.ReloadProfile();
|
|
||||||
LoadoutUpdated?.Invoke(_loadout);
|
|
||||||
};
|
|
||||||
|
|
||||||
_loadoutWindow.OnLoadoutPressed += (selectedGroup, selectedLoadout) =>
|
|
||||||
{
|
|
||||||
if (!_loadout.AddLoadout(selectedGroup, selectedLoadout, protoManager))
|
|
||||||
return;
|
|
||||||
|
|
||||||
_loadout.EnsureValid(session, collection);
|
|
||||||
_loadoutWindow.RefreshLoadouts(_loadout, session, collection);
|
|
||||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
|
||||||
controller.ReloadProfile();
|
|
||||||
LoadoutUpdated?.Invoke(_loadout);
|
|
||||||
};
|
|
||||||
|
|
||||||
_loadoutWindow.OpenCenteredLeft();
|
|
||||||
_loadoutWindow.OnClose += () =>
|
|
||||||
{
|
|
||||||
loadoutWindowBtn.Pressed = false;
|
|
||||||
_loadoutWindow?.Dispose();
|
|
||||||
_loadoutWindow = null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CloseLoadout();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
AddChild(loadoutWindowBtn);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CloseLoadout()
|
|
||||||
{
|
|
||||||
_loadoutWindow?.Close();
|
|
||||||
_loadoutWindow?.Dispose();
|
|
||||||
_loadoutWindow = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LockRequirements(FormattedMessage requirements)
|
|
||||||
{
|
|
||||||
var tooltip = new Tooltip();
|
|
||||||
tooltip.SetMessage(requirements);
|
|
||||||
_lockStripe.TooltipSupplier = _ => tooltip;
|
|
||||||
_lockStripe.Visible = true;
|
|
||||||
Options.Visible = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Subscribe to roletimers event. I am too lazy to do this RN But I doubt most people will notice fn
|
|
||||||
public void UnlockRequirements()
|
|
||||||
{
|
|
||||||
_lockStripe.Visible = false;
|
|
||||||
Options.Visible = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Button GenerateButton(string text, int value)
|
|
||||||
{
|
|
||||||
return new Button
|
|
||||||
{
|
|
||||||
Text = text,
|
|
||||||
MinWidth = 90,
|
|
||||||
HorizontalExpand = true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using Content.Client.Lobby;
|
using Content.Client.Lobby;
|
||||||
using Content.Client.Preferences;
|
|
||||||
using Content.Server.Preferences.Managers;
|
using Content.Server.Preferences.Managers;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
using Robust.Client.State;
|
using Robust.Client.State;
|
||||||
|
|||||||
@@ -39,32 +39,21 @@ namespace Content.IntegrationTests.Tests.Preferences
|
|||||||
|
|
||||||
private static HumanoidCharacterProfile CharlieCharlieson()
|
private static HumanoidCharacterProfile CharlieCharlieson()
|
||||||
{
|
{
|
||||||
return new(
|
return new()
|
||||||
"Charlie Charlieson",
|
{
|
||||||
"The biggest boy around.",
|
Name = "Charlie Charlieson",
|
||||||
"Human",
|
FlavorText = "The biggest boy around.",
|
||||||
21,
|
Species = "Human",
|
||||||
Sex.Male,
|
Age = 21,
|
||||||
Gender.Epicene,
|
Appearance = new(
|
||||||
new HumanoidCharacterAppearance(
|
|
||||||
"Afro",
|
"Afro",
|
||||||
Color.Aqua,
|
Color.Aqua,
|
||||||
"Shaved",
|
"Shaved",
|
||||||
Color.Aquamarine,
|
Color.Aquamarine,
|
||||||
Color.Azure,
|
Color.Azure,
|
||||||
Color.Beige,
|
Color.Beige,
|
||||||
new ()
|
new ())
|
||||||
),
|
};
|
||||||
SpawnPriorityPreference.None,
|
|
||||||
new Dictionary<string, JobPriority>
|
|
||||||
{
|
|
||||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High}
|
|
||||||
},
|
|
||||||
PreferenceUnavailableMode.StayInLobby,
|
|
||||||
new List<string> (),
|
|
||||||
new List<string>(),
|
|
||||||
new Dictionary<string, RoleLoadout>()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ServerDbSqlite GetDb(RobustIntegrationTest.ServerIntegrationInstance server)
|
private static ServerDbSqlite GetDb(RobustIntegrationTest.ServerIntegrationInstance server)
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ using Content.Shared.Humanoid;
|
|||||||
using Content.Shared.Humanoid.Markings;
|
using Content.Shared.Humanoid.Markings;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
using Content.Shared.Preferences.Loadouts;
|
using Content.Shared.Preferences.Loadouts;
|
||||||
using Content.Shared.Preferences.Loadouts.Effects;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Robust.Shared.Enums;
|
using Robust.Shared.Enums;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
@@ -253,8 +252,8 @@ namespace Content.Server.Database
|
|||||||
spawnPriority,
|
spawnPriority,
|
||||||
jobs,
|
jobs,
|
||||||
(PreferenceUnavailableMode) profile.PreferenceUnavailable,
|
(PreferenceUnavailableMode) profile.PreferenceUnavailable,
|
||||||
antags.ToList(),
|
antags.ToHashSet(),
|
||||||
traits.ToList(),
|
traits.ToHashSet(),
|
||||||
loadouts
|
loadouts
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,29 @@ namespace Content.Shared.Humanoid;
|
|||||||
|
|
||||||
[DataDefinition]
|
[DataDefinition]
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance
|
public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance, IEquatable<HumanoidCharacterAppearance>
|
||||||
{
|
{
|
||||||
|
[DataField("hair")]
|
||||||
|
public string HairStyleId { get; set; } = HairStyles.DefaultHairStyle;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public Color HairColor { get; set; } = Color.Black;
|
||||||
|
|
||||||
|
[DataField("facialHair")]
|
||||||
|
public string FacialHairStyleId { get; set; } = HairStyles.DefaultFacialHairStyle;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public Color FacialHairColor { get; set; } = Color.Black;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public Color EyeColor { get; set; } = Color.Black;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public Color SkinColor { get; set; } = Humanoid.SkinColor.ValidHumanSkinTone;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public List<Marking> Markings { get; set; } = new();
|
||||||
|
|
||||||
public HumanoidCharacterAppearance(string hairStyleId,
|
public HumanoidCharacterAppearance(string hairStyleId,
|
||||||
Color hairColor,
|
Color hairColor,
|
||||||
string facialHairStyleId,
|
string facialHairStyleId,
|
||||||
@@ -28,26 +49,11 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance
|
|||||||
Markings = markings;
|
Markings = markings;
|
||||||
}
|
}
|
||||||
|
|
||||||
[DataField("hair")]
|
public HumanoidCharacterAppearance(HumanoidCharacterAppearance other) :
|
||||||
public string HairStyleId { get; private set; }
|
this(other.HairStyleId, other.HairColor, other.FacialHairStyleId, other.FacialHairColor, other.EyeColor, other.SkinColor, new(other.Markings))
|
||||||
|
{
|
||||||
|
|
||||||
[DataField("hairColor")]
|
}
|
||||||
public Color HairColor { get; private set; }
|
|
||||||
|
|
||||||
[DataField("facialHair")]
|
|
||||||
public string FacialHairStyleId { get; private set; }
|
|
||||||
|
|
||||||
[DataField("facialHairColor")]
|
|
||||||
public Color FacialHairColor { get; private set; }
|
|
||||||
|
|
||||||
[DataField("eyeColor")]
|
|
||||||
public Color EyeColor { get; private set; }
|
|
||||||
|
|
||||||
[DataField("skinColor")]
|
|
||||||
public Color SkinColor { get; private set; }
|
|
||||||
|
|
||||||
[DataField("markings")]
|
|
||||||
public List<Marking> Markings { get; private set; }
|
|
||||||
|
|
||||||
public HumanoidCharacterAppearance WithHairStyleName(string newName)
|
public HumanoidCharacterAppearance WithHairStyleName(string newName)
|
||||||
{
|
{
|
||||||
@@ -84,18 +90,6 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance
|
|||||||
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, newMarkings);
|
return new(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, newMarkings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HumanoidCharacterAppearance() : this(
|
|
||||||
HairStyles.DefaultHairStyle,
|
|
||||||
Color.Black,
|
|
||||||
HairStyles.DefaultFacialHairStyle,
|
|
||||||
Color.Black,
|
|
||||||
Color.Black,
|
|
||||||
Humanoid.SkinColor.ValidHumanSkinTone,
|
|
||||||
new ()
|
|
||||||
)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HumanoidCharacterAppearance DefaultWithSpecies(string species)
|
public static HumanoidCharacterAppearance DefaultWithSpecies(string species)
|
||||||
{
|
{
|
||||||
var speciesPrototype = IoCManager.Resolve<IPrototypeManager>().Index<SpeciesPrototype>(species);
|
var speciesPrototype = IoCManager.Resolve<IPrototypeManager>().Index<SpeciesPrototype>(species);
|
||||||
@@ -245,4 +239,32 @@ public sealed partial class HumanoidCharacterAppearance : ICharacterAppearance
|
|||||||
if (!Markings.SequenceEqual(other.Markings)) return false;
|
if (!Markings.SequenceEqual(other.Markings)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Equals(HumanoidCharacterAppearance? other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, other)) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return HairStyleId == other.HairStyleId &&
|
||||||
|
HairColor.Equals(other.HairColor) &&
|
||||||
|
FacialHairStyleId == other.FacialHairStyleId &&
|
||||||
|
FacialHairColor.Equals(other.FacialHairColor) &&
|
||||||
|
EyeColor.Equals(other.EyeColor) &&
|
||||||
|
SkinColor.Equals(other.SkinColor) &&
|
||||||
|
Markings.SequenceEqual(other.Markings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return ReferenceEquals(this, obj) || obj is HumanoidCharacterAppearance other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(HairStyleId, HairColor, FacialHairStyleId, FacialHairColor, EyeColor, SkinColor, Markings);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HumanoidCharacterAppearance Clone()
|
||||||
|
{
|
||||||
|
return new(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
Content.Shared/Humanoid/HumanoidProfileExport.cs
Normal file
19
Content.Shared/Humanoid/HumanoidProfileExport.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Shared.Preferences;
|
||||||
|
|
||||||
|
namespace Content.Shared.Humanoid;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Holds all of the data for importing / exporting character profiles.
|
||||||
|
/// </summary>
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed partial class HumanoidProfileExport
|
||||||
|
{
|
||||||
|
[DataField]
|
||||||
|
public string ForkId;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public int Version = 1;
|
||||||
|
|
||||||
|
[DataField(required: true)]
|
||||||
|
public HumanoidCharacterProfile Profile = default!;
|
||||||
|
}
|
||||||
@@ -1,13 +1,22 @@
|
|||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Decals;
|
using Content.Shared.Decals;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Humanoid.Markings;
|
using Content.Shared.Humanoid.Markings;
|
||||||
using Content.Shared.Humanoid.Prototypes;
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Preferences;
|
using Content.Shared.Preferences;
|
||||||
|
using Robust.Shared;
|
||||||
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.GameObjects.Components.Localization;
|
using Robust.Shared.GameObjects.Components.Localization;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.Manager;
|
||||||
|
using Robust.Shared.Serialization.Markdown;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using YamlDotNet.RepresentationModel;
|
||||||
|
|
||||||
namespace Content.Shared.Humanoid;
|
namespace Content.Shared.Humanoid;
|
||||||
|
|
||||||
@@ -22,8 +31,10 @@ namespace Content.Shared.Humanoid;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
|
||||||
[Dependency] private readonly INetManager _netManager = default!;
|
[Dependency] private readonly INetManager _netManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||||
|
[Dependency] private readonly ISerializationManager _serManager = default!;
|
||||||
[Dependency] private readonly MarkingManager _markingManager = default!;
|
[Dependency] private readonly MarkingManager _markingManager = default!;
|
||||||
|
|
||||||
[ValidatePrototypeId<SpeciesPrototype>]
|
[ValidatePrototypeId<SpeciesPrototype>]
|
||||||
@@ -37,6 +48,37 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<HumanoidAppearanceComponent, ExaminedEvent>(OnExamined);
|
SubscribeLocalEvent<HumanoidAppearanceComponent, ExaminedEvent>(OnExamined);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DataNode ToDataNode(HumanoidCharacterProfile profile)
|
||||||
|
{
|
||||||
|
var export = new HumanoidProfileExport()
|
||||||
|
{
|
||||||
|
ForkId = _cfgManager.GetCVar(CVars.BuildForkId),
|
||||||
|
Profile = profile,
|
||||||
|
};
|
||||||
|
|
||||||
|
var dataNode = _serManager.WriteValue(export, alwaysWrite: true, notNullableOverride: true);
|
||||||
|
return dataNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HumanoidCharacterProfile FromStream(Stream stream, ICommonSession session)
|
||||||
|
{
|
||||||
|
using var reader = new StreamReader(stream, EncodingHelpers.UTF8);
|
||||||
|
var yamlStream = new YamlStream();
|
||||||
|
yamlStream.Load(reader);
|
||||||
|
|
||||||
|
var root = yamlStream.Documents[0].RootNode;
|
||||||
|
var export = _serManager.Read<HumanoidProfileExport>(root.ToDataNode(), notNullableOverride: true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add custom handling here for forks / version numbers if you care.
|
||||||
|
*/
|
||||||
|
|
||||||
|
var profile = export.Profile;
|
||||||
|
var collection = IoCManager.Instance;
|
||||||
|
profile.EnsureValid(session, collection!);
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnInit(EntityUid uid, HumanoidAppearanceComponent humanoid, ComponentInit args)
|
private void OnInit(EntityUid uid, HumanoidAppearanceComponent humanoid, ComponentInit args)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(humanoid.Species) || _netManager.IsClient && !IsClientSide(uid))
|
if (string.IsNullOrEmpty(humanoid.Species) || _netManager.IsClient && !IsClientSide(uid))
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Content.Shared.GameTicking;
|
|||||||
using Content.Shared.Humanoid;
|
using Content.Shared.Humanoid;
|
||||||
using Content.Shared.Humanoid.Prototypes;
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
using Content.Shared.Preferences.Loadouts;
|
using Content.Shared.Preferences.Loadouts;
|
||||||
using Content.Shared.Preferences.Loadouts.Effects;
|
|
||||||
using Content.Shared.Roles;
|
using Content.Shared.Roles;
|
||||||
using Content.Shared.Traits;
|
using Content.Shared.Traits;
|
||||||
using Robust.Shared.Collections;
|
using Robust.Shared.Collections;
|
||||||
@@ -32,16 +31,101 @@ namespace Content.Shared.Preferences
|
|||||||
public const int MaxNameLength = 32;
|
public const int MaxNameLength = 32;
|
||||||
public const int MaxDescLength = 512;
|
public const int MaxDescLength = 512;
|
||||||
|
|
||||||
private readonly Dictionary<string, JobPriority> _jobPriorities;
|
/// <summary>
|
||||||
private readonly List<string> _antagPreferences;
|
/// Job preferences for initial spawn.
|
||||||
private readonly List<string> _traitPreferences;
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
private Dictionary<string, JobPriority> _jobPriorities = new()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
SharedGameTicker.FallbackOverflowJob, JobPriority.High
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Antags we have opted in to.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
private HashSet<string> _antagPreferences = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enabled traits.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
private HashSet<string> _traitPreferences = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="_loadouts"/>
|
||||||
|
/// </summary>
|
||||||
public IReadOnlyDictionary<string, RoleLoadout> Loadouts => _loadouts;
|
public IReadOnlyDictionary<string, RoleLoadout> Loadouts => _loadouts;
|
||||||
|
|
||||||
private Dictionary<string, RoleLoadout> _loadouts;
|
[DataField]
|
||||||
|
private Dictionary<string, RoleLoadout> _loadouts = new();
|
||||||
|
|
||||||
// What in the lord is happening here.
|
[DataField]
|
||||||
private HumanoidCharacterProfile(
|
public string Name { get; set; } = "John Doe";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Detailed text that can appear for the character if <see cref="CCVars.FlavorText"/> is enabled.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string FlavorText { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Associated <see cref="SpeciesPrototype"/> for this profile.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string Species { get; set; } = SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public int Age { get; set; } = 18;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public Sex Sex { get; private set; } = Sex.Male;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public Gender Gender { get; private set; } = Gender.Male;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="Appearance"/>
|
||||||
|
/// </summary>
|
||||||
|
public ICharacterAppearance CharacterAppearance => Appearance;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores markings, eye colors, etc for the profile.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public HumanoidCharacterAppearance Appearance { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When spawning into a round what's the preferred spot to spawn.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public SpawnPriorityPreference SpawnPriority { get; private set; } = SpawnPriorityPreference.None;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="_jobPriorities"/>
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyDictionary<string, JobPriority> JobPriorities => _jobPriorities;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="_antagPreferences"/>
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlySet<string> AntagPreferences => _antagPreferences;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="_traitPreferences"/>
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlySet<string> TraitPreferences => _traitPreferences;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If we're unable to get one of our preferred jobs do we spawn as a fallback job or do we stay in lobby.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public PreferenceUnavailableMode PreferenceUnavailable { get; private set; } =
|
||||||
|
PreferenceUnavailableMode.SpawnAsOverflow;
|
||||||
|
|
||||||
|
public HumanoidCharacterProfile(
|
||||||
string name,
|
string name,
|
||||||
string flavortext,
|
string flavortext,
|
||||||
string species,
|
string species,
|
||||||
@@ -52,8 +136,8 @@ namespace Content.Shared.Preferences
|
|||||||
SpawnPriorityPreference spawnPriority,
|
SpawnPriorityPreference spawnPriority,
|
||||||
Dictionary<string, JobPriority> jobPriorities,
|
Dictionary<string, JobPriority> jobPriorities,
|
||||||
PreferenceUnavailableMode preferenceUnavailable,
|
PreferenceUnavailableMode preferenceUnavailable,
|
||||||
List<string> antagPreferences,
|
HashSet<string> antagPreferences,
|
||||||
List<string> traitPreferences,
|
HashSet<string> traitPreferences,
|
||||||
Dictionary<string, RoleLoadout> loadouts)
|
Dictionary<string, RoleLoadout> loadouts)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
@@ -71,40 +155,21 @@ namespace Content.Shared.Preferences
|
|||||||
_loadouts = loadouts;
|
_loadouts = loadouts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Copy constructor but with overridable references (to prevent useless copies)</summary>
|
|
||||||
private HumanoidCharacterProfile(
|
|
||||||
HumanoidCharacterProfile other,
|
|
||||||
Dictionary<string, JobPriority> jobPriorities,
|
|
||||||
List<string> antagPreferences,
|
|
||||||
List<string> traitPreferences,
|
|
||||||
Dictionary<string, RoleLoadout> loadouts)
|
|
||||||
: this(other.Name, other.FlavorText, other.Species, other.Age, other.Sex, other.Gender, other.Appearance, other.SpawnPriority,
|
|
||||||
jobPriorities, other.PreferenceUnavailable, antagPreferences, traitPreferences, loadouts)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>Copy constructor</summary>
|
/// <summary>Copy constructor</summary>
|
||||||
private HumanoidCharacterProfile(HumanoidCharacterProfile other)
|
public HumanoidCharacterProfile(HumanoidCharacterProfile other)
|
||||||
: this(other, new Dictionary<string, JobPriority>(other.JobPriorities), new List<string>(other.AntagPreferences), new List<string>(other.TraitPreferences), new Dictionary<string, RoleLoadout>(other.Loadouts))
|
: this(other.Name,
|
||||||
{
|
other.FlavorText,
|
||||||
}
|
other.Species,
|
||||||
|
other.Age,
|
||||||
public HumanoidCharacterProfile(
|
other.Sex,
|
||||||
string name,
|
other.Gender,
|
||||||
string flavortext,
|
other.Appearance.Clone(),
|
||||||
string species,
|
other.SpawnPriority,
|
||||||
int age,
|
new Dictionary<string, JobPriority>(other.JobPriorities),
|
||||||
Sex sex,
|
other.PreferenceUnavailable,
|
||||||
Gender gender,
|
new HashSet<string>(other.AntagPreferences),
|
||||||
HumanoidCharacterAppearance appearance,
|
new HashSet<string>(other.TraitPreferences),
|
||||||
SpawnPriorityPreference spawnPriority,
|
new Dictionary<string, RoleLoadout>(other.Loadouts))
|
||||||
IReadOnlyDictionary<string, JobPriority> jobPriorities,
|
|
||||||
PreferenceUnavailableMode preferenceUnavailable,
|
|
||||||
IReadOnlyList<string> antagPreferences,
|
|
||||||
IReadOnlyList<string> traitPreferences,
|
|
||||||
Dictionary<string, RoleLoadout> loadouts)
|
|
||||||
: this(name, flavortext, species, age, sex, gender, appearance, spawnPriority, new Dictionary<string, JobPriority>(jobPriorities),
|
|
||||||
preferenceUnavailable, new List<string>(antagPreferences), new List<string>(traitPreferences), new Dictionary<string, RoleLoadout>(loadouts))
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,23 +178,7 @@ namespace Content.Shared.Preferences
|
|||||||
/// Defaults to <see cref="SharedHumanoidAppearanceSystem.DefaultSpecies"/> for the species.
|
/// Defaults to <see cref="SharedHumanoidAppearanceSystem.DefaultSpecies"/> for the species.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public HumanoidCharacterProfile() : this(
|
public HumanoidCharacterProfile()
|
||||||
"John Doe",
|
|
||||||
"",
|
|
||||||
SharedHumanoidAppearanceSystem.DefaultSpecies,
|
|
||||||
18,
|
|
||||||
Sex.Male,
|
|
||||||
Gender.Male,
|
|
||||||
new HumanoidCharacterAppearance(),
|
|
||||||
SpawnPriorityPreference.None,
|
|
||||||
new Dictionary<string, JobPriority>
|
|
||||||
{
|
|
||||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High}
|
|
||||||
},
|
|
||||||
PreferenceUnavailableMode.SpawnAsOverflow,
|
|
||||||
new List<string>(),
|
|
||||||
new List<string>(),
|
|
||||||
new Dictionary<string, RoleLoadout>())
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,23 +189,10 @@ namespace Content.Shared.Preferences
|
|||||||
/// <returns>Humanoid character profile with default settings.</returns>
|
/// <returns>Humanoid character profile with default settings.</returns>
|
||||||
public static HumanoidCharacterProfile DefaultWithSpecies(string species = SharedHumanoidAppearanceSystem.DefaultSpecies)
|
public static HumanoidCharacterProfile DefaultWithSpecies(string species = SharedHumanoidAppearanceSystem.DefaultSpecies)
|
||||||
{
|
{
|
||||||
return new(
|
return new()
|
||||||
"John Doe",
|
{
|
||||||
"",
|
Species = species,
|
||||||
species,
|
};
|
||||||
18,
|
|
||||||
Sex.Male,
|
|
||||||
Gender.Male,
|
|
||||||
HumanoidCharacterAppearance.DefaultWithSpecies(species),
|
|
||||||
SpawnPriorityPreference.None,
|
|
||||||
new Dictionary<string, JobPriority>
|
|
||||||
{
|
|
||||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High}
|
|
||||||
},
|
|
||||||
PreferenceUnavailableMode.SpawnAsOverflow,
|
|
||||||
new List<string>(),
|
|
||||||
new List<string>(),
|
|
||||||
new Dictionary<string, RoleLoadout>());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This should eventually not be a visual change only.
|
// TODO: This should eventually not be a visual change only.
|
||||||
@@ -201,36 +237,17 @@ namespace Content.Shared.Preferences
|
|||||||
|
|
||||||
var name = GetName(species, gender);
|
var name = GetName(species, gender);
|
||||||
|
|
||||||
return new HumanoidCharacterProfile(name, "", species, age, sex, gender, HumanoidCharacterAppearance.Random(species, sex), SpawnPriorityPreference.None,
|
return new HumanoidCharacterProfile()
|
||||||
new Dictionary<string, JobPriority>
|
{
|
||||||
{
|
Name = name,
|
||||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High},
|
Sex = sex,
|
||||||
}, PreferenceUnavailableMode.StayInLobby, new List<string>(), new List<string>(), new Dictionary<string, RoleLoadout>());
|
Age = age,
|
||||||
|
Gender = gender,
|
||||||
|
Species = species,
|
||||||
|
Appearance = HumanoidCharacterAppearance.Random(species, sex),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name { get; private set; }
|
|
||||||
public string FlavorText { get; private set; }
|
|
||||||
public string Species { get; private set; }
|
|
||||||
|
|
||||||
[DataField("age")]
|
|
||||||
public int Age { get; private set; }
|
|
||||||
|
|
||||||
[DataField("sex")]
|
|
||||||
public Sex Sex { get; private set; }
|
|
||||||
|
|
||||||
[DataField("gender")]
|
|
||||||
public Gender Gender { get; private set; }
|
|
||||||
|
|
||||||
public ICharacterAppearance CharacterAppearance => Appearance;
|
|
||||||
|
|
||||||
[DataField("appearance")]
|
|
||||||
public HumanoidCharacterAppearance Appearance { get; private set; }
|
|
||||||
public SpawnPriorityPreference SpawnPriority { get; private set; }
|
|
||||||
public IReadOnlyDictionary<string, JobPriority> JobPriorities => _jobPriorities;
|
|
||||||
public IReadOnlyList<string> AntagPreferences => _antagPreferences;
|
|
||||||
public IReadOnlyList<string> TraitPreferences => _traitPreferences;
|
|
||||||
public PreferenceUnavailableMode PreferenceUnavailable { get; private set; }
|
|
||||||
|
|
||||||
public HumanoidCharacterProfile WithName(string name)
|
public HumanoidCharacterProfile WithName(string name)
|
||||||
{
|
{
|
||||||
return new(this) { Name = name };
|
return new(this) { Name = name };
|
||||||
@@ -274,7 +291,10 @@ namespace Content.Shared.Preferences
|
|||||||
|
|
||||||
public HumanoidCharacterProfile WithJobPriorities(IEnumerable<KeyValuePair<string, JobPriority>> jobPriorities)
|
public HumanoidCharacterProfile WithJobPriorities(IEnumerable<KeyValuePair<string, JobPriority>> jobPriorities)
|
||||||
{
|
{
|
||||||
return new(this, new Dictionary<string, JobPriority>(jobPriorities), _antagPreferences, _traitPreferences, _loadouts);
|
return new(this)
|
||||||
|
{
|
||||||
|
_jobPriorities = new Dictionary<string, JobPriority>(jobPriorities),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priority)
|
public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priority)
|
||||||
@@ -288,7 +308,11 @@ namespace Content.Shared.Preferences
|
|||||||
{
|
{
|
||||||
dictionary[jobId] = priority;
|
dictionary[jobId] = priority;
|
||||||
}
|
}
|
||||||
return new(this, dictionary, _antagPreferences, _traitPreferences, _loadouts);
|
|
||||||
|
return new(this)
|
||||||
|
{
|
||||||
|
_jobPriorities = dictionary,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableMode mode)
|
public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableMode mode)
|
||||||
@@ -298,50 +322,47 @@ namespace Content.Shared.Preferences
|
|||||||
|
|
||||||
public HumanoidCharacterProfile WithAntagPreferences(IEnumerable<string> antagPreferences)
|
public HumanoidCharacterProfile WithAntagPreferences(IEnumerable<string> antagPreferences)
|
||||||
{
|
{
|
||||||
return new(this, _jobPriorities, new List<string>(antagPreferences), _traitPreferences, _loadouts);
|
return new(this)
|
||||||
|
{
|
||||||
|
_antagPreferences = new HashSet<string>(antagPreferences),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref)
|
public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref)
|
||||||
{
|
{
|
||||||
var list = new List<string>(_antagPreferences);
|
var list = new HashSet<string>(_antagPreferences);
|
||||||
if (pref)
|
if (pref)
|
||||||
{
|
{
|
||||||
if (!list.Contains(antagId))
|
list.Add(antagId);
|
||||||
{
|
|
||||||
list.Add(antagId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (list.Contains(antagId))
|
list.Remove(antagId);
|
||||||
{
|
|
||||||
list.Remove(antagId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new(this, _jobPriorities, list, _traitPreferences, _loadouts);
|
return new(this)
|
||||||
|
{
|
||||||
|
_antagPreferences = list,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref)
|
public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref)
|
||||||
{
|
{
|
||||||
var list = new List<string>(_traitPreferences);
|
var list = new HashSet<string>(_traitPreferences);
|
||||||
|
|
||||||
// TODO: Maybe just refactor this to HashSet? Same with _antagPreferences
|
|
||||||
if (pref)
|
if (pref)
|
||||||
{
|
{
|
||||||
if (!list.Contains(traitId))
|
list.Add(traitId);
|
||||||
{
|
|
||||||
list.Add(traitId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (list.Contains(traitId))
|
list.Remove(traitId);
|
||||||
{
|
|
||||||
list.Remove(traitId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return new(this, _jobPriorities, _antagPreferences, list, _loadouts);
|
|
||||||
|
return new(this)
|
||||||
|
{
|
||||||
|
_traitPreferences = list,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Summary =>
|
public string Summary =>
|
||||||
@@ -498,10 +519,10 @@ namespace Content.Shared.Preferences
|
|||||||
PreferenceUnavailable = prefsUnavailableMode;
|
PreferenceUnavailable = prefsUnavailableMode;
|
||||||
|
|
||||||
_antagPreferences.Clear();
|
_antagPreferences.Clear();
|
||||||
_antagPreferences.AddRange(antags);
|
_antagPreferences.UnionWith(antags);
|
||||||
|
|
||||||
_traitPreferences.Clear();
|
_traitPreferences.Clear();
|
||||||
_traitPreferences.AddRange(traits);
|
_traitPreferences.UnionWith(traits);
|
||||||
|
|
||||||
// Checks prototypes exist for all loadouts and dump / set to default if not.
|
// Checks prototypes exist for all loadouts and dump / set to default if not.
|
||||||
var toRemove = new ValueList<string>();
|
var toRemove = new ValueList<string>();
|
||||||
@@ -514,7 +535,7 @@ namespace Content.Shared.Preferences
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
loadouts.EnsureValid(session, collection);
|
loadouts.EnsureValid(this, session, collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var value in toRemove)
|
foreach (var value in toRemove)
|
||||||
@@ -540,27 +561,26 @@ namespace Content.Shared.Preferences
|
|||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
return obj is HumanoidCharacterProfile other && MemberwiseEquals(other);
|
return ReferenceEquals(this, obj) || obj is HumanoidCharacterProfile other && Equals(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetHashCode()
|
public override int GetHashCode()
|
||||||
{
|
{
|
||||||
return HashCode.Combine(
|
var hashCode = new HashCode();
|
||||||
HashCode.Combine(
|
hashCode.Add(_jobPriorities);
|
||||||
Name,
|
hashCode.Add(_antagPreferences);
|
||||||
Species,
|
hashCode.Add(_traitPreferences);
|
||||||
Age,
|
hashCode.Add(_loadouts);
|
||||||
Sex,
|
hashCode.Add(Name);
|
||||||
Gender,
|
hashCode.Add(FlavorText);
|
||||||
Appearance
|
hashCode.Add(Species);
|
||||||
),
|
hashCode.Add(Age);
|
||||||
SpawnPriority,
|
hashCode.Add((int)Sex);
|
||||||
PreferenceUnavailable,
|
hashCode.Add((int)Gender);
|
||||||
_jobPriorities,
|
hashCode.Add(Appearance);
|
||||||
_antagPreferences,
|
hashCode.Add((int)SpawnPriority);
|
||||||
_traitPreferences,
|
hashCode.Add((int)PreferenceUnavailable);
|
||||||
_loadouts
|
return hashCode.ToHashCode();
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetLoadout(RoleLoadout loadout)
|
public void SetLoadout(RoleLoadout loadout)
|
||||||
@@ -582,10 +602,12 @@ namespace Content.Shared.Preferences
|
|||||||
}
|
}
|
||||||
|
|
||||||
copied[loadout.Role] = loadout.Clone();
|
copied[loadout.Role] = loadout.Clone();
|
||||||
return new(this, _jobPriorities, _antagPreferences, _traitPreferences, copied);
|
var profile = Clone();
|
||||||
|
profile._loadouts = copied;
|
||||||
|
return profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RoleLoadout GetLoadoutOrDefault(string id, IEntityManager entManager, IPrototypeManager protoManager)
|
public RoleLoadout GetLoadoutOrDefault(string id, ProtoId<SpeciesPrototype>? species, IEntityManager entManager, IPrototypeManager protoManager)
|
||||||
{
|
{
|
||||||
if (!_loadouts.TryGetValue(id, out var loadout))
|
if (!_loadouts.TryGetValue(id, out var loadout))
|
||||||
{
|
{
|
||||||
@@ -596,5 +618,10 @@ namespace Content.Shared.Preferences
|
|||||||
loadout.SetDefault(protoManager);
|
loadout.SetDefault(protoManager);
|
||||||
return loadout;
|
return loadout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HumanoidCharacterProfile Clone()
|
||||||
|
{
|
||||||
|
return new HumanoidCharacterProfile(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,13 @@ public sealed partial class GroupLoadoutEffect : LoadoutEffect
|
|||||||
[DataField(required: true)]
|
[DataField(required: true)]
|
||||||
public ProtoId<LoadoutEffectGroupPrototype> Proto;
|
public ProtoId<LoadoutEffectGroupPrototype> Proto;
|
||||||
|
|
||||||
public override bool Validate(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason)
|
public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||||
{
|
{
|
||||||
var effectsProto = collection.Resolve<IPrototypeManager>().Index(Proto);
|
var effectsProto = collection.Resolve<IPrototypeManager>().Index(Proto);
|
||||||
|
|
||||||
foreach (var effect in effectsProto.Effects)
|
foreach (var effect in effectsProto.Effects)
|
||||||
{
|
{
|
||||||
if (!effect.Validate(loadout, session, collection, out reason))
|
if (!effect.Validate(profile, loadout, session, collection, out reason))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public sealed partial class JobRequirementLoadoutEffect : LoadoutEffect
|
|||||||
[DataField(required: true)]
|
[DataField(required: true)]
|
||||||
public JobRequirement Requirement = default!;
|
public JobRequirement Requirement = default!;
|
||||||
|
|
||||||
public override bool Validate(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason)
|
public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||||
{
|
{
|
||||||
var manager = collection.Resolve<ISharedPlaytimeManager>();
|
var manager = collection.Resolve<ISharedPlaytimeManager>();
|
||||||
var playtimes = manager.GetPlayTimes(session);
|
var playtimes = manager.GetPlayTimes(session);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public abstract partial class LoadoutEffect
|
|||||||
/// Tries to validate the effect.
|
/// Tries to validate the effect.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract bool Validate(
|
public abstract bool Validate(
|
||||||
|
HumanoidCharacterProfile profile,
|
||||||
RoleLoadout loadout,
|
RoleLoadout loadout,
|
||||||
ICommonSession session,
|
ICommonSession session,
|
||||||
IDependencyCollection collection,
|
IDependencyCollection collection,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public sealed partial class PointsCostLoadoutEffect : LoadoutEffect
|
|||||||
public int Cost = 1;
|
public int Cost = 1;
|
||||||
|
|
||||||
public override bool Validate(
|
public override bool Validate(
|
||||||
|
HumanoidCharacterProfile profile,
|
||||||
RoleLoadout loadout,
|
RoleLoadout loadout,
|
||||||
ICommonSession session,
|
ICommonSession session,
|
||||||
IDependencyCollection collection,
|
IDependencyCollection collection,
|
||||||
|
|||||||
@@ -1,6 +1,26 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Preferences.Loadouts.Effects;
|
namespace Content.Shared.Preferences.Loadouts.Effects;
|
||||||
|
|
||||||
public sealed class SpeciesLoadoutEffect
|
public sealed partial class SpeciesLoadoutEffect : LoadoutEffect
|
||||||
{
|
{
|
||||||
|
[DataField(required: true)]
|
||||||
|
public List<ProtoId<SpeciesPrototype>> Species = new();
|
||||||
|
|
||||||
|
public override bool Validate(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection,
|
||||||
|
[NotNullWhen(false)] out FormattedMessage? reason)
|
||||||
|
{
|
||||||
|
if (Species.Contains(profile.Species))
|
||||||
|
{
|
||||||
|
reason = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
reason = FormattedMessage.FromUnformatted(Loc.GetString("loadout-group-species-restriction"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,9 @@ namespace Content.Shared.Preferences.Loadouts;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies the selected prototype and custom data for a loadout.
|
/// Specifies the selected prototype and custom data for a loadout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable, DataDefinition]
|
||||||
public sealed class Loadout
|
public sealed partial class Loadout
|
||||||
{
|
{
|
||||||
|
[DataField]
|
||||||
public ProtoId<LoadoutPrototype> Prototype;
|
public ProtoId<LoadoutPrototype> Prototype;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Shared.Humanoid.Prototypes;
|
||||||
using Content.Shared.Random;
|
using Content.Shared.Random;
|
||||||
using Robust.Shared.Collections;
|
using Robust.Shared.Collections;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
@@ -11,11 +13,13 @@ namespace Content.Shared.Preferences.Loadouts;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains all of the selected data for a role's loadout.
|
/// Contains all of the selected data for a role's loadout.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable, DataDefinition]
|
||||||
public sealed class RoleLoadout
|
public sealed partial class RoleLoadout : IEquatable<RoleLoadout>
|
||||||
{
|
{
|
||||||
public readonly ProtoId<RoleLoadoutPrototype> Role;
|
[DataField]
|
||||||
|
public ProtoId<RoleLoadoutPrototype> Role;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
public Dictionary<ProtoId<LoadoutGroupPrototype>, List<Loadout>> SelectedLoadouts = new();
|
public Dictionary<ProtoId<LoadoutGroupPrototype>, List<Loadout>> SelectedLoadouts = new();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -44,7 +48,7 @@ public sealed class RoleLoadout
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ensures all prototypes exist and effects can be applied.
|
/// Ensures all prototypes exist and effects can be applied.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void EnsureValid(ICommonSession session, IDependencyCollection collection)
|
public void EnsureValid(HumanoidCharacterProfile profile, ICommonSession session, IDependencyCollection collection)
|
||||||
{
|
{
|
||||||
var groupRemove = new ValueList<string>();
|
var groupRemove = new ValueList<string>();
|
||||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||||
@@ -81,7 +85,7 @@ public sealed class RoleLoadout
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Validate the loadout can be applied (e.g. points).
|
// Validate the loadout can be applied (e.g. points).
|
||||||
if (!IsValid(session, loadout.Prototype, collection, out _))
|
if (!IsValid(profile, session, loadout.Prototype, collection, out _))
|
||||||
{
|
{
|
||||||
loadouts.RemoveAt(i);
|
loadouts.RemoveAt(i);
|
||||||
continue;
|
continue;
|
||||||
@@ -167,7 +171,7 @@ public sealed class RoleLoadout
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns whether a loadout is valid or not.
|
/// Returns whether a loadout is valid or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsValid(ICommonSession session, ProtoId<LoadoutPrototype> loadout, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason)
|
public bool IsValid(HumanoidCharacterProfile profile, ICommonSession session, ProtoId<LoadoutPrototype> loadout, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||||
{
|
{
|
||||||
reason = null;
|
reason = null;
|
||||||
|
|
||||||
@@ -180,7 +184,7 @@ public sealed class RoleLoadout
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!protoManager.TryIndex(Role, out var roleProto))
|
if (!protoManager.HasIndex(Role))
|
||||||
{
|
{
|
||||||
reason = FormattedMessage.FromUnformatted("loadouts-prototype-missing");
|
reason = FormattedMessage.FromUnformatted("loadouts-prototype-missing");
|
||||||
return false;
|
return false;
|
||||||
@@ -190,7 +194,7 @@ public sealed class RoleLoadout
|
|||||||
|
|
||||||
foreach (var effect in loadoutProto.Effects)
|
foreach (var effect in loadoutProto.Effects)
|
||||||
{
|
{
|
||||||
valid = valid && effect.Validate(this, session, collection, out reason);
|
valid = valid && effect.Validate(profile, this, session, collection, out reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
return valid;
|
return valid;
|
||||||
@@ -257,4 +261,21 @@ public sealed class RoleLoadout
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Equals(RoleLoadout? other)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, other)) return false;
|
||||||
|
if (ReferenceEquals(this, other)) return true;
|
||||||
|
return Role.Equals(other.Role) && SelectedLoadouts.SequenceEqual(other.SelectedLoadouts) && Points == other.Points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Equals(object? obj)
|
||||||
|
{
|
||||||
|
return ReferenceEquals(this, obj) || obj is RoleLoadout other && Equals(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(Role, SelectedLoadouts, Points);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
character-setup-gui-character-setup-label = Character setup
|
character-setup-gui-character-setup-label = Character setup
|
||||||
character-setup-gui-character-setup-stats-button = Stats
|
character-setup-gui-character-setup-stats-button = Stats
|
||||||
character-setup-gui-character-setup-rules-button = Rules
|
character-setup-gui-character-setup-rules-button = Rules
|
||||||
character-setup-gui-character-setup-save-button = Save
|
|
||||||
character-setup-gui-character-setup-close-button = Close
|
character-setup-gui-character-setup-close-button = Close
|
||||||
character-setup-gui-create-new-character-button = Create new slot...
|
character-setup-gui-create-new-character-button = Create new slot...
|
||||||
character-setup-gui-create-new-character-button-tooltip = A maximum of {$maxCharacters} characters are allowed.
|
character-setup-gui-create-new-character-button-tooltip = A maximum of {$maxCharacters} characters are allowed.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ humanoid-profile-editor-pronouns-neuter-text = It / It
|
|||||||
humanoid-profile-editor-import-button = Import
|
humanoid-profile-editor-import-button = Import
|
||||||
humanoid-profile-editor-export-button = Export
|
humanoid-profile-editor-export-button = Export
|
||||||
humanoid-profile-editor-save-button = Save
|
humanoid-profile-editor-save-button = Save
|
||||||
|
humanoid-profile-editor-reset-button = Reset
|
||||||
humanoid-profile-editor-spawn-priority-label = Spawn priority:
|
humanoid-profile-editor-spawn-priority-label = Spawn priority:
|
||||||
humanoid-profile-editor-eyes-label = Eye color:
|
humanoid-profile-editor-eyes-label = Eye color:
|
||||||
humanoid-profile-editor-jobs-tab = Jobs
|
humanoid-profile-editor-jobs-tab = Jobs
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
- type: species
|
- type: species
|
||||||
id: Vox
|
id: Vox
|
||||||
name: species-name-vox
|
name: species-name-vox
|
||||||
roundStart: false # sad...
|
roundStart: false # sad
|
||||||
prototype: MobVox
|
prototype: MobVox
|
||||||
sprites: MobVoxSprites
|
sprites: MobVoxSprites
|
||||||
markingLimits: MobVoxMarkingLimits
|
markingLimits: MobVoxMarkingLimits
|
||||||
|
|||||||
Reference in New Issue
Block a user