Merge pull request #1122 from DeltaV-Station/2024/04/21-loadouts
Loadouts
This commit is contained in:
commit
f9ec70d258
|
|
@ -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);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@
|
|||
<ItemGroup>
|
||||
<Folder Include="Spawners\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Lobby\UI\LobbyCharacterPreviewPanel.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\RobustToolbox\MSBuild\XamlIL.targets" />
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,13 +64,19 @@ namespace Content.Client.Lobby
|
|||
|
||||
_characterSetup.CloseButton.OnPressed += _ =>
|
||||
{
|
||||
// Reset sliders etc.
|
||||
_characterSetup?.UpdateControls();
|
||||
|
||||
var controller = _userInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.SetClothes(true);
|
||||
controller.UpdateProfile();
|
||||
_lobby.SwitchState(LobbyGui.LobbyGuiState.Default);
|
||||
};
|
||||
|
||||
_characterSetup.SaveButton.OnPressed += _ =>
|
||||
{
|
||||
_characterSetup.Save();
|
||||
_lobby.CharacterPreview.UpdateUI();
|
||||
_userInterfaceManager.GetUIController<LobbyUIController>().ReloadProfile();
|
||||
};
|
||||
|
||||
LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide);
|
||||
|
|
@ -84,10 +90,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 +111,6 @@ namespace Content.Client.Lobby
|
|||
|
||||
_characterSetup?.Dispose();
|
||||
_characterSetup = null;
|
||||
|
||||
_preferencesManager.OnServerDataLoaded -= PreferencesDataLoaded;
|
||||
}
|
||||
|
||||
private void PreferencesDataLoaded()
|
||||
{
|
||||
_lobby?.CharacterPreview.UpdateUI();
|
||||
}
|
||||
|
||||
private void OnSetupPressed(BaseButton.ButtonEventArgs args)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,286 @@
|
|||
using System.Linq;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Inventory;
|
||||
using Content.Client.Lobby.UI;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.Preferences.UI;
|
||||
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;
|
||||
|
||||
private bool _showClothes = true;
|
||||
|
||||
/*
|
||||
* Each character profile has its own dummy. There is also a dummy for the lobby screen + character editor
|
||||
* that is shared too.
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// Preview dummy for role gear.
|
||||
/// </summary>
|
||||
private EntityUid? _previewDummy;
|
||||
|
||||
/// <summary>
|
||||
/// If we currently have a job prototype selected.
|
||||
/// </summary>
|
||||
private JobPrototype? _dummyJob;
|
||||
|
||||
// TODO: Load the species directly and don't update entity ever.
|
||||
public event Action<EntityUid>? PreviewDummyUpdated;
|
||||
|
||||
private HumanoidCharacterProfile? _profile;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_preferencesManager.OnServerDataLoaded += PreferencesDataLoaded;
|
||||
}
|
||||
|
||||
private void PreferencesDataLoaded()
|
||||
{
|
||||
UpdateProfile();
|
||||
}
|
||||
|
||||
public void OnStateEntered(LobbyState state)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnStateExited(LobbyState state)
|
||||
{
|
||||
EntityManager.DeleteEntity(_previewDummy);
|
||||
_previewDummy = null;
|
||||
}
|
||||
|
||||
public void SetPreviewPanel(LobbyCharacterPreviewPanel? panel)
|
||||
{
|
||||
_previewPanel = panel;
|
||||
ReloadProfile();
|
||||
}
|
||||
|
||||
public void SetClothes(bool value)
|
||||
{
|
||||
if (_showClothes == value)
|
||||
return;
|
||||
|
||||
_showClothes = value;
|
||||
ReloadCharacterUI();
|
||||
}
|
||||
|
||||
public void SetDummyJob(JobPrototype? job)
|
||||
{
|
||||
_dummyJob = job;
|
||||
ReloadCharacterUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the character only with the specified profile change.
|
||||
/// </summary>
|
||||
public void ReloadProfile()
|
||||
{
|
||||
// Test moment
|
||||
if (_profile == null || _stateManager.CurrentState is not LobbyState)
|
||||
return;
|
||||
|
||||
// Ignore job clothes and the likes so we don't spam entities out every frame of color changes.
|
||||
var previewDummy = EnsurePreviewDummy(_profile);
|
||||
_humanoid.LoadProfile(previewDummy, _profile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the currently selected character's preview.
|
||||
/// </summary>
|
||||
public void ReloadCharacterUI()
|
||||
{
|
||||
// Test moment
|
||||
if (_profile == null || _stateManager.CurrentState is not LobbyState)
|
||||
return;
|
||||
|
||||
EntityManager.DeleteEntity(_previewDummy);
|
||||
_previewDummy = null;
|
||||
_previewDummy = EnsurePreviewDummy(_profile);
|
||||
_previewPanel?.SetSprite(_previewDummy.Value);
|
||||
_previewPanel?.SetSummaryText(_profile.Summary);
|
||||
_humanoid.LoadProfile(_previewDummy.Value, _profile);
|
||||
|
||||
if (_showClothes)
|
||||
GiveDummyJobClothesLoadout(_previewDummy.Value, _profile);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates character profile to the default.
|
||||
/// </summary>
|
||||
public void UpdateProfile()
|
||||
{
|
||||
if (!_preferencesManager.ServerDataLoaded)
|
||||
{
|
||||
_profile = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_preferencesManager.Preferences?.SelectedCharacter is HumanoidCharacterProfile selectedCharacter)
|
||||
{
|
||||
_profile = selectedCharacter;
|
||||
_previewPanel?.SetLoaded(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
_previewPanel?.SetSummaryText(string.Empty);
|
||||
_previewPanel?.SetLoaded(false);
|
||||
}
|
||||
|
||||
ReloadCharacterUI();
|
||||
}
|
||||
|
||||
public void UpdateProfile(HumanoidCharacterProfile? profile)
|
||||
{
|
||||
if (_profile?.Equals(profile) == true)
|
||||
return;
|
||||
|
||||
if (_stateManager.CurrentState is not LobbyState)
|
||||
return;
|
||||
|
||||
_profile = profile;
|
||||
}
|
||||
|
||||
private EntityUid EnsurePreviewDummy(HumanoidCharacterProfile profile)
|
||||
{
|
||||
if (_previewDummy != null)
|
||||
return _previewDummy.Value;
|
||||
|
||||
_previewDummy = EntityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(profile.Species).DollPrototype, MapCoordinates.Nullspace);
|
||||
PreviewDummyUpdated?.Invoke(_previewDummy.Value);
|
||||
return _previewDummy.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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 partial class JobRequirementsManager
|
||||
public sealed partial class JobRequirementsManager : ISharedPlaytimeManager
|
||||
{
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IClientNetManager _net = default!;
|
||||
|
|
@ -134,5 +135,13 @@ public sealed partial 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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
@ -103,6 +97,12 @@ namespace Content.Client.Preferences.UI
|
|||
UpdateUI();
|
||||
}
|
||||
|
||||
public void UpdateControls()
|
||||
{
|
||||
// Reset sliders etc. upon going going back to GUI.
|
||||
_humanoidProfileEditor.LoadServerData();
|
||||
}
|
||||
|
||||
private void UpdateUI()
|
||||
{
|
||||
var numberOfFullSlots = 0;
|
||||
|
|
@ -120,11 +120,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,
|
||||
|
|
@ -140,6 +135,9 @@ namespace Content.Client.Preferences.UI
|
|||
_humanoidProfileEditor.CharacterSlot = characterIndexCopy;
|
||||
_humanoidProfileEditor.UpdateControls();
|
||||
_preferencesManager.SelectCharacter(character);
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.UpdateProfile(_humanoidProfileEditor.Profile);
|
||||
controller.ReloadCharacterUI();
|
||||
UpdateUI();
|
||||
args.Event.Handle();
|
||||
};
|
||||
|
|
@ -148,8 +146,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 +182,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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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 =>
|
||||
|
|
@ -220,7 +190,7 @@ namespace Content.Client.Preferences.UI
|
|||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithHairStyleName(newStyle.id));
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
_hairPicker.OnColorChanged += newColor =>
|
||||
|
|
@ -230,7 +200,7 @@ namespace Content.Client.Preferences.UI
|
|||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithHairColor(newColor.marking.MarkingColors[0]));
|
||||
UpdateCMarkingsHair();
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
_facialHairPicker.OnMarkingSelect += newStyle =>
|
||||
|
|
@ -239,7 +209,7 @@ namespace Content.Client.Preferences.UI
|
|||
return;
|
||||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithFacialHairStyleName(newStyle.id));
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
_facialHairPicker.OnColorChanged += newColor =>
|
||||
|
|
@ -249,7 +219,7 @@ namespace Content.Client.Preferences.UI
|
|||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithFacialHairColor(newColor.marking.MarkingColors[0]));
|
||||
UpdateCMarkingsFacialHair();
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
_hairPicker.OnSlotRemove += _ =>
|
||||
|
|
@ -261,7 +231,7 @@ namespace Content.Client.Preferences.UI
|
|||
);
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsHair();
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
_facialHairPicker.OnSlotRemove += _ =>
|
||||
|
|
@ -273,7 +243,7 @@ namespace Content.Client.Preferences.UI
|
|||
);
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsFacialHair();
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
_hairPicker.OnSlotAdd += delegate()
|
||||
|
|
@ -293,7 +263,7 @@ namespace Content.Client.Preferences.UI
|
|||
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsHair();
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
_facialHairPicker.OnSlotAdd += delegate()
|
||||
|
|
@ -313,38 +283,11 @@ namespace Content.Client.Preferences.UI
|
|||
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsFacialHair();
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
#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>())
|
||||
|
|
@ -369,7 +312,7 @@ namespace Content.Client.Preferences.UI
|
|||
Profile = Profile.WithCharacterAppearance(
|
||||
Profile.Appearance.WithEyeColor(newColor));
|
||||
CMarkings.CurrentEyeColor = Profile.Appearance.EyeColor;
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
#endregion Eyes
|
||||
|
|
@ -393,46 +336,22 @@ namespace Content.Client.Preferences.UI
|
|||
_preferenceUnavailableButton.SelectId(args.Id);
|
||||
|
||||
Profile = Profile?.WithPreferenceUnavailable((PreferenceUnavailableMode) args.Id);
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
_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();
|
||||
|
|
@ -450,7 +369,7 @@ namespace Content.Client.Preferences.UI
|
|||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithTraitPreference(trait.ID, preference);
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -525,6 +436,13 @@ namespace Content.Client.Preferences.UI
|
|||
LoadServerData();
|
||||
}
|
||||
|
||||
ShowClothes.OnToggled += args =>
|
||||
{
|
||||
var lobby = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
lobby.SetClothes(args.Pressed);
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
preferencesManager.OnServerDataLoaded += LoadServerData;
|
||||
|
||||
SpeciesInfoButton.OnPressed += OnSpeciesInfoButtonPressed;
|
||||
|
|
@ -532,28 +450,69 @@ namespace Content.Client.Preferences.UI
|
|||
UpdateSpeciesGuidebookIcon();
|
||||
|
||||
IsDirty = false;
|
||||
controller.UpdateProfile();
|
||||
}
|
||||
|
||||
private void SetDirty()
|
||||
{
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.UpdateProfile(Profile);
|
||||
controller.ReloadCharacterUI();
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void OnSpeciesInfoButtonPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
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);
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
selector.PreferenceChanged += preference =>
|
||||
{
|
||||
Profile = Profile?.WithAntagPreference(antag.ID, preference);
|
||||
SetDirty();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void UpdateRoleRequirements()
|
||||
|
|
@ -614,10 +573,19 @@ 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;
|
||||
|
||||
// Clone so we don't modify the underlying loadout.
|
||||
Profile?.Loadouts.TryGetValue(LoadoutSystem.GetJobPrototype(job.ID), out loadout);
|
||||
loadout = loadout?.Clone();
|
||||
var selector = new JobPrioritySelector(loadout, job, jobLoadoutGroup, _prototypeManager)
|
||||
{
|
||||
Margin = new Thickness(3f, 3f, 3f, 0f),
|
||||
};
|
||||
|
||||
if (!_requirements.IsAllowed(job, out var reason))
|
||||
{
|
||||
|
|
@ -626,12 +594,16 @@ namespace Content.Client.Preferences.UI
|
|||
|
||||
category.AddChild(selector);
|
||||
_jobPriorities.Add(selector);
|
||||
EnsureJobRequirementsValid(); // DeltaV
|
||||
|
||||
selector.LoadoutUpdated += args =>
|
||||
{
|
||||
Profile = Profile?.WithLoadout(args);
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
selector.PriorityChanged += priority =>
|
||||
{
|
||||
Profile = Profile?.WithJobPriority(job.ID, priority);
|
||||
IsDirty = true;
|
||||
|
||||
foreach (var jobSelector in _jobPriorities)
|
||||
{
|
||||
|
|
@ -647,6 +619,8 @@ namespace Content.Client.Preferences.UI
|
|||
Profile = Profile?.WithJobPriority(jobSelector.Proto.ID, JobPriority.Medium);
|
||||
}
|
||||
}
|
||||
|
||||
SetDirty();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -658,35 +632,13 @@ namespace Content.Client.Preferences.UI
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// DeltaV - Make sure that no invalid job priorities get through.
|
||||
/// </summary>
|
||||
private void EnsureJobRequirementsValid()
|
||||
{
|
||||
var changed = false;
|
||||
foreach (var selector in _jobPriorities)
|
||||
{
|
||||
if (_requirements.IsAllowed(selector.Proto, out var _) || selector.Priority == JobPriority.Never)
|
||||
continue;
|
||||
|
||||
selector.Priority = JobPriority.Never;
|
||||
Profile = Profile?.WithJobPriority(selector.Proto.ID, JobPriority.Never);
|
||||
changed = true;
|
||||
}
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
_needUpdatePreview = true;
|
||||
Save();
|
||||
}
|
||||
|
||||
private void OnFlavorTextChange(string content)
|
||||
{
|
||||
if (Profile is null)
|
||||
return;
|
||||
|
||||
Profile = Profile.WithFlavorText(content);
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
private void OnMarkingChange(MarkingSet markings)
|
||||
|
|
@ -695,20 +647,12 @@ namespace Content.Client.Preferences.UI
|
|||
return;
|
||||
|
||||
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithMarkings(markings.GetForwardEnumerator().ToList()));
|
||||
_needUpdatePreview = true;
|
||||
IsDirty = true;
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.UpdateProfile(Profile);
|
||||
controller.ReloadProfile();
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -782,6 +726,9 @@ namespace Content.Client.Preferences.UI
|
|||
}
|
||||
|
||||
IsDirty = true;
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.UpdateProfile(Profile);
|
||||
controller.ReloadProfile();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
|
@ -790,40 +737,27 @@ 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()
|
||||
public void LoadServerData()
|
||||
{
|
||||
Profile = (HumanoidCharacterProfile) _preferencesManager.Preferences!.SelectedCharacter;
|
||||
CharacterSlot = _preferencesManager.Preferences.SelectedCharacterIndex;
|
||||
|
||||
UpdateAntagRequirements();
|
||||
UpdateControls();
|
||||
EnsureJobRequirementsValid(); // DeltaV
|
||||
_needUpdatePreview = true;
|
||||
ShowClothes.Pressed = true;
|
||||
}
|
||||
|
||||
private void SetAge(int newAge)
|
||||
{
|
||||
Profile = Profile?.WithAge(newAge);
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
private void SetSex(Sex newSex)
|
||||
|
|
@ -844,13 +778,13 @@ namespace Content.Client.Preferences.UI
|
|||
}
|
||||
UpdateGenderControls();
|
||||
CMarkings.SetSex(newSex);
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
private void SetGender(Gender newGender)
|
||||
{
|
||||
Profile = Profile?.WithGender(newGender);
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
private void SetSpecies(string newSpecies)
|
||||
|
|
@ -859,46 +793,34 @@ 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;
|
||||
SetDirty();
|
||||
UpdatePreview();
|
||||
}
|
||||
|
||||
private void SetName(string newName)
|
||||
{
|
||||
Profile = Profile?.WithName(newName);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetClothing(ClothingPreference newClothing)
|
||||
{
|
||||
Profile = Profile?.WithClothingPreference(newClothing);
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void SetBackpack(BackpackPreference newBackpack)
|
||||
{
|
||||
Profile = Profile?.WithBackpackPreference(newBackpack);
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
private void SetSpawnPriority(SpawnPriorityPreference newSpawnPriority)
|
||||
{
|
||||
Profile = Profile?.WithSpawnPriorityPreference(newSpawnPriority);
|
||||
IsDirty = true;
|
||||
SetDirty();
|
||||
}
|
||||
|
||||
public void Save()
|
||||
{
|
||||
IsDirty = false;
|
||||
|
||||
if (Profile != null)
|
||||
{
|
||||
_preferencesManager.UpdateCharacter(Profile, CharacterSlot);
|
||||
OnProfileChanged?.Invoke(Profile, CharacterSlot);
|
||||
_needUpdatePreview = true;
|
||||
}
|
||||
if (Profile == null)
|
||||
return;
|
||||
|
||||
_preferencesManager.UpdateCharacter(Profile, CharacterSlot);
|
||||
OnProfileChanged?.Invoke(Profile, CharacterSlot);
|
||||
// Reset profile to default.
|
||||
UserInterfaceManager.GetUIController<LobbyUIController>().UpdateProfile();
|
||||
}
|
||||
|
||||
private bool IsDirty
|
||||
|
|
@ -907,7 +829,6 @@ namespace Content.Client.Preferences.UI
|
|||
set
|
||||
{
|
||||
_isDirty = value;
|
||||
_needUpdatePreview = true;
|
||||
UpdateSaveButton();
|
||||
}
|
||||
}
|
||||
|
|
@ -1039,7 +960,7 @@ namespace Content.Client.Preferences.UI
|
|||
if (!_prototypeManager.HasIndex<GuideEntryPrototype>(species))
|
||||
return;
|
||||
|
||||
var style = speciesProto.GuideBookIcon;
|
||||
const string style = "SpeciesInfoDefault";
|
||||
SpeciesInfoButton.StyleClasses.Add(style);
|
||||
}
|
||||
|
||||
|
|
@ -1075,26 +996,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)
|
||||
|
|
@ -1224,13 +1125,13 @@ namespace Content.Client.Preferences.UI
|
|||
if (Profile is null)
|
||||
return;
|
||||
|
||||
var humanoid = _entMan.System<HumanoidAppearanceSystem>();
|
||||
humanoid.LoadProfile(_previewDummy!.Value, Profile);
|
||||
UserInterfaceManager.GetUIController<LobbyUIController>().ReloadProfile();
|
||||
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()
|
||||
|
|
@ -1242,17 +1143,16 @@ namespace Content.Client.Preferences.UI
|
|||
UpdateGenderControls();
|
||||
UpdateSkinColor();
|
||||
UpdateSpecies();
|
||||
UpdateClothingControls();
|
||||
UpdateBackpackControls();
|
||||
UpdateSpawnPriorityControls();
|
||||
UpdateAgeEdit();
|
||||
UpdateEyePickers();
|
||||
UpdateSaveButton();
|
||||
UpdateLoadouts();
|
||||
UpdateRoleRequirements();
|
||||
UpdateJobPriorities();
|
||||
UpdateAntagPreferences();
|
||||
UpdateTraitPreferences();
|
||||
UpdateMarkings();
|
||||
RebuildSpriteView();
|
||||
UpdateHairPickers();
|
||||
UpdateCMarkingsHair();
|
||||
UpdateCMarkingsFacialHair();
|
||||
|
|
@ -1260,17 +1160,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)
|
||||
|
|
@ -1283,143 +1172,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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1444,41 +1201,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; }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.ReloadProfile();
|
||||
LoadoutUpdated?.Invoke(_loadout);
|
||||
};
|
||||
|
||||
_loadoutWindow.OnLoadoutPressed += (selectedGroup, selectedLoadout) =>
|
||||
{
|
||||
if (!_loadout.AddLoadout(selectedGroup, selectedLoadout, protoManager))
|
||||
return;
|
||||
|
||||
_loadout.EnsureValid(session, collection);
|
||||
_loadoutWindow.RefreshLoadouts(_loadout, session, collection);
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.ReloadProfile();
|
||||
LoadoutUpdated?.Invoke(_loadout);
|
||||
};
|
||||
|
||||
_loadoutWindow.OpenCenteredLeft();
|
||||
_loadoutWindow.OnClose += () =>
|
||||
{
|
||||
loadoutWindowBtn.Pressed = false;
|
||||
_loadoutWindow?.Dispose();
|
||||
_loadoutWindow = null;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseLoadout();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
AddChild(loadoutWindowBtn);
|
||||
}
|
||||
|
||||
public void CloseLoadout()
|
||||
{
|
||||
_loadoutWindow?.Close();
|
||||
_loadoutWindow?.Dispose();
|
||||
_loadoutWindow = null;
|
||||
}
|
||||
|
||||
public void LockRequirements(FormattedMessage requirements)
|
||||
{
|
||||
var tooltip = new Tooltip();
|
||||
tooltip.SetMessage(requirements);
|
||||
_lockStripe.TooltipSupplier = _ => tooltip;
|
||||
_lockStripe.Visible = true;
|
||||
Options.Visible = false;
|
||||
}
|
||||
|
||||
// TODO: Subscribe to roletimers event. I am too lazy to do this RN But I doubt most people will notice fn
|
||||
public void UnlockRequirements()
|
||||
{
|
||||
_lockStripe.Visible = false;
|
||||
Options.Visible = true;
|
||||
}
|
||||
|
||||
private Button GenerateButton(string text, int value)
|
||||
{
|
||||
return new Button
|
||||
{
|
||||
Text = text,
|
||||
MinWidth = 90,
|
||||
HorizontalExpand = true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -56,8 +56,26 @@ namespace Content.Server.Database
|
|||
.IsUnique();
|
||||
|
||||
modelBuilder.Entity<Trait>()
|
||||
.HasIndex(p => new {HumanoidProfileId = p.ProfileId, p.TraitName})
|
||||
.IsUnique();
|
||||
.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
|
||||
|
|
|
|||
|
|
@ -144,8 +144,8 @@ public sealed class ParadoxAnomalySystem : EntitySystem
|
|||
|
||||
if (job.StartingGear != null && _proto.TryIndex<StartingGearPrototype>(job.StartingGear, out var gear))
|
||||
{
|
||||
_stationSpawning.EquipStartingGear(spawned, gear, profile);
|
||||
_stationSpawning.EquipIdCard(spawned,
|
||||
_stationSpawning.EquipStartingGear(spawned, gear);
|
||||
_stationSpawning.SetPdaAndIdCardData(spawned,
|
||||
profile.Name,
|
||||
job,
|
||||
station);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -250,7 +250,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>();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,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 partial class PlayTimeTrackingManager
|
||||
public sealed partial class PlayTimeTrackingManager : ISharedPlaytimeManager
|
||||
{
|
||||
[Dependency] private readonly IServerDbManager _db = default!;
|
||||
[Dependency] private readonly IServerNetManager _net = default!;
|
||||
|
|
@ -209,6 +209,11 @@ public sealed partial 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,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;
|
||||
|
|
@ -11,10 +12,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;
|
||||
|
|
@ -89,7 +92,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);
|
||||
|
|
@ -104,9 +107,11 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,7 +142,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)
|
||||
|
|
@ -179,13 +184,52 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
|||
if (prototype?.StartingGear != null)
|
||||
{
|
||||
var startingGear = _prototypeManager.Index<StartingGearPrototype>(prototype.StartingGear);
|
||||
EquipStartingGear(entity.Value, startingGear, profile);
|
||||
if (profile != null)
|
||||
EquipIdCard(entity.Value, profile.Name, prototype, station);
|
||||
EquipStartingGear(entity.Value, startingGear);
|
||||
}
|
||||
|
||||
// Run loadouts after so stuff like storage loadouts can get
|
||||
var jobLoadout = LoadoutSystem.GetJobPrototype(prototype?.ID);
|
||||
|
||||
if (_prototypeManager.TryIndex(jobLoadout, out RoleLoadoutPrototype? roleProto))
|
||||
{
|
||||
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 => roleProto.Groups.FindIndex(e => e == x.Key)))
|
||||
{
|
||||
foreach (var items in group.Value)
|
||||
{
|
||||
if (!_prototypeManager.TryIndex(items.Prototype, out var loadoutProto))
|
||||
{
|
||||
Log.Error($"Unable to find loadout prototype for {items.Prototype}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_prototypeManager.TryIndex(loadoutProto.Equipment, out var startingGear))
|
||||
{
|
||||
Log.Error($"Unable to find starting gear {loadoutProto.Equipment} for loadout {loadoutProto}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle any extra data here.
|
||||
EquipStartingGear(entity.Value, startingGear);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (profile != null)
|
||||
{
|
||||
if (prototype != null)
|
||||
SetPdaAndIdCardData(entity.Value, profile.Name, prototype, station);
|
||||
|
||||
_humanoidSystem.LoadProfile(entity.Value, profile);
|
||||
_metaSystem.SetEntityName(entity.Value, profile.Name);
|
||||
if (profile.FlavorText != "" && _configurationManager.GetCVar(CCVars.FlavorText))
|
||||
|
|
@ -211,13 +255,13 @@ public sealed class StationSpawningSystem : SharedStationSpawningSystem
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equips an ID card and PDA onto the given entity.
|
||||
/// Sets the ID card and PDA name, job, and access data.
|
||||
/// </summary>
|
||||
/// <param name="entity">Entity to load out.</param>
|
||||
/// <param name="characterName">Character name to use for the ID.</param>
|
||||
/// <param name="jobPrototype">Job prototype to use for the PDA and ID.</param>
|
||||
/// <param name="station">The station this player is being spawned on.</param>
|
||||
public void EquipIdCard(EntityUid entity, string characterName, JobPrototype jobPrototype, EntityUid? station)
|
||||
public void SetPdaAndIdCardData(EntityUid entity, string characterName, JobPrototype jobPrototype, EntityUid? station)
|
||||
{
|
||||
if (!InventorySystem.TryGetSlotEntity(entity, "id", out var idUid))
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,10 @@
|
|||
<ItemGroup>
|
||||
<Folder Include="DeltaV\Abilities\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Remove="Preferences\BackpackPreference.cs" />
|
||||
<Compile Remove="Preferences\ClothingPreference.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.CompNetworkGenerator.targets" />
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -43,6 +45,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,
|
||||
|
|
@ -51,13 +58,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;
|
||||
|
|
@ -66,13 +72,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>
|
||||
|
|
@ -80,15 +85,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))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -100,15 +106,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))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -125,8 +130,6 @@ namespace Content.Shared.Preferences
|
|||
Sex.Male,
|
||||
Gender.Male,
|
||||
new HumanoidCharacterAppearance(),
|
||||
ClothingPreference.Jumpsuit,
|
||||
BackpackPreference.Backpack,
|
||||
SpawnPriorityPreference.None,
|
||||
new Dictionary<string, JobPriority>
|
||||
{
|
||||
|
|
@ -134,7 +137,8 @@ namespace Content.Shared.Preferences
|
|||
},
|
||||
PreferenceUnavailableMode.SpawnAsOverflow,
|
||||
new List<string>(),
|
||||
new List<string>())
|
||||
new List<string>(),
|
||||
new Dictionary<string, RoleLoadout>())
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -153,8 +157,6 @@ namespace Content.Shared.Preferences
|
|||
Sex.Male,
|
||||
Gender.Male,
|
||||
HumanoidCharacterAppearance.DefaultWithSpecies(species),
|
||||
ClothingPreference.Jumpsuit,
|
||||
BackpackPreference.Backpack,
|
||||
SpawnPriorityPreference.None,
|
||||
new Dictionary<string, JobPriority>
|
||||
{
|
||||
|
|
@ -162,7 +164,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.
|
||||
|
|
@ -207,11 +210,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; }
|
||||
|
|
@ -231,8 +234,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;
|
||||
|
|
@ -275,21 +276,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)
|
||||
|
|
@ -303,7 +297,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)
|
||||
|
|
@ -313,7 +307,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)
|
||||
|
|
@ -333,7 +327,8 @@ 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)
|
||||
|
|
@ -355,7 +350,7 @@ namespace Content.Shared.Preferences
|
|||
list.Remove(traitId);
|
||||
}
|
||||
}
|
||||
return new(this, _jobPriorities, _antagPreferences, list);
|
||||
return new(this, _jobPriorities, _antagPreferences, list, _loadouts);
|
||||
}
|
||||
|
||||
public string Summary =>
|
||||
|
|
@ -375,17 +370,19 @@ namespace Content.Shared.Preferences
|
|||
if (Gender != other.Gender) return false;
|
||||
if (Species != other.Species) 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;
|
||||
|
|
@ -466,21 +463,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,
|
||||
|
|
@ -513,8 +495,6 @@ namespace Content.Shared.Preferences
|
|||
Sex = sex;
|
||||
Gender = gender;
|
||||
Appearance = appearance;
|
||||
Clothing = clothing;
|
||||
Backpack = backpack;
|
||||
SpawnPriority = spawnPriority;
|
||||
|
||||
_jobPriorities.Clear();
|
||||
|
|
@ -531,12 +511,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;
|
||||
}
|
||||
|
||||
loadouts.EnsureValid(session, collection);
|
||||
}
|
||||
|
||||
foreach (var value in toRemove)
|
||||
{
|
||||
_loadouts.Remove(value);
|
||||
}
|
||||
}
|
||||
|
||||
public ICharacterProfile Validated(IConfigurationManager configManager, IPrototypeManager prototypeManager)
|
||||
public ICharacterProfile Validated(ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
var profile = new HumanoidCharacterProfile(this);
|
||||
profile.EnsureValid(configManager, prototypeManager);
|
||||
profile.EnsureValid(session, collection);
|
||||
return profile;
|
||||
}
|
||||
|
||||
|
|
@ -562,16 +561,49 @@ 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 HumanoidCharacterProfile WithLoadout(RoleLoadout loadout)
|
||||
{
|
||||
// Deep copies so we don't modify the DB profile.
|
||||
var copied = new Dictionary<string, RoleLoadout>();
|
||||
|
||||
foreach (var proto in _loadouts)
|
||||
{
|
||||
if (proto.Key == loadout.Role)
|
||||
continue;
|
||||
|
||||
copied[proto.Key] = proto.Value.Clone();
|
||||
}
|
||||
|
||||
copied[loadout.Role] = loadout.Clone();
|
||||
return new(this, _jobPriorities, _antagPreferences, _traitPreferences, copied);
|
||||
}
|
||||
|
||||
public RoleLoadout GetLoadoutOrDefault(string id, IEntityManager entManager, IPrototypeManager protoManager)
|
||||
{
|
||||
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,30 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Shared.Players;
|
||||
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);
|
||||
var isWhitelisted = session.ContentData()?.Whitelisted ?? false; // DeltaV - Whitelist requirement
|
||||
|
||||
return JobRequirements.TryRequirementMet(Requirement, playtimes, out reason,
|
||||
collection.Resolve<IEntityManager>(),
|
||||
collection.Resolve<IPrototypeManager>(),
|
||||
isWhitelisted); // DeltaV
|
||||
}
|
||||
}
|
||||
|
|
@ -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 partial 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 partial 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();
|
||||
}
|
||||
|
|
@ -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 partial 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();
|
||||
}
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
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;
|
||||
}
|
||||
|
||||
public RoleLoadout Clone()
|
||||
{
|
||||
var weh = new RoleLoadout(Role);
|
||||
|
||||
foreach (var selected in SelectedLoadouts)
|
||||
{
|
||||
weh.SelectedLoadouts.Add(selected.Key, new List<Loadout>(selected.Value));
|
||||
}
|
||||
|
||||
return weh;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 partial 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;
|
||||
}
|
||||
|
|
@ -97,7 +97,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,
|
||||
|
|
@ -164,7 +164,7 @@ namespace Content.Shared.Roles
|
|||
return true;
|
||||
|
||||
reason = FormattedMessage.FromMarkup(Loc.GetString(
|
||||
"role-timer-overall-insufficient",
|
||||
"role-timer-overall-insufficient",
|
||||
("time", Math.Ceiling(overallDiff))));
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,38 +10,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)
|
||||
|| slot == "jumpsuit" && profile.Species == "Harpy" && !string.IsNullOrEmpty(InnerClothingSkirt)) //DeltaV adds this line to prevent Harpies from starting with jumpsuits
|
||||
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,40 +13,69 @@ 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;
|
||||
|
||||
var inhand = startingGear.Inhand;
|
||||
var coords = EntityManager.GetComponent<TransformComponent>(entity).Coordinates;
|
||||
foreach (var prototype in inhand)
|
||||
if (TryComp(entity, out HandsComponent? handsComponent))
|
||||
{
|
||||
var inhandEntity = EntityManager.SpawnEntity(prototype, coords);
|
||||
|
||||
if (_handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent))
|
||||
var inhand = startingGear.Inhand;
|
||||
var coords = EntityManager.GetComponent<TransformComponent>(entity).Coordinates;
|
||||
foreach (var prototype in inhand)
|
||||
{
|
||||
_handsSystem.TryPickup(entity, inhandEntity, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
|
||||
var inhandEntity = EntityManager.SpawnEntity(prototype, coords);
|
||||
|
||||
if (_handsSystem.TryGetEmptyHand(entity, out var emptyHand, handsComponent))
|
||||
{
|
||||
_handsSystem.TryPickup(entity, inhandEntity, emptyHand, checkActionBlocker: false, handsComp: handsComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
job-description-medical-borg = Half-human, Half-machine. Follow your laws, serve the crew, and assist the medical department.
|
||||
job-description-courier = Deliver mail and other packages from and to logistics. Avoid dogs.
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
job-name-medical-borg = Medical Cyborg
|
||||
job-name-courier = Courier
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
# This file will contain strings for both DeltaV and Nyanotrasen loadouts, because I'm lazy
|
||||
|
||||
# Logistics
|
||||
loadout-group-courier-head = Courier head
|
||||
loadout-group-courier-jumpsuit = Courier jumpsuit
|
||||
loadout-group-courier-shoes = Courier shoes
|
||||
loadout-group-courier-outerclothing = Courier outer clothing
|
||||
loadout-group-courier-id = Courier ID
|
||||
|
||||
loadout-group-mail-carrier-head = Mail Carrier head
|
||||
loadout-group-mail-carrier-jumpsuit = Mail Carrier jumpsuit
|
||||
loadout-group-mail-carrier-outerclothing = Mail Carrier outer clothing
|
||||
|
||||
# Epistemics
|
||||
loadout-group-mantis-head = Mantis head
|
||||
loadout-group-mantis-jumpsuit = Mantis jumpsuit
|
||||
loadout-group-mantis-backpack = Mantis backpack
|
||||
loadout-group-mantis-outerclothing = Mantis outer clothing
|
||||
loadout-group-mantis-shoes = Mantis shoes
|
||||
loadout-group-mantis-gloves = Mantis gloves
|
||||
|
||||
# Security
|
||||
loadout-group-brig-medic-head = Brigmedic head
|
||||
loadout-group-brig-medic-jumpsuit = Brigmedic jumpsuit
|
||||
loadout-group-brig-medic-back = Brigmedic backpack
|
||||
|
||||
loadout-group-prison-guard-head = Prison Guard head
|
||||
loadout-group-prison-guard-jumpsuit = Prison Guard jumpsuit
|
||||
|
||||
# Wildcards
|
||||
loadout-group-prisoner-jumpsuit = Prisoner jumpsuit
|
||||
|
||||
loadout-group-martial-artist-jumpsuit = Martial Artist jumpsuit
|
||||
loadout-group-martial-belt = Martial Artist belt
|
||||
loadout-group-martial-artist-shoes = Martial Artist shoes
|
||||
|
||||
loadout-group-gladiator-jumpsuit = Gladiator jumpsuit
|
||||
loadout-group-gladiator-outerclothing = Gladiator outer clothing
|
||||
|
|
@ -45,8 +45,4 @@ job-description-security = Catch criminals and enemies of the station, enforce t
|
|||
job-description-serviceworker = Learn the basics of bartending, cooking, and growing plants.
|
||||
job-description-visitor = Enjoy your visit to the station.
|
||||
job-description-warden = Patrol the security department, ensure that no one is stealing from the armory, and make sure that all prisoners are processed and let out when their time is up.
|
||||
job-description-zookeeper = Put on a joyful display of cute animals and space carps for all the crew to see. Currently unavailable.
|
||||
job-description-senior-engineer = Teach new engineers the basics of the station's engine, repairing, atmospherics and power.
|
||||
job-description-senior-researcher = Teach new scientists the basics of item printing, artifact research and anomalous objects.
|
||||
job-description-senior-physician = Teach new medics the basics of tending to the wounded, chemistry, diagnosing the diseased and disposing of the dead.
|
||||
job-description-senior-officer = Teach new officers the basics of searches, performing arrests, prison times and how to properly shoot a firearm.
|
||||
job-description-zookeeper = Put on a joyful display of cute animals and space carps for all the crew to see. Currently available on Gemini Station.
|
||||
|
|
|
|||
|
|
@ -90,10 +90,6 @@ JobSalvageSpecialist = Salvage Specialist
|
|||
JobScientist = Scientist
|
||||
JobSecurityCadet = Security Cadet
|
||||
JobSecurityOfficer = Security Officer
|
||||
JobSeniorEngineer = Senior Engineer
|
||||
JobSeniorOfficer = Senior Officer
|
||||
JobSeniorPhysician = Senior Physician
|
||||
JobSeniorResearcher = Senior Researcher
|
||||
JobServiceWorker = Service Worker
|
||||
JobStationEngineer = Station Engineer
|
||||
JobTechnicalAssistant = Technical Assistant
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
loadout-window = Loadout
|
||||
loadout-none = None
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
job-name-gladiator = Gladiator
|
||||
job-name-guard = Prison Guard
|
||||
job-name-mail-carrier = Courier
|
||||
job-name-mail-carrier = Mail Carrier
|
||||
job-name-martialartist = Martial Artist
|
||||
job-name-prisoner = Prisoner
|
||||
job-name-mantis = Psionic Mantis
|
||||
|
|
|
|||
|
|
@ -0,0 +1,192 @@
|
|||
# Miscellaneous
|
||||
loadout-group-trinkets = Trinkets
|
||||
loadout-group-glasses = Glasses
|
||||
|
||||
# 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-captain-outerclothing = Captain outer clothing
|
||||
|
||||
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
|
||||
loadout-group-hop-outerclothing = Head of Personnel outer clothing
|
||||
|
||||
# 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-passenger-outerclothing = Passenger outer clothing
|
||||
loadout-group-passenger-shoes = Passenger shoes
|
||||
|
||||
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-lawyer-backpack = Lawyer backpack
|
||||
|
||||
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-janitor-gloves = Janitor gloves
|
||||
loadout-group-janitor-outerclothing = Janitor outer clothing
|
||||
|
||||
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-outerclothing = Clown outer clothing
|
||||
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-mime-outerclothing = Mime outer clothing
|
||||
|
||||
loadout-group-musician-backpack = Musician backpack
|
||||
loadout-group-musician-outerclothing = Musician outer clothing
|
||||
|
||||
# Cargo
|
||||
loadout-group-quartermaster-head = Logistics Officer head
|
||||
loadout-group-quartermaster-jumpsuit = Logistics Officer jumpsuit
|
||||
loadout-group-quartermaster-backpack = Logistics Officer backpack
|
||||
loadout-group-quartermaster-neck = Logistics Officer neck
|
||||
loadout-group-quartermaster-outerclothing = Logistics Officer outer clothing
|
||||
loadout-group-quartermaster-shoes = Logistics Officer shoes
|
||||
|
||||
loadout-group-cargo-technician-head = Cargo Technician head
|
||||
loadout-group-cargo-technician-jumpsuit = Cargo Technician jumpsuit
|
||||
loadout-group-cargo-technician-backpack = Cargo Technician backpack
|
||||
loadout-group-cargo-technician-outerclothing = Cargo Technician outer clothing
|
||||
loadout-group-cargo-technician-shoes = Cargo Technician shoes
|
||||
|
||||
loadout-group-salvage-specialist-backpack = Salvage Specialist backpack
|
||||
loadout-group-salvage-specialist-outerclothing = Salvage Specialist outer clothing
|
||||
loadout-group-salvage-specialist-shoes = Salvage Specialist shoes
|
||||
|
||||
# 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-outerclothing = Chief Engineer outer clothing
|
||||
loadout-group-chief-engineer-neck = Chief Engineer neck
|
||||
loadout-group-chief-engineer-shoes = Chief Engineer shoes
|
||||
|
||||
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-shoes = Station Engineer shoes
|
||||
loadout-group-station-engineer-id = Station Engineer ID
|
||||
|
||||
loadout-group-atmospheric-technician-jumpsuit = Atmospheric Technician jumpsuit
|
||||
loadout-group-atmospheric-technician-backpack = Atmospheric Technician backpack
|
||||
loadout-group-atmospheric-technician-outerclothing = Atmospheric Technician outer clothing
|
||||
loadout-group-atmospheric-technician-shoes = Atmospheric Technician shoes
|
||||
|
||||
# Science
|
||||
loadout-group-research-director-head = Mystagogue head
|
||||
loadout-group-research-director-neck = Mystagogue neck
|
||||
loadout-group-research-director-jumpsuit = Mystagogue jumpsuit
|
||||
loadout-group-research-director-backpack = Mystagogue backpack
|
||||
loadout-group-research-director-outerclothing = Mystagogue outer clothing
|
||||
loadout-group-research-director-shoes = Mystagogue shoes
|
||||
|
||||
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-gloves = Scientist gloves
|
||||
loadout-group-scientist-shoes = Scientist shoes
|
||||
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-belt = Security Belt
|
||||
loadout-group-security-outerclothing = Security outer clothing
|
||||
loadout-group-security-shoes = Security shoes
|
||||
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-medical-gloves = Medical gloves
|
||||
loadout-group-medical-mask = Medical mask
|
||||
|
||||
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-shoes = Chief Medical Officer shoes
|
||||
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-shoes = Medical Doctor shoes
|
||||
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-shoes = Paramedic shoes
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -106,3 +106,6 @@ MagicalLamp: Lamp
|
|||
|
||||
# 2024-04-21
|
||||
ClothingEyesHudSyndicateMed: ClothingEyesHudSyndicateAgent
|
||||
|
||||
# 2024-04-23
|
||||
SpawnPointMailCarrier: SpawnPointCourier
|
||||
|
|
|
|||
|
|
@ -88,6 +88,12 @@ GeneratorUranium: PortableGeneratorSuperPacman
|
|||
GeneratorPlasmaMachineCircuitboard: PortableGeneratorPacmanMachineCircuitboard
|
||||
GeneratorUraniumMachineCircuitboard: PortableGeneratorSuperPacmanMachineCircuitboard
|
||||
|
||||
# 2023-12-10
|
||||
SpawnPointSeniorResearcher: null
|
||||
SpawnPointSeniorOfficer: null
|
||||
SpawnPointSeniorEngineer: null
|
||||
SpawnPointSeniorPhysician: null
|
||||
|
||||
# 2023-12-12
|
||||
#No this is not the CMO hardsuit, their prototype IDs were just confusingly similar
|
||||
ClothingOuterHardsuitMedic: ClothingOuterHardsuitSyndieMedic
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -202,7 +213,7 @@
|
|||
- id: BoxSurvival
|
||||
- id: Bible
|
||||
- id: RubberStampChaplain
|
||||
|
||||
|
||||
- type: entity
|
||||
noSpawn: true
|
||||
parent: ClothingBackpack
|
||||
|
|
|
|||
|
|
@ -54,6 +54,19 @@
|
|||
- id: Handcuffs
|
||||
- id: Handcuffs
|
||||
|
||||
- type: entity
|
||||
id: ClothingBeltSecurityWebbingFilled
|
||||
parent: ClothingBeltSecurityWebbing
|
||||
suffix: Filled
|
||||
components:
|
||||
- type: StorageFill
|
||||
contents:
|
||||
- id: GrenadeFlashBang
|
||||
- id: TearGasGrenade
|
||||
- id: Stunbaton
|
||||
- id: Handcuffs
|
||||
- id: Handcuffs
|
||||
|
||||
- type: entity
|
||||
id: ClothingBeltJanitorFilled
|
||||
parent: ClothingBeltJanitor
|
||||
|
|
|
|||
|
|
@ -1695,7 +1695,6 @@
|
|||
whitelist:
|
||||
- Zookeeper
|
||||
- Scientist
|
||||
- SeniorResearcher
|
||||
- ResearchDirector
|
||||
- Chef
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
- type: entity
|
||||
id: SpawnPointMedicalBorg
|
||||
parent: SpawnPointJobBase
|
||||
|
|
@ -13,3 +12,16 @@
|
|||
state: medical
|
||||
- sprite: Mobs/Silicon/chassis.rsi
|
||||
state: medical_e
|
||||
|
||||
- type: entity
|
||||
id: SpawnPointCourier
|
||||
parent: SpawnPointJobBase
|
||||
name: courier
|
||||
components:
|
||||
- type: SpawnPoint
|
||||
job_id: Courier
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: green
|
||||
- sprite: DeltaV/Markers/jobs.rsi
|
||||
state: courier
|
||||
|
|
|
|||
|
|
@ -124,7 +124,17 @@
|
|||
baseSprintSpeed: 5.0
|
||||
- type: Inventory
|
||||
speciesId: harpy
|
||||
templateId: digitigrade
|
||||
# templateId: digitigrade
|
||||
displacements:
|
||||
jumpsuit:
|
||||
layer:
|
||||
sprite: DeltaV/Mobs/Species/Harpy/displacement.rsi
|
||||
state: jumpsuit
|
||||
copyToShaderParameters:
|
||||
# Value required, provide a dummy. Gets overridden when applied.
|
||||
layerKey: dummy
|
||||
parameterTexture: displacementMap
|
||||
parameterUV: displacementUV
|
||||
- type: HarpyVisuals
|
||||
- type: UltraVision
|
||||
|
||||
|
|
@ -140,6 +150,16 @@
|
|||
species: Harpy
|
||||
- type: Inventory
|
||||
speciesId: harpy
|
||||
displacements:
|
||||
jumpsuit:
|
||||
layer:
|
||||
sprite: DeltaV/Mobs/Species/Harpy/displacement.rsi
|
||||
state: jumpsuit
|
||||
copyToShaderParameters:
|
||||
# Value required, provide a dummy. Gets overridden when applied.
|
||||
layerKey: dummy
|
||||
parameterTexture: displacementMap
|
||||
parameterUV: displacementUV
|
||||
- type: Sprite
|
||||
scale: 0.9, 0.9
|
||||
layers:
|
||||
|
|
|
|||
|
|
@ -52,3 +52,31 @@
|
|||
whitelist:
|
||||
tags:
|
||||
- Write
|
||||
|
||||
- type: entity
|
||||
parent: BasePDA
|
||||
id: CourierPDA
|
||||
name: courier PDA
|
||||
description: Smells like unopened letters.
|
||||
components:
|
||||
- type: Sprite
|
||||
sprite: DeltaV/Objects/Devices/pda.rsi
|
||||
layers:
|
||||
- map: [ "enum.PdaVisualLayers.Base" ]
|
||||
- state: "light_overlay"
|
||||
map: [ "enum.PdaVisualLayers.Flashlight" ]
|
||||
shader: "unshaded"
|
||||
visible: false
|
||||
- state: "id_overlay"
|
||||
map: [ "enum.PdaVisualLayers.IdLight" ]
|
||||
shader: "unshaded"
|
||||
visible: false
|
||||
- type: Pda
|
||||
id: CourierIDCard
|
||||
state: pda-mailcarrier
|
||||
- type: PdaBorderColor
|
||||
borderColor: "#e39751"
|
||||
accentVColor: "#050c4d"
|
||||
- type: Icon
|
||||
sprite: DeltaV/Objects/Devices/pda.rsi
|
||||
state: pda-mailcarrier
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
- type: entity
|
||||
parent: IDCardStandard
|
||||
id: CourierIDCard
|
||||
name: courier ID card
|
||||
components:
|
||||
- type: Sprite
|
||||
layers:
|
||||
- state: default
|
||||
- sprite: DeltaV/Objects/Misc/id_cards.rsi
|
||||
state: nyanomailcarrier
|
||||
- type: PresetIdCard
|
||||
job: Courier
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
# Head
|
||||
- type: loadout
|
||||
id: CourierHead
|
||||
equipment: CourierHead
|
||||
|
||||
- type: startingGear
|
||||
id: CourierHead
|
||||
equipment:
|
||||
head: ClothingHeadCourier
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: CourierJumpsuit
|
||||
equipment: CourierJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: CourierJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformCourier
|
||||
|
||||
- type: loadout
|
||||
id: CourierJumpskirt
|
||||
equipment: CourierJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: CourierJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformSkirtCourier
|
||||
|
||||
# Shoes
|
||||
- type: loadout
|
||||
id: RollerSkates
|
||||
equipment: RollerSkates
|
||||
|
||||
- type: startingGear
|
||||
id: RollerSkates
|
||||
equipment:
|
||||
shoes: ClothingShoesSkates
|
||||
|
||||
# PDA
|
||||
- type: loadout
|
||||
id: CourierPDA
|
||||
equipment: CourierPDA
|
||||
|
||||
- type: startingGear
|
||||
id: CourierPDA
|
||||
equipment:
|
||||
id: CourierPDA
|
||||
|
||||
- type: loadout
|
||||
id: MailCarrierPDA
|
||||
equipment: MailCarrierPDA
|
||||
|
||||
- type: startingGear
|
||||
id: MailCarrierPDA
|
||||
equipment:
|
||||
id: MailCarrierPDA
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# Head
|
||||
- type: loadout
|
||||
id: CorpsmanBeret
|
||||
equipment: CorpsmanBeret
|
||||
|
||||
- type: startingGear
|
||||
id: CorpsmanBeret
|
||||
equipment:
|
||||
head: ClothingHeadHatBeretCorpsman
|
||||
|
||||
# Jumpsuit
|
||||
- type: loadout
|
||||
id: BrigMedicJumpsuit
|
||||
equipment: BrigMedicJumpsuit
|
||||
|
||||
- type: startingGear
|
||||
id: BrigMedicJumpsuit
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitBrigmedic
|
||||
|
||||
- type: loadout
|
||||
id: BrigMedicJumpskirt
|
||||
equipment: BrigMedicJumpskirt
|
||||
|
||||
- type: startingGear
|
||||
id: BrigMedicJumpskirt
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpskirtBrigmedic
|
||||
|
||||
# Back
|
||||
- type: loadout
|
||||
id: BrigMedicBackpack
|
||||
equipment: BrigMedicBackpack
|
||||
|
||||
- type: startingGear
|
||||
id: BrigMedicBackpack
|
||||
equipment:
|
||||
back: ClothingBackpackBrigmedicDeltaVFilled
|
||||
|
||||
- type: loadout
|
||||
id: BrigMedicSatchel
|
||||
equipment: BrigMedicSatchel
|
||||
|
||||
- type: startingGear
|
||||
id: BrigMedicSatchel
|
||||
equipment:
|
||||
back: ClothingBackpackSatchelBrigmedicDeltaVFilled
|
||||
|
||||
- type: loadout
|
||||
id: BrigMedicDuffel
|
||||
equipment: BrigMedicDuffel
|
||||
|
||||
- type: startingGear
|
||||
id: BrigMedicDuffel
|
||||
equipment:
|
||||
back: ClothingBackpackDuffelBrigmedicDeltaVFilled
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
# Logistics
|
||||
## Courier
|
||||
- type: loadoutGroup
|
||||
id: CourierHead
|
||||
name: loadout-group-courier-head
|
||||
minLimit: 0
|
||||
loadouts:
|
||||
- CourierHead
|
||||
- MailCarrierHead
|
||||
|
||||
- type: loadoutGroup
|
||||
id: CourierJumpsuit
|
||||
name: loadout-group-courier-jumpsuit
|
||||
loadouts:
|
||||
- CourierJumpsuit
|
||||
- CourierJumpskirt
|
||||
- MailCarrierJumpsuit
|
||||
- MailCarrierJumpskirt
|
||||
|
||||
- type: loadoutGroup
|
||||
id: CourierOuterClothing
|
||||
name: loadout-group-courier-outerclothing
|
||||
minLimit: 0
|
||||
loadouts:
|
||||
- CargoTechnicianWintercoat
|
||||
- MailCarrierWintercoat
|
||||
|
||||
- type: loadoutGroup
|
||||
id: CourierShoes
|
||||
name: loadout-group-courier-shoes
|
||||
loadouts:
|
||||
- BlackShoes
|
||||
- CargoWinterBoots
|
||||
- RollerSkates
|
||||
|
||||
- type: loadoutGroup
|
||||
id: CourierPDA
|
||||
name: loadout-group-courier-id
|
||||
loadouts:
|
||||
- CourierPDA
|
||||
- MailCarrierPDA
|
||||
|
||||
# Security
|
||||
## Brig Medic
|
||||
- type: loadoutGroup
|
||||
id: BrigMedicHead
|
||||
name: loadout-group-brig-medic-head
|
||||
minLimit: 0
|
||||
loadouts:
|
||||
- CorpsmanBeret
|
||||
|
||||
- type: loadoutGroup
|
||||
id: BrigMedicJumpsuit
|
||||
name: loadout-group-brig-medic-jumpsuit
|
||||
loadouts:
|
||||
- BrigMedicJumpsuit
|
||||
- BrigMedicJumpskirt
|
||||
|
||||
- type: loadoutGroup
|
||||
id: BrigMedicBack
|
||||
name: loadout-group-brig-medic-back
|
||||
loadouts:
|
||||
- BrigMedicBackpack
|
||||
- BrigMedicSatchel
|
||||
- BrigMedicDuffel
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Logistics
|
||||
- type: roleLoadout
|
||||
id: JobCourier
|
||||
groups:
|
||||
- CourierHead
|
||||
- CourierJumpsuit
|
||||
- CourierOuterClothing
|
||||
- CourierPDA
|
||||
- CargoTechnicianBackpack
|
||||
- CourierShoes
|
||||
- Trinkets
|
||||
|
||||
# Security
|
||||
- type: roleLoadout
|
||||
id: JobBrigmedic
|
||||
groups:
|
||||
- BrigMedicHead
|
||||
- BrigMedicJumpsuit
|
||||
- BrigMedicBack
|
||||
- SecurityShoes
|
||||
- SecurityOuterClothing
|
||||
- MedicalGloves
|
||||
- Trinkets
|
||||
|
|
@ -1,13 +1,19 @@
|
|||
- type: job
|
||||
id: Courier
|
||||
name: job-name-courier
|
||||
description: job-description-courier
|
||||
startingGear: CourierGear
|
||||
playTimeTracker: JobCourier
|
||||
icon: "JobIconCourier"
|
||||
supervisors: job-supervisors-qm
|
||||
access:
|
||||
- Cargo
|
||||
- Maintenance
|
||||
- Mail
|
||||
|
||||
- type: startingGear
|
||||
id: CourierGear
|
||||
equipment:
|
||||
head: ClothingHeadCourier
|
||||
jumpsuit: ClothingUniformCourier
|
||||
back: ClothingBackpackFilled
|
||||
shoes: ClothingShoesColorBlack
|
||||
id: MailCarrierPDA
|
||||
ears: ClothingHeadsetCargo
|
||||
belt: CourierBag
|
||||
innerClothingSkirt: ClothingUniformSkirtCourier
|
||||
satchel: ClothingBackpackSatchelFilled
|
||||
duffelbag: ClothingBackpackDuffelFilled
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@
|
|||
gloves: ClothingHandsGlovesCombat
|
||||
shoes: ClothingShoesSlippers
|
||||
id: SyndiListeningPostPDA
|
||||
innerClothingSkirt: ClothingUniformJumpsuitPyjamaSyndicatePink
|
||||
# innerClothingSkirt: ClothingUniformJumpsuitPyjamaSyndicatePink # I don't think loadouts work for ghost roles anyways
|
||||
|
||||
# Mobsters
|
||||
|
||||
|
|
@ -81,9 +81,9 @@
|
|||
outerClothing: ClothingOuterArmorBasicSlim
|
||||
id: PassengerPDA
|
||||
belt: ClothingBeltHolster
|
||||
innerClothingSkirt: ClothingUniformJumpsuitSuitBlackMob
|
||||
satchel: ClothingBackpackMafiaFilled
|
||||
duffelbag: ClothingBackpackMafiaFilled
|
||||
# innerClothingSkirt: ClothingUniformJumpsuitSuitBlackMob # TODO: Loadouts
|
||||
# satchel: ClothingBackpackMafiaFilled
|
||||
# duffelbag: ClothingBackpackMafiaFilled
|
||||
|
||||
- type: startingGear
|
||||
id: MobsterGearAlt
|
||||
|
|
@ -97,6 +97,6 @@
|
|||
id: PassengerPDA
|
||||
belt: ClothingBeltSuspendersBlack
|
||||
pocket1: CombatKnife
|
||||
innerClothingSkirt: ClothingUniformJumpsuitSuitBrown
|
||||
satchel: ClothingBackpackMafiaFilled
|
||||
duffelbag: ClothingBackpackMafiaFilled
|
||||
# innerClothingSkirt: ClothingUniformJumpsuitSuitBrown # TODO: Loadouts
|
||||
# satchel: ClothingBackpackMafiaFilled
|
||||
# duffelbag: ClothingBackpackMafiaFilled
|
||||
|
|
|
|||
|
|
@ -30,17 +30,8 @@
|
|||
- type: startingGear
|
||||
id: CorpsmanGear # see Prototypes/Roles/Jobs/Fun/misc_startinggear.yml for "BrigmedicGear"
|
||||
equipment:
|
||||
jumpsuit: ClothingUniformJumpsuitBrigmedic
|
||||
outerClothing: ClothingOuterArmorPlateCarrier
|
||||
back: ClothingBackpackBrigmedicDeltaVFilled
|
||||
shoes: ClothingShoesBootsCombatFilled
|
||||
gloves: ClothingHandsGlovesNitrile
|
||||
eyes: ClothingEyesHudMedical
|
||||
head: ClothingHeadHatBeretCorpsman
|
||||
id: CorpsmanPDA
|
||||
ears: ClothingHeadsetBrigmedic
|
||||
belt: ClothingBeltCorpsmanWebbingFilled
|
||||
pocket1: WeaponPistolMk58Nonlethal
|
||||
innerClothingSkirt: ClothingUniformJumpskirtBrigmedic
|
||||
satchel: ClothingBackpackSatchelBrigmedicDeltaVFilled
|
||||
duffelbag: ClothingBackpackDuffelBrigmedicDeltaVFilled
|
||||
|
|
|
|||
|
|
@ -3,3 +3,6 @@
|
|||
|
||||
- type: playTimeTracker
|
||||
id: JobMedicalBorg
|
||||
|
||||
- type: playTimeTracker
|
||||
id: JobCourier
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
- type: statusIcon
|
||||
parent: JobIcon
|
||||
id: JobIconCourier
|
||||
icon:
|
||||
sprite: /Textures/DeltaV/Interface/Misc/job_icons.rsi
|
||||
state: nyanoMailCarrier
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue