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
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 =>
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
268
Content.Client/UserInterface/CharacterSetupGui.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
347
Content.Client/UserInterface/HumanoidProfileEditor.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Content.Client/UserInterface/LobbyCharacterPreviewPanel.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets"/>
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
@@ -11,18 +11,14 @@
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.DefineConstants.targets"/>
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.DefineConstants.targets" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Migrations"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Content.Server.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
namespace Content.Server.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(PreferencesDbContext))]
|
||||
[Migration("20200111103836_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0");
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
|
||||
{
|
||||
b.Property<int>("HumanoidProfileId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Age")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CharacterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EyeColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FacialHairColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FacialHairName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HairColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HairName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("PrefsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Sex")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SkinColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Slot")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SlotName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("HumanoidProfileId");
|
||||
|
||||
b.HasIndex("PrefsId");
|
||||
|
||||
b.ToTable("HumanoidProfile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Prefs", b =>
|
||||
{
|
||||
b.Property<int>("PrefsId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SelectedCharacterSlot")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("PrefsId");
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Preferences");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Prefs", null)
|
||||
.WithMany("HumanoidProfiles")
|
||||
.HasForeignKey("PrefsId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace Content.Server.Database.Migrations
|
||||
{
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
"Preferences",
|
||||
table => new
|
||||
{
|
||||
PrefsId = table.Column<int>()
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Username = table.Column<string>(),
|
||||
SelectedCharacterSlot = table.Column<int>()
|
||||
},
|
||||
constraints: table => { table.PrimaryKey("PK_Preferences", x => x.PrefsId); });
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
"HumanoidProfile",
|
||||
table => new
|
||||
{
|
||||
HumanoidProfileId = table.Column<int>()
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
Slot = table.Column<int>(),
|
||||
SlotName = table.Column<string>(),
|
||||
CharacterName = table.Column<string>(),
|
||||
Age = table.Column<int>(),
|
||||
Sex = table.Column<string>(),
|
||||
HairName = table.Column<string>(),
|
||||
HairColor = table.Column<string>(),
|
||||
FacialHairName = table.Column<string>(),
|
||||
FacialHairColor = table.Column<string>(),
|
||||
EyeColor = table.Column<string>(),
|
||||
SkinColor = table.Column<string>(),
|
||||
PrefsId = table.Column<int>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_HumanoidProfile", x => x.HumanoidProfileId);
|
||||
table.ForeignKey(
|
||||
"FK_HumanoidProfile_Preferences_PrefsId",
|
||||
x => x.PrefsId,
|
||||
"Preferences",
|
||||
"PrefsId",
|
||||
onDelete: ReferentialAction.Restrict);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_HumanoidProfile_PrefsId",
|
||||
"HumanoidProfile",
|
||||
"PrefsId");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
"IX_Preferences_Username",
|
||||
"Preferences",
|
||||
"Username",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
"HumanoidProfile");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
"Preferences");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
// <auto-generated />
|
||||
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace Content.Server.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(PreferencesDbContext))]
|
||||
internal class PreferencesDbContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0");
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
|
||||
{
|
||||
b.Property<int>("HumanoidProfileId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("Age")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("CharacterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("EyeColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FacialHairColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FacialHairName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HairColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("HairName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int?>("PrefsId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Sex")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("SkinColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Slot")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("SlotName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("HumanoidProfileId");
|
||||
|
||||
b.HasIndex("PrefsId");
|
||||
|
||||
b.ToTable("HumanoidProfile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Prefs", b =>
|
||||
{
|
||||
b.Property<int>("PrefsId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("SelectedCharacterSlot")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("PrefsId");
|
||||
|
||||
b.HasIndex("Username")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Preferences");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.HumanoidProfile", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Prefs", null)
|
||||
.WithMany("HumanoidProfiles")
|
||||
.HasForeignKey("PrefsId");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,5 +47,8 @@ namespace Content.Server.Database
|
||||
public string FacialHairColor { get; set; } = null!;
|
||||
public string EyeColor { get; set; } = null!;
|
||||
public string SkinColor { get; set; } = null!;
|
||||
|
||||
public int PrefsId { get; set; }
|
||||
public Prefs Prefs { get; set; } = null!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ namespace Content.Server.Database
|
||||
|
||||
public Prefs GetPlayerPreferences(string username)
|
||||
{
|
||||
return _prefsCtx.Preferences.SingleOrDefault(p => p.Username == username);
|
||||
return _prefsCtx
|
||||
.Preferences
|
||||
.Include(p => p.HumanoidProfiles)
|
||||
.SingleOrDefault(p => p.Username == username);
|
||||
}
|
||||
|
||||
public void SaveSelectedCharacterIndex(string username, int slot)
|
||||
@@ -45,6 +48,7 @@ namespace Content.Server.Database
|
||||
.SingleOrDefault(h => h.Slot == newProfile.Slot);
|
||||
if (!(oldProfile is null)) prefs.HumanoidProfiles.Remove(oldProfile);
|
||||
prefs.HumanoidProfiles.Add(newProfile);
|
||||
_prefsCtx.SaveChanges();
|
||||
}
|
||||
|
||||
public void DeleteCharacterSlot(string username, int slot)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<PackageReference Include="YamlDotNet" Version="6.1.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Server.Database\Content.Server.Database.csproj"/>
|
||||
<ProjectReference Include="..\Content.Server.Database\Content.Server.Database.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj">
|
||||
<Private>false</Private>
|
||||
</ProjectReference>
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Content.Server.GameObjects.Components
|
||||
|
||||
private static void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
if (!obj.Session.AttachedEntity.TryGetComponent(out HairComponent hair))
|
||||
if (!obj.Session.AttachedEntity.TryGetComponent(out HumanoidAppearanceComponent looks))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -42,11 +42,11 @@ namespace Content.Server.GameObjects.Components
|
||||
|
||||
if (msg.IsFacialHair)
|
||||
{
|
||||
hair.FacialHairStyleName = msg.HairName;
|
||||
looks.Appearance = looks.Appearance.WithFacialHairStyleName(msg.HairName);
|
||||
}
|
||||
else
|
||||
{
|
||||
hair.HairStyleName = msg.HairName;
|
||||
looks.Appearance = looks.Appearance.WithHairStyleName(msg.HairName);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -57,11 +57,11 @@ namespace Content.Server.GameObjects.Components
|
||||
|
||||
if (msg.IsFacialHair)
|
||||
{
|
||||
hair.FacialHairColor = color;
|
||||
looks.Appearance = looks.Appearance.WithFacialHairColor(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
hair.HairColor = color;
|
||||
looks.Appearance = looks.Appearance.WithHairColor(color);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -75,7 +75,7 @@ namespace Content.Server.GameObjects.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (!eventArgs.User.TryGetComponent(out HairComponent hair))
|
||||
if (!eventArgs.User.TryGetComponent(out HumanoidAppearanceComponent looks))
|
||||
{
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("You can't have any hair!"));
|
||||
return;
|
||||
@@ -83,8 +83,8 @@ namespace Content.Server.GameObjects.Components
|
||||
|
||||
_userInterface.Open(actor.playerSession);
|
||||
|
||||
var msg = new MagicMirrorInitialDataMessage(hair.HairColor, hair.FacialHairColor, hair.HairStyleName,
|
||||
hair.FacialHairStyleName);
|
||||
var msg = new MagicMirrorInitialDataMessage(looks.Appearance.HairColor, looks.Appearance.FacialHairColor, looks.Appearance.HairStyleName,
|
||||
looks.Appearance.FacialHairStyleName);
|
||||
|
||||
_userInterface.SendMessage(msg, actor.playerSession);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.GameObjects;
|
||||
namespace Content.Server.GameObjects.Components.Mobs
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class HairComponent : SharedHairComponent
|
||||
public sealed class HumanoidAppearanceComponent : SharedHumanoidAppearanceComponent
|
||||
{
|
||||
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects;
|
||||
using Content.Server.GameObjects.Components.Markers;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameTicking.GamePresets;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Server.Interfaces.Chat;
|
||||
@@ -13,6 +14,7 @@ using Content.Server.Players;
|
||||
using Content.Shared;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.Jobs;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Server.Interfaces.Maps;
|
||||
using Robust.Server.Interfaces.Player;
|
||||
using Robust.Server.Player;
|
||||
@@ -41,16 +43,37 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
public class GameTicker : SharedGameTicker, IGameTicker
|
||||
{
|
||||
private const string PlayerPrototypeName = "HumanMob_Content";
|
||||
private const string ObserverPrototypeName = "MobObserver";
|
||||
private const string MapFile = "Maps/stationstation.yml";
|
||||
|
||||
// Seconds.
|
||||
private const float LobbyDuration = 20;
|
||||
|
||||
[ViewVariables] private readonly List<GameRule> _gameRules = new List<GameRule>();
|
||||
|
||||
// Value is whether they're ready.
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<IPlayerSession, bool> _playersInLobby = new Dictionary<IPlayerSession, bool>();
|
||||
|
||||
[ViewVariables] private bool _initialized;
|
||||
|
||||
[ViewVariables] private Type _presetType;
|
||||
|
||||
[ViewVariables] private bool _roundStartCountdownHasNotStartedYetDueToNoPlayers;
|
||||
private DateTime _roundStartTimeUtc;
|
||||
[ViewVariables] private GameRunLevel _runLevel;
|
||||
[ViewVariables(VVAccess.ReadWrite)] private GridCoordinates _spawnPoint;
|
||||
|
||||
[ViewVariables] private bool LobbyEnabled => _configurationManager.GetCVar<bool>("game.lobbyenabled");
|
||||
|
||||
[ViewVariables]
|
||||
public GameRunLevel RunLevel
|
||||
{
|
||||
get => _runLevel;
|
||||
private set
|
||||
{
|
||||
if (_runLevel == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_runLevel == value) return;
|
||||
|
||||
var old = _runLevel;
|
||||
_runLevel = value;
|
||||
@@ -61,46 +84,6 @@ namespace Content.Server.GameTicking
|
||||
|
||||
public event Action<GameRunLevelChangedEventArgs> OnRunLevelChanged;
|
||||
|
||||
private const string PlayerPrototypeName = "HumanMob_Content";
|
||||
private const string ObserverPrototypeName = "MobObserver";
|
||||
private const string MapFile = "Maps/stationstation.yml";
|
||||
|
||||
// Seconds.
|
||||
private const float LobbyDuration = 20;
|
||||
|
||||
[ViewVariables] private bool _initialized;
|
||||
[ViewVariables(VVAccess.ReadWrite)] private GridCoordinates _spawnPoint;
|
||||
[ViewVariables] private GameRunLevel _runLevel;
|
||||
|
||||
[ViewVariables] private bool LobbyEnabled => _configurationManager.GetCVar<bool>("game.lobbyenabled");
|
||||
|
||||
// Value is whether they're ready.
|
||||
[ViewVariables]
|
||||
private readonly Dictionary<IPlayerSession, bool> _playersInLobby = new Dictionary<IPlayerSession, bool>();
|
||||
|
||||
[ViewVariables] private bool _roundStartCountdownHasNotStartedYetDueToNoPlayers;
|
||||
private DateTime _roundStartTimeUtc;
|
||||
|
||||
[ViewVariables] private readonly List<GameRule> _gameRules = new List<GameRule>();
|
||||
|
||||
[ViewVariables] private Type _presetType;
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IEntityManager _entityManager;
|
||||
[Dependency] private IMapManager _mapManager;
|
||||
[Dependency] private IMapLoader _mapLoader;
|
||||
[Dependency] private IGameTiming _gameTiming;
|
||||
[Dependency] private IConfigurationManager _configurationManager;
|
||||
[Dependency] private IPlayerManager _playerManager;
|
||||
[Dependency] private IChatManager _chatManager;
|
||||
[Dependency] private IServerNetManager _netManager;
|
||||
[Dependency] private IDynamicTypeFactory _dynamicTypeFactory;
|
||||
[Dependency] private IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly ILocalizationManager _localization;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom;
|
||||
[Dependency] private readonly IServerPreferencesManager _prefsManager;
|
||||
#pragma warning restore 649
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
DebugTools.Assert(!_initialized);
|
||||
@@ -122,9 +105,7 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
if (RunLevel != GameRunLevel.PreRoundLobby || _roundStartTimeUtc > DateTime.UtcNow ||
|
||||
_roundStartCountdownHasNotStartedYetDueToNoPlayers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
StartRound();
|
||||
}
|
||||
@@ -144,13 +125,9 @@ namespace Content.Server.GameTicking
|
||||
else
|
||||
{
|
||||
if (_playerManager.PlayerCount == 0)
|
||||
{
|
||||
_roundStartCountdownHasNotStartedYetDueToNoPlayers = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_roundStartTimeUtc = DateTime.UtcNow + TimeSpan.FromSeconds(LobbyDuration);
|
||||
}
|
||||
|
||||
_sendStatusToAll();
|
||||
}
|
||||
@@ -168,10 +145,7 @@ namespace Content.Server.GameTicking
|
||||
|
||||
foreach (var (playerSession, ready) in _playersInLobby.ToList())
|
||||
{
|
||||
if (LobbyEnabled && !ready)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (LobbyEnabled && !ready) continue;
|
||||
|
||||
_spawnPlayer(playerSession);
|
||||
}
|
||||
@@ -192,41 +166,28 @@ namespace Content.Server.GameTicking
|
||||
targetPlayer.ContentData().WipeMind();
|
||||
|
||||
if (LobbyEnabled)
|
||||
{
|
||||
_playerJoinLobby(targetPlayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
_spawnPlayer(targetPlayer);
|
||||
}
|
||||
}
|
||||
|
||||
public void MakeObserve(IPlayerSession player)
|
||||
{
|
||||
if (!_playersInLobby.ContainsKey(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!_playersInLobby.ContainsKey(player)) return;
|
||||
|
||||
_spawnObserver(player);
|
||||
}
|
||||
|
||||
public void MakeJoinGame(IPlayerSession player)
|
||||
{
|
||||
if (!_playersInLobby.ContainsKey(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!_playersInLobby.ContainsKey(player)) return;
|
||||
|
||||
_spawnPlayer(player);
|
||||
}
|
||||
|
||||
public void ToggleReady(IPlayerSession player, bool ready)
|
||||
{
|
||||
if (!_playersInLobby.ContainsKey(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!_playersInLobby.ContainsKey(player)) return;
|
||||
|
||||
_playersInLobby[player] = ready;
|
||||
_netManager.ServerSendMessage(_getStatusMsg(player), player.ConnectedClient);
|
||||
@@ -244,10 +205,7 @@ namespace Content.Server.GameTicking
|
||||
|
||||
public void RemoveGameRule(GameRule rule)
|
||||
{
|
||||
if (_gameRules.Contains(rule))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_gameRules.Contains(rule)) return;
|
||||
|
||||
rule.Removed();
|
||||
|
||||
@@ -258,10 +216,8 @@ namespace Content.Server.GameTicking
|
||||
|
||||
public void SetStartPreset(Type type)
|
||||
{
|
||||
if (!typeof(GamePreset).IsAssignableFrom(type))
|
||||
{
|
||||
throw new ArgumentException("type must inherit GamePreset");
|
||||
}
|
||||
if (!typeof(GamePreset).IsAssignableFrom(type)) throw new ArgumentException("type must inherit GamePreset");
|
||||
|
||||
_presetType = type;
|
||||
UpdateInfoText();
|
||||
}
|
||||
@@ -280,6 +236,7 @@ namespace Content.Server.GameTicking
|
||||
Logger.Error("{0} is an invalid equipment slot.", slotStr);
|
||||
continue;
|
||||
}
|
||||
|
||||
var equipmentEntity = _entityManager.SpawnEntity(equipmentStr, entity.Transform.GridPosition);
|
||||
inventory.Equip(slot, equipmentEntity.GetComponent<ClothingComponent>());
|
||||
}
|
||||
@@ -288,6 +245,14 @@ namespace Content.Server.GameTicking
|
||||
return entity;
|
||||
}
|
||||
|
||||
private void ApplyCharacterProfile(IEntity entity, ICharacterProfile profile)
|
||||
{
|
||||
if (profile is null)
|
||||
return;
|
||||
entity.GetComponent<HumanoidAppearanceComponent>().UpdateFromProfile(profile);
|
||||
entity.Name = profile.Name;
|
||||
}
|
||||
|
||||
private IEntity _spawnObserverMob()
|
||||
{
|
||||
return _entityManager.SpawnEntityAt(ObserverPrototypeName, _getLateJoinSpawnPoint());
|
||||
@@ -301,16 +266,10 @@ namespace Content.Server.GameTicking
|
||||
foreach (var entity in _entityManager.GetEntities(new TypeEntityQuery(typeof(SpawnPointComponent))))
|
||||
{
|
||||
var point = entity.GetComponent<SpawnPointComponent>();
|
||||
if (point.SpawnType == SpawnPointType.LateJoin)
|
||||
{
|
||||
possiblePoints.Add(entity.Transform.GridPosition);
|
||||
}
|
||||
if (point.SpawnType == SpawnPointType.LateJoin) possiblePoints.Add(entity.Transform.GridPosition);
|
||||
}
|
||||
|
||||
if (possiblePoints.Count != 0)
|
||||
{
|
||||
location = _robustRandom.Pick(possiblePoints);
|
||||
}
|
||||
if (possiblePoints.Count != 0) location = _robustRandom.Pick(possiblePoints);
|
||||
|
||||
return location;
|
||||
}
|
||||
@@ -323,44 +282,29 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
// Delete all entities.
|
||||
foreach (var entity in _entityManager.GetEntities().ToList())
|
||||
{
|
||||
// TODO: Maybe something less naive here?
|
||||
// FIXME: Actually, definitely.
|
||||
entity.Delete();
|
||||
}
|
||||
|
||||
// Delete all maps outside of nullspace.
|
||||
foreach (var mapId in _mapManager.GetAllMapIds().ToList())
|
||||
{
|
||||
// TODO: Maybe something less naive here?
|
||||
if (mapId != MapId.Nullspace)
|
||||
{
|
||||
_mapManager.DeleteMap(mapId);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete the minds of everybody.
|
||||
// TODO: Maybe move this into a separate manager?
|
||||
foreach (var unCastData in _playerManager.GetAllPlayerData())
|
||||
{
|
||||
unCastData.ContentData().WipeMind();
|
||||
}
|
||||
foreach (var unCastData in _playerManager.GetAllPlayerData()) unCastData.ContentData().WipeMind();
|
||||
|
||||
// Clear up any game rules.
|
||||
foreach (var rule in _gameRules)
|
||||
{
|
||||
rule.Removed();
|
||||
}
|
||||
foreach (var rule in _gameRules) rule.Removed();
|
||||
|
||||
_gameRules.Clear();
|
||||
|
||||
// Move everybody currently in the server to lobby.
|
||||
foreach (var player in _playerManager.GetAllPlayers())
|
||||
{
|
||||
if (_playersInLobby.ContainsKey(player))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (_playersInLobby.ContainsKey(player)) continue;
|
||||
|
||||
_playerJoinLobby(player);
|
||||
}
|
||||
@@ -388,9 +332,7 @@ namespace Content.Server.GameTicking
|
||||
{
|
||||
// Always make sure the client has player data. Mind gets assigned on spawn.
|
||||
if (session.Data.ContentDataUncast == null)
|
||||
{
|
||||
session.Data.ContentDataUncast = new PlayerData(session.SessionId);
|
||||
}
|
||||
|
||||
// timer time must be > tick length
|
||||
Timer.Spawn(0, args.Session.JoinGame);
|
||||
@@ -437,10 +379,7 @@ namespace Content.Server.GameTicking
|
||||
|
||||
case SessionStatus.Disconnected:
|
||||
{
|
||||
if (_playersInLobby.ContainsKey(session))
|
||||
{
|
||||
_playersInLobby.Remove(session);
|
||||
}
|
||||
if (_playersInLobby.ContainsKey(session)) _playersInLobby.Remove(session);
|
||||
|
||||
_chatManager.DispatchServerAnnouncement($"Player {args.Session.SessionId} left server!");
|
||||
break;
|
||||
@@ -460,6 +399,10 @@ namespace Content.Server.GameTicking
|
||||
|
||||
var mob = _spawnPlayerMob(job);
|
||||
data.Mind.TransferTo(mob);
|
||||
var character = _prefsManager
|
||||
.GetPreferences(session.SessionId.Username)
|
||||
.SelectedCharacter;
|
||||
ApplyCharacterProfile(mob, character);
|
||||
}
|
||||
|
||||
private void _spawnObserver(IPlayerSession session)
|
||||
@@ -485,11 +428,9 @@ namespace Content.Server.GameTicking
|
||||
|
||||
private void _playerJoinGame(IPlayerSession session)
|
||||
{
|
||||
_chatManager.DispatchServerMessage(session, $"Welcome to Space Station 14! If this is your first time checking out the game, be sure to check out the tutorial in the top left!");
|
||||
if (_playersInLobby.ContainsKey(session))
|
||||
{
|
||||
_playersInLobby.Remove(session);
|
||||
}
|
||||
_chatManager.DispatchServerMessage(session,
|
||||
"Welcome to Space Station 14! If this is your first time checking out the game, be sure to check out the tutorial in the top left!");
|
||||
if (_playersInLobby.ContainsKey(session)) _playersInLobby.Remove(session);
|
||||
|
||||
_netManager.ServerSendMessage(_netManager.CreateNetMessage<MsgTickerJoinGame>(), session.ConnectedClient);
|
||||
}
|
||||
@@ -514,9 +455,7 @@ namespace Content.Server.GameTicking
|
||||
private void _sendStatusToAll()
|
||||
{
|
||||
foreach (var player in _playersInLobby.Keys)
|
||||
{
|
||||
_netManager.ServerSendMessage(_getStatusMsg(player), player.ConnectedClient);
|
||||
}
|
||||
}
|
||||
|
||||
private string GetInfoText()
|
||||
@@ -538,6 +477,22 @@ The current game mode is [color=white]{0}[/color]", gameMode);
|
||||
{
|
||||
return _dynamicTypeFactory.CreateInstance<GamePreset>(_presetType ?? typeof(PresetSandbox));
|
||||
}
|
||||
|
||||
#pragma warning disable 649
|
||||
[Dependency] private IEntityManager _entityManager;
|
||||
[Dependency] private IMapManager _mapManager;
|
||||
[Dependency] private IMapLoader _mapLoader;
|
||||
[Dependency] private IGameTiming _gameTiming;
|
||||
[Dependency] private IConfigurationManager _configurationManager;
|
||||
[Dependency] private IPlayerManager _playerManager;
|
||||
[Dependency] private IChatManager _chatManager;
|
||||
[Dependency] private IServerNetManager _netManager;
|
||||
[Dependency] private IDynamicTypeFactory _dynamicTypeFactory;
|
||||
[Dependency] private IPrototypeManager _prototypeManager;
|
||||
[Dependency] private readonly ILocalizationManager _localization;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom;
|
||||
[Dependency] private readonly IServerPreferencesManager _prefsManager;
|
||||
#pragma warning restore 649
|
||||
}
|
||||
|
||||
public enum GameRunLevel
|
||||
@@ -549,13 +504,13 @@ The current game mode is [color=white]{0}[/color]", gameMode);
|
||||
|
||||
public class GameRunLevelChangedEventArgs : EventArgs
|
||||
{
|
||||
public GameRunLevel OldRunLevel { get; }
|
||||
public GameRunLevel NewRunLevel { get; }
|
||||
|
||||
public GameRunLevelChangedEventArgs(GameRunLevel oldRunLevel, GameRunLevel newRunLevel)
|
||||
{
|
||||
OldRunLevel = oldRunLevel;
|
||||
NewRunLevel = newRunLevel;
|
||||
}
|
||||
|
||||
public GameRunLevel OldRunLevel { get; }
|
||||
public GameRunLevel NewRunLevel { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,27 +29,28 @@ namespace Content.Server.Preferences
|
||||
|
||||
var profiles = new ICharacterProfile[_maxCharacterSlots];
|
||||
foreach (var profile in prefs.HumanoidProfiles)
|
||||
profiles[profile.Slot] = new HumanoidCharacterProfile
|
||||
{
|
||||
Name = profile.CharacterName,
|
||||
Age = profile.Age,
|
||||
Sex = profile.Sex == "Male" ? Male : Female,
|
||||
CharacterAppearance = new HumanoidCharacterAppearance
|
||||
{
|
||||
HairStyleName = profile.HairName,
|
||||
HairColor = Color.FromHex(profile.HairColor),
|
||||
FacialHairStyleName = profile.FacialHairName,
|
||||
FacialHairColor = Color.FromHex(profile.FacialHairColor),
|
||||
EyeColor = Color.FromHex(profile.EyeColor),
|
||||
SkinColor = Color.FromHex(profile.SkinColor)
|
||||
}
|
||||
};
|
||||
{
|
||||
profiles[profile.Slot] = new HumanoidCharacterProfile(
|
||||
profile.CharacterName,
|
||||
profile.Age,
|
||||
profile.Sex == "Male" ? Male : Female,
|
||||
new HumanoidCharacterAppearance
|
||||
(
|
||||
profile.HairName,
|
||||
Color.FromHex(profile.HairColor),
|
||||
profile.FacialHairName,
|
||||
Color.FromHex(profile.FacialHairColor),
|
||||
Color.FromHex(profile.EyeColor),
|
||||
Color.FromHex(profile.SkinColor)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return new PlayerPreferences
|
||||
{
|
||||
SelectedCharacterIndex = prefs.SelectedCharacterSlot,
|
||||
Characters = profiles.ToList()
|
||||
};
|
||||
(
|
||||
profiles,
|
||||
prefs.SelectedCharacterSlot
|
||||
);
|
||||
}
|
||||
|
||||
public void SaveSelectedCharacterIndex(string username, int index)
|
||||
@@ -71,10 +72,10 @@ namespace Content.Server.Preferences
|
||||
if (!(profile is HumanoidCharacterProfile humanoid))
|
||||
// TODO: Handle other ICharacterProfile implementations properly
|
||||
throw new NotImplementedException();
|
||||
|
||||
var appearance = (HumanoidCharacterAppearance) humanoid.CharacterAppearance;
|
||||
_prefsDb.SaveCharacterSlot(username, new HumanoidProfile
|
||||
{
|
||||
SlotName = humanoid.Name,
|
||||
CharacterName = humanoid.Name,
|
||||
Age = humanoid.Age,
|
||||
Sex = humanoid.Sex.ToString(),
|
||||
|
||||
@@ -1,109 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.Preferences.Appearance;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Mobs
|
||||
{
|
||||
public abstract class SharedHairComponent : Component
|
||||
{
|
||||
private static readonly Color DefaultHairColor = Color.FromHex("#232323");
|
||||
|
||||
private string _facialHairStyleName = HairStyles.DefaultFacialHairStyle;
|
||||
private string _hairStyleName = HairStyles.DefaultHairStyle;
|
||||
private Color _hairColor = DefaultHairColor;
|
||||
private Color _facialHairColor = DefaultHairColor;
|
||||
|
||||
public sealed override string Name => "Hair";
|
||||
public sealed override uint? NetID => ContentNetIDs.HAIR;
|
||||
public sealed override Type StateType => typeof(HairComponentState);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public virtual string HairStyleName
|
||||
{
|
||||
get => _hairStyleName;
|
||||
set
|
||||
{
|
||||
_hairStyleName = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public virtual string FacialHairStyleName
|
||||
{
|
||||
get => _facialHairStyleName;
|
||||
set
|
||||
{
|
||||
_facialHairStyleName = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public virtual Color HairColor
|
||||
{
|
||||
get => _hairColor;
|
||||
set
|
||||
{
|
||||
_hairColor = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public virtual Color FacialHairColor
|
||||
{
|
||||
get => _facialHairColor;
|
||||
set
|
||||
{
|
||||
_facialHairColor = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new HairComponentState(HairStyleName, FacialHairStyleName, HairColor, FacialHairColor);
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
var cast = (HairComponentState) curState;
|
||||
|
||||
HairStyleName = cast.HairStyleName;
|
||||
FacialHairStyleName = cast.FacialHairStyleName;
|
||||
HairColor = cast.HairColor;
|
||||
FacialHairColor = cast.FacialHairColor;
|
||||
}
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref _hairColor, "hairColor", DefaultHairColor);
|
||||
serializer.DataField(ref _facialHairColor, "facialHairColor", DefaultHairColor);
|
||||
serializer.DataField(ref _hairStyleName, "hairStyle", HairStyles.DefaultHairStyle);
|
||||
serializer.DataField(ref _facialHairStyleName, "facialHairStyle", HairStyles.DefaultFacialHairStyle);
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
private sealed class HairComponentState : ComponentState
|
||||
{
|
||||
public string HairStyleName { get; }
|
||||
public string FacialHairStyleName { get; }
|
||||
public Color HairColor { get; }
|
||||
public Color FacialHairColor { get; }
|
||||
|
||||
public HairComponentState(string hairStyleName, string facialHairStyleName, Color hairColor, Color facialHairColor) : base(ContentNetIDs.HAIR)
|
||||
{
|
||||
HairStyleName = hairStyleName;
|
||||
FacialHairStyleName = facialHairStyleName;
|
||||
HairColor = hairColor;
|
||||
FacialHairColor = facialHairColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Mobs
|
||||
{
|
||||
public abstract class SharedHumanoidAppearanceComponent : Component
|
||||
{
|
||||
private HumanoidCharacterAppearance _appearance;
|
||||
private Sex _sex;
|
||||
|
||||
public sealed override string Name => "HumanoidAppearance";
|
||||
public sealed override uint? NetID => ContentNetIDs.HUMANOID_APPEARANCE;
|
||||
public sealed override Type StateType => typeof(HumanoidAppearanceComponentState);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public virtual HumanoidCharacterAppearance Appearance
|
||||
{
|
||||
get => _appearance;
|
||||
set
|
||||
{
|
||||
_appearance = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public virtual Sex Sex
|
||||
{
|
||||
get => _sex;
|
||||
set
|
||||
{
|
||||
_sex = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new HumanoidAppearanceComponentState(Appearance, Sex);
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState curState, ComponentState nextState)
|
||||
{
|
||||
var cast = (HumanoidAppearanceComponentState) curState;
|
||||
Appearance = cast.Appearance;
|
||||
Sex = cast.Sex;
|
||||
}
|
||||
|
||||
public void UpdateFromProfile(ICharacterProfile profile)
|
||||
{
|
||||
var humanoid = (HumanoidCharacterProfile) profile;
|
||||
Appearance = (HumanoidCharacterAppearance) humanoid.CharacterAppearance;
|
||||
Sex = humanoid.Sex;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
[NetSerializable]
|
||||
private sealed class HumanoidAppearanceComponentState : ComponentState
|
||||
{
|
||||
public HumanoidAppearanceComponentState(HumanoidCharacterAppearance appearance, Sex sex) : base(ContentNetIDs.HUMANOID_APPEARANCE)
|
||||
{
|
||||
Appearance = appearance;
|
||||
Sex = sex;
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance Appearance { get; }
|
||||
public Sex Sex { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
public const uint ITEMCOOLDOWN = 1029;
|
||||
public const uint CARGO_ORDER_DATABASE = 1030;
|
||||
public const uint GALACTIC_MARKET = 1031;
|
||||
public const uint HAIR = 1032;
|
||||
public const uint HUMANOID_APPEARANCE = 1032;
|
||||
public const uint INSTRUMENTS = 1033;
|
||||
public const uint WELDER = 1034;
|
||||
public const uint STACK = 1035;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Preferences.Appearance
|
||||
{
|
||||
public enum HumanoidVisualLayers
|
||||
{
|
||||
Hair,
|
||||
FacialHair,
|
||||
Body
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,24 +7,69 @@ namespace Content.Shared.Preferences
|
||||
[Serializable, NetSerializable]
|
||||
public class HumanoidCharacterAppearance : ICharacterAppearance
|
||||
{
|
||||
public string HairStyleName { get; set; }
|
||||
public Color HairColor { get; set; }
|
||||
public string FacialHairStyleName { get; set; }
|
||||
public Color FacialHairColor { get; set; }
|
||||
public Color EyeColor { get; set; }
|
||||
public Color SkinColor { get; set; }
|
||||
public HumanoidCharacterAppearance(string hairStyleName,
|
||||
Color hairColor,
|
||||
string facialHairStyleName,
|
||||
Color facialHairColor,
|
||||
Color eyeColor,
|
||||
Color skinColor)
|
||||
{
|
||||
HairStyleName = hairStyleName;
|
||||
HairColor = hairColor;
|
||||
FacialHairStyleName = facialHairStyleName;
|
||||
FacialHairColor = facialHairColor;
|
||||
EyeColor = eyeColor;
|
||||
SkinColor = skinColor;
|
||||
}
|
||||
|
||||
public string HairStyleName { get; }
|
||||
public Color HairColor { get; }
|
||||
public string FacialHairStyleName { get; }
|
||||
public Color FacialHairColor { get; }
|
||||
public Color EyeColor { get; }
|
||||
public Color SkinColor { get; }
|
||||
|
||||
public HumanoidCharacterAppearance WithHairStyleName(string newName)
|
||||
{
|
||||
return new HumanoidCharacterAppearance(newName, HairColor, FacialHairStyleName, FacialHairColor, EyeColor, SkinColor);
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithHairColor(Color newColor)
|
||||
{
|
||||
return new HumanoidCharacterAppearance(HairStyleName, newColor, FacialHairStyleName, FacialHairColor, EyeColor, SkinColor);
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithFacialHairStyleName(string newName)
|
||||
{
|
||||
return new HumanoidCharacterAppearance(HairStyleName, HairColor, newName, FacialHairColor, EyeColor, SkinColor);
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithFacialHairColor(Color newColor)
|
||||
{
|
||||
return new HumanoidCharacterAppearance(HairStyleName, HairColor, FacialHairStyleName, newColor, EyeColor, SkinColor);
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithEyeColor(Color newColor)
|
||||
{
|
||||
return new HumanoidCharacterAppearance(HairStyleName, HairColor, FacialHairStyleName, FacialHairColor, newColor, SkinColor);
|
||||
}
|
||||
|
||||
public HumanoidCharacterAppearance WithSkinColor(Color newColor)
|
||||
{
|
||||
return new HumanoidCharacterAppearance(HairStyleName, HairColor, FacialHairStyleName, FacialHairColor, EyeColor, newColor);
|
||||
}
|
||||
|
||||
public static HumanoidCharacterAppearance Default()
|
||||
{
|
||||
return new HumanoidCharacterAppearance
|
||||
{
|
||||
HairStyleName = "Bald",
|
||||
HairColor = Color.Black,
|
||||
FacialHairStyleName = "Shaved",
|
||||
FacialHairColor = Color.Black,
|
||||
EyeColor = Color.Black,
|
||||
SkinColor = Color.Black
|
||||
};
|
||||
(
|
||||
"Bald",
|
||||
Color.Black,
|
||||
"Shaved",
|
||||
Color.Black,
|
||||
Color.Black,
|
||||
Color.Black
|
||||
);
|
||||
}
|
||||
|
||||
public bool MemberwiseEquals(ICharacterAppearance maybeOther)
|
||||
|
||||
@@ -6,21 +6,49 @@ namespace Content.Shared.Preferences
|
||||
[Serializable, NetSerializable]
|
||||
public class HumanoidCharacterProfile : ICharacterProfile
|
||||
{
|
||||
public static HumanoidCharacterProfile Default()
|
||||
public HumanoidCharacterProfile(string name,
|
||||
int age,
|
||||
Sex sex,
|
||||
HumanoidCharacterAppearance appearance)
|
||||
{
|
||||
return new HumanoidCharacterProfile
|
||||
{
|
||||
Name = "John Doe",
|
||||
Age = 18,
|
||||
Sex = Sex.Male,
|
||||
CharacterAppearance = HumanoidCharacterAppearance.Default()
|
||||
};
|
||||
Name = name;
|
||||
Age = age;
|
||||
Sex = sex;
|
||||
Appearance = appearance;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public int Age { get; set; }
|
||||
public Sex Sex { get; set; }
|
||||
public ICharacterAppearance CharacterAppearance { get; set; }
|
||||
public static HumanoidCharacterProfile Default()
|
||||
{
|
||||
return new HumanoidCharacterProfile("John Doe", 18, Sex.Male, HumanoidCharacterAppearance.Default());
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
public int Age { get; }
|
||||
public Sex Sex { get; }
|
||||
public ICharacterAppearance CharacterAppearance => Appearance;
|
||||
public HumanoidCharacterAppearance Appearance { get; }
|
||||
|
||||
public HumanoidCharacterProfile WithName(string name)
|
||||
{
|
||||
return new HumanoidCharacterProfile(name, Age, Sex, Appearance);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithAge(int age)
|
||||
{
|
||||
return new HumanoidCharacterProfile(Name, age, Sex, Appearance);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithSex(Sex sex)
|
||||
{
|
||||
return new HumanoidCharacterProfile(Name, Age, sex, Appearance);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithCharacterAppearance(HumanoidCharacterAppearance appearance)
|
||||
{
|
||||
return new HumanoidCharacterProfile(Name, Age, Sex, appearance);
|
||||
}
|
||||
|
||||
public string Summary => $"{Name}, {Age} years old {Sex.ToString().ToLower()} human.\nOccupation: to be implemented.";
|
||||
|
||||
public bool MemberwiseEquals(ICharacterProfile maybeOther)
|
||||
{
|
||||
@@ -28,9 +56,7 @@ namespace Content.Shared.Preferences
|
||||
if (Name != other.Name) return false;
|
||||
if (Age != other.Age) return false;
|
||||
if (Sex != other.Sex) return false;
|
||||
if (CharacterAppearance is null)
|
||||
return other.CharacterAppearance is null;
|
||||
return CharacterAppearance.MemberwiseEquals(other.CharacterAppearance);
|
||||
return Appearance.MemberwiseEquals(other.Appearance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ namespace Content.Shared.Preferences
|
||||
{
|
||||
public interface ICharacterProfile
|
||||
{
|
||||
string Name { get; }
|
||||
ICharacterAppearance CharacterAppearance { get; }
|
||||
bool MemberwiseEquals(ICharacterProfile other);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Preferences
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains all player characters and the index of the currently selected character.
|
||||
/// Serialized both over the network and to disk.
|
||||
/// Contains all player characters and the index of the currently selected character.
|
||||
/// Serialized both over the network and to disk.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public class PlayerPreferences
|
||||
[Serializable]
|
||||
[NetSerializable]
|
||||
public sealed class PlayerPreferences
|
||||
{
|
||||
private List<ICharacterProfile> _characters;
|
||||
|
||||
public PlayerPreferences(IEnumerable<ICharacterProfile> characters, int selectedCharacterIndex)
|
||||
{
|
||||
_characters = characters.ToList();
|
||||
SelectedCharacterIndex = selectedCharacterIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All player characters.
|
||||
/// </summary>
|
||||
public IEnumerable<ICharacterProfile> Characters => _characters.AsEnumerable();
|
||||
|
||||
/// <summary>
|
||||
/// Index of the currently selected character.
|
||||
/// </summary>
|
||||
public int SelectedCharacterIndex { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The currently selected character.
|
||||
/// </summary>
|
||||
public ICharacterProfile SelectedCharacter => Characters.ElementAtOrDefault(SelectedCharacterIndex);
|
||||
|
||||
public int FirstEmptySlot => IndexOfCharacter(null);
|
||||
|
||||
public static PlayerPreferences Default()
|
||||
{
|
||||
return new PlayerPreferences
|
||||
{
|
||||
Characters = new List<ICharacterProfile>
|
||||
return new PlayerPreferences(new List<ICharacterProfile>
|
||||
{
|
||||
HumanoidCharacterProfile.Default()
|
||||
},
|
||||
SelectedCharacterIndex = 0
|
||||
};
|
||||
0);
|
||||
}
|
||||
|
||||
private List<ICharacterProfile> _characters;
|
||||
private int _selectedCharacterIndex;
|
||||
|
||||
/// <summary>
|
||||
/// All player characters.
|
||||
/// </summary>
|
||||
public List<ICharacterProfile> Characters
|
||||
public int IndexOfCharacter(ICharacterProfile profile)
|
||||
{
|
||||
get => _characters;
|
||||
set => _characters = value;
|
||||
return _characters.FindIndex(x => x == profile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Index of the currently selected character.
|
||||
/// </summary>
|
||||
public int SelectedCharacterIndex
|
||||
{
|
||||
get => _selectedCharacterIndex;
|
||||
set => _selectedCharacterIndex = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the currently selected character.
|
||||
/// </summary>
|
||||
public ICharacterProfile SelectedCharacter => Characters.ElementAtOrDefault(SelectedCharacterIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Content.Server.Preferences;
|
||||
using Content.Shared.Preferences;
|
||||
using NUnit.Framework;
|
||||
@@ -12,23 +13,21 @@ namespace Content.Tests.Server.Preferences
|
||||
{
|
||||
private const int MaxCharacterSlots = 10;
|
||||
|
||||
private static ICharacterProfile CharlieCharlieson()
|
||||
private static HumanoidCharacterProfile CharlieCharlieson()
|
||||
{
|
||||
return new HumanoidCharacterProfile
|
||||
{
|
||||
Name = "Charlie Charlieson",
|
||||
Age = 21,
|
||||
Sex = Sex.Male,
|
||||
CharacterAppearance = new HumanoidCharacterAppearance()
|
||||
{
|
||||
HairStyleName = "Afro",
|
||||
HairColor = Color.Aqua,
|
||||
FacialHairStyleName = "Shaved",
|
||||
FacialHairColor = Color.Aquamarine,
|
||||
EyeColor = Color.Azure,
|
||||
SkinColor = Color.Beige
|
||||
}
|
||||
};
|
||||
return new HumanoidCharacterProfile(
|
||||
"Charlie Charlieson",
|
||||
21,
|
||||
Sex.Male,
|
||||
new HumanoidCharacterAppearance(
|
||||
"Afro",
|
||||
Color.Aqua,
|
||||
"Shaved",
|
||||
Color.Aquamarine,
|
||||
Color.Azure,
|
||||
Color.Beige
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private static PreferencesDatabase GetDb()
|
||||
@@ -52,7 +51,7 @@ namespace Content.Tests.Server.Preferences
|
||||
var prefs = db.GetPlayerPreferences(username);
|
||||
Assert.NotNull(prefs);
|
||||
Assert.Zero(prefs.SelectedCharacterIndex);
|
||||
Assert.That(prefs.Characters.TrueForAll(character => character is null));
|
||||
Assert.That(prefs.Characters.ToList().TrueForAll(character => character is null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -65,7 +64,7 @@ namespace Content.Tests.Server.Preferences
|
||||
db.SaveSelectedCharacterIndex(username, slot);
|
||||
db.SaveCharacterSlot(username, originalProfile, slot);
|
||||
var prefs = db.GetPlayerPreferences(username);
|
||||
Assert.That(prefs.Characters[slot].MemberwiseEquals(originalProfile));
|
||||
Assert.That(prefs.Characters.ElementAt(slot).MemberwiseEquals(originalProfile));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -78,7 +77,7 @@ namespace Content.Tests.Server.Preferences
|
||||
db.SaveCharacterSlot(username, CharlieCharlieson(), slot);
|
||||
db.SaveCharacterSlot(username, null, slot);
|
||||
var prefs = db.GetPlayerPreferences(username);
|
||||
Assert.That(prefs.Characters.TrueForAll(character => character is null));
|
||||
Assert.That(prefs.Characters.ToList().TrueForAll(character => character is null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -22,7 +22,8 @@
|
||||
drawdepth: Mobs
|
||||
|
||||
layers:
|
||||
- sprite: Mob/human.rsi
|
||||
- map: ["enum.HumanoidVisualLayers.Body"]
|
||||
sprite: Mob/human.rsi
|
||||
state: male
|
||||
- map: ["enum.Slots.INNERCLOTHING"]
|
||||
- map: ["enum.Slots.IDCARD"]
|
||||
@@ -82,4 +83,71 @@
|
||||
- type: Examiner
|
||||
- type: CharacterInfo
|
||||
- type: FootstepSound
|
||||
- type: Hair
|
||||
- type: HumanoidAppearance
|
||||
|
||||
- type: entity
|
||||
save: false
|
||||
name: Urist McHands
|
||||
id: HumanMob_Dummy
|
||||
description: A dummy human meant to be used in character setup
|
||||
components:
|
||||
- type: Hands
|
||||
hands:
|
||||
- left
|
||||
- right
|
||||
# Organs
|
||||
- type: Stomach
|
||||
|
||||
- type: Inventory
|
||||
- type: Sprite
|
||||
netsync: false
|
||||
drawdepth: Mobs
|
||||
|
||||
layers:
|
||||
- map: ["enum.HumanoidVisualLayers.Body"]
|
||||
sprite: Mob/human.rsi
|
||||
state: male
|
||||
- map: ["enum.Slots.INNERCLOTHING"]
|
||||
- map: ["enum.Slots.IDCARD"]
|
||||
- map: ["enum.Slots.GLOVES"]
|
||||
- map: ["enum.Slots.SHOES"]
|
||||
- map: ["enum.Slots.EARS"]
|
||||
- map: ["enum.Slots.OUTERCLOTHING"]
|
||||
- map: ["enum.Slots.EYES"]
|
||||
- map: ["enum.Slots.BELT"]
|
||||
- map: ["enum.Slots.BACKPACK"]
|
||||
- map: ["enum.HumanoidVisualLayers.FacialHair"]
|
||||
state: shaved
|
||||
sprite: Mob/human_facial_hair.rsi
|
||||
- map: ["enum.HumanoidVisualLayers.Hair"]
|
||||
state: bald
|
||||
sprite: Mob/human_hair.rsi
|
||||
- map: ["enum.Slots.MASK"]
|
||||
- map: ["enum.Slots.HEAD"]
|
||||
- map: ["hand-left"]
|
||||
- map: ["hand-right"]
|
||||
|
||||
- type: Icon
|
||||
sprite: Mob/human.rsi
|
||||
state: male
|
||||
|
||||
- type: Physics
|
||||
mass: 85
|
||||
|
||||
- type: Collidable
|
||||
shapes:
|
||||
- !type:PhysShapeAabb
|
||||
bounds: "-0.35,-0.35,0.35,0.35"
|
||||
mask: 30
|
||||
layer: 4
|
||||
|
||||
- type: Species
|
||||
Template: Human
|
||||
HeatResistance: 323
|
||||
- type: Damageable
|
||||
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: SpeciesVisualizer2D
|
||||
|
||||
- type: HumanoidAppearance
|
||||
|
||||
BIN
Resources/Textures/Mob/human.rsi/fat_husk.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
Resources/Textures/Mob/human.rsi/female.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Resources/Textures/Mob/human.rsi/female_fat.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/Textures/Mob/human.rsi/female_slim.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
Resources/Textures/Mob/human.rsi/husk.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/Textures/Mob/human.rsi/male_fat.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
Resources/Textures/Mob/human.rsi/male_slim.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
@@ -1,29 +1,153 @@
|
||||
{
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
"version": 1,
|
||||
"size": {
|
||||
"x": 32,
|
||||
"y": 32
|
||||
},
|
||||
"states": [
|
||||
{
|
||||
"name": "fat_husk",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
"license": "CC-BY-SA-3.0",
|
||||
"copyright": "Taken from https://github.com/discordia-space/CEV-Eris at commit 9a3a3a180344460263e8df7ea2565128e07b86b5",
|
||||
"states": [
|
||||
{
|
||||
"name": "male",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
{
|
||||
"name": "female",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "female_fat",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "female_slim",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "husk",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "male",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "male_fat",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "male_slim",
|
||||
"directions": 4,
|
||||
"delays": [
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
],
|
||||
[
|
||||
1.0
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||