From 0b448b500dfa6ee6a45181904a1ee56095085dd5 Mon Sep 17 00:00:00 2001 From: Visne Date: Sun, 16 Aug 2020 14:54:52 +0200 Subject: [PATCH 01/53] Merge MathHelper and FloatMath Requires space-wizards/RobustToolbox#1234 --- Content.Client/Chat/SpeechBubble.cs | 6 +++--- .../GameObjects/Components/Disposal/DisposalUnitWindow.cs | 4 ++-- .../GameObjects/Components/HandheldLightComponent.cs | 2 +- .../GameObjects/Components/MagicMirrorBoundUserInterface.cs | 2 +- .../GameObjects/Components/Mobs/CameraRecoilComponent.cs | 2 +- .../Components/Mobs/ClientStatusEffectsComponent.cs | 2 +- .../GameObjects/Components/Power/ApcBoundUserInterface.cs | 4 ++-- .../GameObjects/Components/Weapons/FlashableComponent.cs | 2 +- Content.Client/UserInterface/CooldownGraphic.cs | 2 +- Content.Client/UserInterface/ItemSlotManager.cs | 2 +- Content.Server/AI/Utility/Considerations/Consideration.cs | 6 +++--- Content.Server/Atmos/HighPressureMovementController.cs | 4 ++-- Content.Server/Atmos/TileAtmosphere.cs | 4 ++-- .../GameObjects/Components/Power/BatteryComponent.cs | 2 +- .../Weapon/Ranged/Barrels/ServerRangedBarrelComponent.cs | 2 +- .../EntitySystems/AI/Steering/AiSteeringSystem.cs | 2 +- Content.Server/GameTicking/GamePresets/PresetSuspicion.cs | 4 ++-- Content.Server/Preferences/PreferencesDatabase.cs | 2 +- 18 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Content.Client/Chat/SpeechBubble.cs b/Content.Client/Chat/SpeechBubble.cs index 715715d9a1..bffe42c721 100644 --- a/Content.Client/Chat/SpeechBubble.cs +++ b/Content.Client/Chat/SpeechBubble.cs @@ -106,13 +106,13 @@ namespace Content.Client.Chat } // Lerp to our new vertical offset if it's been modified. - if (FloatMath.CloseTo(_verticalOffsetAchieved - VerticalOffset, 0, 0.1)) + if (MathHelper.CloseTo(_verticalOffsetAchieved - VerticalOffset, 0, 0.1)) { _verticalOffsetAchieved = VerticalOffset; } else { - _verticalOffsetAchieved = FloatMath.Lerp(_verticalOffsetAchieved, VerticalOffset, 10 * args.DeltaSeconds); + _verticalOffsetAchieved = MathHelper.Lerp(_verticalOffsetAchieved, VerticalOffset, 10 * args.DeltaSeconds); } var worldPos = _senderEntity.Transform.WorldPosition; @@ -122,7 +122,7 @@ namespace Content.Client.Chat var screenPos = lowerCenter - (Width / 2, ContentHeight + _verticalOffsetAchieved); LayoutContainer.SetPosition(this, screenPos); - var height = FloatMath.Clamp(lowerCenter.Y - screenPos.Y, 0, ContentHeight); + var height = MathHelper.Clamp(lowerCenter.Y - screenPos.Y, 0, ContentHeight); LayoutContainer.SetSize(this, (Size.X, height)); } diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalUnitWindow.cs b/Content.Client/GameObjects/Components/Disposal/DisposalUnitWindow.cs index 62e6bd1a24..98bf7af37c 100644 --- a/Content.Client/GameObjects/Components/Disposal/DisposalUnitWindow.cs +++ b/Content.Client/GameObjects/Components/Disposal/DisposalUnitWindow.cs @@ -113,12 +113,12 @@ namespace Content.Client.GameObjects.Components.Disposal if (normalized <= leftSideSize) { normalized /= leftSideSize; // Adjust range to 0.0 to 1.0 - finalHue = FloatMath.Lerp(leftHue, middleHue, normalized); + finalHue = MathHelper.Lerp(leftHue, middleHue, normalized); } else { normalized = (normalized - leftSideSize) / rightSideSize; // Adjust range to 0.0 to 1.0. - finalHue = FloatMath.Lerp(middleHue, rightHue, normalized); + finalHue = MathHelper.Lerp(middleHue, rightHue, normalized); } // Check if null first to avoid repeatedly creating this. diff --git a/Content.Client/GameObjects/Components/HandheldLightComponent.cs b/Content.Client/GameObjects/Components/HandheldLightComponent.cs index d6f4a75aaa..2b2d43b1a7 100644 --- a/Content.Client/GameObjects/Components/HandheldLightComponent.cs +++ b/Content.Client/GameObjects/Components/HandheldLightComponent.cs @@ -78,7 +78,7 @@ namespace Content.Client.GameObjects.Components int level; - if (FloatMath.CloseTo(charge, 0)) + if (MathHelper.CloseTo(charge, 0)) { level = 0; } diff --git a/Content.Client/GameObjects/Components/MagicMirrorBoundUserInterface.cs b/Content.Client/GameObjects/Components/MagicMirrorBoundUserInterface.cs index aea3434e61..440b9f1d2d 100644 --- a/Content.Client/GameObjects/Components/MagicMirrorBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/MagicMirrorBoundUserInterface.cs @@ -243,7 +243,7 @@ namespace Content.Client.GameObjects.Components if (int.TryParse(ev.Text, out var result)) { - result = FloatMath.Clamp(result, 0, byte.MaxValue); + result = MathHelper.Clamp(result, 0, byte.MaxValue); _ignoreEvents = true; _colorValue = (byte) result; diff --git a/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs b/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs index 68288ff66a..7cda73f26b 100644 --- a/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs +++ b/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs @@ -87,7 +87,7 @@ namespace Content.Client.GameObjects.Components.Mobs // Continually restore camera to 0. var normalized = _currentKick.Normalized; _lastKickTime += frameTime; - var restoreRate = FloatMath.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, _lastKickTime/RestoreRateRamp)); + var restoreRate = MathHelper.Lerp(RestoreRateMin, RestoreRateMax, Math.Min(1, _lastKickTime/RestoreRateRamp)); var restore = normalized * restoreRate * frameTime; var (x, y) = _currentKick - restore; if (Math.Sign(x) != Math.Sign(_currentKick.X)) diff --git a/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs b/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs index 926e6a5c46..8c9344d5fd 100644 --- a/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs +++ b/Content.Client/GameObjects/Components/Mobs/ClientStatusEffectsComponent.cs @@ -152,7 +152,7 @@ namespace Content.Client.GameObjects.Components.Mobs var progress = (_gameTiming.CurTime - start).TotalSeconds / length; var ratio = (progress <= 1 ? (1 - progress) : (_gameTiming.CurTime - end).TotalSeconds * -5); - cooldownGraphic.Progress = FloatMath.Clamp((float)ratio, -1, 1); + cooldownGraphic.Progress = MathHelper.Clamp((float)ratio, -1, 1); cooldownGraphic.Visible = ratio > -1f; } } diff --git a/Content.Client/GameObjects/Components/Power/ApcBoundUserInterface.cs b/Content.Client/GameObjects/Components/Power/ApcBoundUserInterface.cs index 616997820e..108b85fe55 100644 --- a/Content.Client/GameObjects/Components/Power/ApcBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/Power/ApcBoundUserInterface.cs @@ -86,12 +86,12 @@ namespace Content.Client.GameObjects.Components.Power if (normalizedCharge <= leftSideSize) { normalizedCharge /= leftSideSize; // Adjust range to 0.0 to 1.0 - finalHue = FloatMath.Lerp(leftHue, middleHue, normalizedCharge); + finalHue = MathHelper.Lerp(leftHue, middleHue, normalizedCharge); } else { normalizedCharge = (normalizedCharge - leftSideSize) / rightSideSize; // Adjust range to 0.0 to 1.0. - finalHue = FloatMath.Lerp(middleHue, rightHue, normalizedCharge); + finalHue = MathHelper.Lerp(middleHue, rightHue, normalizedCharge); } // Check if null first to avoid repeatedly creating this. diff --git a/Content.Client/GameObjects/Components/Weapons/FlashableComponent.cs b/Content.Client/GameObjects/Components/Weapons/FlashableComponent.cs index 18047a8f85..5e8573034a 100644 --- a/Content.Client/GameObjects/Components/Weapons/FlashableComponent.cs +++ b/Content.Client/GameObjects/Components/Weapons/FlashableComponent.cs @@ -141,7 +141,7 @@ namespace Content.Client.GameObjects.Components.Weapons const float xOffset = 0.0f; // Overkill but easy to adjust if you want to mess around with the design - var result = (float) FloatMath.Clamp(slope * (float) Math.Pow(ratio - xOffset, exponent) + yOffset, 0.0, 1.0); + var result = (float) MathHelper.Clamp(slope * (float) Math.Pow(ratio - xOffset, exponent) + yOffset, 0.0, 1.0); DebugTools.Assert(!float.IsNaN(result)); return result; } diff --git a/Content.Client/UserInterface/CooldownGraphic.cs b/Content.Client/UserInterface/CooldownGraphic.cs index 777ec9e1d9..a092cf425b 100644 --- a/Content.Client/UserInterface/CooldownGraphic.cs +++ b/Content.Client/UserInterface/CooldownGraphic.cs @@ -41,7 +41,7 @@ namespace Content.Client.UserInterface } else { - var alpha = FloatMath.Clamp(0.5f * lerp, 0f, 0.5f); + var alpha = MathHelper.Clamp(0.5f * lerp, 0f, 0.5f); color = new Color(1f, 1f, 1f, alpha); } diff --git a/Content.Client/UserInterface/ItemSlotManager.cs b/Content.Client/UserInterface/ItemSlotManager.cs index d7d4c1e78d..2ac6588f73 100644 --- a/Content.Client/UserInterface/ItemSlotManager.cs +++ b/Content.Client/UserInterface/ItemSlotManager.cs @@ -107,7 +107,7 @@ namespace Content.Client.UserInterface var progress = (_gameTiming.CurTime - start).TotalSeconds / length; var ratio = (progress <= 1 ? (1 - progress) : (_gameTiming.CurTime - end).TotalSeconds * -5); - cooldownDisplay.Progress = FloatMath.Clamp((float)ratio, -1, 1); + cooldownDisplay.Progress = MathHelper.Clamp((float)ratio, -1, 1); if (ratio > -1f) { diff --git a/Content.Server/AI/Utility/Considerations/Consideration.cs b/Content.Server/AI/Utility/Considerations/Consideration.cs index 20afc4dec1..6fc9966607 100644 --- a/Content.Server/AI/Utility/Considerations/Consideration.cs +++ b/Content.Server/AI/Utility/Considerations/Consideration.cs @@ -17,7 +17,7 @@ namespace Content.Server.AI.Utility.Considerations var modificationFactor = 1.0f - 1.0f / considerationsCount; var makeUpValue = (1.0f - score) * modificationFactor; var adjustedScore = score + makeUpValue * score; - return FloatMath.Clamp(adjustedScore, 0.0f, 1.0f); + return MathHelper.Clamp(adjustedScore, 0.0f, 1.0f); } [Pure] @@ -59,7 +59,7 @@ namespace Content.Server.AI.Utility.Considerations [Pure] private static float LogisticCurve(float x, float slope, float exponent, float yOffset, float xOffset) { - return FloatMath.Clamp( + return MathHelper.Clamp( exponent * (1 / (1 + (float) Math.Pow(Math.Log(1000) * slope, -1 * x + xOffset))) + yOffset, 0.0f, 1.0f); } @@ -77,7 +77,7 @@ namespace Content.Server.AI.Utility.Considerations [Pure] private static float QuadraticCurve(float x, float slope, float exponent, float yOffset, float xOffset) { - return FloatMath.Clamp(slope * (float) Math.Pow(x - xOffset, exponent) + yOffset, 0.0f, 1.0f); + return MathHelper.Clamp(slope * (float) Math.Pow(x - xOffset, exponent) + yOffset, 0.0f, 1.0f); } public Func QuadraticCurve(Blackboard context, float slope, float exponent, float yOffset, float xOffset) diff --git a/Content.Server/Atmos/HighPressureMovementController.cs b/Content.Server/Atmos/HighPressureMovementController.cs index 2e27f513ad..bd9f6b3e38 100644 --- a/Content.Server/Atmos/HighPressureMovementController.cs +++ b/Content.Server/Atmos/HighPressureMovementController.cs @@ -53,14 +53,14 @@ namespace Content.Server.Atmos { if (throwTarget != GridCoordinates.InvalidGrid) { - var moveForce = maxForce * FloatMath.Clamp(moveProb, 0, 100) / 150f; + var moveForce = maxForce * MathHelper.Clamp(moveProb, 0, 100) / 150f; var pos = ((throwTarget.Position - transform.GridPosition.Position).Normalized + direction.ToVec()).Normalized; LinearVelocity = pos * moveForce; } else { - var moveForce = MathF.Min(maxForce * FloatMath.Clamp(moveProb, 0, 100) / 2500f, 20f); + var moveForce = MathF.Min(maxForce * MathHelper.Clamp(moveProb, 0, 100) / 2500f, 20f); LinearVelocity = direction.ToVec() * moveForce; } diff --git a/Content.Server/Atmos/TileAtmosphere.cs b/Content.Server/Atmos/TileAtmosphere.cs index 8e634a1c7d..9cf67015ac 100644 --- a/Content.Server/Atmos/TileAtmosphere.cs +++ b/Content.Server/Atmos/TileAtmosphere.cs @@ -171,7 +171,7 @@ namespace Content.Server.Atmos { if(_soundCooldown == 0) EntitySystem.Get().PlayAtCoords("/Audio/Effects/space_wind.ogg", - GridIndices.ToGridCoordinates(_mapManager, GridIndex), AudioHelpers.WithVariation(0.125f).WithVolume(FloatMath.Clamp(PressureDifference / 10, 10, 100))); + GridIndices.ToGridCoordinates(_mapManager, GridIndex), AudioHelpers.WithVariation(0.125f).WithVolume(MathHelper.Clamp(PressureDifference / 10, 10, 100))); } @@ -1021,7 +1021,7 @@ namespace Content.Server.Atmos private void HandleDecompressionFloorRip(float sum) { - var chance = FloatMath.Clamp(sum / 500, 0.005f, 0.5f); + var chance = MathHelper.Clamp(sum / 500, 0.005f, 0.5f); if (sum > 20 && _robustRandom.Prob(chance)) _gridAtmosphereComponent.PryTile(GridIndices); } diff --git a/Content.Server/GameObjects/Components/Power/BatteryComponent.cs b/Content.Server/GameObjects/Components/Power/BatteryComponent.cs index 77ce70f0d4..de481e77ec 100644 --- a/Content.Server/GameObjects/Components/Power/BatteryComponent.cs +++ b/Content.Server/GameObjects/Components/Power/BatteryComponent.cs @@ -100,7 +100,7 @@ namespace Content.Server.GameObjects.Components.Power private void SetCurrentCharge(float newChargeAmount) { - _currentCharge = FloatMath.Clamp(newChargeAmount, 0, MaxCharge); + _currentCharge = MathHelper.Clamp(newChargeAmount, 0, MaxCharge); UpdateStorageState(); OnChargeChanged(); } diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerRangedBarrelComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerRangedBarrelComponent.cs index 4b0553fe2b..61cecee6c2 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerRangedBarrelComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerRangedBarrelComponent.cs @@ -178,7 +178,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { var currentTime = _gameTiming.CurTime; var timeSinceLastFire = (currentTime - _lastFire).TotalSeconds; - var newTheta = FloatMath.Clamp(_currentAngle.Theta + _angleIncrease - _angleDecay * timeSinceLastFire, _minAngle.Theta, _maxAngle.Theta); + var newTheta = MathHelper.Clamp(_currentAngle.Theta + _angleIncrease - _angleDecay * timeSinceLastFire, _minAngle.Theta, _maxAngle.Theta); _currentAngle = new Angle(newTheta); var random = (_robustRandom.NextDouble() - 0.5) * 2; diff --git a/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs b/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs index a9a7899300..2d955dbe82 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Steering/AiSteeringSystem.cs @@ -647,7 +647,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Steering var additionalVector = (centerGrid.Position - entityGridCoords.Position); var distance = additionalVector.Length; // If we're too far no point, if we're close then cap it at the normalized vector - distance = FloatMath.Clamp(2.5f - distance, 0.0f, 1.0f); + distance = MathHelper.Clamp(2.5f - distance, 0.0f, 1.0f); additionalVector = new Angle(90 * distance).RotateVec(additionalVector); avoidanceVector += additionalVector; // if we do need to avoid that means we'll have to lookahead for the next tile diff --git a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs index bd2f43a13d..093135da42 100644 --- a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs +++ b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs @@ -56,8 +56,8 @@ namespace Content.Server.GameTicking.GamePresets } } - var numTraitors = FloatMath.Clamp(readyPlayers.Count % PlayersPerTraitor, - MinTraitors, readyPlayers.Count); + var numTraitors = MathHelper.Clamp(readyPlayers.Count % PlayersPerTraitor, + MinTraitors, readyPlayers.Count); for (var i = 0; i < numTraitors; i++) { diff --git a/Content.Server/Preferences/PreferencesDatabase.cs b/Content.Server/Preferences/PreferencesDatabase.cs index 8f13a1324e..e4a0b4176f 100644 --- a/Content.Server/Preferences/PreferencesDatabase.cs +++ b/Content.Server/Preferences/PreferencesDatabase.cs @@ -60,7 +60,7 @@ namespace Content.Server.Preferences await _prefsSemaphore.WaitAsync(); try { - index = FloatMath.Clamp(index, 0, _maxCharacterSlots - 1); + index = MathHelper.Clamp(index, 0, _maxCharacterSlots - 1); await _prefsDb.SaveSelectedCharacterIndex(username, index); } finally From b8fef91922fb67758b4879d09dd784f970703586 Mon Sep 17 00:00:00 2001 From: nuke <47336974+nuke-makes-games@users.noreply.github.com> Date: Wed, 19 Aug 2020 02:54:10 -0400 Subject: [PATCH 02/53] Random events no longer start while in lobby and round ending forces current event to end (#1782) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Random events cannot run in lobby and round ending forces events to end. * Thanks p4merge * Get rid of unused * Apply suggestions from code review Co-authored-by: Víctor Aguilera Puerto <6766154+Zumorica@users.noreply.github.com> --- .../StationEvents/StationEventSystem.cs | 27 ++++++++++++++----- .../StationEvents/StationEventCommand.cs | 1 - 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/Content.Server/GameObjects/EntitySystems/StationEvents/StationEventSystem.cs b/Content.Server/GameObjects/EntitySystems/StationEvents/StationEventSystem.cs index 48786d52dc..c34a502b21 100644 --- a/Content.Server/GameObjects/EntitySystems/StationEvents/StationEventSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/StationEvents/StationEventSystem.cs @@ -1,4 +1,8 @@ -using Content.Server.StationEvents; +using System; +using System.Collections.Generic; +using System.Text; +using Content.Server.StationEvents; +using Content.Server.Interfaces.GameTicking; using JetBrains.Annotations; using Robust.Server.Console; using Robust.Server.Interfaces.Player; @@ -9,25 +13,23 @@ using Robust.Shared.Interfaces.Reflection; using Robust.Shared.Interfaces.Timing; using Robust.Shared.IoC; using Robust.Shared.Localization; -using System; -using System.Collections.Generic; -using System.Text; using static Content.Shared.StationEvents.SharedStationEvent; namespace Content.Server.GameObjects.EntitySystems.StationEvents { [UsedImplicitly] + // Somewhat based off of TG's implementation of events public sealed class StationEventSystem : EntitySystem { #pragma warning disable 649 [Dependency] private readonly IServerNetManager _netManager; [Dependency] private readonly IPlayerManager _playerManager; + [Dependency] private readonly IGameTicker _gameTicker; #pragma warning restore 649 - // Somewhat based off of TG's implementation of events public StationEvent CurrentEvent { get; private set; } - public IReadOnlyCollection StationEvents => _stationEvents; + private List _stationEvents = new List(); private const float MinimumTimeUntilFirstEvent = 600; @@ -194,7 +196,18 @@ namespace Content.Server.GameObjects.EntitySystems.StationEvents { return; } - + + // Stop events from happening in lobby and force active event to end if the round ends + if (_gameTicker.RunLevel != GameTicking.GameRunLevel.InRound) + { + if (CurrentEvent != null) + { + Enabled = false; + } + + return; + } + // Keep running the current event if (CurrentEvent != null) { diff --git a/Content.Server/StationEvents/StationEventCommand.cs b/Content.Server/StationEvents/StationEventCommand.cs index 6712c8fa46..85f335b2ff 100644 --- a/Content.Server/StationEvents/StationEventCommand.cs +++ b/Content.Server/StationEvents/StationEventCommand.cs @@ -1,5 +1,4 @@ #nullable enable -using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems.StationEvents; using JetBrains.Annotations; using Robust.Server.Interfaces.Console; From 2448864035e45e0cadeb3df03c604da70e020dc1 Mon Sep 17 00:00:00 2001 From: Exp Date: Wed, 19 Aug 2020 14:27:51 +0200 Subject: [PATCH 03/53] Fixes sending empty msgs and trims unnecessary whitespaces (#1785) --- Content.Client/Chat/ChatManager.cs | 8 +++++++- Content.Server/Chat/ChatCommands.cs | 32 ++++++++++++++++++++++------- 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/Content.Client/Chat/ChatManager.cs b/Content.Client/Chat/ChatManager.cs index 61dbdc28f1..8553c6ed58 100644 --- a/Content.Client/Chat/ChatManager.cs +++ b/Content.Client/Chat/ChatManager.cs @@ -247,13 +247,17 @@ namespace Content.Client.Chat case OOCAlias: { var conInput = text.Substring(1); + if (string.IsNullOrWhiteSpace(conInput)) + return; _console.ProcessCommand($"ooc \"{CommandParsing.Escape(conInput)}\""); break; } case AdminChatAlias: { var conInput = text.Substring(1); - if(_groupController.CanCommand("asay")){ + if (string.IsNullOrWhiteSpace(conInput)) + return; + if (_groupController.CanCommand("asay")){ _console.ProcessCommand($"asay \"{CommandParsing.Escape(conInput)}\""); } else @@ -265,6 +269,8 @@ namespace Content.Client.Chat case MeAlias: { var conInput = text.Substring(1); + if (string.IsNullOrWhiteSpace(conInput)) + return; _console.ProcessCommand($"me \"{CommandParsing.Escape(conInput)}\""); break; } diff --git a/Content.Server/Chat/ChatCommands.cs b/Content.Server/Chat/ChatCommands.cs index 777bba1fe9..c5f194d429 100644 --- a/Content.Server/Chat/ChatCommands.cs +++ b/Content.Server/Chat/ChatCommands.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; @@ -32,9 +32,11 @@ namespace Content.Server.Chat if (args.Length < 1) return; - var chat = IoCManager.Resolve(); + var message = string.Join(" ", args).Trim(); + if (string.IsNullOrEmpty(message)) + return; - var message = string.Join(" ", args); + var chat = IoCManager.Resolve(); if (player.AttachedEntity.HasComponent()) chat.SendDeadChat(player, message); @@ -61,9 +63,11 @@ namespace Content.Server.Chat if (args.Length < 1) return; - var chat = IoCManager.Resolve(); + var action = string.Join(" ", args).Trim(); + if (string.IsNullOrEmpty(action)) + return; - var action = string.Join(" ", args); + var chat = IoCManager.Resolve(); var mindComponent = player.ContentData().Mind; chat.EntityMe(mindComponent.OwnedEntity, action); @@ -78,8 +82,15 @@ namespace Content.Server.Chat public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) { + if (args.Length < 1) + return; + + var message = string.Join(" ", args).Trim(); + if (string.IsNullOrEmpty(message)) + return; + var chat = IoCManager.Resolve(); - chat.SendOOC(player, string.Join(" ", args)); + chat.SendOOC(player, message); } } @@ -91,8 +102,15 @@ namespace Content.Server.Chat public void Execute(IConsoleShell shell, IPlayerSession player, string[] args) { + if (args.Length < 1) + return; + + var message = string.Join(" ", args).Trim(); + if (string.IsNullOrEmpty(message)) + return; + var chat = IoCManager.Resolve(); - chat.SendAdminChat(player, string.Join(" ", args)); + chat.SendAdminChat(player, message); } } From 7be8657417848ee33ef6015ec7069b70b264989c Mon Sep 17 00:00:00 2001 From: Exp Date: Wed, 19 Aug 2020 15:09:16 +0200 Subject: [PATCH 04/53] Fix Suspicion selecting too many traitors (#1790) --- Content.Server/GameTicking/GamePresets/PresetSuspicion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs index fc09cc14a3..3ce23717d4 100644 --- a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs +++ b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs @@ -65,7 +65,7 @@ namespace Content.Server.GameTicking.GamePresets player.AttachedEntity?.EnsureComponent(); } - var numTraitors = FloatMath.Clamp(readyPlayers.Count % PlayersPerTraitor, + var numTraitors = FloatMath.Clamp(readyPlayers.Count / PlayersPerTraitor, MinTraitors, readyPlayers.Count); for (var i = 0; i < numTraitors; i++) From 1292adb001beb0285cd6303f64e8e9d9bc37fcfd Mon Sep 17 00:00:00 2001 From: Julian Giebel Date: Wed, 19 Aug 2020 15:50:06 +0200 Subject: [PATCH 05/53] Disposal routing (#1710) * Implement disposal tagger Implement disposal router Combine sprites to make conpipe-tagger sprite * Implement change requests * Remove nullable * Update DisposalHolderComponent.cs * Update DisposalHolderComponent.cs * Update Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs Co-authored-by: DrSmugleaf Co-authored-by: Julian Giebel Co-authored-by: DrSmugleaf --- .../DisposalRouterBoundUserInterface.cs | 69 ++ .../Disposal/DisposalRouterWindow.cs | 56 ++ .../DisposalTaggerBoundUserInterface.cs | 69 ++ .../Disposal/DisposalTaggerWindow.cs | 56 ++ Content.Client/IgnoredComponents.cs | 2 + .../Disposal/DisposalHolderComponent.cs | 7 + .../Disposal/DisposalRouterComponent.cs | 179 ++++ .../Disposal/DisposalTaggerComponent.cs | 150 ++++ .../Disposal/SharedDisposalRouterComponent.cs | 52 ++ .../Disposal/SharedDisposalTaggerComponent.cs | 53 ++ .../Entities/Constructible/disposal.yml | 82 ++ .../Power/disposal.rsi/conpipe-tagger.png | Bin 0 -> 2549 bytes .../Power/disposal.rsi/meta.json | 780 +++++++++++++++++- .../Power/disposal.rsi/pipe-tagger.png | Bin 2228 -> 2383 bytes 14 files changed, 1554 insertions(+), 1 deletion(-) create mode 100644 Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs create mode 100644 Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs create mode 100644 Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs create mode 100644 Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs create mode 100644 Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs create mode 100644 Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs create mode 100644 Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs create mode 100644 Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs create mode 100644 Resources/Textures/Constructible/Power/disposal.rsi/conpipe-tagger.png diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs b/Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs new file mode 100644 index 0000000000..00f30c02ff --- /dev/null +++ b/Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs @@ -0,0 +1,69 @@ +#nullable enable +using JetBrains.Annotations; +using Robust.Client.GameObjects.Components.UserInterface; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.Localization; +using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent; + +namespace Content.Client.GameObjects.Components.Disposal +{ + /// + /// Initializes a and updates it when new server messages are received. + /// + [UsedImplicitly] + public class DisposalRouterBoundUserInterface : BoundUserInterface + { + private DisposalRouterWindow? _window; + + public DisposalRouterBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _window = new DisposalRouterWindow + { + Title = Loc.GetString("Disposal Router"), + }; + + _window.OpenCentered(); + _window.OnClose += Close; + + _window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text); + + } + + private void ButtonPressed(UiAction action, string tag) + { + SendMessage(new UiActionMessage(action, tag)); + _window?.Close(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (!(state is DisposalRouterUserInterfaceState cast)) + { + return; + } + + _window?.UpdateState(cast); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + _window?.Dispose(); + } + } + + + } + +} diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs b/Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs new file mode 100644 index 0000000000..f9f850ae18 --- /dev/null +++ b/Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs @@ -0,0 +1,56 @@ +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using Content.Shared.GameObjects.Components.Disposal; +using Robust.Client.Graphics.Drawing; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent; + +namespace Content.Client.GameObjects.Components.Disposal +{ + /// + /// Client-side UI used to control a + /// + public class DisposalRouterWindow : SS14Window + { + public readonly LineEdit TagInput; + public readonly Button Confirm; + + protected override Vector2? CustomSize => (400, 80); + + private Regex _tagRegex; + + public DisposalRouterWindow() + { + _tagRegex = new Regex("^[a-zA-Z0-9, ]*$", RegexOptions.Compiled); + + Contents.AddChild(new VBoxContainer + { + Children = + { + new Label {Text = Loc.GetString("Tags:")}, + new Control {CustomMinimumSize = (0, 10)}, + new HBoxContainer + { + Children = + { + (TagInput = new LineEdit {SizeFlagsHorizontal = SizeFlags.Expand, CustomMinimumSize = (320, 0), + ToolTip = Loc.GetString("A comma separated list of tags"), IsValid = tags => _tagRegex.IsMatch(tags)}), + new Control {CustomMinimumSize = (10, 0)}, + (Confirm = new Button {Text = Loc.GetString("Confirm")}) + } + } + } + }); + } + + + public void UpdateState(DisposalRouterUserInterfaceState state) + { + TagInput.Text = state.Tags; + } + } +} diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs new file mode 100644 index 0000000000..273b8c06ec --- /dev/null +++ b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs @@ -0,0 +1,69 @@ +#nullable enable +using JetBrains.Annotations; +using Robust.Client.GameObjects.Components.UserInterface; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.Localization; +using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent; + +namespace Content.Client.GameObjects.Components.Disposal +{ + /// + /// Initializes a and updates it when new server messages are received. + /// + [UsedImplicitly] + public class DisposalTaggerBoundUserInterface : BoundUserInterface + { + private DisposalTaggerWindow? _window; + + public DisposalTaggerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _window = new DisposalTaggerWindow + { + Title = Loc.GetString("Disposal Tagger"), + }; + + _window.OpenCentered(); + _window.OnClose += Close; + + _window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text); + + } + + private void ButtonPressed(UiAction action, string tag) + { + SendMessage(new UiActionMessage(action, tag)); + _window?.Close(); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (!(state is DisposalTaggerUserInterfaceState cast)) + { + return; + } + + _window?.UpdateState(cast); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + _window?.Dispose(); + } + } + + + } + +} diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs new file mode 100644 index 0000000000..7fb6dc6937 --- /dev/null +++ b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs @@ -0,0 +1,56 @@ +using System.Runtime.CompilerServices; +using System.Text.RegularExpressions; +using Content.Shared.GameObjects.Components.Disposal; +using Robust.Client.Graphics.Drawing; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent; + +namespace Content.Client.GameObjects.Components.Disposal +{ + /// + /// Client-side UI used to control a + /// + public class DisposalTaggerWindow : SS14Window + { + public readonly LineEdit TagInput; + public readonly Button Confirm; + + protected override Vector2? CustomSize => (400, 80); + + private Regex _tagRegex; + + public DisposalTaggerWindow() + { + _tagRegex = new Regex("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled); + + Contents.AddChild(new VBoxContainer + { + Children = + { + new Label {Text = Loc.GetString("Tag:")}, + new Control {CustomMinimumSize = (0, 10)}, + new HBoxContainer + { + Children = + { + (TagInput = new LineEdit {SizeFlagsHorizontal = SizeFlags.Expand, CustomMinimumSize = (320, 0), + IsValid = tag => _tagRegex.IsMatch(tag)}), + new Control {CustomMinimumSize = (10, 0)}, + (Confirm = new Button {Text = Loc.GetString("Confirm")}) + } + } + } + }); + } + + + public void UpdateState(DisposalTaggerUserInterfaceState state) + { + TagInput.Text = state.Tag; + } + } +} diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs index d4588e5c84..6e67d79995 100644 --- a/Content.Client/IgnoredComponents.cs +++ b/Content.Client/IgnoredComponents.cs @@ -142,6 +142,8 @@ "Listening", "Radio", "DisposalHolder", + "DisposalTagger", + "DisposalRouter", "DisposalTransit", "DisposalEntry", "DisposalJunction", diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs index ee39a57d76..44c1a1eab3 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs @@ -1,4 +1,5 @@ #nullable enable +using System.Collections.Generic; using System.Linq; using Content.Server.GameObjects.Components.Items.Storage; using Content.Shared.GameObjects.Components.Body; @@ -41,6 +42,12 @@ namespace Content.Server.GameObjects.Components.Disposal [ViewVariables] public IDisposalTubeComponent? NextTube { get; set; } + /// + /// A list of tags attached to the content, used for sorting + /// + [ViewVariables] + public HashSet Tags { get; set; } = new HashSet(); + private bool CanInsert(IEntity entity) { if (!_contents.CanInsert(entity)) diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs new file mode 100644 index 0000000000..1abf3a7b2d --- /dev/null +++ b/Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs @@ -0,0 +1,179 @@ +using Content.Server.Interfaces; +using Content.Server.Interfaces.GameObjects.Components.Items; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; +using Robust.Server.GameObjects.Components.UserInterface; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.ViewVariables; +using System; +using System.Collections.Generic; +using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalRouterComponent; + +namespace Content.Server.GameObjects.Components.Disposal +{ + [RegisterComponent] + [ComponentReference(typeof(IActivate))] + [ComponentReference(typeof(IDisposalTubeComponent))] + public class DisposalRouterComponent : DisposalJunctionComponent, IActivate + { +#pragma warning disable 649 + [Dependency] private readonly IServerNotifyManager _notifyManager; +#pragma warning restore 649 + public override string Name => "DisposalRouter"; + + [ViewVariables] + private BoundUserInterface _userInterface; + + [ViewVariables] + private HashSet _tags; + + [ViewVariables] + public bool Anchored => + !Owner.TryGetComponent(out CollidableComponent collidable) || + collidable.Anchored; + + public override Direction NextDirection(DisposalHolderComponent holder) + { + var directions = ConnectableDirections(); + + if (holder.Tags.Overlaps(_tags)) + { + return directions[1]; + } + + return Owner.Transform.LocalRotation.GetDir(); + } + + + public override void Initialize() + { + base.Initialize(); + _userInterface = Owner.GetComponent() + .GetBoundUserInterface(DisposalRouterUiKey.Key); + _userInterface.OnReceiveMessage += OnUiReceiveMessage; + + _tags = new HashSet(); + + UpdateUserInterface(); + } + + /// + /// Handles ui messages from the client. For things such as button presses + /// which interact with the world and require server action. + /// + /// A user interface message from the client. + private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj) + { + var msg = (UiActionMessage) obj.Message; + + if (!PlayerCanUseDisposalTagger(obj.Session.AttachedEntity)) + return; + + if (msg.Action == UiAction.Ok) + { + _tags.Clear(); + foreach (var tag in msg.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries)) + { + _tags.Add(tag.Trim()); + } + } + + ClickSound(); + } + + /// + /// Checks whether the player entity is able to use the configuration interface of the pipe tagger. + /// + /// The player entity. + /// Returns true if the entity can use the configuration interface, and false if it cannot. + private bool PlayerCanUseDisposalTagger(IEntity playerEntity) + { + //Need player entity to check if they are still able to use the configuration interface + if (playerEntity == null) + return false; + if (!Anchored) + return false; + //Check if player can interact in their current state + if (!ActionBlockerSystem.CanInteract(playerEntity) || !ActionBlockerSystem.CanUse(playerEntity)) + return false; + + return true; + } + + /// + /// Gets component data to be used to update the user interface client-side. + /// + /// Returns a + private DisposalRouterUserInterfaceState GetUserInterfaceState() + { + if(_tags == null || _tags.Count <= 0) + { + return new DisposalRouterUserInterfaceState(""); + } + + var taglist = new System.Text.StringBuilder(); + + foreach (var tag in _tags) + { + taglist.Append(tag); + taglist.Append(", "); + } + + taglist.Remove(taglist.Length - 2, 2); + + return new DisposalRouterUserInterfaceState(taglist.ToString()); + } + + private void UpdateUserInterface() + { + var state = GetUserInterfaceState(); + _userInterface.SetState(state); + } + + private void ClickSound() + { + EntitySystem.Get().PlayFromEntity("/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f)); + } + + /// + /// Called when you click the owner entity with an empty hand. Opens the UI client-side if possible. + /// + /// Data relevant to the event such as the actor which triggered it. + void IActivate.Activate(ActivateEventArgs args) + { + if (!args.User.TryGetComponent(out IActorComponent actor)) + { + return; + } + + if (!args.User.TryGetComponent(out IHandsComponent hands)) + { + _notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User, + Loc.GetString("You have no hands.")); + return; + } + + var activeHandEntity = hands.GetActiveHand?.Owner; + if (activeHandEntity == null) + { + UpdateUserInterface(); + _userInterface.Open(actor.playerSession); + } + } + + public override void OnRemove() + { + _userInterface.CloseAll(); + base.OnRemove(); + } + } +} diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs new file mode 100644 index 0000000000..f949e37090 --- /dev/null +++ b/Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs @@ -0,0 +1,150 @@ +using Content.Server.Interfaces; +using Content.Server.Interfaces.GameObjects.Components.Items; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; +using Robust.Server.GameObjects.Components.UserInterface; +using Robust.Server.GameObjects.EntitySystems; +using Robust.Server.Interfaces.GameObjects; +using Robust.Shared.Audio; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Maths; +using Robust.Shared.ViewVariables; +using static Content.Shared.GameObjects.Components.Disposal.SharedDisposalTaggerComponent; + +namespace Content.Server.GameObjects.Components.Disposal +{ + [RegisterComponent] + [ComponentReference(typeof(IActivate))] + [ComponentReference(typeof(IDisposalTubeComponent))] + public class DisposalTaggerComponent : DisposalTransitComponent, IActivate + { +#pragma warning disable 649 + [Dependency] private readonly IServerNotifyManager _notifyManager; +#pragma warning restore 649 + public override string Name => "DisposalTagger"; + + [ViewVariables] + private BoundUserInterface _userInterface; + + [ViewVariables(VVAccess.ReadWrite)] + private string _tag = ""; + + [ViewVariables] + public bool Anchored => + !Owner.TryGetComponent(out CollidableComponent collidable) || + collidable.Anchored; + + public override Direction NextDirection(DisposalHolderComponent holder) + { + holder.Tags.Add(_tag); + return base.NextDirection(holder); + } + + + public override void Initialize() + { + base.Initialize(); + _userInterface = Owner.GetComponent() + .GetBoundUserInterface(DisposalTaggerUiKey.Key); + _userInterface.OnReceiveMessage += OnUiReceiveMessage; + + UpdateUserInterface(); + } + + /// + /// Handles ui messages from the client. For things such as button presses + /// which interact with the world and require server action. + /// + /// A user interface message from the client. + private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj) + { + var msg = (UiActionMessage) obj.Message; + + if (!PlayerCanUseDisposalTagger(obj.Session.AttachedEntity)) + return; + + if (msg.Action == UiAction.Ok) + { + _tag = msg.Tag; + } + + ClickSound(); + } + + /// + /// Checks whether the player entity is able to use the configuration interface of the pipe tagger. + /// + /// The player entity. + /// Returns true if the entity can use the configuration interface, and false if it cannot. + private bool PlayerCanUseDisposalTagger(IEntity playerEntity) + { + //Need player entity to check if they are still able to use the configuration interface + if (playerEntity == null) + return false; + if (!Anchored) + return false; + //Check if player can interact in their current state + if (!ActionBlockerSystem.CanInteract(playerEntity) || !ActionBlockerSystem.CanUse(playerEntity)) + return false; + + return true; + } + + /// + /// Gets component data to be used to update the user interface client-side. + /// + /// Returns a + private DisposalTaggerUserInterfaceState GetUserInterfaceState() + { + return new DisposalTaggerUserInterfaceState(_tag); + } + + private void UpdateUserInterface() + { + var state = GetUserInterfaceState(); + _userInterface.SetState(state); + } + + private void ClickSound() + { + EntitySystem.Get().PlayFromEntity("/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f)); + } + + /// + /// Called when you click the owner entity with an empty hand. Opens the UI client-side if possible. + /// + /// Data relevant to the event such as the actor which triggered it. + void IActivate.Activate(ActivateEventArgs args) + { + if (!args.User.TryGetComponent(out IActorComponent actor)) + { + return; + } + + if (!args.User.TryGetComponent(out IHandsComponent hands)) + { + _notifyManager.PopupMessage(Owner.Transform.GridPosition, args.User, + Loc.GetString("You have no hands.")); + return; + } + + var activeHandEntity = hands.GetActiveHand?.Owner; + if (activeHandEntity == null) + { + UpdateUserInterface(); + _userInterface.Open(actor.playerSession); + } + } + + public override void OnRemove() + { + base.OnRemove(); + _userInterface.CloseAll(); + } + } +} diff --git a/Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs new file mode 100644 index 0000000000..c0323cbaa1 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs @@ -0,0 +1,52 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.Serialization; +using System; + +namespace Content.Shared.GameObjects.Components.Disposal +{ + public class SharedDisposalRouterComponent : Component + { + public override string Name => "DisposalRouter"; + + [Serializable, NetSerializable] + public class DisposalRouterUserInterfaceState : BoundUserInterfaceState + { + public readonly string Tags; + + public DisposalRouterUserInterfaceState(string tags) + { + Tags = tags; + } + } + + [Serializable, NetSerializable] + public class UiActionMessage : BoundUserInterfaceMessage + { + public readonly UiAction Action; + public readonly string Tags = ""; + + public UiActionMessage(UiAction action, string tags) + { + Action = action; + + if (Action == UiAction.Ok) + { + Tags = tags.Substring(0, Math.Min(tags.Length, 150)); + } + } + } + + [Serializable, NetSerializable] + public enum UiAction + { + Ok + } + + [Serializable, NetSerializable] + public enum DisposalRouterUiKey + { + Key + } + } +} diff --git a/Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs new file mode 100644 index 0000000000..d12e9060e9 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs @@ -0,0 +1,53 @@ +#nullable enable +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components.UserInterface; +using Robust.Shared.Serialization; +using System; + +namespace Content.Shared.GameObjects.Components.Disposal +{ + public class SharedDisposalTaggerComponent : Component + { + public override string Name => "DisposalTagger"; + + [Serializable, NetSerializable] + public class DisposalTaggerUserInterfaceState : BoundUserInterfaceState + { + public readonly string Tag; + + public DisposalTaggerUserInterfaceState(string tag) + { + Tag = tag; + } + } + + [Serializable, NetSerializable] + public class UiActionMessage : BoundUserInterfaceMessage + { + public readonly UiAction Action; + public readonly string Tag = ""; + + public UiActionMessage(UiAction action, string tag) + { + Action = action; + + if (Action == UiAction.Ok) + { + Tag = tag; + } + } + } + + [Serializable, NetSerializable] + public enum UiAction + { + Ok + } + + [Serializable, NetSerializable] + public enum DisposalTaggerUiKey + { + Key + } + } +} diff --git a/Resources/Prototypes/Entities/Constructible/disposal.yml b/Resources/Prototypes/Entities/Constructible/disposal.yml index 5187ce2a88..1d85dd33a6 100644 --- a/Resources/Prototypes/Entities/Constructible/disposal.yml +++ b/Resources/Prototypes/Entities/Constructible/disposal.yml @@ -44,6 +44,31 @@ state_anchored: pipe-s state_broken: pipe-b +- type: entity + id: DisposalTagger + parent: DisposalPipeBase + name: disposal pipe tagger + description: A pipe that tags entities for routing + components: + - type: Sprite + drawdepth: BelowFloor + sprite: Constructible/Power/disposal.rsi + state: conpipe-tagger + - type: Icon + sprite: Constructible/Power/disposal.rsi + state: conpipe-tagger + - type: DisposalTagger + - type: Appearance + visuals: + - type: DisposalVisualizer + state_free: conpipe-tagger + state_anchored: pipe-tagger + state_broken: pipe-b + - type: UserInterface + interfaces: + - key: enum.DisposalTaggerUiKey.Key + type: DisposalTaggerBoundUserInterface + - type: entity id: DisposalTrunk parent: DisposalPipeBase @@ -131,6 +156,63 @@ - key: enum.DisposalUnitUiKey.Key type: DisposalUnitBoundUserInterface +- type: entity + id: DisposalRouter + parent: DisposalPipeBase + name: disposal router + description: A three-way router. Entities with matching tags get routed to the side + components: + - type: Sprite + drawdepth: BelowFloor + sprite: Constructible/Power/disposal.rsi + state: conpipe-j1s + - type: Icon + sprite: Constructible/Power/disposal.rsi + state: conpipe-j1s + - type: DisposalRouter + degrees: + - 0 + - -90 + - 180 + - type: Appearance + visuals: + - type: DisposalVisualizer + state_free: conpipe-j1s + state_anchored: pipe-j1s + state_broken: pipe-b + - type: Flippable + entity: DisposalRouterFlipped + - type: UserInterface + interfaces: + - key: enum.DisposalRouterUiKey.Key + type: DisposalRouterBoundUserInterface + +- type: entity + id: DisposalRouterFlipped + parent: DisposalRouter + name: flipped router junction + components: + - type: Sprite + drawdepth: BelowFloor + sprite: Constructible/Power/disposal.rsi + state: conpipe-j2s + - type: Icon + sprite: Constructible/Power/disposal.rsi + state: conpipe-j2s + - type: DisposalRouter + degrees: + - 0 + - 90 + - 180 + - type: Appearance + visuals: + - type: DisposalVisualizer + state_free: conpipe-j2s + state_anchored: pipe-j2s + state_broken: pipe-b + - type: Flippable + entity: DisposalRouter + - type: entity id: DisposalJunction parent: DisposalPipeBase diff --git a/Resources/Textures/Constructible/Power/disposal.rsi/conpipe-tagger.png b/Resources/Textures/Constructible/Power/disposal.rsi/conpipe-tagger.png new file mode 100644 index 0000000000000000000000000000000000000000..e1c4637244ab4b0390a785d54665da75ce7e54a4 GIT binary patch literal 2549 zcmVPx;uSrBfRCt{2noVyUMHHb3VjwXYY~wfy*wfR~J>6A%m=}AP z3Dc8IgtVETG?Jw%*T3qimwuk73fer_h(;Brrl$1kufI-jPY*&As|z72f0xk!TBEfF z;K$Wf-g@gTjvYJJP@|Fl8*jY9&aSSCHbT^NTyN3VTC=pg%-e6j-B4dP>cEK;Cm0wQ zU}<@o&h6XzVQGmyd-rlZlVSf;PjNky;g2IDWHK2Z>+9o(r6oGIZ^!q2PM$n@m&4xV zOE|ku!@8rRgIFwvX_{Ci-9#cm zEEYp+jcwcH9fzFbVB0pn=i>PRwrz9i(xqS3Hyd@}(xppcaNj=Nwfzxv;FHf~Q3aQr zy}?hnB%nbg;!sFl?xXx+{^ema^wgfeDDFgdv;I=OmtAdcO5o< zHjuJbUn?I3(qI?{r%t`Yv7<);SXo}e z&N+Pc`4_CM-Q?@bm+xuz%M1^v^uf{5ru5g>*GYf*RYU!TX8?dv;qPA1PWg`FE`!mhcA98l*uyX7SM{|KaM@t2I?dM^ZSBLqRKq zG(tDsP#V#+epo^qKuCjVEY8QD7R3{mRaIW@(7ygh5mMIp3HM5~`e6xe04WWGXxwne zwhj=b8EX+j+=YHfnmPlN-*|6zbR>~N(7NH5K2=TXhb8n{(LsRz#ol`5`8bCTR1dYI zV;k{!Jah*_A;f*t4{ZP;B}O#bu%4CNMkLx=18A-9n|}B+z=+gsxMzn2n{*k4&(-yo3t;Qqr<@OF*ljY5lN- zGJs)7RN!wOLPGG`-(Dem`v#H3!^J>FO3X-L(@LQc`qz zWtEp-ewjDleDj_hg5$@J>-XM!k6pdp7=}SC8pAXV3@IyNqEZSK1bBr4z8`@0SzY^y z%U7=4ds6iOJuTjF3n-FimD>XHiNqJw1(-lDWA#VzC$(FJ2@b zkF&VA*b;YGYt7i^O6v=H}**QZhX~jZ%u)*;x$JWPX00cs$N`*A}rX3#A%+ zgb)1HSerI&+O%oYrcIj%o~fxRol2$j@bIuce*AcgGn&PyZg_ZDr&1|BH8oY!{?ycz z9vmFhLqkI?*DfN4?Y6d?uabedhgy)`o=gkWT3gnT{^h?eUkOw6cRnSe6p z)((t~jp4d363xi|rz(}S)_nNkhfOWHzaDyqyz3wky!65#|Ni6?6q2#AF}`11touLd zbeaPP4j`1@a{YhK%n(l`nV+Apnt;s}AOz>monw4_oS@(mkH?Ev*P3@vpRW5R=hyfc zc<$L}FbtDK(jpp(U_|50&CRiU_wJhdLWrWa?<1wSFZv0~Vtjm@@4o%E)&K&Sn3zB* zg}we0zULwh$?1Q*U-wN;Bd%V(%16MF=bx>-=}ROMOiWDhWjbBg0CRJ596o#)0WH^; zhC$r2n4O*F(Jd2Dbq1U{bA}gRd=UgV8#nKGyHkFZQM{Yqi=!mC4CTq?FvgwbpWdDJ98dlF7+QzWDs}N_kaIaB_08bl3=PZMZ}d57+HL&kn0f zeEEh`_o^D;_U)UTI`s~^biF+}Iaz;Hz|71{G2Mo4xjqyR&6$~*s`9E8Ff%j5;lqbf zO3~B13w?*AVy&g`oOX*_3632-%74$#W7~Gk65Ptf#00MEf~MvAXV0F+b={gRSXlvD zYsSaN$vY0JbP*|}Wb(*F-MhF(zyAJy4m~|uc`)g^dGe0K`1p9;Y_X=e1#`Jv?HR*JkJ&*TcNX_-{7x{f>Cx&5y&J!^WT-PBMi;~Y} z(Lxe2BS-{Z-a!SW2hu`ND0t-ad8ClILh|*+FNjACe9t3&u_OK9g?+h9OZ(5w{XG!AO~{!Px5RDv5+u>71r~Ayd8%6q^Cq zFAl)1i?GEc$+qD+O%oY=E38CF3H$ZlyxZJ00000 LNkvXXu0mjf)SB-9 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Constructible/Power/disposal.rsi/meta.json b/Resources/Textures/Constructible/Power/disposal.rsi/meta.json index 96aadcfa66..f11b589795 100644 --- a/Resources/Textures/Constructible/Power/disposal.rsi/meta.json +++ b/Resources/Textures/Constructible/Power/disposal.rsi/meta.json @@ -1 +1,779 @@ -{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "https://github.com/discordia-space/CEV-Eris/blob/bbe32606902c90f5290b57d905a3f31b84dc6d7d/icons/obj/pipes/disposal.dmi and modified by DrSmugleaf", "states": [{"name": "condisposal", "directions": 1, "delays": [[1.0]]}, {"name": "conpipe-c", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j1s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-j2s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-t", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "conpipe-y", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "disposal", "directions": 1, "delays": [[1.0]]}, {"name": "disposal-charging", "directions": 1, "delays": [[1.0]]}, {"name": "disposal-flush", "directions": 1, "delays": [[0.1, 0.1, 0.1, 0.1, 0.1, 0.5, 0.1, 0.1, 0.1]]}, {"name": "dispover-charge", "directions": 1, "delays": [[0.4, 0.4]]}, {"name": "dispover-full", "directions": 1, "delays": [[0.2, 0.2]]}, {"name": "dispover-handle", "directions": 1, "delays": [[1.0]]}, {"name": "dispover-ready", "directions": 1, "delays": [[1.0]]}, {"name": "intake", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "intake-closing", "directions": 4, "delays": [[0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1], [0.5, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]]}, {"name": "outlet", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "outlet-open", "directions": 4, "delays": [[0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1], [0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1], [0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1], [0.5, 0.5, 0.5, 0.5, 0.5, 0.1, 1.5, 0.1]]}, {"name": "pipe-b", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-bf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-c", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-cf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-d", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1f", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j1sf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2f", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-j2sf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-s", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-sf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-t", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-tagger", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-tagger-partial", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-tf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-u", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-y", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}, {"name": "pipe-yf", "directions": 4, "delays": [[1.0], [1.0], [1.0], [1.0]]}]} \ No newline at end of file +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "https://github.com/discordia-space/CEV-Eris/blob/bbe32606902c90f5290b57d905a3f31b84dc6d7d/icons/obj/pipes/disposal.dmi and modified by DrSmugleaf", + "states": [ + { + "name": "condisposal", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "conpipe-c", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "conpipe-j1", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "conpipe-j1s", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "conpipe-j2", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "conpipe-j2s", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "conpipe-s", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "conpipe-t", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "conpipe-tagger", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "conpipe-y", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "disposal", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "disposal-charging", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "disposal-flush", + "directions": 1, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.5, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "dispover-charge", + "directions": 1, + "delays": [ + [ + 0.4, + 0.4 + ] + ] + }, + { + "name": "dispover-full", + "directions": 1, + "delays": [ + [ + 0.2, + 0.2 + ] + ] + }, + { + "name": "dispover-handle", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "dispover-ready", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "intake", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "intake-closing", + "directions": 4, + "delays": [ + [ + 0.5, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.5, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.5, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ], + [ + 0.5, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "outlet", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "outlet-open", + "directions": 4, + "delays": [ + [ + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.1, + 1.5, + 0.1 + ], + [ + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.1, + 1.5, + 0.1 + ], + [ + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.1, + 1.5, + 0.1 + ], + [ + 0.5, + 0.5, + 0.5, + 0.5, + 0.5, + 0.1, + 1.5, + 0.1 + ] + ] + }, + { + "name": "pipe-b", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-bf", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-c", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-cf", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-d", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-j1", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-j1f", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-j1s", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-j1sf", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-j2", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-j2f", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-j2s", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-j2sf", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-s", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-sf", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-t", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-tagger", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-tagger-partial", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-tf", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-u", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-y", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + }, + { + "name": "pipe-yf", + "directions": 4, + "delays": [ + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ], + [ + 1.0 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Constructible/Power/disposal.rsi/pipe-tagger.png b/Resources/Textures/Constructible/Power/disposal.rsi/pipe-tagger.png index a8463fd0c1f57c834a4c27a9acdba9864a389a40..ce48830a50e8f73a57d173e8927f590c0bc92ddb 100644 GIT binary patch delta 2373 zcmV-L3A*;Q5zi8kBYz1NNklu(&@6~KS@F|%*S+1M^gNL!aAl!%fh5JYGr z(Msh5ph`_8RQd;!iVykHr;3UyA(amilqjgQPEscblGcyiwf8a4dv8C??#7$f4z_n! zqB+u9E6MLNseT$M_>jOTd>DR2&) zgUZ}Nj4^Dk-J#d*5(FWMb$ojDGp^sbu_qreY5w@fBLry@QSf|XBY5_^&rm9sP<_i@ zUs<7EKLpO^Y&LJ=c^>Rx`cI25bMSH2;&}yDu79s!(-d4v5{Fom@X<$qBaK7S zBqB~CjI(Sswvkfp*ZohFpZf(hY0TN@pQW?iM7RW{6j7S;lOMlKuiZdMg-Iez9Ps@U z-=iBwd%5qa@y8m#IS|?d0a|-F9LA*l=`SCS=v{c_EO8tY#W5yHamM1@?rwIP{9H3Y zA+wQ4MSrnS;{CsV#Omtm1A1p)I7JWyB+e2?y?l2#V}33J2q_R!;TMX0@L>iomr5f# zg8`jfK8BDIGk$@|EtBMrHGmL+qwiG4`b?^|YX79hzB$23^7Fj{T7w|-z~cr`tssSf z9D~g(ljP@E1|`O%IAd_mjvRaY^&%I}jCz`CwSPvjSftbL=6YhY=8x6L;%N|4qI`es z1xuA0o?m2s{vg)cvHG*-k2Qdl0wEMqd3b*Df#Zi4N1c)O3i!ne)>)+7yAzl+KZg$x zI3baqhg9BJ?Kp{vf(UC8a%?qn$Yl9rXMl4q+kqi)_eBYTTaEVbkL0GQiI77cX99d3l*$ z5R815mzTNlqgO!)lGI@A{uyAB{INd3T7QcpyVxMf`0IDx;oVD@M)cl#^9|PS-ocs_ zgg{8SZ-LMx`8k}RBMy2LegB>l5`uSr^){X6I$rrewo#E1?fE1o+pmYc4zLdifeG_- z#R1kP#Jvu8?|jwg3#`dP)U~xWgv>4@rNUTCV{?-?)S=yh5+lk)kEFL*8ph{KSm*CtLa|G9gI`hgjWJkjiQ|}7tM%C8K; zGs2q{Uk@xfST3Z1LZLvpP=CTNSCL8&Pf$u_IhOta91fc#Sd-waMJS19K{$joPvYF8k1#Pvdm0E z5{H;LMhJ=28s{u=6oNIRX~HL;{fm<)PU5R=d@H04SOgw6sOA*j`Av|6p9?kxElvUj3Vsbt}$ zbM!hb(l|mY$)(@C_kYlyoN=Mj#A+r#+QmKrDsI%k`M^P@9vo8Xi zNeSAUk8Cp=bYPH1^~ znG3I+MaX_NrysZgblPpw9UWh@uTGNrLh%p@``9+g4hw)-Yu?YyQvx z+uPgyv~$AB$_h^%K0^J_A(YbKLOiXAqJTodC+u}_LVx0E4~ZZS1FT8=4T^#!i3!6H zDI}4Q{PQ1IS*XuZE@lzffVI;Av*Hg8;GCo4OQI<3*VR(Cni~|$Rh+dbDbPxggc5Fn^_S^Y%oyqf#jfRf|%J)myg!X37@;2M-=}gB{uZ35k;@Pw>MZJkQ*Ld6u6! z&f?-Cg+c*m(+BGFUwv_#AdJ{x3DC7^s5f6UJRY5_KkivsS0x}B8VrLBkJK4+3?9Qbx)m4>;o*8z0n`U=s zM+*CsW;L2V-CyQkpq)e0tPVPU~Par*Qmv-@i8)-ATTw|~W+PCg|5Lk})4F8Xul z&gF(e5Cj215DE?H&>@ttr7$QLJGVG z@1Zt#9BVCG>$mCmdPGr-ah?x9`j}5|+}KwPm^Od%;}L?)B$R@X)C!*Y&iAO4E2x2I zKV4mAVc`ULk2eObN^G@m5(EM4qx&BgUl!owog)ZJtbcyGipw(a877T!*6{xOe35n4sjw!&rV-zN z>bvyfWIy+PX#9x|@E(K?K!DZ(9*?ydfBf@%V|Eu`S|LqSk~GDd3~wFY@9ky}lV7L@ zC}iFesedS!D!lWT_gPz8yT@+jxidsjgz=6v=@+}h8S@J{KuCd*im+7X-S={MwNe?g z88+zY<94J1fJA@dJQQA z6liQwO_N_>7?fC>;jP7cH+JleSIb;HHy&x~^?wHCa+z+YSLlh&nm^GZ&!<61i3-Ds z7p&A81Yw!^`Qtd}Cfd)MKhXhF3WQKd6%d5wdyb!cWIPz@phQ@%;k-l2{Re?*^9#fP zffo`P1V|N3v`$S*5+yimC@|G1py~1__5kmFJ_AGG@5(IhY()357nsb@>-Fxj-;1Wr zFMkxJgb<_8Bspsl-chgBid?#orpYgs2nhkZ=HNrU_jdurW4~>!|CcE4)7@?tYQU`d zg~urYg}Rgjk|ZTNfDzy@_yzI+fwLBCZJ`S$aRLZ3>-SX-J1l-72kicS`SN9!mzU{B z(b#8sd6|npd>MqmWESfV&H>ZpPmBT1Ie$F)#fC-3U%mMzZ(X@EX7~D~*I2)E8)q{R z0wLvrB|_8W7YKr$H0o0h!>@vn5WM-zH|VxD2&%{OiHelyAjH^wz8?3xzyTBlrpzyt z2RLU)`(5tb{&K(+9{bR))rZ24=fLuCVz58$oV|5c-XjklYfO1 zCkVBo-)-Y<#wRyE=h-MCjboC2hct8i=gw^wj+OE5030x7exVp(y&z6fj?dNk?B*&# zpa-=ocx2%iUw-y)y!T|rV60_su0b!34=e^ulmE~#xwN$8z4!ZCm}s?Htgo-{X*f6x z{zH3!_n!0T&+lnzDvo1Hb;riW#(!S!{m}Rm9WY#EwASLBBTZAn!N!!ZO64_{K?6J!47Vj(w4O*jgh%w3N34dv=$BLY@)vJ6R{p8{W;v_;K`1#MCL2Atxw{D`8Vt;;qo-|Fz955@s z1_;BDMx!y=6~raaAN6ONTm?M69yrfNr-wKy4?=lZjWxKOLKDr>l`IxP@y9h3H`yg zm3F&5s`k#BKXSm%&d#9hY*<}g<rt!KkSah5NxR*m-l);qYSQgT^m{$RQWfD6PMuuBIg4>QBaY*- zwfkA}_1djloIQIM=Nz3lqj~eQsZK|wQk3crr4(zoZUM}cF941oKkkP!vco7yWLbvy re)R8bvm)jn=}1R9(vgnz4b}eu_`T2@3AGA-00000NkvXXu0mjfilj># From cd1afb6582b246a0e148b9bf822c9f7bdd9d0f64 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 19 Aug 2020 15:54:45 +0200 Subject: [PATCH 06/53] Disable suffocation damage temporarily --- .../GameObjects/Components/Metabolism/MetabolismComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/GameObjects/Components/Metabolism/MetabolismComponent.cs b/Content.Server/GameObjects/Components/Metabolism/MetabolismComponent.cs index 913d571dd6..10e9e2c592 100644 --- a/Content.Server/GameObjects/Components/Metabolism/MetabolismComponent.cs +++ b/Content.Server/GameObjects/Components/Metabolism/MetabolismComponent.cs @@ -213,7 +213,7 @@ namespace Content.Server.GameObjects.Components.Metabolism if (Suffocating && Owner.TryGetComponent(out IDamageableComponent damageable)) { - damageable.ChangeDamage(DamageClass.Airloss, _suffocationDamage, false); + // damageable.ChangeDamage(DamageClass.Airloss, _suffocationDamage, false); } } From dc77c399b9d9fdf5ee8e84bcbbf64de2c99e0432 Mon Sep 17 00:00:00 2001 From: SoulSloth <67545203+SoulSloth@users.noreply.github.com> Date: Wed, 19 Aug 2020 10:23:20 -0400 Subject: [PATCH 07/53] Add 'Scan DNA' function to medical scanner (#1783) * Add art assets for cloning * Added a 'Scan DNA' button to the medical scanner * Made the UI update unconditional for the medical scanner until checks for power changes are in place * Update Medical scanner to reflect powered status and fix #1774 * added a 'scan dna' button the the medical scanner that will add the contained bodies Uid to a list in CloningSystem, fixed an issue with the menu not populating if the scanner starts in an unpowered state * Add disabling logic to 'Scan DNA' button on medical scanner * Removed un-used libraries * changed scan dna button to Scan and Save DNA --- .../MedicalScannerBoundUserInterface.cs | 1 + .../MedicalScanner/MedicalScannerWindow.cs | 29 +++++- .../Medical/MedicalScannerComponent.cs | 51 +++++++--- .../EntitySystems/CloningSystem.cs | 24 +++++ .../EntitySystems/MedicalScannerSystem.cs | 1 + .../Medical/SharedMedicalScannerComponent.cs | 24 ++++- .../Specific/Medical/Cloning.rsi/meta.json | 88 ++++++++++++++++++ .../Specific/Medical/Cloning.rsi/pod_0.png | Bin 0 -> 1319 bytes .../Specific/Medical/Cloning.rsi/scanner.png | Bin 0 -> 1938 bytes .../Cloning.rsi/scanner_maintenance.png | Bin 0 -> 1782 bytes .../Medical/Cloning.rsi/scanner_occupied.png | Bin 0 -> 4782 bytes .../Medical/Cloning.rsi/scanner_open.png | Bin 0 -> 1910 bytes .../Cloning.rsi/scanner_open_maintenance.png | Bin 0 -> 1750 bytes .../Cloning.rsi/scanner_open_unpowered.png | Bin 0 -> 1664 bytes .../Medical/Cloning.rsi/scanner_unpowered.png | Bin 0 -> 1652 bytes 15 files changed, 201 insertions(+), 17 deletions(-) create mode 100644 Content.Server/GameObjects/EntitySystems/CloningSystem.cs create mode 100644 Resources/Textures/Objects/Specific/Medical/Cloning.rsi/meta.json create mode 100644 Resources/Textures/Objects/Specific/Medical/Cloning.rsi/pod_0.png create mode 100644 Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner.png create mode 100644 Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_maintenance.png create mode 100644 Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_occupied.png create mode 100644 Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_open.png create mode 100644 Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_open_maintenance.png create mode 100644 Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_open_unpowered.png create mode 100644 Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_unpowered.png diff --git a/Content.Client/GameObjects/Components/MedicalScanner/MedicalScannerBoundUserInterface.cs b/Content.Client/GameObjects/Components/MedicalScanner/MedicalScannerBoundUserInterface.cs index 4b2fa2a1fc..2280406c5b 100644 --- a/Content.Client/GameObjects/Components/MedicalScanner/MedicalScannerBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/MedicalScanner/MedicalScannerBoundUserInterface.cs @@ -22,6 +22,7 @@ namespace Content.Client.GameObjects.Components.MedicalScanner Title = Owner.Owner.Name, }; _window.OnClose += Close; + _window.ScanButton.OnPressed += _ => SendMessage(new UiButtonPressedMessage(UiButton.ScanDNA)); _window.OpenCentered(); } diff --git a/Content.Client/GameObjects/Components/MedicalScanner/MedicalScannerWindow.cs b/Content.Client/GameObjects/Components/MedicalScanner/MedicalScannerWindow.cs index 61adc113f2..4668854fbf 100644 --- a/Content.Client/GameObjects/Components/MedicalScanner/MedicalScannerWindow.cs +++ b/Content.Client/GameObjects/Components/MedicalScanner/MedicalScannerWindow.cs @@ -12,18 +12,38 @@ namespace Content.Client.GameObjects.Components.MedicalScanner { public class MedicalScannerWindow : SS14Window { + public readonly Button ScanButton; + private readonly Label _diagnostics; protected override Vector2? CustomSize => (485, 90); + public MedicalScannerWindow() + { + Contents.AddChild(new VBoxContainer + { + Children = + { + (ScanButton = new Button + { + Text = "Scan and Save DNA" + }), + (_diagnostics = new Label + { + Text = "" + }) + } + }); + } + public void Populate(MedicalScannerBoundUserInterfaceState state) { - Contents.RemoveAllChildren(); var text = new StringBuilder(); if (!state.Entity.HasValue || !state.HasDamage() || !IoCManager.Resolve().TryGetEntity(state.Entity.Value, out var entity)) { - text.Append(Loc.GetString("No patient data.")); + _diagnostics.Text = Loc.GetString("No patient data."); + ScanButton.Disabled = true; } else { @@ -45,9 +65,10 @@ namespace Content.Client.GameObjects.Components.MedicalScanner text.Append("\n"); } - } - Contents.AddChild(new Label() {Text = text.ToString()}); + _diagnostics.Text = text.ToString(); + ScanButton.Disabled = state.IsScanned; + } } } } diff --git a/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs b/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs index 4e36ffed71..d50513e374 100644 --- a/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs +++ b/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Content.Server.GameObjects.Components.Power.ApcNetComponents; +using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.Components.Medical; using Content.Shared.GameObjects.EntitySystems; @@ -38,9 +39,14 @@ namespace Content.Server.GameObjects.Components.Medical _appearance = Owner.GetComponent(); _userInterface = Owner.GetComponent() .GetBoundUserInterface(MedicalScannerUiKey.Key); + _userInterface.OnReceiveMessage += OnUiReceiveMessage; _bodyContainer = ContainerManagerComponent.Ensure($"{Name}-bodyContainer", Owner); _powerReceiver = Owner.GetComponent(); + //TODO: write this so that it checks for a change in power events and acts accordingly. + var newState = GetUserInterfaceState(); + _userInterface.SetState(newState); + UpdateUserInterface(); } @@ -48,7 +54,8 @@ namespace Content.Server.GameObjects.Components.Medical new MedicalScannerBoundUserInterfaceState( null, new Dictionary(), - new Dictionary()); + new Dictionary(), + false); private MedicalScannerBoundUserInterfaceState GetUserInterfaceState() { @@ -68,7 +75,7 @@ namespace Content.Server.GameObjects.Components.Medical var classes = new Dictionary(damageable.DamageClasses); var types = new Dictionary(damageable.DamageTypes); - return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types); + return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, CloningSystem.HasUid(body.Uid)); } private void UpdateUserInterface() @@ -92,12 +99,18 @@ namespace Content.Server.GameObjects.Components.Medical default: throw new ArgumentException(nameof(damageState)); } } + private MedicalScannerStatus GetStatus() { - var body = _bodyContainer.ContainedEntity; - return body == null - ? MedicalScannerStatus.Open - : GetStatusFromDamageState(body.GetComponent().CurrentDamageState); + if (Powered) + { + var body = _bodyContainer.ContainedEntity; + return body == null + ? MedicalScannerStatus.Open + : GetStatusFromDamageState(body.GetComponent().CurrentDamageState); + } + + return MedicalScannerStatus.Off; } private void UpdateAppearance() @@ -178,14 +191,28 @@ namespace Content.Server.GameObjects.Components.Medical public void Update(float frameTime) { - if (_bodyContainer.ContainedEntity == null) - { - // There's no need to update if there's no one inside - return; - } - UpdateUserInterface(); UpdateAppearance(); } + + private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj) + { + if (!(obj.Message is UiButtonPressedMessage message)) + { + return; + } + + switch (message.Button) + { + case UiButton.ScanDNA: + if (_bodyContainer.ContainedEntity != null) + { + CloningSystem.AddToScannedUids(_bodyContainer.ContainedEntity.Uid); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + } } } diff --git a/Content.Server/GameObjects/EntitySystems/CloningSystem.cs b/Content.Server/GameObjects/EntitySystems/CloningSystem.cs new file mode 100644 index 0000000000..469c0f6157 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/CloningSystem.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Systems; + +namespace Content.Server.GameObjects.EntitySystems +{ + internal sealed class CloningSystem : EntitySystem + { + public static List scannedUids = new List(); + + public static void AddToScannedUids(EntityUid uid) + { + if (!scannedUids.Contains(uid)) + { + scannedUids.Add(uid); + } + } + + public static bool HasUid(EntityUid uid) + { + return scannedUids.Contains(uid); + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/MedicalScannerSystem.cs b/Content.Server/GameObjects/EntitySystems/MedicalScannerSystem.cs index 3180063247..01d1cd20ac 100644 --- a/Content.Server/GameObjects/EntitySystems/MedicalScannerSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/MedicalScannerSystem.cs @@ -7,6 +7,7 @@ namespace Content.Server.GameObjects.EntitySystems [UsedImplicitly] internal sealed class MedicalScannerSystem : EntitySystem { + public override void Update(float frameTime) { foreach (var comp in ComponentManager.EntityQuery()) diff --git a/Content.Shared/GameObjects/Components/Medical/SharedMedicalScannerComponent.cs b/Content.Shared/GameObjects/Components/Medical/SharedMedicalScannerComponent.cs index 8573a50c63..10262d1fd2 100644 --- a/Content.Shared/GameObjects/Components/Medical/SharedMedicalScannerComponent.cs +++ b/Content.Shared/GameObjects/Components/Medical/SharedMedicalScannerComponent.cs @@ -17,15 +17,18 @@ namespace Content.Shared.GameObjects.Components.Medical public readonly EntityUid? Entity; public readonly Dictionary DamageClasses; public readonly Dictionary DamageTypes; + public readonly bool IsScanned; public MedicalScannerBoundUserInterfaceState( EntityUid? entity, Dictionary damageClasses, - Dictionary damageTypes) + Dictionary damageTypes, + bool isScanned) { Entity = entity; DamageClasses = damageClasses; DamageTypes = damageTypes; + IsScanned = isScanned; } public bool HasDamage() @@ -56,5 +59,24 @@ namespace Content.Shared.GameObjects.Components.Medical Green, Yellow, } + + [Serializable, NetSerializable] + public enum UiButton + { + ScanDNA, + } + + [Serializable, NetSerializable] + public class UiButtonPressedMessage : BoundUserInterfaceMessage + { + public readonly UiButton Button; + + public UiButtonPressedMessage(UiButton button) + { + Button = button; + } + } + + } } diff --git a/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/meta.json b/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/meta.json new file mode 100644 index 0000000000..36d0f22970 --- /dev/null +++ b/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/meta.json @@ -0,0 +1,88 @@ +{ + "version": 1, + "license": "CC BY-SA 3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation at commit 9bebd81ae0b0a7f952b59886a765c681205de31f", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "pod_0", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "scanner", + "directions": 1, + "delays": [ + [ + 0.2, + 0.2 + ] + ] + }, + { + "name": "scanner_maintenance", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "scanner_occupied", + "directions": 1, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "scanner_open", + "directions": 1, + "delays": [ + [ + 0.2, + 0.2 + ] + ] + }, + { + "name": "scanner_open_maintenance", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "scanner_open_unpowered", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "scanner_unpowered", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/pod_0.png b/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/pod_0.png new file mode 100644 index 0000000000000000000000000000000000000000..13d289fd6cc923117d0ff7e9e6e78dc6fa29f990 GIT binary patch literal 1319 zcmV+?1=#wDP)kdTld!5zkG*y4t*8g!ux zT_8I#0gVD7DMZ>LrBCqz0)37%(|O#PxsUIDF79+{Tj*SpCVGk6Wg2o13a!jt69B7h7E z5eQ)K{=@R8b6+re?-4sY+W>G}!L`02as`u6C`7y=%9fV(#Ofj(J#r*4L`{G>@mphm zup!Z05qEj6B|&Rq4Oe?75T3{Qw95E&isv`C(iCstVYwm@Kp+Q-Wt+)ln(G6jtZj_*=feqpywO)_I29KgMfIIF%_8ZXi4fij z0+cKOhR1&6L4Aw|6O-igd88)+fsh`WZXl(^c5HG56IZOL17-52iGd(rFiFkka2!N!Ln_N<2ZPpBosF06Qpmq^dwSBJkO(S+kpTAmB14&CEN8EXeyor+j1)6AW;yGi>4V> z7lKA02a1-BW7~+z86aG@Dh}Xzz5|M)peP!`leliMDF_5mFiTjjTRDV$F;!J7-3OZP zm&0#LpQ=zS1_B6F0@wBJ)-?@04>8yMYK31>7UjE?=Ss8^0CvSrR|s6s!?qoN3IIjb z0MHEGcLfFM3CiWt$^nFQ6&m7fNHn7BI+kr$#6U_9JrqJy^-2RuK*21pumjOZn1I{|7rn>!=^$YX5l=o4!!0$Jp#urgy+wSTq^Tt~T*tC8vr+N zjj5rYW%m!%vah_hk1dI32pI;c%q-J?r|G?MV_whD+0?d`STu$ntw&1G41>bdy_$PJ z7GUF!SEXh7Z?j}FMKYP9dCwknO=EmINj6tlvc|DW5RKt^0_jN}j12(ulN1hz_^bcQ zl5#8nm`i@|=t)M$Mwp(S;^6)_u^pGwpPZ@YHLYvKFbpD*NW}qz#&xYc{B>x4uexUe z$e8!p*t!`ZG_1=0|L%(gM2T&?<*5@Na{KlzCMPE;ni&8rtH6PSN3pCj;c$e{&z}Y0 zH7PM%n-t|~hIa0~$3vM80ZD0-OY#OJyST+d9tmav^MK}~DpU)8rhp{b-4IBMGFn50+#Eq?5VEYpR9v5>^AH37erAyz@ z)%7|TFJ7R%y_3<=VcOd}863JpJRWDDzlYB4I~X49Cs&z-T4Naa^JTF+Pm1Q|Mv}>N dP2vAd`~xa+a~}W~hi(7>002ovPDHLkV1hLkWOV=l literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner.png b/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner.png new file mode 100644 index 0000000000000000000000000000000000000000..01b9c37f9e8b00fda6750dd0c4ed6d440969650e GIT binary patch literal 1938 zcmV;D2W|L?P)?6C~HM^LaY{JqdCT(pDg&G5jptVq{LMyE`5Y#9>P>f&v zprRkF2!5z2HAN`6R7#~%iERi}X`0YB4>xJjytA3v_wGFJ+`vCC$B2p>>6_Cqh@rp&X zQdQ-(Qb?sRrt;hC|Nh7A;O{8EdH^<2e}oqxm&@T7i{&h(6{!;1qm~rW zN};E&V!H0Swca(DUuk|z@GpOM0LO7~T#z0(Zut3ymo5U(yD-T19lHRKQW69{Mk#z5 zl=A>&5MZ>TH175?)P)Q0Rhxek_*Du#`ne%yCTDS?4vur(K%9t!>&6&+^&*@?^!5#! z%_Cd+>))Sew&2kjcX{pfS$wVe^ACO&*7^F^NW@~a$6b0lI@sLT%l_TpuHE@2@vB@w zN{P}MTUT153?#U*io72^`wRfbl9ODWo#Tmnw=h1DWY06d)e|{(96G;w?pCPLi3%)Fu z+y1eBj!eyv%;Xs9t=nY7`PPk%N~s6}Ne~2>rCBiAAeS{*l9Q<^-nej?;kDh2Pb4{f z=?Yk)0uJ!@M2ZLQ>ScCuk%I5n?Np=r)h-Z7w9+V{Q1be+5d=XwxRj)r%aO?!@ZvF! zUQ6-6nRz^|m@aq#bjMv>%SH%+l#=OemfX^}6$hZf{3;6oFvg&bL1~TBnji=&lqtz< z!DGtv2t1DyscAeVnabucQetG7_LRiaiu2Pm6tp3AlL2TnzlIA)gb-+@!!=ZeGog(^ z-k`*sSHxbN#|VM16;ru9MoRob0l!cPmB{DGESX&(CCN;#Zs!}#uNef5F$f`0QeiuG z87zZJ6P(UwNh`QGJBwd zZ+@0(B)_H$2vNQM)>3No$@2hAuU-Yl;6|d@SS;o;2q4kghKV=?#^7tiz<=MtzsWM# zXnx%opjViQ)oa$8haY)@Y%WJ21LUD3+&6+JeN?eXRD_0=Ef%mv3_IGw!i%qN6)`gZQx+9D3aZj`(G)*>)CmW^!;+Gv!~*v8=4 z4nhdlcdY@SCtBO+n#H$D@X|fofy48x?&_%d95_4=z`CK02B0MtBe8Tw?{00yI1acj zmg_<^3N0-d$DupX3P8-Z>5NxfKr{JH3BGUt?s8@cw30mZ&=>gEt1sfl<3yY&5!WT| zROF>JtC7Q%6eI}swbO^@s1#wss0n$33r_U+%zw)LT(96e{)_vr7cpwGVa z8-~^;0oecMZ|P-_7RSbQEIQp7LI`?VTA0e^DQ&%k5cIURAcSC*W7F0WCl;x8foAe+ zI^-|LzE^YPhp1k;qOll`yjxUxkKihZgLQ2WYr~gu=q!e zD->AUyN=1!<(hDSuC6|cev!LBv58{QBavt)n@uy7y2kwc3?uj4Usb12$dJ5tg-m*m zj*d>W5f~F5ax&?;3QK4PzhWz#&u0M`-Lj+Ny?JCSt!*6u*p|R7{de=d0sw2edH_fz zFIPQ_3*nmHzN!mt>fiG>gI^I5GU+)0W@l0r@ATp#f$x#aECBHCyR$VrP;d5bMVi6? YKME-2{eM@NKL7v#07*qoM6N<$f+N+!;s5{u literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_maintenance.png b/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_maintenance.png new file mode 100644 index 0000000000000000000000000000000000000000..b659cb7f35c244a5481737196924d6888fced1a2 GIT binary patch literal 1782 zcmV6~}*f=FaY{y>^`V5xX{VL`&*KR;`e>v>~cgLM0Fqk4jY$Qj16-Qp8gNR7Ci~ z2SD(F@-?6WK}eN|imD>2qR^s9fSSf?n+JB9H19aOetX}uGxx!V@oqfXb=veuS9AA1 z&iUW}IrrSNf;*JKp;3FMdu}K4!)*HYcMk#(hIN$EL^`5UDihS}7_D2@8?8}VV{LNo z^?&}Y4S?<3{)8(~sZQu;`?ciym}d?5radccE|WG{`}WJu~-Y}&G@`__B^36JpcWl z#CyK_6|(6x-5H;Oo*s4#4RUz@x0=SGO$;cd(8l1Jq$gCQz)vT}et76P08Zv+xxTo> z(+})qY9_~l=YG!BrDa^t1K`AsJYl7R3Tph~FNgW^0}tIi*sTB{_M27?!V&GJo03VT zc>UrPj=lRHKmF`pre<;+ojOO^XjVeiT(|op!N)gs-gtb}>qFSZ2N_(Ufxl$#eqiqmcYY{@A zmBy8B)3Az?E|@QuDQdVpzsO82K-U8-0#&QtToi_wNI`cO0Hs^p*wzJNO?qfYeM5?2 zoOp~K5x8#?$ zE)b&i{<~MEtYw^7lE)IoC2?1m~yRe=lvX;W8c zy)I8O=BK!4XfpzfbX;6(G1j21!L=6YN`w&H-M0yVfmD)or(wKN=N&!1%*MW+w(aQg zWdOEpz1ISCq|;Ub*Y;2npj;~O}RKN;4BS zojN!)YPXF|prWYd#cD`UtFW+;W82sS6B9ehbY`1>$tK_Y4I|MEzIVxwlxmW*Ed8xtaH!Dwo|VMWV79r%SGn$H&|X?VEn#M zwCt(XO5|=_qf}g?r>7TV1=hxoT&cK}OvTk|8GwnMdlKIrH)xY1a|d*Ul`N? z*wi-wKt6Z1r4?4f4mP)jxV#cVfP@q>Nywya(oATElqA!pw1LSa z(`Fi)>69|bB+PUmfs{N<0tqFK3Be`?+XV5l0bwl5vMgz}UTODgANR4lSKB}C-hFFj znM{~z`OWNT_ug~Q_nh-PkKg@$zp}AR>+I?+EccmaHWN8zdnfT7=km}OZvSfOyQ-@6 zWOV>W(`h296p|oTl^+tEyd=j_90Cgq`O@{LpL+aU;Qy%j^#d?K^AVO!P+iwB3t?{v8XLhb82T_A|ATpL`^_LnU?^i__(Xx3 z$SK>wXMbB5Sign;e*DKIQfWdyFHeoU1;Bk@`bPj-{^FA$iHwONz2Pt$JKOp3=fBjr z^F_PiTC?*Owq<4;Y&^cF9zT;a@Hp)}eQFAT?|trX0SJEO?{K*!qN2#4C~#hDn1?_8 zxhnj+0T4aq@BLfL9td_pe5$(a-+%u<$m9%OoSY^$pX3AYxsdT>np^Mr7Sl5`I9+Z4 zUYL&3qN$+k{PWKq#G_b<_>Oay&3OJu^{PRaSG&W9nZ9l9!kH=mc;gc6_=B1e!YP$JG7@vYekHEH@s__p! zQa=O7XXkijPUZrio1YvzL9d$Ts&Ih#^M`nTe1fi_t@zJh!=9-#Tzmd`9Em5sp8pPC0Cqi0E~ z8i~BobN~P{iSjJ4W`Z06JyTt7M$<`Y8q$J6dTy2v^mem*&tBx&S=M>oM27!>o`{#q z+p;=}u2g>uO>PHVALoKO`M{4LzGBG7}}ED0suIL}w!C^Yi$e4q7_9KvmGQ=Af&phT``%9{@Oe56GHEJ~dC$ zX=h^WBtmx=da?5WYGfKkQ?a>Sq`Ft)kwo)2T*87Y%)d(=!*d02s2LKPjqzs>AbQ;> z*&KEtmCG~t@{4HkIIFe-oo1zRy?O@L4Zw0d0Fc#mOoG0N%gZhXaP&%)P7UZ*YSCv*O4 zz((Hfsj9bOYtGgK8;(yhP>?vPoGD$KmCYHD&Q_0fa(Tz~z?v1Kx(^eiiK_3One2E27s!j z(R7vI1#4k$wsb$8%_6$p_dbznKFL3 zmG*)46e3f+F>7IpH+3dtw5+<5@_o%cTCCUq96#RdwPS2 zuSM4gI_%B*Uu8C%x%3vZHo=Ljrw<BP`fM7O&NU)6OSqKFRoC84U0(hRV2 zZOie1)8%H}_8oMs-vBNb0Ii`AuCA5kP08iL?e{Y@bP)@pM61tRUr_~qe9An9tc`f| z1#<(o+Er;{Ai%aOE~oX9?Kpe^f(|=vot?CAxd<`b3V6bJ?U^!A*G%rn*-2$r6)iz3cVTL`wd5e|n~ zQPY50d@Jh&<>d$FF z)L;*|>%m*uvB`#>F}-}Y_?89TZai)`-cX3Nv4By`3LCjx>18PBcH>vGxW(!jShWe> z{59Bj9ei{5m+Be-@Qr(IJaBvYchuu25RH)#JgoA12->@l<$2Ot4kQ;8y1_LapI&#f(t-|6jHZ1_3-FXMYCkotl zojLV)?`vWAz7~eo*ce)4WA{FDue|NL0>dW??CQ9&RDsjw=J?DRwr}2OHY6CpyO1|K zFS!r|k*(|2^6L1+qK)|M&O1tVH^aBuXyL0Zb|j~RSLS3sIM5G3*j4thwFEBf?&P&( z21kQ8JO@B3JlLB z)mBM0UApsqdpJ>3SP1DM$+jtrzeh=u3dlq$GPyYZelK#rcH1X-~VF64lZ1} z2{~^Nbl4gD;m>$x|DIAL_IB|HzxWyAnvPr7NbEh>5MTVzm8H&~yytsW_@!;Jv#Ymo z!MZINhE6t{Ygmz{tHh#{46NV6MMK*#3wD;>VA1&AI_b z@_FWq8KO5vj&k&s=iYJrs!cE<$B0EI8-fG0cdWu6XeHnea^%RX09@A6!bjp!MwARk z#*X0>s?S+XRoSAX`Pg&&xZCYzf5q!)XIF348{s>FU-d4a7kBMVgO}F*m8AOD&0JRH zsFCM8ZZE%|k25j z`hXpqY>OsjhSoIJCs_CgBQdmWwxkc}`r4fUP4xkF9@w$T#`!~ylaeKs$cPz1KK#-! z7Y4i>h^Ls2Cu0d-ot|OrjCpJYR;>cy%C-ni+<(XQl*)gI}LX@Mh!; z+qzb;cXW)`BT-(9M9q3EeL#^KSM3GxRFeyJNzqDfaH*8on;Dgth0*}nhF*$mi zs|*$Yt1oimv0rfJs7d2IJ)2-K=p&-m3lwUtl9?KPp?OMde0Gk}XpFv~kDnYn!O8Jc zT<#I+I6lJoZ}*~4PoihD?2p9Q6!tTdQJ2;Sz}b61&l{wR9ZH^wa%EeP-FxH~_|;c-=4 zS|6|+ZcsK1l)Qm#7^r!pil28G21>k4myf2+FgLZt3|I~~7}s>-x(+^fLw!KDs76uq z29p_+)QA?Ux1P7f4W@Di^Z7i%VpgV-pPx*nafe$GD^lh2`8?4YcDB}*!wo7%0dhGM z!vImN(+61eyvt!HUz`PVa-#VFEXM<9iaY&C+T`Gk-qqw22~!`?Vkfk^4?UZuvfZg% z^NnyhBB{8ZMOIX%-gvDsKVNI{DOk6C>0`K7pU}Q*M?-x;=inCd z<712J6B-6Wmeq2Dmeum}_RDtQ@CC@&TI%?DPkWI)-g!ys`K9y$meq2DRv=WXPjGBl zhb!PO=>vcgzSn;a`hc@#XRo^V-^um&(Jh)`qZN2GqQ+ZAk&e6WLRmXt@$){1gHU@Ln^$&{ zcX`OzT5yRXp7s?bUQS%OiZxeVP21){q!lY@TS6c3_@2U|5}jK7pxsWOeFa7r^)w5%D_>v&twkt@*;LDQh7&S>zZ)X=$AVAo#v9gAqFX;om z`-Dk-FU8M)`d+y0x@vyj=Wrkj0{+e}T6B%*A`SOC97y*1gU8Y*m~}6X|Bc5YJaBti zA7F9)LAxD~B;pO3>${d6XtjY&a^r7FQER0=;DOuA$4oPRzEU4xfdi~^OBgN@Tw>h- zxE*$Uj-@m35C2nh{0DBYKRGRZK&vR?3I>R)It!wNk;|2q0YHbX!084_jw*eCrO&Z= z`7eLHo}aJP2Z#%^Gt?TKWLH z(}|JG;jx+efZ^FhJur*t11w(t(m%6t;Gsf^qp#)XD{U;JRP*!e&Ho&eWw~kR-wwW| z4*(d6%~j*?*jmD`)Cbfq!MCjs*mma^iRxJbIi1MQ_B8CE`;#}C9nBe>{N}g+sQQ2{ zcYJ|4IYHP4kzYRD5MTVzm4KN*c~>9su0G&heL$7{zt;!+FR{?d%MIhxZvX%Q07*qo IM6N<$g2$F^TL1t6 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_open.png b/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_open.png new file mode 100644 index 0000000000000000000000000000000000000000..4af3b016115cdbbb9d2363751afdb1c8e6e8cb78 GIT binary patch literal 1910 zcmV-+2Z{KJP)Bd~={ zsZhkMRFFah-j_n)2!ZR?{`T&_-@Fz4ZRH0Yuz|)No`zDXgjK2dMM4Tvr;gIye;1A= zupJ9A+Yu=Qa$*cOcE`<)uF3q`@SBBy^$Q6!O+z!lF+nrDpI?1_1c3PTDz45@B)Fpr^BwjlFU9@A_{2 z!MBJXSOSjYAf!ZbSjIg>i`_R zmclBPaLh8l`TKspcK2rjV{a9|t_VsY5Kags)L5XS;V|!Byv(7G&hy-tb})1`$$_DB z6s2I=a{TwUeQE_qC(u7jw~ePS0x;RN1T8bgz>0N56gVuI^Cz&ROX9!Bv2L&pHk;ot0%RR1 zK-bdc?gI}#O0iVJc5IwONyPFVPFx)ZU>TmrtAE?w-AupsmappT`v(Brg|5QQw~}k?&^0>S+lijzz{_@OGinrAz3I7?Tov+i@?a29}-eRsM5ZIKA=Muau-9^6ENv<`)^=TqA4v#+W-D< ziL;6}O~uecbQ=+0Yyb+m*#X45P&7}DmS`#+ZBZh6a0#@MUw1+NYVhfL#q<%}xx$f1 zP3{h$V@VePGxN$K;V?$H@j1YVMgX&^{1Cvm?)?eJF1Q@K;OWrd!4QK7Lu_56uyu{X z;6ZO)Ro}Tc{uKbEs$m!g!{cdw`SfnT(LydeHw(bt=YGM-)Wm`VukznJ?cLR@+^;{- z$31sy{PC^1=(yc{4WL-a@$lv+kU}tc@=xsjw&#c+|5@?B18nWs0l-YjyHiJWFLNt( zjSJZv`<{IUE=|_}&UHx6PNdOw9b3x4Y}MmG_6K<72P)gYbD81Y@bd2eA77cj{cPL5 z6UT9Q_LGrmAV^_}mWe-2Hi`Dn8Po(&;4Y`v({p*i3X$yZ`qT&XZ`kCQNg+5hbduAj-fua-rUT34D;ZCX)`bJaV!c$X3TxMGpi(huZ(mHY zm}eq&jchi}rn~M5)G3z>B(Gheke{Nnvm5CuxUP4}DdeYWBB2%hnxpW{Oc8*A&D(1} zH*VU(qK-}g)R2NZo2#=-FX6Z})&oE)IU0C!)1DYDUJ^sPjr07i;MXWZA@5~WGU-&! wXFiw1woFQeX#hU_FjIE`jb`s=tQGwK0eA#*E6!n-IRF3v07*qoM6N<$g0pJA4FCWD literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_open_maintenance.png b/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_open_maintenance.png new file mode 100644 index 0000000000000000000000000000000000000000..513eb24d71fac5d24083355b81a86f45d1b3965f GIT binary patch literal 1750 zcmV;{1}XW8P)`BYZSQ)$U*6f7d++dLykn1d zosu4Dw7d7tz3+S8d(OFMHSW@q-96sjuDO#LA7{~bzjq7($EhKN#FZ|UQW?8eLrK~2 zzLF9lC7u_ad-tDzZ9-r#clK}*DwPUOtriSPCCOa5Kxf~*2uI=y2f0i{Nr{@9!b{xK zbUUpX_}4z4!Y~XB3xo}ZD-Yzx7p6 zw{Cq%COx4w5P0MZy<}%|7*PYmSP{rD4J<3h>9@yW1TER!ZoUwYRgcJISe|-wD8>Nel=fkV>Je@JR?)fE5dk{ov%w z09;DXGL>84@%#5PJe}srmw&D#)6;psHzCT>GS3xNZ?Kb_$-_aw;`3RE2@G}m|p zu0TnJ)Fe`@q>bykL2%WrR;f^0s$$1u49#TFO_QnIVpFw*5X_g$RI2u!2>^JWhw?n6 zQb7Gk8uL3>wVAVRcH1_?xkYxh#@W>x5504vTm@ikK1)@3WIl?35CTopP*Md&MGA@X zJj5Cj3w8~?kjG1OuzQ2Wi4!N#bU)d&& zr1CRimCcvS6eWz$=Q#1oD*>=(g#G%>OUHxnr8URclz_IXJT#NOZbip0JZgEJ$(ZJUM4-E-SIkBQms2TJbVcN zyJoXgDx&R3q4oCSjgO;4Z~=L#6fEQz8NQ5em<{_h4Z^w#tDn`DUzpXdU%M25r;a>^ z7Fh|rW??(7EH`sj-83k0CYveqDSiB^*-;+xjY?-_NLF7b9n%^_kPj? zXoUv-g2g7XfY#fKRw`oZI$f596$>{t2EegXN4amOUwnpc zdK`P?*A1X2-+YPQ?S5rD_1>>Y(nE`(V;K=TtymCj0HwmR1Bnha!w<(zhE7{coR}Ft zf~HyU^V82Z^}LtH3K@;XLcJqE+olcxmR60$qEW2qI(6WQ2Y<-WO^>0QejJ`0fuRxb zM&QWc3mh4I0U&ti!Q__#P`ZI-S&YtR`PsA2gcJx=0FJ--Q!Z!bnmT0wluHF3-Tx#? zN={$?Z7})7w|(GOe|MbU?}hu?4*8x{{5w5n`kk{f4Q}QOoOu2@xV0DpxZb9?HkZXT zO?=1wL&(VW>4P$0|WbsZ)gqv zk`0~z51%^lK*JixakzTrefswH2V+V~MusnQ^~!r%h``q5c4jk^O~C;YiEe66ja|F< zQmfgtwr-?cE;5&yA)n9EfA6Oo)>Nw{(lfUy6&Gl4??ibTp66e3rQ$-k6fZ560T|dn z82a4Te}D~b?EvTz4R5(ubZo!j+??nFAd{YKcnXVtFmBwGKzWT>6pDya(eG$-*-YrO sSSa8+HkHyM03UpiYpT$l)LYN~2ddpSeQimav;Y7A07*qoM6N<$f_lVGJpcdz literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_open_unpowered.png b/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_open_unpowered.png new file mode 100644 index 0000000000000000000000000000000000000000..206e0c02aca134280fd9a43e1f81805bd58cacec GIT binary patch literal 1664 zcmV-`27md9P)A6O6~}+?Yy9n*jO~nLf*n;Pbs8%}KBPn@1WFqfS`|brr8GAf4Z{Cb&-p9R{#hZDa=Xp+*a-^#_@4kD^ z|J?um-*fM1Jf=+^m{E`G^C&YviPMjMd>H`Ga|r^0FMJwKotEn&h3FYCg&+_DrQ+ZI z`j=M|5}4xAGgQ!MH1J$E>Jm~=So#-Zv&RWMfggB?b|O*;mq%RXTJTlSd2swDk+gdL(_T%)om%$WO<{+?e!wx$RFcj zxysFl1zJk79qjlX9?P;~J5-u32h;HwJq$=G34}xl5wW0@LIl0ZJg{qgu(`!ldX$Id zDj$`~pc4ZJ_;0z&;eBc9&27B4KK5il2pKW}$S&H25RtMw=DRK}*Tc6g?pAGd)1X}U z6WJ0{;b{S*-9U{DG1+IaaP=yh7Otvhgij?T7cM-D6NC&_nrQ2` zgLY<*z7uwLGXAc3dum`;jcg|q&)#SkDor=gG#$NO4?kqHC>^v)HGH>r3Yx#Gz9c7* z0G@P0JUcQ4BU4+t>e2pcJR&AYQS-haa0jn}NN^ z-08L^cbJI=7c^V3<}dBJe2|R`))P9>*rDY{9g(58%6{Uph~3(umi3(H)B2{R(u(v1U|`Kuiw-e?C-BhX-U092^Oo*37#bN&Y)f|<08AfxQUUat7X6k*EtzkL+;A}VCg!;srXr8cEB%RxfFGFfcCPdpMHUb*M5(cN@3_G zh7|_Y_U}jE*kEWhO+RUB)gs!&1X{C+q3euV7M2;`cqYK*E0=g87gnE@qT=!oe%S+h z<&~c?F;)fOHw(XDk{MElhGl3Br_9*eJ9Z`d-4W)XuG8O_!ZM9m+{8NY^I!iYapn^V zCDJr43_TunB)t!St!lAroQY+`$3a^I-+%F^tP~Y1#ZZS&LF{^mvQ+jiolOn|y$^U@c8h!ld~{o_}O0|8DS4EOMMBh-jx8W_5c zGIffLZNB&7^PE3=F6Q&wcNbW%IT(gXASJpU-vwIt_M1O(YdHk3c>B=8{Axa zM~fwpo1CUtSWN^67#}}?>$!a9@KId1#qjVb^?Hqs!WxxIiMiuvdVHEqhxN7lIJGjF z%otK>C>0)ZPOTg-#oOEAg(83ATKQw8At_qP%mdK7zm+5Z3nyb83G`EeEi0000< KMNUMnLSTYH3KfX} literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_unpowered.png b/Resources/Textures/Objects/Specific/Medical/Cloning.rsi/scanner_unpowered.png new file mode 100644 index 0000000000000000000000000000000000000000..4d0705aac0731debfd943acfaba62f97cb534dcd GIT binary patch literal 1652 zcmV-)28;QLP)93lX(4rp^YBdKj6LtUk8=)-J2Rf^xeiMCq@%h1&iP*d z?{SU=Pe^m~i}p!n9!JiH5&F(|F9Hw*K2f9zbx6C}qUZY z2NrmIgcjQEHi7RCS=wkStN-A@(hEd^CX51f-w~rV=D}TTX(3geNHe~&Ttq2_bisrm z-FW+NZ$AWJ&MtEH+$#V?QA8L9Sgi@7aL5CQ!VqgTgSgd*sav<+r?Polj5%?rC9;O=zkd^e+R_oSuFHh$ zQka_LBQrDH_|@g1aR9j&60J4HTCBBu_+|{gl~+Fnz{XCUzpmfr#KJ74i3!S|`y_`x zaf*-49%QLFgN{NX-^YXjFMsVz2_30LXpO;Wfe_;bHA}7$R-ZS`SgM)}o_vXYSS;y#MeK3)2Pe)pqz` zs|J#Uae)8Sb~yUfG_7ujpkE*RBA~U25dh2x>{{z#We+IyeR_UC=(^n6sdKm4CbWh| z5Eo;?b8&@42++pR?Dg>bd;2;NhfM6i00CnX&J3s(gfzmCxiH{fD{@HKyM98)z6c=V6i6k}eZzY@q1o%vHSn;}q85gjesnqrb`M1nMmsPu0YJ|r(>R6z zkQh&C&=08FFE(p5SPK?x6oC*iio=52ZRv22zH|rn8OD3(yy_1sA=TgjL{Z;=0Z{~6 zC-jAT=1Y1CL<%p(sn6yYAiuzs?G3ONPdP{g9lwVFGMOwWMFd3FvSKC)s%`%LKOGp< zKi_AbhC$MmGv0K_Pat`XX5tUNLT`+=9-c6=qc>2S<^|Fp+!Bfo56cB(C5@{`lcu8wfQX+(4HlG7v z+DXPN2gV2Qyw#g+rt*`iYxQOufWt?gvj7>#CEJgvkjY?`0@p>lZd{OF%qwK#6uDB8 z^Sp!u&cOJ^OBaUBfua5ArI%jg^6!3$>v<^Ypj;1E1xUxi3W2Z|3Gug7xJsdKyfmI<2blVjmb-Z zZ+_wXtXD1T)!6fAX2sYVptD}Jy!hM~0kA^hdM=fElliGBijx^UDJf+$l(O-6aWcbv zAHR3vPZmlD!wmUpqD)Qp-a|xNB zzjc|-MiZqRB4dy;*#%;>{m!0yg(!;n^_4fqjDP0Dc^dUPckkRB@?QV?1-2(N^`1}8 zb2%br#^kKETo1PJjlnt&Gq<<+^Phe@oP@)q&do2{V@qYCFeDg#P>fKo+h%KPlVeL| z%H@+xOy!3ElD+-bo18lR@iCbo2)MTTPnM1^4|zswZmhk_wbj3igafm4hpARJQjG(Y zO7r-E&$CC5 Date: Wed, 19 Aug 2020 16:49:34 +0200 Subject: [PATCH 08/53] Fix disposals throwing you into the shadow realm (#1792) --- .../Disposal/DisposalHolderComponent.cs | 17 +++++++++++++++++ .../Disposal/DisposalUnitComponent.cs | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs index 44c1a1eab3..b07efbdef1 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs @@ -6,6 +6,7 @@ using Content.Shared.GameObjects.Components.Body; using Robust.Server.GameObjects.Components.Container; using Robust.Shared.Containers; using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Maths; using Robust.Shared.ViewVariables; @@ -55,6 +56,12 @@ namespace Content.Server.GameObjects.Components.Disposal return false; } + if (!entity.TryGetComponent(out ICollidableComponent collidable) || + !collidable.CanCollide) + { + return false; + } + return entity.HasComponent() || entity.HasComponent(); } @@ -66,6 +73,11 @@ namespace Content.Server.GameObjects.Components.Disposal return false; } + if (entity.TryGetComponent(out ICollidableComponent collidable)) + { + collidable.CanCollide = false; + } + return true; } @@ -93,6 +105,11 @@ namespace Content.Server.GameObjects.Components.Disposal foreach (var entity in _contents.ContainedEntities.ToArray()) { + if (entity.TryGetComponent(out ICollidableComponent collidable)) + { + collidable.CanCollide = true; + } + _contents.ForceRemove(entity); if (entity.Transform.Parent == Owner.Transform) diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs index 399ba6ffbd..2849bb3c16 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs @@ -122,6 +122,12 @@ namespace Content.Server.GameObjects.Components.Disposal return false; } + if (!entity.TryGetComponent(out ICollidableComponent collidable) || + !collidable.CanCollide) + { + return false; + } + if (!entity.HasComponent() && !entity.HasComponent()) { From 81fea61b7228817c6e066804320a863e38bc0d06 Mon Sep 17 00:00:00 2001 From: Exp Date: Wed, 19 Aug 2020 17:02:31 +0200 Subject: [PATCH 09/53] Fix Roundend Summary Overlap (#1789) * Fixed translation * Temp fix * Add "TODO: Remove" Comment --- .../UserInterface/RoundEndSummaryWindow.cs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Content.Client/UserInterface/RoundEndSummaryWindow.cs b/Content.Client/UserInterface/RoundEndSummaryWindow.cs index 5f2d736d9c..550c48032f 100644 --- a/Content.Client/UserInterface/RoundEndSummaryWindow.cs +++ b/Content.Client/UserInterface/RoundEndSummaryWindow.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using Content.Client.Utility; +using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.Localization; @@ -17,7 +18,7 @@ namespace Content.Client.UserInterface private TabContainer RoundEndWindowTabs { get; } protected override Vector2? CustomSize => (520, 580); - public RoundEndSummaryWindow(string gm, TimeSpan roundTimeSpan, List info ) + public RoundEndSummaryWindow(string gm, TimeSpan roundTimeSpan, List info) { Title = Loc.GetString("Round End Summary"); @@ -65,30 +66,40 @@ namespace Content.Client.UserInterface //Create labels for each player info. foreach (var plyinfo in manifestSortedList) { - var playerInfoText = new RichTextLabel() { - SizeFlagsVertical = SizeFlags.Fill + SizeFlagsVertical = SizeFlags.Fill, }; //TODO: On Hover display a popup detailing more play info. //For example: their antag goals and if they completed them sucessfully. var icNameColor = plyinfo.Antag ? "red" : "white"; playerInfoText.SetMarkup( - Loc.GetString($"[color=gray]{plyinfo.PlayerOOCName}[/color] was [color={icNameColor}]{plyinfo.PlayerICName}[/color] playing role of [color=orange]{plyinfo.Role}[/color].")); + Loc.GetString("[color=gray]{0}[/color] was [color={1}]{2}[/color] playing role of [color=orange]{3}[/color].", + plyinfo.PlayerOOCName, icNameColor, plyinfo.PlayerICName, Loc.GetString(plyinfo.Role))); innerScrollContainer.AddChild(playerInfoText); } scrollContainer.AddChild(innerScrollContainer); //Attach the entire ScrollContainer that holds all the playerinfo. PlayerManifestoTab.AddChild(scrollContainer); + // TODO: 1240 Overlap, remove once it's fixed. Temp Hack to make the lines not overlap + PlayerManifestoTab.OnVisibilityChanged += PlayerManifestoTab_OnVisibilityChanged; //Finally, display the window. OpenCentered(); MoveToFront(); - } + private void PlayerManifestoTab_OnVisibilityChanged(Control obj) + { + if (obj.Visible) + { + // For some reason the lines get not properly drawn with the right height + // so we just force a update + ForceRunLayoutUpdate(); + } + } } } From f79085d43e05af6d0e2037e1512fa6554dcbf539 Mon Sep 17 00:00:00 2001 From: Swept Date: Wed, 19 Aug 2020 09:24:50 -0700 Subject: [PATCH 10/53] Tones down screenshake (#1794) --- .../GameObjects/Components/Mobs/CameraRecoilComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs b/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs index 68288ff66a..f9c317d2bb 100644 --- a/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs +++ b/Content.Client/GameObjects/Components/Mobs/CameraRecoilComponent.cs @@ -23,7 +23,7 @@ namespace Content.Client.GameObjects.Components.Mobs private const float RestoreRateRamp = 0.1f; // The maximum magnitude of the kick applied to the camera at any point. - private const float KickMagnitudeMax = 5f; + private const float KickMagnitudeMax = 2f; private Vector2 _currentKick; private float _lastKickTime; From 9306f41bedf576e5ea1da6d7ba12c554276c6285 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Wed, 19 Aug 2020 18:29:48 +0200 Subject: [PATCH 11/53] Update submodule --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index c72ea7194e..1513389fc1 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit c72ea7194e08bba42371a06e353bcd79f5a7c7cc +Subproject commit 1513389fc195442a0f08b0af488a25affb33d410 From 17080a92ee2abbf19ef1df2b6188d3c11885fbdb Mon Sep 17 00:00:00 2001 From: Exp Date: Wed, 19 Aug 2020 20:41:03 +0200 Subject: [PATCH 12/53] Clear the ready list once the lobby is left (#1791) --- Content.Client/State/LobbyState.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Content.Client/State/LobbyState.cs b/Content.Client/State/LobbyState.cs index 20b4256393..b5fc2c35a1 100644 --- a/Content.Client/State/LobbyState.cs +++ b/Content.Client/State/LobbyState.cs @@ -107,7 +107,10 @@ namespace Content.Client.State { _playerManager.PlayerListUpdated -= PlayerManagerOnPlayerListUpdated; _clientGameTicker.InfoBlobUpdated -= UpdateLobbyUi; - _clientGameTicker.LobbyStatusUpdated -= UpdateLobbyUi; + _clientGameTicker.LobbyStatusUpdated -= LobbyStatusUpdated; + _clientGameTicker.LobbyReadyUpdated -= LobbyReadyUpdated; + + _clientGameTicker.Ready.Clear(); _lobby.Dispose(); _characterSetup.Dispose(); From 20ab566f8cfcac6c9c2563b48f01c901c5a68be5 Mon Sep 17 00:00:00 2001 From: Swept Date: Wed, 19 Aug 2020 11:41:33 -0700 Subject: [PATCH 13/53] Switches current PDAs to the /tg/ ones. (#1796) * Switches PDAs with tg ones and renames flashlight component * Which chucklefuck hid the ID cards in the clothing folder * Removes Mime PDA as i'm gonna do that in another PR * Moves EVERY SINGLE PDA over by exactly one pixel so they're aligned perfectly with the UI slot * On second thought moves back the id-cards since I don't want to deal with conflicts --- .../Components/PDA/PDAVisualizer.cs | 12 +- .../Components/PDA/PDAComponent.cs | 2 +- .../Components/PDA/SharedPDAComponent.cs | 4 +- .../Clothing/Belt/identification_cards.yml | 6 +- .../Entities/Objects/Devices/pda.yml | 94 +++-- .../Objects/Devices/pda.rsi/id_overlay.png | Bin 0 -> 90 bytes .../Devices/pda.rsi/insert_overlay.png | Bin 0 -> 117 bytes .../Objects/Devices/pda.rsi/light_overlay.png | Bin 0 -> 763 bytes .../Objects/Devices/pda.rsi/meta.json | 368 +++++++++++++++++- .../Devices/pda.rsi/pai_off_overlay.png | Bin 0 -> 148 bytes .../Objects/Devices/pda.rsi/pai_overlay.png | Bin 0 -> 171 bytes .../Objects/Devices/pda.rsi/pda-atmo.png | Bin 500 -> 0 bytes .../Objects/Devices/pda.rsi/pda-atmos.png | Bin 0 -> 492 bytes .../Objects/Devices/pda.rsi/pda-bar.png | Bin 403 -> 0 bytes .../Objects/Devices/pda.rsi/pda-bartender.png | Bin 0 -> 432 bytes .../Objects/Devices/pda.rsi/pda-c.png | Bin 442 -> 0 bytes .../Objects/Devices/pda.rsi/pda-captain.png | Bin 0 -> 479 bytes .../Objects/Devices/pda.rsi/pda-cargo.png | Bin 417 -> 435 bytes .../Objects/Devices/pda.rsi/pda-ce.png | Bin 482 -> 530 bytes .../Objects/Devices/pda.rsi/pda-chaplain.png | Bin 0 -> 454 bytes .../Objects/Devices/pda.rsi/pda-chef.png | Bin 420 -> 0 bytes .../Objects/Devices/pda.rsi/pda-chem.png | Bin 478 -> 0 bytes .../Objects/Devices/pda.rsi/pda-chemistry.png | Bin 0 -> 479 bytes .../Objects/Devices/pda.rsi/pda-clear.png | Bin 0 -> 516 bytes .../Objects/Devices/pda.rsi/pda-clown.png | Bin 526 -> 453 bytes .../Objects/Devices/pda.rsi/pda-cmo.png | Bin 450 -> 536 bytes .../Objects/Devices/pda.rsi/pda-cook.png | Bin 0 -> 433 bytes .../Objects/Devices/pda.rsi/pda-det.png | Bin 434 -> 0 bytes .../Objects/Devices/pda.rsi/pda-detective.png | Bin 0 -> 432 bytes .../Objects/Devices/pda.rsi/pda-e.png | Bin 500 -> 0 bytes .../Objects/Devices/pda.rsi/pda-engineer.png | Bin 0 -> 490 bytes .../Objects/Devices/pda.rsi/pda-genetics.png | Bin 0 -> 475 bytes .../Objects/Devices/pda.rsi/pda-h.png | Bin 452 -> 0 bytes .../Objects/Devices/pda.rsi/pda-holy.png | Bin 408 -> 0 bytes .../Objects/Devices/pda.rsi/pda-hop.png | Bin 462 -> 488 bytes .../Objects/Devices/pda.rsi/pda-hos.png | Bin 561 -> 503 bytes .../Objects/Devices/pda.rsi/pda-hydro.png | Bin 478 -> 491 bytes .../Objects/Devices/pda.rsi/pda-j.png | Bin 415 -> 0 bytes .../Objects/Devices/pda.rsi/pda-janitor.png | Bin 0 -> 473 bytes .../Devices/pda.rsi/pda-lawyer-old.png | Bin 462 -> 0 bytes .../Objects/Devices/pda.rsi/pda-lawyer.png | Bin 436 -> 446 bytes .../Objects/Devices/pda.rsi/pda-libb.png | Bin 456 -> 0 bytes .../Objects/Devices/pda.rsi/pda-libc.png | Bin 902 -> 0 bytes .../Objects/Devices/pda.rsi/pda-library.png | Bin 0 -> 922 bytes .../Objects/Devices/pda.rsi/pda-m.png | Bin 465 -> 0 bytes .../Objects/Devices/pda.rsi/pda-medical.png | Bin 0 -> 462 bytes .../Objects/Devices/pda.rsi/pda-mime.png | Bin 492 -> 505 bytes .../Objects/Devices/pda.rsi/pda-miner.png | Bin 489 -> 466 bytes .../Objects/Devices/pda.rsi/pda-q.png | Bin 461 -> 0 bytes .../Objects/Devices/pda.rsi/pda-qm.png | Bin 0 -> 478 bytes .../Objects/Devices/pda.rsi/pda-r-library.png | Bin 0 -> 350 bytes .../Objects/Devices/pda.rsi/pda-r.png | Bin 555 -> 353 bytes .../Objects/Devices/pda.rsi/pda-rd.png | Bin 472 -> 565 bytes .../Objects/Devices/pda.rsi/pda-robot.png | Bin 480 -> 0 bytes .../Devices/pda.rsi/pda-roboticist.png | Bin 0 -> 486 bytes .../Objects/Devices/pda.rsi/pda-s.png | Bin 575 -> 0 bytes .../Objects/Devices/pda.rsi/pda-science.png | Bin 0 -> 485 bytes .../Objects/Devices/pda.rsi/pda-security.png | Bin 0 -> 445 bytes .../Objects/Devices/pda.rsi/pda-syn.png | Bin 435 -> 0 bytes .../Objects/Devices/pda.rsi/pda-syndi.png | Bin 0 -> 424 bytes .../Objects/Devices/pda.rsi/pda-tox.png | Bin 463 -> 0 bytes .../Objects/Devices/pda.rsi/pda-transp.png | Bin 534 -> 0 bytes .../Objects/Devices/pda.rsi/pda-v.png | Bin 472 -> 0 bytes .../Objects/Devices/pda.rsi/pda-virology.png | Bin 0 -> 478 bytes .../Objects/Devices/pda.rsi/pda-warden.png | Bin 591 -> 491 bytes .../Textures/Objects/Devices/pda.rsi/pda.png | Bin 416 -> 428 bytes .../Objects/Devices/pda.rsi/pda_pen.png | Bin 129 -> 0 bytes .../Objects/Devices/pda.rsi/pdabox.png | Bin 403 -> 0 bytes .../Devices/pda.rsi/unlit_pda_screen.png | Bin 5834 -> 0 bytes 69 files changed, 425 insertions(+), 61 deletions(-) create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/id_overlay.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/insert_overlay.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/light_overlay.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pai_off_overlay.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pai_overlay.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-atmo.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-atmos.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-bar.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-bartender.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-c.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-captain.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-chaplain.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-chef.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-chem.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-chemistry.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-clear.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-cook.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-det.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-detective.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-e.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-engineer.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-genetics.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-h.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-holy.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-j.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-janitor.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-lawyer-old.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-libb.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-libc.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-library.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-m.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-medical.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-q.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-qm.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-r-library.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-robot.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-roboticist.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-s.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-science.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-security.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-syn.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-syndi.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-tox.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-transp.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-v.png create mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda-virology.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pda_pen.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/pdabox.png delete mode 100644 Resources/Textures/Objects/Devices/pda.rsi/unlit_pda_screen.png diff --git a/Content.Client/GameObjects/Components/PDA/PDAVisualizer.cs b/Content.Client/GameObjects/Components/PDA/PDAVisualizer.cs index 019bd03ae1..5ea8bbbbc1 100644 --- a/Content.Client/GameObjects/Components/PDA/PDAVisualizer.cs +++ b/Content.Client/GameObjects/Components/PDA/PDAVisualizer.cs @@ -1,4 +1,4 @@ -using Content.Shared.GameObjects.Components.PDA; +using Content.Shared.GameObjects.Components.PDA; using Robust.Client.GameObjects; using Robust.Client.Interfaces.GameObjects.Components; @@ -10,7 +10,7 @@ namespace Content.Client.GameObjects.Components.PDA private enum PDAVisualLayers { Base, - Unlit + Flashlight } @@ -22,13 +22,13 @@ namespace Content.Client.GameObjects.Components.PDA return; } var sprite = component.Owner.GetComponent(); - sprite.LayerSetVisible(PDAVisualLayers.Unlit, false); - if(!component.TryGetData(PDAVisuals.ScreenLit, out var isScreenLit)) + sprite.LayerSetVisible(PDAVisualLayers.Flashlight, false); + if(!component.TryGetData(PDAVisuals.FlashlightLit, out var isScreenLit)) { return; } - sprite.LayerSetState(PDAVisualLayers.Unlit, "unlit_pda_screen"); - sprite.LayerSetVisible(PDAVisualLayers.Unlit, isScreenLit); + sprite.LayerSetState(PDAVisualLayers.Flashlight, "light_overlay"); + sprite.LayerSetVisible(PDAVisualLayers.Flashlight, isScreenLit); } diff --git a/Content.Server/GameObjects/Components/PDA/PDAComponent.cs b/Content.Server/GameObjects/Components/PDA/PDAComponent.cs index 53f3543d62..91bc92bb3c 100644 --- a/Content.Server/GameObjects/Components/PDA/PDAComponent.cs +++ b/Content.Server/GameObjects/Components/PDA/PDAComponent.cs @@ -141,7 +141,7 @@ namespace Content.Server.GameObjects.Components.PDA private void UpdatePDAAppearance() { - _appearance?.SetData(PDAVisuals.ScreenLit, _lightOn); + _appearance?.SetData(PDAVisuals.FlashlightLit, _lightOn); } public async Task InteractUsing(InteractUsingEventArgs eventArgs) diff --git a/Content.Shared/GameObjects/Components/PDA/SharedPDAComponent.cs b/Content.Shared/GameObjects/Components/PDA/SharedPDAComponent.cs index 8e24cbcd95..cb2fdcd60e 100644 --- a/Content.Shared/GameObjects/Components/PDA/SharedPDAComponent.cs +++ b/Content.Shared/GameObjects/Components/PDA/SharedPDAComponent.cs @@ -1,4 +1,4 @@ -using System; +using System; using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components.UserInterface; using Robust.Shared.Serialization; @@ -108,7 +108,7 @@ namespace Content.Shared.GameObjects.Components.PDA [NetSerializable, Serializable] public enum PDAVisuals { - ScreenLit, + FlashlightLit, } [NetSerializable, Serializable] diff --git a/Resources/Prototypes/Entities/Clothing/Belt/identification_cards.yml b/Resources/Prototypes/Entities/Clothing/Belt/identification_cards.yml index 0364c3a024..8a2135e601 100644 --- a/Resources/Prototypes/Entities/Clothing/Belt/identification_cards.yml +++ b/Resources/Prototypes/Entities/Clothing/Belt/identification_cards.yml @@ -6,13 +6,13 @@ abstract: true components: - type: Sprite - sprite: Clothing/Belt/id_cards.rsi + sprite: Objects/Misc/id_cards.rsi - type: Icon - sprite: Clothing/Belt/id_cards.rsi + sprite: Objects/Misc/id_cards.rsi - type: Clothing Slots: - idcard - sprite: Clothing/Belt/id_cards.rsi + sprite: Objects/Misc/id_cards.rsi HeldPrefix: default - type: Access - type: IdCard diff --git a/Resources/Prototypes/Entities/Objects/Devices/pda.yml b/Resources/Prototypes/Entities/Objects/Devices/pda.yml index b2395af536..672db4b488 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/pda.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/pda.yml @@ -21,7 +21,6 @@ type: PDABoundUserInterface - type: LoopingSound - - type: entity name: Assistant PDA parent: BasePDA @@ -39,9 +38,9 @@ layers: - state: pda map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: Chef PDA @@ -53,16 +52,16 @@ idCard: ChefIDCard - type: Icon sprite: Objects/Devices/pda.rsi - state: pda-chef + state: pda-cook - type: Sprite sprite: Objects/Devices/pda.rsi netsync: false layers: - - state: pda-chef + - state: pda-cook map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: Clown PDA @@ -81,9 +80,9 @@ layers: - state: pda-clown map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: Slippery paralyzeTime: 4 @@ -104,9 +103,9 @@ layers: - state: pda-cargo map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: Bartender PDA @@ -118,16 +117,16 @@ idCard: BartenderIDCard - type: Icon sprite: Objects/Devices/pda.rsi - state: pda-bar + state: pda-bartender - type: Sprite sprite: Objects/Devices/pda.rsi netsync: false layers: - - state: pda-bar + - state: pda-bartender map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity @@ -140,16 +139,16 @@ idCard: JanitorIDCard - type: Icon sprite: Objects/Devices/pda.rsi - state: pda-j + state: pda-janitor - type: Sprite sprite: Objects/Devices/pda.rsi netsync: false layers: - - state: pda-j + - state: pda-janitor map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: Captain PDA @@ -161,16 +160,16 @@ idCard: CaptainIDCard - type: Icon sprite: Objects/Devices/pda.rsi - state: pda-c + state: pda-captain - type: Sprite sprite: Objects/Devices/pda.rsi netsync: false layers: - - state: pda-c + - state: pda-captain map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: HoP PDA @@ -188,9 +187,9 @@ layers: - state: pda-hop map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: CE PDA @@ -208,9 +207,9 @@ layers: - state: pda-ce map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity @@ -222,16 +221,16 @@ idCard: EngineeringIDCard - type: Icon sprite: Objects/Devices/pda.rsi - state: pda-e + state: pda-engineer - type: Sprite sprite: Objects/Devices/pda.rsi netsync: false layers: - - state: pda-e + - state: pda-engineer map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: CMO PDA @@ -249,10 +248,9 @@ layers: - state: pda-cmo map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] - + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: Medical PDA @@ -263,16 +261,16 @@ idCard: MedicalIDCard - type: Icon sprite: Objects/Devices/pda.rsi - state: pda-m + state: pda-medical - type: Sprite sprite: Objects/Devices/pda.rsi netsync: false layers: - - state: pda-m + - state: pda-medical map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: RnD PDA @@ -290,9 +288,9 @@ layers: - state: pda-rd map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: Science PDA @@ -310,9 +308,9 @@ layers: - state: pda-rd map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: HoS PDA @@ -330,9 +328,9 @@ layers: - state: pda-hos map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] - type: entity name: Security PDA @@ -343,13 +341,13 @@ idCard: SecurityIDCard - type: Icon sprite: Objects/Devices/pda.rsi - state: pda-s + state: pda-security - type: Sprite sprite: Objects/Devices/pda.rsi netsync: false layers: - - state: pda-s + - state: pda-security map: ["enum.PDAVisualLayers.Base"] - - state: unlit_pda_screen + - state: light_overlay shader: unshaded - map: ["enum.PDAVisualLayers.Unlit"] + map: ["enum.PDAVisualLayers.Flashlight"] diff --git a/Resources/Textures/Objects/Devices/pda.rsi/id_overlay.png b/Resources/Textures/Objects/Devices/pda.rsi/id_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..3f5d310e703db67bf691eb5e9233975a79874bf3 GIT binary patch literal 90 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzMNb#Ukcif|7ae&S7&sUfzK)Ly m{!!4(#lpZ~?~}~+z-8;%cZ~mSC-Z^yFnGH9xvXsQXaTVE0V|J#%x>Lu2 OTn0~9KbLh*2~7Y?(Iv0| literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/pda.rsi/light_overlay.png b/Resources/Textures/Objects/Devices/pda.rsi/light_overlay.png new file mode 100644 index 0000000000000000000000000000000000000000..286a6c32558c5afda2a6acf326230dd34fdc13a2 GIT binary patch literal 763 zcmV=f+5pjwRttMKC|^U0g2r{Lek--gBTxks?Kk z6e&_bFaV%$^9M9%odYhE{_Rr%*h|hafxZ9}?d-%r>G!QwH45Rt;teOfKKANZqu4m^ zwK`60l|r+3rLJdBgk$O%jJLn$S48F;OaUB#BSWvaR@1?{YsyPpbvB!#r6ijs3pKt3 ztsy+Wqv0hI6(<=P8Kb#pSIy7w;D79AuL&2#7>e4fD_I9Mq7p7oaWO(o|(=OeHBcJUXaE`^v5-wdzae z*^Y)xu8q73kypA$9`1fXy7qlZKHa88O_k1v!)T6i@*%_Oodo+}%=4%}9roTa+BbSu z)PO8`*MTg-U=Yi1PcxOro4Dc68ncJ3Fs9dWU=u&+<uuN|8V4XPap= t6Z}$=7AMMG;u>j|+#GLF0lTttI47+kCyqU_w* soz+>~WDU674r+kS;qgCk-~a=I1<%`c2FsmhrR(Ts6$n4=d_#(&aKtnsj#e~<_|NnQ4sJ7MOJ>+b~ z(s8Zh{PKHuo>(`uX0Y3|Ubs9>@yMn{C$@?)ve~e>9n^rD-pI$+Y%0RV|aLiUrw3UCb61F-JZc&+RB#+l8Pn8HWJ6|nBr zQu~@Ge+D0ytS3ALNjL`TQ!b3v`cmsyt&hQ)bJ=qFIj|k;{k9sA^B^Y&;Oo@s`;t14 ze(HQ2$Y~0aBJ_st48EjOQ1x(}!OKoTUiU!B9nf{1wZr?Io8>KFM!5iBCA?$AO`}=k zq6x5C&0Jo&czk{V;Ep*oP2;iJh*fQb{+?alBz^?pdmt1F0T7GKGwE>|A1^J@xVHzu zq{oHTVmwR&fk5E}Y;P{&R~2mS7G~{J07R7tQ6&PvtbGbwy9K|haIn82EI|5AI5a%* qYhJQ>LEnVgg!}~2#$}Z8Z}0)K?WT-mIe?u20000L6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUCZPGyDj^G?k|k)LFi z2BtrL{EH&a&(F-TcvskQhC~$c^o0zr zoU#%JF-8;T0FdRSttAY%9$vzbQLxE=X}WCsu(1;`? za*7`zD9O)YWyXV3&)s{(Wl2J{)&IhLaWDf^wj+lC2-JvW;HCC(fG0000P)BE{Q*zm@Cv2k6~jH!EPi zNQ-#*oRsqKOg{D$Ovhf~!eq28&dF$r^WiO*uAT#zuV-85CwLxIasXK0dNXd-fz4aj zaiG!^)k+*=dncx5b002ovPDHLkV1k0=wr2nU diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-bartender.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-bartender.png new file mode 100644 index 0000000000000000000000000000000000000000..c9348a7cf4411de7186525223beea564ac77ba17 GIT binary patch literal 432 zcmV;h0Z;ykP)PU)WId-CV>P(iNUmFQ6hKk zowm~63$1kUeh_-RJMR7a-rfH|p-^Bs@As>}DAw+_JFtUTv(7)4?!R&YA*5=xTGA|e zo`#Se6Azl0Y2C(i{!x?f_sIA26AeOXs`Y4itqo7o$Qjpajt5m)95Y^7{O1 z?&SCsZmw=2&p*NUeQ;e@%n?E$48xUsqXbYM9`D8Y@aTa3j?xT9>394o2(a7L7qGW) zNMm<{L2Uu(Yxw?Fh;K2*@jS1#fQ?pww}9RNil%9s6QDJl(im@nZQJZ>VyNwe(}Oqt z7U3(usWjrag>TRyaluZSRX(+q0ETdZ5^U$%p4GYoB`7`sSm90 zghkO{Mzf488_&(~Gaxe|w18kZgtJ}`vs8dR1G3kuavX;Zus!98XAhAy$JU=?g+lSq a0e~;aP>w&!7SuWb0000XKrmX{JY<&)AbG$r+A^(>| z^O#6=K!&#@JQNbScERaHu3Zd%94AOMjxcV2ZOi-f}MMR1%eM{lLCl#!1e2p#>iwz4@6_+#|OB4*zpw%lNBM2l9MM- z672wzT13GS=?qeg1q?4=Kz#i21w-( k-66ODu@;YlQ7~u#0Q29GN+2DAl>h($07*qoM6N<$f&s#}Gynhq diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-captain.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-captain.png new file mode 100644 index 0000000000000000000000000000000000000000..c9880c57a61d84bcb11a0e9f62a696f6d9c1ff1c GIT binary patch literal 479 zcmV<50U-W~P)ABL^@MqX%XwF3TwaEJW!2k7W6O z679kp!XQA%0smJ`1X1{d_vH%)20sf11_2%jUtEHL;piy_hKWld(ME{_z+l>XeTJ`J zzvGnW=H_I`3cQFSCL%iHda;!HF-UT?&=6PLn;@Y+GJx+J3tCe{2=FbYP&NCpfH3;_8g VhGWZSMjrqG002ovPDHLkV1kitw{8Fc literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-cargo.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-cargo.png index f43bcb24816a23ab18af3d1249dfca3443042827..8477ee86da8fd7a49fd6963aa05548dc424b36b5 100644 GIT binary patch delta 409 zcmV;K0cQT81G58=B!7BIL_t(og<}{6qhJ&e3oOjc{?j6`mQ8x-_%r%prX^K zZ;@p_UInJ6YX8?IX%n>k-FHTY4=*1waC31ou=0vA2yy;rh+TP);lP251npoUXgUJ} z*s!l(zvGnW=H_Hr9IgcAL6Sq3bPnm5fQY5sOp#~o5?qS6pVsVFp>cS0|NllnG$^47An&)00000NkvXXu0mjf Dp7O6> delta 390 zcmV;10eSwj1EB+uB!6g0L_t(|oMT`Z1*2eq0VA;rqmB9hlVb4Fvmc2vn}t}*owNHH zoZc@b%JL1&z6=Zu_5X=+1QSsv(hv|80xwI zf#Ki3f23-mt^+c>A>Ny@^*O^2aXS<_W@Z)!rpLQ!>wpM&P1g`W_BACTKvph* zIpVjJ6NWrpa{=qKeHikj<^qb!0lJ64@87=}rfzydwq+DMpjP`4QR4$@^=`Lw?ZnUSFc{d#gn)GW2iIzM5=MbHOXMj zG1J&ahJe4D86Lj+z#uOs0B;|_*z#fm3>%nz8BQ;*XJB9;u31KMo6t125wAK@EGNkU k=nlaJh_!eWjDkS}0QLojI2@oa8vpQ8x-_%r%prX^K zZ;@p_UInJ6YX4_WWhH3&$B%ze`276L42ze(Vd$UulAs+d1WjjPU??hMVfgy>J5G6S zZcc{2u0JSZ%1Ru>7)_i5K$e%bmN49UcnL#JOP9w0)JS=KvAH_z`(L5ivbiCUx_f6=qO-gWrbsOF&N;~gZTbW$TWtlnps4d zOV9xmEUZ!ZpkRk#WUwqKo#8vHHCR131g=k`jssu-l-(Gh!Oknd0}uXZk6$4aorO?6 zJr^0Ot1l8|F2OXw^iP|Co0|ok*Kq+*L3Hz)Jj3i19&rYSe+&%8WskusdAMH?bO6ys z@v-Ay2pVQ($pdGD)aT?sBxnawxxmouDg(nQ((?G-dq0R$N2s>?UzjfrW)NGp!z_kY uL3!OHf;PcCTN8#OADVYiSLRsRc?Qf;iz4Y)<8Vl(BrB1d>`Lo%tt*18X|tT-UZ2((^tur!{$_xHJ!B%q+1(? zRRvoQOmzV0o`1SG9%~1BPi>_GrlvqML1x$c__0pGErr4$TEw}MNT6zSmaX(w@Mr_P<)&8egKBV3)jw0G$u6dZ~WLBz&J z$x%NH-~$U=i|MIh=|~FQ7DZ6B3ZFrS747}v&lb~-oMr$pU;nz z$)wbaZQH@VR%@YDGSTUDU|E*b2de~{_Jvr?S-En#G<01@wc5g908}ahs;Y9XMx0|b z8o9W+yT;r5>(aN=lQZ1kKESrWQ7k^9Ucd4(0N9j4G@H%M10bHB8^QkQ_|W_9b^GXb z8#7h{n#R2`WB~gI%Qr8cnWjLwY=#PeJ%*njPOuiwe1y0R<($}R2B-uCO#q=N%Ju?K z5(()Um4L`*vvZ@lj?hXtX}rl=#4iD5z;)g220-J6Y1%bC9i-oCcW11AGr-jWmJ*T> zkru2Xl~U1e4?Qu$Ob=u-8Pb8ZK3F~AIB`#CR?+Vd=f3zoAQK^W053w|+zdD0+#Zm< wR%633ybZUezT@dbB=xcL=QtuF;-3TH2MN8CkSi3*B>(^b07*qoM6N<$fTN`FA=+l72U9UKH7!KL7kwNtlNaB%A<_ye}x975LzRk~D&;M7p}fU9C~ z%^i~e^#Krs0 z;YzMtuAuaEo2`K1#0}%>>wPT$q1aD(3Odbt=t8?T4cB&Uij(S0wOu*~uHKGU)-R}e zkjMdGx$9`0s{_$pr*Ri&3+#Qfo;_~`AFz4J3t zoo(CXo=RiL7=zKcHzEOw$AaxbE^mnx+ZP<7Z+W$e&;^xPa$* zFbpFkiV||p!5D)O0;5r%EI|2A*lL~rS{IO0?}TIl--Y}HDsh1V{|28oe1p4`R5pwN O000021H<=)3R?|Z)Y%RQF^CQSS>21FCR-tn>w@x{fh5VuXV zcq9_T@Am^>-*mOiD{d43P*oL3ltn~XLOs4^OyK6)C8Sj~Z)9htsGrHV5 zA$y6g>&(qe_C^3esZ^5v<#z>y7n}ejk2H!Svkft;)rRH%q_+Z+M_P4ne>!W5zf1B- zcR_f;S#=>abX;9SL&q%Iv$AyEIk5ja->_C7=RrpfK<(7^`@T9*f9h@==%@<}5&EdF zDc;vE7<|k$#bvvot9PL94(Phh&cQ>+#kv*{TU!O-Wox=+e0cpFfLtyo%qi7|ilUIu z=kYke;{a`GkL#&;^*fWvpeTwArTjbKcDvafivyr(wF|jB7RTpnz7sz|XlydZv0yMt zAQ0eY#f8TKSI?EWv7gv3psFgVR00qi%cN(*U@-b^o=kBm0bhl*gJ@wBCjJdR0FKC! UB@#n?QUCw|07*qoM6N<$g3(ve{r~^~ diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-chemistry.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-chemistry.png new file mode 100644 index 0000000000000000000000000000000000000000..e8a4c7cec5d722fccd26701129298a5fb81f0385 GIT binary patch literal 479 zcmV<50U-W~P)L6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUBuO7jO&CSWMXwhc4 zdXOWeq@)NLO`HQj0AzV-YYD@xhnFzq)a3OTx~qE_HgDX>ATnVBgXRAHa5+%a;fNYI zAUY8qJAHuR&8v4{3>9KvW2T0G}W)K{1C14~VuH28a%UPwzj& zF}fHG9Cvgi*#T4y0az4(5-qWS=n!CIWrbsOF&KbF0Vqd=2B#2ZF3}+X1IUR`L`(?I ze*f+rI0`_yfF>aT%5DtMVCNO!fd~Jy$FJaOU{OHz5C9c4puCPxo&hNeNC^R=902kW zsGw2WoeLMc^_hX;vqK|Z_1U?(SmjA_0H|2Cu(V|Gee{Td<08U9V=)GXABl-L^*wv` zjG*NpK&ZC*UtL)VW`N3eL6Nu$Trq23QEH*!}7lhRB!V1xq(1_l6iimMVGnR>SX0000Q8x-_%r%prX^K zZ;@p_UInJ6YX47l-y~=`!&WrDHv@xXg8{>V0~ZO}!9v({h+$v9e#a@#&CSWMzu^Lk z*op%?86+4a2%1fl13;FSww5s5dUy##PEB5qp}V?=;ibb%hJS;~)(l~D@4)5k80;7h zF&v`M0S=4|45n%f$4(!B3yFvcf$2A|-Z8v+g%JOF|2{GPK>>tw!2w9_;}hhCV;Df^ z$L{+}0|z)j($J^(pWzq=(D|#9RY-OK;V1xwK&d*4PhkLE3>F2TM9bg+(#ucKc%nmq zjg=LSVE~;Ei+=)8jsWEXVgS);00tll4}#GFEDAs&5aHlXB?mx50F>Prpux^7zylBd zXOCaOmBONcln@{)4RALYfb%*o04j(WkfMN;G(dDw3@T^{0r3Oka1|i+(2|hyq8Jnn z;s;2pjlpT)0AY26YODVa4*FmQv2hAZe9(%BpdCb2$0S7AOuqS}U=)mkkqj6Z7yxQo Vh?!)QTNeNT002ovPDHLkV1jJdwD14` delta 500 zcmVOT$1Ief=Sn3bx=NErlYwI2YF-4qbx&11@Ik zpp%2pDN{Ca5TRRKoE0TY1&2bx#o5JDL}}LFt!#Ki<19 z-z5hu@ZS*$a`x2h$a$SoTY>S@;d1psu0E4b-zGi&%kXcrJ- z0)0@N_#eZs2W$imAq%7fO0r58Ue6RMFz$zHGTq!E!(Fd0H~oBTAxdR qTSO2<$b|&Bh4_J&c!34}488$Pl*}5$qfFWW0000Q8x-_%r%prX^K zZ;@p_UInJ6YX2uqTF4+REe+;ZZ@+^g-|DG?q37w-r(k-^rcDg3?d=5ZU?Jo}kmYf4 zz6@W#e#ejto&%w|xj7jYE!qrM4|0T*loTPOiE{u5fGjU+!Z7~cGbU@Um$6)%?`_FJO5iubQv4(4G4B5Hc8EmHLfcZDCu`wu6!vQcr zOb9@tK!<^4O@9`HHYn{7VJ^{WfQ^+Ej?u+nz-CH3nDr-Q8pBo1ETYUM=zs|p)+l^f zLPSoCAh0Ybo#9B%6|j162wa~=9S6VwD7!I0gPm7^2Oj*-9=}3T1<3_HJr^0Ot1l8| zF2OV)CiaMdo7)$h*Kq+*L3Hw1Bg3Odi3|)3HQ<5^0(USAFOnSq0-#b3RM4z`$%i8D z!ukNO`t00X${heIRxKVI`*C71y!+tEWH zRQwZ8i=h<}K|6@5j)_o1iTR^o6pVtA3>X*~09#L)v2pnIVE_OC07*qoM6N<$f=!m% A3;+NC delta 424 zcmV;Z0ayN*1i}N5B!7uXL_t(|oMT`Z1*2eq0VA;rO-u_=@<%s=7M-A>mXe0_wU~fBHHYd=%`RyMf{L%hwF`iH&eEWD7Ml zH5p7yOc)yH*E3w&a+GKXklZHJ^RdRO4rV#AZ9<|QfFbS>T!2`MN5LoL6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUBuO+q%{Jei3%pxVqhzbD^;1lE}DCY3s0nrx20MQ}v>HTLoMi+yDZx}XV0DyrH)W-^}o8Z63igBY=>D4t%wNPK~!~2l+EOuKMF>{C>Y6r bfq?-4=BSCvltDRe00000NkvXXu0mjf{28}P literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-det.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-det.png deleted file mode 100644 index e93ec473b476cf650016c51773a1763ce004218b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 434 zcmV;j0ZsmiP)C?A}GMj~1%QG`87=E5CAj>ASf#1J>Gfds|glx+wc7Q3%W+GLZ5p5<(*-)65kwMC3B7-n5Bb?2sT>DOPt7I-`vz*vAA!!lDK)7*Ati_{X c6bu>w0K*Y~Tm@W*L6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUC_j9w75{O*fC3~xXG zWf10N2J@9ASQt*-{>dxuE=wdJcivm!NFyX&Vl(|HQ01SW<9t^+|BMg9|0F(=85(1#? z#sCd=UI89>@IQO}3a$ne1q`$Z0Z>5$%Io;#8IYoYln@}w0U#fN3L0VoOnuLRZ`5%> z#>`I)Gt&8pb^r)~>IPx}q1x(yq7yrqL2TI$vlvcS a0|Nl?+JBk5H!4^F00001iHj%*K0ucYE-r#Y6+{$j z+rdGEjv_vau1cvyQ-{*{h}C;rTOIN*_ax`P=lu64CtPsg;E&-FC3dzq=VS^W$5TSJ zg-C_wi%SXs=1e^RXW=jaeq9GZnY#mEe9ROUp@;|om|xhiz4Pn3)%+)JG1Le%f!N6- z0NHEf%e?3034oS1@tRTs4wGH^ptT4g35H=*P5^*unzCDdSHNg*0{|<#X|nZ!4>`BH z2IXqMvI16i(^l-cJF?|pB?$^4VDWL@1C_rynFO{;kE`my`%{L6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUCZPGyDj|5b>i$TM}k z1k)cs{zVbz=VxYEyz~u2|HPLB?O-9~LJ%k_V`2FE^*aLt!xL1AYzW28&B@T$^#?^t zS&4%fqlt3>$nw(G5{6q3FJZ{3$?Gw6SNAX+JN}bFbP@}L>T-m|jt+bb`wx7g+yNkP z?DPQ!5iub!`^~F&aPc><4#3$z@84%&V9+GiNH8Ee7w`%4qVTmebio)I=$s(Ua*`bI z>HTLE{~DVkV&KUBiK5=hk_XNPsn5xONYDL6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUBuO(}oLrVI=W!SkSwLt=Atb22Pi zv>C1*K=y88#gj!=dv+8dgKY014SK< zsDT5b6XCJb2N>SGdI!cZ0Ahp05#TJC1&cCb08t?T0(^qJ1jQabsv+897$7dHzm15~!7hX9O?&0=UpM9>bRs$(M5P-6Zl7zLwXBm)Kp1^`+-n2b;I R2%7)^002ovPDHLkV1h#bx�K literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-h.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-h.png deleted file mode 100644 index 39b498c96d4117c6d3f75c8a48e5dd7c1a5bccdf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 452 zcmV;#0XzPQP)q6z`(HPOc{!}#1AQk znGCc46XOUbVjMtAKvW38z|E^yQ26hm0rKd{GZb+N5h>a@!2Z)Q6#mQw@fd1;|NhOu zAVR#EBsqYAfq~)Qzkj4^p{@fmyd@bJ7#J94Y<safV40mD+CxA5WYrc z(=`N;eN9OSkd+Hyj`%I*gdtDYT)_HlABH@sxqzZ_fbJpi`}c2#shggVZ5hQ55HaMY z*g}dNASx)x!26b;;j{2Z1_lNO1~oPfhUd?p!})NzAH$n=$_TfAn`+rK~6>rZqe26*HPGVGE$J%(2ChaJAmXiVR}R= uUUe|biER@S?ErL#-~z;2JPJm^paB3$>X9eV0FmAR0000T^z{O1(67I-p%Ao?Sj*n`%IqN1y28g(j73y;O6c*cQI!HZ^5H;!9$SRP-~6Z zYz?h-98V_6a}*aQ008>^_V>U61VQp%N+08Kn@VRll11Z~5DG$cptS~LEIw@%GsYmL z4=Hz-l+8p$o(YA>UO;J3ijxE!3i%1Pg)3C}H~0b#%97B2r`=Ql0000Q8x-_%r%prX^K zZ;@p_UInJ6YX6;+0|;9F`u%%`?_a+&aB_2k`J#e?3@7%VW;n2NKS4WK2%65oz;I&4 zPKK{vzvGnW=H_Iuh;T;{lTwu?#%SUk0J6NawS?i;!%G-)YJc*24Bgc|43C~XWAI;o zoZ)=ujFF|;BgXa`Z%F;O;?Z~iD41*2dj0|o{L0CP&1uM?P&BLDyZ07*qoM6N<$ Ef?pNMp#T5? delta 436 zcmV;l0Zabq1I`1GB!87jL_t(|ob6UIOF~f?J*kNpB9f6l#L8cMu0NK~MYE$>bGzbhb}3(P>E;t@e`zs)mXGZLKJU8XvCHbVgAe{^wE~LB#V@!DqSoD-+>8v?$P+t(n z7UVqW$pN5q>VNKdpbm7O+KK}`bwOJMSLRLmfp)=gwP?!Ac7dgPVBijL&arz^>A9F? z0m11J%LOe77z+dtbH{P}b`1cCx#M7r;dS&G0N|_nTd_vxl)eYf?uzi$`~U#PIzObZ z=7-uS%hJ&WZ0&6$q$!9-6r+&ZC<}2kqO@K^M>(Q8x-_%r%prX^K zZ;@p_UInJ6YX7J6$`G{tEFN09jtzTEcMa;Ux??HGg?MhVJSfhAZD+F-*?A z!Z35C6I>1ybvUAiGzWmdvC{|OY!NXbF#YD$JBBx}5aJAHi8YN32k;5<5)_+x#|fKO z${bJ-)Xc!JFp=TY`_FJOSO_4CmD=Vq$Qh(FaQ;3D=HI+}g+bv=FXaw^0b)V`90hs| z3@mH17_>oYhkpoji7trPSXtp1T?_^|^&r0g6EcnAs%92Z<`Q(k1Pf~vJ|y8mFfv#c zl+N&-)f%iG90J#;QO5x=0LpF*&|v2k;DHDKv&XLxilDimr{^L=b@fG}%q6NMBv=q} zb222n`OBaq1}Tg{>KUNr$LZ6zC@%?nn1sOv4Nma=FIJ}dSFacnuHY|(h<1SSzI_Y~ zPNe1WCqI4=rH)W-^}j_$1D4t%wNPK~!~2l+EOuKMF>{C>Y6rfq?-47IKde TNA%8300000NkvXXu0mjf1lQSn delta 536 zcmV+z0_Xkr1F-~ z<%xOIp55-iz<B2Ou^L#a0MNK~v)-A-aQJ*U{2IeT9uLB~Jm5kj;g>$`av_?@;JjFb!{*j-LZaDj1-CMi_oXiY a@Cji?#Sy>~QW>5A0000Q8x-_%TvprX^K zZjog^UIn0F=Q0;&U=?Qu^Y?iCM3Hyd!H%Kl_shRv`rWM$43~FaCuj!?As2!y|Ficq z!`H9h8B8~!%6){=+}xZDEY7@e^&m&^OYsphnm7l50Lb#vmVXk4+Yc{e$f?TfGIZDU zFtFd{U|41<&5-o&69PnSMq;Nc}M zG>8rXHdaKB0az4BN6Ry?YYS4>0Wbhbco2*ZU{L@HfuC33Q`Z5Y?8X2Mc3uG< zc>ASf#1J>Gfds| zglx+wcEAVm`+sou7v1A1ay*wUk$8mb3>Y9O8?uTsGc4AWWMCC%W;knP%CP=V5JTS` zV}`RvrVOm&%nVJOzZh7>nMpB{^l~8f`g4ZbPv03B7#JAtKX?ciul@9$q3$~ysm2lS zfa%ja7<_m_7+A%b;T9FCoMk9dIm^Jnz`#)VosEH2oIRPrhbM$#>C$OLJAmXiVRUpB uUUe|biER@S?ErL#-~z;2JPJm^paB4h4v50pTQ*4m0000vK4p6PNCKTqRk{J8}hRYGUUgvV&G>NgtK!!o8f#In-oBDInXrQ zl_Aclh=GBDf#KgPMC};oRK(D-F^5#+h;I=wFfcIe&brO8XkQ=0qtEvkL6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUD=IGYGs{_*E~hL6AA zgW3FSf(-oZf(-i~Z)aF{e<49TSO}WVz`(GUsh#2L*Y7yxxw$zRg4ruk#FPcpi7}cu z2Y@UuZ7pHA_3#py|LgZ}FwMrw%Ah8%$IxBf!|>?OJqD9ca~WPqx5D*-q7FyYkmdjo zIClB~3ZGAqm*LH;cMNY{A;f>)zt6xRO{|F|IsgRZ6%@eq!v_z*z6Uu18GN)^Mw;!U zW`;i&Vs+=nxP}?UEwd{7W2MyQv^_5J5p`2ZuT;xH`M3*eOevDxEr1 zv|usRDz!1R2z7{;YNfe5%w~RY5Z?QE@8i8o0{Hm&2(Xe)_q?t->UBX;MmTjnMUj~z zU9SlDU>Z;qxu+&Fu9|N518u8`PN$7vFa!VyNm10E$~eC~aqTn4GC;YxJBDdmL-T}4 zSejq^5t|4`n02`gn5KoT{Y_NvZ-3?FcoMm69?!22I6Z1%dpG6e6h$7k8ZiR^py@^D zc_tPG05psy8b&|fzPJLA0xv=aBGX}JY){(|DJk|EwqD+xGZ>>B?39EIcs>ET0;Ti_V10qenhTt|Bg9zf9c`l0B!7iTL_t(og<}{6qhJ&e3oOjc{?j6`mQ8x-_%r%prX^K zZ;@p_UInJ6YX7Zta_}1P>(?KKFW=uW{QUWaft{TPEXKnr#PIm#6^1Q)<`J}mg^&wD z;Ntzc3}3%~$0^Uv&B>rC9giZ$#m+~J(Zo3bWO-?83B#?2mwzzi)a3OTx~qE_-hOz* zuyE=phKfc5xEv_za6}Dh4gi5;rw_o{B4R>d`pv6%3~yc`#J}v`%fP^3fK@-m4&W2y zB`DT%te7OrL4fFN`04#;I7SzPfsXnMBs+krApnblV^^kt6DBc$=n!CIWrbsOF&KbF z0VwN=aXV7i0e>(6Nq7*94q#CL3V~N&_fyvapzOu~4R&4u9(eFSd;AKn1`-9Ng#b}$ z02H5~ypB(v0VxVdNdrVVKwmML;rXk3L>b5c4FQn)RR<B!7EJL_t(|ob8pdO2beThCjs?(l&AxaVV4yg~DCB1d6ZGhp11W zle3HZE`5YLxkwh5f{;N9DU_fWYs{k5Ax#RE=H}XfOMbYJd+y~wf6ht%10_n7Fu+;A zzbu+_zi2{J?B%?-%bREB&$8m`gB})7Ns}tjnPK?dzDF6Tf07*qoM6N<$ Eg3Y$alK=n! diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-libb.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-libb.png deleted file mode 100644 index 579c665884e467c5542d7f9a83fda1d4981363a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 456 zcmV;(0XP1MP)!T>nHoH4w)!0R3ZD+dva^Z>M(y(k&o_Aj>k|as!mYwrx;Kc|T24K4yI# zq$^;WrZ5hIfcL{NL>$MMOeXwN!sh6TOAW9(sKy^3?s0l{F2u@$lu{5vU>F8oo}ZKr yDBatDpL=4}L=`5kdM8viP&HDAs_?+T%CG=FtEkOimEX7k0000{O$-@27+T7KPzoU?NEeHXgM%Ory16)ri-S&`o!Y@g9Q*^?K|~h? z7wce$RtPu-DO91ukzO<*6mo@gNZ*&suY2!#d+(hM?+1=I?|ZrT`|$L6-}lnz0W=zo zMxzmOy;5<W=ptm!Fx%p3sEw^u70k>ib7PAL&eL(0CdAHHFJrF-@$K-kE=wauY3>aKV}$-3sQkZMuooA zCa8L?k0O`P_JqH81N62O$eW;_0Z}k)ouB6TCC5(K22iM5Hqx0CnvE838U6jBkQ|`G z01C{}-nQd7d`ZguAxI8TVE`4j(+_igB*$RKanNy`lOG{Ae*>&rHb16RVHTEpc~#-o<0gZDf{nC z_j!4u&>!i5+D;YIrMZ(Yb=TeD#z$O!au0{E-VU_W5ANXa#s}cq;=nvI#I4V80k~$R z=mK!dWiwFQcb#Q@jYgxODZDuY3;> z<>yuJ0V4g#a)WW=mz-4|0XjQ8hS_2ADG;^+GTKbjOFENMe+;THfJ*<{VBN9@YKJJ0 zj={x+5}J(`zY>Pg(=INQq>P~Gr!y&jbsVRsT`sSX=Nzm0fKT|M>jS9zx;{Wa-1+&q c^)+M9KenkZKyBE5q5uE@07*qoM6N<$g2EHC>Hq)$ diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-library.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-library.png new file mode 100644 index 0000000000000000000000000000000000000000..6e8ed2bdf2227d03a6e40c0202c249a900022174 GIT binary patch literal 922 zcmV;L17-Y)P)=|az#b}Znzu~$*X*v{B-r`M#Z*o!QNMz%UHMFjlT+GW)~q zmO<$sAB4y#bIj~pDwXow^NBy5RFEa;^lG~;_*1N zS}n+CvyR8L+ig+eaivnp)`?;2pD>@$?RG`^a^(r^?sgr2cw*udXj&H5)>ffd{Nj4t z?Cf<&CX>pG8iu~511i-O(6j|1e(&KT)SK2Rm7Gq&(t~AD7sowXgwJ0;T8_JT;W8}g z%b;l*6rVnZbY=!BAJ!e6{TKc9<|o+P*n-Ka)3CGsTYPsdeM_ltSp?Hp=8XFL>TB5i z@lzOqb!?Y;S2_6H`L$z|Xw;6}ym3c)@j>B0J`*FLB8a}|n2jyN{_O1u!@5e}atp{K(An-7_1L+ymNo(} zS{9=Yp&0?a5hxT2$_@yk553NJYje^+lo>!`WY{4z6NoTp0FAm_lBx7v*8sW@G;^C@ z2ZF2tUiIZ$k4l_{<{9974KOaSw-CvTkzWUb%mA01^-0v{k~uXXyKz`fRv)nZk7!C~qH zyin>mcEIhqyRIkFBj)`4f>I<1gVJ|=8@#XUqSW;m=z7zro1Gv>RN%9?9|omwelgk4 z=kub1FB+;J#OLdJz3zG}3HM$^>@hetr4VHk#C7=~dOhB>-$e!vld z^8?J{oFBmX0m0+*W-wqesQdsq6i>lfFZl_s(P23bPtnLf12{VDH#Uf|crtUFUk8FjhrQ~D79I9F0%1jm{mj5nqQhR}^IjYXGe1Dq!Hy@B z2{k{!ivwZi2gu1;ASG$ZDLSF%2jF%ii{c1j=_NjY82XqF2h#3C13>L=`u}x)z;k$| w)ZzR9r!P1^fb#=Ff%5|xhG7_n84Up7F97S6zpB_{V*mgE07*qoM6N<$g5nms8~^|S literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-m.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-m.png deleted file mode 100644 index 3adb51ab574fa04281d89a73a722262abb8f32c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 465 zcmV;?0WSWDP)?LLZ>ittA^TRMb#l(4^=tgcwvP z4y_WrrVXWOn%t{t4L-{~FVA_;dG5nKmkSe2@W+@^P4svc8ajou*?XnhX4U55a1@`< z2SDuVuxDOma~A+9B@k<#0&df}&&br9f5>4N2J5TK zqY(g5uh(^d`CS1a|2zPR%QCOCYaN`)WM=y5X0!qlm*rM$soZt(Z<_VAry%5?Z@Cb( zUANYt?V2r1TDRPA4jflEI@S;9c`%Rz@U`pqc&rYz?|K*q2AYDV2*c&=;>S7#Go@4) zuR8@p-2-EHz%UGs&YuTvHnf1~{vH6Q>64!P_R07FiJ`=wtARa$JN{JAHVyyr`u~z7a zg%C(7NhV{;0<_L6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUBuO)2Cp1%ce~Xt?lgu?O-9~LXhQgalQ;+zkbJ%3!Vd^xw$zR7A@KgR}XT8 zl#~=9qlt3>2!JdvZ7pHA_3#pgoSM8ILw9u#!{&_}8M1TP7#=+!Z7~cG9Ri=;e}-dpF&L1O z+fK3rs2T#0DDVU)T4DjwA;8AU3diVTFaV1JP>u)Z2#XsS+7+Mh#w1cSXmjYdb+YBdhvo{;Paz<&dPCS&YHRaGd8BFsT4 z0Frcw!C*Mo&+B!gRJ!_RYq#4-CX>P(t5%x^c=D68V>~qT%N_u+E6goh(Bz_GsJ z6WV0IpBj!{fPXB@$Yc)i_WEwx3sI@{-Up;wP5WL%1Hwt@@mP3F76{m8jH%Ey&}cM-IYL_t(|ob8rBO9L?&#(!!HrGs{pxCsspZb1hJ!BPYf1V2Cq z7ZGpihwuY*u{hOlP(jc^QP9c3Itk+7a*9EUgM)_<+H=r5JPK{nKf3gRBkz$XPo9ML zO#(v<@y{4>;D&?Hwlxq|DisQaf)%-1tx_x&t@N!8NGVCD(|=ZEV+^%g%}PIIX#jwF zy>7+J<#N`V)&@M!Ks0fL%pQo6$wu^y;kv?kQ8x-_%r%prX^K zZ;@p_UInJ6YX7U^9N_#1ixW`f6=D~F=~pkGp@@r#OEH{3eT1QD(KUj0un=+~2uxgi zlHu#u?+gqK2T&yzK`3r+PKNwoa}+5FAr4}UCe8sM%S&5J7=La(yo4dACa=fPUERa* z{O%cspU0-aLxlkng1DlFGzWmdvC|0Oi--w<={K+5F}!(&5I=lsGlN1bu_lt}06sxp zf?~0g-;!oKDcSJT`_FKUE(QbWiIf;X)ewM10VvTD3y2N@Hdan6omYSd9{kT9zk;g)MF9heAwX0b02MT# zypB(v0VxVdNdrVV0OTW3L9=IqJBqkV!A`vDvnJp#got)PK<+Gt4U^LtY|VU8#3hw+ z=ld7$eh_0ceg}Yx(es)t4Cmvt7$okVVK{tB7)74l>M;yXi=h<}K|oY>Oq5C30Ws!} if>AIEMlxVvU;qG1l8{x7cl03u0000wka@Z%GCQ1_p*1Tc0!h z5Vu2-V`gSyV0yfpwhoxF^*Nl)@$wLc{9h8yBRLmjcuT@VA(3kroKEE0#o)(r0-si_ zfT9qX|IH1Bh5*vSfUFQ;U_kg9nN8OaK=w5yAwX6xfH~r~loN(LU2_5Jvwaxyq~-#O z$^p8E!0+F`8Goj3dP25k6g$96?Gv1Ru_X{iPQPY7oc)~yGfB#ZV&YN^2k(7l5EGYT zP&LzJ_z<;(;kN5k230dn1~G9dhBYUjF^Gvvkzyq2<$%2sH^a0|XBZe57#Lo^{soX*{?%d-6PJQpWVE}E!Dx3K14sh{14D?p6oZ(!6vM;6S`15< zP9xd@B)18pqqFd;gIP{&n~-P+pgRN?AlBkhFbW0@005&JkOQu|-x&Y^002ovPDHLk FV1j!!$iDyp diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-q.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-q.png deleted file mode 100644 index d90ee0c144b7e46d0ab551c131e895a61418c32c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 461 zcmV;;0W$uHP)M(a~T*I7#JSD`aqnil2mrU-sO`Sl&bp~CO^?3sP^~o-wck_b^tQ?_wOI6S}1n_ zED9JH7#K3VA>Ny@^*O^2aXS=s%*-qdOpkX{-vO{FfLRO!94`-HsQpWl^1hBt1m=HpL!lvnv@jqm1Q-|)zD8!#H3X1-O-Trl?0^h! zNw_0^OF3c4(WtPp@Xgzh2m`}c2#shggVZ5hQ5sMUT%l)57f zW<;AwQZ^LfVQ07`o5Uc(!wzTfWbuUaVQf+W$>o6Z&8ZBBO7}A`FfcH%q@*y&*48pG zFfcG2D&5au^JyKa#u3*fgEhy(a~l}~{%&S?`04|LyqEyIeE?(2iwQ7nVD@ELIk}#J zfq}SY8Od$J@Z3hc>PWGiBnO~71Q#II;!!XP1`PlJ1XGRGGU&pf00000NkvXXu0mjf DkMF?L diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-qm.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-qm.png new file mode 100644 index 0000000000000000000000000000000000000000..4b8ac99881b48360981282fb70daf7b07f1b1b47 GIT binary patch literal 478 zcmV<40U`d0P)L6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUD!C27O?`{uV24oh)n6omYSd9{kT9zk;iQL;(##094R`@;W|w2Bat;B?O3a z0LVw6f@W#wSrqZ;a$~&e?K|-oLPR@2CUZRl!}LG~8#`_kaU}&|oO@~07*qoM6N<$f=+D4F#rGn literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-r-library.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-r-library.png new file mode 100644 index 0000000000000000000000000000000000000000..4d36793ed9779d100a191c86d5a70b7364af17f6 GIT binary patch literal 350 zcmV-k0iphhP)MA8HRf-8P*TPN$-Q; z620E5YOY<>YL6z0zPx-tqaE02)iE3m@V#7OG5bLNCWnh`7JrrU1KQ0_I+1Q5!H3UP zd@dq8pisQV^nGscukr!<-Itx`KGzPE;DrQ#TPoN#-tXIL@X{jI?>zxRRzKIAspg&cu({XWHmb>|=0KM7kM0mtotV@YG>9d#9uU;Sf> zuX>u)JK+BA$v>7oxr{Nt_mj8TLDC}SZkv8B^$tj?_3}r=Cz(H~*8BcG5fKp)5uF$S YKRT^t%lkyyS^xk507*qoM6N<$g3JSd&B!BWrL_t(|ob8r9XcR#d$A52bKW^nrAVoky3lTd(gOExP18Hn5 z1PcqX6Dv_t9B3tkfM9JAu@FR3DItxZy%4dm3FNrKA_o?t5kD?-J7aMlZgkb1#Z3_B z*X-=R$A8}c%?Aq}kH_Qjcs&0p;*xys!bSV19IZ7^mL59k7JsFAamjyL{Sw&)DG^0N%ZR%*4^Nqxfd) z8FA8Z(sXVBY&Tl9=`3k)3;p#A8>^2HlY%vLUOWI%SXCpgHwePmNz=Ijm-amUZG7?L zjH_f}1eibaihu5hx4H3ne3Dz8Q%;(yOd z)59bu%~D4I`i6(QVJFN3WUXC$d5}9N-i*d5`6;Ier5gY{XZ%Mjflp@)qtvv{)!+B3F`@q%Pp)m-)}tW z?}DhF5Y!?!O_yY?wUhYYN0B6Z38R6@%&#yOf*>Y{!rd#+Q8x-_%r%prX^K zZ;@p_UInJ6YX2uqTF4+REe+=Hk>fy-cX{*!L(kKvPr>w-O`8~6+uI4+!9vJ|Aj{+8 zd>Oue{m#H(iYhlf7(#P%b22Piv>C1*gnfM66vZF&r*KfV9VAR;CN7r%DnGK#u}YitbJx!W0Rrs#nA zH?OfVC{V)zFn@p?0_dTE0H7$)VPIL4#h?vJJ4BdEbQFL>02JpSz{bi7=7TVZ4Ffh) z;=!yxA=4PHYGx5-Ea1L?@3mGCX>e$iTo*11`8A z;5+3G00B@b2P$a3p2??hS`4j-2--nZbxedBO3WVxqhJ(_ eWWd0{02Kg$5uZ{uZJZqd0000OF~f;{!#;Ra(RY52%W-zAo>ADAuU3CQ@FVV zwgrLP8{Aw1LwL2dIRq{y4|1?vC6T7WqDwdg4HDeahG$P-R@~#uSHt_@anHTy+;hMC z@y_J{4jlY3rbGiJgnE?3ZQKgw77CIO>Sfe4)XuzVJ-fI90DlMsTBh|vp#UY76(Zph zAuvgJD8bi_E7Sa`FG#&!wuiv#bJa9IJl^hG=f)c69Ba!9lOX^A-ENn<<#!1r<3RvG zuE_8*6E*qVZVy~{XOkt6D>B3OK2}EjZ^V4$DoDnIh6)K^$ruy95;nb;)O6MqIO=Se zMjbQ_#!>(n+<$u5wpD@QTU$|JtSa~*!DrV;{I*uX^n*6yr&fWbcffW9IOjMxy&wB9 z%LMjzcK`sDrLT)8dZ9nlwOUO$C#ei&Sw^$jgoLpEsSUg;Wn->s8f00fq@=$Ce!m|~ z$pZiw8~l(=$s-bpP}7NbL;#Rc(}=}lIGJOR5Jnp}eK)^uZ?%F(Hw1{aW#pc4Kb!h7 oj&eAOz-J=gLG)k;4*m_^0Sx4h5wfHkCjbBd07*qoM6N<$g3rLo4*&oF diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-robot.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-robot.png deleted file mode 100644 index 1923f8ee03b52e15ad8fd078d11219280a9536a5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 480 zcmV<60U!Q}P)LrPv|pw;cx_@hpY7ykQJ!6OyQ3X3{UO{slDPQ zrC1yJ=nb*`iVB&1ux%TecuLs55deT{wTfoG2mmn6>4j|Oi)b_&)N*Po*<`yZ z^bMTG(l~nS;(RMDre%x?-|?`Ei*5IlQd!H$J7J;k{eWapnv)336Zr|MjROSuH~0dZ WZoWN%Sgn%)0000@P)S5XUEC?MWfQc&bRWwIZ5JVeu}=`T+U}zCrKaeFGmsU!VwuoB0P9$?QjF|C!mHKt@J}E_;Lw{~}m(v(A8_v1XbtSNc~i7!pFj zvMkX#8@xi*+!(XNaLz@q*Xx1jdCEPM0w|@>YPF=ZL>$L~a=9E#NfC@O!Y0iL_0IYJd&|F$XjIDYM+73xvj}gE+Fsg*p2|!1; zjKLwLK#daAO}`kO8YQTX>?H7Cpo3BXI$C!eusMese+E7@#dz3`uZL0j)?wzqJ%=P)14=YnjtQf zQew#BsRgG)iAzI`(=LK=I1#Zs1j&-^_$2>mhx&og_jFJ1o}NzV3rqa(xD$qy&1U=l zalKw2d!Jz#0bZlg(CTXm?&95U*SnTzwZ?Mo%}uX%`8pG;Is>klWm!Df+q=1t$xP%Q znI@G=C1CnCtb{SJNQ6qj#h3H*e{0}f#?W#c$KhE7P3S2z+5c0%F2)Tk@O-P*ga@_^;+lC$ozFgy~tldA)oY8*bz1F zyd!FuYsXRLW=r6;+6tU+5G{kL5`e)|`|IHZ-rS98^`=h1|J1i7@Z$G1ALnhLkWYFh zoO{nl=KWv6LK6Up|NJmA@81CnOTabq*As9YhjR67>|*0E_XJ*j`x=V(`SC*l&gStu zzPK2~D5d1PWg(^X>f`NgZ|v4C-p1pz8yLp@a+$+i4uE!huptj~IUeorN0rmJWa)Ie zFN7e`YOz%;@}yeDwr!-8*tQMe)ly21Op{uvL?@L(DMho{oUJ7jLZFnQlS)x5m7rLh z$oDTUgwV-{>xvD-=qsfF*wA!Er|avU@ws%xeiQ!ju=q4B`4US2`~mYn;WZXEQ{eyr N002ovPDHLkV1j4nBeDPh diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-science.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-science.png new file mode 100644 index 0000000000000000000000000000000000000000..c7b103cf24575709e74a236ed74b7e3348feb7a8 GIT binary patch literal 485 zcmVL6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUBuOgnfM^GNdjA=P zf9=L)Fh&M)a@$FE08wdx0WAd3LjwUoQQ!$qw8R3ELI4!!Ai&1T3g&|_hz$dZ2XHo)mHzjD=WbaP}zL6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUDE^U4sk{Nu-u43B^Q zWRPWN2lM&)`5AURdc;t3=MF(TSO}WVz`#)Q?=8dEuitUXbMtaC^ze(Jh{)|B~IW>7bhVJSfhAZD+F-*?A!Z35C6I>1ybvUAiGzWmdvC{|OY!NXb zF#YD$JBBx}5aJAHi8YN32k;5<5)_+x#|fKOynyIz`04#;I7SzPf%NcwBs+krApna4 zP@-iJVP;_XO4wYYLx7Ey6^_xxU;q{cJxsz332*)qWiH7jAtd2JFgk!m0Vo6(fI@(p z4gh60257MJ3h=;#|JmbL2u08+ASDEdN(0>7oZ!5UPoClXUw9Odk_L!!0LVw6f(9q} z{ufjIt5*yOSMV1?L_5HE-#!KgC(`oxlOI2bQb(w^`ro3W0%j0fw!wx9ZZx1XbD(I zQGEONF9sbpb`%;LP^XmxUVHlzRP+1yZ`wJ4fq{YH-@kvPYN4(JGQ1@j7#J8BW^8@V z@I%}VMUI)7g@NhuZrVCv#@6R>Hpk0D81jEfG>_z5kl`%}4~0aoU2r;)YZrqb#|cu6 zqbvmGe{(~jA%L_nAS(nI7!bZjX45qUkbO-_2#}QvV2=1L<%A(m*IdB*Y#)X^skwln za)9n3@cZ{~hN+vLkZl>o4hT7fS@2*3Z>`LTHj|`m`1oN8 d3P!=80RZoHo~Xqfge3p~002ovPDHLkV1jQ6z5M_H diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-syndi.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-syndi.png new file mode 100644 index 0000000000000000000000000000000000000000..51fa1bd294e88c66c7d6c868b7df20f870818ec6 GIT binary patch literal 424 zcmV;Z0ayNsP)L6Nu$Trq1{jG@^WT<>o1mh@-@cM%K3)a3T-^VCxOfO!&dP{t%(}o%<+-^z89cANKoQenW5*RLq&om)d1-42!>xyx zFyz$a^%%OVdl(-4`OPr>LpwvKNF7`b6m^6gL6ieP;MnN{aJGn;5SV`R>K((IR|k+( zoh8;d(jCAj$V*VHQ)DB_4j?)ketQ2Ij?u+np!`h}$qt}u2*9EMlmn>l05(=uf?}{J z0A<}T|Nk=__)a(%kX#Z%5*`Gj16UM*LVy^{Npb)vyD>n6omYSd9{kT9zd}+4i2_gv zfXV@408u3&F$Iz4g~tpEERZNcP6LO(eZ^}B(M2(+pdkcc4uGkj{^1=#JBUhzWp7?G zbcu))qZAIV{Q60hIzqJ-ye1?zPGJ@!6~W|30n8CZ*-XCqqhJ(_f{_du7#IM5e2IH6 SyO0C`0000AP6?L2^KCU1}-T?TR0XL z9pPTWl{Q?R+MV_LDO!VHIDYT=KHmGj$M5}ku)qR;j3rS=grDD8YDfkj$tVV78T&H09 zsb=G4r(mjgVD23-41?pd$BC0oE#P2(4}ki*^YBEj%xAjUYzoJe8bei8X|-A?gv_sS z%bQlU{%f@ws;bI_lz#`p;V`c7gE>qM5FdQ@f(CUB7jUbO(K!tG@zppqQl)M zZ`wO;zqJj4*jgsN6K1mMAN^#KO9}WcWE?~jFR;MB!3QSZjvtM^aiRbK002ovPDHLk FV1hE@)QbQB diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-transp.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-transp.png deleted file mode 100644 index e140da7f0424359cef9766547892c1761e358226..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 534 zcmV+x0_pvUP)(Yz|-a;EbS&7br@=${=WgzS|W5l(GL)K3y^A`n2PV)@1IR;rbF0c0BB3z zQz~6CHV#dHIQRJ)D8z&ICv!SLsr16vrCF};&Z^FOI(zv(w+sRnb}pHp41)k#tm`@w zVdmrzp%oEYQN@H-By)18_OH1d2>2Wrl|xv-HUj|u=4$dQW8aoX0Jsp_?lYi2J*!n2 zGqA9JDPL?fhj(D$HJ1azWDim@Fk}83I%42MeZ5+hF$2aGgrYG*(HNvPQ{VU}Cp3ik zs~8O!SCBnONfs=O$k6~joxOmhz3F44qW>YUsT_F)_8F+L>?)mbN*dA2U(`^C=`v!LB4DxT(M$>@)xcqI{@ea zR+I9X59je8`O!~x7{)~-Di^xB{k^Do6jgcpCtbZ~2RGKNN?U6HEQMqWi@(5w2M->8 Y0$SXrcYKGE!2kdN07*qoM6N<$g2zqa^Z)<= diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda-v.png b/Resources/Textures/Objects/Devices/pda.rsi/pda-v.png deleted file mode 100644 index b5c169dde3b8487f355ab34a63a608ceffbbea57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 472 zcmV;}0Vn>6P)NklTCyq#L{}lSV{?`-c&@mR0Q-BN)FU2D zV%s(gb2I%B08p#dbhrGjfVIdZ0DIXo&lB@4oZIce^>EQ&0eji9Gxt`m+W1$^eA->G z7MXNhi22T)G3GmG*_+c%*P8GU>IN`w&HzT5y{ z7=}~y_T>g41X4;enUt~s?LT2On)q>Dy}0V1P+7oNA>To@@DM}%8@vM-JDDZtW2@2t O0000L6Nu$Trq1{jG@^WW4|ji92_r*DyEK3)Z;rfUBuO(}pK{@%G8;BqiFH#aB4qD7nG z>Oqc>l9D21G;t090g&aTttAY%9$vzbQ?|9TJZv+L*Uc<&v1+` z1_N@hYI63@U?_3{UcgV=Fii8%E= zd-jZ=L6Nu$Trq1{jG@^WW4|ji92_ zr*DyEK3)Z;rfUDE^U5Fuy7ocnGA9&uA3uI%c>MDxgDg8cn9tA8&#>dsBZitgcL>_S zLdb<6Q1b6B!`H9h85lBQA@Kx7o|~7Gp@&}#MNEc+gBYWUbAJHH^3v84hFcFWVaTb; z>oIg!_b^=f{)%C8?iGfaD-odsiaH!oLz)9X;MnN{aJGn;5SV`R>K((IR|s*2v&5Q4 zh6DHnc?pWmyyJvTD_%f!HvIJdGaRFf!9aTWK9U_k)ewM10VvTjh%hrSd?joy(ILRb z$_mHmVlV)Uf`1++VTOb^e~B`eVa7dt%wK$qN-z}Ou`O`F@F?{f>AJ%0RsaA0J9I1eX%VDX8-^I07*qo IM6N<$f+2Rzpa1{> delta 567 zcmV-70?7UA1J4AIBYy%GNklr+1l|WsN;9<_eyw^J~_1??1 zSvVk5otL|LK>{>;5$*c}&&*uDPLp(8{4MaA8ou2#D7K17x@${Elj#D6hy^s z3!=iK(cP%&;yJKs&HLIvh~`004#4SGd;5KL!28v49O&s3ND=nbTp-@pQ;_`nDiDu) z3S!*@eRsgNZPs6Y>A6^J0UyN+4#pg9Ul!_}0kRrU}zmxa!Bp+Y^fZw3|#x~H~QnUa7002ovPDHLk FV1i*=918#d diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda.png b/Resources/Textures/Objects/Devices/pda.rsi/pda.png index 0f0abfbe1e1eb7dff368e26d477a7e8716b33c99..8473ddaab11bf28a875c211c9427c275545e9c73 100644 GIT binary patch delta 402 zcmV;D0d4-E1FQp(B!6>BL_t(og<}{6qhJ&e3oOjc{?j6`mQ8x-_%r%prX^K zZ;@p_UInJ6YX4J`{RvwB=FK~X&!4|DaImv6u(B~Th=>R=EMKvU;lP251npoUXgUJ} z*s!l(zvGnW=H_IGjBrB{v$oQKIRGyp$^jtDOIu4AZaut&A%CYPugB0`-NW$c(K80U zl_wZJlm@}&Kv9PyYT$q<2Y|q_(+A*e5iub!{pQs>hBvPe;y>@-C)PO99l$5ZOHl0l zv|^GSKy)_z^!_s(ql>|SK=(S59YEC(fJFf)(Gm-Y4goe+Ryal%g8^6+fN}&V7Z3x8 z4gnYdB|I2_C4WX307U^P7tkaGK-rA}8tl9RJn-Ou_V^WC4J-<%9s;0(29($F$ul5D z0VyFslmkFM0u?mG0+{;Nwx!f@Kw3sO!>k#lL^}Wkjvl*0lsZDS)qe*EeK3RAIE7h^ wR1e^_gQ)75D4WSQe-w;@Q81DL0|Ns9L(hLRNNx$X01E&B07*qoM6N<$g1T^~^#A|> delta 389 zcmV;00eb$d1E2$tB!6c~L_t(|oMT`Z1*2eq0VA;rO-ED5Ul{rfj5Mv~?L z1_lO(fB*iGs)f1^$nchAU|?Win6dRa!w+#g6gg&Q76zuryMJlxfEin#!`U1!F>}RV z63rty7i4%#!b2gEYZshOky2n_=pvCuCbju>-uEq={1Z{Ri=8 zl9UafKYPu<%6_KF@ch|pIQz|80XQGVCIygO4qUppje(V272=@hNC}c%mErZP^Q0O_ zyaT3B?_dxRGGGwk=Yd=F`qg<9wg5j5gMg3$!_uYGh;{(UZNlj2EWGMqmJ{11B-#P! j4#5S8wRjYaf00000NkvXXu0mjf^aQV) diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pda_pen.png b/Resources/Textures/Objects/Devices/pda.rsi/pda_pen.png deleted file mode 100644 index 324a706220ccfe65071b5fe9076e3f6eae715518..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz4^J1zkcv5P&pGlkDDbd29&in2 zof`PxNo%o31N()RH+%G_=kgv{@1k;6Cw%uurDZA#N>!N$^q4pl8X8tJzKfGyu*&>B cBSX5+*)?n{wH?%(fo3vzy85}Sb4q9e0REOJJpcdz diff --git a/Resources/Textures/Objects/Devices/pda.rsi/pdabox.png b/Resources/Textures/Objects/Devices/pda.rsi/pdabox.png deleted file mode 100644 index 4f5012c89655abedf6834b5c145fba97306ee123..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 403 zcmV;E0c`$>P)?SD*0a*#CA+hoE7ze^&9E^Sfzk;)qap(eJA<;lgTG4?;f@L8w zrEkQk27^5M2jkMS_1*XG@4U9jg@lBJgnwp3RxFnCvpCJWPtVf+09&%~bb2RJu;Yvg zdiGa=Vktj6JURftwrsN5J!zAE{}nx_<4s*Uokt4A0w1GMbOGPqwro^YVK{u3Hc?dt z+p^KLOyHYK1?C$!>KB0;U0$7|Y^$NdR|@zvn@wrmFpSW_p#%V0ZF6C*Qmz3Yg!o&5 zR@-F%So?k!g9+K|Tkhs0qOuwFptw1W73>rE6NtfNErFp0QmGV`a&6%sczOGb{rXo5 zxUPo~f@-z8bdO(iU2pvYdQPX_I73yHrF&X!6W8_7bNY{U#~H^L@X73I(z_$i6{w5ELSKf%1_J8NmVGR zEJ#&It;kGcV5qqDHav2Ysyfe~=OT7YlLVR`ceo1fYwUl&Gbsbzi&%CE&6LGx9fkav&hz z?{1$*f386xgX+#{p(cmtKf1f_+Qq1EHY=Y@O_$4aSZmiK6|e35H(mKeukH?N^ZMu+a>Ru zlcEewm0kbD;!|F4I2JxdWX@WHHWra5UW-CBr8f(TK4fc>nYLL(E}_2+9pvXs=F%c>XyuK|In9^!ji0X21UDn}n^s%YEH;t+Puhzoxsluh82~1rtB)T%l34iuL+! zE*+Q9>fK83wKglWJ8>tKTU@#^@sEd~kGtyc(*KwKKAmAuaP4fxI@g52DZG!P-ds%9 z?YPYKHGJVKhTV-AlVE5@DoOR>YZnSQ?73pC^lAV8 zrtN#TR;MWE{w=bq-|OE${lYuC|O8i@JwW#gaD* zMQzV%y|cByzPw*v&OvM{lkVlpq!%((t8DTrwKgn_4Lq7_@~66ymGw)xZIO9lVCCi} zH9d`gKjsv^eo)vyFU)?{)O}Cf|82fFzxUteUY!EYcQ4~Vb~Bm@xcn<)U|?*?baoE# zbasXnR}2gq6Kf}0dmIiBY5TuS%=H!X8-+&`7FtCLh!$Sa5?Sc9LNuz>VCPHjStcr) zdVNjV2M-=kx_a<7c#NT zZ`9uVbp4ganf{lub#<#)zT`5CU)S}$$*fo2a8TXZEH#`fi1Vk-hdQ_WQ&`#Z8sC}b z=bbH{W5=xNdUHSC3|Nb>C3(BM0BIoj z>AbrhNO2Z;L>4nJ=qZCRW5rT?V6+Knd%8G=L~t@H2;6@URy<0KhQMeDjE2BKh5#ce zmWM>g|0ksW_wU~f8&@qO+FTYw2BORV`}YrBh$2484#@D91Y0;`>vM)5;&v#OGc&U= zFg@OlB1VWqvI9UC!vM$2LogZP$DFh2LAs2OHv{vD+EA}f%zJlJtRYb$bK*&HMn8=M(NQI7!85Z5Eu=CK@|e4vwal~&617= OIm*-3&t;ucLK6U6_+|_M From 7771f584606b040bfd6134e7e7f832f578a5ac48 Mon Sep 17 00:00:00 2001 From: Julian Giebel Date: Wed, 19 Aug 2020 20:43:56 +0200 Subject: [PATCH 14/53] Improve on disposal routing code (#1795) Co-authored-by: Julian Giebel --- .../Disposal/DisposalRouterBoundUserInterface.cs | 6 ++---- .../Components/Disposal/DisposalRouterWindow.cs | 11 +++-------- .../Disposal/DisposalTaggerBoundUserInterface.cs | 6 ++---- .../Components/Disposal/DisposalTaggerWindow.cs | 11 +++-------- .../Components/Disposal/DisposalRouterComponent.cs | 6 +++--- .../Components/Disposal/DisposalTaggerComponent.cs | 6 +++--- .../Disposal/SharedDisposalRouterComponent.cs | 3 +++ .../Disposal/SharedDisposalTaggerComponent.cs | 5 ++++- 8 files changed, 23 insertions(+), 31 deletions(-) diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs b/Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs index 00f30c02ff..1bb74c1bee 100644 --- a/Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/Disposal/DisposalRouterBoundUserInterface.cs @@ -23,15 +23,13 @@ namespace Content.Client.GameObjects.Components.Disposal { base.Open(); - _window = new DisposalRouterWindow - { - Title = Loc.GetString("Disposal Router"), - }; + _window = new DisposalRouterWindow(); _window.OpenCentered(); _window.OnClose += Close; _window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text); + _window.TagInput.OnTextEntered += args => ButtonPressed(UiAction.Ok, args.Text); } diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs b/Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs index f9f850ae18..98e345f124 100644 --- a/Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs +++ b/Content.Client/GameObjects/Components/Disposal/DisposalRouterWindow.cs @@ -1,7 +1,4 @@ -using System.Runtime.CompilerServices; -using System.Text.RegularExpressions; -using Content.Shared.GameObjects.Components.Disposal; -using Robust.Client.Graphics.Drawing; +using Content.Shared.GameObjects.Components.Disposal; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; @@ -21,11 +18,9 @@ namespace Content.Client.GameObjects.Components.Disposal protected override Vector2? CustomSize => (400, 80); - private Regex _tagRegex; - public DisposalRouterWindow() { - _tagRegex = new Regex("^[a-zA-Z0-9, ]*$", RegexOptions.Compiled); + Title = Loc.GetString("Disposal Router"); Contents.AddChild(new VBoxContainer { @@ -38,7 +33,7 @@ namespace Content.Client.GameObjects.Components.Disposal Children = { (TagInput = new LineEdit {SizeFlagsHorizontal = SizeFlags.Expand, CustomMinimumSize = (320, 0), - ToolTip = Loc.GetString("A comma separated list of tags"), IsValid = tags => _tagRegex.IsMatch(tags)}), + ToolTip = Loc.GetString("A comma separated list of tags"), IsValid = tags => TagRegex.IsMatch(tags)}), new Control {CustomMinimumSize = (10, 0)}, (Confirm = new Button {Text = Loc.GetString("Confirm")}) } diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs index 273b8c06ec..76d8a4fd48 100644 --- a/Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs +++ b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerBoundUserInterface.cs @@ -23,15 +23,13 @@ namespace Content.Client.GameObjects.Components.Disposal { base.Open(); - _window = new DisposalTaggerWindow - { - Title = Loc.GetString("Disposal Tagger"), - }; + _window = new DisposalTaggerWindow(); _window.OpenCentered(); _window.OnClose += Close; _window.Confirm.OnPressed += _ => ButtonPressed(UiAction.Ok, _window.TagInput.Text); + _window.TagInput.OnTextEntered += args => ButtonPressed(UiAction.Ok, args.Text); } diff --git a/Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs index 7fb6dc6937..54dec5b807 100644 --- a/Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs +++ b/Content.Client/GameObjects/Components/Disposal/DisposalTaggerWindow.cs @@ -1,7 +1,4 @@ -using System.Runtime.CompilerServices; -using System.Text.RegularExpressions; -using Content.Shared.GameObjects.Components.Disposal; -using Robust.Client.Graphics.Drawing; +using Content.Shared.GameObjects.Components.Disposal; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; @@ -21,11 +18,9 @@ namespace Content.Client.GameObjects.Components.Disposal protected override Vector2? CustomSize => (400, 80); - private Regex _tagRegex; - public DisposalTaggerWindow() { - _tagRegex = new Regex("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled); + Title = Loc.GetString("Disposal Tagger"); Contents.AddChild(new VBoxContainer { @@ -38,7 +33,7 @@ namespace Content.Client.GameObjects.Components.Disposal Children = { (TagInput = new LineEdit {SizeFlagsHorizontal = SizeFlags.Expand, CustomMinimumSize = (320, 0), - IsValid = tag => _tagRegex.IsMatch(tag)}), + IsValid = tag => TagRegex.IsMatch(tag)}), new Control {CustomMinimumSize = (10, 0)}, (Confirm = new Button {Text = Loc.GetString("Confirm")}) } diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs index 1abf3a7b2d..1d3662a85c 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalRouterComponent.cs @@ -78,16 +78,16 @@ namespace Content.Server.GameObjects.Components.Disposal if (!PlayerCanUseDisposalTagger(obj.Session.AttachedEntity)) return; - if (msg.Action == UiAction.Ok) + //Check for correct message and ignore maleformed strings + if (msg.Action == UiAction.Ok && TagRegex.IsMatch(msg.Tags)) { _tags.Clear(); foreach (var tag in msg.Tags.Split(',', StringSplitOptions.RemoveEmptyEntries)) { _tags.Add(tag.Trim()); + ClickSound(); } } - - ClickSound(); } /// diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs index f949e37090..3c1ab7bf81 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalTaggerComponent.cs @@ -68,12 +68,12 @@ namespace Content.Server.GameObjects.Components.Disposal if (!PlayerCanUseDisposalTagger(obj.Session.AttachedEntity)) return; - if (msg.Action == UiAction.Ok) + //Check for correct message and ignore maleformed strings + if (msg.Action == UiAction.Ok && TagRegex.IsMatch(msg.Tag)) { _tag = msg.Tag; + ClickSound(); } - - ClickSound(); } /// diff --git a/Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs index c0323cbaa1..f580fcbfbd 100644 --- a/Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs +++ b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalRouterComponent.cs @@ -2,6 +2,7 @@ using Robust.Shared.GameObjects.Components.UserInterface; using Robust.Shared.Serialization; using System; +using System.Text.RegularExpressions; namespace Content.Shared.GameObjects.Components.Disposal { @@ -9,6 +10,8 @@ namespace Content.Shared.GameObjects.Components.Disposal { public override string Name => "DisposalRouter"; + public static readonly Regex TagRegex = new Regex("^[a-zA-Z0-9, ]*$", RegexOptions.Compiled); + [Serializable, NetSerializable] public class DisposalRouterUserInterfaceState : BoundUserInterfaceState { diff --git a/Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs index d12e9060e9..f20d6248d4 100644 --- a/Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs +++ b/Content.Shared/GameObjects/Components/Disposal/SharedDisposalTaggerComponent.cs @@ -3,6 +3,7 @@ using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Components.UserInterface; using Robust.Shared.Serialization; using System; +using System.Text.RegularExpressions; namespace Content.Shared.GameObjects.Components.Disposal { @@ -10,6 +11,8 @@ namespace Content.Shared.GameObjects.Components.Disposal { public override string Name => "DisposalTagger"; + public static readonly Regex TagRegex = new Regex("^[a-zA-Z0-9 ]*$", RegexOptions.Compiled); + [Serializable, NetSerializable] public class DisposalTaggerUserInterfaceState : BoundUserInterfaceState { @@ -33,7 +36,7 @@ namespace Content.Shared.GameObjects.Components.Disposal if (Action == UiAction.Ok) { - Tag = tag; + Tag = tag.Substring(0, Math.Min(tag.Length, 30)); } } } From b7e5aafdbcb92cb6b9a3e4f97804949c2fe8abe7 Mon Sep 17 00:00:00 2001 From: Swept Date: Wed, 19 Aug 2020 11:44:33 -0700 Subject: [PATCH 15/53] My bad (#1797) --- .../Entities/Clothing/Belt/identification_cards.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/Entities/Clothing/Belt/identification_cards.yml b/Resources/Prototypes/Entities/Clothing/Belt/identification_cards.yml index 8a2135e601..0364c3a024 100644 --- a/Resources/Prototypes/Entities/Clothing/Belt/identification_cards.yml +++ b/Resources/Prototypes/Entities/Clothing/Belt/identification_cards.yml @@ -6,13 +6,13 @@ abstract: true components: - type: Sprite - sprite: Objects/Misc/id_cards.rsi + sprite: Clothing/Belt/id_cards.rsi - type: Icon - sprite: Objects/Misc/id_cards.rsi + sprite: Clothing/Belt/id_cards.rsi - type: Clothing Slots: - idcard - sprite: Objects/Misc/id_cards.rsi + sprite: Clothing/Belt/id_cards.rsi HeldPrefix: default - type: Access - type: IdCard From f4909cdb9804a7f4a393022d81d9fd0399e44a60 Mon Sep 17 00:00:00 2001 From: nuke <47336974+nuke-makes-games@users.noreply.github.com> Date: Wed, 19 Aug 2020 18:13:22 -0400 Subject: [PATCH 16/53] Climbing system (#1750) * Initial commit * Climbing uses its own controller now * Missed a check * Get rid of hands check * Cleanup * Get rid of speciescomponent stuff * Remove unneeded check, add separate case for moving other players. * Add DoAfter * IClientDraggable added to ClimbingComponent * Added some basic integration tests. Renamed ClimbMode to Climbing. * oops * Minor fixes * ffff * Table fix * Revamped system so its more predicted, uses proper logic for de-climbing. Get hype!!! * Flag check fix * Distance check and reset numticksblocked * get rid --- .../Components/Movement/ClimbableComponent.cs | 12 + .../Components/Movement/ClimbingComponent.cs | 34 +++ .../Components/Movement/ClimbUnitTest.cs | 64 +++++ .../Components/Movement/ClimbableComponent.cs | 235 ++++++++++++++++++ .../Components/Movement/ClimbingComponent.cs | 78 ++++++ .../GameObjects/EntitySystems/ClimbSystem.cs | 18 ++ .../Movement/SharedClimbableComponent.cs | 11 + .../Movement/SharedClimbingComponent.cs | 66 +++++ Content.Shared/GameObjects/ContentNetIDs.cs | 2 +- .../EntitySystems/ActionBlockerSystem.cs | 8 - Content.Shared/Physics/ClimbController.cs | 87 +++++++ .../Entities/Constructible/Ground/table.yml | 1 + .../Entities/Mobs/Species/human.yml | 1 + 13 files changed, 608 insertions(+), 9 deletions(-) create mode 100644 Content.Client/GameObjects/Components/Movement/ClimbableComponent.cs create mode 100644 Content.Client/GameObjects/Components/Movement/ClimbingComponent.cs create mode 100644 Content.IntegrationTests/Tests/GameObjects/Components/Movement/ClimbUnitTest.cs create mode 100644 Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs create mode 100644 Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs create mode 100644 Content.Server/GameObjects/EntitySystems/ClimbSystem.cs create mode 100644 Content.Shared/GameObjects/Components/Movement/SharedClimbableComponent.cs create mode 100644 Content.Shared/GameObjects/Components/Movement/SharedClimbingComponent.cs create mode 100644 Content.Shared/Physics/ClimbController.cs diff --git a/Content.Client/GameObjects/Components/Movement/ClimbableComponent.cs b/Content.Client/GameObjects/Components/Movement/ClimbableComponent.cs new file mode 100644 index 0000000000..d637853960 --- /dev/null +++ b/Content.Client/GameObjects/Components/Movement/ClimbableComponent.cs @@ -0,0 +1,12 @@ +using Robust.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Movement; + +namespace Content.Client.GameObjects.Components.Movement +{ + [RegisterComponent] + [ComponentReference(typeof(IClimbable))] + public class ClimbableComponent : SharedClimbableComponent + { + + } +} diff --git a/Content.Client/GameObjects/Components/Movement/ClimbingComponent.cs b/Content.Client/GameObjects/Components/Movement/ClimbingComponent.cs new file mode 100644 index 0000000000..07f8c7c5b6 --- /dev/null +++ b/Content.Client/GameObjects/Components/Movement/ClimbingComponent.cs @@ -0,0 +1,34 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Content.Shared.GameObjects.Components.Movement; +using Content.Client.Interfaces.GameObjects.Components.Interaction; +using Content.Shared.Physics; + +namespace Content.Client.GameObjects.Components.Movement +{ + [RegisterComponent] + public class ClimbingComponent : SharedClimbingComponent, IClientDraggable + { + public override void HandleComponentState(ComponentState curState, ComponentState nextState) + { + if (!(curState is ClimbModeComponentState climbModeState) || Body == null) + { + return; + } + + IsClimbing = climbModeState.Climbing; + } + + public override bool IsClimbing { get; set; } + + bool IClientDraggable.ClientCanDropOn(CanDropEventArgs eventArgs) + { + return eventArgs.Target.HasComponent(); + } + + bool IClientDraggable.ClientCanDrag(CanDragEventArgs eventArgs) + { + return true; + } + } +} diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/Movement/ClimbUnitTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/Movement/ClimbUnitTest.cs new file mode 100644 index 0000000000..7ae95a83af --- /dev/null +++ b/Content.IntegrationTests/Tests/GameObjects/Components/Movement/ClimbUnitTest.cs @@ -0,0 +1,64 @@ +#nullable enable + +using System.Threading.Tasks; +using NUnit.Framework; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Content.Server.GameObjects.Components.Movement; +using Content.Shared.Physics; +using Robust.Shared.GameObjects.Components; + +namespace Content.IntegrationTests.Tests.GameObjects.Components.Movement +{ + [TestFixture] + [TestOf(typeof(ClimbableComponent))] + [TestOf(typeof(ClimbingComponent))] + public class ClimbUnitTest : ContentIntegrationTest + { + [Test] + public async Task Test() + { + var server = StartServerDummyTicker(); + + IEntity human; + IEntity table; + IEntity carpet; + ClimbableComponent climbable; + ClimbingComponent climbing; + + server.Assert(() => + { + var mapManager = IoCManager.Resolve(); + mapManager.CreateNewMapEntity(MapId.Nullspace); + + var entityManager = IoCManager.Resolve(); + + // Spawn the entities + human = entityManager.SpawnEntity("HumanMob_Content", MapCoordinates.Nullspace); + table = entityManager.SpawnEntity("Table", MapCoordinates.Nullspace); + + // Test for climb components existing + // Players and tables should have these in their prototypes. + Assert.True(human.TryGetComponent(out climbing), "Human has no climbing"); + Assert.True(table.TryGetComponent(out climbable), "Table has no climbable"); + + // Now let's make the player enter a climbing transitioning state. + climbing.IsClimbing = true; + climbing.TryMoveTo(human.Transform.WorldPosition, table.Transform.WorldPosition); + human.TryGetComponent(out ICollidableComponent body); + + Assert.True(body.HasController(), "Player has no ClimbController"); + + // Force the player out of climb state. It should immediately remove the ClimbController. + climbing.IsClimbing = false; + + Assert.True(!body.HasController(), "Player wrongly has a ClimbController"); + + }); + + await server.WaitIdleAsync(); + } + } +} diff --git a/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs b/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs new file mode 100644 index 0000000000..498f4afffb --- /dev/null +++ b/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs @@ -0,0 +1,235 @@ + +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.GameObjects.Systems; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using Robust.Server.Interfaces.Player; +using Content.Server.Interfaces; +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Interfaces.GameObjects.Components; +using Content.Shared.GameObjects.Components.Movement; +using Content.Shared.Interfaces; +using Content.Server.GameObjects.Components.Body; +using Content.Server.GameObjects.EntitySystems.DoAfter; +using Robust.Shared.Maths; +using System; + +namespace Content.Server.GameObjects.Components.Movement +{ + [RegisterComponent] + [ComponentReference(typeof(IClimbable))] + public class ClimbableComponent : SharedClimbableComponent, IDragDropOn + { +#pragma warning disable 649 + [Dependency] private readonly IServerNotifyManager _notifyManager = default!; + [Dependency] private readonly IPlayerManager _playerManager = default!; +#pragma warning restore 649 + + /// + /// The range from which this entity can be climbed. + /// + [ViewVariables] + private float _range; + + /// + /// The time it takes to climb onto the entity. + /// + [ViewVariables] + private float _climbDelay; + + private ICollidableComponent _collidableComponent; + private DoAfterSystem _doAfterSystem; + + public override void Initialize() + { + base.Initialize(); + + _collidableComponent = Owner.GetComponent(); + _doAfterSystem = EntitySystem.Get(); + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(ref _range, "range", SharedInteractionSystem.InteractionRange / 1.4f); + serializer.DataField(ref _climbDelay, "delay", 0.8f); + } + + bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs) + { + if (!ActionBlockerSystem.CanInteract(eventArgs.User)) + { + _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't do that!")); + + return false; + } + + if (eventArgs.User == eventArgs.Dropped) // user is dragging themselves onto a climbable + { + if (!eventArgs.User.HasComponent()) + { + _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are incapable of climbing!")); + + return false; + } + + var bodyManager = eventArgs.User.GetComponent(); + + if (bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Leg).Count == 0 || + bodyManager.GetBodyPartsOfType(Shared.GameObjects.Components.Body.BodyPartType.Foot).Count == 0) + { + _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You are unable to climb!")); + + return false; + } + + var userPosition = eventArgs.User.Transform.MapPosition; + var climbablePosition = eventArgs.Target.Transform.MapPosition; + var interaction = EntitySystem.Get(); + bool Ignored(IEntity entity) => (entity == eventArgs.Target || entity == eventArgs.User); + + if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored)) + { + _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't reach there!")); + + return false; + } + } + else // user is dragging some other entity onto a climbable + { + if (eventArgs.Target == null || !eventArgs.Dropped.HasComponent()) + { + _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't do that!")); + + return false; + } + + var userPosition = eventArgs.User.Transform.MapPosition; + var otherUserPosition = eventArgs.Dropped.Transform.MapPosition; + var climbablePosition = eventArgs.Target.Transform.MapPosition; + var interaction = EntitySystem.Get(); + bool Ignored(IEntity entity) => (entity == eventArgs.Target || entity == eventArgs.User || entity == eventArgs.Dropped); + + if (!interaction.InRangeUnobstructed(userPosition, climbablePosition, _range, predicate: Ignored) || + !interaction.InRangeUnobstructed(userPosition, otherUserPosition, _range, predicate: Ignored)) + { + _notifyManager.PopupMessage(eventArgs.User, eventArgs.User, Loc.GetString("You can't reach there!")); + + return false; + } + } + + return true; + } + + bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs) + { + if (eventArgs.User == eventArgs.Dropped) + { + TryClimb(eventArgs.User); + } + else + { + TryMoveEntity(eventArgs.User, eventArgs.Dropped); + } + + return true; + } + + private async void TryMoveEntity(IEntity user, IEntity entityToMove) + { + var doAfterEventArgs = new DoAfterEventArgs(user, _climbDelay, default, entityToMove) + { + BreakOnTargetMove = true, + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnStun = true + }; + + var result = await _doAfterSystem.DoAfter(doAfterEventArgs); + + if (result != DoAfterStatus.Cancelled && entityToMove.TryGetComponent(out ICollidableComponent body) && body.PhysicsShapes.Count >= 1) + { + var direction = (Owner.Transform.WorldPosition - entityToMove.Transform.WorldPosition).Normalized; + var endPoint = Owner.Transform.WorldPosition; + + var climbMode = entityToMove.GetComponent(); + climbMode.IsClimbing = true; + + if (MathF.Abs(direction.X) < 0.6f) // user climbed mostly vertically so lets make it a clean straight line + { + endPoint = new Vector2(entityToMove.Transform.WorldPosition.X, endPoint.Y); + } + else if (MathF.Abs(direction.Y) < 0.6f) // user climbed mostly horizontally so lets make it a clean straight line + { + endPoint = new Vector2(endPoint.X, entityToMove.Transform.WorldPosition.Y); + } + + climbMode.TryMoveTo(entityToMove.Transform.WorldPosition, endPoint); + // we may potentially need additional logic since we're forcing a player onto a climbable + // there's also the cases where the user might collide with the person they are forcing onto the climbable that i haven't accounted for + + PopupMessageOtherClientsInRange(user, Loc.GetString("{0:them} forces {1:them} onto {2:theName}!", user, entityToMove, Owner), 15); + _notifyManager.PopupMessage(user, user, Loc.GetString("You force {0:them} onto {1:theName}!", entityToMove, Owner)); + } + } + + private async void TryClimb(IEntity user) + { + var doAfterEventArgs = new DoAfterEventArgs(user, _climbDelay, default, Owner) + { + BreakOnTargetMove = true, + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnStun = true + }; + + var result = await _doAfterSystem.DoAfter(doAfterEventArgs); + + if (result != DoAfterStatus.Cancelled && user.TryGetComponent(out ICollidableComponent body) && body.PhysicsShapes.Count >= 1) + { + var direction = (Owner.Transform.WorldPosition - user.Transform.WorldPosition).Normalized; + var endPoint = Owner.Transform.WorldPosition; + + var climbMode = user.GetComponent(); + climbMode.IsClimbing = true; + + if (MathF.Abs(direction.X) < 0.6f) // user climbed mostly vertically so lets make it a clean straight line + { + endPoint = new Vector2(user.Transform.WorldPosition.X, endPoint.Y); + } + else if (MathF.Abs(direction.Y) < 0.6f) // user climbed mostly horizontally so lets make it a clean straight line + { + endPoint = new Vector2(endPoint.X, user.Transform.WorldPosition.Y); + } + + climbMode.TryMoveTo(user.Transform.WorldPosition, endPoint); + + PopupMessageOtherClientsInRange(user, Loc.GetString("{0:them} jumps onto {1:theName}!", user, Owner), 15); + _notifyManager.PopupMessage(user, user, Loc.GetString("You jump onto {0:theName}!", Owner)); + } + } + + private void PopupMessageOtherClientsInRange(IEntity source, string message, int maxReceiveDistance) + { + var viewers = _playerManager.GetPlayersInRange(source.Transform.GridPosition, maxReceiveDistance); + + foreach (var viewer in viewers) + { + var viewerEntity = viewer.AttachedEntity; + + if (viewerEntity == null || source == viewerEntity) + { + continue; + } + + source.PopupMessage(viewer.AttachedEntity, message); + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs b/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs new file mode 100644 index 0000000000..2ebbdb5397 --- /dev/null +++ b/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs @@ -0,0 +1,78 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Maths; +using Robust.Shared.GameObjects.Components; +using Content.Shared.Physics; +using Content.Shared.Maps; +using Robust.Shared.IoC; +using Robust.Shared.Interfaces.GameObjects; +using Content.Shared.GameObjects.Components.Movement; +using Content.Shared.GameObjects.EntitySystems; +using System.Collections.Generic; +using Robust.Shared.Physics; +using System.Diagnostics; + +namespace Content.Server.GameObjects.Components.Movement +{ + [RegisterComponent] + public class ClimbingComponent : SharedClimbingComponent, IActionBlocker + { + private bool _isClimbing = false; + private ClimbController _climbController = default; + + public override bool IsClimbing + { + get + { + return _isClimbing; + } + set + { + if (!value && Body != null) + { + Body.TryRemoveController(); + } + + _isClimbing = value; + Dirty(); + } + } + + /// + /// Make the owner climb from one point to another + /// + public void TryMoveTo(Vector2 from, Vector2 to) + { + if (Body != null) + { + _climbController = Body.EnsureController(); + _climbController.TryMoveTo(from, to); + } + } + + public void Update(float frameTime) + { + if (Body != null && IsClimbing) + { + if (_climbController != null && (_climbController.IsBlocked || !_climbController.IsActive)) + { + if (Body.TryRemoveController()) + { + _climbController = null; + } + } + + if (!IsOnClimbableThisFrame && IsClimbing && _climbController == null) + { + IsClimbing = false; + } + + IsOnClimbableThisFrame = false; + } + } + + public override ComponentState GetComponentState() + { + return new ClimbModeComponentState(_isClimbing); + } + } +} diff --git a/Content.Server/GameObjects/EntitySystems/ClimbSystem.cs b/Content.Server/GameObjects/EntitySystems/ClimbSystem.cs new file mode 100644 index 0000000000..f7d2e37372 --- /dev/null +++ b/Content.Server/GameObjects/EntitySystems/ClimbSystem.cs @@ -0,0 +1,18 @@ +using Content.Server.GameObjects.Components.Movement; +using JetBrains.Annotations; +using Robust.Shared.GameObjects.Systems; + +namespace Content.Server.GameObjects.EntitySystems +{ + [UsedImplicitly] + internal sealed class ClimbSystem : EntitySystem + { + public override void Update(float frameTime) + { + foreach (var comp in ComponentManager.EntityQuery()) + { + comp.Update(frameTime); + } + } + } +} diff --git a/Content.Shared/GameObjects/Components/Movement/SharedClimbableComponent.cs b/Content.Shared/GameObjects/Components/Movement/SharedClimbableComponent.cs new file mode 100644 index 0000000000..951d051ac1 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Movement/SharedClimbableComponent.cs @@ -0,0 +1,11 @@ +using Robust.Shared.GameObjects; + +namespace Content.Shared.GameObjects.Components.Movement +{ + public interface IClimbable { }; + + public class SharedClimbableComponent : Component, IClimbable + { + public sealed override string Name => "Climbable"; + } +} diff --git a/Content.Shared/GameObjects/Components/Movement/SharedClimbingComponent.cs b/Content.Shared/GameObjects/Components/Movement/SharedClimbingComponent.cs new file mode 100644 index 0000000000..3fdb287576 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Movement/SharedClimbingComponent.cs @@ -0,0 +1,66 @@ +using Content.Shared.GameObjects.EntitySystems; +using Content.Shared.Physics; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Physics; +using Robust.Shared.Serialization; +using System; + +namespace Content.Shared.GameObjects.Components.Movement +{ + public abstract class SharedClimbingComponent : Component, IActionBlocker, ICollideSpecial + { + public sealed override string Name => "Climbing"; + public sealed override uint? NetID => ContentNetIDs.CLIMBING; + + protected ICollidableComponent Body; + protected bool IsOnClimbableThisFrame = false; + + protected bool OwnerIsTransitioning + { + get + { + if (Body.TryGetController(out var controller)) + { + return controller.IsActive; + } + + return false; + } + } + + public abstract bool IsClimbing { get; set; } + + bool IActionBlocker.CanMove() => !OwnerIsTransitioning; + bool IActionBlocker.CanChangeDirection() => !OwnerIsTransitioning; + + bool ICollideSpecial.PreventCollide(IPhysBody collided) + { + if (((CollisionGroup)collided.CollisionLayer).HasFlag(CollisionGroup.VaultImpassable) && collided.Entity.HasComponent()) + { + IsOnClimbableThisFrame = true; + return IsClimbing; + } + + return false; + } + + public override void Initialize() + { + base.Initialize(); + + Owner.TryGetComponent(out Body); + } + + [Serializable, NetSerializable] + protected sealed class ClimbModeComponentState : ComponentState + { + public ClimbModeComponentState(bool climbing) : base(ContentNetIDs.CLIMBING) + { + Climbing = climbing; + } + + public bool Climbing { get; } + } + } +} diff --git a/Content.Shared/GameObjects/ContentNetIDs.cs b/Content.Shared/GameObjects/ContentNetIDs.cs index 337a543627..7d7bcfdc4f 100644 --- a/Content.Shared/GameObjects/ContentNetIDs.cs +++ b/Content.Shared/GameObjects/ContentNetIDs.cs @@ -54,7 +54,6 @@ public const uint STUNNABLE = 1048; public const uint HUNGER = 1049; public const uint THIRST = 1050; - public const uint FLASHABLE = 1051; public const uint BUCKLE = 1052; public const uint PROJECTILE = 1053; @@ -65,6 +64,7 @@ public const uint DO_AFTER = 1058; public const uint RADIATION_PULSE = 1059; public const uint BODY_MANAGER = 1060; + public const uint CLIMBING = 1061; // Net IDs for integration tests. public const uint PREDICTION_TEST = 10001; diff --git a/Content.Shared/GameObjects/EntitySystems/ActionBlockerSystem.cs b/Content.Shared/GameObjects/EntitySystems/ActionBlockerSystem.cs index c06963d53f..e7a32bf4fe 100644 --- a/Content.Shared/GameObjects/EntitySystems/ActionBlockerSystem.cs +++ b/Content.Shared/GameObjects/EntitySystems/ActionBlockerSystem.cs @@ -10,21 +10,13 @@ namespace Content.Shared.GameObjects.EntitySystems public interface IActionBlocker { bool CanMove() => true; - bool CanInteract() => true; - bool CanUse() => true; - bool CanThrow() => true; - bool CanSpeak() => true; - bool CanDrop() => true; - bool CanPickup() => true; - bool CanEmote() => true; - bool CanAttack() => true; bool CanEquip() => true; diff --git a/Content.Shared/Physics/ClimbController.cs b/Content.Shared/Physics/ClimbController.cs new file mode 100644 index 0000000000..8c779aa41d --- /dev/null +++ b/Content.Shared/Physics/ClimbController.cs @@ -0,0 +1,87 @@ +#nullable enable +using Robust.Shared.Maths; +using Robust.Shared.Physics; + +namespace Content.Shared.Physics +{ + /// + /// Movement controller used by the climb system. Lerps the player from A to B. + /// Also does checks to make sure the player isn't blocked. + /// + public class ClimbController : VirtualController + { + private Vector2? _movingTo = null; + private Vector2 _lastKnownPosition = default; + private int _numTicksBlocked = 0; + + /// + /// If 5 ticks have passed and our position has not changed then something is blocking us. + /// + public bool IsBlocked => _numTicksBlocked > 5 || _isMovingWrongDirection; + + /// + /// If the controller is currently moving the player somewhere, it is considered active. + /// + public bool IsActive => _movingTo.HasValue; + + private float _initialDist = default; + private bool _isMovingWrongDirection = false; + + public void TryMoveTo(Vector2 from, Vector2 to) + { + if (ControlledComponent == null) + { + return; + } + + _initialDist = (from - to).Length; + _numTicksBlocked = 0; + _lastKnownPosition = from; + _movingTo = to; + _isMovingWrongDirection = false; + } + + public override void UpdateAfterProcessing() + { + base.UpdateAfterProcessing(); + + if (ControlledComponent == null || _movingTo == null) + { + return; + } + + if ((ControlledComponent.Owner.Transform.WorldPosition - _lastKnownPosition).Length <= 0.05f) + { + _numTicksBlocked++; + } + else + { + _numTicksBlocked = 0; + } + + _lastKnownPosition = ControlledComponent.Owner.Transform.WorldPosition; + + if ((ControlledComponent.Owner.Transform.WorldPosition - _movingTo.Value).Length <= 0.05f) + { + _movingTo = null; + } + + if (_movingTo.HasValue) + { + var dist = (_lastKnownPosition - _movingTo.Value).Length; + + if (dist > _initialDist) + { + _isMovingWrongDirection = true; + } + + var diff = _movingTo.Value - ControlledComponent.Owner.Transform.WorldPosition; + LinearVelocity = diff.Normalized * 5; + } + else + { + LinearVelocity = Vector2.Zero; + } + } + } +} diff --git a/Resources/Prototypes/Entities/Constructible/Ground/table.yml b/Resources/Prototypes/Entities/Constructible/Ground/table.yml index 568a1f958d..1dd92f308e 100644 --- a/Resources/Prototypes/Entities/Constructible/Ground/table.yml +++ b/Resources/Prototypes/Entities/Constructible/Ground/table.yml @@ -22,6 +22,7 @@ - type: IconSmooth key: generic base: solid_ + - type: Climbable - type: Destructible maxHP: 50 spawnOnDestroy: SteelSheet1 diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 637f722349..e104ad5ccb 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -141,6 +141,7 @@ - type: RotationVisualizer - type: BuckleVisualizer - type: CombatMode + - type: Climbing - type: Teleportable - type: CharacterInfo - type: FootstepSound From 45380c129b31b76494df4245794cec471a8cadaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= Date: Thu, 20 Aug 2020 01:11:43 +0200 Subject: [PATCH 17/53] Fix traitors not knowing who their friends are. --- .../Mobs/Roles/SuspicionInnocentRole.cs | 2 +- .../Mobs/Roles/SuspicionTraitorRole.cs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Content.Server/Mobs/Roles/SuspicionInnocentRole.cs b/Content.Server/Mobs/Roles/SuspicionInnocentRole.cs index e8ab7a96be..570e307c79 100644 --- a/Content.Server/Mobs/Roles/SuspicionInnocentRole.cs +++ b/Content.Server/Mobs/Roles/SuspicionInnocentRole.cs @@ -24,7 +24,7 @@ namespace Content.Server.Mobs.Roles base.Greet(); var chat = IoCManager.Resolve(); - chat.DispatchServerMessage(Mind.Session, $"You're a {Name}!"); + chat.DispatchServerMessage(Mind.Session, $"You're an {Name}!"); chat.DispatchServerMessage(Mind.Session, $"Objective: {Objective}"); } } diff --git a/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs b/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs index 753f5424e1..5279fae886 100644 --- a/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs +++ b/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs @@ -1,5 +1,8 @@ +using Content.Server.GameObjects.Components.Suspicion; using Content.Server.Interfaces.Chat; using Content.Shared.Roles; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; namespace Content.Server.Mobs.Roles @@ -26,6 +29,19 @@ namespace Content.Server.Mobs.Roles var chat = IoCManager.Resolve(); chat.DispatchServerMessage(Mind.Session, $"You're a {Name}!"); chat.DispatchServerMessage(Mind.Session, $"Objective: {Objective}"); + + var traitors = ""; + + foreach (var sus in IoCManager.Resolve().EntityQuery()) + { + if (!sus.IsTraitor()) continue; + if (traitors.Length > 0) + traitors += $", {sus.Owner.Name}"; + else + traitors += sus.Owner.Name; + } + + chat.DispatchServerMessage(Mind.Session, $"The traitors are: {traitors}"); } } } From 322e38b173a222687ea26af79c1d7dc072bf868d Mon Sep 17 00:00:00 2001 From: nuke <47336974+nuke-makes-games@users.noreply.github.com> Date: Wed, 19 Aug 2020 20:21:15 -0400 Subject: [PATCH 18/53] Fix for climb jank (#1803) Player body was going to sleep, so now we spam WakeBody() at it. --- .../Components/Movement/ClimbingComponent.cs | 12 +++++------- Content.Shared/Physics/ClimbController.cs | 4 +++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs b/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs index 2ebbdb5397..591cd4c761 100644 --- a/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/ClimbingComponent.cs @@ -1,15 +1,8 @@ using Robust.Shared.GameObjects; using Robust.Shared.Maths; -using Robust.Shared.GameObjects.Components; using Content.Shared.Physics; -using Content.Shared.Maps; -using Robust.Shared.IoC; -using Robust.Shared.Interfaces.GameObjects; using Content.Shared.GameObjects.Components.Movement; using Content.Shared.GameObjects.EntitySystems; -using System.Collections.Generic; -using Robust.Shared.Physics; -using System.Diagnostics; namespace Content.Server.GameObjects.Components.Movement { @@ -61,6 +54,11 @@ namespace Content.Server.GameObjects.Components.Movement } } + if (IsClimbing) + { + Body.WakeBody(); + } + if (!IsOnClimbableThisFrame && IsClimbing && _climbController == null) { IsClimbing = false; diff --git a/Content.Shared/Physics/ClimbController.cs b/Content.Shared/Physics/ClimbController.cs index 8c779aa41d..a61be77317 100644 --- a/Content.Shared/Physics/ClimbController.cs +++ b/Content.Shared/Physics/ClimbController.cs @@ -50,6 +50,8 @@ namespace Content.Shared.Physics return; } + ControlledComponent.WakeBody(); + if ((ControlledComponent.Owner.Transform.WorldPosition - _lastKnownPosition).Length <= 0.05f) { _numTicksBlocked++; @@ -61,7 +63,7 @@ namespace Content.Shared.Physics _lastKnownPosition = ControlledComponent.Owner.Transform.WorldPosition; - if ((ControlledComponent.Owner.Transform.WorldPosition - _movingTo.Value).Length <= 0.05f) + if ((ControlledComponent.Owner.Transform.WorldPosition - _movingTo.Value).Length <= 0.1f) { _movingTo = null; } From b7de0c9bb3a917b6160fc93af25dda870d8ade5c Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 20 Aug 2020 19:21:23 +1000 Subject: [PATCH 19/53] Nerf xeno damage (#1806) Still higher than human fists but won't 2 shot you into crit. Co-authored-by: Metal Gear Sloth --- Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index f048fefc25..ada753f860 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -50,7 +50,7 @@ range: 1.5 arcwidth: 0 arc: claw - damage: 90 + damage: 10 - type: Appearance visuals: - type: DamageStateVisualizer From f61d891ebc4483cfc446c0dd9379c01716754269 Mon Sep 17 00:00:00 2001 From: py01 <60152240+collinlunn@users.noreply.github.com> Date: Thu, 20 Aug 2020 03:44:39 -0600 Subject: [PATCH 20/53] Power integration tests (#1805) * Power test draft 1 * power tests work * Apc charging test Co-authored-by: py01 --- Content.IntegrationTests/Tests/PowerTest.cs | 150 ++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 Content.IntegrationTests/Tests/PowerTest.cs diff --git a/Content.IntegrationTests/Tests/PowerTest.cs b/Content.IntegrationTests/Tests/PowerTest.cs new file mode 100644 index 0000000000..b53fa69734 --- /dev/null +++ b/Content.IntegrationTests/Tests/PowerTest.cs @@ -0,0 +1,150 @@ +using Content.Server.GameObjects.Components.Power; +using Content.Server.GameObjects.Components.Power.ApcNetComponents; +using Content.Server.GameObjects.Components.Power.PowerNetComponents; +using NUnit.Framework; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using System.Threading.Tasks; + +namespace Content.IntegrationTests.Tests +{ + [TestFixture] + public class PowerTest : ContentIntegrationTest + { + [Test] + public async Task PowerNetTest() + { + var server = StartServerDummyTicker(); + + PowerSupplierComponent supplier = null; + PowerConsumerComponent consumer1 = null; + PowerConsumerComponent consumer2 = null; + + server.Assert(() => + { + var mapMan = IoCManager.Resolve(); + var entityMan = IoCManager.Resolve(); + mapMan.CreateMap(new MapId(1)); + var grid = mapMan.CreateGrid(new MapId(1)); + + var generatorEnt = entityMan.SpawnEntity("DebugGenerator", new GridCoordinates(new Vector2(0, 0), grid.Index)); + var consumerEnt1 = entityMan.SpawnEntity("DebugConsumer", new GridCoordinates(new Vector2(0, 1), grid.Index)); + var consumerEnt2 = entityMan.SpawnEntity("DebugConsumer", new GridCoordinates(new Vector2(0, 2), grid.Index)); + + Assert.That(generatorEnt.TryGetComponent(out supplier)); + Assert.That(consumerEnt1.TryGetComponent(out consumer1)); + Assert.That(consumerEnt2.TryGetComponent(out consumer2)); + + var supplyRate = 1000; //arbitrary amount of power supply + + supplier.SupplyRate = supplyRate; + consumer1.DrawRate = supplyRate / 2; //arbitrary draw less than supply + consumer2.DrawRate = supplyRate * 2; //arbitrary draw greater than supply + + consumer1.Priority = Priority.First; //power goes to this consumer first + consumer2.Priority = Priority.Last; //any excess power should go to low priority consumer + }); + + server.RunTicks(1); //let run a tick for PowerNet to process power + + server.Assert(() => + { + Assert.That(consumer1.DrawRate, Is.EqualTo(consumer1.ReceivedPower)); //first should be fully powered + Assert.That(consumer2.ReceivedPower, Is.EqualTo(supplier.SupplyRate - consumer1.ReceivedPower)); //second should get remaining power + }); + + await server.WaitIdleAsync(); + } + + [Test] + public async Task ApcChargingTest() + { + var server = StartServerDummyTicker(); + + BatteryComponent apcBattery = null; + PowerSupplierComponent substationSupplier = null; + + server.Assert(() => + { + var mapMan = IoCManager.Resolve(); + var entityMan = IoCManager.Resolve(); + mapMan.CreateMap(new MapId(1)); + var grid = mapMan.CreateGrid(new MapId(1)); + + var generatorEnt = entityMan.SpawnEntity("DebugGenerator", new GridCoordinates(new Vector2(0, 0), grid.Index)); + var substationEnt = entityMan.SpawnEntity("DebugSubstation", new GridCoordinates(new Vector2(0, 1), grid.Index)); + var apcEnt = entityMan.SpawnEntity("DebugApc", new GridCoordinates(new Vector2(0, 2), grid.Index)); + + Assert.That(generatorEnt.TryGetComponent(out var generatorSupplier)); + + Assert.That(substationEnt.TryGetComponent(out substationSupplier)); + Assert.That(substationEnt.TryGetComponent(out var substationStorage)); + Assert.That(substationEnt.TryGetComponent(out var substationDischarger)); + + Assert.That(apcEnt.TryGetComponent(out apcBattery)); + Assert.That(apcEnt.TryGetComponent(out var apcStorage)); + + generatorSupplier.SupplyRate = 1000; //arbitrary nonzero amount of power + substationStorage.ActiveDrawRate = 1000; //arbitrary nonzero power draw + substationDischarger.ActiveSupplyRate = 500; //arbitirary nonzero power supply less than substation storage draw + apcStorage.ActiveDrawRate = 500; //arbitrary nonzero power draw + apcBattery.MaxCharge = 100; //abbitrary nonzero amount of charge + apcBattery.CurrentCharge = 0; //no charge + }); + + server.RunTicks(5); //let run a few ticks for PowerNets to reevaluate and start charging apc + + server.Assert(() => + { + Assert.That(substationSupplier.SupplyRate, Is.Not.EqualTo(0)); //substation should be providing power + Assert.That(apcBattery.CurrentCharge, Is.Not.EqualTo(0)); //apc battery should have gained charge + }); + + await server.WaitIdleAsync(); + } + + [Test] + public async Task ApcNetTest() + { + var server = StartServerDummyTicker(); + + PowerReceiverComponent receiver = null; + + server.Assert(() => + { + var mapMan = IoCManager.Resolve(); + var entityMan = IoCManager.Resolve(); + mapMan.CreateMap(new MapId(1)); + var grid = mapMan.CreateGrid(new MapId(1)); + + var apcEnt = entityMan.SpawnEntity("DebugApc", new GridCoordinates(new Vector2(0, 0), grid.Index)); + var apcExtensionEnt = entityMan.SpawnEntity("ApcExtensionCable", new GridCoordinates(new Vector2(0, 1), grid.Index)); + var powerReceiverEnt = entityMan.SpawnEntity("DebugPowerReceiver", new GridCoordinates(new Vector2(0, 2), grid.Index)); + + Assert.That(apcEnt.TryGetComponent(out var apc)); + Assert.That(apcExtensionEnt.TryGetComponent(out var provider)); + Assert.That(powerReceiverEnt.TryGetComponent(out receiver)); + + provider.PowerTransferRange = 5; //arbitrary range to reach receiver + receiver.PowerReceptionRange = 5; //arbitrary range to reach provider + + apc.Battery.MaxCharge = 10000; //arbitrary nonzero amount of charge + apc.Battery.CurrentCharge = apc.Battery.MaxCharge; //fill battery + + receiver.Load = 1; //arbitrary small amount of power + }); + + server.RunTicks(1); //let run a tick for ApcNet to process power + + server.Assert(() => + { + Assert.That(receiver.Powered); + }); + + await server.WaitIdleAsync(); + } + } +} From c6abeda53b99e0d8d7a0457b0fd771fa6eb04674 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 20 Aug 2020 21:47:20 +1000 Subject: [PATCH 21/53] Fix AI inventory deleted exc (#1810) Was throwing during eating / drinking. Co-authored-by: Metal Gear Sloth --- .../AI/WorldState/States/Inventory/InventoryState.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Content.Server/AI/WorldState/States/Inventory/InventoryState.cs b/Content.Server/AI/WorldState/States/Inventory/InventoryState.cs index 3f570d58bd..8f9701a9ee 100644 --- a/Content.Server/AI/WorldState/States/Inventory/InventoryState.cs +++ b/Content.Server/AI/WorldState/States/Inventory/InventoryState.cs @@ -16,6 +16,9 @@ namespace Content.Server.AI.WorldState.States.Inventory { foreach (var item in handsComponent.GetAllHeldItems()) { + if (item.Owner.Deleted) + continue; + yield return item.Owner; } } From 599b31621229da3d659c7af687e08264adbd0c8a Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 20 Aug 2020 21:47:47 +1000 Subject: [PATCH 22/53] Fix AI bool curves (#1809) I was a silly billy. AI were only picking up 1 item because 0.5 was being treated as a fail. Co-authored-by: Metal Gear Sloth --- Content.Server/AI/Utility/Considerations/Consideration.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Content.Server/AI/Utility/Considerations/Consideration.cs b/Content.Server/AI/Utility/Considerations/Consideration.cs index 20afc4dec1..67b76ae559 100644 --- a/Content.Server/AI/Utility/Considerations/Consideration.cs +++ b/Content.Server/AI/Utility/Considerations/Consideration.cs @@ -24,7 +24,7 @@ namespace Content.Server.AI.Utility.Considerations private static float BoolCurve(float x) { // ReSharper disable once CompareOfFloatsByEqualityOperator - return x == 1.0f ? 1.0f : 0.0f; + return x > 0.0f ? 1.0f : 0.0f; } public Func BoolCurve(Blackboard context) @@ -42,7 +42,7 @@ namespace Content.Server.AI.Utility.Considerations private static float InverseBoolCurve(float x) { // ReSharper disable once CompareOfFloatsByEqualityOperator - return x == 1.0f ? 0.0f : 1.0f; + return x == 0.0f ? 1.0f : 0.0f; } public Func InverseBoolCurve(Blackboard context) From d8a78f2de6fba6d67f912da095927c53708a7c65 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Thu, 20 Aug 2020 22:16:12 +1000 Subject: [PATCH 23/53] Geometric mean for AI considerations (#1812) * Geometric mean for AI cons More forgiving for having more considerations for an action. * You had 1 job Co-authored-by: Metal Gear Sloth --- .../AI/Utility/Actions/UtilityAction.cs | 13 ++++--------- .../AI/Utility/Considerations/Consideration.cs | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Content.Server/AI/Utility/Actions/UtilityAction.cs b/Content.Server/AI/Utility/Actions/UtilityAction.cs index f8be97abaa..b79fa0955a 100644 --- a/Content.Server/AI/Utility/Actions/UtilityAction.cs +++ b/Content.Server/AI/Utility/Actions/UtilityAction.cs @@ -112,19 +112,14 @@ namespace Content.Server.AI.Utility.Actions UpdateBlackboard(context); var considerations = GetConsiderations(context); DebugTools.Assert(considerations.Count > 0); - // I used the IAUS video although I did have some confusion on how to structure it overall - // as some of the slides seemed contradictory - // Ideally we should early-out each action as cheaply as possible if it's not valid - - // We also need some way to tell if the action isn't going to - // have a better score than the current action (if applicable) and early-out that way as well. - - // 23:00 Building a better centaur + // Overall structure is based on Building a better centaur + // Ideally we should early-out each action as cheaply as possible if it's not valid, thus + // the finalScore can only go down over time. + var finalScore = 1.0f; var minThreshold = min / Bonus; context.GetState().SetValue(considerations.Count); - // See 10:09 for this and the adjustments foreach (var consideration in considerations) { diff --git a/Content.Server/AI/Utility/Considerations/Consideration.cs b/Content.Server/AI/Utility/Considerations/Consideration.cs index 67b76ae559..39d125941f 100644 --- a/Content.Server/AI/Utility/Considerations/Consideration.cs +++ b/Content.Server/AI/Utility/Considerations/Consideration.cs @@ -13,10 +13,19 @@ namespace Content.Server.AI.Utility.Considerations private float GetAdjustedScore(Blackboard context) { var score = GetScore(context); + /* + * Now using the geometric mean + * for n scores you take the n-th root of the scores multiplied + * e.g. a, b, c scores you take Math.Pow(a * b * c, 1/3) + * To get the ACTUAL geometric mean at any one stage you'd need to divide by the running consideration count + * however, the downside to this is it will fluctuate up and down over time. + * For our purposes if we go below the minimum threshold we want to cut it off, thus we take a + * "running geometric mean" which can only ever go down (and by the final value will equal the actual geometric mean). + */ + + // Previously we used a makeupvalue method although the geometric mean is less punishing for more considerations var considerationsCount = context.GetState().GetValue(); - var modificationFactor = 1.0f - 1.0f / considerationsCount; - var makeUpValue = (1.0f - score) * modificationFactor; - var adjustedScore = score + makeUpValue * score; + var adjustedScore = MathF.Pow(score, 1 / (float) considerationsCount); return FloatMath.Clamp(adjustedScore, 0.0f, 1.0f); } From d703dc8dccdcf82e768ec6f05e42ba974b41027c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= Date: Thu, 20 Aug 2020 15:19:36 +0200 Subject: [PATCH 24/53] Adds readyall (b)admin command. --- Content.Server/Administration/ReadyAll.cs | 39 +++++++++++++++++++++++ Resources/Groups/groups.yml | 2 ++ 2 files changed, 41 insertions(+) create mode 100644 Content.Server/Administration/ReadyAll.cs diff --git a/Content.Server/Administration/ReadyAll.cs b/Content.Server/Administration/ReadyAll.cs new file mode 100644 index 0000000000..a03a5b447e --- /dev/null +++ b/Content.Server/Administration/ReadyAll.cs @@ -0,0 +1,39 @@ +using Content.Server.GameTicking; +using Content.Server.Interfaces.GameTicking; +using Robust.Server.Interfaces.Console; +using Robust.Server.Interfaces.Player; +using Robust.Shared.IoC; + +namespace Content.Server.Administration +{ + public class ReadyAll : IClientCommand + { + public string Command => "readyall"; + public string Description => "Readies up all players in the lobby."; + public string Help => $"{Command} | ̣{Command} "; + public void Execute(IConsoleShell shell, IPlayerSession? player, string[] args) + { + var ready = true; + + if (args.Length > 0) + { + ready = bool.Parse(args[0]); + } + + var gameTicker = IoCManager.Resolve(); + var playerManager = IoCManager.Resolve(); + + + if (gameTicker.RunLevel != GameRunLevel.PreRoundLobby) + { + shell.SendText(player, "This command can only be ran while in the lobby!"); + return; + } + + foreach (var p in playerManager.GetAllPlayers()) + { + gameTicker.ToggleReady(p, ready); + } + } + } +} diff --git a/Resources/Groups/groups.yml b/Resources/Groups/groups.yml index ea45d9a588..e18adbfd95 100644 --- a/Resources/Groups/groups.yml +++ b/Resources/Groups/groups.yml @@ -96,6 +96,7 @@ - tilewalls - events - destroymechanism + - readyall CanViewVar: true CanAdminPlace: true @@ -186,6 +187,7 @@ - tilewalls - events - destroymechanism + - readyall CanViewVar: true CanAdminPlace: true CanScript: true From 2bdf35928903cb9a768ea36424424023b5aacfa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= Date: Thu, 20 Aug 2020 16:20:08 +0200 Subject: [PATCH 25/53] Fix readyall nullable --- Content.Server/Administration/ReadyAll.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Content.Server/Administration/ReadyAll.cs b/Content.Server/Administration/ReadyAll.cs index a03a5b447e..05d0b48fc8 100644 --- a/Content.Server/Administration/ReadyAll.cs +++ b/Content.Server/Administration/ReadyAll.cs @@ -1,4 +1,5 @@ -using Content.Server.GameTicking; +#nullable enable +using Content.Server.GameTicking; using Content.Server.Interfaces.GameTicking; using Robust.Server.Interfaces.Console; using Robust.Server.Interfaces.Player; From 944ce2cc921e341c776d9fdf01cab228cf27c1fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= <6766154+Zumorica@users.noreply.github.com> Date: Thu, 20 Aug 2020 16:20:48 +0200 Subject: [PATCH 26/53] Allow game presets to disallow latejoining (#1816) * Allow game presets to disallow latejoining * Update Content.Server/GameTicking/GameTicker.cs Co-authored-by: DrSmugleaf Co-authored-by: DrSmugleaf --- .../GameTicking/ClientGameTicker.cs | 8 ++++++ .../Interfaces/IClientGameTicker.cs | 2 ++ Content.Client/State/LobbyState.cs | 8 ++++++ Content.Server/GameTicking/GamePreset.cs | 1 + .../GamePresets/PresetSuspicion.cs | 3 +++ Content.Server/GameTicking/GameTicker.cs | 23 +++++++++++++++-- Content.Shared/SharedGameTicker.cs | 25 +++++++++++++++++++ 7 files changed, 68 insertions(+), 2 deletions(-) diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs index 3b58d1c17b..d618046255 100644 --- a/Content.Client/GameTicking/ClientGameTicker.cs +++ b/Content.Client/GameTicking/ClientGameTicker.cs @@ -26,6 +26,7 @@ namespace Content.Client.GameTicking [ViewVariables] public bool AreWeReady { get; private set; } [ViewVariables] public bool IsGameStarted { get; private set; } + [ViewVariables] public bool DisallowedLateJoin { get; private set; } [ViewVariables] public string ServerInfoBlob { get; private set; } [ViewVariables] public DateTime StartTime { get; private set; } [ViewVariables] public bool Paused { get; private set; } @@ -34,6 +35,7 @@ namespace Content.Client.GameTicking public event Action InfoBlobUpdated; public event Action LobbyStatusUpdated; public event Action LobbyReadyUpdated; + public event Action LobbyLateJoinStatusUpdated; public void Initialize() { @@ -50,11 +52,17 @@ namespace Content.Client.GameTicking { IoCManager.Resolve().RequestWindowAttention(); }); + _netManager.RegisterNetMessage(nameof(MsgTickerLateJoinStatus), LateJoinStatus); Ready = new Dictionary(); _initialized = true; } + private void LateJoinStatus(MsgTickerLateJoinStatus message) + { + DisallowedLateJoin = message.Disallowed; + LobbyLateJoinStatusUpdated?.Invoke(); + } private void JoinLobby(MsgTickerJoinLobby message) diff --git a/Content.Client/Interfaces/IClientGameTicker.cs b/Content.Client/Interfaces/IClientGameTicker.cs index 0bedce3995..e1e584e2f3 100644 --- a/Content.Client/Interfaces/IClientGameTicker.cs +++ b/Content.Client/Interfaces/IClientGameTicker.cs @@ -9,6 +9,7 @@ namespace Content.Client.Interfaces bool IsGameStarted { get; } string ServerInfoBlob { get; } bool AreWeReady { get; } + bool DisallowedLateJoin { get; } DateTime StartTime { get; } bool Paused { get; } Dictionary Ready { get; } @@ -17,5 +18,6 @@ namespace Content.Client.Interfaces event Action InfoBlobUpdated; event Action LobbyStatusUpdated; event Action LobbyReadyUpdated; + event Action LobbyLateJoinStatusUpdated; } } diff --git a/Content.Client/State/LobbyState.cs b/Content.Client/State/LobbyState.cs index b5fc2c35a1..2580dadb64 100644 --- a/Content.Client/State/LobbyState.cs +++ b/Content.Client/State/LobbyState.cs @@ -101,6 +101,7 @@ namespace Content.Client.State _clientGameTicker.InfoBlobUpdated += UpdateLobbyUi; _clientGameTicker.LobbyStatusUpdated += LobbyStatusUpdated; _clientGameTicker.LobbyReadyUpdated += LobbyReadyUpdated; + _clientGameTicker.LobbyLateJoinStatusUpdated += LobbyLateJoinStatusUpdated; } public override void Shutdown() @@ -109,6 +110,7 @@ namespace Content.Client.State _clientGameTicker.InfoBlobUpdated -= UpdateLobbyUi; _clientGameTicker.LobbyStatusUpdated -= LobbyStatusUpdated; _clientGameTicker.LobbyReadyUpdated -= LobbyReadyUpdated; + _clientGameTicker.LobbyLateJoinStatusUpdated -= LobbyLateJoinStatusUpdated; _clientGameTicker.Ready.Clear(); @@ -173,6 +175,11 @@ namespace Content.Client.State UpdateLobbyUi(); } + private void LobbyLateJoinStatusUpdated() + { + _lobby.ReadyButton.Disabled = _clientGameTicker.DisallowedLateJoin; + } + private void UpdateLobbyUi() { if (_lobby == null) @@ -191,6 +198,7 @@ namespace Content.Client.State _lobby.StartTime.Text = ""; _lobby.ReadyButton.Text = Loc.GetString("Ready Up"); _lobby.ReadyButton.ToggleMode = true; + _lobby.ReadyButton.Disabled = false; _lobby.ReadyButton.Pressed = _clientGameTicker.AreWeReady; } diff --git a/Content.Server/GameTicking/GamePreset.cs b/Content.Server/GameTicking/GamePreset.cs index 0e7ce86795..ddde553722 100644 --- a/Content.Server/GameTicking/GamePreset.cs +++ b/Content.Server/GameTicking/GamePreset.cs @@ -12,6 +12,7 @@ namespace Content.Server.GameTicking public abstract bool Start(IReadOnlyList readyPlayers, bool force = false); public virtual string ModeTitle => "Sandbox"; public virtual string Description => "Secret!"; + public virtual bool DisallowLateJoin => false; public Dictionary readyProfiles; } } diff --git a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs index 3ce23717d4..c77b8a110e 100644 --- a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs +++ b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs @@ -30,6 +30,9 @@ namespace Content.Server.GameTicking.GamePresets public int MinPlayers { get; set; } = 5; public int MinTraitors { get; set; } = 2; public int PlayersPerTraitor { get; set; } = 5; + + public override bool DisallowLateJoin => true; + private static string TraitorID = "SuspicionTraitor"; private static string InnocentID = "SuspicionInnocent"; diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 476a7c55f8..34d40c8df3 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -94,6 +94,8 @@ namespace Content.Server.GameTicking [ViewVariables] private GameRunLevel _runLevel; [ViewVariables(VVAccess.ReadWrite)] private GridCoordinates _spawnPoint; + [ViewVariables] private bool DisallowLateJoin { get; set; } = false; + [ViewVariables] private bool LobbyEnabled => _configurationManager.GetCVar("game.lobbyenabled"); [ViewVariables] private bool _updateOnRoundEnd; @@ -142,6 +144,7 @@ namespace Content.Server.GameTicking _netManager.RegisterNetMessage(nameof(MsgTickerLobbyReady)); _netManager.RegisterNetMessage(nameof(MsgRoundEndMessage)); _netManager.RegisterNetMessage(nameof(MsgRequestWindowAttention)); + _netManager.RegisterNetMessage(nameof(MsgTickerLateJoinStatus)); SetStartPreset(_configurationManager.GetCVar("game.defaultpreset")); @@ -285,6 +288,8 @@ namespace Content.Server.GameTicking // Time to start the preset. var preset = MakeGamePreset(profiles); + DisallowLateJoin |= preset.DisallowLateJoin; + if (!preset.Start(assignedJobs.Keys.ToList(), force)) { SetStartPreset(_configurationManager.GetCVar("game.fallbackpreset")); @@ -300,6 +305,13 @@ namespace Content.Server.GameTicking _roundStartTimeSpan = IoCManager.Resolve().RealTime; _sendStatusToAll(); ReqWindowAttentionAll(); + UpdateLateJoinStatus(); + } + + private void UpdateLateJoinStatus() + { + var msg = new MsgTickerLateJoinStatus(null) {Disallowed = DisallowLateJoin}; + _netManager.ServerSendToAll(msg); } private void SendServerMessage(string message) @@ -405,7 +417,7 @@ namespace Content.Server.GameTicking foreach (var rule in _gameRules) { - if (rule.GetType().Equals(t)) + if (rule.GetType().IsAssignableFrom(t)) return true; } @@ -637,7 +649,7 @@ namespace Content.Server.GameTicking _playerJoinLobby(player); } - + EntitySystem.Get().ResettingCleanup(); EntitySystem.Get().ResettingCleanup(); EntitySystem.Get().ResettingCleanup(); @@ -646,6 +658,7 @@ namespace Content.Server.GameTicking _spawnedPositions.Clear(); _manifest.Clear(); + DisallowLateJoin = false; } private void _preRoundSetup() @@ -776,6 +789,12 @@ namespace Content.Server.GameTicking string jobId = null, bool lateJoin = true) { + if (lateJoin && DisallowLateJoin) + { + MakeObserve(session); + return; + } + _playerJoinGame(session); var data = session.ContentData(); diff --git a/Content.Shared/SharedGameTicker.cs b/Content.Shared/SharedGameTicker.cs index d7100f93e4..e180ddb198 100644 --- a/Content.Shared/SharedGameTicker.cs +++ b/Content.Shared/SharedGameTicker.cs @@ -54,6 +54,31 @@ namespace Content.Shared } } + protected class MsgTickerLateJoinStatus : NetMessage + { + #region REQUIRED + + public const MsgGroups GROUP = MsgGroups.Command; + public const string NAME = nameof(MsgTickerLateJoinStatus); + + public bool Disallowed { get; set; } + + public MsgTickerLateJoinStatus(INetChannel channel) : base(NAME, GROUP) { } + + #endregion + + public override void ReadFromBuffer(NetIncomingMessage buffer) + { + Disallowed = buffer.ReadBoolean(); + } + + public override void WriteToBuffer(NetOutgoingMessage buffer) + { + buffer.Write(Disallowed); + } + } + + protected class MsgTickerLobbyStatus : NetMessage { #region REQUIRED From 3372ab050cd5b489b5962c4cb79967ace93b0602 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 16:47:46 +0200 Subject: [PATCH 27/53] Update submodule. --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 1513389fc1..d4fc0517c4 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 1513389fc195442a0f08b0af488a25affb33d410 +Subproject commit d4fc0517c4d7bc4c86416da065a2bcb37591a8c9 From ed1a96e536026046c4ad6c2c73f7968f09e1205c Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 16:48:00 +0200 Subject: [PATCH 28/53] Nullability fixes. --- .../Components/Body/BodyManagerComponent.cs | 4 +- .../Components/ClickableComponent.cs | 2 +- .../Components/Items/HandsComponent.cs | 2 +- .../Components/Mobs/StunnableComponent.cs | 2 +- .../Components/Nutrition/HungerComponent.cs | 2 +- .../Components/Nutrition/ThirstComponent.cs | 2 +- .../EntitySystems/DoAfter/DoAfterGui.cs | 2 +- .../EntitySystems/DoAfter/DoAfterSystem.cs | 4 +- .../GameObjects/EntitySystems/MoverSystem.cs | 2 +- .../UserInterface/CooldownGraphic.cs | 1 + .../Tests/Disposal/DisposalUnitTest.cs | 12 ++-- .../Components/Movement/ClimbUnitTest.cs | 6 +- Content.Server/Body/BodyCommands.cs | 6 +- .../Mechanisms/Behaviors/HeartBehavior.cs | 2 +- .../Body/Mechanisms/Behaviors/LungBehavior.cs | 2 +- .../Mechanisms/Behaviors/StomachBehavior.cs | 2 +- .../Access/AccessReaderComponent.cs | 10 +-- .../Components/AnchorableComponent.cs | 4 +- .../Components/Atmos/GasAnalyzerComponent.cs | 18 ++--- .../Components/Body/BodyManagerComponent.cs | 2 +- .../Components/Buckle/BuckleComponent.cs | 16 ++--- .../Components/Cargo/CargoConsoleComponent.cs | 2 +- .../Components/Conveyor/ConveyorComponent.cs | 14 ++-- .../Conveyor/ConveyorSwitchComponent.cs | 10 +-- .../Components/Disposal/DisposalCommands.cs | 2 +- .../Disposal/DisposalHolderComponent.cs | 6 +- .../Disposal/DisposalTubeComponent.cs | 10 +-- .../Disposal/DisposalUnitComponent.cs | 24 +++---- .../Components/DoAfterComponent.cs | 2 +- .../Components/Fluids/SpillHelper.cs | 2 +- .../Components/GUI/HandsComponent.cs | 10 +-- .../Components/Interactable/ToolCommands.cs | 4 +- .../Items/Storage/ServerStorageComponent.cs | 14 ++-- .../Components/Mobs/MindComponent.cs | 2 +- .../Movement/ShuttleControllerComponent.cs | 12 ++-- .../Components/PDA/PDAComponent.cs | 4 +- .../Pointing/PointingArrowComponent.cs | 2 +- .../Pointing/RoguePointingArrowComponent.cs | 4 +- .../Rotatable/FlippableComponent.cs | 2 +- .../GameObjects/Components/WiresComponent.cs | 2 +- .../Atmos/GasTileOverlaySystem.cs | 66 +++++++++---------- .../EntitySystems/AtmosphereSystem.cs | 2 +- .../EntitySystems/DoAfter/DoAfter.cs | 6 +- .../GameObjects/EntitySystems/MoverSystem.cs | 2 +- .../EntitySystems/PointingSystem.cs | 2 +- .../SharedPlayerInputMoverComponent.cs | 4 +- .../EntitySystems/SharedMoverSystem.cs | 4 +- 47 files changed, 159 insertions(+), 158 deletions(-) diff --git a/Content.Client/GameObjects/Components/Body/BodyManagerComponent.cs b/Content.Client/GameObjects/Components/Body/BodyManagerComponent.cs index 2948c9191e..61049ec7a0 100644 --- a/Content.Client/GameObjects/Components/Body/BodyManagerComponent.cs +++ b/Content.Client/GameObjects/Components/Body/BodyManagerComponent.cs @@ -33,7 +33,7 @@ namespace Content.Client.GameObjects.Components.Body public override void HandleNetworkMessage(ComponentMessage message, INetChannel netChannel, ICommonSession? session = null) { - if (!Owner.TryGetComponent(out ISpriteComponent sprite)) + if (!Owner.TryGetComponent(out ISpriteComponent? sprite)) { return; } @@ -50,7 +50,7 @@ namespace Content.Client.GameObjects.Components.Body if (!partRemoved.Dropped.HasValue || !_entityManager.TryGetEntity(partRemoved.Dropped.Value, out var entity) || - !entity.TryGetComponent(out ISpriteComponent droppedSprite)) + !entity.TryGetComponent(out ISpriteComponent? droppedSprite)) { break; } diff --git a/Content.Client/GameObjects/Components/ClickableComponent.cs b/Content.Client/GameObjects/Components/ClickableComponent.cs index 57d9420167..a92af22451 100644 --- a/Content.Client/GameObjects/Components/ClickableComponent.cs +++ b/Content.Client/GameObjects/Components/ClickableComponent.cs @@ -37,7 +37,7 @@ namespace Content.Client.GameObjects.Components /// True if the click worked, false otherwise. public bool CheckClick(Vector2 worldPos, out int drawDepth, out uint renderOrder) { - if (!Owner.TryGetComponent(out ISpriteComponent sprite) || !sprite.Visible) + if (!Owner.TryGetComponent(out ISpriteComponent? sprite) || !sprite.Visible) { drawDepth = default; renderOrder = default; diff --git a/Content.Client/GameObjects/Components/Items/HandsComponent.cs b/Content.Client/GameObjects/Components/Items/HandsComponent.cs index daa03d74e1..30e32afad3 100644 --- a/Content.Client/GameObjects/Components/Items/HandsComponent.cs +++ b/Content.Client/GameObjects/Components/Items/HandsComponent.cs @@ -148,7 +148,7 @@ namespace Content.Client.GameObjects.Components.Items return; } - if (!entity.TryGetComponent(out ItemComponent item)) return; + if (!entity.TryGetComponent(out ItemComponent? item)) return; var maybeInHands = item.GetInHandStateInfo(hand.Location); diff --git a/Content.Client/GameObjects/Components/Mobs/StunnableComponent.cs b/Content.Client/GameObjects/Components/Mobs/StunnableComponent.cs index 4c02d343cb..9d6ce08f8f 100644 --- a/Content.Client/GameObjects/Components/Mobs/StunnableComponent.cs +++ b/Content.Client/GameObjects/Components/Mobs/StunnableComponent.cs @@ -34,7 +34,7 @@ namespace Content.Client.GameObjects.Components.Mobs WalkModifierOverride = state.WalkModifierOverride; RunModifierOverride = state.RunModifierOverride; - if (Owner.TryGetComponent(out MovementSpeedModifierComponent movement)) + if (Owner.TryGetComponent(out MovementSpeedModifierComponent? movement)) { movement.RefreshMovementSpeedModifiers(); } diff --git a/Content.Client/GameObjects/Components/Nutrition/HungerComponent.cs b/Content.Client/GameObjects/Components/Nutrition/HungerComponent.cs index 8323fb5e7a..6db62b1bb3 100644 --- a/Content.Client/GameObjects/Components/Nutrition/HungerComponent.cs +++ b/Content.Client/GameObjects/Components/Nutrition/HungerComponent.cs @@ -20,7 +20,7 @@ namespace Content.Client.GameObjects.Components.Nutrition _currentHungerThreshold = hunger.CurrentThreshold; - if (Owner.TryGetComponent(out MovementSpeedModifierComponent movement)) + if (Owner.TryGetComponent(out MovementSpeedModifierComponent? movement)) { movement.RefreshMovementSpeedModifiers(); } diff --git a/Content.Client/GameObjects/Components/Nutrition/ThirstComponent.cs b/Content.Client/GameObjects/Components/Nutrition/ThirstComponent.cs index b77d59a34a..a211afe239 100644 --- a/Content.Client/GameObjects/Components/Nutrition/ThirstComponent.cs +++ b/Content.Client/GameObjects/Components/Nutrition/ThirstComponent.cs @@ -20,7 +20,7 @@ namespace Content.Client.GameObjects.Components.Nutrition _currentThirstThreshold = thirst.CurrentThreshold; - if (Owner.TryGetComponent(out MovementSpeedModifierComponent movement)) + if (Owner.TryGetComponent(out MovementSpeedModifierComponent? movement)) { movement.RefreshMovementSpeedModifiers(); } diff --git a/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterGui.cs b/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterGui.cs index b46b359ff4..a6724c0f6a 100644 --- a/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterGui.cs +++ b/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterGui.cs @@ -143,7 +143,7 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter { base.FrameUpdate(args); - if (AttachedEntity?.IsValid() != true || !AttachedEntity.TryGetComponent(out DoAfterComponent doAfterComponent)) + if (AttachedEntity?.IsValid() != true || !AttachedEntity.TryGetComponent(out DoAfterComponent? doAfterComponent)) { return; } diff --git a/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterSystem.cs b/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterSystem.cs index 8aed094c7f..cc0337a941 100644 --- a/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/DoAfter/DoAfterSystem.cs @@ -67,7 +67,7 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter Gui ??= new DoAfterGui(); Gui.AttachedEntity = entity; - if (entity.TryGetComponent(out DoAfterComponent doAfterComponent)) + if (entity.TryGetComponent(out DoAfterComponent? doAfterComponent)) { foreach (var (_, doAfter) in doAfterComponent.DoAfters) { @@ -87,7 +87,7 @@ namespace Content.Client.GameObjects.EntitySystems.DoAfter return; } - if (!_player.TryGetComponent(out DoAfterComponent doAfterComponent)) + if (!_player.TryGetComponent(out DoAfterComponent? doAfterComponent)) { return; } diff --git a/Content.Client/GameObjects/EntitySystems/MoverSystem.cs b/Content.Client/GameObjects/EntitySystems/MoverSystem.cs index cdabb87d7c..db3ad9fc33 100644 --- a/Content.Client/GameObjects/EntitySystems/MoverSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/MoverSystem.cs @@ -25,7 +25,7 @@ namespace Content.Client.GameObjects.EntitySystems { var playerEnt = _playerManager.LocalPlayer?.ControlledEntity; - if (playerEnt == null || !playerEnt.TryGetComponent(out IMoverComponent mover)) + if (playerEnt == null || !playerEnt.TryGetComponent(out IMoverComponent? mover)) { return; } diff --git a/Content.Client/UserInterface/CooldownGraphic.cs b/Content.Client/UserInterface/CooldownGraphic.cs index 777ec9e1d9..c659d2ce19 100644 --- a/Content.Client/UserInterface/CooldownGraphic.cs +++ b/Content.Client/UserInterface/CooldownGraphic.cs @@ -30,6 +30,7 @@ namespace Content.Client.UserInterface protected override void Draw(DrawingHandleScreen handle) { + Span x = stackalloc float[10]; Color color; var lerp = 1f - MathF.Abs(Progress); // for future bikeshedding purposes diff --git a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs index 4f6b309bf7..3eb3821634 100644 --- a/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs +++ b/Content.IntegrationTests/Tests/Disposal/DisposalUnitTest.cs @@ -81,8 +81,8 @@ namespace Content.IntegrationTests.Tests.Disposal var disposalTrunk = entityManager.SpawnEntity("DisposalTrunk", disposalUnit.Transform.MapPosition); // Test for components existing - Assert.True(disposalUnit.TryGetComponent(out unit)); - Assert.True(disposalTrunk.TryGetComponent(out entry)); + Assert.True(disposalUnit.TryGetComponent(out unit!)); + Assert.True(disposalTrunk.TryGetComponent(out entry!)); // Can't insert, unanchored and unpowered var disposalUnitAnchorable = disposalUnit.GetComponent(); @@ -92,8 +92,8 @@ namespace Content.IntegrationTests.Tests.Disposal // Anchor the disposal unit await disposalUnitAnchorable.TryAnchor(human, null, true); - Assert.True(disposalUnit.TryGetComponent(out AnchorableComponent anchorableUnit)); - Assert.True(await anchorableUnit.TryAnchor(human, wrench)); + Assert.True(disposalUnit.TryGetComponent(out AnchorableComponent? anchorableUnit)); + Assert.True(await anchorableUnit!.TryAnchor(human, wrench)); Assert.True(unit.Anchored); // No power @@ -118,8 +118,8 @@ namespace Content.IntegrationTests.Tests.Disposal Flush(unit, false, entry, human, wrench); // Remove power need - Assert.True(disposalUnit.TryGetComponent(out PowerReceiverComponent power)); - power.NeedsPower = false; + Assert.True(disposalUnit.TryGetComponent(out PowerReceiverComponent? power)); + power!.NeedsPower = false; Assert.True(unit.Powered); // Flush with a mob and an item diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/Movement/ClimbUnitTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/Movement/ClimbUnitTest.cs index 7ae95a83af..e55218ef09 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/Movement/ClimbUnitTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/Movement/ClimbUnitTest.cs @@ -41,13 +41,13 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Movement // Test for climb components existing // Players and tables should have these in their prototypes. - Assert.True(human.TryGetComponent(out climbing), "Human has no climbing"); - Assert.True(table.TryGetComponent(out climbable), "Table has no climbable"); + Assert.True(human.TryGetComponent(out climbing!), "Human has no climbing"); + Assert.True(table.TryGetComponent(out climbable!), "Table has no climbable"); // Now let's make the player enter a climbing transitioning state. climbing.IsClimbing = true; climbing.TryMoveTo(human.Transform.WorldPosition, table.Transform.WorldPosition); - human.TryGetComponent(out ICollidableComponent body); + var body = human.GetComponent(); Assert.True(body.HasController(), "Player has no ClimbController"); diff --git a/Content.Server/Body/BodyCommands.cs b/Content.Server/Body/BodyCommands.cs index 89d6355203..9809a5845f 100644 --- a/Content.Server/Body/BodyCommands.cs +++ b/Content.Server/Body/BodyCommands.cs @@ -32,7 +32,7 @@ namespace Content.Server.Body return; } - if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent body)) + if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent? body)) { var random = IoCManager.Resolve(); var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}"; @@ -72,7 +72,7 @@ namespace Content.Server.Body return; } - if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent body)) + if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent? body)) { var random = IoCManager.Resolve(); var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}"; @@ -119,7 +119,7 @@ namespace Content.Server.Body return; } - if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent body)) + if (!player.AttachedEntity.TryGetComponent(out BodyManagerComponent? body)) { var random = IoCManager.Resolve(); var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}"; diff --git a/Content.Server/Body/Mechanisms/Behaviors/HeartBehavior.cs b/Content.Server/Body/Mechanisms/Behaviors/HeartBehavior.cs index 51366583bd..2229357bfc 100644 --- a/Content.Server/Body/Mechanisms/Behaviors/HeartBehavior.cs +++ b/Content.Server/Body/Mechanisms/Behaviors/HeartBehavior.cs @@ -19,7 +19,7 @@ namespace Content.Server.Body.Mechanisms.Behaviors base.PreMetabolism(frameTime); if (Mechanism.Body == null || - !Mechanism.Body.Owner.TryGetComponent(out BloodstreamComponent bloodstream)) + !Mechanism.Body.Owner.TryGetComponent(out BloodstreamComponent? bloodstream)) { return; } diff --git a/Content.Server/Body/Mechanisms/Behaviors/LungBehavior.cs b/Content.Server/Body/Mechanisms/Behaviors/LungBehavior.cs index 675cc37e76..bc2591c387 100644 --- a/Content.Server/Body/Mechanisms/Behaviors/LungBehavior.cs +++ b/Content.Server/Body/Mechanisms/Behaviors/LungBehavior.cs @@ -16,7 +16,7 @@ namespace Content.Server.Body.Mechanisms.Behaviors base.PreMetabolism(frameTime); if (Mechanism.Body == null || - !Mechanism.Body.Owner.TryGetComponent(out LungComponent lung)) + !Mechanism.Body.Owner.TryGetComponent(out LungComponent? lung)) { return; } diff --git a/Content.Server/Body/Mechanisms/Behaviors/StomachBehavior.cs b/Content.Server/Body/Mechanisms/Behaviors/StomachBehavior.cs index ae1e17b49b..ce6a2bcf43 100644 --- a/Content.Server/Body/Mechanisms/Behaviors/StomachBehavior.cs +++ b/Content.Server/Body/Mechanisms/Behaviors/StomachBehavior.cs @@ -18,7 +18,7 @@ namespace Content.Server.Body.Mechanisms.Behaviors base.PreMetabolism(frameTime); if (Mechanism.Body == null || - !Mechanism.Body.Owner.TryGetComponent(out StomachComponent stomach)) + !Mechanism.Body.Owner.TryGetComponent(out StomachComponent? stomach)) { return; } diff --git a/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs b/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs index 4ebfcd20bd..c861074d34 100644 --- a/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs +++ b/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs @@ -71,16 +71,16 @@ namespace Content.Server.GameObjects.Components.Access public static ICollection FindAccessTags(IEntity entity) { - if (entity.TryGetComponent(out IAccess accessComponent)) + if (entity.TryGetComponent(out IAccess? accessComponent)) { return accessComponent.Tags; } - if (entity.TryGetComponent(out IHandsComponent handsComponent)) + if (entity.TryGetComponent(out IHandsComponent? handsComponent)) { var activeHandEntity = handsComponent.GetActiveHand?.Owner; if (activeHandEntity != null && - activeHandEntity.TryGetComponent(out IAccess handAccessComponent)) + activeHandEntity.TryGetComponent(out IAccess? handAccessComponent)) { return handAccessComponent.Tags; } @@ -90,11 +90,11 @@ namespace Content.Server.GameObjects.Components.Access return Array.Empty(); } - if (entity.TryGetComponent(out InventoryComponent inventoryComponent)) + if (entity.TryGetComponent(out InventoryComponent? inventoryComponent)) { if (inventoryComponent.HasSlot(EquipmentSlotDefines.Slots.IDCARD) && inventoryComponent.TryGetSlotItem(EquipmentSlotDefines.Slots.IDCARD, out ItemComponent item) && - item.Owner.TryGetComponent(out IAccess idAccessComponent) + item.Owner.TryGetComponent(out IAccess? idAccessComponent) ) { return idAccessComponent.Tags; diff --git a/Content.Server/GameObjects/Components/AnchorableComponent.cs b/Content.Server/GameObjects/Components/AnchorableComponent.cs index 9837ac04cb..224ce59886 100644 --- a/Content.Server/GameObjects/Components/AnchorableComponent.cs +++ b/Content.Server/GameObjects/Components/AnchorableComponent.cs @@ -34,7 +34,7 @@ namespace Content.Server.GameObjects.Components if (!force) { if (utilizing == null || - !utilizing.TryGetComponent(out ToolComponent tool) || + !utilizing.TryGetComponent(out ToolComponent? tool) || !(await tool.UseTool(user, Owner, 0.5f, ToolQuality.Anchoring))) { return false; @@ -93,7 +93,7 @@ namespace Content.Server.GameObjects.Components /// true if toggled, false otherwise private async Task TryToggleAnchor(IEntity user, IEntity? utilizing = null, bool force = false) { - if (!Owner.TryGetComponent(out ICollidableComponent collidable)) + if (!Owner.TryGetComponent(out ICollidableComponent? collidable)) { return false; } diff --git a/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs b/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs index 066bc0940a..2a6a2be135 100644 --- a/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs +++ b/Content.Server/GameObjects/Components/Atmos/GasAnalyzerComponent.cs @@ -116,7 +116,7 @@ namespace Content.Server.GameObjects.Components.Atmos { _pressureDanger = GasAnalyzerDanger.Nominal; } - + Dirty(); _timeSinceSync = 0f; } @@ -131,11 +131,11 @@ namespace Content.Server.GameObjects.Components.Atmos if (session.AttachedEntity == null) return; - if (!session.AttachedEntity.TryGetComponent(out IHandsComponent handsComponent)) + if (!session.AttachedEntity.TryGetComponent(out IHandsComponent? handsComponent)) return; var activeHandEntity = handsComponent?.GetActiveHand?.Owner; - if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent gasAnalyzer)) + if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent? gasAnalyzer)) { return; } @@ -147,7 +147,7 @@ namespace Content.Server.GameObjects.Components.Atmos // Check if position is out of range => don't update if (!_position.Value.InRange(_mapManager, pos, SharedInteractionSystem.InteractionRange)) return; - + pos = _position.Value; } @@ -195,7 +195,7 @@ namespace Content.Server.GameObjects.Components.Atmos return; } - if (!player.TryGetComponent(out IHandsComponent handsComponent)) + if (!player.TryGetComponent(out IHandsComponent? handsComponent)) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, Loc.GetString("You have no hands.")); @@ -203,7 +203,7 @@ namespace Content.Server.GameObjects.Components.Atmos } var activeHandEntity = handsComponent.GetActiveHand?.Owner; - if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent gasAnalyzer)) + if (activeHandEntity == null || !activeHandEntity.TryGetComponent(out GasAnalyzerComponent? gasAnalyzer)) { _notifyManager.PopupMessage(serverMsg.Session.AttachedEntity, serverMsg.Session.AttachedEntity, @@ -225,7 +225,7 @@ namespace Content.Server.GameObjects.Components.Atmos return; } - if (eventArgs.User.TryGetComponent(out IActorComponent actor)) + if (eventArgs.User.TryGetComponent(out IActorComponent? actor)) { OpenInterface(actor.playerSession, eventArgs.ClickLocation); //TODO: show other sprite when ui open? @@ -236,7 +236,7 @@ namespace Content.Server.GameObjects.Components.Atmos void IDropped.Dropped(DroppedEventArgs eventArgs) { - if (eventArgs.User.TryGetComponent(out IActorComponent actor)) + if (eventArgs.User.TryGetComponent(out IActorComponent? actor)) { CloseInterface(actor.playerSession); //TODO: if other sprite is shown, change again @@ -245,7 +245,7 @@ namespace Content.Server.GameObjects.Components.Atmos bool IUse.UseEntity(UseEntityEventArgs eventArgs) { - if (eventArgs.User.TryGetComponent(out IActorComponent actor)) + if (eventArgs.User.TryGetComponent(out IActorComponent? actor)) { OpenInterface(actor.playerSession); //TODO: show other sprite when ui open? diff --git a/Content.Server/GameObjects/Components/Body/BodyManagerComponent.cs b/Content.Server/GameObjects/Components/Body/BodyManagerComponent.cs index ac2f427299..5c14e4da0c 100644 --- a/Content.Server/GameObjects/Components/Body/BodyManagerComponent.cs +++ b/Content.Server/GameObjects/Components/Body/BodyManagerComponent.cs @@ -272,7 +272,7 @@ namespace Content.Server.GameObjects.Components.Body private void CalculateSpeed() { - if (!Owner.TryGetComponent(out MovementSpeedModifierComponent playerMover)) + if (!Owner.TryGetComponent(out MovementSpeedModifierComponent? playerMover)) { return; } diff --git a/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs b/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs index 877a0c2727..fcb1144b47 100644 --- a/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs +++ b/Content.Server/GameObjects/Components/Buckle/BuckleComponent.cs @@ -112,7 +112,7 @@ namespace Content.Server.GameObjects.Components.Buckle /// private void BuckleStatus() { - if (Owner.TryGetComponent(out ServerStatusEffectsComponent status)) + if (Owner.TryGetComponent(out ServerStatusEffectsComponent? status)) { status.ChangeStatusEffectIcon(StatusEffect.Buckled, Buckled @@ -291,7 +291,7 @@ namespace Content.Server.GameObjects.Components.Buckle return false; } - if (Owner.TryGetComponent(out AppearanceComponent appearance)) + if (Owner.TryGetComponent(out AppearanceComponent? appearance)) { appearance.SetData(BuckleVisuals.Buckled, true); } @@ -359,12 +359,12 @@ namespace Content.Server.GameObjects.Components.Buckle Owner.Transform.WorldRotation = oldBuckledTo.Owner.Transform.WorldRotation; } - if (Owner.TryGetComponent(out AppearanceComponent appearance)) + if (Owner.TryGetComponent(out AppearanceComponent? appearance)) { appearance.SetData(BuckleVisuals.Buckled, false); } - if (Owner.TryGetComponent(out StunnableComponent stunnable) && stunnable.KnockedDown) + if (Owner.TryGetComponent(out StunnableComponent? stunnable) && stunnable.KnockedDown) { StandingStateHelper.Down(Owner); } @@ -373,14 +373,14 @@ namespace Content.Server.GameObjects.Components.Buckle StandingStateHelper.Standing(Owner); } - if (Owner.TryGetComponent(out MobStateManagerComponent stateManager)) + if (Owner.TryGetComponent(out MobStateManagerComponent? stateManager)) { stateManager.CurrentMobState.EnterState(Owner); } BuckleStatus(); - if (oldBuckledTo.Owner.TryGetComponent(out StrapComponent strap)) + if (oldBuckledTo.Owner.TryGetComponent(out StrapComponent? strap)) { strap.Remove(this); _entitySystem.GetEntitySystem() @@ -535,7 +535,7 @@ namespace Content.Server.GameObjects.Components.Buckle _entityManager.EventBus.UnsubscribeEvents(this); if (BuckledTo != null && - BuckledTo.Owner.TryGetComponent(out StrapComponent strap)) + BuckledTo.Owner.TryGetComponent(out StrapComponent? strap)) { strap.Remove(this); } @@ -552,7 +552,7 @@ namespace Content.Server.GameObjects.Components.Buckle if (BuckledTo != null && Owner.Transform.WorldRotation.GetCardinalDir() == Direction.North && - BuckledTo.Owner.TryGetComponent(out SpriteComponent strapSprite)) + BuckledTo.Owner.TryGetComponent(out SpriteComponent? strapSprite)) { drawDepth = strapSprite.DrawDepth - 1; } diff --git a/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs b/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs index 9e263f5795..ca6445e5e6 100644 --- a/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs +++ b/Content.Server/GameObjects/Components/Cargo/CargoConsoleComponent.cs @@ -159,7 +159,7 @@ namespace Content.Server.GameObjects.Components.Cargo void IActivate.Activate(ActivateEventArgs eventArgs) { - if (!eventArgs.User.TryGetComponent(out IActorComponent actor)) + if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) { return; } diff --git a/Content.Server/GameObjects/Components/Conveyor/ConveyorComponent.cs b/Content.Server/GameObjects/Components/Conveyor/ConveyorComponent.cs index ab636c4fe4..689e3f8bea 100644 --- a/Content.Server/GameObjects/Components/Conveyor/ConveyorComponent.cs +++ b/Content.Server/GameObjects/Components/Conveyor/ConveyorComponent.cs @@ -60,7 +60,7 @@ namespace Content.Server.GameObjects.Components.Conveyor { _state = value; - if (!Owner.TryGetComponent(out AppearanceComponent appearance)) + if (!Owner.TryGetComponent(out AppearanceComponent? appearance)) { return; } @@ -93,7 +93,7 @@ namespace Content.Server.GameObjects.Components.Conveyor return false; } - if (Owner.TryGetComponent(out PowerReceiverComponent receiver) && + if (Owner.TryGetComponent(out PowerReceiverComponent? receiver) && !receiver.Powered) { return false; @@ -114,7 +114,7 @@ namespace Content.Server.GameObjects.Components.Conveyor return false; } - if (!entity.TryGetComponent(out ICollidableComponent collidable) || + if (!entity.TryGetComponent(out ICollidableComponent? collidable) || collidable.Anchored) { return false; @@ -155,7 +155,7 @@ namespace Content.Server.GameObjects.Components.Conveyor continue; } - if (entity.TryGetComponent(out ICollidableComponent collidable)) + if (entity.TryGetComponent(out ICollidableComponent? collidable)) { var controller = collidable.EnsureController(); controller.Move(direction, _speed * frameTime); @@ -225,7 +225,7 @@ namespace Content.Server.GameObjects.Components.Conveyor continue; } - if (!@switch.TryGetComponent(out ConveyorSwitchComponent component)) + if (!@switch.TryGetComponent(out ConveyorSwitchComponent? component)) { continue; } @@ -247,13 +247,13 @@ namespace Content.Server.GameObjects.Components.Conveyor async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { - if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent conveyorSwitch)) + if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent? conveyorSwitch)) { conveyorSwitch.Connect(this, eventArgs.User); return true; } - if (eventArgs.Using.TryGetComponent(out ToolComponent tool)) + if (eventArgs.Using.TryGetComponent(out ToolComponent? tool)) { return await ToolUsed(eventArgs.User, tool); } diff --git a/Content.Server/GameObjects/Components/Conveyor/ConveyorSwitchComponent.cs b/Content.Server/GameObjects/Components/Conveyor/ConveyorSwitchComponent.cs index 3c06575fd2..ae5e2c3aa1 100644 --- a/Content.Server/GameObjects/Components/Conveyor/ConveyorSwitchComponent.cs +++ b/Content.Server/GameObjects/Components/Conveyor/ConveyorSwitchComponent.cs @@ -34,7 +34,7 @@ namespace Content.Server.GameObjects.Components.Conveyor { _state = value; - if (Owner.TryGetComponent(out AppearanceComponent appearance)) + if (Owner.TryGetComponent(out AppearanceComponent? appearance)) { appearance.SetData(ConveyorVisuals.State, value); } @@ -145,7 +145,7 @@ namespace Content.Server.GameObjects.Components.Conveyor continue; } - if (!conveyor.TryGetComponent(out ConveyorComponent component)) + if (!conveyor.TryGetComponent(out ConveyorComponent? component)) { continue; } @@ -172,7 +172,7 @@ namespace Content.Server.GameObjects.Components.Conveyor continue; } - if (!@switch.TryGetComponent(out ConveyorSwitchComponent component)) + if (!@switch.TryGetComponent(out ConveyorSwitchComponent? component)) { continue; } @@ -196,13 +196,13 @@ namespace Content.Server.GameObjects.Components.Conveyor async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { - if (eventArgs.Using.TryGetComponent(out ConveyorComponent conveyor)) + if (eventArgs.Using.TryGetComponent(out ConveyorComponent? conveyor)) { Connect(conveyor, eventArgs.User); return true; } - if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent otherSwitch)) + if (eventArgs.Using.TryGetComponent(out ConveyorSwitchComponent? otherSwitch)) { SyncWith(otherSwitch, eventArgs.User); return true; diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalCommands.cs b/Content.Server/GameObjects/Components/Disposal/DisposalCommands.cs index 5401182b5b..238b31c737 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalCommands.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalCommands.cs @@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.Components.Disposal return; } - if (!entity.TryGetComponent(out IDisposalTubeComponent tube)) + if (!entity.TryGetComponent(out IDisposalTubeComponent? tube)) { shell.SendText(player, Loc.GetString("Entity with uid {0} doesn't have a {1} component", id, nameof(IDisposalTubeComponent))); return; diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs index b07efbdef1..838cf933d2 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalHolderComponent.cs @@ -56,7 +56,7 @@ namespace Content.Server.GameObjects.Components.Disposal return false; } - if (!entity.TryGetComponent(out ICollidableComponent collidable) || + if (!entity.TryGetComponent(out ICollidableComponent? collidable) || !collidable.CanCollide) { return false; @@ -73,7 +73,7 @@ namespace Content.Server.GameObjects.Components.Disposal return false; } - if (entity.TryGetComponent(out ICollidableComponent collidable)) + if (entity.TryGetComponent(out ICollidableComponent? collidable)) { collidable.CanCollide = false; } @@ -105,7 +105,7 @@ namespace Content.Server.GameObjects.Components.Disposal foreach (var entity in _contents.ContainedEntities.ToArray()) { - if (entity.TryGetComponent(out ICollidableComponent collidable)) + if (entity.TryGetComponent(out ICollidableComponent? collidable)) { collidable.CanCollide = true; } diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalTubeComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalTubeComponent.cs index 37b2f2275e..94dccce0a7 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalTubeComponent.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalTubeComponent.cs @@ -44,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Disposal [ViewVariables] private bool Anchored => - !Owner.TryGetComponent(out CollidableComponent collidable) || + !Owner.TryGetComponent(out CollidableComponent? collidable) || collidable.Anchored; /// @@ -71,7 +71,7 @@ namespace Content.Server.GameObjects.Components.Disposal var snapGrid = Owner.GetComponent(); var tube = snapGrid .GetInDir(nextDirection) - .Select(x => x.TryGetComponent(out IDisposalTubeComponent c) ? c : null) + .Select(x => x.TryGetComponent(out IDisposalTubeComponent? c) ? c : null) .FirstOrDefault(x => x != null && x != this); if (tube == null) @@ -153,7 +153,7 @@ namespace Content.Server.GameObjects.Components.Disposal foreach (var entity in Contents.ContainedEntities.ToArray()) { - if (!entity.TryGetComponent(out DisposalHolderComponent holder)) + if (!entity.TryGetComponent(out DisposalHolderComponent? holder)) { continue; } @@ -171,7 +171,7 @@ namespace Content.Server.GameObjects.Components.Disposal private void UpdateVisualState() { - if (!Owner.TryGetComponent(out AppearanceComponent appearance)) + if (!Owner.TryGetComponent(out AppearanceComponent? appearance)) { return; } @@ -187,7 +187,7 @@ namespace Content.Server.GameObjects.Components.Disposal private void AnchoredChanged() { - if (!Owner.TryGetComponent(out CollidableComponent collidable)) + if (!Owner.TryGetComponent(out CollidableComponent? collidable)) { return; } diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs index 2849bb3c16..2e420ef91e 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs @@ -86,12 +86,12 @@ namespace Content.Server.GameObjects.Components.Disposal [ViewVariables] public bool Powered => - !Owner.TryGetComponent(out PowerReceiverComponent receiver) || + !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered; [ViewVariables] public bool Anchored => - !Owner.TryGetComponent(out CollidableComponent collidable) || + !Owner.TryGetComponent(out CollidableComponent? collidable) || collidable.Anchored; [ViewVariables] @@ -122,7 +122,7 @@ namespace Content.Server.GameObjects.Components.Disposal return false; } - if (!entity.TryGetComponent(out ICollidableComponent collidable) || + if (!entity.TryGetComponent(out ICollidableComponent? collidable) || !collidable.CanCollide) { return false; @@ -159,7 +159,7 @@ namespace Content.Server.GameObjects.Components.Disposal { TryQueueEngage(); - if (entity.TryGetComponent(out IActorComponent actor)) + if (entity.TryGetComponent(out IActorComponent? actor)) { _userInterface.Close(actor.playerSession); } @@ -181,7 +181,7 @@ namespace Content.Server.GameObjects.Components.Disposal private bool TryDrop(IEntity user, IEntity entity) { - if (!user.TryGetComponent(out HandsComponent hands)) + if (!user.TryGetComponent(out HandsComponent? hands)) { return false; } @@ -273,7 +273,7 @@ namespace Content.Server.GameObjects.Components.Disposal private void TogglePower() { - if (!Owner.TryGetComponent(out PowerReceiverComponent receiver)) + if (!Owner.TryGetComponent(out PowerReceiverComponent? receiver)) { return; } @@ -352,7 +352,7 @@ namespace Content.Server.GameObjects.Components.Disposal private void UpdateVisualState(bool flush) { - if (!Owner.TryGetComponent(out AppearanceComponent appearance)) + if (!Owner.TryGetComponent(out AppearanceComponent? appearance)) { return; } @@ -488,7 +488,7 @@ namespace Content.Server.GameObjects.Components.Disposal var collidable = Owner.EnsureComponent(); collidable.AnchoredChanged += UpdateVisualState; - if (Owner.TryGetComponent(out PowerReceiverComponent receiver)) + if (Owner.TryGetComponent(out PowerReceiverComponent? receiver)) { receiver.OnPowerStateChanged += PowerStateChanged; } @@ -498,12 +498,12 @@ namespace Content.Server.GameObjects.Components.Disposal public override void OnRemove() { - if (Owner.TryGetComponent(out ICollidableComponent collidable)) + if (Owner.TryGetComponent(out ICollidableComponent? collidable)) { collidable.AnchoredChanged -= UpdateVisualState; } - if (Owner.TryGetComponent(out PowerReceiverComponent receiver)) + if (Owner.TryGetComponent(out PowerReceiverComponent? receiver)) { receiver.OnPowerStateChanged -= PowerStateChanged; } @@ -530,7 +530,7 @@ namespace Content.Server.GameObjects.Components.Disposal switch (message) { case RelayMovementEntityMessage msg: - if (!msg.Entity.TryGetComponent(out HandsComponent hands) || + if (!msg.Entity.TryGetComponent(out HandsComponent? hands) || hands.Count == 0 || _gameTiming.CurTime < _lastExitAttempt + ExitAttemptDelay) { @@ -559,7 +559,7 @@ namespace Content.Server.GameObjects.Components.Disposal return false; } - if (!eventArgs.User.TryGetComponent(out IActorComponent actor)) + if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) { return false; } diff --git a/Content.Server/GameObjects/Components/DoAfterComponent.cs b/Content.Server/GameObjects/Components/DoAfterComponent.cs index d77fc080d0..2013028ae6 100644 --- a/Content.Server/GameObjects/Components/DoAfterComponent.cs +++ b/Content.Server/GameObjects/Components/DoAfterComponent.cs @@ -60,7 +60,7 @@ namespace Content.Server.GameObjects.Components { connectedClient = null; - if (!Owner.TryGetComponent(out IActorComponent actorComponent)) + if (!Owner.TryGetComponent(out IActorComponent? actorComponent)) { return false; } diff --git a/Content.Server/GameObjects/Components/Fluids/SpillHelper.cs b/Content.Server/GameObjects/Components/Fluids/SpillHelper.cs index 5b85924b8d..e616a7f5f0 100644 --- a/Content.Server/GameObjects/Components/Fluids/SpillHelper.cs +++ b/Content.Server/GameObjects/Components/Fluids/SpillHelper.cs @@ -63,7 +63,7 @@ namespace Content.Server.GameObjects.Components.Fluids foreach (var spillEntity in entityManager.GetEntitiesAt(spillTileMapGrid.ParentMapId, spillGridCoords.Position)) { - if (!spillEntity.TryGetComponent(out PuddleComponent puddleComponent)) + if (!spillEntity.TryGetComponent(out PuddleComponent? puddleComponent)) { continue; } diff --git a/Content.Server/GameObjects/Components/GUI/HandsComponent.cs b/Content.Server/GameObjects/Components/GUI/HandsComponent.cs index 572e504f53..69fe0be734 100644 --- a/Content.Server/GameObjects/Components/GUI/HandsComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/HandsComponent.cs @@ -711,7 +711,7 @@ namespace Content.Server.GameObjects.Components.GUI Dirty(); - if (!message.Entity.TryGetComponent(out ICollidableComponent collidable)) + if (!message.Entity.TryGetComponent(out ICollidableComponent? collidable)) { return; } @@ -724,13 +724,13 @@ namespace Content.Server.GameObjects.Components.GUI private void AddPullingStatuses(IEntity pulled) { - if (pulled.TryGetComponent(out ServerStatusEffectsComponent pulledStatus)) + if (pulled.TryGetComponent(out ServerStatusEffectsComponent? pulledStatus)) { pulledStatus.ChangeStatusEffectIcon(StatusEffect.Pulled, "/Textures/Interface/StatusEffects/Pull/pulled.png"); } - if (Owner.TryGetComponent(out ServerStatusEffectsComponent ownerStatus)) + if (Owner.TryGetComponent(out ServerStatusEffectsComponent? ownerStatus)) { ownerStatus.ChangeStatusEffectIcon(StatusEffect.Pulling, "/Textures/Interface/StatusEffects/Pull/pulling.png"); @@ -739,12 +739,12 @@ namespace Content.Server.GameObjects.Components.GUI private void RemovePullingStatuses(IEntity pulled) { - if (pulled.TryGetComponent(out ServerStatusEffectsComponent pulledStatus)) + if (pulled.TryGetComponent(out ServerStatusEffectsComponent? pulledStatus)) { pulledStatus.RemoveStatusEffect(StatusEffect.Pulled); } - if (Owner.TryGetComponent(out ServerStatusEffectsComponent ownerStatus)) + if (Owner.TryGetComponent(out ServerStatusEffectsComponent? ownerStatus)) { ownerStatus.RemoveStatusEffect(StatusEffect.Pulling); } diff --git a/Content.Server/GameObjects/Components/Interactable/ToolCommands.cs b/Content.Server/GameObjects/Components/Interactable/ToolCommands.cs index 3008f11d44..9dd07ff951 100644 --- a/Content.Server/GameObjects/Components/Interactable/ToolCommands.cs +++ b/Content.Server/GameObjects/Components/Interactable/ToolCommands.cs @@ -106,7 +106,7 @@ namespace Content.Server.GameObjects.Components.Interactable foreach (var entity in entities) { - if (entity.TryGetComponent(out AnchorableComponent anchorable)) + if (entity.TryGetComponent(out AnchorableComponent? anchorable)) { anchorable.TryAnchor(player.AttachedEntity, force: true); } @@ -151,7 +151,7 @@ namespace Content.Server.GameObjects.Components.Interactable foreach (var entity in entities) { - if (entity.TryGetComponent(out AnchorableComponent anchorable)) + if (entity.TryGetComponent(out AnchorableComponent? anchorable)) { anchorable.TryUnAnchor(player.AttachedEntity, force: true); } diff --git a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs index 96aef95564..24272ed72c 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs @@ -101,13 +101,13 @@ namespace Content.Server.GameObjects.Components.Items.Storage { EnsureInitialCalculated(); - if (entity.TryGetComponent(out ServerStorageComponent storage) && + if (entity.TryGetComponent(out ServerStorageComponent? storage) && storage._storageCapacityMax >= _storageCapacityMax) { return false; } - if (entity.TryGetComponent(out StorableComponent store) && + if (entity.TryGetComponent(out StorableComponent? store) && store.ObjectSize > _storageCapacityMax - _storageUsed) { return false; @@ -164,7 +164,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage Logger.DebugS(LoggerName, $"Storage (UID {Owner.Uid}) had entity (UID {message.Entity.Uid}) removed from it."); - if (!message.Entity.TryGetComponent(out StorableComponent storable)) + if (!message.Entity.TryGetComponent(out StorableComponent? storable)) { Logger.WarningS(LoggerName, $"Removed entity {message.Entity.Uid} without a StorableComponent from storage {Owner.Uid} at {Owner.Transform.MapPosition}"); @@ -186,7 +186,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage { EnsureInitialCalculated(); - if (!player.TryGetComponent(out IHandsComponent hands) || + if (!player.TryGetComponent(out IHandsComponent? hands) || hands.GetActiveHand == null) { return false; @@ -317,7 +317,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage private void UpdateDoorState() { - if (Owner.TryGetComponent(out AppearanceComponent appearance)) + if (Owner.TryGetComponent(out AppearanceComponent? appearance)) { appearance.SetData(StorageVisuals.Open, SubscribedSessions.Count != 0); } @@ -382,7 +382,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage var item = entity.GetComponent(); if (item == null || - !player.TryGetComponent(out HandsComponent hands)) + !player.TryGetComponent(out HandsComponent? hands)) { break; } @@ -506,7 +506,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage bool IDragDrop.CanDragDrop(DragDropEventArgs eventArgs) { - return eventArgs.Target.TryGetComponent(out PlaceableSurfaceComponent placeable) && + return eventArgs.Target.TryGetComponent(out PlaceableSurfaceComponent? placeable) && placeable.IsPlaceable; } diff --git a/Content.Server/GameObjects/Components/Mobs/MindComponent.cs b/Content.Server/GameObjects/Components/Mobs/MindComponent.cs index fd2fed7f5f..b5f9eba7e1 100644 --- a/Content.Server/GameObjects/Components/Mobs/MindComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/MindComponent.cs @@ -79,7 +79,7 @@ namespace Content.Server.GameObjects.Components.Mobs var visiting = Mind?.VisitingEntity; if (visiting != null) { - if (visiting.TryGetComponent(out GhostComponent ghost)) + if (visiting.TryGetComponent(out GhostComponent? ghost)) { ghost.CanReturnToBody = false; } diff --git a/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs b/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs index 85bb04b84b..1b39598c2f 100644 --- a/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/ShuttleControllerComponent.cs @@ -71,7 +71,7 @@ namespace Content.Server.GameObjects.Components.Movement _entityManager.TryGetEntity(grid.GridEntityId, out var gridEntity)) { //TODO: Switch to shuttle component - if (!gridEntity.TryGetComponent(out ICollidableComponent collidable)) + if (!gridEntity.TryGetComponent(out ICollidableComponent? collidable)) { collidable = gridEntity.AddComponent(); collidable.Mass = 1; @@ -137,9 +137,9 @@ namespace Content.Server.GameObjects.Components.Movement private void SetController(IEntity entity) { if (_controller != null || - !entity.TryGetComponent(out MindComponent mind) || + !entity.TryGetComponent(out MindComponent? mind) || mind.Mind == null || - !Owner.TryGetComponent(out ServerStatusEffectsComponent status)) + !Owner.TryGetComponent(out ServerStatusEffectsComponent? status)) { return; } @@ -179,17 +179,17 @@ namespace Content.Server.GameObjects.Components.Movement /// The entity to update private void UpdateRemovedEntity(IEntity entity) { - if (Owner.TryGetComponent(out ServerStatusEffectsComponent status)) + if (Owner.TryGetComponent(out ServerStatusEffectsComponent? status)) { status.RemoveStatusEffect(StatusEffect.Piloting); } - if (entity.TryGetComponent(out MindComponent mind)) + if (entity.TryGetComponent(out MindComponent? mind)) { mind.Mind?.UnVisit(); } - if (entity.TryGetComponent(out BuckleComponent buckle)) + if (entity.TryGetComponent(out BuckleComponent? buckle)) { buckle.TryUnbuckle(entity, true); } diff --git a/Content.Server/GameObjects/Components/PDA/PDAComponent.cs b/Content.Server/GameObjects/Components/PDA/PDAComponent.cs index 91bc92bb3c..1aa9f2f776 100644 --- a/Content.Server/GameObjects/Components/PDA/PDAComponent.cs +++ b/Content.Server/GameObjects/Components/PDA/PDAComponent.cs @@ -164,7 +164,7 @@ namespace Content.Server.GameObjects.Components.PDA void IActivate.Activate(ActivateEventArgs eventArgs) { - if (!eventArgs.User.TryGetComponent(out IActorComponent actor)) + if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) { return; } @@ -175,7 +175,7 @@ namespace Content.Server.GameObjects.Components.PDA public bool UseEntity(UseEntityEventArgs eventArgs) { - if (!eventArgs.User.TryGetComponent(out IActorComponent actor)) + if (!eventArgs.User.TryGetComponent(out IActorComponent? actor)) { return false; } diff --git a/Content.Server/GameObjects/Components/Pointing/PointingArrowComponent.cs b/Content.Server/GameObjects/Components/Pointing/PointingArrowComponent.cs index 0c95a3d69f..caff655173 100644 --- a/Content.Server/GameObjects/Components/Pointing/PointingArrowComponent.cs +++ b/Content.Server/GameObjects/Components/Pointing/PointingArrowComponent.cs @@ -64,7 +64,7 @@ namespace Content.Server.GameObjects.Components.Pointing { base.Startup(); - if (Owner.TryGetComponent(out SpriteComponent sprite)) + if (Owner.TryGetComponent(out SpriteComponent? sprite)) { sprite.DrawDepth = (int) DrawDepth.Overlays; } diff --git a/Content.Server/GameObjects/Components/Pointing/RoguePointingArrowComponent.cs b/Content.Server/GameObjects/Components/Pointing/RoguePointingArrowComponent.cs index 35706da030..f20f1305d5 100644 --- a/Content.Server/GameObjects/Components/Pointing/RoguePointingArrowComponent.cs +++ b/Content.Server/GameObjects/Components/Pointing/RoguePointingArrowComponent.cs @@ -57,7 +57,7 @@ namespace Content.Server.GameObjects.Components.Pointing private void UpdateAppearance() { if (_chasing == null || - !Owner.TryGetComponent(out AppearanceComponent appearance)) + !Owner.TryGetComponent(out AppearanceComponent? appearance)) { return; } @@ -69,7 +69,7 @@ namespace Content.Server.GameObjects.Components.Pointing { base.Startup(); - if (Owner.TryGetComponent(out SpriteComponent sprite)) + if (Owner.TryGetComponent(out SpriteComponent? sprite)) { sprite.DrawDepth = (int) DrawDepth.Overlays; } diff --git a/Content.Server/GameObjects/Components/Rotatable/FlippableComponent.cs b/Content.Server/GameObjects/Components/Rotatable/FlippableComponent.cs index c986fe04dd..ad524e517c 100644 --- a/Content.Server/GameObjects/Components/Rotatable/FlippableComponent.cs +++ b/Content.Server/GameObjects/Components/Rotatable/FlippableComponent.cs @@ -24,7 +24,7 @@ namespace Content.Server.GameObjects.Components.Rotatable private void TryFlip(IEntity user) { - if (Owner.TryGetComponent(out ICollidableComponent collidable) && + if (Owner.TryGetComponent(out ICollidableComponent? collidable) && collidable.Anchored) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, user, Loc.GetString("It's stuck.")); diff --git a/Content.Server/GameObjects/Components/WiresComponent.cs b/Content.Server/GameObjects/Components/WiresComponent.cs index d8abb14bc7..6867052599 100644 --- a/Content.Server/GameObjects/Components/WiresComponent.cs +++ b/Content.Server/GameObjects/Components/WiresComponent.cs @@ -373,7 +373,7 @@ namespace Content.Server.GameObjects.Components return; } - if (!player.TryGetComponent(out IHandsComponent handsComponent)) + if (!player.TryGetComponent(out IHandsComponent? handsComponent)) { _notifyManager.PopupMessage(Owner.Transform.GridPosition, player, Loc.GetString("You have no hands.")); diff --git a/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs b/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs index 456bb14293..f1cb864dbd 100644 --- a/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs @@ -30,19 +30,19 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos [Robust.Shared.IoC.Dependency] private readonly IPlayerManager _playerManager = default!; [Robust.Shared.IoC.Dependency] private readonly IMapManager _mapManager = default!; [Robust.Shared.IoC.Dependency] private readonly IConfigurationManager _configManager = default!; - + /// /// The tiles that have had their atmos data updated since last tick /// private Dictionary> _invalidTiles = new Dictionary>(); - - private Dictionary _knownPlayerChunks = + + private Dictionary _knownPlayerChunks = new Dictionary(); - + /// /// Gas data stored in chunks to make PVS / bubbling easier. /// - private Dictionary> _overlay = + private Dictionary> _overlay = new Dictionary>(); /// @@ -52,7 +52,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos // Because the gas overlay updates aren't run every tick we need to avoid the pop-in that might occur with // the regular PVS range. private const float RangeOffset = 6.0f; - + /// /// Overlay update ticks per second. /// @@ -164,7 +164,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos var moles = tile.Air.Gases[i]; if (moles < gas.GasMolesVisible) continue; - + var data = new GasData(i, (byte) (FloatMath.Clamp01(moles / gas.GasMolesVisibleMax) * 255)); tileData.Add(data); } @@ -175,7 +175,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos { return false; } - + return true; } @@ -187,10 +187,10 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos private List GetChunksInRange(IEntity entity) { var inRange = new List(); - + // This is the max in any direction that we can get a chunk (e.g. max 2 chunks away of data). var (maxXDiff, maxYDiff) = ((int) (_updateRange / ChunkSize) + 1, (int) (_updateRange / ChunkSize) + 1); - + var worldBounds = Box2.CenteredAround(entity.Transform.WorldPosition, new Vector2(_updateRange, _updateRange)); @@ -202,7 +202,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos } var entityTile = grid.GetTileRef(entity.Transform.GridPosition).GridIndices; - + for (var x = -maxXDiff; x <= maxXDiff; x++) { for (var y = -maxYDiff; y <= maxYDiff; y++) @@ -210,7 +210,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos var chunkIndices = GetGasChunkIndices(new MapIndices(entityTile.X + x * ChunkSize, entityTile.Y + y * ChunkSize)); if (!chunks.TryGetValue(chunkIndices, out var chunk)) continue; - + // Now we'll check if it's in range and relevant for us // (e.g. if we're on the very edge of a chunk we may need more chunks). @@ -219,7 +219,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos yDiff > 0 && yDiff > _updateRange || xDiff < 0 && Math.Abs(xDiff + ChunkSize) > _updateRange || yDiff < 0 && Math.Abs(yDiff + ChunkSize) > _updateRange) continue; - + inRange.Add(chunk); } } @@ -237,25 +237,25 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos { return; } - + _updateRange = _configManager.GetCVar("net.maxupdaterange") + RangeOffset; - + // TODO: So in the worst case scenario we still have to send a LOT of tile data per tick if there's a fire. // If we go with say 15 tile radius then we have up to 900 tiles to update per tick. // In a saltern fire the worst you'll normally see is around 650 at the moment. // Need a way to fake this more because sending almost 2,000 tile updates per second to even 50 players is... yikes // I mean that's as big as it gets so larger maps will have the same but still, that's a lot of data. - + // Some ways to do this are potentially: splitting fire and gas update data so they don't update at the same time // (gives the illusion of more updates happening), e.g. if gas updates are 3 times a second and fires are 1.6 times a second or something. // Could also look at updating tiles close to us more frequently (e.g. within 1 chunk every tick). // Stuff just out of our viewport we need so when we move it doesn't pop in but it doesn't mean we need to update it every tick. - + AccumulatedFrameTime -= _updateCooldown; var gridAtmosComponents = new Dictionary(); var updatedTiles = new Dictionary>(); - + // So up to this point we've been caching the updated tiles for multiple ticks. // Now we'll go through and check whether the update actually matters for the overlay or not, // and if not then we won't bother sending the data. @@ -263,7 +263,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos { var gridEntityId = _mapManager.GetGrid(gridId).GridEntityId; - if (!EntityManager.GetEntity(gridEntityId).TryGetComponent(out GridAtmosphereComponent gam)) + if (!EntityManager.GetEntity(gridEntityId).TryGetComponent(out GridAtmosphereComponent? gam)) { continue; } @@ -286,7 +286,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos tiles = new HashSet(); updatedTiles[chunk] = tiles; } - + updatedTiles[chunk].Add(invalid); chunk.Update(data, invalid); } @@ -306,13 +306,13 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos foreach (var (session, overlay) in _knownPlayerChunks) { if (session.AttachedEntity == null) continue; - + // Get chunks in range and update if we've moved around or the chunks have new overlay data var chunksInRange = GetChunksInRange(session.AttachedEntity); var knownChunks = overlay.GetKnownChunks(); var chunksToRemove = new List(); var chunksToAdd = new List(); - + foreach (var chunk in chunksInRange) { if (!knownChunks.Contains(chunk)) @@ -328,7 +328,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos chunksToRemove.Add(chunk); } } - + foreach (var chunk in chunksToAdd) { var message = overlay.AddChunk(currentTick, chunk); @@ -342,7 +342,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos { overlay.RemoveChunk(chunk); } - + var clientInvalids = new Dictionary>(); // Check for any dirty chunks in range and bundle the data to send to the client. @@ -355,7 +355,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos existingData = new List<(MapIndices, GasOverlayData)>(); clientInvalids[chunk.GridIndices] = existingData; } - + chunk.GetData(existingData, invalids); } @@ -370,10 +370,10 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos } private sealed class PlayerGasOverlay { - private readonly Dictionary> _data = + private readonly Dictionary> _data = new Dictionary>(); - - private readonly Dictionary _lastSent = + + private readonly Dictionary _lastSent = new Dictionary(); public GasOverlayMessage UpdateClient(GridId grid, List<(MapIndices, GasOverlayData)> data) @@ -386,11 +386,11 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos _data.Clear(); _lastSent.Clear(); } - + public List GetKnownChunks() { var known = new List(); - + foreach (var (_, chunks) in _data) { foreach (var (_, chunk) in chunks) @@ -414,7 +414,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos { return null; } - + _lastSent[chunk] = currentTick; var message = ChunkToMessage(chunk); @@ -444,7 +444,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos { // Chunk data should already be up to date. // Only send relevant tiles to client. - + var tileData = new List<(MapIndices, GasOverlayData)>(); for (var x = 0; x < ChunkSize; x++) @@ -467,7 +467,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos { return null; } - + return new GasOverlayMessage(chunk.GridIndices, tileData); } } diff --git a/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs b/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs index 327104a148..a5ffa6e9ce 100644 --- a/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/AtmosphereSystem.cs @@ -34,7 +34,7 @@ namespace Content.Server.GameObjects.EntitySystems if (!EntityManager.TryGetEntity(grid.GridEntityId, out var gridEnt)) return null; - return gridEnt.TryGetComponent(out IGridAtmosphereComponent atmos) ? atmos : null; + return gridEnt.TryGetComponent(out IGridAtmosphereComponent? atmos) ? atmos : null; } public override void Update(float frameTime) diff --git a/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfter.cs b/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfter.cs index 3e00f334fd..2f45195624 100644 --- a/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfter.cs +++ b/Content.Server/GameObjects/EntitySystems/DoAfter/DoAfter.cs @@ -54,7 +54,7 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter // For this we need to stay on the same hand slot and need the same item in that hand slot // (or if there is no item there we need to keep it free). - if (eventArgs.NeedHand && eventArgs.User.TryGetComponent(out HandsComponent handsComponent)) + if (eventArgs.NeedHand && eventArgs.User.TryGetComponent(out HandsComponent? handsComponent)) { _activeHand = handsComponent.ActiveHand; _activeItem = handsComponent.GetActiveHand; @@ -126,7 +126,7 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter } if (EventArgs.BreakOnStun && - EventArgs.User.TryGetComponent(out StunnableComponent stunnableComponent) && + EventArgs.User.TryGetComponent(out StunnableComponent? stunnableComponent) && stunnableComponent.Stunned) { return true; @@ -134,7 +134,7 @@ namespace Content.Server.GameObjects.EntitySystems.DoAfter if (EventArgs.NeedHand) { - if (!EventArgs.User.TryGetComponent(out HandsComponent handsComponent)) + if (!EventArgs.User.TryGetComponent(out HandsComponent? handsComponent)) { // If we had a hand but no longer have it that's still a paddlin' if (_activeHand != null) diff --git a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs index 84250d4e91..41ded38a03 100644 --- a/Content.Server/GameObjects/EntitySystems/MoverSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/MoverSystem.cs @@ -83,7 +83,7 @@ namespace Content.Server.GameObjects.EntitySystems ev.Entity.RemoveComponent(); } - if (ev.Entity.TryGetComponent(out ICollidableComponent physics) && + if (ev.Entity.TryGetComponent(out ICollidableComponent? physics) && physics.TryGetController(out MoverController controller)) { controller.StopMoving(); diff --git a/Content.Server/GameObjects/EntitySystems/PointingSystem.cs b/Content.Server/GameObjects/EntitySystems/PointingSystem.cs index d27857954f..28efbd4f8a 100644 --- a/Content.Server/GameObjects/EntitySystems/PointingSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/PointingSystem.cs @@ -116,7 +116,7 @@ namespace Content.Server.GameObjects.EntitySystems var arrow = EntityManager.SpawnEntity("pointingarrow", coords); - if (player.TryGetComponent(out VisibilityComponent playerVisibility)) + if (player.TryGetComponent(out VisibilityComponent? playerVisibility)) { var arrowVisibility = arrow.EnsureComponent(); arrowVisibility.Layer = playerVisibility.Layer; diff --git a/Content.Shared/GameObjects/Components/Movement/SharedPlayerInputMoverComponent.cs b/Content.Shared/GameObjects/Components/Movement/SharedPlayerInputMoverComponent.cs index 6d47299c4d..a3b3794d3e 100644 --- a/Content.Shared/GameObjects/Components/Movement/SharedPlayerInputMoverComponent.cs +++ b/Content.Shared/GameObjects/Components/Movement/SharedPlayerInputMoverComponent.cs @@ -56,7 +56,7 @@ namespace Content.Shared.GameObjects.Components.Movement { get { - if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) + if (Owner.TryGetComponent(out MovementSpeedModifierComponent? component)) { return component.CurrentWalkSpeed; } @@ -69,7 +69,7 @@ namespace Content.Shared.GameObjects.Components.Movement { get { - if (Owner.TryGetComponent(out MovementSpeedModifierComponent component)) + if (Owner.TryGetComponent(out MovementSpeedModifierComponent? component)) { return component.CurrentSprintSpeed; } diff --git a/Content.Shared/GameObjects/EntitySystems/SharedMoverSystem.cs b/Content.Shared/GameObjects/EntitySystems/SharedMoverSystem.cs index 49f77ad735..57c66cb3f5 100644 --- a/Content.Shared/GameObjects/EntitySystems/SharedMoverSystem.cs +++ b/Content.Shared/GameObjects/EntitySystems/SharedMoverSystem.cs @@ -179,7 +179,7 @@ namespace Content.Shared.GameObjects.EntitySystems } private static bool TryGetAttachedComponent(ICommonSession? session, [MaybeNullWhen(false)] out T component) - where T : IComponent + where T : class, IComponent { component = default; @@ -188,7 +188,7 @@ namespace Content.Shared.GameObjects.EntitySystems if (ent == null || !ent.IsValid()) return false; - if (!ent.TryGetComponent(out T comp)) + if (!ent.TryGetComponent(out T? comp)) return false; component = comp; From de61a01703860fc448611e496ff0b579bf3b2db5 Mon Sep 17 00:00:00 2001 From: Visne <39844191+Visne@users.noreply.github.com> Date: Thu, 20 Aug 2020 16:59:59 +0200 Subject: [PATCH 29/53] Fix HasGameRule bug (#1819) --- Content.Server/GameTicking/GameTicker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 34d40c8df3..62d6dfa1a6 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -412,7 +412,7 @@ namespace Content.Server.GameTicking public bool HasGameRule(Type t) { - if (t == null || !t.IsAssignableFrom(typeof(GameRule))) + if (t == null || !typeof(GameRule).IsAssignableFrom(t)) return false; foreach (var rule in _gameRules) From 9e7d698145612ca02658e0ea218de4317db8bf6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= <6766154+Zumorica@users.noreply.github.com> Date: Thu, 20 Aug 2020 18:09:29 +0200 Subject: [PATCH 30/53] Allow specifying a text to be shown to players in the summary when ending the round. (#1818) * Allow specifying a text to be shown to players when ending the round. Also sets text * Fix comment --- .../GameTicking/ClientGameTicker.cs | 2 +- .../UserInterface/RoundEndSummaryWindow.cs | 10 +++++- Content.IntegrationTests/DummyGameTicker.cs | 2 +- .../GameTicking/GameRules/RuleSuspicion.cs | 36 +++++++++++++++---- Content.Server/GameTicking/GameTicker.cs | 3 +- .../Interfaces/GameTicking/IGameTicker.cs | 2 +- Content.Shared/SharedGameTicker.cs | 3 ++ 7 files changed, 47 insertions(+), 11 deletions(-) diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs index d618046255..1ae8bcd8a7 100644 --- a/Content.Client/GameTicking/ClientGameTicker.cs +++ b/Content.Client/GameTicking/ClientGameTicker.cs @@ -113,7 +113,7 @@ namespace Content.Client.GameTicking private void RoundEnd(MsgRoundEndMessage message) { //This is not ideal at all, but I don't see an immediately better fit anywhere else. - var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundDuration, message.AllPlayersEndInfo); + var roundEnd = new RoundEndSummaryWindow(message.GamemodeTitle, message.RoundEndText, message.RoundDuration, message.AllPlayersEndInfo); } } diff --git a/Content.Client/UserInterface/RoundEndSummaryWindow.cs b/Content.Client/UserInterface/RoundEndSummaryWindow.cs index 550c48032f..8d40c0b8ba 100644 --- a/Content.Client/UserInterface/RoundEndSummaryWindow.cs +++ b/Content.Client/UserInterface/RoundEndSummaryWindow.cs @@ -18,7 +18,7 @@ namespace Content.Client.UserInterface private TabContainer RoundEndWindowTabs { get; } protected override Vector2? CustomSize => (520, 580); - public RoundEndSummaryWindow(string gm, TimeSpan roundTimeSpan, List info) + public RoundEndSummaryWindow(string gm, string roundEnd, TimeSpan roundTimeSpan, List info) { Title = Loc.GetString("Round End Summary"); @@ -50,6 +50,14 @@ namespace Content.Client.UserInterface gamemodeLabel.SetMarkup(Loc.GetString("Round of [color=white]{0}[/color] has ended.", gm)); RoundEndSummaryTab.AddChild(gamemodeLabel); + //Round end text + if (!string.IsNullOrEmpty(roundEnd)) + { + var roundendLabel = new RichTextLabel(); + roundendLabel.SetMarkup(Loc.GetString(roundEnd)); + RoundEndSummaryTab.AddChild(roundendLabel); + } + //Duration var roundTimeLabel = new RichTextLabel(); roundTimeLabel.SetMarkup(Loc.GetString("It lasted for [color=yellow]{0} hours, {1} minutes, and {2} seconds.", diff --git a/Content.IntegrationTests/DummyGameTicker.cs b/Content.IntegrationTests/DummyGameTicker.cs index f135339c75..afe44ed8ff 100644 --- a/Content.IntegrationTests/DummyGameTicker.cs +++ b/Content.IntegrationTests/DummyGameTicker.cs @@ -41,7 +41,7 @@ namespace Content.IntegrationTests { } - public void EndRound() + public void EndRound(string roundEnd) { } diff --git a/Content.Server/GameTicking/GameRules/RuleSuspicion.cs b/Content.Server/GameTicking/GameRules/RuleSuspicion.cs index 553df745a2..026c11516a 100644 --- a/Content.Server/GameTicking/GameRules/RuleSuspicion.cs +++ b/Content.Server/GameTicking/GameRules/RuleSuspicion.cs @@ -1,5 +1,6 @@ using System; using System.Threading; +using Content.Server.GameObjects.Components.Suspicion; using Content.Server.Interfaces.Chat; using Content.Server.Interfaces.GameTicking; using Content.Server.Mobs.Roles; @@ -50,7 +51,8 @@ namespace Content.Server.GameTicking.GameRules foreach (var playerSession in _playerManager.GetAllPlayers()) { if (playerSession.AttachedEntity == null - || !playerSession.AttachedEntity.TryGetComponent(out IDamageableComponent damageable)) + || !playerSession.AttachedEntity.TryGetComponent(out IDamageableComponent damageable) + || !playerSession.AttachedEntity.TryGetComponent(out SuspicionRoleComponent suspicionRole)) { continue; } @@ -69,24 +71,46 @@ namespace Content.Server.GameTicking.GameRules if ((innocentsAlive + traitorsAlive) == 0) { _chatManager.DispatchServerAnnouncement("Everybody is dead, it's a stalemate!"); - EndRound(); + EndRound(Victory.Stalemate); } else if (traitorsAlive == 0) { _chatManager.DispatchServerAnnouncement("The traitors are dead! The innocents win."); - EndRound(); + EndRound(Victory.Innocents); } else if (innocentsAlive == 0) { _chatManager.DispatchServerAnnouncement("The innocents are dead! The traitors win."); - EndRound(); + EndRound(Victory.Traitors); } } - private void EndRound() + private enum Victory { - _gameTicker.EndRound(); + Stalemate, + Innocents, + Traitors + } + + private void EndRound(Victory victory) + { + string text; + + switch (victory) + { + case Victory.Innocents: + text = "The innocents have won!"; + break; + case Victory.Traitors: + text = "The traitors have won!"; + break; + default: + text = "Nobody wins!"; + break; + } + + _gameTicker.EndRound(text); _chatManager.DispatchServerAnnouncement($"Restarting in 10 seconds."); _checkTimerCancel.Cancel(); Timer.Spawn(TimeSpan.FromSeconds(10), () => _gameTicker.RestartRound()); diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 62d6dfa1a6..6ff0061c25 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -326,7 +326,7 @@ namespace Content.Server.GameTicking (HumanoidCharacterProfile) (await _prefsManager.GetPreferencesAsync(p.SessionId.Username)) .SelectedCharacter; - public void EndRound() + public void EndRound(string roundEndText = "") { DebugTools.Assert(RunLevel == GameRunLevel.InRound); Logger.InfoS("ticker", "Ending round!"); @@ -336,6 +336,7 @@ namespace Content.Server.GameTicking //Tell every client the round has ended. var roundEndMessage = _netManager.CreateNetMessage(); roundEndMessage.GamemodeTitle = MakeGamePreset(null).ModeTitle; + roundEndMessage.RoundEndText = roundEndText; //Get the timespan of the round. roundEndMessage.RoundDuration = IoCManager.Resolve().RealTime.Subtract(_roundStartTimeSpan); diff --git a/Content.Server/Interfaces/GameTicking/IGameTicker.cs b/Content.Server/Interfaces/GameTicking/IGameTicker.cs index 6ec11e8421..a4e045e2ee 100644 --- a/Content.Server/Interfaces/GameTicking/IGameTicker.cs +++ b/Content.Server/Interfaces/GameTicking/IGameTicker.cs @@ -22,7 +22,7 @@ namespace Content.Server.Interfaces.GameTicking void RestartRound(); void StartRound(bool force = false); - void EndRound(); + void EndRound(string roundEndText = ""); void Respawn(IPlayerSession targetPlayer); void MakeObserve(IPlayerSession player); diff --git a/Content.Shared/SharedGameTicker.cs b/Content.Shared/SharedGameTicker.cs index e180ddb198..d29cc0f84e 100644 --- a/Content.Shared/SharedGameTicker.cs +++ b/Content.Shared/SharedGameTicker.cs @@ -253,6 +253,7 @@ namespace Content.Shared #endregion public string GamemodeTitle; + public string RoundEndText; public TimeSpan RoundDuration; @@ -263,6 +264,7 @@ namespace Content.Shared public override void ReadFromBuffer(NetIncomingMessage buffer) { GamemodeTitle = buffer.ReadString(); + RoundEndText = buffer.ReadString(); var hours = buffer.ReadInt32(); var mins = buffer.ReadInt32(); @@ -289,6 +291,7 @@ namespace Content.Shared public override void WriteToBuffer(NetOutgoingMessage buffer) { buffer.Write(GamemodeTitle); + buffer.Write(RoundEndText); buffer.Write(RoundDuration.Hours); buffer.Write(RoundDuration.Minutes); buffer.Write(RoundDuration.Seconds); From 3203fdfb7ffd3aa6f2580409191a40c80dea8f92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= Date: Thu, 20 Aug 2020 18:18:42 +0200 Subject: [PATCH 31/53] Fix fallback preset DisallowLateJoin not being taken into account --- Content.Server/GameTicking/GameTicker.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 6ff0061c25..49fb6c7157 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -300,6 +300,9 @@ namespace Content.Server.GameTicking { throw new ApplicationException("Fallback preset failed to start!"); } + + DisallowLateJoin = false; + DisallowLateJoin |= newPreset.DisallowLateJoin; } _roundStartTimeSpan = IoCManager.Resolve().RealTime; From 496b456120b75b0e2da8d2dda95ff15d0abddbbf Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 18:46:46 +0200 Subject: [PATCH 32/53] Use in-memory SQLite prefs DB when UserData is virtual. --- Content.Server.Database/Configuration.cs | 23 +++++++++++++++---- .../Preferences/ServerPreferencesManager.cs | 8 +++---- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/Content.Server.Database/Configuration.cs b/Content.Server.Database/Configuration.cs index 1541b42ed8..6db9a594d3 100644 --- a/Content.Server.Database/Configuration.cs +++ b/Content.Server.Database/Configuration.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; using Npgsql; namespace Content.Server.Database @@ -50,9 +51,10 @@ namespace Content.Server.Database public class SqliteConfiguration : IDatabaseConfiguration { - private readonly string _databaseFilePath; + private readonly string? _databaseFilePath; - public SqliteConfiguration(string databaseFilePath) + /// If null, an in-memory database is used. + public SqliteConfiguration(string? databaseFilePath) { _databaseFilePath = databaseFilePath; } @@ -62,7 +64,20 @@ namespace Content.Server.Database get { var optionsBuilder = new DbContextOptionsBuilder(); - optionsBuilder.UseSqlite($"Data Source={_databaseFilePath}"); + SqliteConnection connection; + if (_databaseFilePath != null) + { + connection = new SqliteConnection($"Data Source={_databaseFilePath}"); + } + else + { + connection = new SqliteConnection("Data Source=:memory:"); + // When using an in-memory DB we have to open it manually + // so EFCore doesn't open, close and wipe it. + connection.Open(); + } + + optionsBuilder.UseSqlite(connection); return optionsBuilder.Options; } } diff --git a/Content.Server/Preferences/ServerPreferencesManager.cs b/Content.Server/Preferences/ServerPreferencesManager.cs index 9b10a7e725..ac9e944f56 100644 --- a/Content.Server/Preferences/ServerPreferencesManager.cs +++ b/Content.Server/Preferences/ServerPreferencesManager.cs @@ -50,11 +50,11 @@ namespace Content.Server.Preferences { case "sqlite": var configPreferencesDbPath = _configuration.GetCVar("database.prefs_sqlite_dbpath"); - var finalPreferencesDbPath = + var inMemory = _resourceManager.UserData.RootDir == null; + var finalPreferencesDbPath = inMemory ? + null : Path.Combine(_resourceManager.UserData.RootDir, configPreferencesDbPath); - dbConfig = new SqliteConfiguration( - finalPreferencesDbPath - ); + dbConfig = new SqliteConfiguration(finalPreferencesDbPath); break; case "postgres": dbConfig = new PostgresConfiguration( From 4cb00400452d95764a793f1afae7c2e7003765c9 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 18:47:16 +0200 Subject: [PATCH 33/53] Fix some UserData file system usage inconsistencies with map save tests. --- Content.IntegrationTests/Tests/SaveLoadMapTest.cs | 9 ++++++++- Content.IntegrationTests/Tests/SaveLoadSaveTest.cs | 10 +++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs index 05c3a6f243..e6c685285c 100644 --- a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs @@ -3,8 +3,11 @@ using NUnit.Framework; using Robust.Server.Interfaces.Maps; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; +using Robust.Shared.Interfaces.Resources; +using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Maths; +using Robust.Shared.Utility; namespace Content.IntegrationTests.Tests { @@ -14,7 +17,7 @@ namespace Content.IntegrationTests.Tests [Test] public async Task SaveLoadMultiGridMap() { - const string mapPath = @"Maps/Test/TestMap.yml"; + const string mapPath = @"/Maps/Test/TestMap.yml"; var server = StartServer(); await server.WaitIdleAsync(); @@ -24,6 +27,10 @@ namespace Content.IntegrationTests.Tests server.Post(() => { + var dir = new ResourcePath(mapPath).Directory; + IoCManager.Resolve() + .UserData.CreateDir(dir); + var mapId = mapManager.CreateMap(new MapId(5)); { diff --git a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs index d462beb279..cb4a8d3ea4 100644 --- a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs @@ -38,14 +38,14 @@ namespace Content.IntegrationTests.Tests string one; string two; - var rp1 = new ResourcePath("save load save 1.yml"); + var rp1 = new ResourcePath("/save load save 1.yml"); using (var stream = userData.Open(rp1, FileMode.Open)) using (var reader = new StreamReader(stream)) { one = reader.ReadToEnd(); } - var rp2 = new ResourcePath("save load save 2.yml"); + var rp2 = new ResourcePath("/save load save 2.yml"); using (var stream = userData.Open(rp2, FileMode.Open)) using (var reader = new StreamReader(stream)) { @@ -96,7 +96,7 @@ namespace Content.IntegrationTests.Tests server.Post(() => { - mapLoader.SaveBlueprint(grid.Index, "load save ticks save 2.yml"); + mapLoader.SaveBlueprint(grid.Index, "/load save ticks save 2.yml"); }); await server.WaitIdleAsync(); @@ -105,13 +105,13 @@ namespace Content.IntegrationTests.Tests string one; string two; - using (var stream = userData.Open(new ResourcePath("load save ticks save 1.yml"), FileMode.Open)) + using (var stream = userData.Open(new ResourcePath("/load save ticks save 1.yml"), FileMode.Open)) using (var reader = new StreamReader(stream)) { one = reader.ReadToEnd(); } - using (var stream = userData.Open(new ResourcePath("load save ticks save 2.yml"), FileMode.Open)) + using (var stream = userData.Open(new ResourcePath("/load save ticks save 2.yml"), FileMode.Open)) using (var reader = new StreamReader(stream)) { two = reader.ReadToEnd(); From f57269c5dc1cddab5ee3619cd13b7dc9b560b528 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 19:22:23 +0200 Subject: [PATCH 34/53] Update submodule. --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index d4fc0517c4..9d1bec9bb2 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit d4fc0517c4d7bc4c86416da065a2bcb37591a8c9 +Subproject commit 9d1bec9bb253f3de029e836d5d342073541d2b48 From 75a7223aa16035a5054d544c2ef46afef54e28db Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 19:23:16 +0200 Subject: [PATCH 35/53] Integration tests go brrrr --- Content.IntegrationTests/ContentIntegrationTest.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Content.IntegrationTests/ContentIntegrationTest.cs b/Content.IntegrationTests/ContentIntegrationTest.cs index 1970ba9ee7..023cf5fe0f 100644 --- a/Content.IntegrationTests/ContentIntegrationTest.cs +++ b/Content.IntegrationTests/ContentIntegrationTest.cs @@ -4,6 +4,7 @@ using Content.Client; using Content.Client.Interfaces.Parallax; using Content.Server; using Content.Server.Interfaces.GameTicking; +using NUnit.Framework; using Robust.Shared.ContentPack; using Robust.Shared.Interfaces.Network; using Robust.Shared.IoC; @@ -12,6 +13,7 @@ using EntryPoint = Content.Client.EntryPoint; namespace Content.IntegrationTests { + [Parallelizable(ParallelScope.All)] public abstract class ContentIntegrationTest : RobustIntegrationTest { protected sealed override ClientIntegrationInstance StartClient(ClientIntegrationOptions options = null) From ca5002981d026d50dc636e1f2b62af93ad05b9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= Date: Thu, 20 Aug 2020 20:21:25 +0200 Subject: [PATCH 36/53] Downgrade submodule. --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 9d1bec9bb2..31ff253fc0 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 9d1bec9bb253f3de029e836d5d342073541d2b48 +Subproject commit 31ff253fc04456bf750d33808e6ab581dfbca83a From 7575e143f64bc531617b5f6e9a65c1bfe4ecdc05 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 20:32:05 +0200 Subject: [PATCH 37/53] Update submodule again --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 31ff253fc0..0d7e6a8e1c 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 31ff253fc04456bf750d33808e6ab581dfbca83a +Subproject commit 0d7e6a8e1cbc4931ebfd939cdcbdd42d876ad89b From 8a2530e7a0b8a1819d16174d290e38865b2324dc Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 20:34:32 +0200 Subject: [PATCH 38/53] More FloatMath -> MathHelper from merge. --- Content.Server/AI/Utility/Considerations/Consideration.cs | 2 +- .../GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs | 2 +- Content.Server/GameTicking/GamePresets/PresetSuspicion.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Server/AI/Utility/Considerations/Consideration.cs b/Content.Server/AI/Utility/Considerations/Consideration.cs index 80860b07c1..e7212bf1dd 100644 --- a/Content.Server/AI/Utility/Considerations/Consideration.cs +++ b/Content.Server/AI/Utility/Considerations/Consideration.cs @@ -26,7 +26,7 @@ namespace Content.Server.AI.Utility.Considerations // Previously we used a makeupvalue method although the geometric mean is less punishing for more considerations var considerationsCount = context.GetState().GetValue(); var adjustedScore = MathF.Pow(score, 1 / (float) considerationsCount); - return FloatMath.Clamp(adjustedScore, 0.0f, 1.0f); + return MathHelper.Clamp(adjustedScore, 0.0f, 1.0f); } [Pure] diff --git a/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs b/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs index f1cb864dbd..d70331f71e 100644 --- a/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Atmos/GasTileOverlaySystem.cs @@ -165,7 +165,7 @@ namespace Content.Server.GameObjects.EntitySystems.Atmos if (moles < gas.GasMolesVisible) continue; - var data = new GasData(i, (byte) (FloatMath.Clamp01(moles / gas.GasMolesVisibleMax) * 255)); + var data = new GasData(i, (byte) (MathHelper.Clamp01(moles / gas.GasMolesVisibleMax) * 255)); tileData.Add(data); } diff --git a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs index c77b8a110e..379d639760 100644 --- a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs +++ b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs @@ -68,7 +68,7 @@ namespace Content.Server.GameTicking.GamePresets player.AttachedEntity?.EnsureComponent(); } - var numTraitors = FloatMath.Clamp(readyPlayers.Count / PlayersPerTraitor, + var numTraitors = MathHelper.Clamp(readyPlayers.Count / PlayersPerTraitor, MinTraitors, readyPlayers.Count); for (var i = 0; i < numTraitors; i++) From fc2ee61f755cd245a8cbc3fd73c93357038471ed Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 21:45:28 +0200 Subject: [PATCH 39/53] Fix verbs in-hand. Properly this time. --- .../GameObjects/EntitySystems/VerbSystem.cs | 14 ++++---- .../GameObjects/EntitySystems/VerbSystem.cs | 36 ++++--------------- .../GameObjects/Verbs/GlobalVerb.cs | 14 +------- Content.Shared/GameObjects/Verbs/Verb.cs | 15 +------- Content.Shared/GameObjects/Verbs/VerbBase.cs | 18 ++++++++++ .../GameObjects/Verbs/VerbUtility.cs | 30 ++++++++++++++++ 6 files changed, 63 insertions(+), 64 deletions(-) create mode 100644 Content.Shared/GameObjects/Verbs/VerbBase.cs diff --git a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs index f388263d65..486d491d1c 100644 --- a/Content.Client/GameObjects/EntitySystems/VerbSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/VerbSystem.cs @@ -207,11 +207,10 @@ namespace Content.Client.GameObjects.EntitySystems //Get verbs, component dependent. foreach (var (component, verb) in VerbUtility.GetVerbs(entity)) { - if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity)) - continue; - - if (verb.BlockedByContainers && !user.IsInSameOrNoContainer(entity)) + if (!VerbUtility.VerbAccessChecks(user, entity, verb)) + { continue; + } var verbData = verb.GetData(user, component); @@ -232,11 +231,10 @@ namespace Content.Client.GameObjects.EntitySystems //Get global verbs. Visible for all entities regardless of their components. foreach (var globalVerb in VerbUtility.GetGlobalVerbs(Assembly.GetExecutingAssembly())) { - if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(user, entity)) - continue; - - if (globalVerb.BlockedByContainers && !user.IsInSameOrNoContainer(entity)) + if (!VerbUtility.VerbAccessChecks(user, entity, globalVerb)) + { continue; + } var verbData = globalVerb.GetData(user, entity); diff --git a/Content.Server/GameObjects/EntitySystems/VerbSystem.cs b/Content.Server/GameObjects/EntitySystems/VerbSystem.cs index 761681b1ac..63775b3e44 100644 --- a/Content.Server/GameObjects/EntitySystems/VerbSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/VerbSystem.cs @@ -51,12 +51,7 @@ namespace Content.Server.GameObjects.EntitySystems continue; } - if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity)) - { - break; - } - - if (verb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity)) + if (!VerbUtility.VerbAccessChecks(userEntity, entity, verb)) { break; } @@ -72,13 +67,7 @@ namespace Content.Server.GameObjects.EntitySystems continue; } - if (globalVerb.RequireInteractionRange && - !VerbUtility.InVerbUseRange(userEntity, entity)) - { - break; - } - - if (globalVerb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity)) + if (!VerbUtility.VerbAccessChecks(userEntity, entity, globalVerb)) { break; } @@ -110,19 +99,9 @@ namespace Content.Server.GameObjects.EntitySystems //Get verbs, component dependent. foreach (var (component, verb) in VerbUtility.GetVerbs(entity)) { - if (verb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity)) - continue; - - if (verb.BlockedByContainers) + if (!VerbUtility.VerbAccessChecks(userEntity, entity, verb)) { - if (!userEntity.IsInSameOrNoContainer(entity)) - { - if (!ContainerHelpers.TryGetContainer(entity, out var container) || - container.Owner != userEntity) - { - continue; - } - } + continue; } var verbData = verb.GetData(userEntity, component); @@ -136,11 +115,10 @@ namespace Content.Server.GameObjects.EntitySystems //Get global verbs. Visible for all entities regardless of their components. foreach (var globalVerb in VerbUtility.GetGlobalVerbs(Assembly.GetExecutingAssembly())) { - if (globalVerb.RequireInteractionRange && !VerbUtility.InVerbUseRange(userEntity, entity)) - continue; - - if (globalVerb.BlockedByContainers && !userEntity.IsInSameOrNoContainer(entity)) + if (!VerbUtility.VerbAccessChecks(userEntity, entity, globalVerb)) + { continue; + } var verbData = globalVerb.GetData(userEntity, entity); if (verbData.IsInvisible) diff --git a/Content.Shared/GameObjects/Verbs/GlobalVerb.cs b/Content.Shared/GameObjects/Verbs/GlobalVerb.cs index e14830fab3..c4ff6bfe0c 100644 --- a/Content.Shared/GameObjects/Verbs/GlobalVerb.cs +++ b/Content.Shared/GameObjects/Verbs/GlobalVerb.cs @@ -12,20 +12,8 @@ namespace Content.Shared.GameObjects.Verbs /// To add a global verb to all entities, /// define it and mark it with /// - public abstract class GlobalVerb + public abstract class GlobalVerb : VerbBase { - /// - /// If true, this verb requires the user to be within - /// meters from the entity on which this verb resides. - /// - public virtual bool RequireInteractionRange => true; - - /// - /// If true, this verb requires both the user and the entity on which - /// this verb resides to be in the same container or no container. - /// - public virtual bool BlockedByContainers => true; - /// /// Gets the visible verb data for the user. /// diff --git a/Content.Shared/GameObjects/Verbs/Verb.cs b/Content.Shared/GameObjects/Verbs/Verb.cs index 723f90f1f3..89b1ac59c6 100644 --- a/Content.Shared/GameObjects/Verbs/Verb.cs +++ b/Content.Shared/GameObjects/Verbs/Verb.cs @@ -12,21 +12,8 @@ namespace Content.Shared.GameObjects.Verbs /// and mark it with /// [UsedImplicitly] - public abstract class Verb + public abstract class Verb : VerbBase { - /// - /// If true, this verb requires the user to be inside within - /// meters from the entity on which this verb resides. - /// - public virtual bool RequireInteractionRange => true; - - /// - /// If true, this verb requires both the user and the entity on which - /// this verb resides to be in the same container or no container. - /// OR the user can be the entity's container - /// - public virtual bool BlockedByContainers => true; - /// /// Gets the visible verb data for the user. /// diff --git a/Content.Shared/GameObjects/Verbs/VerbBase.cs b/Content.Shared/GameObjects/Verbs/VerbBase.cs new file mode 100644 index 0000000000..a1b6c21cd3 --- /dev/null +++ b/Content.Shared/GameObjects/Verbs/VerbBase.cs @@ -0,0 +1,18 @@ +namespace Content.Shared.GameObjects.Verbs +{ + public abstract class VerbBase + { + /// + /// If true, this verb requires the user to be inside within + /// meters from the entity on which this verb resides. + /// + public virtual bool RequireInteractionRange => true; + + /// + /// If true, this verb requires both the user and the entity on which + /// this verb resides to be in the same container or no container. + /// OR the user can be the entity's container + /// + public virtual bool BlockedByContainers => true; + } +} diff --git a/Content.Shared/GameObjects/Verbs/VerbUtility.cs b/Content.Shared/GameObjects/Verbs/VerbUtility.cs index 275a00330f..a876f7c11b 100644 --- a/Content.Shared/GameObjects/Verbs/VerbUtility.cs +++ b/Content.Shared/GameObjects/Verbs/VerbUtility.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using Robust.Shared.Containers; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Utility; @@ -50,6 +51,21 @@ namespace Content.Shared.GameObjects.Verbs } } + public static bool VerbAccessChecks(IEntity user, IEntity target, VerbBase verb) + { + if (verb.RequireInteractionRange && !InVerbUseRange(user, target)) + { + return false; + } + + if (verb.BlockedByContainers && !VerbContainerCheck(user, target)) + { + return false; + } + + return true; + } + public static bool InVerbUseRange(IEntity user, IEntity target) { var distanceSquared = (user.Transform.WorldPosition - target.Transform.WorldPosition) @@ -60,5 +76,19 @@ namespace Content.Shared.GameObjects.Verbs } return true; } + + public static bool VerbContainerCheck(IEntity user, IEntity target) + { + if (!user.IsInSameOrNoContainer(target)) + { + if (!ContainerHelpers.TryGetContainer(target, out var container) || + container.Owner != user) + { + return false; + } + } + + return true; + } } } From 3d75f0d7537d34e36d02a544fe5fcfb588d0f243 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 21:45:50 +0200 Subject: [PATCH 40/53] Update submodule. --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 0d7e6a8e1c..68d80cb136 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 0d7e6a8e1cbc4931ebfd939cdcbdd42d876ad89b +Subproject commit 68d80cb1367d4eb3b995a425974408ea9f079095 From 82c4c3eb4a7782f0eba31aac23386cb434c13d9e Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 22:04:55 +0200 Subject: [PATCH 41/53] Suspicion improvements: 1. pull settings from cvars. 2. Fix greet. Fixes #1823 --- .../GamePresets/PresetSuspicion.cs | 30 +++++++++++++-- Content.Server/GameTicking/GameTicker.cs | 2 + .../Mobs/Roles/SuspicionTraitorRole.cs | 38 +++++++++---------- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs index 379d639760..30bcfe89c8 100644 --- a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs +++ b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs @@ -13,6 +13,7 @@ using System.Linq; using Content.Server.GameObjects.Components.Suspicion; using Content.Shared.Roles; using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Log; using Robust.Shared.Maths; @@ -24,20 +25,32 @@ namespace Content.Server.GameTicking.GamePresets [Dependency] private readonly IChatManager _chatManager; [Dependency] private readonly IGameTicker _gameTicker; [Dependency] private readonly IRobustRandom _random; + [Dependency] private readonly IConfigurationManager _cfg; [Dependency] private IPrototypeManager _prototypeManager; #pragma warning restore 649 - public int MinPlayers { get; set; } = 5; - public int MinTraitors { get; set; } = 2; - public int PlayersPerTraitor { get; set; } = 5; + public int MinPlayers { get; set; } + public int MinTraitors { get; set; } + public int PlayersPerTraitor { get; set; } public override bool DisallowLateJoin => true; private static string TraitorID = "SuspicionTraitor"; private static string InnocentID = "SuspicionInnocent"; + public static void RegisterCVars(IConfigurationManager cfg) + { + cfg.RegisterCVar("game.suspicion_min_players", 5); + cfg.RegisterCVar("game.suspicion_min_traitors", 2); + cfg.RegisterCVar("game.suspicion_players_per_traitor", 5); + } + public override bool Start(IReadOnlyList readyPlayers, bool force = false) { + MinPlayers = _cfg.GetCVar("game.suspicion_min_players"); + MinTraitors = _cfg.GetCVar("game.suspicion_min_traitors"); + PlayersPerTraitor = _cfg.GetCVar("game.suspicion_players_per_traitor"); + if (!force && readyPlayers.Count < MinPlayers) { _chatManager.DispatchServerAnnouncement($"Not enough players readied up for the game! There were {readyPlayers.Count} players readied up out of {MinPlayers} needed."); @@ -71,6 +84,8 @@ namespace Content.Server.GameTicking.GamePresets var numTraitors = MathHelper.Clamp(readyPlayers.Count / PlayersPerTraitor, MinTraitors, readyPlayers.Count); + var traitors = new List(); + for (var i = 0; i < numTraitors; i++) { IPlayerSession traitor; @@ -87,7 +102,9 @@ namespace Content.Server.GameTicking.GamePresets } var mind = traitor.Data.ContentData().Mind; var antagPrototype = _prototypeManager.Index(TraitorID); - mind.AddRole(new SuspicionTraitorRole(mind, antagPrototype)); + var traitorRole = new SuspicionTraitorRole(mind, antagPrototype); + mind.AddRole(traitorRole); + traitors.Add(traitorRole); } foreach (var player in list) @@ -97,6 +114,11 @@ namespace Content.Server.GameTicking.GamePresets mind.AddRole(new SuspicionInnocentRole(mind, antagPrototype)); } + foreach (var traitor in traitors) + { + traitor.GreetSuspicion(traitors, _chatManager); + } + _gameTicker.AddGameRule(); return true; } diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 49fb6c7157..0df79eb51c 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -134,6 +134,8 @@ namespace Content.Server.GameTicking _configurationManager.RegisterCVar("game.defaultpreset", "Suspicion", CVar.ARCHIVE); _configurationManager.RegisterCVar("game.fallbackpreset", "Sandbox", CVar.ARCHIVE); + PresetSuspicion.RegisterCVars(_configurationManager); + _playerManager.PlayerStatusChanged += _handlePlayerStatusChanged; _netManager.RegisterNetMessage(nameof(MsgTickerJoinLobby)); diff --git a/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs b/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs index 5279fae886..8a834564fd 100644 --- a/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs +++ b/Content.Server/Mobs/Roles/SuspicionTraitorRole.cs @@ -1,9 +1,8 @@ -using Content.Server.GameObjects.Components.Suspicion; +using System.Collections.Generic; +using System.Linq; using Content.Server.Interfaces.Chat; using Content.Shared.Roles; -using Robust.Shared.GameObjects; -using Robust.Shared.Interfaces.GameObjects; -using Robust.Shared.IoC; +using Robust.Shared.Localization; namespace Content.Server.Mobs.Roles { @@ -22,26 +21,25 @@ namespace Content.Server.Mobs.Roles public string Objective => Prototype.Objective; public override bool Antagonist { get; } - public override void Greet() + public void GreetSuspicion(List traitors, IChatManager chatMgr) { - base.Greet(); + chatMgr.DispatchServerMessage(Mind.Session, Loc.GetString("You're a {0}!", Name)); + chatMgr.DispatchServerMessage(Mind.Session, Loc.GetString("Objective: {0}", Objective)); - var chat = IoCManager.Resolve(); - chat.DispatchServerMessage(Mind.Session, $"You're a {Name}!"); - chat.DispatchServerMessage(Mind.Session, $"Objective: {Objective}"); - - var traitors = ""; - - foreach (var sus in IoCManager.Resolve().EntityQuery()) + if (traitors.Count == 1) { - if (!sus.IsTraitor()) continue; - if (traitors.Length > 0) - traitors += $", {sus.Owner.Name}"; - else - traitors += sus.Owner.Name; + // Only traitor. + chatMgr.DispatchServerMessage(Mind.Session, Loc.GetString("You're on your own. Good luck!")); + return; } - - chat.DispatchServerMessage(Mind.Session, $"The traitors are: {traitors}"); + + var text = string.Join(", ", traitors.Where(p => p != this).Select(p => p.Mind.CharacterName)); + + var pluralText = Loc.GetPluralString("Your partner in crime is: {0}", + "Your partners in crime are: {0}", + traitors.Count-1, text); + + chatMgr.DispatchServerMessage(Mind.Session, pluralText); } } } From daf17e1c64bf9a98c5243ef3b85038cdb98aa6ee Mon Sep 17 00:00:00 2001 From: Exp Date: Thu, 20 Aug 2020 22:16:34 +0200 Subject: [PATCH 42/53] Fixed Exception when ReadyPlayers < MinTraitors --- Content.Server/GameTicking/GamePresets/PresetSuspicion.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs index 30bcfe89c8..f79abde6b2 100644 --- a/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs +++ b/Content.Server/GameTicking/GamePresets/PresetSuspicion.cs @@ -91,6 +91,11 @@ namespace Content.Server.GameTicking.GamePresets IPlayerSession traitor; if(prefList.Count == 0) { + if (list.Count == 0) + { + Logger.InfoS("preset", "Insufficient ready players to fill up with traitors, stopping the selection."); + break; + } traitor = _random.PickAndTake(list); Logger.InfoS("preset", "Insufficient preferred traitors, picking at random."); } From 0eba05c6041d61292d8254c42f1feb93187ba820 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 22:37:48 +0200 Subject: [PATCH 43/53] Fix exception in preferences preview. --- Content.Client/UserInterface/LobbyCharacterPreviewPanel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Content.Client/UserInterface/LobbyCharacterPreviewPanel.cs b/Content.Client/UserInterface/LobbyCharacterPreviewPanel.cs index b4dcdca7fb..1a4e40ac88 100644 --- a/Content.Client/UserInterface/LobbyCharacterPreviewPanel.cs +++ b/Content.Client/UserInterface/LobbyCharacterPreviewPanel.cs @@ -82,6 +82,8 @@ namespace Content.Client.UserInterface protected override void Dispose(bool disposing) { base.Dispose(disposing); + _preferencesManager.OnServerDataLoaded -= UpdateUI; + if (!disposing) return; _previewDummy.Delete(); _previewDummy = null; From f27ce22dc9df1f5e76540fcd2b6b2ce901328a13 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 22:38:00 +0200 Subject: [PATCH 44/53] Reduce airlock prying time. --- Content.Server/GameObjects/Components/Doors/AirlockComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs b/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs index 6f8f94d217..398e9230c9 100644 --- a/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs +++ b/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs @@ -418,7 +418,7 @@ namespace Content.Server.GameObjects.Components.Doors return true; } - if (!await tool.UseTool(eventArgs.User, Owner, 3f, ToolQuality.Prying, AirlockCheck)) return false; + if (!await tool.UseTool(eventArgs.User, Owner, 0.2f, ToolQuality.Prying, AirlockCheck)) return false; if (State == DoorState.Closed) Open(); From c2fc4d662811d8450575f3f82470dd07709a9cb5 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 22:43:59 +0200 Subject: [PATCH 45/53] Fix squeaking sounds. --- Resources/Prototypes/SoundCollections/toy_squeak.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Resources/Prototypes/SoundCollections/toy_squeak.yml b/Resources/Prototypes/SoundCollections/toy_squeak.yml index fa626f486c..d4675cc4ec 100644 --- a/Resources/Prototypes/SoundCollections/toy_squeak.yml +++ b/Resources/Prototypes/SoundCollections/toy_squeak.yml @@ -1,6 +1,6 @@ - type: soundCollection id: ToySqueak files: - - /Audio/Items/toys/toysqueak1.ogg - - /Audio/Items/toys/toysqueak2.ogg - - /Audio/Items/toys/toysqueak3.ogg + - /Audio/Items/Toys/toysqueak1.ogg + - /Audio/Items/Toys/toysqueak2.ogg + - /Audio/Items/Toys/toysqueak3.ogg From b1eb320b0a5fde48796378883d51c84bc9378e28 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Thu, 20 Aug 2020 23:44:35 +0200 Subject: [PATCH 46/53] Fix exception with explosions and storage items. --- .../Components/Items/Storage/ServerStorageComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs index 24272ed72c..8d6f4650ae 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs @@ -496,7 +496,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage foreach (var entity in storedEntities) { - var exActs = entity.GetAllComponents(); + var exActs = entity.GetAllComponents().ToArray(); foreach (var exAct in exActs) { exAct.OnExplosion(eventArgs); From ce1dc6d86b923a6946902c3951f5964b2bcd8162 Mon Sep 17 00:00:00 2001 From: Exp Date: Fri, 21 Aug 2020 00:08:16 +0200 Subject: [PATCH 47/53] Table Vaulting notify messages use the name (#1828) --- .../GameObjects/Components/Movement/ClimbableComponent.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs b/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs index 498f4afffb..413c8a169c 100644 --- a/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs @@ -174,8 +174,8 @@ namespace Content.Server.GameObjects.Components.Movement // we may potentially need additional logic since we're forcing a player onto a climbable // there's also the cases where the user might collide with the person they are forcing onto the climbable that i haven't accounted for - PopupMessageOtherClientsInRange(user, Loc.GetString("{0:them} forces {1:them} onto {2:theName}!", user, entityToMove, Owner), 15); - _notifyManager.PopupMessage(user, user, Loc.GetString("You force {0:them} onto {1:theName}!", entityToMove, Owner)); + PopupMessageOtherClientsInRange(user, Loc.GetString("{0} forces {1} onto {2:theName}!", user.Name, entityToMove.Name, Owner), 15); + _notifyManager.PopupMessage(user, user, Loc.GetString("You force {0} onto {1:theName}!", entityToMove.Name, Owner)); } } @@ -210,7 +210,7 @@ namespace Content.Server.GameObjects.Components.Movement climbMode.TryMoveTo(user.Transform.WorldPosition, endPoint); - PopupMessageOtherClientsInRange(user, Loc.GetString("{0:them} jumps onto {1:theName}!", user, Owner), 15); + PopupMessageOtherClientsInRange(user, Loc.GetString("{0} jumps onto {1:theName}!", user.Name, Owner), 15); _notifyManager.PopupMessage(user, user, Loc.GetString("You jump onto {0:theName}!", Owner)); } } From 8fe211bf882e6ddc7a6a7fe7a8b07add6a1f823a Mon Sep 17 00:00:00 2001 From: Exp Date: Fri, 21 Aug 2020 11:00:27 +0200 Subject: [PATCH 48/53] Make Table Vaulting use "theName" macro (#1832) --- .../GameObjects/Components/Movement/ClimbableComponent.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs b/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs index 413c8a169c..a6bb5ed2fb 100644 --- a/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs +++ b/Content.Server/GameObjects/Components/Movement/ClimbableComponent.cs @@ -174,8 +174,8 @@ namespace Content.Server.GameObjects.Components.Movement // we may potentially need additional logic since we're forcing a player onto a climbable // there's also the cases where the user might collide with the person they are forcing onto the climbable that i haven't accounted for - PopupMessageOtherClientsInRange(user, Loc.GetString("{0} forces {1} onto {2:theName}!", user.Name, entityToMove.Name, Owner), 15); - _notifyManager.PopupMessage(user, user, Loc.GetString("You force {0} onto {1:theName}!", entityToMove.Name, Owner)); + PopupMessageOtherClientsInRange(user, Loc.GetString("{0:theName} forces {1:theName} onto {2:theName}!", user, entityToMove, Owner), 15); + _notifyManager.PopupMessage(user, user, Loc.GetString("You force {0:theName} onto {1:theName}!", entityToMove, Owner)); } } @@ -210,7 +210,7 @@ namespace Content.Server.GameObjects.Components.Movement climbMode.TryMoveTo(user.Transform.WorldPosition, endPoint); - PopupMessageOtherClientsInRange(user, Loc.GetString("{0} jumps onto {1:theName}!", user.Name, Owner), 15); + PopupMessageOtherClientsInRange(user, Loc.GetString("{0:theName} jumps onto {1:theName}!", user, Owner), 15); _notifyManager.PopupMessage(user, user, Loc.GetString("You jump onto {0:theName}!", Owner)); } } From 02b5632dac574dd2032812cf7dba07790fea80bc Mon Sep 17 00:00:00 2001 From: Exp Date: Fri, 21 Aug 2020 11:24:06 +0200 Subject: [PATCH 49/53] Observers are now shown in the Lobby as Observers (#1834) * Observers are now shown in the Lobby as Observers * Weviews adwessed --- .../GameTicking/ClientGameTicker.cs | 10 +++--- .../Interfaces/IClientGameTicker.cs | 3 +- Content.Client/State/LobbyState.cs | 25 +++++++++---- Content.Server/GameTicking/GameTicker.cs | 36 ++++++++++--------- Content.Shared/SharedGameTicker.cs | 24 ++++++++----- 5 files changed, 60 insertions(+), 38 deletions(-) diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs index 1ae8bcd8a7..3eeda60421 100644 --- a/Content.Client/GameTicking/ClientGameTicker.cs +++ b/Content.Client/GameTicking/ClientGameTicker.cs @@ -30,7 +30,7 @@ namespace Content.Client.GameTicking [ViewVariables] public string ServerInfoBlob { get; private set; } [ViewVariables] public DateTime StartTime { get; private set; } [ViewVariables] public bool Paused { get; private set; } - [ViewVariables] public Dictionary Ready { get; private set; } + [ViewVariables] public Dictionary Status { get; private set; } public event Action InfoBlobUpdated; public event Action LobbyStatusUpdated; @@ -54,7 +54,7 @@ namespace Content.Client.GameTicking }); _netManager.RegisterNetMessage(nameof(MsgTickerLateJoinStatus), LateJoinStatus); - Ready = new Dictionary(); + Status = new Dictionary(); _initialized = true; } @@ -77,7 +77,7 @@ namespace Content.Client.GameTicking AreWeReady = message.YouAreReady; Paused = message.Paused; if (IsGameStarted) - Ready.Clear(); + Status.Clear(); LobbyStatusUpdated?.Invoke(); } @@ -103,9 +103,9 @@ namespace Content.Client.GameTicking private void LobbyReady(MsgTickerLobbyReady message) { // Merge the Dictionaries - foreach (var p in message.PlayerReady) + foreach (var p in message.PlayerStatus) { - Ready[p.Key] = p.Value; + Status[p.Key] = p.Value; } LobbyReadyUpdated?.Invoke(); } diff --git a/Content.Client/Interfaces/IClientGameTicker.cs b/Content.Client/Interfaces/IClientGameTicker.cs index e1e584e2f3..45e9a05faf 100644 --- a/Content.Client/Interfaces/IClientGameTicker.cs +++ b/Content.Client/Interfaces/IClientGameTicker.cs @@ -1,6 +1,7 @@ using Robust.Shared.Network; using System; using System.Collections.Generic; +using static Content.Shared.SharedGameTicker; namespace Content.Client.Interfaces { @@ -12,7 +13,7 @@ namespace Content.Client.Interfaces bool DisallowedLateJoin { get; } DateTime StartTime { get; } bool Paused { get; } - Dictionary Ready { get; } + Dictionary Status { get; } void Initialize(); event Action InfoBlobUpdated; diff --git a/Content.Client/State/LobbyState.cs b/Content.Client/State/LobbyState.cs index 2580dadb64..d3f756a0c8 100644 --- a/Content.Client/State/LobbyState.cs +++ b/Content.Client/State/LobbyState.cs @@ -18,6 +18,7 @@ using Robust.Shared.Localization; using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Robust.Shared.ViewVariables; +using static Content.Shared.SharedGameTicker; namespace Content.Client.State { @@ -112,7 +113,7 @@ namespace Content.Client.State _clientGameTicker.LobbyReadyUpdated -= LobbyReadyUpdated; _clientGameTicker.LobbyLateJoinStatusUpdated -= LobbyLateJoinStatusUpdated; - _clientGameTicker.Ready.Clear(); + _clientGameTicker.Status.Clear(); _lobby.Dispose(); _characterSetup.Dispose(); @@ -158,11 +159,14 @@ namespace Content.Client.State private void PlayerManagerOnPlayerListUpdated(object sender, EventArgs e) { // Remove disconnected sessions from the Ready Dict - foreach (var p in _clientGameTicker.Ready) + foreach (var p in _clientGameTicker.Status) { if (!_playerManager.SessionsDict.TryGetValue(p.Key, out _)) { - _clientGameTicker.Ready.Remove(p.Key); + // This is a shitty fix. Observers can rejoin because they are already in the game. + // So we don't delete them, but keep them if they decide to rejoin + if (p.Value != PlayerStatus.Observer) + _clientGameTicker.Status.Remove(p.Key); } } UpdatePlayerList(); @@ -218,12 +222,19 @@ namespace Content.Client.State // Don't show ready state if we're ingame if (!_clientGameTicker.IsGameStarted) { - var ready = false; + var status = PlayerStatus.NotReady; if (session.SessionId == _playerManager.LocalPlayer.SessionId) - ready = _clientGameTicker.AreWeReady; + status = _clientGameTicker.AreWeReady ? PlayerStatus.Ready : PlayerStatus.NotReady; else - _clientGameTicker.Ready.TryGetValue(session.SessionId, out ready); - readyState = ready ? Loc.GetString("Ready") : Loc.GetString("Not Ready"); + _clientGameTicker.Status.TryGetValue(session.SessionId, out status); + + readyState = status switch + { + PlayerStatus.NotReady => Loc.GetString("Not Ready"), + PlayerStatus.Ready => Loc.GetString("Ready"), + PlayerStatus.Observer => Loc.GetString("Observer"), + _ => "", + }; } _lobby.PlayerReadyList.AddItem(readyState, null, false); } diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 0df79eb51c..785ec372a0 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -80,9 +80,8 @@ namespace Content.Server.GameTicking [ViewVariables] private readonly List _gameRules = new List(); [ViewVariables] private readonly List _manifest = new List(); - // Value is whether they're ready. [ViewVariables] - private readonly Dictionary _playersInLobby = new Dictionary(); + private readonly Dictionary _playersInLobby = new Dictionary(); [ViewVariables] private bool _initialized; @@ -239,7 +238,7 @@ namespace Content.Server.GameTicking List readyPlayers; if (LobbyEnabled) { - readyPlayers = _playersInLobby.Where(p => p.Value).Select(p => p.Key).ToList(); + readyPlayers = _playersInLobby.Where(p => p.Value == PlayerStatus.Ready).Select(p => p.Key).ToList(); } else { @@ -386,6 +385,8 @@ namespace Content.Server.GameTicking if (!_playersInLobby.ContainsKey(player)) return; _spawnObserver(player); + _playersInLobby[player] = PlayerStatus.Observer; + _netManager.ServerSendToAll(GetStatusSingle(player, PlayerStatus.Observer)); } public void MakeJoinGame(IPlayerSession player, string jobId = null) @@ -399,9 +400,10 @@ namespace Content.Server.GameTicking { if (!_playersInLobby.ContainsKey(player)) return; - _playersInLobby[player] = ready; + var status = ready ? PlayerStatus.Ready : PlayerStatus.NotReady; + _playersInLobby[player] = ready ? PlayerStatus.Ready : PlayerStatus.NotReady; _netManager.ServerSendMessage(_getStatusMsg(player), player.ConnectedClient); - _netManager.ServerSendToAll(GetReadySingle(player, ready)); + _netManager.ServerSendToAll(GetStatusSingle(player, status)); } public T AddGameRule() where T : GameRule, new() @@ -893,13 +895,13 @@ namespace Content.Server.GameTicking private void _playerJoinLobby(IPlayerSession session) { - _playersInLobby.Add(session, false); + _playersInLobby.Add(session, PlayerStatus.NotReady); _prefsManager.OnClientConnected(session); _netManager.ServerSendMessage(_netManager.CreateNetMessage(), session.ConnectedClient); _netManager.ServerSendMessage(_getStatusMsg(session), session.ConnectedClient); _netManager.ServerSendMessage(GetInfoMsg(), session.ConnectedClient); - _netManager.ServerSendMessage(GetReadyStatus(), session.ConnectedClient); + _netManager.ServerSendMessage(GetPlayerStatus(), session.ConnectedClient); } private void _playerJoinGame(IPlayerSession session) @@ -911,33 +913,35 @@ namespace Content.Server.GameTicking _netManager.ServerSendMessage(_netManager.CreateNetMessage(), session.ConnectedClient); } - private MsgTickerLobbyReady GetReadyStatus() + private MsgTickerLobbyReady GetPlayerStatus() { var msg = _netManager.CreateNetMessage(); - msg.PlayerReady = new Dictionary(); + msg.PlayerStatus = new Dictionary(); foreach (var player in _playersInLobby.Keys) { - _playersInLobby.TryGetValue(player, out var ready); - msg.PlayerReady.Add(player.SessionId, ready); + _playersInLobby.TryGetValue(player, out var status); + msg.PlayerStatus.Add(player.SessionId, status); } return msg; } - private MsgTickerLobbyReady GetReadySingle(IPlayerSession player, bool ready) + private MsgTickerLobbyReady GetStatusSingle(IPlayerSession player, PlayerStatus status) { var msg = _netManager.CreateNetMessage(); - msg.PlayerReady = new Dictionary(); - msg.PlayerReady.Add(player.SessionId, ready); + msg.PlayerStatus = new Dictionary + { + { player.SessionId, status } + }; return msg; } private MsgTickerLobbyStatus _getStatusMsg(IPlayerSession session) { - _playersInLobby.TryGetValue(session, out var ready); + _playersInLobby.TryGetValue(session, out var status); var msg = _netManager.CreateNetMessage(); msg.IsRoundStarted = RunLevel != GameRunLevel.PreRoundLobby; msg.StartTime = _roundStartTimeUtc; - msg.YouAreReady = ready; + msg.YouAreReady = status == PlayerStatus.Ready; msg.Paused = Paused; return msg; } diff --git a/Content.Shared/SharedGameTicker.cs b/Content.Shared/SharedGameTicker.cs index d29cc0f84e..077f4c71b0 100644 --- a/Content.Shared/SharedGameTicker.cs +++ b/Content.Shared/SharedGameTicker.cs @@ -191,13 +191,13 @@ namespace Content.Shared #endregion /// - /// The Players Ready (SessionID:ready) + /// The Status of the Player in the lobby (ready, observer, ...) /// - public Dictionary PlayerReady { get; set; } + public Dictionary PlayerStatus { get; set; } public override void ReadFromBuffer(NetIncomingMessage buffer) { - PlayerReady = new Dictionary(); + PlayerStatus = new Dictionary(); var length = buffer.ReadInt32(); for (int i = 0; i < length; i++) { @@ -208,16 +208,16 @@ namespace Content.Shared { serializer.DeserializeDirect(stream, out sessionID); } - var ready = buffer.ReadBoolean(); - PlayerReady.Add(sessionID, ready); + var status = (PlayerStatus)buffer.ReadByte(); + PlayerStatus.Add(sessionID, status); } } public override void WriteToBuffer(NetOutgoingMessage buffer) { var serializer = IoCManager.Resolve(); - buffer.Write(PlayerReady.Count); - foreach (var p in PlayerReady) + buffer.Write(PlayerStatus.Count); + foreach (var p in PlayerStatus) { using (var stream = new MemoryStream()) { @@ -226,7 +226,7 @@ namespace Content.Shared stream.TryGetBuffer(out var segment); buffer.Write(segment); } - buffer.Write(p.Value); + buffer.Write((byte)p.Value); } } } @@ -238,7 +238,6 @@ namespace Content.Shared public string PlayerICName; public string Role; public bool Antag; - } protected class MsgRoundEndMessage : NetMessage @@ -308,6 +307,13 @@ namespace Content.Shared } } + + public enum PlayerStatus : byte + { + NotReady = 0, + Ready, + Observer, + } } } From c042b462202ea51a5204ee1067604eb8a0510534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Aguilera=20Puerto?= Date: Fri, 21 Aug 2020 13:32:04 +0200 Subject: [PATCH 50/53] Fix arrow visibility for ghosts. metacomming bad --- .../EntitySystems/PointingSystem.cs | 24 +++++++++++++++---- Content.Server/GameObjects/VisibilityFlags.cs | 1 + 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/Content.Server/GameObjects/EntitySystems/PointingSystem.cs b/Content.Server/GameObjects/EntitySystems/PointingSystem.cs index 28efbd4f8a..301d55db95 100644 --- a/Content.Server/GameObjects/EntitySystems/PointingSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/PointingSystem.cs @@ -2,10 +2,13 @@ using System; using System.Collections.Generic; using Content.Server.GameObjects.Components.Pointing; +using Content.Server.Players; +using Content.Server.Utility; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Input; using Content.Shared.Interfaces; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Diagnostics; using Robust.Server.GameObjects.Components; using Robust.Server.Interfaces.Player; using Robust.Server.Player; @@ -40,6 +43,8 @@ namespace Content.Server.GameObjects.EntitySystems /// private readonly Dictionary _pointers = new Dictionary(); + private const float PointingRange = 15f; + private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) { if (e.NewStatus != SessionStatus.Disconnected) @@ -79,7 +84,7 @@ namespace Content.Server.GameObjects.EntitySystems public bool TryPoint(ICommonSession? session, GridCoordinates coords, EntityUid uid) { - var player = session?.AttachedEntity; + var player = (session as IPlayerSession)?.ContentData().Mind.CurrentEntity; if (player == null) { return false; @@ -112,16 +117,27 @@ namespace Content.Server.GameObjects.EntitySystems } } - var viewers = _playerManager.GetPlayersInRange(player.Transform.GridPosition, 15); - var arrow = EntityManager.SpawnEntity("pointingarrow", coords); + var layer = (int)VisibilityFlags.Normal; if (player.TryGetComponent(out VisibilityComponent? playerVisibility)) { var arrowVisibility = arrow.EnsureComponent(); - arrowVisibility.Layer = playerVisibility.Layer; + layer = arrowVisibility.Layer = playerVisibility.Layer; } + // Get players that are in range and whose visibility layer matches the arrow's. + var viewers = _playerManager.GetPlayersBy((playerSession) => + { + if ((playerSession.VisibilityMask & layer) == 0) + return false; + + var ent = playerSession.ContentData().Mind.CurrentEntity; + + return ent != null + && ent.Transform.MapPosition.InRange(player.Transform.MapPosition, PointingRange); + }); + string selfMessage; string viewerMessage; string? viewerPointedAtMessage = null; diff --git a/Content.Server/GameObjects/VisibilityFlags.cs b/Content.Server/GameObjects/VisibilityFlags.cs index 4acb9c988a..bcd103ddfc 100644 --- a/Content.Server/GameObjects/VisibilityFlags.cs +++ b/Content.Server/GameObjects/VisibilityFlags.cs @@ -5,6 +5,7 @@ namespace Content.Server.GameObjects [Flags] public enum VisibilityFlags { + Normal = 1, Ghost = 2, } } From a9d8d8051dbde200c73be0be84c0fb84751508c9 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 21 Aug 2020 14:54:11 +0200 Subject: [PATCH 51/53] Update submodule. --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 68d80cb136..f0824212da 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 68d80cb1367d4eb3b995a425974408ea9f079095 +Subproject commit f0824212da5d913a51c8e40dee9a6622b9d5d1b7 From 80e175aaa90176f850224f804e28770781dc159e Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 21 Aug 2020 14:54:38 +0200 Subject: [PATCH 52/53] Fix deletion of inventories of dead people. --- .../Components/GUI/InventoryComponent.cs | 50 +++++++++++++++---- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs index 57ab715eb5..a0b86f0625 100644 --- a/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/InventoryComponent.cs @@ -10,6 +10,7 @@ using Content.Server.Interfaces.GameObjects; using Content.Shared.GameObjects.Components.Inventory; using Content.Shared.GameObjects.EntitySystems; using Robust.Server.GameObjects.Components.Container; +using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.GameObjects.Components; @@ -115,8 +116,14 @@ namespace Content.Server.GameObjects.Components.GUI public override void OnRemove() { var slots = _slotContainers.Keys.ToList(); + foreach (var slot in slots) { + if (TryGetSlotItem(slot, out ItemComponent item)) + { + item.Owner.Delete(); + } + RemoveSlot(slot); } @@ -267,17 +274,17 @@ namespace Content.Server.GameObjects.Components.GUI } var inventorySlot = _slotContainers[slot]; - var item = inventorySlot.ContainedEntity.GetComponent(); - if (!inventorySlot.Remove(inventorySlot.ContainedEntity)) + var entity = inventorySlot.ContainedEntity; + var item = entity.GetComponent(); + if (!inventorySlot.Remove(entity)) { return false; } // TODO: The item should be dropped to the container our owner is in, if any. - var itemTransform = item.Owner.GetComponent(); - itemTransform.GridPosition = Owner.GetComponent().GridPosition; + ContainerHelpers.AttachParentToContainerOrGrid(entity.Transform); - _entitySystemManager.GetEntitySystem().UnequippedInteraction(Owner, item.Owner, slot); + _entitySystemManager.GetEntitySystem().UnequippedInteraction(Owner, entity, slot); OnItemChanged?.Invoke(); @@ -286,6 +293,29 @@ namespace Content.Server.GameObjects.Components.GUI return true; } + public void ForceUnequip(Slots slot) + { + var inventorySlot = _slotContainers[slot]; + var entity = inventorySlot.ContainedEntity; + if (entity == null) + { + return; + } + + var item = entity.GetComponent(); + inventorySlot.ForceRemove(entity); + + var itemTransform = entity.Transform; + + ContainerHelpers.AttachParentToContainerOrGrid(itemTransform); + + _entitySystemManager.GetEntitySystem().UnequippedInteraction(Owner, item.Owner, slot); + + OnItemChanged?.Invoke(); + + Dirty(); + } + /// /// Checks whether an item can be dropped from the specified slot. /// @@ -340,13 +370,11 @@ namespace Content.Server.GameObjects.Components.GUI throw new InvalidOperationException($"Slow '{slot}' does not exist."); } - if (GetSlotItem(slot) != null && !Unequip(slot)) - { - // TODO: Handle this potential failiure better. - throw new InvalidOperationException( - "Unable to remove slot as the contained clothing could not be dropped"); - } + ForceUnequip(slot); + var container = _slotContainers[slot]; + + container.Shutdown(); _slotContainers.Remove(slot); OnItemChanged?.Invoke(); From 1f1e95f5351decadeae653c048974b565cde67b7 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Fri, 21 Aug 2020 14:54:50 +0200 Subject: [PATCH 53/53] Add integration test for entity deletion. --- .../Tests/DeleteInventoryTest.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 Content.IntegrationTests/Tests/DeleteInventoryTest.cs diff --git a/Content.IntegrationTests/Tests/DeleteInventoryTest.cs b/Content.IntegrationTests/Tests/DeleteInventoryTest.cs new file mode 100644 index 0000000000..4c3782b7fe --- /dev/null +++ b/Content.IntegrationTests/Tests/DeleteInventoryTest.cs @@ -0,0 +1,51 @@ +using System.Threading.Tasks; +using Content.Server.GameObjects.Components.GUI; +using Content.Server.GameObjects.Components.Items.Clothing; +using NUnit.Framework; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefines; + +namespace Content.IntegrationTests.Tests +{ + [TestFixture] + public class DeleteInventoryTest : ContentIntegrationTest + { + // Test that when deleting an entity with an InventoryComponent, + // any equipped items also get deleted. + [Test] + public async Task Test() + { + var server = StartServerDummyTicker(); + + server.Assert(() => + { + // Spawn everything. + var mapMan = IoCManager.Resolve(); + + mapMan.CreateNewMapEntity(MapId.Nullspace); + + var entMgr = IoCManager.Resolve(); + var container = entMgr.SpawnEntity(null, MapCoordinates.Nullspace); + var inv = container.AddComponent(); + + var child = entMgr.SpawnEntity(null, MapCoordinates.Nullspace); + var item = child.AddComponent(); + item.SlotFlags = SlotFlags.HEAD; + + // Equip item. + Assert.That(inv.Equip(Slots.HEAD, item, false), Is.True); + + // Delete parent. + container.Delete(); + + // Assert that child item was also deleted. + Assert.That(item.Deleted, Is.True); + }); + + await server.WaitIdleAsync(); + } + } +}