Merge branch 'master' into expl_int_analyzer
This commit is contained in:
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -34,6 +34,7 @@
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
Resources/Maps/**.yml merge=mapping-merge-driver
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -284,3 +284,7 @@ BuildFiles/Windows/Godot/*
|
||||
# Windows image file caches
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
|
||||
# Merge driver stuff
|
||||
Content.Tools/test/out.yml
|
||||
|
||||
|
||||
@@ -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.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<string>());
|
||||
shell.ConsoleHost.RegisteredCommands["showsubfloorforever"].Execute(shell, string.Empty, Array.Empty<string>());
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
68
Content.Client/Placement/Modes/WallmountLight.cs
Normal file
68
Content.Client/Placement/Modes/WallmountLight.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,9 +246,13 @@ namespace Content.Client.ParticleAccelerator
|
||||
}
|
||||
}
|
||||
},
|
||||
new MarginContainer
|
||||
{
|
||||
CustomMinimumSize = (186, 0),
|
||||
Children =
|
||||
{
|
||||
new VBoxContainer
|
||||
{
|
||||
SizeFlagsHorizontal = SizeFlags.FillExpand,
|
||||
Children =
|
||||
{
|
||||
(_statusLabel = new Label
|
||||
@@ -297,6 +301,8 @@ namespace Content.Client.ParticleAccelerator
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
new StripeBack
|
||||
{
|
||||
Children =
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<IMapManager>();
|
||||
|
||||
mapManager.CreateNewMapEntity(MapId.Nullspace);
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
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<IMapManager>();
|
||||
|
||||
var mapId = new MapId(1);
|
||||
mapManager.CreateNewMapEntity(mapId);
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
var gridId = new GridId(1);
|
||||
var grid = mapManager.CreateGrid(mapId, gridId);
|
||||
var coordinates = grid.GridEntityId.ToCoordinates();
|
||||
var tileManager = IoCManager.Resolve<ITileDefinitionManager>();
|
||||
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<IMapManager>();
|
||||
|
||||
var mapId = new MapId(1);
|
||||
mapManager.CreateNewMapEntity(mapId);
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
var gridId = new GridId(1);
|
||||
var grid = mapManager.CreateGrid(mapId, gridId);
|
||||
var coordinates = grid.GridEntityId.ToCoordinates();
|
||||
var tileManager = IoCManager.Resolve<ITileDefinitionManager>();
|
||||
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);
|
||||
|
||||
@@ -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<IComponentFactory>().Register<TestThresholdListenerComponent>();
|
||||
}
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||
|
||||
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<IDamageableComponent>();
|
||||
sThresholdListenerComponent = sDestructibleEntity.GetComponent<TestThresholdListenerComponent>();
|
||||
});
|
||||
|
||||
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<TotalDamageClassesTrigger>(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<TotalDamageClassesTrigger>(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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IComponentFactory>().Register<TestThresholdListenerComponent>();
|
||||
}
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||
|
||||
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<IDamageableComponent>();
|
||||
sThresholdListenerComponent = sDestructibleEntity.GetComponent<TestThresholdListenerComponent>();
|
||||
});
|
||||
|
||||
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<TotalDamageTypesTrigger>(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<TotalDamageTypesTrigger>(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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<IComponentFactory>().Register<TestThresholdListenerComponent>();
|
||||
}
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||
|
||||
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<IDamageableComponent>();
|
||||
sThresholdListenerComponent = sDestructibleEntity.GetComponent<TestThresholdListenerComponent>();
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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<DestructibleThresholdReachedMessage> 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<IComponentFactory>().Register<TestThresholdListenerComponent>();
|
||||
}
|
||||
});
|
||||
|
||||
await server.WaitIdleAsync();
|
||||
|
||||
var sEntityManager = server.ResolveDependency<IEntityManager>();
|
||||
var sMapManager = server.ResolveDependency<IMapManager>();
|
||||
|
||||
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<IDamageableComponent>();
|
||||
sDestructibleComponent = sDestructibleEntity.GetComponent<DestructibleComponent>();
|
||||
sThresholdListenerComponent = sDestructibleEntity.GetComponent<TestThresholdListenerComponent>();
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<DestructibleThresholdReachedMessage> ThresholdsReached { get; } = new();
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case DestructibleThresholdReachedMessage msg:
|
||||
ThresholdsReached.Add(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
});
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
26
Content.Server/Alert/Click/RemoveCuffs.cs
Normal file
26
Content.Server/Alert/Click/RemoveCuffs.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
#nullable enable
|
||||
using Content.Server.GameObjects.Components.ActionBlocking;
|
||||
using Content.Shared.Alert;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Alert.Click
|
||||
{
|
||||
/// <summary>
|
||||
/// Try to remove handcuffs from yourself
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
public class RemoveCuffs : IAlertClick
|
||||
{
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer) {}
|
||||
|
||||
public void AlertClicked(ClickAlertEventArgs args)
|
||||
{
|
||||
if (args.Player.TryGetComponent(out CuffableComponent? cuffableComponent))
|
||||
{
|
||||
cuffableComponent.TryUncuff(args.Player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -9,6 +9,7 @@ using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Map;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -83,7 +84,7 @@ namespace Content.Server.Chemistry.ReactionEffects
|
||||
IoCManager.InjectDependencies(this);
|
||||
}
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _rangeConstant, "rangeConstant",0f);
|
||||
serializer.DataField(ref _rangeMultiplier, "rangeMultiplier",1.1f);
|
||||
|
||||
@@ -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.WriteError("WARNING: The server is using a debug build. You are risking losing your changes.");
|
||||
#endif
|
||||
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
int mapId;
|
||||
string mapName;
|
||||
|
||||
@@ -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<MindComponent>();
|
||||
entity.EnsureComponent<PlayerInputMoverComponent>();
|
||||
entity.EnsureComponent<SharedSpeechComponent>();
|
||||
entity.EnsureComponent<SharedEmotingComponent>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<IPhysicsComponent>().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();
|
||||
}
|
||||
|
||||
@@ -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<Container>(Name, Owner);
|
||||
_interactRange = SharedInteractionSystem.InteractionRange / 2;
|
||||
|
||||
Owner.EntityManager.EventBus.SubscribeEvent<HandCountChangedEvent>(EventSource.Local, this, HandleHandCountChange);
|
||||
|
||||
if (!Owner.TryGetComponent(out _hands))
|
||||
{
|
||||
Logger.Warning("Player does not have an IHandsComponent!");
|
||||
}
|
||||
Owner.EnsureComponentWarn<HandsComponent>();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
@@ -99,27 +93,35 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
|
||||
/// Add a set of cuffs to an existing CuffedComponent.
|
||||
/// </summary>
|
||||
/// <param name="prototype"></param>
|
||||
public void AddNewCuffs(IEntity handcuff)
|
||||
public bool TryAddNewCuffs(IEntity user, IEntity handcuff)
|
||||
{
|
||||
if (!handcuff.HasComponent<HandcuffComponent>())
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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
|
||||
/// </summary>
|
||||
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
|
||||
/// </summary>
|
||||
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
|
||||
/// </summary>
|
||||
/// <param name="user">The cuffed entity</param>
|
||||
/// <param name="cuffsToRemove">Optional param for the handcuff entity to remove from the cuffed entity. If null, uses the most recently added handcuff entity.</param>
|
||||
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<AudioSystem>();
|
||||
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,10 +275,15 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
|
||||
};
|
||||
|
||||
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
|
||||
_uncuffing = true;
|
||||
|
||||
var result = await doAfterSystem.DoAfter(doAfterEventArgs);
|
||||
|
||||
_uncuffing = false;
|
||||
|
||||
if (result != DoAfterStatus.Cancelled)
|
||||
{
|
||||
if (cuff.EndUncuffSound != null)
|
||||
audio.PlayFromEntity(cuff.EndUncuffSound, Owner);
|
||||
|
||||
_container.ForceRemove(cuffsToRemove);
|
||||
@@ -276,14 +297,14 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
|
||||
cuffsToRemove.Name = cuff.BrokenName;
|
||||
cuffsToRemove.Description = cuff.BrokenDesc;
|
||||
|
||||
if (cuffsToRemove.TryGetComponent<SpriteComponent>(out var sprite))
|
||||
if (cuffsToRemove.TryGetComponent<SpriteComponent>(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();
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
@@ -58,31 +58,31 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
|
||||
/// The path of the RSI file used for the player cuffed overlay.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string CuffedRSI { get; set; }
|
||||
public string? CuffedRSI { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The iconstate used with the RSI file for the player cuffed overlay.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string OverlayIconState { get; set; }
|
||||
public string? OverlayIconState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The iconstate used for broken handcuffs
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string BrokenState { get; set; }
|
||||
public string? BrokenState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The iconstate used for broken handcuffs
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public string BrokenName { get; set; }
|
||||
public string BrokenName { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The iconstate used for broken handcuffs
|
||||
/// </summary>
|
||||
[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<AudioSystem>();
|
||||
_interactRange = SharedInteractionSystem.InteractionRange / 2;
|
||||
}
|
||||
/// <summary>
|
||||
/// Used to prevent DoAfter getting spammed.
|
||||
/// </summary>
|
||||
private bool _cuffing;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
@@ -150,6 +145,8 @@ namespace Content.Server.GameObjects.Components.ActionBlocking
|
||||
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (_cuffing) return true;
|
||||
|
||||
if (eventArgs.Target == null || !ActionBlockerSystem.CanUse(eventArgs.User) || !eventArgs.Target.TryGetComponent<CuffableComponent>(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<AudioSystem>().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<DoAfterSystem>().DoAfter(doAfterEventArgs);
|
||||
|
||||
_cuffing = false;
|
||||
|
||||
if (result != DoAfterStatus.Cancelled)
|
||||
{
|
||||
_audioSystem.PlayFromEntity(EndCuffSound, Owner);
|
||||
if (cuffs.TryAddNewCuffs(user, Owner))
|
||||
{
|
||||
if (EndCuffSound != null)
|
||||
EntitySystem.Get<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 (user.TryGetComponent<HandsComponent>(out var hands))
|
||||
{
|
||||
hands.Drop(Owner);
|
||||
cuffs.AddNewCuffs(Owner);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning("Unable to remove handcuffs from player's hands! This should not happen!");
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace Content.Server.GameObjects.Components.Atmos.Piping.Pumps
|
||||
_appearance?.SetData(PumpVisuals.VisualState, new PumpVisualState(_initialInletDirection, _initialOutletDirection, PumpEnabled));
|
||||
}
|
||||
|
||||
public void Activate(ActivateEventArgs eventArgs)
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
PumpEnabled = !PumpEnabled;
|
||||
}
|
||||
|
||||
@@ -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<MindComponent>();
|
||||
var oldMind = oldEntity.EnsureComponent<MindComponent>();
|
||||
|
||||
if (!newEntity.HasComponent<IGhostOnMove>())
|
||||
newEntity.AddComponent<GhostOnMoveComponent>();
|
||||
|
||||
// TODO: This is an awful solution.
|
||||
if (!newEntity.HasComponent<IMoverComponent>())
|
||||
newEntity.AddComponent<SharedDummyInputMoverComponent>();
|
||||
|
||||
oldMind.Mind?.TransferTo(newEntity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!;
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -22,22 +22,15 @@ namespace Content.Server.GameObjects.Components.Destructible
|
||||
|
||||
public override string Name => "Destructible";
|
||||
|
||||
[ViewVariables]
|
||||
private SortedDictionary<int, Threshold> _lowestToHighestThresholds = new();
|
||||
[ViewVariables] private List<Threshold> _thresholds = new();
|
||||
|
||||
[ViewVariables] private int PreviousTotalDamage { get; set; }
|
||||
|
||||
public IReadOnlyDictionary<int, Threshold> LowestToHighestThresholds => _lowestToHighestThresholds;
|
||||
public IReadOnlyList<Threshold> Thresholds => _thresholds;
|
||||
|
||||
public override void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataReadWriteFunction(
|
||||
"thresholds",
|
||||
new Dictionary<int, Threshold>(),
|
||||
thresholds => _lowestToHighestThresholds = new SortedDictionary<int, Threshold>(thresholds),
|
||||
() => new Dictionary<int, Threshold>(_lowestToHighestThresholds));
|
||||
serializer.DataField(ref _thresholds, "thresholds", new List<Threshold>());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of total damage currently had that triggered this threshold.
|
||||
/// </summary>
|
||||
public int TotalDamage { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold triggers.
|
||||
/// </summary>
|
||||
public int ThresholdAmount { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
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.Interfaces.Serialization;
|
||||
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;
|
||||
@@ -30,7 +32,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))
|
||||
{
|
||||
@@ -5,7 +5,7 @@ using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
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
|
||||
@@ -17,7 +17,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))
|
||||
{
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Triggers this behavior.
|
||||
/// Executes this behavior.
|
||||
/// </summary>
|
||||
/// <param name="owner">The entity that owns this behavior.</param>
|
||||
/// <param name="system">
|
||||
/// An instance of <see cref="DestructibleSystem"/> to pull dependencies
|
||||
/// and other systems from.
|
||||
/// </param>
|
||||
void Trigger(IEntity owner, DestructibleSystem system);
|
||||
void Execute(IEntity owner, DestructibleSystem system);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using System;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Audio;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
@@ -18,7 +20,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))
|
||||
{
|
||||
@@ -1,11 +1,13 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using System;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Audio;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
@@ -18,7 +20,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))
|
||||
{
|
||||
@@ -1,4 +1,5 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Stack;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
@@ -7,8 +8,9 @@ using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
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
|
||||
{
|
||||
/// <summary>
|
||||
@@ -21,7 +23,7 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior
|
||||
serializer.DataField(this, x => x.Spawn, "spawn", new Dictionary<string, MinMax>());
|
||||
}
|
||||
|
||||
public void Trigger(IEntity owner, DestructibleSystem system)
|
||||
public void Execute(IEntity owner, DestructibleSystem system)
|
||||
{
|
||||
foreach (var (entityId, minMax) in Spawn)
|
||||
{
|
||||
@@ -7,14 +7,14 @@ using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
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
|
||||
{
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer) { }
|
||||
|
||||
public void Trigger(IEntity owner, DestructibleSystem system)
|
||||
public void Execute(IEntity owner, DestructibleSystem system)
|
||||
{
|
||||
if (!owner.TryGetComponent(out SolutionContainerComponent? solutionContainer))
|
||||
return;
|
||||
@@ -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]
|
||||
|
||||
@@ -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<IThresholdBehavior> _behaviors = new();
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this threshold was triggered in the previous call to
|
||||
/// <see cref="Reached"/>.
|
||||
/// </summary>
|
||||
[ViewVariables] public bool OldTriggered { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this threshold has already been triggered.
|
||||
/// </summary>
|
||||
@@ -26,6 +34,11 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds
|
||||
/// </summary>
|
||||
[ViewVariables] public bool TriggersOnce { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The trigger that decides if this threshold has been reached.
|
||||
/// </summary>
|
||||
[ViewVariables] public IThresholdTrigger? Trigger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Behaviors to activate once this threshold is triggered.
|
||||
/// </summary>
|
||||
@@ -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<IThresholdBehavior>());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers this threshold.
|
||||
/// </summary>
|
||||
@@ -46,7 +87,7 @@ namespace Content.Server.GameObjects.Components.Destructible.Thresholds
|
||||
/// An instance of <see cref="DestructibleSystem"/> to get dependency and
|
||||
/// system references from, if relevant.
|
||||
/// </param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if this trigger has been reached.
|
||||
/// </summary>
|
||||
/// <param name="damageable">The damageable component to check with.</param>
|
||||
/// <param name="system">
|
||||
/// An instance of <see cref="DestructibleSystem"/> to pull
|
||||
/// dependencies from, if any.
|
||||
/// </param>
|
||||
/// <returns>true if this trigger has been reached, false otherwise.</returns>
|
||||
bool Reached(IDamageableComponent damageable, DestructibleSystem system);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#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.Interfaces.Serialization;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when all of the damage classes received
|
||||
/// are above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class TotalDamageClassesTrigger : IThresholdTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold will trigger.
|
||||
/// The damage requirements of all <see cref="DamageClass"/> must be met.
|
||||
/// </summary>
|
||||
private Dictionary<DamageClass, int> Damage { get; set; } = new();
|
||||
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.Damage, "damage", new Dictionary<DamageClass, int>());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when the amount of total damage received
|
||||
/// is above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class TotalDamageTrigger : IThresholdTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of total damage at which this threshold will trigger.
|
||||
/// </summary>
|
||||
public int Damage { get; private set; }
|
||||
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.Damage, "damage", 0);
|
||||
}
|
||||
|
||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
return damageable.TotalDamage >= Damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#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.Interfaces.Serialization;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when all of the damage types received
|
||||
/// are above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public class TotalDamageTypesTrigger : IThresholdTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold will trigger.
|
||||
/// The damage requirements of all <see cref="DamageType"/> must be met.
|
||||
/// </summary>
|
||||
private Dictionary<DamageType, int> Damage { get; set; } = new();
|
||||
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.Damage, "damage", new Dictionary<DamageType, int>());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,9 +80,18 @@ namespace Content.Server.GameObjects.Components.Disposal
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private TimeSpan _flushDelay;
|
||||
|
||||
/// <summary>
|
||||
/// Delay from trying to enter disposals ourselves.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private float _entryDelay;
|
||||
|
||||
/// <summary>
|
||||
/// Delay from trying to shove someone else into disposals.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private float _draggedEntryDelay;
|
||||
|
||||
/// <summary>
|
||||
/// 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<DoAfterSystem>();
|
||||
|
||||
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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
{
|
||||
@@ -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()
|
||||
|
||||
@@ -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<ISolutionInteractionsComponent>()?.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<SolutionContainerComponent>();
|
||||
}
|
||||
|
||||
_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<bool> 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<StomachBehavior>(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;
|
||||
}
|
||||
|
||||
if (_useSound != null)
|
||||
interactions.Refill(drain);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(_useSound))
|
||||
{
|
||||
EntitySystem.Get<AudioSystem>().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<AudioSystem>().PlayFromEntity(_burstSound, Owner,
|
||||
|
||||
@@ -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<VisitingMindComponent>()) return;
|
||||
if (!Owner.TryGetComponent(out MindComponent? mind) || !mind.HasMind || mind.Mind!.IsVisitingEntity) return;
|
||||
|
||||
var host = IoCManager.Resolve<IServerConsoleHost>();
|
||||
new Ghost().Execute(new ConsoleShell(host, session), string.Empty, Array.Empty<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Observer
|
||||
{
|
||||
public interface IGhostOnMove
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -228,6 +230,8 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece
|
||||
}
|
||||
|
||||
void IMapInit.MapInit()
|
||||
{
|
||||
if (_hasLampOnSpawn)
|
||||
{
|
||||
var prototype = BulbType switch
|
||||
{
|
||||
@@ -238,6 +242,8 @@ namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerRece
|
||||
|
||||
var entity = Owner.EntityManager.SpawnEntity(prototype, Owner.Transform.Coordinates);
|
||||
_lightBulbContainer.Insert(entity);
|
||||
UpdateLight();
|
||||
}
|
||||
}
|
||||
|
||||
public void TriggerSignal(bool signal)
|
||||
|
||||
@@ -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<IDamageableComponent>()?.TotalDamage;
|
||||
var damageThreshold = Owner.GetComponentOrNull<DestructibleComponent>()?.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:
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Content.Server.GameObjects.EntitySystems
|
||||
[Dependency] public readonly IRobustRandom Random = default!;
|
||||
|
||||
public AudioSystem AudioSystem { get; private set; }
|
||||
|
||||
public ActSystem ActSystem { get; private set; }
|
||||
|
||||
public override void Initialize()
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Content.Shared
|
||||
GameLobbyEnabled = CVarDef.Create("game.lobbyenabled", false, CVar.ARCHIVE);
|
||||
|
||||
public static readonly CVarDef<int>
|
||||
GameLobbyDuration = CVarDef.Create("game.lobbyduration", 20, CVar.ARCHIVE);
|
||||
GameLobbyDuration = CVarDef.Create("game.lobbyduration", 60, CVar.ARCHIVE);
|
||||
|
||||
public static readonly CVarDef<string>
|
||||
GameLobbyDefaultPreset = CVarDef.Create("game.defaultpreset", "Suspicion", CVar.ARCHIVE);
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#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;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
|
||||
namespace Content.Shared.Construction.ConstructionConditions
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class WallmountCondition : IConstructionCondition
|
||||
{
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer) { }
|
||||
|
||||
public bool Condition(IEntity user, EntityCoordinates location, Direction direction)
|
||||
{
|
||||
var entManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
// 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<IPhysicsManager>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -12,13 +12,13 @@ namespace Content.Shared.GameObjects.Components.GUI
|
||||
{
|
||||
public override string Name => "Stripping";
|
||||
|
||||
public bool CanDragDropOn(DragDropEventArgs eventArgs)
|
||||
bool IDragDropOn.CanDragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.Dragged.TryGetComponent(out SharedStrippableComponent? strippable)) return false;
|
||||
return strippable.CanBeStripped(Owner);
|
||||
}
|
||||
|
||||
public bool DragDropOn(DragDropEventArgs eventArgs)
|
||||
bool IDragDropOn.DragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
// Handled by StrippableComponent
|
||||
return true;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Component required for entities to be able to speak.
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,11 @@
|
||||
#nullable enable
|
||||
using Robust.Shared.GameObjects;
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.GameObjects.Components.Tag
|
||||
{
|
||||
[Serializable, NetSerializable]
|
||||
public class TagComponentState : ComponentState
|
||||
{
|
||||
public TagComponentState(string[] tags) : base(ContentNetIDs.TAG)
|
||||
|
||||
@@ -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<SharedSpeechComponent>())
|
||||
return false;
|
||||
|
||||
var canSpeak = true;
|
||||
|
||||
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
|
||||
@@ -98,6 +102,9 @@ namespace Content.Shared.GameObjects.EntitySystems.ActionBlocker
|
||||
|
||||
public static bool CanEmote(IEntity entity)
|
||||
{
|
||||
if (!entity.HasComponent<SharedEmotingComponent>())
|
||||
return false;
|
||||
|
||||
var canEmote = true;
|
||||
|
||||
foreach (var blocker in entity.GetAllComponents<IActionBlocker>())
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Content.Shared.Prototypes.Tag
|
||||
{
|
||||
public string ID { get; [UsedImplicitly] private set; } = default!;
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
void IExposeData.ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.ID, "id", "");
|
||||
}
|
||||
|
||||
16
Content.Tools/Content.Tools.csproj
Normal file
16
Content.Tools/Content.Tools.csproj
Normal file
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net5.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="YamlDotNet" Version="9.1.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
83
Content.Tools/Map.cs
Normal file
83
Content.Tools/Map.cs
Normal file
@@ -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<uint, YamlMappingNode> Entities { get; } = new Dictionary<uint, YamlMappingNode>();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
38
Content.Tools/MappingMergeDriver.cs
Normal file
38
Content.Tools/MappingMergeDriver.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
356
Content.Tools/Merger.cs
Normal file
356
Content.Tools/Merger.cs
Normal file
@@ -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<uint, uint> TileMapFromOtherToOurs { get; } = new Dictionary<uint, uint>();
|
||||
public Dictionary<uint, uint> TileMapFromBasedToOurs { get; } = new Dictionary<uint, uint>();
|
||||
public Dictionary<uint, uint> EntityMapFromOtherToOurs { get; } = new Dictionary<uint, uint>();
|
||||
public List<uint> EntityListDirectMerge { get; } = new List<uint>();
|
||||
|
||||
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<uint, uint> relativeOtherToOurs, Map relativeOther)
|
||||
{
|
||||
var mapping = new Dictionary<string, uint>();
|
||||
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<string>();
|
||||
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<uint, uint> mapping)
|
||||
{
|
||||
return (src & 0xFFFF0000) | mapping[src & 0xFFFF];
|
||||
}
|
||||
|
||||
public Dictionary<string, YamlMappingNode> ConvertTileChunks(YamlSequenceNode chunks)
|
||||
{
|
||||
var map = new Dictionary<string, YamlMappingNode>();
|
||||
foreach (var chunk in chunks)
|
||||
map[chunk["ind"].ToString()] = (YamlMappingNode) chunk;
|
||||
return map;
|
||||
}
|
||||
|
||||
public byte[] GetChunkBytes(Dictionary<string, YamlMappingNode> 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<string, YamlMappingNode>();
|
||||
var basedComponents = new Dictionary<string, YamlMappingNode>();
|
||||
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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
25
Content.Tools/TypeTagPreserver.cs
Normal file
25
Content.Tools/TypeTagPreserver.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
199
Content.Tools/YamlTools.cs
Normal file
199
Content.Tools/YamlTools.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
95
Content.Tools/test/0A.yml
Normal file
95
Content.Tools/test/0A.yml
Normal file
@@ -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
|
||||
|
||||
86
Content.Tools/test/0B.yml
Normal file
86
Content.Tools/test/0B.yml
Normal file
@@ -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
|
||||
|
||||
105
Content.Tools/test/0C.yml
Normal file
105
Content.Tools/test/0C.yml
Normal file
@@ -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
|
||||
|
||||
3
Content.Tools/test/run.sh
Executable file
3
Content.Tools/test/run.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cp 0A.yml out.yml
|
||||
../bin/Debug/net5.0/Content.Tools out.yml 0B.yml 0C.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
|
||||
|
||||
54
Resources/Maps/Test/empty.yml
Normal file
54
Resources/Maps/Test/empty.yml
Normal file
@@ -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
|
||||
...
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -60,7 +60,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
500:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 500
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -25,7 +25,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
75:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 75
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
30:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 30
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/woodhit.ogg
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
50:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 50
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
100:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -34,7 +34,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
50:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 50
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
30:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 30
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/metalbreak.ogg
|
||||
@@ -76,7 +78,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
30:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 30
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/metalbreak.ogg
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
15:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 15
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/metalbreak.ogg
|
||||
@@ -65,7 +67,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
1:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 1
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/metalbreak.ogg
|
||||
@@ -94,7 +98,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
1:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 1
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/metalbreak.ogg
|
||||
@@ -120,7 +126,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
15:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 15
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/metalbreak.ogg
|
||||
@@ -149,7 +157,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
75:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 75
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/metalbreak.ogg
|
||||
@@ -178,7 +188,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
5:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 5
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/glass_break2.ogg
|
||||
@@ -207,7 +219,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
20:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 20
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/glass_break2.ogg
|
||||
@@ -236,7 +250,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
15:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 15
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/woodhit.ogg
|
||||
@@ -265,7 +281,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
15:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 15
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/woodhit.ogg
|
||||
@@ -294,7 +312,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
50:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 50
|
||||
behaviors:
|
||||
- !type:PlaySoundBehavior
|
||||
sound: /Audio/Effects/picaxe2.ogg
|
||||
@@ -315,7 +335,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
1:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 1
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
- type: Damageable
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
100:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
- type: Damageable
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
100:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
100:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
- type: Damageable
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
50:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 50
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- type: entity
|
||||
- type: entity
|
||||
abstract: true
|
||||
id: PipeBase
|
||||
name: Pipe
|
||||
@@ -14,7 +14,9 @@
|
||||
- type: Damageable
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
100:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
100:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
@@ -44,4 +46,3 @@
|
||||
- type: PressurePump
|
||||
inletDirection: West
|
||||
outletDirection: East
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
100:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -13,7 +13,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
100:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -98,7 +98,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
100:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Destruction"]
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
resistances: metallicResistances
|
||||
- type: Destructible
|
||||
thresholds:
|
||||
100:
|
||||
- trigger:
|
||||
!type:TotalDamageTrigger
|
||||
damage: 100
|
||||
behaviors:
|
||||
- !type:DoActsBehavior
|
||||
acts: ["Breakage"]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user