LobbyUI fixes (#27033)

* LobbyUI fixes

I have no idea which were bugs prior but anyway fix stuff.

* More fixes

* Test moment
This commit is contained in:
metalgearsloth
2024-04-17 12:54:54 +10:00
committed by GitHub
parent 30f73cfb6c
commit fcd6c25242
7 changed files with 181 additions and 50 deletions

View File

@@ -64,13 +64,19 @@ namespace Content.Client.Lobby
_characterSetup.CloseButton.OnPressed += _ => _characterSetup.CloseButton.OnPressed += _ =>
{ {
// Reset sliders etc.
_characterSetup?.UpdateControls();
var controller = _userInterfaceManager.GetUIController<LobbyUIController>();
controller.SetClothes(true);
controller.UpdateProfile();
_lobby.SwitchState(LobbyGui.LobbyGuiState.Default); _lobby.SwitchState(LobbyGui.LobbyGuiState.Default);
}; };
_characterSetup.SaveButton.OnPressed += _ => _characterSetup.SaveButton.OnPressed += _ =>
{ {
_characterSetup.Save(); _characterSetup.Save();
_userInterfaceManager.GetUIController<LobbyUIController>().UpdateCharacterUI(); _userInterfaceManager.GetUIController<LobbyUIController>().ReloadProfile();
}; };
LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide); LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide);

View File

@@ -3,6 +3,7 @@ using Content.Client.Humanoid;
using Content.Client.Inventory; using Content.Client.Inventory;
using Content.Client.Lobby.UI; using Content.Client.Lobby.UI;
using Content.Client.Preferences; using Content.Client.Preferences;
using Content.Client.Preferences.UI;
using Content.Client.Station; using Content.Client.Station;
using Content.Shared.Clothing; using Content.Shared.Clothing;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
@@ -30,6 +31,8 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
private LobbyCharacterPreviewPanel? _previewPanel; private LobbyCharacterPreviewPanel? _previewPanel;
private bool _showClothes = true;
/* /*
* Each character profile has its own dummy. There is also a dummy for the lobby screen + character editor * Each character profile has its own dummy. There is also a dummy for the lobby screen + character editor
* that is shared too. * that is shared too.
@@ -41,13 +44,15 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
private EntityUid? _previewDummy; private EntityUid? _previewDummy;
/// <summary> /// <summary>
/// If we currently have a loadout selected. /// If we currently have a job prototype selected.
/// </summary> /// </summary>
private JobPrototype? _dummyJob; private JobPrototype? _dummyJob;
// TODO: Load the species directly and don't update entity ever. // TODO: Load the species directly and don't update entity ever.
public event Action<EntityUid>? PreviewDummyUpdated; public event Action<EntityUid>? PreviewDummyUpdated;
private HumanoidCharacterProfile? _profile;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -56,7 +61,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
private void PreferencesDataLoaded() private void PreferencesDataLoaded()
{ {
UpdateCharacterUI(); UpdateProfile();
} }
public void OnStateEntered(LobbyState state) public void OnStateEntered(LobbyState state)
@@ -72,44 +77,102 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
public void SetPreviewPanel(LobbyCharacterPreviewPanel? panel) public void SetPreviewPanel(LobbyCharacterPreviewPanel? panel)
{ {
_previewPanel = panel; _previewPanel = panel;
UpdateCharacterUI(); ReloadProfile();
}
public void SetClothes(bool value)
{
if (_showClothes == value)
return;
_showClothes = value;
ReloadCharacterUI();
} }
public void SetDummyJob(JobPrototype? job) public void SetDummyJob(JobPrototype? job)
{ {
_dummyJob = job; _dummyJob = job;
UpdateCharacterUI(); ReloadCharacterUI();
} }
public void UpdateCharacterUI() /// <summary>
/// Updates the character only with the specified profile change.
/// </summary>
public void ReloadProfile()
{ {
// Test moment // Test moment
if (_stateManager.CurrentState is not LobbyState) if (_profile == null || _stateManager.CurrentState is not LobbyState)
return; return;
// Ignore job clothes and the likes so we don't spam entities out every frame of color changes.
var previewDummy = EnsurePreviewDummy(_profile);
_humanoid.LoadProfile(previewDummy, _profile);
}
/// <summary>
/// Updates the currently selected character's preview.
/// </summary>
public void ReloadCharacterUI()
{
// Test moment
if (_profile == null || _stateManager.CurrentState is not LobbyState)
return;
EntityManager.DeleteEntity(_previewDummy);
_previewDummy = null;
_previewDummy = EnsurePreviewDummy(_profile);
_previewPanel?.SetSprite(_previewDummy.Value);
_previewPanel?.SetSummaryText(_profile.Summary);
_humanoid.LoadProfile(_previewDummy.Value, _profile);
if (_showClothes)
GiveDummyJobClothesLoadout(_previewDummy.Value, _profile);
}
/// <summary>
/// Updates character profile to the default.
/// </summary>
public void UpdateProfile()
{
if (!_preferencesManager.ServerDataLoaded) if (!_preferencesManager.ServerDataLoaded)
{ {
_previewPanel?.SetLoaded(false); _profile = null;
return; return;
} }
_previewPanel?.SetLoaded(true); if (_preferencesManager.Preferences?.SelectedCharacter is HumanoidCharacterProfile selectedCharacter)
if (_preferencesManager.Preferences?.SelectedCharacter is not HumanoidCharacterProfile selectedCharacter)
{ {
_previewPanel?.SetSummaryText(string.Empty); _profile = selectedCharacter;
_previewPanel?.SetLoaded(true);
} }
else else
{ {
EntityManager.DeleteEntity(_previewDummy); _previewPanel?.SetSummaryText(string.Empty);
_previewDummy = EntityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(selectedCharacter.Species).DollPrototype, MapCoordinates.Nullspace); _previewPanel?.SetLoaded(false);
_previewPanel?.SetSprite(_previewDummy.Value);
_previewPanel?.SetSummaryText(selectedCharacter.Summary);
_humanoid.LoadProfile(_previewDummy.Value, selectedCharacter);
GiveDummyJobClothesLoadout(_previewDummy.Value, selectedCharacter);
PreviewDummyUpdated?.Invoke(_previewDummy.Value);
} }
ReloadCharacterUI();
}
public void UpdateProfile(HumanoidCharacterProfile? profile)
{
if (_profile?.Equals(profile) == true)
return;
if (_stateManager.CurrentState is not LobbyState)
return;
_profile = profile;
}
private EntityUid EnsurePreviewDummy(HumanoidCharacterProfile profile)
{
if (_previewDummy != null)
return _previewDummy.Value;
_previewDummy = EntityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(profile.Species).DollPrototype, MapCoordinates.Nullspace);
PreviewDummyUpdated?.Invoke(_previewDummy.Value);
return _previewDummy.Value;
} }
/// <summary> /// <summary>

View File

@@ -97,9 +97,14 @@ namespace Content.Client.Preferences.UI
UpdateUI(); UpdateUI();
} }
public void UpdateControls()
{
// Reset sliders etc. upon going going back to GUI.
_humanoidProfileEditor.LoadServerData();
}
private void UpdateUI() private void UpdateUI()
{ {
UserInterfaceManager.GetUIController<LobbyUIController>().UpdateCharacterUI();
var numberOfFullSlots = 0; var numberOfFullSlots = 0;
var characterButtonsGroup = new ButtonGroup(); var characterButtonsGroup = new ButtonGroup();
Characters.RemoveAllChildren(); Characters.RemoveAllChildren();

View File

@@ -190,7 +190,7 @@ namespace Content.Client.Preferences.UI
return; return;
Profile = Profile.WithCharacterAppearance( Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithHairStyleName(newStyle.id)); Profile.Appearance.WithHairStyleName(newStyle.id));
IsDirty = true; SetDirty();
}; };
_hairPicker.OnColorChanged += newColor => _hairPicker.OnColorChanged += newColor =>
@@ -200,7 +200,7 @@ namespace Content.Client.Preferences.UI
Profile = Profile.WithCharacterAppearance( Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithHairColor(newColor.marking.MarkingColors[0])); Profile.Appearance.WithHairColor(newColor.marking.MarkingColors[0]));
UpdateCMarkingsHair(); UpdateCMarkingsHair();
IsDirty = true; SetDirty();
}; };
_facialHairPicker.OnMarkingSelect += newStyle => _facialHairPicker.OnMarkingSelect += newStyle =>
@@ -209,7 +209,7 @@ namespace Content.Client.Preferences.UI
return; return;
Profile = Profile.WithCharacterAppearance( Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithFacialHairStyleName(newStyle.id)); Profile.Appearance.WithFacialHairStyleName(newStyle.id));
IsDirty = true; SetDirty();
}; };
_facialHairPicker.OnColorChanged += newColor => _facialHairPicker.OnColorChanged += newColor =>
@@ -219,7 +219,7 @@ namespace Content.Client.Preferences.UI
Profile = Profile.WithCharacterAppearance( Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithFacialHairColor(newColor.marking.MarkingColors[0])); Profile.Appearance.WithFacialHairColor(newColor.marking.MarkingColors[0]));
UpdateCMarkingsFacialHair(); UpdateCMarkingsFacialHair();
IsDirty = true; SetDirty();
}; };
_hairPicker.OnSlotRemove += _ => _hairPicker.OnSlotRemove += _ =>
@@ -231,7 +231,7 @@ namespace Content.Client.Preferences.UI
); );
UpdateHairPickers(); UpdateHairPickers();
UpdateCMarkingsHair(); UpdateCMarkingsHair();
IsDirty = true; SetDirty();
}; };
_facialHairPicker.OnSlotRemove += _ => _facialHairPicker.OnSlotRemove += _ =>
@@ -243,7 +243,7 @@ namespace Content.Client.Preferences.UI
); );
UpdateHairPickers(); UpdateHairPickers();
UpdateCMarkingsFacialHair(); UpdateCMarkingsFacialHair();
IsDirty = true; SetDirty();
}; };
_hairPicker.OnSlotAdd += delegate() _hairPicker.OnSlotAdd += delegate()
@@ -263,7 +263,7 @@ namespace Content.Client.Preferences.UI
UpdateHairPickers(); UpdateHairPickers();
UpdateCMarkingsHair(); UpdateCMarkingsHair();
IsDirty = true; SetDirty();
}; };
_facialHairPicker.OnSlotAdd += delegate() _facialHairPicker.OnSlotAdd += delegate()
@@ -283,7 +283,7 @@ namespace Content.Client.Preferences.UI
UpdateHairPickers(); UpdateHairPickers();
UpdateCMarkingsFacialHair(); UpdateCMarkingsFacialHair();
IsDirty = true; SetDirty();
}; };
#endregion Hair #endregion Hair
@@ -312,7 +312,7 @@ namespace Content.Client.Preferences.UI
Profile = Profile.WithCharacterAppearance( Profile = Profile.WithCharacterAppearance(
Profile.Appearance.WithEyeColor(newColor)); Profile.Appearance.WithEyeColor(newColor));
CMarkings.CurrentEyeColor = Profile.Appearance.EyeColor; CMarkings.CurrentEyeColor = Profile.Appearance.EyeColor;
IsDirty = true; SetDirty();
}; };
#endregion Eyes #endregion Eyes
@@ -336,7 +336,7 @@ namespace Content.Client.Preferences.UI
_preferenceUnavailableButton.SelectId(args.Id); _preferenceUnavailableButton.SelectId(args.Id);
Profile = Profile?.WithPreferenceUnavailable((PreferenceUnavailableMode) args.Id); Profile = Profile?.WithPreferenceUnavailable((PreferenceUnavailableMode) args.Id);
IsDirty = true; SetDirty();
}; };
_jobPriorities = new List<JobPrioritySelector>(); _jobPriorities = new List<JobPrioritySelector>();
@@ -369,7 +369,7 @@ namespace Content.Client.Preferences.UI
selector.PreferenceChanged += preference => selector.PreferenceChanged += preference =>
{ {
Profile = Profile?.WithTraitPreference(trait.ID, preference); Profile = Profile?.WithTraitPreference(trait.ID, preference);
IsDirty = true; SetDirty();
}; };
} }
} }
@@ -436,6 +436,13 @@ namespace Content.Client.Preferences.UI
LoadServerData(); LoadServerData();
} }
ShowClothes.OnToggled += args =>
{
var lobby = UserInterfaceManager.GetUIController<LobbyUIController>();
lobby.SetClothes(args.Pressed);
SetDirty();
};
preferencesManager.OnServerDataLoaded += LoadServerData; preferencesManager.OnServerDataLoaded += LoadServerData;
SpeciesInfoButton.OnPressed += OnSpeciesInfoButtonPressed; SpeciesInfoButton.OnPressed += OnSpeciesInfoButtonPressed;
@@ -443,6 +450,15 @@ namespace Content.Client.Preferences.UI
UpdateSpeciesGuidebookIcon(); UpdateSpeciesGuidebookIcon();
IsDirty = false; IsDirty = false;
controller.UpdateProfile();
}
private void SetDirty()
{
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
controller.UpdateProfile(Profile);
controller.ReloadCharacterUI();
IsDirty = true;
} }
private void OnSpeciesInfoButtonPressed(BaseButton.ButtonEventArgs args) private void OnSpeciesInfoButtonPressed(BaseButton.ButtonEventArgs args)
@@ -487,13 +503,13 @@ namespace Content.Client.Preferences.UI
if (selector.Disabled) if (selector.Disabled)
{ {
Profile = Profile?.WithAntagPreference(antag.ID, false); Profile = Profile?.WithAntagPreference(antag.ID, false);
IsDirty = true; SetDirty();
} }
selector.PreferenceChanged += preference => selector.PreferenceChanged += preference =>
{ {
Profile = Profile?.WithAntagPreference(antag.ID, preference); Profile = Profile?.WithAntagPreference(antag.ID, preference);
IsDirty = true; SetDirty();
}; };
} }
@@ -562,7 +578,10 @@ namespace Content.Client.Preferences.UI
foreach (var job in jobs) foreach (var job in jobs)
{ {
RoleLoadout? loadout = null; RoleLoadout? loadout = null;
// Clone so we don't modify the underlying loadout.
Profile?.Loadouts.TryGetValue(LoadoutSystem.GetJobPrototype(job.ID), out loadout); Profile?.Loadouts.TryGetValue(LoadoutSystem.GetJobPrototype(job.ID), out loadout);
loadout = loadout?.Clone();
var selector = new JobPrioritySelector(loadout, job, jobLoadoutGroup, _prototypeManager) var selector = new JobPrioritySelector(loadout, job, jobLoadoutGroup, _prototypeManager)
{ {
Margin = new Thickness(3f, 3f, 3f, 0f), Margin = new Thickness(3f, 3f, 3f, 0f),
@@ -578,15 +597,13 @@ namespace Content.Client.Preferences.UI
selector.LoadoutUpdated += args => selector.LoadoutUpdated += args =>
{ {
Profile?.SetLoadout(args); Profile = Profile?.WithLoadout(args);
UserInterfaceManager.GetUIController<LobbyUIController>().UpdateCharacterUI(); SetDirty();
IsDirty = true;
}; };
selector.PriorityChanged += priority => selector.PriorityChanged += priority =>
{ {
Profile = Profile?.WithJobPriority(job.ID, priority); Profile = Profile?.WithJobPriority(job.ID, priority);
IsDirty = true;
foreach (var jobSelector in _jobPriorities) foreach (var jobSelector in _jobPriorities)
{ {
@@ -602,6 +619,8 @@ namespace Content.Client.Preferences.UI
Profile = Profile?.WithJobPriority(jobSelector.Proto.ID, JobPriority.Medium); Profile = Profile?.WithJobPriority(jobSelector.Proto.ID, JobPriority.Medium);
} }
} }
SetDirty();
}; };
} }
@@ -619,7 +638,7 @@ namespace Content.Client.Preferences.UI
return; return;
Profile = Profile.WithFlavorText(content); Profile = Profile.WithFlavorText(content);
IsDirty = true; SetDirty();
} }
private void OnMarkingChange(MarkingSet markings) private void OnMarkingChange(MarkingSet markings)
@@ -628,8 +647,10 @@ namespace Content.Client.Preferences.UI
return; return;
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithMarkings(markings.GetForwardEnumerator().ToList())); Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithMarkings(markings.GetForwardEnumerator().ToList()));
UpdatePreview();
IsDirty = true; IsDirty = true;
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
controller.UpdateProfile(Profile);
controller.ReloadProfile();
} }
private void OnSkinColorOnValueChanged() private void OnSkinColorOnValueChanged()
@@ -683,6 +704,9 @@ namespace Content.Client.Preferences.UI
} }
IsDirty = true; IsDirty = true;
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
controller.UpdateProfile(Profile);
controller.ReloadProfile();
} }
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
@@ -698,7 +722,7 @@ namespace Content.Client.Preferences.UI
_preferencesManager.OnServerDataLoaded -= LoadServerData; _preferencesManager.OnServerDataLoaded -= LoadServerData;
} }
private void LoadServerData() public void LoadServerData()
{ {
Profile = (HumanoidCharacterProfile) _preferencesManager.Preferences!.SelectedCharacter; Profile = (HumanoidCharacterProfile) _preferencesManager.Preferences!.SelectedCharacter;
CharacterSlot = _preferencesManager.Preferences.SelectedCharacterIndex; CharacterSlot = _preferencesManager.Preferences.SelectedCharacterIndex;
@@ -706,12 +730,13 @@ namespace Content.Client.Preferences.UI
UpdateAntagRequirements(); UpdateAntagRequirements();
UpdateRoleRequirements(); UpdateRoleRequirements();
UpdateControls(); UpdateControls();
ShowClothes.Pressed = true;
} }
private void SetAge(int newAge) private void SetAge(int newAge)
{ {
Profile = Profile?.WithAge(newAge); Profile = Profile?.WithAge(newAge);
IsDirty = true; SetDirty();
} }
private void SetSex(Sex newSex) private void SetSex(Sex newSex)
@@ -732,13 +757,13 @@ namespace Content.Client.Preferences.UI
} }
UpdateGenderControls(); UpdateGenderControls();
CMarkings.SetSex(newSex); CMarkings.SetSex(newSex);
IsDirty = true; SetDirty();
} }
private void SetGender(Gender newGender) private void SetGender(Gender newGender)
{ {
Profile = Profile?.WithGender(newGender); Profile = Profile?.WithGender(newGender);
IsDirty = true; SetDirty();
} }
private void SetSpecies(string newSpecies) private void SetSpecies(string newSpecies)
@@ -748,20 +773,20 @@ namespace Content.Client.Preferences.UI
CMarkings.SetSpecies(newSpecies); // Repopulate the markings tab as well. CMarkings.SetSpecies(newSpecies); // Repopulate the markings tab as well.
UpdateSexControls(); // update sex for new species UpdateSexControls(); // update sex for new species
UpdateSpeciesGuidebookIcon(); UpdateSpeciesGuidebookIcon();
IsDirty = true; SetDirty();
UpdatePreview(); UpdatePreview();
} }
private void SetName(string newName) private void SetName(string newName)
{ {
Profile = Profile?.WithName(newName); Profile = Profile?.WithName(newName);
IsDirty = true; SetDirty();
} }
private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority) private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority)
{ {
Profile = Profile?.WithSpawnPriorityPreference(newSpawnPriority); Profile = Profile?.WithSpawnPriorityPreference(newSpawnPriority);
IsDirty = true; SetDirty();
} }
public void Save() public void Save()
@@ -773,6 +798,8 @@ namespace Content.Client.Preferences.UI
_preferencesManager.UpdateCharacter(Profile, CharacterSlot); _preferencesManager.UpdateCharacter(Profile, CharacterSlot);
OnProfileChanged?.Invoke(Profile, CharacterSlot); OnProfileChanged?.Invoke(Profile, CharacterSlot);
// Reset profile to default.
UserInterfaceManager.GetUIController<LobbyUIController>().UpdateProfile();
} }
private bool IsDirty private bool IsDirty
@@ -1065,7 +1092,7 @@ namespace Content.Client.Preferences.UI
if (Profile is null) if (Profile is null)
return; return;
UserInterfaceManager.GetUIController<LobbyUIController>().UpdateCharacterUI(); UserInterfaceManager.GetUIController<LobbyUIController>().ReloadProfile();
SetPreviewRotation(_previewRotation); SetPreviewRotation(_previewRotation);
} }

View File

@@ -153,7 +153,7 @@ public abstract class RequirementsSelector<T> : BoxContainer where T : IPrototyp
_loadout.EnsureValid(session, collection); _loadout.EnsureValid(session, collection);
_loadoutWindow.RefreshLoadouts(_loadout, session, collection); _loadoutWindow.RefreshLoadouts(_loadout, session, collection);
var controller = UserInterfaceManager.GetUIController<LobbyUIController>(); var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
controller.UpdateCharacterUI(); controller.ReloadProfile();
LoadoutUpdated?.Invoke(_loadout); LoadoutUpdated?.Invoke(_loadout);
}; };
@@ -165,7 +165,7 @@ public abstract class RequirementsSelector<T> : BoxContainer where T : IPrototyp
_loadout.EnsureValid(session, collection); _loadout.EnsureValid(session, collection);
_loadoutWindow.RefreshLoadouts(_loadout, session, collection); _loadoutWindow.RefreshLoadouts(_loadout, session, collection);
var controller = UserInterfaceManager.GetUIController<LobbyUIController>(); var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
controller.UpdateCharacterUI(); controller.ReloadProfile();
LoadoutUpdated?.Invoke(_loadout); LoadoutUpdated?.Invoke(_loadout);
}; };

View File

@@ -315,6 +315,7 @@ namespace Content.Shared.Preferences
list.Remove(antagId); list.Remove(antagId);
} }
} }
return new(this, _jobPriorities, list, _traitPreferences, _loadouts); return new(this, _jobPriorities, list, _traitPreferences, _loadouts);
} }
@@ -565,6 +566,23 @@ namespace Content.Shared.Preferences
_loadouts[loadout.Role.Id] = loadout; _loadouts[loadout.Role.Id] = loadout;
} }
public HumanoidCharacterProfile WithLoadout(RoleLoadout loadout)
{
// Deep copies so we don't modify the DB profile.
var copied = new Dictionary<string, RoleLoadout>();
foreach (var proto in _loadouts)
{
if (proto.Key == loadout.Role)
continue;
copied[proto.Key] = proto.Value.Clone();
}
copied[loadout.Role] = loadout.Clone();
return new(this, _jobPriorities, _antagPreferences, _traitPreferences, copied);
}
public RoleLoadout GetLoadoutOrDefault(string id, IEntityManager entManager, IPrototypeManager protoManager) public RoleLoadout GetLoadoutOrDefault(string id, IEntityManager entManager, IPrototypeManager protoManager)
{ {
if (!_loadouts.TryGetValue(id, out var loadout)) if (!_loadouts.TryGetValue(id, out var loadout))

View File

@@ -29,6 +29,18 @@ public sealed class RoleLoadout
Role = role; Role = role;
} }
public RoleLoadout Clone()
{
var weh = new RoleLoadout(Role);
foreach (var selected in SelectedLoadouts)
{
weh.SelectedLoadouts.Add(selected.Key, new List<Loadout>(selected.Value));
}
return weh;
}
/// <summary> /// <summary>
/// Ensures all prototypes exist and effects can be applied. /// Ensures all prototypes exist and effects can be applied.
/// </summary> /// </summary>