diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index cedb75f726..9695eae1f5 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,7 +2,7 @@ name: Publish on: push: - branches: [ master ] + branches: [ stable ] jobs: build: diff --git a/Content.Client/Chat/UI/ChatBox.xaml.cs b/Content.Client/Chat/UI/ChatBox.xaml.cs index 74d3f8eb11..0199d0666a 100644 --- a/Content.Client/Chat/UI/ChatBox.xaml.cs +++ b/Content.Client/Chat/UI/ChatBox.xaml.cs @@ -395,7 +395,13 @@ namespace Content.Client.Chat.UI private void WriteChatMessage(StoredChatMessage message) { - Logger.DebugS("chat", $"{message.Channel}: {message.Message}"); + var messageText = FormattedMessage.EscapeText(message.Message); + if (!string.IsNullOrEmpty(message.MessageWrap)) + { + messageText = string.Format(message.MessageWrap, messageText); + } + + Logger.DebugS("chat", $"{message.Channel}: {messageText}"); if (IsFilteredOut(message.Channel)) return; @@ -403,12 +409,6 @@ namespace Content.Client.Chat.UI // TODO: Can make this "smarter" later by only setting it false when the message has been scrolled to message.Read = true; - var messageText = FormattedMessage.EscapeText(message.Message); - if (!string.IsNullOrEmpty(message.MessageWrap)) - { - messageText = string.Format(message.MessageWrap, messageText); - } - var color = message.MessageColorOverride != Color.Transparent ? message.MessageColorOverride : ChatHelper.ChatColor(message.Channel); diff --git a/Content.Client/Crayon/CrayonDecalVisualizer.cs b/Content.Client/Crayon/CrayonDecalVisualizer.cs index cfeb50bf90..e69de29bb2 100644 --- a/Content.Client/Crayon/CrayonDecalVisualizer.cs +++ b/Content.Client/Crayon/CrayonDecalVisualizer.cs @@ -1,30 +0,0 @@ -using Content.Shared.Crayon; -using JetBrains.Annotations; -using Robust.Client.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Maths; - -namespace Content.Client.Crayon -{ - [UsedImplicitly] - public class CrayonDecalVisualizer : AppearanceVisualizer - { - public override void OnChangeData(AppearanceComponent component) - { - base.OnChangeData(component); - - var sprite = IoCManager.Resolve().GetComponent(component.Owner); - - if (component.TryGetData(CrayonVisuals.State, out string state)) - { - sprite.LayerSetState(0, state); - } - - if (component.TryGetData(CrayonVisuals.Color, out string color)) - { - sprite.LayerSetColor(0, Color.FromName(color)); - } - } - } -} diff --git a/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs b/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs index e6fcf8273c..37439e21f8 100644 --- a/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs +++ b/Content.Client/Crayon/UI/CrayonBoundUserInterface.cs @@ -1,5 +1,6 @@ using System.Linq; using Content.Shared.Crayon; +using Content.Shared.Decals; using Robust.Client.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -22,9 +23,8 @@ namespace Content.Client.Crayon.UI _menu.OnClose += Close; var prototypeManager = IoCManager.Resolve(); - var crayonDecals = prototypeManager.EnumeratePrototypes().FirstOrDefault(); - if (crayonDecals != null) - _menu.Populate(crayonDecals); + var crayonDecals = prototypeManager.EnumeratePrototypes().Where(x => x.Tags.Contains("crayon")); + _menu.Populate(crayonDecals); _menu.OpenCentered(); } diff --git a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs index 7e676e473b..90ce4b2523 100644 --- a/Content.Client/Crayon/UI/CrayonWindow.xaml.cs +++ b/Content.Client/Crayon/UI/CrayonWindow.xaml.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Content.Client.Stylesheets; using Content.Shared.Crayon; +using Content.Shared.Decals; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; @@ -89,14 +90,12 @@ namespace Content.Client.Crayon.UI RefreshList(); } - public void Populate(CrayonDecalPrototype proto) + public void Populate(IEnumerable prototypes) { - var path = new ResourcePath(proto.SpritePath); _decals = new Dictionary(); - foreach (var state in proto.Decals) + foreach (var decalPrototype in prototypes) { - var rsi = new SpriteSpecifier.Rsi(path, state); - _decals.Add(state, rsi.Frame0()); + _decals.Add(decalPrototype.ID, decalPrototype.Sprite.Frame0()); } RefreshList(); diff --git a/Content.Client/Decals/DecalOverlay.cs b/Content.Client/Decals/DecalOverlay.cs new file mode 100644 index 0000000000..5a8169b957 --- /dev/null +++ b/Content.Client/Decals/DecalOverlay.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using Content.Shared.Decals; +using Robust.Client.Graphics; +using Robust.Client.Utility; +using Robust.Shared.Enums; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Prototypes; +using Robust.Shared.Utility; +using Robust.Shared.Maths; + +namespace Content.Client.Decals +{ + public class DecalOverlay : Overlay + { + private readonly DecalSystem _system; + private readonly IMapManager _mapManager; + private readonly IPrototypeManager _prototypeManager; + + public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities; + + public DecalOverlay(DecalSystem system, IMapManager mapManager, IPrototypeManager prototypeManager) + { + _system = system; + _mapManager = mapManager; + _prototypeManager = prototypeManager; + } + + protected override void Draw(in OverlayDrawArgs args) + { + var handle = args.WorldHandle; + + Dictionary cachedTextures = new(); + + SpriteSpecifier GetSpriteSpecifier(string id) + { + if (cachedTextures.TryGetValue(id, out var spriteSpecifier)) + return spriteSpecifier; + + spriteSpecifier = _prototypeManager.Index(id).Sprite; + cachedTextures.Add(id, spriteSpecifier); + return spriteSpecifier; + } + + foreach (var (gridId, zIndexDictionary) in _system.DecalRenderIndex) + { + var grid = _mapManager.GetGrid(gridId); + handle.SetTransform(grid.WorldMatrix); + foreach (var (_, decals) in zIndexDictionary) + { + foreach (var (_, decal) in decals) + { + var spriteSpecifier = GetSpriteSpecifier(decal.Id); + handle.DrawTexture(spriteSpecifier.Frame0(), decal.Coordinates, decal.Angle, decal.Color); + } + } + } + } + } +} diff --git a/Content.Client/Decals/DecalSystem.cs b/Content.Client/Decals/DecalSystem.cs new file mode 100644 index 0000000000..4f16c4ba5c --- /dev/null +++ b/Content.Client/Decals/DecalSystem.cs @@ -0,0 +1,114 @@ +using System.Collections.Generic; +using Content.Shared.Decals; +using Robust.Client.Graphics; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; + +namespace Content.Client.Decals +{ + public class DecalSystem : SharedDecalSystem + { + [Dependency] private readonly IOverlayManager _overlayManager = default!; + + private DecalOverlay _overlay = default!; + public Dictionary>> DecalRenderIndex = new(); + private Dictionary> DecalZIndexIndex = new(); + + public override void Initialize() + { + base.Initialize(); + + _overlay = new DecalOverlay(this, MapManager, PrototypeManager); + _overlayManager.AddOverlay(_overlay); + + SubscribeNetworkEvent(OnChunkUpdate); + SubscribeLocalEvent(OnGridInitialize); + SubscribeLocalEvent(OnGridRemoval); + } + + public void ToggleOverlay() + { + if (_overlayManager.HasOverlay()) + { + _overlayManager.RemoveOverlay(_overlay); + } + else + { + _overlayManager.AddOverlay(_overlay); + } + } + + private void OnGridRemoval(GridRemovalEvent ev) + { + DecalRenderIndex.Remove(ev.GridId); + DecalZIndexIndex.Remove(ev.GridId); + } + + private void OnGridInitialize(GridInitializeEvent ev) + { + DecalRenderIndex[ev.GridId] = new(); + DecalZIndexIndex[ev.GridId] = new(); + } + + public override void Shutdown() + { + base.Shutdown(); + _overlayManager.RemoveOverlay(_overlay); + } + + protected override bool RemoveDecalHook(GridId gridId, uint uid) + { + RemoveDecalFromRenderIndex(gridId, uid); + + return base.RemoveDecalHook(gridId, uid); + } + + private void RemoveDecalFromRenderIndex(GridId gridId, uint uid) + { + var zIndex = DecalZIndexIndex[gridId][uid]; + + DecalRenderIndex[gridId][zIndex].Remove(uid); + if (DecalRenderIndex[gridId][zIndex].Count == 0) + DecalRenderIndex[gridId].Remove(zIndex); + + DecalZIndexIndex[gridId].Remove(uid); + } + + private void OnChunkUpdate(DecalChunkUpdateEvent ev) + { + foreach (var (gridId, gridChunks) in ev.Data) + { + foreach (var (indices, newChunkData) in gridChunks) + { + var chunkCollection = ChunkCollection(gridId); + if (chunkCollection.TryGetValue(indices, out var chunk)) + { + var removedUids = new HashSet(chunk.Keys); + removedUids.ExceptWith(newChunkData.Keys); + foreach (var removedUid in removedUids) + { + RemoveDecalFromRenderIndex(gridId, removedUid); + } + } + foreach (var (uid, decal) in newChunkData) + { + if(!DecalRenderIndex[gridId].ContainsKey(decal.ZIndex)) + DecalRenderIndex[gridId][decal.ZIndex] = new(); + + if (DecalZIndexIndex.TryGetValue(gridId, out var values) && values.TryGetValue(uid, out var zIndex)) + { + DecalRenderIndex[gridId][zIndex].Remove(uid); + } + + DecalRenderIndex[gridId][decal.ZIndex][uid] = decal; + DecalZIndexIndex[gridId][uid] = decal.ZIndex; + + ChunkIndex[gridId][uid] = indices; + } + chunkCollection[indices] = newChunkData; + } + } + } + } +} diff --git a/Content.Client/Decals/ToggleDecalCommand.cs b/Content.Client/Decals/ToggleDecalCommand.cs new file mode 100644 index 0000000000..37930701f7 --- /dev/null +++ b/Content.Client/Decals/ToggleDecalCommand.cs @@ -0,0 +1,15 @@ +using Robust.Shared.Console; +using Robust.Shared.GameObjects; + +namespace Content.Client.Decals; + +public class ToggleDecalCommand : IConsoleCommand +{ + public string Command => "toggledecals"; + public string Description => "Toggles decaloverlay"; + public string Help => $"{Command}"; + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + EntitySystem.Get().ToggleOverlay(); + } +} diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 48d30e0eea..e5ba6ea452 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -8,6 +8,7 @@ namespace Content.Client.Entry "Anchorable", "AmmoBox", "Pickaxe", + "IngestionBlocker", "Interactable", "CloningPod", "Destructible", diff --git a/Content.Client/Tabletop/TabletopSystem.cs b/Content.Client/Tabletop/TabletopSystem.cs index a71d83e224..bf400df408 100644 --- a/Content.Client/Tabletop/TabletopSystem.cs +++ b/Content.Client/Tabletop/TabletopSystem.cs @@ -18,6 +18,7 @@ using Robust.Shared.IoC; using Robust.Shared.Log; using Robust.Shared.Map; using Robust.Shared.Maths; +using Robust.Shared.Timing; using static Robust.Shared.Input.Binding.PointerInputCmdHandler; using DrawDepth = Content.Shared.DrawDepth.DrawDepth; @@ -29,6 +30,7 @@ namespace Content.Client.Tabletop [Dependency] private readonly IInputManager _inputManager = default!; [Dependency] private readonly IUserInterfaceManager _uiManger = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; // Time in seconds to wait until sending the location of a dragged entity to the server again private const float Delay = 1f / 10; // 10 Hz @@ -51,6 +53,10 @@ namespace Content.Client.Tabletop public override void Update(float frameTime) { + // don't send network messages when doing prediction. + if (!_gameTiming.IsFirstTimePredicted) + return; + // If there is no player entity, return if (_playerManager.LocalPlayer is not {ControlledEntity: { } playerEntity}) return; diff --git a/Content.Client/Weapons/Ranged/Barrels/Components/ClientBatteryBarrelComponent.cs b/Content.Client/Weapons/Ranged/Barrels/Components/ClientBatteryBarrelComponent.cs index f84355b22b..4ef5813498 100644 --- a/Content.Client/Weapons/Ranged/Barrels/Components/ClientBatteryBarrelComponent.cs +++ b/Content.Client/Weapons/Ranged/Barrels/Components/ClientBatteryBarrelComponent.cs @@ -1,6 +1,7 @@ using System; using Content.Client.Items.Components; using Content.Client.Stylesheets; +using Content.Shared.Containers.ItemSlots; using Content.Shared.Weapons.Ranged.Barrels.Components; using Robust.Client.Graphics; using Robust.Client.UserInterface; @@ -8,6 +9,7 @@ using Robust.Client.UserInterface.Controls; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.Maths; +using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; using static Robust.Client.UserInterface.Controls.BoxContainer; @@ -21,6 +23,9 @@ namespace Content.Client.Weapons.Ranged.Barrels.Components private StatusControl? _statusControl; + [DataField("cellSlot", required: true)] + public ItemSlot CellSlot = default!; + /// /// Count of bullets in the magazine. /// @@ -30,6 +35,18 @@ namespace Content.Client.Weapons.Ranged.Barrels.Components [ViewVariables] public (int count, int max)? MagazineCount { get; private set; } + protected override void Initialize() + { + base.Initialize(); + EntitySystem.Get().AddItemSlot(Owner, $"{Name}-powercell-container", CellSlot); + } + + protected override void OnRemove() + { + base.OnRemove(); + EntitySystem.Get().RemoveItemSlot(Owner, CellSlot); + } + public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) { base.HandleComponentState(curState, nextState); diff --git a/Content.IntegrationTests/Tests/EntityTest.cs b/Content.IntegrationTests/Tests/EntityTest.cs index 6a4b69e38f..f4f005a830 100644 --- a/Content.IntegrationTests/Tests/EntityTest.cs +++ b/Content.IntegrationTests/Tests/EntityTest.cs @@ -23,7 +23,7 @@ namespace Content.IntegrationTests.Tests { var options = new ServerContentIntegrationOption() { - CVarOverrides = {{CCVars.AIMaxUpdates.Name, int.MaxValue.ToString()}} + CVarOverrides = {{CCVars.NPCMaxUpdates.Name, int.MaxValue.ToString()}} }; var server = StartServer(options); diff --git a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs index c58d26ab0a..91e644c344 100644 --- a/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs +++ b/Content.IntegrationTests/Tests/Fluids/PuddleTest.cs @@ -1,6 +1,7 @@ using System; using System.Threading.Tasks; using Content.Server.Fluids.Components; +using Content.Server.Fluids.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Coordinates; using Content.Shared.FixedPoint; @@ -24,6 +25,8 @@ namespace Content.IntegrationTests.Tests.Fluids await server.WaitIdleAsync(); var mapManager = server.ResolveDependency(); + var entitySystemManager = server.ResolveDependency(); + var spillSystem = entitySystemManager.GetEntitySystem(); server.Assert(() => { @@ -31,7 +34,7 @@ namespace Content.IntegrationTests.Tests.Fluids var grid = GetMainGrid(mapManager); var (x, y) = GetMainTile(grid).GridIndices; var coordinates = new EntityCoordinates(grid.GridEntityId, x, y); - var puddle = solution.SpillAt(coordinates, "PuddleSmear"); + var puddle = spillSystem.SpillAt(solution, coordinates, "PuddleSmear"); Assert.NotNull(puddle); }); @@ -47,6 +50,8 @@ namespace Content.IntegrationTests.Tests.Fluids await server.WaitIdleAsync(); var mapManager = server.ResolveDependency(); + var entitySystemManager = server.ResolveDependency(); + var spillSystem = entitySystemManager.GetEntitySystem(); IMapGrid grid = null; @@ -67,7 +72,7 @@ namespace Content.IntegrationTests.Tests.Fluids { var coordinates = grid.ToCoordinates(); var solution = new Solution("Water", FixedPoint2.New(20)); - var puddle = solution.SpillAt(coordinates, "PuddleSmear"); + var puddle = spillSystem.SpillAt(solution, coordinates, "PuddleSmear"); Assert.Null(puddle); }); @@ -120,13 +125,17 @@ namespace Content.IntegrationTests.Tests.Fluids float evaporateTime = default; PuddleComponent puddle = null; EvaporationComponent evaporation; + var amount = 2; + + var entitySystemManager = server.ResolveDependency(); + var spillSystem = entitySystemManager.GetEntitySystem(); // Spawn a puddle await server.WaitAssertion(() => { var solution = new Solution("Water", FixedPoint2.New(amount)); - puddle = solution.SpillAt(sCoordinates, "PuddleSmear"); + puddle = spillSystem.SpillAt(solution, sCoordinates, "PuddleSmear"); // Check that the puddle was created Assert.NotNull(puddle); @@ -184,4 +193,4 @@ namespace Content.IntegrationTests.Tests.Fluids }); } } -} +} \ No newline at end of file diff --git a/Content.IntegrationTests/Tests/InventoryHelpersTest.cs b/Content.IntegrationTests/Tests/InventoryHelpersTest.cs index 33f8447522..bc7985020b 100644 --- a/Content.IntegrationTests/Tests/InventoryHelpersTest.cs +++ b/Content.IntegrationTests/Tests/InventoryHelpersTest.cs @@ -78,7 +78,7 @@ namespace Content.IntegrationTests.Tests ID: "InventoryJumpsuitJanitorDummy" }); - EntitySystem.Get().TryStun(human, TimeSpan.FromSeconds(1f)); + EntitySystem.Get().TryStun(human, TimeSpan.FromSeconds(1f), true); // Since the mob is stunned, they can't equip this. Assert.That(inventory.SpawnItemInSlot(Slots.IDCARD, "InventoryIDCardDummy", true), Is.False); diff --git a/Content.Server/AI/Components/AiControllerComponent.cs b/Content.Server/AI/Components/AiControllerComponent.cs index ca4c4b7af8..0cf2cf33cf 100644 --- a/Content.Server/AI/Components/AiControllerComponent.cs +++ b/Content.Server/AI/Components/AiControllerComponent.cs @@ -1,4 +1,5 @@ -using Content.Server.GameTicking; +using Content.Server.AI.EntitySystems; +using Content.Server.GameTicking; using Content.Shared.Movement.Components; using Content.Shared.Roles; using Robust.Shared.GameObjects; @@ -19,6 +20,29 @@ namespace Content.Server.AI.Components public override string Name => "AiController"; + // TODO: Need to ECS a lot more of the AI first before we can ECS this + /// + /// Whether the AI is actively iterated. + /// + public bool Awake + { + get => _awake; + set + { + if (_awake == value) return; + + _awake = value; + + if (_awake) + EntitySystem.Get().WakeNPC(this); + else + EntitySystem.Get().SleepNPC(this); + } + } + + [DataField("awake")] + private bool _awake = true; + [ViewVariables(VVAccess.ReadWrite)] [DataField("startingGear")] public string? StartingGearPrototype { get; set; } diff --git a/Content.Server/AI/EntitySystems/AiSystem.cs b/Content.Server/AI/EntitySystems/AiSystem.cs deleted file mode 100644 index 3e682e47ef..0000000000 --- a/Content.Server/AI/EntitySystems/AiSystem.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Collections.Generic; -using Content.Server.AI.Components; -using Content.Server.AI.Utility.AiLogic; -using Content.Shared; -using Content.Shared.CCVar; -using Content.Shared.MobState; -using JetBrains.Annotations; -using Robust.Shared.Configuration; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Log; - -namespace Content.Server.AI.EntitySystems -{ - /// - /// Handles NPCs running every tick. - /// - [UsedImplicitly] - internal class AiSystem : EntitySystem - { - [Dependency] private readonly IConfigurationManager _configurationManager = default!; - - /// - /// To avoid iterating over dead AI continuously they can wake and sleep themselves when necessary. - /// - private readonly HashSet _awakeAi = new(); - - // To avoid modifying awakeAi while iterating over it. - private readonly List _queuedSleepMessages = new(); - - private readonly List _queuedMobStateMessages = new(); - - public bool IsAwake(AiControllerComponent npc) => _awakeAi.Contains(npc); - - /// - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(HandleAiSleep); - SubscribeLocalEvent(MobStateChanged); - } - - /// - public override void Update(float frameTime) - { - var cvarMaxUpdates = _configurationManager.GetCVar(CCVars.AIMaxUpdates); - if (cvarMaxUpdates <= 0) - return; - - foreach (var message in _queuedMobStateMessages) - { - // TODO: Need to generecise this but that will be part of a larger cleanup later anyway. - if ((!IoCManager.Resolve().EntityExists(message.Entity) ? EntityLifeStage.Deleted : IoCManager.Resolve().GetComponent(message.Entity).EntityLifeStage) >= EntityLifeStage.Deleted || - !IoCManager.Resolve().TryGetComponent(message.Entity, out UtilityAi? controller)) - { - continue; - } - - controller.MobStateChanged(message); - } - - _queuedMobStateMessages.Clear(); - - foreach (var message in _queuedSleepMessages) - { - switch (message.Sleep) - { - case true: - if (_awakeAi.Count == cvarMaxUpdates && _awakeAi.Contains(message.Component)) - { - Logger.Warning($"Under AI limit again: {_awakeAi.Count - 1} / {cvarMaxUpdates}"); - } - _awakeAi.Remove(message.Component); - break; - case false: - _awakeAi.Add(message.Component); - - if (_awakeAi.Count > cvarMaxUpdates) - { - Logger.Warning($"AI limit exceeded: {_awakeAi.Count} / {cvarMaxUpdates}"); - } - break; - } - } - - _queuedSleepMessages.Clear(); - var toRemove = new List(); - var maxUpdates = Math.Min(_awakeAi.Count, cvarMaxUpdates); - var count = 0; - - foreach (var npc in _awakeAi) - { - if (npc.Deleted) - { - toRemove.Add(npc); - continue; - } - - if (npc.Paused) - continue; - - if (count >= maxUpdates) - { - break; - } - - npc.Update(frameTime); - count++; - } - - foreach (var processor in toRemove) - { - _awakeAi.Remove(processor); - } - } - - private void HandleAiSleep(SleepAiMessage message) - { - _queuedSleepMessages.Add(message); - } - - private void MobStateChanged(MobStateChangedMessage message) - { - if (!IoCManager.Resolve().HasComponent(message.Entity)) - { - return; - } - - _queuedMobStateMessages.Add(message); - } - } -} diff --git a/Content.Server/AI/EntitySystems/NPCSystem.cs b/Content.Server/AI/EntitySystems/NPCSystem.cs new file mode 100644 index 0000000000..a3e5b518fb --- /dev/null +++ b/Content.Server/AI/EntitySystems/NPCSystem.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; +using System.Linq; +using Content.Server.AI.Components; +using Content.Server.MobState.States; +using Content.Shared.CCVar; +using Content.Shared.MobState; +using JetBrains.Annotations; +using Robust.Shared.Configuration; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Random; + +namespace Content.Server.AI.EntitySystems +{ + /// + /// Handles NPCs running every tick. + /// + [UsedImplicitly] + internal class NPCSystem : EntitySystem + { + [Dependency] private readonly IConfigurationManager _configurationManager = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + + /// + /// To avoid iterating over dead AI continuously they can wake and sleep themselves when necessary. + /// + private readonly HashSet _awakeNPCs = new(); + + /// + /// Whether any NPCs are allowed to run at all. + /// + public bool Enabled { get; set; } = true; + + /// + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMobStateChange); + SubscribeLocalEvent(OnNPCInit); + SubscribeLocalEvent(OnNPCShutdown); + _configurationManager.OnValueChanged(CCVars.NPCEnabled, SetEnabled, true); + + var maxUpdates = _configurationManager.GetCVar(CCVars.NPCMaxUpdates); + + if (maxUpdates < 1024) + _awakeNPCs.EnsureCapacity(maxUpdates); + } + + private void SetEnabled(bool value) => Enabled = value; + + public override void Shutdown() + { + base.Shutdown(); + _configurationManager.UnsubValueChanged(CCVars.NPCEnabled, SetEnabled); + } + + private void OnNPCInit(EntityUid uid, AiControllerComponent component, ComponentInit args) + { + if (!component.Awake) return; + + _awakeNPCs.Add(component); + } + + private void OnNPCShutdown(EntityUid uid, AiControllerComponent component, ComponentShutdown args) + { + _awakeNPCs.Remove(component); + } + + /// + /// Allows the NPC to actively be updated. + /// + /// + public void WakeNPC(AiControllerComponent component) + { + _awakeNPCs.Add(component); + } + + /// + /// Stops the NPC from actively being updated. + /// + /// + public void SleepNPC(AiControllerComponent component) + { + _awakeNPCs.Remove(component); + } + + /// + public override void Update(float frameTime) + { + if (!Enabled) return; + + var cvarMaxUpdates = _configurationManager.GetCVar(CCVars.NPCMaxUpdates); + + if (cvarMaxUpdates <= 0) return; + + var npcs = _awakeNPCs.ToArray(); + var startIndex = 0; + + // If we're overcap we'll just update randomly so they all still at least do something + // Didn't randomise the array (even though it'd probably be better) because god damn that'd be expensive. + if (npcs.Length > cvarMaxUpdates) + { + startIndex = _robustRandom.Next(npcs.Length); + } + + for (var i = 0; i < npcs.Length; i++) + { + var index = (i + startIndex) % npcs.Length; + var npc = npcs[index]; + + if (npc.Deleted) + continue; + + if (npc.Paused) + continue; + + npc.Update(frameTime); + } + } + + private void OnMobStateChange(EntityUid uid, AiControllerComponent component, MobStateChangedEvent args) + { + switch (args.CurrentMobState) + { + case NormalMobState: + component.Awake = true; + break; + case CriticalMobState: + case DeadMobState: + component.Awake = false; + break; + } + } + } +} diff --git a/Content.Server/AI/SleepAiMessage.cs b/Content.Server/AI/SleepAiMessage.cs deleted file mode 100644 index 8e5f81b6b9..0000000000 --- a/Content.Server/AI/SleepAiMessage.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Content.Server.AI.Components; -using Robust.Shared.GameObjects; - -namespace Content.Server.AI -{ - /// - /// Indicates whether an AI should be updated by the AiSystem or not. - /// Useful to sleep AI when they die or otherwise should be inactive. - /// - internal sealed class SleepAiMessage : EntityEventArgs - { - /// - /// Sleep or awake. - /// - public bool Sleep { get; } - public AiControllerComponent Component { get; } - - public SleepAiMessage(AiControllerComponent component, bool sleep) - { - Component = component; - Sleep = sleep; - } - } -} diff --git a/Content.Server/AI/Utility/AiLogic/UtilityAI.cs b/Content.Server/AI/Utility/AiLogic/UtilityAI.cs index cff3df39b2..565c402294 100644 --- a/Content.Server/AI/Utility/AiLogic/UtilityAI.cs +++ b/Content.Server/AI/Utility/AiLogic/UtilityAI.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Runtime.ExceptionServices; using System.Threading; using Content.Server.AI.Components; +using Content.Server.AI.EntitySystems; using Content.Server.AI.LoadBalancer; using Content.Server.AI.Operators; using Content.Server.AI.Utility.Actions; @@ -59,33 +60,13 @@ namespace Content.Server.AI.Utility.AiLogic private CancellationTokenSource? _actionCancellation; - /// - /// If we can't do anything then stop thinking; should probably use ActionBlocker instead - /// - private bool _isDead; - - /*public void AfterDeserialization() - { - if (BehaviorSets.Count > 0) - { - var behaviorManager = IoCManager.Resolve(); - - foreach (var bSet in BehaviorSets) - { - behaviorManager.AddBehaviorSet(this, bSet, false); - } - - behaviorManager.RebuildActions(this); - } - }*/ - protected override void Initialize() { if (BehaviorSets.Count > 0) { var behaviorManager = IoCManager.Resolve(); behaviorManager.RebuildActions(this); - IoCManager.Resolve().EventBus.RaiseEvent(EventSource.Local, new SleepAiMessage(this, false)); + EntitySystem.Get().WakeNPC(this); } base.Initialize(); @@ -103,27 +84,6 @@ namespace Content.Server.AI.Utility.AiLogic CurrentAction = null; } - public void MobStateChanged(MobStateChangedMessage message) - { - var oldDeadState = _isDead; - _isDead = message.Component.IsIncapacitated(); - - if (oldDeadState != _isDead) - { - var entityManager = IoCManager.Resolve(); - - switch (_isDead) - { - case true: - entityManager.EventBus.RaiseEvent(EventSource.Local, new SleepAiMessage(this, true)); - break; - case false: - entityManager.EventBus.RaiseEvent(EventSource.Local, new SleepAiMessage(this, false)); - break; - } - } - } - private void ReceivedAction() { if (_actionRequest == null) diff --git a/Content.Server/AI/Utility/NpcBehaviorManager.cs b/Content.Server/AI/Utility/NpcBehaviorManager.cs index 8610bc4ed2..f1760ef3e2 100644 --- a/Content.Server/AI/Utility/NpcBehaviorManager.cs +++ b/Content.Server/AI/Utility/NpcBehaviorManager.cs @@ -84,9 +84,9 @@ namespace Content.Server.AI.Utility if (rebuild) RebuildActions(npc); - if (npc.BehaviorSets.Count == 1 && !EntitySystem.Get().IsAwake(npc)) + if (npc.BehaviorSets.Count == 1 && !npc.Awake) { - _entityManager.EventBus.RaiseEvent(EventSource.Local, new SleepAiMessage(npc, false)); + EntitySystem.Get().WakeNPC(npc); } } @@ -113,9 +113,9 @@ namespace Content.Server.AI.Utility if (rebuild) RebuildActions(npc); - if (npc.BehaviorSets.Count == 0 && EntitySystem.Get().IsAwake(npc)) + if (npc.BehaviorSets.Count == 0 && npc.Awake) { - _entityManager.EventBus.RaiseEvent(EventSource.Local, new SleepAiMessage(npc, true)); + EntitySystem.Get().SleepNPC(npc); } } diff --git a/Content.Server/Access/Components/AccessReaderComponent.cs b/Content.Server/Access/Components/AccessReaderComponent.cs index e71e71460e..dbcfa39999 100644 --- a/Content.Server/Access/Components/AccessReaderComponent.cs +++ b/Content.Server/Access/Components/AccessReaderComponent.cs @@ -25,6 +25,13 @@ namespace Content.Server.Access.Components { public override string Name => "AccessReader"; + /// + /// Whether this reader is enabled or not. If disabled, all access + /// checks will pass. + /// + [ViewVariables(VVAccess.ReadWrite)] + public bool Enabled = true; + /// /// The set of tags that will automatically deny an allowed check, if any of them are present. /// diff --git a/Content.Server/Access/Systems/AccessReaderSystem.cs b/Content.Server/Access/Systems/AccessReaderSystem.cs index 0a14852427..25c81350e5 100644 --- a/Content.Server/Access/Systems/AccessReaderSystem.cs +++ b/Content.Server/Access/Systems/AccessReaderSystem.cs @@ -60,7 +60,7 @@ namespace Content.Server.Access.Systems return false; } - return reader.AccessLists.Count == 0 || reader.AccessLists.Any(a => a.IsSubsetOf(accessTags)); + return !reader.Enabled || reader.AccessLists.Count == 0 || reader.AccessLists.Any(a => a.IsSubsetOf(accessTags)); } public ICollection FindAccessTags(EntityUid uid) diff --git a/Content.Server/Administration/AdminVerbSystem.cs b/Content.Server/Administration/AdminVerbSystem.cs index e22bf17073..9844983ca5 100644 --- a/Content.Server/Administration/AdminVerbSystem.cs +++ b/Content.Server/Administration/AdminVerbSystem.cs @@ -125,7 +125,7 @@ namespace Content.Server.Administration verb.Act = () => { var coords = EntityManager.GetComponent(args.Target).Coordinates; - Timer.Spawn(_gameTiming.TickPeriod, () => _explosions.SpawnExplosion(coords, 0, 1, 2, 1), CancellationToken.None); + Timer.Spawn(_gameTiming.TickPeriod, () => _explosions.SpawnExplosion(coords, 0, 1, 2, 1, args.Target), CancellationToken.None); if (EntityManager.TryGetComponent(args.Target, out SharedBodyComponent? body)) { body.Gib(); diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index 5fb9800773..c564504669 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -179,7 +179,7 @@ namespace Content.Server.Atmos.EntitySystems flammable.Resisting = true; flammable.Owner.PopupMessage(Loc.GetString("flammable-component-resist-message")); - _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(2f), alerts: alerts); + _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(2f), true, alerts: alerts); // TODO FLAMMABLE: Make this not use TimerComponent... flammable.Owner.SpawnTimer(2000, () => diff --git a/Content.Server/Atmos/Reactions/WaterVaporReaction.cs b/Content.Server/Atmos/Reactions/WaterVaporReaction.cs index 6d489ad584..ba30ef4ec8 100644 --- a/Content.Server/Atmos/Reactions/WaterVaporReaction.cs +++ b/Content.Server/Atmos/Reactions/WaterVaporReaction.cs @@ -1,10 +1,12 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Fluids.Components; +using Content.Server.Fluids.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using Content.Shared.Maps; using JetBrains.Annotations; +using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Server.Atmos.Reactions @@ -35,8 +37,9 @@ namespace Content.Server.Atmos.Reactions // Remove the moles from the mixture... mixture.AdjustMoles(GasId, -MolesPerUnit); - var tileRef = tile.GridIndices.GetTileRef(tile.GridIndex); - tileRef.SpillAt(new Solution(Reagent, FixedPoint2.New(MolesPerUnit)), PuddlePrototype, sound: false); + var tileRef = tile.GridIndices.GetTileRef(tile.GridIndex); + EntitySystem.Get() + .SpillAt(tileRef, new Solution(Reagent, FixedPoint2.New(MolesPerUnit)), PuddlePrototype, sound: false); return ReactionResult.Reacting; } diff --git a/Content.Server/Body/Systems/LungSystem.cs b/Content.Server/Body/Systems/LungSystem.cs index 7c5561090e..89db6d04c8 100644 --- a/Content.Server/Body/Systems/LungSystem.cs +++ b/Content.Server/Body/Systems/LungSystem.cs @@ -50,7 +50,7 @@ public class LungSystem : EntitySystem Inhale(uid, lung.CycleDelay); } - public void UpdateLung(EntityUid uid, float frameTime, + public void UpdateLung(EntityUid uid, LungComponent? lung=null, SharedMechanismComponent? mech=null) { @@ -69,8 +69,8 @@ public class LungSystem : EntitySystem lung.AccumulatedFrametime += lung.Status switch { - LungStatus.Inhaling => frameTime, - LungStatus.Exhaling => -frameTime, + LungStatus.Inhaling => 1, + LungStatus.Exhaling => -1, _ => throw new ArgumentOutOfRangeException() }; diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index 5beb6eaf60..d3c29e80ec 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -32,41 +32,40 @@ namespace Content.Server.Body.Systems foreach (var (respirator, blood, body) in EntityManager.EntityQuery()) { - var uid = (respirator).Owner; + var uid = respirator.Owner; if (!EntityManager.TryGetComponent(uid, out var state) || state.IsDead()) { - return; + continue; } respirator.AccumulatedFrametime += frameTime; if (respirator.AccumulatedFrametime < 1) { - return; + continue; } - ProcessGases(uid, respirator, frameTime, blood, body); + ProcessGases(uid, respirator, blood, body); respirator.AccumulatedFrametime -= 1; if (SuffocatingPercentage(respirator) > 0) { TakeSuffocationDamage(uid, respirator); - return; + continue; } StopSuffocation(uid, respirator); } } - - private Dictionary NeedsAndDeficit(RespiratorComponent respirator, float frameTime) + private Dictionary NeedsAndDeficit(RespiratorComponent respirator) { var needs = new Dictionary(respirator.NeedsGases); foreach (var (gas, amount) in respirator.DeficitGases) { - var newAmount = (needs.GetValueOrDefault(gas) + amount) * frameTime; + var newAmount = (needs.GetValueOrDefault(gas) + amount); needs[gas] = newAmount; } @@ -130,7 +129,7 @@ namespace Content.Server.Body.Systems return respirator.ProducesGases.ToDictionary(pair => pair.Key, pair => GasProducedMultiplier(respirator, pair.Key, usedAverage)); } - private void ProcessGases(EntityUid uid, RespiratorComponent respirator, float frameTime, + private void ProcessGases(EntityUid uid, RespiratorComponent respirator, BloodstreamComponent? bloodstream, SharedBodyComponent? body) { @@ -139,12 +138,12 @@ namespace Content.Server.Body.Systems var lungs = _bodySystem.GetComponentsOnMechanisms(uid, body).ToArray(); - var needs = NeedsAndDeficit(respirator, frameTime); + var needs = NeedsAndDeficit(respirator); var used = 0f; foreach (var (lung, mech) in lungs) { - _lungSystem.UpdateLung((lung).Owner, frameTime, lung, mech); + _lungSystem.UpdateLung(lung.Owner, lung, mech); } foreach (var (gas, amountNeeded) in needs) diff --git a/Content.Server/Chemistry/Components/InjectorComponent.cs b/Content.Server/Chemistry/Components/InjectorComponent.cs index dd16ca6d97..292721c0dd 100644 --- a/Content.Server/Chemistry/Components/InjectorComponent.cs +++ b/Content.Server/Chemistry/Components/InjectorComponent.cs @@ -1,18 +1,28 @@ using System; +using System.Threading; using System.Threading.Tasks; +using Content.Server.Administration.Logs; using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Chemistry.EntitySystems; +using Content.Server.CombatMode; +using Content.Server.DoAfter; +using Content.Shared.ActionBlocker; +using Content.Shared.Body.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; +using Content.Shared.Database; using Content.Shared.FixedPoint; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; +using Content.Shared.MobState.Components; using Content.Shared.Popups; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; +using Robust.Shared.Player; +using Robust.Shared.Players; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; @@ -42,16 +52,26 @@ namespace Content.Server.Chemistry.Components /// Amount to inject or draw on each usage. If the injector is inject only, it will /// attempt to inject it's entire contents upon use. /// - [ViewVariables] + [ViewVariables(VVAccess.ReadWrite)] [DataField("transferAmount")] private FixedPoint2 _transferAmount = FixedPoint2.New(5); /// - /// Initial storage volume of the injector + /// Injection delay (seconds) when the target is a mob. /// - [ViewVariables] - [DataField("initialMaxVolume")] - private FixedPoint2 _initialMaxVolume = FixedPoint2.New(15); + /// + /// The base delay has a minimum of 1 second, but this will still be modified if the target is incapacitated or + /// in combat mode. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("delay")] + public float Delay = 5; + + /// + /// Token for interrupting a do-after action (e.g., injection another player). If not null, implies + /// component is currently "in use". + /// + public CancellationTokenSource? CancelToken; private InjectorToggleMode _toggleState; @@ -112,9 +132,18 @@ namespace Content.Server.Chemistry.Components /// async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { + if (CancelToken != null) + { + CancelToken.Cancel(); + return true; + } + if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true)) return false; + if (!EntitySystem.Get().CanInteract(eventArgs.User)) + return false; + var solutionsSys = EntitySystem.Get(); //Make sure we have the attacking entity if (eventArgs.Target is not {Valid: true} target || @@ -123,6 +152,14 @@ namespace Content.Server.Chemistry.Components return false; } + // Is the target a mob? If yes, use a do-after to give them time to respond. + if (_entities.HasComponent(target) || + _entities.HasComponent(target)) + { + if (!await TryInjectDoAfter(eventArgs.User, target)) + return true; + } + // Handle injecting/drawing for solutions if (ToggleState == InjectorToggleMode.Inject) { @@ -162,6 +199,75 @@ namespace Content.Server.Chemistry.Components return true; } + /// + /// Send informative pop-up messages and wait for a do-after to complete. + /// + public async Task TryInjectDoAfter(EntityUid user, EntityUid target) + { + var popupSys = EntitySystem.Get(); + + // Create a pop-up for the user + popupSys.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, Filter.Entities(user)); + + // Get entity for logging. Log with EntityUids when? + var logSys = EntitySystem.Get(); + + var actualDelay = MathF.Max(Delay, 1f); + if (user != target) + { + // Create a pop-up for the target + var userName = _entities.GetComponent(user).EntityName; + popupSys.PopupEntity(Loc.GetString("injector-component-injecting-target", + ("user", userName)), user, Filter.Entities(target)); + + // Check if the target is incapacitated or in combat mode and modify time accordingly. + if (_entities.TryGetComponent(target, out var mobState) && + mobState.IsIncapacitated()) + { + actualDelay /= 2; + } + else if (_entities.TryGetComponent(target, out var combat) && + combat.IsInCombatMode) + { + // Slightly increase the delay when the target is in combat mode. Helps prevents cheese injections in + // combat with fast syringes & lag. + actualDelay += 1; + } + + // Add an admin log, using the "force feed" log type. It's not quite feeding, but the effect is the same. + if (ToggleState == InjectorToggleMode.Inject) + { + logSys.Add(LogType.ForceFeed, + $"{_entities.ToPrettyString(user)} is attempting to inject a solution into {_entities.ToPrettyString(target)}"); + // TODO solution pretty string. + } + } + else + { + // Self-injections take half as long. + actualDelay /= 2; + + if (ToggleState == InjectorToggleMode.Inject) + logSys.Add(LogType.Ingestion, + $"{_entities.ToPrettyString(user)} is attempting to inject themselves with a solution."); + //TODO solution pretty string. + } + + CancelToken = new(); + var status = await EntitySystem.Get().WaitDoAfter( + new DoAfterEventArgs(user, actualDelay, CancelToken.Token, target) + { + BreakOnUserMove = true, + BreakOnDamage = true, + BreakOnStun = true, + BreakOnTargetMove = true, + MovementThreshold = 1.0f + }); + CancelToken = null; + + return status == DoAfterStatus.Finished; + } + /// /// Called when use key is pressed when held in active hand /// diff --git a/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs b/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs index e4affc4678..19bc66986e 100644 --- a/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/InjectorSystem.cs @@ -1,6 +1,8 @@ -using Content.Server.Chemistry.Components; +using Content.Server.Chemistry.Components; +using Content.Shared.Hands; using JetBrains.Annotations; using Robust.Shared.GameObjects; +using System; namespace Content.Server.Chemistry.EntitySystems { @@ -12,6 +14,16 @@ namespace Content.Server.Chemistry.EntitySystems base.Initialize(); SubscribeLocalEvent(OnSolutionChange); + SubscribeLocalEvent(OnInjectorDeselected); + } + + private void OnInjectorDeselected(EntityUid uid, InjectorComponent component, HandDeselectedEvent args) + { + if (component.CancelToken != null) + { + component.CancelToken.Cancel(); + component.CancelToken = null; + } } private void OnSolutionChange(EntityUid uid, InjectorComponent component, SolutionChangedEvent args) diff --git a/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.Capabilities.cs b/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.Capabilities.cs index ed4dca054e..74c594637f 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.Capabilities.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionContainerSystem.Capabilities.cs @@ -1,5 +1,7 @@ using System.Diagnostics.CodeAnalysis; +using System.Text; using Content.Server.Chemistry.Components.SolutionManager; +using Content.Server.Database; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; @@ -128,5 +130,27 @@ namespace Content.Server.Chemistry.EntitySystems return true; } + + public static string ToPrettyString(Solution solution) + { + var sb = new StringBuilder(); + sb.Append("Solution content: ["); + var first = true; + foreach (var (id, quantity) in solution.Contents) + { + if (first) + { + first = false; + } + else + { + sb.Append(", "); + } + sb.AppendFormat("{0}: {1}u", id, quantity); + + } + sb.Append(']'); + return sb.ToString(); + } } } diff --git a/Content.Server/Chemistry/ReagentEffects/Electrocute.cs b/Content.Server/Chemistry/ReagentEffects/Electrocute.cs index 049b594cb5..95574d36ca 100644 --- a/Content.Server/Chemistry/ReagentEffects/Electrocute.cs +++ b/Content.Server/Chemistry/ReagentEffects/Electrocute.cs @@ -15,12 +15,17 @@ public class Electrocute : ReagentEffect [DataField("electrocuteDamageScale")] public int ElectrocuteDamageScale = 5; + /// + /// true - refresh electrocute time, false - accumulate electrocute time + /// + [DataField("refresh")] public bool Refresh = true; + public override bool ShouldLog => true; public override void Effect(ReagentEffectArgs args) { EntitySystem.Get().TryDoElectrocution(args.SolutionEntity, null, - Math.Max((args.Quantity * ElectrocuteDamageScale).Int(), 1), TimeSpan.FromSeconds(ElectrocuteTime)); + Math.Max((args.Quantity * ElectrocuteDamageScale).Int(), 1), TimeSpan.FromSeconds(ElectrocuteTime), Refresh); args.Source?.RemoveReagent(args.Reagent.ID, args.Quantity); } diff --git a/Content.Server/Chemistry/ReagentEffects/StatusEffects/GenericStatusEffect.cs b/Content.Server/Chemistry/ReagentEffects/StatusEffects/GenericStatusEffect.cs index b0c7f5711d..6c3dfe9014 100644 --- a/Content.Server/Chemistry/ReagentEffects/StatusEffects/GenericStatusEffect.cs +++ b/Content.Server/Chemistry/ReagentEffects/StatusEffects/GenericStatusEffect.cs @@ -30,6 +30,12 @@ namespace Content.Server.Chemistry.ReagentEffects.StatusEffects [DataField("time")] public float Time = 2.0f; + /// + /// true - refresh status effect time, false - accumulate status effect time + /// + [DataField("refresh")] + public bool Refresh = true; + /// /// Should this effect add the status effect, remove time from it, or set its cooldown? /// @@ -41,7 +47,7 @@ namespace Content.Server.Chemistry.ReagentEffects.StatusEffects var statusSys = args.EntityManager.EntitySysManager.GetEntitySystem(); if (Type == StatusEffectMetabolismType.Add && Component != String.Empty) { - statusSys.TryAddStatusEffect(args.SolutionEntity, Key, TimeSpan.FromSeconds(Time), Component); + statusSys.TryAddStatusEffect(args.SolutionEntity, Key, TimeSpan.FromSeconds(Time), Refresh, Component); } else if (Type == StatusEffectMetabolismType.Remove) { diff --git a/Content.Server/Chemistry/ReagentEffects/StatusEffects/Jitter.cs b/Content.Server/Chemistry/ReagentEffects/StatusEffects/Jitter.cs index 5310d27e62..c5cf34d0a6 100644 --- a/Content.Server/Chemistry/ReagentEffects/StatusEffects/Jitter.cs +++ b/Content.Server/Chemistry/ReagentEffects/StatusEffects/Jitter.cs @@ -23,10 +23,16 @@ namespace Content.Server.Chemistry.ReagentEffects.StatusEffects [DataField("time")] public float Time = 2.0f; + /// + /// true - refresh jitter time, false - accumulate jitter time + /// + [DataField("refresh")] + public bool Refresh = true; + public override void Effect(ReagentEffectArgs args) { args.EntityManager.EntitySysManager.GetEntitySystem() - .DoJitter(args.SolutionEntity, TimeSpan.FromSeconds(Time), Amplitude, Frequency); + .DoJitter(args.SolutionEntity, TimeSpan.FromSeconds(Time), Refresh, Amplitude, Frequency); } } } diff --git a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs index b2b0f39426..6a934338b3 100644 --- a/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/CleanTileReaction.cs @@ -1,12 +1,14 @@ using System.Linq; using Content.Server.Cleanable; using Content.Server.Coordinates.Helpers; +using Content.Server.Decals; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; +using Robust.Shared.Maths; using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Server.Chemistry.TileReactions @@ -36,6 +38,12 @@ namespace Content.Server.Chemistry.TileReactions } } + var decalSystem = EntitySystem.Get(); + foreach (var uid in decalSystem.GetDecalsInRange(tile.GridIndex, tile.GridIndices+new Vector2(0.5f, 0.5f), validDelegate: x => x.Cleanable)) + { + decalSystem.RemoveDecal(tile.GridIndex, uid); + } + return amount; } } diff --git a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs index 19fe6ff906..f0a1bdc3a5 100644 --- a/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillIfPuddlePresentTileReaction.cs @@ -1,9 +1,11 @@ using Content.Server.Fluids.Components; +using Content.Server.Fluids.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using JetBrains.Annotations; +using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Serialization.Manager.Attributes; @@ -15,9 +17,12 @@ namespace Content.Server.Chemistry.TileReactions { public FixedPoint2 TileReact(TileRef tile, ReagentPrototype reagent, FixedPoint2 reactVolume) { - if (reactVolume < 5 || !tile.TryGetPuddle(null, out _)) return FixedPoint2.Zero; + var spillSystem = EntitySystem.Get(); + if (reactVolume < 5 || !spillSystem.TryGetPuddle(tile, out _)) return FixedPoint2.Zero; - return tile.SpillAt(new Solution(reagent.ID, reactVolume), "PuddleSmear", true, false, true) != null ? reactVolume : FixedPoint2.Zero; + return spillSystem.SpillAt(tile,new Solution(reagent.ID, reactVolume), "PuddleSmear", true, false, true) != null + ? reactVolume + : FixedPoint2.Zero; } } } diff --git a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs index 05f7fa2d64..2123a76722 100644 --- a/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/SpillTileReaction.cs @@ -1,4 +1,5 @@ using Content.Server.Fluids.Components; +using Content.Server.Fluids.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reaction; using Content.Shared.Chemistry.Reagent; @@ -26,7 +27,8 @@ namespace Content.Server.Chemistry.TileReactions if (reactVolume < 5) return FixedPoint2.Zero; // TODO Make this not puddle smear. - var puddle = tile.SpillAt(new Solution(reagent.ID, reactVolume), "PuddleSmear", _overflow, false, true); + var puddle = EntitySystem.Get() + .SpillAt(tile, new Solution(reagent.ID, reactVolume), "PuddleSmear", _overflow, false, true); if (puddle != null) { diff --git a/Content.Server/Construction/Conditions/Locked.cs b/Content.Server/Construction/Conditions/Locked.cs new file mode 100644 index 0000000000..36f6c1899d --- /dev/null +++ b/Content.Server/Construction/Conditions/Locked.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Content.Server.Storage.Components; +using Content.Shared.Construction; +using Content.Shared.Examine; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Utility; + +namespace Content.Server.Construction.Conditions +{ + [UsedImplicitly] + [DataDefinition] + public class Locked : IGraphCondition + { + [DataField("locked")] + public bool IsLocked { get; private set; } = true; + + public bool Condition(EntityUid uid, IEntityManager entityManager) + { + if (!entityManager.TryGetComponent(uid, out LockComponent? lockcomp)) + return false; + + return lockcomp.Locked == IsLocked; + } + + public bool DoExamine(ExaminedEvent args) + { + var entMan = IoCManager.Resolve(); + var entity = args.Examined; + + if (!entMan.TryGetComponent(entity, out LockComponent? lockcomp)) return false; + + switch (IsLocked) + { + case true when !lockcomp.Locked: + args.PushMarkup(Loc.GetString("construction-examine-condition-lock")); + return true; + case false when lockcomp.Locked: + args.PushMarkup(Loc.GetString("construction-examine-condition-unlock")); + return true; + } + + return false; + } + + public IEnumerable GenerateGuideEntry() + { + yield return new ConstructionGuideEntry() + { + Localization = IsLocked + ? "construction-step-condition-wire-panel-lock" + : "construction-step-condition-wire-panel-unlock" + }; + } + } +} diff --git a/Content.Server/Construction/Conditions/StorageWelded.cs b/Content.Server/Construction/Conditions/StorageWelded.cs new file mode 100644 index 0000000000..b3780821c2 --- /dev/null +++ b/Content.Server/Construction/Conditions/StorageWelded.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using Content.Server.Storage.Components; +using Content.Shared.Construction; +using Content.Shared.Examine; +using JetBrains.Annotations; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Construction.Conditions +{ + [UsedImplicitly] + [DataDefinition] + public class StorageWelded : IGraphCondition + { + [DataField("welded")] + public bool Welded { get; private set; } = true; + + public bool Condition(EntityUid uid, IEntityManager entityManager) + { + if (!entityManager.TryGetComponent(uid, out EntityStorageComponent? entityStorageComponent)) + return false; + + return entityStorageComponent.IsWeldedShut == Welded; + } + + public bool DoExamine(ExaminedEvent args) + { + var entMan = IoCManager.Resolve(); + var entity = args.Examined; + + if (!entMan.TryGetComponent(entity, out EntityStorageComponent? entityStorage)) return false; + + var metaData = entMan.GetComponent(entity); + + if (entityStorage.IsWeldedShut != Welded) + { + if (Welded == true) + args.PushMarkup(Loc.GetString("construction-examine-condition-door-weld", ("entityName", metaData.EntityName)) + "\n"); + else + args.PushMarkup(Loc.GetString("construction-examine-condition-door-unweld", ("entityName", metaData.EntityName)) + "\n"); + return true; + } + + return false; + } + + public IEnumerable GenerateGuideEntry() + { + yield return new ConstructionGuideEntry() + { + Localization = Welded + ? "construction-guide-condition-door-weld" + : "construction-guide-condition-door-unweld", + }; + } + } +} diff --git a/Content.Server/Conveyor/ConveyorSystem.cs b/Content.Server/Conveyor/ConveyorSystem.cs index b5ee3a5dd3..cef36ac6a3 100644 --- a/Content.Server/Conveyor/ConveyorSystem.cs +++ b/Content.Server/Conveyor/ConveyorSystem.cs @@ -59,7 +59,7 @@ namespace Content.Server.Conveyor signal != TwoWayLeverSignal.Middle) { args.Cancel(); - _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(2f)); + _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(2f), true); component.Owner.PopupMessage(args.Attemptee, Loc.GetString("conveyor-component-failed-link")); } } diff --git a/Content.Server/Crayon/CrayonComponent.cs b/Content.Server/Crayon/CrayonComponent.cs index c863e18ec9..e7a1c32df7 100644 --- a/Content.Server/Crayon/CrayonComponent.cs +++ b/Content.Server/Crayon/CrayonComponent.cs @@ -2,9 +2,10 @@ using System.Linq; using System.Threading.Tasks; using Content.Server.Administration.Logs; using Content.Server.UserInterface; -using Content.Shared.Administration.Logs; using Content.Shared.Audio; using Content.Shared.Crayon; +using Content.Server.Decals; +using Content.Shared.Decals; using Content.Shared.Database; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; @@ -60,11 +61,8 @@ namespace Content.Server.Crayon Charges = Capacity; // Get the first one from the catalog and set it as default - var decals = _prototypeManager.EnumeratePrototypes().FirstOrDefault(); - if (decals != null) - { - SelectedState = decals.Decals.First(); - } + var decal = _prototypeManager.EnumeratePrototypes().FirstOrDefault(x => x.Tags.Contains("crayon")); + SelectedState = decal?.ID ?? string.Empty; Dirty(); } @@ -74,14 +72,10 @@ namespace Content.Server.Crayon { case CrayonSelectMessage msg: // Check if the selected state is valid - var crayonDecals = _prototypeManager.EnumeratePrototypes().FirstOrDefault(); - if (crayonDecals != null) + if (_prototypeManager.TryIndex(msg.State, out var prototype) && prototype.Tags.Contains("crayon")) { - if (crayonDecals.Decals.Contains(msg.State)) - { - SelectedState = msg.State; - Dirty(); - } + SelectedState = msg.State; + Dirty(); } break; default: @@ -131,12 +125,8 @@ namespace Content.Server.Crayon return true; } - var entity = IoCManager.Resolve().SpawnEntity("CrayonDecal", eventArgs.ClickLocation); - if (IoCManager.Resolve().TryGetComponent(entity, out AppearanceComponent? appearance)) - { - appearance.SetData(CrayonVisuals.State, SelectedState); - appearance.SetData(CrayonVisuals.Color, _color); - } + if(!EntitySystem.Get().TryAddDecal(SelectedState, eventArgs.ClickLocation.Offset(new Vector2(-0.5f,-0.5f)), out _, Color.FromName(_color), cleanable: true)) + return false; if (_useSound != null) SoundSystem.Play(Filter.Pvs(Owner), _useSound.GetSound(), Owner, AudioHelpers.WithVariation(0.125f)); diff --git a/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs b/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs index 5bbbd64422..e5b093e86c 100644 --- a/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs +++ b/Content.Server/Damage/Systems/DamageOnHighSpeedImpactSystem.cs @@ -51,7 +51,7 @@ namespace Content.Server.Damage.Systems component.LastHit = _gameTiming.CurTime; if (_robustRandom.Prob(component.StunChance)) - _stunSystem.TryStun(uid, TimeSpan.FromSeconds(component.StunSeconds)); + _stunSystem.TryStun(uid, TimeSpan.FromSeconds(component.StunSeconds), true); var damageScale = (speed / component.MinimumSpeed) * component.Factor; diff --git a/Content.Server/Decals/Commands/AddDecalCommand.cs b/Content.Server/Decals/Commands/AddDecalCommand.cs new file mode 100644 index 0000000000..e9ef52fdea --- /dev/null +++ b/Content.Server/Decals/Commands/AddDecalCommand.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using Content.Server.Administration; +using Content.Shared.Administration; +using Content.Shared.Decals; +using Content.Shared.Maps; +using Robust.Shared.Console; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Prototypes; + +namespace Content.Server.Decals.Commands +{ + [AdminCommand(AdminFlags.Mapping)] + public sealed class AddDecalCommand : IConsoleCommand + { + public string Command => "adddecal"; + public string Description => "Creates a decal on the map"; + public string Help => $"{Command} [angle= zIndex= color=]"; + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length < 4 || args.Length > 7) + { + shell.WriteError($"Received invalid amount of arguments arguments. Expected 4 to 7, got {args.Length}.\nUsage: {Help}"); + return; + } + + if (!IoCManager.Resolve().HasIndex(args[0])) + { + shell.WriteError($"Cannot find decalprototype '{args[0]}'."); + } + + if (!float.TryParse(args[1], out var x)) + { + shell.WriteError($"Failed parsing x-coordinate '{args[1]}'."); + return; + } + + if (!float.TryParse(args[2], out var y)) + { + shell.WriteError($"Failed parsing y-coordinate'{args[2]}'."); + return; + } + + var mapManager = IoCManager.Resolve(); + if (!int.TryParse(args[3], out var gridIdRaw) || !mapManager.TryGetGrid(new GridId(gridIdRaw), out var grid)) + { + shell.WriteError($"Failed parsing gridId '{args[3]}'."); + return; + } + + var coordinates = new EntityCoordinates(grid.GridEntityId, new Vector2(x, y)); + if (grid.GetTileRef(coordinates).IsSpace()) + { + shell.WriteError($"Cannot create decal on space tile at {coordinates}."); + return; + } + + Color? color = null; + var zIndex = 0; + Angle? rotation = null; + if (args.Length > 4) + { + for (int i = 4; i < args.Length; i++) + { + var rawValue = args[i].Split('='); + if (rawValue.Length != 2) + { + shell.WriteError($"Failed parsing parameter: '{args[i]}'"); + return; + } + + switch (rawValue[0]) + { + case "angle": + if (!double.TryParse(rawValue[1], out var degrees)) + { + shell.WriteError($"Failed parsing angle '{rawValue[1]}'."); + return; + } + rotation = Angle.FromDegrees(degrees); + break; + case "zIndex": + if (!int.TryParse(rawValue[1], out zIndex)) + { + shell.WriteError($"Failed parsing zIndex '{rawValue[1]}'."); + return; + } + break; + case "color": + if (!Color.TryFromName(rawValue[1], out var colorRaw)) + { + shell.WriteError($"Failed parsing color '{rawValue[1]}'."); + return; + } + + color = colorRaw; + break; + default: + shell.WriteError($"Unknown parameter key '{rawValue[0]}'."); + return; + } + } + } + + if(EntitySystem.Get().TryAddDecal(args[0], coordinates, out var uid, color, rotation, zIndex)) + { + shell.WriteLine($"Successfully created decal {uid}."); + } + else + { + shell.WriteError($"Failed adding decal."); + } + } + } +} diff --git a/Content.Server/Decals/Commands/EditDecalCommand.cs b/Content.Server/Decals/Commands/EditDecalCommand.cs new file mode 100644 index 0000000000..af01b4d79b --- /dev/null +++ b/Content.Server/Decals/Commands/EditDecalCommand.cs @@ -0,0 +1,162 @@ +using Content.Server.Administration; +using Content.Shared.Administration; +using Robust.Shared.Console; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; + +namespace Content.Server.Decals; + +[AdminCommand(AdminFlags.Mapping)] +public class EditDecalCommand : IConsoleCommand +{ + public string Command => "editdecal"; + public string Description => "Edits a decal."; + public string Help => $@"{Command} \n +Possible modes are:\n +- position \n +- color \n +- id \n +- rotation \n +- zindex \n +- clean +"; + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length < 4) + { + shell.WriteError("Expected at least 5 arguments."); + return; + } + + if (!int.TryParse(args[0], out var gridIdRaw)) + { + shell.WriteError($"Failed parsing gridId '{args[3]}'."); + return; + } + + if (!uint.TryParse(args[1], out var uid)) + { + shell.WriteError($"Failed parsing uid '{args[1]}'."); + return; + } + + var gridId = new GridId(gridIdRaw); + if (!IoCManager.Resolve().GridExists(gridId)) + { + shell.WriteError($"No grid with gridId {gridId} exists."); + return; + } + + var decalSystem = EntitySystem.Get(); + switch (args[2].ToLower()) + { + case "position": + if(args.Length != 5) + { + shell.WriteError("Expected 6 arguments."); + return; + } + + if (!float.TryParse(args[3], out var x) || !float.TryParse(args[4], out var y)) + { + shell.WriteError("Failed parsing position."); + return; + } + + if (!decalSystem.SetDecalPosition(gridId, uid, gridId, new Vector2(x, y))) + { + shell.WriteError("Failed changing decalposition."); + } + break; + case "color": + if(args.Length != 4) + { + shell.WriteError("Expected 5 arguments."); + return; + } + + if (!Color.TryFromName(args[3], out var color)) + { + shell.WriteError("Failed parsing color."); + return; + } + + if (!decalSystem.SetDecalColor(gridId, uid, color)) + { + shell.WriteError("Failed changing decal color."); + } + break; + case "id": + if(args.Length != 4) + { + shell.WriteError("Expected 5 arguments."); + return; + } + + if (!decalSystem.SetDecalId(gridId, uid, args[3])) + { + shell.WriteError("Failed changing decal id."); + } + break; + case "rotation": + if(args.Length != 4) + { + shell.WriteError("Expected 5 arguments."); + return; + } + + if (!double.TryParse(args[3], out var degrees)) + { + shell.WriteError("Failed parsing degrees."); + return; + } + + if (!decalSystem.SetDecalRotation(gridId, uid, Angle.FromDegrees(degrees))) + { + shell.WriteError("Failed changing decal rotation."); + } + break; + case "zindex": + if(args.Length != 4) + { + shell.WriteError("Expected 5 arguments."); + return; + } + + if (!int.TryParse(args[3], out var zIndex)) + { + shell.WriteError("Failed parsing zIndex."); + return; + } + + if (!decalSystem.SetDecalZIndex(gridId, uid, zIndex)) + { + shell.WriteError("Failed changing decal zIndex."); + } + break; + case "clean": + if(args.Length != 4) + { + shell.WriteError("Expected 5 arguments."); + return; + } + + if (!bool.TryParse(args[3], out var cleanable)) + { + shell.WriteError("Failed parsing cleanable."); + return; + } + + if (!decalSystem.SetDecalCleanable(gridId, uid, cleanable)) + { + shell.WriteError("Failed changing decal cleanable flag."); + } + break; + default: + shell.WriteError("Invalid mode."); + return; + } + } +} diff --git a/Content.Server/Decals/Commands/RemoveDecalCommand.cs b/Content.Server/Decals/Commands/RemoveDecalCommand.cs new file mode 100644 index 0000000000..a2bf370f34 --- /dev/null +++ b/Content.Server/Decals/Commands/RemoveDecalCommand.cs @@ -0,0 +1,46 @@ +using Content.Server.Administration; +using Content.Shared.Administration; +using Robust.Shared.Console; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; + +namespace Content.Server.Decals.Commands +{ + [AdminCommand(AdminFlags.Mapping)] + public class RemoveDecalCommand : IConsoleCommand + { + public string Command => "rmdecal"; + public string Description => "removes a decal"; + public string Help => $"{Command} "; + public void Execute(IConsoleShell shell, string argStr, string[] args) + { + if (args.Length != 2) + { + shell.WriteError($"Unexpected number of arguments.\nExpected two: {Help}"); + return; + } + + if (!uint.TryParse(args[0], out var uid)) + { + shell.WriteError($"Failed parsing uid."); + return; + } + + if (!int.TryParse(args[1], out var rawGridId) || + !IoCManager.Resolve().GridExists(new GridId(rawGridId))) + { + shell.WriteError("Failed parsing gridId."); + } + + var decalSystem = EntitySystem.Get(); + if (decalSystem.RemoveDecal(new GridId(rawGridId), uid)) + { + shell.WriteLine($"Successfully removed decal {uid}."); + return; + } + + shell.WriteError($"Failed trying to remove decal {uid}."); + } + } +} diff --git a/Content.Server/Decals/DecalSystem.cs b/Content.Server/Decals/DecalSystem.cs new file mode 100644 index 0000000000..f7f4f9525c --- /dev/null +++ b/Content.Server/Decals/DecalSystem.cs @@ -0,0 +1,326 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Content.Shared.Decals; +using Content.Shared.Maps; +using Robust.Server.Player; +using Robust.Shared.Enums; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Player; + +namespace Content.Server.Decals +{ + public class DecalSystem : SharedDecalSystem + { + [Dependency] private readonly IPlayerManager _playerManager = default!; + + private readonly Dictionary> _dirtyChunks = new(); + private readonly Dictionary>> _previousSentChunks = new(); + + public override void Initialize() + { + base.Initialize(); + + _playerManager.PlayerStatusChanged += OnPlayerStatusChanged; + MapManager.TileChanged += OnTileChanged; + } + + public override void Shutdown() + { + base.Shutdown(); + + _playerManager.PlayerStatusChanged -= OnPlayerStatusChanged; + MapManager.TileChanged -= OnTileChanged; + } + + private void OnTileChanged(object? sender, TileChangedEventArgs e) + { + if (!e.NewTile.IsSpace()) + return; + + var chunkCollection = ChunkCollection(e.NewTile.GridIndex); + var indices = GetChunkIndices(e.NewTile.GridIndices); + var toDelete = new HashSet(); + if (chunkCollection.TryGetValue(indices, out var chunk)) + { + foreach (var (uid, decal) in chunk) + { + if (new Vector2((int) Math.Floor(decal.Coordinates.X), (int) Math.Floor(decal.Coordinates.Y)) == + e.NewTile.GridIndices) + { + toDelete.Add(uid); + } + } + } + + if (toDelete.Count == 0) return; + + foreach (var uid in toDelete) + { + RemoveDecalInternal(e.NewTile.GridIndex, uid); + } + + DirtyChunk(e.NewTile.GridIndex, indices); + } + + private void OnPlayerStatusChanged(object? sender, SessionStatusEventArgs e) + { + switch (e.NewStatus) + { + case SessionStatus.InGame: + _previousSentChunks[e.Session] = new(); + break; + case SessionStatus.Disconnected: + _previousSentChunks.Remove(e.Session); + break; + } + } + + protected override void DirtyChunk(GridId id, Vector2i chunkIndices) + { + if(!_dirtyChunks.ContainsKey(id)) + _dirtyChunks[id] = new HashSet(); + _dirtyChunks[id].Add(chunkIndices); + } + + public bool TryAddDecal(string id, EntityCoordinates coordinates, [NotNullWhen(true)] out uint? uid, Color? color = null, Angle? rotation = null, int zIndex = 0, bool cleanable = false) + { + uid = 0; + if (!PrototypeManager.HasIndex(id)) + return false; + + var gridId = coordinates.GetGridId(EntityManager); + if (MapManager.GetGrid(gridId).GetTileRef(coordinates).IsSpace()) + return false; + + rotation ??= Angle.Zero; + var decal = new Decal(coordinates.Position, id, color, rotation.Value, zIndex, cleanable); + var chunkCollection = DecalGridChunkCollection(gridId); + uid = chunkCollection.NextUid++; + var chunkIndices = GetChunkIndices(decal.Coordinates); + if(!chunkCollection.ChunkCollection.ContainsKey(chunkIndices)) + chunkCollection.ChunkCollection[chunkIndices] = new(); + chunkCollection.ChunkCollection[chunkIndices][uid.Value] = decal; + ChunkIndex[gridId][uid.Value] = chunkIndices; + DirtyChunk(gridId, chunkIndices); + return true; + } + + public bool RemoveDecal(GridId gridId, uint uid) => RemoveDecalInternal(gridId, uid); + + public HashSet GetDecalsInRange(GridId gridId, Vector2 position, float distance = 0.75f, Func? validDelegate = null) + { + var uids = new HashSet(); + var chunkCollection = ChunkCollection(gridId); + var chunkIndices = GetChunkIndices(position); + if (!chunkCollection.TryGetValue(chunkIndices, out var chunk)) + return uids; + + foreach (var (uid, decal) in chunk) + { + if ((position - decal.Coordinates-new Vector2(0.5f, 0.5f)).Length > distance) + continue; + + if (validDelegate == null || validDelegate(decal)) + { + uids.Add(uid); + } + } + + return uids; + } + + public bool SetDecalPosition(GridId gridId, uint uid, EntityCoordinates coordinates) + { + return SetDecalPosition(gridId, uid, coordinates.GetGridId(EntityManager), coordinates.Position); + } + + public bool SetDecalPosition(GridId gridId, uint uid, GridId newGridId, Vector2 position) + { + if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices)) + { + return false; + } + + DirtyChunk(gridId, indices); + var chunkCollection = ChunkCollection(gridId); + var decal = chunkCollection[indices][uid]; + if (newGridId == gridId) + { + chunkCollection[indices][uid] = decal.WithCoordinates(position); + return true; + } + + RemoveDecalInternal(gridId, uid); + + var newChunkCollection = ChunkCollection(newGridId); + var chunkIndices = GetChunkIndices(position); + if(!newChunkCollection.ContainsKey(chunkIndices)) + newChunkCollection[chunkIndices] = new(); + newChunkCollection[chunkIndices][uid] = decal.WithCoordinates(position); + ChunkIndex[newGridId][uid] = chunkIndices; + DirtyChunk(newGridId, chunkIndices); + return true; + } + + public bool SetDecalColor(GridId gridId, uint uid, Color? color) + { + if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices)) + { + return false; + } + + var chunkCollection = ChunkCollection(gridId); + var decal = chunkCollection[indices][uid]; + chunkCollection[indices][uid] = decal.WithColor(color); + DirtyChunk(gridId, indices); + return true; + } + + public bool SetDecalId(GridId gridId, uint uid, string id) + { + if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices)) + { + return false; + } + + if (!PrototypeManager.HasIndex(id)) + throw new ArgumentOutOfRangeException($"Tried to set decal id to invalid prototypeid: {id}"); + + var chunkCollection = ChunkCollection(gridId); + var decal = chunkCollection[indices][uid]; + chunkCollection[indices][uid] = decal.WithId(id); + DirtyChunk(gridId, indices); + return true; + } + + public bool SetDecalRotation(GridId gridId, uint uid, Angle angle) + { + if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices)) + { + return false; + } + + var chunkCollection = ChunkCollection(gridId); + var decal = chunkCollection[indices][uid]; + chunkCollection[indices][uid] = decal.WithRotation(angle); + DirtyChunk(gridId, indices); + return true; + } + + public bool SetDecalZIndex(GridId gridId, uint uid, int zIndex) + { + if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices)) + { + return false; + } + + var chunkCollection = ChunkCollection(gridId); + var decal = chunkCollection[indices][uid]; + chunkCollection[indices][uid] = decal.WithZIndex(zIndex); + DirtyChunk(gridId, indices); + return true; + } + + public bool SetDecalCleanable(GridId gridId, uint uid, bool cleanable) + { + if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices)) + { + return false; + } + + var chunkCollection = ChunkCollection(gridId); + var decal = chunkCollection[indices][uid]; + chunkCollection[indices][uid] = decal.WithCleanable(cleanable); + DirtyChunk(gridId, indices); + return true; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + + foreach (var session in Filter.Broadcast().Recipients) + { + if(session is not IPlayerSession playerSession || playerSession.Status != SessionStatus.InGame) + continue; + + var chunks = GetChunksForSession(playerSession); + var updatedChunks = new Dictionary>(); + foreach (var (gridId, gridChunks) in chunks) + { + var newChunks = new HashSet(gridChunks); + if (_previousSentChunks[playerSession].TryGetValue(gridId, out var previousChunks)) + { + newChunks.ExceptWith(previousChunks); + } + + if (_dirtyChunks.TryGetValue(gridId, out var dirtyChunks)) + { + gridChunks.IntersectWith(dirtyChunks); + newChunks.UnionWith(gridChunks); + } + + if (newChunks.Count == 0) + continue; + + updatedChunks[gridId] = newChunks; + } + + if(updatedChunks.Count == 0) + continue; + + _previousSentChunks[playerSession] = chunks; + + //send all gridChunks to client + SendChunkUpdates(playerSession, updatedChunks); + } + + _dirtyChunks.Clear(); + } + + private void SendChunkUpdates(IPlayerSession session, Dictionary> updatedChunks) + { + var updatedDecals = new Dictionary>>(); + foreach (var (gridId, chunks) in updatedChunks) + { + var gridChunks = new Dictionary>(); + foreach (var indices in chunks) + { + gridChunks.Add(indices, + ChunkCollection(gridId).TryGetValue(indices, out var chunk) + ? chunk + : new Dictionary()); + } + updatedDecals[gridId] = gridChunks; + } + + RaiseNetworkEvent(new DecalChunkUpdateEvent{Data = updatedDecals}, Filter.SinglePlayer(session)); + } + + private HashSet GetSessionViewers(IPlayerSession session) + { + var viewers = new HashSet(); + if (session.Status != SessionStatus.InGame || session.AttachedEntity is null) + return viewers; + + viewers.Add(session.AttachedEntity.Value); + + foreach (var uid in session.ViewSubscriptions) + { + viewers.Add(uid); + } + + return viewers; + } + + private Dictionary> GetChunksForSession(IPlayerSession session) + { + return GetChunksForViewers(GetSessionViewers(session)); + } + } +} diff --git a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs index b2d03e03c7..dbb9a8fca6 100644 --- a/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs +++ b/Content.Server/Destructible/Thresholds/Behaviors/SpillBehavior.cs @@ -1,5 +1,6 @@ using Content.Server.Chemistry.EntitySystems; using Content.Server.Fluids.Components; +using Content.Server.Fluids.EntitySystems; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.Serialization.Manager.Attributes; @@ -20,10 +21,10 @@ namespace Content.Server.Destructible.Thresholds.Behaviors /// /// Entity on which behavior is executed /// system calling the behavior - /// public void Execute(EntityUid owner, DestructibleSystem system) { var solutionContainerSystem = EntitySystem.Get(); + var spillableSystem = EntitySystem.Get(); var coordinates = system.EntityManager.GetComponent(owner).Coordinates; @@ -31,12 +32,12 @@ namespace Content.Server.Destructible.Thresholds.Behaviors solutionContainerSystem.TryGetSolution(owner, spillableComponent.SolutionName, out var compSolution)) { - compSolution.SpillAt(coordinates, "PuddleSmear", false); + spillableSystem.SpillAt(compSolution, coordinates, "PuddleSmear", false); } else if (Solution != null && solutionContainerSystem.TryGetSolution(owner, Solution, out var behaviorSolution)) { - behaviorSolution.SpillAt(coordinates, "PuddleSmear", false); + spillableSystem.SpillAt(behaviorSolution, coordinates, "PuddleSmear", false); } } } diff --git a/Content.Server/DoAfter/DoAfterSystem.cs b/Content.Server/DoAfter/DoAfterSystem.cs index e64afc9456..ea301b9743 100644 --- a/Content.Server/DoAfter/DoAfterSystem.cs +++ b/Content.Server/DoAfter/DoAfterSystem.cs @@ -12,8 +12,8 @@ namespace Content.Server.DoAfter public sealed class DoAfterSystem : EntitySystem { // We cache these lists as to not allocate them every update tick... - private readonly List _cancelled = new(); - private readonly List _finished = new(); + private readonly Queue _cancelled = new(); + private readonly Queue _finished = new(); public override void Initialize() { @@ -52,17 +52,17 @@ namespace Content.Server.DoAfter case DoAfterStatus.Running: break; case DoAfterStatus.Cancelled: - _cancelled.Add(doAfter); + _cancelled.Enqueue(doAfter); break; case DoAfterStatus.Finished: - _finished.Add(doAfter); + _finished.Enqueue(doAfter); break; default: throw new ArgumentOutOfRangeException(); } } - foreach (var doAfter in _cancelled) + while (_cancelled.TryDequeue(out var doAfter)) { comp.Cancelled(doAfter); @@ -76,7 +76,7 @@ namespace Content.Server.DoAfter RaiseLocalEvent(doAfter.EventArgs.BroadcastCancelledEvent); } - foreach (var doAfter in _finished) + while (_finished.TryDequeue(out var doAfter)) { comp.Finished(doAfter); @@ -89,10 +89,6 @@ namespace Content.Server.DoAfter if(doAfter.EventArgs.BroadcastFinishedEvent != null) RaiseLocalEvent(doAfter.EventArgs.BroadcastFinishedEvent); } - - // Clean the shared lists at the end, ensuring they'll be clean for the next time we need them. - _cancelled.Clear(); - _finished.Clear(); } } diff --git a/Content.Server/Doors/Components/ServerDoorComponent.cs b/Content.Server/Doors/Components/ServerDoorComponent.cs index 34f7a46b28..6948a336c0 100644 --- a/Content.Server/Doors/Components/ServerDoorComponent.cs +++ b/Content.Server/Doors/Components/ServerDoorComponent.cs @@ -584,7 +584,7 @@ namespace Content.Server.Doors.Components if (IoCManager.Resolve().HasComponent(e.Owner)) EntitySystem.Get().TryChangeDamage(e.Owner, CrushDamage); - EntitySystem.Get().TryParalyze(e.Owner, TimeSpan.FromSeconds(DoorStunTime)); + EntitySystem.Get().TryParalyze(e.Owner, TimeSpan.FromSeconds(DoorStunTime), true); } // If we hit someone, open up after stun (opens right when stun ends) diff --git a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs b/Content.Server/Electrocution/Components/ElectrifiedComponent.cs index 1c10308613..9e0da6d030 100644 --- a/Content.Server/Electrocution/Components/ElectrifiedComponent.cs +++ b/Content.Server/Electrocution/Components/ElectrifiedComponent.cs @@ -26,6 +26,9 @@ namespace Content.Server.Electrocution [DataField("onHandInteract")] public bool OnHandInteract { get; set; } = true; + [DataField("onInteractUsing")] + public bool OnInteractUsing { get; set; } = true; + [DataField("requirePower")] public bool RequirePower { get; } = true; diff --git a/Content.Server/Electrocution/ElectrocuteCommand.cs b/Content.Server/Electrocution/ElectrocuteCommand.cs index cbcc6b54ce..3b753da297 100644 --- a/Content.Server/Electrocution/ElectrocuteCommand.cs +++ b/Content.Server/Electrocution/ElectrocuteCommand.cs @@ -52,7 +52,7 @@ namespace Content.Server.Electrocution } entityManager.EntitySysManager.GetEntitySystem() - .TryDoElectrocution(uid, null, damage, TimeSpan.FromSeconds(seconds)); + .TryDoElectrocution(uid, null, damage, TimeSpan.FromSeconds(seconds), true); } } } diff --git a/Content.Server/Electrocution/ElectrocutionSystem.cs b/Content.Server/Electrocution/ElectrocutionSystem.cs index a8194623d0..6c2893936c 100644 --- a/Content.Server/Electrocution/ElectrocutionSystem.cs +++ b/Content.Server/Electrocution/ElectrocutionSystem.cs @@ -73,6 +73,7 @@ namespace Content.Server.Electrocution SubscribeLocalEvent(OnElectrifiedStartCollide); SubscribeLocalEvent(OnElectrifiedAttacked); SubscribeLocalEvent(OnElectrifiedHandInteract); + SubscribeLocalEvent(OnElectrifiedInteractUsing); SubscribeLocalEvent(OnRandomInsulationMapInit); UpdatesAfter.Add(typeof(PowerNetSystem)); @@ -140,6 +141,14 @@ namespace Content.Server.Electrocution TryDoElectrifiedAct(uid, args.User, electrified); } + private void OnElectrifiedInteractUsing(EntityUid uid, ElectrifiedComponent electrified, InteractUsingEvent args) + { + if (!electrified.OnInteractUsing) + return; + + TryDoElectrifiedAct(uid, args.User, electrified); + } + public bool TryDoElectrifiedAct(EntityUid uid, EntityUid targetUid, ElectrifiedComponent? electrified = null, NodeContainerComponent? nodeContainer = null, @@ -177,7 +186,7 @@ namespace Content.Server.Electrocution entity, uid, (int) (electrified.ShockDamage * MathF.Pow(RecursiveDamageMultiplier, depth)), - TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth)), + TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth)), true, electrified.SiemensCoefficient); } @@ -213,7 +222,7 @@ namespace Content.Server.Electrocution node, (int) (electrified.ShockDamage * MathF.Pow(RecursiveDamageMultiplier, depth) * damageMult), TimeSpan.FromSeconds(electrified.ShockTime * MathF.Pow(RecursiveTimeMultiplier, depth) * - timeMult), + timeMult), true, electrified.SiemensCoefficient); } @@ -235,12 +244,12 @@ namespace Content.Server.Electrocution /// Whether the entity was stunned by the shock. public bool TryDoElectrocution( - EntityUid uid, EntityUid? sourceUid, int shockDamage, TimeSpan time, float siemensCoefficient = 1f, + EntityUid uid, EntityUid? sourceUid, int shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f, StatusEffectsComponent? statusEffects = null, SharedAlertsComponent? alerts = null) { if (!DoCommonElectrocutionAttempt(uid, sourceUid, ref siemensCoefficient) - || !DoCommonElectrocution(uid, sourceUid, shockDamage, time, siemensCoefficient, statusEffects, alerts)) + || !DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects, alerts)) return false; RaiseLocalEvent(uid, new ElectrocutedEvent(uid, sourceUid, siemensCoefficient)); @@ -254,6 +263,7 @@ namespace Content.Server.Electrocution Node node, int shockDamage, TimeSpan time, + bool refresh, float siemensCoefficient = 1f, StatusEffectsComponent? statusEffects = null, SharedAlertsComponent? alerts = null, @@ -264,9 +274,9 @@ namespace Content.Server.Electrocution // Coefficient needs to be higher than this to do a powered electrocution! if(siemensCoefficient <= 0.5f) - return DoCommonElectrocution(uid, sourceUid, shockDamage, time, siemensCoefficient, statusEffects, alerts); + return DoCommonElectrocution(uid, sourceUid, shockDamage, time, refresh, siemensCoefficient, statusEffects, alerts); - if (!DoCommonElectrocution(uid, sourceUid, null, time, siemensCoefficient, statusEffects, alerts)) + if (!DoCommonElectrocution(uid, sourceUid, null, time, refresh, siemensCoefficient, statusEffects, alerts)) return false; if (!Resolve(sourceUid, ref sourceTransform)) // This shouldn't really happen, but just in case... @@ -307,7 +317,7 @@ namespace Content.Server.Electrocution } private bool DoCommonElectrocution(EntityUid uid, EntityUid? sourceUid, - int? shockDamage, TimeSpan time, float siemensCoefficient = 1f, + int? shockDamage, TimeSpan time, bool refresh, float siemensCoefficient = 1f, StatusEffectsComponent? statusEffects = null, SharedAlertsComponent? alerts = null) { @@ -329,14 +339,14 @@ namespace Content.Server.Electrocution !_statusEffectsSystem.CanApplyEffect(uid, StatusEffectKey, statusEffects)) return false; - if (!_statusEffectsSystem.TryAddStatusEffect(uid, StatusEffectKey, time, + if (!_statusEffectsSystem.TryAddStatusEffect(uid, StatusEffectKey, time, refresh, statusEffects, alerts)) return false; var shouldStun = siemensCoefficient > 0.5f; if (shouldStun) - _stunSystem.TryParalyze(uid, time * ParalyzeTimeMultiplier, statusEffects, alerts); + _stunSystem.TryParalyze(uid, time * ParalyzeTimeMultiplier, refresh, statusEffects, alerts); // TODO: Sparks here. @@ -350,8 +360,8 @@ namespace Content.Server.Electrocution $"{statusEffects.Owner} took {actual.Total} powered electrocution damage"); } - _stutteringSystem.DoStutter(uid, time * StutteringTimeMultiplier, statusEffects, alerts); - _jitteringSystem.DoJitter(uid, time * JitterTimeMultiplier, JitterAmplitude, JitterFrequency, true, + _stutteringSystem.DoStutter(uid, time * StutteringTimeMultiplier, refresh, statusEffects, alerts); + _jitteringSystem.DoJitter(uid, time * JitterTimeMultiplier, refresh, JitterAmplitude, JitterFrequency, true, statusEffects, alerts); _popupSystem.PopupEntity(Loc.GetString("electrocuted-component-mob-shocked-popup-player"), uid, diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index fcd52df36d..f01701a824 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -294,6 +294,7 @@ namespace Content.Server.Explosion.EntitySystems int heavyImpactRange = 0, int lightImpactRange = 0, int flashRange = 0, + EntityUid? user = null, ExplosiveComponent? explosive = null, TransformComponent? transform = null) { @@ -306,7 +307,7 @@ namespace Content.Server.Explosion.EntitySystems if (explosive is { Exploding: false }) { - _triggers.Explode(entity, explosive); + _triggers.Explode(entity, explosive, user); } else { @@ -322,7 +323,7 @@ namespace Content.Server.Explosion.EntitySystems var epicenter = transform.Coordinates; - SpawnExplosion(epicenter, devastationRange, heavyImpactRange, lightImpactRange, flashRange); + SpawnExplosion(epicenter, devastationRange, heavyImpactRange, lightImpactRange, flashRange, entity, user); } } @@ -331,7 +332,9 @@ namespace Content.Server.Explosion.EntitySystems int devastationRange = 0, int heavyImpactRange = 0, int lightImpactRange = 0, - int flashRange = 0) + int flashRange = 0, + EntityUid? entity = null, + EntityUid? user = null) { var mapId = epicenter.GetMapId(EntityManager); if (mapId == MapId.Nullspace) @@ -339,8 +342,22 @@ namespace Content.Server.Explosion.EntitySystems return; } - _logSystem.Add(LogType.Damaged, LogImpact.High , - $"Spawned explosion at {epicenter} with range {devastationRange}/{heavyImpactRange}/{lightImpactRange}/{flashRange}"); + // logging + var text = $"{epicenter} with range {devastationRange}/{heavyImpactRange}/{lightImpactRange}/{flashRange}"; + if (entity == null) + { + _logSystem.Add(LogType.Explosion, LogImpact.High, $"Explosion spawned at {text}"); + } + else if (user == null) + { + _logSystem.Add(LogType.Explosion, LogImpact.High, + $"{entity.Value} exploded at {text}"); + } + else + { + _logSystem.Add(LogType.Explosion, LogImpact.High, + $"{entity.Value} caused {entity.Value} to explode at {text}"); + } var maxRange = MathHelper.Max(devastationRange, heavyImpactRange, lightImpactRange, 0); var epicenterMapPos = epicenter.ToMapPos(EntityManager); diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs index 1df2297336..00039ca069 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.cs @@ -1,10 +1,14 @@ using System; +using Content.Server.Administration.Logs; using Content.Server.Doors.Components; using Content.Server.Explosion.Components; using Content.Server.Flash; using Content.Server.Flash.Components; +using Content.Server.Projectiles.Components; using Content.Shared.Audio; +using Content.Shared.Database; using Content.Shared.Doors; +using Content.Shared.Throwing; using JetBrains.Annotations; using Robust.Shared.Audio; using Robust.Shared.GameObjects; @@ -21,9 +25,9 @@ namespace Content.Server.Explosion.EntitySystems public class TriggerEvent : HandledEntityEventArgs { public EntityUid Triggered { get; } - public EntityUid User { get; } + public EntityUid? User { get; } - public TriggerEvent(EntityUid triggered, EntityUid user = default) + public TriggerEvent(EntityUid triggered, EntityUid? user = null) { Triggered = triggered; User = user; @@ -35,6 +39,7 @@ namespace Content.Server.Explosion.EntitySystems { [Dependency] private readonly ExplosionSystem _explosions = default!; [Dependency] private readonly FlashSystem _flashSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; public override void Initialize() { @@ -53,11 +58,11 @@ namespace Content.Server.Explosion.EntitySystems { if (!EntityManager.TryGetComponent(uid, out ExplosiveComponent? explosiveComponent)) return; - Explode(uid, explosiveComponent); + Explode(uid, explosiveComponent, args.User); } // You really shouldn't call this directly (TODO Change that when ExplosionHelper gets changed). - public void Explode(EntityUid uid, ExplosiveComponent component) + public void Explode(EntityUid uid, ExplosiveComponent component, EntityUid? user = null) { if (component.Exploding) { @@ -65,7 +70,12 @@ namespace Content.Server.Explosion.EntitySystems } component.Exploding = true; - _explosions.SpawnExplosion(uid, component.DevastationRange, component.HeavyImpactRange, component.LightImpactRange, component.FlashRange); + _explosions.SpawnExplosion(uid, + component.DevastationRange, + component.HeavyImpactRange, + component.LightImpactRange, + component.FlashRange, + user); EntityManager.QueueDeleteEntity(uid); } #endregion @@ -113,16 +123,23 @@ namespace Content.Server.Explosion.EntitySystems private void HandleCollide(EntityUid uid, TriggerOnCollideComponent component, StartCollideEvent args) { - Trigger(component.Owner); + EntityUid? user = null; + if (EntityManager.TryGetComponent(uid, out ProjectileComponent projectile)) + user = projectile.Shooter; + else if (EntityManager.TryGetComponent(uid, out ThrownItemComponent thrown)) + user = thrown.Thrower; + + Trigger(component.Owner, user); } - public void Trigger(EntityUid trigger, EntityUid user = default) + + public void Trigger(EntityUid trigger, EntityUid? user = null) { var triggerEvent = new TriggerEvent(trigger, user); EntityManager.EventBus.RaiseLocalEvent(trigger, triggerEvent); } - public void HandleTimerTrigger(TimeSpan delay, EntityUid triggered, EntityUid user = default) + public void HandleTimerTrigger(TimeSpan delay, EntityUid triggered, EntityUid? user = null) { if (delay.TotalSeconds <= 0) { diff --git a/Content.Server/Flash/FlashSystem.cs b/Content.Server/Flash/FlashSystem.cs index 20ff2cf67e..fb25b4844f 100644 --- a/Content.Server/Flash/FlashSystem.cs +++ b/Content.Server/Flash/FlashSystem.cs @@ -129,7 +129,7 @@ namespace Content.Server.Flash flashable.Dirty(); } - _stunSystem.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration/1000f), + _stunSystem.TrySlowdown(target, TimeSpan.FromSeconds(flashDuration/1000f), true, slowTo, slowTo); if (displayPopup && user != null && target != user) diff --git a/Content.Server/Fluids/Components/MopComponent.cs b/Content.Server/Fluids/Components/MopComponent.cs index 8a0acc6614..c728e27c35 100644 --- a/Content.Server/Fluids/Components/MopComponent.cs +++ b/Content.Server/Fluids/Components/MopComponent.cs @@ -1,6 +1,7 @@ using System.Threading.Tasks; using Content.Server.Chemistry.EntitySystems; using Content.Server.DoAfter; +using Content.Server.Fluids.EntitySystems; using Content.Shared.Chemistry.Components; using Content.Shared.FixedPoint; using Content.Shared.Interaction; @@ -89,6 +90,7 @@ namespace Content.Server.Fluids.Components * will spill some of the mop's solution onto the puddle which will evaporate eventually. */ var solutionSystem = EntitySystem.Get(); + var spillableSystem = EntitySystem.Get(); if (!solutionSystem.TryGetSolution(Owner, SolutionName, out var contents ) || Mopping || @@ -106,8 +108,8 @@ namespace Content.Server.Fluids.Components if (eventArgs.Target is not {Valid: true} target) { // Drop the liquid on the mop on to the ground - solutionSystem.SplitSolution(Owner, contents, FixedPoint2.Min(ResidueAmount, CurrentVolume)) - .SpillAt(eventArgs.ClickLocation, "PuddleSmear"); + var solution = solutionSystem.SplitSolution(Owner, contents, FixedPoint2.Min(ResidueAmount, CurrentVolume)); + spillableSystem.SpillAt(solution, eventArgs.ClickLocation, "PuddleSmear"); return true; } @@ -147,9 +149,9 @@ namespace Content.Server.Fluids.Components // After cleaning the puddle, make a new puddle with solution from the mop as a "wet floor". Then evaporate it slowly. // we do this WITHOUT adding to the existing puddle. Otherwise we have might have water puddles with the vomit sprite. - solutionSystem.SplitSolution(Owner, contents, transferAmount) - .SplitSolution(ResidueAmount) - .SpillAt(eventArgs.ClickLocation, "PuddleSmear", combine: false); + var splitSolution = solutionSystem.SplitSolution(Owner, contents, transferAmount) + .SplitSolution(ResidueAmount); + spillableSystem.SpillAt(splitSolution, eventArgs.ClickLocation, "PuddleSmear", combine: false); } else { diff --git a/Content.Server/Fluids/Components/SpillExtensions.cs b/Content.Server/Fluids/Components/SpillExtensions.cs deleted file mode 100644 index b79072698f..0000000000 --- a/Content.Server/Fluids/Components/SpillExtensions.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using Content.Server.Chemistry.EntitySystems; -using Content.Server.Coordinates.Helpers; -using Content.Server.Fluids.EntitySystems; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Reagent; -using Content.Shared.FixedPoint; -using Robust.Server.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Map; -using Robust.Shared.Prototypes; - -namespace Content.Server.Fluids.Components -{ - // TODO: Kill these with fire - public static class SpillExtensions - { - /// - /// Spills the specified solution at the entity's location if possible. - /// - /// - /// The entity to use as a location to spill the solution at. - /// - /// Initial solution for the prototype. - /// The prototype to use. - /// Play the spill sound. - /// The puddle if one was created, null otherwise. - /// Whether to attempt to merge with existing puddles - public static PuddleComponent? SpillAt(this Solution solution, EntityUid entity, string prototype, - bool sound = true, bool combine = true) - { - return solution.SpillAt(IoCManager.Resolve().GetComponent(entity).Coordinates, prototype, sound, combine: combine); - } - - /// - /// Spills the specified solution at the entity's location if possible. - /// - /// - /// The entity to use as a location to spill the solution at. - /// - /// Initial solution for the prototype. - /// The prototype to use. - /// The puddle if one was created, null otherwise. - /// Play the spill sound. - /// Whether to attempt to merge with existing puddles - /// True if a puddle was created, false otherwise. - public static bool TrySpillAt(this Solution solution, EntityUid entity, string prototype, - [NotNullWhen(true)] out PuddleComponent? puddle, bool sound = true, bool combine = true) - { - puddle = solution.SpillAt(entity, prototype, sound, combine: combine); - return puddle != null; - } - - /// - /// Spills solution at the specified grid coordinates. - /// - /// Initial solution for the prototype. - /// The coordinates to spill the solution at. - /// The prototype to use. - /// Whether or not to play the spill sound. - /// Whether to attempt to merge with existing puddles - /// The puddle if one was created, null otherwise. - public static PuddleComponent? SpillAt(this Solution solution, EntityCoordinates coordinates, string prototype, - bool overflow = true, bool sound = true, bool combine = true) - { - if (solution.TotalVolume == 0) return null; - - var mapManager = IoCManager.Resolve(); - var entityManager = IoCManager.Resolve(); - - if (!mapManager.TryGetGrid(coordinates.GetGridId(entityManager), out var mapGrid)) - return null; // Let's not spill to space. - - return SpillAt(mapGrid.GetTileRef(coordinates), solution, prototype, overflow, sound, combine: combine); - } - - /// - /// Spills the specified solution at the entity's location if possible. - /// - /// The coordinates to spill the solution at. - /// Initial solution for the prototype. - /// The prototype to use. - /// The puddle if one was created, null otherwise. - /// Play the spill sound. - /// Whether to attempt to merge with existing puddles - /// True if a puddle was created, false otherwise. - public static bool TrySpillAt(this Solution solution, EntityCoordinates coordinates, string prototype, - [NotNullWhen(true)] out PuddleComponent? puddle, bool sound = true, bool combine = true) - { - puddle = solution.SpillAt(coordinates, prototype, sound, combine: combine); - return puddle != null; - } - - public static bool TryGetPuddle(this TileRef tileRef, GridTileLookupSystem? gridTileLookupSystem, - [NotNullWhen(true)] out PuddleComponent? puddle) - { - foreach (var entity in tileRef.GetEntitiesInTileFast(gridTileLookupSystem)) - { - if (IoCManager.Resolve().TryGetComponent(entity, out PuddleComponent? p)) - { - puddle = p; - return true; - } - } - - puddle = null; - return false; - } - - public static PuddleComponent? SpillAt(this TileRef tileRef, Solution solution, string prototype, - bool overflow = true, bool sound = true, bool noTileReact = false, bool combine = true) - { - if (solution.TotalVolume <= 0) return null; - - // If space return early, let that spill go out into the void - if (tileRef.Tile.IsEmpty) return null; - - var mapManager = IoCManager.Resolve(); - var prototypeManager = IoCManager.Resolve(); - var serverEntityManager = IoCManager.Resolve(); - - var gridId = tileRef.GridIndex; - if (!mapManager.TryGetGrid(gridId, out var mapGrid)) return null; // Let's not spill to invalid grids. - - if (!noTileReact) - { - // First, do all tile reactions - foreach (var reagent in solution.Contents.ToArray()) - { - var proto = prototypeManager.Index(reagent.ReagentId); - proto.ReactionTile(tileRef, reagent.Quantity); - } - } - - // Tile reactions used up everything. - if (solution.CurrentVolume == FixedPoint2.Zero) - return null; - - // Get normalized co-ordinate for spill location and spill it in the centre - // TODO: Does SnapGrid or something else already do this? - var spillGridCoords = mapGrid.GridTileToWorld(tileRef.GridIndices); - - var spillEntities = IoCManager.Resolve() - .GetEntitiesIntersecting(mapGrid.ParentMapId, spillGridCoords.Position).ToArray(); - foreach (var spillEntity in spillEntities) - { - if (EntitySystem.Get() - .TryGetRefillableSolution(spillEntity, out var solutionContainerComponent)) - { - EntitySystem.Get().Refill(spillEntity, solutionContainerComponent, - solution.SplitSolution(FixedPoint2.Min( - solutionContainerComponent.AvailableVolume, - solutionContainerComponent.MaxSpillRefill)) - ); - } - } - - var puddleSystem = EntitySystem.Get(); - - if (combine) - { - foreach (var spillEntity in spillEntities) - { - if (!IoCManager.Resolve().TryGetComponent(spillEntity, out PuddleComponent? puddleComponent)) continue; - - if (!overflow && puddleSystem.WouldOverflow(puddleComponent.Owner, solution, puddleComponent)) return null; - - if (!puddleSystem.TryAddSolution(puddleComponent.Owner, solution, sound)) continue; - - return puddleComponent; - } - } - - var puddleEnt = serverEntityManager.SpawnEntity(prototype, spillGridCoords); - var newPuddleComponent = IoCManager.Resolve().GetComponent(puddleEnt); - - puddleSystem.TryAddSolution(newPuddleComponent.Owner, solution, sound); - - return newPuddleComponent; - } - } -} diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index f78866590b..f1cc0c1583 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -38,7 +38,6 @@ namespace Content.Server.Fluids.EntitySystems base.Initialize(); SubscribeLocalEvent(OnUnanchored); - SubscribeLocalEvent(AddSpillVerb); SubscribeLocalEvent(HandlePuddleExamined); SubscribeLocalEvent(OnUpdate); SubscribeLocalEvent(OnInit); @@ -88,26 +87,6 @@ namespace Content.Server.Fluids.EntitySystems } } - private void AddSpillVerb(EntityUid uid, SpillableComponent component, GetOtherVerbsEvent args) - { - if (!args.CanAccess || !args.CanInteract) - return; - - if (!_solutionContainerSystem.TryGetDrainableSolution(args.Target, out var solution)) - return; - - if (solution.DrainAvailable == FixedPoint2.Zero) - return; - - Verb verb = new(); - verb.Text = Loc.GetString("spill-target-verb-get-data-text"); - // TODO VERB ICONS spill icon? pouring out a glass/beaker? - verb.Act = () => _solutionContainerSystem.SplitSolution(args.Target, - solution, solution.DrainAvailable).SpillAt(EntityManager.GetComponent(args.Target).Coordinates, "PuddleSmear"); - verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately. - args.Verbs.Add(verb); - } - private void HandlePuddleExamined(EntityUid uid, PuddleComponent component, ExaminedEvent args) { if (EntityManager.TryGetComponent(uid, out var slippery) && slippery.Slippery) diff --git a/Content.Server/Fluids/EntitySystems/SpillableSystem.cs b/Content.Server/Fluids/EntitySystems/SpillableSystem.cs index a780346bb4..1153a0f2d6 100644 --- a/Content.Server/Fluids/EntitySystems/SpillableSystem.cs +++ b/Content.Server/Fluids/EntitySystems/SpillableSystem.cs @@ -1,12 +1,22 @@ +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using Content.Server.Administration.Logs; using Content.Server.Chemistry.EntitySystems; -using Content.Server.Construction.Components; +using Content.Server.Coordinates.Helpers; using Content.Server.Fluids.Components; -using Content.Shared.Examine; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Database; +using Content.Shared.FixedPoint; using Content.Shared.Throwing; using Content.Shared.Verbs; using JetBrains.Annotations; +using Robust.Server.GameObjects; using Robust.Shared.GameObjects; using Robust.Shared.IoC; +using Robust.Shared.Localization; +using Robust.Shared.Map; +using Robust.Shared.Prototypes; namespace Content.Server.Fluids.EntitySystems; @@ -14,20 +24,178 @@ namespace Content.Server.Fluids.EntitySystems; public class SpillableSystem : EntitySystem { [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly PuddleSystem _puddleSystem = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IEntityLookup _entityLookup = default!; + [Dependency] private readonly GridTileLookupSystem _gridTileLookupSystem = default!; + [Dependency] private readonly AdminLogSystem _logSystem = default!; - public override void Initialize() + public override void Initialize() { base.Initialize(); SubscribeLocalEvent(SpillOnLand); + SubscribeLocalEvent(AddSpillVerb); } - void SpillOnLand(EntityUid uid, SpillableComponent component, LandEvent args) { - if (args.User != null && _solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solutionComponent)) + /// + /// Spills the specified solution at the entity's location if possible. + /// + /// + /// The entity to use as a location to spill the solution at. + /// + /// Initial solution for the prototype. + /// The prototype to use. + /// Play the spill sound. + /// Whether to attempt to merge with existing puddles + /// Optional Transform component + /// The puddle if one was created, null otherwise. + public PuddleComponent? SpillAt(EntityUid uid, Solution solution, string prototype, + bool sound = true, bool combine = true, TransformComponent? transformComponent = null) + { + return !Resolve(uid, ref transformComponent, false) + ? null + : SpillAt(solution, transformComponent.Coordinates, prototype, sound: sound, combine: combine); + } + + private void SpillOnLand(EntityUid uid, SpillableComponent component, LandEvent args) + { + if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution)) return; + + if (args.User != null) { - _solutionContainerSystem - .Drain(uid, solutionComponent, solutionComponent.DrainAvailable) - .SpillAt(EntityManager.GetComponent(uid).Coordinates, "PuddleSmear"); + _logSystem.Add(LogType.Landed, + $"{EntityManager.ToPrettyString(uid)} spilled {SolutionContainerSystem.ToPrettyString(solution)} on landing"); } + + var drainedSolution = _solutionContainerSystem.Drain(uid, solution, solution.DrainAvailable); + SpillAt(drainedSolution, EntityManager.GetComponent(uid).Coordinates, "PuddleSmear"); } + private void AddSpillVerb(EntityUid uid, SpillableComponent component, GetOtherVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + if (!_solutionContainerSystem.TryGetDrainableSolution(args.Target, out var solution)) + return; + + if (solution.DrainAvailable == FixedPoint2.Zero) + return; + + Verb verb = new(); + verb.Text = Loc.GetString("spill-target-verb-get-data-text"); + // TODO VERB ICONS spill icon? pouring out a glass/beaker? + verb.Act = () => + { + var puddleSolution = _solutionContainerSystem.SplitSolution(args.Target, + solution, solution.DrainAvailable); + SpillAt(puddleSolution, Transform(args.Target).Coordinates, "PuddleSmear"); + }; + verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately. + args.Verbs.Add(verb); + } + + /// + /// Spills solution at the specified grid coordinates. + /// + /// Initial solution for the prototype. + /// The coordinates to spill the solution at. + /// The prototype to use. + /// If the puddle overflow will be calculated. Defaults to true. + /// Whether or not to play the spill sound. + /// Whether to attempt to merge with existing puddles + /// The puddle if one was created, null otherwise. + public PuddleComponent? SpillAt(Solution solution, EntityCoordinates coordinates, string prototype, + bool overflow = true, bool sound = true, bool combine = true) + { + if (solution.TotalVolume == 0) return null; + + + if (!_mapManager.TryGetGrid(coordinates.GetGridId(EntityManager), out var mapGrid)) + return null; // Let's not spill to space. + + return SpillAt(mapGrid.GetTileRef(coordinates), solution, prototype, overflow, sound, + combine: combine); + } + + public bool TryGetPuddle(TileRef tileRef, [NotNullWhen(true)] out PuddleComponent? puddle) + { + foreach (var entity in tileRef.GetEntitiesInTileFast(_gridTileLookupSystem)) + { + if (EntityManager.TryGetComponent(entity, out PuddleComponent? p)) + { + puddle = p; + return true; + } + } + + puddle = null; + return false; + } + + public PuddleComponent? SpillAt(TileRef tileRef, Solution solution, string prototype, + bool overflow = true, bool sound = true, bool noTileReact = false, bool combine = true) + { + if (solution.TotalVolume <= 0) return null; + + // If space return early, let that spill go out into the void + if (tileRef.Tile.IsEmpty) return null; + + var gridId = tileRef.GridIndex; + if (!_mapManager.TryGetGrid(gridId, out var mapGrid)) return null; // Let's not spill to invalid grids. + + if (!noTileReact) + { + // First, do all tile reactions + foreach (var (reagentId, quantity) in solution.Contents) + { + var proto = _prototypeManager.Index(reagentId); + proto.ReactionTile(tileRef, quantity); + } + } + + // Tile reactions used up everything. + if (solution.CurrentVolume == FixedPoint2.Zero) + return null; + + // Get normalized co-ordinate for spill location and spill it in the centre + // TODO: Does SnapGrid or something else already do this? + var spillGridCoords = mapGrid.GridTileToWorld(tileRef.GridIndices); + + var spillEntities = _entityLookup.GetEntitiesIntersecting(mapGrid.ParentMapId, spillGridCoords.Position).ToArray(); + foreach (var spillEntity in spillEntities) + { + if (_solutionContainerSystem.TryGetRefillableSolution(spillEntity, out var solutionContainerComponent)) + { + _solutionContainerSystem.Refill(spillEntity, solutionContainerComponent, + solution.SplitSolution(FixedPoint2.Min( + solutionContainerComponent.AvailableVolume, + solutionContainerComponent.MaxSpillRefill)) + ); + } + } + + if (combine) + { + foreach (var spillEntity in spillEntities) + { + if (!EntityManager.TryGetComponent(spillEntity, out PuddleComponent? puddleComponent)) continue; + + if (!overflow && _puddleSystem.WouldOverflow(puddleComponent.Owner, solution, puddleComponent)) + return null; + + if (!_puddleSystem.TryAddSolution(puddleComponent.Owner, solution, sound)) continue; + + return puddleComponent; + } + } + + var puddleEnt = EntityManager.SpawnEntity(prototype, spillGridCoords); + var newPuddleComponent = EntityManager.GetComponent(puddleEnt); + + _puddleSystem.TryAddSolution(newPuddleComponent.Owner, solution, sound); + + return newPuddleComponent; + } } diff --git a/Content.Server/Instruments/InstrumentSystem.cs b/Content.Server/Instruments/InstrumentSystem.cs index e84466bb6b..ed5161abe3 100644 --- a/Content.Server/Instruments/InstrumentSystem.cs +++ b/Content.Server/Instruments/InstrumentSystem.cs @@ -159,7 +159,7 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem if (instrument.InstrumentPlayer.AttachedEntity is {Valid: true} mob) { - _stunSystem.TryParalyze(mob, TimeSpan.FromSeconds(1)); + _stunSystem.TryParalyze(mob, TimeSpan.FromSeconds(1), true); instrument.Owner.PopupMessage(mob, "instrument-component-finger-cramps-max-message"); } diff --git a/Content.Server/Nutrition/Components/DrinkComponent.cs b/Content.Server/Nutrition/Components/DrinkComponent.cs index 2dfc6c495d..b6d893ae02 100644 --- a/Content.Server/Nutrition/Components/DrinkComponent.cs +++ b/Content.Server/Nutrition/Components/DrinkComponent.cs @@ -1,4 +1,3 @@ -using Content.Shared.Chemistry.Reagent; using Content.Shared.Sound; using JetBrains.Annotations; using Robust.Shared.GameObjects; @@ -7,6 +6,7 @@ using Robust.Shared.ViewVariables; using Content.Server.Nutrition.EntitySystems; using Content.Shared.FixedPoint; using Robust.Shared.Analyzers; +using System.Threading; namespace Content.Server.Nutrition.Components { @@ -50,8 +50,9 @@ namespace Content.Server.Nutrition.Components public float ForceFeedDelay = 3; /// - /// If true, this drink has some DoAfter active (someone is being force fed). + /// Token for interrupting a do-after action (e.g., force feeding). If not null, implies component is + /// currently "in use". /// - public bool InUse = false; + public CancellationTokenSource? CancelToken; } } diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Server/Nutrition/Components/FoodComponent.cs index b53a2cb183..103bd09a41 100644 --- a/Content.Server/Nutrition/Components/FoodComponent.cs +++ b/Content.Server/Nutrition/Components/FoodComponent.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using Content.Server.Chemistry.EntitySystems; using Content.Server.Nutrition.EntitySystems; using Content.Shared.FixedPoint; @@ -55,9 +56,10 @@ namespace Content.Server.Nutrition.Components public float ForceFeedDelay = 3; /// - /// If true, this food has some DoAfter active (someone is being force fed). + /// Token for interrupting a do-after action (e.g., force feeding). If not null, implies component is + /// currently "in use". /// - public bool InUse = false; + public CancellationTokenSource? CancelToken; [ViewVariables] public int UsesRemaining diff --git a/Content.Server/Nutrition/Components/IngestionBlockerComponent.cs b/Content.Server/Nutrition/Components/IngestionBlockerComponent.cs new file mode 100644 index 0000000000..6e556d0a02 --- /dev/null +++ b/Content.Server/Nutrition/Components/IngestionBlockerComponent.cs @@ -0,0 +1,26 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Analyzers; +using Robust.Shared.ViewVariables; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Server.Nutrition.EntitySystems; + +/// +/// Component that denotes a piece of clothing that blocks the mouth or otherwise prevents eating & drinking. +/// +/// +/// In the event that more head-wear & mask functionality is added (like identity systems, or raising/lowering of +/// masks), then this component might become redundant. +/// +[RegisterComponent, Friend(typeof(FoodSystem), typeof(DrinkSystem))] +public class IngestionBlockerComponent : Component +{ + public override string Name => "IngestionBlocker"; + + /// + /// Is this component currently blocking consumption. + /// + [ViewVariables(VVAccess.ReadWrite)] + [DataField("enabled")] + public bool Enabled { get; set; } = true; +} diff --git a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs index 83ec89e6fa..41e2e5bde4 100644 --- a/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/CreamPieSystem.cs @@ -1,5 +1,6 @@ using Content.Server.Chemistry.EntitySystems; using Content.Server.Fluids.Components; +using Content.Server.Fluids.EntitySystems; using Content.Server.Nutrition.Components; using Content.Server.Popups; using Content.Shared.Audio; @@ -20,6 +21,7 @@ namespace Content.Server.Nutrition.EntitySystems public class CreamPieSystem : SharedCreamPieSystem { [Dependency] private readonly SolutionContainerSystem _solutionsSystem = default!; + [Dependency] private readonly SpillableSystem _spillableSystem = default!; protected override void SplattedCreamPie(EntityUid uid, CreamPieComponent creamPie) { @@ -27,7 +29,7 @@ namespace Content.Server.Nutrition.EntitySystems if (IoCManager.Resolve().TryGetComponent(creamPie.Owner, out var foodComp) && _solutionsSystem.TryGetSolution(creamPie.Owner, foodComp.SolutionName, out var solution)) { - solution.SpillAt(creamPie.Owner, "PuddleSmear", false); + _spillableSystem.SpillAt(creamPie.Owner, solution, "PuddleSmear", false); } } diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index 7135a464c0..adacaf613a 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Chemistry.Components.SolutionManager; using Content.Server.Chemistry.EntitySystems; using Content.Server.DoAfter; using Content.Server.Fluids.Components; +using Content.Server.Fluids.EntitySystems; using Content.Server.Nutrition.Components; using Content.Server.Popups; using Content.Shared.ActionBlocker; @@ -14,6 +15,7 @@ using Content.Shared.Chemistry.Reagent; using Content.Shared.Database; using Content.Shared.Examine; using Content.Shared.FixedPoint; +using Content.Shared.Hands; using Content.Shared.Interaction; using Content.Shared.Interaction.Helpers; using Content.Shared.Nutrition.Components; @@ -42,6 +44,7 @@ namespace Content.Server.Nutrition.EntitySystems [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedAdminLogSystem _logSystem = default!; [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; + [Dependency] private readonly SpillableSystem _spillableSystem = default!; public override void Initialize() { @@ -51,12 +54,26 @@ namespace Content.Server.Nutrition.EntitySystems SubscribeLocalEvent(OnDrinkInit); SubscribeLocalEvent(HandleLand); SubscribeLocalEvent(OnUse); + SubscribeLocalEvent(OnDrinkDeselected); SubscribeLocalEvent(AfterInteract); SubscribeLocalEvent(OnExamined); SubscribeLocalEvent(OnForceDrink); SubscribeLocalEvent(OnForceDrinkCancelled); } + /// + /// If the user is currently forcing someone do drink, this cancels the attempt if they swap hands or + /// otherwise loose the item. Prevents force-feeding dual-wielding. + /// + private void OnDrinkDeselected(EntityUid uid, DrinkComponent component, HandDeselectedEvent args) + { + if (component.CancelToken != null) + { + component.CancelToken.Cancel(); + component.CancelToken = null; + } + } + public bool IsEmpty(EntityUid uid, DrinkComponent? component = null) { if(!Resolve(uid, ref component)) @@ -185,7 +202,7 @@ namespace Content.Server.Nutrition.EntitySystems UpdateAppearance(component); var solution = _solutionContainerSystem.Drain(uid, interactions, interactions.DrainAvailable); - solution.SpillAt(uid, "PuddleSmear"); + _spillableSystem.SpillAt(uid, solution, "PuddleSmear"); SoundSystem.Play(Filter.Pvs(uid), component.BurstSound.GetSound(), uid, AudioParams.Default.WithVolume(-4)); } @@ -235,6 +252,14 @@ namespace Content.Server.Nutrition.EntitySystems if (!Resolve(uid, ref drink)) return false; + // if currently being used to force-feed, cancel that action. + if (drink.CancelToken != null) + { + drink.CancelToken.Cancel(); + drink.CancelToken = null; + return true; + } + if (!drink.Opened) { _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-not-open", @@ -260,13 +285,8 @@ namespace Content.Server.Nutrition.EntitySystems return true; } - if (_foodSystem.IsMouthBlocked(userUid, out var blocker)) - { - var name = EntityManager.GetComponent(blocker.Value).EntityName; - _popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)), - userUid, Filter.Entities(userUid)); + if (_foodSystem.IsMouthBlocked(userUid, userUid)) return true; - } var transferAmount = FixedPoint2.Min(drink.TransferAmount, drinkSolution.DrainAvailable); var drain = _solutionContainerSystem.Drain(uid, drinkSolution, transferAmount); @@ -281,7 +301,7 @@ namespace Content.Server.Nutrition.EntitySystems if (EntityManager.HasComponent(uid)) { - drain.SpillAt(userUid, "PuddleSmear"); + _spillableSystem.SpillAt(userUid, drain, "PuddleSmear"); return true; } @@ -312,8 +332,12 @@ namespace Content.Server.Nutrition.EntitySystems return false; // cannot stack do-afters - if (drink.InUse) - return false; + if (drink.CancelToken != null) + { + drink.CancelToken.Cancel(); + drink.CancelToken = null; + return true; + } if (!EntityManager.HasComponent(targetUid)) return false; @@ -333,13 +357,8 @@ namespace Content.Server.Nutrition.EntitySystems return true; } - if (_foodSystem.IsMouthBlocked(targetUid, out var blocker)) - { - var name = EntityManager.GetComponent(blocker.Value).EntityName; - _popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)), - userUid, Filter.Entities(userUid)); + if (_foodSystem.IsMouthBlocked(targetUid, userUid)) return true; - } EntityManager.TryGetComponent(userUid, out MetaDataComponent? meta); var userName = meta?.EntityName ?? string.Empty; @@ -347,7 +366,8 @@ namespace Content.Server.Nutrition.EntitySystems _popupSystem.PopupEntity(Loc.GetString("drink-component-force-feed", ("user", userName)), userUid, Filter.Entities(targetUid)); - _doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, drink.ForceFeedDelay, target: targetUid) + drink.CancelToken = new(); + _doAfterSystem.DoAfter(new DoAfterEventArgs(userUid, drink.ForceFeedDelay, drink.CancelToken.Token, targetUid) { BreakOnUserMove = true, BreakOnDamage = true, @@ -355,13 +375,12 @@ namespace Content.Server.Nutrition.EntitySystems BreakOnTargetMove = true, MovementThreshold = 1.0f, TargetFinishedEvent = new ForceDrinkEvent(userUid, drink, drinkSolution), - BroadcastCancelledEvent = new ForceDrinkCancelledEvent(drink) + BroadcastCancelledEvent = new ForceDrinkCancelledEvent(drink), }); // logging _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{userUid} is forcing {targetUid} to drink {uid}"); - drink.InUse = true; return true; } @@ -370,7 +389,10 @@ namespace Content.Server.Nutrition.EntitySystems /// private void OnForceDrink(EntityUid uid, SharedBodyComponent body, ForceDrinkEvent args) { - args.Drink.InUse = false; + if (args.Drink.Deleted) + return; + + args.Drink.CancelToken = null; var transferAmount = FixedPoint2.Min(args.Drink.TransferAmount, args.DrinkSolution.DrainAvailable); var drained = _solutionContainerSystem.Drain((args.Drink).Owner, args.DrinkSolution, transferAmount); @@ -379,7 +401,7 @@ namespace Content.Server.Nutrition.EntitySystems _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-cannot-drink-other"), uid, Filter.Entities(args.User)); - drained.SpillAt(uid, "PuddleSmear"); + _spillableSystem.SpillAt(uid, drained, "PuddleSmear"); return; } @@ -392,7 +414,7 @@ namespace Content.Server.Nutrition.EntitySystems _popupSystem.PopupEntity(Loc.GetString("drink-component-try-use-drink-had-enough-other"), uid, Filter.Entities(args.User)); - drained.SpillAt(uid, "PuddleSmear"); + _spillableSystem.SpillAt(uid, drained, "PuddleSmear"); return; } @@ -416,31 +438,7 @@ namespace Content.Server.Nutrition.EntitySystems private void OnForceDrinkCancelled(ForceDrinkCancelledEvent args) { - args.Drink.InUse = false; - } - } - - public sealed class ForceDrinkEvent : EntityEventArgs - { - public readonly EntityUid User; - public readonly DrinkComponent Drink; - public readonly Solution DrinkSolution; - - public ForceDrinkEvent(EntityUid user, DrinkComponent drink, Solution drinkSolution) - { - User = user; - Drink = drink; - DrinkSolution = drinkSolution; - } - } - - public sealed class ForceDrinkCancelledEvent : EntityEventArgs - { - public readonly DrinkComponent Drink; - - public ForceDrinkCancelledEvent( DrinkComponent drink) - { - Drink = drink; + args.Drink.CancelToken = null; } } } diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs index 22c66d3685..cce1876070 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/FoodSystem.cs @@ -12,7 +12,6 @@ using Content.Server.Popups; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; using Content.Shared.Body.Components; -using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.Database; using Content.Shared.FixedPoint; @@ -27,7 +26,11 @@ using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; using Robust.Shared.Player; +using System.Collections.Generic; using Robust.Shared.Utility; +using Content.Server.Inventory.Components; +using Content.Shared.Inventory; +using Content.Shared.Hands; namespace Content.Server.Nutrition.EntitySystems { @@ -51,9 +54,24 @@ namespace Content.Server.Nutrition.EntitySystems SubscribeLocalEvent(OnUseFoodInHand); SubscribeLocalEvent(OnFeedFood); + SubscribeLocalEvent(OnFoodDeselected); SubscribeLocalEvent(AddEatVerb); SubscribeLocalEvent(OnForceFeed); SubscribeLocalEvent(OnForceFeedCancelled); + SubscribeLocalEvent(OnInventoryIngestAttempt); + } + + /// + /// If the user is currently force feeding someone, this cancels the attempt if they swap hands or otherwise + /// loose the item. Prevents force-feeding dual-wielding. + /// + private void OnFoodDeselected(EntityUid uid, FoodComponent component, HandDeselectedEvent args) + { + if (component.CancelToken != null) + { + component.CancelToken.Cancel(); + component.CancelToken = null; + } } /// @@ -119,6 +137,14 @@ namespace Content.Server.Nutrition.EntitySystems if (!Resolve(uid, ref food)) return false; + // if currently being used to force-feed, cancel that action. + if (food.CancelToken != null) + { + food.CancelToken.Cancel(); + food.CancelToken = null; + return true; + } + if (uid == user || //Suppresses self-eating EntityManager.TryGetComponent(uid, out var mobState) && mobState.IsAlive()) // Suppresses eating alive mobs return false; @@ -137,11 +163,8 @@ namespace Content.Server.Nutrition.EntitySystems !_bodySystem.TryGetComponentsOnMechanisms(user, out var stomachs, body)) return false; - if (IsMouthBlocked(user, out var blocker)) + if (IsMouthBlocked(user, user)) { - var name = EntityManager.GetComponent(blocker.Value).EntityName; - _popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)), - user, Filter.Entities(user)); return true; } @@ -218,6 +241,9 @@ namespace Content.Server.Nutrition.EntitySystems private void AddEatVerb(EntityUid uid, FoodComponent component, GetInteractionVerbsEvent ev) { + if (component.CancelToken != null) + return; + if (uid == ev.User || !ev.CanInteract || !ev.CanAccess || @@ -250,6 +276,14 @@ namespace Content.Server.Nutrition.EntitySystems if (!Resolve(uid, ref food)) return false; + // if currently being used to force-feed, cancel that action. + if (food.CancelToken != null) + { + food.CancelToken.Cancel(); + food.CancelToken = null; + return true; + } + if (!EntityManager.HasComponent(target)) return false; @@ -264,11 +298,8 @@ namespace Content.Server.Nutrition.EntitySystems return true; } - if (IsMouthBlocked(target, out var blocker)) + if (IsMouthBlocked(target, user)) { - var name = EntityManager.GetComponent(blocker.Value).EntityName; - _popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)), - user, Filter.Entities(user)); return true; } @@ -281,7 +312,8 @@ namespace Content.Server.Nutrition.EntitySystems _popupSystem.PopupEntity(Loc.GetString("food-system-force-feed", ("user", userName)), user, Filter.Entities(target)); - _doAfterSystem.DoAfter(new DoAfterEventArgs(user, food.ForceFeedDelay, target: target) + food.CancelToken = new(); + _doAfterSystem.DoAfter(new DoAfterEventArgs(user, food.ForceFeedDelay, food.CancelToken.Token, target) { BreakOnUserMove = true, BreakOnDamage = true, @@ -295,13 +327,15 @@ namespace Content.Server.Nutrition.EntitySystems // logging _logSystem.Add(LogType.ForceFeed, LogImpact.Medium, $"{user} is forcing {target} to eat {uid}"); - food.InUse = true; return true; } private void OnForceFeed(EntityUid uid, SharedBodyComponent body, ForceFeedEvent args) { - args.Food.InUse = false; + if (args.Food.Deleted) + return; + + args.Food.CancelToken = null; if (!_bodySystem.TryGetComponentsOnMechanisms(uid, out var stomachs, body)) return; @@ -361,7 +395,7 @@ namespace Content.Server.Nutrition.EntitySystems if (!Resolve(uid, ref food) || !Resolve(target, ref body, false)) return; - if (IsMouthBlocked(target, out _)) + if (IsMouthBlocked(target)) return; if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var foodSolution)) @@ -438,66 +472,57 @@ namespace Content.Server.Nutrition.EntitySystems private void OnForceFeedCancelled(ForceFeedCancelledEvent args) { - args.Food.InUse = false; + args.Food.CancelToken = null; } /// - /// Is an entity's mouth accessible, or is it blocked by something like a mask? Does not actually check if - /// the user has a mouth. Body system when? + /// Block ingestion attempts based on the equipped mask or head-wear /// - public bool IsMouthBlocked(EntityUid uid, [NotNullWhen(true)] out EntityUid? blockingEntity, - InventoryComponent? inventory = null) + private void OnInventoryIngestAttempt(EntityUid uid, InventoryComponent component, IngestionAttemptEvent args) { - blockingEntity = null; + if (args.Cancelled) + return; - if (!Resolve(uid, ref inventory, false)) - return false; + IngestionBlockerComponent blocker; - // check masks - if (inventory.TryGetSlotItem(EquipmentSlotDefines.Slots.MASK, out ItemComponent? mask)) + if (component.TryGetSlotItem(EquipmentSlotDefines.Slots.MASK, out ItemComponent? mask) && + EntityManager.TryGetComponent(mask.Owner, out blocker) && + blocker.Enabled) { - // For now, lets just assume that any masks always covers the mouth - // TODO MASKS if the ability is added to raise/lower masks, this needs to be updated. - blockingEntity = mask.Owner; - return true; + args.Blocker = mask.Owner; + args.Cancel(); + return; } - // check helmets. Note that not all helmets cover the face. - if (inventory.TryGetSlotItem(EquipmentSlotDefines.Slots.HEAD, out ItemComponent? head) && - EntityManager.TryGetComponent(((IComponent) head).Owner, out TagComponent tag) && - tag.HasTag("ConcealsFace")) + if (component.TryGetSlotItem(EquipmentSlotDefines.Slots.HEAD, out ItemComponent? head) && + EntityManager.TryGetComponent(head.Owner, out blocker) && + blocker.Enabled) { - blockingEntity = head.Owner; - return true; + args.Blocker = head.Owner; + args.Cancel(); + } + } + + + /// + /// Check whether the target's mouth is blocked by equipment (masks or head-wear). + /// + /// The target whose equipment is checked + /// Optional entity that will receive an informative pop-up identifying the blocking + /// piece of equipment. + /// + public bool IsMouthBlocked(EntityUid uid, EntityUid? popupUid = null) + { + var attempt = new IngestionAttemptEvent(); + RaiseLocalEvent(uid, attempt, false); + if (attempt.Cancelled && attempt.Blocker != null && popupUid != null) + { + var name = EntityManager.GetComponent(attempt.Blocker.Value).EntityName; + _popupSystem.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", name)), + uid, Filter.Entities(popupUid.Value)); } - return false; - } - } - - public sealed class ForceFeedEvent : EntityEventArgs - { - public readonly EntityUid User; - public readonly FoodComponent Food; - public readonly Solution FoodSolution; - public readonly List Utensils; - - public ForceFeedEvent(EntityUid user, FoodComponent food, Solution foodSolution, List utensils) - { - User = user; - Food = food; - FoodSolution = foodSolution; - Utensils = utensils; - } - } - - public sealed class ForceFeedCancelledEvent : EntityEventArgs - { - public readonly FoodComponent Food; - - public ForceFeedCancelledEvent(FoodComponent food) - { - Food = food; + return attempt.Cancelled; } } } diff --git a/Content.Server/Nutrition/IngestionEvents.cs b/Content.Server/Nutrition/IngestionEvents.cs new file mode 100644 index 0000000000..1db57ba3b3 --- /dev/null +++ b/Content.Server/Nutrition/IngestionEvents.cs @@ -0,0 +1,79 @@ +using Content.Server.Nutrition.Components; +using Content.Shared.Chemistry.Components; +using Robust.Shared.GameObjects; +using System.Collections.Generic; + +namespace Content.Server.Nutrition; + +/// +/// Raised directed at the consumer when attempting to ingest something. +/// +public sealed class IngestionAttemptEvent : CancellableEntityEventArgs +{ + /// + /// The equipment that is blocking consumption. Should only be non-null if the event was canceled. + /// + public EntityUid? Blocker = null; +} + +/// +/// Raised directed at the food after a successful force-feed do-after. +/// +public sealed class ForceFeedEvent : EntityEventArgs +{ + public readonly EntityUid User; + public readonly FoodComponent Food; + public readonly Solution FoodSolution; + public readonly List Utensils; + + public ForceFeedEvent(EntityUid user, FoodComponent food, Solution foodSolution, List utensils) + { + User = user; + Food = food; + FoodSolution = foodSolution; + Utensils = utensils; + } +} + +/// +/// Raised directed at the food after a failed force-feed do-after. +/// +public sealed class ForceFeedCancelledEvent : EntityEventArgs +{ + public readonly FoodComponent Food; + + public ForceFeedCancelledEvent(FoodComponent food) + { + Food = food; + } +} + +/// +/// Raised directed at the drink after a successful force-drink do-after. +/// +public sealed class ForceDrinkEvent : EntityEventArgs +{ + public readonly EntityUid User; + public readonly DrinkComponent Drink; + public readonly Solution DrinkSolution; + + public ForceDrinkEvent(EntityUid user, DrinkComponent drink, Solution drinkSolution) + { + User = user; + Drink = drink; + DrinkSolution = drinkSolution; + } +} + +/// +/// Raised directed at the food after a failed force-dink do-after. +/// +public sealed class ForceDrinkCancelledEvent : EntityEventArgs +{ + public readonly DrinkComponent Drink; + + public ForceDrinkCancelledEvent(DrinkComponent drink) + { + Drink = drink; + } +} diff --git a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs index 5a14957b0d..0fa7407afe 100644 --- a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs +++ b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs @@ -242,7 +242,8 @@ namespace Content.Server.PneumaticCannon if(EntityManager.TryGetComponent(data.User, out var status) && comp.Power == PneumaticCannonPower.High) { - _stun.TryParalyze(data.User, TimeSpan.FromSeconds(comp.HighPowerStunTime), status); + _stun.TryParalyze(data.User, TimeSpan.FromSeconds(comp.HighPowerStunTime), true, status); + data.User.PopupMessage(Loc.GetString("pneumatic-cannon-component-power-stun", ("cannon", comp.Owner))); } diff --git a/Content.Server/Speech/EntitySystems/StutteringSystem.cs b/Content.Server/Speech/EntitySystems/StutteringSystem.cs index fdf3e8a286..48e3df69a9 100644 --- a/Content.Server/Speech/EntitySystems/StutteringSystem.cs +++ b/Content.Server/Speech/EntitySystems/StutteringSystem.cs @@ -28,13 +28,13 @@ namespace Content.Server.Speech.EntitySystems SubscribeLocalEvent(OnAccent); } - public override void DoStutter(EntityUid uid, TimeSpan time, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null) + public override void DoStutter(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null) { if (!Resolve(uid, ref status, false)) return; if (!_statusEffectsSystem.HasStatusEffect(uid, StutterKey, status)) - _statusEffectsSystem.TryAddStatusEffect(uid, StutterKey, time, status, alerts); + _statusEffectsSystem.TryAddStatusEffect(uid, StutterKey, time, refresh, status, alerts); else _statusEffectsSystem.TryAddTime(uid, StutterKey, time, status); } diff --git a/Content.Server/Storage/Components/EntityStorageComponent.cs b/Content.Server/Storage/Components/EntityStorageComponent.cs index a227f78874..72e58cea4b 100644 --- a/Content.Server/Storage/Components/EntityStorageComponent.cs +++ b/Content.Server/Storage/Components/EntityStorageComponent.cs @@ -365,18 +365,12 @@ namespace Content.Server.Storage.Components // Trying to add while open just dumps it on the ground below us. if (Open) { - IoCManager.Resolve().GetComponent(entity).WorldPosition = IoCManager.Resolve().GetComponent(Owner).WorldPosition; + var entMan = IoCManager.Resolve(); + entMan.GetComponent(entity).WorldPosition = entMan.GetComponent(Owner).WorldPosition; return true; } - if (!Contents.Insert(entity)) return false; - - IoCManager.Resolve().GetComponent(entity).LocalPosition = Vector2.Zero; - if (IoCManager.Resolve().TryGetComponent(entity, out IPhysBody? body)) - { - body.CanCollide = false; - } - return true; + return Contents.Insert(entity); } /// diff --git a/Content.Server/Storage/Components/IStorageComponent.cs b/Content.Server/Storage/Components/IStorageComponent.cs index 85f560b68b..52f0388479 100644 --- a/Content.Server/Storage/Components/IStorageComponent.cs +++ b/Content.Server/Storage/Components/IStorageComponent.cs @@ -2,7 +2,7 @@ using Robust.Shared.GameObjects; namespace Content.Server.Storage.Components { - public interface IStorageComponent + public interface IStorageComponent : IComponent { bool Remove(EntityUid entity); bool Insert(EntityUid entity); diff --git a/Content.Server/Storage/Components/StorageFillComponent.cs b/Content.Server/Storage/Components/StorageFillComponent.cs index c166356710..3e01d3f707 100644 --- a/Content.Server/Storage/Components/StorageFillComponent.cs +++ b/Content.Server/Storage/Components/StorageFillComponent.cs @@ -48,10 +48,18 @@ namespace Content.Server.Storage.Components continue; } + var entMan = IoCManager.Resolve(); + var transform = entMan.GetComponent(Owner); + for (var i = 0; i < storageItem.Amount; i++) { - storage.Insert( - IoCManager.Resolve().SpawnEntity(storageItem.PrototypeId, IoCManager.Resolve().GetComponent(Owner).Coordinates)); + + var ent = entMan.SpawnEntity(storageItem.PrototypeId, transform.Coordinates); + + if (storage.Insert(ent)) continue; + + Logger.ErrorS("storage", $"Tried to StorageFill {storageItem.PrototypeId} inside {Owner} but can't."); + entMan.DeleteEntity(ent); } if (!string.IsNullOrEmpty(storageItem.GroupId)) alreadySpawnedGroups.Add(storageItem.GroupId); diff --git a/Content.Server/Stunnable/StunOnCollideSystem.cs b/Content.Server/Stunnable/StunOnCollideSystem.cs index 026d5fa2f5..e88fae7ac0 100644 --- a/Content.Server/Stunnable/StunOnCollideSystem.cs +++ b/Content.Server/Stunnable/StunOnCollideSystem.cs @@ -34,12 +34,12 @@ namespace Content.Server.Stunnable // Let the actual methods log errors for these. Resolve(otherUid, ref alerts, ref standingState, ref appearance, false); - _stunSystem.TryStun(otherUid, TimeSpan.FromSeconds(component.StunAmount), status, alerts); + _stunSystem.TryStun(otherUid, TimeSpan.FromSeconds(component.StunAmount), true, status, alerts); - _stunSystem.TryKnockdown(otherUid, TimeSpan.FromSeconds(component.KnockdownAmount), + _stunSystem.TryKnockdown(otherUid, TimeSpan.FromSeconds(component.KnockdownAmount), true, status, alerts); - _stunSystem.TrySlowdown(otherUid, TimeSpan.FromSeconds(component.SlowdownAmount), + _stunSystem.TrySlowdown(otherUid, TimeSpan.FromSeconds(component.SlowdownAmount), true, component.WalkSpeedMultiplier, component.RunSpeedMultiplier, status, alerts); } } diff --git a/Content.Server/Stunnable/StunSystem.cs b/Content.Server/Stunnable/StunSystem.cs index 7c7e074875..2fa753c498 100644 --- a/Content.Server/Stunnable/StunSystem.cs +++ b/Content.Server/Stunnable/StunSystem.cs @@ -34,7 +34,7 @@ namespace Content.Server.Stunnable if (args.Handled || !_random.Prob(args.PushProbability)) return; - if (!TryParalyze(uid, TimeSpan.FromSeconds(4f), status)) + if (!TryParalyze(uid, TimeSpan.FromSeconds(4f), true, status)) return; var source = args.Source; diff --git a/Content.Server/Stunnable/StunbatonSystem.cs b/Content.Server/Stunnable/StunbatonSystem.cs index 1225c293e5..076efcb646 100644 --- a/Content.Server/Stunnable/StunbatonSystem.cs +++ b/Content.Server/Stunnable/StunbatonSystem.cs @@ -120,7 +120,7 @@ namespace Content.Server.Stunnable private void StunEntity(EntityUid entity, StunbatonComponent comp) { - if (!IoCManager.Resolve().TryGetComponent(entity, out StatusEffectsComponent? status) || !comp.Activated) return; + if (!EntityManager.TryGetComponent(entity, out StatusEffectsComponent? status) || !comp.Activated) return; // TODO: Make slowdown inflicted customizable. @@ -128,23 +128,23 @@ namespace Content.Server.Stunnable if (!EntityManager.HasComponent(entity)) { if (_robustRandom.Prob(comp.ParalyzeChanceNoSlowdown)) - _stunSystem.TryParalyze(entity, TimeSpan.FromSeconds(comp.ParalyzeTime), status); + _stunSystem.TryParalyze(entity, TimeSpan.FromSeconds(comp.ParalyzeTime), true, status); else - _stunSystem.TrySlowdown(entity, TimeSpan.FromSeconds(comp.SlowdownTime), 0.5f, 0.5f, status); + _stunSystem.TrySlowdown(entity, TimeSpan.FromSeconds(comp.SlowdownTime), true, 0.5f, 0.5f, status); } else { if (_robustRandom.Prob(comp.ParalyzeChanceWithSlowdown)) - _stunSystem.TryParalyze(entity, TimeSpan.FromSeconds(comp.ParalyzeTime), status); + _stunSystem.TryParalyze(entity, TimeSpan.FromSeconds(comp.ParalyzeTime), true, status); else - _stunSystem.TrySlowdown(entity, TimeSpan.FromSeconds(comp.SlowdownTime), 0.5f, 0.5f, status); + _stunSystem.TrySlowdown(entity, TimeSpan.FromSeconds(comp.SlowdownTime), true, 0.5f, 0.5f, status); } var slowdownTime = TimeSpan.FromSeconds(comp.SlowdownTime); - _jitterSystem.DoJitter(entity, slowdownTime, status:status); - _stutteringSystem.DoStutter(entity, slowdownTime, status); + _jitterSystem.DoJitter(entity, slowdownTime, true, status:status); + _stutteringSystem.DoStutter(entity, slowdownTime, true, status); - if (!IoCManager.Resolve().TryGetComponent(comp.Owner, out var slot) || slot.Cell == null || !(slot.Cell.CurrentCharge < comp.EnergyPerUse)) + if (!EntityManager.TryGetComponent(comp.Owner, out var slot) || slot.Cell == null || !(slot.Cell.CurrentCharge < comp.EnergyPerUse)) return; SoundSystem.Play(Filter.Pvs(comp.Owner), comp.SparksSound.GetSound(), comp.Owner, AudioHelpers.WithVariation(0.25f)); @@ -158,8 +158,8 @@ namespace Content.Server.Stunnable return; } - if (!IoCManager.Resolve().TryGetComponent(comp.Owner, out var sprite) || - !IoCManager.Resolve().TryGetComponent(comp.Owner, out var item)) return; + if (!EntityManager.TryGetComponent(comp.Owner, out var sprite) || + !EntityManager.TryGetComponent(comp.Owner, out var item)) return; SoundSystem.Play(Filter.Pvs(comp.Owner), comp.SparksSound.GetSound(), comp.Owner, AudioHelpers.WithVariation(0.25f)); item.EquippedPrefix = "off"; diff --git a/Content.Server/Throwing/ThrowHelper.cs b/Content.Server/Throwing/ThrowHelper.cs index 2f36b4b76f..c962a2ac40 100644 --- a/Content.Server/Throwing/ThrowHelper.cs +++ b/Content.Server/Throwing/ThrowHelper.cs @@ -30,8 +30,8 @@ namespace Content.Server.Throwing /// A vector pointing from the entity to its destination. /// How much the direction vector should be multiplied for velocity. /// - /// The ratio of impulse applied to the thrower - internal static void TryThrow(this EntityUid entity, Vector2 direction, float strength = 1.0f, EntityUid? user = null, float pushbackRatio = 1.0f) + /// The ratio of impulse applied to the thrower - defaults to 10 because otherwise it's not enough to properly recover from getting spaced + internal static void TryThrow(this EntityUid entity, Vector2 direction, float strength = 1.0f, EntityUid? user = null, float pushbackRatio = 10.0f) { var entities = IoCManager.Resolve(); if (entities.GetComponent(entity).EntityDeleted || diff --git a/Content.Server/Weapon/Ranged/Barrels/BarrelSystem.cs b/Content.Server/Weapon/Ranged/Barrels/BarrelSystem.cs index 1f3f069b14..60013c7361 100644 --- a/Content.Server/Weapon/Ranged/Barrels/BarrelSystem.cs +++ b/Content.Server/Weapon/Ranged/Barrels/BarrelSystem.cs @@ -3,9 +3,11 @@ using Content.Server.Weapon.Ranged.Barrels.Components; using Content.Shared.ActionBlocker; using Content.Shared.Popups; using Content.Shared.Verbs; +using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Localization; +using System; namespace Content.Server.Weapon.Ranged.Barrels { @@ -19,8 +21,8 @@ namespace Content.Server.Weapon.Ranged.Barrels SubscribeLocalEvent(AddSpinVerb); - SubscribeLocalEvent(AddEjectCellVerb); - SubscribeLocalEvent(AddInsertCellVerb); + SubscribeLocalEvent(OnCellSlotUpdated); + SubscribeLocalEvent(OnCellSlotUpdated); SubscribeLocalEvent(AddToggleBoltVerb); @@ -28,6 +30,12 @@ namespace Content.Server.Weapon.Ranged.Barrels SubscribeLocalEvent(AddEjectMagazineVerb); } + private void OnCellSlotUpdated(EntityUid uid, ServerBatteryBarrelComponent component, ContainerModifiedMessage args) + { + if (args.Container.ID == component.CellSlot.ID) + component.UpdateAppearance(); + } + private void AddSpinVerb(EntityUid uid, RevolverBarrelComponent component, GetAlternativeVerbsEvent args) { if (args.Hands == null || !args.CanAccess || !args.CanInteract) @@ -62,44 +70,6 @@ namespace Content.Server.Weapon.Ranged.Barrels args.Verbs.Add(verb); } - // TODO VERBS EJECTABLES Standardize eject/insert verbs into a single system? - // Really, why isn't this just PowerCellSlotComponent? - private void AddEjectCellVerb(EntityUid uid, ServerBatteryBarrelComponent component, GetAlternativeVerbsEvent args) - { - if (args.Hands == null || - !args.CanAccess || - !args.CanInteract || - !component.PowerCellRemovable || - component.PowerCell == null || - !_actionBlockerSystem.CanPickup(args.User)) - return; - - Verb verb = new() - { - Text = EntityManager.GetComponent(component.PowerCell.Owner).EntityName, - Category = VerbCategory.Eject, - Act = () => component.TryEjectCell(args.User) - }; - args.Verbs.Add(verb); - } - - private void AddInsertCellVerb(EntityUid uid, ServerBatteryBarrelComponent component, GetInteractionVerbsEvent args) - { - if (args.Using is not {Valid: true} @using || - !args.CanAccess || - !args.CanInteract || - component.PowerCell != null || - !EntityManager.HasComponent(@using) || - !_actionBlockerSystem.CanDrop(args.User)) - return; - - Verb verb = new(); - verb.Text = EntityManager.GetComponent(@using).EntityName; - verb.Category = VerbCategory.Insert; - verb.Act = () => component.TryInsertPowerCell(@using); - args.Verbs.Add(verb); - } - private void AddEjectMagazineVerb(EntityUid uid, ServerMagazineBarrelComponent component, GetAlternativeVerbsEvent args) { if (args.Hands == null || diff --git a/Content.Server/Weapon/Ranged/Barrels/Components/BoltActionBarrelComponent.cs b/Content.Server/Weapon/Ranged/Barrels/Components/BoltActionBarrelComponent.cs index 7f23503c54..dc6012d76b 100644 --- a/Content.Server/Weapon/Ranged/Barrels/Components/BoltActionBarrelComponent.cs +++ b/Content.Server/Weapon/Ranged/Barrels/Components/BoltActionBarrelComponent.cs @@ -26,7 +26,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components [RegisterComponent] [NetworkedComponent()] #pragma warning disable 618 - public sealed class BoltActionBarrelComponent : ServerRangedBarrelComponent, IMapInit, IExamine + public sealed class BoltActionBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, IMapInit, IExamine #pragma warning restore 618 { // Originally I had this logic shared with PumpBarrel and used a couple of variables to control things @@ -270,7 +270,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components return false; } - public override bool UseEntity(UseEntityEventArgs eventArgs) + public bool UseEntity(UseEntityEventArgs eventArgs) { if (BoltOpen) { @@ -284,7 +284,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components return true; } - public override async Task InteractUsing(InteractUsingEventArgs eventArgs) + public async Task InteractUsing(InteractUsingEventArgs eventArgs) { return TryInsertBullet(eventArgs.User, eventArgs.Using); } diff --git a/Content.Server/Weapon/Ranged/Barrels/Components/PumpBarrelComponent.cs b/Content.Server/Weapon/Ranged/Barrels/Components/PumpBarrelComponent.cs index 8daffb7ace..fe4c146299 100644 --- a/Content.Server/Weapon/Ranged/Barrels/Components/PumpBarrelComponent.cs +++ b/Content.Server/Weapon/Ranged/Barrels/Components/PumpBarrelComponent.cs @@ -25,7 +25,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components /// [RegisterComponent] [NetworkedComponent()] - public sealed class PumpBarrelComponent : ServerRangedBarrelComponent, IMapInit, ISerializationHooks + public sealed class PumpBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, IMapInit, ISerializationHooks { public override string Name => "PumpBarrel"; @@ -224,13 +224,13 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components return false; } - public override bool UseEntity(UseEntityEventArgs eventArgs) + public bool UseEntity(UseEntityEventArgs eventArgs) { Cycle(true); return true; } - public override async Task InteractUsing(InteractUsingEventArgs eventArgs) + public async Task InteractUsing(InteractUsingEventArgs eventArgs) { return TryInsertBullet(eventArgs); } diff --git a/Content.Server/Weapon/Ranged/Barrels/Components/RevolverBarrelComponent.cs b/Content.Server/Weapon/Ranged/Barrels/Components/RevolverBarrelComponent.cs index 236764bfa9..09ca4e45c6 100644 --- a/Content.Server/Weapon/Ranged/Barrels/Components/RevolverBarrelComponent.cs +++ b/Content.Server/Weapon/Ranged/Barrels/Components/RevolverBarrelComponent.cs @@ -22,7 +22,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components { [RegisterComponent] [NetworkedComponent()] - public sealed class RevolverBarrelComponent : ServerRangedBarrelComponent, ISerializationHooks + public sealed class RevolverBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, ISerializationHooks { [Dependency] private readonly IRobustRandom _random = default!; @@ -251,7 +251,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components /// /// /// - public override bool UseEntity(UseEntityEventArgs eventArgs) + public bool UseEntity(UseEntityEventArgs eventArgs) { EjectAllSlots(); Dirty(); @@ -259,7 +259,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components return true; } - public override async Task InteractUsing(InteractUsingEventArgs eventArgs) + public async Task InteractUsing(InteractUsingEventArgs eventArgs) { return TryInsertBullet(eventArgs.User, eventArgs.Using); } diff --git a/Content.Server/Weapon/Ranged/Barrels/Components/ServerBatteryBarrelComponent.cs b/Content.Server/Weapon/Ranged/Barrels/Components/ServerBatteryBarrelComponent.cs index bd7ebb5cab..c0c9445540 100644 --- a/Content.Server/Weapon/Ranged/Barrels/Components/ServerBatteryBarrelComponent.cs +++ b/Content.Server/Weapon/Ranged/Barrels/Components/ServerBatteryBarrelComponent.cs @@ -1,19 +1,15 @@ using System; -using System.Threading.Tasks; -using Content.Server.Hands.Components; -using Content.Server.Items; using Content.Server.Power.Components; using Content.Server.Projectiles.Components; -using Content.Shared.Interaction; -using Content.Shared.Sound; +using Content.Shared.Containers.ItemSlots; using Content.Shared.Weapons.Ranged.Barrels.Components; -using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Player; +using Robust.Shared.Players; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; @@ -27,6 +23,9 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components public override string Name => "BatteryBarrel"; + [DataField("cellSlot", required: true)] + public ItemSlot CellSlot = new(); + // The minimum change we need before we can fire [DataField("lowerChargeLimit")] [ViewVariables] private float _lowerChargeLimit = 10; @@ -36,23 +35,14 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components [DataField("ammoPrototype")] [ViewVariables] private string? _ammoPrototype; - [ViewVariables] public EntityUid? PowerCellEntity => _powerCellContainer.ContainedEntity; - public BatteryComponent? PowerCell => _powerCellContainer.ContainedEntity == null - ? null - : _entities.GetComponentOrNull(_powerCellContainer.ContainedEntity.Value); - - private ContainerSlot _powerCellContainer = default!; + public BatteryComponent? PowerCell => _entities.GetComponentOrNull(CellSlot.Item); private ContainerSlot _ammoContainer = default!; - [DataField("powerCellPrototype")] - private string? _powerCellPrototype; - [DataField("powerCellRemovable")] - [ViewVariables] public bool PowerCellRemovable; public override int ShotsLeft { get { - if (_powerCellContainer.ContainedEntity is not {Valid: true} powerCell) + if (CellSlot.Item is not {} powerCell) { return 0; } @@ -65,7 +55,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components { get { - if (_powerCellContainer.ContainedEntity is not {Valid: true} powerCell) + if (CellSlot.Item is not {} powerCell) { return 0; } @@ -76,12 +66,6 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components private AppearanceComponent? _appearanceComponent; - // Sounds - [DataField("soundPowerCellInsert", required: true)] - private SoundSpecifier _soundPowerCellInsert = default!; - [DataField("soundPowerCellEject", required: true)] - private SoundSpecifier _soundPowerCellEject = default!; - public override ComponentState GetComponentState() { (int, int)? count = (ShotsLeft, Capacity); @@ -94,12 +78,8 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components protected override void Initialize() { base.Initialize(); - _powerCellContainer = Owner.EnsureContainer($"{Name}-powercell-container", out var existing); - if (!existing && _powerCellPrototype != null) - { - var powerCellEntity = _entities.SpawnEntity(_powerCellPrototype, _entities.GetComponent(Owner).Coordinates); - _powerCellContainer.Insert(powerCellEntity); - } + + EntitySystem.Get().AddItemSlot(Owner, $"{Name}-powercell-container", CellSlot); if (_ammoPrototype != null) { @@ -113,6 +93,12 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components Dirty(); } + protected override void OnRemove() + { + base.OnRemove(); + EntitySystem.Get().RemoveItemSlot(Owner, CellSlot); + } + protected override void Startup() { base.Startup(); @@ -121,7 +107,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components public void UpdateAppearance() { - _appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, _powerCellContainer.ContainedEntity != null); + _appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, CellSlot.HasItem); _appearanceComponent?.SetData(AmmoVisuals.AmmoCount, ShotsLeft); _appearanceComponent?.SetData(AmmoVisuals.AmmoMax, Capacity); Dirty(); @@ -143,7 +129,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components public override EntityUid? TakeProjectile(EntityCoordinates spawnAt) { - var powerCellEntity = _powerCellContainer.ContainedEntity; + var powerCellEntity = CellSlot.Item; if (powerCellEntity == null) { @@ -198,76 +184,5 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components UpdateAppearance(); return entity.Value; } - - public bool TryInsertPowerCell(EntityUid entity) - { - if (_powerCellContainer.ContainedEntity != null) - { - return false; - } - - if (!_entities.HasComponent(entity)) - { - return false; - } - - SoundSystem.Play(Filter.Pvs(Owner), _soundPowerCellInsert.GetSound(), Owner, AudioParams.Default.WithVolume(-2)); - - _powerCellContainer.Insert(entity); - - Dirty(); - UpdateAppearance(); - return true; - } - - public override bool UseEntity(UseEntityEventArgs eventArgs) - { - if (!PowerCellRemovable) - { - return false; - } - - return PowerCellEntity != default && TryEjectCell(eventArgs.User); - } - - public bool TryEjectCell(EntityUid user) - { - if (PowerCell == null || !PowerCellRemovable) - { - return false; - } - - if (!_entities.TryGetComponent(user, out HandsComponent? hands)) - { - return false; - } - - var cell = PowerCell; - if (!_powerCellContainer.Remove(cell.Owner)) - { - return false; - } - - Dirty(); - UpdateAppearance(); - - if (!hands.PutInHand(_entities.GetComponent(cell.Owner))) - { - _entities.GetComponent(cell.Owner).Coordinates = _entities.GetComponent(user).Coordinates; - } - - SoundSystem.Play(Filter.Pvs(Owner), _soundPowerCellEject.GetSound(), Owner, AudioParams.Default.WithVolume(-2)); - return true; - } - - public override async Task InteractUsing(InteractUsingEventArgs eventArgs) - { - if (!_entities.HasComponent(eventArgs.Using)) - { - return false; - } - - return TryInsertPowerCell(eventArgs.Using); - } } } diff --git a/Content.Server/Weapon/Ranged/Barrels/Components/ServerMagazineBarrelComponent.cs b/Content.Server/Weapon/Ranged/Barrels/Components/ServerMagazineBarrelComponent.cs index ef7be5aa9d..7e0a30b51f 100644 --- a/Content.Server/Weapon/Ranged/Barrels/Components/ServerMagazineBarrelComponent.cs +++ b/Content.Server/Weapon/Ranged/Barrels/Components/ServerMagazineBarrelComponent.cs @@ -27,7 +27,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components [RegisterComponent] [NetworkedComponent()] #pragma warning disable 618 - public sealed class ServerMagazineBarrelComponent : ServerRangedBarrelComponent, IExamine + public sealed class ServerMagazineBarrelComponent : ServerRangedBarrelComponent, IUse, IInteractUsing, IExamine #pragma warning restore 618 { [Dependency] private readonly IEntityManager _entities = default!; @@ -248,7 +248,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components _appearanceComponent?.SetData(AmmoVisuals.AmmoMax, Capacity); } - public override bool UseEntity(UseEntityEventArgs eventArgs) + public bool UseEntity(UseEntityEventArgs eventArgs) { // Behavior: // If bolt open just close it @@ -391,7 +391,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components UpdateAppearance(); } - public override async Task InteractUsing(InteractUsingEventArgs eventArgs) + public async Task InteractUsing(InteractUsingEventArgs eventArgs) { if (CanInsertMagazine(eventArgs.User, eventArgs.Using, quiet: false)) { diff --git a/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs b/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs index f79374e3fe..dd83b75198 100644 --- a/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs +++ b/Content.Server/Weapon/Ranged/Barrels/Components/ServerRangedBarrelComponent.cs @@ -35,7 +35,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components /// Only difference between them is how they retrieve a projectile to shoot (battery, magazine, etc.) /// #pragma warning disable 618 - public abstract class ServerRangedBarrelComponent : SharedRangedBarrelComponent, IUse, IInteractUsing, IExamine, ISerializationHooks + public abstract class ServerRangedBarrelComponent : SharedRangedBarrelComponent, IExamine, ISerializationHooks #pragma warning restore 618 { // There's still some of py01 and PJB's work left over, especially in underlying shooting logic, @@ -133,9 +133,9 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components } } - protected override void OnAdd() + protected override void Initialize() { - base.OnAdd(); + base.Initialize(); Owner.EnsureComponentWarn(out ServerRangedWeaponComponent rangedWeaponComponent); @@ -167,10 +167,6 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components return angle; } - public abstract bool UseEntity(UseEntityEventArgs eventArgs); - - public abstract Task InteractUsing(InteractUsingEventArgs eventArgs); - public void ChangeFireSelector(FireRateSelector rateSelector) { if ((rateSelector & AllRateSelectors) != 0) diff --git a/Content.Server/Weapon/Ranged/ServerRangedWeaponComponent.cs b/Content.Server/Weapon/Ranged/ServerRangedWeaponComponent.cs index 0a68f8c03c..ae2cdb74a7 100644 --- a/Content.Server/Weapon/Ranged/ServerRangedWeaponComponent.cs +++ b/Content.Server/Weapon/Ranged/ServerRangedWeaponComponent.cs @@ -171,7 +171,7 @@ namespace Content.Server.Weapon.Ranged { //Wound them EntitySystem.Get().TryChangeDamage(user, ClumsyDamage); - EntitySystem.Get().TryParalyze(user, TimeSpan.FromSeconds(3f)); + EntitySystem.Get().TryParalyze(user, TimeSpan.FromSeconds(3f), true); // Apply salt to the wound ("Honk!") SoundSystem.Play( diff --git a/Content.Shared.Database/LogType.cs b/Content.Shared.Database/LogType.cs index e6a5fe9bed..a86db2afcd 100644 --- a/Content.Shared.Database/LogType.cs +++ b/Content.Shared.Database/LogType.cs @@ -1,4 +1,4 @@ -namespace Content.Shared.Database; +namespace Content.Shared.Database; // DO NOT CHANGE THE NUMERIC VALUES OF THESE public enum LogType @@ -40,12 +40,13 @@ public enum LogType Pickup = 36, Drop = 37, BulletHit = 38, - ForceFeed = 40, + ForceFeed = 40, // involuntary + Ingestion = 53, // voluntary MeleeHit = 41, HitScanHit = 42, Suicide = 43, Explosion = 44, - Radiation = 45, + Radiation = 45, // Unused Barotrauma = 46, Flammable = 47, Asphyxiation = 48, diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs index 3986dfbfd4..a456db86aa 100644 --- a/Content.Shared/CCVar/CCVars.cs +++ b/Content.Shared/CCVar/CCVars.cs @@ -300,12 +300,13 @@ namespace Content.Shared.CCVar CVarDef.Create("hud.fps_counter_visible", false, CVar.CLIENTONLY | CVar.ARCHIVE); /* - * AI + * NPCs */ - public static readonly CVarDef AIMaxUpdates = - CVarDef.Create("ai.maxupdates", 64); + public static readonly CVarDef NPCMaxUpdates = + CVarDef.Create("npc.max_updates", 64); + public static readonly CVarDef NPCEnabled = CVarDef.Create("npc.enabled", true); /* * Net diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs index 6b37f44178..95a32b5a10 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsComponent.cs @@ -1,6 +1,7 @@ using Content.Shared.Sound; using Content.Shared.Whitelist; using Robust.Shared.Analyzers; +using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Prototypes; @@ -81,6 +82,12 @@ namespace Content.Shared.Containers.ItemSlots public SoundSpecifier? EjectSound; // maybe default to /Audio/Machines/id_swipe.ogg? + /// + /// Options used for playing the insert/eject sounds. + /// + [DataField("soundOptions")] + public AudioParams SoundOptions = AudioParams.Default; + /// /// The name of this item slot. This will be shown to the user in the verb menu. /// @@ -116,6 +123,18 @@ namespace Content.Shared.Containers.ItemSlots [DataField("ejectOnInteract")] public bool EjectOnInteract = false; + /// + /// If true, and if this slot is attached to an item, then it will attempt to eject slot when to the slot is + /// used in the user's hands. + /// + /// + /// Desirable for things like ranged weapons ('Z' to eject), but not desirable for others (e.g., PDA uses + /// 'Z' to open UI). Unlike , this will not make any changes to the context + /// menu, nor will it disable alt-click interactions. + /// + [DataField("ejectOnUse")] + public bool EjectOnUse = false; + /// /// Override the insert verb text. Defaults to [insert category] -> [item-name]. If not null, the verb will /// not be given a category. diff --git a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs index bf62dbe3bb..7fbc39a22a 100644 --- a/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs +++ b/Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs @@ -34,6 +34,7 @@ namespace Content.Shared.Containers.ItemSlots SubscribeLocalEvent(OnInteractUsing); SubscribeLocalEvent(OnInteractHand); + SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(AddEjectVerbs); SubscribeLocalEvent(AddInteractionVerbsVerbs); @@ -125,6 +126,25 @@ namespace Content.Shared.Containers.ItemSlots } } + /// + /// Attempt to eject an item from the first valid item slot. + /// + private void OnUseInHand(EntityUid uid, ItemSlotsComponent itemSlots, UseInHandEvent args) + { + if (args.Handled) + return; + + foreach (var slot in itemSlots.Slots.Values) + { + if (slot.Locked || !slot.EjectOnUse || slot.Item == null) + continue; + + args.Handled = true; + TryEjectToHands(uid, slot, args.User); + break; + } + } + /// /// Tries to insert a held item in any fitting item slot. If a valid slot already contains an item, it will /// swap it out and place the old one in the user's hand. @@ -172,7 +192,7 @@ namespace Content.Shared.Containers.ItemSlots // ContainerSlot automatically raises a directed EntInsertedIntoContainerMessage if (slot.InsertSound != null) - SoundSystem.Play(Filter.Pvs(uid), slot.InsertSound.GetSound(), uid); + SoundSystem.Play(Filter.Pvs(uid), slot.InsertSound.GetSound(), uid, slot.SoundOptions); } /// @@ -267,7 +287,7 @@ namespace Content.Shared.Containers.ItemSlots // ContainerSlot automatically raises a directed EntRemovedFromContainerMessage if (slot.EjectSound != null) - SoundSystem.Play(Filter.Pvs(uid), slot.EjectSound.GetSound(), uid); + SoundSystem.Play(Filter.Pvs(uid), slot.EjectSound.GetSound(), uid, slot.SoundOptions); } /// @@ -317,7 +337,7 @@ namespace Content.Shared.Containers.ItemSlots return false; if (user != null && EntityManager.TryGetComponent(user.Value, out SharedHandsComponent? hands)) - hands.TryPutInAnyHand(item.Value); + hands.TryPutInActiveHandOrAny(item.Value); return true; } diff --git a/Content.Shared/Crayon/SharedCrayonComponent.cs b/Content.Shared/Crayon/SharedCrayonComponent.cs index 6019a72300..4fd6cf44d1 100644 --- a/Content.Shared/Crayon/SharedCrayonComponent.cs +++ b/Content.Shared/Crayon/SharedCrayonComponent.cs @@ -72,16 +72,4 @@ namespace Content.Shared.Crayon Color = color; } } - - [Serializable, NetSerializable, Prototype("crayonDecal")] - public class CrayonDecalPrototype : IPrototype - { - [ViewVariables] - [DataField("id", required: true)] - public string ID { get; } = default!; - - [DataField("spritePath")] public string SpritePath { get; } = string.Empty; - - [DataField("decals")] public List Decals { get; } = new(); - } } diff --git a/Content.Shared/Damage/Components/DamageableComponent.cs b/Content.Shared/Damage/Components/DamageableComponent.cs index b8b42a03f0..f446192924 100644 --- a/Content.Shared/Damage/Components/DamageableComponent.cs +++ b/Content.Shared/Damage/Components/DamageableComponent.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using Content.Shared.Acts; using Content.Shared.Damage.Prototypes; -using Content.Shared.Damage; using Content.Shared.FixedPoint; using Content.Shared.Radiation; using Robust.Shared.Analyzers; @@ -13,8 +12,6 @@ using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List; using Robust.Shared.ViewVariables; -using Content.Shared.Administration.Logs; -using Content.Shared.Database; namespace Content.Shared.Damage { @@ -95,11 +92,7 @@ namespace Content.Shared.Damage damage.DamageDict.Add(typeID, damageValue); } - var actual = EntitySystem.Get().TryChangeDamage(Owner, damage); - - // should logging be disabled during rad storms? a lot of entities are going to be damaged. - if (actual != null && !actual.Empty) - EntitySystem.Get().Add(LogType.Radiation, $"{Owner} took {actual.Total} radiation damage"); + EntitySystem.Get().TryChangeDamage(Owner, damage); } // TODO EXPLOSION Remove this. @@ -120,11 +113,7 @@ namespace Content.Shared.Damage damage.DamageDict.Add(typeID, damageValue); } - var actual = EntitySystem.Get().TryChangeDamage(Owner, damage); - - // will logging handle nukes? - if (actual != null && !actual.Empty) - EntitySystem.Get().Add(LogType.Explosion, $"{Owner} took {actual.Total} explosion damage"); + EntitySystem.Get().TryChangeDamage(Owner, damage); } } diff --git a/Content.Shared/Damage/Systems/DamageableSystem.cs b/Content.Shared/Damage/Systems/DamageableSystem.cs index 20c6707db6..16b409df8d 100644 --- a/Content.Shared/Damage/Systems/DamageableSystem.cs +++ b/Content.Shared/Damage/Systems/DamageableSystem.cs @@ -1,7 +1,5 @@ using System.Linq; -using Content.Shared.Administration.Logs; using Content.Shared.Damage.Prototypes; -using Content.Shared.Database; using Content.Shared.FixedPoint; using Robust.Shared.GameObjects; using Robust.Shared.GameStates; @@ -15,8 +13,6 @@ namespace Content.Shared.Damage { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly SharedAdminLogSystem _logs = default!; - public override void Initialize() { SubscribeLocalEvent(DamageableInit); @@ -24,45 +20,6 @@ namespace Content.Shared.Damage SubscribeLocalEvent(DamageableGetState); } - /// - /// Update the total damage value and optionally add to admin logs - /// - protected virtual void SetTotalDamage(DamageableComponent damageable, FixedPoint2 @new, bool logChange) - { - var owner = damageable.Owner; - var old = damageable.TotalDamage; - - if (@new == old) - { - return; - } - - damageable.TotalDamage = @new; - - if (!logChange) - return; - - LogType logType; - string type; - FixedPoint2 change; - - if (@new > old) - { - logType = LogType.Damaged; - type = "received"; - change = @new - old; - } - else - { - logType = LogType.Healed; - type = "healed"; - change = old - @new; - } - - _logs.Add(logType, $"{owner} {type} {change} damage. Old: {old} | New: {@new}"); - - } - /// /// Initialize a damageable component /// @@ -111,7 +68,7 @@ namespace Content.Shared.Damage public void SetDamage(DamageableComponent damageable, DamageSpecifier damage) { damageable.Damage = damage; - DamageChanged(damageable, false); + DamageChanged(damageable); } /// @@ -121,11 +78,11 @@ namespace Content.Shared.Damage /// This updates cached damage information, flags the component as dirty, and raises a damage changed event. /// The damage changed event is used by other systems, such as damage thresholds. /// - public void DamageChanged(DamageableComponent component, bool logChange, DamageSpecifier? damageDelta = null, + public void DamageChanged(DamageableComponent component, DamageSpecifier? damageDelta = null, bool interruptsDoAfters = true) { component.DamagePerGroup = component.Damage.GetDamagePerGroup(); - SetTotalDamage(component, component.Damage.Total, logChange); + component.TotalDamage = component.Damage.Total; component.Dirty(); if (EntityManager.TryGetComponent(component.Owner, out var appearance) && damageDelta != null) @@ -146,7 +103,7 @@ namespace Content.Shared.Damage /// null if the user had no applicable components that can take damage. /// public DamageSpecifier? TryChangeDamage(EntityUid? uid, DamageSpecifier damage, bool ignoreResistances = false, - bool interruptsDoAfters = true, bool logChange = false) + bool interruptsDoAfters = true) { if (!EntityManager.TryGetComponent(uid, out var damageable)) { @@ -195,7 +152,7 @@ namespace Content.Shared.Damage if (!delta.Empty) { - DamageChanged(damageable, logChange, delta, interruptsDoAfters); + DamageChanged(damageable, delta, interruptsDoAfters); } return delta; @@ -222,7 +179,7 @@ namespace Content.Shared.Damage // Setting damage does not count as 'dealing' damage, even if it is set to a larger value, so we pass an // empty damage delta. - DamageChanged(component, false, new DamageSpecifier()); + DamageChanged(component, new DamageSpecifier()); } private void DamageableGetState(EntityUid uid, DamageableComponent component, ref ComponentGetState args) @@ -247,7 +204,7 @@ namespace Content.Shared.Damage if (!delta.Empty) { component.Damage = newDamage; - DamageChanged(component, false, delta); + DamageChanged(component, delta); } } } diff --git a/Content.Shared/Decals/Decal.cs b/Content.Shared/Decals/Decal.cs new file mode 100644 index 0000000000..edca3da7e4 --- /dev/null +++ b/Content.Shared/Decals/Decal.cs @@ -0,0 +1,38 @@ +using System; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Shared.Decals +{ + [Serializable, NetSerializable] + [DataDefinition] + public class Decal + { + [DataField("coordinates")] public readonly Vector2 Coordinates = Vector2.Zero; + [DataField("id")] public readonly string Id = string.Empty; + [DataField("color")] public readonly Color? Color; + [DataField("angle")] public readonly Angle Angle = Angle.Zero; + [DataField("zIndex")] public readonly int ZIndex; + [DataField("cleanable")] public bool Cleanable; + + public Decal() {} + + public Decal(Vector2 coordinates, string id, Color? color, Angle angle, int zIndex, bool cleanable) + { + Coordinates = coordinates; + Id = id; + Color = color; + Angle = angle; + ZIndex = zIndex; + Cleanable = cleanable; + } + + public Decal WithCoordinates(Vector2 coordinates) => new(coordinates, Id, Color, Angle, ZIndex, Cleanable); + public Decal WithId(string id) => new(Coordinates, id, Color, Angle, ZIndex, Cleanable); + public Decal WithColor(Color? color) => new(Coordinates, Id, color, Angle, ZIndex, Cleanable); + public Decal WithRotation(Angle angle) => new(Coordinates, Id, Color, angle, ZIndex, Cleanable); + public Decal WithZIndex(int zIndex) => new(Coordinates, Id, Color, Angle, zIndex, Cleanable); + public Decal WithCleanable(bool cleanable) => new(Coordinates, Id, Color, Angle, ZIndex, cleanable); + } +} diff --git a/Content.Shared/Decals/DecalChunkUpdateEvent.cs b/Content.Shared/Decals/DecalChunkUpdateEvent.cs new file mode 100644 index 0000000000..99bbbd5a84 --- /dev/null +++ b/Content.Shared/Decals/DecalChunkUpdateEvent.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; + +namespace Content.Shared.Decals +{ + [Serializable, NetSerializable] + public class DecalChunkUpdateEvent : EntityEventArgs + { + public Dictionary>> Data = new(); + } +} diff --git a/Content.Shared/Decals/DecalGridChunkCollectionTypeSerializer.cs b/Content.Shared/Decals/DecalGridChunkCollectionTypeSerializer.cs new file mode 100644 index 0000000000..301fb2aaed --- /dev/null +++ b/Content.Shared/Decals/DecalGridChunkCollectionTypeSerializer.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using Robust.Shared.IoC; +using Robust.Shared.Maths; +using Robust.Shared.Serialization.Manager; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization.Manager.Result; +using Robust.Shared.Serialization.Markdown; +using Robust.Shared.Serialization.Markdown.Mapping; +using Robust.Shared.Serialization.Markdown.Validation; +using Robust.Shared.Serialization.TypeSerializers.Interfaces; + +namespace Content.Shared.Decals +{ + [TypeSerializer] + public class DecalGridChunkCollectionTypeSerializer : ITypeSerializer + { + public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node, + IDependencyCollection dependencies, ISerializationContext? context = null) + { + return serializationManager.ValidateNode>>(node, context); + } + + public DeserializationResult Read(ISerializationManager serializationManager, MappingDataNode node, + IDependencyCollection dependencies, bool skipHook, ISerializationContext? context = null) + { + //todo this read method does not support pushing inheritance + var dictionary = + serializationManager.ReadValueOrThrow>>(node, context, skipHook); + + var uids = new SortedSet(); + var uidChunkMap = new Dictionary(); + foreach (var (indices, decals) in dictionary) + { + foreach (var (uid, _) in decals) + { + uids.Add(uid); + uidChunkMap[uid] = indices; + } + } + + var uidMap = new Dictionary(); + uint nextIndex = 0; + foreach (var uid in uids) + { + uidMap[uid] = nextIndex++; + } + + var newDict = new Dictionary>(); + foreach (var (oldUid, newUid) in uidMap) + { + var indices = uidChunkMap[oldUid]; + if(!newDict.ContainsKey(indices)) + newDict[indices] = new(); + newDict[indices][newUid] = dictionary[indices][oldUid]; + } + + return new DeserializedValue( + new DecalGridComponent.DecalGridChunkCollection(newDict){NextUid = nextIndex}); + } + + public DataNode Write(ISerializationManager serializationManager, DecalGridComponent.DecalGridChunkCollection value, bool alwaysWrite = false, + ISerializationContext? context = null) + { + return serializationManager.WriteValue(value.ChunkCollection, alwaysWrite, context); + } + + public DecalGridComponent.DecalGridChunkCollection Copy(ISerializationManager serializationManager, DecalGridComponent.DecalGridChunkCollection source, + DecalGridComponent.DecalGridChunkCollection target, bool skipHook, ISerializationContext? context = null) + { + var dict = serializationManager.Copy(source.ChunkCollection, target.ChunkCollection, context, skipHook)!; + return new DecalGridComponent.DecalGridChunkCollection(dict) {NextUid = source.NextUid}; + } + } +} diff --git a/Content.Shared/Decals/DecalGridComponent.cs b/Content.Shared/Decals/DecalGridComponent.cs new file mode 100644 index 0000000000..205a5baed8 --- /dev/null +++ b/Content.Shared/Decals/DecalGridComponent.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Robust.Shared.Analyzers; +using Robust.Shared.GameObjects; +using Robust.Shared.Maths; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Shared.Decals +{ + [RegisterComponent] + [Friend(typeof(SharedDecalSystem))] + public class DecalGridComponent : Component + { + public override string Name => "DecalGrid"; + + [DataField("chunkCollection", serverOnly: true)] + public DecalGridChunkCollection ChunkCollection = new(new ()); + + public record DecalGridChunkCollection(Dictionary> ChunkCollection) + { + public uint NextUid; + } + } +} diff --git a/Content.Shared/Decals/DecalPrototype.cs b/Content.Shared/Decals/DecalPrototype.cs new file mode 100644 index 0000000000..cd648fa162 --- /dev/null +++ b/Content.Shared/Decals/DecalPrototype.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Utility; + +namespace Content.Shared.Decals +{ + [Prototype("decal")] + public class DecalPrototype : IPrototype + { + [DataField("id")] public string ID { get; } = null!; + [DataField("sprite")] public SpriteSpecifier Sprite { get; } = SpriteSpecifier.Invalid; + [DataField("tags")] public List Tags = new(); + } +} diff --git a/Content.Shared/Decals/SharedDecalSystem.cs b/Content.Shared/Decals/SharedDecalSystem.cs new file mode 100644 index 0000000000..2d2b65640e --- /dev/null +++ b/Content.Shared/Decals/SharedDecalSystem.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using Robust.Shared; +using Robust.Shared.Configuration; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Decals +{ + public abstract class SharedDecalSystem : EntitySystem + { + [Dependency] protected readonly IPrototypeManager PrototypeManager = default!; + [Dependency] private readonly IConfigurationManager _configurationManager = default!; + [Dependency] protected readonly IMapManager MapManager = default!; + + protected readonly Dictionary> ChunkIndex = new(); + + public const int ChunkSize = 32; + public static Vector2i GetChunkIndices(Vector2 coordinates) => new ((int) Math.Floor(coordinates.X / ChunkSize), (int) Math.Floor(coordinates.Y / ChunkSize)); + + private float _viewSize; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGridInitialize); + _configurationManager.OnValueChanged(CVars.NetMaxUpdateRange, OnPvsRangeChanged, true); + } + + public override void Shutdown() + { + base.Shutdown(); + _configurationManager.UnsubValueChanged(CVars.NetMaxUpdateRange, OnPvsRangeChanged); + } + + private void OnPvsRangeChanged(float obj) + { + _viewSize = obj * 2f; + } + + private void OnGridInitialize(GridInitializeEvent msg) + { + var comp = EntityManager.EnsureComponent(MapManager.GetGrid(msg.GridId).GridEntityId); + ChunkIndex[msg.GridId] = new(); + foreach (var (indices, decals) in comp.ChunkCollection.ChunkCollection) + { + foreach (var uid in decals.Keys) + { + ChunkIndex[msg.GridId][uid] = indices; + } + } + } + + protected DecalGridComponent.DecalGridChunkCollection DecalGridChunkCollection(GridId gridId) => EntityManager + .GetComponent(MapManager.GetGrid(gridId).GridEntityId).ChunkCollection; + protected Dictionary> ChunkCollection(GridId gridId) => DecalGridChunkCollection(gridId).ChunkCollection; + + protected virtual void DirtyChunk(GridId id, Vector2i chunkIndices) {} + + protected bool RemoveDecalInternal(GridId gridId, uint uid) + { + if (!RemoveDecalHook(gridId, uid)) return false; + + if (!ChunkIndex.TryGetValue(gridId, out var values) || !values.TryGetValue(uid, out var indices)) + { + return false; + } + + var chunkCollection = ChunkCollection(gridId); + if (!chunkCollection.TryGetValue(indices, out var chunk) || !chunk.Remove(uid)) + { + return false; + } + + if (chunkCollection[indices].Count == 0) + chunkCollection.Remove(indices); + + ChunkIndex[gridId]?.Remove(uid); + DirtyChunk(gridId, indices); + return true; + } + + protected virtual bool RemoveDecalHook(GridId gridId, uint uid) => true; + + private (Box2 view, MapId mapId) CalcViewBounds(in EntityUid euid) + { + var xform = EntityManager.GetComponent(euid); + + var view = Box2.UnitCentered.Scale(_viewSize).Translated(xform.WorldPosition); + var map = xform.MapID; + + return (view, map); + } + + protected Dictionary> GetChunksForViewers(HashSet viewers) + { + var chunks = new Dictionary>(); + foreach (var viewerUid in viewers) + { + var (bounds, mapId) = CalcViewBounds(viewerUid); + MapManager.FindGridsIntersectingEnumerator(mapId, bounds, out var gridsEnumerator, true); + while(gridsEnumerator.MoveNext(out var grid)) + { + if(!chunks.ContainsKey(grid.Index)) + chunks[grid.Index] = new(); + var enumerator = new ChunkIndicesEnumerator(grid.InvWorldMatrix.TransformBox(bounds), ChunkSize); + while (enumerator.MoveNext(out var indices)) + { + chunks[grid.Index].Add(indices.Value); + } + } + } + return chunks; + } + } + + internal struct ChunkIndicesEnumerator + { + private Vector2i _chunkLB; + private Vector2i _chunkRT; + + private int _xIndex; + private int _yIndex; + + internal ChunkIndicesEnumerator(Box2 localAABB, int chunkSize) + { + _chunkLB = new Vector2i((int)Math.Floor(localAABB.Left / chunkSize), (int)Math.Floor(localAABB.Bottom / chunkSize)); + _chunkRT = new Vector2i((int)Math.Floor(localAABB.Right / chunkSize), (int)Math.Floor(localAABB.Top / chunkSize)); + + _xIndex = _chunkLB.X; + _yIndex = _chunkLB.Y; + } + + public bool MoveNext([NotNullWhen(true)] out Vector2i? indices) + { + if (_yIndex > _chunkRT.Y) + { + _yIndex = _chunkLB.Y; + _xIndex += 1; + } + + indices = new Vector2i(_xIndex, _yIndex); + _yIndex += 1; + + return _xIndex <= _chunkRT.X; + } + } +} diff --git a/Content.Shared/Jittering/SharedJitteringSystem.cs b/Content.Shared/Jittering/SharedJitteringSystem.cs index b30fd0e3fb..7a5757d2c9 100644 --- a/Content.Shared/Jittering/SharedJitteringSystem.cs +++ b/Content.Shared/Jittering/SharedJitteringSystem.cs @@ -53,12 +53,13 @@ namespace Content.Shared.Jittering /// /// Entity in question. /// For how much time to apply the effect. + /// The status effect cooldown should be refreshed (true) or accumulated (false). /// Jitteriness of the animation. See and . /// Frequency for jittering. See and . /// Whether to change any existing jitter value even if they're greater than the ones we're setting. /// The status effects component to modify. /// The alerts component. - public void DoJitter(EntityUid uid, TimeSpan time, float amplitude = 10f, float frequency = 4f, bool forceValueChange = false, + public void DoJitter(EntityUid uid, TimeSpan time, bool refresh, float amplitude = 10f, float frequency = 4f, bool forceValueChange = false, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null) { @@ -68,7 +69,7 @@ namespace Content.Shared.Jittering amplitude = Math.Clamp(amplitude, MinAmplitude, MaxAmplitude); frequency = Math.Clamp(frequency, MinFrequency, MaxFrequency); - if (StatusEffects.TryAddStatusEffect(uid, "Jitter", time, status, alerts)) + if (StatusEffects.TryAddStatusEffect(uid, "Jitter", time, refresh, status, alerts)) { var jittering = EntityManager.GetComponent(uid); diff --git a/Content.Shared/MobState/Components/MobStateComponent.cs b/Content.Shared/MobState/Components/MobStateComponent.cs index 7394ee3a2f..484f354611 100644 --- a/Content.Shared/MobState/Components/MobStateComponent.cs +++ b/Content.Shared/MobState/Components/MobStateComponent.cs @@ -289,9 +289,11 @@ namespace Content.Shared.MobState.Components /// private void SetMobState(IMobState? old, (IMobState state, FixedPoint2 threshold)? current) { + var entMan = IoCManager.Resolve(); + if (!current.HasValue) { - old?.ExitState(Owner, IoCManager.Resolve()); + old?.ExitState(Owner, entMan); return; } @@ -301,22 +303,19 @@ namespace Content.Shared.MobState.Components if (state == old) { - state.UpdateState(Owner, threshold, IoCManager.Resolve()); + state.UpdateState(Owner, threshold, entMan); return; } - old?.ExitState(Owner, IoCManager.Resolve()); + old?.ExitState(Owner, entMan); CurrentState = state; - state.EnterState(Owner, IoCManager.Resolve()); - state.UpdateState(Owner, threshold, IoCManager.Resolve()); + state.EnterState(Owner, entMan); + state.UpdateState(Owner, threshold, entMan); - var message = new MobStateChangedMessage(this, old, state); -#pragma warning disable 618 - SendMessage(message); -#pragma warning restore 618 - IoCManager.Resolve().EventBus.RaiseEvent(EventSource.Local, message); + var message = new MobStateChangedEvent(this, old, state); + entMan.EventBus.RaiseLocalEvent(Owner, message); Dirty(); } diff --git a/Content.Shared/MobState/MobStateChangedMessage.cs b/Content.Shared/MobState/MobStateChangedEvent.cs similarity index 80% rename from Content.Shared/MobState/MobStateChangedMessage.cs rename to Content.Shared/MobState/MobStateChangedEvent.cs index b6d6ae71ff..8fcb40b1fc 100644 --- a/Content.Shared/MobState/MobStateChangedMessage.cs +++ b/Content.Shared/MobState/MobStateChangedEvent.cs @@ -4,11 +4,9 @@ using Robust.Shared.GameObjects; namespace Content.Shared.MobState { -#pragma warning disable 618 - public class MobStateChangedMessage : ComponentMessage -#pragma warning restore 618 + public class MobStateChangedEvent : EntityEventArgs { - public MobStateChangedMessage( + public MobStateChangedEvent( MobStateComponent component, IMobState? oldMobState, IMobState currentMobState) diff --git a/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs b/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs index 399a71491e..b690d57341 100644 --- a/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/SharedCreamPieSystem.cs @@ -68,7 +68,7 @@ namespace Content.Shared.Nutrition.EntitySystems CreamedEntity(uid, creamPied, args); - _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(creamPie.ParalyzeTime)); + _stunSystem.TryParalyze(uid, TimeSpan.FromSeconds(creamPie.ParalyzeTime), true); } protected virtual void CreamedEntity(EntityUid uid, CreamPiedComponent creamPied, ThrowHitByEvent args) {} diff --git a/Content.Shared/Slippery/SharedSlipperySystem.cs b/Content.Shared/Slippery/SharedSlipperySystem.cs index 091bc4cff7..ba926a9200 100644 --- a/Content.Shared/Slippery/SharedSlipperySystem.cs +++ b/Content.Shared/Slippery/SharedSlipperySystem.cs @@ -63,7 +63,7 @@ namespace Content.Shared.Slippery if (!component.Slippery || component.Owner.IsInContainer() || component.Slipped.Contains(uid) - || !_statusEffectsSystem.CanApplyEffect(uid, "Stun")) + || !_statusEffectsSystem.CanApplyEffect(uid, "Stun")) //Should be KnockedDown instead? { return false; } @@ -94,11 +94,17 @@ namespace Content.Shared.Slippery otherBody.LinearVelocity *= component.LaunchForwardsMultiplier; - _stunSystem.TryParalyze(otherBody.Owner, TimeSpan.FromSeconds(5)); + bool playSound = !_statusEffectsSystem.HasStatusEffect(otherBody.Owner, "KnockedDown"); + + _stunSystem.TryParalyze(otherBody.Owner, TimeSpan.FromSeconds(component.ParalyzeTime), true); component.Slipped.Add(otherBody.Owner); component.Dirty(); - PlaySound(component); + //Preventing from playing the slip sound when you are already knocked down. + if(playSound) + { + PlaySound(component); + } _adminLog.Add(LogType.Slip, LogImpact.Low, $"{component.Owner} slipped on collision with {otherBody.Owner}"); diff --git a/Content.Shared/Slippery/SlipperyComponent.cs b/Content.Shared/Slippery/SlipperyComponent.cs index 3d6f691069..eb33eb1e39 100644 --- a/Content.Shared/Slippery/SlipperyComponent.cs +++ b/Content.Shared/Slippery/SlipperyComponent.cs @@ -18,7 +18,7 @@ namespace Content.Shared.Slippery { public override string Name => "Slippery"; - private float _paralyzeTime = 3f; + private float _paralyzeTime = 5f; private float _intersectPercentage = 0.3f; private float _requiredSlipSpeed = 5f; private float _launchForwardsMultiplier = 1f; diff --git a/Content.Shared/Speech/EntitySystems/SharedStutteringSystem.cs b/Content.Shared/Speech/EntitySystems/SharedStutteringSystem.cs index ed9c5d545f..a5b63bab40 100644 --- a/Content.Shared/Speech/EntitySystems/SharedStutteringSystem.cs +++ b/Content.Shared/Speech/EntitySystems/SharedStutteringSystem.cs @@ -8,7 +8,7 @@ namespace Content.Shared.Speech.EntitySystems public abstract class SharedStutteringSystem : EntitySystem { // For code in shared... I imagine we ain't getting accent prediction anytime soon so let's not bother. - public virtual void DoStutter(EntityUid uid, TimeSpan time, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null) + public virtual void DoStutter(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null) { } } diff --git a/Content.Shared/StatusEffect/StatusEffectsComponent.cs b/Content.Shared/StatusEffect/StatusEffectsComponent.cs index 5bfd33f345..297a7573a1 100644 --- a/Content.Shared/StatusEffect/StatusEffectsComponent.cs +++ b/Content.Shared/StatusEffect/StatusEffectsComponent.cs @@ -38,6 +38,13 @@ namespace Content.Shared.StatusEffect [ViewVariables] public (TimeSpan, TimeSpan) Cooldown; + /// + /// Specifies whether to refresh or accumulate the cooldown of the status effect. + /// true - refresh time, false - accumulate time. + /// + [ViewVariables] + public bool CooldownRefresh = true; + /// /// The name of the relevant component that /// was added alongside the effect, if any. @@ -45,9 +52,10 @@ namespace Content.Shared.StatusEffect [ViewVariables] public string? RelevantComponent; - public StatusEffectState((TimeSpan, TimeSpan) cooldown, string? relevantComponent=null) + public StatusEffectState((TimeSpan, TimeSpan) cooldown, bool refresh, string? relevantComponent=null) { Cooldown = cooldown; + CooldownRefresh = refresh; RelevantComponent = relevantComponent; } } diff --git a/Content.Shared/StatusEffect/StatusEffectsSystem.cs b/Content.Shared/StatusEffect/StatusEffectsSystem.cs index 61c3c94300..d724056d5c 100644 --- a/Content.Shared/StatusEffect/StatusEffectsSystem.cs +++ b/Content.Shared/StatusEffect/StatusEffectsSystem.cs @@ -64,7 +64,8 @@ namespace Content.Shared.StatusEffect } var time = effect.Value.Cooldown.Item2 - effect.Value.Cooldown.Item1; - TryAddStatusEffect(uid, effect.Key, time); + //TODO: Not sure how to handle refresh here. + TryAddStatusEffect(uid, effect.Key, time, true); } } } @@ -75,11 +76,12 @@ namespace Content.Shared.StatusEffect /// The entity to add the effect to. /// The status effect ID to add. /// How long the effect should last for. + /// The status effect cooldown should be refreshed (true) or accumulated (false). /// The status effects component to change, if you already have it. /// The alerts component to modify, if the status effect has an alert. /// False if the effect could not be added or the component already exists, true otherwise. /// The component type to add and remove from the entity. - public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, + public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, StatusEffectsComponent? status=null, SharedAlertsComponent? alerts=null) where T: Component, new() @@ -89,7 +91,7 @@ namespace Content.Shared.StatusEffect Resolve(uid, ref alerts, false); - if (TryAddStatusEffect(uid, key, time, status, alerts)) + if (TryAddStatusEffect(uid, key, time, refresh, status, alerts)) { // If they already have the comp, we just won't bother updating anything. if (!EntityManager.HasComponent(uid)) @@ -103,7 +105,7 @@ namespace Content.Shared.StatusEffect return false; } - public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, string component, + public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, string component, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null) { @@ -112,7 +114,7 @@ namespace Content.Shared.StatusEffect Resolve(uid, ref alerts, false); - if (TryAddStatusEffect(uid, key, time, status, alerts)) + if (TryAddStatusEffect(uid, key, time, refresh, status, alerts)) { // If they already have the comp, we just won't bother updating anything. if (!EntityManager.HasComponent(uid, _componentFactory.GetRegistration(component).Type)) @@ -136,6 +138,7 @@ namespace Content.Shared.StatusEffect /// The entity to add the effect to. /// The status effect ID to add. /// How long the effect should last for. + /// The status effect cooldown should be refreshed (true) or accumulated (false). /// The status effects component to change, if you already have it. /// The alerts component to modify, if the status effect has an alert. /// False if the effect could not be added, or if the effect already existed. @@ -146,7 +149,7 @@ namespace Content.Shared.StatusEffect /// If the effect already exists, it will simply replace the cooldown with the new one given. /// If you want special 'effect merging' behavior, do it your own damn self! /// - public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, + public bool TryAddStatusEffect(EntityUid uid, string key, TimeSpan time, bool refresh, StatusEffectsComponent? status=null, SharedAlertsComponent? alerts=null) { @@ -163,14 +166,27 @@ namespace Content.Shared.StatusEffect (TimeSpan, TimeSpan) cooldown = (_gameTiming.CurTime, _gameTiming.CurTime + time); - // If they already have this status effect, add the time onto it's cooldown rather than anything else. if (HasStatusEffect(uid, key, status)) { - status.ActiveEffects[key].Cooldown.Item2 += time; + status.ActiveEffects[key].CooldownRefresh = refresh; + if(refresh) + { + //Making sure we don't reset a longer cooldown by applying a shorter one. + if((status.ActiveEffects[key].Cooldown.Item2 - _gameTiming.CurTime) < time) + { + //Refresh cooldown time. + status.ActiveEffects[key].Cooldown = cooldown; + } + } + else + { + //Accumulate cooldown time. + status.ActiveEffects[key].Cooldown.Item2 += time; + } } else { - status.ActiveEffects.Add(key, new StatusEffectState(cooldown, null)); + status.ActiveEffects.Add(key, new StatusEffectState(cooldown, refresh, null)); } if (proto.Alert != null && alerts != null) diff --git a/Content.Shared/Stunnable/SharedStunSystem.cs b/Content.Shared/Stunnable/SharedStunSystem.cs index 9b5570f72c..92d85fcaa9 100644 --- a/Content.Shared/Stunnable/SharedStunSystem.cs +++ b/Content.Shared/Stunnable/SharedStunSystem.cs @@ -118,7 +118,7 @@ namespace Content.Shared.Stunnable /// /// Stuns the entity, disallowing it from doing many interactions temporarily. /// - public bool TryStun(EntityUid uid, TimeSpan time, + public bool TryStun(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null) { @@ -130,13 +130,13 @@ namespace Content.Shared.Stunnable Resolve(uid, ref alerts, false); - return _statusEffectSystem.TryAddStatusEffect(uid, "Stun", time, alerts: alerts); + return _statusEffectSystem.TryAddStatusEffect(uid, "Stun", time, refresh, alerts: alerts); } /// /// Knocks down the entity, making it fall to the ground. /// - public bool TryKnockdown(EntityUid uid, TimeSpan time, + public bool TryKnockdown(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null) { @@ -148,13 +148,13 @@ namespace Content.Shared.Stunnable Resolve(uid, ref alerts, false); - return _statusEffectSystem.TryAddStatusEffect(uid, "KnockedDown", time, alerts: alerts); + return _statusEffectSystem.TryAddStatusEffect(uid, "KnockedDown", time, refresh, alerts: alerts); } /// /// Applies knockdown and stun to the entity temporarily. /// - public bool TryParalyze(EntityUid uid, TimeSpan time, + public bool TryParalyze(EntityUid uid, TimeSpan time, bool refresh, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null) { @@ -164,13 +164,13 @@ namespace Content.Shared.Stunnable // Optional component. Resolve(uid, ref alerts, false); - return TryKnockdown(uid, time, status, alerts) && TryStun(uid, time, status, alerts); + return TryKnockdown(uid, time, refresh, status, alerts) && TryStun(uid, time, refresh, status, alerts); } /// /// Slows down the mob's walking/running speed temporarily /// - public bool TrySlowdown(EntityUid uid, TimeSpan time, + public bool TrySlowdown(EntityUid uid, TimeSpan time, bool refresh, float walkSpeedMultiplier = 1f, float runSpeedMultiplier = 1f, StatusEffectsComponent? status = null, SharedAlertsComponent? alerts = null) @@ -184,7 +184,7 @@ namespace Content.Shared.Stunnable if (time <= TimeSpan.Zero) return false; - if (_statusEffectSystem.TryAddStatusEffect(uid, "SlowedDown", time, status, alerts)) + if (_statusEffectSystem.TryAddStatusEffect(uid, "SlowedDown", time, refresh, status, alerts)) { var slowed = EntityManager.GetComponent(uid); // Doesn't make much sense to have the "TrySlowdown" method speed up entities now does it? diff --git a/Resources/Changelog/Changelog.yml b/Resources/Changelog/Changelog.yml index 64145113cb..f61f291b5a 100644 --- a/Resources/Changelog/Changelog.yml +++ b/Resources/Changelog/Changelog.yml @@ -1,68 +1,4 @@ Entries: -- author: Hardly - changes: - - {message: Reduced Syndicate minibomb's radius, type: Tweak} - id: 199 - time: '2021-05-21T11:26:10.0000000+00:00' -- author: Swept - changes: - - {message: Removed solution transfer verbs., type: Remove} - id: 200 - time: '2021-05-21T21:36:16.0000000+00:00' -- author: Zumorica - changes: - - {message: Fixes lathes not printing any recipe., type: Fix} - id: 201 - time: '2021-05-22T09:31:38.140827+00:00' -- author: Ygg01 - changes: - - {message: StackVisualizer now correctly displays levels, type: Fix} - id: 202 - time: '2021-05-23T04:05:55.0000000+00:00' -- author: 20kdc - changes: - - {message: Hopefully fix power equipment disappearing., type: Fix} - id: 203 - time: '2021-05-23T18:18:36.0000000+00:00' -- author: Seth - changes: - - {message: Added new technologies and recipes to the lathe., type: Add} - id: 204 - time: '2021-05-24T00:38:26.0000000+00:00' -- author: 20kdc - changes: - - {message: Particle Accelerator particles actually affect singularity & singularity - generator again, type: Fix} - - {message: Singularity doesn't instantly escape all containment, type: Fix} - id: 205 - time: '2021-05-24T04:14:15.0000000+00:00' -- author: 20kdc - changes: - - {message: Attempt 2 at fixing substation/apc/generator disappearances, type: Fix} - id: 206 - time: '2021-05-24T14:39:58.0000000+00:00' -- author: scrato - changes: - - {message: You now won't be able to get out of handcuffs if you're dead or incapacitated, - type: Fix} - id: 207 - time: '2021-05-24T14:55:48.0000000+00:00' -- author: 20kdc - changes: - - {message: Dragging an object causes it to not count as thrown anymore., type: Fix} - id: 208 - time: '2021-05-27T10:47:40.0000000+00:00' -- author: 20kdc - changes: - - {message: 'Spears now apply damage when thrown, and have more damage', type: Add} - id: 209 - time: '2021-05-27T10:47:49.0000000+00:00' -- author: 20kdc - changes: - - {message: 'Pulling is now more reliable across different object weights, and feels - better.', type: Fix} - id: 210 - time: '2021-05-27T10:48:37.0000000+00:00' - author: 20kdc changes: - {message: 'You can rotate barstools, PA components, and radiation collectors.', @@ -2865,3 +2801,71 @@ Entries: - {message: Modified gyroscope sprite., type: Tweak} id: 700 time: '2021-12-03T09:39:18.0000000+00:00' +- author: 20kdc + changes: + - {message: 'The metaphysical department has finally learned what a monkey is, and + has adjusted soul binding to monkeys accordingly.', type: Fix} + id: 701 + time: '2021-12-03T17:11:51.0000000+00:00' +- author: 20kdc + changes: + - {message: The Salvage department is now partially there., type: Add} + id: 702 + time: '2021-12-03T22:24:19.0000000+00:00' +- author: 20kdc + changes: + - {message: Throw pushback ratio multiplied by 10., type: Tweak} + id: 703 + time: '2021-12-04T00:26:52.0000000+00:00' +- author: ElectroJr + changes: + - {message: Fixed some issues preventing mobs from taking proper suffocation damage., + type: Fix} + id: 704 + time: '2021-12-04T08:11:48.0000000+00:00' +- author: vulppine + changes: + - {message: Tools (along with many other objects) have been stripped of their insulated + property. Go get some real insulated gloves!, type: Fix} + id: 705 + time: '2021-12-04T09:59:09.0000000+00:00' +- author: Peptide90 + changes: + - {message: Fixed flares so they now burn longer and burn out slower., type: Fix} + id: 706 + time: '2021-12-04T18:50:25.0000000+00:00' +- author: Rane + changes: + - {message: Added nettles! They're an amazing source of histamine when cultivated + to high potency or ground up., type: Add} + id: 707 + time: '2021-12-04T18:55:04.0000000+00:00' +- author: ElectroJr + changes: + - {message: Cigars and clown/mime masks no longer block drinking & eating., type: Fix} + id: 708 + time: '2021-12-04T18:56:27.0000000+00:00' +- author: Rane + changes: + - {message: Airlock access to high security areas has been updated to include only + those who need it., type: Tweak} + id: 709 + time: '2021-12-05T03:20:06.0000000+00:00' +- author: Mith-randalf + changes: + - {message: 'Fixed some clothing names, applied armour to previously unarmoured + clothes, tweaked some armour values.', type: Tweak} + id: 710 + time: '2021-12-05T06:39:52.0000000+00:00' +- author: Peptide90 + changes: + - {message: Added construction for main crates and deconstruction for all crates. + Use a screwdriver to deconstruct crates., type: Add} + id: 711 + time: '2021-12-06T22:10:04.0000000+00:00' +- author: pointer-to-null + changes: + - {message: Fixed the status effects cooldown time accumulation. (Major clown nerf)., + type: Fix} + id: 712 + time: '2021-12-07T06:18:07.0000000+00:00' diff --git a/Resources/Locale/en-US/chemistry/components/injector-component.ftl b/Resources/Locale/en-US/chemistry/components/injector-component.ftl index 84e58824a6..4249c14e87 100644 --- a/Resources/Locale/en-US/chemistry/components/injector-component.ftl +++ b/Resources/Locale/en-US/chemistry/components/injector-component.ftl @@ -17,3 +17,8 @@ injector-component-transfer-success-message = You transfer {$amount}u into {$tar injector-component-draw-success-message = You draw {$amount}u from {$target}. injector-component-target-already-full-message = {$target} is already full! injector-component-target-is-empty-message = {$target} is empty! + +## mob-inject doafter messages + +injector-component-injecting-user = You start inserting the needle. +injector-component-injecting-target = {$user} is trying to stick a needle into you! diff --git a/Resources/Locale/en-US/construction/conditions/locked.ftl b/Resources/Locale/en-US/construction/conditions/locked.ftl new file mode 100644 index 0000000000..8093ee27a1 --- /dev/null +++ b/Resources/Locale/en-US/construction/conditions/locked.ftl @@ -0,0 +1,5 @@ +# Locked +construction-examine-condition-unlock = First, [color=limegreen]unlock[/color] it. +construction-examine-condition-lock = First, [color=red]lock[/color] it. +construction-step-condition-unlock = It must be unlocked. +construction-step-condition-lock = It must be locked. \ No newline at end of file diff --git a/Resources/Maps/packedstation.yml b/Resources/Maps/packedstation.yml index eebdac31b5..1c9fac5660 100644 --- a/Resources/Maps/packedstation.yml +++ b/Resources/Maps/packedstation.yml @@ -161,4403 +161,8 @@ entities: type: Transform - index: 0 type: MapGrid - - angularDamping: 0.3 + - linearDamping: 0.1 fixedRotation: False - fixtures: - - shape: !type:PolygonShape - vertices: - - 0,0 - - 0,2 - - -10,2 - - -10,0 - id: grid_chunk--10-0 - mask: - - MapGrid - layer: - - MapGrid - mass: 80 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -16,-6 - - -16,-5 - - -21,-5 - - -21,-6 - id: grid_chunk--21--6 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-3 - - 0,0 - - -11,0 - - -11,-3 - id: grid_chunk--11--3 - mask: - - MapGrid - layer: - - MapGrid - mass: 132 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-5 - - 0,-3 - - -10,-3 - - -10,-5 - id: grid_chunk--10--5 - mask: - - MapGrid - layer: - - MapGrid - mass: 80 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-6 - - 0,-5 - - -16,-5 - - -16,-6 - id: grid_chunk--16--6 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -16,-5 - - -16,-4 - - -18,-4 - - -18,-5 - id: grid_chunk--18--5 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -17,-4 - - -17,-3 - - -19,-3 - - -19,-4 - id: grid_chunk--19--4 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -18,-3 - - -18,0 - - -19,0 - - -19,-3 - id: grid_chunk--19--3 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -17,0 - - -17,1 - - -19,1 - - -19,0 - id: grid_chunk--19-0 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -16,1 - - -16,2 - - -18,2 - - -18,1 - id: grid_chunk--18-1 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-10 - - 0,-6 - - -11,-6 - - -11,-10 - id: grid_chunk--11--10 - mask: - - MapGrid - layer: - - MapGrid - mass: 176 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-14 - - 0,-11 - - -8,-11 - - -8,-14 - id: grid_chunk--8--14 - mask: - - MapGrid - layer: - - MapGrid - mass: 96 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-11 - - 0,-10 - - -9,-10 - - -9,-11 - id: grid_chunk--9--11 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-16 - - 0,-14 - - -1,-14 - - -1,-16 - id: grid_chunk--1--16 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,3 - - 0,6 - - -2,6 - - -2,3 - id: grid_chunk--2-3 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,9 - - 0,12 - - -2,12 - - -2,9 - id: grid_chunk--2-9 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,16 - - 0,21 - - -4,21 - - -4,16 - id: grid_chunk--4-16 - mask: - - MapGrid - layer: - - MapGrid - mass: 80 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,13 - - 0,16 - - -4,16 - - -4,13 - id: grid_chunk--4-13 - mask: - - MapGrid - layer: - - MapGrid - mass: 48 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-16 - - 16,0 - - 0,0 - - 0,-16 - id: grid_chunk-0--16 - mask: - - MapGrid - layer: - - MapGrid - mass: 1024 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,0 - - 16,16 - - 0,16 - - 0,0 - id: grid_chunk-0-0 - mask: - - MapGrid - layer: - - MapGrid - mass: 1024 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,-16 - - 32,0 - - 16,0 - - 16,-16 - id: grid_chunk-16--16 - mask: - - MapGrid - layer: - - MapGrid - mass: 1024 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 43,-16 - - 43,-14 - - 32,-14 - - 32,-16 - id: grid_chunk-32--16 - mask: - - MapGrid - layer: - - MapGrid - mass: 88 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 43,-17 - - 43,-16 - - 32,-16 - - 32,-17 - id: grid_chunk-32--17 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-16 - - 48,-14 - - 46,-14 - - 46,-16 - id: grid_chunk-46--16 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-17 - - 48,-16 - - 46,-16 - - 46,-17 - id: grid_chunk-46--17 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,-32 - - 32,-16 - - 16,-16 - - 16,-32 - id: grid_chunk-16--32 - mask: - - MapGrid - layer: - - MapGrid - mass: 1024 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,21 - - 16,22 - - 6,22 - - 6,21 - id: grid_chunk-6-21 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,16 - - 16,21 - - 4,21 - - 4,16 - id: grid_chunk-4-16 - mask: - - MapGrid - layer: - - MapGrid - mass: 240 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,22 - - 16,29 - - 4,29 - - 4,22 - id: grid_chunk-4-22 - mask: - - MapGrid - layer: - - MapGrid - mass: 336 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,29 - - 16,32 - - 5,32 - - 5,29 - id: grid_chunk-5-29 - mask: - - MapGrid - layer: - - MapGrid - mass: 132 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,0 - - 32,16 - - 16,16 - - 16,0 - id: grid_chunk-16-0 - mask: - - MapGrid - layer: - - MapGrid - mass: 1024 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-14 - - 48,0 - - 32,0 - - 32,-14 - id: grid_chunk-32--14 - mask: - - MapGrid - layer: - - MapGrid - mass: 896 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,16 - - 32,32 - - 16,32 - - 16,16 - id: grid_chunk-16-16 - mask: - - MapGrid - layer: - - MapGrid - mass: 1024 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,48 - - 32,50 - - 21,50 - - 21,48 - id: grid_chunk-21-48 - mask: - - MapGrid - layer: - - MapGrid - mass: 88 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 59,-14 - - 59,-13 - - 48,-13 - - 48,-14 - id: grid_chunk-48--14 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,-16 - - 64,-14 - - 48,-14 - - 48,-16 - id: grid_chunk-48--16 - mask: - - MapGrid - layer: - - MapGrid - mass: 128 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,-13 - - 64,0 - - 48,0 - - 48,-13 - id: grid_chunk-48--13 - mask: - - MapGrid - layer: - - MapGrid - mass: 832 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,0 - - 48,16 - - 32,16 - - 32,0 - id: grid_chunk-32-0 - mask: - - MapGrid - layer: - - MapGrid - mass: 1024 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 41,39 - - 41,40 - - 32,40 - - 32,39 - id: grid_chunk-32-39 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 39,44 - - 39,45 - - 32,45 - - 32,44 - id: grid_chunk-32-44 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 37,45 - - 37,47 - - 32,47 - - 32,45 - id: grid_chunk-32-45 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 41,44 - - 41,47 - - 40,47 - - 40,44 - id: grid_chunk-40-44 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,47 - - 48,48 - - 32,48 - - 32,47 - id: grid_chunk-32-47 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 46,44 - - 46,45 - - 42,45 - - 42,44 - id: grid_chunk-42-44 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,45 - - 48,47 - - 42,47 - - 42,45 - id: grid_chunk-42-45 - mask: - - MapGrid - layer: - - MapGrid - mass: 48 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 51,45 - - 51,47 - - 48,47 - - 48,45 - id: grid_chunk-48-45 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,32 - - 48,39 - - 32,39 - - 32,32 - id: grid_chunk-32-32 - mask: - - MapGrid - layer: - - MapGrid - mass: 448 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,40 - - 48,41 - - 32,41 - - 32,40 - id: grid_chunk-32-40 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 47,41 - - 47,44 - - 32,44 - - 32,41 - id: grid_chunk-32-41 - mask: - - MapGrid - layer: - - MapGrid - mass: 180 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,39 - - 48,40 - - 43,40 - - 43,39 - id: grid_chunk-43-39 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 60,41 - - 60,42 - - 53,42 - - 53,41 - id: grid_chunk-53-41 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 62,40 - - 62,41 - - 51,41 - - 51,40 - id: grid_chunk-51-40 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 63,32 - - 63,40 - - 50,40 - - 50,32 - id: grid_chunk-50-32 - mask: - - MapGrid - layer: - - MapGrid - mass: 416 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,29 - - 64,30 - - 51,30 - - 51,29 - id: grid_chunk-51-29 - mask: - - MapGrid - layer: - - MapGrid - mass: 52 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,45 - - 64,47 - - 62,47 - - 62,45 - id: grid_chunk-62-45 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,47 - - 64,48 - - 48,48 - - 48,47 - id: grid_chunk-48-47 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,27 - - 64,28 - - 61,28 - - 61,27 - id: grid_chunk-61-27 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,16 - - 48,32 - - 32,32 - - 32,16 - id: grid_chunk-32-16 - mask: - - MapGrid - layer: - - MapGrid - mass: 1024 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 52,27 - - 52,28 - - 48,28 - - 48,27 - id: grid_chunk-48-27 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,28 - - 64,29 - - 48,29 - - 48,28 - id: grid_chunk-48-28 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 62,30 - - 62,32 - - 51,32 - - 51,30 - id: grid_chunk-51-30 - mask: - - MapGrid - layer: - - MapGrid - mass: 88 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,16 - - 64,27 - - 48,27 - - 48,16 - id: grid_chunk-48-16 - mask: - - MapGrid - layer: - - MapGrid - mass: 704 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,0 - - 64,16 - - 48,16 - - 48,0 - id: grid_chunk-48-0 - mask: - - MapGrid - layer: - - MapGrid - mass: 1024 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,-10 - - 80,-8 - - 75,-8 - - 75,-10 - id: grid_chunk-75--10 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,-8 - - 80,0 - - 64,0 - - 64,-8 - id: grid_chunk-64--8 - mask: - - MapGrid - layer: - - MapGrid - mass: 512 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 74,-11 - - 74,-8 - - 64,-8 - - 64,-11 - id: grid_chunk-64--11 - mask: - - MapGrid - layer: - - MapGrid - mass: 120 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,-16 - - 80,-11 - - 64,-11 - - 64,-16 - id: grid_chunk-64--16 - mask: - - MapGrid - layer: - - MapGrid - mass: 320 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 22,50 - - 22,51 - - 21,51 - - 21,50 - id: grid_chunk-21-50 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,51 - - 32,52 - - 21,52 - - 21,51 - id: grid_chunk-21-51 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 22,52 - - 22,53 - - 21,53 - - 21,52 - id: grid_chunk-21-52 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,53 - - 32,54 - - 21,54 - - 21,53 - id: grid_chunk-21-53 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,54 - - 32,55 - - 22,55 - - 22,54 - id: grid_chunk-22-54 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 31,55 - - 31,56 - - 24,56 - - 24,55 - id: grid_chunk-24-55 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 30,56 - - 30,57 - - 25,57 - - 25,56 - id: grid_chunk-25-56 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 29,57 - - 29,58 - - 26,58 - - 26,57 - id: grid_chunk-26-57 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 28,58 - - 28,61 - - 27,61 - - 27,58 - id: grid_chunk-27-58 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-48 - - 48,-32 - - 32,-32 - - 32,-48 - id: grid_chunk-32--48 - mask: - - MapGrid - layer: - - MapGrid - mass: 1024 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-32 - - 48,-17 - - 32,-17 - - 32,-32 - id: grid_chunk-32--32 - mask: - - MapGrid - layer: - - MapGrid - mass: 960 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 117,-26 - - 117,-21 - - 112,-21 - - 112,-26 - id: grid_chunk-112--26 - mask: - - MapGrid - layer: - - MapGrid - mass: 100 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 122,-21 - - 122,-16 - - 112,-16 - - 112,-21 - id: grid_chunk-112--21 - mask: - - MapGrid - layer: - - MapGrid - mass: 200 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 122,-16 - - 122,-14 - - 112,-14 - - 112,-16 - id: grid_chunk-112--16 - mask: - - MapGrid - layer: - - MapGrid - mass: 80 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 117,-14 - - 117,-9 - - 112,-9 - - 112,-14 - id: grid_chunk-112--14 - mask: - - MapGrid - layer: - - MapGrid - mass: 100 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 59,27 - - 59,28 - - 54,28 - - 54,27 - id: grid_chunk-54-27 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 15,-48 - - 15,-47 - - 10,-47 - - 10,-48 - id: grid_chunk-10--48 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 15,-47 - - 15,-46 - - 11,-46 - - 11,-47 - id: grid_chunk-11--47 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-46 - - 16,-45 - - 11,-45 - - 11,-46 - id: grid_chunk-11--46 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-45 - - 16,-44 - - 12,-44 - - 12,-45 - id: grid_chunk-12--45 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 3,-42 - - 3,-41 - - 0,-41 - - 0,-42 - id: grid_chunk-0--42 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-44 - - 16,-40 - - 8,-40 - - 8,-44 - id: grid_chunk-8--44 - mask: - - MapGrid - layer: - - MapGrid - mass: 128 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 2,-41 - - 2,-39 - - 0,-39 - - 0,-41 - id: grid_chunk-0--41 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 1,-39 - - 1,-38 - - 0,-38 - - 0,-39 - id: grid_chunk-0--39 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 14,-40 - - 14,-38 - - 8,-38 - - 8,-40 - id: grid_chunk-8--40 - mask: - - MapGrid - layer: - - MapGrid - mass: 48 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-40 - - 16,-38 - - 15,-38 - - 15,-40 - id: grid_chunk-15--40 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-38 - - 16,-37 - - 0,-37 - - 0,-38 - id: grid_chunk-0--38 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 6,-37 - - 6,-34 - - 0,-34 - - 0,-37 - id: grid_chunk-0--37 - mask: - - MapGrid - layer: - - MapGrid - mass: 72 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-37 - - 16,-34 - - 9,-34 - - 9,-37 - id: grid_chunk-9--37 - mask: - - MapGrid - layer: - - MapGrid - mass: 84 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-34 - - 16,-33 - - 0,-33 - - 0,-34 - id: grid_chunk-0--34 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 7,-33 - - 7,-32 - - 0,-32 - - 0,-33 - id: grid_chunk-0--33 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-33 - - 16,-32 - - 8,-32 - - 8,-33 - id: grid_chunk-8--33 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 27,-48 - - 27,-46 - - 16,-46 - - 16,-48 - id: grid_chunk-16--48 - mask: - - MapGrid - layer: - - MapGrid - mass: 88 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,-48 - - 32,-46 - - 28,-46 - - 28,-48 - id: grid_chunk-28--48 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,-46 - - 32,-32 - - 16,-32 - - 16,-46 - id: grid_chunk-16--46 - mask: - - MapGrid - layer: - - MapGrid - mass: 896 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,32 - - 16,34 - - 5,34 - - 5,32 - id: grid_chunk-5-32 - mask: - - MapGrid - layer: - - MapGrid - mass: 88 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 6,34 - - 6,36 - - 5,36 - - 5,34 - id: grid_chunk-5-34 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,34 - - 16,36 - - 10,36 - - 10,34 - id: grid_chunk-10-34 - mask: - - MapGrid - layer: - - MapGrid - mass: 48 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,36 - - 16,38 - - 5,38 - - 5,36 - id: grid_chunk-5-36 - mask: - - MapGrid - layer: - - MapGrid - mass: 88 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 6,38 - - 6,39 - - 5,39 - - 5,38 - id: grid_chunk-5-38 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,38 - - 16,39 - - 12,39 - - 12,38 - id: grid_chunk-12-38 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,39 - - 16,40 - - 5,40 - - 5,39 - id: grid_chunk-5-39 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 6,40 - - 6,42 - - 5,42 - - 5,40 - id: grid_chunk-5-40 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,40 - - 16,42 - - 12,42 - - 12,40 - id: grid_chunk-12-40 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,42 - - 16,43 - - 5,43 - - 5,42 - id: grid_chunk-5-42 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 6,43 - - 6,47 - - 5,47 - - 5,43 - id: grid_chunk-5-43 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,47 - - 16,48 - - 5,48 - - 5,47 - id: grid_chunk-5-47 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,32 - - 32,43 - - 16,43 - - 16,32 - id: grid_chunk-16-32 - mask: - - MapGrid - layer: - - MapGrid - mass: 704 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,43 - - 32,44 - - 17,44 - - 17,43 - id: grid_chunk-17-43 - mask: - - MapGrid - layer: - - MapGrid - mass: 60 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,44 - - 32,47 - - 18,47 - - 18,44 - id: grid_chunk-18-44 - mask: - - MapGrid - layer: - - MapGrid - mass: 168 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,47 - - 32,48 - - 16,48 - - 16,47 - id: grid_chunk-16-47 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,0 - - 80,14 - - 64,14 - - 64,0 - id: grid_chunk-64-0 - mask: - - MapGrid - layer: - - MapGrid - mass: 896 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 77,14 - - 77,15 - - 64,15 - - 64,14 - id: grid_chunk-64-14 - mask: - - MapGrid - layer: - - MapGrid - mass: 52 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 76,15 - - 76,16 - - 64,16 - - 64,15 - id: grid_chunk-64-15 - mask: - - MapGrid - layer: - - MapGrid - mass: 48 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,16 - - 80,18 - - 79,18 - - 79,16 - id: grid_chunk-79-16 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 76,16 - - 76,19 - - 64,19 - - 64,16 - id: grid_chunk-64-16 - mask: - - MapGrid - layer: - - MapGrid - mass: 144 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,18 - - 80,19 - - 77,19 - - 77,18 - id: grid_chunk-77-18 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 78,19 - - 78,20 - - 64,20 - - 64,19 - id: grid_chunk-64-19 - mask: - - MapGrid - layer: - - MapGrid - mass: 56 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 74,20 - - 74,24 - - 64,24 - - 64,20 - id: grid_chunk-64-20 - mask: - - MapGrid - layer: - - MapGrid - mass: 160 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 77,20 - - 77,24 - - 76,24 - - 76,20 - id: grid_chunk-76-20 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,24 - - 80,25 - - 64,25 - - 64,24 - id: grid_chunk-64-24 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 75,25 - - 75,26 - - 64,26 - - 64,25 - id: grid_chunk-64-25 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 74,26 - - 74,30 - - 64,30 - - 64,26 - id: grid_chunk-64-26 - mask: - - MapGrid - layer: - - MapGrid - mass: 160 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 74,30 - - 74,32 - - 65,32 - - 65,30 - id: grid_chunk-65-30 - mask: - - MapGrid - layer: - - MapGrid - mass: 72 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 82,16 - - 82,17 - - 80,17 - - 80,16 - id: grid_chunk-80-16 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 91,0 - - 91,3 - - 80,3 - - 80,0 - id: grid_chunk-80-0 - mask: - - MapGrid - layer: - - MapGrid - mass: 132 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 88,3 - - 88,6 - - 80,6 - - 80,3 - id: grid_chunk-80-3 - mask: - - MapGrid - layer: - - MapGrid - mass: 96 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 91,6 - - 91,7 - - 80,7 - - 80,6 - id: grid_chunk-80-6 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 87,7 - - 87,9 - - 80,9 - - 80,7 - id: grid_chunk-80-7 - mask: - - MapGrid - layer: - - MapGrid - mass: 56 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 88,9 - - 88,14 - - 80,14 - - 80,9 - id: grid_chunk-80-9 - mask: - - MapGrid - layer: - - MapGrid - mass: 160 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 90,7 - - 90,14 - - 89,14 - - 89,7 - id: grid_chunk-89-7 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 93,0 - - 93,14 - - 92,14 - - 92,0 - id: grid_chunk-92-0 - mask: - - MapGrid - layer: - - MapGrid - mass: 56 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 93,14 - - 93,15 - - 84,15 - - 84,14 - id: grid_chunk-84-14 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 85,15 - - 85,16 - - 81,16 - - 81,15 - id: grid_chunk-81-15 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 88,-26 - - 88,-25 - - 80,-25 - - 80,-26 - id: grid_chunk-80--26 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 81,-25 - - 81,-24 - - 80,-24 - - 80,-25 - id: grid_chunk-80--25 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 88,-25 - - 88,-24 - - 87,-24 - - 87,-25 - id: grid_chunk-87--25 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 88,-24 - - 88,-23 - - 86,-23 - - 86,-24 - id: grid_chunk-86--24 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 96,-23 - - 96,-22 - - 87,-22 - - 87,-23 - id: grid_chunk-87--23 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 96,-22 - - 96,-19 - - 84,-19 - - 84,-22 - id: grid_chunk-84--22 - mask: - - MapGrid - layer: - - MapGrid - mass: 144 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 96,-19 - - 96,-18 - - 87,-18 - - 87,-19 - id: grid_chunk-87--19 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 96,-18 - - 96,-17 - - 80,-17 - - 80,-18 - id: grid_chunk-80--18 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 91,-17 - - 91,-16 - - 80,-16 - - 80,-17 - id: grid_chunk-80--17 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 59,-32 - - 59,-26 - - 58,-26 - - 58,-32 - id: grid_chunk-58--32 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 62,-26 - - 62,-25 - - 58,-25 - - 58,-26 - id: grid_chunk-58--26 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,-26 - - 64,-25 - - 63,-25 - - 63,-26 - id: grid_chunk-63--26 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,-25 - - 64,-24 - - 58,-24 - - 58,-25 - id: grid_chunk-58--25 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 56,-32 - - 56,-21 - - 48,-21 - - 48,-32 - id: grid_chunk-48--32 - mask: - - MapGrid - layer: - - MapGrid - mass: 352 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,-21 - - 64,-20 - - 56,-20 - - 56,-21 - id: grid_chunk-56--21 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,-20 - - 64,-16 - - 48,-16 - - 48,-20 - id: grid_chunk-48--20 - mask: - - MapGrid - layer: - - MapGrid - mass: 256 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 68,-26 - - 68,-25 - - 64,-25 - - 64,-26 - id: grid_chunk-64--26 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 75,-26 - - 75,-25 - - 69,-25 - - 69,-26 - id: grid_chunk-69--26 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,-26 - - 80,-25 - - 79,-25 - - 79,-26 - id: grid_chunk-79--26 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,-25 - - 80,-24 - - 64,-24 - - 64,-25 - id: grid_chunk-64--25 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 73,-21 - - 73,-18 - - 64,-18 - - 64,-21 - id: grid_chunk-64--21 - mask: - - MapGrid - layer: - - MapGrid - mass: 108 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,-18 - - 80,-16 - - 64,-16 - - 64,-18 - id: grid_chunk-64--18 - mask: - - MapGrid - layer: - - MapGrid - mass: 128 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 41,-62 - - 41,-61 - - 32,-61 - - 32,-62 - id: grid_chunk-32--62 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-61 - - 48,-60 - - 40,-60 - - 40,-61 - id: grid_chunk-40--61 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 41,-60 - - 41,-59 - - 40,-59 - - 40,-60 - id: grid_chunk-40--60 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 42,-59 - - 42,-58 - - 32,-58 - - 32,-59 - id: grid_chunk-32--59 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 42,-58 - - 42,-56 - - 40,-56 - - 40,-58 - id: grid_chunk-40--58 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-60 - - 48,-56 - - 47,-56 - - 47,-60 - id: grid_chunk-47--60 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-56 - - 48,-55 - - 40,-55 - - 40,-56 - id: grid_chunk-40--56 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 42,-55 - - 42,-52 - - 40,-52 - - 40,-55 - id: grid_chunk-40--55 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-55 - - 48,-52 - - 47,-52 - - 47,-55 - id: grid_chunk-47--55 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-52 - - 48,-51 - - 40,-51 - - 40,-52 - id: grid_chunk-40--52 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 42,-51 - - 42,-50 - - 40,-50 - - 40,-51 - id: grid_chunk-40--51 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 42,-50 - - 42,-49 - - 32,-49 - - 32,-50 - id: grid_chunk-32--50 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-51 - - 48,-49 - - 47,-49 - - 47,-51 - id: grid_chunk-47--51 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,-49 - - 48,-48 - - 32,-48 - - 32,-49 - id: grid_chunk-32--49 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 51,-48 - - 51,-41 - - 50,-41 - - 50,-48 - id: grid_chunk-50--48 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 53,-41 - - 53,-40 - - 48,-40 - - 48,-41 - id: grid_chunk-48--41 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 53,-40 - - 53,-36 - - 52,-36 - - 52,-40 - id: grid_chunk-52--40 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 56,-36 - - 56,-35 - - 52,-35 - - 52,-36 - id: grid_chunk-52--36 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 53,-35 - - 53,-34 - - 52,-34 - - 52,-35 - id: grid_chunk-52--35 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 59,-35 - - 59,-34 - - 55,-34 - - 55,-35 - id: grid_chunk-55--35 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 49,-40 - - 49,-33 - - 48,-33 - - 48,-40 - id: grid_chunk-48--40 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 50,-33 - - 50,-32 - - 48,-32 - - 48,-33 - id: grid_chunk-48--33 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 59,-34 - - 59,-32 - - 58,-32 - - 58,-34 - id: grid_chunk-58--34 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 49,-56 - - 49,-55 - - 48,-55 - - 48,-56 - id: grid_chunk-48--56 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 51,-52 - - 51,-51 - - 48,-51 - - 48,-52 - id: grid_chunk-48--52 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 51,-51 - - 51,-48 - - 50,-48 - - 50,-51 - id: grid_chunk-50--51 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,-62 - - 32,-61 - - 29,-61 - - 29,-62 - id: grid_chunk-29--62 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 23,-61 - - 23,-60 - - 20,-60 - - 20,-61 - id: grid_chunk-20--61 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 31,-61 - - 31,-60 - - 29,-60 - - 29,-61 - id: grid_chunk-29--61 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 31,-60 - - 31,-59 - - 16,-59 - - 16,-60 - id: grid_chunk-16--60 - mask: - - MapGrid - layer: - - MapGrid - mass: 60 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,-59 - - 32,-58 - - 16,-58 - - 16,-59 - id: grid_chunk-16--59 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 31,-58 - - 31,-56 - - 16,-56 - - 16,-58 - id: grid_chunk-16--58 - mask: - - MapGrid - layer: - - MapGrid - mass: 120 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 27,-56 - - 27,-55 - - 16,-55 - - 16,-56 - id: grid_chunk-16--56 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 27,-55 - - 27,-54 - - 25,-54 - - 25,-55 - id: grid_chunk-25--55 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 31,-56 - - 31,-54 - - 28,-54 - - 28,-56 - id: grid_chunk-28--56 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 31,-54 - - 31,-53 - - 25,-53 - - 25,-54 - id: grid_chunk-25--54 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 18,-55 - - 18,-52 - - 16,-52 - - 16,-55 - id: grid_chunk-16--55 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 22,-55 - - 22,-52 - - 21,-52 - - 21,-55 - id: grid_chunk-21--55 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 27,-53 - - 27,-52 - - 25,-52 - - 25,-53 - id: grid_chunk-25--53 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 27,-52 - - 27,-51 - - 16,-51 - - 16,-52 - id: grid_chunk-16--52 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 31,-53 - - 31,-51 - - 28,-51 - - 28,-53 - id: grid_chunk-28--53 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 31,-51 - - 31,-50 - - 25,-50 - - 25,-51 - id: grid_chunk-25--51 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 18,-51 - - 18,-48 - - 16,-48 - - 16,-51 - id: grid_chunk-16--51 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 22,-51 - - 22,-48 - - 21,-48 - - 21,-51 - id: grid_chunk-21--51 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 27,-50 - - 27,-48 - - 25,-48 - - 25,-50 - id: grid_chunk-25--50 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 32,-50 - - 32,-48 - - 28,-48 - - 28,-50 - id: grid_chunk-28--50 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 12,-62 - - 12,-61 - - 11,-61 - - 11,-62 - id: grid_chunk-11--62 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 13,-61 - - 13,-60 - - 10,-60 - - 10,-61 - id: grid_chunk-10--61 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-60 - - 16,-56 - - 11,-56 - - 11,-60 - id: grid_chunk-11--60 - mask: - - MapGrid - layer: - - MapGrid - mass: 80 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 15,-56 - - 15,-55 - - 10,-55 - - 10,-56 - id: grid_chunk-10--56 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 15,-55 - - 15,-54 - - 11,-54 - - 11,-55 - id: grid_chunk-11--55 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-54 - - 16,-53 - - 11,-53 - - 11,-54 - id: grid_chunk-11--54 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 15,-53 - - 15,-51 - - 10,-51 - - 10,-53 - id: grid_chunk-10--53 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-51 - - 16,-50 - - 10,-50 - - 10,-51 - id: grid_chunk-10--51 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 15,-50 - - 15,-48 - - 10,-48 - - 10,-50 - id: grid_chunk-10--50 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -11,-32 - - -11,-31 - - -14,-31 - - -14,-32 - id: grid_chunk--14--32 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -3,-32 - - -3,-31 - - -10,-31 - - -10,-32 - id: grid_chunk--10--32 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-32 - - 0,-31 - - -2,-31 - - -2,-32 - id: grid_chunk--2--32 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-31 - - 0,-30 - - -16,-30 - - -16,-31 - id: grid_chunk--16--31 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-30 - - 0,-29 - - -3,-29 - - -3,-30 - id: grid_chunk--3--30 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-29 - - 0,-28 - - -16,-28 - - -16,-29 - id: grid_chunk--16--29 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-28 - - 0,-27 - - -2,-27 - - -2,-28 - id: grid_chunk--2--28 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-27 - - 0,-26 - - -1,-26 - - -1,-27 - id: grid_chunk--1--27 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-18 - - 0,-16 - - -1,-16 - - -1,-18 - id: grid_chunk--1--18 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -16,-31 - - -16,-28 - - -17,-28 - - -17,-31 - id: grid_chunk--17--31 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -2,-43 - - -2,-42 - - -16,-42 - - -16,-43 - id: grid_chunk--16--43 - mask: - - MapGrid - layer: - - MapGrid - mass: 56 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -14,-42 - - -14,-41 - - -16,-41 - - -16,-42 - id: grid_chunk--16--42 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -2,-42 - - -2,-41 - - -3,-41 - - -3,-42 - id: grid_chunk--3--42 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-42 - - 0,-41 - - -1,-41 - - -1,-42 - id: grid_chunk--1--42 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -2,-41 - - -2,-40 - - -16,-40 - - -16,-41 - id: grid_chunk--16--41 - mask: - - MapGrid - layer: - - MapGrid - mass: 56 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -11,-40 - - -11,-39 - - -14,-39 - - -14,-40 - id: grid_chunk--14--40 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -7,-40 - - -7,-39 - - -10,-39 - - -10,-40 - id: grid_chunk--10--40 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -7,-39 - - -7,-38 - - -14,-38 - - -14,-39 - id: grid_chunk--14--39 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -3,-40 - - -3,-38 - - -6,-38 - - -6,-40 - id: grid_chunk--6--40 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -3,-38 - - -3,-37 - - -16,-37 - - -16,-38 - id: grid_chunk--16--38 - mask: - - MapGrid - layer: - - MapGrid - mass: 52 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -14,-37 - - -14,-36 - - -16,-36 - - -16,-37 - id: grid_chunk--16--37 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -6,-37 - - -6,-36 - - -13,-36 - - -13,-37 - id: grid_chunk--13--37 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-37 - - 0,-36 - - -5,-36 - - -5,-37 - id: grid_chunk--5--37 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-36 - - 0,-35 - - -16,-35 - - -16,-36 - id: grid_chunk--16--36 - mask: - - MapGrid - layer: - - MapGrid - mass: 64 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -15,-35 - - -15,-34 - - -16,-34 - - -16,-35 - id: grid_chunk--16--35 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -12,-35 - - -12,-34 - - -14,-34 - - -14,-35 - id: grid_chunk--14--35 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -8,-35 - - -8,-34 - - -11,-34 - - -11,-35 - id: grid_chunk--11--35 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-35 - - 0,-34 - - -5,-34 - - -5,-35 - id: grid_chunk--5--35 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -3,-34 - - -3,-33 - - -16,-33 - - -16,-34 - id: grid_chunk--16--34 - mask: - - MapGrid - layer: - - MapGrid - mass: 52 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -7,-33 - - -7,-32 - - -14,-32 - - -14,-33 - id: grid_chunk--14--33 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -3,-33 - - -3,-32 - - -6,-32 - - -6,-33 - id: grid_chunk--6--33 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 0,-33 - - 0,-32 - - -2,-32 - - -2,-33 - id: grid_chunk--2--33 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -16,-43 - - -16,-40 - - -17,-40 - - -17,-43 - id: grid_chunk--17--43 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -16,-39 - - -16,-37 - - -19,-37 - - -19,-39 - id: grid_chunk--19--39 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -18,-37 - - -18,-33 - - -19,-33 - - -19,-37 - id: grid_chunk--19--37 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -16,-37 - - -16,-33 - - -17,-33 - - -17,-37 - id: grid_chunk--17--37 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - -16,-33 - - -16,-32 - - -19,-32 - - -19,-33 - id: grid_chunk--19--33 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 7,-32 - - 7,-27 - - 0,-27 - - 0,-32 - id: grid_chunk-0--32 - mask: - - MapGrid - layer: - - MapGrid - mass: 140 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 7,-27 - - 7,-26 - - 3,-26 - - 3,-27 - id: grid_chunk-3--27 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-32 - - 16,-26 - - 8,-26 - - 8,-32 - id: grid_chunk-8--32 - mask: - - MapGrid - layer: - - MapGrid - mass: 192 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-26 - - 16,-23 - - 3,-23 - - 3,-26 - id: grid_chunk-3--26 - mask: - - MapGrid - layer: - - MapGrid - mass: 156 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-23 - - 16,-22 - - 2,-22 - - 2,-23 - id: grid_chunk-2--23 - mask: - - MapGrid - layer: - - MapGrid - mass: 56 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 3,-22 - - 3,-18 - - 2,-18 - - 2,-22 - id: grid_chunk-2--22 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 6,-22 - - 6,-18 - - 5,-18 - - 5,-22 - id: grid_chunk-5--22 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-22 - - 16,-18 - - 7,-18 - - 7,-22 - id: grid_chunk-7--22 - mask: - - MapGrid - layer: - - MapGrid - mass: 144 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 16,-18 - - 16,-16 - - 0,-16 - - 0,-18 - id: grid_chunk-0--18 - mask: - - MapGrid - layer: - - MapGrid - mass: 128 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 68,48 - - 68,49 - - 64,49 - - 64,48 - id: grid_chunk-64-48 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 65,49 - - 65,50 - - 64,50 - - 64,49 - id: grid_chunk-64-49 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 73,48 - - 73,51 - - 72,51 - - 72,48 - id: grid_chunk-72-48 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,48 - - 80,51 - - 77,51 - - 77,48 - id: grid_chunk-77-48 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,51 - - 80,52 - - 72,52 - - 72,51 - id: grid_chunk-72-51 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 88,48 - - 88,49 - - 80,49 - - 80,48 - id: grid_chunk-80-48 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 92,48 - - 92,49 - - 89,49 - - 89,48 - id: grid_chunk-89-48 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 84,49 - - 84,50 - - 80,50 - - 80,49 - id: grid_chunk-80-49 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 88,49 - - 88,50 - - 85,50 - - 85,49 - id: grid_chunk-85-49 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 91,49 - - 91,50 - - 89,50 - - 89,49 - id: grid_chunk-89-49 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 86,50 - - 86,51 - - 80,51 - - 80,50 - id: grid_chunk-80-50 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 91,50 - - 91,51 - - 87,51 - - 87,50 - id: grid_chunk-87-50 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 90,51 - - 90,52 - - 80,52 - - 80,51 - id: grid_chunk-80-51 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 91,38 - - 91,39 - - 80,39 - - 80,38 - id: grid_chunk-80-38 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 91,39 - - 91,40 - - 88,40 - - 88,39 - id: grid_chunk-88-39 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 87,40 - - 87,41 - - 81,41 - - 81,40 - id: grid_chunk-81-40 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 84,41 - - 84,42 - - 81,42 - - 81,41 - id: grid_chunk-81-41 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 88,41 - - 88,42 - - 85,42 - - 85,41 - id: grid_chunk-85-41 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 91,40 - - 91,42 - - 89,42 - - 89,40 - id: grid_chunk-89-40 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 88,42 - - 88,43 - - 81,43 - - 81,42 - id: grid_chunk-81-42 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 92,42 - - 92,43 - - 89,43 - - 89,42 - id: grid_chunk-89-42 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 92,43 - - 92,44 - - 81,44 - - 81,43 - id: grid_chunk-81-43 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 85,44 - - 85,45 - - 82,45 - - 82,44 - id: grid_chunk-82-44 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 90,44 - - 90,45 - - 86,45 - - 86,44 - id: grid_chunk-86-44 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 90,45 - - 90,46 - - 80,46 - - 80,45 - id: grid_chunk-80-45 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 81,46 - - 81,47 - - 80,47 - - 80,46 - id: grid_chunk-80-46 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 87,46 - - 87,47 - - 82,47 - - 82,46 - id: grid_chunk-82-46 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 90,46 - - 90,47 - - 89,47 - - 89,46 - id: grid_chunk-89-46 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 84,47 - - 84,48 - - 81,48 - - 81,47 - id: grid_chunk-81-47 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 89,47 - - 89,48 - - 85,48 - - 85,47 - id: grid_chunk-85-47 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 92,44 - - 92,48 - - 91,48 - - 91,44 - id: grid_chunk-91-44 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 74,32 - - 74,38 - - 65,38 - - 65,32 - id: grid_chunk-65-32 - mask: - - MapGrid - layer: - - MapGrid - mass: 216 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,38 - - 80,39 - - 65,39 - - 65,38 - id: grid_chunk-65-38 - mask: - - MapGrid - layer: - - MapGrid - mass: 60 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 74,39 - - 74,40 - - 65,40 - - 65,39 - id: grid_chunk-65-39 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,40 - - 80,41 - - 65,41 - - 65,40 - id: grid_chunk-65-40 - mask: - - MapGrid - layer: - - MapGrid - mass: 60 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 73,41 - - 73,42 - - 65,42 - - 65,41 - id: grid_chunk-65-41 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 72,42 - - 72,43 - - 65,43 - - 65,42 - id: grid_chunk-65-42 - mask: - - MapGrid - layer: - - MapGrid - mass: 28 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 73,43 - - 73,44 - - 65,44 - - 65,43 - id: grid_chunk-65-43 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,41 - - 80,44 - - 77,44 - - 77,41 - id: grid_chunk-77-41 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 75,44 - - 75,45 - - 65,45 - - 65,44 - id: grid_chunk-65-44 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 79,44 - - 79,45 - - 78,45 - - 78,44 - id: grid_chunk-78-44 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,45 - - 80,47 - - 64,47 - - 64,45 - id: grid_chunk-64-45 - mask: - - MapGrid - layer: - - MapGrid - mass: 128 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 73,47 - - 73,48 - - 64,48 - - 64,47 - id: grid_chunk-64-47 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 80,47 - - 80,48 - - 77,48 - - 77,47 - id: grid_chunk-77-47 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,48 - - 48,49 - - 45,49 - - 45,48 - id: grid_chunk-45-48 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 34,48 - - 34,50 - - 32,50 - - 32,48 - id: grid_chunk-32-48 - mask: - - MapGrid - layer: - - MapGrid - mass: 16 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 34,50 - - 34,51 - - 33,51 - - 33,50 - id: grid_chunk-33-50 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 43,48 - - 43,51 - - 42,51 - - 42,48 - id: grid_chunk-42-48 - mask: - - MapGrid - layer: - - MapGrid - mass: 12 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 34,51 - - 34,52 - - 32,52 - - 32,51 - id: grid_chunk-32-51 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 48,51 - - 48,52 - - 42,52 - - 42,51 - id: grid_chunk-42-51 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 43,52 - - 43,53 - - 33,53 - - 33,52 - id: grid_chunk-33-52 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 34,53 - - 34,54 - - 32,54 - - 32,53 - id: grid_chunk-32-53 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 33,54 - - 33,55 - - 32,55 - - 32,54 - id: grid_chunk-32-54 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 64,48 - - 64,50 - - 48,50 - - 48,48 - id: grid_chunk-48-48 - mask: - - MapGrid - layer: - - MapGrid - mass: 128 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 50,50 - - 50,51 - - 49,51 - - 49,50 - id: grid_chunk-49-50 - mask: - - MapGrid - layer: - - MapGrid - mass: 4 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 50,51 - - 50,52 - - 48,52 - - 48,51 - id: grid_chunk-48-51 - mask: - - MapGrid - layer: - - MapGrid - mass: 8 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 58,50 - - 58,52 - - 55,52 - - 55,50 - id: grid_chunk-55-50 - mask: - - MapGrid - layer: - - MapGrid - mass: 24 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 91,-16 - - 91,-12 - - 80,-12 - - 80,-16 - id: grid_chunk-80--16 - mask: - - MapGrid - layer: - - MapGrid - mass: 176 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 92,-12 - - 92,-11 - - 80,-11 - - 80,-12 - id: grid_chunk-80--12 - mask: - - MapGrid - layer: - - MapGrid - mass: 48 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 84,-9 - - 84,-7 - - 80,-7 - - 80,-9 - id: grid_chunk-80--9 - mask: - - MapGrid - layer: - - MapGrid - mass: 32 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 92,-11 - - 92,-7 - - 85,-7 - - 85,-11 - id: grid_chunk-85--11 - mask: - - MapGrid - layer: - - MapGrid - mass: 112 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 92,-7 - - 92,-2 - - 80,-2 - - 80,-7 - id: grid_chunk-80--7 - mask: - - MapGrid - layer: - - MapGrid - mass: 240 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 93,-2 - - 93,0 - - 80,0 - - 80,-2 - id: grid_chunk-80--2 - mask: - - MapGrid - layer: - - MapGrid - mass: 104 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 112,-16 - - 112,-14 - - 101,-14 - - 101,-16 - id: grid_chunk-101--16 - mask: - - MapGrid - layer: - - MapGrid - mass: 88 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 109,-14 - - 109,-11 - - 101,-11 - - 101,-14 - id: grid_chunk-101--14 - mask: - - MapGrid - layer: - - MapGrid - mass: 96 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 109,-11 - - 109,-10 - - 100,-10 - - 100,-11 - id: grid_chunk-100--11 - mask: - - MapGrid - layer: - - MapGrid - mass: 36 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 109,-10 - - 109,-9 - - 104,-9 - - 104,-10 - id: grid_chunk-104--10 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 112,-14 - - 112,-9 - - 110,-9 - - 110,-14 - id: grid_chunk-110--14 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 101,-23 - - 101,-22 - - 96,-22 - - 96,-23 - id: grid_chunk-96--23 - mask: - - MapGrid - layer: - - MapGrid - mass: 20 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 109,-26 - - 109,-22 - - 104,-22 - - 104,-26 - id: grid_chunk-104--26 - mask: - - MapGrid - layer: - - MapGrid - mass: 80 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 109,-22 - - 109,-21 - - 96,-21 - - 96,-22 - id: grid_chunk-96--22 - mask: - - MapGrid - layer: - - MapGrid - mass: 52 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 112,-26 - - 112,-21 - - 110,-21 - - 110,-26 - id: grid_chunk-110--26 - mask: - - MapGrid - layer: - - MapGrid - mass: 40 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 112,-21 - - 112,-17 - - 96,-17 - - 96,-21 - id: grid_chunk-96--21 - mask: - - MapGrid - layer: - - MapGrid - mass: 256 - restitution: 0.1 - - shape: !type:PolygonShape - vertices: - - 112,-17 - - 112,-16 - - 101,-16 - - 101,-17 - id: grid_chunk-101--17 - mask: - - MapGrid - layer: - - MapGrid - mass: 44 - restitution: 0.1 bodyType: Dynamic type: Physics - gravityShakeSound: !type:SoundPathSpecifier @@ -12666,6 +8271,4402 @@ entities: - 0 - 0 type: GridAtmosphere + - fixtures: + - shape: !type:PolygonShape + vertices: + - 0,-16 + - 0,-14 + - -1,-14 + - -1,-16 + id: grid_chunk--1--16 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-14 + - 0,-11 + - -8,-11 + - -8,-14 + id: grid_chunk--8--14 + mask: + - MapGrid + layer: + - MapGrid + mass: 96 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-11 + - 0,-10 + - -9,-10 + - -9,-11 + id: grid_chunk--9--11 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-10 + - 0,-6 + - -11,-6 + - -11,-10 + id: grid_chunk--11--10 + mask: + - MapGrid + layer: + - MapGrid + mass: 176 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-6 + - 0,-5 + - -16,-5 + - -16,-6 + id: grid_chunk--16--6 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-5 + - 0,-3 + - -10,-3 + - -10,-5 + id: grid_chunk--10--5 + mask: + - MapGrid + layer: + - MapGrid + mass: 80 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-3 + - 0,0 + - -11,0 + - -11,-3 + id: grid_chunk--11--3 + mask: + - MapGrid + layer: + - MapGrid + mass: 132 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,0 + - 0,2 + - -10,2 + - -10,0 + id: grid_chunk--10-0 + mask: + - MapGrid + layer: + - MapGrid + mass: 80 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,3 + - 0,6 + - -2,6 + - -2,3 + id: grid_chunk--2-3 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,9 + - 0,12 + - -2,12 + - -2,9 + id: grid_chunk--2-9 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,13 + - 0,16 + - -4,16 + - -4,13 + id: grid_chunk--4-13 + mask: + - MapGrid + layer: + - MapGrid + mass: 48 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,0 + - 16,16 + - 0,16 + - 0,0 + id: grid_chunk-0-0 + mask: + - MapGrid + layer: + - MapGrid + mass: 1024 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-16 + - 16,0 + - 0,0 + - 0,-16 + id: grid_chunk-0--16 + mask: + - MapGrid + layer: + - MapGrid + mass: 1024 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -16,-6 + - -16,-5 + - -21,-5 + - -21,-6 + id: grid_chunk--21--6 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -16,-5 + - -16,-4 + - -18,-4 + - -18,-5 + id: grid_chunk--18--5 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -17,-4 + - -17,-3 + - -19,-3 + - -19,-4 + id: grid_chunk--19--4 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -18,-3 + - -18,0 + - -19,0 + - -19,-3 + id: grid_chunk--19--3 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -17,0 + - -17,1 + - -19,1 + - -19,0 + id: grid_chunk--19-0 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -16,1 + - -16,2 + - -18,2 + - -18,1 + id: grid_chunk--18-1 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -11,-32 + - -11,-31 + - -14,-31 + - -14,-32 + id: grid_chunk--14--32 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -3,-32 + - -3,-31 + - -10,-31 + - -10,-32 + id: grid_chunk--10--32 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-32 + - 0,-31 + - -2,-31 + - -2,-32 + id: grid_chunk--2--32 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-31 + - 0,-30 + - -16,-30 + - -16,-31 + id: grid_chunk--16--31 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-30 + - 0,-29 + - -3,-29 + - -3,-30 + id: grid_chunk--3--30 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-29 + - 0,-28 + - -16,-28 + - -16,-29 + id: grid_chunk--16--29 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-28 + - 0,-27 + - -2,-27 + - -2,-28 + id: grid_chunk--2--28 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-27 + - 0,-26 + - -1,-26 + - -1,-27 + id: grid_chunk--1--27 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-18 + - 0,-16 + - -1,-16 + - -1,-18 + id: grid_chunk--1--18 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 7,-32 + - 7,-27 + - 0,-27 + - 0,-32 + id: grid_chunk-0--32 + mask: + - MapGrid + layer: + - MapGrid + mass: 140 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 7,-27 + - 7,-26 + - 3,-26 + - 3,-27 + id: grid_chunk-3--27 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-32 + - 16,-26 + - 8,-26 + - 8,-32 + id: grid_chunk-8--32 + mask: + - MapGrid + layer: + - MapGrid + mass: 192 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-26 + - 16,-23 + - 3,-23 + - 3,-26 + id: grid_chunk-3--26 + mask: + - MapGrid + layer: + - MapGrid + mass: 156 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-23 + - 16,-22 + - 2,-22 + - 2,-23 + id: grid_chunk-2--23 + mask: + - MapGrid + layer: + - MapGrid + mass: 56 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 3,-22 + - 3,-18 + - 2,-18 + - 2,-22 + id: grid_chunk-2--22 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 6,-22 + - 6,-18 + - 5,-18 + - 5,-22 + id: grid_chunk-5--22 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-22 + - 16,-18 + - 7,-18 + - 7,-22 + id: grid_chunk-7--22 + mask: + - MapGrid + layer: + - MapGrid + mass: 144 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-18 + - 16,-16 + - 0,-16 + - 0,-18 + id: grid_chunk-0--18 + mask: + - MapGrid + layer: + - MapGrid + mass: 128 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,16 + - 0,21 + - -4,21 + - -4,16 + id: grid_chunk--4-16 + mask: + - MapGrid + layer: + - MapGrid + mass: 80 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,16 + - 16,21 + - 4,21 + - 4,16 + id: grid_chunk-4-16 + mask: + - MapGrid + layer: + - MapGrid + mass: 240 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,21 + - 16,22 + - 6,22 + - 6,21 + id: grid_chunk-6-21 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,22 + - 16,29 + - 4,29 + - 4,22 + id: grid_chunk-4-22 + mask: + - MapGrid + layer: + - MapGrid + mass: 336 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,29 + - 16,32 + - 5,32 + - 5,29 + id: grid_chunk-5-29 + mask: + - MapGrid + layer: + - MapGrid + mass: 132 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,0 + - 32,16 + - 16,16 + - 16,0 + id: grid_chunk-16-0 + mask: + - MapGrid + layer: + - MapGrid + mass: 1024 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,-16 + - 32,0 + - 16,0 + - 16,-16 + id: grid_chunk-16--16 + mask: + - MapGrid + layer: + - MapGrid + mass: 1024 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,-32 + - 32,-16 + - 16,-16 + - 16,-32 + id: grid_chunk-16--32 + mask: + - MapGrid + layer: + - MapGrid + mass: 1024 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 15,-48 + - 15,-47 + - 10,-47 + - 10,-48 + id: grid_chunk-10--48 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 15,-47 + - 15,-46 + - 11,-46 + - 11,-47 + id: grid_chunk-11--47 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-46 + - 16,-45 + - 11,-45 + - 11,-46 + id: grid_chunk-11--46 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-45 + - 16,-44 + - 12,-44 + - 12,-45 + id: grid_chunk-12--45 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 3,-42 + - 3,-41 + - 0,-41 + - 0,-42 + id: grid_chunk-0--42 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-44 + - 16,-40 + - 8,-40 + - 8,-44 + id: grid_chunk-8--44 + mask: + - MapGrid + layer: + - MapGrid + mass: 128 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 2,-41 + - 2,-39 + - 0,-39 + - 0,-41 + id: grid_chunk-0--41 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 1,-39 + - 1,-38 + - 0,-38 + - 0,-39 + id: grid_chunk-0--39 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 14,-40 + - 14,-38 + - 8,-38 + - 8,-40 + id: grid_chunk-8--40 + mask: + - MapGrid + layer: + - MapGrid + mass: 48 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-40 + - 16,-38 + - 15,-38 + - 15,-40 + id: grid_chunk-15--40 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-38 + - 16,-37 + - 0,-37 + - 0,-38 + id: grid_chunk-0--38 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 6,-37 + - 6,-34 + - 0,-34 + - 0,-37 + id: grid_chunk-0--37 + mask: + - MapGrid + layer: + - MapGrid + mass: 72 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-37 + - 16,-34 + - 9,-34 + - 9,-37 + id: grid_chunk-9--37 + mask: + - MapGrid + layer: + - MapGrid + mass: 84 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-34 + - 16,-33 + - 0,-33 + - 0,-34 + id: grid_chunk-0--34 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 7,-33 + - 7,-32 + - 0,-32 + - 0,-33 + id: grid_chunk-0--33 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-33 + - 16,-32 + - 8,-32 + - 8,-33 + id: grid_chunk-8--33 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 27,-48 + - 27,-46 + - 16,-46 + - 16,-48 + id: grid_chunk-16--48 + mask: + - MapGrid + layer: + - MapGrid + mass: 88 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,-48 + - 32,-46 + - 28,-46 + - 28,-48 + id: grid_chunk-28--48 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,-46 + - 32,-32 + - 16,-32 + - 16,-46 + id: grid_chunk-16--46 + mask: + - MapGrid + layer: + - MapGrid + mass: 896 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-32 + - 48,-17 + - 32,-17 + - 32,-32 + id: grid_chunk-32--32 + mask: + - MapGrid + layer: + - MapGrid + mass: 960 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 43,-17 + - 43,-16 + - 32,-16 + - 32,-17 + id: grid_chunk-32--17 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-17 + - 48,-16 + - 46,-16 + - 46,-17 + id: grid_chunk-46--17 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-48 + - 48,-32 + - 32,-32 + - 32,-48 + id: grid_chunk-32--48 + mask: + - MapGrid + layer: + - MapGrid + mass: 1024 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,16 + - 32,32 + - 16,32 + - 16,16 + id: grid_chunk-16-16 + mask: + - MapGrid + layer: + - MapGrid + mass: 1024 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,0 + - 48,16 + - 32,16 + - 32,0 + id: grid_chunk-32-0 + mask: + - MapGrid + layer: + - MapGrid + mass: 1024 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 43,-16 + - 43,-14 + - 32,-14 + - 32,-16 + id: grid_chunk-32--16 + mask: + - MapGrid + layer: + - MapGrid + mass: 88 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-16 + - 48,-14 + - 46,-14 + - 46,-16 + id: grid_chunk-46--16 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-14 + - 48,0 + - 32,0 + - 32,-14 + id: grid_chunk-32--14 + mask: + - MapGrid + layer: + - MapGrid + mass: 896 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,-62 + - 32,-61 + - 29,-61 + - 29,-62 + id: grid_chunk-29--62 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 23,-61 + - 23,-60 + - 20,-60 + - 20,-61 + id: grid_chunk-20--61 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 31,-61 + - 31,-60 + - 29,-60 + - 29,-61 + id: grid_chunk-29--61 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 31,-60 + - 31,-59 + - 16,-59 + - 16,-60 + id: grid_chunk-16--60 + mask: + - MapGrid + layer: + - MapGrid + mass: 60 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,-59 + - 32,-58 + - 16,-58 + - 16,-59 + id: grid_chunk-16--59 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 31,-58 + - 31,-56 + - 16,-56 + - 16,-58 + id: grid_chunk-16--58 + mask: + - MapGrid + layer: + - MapGrid + mass: 120 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 27,-56 + - 27,-55 + - 16,-55 + - 16,-56 + id: grid_chunk-16--56 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 27,-55 + - 27,-54 + - 25,-54 + - 25,-55 + id: grid_chunk-25--55 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 31,-56 + - 31,-54 + - 28,-54 + - 28,-56 + id: grid_chunk-28--56 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 31,-54 + - 31,-53 + - 25,-53 + - 25,-54 + id: grid_chunk-25--54 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 18,-55 + - 18,-52 + - 16,-52 + - 16,-55 + id: grid_chunk-16--55 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 22,-55 + - 22,-52 + - 21,-52 + - 21,-55 + id: grid_chunk-21--55 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 27,-53 + - 27,-52 + - 25,-52 + - 25,-53 + id: grid_chunk-25--53 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 27,-52 + - 27,-51 + - 16,-51 + - 16,-52 + id: grid_chunk-16--52 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 31,-53 + - 31,-51 + - 28,-51 + - 28,-53 + id: grid_chunk-28--53 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 31,-51 + - 31,-50 + - 25,-50 + - 25,-51 + id: grid_chunk-25--51 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 18,-51 + - 18,-48 + - 16,-48 + - 16,-51 + id: grid_chunk-16--51 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 22,-51 + - 22,-48 + - 21,-48 + - 21,-51 + id: grid_chunk-21--51 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 27,-50 + - 27,-48 + - 25,-48 + - 25,-50 + id: grid_chunk-25--50 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,-50 + - 32,-48 + - 28,-48 + - 28,-50 + id: grid_chunk-28--50 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 41,-62 + - 41,-61 + - 32,-61 + - 32,-62 + id: grid_chunk-32--62 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-61 + - 48,-60 + - 40,-60 + - 40,-61 + id: grid_chunk-40--61 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 41,-60 + - 41,-59 + - 40,-59 + - 40,-60 + id: grid_chunk-40--60 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 42,-59 + - 42,-58 + - 32,-58 + - 32,-59 + id: grid_chunk-32--59 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 42,-58 + - 42,-56 + - 40,-56 + - 40,-58 + id: grid_chunk-40--58 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-60 + - 48,-56 + - 47,-56 + - 47,-60 + id: grid_chunk-47--60 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-56 + - 48,-55 + - 40,-55 + - 40,-56 + id: grid_chunk-40--56 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 42,-55 + - 42,-52 + - 40,-52 + - 40,-55 + id: grid_chunk-40--55 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-55 + - 48,-52 + - 47,-52 + - 47,-55 + id: grid_chunk-47--55 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-52 + - 48,-51 + - 40,-51 + - 40,-52 + id: grid_chunk-40--52 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 42,-51 + - 42,-50 + - 40,-50 + - 40,-51 + id: grid_chunk-40--51 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 42,-50 + - 42,-49 + - 32,-49 + - 32,-50 + id: grid_chunk-32--50 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-51 + - 48,-49 + - 47,-49 + - 47,-51 + id: grid_chunk-47--51 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,-49 + - 48,-48 + - 32,-48 + - 32,-49 + id: grid_chunk-32--49 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 12,-62 + - 12,-61 + - 11,-61 + - 11,-62 + id: grid_chunk-11--62 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 13,-61 + - 13,-60 + - 10,-60 + - 10,-61 + id: grid_chunk-10--61 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-60 + - 16,-56 + - 11,-56 + - 11,-60 + id: grid_chunk-11--60 + mask: + - MapGrid + layer: + - MapGrid + mass: 80 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 15,-56 + - 15,-55 + - 10,-55 + - 10,-56 + id: grid_chunk-10--56 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 15,-55 + - 15,-54 + - 11,-54 + - 11,-55 + id: grid_chunk-11--55 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-54 + - 16,-53 + - 11,-53 + - 11,-54 + id: grid_chunk-11--54 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 15,-53 + - 15,-51 + - 10,-51 + - 10,-53 + id: grid_chunk-10--53 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,-51 + - 16,-50 + - 10,-50 + - 10,-51 + id: grid_chunk-10--51 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 15,-50 + - 15,-48 + - 10,-48 + - 10,-50 + id: grid_chunk-10--50 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -2,-43 + - -2,-42 + - -16,-42 + - -16,-43 + id: grid_chunk--16--43 + mask: + - MapGrid + layer: + - MapGrid + mass: 56 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -14,-42 + - -14,-41 + - -16,-41 + - -16,-42 + id: grid_chunk--16--42 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -2,-42 + - -2,-41 + - -3,-41 + - -3,-42 + id: grid_chunk--3--42 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-42 + - 0,-41 + - -1,-41 + - -1,-42 + id: grid_chunk--1--42 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -2,-41 + - -2,-40 + - -16,-40 + - -16,-41 + id: grid_chunk--16--41 + mask: + - MapGrid + layer: + - MapGrid + mass: 56 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -11,-40 + - -11,-39 + - -14,-39 + - -14,-40 + id: grid_chunk--14--40 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -7,-40 + - -7,-39 + - -10,-39 + - -10,-40 + id: grid_chunk--10--40 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -7,-39 + - -7,-38 + - -14,-38 + - -14,-39 + id: grid_chunk--14--39 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -3,-40 + - -3,-38 + - -6,-38 + - -6,-40 + id: grid_chunk--6--40 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -3,-38 + - -3,-37 + - -16,-37 + - -16,-38 + id: grid_chunk--16--38 + mask: + - MapGrid + layer: + - MapGrid + mass: 52 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -14,-37 + - -14,-36 + - -16,-36 + - -16,-37 + id: grid_chunk--16--37 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -6,-37 + - -6,-36 + - -13,-36 + - -13,-37 + id: grid_chunk--13--37 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-37 + - 0,-36 + - -5,-36 + - -5,-37 + id: grid_chunk--5--37 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-36 + - 0,-35 + - -16,-35 + - -16,-36 + id: grid_chunk--16--36 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -15,-35 + - -15,-34 + - -16,-34 + - -16,-35 + id: grid_chunk--16--35 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -12,-35 + - -12,-34 + - -14,-34 + - -14,-35 + id: grid_chunk--14--35 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -8,-35 + - -8,-34 + - -11,-34 + - -11,-35 + id: grid_chunk--11--35 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-35 + - 0,-34 + - -5,-34 + - -5,-35 + id: grid_chunk--5--35 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -3,-34 + - -3,-33 + - -16,-33 + - -16,-34 + id: grid_chunk--16--34 + mask: + - MapGrid + layer: + - MapGrid + mass: 52 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -7,-33 + - -7,-32 + - -14,-32 + - -14,-33 + id: grid_chunk--14--33 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -3,-33 + - -3,-32 + - -6,-32 + - -6,-33 + id: grid_chunk--6--33 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 0,-33 + - 0,-32 + - -2,-32 + - -2,-33 + id: grid_chunk--2--33 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -16,-43 + - -16,-40 + - -17,-40 + - -17,-43 + id: grid_chunk--17--43 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -16,-39 + - -16,-37 + - -19,-37 + - -19,-39 + id: grid_chunk--19--39 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -18,-37 + - -18,-33 + - -19,-33 + - -19,-37 + id: grid_chunk--19--37 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -16,-37 + - -16,-33 + - -17,-33 + - -17,-37 + id: grid_chunk--17--37 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -16,-33 + - -16,-32 + - -19,-32 + - -19,-33 + id: grid_chunk--19--33 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,-16 + - 64,-14 + - 48,-14 + - 48,-16 + id: grid_chunk-48--16 + mask: + - MapGrid + layer: + - MapGrid + mass: 128 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 59,-14 + - 59,-13 + - 48,-13 + - 48,-14 + id: grid_chunk-48--14 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,-13 + - 64,0 + - 48,0 + - 48,-13 + id: grid_chunk-48--13 + mask: + - MapGrid + layer: + - MapGrid + mass: 832 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 59,-32 + - 59,-26 + - 58,-26 + - 58,-32 + id: grid_chunk-58--32 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 62,-26 + - 62,-25 + - 58,-25 + - 58,-26 + id: grid_chunk-58--26 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,-26 + - 64,-25 + - 63,-25 + - 63,-26 + id: grid_chunk-63--26 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,-25 + - 64,-24 + - 58,-24 + - 58,-25 + id: grid_chunk-58--25 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 56,-32 + - 56,-21 + - 48,-21 + - 48,-32 + id: grid_chunk-48--32 + mask: + - MapGrid + layer: + - MapGrid + mass: 352 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,-21 + - 64,-20 + - 56,-20 + - 56,-21 + id: grid_chunk-56--21 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,-20 + - 64,-16 + - 48,-16 + - 48,-20 + id: grid_chunk-48--20 + mask: + - MapGrid + layer: + - MapGrid + mass: 256 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,32 + - 16,34 + - 5,34 + - 5,32 + id: grid_chunk-5-32 + mask: + - MapGrid + layer: + - MapGrid + mass: 88 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 6,34 + - 6,36 + - 5,36 + - 5,34 + id: grid_chunk-5-34 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,34 + - 16,36 + - 10,36 + - 10,34 + id: grid_chunk-10-34 + mask: + - MapGrid + layer: + - MapGrid + mass: 48 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,36 + - 16,38 + - 5,38 + - 5,36 + id: grid_chunk-5-36 + mask: + - MapGrid + layer: + - MapGrid + mass: 88 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 6,38 + - 6,39 + - 5,39 + - 5,38 + id: grid_chunk-5-38 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,38 + - 16,39 + - 12,39 + - 12,38 + id: grid_chunk-12-38 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,39 + - 16,40 + - 5,40 + - 5,39 + id: grid_chunk-5-39 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 6,40 + - 6,42 + - 5,42 + - 5,40 + id: grid_chunk-5-40 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,40 + - 16,42 + - 12,42 + - 12,40 + id: grid_chunk-12-40 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,42 + - 16,43 + - 5,43 + - 5,42 + id: grid_chunk-5-42 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 6,43 + - 6,47 + - 5,47 + - 5,43 + id: grid_chunk-5-43 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 16,47 + - 16,48 + - 5,48 + - 5,47 + id: grid_chunk-5-47 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,32 + - 32,43 + - 16,43 + - 16,32 + id: grid_chunk-16-32 + mask: + - MapGrid + layer: + - MapGrid + mass: 704 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,43 + - 32,44 + - 17,44 + - 17,43 + id: grid_chunk-17-43 + mask: + - MapGrid + layer: + - MapGrid + mass: 60 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,44 + - 32,47 + - 18,47 + - 18,44 + id: grid_chunk-18-44 + mask: + - MapGrid + layer: + - MapGrid + mass: 168 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,47 + - 32,48 + - 16,48 + - 16,47 + id: grid_chunk-16-47 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,16 + - 48,32 + - 32,32 + - 32,16 + id: grid_chunk-32-16 + mask: + - MapGrid + layer: + - MapGrid + mass: 1024 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,32 + - 48,39 + - 32,39 + - 32,32 + id: grid_chunk-32-32 + mask: + - MapGrid + layer: + - MapGrid + mass: 448 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 41,39 + - 41,40 + - 32,40 + - 32,39 + id: grid_chunk-32-39 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,39 + - 48,40 + - 43,40 + - 43,39 + id: grid_chunk-43-39 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,40 + - 48,41 + - 32,41 + - 32,40 + id: grid_chunk-32-40 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 47,41 + - 47,44 + - 32,44 + - 32,41 + id: grid_chunk-32-41 + mask: + - MapGrid + layer: + - MapGrid + mass: 180 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 39,44 + - 39,45 + - 32,45 + - 32,44 + id: grid_chunk-32-44 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 46,44 + - 46,45 + - 42,45 + - 42,44 + id: grid_chunk-42-44 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 37,45 + - 37,47 + - 32,47 + - 32,45 + id: grid_chunk-32-45 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 41,44 + - 41,47 + - 40,47 + - 40,44 + id: grid_chunk-40-44 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,45 + - 48,47 + - 42,47 + - 42,45 + id: grid_chunk-42-45 + mask: + - MapGrid + layer: + - MapGrid + mass: 48 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,47 + - 48,48 + - 32,48 + - 32,47 + id: grid_chunk-32-47 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,48 + - 32,50 + - 21,50 + - 21,48 + id: grid_chunk-21-48 + mask: + - MapGrid + layer: + - MapGrid + mass: 88 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 22,50 + - 22,51 + - 21,51 + - 21,50 + id: grid_chunk-21-50 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,51 + - 32,52 + - 21,52 + - 21,51 + id: grid_chunk-21-51 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 22,52 + - 22,53 + - 21,53 + - 21,52 + id: grid_chunk-21-52 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,53 + - 32,54 + - 21,54 + - 21,53 + id: grid_chunk-21-53 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 32,54 + - 32,55 + - 22,55 + - 22,54 + id: grid_chunk-22-54 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 31,55 + - 31,56 + - 24,56 + - 24,55 + id: grid_chunk-24-55 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 30,56 + - 30,57 + - 25,57 + - 25,56 + id: grid_chunk-25-56 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 29,57 + - 29,58 + - 26,58 + - 26,57 + id: grid_chunk-26-57 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 28,58 + - 28,61 + - 27,61 + - 27,58 + id: grid_chunk-27-58 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,48 + - 48,49 + - 45,49 + - 45,48 + id: grid_chunk-45-48 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 34,48 + - 34,50 + - 32,50 + - 32,48 + id: grid_chunk-32-48 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 34,50 + - 34,51 + - 33,51 + - 33,50 + id: grid_chunk-33-50 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 43,48 + - 43,51 + - 42,51 + - 42,48 + id: grid_chunk-42-48 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 34,51 + - 34,52 + - 32,52 + - 32,51 + id: grid_chunk-32-51 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 48,51 + - 48,52 + - 42,52 + - 42,51 + id: grid_chunk-42-51 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 43,52 + - 43,53 + - 33,53 + - 33,52 + id: grid_chunk-33-52 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 34,53 + - 34,54 + - 32,54 + - 32,53 + id: grid_chunk-32-53 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 33,54 + - 33,55 + - 32,55 + - 32,54 + id: grid_chunk-32-54 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,-16 + - 80,-11 + - 64,-11 + - 64,-16 + id: grid_chunk-64--16 + mask: + - MapGrid + layer: + - MapGrid + mass: 320 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 74,-11 + - 74,-8 + - 64,-8 + - 64,-11 + id: grid_chunk-64--11 + mask: + - MapGrid + layer: + - MapGrid + mass: 120 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,-10 + - 80,-8 + - 75,-8 + - 75,-10 + id: grid_chunk-75--10 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,-8 + - 80,0 + - 64,0 + - 64,-8 + id: grid_chunk-64--8 + mask: + - MapGrid + layer: + - MapGrid + mass: 512 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 68,-26 + - 68,-25 + - 64,-25 + - 64,-26 + id: grid_chunk-64--26 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 75,-26 + - 75,-25 + - 69,-25 + - 69,-26 + id: grid_chunk-69--26 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,-26 + - 80,-25 + - 79,-25 + - 79,-26 + id: grid_chunk-79--26 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,-25 + - 80,-24 + - 64,-24 + - 64,-25 + id: grid_chunk-64--25 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 73,-21 + - 73,-18 + - 64,-18 + - 64,-21 + id: grid_chunk-64--21 + mask: + - MapGrid + layer: + - MapGrid + mass: 108 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,-18 + - 80,-16 + - 64,-16 + - 64,-18 + id: grid_chunk-64--18 + mask: + - MapGrid + layer: + - MapGrid + mass: 128 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,0 + - 64,16 + - 48,16 + - 48,0 + id: grid_chunk-48-0 + mask: + - MapGrid + layer: + - MapGrid + mass: 1024 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,0 + - 80,14 + - 64,14 + - 64,0 + id: grid_chunk-64-0 + mask: + - MapGrid + layer: + - MapGrid + mass: 896 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 77,14 + - 77,15 + - 64,15 + - 64,14 + id: grid_chunk-64-14 + mask: + - MapGrid + layer: + - MapGrid + mass: 52 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 76,15 + - 76,16 + - 64,16 + - 64,15 + id: grid_chunk-64-15 + mask: + - MapGrid + layer: + - MapGrid + mass: 48 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 91,0 + - 91,3 + - 80,3 + - 80,0 + id: grid_chunk-80-0 + mask: + - MapGrid + layer: + - MapGrid + mass: 132 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 88,3 + - 88,6 + - 80,6 + - 80,3 + id: grid_chunk-80-3 + mask: + - MapGrid + layer: + - MapGrid + mass: 96 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 91,6 + - 91,7 + - 80,7 + - 80,6 + id: grid_chunk-80-6 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 87,7 + - 87,9 + - 80,9 + - 80,7 + id: grid_chunk-80-7 + mask: + - MapGrid + layer: + - MapGrid + mass: 56 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 88,9 + - 88,14 + - 80,14 + - 80,9 + id: grid_chunk-80-9 + mask: + - MapGrid + layer: + - MapGrid + mass: 160 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 90,7 + - 90,14 + - 89,14 + - 89,7 + id: grid_chunk-89-7 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 93,0 + - 93,14 + - 92,14 + - 92,0 + id: grid_chunk-92-0 + mask: + - MapGrid + layer: + - MapGrid + mass: 56 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 93,14 + - 93,15 + - 84,15 + - 84,14 + id: grid_chunk-84-14 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 85,15 + - 85,16 + - 81,16 + - 81,15 + id: grid_chunk-81-15 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,16 + - 64,27 + - 48,27 + - 48,16 + id: grid_chunk-48-16 + mask: + - MapGrid + layer: + - MapGrid + mass: 704 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 52,27 + - 52,28 + - 48,28 + - 48,27 + id: grid_chunk-48-27 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 59,27 + - 59,28 + - 54,28 + - 54,27 + id: grid_chunk-54-27 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,27 + - 64,28 + - 61,28 + - 61,27 + id: grid_chunk-61-27 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,28 + - 64,29 + - 48,29 + - 48,28 + id: grid_chunk-48-28 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,29 + - 64,30 + - 51,30 + - 51,29 + id: grid_chunk-51-29 + mask: + - MapGrid + layer: + - MapGrid + mass: 52 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 62,30 + - 62,32 + - 51,32 + - 51,30 + id: grid_chunk-51-30 + mask: + - MapGrid + layer: + - MapGrid + mass: 88 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,48 + - 64,50 + - 48,50 + - 48,48 + id: grid_chunk-48-48 + mask: + - MapGrid + layer: + - MapGrid + mass: 128 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 50,50 + - 50,51 + - 49,51 + - 49,50 + id: grid_chunk-49-50 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 50,51 + - 50,52 + - 48,52 + - 48,51 + id: grid_chunk-48-51 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 58,50 + - 58,52 + - 55,52 + - 55,50 + id: grid_chunk-55-50 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 63,32 + - 63,40 + - 50,40 + - 50,32 + id: grid_chunk-50-32 + mask: + - MapGrid + layer: + - MapGrid + mass: 416 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 62,40 + - 62,41 + - 51,41 + - 51,40 + id: grid_chunk-51-40 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 60,41 + - 60,42 + - 53,42 + - 53,41 + id: grid_chunk-53-41 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 51,45 + - 51,47 + - 48,47 + - 48,45 + id: grid_chunk-48-45 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,45 + - 64,47 + - 62,47 + - 62,45 + id: grid_chunk-62-45 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 64,47 + - 64,48 + - 48,48 + - 48,47 + id: grid_chunk-48-47 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,16 + - 80,18 + - 79,18 + - 79,16 + id: grid_chunk-79-16 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 76,16 + - 76,19 + - 64,19 + - 64,16 + id: grid_chunk-64-16 + mask: + - MapGrid + layer: + - MapGrid + mass: 144 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,18 + - 80,19 + - 77,19 + - 77,18 + id: grid_chunk-77-18 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 78,19 + - 78,20 + - 64,20 + - 64,19 + id: grid_chunk-64-19 + mask: + - MapGrid + layer: + - MapGrid + mass: 56 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 74,20 + - 74,24 + - 64,24 + - 64,20 + id: grid_chunk-64-20 + mask: + - MapGrid + layer: + - MapGrid + mass: 160 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 77,20 + - 77,24 + - 76,24 + - 76,20 + id: grid_chunk-76-20 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,24 + - 80,25 + - 64,25 + - 64,24 + id: grid_chunk-64-24 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 75,25 + - 75,26 + - 64,26 + - 64,25 + id: grid_chunk-64-25 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 74,26 + - 74,30 + - 64,30 + - 64,26 + id: grid_chunk-64-26 + mask: + - MapGrid + layer: + - MapGrid + mass: 160 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 74,30 + - 74,32 + - 65,32 + - 65,30 + id: grid_chunk-65-30 + mask: + - MapGrid + layer: + - MapGrid + mass: 72 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 74,32 + - 74,38 + - 65,38 + - 65,32 + id: grid_chunk-65-32 + mask: + - MapGrid + layer: + - MapGrid + mass: 216 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,38 + - 80,39 + - 65,39 + - 65,38 + id: grid_chunk-65-38 + mask: + - MapGrid + layer: + - MapGrid + mass: 60 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 74,39 + - 74,40 + - 65,40 + - 65,39 + id: grid_chunk-65-39 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,40 + - 80,41 + - 65,41 + - 65,40 + id: grid_chunk-65-40 + mask: + - MapGrid + layer: + - MapGrid + mass: 60 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 73,41 + - 73,42 + - 65,42 + - 65,41 + id: grid_chunk-65-41 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 72,42 + - 72,43 + - 65,43 + - 65,42 + id: grid_chunk-65-42 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 73,43 + - 73,44 + - 65,44 + - 65,43 + id: grid_chunk-65-43 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,41 + - 80,44 + - 77,44 + - 77,41 + id: grid_chunk-77-41 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 75,44 + - 75,45 + - 65,45 + - 65,44 + id: grid_chunk-65-44 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 79,44 + - 79,45 + - 78,45 + - 78,44 + id: grid_chunk-78-44 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,45 + - 80,47 + - 64,47 + - 64,45 + id: grid_chunk-64-45 + mask: + - MapGrid + layer: + - MapGrid + mass: 128 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 73,47 + - 73,48 + - 64,48 + - 64,47 + id: grid_chunk-64-47 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,47 + - 80,48 + - 77,48 + - 77,47 + id: grid_chunk-77-47 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 68,48 + - 68,49 + - 64,49 + - 64,48 + id: grid_chunk-64-48 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 65,49 + - 65,50 + - 64,50 + - 64,49 + id: grid_chunk-64-49 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 73,48 + - 73,51 + - 72,51 + - 72,48 + id: grid_chunk-72-48 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,48 + - 80,51 + - 77,51 + - 77,48 + id: grid_chunk-77-48 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 80,51 + - 80,52 + - 72,52 + - 72,51 + id: grid_chunk-72-51 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 91,-16 + - 91,-12 + - 80,-12 + - 80,-16 + id: grid_chunk-80--16 + mask: + - MapGrid + layer: + - MapGrid + mass: 176 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 92,-12 + - 92,-11 + - 80,-11 + - 80,-12 + id: grid_chunk-80--12 + mask: + - MapGrid + layer: + - MapGrid + mass: 48 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 84,-9 + - 84,-7 + - 80,-7 + - 80,-9 + id: grid_chunk-80--9 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 92,-11 + - 92,-7 + - 85,-7 + - 85,-11 + id: grid_chunk-85--11 + mask: + - MapGrid + layer: + - MapGrid + mass: 112 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 92,-7 + - 92,-2 + - 80,-2 + - 80,-7 + id: grid_chunk-80--7 + mask: + - MapGrid + layer: + - MapGrid + mass: 240 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 93,-2 + - 93,0 + - 80,0 + - 80,-2 + id: grid_chunk-80--2 + mask: + - MapGrid + layer: + - MapGrid + mass: 104 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 88,-26 + - 88,-25 + - 80,-25 + - 80,-26 + id: grid_chunk-80--26 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 81,-25 + - 81,-24 + - 80,-24 + - 80,-25 + id: grid_chunk-80--25 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 88,-25 + - 88,-24 + - 87,-24 + - 87,-25 + id: grid_chunk-87--25 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 88,-24 + - 88,-23 + - 86,-23 + - 86,-24 + id: grid_chunk-86--24 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 96,-23 + - 96,-22 + - 87,-22 + - 87,-23 + id: grid_chunk-87--23 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 96,-22 + - 96,-19 + - 84,-19 + - 84,-22 + id: grid_chunk-84--22 + mask: + - MapGrid + layer: + - MapGrid + mass: 144 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 96,-19 + - 96,-18 + - 87,-18 + - 87,-19 + id: grid_chunk-87--19 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 96,-18 + - 96,-17 + - 80,-17 + - 80,-18 + id: grid_chunk-80--18 + mask: + - MapGrid + layer: + - MapGrid + mass: 64 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 91,-17 + - 91,-16 + - 80,-16 + - 80,-17 + id: grid_chunk-80--17 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 91,38 + - 91,39 + - 80,39 + - 80,38 + id: grid_chunk-80-38 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 91,39 + - 91,40 + - 88,40 + - 88,39 + id: grid_chunk-88-39 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 87,40 + - 87,41 + - 81,41 + - 81,40 + id: grid_chunk-81-40 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 84,41 + - 84,42 + - 81,42 + - 81,41 + id: grid_chunk-81-41 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 88,41 + - 88,42 + - 85,42 + - 85,41 + id: grid_chunk-85-41 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 91,40 + - 91,42 + - 89,42 + - 89,40 + id: grid_chunk-89-40 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 88,42 + - 88,43 + - 81,43 + - 81,42 + id: grid_chunk-81-42 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 92,42 + - 92,43 + - 89,43 + - 89,42 + id: grid_chunk-89-42 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 92,43 + - 92,44 + - 81,44 + - 81,43 + id: grid_chunk-81-43 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 85,44 + - 85,45 + - 82,45 + - 82,44 + id: grid_chunk-82-44 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 90,44 + - 90,45 + - 86,45 + - 86,44 + id: grid_chunk-86-44 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 90,45 + - 90,46 + - 80,46 + - 80,45 + id: grid_chunk-80-45 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 81,46 + - 81,47 + - 80,47 + - 80,46 + id: grid_chunk-80-46 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 87,46 + - 87,47 + - 82,47 + - 82,46 + id: grid_chunk-82-46 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 90,46 + - 90,47 + - 89,47 + - 89,46 + id: grid_chunk-89-46 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 84,47 + - 84,48 + - 81,48 + - 81,47 + id: grid_chunk-81-47 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 89,47 + - 89,48 + - 85,48 + - 85,47 + id: grid_chunk-85-47 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 92,44 + - 92,48 + - 91,48 + - 91,44 + id: grid_chunk-91-44 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 88,48 + - 88,49 + - 80,49 + - 80,48 + id: grid_chunk-80-48 + mask: + - MapGrid + layer: + - MapGrid + mass: 32 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 92,48 + - 92,49 + - 89,49 + - 89,48 + id: grid_chunk-89-48 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 84,49 + - 84,50 + - 80,50 + - 80,49 + id: grid_chunk-80-49 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 88,49 + - 88,50 + - 85,50 + - 85,49 + id: grid_chunk-85-49 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 91,49 + - 91,50 + - 89,50 + - 89,49 + id: grid_chunk-89-49 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 86,50 + - 86,51 + - 80,51 + - 80,50 + id: grid_chunk-80-50 + mask: + - MapGrid + layer: + - MapGrid + mass: 24 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 91,50 + - 91,51 + - 87,51 + - 87,50 + id: grid_chunk-87-50 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 90,51 + - 90,52 + - 80,52 + - 80,51 + id: grid_chunk-80-51 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 51,-48 + - 51,-41 + - 50,-41 + - 50,-48 + id: grid_chunk-50--48 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 53,-41 + - 53,-40 + - 48,-40 + - 48,-41 + id: grid_chunk-48--41 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 53,-40 + - 53,-36 + - 52,-36 + - 52,-40 + id: grid_chunk-52--40 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 56,-36 + - 56,-35 + - 52,-35 + - 52,-36 + id: grid_chunk-52--36 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 53,-35 + - 53,-34 + - 52,-34 + - 52,-35 + id: grid_chunk-52--35 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 59,-35 + - 59,-34 + - 55,-34 + - 55,-35 + id: grid_chunk-55--35 + mask: + - MapGrid + layer: + - MapGrid + mass: 16 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 49,-40 + - 49,-33 + - 48,-33 + - 48,-40 + id: grid_chunk-48--40 + mask: + - MapGrid + layer: + - MapGrid + mass: 28 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 50,-33 + - 50,-32 + - 48,-32 + - 48,-33 + id: grid_chunk-48--33 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 59,-34 + - 59,-32 + - 58,-32 + - 58,-34 + id: grid_chunk-58--34 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 101,-23 + - 101,-22 + - 96,-22 + - 96,-23 + id: grid_chunk-96--23 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 109,-26 + - 109,-22 + - 104,-22 + - 104,-26 + id: grid_chunk-104--26 + mask: + - MapGrid + layer: + - MapGrid + mass: 80 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 109,-22 + - 109,-21 + - 96,-21 + - 96,-22 + id: grid_chunk-96--22 + mask: + - MapGrid + layer: + - MapGrid + mass: 52 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 112,-26 + - 112,-21 + - 110,-21 + - 110,-26 + id: grid_chunk-110--26 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 112,-21 + - 112,-17 + - 96,-17 + - 96,-21 + id: grid_chunk-96--21 + mask: + - MapGrid + layer: + - MapGrid + mass: 256 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 112,-17 + - 112,-16 + - 101,-16 + - 101,-17 + id: grid_chunk-101--17 + mask: + - MapGrid + layer: + - MapGrid + mass: 44 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 112,-16 + - 112,-14 + - 101,-14 + - 101,-16 + id: grid_chunk-101--16 + mask: + - MapGrid + layer: + - MapGrid + mass: 88 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 109,-14 + - 109,-11 + - 101,-11 + - 101,-14 + id: grid_chunk-101--14 + mask: + - MapGrid + layer: + - MapGrid + mass: 96 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 109,-11 + - 109,-10 + - 100,-10 + - 100,-11 + id: grid_chunk-100--11 + mask: + - MapGrid + layer: + - MapGrid + mass: 36 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 109,-10 + - 109,-9 + - 104,-9 + - 104,-10 + id: grid_chunk-104--10 + mask: + - MapGrid + layer: + - MapGrid + mass: 20 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 112,-14 + - 112,-9 + - 110,-9 + - 110,-14 + id: grid_chunk-110--14 + mask: + - MapGrid + layer: + - MapGrid + mass: 40 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 117,-26 + - 117,-21 + - 112,-21 + - 112,-26 + id: grid_chunk-112--26 + mask: + - MapGrid + layer: + - MapGrid + mass: 100 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 122,-21 + - 122,-16 + - 112,-16 + - 112,-21 + id: grid_chunk-112--21 + mask: + - MapGrid + layer: + - MapGrid + mass: 200 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 122,-16 + - 122,-14 + - 112,-14 + - 112,-16 + id: grid_chunk-112--16 + mask: + - MapGrid + layer: + - MapGrid + mass: 80 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 117,-14 + - 117,-9 + - 112,-9 + - 112,-14 + id: grid_chunk-112--14 + mask: + - MapGrid + layer: + - MapGrid + mass: 100 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 82,16 + - 82,17 + - 80,17 + - 80,16 + id: grid_chunk-80-16 + mask: + - MapGrid + layer: + - MapGrid + mass: 8 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 49,-56 + - 49,-55 + - 48,-55 + - 48,-56 + id: grid_chunk-48--56 + mask: + - MapGrid + layer: + - MapGrid + mass: 4 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 51,-52 + - 51,-51 + - 48,-51 + - 48,-52 + id: grid_chunk-48--52 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - 51,-51 + - 51,-48 + - 50,-48 + - 50,-51 + id: grid_chunk-50--51 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + - shape: !type:PolygonShape + vertices: + - -16,-31 + - -16,-28 + - -17,-28 + - -17,-31 + id: grid_chunk--17--31 + mask: + - MapGrid + layer: + - MapGrid + mass: 12 + restitution: 0.1 + type: Fixtures - uid: 1 type: Grille components: @@ -13750,6 +13751,8 @@ entities: - pos: 0.3026234,-5.6171584 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 164 type: AirlockMaintLocked components: @@ -15780,7 +15783,7 @@ entities: parent: 0 type: Transform - uid: 456 - type: CrateGenericonimo + type: CrateGenericSteel components: - pos: 5.5,19.5 parent: 0 @@ -16761,9 +16764,9 @@ entities: - enabled: False type: AmbientSound - uid: 598 - type: AirlockMaintCommandLocked + type: AirlockHeadOfPersonnelLocked components: - - pos: 14.5,-6.5 + - pos: 13.5,-3.5 parent: 0 type: Transform - containers: @@ -16771,11 +16774,9 @@ entities: ents: [] type: ContainerContainer - uid: 599 - type: AirlockCommandLocked + type: AirlockMaintHOPLocked components: - - name: Head of Personnel's Office - type: MetaData - - pos: 13.5,-3.5 + - pos: 14.5,-6.5 parent: 0 type: Transform - containers: @@ -16984,7 +16985,7 @@ entities: parent: 0 type: Transform - uid: 623 - type: AirlockScienceLocked + type: AirlockCommandLocked components: - pos: 24.5,-11.5 parent: 0 @@ -19394,7 +19395,7 @@ entities: parent: 0 type: Transform - uid: 970 - type: AirlockCommandLocked + type: AirlockChiefEngineerLocked components: - pos: 14.5,-34.5 parent: 0 @@ -26833,6 +26834,8 @@ entities: - pos: 6.5559034,25.52264 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 2066 type: WallSolid components: @@ -26956,6 +26959,8 @@ entities: - pos: 6.5559034,25.783308 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 2085 type: BlastDoor components: @@ -26998,7 +27003,7 @@ entities: parent: 0 type: Transform - uid: 2090 - type: CrateGenericonimo + type: CrateGenericSteel components: - pos: 6.5,32.5 parent: 0 @@ -27012,7 +27017,7 @@ entities: PaperLabel: !type:ContainerSlot {} type: ContainerContainer - uid: 2091 - type: CrateGenericonimo + type: CrateGenericSteel components: - pos: 7.5,31.5 parent: 0 @@ -29199,7 +29204,7 @@ entities: parent: 0 type: Transform - uid: 2397 - type: AirlockCommandGlassLocked + type: AirlockHeadOfSecurityLocked components: - pos: 42.5,23.5 parent: 0 @@ -30984,9 +30989,9 @@ entities: parent: 0 type: Transform - uid: 2638 - type: AirlockCommandLocked + type: AirlockCaptainLocked components: - - pos: 35.5,40.5 + - pos: 32.5,44.5 parent: 0 type: Transform - containers: @@ -30994,9 +30999,9 @@ entities: ents: [] type: ContainerContainer - uid: 2639 - type: AirlockCommandLocked + type: AirlockCaptainLocked components: - - pos: 32.5,44.5 + - pos: 35.5,40.5 parent: 0 type: Transform - containers: @@ -32244,7 +32249,7 @@ entities: light_bulb: !type:ContainerSlot {} type: ContainerContainer - uid: 2810 - type: AirlockCommandLocked + type: AirlockChiefEngineerLocked components: - pos: 16.5,36.5 parent: 0 @@ -32493,12 +32498,16 @@ entities: - pos: 10.555903,29.382763 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 2851 type: SignalButton components: - pos: 7.2902784,29.867138 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 2852 type: Poweredlight components: @@ -38043,7 +38052,7 @@ entities: parent: 0 type: Transform - uid: 3743 - type: CrateGenericonimo + type: CrateGenericSteel components: - pos: 89.5,-7.5 parent: 0 @@ -38057,7 +38066,7 @@ entities: PaperLabel: !type:ContainerSlot {} type: ContainerContainer - uid: 3744 - type: CrateGenericonimo + type: CrateGenericSteel components: - pos: 90.5,-7.5 parent: 0 @@ -48322,7 +48331,7 @@ entities: ents: [] type: ContainerContainer - uid: 4940 - type: CrateGenericonimo + type: CrateGenericSteel components: - pos: 17.5,41.5 parent: 0 @@ -49306,7 +49315,7 @@ entities: parent: 0 type: Transform - uid: 5062 - type: AirlockCommandGlassLocked + type: AirlockChiefMedicalOfficerLocked components: - pos: 56.5,-8.5 parent: 0 @@ -50420,6 +50429,8 @@ entities: - pos: 53.770565,-11.798379 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 5185 type: LockerChiefMedicalOfficerFilled components: @@ -51111,7 +51122,7 @@ entities: ents: [] type: ContainerContainer - uid: 5255 - type: AirlockCommandGlassLocked + type: AirlockResearchDirectorLocked components: - pos: 62.5,12.5 parent: 0 @@ -51203,12 +51214,16 @@ entities: - pos: 59.81175,10.264227 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 5266 type: SignalButton components: - pos: 59.81175,10.529852 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 5267 type: AirlockScienceGlassLocked components: @@ -51290,9 +51305,9 @@ entities: ents: [] type: ContainerContainer - uid: 5275 - type: AirlockCommandLocked + type: AirlockResearchDirectorLocked components: - - pos: 52.5,24.5 + - pos: 56.5,28.5 parent: 0 type: Transform - containers: @@ -51300,9 +51315,9 @@ entities: ents: [] type: ContainerContainer - uid: 5276 - type: AirlockCommandLocked + type: AirlockResearchDirectorLocked components: - - pos: 50.5,24.5 + - pos: 52.5,24.5 parent: 0 type: Transform - containers: @@ -52917,7 +52932,7 @@ entities: parent: 0 type: Transform - uid: 5459 - type: CrateGenericonimo + type: CrateGenericSteel components: - pos: 9.5,-18.5 parent: 0 @@ -55405,7 +55420,7 @@ entities: cellslot_cell_container: !type:ContainerSlot {} type: ContainerContainer - uid: 5767 - type: CrateGenericonimo + type: CrateGenericSteel components: - pos: 12.5,16.5 parent: 0 @@ -64897,6 +64912,16 @@ entities: type: Transform - canCollide: False type: Physics +- uid: 6814 + type: AirlockResearchDirectorLocked + components: + - pos: 50.5,24.5 + parent: 0 + type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer - uid: 6825 type: CableApcExtension components: @@ -76020,24 +76045,32 @@ entities: - pos: 53.5,-24.5 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 7945 type: GasMinerNitrogen components: - pos: 53.5,-22.5 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 7946 type: GasMinerCarbonDioxide components: - pos: 53.5,-26.5 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 7947 type: GasMinerPlasma components: - pos: 53.5,-28.5 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 7948 type: WarningO2 components: @@ -83056,12 +83089,16 @@ entities: - pos: 49.485035,2.0687056 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 8698 type: SignalButton components: - pos: 53.74233,1.4437056 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 8699 type: SpawnPointMedicalDoctor components: @@ -106930,6 +106967,8 @@ entities: - pos: 37.465042,-25.226236 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 10905 type: CableMV components: @@ -108053,16 +108092,6 @@ entities: board: !type:Container ents: [] type: ContainerContainer -- uid: 11055 - type: AirlockCommand - components: - - pos: 56.5,28.5 - parent: 0 - type: Transform - - containers: - board: !type:Container - ents: [] - type: ContainerContainer - uid: 11056 type: AirlockMaintLocked components: @@ -114324,12 +114353,16 @@ entities: - pos: 9.5,-33.5 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 11950 type: SignalButton components: - pos: 9.5,-34.5 parent: 0 type: Transform + - fixtures: [] + type: Fixtures - uid: 11951 type: PowerCellRecharger components: diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index 5267c40d53..adf8ab6f7c 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -5937,12 +5937,9 @@ entities: parent: 853 type: Transform - uid: 561 - type: AirlockCommandGlassLocked + type: AirlockHeadOfSecurityLocked components: - - name: Head of Security's Office - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: -8.5,18.5 + - pos: -8.5,18.5 parent: 853 type: Transform - containers: @@ -6082,12 +6079,9 @@ entities: ents: [] type: ContainerContainer - uid: 574 - type: AirlockVaultLocked + type: AirlockHeadOfSecurityLocked components: - - name: Armory - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: -10.5,20.5 + - pos: -10.5,20.5 parent: 853 type: Transform - containers: @@ -6272,12 +6266,9 @@ entities: ents: [] type: ContainerContainer - uid: 593 - type: AirlockCommandGlassLocked + type: AirlockResearchDirectorLocked components: - - name: Research Director's Office - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: -3.5,-21.5 + - pos: -8.5,-21.5 parent: 853 type: Transform - containers: @@ -6285,12 +6276,9 @@ entities: ents: [] type: ContainerContainer - uid: 594 - type: AirlockCommandGlassLocked + type: AirlockResearchDirectorLocked components: - - name: Server Room - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: -8.5,-21.5 + - pos: -3.5,-21.5 parent: 853 type: Transform - containers: @@ -6298,12 +6286,9 @@ entities: ents: [] type: ContainerContainer - uid: 595 - type: AirlockCommandGlassLocked + type: AirlockChiefMedicalOfficerLocked components: - - name: Chief Medical Officer's Office - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: 20.5,-13.5 + - pos: 20.5,-13.5 parent: 853 type: Transform - containers: @@ -6569,12 +6554,9 @@ entities: parent: 853 type: Transform - uid: 620 - type: AirlockCommandGlassLocked + type: AirlockChiefEngineerLocked components: - - name: Chief Engineer's Office - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: 38.5,1.5 + - pos: 38.5,1.5 parent: 853 type: Transform - containers: @@ -6792,12 +6774,9 @@ entities: - canCollide: False type: Physics - uid: 639 - type: AirlockCommandLocked + type: AirlockCaptainLocked components: - - name: Captain's Office - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: 5.5,25.5 + - pos: 10.5,26.5 parent: 853 type: Transform - containers: @@ -7381,11 +7360,6 @@ entities: - containers: cellslot_cell_container: !type:ContainerSlot {} type: ContainerContainer - - cellInsertSound: !type:SoundPathSpecifier - path: /Audio/Items/pistol_magout.ogg - cellRemoveSound: !type:SoundPathSpecifier - path: /Audio/Items/pistol_magin.ogg - type: PowerCellSlot - uid: 708 type: WallReinforced components: @@ -13585,7 +13559,7 @@ entities: - 0 - 0 type: GridAtmosphere - - angularDamping: 0.3 + - linearDamping: 0.1 fixedRotation: False bodyType: Dynamic type: Physics @@ -23720,12 +23694,9 @@ entities: parent: 853 type: Transform - uid: 1869 - type: AirlockCommandLocked + type: AirlockHeadOfPersonnelLocked components: - - name: Head of Personnel's Office - type: MetaData - - rot: 4.371139006309477E-08 rad - pos: 6.5,17.5 + - pos: 6.5,17.5 parent: 853 type: Transform - containers: @@ -35844,6 +35815,16 @@ entities: type: Transform - canCollide: False type: Physics +- uid: 3221 + type: AirlockCaptainLocked + components: + - pos: 5.5,25.5 + parent: 853 + type: Transform + - containers: + board: !type:Container + ents: [] + type: ContainerContainer - uid: 3222 type: CableApcExtension components: @@ -52630,16 +52611,6 @@ entities: - pos: 13.5,26.5 parent: 853 type: Transform -- uid: 5089 - type: AirlockCommandLocked - components: - - pos: 10.5,26.5 - parent: 853 - type: Transform - - containers: - board: !type:Container - ents: [] - type: ContainerContainer - uid: 5090 type: ToiletEmpty components: diff --git a/Resources/Prototypes/AccessLevels/engineering.yml b/Resources/Prototypes/AccessLevels/engineering.yml index 5da12d0981..d4ed2400f6 100644 --- a/Resources/Prototypes/AccessLevels/engineering.yml +++ b/Resources/Prototypes/AccessLevels/engineering.yml @@ -1,6 +1,6 @@ -#- type: accessLevel -# id: ChiefEngineer -# name: Chief Engineer +- type: accessLevel + id: ChiefEngineer + name: Chief Engineer - type: accessLevel id: Engineering diff --git a/Resources/Prototypes/AccessLevels/medical.yml b/Resources/Prototypes/AccessLevels/medical.yml index 35c0e55b66..64b584ff21 100644 --- a/Resources/Prototypes/AccessLevels/medical.yml +++ b/Resources/Prototypes/AccessLevels/medical.yml @@ -1,6 +1,6 @@ -#- type: accessLevel -# id: ChiefMedicalOfficer -# name: Chief Medical Officer +- type: accessLevel + id: ChiefMedicalOfficer + name: Chief Medical Officer - type: accessLevel id: Medical diff --git a/Resources/Prototypes/AccessLevels/research.yml b/Resources/Prototypes/AccessLevels/research.yml index 605641f9dc..774c325aa0 100644 --- a/Resources/Prototypes/AccessLevels/research.yml +++ b/Resources/Prototypes/AccessLevels/research.yml @@ -1,6 +1,6 @@ -#- type: accessLevel -# id: ResearchDirector -# name: Research Director +- type: accessLevel + id: ResearchDirector + name: Research Director - type: accessLevel id: Research diff --git a/Resources/Prototypes/AccessLevels/security.yml b/Resources/Prototypes/AccessLevels/security.yml index b72a4e45bf..b214e4fa17 100644 --- a/Resources/Prototypes/AccessLevels/security.yml +++ b/Resources/Prototypes/AccessLevels/security.yml @@ -1,15 +1,15 @@ -#- type: accessLevel -# id: HeadOfSecurity -# name: Head of Security +- type: accessLevel + id: HeadOfSecurity + name: Head of Security - type: accessLevel id: Security + +- type: accessLevel + id: Armory #- type: accessLevel # id: Brig #- type: accessLevel # id: Detective - -- type: accessLevel - id: Armory diff --git a/Resources/Prototypes/Catalog/Fills/Crates/botany.yml b/Resources/Prototypes/Catalog/Fills/Crates/botany.yml index 1073a53069..b1f4ba9713 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/botany.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/botany.yml @@ -16,8 +16,8 @@ amount: 2 - id: CannabisSeeds amount: 2 -# - id: NettleSeeds -# amount: 2 + - id: NettleSeeds + amount: 2 # - id: RainbowBunchSeeds # amount: 2 # - id: StrangeSeeds diff --git a/Resources/Prototypes/Catalog/Fills/Crates/emergency.yml b/Resources/Prototypes/Catalog/Fills/Crates/emergency.yml index 23680b8a99..3e9a83b8be 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/emergency.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/emergency.yml @@ -21,7 +21,7 @@ - type: entity id: CrateEmergencyFire name: firefighting crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: diff --git a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml index 524bb64cde..3b77597215 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/fun.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/fun.yml @@ -1,7 +1,7 @@ - type: entity id: CrateFunPlushie name: plushie crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -26,7 +26,7 @@ id: CrateFunInstruments name: big band instrument collection description: Get your sad station movin' and groovin' with this fine collection! Contains thirteen different instruments. - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -60,7 +60,7 @@ - type: entity id: CrateFunArtSupplies name: art supplies - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: diff --git a/Resources/Prototypes/Catalog/Fills/Crates/materials.yml b/Resources/Prototypes/Catalog/Fills/Crates/materials.yml index c9962f9073..49fd9b5418 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/materials.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/materials.yml @@ -1,7 +1,7 @@ - type: entity id: CrateMaterialGlass name: glass sheet crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -11,7 +11,7 @@ - type: entity id: CrateMaterialSteel name: steel sheet crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -21,7 +21,7 @@ - type: entity id: CrateMaterialPlastic name: plastic sheet crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -31,7 +31,7 @@ - type: entity id: CrateMaterialWood name: wood crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -41,7 +41,7 @@ - type: entity id: CrateMaterialFuelTank name: fueltank crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -51,7 +51,7 @@ #- type: entity # id: CrateMaterialHFuelTank # name: fueltank crate -# parent: CrateGenericonimo +# parent: CrateGenericSteel # components: # - type: StorageFill # contents: @@ -61,7 +61,7 @@ - type: entity id: CrateMaterialWaterTank name: watertank crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -71,7 +71,7 @@ #- type: entity # id: CrateMaterialHWaterTank # name: watertank crate -# parent: CrateGenericonimo +# parent: CrateGenericSteel # components: # - type: StorageFill # contents: @@ -81,7 +81,7 @@ - type: entity id: CrateMaterialPlasteel name: plasteel crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -91,7 +91,7 @@ - type: entity id: CrateMaterialPlasma name: solid plasma crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: diff --git a/Resources/Prototypes/Catalog/Fills/Crates/npc.yml b/Resources/Prototypes/Catalog/Fills/Crates/npc.yml index 65063bcc49..26c7bcb3ca 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/npc.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/npc.yml @@ -99,7 +99,7 @@ - type: entity id: CrateNPCMonkeyCube name: monkey cube crate - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: diff --git a/Resources/Prototypes/Catalog/Fills/Crates/service.yml b/Resources/Prototypes/Catalog/Fills/Crates/service.yml index 00e85f84fa..2e5a757526 100644 --- a/Resources/Prototypes/Catalog/Fills/Crates/service.yml +++ b/Resources/Prototypes/Catalog/Fills/Crates/service.yml @@ -25,7 +25,7 @@ id: CrateServiceReplacementLights name: replacement lights crate description: May the light of Aether shine upon this station! Or at least, the light of forty two light tubes and twenty one light bulbs. - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -38,7 +38,7 @@ id: CrateServiceSmokeables name: smokeables crate description: "Tired of a quick death on the station? Order this crate and chain-smoke your way to a coughy demise!" - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: @@ -64,7 +64,7 @@ id: CrateServiceCustomSmokable name: DIY smokeables crate description: Want to get a little creative with what you use to destroy your lungs? Then this crate is for you! Has everything you need to roll your own Cigarettes. - parent: CrateGenericonimo + parent: CrateGenericSteel components: - type: StorageFill contents: diff --git a/Resources/Prototypes/Catalog/crayon_decals.yml b/Resources/Prototypes/Catalog/crayon_decals.yml deleted file mode 100644 index f138f966a1..0000000000 --- a/Resources/Prototypes/Catalog/crayon_decals.yml +++ /dev/null @@ -1,135 +0,0 @@ -- type: crayonDecal - id: BaseDecals - spritePath: Effects/crayondecals.rsi - decals: - - 0 - - 1 - - 2 - - 3 - - 4 - - 5 - - 6 - - 7 - - 8 - - 9 - - Blasto - - Clandestine - - Cyber - - Diablo - - Donk - - Gene - - Gib - - Max - - Newton - - North - - Omni - - Osiron - - Prima - - Psyke - - Sirius - - Tunnel - - Waffle - - a - - ampersand - - amyjon - - antilizard - - arrow - - b - - beepsky - - biohazard - - blueprint - - body - - bottle - - brush - - c - - carp - - cat - - chevron - - clawprint - - clown - - comma - - corgi - - credit - - cyka - - d - - danger - - disk - - dot - - dwarf - - e - - electricdanger - - end - - engie - - equals - - evac - - exclamationmark - - f - - face - - fireaxe - - firedanger - - food - - footprint - - g - - ghost - - guy - - h - - heart - - i - - j - - k - - l - - largebrush - - like - - line - - m - - matt - - med - - minus - - n - - nay - - o - - p - - pawprint - - peace - - percent - - plus - - pound - - prolizard - - q - - questionmark - - r - - radiation - - revolution - - rune1 - - rune2 - - rune3 - - rune4 - - rune5 - - rune6 - - s - - safe - - scroll - - shop - - shortline - - shotgun - - skull - - slash - - smallbrush - - snake - - space - - splatter - - star - - stickman - - t - - taser - - thinline - - toilet - - toolbox - - trade - - u - - uboa - - v - - w - - x - - y - - z diff --git a/Resources/Prototypes/Decals/crayons.yml b/Resources/Prototypes/Decals/crayons.yml new file mode 100644 index 0000000000..d13bd0e3f3 --- /dev/null +++ b/Resources/Prototypes/Decals/crayons.yml @@ -0,0 +1,916 @@ +- type: decal + id: 0 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: 0 + +- type: decal + id: 1 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: 1 + +- type: decal + id: 2 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: 2 + +- type: decal + id: 3 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: 3 + +- type: decal + id: 4 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: 4 + +- type: decal + id: 5 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: 5 + +- type: decal + id: 6 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: 6 + +- type: decal + id: 7 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: 7 + +- type: decal + id: 8 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: 8 + +- type: decal + id: 9 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: 9 + +- type: decal + id: Blasto + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Blasto + +- type: decal + id: Clandestine + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Clandestine + +- type: decal + id: Cyber + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Cyber + +- type: decal + id: Diablo + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Diablo + +- type: decal + id: Donk + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Donk + +- type: decal + id: Gene + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Gene + +- type: decal + id: Gib + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Gib + +- type: decal + id: Max + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Max + +- type: decal + id: Newton + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Newton + +- type: decal + id: North + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: North + +- type: decal + id: Omni + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Omni + +- type: decal + id: Osiron + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Osiron + +- type: decal + id: Prima + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Prima + +- type: decal + id: Psyke + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Psyke + +- type: decal + id: Sirius + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Sirius + +- type: decal + id: Tunnel + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Tunnel + +- type: decal + id: Waffle + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: Waffle + +- type: decal + id: a + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: a + +- type: decal + id: ampersand + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: ampersand + +- type: decal + id: amyjon + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: amyjon + +- type: decal + id: antilizard + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: antilizard + +- type: decal + id: arrow + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: arrow + +- type: decal + id: b + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: b + +- type: decal + id: beepsky + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: beepsky + +- type: decal + id: biohazard + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: biohazard + +- type: decal + id: blueprint + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: blueprint + +- type: decal + id: body + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: body + +- type: decal + id: bottle + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: bottle + +- type: decal + id: brush + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: brush + +- type: decal + id: c + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: c + +- type: decal + id: carp + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: carp + +- type: decal + id: cat + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: cat + +- type: decal + id: chevron + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: chevron + +- type: decal + id: clawprint + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: clawprint + +- type: decal + id: clown + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: clown + +- type: decal + id: comma + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: comma + +- type: decal + id: corgi + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: corgi + +- type: decal + id: credit + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: credit + +- type: decal + id: cyka + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: cyka + +- type: decal + id: d + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: d + +- type: decal + id: danger + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: danger + +- type: decal + id: disk + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: disk + +- type: decal + id: dot + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: dot + +- type: decal + id: dwarf + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: dwarf + +- type: decal + id: e + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: e + +- type: decal + id: electricdanger + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: electricdanger + +- type: decal + id: end + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: end + +- type: decal + id: engie + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: engie + +- type: decal + id: equals + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: equals + +- type: decal + id: evac + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: evac + +- type: decal + id: exclamationmark + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: exclamationmark + +- type: decal + id: f + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: f + +- type: decal + id: face + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: face + +- type: decal + id: fireaxe + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: fireaxe + +- type: decal + id: firedanger + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: firedanger + +- type: decal + id: food + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: food + +- type: decal + id: footprint + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: footprint + +- type: decal + id: g + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: g + +- type: decal + id: ghost + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: ghost + +- type: decal + id: guy + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: guy + +- type: decal + id: h + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: h + +- type: decal + id: heart + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: heart + +- type: decal + id: i + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: i + +- type: decal + id: j + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: j + +- type: decal + id: k + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: k + +- type: decal + id: l + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: l + +- type: decal + id: largebrush + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: largebrush + +- type: decal + id: like + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: like + +- type: decal + id: line + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: line + +- type: decal + id: m + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: m + +- type: decal + id: matt + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: matt + +- type: decal + id: med + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: med + +- type: decal + id: minus + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: minus + +- type: decal + id: n + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: n + +- type: decal + id: nay + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: nay + +- type: decal + id: o + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: o + +- type: decal + id: p + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: p + +- type: decal + id: pawprint + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: pawprint + +- type: decal + id: peace + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: peace + +- type: decal + id: percent + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: percent + +- type: decal + id: plus + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: plus + +- type: decal + id: pound + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: pound + +- type: decal + id: prolizard + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: prolizard + +- type: decal + id: q + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: q + +- type: decal + id: questionmark + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: questionmark + +- type: decal + id: r + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: r + +- type: decal + id: radiation + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: radiation + +- type: decal + id: revolution + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: revolution + +- type: decal + id: rune1 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: rune1 + +- type: decal + id: rune2 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: rune2 + +- type: decal + id: rune3 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: rune3 + +- type: decal + id: rune4 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: rune4 + +- type: decal + id: rune5 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: rune5 + +- type: decal + id: rune6 + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: rune6 + +- type: decal + id: s + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: s + +- type: decal + id: safe + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: safe + +- type: decal + id: scroll + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: scroll + +- type: decal + id: shop + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: shop + +- type: decal + id: shortline + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: shortline + +- type: decal + id: shotgun + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: shotgun + +- type: decal + id: skull + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: skull + +- type: decal + id: slash + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: slash + +- type: decal + id: smallbrush + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: smallbrush + +- type: decal + id: snake + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: snake + +- type: decal + id: space + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: space + +- type: decal + id: splatter + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: splatter + +- type: decal + id: star + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: star + +- type: decal + id: stickman + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: stickman + +- type: decal + id: t + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: t + +- type: decal + id: taser + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: taser + +- type: decal + id: thinline + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: thinline + +- type: decal + id: toilet + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: toilet + +- type: decal + id: toolbox + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: toolbox + +- type: decal + id: trade + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: trade + +- type: decal + id: u + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: u + +- type: decal + id: uboa + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: uboa + +- type: decal + id: v + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: v + +- type: decal + id: w + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: w + +- type: decal + id: x + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: x + +- type: decal + id: y + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: y + +- type: decal + id: z + tags: ["crayon"] + sprite: + sprite: Effects/crayondecals.rsi + state: z diff --git a/Resources/Prototypes/Entities/Clothing/Head/base.yml b/Resources/Prototypes/Entities/Clothing/Head/base.yml index 1fd8f34843..d3c636aee7 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/base.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/base.yml @@ -41,9 +41,7 @@ Piercing: 0.95 Heat: 0.90 Radiation: 0.25 - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity abstract: true diff --git a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml index 80f6d6c27d..bc277370b4 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/helmets.yml @@ -8,9 +8,7 @@ sprite: Clothing/Head/Helmets/bombsuit.rsi - type: Clothing sprite: Clothing/Head/Helmets/bombsuit.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -22,9 +20,7 @@ sprite: Clothing/Head/Helmets/cosmonaut.rsi - type: Clothing sprite: Clothing/Head/Helmets/cosmonaut.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -36,9 +32,7 @@ sprite: Clothing/Head/Helmets/cult.rsi - type: Clothing sprite: Clothing/Head/Helmets/cult.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -79,23 +73,19 @@ sprite: Clothing/Head/Helmets/light_riot.rsi - type: Clothing sprite: Clothing/Head/Helmets/light_riot.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase id: ClothingHeadHelmetScaf - name: scaf + name: scaf helmet description: A robust, strong helmet. components: - type: Sprite sprite: Clothing/Head/Helmets/scaf.rsi - type: Clothing sprite: Clothing/Head/Helmets/scaf.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -107,9 +97,7 @@ sprite: Clothing/Head/Helmets/spaceninja.rsi - type: Clothing sprite: Clothing/Head/Helmets/spaceninja.rsi - - type: Tag - tags: - - ConcealsFace # well only partially? + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -121,9 +109,7 @@ sprite: Clothing/Head/Helmets/syndicate.rsi - type: Clothing sprite: Clothing/Head/Helmets/syndicate.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -135,9 +121,7 @@ sprite: Clothing/Head/Helmets/templar.rsi - type: Clothing sprite: Clothing/Head/Helmets/templar.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -160,6 +144,4 @@ sprite: Clothing/Head/Helmets/wizardhelm.rsi - type: Clothing sprite: Clothing/Head/Helmets/wizardhelm.rsi - - type: Tag - tags: - - ConcealsFace \ No newline at end of file + - type: IngestionBlocker diff --git a/Resources/Prototypes/Entities/Clothing/Head/misc.yml b/Resources/Prototypes/Entities/Clothing/Head/misc.yml index 0a4d15f11b..4f3ef5fd51 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/misc.yml @@ -30,9 +30,7 @@ sprite: Clothing/Head/Misc/chickenhead.rsi - type: Clothing sprite: Clothing/Head/Misc/chickenhead.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -55,9 +53,7 @@ sprite: Clothing/Head/Misc/pumpkin.rsi - type: Clothing sprite: Clothing/Head/Misc/pumpkin.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -80,9 +76,7 @@ sprite: Clothing/Head/Misc/richard.rsi - type: Clothing sprite: Clothing/Head/Misc/richard.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -94,9 +88,7 @@ sprite: Clothing/Head/Misc/skubhead.rsi - type: Clothing sprite: Clothing/Head/Misc/skubhead.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -108,9 +100,7 @@ sprite: Clothing/Head/Misc/xenom.rsi - type: Clothing sprite: Clothing/Head/Misc/xenom.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase @@ -122,9 +112,7 @@ sprite: Clothing/Head/Misc/xenos.rsi - type: Clothing sprite: Clothing/Head/Misc/xenos.rsi - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: ClothingHeadBase diff --git a/Resources/Prototypes/Entities/Clothing/Head/welding.yml b/Resources/Prototypes/Entities/Clothing/Head/welding.yml index 375f3ecebd..ba2ab93044 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/welding.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/welding.yml @@ -4,9 +4,7 @@ name: welding mask abstract: true components: - - type: Tag - tags: - - ConcealsFace + - type: IngestionBlocker - type: entity parent: WeldingMaskBase diff --git a/Resources/Prototypes/Entities/Clothing/Masks/masks.yml b/Resources/Prototypes/Entities/Clothing/Masks/masks.yml index cb9afb25a7..9112452d8e 100644 --- a/Resources/Prototypes/Entities/Clothing/Masks/masks.yml +++ b/Resources/Prototypes/Entities/Clothing/Masks/masks.yml @@ -9,6 +9,7 @@ - type: Clothing sprite: Clothing/Mask/gas.rsi - type: BreathMask + - type: IngestionBlocker - type: entity parent: ClothingMaskBase @@ -21,6 +22,7 @@ - type: Clothing sprite: Clothing/Mask/breath.rsi - type: BreathMask + - type: IngestionBlocker - type: entity parent: ClothingMaskBase @@ -69,3 +71,4 @@ - type: Clothing sprite: Clothing/Mask/sterile.rsi - type: BreathMask + - type: IngestionBlocker diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index 6cd0dda45a..87d1b55132 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -16,7 +16,7 @@ Blunt: 0.9 Slash: 0.9 Piercing: 0.9 - Heat: 0.4 # this technically means it protects against fires pretty well? + Heat: 0.4 # this technically means it protects against fires pretty well? -heat is just for lasers and stuff, not atmos temperature - type: entity parent: ClothingOuterBase @@ -98,7 +98,7 @@ - type: entity parent: ClothingOuterArmorHeavy id: ClothingOuterArmorMagusblue - name: magus blue + name: blue magus armor description: An blue armored suit that provides good protection. components: - type: Sprite @@ -109,7 +109,7 @@ - type: entity parent: ClothingOuterArmorHeavy id: ClothingOuterArmorMagusred - name: magus red + name: red magus armor description: A red armored suit that provides good protection. components: - type: Sprite @@ -127,6 +127,13 @@ sprite: Clothing/OuterClothing/Armor/riot.rsi - type: Clothing sprite: Clothing/OuterClothing/Armor/riot.rsi + - type: Armor + modifiers: + coefficients: + Blunt: 0.4 + Slash: 0.4 + Piercing: 0.9 + Heat: 0.9 - type: entity parent: ClothingOuterBase @@ -138,3 +145,10 @@ sprite: Clothing/OuterClothing/Armor/scaf.rsi - type: Clothing sprite: Clothing/OuterClothing/Armor/scaf.rsi + - type: Armor + modifiers: + coefficients: + Blunt: 0.7 + Slash: 0.7 + Piercing: 0.4 + Heat: 0.9 diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml index 0e4d231da6..055546bb53 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/coats.yml @@ -54,7 +54,7 @@ coefficients: Blunt: 0.7 Slash: 0.7 - Piercing: 0.7 + Piercing: 0.4 Heat: 0.7 - type: entity diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml index 23924e4a2a..89b26eaeeb 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml @@ -11,6 +11,14 @@ - type: PressureProtection highPressureMultiplier: 0.5 lowPressureMultiplier: 100 + - type: Armor + modifiers: + coefficients: + Blunt: 0.9 + Slash: 0.9 + Piercing: 0.95 + Heat: 0.8 #All default but having a little better heat prot makes sense based on the description + Radiation: 0.25 - type: entity parent: ClothingOuterHardsuitBase @@ -25,6 +33,14 @@ - type: PressureProtection highPressureMultiplier: 0.5 lowPressureMultiplier: 100 + - type: Armor + modifiers: + coefficients: + Blunt: 0.8 + Slash: 0.8 + Piercing: 0.85 + Heat: 0.8 + Radiation: 0.1 - type: entity parent: ClothingOuterHardsuitBase @@ -39,6 +55,14 @@ - type: PressureProtection highPressureMultiplier: 0.45 lowPressureMultiplier: 100 + - type: Armor + modifiers: + coefficients: + Blunt: 0.2 #best armor in the game + Slash: 0.2 + Piercing: 0.2 + Heat: 0.2 + Radiation: 0.2 - type: entity parent: ClothingOuterHardsuitBase @@ -53,6 +77,14 @@ - type: PressureProtection highPressureMultiplier: 0.65 lowPressureMultiplier: 100 + - type: Armor + modifiers: + coefficients: + Blunt: 0.90 + Slash: 0.90 + Piercing: 0.95 + Heat: 0.90 + Radiation: 0.05 #all default, rad armor is higher - type: entity parent: ClothingOuterHardsuitBase @@ -67,6 +99,14 @@ - type: PressureProtection highPressureMultiplier: 0.45 lowPressureMultiplier: 100 + - type: Armor + modifiers: + coefficients: + Blunt: 0.9 + Slash: 0.9 + Piercing: 0.95 + Heat: 0.8 + Radiation: 0.05 #combines the engi and atmos bonuses - type: entity parent: ClothingOuterHardsuitBase @@ -109,6 +149,14 @@ - type: PressureProtection highPressureMultiplier: 0.75 lowPressureMultiplier: 100 + - type: Armor + modifiers: + coefficients: + Blunt: 0.6 #increased blunt as per description, for bomb survival + Slash: 0.9 + Piercing: 0.95 + Heat: 0.9 + Radiation: 0.25 - type: entity parent: ClothingOuterHardsuitBase @@ -137,9 +185,17 @@ - type: PressureProtection highPressureMultiplier: 0.75 lowPressureMultiplier: 100 + - type: Armor + modifiers: + coefficients: + Blunt: 0.8 + Slash: 0.8 + Piercing: 0.7 + Heat: 0.8 #buffed combat stats all round + Radiation: 0.25 - type: entity - parent: ClothingOuterHardsuitBase + parent: ClothingOuterHardsuitSecurity #set parent to sec hardsuit, hopefully wont break anything id: ClothingOuterHardsuitSecurityRed name: head of security's hardsuit description: A special suit that protects against hazardous, low pressure environments. Has an additional layer of armor. @@ -165,6 +221,14 @@ - type: PressureProtection highPressureMultiplier: 0.45 lowPressureMultiplier: 100 + - type: Armor + modifiers: + coefficients: + Blunt: 0.6 + Slash: 0.6 + Piercing: 0.4 + Heat: 0.4 #all the combat numbers are pumped up here + Radiation: 0.20 - type: entity parent: ClothingOuterHardsuitBase @@ -179,3 +243,11 @@ - type: PressureProtection highPressureMultiplier: 0.45 lowPressureMultiplier: 100 + - type: Armor + modifiers: + coefficients: + Blunt: 0.6 + Slash: 0.6 + Piercing: 0.4 + Heat: 0.4 #same as the nukie suit + Radiation: 0.20 diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml index af62d16e74..eb8c08c4df 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/suits.yml @@ -8,6 +8,13 @@ sprite: Clothing/OuterClothing/Suits/bombsuit.rsi - type: Clothing sprite: Clothing/OuterClothing/Suits/bombsuit.rsi + - type: Armor + modifiers: + coefficients: + Blunt: 0.2 #big blast protection, a little protection from heat, negligable cushioning from other damage cos its thick + Slash: 0.9 + Piercing: 0.9 + Heat: 0.75 - type: entity parent: ClothingOuterBase @@ -35,6 +42,11 @@ coefficient: 0.001 - type: Clothing sprite: Clothing/OuterClothing/Suits/fire.rsi + - type: Armor + modifiers: + coefficients: + Slash: 0.9 + Heat: 0.7 #makes sense right? - type: entity parent: ClothingOuterBase @@ -74,6 +86,13 @@ sprite: Clothing/OuterClothing/Suits/spaceninja.rsi - type: Clothing sprite: Clothing/OuterClothing/Suits/spaceninja.rsi + - type: Armor + modifiers: + coefficients: + Blunt: 0.6 + Slash: 0.6 + Piercing: 0.6 + Heat: 0.6 - type: entity parent: ClothingOuterBase diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml index 061acb6335..e309e41a00 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/vests.yml @@ -8,6 +8,13 @@ sprite: Clothing/OuterClothing/Vests/mercwebvest.rsi - type: Clothing sprite: Clothing/OuterClothing/Vests/mercwebvest.rsi + - type: Armor + modifiers: + coefficients: + Blunt: 0.7 #slightly better overall protection but slightly worse than kevlar against bullets seems sensible + Slash: 0.7 + Piercing: 0.5 + Heat: 0.9 - type: entity parent: ClothingOuterBase @@ -19,6 +26,13 @@ sprite: Clothing/OuterClothing/Vests/webvest.rsi - type: Clothing sprite: Clothing/OuterClothing/Vests/webvest.rsi + - type: Armor + modifiers: + coefficients: + Blunt: 0.6 #ballistic plates = better protection + Slash: 0.6 + Piercing: 0.3 + Heat: 0.9 - type: entity parent: ClothingOuterBase diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml index 65ed728cb5..f692843020 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpskirts.yml @@ -41,6 +41,10 @@ sprite: Clothing/Uniforms/Jumpskirt/ce.rsi - type: Clothing sprite: Clothing/Uniforms/Jumpskirt/ce.rsi + - type: Armor + modifiers: + coefficients: + Radiation: 0.8 - type: entity parent: ClothingUniformSkirtBase @@ -156,7 +160,7 @@ parent: ClothingUniformSkirtBase id: ClothingUniformJumpskirtHoSParadeMale name: head of security's parade uniform - description: A male head of security's luxury-wear, for special occasions. + description: A head of security's luxury-wear, for special occasions. components: - type: Sprite sprite: Clothing/Uniforms/Jumpskirt/hos_parade.rsi diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml index 5749948973..5894f40337 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/jumpsuits.yml @@ -63,6 +63,11 @@ sprite: Clothing/Uniforms/Jumpsuit/ce.rsi - type: Clothing sprite: Clothing/Uniforms/Jumpsuit/ce.rsi + - type: Armor + modifiers: + coefficients: + Radiation: 0.8 + - type: entity parent: ClothingUniformBase diff --git a/Resources/Prototypes/Entities/Effects/crayondecals.yml b/Resources/Prototypes/Entities/Effects/crayondecals.yml deleted file mode 100644 index b2ffa3862f..0000000000 --- a/Resources/Prototypes/Entities/Effects/crayondecals.yml +++ /dev/null @@ -1,16 +0,0 @@ -- type: entity - abstract: true - id: CrayonDecal - name: crayon drawing - description: "Graffiti. Damn kids." - components: - - type: Clickable - - type: InteractionOutline - - type: Physics - - type: Sprite - sprite: Effects/crayondecals.rsi - state: corgi - - type: Cleanable - - type: Appearance - visuals: - - type: CrayonDecalVisualizer diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 5bc0432185..d00a69ca86 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -310,6 +310,9 @@ description: New church of neo-darwinists actually believe that EVERY animal evolved from a monkey. Tastes like pork, and killing them is both fun and relaxing. components: - type: GhostTakeoverAvailable + makeSentient: true + name: monkey + description: Ook ook! - type: Sprite drawdepth: Mobs layers: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index 538049024d..503ba2497e 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -102,6 +102,37 @@ seed: towercap - type: Log +- type: entity + name: nettle + description: Stingy little prick. + id: Nettle + parent: ProduceBase + components: + - type: Sprite + sprite: Objects/Specific/Hydroponics/nettle.rsi + - type: Item + size: 10 + sprite: Objects/Specific/Hydroponics/nettle.rsi + prefix: inhand + - type: MeleeWeapon + damage: + types: + Heat: 10 + - type: SolutionContainerManager + solutions: + food: + reagents: + - ReagentId: Histamine + Quantity: 25 + - type: Produce + seed: nettle + - type: MeleeChemicalInjector + transferAmount: 6 #To OD someone you would need 2 nettles and about 6-7 hits, the DOT is likely to crit them if they are running away with almost no health + - type: Extractable + grindableSolutionName: food + - type: InjectableSolution + solution: food + - type: entity name: banana parent: FoodProduceBase diff --git a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml index 2efe6a83b4..4d8ad1d51b 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml @@ -181,3 +181,14 @@ seed: cannabis - type: Sprite sprite: Objects/Specific/Hydroponics/cannabis.rsi + +- type: entity + parent: SeedBase + name: packet of nettle seeds + description: "Handle with gloves." + id: NettleSeeds + components: + - type: Seed + seed: nettle + - type: Sprite + sprite: Objects/Specific/Hydroponics/nettle.rsi diff --git a/Resources/Prototypes/Entities/Objects/Tools/flare.yml b/Resources/Prototypes/Entities/Objects/Tools/flare.yml index 06d0a57b55..10175d6a08 100644 --- a/Resources/Prototypes/Entities/Objects/Tools/flare.yml +++ b/Resources/Prototypes/Entities/Objects/Tools/flare.yml @@ -10,8 +10,8 @@ - type: ExpendableLight spentName: spent flare spentDesc: It looks like this flare has burnt out. What a bummer. - glowDuration: 150 - fadeOutDuration: 4 + glowDuration: 45 + fadeOutDuration: 15 iconStateOn: flare_unlit iconStateSpent: flare_spent turnOnBehaviourID: turn_on @@ -52,15 +52,15 @@ minDuration: 0.02 maxDuration: 0.06 startValue: 6.0 - endValue: 9.0 + endValue: 15.0 property: Energy isLooped: true - !type:FadeBehaviour # have the radius start small and get larger as it starts to burn id: turn_on interpolate: Linear - maxDuration: 8.0 - startValue: 1.0 - endValue: 6.0 + maxDuration: 45.0 #duration of light + startValue: 2.5 + endValue: 10.0 property: Radius - !type:RandomizeBehaviour # weaker flicker as it fades out id: fade_out @@ -74,7 +74,7 @@ - !type:FadeBehaviour # fade out radius as it burns out id: fade_out interpolate: Linear - maxDuration: 4.0 - startValue: 6.0 + maxDuration: 15.0 + startValue: 8.0 endValue: 1.0 property: Radius diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 3943ce4fa3..3af39fcf6b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -1,6 +1,41 @@ +- type: entity + id: BatteryGunBase + parent: BaseItem + abstract: true + components: + - type: RangedWeapon + - type: BatteryBarrel + fireRate: 2 + minAngle: 0 + maxAngle: 45 + angleIncrease: 15 + angleDecay: 45 + ammoPrototype: RedLaser + currentSelector: Single + allSelectors: + - Single + soundGunshot: + path: /Audio/Weapons/Guns/Gunshots/laser.ogg + cellSlot: + ejectOnUse: true + insertSound: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg + ejectSound: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg + soundOptions: + volume: -2 + startingItem: PowerCellSmallStandard + whitelist: + components: + - Battery + - type: Appearance + visuals: + - type: MagVisualizer + magState: mag + steps: 5 + zeroVisible: false + - type: entity name: retro laser gun - parent: BaseItem + parent: BatteryGunBase id: LaserGun description: A weapon using light amplified by the stimulated emission of radiation. components: @@ -19,23 +54,7 @@ - type: RangedWeapon - type: BatteryBarrel minAngle: 10 - maxAngle: 45 - angleIncrease: 15 - angleDecay: 45 - currentSelector: Single - allSelectors: - - Single - fireRate: 2 - powerCellPrototype: PowerCellSmallStandard - powerCellRemovable: true fireCost: 40 - ammoPrototype: RedLaser - soundGunshot: - path: /Audio/Weapons/Guns/Gunshots/laser.ogg - soundPowerCellInsert: - path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg - soundPowerCellEject: - path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg - type: Appearance visuals: - type: MagVisualizer @@ -45,7 +64,7 @@ - type: entity name: makeshift laser gun - parent: BaseItem + parent: BatteryGunBase id: MakeshiftLaser description: Better pray it won't burn your hands off. components: @@ -61,36 +80,12 @@ - type: Item size: 24 sprite: Objects/Weapons/Guns/Battery/makeshift.rsi - - type: RangedWeapon - type: BatteryBarrel - currentSelector: Single - allSelectors: - - Single - fireRate: 2 - minAngle: 0 - maxAngle: 45 - angleIncrease: 15 - angleDecay: 45 - powerCellPrototype: PowerCellSmallStandard - powerCellRemovable: true fireCost: 40 - ammoPrototype: RedLaser - soundGunshot: - path: /Audio/Weapons/Guns/Gunshots/laser.ogg - soundPowerCellInsert: - path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg - soundPowerCellEject: - path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg - - type: Appearance - visuals: - - type: MagVisualizer - magState: mag - steps: 5 - zeroVisible: false - type: entity name: svalinn laser pistol - parent: BaseItem + parent: BatteryGunBase id: LaserPistolSvalinn description: A cheap and widely used laser pistol. components: @@ -106,36 +101,12 @@ - type: Item size: 24 sprite: Objects/Weapons/Guns/Battery/svalinn.rsi - - type: RangedWeapon - type: BatteryBarrel - currentSelector: Single - allSelectors: - - Single - fireRate: 2 - minAngle: 0 - maxAngle: 45 - angleIncrease: 15 - angleDecay: 45 - powerCellPrototype: PowerCellSmallStandard - powerCellRemovable: true fireCost: 20 - ammoPrototype: RedLaser - soundGunshot: - path: /Audio/Weapons/Guns/Gunshots/laser.ogg - soundPowerCellInsert: - path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg - soundPowerCellEject: - path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg - - type: Appearance - visuals: - - type: MagVisualizer - magState: mag - steps: 5 - zeroVisible: false - type: entity name: cog laser carbine - parent: BaseItem + parent: BatteryGunBase id: LaserRifleCog description: Favoured by Nanotrasen Security for being cheap and easy to use. components: @@ -151,36 +122,12 @@ - type: Item size: 24 sprite: Objects/Weapons/Guns/Battery/cog.rsi - - type: RangedWeapon - type: BatteryBarrel - currentSelector: Single - allSelectors: - - Single - fireRate: 2 - minAngle: 0 - maxAngle: 45 - angleIncrease: 15 - angleDecay: 45 - powerCellPrototype: PowerCellSmallStandard - powerCellRemovable: true fireCost: 10 - ammoPrototype: RedLaser - soundGunshot: - path: /Audio/Weapons/Guns/Gunshots/laser.ogg - soundPowerCellInsert: - path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg - soundPowerCellEject: - path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg - - type: Appearance - visuals: - - type: MagVisualizer - magState: mag - steps: 5 - zeroVisible: false - type: entity name: laser cannon - parent: BaseItem + parent: BatteryGunBase id: LaserCannon description: A heavy duty, high powered laser weapon. components: @@ -196,36 +143,25 @@ - type: Item size: 24 sprite: Objects/Weapons/Guns/Battery/laser_cannon.rsi - - type: RangedWeapon - type: BatteryBarrel - currentSelector: Single - allSelectors: - - Single - fireRate: 2 - minAngle: 0 - maxAngle: 45 - angleIncrease: 15 - angleDecay: 45 - powerCellPrototype: PowerCellSmallSuper - powerCellRemovable: true fireCost: 600 ammoPrototype: RedHeavyLaser soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser_cannon.ogg - soundPowerCellInsert: - path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg - soundPowerCellEject: - path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg - - type: Appearance - visuals: - - type: MagVisualizer - magState: mag - steps: 5 - zeroVisible: false + cellSlot: + ejectOnUse: true + insertSound: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg + ejectSound: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg + soundOptions: + volume: -2 + startingItem: PowerCellSmallSuper + whitelist: + components: + - Battery - type: entity name: x-ray cannon - parent: BaseItem + parent: BatteryGunBase id: XrayCannon description: An experimental weapon that uses concentrated x-ray energy against its target. components: @@ -241,26 +177,21 @@ - type: Item size: 24 sprite: Objects/Weapons/Guns/Battery/xray.rsi - - type: RangedWeapon - type: BatteryBarrel - currentSelector: Single - allSelectors: - - Single - fireRate: 2 - minAngle: 0 - maxAngle: 45 - angleIncrease: 15 - angleDecay: 45 - powerCellPrototype: PowerCellSmallSuper - powerCellRemovable: true fireCost: 600 ammoPrototype: XrayLaser soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser3.ogg - soundPowerCellInsert: - path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg - soundPowerCellEject: - path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg + cellSlot: + ejectOnUse: true + insertSound: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg + ejectSound: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg + soundOptions: + volume: -2 + startingItem: PowerCellSmallSuper + whitelist: + components: + - Battery - type: Appearance visuals: - type: MagVisualizer @@ -270,7 +201,7 @@ - type: entity name: taser - parent: BaseItem + parent: BatteryGunBase id: TaserGun description: A low-capacity, energy-based stun gun used by security teams to subdue targets at range. components: @@ -293,26 +224,23 @@ Slots: - Belt HeldPrefix: taser4 - - type: RangedWeapon - type: BatteryBarrel - currentSelector: Single - allSelectors: - - Single - fireRate: 2 minAngle: 5 - maxAngle: 45 angleIncrease: 20 - angleDecay: 15 - powerCellPrototype: PowerCellSmallStandard - powerCellRemovable: false fireCost: 80 ammoPrototype: BulletTaser soundGunshot: path: /Audio/Weapons/Guns/Gunshots/taser.ogg - soundPowerCellInsert: - path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg - soundPowerCellEject: - path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg + cellSlot: + insertSound: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg + ejectSound: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg + soundOptions: + volume: -2 + locked: true + startingItem: PowerCellSmallStandard + whitelist: + components: + - Battery - type: Appearance visuals: - type: MagVisualizer @@ -322,7 +250,7 @@ - type: entity name: laser gun - parent: BaseItem + parent: BatteryGunBase id: LaserSecGun description: A laser gun. components: @@ -338,28 +266,16 @@ - type: Item size: 24 sprite: Objects/Weapons/Guns/Battery/laser_gun.rsi - - type: RangedWeapon - type: BatteryBarrel - minAngle: 10 - maxAngle: 45 - angleIncrease: 15 - angleDecay: 45 - currentSelector: Single - allSelectors: - - Single + minAngle: 1 fireRate: 6 - powerCellPrototype: PowerCellMediumStandard - powerCellRemovable: true - ammoPrototype: RedLaser - soundGunshot: - path: /Audio/Weapons/Guns/Gunshots/laser.ogg - soundPowerCellInsert: - path: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg - soundPowerCellEject: - path: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg - - type: Appearance - visuals: - - type: MagVisualizer - magState: mag - steps: 5 - zeroVisible: false + cellSlot: + ejectOnUse: true + insertSound: /Audio/Weapons/Guns/MagIn/revolver_magin.ogg + ejectSound: /Audio/Weapons/Guns/MagOut/revolver_magout.ogg + soundOptions: + volume: -2 + startingItem: PowerCellMediumStandard + whitelist: + components: + - Battery diff --git a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml index 5a7f6a420e..d59dc02d19 100644 --- a/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml +++ b/Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml @@ -79,6 +79,54 @@ - type: AccessReader access: [["Command"]] +- type: entity + parent: AirlockCommand + id: AirlockCaptainLocked + suffix: Captain, Locked + components: + - type: AccessReader + access: [["Captain"]] + +- type: entity + parent: AirlockCommand + id: AirlockChiefMedicalOfficerLocked + suffix: ChiefMedicalOfficer, Locked + components: + - type: AccessReader + access: [["ChiefMedicalOfficer"]] + +- type: entity + parent: AirlockCommand + id: AirlockChiefEngineerLocked + suffix: ChiefEngineer, Locked + components: + - type: AccessReader + access: [["ChiefEngineer"]] + +- type: entity + parent: AirlockCommand + id: AirlockHeadOfSecurityLocked + suffix: HeadOfSecurity, Locked + components: + - type: AccessReader + access: [["HeadOfSecurity"]] + +- type: entity + parent: AirlockCommand + id: AirlockResearchDirectorLocked + suffix: ResearchDirector, Locked + components: + - type: AccessReader + access: [["ResearchDirector"]] + +- type: entity + parent: AirlockCommand + id: AirlockHeadOfPersonnelLocked + suffix: HeadOfPersonnel, Locked + components: + - type: AccessReader + access: [["HeadOfPersonnel"]] + - type: entity parent: AirlockSecurity id: AirlockSecurityLocked @@ -87,14 +135,6 @@ - type: AccessReader access: [["Security"]] -- type: entity - parent: AirlockSecurity - id: AirlockVaultLocked - suffix: Vault, Locked - components: - - type: AccessReader - access: [["Security", "Command"]] - - type: entity parent: AirlockSecurity id: AirlockArmoryLocked @@ -103,6 +143,14 @@ - type: AccessReader access: [["Armory"]] +- type: entity + parent: AirlockSecurity + id: AirlockVaultLocked + suffix: Vault, Locked + components: + - type: AccessReader + access: [["Security", "Command"]] + - type: entity parent: AirlockCommand id: AirlockEVALocked @@ -280,3 +328,11 @@ components: - type: AccessReader access: [["Security"]] + +- type: entity + parent: AirlockMaint + id: AirlockMaintHOPLocked + suffix: HeadOfPersonnel, Locked + components: + - type: AccessReader + access: [["HeadOfPersonnel"]] diff --git a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml index f5aa2f58a6..1ea3b3c08e 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/chairs.yml @@ -13,12 +13,12 @@ - type: Fixtures fixtures: - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.5,0.25,0.5" + !type:PhysShapeCircle + radius: 0.2 mask: - Impassable - - VaultImpassable - SmallImpassable + mass: 10 - type: Sprite sprite: Structures/Furniture/chairs.rsi noRot: true @@ -51,16 +51,6 @@ - type: Rotatable - type: Sprite state: chair - - type: Physics - - type: Fixtures - fixtures: - - shape: - !type:PhysShapeAabb - bounds: "-0.45,-0.45,0.45,0.05" - mask: - - Impassable - - VaultImpassable - - SmallImpassable - type: Construction graph: seat node: chair @@ -74,17 +64,6 @@ - type: Anchorable - type: Sprite state: stool - - type: Physics - bodyType: Static - - type: Fixtures - fixtures: - - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.5,0.25,0.05" - mask: - - Impassable - - VaultImpassable - - SmallImpassable - type: Construction graph: seat node: stool @@ -101,17 +80,6 @@ - type: Anchorable - type: Sprite state: bar - - type: Physics - bodyType: Static - - type: Fixtures - fixtures: - - shape: - !type:PhysShapeAabb - bounds: "-0.2,-0.5,0.25,0.2" - mask: - - Impassable - - VaultImpassable - - SmallImpassable - type: Construction graph: seat node: stoolBar @@ -125,16 +93,6 @@ rotateWhileAnchored: true - type: Sprite state: office-white - - type: Physics - - type: Fixtures - fixtures: - - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.49,0.25,0.37" - mask: - - Impassable - - VaultImpassable - - SmallImpassable - type: Construction graph: seat node: chairOffice @@ -161,17 +119,6 @@ - type: Anchorable - type: Sprite state: comfy - - type: Physics - bodyType: Static - - type: Fixtures - fixtures: - - shape: - !type:PhysShapeAabb - bounds: "-0.3,-0.45,0.3,0.35" - mask: - - Impassable - - VaultImpassable - - SmallImpassable - type: Construction graph: seat node: chairComfy @@ -184,16 +131,6 @@ - type: Sprite state: wooden - type: Rotatable - - type: Physics - - type: Fixtures - fixtures: - - shape: - !type:PhysShapeAabb - bounds: "-0.25,-0.37,0.24,0.49" - mask: - - Impassable - - VaultImpassable - - SmallImpassable - type: Construction graph: seat node: chairWood @@ -232,14 +169,3 @@ - type: Sprite state: shuttle netsync: false - - type: Physics - bodyType: Static - - type: Fixtures - fixtures: - - shape: - !type:PhysShapeAabb - bounds: "-0.45,-0.45,0.45,0.05" - mask: - - Impassable - - VaultImpassable - - SmallImpassable diff --git a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml index 11a620b6f5..ad961c353b 100644 --- a/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml +++ b/Resources/Prototypes/Entities/Structures/Furniture/toilet.yml @@ -17,12 +17,6 @@ solutions: toilet: maxVol: 250 - - type: Physics - - type: Fixtures - fixtures: - - shape: - !type:PhysShapeAabb {} - layer: [ Passable ] - type: Construction graph: toilet node: toilet diff --git a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml index 1bcd889158..bd40a16d06 100644 --- a/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml +++ b/Resources/Prototypes/Entities/Structures/Storage/Crates/crates.yml @@ -1,5 +1,5 @@ - type: entity - id: CrateGenericonimo + id: CrateGenericSteel name: crate parent: CrateGeneric components: @@ -20,6 +20,10 @@ - type: StorageVisualizer state_open: crate_open state_closed: crate_door + - type: Construction + graph: CrateGenericSteel + node: crategenericsteel + - type: entity id: CratePlastic @@ -43,6 +47,9 @@ - type: StorageVisualizer state_open: plasticcrate_open state_closed: plasticcrate_door + - type: Construction + graph: CratePlastic + node: crateplastic - type: entity id: CrateFreezer @@ -66,6 +73,9 @@ - type: StorageVisualizer state_open: freezer_open state_closed: freezer_door + - type: Construction + graph: CrateGenericSteel + node: crategenericsteel - type: entity id: CrateHydroponics @@ -89,6 +99,9 @@ - type: StorageVisualizer state_open: hydrocrate_open state_closed: hydrocrate_door + - type: Construction + graph: CrateGenericSteel + node: crategenericsteel - type: entity id: CrateMedical @@ -112,6 +125,9 @@ - type: StorageVisualizer state_open: medicalcrate_open state_closed: medicalcrate_door + - type: Construction + graph: CrateGenericSteel + node: crategenericsteel - type: entity id: CrateRadiation @@ -136,6 +152,9 @@ - type: StorageVisualizer state_open: radiationcrate_open state_closed: radiationcrate_door + - type: Construction + graph: CrateGenericSteel + node: crategenericsteel - type: entity id: CrateInternals @@ -159,6 +178,9 @@ - type: StorageVisualizer state_open: o2crate_open state_closed: o2crate_door + - type: Construction + graph: CrateGenericSteel + node: crategenericsteel - type: entity id: CrateElectrical @@ -182,6 +204,9 @@ - type: StorageVisualizer state_open: electricalcrate_open state_closed: electricalcrate_door + - type: Construction + graph: CrateGenericSteel + node: crategenericsteel - type: entity id: CrateEngineering @@ -205,6 +230,9 @@ - type: StorageVisualizer state_open: engicrate_open state_closed: engicrate_door + - type: Construction + graph: CrateGenericSteel + node: crategenericsteel - type: entity id: CrateScience @@ -228,6 +256,9 @@ - type: StorageVisualizer state_open: scicrate_open state_closed: scicrate_door + - type: Construction + graph: CrateGenericSteel + node: crategenericsteel - type: entity id: CrateSurgery @@ -251,6 +282,9 @@ - type: StorageVisualizer state_open: surgerycrate_open state_closed: surgerycrate_door + - type: Construction + graph: CrateGenericSteel + node: crategenericsteel # Secure Crates @@ -282,6 +316,9 @@ - type: StorageVisualizer state_open: secgearcrate_open state_closed: secgearcrate_door + - type: Construction + graph: CrateSecure + node: cratesecure - type: entity id: CrateEngineeringSecure @@ -311,6 +348,9 @@ - type: StorageVisualizer state_open: engicratesecure_open state_closed: engicratesecure_door + - type: Construction + graph: CrateSecure + node: cratesecure - type: entity id: CrateMedicalSecure @@ -340,6 +380,9 @@ - type: StorageVisualizer state_open: medicalcratesecure_open state_closed: medicalcratesecure_door + - type: Construction + graph: CrateSecure + node: cratesecure - type: entity id: CratePrivateSecure @@ -368,6 +411,9 @@ - type: StorageVisualizer state_open: privatecrate_open state_closed: privatecrate_door + - type: Construction + graph: CrateSecure + node: cratesecure - type: entity id: CrateScienceSecure @@ -397,7 +443,10 @@ - type: StorageVisualizer state_open: scicratesecure_open state_closed: scicratesecure_door - + - type: Construction + graph: CrateSecure + node: cratesecure + - type: entity id: CratePlasma name: plasma crate @@ -426,6 +475,9 @@ - type: StorageVisualizer state_open: plasmacrate_open state_closed: plasmacrate_door + - type: Construction + graph: CrateSecure + node: cratesecure - type: entity id: CrateSecure @@ -454,6 +506,9 @@ - type: StorageVisualizer state_open: securecrate_open state_closed: securecrate_door + - type: Construction + graph: CrateSecure + node: cratesecure - type: entity id: CrateHydroSecure @@ -483,7 +538,10 @@ - type: StorageVisualizer state_open: hydrocratesecure_open state_closed: hydrocratesecure_door - + - type: Construction + graph: CrateSecure + node: cratesecure + - type: entity id: CrateWeaponSecure name: secure weapon crate @@ -509,6 +567,9 @@ - type: StorageVisualizer state_open: weaponcrate_open state_closed: weaponcrate_door + - type: Construction + graph: CrateSecure + node: cratesecure - type: entity id: CrateLivestock @@ -569,4 +630,5 @@ - SmallImpassable - type: Construction graph: CrateLivestock - node: start + node: cratelivestock + diff --git a/Resources/Prototypes/Entities/Structures/Walls/grille.yml b/Resources/Prototypes/Entities/Structures/Walls/grille.yml index 02e8ec7b9d..5e6b6a9650 100644 --- a/Resources/Prototypes/Entities/Structures/Walls/grille.yml +++ b/Resources/Prototypes/Entities/Structures/Walls/grille.yml @@ -85,17 +85,6 @@ graph: grille node: grilleBroken deconstructionTarget: start - - type: Physics - - type: Fixtures - fixtures: - - shape: - !type:PhysShapeAabb - bounds: "-0.5,-0.5,0.5,0.5" - mass: 50 - layer: - - Passable - mask: - - Passable - type: Damageable damageContainer: Inorganic damageModifierSet: FlimsyMetallic diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml index 6a748b5dbf..02d9b2c056 100644 --- a/Resources/Prototypes/Hydroponics/seeds.yml +++ b/Resources/Prototypes/Hydroponics/seeds.yml @@ -424,3 +424,26 @@ Min: 1 Max: 10 PotencyDivisor: 10 + +- type: seed + id: nettle + name: nettle + seedName: nettle + displayName: nettles + plantRsi: Objects/Specific/Hydroponics/nettle.rsi + productPrototypes: + - Nettle + lifespan: 25 + maturation: 8 + production: 6 + yield: 3 + potency: 20 + growthStages: 5 + idealLight: 8 + waterConsumption: 6 + idealHeat: 298 + chemicals: + Histamine: + Min: 1 + Max: 25 + PotencyDivisor: 4 diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/crategenericsteel.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/crategenericsteel.yml new file mode 100644 index 0000000000..d3b6f82446 --- /dev/null +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/crategenericsteel.yml @@ -0,0 +1,28 @@ +- type: constructionGraph + id: CrateGenericSteel + start: start + graph: + - node: start + edges: + - to: crategenericsteel + steps: + - material: Steel + amount: 5 + doAfter: 5 + + + - node: crategenericsteel + entity: CrateGenericSteel + edges: + - to: start + steps: + - tool: Screwing + doAfter: 5 + conditions: + - !type:StorageWelded + welded: false + completed: + - !type:SpawnPrototype + prototype: SheetSteel1 + amount: 5 + - !type:DeleteEntity {} \ No newline at end of file diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/crateplastic.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/crateplastic.yml new file mode 100644 index 0000000000..ae5167afe1 --- /dev/null +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/crateplastic.yml @@ -0,0 +1,28 @@ +- type: constructionGraph + id: CratePlastic + start: start + graph: + - node: start + edges: + - to: crateplastic + steps: + - material: Plastic + amount: 5 + doAfter: 5 + + + - node: crateplastic + entity: CratePlastic + edges: + - to: start + steps: + - tool: Screwing + doAfter: 5 + conditions: + - !type:StorageWelded + welded: false + completed: + - !type:SpawnPrototype + prototype: SheetPlastic1 + amount: 5 + - !type:DeleteEntity {} \ No newline at end of file diff --git a/Resources/Prototypes/Recipes/Crafting/Graphs/cratesecure.yml b/Resources/Prototypes/Recipes/Crafting/Graphs/cratesecure.yml new file mode 100644 index 0000000000..bc4aa4bd43 --- /dev/null +++ b/Resources/Prototypes/Recipes/Crafting/Graphs/cratesecure.yml @@ -0,0 +1,30 @@ +- type: constructionGraph + id: CrateSecure + start: start + graph: + - node: start + edges: + - to: cratesecure + steps: + - material: Steel + amount: 5 + doAfter: 5 + + + - node: cratesecure + entity: CrateSecure + edges: + - to: start + steps: + - tool: Screwing + doAfter: 5 + conditions: + - !type:StorageWelded + welded: false + - !type:Locked + locked: false + completed: + - !type:SpawnPrototype + prototype: SheetSteel1 + amount: 5 + - !type:DeleteEntity {} \ No newline at end of file diff --git a/Resources/Prototypes/Recipes/Crafting/crates.yml b/Resources/Prototypes/Recipes/Crafting/crates.yml index 51ca15e03c..767ff0e391 100644 --- a/Resources/Prototypes/Recipes/Crafting/crates.yml +++ b/Resources/Prototypes/Recipes/Crafting/crates.yml @@ -7,5 +7,26 @@ category: Storage description: "A wooden crate for holding livestock" icon: Structures/Storage/Crates/livestock.rsi/livestockcrate.png - objectType: Item - \ No newline at end of file + objectType: Structure + +- type: construction + name: steel crate + id: CrateGenericSteel + graph: CrateGenericSteel + startNode: start + targetNode: crategenericsteel + category: Storage + description: "A metal crate for storing things" + icon: Structures/Storage/Crates/generic.rsi/crate_icon.png + objectType: Structure + +- type: construction + name: plastic crate + id: CratePlastic + graph: CratePlastic + startNode: start + targetNode: crateplastic + category: Storage + description: "A plastic crate for storing things" + icon: Structures/Storage/Crates/plastic.rsi/plasticcrate_icon.png + objectType: Structure \ No newline at end of file diff --git a/Resources/Prototypes/Roles/Jobs/Command/captain.yml b/Resources/Prototypes/Roles/Jobs/Command/captain.yml index a16e51a240..3cbe3e05a4 100644 --- a/Resources/Prototypes/Roles/Jobs/Command/captain.yml +++ b/Resources/Prototypes/Roles/Jobs/Command/captain.yml @@ -17,6 +17,10 @@ # Guys please don't fight - Captain - HeadOfPersonnel + - ChiefEngineer + - ChiefMedicalOfficer + - HeadOfSecurity + - ResearchDirector - Command - Security - Armory @@ -24,7 +28,6 @@ - Engineering - Medical - Cargo - # - ResearchDirector - Research - Service - Maintenance @@ -33,7 +36,7 @@ - Theatre - Bar - Chemistry - + - type: startingGear id: CaptainGear equipment: diff --git a/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml b/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml index 11ef8097ac..0823b48999 100644 --- a/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml +++ b/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml @@ -15,6 +15,7 @@ - Engineering - Command - External + - ChiefEngineer - type: startingGear id: ChiefEngineerGear diff --git a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml index 955ea5e5bf..79627ff8aa 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml @@ -17,6 +17,7 @@ - Command - Maintenance - Chemistry + - ChiefMedicalOfficer - type: startingGear id: CMOGear diff --git a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml index 8c1458665a..e6989a9ec1 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/research_director.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/research_director.yml @@ -14,6 +14,7 @@ - Research - Command - Maintenance + - ResearchDirector - type: startingGear id: ResearchDirectorGear diff --git a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml index 21f4d37eb7..402746afb1 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml @@ -11,6 +11,7 @@ supervisors: "the captain" canBeAntag: false access: + - HeadOfSecurity - Command # - Brig - Security diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index b9f6328282..ea594aba85 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -242,7 +242,3 @@ - type: Tag id: Write - -# for head wear & masks that cover the face. -- type: Tag - id: ConcealsFace diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/dead.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/dead.png new file mode 100644 index 0000000000..f9d49d6a6f Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/dead.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/harvest.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/harvest.png new file mode 100644 index 0000000000..701cd8868f Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/harvest.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/inhand-left.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/inhand-left.png new file mode 100644 index 0000000000..d6a98c8cf2 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/inhand-left.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/inhand-right.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/inhand-right.png new file mode 100644 index 0000000000..bd346bdfed Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/inhand-right.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/meta.json b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/meta.json new file mode 100644 index 0000000000..361c777a97 --- /dev/null +++ b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/meta.json @@ -0,0 +1,46 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "dead" + }, + { + "name": "harvest" + }, + { + "name": "produce" + }, + { + "name": "inhand-left", + "directions": 4 + }, + { + "name": "inhand-right", + "directions": 4 + }, + { + "name": "seed" + }, + { + "name": "stage-1" + }, + { + "name": "stage-2" + }, + { + "name": "stage-3" + }, + { + "name": "stage-4" + }, + { + "name": "stage-5" + } + ] +} diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/produce.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/produce.png new file mode 100644 index 0000000000..3b50100cd0 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/produce.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/seed.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/seed.png new file mode 100644 index 0000000000..3c2e747b7c Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/seed.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-1.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-1.png new file mode 100644 index 0000000000..b8306090d6 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-1.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-2.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-2.png new file mode 100644 index 0000000000..55be8ad7c0 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-2.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-3.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-3.png new file mode 100644 index 0000000000..6100f87490 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-3.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-4.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-4.png new file mode 100644 index 0000000000..fb65b55b36 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-4.png differ diff --git a/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-5.png b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-5.png new file mode 100644 index 0000000000..c6e8070331 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/nettle.rsi/stage-5.png differ diff --git a/RobustToolbox b/RobustToolbox index 5f1feb9bb1..2aa5e8c07f 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 5f1feb9bb18a00bb99174d9baa7a11b51c9a5528 +Subproject commit 2aa5e8c07fdfc2bad06d2d0189f6f156665eb263 diff --git a/Tools/gen_build_info.py b/Tools/gen_build_info.py index 6c3878ba55..551605eb53 100755 --- a/Tools/gen_build_info.py +++ b/Tools/gen_build_info.py @@ -19,7 +19,7 @@ SERVER_FILES = [ VERSION = os.environ['GITHUB_SHA'] FORK_ID = "wizards" -BUILD_URL = f"https://central.spacestation14.io/builds/wizards/builds/{VERSION}/{FILE}" +BUILD_URL = f"http://centcomm.spacestation14.io/builds/wizards/builds/{VERSION}/{FILE}" def main() -> None: manifest = generate_manifest("release")