Loadouts redux (#25715)
* Loadouts redux * Loadout window mockup * More workout * rent * validation * Developments * bcs * More cleanup * Rebuild working * Fix model and loading * obsession * efcore * We got a stew goin * Cleanup * Optional + SeniorEngineering fix * Fixes * Update science.yml * add add * Automatic naming * Update nukeops * Coming together * Right now * stargate * rejig the UI * weh * Loadouts tweaks * Merge conflicts + ordering fix * yerba mate * chocolat * More updates * Add multi-selection support * test h * fikss * a * add tech assistant and hazard suit * huh * Latest changes * add medical loadouts * and science * finish security loadouts * cargo * service done * added wildcards * add command * Move restrictions * Finalising * Fix existing work * Localise next batch * clothing fix * Fix storage names * review * the scooping room * Test fixes * Xamlify * Xamlify this too * Update Resources/Prototypes/Loadouts/Jobs/Medical/paramedic.yml Co-authored-by: Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com> * Update Resources/Prototypes/Loadouts/loadout_groups.yml Co-authored-by: Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com> * Update Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml Co-authored-by: Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com> * Update Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml Co-authored-by: Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com> * Update Resources/Prototypes/Loadouts/loadout_groups.yml Co-authored-by: Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com> * Update Resources/Prototypes/Loadouts/Jobs/Security/detective.yml Co-authored-by: Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com> * Update Resources/Prototypes/Loadouts/loadout_groups.yml Co-authored-by: Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com> * ben * Margins --------- Co-authored-by: Firewatch <54725557+musicmanvr@users.noreply.github.com> Co-authored-by: Mr. 27 <koolthunder019@gmail.com> Co-authored-by: Mr. 27 <45323883+Dutch-VanDerLinde@users.noreply.github.com>
This commit is contained in:
@@ -58,7 +58,7 @@ public class SpawnEquipDeleteBenchmark
|
||||
for (var i = 0; i < N; i++)
|
||||
{
|
||||
_entity = server.EntMan.SpawnAttachedTo(Mob, _coords);
|
||||
_spawnSys.EquipStartingGear(_entity, _gear, null);
|
||||
_spawnSys.EquipStartingGear(_entity, _gear);
|
||||
server.EntMan.DeleteEntity(_entity);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ using Content.Shared.Module;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Replay;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
|
||||
|
||||
namespace Content.Client.IoC
|
||||
@@ -29,26 +30,29 @@ namespace Content.Client.IoC
|
||||
{
|
||||
public static void Register()
|
||||
{
|
||||
IoCManager.Register<IParallaxManager, ParallaxManager>();
|
||||
IoCManager.Register<IChatManager, ChatManager>();
|
||||
IoCManager.Register<IClientPreferencesManager, ClientPreferencesManager>();
|
||||
IoCManager.Register<IStylesheetManager, StylesheetManager>();
|
||||
IoCManager.Register<IScreenshotHook, ScreenshotHook>();
|
||||
IoCManager.Register<FullscreenHook, FullscreenHook>();
|
||||
IoCManager.Register<IClickMapManager, ClickMapManager>();
|
||||
IoCManager.Register<IClientAdminManager, ClientAdminManager>();
|
||||
IoCManager.Register<ISharedAdminManager, ClientAdminManager>();
|
||||
IoCManager.Register<EuiManager, EuiManager>();
|
||||
IoCManager.Register<IVoteManager, VoteManager>();
|
||||
IoCManager.Register<ChangelogManager, ChangelogManager>();
|
||||
IoCManager.Register<RulesManager, RulesManager>();
|
||||
IoCManager.Register<ViewportManager, ViewportManager>();
|
||||
IoCManager.Register<ISharedAdminLogManager, SharedAdminLogManager>();
|
||||
IoCManager.Register<GhostKickManager>();
|
||||
IoCManager.Register<ExtendedDisconnectInformationManager>();
|
||||
IoCManager.Register<JobRequirementsManager>();
|
||||
IoCManager.Register<DocumentParsingManager>();
|
||||
IoCManager.Register<ContentReplayPlaybackManager, ContentReplayPlaybackManager>();
|
||||
var collection = IoCManager.Instance!;
|
||||
|
||||
collection.Register<IParallaxManager, ParallaxManager>();
|
||||
collection.Register<IChatManager, ChatManager>();
|
||||
collection.Register<IClientPreferencesManager, ClientPreferencesManager>();
|
||||
collection.Register<IStylesheetManager, StylesheetManager>();
|
||||
collection.Register<IScreenshotHook, ScreenshotHook>();
|
||||
collection.Register<FullscreenHook, FullscreenHook>();
|
||||
collection.Register<IClickMapManager, ClickMapManager>();
|
||||
collection.Register<IClientAdminManager, ClientAdminManager>();
|
||||
collection.Register<ISharedAdminManager, ClientAdminManager>();
|
||||
collection.Register<EuiManager, EuiManager>();
|
||||
collection.Register<IVoteManager, VoteManager>();
|
||||
collection.Register<ChangelogManager, ChangelogManager>();
|
||||
collection.Register<RulesManager, RulesManager>();
|
||||
collection.Register<ViewportManager, ViewportManager>();
|
||||
collection.Register<ISharedAdminLogManager, SharedAdminLogManager>();
|
||||
collection.Register<GhostKickManager>();
|
||||
collection.Register<ExtendedDisconnectInformationManager>();
|
||||
collection.Register<JobRequirementsManager>();
|
||||
collection.Register<DocumentParsingManager>();
|
||||
collection.Register<ContentReplayPlaybackManager, ContentReplayPlaybackManager>();
|
||||
collection.Register<ISharedPlaytimeManager, JobRequirementsManager>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Content.Client.Lobby
|
||||
_characterSetup.SaveButton.OnPressed += _ =>
|
||||
{
|
||||
_characterSetup.Save();
|
||||
_lobby.CharacterPreview.UpdateUI();
|
||||
_userInterfaceManager.GetUIController<LobbyUIController>().UpdateCharacterUI();
|
||||
};
|
||||
|
||||
LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide);
|
||||
@@ -84,10 +84,6 @@ namespace Content.Client.Lobby
|
||||
_gameTicker.InfoBlobUpdated += UpdateLobbyUi;
|
||||
_gameTicker.LobbyStatusUpdated += LobbyStatusUpdated;
|
||||
_gameTicker.LobbyLateJoinStatusUpdated += LobbyLateJoinStatusUpdated;
|
||||
|
||||
_preferencesManager.OnServerDataLoaded += PreferencesDataLoaded;
|
||||
|
||||
_lobby.CharacterPreview.UpdateUI();
|
||||
}
|
||||
|
||||
protected override void Shutdown()
|
||||
@@ -109,13 +105,6 @@ namespace Content.Client.Lobby
|
||||
|
||||
_characterSetup?.Dispose();
|
||||
_characterSetup = null;
|
||||
|
||||
_preferencesManager.OnServerDataLoaded -= PreferencesDataLoaded;
|
||||
}
|
||||
|
||||
private void PreferencesDataLoaded()
|
||||
{
|
||||
_lobby?.CharacterPreview.UpdateUI();
|
||||
}
|
||||
|
||||
private void OnSetupPressed(BaseButton.ButtonEventArgs args)
|
||||
|
||||
223
Content.Client/Lobby/LobbyUIController.cs
Normal file
223
Content.Client/Lobby/LobbyUIController.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Inventory;
|
||||
using Content.Client.Lobby.UI;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.Station;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Lobby;
|
||||
|
||||
public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState>, IOnStateExited<LobbyState>
|
||||
{
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[UISystemDependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
|
||||
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
|
||||
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
|
||||
|
||||
private LobbyCharacterPreviewPanel? _previewPanel;
|
||||
|
||||
/*
|
||||
* Each character profile has its own dummy. There is also a dummy for the lobby screen + character editor
|
||||
* that is shared too.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Preview dummy for role gear.
|
||||
/// </summary>
|
||||
private EntityUid? _previewDummy;
|
||||
|
||||
/// <summary>
|
||||
/// If we currently have a loadout selected.
|
||||
/// </summary>
|
||||
private JobPrototype? _dummyJob;
|
||||
|
||||
// TODO: Load the species directly and don't update entity ever.
|
||||
public event Action<EntityUid>? PreviewDummyUpdated;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_preferencesManager.OnServerDataLoaded += PreferencesDataLoaded;
|
||||
}
|
||||
|
||||
private void PreferencesDataLoaded()
|
||||
{
|
||||
UpdateCharacterUI();
|
||||
}
|
||||
|
||||
public void OnStateEntered(LobbyState state)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnStateExited(LobbyState state)
|
||||
{
|
||||
EntityManager.DeleteEntity(_previewDummy);
|
||||
_previewDummy = null;
|
||||
}
|
||||
|
||||
public void SetPreviewPanel(LobbyCharacterPreviewPanel? panel)
|
||||
{
|
||||
_previewPanel = panel;
|
||||
UpdateCharacterUI();
|
||||
}
|
||||
|
||||
public void SetDummyJob(JobPrototype? job)
|
||||
{
|
||||
_dummyJob = job;
|
||||
UpdateCharacterUI();
|
||||
}
|
||||
|
||||
public void UpdateCharacterUI()
|
||||
{
|
||||
// Test moment
|
||||
if (_stateManager.CurrentState is not LobbyState)
|
||||
return;
|
||||
|
||||
if (!_preferencesManager.ServerDataLoaded)
|
||||
{
|
||||
_previewPanel?.SetLoaded(false);
|
||||
return;
|
||||
}
|
||||
|
||||
_previewPanel?.SetLoaded(true);
|
||||
|
||||
if (_preferencesManager.Preferences?.SelectedCharacter is not HumanoidCharacterProfile selectedCharacter)
|
||||
{
|
||||
_previewPanel?.SetSummaryText(string.Empty);
|
||||
}
|
||||
else
|
||||
{
|
||||
EntityManager.DeleteEntity(_previewDummy);
|
||||
_previewDummy = EntityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(selectedCharacter.Species).DollPrototype, MapCoordinates.Nullspace);
|
||||
_previewPanel?.SetSprite(_previewDummy.Value);
|
||||
_previewPanel?.SetSummaryText(selectedCharacter.Summary);
|
||||
_humanoid.LoadProfile(_previewDummy.Value, selectedCharacter);
|
||||
|
||||
GiveDummyJobClothesLoadout(_previewDummy.Value, selectedCharacter);
|
||||
PreviewDummyUpdated?.Invoke(_previewDummy.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the highest priority job's clothes to the dummy.
|
||||
/// </summary>
|
||||
public void GiveDummyJobClothesLoadout(EntityUid dummy, HumanoidCharacterProfile profile)
|
||||
{
|
||||
var job = _dummyJob ?? GetPreferredJob(profile);
|
||||
GiveDummyJobClothes(dummy, profile, job);
|
||||
|
||||
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
|
||||
{
|
||||
var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), EntityManager, _prototypeManager);
|
||||
GiveDummyLoadout(dummy, loadout);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the highest priority job for the profile.
|
||||
/// </summary>
|
||||
public JobPrototype GetPreferredJob(HumanoidCharacterProfile profile)
|
||||
{
|
||||
var highPriorityJob = profile.JobPriorities.FirstOrDefault(p => p.Value == JobPriority.High).Key;
|
||||
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?)
|
||||
return _prototypeManager.Index<JobPrototype>(highPriorityJob ?? SharedGameTicker.FallbackOverflowJob);
|
||||
}
|
||||
|
||||
public void GiveDummyLoadout(EntityUid uid, RoleLoadout? roleLoadout)
|
||||
{
|
||||
if (roleLoadout == null)
|
||||
return;
|
||||
|
||||
foreach (var group in roleLoadout.SelectedLoadouts.Values)
|
||||
{
|
||||
foreach (var loadout in group)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
|
||||
continue;
|
||||
|
||||
_spawn.EquipStartingGear(uid, _prototypeManager.Index(loadoutProto.Equipment));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the specified job's clothes to the dummy.
|
||||
/// </summary>
|
||||
public void GiveDummyJobClothes(EntityUid dummy, HumanoidCharacterProfile profile, JobPrototype job)
|
||||
{
|
||||
if (!_inventory.TryGetSlots(dummy, out var slots))
|
||||
return;
|
||||
|
||||
// Apply loadout
|
||||
if (profile.Loadouts.TryGetValue(job.ID, out var jobLoadout))
|
||||
{
|
||||
foreach (var loadouts in jobLoadout.SelectedLoadouts.Values)
|
||||
{
|
||||
foreach (var loadout in loadouts)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
|
||||
continue;
|
||||
|
||||
// TODO: Need some way to apply starting gear to an entity coz holy fucking shit dude.
|
||||
var loadoutGear = _prototypeManager.Index(loadoutProto.Equipment);
|
||||
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var itemType = loadoutGear.GetGear(slot.Name);
|
||||
|
||||
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
|
||||
{
|
||||
EntityManager.DeleteEntity(unequippedItem.Value);
|
||||
}
|
||||
|
||||
if (itemType != string.Empty)
|
||||
{
|
||||
var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
|
||||
_inventory.TryEquip(dummy, item, slot.Name, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (job.StartingGear == null)
|
||||
return;
|
||||
|
||||
var gear = _prototypeManager.Index<StartingGearPrototype>(job.StartingGear);
|
||||
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var itemType = gear.GetGear(slot.Name);
|
||||
|
||||
if (_inventory.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
|
||||
{
|
||||
EntityManager.DeleteEntity(unequippedItem.Value);
|
||||
}
|
||||
|
||||
if (itemType != string.Empty)
|
||||
{
|
||||
var item = EntityManager.SpawnEntity(itemType, MapCoordinates.Nullspace);
|
||||
_inventory.TryEquip(dummy, item, slot.Name, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EntityUid? GetPreviewDummy()
|
||||
{
|
||||
return _previewDummy;
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Alerts;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Inventory;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
public sealed class LobbyCharacterPreviewPanel : Control
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
|
||||
private EntityUid? _previewDummy;
|
||||
private readonly Label _summaryLabel;
|
||||
private readonly BoxContainer _loaded;
|
||||
private readonly BoxContainer _viewBox;
|
||||
private readonly Label _unloaded;
|
||||
|
||||
public LobbyCharacterPreviewPanel()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
var header = new NanoHeading
|
||||
{
|
||||
Text = Loc.GetString("lobby-character-preview-panel-header")
|
||||
};
|
||||
|
||||
CharacterSetupButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("lobby-character-preview-panel-character-setup-button"),
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Margin = new Thickness(0, 5, 0, 0),
|
||||
};
|
||||
|
||||
_summaryLabel = new Label
|
||||
{
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
Margin = new Thickness(3, 3),
|
||||
};
|
||||
|
||||
var vBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical
|
||||
};
|
||||
_unloaded = new Label { Text = Loc.GetString("lobby-character-preview-panel-unloaded-preferences-label") };
|
||||
|
||||
_loaded = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Vertical,
|
||||
Visible = false
|
||||
};
|
||||
_viewBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
};
|
||||
var _vSpacer = new VSpacer();
|
||||
|
||||
_loaded.AddChild(_summaryLabel);
|
||||
_loaded.AddChild(_viewBox);
|
||||
_loaded.AddChild(_vSpacer);
|
||||
_loaded.AddChild(CharacterSetupButton);
|
||||
|
||||
vBox.AddChild(header);
|
||||
vBox.AddChild(_loaded);
|
||||
vBox.AddChild(_unloaded);
|
||||
AddChild(vBox);
|
||||
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public Button CharacterSetupButton { get; }
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (!disposing) return;
|
||||
if (_previewDummy != null) _entityManager.DeleteEntity(_previewDummy.Value);
|
||||
_previewDummy = default;
|
||||
}
|
||||
|
||||
public void UpdateUI()
|
||||
{
|
||||
if (!_preferencesManager.ServerDataLoaded)
|
||||
{
|
||||
_loaded.Visible = false;
|
||||
_unloaded.Visible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_loaded.Visible = true;
|
||||
_unloaded.Visible = false;
|
||||
if (_preferencesManager.Preferences?.SelectedCharacter is not HumanoidCharacterProfile selectedCharacter)
|
||||
{
|
||||
_summaryLabel.Text = string.Empty;
|
||||
}
|
||||
else
|
||||
{
|
||||
_previewDummy = _entityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(selectedCharacter.Species).DollPrototype, MapCoordinates.Nullspace);
|
||||
_viewBox.DisposeAllChildren();
|
||||
var spriteView = new SpriteView
|
||||
{
|
||||
OverrideDirection = Direction.South,
|
||||
Scale = new Vector2(4f, 4f),
|
||||
MaxSize = new Vector2(112, 112),
|
||||
Stretch = SpriteView.StretchMode.Fill,
|
||||
};
|
||||
spriteView.SetEntity(_previewDummy.Value);
|
||||
_viewBox.AddChild(spriteView);
|
||||
_summaryLabel.Text = selectedCharacter.Summary;
|
||||
_entityManager.System<HumanoidAppearanceSystem>().LoadProfile(_previewDummy.Value, selectedCharacter);
|
||||
GiveDummyJobClothes(_previewDummy.Value, selectedCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void GiveDummyJobClothes(EntityUid dummy, HumanoidCharacterProfile profile)
|
||||
{
|
||||
var protoMan = IoCManager.Resolve<IPrototypeManager>();
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
var invSystem = EntitySystem.Get<ClientInventorySystem>();
|
||||
|
||||
var highPriorityJob = profile.JobPriorities.FirstOrDefault(p => p.Value == JobPriority.High).Key;
|
||||
|
||||
// ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract (what is resharper smoking?)
|
||||
var job = protoMan.Index<JobPrototype>(highPriorityJob ?? SharedGameTicker.FallbackOverflowJob);
|
||||
|
||||
if (job.StartingGear != null && invSystem.TryGetSlots(dummy, out var slots))
|
||||
{
|
||||
var gear = protoMan.Index<StartingGearPrototype>(job.StartingGear);
|
||||
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
var itemType = gear.GetGear(slot.Name, profile);
|
||||
|
||||
if (invSystem.TryUnequip(dummy, slot.Name, out var unequippedItem, silent: true, force: true, reparent: false))
|
||||
{
|
||||
entMan.DeleteEntity(unequippedItem.Value);
|
||||
}
|
||||
|
||||
if (itemType != string.Empty)
|
||||
{
|
||||
var item = entMan.SpawnEntity(itemType, MapCoordinates.Nullspace);
|
||||
invSystem.TryEquip(dummy, item, slot.Name, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml
Normal file
22
Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml
Normal file
@@ -0,0 +1,22 @@
|
||||
<Control
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls">
|
||||
<BoxContainer Name="VBox" Orientation="Vertical">
|
||||
<controls:NanoHeading Name="Header" Text="{Loc 'lobby-character-preview-panel-header'}">
|
||||
|
||||
</controls:NanoHeading>
|
||||
<BoxContainer Name="Loaded" Orientation="Vertical"
|
||||
Visible="False">
|
||||
<Label Name="Summary" HorizontalAlignment="Center" Margin="3 3"/>
|
||||
<BoxContainer Name="ViewBox" Orientation="Horizontal" HorizontalAlignment="Center">
|
||||
|
||||
</BoxContainer>
|
||||
<controls:VSpacer/>
|
||||
<Button Name="CharacterSetup" Text="{Loc 'lobby-character-preview-panel-character-setup-button'}"
|
||||
HorizontalAlignment="Center"
|
||||
Margin="0 5 0 0"/>
|
||||
</BoxContainer>
|
||||
<Label Name="Unloaded" Text="{Loc 'lobby-character-preview-panel-unloaded-preferences-label'}"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
45
Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml.cs
Normal file
45
Content.Client/Lobby/UI/LobbyCharacterPreviewPanel.xaml.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Lobby.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LobbyCharacterPreviewPanel : Control
|
||||
{
|
||||
public Button CharacterSetupButton => CharacterSetup;
|
||||
|
||||
public LobbyCharacterPreviewPanel()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
UserInterfaceManager.GetUIController<LobbyUIController>().SetPreviewPanel(this);
|
||||
}
|
||||
|
||||
public void SetLoaded(bool value)
|
||||
{
|
||||
Loaded.Visible = value;
|
||||
Unloaded.Visible = !value;
|
||||
}
|
||||
|
||||
public void SetSummaryText(string value)
|
||||
{
|
||||
Summary.Text = string.Empty;
|
||||
}
|
||||
|
||||
public void SetSprite(EntityUid uid)
|
||||
{
|
||||
ViewBox.DisposeAllChildren();
|
||||
var spriteView = new SpriteView
|
||||
{
|
||||
OverrideDirection = Direction.South,
|
||||
Scale = new Vector2(4f, 4f),
|
||||
MaxSize = new Vector2(112, 112),
|
||||
Stretch = SpriteView.StretchMode.Fill,
|
||||
};
|
||||
spriteView.SetEntity(uid);
|
||||
ViewBox.AddChild(spriteView);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,9 @@
|
||||
using Content.Client.Chat.UI;
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.Preferences.UI;
|
||||
using Content.Client.UserInterface.Screens;
|
||||
using Content.Client.UserInterface.Systems.Chat.Widgets;
|
||||
using Content.Client.UserInterface.Systems.EscapeMenu;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
|
||||
@@ -7,12 +7,13 @@ using Robust.Client;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Players.PlayTimeTracking;
|
||||
|
||||
public sealed class JobRequirementsManager
|
||||
public sealed class JobRequirementsManager : ISharedPlaytimeManager
|
||||
{
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IClientNetManager _net = default!;
|
||||
@@ -133,5 +134,13 @@ public sealed class JobRequirementsManager
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, TimeSpan> GetPlayTimes(ICommonSession session)
|
||||
{
|
||||
if (session != _playerManager.LocalSession)
|
||||
{
|
||||
return new Dictionary<string, TimeSpan>();
|
||||
}
|
||||
|
||||
return _roles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -20,8 +18,7 @@ namespace Content.Client.Preferences
|
||||
{
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
[Dependency] private readonly IBaseClient _baseClient = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypes = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
|
||||
public event Action? OnServerDataLoaded;
|
||||
|
||||
@@ -64,7 +61,8 @@ namespace Content.Client.Preferences
|
||||
|
||||
public void UpdateCharacter(ICharacterProfile profile, int slot)
|
||||
{
|
||||
profile.EnsureValid(_cfg, _prototypes);
|
||||
var collection = IoCManager.Instance!;
|
||||
profile.EnsureValid(_playerManager.LocalSession!, collection);
|
||||
var characters = new Dictionary<int, ICharacterProfile>(Preferences.Characters) {[slot] = profile};
|
||||
Preferences = new PlayerPreferences(characters, Preferences.SelectedCharacterIndex, Preferences.AdminOOCColor);
|
||||
var msg = new MsgUpdateCharacter
|
||||
|
||||
41
Content.Client/Preferences/UI/AntagPreferenceSelector.cs
Normal file
41
Content.Client/Preferences/UI/AntagPreferenceSelector.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
|
||||
public sealed class AntagPreferenceSelector : RequirementsSelector<AntagPrototype>
|
||||
{
|
||||
// 0 is yes and 1 is no
|
||||
public bool Preference
|
||||
{
|
||||
get => Options.SelectedValue == 0;
|
||||
set => Options.Select((value && !Disabled) ? 0 : 1);
|
||||
}
|
||||
|
||||
public event Action<bool>? PreferenceChanged;
|
||||
|
||||
public AntagPreferenceSelector(AntagPrototype proto, ButtonGroup btnGroup)
|
||||
: base(proto, btnGroup)
|
||||
{
|
||||
Options.OnItemSelected += args => PreferenceChanged?.Invoke(Preference);
|
||||
|
||||
var items = new[]
|
||||
{
|
||||
("humanoid-profile-editor-antag-preference-yes-button", 0),
|
||||
("humanoid-profile-editor-antag-preference-no-button", 1)
|
||||
};
|
||||
var title = Loc.GetString(proto.Name);
|
||||
var description = Loc.GetString(proto.Objective);
|
||||
// Not supported yet get fucked.
|
||||
Setup(null, items, title, 250, description);
|
||||
|
||||
// immediately lock requirements if they arent met.
|
||||
// another function checks Disabled after creating the selector so this has to be done now
|
||||
var requirements = IoCManager.Resolve<JobRequirementsManager>();
|
||||
if (proto.Requirements != null && !requirements.CheckRoleTime(proto.Requirements, out var reason))
|
||||
{
|
||||
LockRequirements(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@
|
||||
<gfx:StyleBoxFlat BackgroundColor="{x:Static style:StyleNano.NanoGold}" ContentMarginTopOverride="2" />
|
||||
</PanelContainer.PanelOverride>
|
||||
</PanelContainer>
|
||||
<BoxContainer Name="CharEditor" />
|
||||
<BoxContainer Name="CharEditor" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
|
||||
@@ -3,27 +3,23 @@ using System.Numerics;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Info.PlaytimeStats;
|
||||
using Content.Client.Lobby.UI;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
@@ -36,7 +32,6 @@ namespace Content.Client.Preferences.UI
|
||||
private readonly IClientPreferencesManager _preferencesManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
private readonly IConfigurationManager _configurationManager;
|
||||
private readonly Button _createNewCharacterButton;
|
||||
private readonly HumanoidProfileEditor _humanoidProfileEditor;
|
||||
|
||||
@@ -51,7 +46,6 @@ namespace Content.Client.Preferences.UI
|
||||
_entityManager = entityManager;
|
||||
_prototypeManager = prototypeManager;
|
||||
_preferencesManager = preferencesManager;
|
||||
_configurationManager = configurationManager;
|
||||
|
||||
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
@@ -74,7 +68,7 @@ namespace Content.Client.Preferences.UI
|
||||
args.Event.Handle();
|
||||
};
|
||||
|
||||
_humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager, entityManager, configurationManager);
|
||||
_humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager, configurationManager);
|
||||
_humanoidProfileEditor.OnProfileChanged += ProfileChanged;
|
||||
CharEditor.AddChild(_humanoidProfileEditor);
|
||||
|
||||
@@ -105,6 +99,7 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
private void UpdateUI()
|
||||
{
|
||||
UserInterfaceManager.GetUIController<LobbyUIController>().UpdateCharacterUI();
|
||||
var numberOfFullSlots = 0;
|
||||
var characterButtonsGroup = new ButtonGroup();
|
||||
Characters.RemoveAllChildren();
|
||||
@@ -120,11 +115,6 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
foreach (var (slot, character) in _preferencesManager.Preferences!.Characters)
|
||||
{
|
||||
if (character is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
numberOfFullSlots++;
|
||||
var characterPickerButton = new CharacterPickerButton(_entityManager,
|
||||
_preferencesManager,
|
||||
@@ -148,8 +138,12 @@ namespace Content.Client.Preferences.UI
|
||||
_createNewCharacterButton.Disabled =
|
||||
numberOfFullSlots >= _preferencesManager.Settings.MaxCharacterSlots;
|
||||
Characters.AddChild(_createNewCharacterButton);
|
||||
// TODO: Move this shit to the Lobby UI controller
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows individual characters on the side of the character GUI.
|
||||
/// </summary>
|
||||
private sealed class CharacterPickerButton : ContainerButton
|
||||
{
|
||||
private EntityUid _previewDummy;
|
||||
@@ -180,7 +174,15 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
if (humanoid != null)
|
||||
{
|
||||
LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy, humanoid);
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
var job = controller.GetPreferredJob(humanoid);
|
||||
controller.GiveDummyJobClothes(_previewDummy, humanoid, job);
|
||||
|
||||
if (prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
|
||||
{
|
||||
var loadout = humanoid.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), entityManager, prototypeManager);
|
||||
controller.GiveDummyLoadout(_previewDummy, loadout);
|
||||
}
|
||||
}
|
||||
|
||||
var isSelectedCharacter = profile == preferencesManager.Preferences?.SelectedCharacter;
|
||||
|
||||
11
Content.Client/Preferences/UI/HighlightedContainer.xaml
Normal file
11
Content.Client/Preferences/UI/HighlightedContainer.xaml
Normal file
@@ -0,0 +1,11 @@
|
||||
<PanelContainer
|
||||
xmlns="https://spacestation14.io"
|
||||
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#2F2F35"
|
||||
ContentMarginTopOverride="10"
|
||||
ContentMarginBottomOverride="10"
|
||||
ContentMarginLeftOverride="10"
|
||||
ContentMarginRightOverride="10"/>
|
||||
</PanelContainer.PanelOverride>
|
||||
</PanelContainer>
|
||||
14
Content.Client/Preferences/UI/HighlightedContainer.xaml.cs
Normal file
14
Content.Client/Preferences/UI/HighlightedContainer.xaml.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HighlightedContainer : PanelContainer
|
||||
{
|
||||
public HighlightedContainer()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,6 @@ namespace Content.Client.Preferences.UI
|
||||
{
|
||||
public sealed partial class HumanoidProfileEditor
|
||||
{
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
|
||||
private void RandomizeEverything()
|
||||
{
|
||||
Profile = HumanoidCharacterProfile.Random();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:prefUi="clr-namespace:Content.Client.Preferences.UI"
|
||||
xmlns:humanoid="clr-namespace:Content.Client.Humanoid"
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
HorizontalExpand="True">
|
||||
<!-- Left side -->
|
||||
<BoxContainer Orientation="Vertical" Margin="10 10 10 10">
|
||||
<BoxContainer Orientation="Vertical" Margin="10 10 10 10" HorizontalExpand="True">
|
||||
<!-- Middle container -->
|
||||
<BoxContainer Orientation="Horizontal" SeparationOverride="10">
|
||||
<!-- Name box-->
|
||||
@@ -58,7 +58,9 @@
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-species-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<TextureButton Name="SpeciesInfoButton" Scale="0.3 0.3" VerticalAlignment="Center"></TextureButton>
|
||||
<TextureButton Name="SpeciesInfoButton" Scale="0.3 0.3"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip="{Loc 'humanoid-profile-editor-guidebook-button-tooltip'}"/>
|
||||
<OptionButton Name="CSpeciesButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Age -->
|
||||
@@ -85,18 +87,6 @@
|
||||
<Control HorizontalExpand="True"/>
|
||||
<Button Name="ShowClothes" Pressed="True" ToggleMode="True" Text="{Loc 'humanoid-profile-editor-clothing-show'}" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Clothing -->
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-clothing-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="CClothingButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Backpack -->
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-backpack-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="CBackpackButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Spawn Priority -->
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-spawn-priority-label'}" />
|
||||
@@ -151,7 +141,7 @@
|
||||
</TabContainer>
|
||||
</BoxContainer>
|
||||
<!-- Right side -->
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" VerticalAlignment="Center">
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" VerticalAlignment="Center">
|
||||
<SpriteView Name="CSpriteView" Scale="8 8" SizeFlagsStretchRatio="1" />
|
||||
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 5">
|
||||
<Button Name="CSpriteRotateLeft" Text="◀" StyleClasses="OpenRight" />
|
||||
@@ -159,5 +149,4 @@
|
||||
<Button Name="CSpriteRotateRight" Text="▶" StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
</BoxContainer>
|
||||
|
||||
@@ -2,69 +2,48 @@ using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Lobby.UI;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Client.UserInterface.Systems.Guidebook;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Content.Shared.Traits;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
|
||||
namespace Content.Client.Preferences.UI
|
||||
{
|
||||
public sealed class HighlightedContainer : PanelContainer
|
||||
{
|
||||
public HighlightedContainer()
|
||||
{
|
||||
PanelOverride = new StyleBoxFlat()
|
||||
{
|
||||
BackgroundColor = new Color(47, 47, 53),
|
||||
ContentMarginTopOverride = 10,
|
||||
ContentMarginBottomOverride = 10,
|
||||
ContentMarginLeftOverride = 10,
|
||||
ContentMarginRightOverride = 10
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HumanoidProfileEditor : Control
|
||||
public sealed partial class HumanoidProfileEditor : BoxContainer
|
||||
{
|
||||
private readonly IClientPreferencesManager _preferencesManager;
|
||||
private readonly IEntityManager _entMan;
|
||||
private readonly IConfigurationManager _configurationManager;
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
private readonly MarkingManager _markingManager;
|
||||
private readonly JobRequirementsManager _requirements;
|
||||
|
||||
private LineEdit _ageEdit => CAgeEdit;
|
||||
private LineEdit _nameEdit => CNameEdit;
|
||||
private TextEdit _flavorTextEdit = null!;
|
||||
private TextEdit? _flavorTextEdit;
|
||||
private Button _nameRandomButton => CNameRandomize;
|
||||
private Button _randomizeEverythingButton => CRandomizeEverything;
|
||||
private RichTextLabel _warningLabel => CWarningLabel;
|
||||
@@ -72,8 +51,6 @@ namespace Content.Client.Preferences.UI
|
||||
private OptionButton _sexButton => CSexButton;
|
||||
private OptionButton _genderButton => CPronounsButton;
|
||||
private Slider _skinColor => CSkin;
|
||||
private OptionButton _clothingButton => CClothingButton;
|
||||
private OptionButton _backpackButton => CBackpackButton;
|
||||
private OptionButton _spawnPriorityButton => CSpawnPriorityButton;
|
||||
private SingleMarkingPicker _hairPicker => CHairStylePicker;
|
||||
private SingleMarkingPicker _facialHairPicker => CFacialHairPicker;
|
||||
@@ -88,44 +65,39 @@ namespace Content.Client.Preferences.UI
|
||||
private readonly Dictionary<string, BoxContainer> _jobCategories;
|
||||
// Mildly hacky, as I don't trust prototype order to stay consistent and don't want the UI to break should a new one get added mid-edit. --moony
|
||||
private readonly List<SpeciesPrototype> _speciesList;
|
||||
private readonly List<AntagPreferenceSelector> _antagPreferences;
|
||||
private readonly List<AntagPreferenceSelector> _antagPreferences = new();
|
||||
private readonly List<TraitPreferenceSelector> _traitPreferences;
|
||||
|
||||
private SpriteView _previewSpriteView => CSpriteView;
|
||||
private Button _previewRotateLeftButton => CSpriteRotateLeft;
|
||||
private Button _previewRotateRightButton => CSpriteRotateRight;
|
||||
private Direction _previewRotation = Direction.North;
|
||||
private EntityUid? _previewDummy;
|
||||
|
||||
private BoxContainer _rgbSkinColorContainer => CRgbSkinColorContainer;
|
||||
private ColorSelectorSliders _rgbSkinColorSelector;
|
||||
|
||||
private bool _isDirty;
|
||||
private bool _needUpdatePreview;
|
||||
public int CharacterSlot;
|
||||
public HumanoidCharacterProfile? Profile;
|
||||
private MarkingSet _markingSet = new(); // storing this here feels iffy but a few things need it this high up
|
||||
|
||||
public event Action<HumanoidCharacterProfile, int>? OnProfileChanged;
|
||||
|
||||
public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager,
|
||||
IEntityManager entityManager, IConfigurationManager configurationManager)
|
||||
[ValidatePrototypeId<GuideEntryPrototype>]
|
||||
private const string DefaultSpeciesGuidebook = "Species";
|
||||
|
||||
public HumanoidProfileEditor(IClientPreferencesManager preferencesManager, IPrototypeManager prototypeManager, IConfigurationManager configurationManager)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_prototypeManager = prototypeManager;
|
||||
_entMan = entityManager;
|
||||
_preferencesManager = preferencesManager;
|
||||
_configurationManager = configurationManager;
|
||||
_markingManager = IoCManager.Resolve<MarkingManager>();
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.PreviewDummyUpdated += OnDummyUpdate;
|
||||
|
||||
SpeciesInfoButton.ToolTip = Loc.GetString("humanoid-profile-editor-guidebook-button-tooltip");
|
||||
_previewSpriteView.SetEntity(controller.GetPreviewDummy());
|
||||
|
||||
#region Left
|
||||
|
||||
#region Randomize
|
||||
|
||||
#endregion Randomize
|
||||
|
||||
#region Name
|
||||
|
||||
_nameEdit.OnTextChanged += args => { SetName(args.Text); };
|
||||
@@ -139,8 +111,6 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
_tabContainer.SetTabTitle(0, Loc.GetString("humanoid-profile-editor-appearance-tab"));
|
||||
|
||||
ShowClothes.OnPressed += ToggleClothes;
|
||||
|
||||
#region Sex
|
||||
|
||||
_sexButton.OnItemSelected += args =>
|
||||
@@ -318,33 +288,6 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
#endregion Hair
|
||||
|
||||
#region Clothing
|
||||
|
||||
_clothingButton.AddItem(Loc.GetString("humanoid-profile-editor-preference-jumpsuit"), (int) ClothingPreference.Jumpsuit);
|
||||
_clothingButton.AddItem(Loc.GetString("humanoid-profile-editor-preference-jumpskirt"), (int) ClothingPreference.Jumpskirt);
|
||||
|
||||
_clothingButton.OnItemSelected += args =>
|
||||
{
|
||||
_clothingButton.SelectId(args.Id);
|
||||
SetClothing((ClothingPreference) args.Id);
|
||||
};
|
||||
|
||||
#endregion Clothing
|
||||
|
||||
#region Backpack
|
||||
|
||||
_backpackButton.AddItem(Loc.GetString("humanoid-profile-editor-preference-backpack"), (int) BackpackPreference.Backpack);
|
||||
_backpackButton.AddItem(Loc.GetString("humanoid-profile-editor-preference-satchel"), (int) BackpackPreference.Satchel);
|
||||
_backpackButton.AddItem(Loc.GetString("humanoid-profile-editor-preference-duffelbag"), (int) BackpackPreference.Duffelbag);
|
||||
|
||||
_backpackButton.OnItemSelected += args =>
|
||||
{
|
||||
_backpackButton.SelectId(args.Id);
|
||||
SetBackpack((BackpackPreference) args.Id);
|
||||
};
|
||||
|
||||
#endregion Backpack
|
||||
|
||||
#region SpawnPriority
|
||||
|
||||
foreach (var value in Enum.GetValues<SpawnPriorityPreference>())
|
||||
@@ -399,40 +342,16 @@ namespace Content.Client.Preferences.UI
|
||||
_jobPriorities = new List<JobPrioritySelector>();
|
||||
_jobCategories = new Dictionary<string, BoxContainer>();
|
||||
_requirements = IoCManager.Resolve<JobRequirementsManager>();
|
||||
// TODO: Move this to the LobbyUIController instead of being spaghetti everywhere.
|
||||
_requirements.Updated += UpdateAntagRequirements;
|
||||
_requirements.Updated += UpdateRoleRequirements;
|
||||
UpdateAntagRequirements();
|
||||
UpdateRoleRequirements();
|
||||
|
||||
#endregion Jobs
|
||||
|
||||
#region Antags
|
||||
|
||||
_tabContainer.SetTabTitle(2, Loc.GetString("humanoid-profile-editor-antags-tab"));
|
||||
|
||||
_antagPreferences = new List<AntagPreferenceSelector>();
|
||||
|
||||
foreach (var antag in prototypeManager.EnumeratePrototypes<AntagPrototype>().OrderBy(a => Loc.GetString(a.Name)))
|
||||
{
|
||||
if (!antag.SetPreference)
|
||||
continue;
|
||||
|
||||
var selector = new AntagPreferenceSelector(antag);
|
||||
_antagList.AddChild(selector);
|
||||
_antagPreferences.Add(selector);
|
||||
if (selector.Disabled)
|
||||
{
|
||||
Profile = Profile?.WithAntagPreference(antag.ID, false);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithAntagPreference(antag.ID, preference);
|
||||
IsDirty = true;
|
||||
};
|
||||
}
|
||||
|
||||
#endregion Antags
|
||||
|
||||
#region Traits
|
||||
|
||||
var traits = prototypeManager.EnumeratePrototypes<TraitPrototype>().OrderBy(t => Loc.GetString(t.Name)).ToList();
|
||||
@@ -483,7 +402,7 @@ namespace Content.Client.Preferences.UI
|
||||
|
||||
#region FlavorText
|
||||
|
||||
if (_configurationManager.GetCVar(CCVars.FlavorText))
|
||||
if (configurationManager.GetCVar(CCVars.FlavorText))
|
||||
{
|
||||
var flavorText = new FlavorText.FlavorText();
|
||||
_tabContainer.AddChild(flavorText);
|
||||
@@ -500,22 +419,14 @@ namespace Content.Client.Preferences.UI
|
||||
_previewRotateLeftButton.OnPressed += _ =>
|
||||
{
|
||||
_previewRotation = _previewRotation.TurnCw();
|
||||
_needUpdatePreview = true;
|
||||
SetPreviewRotation(_previewRotation);
|
||||
};
|
||||
_previewRotateRightButton.OnPressed += _ =>
|
||||
{
|
||||
_previewRotation = _previewRotation.TurnCcw();
|
||||
_needUpdatePreview = true;
|
||||
SetPreviewRotation(_previewRotation);
|
||||
};
|
||||
|
||||
var species = Profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||
var dollProto = _prototypeManager.Index<SpeciesPrototype>(species).DollPrototype;
|
||||
|
||||
if (_previewDummy != null)
|
||||
_entMan.DeleteEntity(_previewDummy!.Value);
|
||||
|
||||
_previewDummy = _entMan.SpawnEntity(dollProto, MapCoordinates.Nullspace);
|
||||
_previewSpriteView.SetEntity(_previewDummy);
|
||||
#endregion Dummy
|
||||
|
||||
#endregion Left
|
||||
@@ -538,22 +449,54 @@ namespace Content.Client.Preferences.UI
|
||||
{
|
||||
var guidebookController = UserInterfaceManager.GetUIController<GuidebookUIController>();
|
||||
var species = Profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||
var page = "Species";
|
||||
var page = DefaultSpeciesGuidebook;
|
||||
if (_prototypeManager.HasIndex<GuideEntryPrototype>(species))
|
||||
page = species;
|
||||
|
||||
if (_prototypeManager.TryIndex<GuideEntryPrototype>("Species", out var guideRoot))
|
||||
if (_prototypeManager.TryIndex<GuideEntryPrototype>(DefaultSpeciesGuidebook, out var guideRoot))
|
||||
{
|
||||
var dict = new Dictionary<string, GuideEntry>();
|
||||
dict.Add("Species", guideRoot);
|
||||
dict.Add(DefaultSpeciesGuidebook, guideRoot);
|
||||
//TODO: Don't close the guidebook if its already open, just go to the correct page
|
||||
guidebookController.ToggleGuidebook(dict, includeChildren:true, selected: page);
|
||||
}
|
||||
}
|
||||
|
||||
private void ToggleClothes(BaseButton.ButtonEventArgs obj)
|
||||
private void OnDummyUpdate(EntityUid value)
|
||||
{
|
||||
RebuildSpriteView();
|
||||
_previewSpriteView.SetEntity(value);
|
||||
}
|
||||
|
||||
private void UpdateAntagRequirements()
|
||||
{
|
||||
_antagList.DisposeAllChildren();
|
||||
_antagPreferences.Clear();
|
||||
var btnGroup = new ButtonGroup();
|
||||
|
||||
foreach (var antag in _prototypeManager.EnumeratePrototypes<AntagPrototype>().OrderBy(a => Loc.GetString(a.Name)))
|
||||
{
|
||||
if (!antag.SetPreference)
|
||||
continue;
|
||||
|
||||
var selector = new AntagPreferenceSelector(antag, btnGroup)
|
||||
{
|
||||
Margin = new Thickness(3f, 3f, 3f, 0f),
|
||||
};
|
||||
_antagList.AddChild(selector);
|
||||
_antagPreferences.Add(selector);
|
||||
if (selector.Disabled)
|
||||
{
|
||||
Profile = Profile?.WithAntagPreference(antag.ID, false);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithAntagPreference(antag.ID, preference);
|
||||
IsDirty = true;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void UpdateRoleRequirements()
|
||||
@@ -614,10 +557,16 @@ namespace Content.Client.Preferences.UI
|
||||
.Where(job => job.SetPreference)
|
||||
.ToArray();
|
||||
Array.Sort(jobs, JobUIComparer.Instance);
|
||||
var jobLoadoutGroup = new ButtonGroup();
|
||||
|
||||
foreach (var job in jobs)
|
||||
{
|
||||
var selector = new JobPrioritySelector(job, _prototypeManager);
|
||||
RoleLoadout? loadout = null;
|
||||
Profile?.Loadouts.TryGetValue(LoadoutSystem.GetJobPrototype(job.ID), out loadout);
|
||||
var selector = new JobPrioritySelector(loadout, job, jobLoadoutGroup, _prototypeManager)
|
||||
{
|
||||
Margin = new Thickness(3f, 3f, 3f, 0f),
|
||||
};
|
||||
|
||||
if (!_requirements.IsAllowed(job, out var reason))
|
||||
{
|
||||
@@ -627,6 +576,13 @@ namespace Content.Client.Preferences.UI
|
||||
category.AddChild(selector);
|
||||
_jobPriorities.Add(selector);
|
||||
|
||||
selector.LoadoutUpdated += args =>
|
||||
{
|
||||
Profile?.SetLoadout(args);
|
||||
UserInterfaceManager.GetUIController<LobbyUIController>().UpdateCharacterUI();
|
||||
IsDirty = true;
|
||||
};
|
||||
|
||||
selector.PriorityChanged += priority =>
|
||||
{
|
||||
Profile = Profile?.WithJobPriority(job.ID, priority);
|
||||
@@ -672,20 +628,10 @@ namespace Content.Client.Preferences.UI
|
||||
return;
|
||||
|
||||
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithMarkings(markings.GetForwardEnumerator().ToList()));
|
||||
_needUpdatePreview = true;
|
||||
UpdatePreview();
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void OnMarkingColorChange(List<Marking> markings)
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithMarkings(markings));
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
|
||||
private void OnSkinColorOnValueChanged()
|
||||
{
|
||||
if (Profile is null) return;
|
||||
@@ -745,33 +691,21 @@ namespace Content.Client.Preferences.UI
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
if (_previewDummy != null)
|
||||
_entMan.DeleteEntity(_previewDummy.Value);
|
||||
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.PreviewDummyUpdated -= OnDummyUpdate;
|
||||
_requirements.Updated -= UpdateAntagRequirements;
|
||||
_requirements.Updated -= UpdateRoleRequirements;
|
||||
_preferencesManager.OnServerDataLoaded -= LoadServerData;
|
||||
}
|
||||
|
||||
private void RebuildSpriteView()
|
||||
{
|
||||
var species = Profile?.Species ?? SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||
var dollProto = _prototypeManager.Index<SpeciesPrototype>(species).DollPrototype;
|
||||
|
||||
if (_previewDummy != null)
|
||||
_entMan.DeleteEntity(_previewDummy!.Value);
|
||||
|
||||
_previewDummy = _entMan.SpawnEntity(dollProto, MapCoordinates.Nullspace);
|
||||
_previewSpriteView.SetEntity(_previewDummy);
|
||||
_needUpdatePreview = true;
|
||||
}
|
||||
|
||||
private void LoadServerData()
|
||||
{
|
||||
Profile = (HumanoidCharacterProfile) _preferencesManager.Preferences!.SelectedCharacter;
|
||||
CharacterSlot = _preferencesManager.Preferences.SelectedCharacterIndex;
|
||||
|
||||
UpdateAntagRequirements();
|
||||
UpdateRoleRequirements();
|
||||
UpdateControls();
|
||||
_needUpdatePreview = true;
|
||||
}
|
||||
|
||||
private void SetAge(int newAge)
|
||||
@@ -813,10 +747,9 @@ namespace Content.Client.Preferences.UI
|
||||
OnSkinColorOnValueChanged(); // Species may have special color prefs, make sure to update it.
|
||||
CMarkings.SetSpecies(newSpecies); // Repopulate the markings tab as well.
|
||||
UpdateSexControls(); // update sex for new species
|
||||
RebuildSpriteView(); // they might have different inv so we need a new dummy
|
||||
UpdateSpeciesGuidebookIcon();
|
||||
IsDirty = true;
|
||||
_needUpdatePreview = true;
|
||||
UpdatePreview();
|
||||
}
|
||||
|
||||
private void SetName(string newName)
|
||||
@@ -825,18 +758,6 @@ namespace Content.Client.Preferences.UI
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetClothing(ClothingPreference newClothing)
|
||||
{
|
||||
Profile = Profile?.WithClothingPreference(newClothing);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetBackpack(BackpackPreference newBackpack)
|
||||
{
|
||||
Profile = Profile?.WithBackpackPreference(newBackpack);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority)
|
||||
{
|
||||
Profile = Profile?.WithSpawnPriorityPreference(newSpawnPriority);
|
||||
@@ -847,12 +768,11 @@ namespace Content.Client.Preferences.UI
|
||||
{
|
||||
IsDirty = false;
|
||||
|
||||
if (Profile != null)
|
||||
{
|
||||
if (Profile == null)
|
||||
return;
|
||||
|
||||
_preferencesManager.UpdateCharacter(Profile, CharacterSlot);
|
||||
OnProfileChanged?.Invoke(Profile, CharacterSlot);
|
||||
_needUpdatePreview = true;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsDirty
|
||||
@@ -861,7 +781,6 @@ namespace Content.Client.Preferences.UI
|
||||
set
|
||||
{
|
||||
_isDirty = value;
|
||||
_needUpdatePreview = true;
|
||||
UpdateSaveButton();
|
||||
}
|
||||
}
|
||||
@@ -981,7 +900,7 @@ namespace Content.Client.Preferences.UI
|
||||
if (!_prototypeManager.HasIndex<GuideEntryPrototype>(species))
|
||||
return;
|
||||
|
||||
var style = speciesProto.GuideBookIcon;
|
||||
const string style = "SpeciesInfoDefault";
|
||||
SpeciesInfoButton.StyleClasses.Add(style);
|
||||
}
|
||||
|
||||
@@ -1017,26 +936,6 @@ namespace Content.Client.Preferences.UI
|
||||
_genderButton.SelectId((int) Profile.Gender);
|
||||
}
|
||||
|
||||
private void UpdateClothingControls()
|
||||
{
|
||||
if (Profile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_clothingButton.SelectId((int) Profile.Clothing);
|
||||
}
|
||||
|
||||
private void UpdateBackpackControls()
|
||||
{
|
||||
if (Profile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_backpackButton.SelectId((int) Profile.Backpack);
|
||||
}
|
||||
|
||||
private void UpdateSpawnPriorityControls()
|
||||
{
|
||||
if (Profile == null)
|
||||
@@ -1166,13 +1065,13 @@ namespace Content.Client.Preferences.UI
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
var humanoid = _entMan.System<HumanoidAppearanceSystem>();
|
||||
humanoid.LoadProfile(_previewDummy!.Value, Profile);
|
||||
UserInterfaceManager.GetUIController<LobbyUIController>().UpdateCharacterUI();
|
||||
SetPreviewRotation(_previewRotation);
|
||||
}
|
||||
|
||||
if (ShowClothes.Pressed)
|
||||
LobbyCharacterPreviewPanel.GiveDummyJobClothes(_previewDummy!.Value, Profile);
|
||||
|
||||
_previewSpriteView.OverrideDirection = (Direction) ((int) _previewRotation % 4 * 2);
|
||||
private void SetPreviewRotation(Direction direction)
|
||||
{
|
||||
_previewSpriteView.OverrideDirection = (Direction) ((int) direction % 4 * 2);
|
||||
}
|
||||
|
||||
public void UpdateControls()
|
||||
@@ -1184,17 +1083,15 @@ namespace Content.Client.Preferences.UI
|
||||
UpdateGenderControls();
|
||||
UpdateSkinColor();
|
||||
UpdateSpecies();
|
||||
UpdateClothingControls();
|
||||
UpdateBackpackControls();
|
||||
UpdateSpawnPriorityControls();
|
||||
UpdateAgeEdit();
|
||||
UpdateEyePickers();
|
||||
UpdateSaveButton();
|
||||
UpdateLoadouts();
|
||||
UpdateJobPriorities();
|
||||
UpdateAntagPreferences();
|
||||
UpdateTraitPreferences();
|
||||
UpdateMarkings();
|
||||
RebuildSpriteView();
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsHair();
|
||||
UpdateCMarkingsFacialHair();
|
||||
@@ -1202,17 +1099,6 @@ namespace Content.Client.Preferences.UI
|
||||
_preferenceUnavailableButton.SelectId((int) Profile.PreferenceUnavailable);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (_needUpdatePreview)
|
||||
{
|
||||
UpdatePreview();
|
||||
_needUpdatePreview = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateJobPriorities()
|
||||
{
|
||||
foreach (var prioritySelector in _jobPriorities)
|
||||
@@ -1225,143 +1111,11 @@ namespace Content.Client.Preferences.UI
|
||||
}
|
||||
}
|
||||
|
||||
private abstract class RequirementsSelector<T> : Control
|
||||
private void UpdateLoadouts()
|
||||
{
|
||||
public T Proto { get; }
|
||||
public bool Disabled => _lockStripe.Visible;
|
||||
|
||||
protected readonly RadioOptions<int> Options;
|
||||
private StripeBack _lockStripe;
|
||||
private Label _requirementsLabel;
|
||||
|
||||
protected RequirementsSelector(T proto)
|
||||
foreach (var prioritySelector in _jobPriorities)
|
||||
{
|
||||
Proto = proto;
|
||||
|
||||
Options = new RadioOptions<int>(RadioOptionsLayout.Horizontal)
|
||||
{
|
||||
FirstButtonStyle = StyleBase.ButtonOpenRight,
|
||||
ButtonStyle = StyleBase.ButtonOpenBoth,
|
||||
LastButtonStyle = StyleBase.ButtonOpenLeft
|
||||
};
|
||||
//Override default radio option button width
|
||||
Options.GenerateItem = GenerateButton;
|
||||
|
||||
Options.OnItemSelected += args => Options.Select(args.Id);
|
||||
|
||||
_requirementsLabel = new Label()
|
||||
{
|
||||
Text = Loc.GetString("role-timer-locked"),
|
||||
Visible = true,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
StyleClasses = {StyleBase.StyleClassLabelSubText},
|
||||
};
|
||||
|
||||
_lockStripe = new StripeBack()
|
||||
{
|
||||
Visible = false,
|
||||
HorizontalExpand = true,
|
||||
MouseFilter = MouseFilterMode.Stop,
|
||||
Children =
|
||||
{
|
||||
_requirementsLabel
|
||||
}
|
||||
};
|
||||
|
||||
// Setup must be called after
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actually adds the controls, must be called in the inheriting class' constructor.
|
||||
/// </summary>
|
||||
protected void Setup((string, int)[] items, string title, int titleSize, string? description, TextureRect? icon = null)
|
||||
{
|
||||
foreach (var (text, value) in items)
|
||||
{
|
||||
Options.AddItem(Loc.GetString(text), value);
|
||||
}
|
||||
|
||||
var titleLabel = new Label()
|
||||
{
|
||||
Margin = new Thickness(5f, 0, 5f, 0),
|
||||
Text = title,
|
||||
MinSize = new Vector2(titleSize, 0),
|
||||
MouseFilter = MouseFilterMode.Stop,
|
||||
ToolTip = description
|
||||
};
|
||||
|
||||
var container = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
};
|
||||
|
||||
if (icon != null)
|
||||
container.AddChild(icon);
|
||||
container.AddChild(titleLabel);
|
||||
container.AddChild(Options);
|
||||
container.AddChild(_lockStripe);
|
||||
|
||||
AddChild(container);
|
||||
}
|
||||
|
||||
public void LockRequirements(FormattedMessage requirements)
|
||||
{
|
||||
var tooltip = new Tooltip();
|
||||
tooltip.SetMessage(requirements);
|
||||
_lockStripe.TooltipSupplier = _ => tooltip;
|
||||
_lockStripe.Visible = true;
|
||||
Options.Visible = false;
|
||||
}
|
||||
|
||||
// TODO: Subscribe to roletimers event. I am too lazy to do this RN But I doubt most people will notice fn
|
||||
public void UnlockRequirements()
|
||||
{
|
||||
_lockStripe.Visible = false;
|
||||
Options.Visible = true;
|
||||
}
|
||||
|
||||
private Button GenerateButton(string text, int value)
|
||||
{
|
||||
return new Button
|
||||
{
|
||||
Text = text,
|
||||
MinWidth = 90
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class JobPrioritySelector : RequirementsSelector<JobPrototype>
|
||||
{
|
||||
public JobPriority Priority
|
||||
{
|
||||
get => (JobPriority) Options.SelectedValue;
|
||||
set => Options.SelectByValue((int) value);
|
||||
}
|
||||
|
||||
public event Action<JobPriority>? PriorityChanged;
|
||||
|
||||
public JobPrioritySelector(JobPrototype proto, IPrototypeManager protoMan)
|
||||
: base(proto)
|
||||
{
|
||||
Options.OnItemSelected += args => PriorityChanged?.Invoke(Priority);
|
||||
|
||||
var items = new[]
|
||||
{
|
||||
("humanoid-profile-editor-job-priority-high-button", (int) JobPriority.High),
|
||||
("humanoid-profile-editor-job-priority-medium-button", (int) JobPriority.Medium),
|
||||
("humanoid-profile-editor-job-priority-low-button", (int) JobPriority.Low),
|
||||
("humanoid-profile-editor-job-priority-never-button", (int) JobPriority.Never),
|
||||
};
|
||||
|
||||
var icon = new TextureRect
|
||||
{
|
||||
TextureScale = new Vector2(2, 2),
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
var jobIcon = protoMan.Index<StatusIconPrototype>(proto.Icon);
|
||||
icon.Texture = jobIcon.Icon.Frame0();
|
||||
|
||||
Setup(items, proto.LocalizedName, 200, proto.LocalizedDescription, icon);
|
||||
prioritySelector.CloseLoadout();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1386,41 +1140,6 @@ namespace Content.Client.Preferences.UI
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class AntagPreferenceSelector : RequirementsSelector<AntagPrototype>
|
||||
{
|
||||
// 0 is yes and 1 is no
|
||||
public bool Preference
|
||||
{
|
||||
get => Options.SelectedValue == 0;
|
||||
set => Options.Select((value && !Disabled) ? 0 : 1);
|
||||
}
|
||||
|
||||
public event Action<bool>? PreferenceChanged;
|
||||
|
||||
public AntagPreferenceSelector(AntagPrototype proto)
|
||||
: base(proto)
|
||||
{
|
||||
Options.OnItemSelected += args => PreferenceChanged?.Invoke(Preference);
|
||||
|
||||
var items = new[]
|
||||
{
|
||||
("humanoid-profile-editor-antag-preference-yes-button", 0),
|
||||
("humanoid-profile-editor-antag-preference-no-button", 1)
|
||||
};
|
||||
var title = Loc.GetString(proto.Name);
|
||||
var description = Loc.GetString(proto.Objective);
|
||||
Setup(items, title, 250, description);
|
||||
|
||||
// immediately lock requirements if they arent met.
|
||||
// another function checks Disabled after creating the selector so this has to be done now
|
||||
var requirements = IoCManager.Resolve<JobRequirementsManager>();
|
||||
if (proto.Requirements != null && !requirements.CheckRoleTime(proto.Requirements, out var reason))
|
||||
{
|
||||
LockRequirements(reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TraitPreferenceSelector : Control
|
||||
{
|
||||
public TraitPrototype Trait { get; }
|
||||
|
||||
46
Content.Client/Preferences/UI/JobPrioritySelector.cs
Normal file
46
Content.Client/Preferences/UI/JobPrioritySelector.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.StatusIcon;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.Utility;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
|
||||
public sealed class JobPrioritySelector : RequirementsSelector<JobPrototype>
|
||||
{
|
||||
public JobPriority Priority
|
||||
{
|
||||
get => (JobPriority) Options.SelectedValue;
|
||||
set => Options.SelectByValue((int) value);
|
||||
}
|
||||
|
||||
public event Action<JobPriority>? PriorityChanged;
|
||||
|
||||
public JobPrioritySelector(RoleLoadout? loadout, JobPrototype proto, ButtonGroup btnGroup, IPrototypeManager protoMan)
|
||||
: base(proto, btnGroup)
|
||||
{
|
||||
Options.OnItemSelected += args => PriorityChanged?.Invoke(Priority);
|
||||
|
||||
var items = new[]
|
||||
{
|
||||
("humanoid-profile-editor-job-priority-high-button", (int) JobPriority.High),
|
||||
("humanoid-profile-editor-job-priority-medium-button", (int) JobPriority.Medium),
|
||||
("humanoid-profile-editor-job-priority-low-button", (int) JobPriority.Low),
|
||||
("humanoid-profile-editor-job-priority-never-button", (int) JobPriority.Never),
|
||||
};
|
||||
|
||||
var icon = new TextureRect
|
||||
{
|
||||
TextureScale = new Vector2(2, 2),
|
||||
VerticalAlignment = VAlignment.Center
|
||||
};
|
||||
var jobIcon = protoMan.Index<StatusIconPrototype>(proto.Icon);
|
||||
icon.Texture = jobIcon.Icon.Frame0();
|
||||
|
||||
Setup(loadout, items, proto.LocalizedName, 200, proto.LocalizedDescription, icon);
|
||||
}
|
||||
}
|
||||
15
Content.Client/Preferences/UI/LoadoutContainer.xaml
Normal file
15
Content.Client/Preferences/UI/LoadoutContainer.xaml
Normal file
@@ -0,0 +1,15 @@
|
||||
<BoxContainer Name="Container" xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
|
||||
Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
MouseFilter="Ignore"
|
||||
Margin="0 0 0 5">
|
||||
<Button Name="SelectButton" ToggleMode="True" Margin="0 0 5 0" HorizontalExpand="True"/>
|
||||
<PanelContainer SetSize="64 64" HorizontalAlignment="Right">
|
||||
<PanelContainer.PanelOverride>
|
||||
<graphics:StyleBoxFlat BackgroundColor="#1B1B1E" />
|
||||
</PanelContainer.PanelOverride>
|
||||
<SpriteView Name="Sprite" Scale="4 4" MouseFilter="Stop"/>
|
||||
</PanelContainer>
|
||||
</BoxContainer>
|
||||
74
Content.Client/Preferences/UI/LoadoutContainer.xaml.cs
Normal file
74
Content.Client/Preferences/UI/LoadoutContainer.xaml.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LoadoutContainer : BoxContainer
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
|
||||
private readonly EntityUid? _entity;
|
||||
|
||||
public Button Select => SelectButton;
|
||||
|
||||
public LoadoutContainer(ProtoId<LoadoutPrototype> proto, bool disabled, FormattedMessage? reason)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
SelectButton.Disabled = disabled;
|
||||
|
||||
if (disabled && reason != null)
|
||||
{
|
||||
var tooltip = new Tooltip();
|
||||
tooltip.SetMessage(reason);
|
||||
SelectButton.TooltipSupplier = _ => tooltip;
|
||||
}
|
||||
|
||||
if (_protoManager.TryIndex(proto, out var loadProto))
|
||||
{
|
||||
var ent = _entManager.System<LoadoutSystem>().GetFirstOrNull(loadProto);
|
||||
|
||||
if (ent != null)
|
||||
{
|
||||
_entity = _entManager.SpawnEntity(ent, MapCoordinates.Nullspace);
|
||||
Sprite.SetEntity(_entity);
|
||||
|
||||
var spriteTooltip = new Tooltip();
|
||||
spriteTooltip.SetMessage(FormattedMessage.FromUnformatted(_entManager.GetComponent<MetaDataComponent>(_entity.Value).EntityDescription));
|
||||
Sprite.TooltipSupplier = _ => spriteTooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_entManager.DeleteEntity(_entity);
|
||||
}
|
||||
|
||||
public bool Pressed
|
||||
{
|
||||
get => SelectButton.Pressed;
|
||||
set => SelectButton.Pressed = value;
|
||||
}
|
||||
|
||||
public string? Text
|
||||
{
|
||||
get => SelectButton.Text;
|
||||
set => SelectButton.Text = value;
|
||||
}
|
||||
}
|
||||
10
Content.Client/Preferences/UI/LoadoutGroupContainer.xaml
Normal file
10
Content.Client/Preferences/UI/LoadoutGroupContainer.xaml
Normal file
@@ -0,0 +1,10 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Orientation="Vertical">
|
||||
<PanelContainer StyleClasses="AngleRect" HorizontalExpand="True">
|
||||
<BoxContainer Name="LoadoutsContainer" Orientation="Vertical"/>
|
||||
</PanelContainer>
|
||||
<!-- Buffer space so we have 10 margin between controls but also 10 to the borders -->
|
||||
<Label Text="{Loc 'loadout-restrictions'}" Margin="5 0 5 5"/>
|
||||
<BoxContainer Name="RestrictionsContainer" Orientation="Vertical" HorizontalExpand="True" />
|
||||
</BoxContainer>
|
||||
93
Content.Client/Preferences/UI/LoadoutGroupContainer.xaml.cs
Normal file
93
Content.Client/Preferences/UI/LoadoutGroupContainer.xaml.cs
Normal file
@@ -0,0 +1,93 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LoadoutGroupContainer : BoxContainer
|
||||
{
|
||||
private readonly LoadoutGroupPrototype _groupProto;
|
||||
|
||||
public event Action<ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
|
||||
public event Action<ProtoId<LoadoutPrototype>>? OnLoadoutUnpressed;
|
||||
|
||||
public LoadoutGroupContainer(RoleLoadout loadout, LoadoutGroupPrototype groupProto, ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_groupProto = groupProto;
|
||||
|
||||
RefreshLoadouts(loadout, session, collection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates button availabilities and buttons.
|
||||
/// </summary>
|
||||
public void RefreshLoadouts(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
var protoMan = collection.Resolve<IPrototypeManager>();
|
||||
var loadoutSystem = collection.Resolve<IEntityManager>().System<LoadoutSystem>();
|
||||
RestrictionsContainer.DisposeAllChildren();
|
||||
|
||||
if (_groupProto.MinLimit > 0)
|
||||
{
|
||||
RestrictionsContainer.AddChild(new Label()
|
||||
{
|
||||
Text = Loc.GetString("loadouts-min-limit", ("count", _groupProto.MinLimit)),
|
||||
Margin = new Thickness(5, 0, 5, 5),
|
||||
});
|
||||
}
|
||||
|
||||
if (_groupProto.MaxLimit > 0)
|
||||
{
|
||||
RestrictionsContainer.AddChild(new Label()
|
||||
{
|
||||
Text = Loc.GetString("loadouts-max-limit", ("count", _groupProto.MaxLimit)),
|
||||
Margin = new Thickness(5, 0, 5, 5),
|
||||
});
|
||||
}
|
||||
|
||||
if (protoMan.TryIndex(loadout.Role, out var roleProto) && roleProto.Points != null && loadout.Points != null)
|
||||
{
|
||||
RestrictionsContainer.AddChild(new Label()
|
||||
{
|
||||
Text = Loc.GetString("loadouts-points-limit", ("count", loadout.Points.Value), ("max", roleProto.Points.Value)),
|
||||
Margin = new Thickness(5, 0, 5, 5),
|
||||
});
|
||||
}
|
||||
|
||||
LoadoutsContainer.DisposeAllChildren();
|
||||
// Didn't use options because this is more robust in future.
|
||||
|
||||
var selected = loadout.SelectedLoadouts[_groupProto.ID];
|
||||
|
||||
foreach (var loadoutProto in _groupProto.Loadouts)
|
||||
{
|
||||
if (!protoMan.TryIndex(loadoutProto, out var loadProto))
|
||||
continue;
|
||||
|
||||
var matchingLoadout = selected.FirstOrDefault(e => e.Prototype == loadoutProto);
|
||||
var pressed = matchingLoadout != null;
|
||||
|
||||
var enabled = loadout.IsValid(session, loadoutProto, collection, out var reason);
|
||||
var loadoutContainer = new LoadoutContainer(loadoutProto, !enabled, reason);
|
||||
loadoutContainer.Select.Pressed = pressed;
|
||||
loadoutContainer.Text = loadoutSystem.GetName(loadProto);
|
||||
|
||||
loadoutContainer.Select.OnPressed += args =>
|
||||
{
|
||||
if (args.Button.Pressed)
|
||||
OnLoadoutPressed?.Invoke(loadoutProto);
|
||||
else
|
||||
OnLoadoutUnpressed?.Invoke(loadoutProto);
|
||||
};
|
||||
|
||||
LoadoutsContainer.AddChild(loadoutContainer);
|
||||
}
|
||||
}
|
||||
}
|
||||
10
Content.Client/Preferences/UI/LoadoutWindow.xaml
Normal file
10
Content.Client/Preferences/UI/LoadoutWindow.xaml
Normal file
@@ -0,0 +1,10 @@
|
||||
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||
SetSize="800 800"
|
||||
MinSize="800 64">
|
||||
<VerticalTabContainer Name="LoadoutGroupsContainer"
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True">
|
||||
</VerticalTabContainer>
|
||||
</controls:FancyWindow>
|
||||
60
Content.Client/Preferences/UI/LoadoutWindow.xaml.cs
Normal file
60
Content.Client/Preferences/UI/LoadoutWindow.xaml.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LoadoutWindow : FancyWindow
|
||||
{
|
||||
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
|
||||
public event Action<ProtoId<LoadoutGroupPrototype>, ProtoId<LoadoutPrototype>>? OnLoadoutUnpressed;
|
||||
|
||||
private List<LoadoutGroupContainer> _groups = new();
|
||||
|
||||
public LoadoutWindow(RoleLoadout loadout, RoleLoadoutPrototype proto, ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||
|
||||
foreach (var group in proto.Groups)
|
||||
{
|
||||
if (!protoManager.TryIndex(group, out var groupProto))
|
||||
continue;
|
||||
|
||||
var container = new LoadoutGroupContainer(loadout, protoManager.Index(group), session, collection);
|
||||
LoadoutGroupsContainer.AddTab(container, Loc.GetString(groupProto.Name));
|
||||
_groups.Add(container);
|
||||
|
||||
container.OnLoadoutPressed += args =>
|
||||
{
|
||||
OnLoadoutPressed?.Invoke(group, args);
|
||||
};
|
||||
|
||||
container.OnLoadoutUnpressed += args =>
|
||||
{
|
||||
OnLoadoutUnpressed?.Invoke(group, args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
base.Close();
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.SetDummyJob(null);
|
||||
}
|
||||
|
||||
public void RefreshLoadouts(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
foreach (var group in _groups)
|
||||
{
|
||||
group.RefreshLoadouts(loadout, session, collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
222
Content.Client/Preferences/UI/RequirementsSelector.cs
Normal file
222
Content.Client/Preferences/UI/RequirementsSelector.cs
Normal file
@@ -0,0 +1,222 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
|
||||
public abstract class RequirementsSelector<T> : BoxContainer where T : IPrototype
|
||||
{
|
||||
private ButtonGroup _loadoutGroup;
|
||||
|
||||
public T Proto { get; }
|
||||
public bool Disabled => _lockStripe.Visible;
|
||||
|
||||
protected readonly RadioOptions<int> Options;
|
||||
private readonly StripeBack _lockStripe;
|
||||
private LoadoutWindow? _loadoutWindow;
|
||||
|
||||
private RoleLoadout? _loadout;
|
||||
|
||||
/// <summary>
|
||||
/// Raised if a loadout has been updated.
|
||||
/// </summary>
|
||||
public event Action<RoleLoadout>? LoadoutUpdated;
|
||||
|
||||
protected RequirementsSelector(T proto, ButtonGroup loadoutGroup)
|
||||
{
|
||||
_loadoutGroup = loadoutGroup;
|
||||
Proto = proto;
|
||||
|
||||
Options = new RadioOptions<int>(RadioOptionsLayout.Horizontal)
|
||||
{
|
||||
FirstButtonStyle = StyleBase.ButtonOpenRight,
|
||||
ButtonStyle = StyleBase.ButtonOpenBoth,
|
||||
LastButtonStyle = StyleBase.ButtonOpenLeft,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
//Override default radio option button width
|
||||
Options.GenerateItem = GenerateButton;
|
||||
|
||||
Options.OnItemSelected += args => Options.Select(args.Id);
|
||||
|
||||
var requirementsLabel = new Label()
|
||||
{
|
||||
Text = Loc.GetString("role-timer-locked"),
|
||||
Visible = true,
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
StyleClasses = {StyleBase.StyleClassLabelSubText},
|
||||
};
|
||||
|
||||
_lockStripe = new StripeBack()
|
||||
{
|
||||
Visible = false,
|
||||
HorizontalExpand = true,
|
||||
HasMargins = false,
|
||||
MouseFilter = MouseFilterMode.Stop,
|
||||
Children =
|
||||
{
|
||||
requirementsLabel
|
||||
}
|
||||
};
|
||||
|
||||
// Setup must be called after
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actually adds the controls, must be called in the inheriting class' constructor.
|
||||
/// </summary>
|
||||
protected void Setup(RoleLoadout? loadout, (string, int)[] items, string title, int titleSize, string? description, TextureRect? icon = null)
|
||||
{
|
||||
_loadout = loadout;
|
||||
|
||||
foreach (var (text, value) in items)
|
||||
{
|
||||
Options.AddItem(Loc.GetString(text), value);
|
||||
}
|
||||
|
||||
var titleLabel = new Label()
|
||||
{
|
||||
Margin = new Thickness(5f, 0, 5f, 0),
|
||||
Text = title,
|
||||
MinSize = new Vector2(titleSize, 0),
|
||||
MouseFilter = MouseFilterMode.Stop,
|
||||
ToolTip = description
|
||||
};
|
||||
|
||||
if (icon != null)
|
||||
AddChild(icon);
|
||||
|
||||
AddChild(titleLabel);
|
||||
AddChild(Options);
|
||||
AddChild(_lockStripe);
|
||||
|
||||
var loadoutWindowBtn = new Button()
|
||||
{
|
||||
Text = Loc.GetString("loadout-window"),
|
||||
HorizontalAlignment = HAlignment.Right,
|
||||
Group = _loadoutGroup,
|
||||
Margin = new Thickness(3f, 0f, 0f, 0f),
|
||||
};
|
||||
|
||||
var collection = IoCManager.Instance!;
|
||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||
|
||||
// If no loadout found then disabled button
|
||||
if (!protoManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(Proto.ID)))
|
||||
{
|
||||
loadoutWindowBtn.Disabled = true;
|
||||
}
|
||||
// else
|
||||
else
|
||||
{
|
||||
var session = collection.Resolve<IPlayerManager>().LocalSession!;
|
||||
// TODO: Most of lobby state should be a uicontroller
|
||||
// trying to handle all this shit is a big-ass mess.
|
||||
// Every time I touch it I try to make it slightly better but it needs a howitzer dropped on it.
|
||||
loadoutWindowBtn.OnPressed += args =>
|
||||
{
|
||||
if (args.Button.Pressed)
|
||||
{
|
||||
// We only create a loadout when necessary to avoid unnecessary DB entries.
|
||||
_loadout ??= new RoleLoadout(LoadoutSystem.GetJobPrototype(Proto.ID));
|
||||
_loadout.SetDefault(protoManager);
|
||||
|
||||
_loadoutWindow = new LoadoutWindow(_loadout, protoManager.Index(_loadout.Role), session, collection)
|
||||
{
|
||||
Title = Loc.GetString(Proto.ID + "-loadout"),
|
||||
};
|
||||
|
||||
_loadoutWindow.RefreshLoadouts(_loadout, session, collection);
|
||||
|
||||
// If it's a job preview then refresh it.
|
||||
if (Proto is JobPrototype jobProto)
|
||||
{
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.SetDummyJob(jobProto);
|
||||
}
|
||||
|
||||
_loadoutWindow.OnLoadoutUnpressed += (selectedGroup, selectedLoadout) =>
|
||||
{
|
||||
if (!_loadout.RemoveLoadout(selectedGroup, selectedLoadout, protoManager))
|
||||
return;
|
||||
|
||||
_loadout.EnsureValid(session, collection);
|
||||
_loadoutWindow.RefreshLoadouts(_loadout, session, collection);
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.UpdateCharacterUI();
|
||||
LoadoutUpdated?.Invoke(_loadout);
|
||||
};
|
||||
|
||||
_loadoutWindow.OnLoadoutPressed += (selectedGroup, selectedLoadout) =>
|
||||
{
|
||||
if (!_loadout.AddLoadout(selectedGroup, selectedLoadout, protoManager))
|
||||
return;
|
||||
|
||||
_loadout.EnsureValid(session, collection);
|
||||
_loadoutWindow.RefreshLoadouts(_loadout, session, collection);
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.UpdateCharacterUI();
|
||||
LoadoutUpdated?.Invoke(_loadout);
|
||||
};
|
||||
|
||||
_loadoutWindow.OpenCenteredLeft();
|
||||
_loadoutWindow.OnClose += () =>
|
||||
{
|
||||
loadoutWindowBtn.Pressed = false;
|
||||
_loadoutWindow?.Dispose();
|
||||
_loadoutWindow = null;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseLoadout();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
AddChild(loadoutWindowBtn);
|
||||
}
|
||||
|
||||
public void CloseLoadout()
|
||||
{
|
||||
_loadoutWindow?.Close();
|
||||
_loadoutWindow?.Dispose();
|
||||
_loadoutWindow = null;
|
||||
}
|
||||
|
||||
public void LockRequirements(FormattedMessage requirements)
|
||||
{
|
||||
var tooltip = new Tooltip();
|
||||
tooltip.SetMessage(requirements);
|
||||
_lockStripe.TooltipSupplier = _ => tooltip;
|
||||
_lockStripe.Visible = true;
|
||||
Options.Visible = false;
|
||||
}
|
||||
|
||||
// TODO: Subscribe to roletimers event. I am too lazy to do this RN But I doubt most people will notice fn
|
||||
public void UnlockRequirements()
|
||||
{
|
||||
_lockStripe.Visible = false;
|
||||
Options.Visible = true;
|
||||
}
|
||||
|
||||
private Button GenerateButton(string text, int value)
|
||||
{
|
||||
return new Button
|
||||
{
|
||||
Text = text,
|
||||
MinWidth = 90,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ public sealed partial class MindTests
|
||||
await using var pair = await PoolManager.GetServerClient(settings);
|
||||
|
||||
// Client is connected with a valid entity & mind
|
||||
Assert.That(pair.Client.EntMan.EntityExists(pair.Client.Player?.ControlledEntity));
|
||||
Assert.That(pair.Client.EntMan.EntityExists(pair.Client.AttachedEntity));
|
||||
Assert.That(pair.Server.EntMan.EntityExists(pair.PlayerData?.Mind));
|
||||
|
||||
// Delete **everything**
|
||||
@@ -28,6 +28,12 @@ public sealed partial class MindTests
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
Assert.That(pair.Server.EntMan.EntityCount, Is.EqualTo(0));
|
||||
|
||||
foreach (var ent in pair.Client.EntMan.GetEntities())
|
||||
{
|
||||
Console.WriteLine(pair.Client.EntMan.ToPrettyString(ent));
|
||||
}
|
||||
|
||||
Assert.That(pair.Client.EntMan.EntityCount, Is.EqualTo(0));
|
||||
|
||||
// Create a new map.
|
||||
@@ -36,7 +42,7 @@ public sealed partial class MindTests
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
// Client is not attached to anything
|
||||
Assert.That(pair.Client.Player?.ControlledEntity, Is.Null);
|
||||
Assert.That(pair.Client.AttachedEntity, Is.Null);
|
||||
Assert.That(pair.PlayerData?.Mind, Is.Null);
|
||||
|
||||
// Attempt to ghost
|
||||
@@ -45,9 +51,9 @@ public sealed partial class MindTests
|
||||
await pair.RunTicksSync(10);
|
||||
|
||||
// Client should be attached to a ghost placed on the new map.
|
||||
Assert.That(pair.Client.EntMan.EntityExists(pair.Client.Player?.ControlledEntity));
|
||||
Assert.That(pair.Client.EntMan.EntityExists(pair.Client.AttachedEntity));
|
||||
Assert.That(pair.Server.EntMan.EntityExists(pair.PlayerData?.Mind));
|
||||
var xform = pair.Client.Transform(pair.Client.Player!.ControlledEntity!.Value);
|
||||
var xform = pair.Client.Transform(pair.Client.AttachedEntity!.Value);
|
||||
Assert.That(xform.MapID, Is.EqualTo(new MapId(mapId)));
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
|
||||
44
Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs
Normal file
44
Content.IntegrationTests/Tests/Preferences/LoadoutTests.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles.Jobs;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Preferences;
|
||||
|
||||
[TestFixture]
|
||||
[Ignore("HumanoidAppearance crashes upon loading default profiles.")]
|
||||
public sealed class LoadoutTests
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks that an empty loadout still spawns with default gear and not naked.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestEmptyLoadout()
|
||||
{
|
||||
var pair = await PoolManager.GetServerClient(new PoolSettings()
|
||||
{
|
||||
Dirty = true,
|
||||
});
|
||||
var server = pair.Server;
|
||||
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
|
||||
// Check that an empty role loadout spawns gear
|
||||
var stationSystem = entManager.System<StationSpawningSystem>();
|
||||
var testMap = await pair.CreateTestMap();
|
||||
|
||||
// That's right I can't even spawn a dummy profile without station spawning / humanoidappearance code crashing.
|
||||
var profile = new HumanoidCharacterProfile();
|
||||
|
||||
profile.SetLoadout(new RoleLoadout("TestRoleLoadout"));
|
||||
|
||||
stationSystem.SpawnPlayerMob(testMap.GridCoords, job: new JobComponent()
|
||||
{
|
||||
// Sue me, there's so much involved in setting up jobs
|
||||
Prototype = "CargoTechnician"
|
||||
}, profile, station: null);
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ using Content.Server.Database;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Robust.Shared.Configuration;
|
||||
@@ -53,8 +55,6 @@ namespace Content.IntegrationTests.Tests.Preferences
|
||||
Color.Beige,
|
||||
new ()
|
||||
),
|
||||
ClothingPreference.Jumpskirt,
|
||||
BackpackPreference.Backpack,
|
||||
SpawnPriorityPreference.None,
|
||||
new Dictionary<string, JobPriority>
|
||||
{
|
||||
@@ -62,7 +62,8 @@ namespace Content.IntegrationTests.Tests.Preferences
|
||||
},
|
||||
PreferenceUnavailableMode.StayInLobby,
|
||||
new List<string> (),
|
||||
new List<string>()
|
||||
new List<string>(),
|
||||
new Dictionary<string, RoleLoadout>()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
1838
Content.Server.Database/Migrations/Postgres/20240301130641_ClothingRemoval.Designer.cs
generated
Normal file
1838
Content.Server.Database/Migrations/Postgres/20240301130641_ClothingRemoval.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ClothingRemoval : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "backpack",
|
||||
table: "profile");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "clothing",
|
||||
table: "profile");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "backpack",
|
||||
table: "profile",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "clothing",
|
||||
table: "profile",
|
||||
type: "text",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
}
|
||||
}
|
||||
1884
Content.Server.Database/Migrations/Postgres/20240403072242_Loadouts.Designer.cs
generated
Normal file
1884
Content.Server.Database/Migrations/Postgres/20240403072242_Loadouts.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Postgres
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Loadouts : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "profile_role_loadout",
|
||||
columns: table => new
|
||||
{
|
||||
profile_role_loadout_id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
profile_id = table.Column<int>(type: "integer", nullable: false),
|
||||
role_name = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_profile_role_loadout", x => x.profile_role_loadout_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_profile_role_loadout_profile_profile_id",
|
||||
column: x => x.profile_id,
|
||||
principalTable: "profile",
|
||||
principalColumn: "profile_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "profile_loadout_group",
|
||||
columns: table => new
|
||||
{
|
||||
profile_loadout_group_id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
profile_role_loadout_id = table.Column<int>(type: "integer", nullable: false),
|
||||
group_name = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_profile_loadout_group", x => x.profile_loadout_group_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_profile_loadout_group_profile_role_loadout_profile_role_loa~",
|
||||
column: x => x.profile_role_loadout_id,
|
||||
principalTable: "profile_role_loadout",
|
||||
principalColumn: "profile_role_loadout_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "profile_loadout",
|
||||
columns: table => new
|
||||
{
|
||||
profile_loadout_id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
profile_loadout_group_id = table.Column<int>(type: "integer", nullable: false),
|
||||
loadout_name = table.Column<string>(type: "text", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_profile_loadout", x => x.profile_loadout_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_profile_loadout_profile_loadout_group_profile_loadout_group~",
|
||||
column: x => x.profile_loadout_group_id,
|
||||
principalTable: "profile_loadout_group",
|
||||
principalColumn: "profile_loadout_group_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_profile_loadout_profile_loadout_group_id",
|
||||
table: "profile_loadout",
|
||||
column: "profile_loadout_group_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_profile_loadout_group_profile_role_loadout_id",
|
||||
table: "profile_loadout_group",
|
||||
column: "profile_role_loadout_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_profile_role_loadout_profile_id",
|
||||
table: "profile_role_loadout",
|
||||
column: "profile_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "profile_loadout");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "profile_loadout_group");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "profile_role_loadout");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -735,21 +735,11 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("age");
|
||||
|
||||
b.Property<string>("Backpack")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("backpack");
|
||||
|
||||
b.Property<string>("CharacterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("char_name");
|
||||
|
||||
b.Property<string>("Clothing")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("clothing");
|
||||
|
||||
b.Property<string>("EyeColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
@@ -832,6 +822,84 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.ToTable("profile", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("profile_loadout_id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("LoadoutName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("loadout_name");
|
||||
|
||||
b.Property<int>("ProfileLoadoutGroupId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("profile_loadout_group_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_profile_loadout");
|
||||
|
||||
b.HasIndex("ProfileLoadoutGroupId");
|
||||
|
||||
b.ToTable("profile_loadout", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("profile_loadout_group_id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<string>("GroupName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("group_name");
|
||||
|
||||
b.Property<int>("ProfileRoleLoadoutId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("profile_role_loadout_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_profile_loadout_group");
|
||||
|
||||
b.HasIndex("ProfileRoleLoadoutId");
|
||||
|
||||
b.ToTable("profile_loadout_group", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("profile_role_loadout_id");
|
||||
|
||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<int>("ProfileId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("profile_id");
|
||||
|
||||
b.Property<string>("RoleName")
|
||||
.IsRequired()
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("role_name");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_profile_role_loadout");
|
||||
|
||||
b.HasIndex("ProfileId");
|
||||
|
||||
b.ToTable("profile_role_loadout", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1519,6 +1587,42 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
b.Navigation("Preference");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.ProfileLoadoutGroup", "ProfileLoadoutGroup")
|
||||
.WithMany("Loadouts")
|
||||
.HasForeignKey("ProfileLoadoutGroupId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_profile_loadout_profile_loadout_group_profile_loadout_group~");
|
||||
|
||||
b.Navigation("ProfileLoadoutGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.ProfileRoleLoadout", "ProfileRoleLoadout")
|
||||
.WithMany("Groups")
|
||||
.HasForeignKey("ProfileRoleLoadoutId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_profile_loadout_group_profile_role_loadout_profile_role_loa~");
|
||||
|
||||
b.Navigation("ProfileRoleLoadout");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Profile", "Profile")
|
||||
.WithMany("Loadouts")
|
||||
.HasForeignKey("ProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_profile_role_loadout_profile_profile_id");
|
||||
|
||||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Server", "Server")
|
||||
@@ -1731,9 +1835,21 @@ namespace Content.Server.Database.Migrations.Postgres
|
||||
|
||||
b.Navigation("Jobs");
|
||||
|
||||
b.Navigation("Loadouts");
|
||||
|
||||
b.Navigation("Traits");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b =>
|
||||
{
|
||||
b.Navigation("Loadouts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b =>
|
||||
{
|
||||
b.Navigation("Groups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.Navigation("AdminLogs");
|
||||
|
||||
1765
Content.Server.Database/Migrations/Sqlite/20240301130602_ClothingRemoval.Designer.cs
generated
Normal file
1765
Content.Server.Database/Migrations/Sqlite/20240301130602_ClothingRemoval.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class ClothingRemoval : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "backpack",
|
||||
table: "profile");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "clothing",
|
||||
table: "profile");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "backpack",
|
||||
table: "profile",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "clothing",
|
||||
table: "profile",
|
||||
type: "TEXT",
|
||||
nullable: false,
|
||||
defaultValue: "");
|
||||
}
|
||||
}
|
||||
}
|
||||
1809
Content.Server.Database/Migrations/Sqlite/20240403072258_Loadouts.Designer.cs
generated
Normal file
1809
Content.Server.Database/Migrations/Sqlite/20240403072258_Loadouts.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,102 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Content.Server.Database.Migrations.Sqlite
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Loadouts : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "profile_role_loadout",
|
||||
columns: table => new
|
||||
{
|
||||
profile_role_loadout_id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
profile_id = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
role_name = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_profile_role_loadout", x => x.profile_role_loadout_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_profile_role_loadout_profile_profile_id",
|
||||
column: x => x.profile_id,
|
||||
principalTable: "profile",
|
||||
principalColumn: "profile_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "profile_loadout_group",
|
||||
columns: table => new
|
||||
{
|
||||
profile_loadout_group_id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
profile_role_loadout_id = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
group_name = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_profile_loadout_group", x => x.profile_loadout_group_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_profile_loadout_group_profile_role_loadout_profile_role_loadout_id",
|
||||
column: x => x.profile_role_loadout_id,
|
||||
principalTable: "profile_role_loadout",
|
||||
principalColumn: "profile_role_loadout_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "profile_loadout",
|
||||
columns: table => new
|
||||
{
|
||||
profile_loadout_id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
profile_loadout_group_id = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
loadout_name = table.Column<string>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_profile_loadout", x => x.profile_loadout_id);
|
||||
table.ForeignKey(
|
||||
name: "FK_profile_loadout_profile_loadout_group_profile_loadout_group_id",
|
||||
column: x => x.profile_loadout_group_id,
|
||||
principalTable: "profile_loadout_group",
|
||||
principalColumn: "profile_loadout_group_id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_profile_loadout_profile_loadout_group_id",
|
||||
table: "profile_loadout",
|
||||
column: "profile_loadout_group_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_profile_loadout_group_profile_role_loadout_id",
|
||||
table: "profile_loadout_group",
|
||||
column: "profile_role_loadout_id");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_profile_role_loadout_profile_id",
|
||||
table: "profile_role_loadout",
|
||||
column: "profile_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "profile_loadout");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "profile_loadout_group");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "profile_role_loadout");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -688,21 +688,11 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("age");
|
||||
|
||||
b.Property<string>("Backpack")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("backpack");
|
||||
|
||||
b.Property<string>("CharacterName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("char_name");
|
||||
|
||||
b.Property<string>("Clothing")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("clothing");
|
||||
|
||||
b.Property<string>("EyeColor")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
@@ -785,6 +775,78 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.ToTable("profile", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("profile_loadout_id");
|
||||
|
||||
b.Property<string>("LoadoutName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("loadout_name");
|
||||
|
||||
b.Property<int>("ProfileLoadoutGroupId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("profile_loadout_group_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_profile_loadout");
|
||||
|
||||
b.HasIndex("ProfileLoadoutGroupId");
|
||||
|
||||
b.ToTable("profile_loadout", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("profile_loadout_group_id");
|
||||
|
||||
b.Property<string>("GroupName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("group_name");
|
||||
|
||||
b.Property<int>("ProfileRoleLoadoutId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("profile_role_loadout_id");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_profile_loadout_group");
|
||||
|
||||
b.HasIndex("ProfileRoleLoadoutId");
|
||||
|
||||
b.ToTable("profile_loadout_group", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("profile_role_loadout_id");
|
||||
|
||||
b.Property<int>("ProfileId")
|
||||
.HasColumnType("INTEGER")
|
||||
.HasColumnName("profile_id");
|
||||
|
||||
b.Property<string>("RoleName")
|
||||
.IsRequired()
|
||||
.HasColumnType("TEXT")
|
||||
.HasColumnName("role_name");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PK_profile_role_loadout");
|
||||
|
||||
b.HasIndex("ProfileId");
|
||||
|
||||
b.ToTable("profile_role_loadout", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
@@ -1450,6 +1512,42 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
b.Navigation("Preference");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileLoadout", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.ProfileLoadoutGroup", "ProfileLoadoutGroup")
|
||||
.WithMany("Loadouts")
|
||||
.HasForeignKey("ProfileLoadoutGroupId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_profile_loadout_profile_loadout_group_profile_loadout_group_id");
|
||||
|
||||
b.Navigation("ProfileLoadoutGroup");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.ProfileRoleLoadout", "ProfileRoleLoadout")
|
||||
.WithMany("Groups")
|
||||
.HasForeignKey("ProfileRoleLoadoutId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_profile_loadout_group_profile_role_loadout_profile_role_loadout_id");
|
||||
|
||||
b.Navigation("ProfileRoleLoadout");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Profile", "Profile")
|
||||
.WithMany("Loadouts")
|
||||
.HasForeignKey("ProfileId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired()
|
||||
.HasConstraintName("FK_profile_role_loadout_profile_profile_id");
|
||||
|
||||
b.Navigation("Profile");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.HasOne("Content.Server.Database.Server", "Server")
|
||||
@@ -1662,9 +1760,21 @@ namespace Content.Server.Database.Migrations.Sqlite
|
||||
|
||||
b.Navigation("Jobs");
|
||||
|
||||
b.Navigation("Loadouts");
|
||||
|
||||
b.Navigation("Traits");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileLoadoutGroup", b =>
|
||||
{
|
||||
b.Navigation("Loadouts");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.ProfileRoleLoadout", b =>
|
||||
{
|
||||
b.Navigation("Groups");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Content.Server.Database.Round", b =>
|
||||
{
|
||||
b.Navigation("AdminLogs");
|
||||
|
||||
@@ -59,6 +59,24 @@ namespace Content.Server.Database
|
||||
.HasIndex(p => new {HumanoidProfileId = p.ProfileId, p.TraitName})
|
||||
.IsUnique();
|
||||
|
||||
modelBuilder.Entity<ProfileRoleLoadout>()
|
||||
.HasOne(e => e.Profile)
|
||||
.WithMany(e => e.Loadouts)
|
||||
.HasForeignKey(e => e.ProfileId)
|
||||
.IsRequired();
|
||||
|
||||
modelBuilder.Entity<ProfileLoadoutGroup>()
|
||||
.HasOne(e => e.ProfileRoleLoadout)
|
||||
.WithMany(e => e.Groups)
|
||||
.HasForeignKey(e => e.ProfileRoleLoadoutId)
|
||||
.IsRequired();
|
||||
|
||||
modelBuilder.Entity<ProfileLoadout>()
|
||||
.HasOne(e => e.ProfileLoadoutGroup)
|
||||
.WithMany(e => e.Loadouts)
|
||||
.HasForeignKey(e => e.ProfileLoadoutGroupId)
|
||||
.IsRequired();
|
||||
|
||||
modelBuilder.Entity<Job>()
|
||||
.HasIndex(j => j.ProfileId);
|
||||
|
||||
@@ -337,13 +355,13 @@ namespace Content.Server.Database
|
||||
public string FacialHairColor { get; set; } = null!;
|
||||
public string EyeColor { get; set; } = null!;
|
||||
public string SkinColor { get; set; } = null!;
|
||||
public string Clothing { get; set; } = null!;
|
||||
public string Backpack { get; set; } = null!;
|
||||
public int SpawnPriority { get; set; } = 0;
|
||||
public List<Job> Jobs { get; } = new();
|
||||
public List<Antag> Antags { get; } = new();
|
||||
public List<Trait> Traits { get; } = new();
|
||||
|
||||
public List<ProfileRoleLoadout> Loadouts { get; } = new();
|
||||
|
||||
[Column("pref_unavailable")] public DbPreferenceUnavailableMode PreferenceUnavailable { get; set; }
|
||||
|
||||
public int PreferenceId { get; set; }
|
||||
@@ -387,6 +405,79 @@ namespace Content.Server.Database
|
||||
public string TraitName { get; set; } = null!;
|
||||
}
|
||||
|
||||
#region Loadouts
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to a single role's loadout inside the DB.
|
||||
/// </summary>
|
||||
public class ProfileRoleLoadout
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int ProfileId { get; set; }
|
||||
|
||||
public Profile Profile { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The corresponding role prototype on the profile.
|
||||
/// </summary>
|
||||
public string RoleName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Store the saved loadout groups. These may get validated and removed when loaded at runtime.
|
||||
/// </summary>
|
||||
public List<ProfileLoadoutGroup> Groups { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to a loadout group prototype with the specified loadouts attached.
|
||||
/// </summary>
|
||||
public class ProfileLoadoutGroup
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int ProfileRoleLoadoutId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The corresponding RoleLoadout that owns this.
|
||||
/// </summary>
|
||||
public ProfileRoleLoadout ProfileRoleLoadout { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The corresponding group prototype.
|
||||
/// </summary>
|
||||
public string GroupName { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Selected loadout prototype. Null if none is set.
|
||||
/// May get validated at runtime and updated to to the default.
|
||||
/// </summary>
|
||||
public List<ProfileLoadout> Loadouts { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to a selected loadout.
|
||||
/// </summary>
|
||||
public class ProfileLoadout
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int ProfileLoadoutGroupId { get; set; }
|
||||
|
||||
public ProfileLoadoutGroup ProfileLoadoutGroup { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Corresponding loadout prototype.
|
||||
/// </summary>
|
||||
public string LoadoutName { get; set; } = string.Empty;
|
||||
|
||||
/*
|
||||
* Insert extra data here like custom descriptions or colors or whatever.
|
||||
*/
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public enum DbPreferenceUnavailableMode
|
||||
{
|
||||
// These enum values HAVE to match the ones in PreferenceUnavailableMode in Shared.
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace Content.Server.Administration.Commands
|
||||
foreach (var slot in slots)
|
||||
{
|
||||
invSystem.TryUnequip(target, slot.Name, true, true, false, inventoryComponent);
|
||||
var gearStr = startingGear.GetGear(slot.Name, profile);
|
||||
var gearStr = startingGear.GetGear(slot.Name);
|
||||
if (gearStr == string.Empty)
|
||||
{
|
||||
continue;
|
||||
|
||||
@@ -13,6 +13,8 @@ using Content.Shared.Database;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Network;
|
||||
@@ -40,6 +42,10 @@ namespace Content.Server.Database
|
||||
.Include(p => p.Profiles).ThenInclude(h => h.Jobs)
|
||||
.Include(p => p.Profiles).ThenInclude(h => h.Antags)
|
||||
.Include(p => p.Profiles).ThenInclude(h => h.Traits)
|
||||
.Include(p => p.Profiles)
|
||||
.ThenInclude(h => h.Loadouts)
|
||||
.ThenInclude(l => l.Groups)
|
||||
.ThenInclude(group => group.Loadouts)
|
||||
.AsSingleQuery()
|
||||
.SingleOrDefaultAsync(p => p.UserId == userId.UserId);
|
||||
|
||||
@@ -88,6 +94,9 @@ namespace Content.Server.Database
|
||||
.Include(p => p.Jobs)
|
||||
.Include(p => p.Antags)
|
||||
.Include(p => p.Traits)
|
||||
.Include(p => p.Loadouts)
|
||||
.ThenInclude(l => l.Groups)
|
||||
.ThenInclude(group => group.Loadouts)
|
||||
.AsSplitQuery()
|
||||
.SingleOrDefault(h => h.Slot == slot);
|
||||
|
||||
@@ -179,14 +188,6 @@ namespace Content.Server.Database
|
||||
if (Enum.TryParse<Sex>(profile.Sex, true, out var sexVal))
|
||||
sex = sexVal;
|
||||
|
||||
var clothing = ClothingPreference.Jumpsuit;
|
||||
if (Enum.TryParse<ClothingPreference>(profile.Clothing, true, out var clothingVal))
|
||||
clothing = clothingVal;
|
||||
|
||||
var backpack = BackpackPreference.Backpack;
|
||||
if (Enum.TryParse<BackpackPreference>(profile.Backpack, true, out var backpackVal))
|
||||
backpack = backpackVal;
|
||||
|
||||
var spawnPriority = (SpawnPriorityPreference) profile.SpawnPriority;
|
||||
|
||||
var gender = sex == Sex.Male ? Gender.Male : Gender.Female;
|
||||
@@ -209,6 +210,27 @@ namespace Content.Server.Database
|
||||
}
|
||||
}
|
||||
|
||||
var loadouts = new Dictionary<string, RoleLoadout>();
|
||||
|
||||
foreach (var role in profile.Loadouts)
|
||||
{
|
||||
var loadout = new RoleLoadout(role.RoleName);
|
||||
|
||||
foreach (var group in role.Groups)
|
||||
{
|
||||
var groupLoadouts = loadout.SelectedLoadouts.GetOrNew(group.GroupName);
|
||||
foreach (var profLoadout in group.Loadouts)
|
||||
{
|
||||
groupLoadouts.Add(new Loadout()
|
||||
{
|
||||
Prototype = profLoadout.LoadoutName,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
loadouts[role.RoleName] = loadout;
|
||||
}
|
||||
|
||||
return new HumanoidCharacterProfile(
|
||||
profile.CharacterName,
|
||||
profile.FlavorText,
|
||||
@@ -226,13 +248,12 @@ namespace Content.Server.Database
|
||||
Color.FromHex(profile.SkinColor),
|
||||
markings
|
||||
),
|
||||
clothing,
|
||||
backpack,
|
||||
spawnPriority,
|
||||
jobs,
|
||||
(PreferenceUnavailableMode) profile.PreferenceUnavailable,
|
||||
antags.ToList(),
|
||||
traits.ToList()
|
||||
traits.ToList(),
|
||||
loadouts
|
||||
);
|
||||
}
|
||||
|
||||
@@ -259,8 +280,6 @@ namespace Content.Server.Database
|
||||
profile.FacialHairColor = appearance.FacialHairColor.ToHex();
|
||||
profile.EyeColor = appearance.EyeColor.ToHex();
|
||||
profile.SkinColor = appearance.SkinColor.ToHex();
|
||||
profile.Clothing = humanoid.Clothing.ToString();
|
||||
profile.Backpack = humanoid.Backpack.ToString();
|
||||
profile.SpawnPriority = (int) humanoid.SpawnPriority;
|
||||
profile.Markings = markings;
|
||||
profile.Slot = slot;
|
||||
@@ -285,6 +304,36 @@ namespace Content.Server.Database
|
||||
.Select(t => new Trait {TraitName = t})
|
||||
);
|
||||
|
||||
profile.Loadouts.Clear();
|
||||
|
||||
foreach (var (role, loadouts) in humanoid.Loadouts)
|
||||
{
|
||||
var dz = new ProfileRoleLoadout()
|
||||
{
|
||||
RoleName = role,
|
||||
};
|
||||
|
||||
foreach (var (group, groupLoadouts) in loadouts.SelectedLoadouts)
|
||||
{
|
||||
var profileGroup = new ProfileLoadoutGroup()
|
||||
{
|
||||
GroupName = group,
|
||||
};
|
||||
|
||||
foreach (var loadout in groupLoadouts)
|
||||
{
|
||||
profileGroup.Loadouts.Add(new ProfileLoadout()
|
||||
{
|
||||
LoadoutName = loadout.Prototype,
|
||||
});
|
||||
}
|
||||
|
||||
dz.Groups.Add(profileGroup);
|
||||
}
|
||||
|
||||
profile.Loadouts.Add(dz);
|
||||
}
|
||||
|
||||
return profile;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -709,7 +709,7 @@ public sealed class NukeopsRuleSystem : GameRuleSystem<NukeopsRuleComponent>
|
||||
_humanoid.LoadProfile(mob, profile);
|
||||
|
||||
var gear = _prototypeManager.Index(spawnDetails.GearProto);
|
||||
_stationSpawning.EquipStartingGear(mob, gear, profile);
|
||||
_stationSpawning.EquipStartingGear(mob, gear);
|
||||
|
||||
_npcFaction.RemoveFaction(mob, "NanoTrasen", false);
|
||||
_npcFaction.AddFaction(mob, "Syndicate");
|
||||
|
||||
@@ -249,7 +249,7 @@ public sealed class PiratesRuleSystem : GameRuleSystem<PiratesRuleComponent>
|
||||
|
||||
_mindSystem.TransferTo(newMind, mob);
|
||||
var profile = _prefs.GetPreferences(session.UserId).SelectedCharacter as HumanoidCharacterProfile;
|
||||
_stationSpawningSystem.EquipStartingGear(mob, pirateGear, profile);
|
||||
_stationSpawningSystem.EquipStartingGear(mob, pirateGear);
|
||||
|
||||
_npcFaction.RemoveFaction(mob, EnemyFactionId, false);
|
||||
_npcFaction.AddFaction(mob, PirateFactionId);
|
||||
|
||||
@@ -22,6 +22,7 @@ using Content.Server.Worldgen.Tools;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Kitchen;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
|
||||
namespace Content.Server.IoC
|
||||
{
|
||||
@@ -58,6 +59,7 @@ namespace Content.Server.IoC
|
||||
IoCManager.Register<PoissonDiskSampler>();
|
||||
IoCManager.Register<DiscordWebhook>();
|
||||
IoCManager.Register<ServerDbEntryManager>();
|
||||
IoCManager.Register<ISharedPlaytimeManager, PlayTimeTrackingManager>();
|
||||
IoCManager.Register<ServerApi>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public delegate void CalcPlayTimeTrackersCallback(ICommonSession player, HashSet
|
||||
/// Operations like refreshing and sending play time info to clients are deferred until the next frame (note: not tick).
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public sealed class PlayTimeTrackingManager
|
||||
public sealed class PlayTimeTrackingManager : ISharedPlaytimeManager
|
||||
{
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly IServerNetManager _net = default!;
|
||||
@@ -201,6 +201,11 @@ public sealed class PlayTimeTrackingManager
|
||||
}
|
||||
}
|
||||
|
||||
public IReadOnlyDictionary<string, TimeSpan> GetPlayTimes(ICommonSession session)
|
||||
{
|
||||
return GetTrackerTimes(session);
|
||||
}
|
||||
|
||||
private void SendPlayTimes(ICommonSession pSession)
|
||||
{
|
||||
var roles = GetTrackerTimes(pSession);
|
||||
|
||||
@@ -8,6 +8,7 @@ using Content.Shared.CCVar;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
@@ -25,6 +26,7 @@ namespace Content.Server.Preferences.Managers
|
||||
[Dependency] private readonly IServerNetManager _netManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protos = default!;
|
||||
|
||||
// Cache player prefs on the server so we don't need as much async hell related to them.
|
||||
@@ -98,8 +100,10 @@ namespace Content.Server.Preferences.Managers
|
||||
}
|
||||
|
||||
var curPrefs = prefsData.Prefs!;
|
||||
var session = _playerManager.GetSessionById(userId);
|
||||
var collection = IoCManager.Instance!;
|
||||
|
||||
profile.EnsureValid(_cfg, _protos);
|
||||
profile.EnsureValid(session, collection);
|
||||
|
||||
var profiles = new Dictionary<int, ICharacterProfile>(curPrefs.Characters)
|
||||
{
|
||||
@@ -260,17 +264,20 @@ namespace Content.Server.Preferences.Managers
|
||||
return await _db.InitPrefsAsync(userId, HumanoidCharacterProfile.Random());
|
||||
}
|
||||
|
||||
return SanitizePreferences(prefs);
|
||||
var session = _playerManager.GetSessionById(userId);
|
||||
var collection = IoCManager.Instance!;
|
||||
|
||||
return SanitizePreferences(session, prefs, collection);
|
||||
}
|
||||
|
||||
private PlayerPreferences SanitizePreferences(PlayerPreferences prefs)
|
||||
private PlayerPreferences SanitizePreferences(ICommonSession session, PlayerPreferences prefs, IDependencyCollection collection)
|
||||
{
|
||||
// Clean up preferences in case of changes to the game,
|
||||
// such as removed jobs still being selected.
|
||||
|
||||
return new PlayerPreferences(prefs.Characters.Select(p =>
|
||||
{
|
||||
return new KeyValuePair<int, ICharacterProfile>(p.Key, p.Value.Validated(_cfg, _protos));
|
||||
return new KeyValuePair<int, ICharacterProfile>(p.Key, p.Value.Validated(session, collection));
|
||||
}), prefs.SelectedCharacterIndex, prefs.AdminOOCColor);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ public sealed class SpawnPointSystem : EntitySystem
|
||||
// TODO: Refactor gameticker spawning code so we don't have to do this!
|
||||
var points2 = EntityQueryEnumerator<SpawnPointComponent, TransformComponent>();
|
||||
|
||||
if (points2.MoveNext(out var uid, out var spawnPoint, out var xform))
|
||||
if (points2.MoveNext(out var spawnPoint, out var xform))
|
||||
{
|
||||
possiblePositions.Add(xform.Coordinates);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Access.Systems;
|
||||
using Content.Server.DetailExaminable;
|
||||
using Content.Server.Humanoid;
|
||||
@@ -10,10 +11,12 @@ using Content.Server.Station.Components;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Random;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Content.Shared.Roles;
|
||||
@@ -86,7 +89,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
||||
|
||||
if (station != null && profile != null)
|
||||
{
|
||||
/// Try to call the character's preferred spawner first.
|
||||
// Try to call the character's preferred spawner first.
|
||||
if (_spawnerCallbacks.TryGetValue(profile.SpawnPriority, out var preferredSpawner))
|
||||
{
|
||||
preferredSpawner(ev);
|
||||
@@ -101,11 +104,13 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Call all of them in the typical order.
|
||||
// Call all of them in the typical order.
|
||||
foreach (var typicalSpawner in _spawnerCallbacks.Values)
|
||||
{
|
||||
typicalSpawner(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RaiseLocalEvent(ev);
|
||||
|
||||
@@ -134,7 +139,7 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
||||
EntityUid? station,
|
||||
EntityUid? entity = null)
|
||||
{
|
||||
_prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out JobPrototype? prototype);
|
||||
_prototypeManager.TryIndex(job?.Prototype ?? string.Empty, out var prototype);
|
||||
|
||||
// If we're not spawning a humanoid, we're gonna exit early without doing all the humanoid stuff.
|
||||
if (prototype?.JobEntity != null)
|
||||
@@ -176,11 +181,38 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
||||
if (prototype?.StartingGear != null)
|
||||
{
|
||||
var startingGear = _prototypeManager.Index<StartingGearPrototype>(prototype.StartingGear);
|
||||
EquipStartingGear(entity.Value, startingGear, profile);
|
||||
EquipStartingGear(entity.Value, startingGear);
|
||||
if (profile != null)
|
||||
EquipIdCard(entity.Value, profile.Name, prototype, station);
|
||||
}
|
||||
|
||||
// Run loadouts after so stuff like storage loadouts can get
|
||||
var jobLoadout = LoadoutSystem.GetJobPrototype(prototype?.ID);
|
||||
|
||||
if (_prototypeManager.TryIndex(jobLoadout, out RoleLoadoutPrototype? loadoutProto))
|
||||
{
|
||||
RoleLoadout? loadout = null;
|
||||
profile?.Loadouts.TryGetValue(jobLoadout, out loadout);
|
||||
|
||||
// Set to default if not present
|
||||
if (loadout == null)
|
||||
{
|
||||
loadout = new RoleLoadout(jobLoadout);
|
||||
loadout.SetDefault(_prototypeManager);
|
||||
}
|
||||
|
||||
// Order loadout selections by the order they appear on the prototype.
|
||||
foreach (var group in loadout.SelectedLoadouts.OrderBy(x => loadoutProto.Groups.FindIndex(e => e == x.Key)))
|
||||
{
|
||||
foreach (var items in group.Value)
|
||||
{
|
||||
// Handle any extra data here.
|
||||
var startingGear = _prototypeManager.Index<StartingGearPrototype>(items.Prototype);
|
||||
EquipStartingGear(entity.Value, startingGear);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (profile != null)
|
||||
{
|
||||
_humanoidSystem.LoadProfile(entity.Value, profile);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Clothing.Components;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Station;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -24,12 +26,94 @@ public sealed class LoadoutSystem : EntitySystem
|
||||
SubscribeLocalEvent<LoadoutComponent, MapInitEvent>(OnMapInit);
|
||||
}
|
||||
|
||||
public static string GetJobPrototype(string? loadout)
|
||||
{
|
||||
if (string.IsNullOrEmpty(loadout))
|
||||
return string.Empty;
|
||||
|
||||
return "Job" + loadout;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the first entity prototype for operations such as sprite drawing.
|
||||
/// </summary>
|
||||
public EntProtoId? GetFirstOrNull(LoadoutPrototype loadout)
|
||||
{
|
||||
if (!_protoMan.TryIndex(loadout.Equipment, out var gear))
|
||||
return null;
|
||||
|
||||
var count = gear.Equipment.Count + gear.Inhand.Count + gear.Storage.Values.Sum(x => x.Count);
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
if (gear.Equipment.Count == 1 && _protoMan.TryIndex<EntityPrototype>(gear.Equipment.Values.First(), out var proto))
|
||||
{
|
||||
return proto.ID;
|
||||
}
|
||||
|
||||
if (gear.Inhand.Count == 1 && _protoMan.TryIndex<EntityPrototype>(gear.Inhand[0], out proto))
|
||||
{
|
||||
return proto.ID;
|
||||
}
|
||||
|
||||
// Storage moment
|
||||
foreach (var ents in gear.Storage.Values)
|
||||
{
|
||||
foreach (var ent in ents)
|
||||
{
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the name of a loadout.
|
||||
/// </summary>
|
||||
public string GetName(LoadoutPrototype loadout)
|
||||
{
|
||||
if (!_protoMan.TryIndex(loadout.Equipment, out var gear))
|
||||
return Loc.GetString("loadout-unknown");
|
||||
|
||||
var count = gear.Equipment.Count + gear.Storage.Values.Sum(o => o.Count) + gear.Inhand.Count;
|
||||
|
||||
if (count == 1)
|
||||
{
|
||||
if (gear.Equipment.Count == 1 && _protoMan.TryIndex<EntityPrototype>(gear.Equipment.Values.First(), out var proto))
|
||||
{
|
||||
return proto.Name;
|
||||
}
|
||||
|
||||
if (gear.Inhand.Count == 1 && _protoMan.TryIndex<EntityPrototype>(gear.Inhand[0], out proto))
|
||||
{
|
||||
return proto.Name;
|
||||
}
|
||||
|
||||
foreach (var values in gear.Storage.Values)
|
||||
{
|
||||
if (values.Count != 1)
|
||||
continue;
|
||||
|
||||
if (_protoMan.TryIndex<EntityPrototype>(values[0], out proto))
|
||||
{
|
||||
return proto.Name;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Loc.GetString($"loadout-{loadout.ID}");
|
||||
}
|
||||
|
||||
private void OnMapInit(EntityUid uid, LoadoutComponent component, MapInitEvent args)
|
||||
{
|
||||
if (component.Prototypes == null)
|
||||
return;
|
||||
|
||||
var proto = _protoMan.Index<StartingGearPrototype>(_random.Pick(component.Prototypes));
|
||||
_station.EquipStartingGear(uid, proto, null);
|
||||
_station.EquipStartingGear(uid, proto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,14 +66,14 @@ public sealed partial class SpeciesPrototype : IPrototype
|
||||
/// <summary>
|
||||
/// Humanoid species variant used by this entity.
|
||||
/// </summary>
|
||||
[DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string Prototype { get; private set; } = default!;
|
||||
[DataField(required: true)]
|
||||
public EntProtoId Prototype { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Prototype used by the species for the dress-up doll in various menus.
|
||||
/// </summary>
|
||||
[DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||
public string DollPrototype { get; private set; } = default!;
|
||||
[DataField(required: true)]
|
||||
public EntProtoId DollPrototype { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Method of skin coloration used by the species.
|
||||
@@ -120,12 +120,6 @@ public sealed partial class SpeciesPrototype : IPrototype
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MaxAge = 120;
|
||||
|
||||
/// <summary>
|
||||
/// The Style used for the guidebook info link in the character profile editor
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string GuideBookIcon = "SpeciesInfoDefault";
|
||||
}
|
||||
|
||||
public enum SpeciesNaming : byte
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Shared.Players.PlayTimeTracking;
|
||||
|
||||
public interface ISharedPlaytimeManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the playtimes for the session or an empty dictionary if none found.
|
||||
/// </summary>
|
||||
IReadOnlyDictionary<string, TimeSpan> GetPlayTimes(ICommonSession session);
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
namespace Content.Shared.Preferences
|
||||
{
|
||||
/// <summary>
|
||||
/// The backpack preference for a profile. Stored in database!
|
||||
/// </summary>
|
||||
public enum BackpackPreference
|
||||
{
|
||||
Backpack,
|
||||
Satchel,
|
||||
Duffelbag
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace Content.Shared.Preferences
|
||||
{
|
||||
/// <summary>
|
||||
/// The clothing preference for a profile. Stored in database!
|
||||
/// </summary>
|
||||
public enum ClothingPreference
|
||||
{
|
||||
Jumpsuit,
|
||||
Jumpskirt
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
using System.Linq;
|
||||
using System.Globalization;
|
||||
using System.Text.RegularExpressions;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.Random.Helpers;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Traits;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -31,6 +33,11 @@ namespace Content.Shared.Preferences
|
||||
private readonly List<string> _antagPreferences;
|
||||
private readonly List<string> _traitPreferences;
|
||||
|
||||
public IReadOnlyDictionary<string, RoleLoadout> Loadouts => _loadouts;
|
||||
|
||||
private Dictionary<string, RoleLoadout> _loadouts;
|
||||
|
||||
// What in the lord is happening here.
|
||||
private HumanoidCharacterProfile(
|
||||
string name,
|
||||
string flavortext,
|
||||
@@ -39,13 +46,12 @@ namespace Content.Shared.Preferences
|
||||
Sex sex,
|
||||
Gender gender,
|
||||
HumanoidCharacterAppearance appearance,
|
||||
ClothingPreference clothing,
|
||||
BackpackPreference backpack,
|
||||
SpawnPriorityPreference spawnPriority,
|
||||
Dictionary<string, JobPriority> jobPriorities,
|
||||
PreferenceUnavailableMode preferenceUnavailable,
|
||||
List<string> antagPreferences,
|
||||
List<string> traitPreferences)
|
||||
List<string> traitPreferences,
|
||||
Dictionary<string, RoleLoadout> loadouts)
|
||||
{
|
||||
Name = name;
|
||||
FlavorText = flavortext;
|
||||
@@ -54,13 +60,12 @@ namespace Content.Shared.Preferences
|
||||
Sex = sex;
|
||||
Gender = gender;
|
||||
Appearance = appearance;
|
||||
Clothing = clothing;
|
||||
Backpack = backpack;
|
||||
SpawnPriority = spawnPriority;
|
||||
_jobPriorities = jobPriorities;
|
||||
PreferenceUnavailable = preferenceUnavailable;
|
||||
_antagPreferences = antagPreferences;
|
||||
_traitPreferences = traitPreferences;
|
||||
_loadouts = loadouts;
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor but with overridable references (to prevent useless copies)</summary>
|
||||
@@ -68,15 +73,16 @@ namespace Content.Shared.Preferences
|
||||
HumanoidCharacterProfile other,
|
||||
Dictionary<string, JobPriority> jobPriorities,
|
||||
List<string> antagPreferences,
|
||||
List<string> traitPreferences)
|
||||
: this(other.Name, other.FlavorText, other.Species, other.Age, other.Sex, other.Gender, other.Appearance, other.Clothing, other.Backpack, other.SpawnPriority,
|
||||
jobPriorities, other.PreferenceUnavailable, antagPreferences, traitPreferences)
|
||||
List<string> traitPreferences,
|
||||
Dictionary<string, RoleLoadout> loadouts)
|
||||
: this(other.Name, other.FlavorText, other.Species, other.Age, other.Sex, other.Gender, other.Appearance, other.SpawnPriority,
|
||||
jobPriorities, other.PreferenceUnavailable, antagPreferences, traitPreferences, loadouts)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Copy constructor</summary>
|
||||
private HumanoidCharacterProfile(HumanoidCharacterProfile other)
|
||||
: this(other, new Dictionary<string, JobPriority>(other.JobPriorities), new List<string>(other.AntagPreferences), new List<string>(other.TraitPreferences))
|
||||
: this(other, new Dictionary<string, JobPriority>(other.JobPriorities), new List<string>(other.AntagPreferences), new List<string>(other.TraitPreferences), new Dictionary<string, RoleLoadout>(other.Loadouts))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -88,15 +94,14 @@ namespace Content.Shared.Preferences
|
||||
Sex sex,
|
||||
Gender gender,
|
||||
HumanoidCharacterAppearance appearance,
|
||||
ClothingPreference clothing,
|
||||
BackpackPreference backpack,
|
||||
SpawnPriorityPreference spawnPriority,
|
||||
IReadOnlyDictionary<string, JobPriority> jobPriorities,
|
||||
PreferenceUnavailableMode preferenceUnavailable,
|
||||
IReadOnlyList<string> antagPreferences,
|
||||
IReadOnlyList<string> traitPreferences)
|
||||
: this(name, flavortext, species, age, sex, gender, appearance, clothing, backpack, spawnPriority, new Dictionary<string, JobPriority>(jobPriorities),
|
||||
preferenceUnavailable, new List<string>(antagPreferences), new List<string>(traitPreferences))
|
||||
IReadOnlyList<string> traitPreferences,
|
||||
Dictionary<string, RoleLoadout> loadouts)
|
||||
: this(name, flavortext, species, age, sex, gender, appearance, spawnPriority, new Dictionary<string, JobPriority>(jobPriorities),
|
||||
preferenceUnavailable, new List<string>(antagPreferences), new List<string>(traitPreferences), new Dictionary<string, RoleLoadout>(loadouts))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -113,8 +118,6 @@ namespace Content.Shared.Preferences
|
||||
Sex.Male,
|
||||
Gender.Male,
|
||||
new HumanoidCharacterAppearance(),
|
||||
ClothingPreference.Jumpsuit,
|
||||
BackpackPreference.Backpack,
|
||||
SpawnPriorityPreference.None,
|
||||
new Dictionary<string, JobPriority>
|
||||
{
|
||||
@@ -122,7 +125,8 @@ namespace Content.Shared.Preferences
|
||||
},
|
||||
PreferenceUnavailableMode.SpawnAsOverflow,
|
||||
new List<string>(),
|
||||
new List<string>())
|
||||
new List<string>(),
|
||||
new Dictionary<string, RoleLoadout>())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -141,8 +145,6 @@ namespace Content.Shared.Preferences
|
||||
Sex.Male,
|
||||
Gender.Male,
|
||||
HumanoidCharacterAppearance.DefaultWithSpecies(species),
|
||||
ClothingPreference.Jumpsuit,
|
||||
BackpackPreference.Backpack,
|
||||
SpawnPriorityPreference.None,
|
||||
new Dictionary<string, JobPriority>
|
||||
{
|
||||
@@ -150,7 +152,8 @@ namespace Content.Shared.Preferences
|
||||
},
|
||||
PreferenceUnavailableMode.SpawnAsOverflow,
|
||||
new List<string>(),
|
||||
new List<string>());
|
||||
new List<string>(),
|
||||
new Dictionary<string, RoleLoadout>());
|
||||
}
|
||||
|
||||
// TODO: This should eventually not be a visual change only.
|
||||
@@ -195,11 +198,11 @@ namespace Content.Shared.Preferences
|
||||
|
||||
var name = GetName(species, gender);
|
||||
|
||||
return new HumanoidCharacterProfile(name, "", species, age, sex, gender, HumanoidCharacterAppearance.Random(species, sex), ClothingPreference.Jumpsuit, BackpackPreference.Backpack, SpawnPriorityPreference.None,
|
||||
return new HumanoidCharacterProfile(name, "", species, age, sex, gender, HumanoidCharacterAppearance.Random(species, sex), SpawnPriorityPreference.None,
|
||||
new Dictionary<string, JobPriority>
|
||||
{
|
||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High},
|
||||
}, PreferenceUnavailableMode.StayInLobby, new List<string>(), new List<string>());
|
||||
}, PreferenceUnavailableMode.StayInLobby, new List<string>(), new List<string>(), new Dictionary<string, RoleLoadout>());
|
||||
}
|
||||
|
||||
public string Name { get; private set; }
|
||||
@@ -219,8 +222,6 @@ namespace Content.Shared.Preferences
|
||||
|
||||
[DataField("appearance")]
|
||||
public HumanoidCharacterAppearance Appearance { get; private set; }
|
||||
public ClothingPreference Clothing { get; private set; }
|
||||
public BackpackPreference Backpack { get; private set; }
|
||||
public SpawnPriorityPreference SpawnPriority { get; private set; }
|
||||
public IReadOnlyDictionary<string, JobPriority> JobPriorities => _jobPriorities;
|
||||
public IReadOnlyList<string> AntagPreferences => _antagPreferences;
|
||||
@@ -263,21 +264,14 @@ namespace Content.Shared.Preferences
|
||||
return new(this) { Appearance = appearance };
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithClothingPreference(ClothingPreference clothing)
|
||||
{
|
||||
return new(this) { Clothing = clothing };
|
||||
}
|
||||
public HumanoidCharacterProfile WithBackpackPreference(BackpackPreference backpack)
|
||||
{
|
||||
return new(this) { Backpack = backpack };
|
||||
}
|
||||
public HumanoidCharacterProfile WithSpawnPriorityPreference(SpawnPriorityPreference spawnPriority)
|
||||
{
|
||||
return new(this) { SpawnPriority = spawnPriority };
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithJobPriorities(IEnumerable<KeyValuePair<string, JobPriority>> jobPriorities)
|
||||
{
|
||||
return new(this, new Dictionary<string, JobPriority>(jobPriorities), _antagPreferences, _traitPreferences);
|
||||
return new(this, new Dictionary<string, JobPriority>(jobPriorities), _antagPreferences, _traitPreferences, _loadouts);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithJobPriority(string jobId, JobPriority priority)
|
||||
@@ -291,7 +285,7 @@ namespace Content.Shared.Preferences
|
||||
{
|
||||
dictionary[jobId] = priority;
|
||||
}
|
||||
return new(this, dictionary, _antagPreferences, _traitPreferences);
|
||||
return new(this, dictionary, _antagPreferences, _traitPreferences, _loadouts);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithPreferenceUnavailable(PreferenceUnavailableMode mode)
|
||||
@@ -301,7 +295,7 @@ namespace Content.Shared.Preferences
|
||||
|
||||
public HumanoidCharacterProfile WithAntagPreferences(IEnumerable<string> antagPreferences)
|
||||
{
|
||||
return new(this, _jobPriorities, new List<string>(antagPreferences), _traitPreferences);
|
||||
return new(this, _jobPriorities, new List<string>(antagPreferences), _traitPreferences, _loadouts);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithAntagPreference(string antagId, bool pref)
|
||||
@@ -321,7 +315,7 @@ namespace Content.Shared.Preferences
|
||||
list.Remove(antagId);
|
||||
}
|
||||
}
|
||||
return new(this, _jobPriorities, list, _traitPreferences);
|
||||
return new(this, _jobPriorities, list, _traitPreferences, _loadouts);
|
||||
}
|
||||
|
||||
public HumanoidCharacterProfile WithTraitPreference(string traitId, bool pref)
|
||||
@@ -343,7 +337,7 @@ namespace Content.Shared.Preferences
|
||||
list.Remove(traitId);
|
||||
}
|
||||
}
|
||||
return new(this, _jobPriorities, _antagPreferences, list);
|
||||
return new(this, _jobPriorities, _antagPreferences, list, _loadouts);
|
||||
}
|
||||
|
||||
public string Summary =>
|
||||
@@ -362,17 +356,19 @@ namespace Content.Shared.Preferences
|
||||
if (Sex != other.Sex) return false;
|
||||
if (Gender != other.Gender) return false;
|
||||
if (PreferenceUnavailable != other.PreferenceUnavailable) return false;
|
||||
if (Clothing != other.Clothing) return false;
|
||||
if (Backpack != other.Backpack) return false;
|
||||
if (SpawnPriority != other.SpawnPriority) return false;
|
||||
if (!_jobPriorities.SequenceEqual(other._jobPriorities)) return false;
|
||||
if (!_antagPreferences.SequenceEqual(other._antagPreferences)) return false;
|
||||
if (!_traitPreferences.SequenceEqual(other._traitPreferences)) return false;
|
||||
if (!Loadouts.SequenceEqual(other.Loadouts)) return false;
|
||||
return Appearance.MemberwiseEquals(other.Appearance);
|
||||
}
|
||||
|
||||
public void EnsureValid(IConfigurationManager configManager, IPrototypeManager prototypeManager)
|
||||
public void EnsureValid(ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
var configManager = collection.Resolve<IConfigurationManager>();
|
||||
var prototypeManager = collection.Resolve<IPrototypeManager>();
|
||||
|
||||
if (!prototypeManager.TryIndex<SpeciesPrototype>(Species, out var speciesPrototype) || speciesPrototype.RoundStart == false)
|
||||
{
|
||||
Species = SharedHumanoidAppearanceSystem.DefaultSpecies;
|
||||
@@ -455,21 +451,6 @@ namespace Content.Shared.Preferences
|
||||
_ => PreferenceUnavailableMode.StayInLobby // Invalid enum values.
|
||||
};
|
||||
|
||||
var clothing = Clothing switch
|
||||
{
|
||||
ClothingPreference.Jumpsuit => ClothingPreference.Jumpsuit,
|
||||
ClothingPreference.Jumpskirt => ClothingPreference.Jumpskirt,
|
||||
_ => ClothingPreference.Jumpsuit // Invalid enum values.
|
||||
};
|
||||
|
||||
var backpack = Backpack switch
|
||||
{
|
||||
BackpackPreference.Backpack => BackpackPreference.Backpack,
|
||||
BackpackPreference.Satchel => BackpackPreference.Satchel,
|
||||
BackpackPreference.Duffelbag => BackpackPreference.Duffelbag,
|
||||
_ => BackpackPreference.Backpack // Invalid enum values.
|
||||
};
|
||||
|
||||
var spawnPriority = SpawnPriority switch
|
||||
{
|
||||
SpawnPriorityPreference.None => SpawnPriorityPreference.None,
|
||||
@@ -502,8 +483,6 @@ namespace Content.Shared.Preferences
|
||||
Sex = sex;
|
||||
Gender = gender;
|
||||
Appearance = appearance;
|
||||
Clothing = clothing;
|
||||
Backpack = backpack;
|
||||
SpawnPriority = spawnPriority;
|
||||
|
||||
_jobPriorities.Clear();
|
||||
@@ -520,12 +499,31 @@ namespace Content.Shared.Preferences
|
||||
|
||||
_traitPreferences.Clear();
|
||||
_traitPreferences.AddRange(traits);
|
||||
|
||||
// Checks prototypes exist for all loadouts and dump / set to default if not.
|
||||
var toRemove = new ValueList<string>();
|
||||
|
||||
foreach (var (roleName, loadouts) in _loadouts)
|
||||
{
|
||||
if (!prototypeManager.HasIndex<RoleLoadoutPrototype>(roleName))
|
||||
{
|
||||
toRemove.Add(roleName);
|
||||
continue;
|
||||
}
|
||||
|
||||
public ICharacterProfile Validated(IConfigurationManager configManager, IPrototypeManager prototypeManager)
|
||||
loadouts.EnsureValid(session, collection);
|
||||
}
|
||||
|
||||
foreach (var value in toRemove)
|
||||
{
|
||||
_loadouts.Remove(value);
|
||||
}
|
||||
}
|
||||
|
||||
public ICharacterProfile Validated(ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
var profile = new HumanoidCharacterProfile(this);
|
||||
profile.EnsureValid(configManager, prototypeManager);
|
||||
profile.EnsureValid(session, collection);
|
||||
return profile;
|
||||
}
|
||||
|
||||
@@ -551,16 +549,32 @@ namespace Content.Shared.Preferences
|
||||
Age,
|
||||
Sex,
|
||||
Gender,
|
||||
Appearance,
|
||||
Clothing,
|
||||
Backpack
|
||||
Appearance
|
||||
),
|
||||
SpawnPriority,
|
||||
PreferenceUnavailable,
|
||||
_jobPriorities,
|
||||
_antagPreferences,
|
||||
_traitPreferences
|
||||
_traitPreferences,
|
||||
_loadouts
|
||||
);
|
||||
}
|
||||
|
||||
public void SetLoadout(RoleLoadout loadout)
|
||||
{
|
||||
_loadouts[loadout.Role.Id] = loadout;
|
||||
}
|
||||
|
||||
public RoleLoadout GetLoadoutOrDefault(string id, IEntityManager entManager, IPrototypeManager protoManager)
|
||||
{
|
||||
if (!_loadouts.TryGetValue(id, out var loadout))
|
||||
{
|
||||
loadout = new RoleLoadout(id);
|
||||
loadout.SetDefault(protoManager, force: true);
|
||||
}
|
||||
|
||||
loadout.SetDefault(protoManager);
|
||||
return loadout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Shared.Humanoid;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Preferences
|
||||
@@ -15,11 +16,11 @@ namespace Content.Shared.Preferences
|
||||
/// <summary>
|
||||
/// Makes this profile valid so there's no bad data like negative ages.
|
||||
/// </summary>
|
||||
void EnsureValid(IConfigurationManager configManager, IPrototypeManager prototypeManager);
|
||||
void EnsureValid(ICommonSession session, IDependencyCollection collection);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of this profile that has <see cref="EnsureValid"/> applied, i.e. no invalid data.
|
||||
/// </summary>
|
||||
ICharacterProfile Validated(IConfigurationManager configManager, IPrototypeManager prototypeManager);
|
||||
ICharacterProfile Validated(ICommonSession session, IDependencyCollection collection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Preferences.Loadouts.Effects;
|
||||
|
||||
/// <summary>
|
||||
/// Uses a <see cref="LoadoutEffectGroupPrototype"/> prototype as a singular effect that can be re-used.
|
||||
/// </summary>
|
||||
public sealed partial class GroupLoadoutEffect : LoadoutEffect
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public ProtoId<LoadoutEffectGroupPrototype> Proto;
|
||||
|
||||
public override bool Validate(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||
{
|
||||
var effectsProto = collection.Resolve<IPrototypeManager>().Index(Proto);
|
||||
|
||||
foreach (var effect in effectsProto.Effects)
|
||||
{
|
||||
if (!effect.Validate(loadout, session, collection, out reason))
|
||||
return false;
|
||||
}
|
||||
|
||||
reason = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Preferences.Loadouts.Effects;
|
||||
|
||||
/// <summary>
|
||||
/// Checks for a job requirement to be met such as playtime.
|
||||
/// </summary>
|
||||
public sealed partial class JobRequirementLoadoutEffect : LoadoutEffect
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public JobRequirement Requirement = default!;
|
||||
|
||||
public override bool Validate(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||
{
|
||||
var manager = collection.Resolve<ISharedPlaytimeManager>();
|
||||
var playtimes = manager.GetPlayTimes(session);
|
||||
return JobRequirements.TryRequirementMet(Requirement, playtimes, out reason,
|
||||
collection.Resolve<IEntityManager>(),
|
||||
collection.Resolve<IPrototypeManager>());
|
||||
}
|
||||
}
|
||||
20
Content.Shared/Preferences/Loadouts/Effects/LoadoutEffect.cs
Normal file
20
Content.Shared/Preferences/Loadouts/Effects/LoadoutEffect.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Preferences.Loadouts.Effects;
|
||||
|
||||
[ImplicitDataDefinitionForInheritors]
|
||||
public abstract partial class LoadoutEffect
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to validate the effect.
|
||||
/// </summary>
|
||||
public abstract bool Validate(
|
||||
RoleLoadout loadout,
|
||||
ICommonSession session,
|
||||
IDependencyCollection collection,
|
||||
[NotNullWhen(false)] out FormattedMessage? reason);
|
||||
|
||||
public virtual void Apply(RoleLoadout loadout) {}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Preferences.Loadouts.Effects;
|
||||
|
||||
/// <summary>
|
||||
/// Stores a group of loadout effects in a prototype for re-use.
|
||||
/// </summary>
|
||||
[Prototype]
|
||||
public sealed class LoadoutEffectGroupPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; } = string.Empty;
|
||||
|
||||
[DataField(required: true)]
|
||||
public List<LoadoutEffect> Effects = new();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Preferences.Loadouts.Effects;
|
||||
|
||||
public sealed partial class PointsCostLoadoutEffect : LoadoutEffect
|
||||
{
|
||||
[DataField(required: true)]
|
||||
public int Cost = 1;
|
||||
|
||||
public override bool Validate(
|
||||
RoleLoadout loadout,
|
||||
ICommonSession session,
|
||||
IDependencyCollection collection,
|
||||
[NotNullWhen(false)] out FormattedMessage? reason)
|
||||
{
|
||||
reason = null;
|
||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||
|
||||
if (!protoManager.TryIndex(loadout.Role, out var roleProto) || roleProto.Points == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (loadout.Points <= Cost)
|
||||
{
|
||||
reason = FormattedMessage.FromUnformatted("loadout-group-points-insufficient");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Apply(RoleLoadout loadout)
|
||||
{
|
||||
loadout.Points -= Cost;
|
||||
}
|
||||
}
|
||||
13
Content.Shared/Preferences/Loadouts/Loadout.cs
Normal file
13
Content.Shared/Preferences/Loadouts/Loadout.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Preferences.Loadouts;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the selected prototype and custom data for a loadout.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class Loadout
|
||||
{
|
||||
public ProtoId<LoadoutPrototype> Prototype;
|
||||
}
|
||||
34
Content.Shared/Preferences/Loadouts/LoadoutGroupPrototype.cs
Normal file
34
Content.Shared/Preferences/Loadouts/LoadoutGroupPrototype.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Preferences.Loadouts;
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to a set of loadouts for a particular slot.
|
||||
/// </summary>
|
||||
[Prototype("loadoutGroup")]
|
||||
public sealed class LoadoutGroupPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// User-friendly name for the group.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public LocId Name;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum number of loadouts that need to be specified for this category.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MinLimit = 1;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum limit for the category.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MaxLimit = 1;
|
||||
|
||||
[DataField(required: true)]
|
||||
public List<ProtoId<LoadoutPrototype>> Loadouts = new();
|
||||
}
|
||||
25
Content.Shared/Preferences/Loadouts/LoadoutPrototype.cs
Normal file
25
Content.Shared/Preferences/Loadouts/LoadoutPrototype.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Content.Shared.Roles;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Preferences.Loadouts;
|
||||
|
||||
/// <summary>
|
||||
/// Individual loadout item to be applied.
|
||||
/// </summary>
|
||||
[Prototype]
|
||||
public sealed class LoadoutPrototype : IPrototype
|
||||
{
|
||||
[IdDataField]
|
||||
public string ID { get; } = string.Empty;
|
||||
|
||||
[DataField(required: true)]
|
||||
public ProtoId<StartingGearPrototype> Equipment;
|
||||
|
||||
/// <summary>
|
||||
/// Effects to be applied when the loadout is applied.
|
||||
/// These can also return true or false for validation purposes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<LoadoutEffect> Effects = new();
|
||||
}
|
||||
248
Content.Shared/Preferences/Loadouts/RoleLoadout.cs
Normal file
248
Content.Shared/Preferences/Loadouts/RoleLoadout.cs
Normal file
@@ -0,0 +1,248 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Random;
|
||||
using Robust.Shared.Collections;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Shared.Preferences.Loadouts;
|
||||
|
||||
/// <summary>
|
||||
/// Contains all of the selected data for a role's loadout.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class RoleLoadout
|
||||
{
|
||||
public readonly ProtoId<RoleLoadoutPrototype> Role;
|
||||
|
||||
public Dictionary<ProtoId<LoadoutGroupPrototype>, List<Loadout>> SelectedLoadouts = new();
|
||||
|
||||
/*
|
||||
* Loadout-specific data used for validation.
|
||||
*/
|
||||
|
||||
public int? Points;
|
||||
|
||||
public RoleLoadout(ProtoId<RoleLoadoutPrototype> role)
|
||||
{
|
||||
Role = role;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures all prototypes exist and effects can be applied.
|
||||
/// </summary>
|
||||
public void EnsureValid(ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
var groupRemove = new ValueList<string>();
|
||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||
|
||||
if (!protoManager.TryIndex(Role, out var roleProto))
|
||||
{
|
||||
SelectedLoadouts.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset points to recalculate.
|
||||
Points = roleProto.Points;
|
||||
|
||||
foreach (var (group, groupLoadouts) in SelectedLoadouts)
|
||||
{
|
||||
// Dump if Group doesn't exist
|
||||
if (!protoManager.TryIndex(group, out var groupProto))
|
||||
{
|
||||
groupRemove.Add(group);
|
||||
continue;
|
||||
}
|
||||
|
||||
var loadouts = groupLoadouts[..Math.Min(groupLoadouts.Count, groupProto.MaxLimit)];
|
||||
|
||||
// Validate first
|
||||
for (var i = loadouts.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var loadout = loadouts[i];
|
||||
|
||||
if (!protoManager.TryIndex(loadout.Prototype, out var loadoutProto))
|
||||
{
|
||||
loadouts.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Validate the loadout can be applied (e.g. points).
|
||||
if (!IsValid(session, loadout.Prototype, collection, out _))
|
||||
{
|
||||
loadouts.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
Apply(loadoutProto);
|
||||
}
|
||||
|
||||
// Apply defaults if required
|
||||
// Technically it's possible for someone to game themselves into loadouts they shouldn't have
|
||||
// If you put invalid ones first but that's your fault for not using sensible defaults
|
||||
if (loadouts.Count < groupProto.MinLimit)
|
||||
{
|
||||
for (var i = 0; i < Math.Min(groupProto.MinLimit, groupProto.Loadouts.Count); i++)
|
||||
{
|
||||
if (!protoManager.TryIndex(groupProto.Loadouts[i], out var loadoutProto))
|
||||
continue;
|
||||
|
||||
var defaultLoadout = new Loadout()
|
||||
{
|
||||
Prototype = loadoutProto.ID,
|
||||
};
|
||||
|
||||
if (loadouts.Contains(defaultLoadout))
|
||||
continue;
|
||||
|
||||
// Still need to apply the effects even if validation is ignored.
|
||||
loadouts.Add(defaultLoadout);
|
||||
Apply(loadoutProto);
|
||||
}
|
||||
}
|
||||
|
||||
SelectedLoadouts[group] = loadouts;
|
||||
}
|
||||
|
||||
foreach (var value in groupRemove)
|
||||
{
|
||||
SelectedLoadouts.Remove(value);
|
||||
}
|
||||
}
|
||||
|
||||
private void Apply(LoadoutPrototype loadoutProto)
|
||||
{
|
||||
foreach (var effect in loadoutProto.Effects)
|
||||
{
|
||||
effect.Apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the selected loadouts to default if no data is present.
|
||||
/// </summary>
|
||||
public void SetDefault(IPrototypeManager protoManager, bool force = false)
|
||||
{
|
||||
if (force)
|
||||
SelectedLoadouts.Clear();
|
||||
|
||||
var roleProto = protoManager.Index(Role);
|
||||
|
||||
for (var i = roleProto.Groups.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var group = roleProto.Groups[i];
|
||||
|
||||
if (!protoManager.TryIndex(group, out var groupProto))
|
||||
continue;
|
||||
|
||||
if (SelectedLoadouts.ContainsKey(group))
|
||||
continue;
|
||||
|
||||
SelectedLoadouts[group] = new List<Loadout>();
|
||||
|
||||
if (groupProto.MinLimit > 0)
|
||||
{
|
||||
// Apply any loadouts we can.
|
||||
for (var j = 0; j < Math.Min(groupProto.MinLimit, groupProto.Loadouts.Count); j++)
|
||||
{
|
||||
AddLoadout(group, groupProto.Loadouts[j], protoManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a loadout is valid or not.
|
||||
/// </summary>
|
||||
public bool IsValid(ICommonSession session, ProtoId<LoadoutPrototype> loadout, IDependencyCollection collection, [NotNullWhen(false)] out FormattedMessage? reason)
|
||||
{
|
||||
reason = null;
|
||||
|
||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||
|
||||
if (!protoManager.TryIndex(loadout, out var loadoutProto))
|
||||
{
|
||||
// Uhh
|
||||
reason = FormattedMessage.FromMarkup("");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!protoManager.TryIndex(Role, out var roleProto))
|
||||
{
|
||||
reason = FormattedMessage.FromUnformatted("loadouts-prototype-missing");
|
||||
return false;
|
||||
}
|
||||
|
||||
var valid = true;
|
||||
|
||||
foreach (var effect in loadoutProto.Effects)
|
||||
{
|
||||
valid = valid && effect.Validate(this, session, collection, out reason);
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the specified loadout to this group.
|
||||
/// </summary>
|
||||
public bool AddLoadout(ProtoId<LoadoutGroupPrototype> selectedGroup, ProtoId<LoadoutPrototype> selectedLoadout, IPrototypeManager protoManager)
|
||||
{
|
||||
var groupLoadouts = SelectedLoadouts[selectedGroup];
|
||||
|
||||
// Need to unselect existing ones if we're at or above limit
|
||||
var limit = Math.Max(0, groupLoadouts.Count + 1 - protoManager.Index(selectedGroup).MaxLimit);
|
||||
|
||||
for (var i = 0; i < groupLoadouts.Count; i++)
|
||||
{
|
||||
var loadout = groupLoadouts[i];
|
||||
|
||||
if (loadout.Prototype != selectedLoadout)
|
||||
{
|
||||
// Remove any other loadouts that might push it above the limit.
|
||||
if (limit > 0)
|
||||
{
|
||||
limit--;
|
||||
groupLoadouts.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
DebugTools.Assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
groupLoadouts.Add(new Loadout()
|
||||
{
|
||||
Prototype = selectedLoadout,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removed the specified loadout from this group.
|
||||
/// </summary>
|
||||
public bool RemoveLoadout(ProtoId<LoadoutGroupPrototype> selectedGroup, ProtoId<LoadoutPrototype> selectedLoadout, IPrototypeManager protoManager)
|
||||
{
|
||||
// Although this may bring us below minimum we'll let EnsureValid handle it.
|
||||
|
||||
var groupLoadouts = SelectedLoadouts[selectedGroup];
|
||||
|
||||
for (var i = 0; i < groupLoadouts.Count; i++)
|
||||
{
|
||||
var loadout = groupLoadouts[i];
|
||||
|
||||
if (loadout.Prototype != selectedLoadout)
|
||||
continue;
|
||||
|
||||
groupLoadouts.RemoveAt(i);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
29
Content.Shared/Preferences/Loadouts/RoleLoadoutPrototype.cs
Normal file
29
Content.Shared/Preferences/Loadouts/RoleLoadoutPrototype.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Preferences.Loadouts;
|
||||
|
||||
/// <summary>
|
||||
/// Corresponds to a Job / Antag prototype and specifies loadouts
|
||||
/// </summary>
|
||||
[Prototype]
|
||||
public sealed class RoleLoadoutPrototype : IPrototype
|
||||
{
|
||||
/*
|
||||
* Separate to JobPrototype / AntagPrototype as they are turning into messy god classes.
|
||||
*/
|
||||
|
||||
[IdDataField]
|
||||
public string ID { get; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Groups that comprise this role loadout.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public List<ProtoId<LoadoutGroupPrototype>> Groups = new();
|
||||
|
||||
/// <summary>
|
||||
/// How many points are allotted for this role loadout prototype.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int? Points;
|
||||
}
|
||||
@@ -96,7 +96,7 @@ namespace Content.Shared.Roles
|
||||
/// </summary>
|
||||
public static bool TryRequirementMet(
|
||||
JobRequirement requirement,
|
||||
Dictionary<string, TimeSpan> playTimes,
|
||||
IReadOnlyDictionary<string, TimeSpan> playTimes,
|
||||
[NotNullWhen(false)] out FormattedMessage? reason,
|
||||
IEntityManager entManager,
|
||||
IPrototypeManager prototypes)
|
||||
|
||||
@@ -9,37 +9,21 @@ namespace Content.Shared.Roles
|
||||
[DataField]
|
||||
public Dictionary<string, EntProtoId> Equipment = new();
|
||||
|
||||
/// <summary>
|
||||
/// if empty, there is no skirt override - instead the uniform provided in equipment is added.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public EntProtoId? InnerClothingSkirt;
|
||||
|
||||
[DataField]
|
||||
public EntProtoId? Satchel;
|
||||
|
||||
[DataField]
|
||||
public EntProtoId? Duffelbag;
|
||||
|
||||
[DataField]
|
||||
public List<EntProtoId> Inhand = new(0);
|
||||
|
||||
/// <summary>
|
||||
/// Inserts entities into the specified slot's storage (if it does have storage).
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public Dictionary<string, List<EntProtoId>> Storage = new();
|
||||
|
||||
[ViewVariables]
|
||||
[IdDataField]
|
||||
public string ID { get; private set; } = string.Empty;
|
||||
|
||||
public string GetGear(string slot, HumanoidCharacterProfile? profile)
|
||||
public string GetGear(string slot)
|
||||
{
|
||||
if (profile != null)
|
||||
{
|
||||
if (slot == "jumpsuit" && profile.Clothing == ClothingPreference.Jumpskirt && !string.IsNullOrEmpty(InnerClothingSkirt))
|
||||
return InnerClothingSkirt;
|
||||
if (slot == "back" && profile.Backpack == BackpackPreference.Satchel && !string.IsNullOrEmpty(Satchel))
|
||||
return Satchel;
|
||||
if (slot == "back" && profile.Backpack == BackpackPreference.Duffelbag && !string.IsNullOrEmpty(Duffelbag))
|
||||
return Duffelbag;
|
||||
}
|
||||
|
||||
return Equipment.TryGetValue(slot, out var equipment) ? equipment : string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Storage;
|
||||
using Content.Shared.Storage.EntitySystems;
|
||||
using Robust.Shared.Collections;
|
||||
|
||||
namespace Content.Shared.Station;
|
||||
|
||||
@@ -10,31 +13,31 @@ public abstract class SharedStationSpawningSystem : EntitySystem
|
||||
{
|
||||
[Dependency] protected readonly InventorySystem InventorySystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly SharedStorageSystem _storage = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
|
||||
|
||||
/// <summary>
|
||||
/// Equips starting gear onto the given entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity to load out.</param>
|
||||
/// <param name="startingGear">Starting gear to use.</param>
|
||||
/// <param name="profile">Character profile to use, if any.</param>
|
||||
public void EquipStartingGear(EntityUid entity, StartingGearPrototype startingGear, HumanoidCharacterProfile? profile)
|
||||
public void EquipStartingGear(EntityUid entity, StartingGearPrototype startingGear)
|
||||
{
|
||||
if (InventorySystem.TryGetSlots(entity, out var slotDefinitions))
|
||||
{
|
||||
foreach (var slot in slotDefinitions)
|
||||
{
|
||||
var equipmentStr = startingGear.GetGear(slot.Name, profile);
|
||||
var equipmentStr = startingGear.GetGear(slot.Name);
|
||||
if (!string.IsNullOrEmpty(equipmentStr))
|
||||
{
|
||||
var equipmentEntity = EntityManager.SpawnEntity(equipmentStr, EntityManager.GetComponent<TransformComponent>(entity).Coordinates);
|
||||
InventorySystem.TryEquip(entity, equipmentEntity, slot.Name, true, force:true);
|
||||
InventorySystem.TryEquip(entity, equipmentEntity, slot.Name, silent: true, force:true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!TryComp(entity, out HandsComponent? handsComponent))
|
||||
return;
|
||||
|
||||
if (TryComp(entity, out HandsComponent? handsComponent))
|
||||
{
|
||||
var inhand = startingGear.Inhand;
|
||||
var coords = EntityManager.GetComponent<TransformComponent>(entity).Coordinates;
|
||||
foreach (var prototype in inhand)
|
||||
@@ -47,4 +50,33 @@ public abstract class SharedStationSpawningSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (startingGear.Storage.Count > 0)
|
||||
{
|
||||
var coords = _xformSystem.GetMapCoordinates(entity);
|
||||
var ents = new ValueList<EntityUid>();
|
||||
TryComp(entity, out InventoryComponent? inventoryComp);
|
||||
|
||||
foreach (var (slot, entProtos) in startingGear.Storage)
|
||||
{
|
||||
if (entProtos.Count == 0)
|
||||
continue;
|
||||
|
||||
foreach (var ent in entProtos)
|
||||
{
|
||||
ents.Add(Spawn(ent, coords));
|
||||
}
|
||||
|
||||
if (inventoryComp != null &&
|
||||
InventorySystem.TryGetSlotEntity(entity, slot, out var slotEnt, inventoryComponent: inventoryComp) &&
|
||||
TryComp(slotEnt, out StorageComponent? storage))
|
||||
{
|
||||
foreach (var ent in ents)
|
||||
{
|
||||
_storage.Insert(slotEnt.Value, ent, out _, storageComp: storage, playSound: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
Resources/Locale/en-US/job/loadouts.ftl
Normal file
2
Resources/Locale/en-US/job/loadouts.ftl
Normal file
@@ -0,0 +1,2 @@
|
||||
loadout-window = Loadout
|
||||
loadout-none = None
|
||||
159
Resources/Locale/en-US/preferences/loadout-groups.ftl
Normal file
159
Resources/Locale/en-US/preferences/loadout-groups.ftl
Normal file
@@ -0,0 +1,159 @@
|
||||
# Miscellaneous
|
||||
loadout-group-trinkets = Trinkets
|
||||
|
||||
# Command
|
||||
loadout-group-captain-head = Captain head
|
||||
loadout-group-captain-jumpsuit = Captain jumpsuit
|
||||
loadout-group-captain-neck = Captain neck
|
||||
loadout-group-captain-backpack = Captain backpack
|
||||
|
||||
loadout-group-hop-head = Head of Personnel head
|
||||
loadout-group-hop-jumpsuit = Head of Personnel jumpsuit
|
||||
loadout-group-hop-neck = Head of Personnel neck
|
||||
loadout-group-hop-backpack = Head of Personnel backpack
|
||||
|
||||
|
||||
# Civilian
|
||||
loadout-group-passenger-jumpsuit = Passenger jumpsuit
|
||||
loadout-group-passenger-mask = Passenger mask
|
||||
loadout-group-passenger-gloves = Passenger gloves
|
||||
loadout-group-passenger-backpack = Passenger backpack
|
||||
|
||||
loadout-group-bartender-head = Bartender head
|
||||
loadout-group-bartender-jumpsuit = Bartender jumpsuit
|
||||
loadout-group-bartender-outerclothing = Bartender outer clothing
|
||||
|
||||
loadout-group-chef-head = Chef head
|
||||
loadout-group-chef-mask = Chef mask
|
||||
loadout-group-chef-jumpsuit = Chef jumpsuit
|
||||
loadout-group-chef-outerclothing = Chef outer clothing
|
||||
|
||||
loadout-group-librarian-jumpsuit = Librarian jumpsuit
|
||||
|
||||
loadout-group-lawyer-jumpsuit = Lawyer jumpsuit
|
||||
loadout-group-lawyer-neck = Lawyer neck
|
||||
|
||||
loadout-group-chaplain-head = Chaplain head
|
||||
loadout-group-chaplain-mask = Chaplain mask
|
||||
loadout-group-chaplain-jumpsuit = Chaplain jumpsuit
|
||||
loadout-group-chaplain-backpack = Chaplain backpack
|
||||
loadout-group-chaplain-outerclothing = Chaplain outer clothing
|
||||
loadout-group-chaplain-neck = Chaplain neck
|
||||
|
||||
loadout-group-janitor-head = Janitor head
|
||||
loadout-group-janitor-jumpsuit = Janitor jumpsuit
|
||||
|
||||
loadout-group-botanist-head = Botanist head
|
||||
loadout-group-botanist-jumpsuit = Botanist jumpsuit
|
||||
loadout-group-botanist-backpack = Botanist backpack
|
||||
loadout-group-botanist-outerclothing = Botanist outer clothing
|
||||
|
||||
loadout-group-clown-head = Clown head
|
||||
loadout-group-clown-jumpsuit = Clown jumpsuit
|
||||
loadout-group-clown-backpack = Clown backpack
|
||||
loadout-group-clown-shoes = Clown shoes
|
||||
|
||||
loadout-group-mime-head = Mime head
|
||||
loadout-group-mime-mask = Mime mask
|
||||
loadout-group-mime-jumpsuit = Mime jumpsuit
|
||||
loadout-group-mime-backpack = Mime backpack
|
||||
|
||||
loadout-group-musician-backpack = Musician backpack
|
||||
|
||||
# Cargo
|
||||
loadout-group-quartermaster-head = Quartermaster head
|
||||
loadout-group-quartermaster-jumpsuit = Quartermaster jumpsuit
|
||||
loadout-group-quartermaster-backpack = Quartermaster backpack
|
||||
loadout-group-quartermaster-neck = Quartermaster neck
|
||||
|
||||
loadout-group-cargo-technician-head = Technician head
|
||||
loadout-group-cargo-technician-jumpsuit = Technician jumpsuit
|
||||
loadout-group-cargo-technician-backpack = Technician backpack
|
||||
|
||||
loadout-group-salvage-specialist-backpack = Salvage Specialist backpack
|
||||
|
||||
# Engineering
|
||||
loadout-group-chief-engineer-head = Chief Engineer head
|
||||
loadout-group-chief-engineer-jumpsuit = Chief Engineer jumpsuit
|
||||
loadout-group-chief-engineer-backpack = Chief Engineer backpack
|
||||
loadout-group-chief-engineer-neck = Chief Engineer neck
|
||||
|
||||
loadout-group-technical-assistant-jumpsuit = Technical Assistant jumpsuit
|
||||
|
||||
loadout-group-station-engineer-head = Station Engineer head
|
||||
loadout-group-station-engineer-jumpsuit = Station Engineer jumpsuit
|
||||
loadout-group-station-engineer-backpack = Station Engineer backpack
|
||||
loadout-group-station-engineer-outerclothing = Station Engineer outer clothing
|
||||
loadout-group-station-engineer-id = Station Engineer ID
|
||||
|
||||
loadout-group-atmospheric-technician-jumpsuit = Atmospheric Technician jumpsuit
|
||||
loadout-group-atmospheric-technician-backpack = Atmospheric Technician backpack
|
||||
|
||||
# Science
|
||||
loadout-group-research-director-head = Research Director head
|
||||
loadout-group-research-director-neck = Research Director neck
|
||||
loadout-group-research-director-jumpsuit = Research Director jumpsuit
|
||||
loadout-group-research-director-backpack = Research Director backpack
|
||||
loadout-group-research-director-outerclothing = Research Director outer clothing
|
||||
|
||||
loadout-group-scientist-head = Scientist head
|
||||
loadout-group-scientist-neck = Scientist neck
|
||||
loadout-group-scientist-jumpsuit = Scientist jumpsuit
|
||||
loadout-group-scientist-backpack = Scientist backpack
|
||||
loadout-group-scientist-outerclothing = Scientist outer clothing
|
||||
loadout-group-scientist-id = Scientist ID
|
||||
|
||||
loadout-group-research-assistant-jumpsuit = Research Assistant jumpsuit
|
||||
|
||||
# Security
|
||||
loadout-group-head-of-security-head = Head of Security head
|
||||
loadout-group-head-of-security-jumpsuit = Head of Security jumpsuit
|
||||
loadout-group-head-of-security-neck = Head of Security neck
|
||||
loadout-group-head-of-security-outerclothing = Head of Security outer clothing
|
||||
|
||||
loadout-group-warden-head = Warden head
|
||||
loadout-group-warden-jumpsuit = Warden jumpsuit
|
||||
loadout-group-warden-outerclothing = Warden outer clothing
|
||||
|
||||
loadout-group-security-head = Security head
|
||||
loadout-group-security-jumpsuit = Security jumpsuit
|
||||
loadout-group-security-backpack = Security backpack
|
||||
loadout-group-security-id = Security ID
|
||||
|
||||
loadout-group-detective-head = Detective head
|
||||
loadout-group-detective-neck = Detective neck
|
||||
loadout-group-detective-jumpsuit = Detective jumpsuit
|
||||
loadout-group-detective-backpack = Detective backpack
|
||||
loadout-group-detective-outerclothing = Detective outer clothing
|
||||
|
||||
loadout-group-security-cadet-jumpsuit = Security cadet jumpsuit
|
||||
|
||||
# Medical
|
||||
loadout-group-chief-medical-officer-head = Chief Medical Officer head
|
||||
loadout-group-chief-medical-officer-jumpsuit = Chief Medical Officer jumpsuit
|
||||
loadout-group-chief-medical-officer-outerclothing = Chief Medical Officer outer clothing
|
||||
loadout-group-chief-medical-officer-backpack = Chief Medical Officer backpack
|
||||
loadout-group-chief-medical-officer-neck = Chief Medical Officer neck
|
||||
|
||||
loadout-group-medical-doctor-head = Medical Doctor head
|
||||
loadout-group-medical-doctor-jumpsuit = Medical Doctor jumpsuit
|
||||
loadout-group-medical-doctor-outerclothing = Medical Doctor outer clothing
|
||||
loadout-group-medical-doctor-backpack = Medical Doctor backpack
|
||||
loadout-group-medical-doctor-id = Medical Doctor ID
|
||||
|
||||
loadout-group-medical-intern-jumpsuit = Medical intern jumpsuit
|
||||
|
||||
loadout-group-chemist-jumpsuit = Chemist jumpsuit
|
||||
loadout-group-chemist-outerclothing = Chemist outer clothing
|
||||
loadout-group-chemist-backpack = Chemist backpack
|
||||
|
||||
loadout-group-paramedic-head = Paramedic head
|
||||
loadout-group-paramedic-jumpsuit = Paramedic jumpsuit
|
||||
loadout-group-paramedic-outerclothing = Paramedic outer clothing
|
||||
loadout-group-paramedic-backpack = Paramedic backpack
|
||||
|
||||
# Wildcards
|
||||
loadout-group-reporter-jumpsuit = Reporter jumpsuit
|
||||
|
||||
loadout-group-boxer-jumpsuit = Boxer jumpsuit
|
||||
loadout-group-boxer-gloves = Boxer gloves
|
||||
7
Resources/Locale/en-US/preferences/loadouts.ftl
Normal file
7
Resources/Locale/en-US/preferences/loadouts.ftl
Normal file
@@ -0,0 +1,7 @@
|
||||
# Restrictions
|
||||
loadout-restrictions = Restrictions
|
||||
loadouts-min-limit = Min count: {$count}
|
||||
loadouts-max-limit = Max count: {$count}
|
||||
loadouts-points-limit = Points: {$count} / {$max}
|
||||
|
||||
loadouts-points-restriction = Insufficient points
|
||||
@@ -19,8 +19,6 @@ humanoid-profile-editor-pronouns-neuter-text = It / It
|
||||
humanoid-profile-editor-import-button = Import
|
||||
humanoid-profile-editor-export-button = Export
|
||||
humanoid-profile-editor-save-button = Save
|
||||
humanoid-profile-editor-clothing-label = Clothing:
|
||||
humanoid-profile-editor-backpack-label = Backpack:
|
||||
humanoid-profile-editor-spawn-priority-label = Spawn priority:
|
||||
humanoid-profile-editor-eyes-label = Eye color:
|
||||
humanoid-profile-editor-jobs-tab = Jobs
|
||||
|
||||
@@ -104,6 +104,17 @@
|
||||
- id: Flash
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
noSpawn: true
|
||||
parent: ClothingBackpackIan
|
||||
id: ClothingBackpackHOPIanFilled
|
||||
components:
|
||||
- type: StorageFill
|
||||
contents:
|
||||
- id: BoxSurvival
|
||||
- id: Flash
|
||||
#- id: TelescopicBaton
|
||||
|
||||
- type: entity
|
||||
noSpawn: true
|
||||
parent: ClothingBackpackMedical
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
# Head
|
||||
- type: loadout
|
||||
id: CargoTechnicianHead
|
||||
equipment: CargoTechnicianHead
|
||||
|
||||
- type: startingGear
|
||||
id: CargoTechnicianHead
|
||||
equipment:
|
||||
head: ClothingHeadHatCargosoft
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: CargoTechnicianJumpsuit
|
||||
equipment: CargoTechnicianJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: CargoTechnicianJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitCargo
|
||||
|
||||
- type: loadout
|
||||
id: CargoTechnicianJumpskirt
|
||||
equipment: CargoTechnicianJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: CargoTechnicianJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtCargo
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: CargoTechnicianBackpack
|
||||
equipment: CargoTechnicianBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: CargoTechnicianBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackCargoFilled
|
||||
|
||||
- type: loadout
|
||||
id: CargoTechnicianSatchel
|
||||
equipment: CargoTechnicianSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: CargoTechnicianSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelCargoFilled
|
||||
|
||||
- type: loadout
|
||||
id: CargoTechnicianDuffel
|
||||
equipment: CargoTechnicianDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: CargoTechnicianDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelCargoFilled
|
||||
111
Resources/Prototypes/Loadouts/Jobs/Cargo/quartermaster.yml
Normal file
111
Resources/Prototypes/Loadouts/Jobs/Cargo/quartermaster.yml
Normal file
@@ -0,0 +1,111 @@
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: QuartermasterJumpsuit
|
||||
equipment: QuartermasterJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitQM
|
||||
|
||||
- type: loadout
|
||||
id: QuartermasterJumpskirt
|
||||
equipment: QuartermasterJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtQM
|
||||
|
||||
- type: loadout
|
||||
id: QuartermasterTurtleneck
|
||||
equipment: QuartermasterTurtleneck
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterTurtleneck
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitQMTurtleneck
|
||||
|
||||
- type: loadout
|
||||
id: QuartermasterTurtleneckSkirt
|
||||
equipment: QuartermasterTurtleneckSkirt
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterTurtleneckSkirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtQMTurtleneck
|
||||
|
||||
- type: loadout
|
||||
id: QuartermasterFormalSuit
|
||||
equipment: QuartermasterFormalSuit
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterFormalSuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitQMFormal
|
||||
|
||||
# Head
|
||||
- type: loadout
|
||||
id: QuartermasterHead
|
||||
equipment: QuartermasterHead
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterHead
|
||||
equipment:
|
||||
head: ClothingHeadHatQMsoft
|
||||
|
||||
- type: loadout
|
||||
id: QuartermasterBeret
|
||||
equipment: QuartermasterBeret
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterBeret
|
||||
equipment:
|
||||
head: ClothingHeadHatBeretQM
|
||||
|
||||
# Neck
|
||||
- type: loadout
|
||||
id: QuartermasterCloak
|
||||
equipment: QuartermasterCloak
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterCloak
|
||||
equipment:
|
||||
neck: ClothingNeckCloakQm
|
||||
|
||||
- type: loadout
|
||||
id: QuartermasterMantle
|
||||
equipment: QuartermasterMantle
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterMantle
|
||||
equipment:
|
||||
neck: ClothingNeckMantleQM
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: QuartermasterBackpack
|
||||
equipment: QuartermasterBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackQuartermasterFilled
|
||||
|
||||
- type: loadout
|
||||
id: QuartermasterSatchel
|
||||
equipment: QuartermasterSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelQuartermasterFilled
|
||||
|
||||
- type: loadout
|
||||
id: QuartermasterDuffel
|
||||
equipment: QuartermasterDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: QuartermasterDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelQuartermasterFilled
|
||||
@@ -0,0 +1,27 @@
|
||||
# Back
|
||||
- type: loadout
|
||||
id: SalvageSpecialistBackpack
|
||||
equipment: SalvageSpecialistBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: SalvageSpecialistBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackSalvageFilled
|
||||
|
||||
- type: loadout
|
||||
id: SalvageSpecialistSatchel
|
||||
equipment: SalvageSpecialistSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: SalvageSpecialistSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelSalvageFilled
|
||||
|
||||
- type: loadout
|
||||
id: SalvageSpecialistDuffel
|
||||
equipment: SalvageSpecialistDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: SalvageSpecialistDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelSalvageFilled
|
||||
65
Resources/Prototypes/Loadouts/Jobs/Civilian/bartender.yml
Normal file
65
Resources/Prototypes/Loadouts/Jobs/Civilian/bartender.yml
Normal file
@@ -0,0 +1,65 @@
|
||||
# Head
|
||||
- type: loadout
|
||||
id: BartenderHead
|
||||
equipment: BartenderHead
|
||||
|
||||
- type: startingGear
|
||||
id: BartenderHead
|
||||
equipment:
|
||||
head: ClothingHeadHatTophat
|
||||
|
||||
- type: loadout
|
||||
id: BartenderBowler
|
||||
equipment: BartenderBowler
|
||||
|
||||
- type: startingGear
|
||||
id: BartenderBowler
|
||||
equipment:
|
||||
head: ClothingHeadHatBowlerHat
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: BartenderJumpsuit
|
||||
equipment: BartenderJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: BartenderJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitBartender
|
||||
|
||||
- type: loadout
|
||||
id: BartenderJumpskirt
|
||||
equipment: BartenderJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: BartenderJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtBartender
|
||||
|
||||
- type: loadout
|
||||
id: BartenderJumpsuitPurple
|
||||
equipment: BartenderJumpsuitPurple
|
||||
|
||||
- type: startingGear
|
||||
id: BartenderJumpsuitPurple
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitBartenderPurple
|
||||
|
||||
# Outer clothing
|
||||
- type: loadout
|
||||
id: BartenderApron
|
||||
equipment: BartenderApron
|
||||
|
||||
- type: startingGear
|
||||
id: BartenderApron
|
||||
equipment:
|
||||
outerClothing: ClothingOuterApronBar
|
||||
|
||||
- type: loadout
|
||||
id: BartenderVest
|
||||
equipment: BartenderVest
|
||||
|
||||
- type: startingGear
|
||||
id: BartenderVest
|
||||
equipment:
|
||||
outerClothing: ClothingOuterVest
|
||||
84
Resources/Prototypes/Loadouts/Jobs/Civilian/botanist.yml
Normal file
84
Resources/Prototypes/Loadouts/Jobs/Civilian/botanist.yml
Normal file
@@ -0,0 +1,84 @@
|
||||
# Head
|
||||
- type: loadout
|
||||
id: BotanistHead
|
||||
equipment: BotanistHead
|
||||
|
||||
- type: startingGear
|
||||
id: BotanistHead
|
||||
equipment:
|
||||
head: ClothingHeadHatTrucker
|
||||
|
||||
- type: loadout
|
||||
id: BotanistBandana
|
||||
equipment: BotanistBandana
|
||||
|
||||
- type: startingGear
|
||||
id: BotanistBandana
|
||||
equipment:
|
||||
head: ClothingHeadBandBotany
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: BotanistJumpsuit
|
||||
equipment: BotanistJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: BotanistJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitHydroponics
|
||||
|
||||
- type: loadout
|
||||
id: BotanistJumpskirt
|
||||
equipment: BotanistJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: BotanistJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtHydroponics
|
||||
|
||||
- type: loadout
|
||||
id: BotanistOveralls
|
||||
equipment: BotanistOveralls
|
||||
|
||||
- type: startingGear
|
||||
id: BotanistOveralls
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformOveralls
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: BotanistBackpack
|
||||
equipment: BotanistBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: BotanistBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackHydroponicsFilled
|
||||
|
||||
- type: loadout
|
||||
id: BotanistSatchel
|
||||
equipment: BotanistSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: BotanistSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelHydroponicsFilled
|
||||
|
||||
- type: loadout
|
||||
id: BotanistDuffel
|
||||
equipment: BotanistDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: BotanistDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelHydroponicsFilled
|
||||
|
||||
# Outer clothing
|
||||
- type: loadout
|
||||
id: BotanistApron
|
||||
equipment: BotanistApron
|
||||
|
||||
- type: startingGear
|
||||
id: BotanistApron
|
||||
equipment:
|
||||
outerClothing: ClothingOuterApronBotanist
|
||||
158
Resources/Prototypes/Loadouts/Jobs/Civilian/chaplain.yml
Normal file
158
Resources/Prototypes/Loadouts/Jobs/Civilian/chaplain.yml
Normal file
@@ -0,0 +1,158 @@
|
||||
# Head
|
||||
- type: loadout
|
||||
id: ChaplainHead
|
||||
equipment: ChaplainHead
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainHead
|
||||
equipment:
|
||||
head: ClothingHeadHatFez
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainPlagueHat
|
||||
equipment: ChaplainPlagueHat
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainPlagueHat
|
||||
equipment:
|
||||
head: ClothingHeadHatPlaguedoctor
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainWitchHat
|
||||
equipment: ChaplainWitchHat
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainWitchHat
|
||||
equipment:
|
||||
head: ClothingHeadHatWitch
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainWitchHatAlt
|
||||
equipment: ChaplainWitchHatAlt
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainWitchHatAlt
|
||||
equipment:
|
||||
head: ClothingHeadHatWitch1
|
||||
|
||||
# Mask
|
||||
- type: loadout
|
||||
id: ChaplainMask
|
||||
equipment: ChaplainMask
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainMask
|
||||
equipment:
|
||||
mask: ClothingMaskPlague
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: ChaplainJumpsuit
|
||||
equipment: ChaplainJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitChaplain
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainJumpskirt
|
||||
equipment: ChaplainJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtChaplain
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainRobesDark
|
||||
equipment: ChaplainRobesDark
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainRobesDark
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitMonasticRobeDark
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainRobesLight
|
||||
equipment: ChaplainRobesLight
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainRobesLight
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitMonasticRobeLight
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: ChaplainBackpack
|
||||
equipment: ChaplainBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackChaplainFilled
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainSatchel
|
||||
equipment: ChaplainSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelChaplainFilled
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainDuffel
|
||||
equipment: ChaplainDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelChaplainFilled
|
||||
|
||||
# Neck
|
||||
- type: loadout
|
||||
id: ChaplainNeck
|
||||
equipment: ChaplainNeck
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainNeck
|
||||
equipment:
|
||||
neck: ClothingNeckStoleChaplain
|
||||
|
||||
# Outer clothing
|
||||
- type: loadout
|
||||
id: ChaplainPlagueSuit
|
||||
equipment: ChaplainPlagueSuit
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainPlagueSuit
|
||||
equipment:
|
||||
outerClothing: ClothingOuterPlagueSuit
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainNunRobe
|
||||
equipment: ChaplainNunRobe
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainNunRobe
|
||||
equipment:
|
||||
outerClothing: ClothingOuterNunRobe
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainBlackHoodie
|
||||
equipment: ChaplainBlackHoodie
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainBlackHoodie
|
||||
equipment:
|
||||
outerClothing: ClothingOuterHoodieBlack
|
||||
|
||||
- type: loadout
|
||||
id: ChaplainHoodie
|
||||
equipment: ChaplainHoodie
|
||||
|
||||
- type: startingGear
|
||||
id: ChaplainHoodie
|
||||
equipment:
|
||||
outerClothing: ClothingOuterHoodieChaplain
|
||||
57
Resources/Prototypes/Loadouts/Jobs/Civilian/chef.yml
Normal file
57
Resources/Prototypes/Loadouts/Jobs/Civilian/chef.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
# Head
|
||||
- type: loadout
|
||||
id: ChefHead
|
||||
equipment: ChefHead
|
||||
|
||||
- type: startingGear
|
||||
id: ChefHead
|
||||
equipment:
|
||||
head: ClothingHeadHatChef
|
||||
|
||||
# Mask
|
||||
- type: loadout
|
||||
id: ChefMask
|
||||
equipment: ChefMask
|
||||
|
||||
- type: startingGear
|
||||
id: ChefMask
|
||||
equipment:
|
||||
mask: ClothingMaskItalianMoustache
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: ChefJumpsuit
|
||||
equipment: ChefJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: ChefJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitChef
|
||||
|
||||
- type: loadout
|
||||
id: ChefJumpskirt
|
||||
equipment: ChefJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: ChefJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtChef
|
||||
|
||||
# Outer clothing
|
||||
- type: loadout
|
||||
id: ChefApron
|
||||
equipment: ChefApron
|
||||
|
||||
- type: startingGear
|
||||
id: ChefApron
|
||||
equipment:
|
||||
outerClothing: ClothingOuterApronChef
|
||||
|
||||
- type: loadout
|
||||
id: ChefJacket
|
||||
equipment: ChefJacket
|
||||
|
||||
- type: startingGear
|
||||
id: ChefJacket
|
||||
equipment:
|
||||
outerClothing: ClothingOuterJacketChef
|
||||
75
Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml
Normal file
75
Resources/Prototypes/Loadouts/Jobs/Civilian/clown.yml
Normal file
@@ -0,0 +1,75 @@
|
||||
# Head
|
||||
- type: loadout
|
||||
id: JesterHat
|
||||
equipment: JesterHat
|
||||
|
||||
- type: startingGear
|
||||
id: JesterHat
|
||||
equipment:
|
||||
head: ClothingHeadHatJesterAlt
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: ClownSuit
|
||||
equipment: ClownSuit
|
||||
|
||||
- type: startingGear
|
||||
id: ClownSuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitClown
|
||||
|
||||
- type: loadout
|
||||
id: JesterSuit
|
||||
equipment: JesterSuit
|
||||
|
||||
- type: startingGear
|
||||
id: JesterSuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitJesterAlt
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: ClownBackpack
|
||||
equipment: ClownBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: ClownBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackClownFilled
|
||||
|
||||
- type: loadout
|
||||
id: ClownSatchel
|
||||
equipment: ClownSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: ClownSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelClownFilled
|
||||
|
||||
- type: loadout
|
||||
id: ClownDuffel
|
||||
equipment: ClownDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: ClownDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelClownFilled
|
||||
|
||||
# Shoes
|
||||
- type: loadout
|
||||
id: ClownShoes
|
||||
equipment: ClownShoes
|
||||
|
||||
- type: startingGear
|
||||
id: ClownShoes
|
||||
equipment:
|
||||
shoes: ClothingShoesClown
|
||||
|
||||
- type: loadout
|
||||
id: JesterShoes
|
||||
equipment: JesterShoes
|
||||
|
||||
- type: startingGear
|
||||
id: JesterShoes
|
||||
equipment:
|
||||
shoes: ClothingShoesJester
|
||||
28
Resources/Prototypes/Loadouts/Jobs/Civilian/janitor.yml
Normal file
28
Resources/Prototypes/Loadouts/Jobs/Civilian/janitor.yml
Normal file
@@ -0,0 +1,28 @@
|
||||
# Head
|
||||
- type: loadout
|
||||
id: JanitorHead
|
||||
equipment: JanitorHead
|
||||
|
||||
- type: startingGear
|
||||
id: JanitorHead
|
||||
equipment:
|
||||
head: ClothingHeadHatPurplesoft
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: JanitorJumpsuit
|
||||
equipment: JanitorJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: JanitorJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitJanitor
|
||||
|
||||
- type: loadout
|
||||
id: JanitorJumpskirt
|
||||
equipment: JanitorJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: JanitorJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtJanitor
|
||||
100
Resources/Prototypes/Loadouts/Jobs/Civilian/lawyer.yml
Normal file
100
Resources/Prototypes/Loadouts/Jobs/Civilian/lawyer.yml
Normal file
@@ -0,0 +1,100 @@
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: LawyerJumpsuit
|
||||
equipment: LawyerJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitLawyerBlack
|
||||
|
||||
- type: loadout
|
||||
id: LawyerJumpskirt
|
||||
equipment: LawyerJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtLawyerBlack
|
||||
|
||||
- type: loadout
|
||||
id: LawyerJumpsuitBlue
|
||||
equipment: LawyerJumpsuitBlue
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerJumpsuitBlue
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitLawyerBlue
|
||||
|
||||
- type: loadout
|
||||
id: LawyerJumpskirtBlue
|
||||
equipment: LawyerJumpskirtBlue
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerJumpskirtBlue
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtLawyerBlue
|
||||
|
||||
- type: loadout
|
||||
id: LawyerJumpsuitPurple
|
||||
equipment: LawyerJumpsuitPurple
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerJumpsuitPurple
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitLawyerPurple
|
||||
|
||||
- type: loadout
|
||||
id: LawyerJumpskirtPurple
|
||||
equipment: LawyerJumpskirtPurple
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerJumpskirtPurple
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtLawyerPurple
|
||||
|
||||
- type: loadout
|
||||
id: LawyerJumpsuitRed
|
||||
equipment: LawyerJumpsuitRed
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerJumpsuitRed
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitLawyerRed
|
||||
|
||||
- type: loadout
|
||||
id: LawyerJumpskirtRed
|
||||
equipment: LawyerJumpskirtRed
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerJumpskirtRed
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtLawyerRed
|
||||
|
||||
- type: loadout
|
||||
id: LawyerJumpsuitGood
|
||||
equipment: LawyerJumpsuitGood
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerJumpsuitGood
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitLawyerGood
|
||||
|
||||
- type: loadout
|
||||
id: LawyerJumpskirtGood
|
||||
equipment: LawyerJumpskirtGood
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerJumpskirtGood
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtLawyerGood
|
||||
|
||||
# Neck
|
||||
- type: loadout
|
||||
id: LawyerNeck
|
||||
equipment: LawyerNeck
|
||||
|
||||
- type: startingGear
|
||||
id: LawyerNeck
|
||||
equipment:
|
||||
neck: ClothingNeckLawyerbadge
|
||||
36
Resources/Prototypes/Loadouts/Jobs/Civilian/librarian.yml
Normal file
36
Resources/Prototypes/Loadouts/Jobs/Civilian/librarian.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: LibrarianJumpsuit
|
||||
equipment: LibrarianJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: LibrarianJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitLibrarian
|
||||
|
||||
- type: loadout
|
||||
id: LibrarianJumpskirt
|
||||
equipment: LibrarianJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: LibrarianJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtLibrarian
|
||||
|
||||
- type: loadout
|
||||
id: CuratorJumpsuit
|
||||
equipment: CuratorJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: CuratorJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitCurator
|
||||
|
||||
- type: loadout
|
||||
id: CuratorJumpskirt
|
||||
equipment: CuratorJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: CuratorJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtCurator
|
||||
102
Resources/Prototypes/Loadouts/Jobs/Civilian/mime.yml
Normal file
102
Resources/Prototypes/Loadouts/Jobs/Civilian/mime.yml
Normal file
@@ -0,0 +1,102 @@
|
||||
# Head
|
||||
- type: loadout
|
||||
id: MimeHead
|
||||
equipment: MimeHead
|
||||
|
||||
- type: startingGear
|
||||
id: MimeHead
|
||||
equipment:
|
||||
head: ClothingHeadHatBeret
|
||||
|
||||
- type: loadout
|
||||
id: MimeFrenchBeret
|
||||
equipment: MimeFrenchBeret
|
||||
|
||||
- type: startingGear
|
||||
id: MimeFrenchBeret
|
||||
equipment:
|
||||
head: ClothingHeadHatBeretFrench
|
||||
|
||||
- type: loadout
|
||||
id: MimeCap
|
||||
equipment: MimeCap
|
||||
|
||||
- type: startingGear
|
||||
id: MimeCap
|
||||
equipment:
|
||||
head: ClothingHeadHatMimesoft
|
||||
|
||||
# Mask
|
||||
- type: loadout
|
||||
id: MimeMask
|
||||
equipment: MimeMask
|
||||
|
||||
- type: startingGear
|
||||
id: MimeMask
|
||||
equipment:
|
||||
mask: ClothingMaskMime
|
||||
|
||||
- type: loadout
|
||||
id: MimeMaskSad
|
||||
equipment: MimeMaskSad
|
||||
|
||||
- type: startingGear
|
||||
id: MimeMaskSad
|
||||
equipment:
|
||||
mask: ClothingMaskSadMime
|
||||
|
||||
- type: loadout
|
||||
id: MimeMaskScared
|
||||
equipment: MimeMaskScared
|
||||
|
||||
- type: startingGear
|
||||
id: MimeMaskScared
|
||||
equipment:
|
||||
mask: ClothingMaskScaredMime
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: MimeJumpsuit
|
||||
equipment: MimeJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: MimeJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitMime
|
||||
|
||||
- type: loadout
|
||||
id: MimeJumpskirt
|
||||
equipment: MimeJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: MimeJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtMime
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: MimeBackpack
|
||||
equipment: MimeBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: MimeBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackMimeFilled
|
||||
|
||||
- type: loadout
|
||||
id: MimeSatchel
|
||||
equipment: MimeSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: MimeSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelMimeFilled
|
||||
|
||||
- type: loadout
|
||||
id: MimeDuffel
|
||||
equipment: MimeDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: MimeDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelMimeFilled
|
||||
27
Resources/Prototypes/Loadouts/Jobs/Civilian/musician.yml
Normal file
27
Resources/Prototypes/Loadouts/Jobs/Civilian/musician.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
# Back
|
||||
- type: loadout
|
||||
id: MusicianBackpack
|
||||
equipment: MusicianBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: MusicianBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackMusicianFilled
|
||||
|
||||
- type: loadout
|
||||
id: MusicianSatchel
|
||||
equipment: MusicianSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: MusicianSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelMusicianFilled
|
||||
|
||||
- type: loadout
|
||||
id: MusicianDuffel
|
||||
equipment: MusicianDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: MusicianDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelMusicianFilled
|
||||
82
Resources/Prototypes/Loadouts/Jobs/Civilian/passenger.yml
Normal file
82
Resources/Prototypes/Loadouts/Jobs/Civilian/passenger.yml
Normal file
@@ -0,0 +1,82 @@
|
||||
# Greytide Time
|
||||
- type: loadoutEffectGroup
|
||||
id: GreyTider
|
||||
effects:
|
||||
- !type:JobRequirementLoadoutEffect
|
||||
requirement:
|
||||
!type:RoleTimeRequirement
|
||||
role: JobPassenger
|
||||
time: 36000 #10 hrs, silly reward for people who play passenger a lot
|
||||
|
||||
# Face
|
||||
- type: loadout
|
||||
id: PassengerFace
|
||||
equipment: GasMask
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: GreyTider
|
||||
|
||||
- type: startingGear
|
||||
id: GasMask
|
||||
equipment:
|
||||
mask: ClothingMaskGas
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: GreyJumpsuit
|
||||
equipment: GreyJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: GreyJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitColorGrey
|
||||
|
||||
- type: loadout
|
||||
id: GreyJumpskirt
|
||||
equipment: GreyJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: GreyJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtColorGrey
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: CommonBackpack
|
||||
equipment: CommonBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: CommonBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackFilled
|
||||
|
||||
- type: loadout
|
||||
id: CommonSatchel
|
||||
equipment: CommonSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: CommonSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelFilled
|
||||
|
||||
- type: loadout
|
||||
id: CommonDuffel
|
||||
equipment: CommonDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: CommonDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelFilled
|
||||
|
||||
# Gloves
|
||||
- type: loadout
|
||||
id: PassengerGloves
|
||||
equipment: FingerlessInsulatedGloves
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: GreyTider
|
||||
|
||||
- type: startingGear
|
||||
id: FingerlessInsulatedGloves
|
||||
equipment:
|
||||
gloves: ClothingHandsGlovesFingerlessInsulated
|
||||
111
Resources/Prototypes/Loadouts/Jobs/Command/captain.yml
Normal file
111
Resources/Prototypes/Loadouts/Jobs/Command/captain.yml
Normal file
@@ -0,0 +1,111 @@
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: CaptainJumpsuit
|
||||
equipment: CaptainJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitCaptain
|
||||
|
||||
- type: loadout
|
||||
id: CaptainJumpskirt
|
||||
equipment: CaptainJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtCaptain
|
||||
|
||||
- type: loadout
|
||||
id: CaptainFormalSuit
|
||||
equipment: CaptainFormalSuit
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainFormalSuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitCapFormal
|
||||
|
||||
- type: loadout
|
||||
id: CaptainFormalSkirt
|
||||
equipment: CaptainFormalSkirt
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainFormalSkirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtCapFormalDress
|
||||
|
||||
# Head
|
||||
- type: loadout
|
||||
id: CaptainHead
|
||||
equipment: CaptainHead
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainHead
|
||||
equipment:
|
||||
head: ClothingHeadHatCaptain
|
||||
|
||||
- type: loadout
|
||||
id: CaptainCap
|
||||
equipment: CaptainCap
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainCap
|
||||
equipment:
|
||||
head: ClothingHeadHatCapcap
|
||||
|
||||
# Neck
|
||||
- type: loadout
|
||||
id: CaptainCloak
|
||||
equipment: CaptainCloak
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainCloak
|
||||
equipment:
|
||||
neck: ClothingNeckCloakCap
|
||||
|
||||
- type: loadout
|
||||
id: CaptainCloakFormal
|
||||
equipment: CaptainCloakFormal
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainCloakFormal
|
||||
equipment:
|
||||
neck: ClothingNeckCloakCapFormal
|
||||
|
||||
- type: loadout
|
||||
id: CaptainMantle
|
||||
equipment: CaptainMantle
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainMantle
|
||||
equipment:
|
||||
neck: ClothingNeckMantleCap
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: CaptainBackpack
|
||||
equipment: CaptainBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackCaptainFilled
|
||||
|
||||
- type: loadout
|
||||
id: CaptainSatchel
|
||||
equipment: CaptainSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelCaptainFilled
|
||||
|
||||
- type: loadout
|
||||
id: CaptainDuffel
|
||||
equipment: CaptainDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: CaptainDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelCaptainFilled
|
||||
@@ -0,0 +1,97 @@
|
||||
# Professional HoP Time
|
||||
- type: loadoutEffectGroup
|
||||
id: ProfessionalHoP
|
||||
effects:
|
||||
- !type:JobRequirementLoadoutEffect
|
||||
requirement:
|
||||
!type:RoleTimeRequirement
|
||||
role: JobHeadOfPersonnel
|
||||
time: 54000 #15 hrs, special reward for HoP mains
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: HoPJumpsuit
|
||||
equipment: HoPJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: HoPJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitHoP
|
||||
|
||||
- type: loadout
|
||||
id: HoPJumpskirt
|
||||
equipment: HoPJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: HoPJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtHoP
|
||||
|
||||
# Head
|
||||
- type: loadout
|
||||
id: HoPHead
|
||||
equipment: HoPHead
|
||||
|
||||
- type: startingGear
|
||||
id: HoPHead
|
||||
equipment:
|
||||
head: ClothingHeadHatHopcap
|
||||
|
||||
# Neck
|
||||
- type: loadout
|
||||
id: HoPCloak
|
||||
equipment: HoPCloak
|
||||
|
||||
- type: startingGear
|
||||
id: HoPCloak
|
||||
equipment:
|
||||
neck: ClothingNeckCloakHop
|
||||
|
||||
- type: loadout
|
||||
id: HoPMantle
|
||||
equipment: HoPMantle
|
||||
|
||||
- type: startingGear
|
||||
id: HoPMantle
|
||||
equipment:
|
||||
neck: ClothingNeckMantleHOP
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: HoPBackpack
|
||||
equipment: HoPBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: HoPBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackHOPFilled
|
||||
|
||||
- type: loadout
|
||||
id: HoPSatchel
|
||||
equipment: HoPSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: HoPSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelHOPFilled
|
||||
|
||||
- type: loadout
|
||||
id: HoPDuffel
|
||||
equipment: HoPDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: HoPDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelHOPFilled
|
||||
|
||||
- type: loadout
|
||||
id: HoPBackpackIan
|
||||
equipment: HoPBackpackIan
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: ProfessionalHoP
|
||||
|
||||
- type: startingGear
|
||||
id: HoPBackpackIan
|
||||
equipment:
|
||||
back: ClothingBackpackHOPIanFilled
|
||||
@@ -0,0 +1,55 @@
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: AtmosphericTechnicianJumpsuit
|
||||
equipment: AtmosphericTechnicianJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: AtmosphericTechnicianJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitAtmos
|
||||
|
||||
- type: loadout
|
||||
id: AtmosphericTechnicianJumpskirt
|
||||
equipment: AtmosphericTechnicianJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: AtmosphericTechnicianJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtAtmos
|
||||
|
||||
- type: loadout
|
||||
id: AtmosphericTechnicianJumpsuitCasual
|
||||
equipment: AtmosphericTechnicianJumpsuitCasual
|
||||
|
||||
- type: startingGear
|
||||
id: AtmosphericTechnicianJumpsuitCasual
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitAtmosCasual
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: AtmosphericTechnicianBackpack
|
||||
equipment: AtmosphericTechnicianBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: AtmosphericTechnicianBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackAtmosphericsFilled
|
||||
|
||||
- type: loadout
|
||||
id: AtmosphericTechnicianSatchel
|
||||
equipment: AtmosphericTechnicianSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: AtmosphericTechnicianSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelEngineeringFilled
|
||||
|
||||
- type: loadout
|
||||
id: AtmosphericTechnicianDuffel
|
||||
equipment: AtmosphericTechnicianDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: AtmosphericTechnicianDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelAtmosphericsFilled
|
||||
@@ -0,0 +1,97 @@
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: ChiefEngineerJumpsuit
|
||||
equipment: ChiefEngineerJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefEngineerJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitChiefEngineer
|
||||
|
||||
- type: loadout
|
||||
id: ChiefEngineerJumpskirt
|
||||
equipment: ChiefEngineerJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefEngineerJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtChiefEngineer
|
||||
|
||||
- type: loadout
|
||||
id: ChiefEngineerTurtleneck
|
||||
equipment: ChiefEngineerTurtleneck
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefEngineerTurtleneck
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitChiefEngineerTurtle
|
||||
|
||||
- type: loadout
|
||||
id: ChiefEngineerTurtleneckSkirt
|
||||
equipment: ChiefEngineerTurtleneckSkirt
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefEngineerTurtleneckSkirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtChiefEngineerTurtle
|
||||
|
||||
# Head
|
||||
- type: loadout
|
||||
id: ChiefEngineerHead
|
||||
equipment: ChiefEngineerHead
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefEngineerHead
|
||||
equipment:
|
||||
head: ClothingHeadHatHardhatWhite
|
||||
|
||||
- type: loadout
|
||||
id: ChiefEngineerBeret
|
||||
equipment: EngineeringBeret
|
||||
|
||||
# Neck
|
||||
- type: loadout
|
||||
id: ChiefEngineerCloak
|
||||
equipment: ChiefEngineerCloak
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefEngineerCloak
|
||||
equipment:
|
||||
neck: ClothingNeckCloakCe
|
||||
|
||||
- type: loadout
|
||||
id: ChiefEngineerMantle
|
||||
equipment: ChiefEngineerMantle
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefEngineerMantle
|
||||
equipment:
|
||||
neck: ClothingNeckMantleCE
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: ChiefEngineerBackpack
|
||||
equipment: ChiefEngineerBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefEngineerBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackChiefEngineerFilled
|
||||
|
||||
- type: loadout
|
||||
id: ChiefEngineerSatchel
|
||||
equipment: ChiefEngineerSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefEngineerSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelChiefEngineerFilled
|
||||
|
||||
- type: loadout
|
||||
id: ChiefEngineerDuffel
|
||||
equipment: ChiefEngineerDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefEngineerDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelChiefEngineerFilled
|
||||
@@ -0,0 +1,189 @@
|
||||
# Senior times
|
||||
- type: loadoutEffectGroup
|
||||
id: SeniorEngineering
|
||||
effects:
|
||||
- !type:JobRequirementLoadoutEffect
|
||||
requirement:
|
||||
!type:RoleTimeRequirement
|
||||
role: JobAtmosphericTechnician
|
||||
time: 21600 #6 hrs
|
||||
- !type:JobRequirementLoadoutEffect
|
||||
requirement:
|
||||
!type:RoleTimeRequirement
|
||||
role: JobStationEngineer
|
||||
time: 21600 #6 hrs
|
||||
- !type:JobRequirementLoadoutEffect
|
||||
requirement:
|
||||
!type:DepartmentTimeRequirement
|
||||
department: Engineering
|
||||
time: 216000 # 60 hrs
|
||||
|
||||
# Head
|
||||
- type: loadout
|
||||
id: StationEngineerHead
|
||||
equipment: StationEngineerHead
|
||||
|
||||
- type: startingGear
|
||||
id: StationEngineerHead
|
||||
equipment:
|
||||
head: ClothingHeadHatHardhatYellow
|
||||
|
||||
- type: loadout
|
||||
id: SeniorEngineerBeret
|
||||
equipment: EngineeringBeret
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorEngineering
|
||||
|
||||
- type: startingGear
|
||||
id: EngineeringBeret
|
||||
equipment:
|
||||
head: ClothingHeadHatBeretEngineering
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: StationEngineerJumpsuit
|
||||
equipment: StationEngineerJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: StationEngineerJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitEngineering
|
||||
|
||||
- type: loadout
|
||||
id: StationEngineerJumpskirt
|
||||
equipment: StationEngineerJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: StationEngineerJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtEngineering
|
||||
|
||||
- type: loadout
|
||||
id: StationEngineerHazardsuit
|
||||
equipment: StationEngineerHazardsuit
|
||||
|
||||
- type: startingGear
|
||||
id: StationEngineerHazardsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitEngineeringHazard
|
||||
|
||||
- type: loadout
|
||||
id: SeniorEngineerJumpsuit
|
||||
equipment: SeniorEngineerJumpsuit
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorEngineering
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorEngineerJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitSeniorEngineer
|
||||
|
||||
- type: loadout
|
||||
id: SeniorEngineerJumpskirt
|
||||
equipment: SeniorEngineerJumpskirt
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorEngineering
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorEngineerJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtSeniorEngineer
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: StationEngineerBackpack
|
||||
equipment: StationEngineerBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: StationEngineerBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackEngineeringFilled
|
||||
|
||||
- type: loadout
|
||||
id: StationEngineerSatchel
|
||||
equipment: StationEngineerSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: StationEngineerSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelEngineeringFilled
|
||||
|
||||
- type: loadout
|
||||
id: StationEngineerDuffel
|
||||
equipment: StationEngineerDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: StationEngineerDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelEngineeringFilled
|
||||
|
||||
- type: loadout
|
||||
id: SeniorEngineerBackpack
|
||||
equipment: SeniorEngineerBackpack
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorEngineering
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorEngineerBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackEngineeringFilled
|
||||
|
||||
- type: loadout
|
||||
id: SeniorEngineerSatchel
|
||||
equipment: SeniorEngineerSatchel
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorEngineering
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorEngineerSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelEngineeringFilled
|
||||
|
||||
- type: loadout
|
||||
id: SeniorEngineerDuffel
|
||||
equipment: SeniorEngineerDuffel
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorEngineering
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorEngineerDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelEngineeringFilled
|
||||
|
||||
# OuterClothing
|
||||
- type: loadout
|
||||
id: StationEngineerOuterVest
|
||||
equipment: StationEngineerOuterVest
|
||||
|
||||
- type: startingGear
|
||||
id: StationEngineerOuterVest
|
||||
equipment:
|
||||
outerClothing: ClothingOuterVestHazard
|
||||
|
||||
# ID
|
||||
- type: loadout
|
||||
id: StationEngineerPDA
|
||||
equipment: StationEngineerPDA
|
||||
|
||||
- type: startingGear
|
||||
id: StationEngineerPDA
|
||||
equipment:
|
||||
id: EngineerPDA
|
||||
|
||||
- type: loadout
|
||||
id: SeniorEngineerPDA
|
||||
equipment: SeniorEngineerPDA
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorEngineering
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorEngineerPDA
|
||||
equipment:
|
||||
id: SeniorEngineerPDA
|
||||
@@ -0,0 +1,18 @@
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: YellowJumpsuit
|
||||
equipment: YellowJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: YellowJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitColorYellow
|
||||
|
||||
- type: loadout
|
||||
id: YellowJumpskirt
|
||||
equipment: YellowJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: YellowJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtColorYellow
|
||||
56
Resources/Prototypes/Loadouts/Jobs/Medical/chemist.yml
Normal file
56
Resources/Prototypes/Loadouts/Jobs/Medical/chemist.yml
Normal file
@@ -0,0 +1,56 @@
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: ChemistJumpsuit
|
||||
equipment: ChemistJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: ChemistJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitChemistry
|
||||
|
||||
- type: loadout
|
||||
id: ChemistJumpskirt
|
||||
equipment: ChemistJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: ChemistJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtChemistry
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: ChemistBackpack
|
||||
equipment: ChemistBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: ChemistBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackChemistryFilled
|
||||
|
||||
- type: loadout
|
||||
id: ChemistSatchel
|
||||
equipment: ChemistSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: ChemistSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelChemistryFilled
|
||||
|
||||
- type: loadout
|
||||
id: ChemistDuffel
|
||||
equipment: ChemistDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: ChemistDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelChemistryFilled
|
||||
|
||||
# Outer clothing
|
||||
- type: loadout
|
||||
id: ChemistLabCoat
|
||||
equipment: ChemistLabCoat
|
||||
|
||||
- type: startingGear
|
||||
id: ChemistLabCoat
|
||||
equipment:
|
||||
outerClothing: ClothingOuterCoatLabChem
|
||||
@@ -0,0 +1,85 @@
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: ChiefMedicalOfficerJumpsuit
|
||||
equipment: ChiefMedicalOfficerJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefMedicalOfficerJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitCMO
|
||||
|
||||
- type: loadout
|
||||
id: ChiefMedicalOfficerJumpskirt
|
||||
equipment: ChiefMedicalOfficerJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefMedicalOfficerJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtCMO
|
||||
|
||||
# Head
|
||||
- type: loadout
|
||||
id: ChiefMedicalOfficerBeret
|
||||
equipment: ChiefMedicalOfficerBeret
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefMedicalOfficerBeret
|
||||
equipment:
|
||||
head: ClothingHeadHatBeretCmo
|
||||
|
||||
# Neck
|
||||
- type: loadout
|
||||
id: ChiefMedicalOfficerCloak
|
||||
equipment: ChiefMedicalOfficerCloak
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefMedicalOfficerCloak
|
||||
equipment:
|
||||
neck: ClothingCloakCmo
|
||||
|
||||
- type: loadout
|
||||
id: ChiefMedicalOfficerMantle
|
||||
equipment: ChiefMedicalOfficerMantle
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefMedicalOfficerMantle
|
||||
equipment:
|
||||
neck: ClothingNeckMantleCMO
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: ChiefMedicalOfficerBackpack
|
||||
equipment: ChiefMedicalOfficerBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefMedicalOfficerBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackCMOFilled
|
||||
|
||||
- type: loadout
|
||||
id: ChiefMedicalOfficerSatchel
|
||||
equipment: ChiefMedicalOfficerSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefMedicalOfficerSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelCMOFilled
|
||||
|
||||
- type: loadout
|
||||
id: ChiefMedicalOfficerDuffel
|
||||
equipment: ChiefMedicalOfficerDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefMedicalOfficerDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelCMOFilled
|
||||
|
||||
# Outer clothing
|
||||
- type: loadout
|
||||
id: ChiefMedicalOfficerLabCoat
|
||||
equipment: ChiefMedicalOfficerLabCoat
|
||||
|
||||
- type: startingGear
|
||||
id: ChiefMedicalOfficerLabCoat
|
||||
equipment:
|
||||
outerClothing: ClothingOuterCoatLabCmo
|
||||
239
Resources/Prototypes/Loadouts/Jobs/Medical/medical_doctor.yml
Normal file
239
Resources/Prototypes/Loadouts/Jobs/Medical/medical_doctor.yml
Normal file
@@ -0,0 +1,239 @@
|
||||
# Senior Time
|
||||
- type: loadoutEffectGroup
|
||||
id: SeniorPhysician
|
||||
effects:
|
||||
- !type:JobRequirementLoadoutEffect
|
||||
requirement:
|
||||
!type:RoleTimeRequirement
|
||||
role: JobChemist
|
||||
time: 21600 #6 hrs
|
||||
- !type:JobRequirementLoadoutEffect
|
||||
requirement:
|
||||
!type:RoleTimeRequirement
|
||||
role: JobMedicalDoctor
|
||||
time: 21600 #6 hrs
|
||||
- !type:JobRequirementLoadoutEffect
|
||||
requirement:
|
||||
!type:DepartmentTimeRequirement
|
||||
department: Medical
|
||||
time: 216000 # 60 hrs
|
||||
|
||||
# Head
|
||||
|
||||
- type: loadout
|
||||
id: SeniorPhysicianBeret
|
||||
equipment: SeniorPhysicianBeret
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorPhysician
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorPhysicianBeret
|
||||
equipment:
|
||||
head: ClothingHeadHatBeretSeniorPhysician
|
||||
|
||||
- type: loadout
|
||||
id: MedicalBeret
|
||||
equipment: MedicalBeret
|
||||
|
||||
- type: startingGear
|
||||
id: MedicalBeret
|
||||
equipment:
|
||||
head: ClothingHeadHatBeretMedic
|
||||
|
||||
- type: loadout
|
||||
id: BlueSurgeryCap
|
||||
equipment: BlueSurgeryCap
|
||||
|
||||
- type: startingGear
|
||||
id: BlueSurgeryCap
|
||||
equipment:
|
||||
head: ClothingHeadHatSurgcapBlue
|
||||
|
||||
- type: loadout
|
||||
id: GreenSurgeryCap
|
||||
equipment: GreenSurgeryCap
|
||||
|
||||
- type: startingGear
|
||||
id: GreenSurgeryCap
|
||||
equipment:
|
||||
head: ClothingHeadHatSurgcapGreen
|
||||
|
||||
- type: loadout
|
||||
id: PurpleSurgeryCap
|
||||
equipment: PurpleSurgeryCap
|
||||
|
||||
- type: startingGear
|
||||
id: PurpleSurgeryCap
|
||||
equipment:
|
||||
head: ClothingHeadHatSurgcapPurple
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: MedicalDoctorJumpsuit
|
||||
equipment: MedicalDoctorJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: MedicalDoctorJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitMedicalDoctor
|
||||
|
||||
- type: loadout
|
||||
id: MedicalDoctorJumpskirt
|
||||
equipment: MedicalDoctorJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: MedicalDoctorJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtMedicalDoctor
|
||||
|
||||
- type: loadout
|
||||
id: SeniorPhysicianJumpsuit
|
||||
equipment: SeniorPhysicianJumpsuit
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorPhysician
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorPhysicianJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitSeniorPhysician
|
||||
|
||||
- type: loadout
|
||||
id: SeniorPhysicianJumpskirt
|
||||
equipment: SeniorPhysicianJumpskirt
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorPhysician
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorPhysicianJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtSeniorPhysician
|
||||
|
||||
- type: loadout
|
||||
id: MedicalBlueScrubs
|
||||
equipment: MedicalBlueScrubs
|
||||
|
||||
- type: startingGear
|
||||
id: MedicalBlueScrubs
|
||||
equipment:
|
||||
jumpsuit: UniformScrubsColorBlue
|
||||
|
||||
- type: loadout
|
||||
id: MedicalGreenScrubs
|
||||
equipment: MedicalGreenScrubs
|
||||
|
||||
- type: startingGear
|
||||
id: MedicalGreenScrubs
|
||||
equipment:
|
||||
jumpsuit: UniformScrubsColorGreen
|
||||
|
||||
- type: loadout
|
||||
id: MedicalPurpleScrubs
|
||||
equipment: MedicalPurpleScrubs
|
||||
|
||||
- type: startingGear
|
||||
id: MedicalPurpleScrubs
|
||||
equipment:
|
||||
jumpsuit: UniformScrubsColorPurple
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: MedicalDoctorBackpack
|
||||
equipment: MedicalDoctorBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: MedicalDoctorBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackMedicalFilled
|
||||
|
||||
- type: loadout
|
||||
id: MedicalDoctorSatchel
|
||||
equipment: MedicalDoctorSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: MedicalDoctorSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelMedicalFilled
|
||||
|
||||
- type: loadout
|
||||
id: MedicalDoctorDuffel
|
||||
equipment: MedicalDoctorDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: MedicalDoctorDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelMedicalFilled
|
||||
|
||||
- type: loadout
|
||||
id: SeniorPhysicianBackpack
|
||||
equipment: SeniorPhysicianBackpack
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorPhysician
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorPhysicianBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelMedicalFilled
|
||||
|
||||
- type: loadout
|
||||
id: SeniorPhysicianSatchel
|
||||
equipment: SeniorPhysicianSatchel
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorPhysician
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorPhysicianSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelMedicalFilled
|
||||
|
||||
- type: loadout
|
||||
id: SeniorPhysicianDuffel
|
||||
equipment: SeniorPhysicianDuffel
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorPhysician
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorPhysicianDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelMedicalFilled
|
||||
|
||||
# OuterClothing
|
||||
|
||||
- type: loadout
|
||||
id: SeniorPhysicianLabCoat
|
||||
equipment: SeniorPhysicianLabCoat
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorPhysician
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorPhysicianLabCoat
|
||||
equipment:
|
||||
outerClothing: ClothingOuterCoatLabSeniorPhysician
|
||||
|
||||
# ID
|
||||
- type: loadout
|
||||
id: MedicalDoctorPDA
|
||||
equipment: MedicalDoctorPDA
|
||||
|
||||
- type: startingGear
|
||||
id: MedicalDoctorPDA
|
||||
equipment:
|
||||
id: MedicalPDA
|
||||
|
||||
- type: loadout
|
||||
id: SeniorPhysicianPDA
|
||||
equipment: SeniorPhysicianPDA
|
||||
effects:
|
||||
- !type:GroupLoadoutEffect
|
||||
proto: SeniorPhysician
|
||||
|
||||
- type: startingGear
|
||||
id: SeniorPhysicianPDA
|
||||
equipment:
|
||||
id: SeniorPhysicianPDA
|
||||
@@ -0,0 +1,18 @@
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: WhiteJumpsuit
|
||||
equipment: WhiteJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: WhiteJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitColorWhite
|
||||
|
||||
- type: loadout
|
||||
id: WhiteJumpskirt
|
||||
equipment: WhiteJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: WhiteJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtColorWhite
|
||||
66
Resources/Prototypes/Loadouts/Jobs/Medical/paramedic.yml
Normal file
66
Resources/Prototypes/Loadouts/Jobs/Medical/paramedic.yml
Normal file
@@ -0,0 +1,66 @@
|
||||
# Head
|
||||
- type: loadout
|
||||
id: ParamedicHead
|
||||
equipment: ParamedicHead
|
||||
|
||||
- type: startingGear
|
||||
id: ParamedicHead
|
||||
equipment:
|
||||
head: ClothingHeadHatParamedicsoft
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: ParamedicJumpsuit
|
||||
equipment: ParamedicJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: ParamedicJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitParamedic
|
||||
|
||||
- type: loadout
|
||||
id: ParamedicJumpskirt
|
||||
equipment: ParamedicJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: ParamedicJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtParamedic
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: ParamedicBackpack
|
||||
equipment: ParamedicBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: ParamedicBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackParamedicFilled
|
||||
|
||||
- type: loadout
|
||||
id: ParamedicSatchel
|
||||
equipment: ParamedicSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: ParamedicSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelParamedicFilled
|
||||
|
||||
- type: loadout
|
||||
id: ParamedicDuffel
|
||||
equipment: ParamedicDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: ParamedicDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelParamedicFilled
|
||||
|
||||
# Outer clothing
|
||||
- type: loadout
|
||||
id: ParamedicWindbreaker
|
||||
equipment: ParamedicWindbreaker
|
||||
|
||||
- type: startingGear
|
||||
id: ParamedicWindbreaker
|
||||
equipment:
|
||||
outerClothing: ClothingOuterCoatParamedicWB
|
||||
@@ -0,0 +1,82 @@
|
||||
# Head
|
||||
|
||||
- type: loadout
|
||||
id: ResearchDirectorBeret
|
||||
equipment: ScientificBeret
|
||||
|
||||
# Neck
|
||||
|
||||
- type: loadout
|
||||
id: ResearchDirectorMantle
|
||||
equipment: ResearchDirectorMantle
|
||||
|
||||
- type: startingGear
|
||||
id: ResearchDirectorMantle
|
||||
equipment:
|
||||
neck: ClothingNeckMantleRD
|
||||
|
||||
- type: loadout
|
||||
id: ResearchDirectorCloak
|
||||
equipment: ResearchDirectorCloak
|
||||
|
||||
- type: startingGear
|
||||
id: ResearchDirectorCloak
|
||||
equipment:
|
||||
neck: ClothingNeckCloakRd
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: ResearchDirectorJumpsuit
|
||||
equipment: ResearchDirectorJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: ResearchDirectorJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitResearchDirector
|
||||
|
||||
- type: loadout
|
||||
id: ResearchDirectorJumpskirt
|
||||
equipment: ResearchDirectorJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: ResearchDirectorJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtResearchDirector
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: ResearchDirectorBackpack
|
||||
equipment: ResearchDirectorBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: ResearchDirectorBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackResearchDirectorFilled
|
||||
|
||||
- type: loadout
|
||||
id: ResearchDirectorSatchel
|
||||
equipment: ResearchDirectorSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: ResearchDirectorSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelResearchDirectorFilled
|
||||
|
||||
- type: loadout
|
||||
id: ResearchDirectorDuffel
|
||||
equipment: ResearchDirectorDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: ResearchDirectorDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelResearchDirectorFilled
|
||||
|
||||
# OuterClothing
|
||||
- type: loadout
|
||||
id: ResearchDirectorLabCoat
|
||||
equipment: ResearchDirectorLabCoat
|
||||
|
||||
- type: startingGear
|
||||
id: ResearchDirectorLabCoat
|
||||
equipment:
|
||||
outerClothing: ClothingOuterCoatRD
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user