From 4d4f67132c5740839c48de06fa93d34b1667f41e Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Wed, 10 Jul 2024 21:48:00 -0700 Subject: [PATCH 01/78] Allow attack while pulling (#29703) * Make VirtualItem not block attacking * Remove unneeded usings --- Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index b67acdea4f9..42b6a3c9c74 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -12,7 +12,7 @@ using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Inventory; -using Content.Shared.Item; +using Content.Shared.Inventory.VirtualItem; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Physics; using Content.Shared.Popups; @@ -27,7 +27,6 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Timing; -using Robust.Shared.Toolshed.Syntax; using ItemToggleMeleeWeaponComponent = Content.Shared.Item.ItemToggle.Components.ItemToggleMeleeWeaponComponent; namespace Content.Shared.Weapons.Melee; @@ -276,7 +275,8 @@ public bool TryGetWeapon(EntityUid entity, out EntityUid weaponUid, [NotNullWhen return true; } - return false; + if (!HasComp(held)) + return false; } // Use hands clothing if applicable. From e01af4d602479887915774d5a6cdcbf0d9c72188 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 11 Jul 2024 04:49:06 +0000 Subject: [PATCH 02/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index f52dc2bfd37..ce042e5e136 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: slarticodefast - changes: - - message: The internal volume of cryopods has been increased from 101.3 to 1000L. - This speeds up cooling patients inside. - type: Tweak - id: 6400 - time: '2024-04-20T03:44:39.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27148 - author: UBlueberry changes: - message: Changed the guidebook entry for the Space Ninja, bringing it up to the @@ -3826,3 +3818,11 @@ id: 6899 time: '2024-07-11T00:24:37.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29875 +- author: ShadowCommander + changes: + - message: Players can now use melee attacks and shoves while dragging an entity + in their active hand. + type: Tweak + id: 6900 + time: '2024-07-11T04:48:00.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29703 From 6371a046215b90f1ca57ca8135c677786f79a413 Mon Sep 17 00:00:00 2001 From: Chief-Engineer <119664036+Chief-Engineer@users.noreply.github.com> Date: Thu, 11 Jul 2024 00:14:01 -0500 Subject: [PATCH 03/78] Fix baby jail (#29896) * Revert "Revert Baby Jail (#29891)" This reverts commit 24a2866747e6de37ab2c4395bee9ea44c9211c6f. * the fix --- .../Administration/UI/AdminMenuWindow.xaml | 4 +- .../Administration/UI/AdminMenuWindow.xaml.cs | 20 ++- .../BabyJailTab/BabyJailStatusWindow.xaml | 6 + .../BabyJailTab/BabyJailStatusWindow.xaml.cs | 21 +++ .../UI/Tabs/BabyJailTab/BabyJailTab.xaml | 26 ++++ .../UI/Tabs/BabyJailTab/BabyJailTab.xaml.cs | 75 ++++++++++ .../Systems/Admin/AdminUIController.cs | 22 +++ Content.Server.Database/Model.cs | 4 + .../Commands/BabyJailCommand.cs | 139 ++++++++++++++++++ .../Administration/Systems/AdminSystem.cs | 50 +++++++ .../Connection/ConnectionManager.cs | 66 +++++++++ .../GameTicking/GameTicker.StatusShell.cs | 6 + .../Events/BabyJailChangedEvent.cs | 22 +++ Content.Shared/CCVar/CCVars.cs | 42 ++++++ .../WizardsDen/wizardsDenGateway.toml | 6 + .../WizardsDen/wizardsDenLRPTide.toml | 4 + .../administration/commands/babyjail.ftl | 19 +++ .../administration/ui/admin-menu-window.ftl | 1 + .../administration/ui/tabs/babyjail-tab.ftl | 16 ++ .../Locale/en-US/connection-messages.ftl | 5 + 20 files changed, 545 insertions(+), 9 deletions(-) create mode 100644 Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml create mode 100644 Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml.cs create mode 100644 Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml create mode 100644 Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml.cs create mode 100644 Content.Server/Administration/Commands/BabyJailCommand.cs create mode 100644 Content.Shared/Administration/Events/BabyJailChangedEvent.cs create mode 100644 Resources/Locale/en-US/administration/commands/babyjail.ftl create mode 100644 Resources/Locale/en-US/administration/ui/tabs/babyjail-tab.ftl diff --git a/Content.Client/Administration/UI/AdminMenuWindow.xaml b/Content.Client/Administration/UI/AdminMenuWindow.xaml index 311d67b826c..d3d3df02d93 100644 --- a/Content.Client/Administration/UI/AdminMenuWindow.xaml +++ b/Content.Client/Administration/UI/AdminMenuWindow.xaml @@ -6,7 +6,8 @@ xmlns:tabs="clr-namespace:Content.Client.Administration.UI.Tabs" xmlns:playerTab="clr-namespace:Content.Client.Administration.UI.Tabs.PlayerTab" xmlns:objectsTab="clr-namespace:Content.Client.Administration.UI.Tabs.ObjectsTab" - xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab"> + xmlns:panic="clr-namespace:Content.Client.Administration.UI.Tabs.PanicBunkerTab" + xmlns:baby="clr-namespace:Content.Client.Administration.UI.Tabs.BabyJailTab"> @@ -14,6 +15,7 @@ + diff --git a/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs b/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs index 51330a547ec..d5c43e2a500 100644 --- a/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs +++ b/Content.Client/Administration/UI/AdminMenuWindow.xaml.cs @@ -15,14 +15,18 @@ public AdminMenuWindow() MinSize = new Vector2(650, 250); Title = Loc.GetString("admin-menu-title"); RobustXamlLoader.Load(this); - MasterTabContainer.SetTabTitle(0, Loc.GetString("admin-menu-admin-tab")); - MasterTabContainer.SetTabTitle(1, Loc.GetString("admin-menu-adminbus-tab")); - MasterTabContainer.SetTabTitle(2, Loc.GetString("admin-menu-atmos-tab")); - MasterTabContainer.SetTabTitle(3, Loc.GetString("admin-menu-round-tab")); - MasterTabContainer.SetTabTitle(4, Loc.GetString("admin-menu-server-tab")); - MasterTabContainer.SetTabTitle(5, Loc.GetString("admin-menu-panic-bunker-tab")); - MasterTabContainer.SetTabTitle(6, Loc.GetString("admin-menu-players-tab")); - MasterTabContainer.SetTabTitle(7, Loc.GetString("admin-menu-objects-tab")); + MasterTabContainer.SetTabTitle((int) TabIndex.Admin, Loc.GetString("admin-menu-admin-tab")); + MasterTabContainer.SetTabTitle((int) TabIndex.Adminbus, Loc.GetString("admin-menu-adminbus-tab")); + MasterTabContainer.SetTabTitle((int) TabIndex.Atmos, Loc.GetString("admin-menu-atmos-tab")); + MasterTabContainer.SetTabTitle((int) TabIndex.Round, Loc.GetString("admin-menu-round-tab")); + MasterTabContainer.SetTabTitle((int) TabIndex.Server, Loc.GetString("admin-menu-server-tab")); + MasterTabContainer.SetTabTitle((int) TabIndex.PanicBunker, Loc.GetString("admin-menu-panic-bunker-tab")); + /* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + MasterTabContainer.SetTabTitle((int) TabIndex.BabyJail, Loc.GetString("admin-menu-baby-jail-tab")); + MasterTabContainer.SetTabTitle((int) TabIndex.Players, Loc.GetString("admin-menu-players-tab")); + MasterTabContainer.SetTabTitle((int) TabIndex.Objects, Loc.GetString("admin-menu-objects-tab")); MasterTabContainer.OnTabChanged += OnTabChanged; } diff --git a/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml new file mode 100644 index 00000000000..b8034faf52a --- /dev/null +++ b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml @@ -0,0 +1,6 @@ + + + diff --git a/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml.cs b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml.cs new file mode 100644 index 00000000000..9e1d53818f2 --- /dev/null +++ b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailStatusWindow.xaml.cs @@ -0,0 +1,21 @@ +using Content.Client.Message; +using Content.Client.UserInterface.Controls; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.Administration.UI.Tabs.BabyJailTab; + +/* + * TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + +[GenerateTypedNameReferences] +public sealed partial class BabyJailStatusWindow : FancyWindow +{ + public BabyJailStatusWindow() + { + RobustXamlLoader.Load(this); + MessageLabel.SetMarkup(Loc.GetString("admin-ui-baby-jail-is-enabled")); + } +} diff --git a/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml new file mode 100644 index 00000000000..dd770c2be53 --- /dev/null +++ b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml.cs b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml.cs new file mode 100644 index 00000000000..aa9d6ced951 --- /dev/null +++ b/Content.Client/Administration/UI/Tabs/BabyJailTab/BabyJailTab.xaml.cs @@ -0,0 +1,75 @@ +using Content.Shared.Administration.Events; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Console; + +/* + * TODO: Remove me once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + +namespace Content.Client.Administration.UI.Tabs.BabyJailTab; + +[GenerateTypedNameReferences] +public sealed partial class BabyJailTab : Control +{ + [Dependency] private readonly IConsoleHost _console = default!; + + private string _maxAccountAge; + private string _maxOverallMinutes; + + public BabyJailTab() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + MaxAccountAge.OnTextEntered += args => SendMaxAccountAge(args.Text); + MaxAccountAge.OnFocusExit += args => SendMaxAccountAge(args.Text); + _maxAccountAge = MaxAccountAge.Text; + + MaxOverallMinutes.OnTextEntered += args => SendMaxOverallMinutes(args.Text); + MaxOverallMinutes.OnFocusExit += args => SendMaxOverallMinutes(args.Text); + _maxOverallMinutes = MaxOverallMinutes.Text; + } + + private void SendMaxAccountAge(string text) + { + if (string.IsNullOrWhiteSpace(text) || + text == _maxAccountAge || + !int.TryParse(text, out var minutes)) + { + return; + } + + _console.ExecuteCommand($"babyjail_max_account_age {minutes}"); + } + + private void SendMaxOverallMinutes(string text) + { + if (string.IsNullOrWhiteSpace(text) || + text == _maxOverallMinutes || + !int.TryParse(text, out var minutes)) + { + return; + } + + _console.ExecuteCommand($"babyjail_max_overall_minutes {minutes}"); + } + + public void UpdateStatus(BabyJailStatus status) + { + EnabledButton.Pressed = status.Enabled; + EnabledButton.Text = Loc.GetString(status.Enabled + ? "admin-ui-baby-jail-enabled" + : "admin-ui-baby-jail-disabled" + ); + EnabledButton.ModulateSelfOverride = status.Enabled ? Color.Red : null; + ShowReasonButton.Pressed = status.ShowReason; + + MaxAccountAge.Text = status.MaxAccountAgeMinutes.ToString(); + _maxAccountAge = MaxAccountAge.Text; + + MaxOverallMinutes.Text = status.MaxOverallMinutes.ToString(); + _maxOverallMinutes = MaxOverallMinutes.Text; + } +} diff --git a/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs b/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs index 392a1a96de2..d36a91c3733 100644 --- a/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs +++ b/Content.Client/UserInterface/Systems/Admin/AdminUIController.cs @@ -3,6 +3,7 @@ using Content.Client.Administration.UI; using Content.Client.Administration.UI.Tabs.ObjectsTab; using Content.Client.Administration.UI.Tabs.PanicBunkerTab; +using Content.Client.Administration.UI.Tabs.BabyJailTab; using Content.Client.Administration.UI.Tabs.PlayerTab; using Content.Client.Gameplay; using Content.Client.Lobby; @@ -37,11 +38,13 @@ public sealed class AdminUIController : UIController, private AdminMenuWindow? _window; private MenuButton? AdminButton => UIManager.GetActiveUIWidgetOrNull()?.AdminButton; private PanicBunkerStatus? _panicBunker; + private BabyJailStatus? _babyJail; public override void Initialize() { base.Initialize(); SubscribeNetworkEvent(OnPanicBunkerUpdated); + SubscribeNetworkEvent(OnBabyJailUpdated); } private void OnPanicBunkerUpdated(PanicBunkerChangedEvent msg, EntitySessionEventArgs args) @@ -56,6 +59,18 @@ private void OnPanicBunkerUpdated(PanicBunkerChangedEvent msg, EntitySessionEven } } + private void OnBabyJailUpdated(BabyJailChangedEvent msg, EntitySessionEventArgs args) + { + var showDialog = _babyJail == null && msg.Status.Enabled; + _babyJail = msg.Status; + _window?.BabyJailControl.UpdateStatus(msg.Status); + + if (showDialog) + { + UIManager.CreateWindow().OpenCentered(); + } + } + public void OnStateEntered(GameplayState state) { EnsureWindow(); @@ -101,6 +116,13 @@ private void EnsureWindow() if (_panicBunker != null) _window.PanicBunkerControl.UpdateStatus(_panicBunker); + /* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + + if (_babyJail != null) + _window.BabyJailControl.UpdateStatus(_babyJail); + _window.PlayerTabControl.OnEntryKeyBindDown += PlayerTabEntryKeyBindDown; _window.ObjectsTabControl.OnEntryKeyBindDown += ObjectsTabEntryKeyBindDown; _window.OnOpen += OnWindowOpen; diff --git a/Content.Server.Database/Model.cs b/Content.Server.Database/Model.cs index bd5c4f5d7e8..dea8f9558ab 100644 --- a/Content.Server.Database/Model.cs +++ b/Content.Server.Database/Model.cs @@ -901,6 +901,10 @@ public enum ConnectionDenyReason : byte Whitelist = 1, Full = 2, Panic = 3, + /* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + BabyJail = 4, } public class ServerBanHit diff --git a/Content.Server/Administration/Commands/BabyJailCommand.cs b/Content.Server/Administration/Commands/BabyJailCommand.cs new file mode 100644 index 00000000000..058b67ca528 --- /dev/null +++ b/Content.Server/Administration/Commands/BabyJailCommand.cs @@ -0,0 +1,139 @@ +using Content.Shared.Administration; +using Content.Shared.CCVar; +using Robust.Shared.Configuration; +using Robust.Shared.Console; + +/* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + +namespace Content.Server.Administration.Commands; + +[AdminCommand(AdminFlags.Server)] +public sealed class BabyJailCommand : LocalizedCommands +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + + public override string Command => "babyjail"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + var toggle = Toggle(CCVars.BabyJailEnabled, shell, args, _cfg); + if (toggle == null) + return; + + shell.WriteLine(Loc.GetString(toggle.Value ? "babyjail-command-enabled" : "babyjail-command-disabled")); + } + + public static bool? Toggle(CVarDef cvar, IConsoleShell shell, string[] args, IConfigurationManager config) + { + if (args.Length > 1) + { + shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1))); + return null; + } + + var enabled = config.GetCVar(cvar); + + switch (args.Length) + { + case 0: + enabled = !enabled; + break; + case 1 when !bool.TryParse(args[0], out enabled): + shell.WriteError(Loc.GetString("shell-argument-must-be-boolean")); + return null; + } + + config.SetCVar(cvar, enabled); + + return enabled; + } +} + + +[AdminCommand(AdminFlags.Server)] +public sealed class BabyJailShowReasonCommand : LocalizedCommands +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + + public override string Command => "babyjail_show_reason"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + var toggle = BabyJailCommand.Toggle(CCVars.BabyJailShowReason, shell, args, _cfg); + if (toggle == null) + return; + + shell.WriteLine(Loc.GetString(toggle.Value + ? "babyjail-command-show-reason-enabled" + : "babyjail-command-show-reason-disabled" + )); + } +} + +[AdminCommand(AdminFlags.Server)] +public sealed class BabyJailMinAccountAgeCommand : LocalizedCommands +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + + public override string Command => "babyjail_max_account_age"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + switch (args.Length) + { + case 0: + { + var current = _cfg.GetCVar(CCVars.BabyJailMaxAccountAge); + shell.WriteLine(Loc.GetString("babyjail-command-max-account-age-is", ("minutes", current))); + break; + } + case > 1: + shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1))); + return; + } + + if (!int.TryParse(args[0], out var minutes)) + { + shell.WriteError(Loc.GetString("shell-argument-must-be-number")); + return; + } + + _cfg.SetCVar(CCVars.BabyJailMaxAccountAge, minutes); + shell.WriteLine(Loc.GetString("babyjail-command-max-account-age-set", ("minutes", minutes))); + } +} + +[AdminCommand(AdminFlags.Server)] +public sealed class BabyJailMinOverallHoursCommand : LocalizedCommands +{ + [Dependency] private readonly IConfigurationManager _cfg = default!; + + public override string Command => "babyjail_max_overall_minutes"; + + public override void Execute(IConsoleShell shell, string argStr, string[] args) + { + switch (args.Length) + { + case 0: + { + var current = _cfg.GetCVar(CCVars.BabyJailMaxOverallMinutes); + shell.WriteLine(Loc.GetString("babyjail-command-max-overall-minutes-is", ("minutes", current))); + break; + } + case > 1: + shell.WriteError(Loc.GetString("shell-need-between-arguments",("lower", 0), ("upper", 1))); + return; + } + + if (!int.TryParse(args[0], out var hours)) + { + shell.WriteError(Loc.GetString("shell-argument-must-be-number")); + return; + } + + _cfg.SetCVar(CCVars.BabyJailMaxOverallMinutes, hours); + shell.WriteLine(Loc.GetString("babyjail-command-overall-minutes-set", ("hours", hours))); + } +} diff --git a/Content.Server/Administration/Systems/AdminSystem.cs b/Content.Server/Administration/Systems/AdminSystem.cs index 6a59aebfd02..16c079e4ba1 100644 --- a/Content.Server/Administration/Systems/AdminSystem.cs +++ b/Content.Server/Administration/Systems/AdminSystem.cs @@ -61,6 +61,7 @@ public sealed class AdminSystem : EntitySystem private readonly HashSet _roundActivePlayers = new(); public readonly PanicBunkerStatus PanicBunker = new(); + public readonly BabyJailStatus BabyJail = new(); public override void Initialize() { @@ -70,6 +71,7 @@ public override void Initialize() _adminManager.OnPermsChanged += OnAdminPermsChanged; _playTime.SessionPlayTimeUpdated += OnSessionPlayTimeUpdated; + // Panic Bunker Settings Subs.CVar(_config, CCVars.PanicBunkerEnabled, OnPanicBunkerChanged, true); Subs.CVar(_config, CCVars.PanicBunkerDisableWithAdmins, OnPanicBunkerDisableWithAdminsChanged, true); Subs.CVar(_config, CCVars.PanicBunkerEnableWithoutAdmins, OnPanicBunkerEnableWithoutAdminsChanged, true); @@ -78,6 +80,16 @@ public override void Initialize() Subs.CVar(_config, CCVars.PanicBunkerMinAccountAge, OnPanicBunkerMinAccountAgeChanged, true); Subs.CVar(_config, CCVars.PanicBunkerMinOverallMinutes, OnPanicBunkerMinOverallMinutesChanged, true); + /* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + + // Baby Jail Settings + Subs.CVar(_config, CCVars.BabyJailEnabled, OnBabyJailChanged, true); + Subs.CVar(_config, CCVars.BabyJailShowReason, OnBabyJailShowReasonChanged, true); + Subs.CVar(_config, CCVars.BabyJailMaxAccountAge, OnBabyJailMaxAccountAgeChanged, true); + Subs.CVar(_config, CCVars.BabyJailMaxOverallMinutes, OnBabyJailMaxOverallMinutesChanged, true); + SubscribeLocalEvent(OnIdentityChanged); SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); @@ -250,6 +262,17 @@ private void OnPanicBunkerChanged(bool enabled) SendPanicBunkerStatusAll(); } + private void OnBabyJailChanged(bool enabled) + { + BabyJail.Enabled = enabled; + _chat.SendAdminAlert(Loc.GetString(enabled + ? "admin-ui-baby-jail-enabled-admin-alert" + : "admin-ui-baby-jail-disabled-admin-alert" + )); + + SendBabyJailStatusAll(); + } + private void OnPanicBunkerDisableWithAdminsChanged(bool enabled) { PanicBunker.DisableWithAdmins = enabled; @@ -274,18 +297,36 @@ private void OnPanicBunkerShowReasonChanged(bool enabled) SendPanicBunkerStatusAll(); } + private void OnBabyJailShowReasonChanged(bool enabled) + { + BabyJail.ShowReason = enabled; + SendBabyJailStatusAll(); + } + private void OnPanicBunkerMinAccountAgeChanged(int minutes) { PanicBunker.MinAccountAgeMinutes = minutes; SendPanicBunkerStatusAll(); } + private void OnBabyJailMaxAccountAgeChanged(int minutes) + { + BabyJail.MaxAccountAgeMinutes = minutes; + SendBabyJailStatusAll(); + } + private void OnPanicBunkerMinOverallMinutesChanged(int minutes) { PanicBunker.MinOverallMinutes = minutes; SendPanicBunkerStatusAll(); } + private void OnBabyJailMaxOverallMinutesChanged(int minutes) + { + BabyJail.MaxOverallMinutes = minutes; + SendBabyJailStatusAll(); + } + private void UpdatePanicBunker() { var admins = PanicBunker.CountDeadminnedAdmins @@ -327,6 +368,15 @@ private void SendPanicBunkerStatusAll() } } + private void SendBabyJailStatusAll() + { + var ev = new BabyJailChangedEvent(BabyJail); + foreach (var admin in _adminManager.AllAdmins) + { + RaiseNetworkEvent(ev, admin); + } + } + /// /// Erases a player from the round. /// This removes them and any trace of them from the round, deleting their diff --git a/Content.Server/Connection/ConnectionManager.cs b/Content.Server/Connection/ConnectionManager.cs index 42732ff1b1c..cf7581aa4e6 100644 --- a/Content.Server/Connection/ConnectionManager.cs +++ b/Content.Server/Connection/ConnectionManager.cs @@ -17,6 +17,9 @@ using Robust.Shared.Player; using Robust.Shared.Timing; +/* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ namespace Content.Server.Connection { @@ -248,6 +251,14 @@ session.Status is SessionStatus.Connected or SessionStatus.InGame } } + if (_cfg.GetCVar(CCVars.BabyJailEnabled) && adminData == null) + { + var result = await IsInvalidConnectionDueToBabyJail(userId, e); + + if (result.IsInvalid) + return (ConnectionDenyReason.BabyJail, result.Reason, null); + } + var wasInGame = EntitySystem.TryGet(out var ticker) && ticker.PlayerGameStatuses.TryGetValue(userId, out var status) && status == PlayerGameStatus.JoinedGame; @@ -277,6 +288,61 @@ session.Status is SessionStatus.Connected or SessionStatus.InGame return null; } + private async Task<(bool IsInvalid, string Reason)> IsInvalidConnectionDueToBabyJail(NetUserId userId, NetConnectingArgs e) + { + // If you're whitelisted then bypass this whole thing + if (await _db.GetWhitelistStatusAsync(userId)) + return (false, ""); + + // Initial cvar retrieval + var showReason = _cfg.GetCVar(CCVars.BabyJailShowReason); + var reason = _cfg.GetCVar(CCVars.BabyJailCustomReason); + var maxAccountAgeMinutes = _cfg.GetCVar(CCVars.BabyJailMaxAccountAge); + var maxPlaytimeMinutes = _cfg.GetCVar(CCVars.BabyJailMaxOverallMinutes); + + // Wait some time to lookup data + var record = await _dbManager.GetPlayerRecordByUserId(userId); + + // No player record = new account or the DB is having a skill issue + if (record == null) + return (false, ""); + + var isAccountAgeInvalid = record.FirstSeenTime.CompareTo(DateTimeOffset.Now - TimeSpan.FromMinutes(maxAccountAgeMinutes)) <= 0; + if (isAccountAgeInvalid && showReason) + { + var locAccountReason = reason != string.Empty + ? reason + : Loc.GetString("baby-jail-account-denied-reason", + ("reason", + Loc.GetString( + "baby-jail-account-reason-account", + ("minutes", maxAccountAgeMinutes)))); + + return (true, locAccountReason); + } + + var overallTime = ( await _db.GetPlayTimes(e.UserId)).Find(p => p.Tracker == PlayTimeTrackingShared.TrackerOverall); + var isTotalPlaytimeInvalid = overallTime == null || overallTime.TimeSpent.TotalMinutes >= maxPlaytimeMinutes; + + if (isTotalPlaytimeInvalid && showReason) + { + var locPlaytimeReason = reason != string.Empty + ? reason + : Loc.GetString("baby-jail-account-denied-reason", + ("reason", + Loc.GetString( + "baby-jail-account-reason-overall", + ("minutes", maxPlaytimeMinutes)))); + + return (true, locPlaytimeReason); + } + + if (!showReason && isTotalPlaytimeInvalid || isAccountAgeInvalid) + return (true, Loc.GetString("baby-jail-account-denied")); + + return (false, ""); + } + private bool HasTemporaryBypass(NetUserId user) { return _temporaryBypasses.TryGetValue(user, out var time) && time > _gameTiming.RealTime; diff --git a/Content.Server/GameTicking/GameTicker.StatusShell.cs b/Content.Server/GameTicking/GameTicker.StatusShell.cs index fcf5b1c25cd..67367b94b7f 100644 --- a/Content.Server/GameTicking/GameTicker.StatusShell.cs +++ b/Content.Server/GameTicking/GameTicker.StatusShell.cs @@ -46,6 +46,12 @@ private void GetStatusResponse(JsonNode jObject) jObject["players"] = _playerManager.PlayerCount; jObject["soft_max_players"] = _cfg.GetCVar(CCVars.SoftMaxPlayers); jObject["panic_bunker"] = _cfg.GetCVar(CCVars.PanicBunkerEnabled); + + /* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + + jObject["baby_jail"] = _cfg.GetCVar(CCVars.BabyJailEnabled); jObject["run_level"] = (int) _runLevel; if (preset != null) jObject["preset"] = Loc.GetString(preset.ModeTitle); diff --git a/Content.Shared/Administration/Events/BabyJailChangedEvent.cs b/Content.Shared/Administration/Events/BabyJailChangedEvent.cs new file mode 100644 index 00000000000..56d5ce51626 --- /dev/null +++ b/Content.Shared/Administration/Events/BabyJailChangedEvent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.Serialization; + +/* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + +namespace Content.Shared.Administration.Events; + +[Serializable, NetSerializable] +public sealed class BabyJailStatus +{ + public bool Enabled; + public bool ShowReason; + public int MaxAccountAgeMinutes; + public int MaxOverallMinutes; +} + +[Serializable, NetSerializable] +public sealed class BabyJailChangedEvent(BabyJailStatus status) : EntityEventArgs +{ + public BabyJailStatus Status = status; +} diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 8c70631b945..a0e9157e922 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -332,6 +332,48 @@ public static readonly CVarDef public static readonly CVarDef BypassBunkerWhitelist = CVarDef.Create("game.panic_bunker.whitelisted_can_bypass", true, CVar.SERVERONLY); + /* + * TODO: Remove baby jail code once a more mature gateway process is established. This code is only being issued as a stopgap to help with potential tiding in the immediate future. + */ + + /// + /// Whether the baby jail is currently enabled. + /// + public static readonly CVarDef BabyJailEnabled = + CVarDef.Create("game.baby_jail.enabled", false, CVar.NOTIFY | CVar.REPLICATED | CVar.SERVER); + + /// + /// Show reason of disconnect for user or not. + /// + public static readonly CVarDef BabyJailShowReason = + CVarDef.Create("game.baby_jail.show_reason", false, CVar.SERVERONLY); + + /// + /// Maximum age of the account (from server's PoV, so from first-seen date) in minutes that can access baby + /// jailed servers. + /// + public static readonly CVarDef BabyJailMaxAccountAge = + CVarDef.Create("game.baby_jail.max_account_age", 1440, CVar.SERVERONLY); + + /// + /// Maximum overall played time allowed to access baby jailed servers. + /// + public static readonly CVarDef BabyJailMaxOverallMinutes = + CVarDef.Create("game.baby_jail.max_overall_minutes", 120, CVar.SERVERONLY); + + /// + /// A custom message that will be used for connections denied due to the baby jail. + /// If not empty, then will overwrite + /// + public static readonly CVarDef BabyJailCustomReason = + CVarDef.Create("game.baby_jail.custom_reason", string.Empty, CVar.SERVERONLY); + + /// + /// Allow bypassing the baby jail if the user is whitelisted. + /// + public static readonly CVarDef BypassBabyJailWhitelist = + CVarDef.Create("game.baby_jail.whitelisted_can_bypass", true, CVar.SERVERONLY); + /// /// Make people bonk when trying to climb certain objects like tables. /// diff --git a/Resources/ConfigPresets/WizardsDen/wizardsDenGateway.toml b/Resources/ConfigPresets/WizardsDen/wizardsDenGateway.toml index 78732123260..b0f955bddec 100644 --- a/Resources/ConfigPresets/WizardsDen/wizardsDenGateway.toml +++ b/Resources/ConfigPresets/WizardsDen/wizardsDenGateway.toml @@ -11,6 +11,12 @@ panic_bunker.enabled = false panic_bunker.disable_with_admins = false panic_bunker.enable_without_admins = false panic_bunker.custom_reason = "" +baby_jail.enabled = true +baby_jail.show_reason = true +baby_jail.max_account_age = 5256000 # 10 years. Disabling this check specifically isn't currently supported +baby_jail.max_overall_minutes = 3000 # 50 hours +baby_jail.custom_reason = "Sorry! Only new players can join the servers, try joining another one instead!" +baby_jail.whitelisted_can_bypass = true [hub] tags = "lang:en,region:am_n_e,rp:low" diff --git a/Resources/ConfigPresets/WizardsDen/wizardsDenLRPTide.toml b/Resources/ConfigPresets/WizardsDen/wizardsDenLRPTide.toml index d1cbfb9c905..21abbf2e3f0 100644 --- a/Resources/ConfigPresets/WizardsDen/wizardsDenLRPTide.toml +++ b/Resources/ConfigPresets/WizardsDen/wizardsDenLRPTide.toml @@ -8,6 +8,10 @@ soft_max_players = 50 hostname = "[EN] Wizard's Den Granite Flea [US East]" desc = "Official English Space Station 14 servers. Vanilla, roleplay ruleset." +panic_bunker.enabled = false +panic_bunker.disable_with_admins = false +panic_bunker.enable_without_admins = false +panic_bunker.custom_reason = "" [hub] tags = "lang:en,region:am_n_e,rp:low" diff --git a/Resources/Locale/en-US/administration/commands/babyjail.ftl b/Resources/Locale/en-US/administration/commands/babyjail.ftl new file mode 100644 index 00000000000..5a9d9490517 --- /dev/null +++ b/Resources/Locale/en-US/administration/commands/babyjail.ftl @@ -0,0 +1,19 @@ +cmd-babyjail-desc = Toggles the baby jail, which enables stricter restrictions on who's allowed to join the server. +cmd-babyjail-help = Usage: babyjail +babyjail-command-enabled = Baby jail has been enabled. +babyjail-command-disabled = Baby jail has been disabled. + +cmd-babyjail_show_reason-desc = Toggles whether or not to show connecting clients the reason why the baby jail blocked them from joining. +cmd-babyjail_show_reason-help = Usage: babyjail_show_reason +babyjail-command-show-reason-enabled = The baby jail will now show a reason to users it blocks from connecting. +babyjail-command-show-reason-disabled = The baby jail will no longer show a reason to users it blocks from connecting. + +cmd-babyjail_max_account_age-desc = Gets or sets the maximum account age in minutes that an account can have to be allowed to connect with the baby jail enabled. +cmd-babyjail_max_account_age-help = Usage: babyjail_max_account_age +babyjail-command-max-account-age-is = The maximum account age for the baby jail is {$minutes} minutes. +babyjail-command-max-account-age-set = Set the maximum account age for the baby jail to {$minutes} minutes. + +cmd-babyjail_max_overall_minutes-desc = Gets or sets the maximum overall playtime in minutes that an account can have to be allowed to connect with the baby jail enabled. +cmd-babyjail_max_overall_minutes-help = Usage: babyjail_max_overall_minutes +babyjail-command-max-overall-minutes-is = The maximum overall playtime for the baby jail is {$minutes} minutes. +babyjail-command-max-overall-minutes-set = Set the maximum overall playtime for the baby jail to {$minutes} minutes. diff --git a/Resources/Locale/en-US/administration/ui/admin-menu-window.ftl b/Resources/Locale/en-US/administration/ui/admin-menu-window.ftl index c759e4c2cb1..03b2046a9e1 100644 --- a/Resources/Locale/en-US/administration/ui/admin-menu-window.ftl +++ b/Resources/Locale/en-US/administration/ui/admin-menu-window.ftl @@ -7,5 +7,6 @@ admin-menu-atmos-tab = Atmos admin-menu-round-tab = Round admin-menu-server-tab = Server admin-menu-panic-bunker-tab = Panic Bunker +admin-menu-baby-jail-tab = Baby Jail admin-menu-players-tab = Players admin-menu-objects-tab = Objects diff --git a/Resources/Locale/en-US/administration/ui/tabs/babyjail-tab.ftl b/Resources/Locale/en-US/administration/ui/tabs/babyjail-tab.ftl new file mode 100644 index 00000000000..46dce54c1fc --- /dev/null +++ b/Resources/Locale/en-US/administration/ui/tabs/babyjail-tab.ftl @@ -0,0 +1,16 @@ +admin-ui-baby-jail-window-title = Baby Jail + +admin-ui-baby-jail-enabled = Baby Jail Enabled +admin-ui-baby-jail-disabled = Baby Jail Disabled +admin-ui-baby-jail-tooltip = The baby jail restricts players from joining if their account is too old or they do have too much overall playtime on this server. + +admin-ui-baby-jail-show-reason = Show Reason +admin-ui-baby-jail-show-reason-tooltip = Show the user why they were blocked from connecting by the baby jail. + +admin-ui-baby-jail-max-account-age = Max. Account Age +admin-ui-baby-jail-max-overall-minutes = Max. Overall Playtime + +admin-ui-baby-jail-is-enabled = [font size=20][bold]The baby jail is currently enabled.[/bold][/font] + +admin-ui-baby-jail-enabled-admin-alert = The baby jail has been enabled. +admin-ui-baby-jail-disabled-admin-alert = The baby jail has been disabled. diff --git a/Resources/Locale/en-US/connection-messages.ftl b/Resources/Locale/en-US/connection-messages.ftl index 65796b9c795..f1596d90152 100644 --- a/Resources/Locale/en-US/connection-messages.ftl +++ b/Resources/Locale/en-US/connection-messages.ftl @@ -40,3 +40,8 @@ panic-bunker-account-denied-reason = This server is in panic bunker mode, often panic-bunker-account-reason-account = Your Space Station 14 account is too new. It must be older than {$minutes} minutes panic-bunker-account-reason-overall = Your overall playtime on the server must be greater than {$minutes} $minutes +baby-jail-account-denied = This server is a newbie server, intended for new players and those who want to help them. New connections by accounts that are too old or are not on a whitelist are not accepted. Check out some other servers and see everything Space Station 14 has to offer. Have fun! +baby-jail-account-denied-reason = This server is a newbie server, intended for new players and those who want to help them. New connections by accounts that are too old or are not on a whitelist are not accepted. Check out some other servers and see everything Space Station 14 has to offer. Have fun! Reason: "{$reason}" +baby-jail-account-reason-account = Your Space Station 14 account is too old. It must be younger than {$minutes} minutes +baby-jail-account-reason-overall = Your overall playtime on the server must be younger than {$minutes} $minutes + From 19a06b6cc0498fa59ffe7328df0f524f753968f4 Mon Sep 17 00:00:00 2001 From: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Date: Thu, 11 Jul 2024 00:14:49 -0500 Subject: [PATCH 04/78] Fix the ability to shoot out of crates (#28961) * Fix the ability to shoot out of crates * Makes it check what inventory the player is in * use IsEntityOrParentInContainer * Fix Issues Github had * gaahhh... Prevents lasers from being shot out of crates * gaahhh... Prevents lasers from being shot out of crates * Fix laser? * hmmm... this is better looking I think? * Uncook indentation * Rerun tests? --- .../Weapons/Ranged/Systems/GunSystem.cs | 22 ++++++++++++------- .../Systems/RequireProjectileTargetSystem.cs | 13 +++++++++-- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 7f7c7ba8557..29f91988182 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -24,6 +24,7 @@ using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Utility; +using Robust.Shared.Containers; namespace Content.Server.Weapons.Ranged.Systems; @@ -38,6 +39,7 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly StunSystem _stun = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; private const float DamagePitchVariation = 0.05f; public const float GunClumsyChance = 0.5f; @@ -204,17 +206,21 @@ public override void Shoot(EntityUid gunUid, GunComponent gun, List<(EntityUid? var result = rayCastResults[0]; - // Checks if the laser should pass over unless targeted by its user - foreach (var collide in rayCastResults) + // Check if laser is shot from in a container + if (!_container.IsEntityOrParentInContainer(lastUser)) { - if (collide.HitEntity != gun.Target && - CompOrNull(collide.HitEntity)?.Active == true) + // Checks if the laser should pass over unless targeted by its user + foreach (var collide in rayCastResults) { - continue; + if (collide.HitEntity != gun.Target && + CompOrNull(collide.HitEntity)?.Active == true) + { + continue; + } + + result = collide; + break; } - - result = collide; - break; } var hit = result.HitEntity; diff --git a/Content.Shared/Damage/Systems/RequireProjectileTargetSystem.cs b/Content.Shared/Damage/Systems/RequireProjectileTargetSystem.cs index 79b374a60f3..12838eb04d9 100644 --- a/Content.Shared/Damage/Systems/RequireProjectileTargetSystem.cs +++ b/Content.Shared/Damage/Systems/RequireProjectileTargetSystem.cs @@ -2,11 +2,14 @@ using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Standing; using Robust.Shared.Physics.Events; +using Robust.Shared.Containers; namespace Content.Shared.Damage.Components; public sealed class RequireProjectileTargetSystem : EntitySystem { + [Dependency] private readonly SharedContainerSystem _container = default!; + public override void Initialize() { SubscribeLocalEvent(PreventCollide); @@ -23,10 +26,16 @@ private void PreventCollide(Entity ent, ref Pr return; var other = args.OtherEntity; - if (HasComp(other) && + if (TryComp(other, out ProjectileComponent? projectile) && CompOrNull(other)?.Target != ent) { - args.Cancelled = true; + // Prevents shooting out of while inside of crates + var shooter = projectile.Shooter; + if (!shooter.HasValue) + return; + + if (!_container.IsEntityOrParentInContainer(shooter.Value)) + args.Cancelled = true; } } From 3ebfe468cafdac8eb9dcfc4ad9105270d29be17e Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 11 Jul 2024 05:15:55 +0000 Subject: [PATCH 05/78] Automatic changelog update --- Resources/Changelog/Admin.yml | 8 ++++++++ Resources/Changelog/Changelog.yml | 15 +++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Admin.yml b/Resources/Changelog/Admin.yml index 1d270f3c0ee..56b708a79e3 100644 --- a/Resources/Changelog/Admin.yml +++ b/Resources/Changelog/Admin.yml @@ -359,5 +359,13 @@ Entries: id: 44 time: '2024-06-30T12:26:41.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29582 +- author: Chief-Engineer + changes: + - message: Baby jail no longer prevents accounts with no prior connections from + connecting + type: Fix + id: 45 + time: '2024-07-11T05:14:01.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29896 Name: Admin Order: 1 diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ce042e5e136..c1589a65671 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: UBlueberry - changes: - - message: Changed the guidebook entry for the Space Ninja, bringing it up to the - same quality as the other antagonist entries. - type: Tweak - id: 6401 - time: '2024-04-20T06:10:22.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26650 - author: Vermidia changes: - message: You can now bless many more containers with a bible. @@ -3826,3 +3818,10 @@ id: 6900 time: '2024-07-11T04:48:00.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29703 +- author: Cojoke-dot + changes: + - message: You can no longer shoot out of crates with guns + type: Fix + id: 6901 + time: '2024-07-11T05:14:49.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/28961 From eae04129cfd3083847bbc4ce16861ca5aacc7f45 Mon Sep 17 00:00:00 2001 From: Cojoke <83733158+Cojoke-dot@users.noreply.github.com> Date: Thu, 11 Jul 2024 00:33:20 -0500 Subject: [PATCH 06/78] Glass Airlocks In Spray Painter (#29869) --- .../Prototypes/Entities/Structures/Doors/airlock_groups.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml b/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml index 09ce1a05d94..4226c39f6b4 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/airlock_groups.yml @@ -24,6 +24,7 @@ science: Structures/Doors/Airlocks/Glass/science.rsi cargo: Structures/Doors/Airlocks/Glass/cargo.rsi engineering: Structures/Doors/Airlocks/Glass/engineering.rsi + glass: Structures/Doors/Airlocks/Glass/glass.rsi maintenance: Structures/Doors/Airlocks/Glass/maint.rsi medical: Structures/Doors/Airlocks/Glass/medical.rsi security: Structures/Doors/Airlocks/Glass/security.rsi @@ -69,6 +70,7 @@ command: Command engineering: Engineering freezer: Civilian + glass: Civilian maintenance: Civilian medical: Medical science: Science From 1ea7e3e71fc2e9c1ee87f95f687d4054decb48d7 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 11 Jul 2024 05:34:27 +0000 Subject: [PATCH 07/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index c1589a65671..3f6bf5cadea 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Vermidia - changes: - - message: You can now bless many more containers with a bible. - type: Tweak - id: 6402 - time: '2024-04-20T06:16:55.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26526 - author: SoulFN changes: - message: Various types of glass shards now causes diffrent damage @@ -3825,3 +3818,11 @@ id: 6901 time: '2024-07-11T05:14:49.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/28961 +- author: Cojoke-dot + changes: + - message: The Spray Painter can now be used to paint glass airlocks to look like + regular glass airlocks. + type: Tweak + id: 6902 + time: '2024-07-11T05:33:20.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29869 From 1a50760e674134de6065bff3bc76739526ea5429 Mon Sep 17 00:00:00 2001 From: MFMessage <22904993+MFMessage@users.noreply.github.com> Date: Thu, 11 Jul 2024 00:53:14 -0500 Subject: [PATCH 08/78] Picking a ghostrole as an admin will now deadmin you. (#29790) * @forcibly deadmins you * Added checks for AdminDeadminOnJoin --- Content.Server/Ghost/Roles/GhostRoleSystem.cs | 13 +++++++++++++ Content.Shared/CCVar/CCVars.cs | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index b6627f11540..dfb0c8e0865 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -31,6 +31,9 @@ using Content.Server.Popups; using Content.Shared.Verbs; using Robust.Shared.Collections; +using Content.Server.Administration.Managers; +using Content.Shared.CCVar; +using Robust.Shared.Configuration; namespace Content.Server.Ghost.Roles { @@ -48,6 +51,8 @@ public sealed class GhostRoleSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly IConfigurationManager _cfg = default!; private uint _nextRoleIdentifier; private bool _needsUpdateGhostRoleCount = true; @@ -587,6 +592,14 @@ private void OnPlayerAttached(PlayerAttachedEvent message) // forced into a ghost role. LeaveAllRaffles(message.Player); CloseEui(message.Player); + + // The player is no longer a ghost, so they should not be adminned anymore. Deadmin them. + // Ensures that admins do not forget to deadmin themselves upon entering a ghost role. + var autoDeAdmin = _cfg.GetCVar(CCVars.AdminDeadminOnJoin); + if (autoDeAdmin && _adminManager.IsAdmin(message.Entity)) + { + _adminManager.DeAdmin(message.Player); + } } private void OnMindAdded(EntityUid uid, GhostTakeoverAvailableComponent component, MindAddedMessage args) diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index a0e9157e922..044a95c4583 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -870,7 +870,7 @@ public static readonly CVarDef CVarDef.Create("admin.show_pii_onban", false, CVar.SERVERONLY); /// - /// If an admin joins a round by reading up or using the late join button, automatically + /// If an admin joins a round by readying up or using the late join button, automatically /// de-admin them. /// public static readonly CVarDef AdminDeadminOnJoin = From e45f55e36d67ace2c2c5bc723082255f2ed909c4 Mon Sep 17 00:00:00 2001 From: PJBot Date: Thu, 11 Jul 2024 05:54:21 +0000 Subject: [PATCH 09/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3f6bf5cadea..edf342f5aa5 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: SoulFN - changes: - - message: Various types of glass shards now causes diffrent damage - type: Tweak - id: 6403 - time: '2024-04-20T06:21:13.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26783 - author: Vermidia changes: - message: Most medical roles now can choose to start with nitrile or latex gloves, @@ -3826,3 +3819,10 @@ id: 6902 time: '2024-07-11T05:33:20.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29869 +- author: MFMessage + changes: + - message: Picking a ghost role as an admin will now deadmin. + type: Fix + id: 6903 + time: '2024-07-11T05:53:15.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29790 From 02636386b5140bdeae7583f2a3eb1e17166e5db3 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Thu, 11 Jul 2024 05:55:56 +0000 Subject: [PATCH 10/78] item toggling giga rework + full ninja refactor (#28039) * item toggle refactoring and some new systems * add ToggleClothing component/system * unhardcode magboots gravity logic * make magboots and speedboots use ItemToggle and stuff * remove now useless clothing components * update client/server magboots systems * add note to use ItemToggledEvent in ToggleActionEvent doc * refactor PowerCellDraw to use ItemToggle for ui open/close control * add TryUseCharges, refactor charges system * update magboot trigger code * make borg use ItemToggle, network SelectedModule instead of now removed Activated * add AccessToggle for borg * the giga ninja refactor * update ninja yml * update ItemToggle usage for some stuff * fix activatableui requires power * random fixing * yaml fixing * nuke ItemToggleDisarmMalus * make defib use ItemToggle * make things that use power not turn on if missing use charge * pro * fix sound prediction * bruh * proximity detector use ItemToggle * oop * big idiot syndrome * fix ninja spawn rule and make it generic * fix ninja spawn rule yml * move loading profiles into AntagLoadProfileRule * more ninja refactor * ninja yml fixes * the dreaded copy paste ops * remove useless NinjaRuleComponent and ue AntagSelection for greeting * fix invisibility * move IsCompleted to SharedObjectivesSystem * ability fixes * oop fix powercell instantly draining itself * sentient speedboots gaming * make reflect use ItemToggle * fix other test * loadprofilerule moved into its own pr * remove conflict with dragon refactor * remove all GenericAntag code from ninja * ) * probably * remove old enabled * great language bravo vince * GREAT LANGUAGE * who made this language * because it stinks * reparent blood-red magboots to magboots probbbly works * most of the review stuff * hasGrav doesnt mean what i thought it did * make health analyzer use itemtoggle, not fail test * fix mag/speed boots being wacky * UNTROLL * add ItemToggle to the random health analyzers * a * remove unused obsolete borg func * untrolling * :trollface: * fix test * fix * g * untroll --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Items/Systems/ItemToggleSystem.cs | 9 - .../Ninja/Systems/ItemCreatorSystem.cs | 5 + .../Ninja/Systems/NinjaGlovesSystem.cs | 7 +- .../Ninja/Systems/NinjaSuitSystem.cs | 21 +-- Content.Client/Ninja/Systems/NinjaSystem.cs | 9 +- .../Ninja/Systems/SpiderChargeSystem.cs | 5 + .../Interaction/InteractionTest.Helpers.cs | 2 +- .../Tests/Interaction/InteractionTest.cs | 4 +- .../Components/AutoRechargeComponent.cs | 1 + .../Charges/Systems/ChargesSystem.cs | 12 +- .../Rules/Components/NinjaRuleComponent.cs | 27 --- .../ItemToggleDisarmMalusComponent.cs | 22 --- .../Components/ItemToggleSharpComponent.cs | 9 - .../Item/ItemToggle/ItemToggleSystem.cs | 44 ----- .../Components/HealthAnalyzerComponent.cs | 3 + Content.Server/Medical/DefibrillatorSystem.cs | 68 +------ .../Medical/HealthAnalyzerSystem.cs | 21 ++- .../Ninja/Events/BatteryChangedEvent.cs | 2 +- .../Ninja/Systems/BatteryDrainerSystem.cs | 21 ++- .../Ninja/Systems/ItemCreatorSystem.cs | 57 ++++++ .../Ninja/Systems/NinjaGlovesSystem.cs | 102 +++-------- .../Ninja/Systems/NinjaSuitSystem.cs | 79 ++------- .../Ninja/Systems/SpaceNinjaSystem.cs | 80 +-------- .../Ninja/Systems/SpiderChargeSystem.cs | 3 +- .../Ninja/Systems/StunProviderSystem.cs | 24 +-- .../Objectives/Systems/CodeConditionSystem.cs | 14 -- .../PowerCell/PowerCellSystem.Draw.cs | 38 ++-- Content.Server/PowerCell/PowerCellSystem.cs | 2 +- .../Silicons/Borgs/BorgSystem.Modules.cs | 4 +- Content.Server/Silicons/Borgs/BorgSystem.cs | 79 +++------ .../Components/NinjaSpawnRuleComponent.cs | 16 -- .../Components/SpaceSpawnRuleComponent.cs | 25 +++ .../{NinjaSpawnRule.cs => SpaceSpawnRule.cs} | 38 ++-- .../Stunnable/Systems/StunbatonSystem.cs | 2 +- .../Weapons/Misc/TetherGunSystem.cs | 6 +- .../Systems/ArtifactMagnetTriggerSystem.cs | 9 +- .../Components/AccessToggleComponent.cs | 11 ++ .../Access/Systems/AccessToggleSystem.cs | 21 +++ .../Beeper/Components/BeeperComponent.cs | 11 +- Content.Shared/Beeper/Systems/BeeperSystem.cs | 37 ++-- .../Beeper/Systems/ProximityBeeperSystem.cs | 84 +-------- .../Charges/Systems/SharedChargesSystem.cs | 33 +++- .../ClothingSpeedModifierComponent.cs | 19 +- .../Clothing/ClothingSpeedModifierSystem.cs | 107 ++--------- .../Components/StealthClothingComponent.cs | 43 ----- .../Components/ToggleClothingComponent.cs | 40 +++++ .../ToggleClothingSpeedComponent.cs | 35 ---- .../EntitySystems/StealthClothingSystem.cs | 144 --------------- .../EntitySystems/ToggleClothingSystem.cs | 58 ++++++ Content.Shared/Clothing/MagbootsComponent.cs | 18 +- Content.Shared/Clothing/MagbootsSystem.cs | 90 ++++++++++ .../Clothing/SharedMagbootsSystem.cs | 160 ----------------- .../Item/ItemToggle/ComponentTogglerSystem.cs | 26 +++ .../Components/ComponentTogglerComponent.cs | 32 ++++ .../ItemToggleActiveSoundComponent.cs | 4 +- .../Components/ItemToggleComponent.cs | 9 +- .../Components/ToggleVerbComponent.cs | 18 ++ ...temToggleSystem.cs => ItemToggleSystem.cs} | 167 ++++++++++-------- .../Item/ItemToggle/ToggleVerbSystem.cs | 34 ++++ .../Medical/DefibrillatorComponent.cs | 16 +- .../Components/BatteryDrainerComponent.cs | 12 +- .../Components/BombingTargetComponent.cs | 4 +- .../Ninja/Components/DashAbilityComponent.cs | 14 +- .../Ninja/Components/EmagProviderComponent.cs | 13 +- .../Ninja/Components/EnergyKatanaComponent.cs | 4 +- .../Ninja/Components/ItemCreatorComponent.cs | 52 ++++++ .../Ninja/Components/NinjaGlovesComponent.cs | 44 +++-- .../Ninja/Components/NinjaSuitComponent.cs | 87 ++------- .../Ninja/Components/SpaceNinjaComponent.cs | 19 +- .../Ninja/Components/SpiderChargeComponent.cs | 9 +- .../Ninja/Components/StunProviderComponent.cs | 38 ++-- .../Ninja/Systems/DashAbilitySystem.cs | 80 +++------ .../Ninja/Systems/EmagProviderSystem.cs | 28 +-- .../Ninja/Systems/EnergyKatanaSystem.cs | 25 +-- .../Ninja/Systems/ItemCreatorSystem.cs | 56 ++++++ .../Systems/SharedBatteryDrainerSystem.cs | 25 ++- .../Ninja/Systems/SharedNinjaGlovesSystem.cs | 108 ++++++----- .../Ninja/Systems/SharedNinjaSuitSystem.cs | 154 +++++++++------- .../Ninja/Systems/SharedSpaceNinjaSystem.cs | 59 ++++--- .../Ninja/Systems/SharedSpiderChargeSystem.cs | 6 + .../Ninja/Systems/SharedStunProviderSystem.cs | 18 +- .../Systems/SharedObjectivesSystem.cs | 35 +++- .../Pinpointer/SharedProximityBeeper.cs | 9 - .../PowerCell/PowerCellDrawComponent.cs | 18 +- .../PowerCell/SharedPowerCellSystem.cs | 27 ++- .../Components/ProximityDetectorComponent.cs | 8 +- .../Systems/ProximityDetectionSystem.cs | 86 +++++---- .../Borgs/Components/BorgChassisComponent.cs | 8 +- .../Silicons/Borgs/SharedBorgSystem.cs | 4 +- .../Toggleable/ToggleActionEvent.cs | 7 +- .../Tools/Systems/SharedToolSystem.cs | 2 +- .../ActivatableUISystem.Power.cs | 24 ++- .../Weapons/Reflect/ReflectComponent.cs | 7 +- .../Weapons/Reflect/ReflectSystem.cs | 14 +- Resources/Prototypes/Actions/ninja.yml | 10 +- .../Entities/Clothing/Hands/gloves.yml | 26 ++- .../Entities/Clothing/Head/misc.yml | 30 ++++ .../Entities/Clothing/OuterClothing/suits.yml | 26 ++- .../Entities/Clothing/Shoes/magboots.yml | 108 ++++------- .../Entities/Clothing/Shoes/misc.yml | 13 +- .../Entities/Clothing/base_clothing.yml | 9 + .../Entities/Markers/Spawners/ghost_roles.yml | 7 +- .../Mobs/Cyborgs/base_borg_chassis.yml | 5 + .../Prototypes/Entities/Mobs/Player/human.yml | 25 --- .../Prototypes/Entities/Mobs/Species/base.yml | 3 - .../Objects/Devices/base_handheld.yml | 11 ++ .../Entities/Objects/Devices/pda.yml | 3 + .../Entities/Objects/Devices/station_map.yml | 11 +- .../Entities/Objects/Shields/shields.yml | 13 +- .../Objects/Specific/Medical/defib.yml | 5 + .../Medical/handheld_crew_monitor.yml | 8 +- .../Specific/Medical/healthanalyzer.yml | 2 + .../Objects/Specific/Research/anomaly.yml | 9 +- .../Objects/Tools/handheld_mass_scanner.yml | 3 +- .../Entities/Objects/Tools/welders.yml | 6 +- .../Weapons/Guns/Launchers/launchers.yml | 4 + .../Objects/Weapons/Melee/e_sword.yml | 32 ++-- .../Entities/Objects/Weapons/Melee/sword.yml | 2 - .../Structures/Machines/Medical/cryo_pod.yml | 1 + Resources/Prototypes/GameRules/events.yml | 38 +++- Resources/Prototypes/GameRules/midround.yml | 18 -- 121 files changed, 1617 insertions(+), 2002 deletions(-) delete mode 100644 Content.Client/Items/Systems/ItemToggleSystem.cs create mode 100644 Content.Client/Ninja/Systems/ItemCreatorSystem.cs create mode 100644 Content.Client/Ninja/Systems/SpiderChargeSystem.cs delete mode 100644 Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs delete mode 100644 Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs delete mode 100644 Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs delete mode 100644 Content.Server/Item/ItemToggle/ItemToggleSystem.cs create mode 100644 Content.Server/Ninja/Systems/ItemCreatorSystem.cs delete mode 100644 Content.Server/StationEvents/Components/NinjaSpawnRuleComponent.cs create mode 100644 Content.Server/StationEvents/Components/SpaceSpawnRuleComponent.cs rename Content.Server/StationEvents/Events/{NinjaSpawnRule.cs => SpaceSpawnRule.cs} (53%) create mode 100644 Content.Shared/Access/Components/AccessToggleComponent.cs create mode 100644 Content.Shared/Access/Systems/AccessToggleSystem.cs delete mode 100644 Content.Shared/Clothing/Components/StealthClothingComponent.cs create mode 100644 Content.Shared/Clothing/Components/ToggleClothingComponent.cs delete mode 100644 Content.Shared/Clothing/Components/ToggleClothingSpeedComponent.cs delete mode 100644 Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs create mode 100644 Content.Shared/Clothing/EntitySystems/ToggleClothingSystem.cs create mode 100644 Content.Shared/Clothing/MagbootsSystem.cs delete mode 100644 Content.Shared/Clothing/SharedMagbootsSystem.cs create mode 100644 Content.Shared/Item/ItemToggle/ComponentTogglerSystem.cs create mode 100644 Content.Shared/Item/ItemToggle/Components/ComponentTogglerComponent.cs create mode 100644 Content.Shared/Item/ItemToggle/Components/ToggleVerbComponent.cs rename Content.Shared/Item/ItemToggle/{SharedItemToggleSystem.cs => ItemToggleSystem.cs} (54%) create mode 100644 Content.Shared/Item/ItemToggle/ToggleVerbSystem.cs create mode 100644 Content.Shared/Ninja/Components/ItemCreatorComponent.cs create mode 100644 Content.Shared/Ninja/Systems/ItemCreatorSystem.cs create mode 100644 Content.Shared/Ninja/Systems/SharedSpiderChargeSystem.cs delete mode 100644 Content.Shared/Pinpointer/SharedProximityBeeper.cs create mode 100644 Resources/Prototypes/Entities/Objects/Devices/base_handheld.yml diff --git a/Content.Client/Items/Systems/ItemToggleSystem.cs b/Content.Client/Items/Systems/ItemToggleSystem.cs deleted file mode 100644 index 46d6f1b464d..00000000000 --- a/Content.Client/Items/Systems/ItemToggleSystem.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Shared.Item.ItemToggle; - -namespace Content.Shared.Item; - -/// -public sealed class ItemToggleSystem : SharedItemToggleSystem -{ - -} diff --git a/Content.Client/Ninja/Systems/ItemCreatorSystem.cs b/Content.Client/Ninja/Systems/ItemCreatorSystem.cs new file mode 100644 index 00000000000..9ab62cc12db --- /dev/null +++ b/Content.Client/Ninja/Systems/ItemCreatorSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Ninja.Systems; + +namespace Content.Client.Ninja.Systems; + +public sealed class ItemCreatorSystem : SharedItemCreatorSystem; diff --git a/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs b/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs index 7758c3d7e2b..5b07b1588fd 100644 --- a/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs +++ b/Content.Client/Ninja/Systems/NinjaGlovesSystem.cs @@ -2,9 +2,4 @@ namespace Content.Client.Ninja.Systems; -/// -/// Does nothing special, only exists to provide a client implementation. -/// -public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem -{ -} +public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem; diff --git a/Content.Client/Ninja/Systems/NinjaSuitSystem.cs b/Content.Client/Ninja/Systems/NinjaSuitSystem.cs index fde1801b37d..852ea8af46e 100644 --- a/Content.Client/Ninja/Systems/NinjaSuitSystem.cs +++ b/Content.Client/Ninja/Systems/NinjaSuitSystem.cs @@ -1,24 +1,5 @@ -using Content.Shared.Clothing.EntitySystems; -using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Systems; namespace Content.Client.Ninja.Systems; -/// -/// Disables cloak prediction since client has no knowledge of battery power. -/// Cloak will still be enabled after server tells it. -/// -public sealed class NinjaSuitSystem : SharedNinjaSuitSystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAttemptStealth); - } - - private void OnAttemptStealth(EntityUid uid, NinjaSuitComponent comp, AttemptStealthEvent args) - { - args.Cancel(); - } -} +public sealed class NinjaSuitSystem : SharedNinjaSuitSystem; diff --git a/Content.Client/Ninja/Systems/NinjaSystem.cs b/Content.Client/Ninja/Systems/NinjaSystem.cs index aa2fa2047f1..958dc6a5d9a 100644 --- a/Content.Client/Ninja/Systems/NinjaSystem.cs +++ b/Content.Client/Ninja/Systems/NinjaSystem.cs @@ -2,11 +2,4 @@ namespace Content.Client.Ninja.Systems; -/// -/// Currently does nothing special clientside. -/// All functionality is in shared and server. -/// Only exists to prevent crashing. -/// -public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem -{ -} +public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem; diff --git a/Content.Client/Ninja/Systems/SpiderChargeSystem.cs b/Content.Client/Ninja/Systems/SpiderChargeSystem.cs new file mode 100644 index 00000000000..b107fd3867d --- /dev/null +++ b/Content.Client/Ninja/Systems/SpiderChargeSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Ninja.Systems; + +namespace Content.Client.Ninja.Systems; + +public sealed class SpiderChargeSystem : SharedSpiderChargeSystem; diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs index a09126a7f7c..33e4da3fa34 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs @@ -171,7 +171,7 @@ await Server.WaitPost(() => // turn on welders if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated) { - Assert.That(ItemToggleSys.TryActivate(item, playerEnt, itemToggle: itemToggle)); + Assert.That(ItemToggleSys.TryActivate((item, itemToggle), user: playerEnt)); } }); diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index 457d3e31920..b3d684e01a0 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -104,7 +104,7 @@ public abstract partial class InteractionTest protected Content.Server.Construction.ConstructionSystem SConstruction = default!; protected SharedDoAfterSystem DoAfterSys = default!; protected ToolSystem ToolSys = default!; - protected SharedItemToggleSystem ItemToggleSys = default!; + protected ItemToggleSystem ItemToggleSys = default!; protected InteractionTestSystem STestSystem = default!; protected SharedTransformSystem Transform = default!; protected SharedMapSystem MapSystem = default!; @@ -165,7 +165,7 @@ public virtual async Task Setup() HandSys = SEntMan.System(); InteractSys = SEntMan.System(); ToolSys = SEntMan.System(); - ItemToggleSys = SEntMan.System(); + ItemToggleSys = SEntMan.System(); DoAfterSys = SEntMan.System(); Transform = SEntMan.System(); MapSystem = SEntMan.System(); diff --git a/Content.Server/Charges/Components/AutoRechargeComponent.cs b/Content.Server/Charges/Components/AutoRechargeComponent.cs index 9dcf555ea93..165b181dcbc 100644 --- a/Content.Server/Charges/Components/AutoRechargeComponent.cs +++ b/Content.Server/Charges/Components/AutoRechargeComponent.cs @@ -7,6 +7,7 @@ namespace Content.Server.Charges.Components; /// Something with limited charges that can be recharged automatically. /// Requires LimitedChargesComponent to function. /// +// TODO: no reason this cant be predicted and server system deleted [RegisterComponent, AutoGenerateComponentPause] [Access(typeof(ChargesSystem))] public sealed partial class AutoRechargeComponent : Component diff --git a/Content.Server/Charges/Systems/ChargesSystem.cs b/Content.Server/Charges/Systems/ChargesSystem.cs index 03e192e680e..974928ee4bb 100644 --- a/Content.Server/Charges/Systems/ChargesSystem.cs +++ b/Content.Server/Charges/Systems/ChargesSystem.cs @@ -37,15 +37,17 @@ protected override void OnExamine(EntityUid uid, LimitedChargesComponent comp, E args.PushMarkup(Loc.GetString("limited-charges-recharging", ("seconds", timeRemaining))); } - public override void UseCharge(EntityUid uid, LimitedChargesComponent? comp = null) + public override void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp = null) { - if (!Resolve(uid, ref comp, false)) + if (!Query.Resolve(uid, ref comp, false)) return; var startRecharge = comp.Charges == comp.MaxCharges; - base.UseCharge(uid, comp); - // start the recharge time after first use at full charge - if (startRecharge && TryComp(uid, out var recharge)) + base.AddCharges(uid, change, comp); + + // if a charge was just used from full, start the recharge timer + // TODO: probably make this an event instead of having le server system that just does this + if (change < 0 && startRecharge && TryComp(uid, out var recharge)) recharge.NextChargeTime = _timing.CurTime + recharge.RechargeDuration; } } diff --git a/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs deleted file mode 100644 index fa352eb320b..00000000000 --- a/Content.Server/GameTicking/Rules/Components/NinjaRuleComponent.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Content.Server.Ninja.Systems; -using Content.Shared.Communications; -using Content.Shared.Random; -using Robust.Shared.Audio; -using Robust.Shared.Prototypes; - -namespace Content.Server.GameTicking.Rules.Components; - -/// -/// Stores some configuration used by the ninja system. -/// Objectives and roundend summary are handled by . -/// -[RegisterComponent, Access(typeof(SpaceNinjaSystem))] -public sealed partial class NinjaRuleComponent : Component -{ - /// - /// List of threats that can be called in. Copied onto when gloves are enabled. - /// - [DataField(required: true)] - public ProtoId Threats = string.Empty; - - /// - /// Sound played when making the player a ninja via antag control or ghost role - /// - [DataField] - public SoundSpecifier? GreetingSound = new SoundPathSpecifier("/Audio/Misc/ninja_greeting.ogg"); -} diff --git a/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs b/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs deleted file mode 100644 index 30fa84ed90b..00000000000 --- a/Content.Server/Item/ItemToggle/Components/ItemToggleDisarmMalusComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Content.Server.Item; - -/// -/// Handles whether this item applies a disarm malus when active. -/// -[RegisterComponent] -public sealed partial class ItemToggleDisarmMalusComponent : Component -{ - /// - /// Item has this modifier to the chance to disarm when activated. - /// If null, the value will be inferred from the current malus just before the malus is first deactivated. - /// - [ViewVariables(VVAccess.ReadOnly), DataField] - public float? ActivatedDisarmMalus = null; - - /// - /// Item has this modifier to the chance to disarm when deactivated. If none is mentioned, it uses the item's default disarm modifier. - /// If null, the value will be inferred from the current malus just before the malus is first activated. - /// - [ViewVariables(VVAccess.ReadOnly), DataField] - public float? DeactivatedDisarmMalus = null; -} diff --git a/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs b/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs deleted file mode 100644 index 227491b16c2..00000000000 --- a/Content.Server/Item/ItemToggle/Components/ItemToggleSharpComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Content.Server.Item; - -/// -/// Handles whether this item is sharp when toggled on. -/// -[RegisterComponent] -public sealed partial class ItemToggleSharpComponent : Component -{ -} diff --git a/Content.Server/Item/ItemToggle/ItemToggleSystem.cs b/Content.Server/Item/ItemToggle/ItemToggleSystem.cs deleted file mode 100644 index f98415eb08f..00000000000 --- a/Content.Server/Item/ItemToggle/ItemToggleSystem.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Content.Server.CombatMode.Disarm; -using Content.Server.Kitchen.Components; -using Content.Shared.Item.ItemToggle; -using Content.Shared.Item.ItemToggle.Components; - -namespace Content.Server.Item; - -public sealed class ItemToggleSystem : SharedItemToggleSystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(ToggleSharp); - SubscribeLocalEvent(ToggleMalus); - } - - private void ToggleSharp(Entity ent, ref ItemToggledEvent args) - { - // TODO generalize this into a "ToggleComponentComponent", though probably with a better name - if (args.Activated) - EnsureComp(ent); - else - RemCompDeferred(ent); - } - - private void ToggleMalus(Entity ent, ref ItemToggledEvent args) - { - if (!TryComp(ent, out var malus)) - return; - - if (args.Activated) - { - ent.Comp.DeactivatedDisarmMalus ??= malus.Malus; - if (ent.Comp.ActivatedDisarmMalus is {} activatedMalus) - malus.Malus = activatedMalus; - return; - } - - ent.Comp.ActivatedDisarmMalus ??= malus.Malus; - if (ent.Comp.DeactivatedDisarmMalus is {} deactivatedMalus) - malus.Malus = deactivatedMalus; - } -} diff --git a/Content.Server/Medical/Components/HealthAnalyzerComponent.cs b/Content.Server/Medical/Components/HealthAnalyzerComponent.cs index 6380b71c8a0..de40e98e182 100644 --- a/Content.Server/Medical/Components/HealthAnalyzerComponent.cs +++ b/Content.Server/Medical/Components/HealthAnalyzerComponent.cs @@ -6,6 +6,9 @@ namespace Content.Server.Medical.Components; /// /// After scanning, retrieves the target Uid to use with its related UI. /// +/// +/// Requires ItemToggleComponent. +/// [RegisterComponent, AutoGenerateComponentPause] [Access(typeof(HealthAnalyzerSystem), typeof(CryoPodSystem))] public sealed partial class HealthAnalyzerComponent : Component diff --git a/Content.Server/Medical/DefibrillatorSystem.cs b/Content.Server/Medical/DefibrillatorSystem.cs index 4373532f018..1896f51eddc 100644 --- a/Content.Server/Medical/DefibrillatorSystem.cs +++ b/Content.Server/Medical/DefibrillatorSystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Interaction; using Content.Shared.Interaction.Components; using Content.Shared.Interaction.Events; +using Content.Shared.Item.ItemToggle; using Content.Shared.Medical; using Content.Shared.Mind; using Content.Shared.Mobs; @@ -37,6 +38,7 @@ public sealed class DefibrillatorSystem : EntitySystem [Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly ElectrocutionSystem _electrocution = default!; [Dependency] private readonly EuiManager _euiManager = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly RottingSystem _rotting = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobThresholdSystem _mobThreshold = default!; @@ -50,30 +52,10 @@ public sealed class DefibrillatorSystem : EntitySystem /// public override void Initialize() { - SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnPowerCellSlotEmpty); SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnDoAfter); } - private void OnUseInHand(EntityUid uid, DefibrillatorComponent component, UseInHandEvent args) - { - if (args.Handled || !TryComp(uid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((uid, useDelay))) - return; - - if (!TryToggle(uid, component, args.User)) - return; - - args.Handled = true; - _useDelay.TryResetDelay((uid, useDelay)); - } - - private void OnPowerCellSlotEmpty(EntityUid uid, DefibrillatorComponent component, ref PowerCellSlotEmptyEvent args) - { - if (!TerminatingOrDeleted(uid)) - TryDisable(uid, component); - } - private void OnAfterInteract(EntityUid uid, DefibrillatorComponent component, AfterInteractEvent args) { if (args.Handled || args.Target is not { } target) @@ -96,54 +78,12 @@ private void OnDoAfter(EntityUid uid, DefibrillatorComponent component, Defibril Zap(uid, target, args.User, component); } - public bool TryToggle(EntityUid uid, DefibrillatorComponent? component = null, EntityUid? user = null) - { - if (!Resolve(uid, ref component)) - return false; - - return component.Enabled - ? TryDisable(uid, component) - : TryEnable(uid, component, user); - } - - public bool TryEnable(EntityUid uid, DefibrillatorComponent? component = null, EntityUid? user = null) - { - if (!Resolve(uid, ref component)) - return false; - - if (component.Enabled) - return false; - - if (!_powerCell.HasActivatableCharge(uid)) - return false; - - component.Enabled = true; - _appearance.SetData(uid, ToggleVisuals.Toggled, true); - _audio.PlayPvs(component.PowerOnSound, uid); - return true; - } - - public bool TryDisable(EntityUid uid, DefibrillatorComponent? component = null) - { - if (!Resolve(uid, ref component)) - return false; - - if (!component.Enabled) - return false; - - component.Enabled = false; - _appearance.SetData(uid, ToggleVisuals.Toggled, false); - - _audio.PlayPvs(component.PowerOffSound, uid); - return true; - } - public bool CanZap(EntityUid uid, EntityUid target, EntityUid? user = null, DefibrillatorComponent? component = null) { if (!Resolve(uid, ref component)) return false; - if (!component.Enabled) + if (!_toggle.IsActivated(uid)) { if (user != null) _popup.PopupEntity(Loc.GetString("defibrillator-not-on"), uid, user.Value); @@ -257,7 +197,7 @@ public void Zap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorCo // if we don't have enough power left for another shot, turn it off if (!_powerCell.HasActivatableCharge(uid)) - TryDisable(uid, component); + _toggle.TryDeactivate(uid); // TODO clean up this clown show above var ev = new TargetDefibrillatedEvent(user, (uid, component)); diff --git a/Content.Server/Medical/HealthAnalyzerSystem.cs b/Content.Server/Medical/HealthAnalyzerSystem.cs index c72cd2ddf6f..1d6e564a32d 100644 --- a/Content.Server/Medical/HealthAnalyzerSystem.cs +++ b/Content.Server/Medical/HealthAnalyzerSystem.cs @@ -8,6 +8,8 @@ using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.MedicalScanner; using Content.Shared.Mobs.Components; using Content.Shared.Popups; @@ -26,6 +28,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem [Dependency] private readonly PowerCellSystem _cell = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; [Dependency] private readonly TransformSystem _transformSystem = default!; @@ -36,7 +39,7 @@ public override void Initialize() SubscribeLocalEvent(OnAfterInteract); SubscribeLocalEvent(OnDoAfter); SubscribeLocalEvent(OnInsertedIntoContainer); - SubscribeLocalEvent(OnPowerCellSlotEmpty); + SubscribeLocalEvent(OnToggled); SubscribeLocalEvent(OnDropped); } @@ -111,16 +114,16 @@ private void OnDoAfter(Entity uid, ref HealthAnalyzerDo private void OnInsertedIntoContainer(Entity uid, ref EntGotInsertedIntoContainerMessage args) { if (uid.Comp.ScannedEntity is { } patient) - StopAnalyzingEntity(uid, patient); + _toggle.TryDeactivate(uid.Owner); } /// - /// Disable continuous updates once battery is dead + /// Disable continuous updates once turned off /// - private void OnPowerCellSlotEmpty(Entity uid, ref PowerCellSlotEmptyEvent args) + private void OnToggled(Entity ent, ref ItemToggledEvent args) { - if (uid.Comp.ScannedEntity is { } patient) - StopAnalyzingEntity(uid, patient); + if (!args.Activated && ent.Comp.ScannedEntity is { } patient) + StopAnalyzingEntity(ent, patient); } /// @@ -129,7 +132,7 @@ private void OnPowerCellSlotEmpty(Entity uid, ref Power private void OnDropped(Entity uid, ref DroppedEvent args) { if (uid.Comp.ScannedEntity is { } patient) - StopAnalyzingEntity(uid, patient); + _toggle.TryDeactivate(uid.Owner); } private void OpenUserInterface(EntityUid user, EntityUid analyzer) @@ -150,7 +153,7 @@ private void BeginAnalyzingEntity(Entity healthAnalyzer //Link the health analyzer to the scanned entity healthAnalyzer.Comp.ScannedEntity = target; - _cell.SetPowerCellDrawEnabled(healthAnalyzer, true); + _toggle.TryActivate(healthAnalyzer.Owner); UpdateScannedUser(healthAnalyzer, target, true); } @@ -165,7 +168,7 @@ private void StopAnalyzingEntity(Entity healthAnalyzer, //Unlink the analyzer healthAnalyzer.Comp.ScannedEntity = null; - _cell.SetPowerCellDrawEnabled(target, false); + _toggle.TryDeactivate(healthAnalyzer.Owner); UpdateScannedUser(healthAnalyzer, target, false); } diff --git a/Content.Server/Ninja/Events/BatteryChangedEvent.cs b/Content.Server/Ninja/Events/BatteryChangedEvent.cs index 45bfedfee76..1848e881868 100644 --- a/Content.Server/Ninja/Events/BatteryChangedEvent.cs +++ b/Content.Server/Ninja/Events/BatteryChangedEvent.cs @@ -1,7 +1,7 @@ namespace Content.Server.Ninja.Events; /// -/// Raised on the ninja when the suit has its powercell changed. +/// Raised on the ninja and suit when the suit has its powercell changed. /// [ByRefEvent] public record struct NinjaBatteryChangedEvent(EntityUid Battery, EntityUid BatteryHolder); diff --git a/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs b/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs index 59dec556fa0..4baf0913cec 100644 --- a/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs +++ b/Content.Server/Ninja/Systems/BatteryDrainerSystem.cs @@ -33,16 +33,17 @@ public override void Initialize() /// Start do after for draining a power source. /// Can't predict PNBC existing so only done on server. /// - private void OnBeforeInteractHand(EntityUid uid, BatteryDrainerComponent comp, BeforeInteractHandEvent args) + private void OnBeforeInteractHand(Entity ent, ref BeforeInteractHandEvent args) { + var (uid, comp) = ent; var target = args.Target; - if (args.Handled || comp.BatteryUid == null || !HasComp(target)) + if (args.Handled || comp.BatteryUid is not {} battery || !HasComp(target)) return; // handles even if battery is full so you can actually see the poup args.Handled = true; - if (_battery.IsFull(comp.BatteryUid.Value)) + if (_battery.IsFull(battery)) { _popup.PopupEntity(Loc.GetString("battery-drainer-full"), uid, uid, PopupType.Medium); return; @@ -59,23 +60,24 @@ private void OnBeforeInteractHand(EntityUid uid, BatteryDrainerComponent comp, B _doAfter.TryStartDoAfter(doAfterArgs); } - private void OnBatteryChanged(EntityUid uid, BatteryDrainerComponent comp, ref NinjaBatteryChangedEvent args) + private void OnBatteryChanged(Entity ent, ref NinjaBatteryChangedEvent args) { - SetBattery(uid, args.Battery, comp); + SetBattery((ent, ent.Comp), args.Battery); } /// - protected override void OnDoAfterAttempt(EntityUid uid, BatteryDrainerComponent comp, DoAfterAttemptEvent args) + protected override void OnDoAfterAttempt(Entity ent, ref DoAfterAttemptEvent args) { - base.OnDoAfterAttempt(uid, comp, args); + base.OnDoAfterAttempt(ent, ref args); - if (comp.BatteryUid == null || _battery.IsFull(comp.BatteryUid.Value)) + if (ent.Comp.BatteryUid is not {} battery || _battery.IsFull(battery)) args.Cancel(); } /// - protected override bool TryDrainPower(EntityUid uid, BatteryDrainerComponent comp, EntityUid target) + protected override bool TryDrainPower(Entity ent, EntityUid target) { + var (uid, comp) = ent; if (comp.BatteryUid == null || !TryComp(comp.BatteryUid.Value, out var battery)) return false; @@ -98,6 +100,7 @@ protected override bool TryDrainPower(EntityUid uid, BatteryDrainerComponent com var output = input * comp.DrainEfficiency; _battery.SetCharge(comp.BatteryUid.Value, battery.CurrentCharge + output, battery); + // TODO: create effect message or something Spawn("EffectSparks", Transform(target).Coordinates); _audio.PlayPvs(comp.SparkSound, target); _popup.PopupEntity(Loc.GetString("battery-drainer-success", ("battery", target)), uid, uid); diff --git a/Content.Server/Ninja/Systems/ItemCreatorSystem.cs b/Content.Server/Ninja/Systems/ItemCreatorSystem.cs new file mode 100644 index 00000000000..d7a7be995db --- /dev/null +++ b/Content.Server/Ninja/Systems/ItemCreatorSystem.cs @@ -0,0 +1,57 @@ +using Content.Server.Ninja.Events; +using Content.Server.Power.EntitySystems; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.Ninja.Components; +using Content.Shared.Ninja.Systems; +using Content.Shared.Popups; + +namespace Content.Server.Ninja.Systems; + +public sealed class ItemCreatorSystem : SharedItemCreatorSystem +{ + [Dependency] private readonly BatterySystem _battery = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnCreateItem); + SubscribeLocalEvent(OnBatteryChanged); + } + + private void OnCreateItem(Entity ent, ref CreateItemEvent args) + { + var (uid, comp) = ent; + if (comp.Battery is not {} battery) + return; + + args.Handled = true; + + var user = args.Performer; + if (!_battery.TryUseCharge(battery, comp.Charge)) + { + _popup.PopupEntity(Loc.GetString(comp.NoPowerPopup), user, user); + return; + } + + var ev = new CreateItemAttemptEvent(user); + RaiseLocalEvent(uid, ref ev); + if (ev.Cancelled) + return; + + // try to put throwing star in hand, otherwise it goes on the ground + var star = Spawn(comp.SpawnedPrototype, Transform(user).Coordinates); + _hands.TryPickupAnyHand(user, star); + } + + private void OnBatteryChanged(Entity ent, ref NinjaBatteryChangedEvent args) + { + if (ent.Comp.Battery == args.Battery) + return; + + ent.Comp.Battery = args.Battery; + Dirty(ent, ent.Comp); + } +} diff --git a/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs b/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs index ac76ae6b771..3aaf7c5d58e 100644 --- a/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs +++ b/Content.Server/Ninja/Systems/NinjaGlovesSystem.cs @@ -1,13 +1,8 @@ -using Content.Server.Communications; -using Content.Server.Mind; using Content.Server.Ninja.Events; -using Content.Server.Objectives.Systems; -using Content.Shared.Communications; -using Content.Shared.CriminalRecords.Components; +using Content.Shared.Mind; +using Content.Shared.Objectives.Systems; using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Systems; -using Content.Shared.Research.Components; -using Content.Shared.Toggleable; namespace Content.Server.Ninja.Systems; @@ -16,89 +11,44 @@ namespace Content.Server.Ninja.Systems; /// public sealed class NinjaGlovesSystem : SharedNinjaGlovesSystem { - [Dependency] private readonly EmagProviderSystem _emagProvider = default!; - [Dependency] private readonly CodeConditionSystem _codeCondition = default!; - [Dependency] private readonly CommsHackerSystem _commsHacker = default!; - [Dependency] private readonly SharedStunProviderSystem _stunProvider = default!; + [Dependency] private readonly SharedMindSystem _mind = default!; + [Dependency] private readonly SharedObjectivesSystem _objectives = default!; [Dependency] private readonly SpaceNinjaSystem _ninja = default!; - public override void Initialize() + protected override void EnableGloves(Entity ent, Entity user) { - base.Initialize(); + base.EnableGloves(ent, user); - SubscribeLocalEvent(OnToggleAction); - } - - /// - /// Toggle gloves, if the user is a ninja wearing a ninja suit. - /// - private void OnToggleAction(EntityUid uid, NinjaGlovesComponent comp, ToggleActionEvent args) - { - if (args.Handled) + // can't use abilities if suit is not equipped, this is checked elsewhere but just making sure to satisfy nullability + if (user.Comp.Suit is not {} suit) return; - args.Handled = true; - - var user = args.Performer; - // need to wear suit to enable gloves - if (!TryComp(user, out var ninja) - || ninja.Suit == null - || !HasComp(ninja.Suit.Value)) - { - Popup.PopupEntity(Loc.GetString("ninja-gloves-not-wearing-suit"), user, user); + if (!_mind.TryGetMind(user, out var mindId, out var mind)) return; - } - - // show its state to the user - var enabling = comp.User == null; - Appearance.SetData(uid, ToggleVisuals.Toggled, enabling); - var message = Loc.GetString(enabling ? "ninja-gloves-on" : "ninja-gloves-off"); - Popup.PopupEntity(message, user, user); - if (enabling) + foreach (var ability in ent.Comp.Abilities) { - EnableGloves(uid, comp, user, ninja); + // non-objective abilities are added in shared already + if (ability.Objective is not {} objId) + continue; + + // prevent doing an objective multiple times by toggling gloves after doing them + // if it's not tied to an objective always add them anyway + if (!_mind.TryFindObjective((mindId, mind), objId, out var obj)) + { + Log.Error($"Ninja glove ability of {ent} referenced missing objective {ability.Objective} of {_mind.MindOwnerLoggingString(mind)}"); + continue; + } + + if (!_objectives.IsCompleted(obj.Value, (mindId, mind))) + EntityManager.AddComponents(user, ability.Components); } - else - { - DisableGloves(uid, comp); - } - } - private void EnableGloves(EntityUid uid, NinjaGlovesComponent comp, EntityUid user, SpaceNinjaComponent ninja) - { - // can't use abilities if suit is not equipped, this is checked elsewhere but just making sure to satisfy nullability - if (ninja.Suit == null) - return; - - comp.User = user; - Dirty(uid, comp); - _ninja.AssignGloves(user, uid, ninja); - - var drainer = EnsureComp(user); - var stun = EnsureComp(user); - _stunProvider.SetNoPowerPopup(user, "ninja-no-power", stun); + // let abilities that use battery power work if (_ninja.GetNinjaBattery(user, out var battery, out var _)) { - var ev = new NinjaBatteryChangedEvent(battery.Value, ninja.Suit.Value); + var ev = new NinjaBatteryChangedEvent(battery.Value, suit); RaiseLocalEvent(user, ref ev); } - - var emag = EnsureComp(user); - _emagProvider.SetWhitelist(user, comp.DoorjackWhitelist, emag); - - EnsureComp(user); - // prevent calling in multiple threats by toggling gloves after - if (!_codeCondition.IsCompleted(user, ninja.TerrorObjective)) - { - var hacker = EnsureComp(user); - var rule = _ninja.NinjaRule(user); - if (rule != null) - _commsHacker.SetThreats(user, rule.Threats, hacker); - } - if (!_codeCondition.IsCompleted(user, ninja.MassArrestObjective)) - { - EnsureComp(user); - } } } diff --git a/Content.Server/Ninja/Systems/NinjaSuitSystem.cs b/Content.Server/Ninja/Systems/NinjaSuitSystem.cs index 04095b549c6..63054eaad50 100644 --- a/Content.Server/Ninja/Systems/NinjaSuitSystem.cs +++ b/Content.Server/Ninja/Systems/NinjaSuitSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Ninja.Events; using Content.Server.Power.Components; using Content.Server.PowerCell; -using Content.Shared.Clothing.EntitySystems; using Content.Shared.Hands.EntitySystems; using Content.Shared.Ninja.Components; using Content.Shared.Ninja.Systems; @@ -29,15 +28,13 @@ public override void Initialize() SubscribeLocalEvent(OnSuitInsertAttempt); SubscribeLocalEvent(OnEmpAttempt); - SubscribeLocalEvent(OnAttemptStealth); - SubscribeLocalEvent(OnCreateThrowingStar); SubscribeLocalEvent(OnRecallKatana); SubscribeLocalEvent(OnEmp); } - protected override void NinjaEquippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user, SpaceNinjaComponent ninja) + protected override void NinjaEquipped(Entity ent, Entity user) { - base.NinjaEquippedSuit(uid, comp, user, ninja); + base.NinjaEquipped(ent, user); _ninja.SetSuitPowerAlert(user); } @@ -57,16 +54,15 @@ private void OnSuitInsertAttempt(EntityUid uid, NinjaSuitComponent comp, Contain // can only upgrade power cell, not swap to recharge instantly otherwise ninja could just swap batteries with flashlights in maints for easy power if (!TryComp(args.EntityUid, out var inserting) || inserting.MaxCharge <= battery.MaxCharge) - { args.Cancel(); - } // tell ninja abilities that use battery to update it so they don't use charge from the old one var user = Transform(uid).ParentUid; - if (!HasComp(user)) + if (!_ninja.IsNinja(user)) return; var ev = new NinjaBatteryChangedEvent(args.EntityUid, uid); + RaiseLocalEvent(uid, ref ev); RaiseLocalEvent(user, ref ev); } @@ -77,64 +73,22 @@ private void OnEmpAttempt(EntityUid uid, NinjaSuitComponent comp, EmpAttemptEven args.Cancel(); } - protected override void UserUnequippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user) + protected override void UserUnequippedSuit(Entity ent, Entity user) { - base.UserUnequippedSuit(uid, comp, user); + base.UserUnequippedSuit(ent, user); // remove power indicator _ninja.SetSuitPowerAlert(user); } - private void OnAttemptStealth(EntityUid uid, NinjaSuitComponent comp, AttemptStealthEvent args) + private void OnRecallKatana(Entity ent, ref RecallKatanaEvent args) { - var user = args.User; - // need 1 second of charge to turn on stealth - var chargeNeeded = SuitWattage(uid, comp); - // being attacked while cloaked gives no power message since it overloads the power supply or something - if (!_ninja.GetNinjaBattery(user, out _, out var battery) || battery.CurrentCharge < chargeNeeded) - { - Popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user); - args.Cancel(); - return; - } - - if (comp.DisableCooldown > GameTiming.CurTime) - { - Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium); - args.Cancel(); - return; - } - - StealthClothing.SetEnabled(uid, user, true); - } - - private void OnCreateThrowingStar(EntityUid uid, NinjaSuitComponent comp, CreateThrowingStarEvent args) - { - args.Handled = true; + var (uid, comp) = ent; var user = args.Performer; - if (!_ninja.TryUseCharge(user, comp.ThrowingStarCharge)) - { - Popup.PopupEntity(Loc.GetString("ninja-no-power"), user, user); + if (!_ninja.NinjaQuery.TryComp(user, out var ninja) || ninja.Katana == null) return; - } - - if (comp.DisableCooldown > GameTiming.CurTime) - { - Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium); - return; - } - - // try to put throwing star in hand, otherwise it goes on the ground - var star = Spawn(comp.ThrowingStarPrototype, Transform(user).Coordinates); - _hands.TryPickupAnyHand(user, star); - } - private void OnRecallKatana(EntityUid uid, NinjaSuitComponent comp, RecallKatanaEvent args) - { args.Handled = true; - var user = args.Performer; - if (!TryComp(user, out var ninja) || ninja.Katana == null) - return; var katana = ninja.Katana.Value; var coords = _transform.GetWorldPosition(katana); @@ -146,11 +100,8 @@ private void OnRecallKatana(EntityUid uid, NinjaSuitComponent comp, RecallKatana return; } - if (comp.DisableCooldown > GameTiming.CurTime) - { - Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium); + if (CheckDisabled(ent, user)) return; - } // TODO: teleporting into belt slot var message = _hands.TryPickupAnyHand(user, katana) @@ -159,9 +110,11 @@ private void OnRecallKatana(EntityUid uid, NinjaSuitComponent comp, RecallKatana Popup.PopupEntity(Loc.GetString(message), user, user); } - private void OnEmp(EntityUid uid, NinjaSuitComponent comp, NinjaEmpEvent args) + private void OnEmp(Entity ent, ref NinjaEmpEvent args) { + var (uid, comp) = ent; args.Handled = true; + var user = args.Performer; if (!_ninja.TryUseCharge(user, comp.EmpCharge)) { @@ -169,13 +122,9 @@ private void OnEmp(EntityUid uid, NinjaSuitComponent comp, NinjaEmpEvent args) return; } - if (comp.DisableCooldown > GameTiming.CurTime) - { - Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium); + if (CheckDisabled(ent, user)) return; - } - // I don't think this affects the suit battery, but if it ever does in the future add a blacklist for it var coords = _transform.GetMapCoordinates(user); _emp.EmpPulse(coords, comp.EmpRange, comp.EmpConsumption, comp.EmpDuration); } diff --git a/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs b/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs index 0c1e88653fa..28ab6332276 100644 --- a/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs +++ b/Content.Server/Ninja/Systems/SpaceNinjaSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Chat.Managers; using Content.Server.CriminalRecords.Systems; using Content.Server.GameTicking.Rules.Components; -using Content.Server.GenericAntag; using Content.Server.Objectives.Components; using Content.Server.Objectives.Systems; using Content.Server.Power.Components; @@ -11,7 +10,6 @@ using Content.Server.Research.Systems; using Content.Server.Roles; using Content.Shared.Alert; -using Content.Shared.Clothing.EntitySystems; using Content.Shared.Doors.Components; using Content.Shared.IdentityManagement; using Content.Shared.Mind; @@ -26,11 +24,6 @@ namespace Content.Server.Ninja.Systems; -// TODO: when syndiborgs are a thing have a borg converter with 6 second doafter -// engi -> saboteur -// medi -> idk reskin it -// other -> assault - /// /// Main ninja system that handles ninja setup, provides helper methods for the rest of the code to use. /// @@ -44,13 +37,11 @@ public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem [Dependency] private readonly RoleSystem _role = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedMindSystem _mind = default!; - [Dependency] private readonly StealthClothingSystem _stealthClothing = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnNinjaCreated); SubscribeLocalEvent(OnDoorjack); SubscribeLocalEvent(OnResearchStolen); SubscribeLocalEvent(OnThreatCalledIn); @@ -62,7 +53,7 @@ public override void Update(float frameTime) var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var ninja)) { - UpdateNinja(uid, ninja, frameTime); + SetSuitPowerAlert((uid, ninja)); } } @@ -80,31 +71,13 @@ private int Download(EntityUid uid, List ids) return newCount - oldCount; } - /// - /// Returns a ninja's gamerule config data. - /// If the gamerule was not started then it will be started automatically. - /// - public NinjaRuleComponent? NinjaRule(EntityUid uid, GenericAntagComponent? comp = null) - { - if (!Resolve(uid, ref comp)) - return null; - - // mind not added yet so no rule - if (comp.RuleEntity == null) - return null; - - return CompOrNull(comp.RuleEntity); - } - // TODO: can probably copy paste borg code here /// /// Update the alert for the ninja's suit power indicator. /// - public void SetSuitPowerAlert(EntityUid uid, SpaceNinjaComponent? comp = null) + public void SetSuitPowerAlert(Entity ent) { - if (!Resolve(uid, ref comp, false)) - return; - + var (uid, comp) = ent; if (comp.Deleted || comp.Suit == null) { _alerts.ClearAlert(uid, comp.SuitPowerAlert); @@ -145,53 +118,6 @@ public override bool TryUseCharge(EntityUid user, float charge) return GetNinjaBattery(user, out var uid, out var battery) && _battery.TryUseCharge(uid.Value, charge, battery); } - /// - /// Set up everything for ninja to work and send the greeting message/sound. - /// Objectives are added by . - /// - private void OnNinjaCreated(EntityUid uid, SpaceNinjaComponent comp, ref GenericAntagCreatedEvent args) - { - var mindId = args.MindId; - var mind = args.Mind; - - if (mind.Session == null) - return; - - var config = NinjaRule(uid); - if (config == null) - return; - - var role = new NinjaRoleComponent - { - PrototypeId = "SpaceNinja" - }; - _role.MindAddRole(mindId, role, mind); - _role.MindPlaySound(mindId, config.GreetingSound, mind); - - var session = mind.Session; - _audio.PlayGlobal(config.GreetingSound, Filter.Empty().AddPlayer(session), false, AudioParams.Default); - _chatMan.DispatchServerMessage(session, Loc.GetString("ninja-role-greeting")); - } - - // TODO: PowerCellDraw, modify when cloak enabled - /// - /// Handle constant power drains from passive usage and cloak. - /// - private void UpdateNinja(EntityUid uid, SpaceNinjaComponent ninja, float frameTime) - { - if (ninja.Suit == null) - return; - - float wattage = Suit.SuitWattage(ninja.Suit.Value); - - SetSuitPowerAlert(uid, ninja); - if (!TryUseCharge(uid, wattage * frameTime)) - { - // ran out of power, uncloak ninja - _stealthClothing.SetEnabled(ninja.Suit.Value, uid, false); - } - } - /// /// Increment greentext when emagging a door. /// diff --git a/Content.Server/Ninja/Systems/SpiderChargeSystem.cs b/Content.Server/Ninja/Systems/SpiderChargeSystem.cs index 64c958d6f1a..c262651f27a 100644 --- a/Content.Server/Ninja/Systems/SpiderChargeSystem.cs +++ b/Content.Server/Ninja/Systems/SpiderChargeSystem.cs @@ -7,6 +7,7 @@ using Content.Server.Sticky.Events; using Content.Shared.Interaction; using Content.Shared.Ninja.Components; +using Content.Shared.Ninja.Systems; using Robust.Shared.GameObjects; namespace Content.Server.Ninja.Systems; @@ -14,7 +15,7 @@ namespace Content.Server.Ninja.Systems; /// /// Prevents planting a spider charge outside of its location and handles greentext. /// -public sealed class SpiderChargeSystem : EntitySystem +public sealed class SpiderChargeSystem : SharedSpiderChargeSystem { [Dependency] private readonly MindSystem _mind = default!; [Dependency] private readonly PopupSystem _popup = default!; diff --git a/Content.Server/Ninja/Systems/StunProviderSystem.cs b/Content.Server/Ninja/Systems/StunProviderSystem.cs index 1768606ad20..822486cff52 100644 --- a/Content.Server/Ninja/Systems/StunProviderSystem.cs +++ b/Content.Server/Ninja/Systems/StunProviderSystem.cs @@ -6,10 +6,11 @@ using Content.Shared.Ninja.Systems; using Content.Shared.Popups; using Content.Shared.Stunnable; -using Robust.Shared.Prototypes; +using Content.Shared.Timing; +using Content.Shared.Whitelist; using Robust.Shared.Audio.Systems; using Robust.Shared.Timing; -using Content.Shared.Whitelist; +using Robust.Shared.Prototypes; namespace Content.Server.Ninja.Systems; @@ -20,12 +21,12 @@ public sealed class StunProviderSystem : SharedStunProviderSystem { [Dependency] private readonly BatterySystem _battery = default!; [Dependency] private readonly DamageableSystem _damageable = default!; - [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedStunSystem _stun = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly UseDelaySystem _useDelay = default!; public override void Initialize() { @@ -38,16 +39,18 @@ public override void Initialize() /// /// Stun clicked mobs on the whitelist, if there is enough power. /// - private void OnBeforeInteractHand(EntityUid uid, StunProviderComponent comp, BeforeInteractHandEvent args) + private void OnBeforeInteractHand(Entity ent, ref BeforeInteractHandEvent args) { // TODO: generic check + var (uid, comp) = ent; if (args.Handled || comp.BatteryUid == null || !_gloves.AbilityCheck(uid, args, out var target)) return; - if (target == uid || _whitelistSystem.IsWhitelistFail(comp.Whitelist, target)) + if (target == uid || _whitelist.IsWhitelistFail(comp.Whitelist, target)) return; - if (_timing.CurTime < comp.NextStun) + var useDelay = EnsureComp(uid); + if (_useDelay.IsDelayed((uid, useDelay), id: comp.DelayId)) return; // take charge from battery @@ -63,13 +66,14 @@ private void OnBeforeInteractHand(EntityUid uid, StunProviderComponent comp, Bef _stun.TryParalyze(target, comp.StunTime, refresh: false); // short cooldown to prevent instant stunlocking - comp.NextStun = _timing.CurTime + comp.Cooldown; + _useDelay.SetLength((uid, useDelay), comp.Cooldown, id: comp.DelayId); + _useDelay.TryResetDelay((uid, useDelay), id: comp.DelayId); args.Handled = true; } - private void OnBatteryChanged(EntityUid uid, StunProviderComponent comp, ref NinjaBatteryChangedEvent args) + private void OnBatteryChanged(Entity ent, ref NinjaBatteryChangedEvent args) { - SetBattery(uid, args.Battery, comp); + SetBattery((ent, ent.Comp), args.Battery); } } diff --git a/Content.Server/Objectives/Systems/CodeConditionSystem.cs b/Content.Server/Objectives/Systems/CodeConditionSystem.cs index 7ba312f4bb9..fbc58dafe82 100644 --- a/Content.Server/Objectives/Systems/CodeConditionSystem.cs +++ b/Content.Server/Objectives/Systems/CodeConditionSystem.cs @@ -35,20 +35,6 @@ public bool IsCompleted(Entity ent) return ent.Comp.Completed; } - /// - /// Returns true if a mob's objective with a certain prototype is completed. - /// - public bool IsCompleted(Entity mob, string prototype) - { - if (_mind.GetMind(mob, mob.Comp) is not {} mindId) - return false; - - if (!_mind.TryFindObjective(mindId, prototype, out var obj)) - return false; - - return IsCompleted(obj.Value); - } - /// /// Sets an objective's completed field. /// diff --git a/Content.Server/PowerCell/PowerCellSystem.Draw.cs b/Content.Server/PowerCell/PowerCellSystem.Draw.cs index 4155a4f6bec..9ebd677f473 100644 --- a/Content.Server/PowerCell/PowerCellSystem.Draw.cs +++ b/Content.Server/PowerCell/PowerCellSystem.Draw.cs @@ -1,4 +1,5 @@ using Content.Server.Power.Components; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.PowerCell; using Content.Shared.PowerCell.Components; @@ -10,22 +11,20 @@ public sealed partial class PowerCellSystem * Handles PowerCellDraw */ - private static readonly TimeSpan Delay = TimeSpan.FromSeconds(1); - public override void Update(float frameTime) { base.Update(frameTime); - var query = EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp, out var slot)) + while (query.MoveNext(out var uid, out var comp, out var slot, out var toggle)) { - if (!comp.Drawing) + if (!comp.Enabled || !toggle.Activated) continue; if (Timing.CurTime < comp.NextUpdateTime) continue; - comp.NextUpdateTime += Delay; + comp.NextUpdateTime += comp.Delay; if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, slot)) continue; @@ -33,7 +32,8 @@ public override void Update(float frameTime) if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate, battery)) continue; - comp.Drawing = false; + Toggle.TryDeactivate((uid, toggle)); + var ev = new PowerCellSlotEmptyEvent(); RaiseLocalEvent(uid, ref ev); } @@ -42,26 +42,9 @@ public override void Update(float frameTime) private void OnDrawChargeChanged(EntityUid uid, PowerCellDrawComponent component, ref ChargeChangedEvent args) { // Update the bools for client prediction. - bool canDraw; - bool canUse; - - if (component.UseRate > 0f) - { - canUse = args.Charge > component.UseRate; - } - else - { - canUse = true; - } + var canUse = component.UseRate <= 0f || args.Charge > component.UseRate; - if (component.DrawRate > 0f) - { - canDraw = args.Charge > 0f; - } - else - { - canDraw = true; - } + var canDraw = component.DrawRate <= 0f || args.Charge > 0f; if (canUse != component.CanUse || canDraw != component.CanDraw) { @@ -76,6 +59,9 @@ private void OnDrawCellChanged(EntityUid uid, PowerCellDrawComponent component, var canDraw = !args.Ejected && HasCharge(uid, float.MinValue); var canUse = !args.Ejected && HasActivatableCharge(uid, component); + if (!canDraw) + Toggle.TryDeactivate(uid); + if (canUse != component.CanUse || canDraw != component.CanDraw) { component.CanDraw = canDraw; diff --git a/Content.Server/PowerCell/PowerCellSystem.cs b/Content.Server/PowerCell/PowerCellSystem.cs index f45a01b2e1b..0d2d9012bc7 100644 --- a/Content.Server/PowerCell/PowerCellSystem.cs +++ b/Content.Server/PowerCell/PowerCellSystem.cs @@ -39,8 +39,8 @@ public override void Initialize() SubscribeLocalEvent(OnDrawChargeChanged); SubscribeLocalEvent(OnDrawCellChanged); - // funny SubscribeLocalEvent(OnCellSlotExamined); + // funny SubscribeLocalEvent(OnSlotMicrowaved); } diff --git a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs index 746d75f0d85..f289752b7cf 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.Modules.cs @@ -29,7 +29,7 @@ private void OnModuleGotInserted(EntityUid uid, BorgModuleComponent component, E if (!TryComp(chassis, out var chassisComp) || args.Container != chassisComp.ModuleContainer || - !chassisComp.Activated) + !Toggle.IsActivated(chassis)) return; if (!_powerCell.HasDrawCharge(uid)) @@ -143,6 +143,7 @@ public void SelectModule(EntityUid chassis, var ev = new BorgModuleSelectedEvent(chassis); RaiseLocalEvent(moduleUid, ref ev); chassisComp.SelectedModule = moduleUid; + Dirty(chassis, chassisComp); } /// @@ -162,6 +163,7 @@ public void UnselectModule(EntityUid chassis, BorgChassisComponent? chassisComp var ev = new BorgModuleUnselectedEvent(chassis); RaiseLocalEvent(chassisComp.SelectedModule.Value, ref ev); chassisComp.SelectedModule = null; + Dirty(chassis, chassisComp); } private void OnItemModuleSelected(EntityUid uid, ItemBorgModuleComponent component, ref BorgModuleSelectedEvent args) diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index c97ca9cbc0d..1c40e9489eb 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Database; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Mind; using Content.Shared.Mind.Components; using Content.Shared.Mobs; @@ -73,6 +74,7 @@ public override void Initialize() SubscribeLocalEvent(OnPowerCellChanged); SubscribeLocalEvent(OnPowerCellSlotEmpty); SubscribeLocalEvent(OnGetDeadIC); + SubscribeLocalEvent(OnToggled); SubscribeLocalEvent(OnBrainMindAdded); SubscribeLocalEvent(OnBrainPointAttempt); @@ -173,11 +175,11 @@ private void OnMobStateChanged(EntityUid uid, BorgChassisComponent component, Mo if (args.NewMobState == MobState.Alive) { if (_mind.TryGetMind(uid, out _, out _)) - _powerCell.SetPowerCellDrawEnabled(uid, true); + _powerCell.SetDrawEnabled(uid, true); } else { - _powerCell.SetPowerCellDrawEnabled(uid, false); + _powerCell.SetDrawEnabled(uid, false); } } @@ -185,24 +187,10 @@ private void OnPowerCellChanged(EntityUid uid, BorgChassisComponent component, P { UpdateBatteryAlert((uid, component)); - if (!TryComp(uid, out var draw)) - return; - - // if we eject the battery or run out of charge, then disable - if (args.Ejected || !_powerCell.HasDrawCharge(uid)) - { - DisableBorgAbilities(uid, component); - return; - } - // if we aren't drawing and suddenly get enough power to draw again, reeanble. - if (_powerCell.HasDrawCharge(uid, draw)) + if (_powerCell.HasDrawCharge(uid)) { - // only reenable the powerdraw if a player has the role. - if (!draw.Drawing && _mind.TryGetMind(uid, out _, out _) && _mobState.IsAlive(uid)) - _powerCell.SetPowerCellDrawEnabled(uid, true); - - EnableBorgAbilities(uid, component); + Toggle.TryActivate(uid); } UpdateUI(uid, component); @@ -210,7 +198,7 @@ private void OnPowerCellChanged(EntityUid uid, BorgChassisComponent component, P private void OnPowerCellSlotEmpty(EntityUid uid, BorgChassisComponent component, ref PowerCellSlotEmptyEvent args) { - DisableBorgAbilities(uid, component); + Toggle.TryDeactivate(uid); UpdateUI(uid, component); } @@ -219,6 +207,23 @@ private void OnGetDeadIC(EntityUid uid, BorgChassisComponent component, ref GetC args.Dead = true; } + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + var (uid, comp) = ent; + if (args.Activated) + InstallAllModules(uid, comp); + else + DisableAllModules(uid, comp); + + // only enable the powerdraw if there is a player in the chassis + var drawing = _mind.TryGetMind(uid, out _, out _) && _mobState.IsAlive(ent); + _powerCell.SetDrawEnabled(uid, drawing); + + UpdateUI(uid, comp); + + _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); + } + private void OnBrainMindAdded(EntityUid uid, BorgBrainComponent component, MindAddedMessage args) { if (!Container.TryGetOuterContainer(uid, Transform(uid), out var container)) @@ -271,44 +276,14 @@ private void UpdateBatteryAlert(Entity ent, PowerCellSlotC _alerts.ShowAlert(ent, ent.Comp.BatteryAlert, chargePercent); } - /// - /// Activates the borg, enabling all of its modules. - /// - public void EnableBorgAbilities(EntityUid uid, BorgChassisComponent component, PowerCellDrawComponent? powerCell = null) - { - if (component.Activated) - return; - - component.Activated = true; - InstallAllModules(uid, component); - Dirty(uid, component); - _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); - } - - /// - /// Deactivates the borg, disabling all of its modules and decreasing its speed. - /// - public void DisableBorgAbilities(EntityUid uid, BorgChassisComponent component) - { - if (!component.Activated) - return; - - component.Activated = false; - DisableAllModules(uid, component); - Dirty(uid, component); - _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); - } - /// /// Activates a borg when a player occupies it /// public void BorgActivate(EntityUid uid, BorgChassisComponent component) { Popup.PopupEntity(Loc.GetString("borg-mind-added", ("name", Identity.Name(uid, EntityManager))), uid); - _powerCell.SetPowerCellDrawEnabled(uid, true); - _access.SetAccessEnabled(uid, true); + Toggle.TryActivate(uid); _appearance.SetData(uid, BorgVisuals.HasPlayer, true); - Dirty(uid, component); } /// @@ -317,10 +292,8 @@ public void BorgActivate(EntityUid uid, BorgChassisComponent component) public void BorgDeactivate(EntityUid uid, BorgChassisComponent component) { Popup.PopupEntity(Loc.GetString("borg-mind-removed", ("name", Identity.Name(uid, EntityManager))), uid); - _powerCell.SetPowerCellDrawEnabled(uid, false); - _access.SetAccessEnabled(uid, false); + Toggle.TryDeactivate(uid); _appearance.SetData(uid, BorgVisuals.HasPlayer, false); - Dirty(uid, component); } /// diff --git a/Content.Server/StationEvents/Components/NinjaSpawnRuleComponent.cs b/Content.Server/StationEvents/Components/NinjaSpawnRuleComponent.cs deleted file mode 100644 index d758247eca2..00000000000 --- a/Content.Server/StationEvents/Components/NinjaSpawnRuleComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Content.Server.StationEvents.Events; - -namespace Content.Server.StationEvents.Components; - -/// -/// Configuration component for the Space Ninja antag. -/// -[RegisterComponent, Access(typeof(NinjaSpawnRule))] -public sealed partial class NinjaSpawnRuleComponent : Component -{ - /// - /// Distance that the ninja spawns from the station's half AABB radius - /// - [DataField("spawnDistance")] - public float SpawnDistance = 20f; -} diff --git a/Content.Server/StationEvents/Components/SpaceSpawnRuleComponent.cs b/Content.Server/StationEvents/Components/SpaceSpawnRuleComponent.cs new file mode 100644 index 00000000000..a0168077fd6 --- /dev/null +++ b/Content.Server/StationEvents/Components/SpaceSpawnRuleComponent.cs @@ -0,0 +1,25 @@ +using Content.Server.StationEvents.Events; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; + +namespace Content.Server.StationEvents.Components; + +/// +/// Component for spawning antags in space around a station. +/// Requires AntagSelectionComponent. +/// +[RegisterComponent, Access(typeof(SpaceSpawnRule))] +public sealed partial class SpaceSpawnRuleComponent : Component +{ + /// + /// Distance that the entity spawns from the station's half AABB radius + /// + [DataField] + public float SpawnDistance = 20f; + + /// + /// Location that was picked. + /// + [DataField] + public MapCoordinates? Coords; +} diff --git a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs b/Content.Server/StationEvents/Events/SpaceSpawnRule.cs similarity index 53% rename from Content.Server/StationEvents/Events/NinjaSpawnRule.cs rename to Content.Server/StationEvents/Events/SpaceSpawnRule.cs index 9cbc193ce61..6fccaaa5cfc 100644 --- a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs +++ b/Content.Server/StationEvents/Events/SpaceSpawnRule.cs @@ -1,5 +1,5 @@ +using Content.Server.Antag; using Content.Server.GameTicking.Rules.Components; -using Content.Server.Ninja.Systems; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; using Content.Shared.GameTicking.Components; @@ -9,18 +9,28 @@ namespace Content.Server.StationEvents.Events; /// -/// Event for spawning a Space Ninja mid-game. +/// Station event component for spawning this rules antags in space around a station. /// -public sealed class NinjaSpawnRule : StationEventSystem +public sealed class SpaceSpawnRule : StationEventSystem { [Dependency] private readonly SharedTransformSystem _transform = default!; - protected override void Started(EntityUid uid, NinjaSpawnRuleComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args) + public override void Initialize() { - base.Started(uid, comp, gameRule, args); + base.Initialize(); + + SubscribeLocalEvent(OnSelectLocation); + } + + protected override void Added(EntityUid uid, SpaceSpawnRuleComponent comp, GameRuleComponent gameRule, GameRuleAddedEvent args) + { + base.Added(uid, comp, gameRule, args); if (!TryGetRandomStation(out var station)) + { + ForceEndSelf(uid, gameRule); return; + } var stationData = Comp(station.Value); @@ -28,22 +38,28 @@ protected override void Started(EntityUid uid, NinjaSpawnRuleComponent comp, Gam var gridUid = StationSystem.GetLargestGrid(stationData); if (gridUid == null || !TryComp(gridUid, out var grid)) { - Sawmill.Warning("Chosen station has no grids, cannot spawn space ninja!"); + Sawmill.Warning("Chosen station has no grids, cannot pick location for {ToPrettyString(uid):rule}"); + ForceEndSelf(uid, gameRule); return; } - // figure out its AABB size and use that as a guide to how far ninja should be + // figure out its AABB size and use that as a guide to how far the spawner should be var size = grid.LocalAABB.Size.Length() / 2; var distance = size + comp.SpawnDistance; var angle = RobustRandom.NextAngle(); // position relative to station center var location = angle.ToVec() * distance; - // create the spawner, the ninja will appear when a ghost has picked the role + // create the spawner! var xform = Transform(gridUid.Value); var position = _transform.GetWorldPosition(xform) + location; - var coords = new MapCoordinates(position, xform.MapID); - Sawmill.Info($"Creating ninja spawnpoint at {coords}"); - Spawn("SpawnPointGhostSpaceNinja", coords); + comp.Coords = new MapCoordinates(position, xform.MapID); + Sawmill.Info($"Picked location {comp.Coords} for {ToPrettyString(uid):rule}"); + } + + private void OnSelectLocation(Entity ent, ref AntagSelectLocationEvent args) + { + if (ent.Comp.Coords is {} coords) + args.Coordinates.Add(coords); } } diff --git a/Content.Server/Stunnable/Systems/StunbatonSystem.cs b/Content.Server/Stunnable/Systems/StunbatonSystem.cs index c1782efabaf..97dd2c7e735 100644 --- a/Content.Server/Stunnable/Systems/StunbatonSystem.cs +++ b/Content.Server/Stunnable/Systems/StunbatonSystem.cs @@ -19,7 +19,7 @@ public sealed class StunbatonSystem : SharedStunbatonSystem [Dependency] private readonly RiggableSystem _riggableSystem = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly BatterySystem _battery = default!; - [Dependency] private readonly SharedItemToggleSystem _itemToggle = default!; + [Dependency] private readonly ItemToggleSystem _itemToggle = default!; public override void Initialize() { diff --git a/Content.Server/Weapons/Misc/TetherGunSystem.cs b/Content.Server/Weapons/Misc/TetherGunSystem.cs index f6aafe376d6..2bf53d46f4b 100644 --- a/Content.Server/Weapons/Misc/TetherGunSystem.cs +++ b/Content.Server/Weapons/Misc/TetherGunSystem.cs @@ -1,4 +1,5 @@ using Content.Server.PowerCell; +using Content.Shared.Item.ItemToggle; using Content.Shared.PowerCell; using Content.Shared.Weapons.Misc; using Robust.Shared.Physics.Components; @@ -8,6 +9,7 @@ namespace Content.Server.Weapons.Misc; public sealed class TetherGunSystem : SharedTetherGunSystem { [Dependency] private readonly PowerCellSystem _cell = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; public override void Initialize() { @@ -36,12 +38,12 @@ protected override void StartTether(EntityUid gunUid, BaseForceGunComponent comp PhysicsComponent? targetPhysics = null, TransformComponent? targetXform = null) { base.StartTether(gunUid, component, target, user, targetPhysics, targetXform); - _cell.SetPowerCellDrawEnabled(gunUid, true); + _toggle.TryActivate(gunUid); } protected override void StopTether(EntityUid gunUid, BaseForceGunComponent component, bool land = true, bool transfer = false) { base.StopTether(gunUid, component, land, transfer); - _cell.SetPowerCellDrawEnabled(gunUid, false); + _toggle.TryDeactivate(gunUid); } } diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMagnetTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMagnetTriggerSystem.cs index c6f56a27508..a585a9ef452 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMagnetTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMagnetTriggerSystem.cs @@ -2,6 +2,7 @@ using Content.Server.Salvage; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Shared.Clothing; +using Content.Shared.Item.ItemToggle.Components; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -29,11 +30,11 @@ public override void Update(float frameTime) _toActivate.Clear(); - //assume that there's more instruments than artifacts - var query = EntityQueryEnumerator(); - while (query.MoveNext(out _, out var magboot, out var magXform)) + //assume that there's more magboots than artifacts + var query = EntityQueryEnumerator(); + while (query.MoveNext(out _, out var magboot, out var magXform, out var toggle)) { - if (!magboot.On) + if (!toggle.Activated) continue; var artiQuery = EntityQueryEnumerator(); diff --git a/Content.Shared/Access/Components/AccessToggleComponent.cs b/Content.Shared/Access/Components/AccessToggleComponent.cs new file mode 100644 index 00000000000..60a606ac7ea --- /dev/null +++ b/Content.Shared/Access/Components/AccessToggleComponent.cs @@ -0,0 +1,11 @@ +using Content.Shared.Access.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Access.Components; + +/// +/// Toggles an access provider with ItemToggle. +/// Requires . +/// +[RegisterComponent, NetworkedComponent, Access(typeof(AccessToggleSystem))] +public sealed partial class AccessToggleComponent : Component; diff --git a/Content.Shared/Access/Systems/AccessToggleSystem.cs b/Content.Shared/Access/Systems/AccessToggleSystem.cs new file mode 100644 index 00000000000..564aca06812 --- /dev/null +++ b/Content.Shared/Access/Systems/AccessToggleSystem.cs @@ -0,0 +1,21 @@ +using Content.Shared.Access.Components; +using Content.Shared.Item.ItemToggle.Components; + +namespace Content.Shared.Access.Systems; + +public sealed class AccessToggleSystem : EntitySystem +{ + [Dependency] private readonly SharedAccessSystem _access = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggled); + } + + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + _access.SetAccessEnabled(ent, args.Activated); + } +} diff --git a/Content.Shared/Beeper/Components/BeeperComponent.cs b/Content.Shared/Beeper/Components/BeeperComponent.cs index 54d242709c4..f6efbb10f3e 100644 --- a/Content.Shared/Beeper/Components/BeeperComponent.cs +++ b/Content.Shared/Beeper/Components/BeeperComponent.cs @@ -10,15 +10,12 @@ namespace Content.Shared.Beeper.Components; /// This is used for an item that beeps based on /// proximity to a specified component. /// +/// +/// Requires ItemToggleComponent to control it. +/// [RegisterComponent, NetworkedComponent, Access(typeof(BeeperSystem)), AutoGenerateComponentState] public sealed partial class BeeperComponent : Component { - /// - /// Whether or not it's on. - /// - [DataField, AutoNetworkedField] - public bool Enabled = true; - /// /// How much to scale the interval by (< 0 = min, > 1 = max) /// @@ -56,7 +53,7 @@ public sealed partial class BeeperComponent : Component /// Is the beep muted /// [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public bool IsMuted = false; + public bool IsMuted; /// /// The sound played when the locator beeps. diff --git a/Content.Shared/Beeper/Systems/BeeperSystem.cs b/Content.Shared/Beeper/Systems/BeeperSystem.cs index c51eef4da9d..a52e19f7552 100644 --- a/Content.Shared/Beeper/Systems/BeeperSystem.cs +++ b/Content.Shared/Beeper/Systems/BeeperSystem.cs @@ -1,5 +1,7 @@ using Content.Shared.Beeper.Components; using Content.Shared.FixedPoint; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; using Robust.Shared.Timing; @@ -11,34 +13,20 @@ namespace Content.Shared.Beeper.Systems; public sealed class BeeperSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly INetManager _net = default!; - - public override void Initialize() - { - } + [Dependency] private readonly ItemToggleSystem _toggle = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; public override void Update(float frameTime) { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var beeper)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var beeper, out var toggle)) { - if (!beeper.Enabled) - continue; - RunUpdate_Internal(uid, beeper); + if (toggle.Activated) + RunUpdate_Internal(uid, beeper); } } - public void SetEnable(EntityUid owner, bool isEnabled, BeeperComponent? beeper = null) - { - if (!Resolve(owner, ref beeper) || beeper.Enabled == isEnabled) - return; - beeper.Enabled = isEnabled; - - RunUpdate_Internal(owner, beeper); - Dirty(owner, beeper); - } - public void SetIntervalScaling(EntityUid owner, BeeperComponent beeper, FixedPoint2 newScaling) { newScaling = FixedPoint2.Clamp(newScaling, 0, 1); @@ -70,6 +58,7 @@ public void SetMute(EntityUid owner, bool isMuted, BeeperComponent? comp = null) if (!Resolve(owner, ref comp)) return; comp.IsMuted = isMuted; + Dirty(owner, comp); } private void UpdateBeepInterval(EntityUid owner, BeeperComponent beeper) @@ -91,19 +80,17 @@ public void ForceUpdate(EntityUid owner, BeeperComponent? beeper = null) private void RunUpdate_Internal(EntityUid owner, BeeperComponent beeper) { - if (!beeper.Enabled) - { + if (!_toggle.IsActivated(owner)) return; - } + UpdateBeepInterval(owner, beeper); if (beeper.NextBeep >= _timing.CurTime) return; + var beepEvent = new BeepPlayedEvent(beeper.IsMuted); RaiseLocalEvent(owner, ref beepEvent); if (!beeper.IsMuted && _net.IsServer) - { _audio.PlayPvs(beeper.BeepSound, owner); - } beeper.LastBeepTime = _timing.CurTime; } } diff --git a/Content.Shared/Beeper/Systems/ProximityBeeperSystem.cs b/Content.Shared/Beeper/Systems/ProximityBeeperSystem.cs index bd857d4c29b..ed3c6366c13 100644 --- a/Content.Shared/Beeper/Systems/ProximityBeeperSystem.cs +++ b/Content.Shared/Beeper/Systems/ProximityBeeperSystem.cs @@ -1,7 +1,6 @@ using Content.Shared.Beeper.Components; -using Content.Shared.Interaction.Events; +using Content.Shared.Item.ItemToggle; using Content.Shared.Pinpointer; -using Content.Shared.PowerCell; using Content.Shared.ProximityDetection; using Content.Shared.ProximityDetection.Components; using Content.Shared.ProximityDetection.Systems; @@ -9,20 +8,17 @@ namespace Content.Shared.Beeper.Systems; /// -/// This handles logic for implementing proximity beeper as a handheld tool /> +/// This handles controlling a beeper from proximity detector events. /// public sealed class ProximityBeeperSystem : EntitySystem { [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedPowerCellSystem _powerCell = default!; [Dependency] private readonly ProximityDetectionSystem _proximity = default!; [Dependency] private readonly BeeperSystem _beeper = default!; /// public override void Initialize() { - SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnPowerCellSlotEmpty); SubscribeLocalEvent(OnNewProximityTarget); SubscribeLocalEvent(OnProximityTargetUpdate); } @@ -33,82 +29,16 @@ private void OnProximityTargetUpdate(EntityUid owner, ProximityBeeperComponent p return; if (args.Target == null) { - _beeper.SetEnable(owner, false, beeper); + _beeper.SetMute(owner, true, beeper); return; } - _beeper.SetIntervalScaling(owner,args.Distance/args.Detector.Range, beeper); - _beeper.SetEnable(owner, true, beeper); - } - - private void OnNewProximityTarget(EntityUid owner, ProximityBeeperComponent proxBeeper, ref NewProximityTargetEvent args) - { - _beeper.SetEnable(owner, args.Target != null); - } - private void OnUseInHand(EntityUid uid, ProximityBeeperComponent proxBeeper, UseInHandEvent args) - { - if (args.Handled) - return; - args.Handled = TryToggle(uid, proxBeeper, user: args.User); + _beeper.SetIntervalScaling(owner, args.Distance / args.Detector.Range, beeper); + _beeper.SetMute(owner, false, beeper); } - private void OnPowerCellSlotEmpty(EntityUid uid, ProximityBeeperComponent beeper, ref PowerCellSlotEmptyEvent args) - { - if (_proximity.GetEnable(uid)) - TryDisable(uid); - } - public bool TryEnable(EntityUid owner, BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null, - PowerCellDrawComponent? draw = null,EntityUid? user = null) - { - if (!Resolve(owner, ref beeper, ref detector)) - return false; - if (Resolve(owner, ref draw, false) && !_powerCell.HasActivatableCharge(owner, battery: draw, user: user)) - return false; - Enable(owner, beeper, detector, draw); - return true; - } - private void Enable(EntityUid owner, BeeperComponent beeper, - ProximityDetectorComponent detector, PowerCellDrawComponent? draw) - { - _proximity.SetEnable(owner, true, detector); - _appearance.SetData(owner, ProximityBeeperVisuals.Enabled, true); - _powerCell.SetPowerCellDrawEnabled(owner, true, draw); - } - - - /// - /// Disables the proximity beeper - /// - public bool TryDisable(EntityUid owner,BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null, PowerCellDrawComponent? draw = null) - { - if (!Resolve(owner, ref beeper, ref detector)) - return false; - - if (!detector.Enabled) - return false; - Disable(owner, beeper, detector, draw); - return true; - } - private void Disable(EntityUid owner, BeeperComponent beeper, - ProximityDetectorComponent detector, PowerCellDrawComponent? draw) - { - _proximity.SetEnable(owner, false, detector); - _appearance.SetData(owner, ProximityBeeperVisuals.Enabled, false); - _beeper.SetEnable(owner, false, beeper); - _powerCell.SetPowerCellDrawEnabled(owner, false, draw); - } - - /// - /// toggles the proximity beeper - /// - public bool TryToggle(EntityUid owner, ProximityBeeperComponent? proxBeeper = null, BeeperComponent? beeper = null, ProximityDetectorComponent? detector = null, - PowerCellDrawComponent? draw = null, EntityUid? user = null) + private void OnNewProximityTarget(EntityUid owner, ProximityBeeperComponent proxBeeper, ref NewProximityTargetEvent args) { - if (!Resolve(owner, ref proxBeeper, ref beeper, ref detector)) - return false; - - return detector.Enabled - ? TryDisable(owner, beeper, detector, draw) - : TryEnable(owner, beeper, detector, draw,user); + _beeper.SetMute(owner, args.Target != null); } } diff --git a/Content.Shared/Charges/Systems/SharedChargesSystem.cs b/Content.Shared/Charges/Systems/SharedChargesSystem.cs index 5de1383cde0..7f95ef184e4 100644 --- a/Content.Shared/Charges/Systems/SharedChargesSystem.cs +++ b/Content.Shared/Charges/Systems/SharedChargesSystem.cs @@ -5,10 +5,14 @@ namespace Content.Shared.Charges.Systems; public abstract class SharedChargesSystem : EntitySystem { + protected EntityQuery Query; + public override void Initialize() { base.Initialize(); + Query = GetEntityQuery(); + SubscribeLocalEvent(OnExamine); } @@ -30,9 +34,9 @@ protected virtual void OnExamine(EntityUid uid, LimitedChargesComponent comp, Ex /// /// Tries to add a number of charges. If it over or underflows it will be clamped, wasting the extra charges. /// - public void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp = null) + public virtual void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp = null) { - if (!Resolve(uid, ref comp, false)) + if (!Query.Resolve(uid, ref comp, false)) return; var old = comp.Charges; @@ -47,7 +51,7 @@ public void AddCharges(EntityUid uid, int change, LimitedChargesComponent? comp public bool IsEmpty(EntityUid uid, LimitedChargesComponent? comp = null) { // can't be empty if there are no limited charges - if (!Resolve(uid, ref comp, false)) + if (!Query.Resolve(uid, ref comp, false)) return false; return comp.Charges <= 0; @@ -56,10 +60,24 @@ public bool IsEmpty(EntityUid uid, LimitedChargesComponent? comp = null) /// /// Uses a single charge. Must check IsEmpty beforehand to prevent using with 0 charge. /// - public virtual void UseCharge(EntityUid uid, LimitedChargesComponent? comp = null) + public void UseCharge(EntityUid uid, LimitedChargesComponent? comp = null) + { + AddCharges(uid, -1, comp); + } + + /// + /// Checks IsEmpty and uses a charge if it isn't empty. + /// + public bool TryUseCharge(Entity ent) { - if (Resolve(uid, ref comp, false)) - AddCharges(uid, -1, comp); + if (!Query.Resolve(ent, ref ent.Comp, false)) + return true; + + if (IsEmpty(ent, ent.Comp)) + return false; + + UseCharge(ent, ent.Comp); + return true; } /// @@ -80,7 +98,6 @@ public bool HasInsufficientCharges(EntityUid uid, int requiredCharges, LimitedCh /// public virtual void UseCharges(EntityUid uid, int chargesUsed, LimitedChargesComponent? comp = null) { - if (Resolve(uid, ref comp, false)) - AddCharges(uid, -chargesUsed, comp); + AddCharges(uid, -chargesUsed, comp); } } diff --git a/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs b/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs index c3c4baf19d0..866ce38a572 100644 --- a/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs +++ b/Content.Shared/Clothing/ClothingSpeedModifierComponent.cs @@ -3,20 +3,18 @@ namespace Content.Shared.Clothing; +/// +/// Modifies speed when worn and activated. +/// Supports ItemToggleComponent. +/// [RegisterComponent, NetworkedComponent, Access(typeof(ClothingSpeedModifierSystem))] public sealed partial class ClothingSpeedModifierComponent : Component { - [DataField("walkModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)] + [DataField] public float WalkModifier = 1.0f; - [DataField("sprintModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)] + [DataField] public float SprintModifier = 1.0f; - - /// - /// Is this clothing item currently 'actively' slowing you down? - /// e.g. magboots can be turned on and off. - /// - [DataField("enabled")] public bool Enabled = true; } [Serializable, NetSerializable] @@ -25,12 +23,9 @@ public sealed class ClothingSpeedModifierComponentState : ComponentState public float WalkModifier; public float SprintModifier; - public bool Enabled; - - public ClothingSpeedModifierComponentState(float walkModifier, float sprintModifier, bool enabled) + public ClothingSpeedModifierComponentState(float walkModifier, float sprintModifier) { WalkModifier = walkModifier; SprintModifier = sprintModifier; - Enabled = enabled; } } diff --git a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs index 4198c5c165b..c1efe0b3ddd 100644 --- a/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs +++ b/Content.Shared/Clothing/ClothingSpeedModifierSystem.cs @@ -1,11 +1,10 @@ -using Content.Shared.Actions; using Content.Shared.Clothing.Components; using Content.Shared.Examine; -using Content.Shared.IdentityManagement; using Content.Shared.Inventory; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Movement.Systems; using Content.Shared.PowerCell; -using Content.Shared.Toggleable; using Content.Shared.Verbs; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -15,12 +14,12 @@ namespace Content.Shared.Clothing; public sealed class ClothingSpeedModifierSystem : EntitySystem { - [Dependency] private readonly SharedActionsSystem _actions = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly ClothingSpeedModifierSystem _clothingSpeedModifier = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedPowerCellSystem _powerCell = default!; public override void Initialize() @@ -31,39 +30,12 @@ public override void Initialize() SubscribeLocalEvent(OnHandleState); SubscribeLocalEvent>(OnRefreshMoveSpeed); SubscribeLocalEvent>(OnClothingVerbExamine); - - SubscribeLocalEvent>(AddToggleVerb); - SubscribeLocalEvent(OnGetActions); - SubscribeLocalEvent(OnToggleSpeed); - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnPowerCellSlotEmpty); + SubscribeLocalEvent(OnToggled); } - // Public API - - public void SetClothingSpeedModifierEnabled(EntityUid uid, bool enabled, ClothingSpeedModifierComponent? component = null) - { - if (!Resolve(uid, ref component, false)) - return; - - if (component.Enabled != enabled) - { - component.Enabled = enabled; - Dirty(uid, component); - - // inventory system will automatically hook into the event raised by this and update accordingly - if (_container.TryGetContainingContainer(uid, out var container)) - { - _movementSpeed.RefreshMovementSpeedModifiers(container.Owner); - } - } - } - - // Event handlers - private void OnGetState(EntityUid uid, ClothingSpeedModifierComponent component, ref ComponentGetState args) { - args.State = new ClothingSpeedModifierComponentState(component.WalkModifier, component.SprintModifier, component.Enabled); + args.State = new ClothingSpeedModifierComponentState(component.WalkModifier, component.SprintModifier); } private void OnHandleState(EntityUid uid, ClothingSpeedModifierComponent component, ref ComponentHandleState args) @@ -71,13 +43,11 @@ private void OnHandleState(EntityUid uid, ClothingSpeedModifierComponent compone if (args.Current is not ClothingSpeedModifierComponentState state) return; - var diff = component.Enabled != state.Enabled || - !MathHelper.CloseTo(component.SprintModifier, state.SprintModifier) || + var diff = !MathHelper.CloseTo(component.SprintModifier, state.SprintModifier) || !MathHelper.CloseTo(component.WalkModifier, state.WalkModifier); component.WalkModifier = state.WalkModifier; component.SprintModifier = state.SprintModifier; - component.Enabled = state.Enabled; // Avoid raising the event for the container if nothing changed. // We'll still set the values in case they're slightly different but within tolerance. @@ -89,10 +59,8 @@ private void OnHandleState(EntityUid uid, ClothingSpeedModifierComponent compone private void OnRefreshMoveSpeed(EntityUid uid, ClothingSpeedModifierComponent component, InventoryRelayedEvent args) { - if (!component.Enabled) - return; - - args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); + if (_toggle.IsActivated(uid)) + args.Args.ModifySpeed(component.WalkModifier, component.SprintModifier); } private void OnClothingVerbExamine(EntityUid uid, ClothingSpeedModifierComponent component, GetVerbsEvent args) @@ -142,60 +110,15 @@ private void OnClothingVerbExamine(EntityUid uid, ClothingSpeedModifierComponent _examine.AddDetailedExamineVerb(args, component, msg, Loc.GetString("clothing-speed-examinable-verb-text"), "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png", Loc.GetString("clothing-speed-examinable-verb-message")); } - private void OnMapInit(Entity uid, ref MapInitEvent args) - { - _actions.AddAction(uid, ref uid.Comp.ToggleActionEntity, uid.Comp.ToggleAction); - } - - private void OnToggleSpeed(Entity uid, ref ToggleClothingSpeedEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - SetSpeedToggleEnabled(uid, !uid.Comp.Enabled, args.Performer); - } - - private void SetSpeedToggleEnabled(Entity uid, bool value, EntityUid? user) + private void OnToggled(Entity ent, ref ItemToggledEvent args) { - if (uid.Comp.Enabled == value) - return; - - TryComp(uid, out var draw); - if (value && !_powerCell.HasDrawCharge(uid, draw, user: user)) - return; + // make sentient boots slow or fast too + _movementSpeed.RefreshMovementSpeedModifiers(ent); - uid.Comp.Enabled = value; - - _appearance.SetData(uid, ToggleVisuals.Toggled, uid.Comp.Enabled); - _actions.SetToggled(uid.Comp.ToggleActionEntity, uid.Comp.Enabled); - _clothingSpeedModifier.SetClothingSpeedModifierEnabled(uid.Owner, uid.Comp.Enabled); - _powerCell.SetPowerCellDrawEnabled(uid, uid.Comp.Enabled, draw); - Dirty(uid, uid.Comp); - } - - private void AddToggleVerb(Entity uid, ref GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - var user = args.User; - ActivationVerb verb = new() + if (_container.TryGetContainingContainer(ent.Owner, out var container)) { - Text = Loc.GetString("toggle-clothing-verb-text", - ("entity", Identity.Entity(uid, EntityManager))), - Act = () => SetSpeedToggleEnabled(uid, !uid.Comp.Enabled, user) - }; - args.Verbs.Add(verb); - } - - private void OnGetActions(Entity uid, ref GetItemActionsEvent args) - { - args.AddAction(ref uid.Comp.ToggleActionEntity, uid.Comp.ToggleAction); - } - - private void OnPowerCellSlotEmpty(Entity uid, ref PowerCellSlotEmptyEvent args) - { - SetSpeedToggleEnabled(uid, false, null); + // inventory system will automatically hook into the event raised by this and update accordingly + _movementSpeed.RefreshMovementSpeedModifiers(container.Owner); + } } } diff --git a/Content.Shared/Clothing/Components/StealthClothingComponent.cs b/Content.Shared/Clothing/Components/StealthClothingComponent.cs deleted file mode 100644 index fedf48b36ed..00000000000 --- a/Content.Shared/Clothing/Components/StealthClothingComponent.cs +++ /dev/null @@ -1,43 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Clothing.EntitySystems; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Clothing.Components; - -/// -/// Adds StealthComponent to the user when enabled, either by an action or the system's SetEnabled method. -/// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true), Access(typeof(StealthClothingSystem))] -public sealed partial class StealthClothingComponent : Component -{ - /// - /// Whether stealth effect is enabled. - /// - [DataField("enabled"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public bool Enabled; - - /// - /// Number added to MinVisibility when stealthed, to make the user not fully invisible. - /// - [DataField("visibility"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public float Visibility; - - [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ToggleAction = "ActionTogglePhaseCloak"; - - /// - /// The action for enabling and disabling stealth. - /// - [DataField, AutoNetworkedField] public EntityUid? ToggleActionEntity; -} - -/// -/// When stealth is enabled, disables it. -/// When it is disabled, raises before enabling. -/// Put any checks in a handler for that event to cancel it. -/// -public sealed partial class ToggleStealthEvent : InstantActionEvent -{ -} diff --git a/Content.Shared/Clothing/Components/ToggleClothingComponent.cs b/Content.Shared/Clothing/Components/ToggleClothingComponent.cs new file mode 100644 index 00000000000..c77aa03475f --- /dev/null +++ b/Content.Shared/Clothing/Components/ToggleClothingComponent.cs @@ -0,0 +1,40 @@ +using Content.Shared.Actions; +using Content.Shared.Clothing.EntitySystems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Clothing.Components; + +/// +/// Clothing that can be enabled and disabled with an action. +/// Requires . +/// +/// +/// Not to be confused with for hardsuit helmets and such. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(ToggleClothingSystem))] +public sealed partial class ToggleClothingComponent : Component +{ + /// + /// The action to add when equipped, even if not worn. + /// This must raise to then get handled. + /// + [DataField(required: true)] + public EntProtoId Action = string.Empty; + + [DataField, AutoNetworkedField] + public EntityUid? ActionEntity; + + /// + /// If true, automatically disable the clothing after unequipping it. + /// + [DataField] + public bool DisableOnUnequip; +} + +/// +/// Raised on the clothing when being equipped to see if it should add the action. +/// +[ByRefEvent] +public record struct ToggleClothingCheckEvent(EntityUid User, bool Cancelled = false); diff --git a/Content.Shared/Clothing/Components/ToggleClothingSpeedComponent.cs b/Content.Shared/Clothing/Components/ToggleClothingSpeedComponent.cs deleted file mode 100644 index 90b2d7322e0..00000000000 --- a/Content.Shared/Clothing/Components/ToggleClothingSpeedComponent.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Content.Shared.Actions; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Clothing.Components; - -/// -/// This is used for a clothing item that gives a speed modification that is toggleable. -/// -[RegisterComponent, NetworkedComponent, Access(typeof(ClothingSpeedModifierSystem)), AutoGenerateComponentState] -public sealed partial class ToggleClothingSpeedComponent : Component -{ - /// - /// The action for toggling the clothing. - /// - [DataField] - public EntProtoId ToggleAction = "ActionToggleSpeedBoots"; - - /// - /// The action entity - /// - [DataField, AutoNetworkedField] - public EntityUid? ToggleActionEntity; - - /// - /// The state of the toggle. - /// - [DataField, AutoNetworkedField] - public bool Enabled; -} - -public sealed partial class ToggleClothingSpeedEvent : InstantActionEvent -{ - -} diff --git a/Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs deleted file mode 100644 index e96d9f866aa..00000000000 --- a/Content.Shared/Clothing/EntitySystems/StealthClothingSystem.cs +++ /dev/null @@ -1,144 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Clothing.Components; -using Content.Shared.Inventory.Events; -using Content.Shared.Stealth; -using Content.Shared.Stealth.Components; - -namespace Content.Shared.Clothing.EntitySystems; - -/// -/// Handles the toggle action and disables stealth when clothing is unequipped. -/// -public sealed class StealthClothingSystem : EntitySystem -{ - [Dependency] private readonly SharedStealthSystem _stealth = default!; - [Dependency] private readonly ActionContainerSystem _actionContainer = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnGetItemActions); - SubscribeLocalEvent(OnToggleStealth); - SubscribeLocalEvent(OnHandleState); - SubscribeLocalEvent(OnUnequipped); - SubscribeLocalEvent(OnMapInit); - } - - private void OnMapInit(EntityUid uid, StealthClothingComponent component, MapInitEvent args) - { - _actionContainer.EnsureAction(uid, ref component.ToggleActionEntity, component.ToggleAction); - Dirty(uid, component); - } - - /// - /// Sets the clothing's stealth effect for the user. - /// - /// True if it was changed, false otherwise - public bool SetEnabled(EntityUid uid, EntityUid user, bool enabled, StealthClothingComponent? comp = null) - { - if (!Resolve(uid, ref comp) || comp.Enabled == enabled) - return false; - - // TODO remove this when clothing unequip on delete is less sus - // prevent debug assert when ending round and its disabled - if (MetaData(user).EntityLifeStage >= EntityLifeStage.Terminating) - return false; - - comp.Enabled = enabled; - Dirty(uid, comp); - - var stealth = EnsureComp(user); - // slightly visible, but doesn't change when moving so it's ok - var visibility = enabled ? stealth.MinVisibility + comp.Visibility : stealth.MaxVisibility; - _stealth.SetVisibility(user, visibility, stealth); - _stealth.SetEnabled(user, enabled, stealth); - return true; - } - - /// - /// Raise then add the toggle action if it was not cancelled. - /// - private void OnGetItemActions(EntityUid uid, StealthClothingComponent comp, GetItemActionsEvent args) - { - var ev = new AddStealthActionEvent(args.User); - RaiseLocalEvent(uid, ev); - if (ev.Cancelled) - return; - - args.AddAction(ref comp.ToggleActionEntity, comp.ToggleAction); - } - - /// - /// Raises if enabling. - /// - private void OnToggleStealth(EntityUid uid, StealthClothingComponent comp, ToggleStealthEvent args) - { - args.Handled = true; - var user = args.Performer; - if (comp.Enabled) - { - SetEnabled(uid, user, false, comp); - return; - } - - var ev = new AttemptStealthEvent(user); - RaiseLocalEvent(uid, ev); - if (ev.Cancelled) - return; - - SetEnabled(uid, user, true, comp); - } - - /// - /// Calls when server sends new state. - /// - private void OnHandleState(EntityUid uid, StealthClothingComponent comp, ref AfterAutoHandleStateEvent args) - { - // SetEnabled checks if it is the same, so change it to before state was received from the server - var enabled = comp.Enabled; - comp.Enabled = !enabled; - var user = Transform(uid).ParentUid; - SetEnabled(uid, user, enabled, comp); - } - - /// - /// Force unstealths the user, doesnt remove StealthComponent since other things might use it - /// - private void OnUnequipped(EntityUid uid, StealthClothingComponent comp, GotUnequippedEvent args) - { - SetEnabled(uid, args.Equipee, false, comp); - } -} - -/// -/// Raised on the stealth clothing when attempting to add an action. -/// -public sealed class AddStealthActionEvent : CancellableEntityEventArgs -{ - /// - /// User that equipped the stealth clothing. - /// - public EntityUid User; - - public AddStealthActionEvent(EntityUid user) - { - User = user; - } -} - -/// -/// Raised on the stealth clothing when the user is attemping to enable it. -/// -public sealed class AttemptStealthEvent : CancellableEntityEventArgs -{ - /// - /// User that is attempting to enable the stealth clothing. - /// - public EntityUid User; - - public AttemptStealthEvent(EntityUid user) - { - User = user; - } -} diff --git a/Content.Shared/Clothing/EntitySystems/ToggleClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/ToggleClothingSystem.cs new file mode 100644 index 00000000000..9889376c9d4 --- /dev/null +++ b/Content.Shared/Clothing/EntitySystems/ToggleClothingSystem.cs @@ -0,0 +1,58 @@ +using Content.Shared.Actions; +using Content.Shared.Clothing; +using Content.Shared.Clothing.Components; +using Content.Shared.Inventory; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Toggleable; + +namespace Content.Shared.Clothing.EntitySystems; + +/// +/// Handles adding and using a toggle action for . +/// +public sealed class ToggleClothingSystem : EntitySystem +{ + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnGetActions); + SubscribeLocalEvent(OnToggleAction); + SubscribeLocalEvent(OnUnequipped); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + var (uid, comp) = ent; + // test funny + if (string.IsNullOrEmpty(comp.Action)) + return; + + _actions.AddAction(uid, ref comp.ActionEntity, comp.Action); + _actions.SetToggled(comp.ActionEntity, _toggle.IsActivated(ent.Owner)); + Dirty(uid, comp); + } + + private void OnGetActions(Entity ent, ref GetItemActionsEvent args) + { + var ev = new ToggleClothingCheckEvent(args.User); + RaiseLocalEvent(ent, ref ev); + if (!ev.Cancelled) + args.AddAction(ent.Comp.ActionEntity); + } + + private void OnToggleAction(Entity ent, ref ToggleActionEvent args) + { + args.Handled = _toggle.Toggle(ent.Owner, args.Performer); + } + + private void OnUnequipped(Entity ent, ref ClothingGotUnequippedEvent args) + { + if (ent.Comp.DisableOnUnequip) + _toggle.TryDeactivate(ent.Owner, args.Wearer); + } +} diff --git a/Content.Shared/Clothing/MagbootsComponent.cs b/Content.Shared/Clothing/MagbootsComponent.cs index b3fb607a38b..4bef74fd335 100644 --- a/Content.Shared/Clothing/MagbootsComponent.cs +++ b/Content.Shared/Clothing/MagbootsComponent.cs @@ -1,23 +1,13 @@ using Content.Shared.Alert; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Shared.Clothing; -[RegisterComponent, NetworkedComponent(), AutoGenerateComponentState] +[RegisterComponent, NetworkedComponent] [Access(typeof(SharedMagbootsSystem))] public sealed partial class MagbootsComponent : Component { - [DataField] - public EntProtoId ToggleAction = "ActionToggleMagboots"; - - [DataField, AutoNetworkedField] - public EntityUid? ToggleActionEntity; - - [DataField("on"), AutoNetworkedField] - public bool On; - [DataField] public ProtoId MagbootsAlert = "Magboots"; @@ -26,4 +16,10 @@ public sealed partial class MagbootsComponent : Component /// [DataField] public bool RequiresGrid = true; + + /// + /// Slot the clothing has to be worn in to work. + /// + [DataField] + public string Slot = "shoes"; } diff --git a/Content.Shared/Clothing/MagbootsSystem.cs b/Content.Shared/Clothing/MagbootsSystem.cs new file mode 100644 index 00000000000..88d987aae18 --- /dev/null +++ b/Content.Shared/Clothing/MagbootsSystem.cs @@ -0,0 +1,90 @@ +using Content.Shared.Actions; +using Content.Shared.Alert; +using Content.Shared.Atmos.Components; +using Content.Shared.Clothing.EntitySystems; +using Content.Shared.Gravity; +using Content.Shared.Inventory; +using Content.Shared.Item; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; +using Robust.Shared.Containers; + +namespace Content.Shared.Clothing; + +public sealed class SharedMagbootsSystem : EntitySystem +{ + [Dependency] private readonly AlertsSystem _alerts = default!; + [Dependency] private readonly ClothingSystem _clothing = default!; + [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly SharedItemSystem _item = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggled); + SubscribeLocalEvent(OnGotEquipped); + SubscribeLocalEvent(OnGotUnequipped); + SubscribeLocalEvent(OnIsWeightless); + SubscribeLocalEvent>(OnIsWeightless); + } + + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + var (uid, comp) = ent; + // only stick to the floor if being worn in the correct slot + if (_container.TryGetContainingContainer(uid, out var container) && + _inventory.TryGetSlotEntity(container.Owner, comp.Slot, out var worn) + && uid == worn) + { + UpdateMagbootEffects(container.Owner, ent, args.Activated); + } + + var prefix = args.Activated ? "on" : null; + _item.SetHeldPrefix(ent, prefix); + _clothing.SetEquippedPrefix(ent, prefix); + } + + private void OnGotUnequipped(Entity ent, ref ClothingGotUnequippedEvent args) + { + UpdateMagbootEffects(args.Wearer, ent, false); + } + + private void OnGotEquipped(Entity ent, ref ClothingGotEquippedEvent args) + { + UpdateMagbootEffects(args.Wearer, ent, _toggle.IsActivated(ent.Owner)); + } + + public void UpdateMagbootEffects(EntityUid user, Entity ent, bool state) + { + // TODO: public api for this and add access + if (TryComp(user, out var moved)) + moved.Enabled = !state; + + if (state) + _alerts.ShowAlert(user, ent.Comp.MagbootsAlert); + else + _alerts.ClearAlert(user, ent.Comp.MagbootsAlert); + } + + private void OnIsWeightless(Entity ent, ref IsWeightlessEvent args) + { + if (args.Handled || !_toggle.IsActivated(ent.Owner)) + return; + + // do not cancel weightlessness if the person is in off-grid. + if (ent.Comp.RequiresGrid && !_gravity.EntityOnGravitySupportingGridOrMap(ent.Owner)) + return; + + args.IsWeightless = false; + args.Handled = true; + } + + private void OnIsWeightless(Entity ent, ref InventoryRelayedEvent args) + { + OnIsWeightless(ent, ref args.Args); + } +} diff --git a/Content.Shared/Clothing/SharedMagbootsSystem.cs b/Content.Shared/Clothing/SharedMagbootsSystem.cs deleted file mode 100644 index 68145936152..00000000000 --- a/Content.Shared/Clothing/SharedMagbootsSystem.cs +++ /dev/null @@ -1,160 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Alert; -using Content.Shared.Atmos.Components; -using Content.Shared.Clothing.EntitySystems; -using Content.Shared.Gravity; -using Content.Shared.Inventory; -using Content.Shared.Item; -using Content.Shared.Slippery; -using Content.Shared.Toggleable; -using Content.Shared.Verbs; -using Robust.Shared.Containers; - -namespace Content.Shared.Clothing; - -public sealed class SharedMagbootsSystem : EntitySystem -{ - [Dependency] private readonly AlertsSystem _alerts = default!; - [Dependency] private readonly ClothingSpeedModifierSystem _clothingSpeedModifier = default!; - [Dependency] private readonly ClothingSystem _clothing = default!; - [Dependency] private readonly SharedGravitySystem _gravity = default!; - [Dependency] private readonly InventorySystem _inventory = default!; - [Dependency] private readonly SharedActionsSystem _sharedActions = default!; - [Dependency] private readonly SharedActionsSystem _actionContainer = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedContainerSystem _sharedContainer = default!; - [Dependency] private readonly SharedItemSystem _item = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent>(AddToggleVerb); - SubscribeLocalEvent>(OnSlipAttempt); - SubscribeLocalEvent(OnGetActions); - SubscribeLocalEvent(OnToggleMagboots); - SubscribeLocalEvent(OnMapInit); - - SubscribeLocalEvent(OnGotEquipped); - SubscribeLocalEvent(OnGotUnequipped); - - SubscribeLocalEvent>(OnIsWeightless); - } - - private void OnMapInit(EntityUid uid, MagbootsComponent component, MapInitEvent args) - { - _actionContainer.AddAction(uid, ref component.ToggleActionEntity, component.ToggleAction); - Dirty(uid, component); - } - - private void OnGotUnequipped(EntityUid uid, MagbootsComponent component, ref ClothingGotUnequippedEvent args) - { - UpdateMagbootEffects(args.Wearer, uid, false, component); - } - - private void OnGotEquipped(EntityUid uid, MagbootsComponent component, ref ClothingGotEquippedEvent args) - { - UpdateMagbootEffects(args.Wearer, uid, true, component); - } - - private void OnToggleMagboots(EntityUid uid, MagbootsComponent component, ToggleMagbootsEvent args) - { - if (args.Handled) - return; - - args.Handled = true; - - ToggleMagboots(uid, component); - } - - private void ToggleMagboots(EntityUid uid, MagbootsComponent magboots) - { - magboots.On = !magboots.On; - - if (_sharedContainer.TryGetContainingContainer((uid, Transform(uid)), out var container) && - _inventory.TryGetSlotEntity(container.Owner, "shoes", out var entityUid) && entityUid == uid) - { - UpdateMagbootEffects(container.Owner, uid, true, magboots); - } - - if (TryComp(uid, out var item)) - { - _item.SetHeldPrefix(uid, magboots.On ? "on" : null, component: item); - _clothing.SetEquippedPrefix(uid, magboots.On ? "on" : null); - } - - _appearance.SetData(uid, ToggleVisuals.Toggled, magboots.On); - OnChanged(uid, magboots); - Dirty(uid, magboots); - } - - public void UpdateMagbootEffects(EntityUid parent, EntityUid uid, bool state, MagbootsComponent? component) - { - if (!Resolve(uid, ref component)) - return; - state = state && component.On; - - if (TryComp(parent, out MovedByPressureComponent? movedByPressure)) - { - movedByPressure.Enabled = !state; - } - - if (state) - { - _alerts.ShowAlert(parent, component.MagbootsAlert); - } - else - { - _alerts.ClearAlert(parent, component.MagbootsAlert); - } - } - - private void OnChanged(EntityUid uid, MagbootsComponent component) - { - _sharedActions.SetToggled(component.ToggleActionEntity, component.On); - _clothingSpeedModifier.SetClothingSpeedModifierEnabled(uid, component.On); - } - - private void AddToggleVerb(EntityUid uid, MagbootsComponent component, GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - ActivationVerb verb = new() - { - Text = Loc.GetString("toggle-magboots-verb-get-data-text"), - Act = () => ToggleMagboots(uid, component), - // TODO VERB ICON add toggle icon? maybe a computer on/off symbol? - }; - args.Verbs.Add(verb); - } - - private void OnSlipAttempt(EntityUid uid, MagbootsComponent component, InventoryRelayedEvent args) - { - if (component.On) - args.Args.Cancel(); - } - - private void OnGetActions(EntityUid uid, MagbootsComponent component, GetItemActionsEvent args) - { - args.AddAction(ref component.ToggleActionEntity, component.ToggleAction); - } - - private void OnIsWeightless(Entity ent, ref InventoryRelayedEvent args) - { - if (args.Args.Handled) - return; - - if (!ent.Comp.On) - return; - - // do not cancel weightlessness if the person is in off-grid. - if (ent.Comp.RequiresGrid && !_gravity.EntityOnGravitySupportingGridOrMap(ent.Owner)) - return; - - args.Args.IsWeightless = false; - args.Args.Handled = true; - } -} - -public sealed partial class ToggleMagbootsEvent : InstantActionEvent; diff --git a/Content.Shared/Item/ItemToggle/ComponentTogglerSystem.cs b/Content.Shared/Item/ItemToggle/ComponentTogglerSystem.cs new file mode 100644 index 00000000000..760cefe27d4 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/ComponentTogglerSystem.cs @@ -0,0 +1,26 @@ +using Content.Shared.Item.ItemToggle.Components; + +namespace Content.Shared.Item.ItemToggle; + +/// +/// Handles component manipulation. +/// +public sealed class ComponentTogglerSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnToggled); + } + + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + var target = ent.Comp.Parent ? Transform(ent).ParentUid : ent.Owner; + + if (args.Activated) + EntityManager.AddComponents(target, ent.Comp.Components); + else + EntityManager.RemoveComponents(target, ent.Comp.RemoveComponents ?? ent.Comp.Components); + } +} diff --git a/Content.Shared/Item/ItemToggle/Components/ComponentTogglerComponent.cs b/Content.Shared/Item/ItemToggle/Components/ComponentTogglerComponent.cs new file mode 100644 index 00000000000..20ef0a02315 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/Components/ComponentTogglerComponent.cs @@ -0,0 +1,32 @@ +using Content.Shared.Item.ItemToggle; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Item.ItemToggle.Components; + +/// +/// Adds or removes components when toggled. +/// Requires . +/// +[RegisterComponent, NetworkedComponent, Access(typeof(ComponentTogglerSystem))] +public sealed partial class ComponentTogglerComponent : Component +{ + /// + /// The components to add when activated. + /// + [DataField(required: true)] + public ComponentRegistry Components = new(); + + /// + /// The components to remove when deactivated. + /// If this is null is reused. + /// + [DataField] + public ComponentRegistry? RemoveComponents; + + /// + /// If true, adds components on the entity's parent instead of the entity itself. + /// + [DataField] + public bool Parent; +} diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs index 6d534713578..cdac49ae6d6 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleActiveSoundComponent.cs @@ -12,12 +12,12 @@ public sealed partial class ItemToggleActiveSoundComponent : Component /// /// The continuous noise this item makes when it's activated (like an e-sword's hum). /// - [ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField] + [DataField(required: true), AutoNetworkedField] public SoundSpecifier? ActiveSound; /// /// Used when the item emits sound while active. /// - [ViewVariables(VVAccess.ReadWrite), DataField] + [DataField] public EntityUid? PlayingStream; } diff --git a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs index 620ddfd1942..46249fdd0de 100644 --- a/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs +++ b/Content.Shared/Item/ItemToggle/Components/ItemToggleComponent.cs @@ -8,7 +8,7 @@ namespace Content.Shared.Item.ItemToggle.Components; /// /// /// If you need extended functionality (e.g. requiring power) then add a new component and use events: -/// ItemToggleActivateAttemptEvent, ItemToggleDeactivateAttemptEvent or ItemToggleForceToggleEvent. +/// ItemToggleActivateAttemptEvent, ItemToggleDeactivateAttemptEvent, ItemToggledEvent. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ItemToggleComponent : Component @@ -19,6 +19,13 @@ public sealed partial class ItemToggleComponent : Component [DataField, AutoNetworkedField] public bool Activated = false; + /// + /// If this is set to false then the item can't be toggled by pressing Z. + /// Use another system to do it then. + /// + [DataField] + public bool OnUse = true; + /// /// Whether the item's toggle can be predicted by the client. /// diff --git a/Content.Shared/Item/ItemToggle/Components/ToggleVerbComponent.cs b/Content.Shared/Item/ItemToggle/Components/ToggleVerbComponent.cs new file mode 100644 index 00000000000..b673c55e0f1 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/Components/ToggleVerbComponent.cs @@ -0,0 +1,18 @@ +using Content.Shared.Item.ItemToggle; +using Robust.Shared.GameStates; + +namespace Content.Shared.Item.ItemToggle.Components; + +/// +/// Adds a verb for toggling something, requires . +/// +[RegisterComponent, NetworkedComponent, Access(typeof(ToggleVerbSystem))] +public sealed partial class ToggleVerbComponent : Component +{ + /// + /// Text the verb will have. + /// Gets passed "entity" as the entity's identity string. + /// + [DataField(required: true)] + public LocId Text = string.Empty; +} diff --git a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs similarity index 54% rename from Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs rename to Content.Shared/Item/ItemToggle/ItemToggleSystem.cs index 523f67bac3d..6b969d1d62b 100644 --- a/Content.Shared/Item/ItemToggle/SharedItemToggleSystem.cs +++ b/Content.Shared/Item/ItemToggle/ItemToggleSystem.cs @@ -15,12 +15,12 @@ namespace Content.Shared.Item.ItemToggle; /// /// If you need extended functionality (e.g. requiring power) then add a new component and use events. /// -public abstract class SharedItemToggleSystem : EntitySystem +public sealed class ItemToggleSystem : EntitySystem { - [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedPointLightSystem _light = default!; - [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; public override void Initialize() @@ -28,8 +28,9 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(TurnOffonUnwielded); - SubscribeLocalEvent(TurnOnonWielded); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(TurnOffOnUnwielded); + SubscribeLocalEvent(TurnOnOnWielded); SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnIsHotEvent); @@ -42,57 +43,76 @@ private void OnStartup(Entity ent, ref ComponentStartup arg UpdateVisuals(ent); } - private void OnUseInHand(EntityUid uid, ItemToggleComponent itemToggle, UseInHandEvent args) + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + if (!ent.Comp.Activated) + return; + + var ev = new ItemToggledEvent(Predicted: ent.Comp.Predictable, Activated: ent.Comp.Activated, User: null); + RaiseLocalEvent(ent, ref ev); + } + + private void OnUseInHand(Entity ent, ref UseInHandEvent args) { - if (args.Handled) + if (args.Handled || !ent.Comp.OnUse) return; args.Handled = true; - Toggle(uid, args.User, predicted: itemToggle.Predictable, itemToggle: itemToggle); + Toggle((ent, ent.Comp), args.User, predicted: ent.Comp.Predictable); } /// /// Used when an item is attempted to be toggled. + /// Sets its state to the opposite of what it is. /// - public void Toggle(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null) + /// Same as + public bool Toggle(Entity ent, EntityUid? user = null, bool predicted = true) { - if (!Resolve(uid, ref itemToggle)) - return; + if (!Resolve(ent, ref ent.Comp)) + return false; - if (itemToggle.Activated) - { - TryDeactivate(uid, user, itemToggle: itemToggle, predicted: predicted); - } + return TrySetActive(ent, !ent.Comp.Activated, user, predicted); + } + + /// + /// Tries to set the activated bool from a value. + /// + /// false if the attempt fails for any reason + public bool TrySetActive(Entity ent, bool active, EntityUid? user = null, bool predicted = true) + { + if (active) + return TryActivate(ent, user, predicted: predicted); else - { - TryActivate(uid, user, itemToggle: itemToggle, predicted: predicted); - } + return TryDeactivate(ent, user, predicted: predicted); } /// /// Used when an item is attempting to be activated. It returns false if the attempt fails any reason, interrupting the activation. /// - public bool TryActivate(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null) + public bool TryActivate(Entity ent, EntityUid? user = null, bool predicted = true) { - if (!Resolve(uid, ref itemToggle)) + if (!Resolve(ent, ref ent.Comp)) return false; - if (itemToggle.Activated) + var uid = ent.Owner; + var comp = ent.Comp; + if (comp.Activated) return true; - if (!itemToggle.Predictable && _netManager.IsClient) + if (!comp.Predictable && _netManager.IsClient) return true; var attempt = new ItemToggleActivateAttemptEvent(user); RaiseLocalEvent(uid, ref attempt); + if (!comp.Predictable) predicted = false; if (attempt.Cancelled) { if (predicted) - _audio.PlayPredicted(itemToggle.SoundFailToActivate, uid, user); + _audio.PlayPredicted(comp.SoundFailToActivate, uid, user); else - _audio.PlayPvs(itemToggle.SoundFailToActivate, uid); + _audio.PlayPvs(comp.SoundFailToActivate, uid); if (attempt.Popup != null && user != null) { @@ -105,7 +125,7 @@ public bool TryActivate(EntityUid uid, EntityUid? user = null, bool predicted = return false; } - Activate(uid, itemToggle, predicted, user); + Activate((uid, comp), predicted, user); return true; } @@ -113,75 +133,65 @@ public bool TryActivate(EntityUid uid, EntityUid? user = null, bool predicted = /// /// Used when an item is attempting to be deactivated. It returns false if the attempt fails any reason, interrupting the deactivation. /// - public bool TryDeactivate(EntityUid uid, EntityUid? user = null, bool predicted = true, ItemToggleComponent? itemToggle = null) + public bool TryDeactivate(Entity ent, EntityUid? user = null, bool predicted = true) { - if (!Resolve(uid, ref itemToggle)) + if (!Resolve(ent, ref ent.Comp)) return false; - if (!itemToggle.Predictable && _netManager.IsClient) + var uid = ent.Owner; + var comp = ent.Comp; + if (!comp.Activated) return true; - if (!itemToggle.Activated) + if (!comp.Predictable && _netManager.IsClient) return true; var attempt = new ItemToggleDeactivateAttemptEvent(user); RaiseLocalEvent(uid, ref attempt); if (attempt.Cancelled) - { return false; - } - Deactivate(uid, itemToggle, predicted, user); + if (!comp.Predictable) predicted = false; + Deactivate((uid, comp), predicted, user); return true; } - private void Activate(EntityUid uid, ItemToggleComponent itemToggle, bool predicted, EntityUid? user = null) + private void Activate(Entity ent, bool predicted, EntityUid? user = null) { - // TODO: Fix this hardcoding - TryComp(uid, out AppearanceComponent? appearance); - _appearance.SetData(uid, ToggleableLightVisuals.Enabled, true, appearance); - _appearance.SetData(uid, ToggleVisuals.Toggled, true, appearance); - - if (_light.TryGetLight(uid, out var light)) - { - _light.SetEnabled(uid, true, light); - } - - var soundToPlay = itemToggle.SoundActivate; + var (uid, comp) = ent; + var soundToPlay = comp.SoundActivate; if (predicted) _audio.PlayPredicted(soundToPlay, uid, user); else _audio.PlayPvs(soundToPlay, uid); - // END FIX HARDCODING + comp.Activated = true; + UpdateVisuals((uid, comp)); + Dirty(uid, comp); var toggleUsed = new ItemToggledEvent(predicted, Activated: true, user); RaiseLocalEvent(uid, ref toggleUsed); - - itemToggle.Activated = true; - UpdateVisuals((uid, itemToggle)); - Dirty(uid, itemToggle); } /// /// Used to make the actual changes to the item's components on deactivation. /// - private void Deactivate(EntityUid uid, ItemToggleComponent itemToggle, bool predicted, EntityUid? user = null) + private void Deactivate(Entity ent, bool predicted, EntityUid? user = null) { - var soundToPlay = itemToggle.SoundDeactivate; + var (uid, comp) = ent; + var soundToPlay = comp.SoundDeactivate; if (predicted) _audio.PlayPredicted(soundToPlay, uid, user); else _audio.PlayPvs(soundToPlay, uid); - // END FIX HARDCODING + + comp.Activated = false; + UpdateVisuals((uid, comp)); + Dirty(uid, comp); var toggleUsed = new ItemToggledEvent(predicted, Activated: false, user); RaiseLocalEvent(uid, ref toggleUsed); - - itemToggle.Activated = false; - UpdateVisuals((uid, itemToggle)); - Dirty(uid, itemToggle); } private void UpdateVisuals(Entity ent) @@ -204,55 +214,56 @@ private void UpdateVisuals(Entity ent) /// /// Used for items that require to be wielded in both hands to activate. For instance the dual energy sword will turn off if not wielded. /// - private void TurnOffonUnwielded(EntityUid uid, ItemToggleComponent itemToggle, ItemUnwieldedEvent args) + private void TurnOffOnUnwielded(Entity ent, ref ItemUnwieldedEvent args) { - if (itemToggle.Activated) - TryDeactivate(uid, args.User, itemToggle: itemToggle); + TryDeactivate((ent, ent.Comp), args.User); } /// /// Wieldable items will automatically turn on when wielded. /// - private void TurnOnonWielded(EntityUid uid, ItemToggleComponent itemToggle, ref ItemWieldedEvent args) + private void TurnOnOnWielded(Entity ent, ref ItemWieldedEvent args) { - if (!itemToggle.Activated) - TryActivate(uid, itemToggle: itemToggle); + // FIXME: for some reason both client and server play sound + TryActivate((ent, ent.Comp)); } - public bool IsActivated(EntityUid uid, ItemToggleComponent? comp = null) + public bool IsActivated(Entity ent) { - if (!Resolve(uid, ref comp, false)) + if (!Resolve(ent, ref ent.Comp, false)) return true; // assume always activated if no component - return comp.Activated; + return ent.Comp.Activated; } /// /// Used to make the item hot when activated. /// - private void OnIsHotEvent(EntityUid uid, ItemToggleHotComponent itemToggleHot, IsHotEvent args) + private void OnIsHotEvent(Entity ent, ref IsHotEvent args) { - args.IsHot |= IsActivated(uid); + args.IsHot |= IsActivated(ent.Owner); } /// /// Used to update the looping active sound linked to the entity. /// - private void UpdateActiveSound(EntityUid uid, ItemToggleActiveSoundComponent activeSound, ref ItemToggledEvent args) + private void UpdateActiveSound(Entity ent, ref ItemToggledEvent args) { - if (args.Activated) + var (uid, comp) = ent; + if (!args.Activated) { - if (activeSound.ActiveSound != null && activeSound.PlayingStream == null) - { - if (args.Predicted) - activeSound.PlayingStream = _audio.PlayPredicted(activeSound.ActiveSound, uid, args.User, AudioParams.Default.WithLoop(true)).Value.Entity; - else - activeSound.PlayingStream = _audio.PlayPvs(activeSound.ActiveSound, uid, AudioParams.Default.WithLoop(true)).Value.Entity; - } + comp.PlayingStream = _audio.Stop(comp.PlayingStream); + return; } - else + + if (comp.ActiveSound != null && comp.PlayingStream == null) { - activeSound.PlayingStream = _audio.Stop(activeSound.PlayingStream); + var loop = AudioParams.Default.WithLoop(true); + var stream = args.Predicted + ? _audio.PlayPredicted(comp.ActiveSound, uid, args.User, loop) + : _audio.PlayPvs(comp.ActiveSound, uid, loop); + if (stream?.Entity is {} entity) + comp.PlayingStream = entity; } } } diff --git a/Content.Shared/Item/ItemToggle/ToggleVerbSystem.cs b/Content.Shared/Item/ItemToggle/ToggleVerbSystem.cs new file mode 100644 index 00000000000..858cd9bc111 --- /dev/null +++ b/Content.Shared/Item/ItemToggle/ToggleVerbSystem.cs @@ -0,0 +1,34 @@ +using Content.Shared.IdentityManagement; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.Verbs; + +namespace Content.Shared.Item.ItemToggle; + +/// +/// Adds a verb for toggling something with . +/// +public sealed class ToggleVerbSystem : EntitySystem +{ + [Dependency] private readonly ItemToggleSystem _toggle = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent>(OnGetVerbs); + } + + private void OnGetVerbs(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + var name = Identity.Entity(ent, EntityManager); + var user = args.User; + args.Verbs.Add(new ActivationVerb() + { + Text = Loc.GetString(ent.Comp.Text, ("entity", name)), + Act = () => _toggle.Toggle(ent.Owner, user) + }); + } +} diff --git a/Content.Shared/Medical/DefibrillatorComponent.cs b/Content.Shared/Medical/DefibrillatorComponent.cs index 61a02187d09..e4cd8077d26 100644 --- a/Content.Shared/Medical/DefibrillatorComponent.cs +++ b/Content.Shared/Medical/DefibrillatorComponent.cs @@ -10,16 +10,11 @@ namespace Content.Shared.Medical; /// /// This is used for defibrillators; a machine that shocks a dead /// person back into the world of the living. +/// Uses ItemToggleComponent /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentPause] public sealed partial class DefibrillatorComponent : Component { - /// - /// Whether or not it's turned on and able to be used. - /// - [DataField("enabled"), ViewVariables(VVAccess.ReadWrite)] - public bool Enabled; - /// /// The time at which the zap cooldown will be completed /// @@ -72,15 +67,6 @@ public sealed partial class DefibrillatorComponent : Component [ViewVariables(VVAccess.ReadWrite), DataField("zapSound")] public SoundSpecifier? ZapSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_zap.ogg"); - /// - /// The sound when the defib is powered on. - /// - [ViewVariables(VVAccess.ReadWrite), DataField("powerOnSound")] - public SoundSpecifier? PowerOnSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_safety_on.ogg"); - - [ViewVariables(VVAccess.ReadWrite), DataField("powerOffSound")] - public SoundSpecifier? PowerOffSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_safety_off.ogg"); - [ViewVariables(VVAccess.ReadWrite), DataField("chargeSound")] public SoundSpecifier? ChargeSound = new SoundPathSpecifier("/Audio/Items/Defib/defib_charge.ogg"); diff --git a/Content.Shared/Ninja/Components/BatteryDrainerComponent.cs b/Content.Shared/Ninja/Components/BatteryDrainerComponent.cs index 55bcdd0f0a5..9c39c4724ce 100644 --- a/Content.Shared/Ninja/Components/BatteryDrainerComponent.cs +++ b/Content.Shared/Ninja/Components/BatteryDrainerComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Ninja.Systems; using Robust.Shared.Audio; +using Robust.Shared.GameStates; namespace Content.Shared.Ninja.Components; @@ -7,32 +8,33 @@ namespace Content.Shared.Ninja.Components; /// Component for draining power from APCs/substations/SMESes, when ProviderUid is set to a battery cell. /// Does not rely on relay, simply being on the user and having BatteryUid set is enough. /// -[RegisterComponent, Access(typeof(SharedBatteryDrainerSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedBatteryDrainerSystem))] public sealed partial class BatteryDrainerComponent : Component { /// /// The powercell entity to drain power into. /// Determines whether draining is possible. /// - [DataField("batteryUid"), ViewVariables(VVAccess.ReadWrite)] + [DataField, AutoNetworkedField] public EntityUid? BatteryUid; /// /// Conversion rate between joules in a device and joules added to battery. /// Should be very low since powercells store nothing compared to even an APC. /// - [DataField("drainEfficiency"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float DrainEfficiency = 0.001f; /// /// Time that the do after takes to drain charge from a battery, in seconds /// - [DataField("drainTime"), ViewVariables(VVAccess.ReadWrite)] + [DataField] public float DrainTime = 1f; /// /// Sound played after the doafter ends. /// - [DataField("sparkSound")] + [DataField] public SoundSpecifier SparkSound = new SoundCollectionSpecifier("sparks"); } diff --git a/Content.Shared/Ninja/Components/BombingTargetComponent.cs b/Content.Shared/Ninja/Components/BombingTargetComponent.cs index bf0eaec84be..c429eb6880e 100644 --- a/Content.Shared/Ninja/Components/BombingTargetComponent.cs +++ b/Content.Shared/Ninja/Components/BombingTargetComponent.cs @@ -4,6 +4,4 @@ namespace Content.Shared.Ninja.Components; /// Makes this warp point a valid bombing target for ninja's spider charge. /// [RegisterComponent] -public sealed partial class BombingTargetComponent : Component -{ -} +public sealed partial class BombingTargetComponent : Component; diff --git a/Content.Shared/Ninja/Components/DashAbilityComponent.cs b/Content.Shared/Ninja/Components/DashAbilityComponent.cs index ba4060c7035..464f48f187e 100644 --- a/Content.Shared/Ninja/Components/DashAbilityComponent.cs +++ b/Content.Shared/Ninja/Components/DashAbilityComponent.cs @@ -8,6 +8,7 @@ namespace Content.Shared.Ninja.Components; /// /// Adds an action to dash, teleport to clicked position, when this item is held. +/// Cancel to prevent using it. /// [RegisterComponent, NetworkedComponent, Access(typeof(DashAbilitySystem)), AutoGenerateComponentState] public sealed partial class DashAbilityComponent : Component @@ -16,19 +17,10 @@ public sealed partial class DashAbilityComponent : Component /// The action id for dashing. /// [DataField] - public EntProtoId DashAction = "ActionEnergyKatanaDash"; + public EntProtoId DashAction = "ActionEnergyKatanaDash"; [DataField, AutoNetworkedField] public EntityUid? DashActionEntity; - - /// - /// Sound played when using dash action. - /// - [DataField("blinkSound"), ViewVariables(VVAccess.ReadWrite)] - public SoundSpecifier BlinkSound = new SoundPathSpecifier("/Audio/Magic/blink.ogg") - { - Params = AudioParams.Default.WithVolume(5f) - }; } -public sealed partial class DashEvent : WorldTargetActionEvent { } +public sealed partial class DashEvent : WorldTargetActionEvent; diff --git a/Content.Shared/Ninja/Components/EmagProviderComponent.cs b/Content.Shared/Ninja/Components/EmagProviderComponent.cs index db7678f61d7..ae3e85cbe42 100644 --- a/Content.Shared/Ninja/Components/EmagProviderComponent.cs +++ b/Content.Shared/Ninja/Components/EmagProviderComponent.cs @@ -2,7 +2,7 @@ using Content.Shared.Tag; using Content.Shared.Whitelist; using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Robust.Shared.Prototypes; namespace Content.Shared.Ninja.Components; @@ -10,19 +10,18 @@ namespace Content.Shared.Ninja.Components; /// Component for emagging things on click. /// No charges but checks against a whitelist. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -[Access(typeof(EmagProviderSystem))] +[RegisterComponent, NetworkedComponent, Access(typeof(EmagProviderSystem))] public sealed partial class EmagProviderComponent : Component { /// /// The tag that marks an entity as immune to emagging. /// - [DataField("emagImmuneTag", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string EmagImmuneTag = "EmagImmune"; + [DataField] + public ProtoId EmagImmuneTag = "EmagImmune"; /// /// Whitelist that entities must be on to work. /// - [DataField("whitelist"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public EntityWhitelist? Whitelist = null; + [DataField] + public EntityWhitelist? Whitelist; } diff --git a/Content.Shared/Ninja/Components/EnergyKatanaComponent.cs b/Content.Shared/Ninja/Components/EnergyKatanaComponent.cs index 33b8fc78933..84c58bb6480 100644 --- a/Content.Shared/Ninja/Components/EnergyKatanaComponent.cs +++ b/Content.Shared/Ninja/Components/EnergyKatanaComponent.cs @@ -7,6 +7,4 @@ namespace Content.Shared.Ninja.Components; /// Requires a ninja with a suit for abilities to work. /// [RegisterComponent, NetworkedComponent] -public sealed partial class EnergyKatanaComponent : Component -{ -} +public sealed partial class EnergyKatanaComponent : Component; diff --git a/Content.Shared/Ninja/Components/ItemCreatorComponent.cs b/Content.Shared/Ninja/Components/ItemCreatorComponent.cs new file mode 100644 index 00000000000..d9f66d21a31 --- /dev/null +++ b/Content.Shared/Ninja/Components/ItemCreatorComponent.cs @@ -0,0 +1,52 @@ +using Content.Shared.Actions; +using Content.Shared.Ninja.Systems; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Ninja.Components; + +/// +/// Uses battery charge to spawn an item and place it in the user's hands. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedItemCreatorSystem))] +public sealed partial class ItemCreatorComponent : Component +{ + /// + /// The battery entity to use charge from + /// + [DataField, AutoNetworkedField] + public EntityUid? Battery; + + /// + /// The action id for creating an item. + /// + [DataField(required: true)] + public EntProtoId Action = string.Empty; + + [DataField, AutoNetworkedField] + public EntityUid? ActionEntity; + + /// + /// Battery charge used to create an item. + /// + [DataField(required: true)] + public float Charge = 14.4f; + + /// + /// Item to create with the action + /// + [DataField(required: true)] + public EntProtoId SpawnedPrototype = string.Empty; + + /// + /// Popup shown to the user when there isn't enough power to create an item. + /// + [DataField(required: true)] + public LocId NoPowerPopup = string.Empty; +} + +/// +/// Action event to use an . +/// +public sealed partial class CreateItemEvent : InstantActionEvent; diff --git a/Content.Shared/Ninja/Components/NinjaGlovesComponent.cs b/Content.Shared/Ninja/Components/NinjaGlovesComponent.cs index 7b57926330b..3b9e2a5e356 100644 --- a/Content.Shared/Ninja/Components/NinjaGlovesComponent.cs +++ b/Content.Shared/Ninja/Components/NinjaGlovesComponent.cs @@ -1,20 +1,17 @@ -using Content.Shared.DoAfter; using Content.Shared.Ninja.Systems; -using Content.Shared.Toggleable; -using Content.Shared.Whitelist; +using Content.Shared.Objectives.Components; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Utility; namespace Content.Shared.Ninja.Components; /// /// Component for toggling glove powers. -/// Powers being enabled is controlled by User not being null. /// +/// +/// Requires ItemToggleComponent. +/// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [Access(typeof(SharedNinjaGlovesSystem))] public sealed partial class NinjaGlovesComponent : Component @@ -22,24 +19,33 @@ public sealed partial class NinjaGlovesComponent : Component /// /// Entity of the ninja using these gloves, usually means enabled /// - [DataField("user"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? User; /// - /// The action id for toggling ninja gloves abilities + /// Abilities to give to the user when enabled. /// - [DataField("toggleAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ToggleAction = "ActionToggleNinjaGloves"; + [DataField(required: true)] + public List Abilities = new(); +} - [DataField, AutoNetworkedField] - public EntityUid? ToggleActionEntity; +/// +/// An ability that adds components to the user when the gloves are enabled. +/// +[DataRecord] +public record struct NinjaGloveAbility() +{ + /// + /// If not null, checks if an objective with this prototype has been completed. + /// If it has, the ability components are skipped to prevent doing the objective twice. + /// The objective must have CodeConditionComponent to be checked. + /// + [DataField] + public EntProtoId? Objective; /// - /// The whitelist used for the emag provider to emag airlocks only (not regular doors). + /// Components to add and remove. /// - [DataField("doorjackWhitelist")] - public EntityWhitelist DoorjackWhitelist = new() - { - Components = new[] {"Airlock"} - }; + [DataField(required: true)] + public ComponentRegistry Components = new(); } diff --git a/Content.Shared/Ninja/Components/NinjaSuitComponent.cs b/Content.Shared/Ninja/Components/NinjaSuitComponent.cs index 7e7b1ffcd30..8b477b2aa5f 100644 --- a/Content.Shared/Ninja/Components/NinjaSuitComponent.cs +++ b/Content.Shared/Ninja/Components/NinjaSuitComponent.cs @@ -3,9 +3,6 @@ using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Utility; namespace Content.Shared.Ninja.Components; @@ -14,68 +11,27 @@ namespace Content.Shared.Ninja.Components; /// Component for ninja suit abilities and power consumption. /// As an implementation detail, dashing with katana is a suit action which isn't ideal. /// -[RegisterComponent, NetworkedComponent, Access(typeof(SharedNinjaSuitSystem)), AutoGenerateComponentState] -[AutoGenerateComponentPause] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedNinjaSuitSystem))] public sealed partial class NinjaSuitComponent : Component { - /// - /// Battery charge used passively, in watts. Will last 1000 seconds on a small-capacity power cell. - /// - [DataField("passiveWattage")] - public float PassiveWattage = 0.36f; - - /// - /// Battery charge used while cloaked, stacks with passive. Will last 200 seconds while cloaked on a small-capacity power cell. - /// - [DataField("cloakWattage")] - public float CloakWattage = 1.44f; - /// /// Sound played when a ninja is hit while cloaked. /// - [DataField("revealSound")] + [DataField] public SoundSpecifier RevealSound = new SoundPathSpecifier("/Audio/Effects/chime.ogg"); /// - /// How long to disable all abilities when revealed. - /// Normally, ninjas are revealed when attacking or getting damaged. - /// - [DataField("disableTime")] - public TimeSpan DisableTime = TimeSpan.FromSeconds(5); - - /// - /// Time at which we will be able to use our abilities again + /// ID of the use delay to disable all ninja abilities. /// - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] - [AutoPausedField] - public TimeSpan DisableCooldown; - - /// - /// The action id for creating throwing stars. - /// - [DataField("createThrowingStarAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string CreateThrowingStarAction = "ActionCreateThrowingStar"; - - [DataField, AutoNetworkedField] - public EntityUid? CreateThrowingStarActionEntity; - - /// - /// Battery charge used to create a throwing star. Can do it 25 times on a small-capacity power cell. - /// - [DataField("throwingStarCharge")] - public float ThrowingStarCharge = 14.4f; - - /// - /// Throwing star item to create with the action - /// - [DataField("throwingStarPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string ThrowingStarPrototype = "ThrowingStarNinja"; + [DataField] + public string DisableDelayId = "suit_powers"; /// /// The action id for recalling a bound energy katana /// - [DataField("recallKatanaAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string RecallKatanaAction = "ActionRecallKatana"; + [DataField] + public EntProtoId RecallKatanaAction = "ActionRecallKatana"; [DataField, AutoNetworkedField] public EntityUid? RecallKatanaActionEntity; @@ -84,14 +40,14 @@ public sealed partial class NinjaSuitComponent : Component /// Battery charge used per tile the katana teleported. /// Uses 1% of a default battery per tile. /// - [DataField("recallCharge")] + [DataField] public float RecallCharge = 3.6f; /// /// The action id for creating an EMP burst /// - [DataField("empAction", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string EmpAction = "ActionNinjaEmp"; + [DataField] + public EntProtoId EmpAction = "ActionNinjaEmp"; [DataField, AutoNetworkedField] public EntityUid? EmpActionEntity; @@ -99,36 +55,29 @@ public sealed partial class NinjaSuitComponent : Component /// /// Battery charge used to create an EMP burst. Can do it 2 times on a small-capacity power cell. /// - [DataField("empCharge")] + [DataField] public float EmpCharge = 180f; + // TODO: EmpOnTrigger bruh /// /// Range of the EMP in tiles. /// - [DataField("empRange")] + [DataField] public float EmpRange = 6f; /// /// Power consumed from batteries by the EMP /// - [DataField("empConsumption")] + [DataField] public float EmpConsumption = 100000f; /// /// How long the EMP effects last for, in seconds /// - [DataField("empDuration")] + [DataField] public float EmpDuration = 60f; } -public sealed partial class CreateThrowingStarEvent : InstantActionEvent -{ -} - -public sealed partial class RecallKatanaEvent : InstantActionEvent -{ -} +public sealed partial class RecallKatanaEvent : InstantActionEvent; -public sealed partial class NinjaEmpEvent : InstantActionEvent -{ -} +public sealed partial class NinjaEmpEvent : InstantActionEvent; diff --git a/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs b/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs index 91c816df5c9..a19537be1c8 100644 --- a/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs +++ b/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs @@ -7,34 +7,28 @@ namespace Content.Shared.Ninja.Components; /// /// Component placed on a mob to make it a space ninja, able to use suit and glove powers. -/// Contains ids of all ninja equipment and the game rule. +/// Contains ids of all ninja equipment. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [Access(typeof(SharedSpaceNinjaSystem))] public sealed partial class SpaceNinjaComponent : Component { - /// - /// The ninja game rule that spawned this ninja. - /// - [DataField("rule")] - public EntityUid? Rule; - /// /// Currently worn suit /// - [DataField("suit"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? Suit; /// - /// Currently worn gloves + /// Currently worn gloves, if enabled. /// - [DataField("gloves"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? Gloves; /// /// Bound katana, set once picked up and never removed /// - [DataField("katana"), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? Katana; /// @@ -55,6 +49,9 @@ public sealed partial class SpaceNinjaComponent : Component [DataField] public EntProtoId SpiderChargeObjective = "SpiderChargeObjective"; + /// + /// Alert to show for suit power. + /// [DataField] public ProtoId SuitPowerAlert = "SuitPower"; } diff --git a/Content.Shared/Ninja/Components/SpiderChargeComponent.cs b/Content.Shared/Ninja/Components/SpiderChargeComponent.cs index dacf47bb235..3ba4494cca4 100644 --- a/Content.Shared/Ninja/Components/SpiderChargeComponent.cs +++ b/Content.Shared/Ninja/Components/SpiderChargeComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Ninja.Systems; using Robust.Shared.GameStates; namespace Content.Shared.Ninja.Components; @@ -6,14 +7,14 @@ namespace Content.Shared.Ninja.Components; /// Component for the Space Ninja's unique Spider Charge. /// Only this component detonating can trigger the ninja's objective. /// -[RegisterComponent, NetworkedComponent] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedSpiderChargeSystem))] public sealed partial class SpiderChargeComponent : Component { /// Range for planting within the target area - [DataField("range")] + [DataField] public float Range = 10f; /// The ninja that planted this charge - [DataField("planter")] - public EntityUid? Planter = null; + [DataField] + public EntityUid? Planter; } diff --git a/Content.Shared/Ninja/Components/StunProviderComponent.cs b/Content.Shared/Ninja/Components/StunProviderComponent.cs index 37a27074a49..2da094291d7 100644 --- a/Content.Shared/Ninja/Components/StunProviderComponent.cs +++ b/Content.Shared/Ninja/Components/StunProviderComponent.cs @@ -3,7 +3,6 @@ using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Ninja.Components; @@ -11,32 +10,33 @@ namespace Content.Shared.Ninja.Components; /// Component for stunning mobs on click outside of harm mode. /// Knocks them down for a bit and deals shock damage. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedStunProviderSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedStunProviderSystem))] public sealed partial class StunProviderComponent : Component { /// /// The powercell entity to take power from. /// Determines whether stunning is possible. /// - [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] + [DataField, AutoNetworkedField] public EntityUid? BatteryUid; /// /// Sound played when stunning someone. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public SoundSpecifier Sound = new SoundCollectionSpecifier("sparks"); /// /// Joules required in the battery to stun someone. Defaults to 10 uses on a small battery. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public float StunCharge = 36f; /// /// Damage dealt when stunning someone /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public DamageSpecifier StunDamage = new() { DamageDict = new() @@ -48,34 +48,30 @@ public sealed partial class StunProviderComponent : Component /// /// Time that someone is stunned for, stacks if done multiple times. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan StunTime = TimeSpan.FromSeconds(5); /// /// How long stunning is disabled after stunning something. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public TimeSpan Cooldown = TimeSpan.FromSeconds(2); /// - /// Locale string to popup when there is no power + /// ID of the cooldown use delay. /// - [DataField(required: true), ViewVariables(VVAccess.ReadWrite)] - public string NoPowerPopup = string.Empty; + [DataField] + public string DelayId = "stun_cooldown"; /// - /// Whitelist for what counts as a mob. + /// Locale string to popup when there is no power /// - [DataField] - public EntityWhitelist Whitelist = new() - { - Components = new[] {"Stamina"} - }; + [DataField(required: true)] + public LocId NoPowerPopup = string.Empty; /// - /// When someone can next be stunned. - /// Essentially a UseDelay unique to this component. + /// Whitelist for what counts as a mob. /// - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)] - public TimeSpan NextStun = TimeSpan.Zero; + [DataField(required: true)] + public EntityWhitelist Whitelist = new(); } diff --git a/Content.Shared/Ninja/Systems/DashAbilitySystem.cs b/Content.Shared/Ninja/Systems/DashAbilitySystem.cs index 4853968b61f..1385219e473 100644 --- a/Content.Shared/Ninja/Systems/DashAbilitySystem.cs +++ b/Content.Shared/Ninja/Systems/DashAbilitySystem.cs @@ -16,6 +16,7 @@ namespace Content.Shared.Ninja.Systems; /// public sealed class DashAbilitySystem : EntitySystem { + [Dependency] private readonly ActionContainerSystem _actionContainer = default!; [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedChargesSystem _charges = default!; @@ -23,48 +24,40 @@ public sealed class DashAbilitySystem : EntitySystem [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly ActionContainerSystem _actionContainer = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnGetItemActions); + SubscribeLocalEvent(OnGetActions); SubscribeLocalEvent(OnDash); SubscribeLocalEvent(OnMapInit); } - private void OnMapInit(EntityUid uid, DashAbilityComponent component, MapInitEvent args) + private void OnMapInit(Entity ent, ref MapInitEvent args) { - _actionContainer.EnsureAction(uid, ref component.DashActionEntity, component.DashAction); - Dirty(uid, component); + var (uid, comp) = ent; + _actionContainer.EnsureAction(uid, ref comp.DashActionEntity, comp.DashAction); + Dirty(uid, comp); } - private void OnGetItemActions(EntityUid uid, DashAbilityComponent comp, GetItemActionsEvent args) + private void OnGetActions(Entity ent, ref GetItemActionsEvent args) { - var ev = new AddDashActionEvent(args.User); - RaiseLocalEvent(uid, ev); - - if (ev.Cancelled) - return; - - args.AddAction(ref comp.DashActionEntity, comp.DashAction); + if (CheckDash(ent, args.User)) + args.AddAction(ent.Comp.DashActionEntity); } /// /// Handle charges and teleport to a visible location. /// - private void OnDash(EntityUid uid, DashAbilityComponent comp, DashEvent args) + private void OnDash(Entity ent, ref DashEvent args) { if (!_timing.IsFirstTimePredicted) return; + var (uid, comp) = ent; var user = args.Performer; - args.Handled = true; - - var ev = new DashAttemptEvent(user); - RaiseLocalEvent(uid, ev); - if (ev.Cancelled) + if (!CheckDash(uid, user)) return; if (!_hands.IsHolding(user, uid, out var _)) @@ -73,15 +66,8 @@ private void OnDash(EntityUid uid, DashAbilityComponent comp, DashEvent args) return; } - TryComp(uid, out var charges); - if (_charges.IsEmpty(uid, charges)) - { - _popup.PopupClient(Loc.GetString("dash-ability-no-charges", ("item", uid)), user, user); - return; - } var origin = _transform.GetMapCoordinates(user); var target = args.Target.ToMap(EntityManager, _transform); - // prevent collision with the user duh if (!_examine.InRangeUnOccluded(origin, target, SharedInteractionSystem.MaxRaycastRange, null)) { // can only dash if the destination is visible on screen @@ -89,36 +75,28 @@ private void OnDash(EntityUid uid, DashAbilityComponent comp, DashEvent args) return; } - _transform.SetCoordinates(user, args.Target); - _transform.AttachToGridOrMap(user); - _audio.PlayPredicted(comp.BlinkSound, user, user); - if (charges != null) - _charges.UseCharge(uid, charges); - } -} + if (!_charges.TryUseCharge(uid)) + { + _popup.PopupClient(Loc.GetString("dash-ability-no-charges", ("item", uid)), user, user); + return; + } -/// -/// Raised on the item before adding the dash action -/// -public sealed class AddDashActionEvent : CancellableEntityEventArgs -{ - public EntityUid User; + var xform = Transform(user); + _transform.SetCoordinates(user, xform, args.Target); + _transform.AttachToGridOrMap(user, xform); + args.Handled = true; + } - public AddDashActionEvent(EntityUid user) + public bool CheckDash(EntityUid uid, EntityUid user) { - User = user; + var ev = new CheckDashEvent(user); + RaiseLocalEvent(uid, ref ev); + return !ev.Cancelled; } } /// -/// Raised on the item before dashing is done. +/// Raised on the item before adding the dash action and when using the action. /// -public sealed class DashAttemptEvent : CancellableEntityEventArgs -{ - public EntityUid User; - - public DashAttemptEvent(EntityUid user) - { - User = user; - } -} +[ByRefEvent] +public record struct CheckDashEvent(EntityUid User, bool Cancelled = false); diff --git a/Content.Shared/Ninja/Systems/EmagProviderSystem.cs b/Content.Shared/Ninja/Systems/EmagProviderSystem.cs index 6838e7982cb..ae0bacaf5f6 100644 --- a/Content.Shared/Ninja/Systems/EmagProviderSystem.cs +++ b/Content.Shared/Ninja/Systems/EmagProviderSystem.cs @@ -1,6 +1,6 @@ using Content.Shared.Administration.Logs; -using Content.Shared.Emag.Systems; using Content.Shared.Database; +using Content.Shared.Emag.Systems; using Content.Shared.Interaction; using Content.Shared.Ninja.Components; using Content.Shared.Tag; @@ -14,10 +14,10 @@ namespace Content.Shared.Ninja.Systems; public sealed class EmagProviderSystem : EntitySystem { [Dependency] private readonly EmagSystem _emag = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelist = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; - [Dependency] private readonly TagSystem _tags = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly TagSystem _tag = default!; public override void Initialize() { @@ -29,18 +29,20 @@ public override void Initialize() /// /// Emag clicked entities that are on the whitelist. /// - private void OnBeforeInteractHand(EntityUid uid, EmagProviderComponent comp, BeforeInteractHandEvent args) + private void OnBeforeInteractHand(Entity ent, ref BeforeInteractHandEvent args) { // TODO: change this into a generic check event thing - if (args.Handled || !_gloves.AbilityCheck(uid, args, out var target)) + if (args.Handled || !_gloves.AbilityCheck(ent, args, out var target)) return; + var (uid, comp) = ent; + // only allowed to emag entities on the whitelist - if (_whitelistSystem.IsWhitelistFail(comp.Whitelist, target)) + if (_whitelist.IsWhitelistFail(comp.Whitelist, target)) return; // only allowed to emag non-immune entities - if (_tags.HasTag(target, comp.EmagImmuneTag)) + if (_tag.HasTag(target, comp.EmagImmuneTag)) return; var handled = _emag.DoEmagEffect(uid, target); @@ -52,18 +54,6 @@ private void OnBeforeInteractHand(EntityUid uid, EmagProviderComponent comp, Bef RaiseLocalEvent(uid, ref ev); args.Handled = true; } - - /// - /// Set the whitelist for emagging something outside of yaml. - /// - public void SetWhitelist(EntityUid uid, EntityWhitelist? whitelist, EmagProviderComponent? comp = null) - { - if (!Resolve(uid, ref comp)) - return; - - comp.Whitelist = whitelist; - Dirty(uid, comp); - } } /// diff --git a/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs b/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs index d427ffa39b4..281b97a648a 100644 --- a/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs +++ b/Content.Shared/Ninja/Systems/EnergyKatanaSystem.cs @@ -15,33 +15,20 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnEquipped); - SubscribeLocalEvent(OnAddDashAction); - SubscribeLocalEvent(OnDashAttempt); + SubscribeLocalEvent(OnCheckDash); } /// /// When equipped by a ninja, try to bind it. /// - private void OnEquipped(EntityUid uid, EnergyKatanaComponent comp, GotEquippedEvent args) + private void OnEquipped(Entity ent, ref GotEquippedEvent args) { - // check if user isnt a ninja or already has a katana bound - var user = args.Equipee; - if (!TryComp(user, out var ninja) || ninja.Katana != null) - return; - - // bind it since its unbound - _ninja.BindKatana(user, uid, ninja); - } - - private void OnAddDashAction(EntityUid uid, EnergyKatanaComponent comp, AddDashActionEvent args) - { - if (!HasComp(args.User)) - args.Cancel(); + _ninja.BindKatana(args.Equipee, ent); } - private void OnDashAttempt(EntityUid uid, EnergyKatanaComponent comp, DashAttemptEvent args) + private void OnCheckDash(Entity ent, ref CheckDashEvent args) { - if (!TryComp(args.User, out var ninja) || ninja.Katana != uid) - args.Cancel(); + if (!_ninja.IsNinja(args.User)) + args.Cancelled = true; } } diff --git a/Content.Shared/Ninja/Systems/ItemCreatorSystem.cs b/Content.Shared/Ninja/Systems/ItemCreatorSystem.cs new file mode 100644 index 00000000000..56112e9a697 --- /dev/null +++ b/Content.Shared/Ninja/Systems/ItemCreatorSystem.cs @@ -0,0 +1,56 @@ +using Content.Shared.Actions; +using Content.Shared.Ninja.Components; + +namespace Content.Shared.Ninja.Systems; + +/// +/// Handles predicting that the action exists, creating items is done serverside. +/// +public abstract class SharedItemCreatorSystem : EntitySystem +{ + [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnGetActions); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + var (uid, comp) = ent; + // test funny dont mind me + if (string.IsNullOrEmpty(comp.Action)) + return; + + _actionContainer.EnsureAction(uid, ref comp.ActionEntity, comp.Action); + Dirty(uid, comp); + } + + private void OnGetActions(Entity ent, ref GetItemActionsEvent args) + { + if (CheckItemCreator(ent, args.User)) + args.AddAction(ent.Comp.ActionEntity); + } + + public bool CheckItemCreator(EntityUid uid, EntityUid user) + { + var ev = new CheckItemCreatorEvent(user); + RaiseLocalEvent(uid, ref ev); + return !ev.Cancelled; + } +} + +/// +/// Raised on the item creator before adding the action. +/// +[ByRefEvent] +public record struct CheckItemCreatorEvent(EntityUid User, bool Cancelled = false); + +/// +/// Raised on the item creator before creating an item. +/// +[ByRefEvent] +public record struct CreateItemAttemptEvent(EntityUid User, bool Cancelled = false); diff --git a/Content.Shared/Ninja/Systems/SharedBatteryDrainerSystem.cs b/Content.Shared/Ninja/Systems/SharedBatteryDrainerSystem.cs index ac11063eb71..0abcca7d1bd 100644 --- a/Content.Shared/Ninja/Systems/SharedBatteryDrainerSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedBatteryDrainerSystem.cs @@ -18,34 +18,32 @@ public override void Initialize() } /// - /// Cancel any drain doafters if the battery is removed or gets filled. + /// Cancel any drain doafters if the battery is removed or, on the server, gets filled. /// - protected virtual void OnDoAfterAttempt(EntityUid uid, BatteryDrainerComponent comp, DoAfterAttemptEvent args) + protected virtual void OnDoAfterAttempt(Entity ent, ref DoAfterAttemptEvent args) { - if (comp.BatteryUid == null) - { + if (ent.Comp.BatteryUid == null) args.Cancel(); - } } /// /// Drain power from a power source (on server) and repeat if it succeeded. /// Client will predict always succeeding since power is serverside. /// - private void OnDoAfter(EntityUid uid, BatteryDrainerComponent comp, DrainDoAfterEvent args) + private void OnDoAfter(Entity ent, ref DrainDoAfterEvent args) { - if (args.Cancelled || args.Handled || args.Target == null) + if (args.Cancelled || args.Handled || args.Target is not {} target) return; // repeat if there is still power to drain - args.Repeat = TryDrainPower(uid, comp, args.Target.Value); + args.Repeat = TryDrainPower(ent, target); } /// /// Attempt to drain as much power as possible into the powercell. /// Client always predicts this as succeeding since power is serverside and it can only fail once, when the powercell is filled or the target is emptied. /// - protected virtual bool TryDrainPower(EntityUid uid, BatteryDrainerComponent comp, EntityUid target) + protected virtual bool TryDrainPower(Entity ent, EntityUid target) { return true; } @@ -53,12 +51,13 @@ protected virtual bool TryDrainPower(EntityUid uid, BatteryDrainerComponent comp /// /// Sets the battery field on the drainer. /// - public void SetBattery(EntityUid uid, EntityUid? battery, BatteryDrainerComponent? comp = null) + public void SetBattery(Entity ent, EntityUid? battery) { - if (!Resolve(uid, ref comp)) + if (!Resolve(ent, ref ent.Comp) || ent.Comp.BatteryUid == battery) return; - comp.BatteryUid = battery; + ent.Comp.BatteryUid = battery; + Dirty(ent, ent.Comp); } } @@ -66,4 +65,4 @@ public void SetBattery(EntityUid uid, EntityUid? battery, BatteryDrainerComponen /// DoAfter event for . /// [Serializable, NetSerializable] -public sealed partial class DrainDoAfterEvent : SimpleDoAfterEvent { } +public sealed partial class DrainDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs b/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs index f61d0c6a908..8b892190b7b 100644 --- a/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedNinjaGlovesSystem.cs @@ -1,15 +1,13 @@ -using Content.Shared.Actions; +using Content.Shared.Clothing.Components; using Content.Shared.CombatMode; -using Content.Shared.Communications; -using Content.Shared.CriminalRecords.Components; using Content.Shared.Examine; using Content.Shared.Hands.Components; using Content.Shared.Interaction; using Content.Shared.Inventory.Events; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Ninja.Components; using Content.Shared.Popups; -using Content.Shared.Research.Components; -using Content.Shared.Toggleable; using Robust.Shared.Timing; namespace Content.Shared.Ninja.Systems; @@ -20,85 +18,105 @@ namespace Content.Shared.Ninja.Systems; public abstract class SharedNinjaGlovesSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] private readonly SharedCombatModeSystem _combatMode = default!; - [Dependency] protected readonly SharedInteractionSystem Interaction = default!; - [Dependency] protected readonly SharedPopupSystem Popup = default!; - [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnGetItemActions); + SubscribeLocalEvent(OnToggleCheck); + SubscribeLocalEvent(OnActivateAttempt); + SubscribeLocalEvent(OnToggled); SubscribeLocalEvent(OnExamined); - SubscribeLocalEvent(OnUnequipped); - SubscribeLocalEvent(OnMapInit); - } - - private void OnMapInit(EntityUid uid, NinjaGlovesComponent component, MapInitEvent args) - { - _actionContainer.EnsureAction(uid, ref component.ToggleActionEntity, component.ToggleAction); - Dirty(uid, component); } /// /// Disable glove abilities and show the popup if they were enabled previously. /// - public void DisableGloves(EntityUid uid, NinjaGlovesComponent? comp = null) + private void DisableGloves(Entity ent) { + var (uid, comp) = ent; + // already disabled? - if (!Resolve(uid, ref comp) || comp.User == null) + if (comp.User is not {} user) return; - var user = comp.User.Value; comp.User = null; Dirty(uid, comp); - Appearance.SetData(uid, ToggleVisuals.Toggled, false); - Popup.PopupClient(Loc.GetString("ninja-gloves-off"), user, user); - - RemComp(user); - RemComp(user); - RemComp(user); - RemComp(user); - RemComp(user); - RemComp(user); + foreach (var ability in comp.Abilities) + { + EntityManager.RemoveComponents(user, ability.Components); + } } /// - /// Adds the toggle action when equipped. + /// Adds the toggle action when equipped by a ninja only. /// - private void OnGetItemActions(EntityUid uid, NinjaGlovesComponent comp, GetItemActionsEvent args) + private void OnToggleCheck(Entity ent, ref ToggleClothingCheckEvent args) { - if (HasComp(args.User)) - args.AddAction(ref comp.ToggleActionEntity, comp.ToggleAction); + if (!_ninja.IsNinja(args.User)) + args.Cancelled = true; } /// /// Show if the gloves are enabled when examining. /// - private void OnExamined(EntityUid uid, NinjaGlovesComponent comp, ExaminedEvent args) + private void OnExamined(Entity ent, ref ExaminedEvent args) { if (!args.IsInDetailsRange) return; - args.PushText(Loc.GetString(comp.User != null ? "ninja-gloves-examine-on" : "ninja-gloves-examine-off")); + var on = _toggle.IsActivated(ent.Owner) ? "on" : "off"; + args.PushText(Loc.GetString($"ninja-gloves-examine-{on}")); } - /// - /// Disable gloves when unequipped and clean up ninja's gloves reference - /// - private void OnUnequipped(EntityUid uid, NinjaGlovesComponent comp, GotUnequippedEvent args) + private void OnActivateAttempt(Entity ent, ref ItemToggleActivateAttemptEvent args) { - if (comp.User != null) + if (args.User is not {} user + || !_ninja.NinjaQuery.TryComp(user, out var ninja) + // need to wear suit to enable gloves + || !HasComp(ninja.Suit)) { - var user = comp.User.Value; - Popup.PopupClient(Loc.GetString("ninja-gloves-off"), user, user); - DisableGloves(uid, comp); + args.Cancelled = true; + args.Popup = Loc.GetString("ninja-gloves-not-wearing-suit"); + return; } } + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + if ((args.User ?? ent.Comp.User) is not {} user) + return; + + var message = Loc.GetString(args.Activated ? "ninja-gloves-on" : "ninja-gloves-off"); + _popup.PopupClient(message, user, user); + + if (args.Activated && _ninja.NinjaQuery.TryComp(user, out var ninja)) + EnableGloves(ent, (user, ninja)); + else + DisableGloves(ent); + } + + protected virtual void EnableGloves(Entity ent, Entity user) + { + var (uid, comp) = ent; + comp.User = user; + Dirty(uid, comp); + _ninja.AssignGloves(user, uid); + + // yeah this is just ComponentToggler but with objective checking + foreach (var ability in comp.Abilities) + { + // can't predict the objective related abilities + if (ability.Objective == null) + EntityManager.AddComponents(user, ability.Components); + } + } // TODO: generic event thing /// @@ -112,6 +130,6 @@ public bool AbilityCheck(EntityUid uid, BeforeInteractHandEvent args, out Entity && !_combatMode.IsInCombatMode(uid) && TryComp(uid, out var hands) && hands.ActiveHandEntity == null - && Interaction.InRangeUnobstructed(uid, target); + && _interaction.InRangeUnobstructed(uid, target); } } diff --git a/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs b/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs index fed41eaed8a..3800d15b267 100644 --- a/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedNinjaSuitSystem.cs @@ -1,11 +1,14 @@ using Content.Shared.Actions; +using Content.Shared.Clothing; using Content.Shared.Clothing.Components; using Content.Shared.Clothing.EntitySystems; using Content.Shared.Inventory.Events; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Ninja.Components; using Content.Shared.Popups; +using Content.Shared.Timing; using Robust.Shared.Audio.Systems; -using Robust.Shared.Timing; namespace Content.Shared.Ninja.Systems; @@ -14,137 +17,158 @@ namespace Content.Shared.Ninja.Systems; /// public abstract class SharedNinjaSuitSystem : EntitySystem { - [Dependency] protected readonly IGameTiming GameTiming = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedNinjaGlovesSystem _gloves = default!; - [Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; - [Dependency] protected readonly StealthClothingSystem StealthClothing = default!; + [Dependency] private readonly SharedSpaceNinjaSystem _ninja = default!; + [Dependency] private readonly UseDelaySystem _useDelay = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); - - SubscribeLocalEvent(OnEquipped); + SubscribeLocalEvent(OnEquipped); SubscribeLocalEvent(OnGetItemActions); - SubscribeLocalEvent(OnAddStealthAction); + SubscribeLocalEvent(OnCloakCheck); + SubscribeLocalEvent(OnStarCheck); + SubscribeLocalEvent(OnCreateStarAttempt); + SubscribeLocalEvent(OnActivateAttempt); SubscribeLocalEvent(OnUnequipped); } - private void OnMapInit(EntityUid uid, NinjaSuitComponent component, MapInitEvent args) + private void OnEquipped(Entity ent, ref ClothingGotEquippedEvent args) { - _actionContainer.EnsureAction(uid, ref component.RecallKatanaActionEntity, component.RecallKatanaAction); - _actionContainer.EnsureAction(uid, ref component.CreateThrowingStarActionEntity, component.CreateThrowingStarAction); - _actionContainer.EnsureAction(uid, ref component.EmpActionEntity, component.EmpAction); - Dirty(uid, component); + var user = args.Wearer; + if (_ninja.NinjaQuery.TryComp(user, out var ninja)) + NinjaEquipped(ent, (user, ninja)); } - /// - /// Call the shared and serverside code for when a ninja equips the suit. - /// - private void OnEquipped(EntityUid uid, NinjaSuitComponent comp, GotEquippedEvent args) + protected virtual void NinjaEquipped(Entity ent, Entity user) { - var user = args.Equipee; - if (!TryComp(user, out var ninja)) - return; + // mark the user as wearing this suit, used when being attacked among other things + _ninja.AssignSuit(user, ent); + } - NinjaEquippedSuit(uid, comp, user, ninja); + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + var (uid, comp) = ent; + _actionContainer.EnsureAction(uid, ref comp.RecallKatanaActionEntity, comp.RecallKatanaAction); + _actionContainer.EnsureAction(uid, ref comp.EmpActionEntity, comp.EmpAction); + Dirty(uid, comp); } /// /// Add all the actions when a suit is equipped by a ninja. /// - private void OnGetItemActions(EntityUid uid, NinjaSuitComponent comp, GetItemActionsEvent args) + private void OnGetItemActions(Entity ent, ref GetItemActionsEvent args) { - if (!HasComp(args.User)) + if (!_ninja.IsNinja(args.User)) return; + var comp = ent.Comp; args.AddAction(ref comp.RecallKatanaActionEntity, comp.RecallKatanaAction); - args.AddAction(ref comp.CreateThrowingStarActionEntity, comp.CreateThrowingStarAction); args.AddAction(ref comp.EmpActionEntity, comp.EmpAction); } /// - /// Only add stealth clothing's toggle action when equipped by a ninja. + /// Only add toggle cloak action when equipped by a ninja. /// - private void OnAddStealthAction(EntityUid uid, NinjaSuitComponent comp, AddStealthActionEvent args) + private void OnCloakCheck(Entity ent, ref ToggleClothingCheckEvent args) { - if (!HasComp(args.User)) - args.Cancel(); + if (!_ninja.IsNinja(args.User)) + args.Cancelled = true; } - /// - /// Call the shared and serverside code for when anyone unequips a suit. - /// - private void OnUnequipped(EntityUid uid, NinjaSuitComponent comp, GotUnequippedEvent args) + private void OnStarCheck(Entity ent, ref CheckItemCreatorEvent args) + { + if (!_ninja.IsNinja(args.User)) + args.Cancelled = true; + } + + private void OnCreateStarAttempt(Entity ent, ref CreateItemAttemptEvent args) { - UserUnequippedSuit(uid, comp, args.Equipee); + if (CheckDisabled(ent, args.User)) + args.Cancelled = true; } /// - /// Called when a suit is equipped by a space ninja. - /// In the future it might be changed to an explicit activation toggle/verb like gloves are. + /// Call the shared and serverside code for when anyone unequips a suit. /// - protected virtual void NinjaEquippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user, SpaceNinjaComponent ninja) + private void OnUnequipped(Entity ent, ref GotUnequippedEvent args) { - // mark the user as wearing this suit, used when being attacked among other things - _ninja.AssignSuit(user, uid, ninja); - - // initialize phase cloak, but keep it off - StealthClothing.SetEnabled(uid, user, false); + var user = args.Equipee; + if (_ninja.NinjaQuery.TryComp(user, out var ninja)) + UserUnequippedSuit(ent, (user, ninja)); } /// /// Force uncloaks the user and disables suit abilities. /// - public void RevealNinja(EntityUid uid, EntityUid user, bool disable = true, NinjaSuitComponent? comp = null, StealthClothingComponent? stealthClothing = null) + public void RevealNinja(Entity ent, EntityUid user, bool disable = true) { - if (!Resolve(uid, ref comp, ref stealthClothing)) + if (!Resolve(ent, ref ent.Comp)) return; - if (!StealthClothing.SetEnabled(uid, user, false, stealthClothing)) - return; - - if (!disable) + var uid = ent.Owner; + var comp = ent.Comp; + if (_toggle.TryDeactivate(uid, user) || !disable) return; // previously cloaked, disable abilities for a short time _audio.PlayPredicted(comp.RevealSound, uid, user); Popup.PopupClient(Loc.GetString("ninja-revealed"), user, user, PopupType.MediumCaution); - comp.DisableCooldown = GameTiming.CurTime + comp.DisableTime; + _useDelay.TryResetDelay(uid, id: comp.DisableDelayId); + } + + private void OnActivateAttempt(Entity ent, ref ItemToggleActivateAttemptEvent args) + { + if (!_ninja.IsNinja(args.User)) + { + args.Cancelled = true; + return; + } + + if (IsDisabled((ent, ent.Comp, null))) + { + args.Cancelled = true; + args.Popup = Loc.GetString("ninja-suit-cooldown"); + } } - // TODO: modify PowerCellDrain /// - /// Returns the power used by a suit + /// Returns true if the suit is currently disabled /// - public float SuitWattage(EntityUid uid, NinjaSuitComponent? suit = null) + public bool IsDisabled(Entity ent) + { + if (!Resolve(ent, ref ent.Comp1, ref ent.Comp2)) + return false; + + return _useDelay.IsDelayed((ent, ent.Comp2), ent.Comp1.DisableDelayId); + } + + protected bool CheckDisabled(Entity ent, EntityUid user) { - if (!Resolve(uid, ref suit)) - return 0f; + if (IsDisabled((ent, ent.Comp, null))) + { + Popup.PopupEntity(Loc.GetString("ninja-suit-cooldown"), user, user, PopupType.Medium); + return true; + } - float wattage = suit.PassiveWattage; - if (TryComp(uid, out var stealthClothing) && stealthClothing.Enabled) - wattage += suit.CloakWattage; - return wattage; + return false; } /// /// Called when a suit is unequipped, not necessarily by a space ninja. /// In the future it might be changed to also have explicit deactivation via toggle. /// - protected virtual void UserUnequippedSuit(EntityUid uid, NinjaSuitComponent comp, EntityUid user) + protected virtual void UserUnequippedSuit(Entity ent, Entity user) { - if (!TryComp(user, out var ninja)) - return; - // mark the user as not wearing a suit - _ninja.AssignSuit(user, null, ninja); + _ninja.AssignSuit(user, null); // disable glove abilities - if (ninja.Gloves != null && TryComp(ninja.Gloves.Value, out var gloves)) - _gloves.DisableGloves(ninja.Gloves.Value, gloves); + if (user.Comp.Gloves is {} uid) + _toggle.TryDeactivate(uid, user: user); } } diff --git a/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs b/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs index 522f29fe420..d738f2dd8a2 100644 --- a/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedSpaceNinjaSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Weapons.Melee.Events; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Popups; +using System.Diagnostics.CodeAnalysis; namespace Content.Shared.Ninja.Systems; @@ -14,49 +15,59 @@ public abstract class SharedSpaceNinjaSystem : EntitySystem [Dependency] protected readonly SharedNinjaSuitSystem Suit = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; + public EntityQuery NinjaQuery; + public override void Initialize() { base.Initialize(); + NinjaQuery = GetEntityQuery(); + SubscribeLocalEvent(OnNinjaAttacked); SubscribeLocalEvent(OnNinjaAttack); SubscribeLocalEvent(OnShotAttempted); } + public bool IsNinja([NotNullWhen(true)] EntityUid? uid) + { + return NinjaQuery.HasComp(uid); + } + /// /// Set the ninja's worn suit entity /// - public void AssignSuit(EntityUid uid, EntityUid? suit, SpaceNinjaComponent? comp = null) + public void AssignSuit(Entity ent, EntityUid? suit) { - if (!Resolve(uid, ref comp) || comp.Suit == suit) + if (ent.Comp.Suit == suit) return; - comp.Suit = suit; - Dirty(uid, comp); + ent.Comp.Suit = suit; + Dirty(ent, ent.Comp); } /// /// Set the ninja's worn gloves entity /// - public void AssignGloves(EntityUid uid, EntityUid? gloves, SpaceNinjaComponent? comp = null) + public void AssignGloves(Entity ent, EntityUid? gloves) { - if (!Resolve(uid, ref comp) || comp.Gloves == gloves) + if (ent.Comp.Gloves == gloves) return; - comp.Gloves = gloves; - Dirty(uid, comp); + ent.Comp.Gloves = gloves; + Dirty(ent, ent.Comp); } /// /// Bind a katana entity to a ninja, letting it be recalled and dash. + /// Does nothing if the player is not a ninja or already has a katana bound. /// - public void BindKatana(EntityUid uid, EntityUid? katana, SpaceNinjaComponent? comp = null) + public void BindKatana(Entity ent, EntityUid katana) { - if (!Resolve(uid, ref comp) || comp.Katana == katana) + if (!NinjaQuery.Resolve(ent, ref ent.Comp) || ent.Comp.Katana != null) return; - comp.Katana = katana; - Dirty(uid, comp); + ent.Comp.Katana = katana; + Dirty(ent, ent.Comp); } /// @@ -71,32 +82,32 @@ public virtual bool TryUseCharge(EntityUid user, float charge) /// /// Handle revealing ninja if cloaked when attacked. /// - private void OnNinjaAttacked(EntityUid uid, SpaceNinjaComponent comp, AttackedEvent args) + private void OnNinjaAttacked(Entity ent, ref AttackedEvent args) { - if (comp.Suit != null && TryComp(comp.Suit, out var stealthClothing) && stealthClothing.Enabled) - { - Suit.RevealNinja(comp.Suit.Value, uid, true, null, stealthClothing); - } + TryRevealNinja(ent, disable: true); } /// /// Handle revealing ninja if cloaked when attacking. /// Only reveals, there is no cooldown. /// - private void OnNinjaAttack(EntityUid uid, SpaceNinjaComponent comp, ref MeleeAttackEvent args) + private void OnNinjaAttack(Entity ent, ref MeleeAttackEvent args) + { + TryRevealNinja(ent, disable: false); + } + + private void TryRevealNinja(Entity ent, bool disable) { - if (comp.Suit != null && TryComp(comp.Suit, out var stealthClothing) && stealthClothing.Enabled) - { - Suit.RevealNinja(comp.Suit.Value, uid, false, null, stealthClothing); - } + if (ent.Comp.Suit is {} uid && TryComp(ent.Comp.Suit, out var suit)) + Suit.RevealNinja((uid, suit), ent, disable: disable); } /// /// Require ninja to fight with HONOR, no guns! /// - private void OnShotAttempted(EntityUid uid, SpaceNinjaComponent comp, ref ShotAttemptedEvent args) + private void OnShotAttempted(Entity ent, ref ShotAttemptedEvent args) { - Popup.PopupClient(Loc.GetString("gun-disabled"), uid, uid); + Popup.PopupClient(Loc.GetString("gun-disabled"), ent, ent); args.Cancel(); } } diff --git a/Content.Shared/Ninja/Systems/SharedSpiderChargeSystem.cs b/Content.Shared/Ninja/Systems/SharedSpiderChargeSystem.cs new file mode 100644 index 00000000000..f4b158aced4 --- /dev/null +++ b/Content.Shared/Ninja/Systems/SharedSpiderChargeSystem.cs @@ -0,0 +1,6 @@ +namespace Content.Shared.Ninja.Systems; + +/// +/// Sticking triggering and exploding are all in server so this is just for access. +/// +public abstract class SharedSpiderChargeSystem : EntitySystem; diff --git a/Content.Shared/Ninja/Systems/SharedStunProviderSystem.cs b/Content.Shared/Ninja/Systems/SharedStunProviderSystem.cs index 61b6e4313ed..061c019c9b6 100644 --- a/Content.Shared/Ninja/Systems/SharedStunProviderSystem.cs +++ b/Content.Shared/Ninja/Systems/SharedStunProviderSystem.cs @@ -11,22 +11,12 @@ public abstract class SharedStunProviderSystem : EntitySystem /// /// Set the battery field on the stun provider. /// - public void SetBattery(EntityUid uid, EntityUid? battery, StunProviderComponent? comp = null) + public void SetBattery(Entity ent, EntityUid? battery) { - if (!Resolve(uid, ref comp)) + if (!Resolve(ent, ref ent.Comp) || ent.Comp.BatteryUid == battery) return; - comp.BatteryUid = battery; - } - - /// - /// Set the no power popup field on the stun provider. - /// - public void SetNoPowerPopup(EntityUid uid, string popup, StunProviderComponent? comp = null) - { - if (!Resolve(uid, ref comp)) - return; - - comp.NoPowerPopup = popup; + ent.Comp.BatteryUid = battery; + Dirty(ent, ent.Comp); } } diff --git a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs index 07032a00ce9..8d2c4dcfebe 100644 --- a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs +++ b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs @@ -92,7 +92,7 @@ public bool CanBeAssigned(EntityUid uid, EntityUid mindId, MindComponent mind, O } /// - /// Get the title, description, icon and progress of an objective using . + /// Get the title, description, icon and progress of an objective using . /// If any of them are null it is logged and null is returned. /// /// ID of the condition entity @@ -103,20 +103,43 @@ public bool CanBeAssigned(EntityUid uid, EntityUid mindId, MindComponent mind, O if (!Resolve(mindId, ref mind)) return null; - var ev = new ObjectiveGetProgressEvent(mindId, mind); - RaiseLocalEvent(uid, ref ev); + if (GetProgress(uid, (mindId, mind)) is not {} progress) + return null; var comp = Comp(uid); var meta = MetaData(uid); var title = meta.EntityName; var description = meta.EntityDescription; - if (comp.Icon == null || ev.Progress == null) + if (comp.Icon == null) { - Log.Error($"An objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind)} is missing icon or progress ({ev.Progress})"); + Log.Error($"An objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind)} is missing an icon!"); return null; } - return new ObjectiveInfo(title, description, comp.Icon, ev.Progress.Value); + return new ObjectiveInfo(title, description, comp.Icon, progress); + } + + /// + /// Gets the progress of an objective using . + /// Returning null is a programmer error. + /// + public float? GetProgress(EntityUid uid, Entity mind) + { + var ev = new ObjectiveGetProgressEvent(mind, mind.Comp); + RaiseLocalEvent(uid, ref ev); + if (ev.Progress != null) + return ev.Progress; + + Log.Error($"Objective {ToPrettyString(uid):objective} of {_mind.MindOwnerLoggingString(mind.Comp)} didn't set a progress value!"); + return null; + } + + /// + /// Returns true if an objective is completed. + /// + public bool IsCompleted(EntityUid uid, Entity mind) + { + return (GetProgress(uid, mind) ?? 0f) >= 0.999f; } /// diff --git a/Content.Shared/Pinpointer/SharedProximityBeeper.cs b/Content.Shared/Pinpointer/SharedProximityBeeper.cs deleted file mode 100644 index 51631126833..00000000000 --- a/Content.Shared/Pinpointer/SharedProximityBeeper.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Pinpointer; - -[Serializable, NetSerializable] -public enum ProximityBeeperVisuals : byte -{ - Enabled -} diff --git a/Content.Shared/PowerCell/PowerCellDrawComponent.cs b/Content.Shared/PowerCell/PowerCellDrawComponent.cs index 708a86a8eaf..94de7c77878 100644 --- a/Content.Shared/PowerCell/PowerCellDrawComponent.cs +++ b/Content.Shared/PowerCell/PowerCellDrawComponent.cs @@ -6,6 +6,10 @@ namespace Content.Shared.PowerCell; /// /// Indicates that the entity's ActivatableUI requires power or else it closes. /// +/// +/// With ActivatableUI it will activate and deactivate when the ui is opened and closed, drawing power inbetween. +/// Requires to work. +/// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] public sealed partial class PowerCellDrawComponent : Component { @@ -26,10 +30,12 @@ public sealed partial class PowerCellDrawComponent : Component #endregion /// - /// Is this power cell currently drawing power every tick. + /// Whether drawing is enabled, regardless of ItemToggle. + /// Having no cell will still disable it. + /// Only use this if you really don't want it to use power for some time. /// - [ViewVariables(VVAccess.ReadWrite), DataField("enabled")] - public bool Drawing; + [DataField, AutoNetworkedField] + public bool Enabled = true; /// /// How much the entity draws while the UI is open. @@ -51,4 +57,10 @@ public sealed partial class PowerCellDrawComponent : Component [DataField("nextUpdate", customTypeSerializer: typeof(TimeOffsetSerializer))] [AutoPausedField] public TimeSpan NextUpdateTime; + + /// + /// How long to wait between power drawing. + /// + [DataField] + public TimeSpan Delay = TimeSpan.FromSeconds(1); } diff --git a/Content.Shared/PowerCell/SharedPowerCellSystem.cs b/Content.Shared/PowerCell/SharedPowerCellSystem.cs index 508bfc85f08..2b2a836633c 100644 --- a/Content.Shared/PowerCell/SharedPowerCellSystem.cs +++ b/Content.Shared/PowerCell/SharedPowerCellSystem.cs @@ -1,4 +1,6 @@ using Content.Shared.Containers.ItemSlots; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.PowerCell.Components; using Content.Shared.Rejuvenate; using Robust.Shared.Containers; @@ -11,14 +13,19 @@ public abstract class SharedPowerCellSystem : EntitySystem [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] protected readonly ItemToggleSystem Toggle = default!; public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnRejuvenate); SubscribeLocalEvent(OnCellInserted); SubscribeLocalEvent(OnCellRemoved); SubscribeLocalEvent(OnCellInsertAttempt); + + SubscribeLocalEvent(OnActivateAttempt); + SubscribeLocalEvent(OnToggled); } private void OnRejuvenate(EntityUid uid, PowerCellSlotComponent component, RejuvenateEvent args) @@ -63,13 +70,25 @@ protected virtual void OnCellRemoved(EntityUid uid, PowerCellSlotComponent compo RaiseLocalEvent(uid, new PowerCellChangedEvent(true), false); } - public void SetPowerCellDrawEnabled(EntityUid uid, bool enabled, PowerCellDrawComponent? component = null) + private void OnActivateAttempt(Entity ent, ref ItemToggleActivateAttemptEvent args) + { + if (!HasDrawCharge(ent, ent.Comp, user: args.User) + || !HasActivatableCharge(ent, ent.Comp, user: args.User)) + args.Cancelled = true; + } + + private void OnToggled(Entity ent, ref ItemToggledEvent args) + { + ent.Comp.NextUpdateTime = Timing.CurTime; + } + + public void SetDrawEnabled(Entity ent, bool enabled) { - if (!Resolve(uid, ref component, false) || enabled == component.Drawing) + if (!Resolve(ent, ref ent.Comp, false) || ent.Comp.Enabled == enabled) return; - component.Drawing = enabled; - component.NextUpdateTime = Timing.CurTime; + ent.Comp.Enabled = enabled; + Dirty(ent, ent.Comp); } /// diff --git a/Content.Shared/ProximityDetection/Components/ProximityDetectorComponent.cs b/Content.Shared/ProximityDetection/Components/ProximityDetectorComponent.cs index 09cb7f06d5d..7e2bb4dfe62 100644 --- a/Content.Shared/ProximityDetection/Components/ProximityDetectorComponent.cs +++ b/Content.Shared/ProximityDetection/Components/ProximityDetectorComponent.cs @@ -10,12 +10,6 @@ namespace Content.Shared.ProximityDetection.Components; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState ,Access(typeof(ProximityDetectionSystem))] public sealed partial class ProximityDetectorComponent : Component { - /// - /// Whether or not it's on. - /// - [DataField, AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)] - public bool Enabled = true; - /// /// The criteria used to filter entities /// Note: RequireAll is only supported for tags, all components are required to count as a match! @@ -35,13 +29,13 @@ public sealed partial class ProximityDetectorComponent : Component [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public FixedPoint2 Distance = -1; - /// /// The farthest distance to search for targets /// [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] public FixedPoint2 Range = 10f; + // TODO: use timespans not this public float AccumulatedFrameTime; [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] diff --git a/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs b/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs index db25e8bc511..df302f94771 100644 --- a/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs +++ b/Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs @@ -1,4 +1,6 @@ -using Content.Shared.ProximityDetection.Components; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; +using Content.Shared.ProximityDetection.Components; using Content.Shared.Tag; using Robust.Shared.Network; @@ -9,6 +11,7 @@ namespace Content.Shared.ProximityDetection.Systems; public sealed class ProximityDetectionSystem : EntitySystem { [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly INetManager _net = default!; @@ -17,10 +20,10 @@ public sealed class ProximityDetectionSystem : EntitySystem public override void Initialize() { - SubscribeLocalEvent(OnPaused); - SubscribeLocalEvent(OnUnpaused); - SubscribeLocalEvent(OnCompInit); + base.Initialize(); + SubscribeLocalEvent(OnCompInit); + SubscribeLocalEvent(OnToggled); } private void OnCompInit(EntityUid uid, ProximityDetectorComponent component, ComponentInit args) @@ -30,57 +33,39 @@ private void OnCompInit(EntityUid uid, ProximityDetectorComponent component, Com Log.Debug("DetectorComponent only supports requireAll = false for tags. All components are required for a match!"); } - private void OnPaused(EntityUid owner, ProximityDetectorComponent component, EntityPausedEvent args) - { - SetEnable_Internal(owner,component,false); - } - - private void OnUnpaused(EntityUid owner, ProximityDetectorComponent detector, ref EntityUnpausedEvent args) - { - SetEnable_Internal(owner, detector,true); - } - public void SetEnable(EntityUid owner, bool enabled, ProximityDetectorComponent? detector = null) - { - if (!Resolve(owner, ref detector) || detector.Enabled == enabled) - return; - SetEnable_Internal(owner ,detector, enabled); - } - public override void Update(float frameTime) { if (_net.IsClient) return; + var query = EntityQueryEnumerator(); while (query.MoveNext(out var owner, out var detector)) { - if (!detector.Enabled) + if (!_toggle.IsActivated(owner)) continue; + detector.AccumulatedFrameTime += frameTime; if (detector.AccumulatedFrameTime < detector.UpdateRate) continue; + detector.AccumulatedFrameTime -= detector.UpdateRate; RunUpdate_Internal(owner, detector); } } - public bool GetEnable(EntityUid owner, ProximityDetectorComponent? detector = null) + private void OnToggled(Entity ent, ref ItemToggledEvent args) { - return Resolve(owner, ref detector, false) && detector.Enabled; - } - - private void SetEnable_Internal(EntityUid owner,ProximityDetectorComponent detector, bool enabled) - { - detector.Enabled = enabled; - var noDetectEvent = new ProximityTargetUpdatedEvent(detector, detector.TargetEnt, detector.Distance); - RaiseLocalEvent(owner, ref noDetectEvent); - if (!enabled) + if (args.Activated) { - detector.AccumulatedFrameTime = 0; - RunUpdate_Internal(owner, detector); - Dirty(owner, detector); + RunUpdate_Internal(ent, ent.Comp); return; } - RunUpdate_Internal(owner, detector); + + var noDetectEvent = new ProximityTargetUpdatedEvent(ent.Comp, Target: null, ent.Comp.Distance); + RaiseLocalEvent(ent, ref noDetectEvent); + + ent.Comp.AccumulatedFrameTime = 0; + Dirty(ent, ent.Comp); } public void ForceUpdate(EntityUid owner, ProximityDetectorComponent? detector = null) @@ -90,11 +75,31 @@ public void ForceUpdate(EntityUid owner, ProximityDetectorComponent? detector = RunUpdate_Internal(owner, detector); } + private void ClearTarget(Entity ent) + { + var (uid, comp) = ent; + if (comp.TargetEnt == null) + return; + + comp.Distance = -1; + comp.TargetEnt = null; + var noDetectEvent = new ProximityTargetUpdatedEvent(comp, null, -1); + RaiseLocalEvent(uid, ref noDetectEvent); + var newTargetEvent = new NewProximityTargetEvent(comp, null); + RaiseLocalEvent(uid, ref newTargetEvent); + Dirty(uid, comp); + } private void RunUpdate_Internal(EntityUid owner,ProximityDetectorComponent detector) { if (!_net.IsServer) //only run detection checks on the server! return; + + if (Deleted(detector.TargetEnt)) + { + ClearTarget((owner, detector)); + } + var xformQuery = GetEntityQuery(); var xform = xformQuery.GetComponent(owner); List<(EntityUid TargetEnt, float Distance)> detections = new(); @@ -173,15 +178,7 @@ private void UpdateTargetFromClosest(EntityUid owner, ProximityDetectorComponent { if (detections.Count == 0) { - if (detector.TargetEnt == null) - return; - detector.Distance = -1; - detector.TargetEnt = null; - var noDetectEvent = new ProximityTargetUpdatedEvent(detector, null, -1); - RaiseLocalEvent(owner, ref noDetectEvent); - var newTargetEvent = new NewProximityTargetEvent(detector, null); - RaiseLocalEvent(owner, ref newTargetEvent); - Dirty(owner, detector); + ClearTarget((owner, detector)); return; } var closestDistance = detections[0].Distance; @@ -198,6 +195,7 @@ private void UpdateTargetFromClosest(EntityUid owner, ProximityDetectorComponent var newData = newTarget || detector.Distance != closestDistance; detector.TargetEnt = closestEnt; detector.Distance = closestDistance; + Dirty(owner, detector); if (newTarget) { var newTargetEvent = new NewProximityTargetEvent(detector, closestEnt); diff --git a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs index e1776873da9..de0fe0bce38 100644 --- a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs +++ b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs @@ -15,12 +15,6 @@ namespace Content.Shared.Silicons.Borgs.Components; [RegisterComponent, NetworkedComponent, Access(typeof(SharedBorgSystem)), AutoGenerateComponentState] public sealed partial class BorgChassisComponent : Component { - /// - /// Whether or not the borg is activated, meaning it has access to modules and a heightened movement speed - /// - [DataField("activated"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public bool Activated; - #region Brain /// /// A whitelist for which entities count as valid brains @@ -68,7 +62,7 @@ public sealed partial class BorgChassisComponent : Component /// /// The currently selected module /// - [DataField("selectedModule")] + [DataField("selectedModule"), AutoNetworkedField] public EntityUid? SelectedModule; #region Visuals diff --git a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs index 2983c0d642f..48d23578368 100644 --- a/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs +++ b/Content.Shared/Silicons/Borgs/SharedBorgSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Access.Components; using Content.Shared.Containers.ItemSlots; +using Content.Shared.Item.ItemToggle; using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; using Content.Shared.Popups; @@ -18,6 +19,7 @@ public abstract partial class SharedBorgSystem : EntitySystem { [Dependency] protected readonly SharedContainerSystem Container = default!; [Dependency] protected readonly ItemSlotsSystem ItemSlots = default!; + [Dependency] protected readonly ItemToggleSystem Toggle = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; /// @@ -96,7 +98,7 @@ protected virtual void OnRemoved(EntityUid uid, BorgChassisComponent component, private void OnRefreshMovementSpeedModifiers(EntityUid uid, BorgChassisComponent component, RefreshMovementSpeedModifiersEvent args) { - if (component.Activated) + if (Toggle.IsActivated(uid)) return; if (!TryComp(uid, out var movement)) diff --git a/Content.Shared/Toggleable/ToggleActionEvent.cs b/Content.Shared/Toggleable/ToggleActionEvent.cs index 1283b6699bf..f28e62e7dd1 100644 --- a/Content.Shared/Toggleable/ToggleActionEvent.cs +++ b/Content.Shared/Toggleable/ToggleActionEvent.cs @@ -4,9 +4,12 @@ namespace Content.Shared.Toggleable; /// -/// Generic action-event for toggle-able components. +/// Generic action-event for toggle-able components. /// -public sealed partial class ToggleActionEvent : InstantActionEvent { } +/// +/// If you are using ItemToggleComponent subscribe to ItemToggledEvent instead. +/// +public sealed partial class ToggleActionEvent : InstantActionEvent; /// /// Generic enum keys for toggle-visualizer appearance data & sprite layers. diff --git a/Content.Shared/Tools/Systems/SharedToolSystem.cs b/Content.Shared/Tools/Systems/SharedToolSystem.cs index 56ce81413fb..201eb19a88b 100644 --- a/Content.Shared/Tools/Systems/SharedToolSystem.cs +++ b/Content.Shared/Tools/Systems/SharedToolSystem.cs @@ -24,7 +24,7 @@ public abstract partial class SharedToolSystem : EntitySystem [Dependency] private readonly SharedAudioSystem _audioSystem = default!; [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; [Dependency] protected readonly SharedInteractionSystem InteractionSystem = default!; - [Dependency] protected readonly SharedItemToggleSystem ItemToggle = default!; + [Dependency] protected readonly ItemToggleSystem ItemToggle = default!; [Dependency] private readonly SharedMapSystem _maps = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] protected readonly SharedSolutionContainerSystem SolutionContainerSystem = default!; diff --git a/Content.Shared/UserInterface/ActivatableUISystem.Power.cs b/Content.Shared/UserInterface/ActivatableUISystem.Power.cs index b8a815c7a81..e494253c832 100644 --- a/Content.Shared/UserInterface/ActivatableUISystem.Power.cs +++ b/Content.Shared/UserInterface/ActivatableUISystem.Power.cs @@ -1,3 +1,5 @@ +using Content.Shared.Item.ItemToggle; +using Content.Shared.Item.ItemToggle.Components; using Content.Shared.PowerCell; using Robust.Shared.Containers; @@ -5,6 +7,7 @@ namespace Content.Shared.UserInterface; public sealed partial class ActivatableUISystem { + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedPowerCellSystem _cell = default!; private void InitializePower() @@ -12,27 +15,22 @@ private void InitializePower() SubscribeLocalEvent(OnBatteryOpenAttempt); SubscribeLocalEvent(OnBatteryOpened); SubscribeLocalEvent(OnBatteryClosed); - - SubscribeLocalEvent(OnPowerCellRemoved); + SubscribeLocalEvent(OnToggled); } - private void OnPowerCellRemoved(EntityUid uid, PowerCellDrawComponent component, EntRemovedFromContainerMessage args) + private void OnToggled(Entity ent, ref ItemToggledEvent args) { - _cell.SetPowerCellDrawEnabled(uid, false); - - if (!HasComp(uid) || - !TryComp(uid, out ActivatableUIComponent? activatable)) - { + // only close ui when losing power + if (!TryComp(ent, out var activatable) || args.Activated) return; - } if (activatable.Key == null) { - Log.Error($"Encountered null key in activatable ui on entity {ToPrettyString(uid)}"); + Log.Error($"Encountered null key in activatable ui on entity {ToPrettyString(ent)}"); return; } - _uiSystem.CloseUi(uid, activatable.Key); + _uiSystem.CloseUi(ent.Owner, activatable.Key); } private void OnBatteryOpened(EntityUid uid, ActivatableUIRequiresPowerCellComponent component, BoundUIOpenedEvent args) @@ -42,7 +40,7 @@ private void OnBatteryOpened(EntityUid uid, ActivatableUIRequiresPowerCellCompon if (!args.UiKey.Equals(activatable.Key)) return; - _cell.SetPowerCellDrawEnabled(uid, true); + _toggle.TryActivate(uid); } private void OnBatteryClosed(EntityUid uid, ActivatableUIRequiresPowerCellComponent component, BoundUIClosedEvent args) @@ -54,7 +52,7 @@ private void OnBatteryClosed(EntityUid uid, ActivatableUIRequiresPowerCellCompon // Stop drawing power if this was the last person with the UI open. if (!_uiSystem.IsUiOpen(uid, activatable.Key)) - _cell.SetPowerCellDrawEnabled(uid, false); + _toggle.TryDeactivate(uid); } /// diff --git a/Content.Shared/Weapons/Reflect/ReflectComponent.cs b/Content.Shared/Weapons/Reflect/ReflectComponent.cs index 8e7b8975d9d..8418c1f3efb 100644 --- a/Content.Shared/Weapons/Reflect/ReflectComponent.cs +++ b/Content.Shared/Weapons/Reflect/ReflectComponent.cs @@ -5,16 +5,11 @@ namespace Content.Shared.Weapons.Reflect; /// /// Entities with this component have a chance to reflect projectiles and hitscan shots +/// Uses ItemToggleComponent to control reflection. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ReflectComponent : Component { - /// - /// Can only reflect when enabled - /// - [DataField("enabled"), ViewVariables(VVAccess.ReadWrite), AutoNetworkedField] - public bool Enabled = true; - /// /// What we reflect. /// diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 7a2e733bf7c..881b547f27f 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Hands; using Content.Shared.Inventory; using Content.Shared.Inventory.Events; +using Content.Shared.Item.ItemToggle; using Content.Shared.Item.ItemToggle.Components; using Content.Shared.Popups; using Content.Shared.Projectiles; @@ -27,10 +28,11 @@ namespace Content.Shared.Weapons.Reflect; /// public sealed class ReflectSystem : EntitySystem { + [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly INetManager _netManager = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly ItemToggleSystem _toggle = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; @@ -93,7 +95,7 @@ private void OnReflectCollide(EntityUid uid, ReflectComponent component, ref Pro private bool TryReflectProjectile(EntityUid user, EntityUid reflector, EntityUid projectile, ProjectileComponent? projectileComp = null, ReflectComponent? reflect = null) { if (!Resolve(reflector, ref reflect, false) || - !reflect.Enabled || + !_toggle.IsActivated(reflector) || !TryComp(projectile, out var reflective) || (reflect.Reflects & reflective.Reflective) == 0x0 || !_random.Prob(reflect.ReflectProb) || @@ -162,7 +164,7 @@ private bool TryReflectHitscan( [NotNullWhen(true)] out Vector2? newDirection) { if (!TryComp(reflector, out var reflect) || - !reflect.Enabled || + !_toggle.IsActivated(reflector) || !_random.Prob(reflect.ReflectProb)) { newDirection = null; @@ -214,8 +216,8 @@ private void OnReflectHandUnequipped(EntityUid uid, ReflectComponent component, private void OnToggleReflect(EntityUid uid, ReflectComponent comp, ref ItemToggledEvent args) { - comp.Enabled = args.Activated; - Dirty(uid, comp); + if (args.User is {} user) + RefreshReflectUser(user); } /// @@ -225,7 +227,7 @@ private void RefreshReflectUser(EntityUid user) { foreach (var ent in _inventorySystem.GetHandOrInventoryEntities(user, SlotFlags.All & ~SlotFlags.POCKET)) { - if (!HasComp(ent)) + if (!HasComp(ent) || !_toggle.IsActivated(ent)) continue; EnsureComp(user); diff --git a/Resources/Prototypes/Actions/ninja.yml b/Resources/Prototypes/Actions/ninja.yml index adaf563692d..47cb8a83f44 100644 --- a/Resources/Prototypes/Actions/ninja.yml +++ b/Resources/Prototypes/Actions/ninja.yml @@ -2,7 +2,7 @@ - type: entity id: ActionToggleNinjaGloves name: Toggle ninja gloves - description: Toggles all glove actions on left click. Includes your doorjack, draining power, stunning enemies, downloading research and calling in a threat. + description: Toggles all glove actions on left click. Includes your doorjack, draining power, stunning enemies and hacking certain computers. components: - type: InstantAction priority: -13 @@ -21,7 +21,7 @@ state: icon itemIconStyle: NoItem priority: -10 - event: !type:CreateThrowingStarEvent {} + event: !type:CreateItemEvent {} - type: entity id: ActionRecallKatana @@ -59,7 +59,7 @@ # have to plan (un)cloaking ahead of time useDelay: 5 priority: -9 - event: !type:ToggleStealthEvent + event: !type:ToggleActionEvent # katana - type: entity @@ -72,6 +72,10 @@ sprite: Objects/Magic/magicactions.rsi state: blink itemIconStyle: NoItem + sound: + path: /Audio/Magic/blink.ogg + params: + volume: 5 priority: -12 event: !type:DashEvent checkCanAccess: false diff --git a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml index 8b73eee0d24..f1d99884658 100644 --- a/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml +++ b/Resources/Prototypes/Entities/Clothing/Hands/gloves.yml @@ -206,7 +206,7 @@ - type: FingerprintMask - type: entity - parent: ClothingHandsBase + parent: [ClothingHandsBase, BaseToggleClothing] id: ClothingHandsGlovesSpaceNinja name: space ninja gloves description: These black nano-enhanced gloves insulate from electricity and provide fire resistance. @@ -234,7 +234,31 @@ - type: Thieving stripTimeReduction: 1 stealthy: true + - type: ToggleClothing + action: ActionToggleNinjaGloves - type: NinjaGloves + abilities: + - components: + - type: BatteryDrainer + - type: StunProvider + noPowerPopup: ninja-no-power + whitelist: + components: + - Stamina + - type: EmagProvider + whitelist: + components: + - Airlock + - objective: StealResearchObjective + components: + - type: ResearchStealer + - objective: TerrorObjective + components: + - type: CommsHacker + threats: NinjaThreats + - objective: MassArrestObjective + components: + - type: CriminalRecordsHacker - type: entity parent: ClothingHandsGlovesColorBlack diff --git a/Resources/Prototypes/Entities/Clothing/Head/misc.yml b/Resources/Prototypes/Entities/Clothing/Head/misc.yml index c6a556b2d32..c32f485f9ca 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/misc.yml @@ -183,6 +183,36 @@ - type: AddAccentClothing accent: OwOAccent +- type: entity + parent: [ClothingHeadHatCatEars, BaseToggleClothing] + id: ClothingHeadHatCatEarsValid + suffix: Valid, DO NOT MAP + components: + - type: ToggleClothing + action: ActionBecomeValid + disableOnUnequip: true + - type: ComponentToggler + parent: true + components: + - type: KillSign + - type: Tag + tags: [] # ignore "WhitelistChameleon" tag + - type: Sprite + sprite: Clothing/Head/Hats/catears.rsi + - type: Clothing + sprite: Clothing/Head/Hats/catears.rsi + - type: AddAccentClothing + accent: OwOAccent + +- type: entity + noSpawn: true + id: ActionBecomeValid + name: Become Valid + description: "*notices your killsign* owo whats this" + components: + - type: InstantAction + event: !type:ToggleActionEvent + - type: entity parent: ClothingHeadBase id: ClothingHeadHatDogEars diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml index 2053ced0f63..bacdd0046f8 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml @@ -127,7 +127,7 @@ slots: WITHOUT_POCKET - type: entity - parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing] + parent: [ClothingOuterBaseLarge, AllowSuitStorageClothing, BaseToggleClothing] id: ClothingOuterSuitSpaceNinja name: space ninja suit description: This black technologically advanced, cybernetically-enhanced suit provides many abilities like invisibility or teleportation. @@ -136,9 +136,7 @@ sprite: Clothing/OuterClothing/Suits/spaceninja.rsi - type: Clothing sprite: Clothing/OuterClothing/Suits/spaceninja.rsi - - type: StealthClothing - visibility: 1.1 - toggleAction: ActionTogglePhaseCloak + # hardsuit stuff - type: PressureProtection highPressureMultiplier: 0.6 lowPressureMultiplier: 1000 @@ -151,7 +149,27 @@ Slash: 0.8 Piercing: 0.8 Heat: 0.8 + # phase cloak + - type: ToggleClothing + action: ActionTogglePhaseCloak + - type: ComponentToggler + parent: true + components: + - type: Stealth + minVisibility: 0.1 + lastVisibility: 0.1 + - type: PowerCellDraw + drawRate: 1.8 # 200 seconds on the default cell + # throwing star ability + - type: ItemCreator + action: ActionCreateThrowingStar + charge: 14.4 + spawnedPrototype: ThrowingStarNinja + noPowerPopup: ninja-no-power + # core ninja suit stuff - type: NinjaSuit + - type: UseDelay + delay: 5 # disable time - type: PowerCellSlot cellSlotId: cell_slot # throwing in a recharger would bypass glove charging mechanic diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml index d934a8b97e1..19fa86a6312 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml @@ -1,33 +1,39 @@ - type: entity - parent: ClothingShoesBase + parent: [ClothingShoesBase, BaseToggleClothing] id: ClothingShoesBootsMag name: magboots description: Magnetic boots, often used during extravehicular activity to ensure the user remains safely attached to the vehicle. components: - - type: Sprite - sprite: Clothing/Shoes/Boots/magboots.rsi - layers: - - state: icon - map: [ "enum.ToggleVisuals.Layer" ] - - type: Clothing - sprite: Clothing/Shoes/Boots/magboots.rsi - - type: Magboots - - type: ClothingSpeedModifier - walkModifier: 0.85 - sprintModifier: 0.8 - enabled: false - - type: Appearance - - type: GenericVisualizer - visuals: - enum.ToggleVisuals.Toggled: - enum.ToggleVisuals.Layer: - True: {state: icon-on} - False: {state: icon} - - type: StaticPrice - price: 200 - - type: Tag - tags: - - WhitelistChameleon + - type: Sprite + sprite: Clothing/Shoes/Boots/magboots.rsi + layers: + - state: icon + map: [ "enum.ToggleVisuals.Layer" ] + - type: Clothing + sprite: Clothing/Shoes/Boots/magboots.rsi + - type: ToggleClothing + action: ActionToggleMagboots + - type: ToggleVerb + text: toggle-magboots-verb-get-data-text + - type: ComponentToggler + components: + - type: NoSlip + - type: Magboots + - type: ClothingSpeedModifier + walkModifier: 0.85 + sprintModifier: 0.8 + - type: Appearance + - type: GenericVisualizer + visuals: + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: + True: {state: icon-on} + False: {state: icon} + - type: StaticPrice + price: 200 + - type: Tag + tags: + - WhitelistChameleon - type: entity parent: ClothingShoesBootsMag @@ -40,13 +46,9 @@ state: icon - type: Clothing sprite: Clothing/Shoes/Boots/magboots-advanced.rsi - - type: Magboots - toggleAction: ActionToggleMagbootsAdvanced - type: ClothingSpeedModifier walkModifier: 1 sprintModifier: 1 - enabled: false - - type: NoSlip - type: Tag tags: - WhitelistChameleon @@ -64,8 +66,6 @@ sprite: Clothing/Shoes/Boots/magboots-science.rsi - type: Clothing sprite: Clothing/Shoes/Boots/magboots-science.rsi - - type: Magboots - toggleAction: ActionToggleMagbootsSci - type: entity parent: ClothingShoesBootsMag @@ -76,7 +76,6 @@ - type: ClothingSpeedModifier walkModifier: 1.10 #PVS isn't too much of an issue when you are blind... sprintModifier: 1.10 - enabled: false - type: StaticPrice price: 3000 @@ -91,12 +90,9 @@ state: icon - type: Clothing sprite: Clothing/Shoes/Boots/magboots-syndicate.rsi - - type: Magboots - toggleAction: ActionToggleMagbootsSyndie - type: ClothingSpeedModifier walkModifier: 0.95 sprintModifier: 0.9 - enabled: false - type: GasTank outputPressure: 42.6 air: @@ -104,49 +100,17 @@ volume: 0.75 temperature: 293.15 moles: - - 0.153853429 # oxygen - - 0.153853429 # nitrogen + - 0.153853429 # oxygen + - 0.153853429 # nitrogen - type: Item sprite: null size: Normal - type: entity - id: ActionBaseToggleMagboots + id: ActionToggleMagboots name: Toggle Magboots description: Toggles the magboots on and off. components: - type: InstantAction - itemIconStyle: NoItem - event: !type:ToggleMagbootsEvent - -- type: entity - id: ActionToggleMagboots - parent: ActionBaseToggleMagboots - components: - - type: InstantAction - icon: { sprite: Clothing/Shoes/Boots/magboots.rsi, state: icon } - iconOn: { sprite : Clothing/Shoes/Boots/magboots.rsi, state: icon-on } - -- type: entity - id: ActionToggleMagbootsAdvanced - parent: ActionBaseToggleMagboots - components: - - type: InstantAction - icon: { sprite: Clothing/Shoes/Boots/magboots-advanced.rsi, state: icon } - iconOn: Clothing/Shoes/Boots/magboots-advanced.rsi/icon-on.png - -- type: entity - id: ActionToggleMagbootsSci - parent: ActionBaseToggleMagboots - components: - - type: InstantAction - icon: { sprite: Clothing/Shoes/Boots/magboots-science.rsi, state: icon } - iconOn: Clothing/Shoes/Boots/magboots-science.rsi/icon-on.png - -- type: entity - id: ActionToggleMagbootsSyndie - parent: ActionBaseToggleMagboots - components: - - type: InstantAction - icon: { sprite: Clothing/Shoes/Boots/magboots-syndicate.rsi, state: icon } - iconOn: Clothing/Shoes/Boots/magboots-syndicate.rsi/icon-on.png + itemIconStyle: BigItem + event: !type:ToggleActionEvent diff --git a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml index 22cd13af600..fae87172238 100644 --- a/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Shoes/misc.yml @@ -88,7 +88,7 @@ - type: NoSlip - type: entity - parent: [ClothingShoesBase, PowerCellSlotSmallItem] + parent: [ClothingShoesBase, PowerCellSlotSmallItem, BaseToggleClothing] id: ClothingShoesBootsSpeed name: speed boots description: High-tech boots woven with quantum fibers, able to convert electricity into pure speed! @@ -100,12 +100,11 @@ map: [ "enum.ToggleVisuals.Layer" ] - type: Clothing sprite: Clothing/Shoes/Boots/speedboots.rsi - - type: ToggleClothingSpeed - toggleAction: ActionToggleSpeedBoots + - type: ToggleClothing + action: ActionToggleSpeedBoots - type: ClothingSpeedModifier walkModifier: 1.5 sprintModifier: 1.5 - enabled: false - type: Appearance - type: GenericVisualizer visuals: @@ -130,10 +129,8 @@ description: Toggles the speed boots on and off. components: - type: InstantAction - itemIconStyle: NoItem - event: !type:ToggleClothingSpeedEvent - icon: { sprite: Clothing/Shoes/Boots/speedboots.rsi, state: icon } - iconOn: { sprite: Clothing/Shoes/Boots/speedboots.rsi, state: icon-on } + itemIconStyle: BigItem + event: !type:ToggleActionEvent - type: entity parent: ClothingShoesBase diff --git a/Resources/Prototypes/Entities/Clothing/base_clothing.yml b/Resources/Prototypes/Entities/Clothing/base_clothing.yml index a96ca2d23c8..02a2ddce411 100644 --- a/Resources/Prototypes/Entities/Clothing/base_clothing.yml +++ b/Resources/Prototypes/Entities/Clothing/base_clothing.yml @@ -57,3 +57,12 @@ - type: GroupExamine - type: Armor modifiers: {} + +# for clothing that can be toggled, like magboots +- type: entity + abstract: true + id: BaseToggleClothing + components: + - type: ItemToggle + onUse: false # can't really wear it like that + - type: ToggleClothing diff --git a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml index b6f9287cf7f..b8cf755d0ff 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml @@ -151,10 +151,9 @@ state: alive - type: entity + noSpawn: true + parent: BaseAntagSpawner id: SpawnPointGhostSpaceNinja - name: ghost role spawn point - suffix: space ninja - parent: MarkerBase components: - type: GhostRole name: ghost-role-information-space-ninja-name @@ -162,8 +161,6 @@ rules: ghost-role-information-antagonist-rules raffle: settings: default - - type: GhostRoleMobSpawner - prototype: MobHumanSpaceNinja - type: Sprite sprite: Markers/jobs.rsi layers: diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 3b8e5bde6ae..6656a7aadbf 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -112,6 +112,11 @@ - type: PowerCellSlot cellSlotId: cell_slot fitsInCharger: true + - type: ItemToggle + onUse: false # no item-borg toggling sorry + - type: AccessToggle + # TODO: refactor movement to just be based on toggle like speedboots but for the boots themselves + # TODO: or just have sentient speedboots be fast idk - type: PowerCellDraw drawRate: 0.6 - type: ItemSlots diff --git a/Resources/Prototypes/Entities/Mobs/Player/human.yml b/Resources/Prototypes/Entities/Mobs/Player/human.yml index 629ba91518a..0b26668e103 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/human.yml @@ -68,28 +68,3 @@ - type: NpcFactionMember factions: - Syndicate - -# Space Ninja -- type: entity - noSpawn: true - name: Space Ninja - parent: MobHuman - id: MobHumanSpaceNinja - components: - - type: RandomHumanoidAppearance - randomizeName: false - - type: Loadout - prototypes: [SpaceNinjaGear] - - type: NpcFactionMember - factions: - - Syndicate - - type: SpaceNinja - - type: GenericAntag - rule: Ninja - - type: AutoImplant - implants: - - DeathAcidifierImplant - - type: RandomMetadata - nameSegments: - - names_ninja_title - - names_ninja diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index d956f1871d7..8702bbe0218 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -142,9 +142,6 @@ - Pacified - StaminaModifier - Flashed - - type: Reflect - enabled: false - reflectProb: 0 - type: Body prototype: Human requiredLegs: 2 diff --git a/Resources/Prototypes/Entities/Objects/Devices/base_handheld.yml b/Resources/Prototypes/Entities/Objects/Devices/base_handheld.yml new file mode 100644 index 00000000000..259323fede3 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/base_handheld.yml @@ -0,0 +1,11 @@ +- type: entity + abstract: true + parent: [BaseItem, PowerCellSlotSmallItem] + id: BaseHandheldComputer + components: + - type: ActivatableUIRequiresPowerCell + - type: ItemToggle + onUse: false # above component does the toggling + - type: PowerCellDraw + drawRate: 0 + useRate: 20 diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index 84f0f4ae94c..bf0b7190b5b 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -109,6 +109,9 @@ id: BaseMedicalPDA abstract: true components: + - type: ItemToggle + toggleLight: false + onUse: false - type: HealthAnalyzer scanDelay: 1 scanningEndSound: diff --git a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml index 0d2f890a1d3..54fc4a70c5a 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/station_map.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/station_map.yml @@ -1,9 +1,9 @@ - type: entity + parent: BaseItem id: BaseHandheldStationMap name: station map description: Displays a readout of the current station. abstract: true - parent: BaseItem components: - type: StationMap - type: Sprite @@ -34,14 +34,9 @@ - type: entity id: HandheldStationMap parent: - - BaseHandheldStationMap - - PowerCellSlotSmallItem + - BaseHandheldStationMap + - BaseHandheldComputer suffix: Handheld, Powered - components: - - type: PowerCellDraw - drawRate: 0 - useRate: 20 - - type: ActivatableUIRequiresPowerCell - type: entity id: HandheldStationMapEmpty diff --git a/Resources/Prototypes/Entities/Objects/Shields/shields.yml b/Resources/Prototypes/Entities/Objects/Shields/shields.yml index dc24ac485a1..8182accfb6f 100644 --- a/Resources/Prototypes/Entities/Objects/Shields/shields.yml +++ b/Resources/Prototypes/Entities/Objects/Shields/shields.yml @@ -382,8 +382,10 @@ path: /Audio/Weapons/ebladehum.ogg - type: ItemToggleSize activatedSize: Huge - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.6 + - type: ComponentToggler + components: + - type: DisarmMalus + malus: 0.6 - type: Sprite sprite: Objects/Weapons/Melee/e_shield.rsi layers: @@ -415,7 +417,6 @@ energy: 2 color: blue - type: Reflect - enabled: false reflectProb: 0.95 reflects: - Energy @@ -492,8 +493,10 @@ path: /Audio/Weapons/telescopicoff.ogg params: volume: -5 - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.6 + - type: ComponentToggler + components: + - type: DisarmMalus + malus: 0.6 - type: ItemToggleSize activatedSize: Huge - type: Sprite diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml index 4a61074a8d5..2d06ed0f1dc 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/defib.yml @@ -31,6 +31,11 @@ size: Large - type: Speech speechVerb: Robotic + - type: ItemToggle + soundActivate: + path: /Audio/Items/Defib/defib_safety_on.ogg + soundDeactivate: + path: /Audio/Items/Defib/defib_safety_off.ogg - type: Defibrillator zapHeal: types: diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml index 19a0b36ee9f..c9ab24274da 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml @@ -1,9 +1,7 @@ - type: entity name: handheld crew monitor suffix: DO NOT MAP - parent: - - BaseItem - - PowerCellSlotSmallItem + parent: BaseHandheldComputer # CMO-only bud, don't add more. id: HandheldCrewMonitor description: A hand-held crew monitor displaying the status of suit sensors. @@ -11,10 +9,6 @@ - type: Sprite sprite: Objects/Specific/Medical/handheldcrewmonitor.rsi state: scanner - - type: PowerCellDraw - drawRate: 0 - useRate: 20 - - type: ActivatableUIRequiresPowerCell - type: ActivatableUI key: enum.CrewMonitoringUIKey.Key - type: UserInterface diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml index c01aaa84a9d..d8547d92948 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml @@ -21,6 +21,8 @@ interfaces: enum.HealthAnalyzerUiKey.Key: type: HealthAnalyzerBoundUserInterface + - type: ItemToggle + onUse: false - type: HealthAnalyzer scanningEndSound: path: "/Audio/Items/Medical/healthscanner.ogg" diff --git a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml index bf0a3be4e51..99f874406b4 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml @@ -36,23 +36,22 @@ - state: screen shader: unshaded visible: false - map: ["enum.PowerDeviceVisualLayers.Powered"] + map: ["enum.ToggleVisuals.Layer"] - type: Appearance - type: GenericVisualizer visuals: - enum.ProximityBeeperVisuals.Enabled: - enum.PowerDeviceVisualLayers.Powered: + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: True: { visible: true } False: { visible: false } + - type: ItemToggle - type: ProximityBeeper - type: ProximityDetector - enabled: false range: 20 criteria: components: - Anomaly - type: Beeper - enabled: false minBeepInterval: 0.15 maxBeepInterval: 1.00 beepSound: diff --git a/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml b/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml index 5938193181e..78abedc28ab 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/handheld_mass_scanner.yml @@ -1,6 +1,6 @@ - type: entity name: handheld mass scanner - parent: [ BaseItem, PowerCellSlotSmallItem] + parent: BaseHandheldComputer id: HandHeldMassScanner description: A hand-held mass scanner. components: @@ -27,7 +27,6 @@ - type: PowerCellDraw drawRate: 3 useRate: 100 - - type: ActivatableUIRequiresPowerCell - type: ActivatableUI key: enum.RadarConsoleUiKey.Key inHandsOnly: true diff --git a/Resources/Prototypes/Entities/Objects/Tools/welders.yml b/Resources/Prototypes/Entities/Objects/Tools/welders.yml index 9db30edb52e..cd188969b53 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/welders.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/welders.yml @@ -55,8 +55,10 @@ - type: ItemToggleSize activatedSize: Large - type: ItemToggleHot - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.6 + - type: ComponentToggler + components: + - type: DisarmMalus + malus: 0.6 - type: ToggleableLightVisuals spriteLayer: flame inhandVisuals: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml index 0d10411cfbb..19a0cf30e86 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Launchers/launchers.yml @@ -166,6 +166,8 @@ - type: TetherGun frequency: 5 dampingRatio: 4 + - type: ItemToggle + onUse: false - type: PowerCellDraw - type: Sprite sprite: Objects/Weapons/Guns/Launchers/tether_gun.rsi @@ -211,6 +213,8 @@ path: /Audio/Weapons/soup.ogg params: volume: 2 + - type: ItemToggle + onUse: false - type: PowerCellDraw - type: Sprite sprite: Objects/Weapons/Guns/Launchers/force_gun.rsi diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml index fbf8b1003c3..ce3545920ac 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/e_sword.yml @@ -13,10 +13,12 @@ - type: ItemToggleActiveSound activeSound: path: /Audio/Weapons/ebladehum.ogg - - type: ItemToggleSharp + - type: ComponentToggler + components: + - type: Sharp + - type: DisarmMalus + malus: 0.6 - type: ItemToggleHot - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.6 - type: ItemToggleSize activatedSize: Huge - type: ItemToggleMeleeWeapon @@ -74,10 +76,7 @@ right: - state: inhand-right-blade shader: unshaded - - type: DisarmMalus - malus: 0 - type: Reflect - enabled: false - type: IgnitionSource temperature: 700 @@ -88,7 +87,6 @@ suffix: E-Dagger description: 'A dark ink pen.' components: - - type: EnergySword - type: ItemToggle soundActivate: path: /Audio/Weapons/ebladeon.ogg @@ -114,8 +112,11 @@ path: /Audio/Weapons/ebladehum.ogg params: volume: -6 - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.4 + - type: ComponentToggler + components: + - type: Sharp + - type: DisarmMalus + malus: 0.4 - type: Sprite sprite: Objects/Weapons/Melee/e_dagger.rsi layers: @@ -183,15 +184,12 @@ id: EnergyCutlass description: An exotic energy weapon. components: - - type: EnergySword - type: ItemToggleMeleeWeapon activatedDamage: types: Slash: 10 Heat: 12 deactivatedSecret: true - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.6 - type: Sprite sprite: Objects/Weapons/Melee/e_cutlass.rsi layers: @@ -220,8 +218,8 @@ id: EnergySwordDouble description: Syndicate Command Interns thought that having one blade on the energy sword was not enough. This can be stored in pockets. components: - - type: EnergySword - type: ItemToggle + onUse: false # wielding events control it instead soundActivate: path: /Audio/Weapons/ebladeon.ogg params: @@ -246,9 +244,13 @@ path: /Audio/Weapons/ebladehum.ogg params: volume: 3 - - type: ItemToggleDisarmMalus - activatedDisarmMalus: 0.7 + - type: ComponentToggler + components: + - type: Sharp + - type: DisarmMalus + malus: 0.7 - type: Wieldable + wieldSound: null # esword light sound instead - type: MeleeWeapon wideAnimationRotation: -135 attackRate: 1.5 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml index d0d85beb6f5..0bab2c828de 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/sword.yml @@ -30,7 +30,6 @@ soundHit: path: /Audio/Weapons/bladeslice.ogg - type: Reflect - enabled: true reflectProb: .1 spread: 90 - type: Item @@ -175,7 +174,6 @@ soundHit: path: /Audio/Effects/explosion_small1.ogg - type: Reflect - enabled: true reflectProb: .25 spread: 90 - type: Item diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml index 4cb76ea4b92..0485b5a5178 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml @@ -85,6 +85,7 @@ whitelist: components: - FitsInDispenser + - type: ItemToggle - type: HealthAnalyzer scanDelay: 0 - type: UserInterface diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 39e29ad1158..0b183039a99 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -149,7 +149,43 @@ earliestStart: 30 reoccurrenceDelay: 20 minimumPlayers: 30 - - type: NinjaSpawnRule + - type: SpaceSpawnRule + - type: AntagLoadProfileRule + - type: AntagObjectives + objectives: + - StealResearchObjective + - DoorjackObjective + - SpiderChargeObjective + - TerrorObjective + - MassArrestObjective + - NinjaSurviveObjective + - type: AntagSelection + agentName: ninja-round-end-agent-name + definitions: + - spawnerPrototype: SpawnPointGhostSpaceNinja + min: 1 + max: 1 + pickPlayer: false + startingGear: SpaceNinjaGear + briefing: + text: ninja-role-greeting + color: Green + sound: /Audio/Misc/ninja_greeting.ogg + components: + - type: SpaceNinja + - type: NpcFactionMember + factions: + - Syndicate + - type: AutoImplant + implants: + - DeathAcidifierImplant + - type: RandomMetadata + nameSegments: + - names_ninja_title + - names_ninja + mindComponents: + - type: NinjaRole + prototype: SpaceNinja - type: entity parent: BaseGameRule diff --git a/Resources/Prototypes/GameRules/midround.yml b/Resources/Prototypes/GameRules/midround.yml index 5a38c222154..43cdca3bc33 100644 --- a/Resources/Prototypes/GameRules/midround.yml +++ b/Resources/Prototypes/GameRules/midround.yml @@ -1,21 +1,3 @@ -# doesnt spawn a ninja or anything, just stores configuration for it -# see NinjaSpawn event for spawning -- type: entity - id: Ninja - parent: BaseGameRule - components: - - type: GenericAntagRule - agentName: ninja-round-end-agent-name - objectives: - - StealResearchObjective - - DoorjackObjective - - SpiderChargeObjective - - TerrorObjective - - MassArrestObjective - - NinjaSurviveObjective - - type: NinjaRule - threats: NinjaThreats - - type: entity parent: BaseGameRule id: Thief From e6f55fafb49625678bff63211313f06aae48e159 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Thu, 11 Jul 2024 02:14:34 -0400 Subject: [PATCH 11/78] Fix AtmosDeviceSystem debug assert Heisenbug (#29752) Fix AtmosDeviceSystem debug assertion Heisenbug --- .../Tests/Atmos/GridJoinTest.cs | 53 +++++++++++++++++++ .../Piping/EntitySystems/AtmosDeviceSystem.cs | 11 +++- 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs diff --git a/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs b/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs new file mode 100644 index 00000000000..3a1ec7fd40e --- /dev/null +++ b/Content.IntegrationTests/Tests/Atmos/GridJoinTest.cs @@ -0,0 +1,53 @@ +using Content.Server.Atmos.Components; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Atmos.Piping.Components; +using Content.Server.Atmos.Piping.EntitySystems; +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.Tests.Atmos; + +[TestFixture] +public sealed class GridJoinTest +{ + private const string CanisterProtoId = "AirCanister"; + + [Test] + public async Task TestGridJoinAtmosphere() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entMan = server.EntMan; + var protoMan = server.ProtoMan; + var atmosSystem = entMan.System(); + var atmosDeviceSystem = entMan.System(); + var transformSystem = entMan.System(); + + var testMap = await pair.CreateTestMap(); + + await server.WaitPost(() => + { + // Spawn an atmos device on the grid + var canister = entMan.Spawn(CanisterProtoId); + transformSystem.SetCoordinates(canister, testMap.GridCoords); + var deviceComp = entMan.GetComponent(canister); + var canisterEnt = (canister, deviceComp); + + // Make sure the canister is tracked as an off-grid device + Assert.That(atmosDeviceSystem.IsJoinedOffGrid(canisterEnt)); + + // Add an atmosphere to the grid + entMan.AddComponent(testMap.Grid); + + // Force AtmosDeviceSystem to update off-grid devices + // This means the canister is now considered on-grid, + // but it's still tracked as off-grid! + Assert.DoesNotThrow(() => atmosDeviceSystem.Update(atmosSystem.AtmosTime)); + + // Make sure that the canister is now properly tracked as on-grid + Assert.That(atmosDeviceSystem.IsJoinedOffGrid(canisterEnt), Is.False); + }); + + await pair.CleanReturnAsync(); + } +} diff --git a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs index 3c73a8f64ee..f932ef36208 100644 --- a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs +++ b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs @@ -132,10 +132,19 @@ public override void Update(float frameTime) var ev = new AtmosDeviceUpdateEvent(_atmosphereSystem.AtmosTime, null, null); foreach (var device in _joinedDevices) { - DebugTools.Assert(!HasComp(Transform(device).GridUid)); + var deviceGrid = Transform(device).GridUid; + if (HasComp(deviceGrid)) + { + RejoinAtmosphere(device); + } RaiseLocalEvent(device, ref ev); device.Comp.LastProcess = time; } } + + public bool IsJoinedOffGrid(Entity device) + { + return _joinedDevices.Contains(device); + } } } From 03f0257ae9b0c3d2d349bd2bcc4670a7ac92e139 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:04:10 +1000 Subject: [PATCH 12/78] Revert "Picking a ghostrole as an admin will now deadmin you. (#29790)" (#29901) This reverts commit 1a50760e674134de6065bff3bc76739526ea5429. --- Content.Server/Ghost/Roles/GhostRoleSystem.cs | 13 ------------- Content.Shared/CCVar/CCVars.cs | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/Content.Server/Ghost/Roles/GhostRoleSystem.cs b/Content.Server/Ghost/Roles/GhostRoleSystem.cs index dfb0c8e0865..b6627f11540 100644 --- a/Content.Server/Ghost/Roles/GhostRoleSystem.cs +++ b/Content.Server/Ghost/Roles/GhostRoleSystem.cs @@ -31,9 +31,6 @@ using Content.Server.Popups; using Content.Shared.Verbs; using Robust.Shared.Collections; -using Content.Server.Administration.Managers; -using Content.Shared.CCVar; -using Robust.Shared.Configuration; namespace Content.Server.Ghost.Roles { @@ -51,8 +48,6 @@ public sealed class GhostRoleSystem : EntitySystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IAdminManager _adminManager = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; private uint _nextRoleIdentifier; private bool _needsUpdateGhostRoleCount = true; @@ -592,14 +587,6 @@ private void OnPlayerAttached(PlayerAttachedEvent message) // forced into a ghost role. LeaveAllRaffles(message.Player); CloseEui(message.Player); - - // The player is no longer a ghost, so they should not be adminned anymore. Deadmin them. - // Ensures that admins do not forget to deadmin themselves upon entering a ghost role. - var autoDeAdmin = _cfg.GetCVar(CCVars.AdminDeadminOnJoin); - if (autoDeAdmin && _adminManager.IsAdmin(message.Entity)) - { - _adminManager.DeAdmin(message.Player); - } } private void OnMindAdded(EntityUid uid, GhostTakeoverAvailableComponent component, MindAddedMessage args) diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 044a95c4583..a0e9157e922 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -870,7 +870,7 @@ public static readonly CVarDef CVarDef.Create("admin.show_pii_onban", false, CVar.SERVERONLY); /// - /// If an admin joins a round by readying up or using the late join button, automatically + /// If an admin joins a round by reading up or using the late join button, automatically /// de-admin them. /// public static readonly CVarDef AdminDeadminOnJoin = From 9a17154d83f99da09ae42ef99bb8f3e6b09f2451 Mon Sep 17 00:00:00 2001 From: chavonadelal <156101927+chavonadelal@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:02:53 +0300 Subject: [PATCH 13/78] =?UTF-8?q?=D0=90nnouncement=20sender=20localization?= =?UTF-8?q?=20(#29907)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Content.Server/Chat/Systems/AnnounceOnSpawnSystem.cs | 2 +- Content.Server/Chat/Systems/ChatSystem.cs | 8 ++++++-- Resources/Locale/en-US/chat/managers/chat-manager.ftl | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Content.Server/Chat/Systems/AnnounceOnSpawnSystem.cs b/Content.Server/Chat/Systems/AnnounceOnSpawnSystem.cs index 0f0365e56ba..cc46545237b 100644 --- a/Content.Server/Chat/Systems/AnnounceOnSpawnSystem.cs +++ b/Content.Server/Chat/Systems/AnnounceOnSpawnSystem.cs @@ -16,7 +16,7 @@ public override void Initialize() private void OnInit(EntityUid uid, AnnounceOnSpawnComponent comp, MapInitEvent args) { var message = Loc.GetString(comp.Message); - var sender = comp.Sender != null ? Loc.GetString(comp.Sender) : "Central Command"; + var sender = comp.Sender != null ? Loc.GetString(comp.Sender) : Loc.GetString("chat-manager-sender-announcement"); _chat.DispatchGlobalAnnouncement(message, sender, playSound: true, comp.Sound, comp.Color); } } diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index 55beaf1f7f5..5358cdb4420 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -319,12 +319,14 @@ public void TrySendInGameOOCMessage( /// Optional color for the announcement message public void DispatchGlobalAnnouncement( string message, - string sender = "Central Command", + string? sender = null, bool playSound = true, SoundSpecifier? announcementSound = null, Color? colorOverride = null ) { + sender ??= Loc.GetString("chat-manager-sender-announcement"); + var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message))); _chatManager.ChatMessageToAll(ChatChannel.Radio, message, wrappedMessage, default, false, true, colorOverride); if (playSound) @@ -345,11 +347,13 @@ public void DispatchGlobalAnnouncement( public void DispatchStationAnnouncement( EntityUid source, string message, - string sender = "Central Command", + string? sender = null, bool playDefaultSound = true, SoundSpecifier? announcementSound = null, Color? colorOverride = null) { + sender ??= Loc.GetString("chat-manager-sender-announcement"); + var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message))); var station = _stationSystem.GetOwningStation(source); diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index 38b12251bec..75ff4351c1d 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -19,6 +19,7 @@ chat-manager-no-such-channel = There is no channel with key '{$key}'! chat-manager-whisper-headset-on-message = You can't whisper on the radio! chat-manager-server-wrap-message = [bold]{$message}[/bold] +chat-manager-sender-announcement = Central Command chat-manager-sender-announcement-wrap-message = [font size=14][bold]{$sender} Announcement:[/font][font size=12] {$message}[/bold][/font] chat-manager-entity-say-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent]{$message}[/BubbleContent]"[/font] From bb673649000315ddaa226342a795ac65e1f65367 Mon Sep 17 00:00:00 2001 From: Winkarst-cpu <74284083+Winkarst-cpu@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:03:21 +0300 Subject: [PATCH 14/78] Better admin note popups text visibility (#29909) * Better admin note popup text visibility * Bring buttons closer to the border --- .../Administration/UI/Notes/AdminNotesLinePopup.xaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml b/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml index f978138ca58..4a3c0ef7ace 100644 --- a/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml +++ b/Content.Client/Administration/UI/Notes/AdminNotesLinePopup.xaml @@ -1,10 +1,10 @@ - + - + - + [DataField] [ViewVariables(VVAccess.ReadWrite)] - public float BaseThrowspeed { get; set; } = 10f; + public float BaseThrowspeed { get; set; } = 11f; /// /// Distance after which longer throw targets stop increasing throw impulse. diff --git a/Content.Shared/Throwing/ThrowingSystem.cs b/Content.Shared/Throwing/ThrowingSystem.cs index 56bbf4c2bf3..549473278e4 100644 --- a/Content.Shared/Throwing/ThrowingSystem.cs +++ b/Content.Shared/Throwing/ThrowingSystem.cs @@ -26,11 +26,6 @@ public sealed class ThrowingSystem : EntitySystem public const float PushbackDefault = 2f; - /// - /// The minimum amount of time an entity needs to be thrown before the timer can be run. - /// Anything below this threshold never enters the air. - /// - public const float MinFlyTime = 0.15f; public const float FlyTimePercentage = 0.8f; private float _frictionModifier; @@ -168,9 +163,6 @@ public void TryThrow(EntityUid uid, var flyTime = direction.Length() / baseThrowSpeed; if (compensateFriction) flyTime *= FlyTimePercentage; - - if (flyTime < MinFlyTime) - flyTime = 0f; comp.ThrownTime = _gameTiming.CurTime; comp.LandTime = comp.ThrownTime + TimeSpan.FromSeconds(flyTime); comp.PlayLandSound = playSound; From caf8776d071027bdefdd0003fb2e99a981040fa6 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 12 Jul 2024 10:33:53 +0000 Subject: [PATCH 30/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 90c02169465..5784e248714 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: IProduceWidgets - changes: - - message: New map Oasis. - type: Add - id: 6409 - time: '2024-04-21T05:59:12.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/25736 - author: KittenColony changes: - message: Added a new lizard snout, featuring a split muzzle and snoot that can @@ -3821,3 +3814,14 @@ id: 6908 time: '2024-07-12T09:24:08.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29900 +- author: coffeeware, slarticodefast + changes: + - message: Fixed items thrown very fast not being in the air. In particular this + fixes the pneumatic cannon. + type: Fix + - message: Buffed base hand throwing speed by 10% to be more similar to before the + recent throwing changes. + type: Tweak + id: 6909 + time: '2024-07-12T10:32:47.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29935 From 7ce3a1f27ff5a1f4c4aa5898227bfca2a427202c Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Fri, 12 Jul 2024 13:36:40 +0200 Subject: [PATCH 31/78] Fix supplybot movement state (#29944) Fix supplybot movement sprite --- Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index 172ed66cc3a..d34b1b536b2 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -337,7 +337,8 @@ - type: Sprite sprite: Mobs/Silicon/Bots/supplybot.rsi layers: - - state: supplybot + - map: ["enum.DamageStateVisualLayers.Base", "movement"] + state: supplybot - type: SpriteMovement movementLayers: movement: From 3ee7d7bc353dffccc0fee088d2480bb11cf78c3a Mon Sep 17 00:00:00 2001 From: osjarw <62134478+osjarw@users.noreply.github.com> Date: Fri, 12 Jul 2024 14:37:47 +0300 Subject: [PATCH 32/78] Implement Health Consideration for NPCs (#29922) Implement TargetHealthCon --- .../NPC/Queries/Considerations/TargetHealthCon.cs | 10 ++++++++++ Content.Server/NPC/Systems/NPCUtilitySystem.cs | 12 +++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Content.Server/NPC/Queries/Considerations/TargetHealthCon.cs b/Content.Server/NPC/Queries/Considerations/TargetHealthCon.cs index d4e8a277ea6..cc9c6df83f3 100644 --- a/Content.Server/NPC/Queries/Considerations/TargetHealthCon.cs +++ b/Content.Server/NPC/Queries/Considerations/TargetHealthCon.cs @@ -1,6 +1,16 @@ +using Content.Shared.Mobs; + namespace Content.Server.NPC.Queries.Considerations; +/// +/// Goes linearly from 1f to 0f, with 0 damage returning 1f and damage returning 0f +/// public sealed partial class TargetHealthCon : UtilityConsideration { + /// + /// Which MobState the consideration returns 0f at, defaults to choosing earliest incapacitating MobState + /// + [DataField("targetState")] + public MobState TargetState = MobState.Invalid; } diff --git a/Content.Server/NPC/Systems/NPCUtilitySystem.cs b/Content.Server/NPC/Systems/NPCUtilitySystem.cs index ca74d713357..72833fc35ef 100644 --- a/Content.Server/NPC/Systems/NPCUtilitySystem.cs +++ b/Content.Server/NPC/Systems/NPCUtilitySystem.cs @@ -7,10 +7,13 @@ using Content.Server.Nutrition.Components; using Content.Server.Nutrition.EntitySystems; using Content.Server.Storage.Components; +using Content.Shared.Damage; using Content.Shared.Examine; using Content.Shared.Fluids.Components; using Content.Shared.Hands.Components; using Content.Shared.Inventory; +using Content.Shared.Mobs; +using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.NPC.Systems; using Content.Shared.Nutrition.Components; @@ -48,6 +51,7 @@ public sealed class NPCUtilitySystem : EntitySystem [Dependency] private readonly WeldableSystem _weldable = default!; [Dependency] private readonly ExamineSystemShared _examine = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly MobThresholdSystem _thresholdSystem = default!; private EntityQuery _puddleQuery; private EntityQuery _xformQuery; @@ -293,8 +297,14 @@ private float GetScore(NPCBlackboard blackboard, EntityUid targetUid, UtilityCon return (float) ev.Count / ev.Capacity; } - case TargetHealthCon: + case TargetHealthCon con: { + if (!TryComp(targetUid, out DamageableComponent? damage)) + return 0f; + if (con.TargetState != MobState.Invalid && _thresholdSystem.TryGetPercentageForState(targetUid, con.TargetState, damage.TotalDamage, out var percentage)) + return Math.Clamp((float)(1 - percentage), 0f, 1f); + if (_thresholdSystem.TryGetIncapPercentage(targetUid, damage.TotalDamage, out var incapPercentage)) + return Math.Clamp((float)(1 - incapPercentage), 0f, 1f); return 0f; } case TargetInLOSCon: From a314a967c29f7f2b2322e1213cd894bb76b6ccc4 Mon Sep 17 00:00:00 2001 From: themias <89101928+themias@users.noreply.github.com> Date: Fri, 12 Jul 2024 07:38:58 -0400 Subject: [PATCH 33/78] Fix timer deconstruction (#29917) Fix deconstruction for timers --- .../Entities/Structures/Wallmounts/timer.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml index dd7eb5bea82..271a9a65cdc 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/timer.yml @@ -44,6 +44,14 @@ - type: Construction graph: Timer node: signal + containers: + - board + - type: ContainerContainer + containers: + board: !type:Container + - type: ContainerFill + containers: + board: [ SignalTimerElectronics ] - type: entity id: ScreenTimer @@ -67,6 +75,11 @@ - type: Construction graph: Timer node: screen + containers: + - board + - type: ContainerFill + containers: + board: [ ScreenTimerElectronics ] - type: entity id: BrigTimer @@ -79,6 +92,11 @@ - type: Construction graph: Timer node: brig + containers: + - board + - type: ContainerFill + containers: + board: [ BrigTimerElectronics ] # Construction Frame From e3697c6fdafc72ebb55aaeecfe5fa52da61c70d7 Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 12 Jul 2024 11:40:04 +0000 Subject: [PATCH 34/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 5784e248714..e51638b71e9 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: KittenColony - changes: - - message: Added a new lizard snout, featuring a split muzzle and snoot that can - be coloured independently - type: Add - id: 6410 - time: '2024-04-21T06:18:33.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27143 - author: SpeltIncorrectyl changes: - message: Security Officers, Head of Security, and Warden can now choose to start @@ -3825,3 +3817,10 @@ id: 6909 time: '2024-07-12T10:32:47.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29935 +- author: themias + changes: + - message: Timers can now be deconstructed + type: Fix + id: 6910 + time: '2024-07-12T11:38:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29917 From c94c72785df97e7a98097a0bc4733e998e4454a8 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Fri, 12 Jul 2024 04:44:45 -0700 Subject: [PATCH 35/78] Updates GasTankSystem and InternalsSystem to not use Component.Owner (#29934) * blah, setup * Updates GasTankSystem and InternalsSystem to not use Component.Owner * squish the diff * Fixa the rest --------- Co-authored-by: plykiya --- .../Atmos/EntitySystems/GasTankSystem.cs | 72 +++++++++++++------ .../Body/Systems/InternalsSystem.cs | 21 +++--- 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs index baad739804b..2d703439c0f 100644 --- a/Content.Server/Atmos/EntitySystems/GasTankSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTankSystem.cs @@ -81,7 +81,7 @@ public void UpdateUserInterface(Entity ent, bool initialUpdate TankPressure = component.Air?.Pressure ?? 0, OutputPressure = initialUpdate ? component.OutputPressure : null, InternalsConnected = component.IsConnected, - CanConnectInternals = CanConnectToInternals(component) + CanConnectInternals = CanConnectToInternals(ent) }); } @@ -217,24 +217,24 @@ public GasMixture RemoveAirVolume(Entity gasTank, float volume return air; } - public bool CanConnectToInternals(GasTankComponent component) + public bool CanConnectToInternals(Entity ent) { - var internals = GetInternalsComponent(component, component.User); - return internals != null && internals.BreathTools.Count != 0 && !component.IsValveOpen; + TryGetInternalsComp(ent, out _, out var internalsComp, ent.Comp.User); + return internalsComp != null && internalsComp.BreathTools.Count != 0 && !ent.Comp.IsValveOpen; } public void ConnectToInternals(Entity ent) { var (owner, component) = ent; - if (component.IsConnected || !CanConnectToInternals(component)) + if (component.IsConnected || !CanConnectToInternals(ent)) return; - var internals = GetInternalsComponent(component); - if (internals == null) + TryGetInternalsComp(ent, out var internalsUid, out var internalsComp, ent.Comp.User); + if (internalsUid == null || internalsComp == null) return; - if (_internals.TryConnectTank((internals.Owner, internals), owner)) - component.User = internals.Owner; + if (_internals.TryConnectTank((internalsUid.Value, internalsComp), owner)) + component.User = internalsUid.Value; _actions.SetToggled(component.ToggleActionEntity, component.IsConnected); @@ -243,7 +243,7 @@ public void ConnectToInternals(Entity ent) return; component.ConnectStream = _audioSys.Stop(component.ConnectStream); - component.ConnectStream = _audioSys.PlayPvs(component.ConnectSound, component.Owner)?.Entity; + component.ConnectStream = _audioSys.PlayPvs(component.ConnectSound, owner)?.Entity; UpdateUserInterface(ent); } @@ -251,29 +251,59 @@ public void ConnectToInternals(Entity ent) public void DisconnectFromInternals(Entity ent) { var (owner, component) = ent; + if (component.User == null) return; - var internals = GetInternalsComponent(component); + TryGetInternalsComp(ent, out var internalsUid, out var internalsComp, component.User); component.User = null; _actions.SetToggled(component.ToggleActionEntity, false); - _internals.DisconnectTank(internals); + if (internalsUid != null && internalsComp != null) + _internals.DisconnectTank((internalsUid.Value, internalsComp)); component.DisconnectStream = _audioSys.Stop(component.DisconnectStream); - component.DisconnectStream = _audioSys.PlayPvs(component.DisconnectSound, component.Owner)?.Entity; + component.DisconnectStream = _audioSys.PlayPvs(component.DisconnectSound, owner)?.Entity; UpdateUserInterface(ent); } - private InternalsComponent? GetInternalsComponent(GasTankComponent component, EntityUid? owner = null) + /// + /// Tries to retrieve the internals component of either the gas tank's user, + /// or the gas tank's... containing container + /// + /// The user of the gas tank + /// True if internals comp isn't null, false if it is null + private bool TryGetInternalsComp(Entity ent, out EntityUid? internalsUid, out InternalsComponent? internalsComp, EntityUid? user = null) { - owner ??= component.User; - if (Deleted(component.Owner))return null; - if (owner != null) return CompOrNull(owner.Value); - return _containers.TryGetContainingContainer(component.Owner, out var container) - ? CompOrNull(container.Owner) - : null; + internalsUid = default; + internalsComp = default; + + // If the gas tank doesn't exist for whatever reason, don't even bother + if (TerminatingOrDeleted(ent.Owner)) + return false; + + user ??= ent.Comp.User; + // Check if the gas tank's user actually has the component that allows them to use a gas tank and mask + if (TryComp(user, out var userInternalsComp) && userInternalsComp != null) + { + internalsUid = user; + internalsComp = userInternalsComp; + return true; + } + + // Yeah I have no clue what this actually does, I appreciate the lack of comments on the original function + if (_containers.TryGetContainingContainer((ent.Owner, Transform(ent.Owner)), out var container) && container != null) + { + if (TryComp(container.Owner, out var containerInternalsComp) && containerInternalsComp != null) + { + internalsUid = container.Owner; + internalsComp = containerInternalsComp; + return true; + } + } + + return false; } public void AssumeAir(Entity ent, GasMixture giver) @@ -321,7 +351,7 @@ public void CheckStatus(Entity ent) if(environment != null) _atmosphereSystem.Merge(environment, component.Air); - _audioSys.PlayPvs(component.RuptureSound, Transform(component.Owner).Coordinates, AudioParams.Default.WithVariation(0.125f)); + _audioSys.PlayPvs(component.RuptureSound, Transform(owner).Coordinates, AudioParams.Default.WithVariation(0.125f)); QueueDel(owner); return; diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index d6ece39d590..10ebd934aa4 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -102,7 +102,7 @@ public void ToggleInternals( { if (force) { - DisconnectTank(internals); + DisconnectTank((uid, internals)); return; } @@ -199,16 +199,13 @@ public void ConnectBreathTool(Entity ent, EntityUid toolEnti _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } - public void DisconnectTank(InternalsComponent? component) + public void DisconnectTank(Entity ent) { - if (component is null) - return; - - if (TryComp(component.GasTankEntity, out GasTankComponent? tank)) - _gasTank.DisconnectFromInternals((component.GasTankEntity.Value, tank)); + if (TryComp(ent.Comp.GasTankEntity, out GasTankComponent? tank)) + _gasTank.DisconnectFromInternals((ent.Comp.GasTankEntity.Value, tank)); - component.GasTankEntity = null; - _alerts.ShowAlert(component.Owner, component.InternalsAlert, GetSeverity(component)); + ent.Comp.GasTankEntity = null; + _alerts.ShowAlert(ent.Owner, ent.Comp.InternalsAlert, GetSeverity(ent.Comp)); } public bool TryConnectTank(Entity ent, EntityUid tankEntity) @@ -267,21 +264,21 @@ private short GetSeverity(InternalsComponent component) if (_inventory.TryGetSlotEntity(user, "back", out var backEntity, user.Comp2, user.Comp3) && TryComp(backEntity, out var backGasTank) && - _gasTank.CanConnectToInternals(backGasTank)) + _gasTank.CanConnectToInternals((backEntity.Value, backGasTank))) { return (backEntity.Value, backGasTank); } if (_inventory.TryGetSlotEntity(user, "suitstorage", out var entity, user.Comp2, user.Comp3) && TryComp(entity, out var gasTank) && - _gasTank.CanConnectToInternals(gasTank)) + _gasTank.CanConnectToInternals((entity.Value, gasTank))) { return (entity.Value, gasTank); } foreach (var item in _inventory.GetHandOrInventoryEntities((user.Owner, user.Comp1, user.Comp2))) { - if (TryComp(item, out gasTank) && _gasTank.CanConnectToInternals(gasTank)) + if (TryComp(item, out gasTank) && _gasTank.CanConnectToInternals((item, gasTank))) return (item, gasTank); } From 2e694f109d3eb998dc237a0fc8ef4a241469b4e9 Mon Sep 17 00:00:00 2001 From: Jonathan Argo Date: Fri, 12 Jul 2024 11:00:40 -0400 Subject: [PATCH 36/78] Centered hard hat sprites (#29953) --- .../Clothing/Head/Hardhats/armored.rsi/icon.png | Bin 326 -> 288 bytes .../Head/Hardhats/armored.rsi/light-icon.png | Bin 256 -> 202 bytes .../Clothing/Head/Hardhats/blue.rsi/icon.png | Bin 242 -> 193 bytes .../Head/Hardhats/blue.rsi/light-icon.png | Bin 256 -> 204 bytes .../Head/Hardhats/dark_yellow.rsi/icon.png | Bin 242 -> 193 bytes .../Hardhats/dark_yellow.rsi/light-icon.png | Bin 256 -> 204 bytes .../Clothing/Head/Hardhats/orange.rsi/icon.png | Bin 242 -> 192 bytes .../Head/Hardhats/orange.rsi/light-icon.png | Bin 243 -> 204 bytes .../Clothing/Head/Hardhats/red.rsi/icon.png | Bin 242 -> 192 bytes .../Head/Hardhats/red.rsi/light-icon.png | Bin 256 -> 204 bytes .../Clothing/Head/Hardhats/white.rsi/icon.png | Bin 242 -> 193 bytes .../Head/Hardhats/white.rsi/light-icon.png | Bin 243 -> 204 bytes .../Clothing/Head/Hardhats/yellow.rsi/icon.png | Bin 242 -> 192 bytes .../Head/Hardhats/yellow.rsi/light-icon.png | Bin 256 -> 204 bytes 14 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/Textures/Clothing/Head/Hardhats/armored.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/armored.rsi/icon.png index 7adc2b1385919de3691bf660a7c812275c6f40da..fc2328e7d149e66e43586cab81345675bbee87ef 100644 GIT binary patch delta 247 zcmV%Q6F@FSSK}|sb0I`n?{9y$E000SaNLh0L01m60x&w?%HUJuEjTQbo6co zvf7vKRX8F0Z1-W#xX<=Wd@BBEaXAznQz_<9XkdtC0+CFcn7f|%=KCxO5iH-xyWjl5 zz0iJt^$XYLYcWhPF_bCiaOgQbuZr2Bih0%F1grREuYW9McpB};z{v81Z4b~5|Nk>H YTvV-`eDAP>9gxf5>FVdQ&MBb@0Q3h^A^-pY diff --git a/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/icon.png index fee14e7a8cd54b8eb6f24241f36bc1f00dad78cf..f3d81549e3d91dc7e42d2ee67330e0a7566ebec5 100644 GIT binary patch delta 98 zcmV-o0GJ5=02lFA*c0^Lt^BVlBP9JE_Ie% xI3l*>Y0#3(tbJd0=ub)zPGasqq-!q_!u~@|+WmgX7jdA`44$rjF6*2UngBnuI8Xop diff --git a/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/blue.rsi/light-icon.png index 3a850ef0d89953c939f371809f8b31c012e41ea9..f385d0187d3475fbbaa1244ba2ba89f9f7a7b2ab 100644 GIT binary patch delta 176 zcmV;h08jsb0?YxBB!2;OQb$4nuFf3k0001nNkl60x&w?%HUJuEjTQbo6co zvf7vKRX8F0Z1-W#xX<=Wd@BBEaXAznQz_<9XkdtC0+CFcn7f|%=KCxO5iH-xyWjl5 zz0iJt^$XYLYcWhPF_bCiaOgQbuZr2Bih0%F1grREuYW9McpB};z{v81Z4b~5|Nk>H YTvV-`eDAP>9gxf5>FVdQ&MBb@0QJvOBme*a diff --git a/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/icon.png index 3d3aa75958dcf9f5e581b13ba9b5e6ba5b8a3554..2728bf501bddd75eede1b8e20096a8d394ae189d 100644 GIT binary patch delta 98 zcmV-o0GDmi~u>Vk#cE4ZpMI2}}gQu&X%Q~loCIGw4HxmE= diff --git a/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/dark_yellow.rsi/light-icon.png index 3a850ef0d89953c939f371809f8b31c012e41ea9..f385d0187d3475fbbaa1244ba2ba89f9f7a7b2ab 100644 GIT binary patch delta 176 zcmV;h08jsb0?YxBB!2;OQb$4nuFf3k0001nNkl60x&w?%HUJuEjTQbo6co zvf7vKRX8F0Z1-W#xX<=Wd@BBEaXAznQz_<9XkdtC0+CFcn7f|%=KCxO5iH-xyWjl5 zz0iJt^$XYLYcWhPF_bCiaOgQbuZr2Bih0%F1grREuYW9McpB};z{v81Z4b~5|Nk>H YTvV-`eDAP>9gxf5>FVdQ&MBb@0QJvOBme*a diff --git a/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/icon.png index ba88c23f723c7f37cf76336606234cd3f7fbf881..b4bbfa0e09e99dfd09969b8553c849e0cf91a31b 100644 GIT binary patch delta 97 zcmeywcz|(&;Y3G^a3@a}#}JF&w`UB67!){|4@wyR`+q#?rN*tG8IvOU{uzmvv4FO#s`A BA=Ur@ delta 145 zcmV;C0B--l0rCNmF%1M~K}|sb0I`n?{9y%=E<1S%a7bBm000id000id0mpBsWB>pF zPf0{UR5(xVjKL0oAPhsxgzo?U*pYFt3h`)6c$cNiYvC_!A=yImf$%8k1I5ZskqEQH ztd5ji1vJJ2o0i7yRH8$f5FsWQ1507b4+QrU09+eR<{agB00000NkvXXu0mjf)|oi) diff --git a/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/orange.rsi/light-icon.png index 316cf7a9bccd9d0bf418f16fc955c0afc93e7888..f385d0187d3475fbbaa1244ba2ba89f9f7a7b2ab 100644 GIT binary patch delta 163 zcmV;U09^m`0n7oAF@J_hL_t(oh3(d%4Z|=H1<-$|qOK7Z+ATUtMky(YN`gbPMdv6i zj}$4g)%)Gy0vzzaWa#tLCubvxmhq;q3m||10tj#hz$JY%ZVp@xh=9M-8!g>H#L)fX zwUYNfI|BefGIeVC?xm2m&Y$cIlKBSA9#6Mvb6dtE1(-f00vre+Kx=@aD1Vswd55Cn R3Sj^M002ovPDHLkV1g#GMt}eS delta 202 zcmX@Z_?dBnVLeN_qpu?a!^VE@KZ&di3=EtF9+AZi4BWyX%*Zfnjs#G!!_&nvB*H!U z&;S4ShaH&IG;9+Rt}rgh;8jB)dyiGZ0a=H-$#3QxUicW(<8aKLZQ0_K z?XOSpHP-*%H0A%l+23CNuh-XIAyCO6!t~_9{r&U)|M=?s`v3p?`={SKuI8_!X3ZiY zx%<~liS+u^q!@EnEf+C~+-dz*5UUc{4Gb6FVdQ&MBb@02<9s AivR!s diff --git a/Resources/Textures/Clothing/Head/Hardhats/red.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/red.rsi/icon.png index 011962037d6b4ab8f8612210bb9a50553287e788..5a8fb92ef916f063c6df7be23c9d514812f9adee 100644 GIT binary patch delta 97 zcmeywcz|(&;Y3G^a3@a}#}JF&w`UB67!){|4@wyR`+q#?rN*tG8IvOU{uzmvv4FO#s`A BA=Ur@ delta 145 zcmV;C0B--l0rCNmF%1M~K}|sb0I`n?{9y%=E<1S%a7bBm000id000id0mpBsWB>pF zPf0{UR5(xVjKL0oAPhsxgzo?U*pYFt3h`)6c$cNiYvC_!A=yImf$%8k1I5ZskqEQH ztd5ji1vJJ2o0i7yRH8$f5FsWQ1507b4+QrU09+eR<{agB00000NkvXXu0mjf)|oi) diff --git a/Resources/Textures/Clothing/Head/Hardhats/red.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/red.rsi/light-icon.png index 3a850ef0d89953c939f371809f8b31c012e41ea9..f385d0187d3475fbbaa1244ba2ba89f9f7a7b2ab 100644 GIT binary patch delta 176 zcmV;h08jsb0?YxBB!2;OQb$4nuFf3k0001nNkl60x&w?%HUJuEjTQbo6co zvf7vKRX8F0Z1-W#xX<=Wd@BBEaXAznQz_<9XkdtC0+CFcn7f|%=KCxO5iH-xyWjl5 zz0iJt^$XYLYcWhPF_bCiaOgQbuZr2Bih0%F1grREuYW9McpB};z{v81Z4b~5|Nk>H YTvV-`eDAP>9gxf5>FVdQ&MBb@0QJvOBme*a diff --git a/Resources/Textures/Clothing/Head/Hardhats/white.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/white.rsi/icon.png index 2a5093d619ea4aa930175f15f39eb8e2cdea69f4..06af0dcd0aec9f5523f4f8eec947871f33b33df7 100644 GIT binary patch delta 98 zcmV-o0GJ5=02lFA*c0^Lt^BVlBP9JE_Ie% xI3l*>Y0#3(tbJd0=ub)zPGasqq-!q_!u~@|+WmgX7jdA`44$rjF6*2UngBnuI8Xop diff --git a/Resources/Textures/Clothing/Head/Hardhats/white.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/white.rsi/light-icon.png index 316cf7a9bccd9d0bf418f16fc955c0afc93e7888..f385d0187d3475fbbaa1244ba2ba89f9f7a7b2ab 100644 GIT binary patch delta 163 zcmV;U09^m`0n7oAF@J_hL_t(oh3(d%4Z|=H1<-$|qOK7Z+ATUtMky(YN`gbPMdv6i zj}$4g)%)Gy0vzzaWa#tLCubvxmhq;q3m||10tj#hz$JY%ZVp@xh=9M-8!g>H#L)fX zwUYNfI|BefGIeVC?xm2m&Y$cIlKBSA9#6Mvb6dtE1(-f00vre+Kx=@aD1Vswd55Cn R3Sj^M002ovPDHLkV1g#GMt}eS delta 202 zcmX@Z_?dBnVLeN_qpu?a!^VE@KZ&di3=EtF9+AZi4BWyX%*Zfnjs#G!!_&nvB*H!U z&;S4ShaH&IG;9+Rt}rgh;8jB)dyiGZ0a=H-$#3QxUicW(<8aKLZQ0_K z?XOSpHP-*%H0A%l+23CNuh-XIAyCO6!t~_9{r&U)|M=?s`v3p?`={SKuI8_!X3ZiY zx%<~liS+u^q!@EnEf+C~+-dz*5UUc{4Gb6FVdQ&MBb@02<9s AivR!s diff --git a/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/icon.png b/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/icon.png index 1a3f49cc1b30f6b656b560d44aefbb4d3ecb4300..0978e7886c605a7a31808c54545265e113cd38e6 100644 GIT binary patch delta 97 zcmeywcz|(&;Y3G^a3@a}#}JF&w`UB67!){|4@wyR`+q#?rN*tG8IvOU{uzmvv4FO#s`A BA=Ur@ delta 145 zcmV;C0B--l0rCNmF%1M~K}|sb0I`n?{9y%=E<1S%a7bBm000id000id0mpBsWB>pF zPf0{UR5(xVjKL0oAPhsxgzo?U*pYFt3h`)6c$cNiYvC_!A=yImf$%8k1I5ZskqEQH ztd5ji1vJJ2o0i7yRH8$f5FsWQ1507b4+QrU09+eR<{agB00000NkvXXu0mjf)|oi) diff --git a/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/light-icon.png b/Resources/Textures/Clothing/Head/Hardhats/yellow.rsi/light-icon.png index 3a850ef0d89953c939f371809f8b31c012e41ea9..f385d0187d3475fbbaa1244ba2ba89f9f7a7b2ab 100644 GIT binary patch delta 176 zcmV;h08jsb0?YxBB!2;OQb$4nuFf3k0001nNkl60x&w?%HUJuEjTQbo6co zvf7vKRX8F0Z1-W#xX<=Wd@BBEaXAznQz_<9XkdtC0+CFcn7f|%=KCxO5iH-xyWjl5 zz0iJt^$XYLYcWhPF_bCiaOgQbuZr2Bih0%F1grREuYW9McpB};z{v81Z4b~5|Nk>H YTvV-`eDAP>9gxf5>FVdQ&MBb@0QJvOBme*a From a1000587cfc0293e045bff29835719e8c4767e4b Mon Sep 17 00:00:00 2001 From: PJBot Date: Fri, 12 Jul 2024 15:01:46 +0000 Subject: [PATCH 37/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index e51638b71e9..1dfa17c2b06 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,12 +1,4 @@ Entries: -- author: SpeltIncorrectyl - changes: - - message: Security Officers, Head of Security, and Warden can now choose to start - with a security webbing instead of a belt in their loadout menu. - type: Add - id: 6411 - time: '2024-04-21T06:19:20.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27189 - author: ElectroJr changes: - message: Fixed the nukie station not getting properly initialized. @@ -3824,3 +3816,10 @@ id: 6910 time: '2024-07-12T11:38:59.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29917 +- author: jonathanargo + changes: + - message: Hard hat icon sprites are now centered correctly. + type: Fix + id: 6911 + time: '2024-07-12T15:00:40.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29953 From 5fa0844f3ca073f0641c54aa6093b328fee10307 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Fri, 12 Jul 2024 20:00:34 -0700 Subject: [PATCH 38/78] Update UtensilSystem.cs to not use Component.Owner (#29971) Update UtensilSystem.cs Co-authored-by: plykiya --- .../Nutrition/EntitySystems/UtensilSystem.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs index 43087214a45..d40288183fc 100644 --- a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs @@ -31,24 +31,24 @@ public override void Initialize() /// /// Clicked with utensil /// - private void OnAfterInteract(EntityUid uid, UtensilComponent component, AfterInteractEvent ev) + private void OnAfterInteract(Entity entity, ref AfterInteractEvent ev) { if (ev.Handled || ev.Target == null || !ev.CanReach) return; - var result = TryUseUtensil(ev.User, ev.Target.Value, component); + var result = TryUseUtensil(ev.User, ev.Target.Value, entity); ev.Handled = result.Handled; } - public (bool Success, bool Handled) TryUseUtensil(EntityUid user, EntityUid target, UtensilComponent component) + public (bool Success, bool Handled) TryUseUtensil(EntityUid user, EntityUid target, Entity utensil) { if (!EntityManager.TryGetComponent(target, out FoodComponent? food)) return (false, true); //Prevents food usage with a wrong utensil - if ((food.Utensil & component.Types) == 0) + if ((food.Utensil & utensil.Comp.Types) == 0) { - _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", target), ("utensil", component.Owner)), user, user); + _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", target), ("utensil", utensil.Owner)), user, user); return (false, true); } From 8ed9ea1586563a3aed9ab4a4636ac03616e7eb7f Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Fri, 12 Jul 2024 20:12:46 -0700 Subject: [PATCH 39/78] Update TriggerSystem.TimedCollide.cs to not use Component.Owner (#29970) Update TriggerSystem.TimedCollide.cs Co-authored-by: plykiya --- .../Explosion/EntitySystems/TriggerSystem.TimedCollide.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.TimedCollide.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.TimedCollide.cs index 29b81deb32c..ea10b7c69b3 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.TimedCollide.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.TimedCollide.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Content.Server.Explosion.Components; using Content.Server.Explosion.EntitySystems; using Robust.Shared.Physics.Dynamics; @@ -42,14 +42,15 @@ private void OnComponentRemove(EntityUid uid, TriggerOnTimedCollideComponent com private void UpdateTimedCollide(float frameTime) { - foreach (var (activeTrigger, triggerOnTimedCollide) in EntityQuery()) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out _, out var triggerOnTimedCollide)) { foreach (var (collidingEntity, collidingTimer) in triggerOnTimedCollide.Colliding) { triggerOnTimedCollide.Colliding[collidingEntity] += frameTime; if (collidingTimer > triggerOnTimedCollide.Threshold) { - RaiseLocalEvent(activeTrigger.Owner, new TriggerEvent(activeTrigger.Owner, collidingEntity), true); + RaiseLocalEvent(uid, new TriggerEvent(uid, collidingEntity), true); triggerOnTimedCollide.Colliding[collidingEntity] -= triggerOnTimedCollide.Threshold; } } From 162348e02d958abddcd06f452262e923e0fab1d7 Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Fri, 12 Jul 2024 20:43:07 -0700 Subject: [PATCH 40/78] Change whitelist to pass when null (#29981) --- Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs index cfa082a9eeb..eb20c65a116 100644 --- a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs @@ -95,7 +95,8 @@ private bool TryGetLayers(EntityUid uid, var list = new List(); foreach (var mapLayerData in itemMapper.MapLayers.Values) { - var count = containedLayers.Count(ent => _whitelistSystem.IsWhitelistPass(mapLayerData.Whitelist, ent)); + var count = containedLayers.Count(ent => _whitelistSystem.IsWhitelistPassOrNull(mapLayerData.Whitelist, + ent)); if (count >= mapLayerData.MinCount && count <= mapLayerData.MaxCount) { list.Add(mapLayerData.Layer); From 55a50d04266916ac130d4c2b6729be51f3190af7 Mon Sep 17 00:00:00 2001 From: lzk <124214523+lzk228@users.noreply.github.com> Date: Sat, 13 Jul 2024 06:03:16 +0200 Subject: [PATCH 41/78] Use reinforced glass damage modifier for secure windoors (#29941) * Use Rglass damage modifier for reinforced windoors * i actually can remove this line since inherited --- .../Structures/Doors/Windoors/base_structurewindoors.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml index 1ec6c04ffe7..d15294cc467 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Windoors/base_structurewindoors.yml @@ -172,6 +172,8 @@ - state: panel_open map: [ "enum.WiresVisualLayers.MaintenancePanel" ] visible: false + - type: Damageable + damageModifierSet: RGlass - type: Destructible thresholds: - trigger: @@ -257,7 +259,7 @@ - type: entity id: BaseSecurePlasmaWindoor - parent: BaseWindoor + parent: BaseSecureWindoor abstract: true components: - type: Sprite @@ -367,7 +369,7 @@ - type: entity id: BaseSecureUraniumWindoor - parent: BaseWindoor + parent: BaseSecureWindoor abstract: true components: - type: Sprite From 1b360ac914edffd22d31e4a87c55a45419148517 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 13 Jul 2024 04:04:22 +0000 Subject: [PATCH 42/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 1dfa17c2b06..4e0604242e3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: ElectroJr - changes: - - message: Fixed the nukie station not getting properly initialized. - type: Fix - id: 6412 - time: '2024-04-21T06:52:45.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27201 - author: Blackern5000 changes: - message: '"Experienced" passengers can now wear a rainbow jumpsuit using loadouts.' @@ -3823,3 +3816,10 @@ id: 6911 time: '2024-07-12T15:00:40.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29953 +- author: lzk228 + changes: + - message: Secure windoors now use reinforced glass damage modifier. + type: Fix + id: 6912 + time: '2024-07-13T04:03:16.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29941 From b94b52396e52e7627d80495f019a89a51751c6a8 Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Fri, 12 Jul 2024 21:04:53 -0700 Subject: [PATCH 43/78] Add doc comments to target action components (#29982) --- Content.Shared/Actions/EntityTargetActionComponent.cs | 10 ++++++++++ Content.Shared/Actions/WorldTargetActionComponent.cs | 3 +++ 2 files changed, 13 insertions(+) diff --git a/Content.Shared/Actions/EntityTargetActionComponent.cs b/Content.Shared/Actions/EntityTargetActionComponent.cs index 9024f42e0e7..7217794b23b 100644 --- a/Content.Shared/Actions/EntityTargetActionComponent.cs +++ b/Content.Shared/Actions/EntityTargetActionComponent.cs @@ -4,6 +4,9 @@ namespace Content.Shared.Actions; +/// +/// Used on action entities to define an action that triggers when targeting an entity. +/// [RegisterComponent, NetworkedComponent] public sealed partial class EntityTargetActionComponent : BaseTargetActionComponent { @@ -16,8 +19,15 @@ public sealed partial class EntityTargetActionComponent : BaseTargetActionCompon [NonSerialized] public EntityTargetActionEvent? Event; + /// + /// Determines which entities are valid targets for this action. + /// + /// No whitelist check when null. [DataField("whitelist")] public EntityWhitelist? Whitelist; + /// + /// Whether this action considers the user as a valid target entity when using this action. + /// [DataField("canTargetSelf")] public bool CanTargetSelf = true; } diff --git a/Content.Shared/Actions/WorldTargetActionComponent.cs b/Content.Shared/Actions/WorldTargetActionComponent.cs index 4974b4478db..8066a640349 100644 --- a/Content.Shared/Actions/WorldTargetActionComponent.cs +++ b/Content.Shared/Actions/WorldTargetActionComponent.cs @@ -3,6 +3,9 @@ namespace Content.Shared.Actions; +/// +/// Used on action entities to define an action that triggers when targeting an entity coordinate. +/// [RegisterComponent, NetworkedComponent] public sealed partial class WorldTargetActionComponent : BaseTargetActionComponent { From 3acc100ae54d56c31aad15a879728c3b67e1e49c Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Fri, 12 Jul 2024 21:05:31 -0700 Subject: [PATCH 44/78] Update ShuttleDockControl.xaml.cs to not use Component.Owner (#29966) Update ShuttleDockControl.xaml.cs Co-authored-by: plykiya --- Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs index 31f0eecad79..61ae0699266 100644 --- a/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs +++ b/Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs @@ -107,7 +107,7 @@ protected override void Draw(DrawingHandleScreen handle) DrawCircles(handle); var gridNent = EntManager.GetNetEntity(GridEntity); var mapPos = _xformSystem.ToMapCoordinates(_coordinates.Value); - var ourGridMatrix = _xformSystem.GetWorldMatrix(gridXform.Owner); + var ourGridMatrix = _xformSystem.GetWorldMatrix(GridEntity.Value); var dockMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, Angle.Zero); var worldFromDock = Matrix3x2.Multiply(dockMatrix, ourGridMatrix); From e11f1e6cf64bda214f17e95e3eb1c58f46d4675b Mon Sep 17 00:00:00 2001 From: Winkarst <74284083+Winkarst-cpu@users.noreply.github.com> Date: Sat, 13 Jul 2024 07:05:51 +0300 Subject: [PATCH 45/78] Update ActionAlertTooltip.cs to use TryFromMarkup (#29975) --- .../Actions/UI/ActionAlertTooltip.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Content.Client/Actions/UI/ActionAlertTooltip.cs b/Content.Client/Actions/UI/ActionAlertTooltip.cs index ddc498b6e91..f805f6643d2 100644 --- a/Content.Client/Actions/UI/ActionAlertTooltip.cs +++ b/Content.Client/Actions/UI/ActionAlertTooltip.cs @@ -1,4 +1,4 @@ -using Content.Client.Stylesheets; +using Content.Client.Stylesheets; using Robust.Client.UserInterface.Controls; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -77,9 +77,12 @@ public ActionAlertTooltip(FormattedMessage name, FormattedMessage? desc, string? MaxWidth = TooltipTextMaxWidth, StyleClasses = {StyleNano.StyleClassTooltipActionRequirements} }; - requiresLabel.SetMessage(FormattedMessage.FromMarkup("[color=#635c5c]" + - requires + - "[/color]")); + + if (!FormattedMessage.TryFromMarkup("[color=#635c5c]" + requires + "[/color]", out var markup)) + return; + + requiresLabel.SetMessage(markup); + vbox.AddChild(requiresLabel); } } @@ -97,8 +100,11 @@ protected override void FrameUpdate(FrameEventArgs args) if (timeLeft > TimeSpan.Zero) { var duration = Cooldown.Value.End - Cooldown.Value.Start; - _cooldownLabel.SetMessage(FormattedMessage.FromMarkup( - $"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]")); + + if (!FormattedMessage.TryFromMarkup($"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]", out var markup)) + return; + + _cooldownLabel.SetMessage(markup); _cooldownLabel.Visible = true; } else From de2ab29f349871797182d105918d3e9c013661e5 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Fri, 12 Jul 2024 21:06:54 -0700 Subject: [PATCH 46/78] Update SmokingSystem.SmokingPipe.cs to not use Component.Owner (#29967) Update SmokingSystem.SmokingPipe.cs Co-authored-by: plykiya --- .../EntitySystems/SmokingSystem.SmokingPipe.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs index 3950c73eb43..26d86acd545 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.SmokingPipe.cs @@ -42,7 +42,7 @@ private void OnPipeInteractUsingEvent(Entity entity, ref I if (!isHotEvent.IsHot) return; - if (TryTransferReagents(entity.Comp, smokable)) + if (TryTransferReagents(entity, (entity.Owner, smokable))) SetSmokableState(entity, SmokableState.Lit, smokable); args.Handled = true; } @@ -62,7 +62,7 @@ public void OnPipeAfterInteract(Entity entity, ref AfterIn if (!isHotEvent.IsHot) return; - if (TryTransferReagents(entity.Comp, smokable)) + if (TryTransferReagents(entity, (entity.Owner, smokable))) SetSmokableState(entity, SmokableState.Lit, smokable); args.Handled = true; } @@ -74,15 +74,15 @@ private void OnPipeSolutionEmptyEvent(Entity entity, ref S } // Convert smokable item into reagents to be smoked - private bool TryTransferReagents(SmokingPipeComponent component, SmokableComponent smokable) + private bool TryTransferReagents(Entity entity, Entity smokable) { - if (component.BowlSlot.Item == null) + if (entity.Comp.BowlSlot.Item == null) return false; - EntityUid contents = component.BowlSlot.Item.Value; + EntityUid contents = entity.Comp.BowlSlot.Item.Value; if (!TryComp(contents, out var reagents) || - !_solutionContainerSystem.TryGetSolution(smokable.Owner, smokable.Solution, out var pipeSolution, out _)) + !_solutionContainerSystem.TryGetSolution(smokable.Owner, smokable.Comp.Solution, out var pipeSolution, out _)) return false; foreach (var (_, soln) in _solutionContainerSystem.EnumerateSolutions((contents, reagents))) @@ -93,7 +93,7 @@ private bool TryTransferReagents(SmokingPipeComponent component, SmokableCompone EntityManager.DeleteEntity(contents); - _itemSlotsSystem.SetLock(component.Owner, component.BowlSlot, true); //no inserting more until current runs out + _itemSlotsSystem.SetLock(entity.Owner, entity.Comp.BowlSlot, true); //no inserting more until current runs out return true; } From 3388c0dcaa4bc1d459ce01bc9b1c1296011018a4 Mon Sep 17 00:00:00 2001 From: Tayrtahn Date: Sat, 13 Jul 2024 00:14:30 -0400 Subject: [PATCH 47/78] Fix antag objectives always overshooting MaxDifficulty (and kill tries20) (#29830) * The death of try20 * Add integration test for traitor gamerule * Fix max difficulty being overshot * Check at least one objective is assigned * EntProtoId --- .../Tests/GameRules/TraitorRuleTest.cs | 133 ++++++++++++++++++ .../Antag/AntagRandomObjectivesSystem.cs | 3 +- Content.Server/Objectives/ObjectivesSystem.cs | 28 ++-- .../Systems/SharedObjectivesSystem.cs | 17 ++- .../Random/Helpers/SharedRandomExtensions.cs | 21 +++ 5 files changed, 184 insertions(+), 18 deletions(-) create mode 100644 Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs diff --git a/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs b/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs new file mode 100644 index 00000000000..31d33ba6174 --- /dev/null +++ b/Content.IntegrationTests/Tests/GameRules/TraitorRuleTest.cs @@ -0,0 +1,133 @@ +using System.Linq; +using Content.Server.Antag.Components; +using Content.Server.GameTicking; +using Content.Server.GameTicking.Rules; +using Content.Server.GameTicking.Rules.Components; +using Content.Server.Mind; +using Content.Server.Roles; +using Content.Shared.GameTicking; +using Content.Shared.GameTicking.Components; +using Content.Shared.Mind; +using Content.Shared.NPC.Systems; +using Content.Shared.Objectives.Components; +using Robust.Shared.GameObjects; +using Robust.Shared.Prototypes; + +namespace Content.IntegrationTests.Tests.GameRules; + +[TestFixture] +public sealed class TraitorRuleTest +{ + private const string TraitorGameRuleProtoId = "Traitor"; + private const string TraitorAntagRoleName = "Traitor"; + + [Test] + public async Task TestTraitorObjectives() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings() + { + Dirty = true, + DummyTicker = false, + Connected = true, + InLobby = true, + }); + var server = pair.Server; + var client = pair.Client; + var entMan = server.EntMan; + var protoMan = server.ProtoMan; + var compFact = server.ResolveDependency(); + var ticker = server.System(); + var mindSys = server.System(); + var roleSys = server.System(); + var factionSys = server.System(); + var traitorRuleSys = server.System(); + + // Look up the minimum player count and max total objective difficulty for the game rule + var minPlayers = 1; + var maxDifficulty = 0f; + await server.WaitAssertion(() => + { + Assert.That(protoMan.TryIndex(TraitorGameRuleProtoId, out var gameRuleEnt), + $"Failed to lookup traitor game rule entity prototype with ID \"{TraitorGameRuleProtoId}\"!"); + + Assert.That(gameRuleEnt.TryGetComponent(out var gameRule, compFact), + $"Game rule entity {TraitorGameRuleProtoId} does not have a GameRuleComponent!"); + + Assert.That(gameRuleEnt.TryGetComponent(out var randomObjectives, compFact), + $"Game rule entity {TraitorGameRuleProtoId} does not have an AntagRandomObjectivesComponent!"); + + minPlayers = gameRule.MinPlayers; + maxDifficulty = randomObjectives.MaxDifficulty; + }); + + // Initially in the lobby + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.PreRoundLobby)); + Assert.That(client.AttachedEntity, Is.Null); + Assert.That(ticker.PlayerGameStatuses[client.User!.Value], Is.EqualTo(PlayerGameStatus.NotReadyToPlay)); + + // Add enough dummy players for the game rule + var dummies = await pair.Server.AddDummySessions(minPlayers); + await pair.RunTicksSync(5); + + // Initially, the players have no attached entities + Assert.That(pair.Player?.AttachedEntity, Is.Null); + Assert.That(dummies.All(x => x.AttachedEntity == null)); + + // Opt-in the player for the traitor role + await pair.SetAntagPreference(TraitorAntagRoleName, true); + + // Add the game rule + var gameRuleEnt = ticker.AddGameRule(TraitorGameRuleProtoId); + Assert.That(entMan.TryGetComponent(gameRuleEnt, out var traitorRule)); + + // Ready up + ticker.ToggleReadyAll(true); + Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.ReadyToPlay)); + + // Start the round + await server.WaitPost(() => + { + ticker.StartRound(); + // Force traitor mode to start (skip the delay) + ticker.StartGameRule(gameRuleEnt); + }); + await pair.RunTicksSync(10); + + // Game should have started + Assert.That(ticker.RunLevel, Is.EqualTo(GameRunLevel.InRound)); + Assert.That(ticker.PlayerGameStatuses.Values.All(x => x == PlayerGameStatus.JoinedGame)); + Assert.That(client.EntMan.EntityExists(client.AttachedEntity)); + + // Check the player and dummies are spawned + var dummyEnts = dummies.Select(x => x.AttachedEntity ?? default).ToArray(); + var player = pair.Player!.AttachedEntity!.Value; + Assert.That(entMan.EntityExists(player)); + Assert.That(dummyEnts.All(entMan.EntityExists)); + + // Make sure the player is a traitor. + var mind = mindSys.GetMind(player)!.Value; + Assert.That(roleSys.MindIsAntagonist(mind)); + Assert.That(factionSys.IsMember(player, "Syndicate"), Is.True); + Assert.That(factionSys.IsMember(player, "NanoTrasen"), Is.False); + Assert.That(traitorRule.TotalTraitors, Is.EqualTo(1)); + Assert.That(traitorRule.TraitorMinds[0], Is.EqualTo(mind)); + + // Check total objective difficulty + Assert.That(entMan.TryGetComponent(mind, out var mindComp)); + var totalDifficulty = mindComp.Objectives.Sum(o => entMan.GetComponent(o).Difficulty); + Assert.That(totalDifficulty, Is.AtMost(maxDifficulty), + $"MaxDifficulty exceeded! Objectives: {string.Join(", ", mindComp.Objectives.Select(o => FormatObjective(o, entMan)))}"); + Assert.That(mindComp.Objectives, Is.Not.Empty, + $"No objectives assigned!"); + + + await pair.CleanReturnAsync(); + } + + private static string FormatObjective(Entity entity, IEntityManager entMan) + { + var meta = entMan.GetComponent(entity); + var objective = entMan.GetComponent(entity); + return $"{meta.EntityName} ({objective.Difficulty})"; + } +} diff --git a/Content.Server/Antag/AntagRandomObjectivesSystem.cs b/Content.Server/Antag/AntagRandomObjectivesSystem.cs index c935b8c0648..b60759a3d5c 100644 --- a/Content.Server/Antag/AntagRandomObjectivesSystem.cs +++ b/Content.Server/Antag/AntagRandomObjectivesSystem.cs @@ -39,7 +39,8 @@ private void OnAntagSelected(Entity ent, ref Aft for (var pick = 0; pick < set.MaxPicks && ent.Comp.MaxDifficulty > difficulty; pick++) { - if (_objectives.GetRandomObjective(mindId, mind, set.Groups) is not {} objective) + var remainingDifficulty = ent.Comp.MaxDifficulty - difficulty; + if (_objectives.GetRandomObjective(mindId, mind, set.Groups, remainingDifficulty) is not { } objective) continue; _mind.AddObjective(mindId, mind, objective); diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index 18077b413ad..c9cdf244e66 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Text; using Robust.Server.Player; +using Robust.Shared.Utility; namespace Content.Server.Objectives; @@ -180,33 +181,32 @@ private void AddSummary(StringBuilder result, string agent, List<(EntityUid, str } } - public EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, string objectiveGroupProto) + public EntityUid? GetRandomObjective(EntityUid mindId, MindComponent mind, ProtoId objectiveGroupProto, float maxDifficulty) { - if (!_prototypeManager.TryIndex(objectiveGroupProto, out var groups)) + if (!_prototypeManager.TryIndex(objectiveGroupProto, out var groupsProto)) { Log.Error($"Tried to get a random objective, but can't index WeightedRandomPrototype {objectiveGroupProto}"); return null; } - // TODO replace whatever the fuck this is with a proper objective selection system - // yeah the old 'preventing infinite loops' thing wasn't super elegant either and it mislead people on what exactly it did - var tries = 0; - while (tries < 20) - { - var groupName = groups.Pick(_random); + // Make a copy of the weights so we don't trash the prototype by removing entries + var groups = groupsProto.Weights.ShallowClone(); + while (_random.TryPickAndTake(groups, out var groupName)) + { if (!_prototypeManager.TryIndex(groupName, out var group)) { Log.Error($"Couldn't index objective group prototype {groupName}"); return null; } - var proto = group.Pick(_random); - var objective = TryCreateObjective(mindId, mind, proto); - if (objective != null) - return objective; - - tries++; + var objectives = group.Weights.ShallowClone(); + while (_random.TryPickAndTake(objectives, out var objectiveProto)) + { + if (TryCreateObjective((mindId, mind), objectiveProto, out var objective) + && Comp(objective.Value).Difficulty <= maxDifficulty) + return objective; + } } return null; diff --git a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs index 8d2c4dcfebe..35fa501398f 100644 --- a/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs +++ b/Content.Shared/Objectives/Systems/SharedObjectivesSystem.cs @@ -1,5 +1,5 @@ +using System.Diagnostics.CodeAnalysis; using Content.Shared.Mind; -using Content.Shared.Objectives; using Content.Shared.Objectives.Components; using Robust.Shared.Prototypes; using Robust.Shared.Utility; @@ -40,7 +40,7 @@ public bool CanBeAssigned(EntityUid uid, EntityUid mindId, MindComponent mind, O if (comp.Unique) { var proto = _metaQuery.GetComponent(uid).EntityPrototype?.ID; - foreach (var objective in mind.AllObjectives) + foreach (var objective in mind.Objectives) { if (_metaQuery.GetComponent(objective).EntityPrototype?.ID == proto) return false; @@ -92,7 +92,18 @@ public bool CanBeAssigned(EntityUid uid, EntityUid mindId, MindComponent mind, O } /// - /// Get the title, description, icon and progress of an objective using . + /// Spawns and assigns an objective for a mind. + /// The objective is not added to the mind's objectives, mind system does that in TryAddObjective. + /// If the objective could not be assigned the objective is deleted and false is returned. + /// + public bool TryCreateObjective(Entity mind, EntProtoId proto, [NotNullWhen(true)] out EntityUid? objective) + { + objective = TryCreateObjective(mind.Owner, mind.Comp, proto); + return objective != null; + } + + /// + /// Get the title, description, icon and progress of an objective using . /// If any of them are null it is logged and null is returned. /// /// ID of the condition entity diff --git a/Content.Shared/Random/Helpers/SharedRandomExtensions.cs b/Content.Shared/Random/Helpers/SharedRandomExtensions.cs index 0b618a262db..376e91743d0 100644 --- a/Content.Shared/Random/Helpers/SharedRandomExtensions.cs +++ b/Content.Shared/Random/Helpers/SharedRandomExtensions.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Shared.Dataset; using Content.Shared.FixedPoint; @@ -87,6 +88,26 @@ public static T Pick(this IRobustRandom random, Dictionary weights) throw new InvalidOperationException("Invalid weighted pick"); } + public static T PickAndTake(this IRobustRandom random, Dictionary weights) + where T : notnull + { + var pick = Pick(random, weights); + weights.Remove(pick); + return pick; + } + + public static bool TryPickAndTake(this IRobustRandom random, Dictionary weights, [NotNullWhen(true)] out T? pick) + where T : notnull + { + if (weights.Count == 0) + { + pick = default; + return false; + } + pick = PickAndTake(random, weights); + return true; + } + public static (string reagent, FixedPoint2 quantity) Pick(this WeightedRandomFillSolutionPrototype prototype, IRobustRandom? random = null) { var randomFill = prototype.PickRandomFill(random); From 87fa6075b64ba097d99cbb04421a0f7dfb2662b2 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 13 Jul 2024 04:15:36 +0000 Subject: [PATCH 48/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 4e0604242e3..eb495bc316d 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: Blackern5000 - changes: - - message: '"Experienced" passengers can now wear a rainbow jumpsuit using loadouts.' - type: Add - id: 6413 - time: '2024-04-21T11:53:08.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27211 - author: PJB3005 changes: - message: The item status menu has been moved to the side of the hands. There is @@ -3823,3 +3816,10 @@ id: 6912 time: '2024-07-13T04:03:16.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29941 +- author: Tayrtahn + changes: + - message: Antag objective total difficulty is now properly capped. + type: Fix + id: 6913 + time: '2024-07-13T04:14:30.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29830 From 7b590122b652096528ec69921924b3db22b27f6a Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Sat, 13 Jul 2024 04:17:10 +0000 Subject: [PATCH 49/78] fire extinguisher using item toggle (#29906) * move SprayAttemptEvent to shared * add SolutionTransferredEvent * replace FireExtinguisher with SpraySafety * update fire extinguisher yml * invert visuals * always handle event in solution transfer, it makes popups * instantly fill it * untroll --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Extinguisher/FireExtinguisherComponent.cs | 7 - .../Extinguisher/FireExtinguisherComponent.cs | 10 -- .../Extinguisher/FireExtinguisherSystem.cs | 139 ------------------ .../Fluids/EntitySystems/SpraySystem.cs | 16 +- .../EntitySystems/SolutionTransferSystem.cs | 16 +- .../SharedFireExtinguisherComponent.cs | 25 ---- .../Fluids/Components/SpraySafetyComponent.cs | 24 +++ Content.Shared/Fluids/Events.cs | 12 ++ Content.Shared/Fluids/SpraySafetySystem.cs | 44 ++++++ .../Objects/Misc/fire_extinguisher.yml | 34 +++-- 10 files changed, 118 insertions(+), 209 deletions(-) delete mode 100644 Content.Client/Extinguisher/FireExtinguisherComponent.cs delete mode 100644 Content.Server/Extinguisher/FireExtinguisherComponent.cs delete mode 100644 Content.Server/Extinguisher/FireExtinguisherSystem.cs delete mode 100644 Content.Shared/Extinguisher/SharedFireExtinguisherComponent.cs create mode 100644 Content.Shared/Fluids/Components/SpraySafetyComponent.cs create mode 100644 Content.Shared/Fluids/SpraySafetySystem.cs diff --git a/Content.Client/Extinguisher/FireExtinguisherComponent.cs b/Content.Client/Extinguisher/FireExtinguisherComponent.cs deleted file mode 100644 index 324b05a93d4..00000000000 --- a/Content.Client/Extinguisher/FireExtinguisherComponent.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Content.Shared.Extinguisher; -using Robust.Shared.GameStates; - -namespace Content.Client.Extinguisher; - -[RegisterComponent] -public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent; diff --git a/Content.Server/Extinguisher/FireExtinguisherComponent.cs b/Content.Server/Extinguisher/FireExtinguisherComponent.cs deleted file mode 100644 index 991fc76c62e..00000000000 --- a/Content.Server/Extinguisher/FireExtinguisherComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Content.Shared.Extinguisher; -using Robust.Shared.GameStates; - -namespace Content.Server.Extinguisher; - -[RegisterComponent] -[Access(typeof(FireExtinguisherSystem))] -public sealed partial class FireExtinguisherComponent : SharedFireExtinguisherComponent -{ -} diff --git a/Content.Server/Extinguisher/FireExtinguisherSystem.cs b/Content.Server/Extinguisher/FireExtinguisherSystem.cs deleted file mode 100644 index b33a1af157f..00000000000 --- a/Content.Server/Extinguisher/FireExtinguisherSystem.cs +++ /dev/null @@ -1,139 +0,0 @@ -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Fluids.EntitySystems; -using Content.Server.Popups; -using Content.Shared.Chemistry.Components; -using Content.Shared.Extinguisher; -using Content.Shared.FixedPoint; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Verbs; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; - -namespace Content.Server.Extinguisher; - -public sealed class FireExtinguisherSystem : EntitySystem -{ - [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnFireExtinguisherInit); - SubscribeLocalEvent(OnUseInHand); - SubscribeLocalEvent(OnAfterInteract); - SubscribeLocalEvent>(OnGetInteractionVerbs); - SubscribeLocalEvent(OnSprayAttempt); - } - - private void OnFireExtinguisherInit(Entity entity, ref ComponentInit args) - { - if (entity.Comp.HasSafety) - { - UpdateAppearance((entity.Owner, entity.Comp)); - } - } - - private void OnUseInHand(Entity entity, ref UseInHandEvent args) - { - if (args.Handled) - return; - - ToggleSafety((entity.Owner, entity.Comp), args.User); - - args.Handled = true; - } - - private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) - { - if (args.Target == null || !args.CanReach) - { - return; - } - - if (args.Handled) - return; - - if (entity.Comp.HasSafety && entity.Comp.Safety) - { - _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), entity.Owner, args.User); - return; - } - - if (args.Target is not { Valid: true } target || - !_solutionContainerSystem.TryGetDrainableSolution(target, out var targetSoln, out var targetSolution) || - !_solutionContainerSystem.TryGetRefillableSolution(entity.Owner, out var containerSoln, out var containerSolution)) - { - return; - } - - args.Handled = true; - - // TODO: why is this copy paste shit here just have fire extinguisher cancel transfer when safety is on - var transfer = containerSolution.AvailableVolume; - if (TryComp(entity.Owner, out var solTrans)) - { - transfer = solTrans.TransferAmount; - } - transfer = FixedPoint2.Min(transfer, targetSolution.Volume); - - if (transfer > 0) - { - var drained = _solutionContainerSystem.Drain(target, targetSoln.Value, transfer); - _solutionContainerSystem.TryAddSolution(containerSoln.Value, drained); - - _audio.PlayPvs(entity.Comp.RefillSound, entity.Owner); - _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-after-interact-refilled-message", ("owner", entity.Owner)), - entity.Owner, args.Target.Value); - } - } - - private void OnGetInteractionVerbs(Entity entity, ref GetVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - var user = args.User; - var verb = new InteractionVerb - { - Act = () => ToggleSafety((entity.Owner, entity.Comp), user), - Text = Loc.GetString("fire-extinguisher-component-verb-text"), - }; - - args.Verbs.Add(verb); - } - - private void OnSprayAttempt(Entity entity, ref SprayAttemptEvent args) - { - if (entity.Comp.HasSafety && entity.Comp.Safety) - { - _popupSystem.PopupEntity(Loc.GetString("fire-extinguisher-component-safety-on-message"), entity, args.User); - args.Cancel(); - } - } - - private void UpdateAppearance(Entity entity) - { - if (!Resolve(entity, ref entity.Comp2, false)) - return; - - if (entity.Comp1.HasSafety) - { - _appearance.SetData(entity, FireExtinguisherVisuals.Safety, entity.Comp1.Safety, entity.Comp2); - } - } - - public void ToggleSafety(Entity extinguisher, EntityUid user) - { - if (!Resolve(extinguisher, ref extinguisher.Comp)) - return; - - extinguisher.Comp.Safety = !extinguisher.Comp.Safety; - _audio.PlayPvs(extinguisher.Comp.SafetySound, extinguisher, AudioParams.Default.WithVariation(0.125f).WithVolume(-4f)); - UpdateAppearance((extinguisher.Owner, extinguisher.Comp)); - } -} diff --git a/Content.Server/Fluids/EntitySystems/SpraySystem.cs b/Content.Server/Fluids/EntitySystems/SpraySystem.cs index 5499070738f..215ed7c33ff 100644 --- a/Content.Server/Fluids/EntitySystems/SpraySystem.cs +++ b/Content.Server/Fluids/EntitySystems/SpraySystem.cs @@ -1,11 +1,11 @@ using Content.Server.Chemistry.Components; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Chemistry.EntitySystems; -using Content.Server.Extinguisher; using Content.Server.Fluids.Components; using Content.Server.Gravity; using Content.Server.Popups; using Content.Shared.FixedPoint; +using Content.Shared.Fluids; using Content.Shared.Interaction; using Content.Shared.Timing; using Content.Shared.Vapor; @@ -34,7 +34,7 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(FireExtinguisherSystem) }); + SubscribeLocalEvent(OnAfterInteract); } private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) @@ -48,7 +48,7 @@ private void OnAfterInteract(Entity entity, ref AfterInteractEve return; var ev = new SprayAttemptEvent(args.User); - RaiseLocalEvent(entity, ev); + RaiseLocalEvent(entity, ref ev); if (ev.Cancelled) return; @@ -148,13 +148,3 @@ private void OnAfterInteract(Entity entity, ref AfterInteractEve _useDelay.TryResetDelay((entity, useDelay)); } } - -public sealed class SprayAttemptEvent : CancellableEntityEventArgs -{ - public EntityUid User; - - public SprayAttemptEvent(EntityUid user) - { - User = user; - } -} diff --git a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs index 3b753925088..e5d8cc5f597 100644 --- a/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs +++ b/Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs @@ -117,6 +117,7 @@ private void OnAfterInteract(Entity ent, ref AfterInt transferAmount = FixedPoint2.Min(transferAmount, maxRefill); var transferred = Transfer(args.User, target, targetSoln.Value, uid, ownerSoln.Value, transferAmount); + args.Handled = true; if (transferred > 0) { var toTheBrim = ownerRefill.AvailableVolume == 0; @@ -125,8 +126,6 @@ private void OnAfterInteract(Entity ent, ref AfterInt : "comp-solution-transfer-fill-normal"; _popup.PopupClient(Loc.GetString(msg, ("owner", args.Target), ("amount", transferred), ("target", uid)), uid, args.User); - - args.Handled = true; return; } } @@ -143,13 +142,11 @@ private void OnAfterInteract(Entity ent, ref AfterInt transferAmount = FixedPoint2.Min(transferAmount, maxRefill); var transferred = Transfer(args.User, uid, ownerSoln.Value, target, targetSoln.Value, transferAmount); - + args.Handled = true; if (transferred > 0) { var message = Loc.GetString("comp-solution-transfer-transfer-solution", ("amount", transferred), ("target", target)); _popup.PopupClient(message, uid, args.User); - - args.Handled = true; } } } @@ -202,6 +199,9 @@ public FixedPoint2 Transfer(EntityUid user, var solution = _solution.SplitSolution(source, actualAmount); _solution.AddSolution(target, solution); + var ev = new SolutionTransferredEvent(sourceEntity, targetEntity, user, actualAmount); + RaiseLocalEvent(targetEntity, ref ev); + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(user):player} transferred {SharedSolutionContainerSystem.ToPrettyString(solution)} to {ToPrettyString(targetEntity):target}, which now contains {SharedSolutionContainerSystem.ToPrettyString(targetSolution)}"); @@ -225,3 +225,9 @@ public void Cancel(string reason) CancelReason = reason; } } + +/// +/// Raised on the target entity when a non-zero amount of solution gets transferred. +/// +[ByRefEvent] +public record struct SolutionTransferredEvent(EntityUid From, EntityUid To, EntityUid User, FixedPoint2 Amount); diff --git a/Content.Shared/Extinguisher/SharedFireExtinguisherComponent.cs b/Content.Shared/Extinguisher/SharedFireExtinguisherComponent.cs deleted file mode 100644 index dbb1f6f2c4d..00000000000 --- a/Content.Shared/Extinguisher/SharedFireExtinguisherComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Robust.Shared.Audio; -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared.Extinguisher; - -[NetworkedComponent] -public abstract partial class SharedFireExtinguisherComponent : Component -{ - [DataField("refillSound")] public SoundSpecifier RefillSound = new SoundPathSpecifier("/Audio/Effects/refill.ogg"); - - [DataField("hasSafety")] public bool HasSafety = true; - - [DataField("safety")] public bool Safety = true; - - [DataField("safetySound")] - public SoundSpecifier SafetySound { get; private set; } = new SoundPathSpecifier("/Audio/Machines/button.ogg"); -} - - -[Serializable, NetSerializable] -public enum FireExtinguisherVisuals : byte -{ - Safety -} diff --git a/Content.Shared/Fluids/Components/SpraySafetyComponent.cs b/Content.Shared/Fluids/Components/SpraySafetyComponent.cs new file mode 100644 index 00000000000..30827d4fd1c --- /dev/null +++ b/Content.Shared/Fluids/Components/SpraySafetyComponent.cs @@ -0,0 +1,24 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared.Fluids.Components; + +/// +/// Uses ItemToggle to control safety for a spray item. +/// You can't spray or refill it while safety is on. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SpraySafetySystem))] +public sealed partial class SpraySafetyComponent : Component +{ + /// + /// Popup shown when trying to spray or refill with safety on. + /// + [DataField] + public LocId Popup = "fire-extinguisher-component-safety-on-message"; + + /// + /// Sound to play after refilling. + /// + [DataField] + public SoundSpecifier RefillSound = new SoundPathSpecifier("/Audio/Effects/refill.ogg"); +} diff --git a/Content.Shared/Fluids/Events.cs b/Content.Shared/Fluids/Events.cs index e281de91377..198e8887742 100644 --- a/Content.Shared/Fluids/Events.cs +++ b/Content.Shared/Fluids/Events.cs @@ -34,3 +34,15 @@ public AbsorbantDoAfterEvent(string targetSolution, string message, SoundSpecifi public override DoAfterEvent Clone() => this; } + +/// +/// Raised when trying to spray something, for example a fire extinguisher. +/// +[ByRefEvent] +public record struct SprayAttemptEvent(EntityUid User, bool Cancelled = false) +{ + public void Cancel() + { + Cancelled = true; + } +} diff --git a/Content.Shared/Fluids/SpraySafetySystem.cs b/Content.Shared/Fluids/SpraySafetySystem.cs new file mode 100644 index 00000000000..82006a995b9 --- /dev/null +++ b/Content.Shared/Fluids/SpraySafetySystem.cs @@ -0,0 +1,44 @@ +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Fluids.Components; +using Content.Shared.Item.ItemToggle; +using Content.Shared.Popups; +using Robust.Shared.Audio.Systems; + +namespace Content.Shared.Fluids; + +public sealed class SpraySafetySystem : EntitySystem +{ + [Dependency] private readonly ItemToggleSystem _toggle = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnTransferAttempt); + SubscribeLocalEvent(OnTransferred); + SubscribeLocalEvent(OnSprayAttempt); + } + + private void OnTransferAttempt(Entity ent, ref SolutionTransferAttemptEvent args) + { + var (uid, comp) = ent; + if (uid == args.To && !_toggle.IsActivated(uid)) + args.Cancel(Loc.GetString(comp.Popup)); + } + + private void OnTransferred(Entity ent, ref SolutionTransferredEvent args) + { + _audio.PlayPredicted(ent.Comp.RefillSound, ent, args.User); + } + + private void OnSprayAttempt(Entity ent, ref SprayAttemptEvent args) + { + if (!_toggle.IsActivated(ent.Owner)) + { + _popup.PopupEntity(Loc.GetString(ent.Comp.Popup), ent, args.User); + args.Cancel(); + } + } +} diff --git a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml index 5aaa58dbbee..89b421c97d4 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fire_extinguisher.yml @@ -7,8 +7,8 @@ - type: Sprite sprite: Objects/Misc/fire_extinguisher.rsi layers: - - state: fire_extinguisher_closed - map: [ "enabled" ] + - state: fire_extinguisher_closed + map: [ "enum.ToggleVisuals.Layer" ] - type: Item sprite: Objects/Misc/fire_extinguisher.rsi size: Normal @@ -24,6 +24,8 @@ - type: DrainableSolution solution: spray - type: SolutionTransfer + maxTransferAmount: 100 + transferAmount: 100 - type: UseDelay - type: Spray transferAmount: 10 @@ -34,8 +36,20 @@ vaporAmount: 3 vaporSpread: 90 sprayVelocity: 2.0 - - type: FireExtinguisher - hasSafety: true + - type: ItemToggle + soundActivate: + path: /Audio/Machines/button.ogg + params: + variation: 0.125 + volume: -4 + soundDeactivate: + path: /Audio/Machines/button.ogg + params: + variation: 0.125 + volume: -4 + - type: ToggleVerb + text: fire-extinguisher-component-verb-text + - type: SpraySafety - type: MeleeWeapon wideAnimationRotation: 180 damage: @@ -46,14 +60,14 @@ - type: Tool qualities: - Rolling - speedModifier: 0.5 # its very big, akward to use + speedModifier: 0.5 # its very big, awkward to use - type: Appearance - type: GenericVisualizer visuals: - enum.FireExtinguisherVisuals.Safety: - enabled: - True: { state: fire_extinguisher_closed } - False: { state: fire_extinguisher_open } + enum.ToggleVisuals.Toggled: + enum.ToggleVisuals.Layer: + True: { state: fire_extinguisher_open } + False: { state: fire_extinguisher_closed } - type: PhysicalComposition materialComposition: Steel: 100 @@ -107,4 +121,4 @@ - type: PhysicalComposition materialComposition: Steel: 50 - Glass: 40 \ No newline at end of file + Glass: 40 From cb98b659adf022deb575910abac4b124baa6e7d4 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Fri, 12 Jul 2024 21:20:17 -0700 Subject: [PATCH 50/78] Update SalvageSystem.Magnet to not use Component.Owner (#29961) * Update SalvageSystem.Magnet.cs * Update SalvageSystem.Magnet.cs the right way --------- Co-authored-by: plykiya --- .../Salvage/SalvageSystem.Magnet.cs | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/Content.Server/Salvage/SalvageSystem.Magnet.cs b/Content.Server/Salvage/SalvageSystem.Magnet.cs index 3fe4baca8b6..5e85fdb573a 100644 --- a/Content.Server/Salvage/SalvageSystem.Magnet.cs +++ b/Content.Server/Salvage/SalvageSystem.Magnet.cs @@ -8,6 +8,7 @@ using Content.Shared.Salvage.Magnet; using Robust.Server.Maps; using Robust.Shared.Map; +using Robust.Shared.Map.Components; namespace Content.Server.Salvage; @@ -253,7 +254,8 @@ private async Task TakeMagnetOffer(Entity data, int var seed = data.Comp.Offered[index]; var offering = GetSalvageOffering(seed); - var salvMap = _mapManager.CreateMap(); + var salvMap = _mapSystem.CreateMap(); + var salvMapXform = Transform(salvMap); // Set values while awaiting asteroid dungeon if relevant so we can't double-take offers. data.Comp.ActiveSeed = seed; @@ -264,8 +266,8 @@ private async Task TakeMagnetOffer(Entity data, int switch (offering) { case AsteroidOffering asteroid: - var grid = _mapManager.CreateGrid(salvMap); - await _dungeon.GenerateDungeonAsync(asteroid.DungeonConfig, grid.Owner, grid, Vector2i.Zero, seed); + var grid = _mapManager.CreateGridEntity(salvMap); + await _dungeon.GenerateDungeonAsync(asteroid.DungeonConfig, grid.Owner, grid.Comp, Vector2i.Zero, seed); break; case SalvageOffering wreck: var salvageProto = wreck.SalvageMap; @@ -275,10 +277,10 @@ private async Task TakeMagnetOffer(Entity data, int Offset = new Vector2(0, 0) }; - if (!_map.TryLoad(salvMap, salvageProto.MapPath.ToString(), out var roots, opts)) + if (!_map.TryLoad(salvMapXform.MapID, salvageProto.MapPath.ToString(), out _, opts)) { Report(magnet, MagnetChannel, "salvage-system-announcement-spawn-debris-disintegrated"); - _mapManager.DeleteMap(salvMap); + _mapManager.DeleteMap(salvMapXform.MapID); return; } @@ -288,15 +290,14 @@ private async Task TakeMagnetOffer(Entity data, int } Box2? bounds = null; - var mapXform = _xformQuery.GetComponent(_mapManager.GetMapEntityId(salvMap)); - if (mapXform.ChildCount == 0) + if (salvMapXform.ChildCount == 0) { Report(magnet.Owner, MagnetChannel, "salvage-system-announcement-spawn-no-debris-available"); return; } - var mapChildren = mapXform.ChildEnumerator; + var mapChildren = salvMapXform.ChildEnumerator; while (mapChildren.MoveNext(out var mapChild)) { @@ -340,19 +341,25 @@ private async Task TakeMagnetOffer(Entity data, int if (!TryGetSalvagePlacementLocation(mapId, attachedBounds, bounds!.Value, worldAngle, out var spawnLocation, out var spawnAngle)) { Report(magnet.Owner, MagnetChannel, "salvage-system-announcement-spawn-no-debris-available"); - _mapManager.DeleteMap(salvMap); + _mapManager.DeleteMap(salvMapXform.MapID); return; } + // I have no idea if we want to return on failure or not + // but I assume trying to set the parent with a null value wouldn't have worked out anyways + if (!_mapSystem.TryGetMap(spawnLocation.MapId, out var spawnUid)) + return; + data.Comp.ActiveEntities = null; - mapChildren = mapXform.ChildEnumerator; + mapChildren = salvMapXform.ChildEnumerator; // It worked, move it into position and cleanup values. while (mapChildren.MoveNext(out var mapChild)) { var salvXForm = _xformQuery.GetComponent(mapChild); var localPos = salvXForm.LocalPosition; - _transform.SetParent(mapChild, salvXForm, _mapManager.GetMapEntityId(spawnLocation.MapId)); + + _transform.SetParent(mapChild, salvXForm, spawnUid.Value); _transform.SetWorldPositionRotation(mapChild, spawnLocation.Position + localPos, spawnAngle, salvXForm); data.Comp.ActiveEntities ??= new List(); @@ -371,7 +378,7 @@ private async Task TakeMagnetOffer(Entity data, int } Report(magnet.Owner, MagnetChannel, "salvage-system-announcement-arrived", ("timeLeft", data.Comp.ActiveTime.TotalSeconds)); - _mapManager.DeleteMap(salvMap); + _mapManager.DeleteMap(salvMapXform.MapID); data.Comp.Announced = false; From f969fd2bfbf143583b87fb91e315ae187344fb4e Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 13 Jul 2024 16:05:32 +1000 Subject: [PATCH 51/78] Update submodule to 228.0.0 (#29947) * Update submodule to 228.0.0 * Fix every single test * Also this one --- .../Tests/Interaction/InteractionTest.EntitySpecifier.cs | 2 +- .../Tests/Interaction/InteractionTest.Helpers.cs | 2 +- RobustToolbox | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs index 053152dbe1b..194bc54fba6 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs @@ -114,7 +114,7 @@ await Server.WaitPost(() => return await SpawnEntity((stack.StackTypeId, spec.Quantity), coords); Assert.That(spec.Quantity, Is.EqualTo(1), "SpawnEntity only supports returning a singular entity"); - await Server.WaitPost(() => uid = SEntMan.SpawnEntity(spec.Prototype, coords)); + await Server.WaitPost(() => uid = SEntMan.SpawnAtPosition(spec.Prototype, coords)); return uid; } diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs index 33e4da3fa34..0f2c314ed01 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs @@ -91,7 +91,7 @@ protected async Task SpawnTarget(string prototype) Target = NetEntity.Invalid; await Server.WaitPost(() => { - Target = SEntMan.GetNetEntity(SEntMan.SpawnEntity(prototype, SEntMan.GetCoordinates(TargetCoords))); + Target = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(TargetCoords))); }); await RunTicks(5); diff --git a/RobustToolbox b/RobustToolbox index a9aea7027f1..fc1cca4f48f 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit a9aea7027f1840c83bcaf1c973caf099745f9eed +Subproject commit fc1cca4f48f2f2d3fbf41aa42b80b4e43b1095a4 From 361ec5941255940db153551f3505bfc7b8f32ee5 Mon Sep 17 00:00:00 2001 From: Errant <35878406+Errant-4@users.noreply.github.com> Date: Sat, 13 Jul 2024 08:05:57 +0200 Subject: [PATCH 52/78] Dummy vox deserves a proper vox name (#29789) SKREEEEEeeeeeeee --- Resources/Prototypes/Entities/Mobs/Player/vox.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Mobs/Player/vox.yml b/Resources/Prototypes/Entities/Mobs/Player/vox.yml index e7ad39d7316..28906584cb9 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/vox.yml @@ -1,5 +1,5 @@ - type: entity save: false - name: Urist McVox + name: Uristititi McVox parent: BaseMobVox - id: MobVox \ No newline at end of file + id: MobVox From 49096cf14f8898bdbcb997fa3bbeacbcdccae67e Mon Sep 17 00:00:00 2001 From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Sat, 13 Jul 2024 02:09:19 -0400 Subject: [PATCH 53/78] Watches (#29550) * watches * rename * add it into loot pools --- Content.Client/Clock/ClockSystem.cs | 26 +++++ Content.Server/Clock/ClockSystem.cs | 42 ++++++++ Content.Shared/Clock/ClockComponent.cs | 42 ++++++++ .../Clock/GlobalTimeManagerComponent.cs | 16 +++ Content.Shared/Clock/SharedClockSystem.cs | 66 ++++++++++++ Resources/Locale/en-US/devices/clock.ftl | 1 + .../Prototypes/Catalog/Fills/Lockers/misc.yml | 2 + .../Markers/Spawners/Random/maintenance.yml | 2 + .../Entities/Objects/Devices/wristwatch.yml | 59 +++++++++++ .../Devices/goldwatch.rsi/equipped-HAND.png | Bin 0 -> 295 bytes .../Devices/goldwatch.rsi/goldwatch.png | Bin 0 -> 1180 bytes .../Objects/Devices/goldwatch.rsi/hours_0.png | Bin 0 -> 145 bytes .../Objects/Devices/goldwatch.rsi/hours_1.png | Bin 0 -> 148 bytes .../Devices/goldwatch.rsi/hours_10.png | Bin 0 -> 150 bytes .../Devices/goldwatch.rsi/hours_11.png | Bin 0 -> 146 bytes .../Objects/Devices/goldwatch.rsi/hours_2.png | Bin 0 -> 149 bytes .../Objects/Devices/goldwatch.rsi/hours_3.png | Bin 0 -> 143 bytes .../Objects/Devices/goldwatch.rsi/hours_4.png | Bin 0 -> 151 bytes .../Objects/Devices/goldwatch.rsi/hours_5.png | Bin 0 -> 149 bytes .../Objects/Devices/goldwatch.rsi/hours_6.png | Bin 0 -> 145 bytes .../Objects/Devices/goldwatch.rsi/hours_7.png | Bin 0 -> 148 bytes .../Objects/Devices/goldwatch.rsi/hours_8.png | Bin 0 -> 149 bytes .../Objects/Devices/goldwatch.rsi/hours_9.png | Bin 0 -> 142 bytes .../Devices/goldwatch.rsi/inhand-left.png | Bin 0 -> 271 bytes .../Devices/goldwatch.rsi/inhand-right.png | Bin 0 -> 273 bytes .../Objects/Devices/goldwatch.rsi/meta.json | 98 ++++++++++++++++++ .../Devices/goldwatch.rsi/minutes_0.png | Bin 0 -> 144 bytes .../Devices/goldwatch.rsi/minutes_1.png | Bin 0 -> 150 bytes .../Devices/goldwatch.rsi/minutes_10.png | Bin 0 -> 151 bytes .../Devices/goldwatch.rsi/minutes_11.png | Bin 0 -> 150 bytes .../Devices/goldwatch.rsi/minutes_2.png | Bin 0 -> 148 bytes .../Devices/goldwatch.rsi/minutes_3.png | Bin 0 -> 142 bytes .../Devices/goldwatch.rsi/minutes_4.png | Bin 0 -> 148 bytes .../Devices/goldwatch.rsi/minutes_5.png | Bin 0 -> 150 bytes .../Devices/goldwatch.rsi/minutes_6.png | Bin 0 -> 143 bytes .../Devices/goldwatch.rsi/minutes_7.png | Bin 0 -> 149 bytes .../Devices/goldwatch.rsi/minutes_8.png | Bin 0 -> 146 bytes .../Devices/goldwatch.rsi/minutes_9.png | Bin 0 -> 143 bytes .../Devices/wristwatch.rsi/equipped-HAND.png | Bin 0 -> 292 bytes .../Devices/wristwatch.rsi/hours_0.png | Bin 0 -> 151 bytes .../Devices/wristwatch.rsi/hours_1.png | Bin 0 -> 152 bytes .../Devices/wristwatch.rsi/hours_10.png | Bin 0 -> 156 bytes .../Devices/wristwatch.rsi/hours_11.png | Bin 0 -> 153 bytes .../Devices/wristwatch.rsi/hours_2.png | Bin 0 -> 152 bytes .../Devices/wristwatch.rsi/hours_3.png | Bin 0 -> 153 bytes .../Devices/wristwatch.rsi/hours_4.png | Bin 0 -> 155 bytes .../Devices/wristwatch.rsi/hours_5.png | Bin 0 -> 155 bytes .../Devices/wristwatch.rsi/hours_6.png | Bin 0 -> 152 bytes .../Devices/wristwatch.rsi/hours_7.png | Bin 0 -> 153 bytes .../Devices/wristwatch.rsi/hours_8.png | Bin 0 -> 152 bytes .../Devices/wristwatch.rsi/hours_9.png | Bin 0 -> 150 bytes .../Devices/wristwatch.rsi/inhand-left.png | Bin 0 -> 252 bytes .../Devices/wristwatch.rsi/inhand-right.png | Bin 0 -> 254 bytes .../Objects/Devices/wristwatch.rsi/meta.json | 98 ++++++++++++++++++ .../Devices/wristwatch.rsi/minutes_0.png | Bin 0 -> 152 bytes .../Devices/wristwatch.rsi/minutes_1.png | Bin 0 -> 158 bytes .../Devices/wristwatch.rsi/minutes_10.png | Bin 0 -> 159 bytes .../Devices/wristwatch.rsi/minutes_11.png | Bin 0 -> 160 bytes .../Devices/wristwatch.rsi/minutes_2.png | Bin 0 -> 157 bytes .../Devices/wristwatch.rsi/minutes_3.png | Bin 0 -> 151 bytes .../Devices/wristwatch.rsi/minutes_4.png | Bin 0 -> 160 bytes .../Devices/wristwatch.rsi/minutes_5.png | Bin 0 -> 159 bytes .../Devices/wristwatch.rsi/minutes_6.png | Bin 0 -> 153 bytes .../Devices/wristwatch.rsi/minutes_7.png | Bin 0 -> 157 bytes .../Devices/wristwatch.rsi/minutes_8.png | Bin 0 -> 155 bytes .../Devices/wristwatch.rsi/minutes_9.png | Bin 0 -> 150 bytes .../Devices/wristwatch.rsi/wristwatch.png | Bin 0 -> 887 bytes 67 files changed, 452 insertions(+) create mode 100644 Content.Client/Clock/ClockSystem.cs create mode 100644 Content.Server/Clock/ClockSystem.cs create mode 100644 Content.Shared/Clock/ClockComponent.cs create mode 100644 Content.Shared/Clock/GlobalTimeManagerComponent.cs create mode 100644 Content.Shared/Clock/SharedClockSystem.cs create mode 100644 Resources/Locale/en-US/devices/clock.ftl create mode 100644 Resources/Prototypes/Entities/Objects/Devices/wristwatch.yml create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/equipped-HAND.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/goldwatch.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_0.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_1.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_10.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_11.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_2.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_3.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_4.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_5.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_6.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_7.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_8.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/hours_9.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/meta.json create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_0.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_1.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_10.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_11.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_2.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_3.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_4.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_5.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_6.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_7.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_8.png create mode 100644 Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_9.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/equipped-HAND.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_0.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_1.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_10.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_11.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_2.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_3.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_4.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_5.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_6.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_7.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_8.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/hours_9.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/meta.json create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_0.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_1.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_10.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_11.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_2.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_3.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_4.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_5.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_6.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_7.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_8.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_9.png create mode 100644 Resources/Textures/Objects/Devices/wristwatch.rsi/wristwatch.png diff --git a/Content.Client/Clock/ClockSystem.cs b/Content.Client/Clock/ClockSystem.cs new file mode 100644 index 00000000000..7097ada9df6 --- /dev/null +++ b/Content.Client/Clock/ClockSystem.cs @@ -0,0 +1,26 @@ +using Content.Shared.Clock; +using Robust.Client.GameObjects; + +namespace Content.Client.Clock; + +public sealed class ClockSystem : SharedClockSystem +{ + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var sprite)) + { + if (!sprite.LayerMapTryGet(ClockVisualLayers.HourHand, out var hourLayer) || + !sprite.LayerMapTryGet(ClockVisualLayers.MinuteHand, out var minuteLayer)) + continue; + + var time = GetClockTime((uid, comp)); + var hourState = $"{comp.HoursBase}{time.Hours % 12}"; + var minuteState = $"{comp.MinutesBase}{time.Minutes / 5}"; + sprite.LayerSetState(hourLayer, hourState); + sprite.LayerSetState(minuteLayer, minuteState); + } + } +} diff --git a/Content.Server/Clock/ClockSystem.cs b/Content.Server/Clock/ClockSystem.cs new file mode 100644 index 00000000000..29400a59f7b --- /dev/null +++ b/Content.Server/Clock/ClockSystem.cs @@ -0,0 +1,42 @@ +using Content.Server.GameTicking.Events; +using Content.Shared.Clock; +using Content.Shared.Destructible; +using Robust.Server.GameStates; +using Robust.Shared.Random; + +namespace Content.Server.Clock; + +public sealed class ClockSystem : SharedClockSystem +{ + [Dependency] private readonly PvsOverrideSystem _pvsOverride = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnRoundStart); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnBreak); + } + + private void OnRoundStart(RoundStartingEvent ev) + { + var manager = Spawn(); + AddComp(manager); + } + + private void OnMapInit(Entity ent, ref MapInitEvent args) + { + ent.Comp.TimeOffset = TimeSpan.FromHours(_robustRandom.NextFloat(0, 24)); + _pvsOverride.AddGlobalOverride(ent); + Dirty(ent); + } + + private void OnBreak(Entity ent, ref BreakageEventArgs args) + { + ent.Comp.StuckTime = GetClockTime(ent); + Dirty(ent, ent.Comp); + } +} diff --git a/Content.Shared/Clock/ClockComponent.cs b/Content.Shared/Clock/ClockComponent.cs new file mode 100644 index 00000000000..3a1027d8136 --- /dev/null +++ b/Content.Shared/Clock/ClockComponent.cs @@ -0,0 +1,42 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Clock; + +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedClockSystem))] +[AutoGenerateComponentState] +public sealed partial class ClockComponent : Component +{ + /// + /// If not null, this time will be permanently shown. + /// + [DataField, AutoNetworkedField] + public TimeSpan? StuckTime; + + /// + /// The format in which time is displayed. + /// + [DataField, AutoNetworkedField] + public ClockType ClockType = ClockType.TwelveHour; + + [DataField] + public string HoursBase = "hours_"; + + [DataField] + public string MinutesBase = "minutes_"; +} + +[Serializable, NetSerializable] +public enum ClockType : byte +{ + TwelveHour, + TwentyFourHour +} + +[Serializable, NetSerializable] +public enum ClockVisualLayers : byte +{ + HourHand, + MinuteHand +} diff --git a/Content.Shared/Clock/GlobalTimeManagerComponent.cs b/Content.Shared/Clock/GlobalTimeManagerComponent.cs new file mode 100644 index 00000000000..764b578b250 --- /dev/null +++ b/Content.Shared/Clock/GlobalTimeManagerComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Clock; + +/// +/// This is used for globally managing the time on-station +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause, Access(typeof(SharedClockSystem))] +public sealed partial class GlobalTimeManagerComponent : Component +{ + /// + /// A fixed random offset, used to fuzz the time between shifts. + /// + [DataField, AutoPausedField, AutoNetworkedField] + public TimeSpan TimeOffset; +} diff --git a/Content.Shared/Clock/SharedClockSystem.cs b/Content.Shared/Clock/SharedClockSystem.cs new file mode 100644 index 00000000000..0a7c1b01b2c --- /dev/null +++ b/Content.Shared/Clock/SharedClockSystem.cs @@ -0,0 +1,66 @@ +using System.Linq; +using Content.Shared.Examine; +using Content.Shared.GameTicking; + +namespace Content.Shared.Clock; + +public abstract class SharedClockSystem : EntitySystem +{ + [Dependency] private readonly SharedGameTicker _ticker = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnExamined); + } + + private void OnExamined(Entity ent, ref ExaminedEvent args) + { + if (!args.IsInDetailsRange) + return; + + args.PushMarkup(Loc.GetString("clock-examine", ("time", GetClockTimeText(ent)))); + } + + public string GetClockTimeText(Entity ent) + { + var time = GetClockTime(ent); + switch (ent.Comp.ClockType) + { + case ClockType.TwelveHour: + return time.ToString(@"h\:mm"); + case ClockType.TwentyFourHour: + return time.ToString(@"hh\:mm"); + default: + throw new ArgumentOutOfRangeException(); + } + } + + private TimeSpan GetGlobalTime() + { + return (EntityQuery().FirstOrDefault()?.TimeOffset ?? TimeSpan.Zero) + _ticker.RoundDuration(); + } + + public TimeSpan GetClockTime(Entity ent) + { + var comp = ent.Comp; + + if (comp.StuckTime != null) + return comp.StuckTime.Value; + + var time = GetGlobalTime(); + + switch (comp.ClockType) + { + case ClockType.TwelveHour: + var adjustedHours = time.Hours % 12; + if (adjustedHours == 0) + adjustedHours = 12; + return new TimeSpan(adjustedHours, time.Minutes, time.Seconds); + case ClockType.TwentyFourHour: + return time; + default: + throw new ArgumentOutOfRangeException(); + } + } +} diff --git a/Resources/Locale/en-US/devices/clock.ftl b/Resources/Locale/en-US/devices/clock.ftl new file mode 100644 index 00000000000..6d0aef1eb76 --- /dev/null +++ b/Resources/Locale/en-US/devices/clock.ftl @@ -0,0 +1 @@ +clock-examine = The time reads: [color=white]{$time}[/color] diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index fac36ba710c..b49fa383bce 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -170,6 +170,8 @@ prob: 0.20 - id: BarberScissors prob: 0.05 + - id: Wristwatch + prob: 0.05 - id: BookRandomStory prob: 0.1 # Syndicate loot diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml index 7d7d9697d2c..00798b36e0f 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/maintenance.yml @@ -67,6 +67,7 @@ - ClothingHeadHatCowboyBountyHunter - ClothingNeckAutismPin - ClothingNeckGoldAutismPin + - WristwatchGold rareChance: 0.01 prototypes: - Lighter @@ -128,6 +129,7 @@ - ClothingShoesTourist - ClothingUniformJumpsuitLoungewear - ClothingHeadHatCowboyRed + - Wristwatch chance: 0.6 offset: 0.0 diff --git a/Resources/Prototypes/Entities/Objects/Devices/wristwatch.yml b/Resources/Prototypes/Entities/Objects/Devices/wristwatch.yml new file mode 100644 index 00000000000..7fbb4aecf61 --- /dev/null +++ b/Resources/Prototypes/Entities/Objects/Devices/wristwatch.yml @@ -0,0 +1,59 @@ +- type: entity + id: Wristwatch + parent: BaseItem + name: wristwatch + description: A cheap watch for telling time. How much did you waste playing Space Station 14? + components: + - type: Sprite + sprite: Objects/Devices/wristwatch.rsi + layers: + - state: wristwatch + - map: [ "enum.ClockVisualLayers.MinuteHand"] + - map: [ "enum.ClockVisualLayers.HourHand"] + - type: Clock + - type: Item + sprite: Objects/Devices/wristwatch.rsi + size: Small + - type: Clothing + sprite: Objects/Devices/wristwatch.rsi + slots: + - gloves + - type: Appearance + - type: Damageable + damageContainer: Inorganic + - type: StaticPrice + price: 50 + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - trigger: + !type:DamageGroupTrigger + damageGroup: Brute + damage: 25 + behaviors: + - !type:DoActsBehavior + acts: [ "Breakage" ] + +- type: entity + id: WristwatchGold + parent: Wristwatch + name: gold watch + description: A fancy watch worth more than your kidney. It was owned by the notorious Syndicate mobster Vunibaldo "200 Pound Horse Meat Grinder" Frediani. + components: + - type: Sprite + sprite: Objects/Devices/goldwatch.rsi + layers: + - state: goldwatch + - map: [ "enum.ClockVisualLayers.MinuteHand"] + - map: [ "enum.ClockVisualLayers.HourHand"] + - type: Item + sprite: Objects/Devices/goldwatch.rsi + - type: Clothing + sprite: Objects/Devices/goldwatch.rsi + - type: StaticPrice + price: 500 #if you ever increase the price of kidneys, increase this too. diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/equipped-HAND.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/equipped-HAND.png new file mode 100644 index 0000000000000000000000000000000000000000..59522f6c12e86f2b7a6364c89a0d9a25bb3e8fef GIT binary patch literal 295 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAQ1GUw zi(^Q|oVT|O`4|I5+#V_iZsP7_4OlsqYcB7K(y)cWw;S@fmPVP1DQx8aB^dakWoE{+ zKk1C-?`u!4zV}v1pWy^I!zZSOJq!v+%=h{Xx4&+g)l>b2-MDRW>hgDUPFIHCUB~YC zbnew>5!L6G|6}@Kmvb@0Pk2xMX<>&rh6&;fPgof$8Syf=GA+1se)V&`L&eh*_Gr}o t3d(0a@cPo%GQlXu3rkS!2RfyiLA^yGryYJmM^L^(#=bn3K5&dQS7w~ThxPPf3CF6+Up_QedvgYyZ#HELMe?$PtS8Y2? zljE;xbl?T;Y;UK@@I4xhk5E}%1NA+4$M0P^y*(9=#^~~`K}SLy3FtVxE)`9h6uo_g zKF^k_CmxD)by2>{;Q2_J9K@}`5;dw)LaI_u+{h`wid;q8_i{={u3SLG{t`Fa4k5## z8QQ#Ujv`SH`TP-bO?&CozM51nCu{|P9QHDyB5&HU+9J=)KgfOlDoP{_788%IOwS~9 z#h2Rw$t?&o#}awI%j9PgHJ)RfNEA_ZU@_N(T`J}kfV|_!U^#%u%QEIMPJ|;IZFRt) zr#(o)7@PsSi>AV}qlG2#EQb z0$rF35}?V?S;WYFam^Mgtyn>!-kTI?K0t3rh$aS~P*Z(^-$5~Al>k^JND=P!iunl# z1c*A&tclldXr_>Qw%8qKaxG-YasxmiB4VF^PSsx#3yDV;ARse(L=JLq`Envf27uIE z;8s^33xO+ry>OyJ&5o$3bzhJaL=1@X*!F~Z9f0kK9ONZS-TVwrG6D;1UiD|%J!upZID_PNGp5Of2y$5+NY&($!mJ+^a_J3@1suQlCa;4i)!4F?n+(>uG@#(#BQ(%`oxI9+ zSskz*tvcM(nNjUA-VZe7>aYgB1)2=jIc!GM0CFx%R1PHo$aNE{%GrKx1&9l1uYhhc zx+Xx3sF8Enj>rMo839}&LXhip^g{<&gxU?Yr6c@oY5EF;d&nXdQk)5pd)bbGpWY2c z1P&xZg1-Lfd6J`(adkkaMaXs0dH7u5qyTLN2;@A_tCTlncbivZne%7J(~1eJ{y6mffknI@wr8A+3=s0Wg|ABlL zF|hz7h#Ixx^OH(TjGaMxoRB*b07yNQ`6~FH52*EMK84m8SJVeYIQ>uHSOB2Y(U5k+ ur2YZJ+GhI)hS(k4FNAzS0z?G;W_$tEs%t8vJoi2T0000mdKI;Vst0Pf`}!2kdN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_11.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_11.png new file mode 100644 index 0000000000000000000000000000000000000000..e364438e4ecd35612f8d36bd0210b081e8e5aee1 GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCpwp z;uumf=k3LdoD2pW%on-JTHe_&Q2vov$iaE&E>op|{Ue|%AZU=!YC-eXS literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_2.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_2.png new file mode 100644 index 0000000000000000000000000000000000000000..87c642239fe1370c4a14745d559d81fdb8716ed6 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCq9# z;uumf=k2+TybKCF%#KP2Io{YSC+ ihVTfs9qbT^EzEK^4LRmc+hjH!B;e`l=d#Wzp$P!(2__T( literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_3.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_3.png new file mode 100644 index 0000000000000000000000000000000000000000..107b210ff4f3c9e0d121ec590091b896ba2918bd GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp?v z;uumf=k2+Tf(!y8E{S@5QFulkC`m%0QriXU5|5dy8{_S}HvH e!X+RJ7CgX|&A~F4?KMLLi0kR<=d#Wzp$PzNDkZr9 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_4.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_4.png new file mode 100644 index 0000000000000000000000000000000000000000..c03f799fcce9cb94ff51072e22083ab1511e0e34 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp_w z;uumf=k3LfoD2#)E`iL2EZ^k~lp|b%b~Lc?J&Z3-l@phL1XKtCf7nmRM&=gJWGUbm jU&8$H57SMiyNqBdc6T$5ojc=SOMrNuu6{1-oD!M<`(-7z literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_5.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_5.png new file mode 100644 index 0000000000000000000000000000000000000000..2835fadd249d0a84532950f44f547515a74f29d6 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCq9# z;uumf=k3LdoIusC7g?ki{$(#|vFmZukn<>c&vJbE_D4Wv5O9zANzWF$H9QO6No-gj izOG>Ff&2PknNn`^0~|XKYR3KtaXnrAT-G@yGywp`$15cO literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_6.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_6.png new file mode 100644 index 0000000000000000000000000000000000000000..55df41a9ce9fa78ca7b747f8b4a7369bde2092ca GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCq3z z;uumf=k0}qoD2pWM=x-_<+xjapzp!Ka|#;riZdTf+&L6GPPMtR?7EOWm%Ouh`_db;|#taD0e0swhmDyIMd literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_7.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/hours_7.png new file mode 100644 index 0000000000000000000000000000000000000000..890650474dfcf2593d48c0df4977ffc49a687cfb GIT binary patch literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp+t z;uumf=k0k%UIqmLW=B0n&L83(EOPCwb+g)1t~)F*Zea#01A`yz7rvLQjWul0=6q14 i&m8mE=zt_h;s_)E*YzB8*#ms5B literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-left.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..ec40991d4ecf42b62c9e4bfa358a24a938dface4 GIT binary patch literal 271 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAQ1GCq zi(^Q|oVPdkaxyuJFgz6G*~)#8$;8NP3s17~YJ<%}+#Ag6t8Q3Zq^aE~kgw61y~p$u zlS3TC32uf;Mg@I_C#(&77$%4_fCZKHtM8qkfBW&?Bu8FW3t4lWwF?<;-+fxXFCxg9 z@mu@rMf3jew{t|%g4LYh7^~~EYL3kDWOx=exqpT+=Yg2)PlCm2j5D%*?UMZFE5Myl a)u10&EMok2KPwB+GYp=telF{r5}E*wty!l4 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-right.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..aac69417e32f8656e2f87d013ae5b462f43879e4 GIT binary patch literal 273 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAQ1Gy) zi(^Q|oVPc3^B!^#V7Tb0)S@>*gFz|D>6C)nVlE@Cg$z}XgPT%sHm0lXII(N$yfxls zy?YoYh%R{O_Nif2&v@5zx%wcbwNdTi`%h`Omrf z)2=8q{C&7<#r^;B{R-H$qL|mPQT)tSqxo6qG8%SK(L`hB}_edZJv0j59y g&3%q>5@G#$kEboFyt=akR{01#^=T>t<8 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_1.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_1.png new file mode 100644 index 0000000000000000000000000000000000000000..0ef2c831f4d7d9a213a9a7a9c20360607b94a848 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCpto z;uumf=k0k%UIqmo7ROl)EPuFNoa9}d^{0Q?ygH$5{X?KC5a0~>sdcW`uwk>=fl7VG kJ6jpgRdYVzgUCs+*L?lWG50GM{~3^gr>mdKI;Vst0C#vP6951J literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_10.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_10.png new file mode 100644 index 0000000000000000000000000000000000000000..3019a20a9ba454202f8ed338bcdfaafcc25aed05 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp_w z;uumf=k3Lfyg=11fu1ES-{mI=Y8Z%ZJL0zf=k=JAr!UU704fB52B8TLciui`!udd^ lj&o;KV@{(yBShv&18c-Yj-3}@i&TIFJYD@<);T3K0RSQ!D?R`K literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_11.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_11.png new file mode 100644 index 0000000000000000000000000000000000000000..fcbf039b6ffdc5668c29a95790386bf6c26e691f GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCpto z;uumf=k29~yg=0~j-Fdse(PTlsz`XzaK2TdTw6l#%taZXIv@yOYMH-b>ssCgYgkUa l4%ObX(c#;7euxyqDLtp@96P_~zHkBwc)I$ztaD0e0s!chD@FhS literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_2.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_2.png new file mode 100644 index 0000000000000000000000000000000000000000..1304f56e757368d4d5d02aa54f405f34fa1b3259 GIT binary patch literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp+t z;uumf=k2+TybKCF%#KP2Io{YSC@ig3`BA%{(F6*2UngBb^DJB2_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_3.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_3.png new file mode 100644 index 0000000000000000000000000000000000000000..8f58881dbda93e25a21ab56f6ab0baeea8616a4b GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCpqn z;uumf=k2+Tf(!y8E{rVMPU3*p+{=*Ho b8Ns4DSugTe~DWM4f6_Y3a literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_5.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_5.png new file mode 100644 index 0000000000000000000000000000000000000000..1d71f45c4791977ae15c34eea85bb396938222a4 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCpto z;uumf=k2A7oIusC7g?ki{$oz?nOXl5b%|uY}?70No*bOBp+ON lUl)+i`2M{-P|Csj0ITK_j-Bsp%Jzd)db;|#taD0e0s!UED|!F` literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_6.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_6.png new file mode 100644 index 0000000000000000000000000000000000000000..5e1070e3a80454ab5edd53e2b7850b6c4fdfdc85 GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp?v z;uumf=j}yDP6h=521kCAroYTm2iwyGrZ~^=HQ2CKg&n901U`r-oZYzSs|WKQjt3kC fzyBLPa0LlV+n-{Y%b>)p1>$(R`njxgN@xNA9D^j} literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_7.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_7.png new file mode 100644 index 0000000000000000000000000000000000000000..4af1070d216a1a25590a53b6aa3f5a91b6413b18 GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCq9# z;uumf=k0k%UIqmo7ROm4oIk`nSmcf#s@3zI9Lu_S=PYKRGBB9IyrNwDTc=rrHs^y~ k(;3gHG`x;uvj9mq@IRl-F?X-$@-rZXp00i_>zopr00p@y8~^|S literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_8.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_8.png new file mode 100644 index 0000000000000000000000000000000000000000..39b252688a78299da0303e13de4000e038bb8339 GIT binary patch literal 146 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCpwp z;uumf=k2+JybJ;&E{;N87wQ|m3KXmgKDruJ-+VlKk{mx!83;V!KOj^4?)=uL0SC^0 i5PXpKgG0a&B;LSk{)1y~>B1A-AfBhIpUXO@geCw@bSf_Z literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_9.png b/Resources/Textures/Objects/Devices/goldwatch.rsi/minutes_9.png new file mode 100644 index 0000000000000000000000000000000000000000..b9614ed80f52a6292f44f8548ded905f170b710e GIT binary patch literal 143 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp?v z;uumf=k2+Tf(!y8E{p=fS?83{1OQ`3CdB{% literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/equipped-HAND.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/equipped-HAND.png new file mode 100644 index 0000000000000000000000000000000000000000..b12afe6d4eec194bee8911634e1060ccaa43b517 GIT binary patch literal 292 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAQ1F_k zi(^Q|oVT|&avlm0aeJsNwV{|RI3mKjO?XG+EiKzjZxu4<9Ggo8nV{7=-Pi>MZof{k)S{$Rl^IG|~>AOD9 z@&C*Ep!UYB=WnaezHI)UTgilJ0Cr;?8QzFrUUt_tqny+1`ECFBSIrC+vAZ7Y9a3QU oCBpE8m7$W+A&y~!IGD*Ft0Yr#`1X`lKp!%Ay85}Sb4q9e08OoHO#lD@ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_0.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_0.png new file mode 100644 index 0000000000000000000000000000000000000000..3af9ca46343c1cc46a95181236029f45ffc7fcf1 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp_w z;uumf=j}yCP6h)G)AZ&ZIon oZP=CWkap_cc7|6F$%euMtesytPYVh}Tm*@Dy85}Sb4q9e0JbwN@c;k- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_10.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_10.png new file mode 100644 index 0000000000000000000000000000000000000000..aeac86265673b1c55070ac1237c027112df89691 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp&lJK6k>0|rH=Th;al2?&AV|Q|)z4*}Q$iB}{GTY9 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_2.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_2.png new file mode 100644 index 0000000000000000000000000000000000000000..386166cabdd3578c61abb30fad6364611cdebaa7 GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp(s z;uumf=j}O1UIqgWmV=Y7h{R237f-o(g-P+l&EI#5pU->B2vi3Gry5MW3#S)0ta|pp nuYyxRQ1_wHf@+A=0jmd$pXYF%-o3Xn4J6{}>gTe~DWM4f#O*FS literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_3.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_3.png new file mode 100644 index 0000000000000000000000000000000000000000..2f5300928f2e1fb8cd3ff0242ab69c5200ab4a26 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCq6! z;uumf=k3LVoD2#)&KJe*9MRmhe)EitB6l4GTz~F-GK1Z-%o3;&0!}w{WGv1;_iX8p n!iFW5uO_`cQ7sOZX?V{dYd?b{?UiYaGf2SG)z4*}Q$iB}9d#~_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_4.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_4.png new file mode 100644 index 0000000000000000000000000000000000000000..c994c0568591ca8dac03794872c2421335f69bcc GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCq0y z;uumf=k3LVoD2#)&Vj7cU+KxN-(9k%i(Py}tJ0sCqBG5FF3JE^f#cos8I85sQF77&b{w4G6F7t^>bP0l+XkKs;?;e literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_7.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_7.png new file mode 100644 index 0000000000000000000000000000000000000000..fa7af8e5cca4735e331a30cfa96e4bd7aba03ac4 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCq6! z;uumf=k0k%UIqgWmV=Y7h{R237f%V~w(;UPxvp{c?sN;FGB9YcWe7dnF~QIwT-9OL p(jE2n@oXi%(;4S70%Z?aJYaO?=RE!C&gP9E#h$KyF6*2UngH6@Dn$SQ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_8.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_8.png new file mode 100644 index 0000000000000000000000000000000000000000..83caf8c8e170e619de757651e787ddb190f0e2bb GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp(s z;uumf=k2+Jyg=0~j-EMPaT@(`8r;e3HZIAVzVCc~PNtt7s1OW3usehn*Snl3b(pnu lhdlpu#p=fS?83{1OTP2D8c{$ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_9.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/hours_9.png new file mode 100644 index 0000000000000000000000000000000000000000..6334bb72fdc1733bc48f6078d32126779a1e23a4 GIT binary patch literal 150 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCpto z;uumf=k2+Jyg=0~j-G2yMQQZMt!Roe*vOdN^y_Br-S^A3pJxOr1%n*Mm9vzSa?7Vb iXE4;Zw`ID+2N9p~=QhXbc(L6vAg-sYpUXO@geCy%Dk<{- literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-left.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-left.png new file mode 100644 index 0000000000000000000000000000000000000000..76ca3c491159bfbe14fc4bbe0712376d010e8e9c GIT binary patch literal 252 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAP;i~6 zi(^Q|oVPbO@;Vs`90*VjH1On7OOR?yW=dO_AZ8_C{lvZNjroDODXLfJPSj>7Vs%)@ za6*(}CzC=r!xOHC7{&=8!5Nls3isB$e}BHS_X|@R-)`G@12*fKQVBg9uYUgjnj1we zRs$@OUaT_y*<-u6LAh<6)#URxiai=a(?3a8|6+pM_Jy&ou2dxFroeASpvxINUHx3v IIVCg!0CAvI4gdfE literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-right.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/inhand-right.png new file mode 100644 index 0000000000000000000000000000000000000000..f9ab9c67d492e6bcbab2032582b09efd6aa0eb53 GIT binary patch literal 254 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9F5M?jcysy3fAP;i5% zi(^Q|oVPbO@-i8U90*VjH1On7OOR?yW=dO_AZ8_?JjZXR(zn%%T{hLNs&`w*a6*(} zCzC=r!xOHC7{&?O3`MLCAi;#w508KSw0FO~y4WR_9Lt*OeF;2an=}tdB(MDV|1~$J z26R(yR38k~{v7$4H7R#yo2B5A_ZY@B z;uumf=k0k%UIqo8qXE+rvQ9Wkl&LXVIy0a4FUu6&bMB%HP#s7q)0;CkY1i0xWb=mJ tXDC14G2za1hFAL-PyH5hkcB9@z*lkX7LO7~v2YVez|+;wWt~$(695Z$EL8vi literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_10.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_10.png new file mode 100644 index 0000000000000000000000000000000000000000..9dad0d83cbe85a64b02d8ee9a552d191ae753355 GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5C>ZGJ z;uumf=k3Lfyg=11fgUkj#|sUvC$bzr_=4e>TjoF0@J~e|o_s*1An<@AL(niwwEVh= sL-^hHvpQk>8)Eh<Z4F z;uumf=k588yg=2gfgUkj#|sUvC$bzrxPwuw<=MKQZ+|<_Vg{-NDQ*xIj1IlcknXoq vi?yKKbaTaRrWHE8D=Pl8t^ipL1R3lxKg0McPTV$f0C7EC{an^LB{Ts5menmb literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_2.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_2.png new file mode 100644 index 0000000000000000000000000000000000000000..a4c3bbe7179bc4ac528d3d1c5c8ed342b7529a29 GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCqC$ z;uumf=k2A9yg=2gfgUl(1S75$Cg|{LYh0XhCtu^es7a*_P$38$FgtK2t$g~zRpE@1 q=cEOt(%;%Tv@I?8{|_R~5U`ciU50ys$SWy#kcg+NpUXO@geCy3Eh>os literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_3.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_3.png new file mode 100644 index 0000000000000000000000000000000000000000..6bfdc405c790914479d3f4582acb23b3840585cd GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCp_w z;uumf=k2-OybKCF%z;W9d6ZKkk98hz;;OQ1dKR;D_w>up8G#DHK!^Fx-rjhnAk)pu k89a?wZ2l(q;2T7yf$!Zf_626eGc!SaPgg&ebxsLQ0QvkX`2YX_ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_4.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_4.png new file mode 100644 index 0000000000000000000000000000000000000000..89c08f3396f0ff1bbfe620de16cadeda888989c7 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5C>Z4F z;uumf=k2A9yg=11fgUl(1S75$Cg||Ca%UtO%x{}C{qZatIiN}~XsBW^p1o7qX+FcZ uhb<-LlB>ceJQi0``C`{nB@0r}@ScJD`byq!byDHkAik%opUXO@geCw@EiDoN literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_5.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_5.png new file mode 100644 index 0000000000000000000000000000000000000000..4cbaa00d14b3d50aba0fe36ec3ee2468db228850 GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5C>ZGJ z;uumf=j|m%UZ851z#BTrET^R0lV0{HBp1Kh&(i<6ONI}q4h#;|G8jw0TX&q{=1sOW vuZ^QQOarRLug}qMVetL>m=~zzf#m^~R||N*ZKyx9AEd+6)z4*}Q$iB}N<1$? literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_6.png b/Resources/Textures/Objects/Devices/wristwatch.rsi/minutes_6.png new file mode 100644 index 0000000000000000000000000000000000000000..e834c52d8cd5aaaa10bbbd97c159e1cd82a8bafb GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?4jBOuH;Rhv&5DCq6! z;uumf=k29NP6h=Y=7SxHEcf$AXO4|Boy*)z@9UU+d{ t*t(DBhM-sW%k_+Xrdz!qwE|ItM6yeoWith n_j|*jLy8B4Jh|H$M47>|>_umJlzhUS9)ftDu6{1-oD!MIP)^@RCt{2S4(IUQ53yN%tErLXj|+;TN*N56cJn$>xT%{T|qa& zjkKbz6~RqFanVx62GlOQiK0TqjXMkOO2I*^;3A;XLK7Mq3#L>TQ-c)g=Q-zPUiv1P zU(<*PUKsA3_dDmF_n9}l=r7xU0^24q774P()`+QWb|FLcn?%<$Yra25V7M*F;2T>a z^lLF|OAdZJL_87TSsTE}78VW!Xk~4K$MyR2-A@$K;dirQd#@y12?)tW)&PdhZ|LCu zVjBR5STSDJVSjF8A{+?-ye=51C`ab?Ml3RYpafZYUoR@SDlo~`iN2&Qdv&aDjej$} z<^u_24>eXjs^ChXFF8ezuXb6Mf9gAJDib8cI~MMpIcF7Y2_zB;_CxngO-(d3F>Bf8 z?Zw-4L(|yk=x7N9B*cyhF>*Ca0sxQ2Vm!|0^R}Hun?j+$lF1||@ofAlTS^R++TgvX z_mZs&6$Avh0RH&uF71h&fpEr za&xaqiS|-j9jD{x2WeUdZ9fvF=@%os9QDm@r%ZjK(Wp;VRfYuM@$Q*`oINrJNW=aE z`l!9UUXeU6HxxQ-C?QmE1NEc;q(fyw^c(ju=HQ&O!tP61@MGmvP|>t|9be?QuJ zf5$G_~!uQ1Zs9Cr2E&X`=DaF3FAtJ>;Q@s2@?gXM3=6po>$c~sA|TAmimt}~y??`6+iz=%nuRh`+1LO8 N002ovPDHLkV1f$6lfVD~ literal 0 HcmV?d00001 From eb4ce85354c7c93da527bebca37cf986973f39a2 Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Sat, 13 Jul 2024 08:10:04 +0200 Subject: [PATCH 54/78] Remove imagesharp and StatusEffectAddedEvent from FlashOverlay (#28930) remove imagesharp and StatusEffectAddedEvent from FlashOverlay --- Content.Client/Flash/FlashOverlay.cs | 27 ++++++--------------------- Content.Client/Flash/FlashSystem.cs | 14 +++----------- 2 files changed, 9 insertions(+), 32 deletions(-) diff --git a/Content.Client/Flash/FlashOverlay.cs b/Content.Client/Flash/FlashOverlay.cs index 9ea00275e84..046be2aa621 100644 --- a/Content.Client/Flash/FlashOverlay.cs +++ b/Content.Client/Flash/FlashOverlay.cs @@ -1,27 +1,22 @@ using Content.Shared.Flash; using Content.Shared.Flash.Components; using Content.Shared.StatusEffect; -using Content.Client.Viewport; using Robust.Client.Graphics; -using Robust.Client.State; using Robust.Client.Player; using Robust.Shared.Enums; using Robust.Shared.Prototypes; using Robust.Shared.Timing; -using SixLabors.ImageSharp.PixelFormats; namespace Content.Client.Flash { public sealed class FlashOverlay : Overlay { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IClyde _displayManager = default!; - [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IGameTiming _timing = default!; - private readonly StatusEffectsSystem _statusSys; + private readonly StatusEffectsSystem _statusSys; public override OverlaySpace Space => OverlaySpace.WorldSpace; private readonly ShaderInstance _shader; @@ -56,20 +51,6 @@ protected override void FrameUpdate(FrameEventArgs args) PercentComplete = timeDone / lastsFor; } - public void ReceiveFlash() - { - if (_stateManager.CurrentState is IMainViewportState state) - { - // take a screenshot - // note that the callback takes a while and ScreenshotTexture will be null the first few Draws - state.Viewport.Viewport.Screenshot(image => - { - var rgba32Image = image.CloneAs(SixLabors.ImageSharp.Configuration.Default); - ScreenshotTexture = _displayManager.LoadTextureFromImage(rgba32Image); - }); - } - } - protected override bool BeforeDraw(in OverlayDrawArgs args) { if (!_entityManager.TryGetComponent(_playerManager.LocalEntity, out EyeComponent? eyeComp)) @@ -82,6 +63,11 @@ protected override bool BeforeDraw(in OverlayDrawArgs args) protected override void Draw(in OverlayDrawArgs args) { + if (RequestScreenTexture && ScreenTexture != null) + { + ScreenshotTexture = ScreenTexture; + RequestScreenTexture = false; // we only need the first frame, so we can stop the request now for performance reasons + } if (ScreenshotTexture == null) return; @@ -96,7 +82,6 @@ protected override void DisposeBehavior() { base.DisposeBehavior(); ScreenshotTexture = null; - PercentComplete = 1.0f; } } } diff --git a/Content.Client/Flash/FlashSystem.cs b/Content.Client/Flash/FlashSystem.cs index 9a0579f6aa3..146d84b990f 100644 --- a/Content.Client/Flash/FlashSystem.cs +++ b/Content.Client/Flash/FlashSystem.cs @@ -22,7 +22,6 @@ public override void Initialize() SubscribeLocalEvent(OnShutdown); SubscribeLocalEvent(OnPlayerAttached); SubscribeLocalEvent(OnPlayerDetached); - SubscribeLocalEvent(OnStatusAdded); _overlay = new(); } @@ -34,8 +33,8 @@ private void OnPlayerAttached(EntityUid uid, FlashedComponent component, LocalPl private void OnPlayerDetached(EntityUid uid, FlashedComponent component, LocalPlayerDetachedEvent args) { - _overlay.PercentComplete = 1.0f; _overlay.ScreenshotTexture = null; + _overlay.RequestScreenTexture = false; _overlayMan.RemoveOverlay(_overlay); } @@ -43,6 +42,7 @@ private void OnInit(EntityUid uid, FlashedComponent component, ComponentInit arg { if (_player.LocalEntity == uid) { + _overlay.RequestScreenTexture = true; _overlayMan.AddOverlay(_overlay); } } @@ -51,17 +51,9 @@ private void OnShutdown(EntityUid uid, FlashedComponent component, ComponentShut { if (_player.LocalEntity == uid) { - _overlay.PercentComplete = 1.0f; _overlay.ScreenshotTexture = null; + _overlay.RequestScreenTexture = false; _overlayMan.RemoveOverlay(_overlay); } } - - private void OnStatusAdded(EntityUid uid, FlashedComponent component, StatusEffectAddedEvent args) - { - if (_player.LocalEntity == uid && args.Key == FlashedKey) - { - _overlay.ReceiveFlash(); - } - } } From 044d84142c473ba91098dd23b85dc200c62d5765 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sat, 13 Jul 2024 06:10:25 +0000 Subject: [PATCH 55/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index eb495bc316d..ca549b51fb3 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,16 +1,4 @@ Entries: -- author: PJB3005 - changes: - - message: The item status menu has been moved to the side of the hands. There is - now one for each hand. - type: Add - - message: The item status menu fits with the rest of the HUD theme now. - type: Add - - message: Improved, fixed and otherwise added a bunch of item status menus. - type: Add - id: 6414 - time: '2024-04-21T13:16:23.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/22986 - author: FairlySadPanda changes: - message: Xenoarchaeology Traversal Distorters have been rolled into the Artifact @@ -3823,3 +3811,10 @@ id: 6913 time: '2024-07-13T04:14:30.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29830 +- author: EmoGarbage404 + changes: + - message: Added wristwatches for telling time. + type: Add + id: 6914 + time: '2024-07-13T06:09:19.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29550 From 87f7ea8aa9758a5f718c5696e5c5e0d6bc995563 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Sat, 13 Jul 2024 06:11:14 +0000 Subject: [PATCH 56/78] make cargo balance ui updating its own component (#28295) * add BankClientComponent and event * query BankClient instead of hardcoded CargoOrderConsole for updating * add BankClient to all ordering consoles * :trollface: * add Balance field to BankClient * forgor Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * m --------- Co-authored-by: deltanedas <@deltanedas:kde.org> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> --- .../Cargo/Systems/CargoSystem.Orders.cs | 10 +++++++ Content.Server/Cargo/Systems/CargoSystem.cs | 14 +++++----- .../Cargo/Components/BankClientComponent.cs | 26 +++++++++++++++++++ .../Entities/Mobs/Player/admin_ghost.yml | 1 + .../Entities/Objects/Misc/paper.yml | 1 + .../Machines/Computers/computers.yml | 1 + 6 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 Content.Shared/Cargo/Components/BankClientComponent.cs diff --git a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs index a288d7b07d4..dd408755e67 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Orders.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Orders.cs @@ -40,6 +40,7 @@ private void InitializeConsole() SubscribeLocalEvent(OnOrderUIOpened); SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnOrderBalanceUpdated); Reset(); } @@ -315,6 +316,15 @@ private void OnOrderUIOpened(EntityUid uid, CargoOrderConsoleComponent component #endregion + + private void OnOrderBalanceUpdated(Entity ent, ref BankBalanceUpdatedEvent args) + { + if (!_uiSystem.IsUiOpen(ent.Owner, CargoConsoleUiKey.Orders)) + return; + + UpdateOrderState(ent, args.Station); + } + private void UpdateOrderState(EntityUid consoleUid, EntityUid? station) { if (station == null || diff --git a/Content.Server/Cargo/Systems/CargoSystem.cs b/Content.Server/Cargo/Systems/CargoSystem.cs index a93a7bdcc22..1b33404e355 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.cs @@ -81,18 +81,18 @@ public override void Update(float frameTime) public void UpdateBankAccount(EntityUid uid, StationBankAccountComponent component, int balanceAdded) { component.Balance += balanceAdded; - var query = EntityQueryEnumerator(); + var query = EntityQueryEnumerator(); - while (query.MoveNext(out var oUid, out var _)) + var ev = new BankBalanceUpdatedEvent(uid, component.Balance); + while (query.MoveNext(out var client, out var comp, out var xform)) { - if (!_uiSystem.IsUiOpen(oUid, CargoConsoleUiKey.Orders)) - continue; - - var station = _station.GetOwningStation(oUid); + var station = _station.GetOwningStation(client, xform); if (station != uid) continue; - UpdateOrderState(oUid, station); + comp.Balance = component.Balance; + Dirty(client, comp); + RaiseLocalEvent(client, ref ev); } } } diff --git a/Content.Shared/Cargo/Components/BankClientComponent.cs b/Content.Shared/Cargo/Components/BankClientComponent.cs new file mode 100644 index 00000000000..4fd70855034 --- /dev/null +++ b/Content.Shared/Cargo/Components/BankClientComponent.cs @@ -0,0 +1,26 @@ +using Content.Shared.Cargo; +using Robust.Shared.GameStates; + +namespace Content.Shared.Cargo.Components; + +/// +/// Makes an entity a client of the station's bank account. +/// When its balance changes it will have raised on it. +/// Other systems can then use this for logic or to update ui states. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedCargoSystem))] +[AutoGenerateComponentState] +public sealed partial class BankClientComponent : Component +{ + /// + /// The balance updated for the last station this entity was a part of. + /// + [DataField, AutoNetworkedField] + public int Balance; +} + +/// +/// Raised on an entity with when the bank's balance is updated. +/// +[ByRefEvent] +public record struct BankBalanceUpdatedEvent(EntityUid Station, int Balance); diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index a0eb01e03b3..0c814e08f71 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -69,6 +69,7 @@ - type: RadarConsole followEntity: true - type: CargoOrderConsole + - type: BankClient - type: CrewMonitoringConsole - type: GeneralStationRecordConsole canDeleteEntries: true diff --git a/Resources/Prototypes/Entities/Objects/Misc/paper.yml b/Resources/Prototypes/Entities/Objects/Misc/paper.yml index 1c8d8754884..5dbafa6416a 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/paper.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/paper.yml @@ -548,6 +548,7 @@ tags: - Write - type: CargoOrderConsole + - type: BankClient - type: ActivatableUI verbText: qm-clipboard-computer-verb-text key: enum.CargoConsoleUiKey.Orders diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index 3f9a9bc222e..4baf9bb5dc6 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -735,6 +735,7 @@ - map: ["computerLayerKeys"] state: tech_key - type: CargoOrderConsole + - type: BankClient - type: ActiveRadio channels: - Supply From ac768fe9f14ffd0ef0e64ebe717e5f4d459192a0 Mon Sep 17 00:00:00 2001 From: CaasGit <87243814+CaasGit@users.noreply.github.com> Date: Fri, 12 Jul 2024 23:12:57 -0700 Subject: [PATCH 57/78] fix(SharedGunSystem): Return and debug log on CreateEffect. (#29656) Sometimes CreateEffect is called on a Invalid Entity. This now causes that to check, thus returning out and printing some hopefully helpful logs to try to track down the real source of this issue. --- Content.Client/Weapons/Ranged/Systems/GunSystem.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs index ac5914d47c0..71342277674 100644 --- a/Content.Client/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Client/Weapons/Ranged/Systems/GunSystem.cs @@ -276,6 +276,14 @@ protected override void CreateEffect(EntityUid gunUid, MuzzleFlashEvent message, if (!Timing.IsFirstTimePredicted) return; + // EntityUid check added to stop throwing exceptions due to https://github.com/space-wizards/space-station-14/issues/28252 + // TODO: Check to see why invalid entities are firing effects. + if (gunUid == EntityUid.Invalid) + { + Log.Debug($"Invalid Entity sent MuzzleFlashEvent (proto: {message.Prototype}, user: {user})"); + return; + } + var gunXform = Transform(gunUid); var gridUid = gunXform.GridUid; EntityCoordinates coordinates; From 6c1d1057d5d45e49e8d4262c495219b89c1bcf38 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Sat, 13 Jul 2024 03:59:33 -0700 Subject: [PATCH 58/78] Replace DetachParentToNull() with DetachEntity() (#29992) DETACH PARENT TO ENTITY Co-authored-by: plykiya --- Content.Client/Materials/MaterialStorageSystem.cs | 4 ++-- Content.Client/Stack/StackSystem.cs | 2 +- Content.Shared/Actions/ActionContainerSystem.cs | 2 +- Content.Shared/Follower/FollowerSystem.cs | 2 +- .../Inventory/VirtualItem/SharedVirtualItemSystem.cs | 2 +- .../Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Content.Client/Materials/MaterialStorageSystem.cs b/Content.Client/Materials/MaterialStorageSystem.cs index edd07391f7b..592471d6736 100644 --- a/Content.Client/Materials/MaterialStorageSystem.cs +++ b/Content.Client/Materials/MaterialStorageSystem.cs @@ -1,4 +1,4 @@ -using Content.Shared.Materials; +using Content.Shared.Materials; using Robust.Client.GameObjects; namespace Content.Client.Materials; @@ -49,7 +49,7 @@ public override bool TryInsertMaterialEntity(EntityUid user, { if (!base.TryInsertMaterialEntity(user, toInsert, receiver, storage, material, composition)) return false; - _transform.DetachParentToNull(toInsert, Transform(toInsert)); + _transform.DetachEntity(toInsert, Transform(toInsert)); return true; } } diff --git a/Content.Client/Stack/StackSystem.cs b/Content.Client/Stack/StackSystem.cs index c081581338f..7e681aeba3e 100644 --- a/Content.Client/Stack/StackSystem.cs +++ b/Content.Client/Stack/StackSystem.cs @@ -44,7 +44,7 @@ public override void SetCount(EntityUid uid, int amount, StackComponent? compone // TODO PREDICT ENTITY DELETION: This should really just be a normal entity deletion call. if (component.Count <= 0 && !component.Lingering) { - Xform.DetachParentToNull(uid, Transform(uid)); + Xform.DetachEntity(uid, Transform(uid)); return; } diff --git a/Content.Shared/Actions/ActionContainerSystem.cs b/Content.Shared/Actions/ActionContainerSystem.cs index 1c5a3ba0d93..1a83cf38e51 100644 --- a/Content.Shared/Actions/ActionContainerSystem.cs +++ b/Content.Shared/Actions/ActionContainerSystem.cs @@ -261,7 +261,7 @@ public void RemoveAction(EntityUid actionId, BaseActionComponent? action = null) if (action.Container == null) return; - _transform.DetachParentToNull(actionId, Transform(actionId)); + _transform.DetachEntity(actionId, Transform(actionId)); // Container removal events should have removed the action from the action container. // However, just in case the container was already deleted we will still manually clear the container field diff --git a/Content.Shared/Follower/FollowerSystem.cs b/Content.Shared/Follower/FollowerSystem.cs index 8027ee449c4..8c35617e2c3 100644 --- a/Content.Shared/Follower/FollowerSystem.cs +++ b/Content.Shared/Follower/FollowerSystem.cs @@ -227,7 +227,7 @@ public void StopFollowingEntity(EntityUid uid, EntityUid target, FollowedCompone if (_netMan.IsClient) { - _transform.DetachParentToNull(uid, xform); + _transform.DetachEntity(uid, xform); return; } diff --git a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs index 4a5894d8958..cd0863ec88d 100644 --- a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs +++ b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs @@ -244,7 +244,7 @@ public void DeleteVirtualItem(Entity item, EntityUid user) if (TerminatingOrDeleted(item)) return; - _transformSystem.DetachParentToNull(item, Transform(item)); + _transformSystem.DetachEntity(item, Transform(item)); if (_netManager.IsServer) QueueDel(item); } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs index adae26a223a..d6f45ba77df 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.ChamberMagazine.cs @@ -108,7 +108,7 @@ private void UseChambered(EntityUid uid, ChamberMagazineAmmoProviderComponent co else { // Similar to below just due to prediction. - TransformSystem.DetachParentToNull(chamberEnt.Value, Transform(chamberEnt.Value)); + TransformSystem.DetachEntity(chamberEnt.Value, Transform(chamberEnt.Value)); } } From 87c82ff114fa0b213b561cd7c93c0855e55cc359 Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Sat, 13 Jul 2024 04:50:21 -0700 Subject: [PATCH 59/78] Update SpawnAfterInteractSystem.cs to not use Component.Owner (#29968) * Update SpawnAfterInteractSystem.cs * what the fuck is that condition --------- Co-authored-by: plykiya --- .../Engineering/EntitySystems/SpawnAfterInteractSystem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs index 281bbc47211..8391e8faada 100644 --- a/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs +++ b/Content.Server/Engineering/EntitySystems/SpawnAfterInteractSystem.cs @@ -65,8 +65,8 @@ bool IsTileClear() EntityManager.SpawnEntity(component.Prototype, args.ClickLocation.SnapToGrid(grid)); - if (component.RemoveOnInteract && stackComp == null && !((!EntityManager.EntityExists(uid) ? EntityLifeStage.Deleted : EntityManager.GetComponent(component.Owner).EntityLifeStage) >= EntityLifeStage.Deleted)) - EntityManager.DeleteEntity(uid); + if (component.RemoveOnInteract && stackComp == null) + TryQueueDel(uid); } } } From 1b0c434a9fa12ebdeadc2ace8e3f7b6f1a39651f Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:15:57 +0300 Subject: [PATCH 60/78] Shiny Diamonds (#25750) * shiny * add * reresprite * rereresprite * Add files via upload * Update ore.yml --- .../Locale/en-US/materials/materials.ftl | 2 + .../Locale/en-US/salvage/salvage-magnet.ftl | 1 + .../Entities/Objects/Materials/materials.yml | 13 ++ .../Entities/Objects/Materials/ore.yml | 31 ++++ .../Entities/Structures/Machines/lathe.yml | 1 + .../Entities/Structures/Walls/asteroid.yml | 155 +++++++++++++++++- .../Entities/World/Debris/asteroids.yml | 3 + .../Prototypes/Procedural/Magnet/asteroid.yml | 1 + .../Procedural/biome_ore_templates.yml | 14 ++ .../Prototypes/Procedural/salvage_loot.yml | 7 + .../Prototypes/Reagents/Materials/ores.yml | 9 + Resources/Prototypes/Recipes/Lathes/sheet.yml | 7 + Resources/Prototypes/Stacks/Materials/ore.yml | 7 + Resources/Prototypes/ore.yml | 6 + .../materials.rsi/diamond-inhand-left.png | Bin 223 -> 236 bytes .../materials.rsi/diamond-inhand-right.png | Bin 231 -> 244 bytes .../Materials/materials.rsi/diamond.png | Bin 297 -> 255 bytes .../Materials/materials.rsi/diamond_2.png | Bin 0 -> 345 bytes .../Materials/materials.rsi/diamond_3.png | Bin 0 -> 419 bytes .../Objects/Materials/materials.rsi/meta.json | 6 + .../Objects/Materials/ore.rsi/diamond.png | Bin 0 -> 390 bytes .../Objects/Materials/ore.rsi/meta.json | 5 +- .../Structures/Walls/rock.rsi/meta.json | 18 +- .../Walls/rock.rsi/rock_diamond.png | Bin 1307 -> 1916 bytes 24 files changed, 283 insertions(+), 3 deletions(-) create mode 100644 Resources/Textures/Objects/Materials/materials.rsi/diamond_2.png create mode 100644 Resources/Textures/Objects/Materials/materials.rsi/diamond_3.png create mode 100644 Resources/Textures/Objects/Materials/ore.rsi/diamond.png diff --git a/Resources/Locale/en-US/materials/materials.ftl b/Resources/Locale/en-US/materials/materials.ftl index a354423d2b7..0fc716bda53 100644 --- a/Resources/Locale/en-US/materials/materials.ftl +++ b/Resources/Locale/en-US/materials/materials.ftl @@ -25,6 +25,7 @@ materials-meat = meat materials-web = silk materials-bones = bone materials-coal = coal +materials-diamond = diamond materials-gunpowder = gunpowder # Ores @@ -36,3 +37,4 @@ materials-raw-plasma = raw plasma materials-raw-uranium = raw uranium materials-raw-bananium = raw bananium materials-raw-salt = raw salt +materials-raw-diamond = raw diamond diff --git a/Resources/Locale/en-US/salvage/salvage-magnet.ftl b/Resources/Locale/en-US/salvage/salvage-magnet.ftl index 7ce2a486de9..c60bafcc138 100644 --- a/Resources/Locale/en-US/salvage/salvage-magnet.ftl +++ b/Resources/Locale/en-US/salvage/salvage-magnet.ftl @@ -13,6 +13,7 @@ salvage-magnet-resources = {$resource -> [OreQuartz] Quartz [OreSalt] Salt [OreGold] Gold + [OreDiamond] Diamond [OreSilver] Silver [OrePlasma] Plasma [OreUranium] Uranium diff --git a/Resources/Prototypes/Entities/Objects/Materials/materials.yml b/Resources/Prototypes/Entities/Objects/Materials/materials.yml index c4de4bb18f2..a3fc4a27745 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/materials.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/materials.yml @@ -350,8 +350,21 @@ components: - type: Stack stackType: Diamond + baseLayer: base + layerStates: + - diamond + - diamond_2 + - diamond_3 - type: Sprite state: diamond + layers: + - state: diamond + map: ["base"] + - type: StaticPrice + price: 0 + - type: StackPrice + price: 500 + - type: Appearance - type: Item heldPrefix: diamond - type: Extractable diff --git a/Resources/Prototypes/Entities/Objects/Materials/ore.yml b/Resources/Prototypes/Entities/Objects/Materials/ore.yml index bf7dbfad5a3..136d20cc81b 100644 --- a/Resources/Prototypes/Entities/Objects/Materials/ore.yml +++ b/Resources/Prototypes/Entities/Objects/Materials/ore.yml @@ -66,6 +66,37 @@ - type: Stack count: 1 +- type: entity + parent: OreBase + id: DiamondOre + name: diamond ore + suffix: Full + components: + - type: Stack + stackType: DiamondOre + - type: Sprite + state: diamond + - type: Material + - type: PhysicalComposition + materialComposition: + RawDiamond: 500 + - type: Extractable + grindableSolutionName: diamondore + - type: SolutionContainerManager + solutions: + diamondore: + reagents: + - ReagentId: Carbon + Quantity: 20 + +- type: entity + parent: DiamondOre + id: DiamondOre1 + suffix: Single + components: + - type: Stack + count: 1 + - type: entity parent: OreBase id: SteelOre diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 67cf6f767da..66f4f684e9a 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -1175,6 +1175,7 @@ - IngotGold30 - IngotSilver30 - MaterialBananium10 + - MaterialDiamond - type: entity parent: OreProcessor diff --git a/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml b/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml index e14bf26e0db..c0a30e76694 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml @@ -93,6 +93,28 @@ state: rock_asteroid_west - state: rock_gold +- type: entity + id: AsteroidRockDiamond + parent: AsteroidRock + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_asteroid + - map: [ "enum.EdgeLayer.South" ] + state: rock_asteroid_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_asteroid_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_asteroid_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_asteroid_west + - state: rock_diamond + - type: entity id: AsteroidRockPlasma parent: AsteroidRock @@ -693,6 +715,28 @@ state: rock_west - state: rock_gold +- type: entity + id: WallRockDiamond + parent: WallRock + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock + - map: [ "enum.EdgeLayer.South" ] + state: rock_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_west + - state: rock_diamond + - type: entity id: WallRockPlasma parent: WallRock @@ -993,6 +1037,28 @@ state: rock_wall_west - state: rock_gold +- type: entity + id: WallRockBasaltDiamond + parent: WallRockBasalt + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_wall + - map: [ "enum.EdgeLayer.South" ] + state: rock_wall_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_wall_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_wall_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_wall_west + - state: rock_diamond + - type: entity id: WallRockBasaltPlasma parent: WallRockBasalt @@ -1236,6 +1302,28 @@ state: rock_snow_west - state: rock_gold +- type: entity + id: WallRockSnowDiamond + parent: WallRockSnow + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_snow + - map: [ "enum.EdgeLayer.South" ] + state: rock_snow_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_snow_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_snow_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_snow_west + - state: rock_diamond + - type: entity id: WallRockSnowPlasma parent: WallRockSnow @@ -1479,6 +1567,28 @@ state: rock_sand_west - state: rock_gold +- type: entity + id: WallRockSandDiamond + parent: WallRockSand + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_sand + - map: [ "enum.EdgeLayer.South" ] + state: rock_sand_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_sand_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_sand_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_sand_west + - state: rock_diamond + - type: entity id: WallRockSandPlasma parent: WallRockSand @@ -1589,7 +1699,6 @@ state: rock_sand_west - state: rock_uranium - - type: entity id: WallRockSandBananium parent: WallRockSand @@ -1722,6 +1831,28 @@ state: rock_chromite_west - state: rock_gold +- type: entity + id: WallRockChromiteDiamond + parent: WallRockChromite + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_chromite + - map: [ "enum.EdgeLayer.South" ] + state: rock_chromite_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_chromite_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_chromite_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_chromite_west + - state: rock_diamond + - type: entity id: WallRockChromitePlasma parent: WallRockChromite @@ -1965,6 +2096,28 @@ state: rock_andesite_west - state: rock_gold +- type: entity + id: WallRockAndesiteDiamond + parent: WallRockAndesite + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: rock_andesite + - map: [ "enum.EdgeLayer.South" ] + state: rock_andesite_south + - map: [ "enum.EdgeLayer.East" ] + state: rock_andesite_east + - map: [ "enum.EdgeLayer.North" ] + state: rock_andesite_north + - map: [ "enum.EdgeLayer.West" ] + state: rock_andesite_west + - state: rock_diamond + - type: entity id: WallRockAndesitePlasma parent: WallRockAndesite diff --git a/Resources/Prototypes/Entities/World/Debris/asteroids.yml b/Resources/Prototypes/Entities/World/Debris/asteroids.yml index 061288d010b..2817b083bed 100644 --- a/Resources/Prototypes/Entities/World/Debris/asteroids.yml +++ b/Resources/Prototypes/Entities/World/Debris/asteroids.yml @@ -32,6 +32,9 @@ - id: WallRockGold prob: 0.05 orGroup: rock + - id: WallRockDiamond + prob: 0.005 + orGroup: rock - id: WallRockSilver prob: 0.05 orGroup: rock diff --git a/Resources/Prototypes/Procedural/Magnet/asteroid.yml b/Resources/Prototypes/Procedural/Magnet/asteroid.yml index c20b80af55b..c380dfbc7cf 100644 --- a/Resources/Prototypes/Procedural/Magnet/asteroid.yml +++ b/Resources/Prototypes/Procedural/Magnet/asteroid.yml @@ -6,6 +6,7 @@ OreCoal: 1.0 OreSalt: 1.0 OreGold: 0.25 + OreDiamond: 0.05 OreSilver: 0.25 OrePlasma: 0.15 OreUranium: 0.15 diff --git a/Resources/Prototypes/Procedural/biome_ore_templates.yml b/Resources/Prototypes/Procedural/biome_ore_templates.yml index 4a60427e305..a6e5fac2d84 100644 --- a/Resources/Prototypes/Procedural/biome_ore_templates.yml +++ b/Resources/Prototypes/Procedural/biome_ore_templates.yml @@ -130,6 +130,20 @@ maxGroupSize: 10 radius: 4 +- type: biomeMarkerLayer + id: OreDiamond + entityMask: + AsteroidRock: AsteroidRockDiamond + WallRock: WallRockDiamond + WallRockBasalt: WallRockBasaltDiamond + WallRockChromite: WallRockChromiteDiamond + WallRockSand: WallRockSandDiamond + WallRockSnow: WallRockSnowDiamond + maxCount: 6 + minGroupSize: 1 + maxGroupSize: 2 + radius: 4 + # Artifact Fragment - type: biomeMarkerLayer id: OreArtifactFragment diff --git a/Resources/Prototypes/Procedural/salvage_loot.yml b/Resources/Prototypes/Procedural/salvage_loot.yml index e8783760ddc..da99da7c75b 100644 --- a/Resources/Prototypes/Procedural/salvage_loot.yml +++ b/Resources/Prototypes/Procedural/salvage_loot.yml @@ -178,6 +178,13 @@ - !type:BiomeMarkerLoot proto: OreBananium +- type: salvageLoot + id: OreDiamond + guaranteed: true + loots: + - !type:BiomeMarkerLoot + proto: OreDiamond + - type: salvageLoot id: OreArtifactFragment guaranteed: true diff --git a/Resources/Prototypes/Reagents/Materials/ores.yml b/Resources/Prototypes/Reagents/Materials/ores.yml index 18f1d9ebb3a..1555ab9e234 100644 --- a/Resources/Prototypes/Reagents/Materials/ores.yml +++ b/Resources/Prototypes/Reagents/Materials/ores.yml @@ -24,6 +24,15 @@ color: "#FFD700" price: 0.2 +- type: material + id: RawDiamond + stackEntity: DiamondOre1 + name: materials-raw-diamond + unit: materials-unit-piece + icon: { sprite: Objects/Materials/ore.rsi, state: diamond } + color: "#C9D8F2" + price: 0.5 + - type: material id: RawSilver stackEntity: SilverOre1 diff --git a/Resources/Prototypes/Recipes/Lathes/sheet.yml b/Resources/Prototypes/Recipes/Lathes/sheet.yml index 053715a1811..3efaac95a72 100644 --- a/Resources/Prototypes/Recipes/Lathes/sheet.yml +++ b/Resources/Prototypes/Recipes/Lathes/sheet.yml @@ -127,6 +127,13 @@ materials: RawBananium: 3000 +- type: latheRecipe + id: MaterialDiamond + result: MaterialDiamond1 + completetime: 3 + materials: + RawDiamond: 1000 + - type: latheRecipe id: SheetUranium1 result: SheetUranium1 diff --git a/Resources/Prototypes/Stacks/Materials/ore.yml b/Resources/Prototypes/Stacks/Materials/ore.yml index 51254b5a7a6..3aaa04601aa 100644 --- a/Resources/Prototypes/Stacks/Materials/ore.yml +++ b/Resources/Prototypes/Stacks/Materials/ore.yml @@ -4,6 +4,13 @@ icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: gold } spawn: GoldOre1 maxCount: 30 + +- type: stack + id: DiamondOre + name: rough diamond + icon: { sprite: /Textures/Objects/Materials/ore.rsi, state: diamond } + spawn: DiamondOre1 + maxCount: 30 - type: stack id: SteelOre diff --git a/Resources/Prototypes/ore.yml b/Resources/Prototypes/ore.yml index 84d1c667369..146f04b49c5 100644 --- a/Resources/Prototypes/ore.yml +++ b/Resources/Prototypes/ore.yml @@ -58,6 +58,12 @@ minOreYield: 1 maxOreYield: 3 +- type: ore + id: OreDiamond + oreEntity: DiamondOre1 + minOreYield: 1 + maxOreYield: 2 + - type: ore id: OreQuartzCrab oreEntity: MobSpawnCrabQuartz diff --git a/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-left.png b/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-left.png index c9b55e9daa52ea2f352f43917ffa03611e0c5512..9eae45d039abc1b118d047edae3ca52f3b28bb1f 100644 GIT binary patch delta 59 zcmcc5_=a(U3KwH>kh>GZx^prwCn}2ZTki6(^2$hD_K{2WaO4v0iEeGe?YVjOf1)Nc O0D-5gpUXO@geCxoViiyT delta 48 zcmaFEc%N~C%0xq9ep@RKE3b^iWgod@4@)jFo*3LFoS&QbXX>LG1|aZs^>bP0l+XkK DuM!c9 diff --git a/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-right.png b/Resources/Textures/Objects/Materials/materials.rsi/diamond-inhand-right.png index 295c2c4eca1dddeb5fb91200177af77b78a76710..3353eb6f7caea4fa57c518ff3ecf7cf12666714b 100644 GIT binary patch delta 198 zcmaFP_=RzT3KwH>kh>GZx^prwCn}27%jgKX6`nSk64_`t$2CGx_imuV5|LvZ4?QX` z{tw;Oy}bD0DRo8$2#9mredSv8x9n$Eq`pjOdwMf)mY(L^|0lNZTK)FvzvySq*%8x9 zrmJ~cc^+Qtd|+#8=^2|_soQLC_kCg6l_vLA=;^0?L1Sj9Squ!(!W)e*7=GF)A0WKd u_`;<(>{n8bzUC{8E3e3uG-+TMTV zR(fX6Tcg|aa^0(#VvW;xyX^cEeB=z&ECz-H*IeH4Y%$#PhW(1mTb?Z@zZp#zKPqM0 k)pbMiMc@B&h6D9Gn3pWeUD_Nmi4mm9)78&qol`;+04q0CTL1t6 diff --git a/Resources/Textures/Objects/Materials/materials.rsi/diamond.png b/Resources/Textures/Objects/Materials/materials.rsi/diamond.png index 5eb8aabf8796f2a3b5ffce29119ea1b73dbdbb9b..8b39437d0ac358f85f04f69f9a743c0325c6dbc9 100644 GIT binary patch delta 239 zcmV+v2VHk$7F-7iz zT59IzT#MrJ6M&|NsAn|Nr~{|9}7g|C#^)&;S2_|NsB_|Nj^K|L<^4gdeDmrzFTP znBl;G5CD@C)eo!&inn>XIEGZrNlvJjxYH0A5FpU1pOTZ)qN5}8v;Z{M6MdNaCi9S$K>qUP^%Twzu?oMZ=a4G?J8i*bPP;Q4t31j!H_&b>4K~m zhqGi$x`cGJk6Xm)0~uj)acvt|max1qU`%0jGY(;OP@T*gz$zhbw1APZgz*;>LyK^% W=;V3o>MMY5V(@hJb6Mw<&;$V4^l+vC diff --git a/Resources/Textures/Objects/Materials/materials.rsi/diamond_2.png b/Resources/Textures/Objects/Materials/materials.rsi/diamond_2.png new file mode 100644 index 0000000000000000000000000000000000000000..410d83f1c29f1e958fb38d3c0594d059846ffe0e GIT binary patch literal 345 zcmV-f0jBPx$6G=otR9J=Wlrc`jFcgNr07481b?b)8#)fYY#Kcv&1-Hm;Ah|&V3tL&cv_k6s zGuTE}sFY_y>45(wvJ>n3{GMYdDJl8yseZb!k=n6u9;)hgF9MC!uAr`g#g6yyKnKjB z8LgbnrVCF8^vQf2ci&b_5E$&@<_b6m*1%JkycPx$T}ebiR9J=Wls!(vKoo?(MF^#!h@hZD$pyeCz!nu(am5w5h3`Q{1@;XhXwoHZ znvfz5)lhg%R>t<)`B92^Bx}9veV*BQ?=AG`(c{!%whc_A)VC)#COU|KL`oMBhQO%e z`y9xC8_0Yj&UPUWELoX60775^q0g!Ft^x{Vb_{@tvtG=ze-T>>s5rs>7`OzMz&r3* z+7LKGrn?Aeo9i`j>yS_EGiXr*wF1hyo|Ht)$sHI2E8qvX243xakPlvXQ&5-dCGbUF zY!4N2a~Ni}2%B7J?1B(@w&aC~phUiFC)7F+8xz^Ua0Cq5IIys(5(|%-Gs3*(VKO2MgDRD+0!h&1O&fMX zBBd{q+@eaPbS7yg>08o=r1GA*gdL6C6>&839zA+&i(e?myS|8_8-D-* N002ovPDHLkV1hl2wF>|M literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Materials/materials.rsi/meta.json b/Resources/Textures/Objects/Materials/materials.rsi/meta.json index 78f497c0cda..3ae12dc14c0 100644 --- a/Resources/Textures/Objects/Materials/materials.rsi/meta.json +++ b/Resources/Textures/Objects/Materials/materials.rsi/meta.json @@ -78,6 +78,12 @@ { "name": "diamond" }, + { + "name": "diamond_2" + }, + { + "name": "diamond_3" + }, { "name": "diamond-inhand-left", "directions": 4 diff --git a/Resources/Textures/Objects/Materials/ore.rsi/diamond.png b/Resources/Textures/Objects/Materials/ore.rsi/diamond.png new file mode 100644 index 0000000000000000000000000000000000000000..b2a3c788d34c3190e6ff143d2b0cc324597bad94 GIT binary patch literal 390 zcmV;10eSw3P)Px$KuJVFR9J=Wl(9<0KoExih!1epam8)g%X$!NjkqZ+?Cllod<9=5*XJb!3wK2X zOD(eHL8=gtq_7{G+jTt1Og){h-5(|$7t;P6ifNlOA8?n z08s(T&Aa4z{;TGmj{z-&)I!*9F@SSGxVi0`oly`;MMNDD@ie2OvkT&BW@E+@x_@{f zQ#W)qUMq+J93P%wGMxbcCezus`1%1z_GZSeg1WK9_Qw!wU{#2uhKJPb!l3C0pyZ|(wP3GpU2GIZ9Ke6)0cT606jQbG zQRE{KAT|iZ_@G97B0gzCOpGRgkw7Q`%SZ6l2Oq$|gAhqffCr;S6MPb5jF`ZKsjL#K zk#Cfb3YCwP%k_G_?%nR*+}_MDxomE)z2EH2?0?MO&VOdXVzF4_!(I`w zev8E~n>stS))>?M7E6Wj>dBJ@pGsTq+zIXU)8!X&k|Iv(HRq&=I4Kb)8Kysxyet=sIk%a_htLt10Dw?fk9v(kytiZ3cu@~3`_&6fL@>-=o%{x ze*J+nIv?A*yMN`Qnwp{=K{6nyKf}EkSPHBL4g|!tc3PqiTMl{a#horGctC_x)P;^Mccm?e|>(ue^|AZ*A?c zf8Koi_NWE2p`#;cVGW|%`~yH&ZG9Clr*549_5lxo!3h1H`!K56>60Z{RYe>jR!gH9KA(@G;OzNPR%r761%oL^Z0-ACIc} z{}N&5XSuzLfF-DA=OA#3kTM_2ya1s1-vJ@Ii@pplDeG_C(0bD;O23Gc@Lpm2L%R&^ z>FMF!_I7QB5T!qnSSH>}hu>0;e64w8$SD68B7gE}d-1{@>2#pP8smTC^y!>`_@jud zY;E=RCA$LD4AcFVxbpsqeyyc$wEvx}SG69z>3&}v)Z8wb?zdPf2;G-iru!`xi^XCv zy5r+b_gmtI?)Z4q{T54wVCwvUlKb~$_43{X1revZkO>M$MdYh)ku{eu7nF!>f3d{X zKYt82^Xo*M#UjpfcU$MT5Vm!9YwdvI;2$;6NN0S$N#qO6(ixv08L{!ZZT@l*r&Ywc z^!tD3jc><|mY!Ki?UkGd_O=8s;aI#P6pMD0zIoN#qSr*BPI0 z3S-U>&>5d^DS%3z9}uPA&JU;%zopa|6@Q3&Px&>bIdy5C}{ z5R8e>*X`FGA8)!pRN@eyU&($;ITD-r{BrYGYJ7fN{QQukb1G$gzUh98#bPOE;(r>S zuiGEm{D5>%59{08rO+AmVWB%dzgT}Lk1&bVqdv-vqR#*?K0a$k{fodS?sfq%IjS?E zQsxKvDu7(^@v9y_aMPPnzXaG0%yhRyPn(M(K7Z9H4;&2?pC5uh7y10d3ShMO_{V`G z*M1r8JG`gPtpz6DU}8Nmk&LI!?SHDS_LbNo{X>X2vYVwN3ZYbf$Q`(q&mXuC5OGq# zJXEu@9hLb#z;56;a6XY()*sYUKzbyRSXTCr=kf^SDrn$?pBMdsdcfIk;EKCFh3be0 zLU80IGd^EO=4+oH5OiIkp#G>GDTPziX?)Z4q{T7SGVt+9h-SP32 z=8w%ilR}8oGHaoYh-KpbDxM(XRBh?(lv0k4WsFbE0~URFcSau(j!TS|2Jf!GSjQjw z8Q{vQP%G_D0PX`ff!?TVVb2^Ygf0rd%Q#e_&jhx9(_6n1weI#zMpjIFg?|*krBl;UwQ^{L zE<%42)$BC?#f%m6k`ws+)}$4v3NSvGdqe@W6foAAgF#fAzwsIVIeU%));xCaEU?kt zKW6(I(BF${^V3gq`&w7xKF?P7Ujx$a{@$_D5W?|40QN29>amM5z?)O=9V27C?d*RD zwG+H}uFY1oGvCCs_rcBq}O z`{iCt&}HCtcRQ5%5y$6up)$D@*n_(I*h5CqBO9i3(GRt(oC1m~wF?fKdtyPz}00000NkvXXu0mjfAd}HG literal 1307 zcmeAS@N?(olHy`uVBq!ia0vp^2|(Py!3HG1+{xJmq&N#aB8wRq_>O=u<5X=vX$A(C zvz{)FAr*7p&OPWI;wW=GUD3cNXrkAIv(0isO?=N(KP-`3Rb28ig`>11DCm>EfXS8@ z0RoaC0vC7gdanCfOybGWGXYK^1*-m+`wV}TeLs1wI{#Vi{p#O;n>Os;@q6aa+WY6; zoI5w&QcYt;21A4i!V>*DHOyyPq~y?guCr2bj6*2FYMJ`I;Jh@JD)y+&=;f)D*iuiw<< z*tmC(PW-m*()Qc0{SamAiJ0|ty-57?f9L!boKv5v|1^B&=~$CG-QazfEgvL0H*e)M z$Y9L*Bx>y=?ek-!{~_~f)1J&ecg~@eGnzrV=Ipn7{g>K`_HAV3xBEY}TKwA)pXB5v zOf98VB3#b_O+kfmzYiY+{^po^;%)(osU)h_wC-PDQ|B6+w94=U%P6m zp7}gcbSMoejrjQcsQJUUJF@qst-d{Lz5Mp0=4J^8(u=IP)}<`|5w%O;X?Uzm&DnS2 z-0a_TKLvkw)p>P8erj&=zmLILJPxO?vL>8uh|bRb6q&94M$1?G{w&5;Cr6&O>rOq- z&7GjMWNQA|hU>Dj7ebG3`Q$A9A31>g2Cq z7kbYc?LX4+y1V;RYKmAH8;@ndi4EY+#ap@ZC_`FSS?xMuXvp+m_iie2xhh88!e@!5Q|3pU8Ml%fTc&hBN6< zY%bR{;rHjmTD&#N%s@u2cIKZ|Yzp6>SGS&0-|;Nmlwqg+eeDptgQoX0IGo;{zwY!* zIObEYdg7CRe{{Ocp18XG8oK9#*+c1Zs zCoNne;CXL;{^b4Vo#x6f_ILaHq*M9azK&3iBEK44-F0vGH8~ANvg=4*z60a%#o&xh-ZP$=0XY zHGX_3TfUWv<>Bi`+W&kWPnv4Bq;dA(F^(@tY?vJ_x`KxT% z#SS;zrr3*J|MzDxmq5DRfhGTEo@4(Z+VHbs!mDIEh1tJ9U#wZz*5t~3068r@|H+tO zcOK~U$~s_td Date: Sat, 13 Jul 2024 12:17:05 +0000 Subject: [PATCH 61/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index ca549b51fb3..e4c8aa1b430 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,18 +1,4 @@ Entries: -- author: FairlySadPanda - changes: - - message: Xenoarchaeology Traversal Distorters have been rolled into the Artifact - Analyzers to make artifact gameplay a bit more controlled. - type: Tweak - - message: The T2 Abnormal Artifact Manipulation tech has been made cheaper to compensate - for losing an unlock. - type: Tweak - - message: The Xenoarchaeology guidebook entry has been rewritten to be more useful - and comprehensive for newbies. - type: Tweak - id: 6415 - time: '2024-04-21T16:09:26.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/26545 - author: deltanedas changes: - message: Flaming mice no longer completely engulf people they touch. @@ -3818,3 +3804,12 @@ id: 6914 time: '2024-07-13T06:09:19.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/29550 +- author: TheShuEd + changes: + - message: Added diamonds ore! + type: Add + - message: Diamonds can now be sold at a bargain price. + type: Add + id: 6915 + time: '2024-07-13T12:15:57.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/25750 From dcb3443b352ac6004886ab6eedf3a0fe3388b707 Mon Sep 17 00:00:00 2001 From: Ed <96445749+TheShuEd@users.noreply.github.com> Date: Sat, 13 Jul 2024 15:45:35 +0300 Subject: [PATCH 62/78] Vgroid diamonds hotfix (#29999) vgroid diamonds --- .../Entities/Structures/Walls/asteroid.yml | 22 +++++++++++++++++++ Resources/Prototypes/Procedural/vgroid.yml | 6 +++++ 2 files changed, 28 insertions(+) diff --git a/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml b/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml index c0a30e76694..4673405e03f 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/asteroid.yml @@ -611,6 +611,28 @@ state: ironrock_west - state: rock_artifact_fragment +- type: entity + id: IronRockDiamond + parent: IronRock + description: An ore vein rich with diamonds. + suffix: Diamond + components: + - type: OreVein + oreChance: 1.0 + currentOre: OreDiamond + - type: Sprite + layers: + - state: ironrock + - map: [ "enum.EdgeLayer.South" ] + state: ironrock_south + - map: [ "enum.EdgeLayer.East" ] + state: ironrock_east + - map: [ "enum.EdgeLayer.North" ] + state: ironrock_north + - map: [ "enum.EdgeLayer.West" ] + state: ironrock_west + - state: rock_diamond + # Rocks and ore veins - type: entity id: WallRock diff --git a/Resources/Prototypes/Procedural/vgroid.yml b/Resources/Prototypes/Procedural/vgroid.yml index 49e956e73f5..6e9fc6f3957 100644 --- a/Resources/Prototypes/Procedural/vgroid.yml +++ b/Resources/Prototypes/Procedural/vgroid.yml @@ -82,6 +82,12 @@ count: 50 minGroupSize: 2 maxGroupSize: 4 + - !type:OreDunGen + replacement: IronRock + entity: IronRockDiamond + count: 15 + minGroupSize: 1 + maxGroupSize: 2 # Configs - type: dungeonConfig From bf4ef62ab3a5be3ecd8aa2ff259622070c8bca32 Mon Sep 17 00:00:00 2001 From: Winkarst <74284083+Winkarst-cpu@users.noreply.github.com> Date: Sat, 13 Jul 2024 19:45:34 +0300 Subject: [PATCH 63/78] Update RadiationPulseOverlay.cs to have 0 warnings (#30004) --- Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs index 8d5607af2d0..9ec24fae0ef 100644 --- a/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs +++ b/Content.Client/Radiation/Overlays/RadiationPulseOverlay.cs @@ -116,7 +116,9 @@ private void RadiationQuery(IEye? currentEye) var shaderInstance = _pulses[pulseEntity]; shaderInstance.instance.CurrentMapCoords = _transform.GetMapCoordinates(pulseEntity); shaderInstance.instance.Range = pulse.VisualRange; - } else { + } + else + { _pulses[pulseEntity].shd.Dispose(); _pulses.Remove(pulseEntity); } @@ -129,7 +131,7 @@ private bool PulseQualifies(EntityUid pulseEntity, MapCoordinates currentEyeLoc) var transformComponent = _entityManager.GetComponent(pulseEntity); var transformSystem = _entityManager.System(); return transformComponent.MapID == currentEyeLoc.MapId - && transformComponent.Coordinates.InRange(_entityManager, transformSystem, EntityCoordinates.FromMap(transformComponent.ParentUid, currentEyeLoc, transformSystem, _entityManager), MaxDist); + && transformSystem.InRange(transformComponent.Coordinates, transformSystem.ToCoordinates(transformComponent.ParentUid, currentEyeLoc), MaxDist); } private sealed record RadiationShaderInstance(MapCoordinates CurrentMapCoords, float Range, TimeSpan Start, float Duration) From 5e800e0ece7beadeb06bba901dd573e84fa4c133 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 13 Jul 2024 19:34:17 +0200 Subject: [PATCH 64/78] New CDN publish workflow (#30009) * LET'S SEE IF THIS WORKS * I forgot the chmod +x * I forgot the shebang --- .github/workflows/publish.yml | 42 +++++++------- SpaceStation14.sln | 1 - Tools/gen_build_info.py | 96 -------------------------------- Tools/publish_github_artifact.py | 56 +++++++++++++++++++ 4 files changed, 75 insertions(+), 120 deletions(-) delete mode 100755 Tools/gen_build_info.py create mode 100755 Tools/publish_github_artifact.py diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4e72256cb87..16cb5017d6a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -41,31 +41,22 @@ jobs: - name: Package client run: dotnet run --project Content.Packaging client --no-wipe-release - - name: Update Build Info - run: Tools/gen_build_info.py - - - name: Shuffle files around - run: | - mkdir "release/${{ github.sha }}" - mv release/*.zip "release/${{ github.sha }}" - - - name: Upload files to centcomm - uses: appleboy/scp-action@master + - name: Upload build artifact + id: artifact-upload-step + uses: actions/upload-artifact@v4 with: - host: centcomm.spacestation14.io - username: wizards-build-push - key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }} - source: "release/${{ github.sha }}" - target: "/home/wizards-build-push/builds_dir/builds/" - strip_components: 1 + name: build + path: release/*.zip + compression-level: 0 + retention-days: 0 - - name: Update manifest JSON - uses: appleboy/ssh-action@master - with: - host: centcomm.spacestation14.io - username: wizards-build-push - key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }} - script: /home/wizards-build-push/push.ps1 ${{ github.sha }} + - name: Publish version + run: Tools/publish_github_artifact.py + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} + ARTIFACT_ID: ${{ steps.artifact-upload-step.outputs.artifact-id }} + GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }} - name: Publish changelog (Discord) run: Tools/actions_changelogs_since_last_run.py @@ -77,3 +68,8 @@ jobs: run: Tools/actions_changelog_rss.py env: CHANGELOG_RSS_KEY: ${{ secrets.CHANGELOG_RSS_KEY }} + + - uses: geekyeggo/delete-artifact@v5 + if: always() + with: + name: build diff --git a/SpaceStation14.sln b/SpaceStation14.sln index e0cb455a6db..bcd013b5981 100644 --- a/SpaceStation14.sln +++ b/SpaceStation14.sln @@ -62,7 +62,6 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{806ED41A-411B-4B3B-BEB6-DEC6DCA4C205}" ProjectSection(SolutionItems) = preProject Tools\generate_hashes.ps1 = Tools\generate_hashes.ps1 - Tools\gen_build_info.py = Tools\gen_build_info.py EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Robust.Shared.Scripting", "RobustToolbox\Robust.Shared.Scripting\Robust.Shared.Scripting.csproj", "{41B450C0-A361-4CD7-8121-7072B8995CFC}" diff --git a/Tools/gen_build_info.py b/Tools/gen_build_info.py deleted file mode 100755 index 0207f568dd7..00000000000 --- a/Tools/gen_build_info.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python3 - -# Generates build info and injects it into the server zip files. - -import codecs -import hashlib -import io -import json -import os -import subprocess -from zipfile import ZipFile, ZIP_DEFLATED - -FILE = "SS14.Client.zip" - -SERVER_FILES = [ - "SS14.Server_linux-x64.zip", - "SS14.Server_linux-arm64.zip", - "SS14.Server_win-x64.zip", - "SS14.Server_osx-x64.zip" -] - -VERSION = os.environ['GITHUB_SHA'] -FORK_ID = "wizards" -BUILD_URL = f"https://cdn.centcomm.spacestation14.com/builds/wizards/builds/{{FORK_VERSION}}/{FILE}" -MANIFEST_URL = f"https://cdn.centcomm.spacestation14.com/cdn/version/{{FORK_VERSION}}/manifest" -MANIFEST_DOWNLOAD_URL = f"https://cdn.centcomm.spacestation14.com/cdn/version/{{FORK_VERSION}}/download" - -def main() -> None: - client_file = os.path.join("release", FILE) - manifest = generate_build_json(client_file) - - for server in SERVER_FILES: - inject_manifest(os.path.join("release", server), manifest) - - -def inject_manifest(zip_path: str, manifest: str) -> None: - with ZipFile(zip_path, "a", compression=ZIP_DEFLATED) as z: - z.writestr("build.json", manifest) - - -def generate_build_json(file: str) -> str: - # Env variables set by Jenkins. - - hash = sha256_file(file) - engine_version = get_engine_version() - manifest_hash = generate_manifest_hash(file) - - return json.dumps({ - "download": BUILD_URL, - "hash": hash, - "version": VERSION, - "fork_id": FORK_ID, - "engine_version": engine_version, - "manifest_url": MANIFEST_URL, - "manifest_download_url": MANIFEST_DOWNLOAD_URL, - "manifest_hash": manifest_hash - }) - -def generate_manifest_hash(file: str) -> str: - zip = ZipFile(file) - infos = zip.infolist() - infos.sort(key=lambda i: i.filename) - - bytesIO = io.BytesIO() - writer = codecs.getwriter("UTF-8")(bytesIO) - writer.write("Robust Content Manifest 1\n") - - for info in infos: - if info.filename[-1] == "/": - continue - - bytes = zip.read(info) - hash = hashlib.blake2b(bytes, digest_size=32).hexdigest().upper() - writer.write(f"{hash} {info.filename}\n") - - manifestHash = hashlib.blake2b(bytesIO.getbuffer(), digest_size=32) - - return manifestHash.hexdigest().upper() - -def get_engine_version() -> str: - proc = subprocess.run(["git", "describe","--tags", "--abbrev=0"], stdout=subprocess.PIPE, cwd="RobustToolbox", check=True, encoding="UTF-8") - tag = proc.stdout.strip() - assert tag.startswith("v") - return tag[1:] # Cut off v prefix. - - -def sha256_file(path: str) -> str: - with open(path, "rb") as f: - h = hashlib.sha256() - for b in iter(lambda: f.read(4096), b""): - h.update(b) - - return h.hexdigest() - -if __name__ == '__main__': - main() diff --git a/Tools/publish_github_artifact.py b/Tools/publish_github_artifact.py new file mode 100755 index 00000000000..b488ccdf2ec --- /dev/null +++ b/Tools/publish_github_artifact.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +import requests +import os +import subprocess + +GITHUB_TOKEN = os.environ["GITHUB_TOKEN"] +PUBLISH_TOKEN = os.environ["PUBLISH_TOKEN"] +ARTIFACT_ID = os.environ["ARTIFACT_ID"] +GITHUB_REPOSITORY = os.environ["GITHUB_REPOSITORY"] +VERSION = os.environ['GITHUB_SHA'] + +# +# CONFIGURATION PARAMETERS +# Forks should change these to publish to their own infrastructure. +# +ROBUST_CDN_URL = "https://wizards.cdn.spacestation14.com/" +FORK_ID = "wizards" + +def main(): + print("Fetching artifact URL from API...") + artifact_url = get_artifact_url() + print(f"Artifact URL is {artifact_url}, publishing to Robust.Cdn") + + data = { + "version": VERSION, + "engineVersion": get_engine_version(), + "archive": artifact_url + } + headers = { + "Authorization": f"Bearer {PUBLISH_TOKEN}", + "Content-Type": "application/json" + } + resp = requests.post(f"{ROBUST_CDN_URL}fork/{FORK_ID}/publish", json=data, headers=headers) + resp.raise_for_status() + print("Publish succeeded!") + +def get_artifact_url() -> str: + headers = { + "Authorization": f"Bearer {GITHUB_TOKEN}", + "X-GitHub-Api-Version": "2022-11-28" + } + resp = requests.get(f"https://api.github.com/repos/{GITHUB_REPOSITORY}/actions/artifacts/{ARTIFACT_ID}/zip", allow_redirects=False, headers=headers) + resp.raise_for_status() + + return resp.headers["Location"] + +def get_engine_version() -> str: + proc = subprocess.run(["git", "describe","--tags", "--abbrev=0"], stdout=subprocess.PIPE, cwd="RobustToolbox", check=True, encoding="UTF-8") + tag = proc.stdout.strip() + assert tag.startswith("v") + return tag[1:] # Cut off v prefix. + + +if __name__ == '__main__': + main() From c1915c9d73bb19e29fc0034b24e34fd776643fe2 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 13 Jul 2024 22:36:20 +0200 Subject: [PATCH 65/78] Remove outdated steps from Test Packaging workflow (#30018) gen build info was removed in 5e800e0ece7beadeb06bba901dd573e84fa4c133 but I didn't realize this workflow also tested it. Gone now. --- .github/workflows/test-packaging.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/workflows/test-packaging.yml b/.github/workflows/test-packaging.yml index 2dce502697d..2d002f869a1 100644 --- a/.github/workflows/test-packaging.yml +++ b/.github/workflows/test-packaging.yml @@ -64,11 +64,3 @@ jobs: - name: Package client run: dotnet run --project Content.Packaging client --no-wipe-release - - - name: Update Build Info - run: Tools/gen_build_info.py - - - name: Shuffle files around - run: | - mkdir "release/${{ github.sha }}" - mv release/*.zip "release/${{ github.sha }}" From a03b88979e25a9778a30f30612790f6966369fca Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Sat, 13 Jul 2024 14:05:22 -0700 Subject: [PATCH 66/78] Replace obsolete xform.ToMap() with xformSystem.ToMapCoordinates() (#30010) * Get rid of a bunch of obsolete usages * position --------- Co-authored-by: plykiya --- .../Construction/ConstructionSystem.cs | 6 +++--- .../ContextMenu/UI/EntityMenuUIController.cs | 2 +- Content.Client/Gameplay/GameplayStateBase.cs | 3 ++- .../Movement/Systems/JetpackSystem.cs | 5 +++-- Content.Client/NPC/PathfindingSystem.cs | 18 +++++++++--------- Content.Client/Physics/JointVisualsOverlay.cs | 4 ++-- Content.Client/Pinpointer/UI/NavMapControl.cs | 14 +++++++------- Content.Client/RCD/AlignRCDConstruction.cs | 6 +++--- Content.Client/Sandbox/SandboxSystem.cs | 6 ++++-- .../Shuttles/Systems/ShuttleSystem.Console.cs | 4 ++-- .../Shuttles/UI/ShuttleMapControl.xaml.cs | 2 +- .../Storage/Systems/StorageSystem.cs | 4 ++-- .../Weapons/Melee/MeleeWeaponSystem.cs | 2 +- .../Weapons/Ranged/Systems/GunSystem.cs | 6 +++--- .../Pointing/EntitySystems/PointingSystem.cs | 5 ++--- Content.Shared/Actions/SharedActionsSystem.cs | 2 +- 16 files changed, 46 insertions(+), 43 deletions(-) diff --git a/Content.Client/Construction/ConstructionSystem.cs b/Content.Client/Construction/ConstructionSystem.cs index 453658bebf9..889c992f7f6 100644 --- a/Content.Client/Construction/ConstructionSystem.cs +++ b/Content.Client/Construction/ConstructionSystem.cs @@ -48,11 +48,11 @@ public override void Initialize() CommandBinds.Builder .Bind(ContentKeyFunctions.OpenCraftingMenu, - new PointerInputCmdHandler(HandleOpenCraftingMenu, outsidePrediction:true)) + new PointerInputCmdHandler(HandleOpenCraftingMenu, outsidePrediction: true)) .Bind(EngineKeyFunctions.Use, new PointerInputCmdHandler(HandleUse, outsidePrediction: true)) .Bind(ContentKeyFunctions.EditorFlipObject, - new PointerInputCmdHandler(HandleFlip, outsidePrediction:true)) + new PointerInputCmdHandler(HandleFlip, outsidePrediction: true)) .Register(); SubscribeLocalEvent(HandleConstructionGhostExamined); @@ -196,7 +196,7 @@ public bool TrySpawnGhost( if (GhostPresent(loc)) return false; - var predicate = GetPredicate(prototype.CanBuildInImpassable, loc.ToMap(EntityManager, _transformSystem)); + var predicate = GetPredicate(prototype.CanBuildInImpassable, _transformSystem.ToMapCoordinates(loc)); if (!_examineSystem.InRangeUnOccluded(user, loc, 20f, predicate: predicate)) return false; diff --git a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs index a60619baa35..0462c095ba8 100644 --- a/Content.Client/ContextMenu/UI/EntityMenuUIController.cs +++ b/Content.Client/ContextMenu/UI/EntityMenuUIController.cs @@ -170,7 +170,7 @@ private bool HandleOpenEntityMenu(in PointerInputCmdHandler.PointerInputCmdArgs if (_combatMode.IsInCombatMode(args.Session?.AttachedEntity)) return false; - var coords = args.Coordinates.ToMap(_entityManager, _xform); + var coords = _xform.ToMapCoordinates(args.Coordinates); if (_verbSystem.TryGetEntityMenuEntities(coords, out var entities)) OpenRootMenu(entities); diff --git a/Content.Client/Gameplay/GameplayStateBase.cs b/Content.Client/Gameplay/GameplayStateBase.cs index 6236cd8e958..63cbfdb09c6 100644 --- a/Content.Client/Gameplay/GameplayStateBase.cs +++ b/Content.Client/Gameplay/GameplayStateBase.cs @@ -104,7 +104,8 @@ private bool HandleInspect(ICommonSession? session, EntityCoordinates coords, En public IEnumerable GetClickableEntities(EntityCoordinates coordinates) { - return GetClickableEntities(coordinates.ToMap(_entityManager, _entitySystemManager.GetEntitySystem())); + var transformSystem = _entitySystemManager.GetEntitySystem(); + return GetClickableEntities(transformSystem.ToMapCoordinates(coordinates)); } public IEnumerable GetClickableEntities(MapCoordinates coordinates) diff --git a/Content.Client/Movement/Systems/JetpackSystem.cs b/Content.Client/Movement/Systems/JetpackSystem.cs index b7f5e48821f..e25300d44c5 100644 --- a/Content.Client/Movement/Systems/JetpackSystem.cs +++ b/Content.Client/Movement/Systems/JetpackSystem.cs @@ -15,6 +15,7 @@ public sealed class JetpackSystem : SharedJetpackSystem [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ClothingSystem _clothing = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; public override void Initialize() { @@ -73,11 +74,11 @@ private void CreateParticles(EntityUid uid) var uidXform = Transform(uid); var coordinates = uidXform.Coordinates; - var gridUid = coordinates.GetGridUid(EntityManager); + var gridUid = _transform.GetGrid(coordinates); if (TryComp(gridUid, out var grid)) { - coordinates = new EntityCoordinates(gridUid.Value, grid.WorldToLocal(coordinates.ToMapPos(EntityManager, _transform))); + coordinates = new EntityCoordinates(gridUid.Value, _mapSystem.WorldToLocal(gridUid.Value, grid, _transform.ToMapCoordinates(coordinates).Position)); } else if (uidXform.MapUid != null) { diff --git a/Content.Client/NPC/PathfindingSystem.cs b/Content.Client/NPC/PathfindingSystem.cs index d3ae5091528..0c72a8f99ff 100644 --- a/Content.Client/NPC/PathfindingSystem.cs +++ b/Content.Client/NPC/PathfindingSystem.cs @@ -203,7 +203,7 @@ private void DrawScreen(OverlayDrawArgs args, DrawingHandleScreen screenHandle) if (found || !_system.Breadcrumbs.TryGetValue(netGrid, out var crumbs) || !xformQuery.TryGetComponent(grid, out var gridXform)) continue; - var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); + var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform); var localAABB = invWorldMatrix.TransformBox(aabb.Enlarged(float.Epsilon - SharedPathfindingSystem.ChunkSize)); foreach (var chunk in crumbs) @@ -287,7 +287,7 @@ private void DrawScreen(OverlayDrawArgs args, DrawingHandleScreen screenHandle) return; } - var invGridMatrix = gridXform.InvWorldMatrix; + var invGridMatrix = _transformSystem.GetInvWorldMatrix(gridXform); DebugPathPoly? nearest = null; foreach (var poly in tile) @@ -359,7 +359,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle) continue; } - var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); + var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform); worldHandle.SetTransform(worldMatrix); var localAABB = invWorldMatrix.TransformBox(aabb); @@ -419,7 +419,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle) !xformQuery.TryGetComponent(grid, out var gridXform)) continue; - var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); + var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform); worldHandle.SetTransform(worldMatrix); var localAABB = invWorldMatrix.TransformBox(aabb); @@ -458,7 +458,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle) !xformQuery.TryGetComponent(grid, out var gridXform)) continue; - var (_, _, worldMatrix, invMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); + var (_, _, worldMatrix, invMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform); worldHandle.SetTransform(worldMatrix); var localAABB = invMatrix.TransformBox(aabb); @@ -483,7 +483,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle) if (neighborPoly.NetEntity != poly.GraphUid) { color = Color.Green; - var neighborMap = _entManager.GetCoordinates(neighborPoly).ToMap(_entManager, _transformSystem); + var neighborMap = _transformSystem.ToMapCoordinates(_entManager.GetCoordinates(neighborPoly)); if (neighborMap.MapId != args.MapId) continue; @@ -517,7 +517,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle) !xformQuery.TryGetComponent(grid, out var gridXform)) continue; - var (_, _, worldMatrix, invWorldMatrix) = gridXform.GetWorldPositionRotationMatrixWithInv(); + var (_, _, worldMatrix, invWorldMatrix) = _transformSystem.GetWorldPositionRotationMatrixWithInv(gridXform); worldHandle.SetTransform(worldMatrix); var localAABB = invWorldMatrix.TransformBox(args.WorldBounds); @@ -544,7 +544,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle) if (!_entManager.TryGetComponent(_entManager.GetEntity(node.GraphUid), out var graphXform)) continue; - worldHandle.SetTransform(graphXform.WorldMatrix); + worldHandle.SetTransform(_transformSystem.GetWorldMatrix(graphXform)); worldHandle.DrawRect(node.Box, Color.Orange.WithAlpha(0.10f)); } } @@ -568,7 +568,7 @@ private void DrawWorld(OverlayDrawArgs args, DrawingHandleWorld worldHandle) continue; matrix = graph; - worldHandle.SetTransform(graphXform.WorldMatrix); + worldHandle.SetTransform(_transformSystem.GetWorldMatrix(graphXform)); } worldHandle.DrawRect(node.Box, new Color(0f, cost / highestGScore, 1f - (cost / highestGScore), 0.10f)); diff --git a/Content.Client/Physics/JointVisualsOverlay.cs b/Content.Client/Physics/JointVisualsOverlay.cs index 09c02746e2e..e0b3499a974 100644 --- a/Content.Client/Physics/JointVisualsOverlay.cs +++ b/Content.Client/Physics/JointVisualsOverlay.cs @@ -58,8 +58,8 @@ protected override void Draw(in OverlayDrawArgs args) coordsA = coordsA.Offset(rotA.RotateVec(visuals.OffsetA)); coordsB = coordsB.Offset(rotB.RotateVec(visuals.OffsetB)); - var posA = coordsA.ToMapPos(_entManager, xformSystem); - var posB = coordsB.ToMapPos(_entManager, xformSystem); + var posA = xformSystem.ToMapCoordinates(coordsA).Position; + var posB = xformSystem.ToMapCoordinates(coordsB).Position; var diff = (posB - posA); var length = diff.Length(); diff --git a/Content.Client/Pinpointer/UI/NavMapControl.cs b/Content.Client/Pinpointer/UI/NavMapControl.cs index 3c99a188181..413b41c36a6 100644 --- a/Content.Client/Pinpointer/UI/NavMapControl.cs +++ b/Content.Client/Pinpointer/UI/NavMapControl.cs @@ -228,8 +228,8 @@ protected override void KeyBindUp(GUIBoundKeyEventArgs args) { if (!blip.Selectable) continue; - - var currentDistance = (blip.Coordinates.ToMapPos(EntManager, _transformSystem) - worldPosition).Length(); + + var currentDistance = (_transformSystem.ToMapCoordinates(blip.Coordinates).Position - worldPosition).Length(); if (closestDistance < currentDistance || currentDistance * MinimapScale > MaxSelectableDistance) continue; @@ -397,7 +397,7 @@ protected override void Draw(DrawingHandleScreen handle) { if (lit && value.Visible) { - var mapPos = coord.ToMap(EntManager, _transformSystem); + var mapPos = _transformSystem.ToMapCoordinates(coord); if (mapPos.MapId != MapId.Nullspace) { @@ -418,7 +418,7 @@ protected override void Draw(DrawingHandleScreen handle) if (blip.Texture == null) continue; - var mapPos = blip.Coordinates.ToMap(EntManager, _transformSystem); + var mapPos = _transformSystem.ToMapCoordinates(blip.Coordinates); if (mapPos.MapId != MapId.Nullspace) { @@ -535,7 +535,7 @@ private void UpdateNavMapWallLines() // East edge neighborData = 0; if (relativeTile.X != SharedNavMapSystem.ChunkSize - 1) - neighborData = chunk.TileData[i+SharedNavMapSystem.ChunkSize]; + neighborData = chunk.TileData[i + SharedNavMapSystem.ChunkSize]; else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Right, out neighborChunk)) neighborData = neighborChunk.TileData[i + SharedNavMapSystem.ChunkSize - SharedNavMapSystem.ArraySize]; @@ -548,7 +548,7 @@ private void UpdateNavMapWallLines() // South edge neighborData = 0; if (relativeTile.Y != 0) - neighborData = chunk.TileData[i-1]; + neighborData = chunk.TileData[i - 1]; else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Down, out neighborChunk)) neighborData = neighborChunk.TileData[i - 1 + SharedNavMapSystem.ChunkSize]; @@ -561,7 +561,7 @@ private void UpdateNavMapWallLines() // West edge neighborData = 0; if (relativeTile.X != 0) - neighborData = chunk.TileData[i-SharedNavMapSystem.ChunkSize]; + neighborData = chunk.TileData[i - SharedNavMapSystem.ChunkSize]; else if (_navMap.Chunks.TryGetValue(chunkOrigin + Vector2i.Left, out neighborChunk)) neighborData = neighborChunk.TileData[i - SharedNavMapSystem.ChunkSize + SharedNavMapSystem.ArraySize]; diff --git a/Content.Client/RCD/AlignRCDConstruction.cs b/Content.Client/RCD/AlignRCDConstruction.cs index d5425425a76..fcbe408a382 100644 --- a/Content.Client/RCD/AlignRCDConstruction.cs +++ b/Content.Client/RCD/AlignRCDConstruction.cs @@ -45,7 +45,7 @@ public override void AlignPlacementMode(ScreenCoordinates mouseScreen) _unalignedMouseCoords = ScreenToCursorGrid(mouseScreen); MouseCoords = _unalignedMouseCoords.AlignWithClosestGridTile(SearchBoxSize, _entityManager, _mapManager); - var gridId = MouseCoords.GetGridUid(_entityManager); + var gridId = _transformSystem.GetGrid(MouseCoords); if (!_entityManager.TryGetComponent(gridId, out var mapGrid)) return; @@ -105,8 +105,8 @@ public override bool IsValidPosition(EntityCoordinates position) if (currentState is not GameplayStateBase screen) return false; - - var target = screen.GetClickedEntity(_unalignedMouseCoords.ToMap(_entityManager, _transformSystem)); + + var target = screen.GetClickedEntity(_transformSystem.ToMapCoordinates(_unalignedMouseCoords)); // Determine if the RCD operation is valid or not if (!_rcdSystem.IsRCDOperationStillValid(heldEntity.Value, rcd, mapGridData.Value, target, player.Value, false)) diff --git a/Content.Client/Sandbox/SandboxSystem.cs b/Content.Client/Sandbox/SandboxSystem.cs index 6a1129bb75d..8a4c93fa354 100644 --- a/Content.Client/Sandbox/SandboxSystem.cs +++ b/Content.Client/Sandbox/SandboxSystem.cs @@ -17,6 +17,7 @@ public sealed class SandboxSystem : SharedSandboxSystem [Dependency] private readonly IPlacementManager _placement = default!; [Dependency] private readonly ContentEyeSystem _contentEye = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _mapSystem = default!; private bool _sandboxEnabled; public bool SandboxAllowed { get; private set; } @@ -92,7 +93,7 @@ public bool Copy(ICommonSession? session, EntityCoordinates coords, EntityUid ui && EntityManager.TryGetComponent(uid, out MetaDataComponent? comp) && !comp.EntityDeleted) { - if (comp.EntityPrototype == null || comp.EntityPrototype.NoSpawn || comp.EntityPrototype.Abstract) + if (comp.EntityPrototype == null || comp.EntityPrototype.HideSpawnMenu || comp.EntityPrototype.Abstract) return false; if (_placement.Eraser) @@ -109,7 +110,8 @@ public bool Copy(ICommonSession? session, EntityCoordinates coords, EntityUid ui } // Try copy tile. - if (!_map.TryFindGridAt(coords.ToMap(EntityManager, _transform), out _, out var grid) || !grid.TryGetTileRef(coords, out var tileRef)) + + if (!_map.TryFindGridAt(_transform.ToMapCoordinates(coords), out var gridUid, out var grid) || !_mapSystem.TryGetTileRef(gridUid, grid, coords, out var tileRef)) return false; if (_placement.Eraser) diff --git a/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs b/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs index c134b7157c4..eb9ec285f8c 100644 --- a/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs +++ b/Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs @@ -35,9 +35,9 @@ public MapCoordinates GetMapCoordinates(IMapObject mapObj) switch (mapObj) { case ShuttleBeaconObject beacon: - return GetCoordinates(beacon.Coordinates).ToMap(EntityManager, XformSystem); + return XformSystem.ToMapCoordinates(GetCoordinates(beacon.Coordinates)); case ShuttleExclusionObject exclusion: - return GetCoordinates(exclusion.Coordinates).ToMap(EntityManager, XformSystem); + return XformSystem.ToMapCoordinates(GetCoordinates(exclusion.Coordinates)); case GridMapObject grid: var gridXform = Transform(grid.Entity); diff --git a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs index 8bd4a338cb0..53ad4a0b23a 100644 --- a/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs +++ b/Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs @@ -519,7 +519,7 @@ private void AddMapObject(List edges, List verts, ValueList(gunUid); _animPlayer.Stop(gunUid, uidPlayer, "muzzle-flash-light"); - _animPlayer.Play((gunUid, uidPlayer), animTwo,"muzzle-flash-light"); + _animPlayer.Play((gunUid, uidPlayer), animTwo, "muzzle-flash-light"); } } diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index 9dc3b5e1e67..ca7791fb68a 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -145,8 +145,7 @@ public bool TryPoint(ICommonSession? session, EntityCoordinates coordsPointed, E _popup.PopupEntity(Loc.GetString("pointing-system-try-point-cannot-reach"), player, player); return false; } - - var mapCoordsPointed = coordsPointed.ToMap(EntityManager, _transform); + var mapCoordsPointed = _transform.ToMapCoordinates(coordsPointed); _rotateToFaceSystem.TryFaceCoordinates(player, mapCoordsPointed.Position); var arrow = EntityManager.SpawnEntity("PointingArrow", coordsPointed); @@ -154,7 +153,7 @@ public bool TryPoint(ICommonSession? session, EntityCoordinates coordsPointed, E if (TryComp(arrow, out var pointing)) { if (TryComp(player, out TransformComponent? xformPlayer)) - pointing.StartPosition = EntityCoordinates.FromMap(arrow, xformPlayer.Coordinates.ToMap(EntityManager, _transform), _transform).Position; + pointing.StartPosition = _transform.ToCoordinates((player, xformPlayer), _transform.ToMapCoordinates(xformPlayer.Coordinates)).Position; pointing.EndTime = _gameTiming.CurTime + PointDuration; diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 0e302f1e028..49c137468e4 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -427,7 +427,7 @@ private void OnActionRequest(RequestPerformActionEvent ev, EntitySessionEventArg } var entityCoordinatesTarget = GetCoordinates(netCoordinatesTarget); - _rotateToFaceSystem.TryFaceCoordinates(user, entityCoordinatesTarget.ToMapPos(EntityManager, _transformSystem)); + _rotateToFaceSystem.TryFaceCoordinates(user, _transformSystem.ToMapCoordinates(entityCoordinatesTarget).Position); if (!ValidateWorldTarget(user, entityCoordinatesTarget, (actionEnt, worldAction))) return; From b7aa97e2030c0b01d07a76b4ee291fe2c8be95fa Mon Sep 17 00:00:00 2001 From: Plykiya <58439124+Plykiya@users.noreply.github.com> Date: Sat, 13 Jul 2024 14:25:51 -0700 Subject: [PATCH 67/78] Replace obsolete EntityCoordiates.InRange() with TransformSystem.InRange() (#29993) * Replace EntityCoordiates.InRange() with TransformSystem.InRange() * nullspace * I figured it out * man I have no clue how client side sutff works * please have mercy * remove RadiationPulseOverlay changes * nullspace --------- Co-authored-by: plykiya --- Content.Client/RCD/AlignRCDConstruction.cs | 2 +- Content.Client/Storage/Systems/StorageSystem.cs | 4 ++-- Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs | 2 +- Content.Server/Dragon/DragonSystem.cs | 2 +- Content.Server/Guardian/GuardianSystem.cs | 2 +- Content.Server/Instruments/InstrumentSystem.cs | 3 ++- Content.Server/Medical/HealthAnalyzerSystem.cs | 2 +- Content.Server/Movement/Systems/PullController.cs | 5 +++-- .../HTN/Preconditions/CoordinatesInRangePrecondition.cs | 9 ++++++++- .../Preconditions/CoordinatesNotInRangePrecondition.cs | 9 ++++++++- .../NPC/HTN/Preconditions/TargetInRangePrecondition.cs | 9 ++++++++- Content.Server/Pointing/EntitySystems/PointingSystem.cs | 2 +- Content.Shared/Actions/SharedActionsSystem.cs | 2 +- Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs | 2 +- 14 files changed, 39 insertions(+), 16 deletions(-) diff --git a/Content.Client/RCD/AlignRCDConstruction.cs b/Content.Client/RCD/AlignRCDConstruction.cs index fcbe408a382..ef99b01855c 100644 --- a/Content.Client/RCD/AlignRCDConstruction.cs +++ b/Content.Client/RCD/AlignRCDConstruction.cs @@ -75,7 +75,7 @@ public override bool IsValidPosition(EntityCoordinates position) if (!_entityManager.TryGetComponent(player, out var xform)) return false; - if (!xform.Coordinates.InRange(_entityManager, _transformSystem, position, SharedInteractionSystem.InteractionRange)) + if (!_transformSystem.InRange(xform.Coordinates, position, SharedInteractionSystem.InteractionRange)) { InvalidPlaceColor = InvalidPlaceColor.WithAlpha(0); return false; diff --git a/Content.Client/Storage/Systems/StorageSystem.cs b/Content.Client/Storage/Systems/StorageSystem.cs index 1e011f08f0e..eea7b9ec797 100644 --- a/Content.Client/Storage/Systems/StorageSystem.cs +++ b/Content.Client/Storage/Systems/StorageSystem.cs @@ -142,8 +142,8 @@ public void PickupAnimation(EntityUid item, EntityCoordinates initialCoords, Ent { if (!_timing.IsFirstTimePredicted) return; - - if (finalCoords.InRange(EntityManager, TransformSystem, initialCoords, 0.1f) || + + if (TransformSystem.InRange(finalCoords, initialCoords, 0.1f) || !Exists(initialCoords.EntityId) || !Exists(finalCoords.EntityId)) { return; diff --git a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs index c2cdd4a1072..0f4490cd7eb 100644 --- a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs @@ -162,7 +162,7 @@ private bool UpdateAnalyzer(EntityUid uid, GasAnalyzerComponent? component = nul if (component.LastPosition.HasValue) { // Check if position is out of range => don't update and disable - if (!component.LastPosition.Value.InRange(EntityManager, _transform, userPos, SharedInteractionSystem.InteractionRange)) + if (!_transform.InRange(component.LastPosition.Value, userPos, SharedInteractionSystem.InteractionRange)) { if (component.User is { } userId && component.Enabled) _popup.PopupEntity(Loc.GetString("gas-analyzer-shutoff"), userId, userId); diff --git a/Content.Server/Dragon/DragonSystem.cs b/Content.Server/Dragon/DragonSystem.cs index 62d1f61a35b..96ca8d3614a 100644 --- a/Content.Server/Dragon/DragonSystem.cs +++ b/Content.Server/Dragon/DragonSystem.cs @@ -146,7 +146,7 @@ private void OnSpawnRift(EntityUid uid, DragonComponent component, DragonSpawnRi // cant stack rifts near eachother foreach (var (_, riftXform) in EntityQuery(true)) { - if (riftXform.Coordinates.InRange(EntityManager, _transform, xform.Coordinates, RiftRange)) + if (_transform.InRange(riftXform.Coordinates, xform.Coordinates, RiftRange)) { _popup.PopupEntity(Loc.GetString("carp-rift-proximity", ("proximity", RiftRange)), uid, uid); return; diff --git a/Content.Server/Guardian/GuardianSystem.cs b/Content.Server/Guardian/GuardianSystem.cs index 203882ed9ef..ae4d0ca2b8c 100644 --- a/Content.Server/Guardian/GuardianSystem.cs +++ b/Content.Server/Guardian/GuardianSystem.cs @@ -325,7 +325,7 @@ private void CheckGuardianMove( if (!guardianComponent.GuardianLoose) return; - if (!guardianXform.Coordinates.InRange(EntityManager, _transform, hostXform.Coordinates, guardianComponent.DistanceAllowed)) + if (!_transform.InRange(guardianXform.Coordinates, hostXform.Coordinates, guardianComponent.DistanceAllowed)) RetractGuardian(hostUid, hostComponent, guardianUid, guardianComponent); } diff --git a/Content.Server/Instruments/InstrumentSystem.cs b/Content.Server/Instruments/InstrumentSystem.cs index 582bf7fa67b..6814b596dc5 100644 --- a/Content.Server/Instruments/InstrumentSystem.cs +++ b/Content.Server/Instruments/InstrumentSystem.cs @@ -402,7 +402,8 @@ public override void Update(float frameTime) var trans = transformQuery.GetComponent(uid); var masterTrans = transformQuery.GetComponent(master); - if (!masterTrans.Coordinates.InRange(EntityManager, _transform, trans.Coordinates, 10f)) + if (!_transform.InRange(masterTrans.Coordinates, trans.Coordinates, 10f) +) { Clean(uid, instrument); } diff --git a/Content.Server/Medical/HealthAnalyzerSystem.cs b/Content.Server/Medical/HealthAnalyzerSystem.cs index 1d6e564a32d..98f4f00d899 100644 --- a/Content.Server/Medical/HealthAnalyzerSystem.cs +++ b/Content.Server/Medical/HealthAnalyzerSystem.cs @@ -65,7 +65,7 @@ public override void Update(float frameTime) //Get distance between health analyzer and the scanned entity var patientCoordinates = Transform(patient).Coordinates; - if (!patientCoordinates.InRange(EntityManager, _transformSystem, transform.Coordinates, component.MaxScanRange)) + if (!_transformSystem.InRange(patientCoordinates, transform.Coordinates, component.MaxScanRange)) { //Range too far, disable updates StopAnalyzingEntity((uid, component), patient); diff --git a/Content.Server/Movement/Systems/PullController.cs b/Content.Server/Movement/Systems/PullController.cs index 340dc5654e8..4bd4b603714 100644 --- a/Content.Server/Movement/Systems/PullController.cs +++ b/Content.Server/Movement/Systems/PullController.cs @@ -58,6 +58,7 @@ public sealed class PullController : VirtualController [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; /// /// If distance between puller and pulled entity lower that this threshold, @@ -133,8 +134,8 @@ private bool OnRequestMovePulledObject(ICommonSession? session, EntityCoordinate var range = 2f; var fromUserCoords = coords.WithEntityId(player, EntityManager); var userCoords = new EntityCoordinates(player, Vector2.Zero); - - if (!coords.InRange(EntityManager, TransformSystem, userCoords, range)) + + if (!_transformSystem.InRange(coords, userCoords, range)) { var direction = fromUserCoords.Position - userCoords.Position; diff --git a/Content.Server/NPC/HTN/Preconditions/CoordinatesInRangePrecondition.cs b/Content.Server/NPC/HTN/Preconditions/CoordinatesInRangePrecondition.cs index 3485bd2a18c..452bf327f25 100644 --- a/Content.Server/NPC/HTN/Preconditions/CoordinatesInRangePrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/CoordinatesInRangePrecondition.cs @@ -8,12 +8,19 @@ namespace Content.Server.NPC.HTN.Preconditions; public sealed partial class CoordinatesInRangePrecondition : HTNPrecondition { [Dependency] private readonly IEntityManager _entManager = default!; + private SharedTransformSystem _transformSystem = default!; [DataField("targetKey", required: true)] public string TargetKey = default!; [DataField("rangeKey", required: true)] public string RangeKey = default!; + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _transformSystem = sysManager.GetEntitySystem(); + } + public override bool IsMet(NPCBlackboard blackboard) { if (!blackboard.TryGetValue(NPCBlackboard.OwnerCoordinates, out var coordinates, _entManager)) @@ -22,6 +29,6 @@ public override bool IsMet(NPCBlackboard blackboard) if (!blackboard.TryGetValue(TargetKey, out var target, _entManager)) return false; - return coordinates.InRange(_entManager, _entManager.System(), target, blackboard.GetValueOrDefault(RangeKey, _entManager)); + return _transformSystem.InRange(coordinates, target, blackboard.GetValueOrDefault(RangeKey, _entManager)); } } diff --git a/Content.Server/NPC/HTN/Preconditions/CoordinatesNotInRangePrecondition.cs b/Content.Server/NPC/HTN/Preconditions/CoordinatesNotInRangePrecondition.cs index 9d000ca2eb8..901831679e8 100644 --- a/Content.Server/NPC/HTN/Preconditions/CoordinatesNotInRangePrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/CoordinatesNotInRangePrecondition.cs @@ -8,12 +8,19 @@ namespace Content.Server.NPC.HTN.Preconditions; public sealed partial class CoordinatesNotInRangePrecondition : HTNPrecondition { [Dependency] private readonly IEntityManager _entManager = default!; + private SharedTransformSystem _transformSystem = default!; [DataField("targetKey", required: true)] public string TargetKey = default!; [DataField("rangeKey", required: true)] public string RangeKey = default!; + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _transformSystem = sysManager.GetEntitySystem(); + } + public override bool IsMet(NPCBlackboard blackboard) { if (!blackboard.TryGetValue(NPCBlackboard.OwnerCoordinates, out var coordinates, _entManager)) @@ -22,7 +29,7 @@ public override bool IsMet(NPCBlackboard blackboard) if (!blackboard.TryGetValue(TargetKey, out var target, _entManager)) return false; - return !coordinates.InRange(_entManager, _entManager.System(), target, blackboard.GetValueOrDefault(RangeKey, _entManager)); + return !_transformSystem.InRange(coordinates, target, blackboard.GetValueOrDefault(RangeKey, _entManager)); } } diff --git a/Content.Server/NPC/HTN/Preconditions/TargetInRangePrecondition.cs b/Content.Server/NPC/HTN/Preconditions/TargetInRangePrecondition.cs index aaccb426d71..921b5ffa226 100644 --- a/Content.Server/NPC/HTN/Preconditions/TargetInRangePrecondition.cs +++ b/Content.Server/NPC/HTN/Preconditions/TargetInRangePrecondition.cs @@ -8,11 +8,17 @@ namespace Content.Server.NPC.HTN.Preconditions; public sealed partial class TargetInRangePrecondition : HTNPrecondition { [Dependency] private readonly IEntityManager _entManager = default!; + private SharedTransformSystem _transformSystem = default!; [DataField("targetKey", required: true)] public string TargetKey = default!; [DataField("rangeKey", required: true)] public string RangeKey = default!; + public override void Initialize(IEntitySystemManager sysManager) + { + base.Initialize(sysManager); + _transformSystem = sysManager.GetEntitySystem(); + } public override bool IsMet(NPCBlackboard blackboard) { @@ -23,6 +29,7 @@ public override bool IsMet(NPCBlackboard blackboard) !_entManager.TryGetComponent(target, out var targetXform)) return false; - return coordinates.InRange(_entManager, _entManager.System(), targetXform.Coordinates, blackboard.GetValueOrDefault(RangeKey, _entManager)); + var transformSystem = _entManager.System; + return _transformSystem.InRange(coordinates, targetXform.Coordinates, blackboard.GetValueOrDefault(RangeKey, _entManager)); } } diff --git a/Content.Server/Pointing/EntitySystems/PointingSystem.cs b/Content.Server/Pointing/EntitySystems/PointingSystem.cs index ca7791fb68a..4b7f50fb86c 100644 --- a/Content.Server/Pointing/EntitySystems/PointingSystem.cs +++ b/Content.Server/Pointing/EntitySystems/PointingSystem.cs @@ -101,7 +101,7 @@ public bool InRange(EntityUid pointer, EntityCoordinates coordinates) { if (HasComp(pointer)) { - return Transform(pointer).Coordinates.InRange(EntityManager, _transform, coordinates, 15); + return _transform.InRange(Transform(pointer).Coordinates, coordinates, 15); } else { diff --git a/Content.Shared/Actions/SharedActionsSystem.cs b/Content.Shared/Actions/SharedActionsSystem.cs index 49c137468e4..013348eb4f7 100644 --- a/Content.Shared/Actions/SharedActionsSystem.cs +++ b/Content.Shared/Actions/SharedActionsSystem.cs @@ -533,7 +533,7 @@ private bool ValidateWorldTargetBase(EntityUid user, EntityCoordinates coords, W if (action.Range <= 0) return true; - return coords.InRange(EntityManager, _transformSystem, Transform(user).Coordinates, action.Range); + return _transformSystem.InRange(coords, Transform(user).Coordinates, action.Range); } return _interactionSystem.InRangeUnobstructed(user, coords, range: action.Range); diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs index 4f77a271b37..ad94f3b9408 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.Update.cs @@ -170,7 +170,7 @@ private bool ShouldCancel(DoAfter doAfter, if (args.BreakOnMove && !(!args.BreakOnWeightlessMove && _gravity.IsWeightless(args.User, xform: userXform))) { // Whether the user has moved too much from their original position. - if (!userXform.Coordinates.InRange(EntityManager, _transform, doAfter.UserPosition, args.MovementThreshold)) + if (!_transform.InRange(userXform.Coordinates, doAfter.UserPosition, args.MovementThreshold)) return true; // Whether the distance between the user and target(if any) has changed too much. From f68712431263fb2d2240fdf3bb9e0cbb278bd61d Mon Sep 17 00:00:00 2001 From: Winkarst <74284083+Winkarst-cpu@users.noreply.github.com> Date: Sun, 14 Jul 2024 02:17:15 +0300 Subject: [PATCH 68/78] Update IdExaminableSystem.cs to use TryFromMarkup (#29957) * Change FromMarkup to TryFromMarkup method in IdExaminableSystem.cs * Update --- Content.Shared/Access/Systems/IdExaminableSystem.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Access/Systems/IdExaminableSystem.cs b/Content.Shared/Access/Systems/IdExaminableSystem.cs index 333272e27ac..13359adcba2 100644 --- a/Content.Shared/Access/Systems/IdExaminableSystem.cs +++ b/Content.Shared/Access/Systems/IdExaminableSystem.cs @@ -27,7 +27,8 @@ private void OnGetExamineVerbs(EntityUid uid, IdExaminableComponent component, G { Act = () => { - var markup = FormattedMessage.FromMarkup(info); + var markup = FormattedMessage.FromMarkupOrThrow(info); + _examineSystem.SendExamineTooltip(args.User, uid, markup, false, false); }, Text = Loc.GetString("id-examinable-component-verb-text"), From 85fba9809ae267d096ff28d425dff0dac085bac8 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 14 Jul 2024 01:18:12 +0200 Subject: [PATCH 69/78] Update changelog RSS configuration (#30024) New server --- Tools/actions_changelog_rss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/actions_changelog_rss.py b/Tools/actions_changelog_rss.py index 5e42a030bde..01ca7852ccb 100755 --- a/Tools/actions_changelog_rss.py +++ b/Tools/actions_changelog_rss.py @@ -29,7 +29,7 @@ # Change these to suit your server settings # https://docs.fabfile.org/en/stable/getting-started.html#run-commands-via-connections-and-run -SSH_HOST = "centcomm.spacestation14.io" +SSH_HOST = "moon.spacestation14.com" SSH_USER = "changelog-rss" SSH_PORT = 22 RSS_FILE = "changelog.xml" From a06309b96492d014781a8d0384588617673b335c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 13 Jul 2024 20:26:39 -0400 Subject: [PATCH 70/78] Update Credits (#30026) Co-authored-by: PJBot --- Resources/Credits/GitHub.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Credits/GitHub.txt b/Resources/Credits/GitHub.txt index f3d458e0468..47b90c9a166 100644 --- a/Resources/Credits/GitHub.txt +++ b/Resources/Credits/GitHub.txt @@ -1 +1 @@ -0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aeshus, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, ArkiveDev, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, bhenrich, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blueDev2, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CaasGit, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, CatTheSystem, Centronias, chairbender, Charlese2, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, DexlerXD, dffdff2423, diraven, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FluidRock, FoLoKe, fooberticus, Fortune117, freeman2651, Froffy025, Fromoriss, FungiFellow, Futuristic-OK, GalacticChimp, gbasood, Geekyhobo, Genkail, geraeumig, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, Interrobang01, IProduceWidgets, ItsMeThom, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JIPDawg, JoeHammad1844, joelsgp, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, Keer-Sar, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, laok233, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, MjrLandWhale, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, RamZ, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, WarMechanic, waylon531, weaversam8, whateverusername0, Willhelm53, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zonespace27, Zumorica, Zymem +0x6273, 2013HORSEMEATSCANDAL, 20kdc, 21Melkuu, 4dplanner, 612git, 778b, Ablankmann, Acruid, actioninja, adamsong, Admiral-Obvious-001, Adrian16199, Aerocrux, Aeshus, Aexxie, Afrokada, Agoichi, Ahion, AJCM-git, AjexRose, Alekshhh, AlexMorgan3817, AlexUm418, AlmondFlour, AlphaQwerty, Altoids1, amylizzle, ancientpower, ArchPigeon, Arendian, arimah, ArkiveDev, Arteben, AruMoon, as334, AsikKEsel, asperger-sind, aspiringLich, avghdev, AzzyIsNotHere, BananaFlambe, Baptr0b0t, BasedUser, beck-thompson, BellwetherLogic, BGare, bhenrich, BingoJohnson-zz, BismarckShuffle, Bixkitts, Blackern5000, Blazeror, blueDev2, Boaz1111, BobdaBiscuit, brainfood1183, Brandon-Huu, Bright0, brndd, BubblegumBlue, BYONDFuckery, c4llv07e, CaasGit, CakeQ, Callmore, CaptainSqrBeard, Carbonhell, CatTheSystem, Centronias, chairbender, Charlese2, chavonadelal, Cheackraze, cheesePizza2, Chief-Engineer, chromiumboy, Chronophylos, Ciac32, clement-or, Clyybber, Cojoke-dot, ColdAutumnRain, collinlunn, ComicIronic, coolmankid12345, corentt, crazybrain23, creadth, CrigCrag, Crotalus, CrudeWax, CrzyPotato, Cyberboss, d34d10cc, Daemon, daerSeebaer, dahnte, dakamakat, dakimasu, DamianX, DangerRevolution, daniel-cr, Darkenson, DawBla, dch-GH, Deahaka, DEATHB4DEFEAT, DeathCamel58, deathride58, DebugOk, Decappi, deepdarkdepths, deepy, Delete69, deltanedas, DerbyX, DexlerXD, dffdff2423, diraven, Doctor-Cpu, DoctorBeard, DogZeroX, dontbetank, Doomsdrayk, Doru991, DoubleRiceEddiedd, DoutorWhite, DrMelon, DrSmugleaf, drteaspoon420, DTanxxx, DubiousDoggo, Duddino, Dutch-VanDerLinde, Easypoller, eclips_e, EdenTheLiznerd, EEASAS, Efruit, ElectroSR, elthundercloud, Emisse, EmoGarbage404, Endecc, enumerate0, eoineoineoin, ERORR404V1, Errant-4, estacaoespacialpirata, exincore, exp111, Fahasor, FairlySadPanda, ficcialfaint, Fildrance, FillerVK, Fishfish458, Flareguy, FluffiestFloof, FoLoKe, fooberticus, Fortune117, freeman2651, Fromoriss, FungiFellow, GalacticChimp, gbasood, Geekyhobo, Genkail, geraeumig, Ghagliiarghii, Git-Nivrak, github-actions[bot], gituhabu, GNF54, Golinth, GoodWheatley, Gotimanga, graevy, GreyMario, gusxyz, Gyrandola, h3half, Hanzdegloker, Hardly3D, harikattar, HerCoyote23, hitomishirichan, Hmeister-real, HoofedEar, hord-brayden, hubismal, Hugal31, Huxellberger, iacore, IamVelcroboy, icekot8, igorsaux, ike709, Illiux, Ilya246, IlyaElDunaev, Injazz, Insineer, Interrobang01, IProduceWidgets, ItsMeThom, Jackal298, Jackrost, jamessimo, janekvap, JerryImMouse, Jessetriesagain, jessicamaybe, Jezithyr, jicksaw, JiimBob, JIPDawg, JoeHammad1844, joelsgp, JohnGinnane, johnku1, joshepvodka, jproads, Jrpl, juliangiebel, JustArt1m, JustCone14, JustinTrotter, K-Dynamic, KaiShibaa, kalane15, kalanosh, Keer-Sar, Kelrak, kerisargit, keronshb, KIBORG04, Killerqu00, KingFroozy, kira-er, Kit0vras, KittenColony, Ko4ergaPunk, komunre, koteq, Krunklehorn, Kukutis96513, kxvvv, Lamrr, LankLTE, laok233, lapatison, Leander-0, LetterN, Level10Cybermancer, lever1209, liltenhead, LittleBuilderJane, Lomcastar, LordCarve, LordEclipse, luckyshotpictures, Lukasz825700516, lunarcomets, luringens, lvvova1, lzimann, lzk228, MACMAN2003, Macoron, MagnusCrowe, ManelNavola, Mangohydra, Matz05, MehimoNemo, MeltedPixel, MemeProof, Menshin, Mervill, metalgearsloth, mhamsterr, MilenVolf, Minty642, Mirino97, mirrorcult, misandrie, MishaUnity, MisterMecky, Mith-randalf, MjrLandWhale, Moneyl, Moomoobeef, moony, Morb0, Mr0maks, musicmanvr, Myakot, Myctai, N3X15, Nairodian, Naive817, namespace-Memory, NickPowers43, nikthechampiongr, Nimfar11, Nirnael, nmajask, nok-ko, Nopey, notafet, notquitehadouken, noudoit, nuke-haus, NULL882, OctoRocket, OldDanceJacket, onoira, osjarw, Owai-Seek, pali6, Pangogie, patrikturi, PaulRitter, Peptide90, peptron1, Phantom-Lily, pigeonpeas, pissdemon, PixelTheKermit, PJB3005, Plykiya, pofitlo, pointer-to-null, PolterTzi, PoorMansDreams, potato1234x, ProfanedBane, PrPleGoo, ps3moira, Psychpsyo, psykzz, PuroSlavKing, PursuitInAshes, quatre, QuietlyWhisper, qwerltaz, Radosvik, Radrark, Rainbeon, Rainfey, RamZ, Rane, ravage123321, rbertoche, Redict, RedlineTriad, RednoWCirabrab, RemberBM, RemieRichards, RemTim, rene-descartes2021, RiceMar1244, RieBi, Rinkashikachi, Rockdtben, rolfero, rosieposieeee, RumiTiger, Saakra, Samsterious, SaphireLattice, ScalyChimp, scrato, Scribbles0, Serkket, SethLafuente, ShadowCommander, Shadowtheprotogen546, shampunj, SignalWalker, Simyon264, Sirionaut, siyengar04, Skarletto, Skrauz, Skyedra, SlamBamActionman, slarticodefast, Slava0135, snebl, Snowni, snowsignal, SonicHDC, SoulFN, SoulSloth, SpaceManiac, SpeltIncorrectyl, SphiraI, spoogemonster, ssdaniel24, Stealthbomber16, StrawberryMoses, superjj18, SweptWasTaken, Szunti, takemysoult, TaralGit, Tayrtahn, tday93, TekuNut, TemporalOroboros, tentekal, Terraspark4941, tgrkzus, thatrandomcanadianguy, TheArturZh, theashtronaut, thedraccx, themias, Theomund, theOperand, TheShuEd, TimrodDX, Titian3, tkdrg, tmtmtl30, TokenStyle, tom-leys, tomasalves8, Tomeno, Tornado-Technology, tosatur, TsjipTsjip, Tunguso4ka, TurboTrackerss14, Tyler-IN, Tyzemol, UbaserB, UBlueberry, UKNOWH, Uriende, UristMcDorf, Vaaankas, Varen, VasilisThePikachu, veliebm, Veritius, Vermidia, Verslebas, VigersRay, Visne, volundr-, Voomra, Vordenburg, vulppine, wafehling, WarMechanic, waylon531, weaversam8, whateverusername0, Willhelm53, Winkarst-cpu, wixoaGit, WlarusFromDaSpace, wrexbe, xRiriq, yathxyz, Ygg01, YotaXP, YuriyKiss, zach-hill, Zandario, Zap527, Zealith-Gamer, ZelteHonor, zerorulez, zionnBE, zlodo, ZNixian, ZoldorfTheWizard, Zonespace27, Zumorica, Zymem From 87467a358bfb874f15e7aa4afef5abd76423d22f Mon Sep 17 00:00:00 2001 From: Luiz Costa <33888056+luizwritescode@users.noreply.github.com> Date: Sat, 13 Jul 2024 23:59:45 -0300 Subject: [PATCH 71/78] Fix lizards losing snouts when equipping a head bandana (#29979) * say goodbye to no-snout lizards * remove snout from plague doctor hat HideLayerClothing component --- .../Components/FoldableClothingComponent.cs | 13 +++++++++++++ .../EntitySystems/FoldableClothingSystem.cs | 7 +++++++ .../Prototypes/Entities/Clothing/Head/bandanas.yml | 2 ++ .../Prototypes/Entities/Clothing/Head/hats.yml | 1 - .../Prototypes/Entities/Clothing/Masks/bandanas.yml | 3 +++ 5 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Content.Shared/Clothing/Components/FoldableClothingComponent.cs b/Content.Shared/Clothing/Components/FoldableClothingComponent.cs index 1a40d1dca12..ffcb52b4576 100644 --- a/Content.Shared/Clothing/Components/FoldableClothingComponent.cs +++ b/Content.Shared/Clothing/Components/FoldableClothingComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Humanoid; using Content.Shared.Inventory; using Robust.Shared.GameStates; @@ -30,4 +31,16 @@ public sealed partial class FoldableClothingComponent : Component /// [DataField] public string? FoldedHeldPrefix; + + /// + /// Which layers does this hide when Unfolded? See and + /// + [DataField] + public HashSet UnfoldedHideLayers = new(); + + /// + /// Which layers does this hide when folded? See and + /// + [DataField] + public HashSet FoldedHideLayers = new(); } diff --git a/Content.Shared/Clothing/EntitySystems/FoldableClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/FoldableClothingSystem.cs index be55588ddd5..603af4099c8 100644 --- a/Content.Shared/Clothing/EntitySystems/FoldableClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/FoldableClothingSystem.cs @@ -47,6 +47,10 @@ private void OnFolded(Entity ent, ref FoldedEvent arg if (ent.Comp.FoldedHeldPrefix != null) _itemSystem.SetHeldPrefix(ent.Owner, ent.Comp.FoldedHeldPrefix, false, itemComp); + + if (TryComp(ent.Owner, out var hideLayerComp)) + hideLayerComp.Slots = ent.Comp.FoldedHideLayers; + } else { @@ -59,6 +63,9 @@ private void OnFolded(Entity ent, ref FoldedEvent arg if (ent.Comp.FoldedHeldPrefix != null) _itemSystem.SetHeldPrefix(ent.Owner, null, false, itemComp); + if (TryComp(ent.Owner, out var hideLayerComp)) + hideLayerComp.Slots = ent.Comp.UnfoldedHideLayers; + } } } diff --git a/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml b/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml index 51a56f1f1d6..da56194f715 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/bandanas.yml @@ -20,6 +20,8 @@ - state: icon_mask map: [ "unfoldedLayer" ] visible: false + - type: HideLayerClothing # needed since head bandana inherits from mask bandana + slots: [] - type: Tag tags: - Bandana diff --git a/Resources/Prototypes/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Entities/Clothing/Head/hats.yml index d220d55f1f5..8eeb82cbf4f 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hats.yml @@ -391,7 +391,6 @@ - type: HideLayerClothing slots: - Hair - - Snout - HeadTop - HeadSide diff --git a/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml b/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml index f5ad2fb6c83..80210300953 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml @@ -11,6 +11,9 @@ - HEAD unfoldedSlots: - MASK + foldedHideLayers: [] + unfoldedHideLayers: + - Snout - type: Mask - type: IngestionBlocker - type: IdentityBlocker From 68982953f3c4ba082e7e4651565bd36ae810d763 Mon Sep 17 00:00:00 2001 From: PJBot Date: Sun, 14 Jul 2024 03:00:52 +0000 Subject: [PATCH 72/78] Automatic changelog update --- Resources/Changelog/Changelog.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index e4c8aa1b430..0a41c2e2e9c 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,11 +1,4 @@ Entries: -- author: deltanedas - changes: - - message: Flaming mice no longer completely engulf people they touch. - type: Tweak - id: 6416 - time: '2024-04-22T08:42:26.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/27202 - author: Weax changes: - message: The CLF3 reaction now requires heating first before you can engulf chemistry @@ -3813,3 +3806,10 @@ id: 6915 time: '2024-07-13T12:15:57.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/25750 +- author: coffeeware + changes: + - message: Lizards will no longer lose their snouts when equipping head bandanas + type: Fix + id: 6916 + time: '2024-07-14T02:59:45.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/29979 From 5d1702d280ff0f20537abaa4ebf0e8f13400ff51 Mon Sep 17 00:00:00 2001 From: Winkarst <74284083+Winkarst-cpu@users.noreply.github.com> Date: Sun, 14 Jul 2024 06:05:39 +0300 Subject: [PATCH 73/78] Update MainMenu.cs to use ISawmill (#29988) * Update MainMenu.cs to use ISawmill * Update * Error --- Content.Client/MainMenu/MainMenu.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Content.Client/MainMenu/MainMenu.cs b/Content.Client/MainMenu/MainMenu.cs index 43c5bfe5674..3c709d2d15b 100644 --- a/Content.Client/MainMenu/MainMenu.cs +++ b/Content.Client/MainMenu/MainMenu.cs @@ -25,6 +25,9 @@ public sealed class MainScreen : Robust.Client.State.State [Dependency] private readonly IGameController _controllerProxy = default!; [Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!; + [Dependency] private readonly ILogManager _logManager = default!; + + private ISawmill _sawmill = default!; private MainMenuControl _mainMenuControl = default!; private bool _isConnecting; @@ -35,6 +38,8 @@ public sealed class MainScreen : Robust.Client.State.State /// protected override void Startup() { + _sawmill = _logManager.GetSawmill("mainmenu"); + _mainMenuControl = new MainMenuControl(_resourceCache, _configurationManager); _userInterfaceManager.StateRoot.AddChild(_mainMenuControl); @@ -116,7 +121,7 @@ private void TryConnect(string address) catch (ArgumentException e) { _userInterfaceManager.Popup($"Unable to connect: {e.Message}", "Connection error."); - Logger.Warning(e.ToString()); + _sawmill.Warning(e.ToString()); _netManager.ConnectFailed -= _onConnectFailed; _setConnectingState(false); } From 922be642904ede5988835a9143357d47be731b8d Mon Sep 17 00:00:00 2001 From: osjarw <62134478+osjarw@users.noreply.github.com> Date: Sun, 14 Jul 2024 06:26:38 +0300 Subject: [PATCH 74/78] NPC exits MeleeOperator on invalid EntityUid (#30005) EntityUid 0 problem fix --- .../HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs | 3 ++- Content.Server/NPC/Systems/NPCJukeSystem.cs | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs index 32be027ec43..5a02b86201b 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/Melee/MeleeOperator.cs @@ -89,7 +89,8 @@ public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTi HTNOperatorStatus status; if (_entManager.TryGetComponent(owner, out var combat) && - blackboard.TryGetValue(TargetKey, out var target, _entManager)) + blackboard.TryGetValue(TargetKey, out var target, _entManager) && + target != EntityUid.Invalid) { combat.Target = target; diff --git a/Content.Server/NPC/Systems/NPCJukeSystem.cs b/Content.Server/NPC/Systems/NPCJukeSystem.cs index da9fa1f7615..94a30feb0cb 100644 --- a/Content.Server/NPC/Systems/NPCJukeSystem.cs +++ b/Content.Server/NPC/Systems/NPCJukeSystem.cs @@ -143,6 +143,9 @@ private void OnJukeSteering(EntityUid uid, NPCJukeComponent component, ref NPCSt if (!_melee.TryGetWeapon(uid, out var weaponUid, out var weapon)) return; + if (!HasComp(melee.Target)) + return; + var cdRemaining = weapon.NextAttack - _timing.CurTime; var attackCooldown = TimeSpan.FromSeconds(1f / _melee.GetAttackRate(weaponUid, uid, weapon)); From bdf7293cfb83dd1e36fad4b40dfc33a9960e1d7a Mon Sep 17 00:00:00 2001 From: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Date: Sat, 13 Jul 2024 20:27:39 -0700 Subject: [PATCH 75/78] Clean itemmapper (#29983) * File scoped namespace * Format file * Fix param name in doc comment * Reflow doc comment --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../EntitySystems/SharedItemMapperSystem.cs | 164 +++++++++--------- 1 file changed, 83 insertions(+), 81 deletions(-) diff --git a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs index eb20c65a116..7ae821d8d91 100644 --- a/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedItemMapperSystem.cs @@ -4,107 +4,109 @@ using JetBrains.Annotations; using Robust.Shared.Containers; -namespace Content.Shared.Storage.EntitySystems +namespace Content.Shared.Storage.EntitySystems; + +/// +/// ItemMapperSystem is a system that on each initialization, insertion, removal of an entity from +/// given (with appropriate storage attached) will check each stored item to see +/// if its tags/component, and overall quantity match . +/// +[UsedImplicitly] +public abstract class SharedItemMapperSystem : EntitySystem { - /// - /// ItemMapperSystem is a system that on each initialization, insertion, removal of an entity from - /// given (with appropriate storage attached) will check each stored item to see - /// if its tags/component, and overall quantity match . - /// - [UsedImplicitly] - public abstract class SharedItemMapperSystem : EntitySystem + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + + /// + public override void Initialize() { - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + base.Initialize(); + SubscribeLocalEvent(InitLayers); + SubscribeLocalEvent(MapperEntityInserted); + SubscribeLocalEvent(MapperEntityRemoved); + } - /// - public override void Initialize() + private void InitLayers(EntityUid uid, ItemMapperComponent component, ComponentInit args) + { + foreach (var (layerName, val) in component.MapLayers) { - base.Initialize(); - SubscribeLocalEvent(InitLayers); - SubscribeLocalEvent(MapperEntityInserted); - SubscribeLocalEvent(MapperEntityRemoved); + val.Layer = layerName; } - private void InitLayers(EntityUid uid, ItemMapperComponent component, ComponentInit args) + if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent)) { - foreach (var (layerName, val) in component.MapLayers) - { - val.Layer = layerName; - } + var list = new List(component.MapLayers.Keys); + _appearance.SetData(uid, StorageMapVisuals.InitLayers, new ShowLayerData(list), appearanceComponent); + } - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent)) - { - var list = new List(component.MapLayers.Keys); - _appearance.SetData(uid, StorageMapVisuals.InitLayers, new ShowLayerData(list), appearanceComponent); - } + // Ensure appearance is correct with current contained entities. + UpdateAppearance(uid, component); + } - // Ensure appearance is correct with current contained entities. - UpdateAppearance(uid, component); - } + private void MapperEntityRemoved(EntityUid uid, ItemMapperComponent itemMapper, EntRemovedFromContainerMessage args) + { + if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID)) + return; - private void MapperEntityRemoved(EntityUid uid, ItemMapperComponent itemMapper, - EntRemovedFromContainerMessage args) - { - if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID)) - return; + UpdateAppearance(uid, itemMapper); + } - UpdateAppearance(uid, itemMapper); - } + private void MapperEntityInserted(EntityUid uid, + ItemMapperComponent itemMapper, + EntInsertedIntoContainerMessage args) + { + if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID)) + return; - private void MapperEntityInserted(EntityUid uid, ItemMapperComponent itemMapper, - EntInsertedIntoContainerMessage args) - { - if (itemMapper.ContainerWhitelist != null && !itemMapper.ContainerWhitelist.Contains(args.Container.ID)) - return; + UpdateAppearance(uid, itemMapper); + } - UpdateAppearance(uid, itemMapper); - } + private void UpdateAppearance(EntityUid uid, ItemMapperComponent? itemMapper = null) + { + if (!Resolve(uid, ref itemMapper)) + return; - private void UpdateAppearance(EntityUid uid, ItemMapperComponent? itemMapper = null) + if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent) + && TryGetLayers(uid, itemMapper, out var containedLayers)) { - if(!Resolve(uid, ref itemMapper)) - return; - - if (EntityManager.TryGetComponent(uid, out AppearanceComponent? appearanceComponent) - && TryGetLayers(uid, itemMapper, out var containedLayers)) - { - _appearance.SetData(uid, StorageMapVisuals.LayerChanged, new ShowLayerData(containedLayers), appearanceComponent); - } + _appearance.SetData(uid, + StorageMapVisuals.LayerChanged, + new ShowLayerData(containedLayers), + appearanceComponent); } + } - /// - /// Method that iterates over storage of the entity in and sets according to - /// definition. It will have O(n*m) time behavior (n - number of entities in container, and m - number of - /// definitions in . - /// - /// EntityUid used to search the storage - /// component that contains definition used to map whitelist in - /// mapLayers to string. - /// - /// list of layers that should be visible - /// false if msg.Container.Owner is not a storage, true otherwise. - private bool TryGetLayers(EntityUid uid, - ItemMapperComponent itemMapper, - out List showLayers) - { - var containedLayers = _container.GetAllContainers(uid) - .Where(c => itemMapper.ContainerWhitelist?.Contains(c.ID) ?? true).SelectMany(cont => cont.ContainedEntities).ToArray(); + /// + /// Method that iterates over storage of the entity in and sets + /// according to definition. It will have O(n*m) time behavior + /// (n - number of entities in container, and m - number of definitions in ). + /// + /// EntityUid used to search the storage + /// component that contains definition used to map + /// Whitelist in to string. + /// + /// list of layers that should be visible + /// false if msg.Container.Owner is not a storage, true otherwise. + private bool TryGetLayers(EntityUid uid, ItemMapperComponent itemMapper, out List showLayers) + { + var containedLayers = _container.GetAllContainers(uid) + .Where(c => itemMapper.ContainerWhitelist?.Contains(c.ID) ?? true) + .SelectMany(cont => cont.ContainedEntities) + .ToArray(); - var list = new List(); - foreach (var mapLayerData in itemMapper.MapLayers.Values) + var list = new List(); + foreach (var mapLayerData in itemMapper.MapLayers.Values) + { + var count = containedLayers.Count(ent => _whitelistSystem.IsWhitelistPassOrNull(mapLayerData.Whitelist, + ent)); + if (count >= mapLayerData.MinCount && count <= mapLayerData.MaxCount) { - var count = containedLayers.Count(ent => _whitelistSystem.IsWhitelistPassOrNull(mapLayerData.Whitelist, - ent)); - if (count >= mapLayerData.MinCount && count <= mapLayerData.MaxCount) - { - list.Add(mapLayerData.Layer); - } + list.Add(mapLayerData.Layer); } - - showLayers = list; - return true; } + + showLayers = list; + return true; } } From b143d1d7f791cb6e1fe2fb9eff4c7a2ac0173f90 Mon Sep 17 00:00:00 2001 From: Morb0 <14136326+Morb0@users.noreply.github.com> Date: Sun, 14 Jul 2024 14:57:18 +0300 Subject: [PATCH 76/78] Fix maps --- Resources/Maps/corvax_avrit.yml | 5 ----- Resources/Maps/corvax_maus.yml | 18 +----------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/Resources/Maps/corvax_avrit.yml b/Resources/Maps/corvax_avrit.yml index b7e10d0d461..61713a45d68 100644 --- a/Resources/Maps/corvax_avrit.yml +++ b/Resources/Maps/corvax_avrit.yml @@ -182228,13 +182228,8 @@ entities: - uid: 47177 components: - type: Transform - rot: -1.5707963267948966 rad pos: 9.863173,-20.066223 parent: 2 - - type: Spray - transferAmount: 0 - - type: FireExtinguisher - safety: False - uid: 67434 components: - type: Transform diff --git a/Resources/Maps/corvax_maus.yml b/Resources/Maps/corvax_maus.yml index 785171aecbc..c7f681b90ad 100644 --- a/Resources/Maps/corvax_maus.yml +++ b/Resources/Maps/corvax_maus.yml @@ -9886,14 +9886,6 @@ entities: parent: 5995 - type: InstantAction container: 5995 -- proto: ActionToggleMagbootsAdvanced - entities: - - uid: 12164 - components: - - type: Transform - parent: 12163 - - type: InstantAction - container: 12163 - proto: AirAlarm entities: - uid: 6 @@ -54123,16 +54115,8 @@ entities: components: - type: Transform parent: 11189 - - type: Magboots - toggleActionEntity: 12164 - type: Physics canCollide: False - - type: ActionsContainer - - type: ContainerContainer - containers: - actions: !type:Container - ents: - - 12164 - type: InsideEntityStorage - proto: ClothingShoesBootsMercFilled entities: @@ -91101,7 +91085,7 @@ entities: - stampedColor: '#FF33CCFF' stampedName: stamp-component-stamped-name-clown content: >- - Здраствуйте, это клоун. Я вернул вам ТЭГ потому что молитвы хонкоматери помогли мне! И я богатейший клоун из Хонкландии! у меня целых 228 триллионов спесо!!!! Но вам ег онужно собрать патамушта я иго спрятыл!! МВААХАХАХАХ + Здраствуйте, это клоун. Я вернул вам ТЭГ потому что молитвы хонкоматери помогли мне! И я богатейший клоун из Хонкландии! у меня целых 228 триллионов спесо!!!! Но вам ег онужно собрать патамушта я иго спрятыл!! МВААХАХАХАХ -Honk honkovich - uid: 10182 From d937534f479b69d8ae622d43d01cc92150b2e913 Mon Sep 17 00:00:00 2001 From: Morb0 <14136326+Morb0@users.noreply.github.com> Date: Sun, 14 Jul 2024 15:03:53 +0300 Subject: [PATCH 77/78] Update locale --- .../ss14-ru/prototypes/actions/ninja.ftl | 2 +- .../corvax/catalog/fills/crates/service.ftl | 2 + .../corvax/entities/objects/misc/books.ftl | 62 +++++++++++++++++++ .../entities/clothing/base_clothing.ftl | 2 + .../entities/clothing/head/misc.ftl | 5 ++ .../entities/clothing/shoes/magboots.ftl | 10 +-- .../entities/markers/spawners/ghost_roles.ftl | 5 +- .../prototypes/entities/mobs/player/human.ftl | 2 - .../prototypes/entities/mobs/player/vox.ftl | 2 +- .../objects/devices/base_handheld.ftl | 2 + .../entities/objects/devices/wristwatch.ftl | 4 ++ .../entities/objects/materials/ore.ftl | 6 ++ .../doors/windoors/base_structurewindoors.ftl | 8 +-- .../entities/structures/walls/asteroid.ftl | 24 +++++++ .../ss14-ru/prototypes/gamerules/midround.ftl | 2 - .../corvax/catalog/fills/crates/service.ftl | 2 + .../corvax/entities/objects/misc/books.ftl | 62 +++++++++++++++++++ .../objects/devices/base_handheld.ftl | 2 + .../entities/objects/devices/wristwatch.ftl | 4 ++ .../objects/weapons/throwable/grenades.ftl | 2 + 20 files changed, 188 insertions(+), 22 deletions(-) create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/corvax/catalog/fills/crates/service.ftl create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/corvax/entities/objects/misc/books.ftl create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/base_handheld.ftl create mode 100644 Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/wristwatch.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/catalog/fills/crates/service.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/objects/misc/books.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/base_handheld.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/wristwatch.ftl diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/actions/ninja.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/actions/ninja.ftl index 23565b0aff2..1835ecf109c 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/actions/ninja.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/actions/ninja.ftl @@ -1,5 +1,5 @@ ent-ActionToggleNinjaGloves = Toggle ninja gloves - .desc = Toggles all glove actions on left click. Includes your doorjack, draining power, stunning enemies, downloading research and calling in a threat. + .desc = Toggles all glove actions on left click. Includes your doorjack, draining power, stunning enemies and hacking certain computers. ent-ActionCreateThrowingStar = Create throwing star .desc = Channels suit power into creating a throwing star that deals extra stamina damage. ent-ActionRecallKatana = Recall katana diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/corvax/catalog/fills/crates/service.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/corvax/catalog/fills/crates/service.ftl new file mode 100644 index 00000000000..5858b28cf39 --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/corvax/catalog/fills/crates/service.ftl @@ -0,0 +1,2 @@ +ent-CrateServiceLoreBooks = ящик космологических книг + .desc = Ящик, содержащий книги по устройству мира, техники, политических тонкостях, ествественных закономерностях и об исторических событиях. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/corvax/entities/objects/misc/books.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/corvax/entities/objects/misc/books.ftl new file mode 100644 index 00000000000..1bd30001dbd --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/corvax/entities/objects/misc/books.ftl @@ -0,0 +1,62 @@ +ent-BookLoreBase = Лорные книги + .desc = Данные книги содержат в себе лор Корвакса. +ent-BookExpensiveCrystal = Кристаллы особой ценности + .desc = Данная книга рассказывает о материалах, которые в обиходе было принято называть Блюспейс и Редспейс кристаллами. +ent-BookClones = Туртельский Кризис + .desc = Данная книга рассказывает о восстании, которое устроили клоны в борьбе за равные права. +ent-BookDAM = Двигатель Антиматерии + .desc = Данная книга рассказывает о сути работы одного из самых распространённых источников электроэнергии на объектах НТ. +ent-BookFaks = Факс + .desc = Данная книга рассказывает о принципе работы Факса дальней связи. +ent-BookFamalis = Семьи Межпланетарного Братства + .desc = Данная книга рассказывает о самых крупных мафиозных кланах Галактики. +ent-BookGrav = Генератор искуственной Гравитации + .desc = Данная книга рассказывает о принципе работы Генераторов искусственной гравитации. +ent-BookHalifat = Нормы Космоислами и шесть стрел + .desc = Данная книга рассказывает о савокупности религиозных и светских догм, которые практикуются в государсвенном устройстве Халифатского Божественного Конгломирата. +ent-BookRedspace = Редспейс + .desc = Данная книга рассказывает о сути редспейса. +ent-BookShips = Классификация кораблей ОПЗ + .desc = Данная книга показывает классификацию флотов в ОПЗ. +ent-BookZP = Зарплатная система НТ + .desc = Данная книга рассказывает о том, как сотрудники проекта "Космическая станция 14" получают зарплату. +ent-BookUSSP = Политическая система СССП + .desc = Что там у них делается в этом их социализма... +ent-BookMirt = Культурный столп Империи Миртана + .desc = Может быть, если поняти их культуру, то можно и налажить связи? +ent-BookGior = ГИОР + .desc = Что такое Глобальная инициатива Объединённых Рас +ent-BookSNK = Внешняя политика СНК + .desc = Да, они наши враги, но стоит осознавать, кто за, а кто против них +ent-BookUmpor = Внешняя политика Умпорской Федерации + .desc = Наши соседи, что стараются улучшить взаимоотношения с нами. А как к ним относятся другие? +ent-BookNooc = Классовая структура Ноократии Эранта + .desc = Она наши соседи. Они наши коллги. Нужно понимать что там у них и как устроено... А то ещё оскорбишь кого... +ent-BookNakamura = Накамура Инжениринг + .desc = Чем занимаются в корпорации Большой Пятёрки накамура Инжениринг +ent-BookVitz = Космофлот корпорации Витезтви + .desc = Как комплектуются базовые соединение космофлота корпорации Большой Пятёрки Витезтви +ent-BookGefest = Компания Гефест + .desc = Немного фактов о корпорации Большой Пятёрки Гефест +ent-BookConarex = История компании Конарекс + .desc = Она и есть. История главного конкурента Космологистики в отрасли +ent-BookPetr = Идеи, цели и культруа Петрищевцев + .desc = Стоит прочитать дабы понять какими же глупыми идеалистами являются эти экстремисты-революционеры +ent-BookSaibasan = Активы компании Саибасан + .desc = рассказывается о том, где можно поработать в известнейшем поставщике электронных компонентов +ent-BookUnath1 = Культура унатхов (ч.1) + .desc = Первая часть великолепного ислледования этой замечательной расы +ent-BookUnath2 = Культура унатхов (ч.2) + .desc = Вторая часть великолепного ислледования этой прекрасной расы +ent-BookDione = Особенности биологического строения дион + .desc = Рассказ о необычном строении желудка, лёгких и мозга и их переплетение с растенеевидным телом +ent-BookBSS = Суть Блюспейс-Скачков + .desc = Объяснение механизма Блюспейс скачков, что позволяют нашим космическим кораблям преодалевать немыслимые рассстояния за крохотное время. +ent-BookScaf = Устройство скафандров утилизаторов + .desc = Тут повествуется о слоистой системе покрытий скафандров, что делают их такими прочными и безопасными +ent-BookMBS1 = Блюспейс-маяки (ч.1) + .desc = Как развивалась БС-технология дальних полётов? Как понять куда ты попадёшь? Об это рассказвается в данной книге. +ent-BookMBS2 = Блюспейс-маяки (ч.2) + .desc = Как развивалась БС-технология дальних полётов? Как понять куда ты попадёшь? Об это рассказвается в данной книге. +ent-BookImplants = Устройство имплантов НТ + .desc = Тут описаны принципы действия стандартный имплантов корпорации НаноТрейзен массового производства. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/base_clothing.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/base_clothing.ftl index 65bbe1b1040..5f530301543 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/base_clothing.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/base_clothing.ftl @@ -10,3 +10,5 @@ ent-ClothingSlotBase = { "" } .desc = { "" } ent-ContentsExplosionResistanceBase = { "" } .desc = { "" } +ent-BaseToggleClothing = { "" } + .desc = { "" } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/misc.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/misc.ftl index c2fbfa0ba72..341888a1634 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/misc.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/head/misc.ftl @@ -25,6 +25,11 @@ ent-ClothingHeadHatFancyCrown = fancy crown ent-ClothingHeadHatCatEars = cat ears .desc = NYAH! .suffix = DO NOT MAP +ent-ClothingHeadHatCatEarsValid = { ent-ClothingHeadHatCatEars } + .suffix = Valid, DO NOT MAP + .desc = { ent-ClothingHeadHatCatEars.desc } +ent-ActionBecomeValid = Become Valid + .desc = *notices your killsign* owo whats this ent-ClothingHeadHatDogEars = doggy ears .desc = Only for good boys. .suffix = DO NOT MAP diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/shoes/magboots.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/shoes/magboots.ftl index 946ef14d6b0..1602c9b85aa 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/shoes/magboots.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/clothing/shoes/magboots.ftl @@ -8,13 +8,5 @@ ent-ClothingShoesBootsMagBlinding = magboots of blinding speed .desc = These would look fetching on a fetcher like you. ent-ClothingShoesBootsMagSyndie = blood-red magboots .desc = Reverse-engineered magnetic boots that have a heavy magnetic pull and integrated thrusters. -ent-ActionBaseToggleMagboots = Toggle Magboots +ent-ActionToggleMagboots = Toggle Magboots .desc = Toggles the magboots on and off. -ent-ActionToggleMagboots = { ent-ActionBaseToggleMagboots } - .desc = { ent-ActionBaseToggleMagboots.desc } -ent-ActionToggleMagbootsAdvanced = { ent-ActionBaseToggleMagboots } - .desc = { ent-ActionBaseToggleMagboots.desc } -ent-ActionToggleMagbootsSci = { ent-ActionBaseToggleMagboots } - .desc = { ent-ActionBaseToggleMagboots.desc } -ent-ActionToggleMagbootsSyndie = { ent-ActionBaseToggleMagboots } - .desc = { ent-ActionBaseToggleMagboots.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/markers/spawners/ghost_roles.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/markers/spawners/ghost_roles.ftl index 8b7a5da047c..6edfc2fdab7 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/markers/spawners/ghost_roles.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/markers/spawners/ghost_roles.ftl @@ -22,6 +22,5 @@ ent-SpawnPointNukeopsOperative = { ent-SpawnPointLoneNukeOperative } .desc = { ent-SpawnPointLoneNukeOperative.desc } ent-SpawnPointGhostDragon = { ent-BaseAntagSpawner } .desc = { ent-BaseAntagSpawner.desc } -ent-SpawnPointGhostSpaceNinja = ghost role spawn point - .suffix = space ninja - .desc = { ent-MarkerBase.desc } +ent-SpawnPointGhostSpaceNinja = { ent-BaseAntagSpawner } + .desc = { ent-BaseAntagSpawner.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/human.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/human.ftl index 0328e828b6e..d3051c9ac44 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/human.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/human.ftl @@ -13,5 +13,3 @@ ent-MobHumanNukeOp = Nuclear Operative .desc = { ent-MobHuman.desc } ent-MobHumanLoneNuclearOperative = Lone Operative .desc = { ent-MobHuman.desc } -ent-MobHumanSpaceNinja = Space Ninja - .desc = { ent-MobHuman.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/vox.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/vox.ftl index a65a8396af6..0cf1b2c2f94 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/vox.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/mobs/player/vox.ftl @@ -1,2 +1,2 @@ -ent-MobVox = Urist McVox +ent-MobVox = Uristititi McVox .desc = { ent-BaseMobVox.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/base_handheld.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/base_handheld.ftl new file mode 100644 index 00000000000..69c4dc5e28d --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/base_handheld.ftl @@ -0,0 +1,2 @@ +ent-BaseHandheldComputer = { ent-BaseItem } + .desc = { ent-BaseItem.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/wristwatch.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/wristwatch.ftl new file mode 100644 index 00000000000..01978bc7bbc --- /dev/null +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/devices/wristwatch.ftl @@ -0,0 +1,4 @@ +ent-Wristwatch = wristwatch + .desc = A cheap watch for telling time. How much did you waste playing Space Station 14? +ent-WristwatchGold = gold watch + .desc = A fancy watch worth more than your kidney. It was owned by the notorious Syndicate mobster Vunibaldo "200 Pound Horse Meat Grinder" Frediani. diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/materials/ore.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/materials/ore.ftl index 261704b81e4..b6acd115997 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/materials/ore.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/objects/materials/ore.ftl @@ -6,6 +6,12 @@ ent-GoldOre = gold ore ent-GoldOre1 = { ent-GoldOre } .suffix = Single .desc = { ent-GoldOre.desc } +ent-DiamondOre = diamond ore + .suffix = Full + .desc = { ent-OreBase.desc } +ent-DiamondOre1 = { ent-DiamondOre } + .suffix = Single + .desc = { ent-DiamondOre.desc } ent-SteelOre = iron ore .suffix = Full .desc = { ent-OreBase.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/windoors/base_structurewindoors.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/windoors/base_structurewindoors.ftl index 8ac8ffb04d7..e9b6453c2b3 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/windoors/base_structurewindoors.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/doors/windoors/base_structurewindoors.ftl @@ -4,9 +4,9 @@ ent-BaseSecureWindoor = { ent-BaseWindoor } .desc = { ent-BaseWindoor.desc } ent-BasePlasmaWindoor = { ent-BaseWindoor } .desc = { ent-BaseWindoor.desc } -ent-BaseSecurePlasmaWindoor = { ent-BaseWindoor } - .desc = { ent-BaseWindoor.desc } +ent-BaseSecurePlasmaWindoor = { ent-BaseSecureWindoor } + .desc = { ent-BaseSecureWindoor.desc } ent-BaseUraniumWindoor = { ent-BaseWindoor } .desc = { ent-BaseWindoor.desc } -ent-BaseSecureUraniumWindoor = { ent-BaseWindoor } - .desc = { ent-BaseWindoor.desc } +ent-BaseSecureUraniumWindoor = { ent-BaseSecureWindoor } + .desc = { ent-BaseSecureWindoor.desc } diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/walls/asteroid.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/walls/asteroid.ftl index 6e8e191bd1a..829a9b3ad87 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/walls/asteroid.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/entities/structures/walls/asteroid.ftl @@ -6,6 +6,9 @@ ent-AsteroidRockCoal = { ent-AsteroidRock } ent-AsteroidRockGold = { ent-AsteroidRock } .desc = An ore vein rich with gold. .suffix = Gold +ent-AsteroidRockDiamond = { ent-AsteroidRock } + .desc = An ore vein rich with diamonds. + .suffix = Diamond ent-AsteroidRockPlasma = { ent-AsteroidRock } .desc = An ore vein rich with plasma. .suffix = Plasma @@ -81,6 +84,9 @@ ent-IronRockSalt = { ent-IronRock } ent-IronRockArtifactFragment = { ent-IronRock } .desc = A rock wall. What's that sticking out of it? .suffix = Artifact Fragment +ent-IronRockDiamond = { ent-IronRock } + .desc = An ore vein rich with diamonds. + .suffix = Diamond ent-WallRock = rock .suffix = planetmap .desc = { ent-BaseWall.desc } @@ -90,6 +96,9 @@ ent-WallRockCoal = { ent-WallRock } ent-WallRockGold = { ent-WallRock } .desc = An ore vein rich with gold. .suffix = Gold +ent-WallRockDiamond = { ent-WallRock } + .desc = An ore vein rich with diamonds. + .suffix = Diamond ent-WallRockPlasma = { ent-WallRock } .desc = An ore vein rich with plasma. .suffix = Plasma @@ -122,6 +131,9 @@ ent-WallRockBasaltCoal = { ent-WallRockBasalt } ent-WallRockBasaltGold = { ent-WallRockBasalt } .desc = An ore vein rich with gold. .suffix = Gold +ent-WallRockBasaltDiamond = { ent-WallRockBasalt } + .desc = An ore vein rich with diamonds. + .suffix = Diamond ent-WallRockBasaltPlasma = { ent-WallRockBasalt } .desc = An ore vein rich with plasma. .suffix = Plasma @@ -154,6 +166,9 @@ ent-WallRockSnowCoal = { ent-WallRockSnow } ent-WallRockSnowGold = { ent-WallRockSnow } .desc = An ore vein rich with gold. .suffix = Gold +ent-WallRockSnowDiamond = { ent-WallRockSnow } + .desc = An ore vein rich with diamonds. + .suffix = Diamond ent-WallRockSnowPlasma = { ent-WallRockSnow } .desc = An ore vein rich with plasma. .suffix = Plasma @@ -186,6 +201,9 @@ ent-WallRockSandCoal = { ent-WallRockSand } ent-WallRockSandGold = { ent-WallRockSand } .desc = An ore vein rich with gold. .suffix = Gold +ent-WallRockSandDiamond = { ent-WallRockSand } + .desc = An ore vein rich with diamonds. + .suffix = Diamond ent-WallRockSandPlasma = { ent-WallRockSand } .desc = An ore vein rich with plasma. .suffix = Plasma @@ -218,6 +236,9 @@ ent-WallRockChromiteCoal = { ent-WallRockChromite } ent-WallRockChromiteGold = { ent-WallRockChromite } .desc = An ore vein rich with gold. .suffix = Gold +ent-WallRockChromiteDiamond = { ent-WallRockChromite } + .desc = An ore vein rich with diamonds. + .suffix = Diamond ent-WallRockChromitePlasma = { ent-WallRockChromite } .desc = An ore vein rich with plasma. .suffix = Plasma @@ -250,6 +271,9 @@ ent-WallRockAndesiteCoal = { ent-WallRockAndesite } ent-WallRockAndesiteGold = { ent-WallRockAndesite } .desc = An ore vein rich with gold. .suffix = Gold +ent-WallRockAndesiteDiamond = { ent-WallRockAndesite } + .desc = An ore vein rich with diamonds. + .suffix = Diamond ent-WallRockAndesitePlasma = { ent-WallRockAndesite } .desc = An ore vein rich with plasma. .suffix = Plasma diff --git a/Resources/Locale/en-US/ss14-ru/prototypes/gamerules/midround.ftl b/Resources/Locale/en-US/ss14-ru/prototypes/gamerules/midround.ftl index ffd302b5a10..7416fb5c77f 100644 --- a/Resources/Locale/en-US/ss14-ru/prototypes/gamerules/midround.ftl +++ b/Resources/Locale/en-US/ss14-ru/prototypes/gamerules/midround.ftl @@ -1,4 +1,2 @@ -ent-Ninja = { ent-BaseGameRule } - .desc = { ent-BaseGameRule.desc } ent-Thief = { ent-BaseGameRule } .desc = { ent-BaseGameRule.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/catalog/fills/crates/service.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/catalog/fills/crates/service.ftl new file mode 100644 index 00000000000..5858b28cf39 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/catalog/fills/crates/service.ftl @@ -0,0 +1,2 @@ +ent-CrateServiceLoreBooks = ящик космологических книг + .desc = Ящик, содержащий книги по устройству мира, техники, политических тонкостях, ествественных закономерностях и об исторических событиях. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/objects/misc/books.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/objects/misc/books.ftl new file mode 100644 index 00000000000..1bd30001dbd --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/corvax/entities/objects/misc/books.ftl @@ -0,0 +1,62 @@ +ent-BookLoreBase = Лорные книги + .desc = Данные книги содержат в себе лор Корвакса. +ent-BookExpensiveCrystal = Кристаллы особой ценности + .desc = Данная книга рассказывает о материалах, которые в обиходе было принято называть Блюспейс и Редспейс кристаллами. +ent-BookClones = Туртельский Кризис + .desc = Данная книга рассказывает о восстании, которое устроили клоны в борьбе за равные права. +ent-BookDAM = Двигатель Антиматерии + .desc = Данная книга рассказывает о сути работы одного из самых распространённых источников электроэнергии на объектах НТ. +ent-BookFaks = Факс + .desc = Данная книга рассказывает о принципе работы Факса дальней связи. +ent-BookFamalis = Семьи Межпланетарного Братства + .desc = Данная книга рассказывает о самых крупных мафиозных кланах Галактики. +ent-BookGrav = Генератор искуственной Гравитации + .desc = Данная книга рассказывает о принципе работы Генераторов искусственной гравитации. +ent-BookHalifat = Нормы Космоислами и шесть стрел + .desc = Данная книга рассказывает о савокупности религиозных и светских догм, которые практикуются в государсвенном устройстве Халифатского Божественного Конгломирата. +ent-BookRedspace = Редспейс + .desc = Данная книга рассказывает о сути редспейса. +ent-BookShips = Классификация кораблей ОПЗ + .desc = Данная книга показывает классификацию флотов в ОПЗ. +ent-BookZP = Зарплатная система НТ + .desc = Данная книга рассказывает о том, как сотрудники проекта "Космическая станция 14" получают зарплату. +ent-BookUSSP = Политическая система СССП + .desc = Что там у них делается в этом их социализма... +ent-BookMirt = Культурный столп Империи Миртана + .desc = Может быть, если поняти их культуру, то можно и налажить связи? +ent-BookGior = ГИОР + .desc = Что такое Глобальная инициатива Объединённых Рас +ent-BookSNK = Внешняя политика СНК + .desc = Да, они наши враги, но стоит осознавать, кто за, а кто против них +ent-BookUmpor = Внешняя политика Умпорской Федерации + .desc = Наши соседи, что стараются улучшить взаимоотношения с нами. А как к ним относятся другие? +ent-BookNooc = Классовая структура Ноократии Эранта + .desc = Она наши соседи. Они наши коллги. Нужно понимать что там у них и как устроено... А то ещё оскорбишь кого... +ent-BookNakamura = Накамура Инжениринг + .desc = Чем занимаются в корпорации Большой Пятёрки накамура Инжениринг +ent-BookVitz = Космофлот корпорации Витезтви + .desc = Как комплектуются базовые соединение космофлота корпорации Большой Пятёрки Витезтви +ent-BookGefest = Компания Гефест + .desc = Немного фактов о корпорации Большой Пятёрки Гефест +ent-BookConarex = История компании Конарекс + .desc = Она и есть. История главного конкурента Космологистики в отрасли +ent-BookPetr = Идеи, цели и культруа Петрищевцев + .desc = Стоит прочитать дабы понять какими же глупыми идеалистами являются эти экстремисты-революционеры +ent-BookSaibasan = Активы компании Саибасан + .desc = рассказывается о том, где можно поработать в известнейшем поставщике электронных компонентов +ent-BookUnath1 = Культура унатхов (ч.1) + .desc = Первая часть великолепного ислледования этой замечательной расы +ent-BookUnath2 = Культура унатхов (ч.2) + .desc = Вторая часть великолепного ислледования этой прекрасной расы +ent-BookDione = Особенности биологического строения дион + .desc = Рассказ о необычном строении желудка, лёгких и мозга и их переплетение с растенеевидным телом +ent-BookBSS = Суть Блюспейс-Скачков + .desc = Объяснение механизма Блюспейс скачков, что позволяют нашим космическим кораблям преодалевать немыслимые рассстояния за крохотное время. +ent-BookScaf = Устройство скафандров утилизаторов + .desc = Тут повествуется о слоистой системе покрытий скафандров, что делают их такими прочными и безопасными +ent-BookMBS1 = Блюспейс-маяки (ч.1) + .desc = Как развивалась БС-технология дальних полётов? Как понять куда ты попадёшь? Об это рассказвается в данной книге. +ent-BookMBS2 = Блюспейс-маяки (ч.2) + .desc = Как развивалась БС-технология дальних полётов? Как понять куда ты попадёшь? Об это рассказвается в данной книге. +ent-BookImplants = Устройство имплантов НТ + .desc = Тут описаны принципы действия стандартный имплантов корпорации НаноТрейзен массового производства. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/base_handheld.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/base_handheld.ftl new file mode 100644 index 00000000000..69c4dc5e28d --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/base_handheld.ftl @@ -0,0 +1,2 @@ +ent-BaseHandheldComputer = { ent-BaseItem } + .desc = { ent-BaseItem.desc } diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/wristwatch.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/wristwatch.ftl new file mode 100644 index 00000000000..01978bc7bbc --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/wristwatch.ftl @@ -0,0 +1,4 @@ +ent-Wristwatch = wristwatch + .desc = A cheap watch for telling time. How much did you waste playing Space Station 14? +ent-WristwatchGold = gold watch + .desc = A fancy watch worth more than your kidney. It was owned by the notorious Syndicate mobster Vunibaldo "200 Pound Horse Meat Grinder" Frediani. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/throwable/grenades.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/throwable/grenades.ftl index 009eedd64a4..8690ff4168b 100644 --- a/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/throwable/grenades.ftl +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/weapons/throwable/grenades.ftl @@ -41,4 +41,6 @@ ent-GrenadeDummy = граната-обманка # Corvax-HiddenDesc-Start ent-SyndieTrickyBomb = хитроумная бомба .desc = Взрывное устройство, больше отвлекающее внимание, чем наносящее реальный вред. + # Corvax-HiddenDesc-End + From 88e5b8b7c416b440163d824fbc7a982cd4bac871 Mon Sep 17 00:00:00 2001 From: Morb0 <14136326+Morb0@users.noreply.github.com> Date: Sun, 14 Jul 2024 15:05:55 +0300 Subject: [PATCH 78/78] Fix books --- .../Corvax/Entities/Objects/Misc/books.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Resources/Prototypes/Corvax/Entities/Objects/Misc/books.yml b/Resources/Prototypes/Corvax/Entities/Objects/Misc/books.yml index ae94d878fad..e67d414c765 100644 --- a/Resources/Prototypes/Corvax/Entities/Objects/Misc/books.yml +++ b/Resources/Prototypes/Corvax/Entities/Objects/Misc/books.yml @@ -1164,13 +1164,13 @@ Следующие итерацией развития скафандров после достижения пределов серией EVA стала разработка принципиально новой серии скафандров. И первым в серии стал скафандр для работы в открытом космосе. Это было совершенно необходимо для строительство новых космических объектов прямо в космосе, для проведения диагностики внешних покровов космических объектов, а также для прочих работ. Скафандр конструировали таким образом, чтобы погасить большую часть вероятных рисков космоса. Из-за этого скафандр насчитывал четыре слоя - • 1) Дереакционное покрытие. Покрытие, сделанное из слабоактивных композитов, что предотвращает деградацию других слоёв, при попадании скафандра в условия действия кислотной атмосферы. + 1) Дереакционное покрытие. Покрытие, сделанное из слабоактивных композитов, что предотвращает деградацию других слоёв, при попадании скафандра в условия действия кислотной атмосферы. - • 2) Бронированный слой. Второй внешний стальной пластинчатый слой, который должен был защитить внутренние слои и оператора от скоростной встречи даже со средним космическим мусором, а также мог спасти жизнь, если происходила авария, сопровождающаяся взрывом. + 2) Бронированный слой. Второй внешний стальной пластинчатый слой, который должен был защитить внутренние слои и оператора от скоростной встречи даже со средним космическим мусором, а также мог спасти жизнь, если происходила авария, сопровождающаяся взрывом. - • 3) Антирадиационный экран. Сделан из листового свинца. Третий слой. Тяжёлый, но дешёвый и блокирует около половины всей космической радиации практически от любой звезды или иного источника. + 3) Антирадиационный экран. Сделан из листового свинца. Третий слой. Тяжёлый, но дешёвый и блокирует около половины всей космической радиации практически от любой звезды или иного источника. - • 4) Внутренний слой. Выполнен из достаточно армированного прорезиненного композита. Он препятствует переохлаждению. А также может защитить от ударов о небольшие предметы. + 4) Внутренний слой. Выполнен из достаточно армированного прорезиненного композита. Он препятствует переохлаждению. А также может защитить от ударов о небольшие предметы. Создатели вдохновлялись Земными латными доспехами эпохи позднего Средневековья. Пластинчатое покрытие позволяло быть защищённым от крупных ударов и при этом иметь определённую мобильность. Но при этом от удара, скажем, ножа или укуса кого-нибудь хищного защищал в результате только внутренний крепкий, но не непробиваемый слой. Впрочем... где в космосе можно попасть на нож или клык… Помимо этого, скафандр вышел банально слишком тяжёлым. В нём неудобно было заниматься строительством, даже в условиях невесомости. В целом разработка не была признана провальной, но требовала доработки. Да и вообще руководство НТ считало эти скафандры лучше серии EVA, а потому развернуло массовое производство, но дала команду конструкторам облегчить модель, добавив больше удобства. @@ -1201,13 +1201,13 @@ Суперкомпьютер располагался на важных космических узлах. Например, на транспортных станция и космопортах. Результаты его использования вдохнули в идеи покорения дальнего космоса новую жизнь. Расчёты получались столь точны, что расхождение в координатах назначенных и полученных составляли всего несколько километров. При этом суперкомпьютер обладал и существенными недостатками - 1. Время расчёта скачка порой составляло несколько суток. (И для каждого корабля группы отдельно) + 1. Время расчёта скачка порой составляло несколько суток. (И для каждого корабля группы отдельно) - 2. Производство суперкомпьютера было сверхдорогим и очень долгим. + 2. Производство суперкомпьютера было сверхдорогим и очень долгим. - 3. Использование суперкомпьютера было сопряжено с значительными энергозатратами, а именно требовалась небольшая электростанция. + 3. Использование суперкомпьютера было сопряжено с значительными энергозатратами, а именно требовалась небольшая электростанция. - 4. Он мог отправить только в одну сторону, но не мог вернуть. + 4. Он мог отправить только в одну сторону, но не мог вернуть. Был совершён эксперимент, при котором суперкомпьютер отправлял шаттл с таким же суперкомпьютеров целевую систему, затем требовалось развернуть в целевой системе электростанцию, подстанцию, коммуникации и установить суперкомпьютер. Идея была чрез чур авантюрно с самого начала. Требовалось быстро возвести космическую станцию, способную обслуживать столь ценное и сложное оборудование. Любой незначительный негативный фактор мог всё испортить.