diff --git a/Content.Client/Alerts/ClientAlertsSystem.cs b/Content.Client/Alerts/ClientAlertsSystem.cs index 223bf7876ac..359c8957f9d 100644 --- a/Content.Client/Alerts/ClientAlertsSystem.cs +++ b/Content.Client/Alerts/ClientAlertsSystem.cs @@ -91,7 +91,7 @@ private void OnPlayerDetached(EntityUid uid, AlertsComponent component, LocalPla ClearAlerts?.Invoke(this, EventArgs.Empty); } - public void AlertClicked(AlertType alertType) + public void AlertClicked(ProtoId alertType) { RaiseNetworkEvent(new ClickAlertEvent(alertType)); } diff --git a/Content.Client/Buckle/BuckleSystem.cs b/Content.Client/Buckle/BuckleSystem.cs index d4614210d9f..4429996aca3 100644 --- a/Content.Client/Buckle/BuckleSystem.cs +++ b/Content.Client/Buckle/BuckleSystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Buckle.Components; using Content.Shared.Rotation; using Robust.Client.GameObjects; +using Robust.Shared.GameStates; namespace Content.Client.Buckle; @@ -14,40 +15,63 @@ public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnBuckleAfterAutoHandleState); + SubscribeLocalEvent(OnHandleState); SubscribeLocalEvent(OnAppearanceChange); + SubscribeLocalEvent(OnStrapMoveEvent); } - private void OnBuckleAfterAutoHandleState(EntityUid uid, BuckleComponent component, ref AfterAutoHandleStateEvent args) + private void OnStrapMoveEvent(EntityUid uid, StrapComponent component, ref MoveEvent args) { - ActionBlocker.UpdateCanMove(uid); + // I'm moving this to the client-side system, but for the sake of posterity let's keep this comment: + // > This is mega cursed. Please somebody save me from Mr Buckle's wild ride - if (!TryComp(uid, out var ownerSprite)) + // The nice thing is its still true, this is quite cursed, though maybe not omega cursed anymore. + // This code is garbage, it doesn't work with rotated viewports. I need to finally get around to reworking + // sprite rendering for entity layers & direction dependent sorting. + + if (args.NewRotation == args.OldRotation) return; - // Adjust draw depth when the chair faces north so that the seat back is drawn over the player. - // Reset the draw depth when rotated in any other direction. - // TODO when ECSing, make this a visualizer - // This code was written before rotatable viewports were introduced, so hard-coding Direction.North - // and comparing it against LocalRotation now breaks this in other rotations. This is a FIXME, but - // better to get it working for most people before we look at a more permanent solution. - if (component is { Buckled: true, LastEntityBuckledTo: { } } && - Transform(component.LastEntityBuckledTo.Value).LocalRotation.GetCardinalDir() == Direction.North && - TryComp(component.LastEntityBuckledTo, out var buckledSprite)) - { - component.OriginalDrawDepth ??= ownerSprite.DrawDepth; - ownerSprite.DrawDepth = buckledSprite.DrawDepth - 1; + if (!TryComp(uid, out var strapSprite)) return; - } - // If here, we're not turning north and should restore the saved draw depth. - if (component.OriginalDrawDepth.HasValue) + var isNorth = Transform(uid).LocalRotation.GetCardinalDir() == Direction.North; + foreach (var buckledEntity in component.BuckledEntities) { - ownerSprite.DrawDepth = component.OriginalDrawDepth.Value; - component.OriginalDrawDepth = null; + if (!TryComp(buckledEntity, out var buckle)) + continue; + + if (!TryComp(buckledEntity, out var buckledSprite)) + continue; + + if (isNorth) + { + buckle.OriginalDrawDepth ??= buckledSprite.DrawDepth; + buckledSprite.DrawDepth = strapSprite.DrawDepth - 1; + } + else if (buckle.OriginalDrawDepth.HasValue) + { + buckledSprite.DrawDepth = buckle.OriginalDrawDepth.Value; + buckle.OriginalDrawDepth = null; + } } } + private void OnHandleState(Entity ent, ref ComponentHandleState args) + { + if (args.Current is not BuckleState state) + return; + + ent.Comp.DontCollide = state.DontCollide; + ent.Comp.BuckleTime = state.BuckleTime; + var strapUid = EnsureEntity(state.BuckledTo, ent); + + SetBuckledTo(ent, strapUid == null ? null : new (strapUid.Value, null)); + + var (uid, component) = ent; + + } + private void OnAppearanceChange(EntityUid uid, BuckleComponent component, ref AppearanceChangeEvent args) { if (!TryComp(uid, out var rotVisuals) diff --git a/Content.Client/Clickable/ClickableComponent.cs b/Content.Client/Clickable/ClickableComponent.cs index 6f75df46830..da81ed4c841 100644 --- a/Content.Client/Clickable/ClickableComponent.cs +++ b/Content.Client/Clickable/ClickableComponent.cs @@ -1,145 +1,17 @@ -using System.Numerics; -using Robust.Client.GameObjects; -using Robust.Client.Graphics; -using Robust.Client.Utility; -using Robust.Shared.Graphics; -using static Robust.Client.GameObjects.SpriteComponent; -using Direction = Robust.Shared.Maths.Direction; +namespace Content.Client.Clickable; -namespace Content.Client.Clickable +[RegisterComponent] +public sealed partial class ClickableComponent : Component { - [RegisterComponent] - public sealed partial class ClickableComponent : Component - { - [Dependency] private readonly IClickMapManager _clickMapManager = default!; - - [DataField("bounds")] public DirBoundData? Bounds; - - /// - /// Used to check whether a click worked. Will first check if the click falls inside of some explicit bounding - /// boxes (see ). If that fails, attempts to use automatically generated click maps. - /// - /// The world position that was clicked. - /// - /// The draw depth for the sprite that captured the click. - /// - /// True if the click worked, false otherwise. - public bool CheckClick(SpriteComponent sprite, TransformComponent transform, EntityQuery xformQuery, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom) - { - if (!sprite.Visible) - { - drawDepth = default; - renderOrder = default; - bottom = default; - return false; - } - - drawDepth = sprite.DrawDepth; - renderOrder = sprite.RenderOrder; - var (spritePos, spriteRot) = transform.GetWorldPositionRotation(xformQuery); - var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye.Rotation); - bottom = Matrix3Helpers.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom; - - Matrix3x2.Invert(sprite.GetLocalMatrix(), out var invSpriteMatrix); - - // This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites. - var relativeRotation = (spriteRot + eye.Rotation).Reduced().FlipPositive(); - - Angle cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero; - - // First we get `localPos`, the clicked location in the sprite-coordinate frame. - var entityXform = Matrix3Helpers.CreateInverseTransform(transform.WorldPosition, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping); - var localPos = Vector2.Transform(Vector2.Transform(worldPos, entityXform), invSpriteMatrix); - - // Check explicitly defined click-able bounds - if (CheckDirBound(sprite, relativeRotation, localPos)) - return true; - - // Next check each individual sprite layer using automatically computed click maps. - foreach (var spriteLayer in sprite.AllLayers) - { - if (!spriteLayer.Visible || spriteLayer is not Layer layer) - continue; - - // Check the layer's texture, if it has one - if (layer.Texture != null) - { - // Convert to image coordinates - var imagePos = (Vector2i) (localPos * EyeManager.PixelsPerMeter * new Vector2(1, -1) + layer.Texture.Size / 2f); - - if (_clickMapManager.IsOccluding(layer.Texture, imagePos)) - return true; - } - - // Either we weren't clicking on the texture, or there wasn't one. In which case: check the RSI next - if (layer.ActualRsi is not { } rsi || !rsi.TryGetState(layer.State, out var rsiState)) - continue; - - var dir = Layer.GetDirection(rsiState.RsiDirections, relativeRotation); + [DataField] public DirBoundData? Bounds; - // convert to layer-local coordinates - layer.GetLayerDrawMatrix(dir, out var matrix); - Matrix3x2.Invert(matrix, out var inverseMatrix); - var layerLocal = Vector2.Transform(localPos, inverseMatrix); - - // Convert to image coordinates - var layerImagePos = (Vector2i) (layerLocal * EyeManager.PixelsPerMeter * new Vector2(1, -1) + rsiState.Size / 2f); - - // Next, to get the right click map we need the "direction" of this layer that is actually being used to draw the sprite on the screen. - // This **can** differ from the dir defined before, but can also just be the same. - if (sprite.EnableDirectionOverride) - dir = sprite.DirectionOverride.Convert(rsiState.RsiDirections); - dir = dir.OffsetRsiDir(layer.DirOffset); - - if (_clickMapManager.IsOccluding(layer.ActualRsi!, layer.State, dir, layer.AnimationFrame, layerImagePos)) - return true; - } - - drawDepth = default; - renderOrder = default; - bottom = default; - return false; - } - - public bool CheckDirBound(SpriteComponent sprite, Angle relativeRotation, Vector2 localPos) - { - if (Bounds == null) - return false; - - // These explicit bounds only work for either 1 or 4 directional sprites. - - // This would be the orientation of a 4-directional sprite. - var direction = relativeRotation.GetCardinalDir(); - - var modLocalPos = sprite.NoRotation - ? localPos - : direction.ToAngle().RotateVec(localPos); - - // First, check the bounding box that is valid for all orientations - if (Bounds.All.Contains(modLocalPos)) - return true; - - // Next, get and check the appropriate bounding box for the current sprite orientation - var boundsForDir = (sprite.EnableDirectionOverride ? sprite.DirectionOverride : direction) switch - { - Direction.East => Bounds.East, - Direction.North => Bounds.North, - Direction.South => Bounds.South, - Direction.West => Bounds.West, - _ => throw new InvalidOperationException() - }; - - return boundsForDir.Contains(modLocalPos); - } - - [DataDefinition] - public sealed partial class DirBoundData - { - [DataField("all")] public Box2 All; - [DataField("north")] public Box2 North; - [DataField("south")] public Box2 South; - [DataField("east")] public Box2 East; - [DataField("west")] public Box2 West; - } + [DataDefinition] + public sealed partial class DirBoundData + { + [DataField] public Box2 All; + [DataField] public Box2 North; + [DataField] public Box2 South; + [DataField] public Box2 East; + [DataField] public Box2 West; } } diff --git a/Content.Client/Clickable/ClickableSystem.cs b/Content.Client/Clickable/ClickableSystem.cs new file mode 100644 index 00000000000..15d13df625c --- /dev/null +++ b/Content.Client/Clickable/ClickableSystem.cs @@ -0,0 +1,168 @@ +using System.Numerics; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; +using Robust.Client.Utility; +using Robust.Shared.Graphics; + +namespace Content.Client.Clickable; + +/// +/// Handles click detection for sprites. +/// +public sealed class ClickableSystem : EntitySystem +{ + [Dependency] private readonly IClickMapManager _clickMapManager = default!; + [Dependency] private readonly SharedTransformSystem _transforms = default!; + [Dependency] private readonly SpriteSystem _sprites = default!; + + private EntityQuery _clickableQuery; + private EntityQuery _xformQuery; + + public override void Initialize() + { + base.Initialize(); + _clickableQuery = GetEntityQuery(); + _xformQuery = GetEntityQuery(); + } + + /// + /// Used to check whether a click worked. Will first check if the click falls inside of some explicit bounding + /// boxes (see ). If that fails, attempts to use automatically generated click maps. + /// + /// The world position that was clicked. + /// + /// The draw depth for the sprite that captured the click. + /// + /// True if the click worked, false otherwise. + public bool CheckClick(Entity entity, Vector2 worldPos, IEye eye, out int drawDepth, out uint renderOrder, out float bottom) + { + if (!_clickableQuery.Resolve(entity.Owner, ref entity.Comp1, false)) + { + drawDepth = default; + renderOrder = default; + bottom = default; + return false; + } + + if (!_xformQuery.Resolve(entity.Owner, ref entity.Comp3)) + { + drawDepth = default; + renderOrder = default; + bottom = default; + return false; + } + + var sprite = entity.Comp2; + var transform = entity.Comp3; + + if (!sprite.Visible) + { + drawDepth = default; + renderOrder = default; + bottom = default; + return false; + } + + drawDepth = sprite.DrawDepth; + renderOrder = sprite.RenderOrder; + var (spritePos, spriteRot) = _transforms.GetWorldPositionRotation(transform); + var spriteBB = sprite.CalculateRotatedBoundingBox(spritePos, spriteRot, eye.Rotation); + bottom = Matrix3Helpers.CreateRotation(eye.Rotation).TransformBox(spriteBB).Bottom; + + Matrix3x2.Invert(sprite.GetLocalMatrix(), out var invSpriteMatrix); + + // This should have been the rotation of the sprite relative to the screen, but this is not the case with no-rot or directional sprites. + var relativeRotation = (spriteRot + eye.Rotation).Reduced().FlipPositive(); + + var cardinalSnapping = sprite.SnapCardinals ? relativeRotation.GetCardinalDir().ToAngle() : Angle.Zero; + + // First we get `localPos`, the clicked location in the sprite-coordinate frame. + var entityXform = Matrix3Helpers.CreateInverseTransform(spritePos, sprite.NoRotation ? -eye.Rotation : spriteRot - cardinalSnapping); + var localPos = Vector2.Transform(Vector2.Transform(worldPos, entityXform), invSpriteMatrix); + + // Check explicitly defined click-able bounds + if (CheckDirBound((entity.Owner, entity.Comp1, entity.Comp2), relativeRotation, localPos)) + return true; + + // Next check each individual sprite layer using automatically computed click maps. + foreach (var spriteLayer in sprite.AllLayers) + { + if (spriteLayer is not SpriteComponent.Layer layer || !_sprites.IsVisible(layer)) + { + continue; + } + + // Check the layer's texture, if it has one + if (layer.Texture != null) + { + // Convert to image coordinates + var imagePos = (Vector2i) (localPos * EyeManager.PixelsPerMeter * new Vector2(1, -1) + layer.Texture.Size / 2f); + + if (_clickMapManager.IsOccluding(layer.Texture, imagePos)) + return true; + } + + // Either we weren't clicking on the texture, or there wasn't one. In which case: check the RSI next + if (layer.ActualRsi is not { } rsi || !rsi.TryGetState(layer.State, out var rsiState)) + continue; + + var dir = SpriteComponent.Layer.GetDirection(rsiState.RsiDirections, relativeRotation); + + // convert to layer-local coordinates + layer.GetLayerDrawMatrix(dir, out var matrix); + Matrix3x2.Invert(matrix, out var inverseMatrix); + var layerLocal = Vector2.Transform(localPos, inverseMatrix); + + // Convert to image coordinates + var layerImagePos = (Vector2i) (layerLocal * EyeManager.PixelsPerMeter * new Vector2(1, -1) + rsiState.Size / 2f); + + // Next, to get the right click map we need the "direction" of this layer that is actually being used to draw the sprite on the screen. + // This **can** differ from the dir defined before, but can also just be the same. + if (sprite.EnableDirectionOverride) + dir = sprite.DirectionOverride.Convert(rsiState.RsiDirections); + dir = dir.OffsetRsiDir(layer.DirOffset); + + if (_clickMapManager.IsOccluding(layer.ActualRsi!, layer.State, dir, layer.AnimationFrame, layerImagePos)) + return true; + } + + drawDepth = default; + renderOrder = default; + bottom = default; + return false; + } + + public bool CheckDirBound(Entity entity, Angle relativeRotation, Vector2 localPos) + { + var clickable = entity.Comp1; + var sprite = entity.Comp2; + + if (clickable.Bounds == null) + return false; + + // These explicit bounds only work for either 1 or 4 directional sprites. + + // This would be the orientation of a 4-directional sprite. + var direction = relativeRotation.GetCardinalDir(); + + var modLocalPos = sprite.NoRotation + ? localPos + : direction.ToAngle().RotateVec(localPos); + + // First, check the bounding box that is valid for all orientations + if (clickable.Bounds.All.Contains(modLocalPos)) + return true; + + // Next, get and check the appropriate bounding box for the current sprite orientation + var boundsForDir = (sprite.EnableDirectionOverride ? sprite.DirectionOverride : direction) switch + { + Direction.East => clickable.Bounds.East, + Direction.North => clickable.Bounds.North, + Direction.South => clickable.Bounds.South, + Direction.West => clickable.Bounds.West, + _ => throw new InvalidOperationException() + }; + + return boundsForDir.Contains(modLocalPos); + } +} diff --git a/Content.Client/Explosion/ExplosionOverlay.cs b/Content.Client/Explosion/ExplosionOverlay.cs index 8cf7447a5d8..f8f8ae53623 100644 --- a/Content.Client/Explosion/ExplosionOverlay.cs +++ b/Content.Client/Explosion/ExplosionOverlay.cs @@ -29,6 +29,7 @@ public ExplosionOverlay() protected override void Draw(in OverlayDrawArgs args) { + var appearanceSystem = _entMan.System(); var drawHandle = args.WorldHandle; drawHandle.UseShader(_shader); @@ -41,7 +42,7 @@ protected override void Draw(in OverlayDrawArgs args) if (visuals.Epicenter.MapId != args.MapId) continue; - if (!appearance.TryGetData(ExplosionAppearanceData.Progress, out int index)) + if (!appearanceSystem.TryGetData(appearance.Owner, ExplosionAppearanceData.Progress, out int index)) continue; index = Math.Min(index, visuals.Intensity.Count - 1); diff --git a/Content.Client/Gameplay/GameplayStateBase.cs b/Content.Client/Gameplay/GameplayStateBase.cs index 6236cd8e958..315c053d935 100644 --- a/Content.Client/Gameplay/GameplayStateBase.cs +++ b/Content.Client/Gameplay/GameplayStateBase.cs @@ -2,6 +2,7 @@ using System.Numerics; using Content.Client.Clickable; using Content.Client.UserInterface; +using Content.Client.Viewport; using Content.Shared.Input; using Robust.Client.ComponentTrees; using Robust.Client.GameObjects; @@ -13,11 +14,13 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.Console; +using Robust.Shared.Graphics; using Robust.Shared.Input; using Robust.Shared.Input.Binding; using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Timing; +using YamlDotNet.Serialization.TypeInspectors; namespace Content.Client.Gameplay { @@ -98,7 +101,15 @@ private bool HandleInspect(ICommonSession? session, EntityCoordinates coords, En public EntityUid? GetClickedEntity(MapCoordinates coordinates) { - var first = GetClickableEntities(coordinates).FirstOrDefault(); + return GetClickedEntity(coordinates, _eyeManager.CurrentEye); + } + + public EntityUid? GetClickedEntity(MapCoordinates coordinates, IEye? eye) + { + if (eye == null) + return null; + + var first = GetClickableEntities(coordinates, eye).FirstOrDefault(); return first.IsValid() ? first : null; } @@ -109,6 +120,20 @@ public IEnumerable GetClickableEntities(EntityCoordinates coordinates public IEnumerable GetClickableEntities(MapCoordinates coordinates) { + return GetClickableEntities(coordinates, _eyeManager.CurrentEye); + } + + public IEnumerable GetClickableEntities(MapCoordinates coordinates, IEye? eye) + { + /* + * TODO: + * 1. Stuff like MeleeWeaponSystem need an easy way to hook into viewport specific entities / entities under mouse + * 2. Cleanup the mess around InteractionOutlineSystem + below the keybind click detection. + */ + + if (eye == null) + return Array.Empty(); + // Find all the entities intersecting our click var spriteTree = _entityManager.EntitySysManager.GetEntitySystem(); var entities = spriteTree.QueryAabb(coordinates.MapId, Box2.CenteredAround(coordinates.Position, new Vector2(1, 1))); @@ -116,15 +141,12 @@ public IEnumerable GetClickableEntities(MapCoordinates coordinates) // Check the entities against whether or not we can click them var foundEntities = new List<(EntityUid, int, uint, float)>(entities.Count); var clickQuery = _entityManager.GetEntityQuery(); - var xformQuery = _entityManager.GetEntityQuery(); - - // TODO: Smelly - var eye = _eyeManager.CurrentEye; + var clickables = _entityManager.System(); foreach (var entity in entities) { if (clickQuery.TryGetComponent(entity.Uid, out var component) && - component.CheckClick(entity.Component, entity.Transform, xformQuery, coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom)) + clickables.CheckClick((entity.Uid, component, entity.Component, entity.Transform), coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom)) { foundEntities.Add((entity.Uid, drawDepthClicked, renderOrder, bottom)); } @@ -187,7 +209,15 @@ protected virtual void OnKeyBindStateChanged(ViewportBoundKeyEventArgs args) if (args.Viewport is IViewportControl vp) { var mousePosWorld = vp.PixelToMap(kArgs.PointerLocation.Position); - entityToClick = GetClickedEntity(mousePosWorld); + + if (vp is ScalingViewport svp) + { + entityToClick = GetClickedEntity(mousePosWorld, svp.Eye); + } + else + { + entityToClick = GetClickedEntity(mousePosWorld); + } coordinates = _mapManager.TryFindGridAt(mousePosWorld, out _, out var grid) ? grid.MapToGrid(mousePosWorld) : diff --git a/Content.Client/Movement/Systems/WaddleAnimationSystem.cs b/Content.Client/Movement/Systems/WaddleAnimationSystem.cs index 9555c1f6b9e..9b45e445fbb 100644 --- a/Content.Client/Movement/Systems/WaddleAnimationSystem.cs +++ b/Content.Client/Movement/Systems/WaddleAnimationSystem.cs @@ -32,7 +32,7 @@ public override void Initialize() SubscribeLocalEvent(OnAnimationCompleted); SubscribeLocalEvent(OnStunned); SubscribeLocalEvent(OnKnockedDown); - SubscribeLocalEvent(OnBuckleChange); + SubscribeLocalEvent(OnBuckled); } private void OnMovementInput(EntityUid entity, WaddleAnimationComponent component, MoveInputEvent args) @@ -148,7 +148,7 @@ private void OnKnockedDown(EntityUid uid, WaddleAnimationComponent component, Kn StopWaddling(uid, component); } - private void OnBuckleChange(EntityUid uid, WaddleAnimationComponent component, BuckleChangeEvent args) + private void OnBuckled(EntityUid uid, WaddleAnimationComponent component, BuckledEvent args) { StopWaddling(uid, component); } diff --git a/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs index 97fea3d0ca9..c5f0123bddb 100644 --- a/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs +++ b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs @@ -11,6 +11,7 @@ namespace Content.Client.Kitchen.Visualizers { public sealed class DeepFriedVisualizerSystem : VisualizerSystem { + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; private readonly static string ShaderName = "Crispy"; public override void Initialize() @@ -26,7 +27,7 @@ protected override void OnAppearanceChange(EntityUid uid, DeepFriedComponent com if (args.Sprite == null) return; - if (!args.Component.TryGetData(DeepFriedVisuals.Fried, out bool isFried)) + if (!_appearanceSystem.TryGetData(uid, DeepFriedVisuals.Fried, out bool isFried)) return; for (var i = 0; i < args.Sprite.AllLayers.Count(); ++i) diff --git a/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs index 5d208d09598..ae7a89d6869 100644 --- a/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs +++ b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs @@ -9,9 +9,11 @@ namespace Content.Client.Kitchen.Visualizers { public sealed class DeepFryerVisualizerSystem : VisualizerSystem { + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; + protected override void OnAppearanceChange(EntityUid uid, DeepFryerComponent component, ref AppearanceChangeEvent args) { - if (!args.Component.TryGetData(DeepFryerVisuals.Bubbling, out bool isBubbling) || + if (!_appearanceSystem.TryGetData(uid, DeepFryerVisuals.Bubbling, out bool isBubbling) || !TryComp(uid, out var scvComponent)) { return; diff --git a/Content.Client/Nyanotrasen/Mail/MailSystem.cs b/Content.Client/Nyanotrasen/Mail/MailSystem.cs index c8d764a22f4..de63d74099b 100644 --- a/Content.Client/Nyanotrasen/Mail/MailSystem.cs +++ b/Content.Client/Nyanotrasen/Mail/MailSystem.cs @@ -28,13 +28,14 @@ public sealed class MailJobVisualizerSystem : VisualizerSystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SpriteSystem _stateManager = default!; [Dependency] private readonly SpriteSystem _spriteSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; protected override void OnAppearanceChange(EntityUid uid, MailComponent component, ref AppearanceChangeEvent args) { if (args.Sprite == null) return; - args.Component.TryGetData(MailVisuals.JobIcon, out string job); + _appearanceSystem.TryGetData(uid, MailVisuals.JobIcon, out string job); if (string.IsNullOrEmpty(job)) job = "JobIconUnknown"; diff --git a/Content.Client/Outline/InteractionOutlineSystem.cs b/Content.Client/Outline/InteractionOutlineSystem.cs index 3dbbafbcaa3..40cb5dfd4a6 100644 --- a/Content.Client/Outline/InteractionOutlineSystem.cs +++ b/Content.Client/Outline/InteractionOutlineSystem.cs @@ -110,11 +110,15 @@ public override void FrameUpdate(float frameTime) && _inputManager.MouseScreenPosition.IsValid) { var mousePosWorld = vp.PixelToMap(_inputManager.MouseScreenPosition.Position); - entityToClick = screen.GetClickedEntity(mousePosWorld); if (vp is ScalingViewport svp) { renderScale = svp.CurrentRenderScale; + entityToClick = screen.GetClickedEntity(mousePosWorld, svp.Eye); + } + else + { + entityToClick = screen.GetClickedEntity(mousePosWorld); } } else if (_uiManager.CurrentlyHovered is EntityMenuElement element) diff --git a/Content.Client/Revenant/RevenantSystem.cs b/Content.Client/Revenant/RevenantSystem.cs index 49d29d8a5f4..e050fe35aa2 100644 --- a/Content.Client/Revenant/RevenantSystem.cs +++ b/Content.Client/Revenant/RevenantSystem.cs @@ -1,5 +1,4 @@ using Content.Client.Alerts; -using Content.Shared.Alert; using Content.Shared.Revenant; using Content.Shared.Revenant.Components; using Robust.Client.GameObjects; @@ -42,7 +41,7 @@ private void OnAppearanceChange(EntityUid uid, RevenantComponent component, ref private void OnUpdateAlert(Entity ent, ref UpdateAlertSpriteEvent args) { - if (args.Alert.AlertType != AlertType.Essence) + if (args.Alert.ID != ent.Comp.EssenceAlert) return; var sprite = args.SpriteViewEnt.Comp; diff --git a/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs b/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs index 3b85972a9b2..5c195120389 100644 --- a/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs +++ b/Content.Client/UserInterface/Systems/Alerts/AlertsUIController.cs @@ -7,6 +7,7 @@ using Robust.Client.Player; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; +using Robust.Shared.Prototypes; namespace Content.Client.UserInterface.Systems.Alerts; @@ -43,7 +44,7 @@ private void OnScreenLoad() SyncAlerts(); } - private void OnAlertPressed(object? sender, AlertType e) + private void OnAlertPressed(object? sender, ProtoId e) { _alertsSystem?.AlertClicked(e); } diff --git a/Content.Client/UserInterface/Systems/Alerts/Widgets/AlertsUI.xaml.cs b/Content.Client/UserInterface/Systems/Alerts/Widgets/AlertsUI.xaml.cs index a1a494c47b3..d6a79a81c46 100644 --- a/Content.Client/UserInterface/Systems/Alerts/Widgets/AlertsUI.xaml.cs +++ b/Content.Client/UserInterface/Systems/Alerts/Widgets/AlertsUI.xaml.cs @@ -4,6 +4,7 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Input; +using Robust.Shared.Prototypes; namespace Content.Client.UserInterface.Systems.Alerts.Widgets; @@ -21,8 +22,10 @@ public AlertsUI() RobustXamlLoader.Load(this); } - public void SyncControls(AlertsSystem alertsSystem, AlertOrderPrototype? alertOrderPrototype, - IReadOnlyDictionary alertStates) + public void SyncControls(AlertsSystem alertsSystem, + AlertOrderPrototype? alertOrderPrototype, + IReadOnlyDictionary alertStates) { // remove any controls with keys no longer present if (SyncRemoveControls(alertStates)) @@ -46,7 +49,7 @@ public void ClearAllControls() _alertControls.Clear(); } - public event EventHandler? AlertPressed; + public event EventHandler>? AlertPressed; private bool SyncRemoveControls(IReadOnlyDictionary alertStates) { @@ -88,7 +91,7 @@ private void SyncUpdateControls(AlertsSystem alertsSystem, AlertOrderPrototype? } if (_alertControls.TryGetValue(newAlert.AlertKey, out var existingAlertControl) && - existingAlertControl.Alert.AlertType == newAlert.AlertType) + existingAlertControl.Alert.ID == newAlert.ID) { // key is the same, simply update the existing control severity / cooldown existingAlertControl.SetSeverity(alertState.Severity); @@ -155,6 +158,6 @@ private void AlertControlPressed(BaseButton.ButtonEventArgs args) if (args.Event.Function != EngineKeyFunctions.UIClick) return; - AlertPressed?.Invoke(this, control.Alert.AlertType); + AlertPressed?.Invoke(this, control.Alert.ID); } } diff --git a/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs b/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs index f436cc8c39b..dd9986e4c6e 100644 --- a/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs +++ b/Content.Client/UserInterface/Systems/Storage/Controls/ItemGridPiece.cs @@ -9,7 +9,7 @@ namespace Content.Client.UserInterface.Systems.Storage.Controls; -public sealed class ItemGridPiece : Control +public sealed class ItemGridPiece : Control, IEntityControl { private readonly IEntityManager _entityManager; private readonly StorageUIController _storageController; @@ -287,6 +287,8 @@ public static Vector2 GetCenterOffset(Entity entity, ItemStorage var actualSize = new Vector2(boxSize.X + 1, boxSize.Y + 1); return actualSize * new Vector2i(8, 8); } + + public EntityUid? UiEntity => Entity; } public enum ItemGridPieceMarks diff --git a/Content.Client/Weather/WeatherSystem.cs b/Content.Client/Weather/WeatherSystem.cs index 24de0bc8c4c..b35483bba48 100644 --- a/Content.Client/Weather/WeatherSystem.cs +++ b/Content.Client/Weather/WeatherSystem.cs @@ -124,9 +124,9 @@ protected override void Run(EntityUid uid, WeatherData weather, WeatherPrototype comp.Occlusion = occlusion; } - protected override bool SetState(WeatherState state, WeatherComponent comp, WeatherData weather, WeatherPrototype weatherProto) + protected override bool SetState(EntityUid uid, WeatherState state, WeatherComponent comp, WeatherData weather, WeatherPrototype weatherProto) { - if (!base.SetState(state, comp, weather, weatherProto)) + if (!base.SetState(uid, state, comp, weather, weatherProto)) return false; if (!Timing.IsFirstTimePredicted) @@ -164,7 +164,7 @@ private void OnWeatherHandleState(EntityUid uid, WeatherComponent component, ref continue; // New weather - StartWeather(component, ProtoMan.Index(proto), weather.EndTime); + StartWeather(uid, component, ProtoMan.Index(proto), weather.EndTime); } } } diff --git a/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs b/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs index 420a90a50bd..45addff00bf 100644 --- a/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs +++ b/Content.IntegrationTests/Tests/Actions/ActionPvsDetachTest.cs @@ -32,11 +32,14 @@ public async Task TestActionDetach() // PVS-detach action entities // We do this by just giving them the ghost layer var visSys = server.System(); - var enumerator = server.Transform(ent).ChildEnumerator; - while (enumerator.MoveNext(out var child)) + server.Post(() => { - visSys.AddLayer(child, (int) VisibilityFlags.Ghost); - } + var enumerator = server.Transform(ent).ChildEnumerator; + while (enumerator.MoveNext(out var child)) + { + visSys.AddLayer(child, (int) VisibilityFlags.Ghost); + } + }); await pair.RunTicksSync(5); // Client's actions have left been detached / are out of view, but action comp state has not changed @@ -44,11 +47,14 @@ public async Task TestActionDetach() Assert.That(cSys.GetActions(cEnt).Count(), Is.EqualTo(initActions)); // Re-enter PVS view - enumerator = server.Transform(ent).ChildEnumerator; - while (enumerator.MoveNext(out var child)) + server.Post(() => { - visSys.RemoveLayer(child, (int) VisibilityFlags.Ghost); - } + var enumerator = server.Transform(ent).ChildEnumerator; + while (enumerator.MoveNext(out var child)) + { + visSys.RemoveLayer(child, (int) VisibilityFlags.Ghost); + } + }); await pair.RunTicksSync(5); Assert.That(sys.GetActions(ent).Count(), Is.EqualTo(initActions)); Assert.That(cSys.GetActions(cEnt).Count(), Is.EqualTo(initActions)); diff --git a/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs b/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs index 32b15252261..c232e823132 100644 --- a/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs +++ b/Content.IntegrationTests/Tests/Actions/ActionsAddedTest.cs @@ -18,7 +18,7 @@ public sealed class ActionsAddedTest [Test] public async Task TestCombatActionsAdded() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false}); + await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false }); var server = pair.Server; var client = pair.Client; var sEntMan = server.ResolveDependency(); diff --git a/Content.IntegrationTests/Tests/Announcers/AnnouncerAudioTest.cs b/Content.IntegrationTests/Tests/Announcers/AnnouncerAudioTest.cs index 87e8caf6ecd..a15163b574b 100644 --- a/Content.IntegrationTests/Tests/Announcers/AnnouncerAudioTest.cs +++ b/Content.IntegrationTests/Tests/Announcers/AnnouncerAudioTest.cs @@ -1,76 +1,76 @@ -using System.Collections.Generic; -using System.Linq; -using Content.Server.Announcements.Systems; -using Content.Server.StationEvents; -using Content.Shared.Announcements.Prototypes; -using Robust.Client.ResourceManagement; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC.Exceptions; -using Robust.Shared.Localization; -using Robust.Shared.Prototypes; -using Serilog; +// using System.Collections.Generic; +// using System.Linq; +// using Content.Server.Announcements.Systems; +// using Content.Server.StationEvents; +// using Content.Shared.Announcements.Prototypes; +// using Robust.Client.ResourceManagement; +// using Robust.Shared.GameObjects; +// using Robust.Shared.IoC.Exceptions; +// using Robust.Shared.Localization; +// using Robust.Shared.Prototypes; +// using Serilog; -namespace Content.IntegrationTests.Tests.Announcers; +// namespace Content.IntegrationTests.Tests.Announcers; -/// -/// Checks if every station event using the announcerSystem has a valid audio file associated with it -/// -[TestFixture] -[TestOf(typeof(AnnouncerPrototype))] -public sealed class AnnouncerAudioTest -{ - /// - [Test] - public async Task TestEventSounds() - { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); - var server = pair.Server; - var client = pair.Client; +// /// +// /// Checks if every station event using the announcerSystem has a valid audio file associated with it +// /// +// [TestFixture] +// [TestOf(typeof(AnnouncerPrototype))] +// public sealed class AnnouncerAudioTest +// { +// /// +// [Test] +// public async Task TestEventSounds() +// { +// await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true }); +// var server = pair.Server; +// var client = pair.Client; - var entSysMan = server.ResolveDependency(); - var proto = server.ResolveDependency(); - var cache = client.ResolveDependency(); - var announcer = entSysMan.GetEntitySystem(); - var events = entSysMan.GetEntitySystem(); +// var entSysMan = server.ResolveDependency(); +// var proto = server.ResolveDependency(); +// var cache = client.ResolveDependency(); +// var announcer = entSysMan.GetEntitySystem(); +// var events = entSysMan.GetEntitySystem(); - await server.WaitAssertion(() => - { - var succeeded = true; - var why = new List(); +// await server.WaitAssertion(() => +// { +// var succeeded = true; +// var why = new List(); - foreach (var announcerProto in proto.EnumeratePrototypes().OrderBy(a => a.ID)) - { - foreach (var ev in events.AllEvents()) - { - if (ev.Value.StartAnnouncement) - { - var announcementId = announcer.GetAnnouncementId(ev.Key.ID); - var path = announcer.GetAnnouncementPath(announcementId, announcerProto); +// foreach (var announcerProto in proto.EnumeratePrototypes().OrderBy(a => a.ID)) +// { +// foreach (var ev in events.AllEvents()) +// { +// if (ev.Value.StartAnnouncement != null) +// { +// var announcementId = ev.Value.StartAnnouncement ?? announcer.GetAnnouncementId(ev.Key.ID); +// var path = announcer.GetAnnouncementPath(announcementId, announcerProto); - if (!cache.ContentFileExists(path)) - { - succeeded = false; - why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{path}\""); - } - } +// if (!cache.ContentFileExists(path)) +// { +// succeeded = false; +// why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{path}\""); +// } +// } - if (ev.Value.EndAnnouncement) - { - var announcementId = announcer.GetAnnouncementId(ev.Key.ID, true); - var path = announcer.GetAnnouncementPath(announcementId, announcerProto); +// if (ev.Value.EndAnnouncement != null) +// { +// var announcementId = ev.Value.StartAnnouncement ?? announcer.GetAnnouncementId(ev.Key.ID); +// var path = announcer.GetAnnouncementPath(announcementId, announcerProto); - if (!cache.ContentFileExists(path)) - { - succeeded = false; - why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{path}\""); - } - } - } - } +// if (!cache.ContentFileExists(path)) +// { +// succeeded = false; +// why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{path}\""); +// } +// } +// } +// } - Assert.That(succeeded, Is.True, $"The following announcements do not have a valid announcement audio:\n {string.Join("\n ", why)}"); - }); +// Assert.That(succeeded, Is.True, $"The following announcements do not have a valid announcement audio:\n {string.Join("\n ", why)}"); +// }); - await pair.CleanReturnAsync(); - } -} +// await pair.CleanReturnAsync(); +// } +// } diff --git a/Content.IntegrationTests/Tests/Announcers/AnnouncerLocalizationTest.cs b/Content.IntegrationTests/Tests/Announcers/AnnouncerLocalizationTest.cs index c9ac18bc68c..b382c23d2ea 100644 --- a/Content.IntegrationTests/Tests/Announcers/AnnouncerLocalizationTest.cs +++ b/Content.IntegrationTests/Tests/Announcers/AnnouncerLocalizationTest.cs @@ -1,76 +1,76 @@ -using System.Collections.Generic; -using System.Linq; -using Content.Server.Announcements.Systems; -using Content.Server.StationEvents; -using Content.Shared.Announcements.Prototypes; -using Robust.Shared.GameObjects; -using Robust.Shared.Localization; -using Robust.Shared.Prototypes; +// using System.Collections.Generic; +// using System.Linq; +// using Content.Server.Announcements.Systems; +// using Content.Server.StationEvents; +// using Content.Shared.Announcements.Prototypes; +// using Robust.Shared.GameObjects; +// using Robust.Shared.Localization; +// using Robust.Shared.Prototypes; -namespace Content.IntegrationTests.Tests.Announcers; +// namespace Content.IntegrationTests.Tests.Announcers; -/// -/// Checks if every station event wanting the announcerSystem to send messages has a localization string -/// If an event doesn't have startAnnouncement or endAnnouncement set to true -/// it will be expected for that system to handle the announcements if it wants them -/// -[TestFixture] -[TestOf(typeof(AnnouncerPrototype))] -public sealed class AnnouncerLocalizationTest -{ - /// - [Test] - public async Task TestEventLocalization() - { - await using var pair = await PoolManager.GetServerClient(); - var server = pair.Server; +// /// +// /// Checks if every station event wanting the announcerSystem to send messages has a localization string +// /// If an event doesn't have startAnnouncement or endAnnouncement set to true +// /// it will be expected for that system to handle the announcements if it wants them +// /// +// [TestFixture] +// [TestOf(typeof(AnnouncerPrototype))] +// public sealed class AnnouncerLocalizationTest +// { +// /// +// [Test] +// public async Task TestEventLocalization() +// { +// await using var pair = await PoolManager.GetServerClient(); +// var server = pair.Server; - var locale = server.ResolveDependency(); - var entSysMan = server.ResolveDependency(); - var proto = server.ResolveDependency(); - var announcer = entSysMan.GetEntitySystem(); - var events = entSysMan.GetEntitySystem(); +// var locale = server.ResolveDependency(); +// var entSysMan = server.ResolveDependency(); +// var proto = server.ResolveDependency(); +// var announcer = entSysMan.GetEntitySystem(); +// var events = entSysMan.GetEntitySystem(); - await server.WaitAssertion(() => - { - var succeeded = true; - var why = new List(); +// await server.WaitAssertion(() => +// { +// var succeeded = true; +// var why = new List(); - foreach (var announcerProto in proto.EnumeratePrototypes().OrderBy(a => a.ID)) - { - foreach (var ev in events.AllEvents()) - { - if (ev.Value.StartAnnouncement) - { - var announcementId = announcer.GetAnnouncementId(ev.Key.ID); - var eventLocaleString = announcer.GetAnnouncementMessage(announcementId, announcerProto.ID) - ?? announcer.GetEventLocaleString(announcementId); +// foreach (var announcerProto in proto.EnumeratePrototypes().OrderBy(a => a.ID)) +// { +// foreach (var ev in events.AllEvents()) +// { +// if (ev.Value.StartAnnouncement != null) +// { +// var announcementId = ev.Value.StartAnnouncement ?? announcer.GetAnnouncementId(ev.Key.ID); +// var eventLocaleString = announcer.GetAnnouncementMessage(announcementId, announcerProto.ID) +// ?? announcer.GetEventLocaleString(announcementId); - if (locale.GetString(eventLocaleString) == eventLocaleString) - { - succeeded = false; - why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{eventLocaleString}\""); - } - } +// if (locale.GetString(eventLocaleString) == eventLocaleString) +// { +// succeeded = false; +// why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{eventLocaleString}\""); +// } +// } - if (ev.Value.EndAnnouncement) - { - var announcementId = announcer.GetAnnouncementId(ev.Key.ID, true); - var eventLocaleString = announcer.GetAnnouncementMessage(announcementId, announcerProto.ID) - ?? announcer.GetEventLocaleString(announcementId); +// if (ev.Value.EndAnnouncement != null) +// { +// var announcementId = ev.Value.StartAnnouncement ?? announcer.GetAnnouncementId(ev.Key.ID); +// var eventLocaleString = announcer.GetAnnouncementMessage(announcementId, announcerProto.ID) +// ?? announcer.GetEventLocaleString(announcementId); - if (locale.GetString(eventLocaleString) == eventLocaleString) - { - succeeded = false; - why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{eventLocaleString}\""); - } - } - } - } +// if (locale.GetString(eventLocaleString) == eventLocaleString) +// { +// succeeded = false; +// why.Add($"\"{announcerProto.ID}\", \"{announcementId}\": \"{eventLocaleString}\""); +// } +// } +// } +// } - Assert.That(succeeded, Is.True, $"The following announcements do not have a localization string:\n {string.Join("\n ", why)}"); - }); +// Assert.That(succeeded, Is.True, $"The following announcements do not have a localization string:\n {string.Join("\n ", why)}"); +// }); - await pair.CleanReturnAsync(); - } -} +// await pair.CleanReturnAsync(); +// } +// } diff --git a/Content.IntegrationTests/Tests/Body/GibTest.cs b/Content.IntegrationTests/Tests/Body/GibTest.cs index c0032a85244..4627c79f64d 100644 --- a/Content.IntegrationTests/Tests/Body/GibTest.cs +++ b/Content.IntegrationTests/Tests/Body/GibTest.cs @@ -5,7 +5,7 @@ namespace Content.IntegrationTests.Tests.Body; [TestFixture] -public sealed class GibTest +public sealed class GibTest { [Test] public async Task TestGib() diff --git a/Content.IntegrationTests/Tests/Body/LegTest.cs b/Content.IntegrationTests/Tests/Body/LegTest.cs index e86966f8f54..7b49bbe84a3 100644 --- a/Content.IntegrationTests/Tests/Body/LegTest.cs +++ b/Content.IntegrationTests/Tests/Body/LegTest.cs @@ -5,7 +5,6 @@ using Content.Shared.Rotation; using Robust.Shared.GameObjects; using Robust.Shared.Map; -using Robust.Shared.Maths; namespace Content.IntegrationTests.Tests.Body { @@ -40,13 +39,14 @@ public async Task RemoveLegsFallTest() var appearanceSystem = entityManager.System(); var xformSystem = entityManager.System(); + var map = await pair.CreateTestMap(); + await server.WaitAssertion(() => { - var mapId = mapManager.CreateMap(); BodyComponent body = null; human = entityManager.SpawnEntity("HumanBodyAndAppearanceDummy", - new MapCoordinates(Vector2.Zero, mapId)); + new MapCoordinates(Vector2.Zero, map.MapId)); Assert.Multiple(() => { @@ -61,7 +61,7 @@ await server.WaitAssertion(() => foreach (var leg in legs) { - xformSystem.DetachParentToNull(leg.Id, entityManager.GetComponent(leg.Id)); + xformSystem.DetachEntity(leg.Id, entityManager.GetComponent(leg.Id)); } }); diff --git a/Content.IntegrationTests/Tests/Body/LungTest.cs b/Content.IntegrationTests/Tests/Body/LungTest.cs index dce3741c98d..9b5ee431f1f 100644 --- a/Content.IntegrationTests/Tests/Body/LungTest.cs +++ b/Content.IntegrationTests/Tests/Body/LungTest.cs @@ -60,8 +60,8 @@ public async Task AirConsistencyTest() var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var mapLoader = entityManager.System(); + var mapSys = entityManager.System(); - MapId mapId; EntityUid? grid = null; BodyComponent body = default; RespiratorComponent resp = default; @@ -73,7 +73,7 @@ public async Task AirConsistencyTest() await server.WaitPost(() => { - mapId = mapManager.CreateMap(); + mapSys.CreateMap(out var mapId); Assert.That(mapLoader.TryLoad(mapId, testMapName, out var roots)); var query = entityManager.GetEntityQuery(); @@ -142,8 +142,8 @@ public async Task NoSuffocationTest() var entityManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); var mapLoader = entityManager.System(); + var mapSys = entityManager.System(); - MapId mapId; EntityUid? grid = null; RespiratorComponent respirator = null; EntityUid human = default; @@ -152,7 +152,7 @@ public async Task NoSuffocationTest() await server.WaitPost(() => { - mapId = mapManager.CreateMap(); + mapSys.CreateMap(out var mapId); Assert.That(mapLoader.TryLoad(mapId, testMapName, out var ents), Is.True); var query = entityManager.GetEntityQuery(); diff --git a/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs b/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs index 670ce1a474d..01482ba8ee2 100644 --- a/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs +++ b/Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs @@ -33,10 +33,11 @@ public async Task Test() var mapLoader = entities.System(); var bodySystem = entities.System(); var containerSystem = entities.System(); + var mapSys = entities.System(); await server.WaitAssertion(() => { - var mapId = maps.CreateMap(); + mapSys.CreateMap(out var mapId); maps.CreateGrid(mapId); var human = entities.SpawnEntity("HumanBodyDummy", new MapCoordinates(0, 0, mapId)); @@ -115,7 +116,7 @@ await server.WaitAssertion(() => mapLoader.SaveMap(mapId, mapPath); maps.DeleteMap(mapId); - mapId = maps.CreateMap(); + mapSys.CreateMap(out mapId); Assert.That(mapLoader.TryLoad(mapId, mapPath, out _), Is.True); var query = EnumerateQueryEnumerator( diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleDragTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleDragTest.cs new file mode 100644 index 00000000000..19e8aba1824 --- /dev/null +++ b/Content.IntegrationTests/Tests/Buckle/BuckleDragTest.cs @@ -0,0 +1,60 @@ +using Content.IntegrationTests.Tests.Interaction; +using Content.Shared.Buckle; +using Content.Shared.Buckle.Components; +using Content.Shared.Input; +using Content.Shared.Movement.Pulling.Components; + +namespace Content.IntegrationTests.Tests.Buckle; + +public sealed class BuckleDragTest : InteractionTest +{ + // Check that dragging a buckled player unbuckles them. + [Test] + public async Task BucklePullTest() + { + var urist = await SpawnTarget("MobHuman"); + var sUrist = ToServer(urist); + await SpawnTarget("Chair"); + + var buckle = Comp(urist); + var strap = Comp(Target); + var puller = Comp(Player); + var pullable = Comp(urist); + +#pragma warning disable RA0002 + buckle.Delay = TimeSpan.Zero; +#pragma warning restore RA0002 + + // Initially not buckled to the chair and not pulling anything + Assert.That(buckle.Buckled, Is.False); + Assert.That(buckle.BuckledTo, Is.Null); + Assert.That(strap.BuckledEntities, Is.Empty); + Assert.That(puller.Pulling, Is.Null); + Assert.That(pullable.Puller, Is.Null); + Assert.That(pullable.BeingPulled, Is.False); + + // Strap the human to the chair + await Server.WaitAssertion(() => + { + Assert.That(Server.System().TryBuckle(sUrist, SPlayer, STarget.Value)); + }); + + await RunTicks(5); + Assert.That(buckle.Buckled, Is.True); + Assert.That(buckle.BuckledTo, Is.EqualTo(STarget)); + Assert.That(strap.BuckledEntities, Is.EquivalentTo(new[] { sUrist })); + Assert.That(puller.Pulling, Is.Null); + Assert.That(pullable.Puller, Is.Null); + Assert.That(pullable.BeingPulled, Is.False); + + // Start pulling, and thus unbuckle them + await PressKey(ContentKeyFunctions.TryPullObject, cursorEntity: urist); + await RunTicks(5); + Assert.That(buckle.Buckled, Is.False); + Assert.That(buckle.BuckledTo, Is.Null); + Assert.That(strap.BuckledEntities, Is.Empty); + Assert.That(puller.Pulling, Is.EqualTo(sUrist)); + Assert.That(pullable.Puller, Is.EqualTo(SPlayer)); + Assert.That(pullable.BeingPulled, Is.True); + } +} diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs new file mode 100644 index 00000000000..d9cce764ab7 --- /dev/null +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.Interact.cs @@ -0,0 +1,108 @@ +using Content.Shared.Buckle; +using Content.Shared.Buckle.Components; +using Content.Shared.Interaction; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; + +namespace Content.IntegrationTests.Tests.Buckle; + +public sealed partial class BuckleTest +{ + [Test] + public async Task BuckleInteractUnbuckleOther() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entMan = server.ResolveDependency(); + var buckleSystem = entMan.System(); + + EntityUid user = default; + EntityUid victim = default; + EntityUid chair = default; + BuckleComponent buckle = null; + StrapComponent strap = null; + + await server.WaitAssertion(() => + { + user = entMan.SpawnEntity(BuckleDummyId, MapCoordinates.Nullspace); + victim = entMan.SpawnEntity(BuckleDummyId, MapCoordinates.Nullspace); + chair = entMan.SpawnEntity(StrapDummyId, MapCoordinates.Nullspace); + + Assert.That(entMan.TryGetComponent(victim, out buckle)); + Assert.That(entMan.TryGetComponent(chair, out strap)); + +#pragma warning disable RA0002 + buckle.Delay = TimeSpan.Zero; +#pragma warning restore RA0002 + + // Buckle victim to chair + Assert.That(buckleSystem.TryBuckle(victim, user, chair, buckle)); + Assert.Multiple(() => + { + Assert.That(buckle.BuckledTo, Is.EqualTo(chair), "Victim did not get buckled to the chair."); + Assert.That(buckle.Buckled, "Victim is not buckled."); + Assert.That(strap.BuckledEntities, Does.Contain(victim), "Chair does not have victim buckled to it."); + }); + + // InteractHand with chair to unbuckle victim + entMan.EventBus.RaiseLocalEvent(chair, new InteractHandEvent(user, chair)); + Assert.Multiple(() => + { + Assert.That(buckle.BuckledTo, Is.Null); + Assert.That(buckle.Buckled, Is.False); + Assert.That(strap.BuckledEntities, Does.Not.Contain(victim)); + }); + }); + + await pair.CleanReturnAsync(); + } + + [Test] + public async Task BuckleInteractBuckleUnbuckleSelf() + { + await using var pair = await PoolManager.GetServerClient(); + var server = pair.Server; + + var entMan = server.ResolveDependency(); + + EntityUid user = default; + EntityUid chair = default; + BuckleComponent buckle = null; + StrapComponent strap = null; + + await server.WaitAssertion(() => + { + user = entMan.SpawnEntity(BuckleDummyId, MapCoordinates.Nullspace); + chair = entMan.SpawnEntity(StrapDummyId, MapCoordinates.Nullspace); + + Assert.That(entMan.TryGetComponent(user, out buckle)); + Assert.That(entMan.TryGetComponent(chair, out strap)); + +#pragma warning disable RA0002 + buckle.Delay = TimeSpan.Zero; +#pragma warning restore RA0002 + + // Buckle user to chair + entMan.EventBus.RaiseLocalEvent(chair, new InteractHandEvent(user, chair)); + Assert.Multiple(() => + { + Assert.That(buckle.BuckledTo, Is.EqualTo(chair), "Victim did not get buckled to the chair."); + Assert.That(buckle.Buckled, "Victim is not buckled."); + Assert.That(strap.BuckledEntities, Does.Contain(user), "Chair does not have victim buckled to it."); + }); + + // InteractHand with chair to unbuckle + entMan.EventBus.RaiseLocalEvent(chair, new InteractHandEvent(user, chair)); + Assert.Multiple(() => + { + Assert.That(buckle.BuckledTo, Is.Null); + Assert.That(buckle.Buckled, Is.False); + Assert.That(strap.BuckledEntities, Does.Not.Contain(user)); + }); + }); + + await pair.CleanReturnAsync(); + } +} diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs index 7c700d9fb8a..94572da4989 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs @@ -15,7 +15,7 @@ namespace Content.IntegrationTests.Tests.Buckle [TestFixture] [TestOf(typeof(BuckleComponent))] [TestOf(typeof(StrapComponent))] - public sealed class BuckleTest + public sealed partial class BuckleTest { private const string BuckleDummyId = "BuckleDummy"; private const string StrapDummyId = "StrapDummy"; @@ -90,7 +90,6 @@ await server.WaitAssertion(() => { Assert.That(strap, Is.Not.Null); Assert.That(strap.BuckledEntities, Is.Empty); - Assert.That(strap.OccupiedSize, Is.Zero); }); // Side effects of buckling @@ -110,8 +109,6 @@ await server.WaitAssertion(() => // Side effects of buckling for the strap Assert.That(strap.BuckledEntities, Does.Contain(human)); - Assert.That(strap.OccupiedSize, Is.EqualTo(buckle.Size)); - Assert.That(strap.OccupiedSize, Is.Positive); }); #pragma warning disable NUnit2045 // Interdependent asserts. @@ -121,7 +118,7 @@ await server.WaitAssertion(() => // Trying to unbuckle too quickly fails Assert.That(buckleSystem.TryUnbuckle(human, human, buckleComp: buckle), Is.False); Assert.That(buckle.Buckled); - Assert.That(buckleSystem.ToggleBuckle(human, human, chair, buckle: buckle), Is.False); + Assert.That(buckleSystem.TryUnbuckle(human, human), Is.False); Assert.That(buckle.Buckled); #pragma warning restore NUnit2045 }); @@ -148,7 +145,6 @@ await server.WaitAssertion(() => // Unbuckle, strap Assert.That(strap.BuckledEntities, Is.Empty); - Assert.That(strap.OccupiedSize, Is.Zero); }); #pragma warning disable NUnit2045 // Interdependent asserts. @@ -159,9 +155,9 @@ await server.WaitAssertion(() => // On cooldown Assert.That(buckleSystem.TryUnbuckle(human, human, buckleComp: buckle), Is.False); Assert.That(buckle.Buckled); - Assert.That(buckleSystem.ToggleBuckle(human, human, chair, buckle: buckle), Is.False); + Assert.That(buckleSystem.TryUnbuckle(human, human), Is.False); Assert.That(buckle.Buckled); - Assert.That(buckleSystem.ToggleBuckle(human, human, chair, buckle: buckle), Is.False); + Assert.That(buckleSystem.TryUnbuckle(human, human), Is.False); Assert.That(buckle.Buckled); #pragma warning restore NUnit2045 }); @@ -188,7 +184,6 @@ await server.WaitAssertion(() => #pragma warning disable NUnit2045 // Interdependent asserts. Assert.That(buckleSystem.TryBuckle(human, human, chair, buckleComp: buckle), Is.False); Assert.That(buckleSystem.TryUnbuckle(human, human, buckleComp: buckle), Is.False); - Assert.That(buckleSystem.ToggleBuckle(human, human, chair, buckle: buckle), Is.False); #pragma warning restore NUnit2045 // Move near the chair @@ -201,12 +196,10 @@ await server.WaitAssertion(() => Assert.That(buckle.Buckled); Assert.That(buckleSystem.TryUnbuckle(human, human, buckleComp: buckle), Is.False); Assert.That(buckle.Buckled); - Assert.That(buckleSystem.ToggleBuckle(human, human, chair, buckle: buckle), Is.False); - Assert.That(buckle.Buckled); #pragma warning restore NUnit2045 // Force unbuckle - Assert.That(buckleSystem.TryUnbuckle(human, human, true, buckleComp: buckle)); + buckleSystem.Unbuckle(human, human); Assert.Multiple(() => { Assert.That(buckle.Buckled, Is.False); @@ -310,7 +303,7 @@ await server.WaitAssertion(() => // Break our guy's kneecaps foreach (var leg in legs) { - xformSystem.DetachParentToNull(leg.Id, entityManager.GetComponent(leg.Id)); + entityManager.DeleteEntity(leg.Id); } }); @@ -327,7 +320,8 @@ await server.WaitAssertion(() => Assert.That(hand.HeldEntity, Is.Null); } - buckleSystem.TryUnbuckle(human, human, true, buckleComp: buckle); + buckleSystem.Unbuckle(human, human); + Assert.That(buckle.Buckled, Is.False); }); await pair.CleanReturnAsync(); diff --git a/Content.IntegrationTests/Tests/CargoTest.cs b/Content.IntegrationTests/Tests/CargoTest.cs index 09f179cf4f5..99160df3c66 100644 --- a/Content.IntegrationTests/Tests/CargoTest.cs +++ b/Content.IntegrationTests/Tests/CargoTest.cs @@ -14,11 +14,11 @@ namespace Content.IntegrationTests.Tests; [TestFixture] public sealed class CargoTest { - public static HashSet> Ignored = new () - { + private static readonly HashSet> Ignored = + [ // This is ignored because it is explicitly intended to be able to sell for more than it costs. new("FunCrateGambling") - }; + ]; [Test] public async Task NoCargoOrderArbitrage() @@ -174,13 +174,16 @@ public async Task StackPrice() { await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; - var entManager = server.ResolveDependency(); - var priceSystem = entManager.System(); - var ent = entManager.SpawnEntity("StackEnt", MapCoordinates.Nullspace); - var price = priceSystem.GetPrice(ent); - Assert.That(price, Is.EqualTo(100.0)); + await server.WaitAssertion(() => + { + var priceSystem = entManager.System(); + + var ent = entManager.SpawnEntity("StackEnt", MapCoordinates.Nullspace); + var price = priceSystem.GetPrice(ent); + Assert.That(price, Is.EqualTo(100.0)); + }); await pair.CleanReturnAsync(); } diff --git a/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs b/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs index a5449308be4..52b7e555a9d 100644 --- a/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/DispenserTest.cs @@ -18,7 +18,7 @@ public async Task InsertEjectBuiTest() ToggleNeedPower(); // Insert beaker - await Interact("Beaker"); + await InteractUsing("Beaker"); Assert.That(Hands.ActiveHandEntity, Is.Null); // Open BUI diff --git a/Content.IntegrationTests/Tests/Chemistry/FixedPoint2SerializationTest.cs b/Content.IntegrationTests/Tests/Chemistry/FixedPoint2SerializationTest.cs index 8e3b89bff11..0e3f89c2825 100644 --- a/Content.IntegrationTests/Tests/Chemistry/FixedPoint2SerializationTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/FixedPoint2SerializationTest.cs @@ -9,10 +9,10 @@ namespace Content.IntegrationTests.Tests.Chemistry { public sealed class FixedPoint2SerializationTest : SerializationTest { - protected override Assembly[] Assemblies => new[] - { + protected override Assembly[] Assemblies => + [ typeof(FixedPoint2SerializationTest).Assembly - }; + ]; [Test] public void DeserializeNullTest() @@ -53,6 +53,6 @@ public void DeserializeNullDefinitionTest() [DataDefinition] public sealed partial class FixedPoint2TestDefinition { - [DataField("unit")] public FixedPoint2? Unit { get; set; } = FixedPoint2.New(5); + [DataField] public FixedPoint2? Unit { get; set; } = FixedPoint2.New(5); } } diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs index 4d19a96d9e7..89d33186a27 100644 --- a/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/SolutionRoundingTest.cs @@ -1,5 +1,5 @@ -using Content.Server.Chemistry.Containers.EntitySystems; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; @@ -76,7 +76,7 @@ public async Task Test() await server.WaitPost(() => { - var system = server.System(); + var system = server.System(); var beaker = server.EntMan.SpawnEntity("SolutionRoundingTestContainer", testMap.GridCoords); system.TryGetSolution(beaker, "beaker", out var newSolutionEnt, out var newSolution); diff --git a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs index d96a035b2dc..6b71dd08be0 100644 --- a/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs +++ b/Content.IntegrationTests/Tests/Chemistry/SolutionSystemTests.cs @@ -1,5 +1,5 @@ -using Content.Server.Chemistry.Containers.EntitySystems; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.FixedPoint; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; @@ -11,7 +11,7 @@ namespace Content.IntegrationTests.Tests.Chemistry; // To ensure volume(A) + volume(B) = volume(A+B) // reactions can change this assumption [TestFixture] -[TestOf(typeof(SolutionContainerSystem))] +[TestOf(typeof(SharedSolutionContainerSystem))] public sealed class SolutionSystemTests { [TestPrototypes] @@ -51,7 +51,7 @@ public async Task TryAddTwoNonReactiveReagent() var entityManager = server.ResolveDependency(); var protoMan = server.ResolveDependency(); - var containerSystem = entityManager.System(); + var containerSystem = entityManager.System(); var testMap = await pair.CreateTestMap(); var coordinates = testMap.GridCoords; @@ -97,7 +97,7 @@ public async Task TryAddTooMuchNonReactiveReagent() var entityManager = server.ResolveDependency(); var protoMan = server.ResolveDependency(); - var containerSystem = entityManager.System(); + var containerSystem = entityManager.System(); var coordinates = testMap.GridCoords; EntityUid beaker; @@ -141,7 +141,7 @@ public async Task TryMixAndOverflowTooMuchReagent() var entityManager = server.ResolveDependency(); var protoMan = server.ResolveDependency(); var testMap = await pair.CreateTestMap(); - var containerSystem = entityManager.System(); + var containerSystem = entityManager.System(); var coordinates = testMap.GridCoords; EntityUid beaker; @@ -194,7 +194,7 @@ public async Task TryMixAndOverflowTooBigOverflow() var entityManager = server.ResolveDependency(); var protoMan = server.ResolveDependency(); - var containerSystem = entityManager.System(); + var containerSystem = entityManager.System(); var testMap = await pair.CreateTestMap(); var coordinates = testMap.GridCoords; diff --git a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs index ddfe7b3481e..3664cda922a 100644 --- a/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs +++ b/Content.IntegrationTests/Tests/Chemistry/TryAllReactionsTest.cs @@ -1,4 +1,3 @@ -using Content.Server.Chemistry.Containers.EntitySystems; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Components; using Robust.Shared.GameObjects; @@ -6,6 +5,7 @@ using Robust.Shared.Prototypes; using Robust.Shared.Utility; using System.Linq; +using Content.Shared.Chemistry.EntitySystems; namespace Content.IntegrationTests.Tests.Chemistry { @@ -34,7 +34,7 @@ public async Task TryAllTest() var prototypeManager = server.ResolveDependency(); var testMap = await pair.CreateTestMap(); var coordinates = testMap.GridCoords; - var solutionContainerSystem = entityManager.System(); + var solutionContainerSystem = entityManager.System(); foreach (var reactionPrototype in prototypeManager.EnumeratePrototypes()) { diff --git a/Content.IntegrationTests/Tests/ClickableTest.cs b/Content.IntegrationTests/Tests/ClickableTest.cs index 76085381852..59836509081 100644 --- a/Content.IntegrationTests/Tests/ClickableTest.cs +++ b/Content.IntegrationTests/Tests/ClickableTest.cs @@ -52,7 +52,6 @@ public async Task Test(string prototype, float clickPosX, float clickPosY, var serverEntManager = server.ResolveDependency(); var eyeManager = client.ResolveDependency(); var spriteQuery = clientEntManager.GetEntityQuery(); - var xformQuery = clientEntManager.GetEntityQuery(); var eye = client.ResolveDependency().CurrentEye; var testMap = await pair.CreateTestMap(); @@ -80,9 +79,8 @@ await client.WaitPost(() => eyeManager.CurrentEye.Rotation = 0; var pos = clientEntManager.System().GetWorldPosition(clientEnt); - var clickable = clientEntManager.GetComponent(clientEnt); - hit = clickable.CheckClick(sprite, xformQuery.GetComponent(clientEnt), xformQuery, new Vector2(clickPosX, clickPosY) + pos, eye, out _, out _, out _); + hit = clientEntManager.System().CheckClick((clientEnt, null, sprite, null), new Vector2(clickPosX, clickPosY) + pos, eye, out _, out _, out _); }); await server.WaitPost(() => diff --git a/Content.IntegrationTests/Tests/Climbing/ClimbingTest.cs b/Content.IntegrationTests/Tests/Climbing/ClimbingTest.cs index d8d3086520e..2db0a9acd3d 100644 --- a/Content.IntegrationTests/Tests/Climbing/ClimbingTest.cs +++ b/Content.IntegrationTests/Tests/Climbing/ClimbingTest.cs @@ -1,5 +1,6 @@ #nullable enable using Content.IntegrationTests.Tests.Interaction; +using Content.IntegrationTests.Tests.Movement; using Robust.Shared.Maths; using ClimbingComponent = Content.Shared.Climbing.Components.ClimbingComponent; using ClimbSystem = Content.Shared.Climbing.Systems.ClimbSystem; diff --git a/Content.IntegrationTests/Tests/Commands/PardonCommand.cs b/Content.IntegrationTests/Tests/Commands/PardonCommand.cs index b3a66e3211c..4db9eabf5c6 100644 --- a/Content.IntegrationTests/Tests/Commands/PardonCommand.cs +++ b/Content.IntegrationTests/Tests/Commands/PardonCommand.cs @@ -28,7 +28,7 @@ public async Task PardonTest() Assert.That(netMan.IsConnected); - Assert.That(sPlayerManager.Sessions.Count(), Is.EqualTo(1)); + Assert.That(sPlayerManager.Sessions, Has.Length.EqualTo(1)); // No bans on record Assert.Multiple(async () => { @@ -50,7 +50,7 @@ public async Task PardonTest() var banReason = "test"; - Assert.That(sPlayerManager.Sessions.Count(), Is.EqualTo(1)); + Assert.That(sPlayerManager.Sessions, Has.Length.EqualTo(1)); // Ban the client for 24 hours await server.WaitPost(() => sConsole.ExecuteCommand($"ban {clientSession.Name} {banReason} 1440")); @@ -63,7 +63,7 @@ public async Task PardonTest() }); await pair.RunTicksSync(5); - Assert.That(sPlayerManager.Sessions.Count(), Is.EqualTo(0)); + Assert.That(sPlayerManager.Sessions, Has.Length.EqualTo(0)); Assert.That(!netMan.IsConnected); // Try to pardon a ban that does not exist @@ -143,11 +143,11 @@ public async Task PardonTest() }); // Reconnect client. Slightly faster than dirtying the pair. - Assert.That(sPlayerManager.Sessions.Count(), Is.EqualTo(0)); + Assert.That(sPlayerManager.Sessions, Is.Empty); client.SetConnectTarget(server); await client.WaitPost(() => netMan.ClientConnect(null!, 0, null!)); await pair.RunTicksSync(5); - Assert.That(sPlayerManager.Sessions.Count(), Is.EqualTo(1)); + Assert.That(sPlayerManager.Sessions, Has.Length.EqualTo(1)); await pair.CleanReturnAsync(); } diff --git a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs index 2fda3ad58e6..cfc80073066 100644 --- a/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs +++ b/Content.IntegrationTests/Tests/Commands/RejuvenateTest.cs @@ -37,9 +37,9 @@ public async Task RejuvenateDeadTest() var server = pair.Server; var entManager = server.ResolveDependency(); var prototypeManager = server.ResolveDependency(); - var mobStateSystem = entManager.EntitySysManager.GetEntitySystem(); - var damSystem = entManager.EntitySysManager.GetEntitySystem(); - var rejuvenateSystem = entManager.EntitySysManager.GetEntitySystem(); + var mobStateSystem = entManager.System(); + var damSystem = entManager.System(); + var rejuvenateSystem = entManager.System(); await server.WaitAssertion(() => { diff --git a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs index 74d014b7724..b94cd7807cf 100644 --- a/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs +++ b/Content.IntegrationTests/Tests/Commands/RestartRoundTest.cs @@ -25,7 +25,7 @@ public async Task RestartRoundAfterStart(bool lobbyEnabled) var configManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); - var gameTicker = entityManager.EntitySysManager.GetEntitySystem(); + var gameTicker = entityManager.System(); await pair.RunTicksSync(5); diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/ComputerContruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/ComputerContruction.cs index 5412469ac5d..8af5edaf316 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/ComputerContruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/ComputerContruction.cs @@ -16,10 +16,8 @@ public async Task ConstructComputer() await StartConstruction(Computer); // Initial interaction (ghost turns into real entity) - await Interact(Steel, 5); - ClientAssertPrototype(ComputerFrame, ClientTarget); - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; - ClientTarget = null; + await InteractUsing(Steel, 5); + ClientAssertPrototype(ComputerFrame, Target); // Perform construction steps await Interact( @@ -41,7 +39,7 @@ public async Task DeconstructComputer() await StartDeconstruction(ComputerId); // Initial interaction turns id computer into generic computer - await Interact(Screw); + await InteractUsing(Screw); AssertPrototype(ComputerFrame); // Perform deconstruction steps @@ -71,7 +69,7 @@ public async Task ChangeComputer() await SpawnTarget(ComputerId); // Initial interaction turns id computer into generic computer - await Interact(Screw); + await InteractUsing(Screw); AssertPrototype(ComputerFrame); // Perform partial deconstruction steps diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/CraftingTests.cs b/Content.IntegrationTests/Tests/Construction/Interaction/CraftingTests.cs index 76911eba5f7..74d0e924217 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/CraftingTests.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/CraftingTests.cs @@ -59,11 +59,6 @@ public async Task CraftSpear() await AssertEntityLookup((Rod, 2), (Cable, 7), (ShardGlass, 2), (Spear, 1)); } - // The following is wrapped in an if DEBUG. This is because of cursed state handling bugs. Tests don't (de)serialize - // net messages and just copy objects by reference. This means that the server will directly modify cached server - // states on the client's end. Crude fix at the moment is to used modified state handling while in debug mode - // Otherwise, this test cannot work. -#if DEBUG /// /// Cancel crafting a complex recipe. /// @@ -93,28 +88,22 @@ public async Task CancelCraft() await RunTicks(1); // DoAfter is in progress. Entity not spawned, stacks have been split and someingredients are in a container. - Assert.Multiple(async () => - { - Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1)); - Assert.That(sys.IsEntityInContainer(shard), Is.True); - Assert.That(sys.IsEntityInContainer(rods), Is.False); - Assert.That(sys.IsEntityInContainer(wires), Is.False); - Assert.That(rodStack, Has.Count.EqualTo(8)); - Assert.That(wireStack, Has.Count.EqualTo(7)); + Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1)); + Assert.That(sys.IsEntityInContainer(shard), Is.True); + Assert.That(sys.IsEntityInContainer(rods), Is.False); + Assert.That(sys.IsEntityInContainer(wires), Is.False); + Assert.That(rodStack, Has.Count.EqualTo(8)); + Assert.That(wireStack, Has.Count.EqualTo(7)); - await FindEntity(Spear, shouldSucceed: false); - }); + await FindEntity(Spear, shouldSucceed: false); // Cancel the DoAfter. Should drop ingredients to the floor. await CancelDoAfters(); - Assert.Multiple(async () => - { - Assert.That(sys.IsEntityInContainer(rods), Is.False); - Assert.That(sys.IsEntityInContainer(wires), Is.False); - Assert.That(sys.IsEntityInContainer(shard), Is.False); - await FindEntity(Spear, shouldSucceed: false); - await AssertEntityLookup((Rod, 10), (Cable, 10), (ShardGlass, 1)); - }); + Assert.That(sys.IsEntityInContainer(rods), Is.False); + Assert.That(sys.IsEntityInContainer(wires), Is.False); + Assert.That(sys.IsEntityInContainer(shard), Is.False); + await FindEntity(Spear, shouldSucceed: false); + await AssertEntityLookup((Rod, 10), (Cable, 10), (ShardGlass, 1)); // Re-attempt the do-after #pragma warning disable CS4014 // Legacy construction code uses DoAfterAwait. See above. @@ -123,24 +112,17 @@ public async Task CancelCraft() await RunTicks(1); // DoAfter is in progress. Entity not spawned, ingredients are in a container. - Assert.Multiple(async () => - { - Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1)); - Assert.That(sys.IsEntityInContainer(shard), Is.True); - await FindEntity(Spear, shouldSucceed: false); - }); + Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1)); + Assert.That(sys.IsEntityInContainer(shard), Is.True); + await FindEntity(Spear, shouldSucceed: false); // Finish the DoAfter await AwaitDoAfters(); // Spear has been crafted. Rods and wires are no longer contained. Glass has been consumed. - Assert.Multiple(async () => - { - await FindEntity(Spear); - Assert.That(sys.IsEntityInContainer(rods), Is.False); - Assert.That(sys.IsEntityInContainer(wires), Is.False); - Assert.That(SEntMan.Deleted(shard)); - }); + await FindEntity(Spear); + Assert.That(sys.IsEntityInContainer(rods), Is.False); + Assert.That(sys.IsEntityInContainer(wires), Is.False); + Assert.That(SEntMan.Deleted(shard)); } -#endif } diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/GrilleWindowConstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/GrilleWindowConstruction.cs index 0de39d27577..ef6a7b09ae3 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/GrilleWindowConstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/GrilleWindowConstruction.cs @@ -17,17 +17,14 @@ public async Task WindowOnGrille() { // Construct Grille await StartConstruction(Grille); - await Interact(Rod, 10); - ClientAssertPrototype(Grille, ClientTarget); - - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; + await InteractUsing(Rod, 10); + ClientAssertPrototype(Grille, Target); var grille = Target; // Construct Window await StartConstruction(Window); - await Interact(Glass, 10); - ClientAssertPrototype(Window, ClientTarget); - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; + await InteractUsing(Glass, 10); + ClientAssertPrototype(Window, Target); // Deconstruct Window await Interact(Screw, Wrench); @@ -35,7 +32,7 @@ public async Task WindowOnGrille() // Deconstruct Grille Target = grille; - await Interact(Cut); + await InteractUsing(Cut); AssertDeleted(); } diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs index cd95a85f205..98db51b4078 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/MachineConstruction.cs @@ -14,9 +14,8 @@ public sealed class MachineConstruction : InteractionTest public async Task ConstructProtolathe() { await StartConstruction(MachineFrame); - await Interact(Steel, 5); - ClientAssertPrototype(Unfinished, ClientTarget); - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; + await InteractUsing(Steel, 5); + ClientAssertPrototype(Unfinished, Target); await Interact(Wrench, Cable); AssertPrototype(MachineFrame); await Interact(ProtolatheBoard, Bin1, Bin1, Manipulator1, Manipulator1, Beaker, Beaker, Screw); @@ -51,7 +50,7 @@ public async Task ChangeMachine() AssertPrototype(MachineFrame); // Change it into an autolathe - await Interact("AutolatheMachineCircuitboard"); + await InteractUsing("AutolatheMachineCircuitboard"); AssertPrototype(MachineFrame); await Interact(Bin1, Bin1, Bin1, Manipulator1, Glass, Screw); AssertPrototype("Autolathe"); diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/PanelScrewing.cs b/Content.IntegrationTests/Tests/Construction/Interaction/PanelScrewing.cs index b6d960e2882..636d58bf96f 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/PanelScrewing.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/PanelScrewing.cs @@ -19,21 +19,21 @@ public async Task WiresPanelScrewing(string prototype) // Open & close panel Assert.That(comp.Open, Is.False); - await Interact(Screw); + await InteractUsing(Screw); Assert.That(comp.Open, Is.True); - await Interact(Screw); + await InteractUsing(Screw); Assert.That(comp.Open, Is.False); // Interrupted DoAfters - await Interact(Screw, awaitDoAfters: false); + await InteractUsing(Screw, awaitDoAfters: false); await CancelDoAfters(); Assert.That(comp.Open, Is.False); - await Interact(Screw); + await InteractUsing(Screw); Assert.That(comp.Open, Is.True); - await Interact(Screw, awaitDoAfters: false); + await InteractUsing(Screw, awaitDoAfters: false); await CancelDoAfters(); Assert.That(comp.Open, Is.True); - await Interact(Screw); + await InteractUsing(Screw); Assert.That(comp.Open, Is.False); } } diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/PlaceableDeconstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/PlaceableDeconstruction.cs index bc0cb9bcef3..783c14c0682 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/PlaceableDeconstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/PlaceableDeconstruction.cs @@ -13,9 +13,9 @@ public async Task DeconstructTable() { await StartDeconstruction("Table"); Assert.That(Comp().IsPlaceable); - await Interact(Wrench); + await InteractUsing(Wrench); AssertPrototype("TableFrame"); - await Interact(Wrench); + await InteractUsing(Wrench); AssertDeleted(); await AssertEntityLookup((Steel, 1), (Rod, 2)); } diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs index 67a2f8025dc..292bf0c55ab 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/WallConstruction.cs @@ -12,11 +12,10 @@ public sealed class WallConstruction : InteractionTest public async Task ConstructWall() { await StartConstruction(Wall); - await Interact(Steel, 2); + await InteractUsing(Steel, 2); Assert.That(Hands.ActiveHandEntity, Is.Null); - ClientAssertPrototype(Girder, ClientTarget); - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; - await Interact(Steel, 2); + ClientAssertPrototype(Girder, Target); + await InteractUsing(Steel, 2); Assert.That(Hands.ActiveHandEntity, Is.Null); AssertPrototype(WallSolid); } @@ -25,7 +24,7 @@ public async Task ConstructWall() public async Task DeconstructWall() { await StartDeconstruction(WallSolid); - await Interact(Weld); + await InteractUsing(Weld); AssertPrototype(Girder); await Interact(Wrench, Screw); AssertDeleted(); diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/WindowConstruction.cs b/Content.IntegrationTests/Tests/Construction/Interaction/WindowConstruction.cs index 46bb892ed99..2ece6b3e397 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/WindowConstruction.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/WindowConstruction.cs @@ -11,8 +11,8 @@ public sealed class WindowConstruction : InteractionTest public async Task ConstructWindow() { await StartConstruction(Window); - await Interact(Glass, 5); - ClientAssertPrototype(Window, ClientTarget); + await InteractUsing(Glass, 5); + ClientAssertPrototype(Window, Target); } [Test] @@ -28,8 +28,8 @@ public async Task DeconstructWindow() public async Task ConstructReinforcedWindow() { await StartConstruction(RWindow); - await Interact(RGlass, 5); - ClientAssertPrototype(RWindow, ClientTarget); + await InteractUsing(RGlass, 5); + ClientAssertPrototype(RWindow, Target); } [Test] diff --git a/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs b/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs index abd4bc265b3..6eea519af3b 100644 --- a/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs +++ b/Content.IntegrationTests/Tests/Construction/Interaction/WindowRepair.cs @@ -24,7 +24,7 @@ public async Task RepairReinforcedWindow() Assert.That(comp.Damage.GetTotal(), Is.GreaterThan(FixedPoint2.Zero)); // Repair the entity - await Interact(Weld); + await InteractUsing(Weld); Assert.That(comp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero)); // Validate that we can still deconstruct the entity (i.e., that welding deconstruction is not blocked). diff --git a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs index c61a70faf0b..c907f6bb1f3 100644 --- a/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs +++ b/Content.IntegrationTests/Tests/ContainerOcclusionTest.cs @@ -43,11 +43,11 @@ public async Task TestA() EntityUid dummy = default; var mapManager = server.ResolveDependency(); - var mapId = mapManager.CreateMap(); + var map = await pair.CreateTestMap(); await server.WaitPost(() => { - var pos = new MapCoordinates(Vector2.Zero, mapId); + var pos = new MapCoordinates(Vector2.Zero, map.MapId); var entStorage = serverEntManager.EntitySysManager.GetEntitySystem(); var container = serverEntManager.SpawnEntity("ContainerOcclusionA", pos); dummy = serverEntManager.SpawnEntity("ContainerOcclusionDummy", pos); @@ -85,11 +85,12 @@ public async Task TestB() EntityUid dummy = default; var mapManager = server.ResolveDependency(); - var mapId = mapManager.CreateMap(); + + var map = await pair.CreateTestMap(); await server.WaitPost(() => { - var pos = new MapCoordinates(Vector2.Zero, mapId); + var pos = new MapCoordinates(Vector2.Zero, map.MapId); var entStorage = serverEntManager.EntitySysManager.GetEntitySystem(); var container = serverEntManager.SpawnEntity("ContainerOcclusionB", pos); dummy = serverEntManager.SpawnEntity("ContainerOcclusionDummy", pos); @@ -99,10 +100,12 @@ await server.WaitPost(() => await pair.RunTicksSync(5); - var clientEnt = clientEntManager.GetEntity(serverEntManager.GetNetEntity(dummy)); + EntityUid clientEnt = default!; await client.WaitAssertion(() => { + clientEnt = clientEntManager.GetEntity(serverEntManager.GetNetEntity(dummy)); + var sprite = clientEntManager.GetComponent(clientEnt); var light = clientEntManager.GetComponent(clientEnt); Assert.Multiple(() => @@ -127,11 +130,12 @@ public async Task TestAb() EntityUid dummy = default; var mapManager = server.ResolveDependency(); - var mapId = mapManager.CreateMap(); + + var map = await pair.CreateTestMap(); await server.WaitPost(() => { - var pos = new MapCoordinates(Vector2.Zero, mapId); + var pos = new MapCoordinates(Vector2.Zero, map.MapId); var entStorage = serverEntManager.EntitySysManager.GetEntitySystem(); var containerA = serverEntManager.SpawnEntity("ContainerOcclusionA", pos); var containerB = serverEntManager.SpawnEntity("ContainerOcclusionB", pos); diff --git a/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs index 41d17ddedae..bd5cac05dd1 100644 --- a/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/DamageSpecifierTest.cs @@ -14,39 +14,39 @@ public void TestDamageSpecifierOperations() // Test basic math operations. // I've already nearly broken these once. When editing the operators. - DamageSpecifier input1 = new() { DamageDict = _input1 }; - DamageSpecifier input2 = new() { DamageDict = _input2 }; - DamageSpecifier output1 = new() { DamageDict = _output1 }; - DamageSpecifier output2 = new() { DamageDict = _output2 }; - DamageSpecifier output3 = new() { DamageDict = _output3 }; - DamageSpecifier output4 = new() { DamageDict = _output4 }; - DamageSpecifier output5 = new() { DamageDict = _output5 }; + DamageSpecifier input1 = new() { DamageDict = Input1 }; + DamageSpecifier input2 = new() { DamageDict = Input2 }; + DamageSpecifier output1 = new() { DamageDict = Output1 }; + DamageSpecifier output2 = new() { DamageDict = Output2 }; + DamageSpecifier output3 = new() { DamageDict = Output3 }; + DamageSpecifier output4 = new() { DamageDict = Output4 }; + DamageSpecifier output5 = new() { DamageDict = Output5 }; Assert.Multiple(() => { - Assert.That((-input1).Equals(output1)); - Assert.That((input1 / 2).Equals(output2)); - Assert.That((input1 * 2).Equals(output3)); + Assert.That(-input1, Is.EqualTo(output1)); + Assert.That(input1 / 2, Is.EqualTo(output2)); + Assert.That(input1 * 2, Is.EqualTo(output3)); }); - var difference = (input1 - input2); - Assert.That(difference.Equals(output4)); + var difference = input1 - input2; + Assert.That(difference, Is.EqualTo(output4)); - var difference2 = (-input2) + input1; - Assert.That(difference.Equals(difference2)); + var difference2 = -input2 + input1; + Assert.That(difference, Is.EqualTo(difference2)); difference.Clamp(-0.25f, 0.25f); - Assert.That(difference.Equals(output5)); + Assert.That(difference, Is.EqualTo(output5)); } - static Dictionary _input1 = new() + private static readonly Dictionary Input1 = new() { { "A", 1.5f }, { "B", 2 }, { "C", 3 } }; - static Dictionary _input2 = new() + private static readonly Dictionary Input2 = new() { { "A", 1 }, { "B", 2 }, @@ -54,28 +54,28 @@ public void TestDamageSpecifierOperations() { "D", 0.05f } }; - static Dictionary _output1 = new() + private static readonly Dictionary Output1 = new() { { "A", -1.5f }, { "B", -2 }, { "C", -3 } }; - static Dictionary _output2 = new() + private static readonly Dictionary Output2 = new() { { "A", 0.75f }, { "B", 1 }, { "C", 1.5 } }; - static Dictionary _output3 = new() + private static readonly Dictionary Output3 = new() { { "A", 3f }, { "B", 4 }, { "C", 6 } }; - static Dictionary _output4 = new() + private static readonly Dictionary Output4 = new() { { "A", 0.5f }, { "B", 0 }, @@ -83,7 +83,7 @@ public void TestDamageSpecifierOperations() { "D", -0.05f } }; - static Dictionary _output5 = new() + private static readonly Dictionary Output5 = new() { { "A", 0.25f }, { "B", 0 }, diff --git a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs index c40b8ed286f..69069fc82fe 100644 --- a/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs +++ b/Content.IntegrationTests/Tests/Damageable/DamageableTest.cs @@ -107,10 +107,11 @@ public async Task TestDamageableComponents() FixedPoint2 typeDamage; + var map = await pair.CreateTestMap(); + await server.WaitPost(() => { - var map = sMapManager.CreateMap(); - var coordinates = new MapCoordinates(0, 0, map); + var coordinates = map.MapCoords; sDamageableEntity = sEntityManager.SpawnEntity("TestDamageableEntityId", coordinates); sDamageableComponent = sEntityManager.GetComponent(sDamageableEntity); diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs index e14a8264678..a50238d8f50 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs @@ -3,6 +3,7 @@ using Content.Server.Destructible.Thresholds.Behaviors; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; +using Content.Shared.Destructible.Thresholds; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes; diff --git a/Content.IntegrationTests/Tests/DoAfter/DoAfterCancellationTests.cs b/Content.IntegrationTests/Tests/DoAfter/DoAfterCancellationTests.cs index 0ebd17d8879..1aaf4a5184c 100644 --- a/Content.IntegrationTests/Tests/DoAfter/DoAfterCancellationTests.cs +++ b/Content.IntegrationTests/Tests/DoAfter/DoAfterCancellationTests.cs @@ -16,31 +16,31 @@ public sealed class DoAfterCancellationTests : InteractionTest public async Task CancelWallDeconstruct() { await StartDeconstruction(WallConstruction.WallSolid); - await Interact(Weld, awaitDoAfters: false); + await InteractUsing(Weld, awaitDoAfters: false); // Failed do-after has no effect await CancelDoAfters(); AssertPrototype(WallConstruction.WallSolid); // Second attempt works fine - await Interact(Weld); + await InteractUsing(Weld); AssertPrototype(WallConstruction.Girder); // Repeat for wrenching interaction AssertAnchored(); - await Interact(Wrench, awaitDoAfters: false); + await InteractUsing(Wrench, awaitDoAfters: false); await CancelDoAfters(); AssertAnchored(); AssertPrototype(WallConstruction.Girder); - await Interact(Wrench); + await InteractUsing(Wrench); AssertAnchored(false); // Repeat for screwdriver interaction. AssertExists(); - await Interact(Screw, awaitDoAfters: false); + await InteractUsing(Screw, awaitDoAfters: false); await CancelDoAfters(); AssertExists(); - await Interact(Screw); + await InteractUsing(Screw); AssertDeleted(); } @@ -48,17 +48,16 @@ public async Task CancelWallDeconstruct() public async Task CancelWallConstruct() { await StartConstruction(WallConstruction.Wall); - await Interact(Steel, 5, awaitDoAfters: false); + await InteractUsing(Steel, 5, awaitDoAfters: false); await CancelDoAfters(); - await Interact(Steel, 5); - ClientAssertPrototype(WallConstruction.Girder, ClientTarget); - Target = CTestSystem.Ghosts[ClientTarget!.Value.GetHashCode()]; - await Interact(Steel, 5, awaitDoAfters: false); + await InteractUsing(Steel, 5); + ClientAssertPrototype(WallConstruction.Girder, Target); + await InteractUsing(Steel, 5, awaitDoAfters: false); await CancelDoAfters(); AssertPrototype(WallConstruction.Girder); - await Interact(Steel, 5); + await InteractUsing(Steel, 5); AssertPrototype(WallConstruction.WallSolid); } @@ -66,11 +65,11 @@ public async Task CancelWallConstruct() public async Task CancelTilePry() { await SetTile(Floor); - await Interact(Pry, awaitDoAfters: false); + await InteractUsing(Pry, awaitDoAfters: false); await CancelDoAfters(); await AssertTile(Floor); - await Interact(Pry); + await InteractUsing(Pry); await AssertTile(Plating); } @@ -78,7 +77,7 @@ public async Task CancelTilePry() public async Task CancelRepeatedTilePry() { await SetTile(Floor); - await Interact(Pry, awaitDoAfters: false); + await InteractUsing(Pry, awaitDoAfters: false); await RunTicks(1); Assert.That(ActiveDoAfters.Count(), Is.EqualTo(1)); await AssertTile(Floor); @@ -89,7 +88,7 @@ public async Task CancelRepeatedTilePry() await AssertTile(Floor); // Third do after will work fine - await Interact(Pry); + await InteractUsing(Pry); Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0)); await AssertTile(Plating); } @@ -102,7 +101,7 @@ public async Task CancelRepeatedWeld() Assert.That(comp.IsWelded, Is.False); - await Interact(Weld, awaitDoAfters: false); + await InteractUsing(Weld, awaitDoAfters: false); await RunTicks(1); Assert.Multiple(() => { @@ -120,7 +119,7 @@ public async Task CancelRepeatedWeld() }); // Third do after will work fine - await Interact(Weld); + await InteractUsing(Weld); Assert.Multiple(() => { Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0)); @@ -128,7 +127,7 @@ public async Task CancelRepeatedWeld() }); // Repeat test for un-welding - await Interact(Weld, awaitDoAfters: false); + await InteractUsing(Weld, awaitDoAfters: false); await RunTicks(1); Assert.Multiple(() => { @@ -141,7 +140,7 @@ public async Task CancelRepeatedWeld() Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0)); Assert.That(comp.IsWelded, Is.True); }); - await Interact(Weld); + await InteractUsing(Weld); Assert.Multiple(() => { Assert.That(ActiveDoAfters.Count(), Is.EqualTo(0)); diff --git a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs index 9f31231091f..fb77bf18d83 100644 --- a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs +++ b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs @@ -123,24 +123,24 @@ public async Task AirlockBlockTest() var xformSystem = entityManager.System(); PhysicsComponent physBody = null; - EntityUid AirlockPhysicsDummy = default; + EntityUid airlockPhysicsDummy = default; EntityUid airlock = default; DoorComponent doorComponent = null; - var AirlockPhysicsDummyStartingX = -1; + var airlockPhysicsDummyStartingX = -1; + + var map = await pair.CreateTestMap(); await server.WaitAssertion(() => { - var mapId = mapManager.CreateMap(); - - var humanCoordinates = new MapCoordinates(new Vector2(AirlockPhysicsDummyStartingX, 0), mapId); - AirlockPhysicsDummy = entityManager.SpawnEntity("AirlockPhysicsDummy", humanCoordinates); + var humanCoordinates = new MapCoordinates(new Vector2(airlockPhysicsDummyStartingX, 0), map.MapId); + airlockPhysicsDummy = entityManager.SpawnEntity("AirlockPhysicsDummy", humanCoordinates); - airlock = entityManager.SpawnEntity("AirlockDummy", new MapCoordinates(new Vector2(0, 0), mapId)); + airlock = entityManager.SpawnEntity("AirlockDummy", new MapCoordinates(new Vector2(0, 0), map.MapId)); Assert.Multiple(() => { - Assert.That(entityManager.TryGetComponent(AirlockPhysicsDummy, out physBody), Is.True); + Assert.That(entityManager.TryGetComponent(airlockPhysicsDummy, out physBody), Is.True); Assert.That(entityManager.TryGetComponent(airlock, out doorComponent), Is.True); }); Assert.That(doorComponent.State, Is.EqualTo(DoorState.Closed)); @@ -152,7 +152,7 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => Assert.That(physBody, Is.Not.EqualTo(null))); await server.WaitPost(() => { - physicsSystem.SetLinearVelocity(AirlockPhysicsDummy, new Vector2(0.5f, 0f), body: physBody); + physicsSystem.SetLinearVelocity(airlockPhysicsDummy, new Vector2(0.5f, 0f), body: physBody); }); for (var i = 0; i < 240; i += 10) @@ -176,7 +176,7 @@ await server.WaitPost(() => // Blocked by the airlock await server.WaitAssertion(() => { - Assert.That(Math.Abs(xformSystem.GetWorldPosition(AirlockPhysicsDummy).X - 1), Is.GreaterThan(0.01f)); + Assert.That(Math.Abs(xformSystem.GetWorldPosition(airlockPhysicsDummy).X - 1), Is.GreaterThan(0.01f)); }); await pair.CleanReturnAsync(); } diff --git a/Content.IntegrationTests/Tests/DummyIconTest.cs b/Content.IntegrationTests/Tests/DummyIconTest.cs index a11191a51ea..df2d28a2ea2 100644 --- a/Content.IntegrationTests/Tests/DummyIconTest.cs +++ b/Content.IntegrationTests/Tests/DummyIconTest.cs @@ -21,7 +21,7 @@ await client.WaitAssertion(() => { foreach (var proto in prototypeManager.EnumeratePrototypes()) { - if (proto.NoSpawn || proto.Abstract || pair.IsTestPrototype(proto) || !proto.Components.ContainsKey("Sprite")) + if (proto.HideSpawnMenu || proto.Abstract || pair.IsTestPrototype(proto) || !proto.Components.ContainsKey("Sprite")) continue; Assert.DoesNotThrow(() => diff --git a/Content.IntegrationTests/Tests/EncryptionKeys/RemoveEncryptionKeys.cs b/Content.IntegrationTests/Tests/EncryptionKeys/RemoveEncryptionKeys.cs index 9e3dbd8863e..f5e8c22242e 100644 --- a/Content.IntegrationTests/Tests/EncryptionKeys/RemoveEncryptionKeys.cs +++ b/Content.IntegrationTests/Tests/EncryptionKeys/RemoveEncryptionKeys.cs @@ -22,7 +22,7 @@ public async Task HeadsetKeys() }); // Remove the key - await Interact(Screw); + await InteractUsing(Screw); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(0)); @@ -34,7 +34,7 @@ public async Task HeadsetKeys() await AssertEntityLookup(("EncryptionKeyCommon", 1)); // Re-insert a key. - await Interact("EncryptionKeyCentCom"); + await InteractUsing("EncryptionKeyCentCom"); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(1)); @@ -59,7 +59,7 @@ public async Task CommsServerKeys() }); // cannot remove keys without opening panel - await Interact(Pry); + await InteractUsing(Pry); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.GreaterThan(0)); @@ -68,7 +68,7 @@ public async Task CommsServerKeys() }); // Open panel - await Interact(Screw); + await InteractUsing(Screw); Assert.Multiple(() => { Assert.That(panel.Open, Is.True); @@ -79,7 +79,7 @@ public async Task CommsServerKeys() }); // Now remove the keys - await Interact(Pry); + await InteractUsing(Pry); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(0)); @@ -87,7 +87,7 @@ public async Task CommsServerKeys() }); // Reinsert a key - await Interact("EncryptionKeyCentCom"); + await InteractUsing("EncryptionKeyCentCom"); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(1)); @@ -97,7 +97,7 @@ public async Task CommsServerKeys() }); // Remove it again - await Interact(Pry); + await InteractUsing(Pry); Assert.Multiple(() => { Assert.That(comp.KeyContainer.ContainedEntities, Has.Count.EqualTo(0)); @@ -106,7 +106,7 @@ public async Task CommsServerKeys() // Prying again will start deconstructing the machine. AssertPrototype("TelecomServerFilled"); - await Interact(Pry); + await InteractUsing(Pry); AssertPrototype("MachineFrame"); } } diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 926374cf050..56645660673 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -1,15 +1,11 @@ using System.Collections.Generic; using System.Linq; using System.Numerics; -using Content.Server.Humanoid.Components; -using Content.Shared.Coordinates; -using Content.Shared.Prototypes; using Robust.Shared; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Log; using Robust.Shared.Map; -using Robust.Shared.Map.Components; using Robust.Shared.Maths; using Robust.Shared.Prototypes; @@ -47,7 +43,7 @@ await server.WaitPost(() => foreach (var protoId in protoIds) { - var mapId = mapManager.CreateMap(); + mapSystem.CreateMap(out var mapId); var grid = mapManager.CreateGridEntity(mapId); // TODO: Fix this better in engine. mapSystem.SetTile(grid.Owner, grid.Comp, Vector2i.Zero, new Tile(1)); @@ -155,6 +151,7 @@ public async Task SpawnAndDirtyAllEntities() var prototypeMan = server.ResolveDependency(); var mapManager = server.ResolveDependency(); var sEntMan = server.ResolveDependency(); + var mapSys = server.System(); Assert.That(cfg.GetCVar(CVars.NetPVS), Is.False); @@ -170,7 +167,7 @@ await server.WaitPost(() => { foreach (var protoId in protoIds) { - var mapId = mapManager.CreateMap(); + mapSys.CreateMap(out var mapId); var grid = mapManager.CreateGridEntity(mapId); var ent = sEntMan.SpawnEntity(protoId, new EntityCoordinates(grid.Owner, 0.5f, 0.5f)); foreach (var (_, component) in sEntMan.GetNetComponents(ent)) @@ -227,6 +224,7 @@ public async Task SpawnAndDeleteEntityCountTest() var settings = new PoolSettings { Connected = true, Dirty = true }; await using var pair = await PoolManager.GetServerClient(settings); var mapManager = pair.Server.ResolveDependency(); + var mapSys = pair.Server.System(); var server = pair.Server; var client = pair.Client; @@ -256,7 +254,7 @@ public async Task SpawnAndDeleteEntityCountTest() await server.WaitPost(() => { - mapId = mapManager.CreateMap(); + mapSys.CreateMap(out mapId); }); var coords = new MapCoordinates(Vector2.Zero, mapId); diff --git a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs index 6e88d6928e6..d6f9bf35986 100644 --- a/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/FluidSpillTest.cs @@ -16,14 +16,15 @@ namespace Content.IntegrationTests.Tests.Fluids; [TestOf(typeof(SpreaderSystem))] public sealed class FluidSpill { - private static PuddleComponent? GetPuddle(IEntityManager entityManager, MapGridComponent mapGrid, Vector2i pos) + private static PuddleComponent? GetPuddle(IEntityManager entityManager, Entity mapGrid, Vector2i pos) { return GetPuddleEntity(entityManager, mapGrid, pos)?.Comp; } - private static Entity? GetPuddleEntity(IEntityManager entityManager, MapGridComponent mapGrid, Vector2i pos) + private static Entity? GetPuddleEntity(IEntityManager entityManager, Entity mapGrid, Vector2i pos) { - foreach (var uid in mapGrid.GetAnchoredEntities(pos)) + var mapSys = entityManager.System(); + foreach (var uid in mapSys.GetAnchoredEntities(mapGrid, mapGrid.Comp, pos)) { if (entityManager.TryGetComponent(uid, out PuddleComponent? puddleComponent)) return (uid, puddleComponent); @@ -39,9 +40,9 @@ public async Task SpillCorner() var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); - var puddleSystem = server.ResolveDependency().GetEntitySystem(); + var puddleSystem = server.System(); + var mapSystem = server.System(); var gameTiming = server.ResolveDependency(); - MapId mapId; EntityUid gridId = default; /* @@ -52,7 +53,7 @@ . . . */ await server.WaitPost(() => { - mapId = mapManager.CreateMap(); + mapSystem.CreateMap(out var mapId); var grid = mapManager.CreateGridEntity(mapId); gridId = grid.Owner; @@ -60,12 +61,12 @@ await server.WaitPost(() => { for (var y = 0; y < 3; y++) { - grid.Comp.SetTile(new Vector2i(x, y), new Tile(1)); + mapSystem.SetTile(grid, new Vector2i(x, y), new Tile(1)); } } - entityManager.SpawnEntity("WallReinforced", grid.Comp.GridTileToLocal(new Vector2i(0, 1))); - entityManager.SpawnEntity("WallReinforced", grid.Comp.GridTileToLocal(new Vector2i(1, 0))); + entityManager.SpawnEntity("WallReinforced", mapSystem.GridTileToLocal(grid, grid.Comp, new Vector2i(0, 1))); + entityManager.SpawnEntity("WallReinforced", mapSystem.GridTileToLocal(grid, grid.Comp, new Vector2i(1, 0))); }); @@ -74,10 +75,10 @@ await server.WaitAssertion(() => { var grid = entityManager.GetComponent(gridId); var solution = new Solution("Blood", FixedPoint2.New(100)); - var tileRef = grid.GetTileRef(puddleOrigin); + var tileRef = mapSystem.GetTileRef(gridId, grid, puddleOrigin); #pragma warning disable NUnit2045 // Interdependent tests Assert.That(puddleSystem.TrySpillAt(tileRef, solution, out _), Is.True); - Assert.That(GetPuddle(entityManager, grid, puddleOrigin), Is.Not.Null); + Assert.That(GetPuddle(entityManager, (gridId, grid), puddleOrigin), Is.Not.Null); #pragma warning restore NUnit2045 }); @@ -87,7 +88,7 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { var grid = entityManager.GetComponent(gridId); - var puddle = GetPuddleEntity(entityManager, grid, puddleOrigin); + var puddle = GetPuddleEntity(entityManager, (gridId, grid), puddleOrigin); #pragma warning disable NUnit2045 // Interdependent tests Assert.That(puddle, Is.Not.Null); @@ -104,7 +105,7 @@ await server.WaitAssertion(() => } var newPos = new Vector2i(x, y); - var sidePuddle = GetPuddle(entityManager, grid, newPos); + var sidePuddle = GetPuddle(entityManager, (gridId, grid), newPos); Assert.That(sidePuddle, Is.Null); } } diff --git a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs index a9069892dff..ee2d0cb1f7a 100644 --- a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs @@ -5,7 +5,6 @@ using Content.Shared.Fluids.Components; using Robust.Shared.GameObjects; using Robust.Shared.Map; -using Robust.Shared.Map.Components; namespace Content.IntegrationTests.Tests.Fluids { @@ -21,8 +20,7 @@ public async Task TilePuddleTest() var testMap = await pair.CreateTestMap(); - var entitySystemManager = server.ResolveDependency(); - var spillSystem = entitySystemManager.GetEntitySystem(); + var spillSystem = server.System(); await server.WaitAssertion(() => { @@ -46,17 +44,19 @@ public async Task SpaceNoPuddleTest() var server = pair.Server; var testMap = await pair.CreateTestMap(); - var grid = testMap.Grid.Comp; + var grid = testMap.Grid; var entitySystemManager = server.ResolveDependency(); - var spillSystem = entitySystemManager.GetEntitySystem(); + var spillSystem = server.System(); + var mapSystem = server.System(); // Remove all tiles await server.WaitPost(() => { - foreach (var tile in grid.GetAllTiles()) + var tiles = mapSystem.GetAllTiles(grid.Owner, grid.Comp); + foreach (var tile in tiles) { - grid.SetTile(tile.GridIndices, Tile.Empty); + mapSystem.SetTile(grid, tile.GridIndices, Tile.Empty); } }); diff --git a/Content.IntegrationTests/Tests/FollowerSystemTest.cs b/Content.IntegrationTests/Tests/FollowerSystemTest.cs index 4d308c6d911..f4447426c77 100644 --- a/Content.IntegrationTests/Tests/FollowerSystemTest.cs +++ b/Content.IntegrationTests/Tests/FollowerSystemTest.cs @@ -22,6 +22,7 @@ public async Task FollowerMapDeleteTest() var mapMan = server.ResolveDependency(); var sysMan = server.ResolveDependency(); var logMan = server.ResolveDependency(); + var mapSys = server.System(); var logger = logMan.RootSawmill; await server.WaitPost(() => @@ -29,7 +30,7 @@ await server.WaitPost(() => var followerSystem = sysMan.GetEntitySystem(); // Create a map to spawn the observers on. - var map = mapMan.CreateMap(); + mapSys.CreateMap(out var map); // Spawn an observer to be followed. var followed = entMan.SpawnEntity(GameTicker.ObserverPrototypeName, new MapCoordinates(0, 0, map)); @@ -41,7 +42,7 @@ await server.WaitPost(() => followerSystem.StartFollowingEntity(follower, followed); - entMan.DeleteEntity(mapMan.GetMapEntityId(map)); + entMan.DeleteEntity(mapSys.GetMap(map)); }); await pair.CleanReturnAsync(); } diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs index c6a8e618cc1..02245c783e8 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs @@ -1,5 +1,4 @@ #nullable enable -using System.Numerics; using Content.Server.Cuffs; using Content.Shared.Body.Components; using Content.Shared.Cuffs.Components; @@ -7,7 +6,6 @@ using Robust.Server.Console; using Robust.Shared.GameObjects; using Robust.Shared.Map; -using Robust.Shared.Maths; namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking { @@ -51,10 +49,11 @@ public async Task Test() var mapManager = server.ResolveDependency(); var host = server.ResolveDependency(); + var map = await pair.CreateTestMap(); + await server.WaitAssertion(() => { - var mapId = mapManager.CreateMap(); - var coordinates = new MapCoordinates(Vector2.Zero, mapId); + var coordinates = map.MapCoords; var cuffableSys = entityManager.System(); var xformSys = entityManager.System(); diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs index b0aceacc03d..9f04660008c 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs @@ -5,7 +5,6 @@ using Robust.Client.UserInterface; using Robust.Server.Player; using Robust.Shared.GameObjects; -using Robust.Shared.IoC; namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs { @@ -45,8 +44,8 @@ await server.WaitAssertion(() => Assert.That(alerts, Is.Not.Null); var alertCount = alerts.Count; - alertsSystem.ShowAlert(playerUid, AlertType.Debug1); - alertsSystem.ShowAlert(playerUid, AlertType.Debug2); + alertsSystem.ShowAlert(playerUid, "Debug1"); + alertsSystem.ShowAlert(playerUid, "Debug2"); Assert.That(alerts, Has.Count.EqualTo(alertCount + 2)); }); @@ -89,14 +88,14 @@ static AlertsUI FindAlertsUI(Control control) // We should be seeing 2 alerts - the 2 debug alerts, in a specific order. Assert.That(clientAlertsUI.AlertContainer.ChildCount, Is.GreaterThanOrEqualTo(2)); var alertControls = clientAlertsUI.AlertContainer.Children.Select(c => (AlertControl) c); - var alertIDs = alertControls.Select(ac => ac.Alert.AlertType).ToArray(); - var expectedIDs = new[] { AlertType.Debug1, AlertType.Debug2 }; + var alertIDs = alertControls.Select(ac => ac.Alert.ID).ToArray(); + var expectedIDs = new[] { "HumanHealth", "Debug1", "Debug2" }; Assert.That(alertIDs, Is.SupersetOf(expectedIDs)); }); await server.WaitAssertion(() => { - alertsSystem.ClearAlert(playerUid, AlertType.Debug1); + alertsSystem.ClearAlert(playerUid, "Debug1"); }); await pair.RunTicksSync(5); @@ -106,8 +105,8 @@ await client.WaitAssertion(() => // We should be seeing 1 alert now because one was cleared Assert.That(clientAlertsUI.AlertContainer.ChildCount, Is.GreaterThanOrEqualTo(1)); var alertControls = clientAlertsUI.AlertContainer.Children.Select(c => (AlertControl) c); - var alertIDs = alertControls.Select(ac => ac.Alert.AlertType).ToArray(); - var expectedIDs = new[] { AlertType.Debug2 }; + var alertIDs = alertControls.Select(ac => ac.Alert.ID).ToArray(); + var expectedIDs = new[] { "HumanHealth", "Debug2" }; Assert.That(alertIDs, Is.SupersetOf(expectedIDs)); }); diff --git a/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs index 662ea3b9747..889c7868d7c 100644 --- a/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/AntagPreferenceTest.cs @@ -47,7 +47,7 @@ public async Task TestLobbyPlayersValid() Assert.That(sys.IsEntityValid(client.AttachedEntity, def), Is.True); // By default, traitor/antag preferences are disabled, so the pool should be empty. - var sessions = new List{pair.Player!}; + var sessions = new List { pair.Player! }; var pool = sys.GetPlayerPool(rule, sessions, def); Assert.That(pool.Count, Is.EqualTo(0)); diff --git a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs index d0e0255ae77..b4e5d2cd8c5 100644 --- a/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/FailAndStartPresetTest.cs @@ -1,4 +1,4 @@ -// #nullable enable +// #nullable enable // using Content.Server.GameTicking; // using Content.Server.GameTicking.Components; // using Content.Server.GameTicking.Presets; @@ -110,14 +110,14 @@ // player = pair.Player!.AttachedEntity!.Value; // Assert.That(entMan.EntityExists(player)); -// ticker.SetGamePreset((GamePresetPrototype?)null); -// server.CfgMan.SetCVar(CCVars.GridFill, false); -// server.CfgMan.SetCVar(CCVars.GameLobbyFallbackEnabled, true); -// server.CfgMan.SetCVar(CCVars.GameLobbyDefaultPreset, "secret"); -// server.System().Run = false; -// await pair.CleanReturnAsync(); -// } -// } +// ticker.SetGamePreset((GamePresetPrototype?) null); +// server.CfgMan.SetCVar(CCVars.GridFill, false); +// server.CfgMan.SetCVar(CCVars.GameLobbyFallbackEnabled, true); +// server.CfgMan.SetCVar(CCVars.GameLobbyDefaultPreset, "secret"); +// server.System().Run = false; +// await pair.CleanReturnAsync(); +// } +//} // public sealed class TestRuleSystem : EntitySystem // { diff --git a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs index 20a157e33e8..611b038309b 100644 --- a/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs +++ b/Content.IntegrationTests/Tests/GameRules/RuleMaxTimeRestartTest.cs @@ -1,9 +1,9 @@ using Content.Server.GameTicking; using Content.Server.GameTicking.Commands; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Shared.CCVar; +using Content.Shared.GameTicking.Components; using Robust.Shared.Configuration; using Robust.Shared.GameObjects; using Robust.Shared.Timing; @@ -27,8 +27,12 @@ public async Task RestartTest() var sGameTicker = server.ResolveDependency().GetEntitySystem(); var sGameTiming = server.ResolveDependency(); - sGameTicker.StartGameRule("MaxTimeRestart", out var ruleEntity); - Assert.That(entityManager.TryGetComponent(ruleEntity, out var maxTime)); + MaxTimeRestartRuleComponent maxTime = null; + await server.WaitPost(() => + { + sGameTicker.StartGameRule("MaxTimeRestart", out var ruleEntity); + Assert.That(entityManager.TryGetComponent(ruleEntity, out maxTime)); + }); Assert.That(server.EntMan.Count(), Is.EqualTo(1)); Assert.That(server.EntMan.Count(), Is.EqualTo(1)); diff --git a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs index 0ad198d6ef2..74641126aee 100644 --- a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs +++ b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs @@ -1,5 +1,6 @@ using Content.Server.Gravity; using Content.Shared.Alert; +using Content.Shared.Gravity; using Robust.Shared.GameObjects; namespace Content.IntegrationTests.Tests.Gravity @@ -38,6 +39,7 @@ public async Task WeightlessStatusTest() var entityManager = server.ResolveDependency(); var alertsSystem = server.ResolveDependency().GetEntitySystem(); + var weightlessAlert = SharedGravitySystem.WeightlessAlert; EntityUid human = default; @@ -56,7 +58,7 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { // No gravity without a gravity generator - Assert.That(alertsSystem.IsShowingAlert(human, AlertType.Weightless)); + Assert.That(alertsSystem.IsShowingAlert(human, weightlessAlert)); generatorUid = entityManager.SpawnEntity("WeightlessGravityGeneratorDummy", entityManager.GetComponent(human).Coordinates); }); @@ -66,7 +68,7 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - Assert.That(alertsSystem.IsShowingAlert(human, AlertType.Weightless), Is.False); + Assert.That(alertsSystem.IsShowingAlert(human, weightlessAlert), Is.False); // This should kill gravity entityManager.DeleteEntity(generatorUid); @@ -76,7 +78,7 @@ await server.WaitAssertion(() => await server.WaitAssertion(() => { - Assert.That(alertsSystem.IsShowingAlert(human, AlertType.Weightless)); + Assert.That(alertsSystem.IsShowingAlert(human, weightlessAlert)); }); await pair.RunTicksSync(10); diff --git a/Content.IntegrationTests/Tests/GravityGridTest.cs b/Content.IntegrationTests/Tests/GravityGridTest.cs index 7f817e8a1e0..64f7a6d0820 100644 --- a/Content.IntegrationTests/Tests/GravityGridTest.cs +++ b/Content.IntegrationTests/Tests/GravityGridTest.cs @@ -34,29 +34,25 @@ public async Task Test() var testMap = await pair.CreateTestMap(); - EntityUid generator = default; - var entityMan = server.ResolveDependency(); - var mapMan = server.ResolveDependency(); + var entityMan = server.EntMan; + var mapMan = server.MapMan; var mapSys = entityMan.System(); - MapGridComponent grid1 = null; - MapGridComponent grid2 = null; - EntityUid grid1Entity = default!; - EntityUid grid2Entity = default!; + EntityUid generator = default; + Entity grid1 = default; + Entity grid2 = default; // Create grids await server.WaitAssertion(() => { var mapId = testMap.MapId; - grid1 = mapMan.CreateGrid(mapId); - grid2 = mapMan.CreateGrid(mapId); - grid1Entity = grid1.Owner; - grid2Entity = grid2.Owner; + grid1 = mapMan.CreateGridEntity(mapId); + grid2 = mapMan.CreateGridEntity(mapId); - mapSys.SetTile(grid1Entity, grid1, Vector2i.Zero, new Tile(1)); - mapSys.SetTile(grid2Entity, grid2, Vector2i.Zero, new Tile(1)); + mapSys.SetTile(grid1, grid1, Vector2i.Zero, new Tile(1)); + mapSys.SetTile(grid2, grid2, Vector2i.Zero, new Tile(1)); - generator = entityMan.SpawnEntity("GridGravityGeneratorDummy", new EntityCoordinates(grid1Entity, 0.5f, 0.5f)); + generator = entityMan.SpawnEntity("GridGravityGeneratorDummy", new EntityCoordinates(grid1, 0.5f, 0.5f)); Assert.Multiple(() => { Assert.That(entityMan.HasComponent(generator)); @@ -77,8 +73,8 @@ await server.WaitAssertion(() => Assert.Multiple(() => { Assert.That(generatorComponent.GravityActive, Is.True); - Assert.That(!entityMan.GetComponent(grid1Entity).EnabledVV); - Assert.That(entityMan.GetComponent(grid2Entity).EnabledVV); + Assert.That(!entityMan.GetComponent(grid1).EnabledVV); + Assert.That(entityMan.GetComponent(grid2).EnabledVV); }); // Re-enable needs power so it turns off again. @@ -95,7 +91,7 @@ await server.WaitAssertion(() => Assert.Multiple(() => { Assert.That(generatorComponent.GravityActive, Is.False); - Assert.That(entityMan.GetComponent(grid2Entity).EnabledVV, Is.False); + Assert.That(entityMan.GetComponent(grid2).EnabledVV, Is.False); }); }); diff --git a/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs b/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs index 4415eddf376..456df3b2f31 100644 --- a/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs +++ b/Content.IntegrationTests/Tests/Interaction/Click/InteractionSystemTests.cs @@ -47,13 +47,9 @@ public async Task InteractionTest() var sysMan = server.ResolveDependency(); var handSys = sysMan.GetEntitySystem(); - var mapId = MapId.Nullspace; - var coords = MapCoordinates.Nullspace; - await server.WaitAssertion(() => - { - mapId = mapManager.CreateMap(); - coords = new MapCoordinates(Vector2.Zero, mapId); - }); + var map = await pair.CreateTestMap(); + var mapId = map.MapId; + var coords = map.MapCoords; await server.WaitIdleAsync(); EntityUid user = default; @@ -117,13 +113,9 @@ public async Task InteractionObstructionTest() var sysMan = server.ResolveDependency(); var handSys = sysMan.GetEntitySystem(); - var mapId = MapId.Nullspace; - var coords = MapCoordinates.Nullspace; - await server.WaitAssertion(() => - { - mapId = mapManager.CreateMap(); - coords = new MapCoordinates(Vector2.Zero, mapId); - }); + var map = await pair.CreateTestMap(); + var mapId = map.MapId; + var coords = map.MapCoords; await server.WaitIdleAsync(); EntityUid user = default; @@ -188,13 +180,9 @@ public async Task InteractionInRangeTest() var sysMan = server.ResolveDependency(); var handSys = sysMan.GetEntitySystem(); - var mapId = MapId.Nullspace; - var coords = MapCoordinates.Nullspace; - await server.WaitAssertion(() => - { - mapId = mapManager.CreateMap(); - coords = new MapCoordinates(Vector2.Zero, mapId); - }); + var map = await pair.CreateTestMap(); + var mapId = map.MapId; + var coords = map.MapCoords; await server.WaitIdleAsync(); EntityUid user = default; @@ -258,13 +246,9 @@ public async Task InteractionOutOfRangeTest() var sysMan = server.ResolveDependency(); var handSys = sysMan.GetEntitySystem(); - var mapId = MapId.Nullspace; - var coords = MapCoordinates.Nullspace; - await server.WaitAssertion(() => - { - mapId = mapManager.CreateMap(); - coords = new MapCoordinates(Vector2.Zero, mapId); - }); + var map = await pair.CreateTestMap(); + var mapId = map.MapId; + var coords = map.MapCoords; await server.WaitIdleAsync(); EntityUid user = default; @@ -328,13 +312,9 @@ public async Task InsideContainerInteractionBlockTest() var handSys = sysMan.GetEntitySystem(); var conSystem = sysMan.GetEntitySystem(); - var mapId = MapId.Nullspace; - var coords = MapCoordinates.Nullspace; - await server.WaitAssertion(() => - { - mapId = mapManager.CreateMap(); - coords = new MapCoordinates(Vector2.Zero, mapId); - }); + var map = await pair.CreateTestMap(); + var mapId = map.MapId; + var coords = map.MapCoords; await server.WaitIdleAsync(); EntityUid user = default; diff --git a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs index b8828763a23..e5ac0f785aa 100644 --- a/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs +++ b/Content.IntegrationTests/Tests/Interaction/InRangeUnobstructed.cs @@ -37,10 +37,11 @@ public async Task EntityEntityTest() EntityUid other = default; MapCoordinates mapCoordinates = default; + var map = await pair.CreateTestMap(); + await server.WaitAssertion(() => { - var mapId = mapManager.CreateMap(); - var coordinates = new MapCoordinates(Vector2.Zero, mapId); + var coordinates = map.MapCoords; origin = sEntities.SpawnEntity(HumanId, coordinates); other = sEntities.SpawnEntity(HumanId, coordinates); diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs index 37dca721373..194bc54fba6 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.EntitySpecifier.cs @@ -33,7 +33,7 @@ protected sealed class EntitySpecifier public int Quantity; /// - /// If true, a check has been performed to see if the prototype ia an entity prototype with a stack component, + /// If true, a check has been performed to see if the prototype is an entity prototype with a stack component, /// in which case the specifier was converted into a stack-specifier /// public bool Converted; @@ -100,7 +100,7 @@ await Server.WaitPost(() => if (!ProtoMan.TryIndex(spec.Prototype, out var entProto)) { - Assert.Fail($"Unkown prototype: {spec.Prototype}"); + Assert.Fail($"Unknown prototype: {spec.Prototype}"); return default; } @@ -114,13 +114,13 @@ await Server.WaitPost(() => return await SpawnEntity((stack.StackTypeId, spec.Quantity), coords); Assert.That(spec.Quantity, Is.EqualTo(1), "SpawnEntity only supports returning a singular entity"); - await Server.WaitPost(() => uid = SEntMan.SpawnEntity(spec.Prototype, coords)); + await Server.WaitPost(() => uid = SEntMan.SpawnAtPosition(spec.Prototype, coords)); return uid; } /// /// Convert an entity-uid to a matching entity specifier. Useful when doing entity lookups & checking that the - /// right quantity of entities/materials werre produced. Returns null if passed an entity with a null prototype. + /// right quantity of entities/materials were produced. Returns null if passed an entity with a null prototype. /// protected EntitySpecifier? ToEntitySpecifier(EntityUid uid) { diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs index 19ca83a9715..a19b62cd70a 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.Helpers.cs @@ -14,6 +14,7 @@ using Content.Shared.Gravity; using Content.Shared.Item; using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Shared.GameObjects; using Robust.Shared.Input; @@ -44,8 +45,9 @@ await Client.WaitPost(() => return; var comp = CEntMan.GetComponent(clientTarget!.Value); - ClientTarget = clientTarget; - ConstructionGhostId = comp.Owner.Id; + Target = CEntMan.GetNetEntity(clientTarget.Value); + Assert.That(Target.Value.IsClientSide()); + ConstructionGhostId = clientTarget.Value.GetHashCode(); }); await RunTicks(1); @@ -82,18 +84,21 @@ protected async Task CraftItem(string prototype, bool shouldSucceed = true) /// /// Spawn an entity entity and set it as the target. /// - [MemberNotNull(nameof(Target))] - protected async Task SpawnTarget(string prototype) + [MemberNotNull(nameof(Target), nameof(STarget), nameof(CTarget))] +#pragma warning disable CS8774 // Member must have a non-null value when exiting. + protected async Task SpawnTarget(string prototype) { Target = NetEntity.Invalid; await Server.WaitPost(() => { - Target = SEntMan.GetNetEntity(SEntMan.SpawnEntity(prototype, SEntMan.GetCoordinates(TargetCoords))); + Target = SEntMan.GetNetEntity(SEntMan.SpawnAtPosition(prototype, SEntMan.GetCoordinates(TargetCoords))); }); await RunTicks(5); AssertPrototype(prototype); + return Target!.Value; } +#pragma warning restore CS8774 // Member must have a non-null value when exiting. /// /// Spawn an entity in preparation for deconstruction @@ -129,21 +134,20 @@ await Server.WaitPost(() => /// /// Place an entity prototype into the players hand. Deletes any currently held entity. /// - /// - /// Automatically enables welders. - /// - protected async Task PlaceInHands(string id, int quantity = 1, bool enableWelder = true) + /// The entity or stack prototype to spawn and place into the users hand + /// The number of entities to spawn. If the prototype is a stack, this sets the stack count. + /// Whether or not to automatically enable any toggleable items + protected async Task PlaceInHands(string id, int quantity = 1, bool enableToggleable = true) { - return await PlaceInHands((id, quantity), enableWelder); + return await PlaceInHands((id, quantity), enableToggleable); } /// /// Place an entity prototype into the players hand. Deletes any currently held entity. /// - /// - /// Automatically enables welders. - /// - protected async Task PlaceInHands(EntitySpecifier entity, bool enableWelder = true) + /// The entity type & quantity to spawn and place into the users hand + /// Whether or not to automatically enable any toggleable items + protected async Task PlaceInHands(EntitySpecifier entity, bool enableToggleable = true) { if (Hands.ActiveHand == null) { @@ -165,7 +169,7 @@ await Server.WaitPost(() => Assert.That(HandSys.TryPickup(playerEnt, item, Hands.ActiveHand, false, false, Hands)); // turn on welders - if (enableWelder && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated) + if (enableToggleable && SEntMan.TryGetComponent(item, out itemToggle) && !itemToggle.Activated) { Assert.That(ItemToggleSys.TryActivate(item, playerEnt, itemToggle: itemToggle)); } @@ -173,7 +177,7 @@ await Server.WaitPost(() => await RunTicks(1); Assert.That(Hands.ActiveHandEntity, Is.EqualTo(item)); - if (enableWelder && itemToggle != null) + if (enableToggleable && itemToggle != null) Assert.That(itemToggle.Activated); return SEntMan.GetNetEntity(item); @@ -254,21 +258,20 @@ await Server.WaitPost(() => /// /// Place an entity prototype into the players hand and interact with the given entity (or target position) /// - /// - /// Empty strings imply empty hands. - /// - protected async Task Interact(string id, int quantity = 1, bool shouldSucceed = true, bool awaitDoAfters = true) + /// The entity or stack prototype to spawn and place into the users hand + /// The number of entities to spawn. If the prototype is a stack, this sets the stack count. + /// Whether or not to wait for any do-afters to complete + protected async Task InteractUsing(string id, int quantity = 1, bool awaitDoAfters = true) { - await Interact((id, quantity), shouldSucceed, awaitDoAfters); + await InteractUsing((id, quantity), awaitDoAfters); } /// - /// Place an entity prototype into the players hand and interact with the given entity (or target position) + /// Place an entity prototype into the players hand and interact with the given entity (or target position). /// - /// - /// Empty strings imply empty hands. - /// - protected async Task Interact(EntitySpecifier entity, bool shouldSucceed = true, bool awaitDoAfters = true) + /// The entity type & quantity to spawn and place into the users hand + /// Whether or not to wait for any do-afters to complete + protected async Task InteractUsing(EntitySpecifier entity, bool awaitDoAfters = true) { // For every interaction, we will also examine the entity, just in case this breaks something, somehow. // (e.g., servers attempt to assemble construction examine hints). @@ -278,38 +281,80 @@ protected async Task Interact(EntitySpecifier entity, bool shouldSucceed = true, } await PlaceInHands(entity); - await Interact(shouldSucceed, awaitDoAfters); + await Interact(awaitDoAfters); } /// /// Interact with an entity using the currently held entity. /// - protected async Task Interact(bool shouldSucceed = true, bool awaitDoAfters = true) + /// Whether or not to wait for any do-afters to complete + protected async Task Interact(bool awaitDoAfters = true) { - var clientTarget = ClientTarget; - - if ((clientTarget?.IsValid() != true || CEntMan.Deleted(clientTarget)) && (Target == null || Target.Value.IsValid())) + if (Target == null || !Target.Value.IsClientSide()) { - await Server.WaitPost(() => InteractSys.UserInteraction(SEntMan.GetEntity(Player), SEntMan.GetCoordinates(TargetCoords), SEntMan.GetEntity(Target))); - await RunTicks(1); + await Interact(Target, TargetCoords, awaitDoAfters); + return; } - else - { - // The entity is client-side, so attempt to start construction - var clientEnt = ClientTarget ?? CEntMan.GetEntity(Target); - await Client.WaitPost(() => CConSys.TryStartConstruction(clientEnt!.Value)); - await RunTicks(5); - } + // The target is a client-side entity, so we will just attempt to start construction under the assumption that + // it is a construction ghost. + + await Client.WaitPost(() => CConSys.TryStartConstruction(CTarget!.Value)); + await RunTicks(5); + + if (awaitDoAfters) + await AwaitDoAfters(); + + await CheckTargetChange(); + } + + /// + protected async Task Interact(NetEntity? target, NetCoordinates coordinates, bool awaitDoAfters = true) + { + Assert.That(SEntMan.TryGetEntity(target, out var sTarget) || target == null); + var coords = SEntMan.GetCoordinates(coordinates); + Assert.That(coords.IsValid(SEntMan)); + await Interact(sTarget, coords, awaitDoAfters); + } + + /// + /// Interact with an entity using the currently held entity. + /// + protected async Task Interact(EntityUid? target, EntityCoordinates coordinates, bool awaitDoAfters = true) + { + Assert.That(SEntMan.TryGetEntity(Player, out var player)); + + await Server.WaitPost(() => InteractSys.UserInteraction(player!.Value, coordinates, target)); + await RunTicks(1); if (awaitDoAfters) - await AwaitDoAfters(shouldSucceed); + await AwaitDoAfters(); - await CheckTargetChange(shouldSucceed && awaitDoAfters); + await CheckTargetChange(); } /// - /// Variant of that performs several interactions using different entities. + /// Activate an entity. + /// + protected async Task Activate(NetEntity? target = null, bool awaitDoAfters = true) + { + target ??= Target; + Assert.That(target, Is.Not.Null); + Assert.That(SEntMan.TryGetEntity(target!.Value, out var sTarget)); + Assert.That(SEntMan.TryGetEntity(Player, out var player)); + + await Server.WaitPost(() => InteractSys.InteractionActivate(player!.Value, sTarget!.Value)); + await RunTicks(1); + + if (awaitDoAfters) + await AwaitDoAfters(); + + await CheckTargetChange(); + } + + /// + /// Variant of that performs several interactions using different entities. + /// Useful for quickly finishing multiple construction steps. /// /// /// Empty strings imply empty hands. @@ -318,7 +363,7 @@ protected async Task Interact(params EntitySpecifier[] specifiers) { foreach (var spec in specifiers) { - await Interact(spec); + await InteractUsing(spec); } } @@ -338,7 +383,7 @@ protected async Task ThrowItem(NetCoordinates? target = null, float minDis /// /// Wait for any currently active DoAfters to finish. /// - protected async Task AwaitDoAfters(bool shouldSucceed = true, int maxExpected = 1) + protected async Task AwaitDoAfters(int maxExpected = 1) { if (!ActiveDoAfters.Any()) return; @@ -353,13 +398,12 @@ protected async Task AwaitDoAfters(bool shouldSucceed = true, int maxExpected = await RunTicks(10); } - if (!shouldSucceed) - return; - foreach (var doAfter in doAfters) { Assert.That(!doAfter.Cancelled); } + + await RunTicks(5); } /// @@ -398,39 +442,28 @@ await Server.WaitPost(() => /// Check if the test's target entity has changed. E.g., construction interactions will swap out entities while /// a structure is being built. /// - protected async Task CheckTargetChange(bool shouldSucceed) + protected async Task CheckTargetChange() { if (Target == null) return; - var target = Target.Value; + var originalTarget = Target.Value; await RunTicks(5); - if (ClientTarget != null && CEntMan.IsClientSide(ClientTarget.Value)) + if (Target.Value.IsClientSide() && CTestSystem.Ghosts.TryGetValue(ConstructionGhostId, out var newWeh)) { - Assert.That(CEntMan.Deleted(ClientTarget.Value), Is.EqualTo(shouldSucceed), - $"Construction ghost was {(shouldSucceed ? "not deleted" : "deleted")}."); - - if (shouldSucceed) - { - Assert.That(CTestSystem.Ghosts.TryGetValue(ConstructionGhostId, out var newWeh), - $"Failed to get construction entity from ghost Id"); - - await Client.WaitPost(() => CLogger.Debug($"Construction ghost {ConstructionGhostId} became entity {newWeh}")); - Target = newWeh; - } + CLogger.Debug($"Construction ghost {ConstructionGhostId} became entity {newWeh}"); + Target = newWeh; } if (STestSystem.EntChanges.TryGetValue(Target.Value, out var newServerWeh)) { - await Server.WaitPost( - () => SLogger.Debug($"Construction entity {Target.Value} changed to {newServerWeh}")); - + SLogger.Debug($"Construction entity {Target.Value} changed to {newServerWeh}"); Target = newServerWeh; } - if (Target != target) - await CheckTargetChange(shouldSucceed); + if (Target != originalTarget) + await CheckTargetChange(); } #region Asserts @@ -444,16 +477,10 @@ protected void ClientAssertPrototype(string? prototype, NetEntity? target = null return; } - var meta = SEntMan.GetComponent(SEntMan.GetEntity(target.Value)); + var meta = CEntMan.GetComponent(CEntMan.GetEntity(target.Value)); Assert.That(meta.EntityPrototype?.ID, Is.EqualTo(prototype)); } - protected void ClientAssertPrototype(string? prototype, EntityUid? target) - { - var netEnt = CTestSystem.Ghosts[target.GetHashCode()]; - AssertPrototype(prototype, netEnt); - } - protected void AssertPrototype(string? prototype, NetEntity? target = null) { target ??= Target; @@ -544,11 +571,11 @@ protected async Task AssertTile(string? proto, NetCoordinates? coords = null) var tile = Tile.Empty; var serverCoords = SEntMan.GetCoordinates(coords ?? TargetCoords); - var pos = serverCoords.ToMap(SEntMan, Transform); + var pos = Transform.ToMapCoordinates(serverCoords); await Server.WaitPost(() => { - if (MapMan.TryFindGridAt(pos, out _, out var grid)) - tile = grid.GetTileRef(serverCoords).Tile; + if (MapMan.TryFindGridAt(pos, out var gridUid, out var grid)) + tile = MapSystem.GetTileRef(gridUid, grid, serverCoords).Tile; }); Assert.That(tile.TypeId, Is.EqualTo(targetTile.TypeId)); @@ -699,6 +726,8 @@ protected async Task FindEntity( protected IEnumerable ActiveDoAfters => DoAfters.DoAfters.Values.Where(x => !x.Cancelled && !x.Completed); + #region Component + /// /// Convenience method to get components on the target. Returns SERVER-SIDE components. /// @@ -708,39 +737,61 @@ protected T Comp(NetEntity? target = null) where T : IComponent if (target == null) Assert.Fail("No target specified"); - return SEntMan.GetComponent(SEntMan.GetEntity(target!.Value)); + return SEntMan.GetComponent(ToServer(target!.Value)); } + /// + protected bool TryComp(NetEntity? target, [NotNullWhen(true)] out T? comp) where T : IComponent + { + return SEntMan.TryGetComponent(ToServer(target), out comp); + } + + /// + protected bool TryComp([NotNullWhen(true)] out T? comp) where T : IComponent + { + return SEntMan.TryGetComponent(STarget, out comp); + } + + #endregion + /// /// Set the tile at the target position to some prototype. /// - protected async Task SetTile(string? proto, NetCoordinates? coords = null, MapGridComponent? grid = null) + protected async Task SetTile(string? proto, NetCoordinates? coords = null, Entity? grid = null) { var tile = proto == null ? Tile.Empty : new Tile(TileMan[proto].TileId); - var pos = SEntMan.GetCoordinates(coords ?? TargetCoords).ToMap(SEntMan, Transform); + var pos = Transform.ToMapCoordinates(SEntMan.GetCoordinates(coords ?? TargetCoords)); + EntityUid gridUid; + MapGridComponent? gridComp; await Server.WaitPost(() => { - if (grid != null || MapMan.TryFindGridAt(pos, out var gridUid, out grid)) + if (grid is { } gridEnt) + { + MapSystem.SetTile(gridEnt, SEntMan.GetCoordinates(coords ?? TargetCoords), tile); + return; + } + else if (MapMan.TryFindGridAt(pos, out var gUid, out var gComp)) { - grid.SetTile(SEntMan.GetCoordinates(coords ?? TargetCoords), tile); + MapSystem.SetTile(gUid, gComp, SEntMan.GetCoordinates(coords ?? TargetCoords), tile); return; } if (proto == null) return; - var gridEnt = MapMan.CreateGridEntity(MapData.MapId); + gridEnt = MapMan.CreateGridEntity(MapData.MapId); grid = gridEnt; gridUid = gridEnt; + gridComp = gridEnt.Comp; var gridXform = SEntMan.GetComponent(gridUid); Transform.SetWorldPosition(gridXform, pos.Position); - grid.SetTile(SEntMan.GetCoordinates(coords ?? TargetCoords), tile); + MapSystem.SetTile((gridUid, gridComp), SEntMan.GetCoordinates(coords ?? TargetCoords), tile); - if (!MapMan.TryFindGridAt(pos, out _, out grid)) + if (!MapMan.TryFindGridAt(pos, out _, out _)) Assert.Fail("Failed to create grid?"); }); await AssertTile(proto, coords); @@ -833,23 +884,70 @@ protected bool TryGetBui(Enum key, [NotNullWhen(true)] out BoundUserInterface? b return true; } + protected bool IsUiOpen(Enum key) + { + if (!TryComp(Player, out UserInterfaceUserComponent? user)) + return false; + + foreach (var keys in user.OpenInterfaces.Values) + { + if (keys.Contains(key)) + return true; + } + + return false; + } + #endregion #region UI /// - /// Presses and releases a button on some client-side window. Will fail if the button cannot be found. + /// Attempts to find, and then presses and releases a control on some client-side window. + /// Will fail if the control cannot be found. /// - protected async Task ClickControl(string name) where TWindow : BaseWindow + protected async Task ClickControl(string name, BoundKeyFunction? function = null) + where TWindow : BaseWindow + where TControl : Control { - await ClickControl(GetControl(name)); + var window = GetWindow(); + var control = GetControlFromField(name, window); + await ClickControl(control, function); } /// - /// Simulates a click and release at the center of some UI Constrol. + /// Attempts to find, and then presses and releases a control on some client-side widget. + /// Will fail if the control cannot be found. /// - protected async Task ClickControl(Control control) + protected async Task ClickWidgetControl(string name, BoundKeyFunction? function = null) + where TWidget : UIWidget, new() + where TControl : Control { + var widget = GetWidget(); + var control = GetControlFromField(name, widget); + await ClickControl(control, function); + } + + /// + protected async Task ClickControl(string name, BoundKeyFunction? function = null) + where TWindow : BaseWindow + { + await ClickControl(name, function); + } + + /// + protected async Task ClickWidgetControl(string name, BoundKeyFunction? function = null) + where TWidget : UIWidget, new() + { + await ClickWidgetControl(name, function); + } + + /// + /// Simulates a click and release at the center of some UI control. + /// + protected async Task ClickControl(Control control, BoundKeyFunction? function = null) + { + function ??= EngineKeyFunctions.UIClick; var screenCoords = new ScreenCoordinates( control.GlobalPixelPosition + control.PixelSize / 2, control.Window?.Id ?? default); @@ -858,7 +956,7 @@ protected async Task ClickControl(Control control) var relativePixelPos = screenCoords.Position - control.GlobalPixelPosition; var args = new GUIBoundKeyEventArgs( - EngineKeyFunctions.UIClick, + function.Value, BoundKeyState.Down, screenCoords, default, @@ -869,7 +967,7 @@ protected async Task ClickControl(Control control) await RunTicks(1); args = new GUIBoundKeyEventArgs( - EngineKeyFunctions.UIClick, + function.Value, BoundKeyState.Up, screenCoords, default, @@ -881,31 +979,26 @@ protected async Task ClickControl(Control control) } /// - /// Attempts to find a control on some client-side window. Will fail if the control cannot be found. + /// Attempt to retrieve a control by looking for a field on some other control. /// - protected TControl GetControl(string name) - where TWindow : BaseWindow + /// + /// Will fail if the control cannot be found. + /// + protected TControl GetControlFromField(string name, Control parent) where TControl : Control - { - var control = GetControl(name); - Assert.That(control.GetType().IsAssignableTo(typeof(TControl))); - return (TControl) control; - } - - protected Control GetControl(string name) where TWindow : BaseWindow { const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - var field = typeof(TWindow).GetField(name, flags); - var prop = typeof(TWindow).GetProperty(name, flags); + var parentType = parent.GetType(); + var field = parentType.GetField(name, flags); + var prop = parentType.GetProperty(name, flags); if (field == null && prop == null) { - Assert.Fail($"Window {typeof(TWindow).Name} does not have a field or property named {name}"); + Assert.Fail($"Window {parentType.Name} does not have a field or property named {name}"); return default!; } - var window = GetWindow(); - var fieldOrProp = field?.GetValue(window) ?? prop?.GetValue(window); + var fieldOrProp = field?.GetValue(parent) ?? prop?.GetValue(parent); if (fieldOrProp is not Control control) { @@ -913,7 +1006,59 @@ protected Control GetControl(string name) where TWindow : BaseWindow return default!; } - return control; + Assert.That(control.GetType().IsAssignableTo(typeof(TControl))); + return (TControl) control; + } + + /// + /// Attempt to retrieve a control that matches some predicate by iterating through a control's children. + /// + /// + /// Will fail if the control cannot be found. + /// + protected TControl GetControlFromChildren(Func predicate, Control parent, bool recursive = true) + where TControl : Control + { + if (TryGetControlFromChildren(predicate, parent, out var control, recursive)) + return control; + + Assert.Fail($"Failed to find a {nameof(TControl)} that satisfies the predicate in {parent.Name}"); + return default!; + } + + /// + /// Attempt to retrieve a control of a given type by iterating through a control's children. + /// + protected TControl GetControlFromChildren(Control parent, bool recursive = false) + where TControl : Control + { + return GetControlFromChildren(static _ => true, parent, recursive); + } + + /// + /// Attempt to retrieve a control that matches some predicate by iterating through a control's children. + /// + protected bool TryGetControlFromChildren( + Func predicate, + Control parent, + [NotNullWhen(true)] out TControl? control, + bool recursive = true) + where TControl : Control + { + foreach (var ctrl in parent.Children) + { + if (ctrl is TControl cast && predicate(cast)) + { + control = cast; + return true; + } + + if (recursive && TryGetControlFromChildren(predicate, ctrl, out control)) + return true; + } + + control = null; + return false; } /// @@ -944,7 +1089,6 @@ protected bool TryFindWindow([NotNullWhen(true)] out TWindow? window) w return window != null; } - /// /// Attempts to find a currently open client-side window. /// @@ -962,6 +1106,34 @@ protected bool TryFindWindow(Type type, [NotNullWhen(true)] out BaseWindow? wind return window != null; } + + /// + /// Attempts to find client-side UI widget. + /// + protected UIWidget GetWidget() + where TWidget : UIWidget, new() + { + if (TryFindWidget(out TWidget? widget)) + return widget; + + Assert.Fail($"Could not find a {typeof(TWidget).Name} widget"); + return default!; + } + + /// + /// Attempts to find client-side UI widget. + /// + private bool TryFindWidget([NotNullWhen(true)] out TWidget? uiWidget) + where TWidget : UIWidget, new() + { + uiWidget = null; + var screen = UiMan.ActiveScreen; + if (screen == null) + return false; + + return screen.TryGetWidget(out uiWidget); + } + #endregion #region Power @@ -1009,14 +1181,17 @@ await Server.WaitPost(() => #region Inputs + + /// /// Make the client press and then release a key. This assumes the key is currently released. + /// This will default to using the entity and coordinates. /// protected async Task PressKey( BoundKeyFunction key, int ticks = 1, NetCoordinates? coordinates = null, - NetEntity cursorEntity = default) + NetEntity? cursorEntity = null) { await SetKey(key, BoundKeyState.Down, coordinates, cursorEntity); await RunTicks(ticks); @@ -1025,15 +1200,17 @@ protected async Task PressKey( } /// - /// Make the client press or release a key + /// Make the client press or release a key. + /// This will default to using the entity and coordinates. /// protected async Task SetKey( BoundKeyFunction key, BoundKeyState state, NetCoordinates? coordinates = null, - NetEntity cursorEntity = default) + NetEntity? cursorEntity = null) { var coords = coordinates ?? TargetCoords; + var target = cursorEntity ?? Target ?? default; ScreenCoordinates screen = default; var funcId = InputManager.NetworkBindMap.KeyFunctionID(key); @@ -1042,7 +1219,7 @@ protected async Task SetKey( State = state, Coordinates = CEntMan.GetCoordinates(coords), ScreenCoordinates = screen, - Uid = CEntMan.GetEntity(cursorEntity), + Uid = CEntMan.GetEntity(target), }; await Client.WaitPost(() => InputSystem.HandleInputCommand(ClientSession, key, message)); diff --git a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs index 42f64b344cd..f07f23d84c8 100644 --- a/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs +++ b/Content.IntegrationTests/Tests/Interaction/InteractionTest.cs @@ -3,6 +3,7 @@ using System.Numerics; using Content.Client.Construction; using Content.Client.Examine; +using Content.Client.Gameplay; using Content.IntegrationTests.Pair; using Content.Server.Body.Systems; using Content.Server.Hands.Systems; @@ -24,6 +25,7 @@ using Robust.Shared.Timing; using Robust.UnitTesting; using Content.Shared.Item.ItemToggle; +using Robust.Client.State; namespace Content.IntegrationTests.Tests.Interaction; @@ -64,15 +66,12 @@ public abstract partial class InteractionTest /// The player entity that performs all these interactions. Defaults to an admin-observer with 1 hand. /// protected NetEntity Player; - - protected EntityUid SPlayer => ToServer(Player); - protected EntityUid CPlayer => ToClient(Player); + protected EntityUid SPlayer; + protected EntityUid CPlayer; protected ICommonSession ClientSession = default!; protected ICommonSession ServerSession = default!; - public EntityUid? ClientTarget; - /// /// The current target entity. This is the default entity for various helper functions. /// @@ -84,6 +83,7 @@ public abstract partial class InteractionTest protected NetEntity? Target; protected EntityUid? STarget => ToServer(Target); + protected EntityUid? CTarget => ToClient(Target); /// @@ -107,7 +107,9 @@ public abstract partial class InteractionTest protected SharedItemToggleSystem ItemToggleSys = default!; protected InteractionTestSystem STestSystem = default!; protected SharedTransformSystem Transform = default!; + protected SharedMapSystem MapSystem = default!; protected ISawmill SLogger = default!; + protected SharedUserInterfaceSystem SUiSys = default!; // CLIENT dependencies protected IEntityManager CEntMan = default!; @@ -119,6 +121,7 @@ public abstract partial class InteractionTest protected ExamineSystem ExamineSys = default!; protected InteractionTestSystem CTestSystem = default!; protected ISawmill CLogger = default!; + protected SharedUserInterfaceSystem CUiSys = default!; // player components protected HandsComponent Hands = default!; @@ -126,7 +129,6 @@ public abstract partial class InteractionTest public float TickPeriod => (float) STiming.TickPeriod.TotalSeconds; - // Simple mob that has one hand and can perform misc interactions. [TestPrototypes] private const string TestPrototypes = @" @@ -139,6 +141,8 @@ public abstract partial class InteractionTest - type: Hands - type: MindContainer - type: Stripping + - type: Puller + - type: Physics - type: Tag tags: - CanPilot @@ -148,7 +152,7 @@ public abstract partial class InteractionTest [SetUp] public virtual async Task Setup() { - Pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, Dirty = true}); + Pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, Dirty = true }); // server dependencies SEntMan = Server.ResolveDependency(); @@ -163,10 +167,12 @@ public virtual async Task Setup() ItemToggleSys = SEntMan.System(); DoAfterSys = SEntMan.System(); Transform = SEntMan.System(); + MapSystem = SEntMan.System(); SConstruction = SEntMan.System(); STestSystem = SEntMan.System(); Stack = SEntMan.System(); SLogger = Server.ResolveDependency().RootSawmill; + SUiSys = Client.System(); // client dependencies CEntMan = Client.ResolveDependency(); @@ -178,12 +184,14 @@ public virtual async Task Setup() CConSys = CEntMan.System(); ExamineSys = CEntMan.System(); CLogger = Client.ResolveDependency().RootSawmill; + CUiSys = Client.System(); // Setup map. await Pair.CreateTestMap(); - PlayerCoords = SEntMan.GetNetCoordinates(MapData.GridCoords.Offset(new Vector2(0.5f, 0.5f)).WithEntityId(MapData.MapUid, Transform, SEntMan)); - TargetCoords = SEntMan.GetNetCoordinates(MapData.GridCoords.Offset(new Vector2(1.5f, 0.5f)).WithEntityId(MapData.MapUid, Transform, SEntMan)); - await SetTile(Plating, grid: MapData.Grid.Comp); + + PlayerCoords = SEntMan.GetNetCoordinates(Transform.WithEntityId(MapData.GridCoords.Offset(new Vector2(0.5f, 0.5f)), MapData.MapUid)); + TargetCoords = SEntMan.GetNetCoordinates(Transform.WithEntityId(MapData.GridCoords.Offset(new Vector2(1.5f, 0.5f)), MapData.MapUid)); + await SetTile(Plating, grid: MapData.Grid); // Get player data var sPlayerMan = Server.ResolveDependency(); @@ -202,16 +210,17 @@ await Server.WaitPost(() => SEntMan.System().WipeMind(ServerSession.ContentData()?.Mind); old = cPlayerMan.LocalEntity; - Player = SEntMan.GetNetEntity(SEntMan.SpawnEntity(PlayerPrototype, SEntMan.GetCoordinates(PlayerCoords))); - var serverPlayerEnt = SEntMan.GetEntity(Player); - Server.PlayerMan.SetAttachedEntity(ServerSession, serverPlayerEnt); - Hands = SEntMan.GetComponent(serverPlayerEnt); - DoAfters = SEntMan.GetComponent(serverPlayerEnt); + SPlayer = SEntMan.SpawnEntity(PlayerPrototype, SEntMan.GetCoordinates(PlayerCoords)); + Player = SEntMan.GetNetEntity(SPlayer); + Server.PlayerMan.SetAttachedEntity(ServerSession, SPlayer); + Hands = SEntMan.GetComponent(SPlayer); + DoAfters = SEntMan.GetComponent(SPlayer); }); // Check player got attached. await RunTicks(5); - Assert.That(CEntMan.GetNetEntity(cPlayerMan.LocalEntity), Is.EqualTo(Player)); + CPlayer = ToClient(Player); + Assert.That(cPlayerMan.LocalEntity, Is.EqualTo(CPlayer)); // Delete old player entity. await Server.WaitPost(() => @@ -234,6 +243,10 @@ await Server.WaitPost(() => } }); + // Change UI state to in-game. + var state = Client.ResolveDependency(); + await Client.WaitPost(() => state.RequestStateChange()); + // Final player asserts/checks. await Pair.ReallyBeIdle(5); Assert.Multiple(() => @@ -251,7 +264,8 @@ public async Task TearDownInternal() await TearDown(); } - protected virtual async Task TearDown() + protected virtual Task TearDown() { + return Task.CompletedTask; } } diff --git a/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs b/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs index 30724b50a6d..0632fe1347c 100644 --- a/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs +++ b/Content.IntegrationTests/Tests/Linter/StaticFieldValidationTest.cs @@ -26,26 +26,26 @@ public async Task TestStaticFieldValidation() protos.Add(kind, ids); } - Assert.That(protoMan.ValidateStaticFields(typeof(StringValid), protos).Count, Is.Zero); - Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayValid), protos).Count, Is.Zero); - Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdValid), protos).Count, Is.Zero); - Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayValid), protos).Count, Is.Zero); - Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestValid), protos).Count, Is.Zero); - Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayValid), protos).Count, Is.Zero); - Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListValid), protos).Count, Is.Zero); - Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdSetValid), protos).Count, Is.Zero); - Assert.That(protoMan.ValidateStaticFields(typeof(PrivateProtoIdArrayValid), protos).Count, Is.Zero); - - Assert.That(protoMan.ValidateStaticFields(typeof(StringInvalid), protos).Count, Is.EqualTo(1)); - Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayInvalid), protos).Count, Is.EqualTo(2)); - Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdInvalid), protos).Count, Is.EqualTo(1)); - Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayInvalid), protos).Count, Is.EqualTo(2)); - Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestInvalid), protos).Count, Is.EqualTo(1)); - Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayInvalid), protos).Count, Is.EqualTo(2)); - Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListInvalid), protos).Count, Is.EqualTo(2)); - Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdSetInvalid), protos).Count, Is.EqualTo(2)); - Assert.That(protoMan.ValidateStaticFields(typeof(PrivateProtoIdArrayInvalid), protos).Count, Is.EqualTo(2)); - + Assert.That(protoMan.ValidateStaticFields(typeof(StringValid), protos), Is.Empty); + Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayValid), protos), Is.Empty); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdValid), protos), Is.Empty); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayValid), protos), Is.Empty); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestValid), protos), Is.Empty); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayValid), protos), Is.Empty); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListValid), protos), Is.Empty); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdSetValid), protos), Is.Empty); + Assert.That(protoMan.ValidateStaticFields(typeof(PrivateProtoIdArrayValid), protos), Is.Empty); + + Assert.That(protoMan.ValidateStaticFields(typeof(StringInvalid), protos), Has.Count.EqualTo(1)); + Assert.That(protoMan.ValidateStaticFields(typeof(StringArrayInvalid), protos), Has.Count.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdInvalid), protos), Has.Count.EqualTo(1)); + Assert.That(protoMan.ValidateStaticFields(typeof(EntProtoIdArrayInvalid), protos), Has.Count.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdTestInvalid), protos), Has.Count.EqualTo(1)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdArrayInvalid), protos), Has.Count.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdListInvalid), protos), Has.Count.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(ProtoIdSetInvalid), protos), Has.Count.EqualTo(2)); + Assert.That(protoMan.ValidateStaticFields(typeof(PrivateProtoIdArrayInvalid), protos), Has.Count.EqualTo(2)); + await pair.CleanReturnAsync(); } @@ -58,93 +58,111 @@ public async Task TestStaticFieldValidation() id: StaticFieldTestTag "; - [Reflect(false)] private sealed class StringValid + [Reflect(false)] + private sealed class StringValid { [ValidatePrototypeId] public static string Tag = "StaticFieldTestTag"; } - [Reflect(false)] private sealed class StringInvalid + [Reflect(false)] + private sealed class StringInvalid { [ValidatePrototypeId] public static string Tag = string.Empty; } - [Reflect(false)] private sealed class StringArrayValid + [Reflect(false)] + private sealed class StringArrayValid { - [ValidatePrototypeId] public static string[] Tag = {"StaticFieldTestTag", "StaticFieldTestTag"}; + [ValidatePrototypeId] public static string[] Tag = ["StaticFieldTestTag", "StaticFieldTestTag"]; } - [Reflect(false)] private sealed class StringArrayInvalid + [Reflect(false)] + private sealed class StringArrayInvalid { - [ValidatePrototypeId] public static string[] Tag = {string.Empty, "StaticFieldTestTag", string.Empty}; + [ValidatePrototypeId] public static string[] Tag = [string.Empty, "StaticFieldTestTag", string.Empty]; } - [Reflect(false)] private sealed class EntProtoIdValid + [Reflect(false)] + private sealed class EntProtoIdValid { public static EntProtoId Tag = "StaticFieldTestEnt"; } - [Reflect(false)] private sealed class EntProtoIdInvalid + [Reflect(false)] + private sealed class EntProtoIdInvalid { public static EntProtoId Tag = string.Empty; } - [Reflect(false)] private sealed class EntProtoIdArrayValid + [Reflect(false)] + private sealed class EntProtoIdArrayValid { - public static EntProtoId[] Tag = {"StaticFieldTestEnt", "StaticFieldTestEnt"}; + public static EntProtoId[] Tag = ["StaticFieldTestEnt", "StaticFieldTestEnt"]; } - [Reflect(false)] private sealed class EntProtoIdArrayInvalid + [Reflect(false)] + private sealed class EntProtoIdArrayInvalid { - public static EntProtoId[] Tag = {string.Empty, "StaticFieldTestEnt", string.Empty}; + public static EntProtoId[] Tag = [string.Empty, "StaticFieldTestEnt", string.Empty]; } - [Reflect(false)] private sealed class ProtoIdTestValid + [Reflect(false)] + private sealed class ProtoIdTestValid { public static ProtoId Tag = "StaticFieldTestTag"; } - [Reflect(false)] private sealed class ProtoIdTestInvalid + [Reflect(false)] + private sealed class ProtoIdTestInvalid { public static ProtoId Tag = string.Empty; } - [Reflect(false)] private sealed class ProtoIdArrayValid + [Reflect(false)] + private sealed class ProtoIdArrayValid { - public static ProtoId[] Tag = {"StaticFieldTestTag", "StaticFieldTestTag"}; + public static ProtoId[] Tag = ["StaticFieldTestTag", "StaticFieldTestTag"]; } - [Reflect(false)] private sealed class ProtoIdArrayInvalid + [Reflect(false)] + private sealed class ProtoIdArrayInvalid { - public static ProtoId[] Tag = {string.Empty, "StaticFieldTestTag", string.Empty}; + public static ProtoId[] Tag = [string.Empty, "StaticFieldTestTag", string.Empty]; } - [Reflect(false)] private sealed class ProtoIdListValid + [Reflect(false)] + private sealed class ProtoIdListValid { - public static List> Tag = new() {"StaticFieldTestTag", "StaticFieldTestTag"}; + public static List> Tag = ["StaticFieldTestTag", "StaticFieldTestTag"]; } - [Reflect(false)] private sealed class ProtoIdListInvalid + [Reflect(false)] + private sealed class ProtoIdListInvalid { - public static List> Tag = new() {string.Empty, "StaticFieldTestTag", string.Empty}; + public static List> Tag = [string.Empty, "StaticFieldTestTag", string.Empty]; } - [Reflect(false)] private sealed class ProtoIdSetValid + [Reflect(false)] + private sealed class ProtoIdSetValid { - public static HashSet> Tag = new() {"StaticFieldTestTag", "StaticFieldTestTag"}; + public static HashSet> Tag = ["StaticFieldTestTag", "StaticFieldTestTag"]; } - [Reflect(false)] private sealed class ProtoIdSetInvalid + [Reflect(false)] + private sealed class ProtoIdSetInvalid { - public static HashSet> Tag = new() {string.Empty, "StaticFieldTestTag", string.Empty, " "}; + public static HashSet> Tag = [string.Empty, "StaticFieldTestTag", string.Empty, " "]; } - [Reflect(false)] private sealed class PrivateProtoIdArrayValid + [Reflect(false)] + private sealed class PrivateProtoIdArrayValid { - private static ProtoId[] Tag = {"StaticFieldTestTag", "StaticFieldTestTag"}; + private static readonly ProtoId[] Tag = ["StaticFieldTestTag", "StaticFieldTestTag"]; } - [Reflect(false)] private sealed class PrivateProtoIdArrayInvalid + [Reflect(false)] + private sealed class PrivateProtoIdArrayInvalid { - private static ProtoId[] Tag = {string.Empty, "StaticFieldTestTag", string.Empty}; + private static readonly ProtoId[] Tag = [string.Empty, "StaticFieldTestTag", string.Empty]; } } diff --git a/Content.IntegrationTests/Tests/MachineBoardTest.cs b/Content.IntegrationTests/Tests/MachineBoardTest.cs index bd3a72f4c1d..097f38af420 100644 --- a/Content.IntegrationTests/Tests/MachineBoardTest.cs +++ b/Content.IntegrationTests/Tests/MachineBoardTest.cs @@ -2,6 +2,7 @@ using System.Linq; using Content.Server.Construction.Components; using Content.Shared.Construction.Components; +using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; @@ -35,6 +36,7 @@ public async Task TestMachineBoardHasValidMachine() var server = pair.Server; var protoMan = server.ResolveDependency(); + var compFact = server.ResolveDependency(); await server.WaitAssertion(() => { @@ -43,7 +45,7 @@ await server.WaitAssertion(() => .Where(p => !pair.IsTestPrototype(p)) .Where(p => !_ignoredPrototypes.Contains(p.ID))) { - if (!p.TryGetComponent(out var mbc)) + if (!p.TryGetComponent(out var mbc, compFact)) continue; var mId = mbc.Prototype; @@ -52,7 +54,7 @@ await server.WaitAssertion(() => Assert.That(mId, Is.Not.Null, $"Machine board {p.ID} does not have a corresponding machine."); Assert.That(protoMan.TryIndex(mId, out var mProto), $"Machine board {p.ID}'s corresponding machine has an invalid prototype."); - Assert.That(mProto.TryGetComponent(out var mComp), + Assert.That(mProto.TryGetComponent(out var mComp, compFact), $"Machine board {p.ID}'s corresponding machine {mId} does not have MachineComponent"); Assert.That(mComp.BoardPrototype, Is.EqualTo(p.ID), $"Machine {mId}'s BoardPrototype is not equal to it's corresponding machine board, {p.ID}"); @@ -74,6 +76,7 @@ public async Task TestComputerBoardHasValidComputer() var server = pair.Server; var protoMan = server.ResolveDependency(); + var compFact = server.ResolveDependency(); await server.WaitAssertion(() => { @@ -82,7 +85,7 @@ await server.WaitAssertion(() => .Where(p => !pair.IsTestPrototype(p)) .Where(p => !_ignoredPrototypes.Contains(p.ID))) { - if (!p.TryGetComponent(out var cbc)) + if (!p.TryGetComponent(out var cbc, compFact)) continue; var cId = cbc.Prototype; @@ -91,7 +94,7 @@ await server.WaitAssertion(() => Assert.That(cId, Is.Not.Null, $"Computer board \"{p.ID}\" does not have a corresponding computer."); Assert.That(protoMan.TryIndex(cId, out var cProto), $"Computer board \"{p.ID}\"'s corresponding computer has an invalid prototype."); - Assert.That(cProto.TryGetComponent(out var cComp), + Assert.That(cProto.TryGetComponent(out var cComp, compFact), $"Computer board {p.ID}'s corresponding computer \"{cId}\" does not have ComputerComponent"); Assert.That(cComp.BoardPrototype, Is.EqualTo(p.ID), $"Computer \"{cId}\"'s BoardPrototype is not equal to it's corresponding computer board, \"{p.ID}\""); diff --git a/Content.IntegrationTests/Tests/Mapping/MappingTests.cs b/Content.IntegrationTests/Tests/Mapping/MappingTests.cs index 287e30eb8b1..be8bad229b4 100644 --- a/Content.IntegrationTests/Tests/Mapping/MappingTests.cs +++ b/Content.IntegrationTests/Tests/Mapping/MappingTests.cs @@ -13,7 +13,7 @@ public sealed class MappingTests [Test] public async Task MappingTest() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings {Dirty = true, Connected = true, DummyTicker = false}); + await using var pair = await PoolManager.GetServerClient(new PoolSettings { Dirty = true, Connected = true, DummyTicker = false }); var server = pair.Server; var entMan = server.EntMan; diff --git a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs index 7f9c02fc13b..12b395f86d3 100644 --- a/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs +++ b/Content.IntegrationTests/Tests/MaterialArbitrageTest.cs @@ -38,15 +38,16 @@ public async Task NoMaterialArbitrage() await server.WaitIdleAsync(); var entManager = server.ResolveDependency(); - var sysManager = server.ResolveDependency(); var mapManager = server.ResolveDependency(); - Assert.That(mapManager.IsMapInitialized(testMap.MapId)); - var protoManager = server.ResolveDependency(); - var pricing = sysManager.GetEntitySystem(); - var stackSys = sysManager.GetEntitySystem(); + + var pricing = entManager.System(); + var stackSys = entManager.System(); + var mapSystem = server.System(); var compFact = server.ResolveDependency(); + Assert.That(mapSystem.IsInitialized(testMap.MapId)); + var constructionName = compFact.GetComponentName(typeof(ConstructionComponent)); var compositionName = compFact.GetComponentName(typeof(PhysicalCompositionComponent)); var materialName = compFact.GetComponentName(typeof(MaterialComponent)); @@ -66,7 +67,7 @@ public async Task NoMaterialArbitrage() Dictionary constructionRecipes = new(); foreach (var proto in protoManager.EnumeratePrototypes()) { - if (proto.NoSpawn || proto.Abstract || pair.IsTestPrototype(proto)) + if (proto.HideSpawnMenu || proto.Abstract || pair.IsTestPrototype(proto)) continue; if (!proto.Components.TryGetValue(constructionName, out var destructible)) @@ -126,7 +127,7 @@ public async Task NoMaterialArbitrage() // Here we get the set of entities/materials spawned when destroying an entity. foreach (var proto in protoManager.EnumeratePrototypes()) { - if (proto.NoSpawn || proto.Abstract || pair.IsTestPrototype(proto)) + if (proto.HideSpawnMenu || proto.Abstract || pair.IsTestPrototype(proto)) continue; if (!proto.Components.TryGetValue(destructibleName, out var destructible)) @@ -297,7 +298,7 @@ public async Task NoMaterialArbitrage() Dictionary physicalCompositions = new(); foreach (var proto in protoManager.EnumeratePrototypes()) { - if (proto.NoSpawn || proto.Abstract || pair.IsTestPrototype(proto)) + if (proto.HideSpawnMenu || proto.Abstract || pair.IsTestPrototype(proto)) continue; if (!proto.Components.TryGetValue(compositionName, out var composition)) diff --git a/Content.IntegrationTests/Tests/Minds/GhostTests.cs b/Content.IntegrationTests/Tests/Minds/GhostTests.cs index ad9d53a70db..3a860267e55 100644 --- a/Content.IntegrationTests/Tests/Minds/GhostTests.cs +++ b/Content.IntegrationTests/Tests/Minds/GhostTests.cs @@ -14,7 +14,7 @@ namespace Content.IntegrationTests.Tests.Minds; [TestFixture] public sealed class GhostTests { - struct GhostTestData + private struct GhostTestData { public IEntityManager SEntMan; public Robust.Server.Player.IPlayerManager SPlayerMan; @@ -23,10 +23,10 @@ struct GhostTestData public TestPair Pair = default!; - public TestMapData MapData => Pair.TestMap!; + public readonly TestMapData MapData => Pair.TestMap!; - public RobustIntegrationTest.ServerIntegrationInstance Server => Pair.Server; - public RobustIntegrationTest.ClientIntegrationInstance Client => Pair.Client; + public readonly RobustIntegrationTest.ServerIntegrationInstance Server => Pair.Server; + public readonly RobustIntegrationTest.ClientIntegrationInstance Client => Pair.Client; /// /// Initial player coordinates. Note that this does not necessarily correspond to the position of the @@ -47,15 +47,16 @@ public GhostTestData() private async Task SetupData() { - var data = new GhostTestData(); - - // Client is needed to create a session for the ghost system. Creating a dummy session was too difficult. - data.Pair = await PoolManager.GetServerClient(new PoolSettings + var data = new GhostTestData { - DummyTicker = false, - Connected = true, - Dirty = true - }); + // Client is needed to create a session for the ghost system. Creating a dummy session was too difficult. + Pair = await PoolManager.GetServerClient(new PoolSettings + { + DummyTicker = false, + Connected = true, + Dirty = true + }) + }; data.SEntMan = data.Pair.Server.ResolveDependency(); data.SPlayerMan = data.Pair.Server.ResolveDependency(); @@ -64,7 +65,8 @@ private async Task SetupData() // Setup map. await data.Pair.CreateTestMap(); - data.PlayerCoords = data.SEntMan.GetNetCoordinates(data.MapData.GridCoords.Offset(new Vector2(0.5f, 0.5f)).WithEntityId(data.MapData.MapUid, data.STransformSys, data.SEntMan)); + var test = data.MapData.GridCoords.Offset(new Vector2(0.5f, 0.5f)); + data.PlayerCoords = data.SEntMan.GetNetCoordinates(data.STransformSys.WithEntityId(data.MapData.GridCoords.Offset(new Vector2(0.5f, 0.5f)), data.MapData.MapUid)); if (data.Client.Session == null) Assert.Fail("No player"); @@ -156,4 +158,20 @@ public async Task TestGridGhostOnQueueDelete() await data.Pair.CleanReturnAsync(); } + [Test] + public async Task TestGhostGridNotTerminating() + { + var data = await SetupData(); + + Assert.DoesNotThrowAsync(async () => + { + // Delete the grid + await data.Server.WaitPost(() => data.SEntMan.DeleteEntity(data.MapData.Grid.Owner)); + }); + + await data.Pair.RunTicksSync(5); + + await data.Pair.CleanReturnAsync(); + } + } diff --git a/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs b/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs index 428380631d7..b12c90e16e2 100644 --- a/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs +++ b/Content.IntegrationTests/Tests/Minds/MindTests.Helpers.cs @@ -169,7 +169,7 @@ private static async Task Connect(Pair.TestPair pair, string username) { var netManager = pair.Client.ResolveDependency(); var playerMan = pair.Server.ResolveDependency(); - Assert.That(!playerMan.Sessions.Any()); + Assert.That(playerMan.Sessions, Is.Empty); await Task.WhenAll(pair.Client.WaitIdleAsync(), pair.Client.WaitIdleAsync()); pair.Client.SetConnectTarget(pair.Server); diff --git a/Content.IntegrationTests/Tests/Movement/BuckleMovementTest.cs b/Content.IntegrationTests/Tests/Movement/BuckleMovementTest.cs new file mode 100644 index 00000000000..3119ee55924 --- /dev/null +++ b/Content.IntegrationTests/Tests/Movement/BuckleMovementTest.cs @@ -0,0 +1,63 @@ +using Content.Shared.Alert; +using Content.Shared.Buckle.Components; +using Robust.Shared.Maths; + +namespace Content.IntegrationTests.Tests.Movement; + +public sealed class BuckleMovementTest : MovementTest +{ + // Check that interacting with a chair straps you to it and prevents movement. + [Test] + public async Task ChairTest() + { + await SpawnTarget("Chair"); + + var cAlert = Client.System(); + var sAlert = Server.System(); + var buckle = Comp(Player); + var strap = Comp(Target); + +#pragma warning disable RA0002 + buckle.Delay = TimeSpan.Zero; +#pragma warning restore RA0002 + + // Initially not buckled to the chair, and standing off to the side + Assert.That(Delta(), Is.InRange(0.9f, 1.1f)); + Assert.That(buckle.Buckled, Is.False); + Assert.That(buckle.BuckledTo, Is.Null); + Assert.That(strap.BuckledEntities, Is.Empty); + Assert.That(cAlert.IsShowingAlert(CPlayer, strap.BuckledAlertType), Is.False); + Assert.That(sAlert.IsShowingAlert(SPlayer, strap.BuckledAlertType), Is.False); + + // Interact results in being buckled to the chair + await Interact(); + Assert.That(Delta(), Is.InRange(-0.01f, 0.01f)); + Assert.That(buckle.Buckled, Is.True); + Assert.That(buckle.BuckledTo, Is.EqualTo(STarget)); + Assert.That(strap.BuckledEntities, Is.EquivalentTo(new[] { SPlayer })); + Assert.That(cAlert.IsShowingAlert(CPlayer, strap.BuckledAlertType), Is.True); + Assert.That(sAlert.IsShowingAlert(SPlayer, strap.BuckledAlertType), Is.True); + + // Attempting to walk away does nothing + await Move(DirectionFlag.East, 1); + Assert.That(Delta(), Is.InRange(-0.01f, 0.01f)); + Assert.That(buckle.Buckled, Is.True); + Assert.That(buckle.BuckledTo, Is.EqualTo(STarget)); + Assert.That(strap.BuckledEntities, Is.EquivalentTo(new[] { SPlayer })); + Assert.That(cAlert.IsShowingAlert(CPlayer, strap.BuckledAlertType), Is.True); + Assert.That(sAlert.IsShowingAlert(SPlayer, strap.BuckledAlertType), Is.True); + + // Interacting again will unbuckle the player + await Interact(); + Assert.That(Delta(), Is.InRange(-0.5f, 0.5f)); + Assert.That(buckle.Buckled, Is.False); + Assert.That(buckle.BuckledTo, Is.Null); + Assert.That(strap.BuckledEntities, Is.Empty); + Assert.That(cAlert.IsShowingAlert(CPlayer, strap.BuckledAlertType), Is.False); + Assert.That(sAlert.IsShowingAlert(SPlayer, strap.BuckledAlertType), Is.False); + + // And now they can move away + await Move(DirectionFlag.SouthEast, 1); + Assert.That(Delta(), Is.LessThan(-1)); + } +} diff --git a/Content.IntegrationTests/Tests/Interaction/MovementTest.cs b/Content.IntegrationTests/Tests/Movement/MovementTest.cs similarity index 93% rename from Content.IntegrationTests/Tests/Interaction/MovementTest.cs rename to Content.IntegrationTests/Tests/Movement/MovementTest.cs index dc5aec92cfc..eba92530388 100644 --- a/Content.IntegrationTests/Tests/Interaction/MovementTest.cs +++ b/Content.IntegrationTests/Tests/Movement/MovementTest.cs @@ -1,8 +1,9 @@ #nullable enable using System.Numerics; +using Content.IntegrationTests.Tests.Interaction; using Robust.Shared.GameObjects; -namespace Content.IntegrationTests.Tests.Interaction; +namespace Content.IntegrationTests.Tests.Movement; /// /// This is a variation of that sets up the player with a normal human entity and a simple @@ -31,7 +32,7 @@ public override async Task Setup() for (var i = -Tiles; i <= Tiles; i++) { - await SetTile(Plating, SEntMan.GetNetCoordinates(pCoords.Offset(new Vector2(i, 0))), MapData.Grid.Comp); + await SetTile(Plating, SEntMan.GetNetCoordinates(pCoords.Offset(new Vector2(i, 0))), MapData.Grid); } AssertGridCount(1); diff --git a/Content.IntegrationTests/Tests/Movement/PullingTest.cs b/Content.IntegrationTests/Tests/Movement/PullingTest.cs new file mode 100644 index 00000000000..d96c4ea0e56 --- /dev/null +++ b/Content.IntegrationTests/Tests/Movement/PullingTest.cs @@ -0,0 +1,73 @@ +#nullable enable +using Content.Shared.Alert; +using Content.Shared.Input; +using Content.Shared.Movement.Pulling.Components; +using Robust.Shared.Maths; + +namespace Content.IntegrationTests.Tests.Movement; + +public sealed class PullingTest : MovementTest +{ + protected override int Tiles => 4; + + [Test] + public async Task PullTest() + { + var cAlert = Client.System(); + var sAlert = Server.System(); + await SpawnTarget("MobHuman"); + + var puller = Comp(Player); + var pullable = Comp(Target); + + // Player is initially to the left of the target and not pulling anything + Assert.That(Delta(), Is.InRange(0.9f, 1.1f)); + Assert.That(puller.Pulling, Is.Null); + Assert.That(pullable.Puller, Is.Null); + Assert.That(pullable.BeingPulled, Is.False); + Assert.That(cAlert.IsShowingAlert(CPlayer, puller.PullingAlert), Is.False); + Assert.That(sAlert.IsShowingAlert(SPlayer, puller.PullingAlert), Is.False); + + // Start pulling + await PressKey(ContentKeyFunctions.TryPullObject); + await RunTicks(5); + Assert.That(puller.Pulling, Is.EqualTo(STarget)); + Assert.That(pullable.Puller, Is.EqualTo(SPlayer)); + Assert.That(pullable.BeingPulled, Is.True); + Assert.That(cAlert.IsShowingAlert(CPlayer, puller.PullingAlert), Is.True); + Assert.That(sAlert.IsShowingAlert(SPlayer, puller.PullingAlert), Is.True); + + // Move to the left and check that the target moves with the player and is still being pulled. + await Move(DirectionFlag.West, 1); + Assert.That(Delta(), Is.InRange(0.9f, 1.3f)); + Assert.That(puller.Pulling, Is.EqualTo(STarget)); + Assert.That(pullable.Puller, Is.EqualTo(SPlayer)); + Assert.That(pullable.BeingPulled, Is.True); + Assert.That(cAlert.IsShowingAlert(CPlayer, puller.PullingAlert), Is.True); + Assert.That(sAlert.IsShowingAlert(SPlayer, puller.PullingAlert), Is.True); + + // Move in the other direction + await Move(DirectionFlag.East, 2); + Assert.That(Delta(), Is.InRange(-1.3f, -0.9f)); + Assert.That(puller.Pulling, Is.EqualTo(STarget)); + Assert.That(pullable.Puller, Is.EqualTo(SPlayer)); + Assert.That(pullable.BeingPulled, Is.True); + Assert.That(cAlert.IsShowingAlert(CPlayer, puller.PullingAlert), Is.True); + Assert.That(sAlert.IsShowingAlert(SPlayer, puller.PullingAlert), Is.True); + + // Stop pulling + await PressKey(ContentKeyFunctions.ReleasePulledObject); + await RunTicks(5); + Assert.That(Delta(), Is.InRange(-1.3f, -0.9f)); + Assert.That(puller.Pulling, Is.Null); + Assert.That(pullable.Puller, Is.Null); + Assert.That(pullable.BeingPulled, Is.False); + Assert.That(cAlert.IsShowingAlert(CPlayer, puller.PullingAlert), Is.False); + Assert.That(sAlert.IsShowingAlert(SPlayer, puller.PullingAlert), Is.False); + + // Move back to the left and ensure the target is no longer following us. + await Move(DirectionFlag.West, 2); + Assert.That(Delta(), Is.GreaterThan(2f)); + } +} + diff --git a/Content.IntegrationTests/Tests/Slipping/SlippingTest.cs b/Content.IntegrationTests/Tests/Movement/SlippingTest.cs similarity index 92% rename from Content.IntegrationTests/Tests/Slipping/SlippingTest.cs rename to Content.IntegrationTests/Tests/Movement/SlippingTest.cs index 28da7a94658..9ac84a0a586 100644 --- a/Content.IntegrationTests/Tests/Slipping/SlippingTest.cs +++ b/Content.IntegrationTests/Tests/Movement/SlippingTest.cs @@ -11,7 +11,7 @@ using Robust.Shared.IoC; using Robust.Shared.Maths; -namespace Content.IntegrationTests.Tests.Slipping; +namespace Content.IntegrationTests.Tests.Movement; public sealed class SlippingTest : MovementTest { @@ -41,18 +41,14 @@ public async Task BananaSlipTest() // Assert.That(modifier, Is.EqualTo(1), "Player is not moving at full speed."); // Yeeting this pointless Assert because it's not actually important. // Player is to the left of the banana peel and has not slipped. -#pragma warning disable NUnit2045 Assert.That(Delta(), Is.GreaterThan(0.5f)); Assert.That(sys.Slipped, Does.Not.Contain(SEntMan.GetEntity(Player))); -#pragma warning restore NUnit2045 // Walking over the banana slowly does not trigger a slip. await SetKey(EngineKeyFunctions.Walk, sprintWalks ? BoundKeyState.Up : BoundKeyState.Down); await Move(DirectionFlag.East, 1f); -#pragma warning disable NUnit2045 Assert.That(Delta(), Is.LessThan(0.5f)); Assert.That(sys.Slipped, Does.Not.Contain(SEntMan.GetEntity(Player))); -#pragma warning restore NUnit2045 AssertComp(false, Player); // Moving at normal speeds does trigger a slip. diff --git a/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs b/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs index 4783d21a053..b3955698489 100644 --- a/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs +++ b/Content.IntegrationTests/Tests/Networking/PvsCommandTest.cs @@ -7,12 +7,12 @@ namespace Content.IntegrationTests.Tests.Networking; [TestFixture] public sealed class PvsCommandTest { - public static EntProtoId TestEnt = "MobHuman"; + private static readonly EntProtoId TestEnt = "MobHuman"; [Test] public async Task TestPvsCommands() { - await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false}); + await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true, DummyTicker = false }); var (server, client) = pair; await pair.RunTicksSync(5); diff --git a/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs b/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs index 52d464fa41e..29f2573c2d9 100644 --- a/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs +++ b/Content.IntegrationTests/Tests/Networking/SimplePredictReconcileTest.cs @@ -51,11 +51,12 @@ public async Task Test() PredictionTestComponent clientComponent = default!; var serverSystem = sEntityManager.System(); var clientSystem = cEntityManager.System(); + var sMapSys = sEntityManager.System(); await server.WaitPost(() => { // Spawn dummy component entity. - var map = sMapManager.CreateMap(); + sMapSys.CreateMap(out var map); serverEnt = sEntityManager.SpawnEntity(null, new MapCoordinates(new Vector2(0, 0), map)); serverComponent = sEntityManager.AddComponent(serverEnt); }); @@ -67,7 +68,7 @@ await server.WaitPost(() => Assert.That(sGameTiming.TickTimingAdjustment, Is.EqualTo(0)); // Check client buffer is full - Assert.That(cGameStateManager.CurrentBufferSize, Is.EqualTo(cGameStateManager.TargetBufferSize)); + Assert.That(cGameStateManager.GetApplicableStateCount(), Is.EqualTo(cGameStateManager.TargetBufferSize)); Assert.That(cGameStateManager.TargetBufferSize, Is.EqualTo(2)); // This isn't required anymore, but the test had this for the sake of "technical things", and I cbf shifting @@ -99,7 +100,7 @@ await client.WaitPost(() => // Client last ran tick 15 meaning it's ahead of the last server tick it processed (12) Assert.That(cGameTiming.CurTick, Is.EqualTo(expected)); - Assert.That(cGameTiming.LastProcessedTick, Is.EqualTo(new GameTick((uint)(baseTick - cGameStateManager.TargetBufferSize)))); + Assert.That(cGameTiming.LastProcessedTick, Is.EqualTo(new GameTick((uint) (baseTick - cGameStateManager.TargetBufferSize)))); }); // *** I am using block scopes to visually distinguish these sections of the test to make it more readable. @@ -264,7 +265,7 @@ await client.WaitPost(() => // Assert timing is still correct. Assert.That(sGameTiming.CurTick, Is.EqualTo(new GameTick(baseTick + 8))); Assert.That(cGameTiming.CurTick, Is.EqualTo(new GameTick(baseTick + 8 + delta))); - Assert.That(cGameTiming.LastProcessedTick, Is.EqualTo(new GameTick((uint)(baseTick + 8 - cGameStateManager.TargetBufferSize)))); + Assert.That(cGameTiming.LastProcessedTick, Is.EqualTo(new GameTick((uint) (baseTick + 8 - cGameStateManager.TargetBufferSize)))); }); { diff --git a/Content.IntegrationTests/Tests/Payload/ModularGrenadeTests.cs b/Content.IntegrationTests/Tests/Payload/ModularGrenadeTests.cs index 70179fdec1a..4db79373d38 100644 --- a/Content.IntegrationTests/Tests/Payload/ModularGrenadeTests.cs +++ b/Content.IntegrationTests/Tests/Payload/ModularGrenadeTests.cs @@ -22,32 +22,32 @@ public async Task AssembleAndDetonateGrenade() Target = SEntMan.GetNetEntity(await FindEntity("ModularGrenade")); await Drop(); - await Interact(Cable); + await InteractUsing(Cable); // Insert & remove trigger AssertComp(false); - await Interact(Trigger); + await InteractUsing(Trigger); AssertComp(); await FindEntity(Trigger, LookupFlags.Uncontained, shouldSucceed: false); - await Interact(Pry); + await InteractUsing(Pry); AssertComp(false); // Trigger was dropped to floor, not deleted. await FindEntity(Trigger, LookupFlags.Uncontained); // Re-insert - await Interact(Trigger); + await InteractUsing(Trigger); AssertComp(); // Insert & remove payload. - await Interact(Payload); + await InteractUsing(Payload); await FindEntity(Payload, LookupFlags.Uncontained, shouldSucceed: false); - await Interact(Pry); + await InteractUsing(Pry); var ent = await FindEntity(Payload, LookupFlags.Uncontained); await Delete(ent); // successfully insert a second time - await Interact(Payload); + await InteractUsing(Payload); ent = await FindEntity(Payload); var sys = SEntMan.System(); Assert.That(sys.IsEntityInContainer(ent)); diff --git a/Content.IntegrationTests/Tests/PostMapInitTest.cs b/Content.IntegrationTests/Tests/PostMapInitTest.cs index 05f603408b3..aebe3a1c0eb 100644 --- a/Content.IntegrationTests/Tests/PostMapInitTest.cs +++ b/Content.IntegrationTests/Tests/PostMapInitTest.cs @@ -16,6 +16,7 @@ using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; +using FastAccessors; using Robust.Shared.Utility; using YamlDotNet.RepresentationModel; @@ -77,13 +78,14 @@ public async Task GridsLoadableTest(string mapFile) var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); + var mapSystem = entManager.System(); var mapManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitPost(() => { - var mapId = mapManager.CreateMap(); + mapSystem.CreateMap(out var mapId); try { #pragma warning disable NUnit2045 @@ -164,6 +166,7 @@ public async Task GameMapsLoadableTest(string mapProto) var mapManager = server.ResolveDependency(); var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); + var mapSystem = entManager.System(); var protoManager = server.ResolveDependency(); var ticker = entManager.EntitySysManager.GetEntitySystem(); var shuttleSystem = entManager.EntitySysManager.GetEntitySystem(); @@ -173,7 +176,7 @@ public async Task GameMapsLoadableTest(string mapProto) await server.WaitPost(() => { - var mapId = mapManager.CreateMap(); + mapSystem.CreateMap(out var mapId); try { ticker.LoadGameMap(protoManager.Index(mapProto), mapId, null); @@ -183,7 +186,7 @@ await server.WaitPost(() => throw new Exception($"Failed to load map {mapProto}", ex); } - var shuttleMap = mapManager.CreateMap(); + mapSystem.CreateMap(out var shuttleMap); var largest = 0f; EntityUid? targetGrid = null; var memberQuery = entManager.GetEntityQuery(); @@ -242,24 +245,17 @@ await server.WaitPost(() => Assert.That(lateSpawns, Is.GreaterThan(0), $"Found no latejoin spawn points on {mapProto}"); } + var comp = entManager.GetComponent(station); + var jobs = new HashSet(comp.SetupAvailableJobs.Keys); + // Test all availableJobs have spawnPoints // This is done inside gamemap test because loading the map takes ages and we already have it. - var jobList = entManager.GetComponent(station).RoundStartJobList - .Where(x => x.Value != 0) - .Select(x => x.Key); var spawnPoints = entManager.EntityQuery() - .Where(spawnpoint => spawnpoint.SpawnType == SpawnPointType.Job) - .Select(spawnpoint => spawnpoint.Job.ID) - .Distinct(); - List missingSpawnPoints = new(); - foreach (var spawnpoint in jobList.Except(spawnPoints)) - { - if (protoManager.Index(spawnpoint).SetPreference) - missingSpawnPoints.Add(spawnpoint); - } + .Where(x => x.SpawnType == SpawnPointType.Job) + .Select(x => x.Job!.ID); - Assert.That(missingSpawnPoints, Has.Count.EqualTo(0), - $"There is no spawnpoint for {string.Join(", ", missingSpawnPoints)} on {mapProto}."); + jobs.ExceptWith(spawnPoints); + Assert.That(jobs, Is.Empty, $"There is no spawnpoints for {string.Join(", ", jobs)} on {mapProto}."); } try @@ -332,6 +328,7 @@ public async Task NonGameMapsLoadableTest() var resourceManager = server.ResolveDependency(); var protoManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); + var mapSystem = server.System(); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); var gameMaps = protoManager.EnumeratePrototypes().Select(o => o.MapPath).ToHashSet(); @@ -362,7 +359,7 @@ await server.WaitPost(() => { foreach (var mapName in mapNames) { - var mapId = mapManager.CreateMap(); + mapSystem.CreateMap(out var mapId); try { Assert.That(mapLoader.TryLoad(mapId, mapName, out _)); diff --git a/Content.IntegrationTests/Tests/Power/PowerTest.cs b/Content.IntegrationTests/Tests/Power/PowerTest.cs index a94e94489c0..55bb42f8ced 100644 --- a/Content.IntegrationTests/Tests/Power/PowerTest.cs +++ b/Content.IntegrationTests/Tests/Power/PowerTest.cs @@ -166,6 +166,7 @@ public async Task TestSimpleSurplus() var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); + var mapSys = entityManager.System(); const float loadPower = 200; PowerSupplierComponent supplier = default!; PowerConsumerComponent consumer1 = default!; @@ -173,21 +174,19 @@ public async Task TestSimpleSurplus() await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 3; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates()); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 1)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); consumer1 = entityManager.GetComponent(consumerEnt1); @@ -229,6 +228,7 @@ public async Task TestSimpleDeficit() var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); + var mapSys = entityManager.System(); const float loadPower = 200; PowerSupplierComponent supplier = default!; PowerConsumerComponent consumer1 = default!; @@ -236,21 +236,19 @@ public async Task TestSimpleDeficit() await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 3; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates()); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 1)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); consumer1 = entityManager.GetComponent(consumerEnt1); @@ -288,25 +286,25 @@ public async Task TestSupplyRamp() var server = pair.Server; var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); + var mapSys = entityManager.System(); var gameTiming = server.ResolveDependency(); PowerSupplierComponent supplier = default!; PowerConsumerComponent consumer = default!; await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 3; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates()); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); consumer = entityManager.GetComponent(consumerEnt); @@ -378,6 +376,7 @@ public async Task TestBatteryRamp() var entityManager = server.ResolveDependency(); var gameTiming = server.ResolveDependency(); var batterySys = entityManager.System(); + var mapSys = entityManager.System(); const float startingCharge = 100_000; PowerNetworkBatteryComponent netBattery = default!; @@ -386,19 +385,18 @@ public async Task TestBatteryRamp() await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 3; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", gridOwner.ToCoordinates()); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.Owner.ToCoordinates()); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2)); netBattery = entityManager.GetComponent(generatorEnt); battery = entityManager.GetComponent(generatorEnt); @@ -479,6 +477,7 @@ public async Task TestNoDemandRampdown() var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var batterySys = entityManager.System(); + var mapSys = entityManager.System(); PowerSupplierComponent supplier = default!; PowerNetworkBatteryComponent netBattery = default!; BatteryComponent battery = default!; @@ -490,20 +489,19 @@ public async Task TestNoDemandRampdown() await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 3; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 1)); - var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates()); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 1)); + var batteryEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.Owner.ToCoordinates(0, 2)); netBattery = entityManager.GetComponent(batteryEnt); battery = entityManager.GetComponent(batteryEnt); supplier = entityManager.GetComponent(generatorEnt); @@ -577,24 +575,24 @@ public async Task TestSimpleBatteryChargeDeficit() var gameTiming = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var batterySys = entityManager.System(); + var mapSys = entityManager.System(); PowerSupplierComponent supplier = default!; BatteryComponent battery = default!; await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 3; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates()); - var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates()); + var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", grid.Owner.ToCoordinates(0, 2)); supplier = entityManager.GetComponent(generatorEnt); var netBattery = entityManager.GetComponent(batteryEnt); @@ -634,6 +632,7 @@ public async Task TestFullBattery() var entityManager = server.ResolveDependency(); var gameTiming = server.ResolveDependency(); var batterySys = entityManager.System(); + var mapSys = entityManager.System(); PowerConsumerComponent consumer = default!; PowerSupplierComponent supplier = default!; PowerNetworkBatteryComponent netBattery = default!; @@ -641,23 +640,22 @@ public async Task TestFullBattery() await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 4; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3)); + var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3)); consumer = entityManager.GetComponent(consumerEnt); supplier = entityManager.GetComponent(supplyEnt); @@ -712,6 +710,7 @@ public async Task TestFullBatteryEfficiencyPassThrough() var entityManager = server.ResolveDependency(); var gameTiming = server.ResolveDependency(); var batterySys = entityManager.System(); + var mapSys = entityManager.System(); PowerConsumerComponent consumer = default!; PowerSupplierComponent supplier = default!; PowerNetworkBatteryComponent netBattery = default!; @@ -719,23 +718,22 @@ public async Task TestFullBatteryEfficiencyPassThrough() await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 4; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3)); + var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3)); consumer = entityManager.GetComponent(consumerEnt); supplier = entityManager.GetComponent(supplyEnt); @@ -789,15 +787,15 @@ public async Task TestFullBatteryEfficiencyDemandPassThrough() var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var batterySys = entityManager.System(); + var mapSys = entityManager.System(); PowerConsumerComponent consumer1 = default!; PowerConsumerComponent consumer2 = default!; PowerSupplierComponent supplier = default!; await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Map layout here is // C - consumer @@ -810,19 +808,19 @@ await server.WaitAssertion(() => // Power only works when anchored for (var i = 0; i < 5; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); - var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); + entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 2)); + var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 2)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 1)); - var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 3)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 2)); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 4)); + var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 1)); + var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 3)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 2)); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 0)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 4)); consumer1 = entityManager.GetComponent(consumerEnt1); consumer2 = entityManager.GetComponent(consumerEnt2); @@ -887,6 +885,7 @@ public async Task TestSupplyPrioritized() var entityManager = server.ResolveDependency(); var gameTiming = server.ResolveDependency(); var batterySys = entityManager.System(); + var mapSys = entityManager.System(); PowerConsumerComponent consumer = default!; PowerSupplierComponent supplier1 = default!; PowerSupplierComponent supplier2 = default!; @@ -897,9 +896,8 @@ public async Task TestSupplyPrioritized() await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Layout is two generators, two batteries, and one load. As to why two: because previously this test // would fail ONLY if there were more than two batteries present, because each of them tries to supply @@ -911,17 +909,17 @@ await server.WaitAssertion(() => // Place cables for (var i = -2; i <= 2; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); - var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, -2)); + var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2)); + var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, -2)); - var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 1)); - var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, -1)); + var supplyEnt1 = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 1)); + var supplyEnt2 = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, -1)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 0)); consumer = entityManager.GetComponent(consumerEnt); supplier1 = entityManager.GetComponent(supplyEnt1); @@ -985,15 +983,15 @@ public async Task TestBatteriesProportional() var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var batterySys = entityManager.System(); + var mapSys = entityManager.System(); PowerConsumerComponent consumer1 = default!; PowerConsumerComponent consumer2 = default!; PowerSupplierComponent supplier = default!; await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Map layout here is // C - consumer @@ -1006,19 +1004,19 @@ await server.WaitAssertion(() => // Power only works when anchored for (var i = 0; i < 5; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); - var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 2)); + entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 2)); + var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 2)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 1)); - var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 3)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 2)); - var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 0)); - var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 4)); + var batteryEnt1 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 1)); + var batteryEnt2 = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 3)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 2)); + var consumerEnt1 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 0)); + var consumerEnt2 = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 4)); consumer1 = entityManager.GetComponent(consumerEnt1); consumer2 = entityManager.GetComponent(consumerEnt2); @@ -1073,29 +1071,29 @@ public async Task TestBatteryEngineCut() var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var batterySys = entityManager.System(); + var mapSys = entityManager.System(); PowerConsumerComponent consumer = default!; PowerSupplierComponent supplier = default!; PowerNetworkBatteryComponent netBattery = default!; await server.WaitPost(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 4; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, i)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i)); } - var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); - var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); - var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", gridOwner.ToCoordinates(0, 3)); + var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2)); + var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0)); + var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3)); consumer = entityManager.GetComponent(consumerEnt); supplier = entityManager.GetComponent(supplyEnt); @@ -1158,6 +1156,7 @@ public async Task TestTerminalNodeGroups() var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var nodeContainer = entityManager.System(); + var mapSys = entityManager.System(); CableNode leftNode = default!; CableNode rightNode = default!; Node batteryInput = default!; @@ -1165,25 +1164,24 @@ public async Task TestTerminalNodeGroups() await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 4; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); } - var leftEnt = entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 0)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 1)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 2)); - var rightEnt = entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 3)); + var leftEnt = entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 0)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 1)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 2)); + var rightEnt = entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 3)); - var terminal = entityManager.SpawnEntity("CableTerminal", gridOwner.ToCoordinates(0, 1)); + var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1)); entityManager.GetComponent(terminal).LocalRotation = Angle.FromDegrees(180); - var battery = entityManager.SpawnEntity("FullBatteryDummy", gridOwner.ToCoordinates(0, 2)); + var battery = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2)); var batteryNodeContainer = entityManager.GetComponent(battery); if (nodeContainer.TryGetNode(entityManager.GetComponent(leftEnt), @@ -1224,29 +1222,29 @@ public async Task ApcChargingTest() var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var batterySys = entityManager.System(); + var mapSys = entityManager.System(); PowerNetworkBatteryComponent substationNetBattery = default!; BatteryComponent apcBattery = default!; await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); // Power only works when anchored for (var i = 0; i < 3; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); } - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 0)); - entityManager.SpawnEntity("CableHV", gridOwner.ToCoordinates(0, 1)); - entityManager.SpawnEntity("CableMV", gridOwner.ToCoordinates(0, 1)); - entityManager.SpawnEntity("CableMV", gridOwner.ToCoordinates(0, 2)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 0)); + entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, 1)); + entityManager.SpawnEntity("CableMV", grid.Owner.ToCoordinates(0, 1)); + entityManager.SpawnEntity("CableMV", grid.Owner.ToCoordinates(0, 2)); - var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", gridOwner.ToCoordinates(0, 0)); - var substationEnt = entityManager.SpawnEntity("SubstationDummy", gridOwner.ToCoordinates(0, 1)); - var apcEnt = entityManager.SpawnEntity("ApcDummy", gridOwner.ToCoordinates(0, 2)); + var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0)); + var substationEnt = entityManager.SpawnEntity("SubstationDummy", grid.Owner.ToCoordinates(0, 1)); + var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.Owner.ToCoordinates(0, 2)); var generatorSupplier = entityManager.GetComponent(generatorEnt); substationNetBattery = entityManager.GetComponent(substationEnt); @@ -1281,33 +1279,33 @@ public async Task ApcNetTest() var entityManager = server.ResolveDependency(); var batterySys = entityManager.System(); var extensionCableSystem = entityManager.System(); + var mapSys = entityManager.System(); PowerNetworkBatteryComponent apcNetBattery = default!; ApcPowerReceiverComponent receiver = default!; ApcPowerReceiverComponent unpoweredReceiver = default!; await server.WaitAssertion(() => { - var map = mapManager.CreateMap(); - var grid = mapManager.CreateGrid(map); - var gridOwner = grid.Owner; + var map = mapSys.CreateMap(out var mapId); + var grid = mapManager.CreateGridEntity(mapId); const int range = 5; // Power only works when anchored for (var i = 0; i < range; i++) { - grid.SetTile(new Vector2i(0, i), new Tile(1)); + mapSys.SetTile(grid, new Vector2i(0, i), new Tile(1)); } - var apcEnt = entityManager.SpawnEntity("ApcDummy", gridOwner.ToCoordinates(0, 0)); - var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", gridOwner.ToCoordinates(0, 0)); + var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.Owner.ToCoordinates(0, 0)); + var apcExtensionEnt = entityManager.SpawnEntity("CableApcExtension", grid.Owner.ToCoordinates(0, 0)); // Create a powered receiver in range (range is 0 indexed) - var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", gridOwner.ToCoordinates(0, range - 1)); + var powerReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.Owner.ToCoordinates(0, range - 1)); receiver = entityManager.GetComponent(powerReceiverEnt); // Create an unpowered receiver outside range - var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", gridOwner.ToCoordinates(0, range)); + var unpoweredReceiverEnt = entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.Owner.ToCoordinates(0, range)); unpoweredReceiver = entityManager.GetComponent(unpoweredReceiverEnt); var battery = entityManager.GetComponent(apcEnt); diff --git a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs index 9e26fa5eaa2..1ef34365ea3 100644 --- a/Content.IntegrationTests/Tests/PrototypeSaveTest.cs +++ b/Content.IntegrationTests/Tests/PrototypeSaveTest.cs @@ -40,6 +40,7 @@ public async Task UninitializedSaveTest() var prototypeMan = server.ResolveDependency(); var seriMan = server.ResolveDependency(); var compFact = server.ResolveDependency(); + var mapSystem = server.System(); var prototypes = new List(); EntityUid uid; @@ -77,7 +78,7 @@ public async Task UninitializedSaveTest() await server.WaitAssertion(() => { - Assert.That(!mapManager.IsMapInitialized(mapId)); + Assert.That(!mapSystem.IsInitialized(mapId)); var testLocation = grid.Owner.ToCoordinates(); Assert.Multiple(() => @@ -184,7 +185,7 @@ public DataNode Write(ISerializationManager serializationManager, EntityUid valu IDependencyCollection dependencies, bool alwaysWrite = false, ISerializationContext? context = null) { - if (WritingComponent != "Transform" && (Prototype?.NoSpawn == false)) + if (WritingComponent != "Transform" && Prototype?.HideSpawnMenu == false) { // Maybe this will be necessary in the future, but at the moment it just indicates that there is some // issue, like a non-nullable entityUid data-field. If a component MUST have an entity uid to work with, diff --git a/Content.IntegrationTests/Tests/Puller/PullerTest.cs b/Content.IntegrationTests/Tests/Puller/PullerTest.cs index 87d174f7272..a4fde86dbfb 100644 --- a/Content.IntegrationTests/Tests/Puller/PullerTest.cs +++ b/Content.IntegrationTests/Tests/Puller/PullerTest.cs @@ -29,7 +29,7 @@ await server.WaitAssertion(() => { foreach (var proto in protoManager.EnumeratePrototypes()) { - if (!proto.TryGetComponent(out PullerComponent? puller)) + if (!proto.TryGetComponent(out PullerComponent? puller, compFactory)) continue; if (!puller.NeedsHands) diff --git a/Content.IntegrationTests/Tests/ResearchTest.cs b/Content.IntegrationTests/Tests/ResearchTest.cs index ee319daa436..7ae29a79ffd 100644 --- a/Content.IntegrationTests/Tests/ResearchTest.cs +++ b/Content.IntegrationTests/Tests/ResearchTest.cs @@ -2,6 +2,7 @@ using System.Linq; using Content.Shared.Lathe; using Content.Shared.Research.Prototypes; +using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests; @@ -52,6 +53,7 @@ public async Task AllTechPrintableTest() var server = pair.Server; var protoManager = server.ResolveDependency(); + var compFact = server.ResolveDependency(); await server.WaitAssertion(() => { @@ -65,7 +67,7 @@ await server.WaitAssertion(() => if (pair.IsTestPrototype(proto)) continue; - if (!proto.TryGetComponent(out var lathe)) + if (!proto.TryGetComponent(out var lathe, compFact)) continue; allLathes.Add(lathe); } diff --git a/Content.IntegrationTests/Tests/SalvageTest.cs b/Content.IntegrationTests/Tests/SalvageTest.cs index 9d75428beb7..5dfba82308f 100644 --- a/Content.IntegrationTests/Tests/SalvageTest.cs +++ b/Content.IntegrationTests/Tests/SalvageTest.cs @@ -1,5 +1,4 @@ using System.Linq; -using Content.Server.Salvage; using Content.Shared.CCVar; using Content.Shared.Salvage; using Robust.Server.GameObjects; @@ -28,6 +27,7 @@ public async Task AllSalvageMapsLoadableTest() var mapManager = server.ResolveDependency(); var prototypeManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); + var mapSystem = entManager.System(); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitPost(() => @@ -36,7 +36,7 @@ await server.WaitPost(() => { var mapFile = salvage.MapPath; - var mapId = mapManager.CreateMap(); + mapSystem.CreateMap(out var mapId); try { Assert.That(mapLoader.TryLoad(mapId, mapFile.ToString(), out var roots)); diff --git a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs index db2109ca599..213da5d7862 100644 --- a/Content.IntegrationTests/Tests/SaveLoadMapTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadMapTest.cs @@ -23,6 +23,7 @@ public async Task SaveLoadMultiGridMap() var mapManager = server.ResolveDependency(); var sEntities = server.ResolveDependency(); var mapLoader = sEntities.System(); + var mapSystem = sEntities.System(); var xformSystem = sEntities.EntitySysManager.GetEntitySystem(); var resManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); @@ -33,19 +34,17 @@ await server.WaitAssertion(() => var dir = new ResPath(mapPath).Directory; resManager.UserData.CreateDir(dir); - var mapId = mapManager.CreateMap(); + mapSystem.CreateMap(out var mapId); { - var mapGrid = mapManager.CreateGrid(mapId); - var mapGridEnt = mapGrid.Owner; - xformSystem.SetWorldPosition(mapGridEnt, new Vector2(10, 10)); - mapGrid.SetTile(new Vector2i(0, 0), new Tile(1, (TileRenderFlag) 1, 255)); + var mapGrid = mapManager.CreateGridEntity(mapId); + xformSystem.SetWorldPosition(mapGrid, new Vector2(10, 10)); + mapSystem.SetTile(mapGrid, new Vector2i(0, 0), new Tile(1, (TileRenderFlag) 1, 255)); } { - var mapGrid = mapManager.CreateGrid(mapId); - var mapGridEnt = mapGrid.Owner; - xformSystem.SetWorldPosition(mapGridEnt, new Vector2(-8, -8)); - mapGrid.SetTile(new Vector2i(0, 0), new Tile(2, (TileRenderFlag) 1, 254)); + var mapGrid = mapManager.CreateGridEntity(mapId); + xformSystem.SetWorldPosition(mapGrid, new Vector2(-8, -8)); + mapSystem.SetTile(mapGrid, new Vector2i(0, 0), new Tile(2, (TileRenderFlag) 1, 254)); } Assert.Multiple(() => mapLoader.SaveMap(mapId, mapPath)); @@ -74,7 +73,7 @@ await server.WaitAssertion(() => Assert.Multiple(() => { Assert.That(xformSystem.GetWorldPosition(gridXform), Is.EqualTo(new Vector2(10, 10))); - Assert.That(mapGrid.GetTileRef(new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(1, (TileRenderFlag) 1, 255))); + Assert.That(mapSystem.GetTileRef(gridUid, mapGrid, new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(1, (TileRenderFlag) 1, 255))); }); } { @@ -88,7 +87,7 @@ await server.WaitAssertion(() => Assert.Multiple(() => { Assert.That(xformSystem.GetWorldPosition(gridXform), Is.EqualTo(new Vector2(-8, -8))); - Assert.That(mapGrid.GetTileRef(new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(2, (TileRenderFlag) 1, 254))); + Assert.That(mapSystem.GetTileRef(gridUid, mapGrid, new Vector2i(0, 0)).Tile, Is.EqualTo(new Tile(2, (TileRenderFlag) 1, 254))); }); } }); diff --git a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs index 01c03aace71..af60db55322 100644 --- a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs @@ -25,17 +25,18 @@ public async Task SaveLoadSave() var server = pair.Server; var entManager = server.ResolveDependency(); var mapLoader = entManager.System(); + var mapSystem = entManager.System(); var mapManager = server.ResolveDependency(); var cfg = server.ResolveDependency(); Assert.That(cfg.GetCVar(CCVars.GridFill), Is.False); await server.WaitPost(() => { - var mapId0 = mapManager.CreateMap(); + mapSystem.CreateMap(out var mapId0); // TODO: Properly find the "main" station grid. - var grid0 = mapManager.CreateGrid(mapId0); + var grid0 = mapManager.CreateGridEntity(mapId0); mapLoader.Save(grid0.Owner, "save load save 1.yml"); - var mapId1 = mapManager.CreateMap(); + mapSystem.CreateMap(out var mapId1); EntityUid grid1 = default!; #pragma warning disable NUnit2045 Assert.That(mapLoader.TryLoad(mapId1, "save load save 1.yml", out var roots, new MapLoadOptions() { LoadMap = false }), $"Failed to load test map {TestMap}"); @@ -101,6 +102,7 @@ public async Task LoadSaveTicksSavePebble() var server = pair.Server; var mapLoader = server.ResolveDependency().GetEntitySystem(); var mapManager = server.ResolveDependency(); + var mapSystem = server.System(); MapId mapId = default; var cfg = server.ResolveDependency(); @@ -109,8 +111,7 @@ public async Task LoadSaveTicksSavePebble() // Load pebble.yml as uninitialized map, and save it to ensure it's up to date. server.Post(() => { - mapId = mapManager.CreateMap(); - mapManager.AddUninitializedMap(mapId); + mapSystem.CreateMap(out mapId, runMapInit: false); mapManager.SetMapPaused(mapId, true); Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}"); mapLoader.SaveMap(mapId, "load save ticks save 1.yml"); @@ -182,7 +183,8 @@ public async Task LoadTickLoadPebble() await using var pair = await PoolManager.GetServerClient(); var server = pair.Server; - var mapLoader = server.ResolveDependency().GetEntitySystem(); + var mapLoader = server.System(); + var mapSystem = server.System(); var mapManager = server.ResolveDependency(); var userData = server.ResolveDependency().UserData; var cfg = server.ResolveDependency(); @@ -197,8 +199,7 @@ public async Task LoadTickLoadPebble() // Load & save the first map server.Post(() => { - mapId = mapManager.CreateMap(); - mapManager.AddUninitializedMap(mapId); + mapSystem.CreateMap(out mapId, runMapInit: false); mapManager.SetMapPaused(mapId, true); Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}"); mapLoader.SaveMap(mapId, fileA); @@ -217,8 +218,7 @@ public async Task LoadTickLoadPebble() server.Post(() => { mapManager.DeleteMap(mapId); - mapManager.CreateMap(mapId); - mapManager.AddUninitializedMap(mapId); + mapSystem.CreateMap(out mapId, runMapInit: false); mapManager.SetMapPaused(mapId, true); Assert.That(mapLoader.TryLoad(mapId, TestMap, out _), $"Failed to load test map {TestMap}"); mapLoader.SaveMap(mapId, fileB); diff --git a/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs b/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs index 052ea997c0d..339420362c1 100644 --- a/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs +++ b/Content.IntegrationTests/Tests/Serialization/SerializationTest.cs @@ -24,7 +24,7 @@ public async Task SerializeGenericEnums() Enum value = TestEnum.Bb; - var node = seriMan.WriteValue(value, notNullableOverride:true); + var node = seriMan.WriteValue(value, notNullableOverride: true); var valueNode = node as ValueDataNode; Assert.That(valueNode, Is.Not.Null); @@ -34,22 +34,22 @@ public async Task SerializeGenericEnums() var errors = seriMan.ValidateNode(valueNode).GetErrors(); Assert.That(errors.Any(), Is.False); - var deserialized = seriMan.Read(node, notNullableOverride:true); + var deserialized = seriMan.Read(node, notNullableOverride: true); Assert.That(deserialized, Is.EqualTo(value)); // Repeat test with enums in a data definitions. var data = new TestData { Value = TestEnum.Cc, - Sequence = new() {TestEnum.Dd, TestEnum.Aa} + Sequence = [TestEnum.Dd, TestEnum.Aa] }; - node = seriMan.WriteValue(data, notNullableOverride:true); + node = seriMan.WriteValue(data, notNullableOverride: true); errors = seriMan.ValidateNode(node).GetErrors(); Assert.That(errors.Any(), Is.False); - var deserializedData = seriMan.Read(node, notNullableOverride:false); + var deserializedData = seriMan.Read(node, notNullableOverride: false); Assert.That(deserializedData.Value, Is.EqualTo(data.Value)); Assert.That(deserializedData.Sequence.Count, Is.EqualTo(data.Sequence.Count)); @@ -60,7 +60,7 @@ public async Task SerializeGenericEnums() Enum genericValue = TestEnum.Bb; TestEnum typedValue = TestEnum.Bb; - var genericNode = seriMan.WriteValue(genericValue, notNullableOverride:true); + var genericNode = seriMan.WriteValue(genericValue, notNullableOverride: true); var typedNode = seriMan.WriteValue(typedValue); Assert.That(seriMan.ValidateNode(genericNode).GetErrors().Any(), Is.False); @@ -76,7 +76,7 @@ private enum TestEnum : byte { Aa, Bb, Cc, Dd } [DataDefinition] private sealed partial class TestData { - [DataField("value")] public Enum Value = default!; - [DataField("sequence")] public List Sequence = default!; + [DataField] public Enum Value = default!; + [DataField] public List Sequence = default!; } } diff --git a/Content.IntegrationTests/Tests/Shuttle/DockTest.cs b/Content.IntegrationTests/Tests/Shuttle/DockTest.cs index a1aa462a697..d91d18793e5 100644 --- a/Content.IntegrationTests/Tests/Shuttle/DockTest.cs +++ b/Content.IntegrationTests/Tests/Shuttle/DockTest.cs @@ -97,13 +97,14 @@ public async Task TestPlanetDock() var entManager = server.ResolveDependency(); var dockingSystem = entManager.System(); var mapSystem = entManager.System(); + MapGridComponent mapGrid = default!; - var mapGrid = entManager.AddComponent(map.MapUid); var shuttle = EntityUid.Invalid; // Spawn shuttle and affirm no valid docks. await server.WaitAssertion(() => { + mapGrid = entManager.AddComponent(map.MapUid); entManager.DeleteEntity(map.Grid); Assert.That(entManager.System().TryLoad(otherMap.MapId, "/Maps/Shuttles/emergency.yml", out var rootUids)); shuttle = rootUids[0]; @@ -125,4 +126,4 @@ await server.WaitAssertion(() => await pair.CleanReturnAsync(); } -} +} \ No newline at end of file diff --git a/Content.IntegrationTests/Tests/ShuttleTest.cs b/Content.IntegrationTests/Tests/ShuttleTest.cs index fb786373a5a..da5b82d91e7 100644 --- a/Content.IntegrationTests/Tests/ShuttleTest.cs +++ b/Content.IntegrationTests/Tests/ShuttleTest.cs @@ -2,7 +2,6 @@ using Content.Server.Shuttles.Components; using Robust.Shared.GameObjects; using Robust.Shared.Map; -using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; @@ -23,33 +22,33 @@ public async Task Test() var entManager = server.ResolveDependency(); var physicsSystem = entManager.System(); - EntityUid gridEnt = default; PhysicsComponent gridPhys = null; + var map = await pair.CreateTestMap(); + await server.WaitAssertion(() => { - var mapId = mapMan.CreateMap(); - var grid = mapMan.CreateGridEntity(mapId); - gridEnt = grid.Owner; + var mapId = map.MapId; + var grid = map.Grid; Assert.Multiple(() => { - Assert.That(entManager.HasComponent(gridEnt)); - Assert.That(entManager.TryGetComponent(gridEnt, out gridPhys)); + Assert.That(entManager.HasComponent(grid)); + Assert.That(entManager.TryGetComponent(grid, out gridPhys)); }); Assert.Multiple(() => { Assert.That(gridPhys.BodyType, Is.EqualTo(BodyType.Dynamic)); - Assert.That(entManager.GetComponent(gridEnt).LocalPosition, Is.EqualTo(Vector2.Zero)); + Assert.That(entManager.GetComponent(grid).LocalPosition, Is.EqualTo(Vector2.Zero)); }); - physicsSystem.ApplyLinearImpulse(gridEnt, Vector2.One, body: gridPhys); + physicsSystem.ApplyLinearImpulse(grid, Vector2.One, body: gridPhys); }); await server.WaitRunTicks(1); await server.WaitAssertion(() => { - Assert.That(entManager.GetComponent(gridEnt).LocalPosition, Is.Not.EqualTo(Vector2.Zero)); + Assert.That(entManager.GetComponent(map.Grid).LocalPosition, Is.Not.EqualTo(Vector2.Zero)); }); await pair.CleanReturnAsync(); } diff --git a/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs b/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs index 1762c4213c4..bf75188f029 100644 --- a/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs +++ b/Content.IntegrationTests/Tests/Sprite/ItemSpriteTest.cs @@ -19,12 +19,12 @@ namespace Content.IntegrationTests.Tests.Sprite; /// - Shouldn't have an item component /// - Is missing the required sprite information. /// If none of the abveo are true, it might need to be added to the list of ignored components, see -/// +/// /// [TestFixture] public sealed class PrototypeSaveTest { - private static HashSet _ignored = new() + private static readonly HashSet Ignored = new() { // The only prototypes that should get ignored are those that REQUIRE setup to get a sprite. At that point it is // the responsibility of the spawner to ensure that a valid sprite is set. @@ -34,13 +34,13 @@ public sealed class PrototypeSaveTest [Test] public async Task AllItemsHaveSpritesTest() { - var settings = new PoolSettings() {Connected = true}; // client needs to be in-game + var settings = new PoolSettings() { Connected = true }; // client needs to be in-game await using var pair = await PoolManager.GetServerClient(settings); - List badPrototypes = new(); + List badPrototypes = []; await pair.Client.WaitPost(() => { - foreach (var proto in pair.GetPrototypesWithComponent(_ignored)) + foreach (var proto in pair.GetPrototypesWithComponent(Ignored)) { var dummy = pair.Client.EntMan.Spawn(proto.ID); pair.Client.EntMan.RunMapInit(dummy, pair.Client.MetaData(dummy)); diff --git a/Content.IntegrationTests/Tests/Minds/JobTests.cs b/Content.IntegrationTests/Tests/Station/JobTests.cs similarity index 100% rename from Content.IntegrationTests/Tests/Minds/JobTests.cs rename to Content.IntegrationTests/Tests/Station/JobTests.cs diff --git a/Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs b/Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs new file mode 100644 index 00000000000..34402dd5e62 --- /dev/null +++ b/Content.IntegrationTests/Tests/Storage/StorageInteractionTest.cs @@ -0,0 +1,75 @@ +using Content.Client.UserInterface.Systems.Hotbar.Widgets; +using Content.Client.UserInterface.Systems.Storage.Controls; +using Content.IntegrationTests.Tests.Interaction; +using Content.Shared.Input; +using Content.Shared.PDA; +using Content.Shared.Storage; +using Robust.Client.UserInterface; +using Robust.Shared.Containers; +using Robust.Shared.GameObjects; + +namespace Content.IntegrationTests.Tests.Storage; + +public sealed class StorageInteractionTest : InteractionTest +{ + /// + /// Check that players can interact with items in storage if the storage UI is open + /// + [Test] + public async Task UiInteractTest() + { + var sys = Server.System(); + + await SpawnTarget("ClothingBackpack"); + var backpack = ToServer(Target); + + // Initially no BUI is open. + Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.False); + Assert.That(IsUiOpen(PdaUiKey.Key), Is.False); + + // Activating the backpack opens the UI + await Activate(); + Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.True); + Assert.That(IsUiOpen(PdaUiKey.Key), Is.False); + + // Pick up a PDA + var pda = await PlaceInHands("PassengerPDA"); + var sPda = ToServer(pda); + Assert.That(sys.IsEntityInContainer(sPda), Is.True); + Assert.That(sys.TryGetContainingContainer((sPda, null), out var container)); + Assert.That(container!.Owner, Is.EqualTo(SPlayer)); + + // Insert the PDA into the backpack + await Interact(); + Assert.That(sys.TryGetContainingContainer((sPda, null), out container)); + Assert.That(container!.Owner, Is.EqualTo(backpack)); + + // Use "e" / ActivateInWorld to open the PDA UI while it is still in the backpack. + var ctrl = GetStorageControl(pda); + await ClickControl(ctrl, ContentKeyFunctions.ActivateItemInWorld); + await RunTicks(10); + Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.True); + Assert.That(IsUiOpen(PdaUiKey.Key), Is.True); + + // Click on the pda to pick it up and remove it from the backpack. + await ClickControl(ctrl, ContentKeyFunctions.MoveStoredItem); + await RunTicks(10); + Assert.That(sys.TryGetContainingContainer((sPda, null), out container)); + Assert.That(container!.Owner, Is.EqualTo(SPlayer)); + + // UIs should still be open + Assert.That(IsUiOpen(StorageComponent.StorageUiKey.Key), Is.True); + Assert.That(IsUiOpen(PdaUiKey.Key), Is.True); + } + + /// + /// Retrieve the control that corresponds to the given entity in the currently open storage UI. + /// + private ItemGridPiece GetStorageControl(NetEntity target) + { + var uid = ToClient(target); + var hotbar = GetWidget(); + var storageContainer = GetControlFromField(nameof(HotbarGui.StorageContainer), hotbar); + return GetControlFromChildren(c => c.Entity == uid, storageContainer); + } +} diff --git a/Content.IntegrationTests/Tests/Tag/TagTest.cs b/Content.IntegrationTests/Tests/Tag/TagTest.cs index ed3c484b435..cbcdd1c6c62 100644 --- a/Content.IntegrationTests/Tests/Tag/TagTest.cs +++ b/Content.IntegrationTests/Tests/Tag/TagTest.cs @@ -130,9 +130,9 @@ await server.WaitAssertion(() => Assert.Multiple(() => { // Cannot add the starting tag again - Assert.That(tagSystem.AddTag(sTagComponent, StartingTag), Is.False); - Assert.That(tagSystem.AddTags(sTagComponent, StartingTag, StartingTag), Is.False); - Assert.That(tagSystem.AddTags(sTagComponent, new List { StartingTag, StartingTag }), Is.False); + Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, StartingTag), Is.False); + Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, StartingTag), Is.False); + Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List { StartingTag, StartingTag }), Is.False); // Has the starting tag Assert.That(tagSystem.HasTag(sTagComponent, StartingTag), Is.True); @@ -157,22 +157,22 @@ await server.WaitAssertion(() => Assert.That(tagSystem.HasAllTags(sTagComponent, new List { StartingTag, AddedTag }), Is.False); // Cannot remove a tag that does not exist - Assert.That(tagSystem.RemoveTag(sTagComponent, AddedTag), Is.False); - Assert.That(tagSystem.RemoveTags(sTagComponent, AddedTag, AddedTag), Is.False); - Assert.That(tagSystem.RemoveTags(sTagComponent, new List { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, AddedTag), Is.False); + Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.False); + Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List { AddedTag, AddedTag }), Is.False); }); // Can add the new tag - Assert.That(tagSystem.AddTag(sTagComponent, AddedTag), Is.True); + Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.True); Assert.Multiple(() => { // Cannot add it twice - Assert.That(tagSystem.AddTag(sTagComponent, AddedTag), Is.False); + Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.False); // Cannot add existing tags - Assert.That(tagSystem.AddTags(sTagComponent, StartingTag, AddedTag), Is.False); - Assert.That(tagSystem.AddTags(sTagComponent, new List { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, AddedTag), Is.False); + Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List { StartingTag, AddedTag }), Is.False); // Now has two tags Assert.That(sTagComponent.Tags, Has.Count.EqualTo(2)); @@ -191,16 +191,16 @@ await server.WaitAssertion(() => Assert.Multiple(() => { // Remove the existing starting tag - Assert.That(tagSystem.RemoveTag(sTagComponent, StartingTag), Is.True); + Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, StartingTag), Is.True); // Remove the existing added tag - Assert.That(tagSystem.RemoveTags(sTagComponent, AddedTag, AddedTag), Is.True); + Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.True); }); Assert.Multiple(() => { // No tags left to remove - Assert.That(tagSystem.RemoveTags(sTagComponent, new List { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List { StartingTag, AddedTag }), Is.False); // No tags left in the component Assert.That(sTagComponent.Tags, Is.Empty); diff --git a/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs b/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs index 083e817d697..6ea8b6882ad 100644 --- a/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs +++ b/Content.IntegrationTests/Tests/Tiles/TileConstructionTests.cs @@ -15,10 +15,10 @@ public async Task PlaceThenCutLattice() await AssertTile(Plating, PlayerCoords); AssertGridCount(1); await SetTile(null); - await Interact(Rod); + await InteractUsing(Rod); await AssertTile(Lattice); Assert.That(Hands.ActiveHandEntity, Is.Null); - await Interact(Cut); + await InteractUsing(Cut); await AssertTile(null); await AssertEntityLookup((Rod, 1)); AssertGridCount(1); @@ -43,14 +43,14 @@ public async Task CutThenPlaceLatticeNewGrid() // Place Lattice var oldPos = TargetCoords; TargetCoords = SEntMan.GetNetCoordinates(new EntityCoordinates(MapData.MapUid, 1, 0)); - await Interact(Rod); + await InteractUsing(Rod); TargetCoords = oldPos; await AssertTile(Lattice); AssertGridCount(1); // Cut lattice Assert.That(Hands.ActiveHandEntity, Is.Null); - await Interact(Cut); + await InteractUsing(Cut); await AssertTile(null); AssertGridCount(0); @@ -76,25 +76,25 @@ public async Task FloorConstructDeconstruct() // Space -> Lattice var oldPos = TargetCoords; TargetCoords = SEntMan.GetNetCoordinates(new EntityCoordinates(MapData.MapUid, 1, 0)); - await Interact(Rod); + await InteractUsing(Rod); TargetCoords = oldPos; await AssertTile(Lattice); AssertGridCount(1); // Lattice -> Plating - await Interact(Steel); + await InteractUsing(Steel); Assert.That(Hands.ActiveHandEntity, Is.Null); await AssertTile(Plating); AssertGridCount(1); // Plating -> Tile - await Interact(FloorItem); + await InteractUsing(FloorItem); Assert.That(Hands.ActiveHandEntity, Is.Null); await AssertTile(Floor); AssertGridCount(1); // Tile -> Plating - await Interact(Pry); + await InteractUsing(Pry); await AssertTile(Plating); AssertGridCount(1); diff --git a/Content.IntegrationTests/Tests/Toolshed/ToolshedTest.cs b/Content.IntegrationTests/Tests/Toolshed/ToolshedTest.cs index dd68ff1ccf1..7de81fb3dc2 100644 --- a/Content.IntegrationTests/Tests/Toolshed/ToolshedTest.cs +++ b/Content.IntegrationTests/Tests/Toolshed/ToolshedTest.cs @@ -36,16 +36,18 @@ public async Task TearDownInternal() await TearDown(); } - protected virtual async Task TearDown() + protected virtual Task TearDown() { Assert.That(_expectedErrors, Is.Empty); ClearErrors(); + + return Task.CompletedTask; } [SetUp] public virtual async Task Setup() { - Pair = await PoolManager.GetServerClient(new PoolSettings {Connected = Connected}); + Pair = await PoolManager.GetServerClient(new PoolSettings { Connected = Connected }); Server = Pair.Server; if (Connected) @@ -142,7 +144,7 @@ public void ReportError(IConError err) ); } - done: + done: _errors.Add(err); } diff --git a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs index 99481db70e7..5bfebfbd530 100644 --- a/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs +++ b/Content.IntegrationTests/Tests/VendingMachineRestockTest.cs @@ -110,6 +110,7 @@ public async Task TestAllRestocksAreAvailableToBuy() await server.WaitIdleAsync(); var prototypeManager = server.ResolveDependency(); + var compFact = server.ResolveDependency(); await server.WaitAssertion(() => { @@ -132,7 +133,7 @@ await server.WaitAssertion(() => // Collect all the prototypes with StorageFills referencing those entities. foreach (var proto in prototypeManager.EnumeratePrototypes()) { - if (!proto.TryGetComponent(out var storage)) + if (!proto.TryGetComponent(out var storage, compFact)) continue; List restockStore = new(); diff --git a/Content.IntegrationTests/Tests/Weldable/WeldableTests.cs b/Content.IntegrationTests/Tests/Weldable/WeldableTests.cs index 6227f3dee1b..e7eadeda0a4 100644 --- a/Content.IntegrationTests/Tests/Weldable/WeldableTests.cs +++ b/Content.IntegrationTests/Tests/Weldable/WeldableTests.cs @@ -18,7 +18,7 @@ public async Task WeldLocker() Assert.That(comp.IsWelded, Is.False); - await Interact(Weld); + await InteractUsing(Weld); Assert.That(comp.IsWelded, Is.True); AssertPrototype(Locker); // Prototype did not change. } diff --git a/Content.Server/Abilities/Mime/MimePowersComponent.cs b/Content.Server/Abilities/Mime/MimePowersComponent.cs index fd4fc2c2af9..d56644ed191 100644 --- a/Content.Server/Abilities/Mime/MimePowersComponent.cs +++ b/Content.Server/Abilities/Mime/MimePowersComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Alert; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -47,5 +48,12 @@ public sealed partial class MimePowersComponent : Component /// [DataField("vowCooldown")] public TimeSpan VowCooldown = TimeSpan.FromMinutes(5); + + [DataField] + public ProtoId VowAlert = "VowOfSilence"; + + [DataField] + public ProtoId VowBrokenAlert = "VowBroken"; + } } diff --git a/Content.Server/Abilities/Mime/MimePowersSystem.cs b/Content.Server/Abilities/Mime/MimePowersSystem.cs index 57163a96a50..a1e50228ae2 100644 --- a/Content.Server/Abilities/Mime/MimePowersSystem.cs +++ b/Content.Server/Abilities/Mime/MimePowersSystem.cs @@ -1,5 +1,4 @@ using Content.Server.Popups; -using Content.Server.Speech.Muting; using Content.Shared.Actions; using Content.Shared.Actions.Events; using Content.Shared.Alert; @@ -56,7 +55,7 @@ public override void Update(float frameTime) private void OnComponentInit(EntityUid uid, MimePowersComponent component, ComponentInit args) { EnsureComp(uid); - _alertsSystem.ShowAlert(uid, AlertType.VowOfSilence); + _alertsSystem.ShowAlert(uid, component.VowAlert); _actionsSystem.AddAction(uid, ref component.InvisibleWallActionEntity, component.InvisibleWallAction, uid); } @@ -120,8 +119,8 @@ public void BreakVow(EntityUid uid, MimePowersComponent? mimePowers = null) mimePowers.VowBroken = true; mimePowers.VowRepentTime = _timing.CurTime + mimePowers.VowCooldown; RemComp(uid); - _alertsSystem.ClearAlert(uid, AlertType.VowOfSilence); - _alertsSystem.ShowAlert(uid, AlertType.VowBroken); + _alertsSystem.ClearAlert(uid, mimePowers.VowAlert); + _alertsSystem.ShowAlert(uid, mimePowers.VowBrokenAlert); _actionsSystem.RemoveAction(uid, mimePowers.InvisibleWallActionEntity); } @@ -143,8 +142,8 @@ public void RetakeVow(EntityUid uid, MimePowersComponent? mimePowers = null) mimePowers.ReadyToRepent = false; mimePowers.VowBroken = false; AddComp(uid); - _alertsSystem.ClearAlert(uid, AlertType.VowBroken); - _alertsSystem.ShowAlert(uid, AlertType.VowOfSilence); + _alertsSystem.ClearAlert(uid, mimePowers.VowAlert); + _alertsSystem.ShowAlert(uid, mimePowers.VowBrokenAlert); _actionsSystem.AddAction(uid, ref mimePowers.InvisibleWallActionEntity, mimePowers.InvisibleWallAction, uid); } } diff --git a/Content.Server/Administration/ServerApi.cs b/Content.Server/Administration/ServerApi.cs index 04fd38598fb..d0f23db637b 100644 --- a/Content.Server/Administration/ServerApi.cs +++ b/Content.Server/Administration/ServerApi.cs @@ -8,13 +8,13 @@ using System.Threading.Tasks; using Content.Server.Administration.Systems; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; using Content.Server.Maps; using Content.Server.RoundEnd; using Content.Shared.Administration.Managers; using Content.Shared.CCVar; +using Content.Shared.GameTicking.Components; using Content.Shared.Prototypes; using Robust.Server.ServerStatus; using Robust.Shared.Asynchronous; diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index 4e6af6ceea5..bda60e9449a 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -635,13 +635,13 @@ private void AddSmiteVerbs(GetVerbsEvent args) { Text = "Remove gravity", Category = VerbCategory.Smite, - Icon = new SpriteSpecifier.Rsi(new ("/Textures/Structures/Machines/gravity_generator.rsi"), "off"), + Icon = new SpriteSpecifier.Rsi(new("/Textures/Structures/Machines/gravity_generator.rsi"), "off"), Act = () => { var grav = EnsureComp(args.Target); grav.Weightless = true; - Dirty(grav); + Dirty(args.Target, grav); }, Impact = LogImpact.Extreme, Message = Loc.GetString("admin-smite-remove-gravity-description"), @@ -738,7 +738,7 @@ private void AddSmiteVerbs(GetVerbsEvent args) var movementSpeed = EnsureComp(args.Target); (movementSpeed.BaseSprintSpeed, movementSpeed.BaseWalkSpeed) = (movementSpeed.BaseWalkSpeed, movementSpeed.BaseSprintSpeed); - Dirty(movementSpeed); + Dirty(args.Target, movementSpeed); _popupSystem.PopupEntity(Loc.GetString("admin-smite-run-walk-swap-prompt"), args.Target, args.Target, PopupType.LargeCaution); diff --git a/Content.Server/Alert/Commands/ClearAlert.cs b/Content.Server/Alert/Commands/ClearAlert.cs index 1759612702f..929c343b50f 100644 --- a/Content.Server/Alert/Commands/ClearAlert.cs +++ b/Content.Server/Alert/Commands/ClearAlert.cs @@ -9,6 +9,7 @@ namespace Content.Server.Alert.Commands [AdminCommand(AdminFlags.Debug)] public sealed class ClearAlert : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; public string Command => "clearalert"; public string Description => "Clears an alert for a player, defaulting to current player"; public string Help => "clearalert "; @@ -37,14 +38,14 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) } var alertType = args[0]; - var alertsSystem = EntitySystem.Get(); - if (!alertsSystem.TryGet(Enum.Parse(alertType), out var alert)) + var alertsSystem = _e.System(); + if (!alertsSystem.TryGet(alertType, out var alert)) { shell.WriteLine("unrecognized alertType " + alertType); return; } - alertsSystem.ClearAlert(attachedEntity, alert.AlertType); + alertsSystem.ClearAlert(attachedEntity, alert.ID); } } } diff --git a/Content.Server/Alert/Commands/ShowAlert.cs b/Content.Server/Alert/Commands/ShowAlert.cs index 11901e9af00..a275dab4fa5 100644 --- a/Content.Server/Alert/Commands/ShowAlert.cs +++ b/Content.Server/Alert/Commands/ShowAlert.cs @@ -9,6 +9,7 @@ namespace Content.Server.Alert.Commands [AdminCommand(AdminFlags.Debug)] public sealed class ShowAlert : IConsoleCommand { + [Dependency] private readonly IEntityManager _e = default!; public string Command => "showalert"; public string Description => "Shows an alert for a player, defaulting to current player"; public string Help => "showalert "; @@ -38,8 +39,8 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var alertType = args[0]; var severity = args[1]; - var alertsSystem = EntitySystem.Get(); - if (!alertsSystem.TryGet(Enum.Parse(alertType), out var alert)) + var alertsSystem = _e.System(); + if (!alertsSystem.TryGet(alertType, out var alert)) { shell.WriteLine("unrecognized alertType " + alertType); return; @@ -51,7 +52,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) } short? severity1 = sevint == -1 ? null : sevint; - alertsSystem.ShowAlert(attachedEntity, alert.AlertType, severity1, null); + alertsSystem.ShowAlert(attachedEntity, alert.ID, severity1, null); } } } diff --git a/Content.Server/Antag/AntagSelectionSystem.cs b/Content.Server/Antag/AntagSelectionSystem.cs index cd4d836e683..f5e6de64e53 100644 --- a/Content.Server/Antag/AntagSelectionSystem.cs +++ b/Content.Server/Antag/AntagSelectionSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Antag.Components; using Content.Server.Chat.Managers; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.Ghost.Roles; using Content.Server.Ghost.Roles.Components; @@ -14,6 +13,7 @@ using Content.Server.Station.Systems; using Content.Shared.Antag; using Content.Shared.GameTicking; +using Content.Shared.GameTicking.Components; using Content.Shared.Ghost; using Content.Shared.Humanoid; using Content.Shared.Players; diff --git a/Content.Server/Antag/Components/AntagSelectionComponent.cs b/Content.Server/Antag/Components/AntagSelectionComponent.cs index 096be14049a..5b6699dab76 100644 --- a/Content.Server/Antag/Components/AntagSelectionComponent.cs +++ b/Content.Server/Antag/Components/AntagSelectionComponent.cs @@ -1,6 +1,6 @@ using Content.Server.Administration.Systems; -using Content.Server.Destructible.Thresholds; using Content.Shared.Antag; +using Content.Shared.Destructible.Thresholds; using Content.Shared.Roles; using Content.Shared.Storage; using Content.Shared.Whitelist; diff --git a/Content.Server/Antag/MobReplacementRuleSystem.cs b/Content.Server/Antag/MobReplacementRuleSystem.cs index dc8103497ee..aa6d1044bfe 100644 --- a/Content.Server/Antag/MobReplacementRuleSystem.cs +++ b/Content.Server/Antag/MobReplacementRuleSystem.cs @@ -1,11 +1,7 @@ using System.Numerics; using Content.Server.Antag.Mimic; -using Content.Server.Chat.Systems; using Content.Server.GameTicking.Rules; -using Content.Server.GameTicking.Components; -using Content.Server.NPC.Systems; -using Content.Server.Station.Systems; -using Content.Server.GameTicking; +using Content.Shared.GameTicking.Components; using Content.Shared.VendingMachines; using Robust.Shared.Map; using Robust.Shared.Prototypes; @@ -23,6 +19,10 @@ using Content.Server.Advertise.Components; using Content.Server.Power.Components; using Content.Shared.CombatMode; +using Content.Server.Station.Systems; +using Content.Server.GameTicking; +using Content.Server.Chat.Systems; +using Content.Server.NPC.Systems; namespace Content.Server.Antag; diff --git a/Content.Server/Atmos/Components/BarotraumaComponent.cs b/Content.Server/Atmos/Components/BarotraumaComponent.cs index 4e29699872e..d261c5ab030 100644 --- a/Content.Server/Atmos/Components/BarotraumaComponent.cs +++ b/Content.Server/Atmos/Components/BarotraumaComponent.cs @@ -1,5 +1,7 @@ +using Content.Shared.Alert; using Content.Shared.Damage; using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; namespace Content.Server.Atmos.Components { @@ -46,5 +48,13 @@ public sealed partial class BarotraumaComponent : Component [ViewVariables(VVAccess.ReadWrite)] public bool HasImmunity = false; + [DataField] + public ProtoId HighPressureAlert = "HighPressure"; + + [DataField] + public ProtoId LowPressureAlert = "LowPressure"; + + [DataField] + public ProtoId PressureAlertCategory = "Pressure"; } } diff --git a/Content.Server/Atmos/Components/FlammableComponent.cs b/Content.Server/Atmos/Components/FlammableComponent.cs index 9f39af540dd..e1c7974307b 100644 --- a/Content.Server/Atmos/Components/FlammableComponent.cs +++ b/Content.Server/Atmos/Components/FlammableComponent.cs @@ -1,5 +1,7 @@ +using Content.Shared.Alert; using Content.Shared.Damage; using Robust.Shared.Physics.Collision.Shapes; +using Robust.Shared.Prototypes; namespace Content.Server.Atmos.Components { @@ -83,5 +85,7 @@ public sealed partial class FlammableComponent : Component /// [DataField] public float FireStackIncreaseMultiplier = 1f; + [DataField] + public ProtoId FireAlert = "Fire"; } } diff --git a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs index 9bea58330cd..ead810bb986 100644 --- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs +++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs @@ -241,7 +241,7 @@ public override void Update(float frameTime) _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} started taking low pressure damage"); } RaiseLocalEvent(uid, new MoodEffectEvent("MobLowPressure")); - _alertsSystem.ShowAlert(uid, AlertType.LowPressure, 2); + _alertsSystem.ShowAlert(uid, barotrauma.LowPressureAlert, 2); } else if (pressure >= Atmospherics.HazardHighPressure) { @@ -255,7 +255,8 @@ public override void Update(float frameTime) barotrauma.TakingDamage = true; _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} started taking high pressure damage"); } - _alertsSystem.ShowAlert(uid, AlertType.HighPressure, 2); + + _alertsSystem.ShowAlert(uid, barotrauma.HighPressureAlert, 2); } else { @@ -269,13 +270,13 @@ public override void Update(float frameTime) switch (pressure) { case <= Atmospherics.WarningLowPressure: - _alertsSystem.ShowAlert(uid, AlertType.LowPressure, 1); + _alertsSystem.ShowAlert(uid, barotrauma.LowPressureAlert, 1); break; case >= Atmospherics.WarningHighPressure: - _alertsSystem.ShowAlert(uid, AlertType.HighPressure, 1); + _alertsSystem.ShowAlert(uid, barotrauma.HighPressureAlert, 1); break; default: - _alertsSystem.ClearAlertCategory(uid, AlertCategory.Pressure); + _alertsSystem.ClearAlertCategory(uid, barotrauma.PressureAlertCategory); break; } } diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index ec0e7b07092..cf6287d7001 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -423,12 +423,12 @@ public override void Update(float frameTime) if (!flammable.OnFire) { - _alertsSystem.ClearAlert(uid, AlertType.Fire); + _alertsSystem.ClearAlert(uid, flammable.FireAlert); RaiseLocalEvent(uid, new MoodRemoveEffectEvent("OnFire")); continue; } - _alertsSystem.ShowAlert(uid, AlertType.Fire); + _alertsSystem.ShowAlert(uid, flammable.FireAlert); RaiseLocalEvent(uid, new MoodEffectEvent("OnFire")); if (flammable.FireStacks > 0) diff --git a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs index 65977f8c140..b42f3626293 100644 --- a/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasAnalyzerSystem.cs @@ -93,7 +93,7 @@ private void ActivateAnalyzer(EntityUid uid, GasAnalyzerComponent component, Ent else component.LastPosition = null; component.Enabled = true; - Dirty(component); + Dirty(uid, component); UpdateAppearance(uid, component); EnsureComp(uid); UpdateAnalyzer(uid, component); @@ -105,7 +105,7 @@ private void ActivateAnalyzer(EntityUid uid, GasAnalyzerComponent component, Ent /// private void OnDropped(EntityUid uid, GasAnalyzerComponent component, DroppedEvent args) { - if(args.User is var userId && component.Enabled) + if (args.User is var userId && component.Enabled) _popup.PopupEntity(Loc.GetString("gas-analyzer-shutoff"), userId, userId); DisableAnalyzer(uid, component, args.User); } @@ -121,7 +121,7 @@ private void DisableAnalyzer(EntityUid uid, GasAnalyzerComponent? component = nu _userInterface.CloseUi(uid, GasAnalyzerUiKey.Key, user); component.Enabled = false; - Dirty(component); + Dirty(uid, component); UpdateAppearance(uid, component); RemCompDeferred(uid); } diff --git a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs index c46701a6a07..89b9c520787 100644 --- a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -122,10 +122,11 @@ private void OnPvsToggle(bool value) } // PVS was turned off, ensure data gets sent to all clients. - foreach (var (grid, meta) in EntityQuery(true)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var grid, out var meta)) { grid.ForceTick = _gameTiming.CurTick; - Dirty(grid, meta); + Dirty(uid, grid, meta); } } @@ -268,9 +269,10 @@ private bool UpdateChunkTile(GridAtmosphereComponent gridAtmosphere, GasOverlayC private void UpdateOverlayData() { // TODO parallelize? - foreach (var (overlay, gam, meta) in EntityQuery(true)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var overlay, out var gam, out var meta)) { - bool changed = false; + var changed = false; foreach (var index in overlay.InvalidTiles) { var chunkIndex = GetGasChunkIndices(index); @@ -282,7 +284,7 @@ private void UpdateOverlayData() } if (changed) - Dirty(overlay, meta); + Dirty(uid, overlay, meta); overlay.InvalidTiles.Clear(); } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs index 7cb8102a388..75be0380ee3 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPortableSystem.cs @@ -17,6 +17,7 @@ public sealed class GasPortableSystem : EntitySystem { [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + [Dependency] private readonly SharedMapSystem _sharedMapSystem = default!; public override void Initialize() { @@ -57,7 +58,7 @@ public bool FindGasPortIn(EntityUid? gridId, EntityCoordinates coordinates, [Not if (!TryComp(gridId, out var grid)) return false; - foreach (var entityUid in grid.GetLocal(coordinates)) + foreach (var entityUid in _sharedMapSystem.GetLocal(gridId.Value, grid, coordinates)) { if (EntityManager.TryGetComponent(entityUid, out port)) { diff --git a/Content.Server/BarSign/Systems/BarSignSystem.cs b/Content.Server/BarSign/Systems/BarSignSystem.cs index 4a481408452..e42394f5a30 100644 --- a/Content.Server/BarSign/Systems/BarSignSystem.cs +++ b/Content.Server/BarSign/Systems/BarSignSystem.cs @@ -34,7 +34,7 @@ private void OnMapInit(EntityUid uid, BarSignComponent component, MapInitEvent a _metaData.SetEntityDescription(uid, Loc.GetString(newPrototype.Description), meta); component.Current = newPrototype.ID; - Dirty(component); + Dirty(uid, component); } } } diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs index 976ef5139c3..089ce322366 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Mobs.Systems; using Robust.Shared.Timing; using Content.Shared.Silicon.Components; // I shouldn't have to modify this. +using Robust.Shared.Utility; namespace Content.Server.Bed { @@ -30,27 +31,31 @@ public sealed class BedSystem : EntitySystem public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(ManageUpdateList); - SubscribeLocalEvent(OnBuckleChange); + SubscribeLocalEvent(OnStrapped); + SubscribeLocalEvent(OnUnstrapped); + SubscribeLocalEvent(OnStasisStrapped); + SubscribeLocalEvent(OnStasisUnstrapped); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnEmagged); SubscribeLocalEvent(OnRefreshParts); SubscribeLocalEvent(OnUpgradeExamine); } - private void ManageUpdateList(EntityUid uid, HealOnBuckleComponent component, ref BuckleChangeEvent args) + private void OnStrapped(Entity bed, ref StrappedEvent args) { - if (args.Buckling) - { - AddComp(uid); - component.NextHealTime = _timing.CurTime + TimeSpan.FromSeconds(component.HealTime); - _actionsSystem.AddAction(args.BuckledEntity, ref component.SleepAction, SleepingSystem.SleepActionId, uid); - return; - } + EnsureComp(bed); + bed.Comp.NextHealTime = _timing.CurTime + TimeSpan.FromSeconds(bed.Comp.HealTime); + _actionsSystem.AddAction(args.Buckle, ref bed.Comp.SleepAction, SleepingSystem.SleepActionId, bed); - _actionsSystem.RemoveAction(args.BuckledEntity, component.SleepAction); - _sleepingSystem.TryWaking(args.BuckledEntity); - RemComp(uid); + // Single action entity, cannot strap multiple entities to the same bed. + DebugTools.AssertEqual(args.Strap.Comp.BuckledEntities.Count, 1); + } + + private void OnUnstrapped(Entity bed, ref UnstrappedEvent args) + { + _actionsSystem.RemoveAction(args.Buckle, bed.Comp.SleepAction); + _sleepingSystem.TryWaking(args.Buckle.Owner); + RemComp(bed); } public override void Update(float frameTime) @@ -89,18 +94,22 @@ private void UpdateAppearance(EntityUid uid, bool isOn) _appearance.SetData(uid, StasisBedVisuals.IsOn, isOn); } - private void OnBuckleChange(EntityUid uid, StasisBedComponent component, ref BuckleChangeEvent args) + private void OnStasisStrapped(Entity bed, ref StrappedEvent args) { - // In testing this also received an unbuckle event when the bed is destroyed - // So don't worry about that - if (!HasComp(args.BuckledEntity)) + if (!HasComp(args.Buckle) || !this.IsPowered(bed, EntityManager)) return; - if (!this.IsPowered(uid, EntityManager)) + var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.Buckle, bed.Comp.Multiplier, true); + RaiseLocalEvent(args.Buckle, ref metabolicEvent); + } + + private void OnStasisUnstrapped(Entity bed, ref UnstrappedEvent args) + { + if (!HasComp(args.Buckle) || !this.IsPowered(bed, EntityManager)) return; - var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.BuckledEntity, component.Multiplier, args.Buckling); - RaiseLocalEvent(args.BuckledEntity, ref metabolicEvent); + var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.Buckle, bed.Comp.Multiplier, false); + RaiseLocalEvent(args.Buckle, ref metabolicEvent); } private void OnPowerChanged(EntityUid uid, StasisBedComponent component, ref PowerChangedEvent args) diff --git a/Content.Server/Bed/Components/HealOnBuckleComponent.cs b/Content.Server/Bed/Components/HealOnBuckleComponent.cs index f29fe30429f..3c6f3a4382b 100644 --- a/Content.Server/Bed/Components/HealOnBuckleComponent.cs +++ b/Content.Server/Bed/Components/HealOnBuckleComponent.cs @@ -5,19 +5,26 @@ namespace Content.Server.Bed.Components [RegisterComponent] public sealed partial class HealOnBuckleComponent : Component { - [DataField("damage", required: true)] - [ViewVariables(VVAccess.ReadWrite)] + /// + /// Damage to apply to entities that are strapped to this entity. + /// + [DataField(required: true)] public DamageSpecifier Damage = default!; - [DataField("healTime", required: false)] - [ViewVariables(VVAccess.ReadWrite)] - public float HealTime = 1f; // How often the bed applies the damage + /// + /// How frequently the damage should be applied, in seconds. + /// + [DataField(required: false)] + public float HealTime = 1f; - [DataField("sleepMultiplier")] + /// + /// Damage multiplier that gets applied if the entity is sleeping. + /// + [DataField] public float SleepMultiplier = 3f; public TimeSpan NextHealTime = TimeSpan.Zero; //Next heal - [DataField("sleepAction")] public EntityUid? SleepAction; + [DataField] public EntityUid? SleepAction; } } diff --git a/Content.Server/Bed/Components/HealOnBuckleHealing.cs b/Content.Server/Bed/Components/HealOnBuckleHealing.cs index a944e67e12d..aaa82c737c5 100644 --- a/Content.Server/Bed/Components/HealOnBuckleHealing.cs +++ b/Content.Server/Bed/Components/HealOnBuckleHealing.cs @@ -1,5 +1,6 @@ namespace Content.Server.Bed.Components { + // TODO rename this component [RegisterComponent] public sealed partial class HealOnBuckleHealingComponent : Component {} diff --git a/Content.Server/Bed/Components/StasisBedComponent.cs b/Content.Server/Bed/Components/StasisBedComponent.cs index bb4096a2a5e..6e0042b2df8 100644 --- a/Content.Server/Bed/Components/StasisBedComponent.cs +++ b/Content.Server/Bed/Components/StasisBedComponent.cs @@ -12,7 +12,8 @@ public sealed partial class StasisBedComponent : Component /// /// What the metabolic update rate will be multiplied by (higher = slower metabolism) /// - [ViewVariables(VVAccess.ReadWrite)] + [ViewVariables(VVAccess.ReadOnly)] // Writing is is not supported. ApplyMetabolicMultiplierEvent needs to be refactored first + [DataField] public float Multiplier = 10f; [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] diff --git a/Content.Server/Bed/Sleep/SleepingSystem.cs b/Content.Server/Bed/Sleep/SleepingSystem.cs index 5e4f0eddb52..83f7be6ba2f 100644 --- a/Content.Server/Bed/Sleep/SleepingSystem.cs +++ b/Content.Server/Bed/Sleep/SleepingSystem.cs @@ -95,7 +95,7 @@ private void OnDamageChanged(EntityUid uid, SleepingComponent component, DamageC return; if (args.DamageDelta.GetTotal() >= component.WakeThreshold) - TryWaking(uid, component); + TryWaking(uid, component, true); } private void OnSleepAction(EntityUid uid, MobStateComponent component, SleepActionEvent args) diff --git a/Content.Server/Body/Components/BloodstreamComponent.cs b/Content.Server/Body/Components/BloodstreamComponent.cs index f7024a63951..ee0de4aa4dc 100644 --- a/Content.Server/Body/Components/BloodstreamComponent.cs +++ b/Content.Server/Body/Components/BloodstreamComponent.cs @@ -2,6 +2,7 @@ using Content.Server.Chemistry.EntitySystems; using Content.Server.Traits; using Content.Server.Traits.Assorted; +using Content.Shared.Alert; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.Damage; @@ -181,5 +182,8 @@ public sealed partial class BloodstreamComponent : Component /// [ViewVariables(VVAccess.ReadWrite)] public TimeSpan StatusTime; + + [DataField] + public ProtoId BleedingAlert = "Bleed"; } } diff --git a/Content.Server/Body/Components/InternalsComponent.cs b/Content.Server/Body/Components/InternalsComponent.cs index 18caab8dcf0..098f1789218 100644 --- a/Content.Server/Body/Components/InternalsComponent.cs +++ b/Content.Server/Body/Components/InternalsComponent.cs @@ -1,3 +1,6 @@ +using Content.Shared.Alert; +using Robust.Shared.Prototypes; + namespace Content.Server.Body.Components { /// @@ -18,5 +21,8 @@ public sealed partial class InternalsComponent : Component [ViewVariables(VVAccess.ReadWrite)] [DataField] public TimeSpan Delay = TimeSpan.FromSeconds(3); + + [DataField] + public ProtoId InternalsAlert = "Internals"; } } diff --git a/Content.Server/Body/Components/LungComponent.cs b/Content.Server/Body/Components/LungComponent.cs index 46600b30207..72af4d9e63a 100644 --- a/Content.Server/Body/Components/LungComponent.cs +++ b/Content.Server/Body/Components/LungComponent.cs @@ -1,8 +1,8 @@ -using Content.Server.Atmos; using Content.Server.Body.Systems; using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Chemistry.Components; +using Robust.Shared.Prototypes; namespace Content.Server.Body.Components; @@ -33,5 +33,5 @@ public sealed partial class LungComponent : Component /// The type of gas this lung needs. Used only for the breathing alerts, not actual metabolism. /// [DataField] - public AlertType Alert = AlertType.LowOxygen; + public ProtoId Alert = "LowOxygen"; } diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index d1fad6541ba..095018f9b9a 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -280,6 +280,9 @@ private void OnApplyMetabolicMultiplier( Entity ent, ref ApplyMetabolicMultiplierEvent args) { + // TODO REFACTOR THIS + // This will slowly drift over time due to floating point errors. + // Instead, raise an event with the base rates and allow modifiers to get applied to it. if (args.Apply) { ent.Comp.UpdateInterval *= args.Multiplier; @@ -412,11 +415,11 @@ public bool TryModifyBleedAmount(EntityUid uid, float amount, BloodstreamCompone component.BleedAmount = Math.Clamp(component.BleedAmount, 0, component.MaxBleedAmount); if (component.BleedAmount == 0) - _alertsSystem.ClearAlert(uid, AlertType.Bleed); + _alertsSystem.ClearAlert(uid, component.BleedingAlert); else { var severity = (short) Math.Clamp(Math.Round(component.BleedAmount, MidpointRounding.ToZero), 0, 10); - _alertsSystem.ShowAlert(uid, AlertType.Bleed, severity); + _alertsSystem.ShowAlert(uid, component.BleedingAlert, severity); } return true; diff --git a/Content.Server/Body/Systems/InternalsSystem.cs b/Content.Server/Body/Systems/InternalsSystem.cs index db078e2f291..fdcc76718cf 100644 --- a/Content.Server/Body/Systems/InternalsSystem.cs +++ b/Content.Server/Body/Systems/InternalsSystem.cs @@ -145,12 +145,12 @@ private void OnDoAfter(Entity ent, ref InternalsDoAfterEvent private void OnInternalsStartup(Entity ent, ref ComponentStartup args) { - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); + _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } private void OnInternalsShutdown(Entity ent, ref ComponentShutdown args) { - _alerts.ClearAlert(ent, AlertType.Internals); + _alerts.ClearAlert(ent, ent.Comp.InternalsAlert); } private void OnInhaleLocation(Entity ent, ref InhaleLocationEvent args) @@ -160,7 +160,7 @@ private void OnInhaleLocation(Entity ent, ref InhaleLocation var gasTank = Comp(ent.Comp.GasTankEntity!.Value); args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume); // TODO: Should listen to gas tank updates instead I guess? - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); + _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } } public void DisconnectBreathTool(Entity ent) @@ -174,7 +174,7 @@ public void DisconnectBreathTool(Entity ent) DisconnectTank(ent); } - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); + _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } public void ConnectBreathTool(Entity ent, EntityUid toolEntity) @@ -185,7 +185,7 @@ public void ConnectBreathTool(Entity ent, EntityUid toolEnti } ent.Comp.BreathToolEntity = toolEntity; - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); + _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); } public void DisconnectTank(InternalsComponent? component) @@ -197,7 +197,7 @@ public void DisconnectTank(InternalsComponent? component) _gasTank.DisconnectFromInternals((component.GasTankEntity.Value, tank)); component.GasTankEntity = null; - _alerts.ShowAlert(component.Owner, AlertType.Internals, GetSeverity(component)); + _alerts.ShowAlert(component.Owner, component.InternalsAlert, GetSeverity(component)); } public bool TryConnectTank(Entity ent, EntityUid tankEntity) @@ -209,7 +209,7 @@ public bool TryConnectTank(Entity ent, EntityUid tankEntity) _gasTank.DisconnectFromInternals((ent.Comp.GasTankEntity.Value, tank)); ent.Comp.GasTankEntity = tankEntity; - _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent)); + _alerts.ShowAlert(ent, ent.Comp.InternalsAlert, GetSeverity(ent)); return true; } diff --git a/Content.Server/Body/Systems/MetabolizerSystem.cs b/Content.Server/Body/Systems/MetabolizerSystem.cs index 066bf0a1c5b..a7eec8e3c02 100644 --- a/Content.Server/Body/Systems/MetabolizerSystem.cs +++ b/Content.Server/Body/Systems/MetabolizerSystem.cs @@ -67,6 +67,9 @@ private void OnApplyMetabolicMultiplier( Entity ent, ref ApplyMetabolicMultiplierEvent args) { + // TODO REFACTOR THIS + // This will slowly drift over time due to floating point errors. + // Instead, raise an event with the base rates and allow modifiers to get applied to it. if (args.Apply) { ent.Comp.UpdateInterval *= args.Multiplier; @@ -232,6 +235,9 @@ private void TryMetabolize(Entity ent, ref ApplyMetabolicMultiplierEvent args) { + // TODO REFACTOR THIS + // This will slowly drift over time due to floating point errors. + // Instead, raise an event with the base rates and allow modifiers to get applied to it. if (args.Apply) { ent.Comp.UpdateInterval *= args.Multiplier; diff --git a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs index 1c5c7ed1c0d..980163f937d 100644 --- a/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs +++ b/Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs @@ -420,7 +420,7 @@ private void SetupTradePost() var shuttleComponent = EnsureComp(grid); shuttleComponent.AngularDamping = 10000; shuttleComponent.LinearDamping = 10000; - Dirty(shuttleComponent); + Dirty(grid, shuttleComponent); } var mapUid = _mapManager.GetMapEntityId(CargoMap.Value); diff --git a/Content.Server/Carrying/CarryingSystem.cs b/Content.Server/Carrying/CarryingSystem.cs index 857c3861a74..ca69d2f9299 100644 --- a/Content.Server/Carrying/CarryingSystem.cs +++ b/Content.Server/Carrying/CarryingSystem.cs @@ -67,7 +67,7 @@ public override void Initialize() SubscribeLocalEvent(OnInteractedWith); SubscribeLocalEvent(OnPullAttempt); SubscribeLocalEvent(OnStartClimb); - SubscribeLocalEvent(OnBuckleChange); + SubscribeLocalEvent(OnBuckled); SubscribeLocalEvent(OnDoAfter); } @@ -215,7 +215,7 @@ private void OnStartClimb(EntityUid uid, BeingCarriedComponent component, ref St DropCarried(component.Carrier, uid); } - private void OnBuckleChange(EntityUid uid, BeingCarriedComponent component, ref BuckleChangeEvent args) + private void OnBuckled(EntityUid uid, BeingCarriedComponent component, ref BuckledEvent args) { DropCarried(component.Carrier, uid); } diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index bbca6f4d153..013ddab7a33 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -343,11 +343,41 @@ public void DispatchGlobalAnnouncement( _chatManager.ChatMessageToAll(ChatChannel.Radio, message, wrappedMessage, default, false, true, colorOverride); if (playSound) { - _audio.PlayGlobal(announcementSound?.GetSound() ?? DefaultAnnouncementSound, Filter.Broadcast(), true, AudioParams.Default.WithVolume(-2f)); + _audio.PlayGlobal(announcementSound == null ? DefaultAnnouncementSound : _audio.GetSound(announcementSound), Filter.Broadcast(), true, AudioParams.Default.WithVolume(-2f)); } _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Global station announcement from {sender}: {message}"); } + /// + /// Dispatches an announcement to players selected by filter. + /// + /// Filter to select players who will recieve the announcement + /// The contents of the message + /// The entity making the announcement (used to determine the station) + /// The sender (Communications Console in Communications Console Announcement) + /// Play the announcement sound + /// Sound to play + /// Optional color for the announcement message + public void DispatchFilteredAnnouncement( + Filter filter, + string message, + EntityUid? source = null, + string? sender = null, + bool playSound = true, + SoundSpecifier? announcementSound = null, + Color? colorOverride = null) + { + sender ??= Loc.GetString("chat-manager-sender-announcement"); + + var wrappedMessage = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender), ("message", FormattedMessage.EscapeText(message))); + _chatManager.ChatMessageToManyFiltered(filter, ChatChannel.Radio, message, wrappedMessage, source ?? default, false, true, colorOverride); + if (playSound) + { + _audio.PlayGlobal(announcementSound?.ToString() ?? DefaultAnnouncementSound, filter, true, AudioParams.Default.WithVolume(-2f)); + } + _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Station Announcement from {sender}: {message}"); + } + /// /// Dispatches an announcement on a specific station /// @@ -381,7 +411,7 @@ public void DispatchStationAnnouncement( if (playDefaultSound) { - _audio.PlayGlobal(announcementSound?.GetSound() ?? DefaultAnnouncementSound, filter, true, AudioParams.Default.WithVolume(-2f)); + _audio.PlayGlobal(announcementSound?.ToString() ?? DefaultAnnouncementSound, filter, true, AudioParams.Default.WithVolume(-2f)); } _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Station Announcement on {station} from {sender}: {message}"); @@ -405,7 +435,7 @@ private void SendEntitySpeak( return; // The original message - var message = TransformSpeech(source, FormattedMessage.RemoveMarkup(originalMessage), language); + var message = TransformSpeech(source, FormattedMessage.RemoveMarkupPermissive(originalMessage), language); if (message.Length == 0) return; @@ -478,7 +508,7 @@ private void SendEntityWhisper( if (!_actionBlocker.CanSpeak(source) && !ignoreActionBlocker) return; - var message = TransformSpeech(source, FormattedMessage.RemoveMarkup(originalMessage), language); + var message = TransformSpeech(source, FormattedMessage.RemoveMarkupPermissive(originalMessage), language); if (message.Length == 0) return; @@ -588,7 +618,7 @@ private void SendEntityEmote( var wrappedMessage = Loc.GetString("chat-manager-entity-me-wrap-message", ("entityName", name), ("entity", ent), - ("message", FormattedMessage.RemoveMarkup(action))); + ("message", FormattedMessage.RemoveMarkupPermissive(action))); if (checkEmote) TryEmoteChatInput(source, action); @@ -931,7 +961,7 @@ private Dictionary GetRecipients(EntityUid recipients.Add(player, new ICChatRecipientData(-1, true)); } - RaiseLocalEvent(new ExpandICChatRecipientstEvent(source, voiceGetRange, recipients)); + RaiseLocalEvent(new ExpandICChatRecipientsEvent(source, voiceGetRange, recipients)); return recipients; } @@ -989,7 +1019,7 @@ private bool CheckAttachedGrids(EntityUid source, EntityUid receiver) /// This event is raised before chat messages are sent out to clients. This enables some systems to send the chat /// messages to otherwise out-of view entities (e.g. for multiple viewports from cameras). /// -public record ExpandICChatRecipientstEvent(EntityUid Source, float VoiceRange, Dictionary Recipients) +public record ExpandICChatRecipientsEvent(EntityUid Source, float VoiceRange, Dictionary Recipients) { } diff --git a/Content.Server/Chat/TelepathicChatSystem.cs b/Content.Server/Chat/TelepathicChatSystem.cs index b1338035adb..f5dbdeafa3e 100644 --- a/Content.Server/Chat/TelepathicChatSystem.cs +++ b/Content.Server/Chat/TelepathicChatSystem.cs @@ -52,7 +52,7 @@ public override void Initialize() private IEnumerable GetAdminClients() { return _adminManager.ActiveAdmins - .Select(p => p.ConnectedClient); + .Select(p => p.Channel); } private List GetDreamers(IEnumerable removeList) @@ -63,7 +63,7 @@ private List GetDreamers(IEnumerable removeList) || HasComp(entity) || HasComp(entity) && !HasComp(entity) && !HasComp(entity)) .Recipients - .Select(p => p.ConnectedClient); + .Select(p => p.Channel); var filteredList = filtered.ToList(); diff --git a/Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs b/Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs index 8d475570ad0..40858176bd1 100644 --- a/Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs +++ b/Content.Server/Chemistry/ReagentEffects/AdjustAlert.cs @@ -10,8 +10,8 @@ public sealed partial class AdjustAlert : ReagentEffect /// /// The specific Alert that will be adjusted /// - [DataField("alertType", required: true)] - public AlertType Type; + [DataField(required: true)] + public ProtoId AlertType; /// /// If true, the alert is removed after Time seconds. If Time was not specified the alert is removed immediately. @@ -42,7 +42,7 @@ public override void Effect(ReagentEffectArgs args) if (Clear && Time <= 0) { - alertSys.ClearAlert(args.SolutionEntity, Type); + alertSys.ClearAlert(args.SolutionEntity, AlertType); } else { @@ -52,7 +52,7 @@ public override void Effect(ReagentEffectArgs args) if ((ShowCooldown || Clear) && Time > 0) cooldown = (timing.CurTime, timing.CurTime + TimeSpan.FromSeconds(Time)); - alertSys.ShowAlert(args.SolutionEntity, Type, cooldown: cooldown, autoRemove: Clear, showCooldown: ShowCooldown); + alertSys.ShowAlert(args.SolutionEntity, AlertType, cooldown: cooldown, autoRemove: Clear, showCooldown: ShowCooldown); } } diff --git a/Content.Server/Clothing/MagbootsSystem.cs b/Content.Server/Clothing/MagbootsSystem.cs index f12558389e3..3838ad168d1 100644 --- a/Content.Server/Clothing/MagbootsSystem.cs +++ b/Content.Server/Clothing/MagbootsSystem.cs @@ -29,11 +29,11 @@ protected override void UpdateMagbootEffects(EntityUid parent, EntityUid uid, bo if (state) { - _alerts.ShowAlert(parent, AlertType.Magboots); + _alerts.ShowAlert(parent, component.MagbootsAlert); } else { - _alerts.ClearAlert(parent, AlertType.Magboots); + _alerts.ClearAlert(parent, component.MagbootsAlert); } } diff --git a/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs b/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs index e20a6c3da97..feb3428884c 100644 --- a/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs +++ b/Content.Server/Clothing/Systems/ChameleonClothingSystem.cs @@ -39,7 +39,7 @@ private void OnVerb(EntityUid uid, ChameleonClothingComponent component, GetVerb args.Verbs.Add(new InteractionVerb() { Text = Loc.GetString("chameleon-component-verb-text"), - Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")), + Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/settings.svg.192dpi.png")), Act = () => TryOpenUi(uid, args.User, component) }); } @@ -91,7 +91,7 @@ public void SetSelectedPrototype(EntityUid uid, string? protoId, bool forceUpdat UpdateIdentityBlocker(uid, component, proto); UpdateVisuals(uid, component); UpdateUi(uid, component); - Dirty(component); + Dirty(uid, component); } private void UpdateIdentityBlocker(EntityUid uid, ChameleonClothingComponent component, EntityPrototype proto) diff --git a/Content.Server/Decals/DecalSystem.cs b/Content.Server/Decals/DecalSystem.cs index da95401d206..c8e062ce6ff 100644 --- a/Content.Server/Decals/DecalSystem.cs +++ b/Content.Server/Decals/DecalSystem.cs @@ -89,10 +89,11 @@ private void OnPvsToggle(bool value) playerData.Clear(); } - foreach (var (grid, meta) in EntityQuery(true)) + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var grid, out var meta)) { grid.ForceTick = _timing.CurTick; - Dirty(grid, meta); + Dirty(uid, grid, meta); } } diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs index 65851f17360..093f05fceee 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs @@ -1,12 +1,11 @@ using System.Numerics; using Content.Shared.Forensics; using Content.Server.Stack; +using Content.Shared.Destructible.Thresholds; using Content.Shared.Prototypes; using Content.Shared.Stacks; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary; -using Content.Server.Administration.Commands; namespace Content.Server.Destructible.Thresholds.Behaviors { @@ -17,8 +16,8 @@ public sealed partial class SpawnEntitiesBehavior : IThresholdBehavior /// /// Entities spawned on reaching this threshold, from a min to a max. /// - [DataField("spawn", customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] - public Dictionary Spawn { get; set; } = new(); + [DataField] + public Dictionary Spawn = new(); [DataField("offset")] public float Offset { get; set; } = 0.5f; diff --git a/Content.Server/Destructible/Thresholds/MinMax.cs b/Content.Server/Destructible/Thresholds/MinMax.cs deleted file mode 100644 index c44864183ab..00000000000 --- a/Content.Server/Destructible/Thresholds/MinMax.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Robust.Shared.Random; - -namespace Content.Server.Destructible.Thresholds -{ - [Serializable] - [DataDefinition] - public partial struct MinMax - { - [DataField("min")] - public int Min; - - [DataField("max")] - public int Max; - - public MinMax(int min, int max) - { - Min = min; - Max = max; - } - - public int Next(IRobustRandom random) - { - return random.Next(Min, Max + 1); - } - } -} diff --git a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs index dc1bf499df5..34601241585 100644 --- a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs @@ -254,7 +254,7 @@ private void SetMode(EntityUid configuratorUid, NetworkConfiguratorComponent con /// private void UpdateModeAppearance(EntityUid userUid, EntityUid configuratorUid, NetworkConfiguratorComponent configurator) { - Dirty(configurator); + Dirty(configuratorUid, configurator); _appearanceSystem.SetData(configuratorUid, NetworkConfiguratorVisuals.Mode, configurator.LinkModeActive); var pitch = configurator.LinkModeActive ? 1 : 0.8f; diff --git a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs index f0f6e9142c6..8668933e7bf 100644 --- a/Content.Server/Disposal/Tube/DisposalTubeSystem.cs +++ b/Content.Server/Disposal/Tube/DisposalTubeSystem.cs @@ -31,6 +31,8 @@ public sealed class DisposalTubeSystem : EntitySystem [Dependency] private readonly DisposableSystem _disposableSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; + [Dependency] private readonly SharedMapSystem _sharedMapSystem = default!; + public override void Initialize() { base.Initialize(); @@ -344,7 +346,7 @@ private void UpdateAnchored(EntityUid uid, DisposalTubeComponent component, bool return null; var position = xform.Coordinates; - foreach (var entity in grid.GetInDir(position, nextDirection)) + foreach (var entity in _sharedMapSystem.GetInDir(target, grid, position, nextDirection)) { if (!TryComp(entity, out DisposalTubeComponent? tube)) { diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 3e81ebfb79f..6ada7d781f3 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -55,6 +55,7 @@ public sealed class DisposalUnitSystem : SharedDisposalUnitSystem [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly UserInterfaceSystem _ui = default!; + [Dependency] private readonly SharedMapSystem _sharedMapSystem = default!; public override void Initialize() { @@ -310,7 +311,7 @@ private void OnPowerChange(EntityUid uid, SharedDisposalUnitComponent component, if (!args.Powered) { component.NextFlush = null; - Dirty(component); + Dirty(uid, component); return; } @@ -366,7 +367,7 @@ private void UpdateState(EntityUid uid, DisposalsPressureState state, SharedDisp component.State = state; UpdateVisualState(uid, component); UpdateInterface(uid, component, component.Powered); - Dirty(component, metadata); + Dirty(uid, component, metadata); if (state == DisposalsPressureState.Ready) { @@ -447,7 +448,7 @@ private void Update(EntityUid uid, SharedDisposalUnitComponent component, MetaDa } if (count != component.RecentlyEjected.Count) - Dirty(component, metadata); + Dirty(uid, component, metadata); } public bool TryInsert(EntityUid unitId, EntityUid toInsertId, EntityUid? userId, DisposalUnitComponent? unit = null) @@ -516,7 +517,7 @@ public bool TryFlush(EntityUid uid, SharedDisposalUnitComponent component) return false; var coords = xform.Coordinates; - var entry = grid.GetLocal(coords) + var entry = _sharedMapSystem.GetLocal(uid, grid, coords) .FirstOrDefault(HasComp); if (entry == default || component is not DisposalUnitComponent sDisposals) @@ -754,7 +755,7 @@ public void QueueAutomaticEngage(EntityUid uid, SharedDisposalUnitComponent comp var flushTime = TimeSpan.FromSeconds(Math.Min((component.NextFlush ?? TimeSpan.MaxValue).TotalSeconds, automaticTime.TotalSeconds)); component.NextFlush = flushTime; - Dirty(component); + Dirty(uid, component); } public void AfterInsert(EntityUid uid, SharedDisposalUnitComponent component, EntityUid inserted, EntityUid? user = null, bool doInsert = false) diff --git a/Content.Server/Dragon/DragonRiftSystem.cs b/Content.Server/Dragon/DragonRiftSystem.cs index c0a81d0d24e..b0dd87d3fdb 100644 --- a/Content.Server/Dragon/DragonRiftSystem.cs +++ b/Content.Server/Dragon/DragonRiftSystem.cs @@ -70,7 +70,7 @@ public override void Update(float frameTime) if (comp.State < DragonRiftState.AlmostFinished && comp.Accumulator > comp.MaxAccumulator / 2f) { comp.State = DragonRiftState.AlmostFinished; - Dirty(comp); + Dirty(uid, comp); _announcer.SendAnnouncement(_announcer.GetAnnouncementId("CarpRift"), Filter.Broadcast(), "carp-rift-warning", colorOverride: Color.Red, localeArgs: ("location", FormattedMessage.RemoveMarkupPermissive(_navMap.GetNearestBeaconString((uid, xform))))); diff --git a/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs b/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs index 202d03bcda9..5117d67e944 100644 --- a/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs +++ b/Content.Server/Ensnaring/EnsnareableSystem.Ensnaring.cs @@ -93,7 +93,7 @@ public void TryEnsnare(EntityUid target, EntityUid ensnare, EnsnaringComponent c component.Ensnared = target; _container.Insert(ensnare, ensnareable.Container); ensnareable.IsEnsnared = true; - Dirty(ensnareable); + Dirty(target, ensnareable); UpdateAlert(target, ensnareable); var ev = new EnsnareEvent(component.WalkSpeed, component.SprintSpeed); @@ -107,7 +107,7 @@ public void TryEnsnare(EntityUid target, EntityUid ensnare, EnsnaringComponent c /// The entity that is freeing the target /// The entity used to ensnare /// The ensnaring component - public void TryFree(EntityUid target, EntityUid user, EntityUid ensnare, EnsnaringComponent component) + public void TryFree(EntityUid target, EntityUid user, EntityUid ensnare, EnsnaringComponent component) { //Don't do anything if they don't have the ensnareable component. if (!HasComp(target)) @@ -149,7 +149,7 @@ public void ForceFree(EntityUid ensnare, EnsnaringComponent component) _container.Remove(ensnare, ensnareable.Container, force: true); ensnareable.IsEnsnared = ensnareable.Container.ContainedEntities.Count > 0; - Dirty(ensnareable); + Dirty(component.Ensnared.Value, ensnareable); component.Ensnared = null; UpdateAlert(target, ensnareable); @@ -164,8 +164,8 @@ public void ForceFree(EntityUid ensnare, EnsnaringComponent component) public void UpdateAlert(EntityUid target, EnsnareableComponent component) { if (!component.IsEnsnared) - _alerts.ClearAlert(target, AlertType.Ensnared); + _alerts.ClearAlert(target, component.EnsnaredAlert); else - _alerts.ShowAlert(target, AlertType.Ensnared); + _alerts.ShowAlert(target, component.EnsnaredAlert); } } diff --git a/Content.Server/Ensnaring/EnsnareableSystem.cs b/Content.Server/Ensnaring/EnsnareableSystem.cs index 7b810b4f49c..0cf4efa21b2 100644 --- a/Content.Server/Ensnaring/EnsnareableSystem.cs +++ b/Content.Server/Ensnaring/EnsnareableSystem.cs @@ -45,7 +45,7 @@ private void OnDoAfter(EntityUid uid, EnsnareableComponent component, DoAfterEve } component.IsEnsnared = component.Container.ContainedEntities.Count > 0; - Dirty(component); + Dirty(uid, component); ensnaring.Ensnared = null; if (ensnaring.DestroyOnRemove) diff --git a/Content.Server/GameTicking/GameTicker.GameRule.cs b/Content.Server/GameTicking/GameTicker.GameRule.cs index f52a3cb296d..9b0ca6b2bbc 100644 --- a/Content.Server/GameTicking/GameTicker.GameRule.cs +++ b/Content.Server/GameTicking/GameTicker.GameRule.cs @@ -1,8 +1,9 @@ using System.Linq; using Content.Server.Administration; -using Content.Server.GameTicking.Components; +using Content.Server.GameTicking.Rules.Components; using Content.Shared.Administration; using Content.Shared.Database; +using Content.Shared.GameTicking.Components; using Content.Shared.Prototypes; using JetBrains.Annotations; using Robust.Shared.Console; diff --git a/Content.Server/GameTicking/GameTicker.Lobby.cs b/Content.Server/GameTicking/GameTicker.Lobby.cs index 82ef8c6012b..61d9fd99cae 100644 --- a/Content.Server/GameTicking/GameTicker.Lobby.cs +++ b/Content.Server/GameTicking/GameTicker.Lobby.cs @@ -179,5 +179,11 @@ public void ToggleReady(ICommonSession player, bool ready) // update server info to reflect new ready count UpdateInfoText(); } + + public bool UserHasJoinedGame(ICommonSession session) + => UserHasJoinedGame(session.UserId); + + public bool UserHasJoinedGame(NetUserId userId) + => PlayerGameStatuses[userId] == PlayerGameStatus.JoinedGame; } } diff --git a/Content.Server/GameTicking/GameTicker.Spawning.cs b/Content.Server/GameTicking/GameTicker.Spawning.cs index 3016195f4b7..c6c8bf50f75 100644 --- a/Content.Server/GameTicking/GameTicker.Spawning.cs +++ b/Content.Server/GameTicking/GameTicker.Spawning.cs @@ -460,11 +460,16 @@ private EntityUid SpawnObserverMob() public EntityCoordinates GetObserverSpawnPoint() { _possiblePositions.Clear(); - - foreach (var (point, transform) in EntityManager.EntityQuery(true)) + var spawnPointQuery = EntityManager.EntityQueryEnumerator(); + while (spawnPointQuery.MoveNext(out var uid, out var point, out var transform)) { - if (point.SpawnType != SpawnPointType.Observer) + if (point.SpawnType != SpawnPointType.Observer + || TerminatingOrDeleted(uid) + || transform.MapUid == null + || TerminatingOrDeleted(transform.MapUid.Value)) + { continue; + } _possiblePositions.Add(transform.Coordinates); } @@ -506,7 +511,9 @@ public EntityCoordinates GetObserverSpawnPoint() if (_mapManager.MapExists(DefaultMap)) { - return new EntityCoordinates(_mapManager.GetMapEntityId(DefaultMap), Vector2.Zero); + var mapUid = _mapManager.GetMapEntityId(DefaultMap); + if (!TerminatingOrDeleted(mapUid)) + return new EntityCoordinates(mapUid, Vector2.Zero); } // Just pick a point at this point I guess. diff --git a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs index 78b8a8a85c8..d03d040261a 100644 --- a/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/DeathMatchRuleSystem.cs @@ -1,12 +1,12 @@ using System.Linq; using Content.Server.Administration.Commands; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.KillTracking; using Content.Server.Mind; using Content.Server.Points; using Content.Server.RoundEnd; using Content.Server.Station.Systems; +using Content.Shared.GameTicking.Components; using Content.Shared.Points; using Content.Shared.Storage; using Robust.Server.Player; diff --git a/Content.Server/GameTicking/Rules/GameRulePrototype.cs b/Content.Server/GameTicking/Rules/GameRulePrototype.cs deleted file mode 100644 index 47f99184f73..00000000000 --- a/Content.Server/GameTicking/Rules/GameRulePrototype.cs +++ /dev/null @@ -1,15 +0,0 @@ - - -namespace Content.Server.GameTicking.Rules; - -/* -[Prototype("gameRule")] -public sealed partial class GameRulePrototype : IPrototype -{ - [IdDataField] - public string ID { get; private set; } = default!; - - [DataField("config", required: true)] - public GameRuleConfiguration Configuration { get; private set; } = default!; -} -*/ diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs index cbd981e99e6..b72ba59ca27 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.Utility.cs @@ -1,8 +1,8 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Random.Helpers; using Robust.Server.GameObjects; using Robust.Shared.Collections; diff --git a/Content.Server/GameTicking/Rules/GameRuleSystem.cs b/Content.Server/GameTicking/Rules/GameRuleSystem.cs index 05374aa1396..730748ce6b9 100644 --- a/Content.Server/GameTicking/Rules/GameRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/GameRuleSystem.cs @@ -1,6 +1,6 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Robust.Server.GameObjects; using Robust.Shared.Random; using Robust.Shared.Timing; diff --git a/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs b/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs index 01fa387595c..e56537c4381 100644 --- a/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/InactivityTimeRestartRuleSystem.cs @@ -1,7 +1,7 @@ using System.Threading; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Robust.Server.Player; using Robust.Shared.Player; using Timer = Robust.Shared.Timing.Timer; diff --git a/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs b/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs index 3da55e30c9e..8f706fd2ad3 100644 --- a/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/KillCalloutRuleSystem.cs @@ -1,8 +1,8 @@ using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.KillTracking; using Content.Shared.Chat; +using Content.Shared.GameTicking.Components; using Robust.Server.Player; using Robust.Shared.Player; using Robust.Shared.Random; diff --git a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs index 3a80d82fd92..30786c84558 100644 --- a/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/LoadMapRuleSystem.cs @@ -1,8 +1,8 @@ using Content.Server.Antag; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.GridPreloader; using Content.Server.Spawners.Components; +using Content.Shared.GameTicking.Components; using Robust.Server.GameObjects; using Robust.Server.Maps; using Robust.Shared.Map; diff --git a/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs b/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs index cae99fee9fc..db9df8a5b00 100644 --- a/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/MaxTimeRestartRuleSystem.cs @@ -1,7 +1,7 @@ using System.Threading; using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Timer = Robust.Shared.Timing.Timer; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs index 3e61fda8744..20f0ffea9a3 100644 --- a/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs @@ -13,8 +13,7 @@ using Content.Server.Station.Components; using Content.Server.Store.Components; using Content.Server.Store.Systems; -using Content.Shared.Humanoid; -using Content.Shared.Humanoid.Prototypes; +using Content.Shared.GameTicking.Components; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Content.Shared.Nuke; @@ -28,9 +27,10 @@ using Robust.Shared.Random; using Robust.Shared.Utility; using System.Linq; -using Content.Server.GameTicking.Components; -using Content.Server.NPC.Components; using Content.Server.NPC.Systems; +using Content.Server.NPC.Components; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Prototypes; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs index 5215da96aa8..920668472ad 100644 --- a/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RespawnRuleSystem.cs @@ -1,8 +1,9 @@ using Content.Server.Chat.Managers; -using Content.Server.GameTicking.Components; +using Content.Server.Database.Migrations.Postgres; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Systems; using Content.Shared.Chat; +using Content.Shared.GameTicking.Components; using Content.Shared.Interaction.Events; using Content.Shared.Mind; using Content.Shared.Mobs; diff --git a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs index 577b0c30303..29f69db14a2 100644 --- a/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RevolutionaryRuleSystem.cs @@ -14,6 +14,7 @@ using Content.Server.Shuttles.Systems; using Content.Server.Station.Systems; using Content.Shared.Database; +using Content.Shared.GameTicking.Components; using Content.Shared.Humanoid; using Content.Shared.IdentityManagement; using Content.Shared.Mind; @@ -27,7 +28,6 @@ using Content.Shared.Zombies; using Robust.Shared.Prototypes; using Robust.Shared.Timing; -using Content.Server.GameTicking.Components; using Content.Shared.Cuffs.Components; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs b/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs index f09ed3ebc3c..570889155b3 100644 --- a/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/RoundstartStationVariationRuleSystem.cs @@ -1,9 +1,9 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Shuttles.Systems; using Content.Server.Station.Components; using Content.Server.Station.Events; +using Content.Shared.GameTicking.Components; using Content.Shared.Storage; using Robust.Shared.Prototypes; using Robust.Shared.Random; diff --git a/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs b/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs index c60670a3ad7..23e9ee5a7d2 100644 --- a/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SandboxRuleSystem.cs @@ -1,6 +1,6 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Sandbox; +using Content.Shared.GameTicking.Components; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs index 95bf5986a5a..3542b2e0864 100644 --- a/Content.Server/GameTicking/Rules/SecretRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SecretRuleSystem.cs @@ -1,10 +1,10 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Content.Server.Administration.Logs; -using Content.Server.GameTicking.Components; using Content.Server.Chat.Managers; using Content.Server.GameTicking.Presets; using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Random; using Content.Shared.CCVar; using Content.Shared.Database; diff --git a/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs b/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs index 4486ee40fbb..4fe3827ce5c 100644 --- a/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs +++ b/Content.Server/GameTicking/Rules/SubGamemodesSystem.cs @@ -1,5 +1,5 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Storage; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs index 1db288799f2..3d0a02d6aa9 100644 --- a/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorRuleSystem.cs @@ -5,8 +5,11 @@ using Content.Server.Objectives; using Content.Server.PDA.Ringer; using Content.Server.Roles; +using Content.Server.Traitor.Components; using Content.Server.Traitor.Uplink; +using Content.Shared.GameTicking.Components; using Content.Shared.Mind; +using Content.Shared.Mood; using Content.Shared.Objectives.Components; using Content.Shared.PDA; using Content.Shared.Roles; @@ -15,9 +18,6 @@ using Robust.Shared.Random; using System.Linq; using System.Text; -using Content.Shared.Mood; -using Content.Server.GameTicking.Components; -using Content.Server.Traitor.Components; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs index 1361ab37338..9158d9af656 100644 --- a/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/ZombieRuleSystem.cs @@ -1,3 +1,4 @@ +using Content.Server.Announcements.Systems; using Content.Server.Antag; using Content.Server.Chat.Systems; using Content.Server.GameTicking.Rules.Components; @@ -6,6 +7,7 @@ using Content.Server.Station.Components; using Content.Server.Station.Systems; using Content.Server.Zombies; +using Content.Shared.GameTicking.Components; using Content.Shared.Humanoid; using Content.Shared.Mind; using Content.Shared.Mobs; @@ -15,8 +17,6 @@ using Robust.Shared.Player; using Robust.Shared.Timing; using System.Globalization; -using Content.Server.Announcements.Systems; -using Content.Server.GameTicking.Components; namespace Content.Server.GameTicking.Rules; diff --git a/Content.Server/Geras/GerasComponent.cs b/Content.Server/Geras/GerasComponent.cs index eaf792502f4..df1fccd7185 100644 --- a/Content.Server/Geras/GerasComponent.cs +++ b/Content.Server/Geras/GerasComponent.cs @@ -12,7 +12,7 @@ public sealed partial class GerasComponent : Component { [DataField] public ProtoId GerasPolymorphId = "SlimeMorphGeras"; - [DataField] public ProtoId GerasAction = "ActionMorphGeras"; + [DataField] public EntProtoId GerasAction = "ActionMorphGeras"; [DataField] public EntityUid? GerasActionEntity; } diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index 254d478bb0f..effed5cdbe1 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -409,23 +409,41 @@ public bool DoGhostBooEvent(EntityUid target) return SpawnGhost(mind, spawnPosition, canReturn); } + private bool IsValidSpawnPosition(EntityCoordinates? spawnPosition) + { + if (spawnPosition?.IsValid(EntityManager) != true) + return false; + + var mapUid = spawnPosition?.GetMapUid(EntityManager); + var gridUid = spawnPosition?.EntityId; + // Test if the map is being deleted + if (mapUid == null || TerminatingOrDeleted(mapUid.Value)) + return false; + // Test if the grid is being deleted + if (gridUid != null && TerminatingOrDeleted(gridUid.Value)) + return false; + + return true; + } + public EntityUid? SpawnGhost(Entity mind, EntityCoordinates? spawnPosition = null, bool canReturn = false) { if (!Resolve(mind, ref mind.Comp)) return null; - // Test if the map is being deleted - var mapUid = spawnPosition?.GetMapUid(EntityManager); - if (mapUid == null || TerminatingOrDeleted(mapUid.Value)) + // Test if the map or grid is being deleted + if (!IsValidSpawnPosition(spawnPosition)) spawnPosition = null; + // If it's bad, look for a valid point to spawn spawnPosition ??= _ticker.GetObserverSpawnPoint(); - if (!spawnPosition.Value.IsValid(EntityManager)) + // Make sure the new point is valid too + if (!IsValidSpawnPosition(spawnPosition)) { Log.Warning($"No spawn valid ghost spawn position found for {mind.Comp.CharacterName}" - + " \"{ToPrettyString(mind)}\""); + + $" \"{ToPrettyString(mind)}\""); _minds.TransferTo(mind.Owner, null, createGhost: false, mind: mind.Comp); return null; } diff --git a/Content.Server/Gravity/GravitySystem.cs b/Content.Server/Gravity/GravitySystem.cs index 5e0332ae491..ea62d4a8195 100644 --- a/Content.Server/Gravity/GravitySystem.cs +++ b/Content.Server/Gravity/GravitySystem.cs @@ -41,7 +41,7 @@ public void RefreshGravity(EntityUid uid, GravityComponent? gravity = null) gravity.Enabled = enabled; var ev = new GravityChangedEvent(uid, enabled); RaiseLocalEvent(uid, ref ev, true); - Dirty(gravity); + Dirty(uid, gravity); if (HasComp(uid)) { @@ -71,7 +71,7 @@ public void EnableGravity(EntityUid uid, GravityComponent? gravity = null) gravity.Enabled = true; var ev = new GravityChangedEvent(uid, true); RaiseLocalEvent(uid, ref ev, true); - Dirty(gravity); + Dirty(uid, gravity); if (HasComp(uid)) { diff --git a/Content.Server/HotPotato/HotPotatoSystem.cs b/Content.Server/HotPotato/HotPotatoSystem.cs index 8091eea6fdd..115a7b6cb76 100644 --- a/Content.Server/HotPotato/HotPotatoSystem.cs +++ b/Content.Server/HotPotato/HotPotatoSystem.cs @@ -29,7 +29,7 @@ private void OnActiveTimer(EntityUid uid, HotPotatoComponent comp, ref ActiveTim comp.CanTransfer = false; _ambientSound.SetAmbience(uid, true); _damageOnHolding.SetEnabled(uid, true); - Dirty(comp); + Dirty(uid, comp); } private void OnMeleeHit(EntityUid uid, HotPotatoComponent comp, MeleeHitEvent args) @@ -56,6 +56,6 @@ private void OnMeleeHit(EntityUid uid, HotPotatoComponent comp, MeleeHitEvent ar break; } comp.CanTransfer = false; - Dirty(comp); + Dirty(uid, comp); } } diff --git a/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.Modifier.cs b/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.Modifier.cs index 06225c9d57c..7744d161519 100644 --- a/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.Modifier.cs +++ b/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.Modifier.cs @@ -29,7 +29,7 @@ private void OnVerbsRequest(EntityUid uid, HumanoidAppearanceComponent component { Text = "Modify markings", Category = VerbCategory.Tricks, - Icon = new SpriteSpecifier.Rsi(new ("/Textures/Mobs/Customization/reptilian_parts.rsi"), "tail_smooth"), + Icon = new SpriteSpecifier.Rsi(new("/Textures/Mobs/Customization/reptilian_parts.rsi"), "tail_smooth"), Act = () => { _uiSystem.OpenUi(uid, HumanoidMarkingModifierKey.Key, actor.PlayerSession); @@ -62,7 +62,7 @@ private void OnBaseLayersSet(EntityUid uid, HumanoidAppearanceComponent componen component.CustomBaseLayers[message.Layer] = message.Info.Value; } - Dirty(component); + Dirty(uid, component); if (message.ResendState) { @@ -86,7 +86,7 @@ private void OnMarkingsSet(EntityUid uid, HumanoidAppearanceComponent component, } component.MarkingSet = message.MarkingSet; - Dirty(component); + Dirty(uid, component); if (message.ResendState) { diff --git a/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.cs b/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.cs index ed6d91f56c7..1811567d270 100644 --- a/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.cs +++ b/Content.Server/Humanoid/Systems/HumanoidAppearanceSystem.cs @@ -53,9 +53,8 @@ public void CloneAppearance(EntityUid source, EntityUid target, HumanoidAppearan grammar.Gender = sourceHumanoid.Gender; } - targetHumanoid.LastProfileLoaded = sourceHumanoid.LastProfileLoaded; // DeltaV - let paradox anomaly be cloned - - Dirty(targetHumanoid); + targetHumanoid.LastProfileLoaded = sourceHumanoid.LastProfileLoaded; + Dirty(target, targetHumanoid); } /// @@ -76,7 +75,7 @@ public void RemoveMarking(EntityUid uid, string marking, bool sync = true, Human humanoid.MarkingSet.Remove(prototype.MarkingCategory, marking); if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -97,7 +96,7 @@ public void RemoveMarking(EntityUid uid, MarkingCategories category, int index, } humanoid.MarkingSet.Remove(category, index); - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -126,7 +125,7 @@ public void SetMarkingId(EntityUid uid, MarkingCategories category, int index, s } humanoid.MarkingSet.Replace(category, index, marking); - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -153,6 +152,6 @@ public void SetMarkingColor(EntityUid uid, MarkingCategories category, int index markings[index].SetColor(i, colors[i]); } - Dirty(humanoid); + Dirty(uid, humanoid); } } diff --git a/Content.Server/Instruments/SwappableInstrumentSystem.cs b/Content.Server/Instruments/SwappableInstrumentSystem.cs index 3f3cfb9e6db..9aef875cd65 100644 --- a/Content.Server/Instruments/SwappableInstrumentSystem.cs +++ b/Content.Server/Instruments/SwappableInstrumentSystem.cs @@ -35,7 +35,7 @@ private void AddStyleVerb(EntityUid uid, SwappableInstrumentComponent component, Priority = priority, Act = () => { - _sharedInstrument.SetInstrumentProgram(instrument, entry.Value.Item1, entry.Value.Item2); + _sharedInstrument.SetInstrumentProgram(uid, instrument, entry.Value.Item1, entry.Value.Item2); _popup.PopupEntity(Loc.GetString("swappable-instrument-component-style-set", ("style", entry.Key)), args.User, args.User); } diff --git a/Content.Server/InteractionVerbs/Actions/ChangeStandingStateAction.cs b/Content.Server/InteractionVerbs/Actions/ChangeStandingStateAction.cs index 59340df58f2..0d74781f62d 100644 --- a/Content.Server/InteractionVerbs/Actions/ChangeStandingStateAction.cs +++ b/Content.Server/InteractionVerbs/Actions/ChangeStandingStateAction.cs @@ -33,7 +33,7 @@ public override bool Perform(InteractionArgs args, InteractionVerbPrototype prot if (state.CurrentState == StandingState.Lying && MakeStanding) return stateSystem.Stand(args.Target); else if (state.CurrentState == StandingState.Standing && MakeLaying) - return stateSystem.Down(args.Target, setDrawDepth: true); + return stateSystem.Down(args.Target); return false; } diff --git a/Content.Server/Light/EntitySystems/RotatingLightSystem.cs b/Content.Server/Light/EntitySystems/RotatingLightSystem.cs index dd72b3a43e8..7ef1357dc31 100644 --- a/Content.Server/Light/EntitySystems/RotatingLightSystem.cs +++ b/Content.Server/Light/EntitySystems/RotatingLightSystem.cs @@ -19,6 +19,6 @@ private void OnLightToggle(EntityUid uid, RotatingLightComponent comp, PointLigh return; comp.Enabled = args.Enabled; - Dirty(comp); + Dirty(uid, comp); } } diff --git a/Content.Server/Lightning/LightningSystem.cs b/Content.Server/Lightning/LightningSystem.cs index 4f975a60fda..0c86646bcbe 100644 --- a/Content.Server/Lightning/LightningSystem.cs +++ b/Content.Server/Lightning/LightningSystem.cs @@ -20,20 +20,24 @@ public sealed class LightningSystem : SharedLightningSystem [Dependency] private readonly BeamSystem _beam = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + + private List _targets = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnRemove); + + _targets = new(); } private void OnRemove(EntityUid uid, LightningComponent component, ComponentRemove args) { if (!TryComp(uid, out var lightningBeam) || !TryComp(lightningBeam.VirtualBeamController, out var beamController)) - { return; - } + beamController.CreatedBeams.Remove(uid); } @@ -74,29 +78,42 @@ public void ShootRandomLightnings(EntityUid user, float range, int boltCount, st //To Do: This is still pretty bad for perf but better than before and at least it doesn't re-allocate // several hashsets every time - var targets = _lookup.GetComponentsInRange(Transform(user).MapPosition, range).ToList(); - _random.Shuffle(targets); - targets.Sort((x, y) => y.Priority.CompareTo(x.Priority)); + var userCoordinates = _transformSystem.GetMapCoordinates(user); + var targetEnts = _lookup.GetEntitiesInRange(userCoordinates, range); + + foreach (var entity in targetEnts) + { + var hasComp = TryComp(entity, out LightningTargetComponent? component); + + if (hasComp) + _targets.Add(component!); + } + + _random.Shuffle(_targets); + _targets.Sort((x, y) => y.Priority.CompareTo(x.Priority)); int shootedCount = 0; int count = -1; - while(shootedCount < boltCount) + + while (shootedCount < boltCount) { count++; - if (count >= targets.Count) { break; } + if (count >= _targets.Count) { break; } - var curTarget = targets[count]; + var curTarget = _targets[count]; if (!_random.Prob(curTarget.HitProbability)) //Chance to ignore target continue; - ShootLightning(user, targets[count].Owner, lightningPrototype, triggerLightningEvents); - if (arcDepth - targets[count].LightningResistance > 0) + ShootLightning(user, _targets[count].Owner, lightningPrototype, triggerLightningEvents); + if (arcDepth - _targets[count].LightningResistance > 0) { - ShootRandomLightnings(targets[count].Owner, range, 1, lightningPrototype, arcDepth - targets[count].LightningResistance, triggerLightningEvents); + ShootRandomLightnings(_targets[count].Owner, range, 1, lightningPrototype, arcDepth - _targets[count].LightningResistance, triggerLightningEvents); } shootedCount++; } + + _targets.Clear(); } } diff --git a/Content.Server/Materials/MaterialReclaimerSystem.cs b/Content.Server/Materials/MaterialReclaimerSystem.cs index de82f125985..3b23308758d 100644 --- a/Content.Server/Materials/MaterialReclaimerSystem.cs +++ b/Content.Server/Materials/MaterialReclaimerSystem.cs @@ -146,7 +146,7 @@ public override bool TryFinishProcessItem(EntityUid uid, MaterialReclaimerCompon return false; Container.Remove(item, active.ReclaimingContainer); - Dirty(component); + Dirty(uid, component); // scales the output if the process was interrupted. var completion = 1f - Math.Clamp((float) Math.Round((active.EndTime - Timing.CurTime) / active.Duration), diff --git a/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs b/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs index fa46792d2af..0382f12d809 100644 --- a/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs +++ b/Content.Server/Mech/Equipment/EntitySystems/MechGrabberSystem.cs @@ -85,7 +85,7 @@ public void RemoveItem(EntityUid uid, EntityUid mech, EntityUid toRemove, MechGr var (mechPos, mechRot) = _transform.GetWorldPositionRotation(mechxform); var offset = mechPos + mechRot.RotateVec(component.DepositOffset); - _transform.SetWorldPositionRotation(xform, offset, Angle.Zero); + _transform.SetWorldPositionRotation(toRemove, offset, Angle.Zero); _mech.UpdateUserInterface(mech); } diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs index 36dce2c9bcc..a728ee7de5e 100644 --- a/Content.Server/Mech/Systems/MechSystem.cs +++ b/Content.Server/Mech/Systems/MechSystem.cs @@ -107,7 +107,7 @@ private void OnInsertBattery(EntityUid uid, MechComponent component, EntInserted component.Energy = battery.CurrentCharge; component.MaxEnergy = battery.MaxCharge; - Dirty(component); + Dirty(uid, component); _actionBlocker.UpdateCanMove(uid); } @@ -137,7 +137,7 @@ private void OnMapInit(EntityUid uid, MechComponent component, MapInitEvent args component.Energy = component.MaxEnergy; _actionBlocker.UpdateCanMove(uid); - Dirty(component); + Dirty(uid, component); } private void OnRemoveEquipmentMessage(EntityUid uid, MechComponent component, MechEquipmentRemoveMessage args) @@ -337,7 +337,7 @@ public override bool TryChangeEnergy(EntityUid uid, FixedPoint2 delta, MechCompo { Log.Debug($"Battery charge was not equal to mech charge. Battery {batteryComp.CurrentCharge}. Mech {component.Energy}"); component.Energy = batteryComp.CurrentCharge; - Dirty(component); + Dirty(uid, component); } _actionBlocker.UpdateCanMove(uid); return true; @@ -357,7 +357,7 @@ public void InsertBattery(EntityUid uid, EntityUid toInsert, MechComponent? comp _actionBlocker.UpdateCanMove(uid); - Dirty(component); + Dirty(uid, component); UpdateUserInterface(uid, component); } @@ -372,7 +372,7 @@ public void RemoveBattery(EntityUid uid, MechComponent? component = null) _actionBlocker.UpdateCanMove(uid); - Dirty(component); + Dirty(uid, component); UpdateUserInterface(uid, component); } diff --git a/Content.Server/Mood/MoodComponent.cs b/Content.Server/Mood/MoodComponent.cs index 7fd4a7136f3..caa221fe18e 100644 --- a/Content.Server/Mood/MoodComponent.cs +++ b/Content.Server/Mood/MoodComponent.cs @@ -1,5 +1,6 @@ using Content.Shared.Alert; using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; namespace Content.Server.Mood; @@ -50,6 +51,9 @@ public sealed partial class MoodComponent : Component [ViewVariables(VVAccess.ReadOnly)] public FixedPoint2 CritThresholdBeforeModify; + [DataField] + public ProtoId MoodCategory = "Mood"; + [DataField(customTypeSerializer: typeof(DictionarySerializer))] public Dictionary MoodThresholds = new() { @@ -65,20 +69,20 @@ public sealed partial class MoodComponent : Component { MoodThreshold.Dead, 0f } }; - [DataField(customTypeSerializer: typeof(DictionarySerializer))] - public Dictionary MoodThresholdsAlerts = new() + [DataField(customTypeSerializer: typeof(DictionarySerializer>))] + public Dictionary> MoodThresholdsAlerts = new() { - { MoodThreshold.Dead, AlertType.MoodDead }, - { MoodThreshold.Horrible, AlertType.Horrible }, - { MoodThreshold.Terrible, AlertType.Terrible }, - { MoodThreshold.Bad, AlertType.Bad }, - { MoodThreshold.Meh, AlertType.Meh }, - { MoodThreshold.Neutral, AlertType.Neutral }, - { MoodThreshold.Good, AlertType.Good }, - { MoodThreshold.Great, AlertType.Great }, - { MoodThreshold.Exceptional, AlertType.Exceptional }, - { MoodThreshold.Perfect, AlertType.Perfect }, - { MoodThreshold.Insane, AlertType.Insane } + { MoodThreshold.Dead, "MoodDead" }, + { MoodThreshold.Horrible, "Horrible" }, + { MoodThreshold.Terrible, "Terrible" }, + { MoodThreshold.Bad, "Bad" }, + { MoodThreshold.Meh, "Meh" }, + { MoodThreshold.Neutral, "Neutral" }, + { MoodThreshold.Good, "Good" }, + { MoodThreshold.Great, "Great" }, + { MoodThreshold.Exceptional, "Exceptional" }, + { MoodThreshold.Perfect, "Perfect" }, + { MoodThreshold.Insane, "Insane" } }; /// diff --git a/Content.Server/Mood/MoodSystem.cs b/Content.Server/Mood/MoodSystem.cs index 4ec4709ea7a..41baf9f5479 100644 --- a/Content.Server/Mood/MoodSystem.cs +++ b/Content.Server/Mood/MoodSystem.cs @@ -57,7 +57,7 @@ public override void Initialize() private void OnShutdown(EntityUid uid, MoodComponent component, ComponentShutdown args) { - _alerts.ClearAlertCategory(uid, AlertCategory.Mood); + _alerts.ClearAlertCategory(uid, component.MoodCategory); } private void OnRemoveEffect(EntityUid uid, MoodComponent component, MoodRemoveEffectEvent args) @@ -331,7 +331,7 @@ private void DoMoodThresholdsEffects(EntityUid uid, MoodComponent? component = n if (component.MoodThresholdsAlerts.TryGetValue(component.CurrentMoodThreshold, out var alertId)) _alerts.ShowAlert(uid, alertId); else - _alerts.ClearAlertCategory(uid, AlertCategory.Mood); + _alerts.ClearAlertCategory(uid, component.MoodCategory); component.LastThreshold = component.CurrentMoodThreshold; } diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/UnbuckleOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/UnbuckleOperator.cs index 207665d786f..116e8fe7c7f 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/UnbuckleOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/Combat/UnbuckleOperator.cs @@ -1,11 +1,9 @@ using Content.Server.Buckle.Systems; -using Content.Shared.Buckle.Components; namespace Content.Server.NPC.HTN.PrimitiveTasks.Operators.Combat; public sealed partial class UnbuckleOperator : HTNOperator { - [Dependency] private readonly IEntityManager _entManager = default!; private BuckleSystem _buckle = default!; [DataField("shutdownState")] @@ -21,10 +19,7 @@ public override void Startup(NPCBlackboard blackboard) { base.Startup(blackboard); var owner = blackboard.GetValue(NPCBlackboard.Owner); - if (!_entManager.TryGetComponent(owner, out var buckle) || !buckle.Buckled) - return; - - _buckle.TryUnbuckle(owner, owner, true, buckle); + _buckle.Unbuckle(owner, null); } public override HTNOperatorStatus Update(NPCBlackboard blackboard, float frameTime) diff --git a/Content.Server/NPC/Systems/NPCJukeSystem.cs b/Content.Server/NPC/Systems/NPCJukeSystem.cs index 5a724762ef6..9aceef289e9 100644 --- a/Content.Server/NPC/Systems/NPCJukeSystem.cs +++ b/Content.Server/NPC/Systems/NPCJukeSystem.cs @@ -22,6 +22,7 @@ public sealed class NPCJukeSystem : EntitySystem [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly MeleeWeaponSystem _melee = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedMapSystem _sharedMapSystem = default!; private EntityQuery _npcMeleeQuery; private EntityQuery _npcRangedQuery; @@ -113,8 +114,8 @@ private void OnJukeSteering(EntityUid uid, NPCJukeComponent component, ref NPCSt return; } - var targetCoords = grid.GridTileToWorld(component.TargetTile.Value); - var targetDir = (targetCoords.Position - args.WorldPosition); + var targetCoords = _sharedMapSystem.GridTileToWorld(uid, grid, component.TargetTile.Value); + var targetDir = targetCoords.Position - args.WorldPosition; targetDir = args.OffsetRotation.RotateVec(targetDir); const float weight = 1f; var norm = targetDir.Normalized(); diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs index 3bc4eae9e49..24b7f5b6e2e 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.Obstacles.cs @@ -36,6 +36,7 @@ public sealed partial class NPCSteeringSystem * Also need to make sure it picks nearest obstacle path so it starts smashing in front of it. */ + [Dependency] private readonly SharedMapSystem _sharedMapSystem = default!; private SteeringObstacleStatus TryHandleFlags(EntityUid uid, NPCSteeringComponent component, PathPoly poly) { @@ -207,7 +208,7 @@ private void GetObstacleEntities(PathPoly poly, int mask, int layer, List ids) /// public void SetSuitPowerAlert(EntityUid uid, SpaceNinjaComponent? comp = null) { - if (!Resolve(uid, ref comp, false) || comp.Deleted || comp.Suit == null) + if (!Resolve(uid, ref comp, false)) + return; + + if (comp.Deleted || comp.Suit == null) { - _alerts.ClearAlert(uid, AlertType.SuitPower); + _alerts.ClearAlert(uid, comp.SuitPowerAlert); return; } if (GetNinjaBattery(uid, out _, out var battery)) { var severity = ContentHelpers.RoundToLevels(MathF.Max(0f, battery.CurrentCharge), battery.MaxCharge, 8); - _alerts.ShowAlert(uid, AlertType.SuitPower, (short) severity); + _alerts.ShowAlert(uid, comp.SuitPowerAlert, (short) severity); } else { - _alerts.ClearAlert(uid, AlertType.SuitPower); + _alerts.ClearAlert(uid, comp.SuitPowerAlert); } } diff --git a/Content.Server/Nyanotrasen/Construction/Commands/TileWindowsCommand.cs b/Content.Server/Nyanotrasen/Construction/Commands/TileWindowsCommand.cs index 9eef7292eaa..387d031bdeb 100644 --- a/Content.Server/Nyanotrasen/Construction/Commands/TileWindowsCommand.cs +++ b/Content.Server/Nyanotrasen/Construction/Commands/TileWindowsCommand.cs @@ -2,9 +2,11 @@ using Content.Shared.Administration; using Content.Shared.Maps; using Content.Shared.Tag; +using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Console; using Robust.Shared.Map; +using Robust.Shared.Map.Components; using Robust.Shared.Maths; using Robust.Shared.Player; @@ -53,8 +55,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - var mapManager = IoCManager.Resolve(); - if (!mapManager.TryGetGrid(gridId, out var grid)) + if (!entityManager.TryGetComponent(gridId, out var grid)) { shell.WriteLine($"No grid exists with id {gridId}"); return; @@ -68,10 +69,14 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) var tileDefinitionManager = IoCManager.Resolve(); var tagSystem = entityManager.EntitySysManager.GetEntitySystem(); + var entityLookupSystem = IoCManager.Resolve(); var underplating = tileDefinitionManager[TilePrototypeId]; var underplatingTile = new Tile(underplating.TileId); + var childEntities = new HashSet>(); + entityLookupSystem.GetChildEntities(grid.Owner, childEntities); + var changed = 0; - foreach (var child in entityManager.GetComponent(grid.Owner).ChildEntities) + foreach (var child in childEntities) { if (!entityManager.EntityExists(child)) { diff --git a/Content.Server/Nyanotrasen/Players/PlayTimeTracking/PlayTimeTrackingManager.Whitelist.cs b/Content.Server/Nyanotrasen/Players/PlayTimeTracking/PlayTimeTrackingManager.Whitelist.cs index ccbe8d8e7fe..53b0889aa64 100644 --- a/Content.Server/Nyanotrasen/Players/PlayTimeTracking/PlayTimeTrackingManager.Whitelist.cs +++ b/Content.Server/Nyanotrasen/Players/PlayTimeTracking/PlayTimeTrackingManager.Whitelist.cs @@ -15,7 +15,7 @@ private void SendWhitelistCached(ICommonSession playerSession) Whitelisted = whitelist }; - _net.ServerSendMessage(msg, playerSession.ConnectedClient); + _net.ServerSendMessage(msg, playerSession.Channel); } /// diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs index 94a488bd84b..c1bce269ff9 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/MidRoundAntagRule.cs @@ -1,4 +1,4 @@ -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Random; diff --git a/Content.Server/Objectives/ObjectivesSystem.cs b/Content.Server/Objectives/ObjectivesSystem.cs index 47fe4eb5f88..bf013bc0402 100644 --- a/Content.Server/Objectives/ObjectivesSystem.cs +++ b/Content.Server/Objectives/ObjectivesSystem.cs @@ -1,6 +1,7 @@ using Content.Server.GameTicking; using Content.Server.Shuttles.Systems; using Content.Shared.Cuffs.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Mind; using Content.Shared.Objectives.Components; using Content.Shared.Objectives.Systems; @@ -9,7 +10,6 @@ using Robust.Shared.Prototypes; using Robust.Shared.Random; using System.Linq; -using Content.Server.GameTicking.Components; using System.Text; using Robust.Server.Player; diff --git a/Content.Server/OfferItem/OfferItemSystem.cs b/Content.Server/OfferItem/OfferItemSystem.cs index 420df71ace7..e2eb65822e0 100644 --- a/Content.Server/OfferItem/OfferItemSystem.cs +++ b/Content.Server/OfferItem/OfferItemSystem.cs @@ -39,11 +39,11 @@ public override void Update(float frameTime) if (!offerItem.IsInReceiveMode) { - _alertsSystem.ClearAlert(uid, AlertType.Offer); + _alertsSystem.ClearAlert(uid, offerItem.OfferAlert); continue; } - _alertsSystem.ShowAlert(uid, AlertType.Offer); + _alertsSystem.ShowAlert(uid, offerItem.OfferAlert); } } diff --git a/Content.Server/Physics/Controllers/ConveyorController.cs b/Content.Server/Physics/Controllers/ConveyorController.cs index 42279bb7496..b3508025cb9 100644 --- a/Content.Server/Physics/Controllers/ConveyorController.cs +++ b/Content.Server/Physics/Controllers/ConveyorController.cs @@ -67,7 +67,7 @@ private void OnPowerChanged(EntityUid uid, ConveyorComponent component, ref Powe { component.Powered = args.Powered; UpdateAppearance(uid, component); - Dirty(component); + Dirty(uid, component); } private void UpdateAppearance(EntityUid uid, ConveyorComponent component) @@ -106,7 +106,7 @@ private void SetState(EntityUid uid, ConveyorState state, ConveyorComponent? com _materialReclaimer.SetReclaimerEnabled(uid, component.State != ConveyorState.Off); UpdateAppearance(uid, component); - Dirty(component); + Dirty(uid, component); } /// diff --git a/Content.Server/Pinpointer/NavMapSystem.cs b/Content.Server/Pinpointer/NavMapSystem.cs index 5881daa0689..7ed13b7b940 100644 --- a/Content.Server/Pinpointer/NavMapSystem.cs +++ b/Content.Server/Pinpointer/NavMapSystem.cs @@ -68,7 +68,7 @@ public override void Initialize() private void OnStationInit(StationGridAddedEvent ev) { var comp = EnsureComp(ev.GridId); - RefreshGrid(comp, Comp(ev.GridId)); + RefreshGrid(ev.GridId, comp, Comp(ev.GridId)); } #region: Grid change event handling @@ -81,10 +81,10 @@ private void OnNavMapSplit(ref GridSplitEvent args) foreach (var grid in args.NewGrids) { var newComp = EnsureComp(grid); - RefreshGrid(comp, newComp); + RefreshGrid(args.Grid, comp, newComp); } - RefreshGrid(comp, _gridQuery.GetComponent(args.Grid)); + RefreshGrid(args.Grid, comp, _gridQuery.GetComponent(args.Grid)); } private NavMapChunk EnsureChunk(NavMapComponent component, Vector2i origin) @@ -231,14 +231,14 @@ private void OnConfigurableExamined(Entity en #region: Grid functions - private void RefreshGrid(NavMapComponent component, MapGridComponent mapGrid) + private void RefreshGrid(EntityUid uid, NavMapComponent component, MapGridComponent mapGrid) { // Clear stale data component.Chunks.Clear(); component.Beacons.Clear(); // Loop over all tiles - var tileRefs = _mapSystem.GetAllTiles(mapGrid.Owner, mapGrid); + var tileRefs = _mapSystem.GetAllTiles(uid, mapGrid); foreach (var tileRef in tileRefs) { @@ -247,10 +247,10 @@ private void RefreshGrid(NavMapComponent component, MapGridComponent mapGrid) var chunk = EnsureChunk(component, chunkOrigin); chunk.LastUpdate = _gameTiming.CurTick; - RefreshTileEntityContents(mapGrid.Owner, component, mapGrid, chunkOrigin, tile, setFloor: true); + RefreshTileEntityContents(uid, component, mapGrid, chunkOrigin, tile, setFloor: true); } - Dirty(mapGrid.Owner, component); + Dirty(uid, component); } private (int NewVal, NavMapChunk Chunk) RefreshTileEntityContents(EntityUid uid, diff --git a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs index 42c84b7f43b..35b17dc9584 100644 --- a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Power.NodeGroups; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Pinpointer; using Content.Shared.Power; using JetBrains.Annotations; @@ -13,7 +14,6 @@ using Robust.Shared.Map.Components; using Robust.Shared.Utility; using System.Linq; -using Content.Server.GameTicking.Components; namespace Content.Server.Power.EntitySystems; diff --git a/Content.Server/PowerCell/PowerCellSystem.Draw.cs b/Content.Server/PowerCell/PowerCellSystem.Draw.cs index 8e960357b7a..4155a4f6bec 100644 --- a/Content.Server/PowerCell/PowerCellSystem.Draw.cs +++ b/Content.Server/PowerCell/PowerCellSystem.Draw.cs @@ -67,7 +67,7 @@ private void OnDrawChargeChanged(EntityUid uid, PowerCellDrawComponent component { component.CanDraw = canDraw; component.CanUse = canUse; - Dirty(component); + Dirty(uid, component); } } @@ -80,7 +80,7 @@ private void OnDrawCellChanged(EntityUid uid, PowerCellDrawComponent component, { component.CanDraw = canDraw; component.CanUse = canUse; - Dirty(component); + Dirty(uid, component); } } } diff --git a/Content.Server/Psionics/Dreams/DreamSystem.cs b/Content.Server/Psionics/Dreams/DreamSystem.cs index d6067717c94..0729f5c59dd 100644 --- a/Content.Server/Psionics/Dreams/DreamSystem.cs +++ b/Content.Server/Psionics/Dreams/DreamSystem.cs @@ -51,7 +51,7 @@ public override void Update(float frameTime) ("telepathicChannelName", Loc.GetString("chat-manager-telepathic-channel-name")), ("message", msg)); _chatManager.ChatMessageToOne(Shared.Chat.ChatChannel.Telepathic, - msg, messageWrap, sleeper.Owner, false, actor.PlayerSession.ConnectedClient, Color.PaleVioletRed); + msg, messageWrap, sleeper.Owner, false, actor.PlayerSession.Channel, Color.PaleVioletRed); } } } diff --git a/Content.Server/Research/Systems/ResearchSystem.Server.cs b/Content.Server/Research/Systems/ResearchSystem.Server.cs index 2a802a91a32..09ca7ed15c2 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Server.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Server.cs @@ -18,7 +18,7 @@ private void OnServerStartup(EntityUid uid, ResearchServerComponent component, C var unusedId = EntityQuery(true) .Max(s => s.Id) + 1; component.Id = unusedId; - Dirty(component); + Dirty(uid, component); } private void OnServerShutdown(EntityUid uid, ResearchServerComponent component, ComponentShutdown args) @@ -74,7 +74,7 @@ public void RegisterClient(EntityUid client, EntityUid server, ResearchClientCom SyncClientWithServer(client, clientComponent: clientComponent); if (dirtyServer) - Dirty(serverComponent); + Dirty(server, serverComponent); var ev = new ResearchRegistrationChangedEvent(server); RaiseLocalEvent(client, ref ev); @@ -117,7 +117,7 @@ public void UnregisterClient(EntityUid client, EntityUid server, ResearchClientC if (dirtyServer) { - Dirty(serverComponent); + Dirty(server, serverComponent); } var ev = new ResearchRegistrationChangedEvent(null); @@ -167,6 +167,6 @@ public void ModifyServerPoints(EntityUid uid, int points, ResearchServerComponen { RaiseLocalEvent(client, ref ev); } - Dirty(component); + Dirty(uid, component); } } diff --git a/Content.Server/Research/Systems/ResearchSystem.Technology.cs b/Content.Server/Research/Systems/ResearchSystem.Technology.cs index 107d51ccd8c..9bd71cf7c6e 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Technology.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Technology.cs @@ -21,7 +21,7 @@ public void Sync(EntityUid primaryUid, EntityUid otherUid, TechnologyDatabaseCom primaryDb.UnlockedTechnologies = otherDb.UnlockedTechnologies; primaryDb.UnlockedRecipes = otherDb.UnlockedRecipes; - Dirty(primaryDb); + Dirty(primaryUid, primaryDb); var ev = new TechnologyDatabaseModifiedEvent(); RaiseLocalEvent(primaryUid, ref ev); @@ -125,7 +125,7 @@ public void AddTechnology(EntityUid uid, TechnologyPrototype technology, Technol continue; component.UnlockedRecipes.Add(unlock); } - Dirty(component); + Dirty(uid, component); var ev = new TechnologyDatabaseModifiedEvent(); RaiseLocalEvent(uid, ref ev); @@ -144,7 +144,7 @@ public void AddLatheRecipe(EntityUid uid, string recipe, TechnologyDatabaseCompo return; component.UnlockedRecipes.Add(recipe); - Dirty(component); + Dirty(uid, component); var ev = new TechnologyDatabaseModifiedEvent(); RaiseLocalEvent(uid, ref ev); @@ -185,6 +185,6 @@ private void OnDatabaseRegistrationChanged(EntityUid uid, TechnologyDatabaseComp component.SupportedDisciplines = new List(); component.UnlockedTechnologies = new List(); component.UnlockedRecipes = new List(); - Dirty(component); + Dirty(uid, component); } } diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs index 428d1ecb75e..595de100e86 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.cs @@ -141,7 +141,7 @@ public bool ChangeEssenceAmount(EntityUid uid, FixedPoint2 amount, RevenantCompo if (TryComp(uid, out var store)) _store.UpdateUserInterface(uid, uid, store); - _alerts.ShowAlert(uid, AlertType.Essence); + _alerts.ShowAlert(uid, component.EssenceAlert); if (component.Essence <= 0) { diff --git a/Content.Server/Shadowkin/ShadowkinSystem.cs b/Content.Server/Shadowkin/ShadowkinSystem.cs index 83461e7a7fe..96bd09db276 100644 --- a/Content.Server/Shadowkin/ShadowkinSystem.cs +++ b/Content.Server/Shadowkin/ShadowkinSystem.cs @@ -55,7 +55,7 @@ private void OnEyeColorChange(EntityUid uid, ShadowkinComponent component, EyeCo component.OldEyeColor = humanoid.EyeColor; humanoid.EyeColor = component.BlackEyeColor; - Dirty(humanoid); + Dirty(uid, humanoid); } private void OnExamined(EntityUid uid, ShadowkinComponent component, ExaminedEvent args) @@ -89,10 +89,10 @@ public void UpdateShadowkinAlert(EntityUid uid, ShadowkinComponent component) if (TryComp(uid, out var magic)) { var severity = (short) ContentHelpers.RoundToLevels(magic.Mana, magic.MaxMana, 8); - _alerts.ShowAlert(uid, AlertType.ShadowkinPower, severity); + _alerts.ShowAlert(uid, component.ShadowkinPowerAlert, severity); } else - _alerts.ClearAlert(uid, AlertType.ShadowkinPower); + _alerts.ClearAlert(uid, component.ShadowkinPowerAlert); } private void OnAttemptPowerUse(EntityUid uid, ShadowkinComponent component, OnAttemptPowerUseEvent args) @@ -115,7 +115,7 @@ private void OnManaUpdate(EntityUid uid, ShadowkinComponent component, ref OnMan if (magic.Mana <= component.BlackEyeMana) ApplyBlackEye(uid); - Dirty(magic); // Update Shadowkin Overlay. + Dirty(uid, magic); // Update Shadowkin Overlay. UpdateShadowkinAlert(uid, component); } @@ -141,7 +141,7 @@ private void OnMindbreak(EntityUid uid, ShadowkinComponent component, ref OnMind { component.OldEyeColor = humanoid.EyeColor; humanoid.EyeColor = component.BlackEyeColor; - Dirty(humanoid); + Dirty(uid, humanoid); } if (component.BlackeyeSpawn) @@ -162,7 +162,7 @@ private void OnRejuvenate(EntityUid uid, ShadowkinComponent component, Rejuvenat if (TryComp(uid, out var humanoid)) { humanoid.EyeColor = component.OldEyeColor; - Dirty(humanoid); + Dirty(uid, humanoid); } EnsureComp(uid, out var magic); diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs index 6f24208c3a6..5550201202f 100644 --- a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs @@ -317,12 +317,12 @@ public void AddPilot(EntityUid uid, EntityUid entity, ShuttleConsoleComponent co component.SubscribedPilots.Add(entity); - _alertsSystem.ShowAlert(entity, AlertType.PilotingShuttle); + _alertsSystem.ShowAlert(entity, pilotComponent.PilotingAlert); pilotComponent.Console = uid; ActionBlockerSystem.UpdateCanMove(entity); pilotComponent.Position = EntityManager.GetComponent(entity).Coordinates; - Dirty(pilotComponent); + Dirty(entity, pilotComponent); } public void RemovePilot(EntityUid pilotUid, PilotComponent pilotComponent) @@ -339,7 +339,7 @@ public void RemovePilot(EntityUid pilotUid, PilotComponent pilotComponent) if (!helm.SubscribedPilots.Remove(pilotUid)) return; - _alertsSystem.ClearAlert(pilotUid, AlertType.PilotingShuttle); + _alertsSystem.ClearAlert(pilotUid, pilotComponent.PilotingAlert); _popup.PopupEntity(Loc.GetString("shuttle-pilot-end"), pilotUid, pilotUid); diff --git a/Content.Server/Silicon/Charge/Systems/SiliconChargeSystem.cs b/Content.Server/Silicon/Charge/Systems/SiliconChargeSystem.cs index d8b034a69f5..444b65530b2 100644 --- a/Content.Server/Silicon/Charge/Systems/SiliconChargeSystem.cs +++ b/Content.Server/Silicon/Charge/Systems/SiliconChargeSystem.cs @@ -91,10 +91,10 @@ public override void Update(float frameTime) if (!TryGetSiliconBattery(silicon, out var batteryComp)) { UpdateChargeState(silicon, 0, siliconComp); - if (_alerts.IsShowingAlert(silicon, AlertType.BorgBattery)) + if (_alerts.IsShowingAlert(silicon, siliconComp.BatteryAlert)) { - _alerts.ClearAlert(silicon, AlertType.BorgBattery); - _alerts.ShowAlert(silicon, AlertType.BorgBatteryNone); + _alerts.ClearAlert(silicon, siliconComp.BatteryAlert); + _alerts.ShowAlert(silicon, siliconComp.NoBatteryAlert); } continue; } @@ -142,10 +142,10 @@ public void UpdateChargeState(EntityUid uid, short chargePercent, SiliconCompone _moveMod.RefreshMovementSpeedModifiers(uid); // If the battery was replaced and the no battery indicator is showing, replace the indicator - if (_alerts.IsShowingAlert(uid, AlertType.BorgBatteryNone) && chargePercent != 0) + if (_alerts.IsShowingAlert(uid, component.NoBatteryAlert) && chargePercent != 0) { - _alerts.ClearAlert(uid, AlertType.BorgBatteryNone); - _alerts.ShowAlert(uid, AlertType.BorgBattery, chargePercent); + _alerts.ClearAlert(uid, component.NoBatteryAlert); + _alerts.ShowAlert(uid, component.BatteryAlert, chargePercent); } } diff --git a/Content.Server/Silicons/Borgs/BorgSystem.cs b/Content.Server/Silicons/Borgs/BorgSystem.cs index 75f25a3a922..97adfd00eb4 100644 --- a/Content.Server/Silicons/Borgs/BorgSystem.cs +++ b/Content.Server/Silicons/Borgs/BorgSystem.cs @@ -84,7 +84,7 @@ public override void Initialize() private void OnMapInit(EntityUid uid, BorgChassisComponent component, MapInitEvent args) { - UpdateBatteryAlert(uid); + UpdateBatteryAlert((uid, component)); _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); } @@ -183,7 +183,7 @@ private void OnMobStateChanged(EntityUid uid, BorgChassisComponent component, Mo private void OnPowerCellChanged(EntityUid uid, BorgChassisComponent component, PowerCellChangedEvent args) { - UpdateBatteryAlert(uid); + UpdateBatteryAlert((uid, component)); if (!TryComp(uid, out var draw)) return; @@ -256,12 +256,12 @@ private void OnBrainPointAttempt(EntityUid uid, BorgBrainComponent component, Po args.Cancel(); } - private void UpdateBatteryAlert(EntityUid uid, PowerCellSlotComponent? slotComponent = null) + private void UpdateBatteryAlert(Entity ent, PowerCellSlotComponent? slotComponent = null) { - if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery, slotComponent)) + if (!_powerCell.TryGetBatteryFromSlot(ent, out var battery, slotComponent)) { - _alerts.ClearAlert(uid, AlertType.BorgBattery); - _alerts.ShowAlert(uid, AlertType.BorgBatteryNone); + _alerts.ClearAlert(ent, ent.Comp.BatteryAlert); + _alerts.ShowAlert(ent, ent.Comp.NoBatteryAlert); return; } @@ -269,13 +269,13 @@ private void UpdateBatteryAlert(EntityUid uid, PowerCellSlotComponent? slotCompo // we make sure 0 only shows if they have absolutely no battery. // also account for floating point imprecision - if (chargePercent == 0 && _powerCell.HasDrawCharge(uid, cell: slotComponent)) + if (chargePercent == 0 && _powerCell.HasDrawCharge(ent, cell: slotComponent)) { chargePercent = 1; } - _alerts.ClearAlert(uid, AlertType.BorgBatteryNone); - _alerts.ShowAlert(uid, AlertType.BorgBattery, chargePercent); + _alerts.ClearAlert(ent, ent.Comp.NoBatteryAlert); + _alerts.ShowAlert(ent, ent.Comp.BatteryAlert, chargePercent); } /// @@ -288,7 +288,7 @@ public void EnableBorgAbilities(EntityUid uid, BorgChassisComponent component, P component.Activated = true; InstallAllModules(uid, component); - Dirty(component); + Dirty(uid, component); _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); } @@ -302,7 +302,7 @@ public void DisableBorgAbilities(EntityUid uid, BorgChassisComponent component) component.Activated = false; DisableAllModules(uid, component); - Dirty(component); + Dirty(uid, component); _movementSpeedModifier.RefreshMovementSpeedModifiers(uid); } diff --git a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs index 75f86187989..f57481b05b6 100644 --- a/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs +++ b/Content.Server/Spawners/EntitySystems/ConditionalSpawnerSystem.cs @@ -1,8 +1,8 @@ using System.Numerics; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Spawners.Components; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; using Robust.Shared.Random; diff --git a/Content.Server/Sprite/RandomSpriteSystem.cs b/Content.Server/Sprite/RandomSpriteSystem.cs index 5d04dd2f5a6..7f81f4bdd45 100644 --- a/Content.Server/Sprite/RandomSpriteSystem.cs +++ b/Content.Server/Sprite/RandomSpriteSystem.cs @@ -63,7 +63,7 @@ private void OnMapInit(EntityUid uid, RandomSpriteComponent component, MapInitEv } } - Dirty(component); + Dirty(uid, component); } private void OnGetState(EntityUid uid, RandomSpriteComponent component, ref ComponentGetState args) diff --git a/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs b/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs index b9eb3b7b09d..aab0c2ca7a8 100644 --- a/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/BasicStationEventSchedulerSystem.cs @@ -1,11 +1,12 @@ using System.Linq; using Content.Server.Administration; -using Content.Server.GameTicking.Components; +using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Shared.Administration; using Content.Shared.CCVar; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; using Robust.Shared.Configuration; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/Components/StationEventComponent.cs b/Content.Server/StationEvents/Components/StationEventComponent.cs index 54af1a59d99..fdd1d3962e7 100644 --- a/Content.Server/StationEvents/Components/StationEventComponent.cs +++ b/Content.Server/StationEvents/Components/StationEventComponent.cs @@ -19,10 +19,22 @@ public sealed partial class StationEventComponent : Component public float Weight = WeightNormal; [DataField] - public bool StartAnnouncement; + public string? StartAnnouncement; [DataField] - public bool EndAnnouncement; + public string? EndAnnouncement; + + [DataField] + public Color StartAnnouncementColor = Color.Gold; + + [DataField] + public Color EndAnnouncementColor = Color.Gold; + + [DataField] + public SoundSpecifier? StartAudio; + + [DataField] + public SoundSpecifier? EndAudio; /// /// In minutes, when is the first round time this event can start @@ -36,12 +48,6 @@ public sealed partial class StationEventComponent : Component [DataField] public int ReoccurrenceDelay = 30; - /// - /// How long after being added does the event start - /// - [DataField] - public TimeSpan StartDelay = TimeSpan.Zero; - /// /// How long the event lasts. /// @@ -69,13 +75,6 @@ public sealed partial class StationEventComponent : Component [DataField] public int? MaxOccurrences; - /// - /// When the station event starts. - /// - [DataField("startTime", customTypeSerializer: typeof(TimeOffsetSerializer))] - [AutoPausedField] - public TimeSpan StartTime; - /// /// When the station event ends. /// diff --git a/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs b/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs index a78a542d3b3..916d7d16883 100644 --- a/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs +++ b/Content.Server/StationEvents/Events/AlertLevelInterceptionRule.cs @@ -1,6 +1,6 @@ -using Content.Server.GameTicking.Components; using Content.Server.StationEvents.Components; using Content.Server.AlertLevel; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; @@ -20,4 +20,4 @@ protected override void Started(EntityUid uid, AlertLevelInterceptionRuleCompone _alertLevelSystem.SetLevel(chosenStation.Value, component.AlertLevel, true, true, true); } -} \ No newline at end of file +} diff --git a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs index 98d5aa76a6a..4ec2a40c4ce 100644 --- a/Content.Server/StationEvents/Events/AnomalySpawnRule.cs +++ b/Content.Server/StationEvents/Events/AnomalySpawnRule.cs @@ -1,9 +1,8 @@ -using Content.Server.Anomaly; -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Server.Announcements.Systems; +using Content.Server.Anomaly; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; -using Content.Server.Announcements.Systems; +using Content.Shared.GameTicking.Components; using Robust.Shared.Player; namespace Content.Server.StationEvents.Events; @@ -15,17 +14,14 @@ public sealed class AnomalySpawnRule : StationEventSystem(uid, out var stationEvent)) + return; + + var str = Loc.GetString("anomaly-spawn-event-announcement", + ("sighting", Loc.GetString($"anomaly-spawn-sighting-{RobustRandom.Next(1, 6)}"))); + stationEvent.StartAnnouncement = str; - _announcer.SendAnnouncement( - _announcer.GetAnnouncementId(args.RuleId), - Filter.Broadcast(), - "anomaly-spawn-event-announcement", - null, - Color.FromHex("#18abf5"), - null, null, - ("sighting", Loc.GetString($"anomaly-spawn-sighting-{RobustRandom.Next(1, 6)}")) - ); + base.Added(uid, component, gameRule, args); } protected override void Started(EntityUid uid, AnomalySpawnRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs index 29c18976576..e4ce0a24b9c 100644 --- a/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs +++ b/Content.Server/StationEvents/Events/BluespaceArtifactRule.cs @@ -1,5 +1,4 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Content.Server.StationEvents.Components; using Robust.Shared.Random; using Content.Server.Announcements.Systems; @@ -13,17 +12,14 @@ public sealed class BluespaceArtifactRule : StationEventSystem(uid, out var stationEvent)) + return; + + var str = Loc.GetString("bluespace-artifact-event-announcement", + ("sighting", Loc.GetString(RobustRandom.Pick(component.PossibleSighting)))); + stationEvent.StartAnnouncement = str; - _announcer.SendAnnouncement( - _announcer.GetAnnouncementId(args.RuleId), - Filter.Broadcast(), - "bluespace-artifact-event-announcement", - null, - Color.FromHex("#18abf5"), - null, null, - ("sighting", Loc.GetString(RobustRandom.Pick(component.PossibleSighting))) - ); + base.Added(uid, component, gameRule, args); } protected override void Started(EntityUid uid, BluespaceArtifactRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/StationEvents/Events/BluespaceLockerRule.cs b/Content.Server/StationEvents/Events/BluespaceLockerRule.cs index eef9850e739..b19485bc31e 100644 --- a/Content.Server/StationEvents/Events/BluespaceLockerRule.cs +++ b/Content.Server/StationEvents/Events/BluespaceLockerRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Resist; using Content.Server.Station.Components; @@ -6,6 +5,7 @@ using Content.Server.Storage.Components; using Content.Server.Storage.EntitySystems; using Content.Shared.Access.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Coordinates; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/BreakerFlipRule.cs b/Content.Server/StationEvents/Events/BreakerFlipRule.cs index 3b2368556be..398dc541884 100644 --- a/Content.Server/StationEvents/Events/BreakerFlipRule.cs +++ b/Content.Server/StationEvents/Events/BreakerFlipRule.cs @@ -1,9 +1,8 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; using Content.Server.Announcements.Systems; using Robust.Shared.Player; @@ -18,17 +17,13 @@ public sealed class BreakerFlipRule : StationEventSystem(uid, out var stationEvent)) + return; - _announcer.SendAnnouncement( - _announcer.GetAnnouncementId(args.RuleId), - Filter.Broadcast(), - "station-event-breaker-flip-announcement", - null, - Color.Gold, - null, null, - ("data", Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}")) - ); + var str = Loc.GetString("station-event-breaker-flip-announcement", ("data", Loc.GetString(Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}")))); + stationEvent.StartAnnouncement = str; + + base.Added(uid, component, gameRule, args); } protected override void Started(EntityUid uid, BreakerFlipRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) diff --git a/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs b/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs index 282e28e4991..6600b0623fd 100644 --- a/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs +++ b/Content.Server/StationEvents/Events/BureaucraticErrorRule.cs @@ -1,9 +1,9 @@ using System.Linq; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.Station.Systems; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/Events/CargoGiftsRule.cs b/Content.Server/StationEvents/Events/CargoGiftsRule.cs index 550f799b27e..e43010cdd99 100644 --- a/Content.Server/StationEvents/Events/CargoGiftsRule.cs +++ b/Content.Server/StationEvents/Events/CargoGiftsRule.cs @@ -2,10 +2,9 @@ using Content.Server.Cargo.Components; using Content.Server.Cargo.Systems; using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Prototypes; using Content.Server.Announcements.Systems; using Robust.Shared.Player; @@ -21,19 +20,14 @@ public sealed class CargoGiftsRule : StationEventSystem protected override void Added(EntityUid uid, CargoGiftsRuleComponent component, GameRuleComponent gameRule, GameRuleAddedEvent args) { - base.Added(uid, component, gameRule, args); + if (!TryComp(uid, out var stationEvent)) + return; - _announcer.SendAnnouncement( - _announcer.GetAnnouncementId(args.RuleId), - Filter.Broadcast(), - component.Announce, - null, - Color.FromHex("#18abf5"), - null, null, - ("sender", Loc.GetString(component.Sender)), - ("description", Loc.GetString(component.Description)), - ("dest", Loc.GetString(component.Dest)) - ); + var str = Loc.GetString(component.Announce, + ("sender", Loc.GetString(component.Sender)), ("description", Loc.GetString(component.Description)), ("dest", Loc.GetString(component.Dest))); + stationEvent.StartAnnouncement = str; + + base.Added(uid, component, gameRule, args); } /// diff --git a/Content.Server/StationEvents/Events/ClericalErrorRule.cs b/Content.Server/StationEvents/Events/ClericalErrorRule.cs index 854ee685b33..e52c2c05aaf 100644 --- a/Content.Server/StationEvents/Events/ClericalErrorRule.cs +++ b/Content.Server/StationEvents/Events/ClericalErrorRule.cs @@ -1,9 +1,9 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.StationRecords; using Content.Server.StationRecords.Systems; using Content.Shared.StationRecords; +using Content.Shared.GameTicking.Components; using Robust.Shared.Random; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/FalseAlarmRule.cs b/Content.Server/StationEvents/Events/FalseAlarmRule.cs index 2d129b35584..3b047ad239f 100644 --- a/Content.Server/StationEvents/Events/FalseAlarmRule.cs +++ b/Content.Server/StationEvents/Events/FalseAlarmRule.cs @@ -1,9 +1,7 @@ using System.Linq; -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; -using Robust.Shared.Player; using Robust.Shared.Random; using Content.Server.Announcements.Systems; @@ -17,22 +15,16 @@ public sealed class FalseAlarmRule : StationEventSystem protected override void Started(EntityUid uid, FalseAlarmRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { - base.Started(uid, component, gameRule, args); + if (!TryComp(uid, out var stationEvent)) + return; - var allEv = _event.AllEvents() - .Where(p => p.Value.StartAnnouncement) - .Select(p => p.Key).ToList(); + var allEv = _event.AllEvents().Select(p => p.Value).ToList(); var picked = RobustRandom.Pick(allEv); - _announcer.SendAnnouncement( - _announcer.GetAnnouncementId(picked.ID), - Filter.Broadcast(), - _announcer.GetEventLocaleString(_announcer.GetAnnouncementId(picked.ID)), - null, - Color.Gold, - null, null, - //TODO This isn't a good solution, but I can't think of something better - ("data", Loc.GetString($"random-sentience-event-data-{RobustRandom.Next(1, 6)}")) - ); + stationEvent.StartAnnouncement = picked.StartAnnouncement; + stationEvent.StartAudio = picked.StartAudio; + stationEvent.StartAnnouncementColor = picked.StartAnnouncementColor; + + base.Started(uid, component, gameRule, args); } } diff --git a/Content.Server/StationEvents/Events/FreeProberRule.cs b/Content.Server/StationEvents/Events/FreeProberRule.cs index a5dfdd6b6ea..96233a85d6f 100644 --- a/Content.Server/StationEvents/Events/FreeProberRule.cs +++ b/Content.Server/StationEvents/Events/FreeProberRule.cs @@ -1,4 +1,4 @@ -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Map; using Robust.Shared.Random; using Content.Server.GameTicking.Rules.Components; @@ -8,13 +8,14 @@ using Content.Server.Psionics.Glimmer; using Content.Shared.Construction.EntitySystems; using Content.Shared.Psionics.Glimmer; +using Robust.Shared.Map.Components; namespace Content.Server.StationEvents.Events; internal sealed class FreeProberRule : StationEventSystem { [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly SharedMapSystem _sharedMapSystem = default!; [Dependency] private readonly AnchorableSystem _anchorable = default!; [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; [Dependency] private readonly StationSystem _stationSystem = default!; @@ -59,10 +60,10 @@ protected override void Started(EntityUid uid, FreeProberRuleComponent component var coordinates = xform.Coordinates; var gridUid = xform.GridUid; - if (!_mapManager.TryGetGrid(gridUid, out var grid)) + if (!TryComp(gridUid, out var grid)) continue; - var tileIndices = grid.TileIndicesFor(coordinates); + var tileIndices = _sharedMapSystem.TileIndicesFor(source, grid, coordinates); for (var i = 0; i < SpawnDirections; i++) { @@ -73,7 +74,7 @@ protected override void Started(EntityUid uid, FreeProberRuleComponent component if (!_anchorable.TileFree(grid, offsetIndices)) continue; - Spawn(ProberPrototype, grid.GridTileToLocal(offsetIndices)); + Spawn(ProberPrototype, _sharedMapSystem.GridTileToLocal(source, grid, offsetIndices)); return; } } diff --git a/Content.Server/StationEvents/Events/GasLeakRule.cs b/Content.Server/StationEvents/Events/GasLeakRule.cs index 1221612171d..9e1f70474c1 100644 --- a/Content.Server/StationEvents/Events/GasLeakRule.cs +++ b/Content.Server/StationEvents/Events/GasLeakRule.cs @@ -1,7 +1,7 @@ using Content.Server.Atmos.EntitySystems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Audio; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -29,10 +29,10 @@ protected override void Started(EntityUid uid, GasLeakRuleComponent component, G component.LeakGas = RobustRandom.Pick(component.LeakableGases); // Was 50-50 on using normal distribution. var totalGas = RobustRandom.Next(component.MinimumGas, component.MaximumGas); - var startAfter = stationEvent.StartDelay; component.MolesPerSecond = RobustRandom.Next(component.MinimumMolesPerSecond, component.MaximumMolesPerSecond); - stationEvent.EndTime = _timing.CurTime + TimeSpan.FromSeconds(totalGas / component.MolesPerSecond + startAfter.TotalSeconds); + if (gameRule.Delay is {} startAfter) + stationEvent.EndTime = _timing.CurTime + TimeSpan.FromSeconds(totalGas / component.MolesPerSecond + startAfter.Next(RobustRandom)); } // Look technically if you wanted to guarantee a leak you'd do this in announcement but having the announcement diff --git a/Content.Server/StationEvents/Events/GlimmerEventSystem.cs b/Content.Server/StationEvents/Events/GlimmerEventSystem.cs index 3e0762c8346..37eb0410fbd 100644 --- a/Content.Server/StationEvents/Events/GlimmerEventSystem.cs +++ b/Content.Server/StationEvents/Events/GlimmerEventSystem.cs @@ -1,4 +1,4 @@ -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Psionics.Glimmer; using Content.Shared.Psionics.Glimmer; diff --git a/Content.Server/StationEvents/Events/GlimmerMobSpawnRule.cs b/Content.Server/StationEvents/Events/GlimmerMobSpawnRule.cs index 702147842c6..f80bc83a1e6 100644 --- a/Content.Server/StationEvents/Events/GlimmerMobSpawnRule.cs +++ b/Content.Server/StationEvents/Events/GlimmerMobSpawnRule.cs @@ -1,5 +1,5 @@ using System.Linq; -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Random; using Content.Server.GameTicking; using Content.Server.NPC.Components; diff --git a/Content.Server/StationEvents/Events/GlimmerRandomSentienceRule.cs b/Content.Server/StationEvents/Events/GlimmerRandomSentienceRule.cs index a288710356a..997b7754a5c 100644 --- a/Content.Server/StationEvents/Events/GlimmerRandomSentienceRule.cs +++ b/Content.Server/StationEvents/Events/GlimmerRandomSentienceRule.cs @@ -1,6 +1,4 @@ -using System.Linq; -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Content.Server.Ghost.Roles.Components; using Content.Shared.Abilities.Psionics; using Content.Server.Speech.Components; diff --git a/Content.Server/StationEvents/Events/GlimmerRevenantSpawnRule.cs b/Content.Server/StationEvents/Events/GlimmerRevenantSpawnRule.cs index 152d6d9fe59..8c90e34bd08 100644 --- a/Content.Server/StationEvents/Events/GlimmerRevenantSpawnRule.cs +++ b/Content.Server/StationEvents/Events/GlimmerRevenantSpawnRule.cs @@ -1,6 +1,5 @@ -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Random; -using Content.Server.GameTicking.Rules.Components; using Content.Server.Psionics.Glimmer; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/ImmovableRodRule.cs b/Content.Server/StationEvents/Events/ImmovableRodRule.cs index 45d6c18189c..37f912773c5 100644 --- a/Content.Server/StationEvents/Events/ImmovableRodRule.cs +++ b/Content.Server/StationEvents/Events/ImmovableRodRule.cs @@ -1,10 +1,10 @@ using System.Numerics; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.ImmovableRod; using Content.Server.StationEvents.Components; using Content.Server.Weapons.Ranged.Systems; -using Robust.Shared.Spawners; +using Content.Shared.GameTicking.Components; +using Content.Shared.Storage; using Robust.Shared.Prototypes; using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent; diff --git a/Content.Server/StationEvents/Events/IonStormRule.cs b/Content.Server/StationEvents/Events/IonStormRule.cs index 8361cc6048a..926ecc2db92 100644 --- a/Content.Server/StationEvents/Events/IonStormRule.cs +++ b/Content.Server/StationEvents/Events/IonStormRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using System.Linq; using Content.Server.Silicons.Laws; using Content.Server.Station.Components; @@ -7,6 +6,7 @@ using Content.Shared.Database; using Content.Shared.Dataset; using Content.Shared.FixedPoint; +using Content.Shared.GameTicking.Components; using Content.Shared.Random; using Content.Shared.Random.Helpers; using Content.Shared.Silicons.Laws; diff --git a/Content.Server/StationEvents/Events/KudzuGrowthRule.cs b/Content.Server/StationEvents/Events/KudzuGrowthRule.cs index 5b56e03846f..42c57ffcaae 100644 --- a/Content.Server/StationEvents/Events/KudzuGrowthRule.cs +++ b/Content.Server/StationEvents/Events/KudzuGrowthRule.cs @@ -1,6 +1,6 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/MassHallucinationsRule.cs b/Content.Server/StationEvents/Events/MassHallucinationsRule.cs index 2239db7f701..c35ba94727f 100644 --- a/Content.Server/StationEvents/Events/MassHallucinationsRule.cs +++ b/Content.Server/StationEvents/Events/MassHallucinationsRule.cs @@ -1,7 +1,7 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.Traits.Assorted; +using Content.Shared.GameTicking.Components; using Content.Shared.Mind.Components; using Content.Shared.Traits.Assorted.Components; diff --git a/Content.Server/StationEvents/Events/MassMindSwapRule.cs b/Content.Server/StationEvents/Events/MassMindSwapRule.cs index beb08eb8a79..4a48bcd0d70 100644 --- a/Content.Server/StationEvents/Events/MassMindSwapRule.cs +++ b/Content.Server/StationEvents/Events/MassMindSwapRule.cs @@ -1,9 +1,6 @@ -using Robust.Server.GameObjects; using Robust.Shared.Random; using Content.Server.Abilities.Psionics; -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Psionics; +using Content.Shared.GameTicking.Components; using Content.Server.StationEvents.Components; using Content.Shared.Abilities.Psionics; using Content.Shared.Mobs.Components; diff --git a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs index 455011259dc..b97cde86a02 100644 --- a/Content.Server/StationEvents/Events/MeteorSwarmRule.cs +++ b/Content.Server/StationEvents/Events/MeteorSwarmRule.cs @@ -1,7 +1,7 @@ using System.Numerics; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; diff --git a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs index d9d68a386cf..9cbc193ce61 100644 --- a/Content.Server/StationEvents/Events/NinjaSpawnRule.cs +++ b/Content.Server/StationEvents/Events/NinjaSpawnRule.cs @@ -1,8 +1,8 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Ninja.Systems; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Map; using Robust.Shared.Map.Components; diff --git a/Content.Server/StationEvents/Events/NoosphericFryRule.cs b/Content.Server/StationEvents/Events/NoosphericFryRule.cs index 85f98d6f4be..796056158e4 100644 --- a/Content.Server/StationEvents/Events/NoosphericFryRule.cs +++ b/Content.Server/StationEvents/Events/NoosphericFryRule.cs @@ -3,9 +3,8 @@ using Robust.Shared.Player; using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Construction.EntitySystems; -using Content.Server.GameTicking.Rules.Components; using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; @@ -18,6 +17,7 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Psionics.Glimmer; using Robust.Shared.Audio.Systems; +using Robust.Shared.Map.Components; namespace Content.Server.StationEvents.Events; @@ -38,6 +38,7 @@ internal sealed class NoosphericFryRule : StationEventSystem(gridUid, out var grid)) continue; - var tileIndices = grid.TileIndicesFor(coordinates); + var tileIndices = _sharedMapSystem.TileIndicesFor((EntityUid) gridUid, grid, coordinates); if (_anchorableSystem.TileFree(grid, tileIndices, physics.CollisionLayer, physics.CollisionMask)) _transformSystem.AnchorEntity(reactive, xform); diff --git a/Content.Server/StationEvents/Events/NoosphericStormRule.cs b/Content.Server/StationEvents/Events/NoosphericStormRule.cs index 1de8bad89b5..f9d07ae02da 100644 --- a/Content.Server/StationEvents/Events/NoosphericStormRule.cs +++ b/Content.Server/StationEvents/Events/NoosphericStormRule.cs @@ -1,13 +1,10 @@ using Robust.Shared.Random; using Content.Server.Abilities.Psionics; -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Content.Server.StationEvents.Components; -using Content.Server.Psionics; using Content.Shared.Abilities.Psionics; using Content.Shared.Mobs.Systems; using Content.Shared.Psionics.Glimmer; -using Content.Shared.Zombies; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/NoosphericZapRule.cs b/Content.Server/StationEvents/Events/NoosphericZapRule.cs index 96c33612036..5df31683e7a 100644 --- a/Content.Server/StationEvents/Events/NoosphericZapRule.cs +++ b/Content.Server/StationEvents/Events/NoosphericZapRule.cs @@ -1,5 +1,4 @@ -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; +using Content.Shared.GameTicking.Components; using Content.Server.Popups; using Content.Server.Psionics; using Content.Server.StationEvents.Components; diff --git a/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs b/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs index e6d36839f92..51a4438583f 100644 --- a/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs +++ b/Content.Server/StationEvents/Events/PirateRadioSpawnRule.cs @@ -8,7 +8,7 @@ using Content.Shared.Salvage; using Content.Shared.Random.Helpers; using System.Linq; -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.CCVar; using Robust.Shared.Serialization.Manager; using Content.Shared.Parallax.Biomes; diff --git a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs index b0a0bbc9fe0..e09c88673fb 100644 --- a/Content.Server/StationEvents/Events/PowerGridCheckRule.cs +++ b/Content.Server/StationEvents/Events/PowerGridCheckRule.cs @@ -1,10 +1,10 @@ using System.Threading; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Station.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.Player; diff --git a/Content.Server/StationEvents/Events/PsionicCatGotYourTongueRule.cs b/Content.Server/StationEvents/Events/PsionicCatGotYourTongueRule.cs index b92097b28d0..56b52b88e06 100644 --- a/Content.Server/StationEvents/Events/PsionicCatGotYourTongueRule.cs +++ b/Content.Server/StationEvents/Events/PsionicCatGotYourTongueRule.cs @@ -1,7 +1,6 @@ -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Robust.Shared.Random; using Robust.Shared.Player; -using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Shared.Mobs.Components; using Content.Shared.Abilities.Psionics; diff --git a/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs b/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs index 87d50fc8b2a..a9f27938180 100644 --- a/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs +++ b/Content.Server/StationEvents/Events/RandomEntityStorageSpawnRule.cs @@ -1,8 +1,8 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.Storage.Components; using Content.Server.Storage.EntitySystems; +using Content.Shared.GameTicking.Components; using Robust.Shared.Map; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/Events/RandomSentienceRule.cs b/Content.Server/StationEvents/Events/RandomSentienceRule.cs index 7b9173241f7..2fb733e1a67 100644 --- a/Content.Server/StationEvents/Events/RandomSentienceRule.cs +++ b/Content.Server/StationEvents/Events/RandomSentienceRule.cs @@ -1,10 +1,9 @@ using System.Linq; -using Content.Server.GameTicking.Components; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.Ghost.Roles.Components; -using Content.Server.StationEvents.Components; using Content.Server.Announcements.Systems; +using Content.Server.Ghost.Roles.Components; using Content.Server.Station.Components; +using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/RandomSpawnRule.cs b/Content.Server/StationEvents/Events/RandomSpawnRule.cs index 77744d44e46..e904c24ba60 100644 --- a/Content.Server/StationEvents/Events/RandomSpawnRule.cs +++ b/Content.Server/StationEvents/Events/RandomSpawnRule.cs @@ -1,6 +1,6 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/SolarFlareRule.cs b/Content.Server/StationEvents/Events/SolarFlareRule.cs index 0370b4ee61d..19f6e393d20 100644 --- a/Content.Server/StationEvents/Events/SolarFlareRule.cs +++ b/Content.Server/StationEvents/Events/SolarFlareRule.cs @@ -1,4 +1,3 @@ -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Radio; using Robust.Shared.Random; @@ -8,6 +7,7 @@ using Content.Shared.Radio.Components; using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/StationEventSystem.cs b/Content.Server/StationEvents/Events/StationEventSystem.cs index 040ebad2260..5eb9e09b396 100644 --- a/Content.Server/StationEvents/Events/StationEventSystem.cs +++ b/Content.Server/StationEvents/Events/StationEventSystem.cs @@ -1,19 +1,17 @@ -using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Chat.Systems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; -using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Systems; using Content.Server.StationEvents.Components; using Content.Shared.Database; +using Content.Shared.GameTicking.Components; using Robust.Shared.Audio.Systems; -using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Content.Server.Announcements.Systems; using Robust.Shared.Player; using Content.Server.Station.Components; +using Content.Server.GameTicking; namespace Content.Server.StationEvents.Events; @@ -23,12 +21,12 @@ namespace Content.Server.StationEvents.Events; public abstract class StationEventSystem : GameRuleSystem where T : IComponent { [Dependency] protected readonly IAdminLogManager AdminLogManager = default!; - [Dependency] protected readonly IMapManager MapManager = default!; [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; [Dependency] protected readonly ChatSystem ChatSystem = default!; [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly StationSystem StationSystem = default!; [Dependency] private readonly AnnouncerSystem _announcer = default!; + [Dependency] protected readonly GameTicker GameTicker = default!; protected ISawmill Sawmill = default!; @@ -47,10 +45,15 @@ protected override void Added(EntityUid uid, T component, GameRuleComponent game if (!TryComp(uid, out var stationEvent)) return; - AdminLogManager.Add(LogType.EventAnnounced, $"Event added / announced: {ToPrettyString(uid)}"); - stationEvent.StartTime = Timing.CurTime + stationEvent.StartDelay; + // we don't want to send to players who aren't in game (i.e. in the lobby) + Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame); + + if (stationEvent.StartAnnouncement != null) + ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.StartAnnouncement), playSound: false, colorOverride: stationEvent.StartAnnouncementColor); + + Audio.PlayGlobal(stationEvent.StartAudio, allPlayersInGame, true); } /// @@ -62,16 +65,10 @@ protected override void Started(EntityUid uid, T component, GameRuleComponent ga return; AdminLogManager.Add(LogType.EventStarted, LogImpact.High, $"Event started: {ToPrettyString(uid)}"); + Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame); - if (stationEvent.StartAnnouncement) - { - _announcer.SendAnnouncement( - _announcer.GetAnnouncementId(args.RuleId), - Filter.Broadcast(), - _announcer.GetEventLocaleString(_announcer.GetAnnouncementId(args.RuleId)), - colorOverride: Color.Gold - ); - } + if (stationEvent.StartAnnouncement != null) + ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.StartAnnouncement), playSound: false, colorOverride: stationEvent.StartAnnouncementColor); if (stationEvent.Duration != null) { @@ -93,14 +90,13 @@ protected override void Ended(EntityUid uid, T component, GameRuleComponent game AdminLogManager.Add(LogType.EventStopped, $"Event ended: {ToPrettyString(uid)}"); - if (stationEvent.EndAnnouncement) - { - _announcer.SendAnnouncement( - _announcer.GetAnnouncementId(args.RuleId, true), - Filter.Broadcast(), - _announcer.GetEventLocaleString(_announcer.GetAnnouncementId(args.RuleId, true)), - colorOverride: Color.Gold); - } + // we don't want to send to players who aren't in game (i.e. in the lobby) + Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame); + + if (stationEvent.EndAnnouncement != null) + ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.EndAnnouncement), playSound: false, colorOverride: stationEvent.EndAnnouncementColor); + + Audio.PlayGlobal(stationEvent.EndAudio, allPlayersInGame, true); } /// @@ -118,11 +114,7 @@ public override void Update(float frameTime) if (!GameTicker.IsGameRuleAdded(uid, ruleData)) continue; - if (!GameTicker.IsGameRuleActive(uid, ruleData) && Timing.CurTime >= stationEvent.StartTime) - { - GameTicker.StartGameRule(uid, ruleData); - } - else if (stationEvent.EndTime != null && Timing.CurTime >= stationEvent.EndTime && GameTicker.IsGameRuleActive(uid, ruleData)) + if (stationEvent.EndTime != null && Timing.CurTime >= stationEvent.EndTime && GameTicker.IsGameRuleActive(uid, ruleData)) { GameTicker.EndGameRule(uid, ruleData); } diff --git a/Content.Server/StationEvents/Events/VentClogRule.cs b/Content.Server/StationEvents/Events/VentClogRule.cs index 867f41dcccf..043ea0375aa 100644 --- a/Content.Server/StationEvents/Events/VentClogRule.cs +++ b/Content.Server/StationEvents/Events/VentClogRule.cs @@ -6,9 +6,9 @@ using Robust.Shared.Random; using System.Linq; using Content.Server.Fluids.EntitySystems; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Shared.GameTicking.Components; namespace Content.Server.StationEvents.Events; diff --git a/Content.Server/StationEvents/Events/VentCrittersRule.cs b/Content.Server/StationEvents/Events/VentCrittersRule.cs index c2605039bce..fba9cfa6a60 100644 --- a/Content.Server/StationEvents/Events/VentCrittersRule.cs +++ b/Content.Server/StationEvents/Events/VentCrittersRule.cs @@ -1,7 +1,7 @@ -using Content.Server.GameTicking.Components; using Content.Server.StationEvents.Components; using Content.Server.GameTicking.Rules.Components; using Content.Server.Station.Components; +using Content.Shared.GameTicking.Components; using Content.Shared.Storage; using Robust.Shared.Map; using Robust.Shared.Random; diff --git a/Content.Server/StationEvents/OscillatingStationEventScheduler.cs b/Content.Server/StationEvents/OscillatingStationEventScheduler.cs index 1e6dbd14a18..a9b9de586ee 100644 --- a/Content.Server/StationEvents/OscillatingStationEventScheduler.cs +++ b/Content.Server/StationEvents/OscillatingStationEventScheduler.cs @@ -2,7 +2,7 @@ using System.Diagnostics.CodeAnalysis; using Content.Server.GameTicking; using Content.Server.GameTicking.Rules; -using Content.Server.GameTicking.Components; +using Content.Shared.GameTicking.Components; using Content.Server.StationEvents.Components; using Content.Shared.CCVar; using Robust.Shared.Configuration; diff --git a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs index a6c38ef765f..545064b39fe 100644 --- a/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs +++ b/Content.Server/StationEvents/RampingStationEventSchedulerSystem.cs @@ -1,10 +1,10 @@ using Content.Server.GameTicking; -using Content.Server.GameTicking.Components; using Content.Server.GameTicking.Rules; using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; using Content.Server.StationEvents.Events; using Content.Shared.CCVar; +using Content.Shared.GameTicking.Components; using Robust.Shared.Configuration; using Robust.Shared.Random; diff --git a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs index f411001bd3a..1adab9930d9 100644 --- a/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs +++ b/Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMicrophoneSystem.cs @@ -16,10 +16,10 @@ public override void Initialize() SubscribeLocalEvent(OnInit); SubscribeLocalEvent(RelayEntityMessage); SubscribeLocalEvent(CanListen); - SubscribeLocalEvent(OnExpandRecipients); + SubscribeLocalEvent(OnExpandRecipients); } - private void OnExpandRecipients(ExpandICChatRecipientstEvent ev) + private void OnExpandRecipients(ExpandICChatRecipientsEvent ev) { var xformQuery = GetEntityQuery(); var sourceXform = Transform(ev.Source); diff --git a/Content.Server/Temperature/Components/TemperatureComponent.cs b/Content.Server/Temperature/Components/TemperatureComponent.cs index ec00a570f96..3bfa12f2693 100644 --- a/Content.Server/Temperature/Components/TemperatureComponent.cs +++ b/Content.Server/Temperature/Components/TemperatureComponent.cs @@ -1,7 +1,9 @@ using Content.Server.Temperature.Systems; +using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Damage; using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; namespace Content.Server.Temperature.Components; @@ -78,4 +80,10 @@ public float HeatCapacity /// [DataField] public bool TakingDamage = false; + + [DataField] + public ProtoId HotAlert = "Hot"; + + [DataField] + public ProtoId ColdAlert = "Cold"; } diff --git a/Content.Server/Temperature/Systems/TemperatureSystem.cs b/Content.Server/Temperature/Systems/TemperatureSystem.cs index 0f57da4b881..2f4497bdbbc 100644 --- a/Content.Server/Temperature/Systems/TemperatureSystem.cs +++ b/Content.Server/Temperature/Systems/TemperatureSystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Rejuvenate; using Content.Shared.Temperature; using Robust.Shared.Physics.Components; +using Robust.Shared.Prototypes; namespace Content.Server.Temperature.Systems; @@ -33,6 +34,9 @@ public sealed class TemperatureSystem : EntitySystem private float _accumulatedFrametime; + [ValidatePrototypeId] + public const string TemperatureAlertCategory = "Temperature"; + public override void Initialize() { SubscribeLocalEvent(EnqueueDamage); @@ -181,13 +185,13 @@ private void OnRejuvenate(EntityUid uid, TemperatureComponent comp, RejuvenateEv private void ServerAlert(EntityUid uid, AlertsComponent status, OnTemperatureChangeEvent args) { - AlertType type; + ProtoId type; float threshold; float idealTemp; if (!TryComp(uid, out var temperature)) { - _alerts.ClearAlertCategory(uid, AlertCategory.Temperature); + _alerts.ClearAlertCategory(uid, TemperatureAlertCategory); return; } @@ -204,12 +208,12 @@ private void ServerAlert(EntityUid uid, AlertsComponent status, OnTemperatureCha if (args.CurrentTemperature <= idealTemp) { - type = AlertType.Cold; + type = temperature.ColdAlert; threshold = temperature.ColdDamageThreshold; } else { - type = AlertType.Hot; + type = temperature.HotAlert; threshold = temperature.HeatDamageThreshold; } @@ -231,7 +235,7 @@ private void ServerAlert(EntityUid uid, AlertsComponent status, OnTemperatureCha break; case > 0.66f: - _alerts.ClearAlertCategory(uid, AlertCategory.Temperature); + _alerts.ClearAlertCategory(uid, TemperatureAlertCategory); break; } } diff --git a/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs b/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs index a35972f1c86..18d1842a87d 100644 --- a/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs +++ b/Content.Server/Traits/Assorted/ForeignerTraitComponent.cs @@ -34,5 +34,5 @@ public sealed partial class ForeignerTraitComponent : Component /// The base translator prototype to use when creating a translator for the entity. /// [DataField(required: true)] - public ProtoId BaseTranslator = default!; + public EntProtoId BaseTranslator = default!; } diff --git a/Content.Server/Traits/Assorted/ParacusiaSystem.cs b/Content.Server/Traits/Assorted/ParacusiaSystem.cs index cf08e09e90e..d92e200a4fa 100644 --- a/Content.Server/Traits/Assorted/ParacusiaSystem.cs +++ b/Content.Server/Traits/Assorted/ParacusiaSystem.cs @@ -14,7 +14,7 @@ public void SetSounds(EntityUid uid, SoundSpecifier sounds, ParacusiaComponent? return; } component.Sounds = sounds; - Dirty(component); + Dirty(uid, component); } public void SetTime(EntityUid uid, float minTime, float maxTime, ParacusiaComponent? component = null) @@ -25,7 +25,7 @@ public void SetTime(EntityUid uid, float minTime, float maxTime, ParacusiaCompon } component.MinTimeBetweenIncidents = minTime; component.MaxTimeBetweenIncidents = maxTime; - Dirty(component); + Dirty(uid, component); } public void SetDistance(EntityUid uid, float maxSoundDistance, ParacusiaComponent? component = null) @@ -35,6 +35,6 @@ public void SetDistance(EntityUid uid, float maxSoundDistance, ParacusiaComponen return; } component.MaxSoundDistance = maxSoundDistance; - Dirty(component); + Dirty(uid, component); } } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs index 25010b22333..0dcd92f9417 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs @@ -51,7 +51,7 @@ private void UpdateShots(EntityUid uid, BatteryAmmoProviderComponent component, if (component.Shots != shots || component.Capacity != maxShots) { - Dirty(component); + Dirty(uid, component); } component.Shots = shots; diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Revolver.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Revolver.cs index 6ff47507299..59e53f1f72a 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.Revolver.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.Revolver.cs @@ -13,6 +13,6 @@ protected override void SpinRevolver(EntityUid revolverUid, RevolverAmmoProvider return; component.CurrentIndex = index; - Dirty(component); + Dirty(revolverUid, component); } } diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/RandomInstrumentArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/RandomInstrumentArtifactSystem.cs index 8945b867954..118bc396a72 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/RandomInstrumentArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/RandomInstrumentArtifactSystem.cs @@ -18,6 +18,6 @@ public override void Initialize() private void OnStartup(EntityUid uid, RandomInstrumentArtifactComponent component, ComponentStartup args) { var instrument = EnsureComp(uid); - _instrument.SetInstrumentProgram(instrument, (byte) _random.Next(0, 127), 0); + _instrument.SetInstrumentProgram(uid, instrument, (byte) _random.Next(0, 127), 0); } } diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index a906652765c..d90ceab0dca 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -191,7 +191,7 @@ public void ZombifyEntity(EntityUid target, MobStateComponent? mobState = null) Dirty(target, pryComp); } - Dirty(melee); + Dirty(target, melee); //The zombie gets the assigned damage weaknesses and strengths _damageable.SetDamageModifierSetId(target, "Zombie"); diff --git a/Content.Shared/Actions/Events/FabricateActionEvent.cs b/Content.Shared/Actions/Events/FabricateActionEvent.cs index 7483cb04d98..33ab826cc8e 100644 --- a/Content.Shared/Actions/Events/FabricateActionEvent.cs +++ b/Content.Shared/Actions/Events/FabricateActionEvent.cs @@ -5,5 +5,5 @@ namespace Content.Shared.Actions.Events; public sealed partial class FabricateActionEvent : InstantActionEvent { [DataField(required: true)] - public ProtoId Fabrication; + public EntProtoId Fabrication; } diff --git a/Content.Shared/Alert/AlertCategory.cs b/Content.Shared/Alert/AlertCategory.cs deleted file mode 100644 index 57a3e40f70e..00000000000 --- a/Content.Shared/Alert/AlertCategory.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Content.Shared.Alert; - -/// -/// Every category of alert. Corresponds to category field in alert prototypes defined in YML -/// -public enum AlertCategory -{ - Pressure, - Temperature, - Breathing, - Buckled, - Health, - Mood, - Internals, - Stamina, - Piloting, - Hunger, - Thirst, - Toxins, - Battery -} diff --git a/Content.Shared/Alert/AlertCategoryPrototype.cs b/Content.Shared/Alert/AlertCategoryPrototype.cs new file mode 100644 index 00000000000..7c7d0475214 --- /dev/null +++ b/Content.Shared/Alert/AlertCategoryPrototype.cs @@ -0,0 +1,14 @@ +using Robust.Shared.Prototypes; + +namespace Content.Shared.Alert; + +/// +/// This is a prototype for a category for marking alerts as mutually exclusive. +/// +[Prototype] +public sealed partial class AlertCategoryPrototype : IPrototype +{ + /// + [IdDataField] + public string ID { get; } = default!; +} diff --git a/Content.Shared/Alert/AlertKey.cs b/Content.Shared/Alert/AlertKey.cs index c784af4cd48..c5c3a7643ec 100644 --- a/Content.Shared/Alert/AlertKey.cs +++ b/Content.Shared/Alert/AlertKey.cs @@ -1,5 +1,5 @@ -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.Manager; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; namespace Content.Shared.Alert; @@ -11,13 +11,13 @@ namespace Content.Shared.Alert; [Serializable, NetSerializable] public struct AlertKey { - public AlertType? AlertType { get; private set; } = Alert.AlertType.Error; - public readonly AlertCategory? AlertCategory; + public ProtoId? AlertType { get; private set; } = default!; + public readonly ProtoId? AlertCategory; /// NOTE: if the alert has a category you must pass the category for this to work /// properly as a key. I.e. if the alert has a category and you pass only the alert type, and you /// compare this to another AlertKey that has both the category and the same alert type, it will not consider them equal. - public AlertKey(AlertType? alertType, AlertCategory? alertCategory) + public AlertKey(ProtoId? alertType, ProtoId? alertCategory) { AlertCategory = alertCategory; AlertType = alertType; @@ -49,7 +49,7 @@ public override int GetHashCode() /// alert category, must not be null /// An alert key for the provided alert category. This must only be used for /// queries and never storage, as it is lacking an alert type. - public static AlertKey ForCategory(AlertCategory category) + public static AlertKey ForCategory(ProtoId category) { return new(null, category); } diff --git a/Content.Shared/Alert/AlertOrderPrototype.cs b/Content.Shared/Alert/AlertOrderPrototype.cs index 8279d592b4b..af4241a27e7 100644 --- a/Content.Shared/Alert/AlertOrderPrototype.cs +++ b/Content.Shared/Alert/AlertOrderPrototype.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Alert /// /// Defines the order of alerts so they show up in a consistent order. /// - [Prototype("alertOrder")] + [Prototype] [DataDefinition] public sealed partial class AlertOrderPrototype : IPrototype, IComparer { @@ -15,7 +15,7 @@ public sealed partial class AlertOrderPrototype : IPrototype, IComparer(alert)] = i++; + _typeToIdx[alert] = i++; break; case "category": - _categoryToIdx[Enum.Parse(alert)] = i++; + _categoryToIdx[alert] = i++; break; default: throw new ArgumentException(); @@ -58,17 +58,17 @@ public sealed partial class AlertOrderPrototype : IPrototype, IComparer _typeToIdx = new(); - private readonly Dictionary _categoryToIdx = new(); + private readonly Dictionary, int> _typeToIdx = new(); + private readonly Dictionary, int> _categoryToIdx = new(); private int GetOrderIndex(AlertPrototype alert) { - if (_typeToIdx.TryGetValue(alert.AlertType, out var idx)) + if (_typeToIdx.TryGetValue(alert.ID, out var idx)) { return idx; } if (alert.Category != null && - _categoryToIdx.TryGetValue((AlertCategory) alert.Category, out idx)) + _categoryToIdx.TryGetValue(alert.Category.Value, out idx)) { return idx; } @@ -78,20 +78,25 @@ private int GetOrderIndex(AlertPrototype alert) public int Compare(AlertPrototype? x, AlertPrototype? y) { - if ((x == null) && (y == null)) return 0; - if (x == null) return 1; - if (y == null) return -1; + if (x == null && y == null) + return 0; + if (x == null) + return 1; + if (y == null) + return -1; var idx = GetOrderIndex(x); var idy = GetOrderIndex(y); if (idx == -1 && idy == -1) { // break ties by type value // Must cast to int to avoid integer overflow when subtracting (enum's unsigned) - return (int)x.AlertType - (int)y.AlertType; + return string.Compare(x.ID, y.ID, StringComparison.InvariantCulture); } - if (idx == -1) return 1; - if (idy == -1) return -1; + if (idx == -1) + return 1; + if (idy == -1) + return -1; var result = idx - idy; // not strictly necessary (we don't care about ones that go at the same index) // but it makes the sort stable @@ -99,7 +104,7 @@ public int Compare(AlertPrototype? x, AlertPrototype? y) { // break ties by type value // Must cast to int to avoid integer overflow when subtracting (enum's unsigned) - return (int)x.AlertType - (int)y.AlertType; + return string.Compare(x.ID, y.ID, StringComparison.InvariantCulture); } return result; diff --git a/Content.Shared/Alert/AlertPrototype.cs b/Content.Shared/Alert/AlertPrototype.cs index 248cc00ba40..f53da27c0de 100644 --- a/Content.Shared/Alert/AlertPrototype.cs +++ b/Content.Shared/Alert/AlertPrototype.cs @@ -1,120 +1,116 @@ using Robust.Shared.Prototypes; using Robust.Shared.Utility; -namespace Content.Shared.Alert +namespace Content.Shared.Alert; + +/// +/// An alert popup with associated icon, tooltip, and other data. +/// +[Prototype] +public sealed partial class AlertPrototype : IPrototype { /// - /// An alert popup with associated icon, tooltip, and other data. + /// Type of alert, no 2 alert prototypes should have the same one. /// - [Prototype("alert")] - public sealed partial class AlertPrototype : IPrototype - { - [ViewVariables] - string IPrototype.ID => AlertType.ToString(); - - /// - /// Type of alert, no 2 alert prototypes should have the same one. - /// - [IdDataField] - public AlertType AlertType { get; private set; } - - /// - /// List of icons to use for this alert. Each entry corresponds to a different severity level, starting from the - /// minimum and incrementing upwards. If severities are not supported, the first entry is used. - /// - [DataField("icons", required: true)] - public List Icons = new(); - - /// - /// An entity used for displaying the in the UI control. - /// - [DataField] - public EntProtoId AlertViewEntity = "AlertSpriteView"; - - /// - /// Name to show in tooltip window. Accepts formatting. - /// - [DataField("name")] - public string Name { get; private set; } = ""; - - /// - /// Description to show in tooltip window. Accepts formatting. - /// - [DataField("description")] - public string Description { get; private set; } = ""; - - /// - /// Category the alert belongs to. Only one alert of a given category - /// can be shown at a time. If one is shown while another is already being shown, - /// it will be replaced. This can be useful for categories of alerts which should naturally - /// replace each other and are mutually exclusive, for example lowpressure / highpressure, - /// hot / cold. If left unspecified, the alert will not replace or be replaced by any other alerts. - /// - [DataField("category")] - public AlertCategory? Category { get; private set; } - - /// - /// Key which is unique w.r.t category semantics (alerts with same category have equal keys, - /// alerts with no category have different keys). - /// - public AlertKey AlertKey => new(AlertType, Category); - - /// - /// -1 (no effect) unless MaxSeverity is specified. Defaults to 1. Minimum severity level supported by this state. - /// - public short MinSeverity => MaxSeverity == -1 ? (short) -1 : _minSeverity; - - [DataField("minSeverity")] private short _minSeverity = 1; - - /// - /// Maximum severity level supported by this state. -1 (default) indicates - /// no severity levels are supported by the state. - /// - [DataField("maxSeverity")] - public short MaxSeverity = -1; - - /// - /// Indicates whether this state support severity levels - /// - public bool SupportsSeverity => MaxSeverity != -1; - - /// - /// Defines what to do when the alert is clicked. - /// This will always be null on clientside. - /// - [DataField("onClick", serverOnly: true)] - public IAlertClick? OnClick { get; private set; } - - /// severity level, if supported by this alert - /// the icon path to the texture for the provided severity level - public SpriteSpecifier GetIcon(short? severity = null) - { - var minIcons = SupportsSeverity - ? MaxSeverity - MinSeverity - : 1; + [IdDataField] + public string ID { get; private set; } = default!; - if (Icons.Count < minIcons) - throw new InvalidOperationException($"Insufficient number of icons given for alert {AlertType}"); + /// + /// List of icons to use for this alert. Each entry corresponds to a different severity level, starting from the + /// minimum and incrementing upwards. If severities are not supported, the first entry is used. + /// + [DataField(required: true)] + public List Icons = new(); - if (!SupportsSeverity) - return Icons[0]; + /// + /// An entity used for displaying the in the UI control. + /// + [DataField] + public EntProtoId AlertViewEntity = "AlertSpriteView"; - if (severity == null) - { - throw new ArgumentException($"No severity specified but this alert ({AlertKey}) has severity.", nameof(severity)); - } + /// + /// Name to show in tooltip window. Accepts formatting. + /// + [DataField] + public string Name { get; private set; } = string.Empty; - if (severity < MinSeverity) - { - throw new ArgumentOutOfRangeException(nameof(severity), $"Severity below minimum severity in {AlertKey}."); - } + /// + /// Description to show in tooltip window. Accepts formatting. + /// + [DataField] + public string Description { get; private set; } = string.Empty; - if (severity > MaxSeverity) - { - throw new ArgumentOutOfRangeException(nameof(severity), $"Severity above maximum severity in {AlertKey}."); - } + /// + /// Category the alert belongs to. Only one alert of a given category + /// can be shown at a time. If one is shown while another is already being shown, + /// it will be replaced. This can be useful for categories of alerts which should naturally + /// replace each other and are mutually exclusive, for example lowpressure / highpressure, + /// hot / cold. If left unspecified, the alert will not replace or be replaced by any other alerts. + /// + [DataField] + public ProtoId? Category { get; private set; } + + /// + /// Key which is unique w.r.t category semantics (alerts with same category have equal keys, + /// alerts with no category have different keys). + /// + public AlertKey AlertKey => new(ID, Category); - return Icons[severity.Value - _minSeverity]; + /// + /// -1 (no effect) unless MaxSeverity is specified. Defaults to 1. Minimum severity level supported by this state. + /// + public short MinSeverity => MaxSeverity == -1 ? (short) -1 : _minSeverity; + + [DataField("minSeverity")] private short _minSeverity = 1; + + /// + /// Maximum severity level supported by this state. -1 (default) indicates + /// no severity levels are supported by the state. + /// + [DataField] + public short MaxSeverity = -1; + + /// + /// Indicates whether this state support severity levels + /// + public bool SupportsSeverity => MaxSeverity != -1; + + /// + /// Defines what to do when the alert is clicked. + /// This will always be null on clientside. + /// + [DataField(serverOnly: true)] + public IAlertClick? OnClick { get; private set; } + + /// severity level, if supported by this alert + /// the icon path to the texture for the provided severity level + public SpriteSpecifier GetIcon(short? severity = null) + { + var minIcons = SupportsSeverity + ? MaxSeverity - MinSeverity + : 1; + + if (Icons.Count < minIcons) + throw new InvalidOperationException($"Insufficient number of icons given for alert {ID}"); + + if (!SupportsSeverity) + return Icons[0]; + + if (severity == null) + { + throw new ArgumentException($"No severity specified but this alert ({AlertKey}) has severity.", nameof(severity)); + } + + if (severity < MinSeverity) + { + throw new ArgumentOutOfRangeException(nameof(severity), $"Severity below minimum severity in {AlertKey}."); } + + if (severity > MaxSeverity) + { + throw new ArgumentOutOfRangeException(nameof(severity), $"Severity above maximum severity in {AlertKey}."); + } + + return Icons[severity.Value - _minSeverity]; } } diff --git a/Content.Shared/Alert/AlertState.cs b/Content.Shared/Alert/AlertState.cs index effd9522036..d6309f6b426 100644 --- a/Content.Shared/Alert/AlertState.cs +++ b/Content.Shared/Alert/AlertState.cs @@ -1,3 +1,4 @@ +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Alert; @@ -9,5 +10,5 @@ public struct AlertState public (TimeSpan, TimeSpan)? Cooldown; public bool AutoRemove; public bool ShowCooldown; - public AlertType Type; + public ProtoId Type; } diff --git a/Content.Shared/Alert/AlertType.cs b/Content.Shared/Alert/AlertType.cs deleted file mode 100644 index bd8c1dbe257..00000000000 --- a/Content.Shared/Alert/AlertType.cs +++ /dev/null @@ -1,78 +0,0 @@ -namespace Content.Shared.Alert -{ - /// - /// Every kind of alert. Corresponds to alertType field in alert prototypes defined in YML - /// NOTE: Using byte for a compact encoding when sending this in messages, can upgrade - /// to ushort - /// - public enum AlertType : byte - { - Error, - LowOxygen, - LowNitrogen, - LowPressure, - HighPressure, - Fire, - Cold, - Hot, - Weightless, - Stun, - Handcuffed, - Ensnared, - Buckled, - HumanCrit, - HumanDead, - HumanHealth, - BorgBattery, - BorgBatteryNone, - - // Mood - Bleeding, - Insane, - Horrible, - Terrible, - Bad, - Meh, - Neutral, - Good, - Great, - Exceptional, - Perfect, - MoodDead, - CultBuffed, - - PilotingShuttle, - Peckish, - Starving, - Thirsty, - Parched, - Stamina, - Pulled, - Pulling, - Magboots, - Internals, - Toxins, - Muted, - Walking, - VowOfSilence, - VowBroken, - Essence, - Corporeal, - Bleed, - Pacified, - Debug1, - Debug2, - Debug3, - Debug4, - Debug5, - Debug6, - SuitPower, - BorgHealth, - BorgCrit, - BorgDead, - Offer, - ShadowkinPower, - Deflecting, - } - -} diff --git a/Content.Shared/Alert/AlertsSystem.cs b/Content.Shared/Alert/AlertsSystem.cs index 5b888e30c4c..83c6fcd0dd7 100644 --- a/Content.Shared/Alert/AlertsSystem.cs +++ b/Content.Shared/Alert/AlertsSystem.cs @@ -11,7 +11,7 @@ public abstract class AlertsSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IGameTiming _timing = default!; - private FrozenDictionary _typeToAlert = default!; + private FrozenDictionary, AlertPrototype> _typeToAlert = default!; public IReadOnlyDictionary? GetActiveAlerts(EntityUid euid) { @@ -20,23 +20,23 @@ public abstract class AlertsSystem : EntitySystem : null; } - public short GetSeverityRange(AlertType alertType) + public short GetSeverityRange(ProtoId alertType) { var minSeverity = _typeToAlert[alertType].MinSeverity; return (short)MathF.Max(minSeverity,_typeToAlert[alertType].MaxSeverity - minSeverity); } - public short GetMaxSeverity(AlertType alertType) + public short GetMaxSeverity(ProtoId alertType) { return _typeToAlert[alertType].MaxSeverity; } - public short GetMinSeverity(AlertType alertType) + public short GetMinSeverity(ProtoId alertType) { return _typeToAlert[alertType].MinSeverity; } - public bool IsShowingAlert(EntityUid euid, AlertType alertType) + public bool IsShowingAlert(EntityUid euid, ProtoId alertType) { if (!EntityManager.TryGetComponent(euid, out AlertsComponent? alertsComponent)) return false; @@ -51,7 +51,7 @@ public bool IsShowingAlert(EntityUid euid, AlertType alertType) } /// true iff an alert of the indicated alert category is currently showing - public bool IsShowingAlertCategory(EntityUid euid, AlertCategory alertCategory) + public bool IsShowingAlertCategory(EntityUid euid, ProtoId alertCategory) { return EntityManager.TryGetComponent(euid, out AlertsComponent? alertsComponent) && alertsComponent.Alerts.ContainsKey(AlertKey.ForCategory(alertCategory)); @@ -78,7 +78,7 @@ public bool TryGetAlertState(EntityUid euid, AlertKey key, out AlertState alertS /// be erased if there is currently a cooldown for the alert) /// if true, the alert will be removed at the end of the cooldown /// if true, the cooldown will be visibly shown over the alert icon - public void ShowAlert(EntityUid euid, AlertType alertType, short? severity = null, (TimeSpan, TimeSpan)? cooldown = null, bool autoRemove = false, bool showCooldown = true ) + public void ShowAlert(EntityUid euid, ProtoId alertType, short? severity = null, (TimeSpan, TimeSpan)? cooldown = null, bool autoRemove = false, bool showCooldown = true ) { // This should be handled as part of networking. if (_timing.ApplyingState) @@ -131,7 +131,7 @@ public void ShowAlert(EntityUid euid, AlertType alertType, short? severity = nul /// /// Clear the alert with the given category, if one is currently showing. /// - public void ClearAlertCategory(EntityUid euid, AlertCategory category) + public void ClearAlertCategory(EntityUid euid, ProtoId category) { if(!TryComp(euid, out AlertsComponent? alertsComponent)) return; @@ -150,7 +150,7 @@ public void ClearAlertCategory(EntityUid euid, AlertCategory category) /// /// Clear the alert of the given type if it is currently showing. /// - public void ClearAlert(EntityUid euid, AlertType alertType) + public void ClearAlert(EntityUid euid, ProtoId alertType) { if (_timing.ApplyingState) return; @@ -286,13 +286,13 @@ private void HandlePrototypesReloaded(PrototypesReloadedEventArgs obj) protected virtual void LoadPrototypes() { - var dict = new Dictionary(); + var dict = new Dictionary, AlertPrototype>(); foreach (var alert in _prototypeManager.EnumeratePrototypes()) { - if (!dict.TryAdd(alert.AlertType, alert)) + if (!dict.TryAdd(alert.ID, alert)) { Log.Error("Found alert with duplicate alertType {0} - all alerts must have" + - " a unique alertType, this one will be skipped", alert.AlertType); + " a unique alertType, this one will be skipped", alert.ID); } } @@ -303,7 +303,7 @@ protected virtual void LoadPrototypes() /// Tries to get the alert of the indicated type /// /// true if found - public bool TryGet(AlertType alertType, [NotNullWhen(true)] out AlertPrototype? alert) + public bool TryGet(ProtoId alertType, [NotNullWhen(true)] out AlertPrototype? alert) { return _typeToAlert.TryGetValue(alertType, out alert); } diff --git a/Content.Shared/Alert/ClickAlertEvent.cs b/Content.Shared/Alert/ClickAlertEvent.cs index fe7ca97e4c2..43dd086b562 100644 --- a/Content.Shared/Alert/ClickAlertEvent.cs +++ b/Content.Shared/Alert/ClickAlertEvent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.Serialization; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; namespace Content.Shared.Alert; @@ -8,9 +9,9 @@ namespace Content.Shared.Alert; [Serializable, NetSerializable] public sealed class ClickAlertEvent : EntityEventArgs { - public readonly AlertType Type; + public readonly ProtoId Type; - public ClickAlertEvent(AlertType alertType) + public ClickAlertEvent(ProtoId alertType) { Type = alertType; } diff --git a/Content.Shared/Buckle/Components/BuckleComponent.cs b/Content.Shared/Buckle/Components/BuckleComponent.cs index cf28b56d51f..ee86e6d4de0 100644 --- a/Content.Shared/Buckle/Components/BuckleComponent.cs +++ b/Content.Shared/Buckle/Components/BuckleComponent.cs @@ -1,10 +1,15 @@ +using System.Diagnostics.CodeAnalysis; using Content.Shared.Interaction; using Robust.Shared.GameStates; using Robust.Shared.Serialization; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Buckle.Components; -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +/// +/// This component allows an entity to be buckled to an entity with a . +/// +[RegisterComponent, NetworkedComponent] [Access(typeof(SharedBuckleSystem))] public sealed partial class BuckleComponent : Component { @@ -14,31 +19,23 @@ public sealed partial class BuckleComponent : Component /// across a table two tiles away" problem. /// [DataField] - [ViewVariables(VVAccess.ReadWrite)] public float Range = SharedInteractionSystem.InteractionRange / 1.4f; /// /// True if the entity is buckled, false otherwise. /// - [ViewVariables(VVAccess.ReadWrite)] - [AutoNetworkedField] - public bool Buckled; - - [ViewVariables] - [AutoNetworkedField] - public EntityUid? LastEntityBuckledTo; + [MemberNotNullWhen(true, nameof(BuckledTo))] + public bool Buckled => BuckledTo != null; /// /// Whether or not collisions should be possible with the entity we are strapped to /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField, AutoNetworkedField] + [DataField] public bool DontCollide; /// /// Whether or not we should be allowed to pull the entity we are strapped to /// - [ViewVariables(VVAccess.ReadWrite)] [DataField] public bool PullStrap; @@ -47,20 +44,18 @@ public sealed partial class BuckleComponent : Component /// be able to unbuckle after recently buckling. /// [DataField] - [ViewVariables(VVAccess.ReadWrite)] public TimeSpan Delay = TimeSpan.FromSeconds(0.25f); /// /// The time that this entity buckled at. /// - [ViewVariables] - public TimeSpan BuckleTime; + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan? BuckleTime; /// /// The strap that this component is buckled to. /// - [ViewVariables] - [AutoNetworkedField] + [DataField] public EntityUid? BuckledTo; /// @@ -68,7 +63,6 @@ public sealed partial class BuckleComponent : Component /// . /// [DataField] - [ViewVariables(VVAccess.ReadWrite)] public int Size = 100; /// @@ -77,11 +71,90 @@ public sealed partial class BuckleComponent : Component [ViewVariables] public int? OriginalDrawDepth; } +[Serializable, NetSerializable] +public sealed class BuckleState(NetEntity? buckledTo, bool dontCollide, TimeSpan? buckleTime) : ComponentState +{ + public readonly NetEntity? BuckledTo = buckledTo; + public readonly bool DontCollide = dontCollide; + public readonly TimeSpan? BuckleTime = buckleTime; +} + + +/// +/// Event raised directed at a strap entity before some entity gets buckled to it. +/// +[ByRefEvent] +public record struct StrapAttemptEvent( + Entity Strap, + Entity Buckle, + EntityUid? User, + bool Popup) +{ + public bool Cancelled; +} + +/// +/// Event raised directed at a buckle entity before it gets buckled to some strap entity. +/// +[ByRefEvent] +public record struct BuckleAttemptEvent( + Entity Strap, + Entity Buckle, + EntityUid? User, + bool Popup) +{ + public bool Cancelled; +} + +/// +/// Event raised directed at a strap entity before some entity gets unbuckled from it. +/// +[ByRefEvent] +public record struct UnstrapAttemptEvent( + Entity Strap, + Entity Buckle, + EntityUid? User, + bool Popup) +{ + public bool Cancelled; +} + +/// +/// Event raised directed at a buckle entity before it gets unbuckled. +/// +[ByRefEvent] +public record struct UnbuckleAttemptEvent( + Entity Strap, + Entity Buckle, + EntityUid? User, + bool Popup) +{ + public bool Cancelled; +} + +/// +/// Event raised directed at a strap entity after something has been buckled to it. +/// +[ByRefEvent] +public readonly record struct StrappedEvent(Entity Strap, Entity Buckle); + +/// +/// Event raised directed at a buckle entity after it has been buckled. +/// +[ByRefEvent] +public readonly record struct BuckledEvent(Entity Strap, Entity Buckle); + +/// +/// Event raised directed at a strap entity after something has been unbuckled from it. +/// [ByRefEvent] -public record struct BuckleAttemptEvent(EntityUid StrapEntity, EntityUid BuckledEntity, EntityUid UserEntity, bool Buckling, bool Cancelled = false); +public readonly record struct UnstrappedEvent(Entity Strap, Entity Buckle); +/// +/// Event raised directed at a buckle entity after it has been unbuckled from some strap entity. +/// [ByRefEvent] -public readonly record struct BuckleChangeEvent(EntityUid StrapEntity, EntityUid BuckledEntity, bool Buckling); +public readonly record struct UnbuckledEvent(Entity Strap, Entity Buckle); [Serializable, NetSerializable] public enum BuckleVisuals diff --git a/Content.Shared/Buckle/Components/StrapComponent.cs b/Content.Shared/Buckle/Components/StrapComponent.cs index 72c92ebf84b..0fbdae693de 100644 --- a/Content.Shared/Buckle/Components/StrapComponent.cs +++ b/Content.Shared/Buckle/Components/StrapComponent.cs @@ -3,6 +3,7 @@ using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Buckle.Components; @@ -12,117 +13,83 @@ namespace Content.Shared.Buckle.Components; public sealed partial class StrapComponent : Component { /// - /// The entities that are currently buckled + /// The entities that are currently buckled to this strap. /// - [AutoNetworkedField] - [ViewVariables] // TODO serialization + [ViewVariables] public HashSet BuckledEntities = new(); /// /// Entities that this strap accepts and can buckle /// If null it accepts any entity /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public EntityWhitelist? Whitelist; /// /// Entities that this strap does not accept and cannot buckle. /// - [DataField, ViewVariables(VVAccess.ReadWrite)] + [DataField] public EntityWhitelist? Blacklist; /// /// The change in position to the strapped mob /// [DataField, AutoNetworkedField] - [ViewVariables(VVAccess.ReadWrite)] public StrapPosition Position = StrapPosition.None; - /// - /// The distance above which a buckled entity will be automatically unbuckled. - /// Don't change it unless you really have to - /// - /// - /// Dont set this below 0.2 because that causes audio issues with - /// My guess after testing is that the client sets BuckledTo to the strap in *some* ticks for some reason - /// whereas the server doesnt, thus the client tries to unbuckle like 15 times because it passes the strap null check - /// This is why this needs to be above 0.1 to make the InRange check fail in both client and server. - /// - [DataField, AutoNetworkedField] - [ViewVariables(VVAccess.ReadWrite)] - public float MaxBuckleDistance = 0.2f; - - /// - /// Gets and clamps the buckle offset to MaxBuckleDistance - /// - [ViewVariables] - public Vector2 BuckleOffsetClamped => Vector2.Clamp( - BuckleOffset, - Vector2.One * -MaxBuckleDistance, - Vector2.One * MaxBuckleDistance); - /// /// The buckled entity will be offset by this amount from the center of the strap object. - /// If this offset it too big, it will be clamped to /// [DataField, AutoNetworkedField] - [ViewVariables(VVAccess.ReadWrite)] public Vector2 BuckleOffset = Vector2.Zero; /// /// The angle to rotate the player by when they get strapped /// [DataField] - [ViewVariables(VVAccess.ReadWrite)] public Angle Rotation; /// /// The size of the strap which is compared against when buckling entities /// [DataField] - [ViewVariables(VVAccess.ReadWrite)] public int Size = 100; /// /// If disabled, nothing can be buckled on this object, and it will unbuckle anything that's already buckled /// - [ViewVariables] + [DataField, AutoNetworkedField] public bool Enabled = true; /// /// You can specify the offset the entity will have after unbuckling. /// [DataField] - [ViewVariables(VVAccess.ReadWrite)] public Vector2 UnbuckleOffset = Vector2.Zero; /// /// The sound to be played when a mob is buckled /// [DataField] - [ViewVariables(VVAccess.ReadWrite)] public SoundSpecifier BuckleSound = new SoundPathSpecifier("/Audio/Effects/buckle.ogg"); /// /// The sound to be played when a mob is unbuckled /// [DataField] - [ViewVariables(VVAccess.ReadWrite)] public SoundSpecifier UnbuckleSound = new SoundPathSpecifier("/Audio/Effects/unbuckle.ogg"); /// /// ID of the alert to show when buckled /// [DataField] - [ViewVariables(VVAccess.ReadWrite)] - public AlertType BuckledAlertType = AlertType.Buckled; + public ProtoId BuckledAlertType = "Buckled"; /// - /// The sum of the sizes of all the buckled entities in this strap + /// Whether InteractHand will buckle the user to the strap. /// - [AutoNetworkedField] - [ViewVariables] - public int OccupiedSize; + [DataField] + public bool BuckleOnInteractHand = true; } public enum StrapPosition diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs index c07d90b3a2b..97d3ed6ae03 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs @@ -1,36 +1,52 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using Content.Shared.Alert; -using Content.Shared.Bed.Sleep; using Content.Shared.Buckle.Components; using Content.Shared.Database; using Content.Shared.Hands.Components; using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Mobs.Components; using Content.Shared.Movement.Events; +using Content.Shared.Movement.Pulling.Events; using Content.Shared.Popups; +using Content.Shared.Pulling.Events; using Content.Shared.Standing; using Content.Shared.Storage.Components; using Content.Shared.Stunnable; using Content.Shared.Throwing; using Content.Shared.Verbs; +using Content.Shared.Whitelist; +using Robust.Shared.Containers; +using Robust.Shared.GameStates; +using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; -using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; namespace Content.Shared.Buckle; public abstract partial class SharedBuckleSystem { + public static ProtoId BuckledAlertCategory = "Buckled"; + + [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; + [Dependency] private readonly ILogManager _logManager = default!; + + private ISawmill _sawmill = default!; + private void InitializeBuckle() { - SubscribeLocalEvent(OnBuckleComponentStartup); + _sawmill = _logManager.GetSawmill("SharedBuckleSystem"); + SubscribeLocalEvent(OnBuckleComponentShutdown); SubscribeLocalEvent(OnBuckleMove); - SubscribeLocalEvent(OnBuckleInteractHand); - SubscribeLocalEvent>(AddUnbuckleVerb); + SubscribeLocalEvent(OnParentChanged); + SubscribeLocalEvent(OnInserted); + + SubscribeLocalEvent(OnPullAttempt); + SubscribeLocalEvent(OnBeingPulledAttempt); + SubscribeLocalEvent(OnPullStarted); + SubscribeLocalEvent(OnBuckleInsertIntoEntityStorageAttempt); SubscribeLocalEvent(OnBucklePreventCollide); @@ -38,66 +54,93 @@ private void InitializeBuckle() SubscribeLocalEvent(OnBuckleStandAttempt); SubscribeLocalEvent(OnBuckleThrowPushbackAttempt); SubscribeLocalEvent(OnBuckleUpdateCanMove); + + SubscribeLocalEvent(OnGetState); } - private void OnBuckleComponentStartup(EntityUid uid, BuckleComponent component, ComponentStartup args) + private void OnGetState(Entity ent, ref ComponentGetState args) { - UpdateBuckleStatus(uid, component); + args.State = new BuckleState(GetNetEntity(ent.Comp.BuckledTo), ent.Comp.DontCollide, ent.Comp.BuckleTime); } - private void OnBuckleComponentShutdown(EntityUid uid, BuckleComponent component, ComponentShutdown args) + private void OnBuckleComponentShutdown(Entity ent, ref ComponentShutdown args) { - TryUnbuckle(uid, uid, true, component); + Unbuckle(ent!, null); + } - component.BuckleTime = default; + #region Pulling + + private void OnPullAttempt(Entity ent, ref StartPullAttemptEvent args) + { + // Prevent people pulling the chair they're on, etc. + if (ent.Comp.BuckledTo == args.Pulled && !ent.Comp.PullStrap) + args.Cancel(); } - private void OnBuckleMove(EntityUid uid, BuckleComponent component, ref MoveEvent ev) + private void OnBeingPulledAttempt(Entity ent, ref BeingPulledAttemptEvent args) { - if (component.BuckledTo is not {} strapUid) + if (args.Cancelled || !ent.Comp.Buckled) return; - if (!TryComp(strapUid, out var strapComp)) - return; + if (!CanUnbuckle(ent!, args.Puller, false)) + args.Cancel(); + } - var strapPosition = Transform(strapUid).Coordinates; - if (ev.NewPosition.EntityId.IsValid() && ev.NewPosition.InRange(EntityManager, _transform, strapPosition, strapComp.MaxBuckleDistance)) - return; + private void OnPullStarted(Entity ent, ref PullStartedMessage args) + { + Unbuckle(ent!, args.PullerUid); + } + + #endregion + + #region Transform - TryUnbuckle(uid, uid, true, component); + private void OnParentChanged(Entity ent, ref EntParentChangedMessage args) + { + BuckleTransformCheck(ent, args.Transform); } - private void OnBuckleInteractHand(EntityUid uid, BuckleComponent component, InteractHandEvent args) + private void OnInserted(Entity ent, ref EntGotInsertedIntoContainerMessage args) { - if (!component.Buckled) - return; + BuckleTransformCheck(ent, Transform(ent)); + } - if (TryUnbuckle(uid, args.User, buckleComp: component)) - args.Handled = true; + private void OnBuckleMove(Entity ent, ref MoveEvent ev) + { + BuckleTransformCheck(ent, ev.Component); } - private void AddUnbuckleVerb(EntityUid uid, BuckleComponent component, GetVerbsEvent args) + /// + /// Check if the entity should get unbuckled as a result of transform or container changes. + /// + private void BuckleTransformCheck(Entity buckle, TransformComponent xform) { - if (!args.CanAccess || !args.CanInteract || !component.Buckled) + if (_gameTiming.ApplyingState) + return; + + if (buckle.Comp.BuckledTo is not { } strapUid) return; - InteractionVerb verb = new() + if (!TryComp(strapUid, out var strapComp)) { - Act = () => TryUnbuckle(uid, args.User, buckleComp: component), - Text = Loc.GetString("verb-categories-unbuckle"), - Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png")) - }; + Log.Error($"Encountered buckle entity {ToPrettyString(buckle)} without a valid strap entity {ToPrettyString(strapUid)}"); + SetBuckledTo(buckle, null); + return; + } - if (args.Target == args.User && args.Using == null) + if (xform.ParentUid != strapUid || _container.IsEntityInContainer(buckle)) { - // A user is left clicking themselves with an empty hand, while buckled. - // It is very likely they are trying to unbuckle themselves. - verb.Priority = 1; + Unbuckle(buckle, (strapUid, strapComp), null); + return; } - args.Verbs.Add(verb); + var delta = (xform.LocalPosition - strapComp.BuckleOffset).LengthSquared(); + if (delta > 1e-5) + Unbuckle(buckle, (strapUid, strapComp), null); } + #endregion + private void OnBuckleInsertIntoEntityStorageAttempt(EntityUid uid, BuckleComponent component, ref InsertIntoEntityStorageAttemptEvent args) { if (component.Buckled) @@ -106,10 +149,7 @@ private void OnBuckleInsertIntoEntityStorageAttempt(EntityUid uid, BuckleCompone private void OnBucklePreventCollide(EntityUid uid, BuckleComponent component, ref PreventCollideEvent args) { - if (args.OtherEntity != component.BuckledTo) - return; - - if (component.Buckled || component.DontCollide) + if (args.OtherEntity == component.BuckledTo && component.DontCollide) args.Cancelled = true; } @@ -133,10 +173,7 @@ private void OnBuckleThrowPushbackAttempt(EntityUid uid, BuckleComponent compone private void OnBuckleUpdateCanMove(EntityUid uid, BuckleComponent component, UpdateCanMoveEvent args) { - if (component.LifeStage > ComponentLifeStage.Running) - return; - - if (component.Buckled) // buckle shitcode + if (component.Buckled) args.Cancel(); } @@ -145,162 +182,146 @@ public bool IsBuckled(EntityUid uid, BuckleComponent? component = null) return Resolve(uid, ref component, false) && component.Buckled; } - /// - /// Shows or hides the buckled status effect depending on if the - /// entity is buckled or not. - /// - /// Entity that we want to show the alert - /// buckle component of the entity - /// strap component of the thing we are strapping to - private void UpdateBuckleStatus(EntityUid uid, BuckleComponent buckleComp, StrapComponent? strapComp = null) + protected void SetBuckledTo(Entity buckle, Entity? strap) { - Appearance.SetData(uid, StrapVisuals.State, buckleComp.Buckled); - if (buckleComp.BuckledTo != null) - { - if (!Resolve(buckleComp.BuckledTo.Value, ref strapComp)) - return; + if (TryComp(buckle.Comp.BuckledTo, out StrapComponent? old)) + old.BuckledEntities.Remove(buckle); - var alertType = strapComp.BuckledAlertType; - _alerts.ShowAlert(uid, alertType); + if (strap is {} strapEnt && Resolve(strapEnt.Owner, ref strapEnt.Comp)) + { + strapEnt.Comp.BuckledEntities.Add(buckle); + _alerts.ShowAlert(buckle, strapEnt.Comp.BuckledAlertType); } else { - _alerts.ClearAlertCategory(uid, AlertCategory.Buckled); + _alerts.ClearAlertCategory(buckle, BuckledAlertCategory); } - } - /// - /// Sets the field in the component to a value - /// - /// Value tat with be assigned to the field - private void SetBuckledTo(EntityUid buckleUid, EntityUid? strapUid, StrapComponent? strapComp, BuckleComponent buckleComp) - { - buckleComp.BuckledTo = strapUid; + _sawmill.Info("Trying to set BuckledTo..."); + _sawmill.Info($"BuckledTo: {ToPrettyString(buckle.Comp.BuckledTo)}"); + _sawmill.Info($"Buckled: {buckle.Comp.Buckled}"); - if (strapUid == null) - { - buckleComp.Buckled = false; - } - else - { - buckleComp.LastEntityBuckledTo = strapUid; - buckleComp.DontCollide = true; - buckleComp.Buckled = true; - buckleComp.BuckleTime = _gameTiming.CurTime; - } + buckle.Comp.BuckledTo = strap; + buckle.Comp.BuckleTime = _gameTiming.CurTime; - ActionBlocker.UpdateCanMove(buckleUid); - UpdateBuckleStatus(buckleUid, buckleComp, strapComp); - Dirty(buckleComp); + _sawmill.Info($"BuckledTo: {ToPrettyString(buckle.Comp.BuckledTo)}"); + _sawmill.Info($"Buckled: {buckle.Comp.Buckled}"); + ActionBlocker.UpdateCanMove(buckle); + Appearance.SetData(buckle, StrapVisuals.State, buckle.Comp.Buckled); + Dirty(buckle); } /// /// Checks whether or not buckling is possible /// /// Uid of the owner of BuckleComponent - /// - /// Uid of a third party entity, - /// i.e, the uid of someone else you are dragging to a chair. - /// Can equal buckleUid sometimes + /// + /// Uid of a third party entity, + /// i.e, the uid of someone else you are dragging to a chair. + /// Can equal buckleUid sometimes /// /// Uid of the owner of strap component - private bool CanBuckle( - EntityUid buckleUid, - EntityUid userUid, + /// + /// + private bool CanBuckle(EntityUid buckleUid, + EntityUid? user, EntityUid strapUid, + bool popup, [NotNullWhen(true)] out StrapComponent? strapComp, - BuckleComponent? buckleComp = null) + BuckleComponent buckleComp) { strapComp = null; - - if (userUid == strapUid || - !Resolve(buckleUid, ref buckleComp, false) || - !Resolve(strapUid, ref strapComp, false)) - { + if (!Resolve(strapUid, ref strapComp, false)) return false; - } // Does it pass the Whitelist if (strapComp.Whitelist != null && !strapComp.Whitelist.IsValid(buckleUid, EntityManager) || strapComp.Blacklist?.IsValid(buckleUid, EntityManager) == true) { - if (_netManager.IsServer) - _popup.PopupEntity(Loc.GetString("buckle-component-cannot-fit-message"), userUid, buckleUid, PopupType.Medium); + if (_netManager.IsServer && popup && user != null) + _popup.PopupEntity(Loc.GetString("buckle-component-cannot-fit-message"), user.Value, user.Value, PopupType.Medium); return false; } - // Is it within range - bool Ignored(EntityUid entity) => entity == buckleUid || entity == userUid || entity == strapUid; - - if (!_interaction.InRangeUnobstructed(buckleUid, strapUid, buckleComp.Range, predicate: Ignored, + if (!_interaction.InRangeUnobstructed(buckleUid, + strapUid, + buckleComp.Range, + predicate: entity => entity == buckleUid || entity == user || entity == strapUid, popup: true)) { return false; } - // If in a container - if (_container.TryGetContainingContainer(buckleUid, out var ownerContainer)) - { - // And not in the same container as the strap - if (!_container.TryGetContainingContainer(strapUid, out var strapContainer) || - ownerContainer != strapContainer) - { - return false; - } - } + if (!_container.IsInSameOrNoContainer((buckleUid, null, null), (strapUid, null, null))) + return false; - if (!HasComp(userUid)) + if (user != null && !HasComp(user)) { // PopupPredicted when - if (_netManager.IsServer) - _popup.PopupEntity(Loc.GetString("buckle-component-no-hands-message"), userUid, userUid); + if (_netManager.IsServer && popup) + _popup.PopupEntity(Loc.GetString("buckle-component-no-hands-message"), user.Value, user.Value); return false; } if (buckleComp.Buckled) { - var message = Loc.GetString(buckleUid == userUid + if (_netManager.IsClient || popup || user == null) + return false; + + var message = Loc.GetString(buckleUid == user ? "buckle-component-already-buckled-message" : "buckle-component-other-already-buckled-message", ("owner", Identity.Entity(buckleUid, EntityManager))); - if (_netManager.IsServer) - _popup.PopupEntity(message, userUid, userUid); + _popup.PopupEntity(message, user.Value, user.Value); return false; } + // Check whether someone is attempting to buckle something to their own child var parent = Transform(strapUid).ParentUid; while (parent.IsValid()) { - if (parent == userUid) + if (parent != buckleUid) { - var message = Loc.GetString(buckleUid == userUid - ? "buckle-component-cannot-buckle-message" - : "buckle-component-other-cannot-buckle-message", ("owner", Identity.Entity(buckleUid, EntityManager))); - if (_netManager.IsServer) - _popup.PopupEntity(message, userUid, userUid); + parent = Transform(parent).ParentUid; + continue; + } + if (_netManager.IsClient || popup || user == null) return false; - } - parent = Transform(parent).ParentUid; + var message = Loc.GetString(buckleUid == user + ? "buckle-component-cannot-buckle-message" + : "buckle-component-other-cannot-buckle-message", + ("owner", Identity.Entity(buckleUid, EntityManager))); + + _popup.PopupEntity(message, user.Value, user.Value); + return false; } if (!StrapHasSpace(strapUid, buckleComp, strapComp)) { - var message = Loc.GetString(buckleUid == userUid - ? "buckle-component-cannot-fit-message" - : "buckle-component-other-cannot-fit-message", ("owner", Identity.Entity(buckleUid, EntityManager))); - if (_netManager.IsServer) - _popup.PopupEntity(message, userUid, userUid); + if (_netManager.IsClient || popup || user == null) + return false; + + var message = Loc.GetString(buckleUid == user + ? "buckle-component-cannot-fit-message" + : "buckle-component-other-cannot-fit-message", + ("owner", Identity.Entity(buckleUid, EntityManager))); + + _popup.PopupEntity(message, user.Value, user.Value); return false; } - var attemptEvent = new BuckleAttemptEvent(strapUid, buckleUid, userUid, true); - RaiseLocalEvent(attemptEvent.BuckledEntity, ref attemptEvent); - RaiseLocalEvent(attemptEvent.StrapEntity, ref attemptEvent); - if (attemptEvent.Cancelled) + var buckleAttempt = new BuckleAttemptEvent((strapUid, strapComp), (buckleUid, buckleComp), user, popup); + RaiseLocalEvent(buckleUid, ref buckleAttempt); + if (buckleAttempt.Cancelled) + return false; + + var strapAttempt = new StrapAttemptEvent((strapUid, strapComp), (buckleUid, buckleComp), user, popup); + RaiseLocalEvent(strapUid, ref strapAttempt); + if (strapAttempt.Cancelled) return false; return true; @@ -309,217 +330,202 @@ private bool CanBuckle( /// /// Attempts to buckle an entity to a strap /// - /// Uid of the owner of BuckleComponent - /// + /// Uid of the owner of BuckleComponent + /// /// Uid of a third party entity, /// i.e, the uid of someone else you are dragging to a chair. /// Can equal buckleUid sometimes /// - /// Uid of the owner of strap component - public bool TryBuckle(EntityUid buckleUid, EntityUid userUid, EntityUid strapUid, BuckleComponent? buckleComp = null) + /// Uid of the owner of strap component + public bool TryBuckle(EntityUid buckle, EntityUid? user, EntityUid strap, BuckleComponent? buckleComp = null, bool popup = true) { - if (!Resolve(buckleUid, ref buckleComp, false)) - return false; - - if (!CanBuckle(buckleUid, userUid, strapUid, out var strapComp, buckleComp)) + _sawmill.Info("Try buckle."); + if (!Resolve(buckle, ref buckleComp, false)) + { + _sawmill.Info("Failed to resolve buckle."); return false; + } - if (!StrapTryAdd(strapUid, buckleUid, buckleComp, false, strapComp)) + if (!CanBuckle(buckle, user, strap, popup, out var strapComp, buckleComp)) { - var message = Loc.GetString(buckleUid == userUid - ? "buckle-component-cannot-buckle-message" - : "buckle-component-other-cannot-buckle-message", ("owner", Identity.Entity(buckleUid, EntityManager))); - if (_netManager.IsServer) - _popup.PopupEntity(message, userUid, userUid); + _sawmill.Info("Can't buckle."); return false; } - if (TryComp(buckleUid, out var appearance)) - Appearance.SetData(buckleUid, BuckleVisuals.Buckled, true, appearance); + Buckle((buckle, buckleComp), (strap, strapComp), user); + _sawmill.Info("Success"); + return true; + } + + private void Buckle(Entity buckle, Entity strap, EntityUid? user) + { + if (user == buckle.Owner) + _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user):player} buckled themselves to {ToPrettyString(strap)}"); + else if (user != null) + _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(user):player} buckled {ToPrettyString(buckle)} to {ToPrettyString(strap)}"); - _rotationVisuals.SetHorizontalAngle(buckleUid, strapComp.Rotation); + _audio.PlayPredicted(strap.Comp.BuckleSound, strap, user); - ReAttach(buckleUid, strapUid, buckleComp, strapComp); - SetBuckledTo(buckleUid, strapUid, strapComp, buckleComp); - // TODO user is currently set to null because if it isn't the sound fails to play in some situations, fix that - _audio.PlayPredicted(strapComp.BuckleSound, strapUid, userUid); + SetBuckledTo(buckle, strap!); + Appearance.SetData(strap, StrapVisuals.State, true); + Appearance.SetData(buckle, BuckleVisuals.Buckled, true); - var ev = new BuckleChangeEvent(strapUid, buckleUid, true); - RaiseLocalEvent(ev.BuckledEntity, ref ev); - RaiseLocalEvent(ev.StrapEntity, ref ev); + _rotationVisuals.SetHorizontalAngle(buckle.Owner, strap.Comp.Rotation); - if (TryComp(buckleUid, out var ownerPullable)) - { - if (ownerPullable.Puller != null) - { - _pulling.TryStopPull(buckleUid, ownerPullable); - } - } + var xform = Transform(buckle); + var coords = new EntityCoordinates(strap, strap.Comp.BuckleOffset); + _transform.SetCoordinates(buckle, xform, coords, rotation: Angle.Zero); - if (TryComp(buckleUid, out var physics)) - { - _physics.ResetDynamics(buckleUid, physics); - } + _joints.SetRelay(buckle, strap); - if (!buckleComp.PullStrap && TryComp(strapUid, out var toPullable)) + switch (strap.Comp.Position) { - if (toPullable.Puller == buckleUid) - { - // can't pull it and buckle to it at the same time - _pulling.TryStopPull(strapUid, toPullable); - } + case StrapPosition.Stand: + _standing.Stand(buckle, force: true); + break; + case StrapPosition.Down: + _standing.Down(buckle, false, false, force: true); + break; } - // Logging - if (userUid != buckleUid) - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(userUid):player} buckled {ToPrettyString(buckleUid)} to {ToPrettyString(strapUid)}"); - else - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(userUid):player} buckled themselves to {ToPrettyString(strapUid)}"); + var ev = new StrappedEvent(strap, buckle); + RaiseLocalEvent(strap, ref ev); - return true; + var gotEv = new BuckledEvent(strap, buckle); + RaiseLocalEvent(buckle, ref gotEv); + + if (TryComp(buckle, out var physics)) + _physics.ResetDynamics(buckle, physics); + + DebugTools.AssertEqual(xform.ParentUid, strap.Owner); } /// /// Tries to unbuckle the Owner of this component from its current strap. /// /// The entity to unbuckle. - /// The entity doing the unbuckling. - /// - /// Whether to force the unbuckling or not. Does not guarantee true to - /// be returned, but guarantees the owner to be unbuckled afterwards. - /// + /// The entity doing the unbuckling. /// The buckle component of the entity to unbuckle. /// /// true if the owner was unbuckled, otherwise false even if the owner /// was previously already unbuckled. /// - public bool TryUnbuckle(EntityUid buckleUid, EntityUid userUid, bool force = false, BuckleComponent? buckleComp = null) + public bool TryUnbuckle(EntityUid buckleUid, + EntityUid? user, + BuckleComponent? buckleComp = null, + bool popup = true) { - if (!Resolve(buckleUid, ref buckleComp, false) || - buckleComp.BuckledTo is not { } strapUid) + return TryUnbuckle((buckleUid, buckleComp), user, popup); + } + + public bool TryUnbuckle(Entity buckle, EntityUid? user, bool popup) + { + if (!Resolve(buckle.Owner, ref buckle.Comp)) return false; - if (!force) - { - var attemptEvent = new BuckleAttemptEvent(strapUid, buckleUid, userUid, false); - RaiseLocalEvent(attemptEvent.BuckledEntity, ref attemptEvent); - RaiseLocalEvent(attemptEvent.StrapEntity, ref attemptEvent); - if (attemptEvent.Cancelled) - return false; + if (!CanUnbuckle(buckle, user, popup, out var strap)) + return false; - if (_gameTiming.CurTime < buckleComp.BuckleTime + buckleComp.Delay) - return false; + Unbuckle(buckle!, strap, user); + return true; + } - if (!_interaction.InRangeUnobstructed(userUid, strapUid, buckleComp.Range, popup: true)) - return false; + public void Unbuckle(Entity buckle, EntityUid? user) + { + if (!Resolve(buckle.Owner, ref buckle.Comp, false)) + return; - if (HasComp(buckleUid) && buckleUid == userUid) - return false; + if (buckle.Comp.BuckledTo is not { } strap) + return; - // If the person is crit or dead in any kind of strap, return. This prevents people from unbuckling themselves while incapacitated. - if (_mobState.IsIncapacitated(buckleUid) && userUid == buckleUid) - return false; + if (!TryComp(strap, out StrapComponent? strapComp)) + { + Log.Error($"Encountered buckle {ToPrettyString(buckle.Owner)} with invalid strap entity {ToPrettyString(strap)}"); + SetBuckledTo(buckle!, null); + return; } - // Logging - if (userUid != buckleUid) - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(userUid):player} unbuckled {ToPrettyString(buckleUid)} from {ToPrettyString(strapUid)}"); - else - _adminLogger.Add(LogType.Action, LogImpact.Low, $"{ToPrettyString(userUid):player} unbuckled themselves from {ToPrettyString(strapUid)}"); + Unbuckle(buckle!, (strap, strapComp), user); + } - SetBuckledTo(buckleUid, null, null, buckleComp); + private void Unbuckle(Entity buckle, Entity strap, EntityUid? user) + { + if (user == buckle.Owner) + _adminLogger.Add(LogType.Action, LogImpact.Low, $"{user} unbuckled themselves from {strap}"); + else if (user != null) + _adminLogger.Add(LogType.Action, LogImpact.Low, $"{user} unbuckled {buckle} from {strap}"); - if (!TryComp(strapUid, out var strapComp)) - return false; + _audio.PlayPredicted(strap.Comp.UnbuckleSound, strap, user); - var buckleXform = Transform(buckleUid); - var oldBuckledXform = Transform(strapUid); + SetBuckledTo(buckle, null); - if (buckleXform.ParentUid == strapUid && !Terminating(buckleXform.ParentUid)) + var buckleXform = Transform(buckle); + var oldBuckledXform = Transform(strap); + + if (buckleXform.ParentUid == strap.Owner && !Terminating(buckleXform.ParentUid)) { - _container.AttachParentToContainerOrGrid((buckleUid, buckleXform)); + _container.AttachParentToContainerOrGrid((buckle, buckleXform)); - var oldBuckledToWorldRot = _transform.GetWorldRotation(strapUid); + var oldBuckledToWorldRot = _transform.GetWorldRotation(strap); _transform.SetWorldRotation(buckleXform, oldBuckledToWorldRot); - if (strapComp.UnbuckleOffset != Vector2.Zero) - buckleXform.Coordinates = oldBuckledXform.Coordinates.Offset(strapComp.UnbuckleOffset); + if (strap.Comp.UnbuckleOffset != Vector2.Zero) + buckleXform.Coordinates = oldBuckledXform.Coordinates.Offset(strap.Comp.UnbuckleOffset); } - if (TryComp(buckleUid, out AppearanceComponent? appearance)) - Appearance.SetData(buckleUid, BuckleVisuals.Buckled, false, appearance); - _rotationVisuals.ResetHorizontalAngle(buckleUid); + _rotationVisuals.ResetHorizontalAngle(buckle.Owner); + Appearance.SetData(strap, StrapVisuals.State, strap.Comp.BuckledEntities.Count != 0); + Appearance.SetData(buckle, BuckleVisuals.Buckled, false); - if (TryComp(buckleUid, out var mobState) - && _mobState.IsIncapacitated(buckleUid, mobState) - || HasComp(buckleUid)) - { - _standing.Down(buckleUid); - } + if (HasComp(buckle) || _mobState.IsIncapacitated(buckle)) + _standing.Down(buckle, playSound: false); else - { - _standing.Stand(buckleUid); - } + _standing.Stand(buckle); - if (_mobState.IsIncapacitated(buckleUid, mobState)) - { - _standing.Down(buckleUid); - } - if (strapComp.BuckledEntities.Remove(buckleUid)) - { - strapComp.OccupiedSize -= buckleComp.Size; - //Dirty(strapUid); - Dirty(strapComp); - } + _joints.RefreshRelay(buckle); - _joints.RefreshRelay(buckleUid); - Appearance.SetData(strapUid, StrapVisuals.State, strapComp.BuckledEntities.Count != 0); + var buckleEv = new UnbuckledEvent(strap, buckle); + RaiseLocalEvent(buckle, ref buckleEv); - // TODO: Buckle listening to moveevents is sussy anyway. - if (!TerminatingOrDeleted(strapUid)) - _audio.PlayPredicted(strapComp.UnbuckleSound, strapUid, userUid); - - var ev = new BuckleChangeEvent(strapUid, buckleUid, false); - RaiseLocalEvent(buckleUid, ref ev); - RaiseLocalEvent(strapUid, ref ev); + var strapEv = new UnstrappedEvent(strap, buckle); + RaiseLocalEvent(strap, ref strapEv); + } - return true; + public bool CanUnbuckle(Entity buckle, EntityUid user, bool popup) + { + return CanUnbuckle(buckle, user, popup, out _); } - /// - /// Makes an entity toggle the buckling status of the owner to a - /// specific entity. - /// - /// The entity to buckle/unbuckle from . - /// The entity doing the buckling/unbuckling. - /// - /// The entity to toggle the buckle status of the owner to. - /// - /// - /// Whether to force the unbuckling or not, if it happens. Does not - /// guarantee true to be returned, but guarantees the owner to be - /// unbuckled afterwards. - /// - /// The buckle component of the entity to buckle/unbuckle from . - /// true if the buckling status was changed, false otherwise. - public bool ToggleBuckle( - EntityUid buckleUid, - EntityUid userUid, - EntityUid strapUid, - bool force = false, - BuckleComponent? buckle = null) + private bool CanUnbuckle(Entity buckle, EntityUid? user, bool popup, out Entity strap) { - if (!Resolve(buckleUid, ref buckle, false)) + strap = default; + if (!Resolve(buckle.Owner, ref buckle.Comp)) return false; - if (!buckle.Buckled) - { - return TryBuckle(buckleUid, userUid, strapUid, buckle); - } - else + if (buckle.Comp.BuckledTo is not { } strapUid) + return false; + + if (!TryComp(strapUid, out StrapComponent? strapComp)) { - return TryUnbuckle(buckleUid, userUid, force, buckle); + Log.Error($"Encountered buckle {ToPrettyString(buckle.Owner)} with invalid strap entity {ToPrettyString(strap)}"); + SetBuckledTo(buckle!, null); + return false; } + strap = (strapUid, strapComp); + if (_gameTiming.CurTime < buckle.Comp.BuckleTime + buckle.Comp.Delay) + return false; + + if (user != null && !_interaction.InRangeUnobstructed(user.Value, strap.Owner, buckle.Comp.Range, popup: popup)) + return false; + + var unbuckleAttempt = new UnbuckleAttemptEvent(strap, buckle!, user, popup); + RaiseLocalEvent(buckle, ref unbuckleAttempt); + if (unbuckleAttempt.Cancelled) + return false; + + var unstrapAttempt = new UnstrapAttemptEvent(strap, buckle!, user, popup); + RaiseLocalEvent(strap, ref unstrapAttempt); + return !unstrapAttempt.Cancelled; } } diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Interaction.cs b/Content.Shared/Buckle/SharedBuckleSystem.Interaction.cs new file mode 100644 index 00000000000..59eff1f8c87 --- /dev/null +++ b/Content.Shared/Buckle/SharedBuckleSystem.Interaction.cs @@ -0,0 +1,200 @@ +using System.Linq; +using Content.Shared.Buckle.Components; +using Content.Shared.DoAfter; +using Content.Shared.DragDrop; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Verbs; +using Robust.Shared.Utility; + +namespace Content.Shared.Buckle; + +// Partial class containing interaction & verb event handlers +public abstract partial class SharedBuckleSystem +{ + private void InitializeInteraction() + { + SubscribeLocalEvent>(AddStrapVerbs); + SubscribeLocalEvent(OnStrapInteractHand); + SubscribeLocalEvent(OnStrapDragDropTarget); + SubscribeLocalEvent(OnCanDropTarget); + + SubscribeLocalEvent>(AddUnbuckleVerb); + } + + private void OnCanDropTarget(EntityUid uid, StrapComponent component, ref CanDropTargetEvent args) + { + args.CanDrop = StrapCanDragDropOn(uid, args.User, uid, args.Dragged, component); + args.Handled = true; + } + + private void OnStrapDragDropTarget(EntityUid uid, StrapComponent component, ref DragDropTargetEvent args) + { + if (!StrapCanDragDropOn(uid, args.User, uid, args.Dragged, component)) + return; + + args.Handled = TryBuckle(args.Dragged, args.User, uid, popup: false); + } + + private bool StrapCanDragDropOn( + EntityUid strapUid, + EntityUid userUid, + EntityUid targetUid, + EntityUid buckleUid, + StrapComponent? strapComp = null, + BuckleComponent? buckleComp = null) + { + if (!Resolve(strapUid, ref strapComp, false) || + !Resolve(buckleUid, ref buckleComp, false)) + { + return false; + } + + bool Ignored(EntityUid entity) => entity == userUid || entity == buckleUid || entity == targetUid; + + return _interaction.InRangeUnobstructed(targetUid, buckleUid, buckleComp.Range, predicate: Ignored); + } + + private void OnStrapInteractHand(EntityUid uid, StrapComponent component, InteractHandEvent args) + { + if (args.Handled) + return; + + if (!TryComp(args.User, out BuckleComponent? buckle)) + return; + + // Buckle self + if (buckle.BuckledTo == null && component.BuckleOnInteractHand && StrapHasSpace(uid, buckle, component)) + { + TryBuckle(args.User, args.User, uid, buckle, popup: true); + args.Handled = true; + return; + } + + // Unbuckle self + if (buckle.BuckledTo == uid && TryUnbuckle(args.User, args.User, buckle, popup: true)) + { + args.Handled = true; + return; + } + + // Unbuckle others + if (component.BuckledEntities.TryFirstOrNull(out var buckled) && TryUnbuckle(buckled.Value, args.User)) + { + args.Handled = true; + return; + } + + // TODO BUCKLE add out bool for whether a pop-up was generated or not. + } + + private void OnBuckleInteractHand(Entity ent, ref InteractHandEvent args) + { + if (args.Handled) + return; + + if (ent.Comp.BuckledTo != null) + TryUnbuckle(ent!, args.User, popup: true); + + // TODO BUCKLE add out bool for whether a pop-up was generated or not. + args.Handled = true; + } + + private void AddStrapVerbs(EntityUid uid, StrapComponent component, GetVerbsEvent args) + { + if (args.Hands == null || !args.CanAccess || !args.CanInteract || !component.Enabled) + return; + + // Note that for whatever bloody reason, buckle component has its own interaction range. Additionally, this + // range can be set per-component, so we have to check a modified InRangeUnobstructed for every verb. + + // Add unstrap verbs for every strapped entity. + foreach (var entity in component.BuckledEntities) + { + var buckledComp = Comp(entity); + + if (!_interaction.InRangeUnobstructed(args.User, args.Target, range: buckledComp.Range)) + continue; + + var verb = new InteractionVerb() + { + Act = () => TryUnbuckle(entity, args.User, buckleComp: buckledComp), + Category = VerbCategory.Unbuckle, + Text = entity == args.User + ? Loc.GetString("verb-self-target-pronoun") + : Identity.Name(entity, EntityManager) + }; + + // In the event that you have more than once entity with the same name strapped to the same object, + // these two verbs will be identical according to Verb.CompareTo, and only one with actually be added to + // the verb list. However this should rarely ever be a problem. If it ever is, it could be fixed by + // appending an integer to verb.Text to distinguish the verbs. + + args.Verbs.Add(verb); + } + + // Add a verb to buckle the user. + if (TryComp(args.User, out var buckle) && + buckle.BuckledTo != uid && + args.User != uid && + StrapHasSpace(uid, buckle, component) && + _interaction.InRangeUnobstructed(args.User, args.Target, range: buckle.Range)) + { + InteractionVerb verb = new() + { + Act = () => TryBuckle(args.User, args.User, args.Target, buckle), + Category = VerbCategory.Buckle, + Text = Loc.GetString("verb-self-target-pronoun") + }; + args.Verbs.Add(verb); + } + + // If the user is currently holding/pulling an entity that can be buckled, add a verb for that. + if (args.Using is { Valid: true } @using && + TryComp(@using, out var usingBuckle) && + StrapHasSpace(uid, usingBuckle, component) && + _interaction.InRangeUnobstructed(@using, args.Target, range: usingBuckle.Range)) + { + // Check that the entity is unobstructed from the target (ignoring the user). + bool Ignored(EntityUid entity) => entity == args.User || entity == args.Target || entity == @using; + if (!_interaction.InRangeUnobstructed(@using, args.Target, usingBuckle.Range, predicate: Ignored)) + return; + + var isPlayer = _playerManager.TryGetSessionByEntity(@using, out var _); + InteractionVerb verb = new() + { + Act = () => TryBuckle(@using, args.User, args.Target, usingBuckle), + Category = VerbCategory.Buckle, + Text = Identity.Name(@using, EntityManager), + // just a held object, the user is probably just trying to sit down. + // If the used entity is a person being pulled, prioritize this verb. Conversely, if it is + Priority = isPlayer ? 1 : -1 + }; + + args.Verbs.Add(verb); + } + } + + private void AddUnbuckleVerb(EntityUid uid, BuckleComponent component, GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract || !component.Buckled) + return; + + InteractionVerb verb = new() + { + Act = () => TryUnbuckle(uid, args.User, buckleComp: component), + Text = Loc.GetString("verb-categories-unbuckle"), + Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/unbuckle.svg.192dpi.png")) + }; + + if (args.Target == args.User && args.Using == null) + { + // A user is left clicking themselves with an empty hand, while buckled. + // It is very likely they are trying to unbuckle themselves. + verb.Priority = 1; + } + + args.Verbs.Add(verb); + } + +} diff --git a/Content.Shared/Buckle/SharedBuckleSystem.Strap.cs b/Content.Shared/Buckle/SharedBuckleSystem.Strap.cs index 7be54360741..eb23aa973b4 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.Strap.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.Strap.cs @@ -2,40 +2,26 @@ using Content.Shared.Buckle.Components; using Content.Shared.Construction; using Content.Shared.Destructible; -using Content.Shared.DragDrop; using Content.Shared.Foldable; -using Content.Shared.Interaction; -using Content.Shared.Rotation; using Content.Shared.Storage; -using Content.Shared.Verbs; using Robust.Shared.Containers; namespace Content.Shared.Buckle; public abstract partial class SharedBuckleSystem { - [Dependency] private readonly SharedRotationVisualsSystem _rotationVisuals = default!; - private void InitializeStrap() { SubscribeLocalEvent(OnStrapStartup); SubscribeLocalEvent(OnStrapShutdown); - SubscribeLocalEvent((_, c, _) => StrapRemoveAll(c)); + SubscribeLocalEvent((e, c, _) => StrapRemoveAll(e, c)); - SubscribeLocalEvent(OnStrapEntModifiedFromContainer); - SubscribeLocalEvent(OnStrapEntModifiedFromContainer); - SubscribeLocalEvent>(AddStrapVerbs); SubscribeLocalEvent(OnStrapContainerGettingInsertedAttempt); - SubscribeLocalEvent(OnStrapInteractHand); - SubscribeLocalEvent((_,c,_) => StrapRemoveAll(c)); - SubscribeLocalEvent((_, c, _) => StrapRemoveAll(c)); + SubscribeLocalEvent((e, c, _) => StrapRemoveAll(e, c)); + SubscribeLocalEvent((e, c, _) => StrapRemoveAll(e, c)); - SubscribeLocalEvent(OnStrapDragDropTarget); - SubscribeLocalEvent(OnCanDropTarget); SubscribeLocalEvent(OnAttemptFold); - - SubscribeLocalEvent(OnStrapMoveEvent); - SubscribeLocalEvent((_, c, _) => StrapRemoveAll(c)); + SubscribeLocalEvent((e, c, _) => StrapRemoveAll(e, c)); } private void OnStrapStartup(EntityUid uid, StrapComponent component, ComponentStartup args) @@ -45,145 +31,17 @@ private void OnStrapStartup(EntityUid uid, StrapComponent component, ComponentSt private void OnStrapShutdown(EntityUid uid, StrapComponent component, ComponentShutdown args) { - if (LifeStage(uid) > EntityLifeStage.MapInitialized) - return; - - StrapRemoveAll(component); - } - - private void OnStrapEntModifiedFromContainer(EntityUid uid, StrapComponent component, ContainerModifiedMessage message) - { - if (_gameTiming.ApplyingState) - return; - - foreach (var buckledEntity in component.BuckledEntities) - { - if (!TryComp(buckledEntity, out var buckleComp)) - { - continue; - } - - ContainerModifiedReAttach(buckledEntity, uid, buckleComp, component); - } - } - - private void ContainerModifiedReAttach(EntityUid buckleUid, EntityUid strapUid, BuckleComponent? buckleComp = null, StrapComponent? strapComp = null) - { - if (!Resolve(buckleUid, ref buckleComp, false) || - !Resolve(strapUid, ref strapComp, false)) - return; - - var contained = _container.TryGetContainingContainer(buckleUid, out var ownContainer); - var strapContained = _container.TryGetContainingContainer(strapUid, out var strapContainer); - - if (contained != strapContained || ownContainer != strapContainer) - { - TryUnbuckle(buckleUid, buckleUid, true, buckleComp); - return; - } - - if (!contained) - { - ReAttach(buckleUid, strapUid, buckleComp, strapComp); - } + if (!TerminatingOrDeleted(uid)) + StrapRemoveAll(uid, component); } private void OnStrapContainerGettingInsertedAttempt(EntityUid uid, StrapComponent component, ContainerGettingInsertedAttemptEvent args) { // If someone is attempting to put this item inside of a backpack, ensure that it has no entities strapped to it. - if (HasComp(args.Container.Owner) && component.BuckledEntities.Count != 0) + if (args.Container.ID == StorageComponent.ContainerId && component.BuckledEntities.Count != 0) args.Cancel(); } - private void OnStrapInteractHand(EntityUid uid, StrapComponent component, InteractHandEvent args) - { - if (args.Handled) - return; - - args.Handled = ToggleBuckle(args.User, args.User, uid); - } - - private void AddStrapVerbs(EntityUid uid, StrapComponent component, GetVerbsEvent args) - { - if (args.Hands == null || !args.CanAccess || !args.CanInteract || !component.Enabled) - return; - - // Note that for whatever bloody reason, buckle component has its own interaction range. Additionally, this - // range can be set per-component, so we have to check a modified InRangeUnobstructed for every verb. - - // Add unstrap verbs for every strapped entity. - foreach (var entity in component.BuckledEntities) - { - var buckledComp = Comp(entity); - - if (!_interaction.InRangeUnobstructed(args.User, args.Target, range: buckledComp.Range)) - continue; - - var verb = new InteractionVerb() - { - Act = () => TryUnbuckle(entity, args.User, buckleComp: buckledComp), - Category = VerbCategory.Unbuckle, - Text = entity == args.User - ? Loc.GetString("verb-self-target-pronoun") - : Comp(entity).EntityName - }; - - // In the event that you have more than once entity with the same name strapped to the same object, - // these two verbs will be identical according to Verb.CompareTo, and only one with actually be added to - // the verb list. However this should rarely ever be a problem. If it ever is, it could be fixed by - // appending an integer to verb.Text to distinguish the verbs. - - args.Verbs.Add(verb); - } - - // Add a verb to buckle the user. - if (TryComp(args.User, out var buckle) && - buckle.BuckledTo != uid && - args.User != uid && - StrapHasSpace(uid, buckle, component) && - _interaction.InRangeUnobstructed(args.User, args.Target, range: buckle.Range)) - { - InteractionVerb verb = new() - { - Act = () => TryBuckle(args.User, args.User, args.Target, buckle), - Category = VerbCategory.Buckle, - Text = Loc.GetString("verb-self-target-pronoun") - }; - args.Verbs.Add(verb); - } - - // If the user is currently holding/pulling an entity that can be buckled, add a verb for that. - if (args.Using is {Valid: true} @using && - TryComp(@using, out var usingBuckle) && - StrapHasSpace(uid, usingBuckle, component) && - _interaction.InRangeUnobstructed(@using, args.Target, range: usingBuckle.Range)) - { - // Check that the entity is unobstructed from the target (ignoring the user). - bool Ignored(EntityUid entity) => entity == args.User || entity == args.Target || entity == @using; - if (!_interaction.InRangeUnobstructed(@using, args.Target, usingBuckle.Range, predicate: Ignored)) - return; - - var isPlayer = _playerManager.TryGetSessionByEntity(@using, out var _); - InteractionVerb verb = new() - { - Act = () => TryBuckle(@using, args.User, args.Target, usingBuckle), - Category = VerbCategory.Buckle, - Text = Comp(@using).EntityName, - // just a held object, the user is probably just trying to sit down. - // If the used entity is a person being pulled, prioritize this verb. Conversely, if it is - Priority = isPlayer ? 1 : -1 - }; - - args.Verbs.Add(verb); - } - } - - private void OnCanDropTarget(EntityUid uid, StrapComponent component, ref CanDropTargetEvent args) - { - args.CanDrop = StrapCanDragDropOn(uid, args.User, uid, args.Dragged, component); - args.Handled = true; - } - private void OnAttemptFold(EntityUid uid, StrapComponent component, ref FoldAttemptEvent args) { if (args.Cancelled) @@ -192,82 +50,15 @@ private void OnAttemptFold(EntityUid uid, StrapComponent component, ref FoldAtte args.Cancelled = component.BuckledEntities.Count != 0; } - private void OnStrapDragDropTarget(EntityUid uid, StrapComponent component, ref DragDropTargetEvent args) - { - if (!StrapCanDragDropOn(uid, args.User, uid, args.Dragged, component)) - return; - - args.Handled = TryBuckle(args.Dragged, args.User, uid); - } - - private void OnStrapMoveEvent(EntityUid uid, StrapComponent component, ref MoveEvent args) - { - // TODO: This looks dirty af. - // On rotation of a strap, reattach all buckled entities. - // This fixes buckle offsets and draw depths. - // This is mega cursed. Please somebody save me from Mr Buckle's wild ride. - // Oh god I'm back here again. Send help. - - // Consider a chair that has a player strapped to it. Then the client receives a new server state, showing - // that the player entity has moved elsewhere, and the chair has rotated. If the client applies the player - // state, then the chairs transform comp state, and then the buckle state. The transform state will - // forcefully teleport the player back to the chair (client-side only). This causes even more issues if the - // chair was teleporting in from nullspace after having left PVS. - // - // One option is to just never trigger re-buckles during state application. - // another is to.. just not do this? Like wtf is this code. But I CBF with buckle atm. - - if (_gameTiming.ApplyingState || args.NewRotation == args.OldRotation) - return; - - foreach (var buckledEntity in component.BuckledEntities) - { - if (!TryComp(buckledEntity, out var buckled)) - continue; - - if (!buckled.Buckled || buckled.LastEntityBuckledTo != uid) - { - Log.Error($"A moving strap entity {ToPrettyString(uid)} attempted to re-parent an entity that does not 'belong' to it {ToPrettyString(buckledEntity)}"); - continue; - } - - ReAttach(buckledEntity, uid, buckled, component); - Dirty(buckled); - } - } - - private bool StrapCanDragDropOn( - EntityUid strapUid, - EntityUid userUid, - EntityUid targetUid, - EntityUid buckleUid, - StrapComponent? strapComp = null, - BuckleComponent? buckleComp = null) - { - if (!Resolve(strapUid, ref strapComp, false) || - !Resolve(buckleUid, ref buckleComp, false)) - { - return false; - } - - bool Ignored(EntityUid entity) => entity == userUid || entity == buckleUid || entity == targetUid; - - return _interaction.InRangeUnobstructed(targetUid, buckleUid, buckleComp.Range, predicate: Ignored); - } - /// /// Remove everything attached to the strap /// - private void StrapRemoveAll(StrapComponent strapComp) + private void StrapRemoveAll(EntityUid uid, StrapComponent strapComp) { foreach (var entity in strapComp.BuckledEntities.ToArray()) { TryUnbuckle(entity, entity, true); } - - strapComp.BuckledEntities.Clear(); - strapComp.OccupiedSize = 0; - Dirty(strapComp); } private bool StrapHasSpace(EntityUid strapUid, BuckleComponent buckleComp, StrapComponent? strapComp = null) @@ -275,30 +66,13 @@ private bool StrapHasSpace(EntityUid strapUid, BuckleComponent buckleComp, Strap if (!Resolve(strapUid, ref strapComp, false)) return false; - return strapComp.OccupiedSize + buckleComp.Size <= strapComp.Size; - } - - /// - /// Try to add an entity to the strap - /// - private bool StrapTryAdd(EntityUid strapUid, EntityUid buckleUid, BuckleComponent buckleComp, bool force = false, StrapComponent? strapComp = null) - { - if (!Resolve(strapUid, ref strapComp, false) || - !strapComp.Enabled) - return false; - - if (!force && !StrapHasSpace(strapUid, buckleComp, strapComp)) - return false; - - if (!strapComp.BuckledEntities.Add(buckleUid)) - return false; - - strapComp.OccupiedSize += buckleComp.Size; - - Appearance.SetData(strapUid, StrapVisuals.State, true); + var avail = strapComp.Size; + foreach (var buckle in strapComp.BuckledEntities) + { + avail -= CompOrNull(buckle)?.Size ?? 0; + } - Dirty(strapUid, strapComp); - return true; + return avail >= buckleComp.Size; } /// @@ -311,8 +85,9 @@ public void StrapSetEnabled(EntityUid strapUid, bool enabled, StrapComponent? st return; strapComp.Enabled = enabled; + Dirty(strapUid, strapComp); if (!enabled) - StrapRemoveAll(strapComp); + StrapRemoveAll(strapUid, strapComp); } } diff --git a/Content.Shared/Buckle/SharedBuckleSystem.cs b/Content.Shared/Buckle/SharedBuckleSystem.cs index 67218657e52..770fababded 100644 --- a/Content.Shared/Buckle/SharedBuckleSystem.cs +++ b/Content.Shared/Buckle/SharedBuckleSystem.cs @@ -1,21 +1,17 @@ using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; using Content.Shared.Alert; -using Content.Shared.Buckle.Components; using Content.Shared.Interaction; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; -using Content.Shared.Pulling; +using Content.Shared.Rotation; using Content.Shared.Standing; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; -using Robust.Shared.Map; using Robust.Shared.Network; using Robust.Shared.Physics.Systems; using Robust.Shared.Player; using Robust.Shared.Timing; -using PullingSystem = Content.Shared.Movement.Pulling.Systems.PullingSystem; namespace Content.Shared.Buckle; @@ -36,10 +32,10 @@ public abstract partial class SharedBuckleSystem : EntitySystem [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedJointSystem _joints = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly PullingSystem _pulling = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedRotationVisualsSystem _rotationVisuals = default!; /// public override void Initialize() @@ -51,45 +47,6 @@ public override void Initialize() InitializeBuckle(); InitializeStrap(); - } - - /// - /// Reattaches this entity to the strap, modifying its position and rotation. - /// - /// The entity to reattach. - /// The entity to reattach the buckleUid entity to. - private void ReAttach( - EntityUid buckleUid, - EntityUid strapUid, - BuckleComponent? buckleComp = null, - StrapComponent? strapComp = null) - { - if (!Resolve(strapUid, ref strapComp, false) - || !Resolve(buckleUid, ref buckleComp, false)) - return; - - _transform.SetCoordinates(buckleUid, new EntityCoordinates(strapUid, strapComp.BuckleOffsetClamped)); - - var buckleTransform = Transform(buckleUid); - - // Buckle subscribes to move for so this might fail. - // TODO: Make buckle not do that. - if (buckleTransform.ParentUid != strapUid) - return; - - _transform.SetLocalRotation(buckleUid, Angle.Zero, buckleTransform); - _joints.SetRelay(buckleUid, strapUid); - - switch (strapComp.Position) - { - case StrapPosition.None: - break; - case StrapPosition.Stand: - _standing.Stand(buckleUid); - break; - case StrapPosition.Down: - _standing.Down(buckleUid, false, false); - break; - } + InitializeInteraction(); } } diff --git a/Content.Shared/Chapel/SharedSacrificialAltarSystem.cs b/Content.Shared/Chapel/SharedSacrificialAltarSystem.cs index 92df7e0f6bf..61e9a68817c 100644 --- a/Content.Shared/Chapel/SharedSacrificialAltarSystem.cs +++ b/Content.Shared/Chapel/SharedSacrificialAltarSystem.cs @@ -15,7 +15,7 @@ public override void Initialize() base.Initialize(); SubscribeLocalEvent(OnExamined); - SubscribeLocalEvent(OnUnstrapped); + SubscribeLocalEvent(OnUnstrapped); SubscribeLocalEvent>(OnGetVerbs); } @@ -24,7 +24,7 @@ private void OnExamined(Entity ent, ref ExaminedEvent args.PushMarkup(Loc.GetString("altar-examine")); } - private void OnUnstrapped(Entity ent, ref BuckleChangeEvent args) + private void OnUnstrapped(Entity ent, ref UnbuckledEvent args) { if (ent.Comp.DoAfter is not { } id) return; diff --git a/Content.Shared/Climbing/Systems/ClimbSystem.cs b/Content.Shared/Climbing/Systems/ClimbSystem.cs index c570a821a6f..d2b5a25aee7 100644 --- a/Content.Shared/Climbing/Systems/ClimbSystem.cs +++ b/Content.Shared/Climbing/Systems/ClimbSystem.cs @@ -59,7 +59,7 @@ public override void Initialize() SubscribeLocalEvent(OnParentChange); SubscribeLocalEvent(OnDoAfter); SubscribeLocalEvent(OnClimbEndCollide); - SubscribeLocalEvent(OnBuckleChange); + SubscribeLocalEvent(OnBuckled); SubscribeLocalEvent(OnCanDragDropOn); SubscribeLocalEvent>(AddClimbableVerb); @@ -479,10 +479,8 @@ public void ForciblyStopClimbing(EntityUid uid, ClimbingComponent? climbing = nu StopClimb(uid, climbing, fixtures); } - private void OnBuckleChange(EntityUid uid, ClimbingComponent component, ref BuckleChangeEvent args) + private void OnBuckled(EntityUid uid, ClimbingComponent component, ref BuckledEvent args) { - if (!args.Buckling) - return; StopClimb(uid, component); } diff --git a/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs b/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs index 7ee0676f9ef..ebc92e8aaf5 100644 --- a/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs +++ b/Content.Shared/Clothing/Loadouts/Systems/LoadoutSystem.cs @@ -21,6 +21,7 @@ public sealed class LoadoutSystem : EntitySystem [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly IConfigurationManager _configuration = default!; [Dependency] private readonly CharacterRequirementsSystem _characterRequirements = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; public override void Initialize() { @@ -79,7 +80,7 @@ public List ApplyCharacterLoadout(EntityUid uid, JobPrototype job, Hu // Spawn the loadout items var spawned = EntityManager.SpawnEntities( - EntityManager.GetComponent(uid).Coordinates.ToMap(EntityManager), + _transformSystem.GetMapCoordinates(uid), loadoutProto.Items.Select(p => (string?) p.ToString()).ToList()); // Dumb cast foreach (var item in spawned) diff --git a/Content.Shared/Clothing/MagbootsComponent.cs b/Content.Shared/Clothing/MagbootsComponent.cs index 0d0d59f89f5..0d074ff38b6 100644 --- a/Content.Shared/Clothing/MagbootsComponent.cs +++ b/Content.Shared/Clothing/MagbootsComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Alert; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; @@ -16,4 +17,7 @@ public sealed partial class MagbootsComponent : Component [DataField("on"), AutoNetworkedField] public bool On; + + [DataField] + public ProtoId MagbootsAlert = "Magboots"; } diff --git a/Content.Shared/CombatMode/Pacification/PacificationSystem.cs b/Content.Shared/CombatMode/Pacification/PacificationSystem.cs index 6d94c087af6..a927e1a6970 100644 --- a/Content.Shared/CombatMode/Pacification/PacificationSystem.cs +++ b/Content.Shared/CombatMode/Pacification/PacificationSystem.cs @@ -7,7 +7,6 @@ using Content.Shared.Popups; using Content.Shared.Throwing; using Content.Shared.Weapons.Ranged.Events; -using Content.Shared.Weapons.Ranged.Systems; using Robust.Shared.Timing; namespace Content.Shared.CombatMode.Pacification; @@ -109,7 +108,7 @@ private void OnStartup(EntityUid uid, PacifiedComponent component, ComponentStar _actionsSystem.SetEnabled(combatMode.CombatToggleActionEntity, false); } - _alertsSystem.ShowAlert(uid, AlertType.Pacified); + _alertsSystem.ShowAlert(uid, component.PacifiedAlert); } private void OnShutdown(EntityUid uid, PacifiedComponent component, ComponentShutdown args) @@ -121,7 +120,7 @@ private void OnShutdown(EntityUid uid, PacifiedComponent component, ComponentShu _combatSystem.SetCanDisarm(uid, true, combatMode); _actionsSystem.SetEnabled(combatMode.CombatToggleActionEntity, true); - _alertsSystem.ClearAlert(uid, AlertType.Pacified); + _alertsSystem.ClearAlert(uid, component.PacifiedAlert); } private void OnBeforeThrow(Entity ent, ref BeforeThrowEvent args) diff --git a/Content.Shared/CombatMode/Pacification/PacifiedComponent.cs b/Content.Shared/CombatMode/Pacification/PacifiedComponent.cs index 464ef778851..96081e5dc67 100644 --- a/Content.Shared/CombatMode/Pacification/PacifiedComponent.cs +++ b/Content.Shared/CombatMode/Pacification/PacifiedComponent.cs @@ -1,4 +1,6 @@ +using Content.Shared.Alert; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; namespace Content.Shared.CombatMode.Pacification; @@ -42,4 +44,6 @@ public sealed partial class PacifiedComponent : Component [DataField] public EntityUid? LastAttackedEntity = null; + [DataField] + public ProtoId PacifiedAlert = "Pacified"; } diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index cb6b2a747bc..4bdcdf07695 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -96,7 +96,7 @@ private void Oninitialize(EntityUid uid, ItemSlotsComponent itemSlots, Component /// public void AddItemSlot(EntityUid uid, string id, ItemSlot slot, ItemSlotsComponent? itemSlots = null) { - itemSlots ??= EntityManager.EnsureComponent(uid); + itemSlots ??= EnsureComp(uid); DebugTools.AssertOwner(uid, itemSlots); if (itemSlots.Slots.TryGetValue(id, out var existing)) @@ -110,7 +110,7 @@ public void AddItemSlot(EntityUid uid, string id, ItemSlot slot, ItemSlotsCompon slot.ContainerSlot = _containers.EnsureContainer(uid, id); itemSlots.Slots[id] = slot; - Dirty(itemSlots); + Dirty(uid, itemSlots); } /// @@ -134,7 +134,7 @@ public void RemoveItemSlot(EntityUid uid, ItemSlot slot, ItemSlotsComponent? ite if (itemSlots.Slots.Count == 0) EntityManager.RemoveComponent(uid, itemSlots); else - Dirty(itemSlots); + Dirty(uid, itemSlots); } public bool TryGetSlot(EntityUid uid, string slotId, [NotNullWhen(true)] out ItemSlot? itemSlot, ItemSlotsComponent? component = null) diff --git a/Content.Shared/Cuffs/Components/CuffableComponent.cs b/Content.Shared/Cuffs/Components/CuffableComponent.cs index 5da6fa41a5f..4ddfe1b53ee 100644 --- a/Content.Shared/Cuffs/Components/CuffableComponent.cs +++ b/Content.Shared/Cuffs/Components/CuffableComponent.cs @@ -1,6 +1,8 @@ +using Content.Shared.Alert; using Content.Shared.Damage; using Robust.Shared.Containers; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -39,6 +41,9 @@ public sealed partial class CuffableComponent : Component /// [DataField("canStillInteract"), ViewVariables(VVAccess.ReadWrite)] public bool CanStillInteract = true; + + [DataField] + public ProtoId CuffedAlert = "Handcuffed"; } [Serializable, NetSerializable] diff --git a/Content.Shared/Cuffs/SharedCuffableSystem.cs b/Content.Shared/Cuffs/SharedCuffableSystem.cs index 9777b239884..d70a1e63083 100644 --- a/Content.Shared/Cuffs/SharedCuffableSystem.cs +++ b/Content.Shared/Cuffs/SharedCuffableSystem.cs @@ -75,6 +75,7 @@ public override void Initialize() SubscribeLocalEvent(OnUnequipAttempt); SubscribeLocalEvent(OnBeingPulledAttempt); SubscribeLocalEvent(OnBuckleAttemptEvent); + SubscribeLocalEvent(OnUnbuckleAttemptEvent); SubscribeLocalEvent>(AddUncuffVerb); SubscribeLocalEvent(OnCuffableDoAfter); SubscribeLocalEvent(OnPull); @@ -177,12 +178,13 @@ public void UpdateCuffState(EntityUid uid, CuffableComponent component) if (component.CanStillInteract) { - _alerts.ClearAlert(uid, AlertType.Handcuffed); + _alerts.ClearAlert(uid, component.CuffedAlert); RaiseLocalEvent(uid, new MoodRemoveEffectEvent("Handcuffed")); } + else { - _alerts.ShowAlert(uid, AlertType.Handcuffed); + _alerts.ShowAlert(uid, component.CuffedAlert); RaiseLocalEvent(uid, new MoodEffectEvent("Handcuffed")); } @@ -199,21 +201,33 @@ private void OnBeingPulledAttempt(EntityUid uid, CuffableComponent component, Be args.Cancel(); } - private void OnBuckleAttemptEvent(EntityUid uid, CuffableComponent component, ref BuckleAttemptEvent args) + private void OnBuckleAttempt(Entity ent, EntityUid? user, ref bool cancelled, bool buckling, bool popup) { - // if someone else is doing it, let it pass. - if (args.UserEntity != uid) + if (cancelled || user != ent.Owner) + return; + + if (!TryComp(ent, out var hands) || ent.Comp.CuffedHandCount != hands.Count) return; - if (!TryComp(uid, out var hands) || component.CuffedHandCount != hands.Count) + cancelled = true; + if (!popup) return; - args.Cancelled = true; - var message = args.Buckling + var message = buckling ? Loc.GetString("handcuff-component-cuff-interrupt-buckled-message") : Loc.GetString("handcuff-component-cuff-interrupt-unbuckled-message"); - _popup.PopupClient(message, uid, args.UserEntity); + _popup.PopupClient(message, ent, user); + } + + private void OnBuckleAttemptEvent(Entity ent, ref BuckleAttemptEvent args) + { + OnBuckleAttempt(ent, args.User, ref args.Cancelled, true, args.Popup); + } + + private void OnUnbuckleAttemptEvent(Entity ent, ref UnbuckleAttemptEvent args) + { + OnBuckleAttempt(ent, args.User, ref args.Cancelled, false, args.Popup); } private void OnPull(EntityUid uid, CuffableComponent component, PullMessage args) @@ -739,4 +753,4 @@ private sealed partial class AddCuffDoAfterEvent : SimpleDoAfterEvent { } } -} \ No newline at end of file +} diff --git a/Content.Shared/Damage/Components/StaminaComponent.cs b/Content.Shared/Damage/Components/StaminaComponent.cs index b78fe978090..46eaa9f1f05 100644 --- a/Content.Shared/Damage/Components/StaminaComponent.cs +++ b/Content.Shared/Damage/Components/StaminaComponent.cs @@ -1,4 +1,6 @@ +using Content.Shared.Alert; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Damage.Components; @@ -70,4 +72,7 @@ public sealed partial class StaminaComponent : Component /// [DataField, AutoNetworkedField] public float SlowdownMultiplier = 0.75f; -} \ No newline at end of file + + [DataField] + public ProtoId StaminaAlert = "Stamina"; +} diff --git a/Content.Shared/Damage/Systems/StaminaSystem.cs b/Content.Shared/Damage/Systems/StaminaSystem.cs index e4840a6630b..40d1c77314f 100644 --- a/Content.Shared/Damage/Systems/StaminaSystem.cs +++ b/Content.Shared/Damage/Systems/StaminaSystem.cs @@ -84,8 +84,7 @@ private void OnShutdown(EntityUid uid, StaminaComponent component, ComponentShut { RemCompDeferred(uid); } - - SetStaminaAlert(uid); + _alerts.ClearAlert(uid, component.StaminaAlert); } private void OnStartup(EntityUid uid, StaminaComponent component, ComponentStartup args) @@ -230,13 +229,10 @@ private void OnCollide(EntityUid uid, StaminaDamageOnCollideComponent component, private void SetStaminaAlert(EntityUid uid, StaminaComponent? component = null) { if (!Resolve(uid, ref component, false) || component.Deleted) - { - _alerts.ClearAlert(uid, AlertType.Stamina); return; - } var severity = ContentHelpers.RoundToLevels(MathF.Max(0f, component.CritThreshold - component.StaminaDamage), component.CritThreshold, 7); - _alerts.ShowAlert(uid, AlertType.Stamina, (short) severity); + _alerts.ShowAlert(uid, component.StaminaAlert, (short) severity); } /// @@ -304,7 +300,7 @@ public void TakeStaminaDamage(EntityUid uid, float value, StaminaComponent? comp } EnsureComp(uid); - Dirty(component); + Dirty(uid, component); if (value <= 0) return; @@ -410,7 +406,7 @@ private void EnterStamCrit(EntityUid uid, StaminaComponent? component = null) // Give them buffer before being able to be re-stunned component.NextUpdate = _timing.CurTime + component.StunTime + StamCritBufferTime; EnsureComp(uid); - Dirty(component); + Dirty(uid, component); _adminLogger.Add(LogType.Stamina, LogImpact.Medium, $"{ToPrettyString(uid):user} entered stamina crit"); } @@ -424,7 +420,8 @@ private void ExitStamCrit(EntityUid uid, StaminaComponent? component = null) component.NextUpdate = _timing.CurTime; _movementSpeed.RefreshMovementSpeedModifiers(uid); SetStaminaAlert(uid, component); - Dirty(component); + RemComp(uid); + Dirty(uid, component); _adminLogger.Add(LogType.Stamina, LogImpact.Low, $"{ToPrettyString(uid):user} recovered from stamina crit"); } } @@ -433,4 +430,4 @@ private void ExitStamCrit(EntityUid uid, StaminaComponent? component = null) /// Raised before stamina damage is dealt to allow other systems to cancel it. /// [ByRefEvent] -public record struct BeforeStaminaDamageEvent(float Value, bool Cancelled = false); \ No newline at end of file +public record struct BeforeStaminaDamageEvent(float Value, bool Cancelled = false); diff --git a/Content.Shared/Decals/SharedDecalSystem.cs b/Content.Shared/Decals/SharedDecalSystem.cs index 9dbef60f0da..0a2349ea299 100644 --- a/Content.Shared/Decals/SharedDecalSystem.cs +++ b/Content.Shared/Decals/SharedDecalSystem.cs @@ -69,7 +69,7 @@ private void OnCompStartup(EntityUid uid, DecalGridComponent component, Componen // This **shouldn't** be required, but just in case we ever get entity prototypes that have decal grids, we // need to ensure that we send an initial full state to players. - Dirty(component); + Dirty(uid, component); } protected Dictionary? ChunkCollection(EntityUid gridEuid, DecalGridComponent? comp = null) diff --git a/Content.Shared/Destructible/Thresholds/MinMax.cs b/Content.Shared/Destructible/Thresholds/MinMax.cs new file mode 100644 index 00000000000..e086a0f61c3 --- /dev/null +++ b/Content.Shared/Destructible/Thresholds/MinMax.cs @@ -0,0 +1,24 @@ +using Robust.Shared.Random; + +namespace Content.Shared.Destructible.Thresholds; + +[DataDefinition, Serializable] +public partial struct MinMax +{ + [DataField] + public int Min; + + [DataField] + public int Max; + + public MinMax(int min, int max) + { + Min = min; + Max = max; + } + + public int Next(IRobustRandom random) + { + return random.Next(Min, Max + 1); + } +} diff --git a/Content.Shared/Dice/SharedDiceSystem.cs b/Content.Shared/Dice/SharedDiceSystem.cs index defb3d5f0e3..8e2868e791d 100644 --- a/Content.Shared/Dice/SharedDiceSystem.cs +++ b/Content.Shared/Dice/SharedDiceSystem.cs @@ -59,7 +59,7 @@ public void SetCurrentSide(EntityUid uid, int side, DiceComponent? die = null) } die.CurrentValue = (side - die.Offset) * die.Multiplier; - Dirty(die); + Dirty(uid, die); UpdateVisuals(uid, die); } diff --git a/Content.Shared/Electrocution/SharedElectrocutionSystem.cs b/Content.Shared/Electrocution/SharedElectrocutionSystem.cs index 5031d8a9115..b228a987af4 100644 --- a/Content.Shared/Electrocution/SharedElectrocutionSystem.cs +++ b/Content.Shared/Electrocution/SharedElectrocutionSystem.cs @@ -20,7 +20,7 @@ public void SetInsulatedSiemensCoefficient(EntityUid uid, float siemensCoefficie return; insulated.Coefficient = siemensCoefficient; - Dirty(insulated); + Dirty(uid, insulated); } /// Entity being electrocuted. diff --git a/Content.Shared/Emoting/EmoteSystem.cs b/Content.Shared/Emoting/EmoteSystem.cs index fd6361245b1..1e06d7e982b 100644 --- a/Content.Shared/Emoting/EmoteSystem.cs +++ b/Content.Shared/Emoting/EmoteSystem.cs @@ -19,7 +19,7 @@ public void SetEmoting(EntityUid uid, bool value, EmotingComponent? component = if (component.Enabled == value) return; - Dirty(component); + Dirty(uid, component); } private void OnEmoteAttempt(EmoteAttemptEvent args) diff --git a/Content.Shared/Ensnaring/Components/EnsnareableComponent.cs b/Content.Shared/Ensnaring/Components/EnsnareableComponent.cs index 553f6df1c77..2536fac4edc 100644 --- a/Content.Shared/Ensnaring/Components/EnsnareableComponent.cs +++ b/Content.Shared/Ensnaring/Components/EnsnareableComponent.cs @@ -1,5 +1,7 @@ +using Content.Shared.Alert; using Robust.Shared.Containers; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Ensnaring.Components; @@ -40,6 +42,9 @@ public sealed partial class EnsnareableComponent : Component [DataField("state")] public string? State; + + [DataField] + public ProtoId EnsnaredAlert = "Ensnared"; } [Serializable, NetSerializable] diff --git a/Content.Shared/Examine/ExamineSystemShared.cs b/Content.Shared/Examine/ExamineSystemShared.cs index f792862be14..7e72f5b97b7 100644 --- a/Content.Shared/Examine/ExamineSystemShared.cs +++ b/Content.Shared/Examine/ExamineSystemShared.cs @@ -175,9 +175,9 @@ public bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates othe length = MaxRaycastRange; } - var occluderSystem = Get(); IoCManager.Resolve(ref entMan); + var occluderSystem = entMan!.System(); var ray = new Ray(origin.Position, dir.Normalized()); var rayResults = occluderSystem .IntersectRayWithPredicate(origin.MapId, ray, length, state, predicate, false).ToList(); @@ -188,13 +188,16 @@ public bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates othe foreach (var result in rayResults) { + if (entMan == null) + continue; + if (!entMan.TryGetComponent(result.HitEntity, out OccluderComponent? o)) { continue; } var bBox = o.BoundingBox; - bBox = bBox.Translated(entMan.GetComponent(result.HitEntity).WorldPosition); + bBox = bBox.Translated(_transform.GetWorldPosition(result.HitEntity)); if (bBox.Contains(origin.Position) || bBox.Contains(other.Position)) { @@ -210,8 +213,8 @@ public bool InRangeUnOccluded(MapCoordinates origin, MapCoordinates othe public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); - var originPos = entMan.GetComponent(origin).MapPosition; - var otherPos = entMan.GetComponent(other).MapPosition; + var originPos = _transform.GetMapCoordinates(origin); + var otherPos = _transform.GetMapCoordinates(other); return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker); } @@ -219,8 +222,8 @@ public bool InRangeUnOccluded(EntityUid origin, EntityUid other, float range = E public bool InRangeUnOccluded(EntityUid origin, EntityCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); - var originPos = entMan.GetComponent(origin).MapPosition; - var otherPos = other.ToMap(entMan); + var originPos = _transform.GetMapCoordinates(origin); + var otherPos = _transform.ToMapCoordinates(other); return InRangeUnOccluded(originPos, otherPos, range, predicate, ignoreInsideBlocker); } @@ -228,7 +231,7 @@ public bool InRangeUnOccluded(EntityUid origin, EntityCoordinates other, float r public bool InRangeUnOccluded(EntityUid origin, MapCoordinates other, float range = ExamineRange, Ignored? predicate = null, bool ignoreInsideBlocker = true) { var entMan = IoCManager.Resolve(); - var originPos = entMan.GetComponent(origin).MapPosition; + var originPos = _transform.GetMapCoordinates(origin); return InRangeUnOccluded(originPos, other, range, predicate, ignoreInsideBlocker); } @@ -370,7 +373,7 @@ int Comparison(ExamineMessagePart a, ExamineMessagePart b) /// sort messages the same as well as grouped together properly, even if subscriptions are different. /// You should wrap it in a using() block so popping automatically occurs. /// - public ExamineGroupDisposable PushGroup(string groupName, int priority=0) + public ExamineGroupDisposable PushGroup(string groupName, int priority = 0) { // Ensure that other examine events correctly ended their groups. DebugTools.Assert(_currentGroupPart == null); @@ -398,7 +401,7 @@ private void PopGroup() /// /// /// - public void PushMessage(FormattedMessage message, int priority=0) + public void PushMessage(FormattedMessage message, int priority = 0) { if (message.Nodes.Count == 0) return; @@ -421,9 +424,9 @@ public void PushMessage(FormattedMessage message, int priority=0) /// /// /// - public void PushMarkup(string markup, int priority=0) + public void PushMarkup(string markup, int priority = 0) { - PushMessage(FormattedMessage.FromMarkup(markup), priority); + PushMessage(FormattedMessage.FromMarkupPermissive(markup), priority); } /// @@ -433,7 +436,7 @@ public void PushMarkup(string markup, int priority=0) /// /// /// - public void PushText(string text, int priority=0) + public void PushText(string text, int priority = 0) { var msg = new FormattedMessage(); msg.AddText(text); @@ -469,9 +472,9 @@ public void AddMessage(FormattedMessage message, int priority = 0) /// /// /// - public void AddMarkup(string markup, int priority=0) + public void AddMarkup(string markup, int priority = 0) { - AddMessage(FormattedMessage.FromMarkup(markup), priority); + AddMessage(FormattedMessage.FromMarkupPermissive(markup), priority); } /// @@ -481,7 +484,7 @@ public void AddMarkup(string markup, int priority=0) /// /// /// - public void AddText(string text, int priority=0) + public void AddText(string text, int priority = 0) { var msg = new FormattedMessage(); msg.AddText(text); diff --git a/Content.Shared/Explosion/Components/OnTrigger/SmokeOnTriggerComponent.cs b/Content.Shared/Explosion/Components/OnTrigger/SmokeOnTriggerComponent.cs index 80d65f4c2cd..1138e74af8f 100644 --- a/Content.Shared/Explosion/Components/OnTrigger/SmokeOnTriggerComponent.cs +++ b/Content.Shared/Explosion/Components/OnTrigger/SmokeOnTriggerComponent.cs @@ -29,7 +29,7 @@ public sealed partial class SmokeOnTriggerComponent : Component /// Defaults to smoke but you can use foam if you want. /// [DataField, ViewVariables(VVAccess.ReadWrite)] - public ProtoId SmokePrototype = "Smoke"; + public EntProtoId SmokePrototype = "Smoke"; /// /// Solution to add to each smoke cloud. diff --git a/Content.Shared/Foldable/FoldableSystem.cs b/Content.Shared/Foldable/FoldableSystem.cs index 10baf8165b5..2a846f4f234 100644 --- a/Content.Shared/Foldable/FoldableSystem.cs +++ b/Content.Shared/Foldable/FoldableSystem.cs @@ -26,7 +26,7 @@ public override void Initialize() SubscribeLocalEvent(OnStoreThisAttempt); SubscribeLocalEvent(OnFoldableOpenAttempt); - SubscribeLocalEvent(OnBuckleAttempt); + SubscribeLocalEvent(OnStrapAttempt); } private void OnHandleState(EntityUid uid, FoldableComponent component, ref AfterAutoHandleStateEvent args) @@ -53,9 +53,9 @@ public void OnStoreThisAttempt(EntityUid uid, FoldableComponent comp, ref StoreM args.Cancelled = true; } - public void OnBuckleAttempt(EntityUid uid, FoldableComponent comp, ref BuckleAttemptEvent args) + public void OnStrapAttempt(EntityUid uid, FoldableComponent comp, ref StrapAttemptEvent args) { - if (args.Buckling && comp.IsFolded) + if (comp.IsFolded) args.Cancelled = true; } diff --git a/Content.Shared/Friction/TileFrictionController.cs b/Content.Shared/Friction/TileFrictionController.cs index 3583947ee36..930de07dab9 100644 --- a/Content.Shared/Friction/TileFrictionController.cs +++ b/Content.Shared/Friction/TileFrictionController.cs @@ -214,7 +214,7 @@ public void SetModifier(EntityUid entityUid, float value, TileFrictionModifierCo return; friction.Modifier = value; - Dirty(friction); + Dirty(entityUid, friction); } } } diff --git a/Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs b/Content.Shared/GameTicking/Components/ActiveGameRuleComponent.cs similarity index 67% rename from Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs rename to Content.Shared/GameTicking/Components/ActiveGameRuleComponent.cs index b9e6fa5d4b8..51bdd1c0371 100644 --- a/Content.Server/GameTicking/Components/ActiveGameRuleComponent.cs +++ b/Content.Shared/GameTicking/Components/ActiveGameRuleComponent.cs @@ -1,10 +1,8 @@ -namespace Content.Server.GameTicking.Components; +namespace Content.Shared.GameTicking.Components; /// /// Added to game rules before and removed before . /// Mutually exclusive with . /// [RegisterComponent] -public sealed partial class ActiveGameRuleComponent : Component -{ -} +public sealed partial class ActiveGameRuleComponent : Component; diff --git a/Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs b/Content.Shared/GameTicking/Components/DelayedStartRuleComponent.cs similarity index 91% rename from Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs rename to Content.Shared/GameTicking/Components/DelayedStartRuleComponent.cs index de4be83627d..9275da29b01 100644 --- a/Content.Server/GameTicking/Components/DelayedStartRuleComponent.cs +++ b/Content.Shared/GameTicking/Components/DelayedStartRuleComponent.cs @@ -1,6 +1,6 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.GameTicking.Components; +namespace Content.Shared.GameTicking.Components; /// /// Generic component used to track a gamerule that's start has been delayed. diff --git a/Content.Server/GameTicking/Components/EndedGameRuleComponent.cs b/Content.Shared/GameTicking/Components/EndedGameRuleComponent.cs similarity index 61% rename from Content.Server/GameTicking/Components/EndedGameRuleComponent.cs rename to Content.Shared/GameTicking/Components/EndedGameRuleComponent.cs index 3234bfff3a0..5e209ed78a2 100644 --- a/Content.Server/GameTicking/Components/EndedGameRuleComponent.cs +++ b/Content.Shared/GameTicking/Components/EndedGameRuleComponent.cs @@ -1,10 +1,8 @@ -namespace Content.Server.GameTicking.Components; +namespace Content.Shared.GameTicking.Components; /// /// Added to game rules before . /// Mutually exclusive with . /// [RegisterComponent] -public sealed partial class EndedGameRuleComponent : Component -{ -} +public sealed partial class EndedGameRuleComponent : Component; diff --git a/Content.Server/GameTicking/Components/GameRuleComponent.cs b/Content.Shared/GameTicking/Components/GameRuleComponent.cs similarity index 92% rename from Content.Server/GameTicking/Components/GameRuleComponent.cs rename to Content.Shared/GameTicking/Components/GameRuleComponent.cs index 1e6c3f0ab1d..28ea435f1b7 100644 --- a/Content.Server/GameTicking/Components/GameRuleComponent.cs +++ b/Content.Shared/GameTicking/Components/GameRuleComponent.cs @@ -1,7 +1,8 @@ -using Content.Server.Destructible.Thresholds; +using Content.Shared.Destructible.Thresholds; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.GameTicking.Components; +namespace Content.Shared.GameTicking.Components; /// /// Component attached to all gamerule entities. diff --git a/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs b/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs index 8fe9e00e7eb..9d8aa4f146e 100644 --- a/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs +++ b/Content.Shared/Gravity/SharedFloatingVisualizerSystem.cs @@ -35,7 +35,7 @@ protected bool CanFloat(EntityUid uid, FloatingVisualsComponent component, Trans return false; component.CanFloat = GravitySystem.IsWeightless(uid, xform: transform); - Dirty(component); + Dirty(uid, component); return component.CanFloat; } diff --git a/Content.Shared/Gravity/SharedGravitySystem.Shake.cs b/Content.Shared/Gravity/SharedGravitySystem.Shake.cs index ad2e0e3ad57..41cf616cc4b 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.Shake.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.Shake.cs @@ -24,7 +24,7 @@ private void UpdateShake() ShakeGrid(uid, gravity); comp.ShakeTimes--; comp.NextShake += TimeSpan.FromSeconds(ShakeCooldown); - Dirty(comp); + Dirty(uid, comp); } } } @@ -44,7 +44,7 @@ public void StartGridShake(EntityUid uid, GravityComponent? gravity = null) } shake.ShakeTimes = 10; - Dirty(shake); + Dirty(uid, shake); } protected virtual void ShakeGrid(EntityUid uid, GravityComponent? comp = null) {} diff --git a/Content.Shared/Gravity/SharedGravitySystem.cs b/Content.Shared/Gravity/SharedGravitySystem.cs index 55187bf14ac..59d75e453af 100644 --- a/Content.Shared/Gravity/SharedGravitySystem.cs +++ b/Content.Shared/Gravity/SharedGravitySystem.cs @@ -18,6 +18,9 @@ public abstract partial class SharedGravitySystem : EntitySystem [Dependency] private readonly AlertsSystem _alerts = default!; [Dependency] private readonly InventorySystem _inventory = default!; + [ValidatePrototypeId] + public const string WeightlessAlert = "Weightless"; + public bool IsWeightless(EntityUid uid, PhysicsComponent? body = null, TransformComponent? xform = null) { Resolve(uid, ref body, false); @@ -97,11 +100,11 @@ private void OnGravityChange(ref GravityChangedEvent ev) if (!ev.HasGravity) { - _alerts.ShowAlert(uid, AlertType.Weightless); + _alerts.ShowAlert(uid, WeightlessAlert); } else { - _alerts.ClearAlert(uid, AlertType.Weightless); + _alerts.ClearAlert(uid, WeightlessAlert); } } } @@ -110,11 +113,11 @@ private void OnAlertsSync(AlertSyncEvent ev) { if (IsWeightless(ev.Euid)) { - _alerts.ShowAlert(ev.Euid, AlertType.Weightless); + _alerts.ShowAlert(ev.Euid, WeightlessAlert); } else { - _alerts.ClearAlert(ev.Euid, AlertType.Weightless); + _alerts.ClearAlert(ev.Euid, WeightlessAlert); } } @@ -122,11 +125,11 @@ private void OnAlertsParentChange(EntityUid uid, AlertsComponent component, ref { if (IsWeightless(uid)) { - _alerts.ShowAlert(uid, AlertType.Weightless); + _alerts.ShowAlert(uid, WeightlessAlert); } else { - _alerts.ClearAlert(uid, AlertType.Weightless); + _alerts.ClearAlert(uid, WeightlessAlert); } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs index 7b169b5d0a6..5a34fd7b8a7 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs @@ -58,7 +58,7 @@ public bool CanDrop(EntityUid uid, EntityUid entity, HandsComponent? handsComp = /// public bool CanDropHeld(EntityUid uid, Hand hand, bool checkActionBlocker = true) { - if (hand.Container?.ContainedEntity is not {} held) + if (hand.Container?.ContainedEntity is not { } held) return false; if (!ContainerSystem.CanRemove(held, hand.Container)) @@ -128,7 +128,7 @@ public bool TryDrop(EntityUid uid, Hand hand, EntityCoordinates? targetDropLocat // TODO recursively check upwards for containers if (!isInContainer - || !ContainerSystem.TryGetContainingContainer(userXform.ParentUid, uid, out var container, skipExistCheck: true) + || !ContainerSystem.TryGetContainingContainer(userXform.ParentUid, uid, out var container) || !ContainerSystem.Insert((entity, itemXform), container)) TransformSystem.AttachToGridOrMap(entity, itemXform); return true; @@ -136,7 +136,7 @@ public bool TryDrop(EntityUid uid, Hand hand, EntityCoordinates? targetDropLocat var (itemPos, itemRot) = TransformSystem.GetWorldPositionRotation(entity); var origin = new MapCoordinates(itemPos, itemXform.MapID); - var target = targetDropLocation.Value.ToMap(EntityManager, TransformSystem); + var target = TransformSystem.ToMapCoordinates(targetDropLocation.Value); TransformSystem.SetWorldPositionRotation(entity, GetFinalDropCoordinates(uid, origin, target), itemRot); return true; } diff --git a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs index a1e8bec2cd8..3727197bef7 100644 --- a/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs +++ b/Content.Shared/Humanoid/SharedHumanoidAppearanceSystem.cs @@ -156,7 +156,7 @@ public void SetLayersVisibility(EntityUid uid, IEnumerable SetLayerVisibility(uid, humanoid, layer, visible, permanent, ref dirty); if (dirty) - Dirty(humanoid); + Dirty(uid, humanoid); } protected virtual void SetLayerVisibility( @@ -202,7 +202,7 @@ public void SetSpecies(EntityUid uid, string species, bool sync = true, Humanoid humanoid.MarkingSet = new(oldMarkings, prototype.MarkingPoints, _markingManager, _proto); if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -228,7 +228,7 @@ public virtual void SetSkinColor(EntityUid uid, Color skinColor, bool sync = tru humanoid.SkinColor = skinColor; if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -252,7 +252,7 @@ public void SetBaseLayerId(EntityUid uid, HumanoidVisualLayers layer, string? id humanoid.CustomBaseLayers[layer] = new(id); if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -273,7 +273,7 @@ public void SetBaseLayerColor(EntityUid uid, HumanoidVisualLayers layer, Color? humanoid.CustomBaseLayers[layer] = new(null, color); if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -294,7 +294,7 @@ public void SetSex(EntityUid uid, Sex sex, bool sync = true, HumanoidAppearanceC RaiseLocalEvent(uid, new SexChangedEvent(oldSex, sex)); if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -313,7 +313,7 @@ public void SetHeight(EntityUid uid, float height, bool sync = true, HumanoidApp humanoid.Height = Math.Clamp(height, species.MinHeight, species.MaxHeight); if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -332,7 +332,7 @@ public void SetWidth(EntityUid uid, float width, bool sync = true, HumanoidAppea humanoid.Width = Math.Clamp(width, species.MinWidth, species.MaxWidth); if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -352,7 +352,7 @@ public void SetScale(EntityUid uid, Vector2 scale, bool sync = true, HumanoidApp humanoid.Width = Math.Clamp(scale.X, species.MinWidth, species.MaxWidth); if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -439,7 +439,7 @@ public virtual void LoadProfile(EntityUid uid, HumanoidCharacterProfile profile, humanoid.LastProfileLoaded = profile; // DeltaV - let paradox anomaly be cloned - Dirty(humanoid); + Dirty(uid, humanoid); } /// @@ -470,7 +470,7 @@ public void AddMarking(EntityUid uid, string marking, Color? color = null, bool humanoid.MarkingSet.AddBack(prototype.MarkingCategory, markingObject); if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } private void EnsureDefaultMarkings(EntityUid uid, HumanoidAppearanceComponent? humanoid) @@ -500,7 +500,7 @@ public void AddMarking(EntityUid uid, string marking, IReadOnlyList color humanoid.MarkingSet.AddBack(prototype.MarkingCategory, markingObject); if (sync) - Dirty(humanoid); + Dirty(uid, humanoid); } /// diff --git a/Content.Shared/Implants/SharedImplanterSystem.cs b/Content.Shared/Implants/SharedImplanterSystem.cs index 36a31bac1d2..d78522b56cc 100644 --- a/Content.Shared/Implants/SharedImplanterSystem.cs +++ b/Content.Shared/Implants/SharedImplanterSystem.cs @@ -77,7 +77,7 @@ public void Implant(EntityUid user, EntityUid target, EntityUid implanter, Impla var ev = new TransferDnaEvent { Donor = target, Recipient = implanter }; RaiseLocalEvent(target, ref ev); - Dirty(component); + Dirty(implanter, component); } public bool CanImplant( @@ -156,7 +156,7 @@ public void Draw(EntityUid implanter, EntityUid user, EntityUid target, Implante if (component.CurrentMode == ImplanterToggleMode.Draw && !component.ImplantOnly && !permanentFound) ImplantMode(implanter, component); - Dirty(component); + Dirty(implanter, component); } } diff --git a/Content.Shared/Instruments/SharedInstrumentSystem.cs b/Content.Shared/Instruments/SharedInstrumentSystem.cs index 87e3a69489c..23bcf67de0e 100644 --- a/Content.Shared/Instruments/SharedInstrumentSystem.cs +++ b/Content.Shared/Instruments/SharedInstrumentSystem.cs @@ -12,10 +12,10 @@ public virtual void EndRenderer(EntityUid uid, bool fromStateChange, SharedInstr { } - public void SetInstrumentProgram(SharedInstrumentComponent component, byte program, byte bank) + public void SetInstrumentProgram(EntityUid uid, SharedInstrumentComponent component, byte program, byte bank) { component.InstrumentBank = bank; component.InstrumentProgram = program; - Dirty(component); + Dirty(uid, component); } } diff --git a/Content.Shared/Interaction/RotateToFaceSystem.cs b/Content.Shared/Interaction/RotateToFaceSystem.cs index ed950240af6..fa213011ef1 100644 --- a/Content.Shared/Interaction/RotateToFaceSystem.cs +++ b/Content.Shared/Interaction/RotateToFaceSystem.cs @@ -1,7 +1,6 @@ using System.Numerics; using Content.Shared.ActionBlocker; using Content.Shared.Buckle.Components; -using Content.Shared.Mobs.Systems; using Content.Shared.Rotatable; using JetBrains.Annotations; @@ -83,24 +82,21 @@ public bool TryFaceAngle(EntityUid user, Angle diffAngle, TransformComponent? xf if (!_actionBlockerSystem.CanChangeDirection(user)) return false; - if (EntityManager.TryGetComponent(user, out BuckleComponent? buckle) && buckle.Buckled) + if (TryComp(user, out BuckleComponent? buckle) && buckle.BuckledTo is {} strap) { - var suid = buckle.LastEntityBuckledTo; - if (suid != null) - { - // We're buckled to another object. Is that object rotatable? - if (TryComp(suid.Value, out var rotatable) && rotatable.RotateWhileAnchored) - { - // Note the assumption that even if unanchored, user can only do spinnychair with an "independent wheel". - // (Since the user being buckled to it holds it down with their weight.) - // This is logically equivalent to RotateWhileAnchored. - // Barstools and office chairs have independent wheels, while regular chairs don't. - _transform.SetWorldRotation(Transform(suid.Value), diffAngle); - return true; - } - } - - return false; + // What if a person is strapped to a borg? + // I'm pretty sure this would allow them to be partially ratatouille'd + + // We're buckled to another object. Is that object rotatable? + if (!TryComp(strap, out var rotatable) || !rotatable.RotateWhileAnchored) + return false; + + // Note the assumption that even if unanchored, user can only do spinnychair with an "independent wheel". + // (Since the user being buckled to it holds it down with their weight.) + // This is logically equivalent to RotateWhileAnchored. + // Barstools and office chairs have independent wheels, while regular chairs don't. + _transform.SetWorldRotation(Transform(strap), diffAngle); + return true; } // user is not buckled in; apply to their transform diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 4c22bcb14e4..1c4a697cc4e 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -1167,7 +1167,7 @@ public bool CanAccessViaStorage(EntityUid user, EntityUid target, BaseContainer return false; // we don't check if the user can access the storage entity itself. This should be handed by the UI system. - return _ui.IsUiOpen(target, StorageComponent.StorageUiKey.Key, user); + return _ui.IsUiOpen(container.Owner, StorageComponent.StorageUiKey.Key, user); } /// diff --git a/Content.Shared/Light/SharedHandheldLightSystem.cs b/Content.Shared/Light/SharedHandheldLightSystem.cs index 2fa15800a31..9bec37a3140 100644 --- a/Content.Shared/Light/SharedHandheldLightSystem.cs +++ b/Content.Shared/Light/SharedHandheldLightSystem.cs @@ -29,7 +29,7 @@ private void OnInit(EntityUid uid, HandheldLightComponent component, ComponentIn UpdateVisuals(uid, component); // Want to make sure client has latest data on level so battery displays properly. - Dirty(component); + Dirty(uid, component); } private void OnHandleState(EntityUid uid, HandheldLightComponent component, ref ComponentHandleState args) diff --git a/Content.Shared/Light/SharedRgbLightControllerSystem.cs b/Content.Shared/Light/SharedRgbLightControllerSystem.cs index 1bba91c5e7b..7d4928f5bc1 100644 --- a/Content.Shared/Light/SharedRgbLightControllerSystem.cs +++ b/Content.Shared/Light/SharedRgbLightControllerSystem.cs @@ -17,13 +17,13 @@ private void OnGetState(EntityUid uid, RgbLightControllerComponent component, re args.State = new RgbLightControllerState(component.CycleRate, component.Layers); } - public void SetLayers(EntityUid uid, List? layers, RgbLightControllerComponent? rgb = null) + public void SetLayers(EntityUid uid, List? layers, RgbLightControllerComponent? rgb = null) { if (!Resolve(uid, ref rgb)) return; rgb.Layers = layers; - Dirty(rgb); + Dirty(uid, rgb); } public void SetCycleRate(EntityUid uid, float rate, RgbLightControllerComponent? rgb = null) @@ -32,6 +32,6 @@ public void SetCycleRate(EntityUid uid, float rate, RgbLightControllerComponent? return; rgb.CycleRate = Math.Clamp(0.01f, rate, 1); // lets not give people seizures - Dirty(rgb); + Dirty(uid, rgb); } } diff --git a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs index 7e44dea5078..b4f1ae9a268 100644 --- a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs +++ b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs @@ -194,7 +194,7 @@ public void CycleEquipment(EntityUid uid, MechComponent? component = null) if (_net.IsServer) _popup.PopupEntity(popupString, uid); - Dirty(component); + Dirty(uid, component); } /// @@ -278,7 +278,7 @@ public virtual bool TryChangeEnergy(EntityUid uid, FixedPoint2 delta, MechCompon return false; component.Energy = FixedPoint2.Clamp(component.Energy + delta, 0, component.MaxEnergy); - Dirty(component); + Dirty(uid, component); UpdateUserInterface(uid, component); return true; } @@ -306,7 +306,7 @@ public void SetIntegrity(EntityUid uid, FixedPoint2 value, MechComponent? compon UpdateAppearance(uid, component); } - Dirty(component); + Dirty(uid, component); UpdateUserInterface(uid, component); } diff --git a/Content.Shared/Mobs/Components/MobThresholdsComponent.cs b/Content.Shared/Mobs/Components/MobThresholdsComponent.cs index e97d3672a21..0e37cf9b10e 100644 --- a/Content.Shared/Mobs/Components/MobThresholdsComponent.cs +++ b/Content.Shared/Mobs/Components/MobThresholdsComponent.cs @@ -2,6 +2,7 @@ using Content.Shared.FixedPoint; using Content.Shared.Mobs.Systems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Mobs.Components; @@ -24,13 +25,16 @@ public sealed partial class MobThresholdsComponent : Component /// Used for alternate health alerts (silicons, for example) /// [DataField("stateAlertDict")] - public Dictionary StateAlertDict = new() + public Dictionary> StateAlertDict = new() { - {MobState.Alive, AlertType.HumanHealth}, - {MobState.Critical, AlertType.HumanCrit}, - {MobState.Dead, AlertType.HumanDead}, + {MobState.Alive, "HumanHealth"}, + {MobState.Critical, "HumanCrit"}, + {MobState.Dead, "HumanDead"}, }; + [DataField] + public ProtoId HealthAlertCategory = "Health"; + /// /// Whether or not this entity should display damage overlays (robots don't feel pain, black out etc.) /// @@ -53,19 +57,19 @@ public sealed class MobThresholdsComponentState : ComponentState public MobState CurrentThresholdState; - public Dictionary StateAlertDict = new() - { - {MobState.Alive, AlertType.HumanHealth}, - {MobState.Critical, AlertType.HumanCrit}, - {MobState.Dead, AlertType.HumanDead}, - }; + public Dictionary> StateAlertDict; public bool ShowOverlays; public bool AllowRevives; - public MobThresholdsComponentState(Dictionary unsortedThresholds, bool triggersAlerts, MobState currentThresholdState, - Dictionary stateAlertDict, bool showOverlays, bool allowRevives) + public MobThresholdsComponentState(Dictionary unsortedThresholds, + bool triggersAlerts, + MobState currentThresholdState, + Dictionary> stateAlertDict, + bool showOverlays, + bool allowRevives) { UnsortedThresholds = unsortedThresholds; TriggersAlerts = triggersAlerts; diff --git a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs index d9ef671afe2..2088bd4161e 100644 --- a/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs +++ b/Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs @@ -1,4 +1,5 @@ using Content.Shared.Bed.Sleep; +using Content.Shared.Buckle.Components; using Content.Shared.CombatMode.Pacification; using Content.Shared.Damage.ForceSay; using Content.Shared.Emoting; @@ -10,15 +11,12 @@ using Content.Shared.Mobs.Components; using Content.Shared.Movement.Events; using Content.Shared.Pointing; -using Content.Shared.Projectiles; using Content.Shared.Pulling.Events; using Content.Shared.Speech; using Content.Shared.Standing; using Content.Shared.Strip.Components; using Content.Shared.Throwing; -using Content.Shared.Weapons.Ranged.Components; using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Events; namespace Content.Shared.Mobs.Systems; @@ -46,6 +44,16 @@ private void SubscribeEvents() SubscribeLocalEvent(OnSleepAttempt); SubscribeLocalEvent(OnCombatModeShouldHandInteract); SubscribeLocalEvent(OnAttemptPacifiedAttack); + + SubscribeLocalEvent(OnUnbuckleAttempt); + } + + private void OnUnbuckleAttempt(Entity ent, ref UnbuckleAttemptEvent args) + { + // TODO is this necessary? + // Shouldn't the interaction have already been blocked by a general interaction check? + if (args.User == ent.Owner && IsIncapacitated(ent)) + args.Cancelled = true; } private void OnStateExitSubscribers(EntityUid target, MobStateComponent component, MobState state) @@ -90,12 +98,12 @@ private void OnStateEnteredSubscribers(EntityUid target, MobStateComponent compo _appearance.SetData(target, MobStateVisuals.State, MobState.Alive); break; case MobState.Critical: - _standing.Down(target, setDrawDepth: true); + _standing.Down(target); _appearance.SetData(target, MobStateVisuals.State, MobState.Critical); break; case MobState.Dead: EnsureComp(target); - _standing.Down(target, setDrawDepth: true); + _standing.Down(target); if (_standing.IsDown(target) && TryComp(target, out var physics)) { diff --git a/Content.Shared/Mobs/Systems/MobThresholdSystem.cs b/Content.Shared/Mobs/Systems/MobThresholdSystem.cs index 59d9fb4c239..b11de9eac56 100644 --- a/Content.Shared/Mobs/Systems/MobThresholdSystem.cs +++ b/Content.Shared/Mobs/Systems/MobThresholdSystem.cs @@ -431,7 +431,7 @@ private void MobThresholdStartup(EntityUid target, MobThresholdsComponent thresh private void MobThresholdShutdown(EntityUid target, MobThresholdsComponent component, ComponentShutdown args) { if (component.TriggersAlerts) - _alerts.ClearAlertCategory(target, AlertCategory.Health); + _alerts.ClearAlertCategory(target, component.HealthAlertCategory); } private void OnUpdateMobState(EntityUid target, MobThresholdsComponent component, ref UpdateMobStateEvent args) diff --git a/Content.Shared/Movement/Pulling/Components/PullableComponent.cs b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs index 01ce0efaae6..9d342fec3cf 100644 --- a/Content.Shared/Movement/Pulling/Components/PullableComponent.cs +++ b/Content.Shared/Movement/Pulling/Components/PullableComponent.cs @@ -1,4 +1,6 @@ +using Content.Shared.Alert; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; namespace Content.Shared.Movement.Pulling.Components; @@ -43,4 +45,6 @@ public sealed partial class PullableComponent : Component /// [DataField, AutoNetworkedField] public bool BeingActivelyPushed = false; + [DataField] + public ProtoId PulledAlert = "Pulled"; } diff --git a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs index 648f06086ba..80a12be690a 100644 --- a/Content.Shared/Movement/Pulling/Components/PullerComponent.cs +++ b/Content.Shared/Movement/Pulling/Components/PullerComponent.cs @@ -1,6 +1,8 @@ -using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Alert; +using Content.Shared.Movement.Pulling.Systems; using Robust.Shared.GameStates; using Robust.Shared.Map; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Movement.Pulling.Components; @@ -64,4 +66,6 @@ public sealed partial class PullerComponent : Component /// [DataField] public float MaxPushRange = 2f; + [DataField] + public ProtoId PullingAlert = "Pulling"; } diff --git a/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs b/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs index 29460e1dfc1..c0775b4ce2d 100644 --- a/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs +++ b/Content.Shared/Movement/Pulling/Events/PullStartedMessage.cs @@ -1,9 +1,6 @@ namespace Content.Shared.Movement.Pulling.Events; -public sealed class PullStartedMessage : PullMessage -{ - public PullStartedMessage(EntityUid pullerUid, EntityUid pullableUid) : - base(pullerUid, pullableUid) - { - } -} +/// +/// Event raised directed BOTH at the puller and pulled entity when a pull starts. +/// +public sealed class PullStartedMessage(EntityUid pullerUid, EntityUid pullableUid) : PullMessage(pullerUid, pullableUid); diff --git a/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs b/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs index 47aa34562fb..6df4d174839 100644 --- a/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs +++ b/Content.Shared/Movement/Pulling/Events/PullStoppedMessage.cs @@ -1,13 +1,6 @@ -using Robust.Shared.Physics.Components; - -namespace Content.Shared.Movement.Pulling.Events; +namespace Content.Shared.Movement.Pulling.Events; /// -/// Raised directed on both puller and pullable. +/// Event raised directed BOTH at the puller and pulled entity when a pull starts. /// -public sealed class PullStoppedMessage : PullMessage -{ - public PullStoppedMessage(EntityUid pullerUid, EntityUid pulledUid) : base(pullerUid, pulledUid) - { - } -} +public sealed class PullStoppedMessage(EntityUid pullerUid, EntityUid pulledUid) : PullMessage(pullerUid, pulledUid); diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index 4bf53c8dbdd..11a1d94b29b 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -1,4 +1,3 @@ -using System.Numerics; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; using Content.Shared.Alert; @@ -17,7 +16,6 @@ using Content.Shared.Projectiles; using Content.Shared.Pulling.Events; using Content.Shared.Standing; -using Content.Shared.Throwing; using Content.Shared.Verbs; using Robust.Shared.Containers; using Robust.Shared.Input.Binding; @@ -29,6 +27,8 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Player; using Robust.Shared.Timing; +using Content.Shared.Throwing; +using System.Numerics; namespace Content.Shared.Movement.Pulling.Systems; @@ -73,11 +73,25 @@ public override void Initialize() SubscribeLocalEvent(OnRefreshMovespeed); SubscribeLocalEvent(OnDropHandItems); + SubscribeLocalEvent(OnBuckled); + SubscribeLocalEvent(OnGotBuckled); + CommandBinds.Builder .Bind(ContentKeyFunctions.MovePulledObject, new PointerInputCmdHandler(OnRequestMovePulledObject)) .Bind(ContentKeyFunctions.ReleasePulledObject, InputCmdHandler.FromDelegate(OnReleasePulledObject, handle: false)) .Register(); } + private void OnBuckled(Entity ent, ref StrappedEvent args) + { + // Prevent people from pulling the entity they are buckled to + if (ent.Comp.Puller == args.Buckle.Owner && !args.Buckle.Comp.PullStrap) + StopPulling(ent, ent); + } + + private void OnGotBuckled(Entity ent, ref BuckledEvent args) + { + StopPulling(ent, ent); + } public override void Shutdown() { @@ -174,7 +188,8 @@ private void OnDropHandItems(EntityUid uid, PullerComponent pullerComp, DropHand private void OnPullerContainerInsert(Entity ent, ref EntGotInsertedIntoContainerMessage args) { - if (ent.Comp.Pulling == null) return; + if (ent.Comp.Pulling == null) + return; if (!TryComp(ent.Comp.Pulling.Value, out PullableComponent? pulling)) return; @@ -307,8 +322,18 @@ private void OnJointRemoved(EntityUid uid, PullableComponent component, JointRem /// private void StopPulling(EntityUid pullableUid, PullableComponent pullableComp) { + if (pullableComp.Puller == null) + return; + if (!_timing.ApplyingState) { + // Joint shutdown + if (pullableComp.PullJointId != null) + { + _joints.RemoveJoint(pullableUid, pullableComp.PullJointId); + pullableComp.PullJointId = null; + } + if (TryComp(pullableUid, out var pullablePhysics)) { _physics.SetFixedRotation(pullableUid, pullableComp.PrevFixedRotation, body: pullablePhysics); @@ -325,7 +350,7 @@ private void StopPulling(EntityUid pullableUid, PullableComponent pullableComp) if (TryComp(oldPuller, out var pullerComp)) { var pullerUid = oldPuller.Value; - _alertsSystem.ClearAlert(pullerUid, AlertType.Pulling); + _alertsSystem.ClearAlert(pullerUid, pullerComp.PullingAlert); pullerComp.Pulling = null; Dirty(oldPuller.Value, pullerComp); @@ -339,7 +364,7 @@ private void StopPulling(EntityUid pullableUid, PullableComponent pullableComp) } - _alertsSystem.ClearAlert(pullableUid, AlertType.Pulled); + _alertsSystem.ClearAlert(pullableUid, pullableComp.PulledAlert); } public bool IsPulled(EntityUid uid, PullableComponent? component = null) @@ -440,15 +465,6 @@ public bool CanPull(EntityUid puller, EntityUid pullableUid, PullerComponent? pu return false; } - if (EntityManager.TryGetComponent(puller, out BuckleComponent? buckle)) - { - // Prevent people pulling the chair they're on, etc. - if (buckle is { PullStrap: false, Buckled: true } && (buckle.LastEntityBuckledTo == pullableUid)) - { - return false; - } - } - var getPulled = new BeingPulledAttemptEvent(puller, pullableUid); RaiseLocalEvent(pullableUid, getPulled, true); var startPull = new StartPullAttemptEvent(puller, pullableUid); @@ -492,11 +508,8 @@ public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, if (!CanPull(pullerUid, pullableUid)) return false; - if (!EntityManager.TryGetComponent(pullerUid, out var pullerPhysics) || - !EntityManager.TryGetComponent(pullableUid, out var pullablePhysics)) - { + if (!HasComp(pullerUid) || !TryComp(pullableUid, out PhysicsComponent? pullablePhysics)) return false; - } // Ensure that the puller is not currently pulling anything. if (TryComp(pullerComp.Pulling, out var oldPullable) @@ -540,7 +553,7 @@ public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, { // Joint startup var union = _physics.GetHardAABB(pullerUid).Union(_physics.GetHardAABB(pullableUid, body: pullablePhysics)); - var length = Math.Max((float) union.Size.X, (float) union.Size.Y) * 0.75f; + var length = Math.Max(union.Size.X, union.Size.Y) * 0.75f; var joint = _joints.CreateDistanceJoint(pullableUid, pullerUid, id: pullableComp.PullJointId); joint.CollideConnected = false; @@ -557,8 +570,8 @@ public bool TryStartPull(EntityUid pullerUid, EntityUid pullableUid, // Messaging var message = new PullStartedMessage(pullerUid, pullableUid); - _alertsSystem.ShowAlert(pullerUid, AlertType.Pulling); - _alertsSystem.ShowAlert(pullableUid, AlertType.Pulled); + _alertsSystem.ShowAlert(pullerUid, pullerComp.PullingAlert); + _alertsSystem.ShowAlert(pullableUid, pullableComp.PulledAlert); RaiseLocalEvent(pullerUid, message); RaiseLocalEvent(pullableUid, message); @@ -584,17 +597,6 @@ public bool TryStopPull(EntityUid pullableUid, PullableComponent pullable, Entit if (msg.Cancelled) return false; - // Stop pulling confirmed! - if (!_timing.ApplyingState) - { - // Joint shutdown - if (pullable.PullJointId != null) - { - _joints.RemoveJoint(pullableUid, pullable.PullJointId); - pullable.PullJointId = null; - } - } - StopPulling(pullableUid, pullable); return true; } diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index 00afa3a4fb8..43a63068cf2 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -296,7 +296,7 @@ protected void HandleMobMovement( private void WalkingAlert(EntityUid player, InputMoverComponent component) { - _alerts.ShowAlert(player, AlertType.Walking, component.Sprinting ? (short) 1 : (short) 0); + _alerts.ShowAlert(player, component.WalkingAlert, component.Sprinting ? (short) 1 : (short) 0); } public void LerpRotation(EntityUid uid, InputMoverComponent mover, float frameTime) diff --git a/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs index f9f6b82bb18..400a675cd25 100644 --- a/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs +++ b/Content.Shared/Movement/Systems/SpeedModifierContactsSystem.cs @@ -58,7 +58,7 @@ public void ChangeModifiers(EntityUid uid, float walkSpeed, float sprintSpeed, S } component.WalkSpeedModifier = walkSpeed; component.SprintSpeedModifier = sprintSpeed; - Dirty(component); + Dirty(uid, component); _toUpdate.UnionWith(_physics.GetContactingEntities(uid)); } diff --git a/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs b/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs index 0f3bff265cb..91c816df5c9 100644 --- a/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs +++ b/Content.Shared/Ninja/Components/SpaceNinjaComponent.cs @@ -1,3 +1,4 @@ +using Content.Shared.Alert; using Content.Shared.Ninja.Systems; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; @@ -53,4 +54,7 @@ public sealed partial class SpaceNinjaComponent : Component /// [DataField] public EntProtoId SpiderChargeObjective = "SpiderChargeObjective"; + + [DataField] + public ProtoId SuitPowerAlert = "SuitPower"; } diff --git a/Content.Shared/Nutrition/Components/HungerComponent.cs b/Content.Shared/Nutrition/Components/HungerComponent.cs index 9ac82ba283c..79d895ddae6 100644 --- a/Content.Shared/Nutrition/Components/HungerComponent.cs +++ b/Content.Shared/Nutrition/Components/HungerComponent.cs @@ -2,6 +2,7 @@ using Content.Shared.Damage; using Content.Shared.Nutrition.EntitySystems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Generic; @@ -65,15 +66,18 @@ public sealed partial class HungerComponent : Component /// /// A dictionary relating hunger thresholds to corresponding alerts. /// - [DataField("hungerThresholdAlerts", customTypeSerializer: typeof(DictionarySerializer))] + [DataField("hungerThresholdAlerts")] [AutoNetworkedField] - public Dictionary HungerThresholdAlerts = new() + public Dictionary> HungerThresholdAlerts = new() { - { HungerThreshold.Peckish, AlertType.Peckish }, - { HungerThreshold.Starving, AlertType.Starving }, - { HungerThreshold.Dead, AlertType.Starving } + { HungerThreshold.Peckish, "Peckish" }, + { HungerThreshold.Starving, "Starving" }, + { HungerThreshold.Dead, "Starving" } }; + [DataField] + public ProtoId HungerAlertCategory = "Hunger"; + /// /// A dictionary relating HungerThreshold to how much they modify . /// diff --git a/Content.Shared/Nutrition/Components/ThirstComponent.cs b/Content.Shared/Nutrition/Components/ThirstComponent.cs index 731346401fd..f3ac881361f 100644 --- a/Content.Shared/Nutrition/Components/ThirstComponent.cs +++ b/Content.Shared/Nutrition/Components/ThirstComponent.cs @@ -1,6 +1,7 @@ using Content.Shared.Alert; using Content.Shared.Nutrition.EntitySystems; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Nutrition.Components; @@ -56,11 +57,14 @@ public sealed partial class ThirstComponent : Component {ThirstThreshold.Dead, 0.0f}, }; - public static readonly Dictionary ThirstThresholdAlertTypes = new() + [DataField] + public ProtoId ThirstyCategory = "Thirst"; + + public static readonly Dictionary> ThirstThresholdAlertTypes = new() { - {ThirstThreshold.Thirsty, AlertType.Thirsty}, - {ThirstThreshold.Parched, AlertType.Parched}, - {ThirstThreshold.Dead, AlertType.Parched}, + {ThirstThreshold.Thirsty, "Thirsty"}, + {ThirstThreshold.Parched, "Parched"}, + {ThirstThreshold.Dead, "Parched"}, }; } diff --git a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs index e6d82553336..6535390d646 100644 --- a/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/HungerSystem.cs @@ -67,7 +67,7 @@ private void OnMapInit(EntityUid uid, HungerComponent component, MapInitEvent ar private void OnShutdown(EntityUid uid, HungerComponent component, ComponentShutdown args) { - _alerts.ClearAlertCategory(uid, AlertCategory.Hunger); + _alerts.ClearAlertCategory(uid, component.HungerAlertCategory); } private void OnRefreshMovespeed(EntityUid uid, HungerComponent component, RefreshMovementSpeedModifiersEvent args) @@ -112,7 +112,7 @@ public void SetHunger(EntityUid uid, float amount, HungerComponent? component = component.Thresholds[HungerThreshold.Dead], component.Thresholds[HungerThreshold.Overfed]); UpdateCurrentThreshold(uid, component); - Dirty(component); + Dirty(uid, component); } private void UpdateCurrentThreshold(EntityUid uid, HungerComponent? component = null) @@ -125,7 +125,7 @@ private void UpdateCurrentThreshold(EntityUid uid, HungerComponent? component = return; component.CurrentThreshold = calculatedHungerThreshold; DoHungerThresholdEffects(uid, component); - Dirty(component); + Dirty(uid, component); } private void DoHungerThresholdEffects(EntityUid uid, HungerComponent? component = null, bool force = false) @@ -153,7 +153,7 @@ private void DoHungerThresholdEffects(EntityUid uid, HungerComponent? component } else { - _alerts.ClearAlertCategory(uid, AlertCategory.Hunger); + _alerts.ClearAlertCategory(uid, component.HungerAlertCategory); } if (component.HungerThresholdDecayModifiers.TryGetValue(component.CurrentThreshold, out var modifier)) diff --git a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs index a068b19104c..4ff49e795c2 100644 --- a/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/ThirstSystem.cs @@ -171,7 +171,7 @@ private void UpdateEffects(EntityUid uid, ThirstComponent component) } else { - _alerts.ClearAlertCategory(uid, AlertCategory.Thirst); + _alerts.ClearAlertCategory(uid, component.ThirstyCategory); } var ev = new MoodEffectEvent("Thirst" + component.CurrentThirstThreshold); diff --git a/Content.Shared/OfferItem/OfferItemComponent.cs b/Content.Shared/OfferItem/OfferItemComponent.cs index eb4d84932e5..f9f55291ddc 100644 --- a/Content.Shared/OfferItem/OfferItemComponent.cs +++ b/Content.Shared/OfferItem/OfferItemComponent.cs @@ -1,4 +1,6 @@ using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Content.Shared.Alert; namespace Content.Shared.OfferItem; @@ -23,4 +25,7 @@ public sealed partial class OfferItemComponent : Component [DataField] public float MaxOfferDistance = 2f; + + [DataField] + public ProtoId OfferAlert = "Offer"; } diff --git a/Content.Shared/PAI/PAIComponent.cs b/Content.Shared/PAI/PAIComponent.cs index b4e4c927354..9d5be302758 100644 --- a/Content.Shared/PAI/PAIComponent.cs +++ b/Content.Shared/PAI/PAIComponent.cs @@ -31,7 +31,7 @@ public sealed partial class PAIComponent : Component public EntityUid? MidiAction; [DataField] - public ProtoId MapActionId = "ActionPAIOpenMap"; + public EntProtoId MapActionId = "ActionPAIOpenMap"; [DataField, AutoNetworkedField] public EntityUid? MapAction; diff --git a/Content.Shared/Physics/Controllers/SharedConveyorController.cs b/Content.Shared/Physics/Controllers/SharedConveyorController.cs index c9ec77ba1c9..e3b22d84319 100644 --- a/Content.Shared/Physics/Controllers/SharedConveyorController.cs +++ b/Content.Shared/Physics/Controllers/SharedConveyorController.cs @@ -101,10 +101,10 @@ private void Convey(EntityUid uid, ConveyorComponent comp, EntityQuery MaxNameLength) + { name = Name[..MaxNameLength]; + } else + { name = Name; + } name = name.Trim(); if (configManager.GetCVar(CCVars.RestrictedNames)) + { name = RestrictedNameRegex.Replace(name, string.Empty); + } if (configManager.GetCVar(CCVars.ICNameCase)) { @@ -409,13 +419,19 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection : FormattedMessage.RemoveMarkup(Customspeciename); if (string.IsNullOrEmpty(name)) + { name = GetName(Species, gender); + } string flavortext; if (FlavorText.Length > MaxDescLength) + { flavortext = FormattedMessage.RemoveMarkup(FlavorText)[..MaxDescLength]; + } else + { flavortext = FormattedMessage.RemoveMarkup(FlavorText); + } var appearance = HumanoidCharacterAppearance.EnsureValid(Appearance, Species, Sex); @@ -468,7 +484,9 @@ public void EnsureValid(ICommonSession session, IDependencyCollection collection _jobPriorities.Clear(); foreach (var (job, priority) in priorities) + { _jobPriorities.Add(job, priority); + } PreferenceUnavailable = prefsUnavailableMode; diff --git a/Content.Shared/RCD/Systems/RCDAmmoSystem.cs b/Content.Shared/RCD/Systems/RCDAmmoSystem.cs index 9481d299aaa..9cb3c264851 100644 --- a/Content.Shared/RCD/Systems/RCDAmmoSystem.cs +++ b/Content.Shared/RCD/Systems/RCDAmmoSystem.cs @@ -36,7 +36,7 @@ private void OnAfterInteract(EntityUid uid, RCDAmmoComponent comp, AfterInteract if (args.Handled || !args.CanReach || !_timing.IsFirstTimePredicted) return; - if (args.Target is not {Valid: true} target || + if (args.Target is not { Valid: true } target || !HasComp(target) || !TryComp(target, out var charges)) return; @@ -53,7 +53,7 @@ private void OnAfterInteract(EntityUid uid, RCDAmmoComponent comp, AfterInteract _popup.PopupClient(Loc.GetString("rcd-ammo-component-after-interact-refilled"), target, user); _charges.AddCharges(target, count, charges); comp.Charges -= count; - Dirty(comp); + Dirty(uid, comp); // prevent having useless ammo with 0 charges if (comp.Charges <= 0) diff --git a/Content.Shared/Revenant/Components/RevenantComponent.cs b/Content.Shared/Revenant/Components/RevenantComponent.cs index 947c1a4b3fc..d7fb28ef136 100644 --- a/Content.Shared/Revenant/Components/RevenantComponent.cs +++ b/Content.Shared/Revenant/Components/RevenantComponent.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Shared.Alert; using Content.Shared.FixedPoint; using Content.Shared.Store; using Content.Shared.Whitelist; @@ -200,6 +201,9 @@ public sealed partial class RevenantComponent : Component public EntityWhitelist? MalfunctionBlacklist; #endregion + [DataField] + public ProtoId EssenceAlert = "Essence"; + #region Visualizer [DataField("state")] public string State = "idle"; diff --git a/Content.Shared/Shadowkin/ShadowkinComponent.cs b/Content.Shared/Shadowkin/ShadowkinComponent.cs index b382f3112b7..a2a4fdf334f 100644 --- a/Content.Shared/Shadowkin/ShadowkinComponent.cs +++ b/Content.Shared/Shadowkin/ShadowkinComponent.cs @@ -1,4 +1,6 @@ using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Content.Shared.Alert; namespace Content.Shared.Shadowkin; @@ -39,4 +41,7 @@ public sealed partial class ShadowkinComponent : Component [DataField] public EntityUid? ShadowkinSleepAction; + + [DataField] + public ProtoId ShadowkinPowerAlert = "ShadowkinPower"; } \ No newline at end of file diff --git a/Content.Shared/Shuttles/Components/PilotComponent.cs b/Content.Shared/Shuttles/Components/PilotComponent.cs index 1a6927cf813..cb42db4436f 100644 --- a/Content.Shared/Shuttles/Components/PilotComponent.cs +++ b/Content.Shared/Shuttles/Components/PilotComponent.cs @@ -1,7 +1,9 @@ using System.Numerics; +using Content.Shared.Alert; using Content.Shared.Movement.Systems; using Robust.Shared.GameStates; using Robust.Shared.Map; +using Robust.Shared.Prototypes; using Robust.Shared.Timing; namespace Content.Shared.Shuttles.Components @@ -32,6 +34,9 @@ public sealed partial class PilotComponent : Component [ViewVariables] public ShuttleButtons HeldButtons = ShuttleButtons.None; + [DataField] + public ProtoId PilotingAlert = "PilotingShuttle"; + public override bool SendOnlyToOwner => true; } } diff --git a/Content.Shared/Silicon/Systems/SharedSiliconSystem.cs b/Content.Shared/Silicon/Systems/SharedSiliconSystem.cs index 8fe87e162bc..37d3bcd5c3b 100644 --- a/Content.Shared/Silicon/Systems/SharedSiliconSystem.cs +++ b/Content.Shared/Silicon/Systems/SharedSiliconSystem.cs @@ -60,12 +60,12 @@ private void OnSiliconInit(EntityUid uid, SiliconComponent component, ComponentI if (!component.BatteryPowered) return; - _alertsSystem.ShowAlert(uid, AlertType.BorgBattery, component.ChargeState); + _alertsSystem.ShowAlert(uid, component.BatteryAlert, component.ChargeState); } private void OnSiliconChargeStateUpdate(EntityUid uid, SiliconComponent component, SiliconChargeStateUpdateEvent ev) { - _alertsSystem.ShowAlert(uid, AlertType.BorgBattery, ev.ChargePercent); + _alertsSystem.ShowAlert(uid, component.BatteryAlert, ev.ChargePercent); } private void OnRefreshMovespeed(EntityUid uid, SiliconComponent component, RefreshMovementSpeedModifiersEvent args) diff --git a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs index 71d3a7bd166..e1776873da9 100644 --- a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs +++ b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs @@ -1,6 +1,8 @@ -using Content.Shared.Whitelist; +using Content.Shared.Alert; +using Content.Shared.Whitelist; using Robust.Shared.Containers; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Silicons.Borgs.Components; @@ -76,6 +78,12 @@ public sealed partial class BorgChassisComponent : Component [DataField("noMindState")] public string NoMindState = string.Empty; #endregion + + [DataField] + public ProtoId BatteryAlert = "BorgBattery"; + + [DataField] + public ProtoId NoBatteryAlert = "BorgBatteryNone"; } [Serializable, NetSerializable] diff --git a/Content.Shared/Singularity/EntitySystems/SharedEventHorizonSystem.cs b/Content.Shared/Singularity/EntitySystems/SharedEventHorizonSystem.cs index f31dd8776a4..c2b52c5af35 100644 --- a/Content.Shared/Singularity/EntitySystems/SharedEventHorizonSystem.cs +++ b/Content.Shared/Singularity/EntitySystems/SharedEventHorizonSystem.cs @@ -66,7 +66,7 @@ public void SetRadius(EntityUid uid, float value, bool updateFixture = true, Eve return; eventHorizon.Radius = value; - Dirty(eventHorizon); + Dirty(uid, eventHorizon); if (updateFixture) UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon); } @@ -89,7 +89,7 @@ public void SetCanBreachContainment(EntityUid uid, bool value, bool updateFixtur return; eventHorizon.CanBreachContainment = value; - Dirty(eventHorizon); + Dirty(uid, eventHorizon); if (updateFixture) UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon); } @@ -112,7 +112,7 @@ public void SetColliderFixtureId(EntityUid uid, string? value, bool updateFixtur return; eventHorizon.ColliderFixtureId = value; - Dirty(eventHorizon); + Dirty(uid, eventHorizon); if (updateFixture) UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon); } @@ -135,7 +135,7 @@ public void SetConsumerFixtureId(EntityUid uid, string? value, bool updateFixtur return; eventHorizon.ConsumerFixtureId = value; - Dirty(eventHorizon); + Dirty(uid, eventHorizon); if (updateFixture) UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon); } diff --git a/Content.Shared/Stacks/SharedStackSystem.cs b/Content.Shared/Stacks/SharedStackSystem.cs index 756c84cac55..e12edd323c7 100644 --- a/Content.Shared/Stacks/SharedStackSystem.cs +++ b/Content.Shared/Stacks/SharedStackSystem.cs @@ -23,8 +23,8 @@ public abstract class SharedStackSystem : EntitySystem [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; [Dependency] protected readonly SharedHandsSystem Hands = default!; [Dependency] protected readonly SharedTransformSystem Xform = default!; - [Dependency] private readonly EntityLookupSystem _entityLookup = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly EntityLookupSystem _entityLookup = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] protected readonly SharedPopupSystem Popup = default!; [Dependency] private readonly SharedStorageSystem _storage = default!; @@ -175,7 +175,7 @@ public virtual void SetCount(EntityUid uid, int amount, StackComponent? componen // Server-side override deletes the entity if count == 0 component.Count = amount; - Dirty(component); + Dirty(uid, component); Appearance.SetData(uid, StackVisuals.Actual, component.Count); RaiseLocalEvent(uid, new StackCountChangedEvent(old, component.Count)); diff --git a/Content.Shared/Standing/SharedLayingDownSystem.cs b/Content.Shared/Standing/SharedLayingDownSystem.cs index 9fa4717045c..b317e6fb2dd 100644 --- a/Content.Shared/Standing/SharedLayingDownSystem.cs +++ b/Content.Shared/Standing/SharedLayingDownSystem.cs @@ -170,7 +170,7 @@ public bool TryLieDown(EntityUid uid, LayingDownComponent? layingDown = null, St return false; } - _standing.Down(uid, true, behavior != DropHeldItemsBehavior.NoDrop, standingState, setDrawDepth: true); + _standing.Down(uid, true, behavior != DropHeldItemsBehavior.NoDrop, standingState: standingState); return true; } } diff --git a/Content.Shared/Standing/StandingStateSystem.cs b/Content.Shared/Standing/StandingStateSystem.cs index 5abbf53f1b2..1dfde1b851d 100644 --- a/Content.Shared/Standing/StandingStateSystem.cs +++ b/Content.Shared/Standing/StandingStateSystem.cs @@ -34,11 +34,13 @@ public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) return standingState.CurrentState is StandingState.Lying or StandingState.GettingUp; } - public bool Down(EntityUid uid, bool playSound = true, bool dropHeldItems = true, + public bool Down(EntityUid uid, + bool playSound = true, + bool dropHeldItems = true, + bool force = false, StandingStateComponent? standingState = null, AppearanceComponent? appearance = null, - HandsComponent? hands = null, - bool setDrawDepth = false) + HandsComponent? hands = null) { // TODO: This should actually log missing comps... if (!Resolve(uid, ref standingState, false)) @@ -57,17 +59,20 @@ public bool Down(EntityUid uid, bool playSound = true, bool dropHeldItems = true if (dropHeldItems && hands != null) RaiseLocalEvent(uid, new DropHandItemsEvent(), false); - if (TryComp(uid, out BuckleComponent? buckle) && buckle.Buckled && !_buckle.TryUnbuckle(uid, uid, buckleComp: buckle)) + if (TryComp(uid, out BuckleComponent? buckle) && buckle.Buckled) return false; - var msg = new DownAttemptEvent(); - RaiseLocalEvent(uid, msg, false); + if (!force) + { + var msg = new DownAttemptEvent(); + RaiseLocalEvent(uid, msg, false); - if (msg.Cancelled) - return false; + if (msg.Cancelled) + return false; + } - standingState.CurrentState = StandingState.Lying; - Dirty(standingState); + standingState.Standing = false; + Dirty(uid, standingState); RaiseLocalEvent(uid, new DownedEvent(), false); // Seemed like the best place to put it diff --git a/Content.Shared/StationRecords/StationRecordKeyStorageSystem.cs b/Content.Shared/StationRecords/StationRecordKeyStorageSystem.cs index 05af0807f21..e9d68721b63 100644 --- a/Content.Shared/StationRecords/StationRecordKeyStorageSystem.cs +++ b/Content.Shared/StationRecords/StationRecordKeyStorageSystem.cs @@ -58,7 +58,7 @@ public void AssignKey(EntityUid uid, StationRecordKey key, StationRecordKeyStora var key = keyStorage.Key; keyStorage.Key = null; - Dirty(keyStorage); + Dirty(uid, keyStorage); return key; } diff --git a/Content.Shared/StatusEffect/StatusEffectPrototype.cs b/Content.Shared/StatusEffect/StatusEffectPrototype.cs index ae9e26879eb..8b1f84e4d81 100644 --- a/Content.Shared/StatusEffect/StatusEffectPrototype.cs +++ b/Content.Shared/StatusEffect/StatusEffectPrototype.cs @@ -10,7 +10,7 @@ public sealed partial class StatusEffectPrototype : IPrototype public string ID { get; private set; } = default!; [DataField("alert")] - public AlertType? Alert { get; private set; } + public ProtoId? Alert { get; private set; } /// /// Whether a status effect should be able to apply to any entity, diff --git a/Content.Shared/StatusEffect/StatusEffectsSystem.cs b/Content.Shared/StatusEffect/StatusEffectsSystem.cs index cc6dedae495..124dcdd8d67 100644 --- a/Content.Shared/StatusEffect/StatusEffectsSystem.cs +++ b/Content.Shared/StatusEffect/StatusEffectsSystem.cs @@ -234,7 +234,7 @@ public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool re _alertsSystem.ShowAlert(uid, proto.Alert.Value, null, cooldown1); } - Dirty(status); + Dirty(uid, status); RaiseLocalEvent(uid, new StatusEffectAddedEvent(uid, key)); return true; } @@ -246,7 +246,7 @@ public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool re /// This is mostly for stuns, since Stun and Knockdown share an alert key. Other times this pretty much /// will not be useful. /// - private (TimeSpan, TimeSpan)? GetAlertCooldown(EntityUid uid, AlertType alert, StatusEffectsComponent status) + private (TimeSpan, TimeSpan)? GetAlertCooldown(EntityUid uid, ProtoId alert, StatusEffectsComponent status) { (TimeSpan, TimeSpan)? maxCooldown = null; foreach (var kvp in status.ActiveEffects) @@ -310,7 +310,7 @@ public bool TryRemoveStatusEffect(EntityUid uid, string key, RemComp(uid); } - Dirty(status); + Dirty(uid, status); RaiseLocalEvent(uid, new StatusEffectEndedEvent(uid, key)); return true; } @@ -334,7 +334,7 @@ public bool TryRemoveAllStatusEffects(EntityUid uid, failed = true; } - Dirty(status); + Dirty(uid, status); return failed; } @@ -408,7 +408,7 @@ public bool TryAddTime(EntityUid uid, string key, TimeSpan time, _alertsSystem.ShowAlert(uid, proto.Alert.Value, null, cooldown); } - Dirty(status); + Dirty(uid, status); return true; } @@ -444,7 +444,7 @@ public bool TryRemoveTime(EntityUid uid, string key, TimeSpan time, _alertsSystem.ShowAlert(uid, proto.Alert.Value, null, cooldown); } - Dirty(status); + Dirty(uid, status); return true; } @@ -465,7 +465,7 @@ public bool TrySetTime(EntityUid uid, string key, TimeSpan time, status.ActiveEffects[key].Cooldown = (_gameTiming.CurTime, _gameTiming.CurTime + time); - Dirty(status); + Dirty(uid, status); return true; } diff --git a/Content.Shared/Storage/EntitySystems/BinSystem.cs b/Content.Shared/Storage/EntitySystems/BinSystem.cs index 17c3eb4288c..1cc95337ea4 100644 --- a/Content.Shared/Storage/EntitySystems/BinSystem.cs +++ b/Content.Shared/Storage/EntitySystems/BinSystem.cs @@ -135,7 +135,7 @@ public bool TryInsertIntoBin(EntityUid uid, EntityUid toInsert, BinComponent? co _container.Insert(toInsert, component.ItemContainer); component.Items.Add(toInsert); - Dirty(component); + Dirty(uid, component); return true; } @@ -151,7 +151,7 @@ public bool TryRemoveFromBin(EntityUid uid, EntityUid? toRemove, BinComponent? c if (!Resolve(uid, ref component)) return false; - if (!component.Items.Any()) + if (component.Items.Count == 0) return false; if (toRemove == null || toRemove != component.Items.LastOrDefault()) @@ -161,7 +161,7 @@ public bool TryRemoveFromBin(EntityUid uid, EntityUid? toRemove, BinComponent? c return false; component.Items.Remove(toRemove.Value); - Dirty(component); + Dirty(uid, component); return true; } } diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index 05d6b8ec533..ad36ba9329a 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -1,7 +1,5 @@ using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; -using Content.Shared.Audio; -using Content.Shared.DragDrop; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; using Content.Shared.Inventory.Events; @@ -11,13 +9,11 @@ using Content.Shared.Hands; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; -using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; using Content.Shared.Standing; using Content.Shared.StatusEffect; using Content.Shared.Throwing; -using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -88,15 +84,15 @@ private void OnMobStateChanged(EntityUid uid, MobStateComponent component, MobSt case MobState.Alive: break; case MobState.Critical: - { - _statusEffect.TryRemoveStatusEffect(uid, "Stun"); - break; - } + { + _statusEffect.TryRemoveStatusEffect(uid, "Stun"); + break; + } case MobState.Dead: - { - _statusEffect.TryRemoveStatusEffect(uid, "Stun"); - break; - } + { + _statusEffect.TryRemoveStatusEffect(uid, "Stun"); + break; + } case MobState.Invalid: default: return; @@ -258,11 +254,11 @@ private void OnInteractHand(EntityUid uid, KnockedDownComponent knocked, Interac return; // Set it to half the help interval so helping is actually useful... - knocked.HelpTimer = knocked.HelpInterval/2f; + knocked.HelpTimer = knocked.HelpInterval / 2f; _statusEffect.TryRemoveTime(uid, "KnockedDown", TimeSpan.FromSeconds(knocked.HelpInterval)); _audio.PlayPredicted(knocked.StunAttemptSound, uid, args.User); - Dirty(knocked); + Dirty(uid, knocked); args.Handled = true; } diff --git a/Content.Shared/SubFloor/SharedTrayScannerSystem.cs b/Content.Shared/SubFloor/SharedTrayScannerSystem.cs index 3dc1575ddb9..6e8393036d4 100644 --- a/Content.Shared/SubFloor/SharedTrayScannerSystem.cs +++ b/Content.Shared/SubFloor/SharedTrayScannerSystem.cs @@ -35,7 +35,7 @@ private void SetScannerEnabled(EntityUid uid, bool enabled, TrayScannerComponent return; scanner.Enabled = enabled; - Dirty(scanner); + Dirty(uid, scanner); // We don't remove from _activeScanners on disabled, because the update function will handle that, as well as // managing the revealed subfloor entities diff --git a/Content.Shared/Tag/TagSystem.cs b/Content.Shared/Tag/TagSystem.cs index 0707308e486..b9fef076c88 100644 --- a/Content.Shared/Tag/TagSystem.cs +++ b/Content.Shared/Tag/TagSystem.cs @@ -79,7 +79,7 @@ private void AssertValidTag(string id) /// public bool AddTag(EntityUid entity, string id) { - return AddTag(EnsureComp(entity), id); + return AddTag(entity, EnsureComp(entity), id); } /// @@ -95,7 +95,7 @@ public bool AddTag(EntityUid entity, string id) /// public bool AddTags(EntityUid entity, params string[] ids) { - return AddTags(EnsureComp(entity), ids); + return AddTags(entity, EnsureComp(entity), ids); } /// @@ -111,7 +111,7 @@ public bool AddTags(EntityUid entity, params string[] ids) /// public bool AddTags(EntityUid entity, IEnumerable ids) { - return AddTags(EnsureComp(entity), ids); + return AddTags(entity, EnsureComp(entity), ids); } /// @@ -128,8 +128,8 @@ public bool AddTags(EntityUid entity, IEnumerable ids) /// public bool TryAddTag(EntityUid entity, string id) { - return _tagQuery.TryComp(entity, out var component) && - AddTag(component, id); + return TryComp(entity, out var component) && + AddTag(entity, component, id); } /// @@ -146,8 +146,8 @@ public bool TryAddTag(EntityUid entity, string id) /// public bool TryAddTags(EntityUid entity, params string[] ids) { - return _tagQuery.TryComp(entity, out var component) && - AddTags(component, ids); + return TryComp(entity, out var component) && + AddTags(entity, component, ids); } /// @@ -164,8 +164,8 @@ public bool TryAddTags(EntityUid entity, params string[] ids) /// public bool TryAddTags(EntityUid entity, IEnumerable ids) { - return _tagQuery.TryComp(entity, out var component) && - AddTags(component, ids); + return TryComp(entity, out var component) && + AddTags(entity, component, ids); } /// @@ -333,8 +333,8 @@ public bool HasAnyTag(EntityUid entity, IEnumerable ids) /// public bool RemoveTag(EntityUid entity, string id) { - return _tagQuery.TryComp(entity, out var component) && - RemoveTag(component, id); + return TryComp(entity, out var component) && + RemoveTag(entity, component, id); } /// @@ -350,8 +350,8 @@ public bool RemoveTag(EntityUid entity, string id) /// public bool RemoveTags(EntityUid entity, params string[] ids) { - return _tagQuery.TryComp(entity, out var component) && - RemoveTags(component, ids); + return TryComp(entity, out var component) && + RemoveTags(entity, component, ids); } /// @@ -367,8 +367,8 @@ public bool RemoveTags(EntityUid entity, params string[] ids) /// public bool RemoveTags(EntityUid entity, IEnumerable ids) { - return _tagQuery.TryComp(entity, out var component) && - RemoveTags(component, ids); + return TryComp(entity, out var component) && + RemoveTags(entity, component, ids); } /// @@ -379,14 +379,14 @@ public bool RemoveTags(EntityUid entity, IEnumerable ids) /// /// Thrown if no exists with the given id. /// - public bool AddTag(TagComponent component, string id) + public bool AddTag(EntityUid uid, TagComponent component, string id) { AssertValidTag(id); var added = component.Tags.Add(id); if (added) { - Dirty(component); + Dirty(uid, component); return true; } @@ -401,9 +401,9 @@ public bool AddTag(TagComponent component, string id) /// /// Thrown if one of the ids represents an unregistered . /// - public bool AddTags(TagComponent component, params string[] ids) + public bool AddTags(EntityUid uid, TagComponent component, params string[] ids) { - return AddTags(component, ids.AsEnumerable()); + return AddTags(uid, component, ids.AsEnumerable()); } /// @@ -414,7 +414,7 @@ public bool AddTags(TagComponent component, params string[] ids) /// /// Thrown if one of the ids represents an unregistered . /// - public bool AddTags(TagComponent component, IEnumerable ids) + public bool AddTags(EntityUid uid, TagComponent component, IEnumerable ids) { var count = component.Tags.Count; @@ -426,7 +426,7 @@ public bool AddTags(TagComponent component, IEnumerable ids) if (component.Tags.Count > count) { - Dirty(component); + Dirty(uid, component); return true; } @@ -642,13 +642,13 @@ public bool HasAnyTag(TagComponent comp, List> ids) /// /// Thrown if no exists with the given id. /// - public bool RemoveTag(TagComponent component, string id) + public bool RemoveTag(EntityUid uid, TagComponent component, string id) { AssertValidTag(id); if (component.Tags.Remove(id)) { - Dirty(component); + Dirty(uid, component); return true; } @@ -665,9 +665,9 @@ public bool RemoveTag(TagComponent component, string id) /// /// Thrown if one of the ids represents an unregistered . /// - public bool RemoveTags(TagComponent component, params string[] ids) + public bool RemoveTags(EntityUid uid, TagComponent component, params string[] ids) { - return RemoveTags(component, ids.AsEnumerable()); + return RemoveTags(uid, component, ids.AsEnumerable()); } /// @@ -678,7 +678,7 @@ public bool RemoveTags(TagComponent component, params string[] ids) /// /// Thrown if one of the ids represents an unregistered . /// - public bool RemoveTags(TagComponent component, IEnumerable ids) + public bool RemoveTags(EntityUid uid, TagComponent component, IEnumerable ids) { var count = component.Tags.Count; @@ -690,7 +690,7 @@ public bool RemoveTags(TagComponent component, IEnumerable ids) if (component.Tags.Count < count) { - Dirty(component); + Dirty(uid, component); return true; } diff --git a/Content.Shared/Teleportation/Systems/LinkedEntitySystem.cs b/Content.Shared/Teleportation/Systems/LinkedEntitySystem.cs index bf2d087c761..35ce5665ddf 100644 --- a/Content.Shared/Teleportation/Systems/LinkedEntitySystem.cs +++ b/Content.Shared/Teleportation/Systems/LinkedEntitySystem.cs @@ -87,7 +87,7 @@ public bool OneWayLink(EntityUid source, EntityUid target, bool deleteOnEmptyLin /// Resolve comp /// Whether unlinking was successful (e.g. they both were actually linked to one another) public bool TryUnlink(EntityUid first, EntityUid second, - LinkedEntityComponent? firstLink=null, LinkedEntityComponent? secondLink=null) + LinkedEntityComponent? firstLink = null, LinkedEntityComponent? secondLink = null) { if (!Resolve(first, ref firstLink)) return false; @@ -101,8 +101,8 @@ public bool TryUnlink(EntityUid first, EntityUid second, _appearance.SetData(first, LinkedEntityVisuals.HasAnyLinks, firstLink.LinkedEntities.Any()); _appearance.SetData(second, LinkedEntityVisuals.HasAnyLinks, secondLink.LinkedEntities.Any()); - Dirty(firstLink); - Dirty(secondLink); + Dirty(first, firstLink); + Dirty(second, secondLink); if (firstLink.LinkedEntities.Count == 0 && firstLink.DeleteOnEmptyLinks) QueueDel(first); diff --git a/Content.Shared/Traits/Assorted/Systems/LegsParalyzedSystem.cs b/Content.Shared/Traits/Assorted/Systems/LegsParalyzedSystem.cs index 8ae0f251b86..4556d8ee991 100644 --- a/Content.Shared/Traits/Assorted/Systems/LegsParalyzedSystem.cs +++ b/Content.Shared/Traits/Assorted/Systems/LegsParalyzedSystem.cs @@ -17,7 +17,8 @@ public override void Initialize() { SubscribeLocalEvent(OnStartup); SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnBuckleChange); + SubscribeLocalEvent(OnBuckled); + SubscribeLocalEvent(OnUnbuckled); SubscribeLocalEvent(OnThrowPushbackAttempt); SubscribeLocalEvent(OnUpdateCanMoveEvent); } @@ -34,25 +35,11 @@ private void OnShutdown(EntityUid uid, Components.LegsParalyzedComponent compone _bodySystem.UpdateMovementSpeed(uid); } - private void OnBuckleChange(EntityUid uid, Components.LegsParalyzedComponent component, ref BuckleChangeEvent args) - { - if (args.Buckling) - { - _standingSystem.Stand(args.BuckledEntity); - } - else - { - _standingSystem.Down(args.BuckledEntity); - } - } + private void OnBuckled(EntityUid uid, Components.LegsParalyzedComponent component, ref BuckledEvent args) => _standingSystem.Stand(uid); - private void OnUpdateCanMoveEvent(EntityUid uid, Components.LegsParalyzedComponent component, UpdateCanMoveEvent args) - { - args.Cancel(); - } + private void OnUnbuckled(EntityUid uid, Components.LegsParalyzedComponent component, ref UnbuckledEvent args) => _standingSystem.Down(uid); - private void OnThrowPushbackAttempt(EntityUid uid, Components.LegsParalyzedComponent component, ThrowPushbackAttemptEvent args) - { - args.Cancel(); - } + private void OnUpdateCanMoveEvent(EntityUid uid, Components.LegsParalyzedComponent component, UpdateCanMoveEvent args) => args.Cancel(); + + private void OnThrowPushbackAttempt(EntityUid uid, Components.LegsParalyzedComponent component, ThrowPushbackAttemptEvent args) => args.Cancel(); } diff --git a/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs b/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs index 13270ae45d2..5bf4e0d3789 100644 --- a/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs +++ b/Content.Shared/Traits/Assorted/Systems/SharedSingerSystem.cs @@ -38,7 +38,7 @@ private void OnStartup(Entity ent, ref ComponentStartup args) var instrumentComp = EnsureInstrumentComp(ent); var defaultData = singer.InstrumentList[singer.DefaultInstrument]; - _instrument.SetInstrumentProgram(instrumentComp, defaultData.Item1, defaultData.Item2); + _instrument.SetInstrumentProgram(ent.Owner, instrumentComp, defaultData.Item1, defaultData.Item2); SetUpSwappableInstrument(ent, singer); } diff --git a/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs b/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs index 63b2d5f2115..d1814020e6e 100644 --- a/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs +++ b/Content.Shared/Weapons/Marker/SharedDamageMarkerSystem.cs @@ -71,7 +71,7 @@ private void OnMarkerCollide(EntityUid uid, DamageMarkerOnCollideComponent compo marker.Marker = projectile.Weapon.Value; marker.EndTime = _timing.CurTime + component.Duration; component.Amount--; - Dirty(marker); + Dirty(args.OtherEntity, marker); if (_netManager.IsServer) { @@ -81,7 +81,7 @@ private void OnMarkerCollide(EntityUid uid, DamageMarkerOnCollideComponent compo } else { - Dirty(component); + Dirty(uid, component); } } } diff --git a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs index 3a950bcd29e..dd297422c37 100644 --- a/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs +++ b/Content.Shared/Weapons/Misc/SharedTetherGunSystem.cs @@ -21,17 +21,17 @@ namespace Content.Shared.Weapons.Misc; public abstract partial class SharedTetherGunSystem : EntitySystem { - [Dependency] private readonly INetManager _netManager = default!; - [Dependency] private readonly ActionBlockerSystem _blocker = default!; - [Dependency] private readonly MobStateSystem _mob = default!; - [Dependency] private readonly SharedAppearanceSystem _appearance = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedContainerSystem _container = default!; - [Dependency] private readonly SharedJointSystem _joints = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly INetManager _netManager = default!; + [Dependency] private readonly ActionBlockerSystem _blocker = default!; + [Dependency] private readonly MobStateSystem _mob = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; + [Dependency] private readonly SharedJointSystem _joints = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; [Dependency] protected readonly SharedTransformSystem TransformSystem = default!; - [Dependency] private readonly ThrowingSystem _throwing = default!; - [Dependency] private readonly ThrownItemSystem _thrown = default!; + [Dependency] private readonly ThrowingSystem _throwing = default!; + [Dependency] private readonly ThrownItemSystem _thrown = default!; private const string TetherJoint = "tether"; @@ -282,7 +282,7 @@ protected virtual void StopTether(EntityUid gunUid, BaseForceGunComponent compon RemComp(component.Tethered.Value); _blocker.UpdateCanMove(component.Tethered.Value); component.Tethered = null; - Dirty(component); + Dirty(gunUid, component); } [Serializable, NetSerializable] diff --git a/Content.Shared/Weapons/Ranged/Systems/RechargeBasicEntityAmmoSystem.cs b/Content.Shared/Weapons/Ranged/Systems/RechargeBasicEntityAmmoSystem.cs index b774c8ab450..9d6d5524001 100644 --- a/Content.Shared/Weapons/Ranged/Systems/RechargeBasicEntityAmmoSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/RechargeBasicEntityAmmoSystem.cs @@ -49,19 +49,19 @@ public override void Update(float frameTime) if (ammo.Count == ammo.Capacity) { recharge.NextCharge = null; - Dirty(recharge); + Dirty(uid, recharge); continue; } recharge.NextCharge = recharge.NextCharge.Value + TimeSpan.FromSeconds(recharge.RechargeCooldown); - Dirty(recharge); + Dirty(uid, recharge); } } private void OnInit(EntityUid uid, RechargeBasicEntityAmmoComponent component, MapInitEvent args) { component.NextCharge = _timing.CurTime; - Dirty(component); + Dirty(uid, component); } private void OnExamined(EntityUid uid, RechargeBasicEntityAmmoComponent component, ExaminedEvent args) @@ -86,7 +86,7 @@ public void Reset(EntityUid uid, RechargeBasicEntityAmmoComponent? recharge = nu if (recharge.NextCharge == null || recharge.NextCharge < _timing.CurTime) { recharge.NextCharge = _timing.CurTime + TimeSpan.FromSeconds(recharge.RechargeCooldown); - Dirty(recharge); + Dirty(uid, recharge); } } } diff --git a/Content.Shared/Weapons/Ranged/Systems/RechargeCycleAmmoSystem.cs b/Content.Shared/Weapons/Ranged/Systems/RechargeCycleAmmoSystem.cs index 136e9b59b2f..a014f8e5c74 100644 --- a/Content.Shared/Weapons/Ranged/Systems/RechargeCycleAmmoSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/RechargeCycleAmmoSystem.cs @@ -25,7 +25,7 @@ private void OnRechargeCycled(EntityUid uid, RechargeCycleAmmoComponent componen return; _gun.UpdateBasicEntityAmmoCount(uid, basic.Count.Value + 1, basic); - Dirty(basic); + Dirty(uid, basic); args.Handled = true; } } diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index b714acefbd1..af295cc83ff 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -118,7 +118,7 @@ private void OnGunMelee(EntityUid uid, GunComponent component, MeleeHitEvent arg if (melee.NextAttack > component.NextFire) { component.NextFire = melee.NextAttack; - Dirty(component); + Dirty(uid, component); } } diff --git a/Content.Shared/Weapons/Reflect/ReflectSystem.cs b/Content.Shared/Weapons/Reflect/ReflectSystem.cs index 36dbedb4cb1..03ad97edff2 100644 --- a/Content.Shared/Weapons/Reflect/ReflectSystem.cs +++ b/Content.Shared/Weapons/Reflect/ReflectSystem.cs @@ -42,6 +42,9 @@ public sealed class ReflectSystem : EntitySystem [Dependency] private readonly StandingStateSystem _standing = default!; [Dependency] private readonly AlertsSystem _alerts = default!; + [ValidatePrototypeId] + private const string DeflectingAlert = "Deflecting"; + public override void Initialize() { base.Initialize(); @@ -296,11 +299,11 @@ private void RefreshReflectUser(EntityUid user) private void EnableAlert(EntityUid alertee) { - _alerts.ShowAlert(alertee, AlertType.Deflecting); + _alerts.ShowAlert(alertee, DeflectingAlert); } private void DisableAlert(EntityUid alertee) { - _alerts.ClearAlert(alertee, AlertType.Deflecting); + _alerts.ClearAlert(alertee, DeflectingAlert); } } diff --git a/Content.Shared/Weather/SharedWeatherSystem.cs b/Content.Shared/Weather/SharedWeatherSystem.cs index 45a2afe7cd9..19671bd77b0 100644 --- a/Content.Shared/Weather/SharedWeatherSystem.cs +++ b/Content.Shared/Weather/SharedWeatherSystem.cs @@ -15,8 +15,8 @@ public abstract class SharedWeatherSystem : EntitySystem [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] protected readonly IMapManager MapManager = default!; [Dependency] protected readonly IPrototypeManager ProtoMan = default!; - [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; - [Dependency] private readonly MetaDataSystem _metadata = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; + [Dependency] private readonly MetaDataSystem _metadata = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; private EntityQuery _blockQuery; @@ -129,7 +129,7 @@ public override void Update(float frameTime) // Shutting down if (endTime != null && remainingTime < WeatherComponent.ShutdownTime) { - SetState(WeatherState.Ending, comp, weather, weatherProto); + SetState(uid, WeatherState.Ending, comp, weather, weatherProto); } // Starting up else @@ -139,7 +139,7 @@ public override void Update(float frameTime) if (elapsed < WeatherComponent.StartupTime) { - SetState(WeatherState.Starting, comp, weather, weatherProto); + SetState(uid, WeatherState.Starting, comp, weather, weatherProto); } } @@ -182,15 +182,15 @@ public void SetWeather(MapId mapId, WeatherPrototype? proto, TimeSpan? endTime) } if (proto != null) - StartWeather(weatherComp, proto, endTime); + StartWeather(mapUid, weatherComp, proto, endTime); } /// /// Run every tick when the weather is running. /// - protected virtual void Run(EntityUid uid, WeatherData weather, WeatherPrototype weatherProto, float frameTime) {} + protected virtual void Run(EntityUid uid, WeatherData weather, WeatherPrototype weatherProto, float frameTime) { } - protected void StartWeather(WeatherComponent component, WeatherPrototype weather, TimeSpan? endTime) + protected void StartWeather(EntityUid uid, WeatherComponent component, WeatherPrototype weather, TimeSpan? endTime) { if (component.Weather.ContainsKey(weather.ID)) return; @@ -202,7 +202,7 @@ protected void StartWeather(WeatherComponent component, WeatherPrototype weather }; component.Weather.Add(weather.ID, data); - Dirty(component); + Dirty(uid, component); } protected virtual void EndWeather(EntityUid uid, WeatherComponent component, string proto) @@ -216,13 +216,13 @@ protected virtual void EndWeather(EntityUid uid, WeatherComponent component, str Dirty(uid, component); } - protected virtual bool SetState(WeatherState state, WeatherComponent component, WeatherData weather, WeatherPrototype weatherProto) + protected virtual bool SetState(EntityUid uid, WeatherState state, WeatherComponent component, WeatherData weather, WeatherPrototype weatherProto) { if (weather.State.Equals(state)) return false; weather.State = state; - Dirty(component); + Dirty(uid, component); return true; } diff --git a/Content.Tests/Shared/Alert/AlertManagerTests.cs b/Content.Tests/Shared/Alert/AlertManagerTests.cs index 2d5f6af5a7f..c57df63d5b7 100644 --- a/Content.Tests/Shared/Alert/AlertManagerTests.cs +++ b/Content.Tests/Shared/Alert/AlertManagerTests.cs @@ -1,6 +1,5 @@ using System.IO; using Content.Client.Alerts; -using Content.Server.Alert; using Content.Shared.Alert; using NUnit.Framework; using Robust.Shared.GameObjects; @@ -45,15 +44,15 @@ public void TestAlertManager() prototypeManager.Initialize(); prototypeManager.LoadFromStream(new StringReader(PROTOTYPES)); - Assert.That(alertsSystem.TryGet(AlertType.LowPressure, out var lowPressure)); - Assert.That(lowPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); - Assert.That(alertsSystem.TryGet(AlertType.HighPressure, out var highPressure)); - Assert.That(highPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/highpressure.png")))); + Assert.That(alertsSystem.TryGet("LowPressure", out var lowPressure)); + Assert.That(lowPressure!.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); + Assert.That(alertsSystem.TryGet("HighPressure", out var highPressure)); + Assert.That(highPressure!.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/highpressure.png")))); - Assert.That(alertsSystem.TryGet(AlertType.LowPressure, out lowPressure)); - Assert.That(lowPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); - Assert.That(alertsSystem.TryGet(AlertType.HighPressure, out highPressure)); - Assert.That(highPressure.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/highpressure.png")))); + Assert.That(alertsSystem.TryGet("LowPressure", out lowPressure)); + Assert.That(lowPressure!.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/lowpressure.png")))); + Assert.That(alertsSystem.TryGet("HighPressure", out highPressure)); + Assert.That(highPressure!.Icons[0], Is.EqualTo(new SpriteSpecifier.Texture(new ("/Textures/Interface/Alerts/Pressure/highpressure.png")))); } } } diff --git a/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs b/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs index 56f76d46a92..efcd59acbbb 100644 --- a/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs +++ b/Content.Tests/Shared/Alert/AlertOrderPrototypeTests.cs @@ -85,24 +85,24 @@ public void TestAlertOrderPrototype() var alerts = prototypeManager.EnumeratePrototypes(); // ensure they sort according to our expected criteria - var expectedOrder = new List(); - expectedOrder.Add(AlertType.Handcuffed); - expectedOrder.Add(AlertType.Ensnared); - expectedOrder.Add(AlertType.HighPressure); + var expectedOrder = new List(); + expectedOrder.Add("Handcuffed"); + expectedOrder.Add("Ensnared"); + expectedOrder.Add("HighPressure"); // stuff with only category + same category ordered by enum value - expectedOrder.Add(AlertType.Peckish); - expectedOrder.Add(AlertType.Hot); - expectedOrder.Add(AlertType.Stun); - expectedOrder.Add(AlertType.LowPressure); - expectedOrder.Add(AlertType.Cold); - // stuff at end of list ordered by enum value - expectedOrder.Add(AlertType.Weightless); - expectedOrder.Add(AlertType.PilotingShuttle); + expectedOrder.Add("Peckish"); + expectedOrder.Add("Hot"); + expectedOrder.Add("Stun"); + expectedOrder.Add("LowPressure"); + expectedOrder.Add("Cold"); + // stuff at end of list ordered by ID + expectedOrder.Add("PilotingShuttle"); + expectedOrder.Add("Weightless"); var actual = alerts.ToList(); actual.Sort(alertOrder); - Assert.That(actual.Select(a => a.AlertType).ToList(), Is.EqualTo(expectedOrder)); + Assert.That(actual.Select(a => a.ID).ToList(), Is.EqualTo(expectedOrder)); } } } diff --git a/Content.Tests/Shared/Alert/AlertPrototypeTests.cs b/Content.Tests/Shared/Alert/AlertPrototypeTests.cs index d95acb8aff4..43ae98b452b 100644 --- a/Content.Tests/Shared/Alert/AlertPrototypeTests.cs +++ b/Content.Tests/Shared/Alert/AlertPrototypeTests.cs @@ -39,9 +39,9 @@ public void OneTimeSetUp() [Test] public void TestAlertKey() { - Assert.That(new AlertKey(AlertType.HumanHealth, null), Is.Not.EqualTo(AlertKey.ForCategory(AlertCategory.Health))); - Assert.That((new AlertKey(null, AlertCategory.Health)), Is.EqualTo(AlertKey.ForCategory(AlertCategory.Health))); - Assert.That((new AlertKey(AlertType.Buckled, AlertCategory.Health)), Is.EqualTo(AlertKey.ForCategory(AlertCategory.Health))); + Assert.That(new AlertKey("HumanHealth", null), Is.Not.EqualTo(AlertKey.ForCategory("Health"))); + Assert.That((new AlertKey(null, "Health")), Is.EqualTo(AlertKey.ForCategory("Health"))); + Assert.That((new AlertKey("Buckled", "Health")), Is.EqualTo(AlertKey.ForCategory("Health"))); } [TestCase(0, "/Textures/Interface/Alerts/Human/human.rsi/human0.png")] diff --git a/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl b/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl index 30fe4971eb8..a80634fcdb2 100644 --- a/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl +++ b/Resources/Locale/en-US/station-events/events/anomaly-spawn.ftl @@ -1,4 +1,4 @@ -anomaly-spawn-event-announcement = Our readings have detected a dangerous interspacial anomaly. Please inform the research team of { $sighting }. +anomaly-spawn-event-announcement = Our readings have detected a dangerous interspacial anomaly. Please inform the research team about { $sighting }. anomaly-spawn-sighting-1 = low pulsating sounds heard throughout the station anomaly-spawn-sighting-2 = strange sources of light diff --git a/Resources/Locale/en-US/station-events/events/vent-critters.ftl b/Resources/Locale/en-US/station-events/events/vent-critters.ftl index d99906be4a3..d1cd281d053 100644 --- a/Resources/Locale/en-US/station-events/events/vent-critters.ftl +++ b/Resources/Locale/en-US/station-events/events/vent-critters.ftl @@ -10,6 +10,7 @@ station-event-argocyte-vents-announcement = Confirmed sightings of hostile alien station-event-light-vents-announcement = Confirmed sightings of hostile alien wildlife on the station. Personnel are advised to arm themselves, barricade doors, and defend themselves if necessary. Security is advised to eradicate the threat as soon as possible. station-event-carp-vents-announcement = Confirmed sightings of hostile alien wildlife on the station. Personnel are advised to arm themselves, barricade doors, and defend themselves if necessary. Security is advised to eradicate the threat as soon as possible. station-event-space-vents-announcement = Confirmed sightings of hostile wildlife on the station. Personnel are advised to arm themselves, barricade doors, and defend themselves if necessary. Security is advised to eradicate the threat as soon as possible. + # Weak station-event-slimes-spawn-weak-announcement = Attention. A moderate influx of unknown life forms have been detected residing within the station's ventilation systems. Please be rid of these creatures before it begins to affect productivity. station-event-spider-spawn-weak-announcement = Attention. A moderate influx of unknown life forms have been detected residing within the station's ventilation systems. Please be rid of these creatures before it begins to affect productivity. diff --git a/Resources/Prototypes/Alerts/categories.yml b/Resources/Prototypes/Alerts/categories.yml new file mode 100644 index 00000000000..1e79d2615bb --- /dev/null +++ b/Resources/Prototypes/Alerts/categories.yml @@ -0,0 +1,38 @@ +- type: alertCategory + id: Pressure + +- type: alertCategory + id: Temperature + +- type: alertCategory + id: Breathing + +- type: alertCategory + id: Buckled + +- type: alertCategory + id: Health + +- type: alertCategory + id: Internals + +- type: alertCategory + id: Stamina + +- type: alertCategory + id: Piloting + +- type: alertCategory + id: Hunger + +- type: alertCategory + id: Thirst + +- type: alertCategory + id: Toxins + +- type: alertCategory + id: Battery + +- type: alertCategory + id: Mood diff --git a/Resources/Prototypes/DeltaV/GameRules/events.yml b/Resources/Prototypes/DeltaV/GameRules/events.yml index db727601f84..7bd50381e51 100644 --- a/Resources/Prototypes/DeltaV/GameRules/events.yml +++ b/Resources/Prototypes/DeltaV/GameRules/events.yml @@ -4,7 +4,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-xeno-vents-start-announcement earliestStart: 20 reoccurrenceDelay: 12 minimumPlayers: 15 @@ -36,7 +36,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-xeno-vents-weak-start-announcement reoccurrenceDelay: 12 minimumPlayers: 1 - type: VentCrittersRule diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/Rslimes.yml b/Resources/Prototypes/Entities/Mobs/NPCs/Rslimes.yml index 3512b518150..d0339a44a11 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/Rslimes.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/Rslimes.yml @@ -225,7 +225,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-reagentslime-vents-announcement earliestStart: 20 reoccurrenceDelay: 12 minimumPlayers: 30 diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xenopet.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xenopet.yml index 23108e521bf..5aefb96cbb8 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xenopet.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xenopet.yml @@ -344,7 +344,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-neutral-xeno-vents-announcement earliestStart: 20 reoccurrenceDelay: 12 minimumPlayers: 1 @@ -367,7 +367,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-argocyte-vents-announcement earliestStart: 20 reoccurrenceDelay: 12 minimumPlayers: 30 @@ -406,7 +406,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-meat-vents-announcement earliestStart: 20 reoccurrenceDelay: 12 minimumPlayers: 20 @@ -431,7 +431,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-tick-vents-announcement earliestStart: 20 reoccurrenceDelay: 12 minimumPlayers: 1 @@ -448,7 +448,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-carp-vents-announcement earliestStart: 20 reoccurrenceDelay: 12 minimumPlayers: 20 @@ -475,7 +475,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-space-vents-announcement earliestStart: 20 reoccurrenceDelay: 12 minimumPlayers: 20 @@ -498,7 +498,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-light-vents-announcement earliestStart: 20 reoccurrenceDelay: 12 minimumPlayers: 20 diff --git a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml index 14b3270ba88..d65d652ff42 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml @@ -358,6 +358,8 @@ components: - type: Foldable folded: true + - type: Strap + enabled: False - type: entity name: steel bench diff --git a/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml b/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml index 161ea25bc43..1cfe98f0f65 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/rollerbeds.yml @@ -55,6 +55,7 @@ rotation: -90 buckleOffset: "0,0.15" unbuckleOffset: "0,0.15" + buckleOnInteractHand: False - type: Appearance - type: GenericVisualizer visuals: @@ -79,6 +80,8 @@ components: - type: Foldable folded: true + - type: Strap + enabled: False - type: entity id: CheapRollerBed @@ -105,6 +108,8 @@ components: - type: Foldable folded: true + - type: Strap + enabled: False - type: entity id: EmergencyRollerBed @@ -131,3 +136,5 @@ components: - type: Foldable folded: true + - type: Strap + enabled: False diff --git a/Resources/Prototypes/GameRules/cargo_gifts.yml b/Resources/Prototypes/GameRules/cargo_gifts.yml index 3787f4e6034..ef52b6f41f8 100644 --- a/Resources/Prototypes/GameRules/cargo_gifts.yml +++ b/Resources/Prototypes/GameRules/cargo_gifts.yml @@ -1,16 +1,30 @@ - type: entity - id: GiftsPizzaPartySmall + id: CargoGiftsBase parent: BaseGameRule + abstract: true + components: + - type: GameRule + delay: + min: 10 + max: 10 + - type: StationEvent + startColor: "#18abf5" + startAudio: + path: /Audio/Announcements/announce.ogg + - type: CargoGiftsRule + sender: cargo-gift-default-sender + +- type: entity + id: GiftsPizzaPartySmall + parent: CargoGiftsBase noSpawn: true components: - type: StationEvent weight: 5 - startDelay: 10 duration: 120 earliestStart: 20 - type: CargoGiftsRule description: cargo-gift-pizza-small - sender: cargo-gift-default-sender dest: cargo-gift-dest-bar gifts: FoodPizzaLarge: 1 # 16 pizzas @@ -20,190 +34,173 @@ - type: entity id: GiftsPizzaPartyLarge - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 2 - startDelay: 10 - duration: 240 - earliestStart: 20 - - type: CargoGiftsRule - description: cargo-gift-pizza-large - sender: cargo-gift-default-sender - dest: cargo-gift-dest-bar - gifts: - FoodPizzaLarge: 4 # 64 pizzas - FoodBarSupply: 1 - FoodSoftdrinksLarge: 1 + - type: StationEvent + weight: 2 + duration: 240 + earliestStart: 20 + minimumPlayers: 40 + - type: CargoGiftsRule + description: cargo-gift-pizza-large + dest: cargo-gift-dest-bar + gifts: + FoodPizzaLarge: 1 # 16 pizzas + FoodBarSupply: 1 + FoodSoftdrinksLarge: 1 - type: entity id: GiftsEngineering - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 5 - startDelay: 10 - duration: 240 - earliestStart: 30 - minimumPlayers: 10 - - type: CargoGiftsRule - description: cargo-gift-eng - sender: cargo-gift-default-sender - dest: cargo-gift-dest-eng - gifts: - EngineeringCableBulk: 1 - AirlockKit: 1 - MaterialSteel: 1 - MaterialPlasteel: 1 - MaterialGlass: 1 - CrateVendingMachineRestockEngineering: 1 + - type: StationEvent + weight: 5 + duration: 240 + earliestStart: 30 + minimumPlayers: 10 + - type: CargoGiftsRule + description: cargo-gift-eng + dest: cargo-gift-dest-eng + gifts: + EngineeringCableBulk: 1 + AirlockKit: 1 + MaterialSteel: 1 + MaterialPlasteel: 1 + MaterialGlass: 1 + CrateVendingMachineRestockEngineering: 1 - type: entity id: GiftsVendingRestock - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 4 - startDelay: 10 - duration: 120 - minimumPlayers: 40 - earliestStart: 30 - - type: CargoGiftsRule - description: cargo-gift-vending - sender: cargo-gift-default-sender - dest: cargo-gift-dest-supp - gifts: - CrateVendingMachineRestockHotDrinks: 3 - CrateVendingMachineRestockBooze: 1 - CrateVendingMachineRestockNutriMax: 1 - CrateVendingMachineRestockRobustSoftdrinks: 2 - CrateVendingMachineRestockVendomat: 1 - CrateVendingMachineRestockGetmoreChocolateCorp: 1 + - type: StationEvent + weight: 4 + duration: 120 + minimumPlayers: 40 + earliestStart: 30 + - type: CargoGiftsRule + description: cargo-gift-vending + dest: cargo-gift-dest-supp + gifts: + CrateVendingMachineRestockHotDrinks: 3 + CrateVendingMachineRestockBooze: 1 + CrateVendingMachineRestockNutriMax: 1 + CrateVendingMachineRestockRobustSoftdrinks: 2 + CrateVendingMachineRestockVendomat: 1 + CrateVendingMachineRestockGetmoreChocolateCorp: 1 - type: entity id: GiftsJanitor - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 6 - startDelay: 10 - duration: 120 - minimumPlayers: 30 - earliestStart: 20 - - type: CargoGiftsRule - description: cargo-gift-cleaning - sender: cargo-gift-default-sender - dest: cargo-gift-dest-janitor - gifts: - ServiceJanitorial: 2 - ServiceLightsReplacement: 2 - ServiceJanitorBiosuit: 1 + - type: StationEvent + weight: 6 + duration: 120 + minimumPlayers: 30 + earliestStart: 20 + - type: CargoGiftsRule + description: cargo-gift-cleaning + dest: cargo-gift-dest-janitor + gifts: + ServiceJanitorial: 2 + ServiceLightsReplacement: 2 + ServiceJanitorBiosuit: 1 - type: entity id: GiftsMedical - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 8 - startDelay: 10 - duration: 120 - earliestStart: 20 - minimumPlayers: 30 - - type: CargoGiftsRule - description: cargo-gift-medical-supply - sender: cargo-gift-default-sender - dest: cargo-gift-dest-med - gifts: - MedicalSupplies: 1 - MedicalChemistrySupplies: 1 - EmergencyBruteKit: 1 - EmergencyAdvancedKit: 1 - MedicalBiosuit: 1 + - type: StationEvent + weight: 8 + duration: 120 + earliestStart: 20 + minimumPlayers: 30 + - type: CargoGiftsRule + description: cargo-gift-medical-supply + dest: cargo-gift-dest-med + gifts: + MedicalSupplies: 1 + MedicalChemistrySupplies: 1 + EmergencyBruteKit: 1 + EmergencyAdvancedKit: 1 + MedicalBiosuit: 1 - type: entity id: GiftsSpacingSupplies - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 4 - startDelay: 10 - duration: 120 - earliestStart: 10 - minimumPlayers: 40 - - type: CargoGiftsRule - description: cargo-gift-space-protection - sender: cargo-gift-default-sender - dest: cargo-gift-dest-supp - gifts: - EmergencyInternalsLarge: 2 - EmergencyInflatablewall: 1 - EmergencyAdvancedKit: 1 - MedicalBiosuit: 1 - EmergencyO2Kit: 1 + - type: StationEvent + weight: 4 + duration: 120 + earliestStart: 10 + minimumPlayers: 40 + - type: CargoGiftsRule + description: cargo-gift-space-protection + dest: cargo-gift-dest-supp + gifts: + EmergencyInternalsLarge: 2 + EmergencyInflatablewall: 1 + EmergencyAdvancedKit: 1 + MedicalBiosuit: 1 + EmergencyO2Kit: 1 - type: entity id: GiftsFireProtection - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 4 - startDelay: 10 - duration: 120 - earliestStart: 20 - minimumPlayers: 40 - - type: CargoGiftsRule - description: cargo-gift-fire-protection - sender: cargo-gift-default-sender - dest: cargo-gift-dest-supp - gifts: - EmergencyFire: 2 - EmergencyBurnKit: 1 - EmergencyBruteKit: 1 + - type: StationEvent + weight: 4 + duration: 120 + earliestStart: 20 + minimumPlayers: 40 + - type: CargoGiftsRule + description: cargo-gift-fire-protection + dest: cargo-gift-dest-supp + gifts: + EmergencyFire: 2 + EmergencyBurnKit: 1 + EmergencyBruteKit: 1 - type: entity id: GiftsSecurityGuns - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 3 - startDelay: 10 - duration: 120 - earliestStart: 20 - minimumPlayers: 50 - - type: CargoGiftsRule - description: cargo-gift-security-guns - sender: cargo-gift-default-sender - dest: cargo-gift-dest-sec - gifts: - SecurityArmor: 3 - ArmorySmg: 1 - ArmoryShotgun: 1 - ArmoryLaser: 1 + - type: StationEvent + weight: 3 + duration: 120 + earliestStart: 20 + minimumPlayers: 50 + - type: CargoGiftsRule + description: cargo-gift-security-guns + dest: cargo-gift-dest-sec + gifts: + SecurityArmor: 3 + ArmorySmg: 1 + ArmoryShotgun: 1 + ArmoryLaser: 1 - type: entity id: GiftsSecurityRiot - parent: BaseGameRule + parent: CargoGiftsBase noSpawn: true components: - - type: StationEvent - weight: 4 - startDelay: 10 - duration: 120 - earliestStart: 20 - minimumPlayers: 50 - - type: CargoGiftsRule - description: cargo-gift-security-riot - sender: cargo-gift-default-sender - dest: cargo-gift-dest-sec - gifts: - SecurityRiot: 2 - SecurityRestraints: 2 - SecurityNonLethal: 2 + - type: StationEvent + weight: 4 + duration: 120 + earliestStart: 20 + minimumPlayers: 50 + - type: CargoGiftsRule + description: cargo-gift-security-riot + dest: cargo-gift-dest-sec + gifts: + SecurityRiot: 2 + SecurityRestraints: 2 + SecurityNonLethal: 2 diff --git a/Resources/Prototypes/GameRules/events.yml b/Resources/Prototypes/GameRules/events.yml index 4923e399dd0..e9e21824dcd 100644 --- a/Resources/Prototypes/GameRules/events.yml +++ b/Resources/Prototypes/GameRules/events.yml @@ -1,22 +1,63 @@ - type: entity - id: AnomalySpawn + id: BaseStationEvent + parent: BaseGameRule + abstract: true + noSpawn: true + components: + - type: GameRule + delay: + min: 10 + max: 20 + +- type: entity + id: BaseStationEventShortDelay + parent: BaseGameRule + abstract: true + noSpawn: true + components: + - type: GameRule + delay: + min: 10 + max: 20 + +- type: entity + id: BaseStationEventLongDelay parent: BaseGameRule + abstract: true + noSpawn: true + components: + - type: GameRule + delay: + min: 40 + max: 60 + +- type: entity + id: AnomalySpawn + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent + startAnnouncementColor: "#18abf5" + startAudio: + path: /Audio/Announcements/announce.ogg weight: 8 - startDelay: 30 duration: 35 - type: AnomalySpawnRule - type: entity id: BluespaceArtifact - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: + - type: GameRule + delay: + min: 30 + max: 30 - type: StationEvent + startAnnouncementColor: "#18abf5" + startAudio: + path: /Audio/Announcements/announce.ogg weight: 8 - startDelay: 30 duration: 35 - type: BluespaceArtifactRule @@ -49,7 +90,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-bureaucratic-error-announcement minimumPlayers: 25 weight: 3 duration: 1 @@ -61,7 +102,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-clerical-error-announcement minimumPlayers: 15 weight: 5 duration: 1 @@ -132,52 +173,50 @@ - type: entity id: GasLeak - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent - startAnnouncement: true - endAnnouncement: true + startAnnouncement: station-event-gas-leak-announcement + endAnnouncement: station-event-gas-leak-complete-announcement weight: 8 - startDelay: 20 - type: GasLeakRule - type: entity id: KudzuGrowth - parent: BaseGameRule + parent: BaseStationEventLongDelay noSpawn: true components: - type: StationEvent earliestStart: 15 minimumPlayers: 15 weight: 7 - startDelay: 50 duration: 240 - type: KudzuGrowthRule - type: entity id: MeteorSwarm - parent: BaseGameRule + parent: BaseStationEventLongDelay noSpawn: true components: - type: StationEvent earliestStart: 30 weight: 7.5 minimumPlayers: 10 #Enough to hopefully have at least one engineering guy - startAnnouncement: true - endAnnouncement: true + startAnnouncement: station-event-meteor-swarm-announcement + endAnnouncement: station-event-meteor-swarm-complete-announcement duration: null #ending is handled by MeteorSwarmRule - startDelay: 30 - type: MeteorSwarmRule - type: entity id: MouseMigration - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent - startAnnouncement: true - startDelay: 10 + startAnnouncement: station-event-vent-creatures-start-announcement + startAudio: + path: /Audio/Announcements/attention.ogg earliestStart: 15 weight: 6 duration: 50 @@ -197,12 +236,13 @@ - type: entity id: CockroachMigration - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent - startAnnouncement: true - startDelay: 10 + startAnnouncement: station-event-vent-creatures-start-announcement + startAudio: + path: /Audio/Announcements/attention.ogg weight: 6 duration: 50 - type: VentCrittersRule @@ -214,14 +254,17 @@ - type: entity id: PowerGridCheck - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent weight: 5 - startAnnouncement: true - endAnnouncement: true - startDelay: 24 + startAnnouncement: station-event-power-grid-check-start-announcement + endAnnouncement: station-event-power-grid-check-end-announcement + startAudio: + path: /Audio/Announcements/power_off.ogg + params: + volume: -4 duration: 60 maxDuration: 120 - type: PowerGridCheckRule @@ -244,8 +287,8 @@ components: - type: StationEvent weight: 8 - startAnnouncement: true - endAnnouncement: true + startAnnouncement: station-event-solar-flare-announcement + endAnnouncement: station-event-solar-flare-complete-announcement duration: 120 maxDuration: 240 - type: SolarFlareRule @@ -266,26 +309,26 @@ - type: entity id: VentClog - parent: BaseGameRule + parent: BaseStationEventLongDelay noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-vent-clog-announcement earliestStart: 15 minimumPlayers: 15 weight: 5 - startDelay: 50 duration: 60 - type: VentClogRule - type: entity id: SlimesSpawn - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent - startAnnouncement: true - startDelay: 10 + startAnnouncement: station-event-vent-creatures-start-announcement + startAudio: + path: /Audio/Announcements/attention.ogg earliestStart: 20 minimumPlayers: 15 weight: 5 @@ -301,12 +344,13 @@ - type: entity id: SpiderSpawn - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent - startAnnouncement: true - startDelay: 10 + startAnnouncement: station-event-vent-creatures-start-announcement + startAudio: + path: /Audio/Announcements/attention.ogg earliestStart: 20 minimumPlayers: 15 weight: 5 @@ -318,12 +362,13 @@ - type: entity id: SpiderClownSpawn - parent: BaseGameRule + parent: BaseStationEventShortDelay noSpawn: true components: - type: StationEvent - startAnnouncement: false - startDelay: 10 + startAnnouncement: station-event-vent-creatures-start-announcement + startAudio: + path: /Audio/Announcements/attention.ogg earliestStart: 20 minimumPlayers: 20 weight: 1.5 @@ -411,7 +456,6 @@ weight: 8 minimumPlayers: 15 reoccurrenceDelay: 30 - startAnnouncement: false - type: AlertLevelInterceptionRule - type: TraitorRule - type: AntagSelection @@ -447,7 +491,6 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: false weight: 2 duration: 1 earliestStart: 45 diff --git a/Resources/Prototypes/GameRules/unknown_shuttles.yml b/Resources/Prototypes/GameRules/unknown_shuttles.yml index f44bbdcaaab..c7f7712e541 100644 --- a/Resources/Prototypes/GameRules/unknown_shuttles.yml +++ b/Resources/Prototypes/GameRules/unknown_shuttles.yml @@ -4,7 +4,6 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: false weight: 5 reoccurrenceDelay: 30 duration: 1 @@ -17,7 +16,6 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: false weight: 5 reoccurrenceDelay: 30 duration: 1 @@ -30,7 +28,6 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: false weight: 5 reoccurrenceDelay: 30 duration: 1 @@ -43,7 +40,6 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: false weight: 2 reoccurrenceDelay: 30 duration: 1 @@ -56,7 +52,6 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: false weight: 2 reoccurrenceDelay: 30 duration: 1 diff --git a/Resources/Prototypes/Nyanotrasen/GameRules/events.yml b/Resources/Prototypes/Nyanotrasen/GameRules/events.yml index de9ea15a699..e40bcc93962 100644 --- a/Resources/Prototypes/Nyanotrasen/GameRules/events.yml +++ b/Resources/Prototypes/Nyanotrasen/GameRules/events.yml @@ -5,7 +5,7 @@ noSpawn: true components: - type: StationEvent - startAnnouncement: true + startAnnouncement: station-event-noospheric-storm-announcement weight: 5 earliestStart: 15 reoccurrenceDelay: 5 diff --git a/RobustToolbox b/RobustToolbox index a9aea7027f1..0f60ad9018f 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit a9aea7027f1840c83bcaf1c973caf099745f9eed +Subproject commit 0f60ad9018f54f9b49da1810bbffa01e2c5975f7