diff --git a/Content.Benchmarks/ComponentManagerGetAllComponents.cs b/Content.Benchmarks/ComponentManagerGetAllComponents.cs index 95063ba8d0..a2db430944 100644 --- a/Content.Benchmarks/ComponentManagerGetAllComponents.cs +++ b/Content.Benchmarks/ComponentManagerGetAllComponents.cs @@ -2,9 +2,13 @@ using System; using System.Collections.Generic; using BenchmarkDotNet.Attributes; using Moq; +using Robust.Shared.Exceptions; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Log; +using Robust.Shared.Interfaces.Reflection; using Robust.Shared.IoC; +using Robust.Shared.Log; namespace Content.Benchmarks { @@ -14,7 +18,17 @@ namespace Content.Benchmarks private IComponentManager _componentManager; - [Params(500, 1000, 5000)] public int N { get; set; } + [Params(5000)] public int N { get; set; } + + public static void TestRun() + { + var x = new ComponentManagerGetAllComponents + { + N = 500 + }; + x.Setup(); + x.Run(); + } [GlobalSetup] public void Setup() @@ -23,6 +37,13 @@ namespace Content.Benchmarks IoCManager.InitThread(); IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + IoCManager.Register(); + var entityManager = new Mock().Object; + IoCManager.RegisterInstance(entityManager); + IoCManager.RegisterInstance(new Mock().Object); var dummyReg = new Mock(); dummyReg.SetupGet(p => p.Name).Returns("Dummy"); @@ -34,17 +55,19 @@ namespace Content.Benchmarks var componentFactory = new Mock(); componentFactory.Setup(p => p.GetComponent()).Returns(new DummyComponent()); componentFactory.Setup(p => p.GetRegistration(It.IsAny())).Returns(dummyReg.Object); + componentFactory.Setup(p => p.GetAllRefTypes()).Returns(new[] {typeof(DummyComponent)}); IoCManager.RegisterInstance(componentFactory.Object); IoCManager.BuildGraph(); - _componentManager = IoCManager.Resolve(); + _componentManager.Initialize(); // Initialize N entities with one component. for (var i = 0; i < N; i++) { var entity = new Entity(); + entity.SetManagers(entityManager); entity.SetUid(new EntityUid(i + 1)); _entities.Add(entity); @@ -65,6 +88,16 @@ namespace Content.Benchmarks return count; } + [Benchmark] + public int Noop() + { + var count = 0; + + _componentManager.TryGetComponent(default, out DummyComponent _); + + return count; + } + private class DummyComponent : Component { public override string Name => "Dummy"; diff --git a/Content.Benchmarks/Program.cs b/Content.Benchmarks/Program.cs index 7eb128092d..74ee4e265b 100644 --- a/Content.Benchmarks/Program.cs +++ b/Content.Benchmarks/Program.cs @@ -6,7 +6,8 @@ namespace Content.Benchmarks { public static void Main(string[] args) { - BenchmarkRunner.Run(); + BenchmarkRunner.Run(); + //ComponentManagerGetAllComponents.TestRun(); } } } diff --git a/Content.Client/Commands/DebugAiCommand.cs b/Content.Client/Commands/DebugAiCommand.cs index 1d463e3072..f5c3088215 100644 --- a/Content.Client/Commands/DebugAiCommand.cs +++ b/Content.Client/Commands/DebugAiCommand.cs @@ -54,8 +54,9 @@ namespace Content.Client.Commands } return !anyAction; -#endif +#else return true; +#endif } } } diff --git a/Content.Client/Commands/DebugPathfindingCommand.cs b/Content.Client/Commands/DebugPathfindingCommand.cs index 8549ed1929..adc4760767 100644 --- a/Content.Client/Commands/DebugPathfindingCommand.cs +++ b/Content.Client/Commands/DebugPathfindingCommand.cs @@ -55,8 +55,9 @@ namespace Content.Client.Commands } return !anyAction; -#endif +#else return true; +#endif } } } diff --git a/Content.Client/Commands/ToggleOutlineCommand.cs b/Content.Client/Commands/ToggleOutlineCommand.cs new file mode 100644 index 0000000000..a96c9e40a5 --- /dev/null +++ b/Content.Client/Commands/ToggleOutlineCommand.cs @@ -0,0 +1,24 @@ +using Robust.Client.Interfaces.Console; +using Robust.Shared.Interfaces.Configuration; +using Robust.Shared.IoC; + +namespace Content.Client.Commands +{ + public class ToggleOutlineCommand : IConsoleCommand + { + public string Command => "toggleoutline"; + + public string Description => "Toggles outline drawing on entities."; + + public string Help => ""; + + public bool Execute(IDebugConsole console, params string[] args) + { + var _configurationManager = IoCManager.Resolve(); + var old = _configurationManager.GetCVar("outline.enabled"); + _configurationManager.SetCVar("outline.enabled", !old); + console.AddLine($"Draw outlines set to: {_configurationManager.GetCVar("outline.enabled")}"); + return false; + } + } +} diff --git a/Content.Client/EntryPoint.cs b/Content.Client/EntryPoint.cs index e6e5177867..d939d9cbce 100644 --- a/Content.Client/EntryPoint.cs +++ b/Content.Client/EntryPoint.cs @@ -24,6 +24,7 @@ using Robust.Client.Interfaces.Input; using Robust.Client.Interfaces.State; using Robust.Client.Player; using Robust.Shared.ContentPack; +using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; using Robust.Shared.IoC; @@ -41,6 +42,7 @@ namespace Content.Client [Dependency] private readonly IEscapeMenuOwner _escapeMenuOwner; [Dependency] private readonly IGameController _gameController; [Dependency] private readonly IStateManager _stateManager; + [Dependency] private readonly IConfigurationManager _configurationManager; #pragma warning restore 649 public override void Init() @@ -223,6 +225,8 @@ namespace Content.Client { IoCManager.Resolve().CreateNewMapEntity(MapId.Nullspace); }; + + _configurationManager.RegisterCVar("outline.enabled", true); } /// @@ -249,7 +253,10 @@ namespace Content.Client /// public static void DetachPlayerFromEntity(EntityDetachedEventArgs eventArgs) { - eventArgs.OldEntity.RemoveComponent(); + if (!eventArgs.OldEntity.Deleted) + { + eventArgs.OldEntity.RemoveComponent(); + } } public override void PostInit() diff --git a/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientMagazineBarrelComponent.cs b/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientMagazineBarrelComponent.cs index 4a4ae191eb..adf93dc3ab 100644 --- a/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientMagazineBarrelComponent.cs +++ b/Content.Client/GameObjects/Components/Weapons/Ranged/Barrels/ClientMagazineBarrelComponent.cs @@ -3,6 +3,7 @@ using Content.Client.Animations; using Content.Client.UserInterface.Stylesheets; using Content.Client.Utility; using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Weapons.Ranged; using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; using Robust.Client.Animations; using Robust.Client.Graphics; @@ -112,10 +113,10 @@ namespace Content.Client.GameObjects.Components.Weapons.Ranged.Barrels switch (message) { - /* - case BmwComponentAutoEjectedMessage _: + + case MagazineAutoEjectMessage _: _statusControl?.PlayAlarmAnimation(); - return;*/ + return; } } diff --git a/Content.Client/GameObjects/EntitySystems/IconSmoothSystem.cs b/Content.Client/GameObjects/EntitySystems/IconSmoothSystem.cs index 9cd35a72f4..901178e9ec 100644 --- a/Content.Client/GameObjects/EntitySystems/IconSmoothSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/IconSmoothSystem.cs @@ -79,10 +79,9 @@ namespace Content.Client.GameObjects.EntitySystems } } - if (ev.LastPosition.HasValue) + // Entity is no longer valid, update around the last position it was at. + if (ev.LastPosition.HasValue && _mapManager.TryGetGrid(ev.LastPosition.Value.grid, out var grid)) { - // Entity is no longer valid, update around the last position it was at. - var grid = _mapManager.GetGrid(ev.LastPosition.Value.grid); var pos = ev.LastPosition.Value.pos; AddValidEntities(grid.GetSnapGridCell(pos + new MapIndices(1, 0), ev.Offset)); diff --git a/Content.Client/GameObjects/EntitySystems/SubFloorHideSystem.cs b/Content.Client/GameObjects/EntitySystems/SubFloorHideSystem.cs index 0551de45de..9a51fb4d8e 100644 --- a/Content.Client/GameObjects/EntitySystems/SubFloorHideSystem.cs +++ b/Content.Client/GameObjects/EntitySystems/SubFloorHideSystem.cs @@ -60,7 +60,11 @@ namespace Content.Client.GameObjects.EntitySystems private void HandleDirtyEvent(SubFloorHideDirtyEvent ev) { - var grid = _mapManager.GetGrid(ev.Sender.Transform.GridID); + if (!_mapManager.TryGetGrid(ev.Sender.Transform.GridID, out var grid)) + { + return; + } + var indices = grid.WorldToTile(ev.Sender.Transform.WorldPosition); UpdateTile(grid, indices); } diff --git a/Content.Client/GameTicking/ClientGameTicker.cs b/Content.Client/GameTicking/ClientGameTicker.cs index 194f600657..a80f606215 100644 --- a/Content.Client/GameTicking/ClientGameTicker.cs +++ b/Content.Client/GameTicking/ClientGameTicker.cs @@ -55,6 +55,7 @@ namespace Content.Client.GameTicking StartTime = message.StartTime; IsGameStarted = message.IsRoundStarted; AreWeReady = message.YouAreReady; + Paused = message.Paused; LobbyStatusUpdated?.Invoke(); } diff --git a/Content.Client/State/GameScreenBase.cs b/Content.Client/State/GameScreenBase.cs index f0496f8663..f407af9fe1 100644 --- a/Content.Client/State/GameScreenBase.cs +++ b/Content.Client/State/GameScreenBase.cs @@ -12,6 +12,7 @@ using Robust.Client.Interfaces.UserInterface; using Robust.Client.Player; using Robust.Shared.GameObjects; using Robust.Shared.Input; +using Robust.Shared.Interfaces.Configuration; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; using Robust.Shared.Interfaces.Timing; @@ -36,6 +37,8 @@ namespace Content.Client.State [Dependency] private readonly IGameTiming _timing; [Dependency] private readonly IMapManager _mapManager; [Dependency] private readonly IUserInterfaceManager _userInterfaceManager; + + [Dependency] private readonly IConfigurationManager _configurationManager; #pragma warning restore 649 private IEntity _lastHoveredEntity; @@ -72,6 +75,15 @@ namespace Content.Client.State } InteractionOutlineComponent outline; + if(!_configurationManager.GetCVar("outline.enabled")) + { + if(entityToClick != null && entityToClick.TryGetComponent(out outline)) + { + outline.OnMouseLeave(); //Prevent outline remains from persisting post command. + } + return; + } + if (entityToClick == _lastHoveredEntity) { if (entityToClick != null && entityToClick.TryGetComponent(out outline)) diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index cab5f7b68c..288105ab42 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -69,7 +69,8 @@ namespace Content.IntegrationTests.Tests catch (Exception e) { Logger.LogS(LogLevel.Error, "EntityTest", "Entity '" + prototype.ID + "' threw: " + e.Message); - Assert.Fail(); + //Assert.Fail(); + throw; } } }); diff --git a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs index 67c5df2ed3..d462beb279 100644 --- a/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs +++ b/Content.IntegrationTests/Tests/SaveLoadSaveTest.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Linq; using System.Threading.Tasks; using NUnit.Framework; using Robust.Server.Interfaces.Maps; @@ -37,19 +38,34 @@ namespace Content.IntegrationTests.Tests string one; string two; - using (var stream = userData.Open(new ResourcePath("save load save 1.yml"), FileMode.Open)) + var rp1 = new ResourcePath("save load save 1.yml"); + using (var stream = userData.Open(rp1, FileMode.Open)) using (var reader = new StreamReader(stream)) { one = reader.ReadToEnd(); } - using (var stream = userData.Open(new ResourcePath("save load save 2.yml"), FileMode.Open)) + var rp2 = new ResourcePath("save load save 2.yml"); + using (var stream = userData.Open(rp2, FileMode.Open)) using (var reader = new StreamReader(stream)) { two = reader.ReadToEnd(); } - Assert.That(one, Is.EqualTo(two)); + Assert.Multiple(() => { + Assert.That(one, Is.EqualTo(two)); + var failed = TestContext.CurrentContext.Result.Assertions.FirstOrDefault(); + if (failed != null) + { + var path1 = Path.Combine(userData.RootDir!,rp1.ToRelativeSystemPath()); + var path2 = Path.Combine(userData.RootDir!,rp2.ToRelativeSystemPath()); + TestContext.AddTestAttachment(path1); + TestContext.AddTestAttachment(path2); + TestContext.Error.WriteLine("Complete output:"); + TestContext.Error.WriteLine(path1); + TestContext.Error.WriteLine(path2); + } + }); } /// diff --git a/Content.Server/AI/Operators/Movement/BaseMover.cs b/Content.Server/AI/Operators/Movement/BaseMover.cs index d17472378f..cbbd5acdae 100644 --- a/Content.Server/AI/Operators/Movement/BaseMover.cs +++ b/Content.Server/AI/Operators/Movement/BaseMover.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading; +using Content.Server.GameObjects.Components.Access; using Content.Server.GameObjects.Components.Movement; using Content.Server.GameObjects.EntitySystems; using Content.Server.GameObjects.EntitySystems.AI.Pathfinding; @@ -240,10 +241,11 @@ namespace Content.Server.AI.Operators.Movement var startGrid = _mapManager.GetGrid(Owner.Transform.GridID).GetTileRef(Owner.Transform.GridPosition); var endGrid = _mapManager.GetGrid(TargetGrid.GridID).GetTileRef(TargetGrid);; - // _routeCancelToken = new CancellationTokenSource(); + var access = AccessReader.FindAccessTags(Owner); RouteJob = _pathfinder.RequestPath(new PathfindingArgs( Owner.Uid, + access, collisionMask, startGrid, endGrid, diff --git a/Content.Server/AI/Utility/Actions/Combat/Melee/MeleeAttackEntity.cs b/Content.Server/AI/Utility/Actions/Combat/Melee/MeleeAttackEntity.cs index 081a569423..25f329fdc1 100644 --- a/Content.Server/AI/Utility/Actions/Combat/Melee/MeleeAttackEntity.cs +++ b/Content.Server/AI/Utility/Actions/Combat/Melee/MeleeAttackEntity.cs @@ -29,11 +29,17 @@ namespace Content.Server.AI.Utility.Actions.Combat.Melee public override void SetupOperators(Blackboard context) { - var moveOperator = new MoveToEntityOperator(Owner, _entity); var equipped = context.GetState().GetValue(); + MoveToEntityOperator moveOperator; if (equipped != null && equipped.TryGetComponent(out MeleeWeaponComponent meleeWeaponComponent)) { - moveOperator.DesiredRange = meleeWeaponComponent.Range - 0.01f; + moveOperator = new MoveToEntityOperator(Owner, _entity, meleeWeaponComponent.Range - 0.01f); + } + // I think it's possible for this to happen given planning is time-sliced? + // TODO: At this point we should abort + else + { + moveOperator = new MoveToEntityOperator(Owner, _entity); } ActionOperators = new Queue(new AiOperator[] diff --git a/Content.Server/AI/Utility/AiLogic/UtilityAI.cs b/Content.Server/AI/Utility/AiLogic/UtilityAI.cs index 82d076ba1a..e886d0ff61 100644 --- a/Content.Server/AI/Utility/AiLogic/UtilityAI.cs +++ b/Content.Server/AI/Utility/AiLogic/UtilityAI.cs @@ -126,6 +126,9 @@ namespace Content.Server.AI.Utility.AiLogic { damageableComponent.DamageThresholdPassed -= DeathHandle; } + + var currentOp = CurrentAction?.ActionOperators.Peek(); + currentOp?.Shutdown(Outcome.Failed); } private void DeathHandle(object sender, DamageThresholdPassedEventArgs eventArgs) diff --git a/Content.Server/GameObjects/Components/Access/AccessReaderChangeMessage.cs b/Content.Server/GameObjects/Components/Access/AccessReaderChangeMessage.cs new file mode 100644 index 0000000000..cc1ae73793 --- /dev/null +++ b/Content.Server/GameObjects/Components/Access/AccessReaderChangeMessage.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameObjects; + +namespace Content.Server.GameObjects.Components.Access +{ + public sealed class AccessReaderChangeMessage : EntitySystemMessage + { + public EntityUid Uid { get; } + public bool Enabled { get; } + + public AccessReaderChangeMessage(EntityUid uid, bool enabled) + { + Uid = uid; + Enabled = enabled; + } + } +} \ No newline at end of file diff --git a/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs b/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs index f9e6c07a9d..2a1d18ac41 100644 --- a/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs +++ b/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs @@ -69,7 +69,7 @@ namespace Content.Server.GameObjects.Components.Access } [CanBeNull] - private static ICollection FindAccessTags(IEntity entity) + public static ICollection FindAccessTags(IEntity entity) { if (entity.TryGetComponent(out IAccess accessComponent)) { diff --git a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs index aa262e1b20..8d023d2580 100644 --- a/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs +++ b/Content.Server/GameObjects/Components/Chemistry/SolutionComponent.cs @@ -219,23 +219,23 @@ namespace Content.Server.GameObjects.Components.Chemistry { protected override void GetData(IEntity user, SolutionComponent component, VerbData data) { - if (user.TryGetComponent(out var hands)) + if (!ActionBlockerSystem.CanInteract(user) || + !user.TryGetComponent(out var hands) || + hands.GetActiveHand == null || + !hands.GetActiveHand.Owner.TryGetComponent(out var solution)) { - if (hands.GetActiveHand != null) - { - if (hands.GetActiveHand.Owner.TryGetComponent(out var solution)) - { - if ((solution.Capabilities & SolutionCaps.PourOut) != 0 && - (component.Capabilities & SolutionCaps.PourIn) != 0) - { - var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? ""; - var myName = component.Owner.Prototype?.Name ?? ""; + data.Visibility = VerbVisibility.Invisible; + return; + } - data.Text= $"Transfer liquid from [{heldEntityName}] to [{myName}]."; - return; - } - } - } + if ((solution.Capabilities & SolutionCaps.PourOut) != 0 && + (component.Capabilities & SolutionCaps.PourIn) != 0) + { + var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? ""; + var myName = component.Owner.Prototype?.Name ?? ""; + + data.Text= $"Transfer liquid from [{heldEntityName}] to [{myName}]."; + return; } data.Visibility = VerbVisibility.Invisible; @@ -318,23 +318,23 @@ namespace Content.Server.GameObjects.Components.Chemistry { protected override void GetData(IEntity user, SolutionComponent component, VerbData data) { - if (user.TryGetComponent(out var hands)) + if (!ActionBlockerSystem.CanInteract(user) || + !user.TryGetComponent(out var hands) || + hands.GetActiveHand == null || + !hands.GetActiveHand.Owner.TryGetComponent(out var solution)) { - if (hands.GetActiveHand != null) - { - if (hands.GetActiveHand.Owner.TryGetComponent(out var solution)) - { - if ((solution.Capabilities & SolutionCaps.PourIn) != 0 && - (component.Capabilities & SolutionCaps.PourOut) != 0) - { - var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? ""; - var myName = component.Owner.Prototype?.Name ?? ""; + data.Visibility = VerbVisibility.Invisible; + return; + } - data.Text = $"Transfer liquid from [{myName}] to [{heldEntityName}]."; - return; - } - } - } + if ((solution.Capabilities & SolutionCaps.PourIn) != 0 && + (component.Capabilities & SolutionCaps.PourOut) != 0) + { + var heldEntityName = hands.GetActiveHand.Owner?.Prototype?.Name ?? ""; + var myName = component.Owner.Prototype?.Name ?? ""; + + data.Text = $"Transfer liquid from [{myName}] to [{heldEntityName}]."; + return; } data.Visibility = VerbVisibility.Invisible; diff --git a/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs b/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs index 68d0c9847a..5ace07e054 100644 --- a/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs +++ b/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs @@ -154,6 +154,8 @@ namespace Content.Server.GameObjects State = DoorState.Open; SetAppearance(DoorVisualState.Open); }, _cancellationTokenSource.Token); + + Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new AccessReaderChangeMessage(Owner.Uid, false)); } public virtual bool CanClose() @@ -203,6 +205,7 @@ namespace Content.Server.GameObjects occluder.Enabled = true; } }, _cancellationTokenSource.Token); + Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new AccessReaderChangeMessage(Owner.Uid, true)); return true; } diff --git a/Content.Server/GameObjects/Components/Fluids/CanSpillComponent.cs b/Content.Server/GameObjects/Components/Fluids/CanSpillComponent.cs index 8d9d2e4ce5..2c4cfc80f3 100644 --- a/Content.Server/GameObjects/Components/Fluids/CanSpillComponent.cs +++ b/Content.Server/GameObjects/Components/Fluids/CanSpillComponent.cs @@ -1,5 +1,6 @@ using System; using Content.Server.GameObjects.Components.Chemistry; +using Content.Server.GameObjects.EntitySystems; using Content.Shared.Chemistry; using Content.Shared.GameObjects; using Robust.Shared.GameObjects; @@ -21,7 +22,8 @@ namespace Content.Server.GameObjects.Components.Fluids { protected override void GetData(IEntity user, CanSpillComponent component, VerbData data) { - if (!component.Owner.TryGetComponent(out SolutionComponent solutionComponent)) + if (!ActionBlockerSystem.CanInteract(user) || + !component.Owner.TryGetComponent(out SolutionComponent solutionComponent)) { data.Visibility = VerbVisibility.Invisible; return; diff --git a/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs b/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs index 71d920d5fb..2e58ab6b6c 100644 --- a/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs +++ b/Content.Server/GameObjects/Components/Interactable/HandheldLightComponent.cs @@ -240,6 +240,12 @@ namespace Content.Server.GameObjects.Components.Interactable { protected override void GetData(IEntity user, HandheldLightComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + if (component.Cell == null) { data.Text = "Eject cell (cell missing)"; diff --git a/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs index 0b94299c7c..e3a7bab178 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/EntityStorageComponent.cs @@ -338,8 +338,13 @@ namespace Content.Server.GameObjects.Components { protected override void GetData(IEntity user, EntityStorageComponent component, VerbData data) { - component.OpenVerbGetData(user, component, data); + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + component.OpenVerbGetData(user, component, data); } /// @@ -351,6 +356,12 @@ namespace Content.Server.GameObjects.Components protected virtual void OpenVerbGetData(IEntity user, EntityStorageComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + if (IsWeldedShut) { data.Visibility = VerbVisibility.Disabled; diff --git a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs index 9e71faa71e..0e34998d11 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ItemComponent.cs @@ -116,7 +116,9 @@ namespace Content.Server.GameObjects { protected override void GetData(IEntity user, ItemComponent component, VerbData data) { - if (ContainerHelpers.IsInContainer(component.Owner) || !component.CanPickup(user)) + if (!ActionBlockerSystem.CanInteract(user) || + ContainerHelpers.IsInContainer(component.Owner) || + !component.CanPickup(user)) { data.Visibility = VerbVisibility.Invisible; return; diff --git a/Content.Server/GameObjects/Components/Items/Storage/SecureEntityStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/SecureEntityStorageComponent.cs index 34ab66e526..d37f3b79c7 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/SecureEntityStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/SecureEntityStorageComponent.cs @@ -139,7 +139,7 @@ namespace Content.Server.GameObjects.Components.Items.Storage { protected override void GetData(IEntity user, SecureEntityStorageComponent component, VerbData data) { - if (component.Open) + if (!ActionBlockerSystem.CanInteract(user) || component.Open) { data.Visibility = VerbVisibility.Invisible; return; diff --git a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs index efff79896e..85efd110b4 100644 --- a/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs +++ b/Content.Server/GameObjects/Components/Items/Storage/ServerStorageComponent.cs @@ -60,7 +60,7 @@ namespace Content.Server.GameObjects base.ExposeData(serializer); serializer.DataField(ref StorageCapacityMax, "Capacity", 10000); - serializer.DataField(ref StorageUsed, "used", 0); + //serializer.DataField(ref StorageUsed, "used", 0); } /// @@ -348,7 +348,7 @@ namespace Content.Server.GameObjects foreach (var entity in storage.ContainedEntities) { - var item = entity.GetComponent(); + var item = entity.GetComponent(); StorageUsed += item.ObjectSize; } diff --git a/Content.Server/GameObjects/Components/Markers/ConditionalSpawnerComponent.cs b/Content.Server/GameObjects/Components/Markers/ConditionalSpawnerComponent.cs index a41c72b31d..53dfca85c1 100644 --- a/Content.Server/GameObjects/Components/Markers/ConditionalSpawnerComponent.cs +++ b/Content.Server/GameObjects/Components/Markers/ConditionalSpawnerComponent.cs @@ -2,18 +2,15 @@ using System; using System.Collections.Generic; using Content.Server.GameTicking; using Content.Server.Interfaces.GameTicking; -using NFluidsynth; using Robust.Server.Interfaces.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Random; using Robust.Shared.Interfaces.Reflection; using Robust.Shared.IoC; -using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; -using SQLitePCL; using Logger = Robust.Shared.Log.Logger; namespace Content.Server.GameObjects.Components.Markers diff --git a/Content.Server/GameObjects/Components/Markers/TimedSpawnerComponent.cs b/Content.Server/GameObjects/Components/Markers/TimedSpawnerComponent.cs new file mode 100644 index 0000000000..96aa996204 --- /dev/null +++ b/Content.Server/GameObjects/Components/Markers/TimedSpawnerComponent.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Random; +using Robust.Shared.IoC; +using Robust.Shared.Random; +using Robust.Shared.Serialization; +using Robust.Shared.ViewVariables; +using Timer = Robust.Shared.Timers.Timer; + +namespace Content.Server.GameObjects.Components.Markers +{ + [RegisterComponent] + public class TimedSpawnerComponent : Component + { +#pragma warning disable 649 + [Dependency] private IEntityManager _entityManager; + [Dependency] private IRobustRandom _robustRandom; +#pragma warning restore 649 + + public override string Name => "TimedSpawner"; + + [ViewVariables(VVAccess.ReadWrite)] + public List Prototypes { get; set; } = new List(); + + [ViewVariables(VVAccess.ReadWrite)] + public float Chance { get; set; } = 1.0f; + + [ViewVariables(VVAccess.ReadWrite)] + public int IntervalSeconds { get; set; } = 60; + + [ViewVariables(VVAccess.ReadWrite)] + public int MinimumEntitiesSpawned { get; set; } = 1; + + [ViewVariables(VVAccess.ReadWrite)] + public int MaximumEntitiesSpawned { get; set; } = 1; + + private CancellationTokenSource TokenSource; + + public override void Initialize() + { + base.Initialize(); + SetupTimer(); + } + + protected override void Shutdown() + { + base.Shutdown(); + TokenSource.Cancel(); + } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(this, x => Prototypes, "prototypes", new List()); + serializer.DataField(this, x => Chance, "chance", 1.0f); + serializer.DataField(this, x => IntervalSeconds, "intervalSeconds", 60); + serializer.DataField(this, x => MinimumEntitiesSpawned, "minimumEntitiesSpawned", 1); + serializer.DataField(this, x => MaximumEntitiesSpawned, "maximumEntitiesSpawned", 1); + + if(MinimumEntitiesSpawned > MaximumEntitiesSpawned) + throw new ArgumentException("MaximumEntitiesSpawned can't be lower than MinimumEntitiesSpawned!"); + } + + private void SetupTimer() + { + TokenSource?.Cancel(); + TokenSource = new CancellationTokenSource(); + Timer.SpawnRepeating(TimeSpan.FromSeconds(IntervalSeconds), OnTimerFired, TokenSource.Token); + } + + private void OnTimerFired() + { + if (!_robustRandom.Prob(Chance)) + return; + + var number = _robustRandom.Next(MinimumEntitiesSpawned, MaximumEntitiesSpawned); + + for (int i = 0; i < number; i++) + { + var entity = _robustRandom.Pick(Prototypes); + _entityManager.SpawnEntity(entity, Owner.Transform.GridPosition); + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs b/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs index d3ef519309..f309e1bce5 100644 --- a/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs +++ b/Content.Server/GameObjects/Components/Medical/MedicalScannerComponent.cs @@ -133,6 +133,12 @@ namespace Content.Server.GameObjects.Components.Medical { protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.Text = "Enter"; data.Visibility = component.IsOccupied ? VerbVisibility.Invisible : VerbVisibility.Visible; } @@ -148,6 +154,12 @@ namespace Content.Server.GameObjects.Components.Medical { protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.Text = "Eject"; data.Visibility = component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible; } diff --git a/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs b/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs index 8948752b88..fbbd165230 100644 --- a/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/StunnableComponent.cs @@ -192,6 +192,17 @@ namespace Content.Server.GameObjects.Components.Mobs _statusRemoveCancellation = new CancellationTokenSource(); } + public void ResetStuns() + { + _stunnedTimer = 0f; + _slowdownTimer = 0f; + + if (KnockedDown) + StandingStateHelper.Standing(Owner); + + _knockdownTimer = 0f; + } + public void Update(float delta) { if (Stunned) diff --git a/Content.Server/GameObjects/Components/PDA/PDAComponent.cs b/Content.Server/GameObjects/Components/PDA/PDAComponent.cs index f70cd43a9e..a6dd1deb05 100644 --- a/Content.Server/GameObjects/Components/PDA/PDAComponent.cs +++ b/Content.Server/GameObjects/Components/PDA/PDAComponent.cs @@ -250,6 +250,12 @@ namespace Content.Server.GameObjects.Components.PDA { protected override void GetData(IEntity user, PDAComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.Text = Loc.GetString("Eject ID"); data.Visibility = component.IdSlotEmpty ? VerbVisibility.Invisible : VerbVisibility.Visible; } diff --git a/Content.Server/GameObjects/Components/Power/Chargers/PowerCellChargerComponent.cs b/Content.Server/GameObjects/Components/Power/Chargers/PowerCellChargerComponent.cs index 526bff9b45..f904673e37 100644 --- a/Content.Server/GameObjects/Components/Power/Chargers/PowerCellChargerComponent.cs +++ b/Content.Server/GameObjects/Components/Power/Chargers/PowerCellChargerComponent.cs @@ -61,6 +61,12 @@ namespace Content.Server.GameObjects.Components.Power.Chargers { protected override void GetData(IEntity user, PowerCellChargerComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + if (!user.TryGetComponent(out HandsComponent handsComponent)) { data.Visibility = VerbVisibility.Invisible; @@ -99,6 +105,12 @@ namespace Content.Server.GameObjects.Components.Power.Chargers { protected override void GetData(IEntity user, PowerCellChargerComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + if (component._container.ContainedEntity == null) { data.Text = "Eject"; diff --git a/Content.Server/GameObjects/Components/Power/Chargers/WeaponCapacitorChargerComponent.cs b/Content.Server/GameObjects/Components/Power/Chargers/WeaponCapacitorChargerComponent.cs index a6530f5daf..2bb1bee4d5 100644 --- a/Content.Server/GameObjects/Components/Power/Chargers/WeaponCapacitorChargerComponent.cs +++ b/Content.Server/GameObjects/Components/Power/Chargers/WeaponCapacitorChargerComponent.cs @@ -46,6 +46,12 @@ namespace Content.Server.GameObjects.Components.Power.Chargers { protected override void GetData(IEntity user, WeaponCapacitorChargerComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + if (!user.TryGetComponent(out HandsComponent handsComponent)) { data.Visibility = VerbVisibility.Invisible; @@ -89,6 +95,12 @@ namespace Content.Server.GameObjects.Components.Power.Chargers { protected override void GetData(IEntity user, WeaponCapacitorChargerComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + if (component._container.ContainedEntity == null) { data.Visibility = VerbVisibility.Disabled; diff --git a/Content.Server/GameObjects/Components/RotatableComponent.cs b/Content.Server/GameObjects/Components/RotatableComponent.cs index 01eba427dc..bd45b22e98 100644 --- a/Content.Server/GameObjects/Components/RotatableComponent.cs +++ b/Content.Server/GameObjects/Components/RotatableComponent.cs @@ -1,3 +1,4 @@ +using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; using Content.Shared.GameObjects; using Robust.Server.GameObjects; @@ -37,6 +38,12 @@ namespace Content.Server.GameObjects.Components { protected override void GetData(IEntity user, RotatableComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.CategoryData = VerbCategories.Rotate; data.Text = "Rotate clockwise"; data.IconTexture = "/Textures/UserInterface/VerbIcons/rotate_cw.svg.96dpi.png"; @@ -53,6 +60,12 @@ namespace Content.Server.GameObjects.Components { protected override void GetData(IEntity user, RotatableComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.CategoryData = VerbCategories.Rotate; data.Text = "Rotate counter-clockwise"; data.IconTexture = "/Textures/UserInterface/VerbIcons/rotate_ccw.svg.96dpi.png"; diff --git a/Content.Server/GameObjects/Components/Weapon/Melee/StunbatonComponent.cs b/Content.Server/GameObjects/Components/Weapon/Melee/StunbatonComponent.cs index 2c9985e99b..15df71beab 100644 --- a/Content.Server/GameObjects/Components/Weapon/Melee/StunbatonComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Melee/StunbatonComponent.cs @@ -39,7 +39,10 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee [ViewVariables] private ContainerSlot _cellContainer; [ViewVariables(VVAccess.ReadWrite)] - private float _paralyzeChance = 0.25f; + private float _paralyzeChanceNoSlowdown = 0.35f; + + [ViewVariables(VVAccess.ReadWrite)] + private float _paralyzeChanceWithSlowdown = 0.85f; [ViewVariables(VVAccess.ReadWrite)] private float _paralyzeTime = 10f; @@ -75,7 +78,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee { base.ExposeData(serializer); - serializer.DataField(ref _paralyzeChance, "paralyzeChance", 0.25f); + serializer.DataField(ref _paralyzeChanceNoSlowdown, "paralyzeChanceNoSlowdown", 0.35f); + serializer.DataField(ref _paralyzeChanceWithSlowdown, "paralyzeChanceWithSlowdown", 0.85f); serializer.DataField(ref _paralyzeTime, "paralyzeTime", 10f); serializer.DataField(ref _slowdownTime, "slowdownTime", 5f); } @@ -92,10 +96,16 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee { if (!entity.TryGetComponent(out StunnableComponent stunnable)) continue; - if(_robustRandom.Prob(_paralyzeChance)) - stunnable.Paralyze(_paralyzeTime); + if(!stunnable.SlowedDown) + if(_robustRandom.Prob(_paralyzeChanceNoSlowdown)) + stunnable.Paralyze(_paralyzeTime); + else + stunnable.Slowdown(_slowdownTime); else - stunnable.Slowdown(_slowdownTime); + if(_robustRandom.Prob(_paralyzeChanceWithSlowdown)) + stunnable.Paralyze(_paralyzeTime); + else + stunnable.Slowdown(_slowdownTime); } cell.DeductCharge(EnergyPerUse); @@ -252,6 +262,12 @@ namespace Content.Server.GameObjects.Components.Weapon.Melee { protected override void GetData(IEntity user, StunbatonComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + if (component.Cell == null) { data.Text = "Eject cell (cell missing)"; diff --git a/Content.Server/GameObjects/Components/Weapon/Melee/UnarmedCombatComponent.cs b/Content.Server/GameObjects/Components/Weapon/Melee/UnarmedCombatComponent.cs new file mode 100644 index 0000000000..9841f283a0 --- /dev/null +++ b/Content.Server/GameObjects/Components/Weapon/Melee/UnarmedCombatComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameObjects; + +namespace Content.Server.GameObjects.Components.Weapon.Melee +{ + [RegisterComponent] + public class UnarmedCombatComponent : MeleeWeaponComponent + { + public override string Name => "UnarmedCombat"; + } +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Ammunition/AmmoBoxComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Ammunition/AmmoBoxComponent.cs index 5918dae974..40d48ee25b 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Ammunition/AmmoBoxComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Ammunition/AmmoBoxComponent.cs @@ -63,9 +63,9 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition _ammoContainer.Insert(entity); } } - + } - + void IMapInit.MapInit() { _unspawnedCount += _capacity; @@ -117,7 +117,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition Owner.PopupMessage(user, Loc.GetString("No room")); return false; } - + _spawnedAmmo.Push(entity); _ammoContainer.Insert(entity); UpdateAppearance(); @@ -136,7 +136,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition for (var i = 0; i < Math.Max(10, rangedMagazine.ShotsLeft); i++) { var ammo = rangedMagazine.TakeAmmo(); - + if (!TryInsertAmmo(eventArgs.User, ammo)) { rangedMagazine.TryInsertAmmo(eventArgs.User, ammo); @@ -146,7 +146,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition return true; } - + return false; } @@ -175,7 +175,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition { var ejectCount = Math.Min(count, Capacity); var ejectAmmo = new List(ejectCount); - + for (var i = 0; i < Math.Min(count, Capacity); i++) { var ammo = TakeAmmo(); @@ -200,13 +200,19 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition { return TryUse(eventArgs.User); } - + // So if you have 200 rounds in a box and that suddenly creates 200 entities you're not having a fun time [Verb] private sealed class DumpVerb : Verb { protected override void GetData(IEntity user, AmmoBoxComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.Text = Loc.GetString("Dump 10"); data.Visibility = component.AmmoLeft > 0 ? VerbVisibility.Visible : VerbVisibility.Disabled; } @@ -217,4 +223,4 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition } } } -} \ No newline at end of file +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/BoltActionBarrelComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/BoltActionBarrelComponent.cs index 5045361a7e..a5bfbbe7a3 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/BoltActionBarrelComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/BoltActionBarrelComponent.cs @@ -28,7 +28,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { // Originally I had this logic shared with PumpBarrel and used a couple of variables to control things // but it felt a lot messier to play around with, especially when adding verbs - + public override string Name => "BoltActionBarrel"; public override int ShotsLeft @@ -62,7 +62,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels } var soundSystem = EntitySystem.Get(); - + if (value) { if (_soundBoltOpen != null) @@ -77,7 +77,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels soundSystem.PlayAtCoords(_soundBoltClosed, Owner.Transform.GridPosition, AudioParams.Default.WithVolume(-2)); } } - + _boltOpen = value; UpdateAppearance(); } @@ -105,7 +105,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels serializer.DataField(ref _soundBoltClosed, "soundBoltClosed", "/Audio/Guns/Bolt/rifle_bolt_closed.ogg"); serializer.DataField(ref _soundInsert, "soundInsert", "/Audio/Guns/MagIn/bullet_insert.ogg"); } - + void IMapInit.MapInit() { if (_fillPrototype != null) @@ -137,7 +137,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { _appearanceComponent = appearanceComponent; } - + _appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, true); UpdateAppearance(); } @@ -183,7 +183,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels var ammoComponent = chamberedEntity.GetComponent(); if (!ammoComponent.Caseless) { - EjectCasing(chamberedEntity); + EjectCasing(chamberedEntity); } } @@ -216,7 +216,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels EntitySystem.Get().PlayAtCoords(_soundCycle, Owner.Transform.GridPosition, AudioParams.Default.WithVolume(-2)); } } - + Dirty(); UpdateAppearance(); } @@ -264,9 +264,9 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels UpdateAppearance(); return true; } - + Owner.PopupMessage(user, Loc.GetString("No room")); - + return false; } @@ -279,7 +279,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels // Dirty(); return true; } - + Cycle(true); return true; } @@ -288,12 +288,18 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { return TryInsertBullet(eventArgs.User, eventArgs.Using); } - + [Verb] private sealed class OpenBoltVerb : Verb { protected override void GetData(IEntity user, BoltActionBarrelComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.Text = Loc.GetString("Open bolt"); data.Visibility = component.BoltOpen ? VerbVisibility.Disabled : VerbVisibility.Visible; } @@ -303,12 +309,18 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels component.BoltOpen = true; } } - + [Verb] private sealed class CloseBoltVerb : Verb { protected override void GetData(IEntity user, BoltActionBarrelComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.Text = Loc.GetString("Close bolt"); data.Visibility = component.BoltOpen ? VerbVisibility.Visible : VerbVisibility.Disabled; } @@ -319,4 +331,4 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels } } } -} \ No newline at end of file +} diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/RevolverBarrelComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/RevolverBarrelComponent.cs index 04c2c7c56f..d5f5540f28 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/RevolverBarrelComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/RevolverBarrelComponent.cs @@ -42,8 +42,15 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { base.ExposeData(serializer); serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified); - var capacity = serializer.ReadDataField("capacity", 6); - _ammoSlots = new IEntity[capacity]; + + if (serializer.Reading) + { + var capacity = serializer.ReadDataField("capacity", 6); + _ammoSlots = new IEntity[capacity]; + } + + // TODO: Writing? + // Sounds serializer.DataField(ref _soundEject, "soundEject", "/Audio/Guns/MagOut/revolver_magout.ogg"); @@ -60,7 +67,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { _appearanceComponent = appearanceComponent; } - + _appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, true); } @@ -78,7 +85,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { return false; } - + if (ammoComponent.Caliber != _caliber) { Owner.PopupMessage(user, Loc.GetString("Wrong caliber")); @@ -208,12 +215,18 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { return TryInsertBullet(eventArgs.User, eventArgs.Using); } - + [Verb] private sealed class SpinRevolverVerb : Verb { protected override void GetData(IEntity user, RevolverBarrelComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.Text = Loc.GetString("Spin"); if (component.Capacity <= 1) { diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerBatteryBarrelComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerBatteryBarrelComponent.cs index e854ee8480..9d49d30257 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerBatteryBarrelComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerBatteryBarrelComponent.cs @@ -170,7 +170,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { if (energyRatio < 1.0) { - var newDamages = new Dictionary(projectileComponent.Damages); + var newDamages = new Dictionary(projectileComponent.Damages.Count); foreach (var (damageType, damage) in projectileComponent.Damages) { newDamages.Add(damageType, (int) (damage * energyRatio)); diff --git a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerMagazineBarrelComponent.cs b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerMagazineBarrelComponent.cs index 5bfc0ede89..d4863587a7 100644 --- a/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerMagazineBarrelComponent.cs +++ b/Content.Server/GameObjects/Components/Weapon/Ranged/Barrels/ServerMagazineBarrelComponent.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition; using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects; +using Content.Shared.GameObjects.Components.Weapons.Ranged; using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels; using Content.Shared.Interfaces; using Robust.Server.GameObjects; @@ -25,7 +26,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { public override string Name => "MagazineBarrel"; public override uint? NetID => ContentNetIDs.MAGAZINE_BARREL; - + private ContainerSlot _chamberContainer; [ViewVariables] public bool HasMagazine => _magazineContainer.ContainedEntity != null; private ContainerSlot _magazineContainer; @@ -117,11 +118,11 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { count = (rangedMagazineComponent.ShotsLeft, rangedMagazineComponent.Capacity); } - + return new MagazineBarrelComponentState( - _chamberContainer.ContainedEntity != null, - FireRateSelector, - count, + _chamberContainer.ContainedEntity != null, + FireRateSelector, + count, SoundGunshot); } @@ -133,7 +134,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { _appearanceComponent = appearanceComponent; } - + _chamberContainer = ContainerManagerComponent.Ensure($"{Name}-chamber", Owner); _magazineContainer = ContainerManagerComponent.Ensure($"{Name}-magazine", Owner); } @@ -193,7 +194,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels var ammoComponent = chamberEntity.GetComponent(); if (!ammoComponent.Caseless) { - EjectCasing(chamberEntity); + EjectCasing(chamberEntity); } } @@ -206,7 +207,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels // If you're really into gunporn you could put a sound here _chamberContainer.Insert(nextRound); } - + var soundSystem = EntitySystem.Get(); if (_autoEjectMag && magazine != null && magazine.GetComponent().ShotsLeft == 0) @@ -217,6 +218,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels } _magazineContainer.Remove(magazine); + SendNetworkMessage(new MagazineAutoEjectMessage()); } if (nextRound == null && !BoltOpen) @@ -243,7 +245,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels soundSystem.PlayAtCoords(_soundRack, Owner.Transform.GridPosition, AudioParams.Default.WithVolume(-2)); } } - + Dirty(); UpdateAppearance(); } @@ -308,7 +310,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels { handsComponent.PutInHandOrDrop(mag.GetComponent()); } - + Dirty(); UpdateAppearance(); } @@ -383,12 +385,18 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels return false; } - + [Verb] private sealed class EjectMagazineVerb : Verb { protected override void GetData(IEntity user, ServerMagazineBarrelComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.Text = Loc.GetString("Eject magazine"); if (component.MagNeedsOpenBolt) { @@ -406,12 +414,18 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels component.RemoveMagazine(user); } } - + [Verb] private sealed class OpenBoltVerb : Verb { protected override void GetData(IEntity user, ServerMagazineBarrelComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.Text = Loc.GetString("Open bolt"); data.Visibility = component.BoltOpen ? VerbVisibility.Disabled : VerbVisibility.Visible; } @@ -421,12 +435,18 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels component.ToggleBolt(); } } - + [Verb] private sealed class CloseBoltVerb : Verb { protected override void GetData(IEntity user, ServerMagazineBarrelComponent component, VerbData data) { + if (!ActionBlockerSystem.CanInteract(user)) + { + data.Visibility = VerbVisibility.Invisible; + return; + } + data.Text = Loc.GetString("Close bolt"); data.Visibility = component.BoltOpen ? VerbVisibility.Visible : VerbVisibility.Disabled; } diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/CollidableMove.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/CollidableMove.cs deleted file mode 100644 index e5df90943d..0000000000 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/CollidableMove.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Robust.Shared.GameObjects.Components.Transform; - -namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.GraphUpdates -{ - public struct CollidableMove : IPathfindingGraphUpdate - { - public MoveEvent MoveEvent { get; } - - public CollidableMove(MoveEvent moveEvent) - { - MoveEvent = moveEvent; - } - } -} diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/CollisionChange.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/CollisionChange.cs deleted file mode 100644 index 0d7dde253a..0000000000 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/CollisionChange.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.Interfaces.GameObjects; - -namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.GraphUpdates -{ - public class CollisionChange : IPathfindingGraphUpdate - { - public IEntity Owner { get; } - public bool Value { get; } - - public CollisionChange(IEntity owner, bool value) - { - Owner = owner; - Value = value; - } - } -} diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/GridRemoval.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/GridRemoval.cs deleted file mode 100644 index 30ee86f2aa..0000000000 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/GridRemoval.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Robust.Shared.Map; - -namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.GraphUpdates -{ - public struct GridRemoval : IPathfindingGraphUpdate - { - public GridId GridId { get; } - - public GridRemoval(GridId gridId) - { - GridId = gridId; - } - } -} diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/IPathfindingGraphUpdate.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/IPathfindingGraphUpdate.cs deleted file mode 100644 index 69aa5c1eac..0000000000 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/IPathfindingGraphUpdate.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.GraphUpdates -{ - public interface IPathfindingGraphUpdate - { - - } -} diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/TileUpdate.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/TileUpdate.cs deleted file mode 100644 index 501e4dabb8..0000000000 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/GraphUpdates/TileUpdate.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Robust.Shared.Map; - -namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.GraphUpdates -{ - public struct TileUpdate : IPathfindingGraphUpdate - { - public TileUpdate(TileRef tile) - { - Tile = tile; - } - - public TileRef Tile { get; } - } -} diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/AStarPathfindingJob.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/AStarPathfindingJob.cs index 1ac13372ad..e69b9e06d8 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/AStarPathfindingJob.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/AStarPathfindingJob.cs @@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders } // If we couldn't get a nearby node that's good enough - if (!Utils.TryEndNode(ref _endNode, _pathfindingArgs)) + if (!PathfindingHelpers.TryEndNode(ref _endNode, _pathfindingArgs)) { return null; } @@ -88,9 +88,9 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders } // If tile is untraversable it'll be null - var tileCost = Utils.GetTileCost(_pathfindingArgs, currentNode, nextNode); + var tileCost = PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, nextNode); - if (tileCost == null || !Utils.DirectionTraversable(_pathfindingArgs.CollisionMask, currentNode, direction)) + if (tileCost == null || !PathfindingHelpers.DirectionTraversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, currentNode, direction)) { continue; } @@ -107,7 +107,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders // pFactor is tie-breaker where the fscore is otherwise equal. // See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#breaking-ties // There's other ways to do it but future consideration - var fScore = gScores[nextNode] + Utils.OctileDistance(_endNode, nextNode) * (1.0f + 1.0f / 1000.0f); + var fScore = gScores[nextNode] + PathfindingHelpers.OctileDistance(_endNode, nextNode) * (1.0f + 1.0f / 1000.0f); openTiles.Add((fScore, nextNode)); } } @@ -117,7 +117,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders return null; } - var route = Utils.ReconstructPath(cameFrom, currentNode); + var route = PathfindingHelpers.ReconstructPath(cameFrom, currentNode); if (route.Count == 1) { diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/JpsPathfindingJob.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/JpsPathfindingJob.cs index 2b12b1b456..ffae0d7d89 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/JpsPathfindingJob.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/JpsPathfindingJob.cs @@ -41,7 +41,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders } // If we couldn't get a nearby node that's good enough - if (!Utils.TryEndNode(ref _endNode, _pathfindingArgs)) + if (!PathfindingHelpers.TryEndNode(ref _endNode, _pathfindingArgs)) { return null; } @@ -89,7 +89,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders jumpNodes.Add(jumpNode); #endif // GetJumpPoint should already check if we can traverse to the node - var tileCost = Utils.GetTileCost(_pathfindingArgs, currentNode, jumpNode); + var tileCost = PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, jumpNode); if (tileCost == null) { @@ -108,7 +108,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders // pFactor is tie-breaker where the fscore is otherwise equal. // See http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html#breaking-ties // There's other ways to do it but future consideration - var fScore = gScores[jumpNode] + Utils.OctileDistance(_endNode, jumpNode) * (1.0f + 1.0f / 1000.0f); + var fScore = gScores[jumpNode] + PathfindingHelpers.OctileDistance(_endNode, jumpNode) * (1.0f + 1.0f / 1000.0f); openTiles.Add((fScore, jumpNode)); } } @@ -119,7 +119,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders return null; } - var route = Utils.ReconstructJumpPath(cameFrom, currentNode); + var route = PathfindingHelpers.ReconstructJumpPath(cameFrom, currentNode); if (route.Count == 1) { return null; @@ -161,7 +161,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders // We'll do opposite DirectionTraversable just because of how the method's setup // Nodes should be 2-way anyway. if (nextNode == null || - Utils.GetTileCost(_pathfindingArgs, currentNode, nextNode) == null) + PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, nextNode) == null) { return null; } @@ -312,14 +312,14 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders throw new ArgumentOutOfRangeException(); } - if ((closedNeighborOne == null || Utils.GetTileCost(_pathfindingArgs, currentNode, closedNeighborOne) == null) - && openNeighborOne != null && Utils.GetTileCost(_pathfindingArgs, currentNode, openNeighborOne) != null) + if ((closedNeighborOne == null || PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, closedNeighborOne) == null) + && openNeighborOne != null && PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, openNeighborOne) != null) { return true; } - if ((closedNeighborTwo == null || Utils.GetTileCost(_pathfindingArgs, currentNode, closedNeighborTwo) == null) - && openNeighborTwo != null && Utils.GetTileCost(_pathfindingArgs, currentNode, openNeighborTwo) != null) + if ((closedNeighborTwo == null || PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, closedNeighborTwo) == null) + && openNeighborTwo != null && PathfindingHelpers.GetTileCost(_pathfindingArgs, currentNode, openNeighborTwo) != null) { return true; } @@ -371,14 +371,14 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders throw new ArgumentOutOfRangeException(); } - if ((closedNeighborOne == null || !Utils.Traversable(_pathfindingArgs.CollisionMask, closedNeighborOne.CollisionMask)) && - (openNeighborOne != null && Utils.Traversable(_pathfindingArgs.CollisionMask, openNeighborOne.CollisionMask))) + if ((closedNeighborOne == null || !PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, closedNeighborOne)) && + (openNeighborOne != null && PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, openNeighborOne))) { return true; } - if ((closedNeighborTwo == null || !Utils.Traversable(_pathfindingArgs.CollisionMask, closedNeighborTwo.CollisionMask)) && - (openNeighborTwo != null && Utils.Traversable(_pathfindingArgs.CollisionMask, openNeighborTwo.CollisionMask))) + if ((closedNeighborTwo == null || !PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, closedNeighborTwo)) && + (openNeighborTwo != null && PathfindingHelpers.Traversable(_pathfindingArgs.CollisionMask, _pathfindingArgs.Access, openNeighborTwo))) { return true; } diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/PathfindingArgs.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/PathfindingArgs.cs index d2a01ea1ce..5b7e9ce3c6 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/PathfindingArgs.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Pathfinders/PathfindingArgs.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -6,6 +7,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders public struct PathfindingArgs { public EntityUid Uid { get; } + public ICollection Access { get; } public int CollisionMask { get; } public TileRef Start { get; } public TileRef End { get; } @@ -20,6 +22,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders public PathfindingArgs( EntityUid entityUid, + ICollection access, int collisionMask, TileRef start, TileRef end, @@ -29,6 +32,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders bool allowSpace = false) { Uid = entityUid; + Access = access; CollisionMask = collisionMask; Start = start; End = end; diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Utils.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingHelpers.cs similarity index 79% rename from Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Utils.cs rename to Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingHelpers.cs index a72869db78..127c43a8d4 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/Utils.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingHelpers.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Content.Server.GameObjects.Components.Access; using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders; using Content.Server.GameObjects.EntitySystems.Pathfinding; using Robust.Shared.Interfaces.GameObjects; @@ -10,19 +11,19 @@ using Robust.Shared.Maths; namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding { - public static class Utils + public static class PathfindingHelpers { public static bool TryEndNode(ref PathfindingNode endNode, PathfindingArgs pathfindingArgs) { - if (!Traversable(pathfindingArgs.CollisionMask, endNode.CollisionMask)) + if (!Traversable(pathfindingArgs.CollisionMask, pathfindingArgs.Access, endNode)) { if (pathfindingArgs.Proximity > 0.0f) { // TODO: Should make this account for proximities, // probably some kind of breadth-first search to find a valid one - foreach (var (direction, node) in endNode.Neighbors) + foreach (var (_, node) in endNode.Neighbors) { - if (Traversable(pathfindingArgs.CollisionMask, node.CollisionMask)) + if (Traversable(pathfindingArgs.CollisionMask, pathfindingArgs.Access, node)) { endNode = node; return true; @@ -36,7 +37,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding return true; } - public static bool DirectionTraversable(int collisionMask, PathfindingNode currentNode, Direction direction) + public static bool DirectionTraversable(int collisionMask, ICollection access, PathfindingNode currentNode, Direction direction) { // If it's a diagonal we need to check NSEW to see if we can get to it and stop corner cutting, NE needs N and E etc. // Given there's different collision layers stored for each node in the graph it's probably not worth it to cache this @@ -51,32 +52,32 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding { case Direction.NorthEast: if (northNeighbor == null || eastNeighbor == null) return false; - if (!Traversable(collisionMask, northNeighbor.CollisionMask) || - !Traversable(collisionMask, eastNeighbor.CollisionMask)) + if (!Traversable(collisionMask, access, northNeighbor) || + !Traversable(collisionMask, access, eastNeighbor)) { return false; } break; case Direction.NorthWest: if (northNeighbor == null || westNeighbor == null) return false; - if (!Traversable(collisionMask, northNeighbor.CollisionMask) || - !Traversable(collisionMask, westNeighbor.CollisionMask)) + if (!Traversable(collisionMask, access, northNeighbor) || + !Traversable(collisionMask, access, westNeighbor)) { return false; } break; case Direction.SouthWest: if (southNeighbor == null || westNeighbor == null) return false; - if (!Traversable(collisionMask, southNeighbor.CollisionMask) || - !Traversable(collisionMask, westNeighbor.CollisionMask)) + if (!Traversable(collisionMask, access, southNeighbor) || + !Traversable(collisionMask, access, westNeighbor)) { return false; } break; case Direction.SouthEast: if (southNeighbor == null || eastNeighbor == null) return false; - if (!Traversable(collisionMask, southNeighbor.CollisionMask) || - !Traversable(collisionMask, eastNeighbor.CollisionMask)) + if (!Traversable(collisionMask, access, southNeighbor) || + !Traversable(collisionMask, access, eastNeighbor)) { return false; } @@ -86,11 +87,24 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding return true; } - public static bool Traversable(int collisionMask, int nodeMask) + public static bool Traversable(int collisionMask, ICollection access, PathfindingNode node) { - return (collisionMask & nodeMask) == 0; - } + if ((collisionMask & node.BlockedCollisionMask) != 0) + { + return false; + } + foreach (var reader in node.AccessReaders) + { + if (!reader.IsAllowed(access)) + { + return false; + } + } + + return true; + } + public static Queue ReconstructPath(Dictionary cameFrom, PathfindingNode current) { var running = new Stack(); @@ -194,6 +208,20 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding return 1.4f * dstX + (dstY - dstX); } + + public static float OctileDistance(TileRef endTile, TileRef startTile) + { + // "Fast Euclidean" / octile. + // This implementation is written down in a few sources; it just saves doing sqrt. + int dstX = Math.Abs(startTile.X - endTile.X); + int dstY = Math.Abs(startTile.Y - endTile.Y); + if (dstX > dstY) + { + return 1.4f * dstY + (dstX - dstY); + } + + return 1.4f * dstX + (dstY - dstX); + } public static float ManhattanDistance(PathfindingNode endNode, PathfindingNode currentNode) { @@ -202,7 +230,7 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding public static float? GetTileCost(PathfindingArgs pathfindingArgs, PathfindingNode start, PathfindingNode end) { - if (!pathfindingArgs.NoClip && !Traversable(pathfindingArgs.CollisionMask, end.CollisionMask)) + if (!pathfindingArgs.NoClip && !Traversable(pathfindingArgs.CollisionMask, pathfindingArgs.Access, end)) { return null; } diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingNode.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingNode.cs index b9b110a566..262631a4b3 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingNode.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingNode.cs @@ -1,6 +1,12 @@ using System; using System.Collections.Generic; +using Content.Server.GameObjects.Components.Access; +using Content.Server.GameObjects.Components.Doors; using Content.Server.GameObjects.EntitySystems.AI.Pathfinding; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Map; using Robust.Shared.Maths; @@ -8,27 +14,34 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding { public class PathfindingNode { - // TODO: Add access ID here public PathfindingChunk ParentChunk => _parentChunk; private readonly PathfindingChunk _parentChunk; - public TileRef TileRef { get; private set; } - public List CollisionLayers { get; } - public int CollisionMask { get; private set; } + public Dictionary Neighbors => _neighbors; private Dictionary _neighbors = new Dictionary(); + + public TileRef TileRef { get; private set; } + + /// + /// Whenever there's a change in the collision layers we update the mask as the graph has more reads than writes + /// + public int BlockedCollisionMask { get; private set; } + private readonly Dictionary _blockedCollidables = new Dictionary(0); - public PathfindingNode(PathfindingChunk parent, TileRef tileRef, List collisionLayers = null) + public IReadOnlyCollection PhysicsUids => _physicsUids; + private readonly HashSet _physicsUids = new HashSet(0); + + /// + /// The entities on this tile that require access to traverse + /// + /// We don't store the ICollection, at least for now, as we'd need to replicate the access code here + public IReadOnlyCollection AccessReaders => _accessReaders.Values; + private readonly Dictionary _accessReaders = new Dictionary(0); + + public PathfindingNode(PathfindingChunk parent, TileRef tileRef) { _parentChunk = parent; TileRef = tileRef; - if (collisionLayers == null) - { - CollisionLayers = new List(); - } - else - { - CollisionLayers = collisionLayers; - } GenerateMask(); } @@ -105,25 +118,70 @@ namespace Content.Server.GameObjects.EntitySystems.Pathfinding TileRef = newTile; } - public void AddCollisionLayer(int layer) + /// + /// Call if this entity is relevant for the pathfinder + /// + /// + /// TODO: These 2 methods currently don't account for a bunch of changes (e.g. airlock unpowered, wrenching, etc.) + public void AddEntity(IEntity entity) { - CollisionLayers.Add(layer); - GenerateMask(); + // If we're a door + if (entity.HasComponent() || entity.HasComponent()) + { + // If we need access to traverse this then add to readers, otherwise no point adding it (except for maybe tile costs in future) + // TODO: Check for powered I think (also need an event for when it's depowered + // AccessReader calls this whenever opening / closing but it can seem to get called multiple times + // Which may or may not be intended? + if (entity.TryGetComponent(out AccessReader accessReader) && !_accessReaders.ContainsKey(entity.Uid)) + { + _accessReaders.Add(entity.Uid, accessReader); + } + return; + } + + if (entity.TryGetComponent(out CollidableComponent collidableComponent)) + { + if (entity.TryGetComponent(out PhysicsComponent physicsComponent) && !physicsComponent.Anchored) + { + _physicsUids.Add(entity.Uid); + } + else + { + _blockedCollidables.TryAdd(entity.Uid, collidableComponent.CollisionLayer); + GenerateMask(); + } + } } - public void RemoveCollisionLayer(int layer) + public void RemoveEntity(IEntity entity) { - CollisionLayers.Remove(layer); - GenerateMask(); + if (_accessReaders.ContainsKey(entity.Uid)) + { + _accessReaders.Remove(entity.Uid); + return; + } + + if (entity.HasComponent()) + { + if (entity.TryGetComponent(out PhysicsComponent physicsComponent) && physicsComponent.Anchored) + { + _blockedCollidables.Remove(entity.Uid); + GenerateMask(); + } + else + { + _physicsUids.Remove(entity.Uid); + } + } } private void GenerateMask() { - CollisionMask = 0x0; + BlockedCollisionMask = 0x0; - foreach (var layer in CollisionLayers) + foreach (var layer in _blockedCollidables.Values) { - CollisionMask |= layer; + BlockedCollisionMask |= layer; } } } diff --git a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingSystem.cs b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingSystem.cs index 07c4273664..62a8479306 100644 --- a/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/AI/Pathfinding/PathfindingSystem.cs @@ -1,12 +1,12 @@ using System; using System.Collections.Generic; using System.Threading; -using Content.Server.GameObjects.Components.Doors; -using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.GraphUpdates; +using Content.Server.GameObjects.Components.Access; using Content.Server.GameObjects.EntitySystems.AI.Pathfinding.Pathfinders; using Content.Server.GameObjects.EntitySystems.JobQueues; using Content.Server.GameObjects.EntitySystems.JobQueues.Queues; using Content.Server.GameObjects.EntitySystems.Pathfinding; +using Content.Shared.Physics; using Robust.Shared.GameObjects.Components; using Robust.Shared.GameObjects.Components.Transform; using Robust.Shared.GameObjects.Systems; @@ -14,6 +14,7 @@ using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; using Robust.Shared.IoC; using Robust.Shared.Map; +using Robust.Shared.Utility; namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding { @@ -29,18 +30,30 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding public class PathfindingSystem : EntitySystem { #pragma warning disable 649 + [Dependency] private readonly IEntityManager _entitymanager; [Dependency] private readonly IMapManager _mapManager; #pragma warning restore 649 public IReadOnlyDictionary> Graph => _graph; private readonly Dictionary> _graph = new Dictionary>(); - // Every tick we queue up all the changes and do them at once - private readonly Queue _queuedGraphUpdates = new Queue(); + private readonly PathfindingJobQueue _pathfindingQueue = new PathfindingJobQueue(); + + // Queued pathfinding graph updates + private readonly Queue _collidableUpdateQueue = new Queue(); + private readonly Queue _moveUpdateQueue = new Queue(); + private readonly Queue _accessReaderUpdateQueue = new Queue(); + private readonly Queue _tileUpdateQueue = new Queue(); // Need to store previously known entity positions for collidables for when they move private readonly Dictionary _lastKnownPositions = new Dictionary(); + public const int TrackedCollisionLayers = (int) + (CollisionGroup.Impassable | + CollisionGroup.MobImpassable | + CollisionGroup.SmallImpassable | + CollisionGroup.VaultImpassable); + /// /// Ask for the pathfinder to gimme somethin /// @@ -68,51 +81,66 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding private void ProcessGraphUpdates() { - for (var i = 0; i < Math.Min(50, _queuedGraphUpdates.Count); i++) + var totalUpdates = 0; + + foreach (var update in _collidableUpdateQueue) { - var update = _queuedGraphUpdates.Dequeue(); - switch (update) + var entity = _entitymanager.GetEntity(update.Owner); + if (update.CanCollide) { - case CollidableMove move: - HandleCollidableMove(move); - break; - case CollisionChange change: - if (change.Value) - { - HandleCollidableAdd(change.Owner); - } - else - { - HandleCollidableRemove(change.Owner); - } - - break; - case GridRemoval removal: - HandleGridRemoval(removal); - break; - case TileUpdate tile: - HandleTileUpdate(tile); - break; - default: - throw new ArgumentOutOfRangeException(); + HandleCollidableAdd(entity); + } + else + { + HandleAccessRemove(entity); } - } - } - private void HandleGridRemoval(GridRemoval removal) - { - if (!_graph.ContainsKey(removal.GridId)) + totalUpdates++; + } + + _collidableUpdateQueue.Clear(); + + foreach (var update in _accessReaderUpdateQueue) { - throw new InvalidOperationException(); + var entity = _entitymanager.GetEntity(update.Uid); + if (update.Enabled) + { + HandleAccessAdd(entity); + } + else + { + HandleAccessRemove(entity); + } + + totalUpdates++; + } + + _accessReaderUpdateQueue.Clear(); + + foreach (var tile in _tileUpdateQueue) + { + HandleTileUpdate(tile); + totalUpdates++; + } + + _tileUpdateQueue.Clear(); + var moveUpdateCount = Math.Max(50 - totalUpdates, 0); + + // Other updates are high priority so for this we'll just defer it if there's a spike (explosion, etc.) + // If the move updates grow too large then we'll just do it + if (_moveUpdateQueue.Count > 100) + { + moveUpdateCount = _moveUpdateQueue.Count - 100; } - _graph.Remove(removal.GridId); - } - - private void HandleTileUpdate(TileUpdate tile) - { - var chunk = GetChunk(tile.Tile); - chunk.UpdateNode(tile.Tile); + moveUpdateCount = Math.Min(moveUpdateCount, _moveUpdateQueue.Count); + + for (var i = 0; i < moveUpdateCount; i++) + { + HandleCollidableMove(_moveUpdateQueue.Dequeue()); + } + + DebugTools.Assert(_moveUpdateQueue.Count < 1000); } public PathfindingChunk GetChunk(TileRef tile) @@ -132,7 +160,6 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding } var newChunk = CreateChunk(tile.GridIndex, mapIndices); - return newChunk; } @@ -179,13 +206,13 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding public override void Initialize() { - IoCManager.InjectDependencies(this); SubscribeLocalEvent(QueueCollisionEnabledEvent); SubscribeLocalEvent(QueueCollidableMove); + SubscribeLocalEvent(QueueAccessChangeEvent); // Handle all the base grid changes // Anything that affects traversal (i.e. collision layer) is handled separately. - _mapManager.OnGridRemoved += QueueGridRemoval; + _mapManager.OnGridRemoved += HandleGridRemoval; _mapManager.GridChanged += QueueGridChange; _mapManager.TileChanged += QueueTileChange; } @@ -193,32 +220,85 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding public override void Shutdown() { base.Shutdown(); - _mapManager.OnGridRemoved -= QueueGridRemoval; + UnsubscribeLocalEvent(); + UnsubscribeLocalEvent(); + UnsubscribeLocalEvent(); + + _mapManager.OnGridRemoved -= HandleGridRemoval; _mapManager.GridChanged -= QueueGridChange; _mapManager.TileChanged -= QueueTileChange; } + + private void HandleTileUpdate(TileRef tile) + { + var node = GetNode(tile); + node.UpdateTile(tile); + } public void ResettingCleanup() { - _queuedGraphUpdates.Clear(); + _graph.Clear(); + _collidableUpdateQueue.Clear(); + _moveUpdateQueue.Clear(); + _accessReaderUpdateQueue.Clear(); + _tileUpdateQueue.Clear(); + _lastKnownPositions.Clear(); } - private void QueueGridRemoval(GridId gridId) + private void HandleGridRemoval(GridId gridId) { - _queuedGraphUpdates.Enqueue(new GridRemoval(gridId)); + if (_graph.ContainsKey(gridId)) + { + _graph.Remove(gridId); + } } private void QueueGridChange(object sender, GridChangedEventArgs eventArgs) { foreach (var (position, _) in eventArgs.Modified) { - _queuedGraphUpdates.Enqueue(new TileUpdate(eventArgs.Grid.GetTileRef(position))); + _tileUpdateQueue.Enqueue(eventArgs.Grid.GetTileRef(position)); } } private void QueueTileChange(object sender, TileChangedEventArgs eventArgs) { - _queuedGraphUpdates.Enqueue(new TileUpdate(eventArgs.NewTile)); + _tileUpdateQueue.Enqueue(eventArgs.NewTile); + } + + private void QueueAccessChangeEvent(AccessReaderChangeMessage message) + { + _accessReaderUpdateQueue.Enqueue(message); + } + + private void HandleAccessAdd(IEntity entity) + { + if (entity.Deleted || !entity.HasComponent()) + { + return; + } + + var grid = _mapManager.GetGrid(entity.Transform.GridID); + var tileRef = grid.GetTileRef(entity.Transform.GridPosition); + + var chunk = GetChunk(tileRef); + var node = chunk.GetNode(tileRef); + node.AddEntity(entity); + } + + private void HandleAccessRemove(IEntity entity) + { + if (entity.Deleted || !entity.HasComponent()) + { + return; + } + + var grid = _mapManager.GetGrid(entity.Transform.GridID); + var tileRef = grid.GetTileRef(entity.Transform.GridPosition); + + var chunk = GetChunk(tileRef); + var node = chunk.GetNode(tileRef); + node.RemoveEntity(entity); } #region collidable @@ -228,25 +308,22 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding /// private void HandleCollidableAdd(IEntity entity) { - // It's a grid / gone / a door / we already have it (which probably shouldn't happen) if (entity.Prototype == null || entity.Deleted || - entity.HasComponent() || - entity.HasComponent() || - _lastKnownPositions.ContainsKey(entity)) + _lastKnownPositions.ContainsKey(entity) || + !entity.TryGetComponent(out CollidableComponent collidableComponent) || + !collidableComponent.CanCollide || + (TrackedCollisionLayers & collidableComponent.CollisionLayer) == 0) { return; } var grid = _mapManager.GetGrid(entity.Transform.GridID); var tileRef = grid.GetTileRef(entity.Transform.GridPosition); - - var collisionLayer = entity.GetComponent().CollisionLayer; - var chunk = GetChunk(tileRef); var node = chunk.GetNode(tileRef); - node.AddCollisionLayer(collisionLayer); + node.AddEntity(entity); _lastKnownPositions.Add(entity, tileRef); } @@ -258,46 +335,37 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding { if (entity.Prototype == null || entity.Deleted || - entity.HasComponent() || - entity.HasComponent() || - !_lastKnownPositions.ContainsKey(entity)) + !_lastKnownPositions.ContainsKey(entity) || + !entity.TryGetComponent(out CollidableComponent collidableComponent) || + !collidableComponent.CanCollide || + (TrackedCollisionLayers & collidableComponent.CollisionLayer) == 0) { return; } - _lastKnownPositions.Remove(entity); - var grid = _mapManager.GetGrid(entity.Transform.GridID); var tileRef = grid.GetTileRef(entity.Transform.GridPosition); - - if (!entity.TryGetComponent(out CollidableComponent collidableComponent)) - { - return; - } - - var collisionLayer = collidableComponent.CollisionLayer; - var chunk = GetChunk(tileRef); var node = chunk.GetNode(tileRef); - node.RemoveCollisionLayer(collisionLayer); + + node.RemoveEntity(entity); + _lastKnownPositions.Remove(entity); } private void QueueCollidableMove(MoveEvent moveEvent) { - _queuedGraphUpdates.Enqueue(new CollidableMove(moveEvent)); + _moveUpdateQueue.Enqueue(moveEvent); } - private void HandleCollidableMove(CollidableMove move) + private void HandleCollidableMove(MoveEvent moveEvent) { - if (!_lastKnownPositions.ContainsKey(move.MoveEvent.Sender)) + if (!_lastKnownPositions.ContainsKey(moveEvent.Sender)) { return; } // The pathfinding graph is tile-based so first we'll check if they're on a different tile and if we need to update. // If you get entities bigger than 1 tile wide you'll need some other system so god help you. - var moveEvent = move.MoveEvent; - if (moveEvent.Sender.Deleted) { HandleCollidableRemove(moveEvent.Sender); @@ -314,14 +382,12 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding _lastKnownPositions[moveEvent.Sender] = newTile; - if (!moveEvent.Sender.TryGetComponent(out CollidableComponent collidableComponent)) + if (!moveEvent.Sender.HasComponent()) { HandleCollidableRemove(moveEvent.Sender); return; } - var collisionLayer = collidableComponent.CollisionLayer; - var gridIds = new HashSet(2) {oldTile.GridIndex, newTile.GridIndex}; foreach (var gridId in gridIds) @@ -330,33 +396,53 @@ namespace Content.Server.GameObjects.EntitySystems.AI.Pathfinding { var oldChunk = GetChunk(oldTile); var oldNode = oldChunk.GetNode(oldTile); - oldNode.RemoveCollisionLayer(collisionLayer); + oldNode.RemoveEntity(moveEvent.Sender); } if (newTile.GridIndex == gridId) { var newChunk = GetChunk(newTile); var newNode = newChunk.GetNode(newTile); - newNode.RemoveCollisionLayer(collisionLayer); + newNode.AddEntity(moveEvent.Sender); } } } private void QueueCollisionEnabledEvent(CollisionChangeEvent collisionEvent) { - // TODO: Handle containers - var entityManager = IoCManager.Resolve(); - var entity = entityManager.GetEntity(collisionEvent.Owner); - switch (collisionEvent.CanCollide) - { - case true: - _queuedGraphUpdates.Enqueue(new CollisionChange(entity, true)); - break; - case false: - _queuedGraphUpdates.Enqueue(new CollisionChange(entity, false)); - break; - } + _collidableUpdateQueue.Enqueue(collisionEvent); } #endregion + + // TODO: Need to rethink the pathfinder utils (traversable etc.). Maybe just chuck them all in PathfindingSystem + // Otherwise you get the steerer using this and the pathfinders using a different traversable. + // Also look at increasing tile cost the more physics entities are on it + public bool CanTraverse(IEntity entity, GridCoordinates grid) + { + var tile = _mapManager.GetGrid(grid.GridID).GetTileRef(grid); + var node = GetNode(tile); + return CanTraverse(entity, node); + } + + public bool CanTraverse(IEntity entity, PathfindingNode node) + { + if (entity.TryGetComponent(out CollidableComponent collidableComponent) && + (collidableComponent.CollisionMask & node.BlockedCollisionMask) != 0) + { + return false; + } + + var access = AccessReader.FindAccessTags(entity); + + foreach (var reader in node.AccessReaders) + { + if (!reader.IsAllowed(access)) + { + return false; + } + } + + return true; + } } } diff --git a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs index 66692a5543..c92df5fcc5 100644 --- a/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/Click/InteractionSystem.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Timing; @@ -955,22 +955,34 @@ namespace Content.Server.GameObjects.EntitySystems return; } - // Verify player has a hand, and find what object he is currently holding in his active hand - if (!player.TryGetComponent(out var hands)) - { - return; - } - - var item = hands.GetActiveHand?.Owner; - - // TODO: If item is null we need some kinda unarmed combat. - if (!ActionBlockerSystem.CanAttack(player) || item == null) + if (!ActionBlockerSystem.CanAttack(player)) { return; } var eventArgs = new AttackEventArgs(player, coordinates); - foreach (var attackComponent in item.GetAllComponents()) + + // Verify player has a hand, and find what object he is currently holding in his active hand + if (player.TryGetComponent(out var hands)) + { + var item = hands.GetActiveHand?.Owner; + + if (item != null) + { + var attacked = false; + foreach (var attackComponent in item.GetAllComponents()) + { + attackComponent.Attack(eventArgs); + attacked = true; + } + if (attacked) + { + return; + } + } + } + + foreach (var attackComponent in player.GetAllComponents()) { attackComponent.Attack(eventArgs); } diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs index 93581796ac..51b0136957 100644 --- a/Content.Server/GameTicking/GameTicker.cs +++ b/Content.Server/GameTicking/GameTicker.cs @@ -437,6 +437,8 @@ namespace Content.Server.GameTicking lobbyCountdownMessage.Paused = Paused; _netManager.ServerSendToAll(lobbyCountdownMessage); + _chatManager.DispatchServerAnnouncement($"Round start has been delayed for {time.TotalSeconds} seconds."); + return true; } @@ -463,6 +465,10 @@ namespace Content.Server.GameTicking lobbyCountdownMessage.Paused = Paused; _netManager.ServerSendToAll(lobbyCountdownMessage); + _chatManager.DispatchServerAnnouncement(Paused + ? "Round start has been paused." + : "Round start countdown is now resumed."); + return true; } @@ -838,6 +844,7 @@ namespace Content.Server.GameTicking msg.IsRoundStarted = RunLevel != GameRunLevel.PreRoundLobby; msg.StartTime = _roundStartTimeUtc; msg.YouAreReady = ready; + msg.Paused = Paused; return msg; } diff --git a/Content.Server/GlobalVerbs/RejuvenateVerb.cs b/Content.Server/GlobalVerbs/RejuvenateVerb.cs index cf141019b6..3c2c69da6f 100644 --- a/Content.Server/GlobalVerbs/RejuvenateVerb.cs +++ b/Content.Server/GlobalVerbs/RejuvenateVerb.cs @@ -1,4 +1,5 @@ using Content.Server.GameObjects; +using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.Components.Nutrition; using Content.Shared.GameObjects; using Robust.Server.Console; @@ -62,6 +63,10 @@ namespace Content.Server.GlobalVerbs { thirst.ResetThirst(); } + if (target.TryGetComponent(out StunnableComponent stun)) + { + stun.ResetStuns(); + } } } } diff --git a/Content.Shared/GameObjects/Components/Sound/SharedLoopingSoundComponent.cs b/Content.Shared/GameObjects/Components/Sound/SharedLoopingSoundComponent.cs index 18f7a9ff7e..b09d353b27 100644 --- a/Content.Shared/GameObjects/Components/Sound/SharedLoopingSoundComponent.cs +++ b/Content.Shared/GameObjects/Components/Sound/SharedLoopingSoundComponent.cs @@ -108,6 +108,9 @@ namespace Content.Shared.GameObjects.Components.Sound public void ExposeData(ObjectSerializer serializer) { + if (!serializer.Reading) + return; + Filename = serializer.ReadDataField("filename", ""); Delay = serializer.ReadDataField("delay", 0u); RandomDelay = serializer.ReadDataField("randomdelay", 0u); diff --git a/Content.Shared/GameObjects/Components/Weapons/Ranged/MagazineAutoEjectMessage.cs b/Content.Shared/GameObjects/Components/Weapons/Ranged/MagazineAutoEjectMessage.cs new file mode 100644 index 0000000000..fe8f2e183c --- /dev/null +++ b/Content.Shared/GameObjects/Components/Weapons/Ranged/MagazineAutoEjectMessage.cs @@ -0,0 +1,12 @@ +using System; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Weapons.Ranged +{ + /// + /// This is sent if the MagazineBarrel AutoEjects the magazine + /// + [Serializable, NetSerializable] + public sealed class MagazineAutoEjectMessage : ComponentMessage {} +} \ No newline at end of file diff --git a/Content.Shared/SharedGameTicker.cs b/Content.Shared/SharedGameTicker.cs index c4dd450c8b..2fea6dd16b 100644 --- a/Content.Shared/SharedGameTicker.cs +++ b/Content.Shared/SharedGameTicker.cs @@ -66,6 +66,7 @@ namespace Content.Shared public bool YouAreReady { get; set; } // UTC. public DateTime StartTime { get; set; } + public bool Paused { get; set; } public override void ReadFromBuffer(NetIncomingMessage buffer) { @@ -78,6 +79,7 @@ namespace Content.Shared YouAreReady = buffer.ReadBoolean(); StartTime = new DateTime(buffer.ReadInt64(), DateTimeKind.Utc); + Paused = buffer.ReadBoolean(); } public override void WriteToBuffer(NetOutgoingMessage buffer) @@ -91,6 +93,7 @@ namespace Content.Shared buffer.Write(YouAreReady); buffer.Write(StartTime.Ticks); + buffer.Write(Paused); } } diff --git a/Resources/Audio/Guns/Gunshots/rpgfire.ogg b/Resources/Audio/Guns/Gunshots/rpgfire.ogg new file mode 100644 index 0000000000..516618fe91 Binary files /dev/null and b/Resources/Audio/Guns/Gunshots/rpgfire.ogg differ diff --git a/Resources/Prototypes/Entities/Markers/gamemode_conditional_spawners.yml b/Resources/Prototypes/Entities/Markers/gamemode_conditional_spawners.yml index 9b5f6757b8..3339833383 100644 --- a/Resources/Prototypes/Entities/Markers/gamemode_conditional_spawners.yml +++ b/Resources/Prototypes/Entities/Markers/gamemode_conditional_spawners.yml @@ -38,10 +38,9 @@ - RifleBlackAk - RifleCarbine - RifleDallas - - RifleIhHeavy - - RifleSolEot - - RifleSolPara - - RifleSts + - RifleSTS + - RifleVintorez + - RifleWintermute chance: 0.75 gameRules: - RuleSuspicion @@ -62,14 +61,16 @@ - type: ConditionalSpawner prototypes: - PistolClarissa - - PistolDeagle - - PistolDeckard + - PistolColt - PistolGiskard - - PistolGyro + - PistolHMPistol - PistolLamia - - PistolMakarov + - PistolMandella - PistolMk58 - - PistolOlivawCivil + - PistolMk58Wood + - PistolMolly + - PistolOlivaw + - PistolPaco chance: 0.75 gameRules: - RuleSuspicion @@ -94,6 +95,171 @@ - Spear - ToolboxEmergency - CrowbarRed + - Stunbaton + chance: 0.75 + gameRules: + - RuleSuspicion + +- type: entity + name: Suspicion Revolver Spawner + id: SuspicionRevolverSpawner + parent: BaseConditionalSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_revolver + - type: Icon + sprite: Objects/markers.rsi + state: spawner_revolver + - type: ConditionalSpawner + prototypes: + - RevolverDeckard + - RevolverInspector + - RevolverMateba + chance: 0.75 + gameRules: + - RuleSuspicion + +- type: entity + name: Suspicion Shotgun Spawner + id: SuspicionShotgunSpawner + parent: BaseConditionalSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_shotgun + - type: Icon + sprite: Objects/markers.rsi + state: spawner_shotgun + - type: ConditionalSpawner + prototypes: + - ShotgunBojevic + - ShotgunDB + - ShotgunBull + - ShotgunGladstone + - ShotgunRegulator + - ShotgunPump + - ShotgunSawn + chance: 0.75 + gameRules: + - RuleSuspicion + +- type: entity + name: Suspicion SMG Spawner + id: SuspicionSMGSpawner + parent: BaseConditionalSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_smg + - type: Icon + sprite: Objects/markers.rsi + state: spawner_smg + - type: ConditionalSpawner + prototypes: + - SmgAtreides + - SmgC20r + - SmgDrozd + - SmgStraylight + - SmgWt550 + - SmgZoric + chance: 0.75 + gameRules: + - RuleSuspicion + +- type: entity + name: Suspicion Sniper Spawner + id: SuspicionSniperSpawner + parent: BaseConditionalSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_sniper + - type: Icon + sprite: Objects/markers.rsi + state: spawner_sniper + - type: ConditionalSpawner + prototypes: + - SniperBoltGun + - SniperBoltGunWood + - SniperHeavy + chance: 0.75 + gameRules: + - RuleSuspicion + +- type: entity + name: Suspicion Hitscan Spawner + id: SuspicionHitscanSpawner + parent: BaseConditionalSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_hitscan + - type: Icon + sprite: Objects/markers.rsi + state: spawner_hitscan + - type: ConditionalSpawner + prototypes: + - RedLaser + - RedHeavyLaser + - XrayLaser + - LaserGun + - LaserCannon + - XrayCannon + - TaserGun + chance: 0.75 + gameRules: + - RuleSuspicion + +- type: entity + name: Suspicion Launchers Spawner + id: SuspicionLaunchersSpawner + parent: BaseConditionalSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_launcher + - type: Icon + sprite: Objects/markers.rsi + state: spawner_launcher + - type: ConditionalSpawner + prototypes: + - LauncherChinaLake + - LauncherRocket + chance: 0.75 + gameRules: + - RuleSuspicion + +- type: entity + name: Suspicion Grenades Spawner + id: SuspicionGrenadesSpawner + parent: BaseConditionalSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_grenade + - type: Icon + sprite: Objects/markers.rsi + state: spawner_grenade + - type: ConditionalSpawner + prototypes: + - ExGrenade + - GrenadeFlashBang + - SyndieMiniBomb chance: 0.75 gameRules: - RuleSuspicion diff --git a/Resources/Prototypes/Entities/Markers/timed_spawners.yml b/Resources/Prototypes/Entities/Markers/timed_spawners.yml new file mode 100644 index 0000000000..4bc96baeed --- /dev/null +++ b/Resources/Prototypes/Entities/Markers/timed_spawners.yml @@ -0,0 +1,42 @@ +- type: entity + name: base timed spawner + id: BaseTimedSpawner + abstract: true + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: cross_blue + - type: Icon + sprite: Objects/markers.rsi + state: cross_blue + - type: Marker + - type: Clickable + - type: InteractionOutline + - type: Collidable + - type: TimedSpawner + placement: + mode: AlignTileAny + +- type: entity + name: AI Timed Spawner + id: AITimedSpawner + parent: BaseTimedSpawner + components: + - type: Sprite + netsync: false + visible: false + sprite: Objects/markers.rsi + state: spawner_rifle + - type: Icon + sprite: Objects/markers.rsi + state: spawner_rifle + - type: TimedSpawner + prototypes: + - HumanMob_Spirate + - HumanMob_Civilian + chance: 0.75 + intervalSeconds: 60 + minimumEntitiesSpawned: 1 + maximumEntitiesSpawned: 5 diff --git a/Resources/Prototypes/Entities/Mobs/human.yml b/Resources/Prototypes/Entities/Mobs/human.yml index 5bb8d68aaf..1527f3d9a9 100644 --- a/Resources/Prototypes/Entities/Mobs/human.yml +++ b/Resources/Prototypes/Entities/Mobs/human.yml @@ -134,12 +134,15 @@ - type: HumanoidAppearance - type: Stunnable - type: AnimationPlayer + - type: UnarmedCombat + range: 0.8 + arcwidth: 30 + arc: fist - type: entity save: false name: Urist McHands parent: BaseHumanMob_Content - abstract: true id: HumanMob_Content description: A miserable pile of secrets drawdepth: Mobs @@ -161,6 +164,7 @@ save: false name: Urist McHands id: HumanMob_Dummy + abstract: true description: A dummy human meant to be used in character setup components: - type: Hands diff --git a/Resources/Prototypes/Entities/Weapons/Launchers/launchers.yml b/Resources/Prototypes/Entities/Weapons/Launchers/launchers.yml index 19fa48c80a..a7544c1923 100644 --- a/Resources/Prototypes/Entities/Weapons/Launchers/launchers.yml +++ b/Resources/Prototypes/Entities/Weapons/Launchers/launchers.yml @@ -73,7 +73,7 @@ fireRate: 0.5 capacity: 1 soundEmpty: /Audio/Guns/Empty/empty.ogg - soundGunshot: /Audio/Guns/Gunshots/bang.ogg + soundGunshot: /Audio/Guns/Gunshots/rpgfire.ogg soundInsert: /Audio/Guns/MagIn/batrifle_magin.ogg - type: Appearance visuals: diff --git a/Resources/Prototypes/Entities/Weapons/Projectiles/projectiles.yml b/Resources/Prototypes/Entities/Weapons/Projectiles/projectiles.yml index 5fff01fdec..0e89d4e0ad 100644 --- a/Resources/Prototypes/Entities/Weapons/Projectiles/projectiles.yml +++ b/Resources/Prototypes/Entities/Weapons/Projectiles/projectiles.yml @@ -18,7 +18,7 @@ hard: false shapes: - !type:PhysShapeAabb - bounds: "-0.2,-0.2,0.2,0.2" + bounds: "-0.1,-0.1,0.1,0.1" layer: [Clickable] mask: - Impassable @@ -130,10 +130,14 @@ - type: Projectile deleteOnCollide: false - type: Explosive - devastationRange: 3 - heavyImpactRange: 5 - lightImpactRange: 7 + devastationRange: 1 + heavyImpactRange: 2 + lightImpactRange: 4 flashRange: 10 + - type: PointLight + radius: 3.5 + color: orange + energy: 0.5 - type: entity id: BulletGrenadeBaton diff --git a/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/mag-1.png b/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/mag-1.png new file mode 100644 index 0000000000..7ef2a79205 Binary files /dev/null and b/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/mag-1.png differ diff --git a/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/meta.json b/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/meta.json index cbc9bce33c..69eaa07a35 100644 --- a/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/meta.json +++ b/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/meta.json @@ -19,6 +19,14 @@ "name": "mag-0", "directions": 1 }, + { + "name": "rocket0-inhand-left", + "directions": 4 + }, + { + "name": "rocket0-inhand-right", + "directions": 4 + }, { "name": "inhand-left", "directions": 4 @@ -28,4 +36,4 @@ "directions": 4 } ] -} \ No newline at end of file +} diff --git a/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/rocket0-inhand-left.png b/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/rocket0-inhand-left.png new file mode 100644 index 0000000000..aa36fdfe14 Binary files /dev/null and b/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/rocket0-inhand-left.png differ diff --git a/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/rocket0-inhand-right.png b/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/rocket0-inhand-right.png new file mode 100644 index 0000000000..c988a13565 Binary files /dev/null and b/Resources/Textures/Objects/Guns/Launchers/rocket.rsi/rocket0-inhand-right.png differ diff --git a/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/frag.png b/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/frag.png index 504580ce49..93251cdc2f 100644 Binary files a/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/frag.png and b/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/frag.png differ diff --git a/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/meta.json b/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/meta.json index 310a348520..cb29fe71ba 100644 --- a/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/meta.json +++ b/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/meta.json @@ -10,6 +10,10 @@ { "name": "frag", "directions": 1 + }, + { + "name": "smallfrag", + "directions": 1 } ] -} \ No newline at end of file +} diff --git a/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/smallfrag.png b/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/smallfrag.png new file mode 100644 index 0000000000..504580ce49 Binary files /dev/null and b/Resources/Textures/Objects/Guns/Projectiles/rocket.rsi/smallfrag.png differ diff --git a/Resources/Textures/Objects/markers.rsi/AI.png b/Resources/Textures/Objects/markers.rsi/AI.png index 1e6533c229..aa331c468b 100644 Binary files a/Resources/Textures/Objects/markers.rsi/AI.png and b/Resources/Textures/Objects/markers.rsi/AI.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Assistant.png b/Resources/Textures/Objects/markers.rsi/Assistant.png index 7588e08c7b..139cba34f5 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Assistant.png and b/Resources/Textures/Objects/markers.rsi/Assistant.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Atmospheric Technician.png b/Resources/Textures/Objects/markers.rsi/Atmospheric Technician.png index 25e454b11d..b25133c96d 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Atmospheric Technician.png and b/Resources/Textures/Objects/markers.rsi/Atmospheric Technician.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Bartender.png b/Resources/Textures/Objects/markers.rsi/Bartender.png index acf2e044e0..d0f4baaf2b 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Bartender.png and b/Resources/Textures/Objects/markers.rsi/Bartender.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Botanist.png b/Resources/Textures/Objects/markers.rsi/Botanist.png index 51eb2b02b3..8e81377214 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Botanist.png and b/Resources/Textures/Objects/markers.rsi/Botanist.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Captain.png b/Resources/Textures/Objects/markers.rsi/Captain.png index 6d832c820e..f32f53e462 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Captain.png and b/Resources/Textures/Objects/markers.rsi/Captain.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Cargo Technician.png b/Resources/Textures/Objects/markers.rsi/Cargo Technician.png index 89cdc3bded..e162a24154 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Cargo Technician.png and b/Resources/Textures/Objects/markers.rsi/Cargo Technician.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Chaplain.png b/Resources/Textures/Objects/markers.rsi/Chaplain.png index 84223f7402..c293074cd4 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Chaplain.png and b/Resources/Textures/Objects/markers.rsi/Chaplain.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Chemist.png b/Resources/Textures/Objects/markers.rsi/Chemist.png index 2e10a6cac9..d983b91c58 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Chemist.png and b/Resources/Textures/Objects/markers.rsi/Chemist.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Chief Engineer.png b/Resources/Textures/Objects/markers.rsi/Chief Engineer.png index aad16c9ba3..ec40156f1f 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Chief Engineer.png and b/Resources/Textures/Objects/markers.rsi/Chief Engineer.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Chief Medical Officer.png b/Resources/Textures/Objects/markers.rsi/Chief Medical Officer.png index 8dc1bcf25e..4411c7fa3c 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Chief Medical Officer.png and b/Resources/Textures/Objects/markers.rsi/Chief Medical Officer.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Clown.png b/Resources/Textures/Objects/markers.rsi/Clown.png index 6282d74de2..994a291f68 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Clown.png and b/Resources/Textures/Objects/markers.rsi/Clown.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Cook.png b/Resources/Textures/Objects/markers.rsi/Cook.png index cc778e79ab..f8eae67a88 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Cook.png and b/Resources/Textures/Objects/markers.rsi/Cook.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Curator.png b/Resources/Textures/Objects/markers.rsi/Curator.png index cd7201ce4d..d9310a0bf3 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Curator.png and b/Resources/Textures/Objects/markers.rsi/Curator.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Cyborg.png b/Resources/Textures/Objects/markers.rsi/Cyborg.png index f406e6b663..8811385665 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Cyborg.png and b/Resources/Textures/Objects/markers.rsi/Cyborg.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Detective.png b/Resources/Textures/Objects/markers.rsi/Detective.png index b91db06e1a..73d944a0a7 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Detective.png and b/Resources/Textures/Objects/markers.rsi/Detective.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Geneticist.png b/Resources/Textures/Objects/markers.rsi/Geneticist.png index aaa3f2c5c6..9f6e4e9ffe 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Geneticist.png and b/Resources/Textures/Objects/markers.rsi/Geneticist.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Head of Personnel.png b/Resources/Textures/Objects/markers.rsi/Head of Personnel.png index 148e5ebbe5..5c9b2f05c1 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Head of Personnel.png and b/Resources/Textures/Objects/markers.rsi/Head of Personnel.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Head of Security.png b/Resources/Textures/Objects/markers.rsi/Head of Security.png index 48fe1dec81..e13caa290a 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Head of Security.png and b/Resources/Textures/Objects/markers.rsi/Head of Security.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Janitor.png b/Resources/Textures/Objects/markers.rsi/Janitor.png index 6872e9340d..7be84691d1 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Janitor.png and b/Resources/Textures/Objects/markers.rsi/Janitor.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Lawyer.png b/Resources/Textures/Objects/markers.rsi/Lawyer.png index b3c3dec78c..403b6dd982 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Lawyer.png and b/Resources/Textures/Objects/markers.rsi/Lawyer.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Medical Doctor.png b/Resources/Textures/Objects/markers.rsi/Medical Doctor.png index f8fea38838..3b2fad9f3f 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Medical Doctor.png and b/Resources/Textures/Objects/markers.rsi/Medical Doctor.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Mime.png b/Resources/Textures/Objects/markers.rsi/Mime.png index 24419d8855..a051ff5117 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Mime.png and b/Resources/Textures/Objects/markers.rsi/Mime.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Paramedic.png b/Resources/Textures/Objects/markers.rsi/Paramedic.png index 786f1b5b93..6e228574e6 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Paramedic.png and b/Resources/Textures/Objects/markers.rsi/Paramedic.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Prisoner.png b/Resources/Textures/Objects/markers.rsi/Prisoner.png index 352fae0668..35383af8cb 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Prisoner.png and b/Resources/Textures/Objects/markers.rsi/Prisoner.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Psychologist.png b/Resources/Textures/Objects/markers.rsi/Psychologist.png index 89d4496621..3c6339791b 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Psychologist.png and b/Resources/Textures/Objects/markers.rsi/Psychologist.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Quartermaster.png b/Resources/Textures/Objects/markers.rsi/Quartermaster.png index 538d063eac..ac2aafa5cd 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Quartermaster.png and b/Resources/Textures/Objects/markers.rsi/Quartermaster.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Research Director.png b/Resources/Textures/Objects/markers.rsi/Research Director.png index ab8775e3cc..bfcd188b59 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Research Director.png and b/Resources/Textures/Objects/markers.rsi/Research Director.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Roboticist.png b/Resources/Textures/Objects/markers.rsi/Roboticist.png index bb9f014e9d..05384b4b76 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Roboticist.png and b/Resources/Textures/Objects/markers.rsi/Roboticist.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Scientist.png b/Resources/Textures/Objects/markers.rsi/Scientist.png index 8cd403488b..ac4849ba69 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Scientist.png and b/Resources/Textures/Objects/markers.rsi/Scientist.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Security Officer.png b/Resources/Textures/Objects/markers.rsi/Security Officer.png index ad1e418991..8d5dcdc9e3 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Security Officer.png and b/Resources/Textures/Objects/markers.rsi/Security Officer.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Shaft Miner.png b/Resources/Textures/Objects/markers.rsi/Shaft Miner.png index 7c647980af..3d9c41b498 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Shaft Miner.png and b/Resources/Textures/Objects/markers.rsi/Shaft Miner.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Station Engineer.png b/Resources/Textures/Objects/markers.rsi/Station Engineer.png index ee306c811d..d561f3de46 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Station Engineer.png and b/Resources/Textures/Objects/markers.rsi/Station Engineer.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Virologist.png b/Resources/Textures/Objects/markers.rsi/Virologist.png index e0e611cbf1..002550aaec 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Virologist.png and b/Resources/Textures/Objects/markers.rsi/Virologist.png differ diff --git a/Resources/Textures/Objects/markers.rsi/Warden.png b/Resources/Textures/Objects/markers.rsi/Warden.png index 6eeace59ab..6d577a752e 100644 Binary files a/Resources/Textures/Objects/markers.rsi/Warden.png and b/Resources/Textures/Objects/markers.rsi/Warden.png differ diff --git a/Resources/Textures/Objects/markers.rsi/cross_green.png b/Resources/Textures/Objects/markers.rsi/cross_green.png index b5366b2bdf..0becfdb0c5 100644 Binary files a/Resources/Textures/Objects/markers.rsi/cross_green.png and b/Resources/Textures/Objects/markers.rsi/cross_green.png differ diff --git a/Resources/Textures/Objects/markers.rsi/cross_pink.png b/Resources/Textures/Objects/markers.rsi/cross_pink.png index e9fa9fc5d3..d5476f99e6 100644 Binary files a/Resources/Textures/Objects/markers.rsi/cross_pink.png and b/Resources/Textures/Objects/markers.rsi/cross_pink.png differ diff --git a/Resources/Textures/Objects/markers.rsi/cross_red.png b/Resources/Textures/Objects/markers.rsi/cross_red.png index 4ac7f06b20..063f341d36 100644 Binary files a/Resources/Textures/Objects/markers.rsi/cross_red.png and b/Resources/Textures/Objects/markers.rsi/cross_red.png differ diff --git a/Resources/Textures/Objects/markers.rsi/meta.json b/Resources/Textures/Objects/markers.rsi/meta.json index 02df455363..7eb1bca74a 100644 --- a/Resources/Textures/Objects/markers.rsi/meta.json +++ b/Resources/Textures/Objects/markers.rsi/meta.json @@ -1 +1,461 @@ -{"version": 1, "size": {"x": 32, "y": 32}, "license": "CC-BY-SA-3.0", "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi", "states": [{"name": "cross_blue", "directions": 1, "delays": [[1.0]]}, {"name": "cross_green", "directions": 1, "delays": [[1.0]]}, {"name": "cross_red", "directions": 1, "delays": [[1.0]]}, {"name": "AI", "directions": 1, "delays": [[1.0]]}, {"name": "Assistant", "directions": 1, "delays": [[1.0]]}, {"name": "Atmospheric Technician", "directions": 1, "delays": [[1.0]]}, {"name": "Bartender", "directions": 1, "delays": [[1.0]]}, {"name": "Botanist", "directions": 1, "delays": [[1.0]]}, {"name": "Captain", "directions": 1, "delays": [[1.0]]}, {"name": "Cargo Technician", "directions": 1, "delays": [[1.0]]}, {"name": "Chaplain", "directions": 1, "delays": [[1.0]]}, {"name": "Chemist", "directions": 1, "delays": [[1.0]]}, {"name": "Chief Engineer", "directions": 1, "delays": [[1.0]]}, {"name": "Chief Medical Officer", "directions": 1, "delays": [[1.0]]}, {"name": "Clown", "directions": 1, "delays": [[1.0]]}, {"name": "Cook", "directions": 1, "delays": [[1.0]]}, {"name": "Curator", "directions": 1, "delays": [[1.0]]}, {"name": "Cyborg", "directions": 1, "delays": [[1.0]]}, {"name": "Detective", "directions": 1, "delays": [[1.0]]}, {"name": "Geneticist", "directions": 1, "delays": [[1.0]]}, {"name": "Head of Personnel", "directions": 1, "delays": [[1.0]]}, {"name": "Head of Security", "directions": 1, "delays": [[1.0]]}, {"name": "Janitor", "directions": 1, "delays": [[1.0]]}, {"name": "Lawyer", "directions": 1, "delays": [[1.0]]}, {"name": "Medical Doctor", "directions": 1, "delays": [[1.0]]}, {"name": "Mime", "directions": 1, "delays": [[1.0]]}, {"name": "Paramedic", "directions": 1, "delays": [[1.0]]}, {"name": "Prisoner", "directions": 1, "delays": [[1.0]]}, {"name": "Psychologist", "directions": 1, "delays": [[1.0]]}, {"name": "Quartermaster", "directions": 1, "delays": [[1.0]]}, {"name": "Research Director", "directions": 1, "delays": [[1.0]]}, {"name": "Roboticist", "directions": 1, "delays": [[1.0]]}, {"name": "Scientist", "directions": 1, "delays": [[1.0]]}, {"name": "Security Officer", "directions": 1, "delays": [[1.0]]}, {"name": "Shaft Miner", "directions": 1, "delays": [[1.0]]}, {"name": "Station Engineer", "directions": 1, "delays": [[1.0]]}, {"name": "Virologist", "directions": 1, "delays": [[1.0]]}, {"name": "Warden", "directions": 1, "delays": [[1.0]]}, {"name": "cross_pink", "directions": 1, "delays": [[1.0]]}, {"name": "observer_start", "directions": 1, "delays": [[1.0]]}, {"name": "spawner_melee", "directions": 1, "delays": [[1.0]]}, {"name": "spawner_rifle", "directions": 1, "delays": [[1.0]]}, {"name": "spawner_pistol", "directions": 1, "delays": [[1.0]]}]} +{ + "version": 1, + "size": { + "x": 32, + "y": 32 + }, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/vgstation-coders/vgstation13/blob/e71d6c4fba5a51f99b81c295dcaec4fc2f58fb19/icons/mob/screen1.dmi", + "states": [ + { + "name": "AI", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Assistant", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Atmospheric Technician", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Bartender", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Botanist", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Captain", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Cargo Technician", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Chaplain", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Chemist", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Chief Engineer", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Chief Medical Officer", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Clown", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Cook", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Curator", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Cyborg", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Detective", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Geneticist", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Head of Personnel", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Head of Security", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Janitor", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Lawyer", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Medical Doctor", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Mime", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Paramedic", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Prisoner", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Psychologist", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Quartermaster", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Research Director", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Roboticist", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Scientist", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Security Officer", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Shaft Miner", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Station Engineer", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Virologist", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "Warden", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "cross_blue", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "cross_green", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "cross_pink", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "cross_red", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "observer_start", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "spawner_grenade", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "spawner_hitscan", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "spawner_launcher", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "spawner_melee", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "spawner_pistol", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "spawner_revolver", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "spawner_rifle", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "spawner_shotgun", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "spawner_smg", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + }, + { + "name": "spawner_sniper", + "directions": 1, + "delays": [ + [ + 1.0 + ] + ] + } + ] +} \ No newline at end of file diff --git a/Resources/Textures/Objects/markers.rsi/observer_start.png b/Resources/Textures/Objects/markers.rsi/observer_start.png index 8f965c9751..309baacb03 100644 Binary files a/Resources/Textures/Objects/markers.rsi/observer_start.png and b/Resources/Textures/Objects/markers.rsi/observer_start.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_grenade.png b/Resources/Textures/Objects/markers.rsi/spawner_grenade.png new file mode 100644 index 0000000000..bac248ef76 Binary files /dev/null and b/Resources/Textures/Objects/markers.rsi/spawner_grenade.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_hitscan.png b/Resources/Textures/Objects/markers.rsi/spawner_hitscan.png new file mode 100644 index 0000000000..5d6dbe39ec Binary files /dev/null and b/Resources/Textures/Objects/markers.rsi/spawner_hitscan.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_launcher.png b/Resources/Textures/Objects/markers.rsi/spawner_launcher.png new file mode 100644 index 0000000000..63f0e1b0cc Binary files /dev/null and b/Resources/Textures/Objects/markers.rsi/spawner_launcher.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_melee.png b/Resources/Textures/Objects/markers.rsi/spawner_melee.png index bc3fd59227..7a1ea775d6 100644 Binary files a/Resources/Textures/Objects/markers.rsi/spawner_melee.png and b/Resources/Textures/Objects/markers.rsi/spawner_melee.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_pistol.png b/Resources/Textures/Objects/markers.rsi/spawner_pistol.png index 6f9b871e53..745347acba 100644 Binary files a/Resources/Textures/Objects/markers.rsi/spawner_pistol.png and b/Resources/Textures/Objects/markers.rsi/spawner_pistol.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_revolver.png b/Resources/Textures/Objects/markers.rsi/spawner_revolver.png new file mode 100644 index 0000000000..0e8add9464 Binary files /dev/null and b/Resources/Textures/Objects/markers.rsi/spawner_revolver.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_rifle.png b/Resources/Textures/Objects/markers.rsi/spawner_rifle.png index 0b1c339595..2dbb558c05 100644 Binary files a/Resources/Textures/Objects/markers.rsi/spawner_rifle.png and b/Resources/Textures/Objects/markers.rsi/spawner_rifle.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_shotgun.png b/Resources/Textures/Objects/markers.rsi/spawner_shotgun.png new file mode 100644 index 0000000000..86092fefa5 Binary files /dev/null and b/Resources/Textures/Objects/markers.rsi/spawner_shotgun.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_smg.png b/Resources/Textures/Objects/markers.rsi/spawner_smg.png new file mode 100644 index 0000000000..b0fb68c028 Binary files /dev/null and b/Resources/Textures/Objects/markers.rsi/spawner_smg.png differ diff --git a/Resources/Textures/Objects/markers.rsi/spawner_sniper.png b/Resources/Textures/Objects/markers.rsi/spawner_sniper.png new file mode 100644 index 0000000000..b1eef469be Binary files /dev/null and b/Resources/Textures/Objects/markers.rsi/spawner_sniper.png differ diff --git a/RobustToolbox b/RobustToolbox index 7dd0a668d5..4650dc2c49 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 7dd0a668d50c462456634bebced035be63225594 +Subproject commit 4650dc2c49888593c63b4a3871d99845fb7acc7a diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 5e9ee1ab64..39b7cb2137 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -57,9 +57,9 @@ True True True + True True True True True - True - True + \ No newline at end of file diff --git a/Tools/package_release_build.py b/Tools/package_release_build.py index 8400021f61..fae399325a 100755 --- a/Tools/package_release_build.py +++ b/Tools/package_release_build.py @@ -75,12 +75,13 @@ WINDOWS_NATIVES = { LINUX_NATIVES = { "libglfw.so.3", - "libswnfd.so" + "libswnfd.so", } MAC_NATIVES = { "libglfw.3.dylib", - "libswnfd.dylib" + "libswnfd.dylib", + "libfreetype.6.dylib" } SERVER_EXTRA_CONTENT_ASSEMBLIES = [