From 9640c33210e914b924acdc9a3d63918bcee3d39e Mon Sep 17 00:00:00 2001 From: Metal Gear Sloth Date: Fri, 5 Feb 2021 18:46:09 +1100 Subject: [PATCH 01/22] Fix strippable crash --- Content.Server/GameObjects/Components/GUI/StrippableComponent.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs b/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs index 6abb62df94..7d0e01bd1c 100644 --- a/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs +++ b/Content.Server/GameObjects/Components/GUI/StrippableComponent.cs @@ -24,6 +24,7 @@ using static Content.Shared.GameObjects.Components.Inventory.EquipmentSlotDefine namespace Content.Server.GameObjects.Components.GUI { [RegisterComponent] + [ComponentReference(typeof(SharedStrippableComponent))] public sealed class StrippableComponent : SharedStrippableComponent { public const float StripDelay = 2f; From b62cc84e8c70901ec1c1eff5229254cc1378d97a Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Fri, 5 Feb 2021 13:41:05 +0100 Subject: [PATCH 02/22] Add destructible component threshold triggers (#3080) * WIP changes * Fix tests, merge conflict and trigger once behavior * Update yml * Change test strings to be consts * Fix total damage types and classes triggers * Simplify damage trigger logic, move state to Threshold * Update outdated code and docs * Change the name of IBehavior back to IThresholdBehavior * Change human gibbing to trigger at 400 brute damage * Change gibbing from brute to blunt damage * Fix one (1) typo * Add damage class trigger test * Add missing nullable enable to thresholds --- .../DestructibleDamageClassTest.cs | 170 +++++++++ .../DestructibleDamageTypeTest.cs | 170 +++++++++ .../DestructibleDestructionTest.cs | 95 +++++ .../DestructibleTestPrototypes.cs | 94 +++++ ...=> DestructibleThresholdActivationTest.cs} | 222 ++---------- .../TestThresholdListenerComponent.cs | 26 ++ .../Destructible/DestructibleComponent.cs | 36 +- .../DestructibleThresholdReachedMessage.cs | 14 +- .../{Behavior => Behaviors}/DoActsBehavior.cs | 8 +- .../{Behavior => Behaviors}/GibBehavior.cs | 4 +- .../IThresholdBehavior.cs | 6 +- .../PlaySoundBehavior.cs | 8 +- .../PlaySoundCollectionBehavior.cs | 8 +- .../SpawnEntitiesBehavior.cs | 6 +- .../{Behavior => Behaviors}/SpillBehavior.cs | 4 +- .../Destructible/Thresholds/MinMax.cs | 4 +- .../Destructible/Thresholds/Threshold.cs | 47 ++- .../Thresholds/Triggers/IThresholdTrigger.cs | 21 ++ .../Triggers/TotalDamageClassesTrigger.cs | 47 +++ .../Thresholds/Triggers/TotalDamageTrigger.cs | 31 ++ .../Triggers/TotalDamageTypesTrigger.cs | 47 +++ .../GameObjects/Components/WindowComponent.cs | 51 ++- .../EntitySystems/DestructibleSystem.cs | 5 +- Content.Shared/Damage/DamageClass.cs | 1 - .../Constructible/Doors/airlock_base.yml | 10 +- .../Entities/Constructible/Furniture/beds.yml | 10 +- .../Constructible/Furniture/bookshelf.yml | 24 +- .../Constructible/Furniture/instruments.yml | 10 +- .../Constructible/Furniture/pilot_chair.yml | 10 +- .../Constructible/Furniture/seats.yml | 10 +- .../Constructible/Furniture/storage.yml | 48 +-- .../Constructible/Furniture/tables.yml | 240 +++++++------ .../Constructible/Ground/gascanisterports.yml | 10 +- .../Constructible/Ground/gascanisters.yml | 10 +- .../Constructible/Ground/gasfilters.yml | 14 +- .../Entities/Constructible/Ground/kitchen.yml | 10 +- .../Entities/Constructible/Ground/pipes.yml | 12 +- .../Entities/Constructible/Ground/pumps.yml | 11 +- .../Constructible/Ground/scrubbers.yml | 12 +- .../Entities/Constructible/Ground/vents.yml | 14 +- .../Constructible/Power/computers.yml | 10 +- .../Constructible/Power/debug_power.yml | 10 +- .../Constructible/Power/power_base.yml | 18 +- .../Constructible/Power/vending_machines.yml | 10 +- .../Entities/Constructible/Power/wires.yml | 80 +++-- .../Specific/Engines/AME/ame_controller.yml | 10 +- .../Specific/Engines/AME/ame_structure.yml | 20 +- .../Specific/Medical/cloning_machine.yml | 10 +- .../Specific/Medical/medical_scanner.yml | 10 +- .../Constructible/Specific/cargo_telepad.yml | 10 +- .../Constructible/Specific/chem_master.yml | 40 ++- .../Constructible/Specific/disposal.yml | 30 +- .../Specific/gravity_generator.yml | 10 +- .../Constructible/Specific/hydroponics.yml | 10 +- .../Constructible/Storage/Closets/closet.yml | 10 +- .../Storage/Crates/crate_base.yml | 10 +- .../Storage/StorageTanks/base_tank.yml | 10 +- .../Entities/Constructible/Walls/asteroid.yml | 10 +- .../Entities/Constructible/Walls/girder.yml | 20 +- .../Entities/Constructible/Walls/lighting.yml | 30 +- .../Entities/Constructible/Walls/low_wall.yml | 10 +- .../Entities/Constructible/Walls/signs.yml | 10 +- .../Entities/Constructible/Walls/walls.yml | 340 ++++++++++-------- .../Entities/Constructible/Walls/windows.yml | 72 ++-- .../Entities/Effects/chemistry_effects.yml | 10 +- .../Entities/Mobs/Species/human.yml | 9 +- .../Entities/Objects/Consumable/drinks.yml | 26 +- .../Objects/Consumable/drinks_bottles.yml | 26 +- .../Entities/Objects/Misc/fluff_lights.yml | 48 +-- .../Entities/Objects/Power/lights.yml | 38 +- .../Weapons/Guns/Explosives/grenades.yml | 40 ++- 71 files changed, 1678 insertions(+), 899 deletions(-) create mode 100644 Content.IntegrationTests/Tests/Destructible/DestructibleDamageClassTest.cs create mode 100644 Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs create mode 100644 Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs create mode 100644 Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs rename Content.IntegrationTests/Tests/Destructible/{DestructibleTests.cs => DestructibleThresholdActivationTest.cs} (60%) create mode 100644 Content.IntegrationTests/Tests/Destructible/TestThresholdListenerComponent.cs rename Content.Server/GameObjects/Components/Destructible/Thresholds/{Behavior => Behaviors}/DoActsBehavior.cs (87%) rename Content.Server/GameObjects/Components/Destructible/Thresholds/{Behavior => Behaviors}/GibBehavior.cs (89%) rename Content.Server/GameObjects/Components/Destructible/Thresholds/{Behavior => Behaviors}/IThresholdBehavior.cs (82%) rename Content.Server/GameObjects/Components/Destructible/Thresholds/{Behavior => Behaviors}/PlaySoundBehavior.cs (82%) rename Content.Server/GameObjects/Components/Destructible/Thresholds/{Behavior => Behaviors}/PlaySoundCollectionBehavior.cs (85%) rename Content.Server/GameObjects/Components/Destructible/Thresholds/{Behavior => Behaviors}/SpawnEntitiesBehavior.cs (93%) rename Content.Server/GameObjects/Components/Destructible/Thresholds/{Behavior => Behaviors}/SpillBehavior.cs (90%) create mode 100644 Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/IThresholdTrigger.cs create mode 100644 Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageClassesTrigger.cs create mode 100644 Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageTrigger.cs create mode 100644 Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageTypesTrigger.cs diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageClassTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageClassTest.cs new file mode 100644 index 0000000000..b7fe431931 --- /dev/null +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageClassTest.cs @@ -0,0 +1,170 @@ +using System.Threading.Tasks; +using Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers; +using Content.Shared.Damage; +using Content.Shared.GameObjects.Components.Damage; +using NUnit.Framework; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes; + +namespace Content.IntegrationTests.Tests.Destructible +{ + [TestFixture] + [TestOf(typeof(TotalDamageClassesTrigger))] + public class DestructibleDamageClassTest : ContentIntegrationTest + { + [Test] + public async Task Test() + { + var server = StartServerDummyTicker(new ServerContentIntegrationOption + { + ExtraPrototypes = Prototypes, + ContentBeforeIoC = () => + { + IoCManager.Resolve().Register(); + } + }); + + await server.WaitIdleAsync(); + + var sEntityManager = server.ResolveDependency(); + var sMapManager = server.ResolveDependency(); + + IEntity sDestructibleEntity; + IDamageableComponent sDamageableComponent = null; + TestThresholdListenerComponent sThresholdListenerComponent = null; + + await server.WaitPost(() => + { + var mapId = new MapId(1); + var coordinates = new MapCoordinates(0, 0, mapId); + sMapManager.CreateMap(mapId); + + sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageClassEntityId, coordinates); + sDamageableComponent = sDestructibleEntity.GetComponent(); + sThresholdListenerComponent = sDestructibleEntity.GetComponent(); + }); + + await server.WaitRunTicks(5); + + await server.WaitAssertion(() => + { + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + }); + + await server.WaitAssertion(() => + { + // Raise brute damage to 5 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 5, true)); + + // No thresholds reached yet, the earliest one is at 10 damage + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise brute damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 5, true)); + + // No threshold reached, burn needs to be 10 as well + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise burn damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true)); + + // One threshold reached, brute 10 + burn 10 + Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + + // Threshold brute 10 + burn 10 + var msg = sThresholdListenerComponent.ThresholdsReached[0]; + var threshold = msg.Threshold; + + // Check that it matches the YAML prototype + Assert.That(threshold.Behaviors, Is.Empty); + Assert.NotNull(threshold.Trigger); + Assert.That(threshold.Triggered, Is.True); + Assert.IsInstanceOf(threshold.Trigger); + + sThresholdListenerComponent.ThresholdsReached.Clear(); + + // Raise brute damage to 20 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true)); + + // No new thresholds reached + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise burn damage to 20 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true)); + + // No new thresholds reached + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Lower brute damage to 0 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, -20, true)); + + // No new thresholds reached, healing should not trigger it + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise brute damage back up to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true)); + + // 10 brute + 10 burn threshold reached, brute was healed and brought back to its threshold amount and slash stayed the same + Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + + sThresholdListenerComponent.ThresholdsReached.Clear(); + + // Heal both classes of damage to 0 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, -10, true)); + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, -20, true)); + + // No new thresholds reached, healing should not trigger it + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise brute damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true)); + + // No new thresholds reached + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise burn damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true)); + + // Both classes of damage were healed and then raised again, the threshold should have been reached as triggers once is default false + Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + + // Threshold brute 10 + burn 10 + msg = sThresholdListenerComponent.ThresholdsReached[0]; + threshold = msg.Threshold; + + // Check that it matches the YAML prototype + Assert.That(threshold.Behaviors, Is.Empty); + Assert.NotNull(threshold.Trigger); + Assert.That(threshold.Triggered, Is.True); + Assert.IsInstanceOf(threshold.Trigger); + + sThresholdListenerComponent.ThresholdsReached.Clear(); + + // Change triggers once to true + threshold.TriggersOnce = true; + + // Heal brute and burn back to 0 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, -10, true)); + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, -10, true)); + + // No new thresholds reached from healing + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise brute damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 10, true)); + + // No new thresholds reached + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise burn damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Burn, 10, true)); + + // No new thresholds reached as triggers once is set to true and it already triggered before + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + }); + } + } +} diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs new file mode 100644 index 0000000000..3ecbacfa30 --- /dev/null +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDamageTypeTest.cs @@ -0,0 +1,170 @@ +using System.Threading.Tasks; +using Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers; +using Content.Shared.Damage; +using Content.Shared.GameObjects.Components.Damage; +using NUnit.Framework; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes; + +namespace Content.IntegrationTests.Tests.Destructible +{ + [TestFixture] + [TestOf(typeof(TotalDamageTypesTrigger))] + public class DestructibleDamageTypeTest : ContentIntegrationTest + { + [Test] + public async Task Test() + { + var server = StartServerDummyTicker(new ServerContentIntegrationOption + { + ExtraPrototypes = Prototypes, + ContentBeforeIoC = () => + { + IoCManager.Resolve().Register(); + } + }); + + await server.WaitIdleAsync(); + + var sEntityManager = server.ResolveDependency(); + var sMapManager = server.ResolveDependency(); + + IEntity sDestructibleEntity; + IDamageableComponent sDamageableComponent = null; + TestThresholdListenerComponent sThresholdListenerComponent = null; + + await server.WaitPost(() => + { + var mapId = new MapId(1); + var coordinates = new MapCoordinates(0, 0, mapId); + sMapManager.CreateMap(mapId); + + sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDamageTypeEntityId, coordinates); + sDamageableComponent = sDestructibleEntity.GetComponent(); + sThresholdListenerComponent = sDestructibleEntity.GetComponent(); + }); + + await server.WaitRunTicks(5); + + await server.WaitAssertion(() => + { + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + }); + + await server.WaitAssertion(() => + { + // Raise blunt damage to 5 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 5, true)); + + // No thresholds reached yet, the earliest one is at 10 damage + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise blunt damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 5, true)); + + // No threshold reached, slash needs to be 10 as well + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise slash damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true)); + + // One threshold reached, blunt 10 + slash 10 + Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + + // Threshold blunt 10 + slash 10 + var msg = sThresholdListenerComponent.ThresholdsReached[0]; + var threshold = msg.Threshold; + + // Check that it matches the YAML prototype + Assert.That(threshold.Behaviors, Is.Empty); + Assert.NotNull(threshold.Trigger); + Assert.That(threshold.Triggered, Is.True); + Assert.IsInstanceOf(threshold.Trigger); + + sThresholdListenerComponent.ThresholdsReached.Clear(); + + // Raise blunt damage to 20 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); + + // No new thresholds reached + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise slash damage to 20 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true)); + + // No new thresholds reached + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Lower blunt damage to 0 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -20, true)); + + // No new thresholds reached, healing should not trigger it + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise blunt damage back up to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); + + // 10 blunt + 10 slash threshold reached, blunt was healed and brought back to its threshold amount and slash stayed the same + Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + + sThresholdListenerComponent.ThresholdsReached.Clear(); + + // Heal both types of damage to 0 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -10, true)); + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, -20, true)); + + // No new thresholds reached, healing should not trigger it + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise blunt damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); + + // No new thresholds reached + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise slash damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true)); + + // Both types of damage were healed and then raised again, the threshold should have been reached as triggers once is default false + Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + + // Threshold blunt 10 + slash 10 + msg = sThresholdListenerComponent.ThresholdsReached[0]; + threshold = msg.Threshold; + + // Check that it matches the YAML prototype + Assert.That(threshold.Behaviors, Is.Empty); + Assert.NotNull(threshold.Trigger); + Assert.That(threshold.Triggered, Is.True); + Assert.IsInstanceOf(threshold.Trigger); + + sThresholdListenerComponent.ThresholdsReached.Clear(); + + // Change triggers once to true + threshold.TriggersOnce = true; + + // Heal blunt and slash back to 0 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, -10, true)); + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, -10, true)); + + // No new thresholds reached from healing + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise blunt damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); + + // No new thresholds reached + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Raise slash damage to 10 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Slash, 10, true)); + + // No new thresholds reached as triggers once is set to true and it already triggered before + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + }); + } + } +} diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs new file mode 100644 index 0000000000..2890b390dc --- /dev/null +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleDestructionTest.cs @@ -0,0 +1,95 @@ +using System.Linq; +using System.Threading.Tasks; +using Content.Server.GameObjects.Components.Destructible.Thresholds; +using Content.Server.GameObjects.Components.Destructible.Thresholds.Behaviors; +using Content.Shared.Damage; +using Content.Shared.GameObjects.Components.Damage; +using NUnit.Framework; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Map; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes; + +namespace Content.IntegrationTests.Tests.Destructible +{ + public class DestructibleDestructionTest : ContentIntegrationTest + { + [Test] + public async Task Test() + { + var server = StartServerDummyTicker(new ServerContentIntegrationOption + { + ExtraPrototypes = Prototypes, + ContentBeforeIoC = () => + { + IoCManager.Resolve().Register(); + } + }); + + await server.WaitIdleAsync(); + + var sEntityManager = server.ResolveDependency(); + var sMapManager = server.ResolveDependency(); + + IEntity sDestructibleEntity = null; + IDamageableComponent sDamageableComponent = null; + TestThresholdListenerComponent sThresholdListenerComponent = null; + + await server.WaitPost(() => + { + var mapId = new MapId(1); + var coordinates = new MapCoordinates(0, 0, mapId); + sMapManager.CreateMap(mapId); + + sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDestructionEntityId, coordinates); + sDamageableComponent = sDestructibleEntity.GetComponent(); + sThresholdListenerComponent = sDestructibleEntity.GetComponent(); + }); + + await server.WaitAssertion(() => + { + var coordinates = sDestructibleEntity.Transform.Coordinates; + + Assert.DoesNotThrow(() => + { + Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 50, true)); + }); + + Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + + var threshold = sThresholdListenerComponent.ThresholdsReached[0].Threshold; + + Assert.That(threshold.Triggered, Is.True); + Assert.That(threshold.Behaviors.Count, Is.EqualTo(3)); + + var spawnEntitiesBehavior = (SpawnEntitiesBehavior) threshold.Behaviors.Single(b => b is SpawnEntitiesBehavior); + + Assert.That(spawnEntitiesBehavior.Spawn.Count, Is.EqualTo(1)); + Assert.That(spawnEntitiesBehavior.Spawn.Keys.Single(), Is.EqualTo(SpawnedEntityId)); + Assert.That(spawnEntitiesBehavior.Spawn.Values.Single(), Is.EqualTo(new MinMax {Min = 1, Max = 1})); + + var entitiesInRange = sEntityManager.GetEntitiesInRange(coordinates, 2); + var found = false; + + foreach (var entity in entitiesInRange) + { + if (entity.Prototype == null) + { + continue; + } + + if (entity.Prototype.Name != SpawnedEntityId) + { + continue; + } + + found = true; + break; + } + + Assert.That(found, Is.True); + }); + } + } +} diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs new file mode 100644 index 0000000000..32fb9048e8 --- /dev/null +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleTestPrototypes.cs @@ -0,0 +1,94 @@ +namespace Content.IntegrationTests.Tests.Destructible +{ + public static class DestructibleTestPrototypes + { + public const string SpawnedEntityId = "DestructibleTestsSpawnedEntity"; + public const string DestructibleEntityId = "DestructibleTestsDestructibleEntity"; + public const string DestructibleDestructionEntityId = "DestructibleTestsDestructibleDestructionEntity"; + public const string DestructibleDamageTypeEntityId = "DestructibleTestsDestructibleDamageTypeEntity"; + public const string DestructibleDamageClassEntityId = "DestructibleTestsDestructibleDamageClassEntity"; + + public static readonly string Prototypes = $@" +- type: entity + id: {SpawnedEntityId} + name: {SpawnedEntityId} + +- type: entity + id: {DestructibleEntityId} + name: {DestructibleEntityId} + components: + - type: Damageable + - type: Destructible + thresholds: + - trigger: + !type:TotalDamageTrigger + damage: 20 + triggersOnce: false + - trigger: + !type:TotalDamageTrigger + damage: 50 + triggersOnce: false + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/woodhit.ogg + - !type:SpawnEntitiesBehavior + spawn: + {SpawnedEntityId}: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [""Breakage""] + - type: TestThresholdListener + +- type: entity + id: {DestructibleDestructionEntityId} + name: {DestructibleDestructionEntityId} + components: + - type: Damageable + - type: Destructible + thresholds: + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/woodhit.ogg + - !type:SpawnEntitiesBehavior + spawn: + {SpawnedEntityId}: + min: 1 + max: 1 + - !type:DoActsBehavior # This must come last as it destroys the entity. + acts: [""Destruction""] + - type: TestThresholdListener + +- type: entity + id: {DestructibleDamageTypeEntityId} + name: {DestructibleDamageTypeEntityId} + components: + - type: Damageable + - type: Destructible + thresholds: + - trigger: + !type:TotalDamageTypesTrigger + damage: + Blunt: 10 + Slash: 10 + - type: TestThresholdListener + + +- type: entity + id: {DestructibleDamageClassEntityId} + name: {DestructibleDamageClassEntityId} + components: + - type: Damageable + - type: Destructible + thresholds: + - trigger: + !type:TotalDamageClassesTrigger + damage: + Brute: 10 + Burn: 10 + - type: TestThresholdListener"; + } +} diff --git a/Content.IntegrationTests/Tests/Destructible/DestructibleTests.cs b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs similarity index 60% rename from Content.IntegrationTests/Tests/Destructible/DestructibleTests.cs rename to Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs index 31c5e1f470..55c1aeadbb 100644 --- a/Content.IntegrationTests/Tests/Destructible/DestructibleTests.cs +++ b/Content.IntegrationTests/Tests/Destructible/DestructibleThresholdActivationTest.cs @@ -1,99 +1,27 @@ -using System.Collections.Generic; -using System.Linq; +using System.Linq; using System.Threading.Tasks; using Content.Server.GameObjects.Components.Destructible; using Content.Server.GameObjects.Components.Destructible.Thresholds; -using Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior; +using Content.Server.GameObjects.Components.Destructible.Thresholds.Behaviors; +using Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers; using Content.Shared.Damage; using Content.Shared.GameObjects.Components.Damage; using NUnit.Framework; -using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Map; using Robust.Shared.IoC; using Robust.Shared.Map; +using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes; namespace Content.IntegrationTests.Tests.Destructible { [TestFixture] [TestOf(typeof(DestructibleComponent))] [TestOf(typeof(Threshold))] - public class DestructibleTests : ContentIntegrationTest + public class DestructibleThresholdActivationTest : ContentIntegrationTest { - private static readonly string SpawnedEntityId = "DestructibleTestsSpawnedEntity"; - private static readonly string DestructibleEntityId = "DestructibleTestsDestructibleEntity"; - private static readonly string DestructibleDestructionEntityId = "DestructibleTestsDestructibleDestructionEntity"; - - private static readonly string Prototypes = $@" -- type: entity - id: {SpawnedEntityId} - name: {SpawnedEntityId} - -- type: entity - id: {DestructibleEntityId} - name: {DestructibleEntityId} - components: - - type: Damageable - - type: Destructible - thresholds: - 20: - triggersOnce: false - 50: - triggersOnce: false - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/woodhit.ogg - - !type:SpawnEntitiesBehavior - spawn: - {SpawnedEntityId}: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [""Breakage""] - - type: TestThresholdListener - -- type: entity - id: {DestructibleDestructionEntityId} - name: {DestructibleDestructionEntityId} - components: - - type: Damageable - - type: Destructible - thresholds: - 50: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/woodhit.ogg - - !type:SpawnEntitiesBehavior - spawn: - {SpawnedEntityId}: - min: 1 - max: 1 - - !type:DoActsBehavior # This must come last as it destroys the entity. - acts: [""Destruction""] - - type: TestThresholdListener -"; - - private class TestThresholdListenerComponent : Component - { - public override string Name => "TestThresholdListener"; - - public List ThresholdsReached { get; } = new(); - - public override void HandleMessage(ComponentMessage message, IComponent component) - { - base.HandleMessage(message, component); - - switch (message) - { - case DestructibleThresholdReachedMessage msg: - ThresholdsReached.Add(msg); - break; - } - } - } - [Test] - public async Task TestThresholdActivation() + public async Task Test() { var server = StartServerDummyTicker(new ServerContentIntegrationOption { @@ -130,7 +58,7 @@ namespace Content.IntegrationTests.Tests.Destructible await server.WaitAssertion(() => { - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.Zero); + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); }); await server.WaitAssertion(() => @@ -138,36 +66,31 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); // No thresholds reached yet, the earliest one is at 20 damage - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.Zero); + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 10, true)); // Only one threshold reached, 20 Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + // Threshold 20 var msg = sThresholdListenerComponent.ThresholdsReached[0]; - - // Check that it matches the total damage dealt - Assert.That(msg.TotalDamage, Is.EqualTo(20)); - var threshold = msg.Threshold; // Check that it matches the YAML prototype Assert.That(threshold.Behaviors, Is.Empty); + Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); sThresholdListenerComponent.ThresholdsReached.Clear(); Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 30, true)); - // Only one threshold reached, 50, since 20 was already reached before + // One threshold reached, 50, since 20 already triggered before and it has not been healed below that amount Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); + // Threshold 50 msg = sThresholdListenerComponent.ThresholdsReached[0]; - - // Check that it matches the total damage dealt - Assert.That(msg.TotalDamage, Is.EqualTo(50)); - threshold = msg.Threshold; // Check that it matches the YAML prototype @@ -184,6 +107,7 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(spawnThreshold.Spawn.Single().Key, Is.EqualTo(SpawnedEntityId)); Assert.That(spawnThreshold.Spawn.Single().Value.Min, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Value.Max, Is.EqualTo(1)); + Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); sThresholdListenerComponent.ThresholdsReached.Clear(); @@ -191,8 +115,19 @@ namespace Content.IntegrationTests.Tests.Destructible // Damage for 50 again, up to 100 now Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 50, true)); - // No new thresholds reached as even though they don't only trigger once, the entity was not healed below the threshold - Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); + // No thresholds reached as they weren't healed below the trigger amount + Assert.IsEmpty(sThresholdListenerComponent.ThresholdsReached); + + // Heal down to 0 + sDamageableComponent.Heal(); + + // Damage for 100, up to 100 + Assert.True(sDamageableComponent.ChangeDamage(DamageType.Blunt, 100, true)); + + // Two thresholds reached as damage increased past the previous, 20 and 50 + Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(2)); + + sThresholdListenerComponent.ThresholdsReached.Clear(); // Heal the entity for 40 damage, down to 60 sDamageableComponent.ChangeDamage(DamageType.Blunt, -40, true); @@ -219,10 +154,6 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); msg = sThresholdListenerComponent.ThresholdsReached[0]; - - // Check that it matches the total damage dealt - Assert.That(msg.TotalDamage, Is.EqualTo(50)); - threshold = msg.Threshold; // Check that it matches the YAML prototype @@ -240,6 +171,7 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(spawnThreshold.Spawn.Single().Key, Is.EqualTo(SpawnedEntityId)); Assert.That(spawnThreshold.Spawn.Single().Value.Min, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Value.Max, Is.EqualTo(1)); + Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); // Reset thresholds reached @@ -259,10 +191,9 @@ namespace Content.IntegrationTests.Tests.Destructible // Verify the first one, should be the lowest one (20) msg = sThresholdListenerComponent.ThresholdsReached[0]; - Assert.That(msg.ThresholdAmount, Is.EqualTo(20)); - - // The total damage should be 50 - Assert.That(msg.TotalDamage, Is.EqualTo(50)); + var trigger = (TotalDamageTrigger) msg.Threshold.Trigger; + Assert.NotNull(trigger); + Assert.That(trigger.Damage, Is.EqualTo(20)); threshold = msg.Threshold; @@ -271,10 +202,9 @@ namespace Content.IntegrationTests.Tests.Destructible // Verify the second one, should be the highest one (50) msg = sThresholdListenerComponent.ThresholdsReached[1]; - Assert.That(msg.ThresholdAmount, Is.EqualTo(50)); - - // Check that it matches the total damage dealt - Assert.That(msg.TotalDamage, Is.EqualTo(50)); + trigger = (TotalDamageTrigger) msg.Threshold.Trigger; + Assert.NotNull(trigger); + Assert.That(trigger.Damage, Is.EqualTo(50)); threshold = msg.Threshold; @@ -292,6 +222,7 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(spawnThreshold.Spawn.Single().Key, Is.EqualTo(SpawnedEntityId)); Assert.That(spawnThreshold.Spawn.Single().Value.Min, Is.EqualTo(1)); Assert.That(spawnThreshold.Spawn.Single().Value.Max, Is.EqualTo(1)); + Assert.NotNull(threshold.Trigger); Assert.That(threshold.Triggered, Is.True); // Reset thresholds reached @@ -304,8 +235,9 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(0)); // Set both thresholds to only trigger once - foreach (var destructibleThreshold in sDestructibleComponent.LowestToHighestThresholds.Values) + foreach (var destructibleThreshold in sDestructibleComponent.Thresholds) { + Assert.NotNull(destructibleThreshold.Trigger); destructibleThreshold.TriggersOnce = true; } @@ -319,8 +251,9 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); // Set both thresholds to trigger multiple times - foreach (var destructibleThreshold in sDestructibleComponent.LowestToHighestThresholds.Values) + foreach (var destructibleThreshold in sDestructibleComponent.Thresholds) { + Assert.NotNull(destructibleThreshold.Trigger); destructibleThreshold.TriggersOnce = false; } @@ -331,84 +264,5 @@ namespace Content.IntegrationTests.Tests.Destructible Assert.That(sThresholdListenerComponent.ThresholdsReached, Is.Empty); }); } - - [Test] - public async Task DestructibleDestructionTest() - { - var server = StartServerDummyTicker(new ServerContentIntegrationOption - { - ExtraPrototypes = Prototypes, - ContentBeforeIoC = () => - { - IoCManager.Resolve().Register(); - } - }); - - await server.WaitIdleAsync(); - - var sEntityManager = server.ResolveDependency(); - var sMapManager = server.ResolveDependency(); - - IEntity sDestructibleEntity = null; - IDamageableComponent sDamageableComponent = null; - DestructibleComponent sDestructibleComponent = null; - TestThresholdListenerComponent sThresholdListenerComponent = null; - - await server.WaitPost(() => - { - var mapId = new MapId(1); - var coordinates = new MapCoordinates(0, 0, mapId); - sMapManager.CreateMap(mapId); - - sDestructibleEntity = sEntityManager.SpawnEntity(DestructibleDestructionEntityId, coordinates); - sDamageableComponent = sDestructibleEntity.GetComponent(); - sDestructibleComponent = sDestructibleEntity.GetComponent(); - sThresholdListenerComponent = sDestructibleEntity.GetComponent(); - }); - - await server.WaitAssertion(() => - { - var coordinates = sDestructibleEntity.Transform.Coordinates; - - Assert.DoesNotThrow(() => - { - Assert.True(sDamageableComponent.ChangeDamage(DamageClass.Brute, 50, true)); - }); - - Assert.That(sThresholdListenerComponent.ThresholdsReached.Count, Is.EqualTo(1)); - - var threshold = sThresholdListenerComponent.ThresholdsReached[0].Threshold; - - Assert.That(threshold.Triggered, Is.True); - Assert.That(threshold.Behaviors.Count, Is.EqualTo(3)); - - var spawnEntitiesBehavior = (SpawnEntitiesBehavior) threshold.Behaviors.Single(b => b is SpawnEntitiesBehavior); - - Assert.That(spawnEntitiesBehavior.Spawn.Count, Is.EqualTo(1)); - Assert.That(spawnEntitiesBehavior.Spawn.Keys.Single(), Is.EqualTo(SpawnedEntityId)); - Assert.That(spawnEntitiesBehavior.Spawn.Values.Single(), Is.EqualTo(new MinMax {Min = 1, Max = 1})); - - var entitiesInRange = sEntityManager.GetEntitiesInRange(coordinates, 2); - var found = false; - - foreach (var entity in entitiesInRange) - { - if (entity.Prototype == null) - { - continue; - } - - if (entity.Prototype.Name != SpawnedEntityId) - { - continue; - } - - found = true; - break; - } - - Assert.That(found, Is.True); - }); - } } } diff --git a/Content.IntegrationTests/Tests/Destructible/TestThresholdListenerComponent.cs b/Content.IntegrationTests/Tests/Destructible/TestThresholdListenerComponent.cs new file mode 100644 index 0000000000..f0d6258355 --- /dev/null +++ b/Content.IntegrationTests/Tests/Destructible/TestThresholdListenerComponent.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using Content.Server.GameObjects.Components.Destructible; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; + +namespace Content.IntegrationTests.Tests.Destructible +{ + public class TestThresholdListenerComponent : Component + { + public override string Name => "TestThresholdListener"; + + public List ThresholdsReached { get; } = new(); + + public override void HandleMessage(ComponentMessage message, IComponent component) + { + base.HandleMessage(message, component); + + switch (message) + { + case DestructibleThresholdReachedMessage msg: + ThresholdsReached.Add(msg); + break; + } + } + } +} diff --git a/Content.Server/GameObjects/Components/Destructible/DestructibleComponent.cs b/Content.Server/GameObjects/Components/Destructible/DestructibleComponent.cs index bde01c3bbe..a63ec55721 100644 --- a/Content.Server/GameObjects/Components/Destructible/DestructibleComponent.cs +++ b/Content.Server/GameObjects/Components/Destructible/DestructibleComponent.cs @@ -22,22 +22,15 @@ namespace Content.Server.GameObjects.Components.Destructible public override string Name => "Destructible"; - [ViewVariables] - private SortedDictionary _lowestToHighestThresholds = new(); + [ViewVariables] private List _thresholds = new(); - [ViewVariables] private int PreviousTotalDamage { get; set; } - - public IReadOnlyDictionary LowestToHighestThresholds => _lowestToHighestThresholds; + public IReadOnlyList Thresholds => _thresholds; public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); - serializer.DataReadWriteFunction( - "thresholds", - new Dictionary(), - thresholds => _lowestToHighestThresholds = new SortedDictionary(thresholds), - () => new Dictionary(_lowestToHighestThresholds)); + serializer.DataField(ref _thresholds, "thresholds", new List()); } public override void Initialize() @@ -60,32 +53,17 @@ namespace Content.Server.GameObjects.Components.Destructible break; } - foreach (var (damage, threshold) in _lowestToHighestThresholds) + foreach (var threshold in _thresholds) { - if (threshold.Triggered) + if (threshold.Reached(msg.Damageable, _destructibleSystem)) { - if (threshold.TriggersOnce) - { - continue; - } - - if (PreviousTotalDamage >= damage) - { - continue; - } - } - - if (msg.Damageable.TotalDamage >= damage) - { - var thresholdMessage = new DestructibleThresholdReachedMessage(this, threshold, msg.Damageable.TotalDamage, damage); + var thresholdMessage = new DestructibleThresholdReachedMessage(this, threshold); SendMessage(thresholdMessage); - threshold.Trigger(Owner, _destructibleSystem); + threshold.Execute(Owner, _destructibleSystem); } } - PreviousTotalDamage = msg.Damageable.TotalDamage; - break; } } diff --git a/Content.Server/GameObjects/Components/Destructible/DestructibleThresholdReachedMessage.cs b/Content.Server/GameObjects/Components/Destructible/DestructibleThresholdReachedMessage.cs index 9321e95a00..6bbad73fbc 100644 --- a/Content.Server/GameObjects/Components/Destructible/DestructibleThresholdReachedMessage.cs +++ b/Content.Server/GameObjects/Components/Destructible/DestructibleThresholdReachedMessage.cs @@ -5,26 +5,14 @@ namespace Content.Server.GameObjects.Components.Destructible { public class DestructibleThresholdReachedMessage : ComponentMessage { - public DestructibleThresholdReachedMessage(DestructibleComponent parent, Threshold threshold, int totalDamage, int thresholdAmount) + public DestructibleThresholdReachedMessage(DestructibleComponent parent, Threshold threshold) { Parent = parent; Threshold = threshold; - TotalDamage = totalDamage; - ThresholdAmount = thresholdAmount; } public DestructibleComponent Parent { get; } public Threshold Threshold { get; } - - /// - /// The amount of total damage currently had that triggered this threshold. - /// - public int TotalDamage { get; } - - /// - /// The amount of damage at which this threshold triggers. - /// - public int ThresholdAmount { get; } } } diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/DoActsBehavior.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/DoActsBehavior.cs similarity index 87% rename from Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/DoActsBehavior.cs rename to Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/DoActsBehavior.cs index e4dd894598..a62bbdfd16 100644 --- a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/DoActsBehavior.cs +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/DoActsBehavior.cs @@ -1,10 +1,12 @@ -using Content.Server.GameObjects.EntitySystems; +using System; +using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; -namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behaviors { + [Serializable] public class DoActsBehavior : IThresholdBehavior { private int _acts; @@ -29,7 +31,7 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior return (_acts & (int) act) != 0; } - public void Trigger(IEntity owner, DestructibleSystem system) + public void Execute(IEntity owner, DestructibleSystem system) { if (HasAct(ThresholdActs.Breakage)) { diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/GibBehavior.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/GibBehavior.cs similarity index 89% rename from Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/GibBehavior.cs rename to Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/GibBehavior.cs index c388666c24..729e421fac 100644 --- a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/GibBehavior.cs +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/GibBehavior.cs @@ -4,7 +4,7 @@ using JetBrains.Annotations; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; -namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behaviors { [UsedImplicitly] public class GibBehavior : IThresholdBehavior @@ -16,7 +16,7 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior serializer.DataField(ref _recursive, "recursive", true); } - public void Trigger(IEntity owner, DestructibleSystem system) + public void Execute(IEntity owner, DestructibleSystem system) { if (owner.TryGetComponent(out IBody body)) { diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/IThresholdBehavior.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/IThresholdBehavior.cs similarity index 82% rename from Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/IThresholdBehavior.cs rename to Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/IThresholdBehavior.cs index d4a6e86e56..20e06e3687 100644 --- a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/IThresholdBehavior.cs +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/IThresholdBehavior.cs @@ -2,18 +2,18 @@ using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; -namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behaviors { public interface IThresholdBehavior : IExposeData { /// - /// Triggers this behavior. + /// Executes this behavior. /// /// The entity that owns this behavior. /// /// An instance of to pull dependencies /// and other systems from. /// - void Trigger(IEntity owner, DestructibleSystem system); + void Execute(IEntity owner, DestructibleSystem system); } } diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/PlaySoundBehavior.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/PlaySoundBehavior.cs similarity index 82% rename from Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/PlaySoundBehavior.cs rename to Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/PlaySoundBehavior.cs index 0ea803f937..38a8af6df7 100644 --- a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/PlaySoundBehavior.cs +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/PlaySoundBehavior.cs @@ -1,10 +1,12 @@ -using Content.Server.GameObjects.EntitySystems; +using System; +using Content.Server.GameObjects.EntitySystems; using Content.Shared.Audio; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; -namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behaviors { + [Serializable] public class PlaySoundBehavior : IThresholdBehavior { /// @@ -17,7 +19,7 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior serializer.DataField(this, x => x.Sound, "sound", string.Empty); } - public void Trigger(IEntity owner, DestructibleSystem system) + public void Execute(IEntity owner, DestructibleSystem system) { if (string.IsNullOrEmpty(Sound)) { diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/PlaySoundCollectionBehavior.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/PlaySoundCollectionBehavior.cs similarity index 85% rename from Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/PlaySoundCollectionBehavior.cs rename to Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/PlaySoundCollectionBehavior.cs index 5a02635b4d..b9caf7a3a6 100644 --- a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/PlaySoundCollectionBehavior.cs +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/PlaySoundCollectionBehavior.cs @@ -1,10 +1,12 @@ -using Content.Server.GameObjects.EntitySystems; +using System; +using Content.Server.GameObjects.EntitySystems; using Content.Shared.Audio; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; -namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behaviors { + [Serializable] public class PlaySoundCollectionBehavior : IThresholdBehavior { /// @@ -17,7 +19,7 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior serializer.DataField(this, x => x.SoundCollection, "soundCollection", string.Empty); } - public void Trigger(IEntity owner, DestructibleSystem system) + public void Execute(IEntity owner, DestructibleSystem system) { if (string.IsNullOrEmpty(SoundCollection)) { diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/SpawnEntitiesBehavior.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs similarity index 93% rename from Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/SpawnEntitiesBehavior.cs rename to Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs index 9864db5175..f3f4f6c04d 100644 --- a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/SpawnEntitiesBehavior.cs +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/SpawnEntitiesBehavior.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using System.Collections.Generic; using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.EntitySystems; @@ -6,8 +7,9 @@ using Content.Shared.Utility; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; -namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behaviors { + [Serializable] public class SpawnEntitiesBehavior : IThresholdBehavior { /// @@ -20,7 +22,7 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior serializer.DataField(this, x => x.Spawn, "spawn", new Dictionary()); } - public void Trigger(IEntity owner, DestructibleSystem system) + public void Execute(IEntity owner, DestructibleSystem system) { foreach (var (entityId, minMax) in Spawn) { diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/SpillBehavior.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/SpillBehavior.cs similarity index 90% rename from Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/SpillBehavior.cs rename to Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/SpillBehavior.cs index 53ac16d632..02a40b1af0 100644 --- a/Content.Server/GameObjects/Components/Destructible/Thresholds/Behavior/SpillBehavior.cs +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Behaviors/SpillBehavior.cs @@ -6,14 +6,14 @@ using JetBrains.Annotations; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; -namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behaviors { [UsedImplicitly] public class SpillBehavior : IThresholdBehavior { public void ExposeData(ObjectSerializer serializer) { } - public void Trigger(IEntity owner, DestructibleSystem system) + public void Execute(IEntity owner, DestructibleSystem system) { if (!owner.TryGetComponent(out SolutionContainerComponent? solutionContainer)) return; diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/MinMax.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/MinMax.cs index c7ef3c7fd0..ab2c576508 100644 --- a/Content.Server/GameObjects/Components/Destructible/Thresholds/MinMax.cs +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/MinMax.cs @@ -1,9 +1,11 @@ -using Robust.Shared.Interfaces.Serialization; +using System; +using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Destructible.Thresholds { + [Serializable] public struct MinMax : IExposeData { [ViewVariables] diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Threshold.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Threshold.cs index e0e402f5df..7e2865c74b 100644 --- a/Content.Server/GameObjects/Components/Destructible/Thresholds/Threshold.cs +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Threshold.cs @@ -1,7 +1,9 @@ #nullable enable using System.Collections.Generic; -using Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior; +using Content.Server.GameObjects.Components.Destructible.Thresholds.Behaviors; +using Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers; using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Components.Damage; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Interfaces.Serialization; using Robust.Shared.Serialization; @@ -13,6 +15,12 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds { private List _behaviors = new(); + /// + /// Whether or not this threshold was triggered in the previous call to + /// . + /// + [ViewVariables] public bool OldTriggered { get; private set; } + /// /// Whether or not this threshold has already been triggered. /// @@ -26,6 +34,11 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds /// [ViewVariables] public bool TriggersOnce { get; set; } + /// + /// The trigger that decides if this threshold has been reached. + /// + [ViewVariables] public IThresholdTrigger? Trigger { get; set; } + /// /// Behaviors to activate once this threshold is triggered. /// @@ -35,9 +48,37 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds { serializer.DataField(this, x => x.Triggered, "triggered", false); serializer.DataField(this, x => x.TriggersOnce, "triggersOnce", false); + serializer.DataField(this, x => x.Trigger, "trigger", null); serializer.DataField(ref _behaviors, "behaviors", new List()); } + public bool Reached(IDamageableComponent damageable, DestructibleSystem system) + { + if (Trigger == null) + { + return false; + } + + if (Triggered && TriggersOnce) + { + return false; + } + + if (OldTriggered) + { + OldTriggered = Trigger.Reached(damageable, system); + return false; + } + + if (!Trigger.Reached(damageable, system)) + { + return false; + } + + OldTriggered = true; + return true; + } + /// /// Triggers this threshold. /// @@ -46,7 +87,7 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds /// An instance of to get dependency and /// system references from, if relevant. /// - public void Trigger(IEntity owner, DestructibleSystem system) + public void Execute(IEntity owner, DestructibleSystem system) { Triggered = true; @@ -56,7 +97,7 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds if (owner.Deleted) return; - behavior.Trigger(owner, system); + behavior.Execute(owner, system); } } } diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/IThresholdTrigger.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/IThresholdTrigger.cs new file mode 100644 index 0000000000..88742fdd4e --- /dev/null +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/IThresholdTrigger.cs @@ -0,0 +1,21 @@ +#nullable enable +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Components.Damage; +using Robust.Shared.Interfaces.Serialization; + +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers +{ + public interface IThresholdTrigger : IExposeData + { + /// + /// Checks if this trigger has been reached. + /// + /// The damageable component to check with. + /// + /// An instance of to pull + /// dependencies from, if any. + /// + /// true if this trigger has been reached, false otherwise. + bool Reached(IDamageableComponent damageable, DestructibleSystem system); + } +} diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageClassesTrigger.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageClassesTrigger.cs new file mode 100644 index 0000000000..c7c469c6fb --- /dev/null +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageClassesTrigger.cs @@ -0,0 +1,47 @@ +#nullable enable +using System; +using System.Collections.Generic; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.Damage; +using Content.Shared.GameObjects.Components.Damage; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers +{ + /// + /// A trigger that will activate when all of the damage classes received + /// are above the specified threshold. + /// + [Serializable] + public class TotalDamageClassesTrigger : IThresholdTrigger + { + /// + /// The amount of damage at which this threshold will trigger. + /// The damage requirements of all must be met. + /// + private Dictionary Damage { get; set; } = new(); + + public void ExposeData(ObjectSerializer serializer) + { + serializer.DataField(this, x => x.Damage, "damage", new Dictionary()); + } + + public bool Reached(IDamageableComponent damageable, DestructibleSystem system) + { + foreach (var (type, damageRequired) in Damage) + { + if (!damageable.TryGetDamage(type, out var damageReceived)) + { + return false; + } + + if (damageReceived < damageRequired) + { + return false; + } + } + + return true; + } + } +} diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageTrigger.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageTrigger.cs new file mode 100644 index 0000000000..978b423363 --- /dev/null +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageTrigger.cs @@ -0,0 +1,31 @@ +#nullable enable +using System; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.GameObjects.Components.Damage; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers +{ + /// + /// A trigger that will activate when the amount of total damage received + /// is above the specified threshold. + /// + [Serializable] + public class TotalDamageTrigger : IThresholdTrigger + { + /// + /// The amount of total damage at which this threshold will trigger. + /// + public int Damage { get; private set; } + + public void ExposeData(ObjectSerializer serializer) + { + serializer.DataField(this, x => x.Damage, "damage", 0); + } + + public bool Reached(IDamageableComponent damageable, DestructibleSystem system) + { + return damageable.TotalDamage >= Damage; + } + } +} diff --git a/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageTypesTrigger.cs b/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageTypesTrigger.cs new file mode 100644 index 0000000000..fa48dfc0cc --- /dev/null +++ b/Content.Server/GameObjects/Components/Destructible/Thresholds/Triggers/TotalDamageTypesTrigger.cs @@ -0,0 +1,47 @@ +#nullable enable +using System; +using System.Collections.Generic; +using Content.Server.GameObjects.EntitySystems; +using Content.Shared.Damage; +using Content.Shared.GameObjects.Components.Damage; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers +{ + /// + /// A trigger that will activate when all of the damage types received + /// are above the specified threshold. + /// + [Serializable] + public class TotalDamageTypesTrigger : IThresholdTrigger + { + /// + /// The amount of damage at which this threshold will trigger. + /// The damage requirements of all must be met. + /// + private Dictionary Damage { get; set; } = new(); + + public void ExposeData(ObjectSerializer serializer) + { + serializer.DataField(this, x => x.Damage, "damage", new Dictionary()); + } + + public bool Reached(IDamageableComponent damageable, DestructibleSystem system) + { + foreach (var (type, damageRequired) in Damage) + { + if (!damageable.TryGetDamage(type, out var damageReceived)) + { + return false; + } + + if (damageReceived < damageRequired) + { + return false; + } + } + + return true; + } + } +} diff --git a/Content.Server/GameObjects/Components/WindowComponent.cs b/Content.Server/GameObjects/Components/WindowComponent.cs index 669971fb6d..da399aae44 100644 --- a/Content.Server/GameObjects/Components/WindowComponent.cs +++ b/Content.Server/GameObjects/Components/WindowComponent.cs @@ -1,5 +1,6 @@ -#nullable enable +#nullable enable using System; +using System.Linq; using Content.Server.Utility; using Content.Shared.Audio; using Content.Shared.GameObjects.Components; @@ -7,6 +8,7 @@ using Content.Shared.GameObjects.Components.Damage; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces.GameObjects.Components; using Content.Server.GameObjects.Components.Destructible; +using Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers; using Content.Shared.Utility; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; @@ -22,7 +24,6 @@ namespace Content.Server.GameObjects.Components [ComponentReference(typeof(SharedWindowComponent))] public class WindowComponent : SharedWindowComponent, IExamine, IInteractHand { - public override void HandleMessage(ComponentMessage message, IComponent? component) { base.HandleMessage(message, component); @@ -40,26 +41,52 @@ namespace Content.Server.GameObjects.Components private void UpdateVisuals(int currentDamage) { - if (Owner.TryGetComponent(out AppearanceComponent? appearance)) + if (Owner.TryGetComponent(out AppearanceComponent? appearance) && + Owner.TryGetComponent(out DestructibleComponent? destructible)) { - if (Owner.TryGetComponent(out DestructibleComponent? destructible)) + foreach (var threshold in destructible.Thresholds) { - var damageThreshold = destructible.LowestToHighestThresholds.FirstOrNull()?.Key; - if (damageThreshold == null) return; - appearance.SetData(WindowVisuals.Damage, (float) currentDamage / damageThreshold); + if (threshold.Trigger is not TotalDamageTrigger trigger) + { + continue; + } + + appearance.SetData(WindowVisuals.Damage, (float) currentDamage / trigger.Damage); } } } void IExamine.Examine(FormattedMessage message, bool inDetailsRange) { - var damage = Owner.GetComponentOrNull()?.TotalDamage; - var damageThreshold = Owner.GetComponentOrNull()?.LowestToHighestThresholds.FirstOrNull()?.Key; - if (damage == null || damageThreshold == null) return; - var fraction = ((damage == 0 || damageThreshold == 0) + if (!Owner.TryGetComponent(out IDamageableComponent? damageable) || + !Owner.TryGetComponent(out DestructibleComponent? destructible)) + { + return; + } + + var damage = damageable.TotalDamage; + TotalDamageTrigger? trigger = null; + + // TODO: Pretend this does not exist until https://github.com/space-wizards/space-station-14/pull/2783 is merged + foreach (var threshold in destructible.Thresholds) + { + if ((trigger = threshold.Trigger as TotalDamageTrigger) != null) + { + break; + } + } + + if (trigger == null) + { + return; + } + + var damageThreshold = trigger.Damage; + var fraction = damage == 0 || damageThreshold == 0 ? 0f - : (float) damage / damageThreshold); + : (float) damage / damageThreshold; var level = Math.Min(ContentHelpers.RoundToLevels((double) fraction, 1, 7), 5); + switch (level) { case 0: diff --git a/Content.Server/GameObjects/EntitySystems/DestructibleSystem.cs b/Content.Server/GameObjects/EntitySystems/DestructibleSystem.cs index 412beefddf..e9449cde08 100644 --- a/Content.Server/GameObjects/EntitySystems/DestructibleSystem.cs +++ b/Content.Server/GameObjects/EntitySystems/DestructibleSystem.cs @@ -11,8 +11,9 @@ namespace Content.Server.GameObjects.EntitySystems public class DestructibleSystem : EntitySystem { [Dependency] public readonly IRobustRandom Random = default!; - + public AudioSystem AudioSystem { get; private set; } + public ActSystem ActSystem { get; private set; } public override void Initialize() @@ -23,4 +24,4 @@ namespace Content.Server.GameObjects.EntitySystems ActSystem = Get(); } } -} \ No newline at end of file +} diff --git a/Content.Shared/Damage/DamageClass.cs b/Content.Shared/Damage/DamageClass.cs index 0801728f39..3463b1c430 100644 --- a/Content.Shared/Damage/DamageClass.cs +++ b/Content.Shared/Damage/DamageClass.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Content.Shared.GameObjects.EntitySystems; -using Robust.Shared.GameObjects.Systems; using Robust.Shared.Serialization; namespace Content.Shared.Damage diff --git a/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml b/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml index 4358793df8..e35a2639b5 100644 --- a/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml +++ b/Resources/Prototypes/Entities/Constructible/Doors/airlock_base.yml @@ -60,10 +60,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 500: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 500 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: IconSmooth key: walls mode: NoSprite diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/beds.yml b/Resources/Prototypes/Entities/Constructible/Furniture/beds.yml index 33ed616d29..1c778b4ff5 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/beds.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/beds.yml @@ -25,9 +25,11 @@ resistances: metallicResistances - type: Destructible thresholds: - 75: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 75 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] placement: mode: SnapgridCenter diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/bookshelf.yml b/Resources/Prototypes/Entities/Constructible/Furniture/bookshelf.yml index e151c5db20..670f0d1972 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/bookshelf.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/bookshelf.yml @@ -23,17 +23,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 30: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/woodhit.ogg - - !type:SpawnEntitiesBehavior - spawn: - WoodPlank: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 30 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/woodhit.ogg + - !type:SpawnEntitiesBehavior + spawn: + WoodPlank: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: ["Destruction"] - type: Occluder sizeX: 32 diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/instruments.yml b/Resources/Prototypes/Entities/Constructible/Furniture/instruments.yml index aa61941654..0efff560fe 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/instruments.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/instruments.yml @@ -24,10 +24,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 50: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: UserInterface interfaces: - key: enum.InstrumentUiKey.Key diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/pilot_chair.yml b/Resources/Prototypes/Entities/Constructible/Furniture/pilot_chair.yml index d275ab8f66..d952e787cf 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/pilot_chair.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/pilot_chair.yml @@ -15,10 +15,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: ShuttleController - type: Strap position: Stand diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/seats.yml b/Resources/Prototypes/Entities/Constructible/Furniture/seats.yml index fae372caf0..893aa5de52 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/seats.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/seats.yml @@ -34,10 +34,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 50: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: entity name: chair diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/storage.yml b/Resources/Prototypes/Entities/Constructible/Furniture/storage.yml index 6d960128fa..9ef4248c6b 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/storage.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/storage.yml @@ -31,17 +31,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 30: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/metalbreak.ogg - - !type:SpawnEntitiesBehavior - spawn: - SteelSheet1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 30 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:SpawnEntitiesBehavior + spawn: + SteelSheet1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity id: Shelf @@ -76,14 +78,16 @@ resistances: metallicResistances - type: Destructible thresholds: - 30: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/metalbreak.ogg - - !type:SpawnEntitiesBehavior - spawn: - SteelSheet1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 30 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:SpawnEntitiesBehavior + spawn: + SteelSheet1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] diff --git a/Resources/Prototypes/Entities/Constructible/Furniture/tables.yml b/Resources/Prototypes/Entities/Constructible/Furniture/tables.yml index 1b279f4080..661981e61d 100644 --- a/Resources/Prototypes/Entities/Constructible/Furniture/tables.yml +++ b/Resources/Prototypes/Entities/Constructible/Furniture/tables.yml @@ -39,17 +39,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 15: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/metalbreak.ogg - - !type:SpawnEntitiesBehavior - spawn: - SteelSheet1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 15 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:SpawnEntitiesBehavior + spawn: + SteelSheet1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity id: TableFrame @@ -65,17 +67,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 1: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/metalbreak.ogg - - !type:SpawnEntitiesBehavior - spawn: - SteelSheet1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 1 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:SpawnEntitiesBehavior + spawn: + SteelSheet1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Construction graph: Tables node: TableFrame @@ -94,17 +98,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 1: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/metalbreak.ogg - - !type:SpawnEntitiesBehavior - spawn: - SteelSheet1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 1 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:SpawnEntitiesBehavior + spawn: + SteelSheet1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity id: TableMetal @@ -120,17 +126,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 15: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/metalbreak.ogg - - !type:SpawnEntitiesBehavior - spawn: - SteelSheet1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 15 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:SpawnEntitiesBehavior + spawn: + SteelSheet1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Construction graph: Tables node: MetalTable @@ -149,17 +157,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 75: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/metalbreak.ogg - - !type:SpawnEntitiesBehavior - spawn: - SteelSheet1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 75 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:SpawnEntitiesBehavior + spawn: + SteelSheet1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Construction graph: Tables node: ReinforcedTable @@ -178,17 +188,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 5: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/glass_break2.ogg - - !type:SpawnEntitiesBehavior - spawn: - ShardGlass: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 5 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/glass_break2.ogg + - !type:SpawnEntitiesBehavior + spawn: + ShardGlass: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Construction graph: Tables node: GlassTable @@ -207,17 +219,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 20: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/glass_break2.ogg - - !type:SpawnEntitiesBehavior - spawn: - ShardGlass: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 20 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/glass_break2.ogg + - !type:SpawnEntitiesBehavior + spawn: + ShardGlass: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Construction graph: Tables node: RGlassTable @@ -236,17 +250,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 15: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/woodhit.ogg - - !type:SpawnEntitiesBehavior - spawn: - WoodPlank: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 15 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/woodhit.ogg + - !type:SpawnEntitiesBehavior + spawn: + WoodPlank: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Construction graph: Tables node: WoodTable @@ -265,17 +281,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 15: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/woodhit.ogg - - !type:SpawnEntitiesBehavior - spawn: - WoodPlank: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 15 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/woodhit.ogg + - !type:SpawnEntitiesBehavior + spawn: + WoodPlank: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Construction graph: Tables node: PokerTable @@ -294,12 +312,14 @@ resistances: metallicResistances - type: Destructible thresholds: - 50: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/picaxe2.ogg - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/picaxe2.ogg + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity id: TableDebug @@ -315,7 +335,9 @@ resistances: metallicResistances - type: Destructible thresholds: - 1: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 1 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] diff --git a/Resources/Prototypes/Entities/Constructible/Ground/gascanisterports.yml b/Resources/Prototypes/Entities/Constructible/Ground/gascanisterports.yml index 52fbfd854c..a5f42544a1 100644 --- a/Resources/Prototypes/Entities/Constructible/Ground/gascanisterports.yml +++ b/Resources/Prototypes/Entities/Constructible/Ground/gascanisterports.yml @@ -19,10 +19,12 @@ - type: Damageable - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: GasCanisterPort - type: entity diff --git a/Resources/Prototypes/Entities/Constructible/Ground/gascanisters.yml b/Resources/Prototypes/Entities/Constructible/Ground/gascanisters.yml index 4c8e4247a7..c013fe4a85 100644 --- a/Resources/Prototypes/Entities/Constructible/Ground/gascanisters.yml +++ b/Resources/Prototypes/Entities/Constructible/Ground/gascanisters.yml @@ -13,10 +13,12 @@ - type: Damageable - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: GasCanister - type: Anchorable - type: Pullable diff --git a/Resources/Prototypes/Entities/Constructible/Ground/gasfilters.yml b/Resources/Prototypes/Entities/Constructible/Ground/gasfilters.yml index 19a2f0e1cb..511c366131 100644 --- a/Resources/Prototypes/Entities/Constructible/Ground/gasfilters.yml +++ b/Resources/Prototypes/Entities/Constructible/Ground/gasfilters.yml @@ -13,10 +13,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Sprite netsync: false sprite: Constructible/Atmos/gasfilter.rsi @@ -27,7 +29,7 @@ - type: Appearance visuals: - type: GasFilterVisualizer - + - type: entity parent: GasFilterBase id: GasFilter @@ -48,4 +50,4 @@ - type: GasFilter inletDirection: South filterOutletDirection: East - outletDirection: North + outletDirection: North diff --git a/Resources/Prototypes/Entities/Constructible/Ground/kitchen.yml b/Resources/Prototypes/Entities/Constructible/Ground/kitchen.yml index 099a3fbfc7..b96351cbe8 100644 --- a/Resources/Prototypes/Entities/Constructible/Ground/kitchen.yml +++ b/Resources/Prototypes/Entities/Constructible/Ground/kitchen.yml @@ -23,8 +23,10 @@ - type: Damageable - type: Destructible thresholds: - 50: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: KitchenSpike diff --git a/Resources/Prototypes/Entities/Constructible/Ground/pipes.yml b/Resources/Prototypes/Entities/Constructible/Ground/pipes.yml index 1d0732e4d3..c28a4aac76 100644 --- a/Resources/Prototypes/Entities/Constructible/Ground/pipes.yml +++ b/Resources/Prototypes/Entities/Constructible/Ground/pipes.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity abstract: true id: PipeBase name: Pipe @@ -14,10 +14,12 @@ - type: Damageable - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Sprite netsync: false - type: Appearance diff --git a/Resources/Prototypes/Entities/Constructible/Ground/pumps.yml b/Resources/Prototypes/Entities/Constructible/Ground/pumps.yml index fc5f3d712c..7c49244cbf 100644 --- a/Resources/Prototypes/Entities/Constructible/Ground/pumps.yml +++ b/Resources/Prototypes/Entities/Constructible/Ground/pumps.yml @@ -13,10 +13,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Sprite netsync: false sprite: Constructible/Atmos/pump.rsi @@ -44,4 +46,3 @@ - type: PressurePump inletDirection: West outletDirection: East - \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Constructible/Ground/scrubbers.yml b/Resources/Prototypes/Entities/Constructible/Ground/scrubbers.yml index f83102c2b7..56a588242c 100644 --- a/Resources/Prototypes/Entities/Constructible/Ground/scrubbers.yml +++ b/Resources/Prototypes/Entities/Constructible/Ground/scrubbers.yml @@ -13,10 +13,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Sprite netsync: false sprite: Constructible/Atmos/scrubber.rsi @@ -27,7 +29,7 @@ - type: Appearance visuals: - type: SiphonVisualizer - + - type: entity parent: ScrubberBase id: Scrubber diff --git a/Resources/Prototypes/Entities/Constructible/Ground/vents.yml b/Resources/Prototypes/Entities/Constructible/Ground/vents.yml index 08c981107a..a07116725e 100644 --- a/Resources/Prototypes/Entities/Constructible/Ground/vents.yml +++ b/Resources/Prototypes/Entities/Constructible/Ground/vents.yml @@ -13,21 +13,23 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Sprite netsync: false sprite: Constructible/Atmos/vent.rsi layers: - sprite: Constructible/Atmos/pipe.rsi state: pipeHalf - - state: ventOff + - state: ventOff - type: Appearance visuals: - type: VentVisualizer - + - type: entity parent: VentBase id: Vent diff --git a/Resources/Prototypes/Entities/Constructible/Power/computers.yml b/Resources/Prototypes/Entities/Constructible/Power/computers.yml index 7a18538c45..540b1c3fca 100644 --- a/Resources/Prototypes/Entities/Constructible/Power/computers.yml +++ b/Resources/Prototypes/Entities/Constructible/Power/computers.yml @@ -98,10 +98,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: BreakableConstruction node: monitorBroken - type: Sprite diff --git a/Resources/Prototypes/Entities/Constructible/Power/debug_power.yml b/Resources/Prototypes/Entities/Constructible/Power/debug_power.yml index 04e78bfc1d..ac2097e731 100644 --- a/Resources/Prototypes/Entities/Constructible/Power/debug_power.yml +++ b/Resources/Prototypes/Entities/Constructible/Power/debug_power.yml @@ -31,10 +31,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Breakage"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Breakage"] - type: Anchorable - type: entity diff --git a/Resources/Prototypes/Entities/Constructible/Power/power_base.yml b/Resources/Prototypes/Entities/Constructible/Power/power_base.yml index aa504efbe8..7dd653d735 100644 --- a/Resources/Prototypes/Entities/Constructible/Power/power_base.yml +++ b/Resources/Prototypes/Entities/Constructible/Power/power_base.yml @@ -99,7 +99,7 @@ - type: ClientEntitySpawner prototypes: - HVDummyWire - + - type: entity id: BaseSubstation description: Reduces the voltage of electricity put into it. @@ -158,7 +158,7 @@ prototypes: - HVDummyWire - MVDummyWire - + - type: entity id: BaseApc description: A control terminal for the area's electrical systems. @@ -215,7 +215,7 @@ prototypes: - MVDummyWire - LVDummyWire - + - type: entity id: SolarPanel name: solar panel @@ -257,12 +257,14 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Breakage"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Breakage"] - type: Anchorable - type: Pullable - type: ClientEntitySpawner prototypes: - - HVDummyWire \ No newline at end of file + - HVDummyWire diff --git a/Resources/Prototypes/Entities/Constructible/Power/vending_machines.yml b/Resources/Prototypes/Entities/Constructible/Power/vending_machines.yml index 863a33a527..f19620729b 100644 --- a/Resources/Prototypes/Entities/Constructible/Power/vending_machines.yml +++ b/Resources/Prototypes/Entities/Constructible/Power/vending_machines.yml @@ -32,10 +32,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 50: - behaviors: - - !type:DoActsBehavior - acts: ["Breakage"] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Breakage"] - type: UserInterface interfaces: - key: enum.VendingMachineUiKey.Key diff --git a/Resources/Prototypes/Entities/Constructible/Power/wires.yml b/Resources/Prototypes/Entities/Constructible/Power/wires.yml index 144c1e1550..4f64639e05 100644 --- a/Resources/Prototypes/Entities/Constructible/Power/wires.yml +++ b/Resources/Prototypes/Entities/Constructible/Power/wires.yml @@ -22,10 +22,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: SubFloorHide - type: entity @@ -53,15 +55,17 @@ wireType: HighVoltage - type: Destructible thresholds: - 100: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - HVWireStack1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + HVWireStack1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity parent: WireBase @@ -90,15 +94,17 @@ wireType: MediumVoltage - type: Destructible thresholds: - 100: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - MVWireStack1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + MVWireStack1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity parent: WireBase @@ -129,15 +135,17 @@ wireType: Apc - type: Destructible thresholds: - 100: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - ApcExtensionCableStack1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + ApcExtensionCableStack1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] #Dummy wires @@ -153,8 +161,8 @@ drawdepth: BelowFloor - type: IconSmooth mode: CardinalFlags - - type: SubFloorHide - + - type: SubFloorHide + - type: entity abstract: true parent: BaseDummyWire @@ -167,7 +175,7 @@ - type: IconSmooth base: hvcable_ key: hv_cables - + - type: entity abstract: true parent: BaseDummyWire @@ -181,7 +189,7 @@ - type: IconSmooth base: mvcable_ key: mv_cables - + - type: entity abstract: true parent: BaseDummyWire @@ -194,4 +202,4 @@ color: Green - type: IconSmooth base: lvcable_ - key: lv_cables \ No newline at end of file + key: lv_cables diff --git a/Resources/Prototypes/Entities/Constructible/Specific/Engines/AME/ame_controller.yml b/Resources/Prototypes/Entities/Constructible/Specific/Engines/AME/ame_controller.yml index c4c1442a6f..6cd9655e6f 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/Engines/AME/ame_controller.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/Engines/AME/ame_controller.yml @@ -25,10 +25,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 500: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 500 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: SnapGrid offset: Center - type: Anchorable diff --git a/Resources/Prototypes/Entities/Constructible/Specific/Engines/AME/ame_structure.yml b/Resources/Prototypes/Entities/Constructible/Specific/Engines/AME/ame_structure.yml index 762aa88d8a..829e1c3c7e 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/Engines/AME/ame_structure.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/Engines/AME/ame_structure.yml @@ -25,15 +25,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 500: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - AMEPart: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 500 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + AMEPart: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: ["Destruction"] - type: SnapGrid offset: Center - type: Airtight diff --git a/Resources/Prototypes/Entities/Constructible/Specific/Medical/cloning_machine.yml b/Resources/Prototypes/Entities/Constructible/Specific/Medical/cloning_machine.yml index f58464d996..c9c770647a 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/Medical/cloning_machine.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/Medical/cloning_machine.yml @@ -34,10 +34,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance visuals: - type: CloningPodVisualizer diff --git a/Resources/Prototypes/Entities/Constructible/Specific/Medical/medical_scanner.yml b/Resources/Prototypes/Entities/Constructible/Specific/Medical/medical_scanner.yml index 1c0b3988c3..23048139d1 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/Medical/medical_scanner.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/Medical/medical_scanner.yml @@ -34,10 +34,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance visuals: - type: MedicalScannerVisualizer diff --git a/Resources/Prototypes/Entities/Constructible/Specific/cargo_telepad.yml b/Resources/Prototypes/Entities/Constructible/Specific/cargo_telepad.yml index 81a14bcffb..f2553f0bd4 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/cargo_telepad.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/cargo_telepad.yml @@ -21,10 +21,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 75: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 75 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Anchorable - type: Pullable - type: PowerReceiver diff --git a/Resources/Prototypes/Entities/Constructible/Specific/chem_master.yml b/Resources/Prototypes/Entities/Constructible/Specific/chem_master.yml index df75f399ba..a29d04f713 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/chem_master.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/chem_master.yml @@ -35,15 +35,17 @@ - type: Damageable - type: Destructible thresholds: - 50: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - chem_master_broken: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + chem_master_broken: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: UserInterface interfaces: - key: enum.ChemMasterUiKey.Key @@ -84,15 +86,17 @@ - type: Damageable - type: Destructible thresholds: - 25: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - SteelSheet1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 25 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + SteelSheet1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: UserInterface interfaces: - key: enum.ChemMasterUiKey.Key diff --git a/Resources/Prototypes/Entities/Constructible/Specific/disposal.yml b/Resources/Prototypes/Entities/Constructible/Specific/disposal.yml index 42260b9e78..68ddd0dcf1 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/disposal.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/disposal.yml @@ -17,10 +17,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Breakage"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Breakage"] - type: Rotatable - type: Pullable @@ -151,10 +153,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance visuals: - type: DisposalUnitVisualizer @@ -392,10 +396,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance visuals: - type: DisposalUnitVisualizer diff --git a/Resources/Prototypes/Entities/Constructible/Specific/gravity_generator.yml b/Resources/Prototypes/Entities/Constructible/Specific/gravity_generator.yml index cfc150fb63..f902754032 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/gravity_generator.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/gravity_generator.yml @@ -33,10 +33,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 150: - behaviors: - - !type:DoActsBehavior - acts: ["Breakage"] + - trigger: + !type:TotalDamageTrigger + damage: 150 + behaviors: + - !type:DoActsBehavior + acts: ["Breakage"] - type: GravityGenerator - type: UserInterface interfaces: diff --git a/Resources/Prototypes/Entities/Constructible/Specific/hydroponics.yml b/Resources/Prototypes/Entities/Constructible/Specific/hydroponics.yml index ab9faa97dd..5afac6dd20 100644 --- a/Resources/Prototypes/Entities/Constructible/Specific/hydroponics.yml +++ b/Resources/Prototypes/Entities/Constructible/Specific/hydroponics.yml @@ -27,10 +27,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 50: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Sprite sprite: Constructible/Hydroponics/hydro_tools.rsi state: soil diff --git a/Resources/Prototypes/Entities/Constructible/Storage/Closets/closet.yml b/Resources/Prototypes/Entities/Constructible/Storage/Closets/closet.yml index bfab9479d3..5c721679e6 100644 --- a/Resources/Prototypes/Entities/Constructible/Storage/Closets/closet.yml +++ b/Resources/Prototypes/Entities/Constructible/Storage/Closets/closet.yml @@ -43,10 +43,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance visuals: - type: StorageVisualizer diff --git a/Resources/Prototypes/Entities/Constructible/Storage/Crates/crate_base.yml b/Resources/Prototypes/Entities/Constructible/Storage/Crates/crate_base.yml index f0b3cd8b06..851baac2b8 100644 --- a/Resources/Prototypes/Entities/Constructible/Storage/Crates/crate_base.yml +++ b/Resources/Prototypes/Entities/Constructible/Storage/Crates/crate_base.yml @@ -39,10 +39,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance visuals: - type: StorageVisualizer diff --git a/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/base_tank.yml b/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/base_tank.yml index 54110ab38d..efce938513 100644 --- a/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/base_tank.yml +++ b/Resources/Prototypes/Entities/Constructible/Storage/StorageTanks/base_tank.yml @@ -27,10 +27,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 10: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: SolutionContainer maxVol: 1500 caps: Drainable diff --git a/Resources/Prototypes/Entities/Constructible/Walls/asteroid.yml b/Resources/Prototypes/Entities/Constructible/Walls/asteroid.yml index a4f7617608..9f87b72f8a 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/asteroid.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/asteroid.yml @@ -18,10 +18,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Occluder sizeX: 32 sizeY: 32 diff --git a/Resources/Prototypes/Entities/Constructible/Walls/girder.yml b/Resources/Prototypes/Entities/Constructible/Walls/girder.yml index 8960980e53..8895c3a02d 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/girder.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/girder.yml @@ -30,15 +30,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 50: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - SteelSheet1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + SteelSheet1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: SnapGrid offset: Edge placement: diff --git a/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml b/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml index 9617beec97..47a5cd08d5 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml @@ -40,10 +40,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 50: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: entity name: small light @@ -69,10 +71,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 25: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 25 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: entity name: unpowered small light @@ -90,7 +94,9 @@ resistances: metallicResistances - type: Destructible thresholds: - 25: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 25 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] diff --git a/Resources/Prototypes/Entities/Constructible/Walls/low_wall.yml b/Resources/Prototypes/Entities/Constructible/Walls/low_wall.yml index 0feb5c8737..855d13a9d0 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/low_wall.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/low_wall.yml @@ -29,10 +29,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: BreakableConstruction node: start - type: SnapGrid diff --git a/Resources/Prototypes/Entities/Constructible/Walls/signs.yml b/Resources/Prototypes/Entities/Constructible/Walls/signs.yml index ce9012762a..5fa72911c7 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/signs.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/signs.yml @@ -12,10 +12,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 5: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 5 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Sprite drawdepth: WallTops sprite: Constructible/Misc/decals.rsi diff --git a/Resources/Prototypes/Entities/Constructible/Walls/walls.yml b/Resources/Prototypes/Entities/Constructible/Walls/walls.yml index 8f8b9cc644..7b1f289dc2 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/walls.yml @@ -48,15 +48,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: brick @@ -74,15 +76,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: clock @@ -100,15 +104,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: clown @@ -127,15 +133,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: cult @@ -153,15 +161,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: debug @@ -179,15 +189,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: diamond @@ -206,15 +218,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: gold @@ -232,15 +246,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: ice @@ -258,15 +274,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: metal @@ -284,15 +302,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: plasma @@ -310,15 +330,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: plastic @@ -341,10 +363,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 600: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 600 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: BreakableConstruction node: girder - type: ReinforcedWall @@ -369,15 +393,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 1000: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 1000 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: riveted @@ -395,15 +421,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: sandstone @@ -421,15 +449,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: silver @@ -451,10 +481,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: BreakableConstruction node: girder destroySound: /Audio/Effects/metalbreak.ogg @@ -475,15 +507,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: uranium @@ -501,15 +535,17 @@ resistances: metallicResistances - type: Destructible thresholds: - 300: - behaviors: - - !type:SpawnEntitiesBehavior - spawn: - Girder: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 300 + behaviors: + - !type:SpawnEntitiesBehavior + spawn: + Girder: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: IconSmooth key: walls base: wood diff --git a/Resources/Prototypes/Entities/Constructible/Walls/windows.yml b/Resources/Prototypes/Entities/Constructible/Walls/windows.yml index 9f9778ae6e..3561517408 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/windows.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/windows.yml @@ -30,17 +30,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 15: - behaviors: - - !type:PlaySoundCollectionBehavior - soundCollection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardGlass: - min: 1 - max: 2 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 15 + behaviors: + - !type:PlaySoundCollectionBehavior + soundCollection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardGlass: + min: 1 + max: 2 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: SnapGrid offset: Center - type: Airtight @@ -67,17 +69,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 75: - behaviors: - - !type:PlaySoundCollectionBehavior - soundCollection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardGlassReinforced: - min: 1 - max: 2 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 75 + behaviors: + - !type:PlaySoundCollectionBehavior + soundCollection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardGlassReinforced: + min: 1 + max: 2 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Window base: rwindow - type: Construction @@ -98,17 +102,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 100: - behaviors: - - !type:PlaySoundCollectionBehavior - soundCollection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardGlassPhoron: - min: 1 - max: 2 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 100 + behaviors: + - !type:PlaySoundCollectionBehavior + soundCollection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardGlassPhoron: + min: 1 + max: 2 + - !type:DoActsBehavior + acts: [ "Destruction" ] resistances: metallicResistances - type: Window base: pwindow diff --git a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml index 00e78fce7f..90d3f438df 100644 --- a/Resources/Prototypes/Entities/Effects/chemistry_effects.yml +++ b/Resources/Prototypes/Entities/Effects/chemistry_effects.yml @@ -125,10 +125,12 @@ resistances: metallicResistances - type: Destructible thresholds: - 50: - behaviors: - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity id: FoamedIronMetal diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 3f9254499c..4291e33116 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -164,9 +164,12 @@ 200: !type:DeadMobState {} - type: Destructible thresholds: - 400: - behaviors: - - !type:GibBehavior { } + - trigger: + !type:TotalDamageTypesTrigger + damage: + Blunt: 400 + behaviors: + - !type:GibBehavior { } - type: HeatResistance - type: Appearance visuals: diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml index 545c343390..31c5f77a48 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks.yml @@ -26,18 +26,20 @@ - type: Damageable - type: Destructible thresholds: - 5: - behaviors: - - !type:PlaySoundCollectionBehavior - soundCollection: GlassBreak - - !type:SpillBehavior { } - - !type:SpawnEntitiesBehavior - spawn: - ShardGlass: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 5 + behaviors: + - !type:PlaySoundCollectionBehavior + soundCollection: GlassBreak + - !type:SpillBehavior { } + - !type:SpawnEntitiesBehavior + spawn: + ShardGlass: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: DamageOnLand amount: 5 - type: DamageOtherOnHit diff --git a/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml b/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml index 1dce8418b5..b294dfab05 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/drinks_bottles.yml @@ -19,18 +19,20 @@ - type: Damageable - type: Destructible thresholds: - 5: - behaviors: - - !type:PlaySoundCollectionBehavior - soundCollection: GlassBreak - - !type:SpillBehavior { } - - !type:SpawnEntitiesBehavior - spawn: - ShardGlass: - min: 1 - max: 2 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 5 + behaviors: + - !type:PlaySoundCollectionBehavior + soundCollection: GlassBreak + - !type:SpillBehavior { } + - !type:SpawnEntitiesBehavior + spawn: + ShardGlass: + min: 1 + max: 2 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity parent: DrinkBottleBaseFull diff --git a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml index f4c5420753..5da29c32bb 100644 --- a/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml +++ b/Resources/Prototypes/Entities/Objects/Misc/fluff_lights.yml @@ -131,17 +131,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 10: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/glass_break1.ogg - - !type:SpawnEntitiesBehavior - spawn: - FloodlightBroken: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 10 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/glass_break1.ogg + - !type:SpawnEntitiesBehavior + spawn: + FloodlightBroken: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Appearance visuals: - type: FlashLightVisualizer @@ -160,17 +162,19 @@ resistances: metallicResistances - type: Destructible thresholds: - 20: - behaviors: - - !type:PlaySoundBehavior - sound: /Audio/Effects/metalbreak.ogg - - !type:SpawnEntitiesBehavior - spawn: - SteelSheet1: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 20 + behaviors: + - !type:PlaySoundBehavior + sound: /Audio/Effects/metalbreak.ogg + - !type:SpawnEntitiesBehavior + spawn: + SteelSheet1: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: Physics shapes: - !type:PhysShapeAabb diff --git a/Resources/Prototypes/Entities/Objects/Power/lights.yml b/Resources/Prototypes/Entities/Objects/Power/lights.yml index 0037831aa0..386970c607 100644 --- a/Resources/Prototypes/Entities/Objects/Power/lights.yml +++ b/Resources/Prototypes/Entities/Objects/Power/lights.yml @@ -11,23 +11,27 @@ amount: 5 - type: Destructible thresholds: - 5: - behaviors: - - !type:PlaySoundCollectionBehavior - soundCollection: GlassBreak - - !type:DoActsBehavior - acts: [ "Breakage" ] - 10: - behaviors: - - !type:PlaySoundCollectionBehavior - soundCollection: GlassBreak - - !type:SpawnEntitiesBehavior - spawn: - ShardGlass: - min: 1 - max: 1 - - !type:DoActsBehavior - acts: [ "Destruction" ] + - trigger: + !type:TotalDamageTrigger + damage: 5 + behaviors: + - !type:PlaySoundCollectionBehavior + soundCollection: GlassBreak + - !type:DoActsBehavior + acts: [ "Breakage" ] + - trigger: + !type:TotalDamageTrigger + damage: 10 + behaviors: + - !type:PlaySoundCollectionBehavior + soundCollection: GlassBreak + - !type:SpawnEntitiesBehavior + spawn: + ShardGlass: + min: 1 + max: 1 + - !type:DoActsBehavior + acts: [ "Destruction" ] - type: entity parent: BaseLightbulb diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Explosives/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Explosives/grenades.yml index 75426cdadc..ac0c09d3ee 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Explosives/grenades.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Explosives/grenades.yml @@ -25,10 +25,12 @@ - type: Damageable - type: Destructible thresholds: - 10: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance visuals: - type: TimerTriggerVisualizer @@ -57,10 +59,12 @@ - type: Damageable - type: Destructible thresholds: - 10: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance visuals: - type: TimerTriggerVisualizer @@ -89,10 +93,12 @@ - type: Damageable - type: Destructible thresholds: - 10: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 10 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance visuals: - type: TimerTriggerVisualizer @@ -120,10 +126,12 @@ - type: Damageable - type: Destructible thresholds: - 50: - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + - trigger: + !type:TotalDamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] - type: Appearance visuals: - type: TimerTriggerVisualizer From 63e12525393aa8340e743d1cdaed49f378665a92 Mon Sep 17 00:00:00 2001 From: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com> Date: Fri, 5 Feb 2021 17:01:54 +0100 Subject: [PATCH 03/22] Speech and Emoting components (#3075) * Add Speech and Emoting components. * makesentient gives you speech and emoting abilities. * Ghosts do *not* need a SpeechComponent. * I hate you all, and I hate shared code as well. --- Content.Server/Chat/ChatManager.cs | 1 + .../Commands/MakeSentientCommand.cs | 4 +++ ...SpeechComponent.cs => IAccentComponent.cs} | 9 ++++- .../Mobs/Speech/SharedEmotingComponent.cs | 33 +++++++++++++++++ .../Mobs/Speech/SharedSpeechComponent.cs | 36 +++++++++++++++++++ .../ActionBlocker/ActionBlockerSystem.cs | 9 ++++- .../Entities/Mobs/Species/human.yml | 2 ++ 7 files changed, 92 insertions(+), 2 deletions(-) rename Content.Server/GameObjects/Components/Mobs/Speech/{SpeechComponent.cs => IAccentComponent.cs} (51%) create mode 100644 Content.Shared/GameObjects/Components/Mobs/Speech/SharedEmotingComponent.cs create mode 100644 Content.Shared/GameObjects/Components/Mobs/Speech/SharedSpeechComponent.cs diff --git a/Content.Server/Chat/ChatManager.cs b/Content.Server/Chat/ChatManager.cs index e1b614f017..d4235b55dc 100644 --- a/Content.Server/Chat/ChatManager.cs +++ b/Content.Server/Chat/ChatManager.cs @@ -4,6 +4,7 @@ using Content.Server.Administration; using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Headset; using Content.Server.GameObjects.Components.Items.Storage; +using Content.Server.GameObjects.Components.Mobs.Speech; using Content.Server.GameObjects.Components.Observer; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces; diff --git a/Content.Server/Commands/MakeSentientCommand.cs b/Content.Server/Commands/MakeSentientCommand.cs index aa9c99cdb0..7882fde7ba 100644 --- a/Content.Server/Commands/MakeSentientCommand.cs +++ b/Content.Server/Commands/MakeSentientCommand.cs @@ -1,8 +1,10 @@ #nullable enable using Content.Server.Administration; using Content.Server.GameObjects.Components.Mobs; +using Content.Server.GameObjects.Components.Mobs.Speech; using Content.Server.GameObjects.Components.Movement; using Content.Shared.Administration; +using Content.Shared.GameObjects.Components.Mobs.Speech; using Robust.Server.Interfaces.Player; using Robust.Shared.Console; using Robust.Shared.GameObjects; @@ -47,6 +49,8 @@ namespace Content.Server.Commands entity.EnsureComponent(); entity.EnsureComponent(); + entity.EnsureComponent(); + entity.EnsureComponent(); } } } diff --git a/Content.Server/GameObjects/Components/Mobs/Speech/SpeechComponent.cs b/Content.Server/GameObjects/Components/Mobs/Speech/IAccentComponent.cs similarity index 51% rename from Content.Server/GameObjects/Components/Mobs/Speech/SpeechComponent.cs rename to Content.Server/GameObjects/Components/Mobs/Speech/IAccentComponent.cs index 35ab69ed11..92efa89b8d 100644 --- a/Content.Server/GameObjects/Components/Mobs/Speech/SpeechComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/Speech/IAccentComponent.cs @@ -1,4 +1,11 @@ -namespace Content.Server.GameObjects.Components.Mobs.Speech +#nullable enable +using Content.Shared.GameObjects.Components.Mobs.Speech; +using Content.Shared.GameObjects.EntitySystems.ActionBlocker; +using Robust.Shared.GameObjects; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Mobs.Speech { internal interface IAccentComponent { diff --git a/Content.Shared/GameObjects/Components/Mobs/Speech/SharedEmotingComponent.cs b/Content.Shared/GameObjects/Components/Mobs/Speech/SharedEmotingComponent.cs new file mode 100644 index 0000000000..edf003a196 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Mobs/Speech/SharedEmotingComponent.cs @@ -0,0 +1,33 @@ +using Content.Shared.GameObjects.EntitySystems.ActionBlocker; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Mobs.Speech +{ + [RegisterComponent] + public class SharedEmotingComponent : Component, IActionBlocker + { + private bool _enabled = true; + public override string Name => "Emoting"; + + public bool Enabled + { + get => _enabled; + set + { + if (_enabled == value) return; + _enabled = value; + Dirty(); + } + } + + bool IActionBlocker.CanEmote() => Enabled; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(this, x => x.Enabled, "enabled", true); + } + } +} diff --git a/Content.Shared/GameObjects/Components/Mobs/Speech/SharedSpeechComponent.cs b/Content.Shared/GameObjects/Components/Mobs/Speech/SharedSpeechComponent.cs new file mode 100644 index 0000000000..12a7a77995 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Mobs/Speech/SharedSpeechComponent.cs @@ -0,0 +1,36 @@ +using Content.Shared.GameObjects.EntitySystems.ActionBlocker; +using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Mobs.Speech +{ + /// + /// Component required for entities to be able to speak. + /// + [RegisterComponent] + public class SharedSpeechComponent : Component, IActionBlocker + { + private bool _enabled = true; + public override string Name => "Speech"; + + public bool Enabled + { + get => _enabled; + set + { + if (_enabled == value) return; + _enabled = value; + Dirty(); + } + } + + bool IActionBlocker.CanSpeak() => Enabled; + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(this, x => x.Enabled, "enabled", true); + } + } +} diff --git a/Content.Shared/GameObjects/EntitySystems/ActionBlocker/ActionBlockerSystem.cs b/Content.Shared/GameObjects/EntitySystems/ActionBlocker/ActionBlockerSystem.cs index 63367caa19..b2adcf73e1 100644 --- a/Content.Shared/GameObjects/EntitySystems/ActionBlocker/ActionBlockerSystem.cs +++ b/Content.Shared/GameObjects/EntitySystems/ActionBlocker/ActionBlockerSystem.cs @@ -1,4 +1,5 @@ -using Content.Shared.GameObjects.EntitySystems.EffectBlocker; +using Content.Shared.GameObjects.Components.Mobs.Speech; +using Content.Shared.GameObjects.EntitySystems.EffectBlocker; using JetBrains.Annotations; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; @@ -62,6 +63,9 @@ namespace Content.Shared.GameObjects.EntitySystems.ActionBlocker public static bool CanSpeak(IEntity entity) { + if (!entity.HasComponent()) + return false; + var canSpeak = true; foreach (var blocker in entity.GetAllComponents()) @@ -98,6 +102,9 @@ namespace Content.Shared.GameObjects.EntitySystems.ActionBlocker public static bool CanEmote(IEntity entity) { + if (!entity.HasComponent()) + return false; + var canEmote = true; foreach (var blocker in entity.GetAllComponents()) diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 4291e33116..c4ec747fc5 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -210,6 +210,8 @@ meat: FoodMeat - type: Recyclable safe: false + - type: Speech + - type: Emoting - type: entity save: false From 9884b14e8d1bdff0b33187e3729676c3501d0689 Mon Sep 17 00:00:00 2001 From: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com> Date: Fri, 5 Feb 2021 17:02:20 +0100 Subject: [PATCH 04/22] Adds a component for ghosting on move. (#3090) * Adds a component for ghosting on move. Adds a dummy input mover for IRelayMoveInput to work. * Add IGhostOnMove * Fix tests. --- .../Components/Body/Behavior/BrainBehavior.cs | 9 ++++ .../Components/Body/BodyComponent.cs | 4 +- .../Observer/GhostOnMoveComponent.cs | 41 +++++++++++++++++++ .../Components/Observer/IGhostOnMove.cs | 8 ++++ .../SharedDummyInputMoverComponent.cs | 29 +++++++++++++ 5 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 Content.Server/GameObjects/Components/Observer/GhostOnMoveComponent.cs create mode 100644 Content.Server/GameObjects/Components/Observer/IGhostOnMove.cs create mode 100644 Content.Shared/GameObjects/Components/Movement/SharedDummyInputMoverComponent.cs diff --git a/Content.Server/GameObjects/Components/Body/Behavior/BrainBehavior.cs b/Content.Server/GameObjects/Components/Body/Behavior/BrainBehavior.cs index d5533a961d..5b740f75f1 100644 --- a/Content.Server/GameObjects/Components/Body/Behavior/BrainBehavior.cs +++ b/Content.Server/GameObjects/Components/Body/Behavior/BrainBehavior.cs @@ -1,7 +1,9 @@ #nullable enable using Content.Server.GameObjects.Components.Mobs; +using Content.Server.GameObjects.Components.Observer; using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body.Part; +using Content.Shared.GameObjects.Components.Movement; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; @@ -56,6 +58,13 @@ namespace Content.Server.GameObjects.Components.Body.Behavior newEntity.EnsureComponent(); var oldMind = oldEntity.EnsureComponent(); + if (!newEntity.HasComponent()) + newEntity.AddComponent(); + + // TODO: This is an awful solution. + if (!newEntity.HasComponent()) + newEntity.AddComponent(); + oldMind.Mind?.TransferTo(newEntity); } } diff --git a/Content.Server/GameObjects/Components/Body/BodyComponent.cs b/Content.Server/GameObjects/Components/Body/BodyComponent.cs index 8f9eea9aac..8adec50390 100644 --- a/Content.Server/GameObjects/Components/Body/BodyComponent.cs +++ b/Content.Server/GameObjects/Components/Body/BodyComponent.cs @@ -1,6 +1,7 @@ #nullable enable using System; using Content.Server.Commands.Observer; +using Content.Server.GameObjects.Components.Observer; using Content.Shared.Audio; using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body.Part; @@ -25,7 +26,8 @@ namespace Content.Server.GameObjects.Components.Body [RegisterComponent] [ComponentReference(typeof(SharedBodyComponent))] [ComponentReference(typeof(IBody))] - public class BodyComponent : SharedBodyComponent, IRelayMoveInput + [ComponentReference(typeof(IGhostOnMove))] + public class BodyComponent : SharedBodyComponent, IRelayMoveInput, IGhostOnMove { private Container _partContainer = default!; diff --git a/Content.Server/GameObjects/Components/Observer/GhostOnMoveComponent.cs b/Content.Server/GameObjects/Components/Observer/GhostOnMoveComponent.cs new file mode 100644 index 0000000000..3ba9cc3a16 --- /dev/null +++ b/Content.Server/GameObjects/Components/Observer/GhostOnMoveComponent.cs @@ -0,0 +1,41 @@ +#nullable enable +using System; +using Content.Server.Commands.Observer; +using Content.Server.GameObjects.Components.Mobs; +using Content.Server.GameObjects.Components.Movement; +using Content.Shared.GameObjects.Components.Movement; +using Robust.Server.Console; +using Robust.Shared.Console; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Players; +using Robust.Shared.Serialization; + +namespace Content.Server.GameObjects.Components.Observer +{ + [RegisterComponent] + [ComponentReference(typeof(IGhostOnMove))] + public class GhostOnMoveComponent : Component, IRelayMoveInput, IGhostOnMove + { + public override string Name => "GhostOnMove"; + + public bool CanReturn { get; set; } + + public override void ExposeData(ObjectSerializer serializer) + { + base.ExposeData(serializer); + + serializer.DataField(this, x => x.CanReturn, "canReturn", true); + } + + void IRelayMoveInput.MoveInputPressed(ICommonSession session) + { + // Let's not ghost if our mind is visiting... + if (Owner.HasComponent()) return; + if (!Owner.TryGetComponent(out MindComponent? mind) || !mind.HasMind || mind.Mind!.IsVisitingEntity) return; + + var host = IoCManager.Resolve(); + new Ghost().Execute(new ConsoleShell(host, session), string.Empty, Array.Empty()); + } + } +} diff --git a/Content.Server/GameObjects/Components/Observer/IGhostOnMove.cs b/Content.Server/GameObjects/Components/Observer/IGhostOnMove.cs new file mode 100644 index 0000000000..5ba726d2f1 --- /dev/null +++ b/Content.Server/GameObjects/Components/Observer/IGhostOnMove.cs @@ -0,0 +1,8 @@ +using Content.Shared.GameObjects.Components.Movement; + +namespace Content.Server.GameObjects.Components.Observer +{ + public interface IGhostOnMove + { + } +} diff --git a/Content.Shared/GameObjects/Components/Movement/SharedDummyInputMoverComponent.cs b/Content.Shared/GameObjects/Components/Movement/SharedDummyInputMoverComponent.cs new file mode 100644 index 0000000000..07a5804b4a --- /dev/null +++ b/Content.Shared/GameObjects/Components/Movement/SharedDummyInputMoverComponent.cs @@ -0,0 +1,29 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Map; +using Robust.Shared.Maths; + +namespace Content.Shared.GameObjects.Components.Movement +{ + [RegisterComponent] + [ComponentReference(typeof(IMoverComponent))] + public class SharedDummyInputMoverComponent : Component, IMoverComponent + { + public override string Name => "DummyInputMover"; + public float CurrentWalkSpeed => 0f; + public float CurrentSprintSpeed => 0f; + public float CurrentPushSpeed => 0f; + public float GrabRange => 0f; + public bool Sprinting => false; + public (Vector2 walking, Vector2 sprinting) VelocityDir => (Vector2.Zero, Vector2.Zero); + public EntityCoordinates LastPosition { get; set; } + public float StepSoundDistance { get; set; } + + public void SetVelocityDirection(Direction direction, ushort subTick, bool enabled) + { + } + + public void SetSprinting(ushort subTick, bool walking) + { + } + } +} From 7ed07c0cacaee14e198bc546619538037325c083 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 6 Feb 2021 03:11:21 +1100 Subject: [PATCH 05/22] Cuff enhancements (#3087) * Cuff enhancements * Cuffs now have an OnClick for the alert to remove them * nullables * Use default interaction range so highlights are accurate * Cuffing fails more gracely * Make shared abstract and add component references to client / server * Don't cache AudioSystem and HandsComponent given cuffs are rarely used * Fix test Co-authored-by: Metal Gear Sloth --- .../ActionBlocking/HandcuffComponent.cs | 6 +- .../{CuffUnitTest.cs => HandCuffTest.cs} | 6 +- Content.Server/Alert/Click/RemoveCuffs.cs | 25 ++++++ .../ActionBlocking/CuffableComponent.cs | 85 ++++++++++++------- .../ActionBlocking/HandcuffComponent.cs | 68 +++++++-------- .../ActionBlocking/SharedHandcuffComponent.cs | 2 +- Resources/Prototypes/Alerts/alerts.yml | 3 +- 7 files changed, 121 insertions(+), 74 deletions(-) rename Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/{CuffUnitTest.cs => HandCuffTest.cs} (95%) create mode 100644 Content.Server/Alert/Click/RemoveCuffs.cs diff --git a/Content.Client/GameObjects/Components/ActionBlocking/HandcuffComponent.cs b/Content.Client/GameObjects/Components/ActionBlocking/HandcuffComponent.cs index adc7488425..9cee1ac181 100644 --- a/Content.Client/GameObjects/Components/ActionBlocking/HandcuffComponent.cs +++ b/Content.Client/GameObjects/Components/ActionBlocking/HandcuffComponent.cs @@ -1,4 +1,5 @@ -using Content.Shared.GameObjects.Components.ActionBlocking; +#nullable enable +using Content.Shared.GameObjects.Components.ActionBlocking; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Shared.GameObjects; @@ -6,9 +7,10 @@ using Robust.Shared.GameObjects; namespace Content.Client.GameObjects.Components.ActionBlocking { [RegisterComponent] + [ComponentReference(typeof(SharedHandcuffComponent))] public class HandcuffComponent : SharedHandcuffComponent { - public override void HandleComponentState(ComponentState curState, ComponentState nextState) + public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) { if (curState is not HandcuffedComponentState state) { diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/CuffUnitTest.cs b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs similarity index 95% rename from Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/CuffUnitTest.cs rename to Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs index 2259df9c43..5910ba4a26 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/CuffUnitTest.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/ActionBlocking/HandCuffTest.cs @@ -18,7 +18,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking [TestFixture] [TestOf(typeof(CuffableComponent))] [TestOf(typeof(HandcuffComponent))] - public class CuffUnitTest : ContentIntegrationTest + public class HandCuffTest : ContentIntegrationTest { private const string PROTOTYPES = @" - type: entity @@ -77,7 +77,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking Assert.True(secondCuffs.TryGetComponent(out secondHandcuff!), $"Second handcuffs has no {nameof(HandcuffComponent)}"); // Test to ensure cuffed players register the handcuffs - cuffed.AddNewCuffs(cuffs); + cuffed.TryAddNewCuffs(human, cuffs); Assert.True(cuffed.CuffedHandCount > 0, "Handcuffing a player did not result in their hands being cuffed"); // Test to ensure a player with 4 hands will still only have 2 hands cuffed @@ -86,7 +86,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking Assert.True(cuffed.CuffedHandCount == 2 && hands.Hands.Count() == 4, "Player doesn't have correct amount of hands cuffed"); // Test to give a player with 4 hands 2 sets of cuffs - cuffed.AddNewCuffs(secondCuffs); + cuffed.TryAddNewCuffs(human, secondCuffs); Assert.True(cuffed.CuffedHandCount == 4, "Player doesn't have correct amount of hands cuffed"); }); diff --git a/Content.Server/Alert/Click/RemoveCuffs.cs b/Content.Server/Alert/Click/RemoveCuffs.cs new file mode 100644 index 0000000000..d4882a93c3 --- /dev/null +++ b/Content.Server/Alert/Click/RemoveCuffs.cs @@ -0,0 +1,25 @@ +#nullable enable +using Content.Server.GameObjects.Components.ActionBlocking; +using Content.Shared.Alert; +using JetBrains.Annotations; +using Robust.Shared.Serialization; + +namespace Content.Server.Alert.Click +{ + /// + /// Try to remove handcuffs from yourself + /// + [UsedImplicitly] + public class RemoveCuffs : IAlertClick + { + public void ExposeData(ObjectSerializer serializer) {} + + public void AlertClicked(ClickAlertEventArgs args) + { + if (args.Player.TryGetComponent(out CuffableComponent? cuffableComponent)) + { + cuffableComponent.TryUncuff(args.Player); + } + } + } +} diff --git a/Content.Server/GameObjects/Components/ActionBlocking/CuffableComponent.cs b/Content.Server/GameObjects/Components/ActionBlocking/CuffableComponent.cs index e82dada792..61354f3538 100644 --- a/Content.Server/GameObjects/Components/ActionBlocking/CuffableComponent.cs +++ b/Content.Server/GameObjects/Components/ActionBlocking/CuffableComponent.cs @@ -1,15 +1,13 @@ -using System; +#nullable enable +using System; using System.Collections.Generic; using System.Linq; using Content.Server.GameObjects.Components.GUI; -using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.EntitySystems.DoAfter; using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Shared.Alert; using Content.Shared.GameObjects.Components.ActionBlocking; -using Content.Shared.GameObjects.Components.Mobs; -using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.GameObjects.Verbs; using Content.Shared.Interfaces; @@ -46,24 +44,20 @@ namespace Content.Server.GameObjects.Components.ActionBlocking [ViewVariables(VVAccess.ReadOnly)] private Container _container = default!; - private float _interactRange; - private IHandsComponent _hands; + // TODO: Make a component message + public event Action? OnCuffedStateChanged; - public event Action OnCuffedStateChanged; + private bool _uncuffing; public override void Initialize() { base.Initialize(); _container = ContainerManagerComponent.Ensure(Name, Owner); - _interactRange = SharedInteractionSystem.InteractionRange / 2; Owner.EntityManager.EventBus.SubscribeEvent(EventSource.Local, this, HandleHandCountChange); - if (!Owner.TryGetComponent(out _hands)) - { - Logger.Warning("Player does not have an IHandsComponent!"); - } + Owner.EnsureComponentWarn(); } public override ComponentState GetComponentState() @@ -99,27 +93,35 @@ namespace Content.Server.GameObjects.Components.ActionBlocking /// Add a set of cuffs to an existing CuffedComponent. /// /// - public void AddNewCuffs(IEntity handcuff) + public bool TryAddNewCuffs(IEntity user, IEntity handcuff) { if (!handcuff.HasComponent()) { Logger.Warning($"Handcuffs being applied to player are missing a {nameof(HandcuffComponent)}!"); - return; + return false; } - if (!handcuff.InRangeUnobstructed(Owner, _interactRange)) + if (!handcuff.InRangeUnobstructed(Owner)) { Logger.Warning("Handcuffs being applied to player are obstructed or too far away! This should not happen!"); - return; + return true; + } + + // Success! + if (user.TryGetComponent(out HandsComponent? handsComponent) && handsComponent.IsHolding(handcuff)) + { + // Good lord handscomponent is scuffed, I hope some smug person will fix it someday + handsComponent.Drop(handcuff); } _container.Insert(handcuff); - CanStillInteract = _hands.Hands.Count() > CuffedHandCount; + CanStillInteract = Owner.TryGetComponent(out HandsComponent? ownerHands) && ownerHands.Hands.Count() > CuffedHandCount; OnCuffedStateChanged?.Invoke(); UpdateAlert(); UpdateHeldItems(); Dirty(); + return true; } /// @@ -128,7 +130,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking private void UpdateHandCount() { var dirty = false; - var handCount = _hands.Hands.Count(); + var handCount = Owner.TryGetComponent(out HandsComponent? handsComponent) ? handsComponent.Hands.Count() : 0; while (CuffedHandCount > handCount && CuffedHandCount > 0) { @@ -142,7 +144,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking if (dirty) { CanStillInteract = handCount > CuffedHandCount; - OnCuffedStateChanged.Invoke(); + OnCuffedStateChanged?.Invoke(); Dirty(); } } @@ -160,17 +162,19 @@ namespace Content.Server.GameObjects.Components.ActionBlocking /// public void UpdateHeldItems() { - var itemCount = _hands.GetAllHeldItems().Count(); - var freeHandCount = _hands.Hands.Count() - CuffedHandCount; + if (!Owner.TryGetComponent(out HandsComponent? handsComponent)) return; + + var itemCount = handsComponent.GetAllHeldItems().Count(); + var freeHandCount = handsComponent.Hands.Count() - CuffedHandCount; if (freeHandCount < itemCount) { - foreach (var item in _hands.GetAllHeldItems()) + foreach (var item in handsComponent.GetAllHeldItems()) { if (freeHandCount < itemCount) { freeHandCount++; - _hands.Drop(item.Owner, false); + handsComponent.Drop(item.Owner, false); } else { @@ -185,7 +189,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking /// private void UpdateAlert() { - if (Owner.TryGetComponent(out ServerAlertsComponent status)) + if (Owner.TryGetComponent(out ServerAlertsComponent? status)) { if (CanStillInteract) { @@ -204,8 +208,10 @@ namespace Content.Server.GameObjects.Components.ActionBlocking /// /// The cuffed entity /// Optional param for the handcuff entity to remove from the cuffed entity. If null, uses the most recently added handcuff entity. - public async void TryUncuff(IEntity user, IEntity cuffsToRemove = null) + public async void TryUncuff(IEntity user, IEntity? cuffsToRemove = null) { + if (_uncuffing) return; + var isOwner = user == Owner; if (cuffsToRemove == null) @@ -232,13 +238,13 @@ namespace Content.Server.GameObjects.Components.ActionBlocking return; } - if (!isOwner && !user.InRangeUnobstructed(Owner, _interactRange)) + if (!isOwner && !user.InRangeUnobstructed(Owner)) { user.PopupMessage(Loc.GetString("You are too far away to remove the cuffs.")); return; } - if (!cuffsToRemove.InRangeUnobstructed(Owner, _interactRange)) + if (!cuffsToRemove.InRangeUnobstructed(Owner)) { Logger.Warning("Handcuffs being removed from player are obstructed or too far away! This should not happen!"); return; @@ -247,7 +253,17 @@ namespace Content.Server.GameObjects.Components.ActionBlocking user.PopupMessage(Loc.GetString("You start removing the cuffs.")); var audio = EntitySystem.Get(); - audio.PlayFromEntity(isOwner ? cuff.StartBreakoutSound : cuff.StartUncuffSound, Owner); + if (isOwner) + { + if (cuff.StartBreakoutSound != null) + audio.PlayFromEntity(cuff.StartBreakoutSound, Owner); + } + else + { + if (cuff.StartUncuffSound != null) + audio.PlayFromEntity(cuff.StartUncuffSound, Owner); + } + var uncuffTime = isOwner ? cuff.BreakoutTime : cuff.UncuffTime; var doAfterEventArgs = new DoAfterEventArgs(user, uncuffTime) @@ -259,11 +275,16 @@ namespace Content.Server.GameObjects.Components.ActionBlocking }; var doAfterSystem = EntitySystem.Get(); + _uncuffing = true; + var result = await doAfterSystem.DoAfter(doAfterEventArgs); + _uncuffing = false; + if (result != DoAfterStatus.Cancelled) { - audio.PlayFromEntity(cuff.EndUncuffSound, Owner); + if (cuff.EndUncuffSound != null) + audio.PlayFromEntity(cuff.EndUncuffSound, Owner); _container.ForceRemove(cuffsToRemove); cuffsToRemove.Transform.AttachToGridOrMap(); @@ -276,14 +297,14 @@ namespace Content.Server.GameObjects.Components.ActionBlocking cuffsToRemove.Name = cuff.BrokenName; cuffsToRemove.Description = cuff.BrokenDesc; - if (cuffsToRemove.TryGetComponent(out var sprite)) + if (cuffsToRemove.TryGetComponent(out var sprite) && cuff.BrokenState != null) { sprite.LayerSetState(0, cuff.BrokenState); // TODO: safety check to see if RSI contains the state? } } - CanStillInteract = _hands.Hands.Count() > CuffedHandCount; - OnCuffedStateChanged.Invoke(); + CanStillInteract = Owner.TryGetComponent(out HandsComponent? handsComponent) && handsComponent.Hands.Count() > CuffedHandCount; + OnCuffedStateChanged?.Invoke(); UpdateAlert(); Dirty(); diff --git a/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs b/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs index 9e05ab7cd1..51f0addcee 100644 --- a/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs +++ b/Content.Server/GameObjects/Components/ActionBlocking/HandcuffComponent.cs @@ -1,10 +1,10 @@ -using System; +#nullable enable +using System; using System.Threading.Tasks; using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.EntitySystems.DoAfter; using Content.Shared.GameObjects.Components.ActionBlocking; -using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; @@ -14,7 +14,6 @@ using Robust.Shared.GameObjects; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Localization; -using Robust.Shared.Log; using Robust.Shared.Maths; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; @@ -22,6 +21,7 @@ using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.ActionBlocking { [RegisterComponent] + [ComponentReference(typeof(SharedHandcuffComponent))] public class HandcuffComponent : SharedHandcuffComponent, IAfterInteract { /// @@ -58,31 +58,31 @@ namespace Content.Server.GameObjects.Components.ActionBlocking /// The path of the RSI file used for the player cuffed overlay. /// [ViewVariables] - public string CuffedRSI { get; set; } + public string? CuffedRSI { get; set; } /// /// The iconstate used with the RSI file for the player cuffed overlay. /// [ViewVariables] - public string OverlayIconState { get; set; } + public string? OverlayIconState { get; set; } /// /// The iconstate used for broken handcuffs /// [ViewVariables] - public string BrokenState { get; set; } + public string? BrokenState { get; set; } /// /// The iconstate used for broken handcuffs /// [ViewVariables] - public string BrokenName { get; set; } + public string BrokenName { get; set; } = default!; /// /// The iconstate used for broken handcuffs /// [ViewVariables] - public string BrokenDesc { get; set; } + public string BrokenDesc { get; set; } = default!; [ViewVariables] public bool Broken @@ -102,25 +102,20 @@ namespace Content.Server.GameObjects.Components.ActionBlocking } } - public string StartCuffSound { get; set; } - public string EndCuffSound { get; set; } - public string StartBreakoutSound { get; set; } - public string StartUncuffSound { get; set; } - public string EndUncuffSound { get; set; } + public string? StartCuffSound { get; set; } + public string? EndCuffSound { get; set; } + public string? StartBreakoutSound { get; set; } + public string? StartUncuffSound { get; set; } + public string? EndUncuffSound { get; set; } public Color Color { get; set; } // Non-exposed data fields private bool _isBroken = false; - private float _interactRange; - private AudioSystem _audioSystem; - public override void Initialize() - { - base.Initialize(); - - _audioSystem = EntitySystem.Get(); - _interactRange = SharedInteractionSystem.InteractionRange / 2; - } + /// + /// Used to prevent DoAfter getting spammed. + /// + private bool _cuffing; public override void ExposeData(ObjectSerializer serializer) { @@ -150,6 +145,8 @@ namespace Content.Server.GameObjects.Components.ActionBlocking async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { + if (_cuffing) return true; + if (eventArgs.Target == null || !ActionBlockerSystem.CanUse(eventArgs.User) || !eventArgs.Target.TryGetComponent(out var cuffed)) { return false; @@ -179,7 +176,7 @@ namespace Content.Server.GameObjects.Components.ActionBlocking return true; } - if (!eventArgs.InRangeUnobstructed(_interactRange, ignoreInsideBlocker: true)) + if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true)) { eventArgs.User.PopupMessage(Loc.GetString("You are too far away to use the cuffs!")); return true; @@ -187,7 +184,9 @@ namespace Content.Server.GameObjects.Components.ActionBlocking eventArgs.User.PopupMessage(Loc.GetString("You start cuffing {0:theName}.", eventArgs.Target)); eventArgs.User.PopupMessage(eventArgs.Target, Loc.GetString("{0:theName} starts cuffing you!", eventArgs.User)); - _audioSystem.PlayFromEntity(StartCuffSound, Owner); + + if (StartCuffSound != null) + EntitySystem.Get().PlayFromEntity(StartCuffSound, Owner); TryUpdateCuff(eventArgs.User, eventArgs.Target, cuffed); return true; @@ -214,22 +213,21 @@ namespace Content.Server.GameObjects.Components.ActionBlocking NeedHand = true }; + _cuffing = true; + var result = await EntitySystem.Get().DoAfter(doAfterEventArgs); + _cuffing = false; + if (result != DoAfterStatus.Cancelled) { - _audioSystem.PlayFromEntity(EndCuffSound, Owner); - user.PopupMessage(Loc.GetString("You successfully cuff {0:theName}.", target)); - target.PopupMessage(Loc.GetString("You have been cuffed by {0:theName}!", user)); + if (cuffs.TryAddNewCuffs(user, Owner)) + { + if (EndCuffSound != null) + EntitySystem.Get().PlayFromEntity(EndCuffSound, Owner); - if (user.TryGetComponent(out var hands)) - { - hands.Drop(Owner); - cuffs.AddNewCuffs(Owner); - } - else - { - Logger.Warning("Unable to remove handcuffs from player's hands! This should not happen!"); + user.PopupMessage(Loc.GetString("You successfully cuff {0:theName}.", target)); + target.PopupMessage(Loc.GetString("You have been cuffed by {0:theName}!", user)); } } else diff --git a/Content.Shared/GameObjects/Components/ActionBlocking/SharedHandcuffComponent.cs b/Content.Shared/GameObjects/Components/ActionBlocking/SharedHandcuffComponent.cs index 6ab252ba2b..e55211987a 100644 --- a/Content.Shared/GameObjects/Components/ActionBlocking/SharedHandcuffComponent.cs +++ b/Content.Shared/GameObjects/Components/ActionBlocking/SharedHandcuffComponent.cs @@ -4,7 +4,7 @@ using Robust.Shared.Serialization; namespace Content.Shared.GameObjects.Components.ActionBlocking { - public class SharedHandcuffComponent : Component + public abstract class SharedHandcuffComponent : Component { public override string Name => "Handcuff"; public override uint? NetID => ContentNetIDs.HANDCUFFS; diff --git a/Resources/Prototypes/Alerts/alerts.yml b/Resources/Prototypes/Alerts/alerts.yml index 5be4593ab4..1c2450b7c8 100644 --- a/Resources/Prototypes/Alerts/alerts.yml +++ b/Resources/Prototypes/Alerts/alerts.yml @@ -89,9 +89,10 @@ - type: alert alertType: Handcuffed + onClick: !type:RemoveCuffs { } icon: /Textures/Interface/Alerts/Handcuffed/Handcuffed.png name: "[color=yellow]Handcuffed[/color]" - description: "You're [color=yellow]handcuffed[/color] and can't use your hands. If anyone drags you, you won't be able to resist.." + description: "You're [color=yellow]handcuffed[/color] and can't use your hands. If anyone drags you, you won't be able to resist." - type: alert alertType: Buckled From b3869985e07c76d0a1d1b2098b697e16a3b27134 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 6 Feb 2021 03:12:10 +1100 Subject: [PATCH 06/22] Increase disposal dragging delay for other people (#3086) Co-authored-by: Metal Gear Sloth --- .../Disposal/DisposalUnitComponent.cs | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs b/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs index d1612da8e5..e28183d4b3 100644 --- a/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs +++ b/Content.Server/GameObjects/Components/Disposal/DisposalUnitComponent.cs @@ -80,9 +80,18 @@ namespace Content.Server.GameObjects.Components.Disposal [ViewVariables(VVAccess.ReadWrite)] private TimeSpan _flushDelay; + /// + /// Delay from trying to enter disposals ourselves. + /// [ViewVariables(VVAccess.ReadWrite)] private float _entryDelay; + /// + /// Delay from trying to shove someone else into disposals. + /// + [ViewVariables(VVAccess.ReadWrite)] + private float _draggedEntryDelay; + /// /// Token used to cancel the automatic engage of a disposal unit /// after an entity enters it. @@ -192,11 +201,15 @@ namespace Content.Server.GameObjects.Components.Disposal if (!CanInsert(entity)) return false; - if (user != null && _entryDelay > 0f) + var delay = user == entity ? _entryDelay : _draggedEntryDelay; + + if (user != null && delay > 0.0f) { var doAfterSystem = EntitySystem.Get(); - var doAfterArgs = new DoAfterEventArgs(user, _entryDelay, default, Owner) + // Can't check if our target AND disposals moves currently so we'll just check target. + // if you really want to check if disposals moves then add a predicate. + var doAfterArgs = new DoAfterEventArgs(user, delay, default, entity) { BreakOnDamage = true, BreakOnStun = true, @@ -209,7 +222,6 @@ namespace Content.Server.GameObjects.Components.Disposal if (result == DoAfterStatus.Cancelled) return false; - } if (!_container.Insert(entity)) @@ -535,7 +547,8 @@ namespace Content.Server.GameObjects.Components.Disposal () => (int) _flushDelay.TotalSeconds); serializer.DataField(this, x => x.Air, "air", new GasMixture(Atmospherics.CellVolume)); - serializer.DataField(ref _entryDelay, "entryDelay", 0.5f); + serializer.DataField(ref _entryDelay, "entryDelay", 1.0f); + serializer.DataField(ref _draggedEntryDelay, "draggedEntryDelay", 3.0f); } public override void Initialize() From abde29ed5a796d43e69527eb92be46d6b5331d14 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sat, 6 Feb 2021 06:43:50 +1100 Subject: [PATCH 07/22] Remove ExplosivePassable collision layer (#3084) * Remove ExplosivePassable collision layer Now we have tags which can do this cleaner. * Update typo * Vera's review Co-authored-by: Metal Gear Sloth --- Content.Server/Explosions/ExplosionHelper.cs | 12 +++++++----- Content.Shared/Physics/CollisionGroup.cs | 2 +- .../Entities/Constructible/Walls/girder.yml | 4 +++- Resources/Prototypes/tags.yml | 2 ++ 4 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 Resources/Prototypes/tags.yml diff --git a/Content.Server/Explosions/ExplosionHelper.cs b/Content.Server/Explosions/ExplosionHelper.cs index 60962b67ec..aa5a97e111 100644 --- a/Content.Server/Explosions/ExplosionHelper.cs +++ b/Content.Server/Explosions/ExplosionHelper.cs @@ -1,8 +1,10 @@ +#nullable enable using System; using System.Collections.Generic; using System.Linq; using Content.Server.GameObjects.Components.Explosion; using Content.Server.GameObjects.Components.Mobs; +using Content.Shared.GameObjects.Components.Tag; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Maps; using Content.Shared.Physics; @@ -39,7 +41,7 @@ namespace Content.Server.Explosions private static readonly float LightBreakChance = 0.3f; private static readonly float HeavyBreakChance = 0.8f; - private static bool IgnoreExplosivePassable(IEntity e) => (e.GetComponent().CollisionLayer & (int) CollisionGroup.ExplosivePassable) != 0; + private static bool IgnoreExplosivePassable(IEntity e) => e.HasTag("ExplosivePassable"); private static ExplosionSeverity CalculateSeverity(float distance, float devastationRange, float heaveyRange) { @@ -96,7 +98,7 @@ namespace Content.Server.Explosions continue; } - if (!entity.TryGetComponent(out IPhysicsComponent body) || body.PhysicsShapes.Count < 1) + if (!entity.TryGetComponent(out IPhysicsComponent? body) || body.PhysicsShapes.Count < 1) { continue; } @@ -117,7 +119,7 @@ namespace Content.Server.Explosions // Impassable entities are handled first. If they are damaged enough, they are destroyed and they may // be able to spawn a new entity. I.e Wall -> Girder. - // Girder has a layer ExplosivePassable, and the predicate make it so the entities with this layer are ignored + // Girder has a tag ExplosivePassable, and the predicate make it so the entities with this tag are ignored var epicenterMapPos = epicenter.ToMap(entityManager); foreach (var (entity, distance) in impassableEntities) { @@ -228,7 +230,7 @@ namespace Content.Server.Explosions var players = playerManager.GetPlayersInRange(epicenter, (int) Math.Ceiling(maxRange)); foreach (var player in players) { - if (player.AttachedEntity == null || !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent recoil)) + if (player.AttachedEntity == null || !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent? recoil)) { continue; } @@ -279,7 +281,7 @@ namespace Content.Server.Explosions int lightImpactRange = 0, int flashRange = 0) { // If you want to directly set off the explosive - if (!entity.Deleted && entity.TryGetComponent(out ExplosiveComponent explosive) && !explosive.Exploding) + if (!entity.Deleted && entity.TryGetComponent(out ExplosiveComponent? explosive) && !explosive.Exploding) { explosive.Explosion(); } diff --git a/Content.Shared/Physics/CollisionGroup.cs b/Content.Shared/Physics/CollisionGroup.cs index 5b676e8432..1916e72dea 100644 --- a/Content.Shared/Physics/CollisionGroup.cs +++ b/Content.Shared/Physics/CollisionGroup.cs @@ -1,3 +1,4 @@ +#nullable enable using System; using JetBrains.Annotations; using Robust.Shared.Map; @@ -23,7 +24,6 @@ namespace Content.Shared.Physics GhostImpassable = 1 << 6, // 64 Things impassible by ghosts/observers, ie blessed tiles or forcefields Underplating = 1 << 7, // 128 Things that are under plating Passable = 1 << 8, // 256 Things that are passable - ExplosivePassable = 1 << 9, // 512 Things that let the pressure of a explosion through MapGrid = MapGridHelpers.CollisionGroup, // Map grids, like shuttles. This is the actual grid itself, not the walls or other entities connected to the grid. MobMask = Impassable | MobImpassable | VaultImpassable | SmallImpassable, diff --git a/Resources/Prototypes/Entities/Constructible/Walls/girder.yml b/Resources/Prototypes/Entities/Constructible/Walls/girder.yml index 8895c3a02d..c1b1756643 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/girder.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/girder.yml @@ -24,7 +24,9 @@ - MobImpassable - VaultImpassable - SmallImpassable - - ExplosivePassable + - type: Tag + tags: + - ExplosivePassable - type: Pullable - type: Damageable resistances: metallicResistances diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml new file mode 100644 index 0000000000..489d564aac --- /dev/null +++ b/Resources/Prototypes/tags.yml @@ -0,0 +1,2 @@ +- type: Tag + id: ExplosivePassable From b3810922f342504da997ad0f267e6ba20b664188 Mon Sep 17 00:00:00 2001 From: Vera Aguilera Puerto Date: Fri, 5 Feb 2021 22:44:55 +0100 Subject: [PATCH 08/22] Fix spawned computers not adding the needed container to the ConstructionComponent Fixes #3091 --- Content.Server/GameObjects/Components/ComputerComponent.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Content.Server/GameObjects/Components/ComputerComponent.cs b/Content.Server/GameObjects/Components/ComputerComponent.cs index f22a6fc094..9ab1995b1b 100644 --- a/Content.Server/GameObjects/Components/ComputerComponent.cs +++ b/Content.Server/GameObjects/Components/ComputerComponent.cs @@ -1,3 +1,4 @@ +using Content.Server.GameObjects.Components.Construction; using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Shared.GameObjects.Components; using JetBrains.Annotations; @@ -87,6 +88,9 @@ namespace Content.Server.GameObjects.Components if(!container.Insert(board)) Logger.Warning($"Couldn't insert board {board} to computer {Owner}!"); + + if (Owner.TryGetComponent(out ConstructionComponent construction)) + construction.AddContainer("board"); } public void MapInit() From 8ec4f1fb3dd8101314269f3bf2bcaaebaf262d6b Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sat, 6 Feb 2021 13:55:36 +0100 Subject: [PATCH 09/22] Fix missing ignored components on the client (#3101) --- Content.Client/IgnoredComponents.cs | 6 +++++- Content.Server/IgnoredComponents.cs | 7 ++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/Content.Client/IgnoredComponents.cs b/Content.Client/IgnoredComponents.cs index c176efa82d..3da41e9fbb 100644 --- a/Content.Client/IgnoredComponents.cs +++ b/Content.Client/IgnoredComponents.cs @@ -1,3 +1,4 @@ +// ReSharper disable ArrangeTrailingCommaInMultilineLists namespace Content.Client { public static class IgnoredComponents @@ -242,7 +243,10 @@ namespace Content.Client "SecretStash", "Toilet", "ClusterFlash", - "GasGenerator" + "GasGenerator", + "SolutionTransfer", + "Shovel", + "ReagentTank", }; } } diff --git a/Content.Server/IgnoredComponents.cs b/Content.Server/IgnoredComponents.cs index 2a06e313f4..9003823e8c 100644 --- a/Content.Server/IgnoredComponents.cs +++ b/Content.Server/IgnoredComponents.cs @@ -1,9 +1,8 @@ +// ReSharper disable ArrangeTrailingCommaInMultilineLists namespace Content.Server { - public static class IgnoredComponents { - public static string[] List => new [] { "ConstructionGhost", "IconSmooth", @@ -18,9 +17,7 @@ namespace Content.Server "Clickable", "RadiatingLight", "Icon", - "ClientEntitySpawner" + "ClientEntitySpawner", }; - } - } From 9db47249cd85578fc53e7a13be31c5df76ddf9cb Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sat, 6 Feb 2021 14:31:44 +0100 Subject: [PATCH 10/22] Fix being able to spill closed drink cans (#3100) --- .../Components/Nutrition/DrinkComponent.cs | 131 ++++++++++++------ 1 file changed, 91 insertions(+), 40 deletions(-) diff --git a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs index cd41d3f2c5..698bb6e0c8 100644 --- a/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs +++ b/Content.Server/GameObjects/Components/Nutrition/DrinkComponent.cs @@ -1,15 +1,17 @@ -using System.Linq; +#nullable enable +using System.Linq; using System.Threading.Tasks; using Content.Server.GameObjects.Components.Body.Behavior; -using Content.Server.GameObjects.Components.Chemistry; using Content.Server.GameObjects.Components.Fluids; using Content.Shared.Audio; using Content.Shared.Chemistry; using Content.Shared.GameObjects.Components.Body; +using Content.Shared.GameObjects.Components.Chemistry; using Content.Shared.GameObjects.Components.Nutrition; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Interfaces; using Content.Shared.Interfaces.GameObjects.Components; +using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Server.GameObjects.EntitySystems; using Robust.Shared.Audio; @@ -36,22 +38,39 @@ namespace Content.Server.GameObjects.Components.Nutrition public override string Name => "Drink"; [ViewVariables] - private SolutionContainerComponent _contents; + private bool _opened; + [ViewVariables] - private string _useSound; + private string _useSound = string.Empty; + [ViewVariables] private bool _defaultToOpened; - [ViewVariables(VVAccess.ReadWrite)] - public ReagentUnit TransferAmount { get; private set; } = ReagentUnit.New(2); - [ViewVariables(VVAccess.ReadWrite)] - public bool Opened { get; protected set; } - [ViewVariables] - public bool Empty => _contents.CurrentVolume.Float() <= 0; - private AppearanceComponent _appearanceComponent; - private string _soundCollection; + [ViewVariables(VVAccess.ReadWrite)] + public ReagentUnit TransferAmount { get; [UsedImplicitly] private set; } = ReagentUnit.New(2); + + [ViewVariables(VVAccess.ReadWrite)] + public bool Opened + { + get => _opened; + set + { + if (_opened == value) + { + return; + } + + _opened = value; + OpenedChanged(); + } + } + + [ViewVariables] + public bool Empty => Owner.GetComponentOrNull()?.DrainAvailable <= 0; + + private string _soundCollection = string.Empty; private bool _pressurized; - private string _burstSound; + private string _burstSound = string.Empty; public override void ExposeData(ObjectSerializer serializer) { @@ -66,18 +85,28 @@ namespace Content.Server.GameObjects.Components.Nutrition public override void Initialize() { base.Initialize(); - Owner.TryGetComponent(out _appearanceComponent); - if (!Owner.TryGetComponent(out _contents)) - { - _contents = Owner.AddComponent(); - } - - _contents.Capabilities = SolutionContainerCaps.Refillable | SolutionContainerCaps.Drainable; Opened = _defaultToOpened; UpdateAppearance(); } + private void OpenedChanged() + { + if (!Owner.TryGetComponent(out SharedSolutionContainerComponent? contents)) + { + return; + } + + if (Opened) + { + contents.Capabilities |= SolutionContainerCaps.Refillable | SolutionContainerCaps.Drainable; + } + else + { + contents.Capabilities &= ~(SolutionContainerCaps.Refillable | SolutionContainerCaps.Drainable); + } + } + void ISolutionChange.SolutionChanged(SolutionChangeEventArgs eventArgs) { UpdateAppearance(); @@ -85,7 +114,13 @@ namespace Content.Server.GameObjects.Components.Nutrition private void UpdateAppearance() { - _appearanceComponent?.SetData(SharedFoodComponent.FoodVisuals.Visual, _contents.CurrentVolume.Float()); + if (!Owner.TryGetComponent(out AppearanceComponent? appearance) || + !Owner.TryGetComponent(out ISolutionInteractionsComponent? contents)) + { + return; + } + + appearance.SetData(SharedFoodComponent.FoodVisuals.Visual, contents.DrainAvailable.Float()); } bool IUse.UseEntity(UseEntityEventArgs args) @@ -101,7 +136,8 @@ namespace Content.Server.GameObjects.Components.Nutrition return false; } - if (_contents.CurrentVolume.Float() <= 0) + if (!Owner.TryGetComponent(out ISolutionInteractionsComponent? contents) || + contents.DrainAvailable <= 0) { args.User.PopupMessage(Loc.GetString("{0:theName} is empty!", Owner)); return true; @@ -113,7 +149,13 @@ namespace Content.Server.GameObjects.Components.Nutrition //Force feeding a drink to someone. async Task IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs) { - TryUseDrink(eventArgs.Target, forced: true); + if (eventArgs.Target == null) + { + return false; + } + + TryUseDrink(eventArgs.Target, true); + return true; } @@ -130,18 +172,15 @@ namespace Content.Server.GameObjects.Components.Nutrition private bool TryUseDrink(IEntity target, bool forced = false) { - if (target == null || !_contents.CanDrain) - { - return false; - } - if (!Opened) { target.PopupMessage(Loc.GetString("Open {0:theName} first!", Owner)); return false; } - if (_contents.CurrentVolume.Float() <= 0) + if (!Owner.TryGetComponent(out ISolutionInteractionsComponent? interactions) || + !interactions.CanDrain || + interactions.DrainAvailable <= 0) { if (!forced) { @@ -151,26 +190,33 @@ namespace Content.Server.GameObjects.Components.Nutrition return false; } - if (!target.TryGetComponent(out IBody body) || + if (!target.TryGetComponent(out IBody? body) || !body.TryGetMechanismBehaviors(out var stomachs)) { + target.PopupMessage(Loc.GetString("You can't drink {0:theName}!", Owner)); return false; } - var transferAmount = ReagentUnit.Min(TransferAmount, _contents.CurrentVolume); - var split = _contents.SplitSolution(transferAmount); - var firstStomach = stomachs.FirstOrDefault(stomach => stomach.CanTransferSolution(split)); - + var transferAmount = ReagentUnit.Min(TransferAmount, interactions.DrainAvailable); + var drain = interactions.Drain(transferAmount); + var firstStomach = stomachs.FirstOrDefault(stomach => stomach.CanTransferSolution(drain)); // All stomach are full or can't handle whatever solution we have. if (firstStomach == null) { - _contents.TryAddSolution(split); target.PopupMessage(Loc.GetString("You've had enough {0:theName}!", Owner)); + + if (!interactions.CanRefill) + { + drain.SpillAt(target, "PuddleSmear"); + return false; + } + + interactions.Refill(drain); return false; } - if (_useSound != null) + if (!string.IsNullOrEmpty(_useSound)) { EntitySystem.Get().PlayFromEntity(_useSound, target, AudioParams.Default.WithVolume(-2f)); } @@ -180,9 +226,9 @@ namespace Content.Server.GameObjects.Components.Nutrition // TODO: Account for partial transfer. - split.DoEntityReaction(target, ReactionMethod.Ingestion); + drain.DoEntityReaction(target, ReactionMethod.Ingestion); - firstStomach.TryTransferSolution(split); + firstStomach.TryTransferSolution(drain); return true; } @@ -192,11 +238,16 @@ namespace Content.Server.GameObjects.Components.Nutrition if (_pressurized && !Opened && _random.Prob(0.25f) && - Owner.TryGetComponent(out SolutionContainerComponent component)) + Owner.TryGetComponent(out ISolutionInteractionsComponent? interactions)) { Opened = true; - var solution = component.SplitSolution(component.CurrentVolume); + if (!interactions.CanDrain) + { + return; + } + + var solution = interactions.Drain(interactions.DrainAvailable); solution.SpillAt(Owner, "PuddleSmear"); EntitySystem.Get().PlayFromEntity(_burstSound, Owner, From 427271628943bf9e11e6d0598e98b8136bfce844 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sat, 6 Feb 2021 14:32:48 +0100 Subject: [PATCH 11/22] Make integration tests use an empty map instead of saltern by default (#3094) --- .../ContentIntegrationTest.cs | 3 ++ .../Tests/Buckle/BuckleTest.cs | 51 +++++++----------- .../Components/Mobs/AlertsComponentTests.cs | 15 +++--- .../Tests/Gravity/WeightlessStatusTests.cs | 15 +----- Resources/Maps/Test/empty.yml | 54 +++++++++++++++++++ 5 files changed, 85 insertions(+), 53 deletions(-) create mode 100644 Resources/Maps/Test/empty.yml diff --git a/Content.IntegrationTests/ContentIntegrationTest.cs b/Content.IntegrationTests/ContentIntegrationTest.cs index 20b35baf4b..e298095260 100644 --- a/Content.IntegrationTests/ContentIntegrationTest.cs +++ b/Content.IntegrationTests/ContentIntegrationTest.cs @@ -80,6 +80,9 @@ namespace Content.IntegrationTests // Avoid funny race conditions with the database. options.CVarOverrides[CCVars.DatabaseSynchronous.Name] = "true"; + // Avoid loading a large map by default for integration tests. + options.CVarOverrides[CCVars.GameMap.Name] = "Maps/Test/empty.yml"; + return base.StartServer(options); } diff --git a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs index 2bf519db3f..f3be87a348 100644 --- a/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs +++ b/Content.IntegrationTests/Tests/Buckle/BuckleTest.cs @@ -6,7 +6,6 @@ using Content.Server.GameObjects.Components.Strap; using Content.Shared.GameObjects.Components.Body; using Content.Shared.GameObjects.Components.Body.Part; using Content.Shared.GameObjects.Components.Buckle; -using Content.Shared.GameObjects.EntitySystems; using Content.Shared.GameObjects.EntitySystems.ActionBlocker; using Content.Shared.GameObjects.EntitySystems.EffectBlocker; using Content.Shared.Utility; @@ -45,7 +44,7 @@ namespace Content.IntegrationTests.Tests.Buckle public async Task BuckleUnbuckleCooldownRangeTest() { var options = new ServerIntegrationOptions {ExtraPrototypes = PROTOTYPES}; - var server = StartServerDummyTicker(options); + var server = StartServer(options); IEntity human = null; IEntity chair = null; @@ -55,13 +54,14 @@ namespace Content.IntegrationTests.Tests.Buckle await server.WaitAssertion(() => { var mapManager = IoCManager.Resolve(); - - mapManager.CreateNewMapEntity(MapId.Nullspace); - var entityManager = IoCManager.Resolve(); - human = entityManager.SpawnEntity("BuckleDummy", MapCoordinates.Nullspace); - chair = entityManager.SpawnEntity("StrapDummy", MapCoordinates.Nullspace); + var gridId = new GridId(1); + var grid = mapManager.GetGrid(gridId); + var coordinates = grid.GridEntityId.ToCoordinates(); + + human = entityManager.SpawnEntity("BuckleDummy", coordinates); + chair = entityManager.SpawnEntity("StrapDummy", coordinates); // Default state, unbuckled Assert.True(human.TryGetComponent(out buckle)); @@ -197,31 +197,24 @@ namespace Content.IntegrationTests.Tests.Buckle var options = new ServerIntegrationOptions {ExtraPrototypes = PROTOTYPES}; var server = StartServer(options); - IEntity human; - IEntity chair; + IEntity human = null; BuckleComponent buckle = null; HandsComponent hands = null; IBody body = null; + await server.WaitIdleAsync(); + await server.WaitAssertion(() => { var mapManager = IoCManager.Resolve(); - - var mapId = new MapId(1); - mapManager.CreateNewMapEntity(mapId); - var entityManager = IoCManager.Resolve(); - var gridId = new GridId(1); - var grid = mapManager.CreateGrid(mapId, gridId); - var coordinates = grid.GridEntityId.ToCoordinates(); - var tileManager = IoCManager.Resolve(); - var tileId = tileManager["underplating"].TileId; - var tile = new Tile(tileId); - grid.SetTile(coordinates, tile); + var gridId = new GridId(1); + var grid = mapManager.GetGrid(gridId); + var coordinates = grid.GridEntityId.ToCoordinates(); human = entityManager.SpawnEntity("BuckleDummy", coordinates); - chair = entityManager.SpawnEntity("StrapDummy", coordinates); + IEntity chair = entityManager.SpawnEntity("StrapDummy", coordinates); // Component sanity check Assert.True(human.TryGetComponent(out buckle)); @@ -279,6 +272,8 @@ namespace Content.IntegrationTests.Tests.Buckle { Assert.Null(hands.GetItem(slot)); } + + buckle.TryUnbuckle(human, true); }); } @@ -295,19 +290,11 @@ namespace Content.IntegrationTests.Tests.Buckle await server.WaitAssertion(() => { var mapManager = IoCManager.Resolve(); - - var mapId = new MapId(1); - mapManager.CreateNewMapEntity(mapId); - var entityManager = IoCManager.Resolve(); - var gridId = new GridId(1); - var grid = mapManager.CreateGrid(mapId, gridId); - var coordinates = grid.GridEntityId.ToCoordinates(); - var tileManager = IoCManager.Resolve(); - var tileId = tileManager["underplating"].TileId; - var tile = new Tile(tileId); - grid.SetTile(coordinates, tile); + var gridId = new GridId(1); + var grid = mapManager.GetGrid(gridId); + var coordinates = grid.GridEntityId.ToCoordinates(); human = entityManager.SpawnEntity("BuckleDummy", coordinates); chair = entityManager.SpawnEntity("StrapDummy", coordinates); diff --git a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs index 073592d783..e2d2abaf94 100644 --- a/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs +++ b/Content.IntegrationTests/Tests/GameObjects/Components/Mobs/AlertsComponentTests.cs @@ -16,7 +16,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs [TestOf(typeof(ServerAlertsComponent))] public class AlertsComponentTests : ContentIntegrationTest { - [Test] public async Task AlertsTest() { @@ -61,11 +60,11 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs Assert.NotNull(alertsUI); // we should be seeing 3 alerts - our health, and the 2 debug alerts, in a specific order. - Assert.That(alertsUI.Grid.ChildCount, Is.EqualTo(3)); - var alertControls = alertsUI.Grid.Children.Select(c => c as AlertControl); + Assert.That(alertsUI.Grid.ChildCount, Is.GreaterThanOrEqualTo(3)); + var alertControls = alertsUI.Grid.Children.Select(c => (AlertControl) c); var alertIDs = alertControls.Select(ac => ac.Alert.AlertType).ToArray(); var expectedIDs = new [] {AlertType.HumanHealth, AlertType.Debug1, AlertType.Debug2}; - Assert.That(alertIDs, Is.EqualTo(expectedIDs)); + Assert.That(alertIDs, Is.SupersetOf(expectedIDs)); }); await server.WaitAssertion(() => @@ -96,12 +95,12 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.Mobs clientUIMgr.StateRoot.Children.FirstOrDefault(c => c is AlertsUI) as AlertsUI; Assert.NotNull(alertsUI); - // we should be seeing only 2 alerts now because one was cleared - Assert.That(alertsUI.Grid.ChildCount, Is.EqualTo(2)); - var alertControls = alertsUI.Grid.Children.Select(c => c as AlertControl); + // we should be seeing 2 alerts now because one was cleared + Assert.That(alertsUI.Grid.ChildCount, Is.GreaterThanOrEqualTo(2)); + var alertControls = alertsUI.Grid.Children.Select(c => (AlertControl) c); var alertIDs = alertControls.Select(ac => ac.Alert.AlertType).ToArray(); var expectedIDs = new [] {AlertType.HumanHealth, AlertType.Debug2}; - Assert.That(alertIDs, Is.EqualTo(expectedIDs)); + Assert.That(alertIDs, Is.SupersetOf(expectedIDs)); }); } } diff --git a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs index c6ea3464a2..bdb654d364 100644 --- a/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs +++ b/Content.IntegrationTests/Tests/Gravity/WeightlessStatusTests.cs @@ -2,7 +2,6 @@ using Content.Server.GameObjects.Components.Gravity; using Content.Server.GameObjects.EntitySystems; using Content.Shared.Alert; -using Content.Shared.GameObjects.Components.Gravity; using Content.Shared.GameObjects.Components.Mobs; using Content.Shared.GameObjects.EntitySystems; using Content.Shared.Utility; @@ -45,10 +44,7 @@ namespace Content.IntegrationTests.Tests.Gravity await server.WaitAssertion(() => { - var mapId = mapManager.CreateMap(); - - pauseManager.AddUninitializedMap(mapId); - + var mapId = new MapId(1); var gridId = new GridId(1); if (!mapManager.TryGetGrid(gridId, out var grid)) @@ -56,14 +52,7 @@ namespace Content.IntegrationTests.Tests.Gravity grid = mapManager.CreateGrid(mapId, gridId); } - var tileDefinition = tileDefinitionManager["underplating"]; - var tile = new Tile(tileDefinition.TileId); var coordinates = grid.ToCoordinates(); - - grid.SetTile(coordinates, tile); - - pauseManager.DoMapInitialize(mapId); - human = entityManager.SpawnEntity("HumanDummy", coordinates); Assert.True(human.TryGetComponent(out alerts)); @@ -98,7 +87,7 @@ namespace Content.IntegrationTests.Tests.Gravity await server.WaitAssertion(() => { - Assert.False(alerts.IsShowingAlert(AlertType.Weightless)); + Assert.True(alerts.IsShowingAlert(AlertType.Weightless)); }); } } diff --git a/Resources/Maps/Test/empty.yml b/Resources/Maps/Test/empty.yml new file mode 100644 index 0000000000..2309b1be44 --- /dev/null +++ b/Resources/Maps/Test/empty.yml @@ -0,0 +1,54 @@ +meta: + format: 2 + name: DemoStation + author: Space-Wizards + postmapinit: false +tilemap: + 0: space + 1: floor_asteroid_coarse_sand0 + 2: floor_asteroid_coarse_sand1 + 3: floor_asteroid_coarse_sand2 + 4: floor_asteroid_coarse_sand_dug + 5: floor_asteroid_sand + 6: floor_asteroid_tile + 7: floor_dark + 8: floor_elevator_shaft + 9: floor_freezer + 10: floor_gold + 11: floor_green_circuit + 12: floor_hydro + 13: floor_lino + 14: floor_mono + 15: floor_reinforced + 16: floor_rock_vault + 17: floor_showroom + 18: floor_snow + 19: floor_steel + 20: floor_steel_dirty + 21: floor_techmaint + 22: floor_white + 23: floor_wood + 24: lattice + 25: plating + 26: underplating +grids: +- settings: + chunksize: 16 + tilesize: 1 + snapsize: 1 + chunks: + - ind: "-1,-1" + tiles: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEwAAAA== +entities: +- uid: 0 + components: + - parent: null + pos: 0,0 + type: Transform + - index: 0 + type: MapGrid + - shapes: + - !type:PhysShapeGrid + grid: 0 + type: Physics +... From 5cbb3f1222c1d85f3f07fae2ccd57c72028c7cb5 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sat, 6 Feb 2021 17:39:53 +0100 Subject: [PATCH 12/22] Fix UI layout bugs with the PA. --- .../ParticleAcceleratorControlMenu.cs | 84 ++++++++++--------- 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/Content.Client/UserInterface/ParticleAccelerator/ParticleAcceleratorControlMenu.cs b/Content.Client/UserInterface/ParticleAccelerator/ParticleAcceleratorControlMenu.cs index bd3703dc19..46cc0066fd 100644 --- a/Content.Client/UserInterface/ParticleAccelerator/ParticleAcceleratorControlMenu.cs +++ b/Content.Client/UserInterface/ParticleAccelerator/ParticleAcceleratorControlMenu.cs @@ -188,11 +188,11 @@ namespace Content.Client.ParticleAccelerator new MarginContainer { MarginLeftOverride = 4, + SizeFlagsHorizontal = SizeFlags.FillExpand, Children = { new VBoxContainer { - SizeFlagsHorizontal = SizeFlags.FillExpand, Children = { new HBoxContainer @@ -246,55 +246,61 @@ namespace Content.Client.ParticleAccelerator } } }, - new VBoxContainer + new MarginContainer { - SizeFlagsHorizontal = SizeFlags.FillExpand, + CustomMinimumSize = (186, 0), Children = { - (_statusLabel = new Label + new VBoxContainer { - SizeFlagsHorizontal = SizeFlags.ShrinkCenter - }), - new Control - { - CustomMinimumSize = (0, 20) - }, - new PanelContainer - { - SizeFlagsHorizontal = SizeFlags.ShrinkCenter, - PanelOverride = back2, Children = { - new GridContainer + (_statusLabel = new Label { - Columns = 3, - VSeparationOverride = 0, - HSeparationOverride = 0, + SizeFlagsHorizontal = SizeFlags.ShrinkCenter + }), + new Control + { + CustomMinimumSize = (0, 20) + }, + new PanelContainer + { + SizeFlagsHorizontal = SizeFlags.ShrinkCenter, + PanelOverride = back2, Children = { - new Control {CustomMinimumSize = imgSize}, - (_endCapTexture = Segment("end_cap")), - new Control {CustomMinimumSize = imgSize}, - (_controlBoxTexture = Segment("control_box")), - (_fuelChamberTexture = Segment("fuel_chamber")), - new Control {CustomMinimumSize = imgSize}, - new Control {CustomMinimumSize = imgSize}, - (_powerBoxTexture = Segment("power_box")), - new Control {CustomMinimumSize = imgSize}, - (_emitterLeftTexture = Segment("emitter_left")), - (_emitterCenterTexture = Segment("emitter_center")), - (_emitterRightTexture = Segment("emitter_right")), + new GridContainer + { + Columns = 3, + VSeparationOverride = 0, + HSeparationOverride = 0, + Children = + { + new Control {CustomMinimumSize = imgSize}, + (_endCapTexture = Segment("end_cap")), + new Control {CustomMinimumSize = imgSize}, + (_controlBoxTexture = Segment("control_box")), + (_fuelChamberTexture = Segment("fuel_chamber")), + new Control {CustomMinimumSize = imgSize}, + new Control {CustomMinimumSize = imgSize}, + (_powerBoxTexture = Segment("power_box")), + new Control {CustomMinimumSize = imgSize}, + (_emitterLeftTexture = Segment("emitter_left")), + (_emitterCenterTexture = Segment("emitter_center")), + (_emitterRightTexture = Segment("emitter_right")), + } + } } - } + }, + (_scanButton = new Button + { + Text = Loc.GetString("Scan Parts"), + SizeFlagsHorizontal = SizeFlags.ShrinkCenter + }) } - }, - (_scanButton = new Button - { - Text = Loc.GetString("Scan Parts"), - SizeFlagsHorizontal = SizeFlags.ShrinkCenter - }) + } } - } + }, } }, new StripeBack @@ -507,7 +513,7 @@ namespace Content.Client.ParticleAccelerator public void SetPowerState(ParticleAcceleratorUIState state, bool exists) { _base.ShaderOverride = exists ? null : _menu._greyScaleShader; - _base.ModulateSelfOverride = exists ? (Color?)null : new Color(127, 127, 127); + _base.ModulateSelfOverride = exists ? (Color?) null : new Color(127, 127, 127); if (!state.Enabled || !exists) { From 2f01d7899f109e286c24cf541d6da4da0a6fa5b3 Mon Sep 17 00:00:00 2001 From: 20kdc Date: Sat, 6 Feb 2021 22:56:40 +0000 Subject: [PATCH 13/22] Mapping merge driver: continued (#2803) Co-authored-by: DrSmugleaf Co-authored-by: Pieter-Jan Briers --- .gitattributes | 1 + .gitignore | 4 + Content.Tools/Content.Tools.csproj | 16 ++ Content.Tools/Map.cs | 83 +++++++ Content.Tools/MappingMergeDriver.cs | 38 +++ Content.Tools/Merger.cs | 356 ++++++++++++++++++++++++++++ Content.Tools/TypeTagPreserver.cs | 25 ++ Content.Tools/YamlTools.cs | 199 ++++++++++++++++ Content.Tools/test/0A.yml | 95 ++++++++ Content.Tools/test/0B.yml | 86 +++++++ Content.Tools/test/0C.yml | 105 ++++++++ Content.Tools/test/run.sh | 3 + SpaceStation14.sln | 6 + Tools/mapping-merge-driver.sh | 9 + 14 files changed, 1026 insertions(+) create mode 100644 Content.Tools/Content.Tools.csproj create mode 100644 Content.Tools/Map.cs create mode 100644 Content.Tools/MappingMergeDriver.cs create mode 100644 Content.Tools/Merger.cs create mode 100644 Content.Tools/TypeTagPreserver.cs create mode 100644 Content.Tools/YamlTools.cs create mode 100644 Content.Tools/test/0A.yml create mode 100644 Content.Tools/test/0B.yml create mode 100644 Content.Tools/test/0C.yml create mode 100755 Content.Tools/test/run.sh create mode 100755 Tools/mapping-merge-driver.sh diff --git a/.gitattributes b/.gitattributes index 0630300756..6292d191fa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -34,6 +34,7 @@ #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary +Resources/Maps/**.yml merge=mapping-merge-driver ############################################################################### # behavior for image files diff --git a/.gitignore b/.gitignore index 05b654564f..98164b71c9 100644 --- a/.gitignore +++ b/.gitignore @@ -284,3 +284,7 @@ BuildFiles/Windows/Godot/* # Windows image file caches Thumbs.db ehthumbs.db + +# Merge driver stuff +Content.Tools/test/out.yml + diff --git a/Content.Tools/Content.Tools.csproj b/Content.Tools/Content.Tools.csproj new file mode 100644 index 0000000000..e3a5e3bca9 --- /dev/null +++ b/Content.Tools/Content.Tools.csproj @@ -0,0 +1,16 @@ + + + + Exe + net5.0 + + + + + + + + + + + diff --git a/Content.Tools/Map.cs b/Content.Tools/Map.cs new file mode 100644 index 0000000000..6e6ed59297 --- /dev/null +++ b/Content.Tools/Map.cs @@ -0,0 +1,83 @@ +using System.IO; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using YamlDotNet.Core; +using Robust.Shared.Utility; +using YamlDotNet.RepresentationModel; + +namespace Content.Tools +{ + public class Map + { + public Map(string path) + { + Path = path; + + using var reader = new StreamReader(path); + var stream = new YamlStream(); + + stream.Load(reader); + + Root = stream.Documents[0].RootNode; + TilemapNode = (YamlMappingNode) Root["tilemap"]; + GridsNode = (YamlSequenceNode) Root["grids"]; + _entitiesNode = (YamlSequenceNode) Root["entities"]; + + foreach (var entity in _entitiesNode) + { + var uid = uint.Parse(entity["uid"].AsString()); + if (uid >= NextAvailableEntityId) + NextAvailableEntityId = uid + 1; + Entities[uid] = (YamlMappingNode) entity; + } + } + + // Core + + public string Path { get; } + + public YamlNode Root { get; } + + // Useful + + public YamlMappingNode TilemapNode { get; } + + public YamlSequenceNode GridsNode { get; } + + // Entities lookup + + private YamlSequenceNode _entitiesNode { get; } + + public Dictionary Entities { get; } = new Dictionary(); + + public uint MaxId => Entities.Max(entry => entry.Key); + + public uint NextAvailableEntityId { get; set; } + + // ---- + + public void Save(string fileName) + { + // Update entities node + _entitiesNode.Children.Clear(); + foreach (var kvp in Entities) + _entitiesNode.Add(kvp.Value); + + using var writer = new StreamWriter(fileName); + var document = new YamlDocument(Root); + var stream = new YamlStream(document); + var emitter = new Emitter(writer); + var fixer = new TypeTagPreserver(emitter); + + stream.Save(fixer, false); + + writer.Flush(); + } + + public void Save() + { + Save(Path); + } + } +} diff --git a/Content.Tools/MappingMergeDriver.cs b/Content.Tools/MappingMergeDriver.cs new file mode 100644 index 0000000000..f069a7a5bb --- /dev/null +++ b/Content.Tools/MappingMergeDriver.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using YamlDotNet.Core; +using YamlDotNet.RepresentationModel; + +namespace Content.Tools +{ + internal static class MappingMergeDriver + { + /// %A: Our file + /// %O: Origin (common, base) file + /// %B: Other file + /// %P: Actual filename of the resulting file + public static void Main(string[] args) + { + var ours = new Map(args[0]); + var based = new Map(args[1]); // On what? + var other = new Map(args[2]); + + if ((ours.GridsNode.Children.Count != 1) || (based.GridsNode.Children.Count != 1) || (other.GridsNode.Children.Count != 1)) + { + Console.WriteLine("one or more files had an amount of grids not equal to 1"); + Environment.Exit(1); + } + + if (!(new Merger(ours, based, other).Merge())) + { + Console.WriteLine("unable to merge!"); + Environment.Exit(1); + } + + ours.Save(); + Environment.Exit(0); + } + } +} diff --git a/Content.Tools/Merger.cs b/Content.Tools/Merger.cs new file mode 100644 index 0000000000..1b5aaa4fe3 --- /dev/null +++ b/Content.Tools/Merger.cs @@ -0,0 +1,356 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using YamlDotNet.Core; +using YamlDotNet.RepresentationModel; + +namespace Content.Tools +{ + public class Merger + { + public Map MapOurs { get; } + public Map MapBased { get; } + public Map MapOther { get; } + + public Dictionary TileMapFromOtherToOurs { get; } = new Dictionary(); + public Dictionary TileMapFromBasedToOurs { get; } = new Dictionary(); + public Dictionary EntityMapFromOtherToOurs { get; } = new Dictionary(); + public List EntityListDirectMerge { get; } = new List(); + + private const int ExpectedChunkSize = 16 * 16 * 4; + + public Merger(Map ours, Map based, Map other) + { + MapOurs = ours; + MapBased = based; + MapOther = other; + } + + public bool Merge() + { + PlanTileMapping(TileMapFromOtherToOurs, MapOther); + PlanTileMapping(TileMapFromBasedToOurs, MapBased); + MergeTiles(); + PlanEntityMapping(); + return MergeEntities(); + } + + // -- Tiles -- + + public void PlanTileMapping(Dictionary relativeOtherToOurs, Map relativeOther) + { + var mapping = new Dictionary(); + uint nextAvailable = 0; + foreach (var kvp in MapOurs.TilemapNode) + { + var k = uint.Parse(kvp.Key.ToString()); + var v = kvp.Value.ToString(); + mapping[v] = k; + if (k >= nextAvailable) + nextAvailable = k + 1; + } + foreach (var kvp in relativeOther.TilemapNode) + { + var k = uint.Parse(kvp.Key.ToString()); + var v = kvp.Value.ToString(); + if (mapping.ContainsKey(v)) + { + relativeOtherToOurs[k] = mapping[v]; + } + else + { + MapOurs.TilemapNode.Add(nextAvailable.ToString(CultureInfo.InvariantCulture), v); + relativeOtherToOurs[k] = nextAvailable++; + } + } + } + + public void MergeTiles() + { + var a = MapOurs.GridsNode.Children[0]; + var b = MapBased.GridsNode.Children[0]; + var c = MapOther.GridsNode.Children[0]; + var aChunks = a["chunks"]; + var bChunks = b["chunks"]; + var cChunks = c["chunks"]; + MergeTileChunks((YamlSequenceNode) aChunks, (YamlSequenceNode) bChunks, (YamlSequenceNode) cChunks); + } + + public void MergeTileChunks(YamlSequenceNode aChunks, YamlSequenceNode bChunks, YamlSequenceNode cChunks) + { + var aMap = ConvertTileChunks(aChunks); + var bMap = ConvertTileChunks(bChunks); + var cMap = ConvertTileChunks(cChunks); + + var xMap = new HashSet(); + foreach (var kvp in aMap) + xMap.Add(kvp.Key); + // don't include b because that would mess with chunk deletion + foreach (var kvp in cMap) + xMap.Add(kvp.Key); + + foreach (var ind in xMap) + { + using var a = new MemoryStream(GetChunkBytes(aMap, ind)); + using var b = new MemoryStream(GetChunkBytes(bMap, ind)); + using var c = new MemoryStream(GetChunkBytes(cMap, ind)); + using var aR = new BinaryReader(a); + using var bR = new BinaryReader(b); + using var cR = new BinaryReader(c); + + var outB = new byte[ExpectedChunkSize]; + + { + using (var outS = new MemoryStream(outB)) + using (var outW = new BinaryWriter(outS)) + + for (var i = 0; i < ExpectedChunkSize; i += 4) + { + var aI = aR.ReadUInt32(); + var bI = MapTileId(bR.ReadUInt32(), TileMapFromBasedToOurs); + var cI = MapTileId(cR.ReadUInt32(), TileMapFromOtherToOurs); + // cI needs translation. + + uint result = aI; + if (aI == bI) + { + // If aI == bI then aI did not change anything, so cI always wins + result = cI; + } + else if (bI != cI) + { + // If bI != cI then cI definitely changed something (conflict, but overrides aI) + result = cI; + Console.WriteLine("WARNING: Tile (" + ind + ")[" + i + "] was changed by both branches."); + } + outW.Write(result); + } + } + + // Actually output chunk + if (!aMap.ContainsKey(ind)) + { + var res = new YamlMappingNode(); + res.Children["ind"] = ind; + aMap[ind] = res; + } + aMap[ind].Children["tiles"] = Convert.ToBase64String(outB); + } + } + + public uint MapTileId(uint src, Dictionary mapping) + { + return (src & 0xFFFF0000) | mapping[src & 0xFFFF]; + } + + public Dictionary ConvertTileChunks(YamlSequenceNode chunks) + { + var map = new Dictionary(); + foreach (var chunk in chunks) + map[chunk["ind"].ToString()] = (YamlMappingNode) chunk; + return map; + } + + public byte[] GetChunkBytes(Dictionary chunks, string ind) + { + if (!chunks.ContainsKey(ind)) + return new byte[ExpectedChunkSize]; + return Convert.FromBase64String(chunks[ind]["tiles"].ToString()); + } + + // -- Entities -- + + public void PlanEntityMapping() + { + // Ok, so here's how it works: + // 1. Entities that do not exist in "based" are additions. + // 2. Entities that exist in "based" but do not exist in the one map or the other are removals. + + // Find modifications and deletions + foreach (var kvp in MapBased.Entities) + { + var deletedByOurs = !MapOurs.Entities.ContainsKey(kvp.Key); + var deletedByOther = !MapOther.Entities.ContainsKey(kvp.Key); + if (deletedByOther && !deletedByOurs) + { + // Delete + MapOurs.Entities.Remove(kvp.Key); + } + else if (!(deletedByOurs || deletedByOther)) + { + // Modify + EntityMapFromOtherToOurs[kvp.Key] = kvp.Key; + } + } + + // Find additions + foreach (var kvp in MapOther.Entities) + { + if (!MapBased.Entities.ContainsKey(kvp.Key)) + { + // New + var newId = MapOurs.NextAvailableEntityId++; + EntityMapFromOtherToOurs[kvp.Key] = newId; + } + } + } + + public bool MergeEntities() + { + bool success = true; + foreach (var kvp in EntityMapFromOtherToOurs) + { + // For debug use. + // Console.WriteLine("Entity C/" + kvp.Key + " -> A/" + kvp.Value); + YamlMappingNode oursEnt; + if (MapOurs.Entities.ContainsKey(kvp.Value)) + { + oursEnt = MapOurs.Entities[kvp.Value]; + if (!MapBased.Entities.TryGetValue(kvp.Value, out var basedEnt)) + { + basedEnt = oursEnt; + } + + if (!MergeEntityNodes(oursEnt, basedEnt, MapOther.Entities[kvp.Key])) + { + Console.WriteLine("Unable to successfully merge entity C/" + kvp.Key); + success = false; + } + } + else + { + oursEnt = (YamlMappingNode) YamlTools.CopyYamlNodes(MapOther.Entities[kvp.Key]); + if (!MapEntity(oursEnt)) { + Console.WriteLine("Unable to successfully import entity C/" + kvp.Key); + success = false; + } else { + MapOurs.Entities[kvp.Value] = oursEnt; + } + } + oursEnt.Children["uid"] = kvp.Value.ToString(CultureInfo.InvariantCulture); + } + return success; + } + + public bool MergeEntityNodes(YamlMappingNode ours, YamlMappingNode based, YamlMappingNode other) + { + // Copy to intermediate + var otherMapped = (YamlMappingNode) YamlTools.CopyYamlNodes(other); + if (!MapEntity(otherMapped)) + return false; + // Merge stuff that isn't components + var path = "Entity" + (other["uid"].ToString()); + YamlTools.MergeYamlMappings(ours, based, otherMapped, path, new string[] {"components"}); + // Components are special + var ourComponents = new Dictionary(); + var basedComponents = new Dictionary(); + var ourComponentsNode = (YamlSequenceNode) ours["components"]; + var basedComponentsNode = (YamlSequenceNode) based["components"]; + var otherComponentsNode = (YamlSequenceNode) otherMapped["components"]; + foreach (var component in ourComponentsNode) + { + var name = component["type"].ToString(); + ourComponents[name] = (YamlMappingNode) component; + } + foreach (var component in basedComponentsNode) + { + var name = component["type"].ToString(); + basedComponents[name] = (YamlMappingNode) component; + } + foreach (var otherComponent in otherComponentsNode) + { + var name = otherComponent["type"].ToString(); + if (ourComponents.ContainsKey(name)) + { + var ourComponent = ourComponents[name]; + if (!basedComponents.TryGetValue(name, out var basedComponent)) + basedComponent = new YamlMappingNode(); + + YamlTools.MergeYamlNodes(ourComponent, basedComponent, otherComponent, path + "/components/" + name); + } + else + { + ourComponentsNode.Add(otherComponent); + } + } + return true; + } + + public bool MapEntity(YamlMappingNode other) + { + var path = "Entity" + (other["uid"].ToString()); + if (other.Children.ContainsKey("components")) + { + var components = (YamlSequenceNode) other["components"]; + foreach (var component in components) + { + var type = component["type"].ToString(); + if (type == "Transform") + { + if (!MapEntityProperty((YamlMappingNode) component, "parent", path)) + return false; + } + else if (type == "ContainerContainer") + { + MapEntityRecursiveAndBadly(component, path); + } + } + } + return true; + } + + public bool MapEntityProperty(YamlMappingNode node, string property, string path) + { + if (node.Children.ContainsKey(property)) { + var prop = node[property]; + if (prop is YamlScalarNode) + return MapEntityProperty((YamlScalarNode) prop, path + "/" + property); + } + return true; + } + + public bool MapEntityProperty(YamlScalarNode node, string path) + { + if (uint.TryParse(node.ToString(), out var uid)) + { + if (EntityMapFromOtherToOurs.ContainsKey(uid)) + { + node.Value = EntityMapFromOtherToOurs[uid].ToString(CultureInfo.InvariantCulture); + } + else + { + Console.WriteLine($"Error finding UID in MapEntityRecursiveAndBadly {path}. To fix this, the merge driver needs to be improved."); + return false; + } + } + return true; + } + + public bool MapEntityRecursiveAndBadly(YamlNode node, string path) + { + switch (node) + { + case YamlSequenceNode subSequence: + var idx = 0; + foreach (var val in subSequence) + if (!MapEntityRecursiveAndBadly(val, path + "/" + (idx++))) + return false; + return true; + case YamlMappingNode subMapping: + foreach (var kvp in subMapping) + if (!MapEntityRecursiveAndBadly(kvp.Key, path)) + return false; + foreach (var kvp in subMapping) + if (!MapEntityRecursiveAndBadly(kvp.Value, path + "/" + kvp.Key.ToString())) + return false; + return true; + case YamlScalarNode subScalar: + return MapEntityProperty(subScalar, path); + default: + throw new ArgumentException($"Unrecognized YAML node type: {node.GetType()} at {path}"); + } + } + } +} diff --git a/Content.Tools/TypeTagPreserver.cs b/Content.Tools/TypeTagPreserver.cs new file mode 100644 index 0000000000..94996767d3 --- /dev/null +++ b/Content.Tools/TypeTagPreserver.cs @@ -0,0 +1,25 @@ +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace Content.Tools +{ + public class TypeTagPreserver : IEmitter + { + public TypeTagPreserver(IEmitter emitter) + { + Emitter = emitter; + } + + private IEmitter Emitter { get; } + + public void Emit(ParsingEvent @event) + { + if (@event is MappingStart mapping) + { + @event = new MappingStart(mapping.Anchor, mapping.Tag, false, mapping.Style, mapping.Start, mapping.End); + } + + Emitter.Emit(@event); + } + } +} diff --git a/Content.Tools/YamlTools.cs b/Content.Tools/YamlTools.cs new file mode 100644 index 0000000000..ec1f432284 --- /dev/null +++ b/Content.Tools/YamlTools.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using YamlDotNet.Core; +using YamlDotNet.RepresentationModel; + +namespace Content.Tools +{ + public static class YamlTools + { + public static YamlNode CopyYamlNodes(YamlNode other) + { + switch (other) + { + case YamlSequenceNode subSequence: + YamlSequenceNode tmp1 = new YamlSequenceNode(); + MergeYamlSequences((YamlSequenceNode) tmp1, new YamlSequenceNode(), (YamlSequenceNode) other, ""); + return tmp1; + case YamlMappingNode subMapping: + YamlMappingNode tmp2 = new YamlMappingNode(); + MergeYamlMappings((YamlMappingNode) tmp2, new YamlMappingNode(), (YamlMappingNode) other, "", new string[] {}); + return tmp2; + case YamlScalarNode subScalar: + YamlScalarNode tmp3 = new YamlScalarNode(); + CopyYamlScalar(tmp3, subScalar); + return tmp3; + default: + throw new ArgumentException($"Unrecognized YAML node type for copy: {other.GetType()}", nameof(other)); + } + } + + public static bool TriTypeMatch(YamlNode ours, YamlNode based, YamlNode other) + { + var refType = other.GetType(); + if (refType != based.GetType()) + return false; + if (refType != ours.GetType()) + return false; + return true; + } + + public static void MergeYamlNodes(YamlNode ours, YamlNode based, YamlNode other, string path) + { + if (!TriTypeMatch(ours, based, other)) + throw new ArgumentException($"Node type mismatch at {path}"); + switch (other) + { + case YamlSequenceNode subSequence: + MergeYamlSequences((YamlSequenceNode) ours, (YamlSequenceNode) based, (YamlSequenceNode) other, path); + break; + case YamlMappingNode subMapping: + MergeYamlMappings((YamlMappingNode) ours, (YamlMappingNode) based, (YamlMappingNode) other, path, new string[] {}); + break; + case YamlScalarNode subScalar: + // Console.WriteLine(path + " - " + ours + " || " + based + " || " + other); + var scalarA = (YamlScalarNode) ours; + var scalarB = (YamlScalarNode) based; + var scalarC = (YamlScalarNode) other; + var aeb = (scalarA.Value == scalarB.Value); + var cneb = (scalarC.Value != scalarB.Value); + if (aeb || cneb) + CopyYamlScalar(scalarA, scalarC); + // Console.WriteLine(path + " . " + ours + " || " + based + " || " + other); + break; + default: + throw new ArgumentException($"Unrecognized YAML node type at {path}: {other.GetType()}", nameof(other)); + } + } + + public static void MergeYamlSequences(YamlSequenceNode ours, YamlSequenceNode based, YamlSequenceNode other, string path) + { + if ((ours.Children.Count == based.Children.Count) && (other.Children.Count == ours.Children.Count)) + { + // this is terrible and doesn't do proper rearrange detection + // but it looks as if vectors might be arrays + // so rearrange detection might break more stuff... + // nope, they aren't, but still good to have + for (var i = 0; i < ours.Children.Count; i++) + MergeYamlNodes(ours.Children[i], based.Children[i], other.Children[i], path + "/" + i); + return; + } + // for now, just copy other -> ours + // I am aware this is terrible + ours.Children.Clear(); + foreach (var c in other.Children) + ours.Add(CopyYamlNodes(c)); + } + + public static void MergeYamlMappings(YamlMappingNode ours, YamlMappingNode based, YamlMappingNode other, string path, string[] ignoreThese) + { + // Deletions/modifications + foreach (var kvp in based) + { + if (ignoreThese.Contains(kvp.Key.ToString())) + continue; + + var localPath = path + "/" + kvp.Key.ToString(); + var deletedByOurs = !ours.Children.ContainsKey(kvp.Key); + var deletedByOther = !other.Children.ContainsKey(kvp.Key); + if (deletedByOther && (!deletedByOurs)) + { + // Delete + ours.Children.Remove(kvp.Key); + } + else if (!(deletedByOurs || deletedByOther)) + { + // Modify + var a = ours[kvp.Key]; + var b = kvp.Value; // based[kvp.Key] + var c = other[kvp.Key]; + if (!TriTypeMatch(a, b, c)) + { + Console.WriteLine("Warning: Type mismatch (defaulting to value C) at " + localPath); + ours.Children[kvp.Key] = CopyYamlNodes(c); + } + else + { + MergeYamlNodes(a, b, c, localPath); + } + } + } + // Additions + foreach (var kvp in other) + { + if (ignoreThese.Contains(kvp.Key.ToString())) + continue; + + var localPath = path + "/" + kvp.Key.ToString(); + if (!based.Children.ContainsKey(kvp.Key)) + { + if (ours.Children.ContainsKey(kvp.Key)) + { + // Both sides added the same key. Try to merge. + var a = ours[kvp.Key]; + var b = based[kvp.Key]; + var c = kvp.Value; // other[kvp.Key] + if (!TriTypeMatch(a, b, c)) + { + Console.WriteLine("Warning: Type mismatch (defaulting to value C) at " + localPath); + ours.Children[kvp.Key] = CopyYamlNodes(c); + } + else + { + MergeYamlNodes(a, b, c, localPath); + } + } + else + { + // Well that was easy + ours.Children[kvp.Key] = CopyYamlNodes(kvp.Value); + } + } + } + } + + // NOTE: This is a heuristic ONLY! And is also not used at the moment because sequence matching isn't in place. + // It could also be massively improved. + public static float YamlNodesHeuristic(YamlNode a, YamlNode b) + { + if (a.GetType() != b.GetType()) + return 0.0f; + switch (a) + { + case YamlSequenceNode x: + return YamlSequencesHeuristic((YamlSequenceNode) a, (YamlSequenceNode) b); + case YamlMappingNode y: + return YamlMappingsHeuristic((YamlMappingNode) a, (YamlMappingNode) b); + case YamlScalarNode z: + return (((YamlScalarNode) a).Value == ((YamlScalarNode) b).Value) ? 1.0f : 0.0f; + default: + throw new ArgumentException($"Unrecognized YAML node type: {a.GetType()}", nameof(a)); + } + } + + public static float YamlSequencesHeuristic(YamlSequenceNode a, YamlSequenceNode b) + { + if (a.Children.Count != b.Children.Count) + return 0.0f; + if (a.Children.Count == 0) + return 1.0f; + var total = 0.0f; + for (var i = 0; i < a.Children.Count; i++) + total += YamlNodesHeuristic(a.Children[i], b.Children[i]); + return total / a.Children.Count; + } + + public static float YamlMappingsHeuristic(YamlMappingNode a, YamlMappingNode b) + { + return (a == b) ? 1.0f : 0.0f; + } + + public static void CopyYamlScalar(YamlScalarNode dst, YamlScalarNode src) + { + dst.Value = src.Value; + dst.Style = src.Style; + } + } +} diff --git a/Content.Tools/test/0A.yml b/Content.Tools/test/0A.yml new file mode 100644 index 0000000000..d977df527a --- /dev/null +++ b/Content.Tools/test/0A.yml @@ -0,0 +1,95 @@ +meta: + format: 2 + name: DemoStation + author: Space-Wizards + postmapinit: false +tilemap: + 0: space + 7: floor_dark + 8: floor_elevator_shaft + 9: floor_freezer + 10: floor_gold + 11: floor_green_circuit + 12: floor_hydro + 13: floor_lino + 14: floor_mono + 15: floor_reinforced + 16: floor_rock_vault + 17: floor_showroom + 18: floor_snow + 19: floor_steel + 20: floor_steel_dirty + 21: floor_techmaint + 22: floor_white + 23: floor_wood + 24: lattice + 25: plating + 26: underplating +grids: +- settings: + chunksize: 16 + tilesize: 1 + snapsize: 1 + chunks: + - ind: "-2,-2" + comment: "Ew in A | FQ in B | Fg in C | Fg in Out - SHOULD CAUSE CONFLICT WARNING" + tiles: EwAAABMAAAATAAAAEwAAABMAAAATAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABcAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAaAAAAGgAAABMAAAATAAAAEwAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABMAAAAaAAAAEwAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAAaAAAAEwAAABoAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAVAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABkAAAAZAAAAGQAAABkAAAAZAAAAGgAAABoAAAAaAAAAGgAAABoAAAATAAAAGgAAABMAAAATAAAAEwAAABUAAAAZAAAAFQAAABUAAAAVAAAAGQAAABkAAAAZAAAAFQAAABUAAAAVAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGQAAABUAAAAaAAAAFQAAAA== + - ind: "-1,-1" + tiles: EwAAABYAAAAWAAAAFgAAABYAAAATAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABoAAAAWAAAAFgAAABYAAAAWAAAAEwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAAaAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAABkAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAGgAAABMAAAAZAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABoAAAATAAAAGQAAABoAAAATAAAAEwAAABMAAAAaAAAAEwAAABoAAAAaAAAAEwAAABMAAAAaAAAAGgAAABMAAAAaAAAAGgAAABkAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAZAAAAFQAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAAA== + - ind: "-1,0" + tiles: EwAAABYAAAAWAAAAFgAAABYAAAATAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABoAAAAWAAAAFgAAABYAAAAWAAAAEwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAAaAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAABkAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAGgAAABMAAAAZAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABoAAAATAAAAGQAAABoAAAATAAAAEwAAABMAAAAaAAAAEwAAABoAAAAaAAAAEwAAABMAAAAaAAAAGgAAABMAAAAaAAAAGgAAABkAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAZAAAAFQAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAAA== + - ind: "0,0" + tiles: FwAAABMAAAATAAAAEwAAABMAAAATAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABcAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAaAAAAGgAAABMAAAATAAAAEwAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABMAAAAaAAAAEwAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAAaAAAAEwAAABoAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAVAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABkAAAAZAAAAGQAAABkAAAAZAAAAGgAAABoAAAAaAAAAGgAAABoAAAATAAAAGgAAABMAAAATAAAAEwAAABUAAAAZAAAAFQAAABUAAAAVAAAAGQAAABkAAAAZAAAAFQAAABUAAAAVAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGQAAABUAAAAaAAAAFQAAAA== +entities: +- uid: 0 + type: FakeTestDummy + components: + - parent: 855 + pos: -5.5,-1.5 + rot: -1.5707963267948966 rad + type: Transform + - type: ContainerContainer + typeChange: + - 0 + - 1 + - 2 + example: + - 0 + - 1 + - 2 + - 3 + - 855 + - "contributionA" +- uid: 1 + type: FakeTestDummy + components: + - parent: 0 + pos: -15.5,-14.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2 + type: FakeTestDummy + components: + - parent: 1 + pos: -15.5,-14.5 + rot: -1.5707963267948966 rad + type: Transform + - type: ThereShouldBeTwoOfTheseInOutputOfMergeTestAAA +- uid: 3 + type: AnnoyingPlaceholderOnPurpose + components: + - parent: 0 + type: Transform +- uid: 855 + components: + - name: Saltern Station + type: MetaData + - parent: null + type: Transform + - index: 0 + type: MapGrid + - shapes: + - !type:PhysShapeGrid + grid: 0 + type: Physics + diff --git a/Content.Tools/test/0B.yml b/Content.Tools/test/0B.yml new file mode 100644 index 0000000000..27430439da --- /dev/null +++ b/Content.Tools/test/0B.yml @@ -0,0 +1,86 @@ +meta: + format: 2 + name: DemoStation + author: Space-Wizards + postmapinit: false +tilemap: + 0: space + 7: floor_dark + 8: floor_elevator_shaft + 9: floor_freezer + 10: floor_gold + 11: floor_green_circuit + 12: floor_hydro + 13: floor_lino + 14: floor_mono + 15: floor_reinforced + 16: floor_rock_vault + 17: floor_showroom + 18: floor_snow + 19: floor_steel + 20: floor_steel_dirty + 21: floor_techmaint + 22: floor_white + 23: floor_wood + 24: lattice + 25: plating + 26: underplating +grids: +- settings: + chunksize: 16 + tilesize: 1 + snapsize: 1 + chunks: + - ind: "-2,-2" + comment: "Ew in A | FQ in B | Fg in C | Fg in Out - SHOULD CAUSE CONFLICT WARNING" + tiles: FQAAABMAAAATAAAAEwAAABMAAAATAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABcAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAaAAAAGgAAABMAAAATAAAAEwAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABMAAAAaAAAAEwAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAAaAAAAEwAAABoAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAVAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABkAAAAZAAAAGQAAABkAAAAZAAAAGgAAABoAAAAaAAAAGgAAABoAAAATAAAAGgAAABMAAAATAAAAEwAAABUAAAAZAAAAFQAAABUAAAAVAAAAGQAAABkAAAAZAAAAFQAAABUAAAAVAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGQAAABUAAAAaAAAAFQAAAA== + - ind: "-1,-1" + tiles: FQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAVAAAAFQAAABoAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAGgAAABUAAAAaAAAAGgAAABoAAAAZAAAAFQAAABUAAAAaAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFQAAABUAAAAVAAAAFQAAABUAAAAaAAAAGQAAABUAAAAVAAAAGgAAABYAAAAWAAAAFgAAABoAAAAaAAAAGgAAABUAAAAVAAAAFQAAABUAAAAVAAAAGgAAABkAAAAVAAAAFQAAABoAAAAaAAAAFgAAABoAAAAaAAAAGQAAABkAAAAVAAAAFQAAABUAAAAVAAAAFQAAABoAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAFQAAABUAAAAVAAAAFQAAABUAAAAaAAAAGQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABoAAAAaAAAAGgAAABUAAAAaAAAAGgAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAZAAAAFQAAAA0AAAANAAAADQAAAA0AAAANAAAAGgAAABUAAAAVAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAANAAAADQAAAA0AAAANAAAADQAAABoAAAAVAAAAFQAAABkAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAADQAAAA0AAAANAAAADQAAAA0AAAAaAAAAGgAAABoAAAAZAAAAEwAAAAkAAAAJAAAACQAAAAkAAAAJAAAAGgAAAA0AAAANAAAADQAAAA0AAAANAAAAFwAAABcAAAAXAAAAGgAAABoAAAAJAAAACQAAAAkAAAAJAAAACQAAABoAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABoAAAAaAAAAGgAAABMAAAAaAAAAGgAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAaAAAAFgAAABYAAAAWAAAAFgAAABYAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAGgAAABYAAAAWAAAAFgAAABYAAAATAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAAA== + - ind: "-1,0" + tiles: EwAAABYAAAAWAAAAFgAAABYAAAATAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABoAAAAWAAAAFgAAABYAAAAWAAAAEwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAAaAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAABkAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAGgAAABMAAAAZAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABoAAAATAAAAGQAAABoAAAATAAAAEwAAABMAAAAaAAAAEwAAABoAAAAaAAAAEwAAABMAAAAaAAAAGgAAABMAAAAaAAAAGgAAABkAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAZAAAAFQAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAAA== + - ind: "0,-1" + tiles: FgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAWAAAAFgAAABYAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAAWAAAAFgAAABYAAAAWAAAAGgAAABYAAAAWAAAAFgAAABYAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAFgAAABYAAAAWAAAAFgAAABoAAAAWAAAAFgAAABYAAAAWAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAWAAAAGgAAABoAAAAaAAAAFgAAABYAAAAWAAAAGgAAABkAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAZAAAAGgAAABMAAAATAAAAEwAAABoAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGQAAABoAAAATAAAAEwAAABMAAAAaAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABkAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAZAAAAFQAAABMAAAATAAAAEwAAABoAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFQAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABYAAAAaAAAAGgAAABYAAAAWAAAAGgAAABoAAAAWAAAAFgAAABoAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAXAAAAGgAAABMAAAATAAAAEwAAABoAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGgAAABYAAAAWAAAAFwAAABoAAAATAAAAEwAAABMAAAAaAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABoAAAAaAAAAFgAAABcAAAATAAAAEwAAABMAAAATAAAAEwAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAXAAAAEwAAABMAAAATAAAAEwAAABMAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGgAAABYAAAAWAAAAFwAAABMAAAATAAAAEwAAABMAAAATAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABoAAAAWAAAAFgAAAA== + - ind: "0,0" + tiles: FwAAABMAAAATAAAAEwAAABMAAAATAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABcAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAaAAAAGgAAABMAAAATAAAAEwAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABMAAAAaAAAAEwAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAAaAAAAEwAAABoAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAVAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABkAAAAZAAAAGQAAABkAAAAZAAAAGgAAABoAAAAaAAAAGgAAABoAAAATAAAAGgAAABMAAAATAAAAEwAAABUAAAAZAAAAFQAAABUAAAAVAAAAGQAAABkAAAAZAAAAFQAAABUAAAAVAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGQAAABUAAAAaAAAAFQAAAA== +entities: +- uid: 0 + type: FakeTestDummy + components: + - parent: 855 + pos: -15.5,-11.5 + rot: -1.5707963267948966 rad + type: Transform + - type: ContainerContainer + typeChange: + - 0 + - 1 + example: + - 0 + - 1 + - 3 + - 855 +- uid: 1 + type: FakeTestDummy + components: + - parent: 0 + pos: -15.5,-14.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 3 + type: AnnoyingPlaceholderOnPurpose + components: + - parent: 0 + type: Transform +- uid: 855 + components: + - name: Saltern Station + type: MetaData + - parent: null + type: Transform + - index: 0 + type: MapGrid + - shapes: + - !type:PhysShapeGrid + grid: 0 + type: Physics + diff --git a/Content.Tools/test/0C.yml b/Content.Tools/test/0C.yml new file mode 100644 index 0000000000..9d1634c44f --- /dev/null +++ b/Content.Tools/test/0C.yml @@ -0,0 +1,105 @@ +meta: + format: 2 + name: DemoStation + author: Space-Wizards + postmapinit: false +tilemap: + 0: space + 1: floor_asteroid_coarse_sand0 + 2: floor_asteroid_coarse_sand1 + 3: floor_asteroid_coarse_sand2 + 4: floor_asteroid_coarse_sand_dug + 5: floor_asteroid_sand + 6: floor_asteroid_tile + 7: floor_dark + 8: floor_elevator_shaft + 9: floor_freezer + 10: floor_gold + 11: floor_green_circuit + 12: floor_hydro + 13: floor_lino + 14: floor_mono + 15: floor_reinforced + 16: floor_rock_vault + 17: floor_showroom + 18: floor_snow + 19: floor_steel + 20: floor_steel_dirty + 21: floor_techmaint + 22: floor_white + 23: floor_wood + 24: lattice + 25: plating + 26: underplating +grids: +- settings: + chunksize: 16 + tilesize: 1 + snapsize: 1 + chunks: + - ind: "-2,-2" + comment: "Ew in A | FQ in B | Fg in C | Fg in Out - SHOULD CAUSE CONFLICT WARNING" + tiles: FgAAABMAAAATAAAAEwAAABMAAAATAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABcAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAaAAAAGgAAABMAAAATAAAAEwAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABMAAAAaAAAAEwAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAAaAAAAEwAAABoAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAVAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAGgAAABkAAAAZAAAAGQAAABkAAAAZAAAAGgAAABoAAAAaAAAAGgAAABoAAAATAAAAGgAAABMAAAATAAAAEwAAABUAAAAZAAAAFQAAABUAAAAVAAAAGQAAABkAAAAZAAAAFQAAABUAAAAVAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGQAAABUAAAAaAAAAFQAAAA== + - ind: "-1,-1" + tiles: FQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAVAAAAFQAAABoAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAGgAAABUAAAAaAAAAGgAAABoAAAAZAAAAFQAAABUAAAAaAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFQAAABUAAAAVAAAAFQAAABUAAAAaAAAAGQAAABUAAAAVAAAAGgAAABYAAAAWAAAAFgAAABoAAAAaAAAAGgAAABUAAAAVAAAAFQAAABUAAAAVAAAAGgAAABkAAAAVAAAAFQAAABoAAAAaAAAAFgAAABoAAAAaAAAAGQAAABkAAAAVAAAAFQAAABUAAAAVAAAAFQAAABoAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAFQAAABUAAAAVAAAAFQAAABUAAAAaAAAAGQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABoAAAAaAAAAGgAAABUAAAAaAAAAGgAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAFQAAABUAAAAVAAAAFQAAABUAAAAVAAAAFQAAABUAAAAZAAAAFQAAAA0AAAANAAAADQAAAA0AAAANAAAAGgAAABUAAAAVAAAAGQAAABkAAAAZAAAAGQAAABkAAAAZAAAAGQAAABkAAAANAAAADQAAAA0AAAANAAAADQAAABoAAAAVAAAAFQAAABkAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAADQAAAA0AAAANAAAADQAAAA0AAAAaAAAAGgAAABoAAAAZAAAAEwAAAAkAAAAJAAAACQAAAAkAAAAJAAAAGgAAAA0AAAANAAAADQAAAA0AAAANAAAAFwAAABcAAAAXAAAAGgAAABoAAAAJAAAACQAAAAkAAAAJAAAACQAAABoAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABoAAAAaAAAAGgAAABMAAAAaAAAAGgAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAaAAAAFgAAABYAAAAWAAAAFgAAABYAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAGgAAABYAAAAWAAAAFgAAABYAAAATAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAAA== + - ind: "-1,0" + tiles: EwAAABYAAAAWAAAAFgAAABYAAAATAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABoAAAAWAAAAFgAAABYAAAAWAAAAEwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAXAAAAFwAAABcAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAGgAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAAaAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAABkAAAAaAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAGgAAABMAAAAZAAAAGgAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABoAAAATAAAAGQAAABoAAAATAAAAEwAAABMAAAAaAAAAEwAAABoAAAAaAAAAEwAAABMAAAAaAAAAGgAAABMAAAAaAAAAGgAAABkAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAZAAAAFQAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABoAAAAaAAAAGgAAABoAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAEwAAABMAAAATAAAAEwAAABoAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAaAAAAGgAAAA== + - ind: "0,-1" + tiles: FgAAABMAAAATAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAWAAAAFgAAABYAAAATAAAAEwAAABMAAAATAAAAEwAAABoAAAAWAAAAFgAAABYAAAAWAAAAGgAAABYAAAAWAAAAFgAAABYAAAAaAAAAEwAAABMAAAATAAAAEwAAABMAAAAaAAAAFgAAABYAAAAWAAAAFgAAABoAAAAWAAAAFgAAABYAAAAWAAAAGgAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABoAAAAWAAAAGgAAABoAAAAaAAAAFgAAABYAAAAWAAAAGgAAABkAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAZAAAAGgAAABMAAAATAAAAEwAAABoAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGQAAABoAAAATAAAAEwAAABMAAAAaAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABkAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAZAAAAFQAAABMAAAATAAAAEwAAABoAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFQAAABoAAAATAAAAEwAAABMAAAAaAAAAGgAAABYAAAAaAAAAGgAAABYAAAAWAAAAGgAAABoAAAAWAAAAFgAAABoAAAAaAAAAEwAAABMAAAATAAAAGgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAXAAAAGgAAABMAAAATAAAAEwAAABoAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGgAAABYAAAAWAAAAFwAAABoAAAATAAAAEwAAABMAAAAaAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABoAAAAaAAAAFgAAABcAAAATAAAAEwAAABMAAAATAAAAEwAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAaAAAAFgAAABYAAAAXAAAAEwAAABMAAAATAAAAEwAAABMAAAAWAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAGgAAABYAAAAWAAAAFwAAABMAAAATAAAAEwAAABMAAAATAAAAFgAAABYAAAAWAAAAFgAAABYAAAAWAAAAFgAAABoAAAAWAAAAFgAAAA== +entities: +- uid: 0 + type: FakeTestDummy + components: + - parent: 855 + pos: -15.5,-11.5 + rot: -1.5707963267948966 rad + type: Transform + - type: ContainerContainer + typeChange: + something: true + example: + - 0 + - 1 + - 2 + - 3 + - 4 + - 855 + - "contributionC" +- uid: 1 + type: FakeTestDummy + components: + - parent: 0 + pos: -5.5,-4.5 + rot: -1.5707963267948966 rad + type: Transform +- uid: 2 + type: FakeTestDummy + components: + - parent: 1 + pos: -15.5,-14.5 + rot: -1.5707963267948966 rad + type: Transform + - type: ThereShouldBeTwoOfTheseInOutputOfMergeTestBBB +- uid: 3 + type: AnnoyingPlaceholderOnPurpose + components: + - parent: 0 + type: Transform +- uid: 4 + type: ThisShouldHaveParentChangedToFollowUID + components: + - parent: 2 + type: Transform +- uid: 855 + components: + - name: Saltern Station + type: MetaData + - parent: null + type: Transform + - index: 0 + type: MapGrid + - shapes: + - !type:PhysShapeGrid + grid: 0 + type: Physics + diff --git a/Content.Tools/test/run.sh b/Content.Tools/test/run.sh new file mode 100755 index 0000000000..7809b4dc92 --- /dev/null +++ b/Content.Tools/test/run.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cp 0A.yml out.yml +../bin/Debug/net5.0/Content.Tools out.yml 0B.yml 0C.yml diff --git a/SpaceStation14.sln b/SpaceStation14.sln index 9736962bea..2ed361745f 100644 --- a/SpaceStation14.sln +++ b/SpaceStation14.sln @@ -89,6 +89,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX", "RobustToolbox\Xaml EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlX.Runtime", "RobustToolbox\XamlX\src\XamlX.Runtime\XamlX.Runtime.csproj", "{440426C1-8DCA-43F6-967F-94439B8DAF47}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Content.Tools", "Content.Tools\Content.Tools.csproj", "{75AB8F8D-9E56-4B12-85E3-E03A852B31CC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -193,6 +195,10 @@ Global {440426C1-8DCA-43F6-967F-94439B8DAF47}.Debug|Any CPU.Build.0 = Debug|Any CPU {440426C1-8DCA-43F6-967F-94439B8DAF47}.Release|Any CPU.ActiveCfg = Release|Any CPU {440426C1-8DCA-43F6-967F-94439B8DAF47}.Release|Any CPU.Build.0 = Release|Any CPU + {75AB8F8D-9E56-4B12-85E3-E03A852B31CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75AB8F8D-9E56-4B12-85E3-E03A852B31CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75AB8F8D-9E56-4B12-85E3-E03A852B31CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75AB8F8D-9E56-4B12-85E3-E03A852B31CC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Tools/mapping-merge-driver.sh b/Tools/mapping-merge-driver.sh new file mode 100755 index 0000000000..0421278f31 --- /dev/null +++ b/Tools/mapping-merge-driver.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +# Add this to .git/config: +# [merge "mapping-merge-driver"] +# name = Merge driver for maps +# driver = Tools/mapping-merge-driver.sh %A %O %B + +dotnet run --project ./Content.Tools "$@" + From 59e72697cbef58b2527e5e0687b51912551f6d43 Mon Sep 17 00:00:00 2001 From: Alex Evgrashin Date: Sun, 7 Feb 2021 02:05:53 +0300 Subject: [PATCH 14/22] Buildable wall light (#2644) * Added empty light * Can build light fixture * Can construct and deconstruct small light * You can build bulbs only on walls * Playing with placement conditions * Refactored code a bit * Added check for north direction and snapping * Fixed all small light sprites (wrong directions order) * Fixed weird problem with bulb lights * Fixed rotation on all stations * Fixed map again * Much better placement mode * Deleted shared wall component and moved all logic to raycasts * Missing bracket * Better texture * Moved wallmount condition to tags * Removed station station * Added suffix and fixed on map init bug --- .../Placement/Modes/WallmountLight.cs | 68 ++++++++++++++++ .../PoweredLightComponent.cs | 20 +++-- .../WallmountCondition.cs | 55 +++++++++++++ Resources/Maps/saltern.yml | 73 ++++++++++-------- .../Entities/Constructible/Walls/lighting.yml | 59 +++++++++----- .../Entities/Constructible/Walls/walls.yml | 5 +- .../Recipes/Construction/Graphs/lighting.yml | 46 +++++++++++ .../Recipes/Construction/lighting.yml | 36 +++++++++ Resources/Prototypes/tags.yml | 3 + .../Lighting/light_small.rsi/broken.png | Bin 246 -> 1047 bytes .../Lighting/light_small.rsi/burned.png | Bin 274 -> 1069 bytes .../Lighting/light_small.rsi/empty.png | Bin 199 -> 1007 bytes .../Lighting/light_small.rsi/off.png | Bin 262 -> 1060 bytes .../Lighting/light_small.rsi/on.png | Bin 287 -> 440 bytes 14 files changed, 306 insertions(+), 59 deletions(-) create mode 100644 Content.Client/Placement/Modes/WallmountLight.cs create mode 100644 Content.Shared/Construction/ConstructionConditions/WallmountCondition.cs create mode 100644 Resources/Prototypes/Recipes/Construction/Graphs/lighting.yml create mode 100644 Resources/Prototypes/Recipes/Construction/lighting.yml diff --git a/Content.Client/Placement/Modes/WallmountLight.cs b/Content.Client/Placement/Modes/WallmountLight.cs new file mode 100644 index 0000000000..ba01d7a293 --- /dev/null +++ b/Content.Client/Placement/Modes/WallmountLight.cs @@ -0,0 +1,68 @@ +#nullable enable +using Robust.Client.Placement; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Content.Client.Placement.Modes +{ + public class WallmountLight : PlacementMode + { + public WallmountLight(PlacementManager pMan) : base(pMan) + { + } + + public override void AlignPlacementMode(ScreenCoordinates mouseScreen) + { + MouseCoords = ScreenToCursorGrid(mouseScreen); + CurrentTile = GetTileRef(MouseCoords); + + if (pManager.CurrentPermission!.IsTile) + { + return; + } + + var tileCoordinates = new EntityCoordinates(MouseCoords.EntityId, CurrentTile.GridIndices); + + Vector2 offset; + switch (pManager.Direction) + { + case Direction.North: + offset = new Vector2(0.5f, 1f); + break; + case Direction.South: + offset = new Vector2(0.5f, 0f); + break; + case Direction.East: + offset = new Vector2(1f, 0.5f); + break; + case Direction.West: + offset = new Vector2(0f, 0.5f); + break; + default: + return; + } + + tileCoordinates = tileCoordinates.Offset(offset); + MouseCoords = tileCoordinates; + } + + public override bool IsValidPosition(EntityCoordinates position) + { + if (pManager.CurrentPermission!.IsTile) + { + return false; + } + else if (!RangeCheck(position)) + { + return false; + } + + return true; + } + } +} diff --git a/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs b/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs index cda8f1b7cd..de2bd96d16 100644 --- a/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs +++ b/Content.Server/GameObjects/Components/Power/ApcNetComponents/PowerReceiverUsers/PoweredLightComponent.cs @@ -38,6 +38,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece private static readonly TimeSpan _thunkDelay = TimeSpan.FromSeconds(2); private TimeSpan _lastThunk; + private bool _hasLampOnSpawn; [ViewVariables] private bool _on; @@ -148,6 +149,7 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece { serializer.DataField(ref BulbType, "bulb", LightBulbType.Tube); serializer.DataField(ref _on, "on", true); + serializer.DataField(ref _hasLampOnSpawn, "hasLampOnSpawn", true); } /// @@ -229,15 +231,19 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece void IMapInit.MapInit() { - var prototype = BulbType switch + if (_hasLampOnSpawn) { - LightBulbType.Bulb => "LightBulb", - LightBulbType.Tube => "LightTube", - _ => throw new ArgumentOutOfRangeException() - }; + var prototype = BulbType switch + { + LightBulbType.Bulb => "LightBulb", + LightBulbType.Tube => "LightTube", + _ => throw new ArgumentOutOfRangeException() + }; - var entity = Owner.EntityManager.SpawnEntity(prototype, Owner.Transform.Coordinates); - _lightBulbContainer.Insert(entity); + var entity = Owner.EntityManager.SpawnEntity(prototype, Owner.Transform.Coordinates); + _lightBulbContainer.Insert(entity); + UpdateLight(); + } } public void TriggerSignal(bool signal) diff --git a/Content.Shared/Construction/ConstructionConditions/WallmountCondition.cs b/Content.Shared/Construction/ConstructionConditions/WallmountCondition.cs new file mode 100644 index 0000000000..73087b1f40 --- /dev/null +++ b/Content.Shared/Construction/ConstructionConditions/WallmountCondition.cs @@ -0,0 +1,55 @@ +#nullable enable +using Content.Shared.GameObjects.Components.Tag; +using Content.Shared.Physics; +using JetBrains.Annotations; +using Robust.Shared.Interfaces.GameObjects; +using Robust.Shared.Interfaces.Physics; +using Robust.Shared.IoC; +using Robust.Shared.Map; +using Robust.Shared.Maths; +using Robust.Shared.Serialization; +using System.Linq; + +namespace Content.Shared.Construction.ConstructionConditions +{ + [UsedImplicitly] + public class WallmountCondition : IConstructionCondition + { + public void ExposeData(ObjectSerializer serializer) { } + + public bool Condition(IEntity user, EntityCoordinates location, Direction direction) + { + var entManager = IoCManager.Resolve(); + + // get blueprint and user position + var userWorldPosition = user.Transform.WorldPosition; + var objWorldPosition = location.ToMap(entManager).Position; + + // find direction from user to blueprint + var userToObject = (objWorldPosition - userWorldPosition); + + // dot product will be positive if user direction and blueprint are co-directed + var dotProd = Vector2.Dot(direction.ToVec(), userToObject); + if (dotProd > 0) + return false; + + // now we need to check that user actually tries to build wallmount on a wall + var physics = IoCManager.Resolve(); + var rUserToObj = new CollisionRay(userWorldPosition, userToObject.Normalized, (int) CollisionGroup.Impassable); + var length = userToObject.Length; + var userToObjRaycastResults = physics.IntersectRayWithPredicate(user.Transform.MapID, rUserToObj, maxLength: length, + predicate: (e) => !e.HasTag("Wall")); + if (!userToObjRaycastResults.Any()) + return false; + + // get this wall entity + var targetWall = userToObjRaycastResults.First().HitEntity; + + // check that we didn't try to build wallmount that facing another adjacent wall + var rAdjWall = new CollisionRay(objWorldPosition, direction.ToVec(), (int) CollisionGroup.Impassable); + var adjWallRaycastResults = physics.IntersectRayWithPredicate(user.Transform.MapID, rAdjWall, maxLength: 0.5f, + predicate: (e) => e == targetWall || !e.HasTag("Wall")); + return !adjWallRaycastResults.Any(); + } + } +} diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml index 6c744ecaf1..b5541b8aeb 100644 --- a/Resources/Maps/saltern.yml +++ b/Resources/Maps/saltern.yml @@ -20002,7 +20002,7 @@ entities: components: - parent: 853 pos: -12.5,-5 - rot: -1.5707963267948966 rad + rot: 1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -21324,7 +21324,7 @@ entities: components: - parent: 853 pos: -6.5,-26 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -21430,6 +21430,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: -7,-23.5 type: Transform - color: '#FFFFFFFF' @@ -22573,7 +22574,7 @@ entities: components: - parent: 853 pos: -29.5,15 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -22602,7 +22603,7 @@ entities: components: - parent: 853 pos: -34,-0.5 - rot: 3.141592653589793 rad + rot: 0 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -22616,6 +22617,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: -18,-4.5 type: Transform - color: '#FFFFFFFF' @@ -22700,7 +22702,7 @@ entities: components: - parent: 853 pos: -29.5,-9 - rot: -1.5707963267948966 rad + rot: 1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -22883,6 +22885,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: -19,9.5 type: Transform - color: '#FFFFFFFF' @@ -22912,7 +22915,7 @@ entities: components: - parent: 853 pos: -14.5,-16 - rot: -1.5707963267948966 rad + rot: 1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -22926,6 +22929,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: -7,-12.5 type: Transform - color: '#FFFFFFFF' @@ -22940,6 +22944,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: -11,-10.5 type: Transform - color: '#FFFFFFFF' @@ -23112,7 +23117,7 @@ entities: components: - parent: 853 pos: -19,16.5 - rot: 3.141592653589793 rad + rot: 0 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -23127,7 +23132,7 @@ entities: components: - parent: 853 pos: -14.5,26 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -23142,7 +23147,7 @@ entities: components: - parent: 853 pos: -19,22.5 - rot: 3.141592653589793 rad + rot: 0 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -23157,7 +23162,7 @@ entities: components: - parent: 853 pos: -18,9.5 - rot: 3.141592653589793 rad + rot: 0 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -23172,7 +23177,7 @@ entities: components: - parent: 853 pos: -0.5,-12 - rot: -1.5707963267948966 rad + rot: 1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -23386,7 +23391,7 @@ entities: components: - parent: 853 pos: -1,8.5 - rot: 3.141592653589793 rad + rot: 0 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -23401,7 +23406,7 @@ entities: components: - parent: 853 pos: -4,8.5 - rot: 3.141592653589793 rad + rot: 0 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -23528,7 +23533,7 @@ entities: components: - parent: 853 pos: -7.5,26 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -23550,7 +23555,7 @@ entities: components: - parent: 853 pos: -4,17.5 - rot: 3.141592653589793 rad + rot: 0 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -23783,6 +23788,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: 17,15.5 type: Transform - color: '#FFFFFFFF' @@ -23797,6 +23803,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: 14,16.5 type: Transform - color: '#FFFFFFFF' @@ -23812,7 +23819,7 @@ entities: components: - parent: 853 pos: 8.5,15 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -24274,7 +24281,7 @@ entities: components: - parent: 853 pos: 30.5,-6 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -24289,7 +24296,7 @@ entities: components: - parent: 853 pos: 25.5,-1 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -24304,7 +24311,7 @@ entities: components: - parent: 853 pos: 9.5,-18 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -24347,7 +24354,7 @@ entities: components: - parent: 853 pos: 12.5,-19 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -24369,7 +24376,7 @@ entities: components: - parent: 853 pos: 25.5,-19 - rot: -1.5707963267948966 rad + rot: 1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -24383,6 +24390,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: 28,-15.5 type: Transform - color: '#FFFFFFFF' @@ -24432,6 +24440,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: 28,-10.5 type: Transform - color: '#FFFFFFFF' @@ -24453,6 +24462,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: 24,0.5 type: Transform - color: '#FFFFFFFF' @@ -24468,7 +24478,7 @@ entities: components: - parent: 853 pos: 26,-4.5 - rot: 3.141592653589793 rad + rot: 0 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -24483,7 +24493,7 @@ entities: components: - parent: 853 pos: 35.5,-6 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -24498,7 +24508,7 @@ entities: components: - parent: 853 pos: 38.5,-3 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -24513,7 +24523,7 @@ entities: components: - parent: 853 pos: 26,11.5 - rot: 3.141592653589793 rad + rot: 0 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -24709,6 +24719,7 @@ entities: type: PoweredSmallLight components: - parent: 853 + rot: 3.141592653589793 rad pos: 44,-1.5 type: Transform - color: '#FFFFFFFF' @@ -26432,7 +26443,7 @@ entities: components: - parent: 853 pos: -12.5,-6 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -26447,7 +26458,7 @@ entities: components: - parent: 853 pos: -25.5,-10 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - color: '#FFFFFFFF' type: PointLight @@ -42390,7 +42401,7 @@ entities: components: - parent: 853 pos: 22.528679,-9.003884 - rot: 1.5707963267948966 rad + rot: -1.5707963267948966 rad type: Transform - powerLoad: 0 type: PowerReceiver @@ -42772,7 +42783,7 @@ entities: components: - parent: 853 pos: -1.4929452,19.970068 - rot: -1.5707963267948966 rad + rot: 1.5707963267948966 rad type: Transform - powerLoad: 0 type: PowerReceiver @@ -42799,7 +42810,7 @@ entities: components: - parent: 853 pos: -15.494916,15.968084 - rot: -1.5707963267948966 rad + rot: 1.5707963267948966 rad type: Transform - powerLoad: 0 type: PowerReceiver @@ -44360,7 +44371,7 @@ entities: components: - parent: 853 pos: -22.5,-16 - rot: -1.5707963267948966 rad + rot: 1.5707963267948966 rad type: Transform - powerLoad: 0 type: PowerReceiver diff --git a/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml b/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml index 47a5cd08d5..ac9555174e 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml @@ -4,6 +4,9 @@ components: - type: Clickable - type: InteractionOutline + - type: Construction + graph: lightFixture + node: tubeLight - type: Physics shapes: - !type:PhysShapeAabb @@ -47,10 +50,39 @@ - !type:DoActsBehavior acts: ["Destruction"] +- type: entity + id: PoweredlightEmpty + suffix: Empty + parent: Poweredlight + components: + - type: Sprite + state: empty + - type: PoweredLight + hasLampOnSpawn: False + +- type: entity + name: unpowered small light + id: SmallLight + parent: WallLight + components: + - type: Sprite + sprite: Constructible/Lighting/light_small.rsi + state: on + - type: PointLight + energy: 1.0 + enabled: true + offset: "-0.5, 0" + - type: Destructible + deadThreshold: 25 + resistances: metallicResistances + - type: Construction + graph: lightFixture + node: bulbLight + - type: entity name: small light id: PoweredSmallLight - parent: WallLight + parent: SmallLight components: - type: Sprite sprite: Constructible/Lighting/light_small.rsi @@ -79,24 +111,11 @@ acts: ["Destruction"] - type: entity - name: unpowered small light - id: SmallLight - parent: WallLight + id: PoweredSmallLightEmpty + suffix: Empty + parent: PoweredSmallLight components: - type: Sprite - sprite: Constructible/Lighting/light_small.rsi - state: on - - type: PointLight - energy: 1.0 - enabled: true - offset: "-0.5, 0" - - type: Damageable - resistances: metallicResistances - - type: Destructible - thresholds: - - trigger: - !type:TotalDamageTrigger - damage: 25 - behaviors: - - !type:DoActsBehavior - acts: ["Destruction"] + state: empty + - type: PoweredLight + hasLampOnSpawn: False diff --git a/Resources/Prototypes/Entities/Constructible/Walls/walls.yml b/Resources/Prototypes/Entities/Constructible/Walls/walls.yml index 7b1f289dc2..80bdf95b40 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/walls.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/walls.yml @@ -1,4 +1,4 @@ -- type: entity +- type: entity id: base_wall name: basewall description: Keeps the air in and the greytide out. @@ -9,6 +9,9 @@ - Wall components: - type: RCDDeconstructWhitelist + - type: Tag + tags: + - Wall - type: Clickable - type: InteractionOutline - type: Sprite diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/lighting.yml b/Resources/Prototypes/Recipes/Construction/Graphs/lighting.yml new file mode 100644 index 0000000000..0d80074446 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/Graphs/lighting.yml @@ -0,0 +1,46 @@ +- type: constructionGraph + id: lightFixture + start: start + graph: + - node: start + edges: + - to: bulbLight + steps: + - material: Metal + amount: 1 + doAfter: 2.0 + - to: tubeLight + steps: + - material: Metal + amount: 2 + doAfter: 2.0 + - node: tubeLight + entity: PoweredlightEmpty + edges: + - to: start + conditions: + - !type:ContainerEmpty + container: "light_bulb" + steps: + - tool: Screwing + doAfter: 2.0 + completed: + - !type:SpawnPrototype + prototype: SteelSheet1 + amount: 2 + - !type:DeleteEntity {} + - node: bulbLight + entity: PoweredSmallLightEmpty + edges: + - to: start + conditions: + - !type:ContainerEmpty + container: "light_bulb" + steps: + - tool: Screwing + doAfter: 2.0 + completed: + - !type:SpawnPrototype + prototype: SteelSheet1 + amount: 1 + - !type:DeleteEntity {} diff --git a/Resources/Prototypes/Recipes/Construction/lighting.yml b/Resources/Prototypes/Recipes/Construction/lighting.yml new file mode 100644 index 0000000000..6fc5ddc8b2 --- /dev/null +++ b/Resources/Prototypes/Recipes/Construction/lighting.yml @@ -0,0 +1,36 @@ +- type: construction + name: wall light + id: LightTubeFixture + graph: lightFixture + startNode: start + targetNode: tubeLight + category: Structures + description: A wall light fixture. Use light tubes. + icon: + sprite: Constructible/Lighting/light_tube.rsi + state: off + objectType: Structure + placementMode: WallmountLight + canRotate: true + canBuildInImpassable: true + conditions: + - !type:WallmountCondition {} + +- type: construction + name: small wall light + id: LightSmallFixture + graph: lightFixture + startNode: start + targetNode: bulbLight + category: Structures + description: A wall light fixture. Use light bulbs. + icon: + sprite: Constructible/Lighting/light_small.rsi + state: off + objectType: Structure + placementMode: WallmountLight + canRotate: true + canBuildInImpassable: true + conditions: + - !type:WallmountCondition {} + diff --git a/Resources/Prototypes/tags.yml b/Resources/Prototypes/tags.yml index 489d564aac..b8023a3ceb 100644 --- a/Resources/Prototypes/tags.yml +++ b/Resources/Prototypes/tags.yml @@ -1,2 +1,5 @@ - type: Tag id: ExplosivePassable + +- type: Tag + id: Wall diff --git a/Resources/Textures/Constructible/Lighting/light_small.rsi/broken.png b/Resources/Textures/Constructible/Lighting/light_small.rsi/broken.png index e0101a39dd6c55380d34c0840566d8b879bb3233..f10246f25055062fa09fd51c7b7acc93d7968495 100644 GIT binary patch delta 1017 zcmV004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}00093P)t-s0000P85ul0Jd=}?u(T}5#2)17jRyw@2nYxX z2?+`c3JVJh3=9kn4Gj(s4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85w^X z8X6lL8yp-Q9UUDW9v&YbA0QwgAt50mA|fLrBP1jwB_$;$CMG8*CnzW=DJdx`Dk>{0 zD=aK5EiElBE-o)GFEB7LF)=YRGBPtWGc+_bH8nLhHa0gmH#j&rIXO8xIyyT$J3Kr* zJv}`>K0ZG`KR`f0K|w)6LPA4BLqvZ>L`6kKMn*_~R#sM5S65hASXo(FT3T9LTU%UQTwPsVUS3{b zUteHgU}0flVq#)rV`F4wWMyS#W@ct*XJ=?=XlZF_YHDh0Yin$5Y;A3AZf<{WZ*OmK zaBy*PadL8Ub8~ZabaZufb#``kcXxMqczAhvd3t(!dwY9)e0+U< zlarH_l$4c~m6n#4mzS5An3#W=nVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAgu zq@<*!rKP5(rl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIf zwY9dkwzs#pxVX5vxw*Q!y1To(yu7@V!^FhI#l^+O z#>U6T$H>UY$;rve%F4^j%goHo&CSiu&d$%z&(P4&(b3V;($dq@)6~?|)z#J3*4Ee8 z*Vx$D+1c6J+S=RO+uYpT-QC^Z-rnEe-{9cj;o;%p;^O1ulq(=H}<;=jiC@ z>FMd}>gwz3>+J08?d^Z~$F2VZ<`Uga z_K*4Yn`%Ec`?*I%M8u`9cg{K>Jg2_vCZ+E^27&I~$b!U!dXZTB08$4{7^QtcH*iod nk^3dgRGoBC{IrP?;dl%HeQg2h5cQo^00000NkvXXu0mjff8pze delta 210 zcmV;@04@KQ2=)Px7zqRe00013M{MnpAsBxZP)t-s0000P85ul0Jd=}?u(T}5#2)17 zjhyphm;e9(0d!JMQvg8b*k%9#0D4J8K~zY`V_?7vm=%Q#81MmR6({@(7+g~sC{w@^ z78ZbCMJO8qD_9w*4hR}a$Wes7MRCXxj2wc|I|@d@C>XMUmBARnF=lYUP>>d?gy23X zxrAbxkmiWsh=isFpv#AaA}dG>z%U`y1=)(wGz0<8#;7zqdi0001;w}I@DAs8(nV@Og>004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}00093kvbxO3JMAf3kwVk3=Itp4h{|v4-XI!5D^g(5)u*< z6B85^6crT}78Vv47Z(^97#SHE8X6iK8yg%P9334U9v&VaA0HqfAR!?kA|fIqBO@dv zBqb#!CMG5)CnqQ@~D=RE4EG;c9E-o%FFE21KFflPPGBPqVGcz=QG&D6e zH8wUjH#avpI5;^uIXXHzJ3Bi(JUl%;Jw84@KR-V}KtMr3K|(@8LqkJEL_|eJMMg$O zM@L6UNJvRZNlHpeOG`^kOiWEpO-@cuPft%!P*71(QBqP;Q&Uq^R8&<}RaRD3S65e9 zSXfzESz20JTU%RPTwGmUU0z;)USD5dU|?WjVPRroVq;@tWMpJzWo2e&W@l$-XlQ6@ zX=!R|YHMq2Y;0_8ZEbFDZf|dIaBy&OadC2Ta&vQYbaZreb#-=jc6WDoczAeud3kzz zdV70&e0+R;eSLm@et&;|fPjF3fq{a8f`fyDgoK2Jg@uNOhKGlTh=_=PiHV7dii(Si zi;Rqnjg5_tj*gFykC2d%k&%&-l9H2?la!Q{m6es2mX?>7mzbECnVFfInwp!No1C1S zot>SYo}QndpP-Ll?si~=|s;aB2tE{Z7t*x!D zuCA}IuduMNv9YnTva+*(v$M3cw6(Rhwzjsnx3{>sxVgExy1Kf%ySu!+yuH1>zP`S{ zzrVo1z`?=6!otGC!^6bH#KpzM#>U3S$H&OX$jQmc%F4>i%gfBn%+1Zs&d$!y&(F}% z(9zM+($dn?)6>+{)YaA1*4Eb7*VowC*xA|H+S=ON+uPjS+}+)O-QM2b-{0Th;Naom z;o{=ruz*=jZ3>=;-O`>FVn0>+9?6?CkCB?e6aG@9*#M@bK~R@$&NW z^Yioc^z`-h_4fAm_xJbs`1twx`TF|$`}_O+{QUj>{r>*`|NsBCOW0Qc0004WQchC< zK<3zH0001*Nkl<^RCwC#ma!58AqYef7MA<}A5M`b#d%J$laALatE@0$_DRKBQT96( z-I9P^U@P&4Oepb5xO4@QaOnz!YaW>B;r|a%Q*EZ@ f+?qHHgTiY7qILrz{}vaA00000NkvXXu0mjfV}j-& delta 208 zcmV;>05AWo2$BMj7zqRe00013M{MnpAs7K3k!B))()n-Z00001bW%=J06^y0W&i*H zjY&j7R7l5TV89C4422UI@Buc9X8Z~m+CA~hGq{G}m**&Q55TK{MWws|zY$ID1l+(; zKuCa8BMCZ+ptlGH48fo!7&!!^cNC0*Q7~jdDMK29lg7}3F3(=>X^h|*w-hy?DJUrk zX+-chBuZ5@(2GH*aAXCp?&!rLhq?!{6;m)vMGsU*hL1+u5Cj09+8T43`4KSy0000< KMNUMnLSTY=*G`ZC diff --git a/Resources/Textures/Constructible/Lighting/light_small.rsi/empty.png b/Resources/Textures/Constructible/Lighting/light_small.rsi/empty.png index 781b969a684fcb88fc905499103bbdd6f0e8d908..2b8c7667fe6bbed5199c77e311608b76c54fde0d 100644 GIT binary patch delta 977 zcmV;?11|i>0q+Np7zqdi0001;w}I@DAsBxkV@Og>004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}00093P)t-s0000P85ul0JZNWIlarIEbQ8aX6$b|g2nYxX z2?+`c3JVJh3=9kn4Gj(s4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85w^X z8X6lL8yp-Q9UUDW9v&YbA0QwgAt50mA|fLrBP1jwB_$;$CMG8*CnzW=DJdx`Dk>{0 zD=aK5EiElBE-o)GFEB7LF)=YRGBPtWGc+_bH8nLhHa0gmH#j&rIXO8xIyyT$J3Kr* zJv}`>K0ZG`KR`f0K|w)6LPA4BLqvZ>L`6kKMn*_~R#sM5S65hASXo(FT3T9LTU%UQTwPsVUS3{b zUteHgU}0flVq#)rV`F4wWMyS#W@ct*XJ=?=XlZF_YHDh0Yin$5Y;A3AZf<{WZ*OmK zaBy*PadL8Ub8~ZabaZufb#``kcXxMqczAhvd3t(!dwY9)e0+U< zlarH_l$4c~m6n#4mzS5An3#W=nVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAgu zq@<*!rKP5(rl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIf zwY9dkwzs#pxVX5vxw*Q!y1To(yu7@V!^FhI#l^+O z#>U6T$H>UY$;rve%F4^j%goHo&CSiu&d$%z&(P4&(b3V;($dq@)6~?|)z#J3*4Ee8 z*Vx$D+1c6J+S=RO+uYpT-QC^Z-rnEe-{9cj;o;%p;^O1ulq(=H}<;=jiC@ z>FMd}>gwz3>+J08?d^ZNw@W!J`3o{ZvyNV>`MgT*Z=?k|NjF3#|Z%0kUHN000000NkvXXu0mjfSliV5 delta 163 zcmaFQew=ZF1SbnK0|P^Zd(PX5iW2pr0X`wFK)$4;q>YVDMtbz*$&*)<2=8wZJs91= z4wPUl3GxeOaCmkj4ao8Kba4#vIG&tvfGa|zRYu|iqu5rJKU@+%&5~}6xeN;zs7hF} zEG`%6X$mxGY2*%5V=~ZIyy3+bA+kYD=|&Rc6Agt7R!8@~CP#h--?QwxiTq85EI@M^ NJYD@<);T3K0RUyTFWUeB diff --git a/Resources/Textures/Constructible/Lighting/light_small.rsi/off.png b/Resources/Textures/Constructible/Lighting/light_small.rsi/off.png index 1de87fbca1f1b337a3b755fe7c22d34ba4cc2adc..027251020c16155895dd4bcd02653657820ba037 100644 GIT binary patch delta 1030 zcmV+h1o``h0;C9#7zqdi0001;w}I@DAsBxkV@Og>004R=004l4008;_004mL004C` z008P>0026e000+nl3&F}00093P)t-s0000P85ul0Jd=}?u(T}5#2)17jRyw@2nYxX z2?+`c3JVJh3=9kn4Gj(s4i66x5D*X%5fKs+5)%^>6ciK{6%`g178e&67#J8C85w^X z8X6lL8yp-Q9UUDW9v&YbA0QwgAt50mA|fLrBP1jwB_$;$CMG8*CnzW=DJdx`Dk>{0 zD=aK5EiElBE-o)GFEB7LF)=YRGBPtWGc+_bH8nLhHa0gmH#j&rIXO8xIyyT$J3Kr* zJv}`>K0ZG`KR`f0K|w)6LPA4BLqvZ>L`6kKMn*_~R#sM5S65hASXo(FT3T9LTU%UQTwPsVUS3{b zUteHgU}0flVq#)rV`F4wWMyS#W@ct*XJ=?=XlZF_YHDh0Yin$5Y;A3AZf<{WZ*OmK zaBy*PadL8Ub8~ZabaZufb#``kcXxMqczAhvd3t(!dwY9)e0+U< zlarH_l$4c~m6n#4mzS5An3#W=nVFiJnwy)OoSdAUot>VZo}ZteprD|kp`oIpqNAgu zq@<*!rKP5(rl+T;sHmu^si~@}s;jH3tgNi9t*x%EuCK4Ju&}VPv9YqUva_?Zw6wIf zwY9dkwzs#pxVX5vxw*Q!y1To(yu7@V!^FhI#l^+O z#>U6T$H>UY$;rve%F4^j%goHo&CSiu&d$%z&(P4&(b3V;($dq@)6~?|)z#J3*4Ee8 z*Vx$D+1c6J+S=RO+uYpT-QC^Z-rnEe-{9cj;o;%p;^O1ulq(=H}<;=jiC@ z>FMd}>gwz3>+J08?d^Z%>EI= z?5C$d$y1;tcLdV}SHM0t``njhS(at_>g(-@IpmSg$_uJ)#7s(L)#Y14l0MHsN)qxQ zKu#g-Rg#AlfHA~@1P&{7M_`Olz|q8ISsWe%02hV=g{jd>j{pDw07*qoM6N<$g7^69 An*aa+ delta 226 zcmV<803H9N2!;ZX7zqRe00013M{MnpAsBxZP)t-s0000P85ul0Jd=}?u(T}5#2)17 zjhyphm;e9(0d!JMQvg8b*k%9#0E$UOK~xwS?ZLqbgfI|5(GM{Vhz%GlAdm*4#Jh(~ zI>_{2!rlVPGWT74kMG<%M=WkTBBoZcwTj7W&se-l#v~P+&)iKHwo@=oq8FB_>4i?| z?AXqZ$!$l(;?_CxdcFRVkQfregI5U*LGfq~J^)5S5Q;?|p6hJDQj0|CWaqVvr2d%$M3o`4c zEc?i(F!i4_1KUa!-^ELew*R#8OxkHL6JgKIC zjg$N22h0yEKZH(xaO}Y4U-#U@7Jgt@B=7J;Tf&xgcGTu19#{DJ9&KS$$cw@c3$P$@%tx3+Oo5^=G3njstr~z={|afA%SZGOC;M(j*X(A gaE5_~_}k|hu6yfeUEF*w86@cG>gTe~i7BB8096Q{OaK4? delta 251 zcmdnNJfCTT1SbnK0|P^Zd(PX5iW2qG0X`wFK>q*#J0VC?Qqso8X7c38|9>9@imY4P z^9d-;QWE4B%%J4lt(mf^3do=3>EamTaXdNU0M`wt7-5MIj2k~(a@oT*Mf+Cohm*!l zA{-9_52W0?7`Vn^7L%gupD+$h>4hr-jP%2j4yZS*W9**TC9-?gk%n|di7hS4?FnD{ zngR{D*w_rTI3GN0V(FRKdSS-`CAXb-EO-z48ta)mX{(*JKIJTU=fbDHB-brM%N8$Y xIavDsg@bk>hn7MHtE0Qn7qx>8Qmk9p8B7@TSq;6m?*KZJ!PC{xWt~$(69DW+WXk{m From f63a89f816893808ae25247aae62c6c50ada5287 Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sun, 7 Feb 2021 06:36:34 +0100 Subject: [PATCH 15/22] Add warning when using the mapping command in debug mode (#3095) --- Content.Client/Commands/DebugCommands.cs | 7 +++++++ Content.Server/Commands/GameTicking/MappingCommand.cs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/Content.Client/Commands/DebugCommands.cs b/Content.Client/Commands/DebugCommands.cs index 15879deece..e402664a97 100644 --- a/Content.Client/Commands/DebugCommands.cs +++ b/Content.Client/Commands/DebugCommands.cs @@ -1,3 +1,5 @@ +// ReSharper disable once RedundantUsingDirective +// Used to warn the player in big red letters in debug mode using System; using Content.Client.GameObjects.Components; using Content.Client.GameObjects.EntitySystems; @@ -8,6 +10,7 @@ using Robust.Shared.Console; using Robust.Shared.GameObjects.Systems; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.IoC; +using Robust.Shared.Maths; namespace Content.Client.Commands { @@ -93,6 +96,10 @@ namespace Content.Client.Commands return; } +#if DEBUG + shell.WriteLine("WARNING: The client is using a debug build. You are risking losing your changes.", Color.Red); +#endif + shell.ConsoleHost.RegisteredCommands["togglelight"].Execute(shell, string.Empty, Array.Empty()); shell.ConsoleHost.RegisteredCommands["showsubfloorforever"].Execute(shell, string.Empty, Array.Empty()); diff --git a/Content.Server/Commands/GameTicking/MappingCommand.cs b/Content.Server/Commands/GameTicking/MappingCommand.cs index 332e5d57c5..9025382be9 100644 --- a/Content.Server/Commands/GameTicking/MappingCommand.cs +++ b/Content.Server/Commands/GameTicking/MappingCommand.cs @@ -1,3 +1,5 @@ +// ReSharper disable once RedundantUsingDirective +// Used to warn the player in big red letters in debug mode using System.Linq; using Content.Server.Administration; using Content.Shared.Administration; @@ -6,6 +8,7 @@ using Robust.Server.Interfaces.Timing; using Robust.Shared.Console; using Robust.Shared.Interfaces.Map; using Robust.Shared.IoC; +using Robust.Shared.Maths; using Robust.Shared.Utility; namespace Content.Server.Commands.GameTicking @@ -26,6 +29,10 @@ namespace Content.Server.Commands.GameTicking return; } +#if DEBUG + shell.WriteLine("WARNING: The server is using a debug build. You are risking losing your changes.", Color.Red); +#endif + var mapManager = IoCManager.Resolve(); int mapId; string mapName; From 834bcda840dcd253fe8ad7de6c3bb6618cfe0a6f Mon Sep 17 00:00:00 2001 From: Vera Aguilera Puerto Date: Sun, 7 Feb 2021 13:52:34 +0100 Subject: [PATCH 16/22] Make VisitingMind's Mind readable by VV --- .../GameObjects/Components/Mobs/VisitingMindComponent.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Content.Server/GameObjects/Components/Mobs/VisitingMindComponent.cs b/Content.Server/GameObjects/Components/Mobs/VisitingMindComponent.cs index 46667aff76..9014ef98b4 100644 --- a/Content.Server/GameObjects/Components/Mobs/VisitingMindComponent.cs +++ b/Content.Server/GameObjects/Components/Mobs/VisitingMindComponent.cs @@ -1,5 +1,6 @@ using Content.Server.Mobs; using Robust.Shared.GameObjects; +using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Mobs { @@ -8,6 +9,7 @@ namespace Content.Server.GameObjects.Components.Mobs { public override string Name => "VisitingMind"; + [ViewVariables] public Mind Mind { get; set; } public override void OnRemove() From 7fbf2c9873a08ef5582013d0dbe4fef11dc9aa6d Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Sun, 7 Feb 2021 23:55:10 +1100 Subject: [PATCH 17/22] Increase lobbyduration default (#3099) I think 60's okay for SSS but when we get actual rounds I'd make it 90. Co-authored-by: Metal Gear Sloth --- Content.Shared/CCVars.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Content.Shared/CCVars.cs b/Content.Shared/CCVars.cs index 7fe2d2eac9..2d69e529f8 100644 --- a/Content.Shared/CCVars.cs +++ b/Content.Shared/CCVars.cs @@ -29,7 +29,7 @@ namespace Content.Shared GameLobbyEnabled = CVarDef.Create("game.lobbyenabled", false, CVar.ARCHIVE); public static readonly CVarDef - GameLobbyDuration = CVarDef.Create("game.lobbyduration", 20, CVar.ARCHIVE); + GameLobbyDuration = CVarDef.Create("game.lobbyduration", 60, CVar.ARCHIVE); public static readonly CVarDef GameLobbyDefaultPreset = CVarDef.Create("game.defaultpreset", "Suspicion", CVar.ARCHIVE); From c479903538c6c64823d548c30d407a9510738ebe Mon Sep 17 00:00:00 2001 From: DrSmugleaf Date: Sun, 7 Feb 2021 14:56:08 +0100 Subject: [PATCH 18/22] Update RobustToolbox --- Content.Client/Commands/DebugCommands.cs | 2 +- Content.Server/Commands/GameTicking/MappingCommand.cs | 2 +- RobustToolbox | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Content.Client/Commands/DebugCommands.cs b/Content.Client/Commands/DebugCommands.cs index e402664a97..e9bb9bf3c8 100644 --- a/Content.Client/Commands/DebugCommands.cs +++ b/Content.Client/Commands/DebugCommands.cs @@ -97,7 +97,7 @@ namespace Content.Client.Commands } #if DEBUG - shell.WriteLine("WARNING: The client is using a debug build. You are risking losing your changes.", Color.Red); + shell.WriteError("WARNING: The client is using a debug build. You are risking losing your changes."); #endif shell.ConsoleHost.RegisteredCommands["togglelight"].Execute(shell, string.Empty, Array.Empty()); diff --git a/Content.Server/Commands/GameTicking/MappingCommand.cs b/Content.Server/Commands/GameTicking/MappingCommand.cs index 9025382be9..0ed2167734 100644 --- a/Content.Server/Commands/GameTicking/MappingCommand.cs +++ b/Content.Server/Commands/GameTicking/MappingCommand.cs @@ -30,7 +30,7 @@ namespace Content.Server.Commands.GameTicking } #if DEBUG - shell.WriteLine("WARNING: The server is using a debug build. You are risking losing your changes.", Color.Red); + shell.WriteError("WARNING: The server is using a debug build. You are risking losing your changes."); #endif var mapManager = IoCManager.Resolve(); diff --git a/RobustToolbox b/RobustToolbox index 18fcab6f71..a183a98f75 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 18fcab6f710947b640b839c458b52193fad2acf7 +Subproject commit a183a98f75b89072989ddc55cb990db6b671363e From 23d432725ce9ffce593d588597d9ed9c09bf0e77 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Sun, 7 Feb 2021 22:28:34 +0100 Subject: [PATCH 19/22] Update patrons. --- Resources/Credits/Patrons.yml | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/Resources/Credits/Patrons.yml b/Resources/Credits/Patrons.yml index 48d17def8d..d224fd7da8 100644 --- a/Resources/Credits/Patrons.yml +++ b/Resources/Credits/Patrons.yml @@ -1,17 +1,21 @@ -- Name: Jigglypuff - Tier: Syndicate Agent +- Name: clyf + Tier: Nuclear Operative +- Name: Kerb 755 + Tier: Revolutionary +- Name: queednyeb + Tier: Revolutionary +- Name: Robin Rottstock + Tier: Revolutionary +- Name: Third + Tier: Revolutionary - Name: Altana Tier: Revolutionary -- Name: showgun117 - Tier: Syndicate Agent - Name: Wolfiten Tier: Revolutionary - Name: Durp Tier: Revolutionary - Name: Joshington Awesomahee Tier: Revolutionary -- Name: Diklyquill - Tier: Revolutionary - Name: Eric VW Tier: Revolutionary - Name: Evan Armstrong @@ -20,16 +24,14 @@ Tier: Revolutionary - Name: Christopher Marmentini Tier: Nuclear Operative +- Name: Curtis Pearson + Tier: Revolutionary - Name: Mono Tier: Revolutionary -- Name: Shootmister - Tier: Syndicate Agent - Name: MonkeePawl Tier: Nuclear Operative - Name: Star Lord Tier: Syndicate Agent -- Name: Ethan Keller - Tier: Revolutionary - Name: creadth Tier: Nuclear Operative - Name: Robert Reed @@ -52,8 +54,6 @@ Tier: Revolutionary - Name: arthropods Tier: Revolutionary -- Name: Nicholas Perry - Tier: Syndicate Agent - Name: Mathieu Déom Tier: Syndicate Agent - Name: merklaw @@ -64,10 +64,8 @@ Tier: Nuclear Operative - Name: ThatGuyGW Tier: Nuclear Operative -- Name: MetalClone - Tier: Nuclear Operative - Name: dean - Tier: Nuclear Operative + Tier: Syndicate Agent - Name: Await Future Tier: Syndicate Agent - Name: Kyle Hipke From 9441defa134a90fc38ce80eef541eaac570d1f60 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 8 Feb 2021 01:25:36 +0100 Subject: [PATCH 20/22] Fix lighting bugs. --- .../Prototypes/Entities/Constructible/Walls/lighting.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml b/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml index ac9555174e..9d750a029d 100644 --- a/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml +++ b/Resources/Prototypes/Entities/Constructible/Walls/lighting.yml @@ -71,7 +71,7 @@ - type: PointLight energy: 1.0 enabled: true - offset: "-0.5, 0" + offset: "0.5, 0" - type: Destructible deadThreshold: 25 resistances: metallicResistances @@ -90,7 +90,7 @@ - type: PointLight energy: 1.0 enabled: false - offset: "-0.5, 0" + offset: "0.5, 0" - type: Physics shapes: - !type:PhysShapeAabb From 506a236e8b18cde9e36ade3ba98202f579e1ba5c Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 8 Feb 2021 05:14:42 +0100 Subject: [PATCH 21/22] Sprite blinking is gone :crab: --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index a183a98f75..91759cdd3c 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit a183a98f75b89072989ddc55cb990db6b671363e +Subproject commit 91759cdd3c378d84a34f0dc00b8d53f6eb72a9e5 From 3025449d5df6440ee5a62ac075e6336750f12089 Mon Sep 17 00:00:00 2001 From: Pieter-Jan Briers Date: Mon, 8 Feb 2021 22:35:58 +0100 Subject: [PATCH 22/22] Update submodule --- RobustToolbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RobustToolbox b/RobustToolbox index 91759cdd3c..5af7e60043 160000 --- a/RobustToolbox +++ b/RobustToolbox @@ -1 +1 @@ -Subproject commit 91759cdd3c378d84a34f0dc00b8d53f6eb72a9e5 +Subproject commit 5af7e60043b2170a1468e67c079f6c2de5ed38d1