added Character Setup (#511)

* added Character Setup

* whoops

* reverted unrelated changes

* Made everything work post-rebase

* Removed unused PreferencesChanged event

* nope, don't need this

* HumanoidProfileEditorPanel -> HumanoidProfileEditor

* Set initial data for hair pickers

* Fixed nullable warning

* Renamed LooksComponent -> HumanoidAppearanceComponent

* Renamed LooksComponentState -> HumanoidAppearanceComponentState

* Final renaming maybe

* Use a human-like dummy instead of a real human

* Change preferences structs back to classes
This commit is contained in:
DamianX
2020-01-18 01:54:13 +01:00
committed by Pieter-Jan Briers
parent d03da83fda
commit a4e369e629
41 changed files with 1423 additions and 756 deletions

View File

@@ -1,3 +1,4 @@
using System.Linq;
using Content.Client.Interfaces;
using Content.Shared.Preferences;
using Robust.Shared.Interfaces.Network;
@@ -6,8 +7,9 @@ using Robust.Shared.IoC;
namespace Content.Client
{
/// <summary>
/// Receives <see cref="PlayerPreferences"/> and <see cref="GameSettings"/> from the server during the initial connection.
/// Stores preferences on the server through <see cref="SelectCharacter"/> and <see cref="UpdateCharacter"/>.
/// Receives <see cref="PlayerPreferences" /> and <see cref="GameSettings" /> from the server during the initial
/// connection.
/// Stores preferences on the server through <see cref="SelectCharacter" /> and <see cref="UpdateCharacter" />.
/// </summary>
public class ClientPreferencesManager : SharedPreferencesManager, IClientPreferencesManager
{
@@ -24,14 +26,14 @@ namespace Content.Client
HandlePreferencesAndSettings);
}
private void HandlePreferencesAndSettings(MsgPreferencesAndSettings message)
public void SelectCharacter(ICharacterProfile profile)
{
Preferences = message.Preferences;
Settings = message.Settings;
SelectCharacter(Preferences.IndexOfCharacter(profile));
}
public void SelectCharacter(int slot)
{
Preferences = new PlayerPreferences(Preferences.Characters, slot);
var msg = _netManager.CreateNetMessage<MsgSelectCharacter>();
msg.SelectedCharacterIndex = slot;
_netManager.ClientSendMessage(msg);
@@ -39,10 +41,34 @@ namespace Content.Client
public void UpdateCharacter(ICharacterProfile profile, int slot)
{
var characters = Preferences.Characters.ToArray();
characters[slot] = profile;
Preferences = new PlayerPreferences(characters, Preferences.SelectedCharacterIndex);
var msg = _netManager.CreateNetMessage<MsgUpdateCharacter>();
msg.Profile = profile;
msg.Slot = slot;
_netManager.ClientSendMessage(msg);
}
public void CreateCharacter(ICharacterProfile profile)
{
UpdateCharacter(profile, Preferences.FirstEmptySlot);
}
public void DeleteCharacter(ICharacterProfile profile)
{
DeleteCharacter(Preferences.IndexOfCharacter(profile));
}
public void DeleteCharacter(int slot)
{
UpdateCharacter(null, slot);
}
private void HandlePreferencesAndSettings(MsgPreferencesAndSettings message)
{
Preferences = message.Preferences;
Settings = message.Settings;
}
}
}

View File

@@ -22,7 +22,9 @@ using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Shared.ContentPack;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
@@ -215,6 +217,7 @@ namespace Content.Client
IoCManager.Resolve<IChatManager>().Initialize();
IoCManager.Resolve<ISandboxManager>().Initialize();
IoCManager.Resolve<IClientPreferencesManager>().Initialize();
IoCManager.Resolve<IMapManager>().CreateNewMapEntity(MapId.Nullspace);
IoCManager.Resolve<IItemSlotManager>().Initialize();
}

View File

@@ -77,7 +77,7 @@ namespace Content.Client.GameObjects.Components
var styles = HairStyles.FacialHairStylesMap.ToList();
styles.Sort(HairStyles.FacialHairStyleComparer);
foreach (var (styleName, styleState) in styles)
foreach (var (styleName, styleState) in HairStyles.FacialHairStylesMap)
{
Items.AddItem(styleName, humanFacialHairRSI[styleState].Frame0);
}
@@ -141,6 +141,7 @@ namespace Content.Client.GameObjects.Components
Items = new ItemList
{
SizeFlagsVertical = SizeFlags.FillExpand,
CustomMinimumSize = (300, 250)
};
vBox.AddChild(Items);
Items.OnItemSelected += ItemSelected;

View File

@@ -1,72 +0,0 @@
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.Preferences.Appearance;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Content.Client.GameObjects.Components.Mobs
{
[RegisterComponent]
public sealed class HairComponent : SharedHairComponent
{
protected override void Startup()
{
base.Startup();
UpdateHairStyle();
}
public override string FacialHairStyleName
{
get => base.FacialHairStyleName;
set
{
base.FacialHairStyleName = value;
UpdateHairStyle();
}
}
public override string HairStyleName
{
get => base.HairStyleName;
set
{
base.HairStyleName = value;
UpdateHairStyle();
}
}
public override Color HairColor
{
get => base.HairColor;
set
{
base.HairColor = value;
UpdateHairStyle();
}
}
public override Color FacialHairColor
{
get => base.FacialHairColor;
set
{
base.FacialHairColor = value;
UpdateHairStyle();
}
}
private void UpdateHairStyle()
{
var sprite = Owner.GetComponent<SpriteComponent>();
sprite.LayerSetColor(HumanoidVisualLayers.Hair, HairColor);
sprite.LayerSetColor(HumanoidVisualLayers.FacialHair, FacialHairColor);
sprite.LayerSetState(HumanoidVisualLayers.Hair,
HairStyles.HairStylesMap[HairStyleName ?? HairStyles.DefaultHairStyle]);
sprite.LayerSetState(HumanoidVisualLayers.FacialHair,
HairStyles.FacialHairStylesMap[FacialHairStyleName ?? HairStyles.DefaultFacialHairStyle]);
}
}
}

View File

@@ -0,0 +1,62 @@
using Content.Shared.GameObjects.Components.Mobs;
using Content.Shared.Preferences;
using Content.Shared.Preferences.Appearance;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
namespace Content.Client.GameObjects.Components.Mobs
{
[RegisterComponent]
public sealed class HumanoidAppearanceComponent : SharedHumanoidAppearanceComponent
{
public override HumanoidCharacterAppearance Appearance
{
get => base.Appearance;
set
{
base.Appearance = value;
UpdateLooks();
}
}
public override Sex Sex
{
get => base.Sex;
set
{
base.Sex = value;
UpdateLooks();
}
}
protected override void Startup()
{
base.Startup();
UpdateLooks();
}
private void UpdateLooks()
{
if (Appearance is null) return;
var sprite = Owner.GetComponent<SpriteComponent>();
sprite.LayerSetColor(HumanoidVisualLayers.Hair, Appearance.HairColor);
sprite.LayerSetColor(HumanoidVisualLayers.FacialHair, Appearance.FacialHairColor);
sprite.LayerSetState(HumanoidVisualLayers.Body, Sex == Sex.Male ? "male" : "female");
var hairStyle = Appearance.HairStyleName;
if (string.IsNullOrWhiteSpace(hairStyle) || !HairStyles.HairStylesMap.ContainsKey(hairStyle))
hairStyle = HairStyles.DefaultHairStyle;
sprite.LayerSetState(HumanoidVisualLayers.Hair,
HairStyles.HairStylesMap[hairStyle]);
var facialHairStyle = Appearance.FacialHairStyleName;
if (string.IsNullOrWhiteSpace(facialHairStyle) || !HairStyles.FacialHairStylesMap.ContainsKey(facialHairStyle))
facialHairStyle = HairStyles.DefaultFacialHairStyle;
sprite.LayerSetState(HumanoidVisualLayers.FacialHair,
HairStyles.FacialHairStylesMap[facialHairStyle]);
}
}
}

View File

@@ -13,9 +13,9 @@ using Robust.Client.Interfaces.Input;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.Interfaces.UserInterface;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
@@ -38,16 +38,19 @@ namespace Content.Client.GameTicking
[Dependency] private IResourceCache _resourceCache;
[Dependency] private IPlayerManager _playerManager;
[Dependency] private IGameHud _gameHud;
[Dependency] private IEntityManager _entityManager;
[Dependency] private IClientPreferencesManager _preferencesManager;
#pragma warning restore 649
[ViewVariables] private bool _areWeReady;
[ViewVariables] private bool _initialized;
[ViewVariables] private TickerState _tickerState;
[ViewVariables] private CharacterSetupGui _characterSetup;
[ViewVariables] private ChatBox _gameChat;
[ViewVariables] private LobbyGui _lobby;
[ViewVariables] private bool _gameStarted;
[ViewVariables] private DateTime _startTime;
[ViewVariables] private bool _initialized;
[ViewVariables] private LobbyGui _lobby;
[ViewVariables] private string _serverInfoBlob;
[ViewVariables] private DateTime _startTime;
[ViewVariables] private TickerState _tickerState;
public void Initialize()
{
@@ -189,7 +192,15 @@ namespace Content.Client.GameTicking
_tickerState = TickerState.InLobby;
_lobby = new LobbyGui(_localization, _resourceCache);
_characterSetup = new CharacterSetupGui(_entityManager, _localization, _resourceCache, _preferencesManager);
LayoutContainer.SetAnchorPreset(_characterSetup, LayoutContainer.LayoutPreset.Wide);
_characterSetup.CloseButton.OnPressed += args =>
{
_lobby.CharacterPreview.UpdateUI();
_userInterfaceManager.StateRoot.AddChild(_lobby);
_userInterfaceManager.StateRoot.RemoveChild(_characterSetup);
};
_lobby = new LobbyGui(_entityManager, _localization, _resourceCache, _preferencesManager);
_userInterfaceManager.StateRoot.AddChild(_lobby);
LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide);
@@ -204,6 +215,12 @@ namespace Content.Client.GameTicking
_updateLobbyUi();
_lobby.CharacterPreview.CharacterSetupButton.OnPressed += args =>
{
_userInterfaceManager.StateRoot.RemoveChild(_lobby);
_userInterfaceManager.StateRoot.AddChild(_characterSetup);
};
_lobby.ObserveButton.OnPressed += args => _console.ProcessCommand("observe");
_lobby.ReadyButton.OnPressed += args =>
{

View File

@@ -4,10 +4,14 @@ namespace Content.Client.Interfaces
{
public interface IClientPreferencesManager
{
void Initialize();
GameSettings Settings { get; }
PlayerPreferences Preferences { get; }
void Initialize();
void SelectCharacter(ICharacterProfile profile);
void SelectCharacter(int slot);
void UpdateCharacter(ICharacterProfile profile, int slot);
void CreateCharacter(ICharacterProfile profile);
void DeleteCharacter(ICharacterProfile profile);
void DeleteCharacter(int slot);
}
}

View File

@@ -0,0 +1,268 @@
using Content.Client.GameObjects.Components.Mobs;
using Content.Client.Interfaces;
using Content.Client.Utility;
using Content.Shared.Preferences;
using Robust.Client.GameObjects;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.Client.UserInterface
{
public class CharacterSetupGui : Control
{
private readonly VBoxContainer _charactersVBox;
private readonly Button _createNewCharacterButton;
private readonly IEntityManager _entityManager;
private readonly HumanoidProfileEditor _humanoidProfileEditor;
private readonly IClientPreferencesManager _preferencesManager;
public readonly Button CloseButton;
public CharacterSetupGui(IEntityManager entityManager,
ILocalizationManager localization,
IResourceCache resourceCache,
IClientPreferencesManager preferencesManager)
{
_entityManager = entityManager;
_preferencesManager = preferencesManager;
var margin = new MarginContainer
{
MarginBottomOverride = 20,
MarginLeftOverride = 20,
MarginRightOverride = 20,
MarginTopOverride = 20
};
AddChild(margin);
var panelTex = resourceCache.GetTexture("/Nano/button.svg.96dpi.png");
var back = new StyleBoxTexture
{
Texture = panelTex,
Modulate = new Color(37, 37, 42)
};
back.SetPatchMargin(StyleBox.Margin.All, 10);
var panel = new PanelContainer
{
PanelOverride = back
};
margin.AddChild(panel);
var vBox = new VBoxContainer {SeparationOverride = 0};
margin.AddChild(vBox);
CloseButton = new Button
{
SizeFlagsHorizontal = SizeFlags.Expand | SizeFlags.ShrinkEnd,
Text = localization.GetString("Save and close"),
StyleClasses = {NanoStyle.StyleClassButtonBig}
};
var topHBox = new HBoxContainer
{
CustomMinimumSize = (0, 40),
Children =
{
new MarginContainer
{
MarginLeftOverride = 8,
Children =
{
new Label
{
Text = localization.GetString("Character Setup"),
StyleClasses = {NanoStyle.StyleClassLabelHeadingBigger},
VAlign = Label.VAlignMode.Center,
SizeFlagsHorizontal = SizeFlags.Expand | SizeFlags.ShrinkCenter
}
}
},
CloseButton
}
};
vBox.AddChild(topHBox);
vBox.AddChild(new PanelContainer
{
PanelOverride = new StyleBoxFlat
{
BackgroundColor = NanoStyle.NanoGold,
ContentMarginTopOverride = 2
}
});
var hBox = new HBoxContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
SeparationOverride = 0
};
vBox.AddChild(hBox);
_charactersVBox = new VBoxContainer();
hBox.AddChild(new MarginContainer
{
CustomMinimumSize = (420, 0),
SizeFlagsHorizontal = SizeFlags.Fill,
MarginTopOverride = 5,
MarginLeftOverride = 5,
Children =
{
new ScrollContainer
{
SizeFlagsVertical = SizeFlags.FillExpand,
Children =
{
_charactersVBox
}
}
}
});
_createNewCharacterButton = new Button
{
Text = "Create new slot...",
ToolTip = $"A maximum of {preferencesManager.Settings.MaxCharacterSlots} characters are allowed."
};
_createNewCharacterButton.OnPressed += args =>
{
preferencesManager.CreateCharacter(HumanoidCharacterProfile.Default());
UpdateUI();
};
hBox.AddChild(new PanelContainer
{
PanelOverride = new StyleBoxFlat {BackgroundColor = NanoStyle.NanoGold},
CustomMinimumSize = (2, 0)
});
_humanoidProfileEditor = new HumanoidProfileEditor(localization, preferencesManager);
_humanoidProfileEditor.OnProfileChanged += newProfile => { UpdateUI(); };
hBox.AddChild(_humanoidProfileEditor);
UpdateUI();
}
private void UpdateUI()
{
var numberOfFullSlots = 0;
var characterButtonsGroup = new ButtonGroup();
_charactersVBox.RemoveAllChildren();
var characterIndex = 0;
foreach (var character in _preferencesManager.Preferences.Characters)
{
if (character is null)
{
characterIndex++;
continue;
}
numberOfFullSlots++;
var characterPickerButton = new CharacterPickerButton(_entityManager,
_preferencesManager,
characterButtonsGroup,
character);
_charactersVBox.AddChild(characterPickerButton);
var characterIndexCopy = characterIndex;
characterPickerButton.ActualButton.OnPressed += args =>
{
_humanoidProfileEditor.Profile = (HumanoidCharacterProfile) character;
_humanoidProfileEditor.CharacterSlot = characterIndexCopy;
_humanoidProfileEditor.UpdateControls();
_preferencesManager.SelectCharacter(character);
};
characterIndex++;
}
_createNewCharacterButton.Disabled =
numberOfFullSlots >= _preferencesManager.Settings.MaxCharacterSlots;
_charactersVBox.AddChild(_createNewCharacterButton);
}
private class CharacterPickerButton : Control
{
public readonly Button ActualButton;
private IEntity _previewDummy;
public CharacterPickerButton(
IEntityManager entityManager,
IClientPreferencesManager preferencesManager,
ButtonGroup group,
ICharacterProfile profile)
{
_previewDummy = entityManager.SpawnEntityAt("HumanMob_Dummy",
new MapCoordinates(Vector2.Zero, MapId.Nullspace));
_previewDummy.GetComponent<HumanoidAppearanceComponent>().UpdateFromProfile(profile);
var isSelectedCharacter = profile == preferencesManager.Preferences.SelectedCharacter;
ActualButton = new Button
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
SizeFlagsVertical = SizeFlags.FillExpand,
ToggleMode = true,
Group = group
};
if (isSelectedCharacter)
ActualButton.Pressed = true;
AddChild(ActualButton);
var view = new SpriteView
{
Sprite = _previewDummy.GetComponent<SpriteComponent>(),
Scale = (2, 2),
MouseFilter = MouseFilterMode.Ignore,
OverrideDirection = Direction.South
};
var descriptionLabel = new Label
{
Text = $"{profile.Name}\nAssistant" //TODO implement job selection
};
var deleteButton = new Button
{
Text = "Delete",
Visible = !isSelectedCharacter,
SizeFlagsHorizontal = SizeFlags.ShrinkEnd
};
deleteButton.OnPressed += args =>
{
Parent.RemoveChild(this);
preferencesManager.DeleteCharacter(profile);
};
var internalHBox = new HBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
MouseFilter = MouseFilterMode.Ignore,
SeparationOverride = 0,
Children =
{
view,
descriptionLabel,
deleteButton
}
};
AddChild(internalHBox);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_previewDummy.Delete();
_previewDummy = null;
}
}
}
}

View File

@@ -0,0 +1,347 @@
using System;
using Content.Client.GameObjects.Components;
using Content.Client.Interfaces;
using Content.Shared.Preferences;
using Robust.Client.Graphics.Drawing;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
namespace Content.Client.UserInterface
{
public class HumanoidProfileEditor : Control
{
private static readonly StyleBoxFlat HighlightedStyle = new StyleBoxFlat
{
BackgroundColor = new Color(47, 47, 53),
ContentMarginTopOverride = 10,
ContentMarginBottomOverride = 10,
ContentMarginLeftOverride = 10,
ContentMarginRightOverride = 10
};
private readonly LineEdit _ageEdit;
private readonly LineEdit _nameEdit;
private readonly IClientPreferencesManager _preferencesManager;
private readonly Button _saveButton;
private readonly Button _sexFemaleButton;
private readonly Button _sexMaleButton;
private readonly HairStylePicker _hairPicker;
private readonly FacialHairStylePicker _facialHairPicker;
private bool _isDirty;
public int CharacterSlot;
public HumanoidCharacterProfile Profile;
public event Action<HumanoidCharacterProfile> OnProfileChanged;
public HumanoidProfileEditor(ILocalizationManager localization,
IClientPreferencesManager preferencesManager)
{
Profile = (HumanoidCharacterProfile) preferencesManager.Preferences.SelectedCharacter;
CharacterSlot = preferencesManager.Preferences.SelectedCharacterIndex;
_preferencesManager = preferencesManager;
var margin = new MarginContainer
{
MarginTopOverride = 10,
MarginBottomOverride = 10,
MarginLeftOverride = 10,
MarginRightOverride = 10
};
AddChild(margin);
var vBox = new VBoxContainer();
margin.AddChild(vBox);
var middleContainer = new HBoxContainer
{
SeparationOverride = 10
};
vBox.AddChild(middleContainer);
var leftColumn = new VBoxContainer();
middleContainer.AddChild(leftColumn);
#region Randomize
{
var panel = HighlightedContainer();
var randomizeEverythingButton = new Button
{
Text = localization.GetString("Randomize everything"),
Disabled = true,
ToolTip = "Not yet implemented!"
};
panel.AddChild(randomizeEverythingButton);
leftColumn.AddChild(panel);
}
#endregion Randomize
var middleColumn = new VBoxContainer();
leftColumn.AddChild(middleColumn);
#region Name
{
var panel = HighlightedContainer();
var hBox = new HBoxContainer
{
SizeFlagsVertical = SizeFlags.FillExpand
};
var nameLabel = new Label {Text = localization.GetString("Name:")};
_nameEdit = new LineEdit
{
CustomMinimumSize = (270, 0),
SizeFlagsVertical = SizeFlags.ShrinkCenter
};
_nameEdit.OnTextChanged += args =>
{
Profile = Profile?.WithName(args.Text);
IsDirty = true;
};
var nameRandomButton = new Button
{
Text = localization.GetString("Randomize"),
Disabled = true,
ToolTip = "Not implemented yet!"
};
hBox.AddChild(nameLabel);
hBox.AddChild(_nameEdit);
hBox.AddChild(nameRandomButton);
panel.AddChild(hBox);
middleColumn.AddChild(panel);
}
#endregion Name
var sexAndAgeRow = new HBoxContainer
{
SeparationOverride = 10
};
middleColumn.AddChild(sexAndAgeRow);
#region Sex
{
var panel = HighlightedContainer();
var hBox = new HBoxContainer();
var sexLabel = new Label {Text = localization.GetString("Sex:")};
var sexButtonGroup = new ButtonGroup();
_sexMaleButton = new Button
{
Text = localization.GetString("Male"),
Group = sexButtonGroup
};
_sexMaleButton.OnPressed += args =>
{
Profile = Profile?.WithSex(Sex.Male);
IsDirty = true;
};
_sexFemaleButton = new Button
{
Text = localization.GetString("Female"),
Group = sexButtonGroup
};
_sexFemaleButton.OnPressed += args =>
{
Profile = Profile?.WithSex(Sex.Female);
IsDirty = true;
};
hBox.AddChild(sexLabel);
hBox.AddChild(_sexMaleButton);
hBox.AddChild(_sexFemaleButton);
panel.AddChild(hBox);
sexAndAgeRow.AddChild(panel);
}
#endregion Sex
#region Age
{
var panel = HighlightedContainer();
var hBox = new HBoxContainer();
var ageLabel = new Label {Text = localization.GetString("Age:")};
_ageEdit = new LineEdit {CustomMinimumSize = (40, 0)};
_ageEdit.OnTextChanged += args =>
{
if (!int.TryParse(args.Text, out var newAge))
return;
Profile = Profile?.WithAge(newAge);
IsDirty = true;
};
hBox.AddChild(ageLabel);
hBox.AddChild(_ageEdit);
panel.AddChild(hBox);
sexAndAgeRow.AddChild(panel);
}
#endregion Age
var rightColumn = new VBoxContainer();
middleContainer.AddChild(rightColumn);
#region Import/Export
{
var panelContainer = HighlightedContainer();
var hBox = new HBoxContainer();
var importButton = new Button
{
Text = localization.GetString("Import"),
Disabled = true,
ToolTip = "Not yet implemented!"
};
var exportButton = new Button
{
Text = localization.GetString("Export"),
Disabled = true,
ToolTip = "Not yet implemented!"
};
hBox.AddChild(importButton);
hBox.AddChild(exportButton);
panelContainer.AddChild(hBox);
rightColumn.AddChild(panelContainer);
}
#endregion Import/Export
#region Save
{
var panel = HighlightedContainer();
_saveButton = new Button
{
Text = localization.GetString("Save"),
SizeFlagsHorizontal = SizeFlags.ShrinkCenter
};
_saveButton.OnPressed += args =>
{
IsDirty = false;
_preferencesManager.UpdateCharacter(Profile, CharacterSlot);
OnProfileChanged?.Invoke(Profile);
};
panel.AddChild(_saveButton);
rightColumn.AddChild(panel);
}
#endregion Save
#region Hair
{
var panel = HighlightedContainer();
panel.SizeFlagsHorizontal = SizeFlags.None;
var hairHBox = new HBoxContainer();
_hairPicker = new HairStylePicker();
_hairPicker.Populate();
_hairPicker.OnHairStylePicked += newStyle =>
{
if (Profile is null)
return;
Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithHairStyleName(newStyle));
IsDirty = true;
};
_hairPicker.OnHairColorPicked += newColor =>
{
if (Profile is null)
return;
Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithHairColor(newColor));
IsDirty = true;
};
_facialHairPicker = new FacialHairStylePicker();
_facialHairPicker.Populate();
_facialHairPicker.OnHairStylePicked += newStyle =>
{
if (Profile is null)
return;
Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithFacialHairStyleName(newStyle));
IsDirty = true;
};
_facialHairPicker.OnHairColorPicked += newColor =>
{
if (Profile is null)
return;
Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithFacialHairColor(newColor));
IsDirty = true;
};
hairHBox.AddChild(_hairPicker);
hairHBox.AddChild(_facialHairPicker);
panel.AddChild(hairHBox);
vBox.AddChild(panel);
}
#endregion Hair
UpdateControls();
}
private bool IsDirty
{
get => _isDirty;
set
{
_isDirty = value;
UpdateSaveButton();
}
}
private static Control HighlightedContainer()
{
return new PanelContainer
{
PanelOverride = HighlightedStyle
};
}
private void UpdateSexControls()
{
if (Profile.Sex == Sex.Male)
_sexMaleButton.Pressed = true;
else
_sexFemaleButton.Pressed = true;
}
private void UpdateHairPickers()
{
_hairPicker.SetInitialData(
Profile.Appearance.HairColor,
Profile.Appearance.HairStyleName);
_facialHairPicker.SetInitialData(
Profile.Appearance.FacialHairColor,
Profile.Appearance.FacialHairStyleName);
}
private void UpdateSaveButton()
{
_saveButton.Disabled = !(Profile is null) || !IsDirty;
}
public void UpdateControls()
{
if (Profile is null) return;
_nameEdit.Text = Profile?.Name;
UpdateSexControls();
_ageEdit.Text = Profile?.Age.ToString();
UpdateHairPickers();
UpdateSaveButton();
}
}
}

View File

@@ -0,0 +1,101 @@
using Content.Client.GameObjects.Components.Mobs;
using Content.Client.Interfaces;
using Content.Shared.Preferences;
using Robust.Client.Interfaces.GameObjects.Components;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Maths;
namespace Content.Client.UserInterface
{
public class LobbyCharacterPreviewPanel : Control
{
private readonly IClientPreferencesManager _preferencesManager;
private IEntity _previewDummy;
private readonly Label _summaryLabel;
public LobbyCharacterPreviewPanel(IEntityManager entityManager,
ILocalizationManager localization,
IClientPreferencesManager preferencesManager)
{
_preferencesManager = preferencesManager;
_previewDummy = entityManager.SpawnEntityAt("HumanMob_Dummy",
new MapCoordinates(Vector2.Zero, MapId.Nullspace));
var header = new NanoHeading
{
Text = localization.GetString("Character setup")
};
CharacterSetupButton = new Button
{
Text = localization.GetString("Customize"),
SizeFlagsHorizontal = SizeFlags.None
};
_summaryLabel = new Label();
var viewSouth = MakeSpriteView(_previewDummy, Direction.South);
var viewNorth = MakeSpriteView(_previewDummy, Direction.North);
var viewWest = MakeSpriteView(_previewDummy, Direction.West);
var viewEast = MakeSpriteView(_previewDummy, Direction.East);
var vBox = new VBoxContainer();
vBox.AddChild(header);
vBox.AddChild(CharacterSetupButton);
vBox.AddChild(_summaryLabel);
var hBox = new HBoxContainer();
hBox.AddChild(viewSouth);
hBox.AddChild(viewNorth);
hBox.AddChild(viewWest);
hBox.AddChild(viewEast);
vBox.AddChild(hBox);
AddChild(vBox);
UpdateUI();
}
public Button CharacterSetupButton { get; }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_previewDummy.Delete();
_previewDummy = null;
}
private static SpriteView MakeSpriteView(IEntity entity, Direction direction)
{
return new SpriteView
{
Sprite = entity.GetComponent<ISpriteComponent>(),
OverrideDirection = direction,
Scale = (2, 2)
};
}
public void UpdateUI()
{
if (!(_preferencesManager.Preferences.SelectedCharacter is HumanoidCharacterProfile selectedCharacter))
{
_summaryLabel.Text = string.Empty;
}
else
{
_summaryLabel.Text = selectedCharacter.Summary;
_previewDummy
.GetComponent<HumanoidAppearanceComponent>()
.Appearance = (HumanoidCharacterAppearance) selectedCharacter.CharacterAppearance;
}
}
}
}

View File

@@ -1,9 +1,11 @@
using Content.Client.Chat;
using Content.Client.Interfaces;
using Content.Client.Utility;
using Robust.Client.Graphics.Drawing;
using Robust.Client.Interfaces.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Maths;
@@ -19,8 +21,12 @@ namespace Content.Client.UserInterface
public ChatBox Chat { get; }
public ItemList OnlinePlayerItemList { get; }
public ServerInfo ServerInfo { get; }
public LobbyCharacterPreviewPanel CharacterPreview { get; }
public LobbyGui(ILocalizationManager localization, IResourceCache resourceCache)
public LobbyGui(IEntityManager entityManager,
ILocalizationManager localization,
IResourceCache resourceCache,
IClientPreferencesManager preferencesManager)
{
var margin = new MarginContainer
{
@@ -107,17 +113,20 @@ namespace Content.Client.UserInterface
};
vBox.AddChild(hBox);
CharacterPreview = new LobbyCharacterPreviewPanel(
entityManager,
localization,
preferencesManager)
{
SizeFlagsHorizontal = SizeFlags.None
};
hBox.AddChild(new VBoxContainer
{
SizeFlagsHorizontal = SizeFlags.FillExpand,
SeparationOverride = 0,
Children =
{
new Placeholder(resourceCache)
{
SizeFlagsVertical = SizeFlags.FillExpand,
PlaceholderText = localization.GetString("Character UI\nPlaceholder")
},
CharacterPreview,
new StripeBack
{