mirror of
https://github.com/corvax-team/ss14-wl.git
synced 2026-02-15 03:31:38 +01:00
Merge remote-tracking branch 'refs/remotes/upstream/master' into upstream-sync
# Conflicts: # Content.Client/IoC/ClientContentIoC.cs # Content.Client/Lobby/UI/HumanoidProfileEditor.xaml.cs # Content.Client/Preferences/UI/CharacterSetupGui.xaml.cs # Content.IntegrationTests/Tests/Preferences/ServerDbSqliteTests.cs # Content.Server/Preferences/Managers/ServerPreferencesManager.cs # Content.Shared/Preferences/HumanoidCharacterProfile.cs # Content.Shared/Preferences/Loadouts/Effects/GroupLoadoutEffect.cs # Content.Shared/Preferences/Loadouts/Effects/JobRequirementLoadoutEffect.cs # Content.Shared/Preferences/Loadouts/RoleLoadout.cs # Resources/ServerInfo/Guidebook/Engineering/TEG.xml # Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/equipped-INNERCLOTHING.png # Resources/Textures/Clothing/Uniforms/Jumpsuit/security.rsi/meta.json
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
using Content.Shared.Access;
|
||||
using Content.Shared.Access.Components;
|
||||
using Content.Shared.Access;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.CrewManifest;
|
||||
|
||||
@@ -247,7 +247,10 @@ namespace Content.Client.Actions
|
||||
if (action.ClientExclusive)
|
||||
{
|
||||
if (instantAction.Event != null)
|
||||
{
|
||||
instantAction.Event.Performer = user;
|
||||
instantAction.Event.Action = actionId;
|
||||
}
|
||||
|
||||
PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
using Content.Shared.Administration;
|
||||
|
||||
namespace Content.Client.Administration.Systems;
|
||||
|
||||
public sealed class AdminFrozenSystem : SharedAdminFrozenSystem
|
||||
{
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Content.Shared.Explosion;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
@@ -22,7 +23,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
|
||||
private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private readonly SpawnExplosionEui _eui;
|
||||
private List<MapId> _mapData = new();
|
||||
@@ -37,6 +38,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
_transform = _entMan.System<TransformSystem>();
|
||||
_eui = eui;
|
||||
|
||||
ExplosionOption.OnItemSelected += ExplosionSelected;
|
||||
@@ -104,7 +106,7 @@ public sealed partial class SpawnExplosionWindow : DefaultWindow
|
||||
|
||||
_pausePreview = true;
|
||||
MapOptions.Select(_mapData.IndexOf(transform.MapID));
|
||||
(MapX.Value, MapY.Value) = transform.MapPosition.Position;
|
||||
(MapX.Value, MapY.Value) = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: transform).Position;
|
||||
_pausePreview = false;
|
||||
|
||||
UpdatePreview();
|
||||
|
||||
@@ -9,7 +9,6 @@ namespace Content.Client.Audio.Jukebox;
|
||||
|
||||
public sealed class JukeboxBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<Control xmlns="https://spacestation14.io" HorizontalExpand="True">
|
||||
<BoxContainer Name="MainContainer"
|
||||
Orientation="Horizontal"
|
||||
SetWidth="160">
|
||||
HorizontalExpand="True">
|
||||
<PanelContainer Name="ColorPanel"
|
||||
VerticalExpand="True"
|
||||
SetWidth="7"
|
||||
@@ -16,8 +16,7 @@
|
||||
VerticalExpand="True"
|
||||
HorizontalExpand="True"
|
||||
Margin="-5 0 0 0">
|
||||
<Label Name="ReagentNameLabel"
|
||||
StyleClasses="LabelSubText" />
|
||||
<Label Name="ReagentNameLabel" />
|
||||
<Label Name="FillLabel"
|
||||
StyleClasses="LabelSubText"
|
||||
Margin="0 -5 0 0" />
|
||||
|
||||
@@ -20,10 +20,12 @@ public sealed partial class ReagentCardControl : Control
|
||||
StorageSlotId = item.StorageSlotId;
|
||||
ColorPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = item.ReagentColor };
|
||||
ReagentNameLabel.Text = item.ReagentLabel;
|
||||
ReagentNameLabel.FontColorOverride = Color.White;
|
||||
FillLabel.Text = item.StoredAmount;
|
||||
FillLabel.Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", item.Quantity));;
|
||||
EjectButtonIcon.Text = Loc.GetString("reagent-dispenser-window-eject-container-button");
|
||||
|
||||
if (item.Quantity == 0.0)
|
||||
MainButton.Disabled = true;
|
||||
|
||||
MainButton.OnPressed += args => OnPressed?.Invoke(StorageSlotId);
|
||||
EjectButton.OnPressed += args => OnEjectButtonPressed?.Invoke(StorageSlotId);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Content.Client.Guidebook.Components;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using JetBrains.Annotations;
|
||||
@@ -34,6 +35,7 @@ namespace Content.Client.Chemistry.UI
|
||||
_window = new()
|
||||
{
|
||||
Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName,
|
||||
HelpGuidebookIds = EntMan.GetComponent<GuideHelpComponent>(Owner).Guides
|
||||
};
|
||||
|
||||
_window.OpenCentered();
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||
xmlns:ui="clr-namespace:Content.Client.Chemistry.UI"
|
||||
Title="{Loc 'reagent-dispenser-bound-user-interface-title'}"
|
||||
MinSize="680 460">
|
||||
MinSize="600 300"
|
||||
SetSize="800 500">
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<BoxContainer Orientation="Vertical" MinWidth="170">
|
||||
<Label Text="{Loc 'reagent-dispenser-window-amount-to-dispense-label'}" HorizontalAlignment="Center" />
|
||||
|
||||
@@ -121,28 +121,4 @@ namespace Content.Client.Chemistry.UI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DispenseReagentButton : Button
|
||||
{
|
||||
public string ReagentId { get; }
|
||||
|
||||
public DispenseReagentButton(string reagentId, string text, string amount)
|
||||
{
|
||||
AddStyleClass("OpenRight");
|
||||
ReagentId = reagentId;
|
||||
Text = text + " " + amount;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class EjectJugButton : Button
|
||||
{
|
||||
public string ReagentId { get; }
|
||||
|
||||
public EjectJugButton(string reagentId)
|
||||
{
|
||||
AddStyleClass("OpenLeft");
|
||||
ReagentId = reagentId;
|
||||
Text = "⏏";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ namespace Content.Client.Decals;
|
||||
|
||||
public sealed class ToggleDecalCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "toggledecals";
|
||||
public string Description => "Toggles decaloverlay";
|
||||
public string Help => $"{Command}";
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<DecalSystem>().ToggleOverlay();
|
||||
_e.System<DecalSystem>().ToggleOverlay();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Content.Client.Decals.UI;
|
||||
public sealed partial class DecalPlacerWindow : DefaultWindow
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
private readonly DecalPlacementSystem _decalPlacementSystem;
|
||||
|
||||
@@ -39,7 +40,7 @@ public sealed partial class DecalPlacerWindow : DefaultWindow
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_decalPlacementSystem = EntitySystem.Get<DecalPlacementSystem>();
|
||||
_decalPlacementSystem = _e.System<DecalPlacementSystem>();
|
||||
|
||||
// This needs to be done in C# so we can have custom stuff passed in the constructor
|
||||
// and thus have a proper step size
|
||||
|
||||
@@ -12,10 +12,10 @@ using Content.Client.Info;
|
||||
using Content.Client.Input;
|
||||
using Content.Client.IoC;
|
||||
using Content.Client.Launcher;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.MainMenu;
|
||||
using Content.Client.Parallax.Managers;
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.Radiation.Overlays;
|
||||
using Content.Client.Replay;
|
||||
using Content.Client.Screenshot;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Client.GPS.Components;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -13,11 +14,13 @@ public sealed class HandheldGpsStatusControl : Control
|
||||
private readonly RichTextLabel _label;
|
||||
private float _updateDif;
|
||||
private readonly IEntityManager _entMan;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
|
||||
public HandheldGpsStatusControl(Entity<HandheldGPSComponent> parent)
|
||||
{
|
||||
_parent = parent;
|
||||
_entMan = IoCManager.Resolve<IEntityManager>();
|
||||
_transform = _entMan.System<TransformSystem>();
|
||||
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
|
||||
AddChild(_label);
|
||||
UpdateGpsDetails();
|
||||
@@ -41,7 +44,7 @@ public sealed class HandheldGpsStatusControl : Control
|
||||
var posText = "Error";
|
||||
if (_entMan.TryGetComponent(_parent, out TransformComponent? transComp))
|
||||
{
|
||||
var pos = transComp.MapPosition;
|
||||
var pos = _transform.GetMapCoordinates(_parent.Owner, xform: transComp);
|
||||
var x = (int) pos.X;
|
||||
var y = (int) pos.Y;
|
||||
posText = $"({x}, {y})";
|
||||
|
||||
@@ -80,6 +80,11 @@ public sealed class GuidebookSystem : EntitySystem
|
||||
});
|
||||
}
|
||||
|
||||
public void OpenHelp(List<string> guides)
|
||||
{
|
||||
OnGuidebookOpen?.Invoke(guides, null, null, true, guides[0]);
|
||||
}
|
||||
|
||||
private void OnInteract(EntityUid uid, GuideHelpComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (!_timing.IsFirstTimePredicted)
|
||||
|
||||
@@ -3,23 +3,21 @@ using Content.Client.Changelog;
|
||||
using Content.Client.Chat.Managers;
|
||||
using Content.Client.Clickable;
|
||||
using Content.Client.Corvax.TTS;
|
||||
using Content.Client.Options;
|
||||
using Content.Client.Eui;
|
||||
using Content.Client.GhostKick;
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Launcher;
|
||||
using Content.Client.Parallax.Managers;
|
||||
using Content.Client.Players.PlayTimeTracking;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.Screenshot;
|
||||
using Content.Client.Fullscreen;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.Viewport;
|
||||
using Content.Client.Voting;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Module;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.Replay;
|
||||
using Content.Shared.Administration.Managers;
|
||||
using Content.Shared.Players.PlayTimeTracking;
|
||||
|
||||
@@ -6,7 +6,7 @@ using Robust.Client.Player;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Preferences
|
||||
namespace Content.Client.Lobby
|
||||
{
|
||||
/// <summary>
|
||||
/// Receives <see cref="PlayerPreferences" /> and <see cref="GameSettings" /> from the server during the initial
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using Content.Shared.Preferences;
|
||||
|
||||
namespace Content.Client.Preferences
|
||||
namespace Content.Client.Lobby
|
||||
{
|
||||
public interface IClientPreferencesManager
|
||||
{
|
||||
@@ -3,8 +3,6 @@ using Content.Client.GameTicking.Managers;
|
||||
using Content.Client.LateJoin;
|
||||
using Content.Client.Lobby.UI;
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Client.Preferences.UI;
|
||||
using Content.Client.UserInterface.Systems.Chat;
|
||||
using Content.Client.Voting;
|
||||
using Robust.Client;
|
||||
@@ -12,8 +10,6 @@ using Robust.Client.Console;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
|
||||
@@ -25,20 +21,15 @@ namespace Content.Client.Lobby
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IVoteManager _voteManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
|
||||
[ViewVariables] private CharacterSetupGui? _characterSetup;
|
||||
|
||||
private ClientGameTicker _gameTicker = default!;
|
||||
private ContentAudioSystem _contentAudioSystem = default!;
|
||||
|
||||
protected override Type? LinkedScreenType { get; } = typeof(LobbyGui);
|
||||
private LobbyGui? _lobby;
|
||||
public LobbyGui? Lobby;
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
@@ -47,45 +38,23 @@ namespace Content.Client.Lobby
|
||||
return;
|
||||
}
|
||||
|
||||
_lobby = (LobbyGui) _userInterfaceManager.ActiveScreen;
|
||||
Lobby = (LobbyGui) _userInterfaceManager.ActiveScreen;
|
||||
|
||||
var chatController = _userInterfaceManager.GetUIController<ChatUIController>();
|
||||
_gameTicker = _entityManager.System<ClientGameTicker>();
|
||||
_contentAudioSystem = _entityManager.System<ContentAudioSystem>();
|
||||
_contentAudioSystem.LobbySoundtrackChanged += UpdateLobbySoundtrackInfo;
|
||||
_characterSetup = new CharacterSetupGui(_entityManager, _resourceCache, _preferencesManager,
|
||||
_prototypeManager, _configurationManager);
|
||||
LayoutContainer.SetAnchorPreset(_characterSetup, LayoutContainer.LayoutPreset.Wide);
|
||||
|
||||
_lobby.CharacterSetupState.AddChild(_characterSetup);
|
||||
chatController.SetMainChat(true);
|
||||
|
||||
_voteManager.SetPopupContainer(_lobby.VoteContainer);
|
||||
|
||||
_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();
|
||||
_userInterfaceManager.GetUIController<LobbyUIController>().ReloadProfile();
|
||||
};
|
||||
|
||||
LayoutContainer.SetAnchorPreset(_lobby, LayoutContainer.LayoutPreset.Wide);
|
||||
_lobby.ServerName.Text = _baseClient.GameInfo?.ServerName; //The eye of refactor gazes upon you...
|
||||
_voteManager.SetPopupContainer(Lobby.VoteContainer);
|
||||
LayoutContainer.SetAnchorPreset(Lobby, LayoutContainer.LayoutPreset.Wide);
|
||||
Lobby.ServerName.Text = _baseClient.GameInfo?.ServerName; //The eye of refactor gazes upon you...
|
||||
UpdateLobbyUi();
|
||||
|
||||
_lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed;
|
||||
_lobby.ReadyButton.OnPressed += OnReadyPressed;
|
||||
_lobby.ReadyButton.OnToggled += OnReadyToggled;
|
||||
Lobby.CharacterPreview.CharacterSetupButton.OnPressed += OnSetupPressed;
|
||||
Lobby.ReadyButton.OnPressed += OnReadyPressed;
|
||||
Lobby.ReadyButton.OnToggled += OnReadyToggled;
|
||||
|
||||
_gameTicker.InfoBlobUpdated += UpdateLobbyUi;
|
||||
_gameTicker.LobbyStatusUpdated += LobbyStatusUpdated;
|
||||
@@ -103,20 +72,23 @@ namespace Content.Client.Lobby
|
||||
|
||||
_voteManager.ClearPopupContainer();
|
||||
|
||||
_lobby!.CharacterPreview.CharacterSetupButton.OnPressed -= OnSetupPressed;
|
||||
_lobby!.ReadyButton.OnPressed -= OnReadyPressed;
|
||||
_lobby!.ReadyButton.OnToggled -= OnReadyToggled;
|
||||
Lobby!.CharacterPreview.CharacterSetupButton.OnPressed -= OnSetupPressed;
|
||||
Lobby!.ReadyButton.OnPressed -= OnReadyPressed;
|
||||
Lobby!.ReadyButton.OnToggled -= OnReadyToggled;
|
||||
|
||||
_lobby = null;
|
||||
Lobby = null;
|
||||
}
|
||||
|
||||
_characterSetup?.Dispose();
|
||||
_characterSetup = null;
|
||||
public void SwitchState(LobbyGui.LobbyGuiState state)
|
||||
{
|
||||
// Yeah I hate this but LobbyState contains all the badness for now.
|
||||
Lobby?.SwitchState(state);
|
||||
}
|
||||
|
||||
private void OnSetupPressed(BaseButton.ButtonEventArgs args)
|
||||
{
|
||||
SetReady(false);
|
||||
_lobby!.SwitchState(LobbyGui.LobbyGuiState.CharacterSetup);
|
||||
Lobby?.SwitchState(LobbyGui.LobbyGuiState.CharacterSetup);
|
||||
}
|
||||
|
||||
private void OnReadyPressed(BaseButton.ButtonEventArgs args)
|
||||
@@ -138,13 +110,13 @@ namespace Content.Client.Lobby
|
||||
{
|
||||
if (_gameTicker.IsGameStarted)
|
||||
{
|
||||
_lobby!.StartTime.Text = string.Empty;
|
||||
Lobby!.StartTime.Text = string.Empty;
|
||||
var roundTime = _gameTiming.CurTime.Subtract(_gameTicker.RoundStartTimeSpan);
|
||||
_lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-time", ("hours", roundTime.Hours), ("minutes", roundTime.Minutes));
|
||||
Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-time", ("hours", roundTime.Hours), ("minutes", roundTime.Minutes));
|
||||
return;
|
||||
}
|
||||
|
||||
_lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started");
|
||||
Lobby!.StationTime.Text = Loc.GetString("lobby-state-player-status-round-not-started");
|
||||
string text;
|
||||
|
||||
if (_gameTicker.Paused)
|
||||
@@ -153,7 +125,7 @@ namespace Content.Client.Lobby
|
||||
}
|
||||
else if (_gameTicker.StartTime < _gameTiming.CurTime)
|
||||
{
|
||||
_lobby!.StartTime.Text = Loc.GetString("lobby-state-soon");
|
||||
Lobby!.StartTime.Text = Loc.GetString("lobby-state-soon");
|
||||
return;
|
||||
}
|
||||
else
|
||||
@@ -170,7 +142,7 @@ namespace Content.Client.Lobby
|
||||
}
|
||||
}
|
||||
|
||||
_lobby!.StartTime.Text = Loc.GetString("lobby-state-round-start-countdown-text", ("timeLeft", text));
|
||||
Lobby!.StartTime.Text = Loc.GetString("lobby-state-round-start-countdown-text", ("timeLeft", text));
|
||||
}
|
||||
|
||||
private void LobbyStatusUpdated()
|
||||
@@ -181,31 +153,31 @@ namespace Content.Client.Lobby
|
||||
|
||||
private void LobbyLateJoinStatusUpdated()
|
||||
{
|
||||
_lobby!.ReadyButton.Disabled = _gameTicker.DisallowedLateJoin;
|
||||
Lobby!.ReadyButton.Disabled = _gameTicker.DisallowedLateJoin;
|
||||
}
|
||||
|
||||
private void UpdateLobbyUi()
|
||||
{
|
||||
if (_gameTicker.IsGameStarted)
|
||||
{
|
||||
_lobby!.ReadyButton.Text = Loc.GetString("lobby-state-ready-button-join-state");
|
||||
_lobby!.ReadyButton.ToggleMode = false;
|
||||
_lobby!.ReadyButton.Pressed = false;
|
||||
_lobby!.ObserveButton.Disabled = false;
|
||||
Lobby!.ReadyButton.Text = Loc.GetString("lobby-state-ready-button-join-state");
|
||||
Lobby!.ReadyButton.ToggleMode = false;
|
||||
Lobby!.ReadyButton.Pressed = false;
|
||||
Lobby!.ObserveButton.Disabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lobby!.StartTime.Text = string.Empty;
|
||||
_lobby!.ReadyButton.Text = Loc.GetString(_lobby!.ReadyButton.Pressed ? "lobby-state-player-status-ready": "lobby-state-player-status-not-ready");
|
||||
_lobby!.ReadyButton.ToggleMode = true;
|
||||
_lobby!.ReadyButton.Disabled = false;
|
||||
_lobby!.ReadyButton.Pressed = _gameTicker.AreWeReady;
|
||||
_lobby!.ObserveButton.Disabled = true;
|
||||
Lobby!.StartTime.Text = string.Empty;
|
||||
Lobby!.ReadyButton.Text = Loc.GetString(Lobby!.ReadyButton.Pressed ? "lobby-state-player-status-ready": "lobby-state-player-status-not-ready");
|
||||
Lobby!.ReadyButton.ToggleMode = true;
|
||||
Lobby!.ReadyButton.Disabled = false;
|
||||
Lobby!.ReadyButton.Pressed = _gameTicker.AreWeReady;
|
||||
Lobby!.ObserveButton.Disabled = true;
|
||||
}
|
||||
|
||||
if (_gameTicker.ServerInfoBlob != null)
|
||||
{
|
||||
_lobby!.ServerInfo.SetInfoBlob(_gameTicker.ServerInfoBlob);
|
||||
Lobby!.ServerInfo.SetInfoBlob(_gameTicker.ServerInfoBlob);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +185,7 @@ namespace Content.Client.Lobby
|
||||
{
|
||||
if (ev.SoundtrackFilename == null)
|
||||
{
|
||||
_lobby!.LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text"));
|
||||
Lobby!.LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text"));
|
||||
}
|
||||
else if (
|
||||
ev.SoundtrackFilename != null
|
||||
@@ -234,7 +206,7 @@ namespace Content.Client.Lobby
|
||||
("songTitle", title),
|
||||
("songArtist", artist));
|
||||
|
||||
_lobby!.LobbySong.SetMarkup(markup);
|
||||
Lobby!.LobbySong.SetMarkup(markup);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,11 +214,11 @@ namespace Content.Client.Lobby
|
||||
{
|
||||
if (_gameTicker.LobbyBackground != null)
|
||||
{
|
||||
_lobby!.Background.Texture = _resourceCache.GetResource<TextureResource>(_gameTicker.LobbyBackground );
|
||||
Lobby!.Background.Texture = _resourceCache.GetResource<TextureResource>(_gameTicker.LobbyBackground );
|
||||
}
|
||||
else
|
||||
{
|
||||
_lobby!.Background.Texture = null;
|
||||
Lobby!.Background.Texture = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,190 +3,292 @@ using Content.Client.Corvax.TTS;
|
||||
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.Players.PlayTimeTracking;
|
||||
using Content.Client.Station;
|
||||
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.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Content.Shared.Preferences.Loadouts.Effects;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Traits;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.State;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controllers;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Lobby;
|
||||
|
||||
public sealed partial class LobbyUIController : UIController, IOnStateEntered<LobbyState>, IOnStateExited<LobbyState>
|
||||
{
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
|
||||
[Dependency] private readonly IFileDialogManager _dialogManager = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly JobRequirementsManager _requirements = default!;
|
||||
[Dependency] private readonly MarkingManager _markings = 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.
|
||||
*/
|
||||
private CharacterSetupGui? _characterSetup;
|
||||
private HumanoidProfileEditor? _profileEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Preview dummy for role gear.
|
||||
/// This is the characher preview panel in the chat. This should only update if their character updates.
|
||||
/// </summary>
|
||||
private EntityUid? _previewDummy;
|
||||
private LobbyCharacterPreviewPanel? PreviewPanel => GetLobbyPreview();
|
||||
|
||||
/// <summary>
|
||||
/// If we currently have a job prototype selected.
|
||||
/// This is the modified profile currently being edited.
|
||||
/// </summary>
|
||||
private JobPrototype? _dummyJob;
|
||||
private HumanoidCharacterProfile? EditedProfile => _profileEditor?.Profile;
|
||||
|
||||
// TODO: Load the species directly and don't update entity ever.
|
||||
public event Action<EntityUid>? PreviewDummyUpdated;
|
||||
|
||||
private HumanoidCharacterProfile? _profile;
|
||||
private int? EditedSlot => _profileEditor?.CharacterSlot;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_prototypeManager.PrototypesReloaded += OnProtoReload;
|
||||
_preferencesManager.OnServerDataLoaded += PreferencesDataLoaded;
|
||||
_requirements.Updated += OnRequirementsUpdated;
|
||||
|
||||
_configurationManager.OnValueChanged(CCVars.FlavorText, args =>
|
||||
{
|
||||
_profileEditor?.RefreshFlavorText();
|
||||
});
|
||||
|
||||
_configurationManager.OnValueChanged(CCVars.GameRoleTimers, args =>
|
||||
{
|
||||
_profileEditor?.RefreshAntags();
|
||||
_profileEditor?.RefreshJobs();
|
||||
_profileEditor?.RefreshLoadouts();
|
||||
});
|
||||
}
|
||||
|
||||
private LobbyCharacterPreviewPanel? GetLobbyPreview()
|
||||
{
|
||||
if (_stateManager.CurrentState is LobbyState lobby)
|
||||
{
|
||||
return lobby.Lobby?.CharacterPreview;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnRequirementsUpdated()
|
||||
{
|
||||
if (_profileEditor != null)
|
||||
{
|
||||
_profileEditor.RefreshAntags();
|
||||
_profileEditor.RefreshJobs();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnProtoReload(PrototypesReloadedEventArgs obj)
|
||||
{
|
||||
if (_profileEditor != null)
|
||||
{
|
||||
if (obj.WasModified<AntagPrototype>())
|
||||
{
|
||||
_profileEditor.RefreshAntags();
|
||||
}
|
||||
|
||||
if (obj.WasModified<JobPrototype>() ||
|
||||
obj.WasModified<DepartmentPrototype>())
|
||||
{
|
||||
_profileEditor.RefreshJobs();
|
||||
}
|
||||
|
||||
if (obj.WasModified<LoadoutPrototype>() ||
|
||||
obj.WasModified<LoadoutGroupPrototype>() ||
|
||||
obj.WasModified<RoleLoadoutPrototype>())
|
||||
{
|
||||
_profileEditor.RefreshLoadouts();
|
||||
}
|
||||
|
||||
if (obj.WasModified<SpeciesPrototype>())
|
||||
{
|
||||
_profileEditor.RefreshSpecies();
|
||||
}
|
||||
|
||||
if (obj.WasModified<TraitPrototype>())
|
||||
{
|
||||
_profileEditor.RefreshTraits();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
PreviewPanel?.SetLoaded(true);
|
||||
|
||||
if (_stateManager.CurrentState is not LobbyState)
|
||||
return;
|
||||
|
||||
_profile = profile;
|
||||
ReloadCharacterSetup();
|
||||
}
|
||||
|
||||
private EntityUid EnsurePreviewDummy(HumanoidCharacterProfile profile)
|
||||
public void OnStateEntered(LobbyState state)
|
||||
{
|
||||
if (_previewDummy != null)
|
||||
return _previewDummy.Value;
|
||||
|
||||
_previewDummy = EntityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(profile.Species).DollPrototype, MapCoordinates.Nullspace);
|
||||
PreviewDummyUpdated?.Invoke(_previewDummy.Value);
|
||||
return _previewDummy.Value;
|
||||
PreviewPanel?.SetLoaded(_preferencesManager.ServerDataLoaded);
|
||||
ReloadCharacterSetup();
|
||||
}
|
||||
|
||||
public void OnStateExited(LobbyState state)
|
||||
{
|
||||
PreviewPanel?.SetLoaded(false);
|
||||
_profileEditor?.Dispose();
|
||||
_characterSetup?.Dispose();
|
||||
|
||||
_characterSetup = null;
|
||||
_profileEditor = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reloads every single character setup control.
|
||||
/// </summary>
|
||||
public void ReloadCharacterSetup()
|
||||
{
|
||||
RefreshLobbyPreview();
|
||||
var (characterGui, profileEditor) = EnsureGui();
|
||||
characterGui.ReloadCharacterPickers();
|
||||
profileEditor.SetProfile(
|
||||
(HumanoidCharacterProfile?) _preferencesManager.Preferences?.SelectedCharacter,
|
||||
_preferencesManager.Preferences?.SelectedCharacterIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the character preview in the lobby chat.
|
||||
/// </summary>
|
||||
private void RefreshLobbyPreview()
|
||||
{
|
||||
if (PreviewPanel == null)
|
||||
return;
|
||||
|
||||
// Get selected character, load it, then set it
|
||||
var character = _preferencesManager.Preferences?.SelectedCharacter;
|
||||
|
||||
if (character is not HumanoidCharacterProfile humanoid)
|
||||
{
|
||||
PreviewPanel.SetSprite(EntityUid.Invalid);
|
||||
PreviewPanel.SetSummaryText(string.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
var dummy = LoadProfileEntity(humanoid, null, true);
|
||||
PreviewPanel.SetSprite(dummy);
|
||||
PreviewPanel.SetSummaryText(humanoid.Summary);
|
||||
}
|
||||
|
||||
private void SaveProfile()
|
||||
{
|
||||
DebugTools.Assert(EditedProfile != null);
|
||||
|
||||
if (EditedProfile == null || EditedSlot == null)
|
||||
return;
|
||||
|
||||
var selected = _preferencesManager.Preferences?.SelectedCharacterIndex;
|
||||
|
||||
if (selected == null)
|
||||
return;
|
||||
|
||||
_preferencesManager.UpdateCharacter(EditedProfile, EditedSlot.Value);
|
||||
ReloadCharacterSetup();
|
||||
}
|
||||
|
||||
private (CharacterSetupGui, HumanoidProfileEditor) EnsureGui()
|
||||
{
|
||||
if (_characterSetup != null && _profileEditor != null)
|
||||
{
|
||||
_characterSetup.Visible = true;
|
||||
_profileEditor.Visible = true;
|
||||
return (_characterSetup, _profileEditor);
|
||||
}
|
||||
|
||||
_profileEditor = new HumanoidProfileEditor(
|
||||
_preferencesManager,
|
||||
_configurationManager,
|
||||
EntityManager,
|
||||
_dialogManager,
|
||||
_logManager,
|
||||
_playerManager,
|
||||
_prototypeManager,
|
||||
_requirements,
|
||||
_markings);
|
||||
|
||||
_characterSetup = new CharacterSetupGui(EntityManager, _prototypeManager, _resourceCache, _preferencesManager, _profileEditor);
|
||||
|
||||
_characterSetup.CloseButton.OnPressed += _ =>
|
||||
{
|
||||
// Reset sliders etc.
|
||||
_profileEditor.SetProfile(null, null);
|
||||
_profileEditor.Visible = false;
|
||||
|
||||
if (_stateManager.CurrentState is LobbyState lobbyGui)
|
||||
{
|
||||
lobbyGui.SwitchState(LobbyGui.LobbyGuiState.Default);
|
||||
}
|
||||
};
|
||||
|
||||
_profileEditor.Save += SaveProfile;
|
||||
|
||||
_characterSetup.SelectCharacter += args =>
|
||||
{
|
||||
_preferencesManager.SelectCharacter(args);
|
||||
ReloadCharacterSetup();
|
||||
};
|
||||
|
||||
_characterSetup.DeleteCharacter += args =>
|
||||
{
|
||||
_preferencesManager.DeleteCharacter(args);
|
||||
|
||||
// Reload everything
|
||||
if (EditedSlot == args)
|
||||
{
|
||||
ReloadCharacterSetup();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Only need to reload character pickers
|
||||
_characterSetup?.ReloadCharacterPickers();
|
||||
}
|
||||
};
|
||||
|
||||
if (_stateManager.CurrentState is LobbyState lobby)
|
||||
{
|
||||
lobby.Lobby?.CharacterSetupState.AddChild(_characterSetup);
|
||||
}
|
||||
|
||||
return (_characterSetup, _profileEditor);
|
||||
}
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Applies the highest priority job's clothes to the dummy.
|
||||
/// </summary>
|
||||
public void GiveDummyJobClothesLoadout(EntityUid dummy, HumanoidCharacterProfile profile)
|
||||
public void GiveDummyJobClothesLoadout(EntityUid dummy, JobPrototype? jobProto, HumanoidCharacterProfile profile)
|
||||
{
|
||||
var job = _dummyJob ?? GetPreferredJob(profile);
|
||||
var job = jobProto ?? GetPreferredJob(profile);
|
||||
GiveDummyJobClothes(dummy, profile, job);
|
||||
|
||||
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
|
||||
{
|
||||
var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), EntityManager, _prototypeManager);
|
||||
var loadout = profile.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), profile.Species, EntityManager, _prototypeManager);
|
||||
GiveDummyLoadout(dummy, loadout);
|
||||
}
|
||||
}
|
||||
@@ -280,8 +382,39 @@ public sealed partial class LobbyUIController : UIController, IOnStateEntered<Lo
|
||||
}
|
||||
}
|
||||
|
||||
public EntityUid? GetPreviewDummy()
|
||||
/// <summary>
|
||||
/// Loads the profile onto a dummy entity.
|
||||
/// </summary>
|
||||
public EntityUid LoadProfileEntity(HumanoidCharacterProfile? humanoid, JobPrototype? job, bool jobClothes)
|
||||
{
|
||||
return _previewDummy;
|
||||
EntityUid dummyEnt;
|
||||
|
||||
if (humanoid is not null)
|
||||
{
|
||||
var dummy = _prototypeManager.Index<SpeciesPrototype>(humanoid.Species).DollPrototype;
|
||||
dummyEnt = EntityManager.SpawnEntity(dummy, MapCoordinates.Nullspace);
|
||||
}
|
||||
else
|
||||
{
|
||||
dummyEnt = EntityManager.SpawnEntity(_prototypeManager.Index<SpeciesPrototype>(SharedHumanoidAppearanceSystem.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
|
||||
}
|
||||
|
||||
_humanoid.LoadProfile(dummyEnt, humanoid);
|
||||
|
||||
if (humanoid != null && jobClothes)
|
||||
{
|
||||
job ??= GetPreferredJob(humanoid);
|
||||
GiveDummyJobClothes(dummyEnt, humanoid, job);
|
||||
|
||||
if (_prototypeManager.HasIndex<RoleLoadoutPrototype>(LoadoutSystem.GetJobPrototype(job.ID)))
|
||||
{
|
||||
var loadout = humanoid.GetLoadoutOrDefault(LoadoutSystem.GetJobPrototype(job.ID), humanoid.Species, EntityManager, _prototypeManager);
|
||||
GiveDummyLoadout(dummyEnt, loadout);
|
||||
}
|
||||
}
|
||||
|
||||
return dummyEnt;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
22
Content.Client/Lobby/UI/CharacterPickerButton.xaml
Normal file
22
Content.Client/Lobby/UI/CharacterPickerButton.xaml
Normal file
@@ -0,0 +1,22 @@
|
||||
<ContainerButton xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:style="clr-namespace:Content.Client.Stylesheets">
|
||||
<BoxContainer Orientation="Horizontal"
|
||||
HorizontalExpand="True"
|
||||
SeparationOverride="0"
|
||||
Name="InternalHBox">
|
||||
<SpriteView Scale="2 2"
|
||||
OverrideDirection="South"
|
||||
Name="View"/>
|
||||
<Label Name="DescriptionLabel"
|
||||
ClipText="True"
|
||||
HorizontalExpand="True"/>
|
||||
<Button Name="DeleteButton"
|
||||
Text="{Loc 'character-setup-gui-character-picker-button-delete-button'}"/>
|
||||
<Button Name="ConfirmDeleteButton"
|
||||
Text="{Loc 'character-setup-gui-character-picker-button-confirm-delete-button'}"
|
||||
Visible="False"
|
||||
ModulateSelfOverride="{x:Static style:StyleNano.ButtonColorCautionDefault}"/>
|
||||
|
||||
</BoxContainer>
|
||||
</ContainerButton>
|
||||
92
Content.Client/Lobby/UI/CharacterPickerButton.xaml.cs
Normal file
92
Content.Client/Lobby/UI/CharacterPickerButton.xaml.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System.Linq;
|
||||
using Content.Client.Humanoid;
|
||||
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.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Lobby.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Holds character data on the side of the setup GUI.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CharacterPickerButton : ContainerButton
|
||||
{
|
||||
private IEntityManager _entManager;
|
||||
|
||||
private EntityUid _previewDummy;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked if we should delete the attached character
|
||||
/// </summary>
|
||||
public event Action? OnDeletePressed;
|
||||
|
||||
public CharacterPickerButton(
|
||||
IEntityManager entityManager,
|
||||
IPrototypeManager prototypeManager,
|
||||
ButtonGroup group,
|
||||
ICharacterProfile profile,
|
||||
bool isSelected)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_entManager = entityManager;
|
||||
AddStyleClass(StyleClassButton);
|
||||
ToggleMode = true;
|
||||
Group = group;
|
||||
var description = profile.Name;
|
||||
|
||||
if (profile is not HumanoidCharacterProfile humanoid)
|
||||
{
|
||||
_previewDummy = entityManager.SpawnEntity(prototypeManager.Index<SpeciesPrototype>(SharedHumanoidAppearanceSystem.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
|
||||
}
|
||||
else
|
||||
{
|
||||
_previewDummy = UserInterfaceManager.GetUIController<LobbyUIController>()
|
||||
.LoadProfileEntity(humanoid, null, true);
|
||||
|
||||
var highPriorityJob = humanoid.JobPriorities.SingleOrDefault(p => p.Value == JobPriority.High).Key;
|
||||
if (highPriorityJob != null)
|
||||
{
|
||||
var jobName = prototypeManager.Index<JobPrototype>(highPriorityJob).LocalizedName;
|
||||
description = $"{description}\n{jobName}";
|
||||
}
|
||||
}
|
||||
|
||||
Pressed = isSelected;
|
||||
DeleteButton.Visible = !isSelected;
|
||||
|
||||
View.SetEntity(_previewDummy);
|
||||
DescriptionLabel.Text = description;
|
||||
|
||||
ConfirmDeleteButton.OnPressed += _ =>
|
||||
{
|
||||
Parent?.RemoveChild(this);
|
||||
Parent?.RemoveChild(ConfirmDeleteButton);
|
||||
OnDeletePressed?.Invoke();
|
||||
};
|
||||
|
||||
DeleteButton.OnPressed += _ =>
|
||||
{
|
||||
DeleteButton.Visible = false;
|
||||
ConfirmDeleteButton.Visible = true;
|
||||
};
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_entManager.DeleteEntity(_previewDummy);
|
||||
_previewDummy = default;
|
||||
}
|
||||
}
|
||||
@@ -23,10 +23,6 @@
|
||||
<Button Name="RulesButton"
|
||||
Text="{Loc 'character-setup-gui-character-setup-rules-button'}"
|
||||
StyleClasses="ButtonBig"/>
|
||||
<Button Name="SaveButton"
|
||||
Access="Public"
|
||||
Text="{Loc 'character-setup-gui-character-setup-save-button'}"
|
||||
StyleClasses="ButtonBig"/>
|
||||
<Button Name="CloseButton"
|
||||
Access="Public"
|
||||
Text="{Loc 'character-setup-gui-character-setup-close-button'}"
|
||||
118
Content.Client/Lobby/UI/CharacterSetupGui.xaml.cs
Normal file
118
Content.Client/Lobby/UI/CharacterSetupGui.xaml.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Info.PlaytimeStats;
|
||||
using Content.Client.Resources;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds the entire character setup GUI, from character picks to individual character editing.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CharacterSetupGui : Control
|
||||
{
|
||||
private readonly IClientPreferencesManager _preferencesManager;
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IPrototypeManager _protomanager;
|
||||
|
||||
private readonly Button _createNewCharacterButton;
|
||||
|
||||
public event Action<int>? SelectCharacter;
|
||||
public event Action<int>? DeleteCharacter;
|
||||
|
||||
public CharacterSetupGui(
|
||||
IEntityManager entManager,
|
||||
IPrototypeManager protoManager,
|
||||
IResourceCache resourceCache,
|
||||
IClientPreferencesManager preferencesManager,
|
||||
HumanoidProfileEditor profileEditor)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_preferencesManager = preferencesManager;
|
||||
_entManager = entManager;
|
||||
_protomanager = protoManager;
|
||||
|
||||
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
{
|
||||
Texture = panelTex,
|
||||
Modulate = new Color(37, 37, 42)
|
||||
};
|
||||
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
|
||||
BackgroundPanel.PanelOverride = back;
|
||||
|
||||
_createNewCharacterButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("character-setup-gui-create-new-character-button"),
|
||||
};
|
||||
|
||||
_createNewCharacterButton.OnPressed += args =>
|
||||
{
|
||||
preferencesManager.CreateCharacter(HumanoidCharacterProfile.Random());
|
||||
ReloadCharacterPickers();
|
||||
args.Event.Handle();
|
||||
};
|
||||
|
||||
CharEditor.AddChild(profileEditor);
|
||||
RulesButton.OnPressed += _ => new RulesAndInfoWindow().Open();
|
||||
|
||||
StatsButton.OnPressed += _ => new PlaytimeStatsWindow().OpenCentered();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes and reloads all character picker buttons from the preferences data.
|
||||
/// </summary>
|
||||
public void ReloadCharacterPickers()
|
||||
{
|
||||
_createNewCharacterButton.Orphan();
|
||||
Characters.DisposeAllChildren();
|
||||
|
||||
var numberOfFullSlots = 0;
|
||||
var characterButtonsGroup = new ButtonGroup();
|
||||
|
||||
if (!_preferencesManager.ServerDataLoaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_createNewCharacterButton.ToolTip =
|
||||
Loc.GetString("character-setup-gui-create-new-character-button-tooltip",
|
||||
("maxCharacters", _preferencesManager.Settings!.MaxCharacterSlots));
|
||||
|
||||
var selectedSlot = _preferencesManager.Preferences?.SelectedCharacterIndex;
|
||||
|
||||
foreach (var (slot, character) in _preferencesManager.Preferences!.Characters)
|
||||
{
|
||||
numberOfFullSlots++;
|
||||
var characterPickerButton = new CharacterPickerButton(_entManager,
|
||||
_protomanager,
|
||||
characterButtonsGroup,
|
||||
character,
|
||||
slot == selectedSlot);
|
||||
|
||||
Characters.AddChild(characterPickerButton);
|
||||
|
||||
characterPickerButton.OnPressed += args =>
|
||||
{
|
||||
SelectCharacter?.Invoke(slot);
|
||||
};
|
||||
|
||||
characterPickerButton.OnDeletePressed += () =>
|
||||
{
|
||||
DeleteCharacter?.Invoke(slot);
|
||||
};
|
||||
}
|
||||
|
||||
_createNewCharacterButton.Disabled = numberOfFullSlots >= _preferencesManager.Settings.MaxCharacterSlots;
|
||||
Characters.AddChild(_createNewCharacterButton);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
namespace Content.Client.Lobby.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class HighlightedContainer : PanelContainer
|
||||
@@ -1,54 +1,50 @@
|
||||
<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"
|
||||
xmlns:ui="clr-namespace:Content.Client.Lobby.UI"
|
||||
HorizontalExpand="True">
|
||||
<!-- Left side -->
|
||||
<BoxContainer Orientation="Vertical" Margin="10 10 10 10" HorizontalExpand="True">
|
||||
<!-- Middle container -->
|
||||
<BoxContainer Orientation="Horizontal" SeparationOverride="10">
|
||||
<BoxContainer Orientation="Horizontal" SeparationOverride="10" HorizontalExpand="True">
|
||||
<!-- Name box-->
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<prefUi:HighlightedContainer>
|
||||
<ui:HighlightedContainer>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<prefUi:HighlightedContainer>
|
||||
<ui:HighlightedContainer>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<BoxContainer Orientation="Horizontal" VerticalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-name-label'}" />
|
||||
<LineEdit Name="CNameEdit" MinSize="270 0" VerticalAlignment="Center" Margin="5 0 0 0" />
|
||||
<Button Name="CNameRandomize" Text="{Loc 'humanoid-profile-editor-name-random-button'}" />
|
||||
<LineEdit Name="NameEdit" MinSize="270 0" VerticalAlignment="Center" Margin="5 0 0 0" />
|
||||
<Button Name="NameRandomize" Text="{Loc 'humanoid-profile-editor-name-random-button'}" />
|
||||
</BoxContainer>
|
||||
<Button Name="CRandomizeEverything" HorizontalAlignment="Center"
|
||||
<Button Name="RandomizeEverythingButton" HorizontalAlignment="Center"
|
||||
HorizontalExpand="False" MaxWidth="256"
|
||||
Text="{Loc 'humanoid-profile-editor-randomize-everything-button'}" />
|
||||
<RichTextLabel Name="CWarningLabel" HorizontalExpand="False"
|
||||
<RichTextLabel Name="WarningLabel" HorizontalExpand="False"
|
||||
VerticalExpand="True" MaxWidth="425"
|
||||
HorizontalAlignment="Left" />
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
</ui:HighlightedContainer>
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
</ui:HighlightedContainer>
|
||||
</BoxContainer>
|
||||
<!-- Import/Export -->
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<prefUi:HighlightedContainer>
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Button Text="{Loc 'humanoid-profile-editor-import-button'}" Disabled="True"
|
||||
ToolTip="{Loc 'generic-not-yet-implemented'}" />
|
||||
<Button Text="{Loc 'humanoid-profile-editor-export-button'}" Disabled="True"
|
||||
ToolTip="{Loc 'generic-not-yet-implemented'}" />
|
||||
<BoxContainer Orientation="Vertical" MinSize="60 0" HorizontalExpand="True" HorizontalAlignment="Right">
|
||||
<ui:HighlightedContainer Name="ProfileHighlight">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<Button Name="SaveButton" Text="{Loc 'humanoid-profile-editor-save-button'}"/>
|
||||
<Button Name="ResetButton" Disabled="True" Text="{Loc 'humanoid-profile-editor-reset-button'}"/>
|
||||
<Button Name="ImportButton" Text="{Loc 'humanoid-profile-editor-import-button'}"/>
|
||||
<Button Name="ExportButton" Text="{Loc 'humanoid-profile-editor-export-button'}"/>
|
||||
</BoxContainer>
|
||||
</prefUi:HighlightedContainer>
|
||||
<!-- Save -->
|
||||
<prefUi:HighlightedContainer>
|
||||
<Button Name="CSaveButton" Text="{Loc 'humanoid-profile-editor-save-button'}" HorizontalAlignment="Center" />
|
||||
</prefUi:HighlightedContainer>
|
||||
</ui:HighlightedContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
<Control MinHeight="10" />
|
||||
<!-- tabContainer -->
|
||||
<TabContainer Name="CTabContainer" VerticalExpand="True">
|
||||
<TabContainer Name="TabContainer" VerticalExpand="True">
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<!-- appearanceList -->
|
||||
@@ -61,25 +57,25 @@
|
||||
<TextureButton Name="SpeciesInfoButton" Scale="0.3 0.3"
|
||||
VerticalAlignment="Center"
|
||||
ToolTip="{Loc 'humanoid-profile-editor-guidebook-button-tooltip'}"/>
|
||||
<OptionButton Name="CSpeciesButton" HorizontalAlignment="Right" />
|
||||
<OptionButton Name="SpeciesButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Age -->
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-age-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<LineEdit Name="CAgeEdit" MinSize="40 0" HorizontalAlignment="Right" />
|
||||
<LineEdit Name="AgeEdit" MinSize="40 0" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Sex -->
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-sex-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="CSexButton" HorizontalAlignment="Right" />
|
||||
<OptionButton Name="SexButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Pronouns -->
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-pronouns-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="CPronounsButton" HorizontalAlignment="Right" />
|
||||
<OptionButton Name="PronounsButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Show clothing -->
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
@@ -91,7 +87,7 @@
|
||||
<BoxContainer HorizontalExpand="True">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-spawn-priority-label'}" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<OptionButton Name="CSpawnPriorityButton" HorizontalAlignment="Right" />
|
||||
<OptionButton Name="SpawnPriorityButton" HorizontalAlignment="Right" />
|
||||
</BoxContainer>
|
||||
<!-- Corvax-TTS-Start -->
|
||||
<BoxContainer HorizontalExpand="True" Visible="False" Name="TTSContainer">
|
||||
@@ -105,56 +101,56 @@
|
||||
<!-- Skin -->
|
||||
<BoxContainer Margin="10" HorizontalExpand="True" Orientation="Vertical">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-skin-color-label'}" />
|
||||
<Slider HorizontalExpand="True" Name="CSkin" MinValue="0" MaxValue="100" Value="20" />
|
||||
<BoxContainer Name="CRgbSkinColorContainer" Visible="False" Orientation="Vertical" HorizontalExpand="True"></BoxContainer>
|
||||
<Slider HorizontalExpand="True" Name="Skin" MinValue="0" MaxValue="100" Value="20" />
|
||||
<BoxContainer Name="RgbSkinColorContainer" Visible="False" Orientation="Vertical" HorizontalExpand="True"></BoxContainer>
|
||||
</BoxContainer>
|
||||
<!-- Hair -->
|
||||
<BoxContainer Margin="10" Orientation="Horizontal">
|
||||
<humanoid:SingleMarkingPicker Name="CHairStylePicker" Category="Hair" />
|
||||
<humanoid:SingleMarkingPicker Name="CFacialHairPicker" Category="FacialHair" />
|
||||
<humanoid:SingleMarkingPicker Name="HairStylePicker" Category="Hair" />
|
||||
<humanoid:SingleMarkingPicker Name="FacialHairPicker" Category="FacialHair" />
|
||||
</BoxContainer>
|
||||
<!-- Eyes -->
|
||||
<BoxContainer Margin="10" Orientation="Vertical">
|
||||
<Label Text="{Loc 'humanoid-profile-editor-eyes-label'}" />
|
||||
<humanoid:EyeColorPicker Name="CEyeColorPicker" />
|
||||
<humanoid:EyeColorPicker Name="EyeColorPicker" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical">
|
||||
<!-- Jobs -->
|
||||
<OptionButton Name="CPreferenceUnavailableButton" />
|
||||
<OptionButton Name="PreferenceUnavailableButton" />
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<BoxContainer Name="CJobList" Orientation="Vertical" />
|
||||
<BoxContainer Name="JobList" Orientation="Vertical" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" Margin="10">
|
||||
<!-- Antags -->
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<BoxContainer Name="CAntagList" Orientation="Vertical" />
|
||||
<BoxContainer Name="AntagList" Orientation="Vertical" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" Margin="10">
|
||||
<!-- Traits -->
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<BoxContainer Name="CTraitsList" Orientation="Vertical" />
|
||||
<BoxContainer Name="TraitsList" Orientation="Vertical" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
<BoxContainer Name="CMarkingsTab" Orientation="Vertical" Margin="10">
|
||||
<BoxContainer Name="MarkingsTab" Orientation="Vertical" Margin="10">
|
||||
<!-- Markings -->
|
||||
<ScrollContainer VerticalExpand="True">
|
||||
<humanoid:MarkingPicker Name="CMarkings" IgnoreCategories="Hair,FacialHair" />
|
||||
<humanoid:MarkingPicker Name="Markings" IgnoreCategories="Hair,FacialHair" />
|
||||
</ScrollContainer>
|
||||
</BoxContainer>
|
||||
</TabContainer>
|
||||
</BoxContainer>
|
||||
<!-- Right side -->
|
||||
<BoxContainer Orientation="Vertical" VerticalExpand="True" VerticalAlignment="Center">
|
||||
<SpriteView Name="CSpriteView" Scale="8 8" SizeFlagsStretchRatio="1" />
|
||||
<SpriteView Name="SpriteView" Scale="8 8" SizeFlagsStretchRatio="1" />
|
||||
<BoxContainer Orientation="Horizontal" HorizontalAlignment="Center" Margin="0 5">
|
||||
<Button Name="CSpriteRotateLeft" Text="◀" StyleClasses="OpenRight" />
|
||||
<Button Name="SpriteRotateLeft" Text="◀" StyleClasses="OpenRight" />
|
||||
<cc:VSeparator Margin="2 0 3 0" />
|
||||
<Button Name="CSpriteRotateRight" Text="▶" StyleClasses="OpenLeft" />
|
||||
<Button Name="SpriteRotateRight" Text="▶" StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
namespace Content.Client.Lobby.UI.Loadouts;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LoadoutContainer : BoxContainer
|
||||
@@ -45,7 +45,7 @@ public sealed partial class LoadoutContainer : BoxContainer
|
||||
|
||||
var spriteTooltip = new Tooltip();
|
||||
spriteTooltip.SetMessage(FormattedMessage.FromUnformatted(_entManager.GetComponent<MetaDataComponent>(_entity.Value).EntityDescription));
|
||||
Sprite.TooltipSupplier = _ => spriteTooltip;
|
||||
TooltipSupplier = _ => spriteTooltip;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using Content.Corvax.Interfaces.Shared;
|
||||
using Content.Shared.Clothing;
|
||||
using Content.Shared.Preferences;
|
||||
using Content.Shared.Preferences.Loadouts;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -8,7 +9,7 @@ using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Preferences.UI;
|
||||
namespace Content.Client.Lobby.UI.Loadouts;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LoadoutGroupContainer : BoxContainer
|
||||
@@ -18,18 +19,18 @@ public sealed partial class LoadoutGroupContainer : BoxContainer
|
||||
public event Action<ProtoId<LoadoutPrototype>>? OnLoadoutPressed;
|
||||
public event Action<ProtoId<LoadoutPrototype>>? OnLoadoutUnpressed;
|
||||
|
||||
public LoadoutGroupContainer(RoleLoadout loadout, LoadoutGroupPrototype groupProto, ICommonSession session, IDependencyCollection collection)
|
||||
public LoadoutGroupContainer(HumanoidCharacterProfile profile, RoleLoadout loadout, LoadoutGroupPrototype groupProto, ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_groupProto = groupProto;
|
||||
|
||||
RefreshLoadouts(loadout, session, collection);
|
||||
RefreshLoadouts(profile, loadout, session, collection);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates button availabilities and buttons.
|
||||
/// </summary>
|
||||
public void RefreshLoadouts(RoleLoadout loadout, ICommonSession session, IDependencyCollection collection)
|
||||
public void RefreshLoadouts(HumanoidCharacterProfile profile, RoleLoadout loadout, ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
var protoMan = collection.Resolve<IPrototypeManager>();
|
||||
var loadoutSystem = collection.Resolve<IEntityManager>().System<LoadoutSystem>();
|
||||
@@ -83,7 +84,7 @@ public sealed partial class LoadoutGroupContainer : BoxContainer
|
||||
var matchingLoadout = selected.FirstOrDefault(e => e.Prototype == loadoutProto);
|
||||
var pressed = matchingLoadout != null;
|
||||
|
||||
var enabled = loadout.IsValid(session, loadoutProto, collection, out var reason);
|
||||
var enabled = loadout.IsValid(profile, session, loadoutProto, collection, out var reason);
|
||||
var loadoutContainer = new LoadoutContainer(loadoutProto, !enabled, reason);
|
||||
loadoutContainer.Select.Pressed = pressed;
|
||||
loadoutContainer.Text = loadoutSystem.GetName(loadProto);
|
||||
@@ -1,13 +1,12 @@
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Preferences;
|
||||
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;
|
||||
namespace Content.Client.Lobby.UI.Loadouts;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LoadoutWindow : FancyWindow
|
||||
@@ -17,9 +16,12 @@ public sealed partial class LoadoutWindow : FancyWindow
|
||||
|
||||
private List<LoadoutGroupContainer> _groups = new();
|
||||
|
||||
public LoadoutWindow(RoleLoadout loadout, RoleLoadoutPrototype proto, ICommonSession session, IDependencyCollection collection)
|
||||
public HumanoidCharacterProfile Profile;
|
||||
|
||||
public LoadoutWindow(HumanoidCharacterProfile profile, RoleLoadout loadout, RoleLoadoutPrototype proto, ICommonSession session, IDependencyCollection collection)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
Profile = profile;
|
||||
var protoManager = collection.Resolve<IPrototypeManager>();
|
||||
|
||||
foreach (var group in proto.Groups)
|
||||
@@ -27,7 +29,7 @@ public sealed partial class LoadoutWindow : FancyWindow
|
||||
if (!protoManager.TryIndex(group, out var groupProto))
|
||||
continue;
|
||||
|
||||
var container = new LoadoutGroupContainer(loadout, protoManager.Index(group), session, collection);
|
||||
var container = new LoadoutGroupContainer(profile, loadout, protoManager.Index(group), session, collection);
|
||||
LoadoutGroupsContainer.AddTab(container, Loc.GetString(groupProto.Name));
|
||||
_groups.Add(container);
|
||||
|
||||
@@ -43,18 +45,11 @@ public sealed partial class LoadoutWindow : FancyWindow
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
group.RefreshLoadouts(Profile, loadout, session, collection);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,16 @@ namespace Content.Client.Lobby.UI;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class LobbyCharacterPreviewPanel : Control
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
public Button CharacterSetupButton => CharacterSetup;
|
||||
|
||||
private EntityUid? _previewDummy;
|
||||
|
||||
public LobbyCharacterPreviewPanel()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
UserInterfaceManager.GetUIController<LobbyUIController>().SetPreviewPanel(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void SetLoaded(bool value)
|
||||
@@ -26,11 +30,18 @@ public sealed partial class LobbyCharacterPreviewPanel : Control
|
||||
|
||||
public void SetSummaryText(string value)
|
||||
{
|
||||
Summary.Text = string.Empty;
|
||||
Summary.Text = value;
|
||||
}
|
||||
|
||||
public void SetSprite(EntityUid uid)
|
||||
{
|
||||
if (_previewDummy != null)
|
||||
{
|
||||
_entManager.DeleteEntity(_previewDummy);
|
||||
}
|
||||
|
||||
_previewDummy = uid;
|
||||
|
||||
ViewBox.DisposeAllChildren();
|
||||
var spriteView = new SpriteView
|
||||
{
|
||||
@@ -42,4 +53,11 @@ public sealed partial class LobbyCharacterPreviewPanel : Control
|
||||
spriteView.SetEntity(uid);
|
||||
ViewBox.AddChild(spriteView);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
_entManager.DeleteEntity(_previewDummy);
|
||||
_previewDummy = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,9 @@ using Robust.Client.UserInterface.XAML;
|
||||
namespace Content.Client.Lobby.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
internal sealed partial class LobbyGui : UIScreen
|
||||
public sealed partial class LobbyGui : UIScreen
|
||||
{
|
||||
[Dependency] private readonly IClientConsoleHost _consoleHost = default!;
|
||||
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
|
||||
|
||||
public LobbyGui()
|
||||
{
|
||||
@@ -23,7 +22,7 @@ namespace Content.Client.Lobby.UI
|
||||
LobbySong.SetMarkup(Loc.GetString("lobby-state-song-no-song-text"));
|
||||
|
||||
LeaveButton.OnPressed += _ => _consoleHost.ExecuteCommand("disconnect");
|
||||
OptionsButton.OnPressed += _ => _userInterfaceManager.GetUIController<OptionsUIController>().ToggleWindow();
|
||||
OptionsButton.OnPressed += _ => UserInterfaceManager.GetUIController<OptionsUIController>().ToggleWindow();
|
||||
}
|
||||
|
||||
public void SwitchState(LobbyGuiState state)
|
||||
@@ -40,7 +39,7 @@ namespace Content.Client.Lobby.UI
|
||||
case LobbyGuiState.CharacterSetup:
|
||||
CharacterSetupState.Visible = true;
|
||||
|
||||
var actualWidth = (float) _userInterfaceManager.RootControl.PixelWidth;
|
||||
var actualWidth = (float) UserInterfaceManager.RootControl.PixelWidth;
|
||||
var setupWidth = (float) LeftSide.PixelWidth;
|
||||
|
||||
if (1 - (setupWidth / actualWidth) > 0.30)
|
||||
@@ -48,6 +47,8 @@ namespace Content.Client.Lobby.UI
|
||||
RightSide.Visible = false;
|
||||
}
|
||||
|
||||
UserInterfaceManager.GetUIController<LobbyUIController>().ReloadCharacterSetup();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,20 @@ using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Client.Lobby.UI
|
||||
namespace Content.Client.Lobby.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
[UsedImplicitly]
|
||||
public sealed partial class ObserveWarningWindow : DefaultWindow
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
[UsedImplicitly]
|
||||
internal sealed partial class ObserveWarningWindow : DefaultWindow
|
||||
public ObserveWarningWindow()
|
||||
{
|
||||
public ObserveWarningWindow()
|
||||
{
|
||||
Title = Loc.GetString("observe-warning-window-title");
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
Title = Loc.GetString("observe-warning-window-title");
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
ObserveButton.OnPressed += _ => { this.Close(); };
|
||||
NevermindButton.OnPressed += _ => { this.Close(); };
|
||||
}
|
||||
ObserveButton.OnPressed += _ => { this.Close(); };
|
||||
NevermindButton.OnPressed += _ => { this.Close(); };
|
||||
}
|
||||
}
|
||||
|
||||
9
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml
Normal file
9
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<BoxContainer xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Orientation="Horizontal">
|
||||
<Label Name="TitleLabel"
|
||||
Margin="5 0"
|
||||
MouseFilter="Stop"/>
|
||||
<BoxContainer Name="OptionsContainer"
|
||||
SetWidth="400"/>
|
||||
</BoxContainer>
|
||||
118
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs
Normal file
118
Content.Client/Lobby/UI/Roles/RequirementsSelector.xaml.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Lobby.UI.Roles;
|
||||
|
||||
/// <summary>
|
||||
/// A generic locking selector.
|
||||
/// </summary>
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class RequirementsSelector : BoxContainer
|
||||
{
|
||||
private readonly RadioOptions<int> _options;
|
||||
private readonly StripeBack _lockStripe;
|
||||
|
||||
public event Action<int>? OnSelected;
|
||||
|
||||
public int Selected => _options.SelectedId;
|
||||
|
||||
public RequirementsSelector()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_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);
|
||||
OnSelected?.Invoke(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
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Actually adds the controls.
|
||||
/// </summary>
|
||||
public 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);
|
||||
}
|
||||
|
||||
TitleLabel.Text = title;
|
||||
TitleLabel.MinSize = new Vector2(titleSize, 0f);
|
||||
TitleLabel.ToolTip = description;
|
||||
|
||||
if (icon != null)
|
||||
{
|
||||
AddChild(icon);
|
||||
icon.SetPositionFirst();
|
||||
}
|
||||
|
||||
OptionsContainer.AddChild(_options);
|
||||
OptionsContainer.AddChild(_lockStripe);
|
||||
}
|
||||
|
||||
public void LockRequirements(FormattedMessage requirements)
|
||||
{
|
||||
var tooltip = new Tooltip();
|
||||
tooltip.SetMessage(requirements);
|
||||
_lockStripe.TooltipSupplier = _ => tooltip;
|
||||
_lockStripe.Visible = true;
|
||||
_options.Visible = false;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
public void Select(int id)
|
||||
{
|
||||
_options.Select(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<Control xmlns="https://spacestation14.io"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
<BoxContainer Name="Container"
|
||||
Orientation="Horizontal">
|
||||
<CheckBox Name="Checkbox"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
@@ -0,0 +1,36 @@
|
||||
using Content.Shared.Traits;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.Lobby.UI.Roles;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class TraitPreferenceSelector : Control
|
||||
{
|
||||
public bool Preference
|
||||
{
|
||||
get => Checkbox.Pressed;
|
||||
set => Checkbox.Pressed = value;
|
||||
}
|
||||
|
||||
public event Action<bool>? PreferenceChanged;
|
||||
|
||||
public TraitPreferenceSelector(TraitPrototype trait)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
Checkbox.Text = Loc.GetString(trait.Name);
|
||||
Checkbox.OnToggled += OnCheckBoxToggled;
|
||||
|
||||
if (trait.Description is { } desc)
|
||||
{
|
||||
Checkbox.ToolTip = Loc.GetString(desc);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCheckBoxToggled(BaseButton.ButtonToggledEventArgs args)
|
||||
{
|
||||
PreferenceChanged?.Invoke(Preference);
|
||||
}
|
||||
}
|
||||
5
Content.Client/Magic/MagicSystem.cs
Normal file
5
Content.Client/Magic/MagicSystem.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.Magic;
|
||||
|
||||
namespace Content.Client.Magic;
|
||||
|
||||
public sealed class MagicSystem : SharedMagicSystem;
|
||||
@@ -2,6 +2,7 @@
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.Replays.Loading;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -37,7 +38,7 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem
|
||||
if (mapPos.MapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
var angle = (mapPos.Position - xform.MapPosition.Position).ToWorldAngle();
|
||||
var angle = (mapPos.Position - _transform.GetMapCoordinates(player.Value, xform: xform).Position).ToWorldAngle();
|
||||
|
||||
var curRot = _transform.GetWorldRotation(xform);
|
||||
|
||||
|
||||
@@ -139,11 +139,13 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem
|
||||
|
||||
public sealed class ClearAllNetworkLinkOverlays : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "clearnetworklinkoverlays";
|
||||
public string Description => "Clear all network link overlays.";
|
||||
public string Help => Command;
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
IoCManager.Resolve<IEntityManager>().System<NetworkConfiguratorSystem>().ClearAllOverlays();
|
||||
_e.System<NetworkConfiguratorSystem>().ClearAllOverlays();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.Client.NodeContainer
|
||||
{
|
||||
public sealed class NodeVisCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "nodevis";
|
||||
public string Description => "Toggles node group visualization";
|
||||
public string Help => "";
|
||||
@@ -21,20 +23,22 @@ namespace Content.Client.NodeContainer
|
||||
return;
|
||||
}
|
||||
|
||||
var sys = EntitySystem.Get<NodeGroupSystem>();
|
||||
var sys = _e.System<NodeGroupSystem>();
|
||||
sys.SetVisEnabled(!sys.VisEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class NodeVisFilterCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "nodevisfilter";
|
||||
public string Description => "Toggles showing a specific group on nodevis";
|
||||
public string Help => "Usage: nodevis [filter]\nOmit filter to list currently masked-off";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var sys = EntitySystem.Get<NodeGroupSystem>();
|
||||
var sys = _e.System<NodeGroupSystem>();
|
||||
|
||||
if (args.Length == 0)
|
||||
{
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Client.Humanoid;
|
||||
using Content.Client.Info;
|
||||
using Content.Client.Info.PlaytimeStats;
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Corvax.Interfaces.Client;
|
||||
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.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||
using Direction = Robust.Shared.Maths.Direction;
|
||||
|
||||
namespace Content.Client.Preferences.UI
|
||||
{
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class CharacterSetupGui : Control
|
||||
{
|
||||
private readonly IClientPreferencesManager _preferencesManager;
|
||||
private readonly IEntityManager _entityManager;
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
private readonly Button _createNewCharacterButton;
|
||||
private readonly HumanoidProfileEditor _humanoidProfileEditor;
|
||||
|
||||
public CharacterSetupGui(
|
||||
IEntityManager entityManager,
|
||||
IResourceCache resourceCache,
|
||||
IClientPreferencesManager preferencesManager,
|
||||
IPrototypeManager prototypeManager,
|
||||
IConfigurationManager configurationManager)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_entityManager = entityManager;
|
||||
_prototypeManager = prototypeManager;
|
||||
_preferencesManager = preferencesManager;
|
||||
|
||||
var panelTex = resourceCache.GetTexture("/Textures/Interface/Nano/button.svg.96dpi.png");
|
||||
var back = new StyleBoxTexture
|
||||
{
|
||||
Texture = panelTex,
|
||||
Modulate = new Color(37, 37, 42)
|
||||
};
|
||||
back.SetPatchMargin(StyleBox.Margin.All, 10);
|
||||
|
||||
BackgroundPanel.PanelOverride = back;
|
||||
|
||||
_createNewCharacterButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("character-setup-gui-create-new-character-button"),
|
||||
};
|
||||
_createNewCharacterButton.OnPressed += args =>
|
||||
{
|
||||
preferencesManager.CreateCharacter(HumanoidCharacterProfile.Random());
|
||||
UpdateUI();
|
||||
args.Event.Handle();
|
||||
};
|
||||
|
||||
_humanoidProfileEditor = new HumanoidProfileEditor(preferencesManager, prototypeManager, configurationManager);
|
||||
_humanoidProfileEditor.OnProfileChanged += ProfileChanged;
|
||||
CharEditor.AddChild(_humanoidProfileEditor);
|
||||
|
||||
UpdateUI();
|
||||
|
||||
RulesButton.OnPressed += _ => new RulesAndInfoWindow().Open();
|
||||
|
||||
StatsButton.OnPressed += _ => new PlaytimeStatsWindow().OpenCentered();
|
||||
preferencesManager.OnServerDataLoaded += UpdateUI;
|
||||
// Corvax-Sponsors-Start
|
||||
if (IoCManager.Instance!.TryResolveType<ISponsorWindowCreator>(out var creator))
|
||||
{
|
||||
SponsorButton.Visible = true;
|
||||
SponsorButton.OnPressed += _ => creator.OpenWindow();
|
||||
}
|
||||
// Corvax-Sponsors-End
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
_preferencesManager.OnServerDataLoaded -= UpdateUI;
|
||||
}
|
||||
|
||||
public void Save() => _humanoidProfileEditor.Save();
|
||||
|
||||
private void ProfileChanged(ICharacterProfile profile, int profileSlot)
|
||||
{
|
||||
_humanoidProfileEditor.UpdateControls();
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public void UpdateControls()
|
||||
{
|
||||
// Reset sliders etc. upon going going back to GUI.
|
||||
_humanoidProfileEditor.LoadServerData();
|
||||
}
|
||||
|
||||
private void UpdateUI()
|
||||
{
|
||||
var numberOfFullSlots = 0;
|
||||
var characterButtonsGroup = new ButtonGroup();
|
||||
Characters.RemoveAllChildren();
|
||||
|
||||
if (!_preferencesManager.ServerDataLoaded)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_createNewCharacterButton.ToolTip =
|
||||
Loc.GetString("character-setup-gui-create-new-character-button-tooltip",
|
||||
("maxCharacters", _preferencesManager.Settings!.MaxCharacterSlots));
|
||||
|
||||
var isDisplayedMaxSlots = false; // Corvax-Sponsors: Additional slots possible
|
||||
foreach (var (slot, character) in _preferencesManager.Preferences!.Characters)
|
||||
{
|
||||
// Corvax-Sponsors-Start
|
||||
isDisplayedMaxSlots = numberOfFullSlots >= _preferencesManager.Settings.MaxCharacterSlots;
|
||||
if (isDisplayedMaxSlots) break;
|
||||
// Corvax-Sponsors-End
|
||||
numberOfFullSlots++;
|
||||
var characterPickerButton = new CharacterPickerButton(_entityManager,
|
||||
_preferencesManager,
|
||||
_prototypeManager,
|
||||
characterButtonsGroup,
|
||||
character);
|
||||
Characters.AddChild(characterPickerButton);
|
||||
|
||||
var characterIndexCopy = slot;
|
||||
characterPickerButton.OnPressed += args =>
|
||||
{
|
||||
_humanoidProfileEditor.Profile = (HumanoidCharacterProfile)character;
|
||||
_humanoidProfileEditor.CharacterSlot = characterIndexCopy;
|
||||
_humanoidProfileEditor.UpdateControls();
|
||||
_preferencesManager.SelectCharacter(character);
|
||||
var controller = UserInterfaceManager.GetUIController<LobbyUIController>();
|
||||
controller.UpdateProfile(_humanoidProfileEditor.Profile);
|
||||
controller.ReloadCharacterUI();
|
||||
UpdateUI();
|
||||
args.Event.Handle();
|
||||
};
|
||||
}
|
||||
|
||||
_createNewCharacterButton.Disabled = isDisplayedMaxSlots; // Corvax-Sponsors
|
||||
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;
|
||||
|
||||
public CharacterPickerButton(
|
||||
IEntityManager entityManager,
|
||||
IClientPreferencesManager preferencesManager,
|
||||
IPrototypeManager prototypeManager,
|
||||
ButtonGroup group,
|
||||
ICharacterProfile profile)
|
||||
{
|
||||
AddStyleClass(StyleClassButton);
|
||||
ToggleMode = true;
|
||||
Group = group;
|
||||
|
||||
var humanoid = profile as HumanoidCharacterProfile;
|
||||
if (humanoid is not null)
|
||||
{
|
||||
var dummy = prototypeManager.Index<SpeciesPrototype>(humanoid.Species).DollPrototype;
|
||||
_previewDummy = entityManager.SpawnEntity(dummy, MapCoordinates.Nullspace);
|
||||
}
|
||||
else
|
||||
{
|
||||
_previewDummy = entityManager.SpawnEntity(prototypeManager.Index<SpeciesPrototype>(SharedHumanoidAppearanceSystem.DefaultSpecies).DollPrototype, MapCoordinates.Nullspace);
|
||||
}
|
||||
|
||||
EntitySystem.Get<HumanoidAppearanceSystem>().LoadProfile(_previewDummy, (HumanoidCharacterProfile)profile);
|
||||
|
||||
if (humanoid != null)
|
||||
{
|
||||
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;
|
||||
|
||||
if (isSelectedCharacter)
|
||||
Pressed = true;
|
||||
|
||||
var view = new SpriteView
|
||||
{
|
||||
Scale = new Vector2(2, 2),
|
||||
OverrideDirection = Direction.South
|
||||
};
|
||||
view.SetEntity(_previewDummy);
|
||||
|
||||
var description = profile.Name;
|
||||
|
||||
var highPriorityJob = humanoid?.JobPriorities.SingleOrDefault(p => p.Value == JobPriority.High).Key;
|
||||
if (highPriorityJob != null)
|
||||
{
|
||||
var jobName = IoCManager.Resolve<IPrototypeManager>().Index<JobPrototype>(highPriorityJob).LocalizedName;
|
||||
description = $"{description}\n{jobName}";
|
||||
}
|
||||
|
||||
var descriptionLabel = new Label
|
||||
{
|
||||
Text = description,
|
||||
ClipText = true,
|
||||
HorizontalExpand = true
|
||||
};
|
||||
var deleteButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("character-setup-gui-character-picker-button-delete-button"),
|
||||
Visible = !isSelectedCharacter,
|
||||
};
|
||||
var confirmDeleteButton = new Button
|
||||
{
|
||||
Text = Loc.GetString("character-setup-gui-character-picker-button-confirm-delete-button"),
|
||||
Visible = false,
|
||||
};
|
||||
confirmDeleteButton.ModulateSelfOverride = StyleNano.ButtonColorCautionDefault;
|
||||
confirmDeleteButton.OnPressed += _ =>
|
||||
{
|
||||
Parent?.RemoveChild(this);
|
||||
Parent?.RemoveChild(confirmDeleteButton);
|
||||
preferencesManager.DeleteCharacter(profile);
|
||||
};
|
||||
deleteButton.OnPressed += _ =>
|
||||
{
|
||||
|
||||
deleteButton.Visible = false;
|
||||
confirmDeleteButton.Visible = true;
|
||||
|
||||
};
|
||||
|
||||
var internalHBox = new BoxContainer
|
||||
{
|
||||
Orientation = LayoutOrientation.Horizontal,
|
||||
HorizontalExpand = true,
|
||||
SeparationOverride = 0,
|
||||
Children =
|
||||
{
|
||||
view,
|
||||
descriptionLabel,
|
||||
deleteButton,
|
||||
confirmDeleteButton
|
||||
}
|
||||
};
|
||||
|
||||
AddChild(internalHBox);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
if (!disposing)
|
||||
return;
|
||||
|
||||
IoCManager.Resolve<IEntityManager>().DeleteEntity(_previewDummy);
|
||||
_previewDummy = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Preferences.UI
|
||||
{
|
||||
public sealed partial class HumanoidProfileEditor
|
||||
{
|
||||
private void RandomizeEverything()
|
||||
{
|
||||
Profile = HumanoidCharacterProfile.Random();
|
||||
UpdateControls();
|
||||
IsDirty = true;
|
||||
}
|
||||
|
||||
private void RandomizeName()
|
||||
{
|
||||
if (Profile == null) return;
|
||||
var name = HumanoidCharacterProfile.GetName(Profile.Species, Profile.Gender);
|
||||
SetName(name);
|
||||
UpdateNameEdit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,9 @@ public sealed class AlignRCDConstruction : PlacementMode
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
|
||||
[Dependency] private readonly RCDSystem _rcdSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
private readonly SharedMapSystem _mapSystem;
|
||||
private readonly RCDSystem _rcdSystem;
|
||||
private readonly SharedTransformSystem _transformSystem;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
|
||||
@@ -32,12 +32,7 @@ public sealed class AlignRCDConstruction : PlacementMode
|
||||
/// </summary>
|
||||
public AlignRCDConstruction(PlacementManager pMan) : base(pMan)
|
||||
{
|
||||
var dependencies = IoCManager.Instance!;
|
||||
_entityManager = dependencies.Resolve<IEntityManager>();
|
||||
_mapManager = dependencies.Resolve<IMapManager>();
|
||||
_playerManager = dependencies.Resolve<IPlayerManager>();
|
||||
_stateManager = dependencies.Resolve<IStateManager>();
|
||||
|
||||
IoCManager.InjectDependencies(this);
|
||||
_mapSystem = _entityManager.System<SharedMapSystem>();
|
||||
_rcdSystem = _entityManager.System<RCDSystem>();
|
||||
_transformSystem = _entityManager.System<SharedTransformSystem>();
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Radiation.Components;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.Graphics;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
@@ -14,6 +16,7 @@ namespace Content.Client.Radiation.Overlays
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
private TransformSystem? _transform;
|
||||
|
||||
private const float MaxDist = 15.0f;
|
||||
|
||||
@@ -72,6 +75,8 @@ namespace Content.Client.Radiation.Overlays
|
||||
//Queries all pulses on the map and either adds or removes them from the list of rendered pulses based on whether they should be drawn (in range? on the same z-level/map? pulse entity still exists?)
|
||||
private void RadiationQuery(IEye? currentEye)
|
||||
{
|
||||
_transform ??= _entityManager.System<TransformSystem>();
|
||||
|
||||
if (currentEye == null)
|
||||
{
|
||||
_pulses.Clear();
|
||||
@@ -91,7 +96,7 @@ namespace Content.Client.Radiation.Overlays
|
||||
(
|
||||
_baseShader.Duplicate(),
|
||||
new RadiationShaderInstance(
|
||||
_entityManager.GetComponent<TransformComponent>(pulseEntity).MapPosition,
|
||||
_transform.GetMapCoordinates(pulseEntity),
|
||||
pulse.VisualRange,
|
||||
pulse.StartTime,
|
||||
pulse.VisualDuration
|
||||
@@ -109,7 +114,7 @@ namespace Content.Client.Radiation.Overlays
|
||||
_entityManager.TryGetComponent(pulseEntity, out RadiationPulseComponent? pulse))
|
||||
{
|
||||
var shaderInstance = _pulses[pulseEntity];
|
||||
shaderInstance.instance.CurrentMapCoords = _entityManager.GetComponent<TransformComponent>(pulseEntity).MapPosition;
|
||||
shaderInstance.instance.CurrentMapCoords = _transform.GetMapCoordinates(pulseEntity);
|
||||
shaderInstance.instance.Range = pulse.VisualRange;
|
||||
} else {
|
||||
_pulses[pulseEntity].shd.Dispose();
|
||||
|
||||
@@ -17,7 +17,6 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPrototypeManager _proto = default!;
|
||||
private readonly LockSystem _lock;
|
||||
private readonly SpriteSystem _sprite;
|
||||
|
||||
|
||||
@@ -20,10 +20,14 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
|
||||
protected override void Open()
|
||||
{
|
||||
base.Open();
|
||||
_window = new OfferingWindow();
|
||||
_window.Title = Loc.GetString("salvage-magnet-window-title");
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCenteredLeft();
|
||||
|
||||
if (_window is null)
|
||||
{
|
||||
_window = new OfferingWindow();
|
||||
_window.Title = Loc.GetString("salvage-magnet-window-title");
|
||||
_window.OnClose += Close;
|
||||
_window.OpenCenteredLeft();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
@@ -108,4 +112,15 @@ public sealed class SalvageMagnetBoundUserInterface : BoundUserInterface
|
||||
_window.AddOption(option);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
if (disposing)
|
||||
{
|
||||
_window?.Close();
|
||||
_window?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using Content.Shared.Shuttles.BUIStates;
|
||||
using Content.Shared.Shuttles.Systems;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
@@ -12,7 +13,10 @@ namespace Content.Client.Shuttles.UI;
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class DockObject : PanelContainer
|
||||
{
|
||||
[PublicAPI]
|
||||
public event Action? UndockPressed;
|
||||
|
||||
[PublicAPI]
|
||||
public event Action? ViewPressed;
|
||||
|
||||
public BoxContainer ContentsContainer => Contents;
|
||||
|
||||
@@ -25,6 +25,7 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
public readonly EntityUid Entity;
|
||||
public float AccumulatedTime;
|
||||
private string _lastValidName;
|
||||
private List<EntityUid> _modules = new();
|
||||
|
||||
public BorgMenu(EntityUid entity)
|
||||
{
|
||||
@@ -114,7 +115,23 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
("actual", _chassis.ModuleCount),
|
||||
("max", _chassis.MaxModules));
|
||||
|
||||
if (_chassis.ModuleContainer.Count == _modules.Count)
|
||||
{
|
||||
var isSame = true;
|
||||
foreach (var module in _chassis.ModuleContainer.ContainedEntities)
|
||||
{
|
||||
if (_modules.Contains(module))
|
||||
continue;
|
||||
isSame = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isSame)
|
||||
return;
|
||||
}
|
||||
|
||||
ModuleContainer.Children.Clear();
|
||||
_modules.Clear();
|
||||
foreach (var module in _chassis.ModuleContainer.ContainedEntities)
|
||||
{
|
||||
var control = new BorgModuleControl(module, _entity);
|
||||
@@ -123,6 +140,7 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
RemoveModuleButtonPressed?.Invoke(module);
|
||||
};
|
||||
ModuleContainer.AddChild(control);
|
||||
_modules.Add(module);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,4 +180,3 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
NameChanged?.Invoke(_lastValidName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Silicons.Laws;
|
||||
using Content.Shared.Silicons.Laws.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Silicons.Laws.Ui;
|
||||
|
||||
@@ -10,6 +11,7 @@ public sealed class SiliconLawBoundUserInterface : BoundUserInterface
|
||||
[ViewVariables]
|
||||
private SiliconLawMenu? _menu;
|
||||
private EntityUid _owner;
|
||||
private List<SiliconLaw>? _laws;
|
||||
|
||||
public SiliconLawBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||
{
|
||||
@@ -41,6 +43,23 @@ public sealed class SiliconLawBoundUserInterface : BoundUserInterface
|
||||
if (state is not SiliconLawBuiState msg)
|
||||
return;
|
||||
|
||||
if (_laws != null && _laws.Count == msg.Laws.Count)
|
||||
{
|
||||
var isSame = true;
|
||||
foreach (var law in msg.Laws)
|
||||
{
|
||||
if (_laws.Contains(law))
|
||||
continue;
|
||||
isSame = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (isSame)
|
||||
return;
|
||||
}
|
||||
|
||||
_laws = msg.Laws.ToList();
|
||||
|
||||
_menu?.Update(_owner, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Shared.Sprite;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.State;
|
||||
using Robust.Shared.Physics;
|
||||
|
||||
namespace Content.Client.Sprite;
|
||||
|
||||
@@ -15,6 +16,7 @@ public sealed class SpriteFadeSystem : EntitySystem
|
||||
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private readonly HashSet<FadingSpriteComponent> _comps = new();
|
||||
|
||||
@@ -48,7 +50,7 @@ public sealed class SpriteFadeSystem : EntitySystem
|
||||
spriteQuery.TryGetComponent(player, out var playerSprite))
|
||||
{
|
||||
var fadeQuery = GetEntityQuery<SpriteFadeComponent>();
|
||||
var mapPos = playerXform.MapPosition;
|
||||
var mapPos = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: playerXform);
|
||||
|
||||
// Also want to handle large entities even if they may not be clickable.
|
||||
foreach (var ent in state.GetClickableEntities(mapPos))
|
||||
|
||||
13
Content.Client/StationRecords/GeneralRecord.xaml
Normal file
13
Content.Client/StationRecords/GeneralRecord.xaml
Normal file
@@ -0,0 +1,13 @@
|
||||
<Control xmlns="https://spacestation14.io">
|
||||
<BoxContainer Orientation="Vertical" Margin="5">
|
||||
<Label Name="RecordName" StyleClasses="LabelBig"/>
|
||||
<Label Name="Age"/>
|
||||
<Label Name="Title"/>
|
||||
<Label Name="Job"/>
|
||||
<Label Name="Species"/>
|
||||
<Label Name="Gender"/>
|
||||
<Label Name="Fingerprint"/>
|
||||
<Label Name="Dna"/>
|
||||
<Button Visible="False" Name="DeleteButton" Text="{Loc 'general-station-record-console-delete'}"/>
|
||||
</BoxContainer>
|
||||
</Control>
|
||||
33
Content.Client/StationRecords/GeneralRecord.xaml.cs
Normal file
33
Content.Client/StationRecords/GeneralRecord.xaml.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
namespace Content.Client.StationRecords;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class GeneralRecord : Control
|
||||
{
|
||||
public Action<uint>? OnDeletePressed;
|
||||
public GeneralRecord(GeneralStationRecord record, bool canDelete, uint? id)
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
RecordName.Text = record.Name;
|
||||
Age.Text = Loc.GetString("general-station-record-console-record-age", ("age", record.Age.ToString()));
|
||||
Title.Text = Loc.GetString("general-station-record-console-record-title",
|
||||
("job", Loc.GetString(record.JobTitle)));
|
||||
Species.Text = Loc.GetString("general-station-record-console-record-species", ("species", record.Species));
|
||||
Gender.Text = Loc.GetString("general-station-record-console-record-gender",
|
||||
("gender", record.Gender.ToString()));
|
||||
Fingerprint.Text = Loc.GetString("general-station-record-console-record-fingerprint",
|
||||
("fingerprint", record.Fingerprint ?? Loc.GetString("generic-not-available-shorthand")));
|
||||
Dna.Text = Loc.GetString("general-station-record-console-record-dna",
|
||||
("dna", record.DNA ?? Loc.GetString("generic-not-available-shorthand")));
|
||||
|
||||
if (canDelete && id != null )
|
||||
{
|
||||
DeleteButton.Visible = true;
|
||||
DeleteButton.OnPressed += _ => OnDeletePressed?.Invoke(id.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ public sealed class GeneralStationRecordConsoleBoundUserInterface : BoundUserInt
|
||||
SendMessage(new SelectStationRecord(key));
|
||||
_window.OnFiltersChanged += (type, filterValue) =>
|
||||
SendMessage(new SetStationRecordFilter(type, filterValue));
|
||||
_window.OnDeleted += id => SendMessage(new DeleteStationRecord(id));
|
||||
_window.OnClose += Close;
|
||||
|
||||
_window.OpenCentered();
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</BoxContainer>
|
||||
<BoxContainer Orientation="Vertical" Margin="5">
|
||||
<Label Name="RecordContainerStatus" Visible="False" Text="{Loc 'general-station-record-console-select-record-info'}"/>
|
||||
<BoxContainer Name="RecordContainer" Orientation="Vertical" />
|
||||
<Control Name="RecordContainer" Visible="False"/>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
</BoxContainer>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Content.Shared.StationRecords;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
|
||||
@@ -13,6 +11,7 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||
public Action<uint?>? OnKeySelected;
|
||||
|
||||
public Action<StationRecordFilterType, string>? OnFiltersChanged;
|
||||
public Action<uint>? OnDeleted;
|
||||
|
||||
private bool _isPopulating;
|
||||
|
||||
@@ -112,11 +111,10 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||
RecordContainerStatus.Text = state.SelectedKey == null
|
||||
? Loc.GetString("general-station-record-console-no-record-found")
|
||||
: Loc.GetString("general-station-record-console-select-record-info");
|
||||
PopulateRecordContainer(state.Record);
|
||||
PopulateRecordContainer(state.Record, state.CanDeleteEntries, state.SelectedKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
RecordContainer.DisposeAllChildren();
|
||||
RecordContainer.RemoveAllChildren();
|
||||
}
|
||||
}
|
||||
@@ -138,49 +136,13 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||
RecordListing.SortItemsByText();
|
||||
}
|
||||
|
||||
private void PopulateRecordContainer(GeneralStationRecord record)
|
||||
private void PopulateRecordContainer(GeneralStationRecord record, bool enableDelete, uint? id)
|
||||
{
|
||||
RecordContainer.DisposeAllChildren();
|
||||
RecordContainer.RemoveAllChildren();
|
||||
// sure
|
||||
var recordControls = new Control[]
|
||||
{
|
||||
new Label()
|
||||
{
|
||||
Text = record.Name,
|
||||
StyleClasses = { "LabelBig" }
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-age", ("age", record.Age.ToString()))
|
||||
var newRecord = new GeneralRecord(record, enableDelete, id);
|
||||
newRecord.OnDeletePressed = OnDeleted;
|
||||
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-title", ("job", Loc.GetString(record.JobTitle)))
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-species", ("species", record.Species))
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-gender", ("gender", record.Gender.ToString()))
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-fingerprint", ("fingerprint", record.Fingerprint ?? Loc.GetString("generic-not-available-shorthand")))
|
||||
},
|
||||
new Label()
|
||||
{
|
||||
Text = Loc.GetString("general-station-record-console-record-dna", ("dna", record.DNA ?? Loc.GetString("generic-not-available-shorthand")))
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var control in recordControls)
|
||||
{
|
||||
RecordContainer.AddChild(control);
|
||||
}
|
||||
RecordContainer.AddChild(newRecord);
|
||||
}
|
||||
|
||||
private void FilterListingOfRecords(string text = "")
|
||||
@@ -195,4 +157,5 @@ public sealed partial class GeneralStationRecordConsoleWindow : DefaultWindow
|
||||
{
|
||||
return Loc.GetString($"general-station-record-{type.ToString().ToLower()}-filter");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Resources;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
|
||||
@@ -1389,6 +1389,17 @@ namespace Content.Client.Stylesheets
|
||||
Element<PanelContainer>().Class("WindowHeadingBackgroundLight")
|
||||
.Prop("panel", new StyleBoxTexture(BaseButtonOpenLeft) { Padding = default }),
|
||||
|
||||
// Window Header Help Button
|
||||
Element<TextureButton>().Class(FancyWindow.StyleClassWindowHelpButton)
|
||||
.Prop(TextureButton.StylePropertyTexture, resCache.GetTexture("/Textures/Interface/Nano/help.png"))
|
||||
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#4B596A")),
|
||||
|
||||
Element<TextureButton>().Class(FancyWindow.StyleClassWindowHelpButton).Pseudo(ContainerButton.StylePseudoClassHover)
|
||||
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#7F3636")),
|
||||
|
||||
Element<TextureButton>().Class(FancyWindow.StyleClassWindowHelpButton).Pseudo(ContainerButton.StylePseudoClassPressed)
|
||||
.Prop(Control.StylePropertyModulateSelf, Color.FromHex("#753131")),
|
||||
|
||||
//The lengths you have to go through to change a background color smh
|
||||
Element<PanelContainer>().Class("PanelBackgroundBaseDark")
|
||||
.Prop("panel", new StyleBoxTexture(BaseButtonOpenBoth) { Padding = default })
|
||||
|
||||
@@ -258,7 +258,7 @@ namespace Content.Client.Tabletop
|
||||
// Set the dragging player on the component to noone
|
||||
if (broadcast && _draggedEntity != null && EntityManager.HasComponent<TabletopDraggableComponent>(_draggedEntity.Value))
|
||||
{
|
||||
RaisePredictiveEvent(new TabletopMoveEvent(GetNetEntity(_draggedEntity.Value), Transform(_draggedEntity.Value).MapPosition, GetNetEntity(_table!.Value)));
|
||||
RaisePredictiveEvent(new TabletopMoveEvent(GetNetEntity(_draggedEntity.Value), Transforms.GetMapCoordinates(_draggedEntity.Value), GetNetEntity(_table!.Value)));
|
||||
RaisePredictiveEvent(new TabletopDraggingPlayerChangedEvent(GetNetEntity(_draggedEntity.Value), false));
|
||||
}
|
||||
|
||||
@@ -277,7 +277,8 @@ namespace Content.Client.Tabletop
|
||||
if (coordinates == MapCoordinates.Nullspace) return MapCoordinates.Nullspace;
|
||||
|
||||
var eye = viewport.Eye;
|
||||
if (eye == null) return MapCoordinates.Nullspace;
|
||||
if (eye == null)
|
||||
return MapCoordinates.Nullspace;
|
||||
|
||||
var size = (Vector2) viewport.ViewportSize / EyeManager.PixelsPerMeter; // Convert to tiles instead of pixels
|
||||
var eyePosition = eye.Position.Position;
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<BoxContainer Margin="4 2 8 0" Orientation="Horizontal">
|
||||
<Label Name="WindowTitle"
|
||||
HorizontalExpand="True" VAlign="Center" StyleClasses="FancyWindowTitle" />
|
||||
<TextureButton Name="HelpButton" StyleClasses="windowHelpButton" VerticalAlignment="Center" Disabled="True" Visible="False" Access="Public" />
|
||||
<TextureButton Name="CloseButton" StyleClasses="windowCloseButton"
|
||||
VerticalAlignment="Center" />
|
||||
</BoxContainer>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Guidebook;
|
||||
using Content.Client.Guidebook.Components;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface.CustomControls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.UserInterface.Controls
|
||||
{
|
||||
@@ -9,13 +12,17 @@ namespace Content.Client.UserInterface.Controls
|
||||
[Virtual]
|
||||
public partial class FancyWindow : BaseWindow
|
||||
{
|
||||
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
|
||||
private GuidebookSystem? _guidebookSystem;
|
||||
private const int DRAG_MARGIN_SIZE = 7;
|
||||
public const string StyleClassWindowHelpButton = "windowHelpButton";
|
||||
|
||||
public FancyWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
|
||||
CloseButton.OnPressed += _ => Close();
|
||||
HelpButton.OnPressed += _ => Help();
|
||||
XamlChildren = ContentsContainer.Children;
|
||||
}
|
||||
|
||||
@@ -25,6 +32,26 @@ namespace Content.Client.UserInterface.Controls
|
||||
set => WindowTitle.Text = value;
|
||||
}
|
||||
|
||||
private List<string>? _helpGuidebookIds;
|
||||
public List<string>? HelpGuidebookIds
|
||||
{
|
||||
get => _helpGuidebookIds;
|
||||
set
|
||||
{
|
||||
_helpGuidebookIds = value;
|
||||
HelpButton.Disabled = _helpGuidebookIds == null;
|
||||
HelpButton.Visible = !HelpButton.Disabled;
|
||||
}
|
||||
}
|
||||
|
||||
public void Help()
|
||||
{
|
||||
if (HelpGuidebookIds is null)
|
||||
return;
|
||||
_guidebookSystem ??= _sysMan.GetEntitySystem<GuidebookSystem>();
|
||||
_guidebookSystem.OpenHelp(HelpGuidebookIds);
|
||||
}
|
||||
|
||||
protected override DragMode GetDragModeFor(Vector2 relativeMousePos)
|
||||
{
|
||||
var mode = DragMode.Move;
|
||||
|
||||
@@ -217,6 +217,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
{
|
||||
action.Event.Target = coords;
|
||||
action.Event.Performer = user;
|
||||
action.Event.Action = actionId;
|
||||
}
|
||||
|
||||
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
|
||||
@@ -251,6 +252,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
|
||||
{
|
||||
action.Event.Target = entity;
|
||||
action.Event.Performer = user;
|
||||
action.Event.Action = actionId;
|
||||
}
|
||||
|
||||
_actionsSystem.PerformAction(user, actionComp, actionId, action, action.Event, _timing.CurTime);
|
||||
|
||||
@@ -285,10 +285,19 @@ public sealed class ActionButton : Control, IEntityControl
|
||||
|
||||
_controller ??= UserInterfaceManager.GetUIController<ActionUIController>();
|
||||
_spriteSys ??= _entities.System<SpriteSystem>();
|
||||
if ((_controller.SelectingTargetFor == ActionId || _action.Toggled) && _action.IconOn != null)
|
||||
SetActionIcon(_spriteSys.Frame0(_action.IconOn));
|
||||
if ((_controller.SelectingTargetFor == ActionId || _action.Toggled))
|
||||
{
|
||||
if (_action.IconOn != null)
|
||||
SetActionIcon(_spriteSys.Frame0(_action.IconOn));
|
||||
|
||||
if (_action.BackgroundOn != null)
|
||||
_buttonBackgroundTexture = _spriteSys.Frame0(_action.BackgroundOn);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetActionIcon(_action.Icon != null ? _spriteSys.Frame0(_action.Icon) : null);
|
||||
_buttonBackgroundTexture = Theme.ResolveTexture("SlotBackground");
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateBackground()
|
||||
|
||||
@@ -60,6 +60,7 @@ public sealed class ChatUIController : UIController
|
||||
[UISystemDependency] private readonly GhostSystem? _ghost = default;
|
||||
[UISystemDependency] private readonly TypingIndicatorSystem? _typingIndicator = default;
|
||||
[UISystemDependency] private readonly ChatSystem? _chatSys = default;
|
||||
[UISystemDependency] private readonly TransformSystem? _transform = default;
|
||||
|
||||
[ValidatePrototypeId<ColorPalettePrototype>]
|
||||
private const string ChatNamePalette = "ChatNames";
|
||||
@@ -625,7 +626,7 @@ public sealed class ChatUIController : UIController
|
||||
var predicate = static (EntityUid uid, (EntityUid compOwner, EntityUid? attachedEntity) data)
|
||||
=> uid == data.compOwner || uid == data.attachedEntity;
|
||||
var playerPos = player != null
|
||||
? EntityManager.GetComponent<TransformComponent>(player.Value).MapPosition
|
||||
? _transform?.GetMapCoordinates(player.Value) ?? MapCoordinates.Nullspace
|
||||
: MapCoordinates.Nullspace;
|
||||
|
||||
var occluded = player != null && _examine.IsOccluded(player.Value);
|
||||
@@ -644,7 +645,7 @@ public sealed class ChatUIController : UIController
|
||||
continue;
|
||||
}
|
||||
|
||||
var otherPos = EntityManager.GetComponent<TransformComponent>(ent).MapPosition;
|
||||
var otherPos = _transform?.GetMapCoordinates(ent) ?? MapCoordinates.Nullspace;
|
||||
|
||||
if (occluded && !_examine.InRangeUnOccluded(
|
||||
playerPos,
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace Content.Client.Verbs
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly ExamineSystem _examine = default!;
|
||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly IStateManager _stateManager = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
@@ -141,7 +142,7 @@ namespace Content.Client.Verbs
|
||||
if ((visibility & MenuVisibility.NoFov) == 0)
|
||||
{
|
||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||
var playerPos = xformQuery.GetComponent(player.Value).MapPosition;
|
||||
var playerPos = _transform.GetMapCoordinates(player.Value, xform: xformQuery.GetComponent(player.Value));
|
||||
|
||||
for (var i = entities.Count - 1; i >= 0; i--)
|
||||
{
|
||||
@@ -149,7 +150,7 @@ namespace Content.Client.Verbs
|
||||
|
||||
if (!_examine.InRangeUnOccluded(
|
||||
playerPos,
|
||||
xformQuery.GetComponent(entity).MapPosition,
|
||||
_transform.GetMapCoordinates(entity, xform: xformQuery.GetComponent(entity)),
|
||||
ExamineSystemShared.ExamineRange,
|
||||
null))
|
||||
{
|
||||
|
||||
@@ -6,12 +6,15 @@ using Robust.Client;
|
||||
using Robust.Client.Audio;
|
||||
using Robust.Client.Console;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Sources;
|
||||
using Robust.Shared.ContentPack;
|
||||
|
||||
|
||||
namespace Content.Client.Voting
|
||||
@@ -31,16 +34,20 @@ namespace Content.Client.Voting
|
||||
|
||||
public sealed class VoteManager : IVoteManager
|
||||
{
|
||||
[Dependency] private readonly IAudioManager _audio = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
[Dependency] private readonly IClientNetManager _netManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IClientConsoleHost _console = default!;
|
||||
[Dependency] private readonly IBaseClient _client = default!;
|
||||
[Dependency] private readonly IResourceCache _res = default!;
|
||||
|
||||
private readonly Dictionary<StandardVoteType, TimeSpan> _standardVoteTimeouts = new();
|
||||
private readonly Dictionary<int, ActiveVote> _votes = new();
|
||||
private readonly Dictionary<int, UI.VotePopup> _votePopups = new();
|
||||
private Control? _popupContainer;
|
||||
|
||||
private IAudioSource? _voteSource;
|
||||
|
||||
public bool CanCallVote { get; private set; }
|
||||
|
||||
public event Action<bool>? CanCallVoteChanged;
|
||||
@@ -49,6 +56,14 @@ namespace Content.Client.Voting
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
const string sound = "/Audio/Effects/voteding.ogg";
|
||||
_voteSource = _audio.CreateAudioSource(_res.GetResource<AudioResource>(sound));
|
||||
|
||||
if (_voteSource != null)
|
||||
{
|
||||
_voteSource.Global = true;
|
||||
}
|
||||
|
||||
_netManager.RegisterNetMessage<MsgVoteData>(ReceiveVoteData);
|
||||
_netManager.RegisterNetMessage<MsgVoteCanCall>(ReceiveVoteCanCall);
|
||||
|
||||
@@ -125,9 +140,8 @@ namespace Content.Client.Voting
|
||||
return;
|
||||
}
|
||||
|
||||
_voteSource?.Restart();
|
||||
@new = true;
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<AudioSystem>()
|
||||
.PlayGlobal("/Audio/Effects/voteding.ogg", Filter.Local(), false);
|
||||
|
||||
// Refresh
|
||||
var container = _popupContainer;
|
||||
|
||||
@@ -20,10 +20,11 @@ public sealed class MeleeArcOverlay : Overlay
|
||||
private readonly IPlayerManager _playerManager;
|
||||
private readonly MeleeWeaponSystem _melee;
|
||||
private readonly SharedCombatModeSystem _combatMode;
|
||||
private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
|
||||
|
||||
public MeleeArcOverlay(IEntityManager entManager, IEyeManager eyeManager, IInputManager inputManager, IPlayerManager playerManager, MeleeWeaponSystem melee, SharedCombatModeSystem combatMode)
|
||||
public MeleeArcOverlay(IEntityManager entManager, IEyeManager eyeManager, IInputManager inputManager, IPlayerManager playerManager, MeleeWeaponSystem melee, SharedCombatModeSystem combatMode, SharedTransformSystem transform)
|
||||
{
|
||||
_entManager = entManager;
|
||||
_eyeManager = eyeManager;
|
||||
@@ -31,6 +32,7 @@ public sealed class MeleeArcOverlay : Overlay
|
||||
_playerManager = playerManager;
|
||||
_melee = melee;
|
||||
_combatMode = combatMode;
|
||||
_transform = transform;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
@@ -52,7 +54,7 @@ public sealed class MeleeArcOverlay : Overlay
|
||||
if (mapPos.MapId != args.MapId)
|
||||
return;
|
||||
|
||||
var playerPos = xform.MapPosition;
|
||||
var playerPos = _transform.GetMapCoordinates(player.Value, xform: xform);
|
||||
|
||||
if (mapPos.MapId != playerPos.MapId)
|
||||
return;
|
||||
|
||||
@@ -35,6 +35,7 @@ public sealed class MeleeSpreadCommand : IConsoleCommand
|
||||
collection.Resolve<IInputManager>(),
|
||||
collection.Resolve<IPlayerManager>(),
|
||||
sysManager.GetEntitySystem<MeleeWeaponSystem>(),
|
||||
sysManager.GetEntitySystem<SharedCombatModeSystem>()));
|
||||
sysManager.GetEntitySystem<SharedCombatModeSystem>(),
|
||||
sysManager.GetEntitySystem<SharedTransformSystem>()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@ using Robust.Client.State;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Weapons.Melee;
|
||||
|
||||
@@ -66,7 +64,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
if (!TryGetWeapon(entity, out var weaponUid, out var weapon))
|
||||
return;
|
||||
|
||||
if (!CombatMode.IsInCombatMode(entity) || !Blocker.CanAttack(entity))
|
||||
if (!CombatMode.IsInCombatMode(entity) || !Blocker.CanAttack(entity, weapon: (weaponUid, weapon)))
|
||||
{
|
||||
weapon.Attacking = false;
|
||||
return;
|
||||
@@ -142,7 +140,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
|
||||
// Light attack
|
||||
if (useDown == BoundKeyState.Down)
|
||||
{
|
||||
var attackerPos = Transform(entity).MapPosition;
|
||||
var attackerPos = TransformSystem.GetMapCoordinates(entity);
|
||||
|
||||
if (mousePos.MapId != attackerPos.MapId ||
|
||||
(attackerPos.Position - mousePos.Position).Length() > weapon.Range)
|
||||
|
||||
@@ -18,8 +18,9 @@ public sealed class GunSpreadOverlay : Overlay
|
||||
private readonly IInputManager _input;
|
||||
private readonly IPlayerManager _player;
|
||||
private readonly GunSystem _guns;
|
||||
private readonly SharedTransformSystem _transform;
|
||||
|
||||
public GunSpreadOverlay(IEntityManager entManager, IEyeManager eyeManager, IGameTiming timing, IInputManager input, IPlayerManager player, GunSystem system)
|
||||
public GunSpreadOverlay(IEntityManager entManager, IEyeManager eyeManager, IGameTiming timing, IInputManager input, IPlayerManager player, GunSystem system, SharedTransformSystem transform)
|
||||
{
|
||||
_entManager = entManager;
|
||||
_eye = eyeManager;
|
||||
@@ -27,6 +28,7 @@ public sealed class GunSpreadOverlay : Overlay
|
||||
_timing = timing;
|
||||
_player = player;
|
||||
_guns = system;
|
||||
_transform = transform;
|
||||
}
|
||||
|
||||
protected override void Draw(in OverlayDrawArgs args)
|
||||
@@ -41,7 +43,7 @@ public sealed class GunSpreadOverlay : Overlay
|
||||
return;
|
||||
}
|
||||
|
||||
var mapPos = xform.MapPosition;
|
||||
var mapPos = _transform.GetMapCoordinates(player.Value, xform: xform);
|
||||
|
||||
if (mapPos.MapId == MapId.Nullspace)
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Animations;
|
||||
using Content.Client.Gameplay;
|
||||
using Content.Client.Items;
|
||||
using Content.Client.Weapons.Ranged.Components;
|
||||
using Content.Shared.Camera;
|
||||
@@ -13,6 +14,7 @@ using Robust.Client.GameObjects;
|
||||
using Robust.Client.Graphics;
|
||||
using Robust.Client.Input;
|
||||
using Robust.Client.Player;
|
||||
using Robust.Client.State;
|
||||
using Robust.Shared.Animations;
|
||||
using Robust.Shared.Input;
|
||||
using Robust.Shared.Map;
|
||||
@@ -30,6 +32,7 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
[Dependency] private readonly IEyeManager _eyeManager = default!;
|
||||
[Dependency] private readonly IInputManager _inputManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
[Dependency] private readonly IStateManager _state = default!;
|
||||
[Dependency] private readonly AnimationPlayerSystem _animPlayer = default!;
|
||||
[Dependency] private readonly InputSystem _inputSystem = default!;
|
||||
[Dependency] private readonly SharedCameraRecoilSystem _recoil = default!;
|
||||
@@ -57,7 +60,8 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
Timing,
|
||||
_inputManager,
|
||||
_player,
|
||||
this));
|
||||
this,
|
||||
TransformSystem));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -174,10 +178,15 @@ public sealed partial class GunSystem : SharedGunSystem
|
||||
// Define target coordinates relative to gun entity, so that network latency on moving grids doesn't fuck up the target location.
|
||||
var coordinates = EntityCoordinates.FromMap(entity, mousePos, TransformSystem, EntityManager);
|
||||
|
||||
NetEntity? target = null;
|
||||
if (_state.CurrentState is GameplayStateBase screen)
|
||||
target = GetNetEntity(screen.GetClickedEntity(mousePos));
|
||||
|
||||
Log.Debug($"Sending shoot request tick {Timing.CurTick} / {Timing.CurTime}");
|
||||
|
||||
EntityManager.RaisePredictiveEvent(new RequestShootEvent
|
||||
{
|
||||
Target = target,
|
||||
Coordinates = GetNetCoordinates(coordinates),
|
||||
Gun = GetNetEntity(gunUid),
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Server.GameTicking.Commands;
|
||||
using Content.Shared.CCVar;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Commands
|
||||
@@ -40,8 +41,7 @@ namespace Content.IntegrationTests.Tests.Commands
|
||||
|
||||
tickBeforeRestart = entityManager.CurrentTick;
|
||||
|
||||
var command = new RestartRoundNowCommand();
|
||||
command.Execute(null, string.Empty, Array.Empty<string>());
|
||||
gameTicker.RestartRound();
|
||||
|
||||
if (lobbyEnabled)
|
||||
{
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Content.IntegrationTests.Tests.Doors
|
||||
// Sloth: Okay I'm sorry but I hate having to rewrite tests for every refactor
|
||||
// If you see this yell at me in discord so I can continue to pretend this didn't happen.
|
||||
// REMINDER THAT I STILL HAVE TO FIX THIS TEST EVERY OTHER PHYSICS PR
|
||||
// Assert.That(AirlockPhysicsDummy.Transform.MapPosition.X, Is.GreaterThan(AirlockPhysicsDummyStartingX));
|
||||
// _transform.GetMapCoordinates(UID HERE, xform: Assert.That(AirlockPhysicsDummy.Transform).X, Is.GreaterThan(AirlockPhysicsDummyStartingX));
|
||||
|
||||
// Blocked by the airlock
|
||||
await server.WaitAssertion(() =>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
@@ -24,6 +25,7 @@ public sealed class HandTests
|
||||
var playerMan = server.ResolveDependency<IPlayerManager>();
|
||||
var mapMan = server.ResolveDependency<IMapManager>();
|
||||
var sys = entMan.System<SharedHandsSystem>();
|
||||
var tSys = entMan.System<TransformSystem>();
|
||||
|
||||
var data = await pair.CreateTestMap();
|
||||
await pair.RunTicksSync(5);
|
||||
@@ -35,7 +37,7 @@ public sealed class HandTests
|
||||
{
|
||||
player = playerMan.Sessions.First().AttachedEntity!.Value;
|
||||
var xform = entMan.GetComponent<TransformComponent>(player);
|
||||
item = entMan.SpawnEntity("Crowbar", xform.MapPosition);
|
||||
item = entMan.SpawnEntity("Crowbar", tSys.GetMapCoordinates(player, xform: xform));
|
||||
hands = entMan.GetComponent<HandsComponent>(player);
|
||||
sys.TryPickup(player, item, hands.ActiveHand!);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
@@ -32,6 +33,7 @@ namespace Content.IntegrationTests.Tests.Interaction
|
||||
var sEntities = server.ResolveDependency<IEntityManager>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var conSystem = sEntities.EntitySysManager.GetEntitySystem<SharedContainerSystem>();
|
||||
var tSystem = sEntities.EntitySysManager.GetEntitySystem<TransformSystem>();
|
||||
|
||||
EntityUid origin = default;
|
||||
EntityUid other = default;
|
||||
@@ -45,7 +47,7 @@ namespace Content.IntegrationTests.Tests.Interaction
|
||||
origin = sEntities.SpawnEntity(HumanId, coordinates);
|
||||
other = sEntities.SpawnEntity(HumanId, coordinates);
|
||||
conSystem.EnsureContainer<Container>(other, "InRangeUnobstructedTestOtherContainer");
|
||||
mapCoordinates = sEntities.GetComponent<TransformComponent>(other).MapPosition;
|
||||
mapCoordinates = tSystem.GetMapCoordinates(other);
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Client.Lobby;
|
||||
using Content.Client.Preferences;
|
||||
using Content.Server.Preferences.Managers;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Client.State;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Server.Ghost.Roles;
|
||||
using Content.Server.Ghost.Roles.Components;
|
||||
using Content.Server.Players;
|
||||
@@ -26,7 +27,7 @@ public sealed class GhostRoleTests
|
||||
";
|
||||
|
||||
/// <summary>
|
||||
/// This is a simple test that just checks if a player can take a ghost roll and then regain control of their
|
||||
/// This is a simple test that just checks if a player can take a ghost role and then regain control of their
|
||||
/// original entity without encountering errors.
|
||||
/// </summary>
|
||||
[Test]
|
||||
@@ -34,12 +35,15 @@ public sealed class GhostRoleTests
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings
|
||||
{
|
||||
Dirty = true,
|
||||
DummyTicker = false,
|
||||
Connected = true
|
||||
});
|
||||
var server = pair.Server;
|
||||
var client = pair.Client;
|
||||
|
||||
var mapData = await pair.CreateTestMap();
|
||||
|
||||
var entMan = server.ResolveDependency<IEntityManager>();
|
||||
var sPlayerMan = server.ResolveDependency<Robust.Server.Player.IPlayerManager>();
|
||||
var conHost = client.ResolveDependency<IConsoleHost>();
|
||||
@@ -51,7 +55,7 @@ public sealed class GhostRoleTests
|
||||
EntityUid originalMob = default;
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
originalMob = entMan.SpawnEntity(null, MapCoordinates.Nullspace);
|
||||
originalMob = entMan.SpawnEntity(null, mapData.GridCoords);
|
||||
mindSystem.TransferTo(originalMindId, originalMob, true);
|
||||
});
|
||||
|
||||
@@ -69,12 +73,12 @@ public sealed class GhostRoleTests
|
||||
Assert.That(entMan.HasComponent<GhostComponent>(ghost));
|
||||
Assert.That(ghost, Is.Not.EqualTo(originalMob));
|
||||
Assert.That(session.ContentData()?.Mind, Is.EqualTo(originalMindId));
|
||||
Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob));
|
||||
Assert.That(originalMind.OwnedEntity, Is.EqualTo(originalMob), $"Original mob: {originalMob}, Ghost: {ghost}");
|
||||
Assert.That(originalMind.VisitingEntity, Is.EqualTo(ghost));
|
||||
|
||||
// Spawn ghost takeover entity.
|
||||
EntityUid ghostRole = default;
|
||||
await server.WaitPost(() => ghostRole = entMan.SpawnEntity("GhostRoleTestEntity", MapCoordinates.Nullspace));
|
||||
await server.WaitPost(() => ghostRole = entMan.SpawnEntity("GhostRoleTestEntity", mapData.GridCoords));
|
||||
|
||||
// Take the ghost role
|
||||
await server.WaitPost(() =>
|
||||
|
||||
159
Content.IntegrationTests/Tests/Minds/GhostTests.cs
Normal file
159
Content.IntegrationTests/Tests/Minds/GhostTests.cs
Normal file
@@ -0,0 +1,159 @@
|
||||
using System.Numerics;
|
||||
using Content.IntegrationTests.Pair;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Players;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.UnitTesting;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Minds;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class GhostTests
|
||||
{
|
||||
struct GhostTestData
|
||||
{
|
||||
public IEntityManager SEntMan;
|
||||
public Robust.Server.Player.IPlayerManager SPlayerMan;
|
||||
public Server.Mind.MindSystem SMindSys;
|
||||
public SharedTransformSystem STransformSys = default!;
|
||||
|
||||
public TestPair Pair = default!;
|
||||
|
||||
public TestMapData MapData => Pair.TestMap!;
|
||||
|
||||
public RobustIntegrationTest.ServerIntegrationInstance Server => Pair.Server;
|
||||
public RobustIntegrationTest.ClientIntegrationInstance Client => Pair.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Initial player coordinates. Note that this does not necessarily correspond to the position of the
|
||||
/// <see cref="Player"/> entity.
|
||||
/// </summary>
|
||||
public NetCoordinates PlayerCoords = default!;
|
||||
|
||||
public NetEntity Player = default!;
|
||||
public EntityUid SPlayerEnt = default!;
|
||||
|
||||
public ICommonSession ClientSession = default!;
|
||||
public ICommonSession ServerSession = default!;
|
||||
|
||||
public GhostTestData()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<GhostTestData> SetupData()
|
||||
{
|
||||
var data = new GhostTestData();
|
||||
|
||||
// Client is needed to create a session for the ghost system. Creating a dummy session was too difficult.
|
||||
data.Pair = await PoolManager.GetServerClient(new PoolSettings
|
||||
{
|
||||
DummyTicker = false,
|
||||
Connected = true,
|
||||
Dirty = true
|
||||
});
|
||||
|
||||
data.SEntMan = data.Pair.Server.ResolveDependency<IServerEntityManager>();
|
||||
data.SPlayerMan = data.Pair.Server.ResolveDependency<Robust.Server.Player.IPlayerManager>();
|
||||
data.SMindSys = data.SEntMan.System<Server.Mind.MindSystem>();
|
||||
data.STransformSys = data.SEntMan.System<SharedTransformSystem>();
|
||||
|
||||
// Setup map.
|
||||
await data.Pair.CreateTestMap();
|
||||
data.PlayerCoords = data.SEntMan.GetNetCoordinates(data.MapData.GridCoords.Offset(new Vector2(0.5f, 0.5f)).WithEntityId(data.MapData.MapUid, data.STransformSys, data.SEntMan));
|
||||
|
||||
if (data.Client.Session == null)
|
||||
Assert.Fail("No player");
|
||||
data.ClientSession = data.Client.Session!;
|
||||
data.ServerSession = data.SPlayerMan.GetSessionById(data.ClientSession.UserId);
|
||||
|
||||
Entity<MindComponent> mind = default!;
|
||||
await data.Pair.Server.WaitPost(() =>
|
||||
{
|
||||
data.Player = data.SEntMan.GetNetEntity(data.SEntMan.SpawnEntity(null, data.SEntMan.GetCoordinates(data.PlayerCoords)));
|
||||
mind = data.SMindSys.CreateMind(data.ServerSession.UserId, "DummyPlayerEntity");
|
||||
data.SPlayerEnt = data.SEntMan.GetEntity(data.Player);
|
||||
data.SMindSys.TransferTo(mind, data.SPlayerEnt, mind: mind.Comp);
|
||||
data.Server.PlayerMan.SetAttachedEntity(data.ServerSession, data.SPlayerEnt);
|
||||
});
|
||||
|
||||
await data.Pair.RunTicksSync(5);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(data.ServerSession.ContentData()?.Mind, Is.EqualTo(mind.Owner));
|
||||
Assert.That(data.ServerSession.AttachedEntity, Is.EqualTo(data.SPlayerEnt));
|
||||
Assert.That(data.ServerSession.AttachedEntity, Is.EqualTo(mind.Comp.CurrentEntity),
|
||||
"Player is not attached to the mind's current entity.");
|
||||
Assert.That(data.SEntMan.EntityExists(mind.Comp.OwnedEntity),
|
||||
"The mind's current entity does not exist");
|
||||
Assert.That(mind.Comp.VisitingEntity == null || data.SEntMan.EntityExists(mind.Comp.VisitingEntity),
|
||||
"The minds visited entity does not exist.");
|
||||
});
|
||||
|
||||
Assert.That(data.SPlayerEnt, Is.Not.EqualTo(null));
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that a ghost gets created when the player entity is deleted.
|
||||
/// 1. Delete mob
|
||||
/// 2. Assert is ghost
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestGridGhostOnDelete()
|
||||
{
|
||||
var data = await SetupData();
|
||||
|
||||
var oldPosition = data.SEntMan.GetComponent<TransformComponent>(data.SPlayerEnt).Coordinates;
|
||||
|
||||
Assert.That(!data.SEntMan.HasComponent<GhostComponent>(data.SPlayerEnt), "Player was initially a ghost?");
|
||||
|
||||
// Delete entity
|
||||
await data.Server.WaitPost(() => data.SEntMan.DeleteEntity(data.SPlayerEnt));
|
||||
await data.Pair.RunTicksSync(5);
|
||||
|
||||
var ghost = data.ServerSession.AttachedEntity!.Value;
|
||||
Assert.That(data.SEntMan.HasComponent<GhostComponent>(ghost), "Player did not become a ghost");
|
||||
|
||||
// Ensure the position is the same
|
||||
var ghostPosition = data.SEntMan.GetComponent<TransformComponent>(ghost).Coordinates;
|
||||
Assert.That(ghostPosition, Is.EqualTo(oldPosition));
|
||||
|
||||
await data.Pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Test that a ghost gets created when the player entity is queue deleted.
|
||||
/// 1. Delete mob
|
||||
/// 2. Assert is ghost
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TestGridGhostOnQueueDelete()
|
||||
{
|
||||
var data = await SetupData();
|
||||
|
||||
var oldPosition = data.SEntMan.GetComponent<TransformComponent>(data.SPlayerEnt).Coordinates;
|
||||
|
||||
Assert.That(!data.SEntMan.HasComponent<GhostComponent>(data.SPlayerEnt), "Player was initially a ghost?");
|
||||
|
||||
// Delete entity
|
||||
await data.Server.WaitPost(() => data.SEntMan.QueueDeleteEntity(data.SPlayerEnt));
|
||||
await data.Pair.RunTicksSync(5);
|
||||
|
||||
var ghost = data.ServerSession.AttachedEntity!.Value;
|
||||
Assert.That(data.SEntMan.HasComponent<GhostComponent>(ghost), "Player did not become a ghost");
|
||||
|
||||
// Ensure the position is the same
|
||||
var ghostPosition = data.SEntMan.GetComponent<TransformComponent>(ghost).Coordinates;
|
||||
Assert.That(ghostPosition, Is.EqualTo(oldPosition));
|
||||
|
||||
await data.Pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
#nullable enable
|
||||
using System.Linq;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.Ghost;
|
||||
@@ -77,7 +78,7 @@ public sealed partial class MindTests
|
||||
await using var pair = await SetupPair(dirty: true);
|
||||
var server = pair.Server;
|
||||
var testMap = await pair.CreateTestMap();
|
||||
var coordinates = testMap.GridCoords;
|
||||
var testMap2 = await pair.CreateTestMap();
|
||||
|
||||
var entMan = server.ResolveDependency<IServerEntityManager>();
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
@@ -91,7 +92,7 @@ public sealed partial class MindTests
|
||||
MindComponent mind = default!;
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
playerEnt = entMan.SpawnEntity(null, coordinates);
|
||||
playerEnt = entMan.SpawnEntity(null, testMap.GridCoords);
|
||||
mindId = player.ContentData()!.Mind!.Value;
|
||||
mind = entMan.GetComponent<MindComponent>(mindId);
|
||||
mindSystem.TransferTo(mindId, playerEnt);
|
||||
@@ -100,14 +101,20 @@ public sealed partial class MindTests
|
||||
});
|
||||
|
||||
await pair.RunTicksSync(5);
|
||||
await server.WaitPost(() => mapManager.DeleteMap(testMap.MapId));
|
||||
await server.WaitAssertion(() => mapManager.DeleteMap(testMap.MapId));
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
#pragma warning disable NUnit2045 // Interdependent assertions.
|
||||
Assert.That(entMan.EntityExists(mind.CurrentEntity), Is.True);
|
||||
Assert.That(mind.CurrentEntity, Is.Not.EqualTo(playerEnt));
|
||||
// Spawn ghost on the second map
|
||||
var attachedEntity = player.AttachedEntity;
|
||||
Assert.That(entMan.EntityExists(attachedEntity), Is.True);
|
||||
Assert.That(attachedEntity, Is.Not.EqualTo(playerEnt));
|
||||
Assert.That(entMan.HasComponent<GhostComponent>(attachedEntity));
|
||||
var transform = entMan.GetComponent<TransformComponent>(attachedEntity.Value);
|
||||
Assert.That(transform.MapID, Is.Not.EqualTo(MapId.Nullspace));
|
||||
Assert.That(transform.MapID, Is.Not.EqualTo(testMap.MapId));
|
||||
#pragma warning restore NUnit2045
|
||||
});
|
||||
|
||||
|
||||
@@ -44,33 +44,22 @@ namespace Content.IntegrationTests.Tests.Preferences
|
||||
|
||||
private static HumanoidCharacterProfile CharlieCharlieson()
|
||||
{
|
||||
return new(
|
||||
"Charlie Charlieson",
|
||||
"The biggest boy around.",
|
||||
"Human",
|
||||
"Eugene", // Corvax-TTS
|
||||
21,
|
||||
Sex.Male,
|
||||
Gender.Epicene,
|
||||
new HumanoidCharacterAppearance(
|
||||
return new()
|
||||
{
|
||||
Name = "Charlie Charlieson",
|
||||
FlavorText = "The biggest boy around.",
|
||||
Species = "Human",
|
||||
Voice = "Eugene", // Corvax-TTS
|
||||
Age = 21,
|
||||
Appearance = new(
|
||||
"Afro",
|
||||
Color.Aqua,
|
||||
"Shaved",
|
||||
Color.Aquamarine,
|
||||
Color.Azure,
|
||||
Color.Beige,
|
||||
new ()
|
||||
),
|
||||
SpawnPriorityPreference.None,
|
||||
new Dictionary<string, JobPriority>
|
||||
{
|
||||
{SharedGameTicker.FallbackOverflowJob, JobPriority.High}
|
||||
},
|
||||
PreferenceUnavailableMode.StayInLobby,
|
||||
new List<string> (),
|
||||
new List<string>(),
|
||||
new Dictionary<string, RoleLoadout>()
|
||||
);
|
||||
new ())
|
||||
};
|
||||
}
|
||||
|
||||
private static ServerDbSqlite GetDb(RobustIntegrationTest.ServerIntegrationInstance server)
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Content.IntegrationTests.Tests
|
||||
{
|
||||
private sealed class RoundEndTestSystem : EntitySystem
|
||||
{
|
||||
public int Count;
|
||||
public int RoundCount;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -22,7 +22,7 @@ namespace Content.IntegrationTests.Tests
|
||||
|
||||
private void OnRoundEnd(RoundEndSystemChangedEvent ev)
|
||||
{
|
||||
Interlocked.Increment(ref Count);
|
||||
Interlocked.Increment(ref RoundCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Content.IntegrationTests.Tests
|
||||
var ticker = sysManager.GetEntitySystem<GameTicker>();
|
||||
var roundEndSystem = sysManager.GetEntitySystem<RoundEndSystem>();
|
||||
var sys = server.System<RoundEndTestSystem>();
|
||||
sys.Count = 0;
|
||||
sys.RoundCount = 0;
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
@@ -128,8 +128,8 @@ namespace Content.IntegrationTests.Tests
|
||||
async Task WaitForEvent()
|
||||
{
|
||||
var timeout = Task.Delay(TimeSpan.FromSeconds(10));
|
||||
var currentCount = Thread.VolatileRead(ref sys.Count);
|
||||
while (currentCount == Thread.VolatileRead(ref sys.Count) && !timeout.IsCompleted)
|
||||
var currentCount = Thread.VolatileRead(ref sys.RoundCount);
|
||||
while (currentCount == Thread.VolatileRead(ref sys.RoundCount) && !timeout.IsCompleted)
|
||||
{
|
||||
await pair.RunTicksSync(5);
|
||||
}
|
||||
|
||||
@@ -87,8 +87,9 @@ public sealed class EvacShuttleTest
|
||||
Assert.That(LifeStage(salternXform.MapUid.Value), Is.EqualTo(EntityLifeStage.MapInitialized));
|
||||
|
||||
// Set up shuttle timing
|
||||
var shuttleSys = server.System<ShuttleSystem>();
|
||||
var evacSys = server.System<EmergencyShuttleSystem>();
|
||||
evacSys.TransitTime = ShuttleSystem.DefaultTravelTime; // Absolute minimum transit time, so the test has to run for at least this long
|
||||
evacSys.TransitTime = shuttleSys.DefaultTravelTime; // Absolute minimum transit time, so the test has to run for at least this long
|
||||
// TODO SHUTTLE fix spaghetti
|
||||
|
||||
var dockTime = server.CfgMan.GetCVar(CCVars.EmergencyShuttleDockTime);
|
||||
@@ -112,7 +113,7 @@ public sealed class EvacShuttleTest
|
||||
Assert.That(shuttleXform.MapUid, Is.EqualTo(ftl.Owner));
|
||||
|
||||
// Shuttle should have arrived at centcomm
|
||||
await pair.RunSeconds(ShuttleSystem.DefaultTravelTime);
|
||||
await pair.RunSeconds(shuttleSys.DefaultTravelTime);
|
||||
Assert.That(shuttleXform.MapUid, Is.EqualTo(centcommMap));
|
||||
|
||||
// Round should be ending now
|
||||
|
||||
@@ -28,7 +28,6 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Interaction;
|
||||
using Robust.Shared.Random;
|
||||
@@ -38,16 +39,27 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
||||
|
||||
private void OnActivate(EntityUid uid, ActionOnInteractComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (args.Handled || component.ActionEntities == null)
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var options = GetValidActions<InstantActionComponent>(component.ActionEntities);
|
||||
if (component.ActionEntities is not {} actionEnts)
|
||||
{
|
||||
if (!TryComp<ActionsContainerComponent>(uid, out var actionsContainerComponent))
|
||||
return;
|
||||
|
||||
actionEnts = actionsContainerComponent.Container.ContainedEntities.ToList();
|
||||
}
|
||||
|
||||
var options = GetValidActions<InstantActionComponent>(actionEnts);
|
||||
if (options.Count == 0)
|
||||
return;
|
||||
|
||||
var (actId, act) = _random.Pick(options);
|
||||
if (act.Event != null)
|
||||
{
|
||||
act.Event.Performer = args.User;
|
||||
act.Event.Action = actId;
|
||||
}
|
||||
|
||||
_actions.PerformAction(args.User, null, actId, act, act.Event, _timing.CurTime, false);
|
||||
args.Handled = true;
|
||||
@@ -55,13 +67,21 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
||||
|
||||
private void OnAfterInteract(EntityUid uid, ActionOnInteractComponent component, AfterInteractEvent args)
|
||||
{
|
||||
if (args.Handled || component.ActionEntities == null)
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (component.ActionEntities is not {} actionEnts)
|
||||
{
|
||||
if (!TryComp<ActionsContainerComponent>(uid, out var actionsContainerComponent))
|
||||
return;
|
||||
|
||||
actionEnts = actionsContainerComponent.Container.ContainedEntities.ToList();
|
||||
}
|
||||
|
||||
// First, try entity target actions
|
||||
if (args.Target != null)
|
||||
{
|
||||
var entOptions = GetValidActions<EntityTargetActionComponent>(component.ActionEntities, args.CanReach);
|
||||
var entOptions = GetValidActions<EntityTargetActionComponent>(actionEnts, args.CanReach);
|
||||
for (var i = entOptions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var action = entOptions[i];
|
||||
@@ -75,6 +95,7 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
||||
if (entAct.Event != null)
|
||||
{
|
||||
entAct.Event.Performer = args.User;
|
||||
entAct.Event.Action = entActId;
|
||||
entAct.Event.Target = args.Target.Value;
|
||||
}
|
||||
|
||||
@@ -100,6 +121,7 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
||||
if (act.Event != null)
|
||||
{
|
||||
act.Event.Performer = args.User;
|
||||
act.Event.Action = actId;
|
||||
act.Event.Target = args.ClickLocation;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ namespace Content.Server.Administration.Commands
|
||||
[AdminCommand(AdminFlags.Admin)]
|
||||
sealed class DSay : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "dsay";
|
||||
|
||||
public string Description => Loc.GetString("dsay-command-description");
|
||||
@@ -32,7 +34,7 @@ namespace Content.Server.Administration.Commands
|
||||
if (string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
var chat = EntitySystem.Get<ChatSystem>();
|
||||
var chat = _e.System<ChatSystem>();
|
||||
chat.TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Dead, false, shell, player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.Administration.Commands;
|
||||
|
||||
@@ -105,7 +106,7 @@ public sealed class ExplosionCommand : IConsoleCommand
|
||||
if (args.Length > 4)
|
||||
coords = new MapCoordinates(new Vector2(x, y), xform.MapID);
|
||||
else
|
||||
coords = xform.MapPosition;
|
||||
coords = entMan.System<TransformSystem>().GetMapCoordinates(shell.Player.AttachedEntity.Value, xform: xform);
|
||||
}
|
||||
|
||||
ExplosionPrototype? type;
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.Server.Administration.Commands
|
||||
[AdminCommand(AdminFlags.Round)]
|
||||
public sealed class ReadyAll : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "readyall";
|
||||
public string Description => "Readies up all players in the lobby, except for observers.";
|
||||
public string Help => $"{Command} | ̣{Command} <ready>";
|
||||
@@ -20,7 +22,7 @@ namespace Content.Server.Administration.Commands
|
||||
ready = bool.Parse(args[0]);
|
||||
}
|
||||
|
||||
var gameTicker = EntitySystem.Get<GameTicker>();
|
||||
var gameTicker = _e.System<GameTicker>();
|
||||
|
||||
|
||||
if (gameTicker.RunLevel != GameRunLevel.PreRoundLobby)
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Content.Server.Administration.Commands
|
||||
[AdminCommand(AdminFlags.Round)]
|
||||
public sealed class CallShuttleCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "callshuttle";
|
||||
public string Description => Loc.GetString("call-shuttle-command-description");
|
||||
public string Help => Loc.GetString("call-shuttle-command-help-text", ("command",Command));
|
||||
@@ -19,7 +21,7 @@ namespace Content.Server.Administration.Commands
|
||||
// ReSharper disable once ConvertIfStatementToSwitchStatement
|
||||
if (args.Length == 1 && TimeSpan.TryParseExact(args[0], ContentLocalizationManager.TimeSpanMinutesFormats, loc.DefaultCulture, out var timeSpan))
|
||||
{
|
||||
EntitySystem.Get<RoundEndSystem>().RequestRoundEnd(timeSpan, shell.Player?.AttachedEntity, false);
|
||||
_e.System<RoundEndSystem>().RequestRoundEnd(timeSpan, shell.Player?.AttachedEntity, false);
|
||||
}
|
||||
else if (args.Length == 1)
|
||||
{
|
||||
@@ -27,7 +29,7 @@ namespace Content.Server.Administration.Commands
|
||||
}
|
||||
else
|
||||
{
|
||||
EntitySystem.Get<RoundEndSystem>().RequestRoundEnd(shell.Player?.AttachedEntity, false);
|
||||
_e.System<RoundEndSystem>().RequestRoundEnd(shell.Player?.AttachedEntity, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,13 +37,15 @@ namespace Content.Server.Administration.Commands
|
||||
[AdminCommand(AdminFlags.Round)]
|
||||
public sealed class RecallShuttleCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "recallshuttle";
|
||||
public string Description => Loc.GetString("recall-shuttle-command-description");
|
||||
public string Help => Loc.GetString("recall-shuttle-command-help-text", ("command",Command));
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
EntitySystem.Get<RoundEndSystem>().CancelRoundEndCountdown(shell.Player?.AttachedEntity, false);
|
||||
_e.System<RoundEndSystem>().CancelRoundEndCountdown(shell.Player?.AttachedEntity, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ namespace Content.Server.Administration.Commands;
|
||||
[AdminCommand(AdminFlags.VarEdit)]
|
||||
public sealed class ThrowScoreboardCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
public string Command => "throwscoreboard";
|
||||
|
||||
public string Description => Loc.GetString("throw-scoreboard-command-description");
|
||||
@@ -20,6 +22,6 @@ public sealed class ThrowScoreboardCommand : IConsoleCommand
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
EntitySystem.Get<GameTicker>().ShowRoundEndScoreboard();
|
||||
_e.System<GameTicker>().ShowRoundEndScoreboard();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ public sealed class AdminLogsEui : BaseEui
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _configuration = default!;
|
||||
[Dependency] private readonly IEntityManager _e = default!;
|
||||
|
||||
private readonly ISawmill _sawmill;
|
||||
|
||||
@@ -51,7 +52,7 @@ public sealed class AdminLogsEui : BaseEui
|
||||
};
|
||||
}
|
||||
|
||||
private int CurrentRoundId => EntitySystem.Get<GameTicker>().RoundId;
|
||||
private int CurrentRoundId => _e.System<GameTicker>().RoundId;
|
||||
|
||||
public override async void Opened()
|
||||
{
|
||||
|
||||
16
Content.Server/Administration/Systems/AdminFrozenSystem.cs
Normal file
16
Content.Server/Administration/Systems/AdminFrozenSystem.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Content.Shared.Administration;
|
||||
|
||||
namespace Content.Server.Administration.Systems;
|
||||
|
||||
public sealed class AdminFrozenSystem : SharedAdminFrozenSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Freezes and mutes the given entity.
|
||||
/// </summary>
|
||||
public void FreezeAndMute(EntityUid uid)
|
||||
{
|
||||
var comp = EnsureComp<AdminFrozenComponent>(uid);
|
||||
comp.Muted = true;
|
||||
Dirty(uid, comp);
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ using Content.Shared.Movement.Components;
|
||||
using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Nutrition.Components;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Slippery;
|
||||
using Content.Shared.Tabletop.Components;
|
||||
using Content.Shared.Tools.Systems;
|
||||
using Content.Shared.Verbs;
|
||||
@@ -77,6 +78,7 @@ public sealed partial class AdminVerbSystem
|
||||
[Dependency] private readonly SharedContentEyeSystem _eyeSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly SuperBonkSystem _superBonkSystem = default!;
|
||||
[Dependency] private readonly SlipperySystem _slipperySystem = default!;
|
||||
|
||||
// All smite verbs have names so invokeverb works.
|
||||
private void AddSmiteVerbs(GetVerbsEvent<Verb> args)
|
||||
@@ -95,12 +97,12 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb explode = new()
|
||||
{
|
||||
Text = "Explode",
|
||||
Text = "admin-smite-explode-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/smite.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var coords = Transform(args.Target).MapPosition;
|
||||
var coords = _transformSystem.GetMapCoordinates(args.Target);
|
||||
Timer.Spawn(_gameTiming.TickPeriod,
|
||||
() => _explosionSystem.QueueExplosion(coords, ExplosionSystem.DefaultExplosionPrototypeId,
|
||||
4, 1, 2, maxTileBreak: 0), // it gibs, damage doesn't need to be high.
|
||||
@@ -115,7 +117,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb chess = new()
|
||||
{
|
||||
Text = "Chess Dimension",
|
||||
Text = "admin-smite-chess-dimension-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/Tabletop/chessboard.rsi"), "chessboard"),
|
||||
Act = () =>
|
||||
@@ -143,7 +145,7 @@ public sealed partial class AdminVerbSystem
|
||||
{
|
||||
Verb flames = new()
|
||||
{
|
||||
Text = "Set Alight",
|
||||
Text = "admin-smite-set-alight-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Fire/fire.png")),
|
||||
Act = () =>
|
||||
@@ -165,7 +167,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb monkey = new()
|
||||
{
|
||||
Text = "Monkeyify",
|
||||
Text = "admin-smite-monkeyify-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Animals/monkey.rsi"), "monkey"),
|
||||
Act = () =>
|
||||
@@ -179,7 +181,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb disposalBin = new()
|
||||
{
|
||||
Text = "Garbage Can",
|
||||
Text = "admin-smite-electrocute-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Structures/Piping/disposal.rsi"), "disposal"),
|
||||
Act = () =>
|
||||
@@ -196,7 +198,7 @@ public sealed partial class AdminVerbSystem
|
||||
{
|
||||
Verb hardElectrocute = new()
|
||||
{
|
||||
Text = "Electrocute",
|
||||
Text = "admin-smite-creampie-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Hands/Gloves/Color/yellow.rsi"), "icon"),
|
||||
Act = () =>
|
||||
@@ -241,7 +243,7 @@ public sealed partial class AdminVerbSystem
|
||||
{
|
||||
Verb creamPie = new()
|
||||
{
|
||||
Text = "Creampie",
|
||||
Text = "admin-smite-remove-blood-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Consumable/Food/Baked/pie.rsi"), "plain-slice"),
|
||||
Act = () =>
|
||||
@@ -258,7 +260,7 @@ public sealed partial class AdminVerbSystem
|
||||
{
|
||||
Verb bloodRemoval = new()
|
||||
{
|
||||
Text = "Remove blood",
|
||||
Text = "admin-smite-vomit-organs-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Fluids/tomato_splat.rsi"), "puddle-1"),
|
||||
Act = () =>
|
||||
@@ -281,7 +283,7 @@ public sealed partial class AdminVerbSystem
|
||||
{
|
||||
Verb vomitOrgans = new()
|
||||
{
|
||||
Text = "Vomit organs",
|
||||
Text = "admin-smite-remove-hands-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Fluids/vomit_toxin.rsi"), "vomit_toxin-1"),
|
||||
Act = () =>
|
||||
@@ -309,7 +311,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb handsRemoval = new()
|
||||
{
|
||||
Text = "Remove hands",
|
||||
Text = "admin-smite-remove-hand-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/remove-hands.png")),
|
||||
Act = () =>
|
||||
@@ -331,7 +333,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb handRemoval = new()
|
||||
{
|
||||
Text = "Remove hand",
|
||||
Text = "admin-smite-pinball-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/remove-hand.png")),
|
||||
Act = () =>
|
||||
@@ -354,7 +356,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb stomachRemoval = new()
|
||||
{
|
||||
Text = "Stomach Removal",
|
||||
Text = "admin-smite-yeet-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Species/Human/organs.rsi"), "stomach"),
|
||||
Act = () =>
|
||||
@@ -374,7 +376,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb lungRemoval = new()
|
||||
{
|
||||
Text = "Lungs Removal",
|
||||
Text = "admin-smite-become-bread-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Species/Human/organs.rsi"), "lung-r"),
|
||||
Act = () =>
|
||||
@@ -397,7 +399,7 @@ public sealed partial class AdminVerbSystem
|
||||
{
|
||||
Verb pinball = new()
|
||||
{
|
||||
Text = "Pinball",
|
||||
Text = "admin-smite-ghostkick-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/toys.rsi"), "basketball"),
|
||||
Act = () =>
|
||||
@@ -431,7 +433,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb yeet = new()
|
||||
{
|
||||
Text = "Yeet",
|
||||
Text = "admin-smite-nyanify-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/eject.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
@@ -462,7 +464,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb bread = new()
|
||||
{
|
||||
Text = "Become Bread",
|
||||
Text = "admin-smite-kill-sign-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Consumable/Food/Baked/bread.rsi"), "plain"),
|
||||
Act = () =>
|
||||
@@ -476,7 +478,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb mouse = new()
|
||||
{
|
||||
Text = "Become Mouse",
|
||||
Text = "admin-smite-cluwne-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Animals/mouse.rsi"), "icon-0"),
|
||||
Act = () =>
|
||||
@@ -492,7 +494,7 @@ public sealed partial class AdminVerbSystem
|
||||
{
|
||||
Verb ghostKick = new()
|
||||
{
|
||||
Text = "Ghostkick",
|
||||
Text = "admin-smite-anger-pointing-arrows-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/gavel.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
@@ -508,7 +510,7 @@ public sealed partial class AdminVerbSystem
|
||||
if (TryComp<InventoryComponent>(args.Target, out var inventory)) {
|
||||
Verb nyanify = new()
|
||||
{
|
||||
Text = "Nyanify",
|
||||
Text = "admin-smite-dust-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Head/Hats/catears.rsi"), "icon"),
|
||||
Act = () =>
|
||||
@@ -525,7 +527,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb killSign = new()
|
||||
{
|
||||
Text = "Kill sign",
|
||||
Text = "admin-smite-buffering-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Misc/killsign.rsi"), "icon"),
|
||||
Act = () =>
|
||||
@@ -539,7 +541,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb cluwne = new()
|
||||
{
|
||||
Text = "Cluwne",
|
||||
Text = "admin-smite-become-instrument-name",
|
||||
Category = VerbCategory.Smite,
|
||||
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Mask/cluwne.rsi"), "icon"),
|
||||
@@ -555,7 +557,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb maiden = new()
|
||||
{
|
||||
Text = "Maid",
|
||||
Text = "admin-smite-remove-gravity-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Clothing/Uniforms/Jumpskirt/janimaid.rsi"), "icon"),
|
||||
Act = () =>
|
||||
@@ -575,7 +577,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb angerPointingArrows = new()
|
||||
{
|
||||
Text = "Anger Pointing Arrows",
|
||||
Text = "admin-smite-reptilian-species-swap-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Interface/Misc/pointing.rsi"), "pointing"),
|
||||
Act = () =>
|
||||
@@ -589,7 +591,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb dust = new()
|
||||
{
|
||||
Text = "Dust",
|
||||
Text = "admin-smite-locker-stuff-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Materials/materials.rsi"), "ash"),
|
||||
Act = () =>
|
||||
@@ -605,7 +607,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb youtubeVideoSimulation = new()
|
||||
{
|
||||
Text = "Buffering",
|
||||
Text = "admin-smite-headstand-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/Misc/buffering_smite_icon.png")),
|
||||
Act = () =>
|
||||
@@ -619,7 +621,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb instrumentation = new()
|
||||
{
|
||||
Text = "Become Instrument",
|
||||
Text = "admin-smite-become-mouse-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/Instruments/h_synthesizer.rsi"), "icon"),
|
||||
Act = () =>
|
||||
@@ -633,7 +635,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb noGravity = new()
|
||||
{
|
||||
Text = "Remove gravity",
|
||||
Text = "admin-smite-maid-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Machines/gravity_generator.rsi"), "off"),
|
||||
Act = () =>
|
||||
@@ -650,7 +652,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb reptilian = new()
|
||||
{
|
||||
Text = "Reptilian Species Swap",
|
||||
Text = "admin-smite-zoom-in-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Objects/Fun/toys.rsi"), "plushie_lizard"),
|
||||
Act = () =>
|
||||
@@ -664,7 +666,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb locker = new()
|
||||
{
|
||||
Text = "Locker stuff",
|
||||
Text = "admin-smite-flip-eye-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new ("/Textures/Structures/Storage/closet.rsi"), "generic"),
|
||||
Act = () =>
|
||||
@@ -686,7 +688,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb headstand = new()
|
||||
{
|
||||
Text = "Headstand",
|
||||
Text = "admin-smite-run-walk-swap-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/refresh.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
@@ -700,7 +702,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb zoomIn = new()
|
||||
{
|
||||
Text = "Zoom in",
|
||||
Text = "admin-smite-super-speed-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/zoom.png")),
|
||||
Act = () =>
|
||||
@@ -715,7 +717,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb flipEye = new()
|
||||
{
|
||||
Text = "Flip eye",
|
||||
Text = "admin-smite-stomach-removal-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/flip.png")),
|
||||
Act = () =>
|
||||
@@ -730,7 +732,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb runWalkSwap = new()
|
||||
{
|
||||
Text = "Run Walk Swap",
|
||||
Text = "admin-smite-speak-backwards-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/run-walk-swap.png")),
|
||||
Act = () =>
|
||||
@@ -750,7 +752,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb backwardsAccent = new()
|
||||
{
|
||||
Text = "Speak Backwards",
|
||||
Text = "admin-smite-lung-removal-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/help-backwards.png")),
|
||||
Act = () =>
|
||||
@@ -764,7 +766,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb disarmProne = new()
|
||||
{
|
||||
Text = "Disarm Prone",
|
||||
Text = "admin-smite-disarm-prone-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/Actions/disarm.png")),
|
||||
Act = () =>
|
||||
@@ -778,7 +780,7 @@ public sealed partial class AdminVerbSystem
|
||||
|
||||
Verb superSpeed = new()
|
||||
{
|
||||
Text = "Super speed",
|
||||
Text = "admin-smite-garbage-can-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/AdminActions/super_speed.png")),
|
||||
Act = () =>
|
||||
@@ -797,7 +799,7 @@ public sealed partial class AdminVerbSystem
|
||||
//Bonk
|
||||
Verb superBonkLite = new()
|
||||
{
|
||||
Text = "Super Bonk Lite",
|
||||
Text = "admin-smite-super-bonk-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new("Structures/Furniture/Tables/glass.rsi"), "full"),
|
||||
Act = () =>
|
||||
@@ -810,7 +812,7 @@ public sealed partial class AdminVerbSystem
|
||||
args.Verbs.Add(superBonkLite);
|
||||
Verb superBonk= new()
|
||||
{
|
||||
Text = "Super Bonk",
|
||||
Text = "admin-smite-super-bonk-lite-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new("Structures/Furniture/Tables/generic.rsi"), "full"),
|
||||
Act = () =>
|
||||
@@ -821,5 +823,31 @@ public sealed partial class AdminVerbSystem
|
||||
Impact = LogImpact.Extreme,
|
||||
};
|
||||
args.Verbs.Add(superBonk);
|
||||
|
||||
Verb superslip = new()
|
||||
{
|
||||
Text = "admin-smite-super-slip-name",
|
||||
Category = VerbCategory.Smite,
|
||||
Icon = new SpriteSpecifier.Rsi(new("Objects/Specific/Janitorial/soap.rsi"), "omega-4"),
|
||||
Act = () =>
|
||||
{
|
||||
var hadSlipComponent = EnsureComp(args.Target, out SlipperyComponent slipComponent);
|
||||
if (!hadSlipComponent)
|
||||
{
|
||||
slipComponent.SuperSlippery = true;
|
||||
slipComponent.ParalyzeTime = 5;
|
||||
slipComponent.LaunchForwardsMultiplier = 20;
|
||||
}
|
||||
|
||||
_slipperySystem.TrySlip(args.Target, slipComponent, args.Target, requiresContact: false);
|
||||
if (!hadSlipComponent)
|
||||
{
|
||||
RemComp(args.Target, slipComponent);
|
||||
}
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = Loc.GetString("admin-smite-super-slip-description")
|
||||
};
|
||||
args.Verbs.Add(superslip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ namespace Content.Server.Administration.Systems
|
||||
[Dependency] private readonly StationSystem _stations = default!;
|
||||
[Dependency] private readonly StationSpawningSystem _spawning = default!;
|
||||
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
||||
[Dependency] private readonly AdminFrozenSystem _freeze = default!;
|
||||
|
||||
private readonly Dictionary<ICommonSession, List<EditSolutionsEui>> _openSolutionUis = new();
|
||||
|
||||
@@ -131,24 +132,57 @@ namespace Content.Server.Administration.Systems
|
||||
args.Verbs.Add(prayerVerb);
|
||||
|
||||
// Freeze
|
||||
var frozen = HasComp<AdminFrozenComponent>(args.Target);
|
||||
args.Verbs.Add(new Verb
|
||||
var frozen = TryComp<AdminFrozenComponent>(args.Target, out var frozenComp);
|
||||
var frozenAndMuted = frozenComp?.Muted ?? false;
|
||||
|
||||
if (!frozen)
|
||||
{
|
||||
Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze.
|
||||
Text = frozen
|
||||
? Loc.GetString("admin-verbs-unfreeze")
|
||||
: Loc.GetString("admin-verbs-freeze"),
|
||||
Category = VerbCategory.Admin,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
args.Verbs.Add(new Verb
|
||||
{
|
||||
if (frozen)
|
||||
RemComp<AdminFrozenComponent>(args.Target);
|
||||
else
|
||||
Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze.
|
||||
Text = Loc.GetString("admin-verbs-freeze"),
|
||||
Category = VerbCategory.Admin,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
EnsureComp<AdminFrozenComponent>(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
});
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
});
|
||||
}
|
||||
|
||||
if (!frozenAndMuted)
|
||||
{
|
||||
// allow you to additionally mute someone when they are already frozen
|
||||
args.Verbs.Add(new Verb
|
||||
{
|
||||
Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze.
|
||||
Text = Loc.GetString("admin-verbs-freeze-and-mute"),
|
||||
Category = VerbCategory.Admin,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_freeze.FreezeAndMute(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
});
|
||||
}
|
||||
|
||||
if (frozen)
|
||||
{
|
||||
args.Verbs.Add(new Verb
|
||||
{
|
||||
Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze.
|
||||
Text = Loc.GetString("admin-verbs-unfreeze"),
|
||||
Category = VerbCategory.Admin,
|
||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")),
|
||||
Act = () =>
|
||||
{
|
||||
RemComp<AdminFrozenComponent>(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
});
|
||||
}
|
||||
|
||||
// Erase
|
||||
args.Verbs.Add(new Verb
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user