diff --git a/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs b/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs index f0201dc81b..ed15579937 100644 --- a/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs +++ b/Content.Client/Atmos/Monitor/UI/AirAlarmWindow.xaml.cs @@ -103,7 +103,7 @@ public sealed partial class AirAlarmWindow : FancyWindow _temperature.SetMarkup(Loc.GetString("air-alarm-ui-window-temperature", ("tempC", $"{TemperatureHelpers.KelvinToCelsius(state.TemperatureAverage):0.#}"), ("temperature", $"{state.TemperatureAverage:0.##}"))); _alarmState.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state", ("color", ColorForAlarm(state.AlarmType)), - ("state", $"{state.AlarmType}"))); + ("state", state.AlarmType))); UpdateModeSelector(state.Mode); UpdateAutoMode(state.AutoMode); foreach (var (addr, dev) in state.DeviceData) diff --git a/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml.cs b/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml.cs index f906bd3930..9e88b0bff4 100644 --- a/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml.cs +++ b/Content.Client/Atmos/Monitor/UI/Widgets/SensorInfo.xaml.cs @@ -27,11 +27,11 @@ public sealed partial class SensorInfo : BoxContainer _address = address; - SensorAddress.Title = $"{address} : {data.AlarmState}"; + SensorAddress.Title = Loc.GetString("air-alarm-ui-window-listing-title", ("address", _address), ("state", data.AlarmState)); AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator", ("color", AirAlarmWindow.ColorForAlarm(data.AlarmState)), - ("state", $"{data.AlarmState}"))); + ("state", data.AlarmState))); PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure-indicator", ("color", AirAlarmWindow.ColorForThreshold(data.Pressure, data.PressureThreshold)), ("pressure", $"{data.Pressure:0.##}"))); @@ -90,11 +90,11 @@ public sealed partial class SensorInfo : BoxContainer public void ChangeData(AtmosSensorData data) { - SensorAddress.Title = $"{_address} : {data.AlarmState}"; + SensorAddress.Title = Loc.GetString("air-alarm-ui-window-listing-title", ("address", _address), ("state", data.AlarmState)); AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator", ("color", AirAlarmWindow.ColorForAlarm(data.AlarmState)), - ("state", $"{data.AlarmState}"))); + ("state", data.AlarmState))); PressureLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-pressure-indicator", ("color", AirAlarmWindow.ColorForThreshold(data.Pressure, data.PressureThreshold)), diff --git a/Content.Client/Buckle/BuckleSystem.cs b/Content.Client/Buckle/BuckleSystem.cs index 40b2092a26..748f15922f 100644 --- a/Content.Client/Buckle/BuckleSystem.cs +++ b/Content.Client/Buckle/BuckleSystem.cs @@ -1,6 +1,7 @@ using Content.Client.Rotation; using Content.Shared.Buckle; using Content.Shared.Buckle.Components; +using Content.Shared.Movement.Systems; using Content.Shared.Rotation; using Robust.Client.GameObjects; using Robust.Client.Graphics; @@ -21,6 +22,15 @@ internal sealed class BuckleSystem : SharedBuckleSystem SubscribeLocalEvent(OnStrapMoveEvent); SubscribeLocalEvent(OnBuckledEvent); SubscribeLocalEvent(OnUnbuckledEvent); + SubscribeLocalEvent(OnMobCollide); + } + + private void OnMobCollide(Entity ent, ref AttemptMobCollideEvent args) + { + if (ent.Comp.Buckled) + { + args.Cancelled = true; + } } private void OnStrapMoveEvent(EntityUid uid, StrapComponent component, ref MoveEvent args) diff --git a/Content.Client/Clickable/ClickableSystem.cs b/Content.Client/Clickable/ClickableSystem.cs index 15d13df625..454bff4349 100644 --- a/Content.Client/Clickable/ClickableSystem.cs +++ b/Content.Client/Clickable/ClickableSystem.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Client.Sprite; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.Utility; @@ -17,12 +18,14 @@ public sealed class ClickableSystem : EntitySystem private EntityQuery _clickableQuery; private EntityQuery _xformQuery; + private EntityQuery _fadingSpriteQuery; public override void Initialize() { base.Initialize(); _clickableQuery = GetEntityQuery(); _xformQuery = GetEntityQuery(); + _fadingSpriteQuery = GetEntityQuery(); } /// @@ -34,7 +37,7 @@ public sealed class ClickableSystem : EntitySystem /// 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) + public bool CheckClick(Entity entity, Vector2 worldPos, IEye eye, bool excludeFaded, out int drawDepth, out uint renderOrder, out float bottom) { if (!_clickableQuery.Resolve(entity.Owner, ref entity.Comp1, false)) { @@ -52,6 +55,14 @@ public sealed class ClickableSystem : EntitySystem return false; } + if (excludeFaded && _fadingSpriteQuery.Resolve(entity.Owner, ref entity.Comp4, false)) + { + drawDepth = default; + renderOrder = default; + bottom = default; + return false; + } + var sprite = entity.Comp2; var transform = entity.Comp3; diff --git a/Content.Client/Gameplay/GameplayStateBase.cs b/Content.Client/Gameplay/GameplayStateBase.cs index 162c45d412..69e6e0b58b 100644 --- a/Content.Client/Gameplay/GameplayStateBase.cs +++ b/Content.Client/Gameplay/GameplayStateBase.cs @@ -113,18 +113,18 @@ namespace Content.Client.Gameplay return first.IsValid() ? first : null; } - public IEnumerable GetClickableEntities(EntityCoordinates coordinates) + public IEnumerable GetClickableEntities(EntityCoordinates coordinates, bool excludeFaded = true) { var transformSystem = _entitySystemManager.GetEntitySystem(); - return GetClickableEntities(transformSystem.ToMapCoordinates(coordinates)); + return GetClickableEntities(transformSystem.ToMapCoordinates(coordinates), excludeFaded); } - public IEnumerable GetClickableEntities(MapCoordinates coordinates) + public IEnumerable GetClickableEntities(MapCoordinates coordinates, bool excludeFaded = true) { - return GetClickableEntities(coordinates, _eyeManager.CurrentEye); + return GetClickableEntities(coordinates, _eyeManager.CurrentEye, excludeFaded); } - public IEnumerable GetClickableEntities(MapCoordinates coordinates, IEye? eye) + public IEnumerable GetClickableEntities(MapCoordinates coordinates, IEye? eye, bool excludeFaded = true) { /* * TODO: @@ -147,7 +147,7 @@ namespace Content.Client.Gameplay foreach (var entity in entities) { if (clickQuery.TryGetComponent(entity.Uid, out var component) && - clickables.CheckClick((entity.Uid, component, entity.Component, entity.Transform), coordinates.Position, eye, out var drawDepthClicked, out var renderOrder, out var bottom)) + clickables.CheckClick((entity.Uid, component, entity.Component, entity.Transform), coordinates.Position, eye, excludeFaded, out var drawDepthClicked, out var renderOrder, out var bottom)) { foundEntities.Add((entity.Uid, drawDepthClicked, renderOrder, bottom)); } diff --git a/Content.Client/Movement/Systems/MobCollisionSystem.cs b/Content.Client/Movement/Systems/MobCollisionSystem.cs new file mode 100644 index 0000000000..b7d464afab --- /dev/null +++ b/Content.Client/Movement/Systems/MobCollisionSystem.cs @@ -0,0 +1,42 @@ +using System.Numerics; +using Content.Shared.CCVar; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Robust.Client.Player; +using Robust.Shared.Physics.Components; +using Robust.Shared.Timing; + +namespace Content.Client.Movement.Systems; + +public sealed class MobCollisionSystem : SharedMobCollisionSystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly IPlayerManager _player = default!; + + public override void Update(float frameTime) + { + if (!CfgManager.GetCVar(CCVars.MovementMobPushing)) + return; + + if (_timing.IsFirstTimePredicted) + { + var player = _player.LocalEntity; + + if (MobQuery.TryComp(player, out var comp) && PhysicsQuery.TryComp(player, out var physics)) + { + HandleCollisions((player.Value, comp, physics), frameTime); + } + } + + base.Update(frameTime); + } + + protected override void RaiseCollisionEvent(EntityUid uid, Vector2 direction, float speedMod) + { + RaisePredictiveEvent(new MobCollisionMessage() + { + Direction = direction, + SpeedModifier = speedMod, + }); + } +} diff --git a/Content.Client/Physics/Controllers/MoverController.cs b/Content.Client/Physics/Controllers/MoverController.cs index d2ac0cdefd..37e3d83ddb 100644 --- a/Content.Client/Physics/Controllers/MoverController.cs +++ b/Content.Client/Physics/Controllers/MoverController.cs @@ -62,16 +62,16 @@ public sealed class MoverController : SharedMoverController private void OnRelayPlayerAttached(Entity entity, ref LocalPlayerAttachedEvent args) { - Physics.UpdateIsPredicted(entity.Owner); - Physics.UpdateIsPredicted(entity.Comp.RelayEntity); + PhysicsSystem.UpdateIsPredicted(entity.Owner); + PhysicsSystem.UpdateIsPredicted(entity.Comp.RelayEntity); if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover)) SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); } private void OnRelayPlayerDetached(Entity entity, ref LocalPlayerDetachedEvent args) { - Physics.UpdateIsPredicted(entity.Owner); - Physics.UpdateIsPredicted(entity.Comp.RelayEntity); + PhysicsSystem.UpdateIsPredicted(entity.Owner); + PhysicsSystem.UpdateIsPredicted(entity.Comp.RelayEntity); if (MoverQuery.TryGetComponent(entity.Comp.RelayEntity, out var inputMover)) SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); } diff --git a/Content.Client/Sprite/SpriteFadeSystem.cs b/Content.Client/Sprite/SpriteFadeSystem.cs index 3323dd660f..949012d04a 100644 --- a/Content.Client/Sprite/SpriteFadeSystem.cs +++ b/Content.Client/Sprite/SpriteFadeSystem.cs @@ -1,8 +1,14 @@ +using System.Numerics; using Content.Client.Gameplay; using Content.Shared.Sprite; using Robust.Client.GameObjects; +using Robust.Client.Input; using Robust.Client.Player; using Robust.Client.State; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface; +using Robust.Shared.Map; +using Robust.Shared.Physics.Systems; using Robust.Shared.Physics; namespace Content.Client.Sprite; @@ -17,6 +23,9 @@ public sealed class SpriteFadeSystem : EntitySystem [Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly IUserInterfaceManager _uiManager = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; private readonly HashSet _comps = new(); @@ -24,6 +33,11 @@ public sealed class SpriteFadeSystem : EntitySystem private EntityQuery _fadeQuery; private EntityQuery _fadingQuery; + /// + /// Radius of the mouse point for the intersection test + /// + private static Vector2 MouseRadius = new Vector2(10f * float.Epsilon, 10f * float.Epsilon); + private const float TargetAlpha = 0.4f; private const float ChangeRate = 1f; @@ -46,46 +60,82 @@ public sealed class SpriteFadeSystem : EntitySystem sprite.Color = sprite.Color.WithAlpha(component.OriginalAlpha); } - public override void FrameUpdate(float frameTime) + /// + /// Adds sprites to the fade set, and brings their alpha downwards + /// + private void FadeIn(float change) { - base.FrameUpdate(frameTime); - var player = _playerManager.LocalEntity; - var change = ChangeRate * frameTime; + // ExcludeBoundingBox is set if we don't want to fade this sprite within the collision bounding boxes for the given POI + var pointsOfInterest = new List<(MapCoordinates Point, bool ExcludeBoundingBox)>(); - if (TryComp(player, out TransformComponent? playerXform) && - _stateManager.CurrentState is GameplayState state && - _spriteQuery.TryGetComponent(player, out var playerSprite)) + if (_uiManager.CurrentlyHovered is IViewportControl vp + && _inputManager.MouseScreenPosition.IsValid) { - var mapPos = _transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: playerXform); + pointsOfInterest.Add((vp.PixelToMap(_inputManager.MouseScreenPosition.Position), true)); + } - // Also want to handle large entities even if they may not be clickable. - foreach (var ent in state.GetClickableEntities(mapPos)) + if (TryComp(player, out TransformComponent? playerXform)) + { + pointsOfInterest.Add((_transform.GetMapCoordinates(_playerManager.LocalEntity!.Value, xform: playerXform), false)); + } + + if (_stateManager.CurrentState is GameplayState state && _spriteQuery.TryGetComponent(player, out var playerSprite)) + { + foreach (var (mapPos, excludeBB) in pointsOfInterest) { - if (ent == player || - !_fadeQuery.HasComponent(ent) || - !_spriteQuery.TryGetComponent(ent, out var sprite) || - sprite.DrawDepth < playerSprite.DrawDepth) + // Also want to handle large entities even if they may not be clickable. + foreach (var ent in state.GetClickableEntities(mapPos, excludeFaded: false)) { - continue; - } + if (ent == player || + !_fadeQuery.HasComponent(ent) || + !_spriteQuery.TryGetComponent(ent, out var sprite) || + sprite.DrawDepth < playerSprite.DrawDepth) + { + continue; + } - if (!_fadingQuery.TryComp(ent, out var fading)) - { - fading = AddComp(ent); - fading.OriginalAlpha = sprite.Color.A; - } + if (excludeBB) + { + var test = new Box2Rotated(mapPos.Position - MouseRadius, mapPos.Position + MouseRadius); + var collided = false; + foreach (var fixture in _physics.GetCollidingEntities(mapPos.MapId, test)) + { + if (fixture.Owner == ent) + { + collided = true; + break; + } + } + if (collided) + { + break; + } + } - _comps.Add(fading); - var newColor = Math.Max(sprite.Color.A - change, TargetAlpha); + if (!_fadingQuery.TryComp(ent, out var fading)) + { + fading = AddComp(ent); + fading.OriginalAlpha = sprite.Color.A; + } - if (!sprite.Color.A.Equals(newColor)) - { - sprite.Color = sprite.Color.WithAlpha(newColor); + _comps.Add(fading); + var newColor = Math.Max(sprite.Color.A - change, TargetAlpha); + + if (!sprite.Color.A.Equals(newColor)) + { + sprite.Color = sprite.Color.WithAlpha(newColor); + } } } } + } + /// + /// Bring sprites back up to their original alpha if they aren't in the fade set, and removes their fade component when done + /// + private void FadeOut(float change) + { var query = AllEntityQuery(); while (query.MoveNext(out var uid, out var comp)) { @@ -106,6 +156,16 @@ public sealed class SpriteFadeSystem : EntitySystem RemCompDeferred(uid); } } + } + + public override void FrameUpdate(float frameTime) + { + base.FrameUpdate(frameTime); + + var change = ChangeRate * frameTime; + + FadeIn(change); + FadeOut(change); _comps.Clear(); } diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index d6513c0a78..92596f9dc0 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -16,6 +16,7 @@ using Robust.Client.State; using Robust.Shared.Configuration; using Robust.Shared.Containers; using Robust.Shared.Map; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Client.Verbs @@ -36,6 +37,8 @@ namespace Content.Client.Verbs private float _lookupSize; + private static readonly ProtoId HideContextMenuTag = "HideContextMenu"; + /// /// These flags determine what entities the user can see on the context menu. /// @@ -147,7 +150,7 @@ namespace Content.Client.Verbs for (var i = entities.Count - 1; i >= 0; i--) { - if (_tagSystem.HasTag(entities[i], "HideContextMenu")) + if (_tagSystem.HasTag(entities[i], HideContextMenuTag)) entities.RemoveSwap(i); } diff --git a/Content.IntegrationTests/PoolManager.Cvars.cs b/Content.IntegrationTests/PoolManager.Cvars.cs index 23f0ded7df..2c51bdbc3a 100644 --- a/Content.IntegrationTests/PoolManager.Cvars.cs +++ b/Content.IntegrationTests/PoolManager.Cvars.cs @@ -39,6 +39,7 @@ public static partial class PoolManager (CVars.NetBufferSize.Name, "0"), (CCVars.InteractionRateLimitCount.Name, "9999999"), (CCVars.InteractionRateLimitPeriod.Name, "0.1"), + (CCVars.MovementMobPushing.Name, "false"), }; public static async Task SetupCVars(RobustIntegrationTest.IntegrationInstance instance, PoolSettings settings) diff --git a/Content.IntegrationTests/Tests/ClickableTest.cs b/Content.IntegrationTests/Tests/ClickableTest.cs index 5983650908..e6d94a43f9 100644 --- a/Content.IntegrationTests/Tests/ClickableTest.cs +++ b/Content.IntegrationTests/Tests/ClickableTest.cs @@ -80,7 +80,7 @@ namespace Content.IntegrationTests.Tests var pos = clientEntManager.System().GetWorldPosition(clientEnt); - hit = clientEntManager.System().CheckClick((clientEnt, null, sprite, null), new Vector2(clickPosX, clickPosY) + pos, eye, out _, out _, out _); + hit = clientEntManager.System().CheckClick((clientEnt, null, sprite, null), new Vector2(clickPosX, clickPosY) + pos, eye, false, out _, out _, out _); }); await server.WaitPost(() => diff --git a/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs b/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs index cf88f3aacd..94dd98425b 100644 --- a/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs +++ b/Content.IntegrationTests/Tests/Commands/SuicideCommandTests.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Execution; @@ -52,7 +52,7 @@ public sealed class SuicideCommandTests name: test version of the material reclaimer components: - type: MaterialReclaimer"; - + private static readonly ProtoId CannotSuicideTag = "CannotSuicide"; /// /// Run the suicide command in the console /// Should successfully kill the player and ghost them @@ -201,7 +201,7 @@ public sealed class SuicideCommandTests mobStateComp = entManager.GetComponent(player); }); - tagSystem.AddTag(player, "CannotSuicide"); + tagSystem.AddTag(player, CannotSuicideTag); // Check that running the suicide command kills the player // and properly ghosts them without them being able to return to their body diff --git a/Content.Server/Access/Systems/AgentIDCardSystem.cs b/Content.Server/Access/Systems/AgentIDCardSystem.cs index a38aefce93..9ede128a5a 100644 --- a/Content.Server/Access/Systems/AgentIDCardSystem.cs +++ b/Content.Server/Access/Systems/AgentIDCardSystem.cs @@ -42,21 +42,9 @@ namespace Content.Server.Access.Systems access.Tags.UnionWith(targetAccess.Tags); var addedLength = access.Tags.Count - beforeLength; - if (addedLength == 0) - { - _popupSystem.PopupEntity(Loc.GetString("agent-id-no-new", ("card", args.Target)), args.Target.Value, args.User); - return; - } - - Dirty(uid, access); - - if (addedLength == 1) - { - _popupSystem.PopupEntity(Loc.GetString("agent-id-new-1", ("card", args.Target)), args.Target.Value, args.User); - return; - } - _popupSystem.PopupEntity(Loc.GetString("agent-id-new", ("number", addedLength), ("card", args.Target)), args.Target.Value, args.User); + if (addedLength > 0) + Dirty(uid, access); } private void AfterUIOpen(EntityUid uid, AgentIDCardComponent component, AfterActivatableUIOpenEvent args) diff --git a/Content.Server/Access/Systems/IdCardConsoleSystem.cs b/Content.Server/Access/Systems/IdCardConsoleSystem.cs index c8fbe2ba9d..a9e5d9a6d3 100644 --- a/Content.Server/Access/Systems/IdCardConsoleSystem.cs +++ b/Content.Server/Access/Systems/IdCardConsoleSystem.cs @@ -168,7 +168,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem /*TODO: ECS SharedIdCardConsoleComponent and then log on card ejection, together with the save. This current implementation is pretty shit as it logs 27 entries (27 lines) if someone decides to give themselves AA*/ - _adminLogger.Add(LogType.Action, LogImpact.High, + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(player):player} has modified {ToPrettyString(targetId):entity} with the following accesses: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]"); } diff --git a/Content.Server/Administration/Commands/ControlMob.cs b/Content.Server/Administration/Commands/ControlMob.cs index 8613fafeae..26cd83510b 100644 --- a/Content.Server/Administration/Commands/ControlMob.cs +++ b/Content.Server/Administration/Commands/ControlMob.cs @@ -43,5 +43,13 @@ namespace Content.Server.Administration.Commands _entities.System().ControlMob(player.UserId, target.Value); } + + public CompletionResult GetCompletion(IConsoleShell shell, string[] args) + { + if (args.Length != 1) + return CompletionResult.Empty; + + return CompletionResult.FromOptions(CompletionHelper.NetEntities(args[0], entManager: _entities)); + } } } diff --git a/Content.Server/Botany/Systems/PlantHolderSystem.cs b/Content.Server/Botany/Systems/PlantHolderSystem.cs index 15cb82ef00..5dbafae5af 100644 --- a/Content.Server/Botany/Systems/PlantHolderSystem.cs +++ b/Content.Server/Botany/Systems/PlantHolderSystem.cs @@ -50,6 +50,9 @@ public sealed class PlantHolderSystem : EntitySystem public const float HydroponicsSpeedMultiplier = 1f; public const float HydroponicsConsumptionMultiplier = 2f; + private static readonly ProtoId HoeTag = "Hoe"; + private static readonly ProtoId PlantSampleTakerTag = "PlantSampleTaker"; + public override void Initialize() { base.Initialize(); @@ -203,7 +206,7 @@ public sealed class PlantHolderSystem : EntitySystem return; } - if (_tagSystem.HasTag(args.Used, "Hoe")) + if (_tagSystem.HasTag(args.Used, HoeTag)) { args.Handled = true; if (component.WeedLevel > 0) @@ -243,7 +246,7 @@ public sealed class PlantHolderSystem : EntitySystem return; } - if (_tagSystem.HasTag(args.Used, "PlantSampleTaker")) + if (_tagSystem.HasTag(args.Used, PlantSampleTakerTag)) { args.Handled = true; if (component.Seed == null) diff --git a/Content.Server/Chat/SuicideSystem.cs b/Content.Server/Chat/SuicideSystem.cs index 4eda532385..b9a3ede2bd 100644 --- a/Content.Server/Chat/SuicideSystem.cs +++ b/Content.Server/Chat/SuicideSystem.cs @@ -4,6 +4,7 @@ using Content.Shared.Chat; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.Hands.Components; +using Content.Shared.IdentityManagement; using Content.Shared.Interaction.Events; using Content.Shared.Item; using Content.Shared.Mind; @@ -13,6 +14,7 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Popups; using Content.Shared.Tag; using Robust.Shared.Player; +using Robust.Shared.Prototypes; namespace Content.Server.Chat; @@ -26,6 +28,8 @@ public sealed class SuicideSystem : EntitySystem [Dependency] private readonly GhostSystem _ghostSystem = default!; [Dependency] private readonly SharedSuicideSystem _suicide = default!; + private static readonly ProtoId CannotSuicideTag = "CannotSuicide"; + public override void Initialize() { base.Initialize(); @@ -59,7 +63,7 @@ public sealed class SuicideSystem : EntitySystem // Suicide is considered a fail if the user wasn't able to ghost // Suiciding with the CannotSuicide tag will ghost the player but not kill the body - if (!suicideGhostEvent.Handled || _tagSystem.HasTag(victim, "CannotSuicide")) + if (!suicideGhostEvent.Handled || _tagSystem.HasTag(victim, CannotSuicideTag)) return false; var suicideEvent = new SuicideEvent(victim); @@ -94,7 +98,7 @@ public sealed class SuicideSystem : EntitySystem // CannotSuicide tag will allow the user to ghost, but also return to their mind // This is kind of weird, not sure what it applies to? - if (_tagSystem.HasTag(victim, "CannotSuicide")) + if (_tagSystem.HasTag(victim, CannotSuicideTag)) args.CanReturnToBody = true; if (_ghostSystem.OnGhostAttempt(victim.Comp.Mind.Value, args.CanReturnToBody, mind: mindComponent)) @@ -149,7 +153,7 @@ public sealed class SuicideSystem : EntitySystem if (args.Handled) return; - var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", victim)); + var othersMessage = Loc.GetString("suicide-command-default-text-others", ("name", Identity.Entity(victim, EntityManager))); _popup.PopupEntity(othersMessage, victim, Filter.PvsExcept(victim), true); var selfMessage = Loc.GetString("suicide-command-default-text-self"); diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs index f15edcf067..503a0ebde6 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnEventSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Projectiles; using Content.Shared.Tag; using Content.Shared.Weapons.Melee.Events; using Robust.Shared.Collections; +using Robust.Shared.Prototypes; namespace Content.Server.Chemistry.EntitySystems; @@ -24,6 +25,8 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; [Dependency] private readonly TagSystem _tag = default!; + private static readonly ProtoId HardsuitTag = "Hardsuit"; + public override void Initialize() { base.Initialize(); @@ -93,7 +96,7 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem // Yuck, this is way to hardcodey for my tastes // TODO blocking injection with a hardsuit should probably done with a cancellable event or something - if (!injector.Comp.PierceArmor && _inventory.TryGetSlotEntity(target, "outerClothing", out var suit) && _tag.HasTag(suit.Value, "Hardsuit")) + if (!injector.Comp.PierceArmor && _inventory.TryGetSlotEntity(target, "outerClothing", out var suit) && _tag.HasTag(suit.Value, HardsuitTag)) { // Only show popup to attacker if (source != null) diff --git a/Content.Server/Connection/IPIntel/IPIntel.cs b/Content.Server/Connection/IPIntel/IPIntel.cs index 51a0b74089..a2e7bc580c 100644 --- a/Content.Server/Connection/IPIntel/IPIntel.cs +++ b/Content.Server/Connection/IPIntel/IPIntel.cs @@ -259,7 +259,7 @@ public sealed class IPIntel { _chatManager.SendAdminAlert(Loc.GetString("admin-alert-ipintel-warning", ("player", username), - ("percent", Math.Round(score)))); + ("percent", score))); } if (!decisionIsReject) @@ -269,7 +269,7 @@ public sealed class IPIntel { _chatManager.SendAdminAlert(Loc.GetString("admin-alert-ipintel-blocked", ("player", username), - ("percent", Math.Round(score)))); + ("percent", score))); } return _rejectBad ? (true, Loc.GetString("ipintel-suspicious")) : (false, string.Empty); diff --git a/Content.Server/Construction/Commands/FixRotationsCommand.cs b/Content.Server/Construction/Commands/FixRotationsCommand.cs index 3232f12ed8..807f81b498 100644 --- a/Content.Server/Construction/Commands/FixRotationsCommand.cs +++ b/Content.Server/Construction/Commands/FixRotationsCommand.cs @@ -5,6 +5,7 @@ using Content.Shared.Construction; using Content.Shared.Tag; using Robust.Shared.Console; using Robust.Shared.Map.Components; +using Robust.Shared.Prototypes; namespace Content.Server.Construction.Commands; @@ -13,6 +14,10 @@ public sealed class FixRotationsCommand : IConsoleCommand { [Dependency] private readonly IEntityManager _entManager = default!; + private static readonly ProtoId ForceFixRotationsTag = "ForceFixRotations"; + private static readonly ProtoId ForceNoFixRotationsTag = "ForceNoFixRotations"; + private static readonly ProtoId DiagonalTag = "Diagonal"; + // ReSharper disable once StringLiteralTypo public string Command => "fixrotations"; public string Description => "Sets the rotation of all occluders, low walls and windows to south."; @@ -86,11 +91,11 @@ public sealed class FixRotationsCommand : IConsoleCommand // cables valid |= _entManager.HasComponent(child); // anything else that might need this forced - valid |= tagSystem.HasTag(child, "ForceFixRotations"); + valid |= tagSystem.HasTag(child, ForceFixRotationsTag); // override - valid &= !tagSystem.HasTag(child, "ForceNoFixRotations"); + valid &= !tagSystem.HasTag(child, ForceNoFixRotationsTag); // remove diagonal entities as well - valid &= !tagSystem.HasTag(child, "Diagonal"); + valid &= !tagSystem.HasTag(child, DiagonalTag); if (!valid) continue; diff --git a/Content.Server/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs b/Content.Server/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs index b0181a0adc..04e08fac50 100644 --- a/Content.Server/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs +++ b/Content.Server/CriminalRecords/Systems/CriminalRecordsHackerSystem.cs @@ -4,7 +4,7 @@ using Content.Server.StationRecords.Systems; using Content.Shared.CriminalRecords; using Content.Shared.CriminalRecords.Components; using Content.Shared.CriminalRecords.Systems; -using Content.Shared.Dataset; +using Content.Shared.Random.Helpers; using Content.Shared.Security; using Content.Shared.StationRecords; using Robust.Shared.Prototypes; @@ -36,10 +36,10 @@ public sealed class CriminalRecordsHackerSystem : SharedCriminalRecordsHackerSys if (_station.GetOwningStation(ent) is not {} station) return; - var reasons = _proto.Index(ent.Comp.Reasons); + var reasons = _proto.Index(ent.Comp.Reasons); foreach (var (key, record) in _records.GetRecordsOfType(station)) { - var reason = _random.Pick(reasons.Values); + var reason = _random.Pick(reasons); _criminalRecords.OverwriteStatus(new StationRecordKey(key, station), record, SecurityStatus.Wanted, reason); // no radio message since spam // no history since lazy and its easy to remove anyway diff --git a/Content.Server/Destructible/Thresholds/Behaviors/BurnBodyBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/BurnBodyBehavior.cs index f0499dc6a2..1d3c1993f9 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/BurnBodyBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/BurnBodyBehavior.cs @@ -1,4 +1,5 @@ using Content.Shared.Body.Components; +using Content.Shared.IdentityManagement; using Content.Shared.Inventory; using Content.Shared.Popups; using JetBrains.Annotations; @@ -25,7 +26,8 @@ public sealed partial class BurnBodyBehavior : IThresholdBehavior } } - sharedPopupSystem.PopupCoordinates(Loc.GetString("bodyburn-text-others", ("name", bodyId)), transformSystem.GetMoverCoordinates(bodyId), PopupType.LargeCaution); + var bodyIdentity = Identity.Entity(bodyId, system.EntityManager); + sharedPopupSystem.PopupCoordinates(Loc.GetString("bodyburn-text-others", ("name", bodyIdentity)), transformSystem.GetMoverCoordinates(bodyId), PopupType.LargeCaution); system.EntityManager.QueueDeleteEntity(bodyId); } diff --git a/Content.Server/Dragon/DragonRiftSystem.cs b/Content.Server/Dragon/DragonRiftSystem.cs index 998834835e..9cab018fd7 100644 --- a/Content.Server/Dragon/DragonRiftSystem.cs +++ b/Content.Server/Dragon/DragonRiftSystem.cs @@ -13,6 +13,7 @@ using Robust.Shared.Serialization.Manager; using System.Numerics; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.GameStates; using Robust.Shared.Utility; namespace Content.Server.Dragon; @@ -33,11 +34,20 @@ public sealed class DragonRiftSystem : EntitySystem { base.Initialize(); + SubscribeLocalEvent(OnGetState); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnAnchorChange); SubscribeLocalEvent(OnShutdown); } + private void OnGetState(Entity ent, ref ComponentGetState args) + { + args.State = new DragonRiftComponentState + { + State = ent.Comp.State, + }; + } + public override void Update(float frameTime) { base.Update(frameTime); diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index eb10f8d280..c7adb311d3 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -62,6 +62,8 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem [ValidatePrototypeId] private const string DamageType = "Shock"; + private static readonly ProtoId WindowTag = "Window"; + // Multiply and shift the log scale for shock damage. private const float RecursiveDamageMultiplier = 0.75f; private const float RecursiveTimeMultiplier = 0.8f; @@ -139,7 +141,7 @@ public sealed class ElectrocutionSystem : SharedElectrocutionSystem { foreach (var entity in _entityLookup.GetLocalEntitiesIntersecting(tileRef.Value, flags: LookupFlags.StaticSundries)) { - if (_tag.HasTag(entity, "Window")) + if (_tag.HasTag(entity, WindowTag)) return false; } } diff --git a/Content.Server/Flash/FlashSystem.cs b/Content.Server/Flash/FlashSystem.cs index fb449a372c..60c09efaea 100644 --- a/Content.Server/Flash/FlashSystem.cs +++ b/Content.Server/Flash/FlashSystem.cs @@ -21,6 +21,7 @@ using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Random; using InventoryComponent = Content.Shared.Inventory.InventoryComponent; +using Robust.Shared.Prototypes; namespace Content.Server.Flash { @@ -39,6 +40,8 @@ namespace Content.Server.Flash [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; + private static readonly ProtoId TrashTag = "Trash"; + public override void Initialize() { base.Initialize(); @@ -94,7 +97,7 @@ namespace Content.Server.Flash if (_charges.IsEmpty(uid, charges)) { _appearance.SetData(uid, FlashVisuals.Burnt, true); - _tag.AddTag(uid, "Trash"); + _tag.AddTag(uid, TrashTag); _popup.PopupEntity(Loc.GetString("flash-component-becomes-empty"), user); } diff --git a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs index 984334ee8e..f0eb12c34d 100644 --- a/Content.Server/Forensics/Systems/ForensicScannerSystem.cs +++ b/Content.Server/Forensics/Systems/ForensicScannerSystem.cs @@ -16,6 +16,7 @@ using Robust.Shared.Audio; using Robust.Shared.Player; using Robust.Shared.Timing; using Content.Server.Chemistry.Containers.EntitySystems; +using Robust.Shared.Prototypes; // todo: remove this stinky LINQy namespace Content.Server.Forensics @@ -33,6 +34,8 @@ namespace Content.Server.Forensics [Dependency] private readonly ForensicsSystem _forensicsSystem = default!; [Dependency] private readonly TagSystem _tag = default!; + private static readonly ProtoId DNASolutionScannableTag = "DNASolutionScannable"; + public override void Initialize() { base.Initialize(); @@ -86,7 +89,7 @@ namespace Content.Server.Forensics scanner.Residues = forensics.Residues.ToList(); } - if (_tag.HasTag(args.Args.Target.Value, "DNASolutionScannable")) + if (_tag.HasTag(args.Args.Target.Value, DNASolutionScannableTag)) { scanner.SolutionDNAs = _forensicsSystem.GetSolutionsDNA(args.Args.Target.Value); } else diff --git a/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs b/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs index 81ad2b1be7..4990b98b91 100644 --- a/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Survivor.Components; using Content.Shared.Tag; using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; namespace Content.Server.GameTicking.Rules; @@ -22,6 +23,8 @@ public sealed class SurvivorRuleSystem : GameRuleSystem [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly MobStateSystem _mobState = default!; + private static readonly ProtoId InvalidForSurvivorAntagTag = "InvalidForSurvivorAntag"; + public override void Initialize() { base.Initialize(); @@ -44,7 +47,7 @@ public sealed class SurvivorRuleSystem : GameRuleSystem var mind = humanMind.Owner; var ent = humanMind.Comp.OwnedEntity.Value; - if (HasComp(mind) || _tag.HasTag(mind, "InvalidForSurvivorAntag")) + if (HasComp(mind) || _tag.HasTag(mind, InvalidForSurvivorAntagTag)) continue; EnsureComp(mind); diff --git a/Content.Server/Ghost/GhostSystem.cs b/Content.Server/Ghost/GhostSystem.cs index e7125ee4f2..435584e908 100644 --- a/Content.Server/Ghost/GhostSystem.cs +++ b/Content.Server/Ghost/GhostSystem.cs @@ -72,6 +72,8 @@ namespace Content.Server.Ghost private EntityQuery _ghostQuery; private EntityQuery _physicsQuery; + private static readonly ProtoId AllowGhostShownByEventTag = "AllowGhostShownByEvent"; + public override void Initialize() { base.Initialize(); @@ -403,7 +405,7 @@ namespace Content.Server.Ghost var entityQuery = EntityQueryEnumerator(); while (entityQuery.MoveNext(out var uid, out var _, out var vis)) { - if (!_tag.HasTag(uid, "AllowGhostShownByEvent")) + if (!_tag.HasTag(uid, AllowGhostShownByEventTag)) continue; if (visible) diff --git a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs index 7f0778cbdd..4f71cc4721 100644 --- a/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs @@ -73,6 +73,9 @@ namespace Content.Server.Kitchen.EntitySystems [ValidatePrototypeId] private const string MalfunctionSpark = "Spark"; + private static readonly ProtoId MetalTag = "Metal"; + private static readonly ProtoId PlasticTag = "Plastic"; + public override void Initialize() { base.Initialize(); @@ -550,12 +553,12 @@ namespace Content.Server.Kitchen.EntitySystems return; } - if (_tag.HasTag(item, "Metal")) + if (_tag.HasTag(item, MetalTag)) { malfunctioning = true; } - if (_tag.HasTag(item, "Plastic")) + if (_tag.HasTag(item, PlasticTag)) { var junk = Spawn(component.BadRecipeEntityId, Transform(uid).Coordinates); _container.Insert(junk, component.Storage); diff --git a/Content.Server/Light/EntitySystems/ExpendableLightSystem.cs b/Content.Server/Light/EntitySystems/ExpendableLightSystem.cs index b56da84f03..7aacf3e7ad 100644 --- a/Content.Server/Light/EntitySystems/ExpendableLightSystem.cs +++ b/Content.Server/Light/EntitySystems/ExpendableLightSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.Verbs; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Server.Light.EntitySystems @@ -24,6 +25,8 @@ namespace Content.Server.Light.EntitySystems [Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; + private static readonly ProtoId TrashTag = "Trash"; + public override void Initialize() { base.Initialize(); @@ -69,7 +72,7 @@ namespace Content.Server.Light.EntitySystems _metaData.SetEntityName(ent, Loc.GetString(component.SpentName), meta); _metaData.SetEntityDescription(ent, Loc.GetString(component.SpentDesc), meta); - _tagSystem.AddTag(ent, "Trash"); + _tagSystem.AddTag(ent, TrashTag); UpdateSounds(ent); UpdateVisualizer(ent); diff --git a/Content.Server/Magic/MagicSystem.cs b/Content.Server/Magic/MagicSystem.cs index 34c12954c6..dafd88dd5f 100644 --- a/Content.Server/Magic/MagicSystem.cs +++ b/Content.Server/Magic/MagicSystem.cs @@ -16,6 +16,8 @@ public sealed class MagicSystem : SharedMagicSystem [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly SharedMindSystem _mind = default!; + private static readonly ProtoId InvalidForSurvivorAntagTag = "InvalidForSurvivorAntag"; + public override void Initialize() { base.Initialize(); @@ -48,8 +50,8 @@ public sealed class MagicSystem : SharedMagicSystem if (!ev.MakeSurvivorAntagonist) return; - if (_mind.TryGetMind(ev.Performer, out var mind, out _) && !_tag.HasTag(mind, "InvalidForSurvivorAntag")) - _tag.AddTag(mind, "InvalidForSurvivorAntag"); + if (_mind.TryGetMind(ev.Performer, out var mind, out _) && !_tag.HasTag(mind, InvalidForSurvivorAntagTag)) + _tag.AddTag(mind, InvalidForSurvivorAntagTag); EntProtoId survivorRule = "Survivor"; diff --git a/Content.Server/MagicMirror/MagicMirrorSystem.cs b/Content.Server/MagicMirror/MagicMirrorSystem.cs index 082fc81bd2..37302999ed 100644 --- a/Content.Server/MagicMirror/MagicMirrorSystem.cs +++ b/Content.Server/MagicMirror/MagicMirrorSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.MagicMirror; using Content.Shared.Popups; using Content.Shared.Tag; using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; namespace Content.Server.MagicMirror; @@ -27,6 +28,8 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly TagSystem _tagSystem = default!; + private static readonly ProtoId HidesHairTag = "HidesHair"; + public override void Initialize() { base.Initialize(); @@ -59,7 +62,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem _popup.PopupEntity( component.Target == message.Actor ? Loc.GetString("magic-mirror-blocked-by-hat-self") - : Loc.GetString("magic-mirror-blocked-by-hat-self-target"), + : Loc.GetString("magic-mirror-blocked-by-hat-self-target", ("target", Identity.Entity(message.Actor, EntityManager))), message.Actor, message.Actor, PopupType.Medium); @@ -95,7 +98,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem } else { - _popup.PopupEntity(Loc.GetString("magic-mirror-change-slot-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium); + _popup.PopupEntity(Loc.GetString("magic-mirror-change-slot-target", ("user", Identity.Entity(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium); } component.DoAfter = doAfterId; @@ -175,7 +178,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem } else { - _popup.PopupEntity(Loc.GetString("magic-mirror-change-color-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium); + _popup.PopupEntity(Loc.GetString("magic-mirror-change-color-target", ("user", Identity.Entity(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium); } component.DoAfter = doAfterId; @@ -253,7 +256,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem } else { - _popup.PopupEntity(Loc.GetString("magic-mirror-remove-slot-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium); + _popup.PopupEntity(Loc.GetString("magic-mirror-remove-slot-target", ("user", Identity.Entity(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium); } component.DoAfter = doAfterId; @@ -331,7 +334,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem } else { - _popup.PopupEntity(Loc.GetString("magic-mirror-add-slot-target", ("user", Identity.Name(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium); + _popup.PopupEntity(Loc.GetString("magic-mirror-add-slot-target", ("user", Identity.Entity(message.Actor, EntityManager))), component.Target.Value, component.Target.Value, PopupType.Medium); } component.DoAfter = doAfterId; @@ -391,7 +394,7 @@ public sealed class MagicMirrorSystem : SharedMagicMirrorSystem var slots = _inventory.GetSlotEnumerator((target, inventoryComp), SlotFlags.WITHOUT_POCKET); while (slots.MoveNext(out var slot)) { - if (slot.ContainedEntity != null && _tagSystem.HasTag(slot.ContainedEntity.Value, "HidesHair")) + if (slot.ContainedEntity != null && _tagSystem.HasTag(slot.ContainedEntity.Value, HidesHairTag)) { return true; } diff --git a/Content.Server/Movement/Systems/MobCollisionSystem.cs b/Content.Server/Movement/Systems/MobCollisionSystem.cs new file mode 100644 index 0000000000..2badac5676 --- /dev/null +++ b/Content.Server/Movement/Systems/MobCollisionSystem.cs @@ -0,0 +1,51 @@ +using System.Numerics; +using Content.Shared.CCVar; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Robust.Shared.Player; + +namespace Content.Server.Movement.Systems; + +public sealed class MobCollisionSystem : SharedMobCollisionSystem +{ + private EntityQuery _actorQuery; + + public override void Initialize() + { + base.Initialize(); + _actorQuery = GetEntityQuery(); + SubscribeLocalEvent(OnServerMobCollision); + } + + private void OnServerMobCollision(Entity ent, ref MobCollisionMessage args) + { + MoveMob((ent.Owner, ent.Comp, Transform(ent.Owner)), args.Direction, args.SpeedModifier); + } + + public override void Update(float frameTime) + { + if (!CfgManager.GetCVar(CCVars.MovementMobPushing)) + return; + + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var comp)) + { + if (_actorQuery.HasComp(uid) || !PhysicsQuery.TryComp(uid, out var physics)) + continue; + + HandleCollisions((uid, comp, physics), frameTime); + } + + base.Update(frameTime); + } + + protected override void RaiseCollisionEvent(EntityUid uid, Vector2 direction, float speedMod) + { + RaiseLocalEvent(uid, new MobCollisionMessage() + { + Direction = direction, + SpeedModifier = speedMod, + }); + } +} diff --git a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs index e64343fdd8..aeedb326e9 100644 --- a/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs +++ b/Content.Server/NPC/HTN/PrimitiveTasks/Operators/MoveToOperator.cs @@ -86,7 +86,7 @@ public sealed partial class MoveToOperator : HTNOperator, IHtnConditionalShutdow return (false, null); if (!_entManager.TryGetComponent(xform.GridUid, out var ownerGrid) || - !_entManager.TryGetComponent(targetCoordinates.GetGridUid(_entManager), out var targetGrid)) + !_entManager.TryGetComponent(_transform.GetGrid(targetCoordinates), out var targetGrid)) { return (false, null); } @@ -155,8 +155,8 @@ public sealed partial class MoveToOperator : HTNOperator, IHtnConditionalShutdow { if (blackboard.TryGetValue(NPCBlackboard.OwnerCoordinates, out var coordinates, _entManager)) { - var mapCoords = coordinates.ToMap(_entManager, _transform); - _steering.PrunePath(uid, mapCoords, targetCoordinates.ToMapPos(_entManager, _transform) - mapCoords.Position, result.Path); + var mapCoords = _transform.ToMapCoordinates(coordinates); + _steering.PrunePath(uid, mapCoords, _transform.ToMapCoordinates(targetCoordinates).Position - mapCoords.Position, result.Path); } comp.CurrentPath = new Queue(result.Path); diff --git a/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs b/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs index f4af65c617..7105bda0a2 100644 --- a/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs +++ b/Content.Server/NPC/Pathfinding/PathfindingSystem.Grid.cs @@ -2,10 +2,6 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Threading; using System.Threading.Tasks; -using Content.Server.Destructible; -using Content.Shared.Access.Components; -using Content.Shared.Climbing.Components; -using Content.Shared.Doors.Components; using Content.Shared.NPC; using Content.Shared.Physics; using Robust.Shared.Collections; @@ -281,7 +277,7 @@ public sealed partial class PathfindingSystem var gridUid = ev.Component.GridUid; var oldGridUid = ev.OldPosition.EntityId == ev.NewPosition.EntityId ? gridUid - : ev.OldPosition.GetGridUid(EntityManager); + : _transform.GetGrid((ev.Entity.Owner, ev.Component)); if (oldGridUid != null && oldGridUid != gridUid) { @@ -395,7 +391,7 @@ public sealed partial class PathfindingSystem private Vector2i GetOrigin(EntityCoordinates coordinates, EntityUid gridUid) { - var localPos = Vector2.Transform(coordinates.ToMapPos(EntityManager, _transform), _transform.GetInvWorldMatrix(gridUid)); + var localPos = Vector2.Transform(_transform.ToMapCoordinates(coordinates).Position, _transform.GetInvWorldMatrix(gridUid)); return new Vector2i((int) Math.Floor(localPos.X / ChunkSize), (int) Math.Floor(localPos.Y / ChunkSize)); } diff --git a/Content.Server/NPC/Systems/NPCSteeringSystem.cs b/Content.Server/NPC/Systems/NPCSteeringSystem.cs index a8124c0249..78610be77e 100644 --- a/Content.Server/NPC/Systems/NPCSteeringSystem.cs +++ b/Content.Server/NPC/Systems/NPCSteeringSystem.cs @@ -461,7 +461,7 @@ public sealed partial class NPCSteeringSystem : SharedNPCSteeringSystem return; } - var targetPos = steering.Coordinates.ToMap(EntityManager, _transform); + var targetPos = _transform.ToMapCoordinates(steering.Coordinates); var ourPos = _transform.GetMapCoordinates(uid, xform: xform); PrunePath(uid, ourPos, targetPos.Position - ourPos.Position, result.Path); diff --git a/Content.Server/Nutrition/EntitySystems/TrashOnSolutionEmptySystem.cs b/Content.Server/Nutrition/EntitySystems/TrashOnSolutionEmptySystem.cs index b2e12036f9..ea3a8be9cb 100644 --- a/Content.Server/Nutrition/EntitySystems/TrashOnSolutionEmptySystem.cs +++ b/Content.Server/Nutrition/EntitySystems/TrashOnSolutionEmptySystem.cs @@ -3,6 +3,7 @@ using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Tag; +using Robust.Shared.Prototypes; namespace Content.Server.Nutrition.EntitySystems { @@ -11,6 +12,8 @@ namespace Content.Server.Nutrition.EntitySystems [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly TagSystem _tagSystem = default!; + private static readonly ProtoId TrashTag = "Trash"; + public override void Initialize() { base.Initialize(); @@ -41,11 +44,11 @@ namespace Content.Server.Nutrition.EntitySystems { if (solution.Volume <= 0) { - _tagSystem.AddTag(entity.Owner, "Trash"); + _tagSystem.AddTag(entity.Owner, TrashTag); return; } - if (_tagSystem.HasTag(entity.Owner, "Trash")) - _tagSystem.RemoveTag(entity.Owner, "Trash"); + + _tagSystem.RemoveTag(entity.Owner, TrashTag); } } } diff --git a/Content.Server/Payload/EntitySystems/PayloadSystem.cs b/Content.Server/Payload/EntitySystems/PayloadSystem.cs index f5159bf223..bf562f747f 100644 --- a/Content.Server/Payload/EntitySystems/PayloadSystem.cs +++ b/Content.Server/Payload/EntitySystems/PayloadSystem.cs @@ -11,6 +11,7 @@ using Robust.Shared.Serialization.Manager; using Robust.Shared.Utility; using System.Linq; using Robust.Server.GameObjects; +using Robust.Shared.Prototypes; namespace Content.Server.Payload.EntitySystems; @@ -23,6 +24,8 @@ public sealed class PayloadSystem : EntitySystem [Dependency] private readonly IComponentFactory _componentFactory = default!; [Dependency] private readonly ISerializationManager _serializationManager = default!; + private static readonly ProtoId PayloadTag = "Payload"; + public override void Initialize() { base.Initialize(); @@ -44,7 +47,7 @@ public sealed class PayloadSystem : EntitySystem { foreach (var entity in container.ContainedEntities) { - if (_tagSystem.HasTag(entity, "Payload")) + if (_tagSystem.HasTag(entity, PayloadTag)) yield return entity; } } @@ -71,7 +74,7 @@ public sealed class PayloadSystem : EntitySystem return; // Ensure we don't enter a trigger-loop - DebugTools.Assert(!_tagSystem.HasTag(uid, "Payload")); + DebugTools.Assert(!_tagSystem.HasTag(uid, PayloadTag)); RaiseLocalEvent(parent, args, false); } diff --git a/Content.Server/Procedural/DungeonJob/DungeonJob.PostGen.cs b/Content.Server/Procedural/DungeonJob/DungeonJob.PostGen.cs index b1c83346d8..84e7563f33 100644 --- a/Content.Server/Procedural/DungeonJob/DungeonJob.PostGen.cs +++ b/Content.Server/Procedural/DungeonJob/DungeonJob.PostGen.cs @@ -1,8 +1,10 @@ using System.Numerics; using Content.Shared.Procedural; +using Content.Shared.Tag; using Robust.Shared.Collections; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; +using Robust.Shared.Prototypes; namespace Content.Server.Procedural.DungeonJob; @@ -12,13 +14,15 @@ public sealed partial class DungeonJob * Run after the main dungeon generation */ + private static readonly ProtoId WallTag = "Wall"; + private bool HasWall(Vector2i tile) { var anchored = _maps.GetAnchoredEntitiesEnumerator(_gridUid, _grid, tile); while (anchored.MoveNext(out var uid)) { - if (_tags.HasTag(uid.Value, "Wall")) + if (_tags.HasTag(uid.Value, WallTag)) return true; } diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index 1eb9aabed6..51cbb6d4d5 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -29,6 +29,7 @@ using Robust.Shared.Physics.Components; using Robust.Shared.Utility; using Robust.Shared.Map.Components; using Content.Shared.Whitelist; +using Robust.Shared.Prototypes; namespace Content.Server.Revenant.EntitySystems; @@ -44,6 +45,8 @@ public sealed partial class RevenantSystem [Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; + private static readonly ProtoId WindowTag = "Window"; + private void InitializeAbilities() { SubscribeLocalEvent(OnInteract); @@ -253,7 +256,7 @@ public sealed partial class RevenantSystem foreach (var ent in lookup) { //break windows - if (tags.HasComponent(ent) && _tag.HasTag(ent, "Window")) + if (tags.HasComponent(ent) && _tag.HasTag(ent, WindowTag)) { //hardcoded damage specifiers til i die. var dspec = new DamageSpecifier(); diff --git a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs index f02ea945d0..478b002e58 100644 --- a/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs +++ b/Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs @@ -21,6 +21,7 @@ using Robust.Shared.GameStates; using Robust.Shared.Map; using Robust.Shared.Utility; using Content.Shared.UserInterface; +using Robust.Shared.Prototypes; namespace Content.Server.Shuttles.Systems; @@ -43,6 +44,8 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem private readonly HashSet> _consoles = new(); + private static readonly ProtoId CanPilotTag = "CanPilot"; + public override void Initialize() { base.Initialize(); @@ -168,7 +171,7 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem private bool TryPilot(EntityUid user, EntityUid uid) { - if (!_tags.HasTag(user, "CanPilot") || + if (!_tags.HasTag(user, CanPilotTag) || !TryComp(uid, out var component) || !this.IsPowered(uid, EntityManager) || !Transform(uid).Anchored || diff --git a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs index 729328b8bd..68543cc175 100644 --- a/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs +++ b/Content.Server/Singularity/EntitySystems/EventHorizonSystem.cs @@ -14,6 +14,7 @@ using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Events; using Robust.Shared.Physics.Systems; +using Robust.Shared.Prototypes; using Robust.Shared.Timing; namespace Content.Server.Singularity.EntitySystems; @@ -36,6 +37,8 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem [Dependency] private readonly TagSystem _tagSystem = default!; #endregion Dependencies + private static readonly ProtoId HighRiskItemTag = "HighRiskItem"; + private EntityQuery _physicsQuery; public override void Initialize() @@ -127,7 +130,7 @@ public sealed class EventHorizonSystem : SharedEventHorizonSystem return; if (HasComp(morsel) - || _tagSystem.HasTag(morsel, "HighRiskItem") + || _tagSystem.HasTag(morsel, HighRiskItemTag) || HasComp(morsel)) { _adminLogger.Add(LogType.EntityDelete, LogImpact.High, $"{ToPrettyString(morsel):player} entered the event horizon of {ToPrettyString(hungry)} and was deleted"); diff --git a/Content.Server/Tools/Innate/InnateToolSystem.cs b/Content.Server/Tools/Innate/InnateToolSystem.cs index e7e5be38c4..b8d1dd935c 100644 --- a/Content.Server/Tools/Innate/InnateToolSystem.cs +++ b/Content.Server/Tools/Innate/InnateToolSystem.cs @@ -8,6 +8,7 @@ using Content.Shared.Interaction.Components; using Content.Shared.Storage; using Content.Shared.Tag; using Robust.Shared.Network; +using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.Tools.Innate; @@ -22,6 +23,8 @@ public sealed class InnateToolSystem : EntitySystem [Dependency] private readonly SharedHandsSystem _sharedHandsSystem = default!; [Dependency] private readonly TagSystem _tagSystem = default!; + private static readonly ProtoId InnateDontDeleteTag = "InnateDontDelete"; + public override void Initialize() { base.Initialize(); @@ -76,7 +79,7 @@ public sealed class InnateToolSystem : EntitySystem { foreach (var tool in component.ToolUids) { - if (_tagSystem.HasTag(tool, "InnateDontDelete")) + if (_tagSystem.HasTag(tool, InnateDontDeleteTag)) { RemComp(tool); } diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index 47d94984c0..155796481b 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -36,6 +36,7 @@ using Content.Shared.Traits.Assorted; using Robust.Shared.Audio.Systems; using Content.Shared.Ghost.Roles.Components; using Content.Shared.Tag; +using Robust.Shared.Prototypes; namespace Content.Server.Zombies; @@ -61,6 +62,8 @@ public sealed partial class ZombieSystem [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly NameModifierSystem _nameMod = default!; + private static readonly ProtoId InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell"; + /// /// Handles an entity turning into a zombie when they die or go into crit /// @@ -290,6 +293,6 @@ public sealed partial class ZombieSystem //Need to prevent them from getting an item, they have no hands. // Also prevents them from becoming a Survivor. They're undead. - _tag.AddTag(target, "InvalidForGlobalSpawnSpell"); + _tag.AddTag(target, InvalidForGlobalSpawnSpellTag); } } diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index 84de549b66..74cf74274d 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -12,6 +12,7 @@ using Robust.Shared.Containers; using Robust.Shared.GameStates; using Content.Shared.GameTicking; using Content.Shared.IdentityManagement; +using Content.Shared.Tag; using Robust.Shared.Collections; using Robust.Shared.Prototypes; using Robust.Shared.Timing; @@ -24,11 +25,14 @@ public sealed class AccessReaderSystem : EntitySystem [Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly EmagSystem _emag = default!; + [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly SharedGameTicker _gameTicker = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [Dependency] private readonly SharedStationRecordsSystem _recordsSystem = default!; + private static readonly ProtoId PreventAccessLoggingTag = "PreventAccessLogging"; + public override void Initialize() { base.Initialize(); @@ -115,13 +119,13 @@ public sealed class AccessReaderSystem : EntitySystem var access = FindAccessTags(user, accessSources); FindStationRecordKeys(user, out var stationKeys, accessSources); - if (IsAllowed(access, stationKeys, target, reader)) - { - LogAccess((target, reader), user); - return true; - } + if (!IsAllowed(access, stationKeys, target, reader)) + return false; - return false; + if (!_tag.HasTag(user, PreventAccessLoggingTag)) + LogAccess((target, reader), user); + + return true; } public bool GetMainAccessReader(EntityUid uid, [NotNullWhen(true)] out Entity? ent) diff --git a/Content.Shared/CCVar/CCVars.Movement.cs b/Content.Shared/CCVar/CCVars.Movement.cs new file mode 100644 index 0000000000..39b9836c40 --- /dev/null +++ b/Content.Shared/CCVar/CCVars.Movement.cs @@ -0,0 +1,59 @@ +using Content.Shared.Administration; +using Content.Shared.CCVar.CVarAccess; +using Robust.Shared.Configuration; + +namespace Content.Shared.CCVar; + +public sealed partial class CCVars +{ + /// + /// Is mob pushing enabled. + /// + [CVarControl(AdminFlags.VarEdit)] + public static readonly CVarDef MovementMobPushing = + CVarDef.Create("movement.mob_pushing", false, CVar.SERVER | CVar.REPLICATED); + + /// + /// Can we push mobs not moving. + /// + [CVarControl(AdminFlags.VarEdit)] + public static readonly CVarDef MovementPushingStatic = + CVarDef.Create("movement.pushing_static", true, CVar.SERVER | CVar.REPLICATED); + + /// + /// Dot product for the pushed entity's velocity to a target entity's velocity before it gets moved. + /// + [CVarControl(AdminFlags.VarEdit)] + public static readonly CVarDef MovementPushingVelocityProduct = + CVarDef.Create("movement.pushing_velocity_product", -1f, CVar.SERVER | CVar.REPLICATED); + + /// + /// Cap for how much an entity can be pushed per second. + /// + [CVarControl(AdminFlags.VarEdit)] + public static readonly CVarDef MovementPushingCap = + CVarDef.Create("movement.pushing_cap", 100f, CVar.SERVER | CVar.REPLICATED); + + /// + /// Minimum pushing impulse per tick. If the value is below this it rounds to 0. + /// This is an optimisation to avoid pushing small values that won't actually move the mobs. + /// + [CVarControl(AdminFlags.VarEdit)] + public static readonly CVarDef MovementMinimumPush = + CVarDef.Create("movement.minimum_push", 0.1f, CVar.SERVER | CVar.REPLICATED); + + // Really this just exists because hot reloading is cooked on rider. + /// + /// Penetration depth cap for considering mob collisions. + /// + [CVarControl(AdminFlags.VarEdit)] + public static readonly CVarDef MovementPenetrationCap = + CVarDef.Create("movement.penetration_cap", 0.3f, CVar.SERVER | CVar.REPLICATED); + + /// + /// Based on the mass difference multiplies the push amount by this proportionally. + /// + [CVarControl(AdminFlags.VarEdit)] + public static readonly CVarDef MovementPushMassCap = + CVarDef.Create("movement.push_mass_cap", 1.75f, CVar.SERVER | CVar.REPLICATED); +} diff --git a/Content.Shared/CCVar/CCVars.Physics.cs b/Content.Shared/CCVar/CCVars.Physics.cs index 379676b5df..32f81f023d 100644 --- a/Content.Shared/CCVar/CCVars.Physics.cs +++ b/Content.Shared/CCVar/CCVars.Physics.cs @@ -15,13 +15,4 @@ public sealed partial class CCVars public static readonly CVarDef StopSpeed = CVarDef.Create("physics.stop_speed", 0.1f, CVar.ARCHIVE | CVar.REPLICATED | CVar.SERVER); - - /// - /// Whether mobs can push objects like lockers. - /// - /// - /// Technically client doesn't need to know about it but this may prevent a bug in the distant future so it stays. - /// - public static readonly CVarDef MobPushing = - CVarDef.Create("physics.mob_pushing", false, CVar.REPLICATED | CVar.SERVER); } diff --git a/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs index 725b034766..f996c65fe8 100644 --- a/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs @@ -23,6 +23,8 @@ public abstract class SharedChameleonClothingSystem : EntitySystem [Dependency] private readonly TagSystem _tag = default!; [Dependency] protected readonly SharedUserInterfaceSystem UI = default!; + private static readonly ProtoId WhitelistChameleonTag = "WhitelistChameleon"; + public override void Initialize() { base.Initialize(); @@ -124,7 +126,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem return false; // check if it is marked as valid chameleon target - if (!proto.TryGetComponent(out TagComponent? tag, _factory) || !_tag.HasTag(tag, "WhitelistChameleon")) + if (!proto.TryGetComponent(out TagComponent? tag, _factory) || !_tag.HasTag(tag, WhitelistChameleonTag)) return false; if (requiredTag != null && !_tag.HasTag(tag, requiredTag)) diff --git a/Content.Shared/Construction/Conditions/NoWindowsInTile.cs b/Content.Shared/Construction/Conditions/NoWindowsInTile.cs index 3ae3b59362..2d37ecebe6 100644 --- a/Content.Shared/Construction/Conditions/NoWindowsInTile.cs +++ b/Content.Shared/Construction/Conditions/NoWindowsInTile.cs @@ -1,7 +1,8 @@ -using Content.Shared.Maps; +using Content.Shared.Maps; using Content.Shared.Tag; using JetBrains.Annotations; using Robust.Shared.Map; +using Robust.Shared.Prototypes; namespace Content.Shared.Construction.Conditions { @@ -9,6 +10,8 @@ namespace Content.Shared.Construction.Conditions [DataDefinition] public sealed partial class NoWindowsInTile : IConstructionCondition { + private static readonly ProtoId WindowTag = "Window"; + public bool Condition(EntityUid user, EntityCoordinates location, Direction direction) { var entManager = IoCManager.Resolve(); @@ -17,7 +20,7 @@ namespace Content.Shared.Construction.Conditions foreach (var entity in location.GetEntitiesInTile(LookupFlags.Static)) { - if (tagSystem.HasTag(entity, "Window")) + if (tagSystem.HasTag(entity, WindowTag)) return false; } diff --git a/Content.Shared/Construction/Conditions/WallmountCondition.cs b/Content.Shared/Construction/Conditions/WallmountCondition.cs index f1d056165e..f32cc19eea 100644 --- a/Content.Shared/Construction/Conditions/WallmountCondition.cs +++ b/Content.Shared/Construction/Conditions/WallmountCondition.cs @@ -6,6 +6,7 @@ using JetBrains.Annotations; using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Shared.Construction.Conditions @@ -14,6 +15,8 @@ namespace Content.Shared.Construction.Conditions [DataDefinition] public sealed partial class WallmountCondition : IConstructionCondition { + private static readonly ProtoId WallTag = "Wall"; + public bool Condition(EntityUid user, EntityCoordinates location, Direction direction) { var entManager = IoCManager.Resolve(); @@ -42,7 +45,7 @@ namespace Content.Shared.Construction.Conditions var tagSystem = entManager.System(); var userToObjRaycastResults = physics.IntersectRayWithPredicate(entManager.GetComponent(user).MapID, rUserToObj, maxLength: length, - predicate: (e) => !tagSystem.HasTag(e, "Wall")); + predicate: (e) => !tagSystem.HasTag(e, WallTag)); var targetWall = userToObjRaycastResults.FirstOrNull(); @@ -53,7 +56,7 @@ namespace Content.Shared.Construction.Conditions // check that we didn't try to build wallmount that facing another adjacent wall var rAdjWall = new CollisionRay(objWorldPosition, directionWithOffset.Normalized(), (int) CollisionGroup.Impassable); var adjWallRaycastResults = physics.IntersectRayWithPredicate(entManager.GetComponent(user).MapID, rAdjWall, maxLength: 0.5f, - predicate: e => e == targetWall.Value.HitEntity || !tagSystem.HasTag(e, "Wall")); + predicate: e => e == targetWall.Value.HitEntity || !tagSystem.HasTag(e, WallTag)); return !adjWallRaycastResults.Any(); } diff --git a/Content.Shared/Delivery/SharedDeliverySystem.cs b/Content.Shared/Delivery/SharedDeliverySystem.cs index 52c9db40a1..319bf41fce 100644 --- a/Content.Shared/Delivery/SharedDeliverySystem.cs +++ b/Content.Shared/Delivery/SharedDeliverySystem.cs @@ -12,6 +12,7 @@ using Content.Shared.Tag; using Content.Shared.Verbs; using Robust.Shared.Audio.Systems; using Robust.Shared.Containers; +using Robust.Shared.Prototypes; namespace Content.Shared.Delivery; @@ -30,6 +31,9 @@ public abstract class SharedDeliverySystem : EntitySystem [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly NameModifierSystem _nameModifier = default!; + private static readonly ProtoId TrashTag = "Trash"; + private static readonly ProtoId RecyclableTag = "Recyclable"; + public override void Initialize() { base.Initialize(); @@ -129,7 +133,7 @@ public abstract class SharedDeliverySystem : EntitySystem ent.Comp.IsOpened = true; _appearance.SetData(ent, DeliveryVisuals.IsTrash, ent.Comp.IsOpened); - _tag.AddTags(ent, "Trash", "Recyclable"); + _tag.AddTags(ent, TrashTag, RecyclableTag); EnsureComp(ent); RemComp(ent); // opened mail should not count for the objective diff --git a/Content.Shared/DoAfter/SharedDoAfterSystem.cs b/Content.Shared/DoAfter/SharedDoAfterSystem.cs index c246c2844a..9765bac912 100644 --- a/Content.Shared/DoAfter/SharedDoAfterSystem.cs +++ b/Content.Shared/DoAfter/SharedDoAfterSystem.cs @@ -5,6 +5,7 @@ using Content.Shared.Damage; using Content.Shared.Hands.Components; using Content.Shared.Tag; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -23,6 +24,8 @@ public abstract partial class SharedDoAfterSystem : EntitySystem /// private static readonly TimeSpan ExcessTime = TimeSpan.FromSeconds(0.5f); + private static readonly ProtoId InstantDoAftersTag = "InstantDoAfters"; + public override void Initialize() { base.Initialize(); @@ -233,7 +236,7 @@ public abstract partial class SharedDoAfterSystem : EntitySystem // TODO DO AFTER // Why does this tag exist? Just make this a bool on the component? - if (args.Delay <= TimeSpan.Zero || _tag.HasTag(args.User, "InstantDoAfters")) + if (args.Delay <= TimeSpan.Zero || _tag.HasTag(args.User, InstantDoAftersTag)) { RaiseDoAfterEvents(doAfter, comp); // We don't store instant do-afters. This is just a lazy way of hiding them from client-side visuals. diff --git a/Content.Shared/Follower/FollowerSystem.cs b/Content.Shared/Follower/FollowerSystem.cs index 243886dbb7..75310a6737 100644 --- a/Content.Shared/Follower/FollowerSystem.cs +++ b/Content.Shared/Follower/FollowerSystem.cs @@ -18,6 +18,7 @@ using Robust.Shared.Network; using Robust.Shared.Physics; using Robust.Shared.Physics.Systems; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Shared.Follower; @@ -32,6 +33,8 @@ public sealed class FollowerSystem : EntitySystem [Dependency] private readonly INetManager _netMan = default!; [Dependency] private readonly ISharedAdminManager _adminManager = default!; + private static readonly ProtoId ForceableFollowTag = "ForceableFollow"; + public override void Initialize() { base.Initialize(); @@ -106,7 +109,7 @@ public sealed class FollowerSystem : EntitySystem ev.Verbs.Add(verb); } - if (_tagSystem.HasTag(ev.Target, "ForceableFollow")) + if (_tagSystem.HasTag(ev.Target, ForceableFollowTag)) { if (!ev.CanAccess || !ev.CanInteract) return; diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs index 223c2d4a37..95773697db 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs @@ -6,12 +6,16 @@ using Content.Shared.Inventory.VirtualItem; using Content.Shared.Tag; using Robust.Shared.Containers; using Robust.Shared.Map; +using Robust.Shared.Prototypes; namespace Content.Shared.Hands.EntitySystems; public abstract partial class SharedHandsSystem { [Dependency] private readonly TagSystem _tagSystem = default!; + + private static readonly ProtoId BypassDropChecksTag = "BypassDropChecks"; + private void InitializeDrop() { SubscribeLocalEvent(HandleEntityRemoved); @@ -37,7 +41,7 @@ public abstract partial class SharedHandsSystem private bool ShouldIgnoreRestrictions(EntityUid user) { //Checks if the Entity is something that shouldn't care about drop distance or walls ie Aghost - return !_tagSystem.HasTag(user, "BypassDropChecks"); + return !_tagSystem.HasTag(user, BypassDropChecksTag); } /// diff --git a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs index bb166b3c5c..e23357448f 100644 --- a/Content.Shared/Implants/SharedSubdermalImplantSystem.cs +++ b/Content.Shared/Implants/SharedSubdermalImplantSystem.cs @@ -7,6 +7,7 @@ using Content.Shared.Tag; using JetBrains.Annotations; using Robust.Shared.Containers; using Robust.Shared.Network; +using Robust.Shared.Prototypes; using System.Linq; namespace Content.Shared.Implants; @@ -21,6 +22,9 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem public const string BaseStorageId = "storagebase"; + private static readonly ProtoId MicroBombTag = "MicroBomb"; + private static readonly ProtoId MacroBombTag = "MacroBomb"; + public override void Initialize() { SubscribeLocalEvent(OnInsert); @@ -43,11 +47,11 @@ public abstract class SharedSubdermalImplantSystem : EntitySystem } //replace micro bomb with macro bomb - if (_container.TryGetContainer(component.ImplantedEntity.Value, ImplanterComponent.ImplantSlotId, out var implantContainer) && _tag.HasTag(uid, "MacroBomb")) + if (_container.TryGetContainer(component.ImplantedEntity.Value, ImplanterComponent.ImplantSlotId, out var implantContainer) && _tag.HasTag(uid, MacroBombTag)) { foreach (var implant in implantContainer.ContainedEntities) { - if (_tag.HasTag(implant, "MicroBomb")) + if (_tag.HasTag(implant, MicroBombTag)) { _container.Remove(implant, implantContainer); QueueDel(implant); diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 2f09f3e549..ff9f41e1f9 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -37,6 +37,7 @@ using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Systems; using Robust.Shared.Player; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -87,6 +88,8 @@ namespace Content.Shared.Interaction public const float MaxRaycastRange = 100f; public const string RateLimitKey = "Interaction"; + private static readonly ProtoId BypassInteractionRangeChecksTag = "BypassInteractionRangeChecks"; + public delegate bool Ignored(EntityUid entity); public override void Initialize() @@ -318,7 +321,7 @@ namespace Content.Shared.Interaction { // This is for Admin/mapping convenience. If ever there are other ghosts that can still interact, this check // might need to be more selective. - return !_tagSystem.HasTag(user, "BypassInteractionRangeChecks"); + return !_tagSystem.HasTag(user, BypassInteractionRangeChecksTag); } /// diff --git a/Content.Shared/Magic/SharedMagicSystem.cs b/Content.Shared/Magic/SharedMagicSystem.cs index b502ff2fbb..9a44407e91 100644 --- a/Content.Shared/Magic/SharedMagicSystem.cs +++ b/Content.Shared/Magic/SharedMagicSystem.cs @@ -65,6 +65,8 @@ public abstract class SharedMagicSystem : EntitySystem [Dependency] private readonly SharedMindSystem _mind = default!; [Dependency] private readonly SharedStunSystem _stun = default!; + private static readonly ProtoId InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell"; + public override void Initialize() { base.Initialize(); @@ -484,7 +486,7 @@ public abstract class SharedMagicSystem : EntitySystem var ent = human.Comp.OwnedEntity.Value; - if (_tag.HasTag(ent, "InvalidForGlobalSpawnSpell")) + if (_tag.HasTag(ent, InvalidForGlobalSpawnSpellTag)) continue; var mapCoords = _transform.GetMapCoordinates(ent); diff --git a/Content.Shared/Movement/Components/MobCollisionComponent.cs b/Content.Shared/Movement/Components/MobCollisionComponent.cs new file mode 100644 index 0000000000..437cdfd409 --- /dev/null +++ b/Content.Shared/Movement/Components/MobCollisionComponent.cs @@ -0,0 +1,60 @@ +using System.Numerics; +using Content.Shared.Movement.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Movement.Components; + +/// +/// Handles mobs pushing against each other. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(fieldDeltas: true)] +public sealed partial class MobCollisionComponent : Component +{ + // If you want to tweak the feel of the pushing use SpeedModifier and Strength. + // Strength goes both ways and affects how much the other mob is pushed by so controls static pushing a lot. + // Speed mod affects your own mob primarily. + + /// + /// Is this mob currently colliding? Used for SpeedModifier. + /// + [DataField, AutoNetworkedField] + public bool Colliding; + + // TODO: I hate this but also I couldn't quite figure out a way to avoid having to dirty it every tick. + // The issue is it's a time target that changes constantly so we can't just use a timespan. + // However that doesn't mean it should be modified every tick if we're still colliding. + + /// + /// Buffer time for to keep applying after the entities are no longer colliding. + /// Without this you will get jittering unless you are very specific with your values. + /// + [DataField, AutoNetworkedField] + public float BufferAccumulator = SharedMobCollisionSystem.BufferTime; + + /// + /// The speed modifier for mobs currently pushing. + /// By setting this low you can ensure you don't have to set the push-strength too high if you can push static entities. + /// + [DataField, AutoNetworkedField] + public float SpeedModifier = 1f; + + [DataField, AutoNetworkedField] + public float MinimumSpeedModifier = 0.35f; + + /// + /// Strength of the pushback for entities. This is combined between the 2 entities being pushed. + /// + [DataField, AutoNetworkedField] + public float Strength = 50f; + + // Yes I know, I will deal with it if I ever refactor collision layers due to misuse. + // If anything it probably needs some assurance on mobcollisionsystem for it. + /// + /// Fixture to listen to for mob collisions. + /// + [DataField, AutoNetworkedField] + public string FixtureId = "flammable"; + + [DataField, AutoNetworkedField] + public Vector2 Direction; +} diff --git a/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs b/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs new file mode 100644 index 0000000000..bcc1fd6d04 --- /dev/null +++ b/Content.Shared/Movement/Systems/SharedMobCollisionSystem.cs @@ -0,0 +1,338 @@ +using System.Numerics; +using Content.Shared.CCVar; +using Content.Shared.Movement.Components; +using Robust.Shared; +using Robust.Shared.Configuration; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Components; +using Robust.Shared.Physics.Systems; +using Robust.Shared.Random; +using Robust.Shared.Serialization; +using Robust.Shared.Utility; + +namespace Content.Shared.Movement.Systems; + +public abstract class SharedMobCollisionSystem : EntitySystem +{ + [Dependency] protected readonly IConfigurationManager CfgManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MovementSpeedModifierSystem _moveMod = default!; + [Dependency] protected readonly SharedPhysicsSystem Physics = default!; + [Dependency] private readonly SharedTransformSystem _xformSystem = default!; + + protected EntityQuery MobQuery; + protected EntityQuery PhysicsQuery; + + /// + /// + /// + private float _pushingCap; + + /// + /// + /// + private float _pushingDotProduct; + + /// + /// + /// + private float _minimumPushSquared = 0.01f; + + private float _penCap; + + /// + /// Time after we stop colliding with another mob before adjusting the movespeedmodifier. + /// This is required so if we stop colliding for a frame we don't fully reset and get jerky movement. + /// + public const float BufferTime = 0.2f; + + private float _massDiffCap; + + public override void Initialize() + { + base.Initialize(); + + UpdatePushCap(); + Subs.CVar(CfgManager, CVars.NetTickrate, _ => UpdatePushCap()); + Subs.CVar(CfgManager, CCVars.MovementMinimumPush, val => _minimumPushSquared = val * val, true); + Subs.CVar(CfgManager, CCVars.MovementPenetrationCap, val => _penCap = val, true); + Subs.CVar(CfgManager, CCVars.MovementPushingCap, _ => UpdatePushCap()); + Subs.CVar(CfgManager, CCVars.MovementPushingVelocityProduct, + value => + { + _pushingDotProduct = value; + }, true); + Subs.CVar(CfgManager, CCVars.MovementPushMassCap, val => _massDiffCap = val, true); + + MobQuery = GetEntityQuery(); + PhysicsQuery = GetEntityQuery(); + SubscribeAllEvent(OnCollision); + SubscribeLocalEvent(OnMoveModifier); + + UpdatesBefore.Add(typeof(SharedPhysicsSystem)); + } + + private void UpdatePushCap() + { + _pushingCap = (1f / CfgManager.GetCVar(CVars.NetTickrate)) * CfgManager.GetCVar(CCVars.MovementPushingCap); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = AllEntityQuery(); + + while (query.MoveNext(out var uid, out var comp)) + { + if (!comp.Colliding) + continue; + + comp.BufferAccumulator -= frameTime; + DirtyField(uid, comp, nameof(MobCollisionComponent.BufferAccumulator)); + var direction = comp.Direction; + + if (comp.BufferAccumulator <= 0f) + { + SetColliding((uid, comp), false, 1f); + } + // Apply the mob collision; if it's too low ignore it (e.g. if mob friction would overcome it). + // This is so we don't spam velocity changes every tick. It's not that expensive for physics but + // avoids the networking side. + else if (direction != Vector2.Zero && PhysicsQuery.TryComp(uid, out var physics)) + { + DebugTools.Assert(direction.LengthSquared() >= _minimumPushSquared); + + if (direction.Length() > _pushingCap) + { + direction = direction.Normalized() * _pushingCap; + } + + Physics.ApplyLinearImpulse(uid, direction * physics.Mass, body: physics); + comp.Direction = Vector2.Zero; + DirtyField(uid, comp, nameof(MobCollisionComponent.Direction)); + } + } + } + + private void OnMoveModifier(Entity ent, ref RefreshMovementSpeedModifiersEvent args) + { + if (!ent.Comp.Colliding) + return; + + args.ModifySpeed(ent.Comp.SpeedModifier); + } + + private void SetColliding(Entity entity, bool value, float speedMod) + { + if (value) + { + entity.Comp.BufferAccumulator = BufferTime; + DirtyField(entity.Owner, entity.Comp, nameof(MobCollisionComponent.BufferAccumulator)); + } + else + { + DebugTools.Assert(speedMod.Equals(1f)); + } + + if (entity.Comp.Colliding != value) + { + entity.Comp.Colliding = value; + DirtyField(entity.Owner, entity.Comp, nameof(MobCollisionComponent.Colliding)); + } + + if (!entity.Comp.SpeedModifier.Equals(speedMod)) + { + entity.Comp.SpeedModifier = speedMod; + _moveMod.RefreshMovementSpeedModifiers(entity.Owner); + DirtyField(entity.Owner, entity.Comp, nameof(MobCollisionComponent.SpeedModifier)); + } + } + + private void OnCollision(MobCollisionMessage msg, EntitySessionEventArgs args) + { + var player = args.SenderSession.AttachedEntity; + + if (!MobQuery.TryComp(player, out var comp)) + return; + + var xform = Transform(player.Value); + + // If not parented directly to a grid then fail it. + if (xform.ParentUid != xform.GridUid && xform.ParentUid != xform.MapUid) + return; + + var direction = msg.Direction; + + MoveMob((player.Value, comp, xform), direction, msg.SpeedModifier); + } + + protected void MoveMob(Entity entity, Vector2 direction, float speedMod) + { + // Length too short to do anything. + var pushing = true; + + if (direction.LengthSquared() < _minimumPushSquared) + { + pushing = false; + direction = Vector2.Zero; + speedMod = 1f; + } + else if (float.IsNaN(direction.X) || float.IsNaN(direction.Y)) + { + direction = Vector2.Zero; + } + + speedMod = Math.Clamp(speedMod, 0f, 1f); + + SetColliding(entity, pushing, speedMod); + + if (direction == entity.Comp1.Direction) + return; + + entity.Comp1.Direction = direction; + DirtyField(entity.Owner, entity.Comp1, nameof(MobCollisionComponent.Direction)); + } + + protected bool HandleCollisions(Entity entity, float frameTime) + { + var physics = entity.Comp2; + + if (physics.ContactCount == 0) + return false; + + var ourVelocity = entity.Comp2.LinearVelocity; + + if (ourVelocity == Vector2.Zero && !CfgManager.GetCVar(CCVars.MovementPushingStatic)) + return false; + + var xform = Transform(entity.Owner); + + if (xform.ParentUid != xform.GridUid && xform.ParentUid != xform.MapUid) + return false; + + var ev = new AttemptMobCollideEvent(); + + RaiseLocalEvent(entity.Owner, ref ev); + + if (ev.Cancelled) + return false; + + var (worldPos, worldRot) = _xformSystem.GetWorldPositionRotation(xform); + var ourTransform = new Transform(worldPos, worldRot); + var contacts = Physics.GetContacts(entity.Owner); + var direction = Vector2.Zero; + var contactCount = 0; + var ourMass = physics.FixturesMass; + var speedMod = 1f; + + while (contacts.MoveNext(out var contact)) + { + if (!contact.IsTouching) + continue; + + var ourFixture = contact.OurFixture(entity.Owner); + + if (ourFixture.Id != entity.Comp1.FixtureId) + continue; + + var other = contact.OtherEnt(entity.Owner); + + if (!MobQuery.TryComp(other, out var otherComp) || !PhysicsQuery.TryComp(other, out var otherPhysics)) + continue; + + var velocityProduct = Vector2.Dot(ourVelocity, otherPhysics.LinearVelocity); + + // If we're moving opposite directions for example then ignore (based on cvar). + if (velocityProduct < _pushingDotProduct) + { + continue; + } + + var targetEv = new AttemptMobTargetCollideEvent(); + RaiseLocalEvent(other, ref targetEv); + + if (targetEv.Cancelled) + continue; + + // TODO: More robust overlap detection. + var otherTransform = Physics.GetPhysicsTransform(other); + var diff = ourTransform.Position - otherTransform.Position; + + if (diff == Vector2.Zero) + { + diff = _random.NextVector2(0.01f); + } + + // 0.7 for 0.35 + 0.35 for mob bounds (see TODO above). + // Clamp so we don't get a heap of penetration depth and suddenly lurch other mobs. + // This is also so we don't have to trigger the speed-cap above. + // Maybe we just do speedcap and dump this? Though it's less configurable and the cap is just there for cheaters. + var penDepth = Math.Clamp(0.7f - diff.Length(), 0f, _penCap); + + // Sum the strengths so we get pushes back the same amount (impulse-wise, ignoring prediction). + var mobMovement = penDepth * diff.Normalized() * (entity.Comp1.Strength + otherComp.Strength); + + // Big mob push smaller mob, needs fine-tuning and potentially another co-efficient. + if (_massDiffCap > 0f) + { + var modifier = Math.Clamp( + otherPhysics.FixturesMass / ourMass, + 1f / _massDiffCap, + _massDiffCap); + + mobMovement *= modifier; + + var speedReduction = 1f - entity.Comp1.MinimumSpeedModifier; + var speedModifier = Math.Clamp( + 1f - speedReduction * modifier, + entity.Comp1.MinimumSpeedModifier, 1f); + + speedMod = MathF.Min(speedModifier, 1f); + } + + // Need the push strength proportional to penetration depth. + direction += mobMovement; + contactCount++; + } + + if (direction == Vector2.Zero) + { + return contactCount > 0; + } + + direction *= frameTime; + RaiseCollisionEvent(entity.Owner, direction, speedMod); + return true; + } + + protected abstract void RaiseCollisionEvent(EntityUid uid, Vector2 direction, float speedmodifier); + + /// + /// Raised from client -> server indicating mob push direction OR server -> server for NPC mob pushes. + /// + [Serializable, NetSerializable] + protected sealed class MobCollisionMessage : EntityEventArgs + { + public Vector2 Direction; + public float SpeedModifier; + } +} + +/// +/// Raised on the entity itself when attempting to handle mob collisions. +/// +[ByRefEvent] +public record struct AttemptMobCollideEvent +{ + public bool Cancelled; +} + +/// +/// Raised on the other entity when attempting mob collisions. +/// +[ByRefEvent] +public record struct AttemptMobTargetCollideEvent +{ + public bool Cancelled; +} diff --git a/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs b/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs index 8156955377..f843b66435 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.Relay.cs @@ -14,12 +14,12 @@ public abstract partial class SharedMoverController private void OnAfterRelayTargetState(Entity entity, ref AfterAutoHandleStateEvent args) { - Physics.UpdateIsPredicted(entity.Owner); + PhysicsSystem.UpdateIsPredicted(entity.Owner); } private void OnAfterRelayState(Entity entity, ref AfterAutoHandleStateEvent args) { - Physics.UpdateIsPredicted(entity.Owner); + PhysicsSystem.UpdateIsPredicted(entity.Owner); } /// @@ -42,7 +42,7 @@ public abstract partial class SharedMoverController { oldTarget.Source = EntityUid.Invalid; RemComp(component.RelayEntity, oldTarget); - Physics.UpdateIsPredicted(component.RelayEntity); + PhysicsSystem.UpdateIsPredicted(component.RelayEntity); } var targetComp = EnsureComp(relayEntity); @@ -50,11 +50,11 @@ public abstract partial class SharedMoverController { oldRelay.RelayEntity = EntityUid.Invalid; RemComp(targetComp.Source, oldRelay); - Physics.UpdateIsPredicted(targetComp.Source); + PhysicsSystem.UpdateIsPredicted(targetComp.Source); } - Physics.UpdateIsPredicted(uid); - Physics.UpdateIsPredicted(relayEntity); + PhysicsSystem.UpdateIsPredicted(uid); + PhysicsSystem.UpdateIsPredicted(relayEntity); component.RelayEntity = relayEntity; targetComp.Source = uid; Dirty(uid, component); @@ -63,8 +63,8 @@ public abstract partial class SharedMoverController private void OnRelayShutdown(Entity entity, ref ComponentShutdown args) { - Physics.UpdateIsPredicted(entity.Owner); - Physics.UpdateIsPredicted(entity.Comp.RelayEntity); + PhysicsSystem.UpdateIsPredicted(entity.Owner); + PhysicsSystem.UpdateIsPredicted(entity.Comp.RelayEntity); if (TryComp(entity.Comp.RelayEntity, out var inputMover)) SetMoveInput((entity.Comp.RelayEntity, inputMover), MoveButtons.None); @@ -78,8 +78,8 @@ public abstract partial class SharedMoverController private void OnTargetRelayShutdown(Entity entity, ref ComponentShutdown args) { - Physics.UpdateIsPredicted(entity.Owner); - Physics.UpdateIsPredicted(entity.Comp.Source); + PhysicsSystem.UpdateIsPredicted(entity.Owner); + PhysicsSystem.UpdateIsPredicted(entity.Comp.Source); if (Timing.ApplyingState) return; diff --git a/Content.Shared/Movement/Systems/SharedMoverController.cs b/Content.Shared/Movement/Systems/SharedMoverController.cs index 6456444080..a5c32b2992 100644 --- a/Content.Shared/Movement/Systems/SharedMoverController.cs +++ b/Content.Shared/Movement/Systems/SharedMoverController.cs @@ -20,6 +20,7 @@ using Robust.Shared.Physics; using Robust.Shared.Physics.Components; using Robust.Shared.Physics.Controllers; using Robust.Shared.Physics.Systems; +using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Robust.Shared.Utility; using PullableComponent = Content.Shared.Movement.Pulling.Components.PullableComponent; @@ -43,7 +44,6 @@ public abstract partial class SharedMoverController : VirtualController [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly SharedGravitySystem _gravity = default!; - [Dependency] protected readonly SharedPhysicsSystem Physics = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; [Dependency] private readonly TagSystem _tags = default!; @@ -60,6 +60,8 @@ public abstract partial class SharedMoverController : VirtualController protected EntityQuery FootstepModifierQuery; protected EntityQuery MapGridQuery; + private static readonly ProtoId FootstepSoundTag = "FootstepSound"; + /// /// /// @@ -105,6 +107,14 @@ public abstract partial class SharedMoverController : VirtualController public override void UpdateAfterSolve(bool prediction, float frameTime) { base.UpdateAfterSolve(prediction, frameTime); + + var query = AllEntityQuery(); + + while (query.MoveNext(out var uid, out var _, out var physics)) + { + //PhysicsSystem.SetLinearVelocity(uid, Vector2.Zero, body: physics); + } + UsedMobMovement.Clear(); } @@ -424,7 +434,7 @@ public abstract partial class SharedMoverController : VirtualController { sound = null; - if (!CanSound() || !_tags.HasTag(uid, "FootstepSound")) + if (!CanSound() || !_tags.HasTag(uid, FootstepSoundTag)) return false; var coordinates = xform.Coordinates; diff --git a/Content.Shared/Paper/PaperSystem.cs b/Content.Shared/Paper/PaperSystem.cs index 712133c0e6..6fda16b1f3 100644 --- a/Content.Shared/Paper/PaperSystem.cs +++ b/Content.Shared/Paper/PaperSystem.cs @@ -9,6 +9,7 @@ using Content.Shared.Tag; using Robust.Shared.Player; using Robust.Shared.Audio.Systems; using static Content.Shared.Paper.PaperComponent; +using Robust.Shared.Prototypes; namespace Content.Shared.Paper; @@ -23,6 +24,9 @@ public sealed class PaperSystem : EntitySystem [Dependency] private readonly MetaDataSystem _metaSystem = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; + private static readonly ProtoId WriteIgnoreStampsTag = "WriteIgnoreStamps"; + private static readonly ProtoId WriteTag = "Write"; + public override void Initialize() { base.Initialize(); @@ -100,8 +104,8 @@ public sealed class PaperSystem : EntitySystem private void OnInteractUsing(Entity entity, ref InteractUsingEvent args) { // only allow editing if there are no stamps or when using a cyberpen - var editable = entity.Comp.StampedBy.Count == 0 || _tagSystem.HasTag(args.Used, "WriteIgnoreStamps"); - if (_tagSystem.HasTag(args.Used, "Write")) + var editable = entity.Comp.StampedBy.Count == 0 || _tagSystem.HasTag(args.Used, WriteIgnoreStampsTag); + if (_tagSystem.HasTag(args.Used, WriteTag)) { if (editable) { diff --git a/Content.Shared/Physics/Controllers/SharedConveyorController.cs b/Content.Shared/Physics/Controllers/SharedConveyorController.cs index 07bf6c7332..4b2523b1d7 100644 --- a/Content.Shared/Physics/Controllers/SharedConveyorController.cs +++ b/Content.Shared/Physics/Controllers/SharedConveyorController.cs @@ -1,7 +1,6 @@ using System.Numerics; using Content.Shared.Conveyor; using Content.Shared.Gravity; -using Content.Shared.Magic; using Content.Shared.Movement.Components; using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; diff --git a/Content.Shared/Projectiles/SharedProjectileSystem.cs b/Content.Shared/Projectiles/SharedProjectileSystem.cs index 7161a39e0a..3fcff5ae56 100644 --- a/Content.Shared/Projectiles/SharedProjectileSystem.cs +++ b/Content.Shared/Projectiles/SharedProjectileSystem.cs @@ -66,8 +66,7 @@ public abstract partial class SharedProjectileSystem : EntitySystem private void OnEmbedRemove(Entity embeddable, ref RemoveEmbeddedProjectileEvent args) { - // Whacky prediction issues. - if (args.Cancelled || _net.IsClient) + if (args.Cancelled) return; EmbedDetach(embeddable, embeddable.Comp, args.User); diff --git a/Content.Shared/RCD/Systems/RCDSystem.cs b/Content.Shared/RCD/Systems/RCDSystem.cs index 62ad2f3be0..9e5096c77c 100644 --- a/Content.Shared/RCD/Systems/RCDSystem.cs +++ b/Content.Shared/RCD/Systems/RCDSystem.cs @@ -51,6 +51,7 @@ public class RCDSystem : EntitySystem private readonly EntProtoId _instantConstructionFx = "EffectRCDConstruct0"; private readonly ProtoId _deconstructTileProto = "DeconstructTile"; private readonly ProtoId _deconstructLatticeProto = "DeconstructLattice"; + private static readonly ProtoId CatwalkTag = "Catwalk"; private HashSet _intersectingEntities = new(); @@ -411,7 +412,7 @@ public class RCDSystem : EntitySystem if (isWindow && HasComp(ent)) continue; - if (isCatwalk && _tags.HasTag(ent, "Catwalk")) + if (isCatwalk && _tags.HasTag(ent, CatwalkTag)) { if (popMsgs) _popup.PopupClient(Loc.GetString("rcd-component-cannot-build-on-occupied-tile-message"), uid, user); diff --git a/Content.Shared/Standing/StandingStateSystem.cs b/Content.Shared/Standing/StandingStateSystem.cs index c534f47955..86d2b961eb 100644 --- a/Content.Shared/Standing/StandingStateSystem.cs +++ b/Content.Shared/Standing/StandingStateSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Hands.Components; +using Content.Shared.Movement.Systems; using Content.Shared.Physics; using Content.Shared.Rotation; using Robust.Shared.Audio.Systems; @@ -16,6 +17,29 @@ public sealed class StandingStateSystem : EntitySystem // If StandingCollisionLayer value is ever changed to more than one layer, the logic needs to be edited. private const int StandingCollisionLayer = (int) CollisionGroup.MidImpassable; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMobCollide); + SubscribeLocalEvent(OnMobTargetCollide); + } + + private void OnMobTargetCollide(Entity ent, ref AttemptMobTargetCollideEvent args) + { + if (!ent.Comp.Standing) + { + args.Cancelled = true; + } + } + + private void OnMobCollide(Entity ent, ref AttemptMobCollideEvent args) + { + if (!ent.Comp.Standing) + { + args.Cancelled = true; + } + } + public bool IsDown(EntityUid uid, StandingStateComponent? standingState = null) { if (!Resolve(uid, ref standingState, false)) diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 3e4a0be541..a33491b645 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,103 +1,4 @@ Entries: -- author: ArZarLordOfMango - changes: - - message: Most toggleable clothing must now be equipped to toggle their actions. - type: Fix - id: 7623 - time: '2024-11-19T20:31:38.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32826 -- author: Plykiya - changes: - - message: The SWAT crate from cargo now requires armory access to open. - type: Fix - id: 7624 - time: '2024-11-20T00:57:01.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33415 -- author: SlamBamActionman - changes: - - message: It's no longer possible to drag an item out of a container's UI to drop - it. - type: Tweak - id: 7625 - time: '2024-11-20T01:00:38.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32706 -- author: Plykiya - changes: - - message: The crew monitoring crate now contains a flatpack of the server and computers, - and can be opened with science access instead of engineering access now. - type: Tweak - id: 7626 - time: '2024-11-20T01:05:20.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33417 -- author: Beck Thompson - changes: - - message: Toggle verbs are no longer duplicated on magboots and fire extinguishers! - type: Fix - id: 7627 - time: '2024-11-20T01:53:53.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32138 -- author: qwerltaz - changes: - - message: A new grid item view is available in the construction menu, togglable - with a button. - type: Add - - message: Construction menu default window size was tweaked. - type: Tweak - id: 7628 - time: '2024-11-20T01:54:49.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32577 -- author: SpaceLizard24 - changes: - - message: Reduced crafting costs of colored light tubes. - type: Tweak - id: 7629 - time: '2024-11-20T01:59:31.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33376 -- author: thetolbean - changes: - - message: Items with a damage of 0 now have correct damage examination text. - type: Fix - id: 7630 - time: '2024-11-20T02:05:15.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33064 -- author: SaphireLattice - changes: - - message: The Singularity/Tesla generator now requires being surrounded by containment - fields to activate. This can be disabled with an Emag. - type: Tweak - id: 7631 - time: '2024-11-20T05:55:58.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33358 -- author: TheWaffleJesus - changes: - - message: You can now craft items with stacks of capacitors without it eating it - all! - type: Fix - id: 7632 - time: '2024-11-20T07:18:38.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/31966 -- author: ScarKy0, GoldenCan - changes: - - message: Ion stormed lawsets no longer persist between shifts. - type: Fix - - message: Cyborgs are now notified when inserted into a chassis with modified laws. - type: Tweak - id: 7633 - time: '2024-11-20T07:55:12.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/33311 -- author: ScarKy0, GoldenCan - changes: - - message: The Derelict Cyborg - a broken cyborg with altered laws due to year of - exposure to ion storms - can now appear as a ghost role through a new midround - event. - type: Add - - message: An ion storm affecting a Cyborg can no longer alter the law order of - the AI, cyborgs created later in the round or AI's and cyborgs in subsequent - rounds. - type: Fix - id: 7634 - time: '2024-11-20T07:55:13.0000000+00:00' - url: https://github.com/space-wizards/space-station-14/pull/32499 - author: IProduceWidgets changes: - message: Presents no longer make non-items into items @@ -3900,3 +3801,90 @@ id: 8122 time: '2025-04-01T23:26:53.0000000+00:00' url: https://github.com/space-wizards/space-station-14/pull/35191 +- author: Fildrance + changes: + - message: fixed missing deconstruct on RCD + type: Fix + id: 8123 + time: '2025-04-02T16:11:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36255 +- author: qwerltaz + changes: + - message: Dragon rifts now shine a different color depending on charge progress. + type: Add + id: 8124 + time: '2025-04-02T18:37:35.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36216 +- author: aada + changes: + - message: Diphenhydramine now causes light drowsiness. + type: Tweak + id: 8125 + time: '2025-04-03T06:29:52.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36212 +- author: sowelipililimute + changes: + - message: You can now more easily interact with objects behind faded ones, and + you can look behind fadeable objects in your FOV by hovering them with your + mouse pointer + type: Add + id: 8126 + time: '2025-04-03T06:58:05.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/35863 +- author: whatston3 + changes: + - message: Fancy tables and curtains now respect carpet stacks. + type: Fix + id: 8127 + time: '2025-04-03T14:45:04.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/33721 +- author: YuNii + changes: + - message: Stop ghosts from being logged to airlocks + type: Fix + id: 8128 + time: '2025-04-03T22:24:30.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36261 +- author: K-Dynamic + changes: + - message: Proto-Kinetic Accelerators and PTK-800 Shuttle Gun recipes are now available + to the security techfab when researched by science. + type: Add + id: 8129 + time: '2025-04-04T13:12:04.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/34566 +- author: lzk228 + changes: + - message: Fix ability to use implants on borgs and bots. + type: Fix + id: 8130 + time: '2025-04-04T15:36:28.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36218 +- author: TheBlueYowie + changes: + - message: Aligned Adv. Mineral Scanner rotation in inventory + type: Tweak + id: 8131 + time: '2025-04-04T15:56:30.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36294 +- author: Tayrtahn + changes: + - message: Fixed ninjas being unable to hack criminal records consoles. + type: Fix + id: 8132 + time: '2025-04-04T17:46:59.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36299 +- author: ScarKy0 + changes: + - message: Players now require 1h of playtime before selecting any antagonist role. + type: Tweak + id: 8133 + time: '2025-04-04T20:24:05.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36276 +- author: aada + changes: + - message: SyndieJuice now comes with 10 units of liquid plasma. + type: Add + id: 8134 + time: '2025-04-05T03:22:12.0000000+00:00' + url: https://github.com/space-wizards/space-station-14/pull/36280 diff --git a/Resources/ConfigPresets/Build/development.toml b/Resources/ConfigPresets/Build/development.toml index 7990b3545b..4465ea8ee0 100644 --- a/Resources/ConfigPresets/Build/development.toml +++ b/Resources/ConfigPresets/Build/development.toml @@ -15,6 +15,9 @@ quick_lottery = true [gateway] generator_enabled = false +[movement] +mob_pushing = true + [physics] # Makes mapping annoying grid_splitting = false diff --git a/Resources/ConfigPresets/WizardsDen/wizardsDen.toml b/Resources/ConfigPresets/WizardsDen/wizardsDen.toml index 558e60da69..b29ee87d5c 100644 --- a/Resources/ConfigPresets/WizardsDen/wizardsDen.toml +++ b/Resources/ConfigPresets/WizardsDen/wizardsDen.toml @@ -31,6 +31,9 @@ appeal = "https://appeal.ss14.io" [server] rules_file = "StandardRuleset" +[movement] +mob_pushing = true + [net] max_connections = 1024 diff --git a/Resources/Locale/en-US/access/components/agent-id-card-component.ftl b/Resources/Locale/en-US/access/components/agent-id-card-component.ftl index 17a92f6012..5e1e3cd7cf 100644 --- a/Resources/Locale/en-US/access/components/agent-id-card-component.ftl +++ b/Resources/Locale/en-US/access/components/agent-id-card-component.ftl @@ -1,6 +1,9 @@ -agent-id-no-new = Didn't gain any new accesses from {THE($card)}. -agent-id-new-1 = Gained one new access from {THE($card)}. -agent-id-new = Gained {$number} new accesses from {THE($card)}. +agent-id-new = { $number -> + [0] Didn't gain any new accesses from {THE($card)}. + [one] Gained one new access from {THE($card)}. + *[other] Gained {$number} new accesses from {THE($card)}. +} + agent-id-card-current-name = Name: agent-id-card-current-job = Job: agent-id-card-job-icon-label = Job icon: diff --git a/Resources/Locale/en-US/administration/admin-alerts.ftl b/Resources/Locale/en-US/administration/admin-alerts.ftl index a7c7f6f402..dd6ea2d892 100644 --- a/Resources/Locale/en-US/administration/admin-alerts.ftl +++ b/Resources/Locale/en-US/administration/admin-alerts.ftl @@ -1,3 +1,3 @@ admin-alert-shared-connection = {$player} is sharing a connection with {$otherCount} connected player(s): {$otherList} -admin-alert-ipintel-blocked = {$player} was rejected from joining due to their IP having a {TOSTRING($percent, "P0")} confidence of being a VPN/Datacenter. -admin-alert-ipintel-warning = {$player} IP has a {TOSTRING($percent, "P0")} confidence of being a VPN/Datacenter. Please watch them. +admin-alert-ipintel-blocked = {$player} was rejected from joining due to their IP having a {TOSTRING($percent, "P2")} confidence of being a VPN/Datacenter. +admin-alert-ipintel-warning = {$player} IP has a {TOSTRING($percent, "P2")} confidence of being a VPN/Datacenter. Please watch them. diff --git a/Resources/Locale/en-US/atmos/air-alarm-ui.ftl b/Resources/Locale/en-US/atmos/air-alarm-ui.ftl index 15043e4984..57e47cf4cf 100644 --- a/Resources/Locale/en-US/atmos/air-alarm-ui.ftl +++ b/Resources/Locale/en-US/atmos/air-alarm-ui.ftl @@ -15,12 +15,21 @@ air-alarm-ui-window-resync-devices-label = Resync air-alarm-ui-window-mode-label = Mode air-alarm-ui-window-auto-mode-label = Auto mode +-air-alarm-state-name = { $state -> + [normal] Normal + [warning] Warning + [danger] Danger + [emagged] Emagged + *[invalid] Invalid +} + +air-alarm-ui-window-listing-title = {$address} : {-air-alarm-state-name(state:$state)} air-alarm-ui-window-pressure = {$pressure} kPa air-alarm-ui-window-pressure-indicator = Pressure: [color={$color}]{$pressure} kPa[/color] air-alarm-ui-window-temperature = {$tempC} C ({$temperature} K) air-alarm-ui-window-temperature-indicator = Temperature: [color={$color}]{$tempC} C ({$temperature} K)[/color] -air-alarm-ui-window-alarm-state = [color={$color}]{$state}[/color] -air-alarm-ui-window-alarm-state-indicator = Status: [color={$color}]{$state}[/color] +air-alarm-ui-window-alarm-state = [color={$color}]{-air-alarm-state-name(state:$state)}[/color] +air-alarm-ui-window-alarm-state-indicator = Status: [color={$color}]{-air-alarm-state-name(state:$state)}[/color] air-alarm-ui-window-tab-vents = Vents air-alarm-ui-window-tab-scrubbers = Scrubbers diff --git a/Resources/Locale/en-US/burning/bodyburn.ftl b/Resources/Locale/en-US/burning/bodyburn.ftl index 58b98c09bb..929b2344cf 100644 --- a/Resources/Locale/en-US/burning/bodyburn.ftl +++ b/Resources/Locale/en-US/burning/bodyburn.ftl @@ -1 +1 @@ -bodyburn-text-others = {$name} burns to ash! +bodyburn-text-others = {CAPITALIZE(THE($name))} burns to ash! diff --git a/Resources/Locale/en-US/character-appearance/components/magic-mirror-component.ftl b/Resources/Locale/en-US/character-appearance/components/magic-mirror-component.ftl index 0906cccee5..1b22fbf828 100644 --- a/Resources/Locale/en-US/character-appearance/components/magic-mirror-component.ftl +++ b/Resources/Locale/en-US/character-appearance/components/magic-mirror-component.ftl @@ -6,10 +6,10 @@ magic-mirror-remove-slot-self = You're removing some of your hair. magic-mirror-change-slot-self = You're changing your hairstyle. magic-mirror-change-color-self = You're changing your hair color. -magic-mirror-add-slot-target = Hair is being added to you by {$user}. -magic-mirror-remove-slot-target = Your hair is being cut off by {$user}. -magic-mirror-change-slot-target = Your hairstyle is being changed by {$user}. -magic-mirror-change-color-target = Your hair color is being changed by {$user}. +magic-mirror-add-slot-target = Hair is being added to you by {THE($user)}. +magic-mirror-remove-slot-target = Your hair is being cut off by {THE($user)}. +magic-mirror-change-slot-target = Your hairstyle is being changed by {THE($user)}. +magic-mirror-change-color-target = Your hair color is being changed by {THE($user)}. magic-mirror-blocked-by-hat-self = You need to take off your hat before changing your hair. -magic-mirror-blocked-by-hat-self-target = You try to change their hair but their clothes gets in the way. +magic-mirror-blocked-by-hat-self-target = You try to change {POSS-ADJ($target)} hair but {POSS-ADJ($target)} clothes get in the way. diff --git a/Resources/Locale/en-US/chat/commands/suicide-command.ftl b/Resources/Locale/en-US/chat/commands/suicide-command.ftl index 36e861169b..4b2fb5c00e 100644 --- a/Resources/Locale/en-US/chat/commands/suicide-command.ftl +++ b/Resources/Locale/en-US/chat/commands/suicide-command.ftl @@ -3,7 +3,7 @@ suicide-command-help-text = The suicide command gives you a quick way out of a r The method varies, first it will attempt to use the held item in your active hand. If that fails, it will attempt to use an object in the environment. Finally, if neither of the above worked, you will die by biting your tongue. -suicide-command-default-text-others = {$name} is attempting to bite their own tongue! +suicide-command-default-text-others = {CAPITALIZE(THE($name))} is attempting to bite {POSS-ADJ($name)} own tongue! suicide-command-default-text-self = You attempt to bite your own tongue! suicide-command-already-dead = You can't suicide. You're dead. suicide-command-no-mind = You have no mind! diff --git a/Resources/Locale/en-US/chemistry/components/injector-component.ftl b/Resources/Locale/en-US/chemistry/components/injector-component.ftl index 24f524081e..0c3152774f 100644 --- a/Resources/Locale/en-US/chemistry/components/injector-component.ftl +++ b/Resources/Locale/en-US/chemistry/components/injector-component.ftl @@ -10,14 +10,14 @@ injector-volume-label = Volume: [color=white]{$currentVolume}/{$totalVolume}[/co injector-component-drawing-text = Now drawing injector-component-injecting-text = Now injecting -injector-component-cannot-transfer-message = You aren't able to transfer to {$target}! -injector-component-cannot-draw-message = You aren't able to draw from {$target}! -injector-component-cannot-inject-message = You aren't able to inject to {$target}! -injector-component-inject-success-message = You inject {$amount}u into {$target}! -injector-component-transfer-success-message = You transfer {$amount}u into {$target}. -injector-component-draw-success-message = You draw {$amount}u from {$target}. -injector-component-target-already-full-message = {$target} is already full! -injector-component-target-is-empty-message = {$target} is empty! +injector-component-cannot-transfer-message = You aren't able to transfer to {THE($target)}! +injector-component-cannot-draw-message = You aren't able to draw from {THE($target)}! +injector-component-cannot-inject-message = You aren't able to inject to {THE($target)}! +injector-component-inject-success-message = You inject {$amount}u into {THE($target)}! +injector-component-transfer-success-message = You transfer {$amount}u into {THE($target)}. +injector-component-draw-success-message = You draw {$amount}u from {THE($target)}. +injector-component-target-already-full-message = {CAPITALIZE(THE($target))} is already full! +injector-component-target-is-empty-message = {CAPITALIZE(THE($target))} is empty! injector-component-cannot-toggle-draw-message = Too full to draw! injector-component-cannot-toggle-inject-message = Nothing to inject! diff --git a/Resources/Locale/en-US/reagents/meta/medicine.ftl b/Resources/Locale/en-US/reagents/meta/medicine.ftl index 0b6695be34..9e5dcc88d4 100644 --- a/Resources/Locale/en-US/reagents/meta/medicine.ftl +++ b/Resources/Locale/en-US/reagents/meta/medicine.ftl @@ -5,7 +5,7 @@ reagent-name-dylovene = dylovene reagent-desc-dylovene = A broad-spectrum anti-toxin, which treats toxin damage in organisms. Overdosing will cause vomiting, dizzyness and pain. reagent-name-diphenhydramine = diphenhydramine -reagent-desc-diphenhydramine = Rapidly purges the body of histamine, reduces jitteriness, and treats poison damage. +reagent-desc-diphenhydramine = Rapidly purges the body of histamine, reduces jitteriness, causes drowsiness, and treats poison damage. Often included in sleep medication. reagent-name-arithrazine = arithrazine reagent-desc-arithrazine = A mildly unstable medication used for the most extreme case of radiation poisoning. Exerts minor stress on the body. diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/chemvend.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/chemvend.yml index 0a1c562dd7..42a484e4ab 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/chemvend.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/chemvend.yml @@ -53,6 +53,7 @@ JugSugar: 3 JugSulfur: 1 JugWeldingFuel: 1 + PlasmaChemistryVial: 1 contrabandInventory: DrinkLithiumFlask: 1 StrangePill: 3 diff --git a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml index 91cf83d61b..0dfc37a627 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml @@ -187,12 +187,12 @@ - HeadTop - HeadSide -#Templar Helmet +#Knight Helmet - type: entity parent: ClothingHeadBase id: ClothingHeadHelmetTemplar - name: templar helmet - description: DEUS VULT! + name: knight helmet + description: Decorative helmet fashioned to resemble the knights of old. components: - type: Sprite sprite: Clothing/Head/Helmets/templar.rsi diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml index 96e8eda43d..ce1fecd03a 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml @@ -236,6 +236,7 @@ - DoorBumpOpener - FootstepSound - CanPilot + - Unimplantable - type: Emoting - type: GuideHelp guides: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml index 280df955af..3dae7023d9 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml @@ -66,6 +66,7 @@ tags: - DoorBumpOpener - Bot + - Unimplantable - type: MobState allowedStates: - Alive @@ -451,6 +452,7 @@ - DoorBumpOpener - FootstepSound - Bot + - Unimplantable - type: ActiveRadio channels: - Common diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index 8fecb53818..e4ce500487 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -14,6 +14,7 @@ - BypassDropChecks - NoConsoleSound - SilentStorageUser + - PreventAccessLogging - type: Input context: "aghost" - type: Ghost diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 970ec52cd7..f7c2f74411 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -8,6 +8,7 @@ - type: Sprite noRot: true drawdepth: Mobs + - type: MobCollision - type: Physics bodyType: KinematicController - type: Fixtures diff --git a/Resources/Prototypes/Entities/Objects/Misc/carpets.yml b/Resources/Prototypes/Entities/Objects/Misc/carpets.yml index dca2d40aed..5cc8035875 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/carpets.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/carpets.yml @@ -14,9 +14,6 @@ - Plating - type: Stack stackType: FloorCarpetRed - - type: Tag - tags: - - CarpetRed - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: Carpet doAfter: 0.5 @@ -33,9 +30,6 @@ heldPrefix: carpet-black - type: Stack stackType: FloorCarpetBlack - - type: Tag - tags: - - CarpetBlack - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetBlack doAfter: 0.5 @@ -52,9 +46,6 @@ heldPrefix: carpet-blue - type: Stack stackType: FloorCarpetBlue - - type: Tag - tags: - - CarpetBlue - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetBlue doAfter: 0.5 @@ -71,9 +62,6 @@ heldPrefix: carpet-green - type: Stack stackType: FloorCarpetGreen - - type: Tag - tags: - - CarpetGreen - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetGreen doAfter: 0.5 @@ -90,9 +78,6 @@ heldPrefix: carpet-orange - type: Stack stackType: FloorCarpetOrange - - type: Tag - tags: - - CarpetOrange - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetOrange doAfter: 0.5 @@ -109,9 +94,6 @@ heldPrefix: carpet-skyblue - type: Stack stackType: FloorCarpetSkyBlue - - type: Tag - tags: - - CarpetSBlue - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetSBlue doAfter: 0.5 @@ -128,9 +110,6 @@ heldPrefix: carpet-purple - type: Stack stackType: FloorCarpetPurple - - type: Tag - tags: - - CarpetPurple - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetPurple doAfter: 0.5 @@ -147,9 +126,6 @@ heldPrefix: carpet-pink - type: Stack stackType: FloorCarpetPink - - type: Tag - tags: - - CarpetPink - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetPink doAfter: 0.5 @@ -166,9 +142,6 @@ heldPrefix: carpet-cyan - type: Stack stackType: FloorCarpetCyan - - type: Tag - tags: - - CarpetCyan - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetCyan doAfter: 0.5 @@ -185,9 +158,6 @@ heldPrefix: carpet-white - type: Stack stackType: FloorCarpetWhite - - type: Tag - tags: - - CarpetWhite - type: SpawnAfterInteract #Nuke after convert to FloorTile prototype: CarpetWhite doAfter: 0.5 diff --git a/Resources/Prototypes/Entities/Objects/Specific/Salvage/scanner.yml b/Resources/Prototypes/Entities/Objects/Specific/Salvage/scanner.yml index 9c3e783c51..d65c1e8bff 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Salvage/scanner.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Salvage/scanner.yml @@ -67,6 +67,8 @@ shader: unshaded visible: false map: ["enum.ToggleVisuals.Layer"] + - type: Item + storedRotation: -90 - type: MiningScanner range: 10 diff --git a/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml b/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml index 646f6a6378..34dcd66b71 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/light_replacer.yml @@ -2,7 +2,7 @@ parent: BaseItem name: light replacer id: LightReplacer - description: An item which uses magnets to easily replace broken lights. Refill By adding more lights into the replacer. + description: An item which uses magnets to easily replace broken lights. Refill by adding more lights into the replacer. components: - type: Sprite sprite: Objects/Specific/Janitorial/light_replacer.rsi diff --git a/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml b/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml index 41e85e6c3f..f322de7283 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Shutter/shutters.yml @@ -240,4 +240,4 @@ layer: - HighImpassable - type: Transform - noRot: true + noRot: false diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index 106448b2e3..cf3bcac302 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -394,6 +394,8 @@ - SecurityAmmoStatic - SecurityWeaponsStatic dynamicPacks: + - SalvageSecurityBoards + - SalvageSecurityWeapons - SecurityEquipment - SecurityBoards - SecurityExplosives diff --git a/Resources/Prototypes/RCD/rcd.yml b/Resources/Prototypes/RCD/rcd.yml index 88a99451cf..f476f06dc4 100644 --- a/Resources/Prototypes/RCD/rcd.yml +++ b/Resources/Prototypes/RCD/rcd.yml @@ -2,25 +2,25 @@ - type: rcd id: Invalid # Hidden prototype - do not add to RCDs mode: Invalid - + - type: rcd id: Deconstruct name: rcd-component-deconstruct - category: Main + category: WallsAndFlooring sprite: /Textures/Interface/Radial/RCD/deconstruct.png mode: Deconstruct prototype: EffectRCDDeconstructPreview rotation: Camera - type: rcd - id: DeconstructLattice # Hidden prototype - do not add to RCDs + id: DeconstructLattice # Hidden prototype - do not add to RCDs name: rcd-component-deconstruct mode: Deconstruct cost: 2 delay: 0 rotation: Camera fx: EffectRCDConstruct0 - + - type: rcd id: DeconstructTile # Hidden prototype - do not add to RCDs name: rcd-component-deconstruct @@ -30,7 +30,7 @@ rotation: Camera fx: EffectRCDDeconstruct4 -# Flooring +# Flooring - type: rcd id: Plating name: rcd-component-plating @@ -44,7 +44,7 @@ rules: - CanBuildOnEmptyTile fx: EffectRCDConstruct1 - + - type: rcd id: FloorSteel name: rcd-component-floor-steel @@ -80,7 +80,7 @@ category: WallsAndFlooring sprite: /Textures/Interface/Radial/RCD/solid_wall.png mode: ConstructObject - prototype: WallSolid + prototype: WallSolid cost: 4 delay: 2 collisionMask: FullTileMask @@ -113,7 +113,7 @@ - IsWindow rotation: Fixed fx: EffectRCDConstruct2 - + - type: rcd id: WindowDirectional category: WindowsAndGrilles @@ -128,7 +128,7 @@ - IsWindow rotation: User fx: EffectRCDConstruct1 - + - type: rcd id: ReinforcedWindow category: WindowsAndGrilles @@ -142,7 +142,7 @@ - IsWindow rotation: User fx: EffectRCDConstruct3 - + - type: rcd id: WindowReinforcedDirectional category: WindowsAndGrilles @@ -170,7 +170,7 @@ collisionMask: FullTileMask rotation: Camera fx: EffectRCDConstruct4 - + - type: rcd id: AirlockGlass category: Airlocks @@ -182,7 +182,7 @@ collisionMask: FullTileMask rotation: Camera fx: EffectRCDConstruct4 - + - type: rcd id: Firelock category: Airlocks @@ -208,7 +208,7 @@ collisionBounds: "-0.23,-0.49,0.23,-0.36" rotation: User fx: EffectRCDConstruct1 - + - type: rcd id: BulbLight category: Lighting @@ -235,7 +235,7 @@ - MustBuildOnSubfloor rotation: Fixed fx: EffectRCDConstruct0 - + - type: rcd id: MVCable category: Electrical @@ -248,7 +248,7 @@ - MustBuildOnSubfloor rotation: Fixed fx: EffectRCDConstruct0 - + - type: rcd id: HVCable category: Electrical @@ -261,7 +261,7 @@ - MustBuildOnSubfloor rotation: Fixed fx: EffectRCDConstruct0 - + - type: rcd id: CableTerminal category: Electrical diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index 26ca6fa266..6c676f0105 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -83,6 +83,12 @@ key: Jitter time: 3.0 type: Remove + - !type:GenericStatusEffect + key: Drowsiness + component: Drowsiness + time: 1.5 + type: Add + refresh: false - !type:HealthChange damage: types: diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/curtains.yml b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/curtains.yml index 90e77d6720..21492af6a9 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/curtains.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/curtains.yml @@ -17,93 +17,66 @@ completed: - !type:SnapToGrid { } steps: - - tag: CarpetBlack + - material: FloorCarpetBlack + amount: 1 doAfter: 1 - name: black carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-black - to: CurtainsBlue completed: - !type:SnapToGrid { } steps: - - tag: CarpetBlue + - material: FloorCarpetBlue + amount: 1 doAfter: 1 - name: blue carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-blue - to: CurtainsCyan completed: - !type:SnapToGrid { } steps: - - tag: CarpetCyan + - material: FloorCarpetCyan + amount: 1 doAfter: 1 - name: cyan carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-cyan - to: CurtainsGreen completed: - !type:SnapToGrid { } steps: - - tag: CarpetGreen + - material: FloorCarpetGreen + amount: 1 doAfter: 1 - name: green carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-green - to: CurtainsOrange completed: - !type:SnapToGrid { } steps: - - tag: CarpetOrange + - material: FloorCarpetOrange + amount: 1 doAfter: 1 - name: orange carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-orange - to: CurtainsPink completed: - !type:SnapToGrid { } steps: - - tag: CarpetPink + - material: FloorCarpetPink + amount: 1 doAfter: 1 - name: pink carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-pink - to: CurtainsPurple completed: - !type:SnapToGrid { } steps: - - tag: CarpetPurple + - material: FloorCarpetPurple + amount: 1 doAfter: 1 - name: purple carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-purple - to: CurtainsRed completed: - !type:SnapToGrid { } steps: - - tag: CarpetRed + - material: FloorCarpetRed + amount: 1 doAfter: 1 - name: red carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-red - to: CurtainsWhite completed: - !type:SnapToGrid { } steps: - - tag: CarpetWhite + - material: FloorCarpetWhite + amount: 1 doAfter: 1 - name: white carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-white - + - node: Curtains entity: HospitalCurtains edges: @@ -126,7 +99,7 @@ steps: - tool: Cutting doAfter: 1 - + - node: CurtainsBlack entity: CurtainsBlack edges: @@ -148,8 +121,8 @@ amount: 1 steps: - tool: Cutting - doAfter: 1 - + doAfter: 1 + - node: CurtainsBlue entity: CurtainsBlue edges: @@ -172,7 +145,7 @@ steps: - tool: Cutting doAfter: 1 - + - node: CurtainsCyan entity: CurtainsCyan edges: @@ -195,7 +168,7 @@ steps: - tool: Cutting doAfter: 1 - + - node: CurtainsGreen entity: CurtainsGreen edges: @@ -218,7 +191,7 @@ steps: - tool: Cutting doAfter: 1 - + - node: CurtainsOrange entity: CurtainsOrange edges: @@ -241,7 +214,7 @@ steps: - tool: Cutting doAfter: 1 - + - node: CurtainsPink entity: CurtainsPink edges: @@ -264,7 +237,7 @@ steps: - tool: Cutting doAfter: 1 - + - node: CurtainsPurple entity: CurtainsPurple edges: @@ -287,7 +260,7 @@ steps: - tool: Cutting doAfter: 1 - + - node: CurtainsRed entity: CurtainsRed edges: @@ -310,7 +283,7 @@ steps: - tool: Cutting doAfter: 1 - + - node: CurtainsWhite entity: CurtainsWhite edges: diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/tables.yml b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/tables.yml index d7ef51e96b..4cee536bf9 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/furniture/tables.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/furniture/tables.yml @@ -218,75 +218,48 @@ - to: TableFancyBlack steps: - - tag: CarpetBlack - name: black carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-black + - material: FloorCarpetBlack + amount: 1 - to: TableFancyBlue steps: - - tag: CarpetBlue - name: blue carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-blue + - material: FloorCarpetBlue + amount: 1 - to: TableFancyCyan steps: - - tag: CarpetCyan - name: cyan carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-cyan + - material: FloorCarpetCyan + amount: 1 - to: TableFancyGreen steps: - - tag: CarpetGreen - name: green carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-green + - material: FloorCarpetGreen + amount: 1 - to: TableFancyOrange steps: - - tag: CarpetOrange - name: orange carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-orange + - material: FloorCarpetOrange + amount: 1 - to: TableFancyPurple steps: - - tag: CarpetPurple - name: purple carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-purple + - material: FloorCarpetPurple + amount: 1 - to: TableFancyPink steps: - - tag: CarpetPink - name: pink carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-pink + - material: FloorCarpetPink + amount: 1 - to: TableFancyRed steps: - - tag: CarpetRed - name: red carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-red + - material: FloorCarpetRed + amount: 1 - to: TableFancyWhite steps: - - tag: CarpetWhite - name: white carpet - icon: - sprite: Objects/Tiles/tile.rsi - state: carpet-white + - material: FloorCarpetWhite + amount: 1 - node: TableCarpet entity: TableCarpet diff --git a/Resources/Prototypes/Recipes/Lathes/Packs/shared.yml b/Resources/Prototypes/Recipes/Lathes/Packs/shared.yml index 44ba33cf8b..6fb00c0257 100644 --- a/Resources/Prototypes/Recipes/Lathes/Packs/shared.yml +++ b/Resources/Prototypes/Recipes/Lathes/Packs/shared.yml @@ -49,3 +49,13 @@ - HandHeldMassScanner - ClothingMaskWeldingGas - SignallerAdvanced + +- type: latheRecipePack + id: SalvageSecurityBoards + recipes: + - ShuttleGunKineticCircuitboard + +- type: latheRecipePack + id: SalvageSecurityWeapons + recipes: + - WeaponProtoKineticAccelerator diff --git a/Resources/Prototypes/Roles/Antags/revolutionary.yml b/Resources/Prototypes/Roles/Antags/revolutionary.yml index 502a68ea9e..eeef73b2d5 100644 --- a/Resources/Prototypes/Roles/Antags/revolutionary.yml +++ b/Resources/Prototypes/Roles/Antags/revolutionary.yml @@ -5,6 +5,9 @@ setPreference: true objective: roles-antag-rev-head-objective guides: [ Revolutionaries ] + requirements: + - !type:OverallPlaytimeRequirement + time: 3600 # 1h - type: antag id: Rev diff --git a/Resources/Prototypes/Roles/Antags/thief.yml b/Resources/Prototypes/Roles/Antags/thief.yml index 12fdefba2b..b8d21d2e83 100644 --- a/Resources/Prototypes/Roles/Antags/thief.yml +++ b/Resources/Prototypes/Roles/Antags/thief.yml @@ -5,6 +5,9 @@ setPreference: true objective: roles-antag-thief-objective guides: [ Thieves ] + requirements: + - !type:OverallPlaytimeRequirement + time: 3600 # 1h - type: startingGear id: ThiefGear diff --git a/Resources/Prototypes/Roles/Antags/traitor.yml b/Resources/Prototypes/Roles/Antags/traitor.yml index 6fc5314629..572adea1e4 100644 --- a/Resources/Prototypes/Roles/Antags/traitor.yml +++ b/Resources/Prototypes/Roles/Antags/traitor.yml @@ -5,6 +5,9 @@ setPreference: true objective: roles-antag-syndicate-agent-objective guides: [ Traitors ] + requirements: + - !type:OverallPlaytimeRequirement + time: 3600 # 1h - type: antag id: TraitorSleeper @@ -13,6 +16,9 @@ setPreference: true objective: roles-antag-syndicate-agent-sleeper-objective guides: [ Traitors ] + requirements: + - !type:OverallPlaytimeRequirement + time: 3600 # 1h # Syndicate Operative Outfit - Monkey - type: startingGear diff --git a/Resources/Prototypes/Roles/Antags/zombie.yml b/Resources/Prototypes/Roles/Antags/zombie.yml index bc3c2d7f22..4629e6b509 100644 --- a/Resources/Prototypes/Roles/Antags/zombie.yml +++ b/Resources/Prototypes/Roles/Antags/zombie.yml @@ -5,6 +5,9 @@ setPreference: true objective: roles-antag-initial-infected-objective guides: [ Zombies ] + requirements: + - !type:OverallPlaytimeRequirement + time: 3600 # 1h - type: antag id: Zombie diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 6577b7eb89..8b2ae35873 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -222,36 +222,6 @@ - type: Tag id: Carpet -- type: Tag - id: CarpetBlack - -- type: Tag - id: CarpetBlue - -- type: Tag - id: CarpetCyan - -- type: Tag - id: CarpetGreen - -- type: Tag - id: CarpetOrange - -- type: Tag - id: CarpetPink - -- type: Tag - id: CarpetPurple - -- type: Tag - id: CarpetRed - -- type: Tag - id: CarpetSBlue - -- type: Tag - id: CarpetWhite - - type: Tag id: Carrot @@ -1056,6 +1026,9 @@ - type: Tag id: Powerdrill +- type: Tag + id: PreventAccessLogging + - type: Tag id: PrisonUniform @@ -1317,10 +1290,10 @@ - type: Tag id: Truncheon - + - type: Tag id: TurretCompatibleWeapon # Used in the construction of sentry turrets - + - type: Tag id: TurretControlElectronics # Used in the construction of sentry turret control panels